PluginProbe
Subscriptions for WooCommerce with Stripe Recurring Payments / 2.0.0
Subscriptions for WooCommerce with Stripe Recurring Payments v2.0.0
2.0.0 1.11.2 1.11.1 1.11.0 1.10.9 1.10.8 1.10.7 1.10.6 1.10.5 1.10.4 1.10.3 1.10.2 1.10.1 1.10.0 1.9.6 1.9.5 trunk 1.3.0 1.3.1 1.3.2 1.4.0 1.4.1 1.4.2 1.5.0 1.5.1 All 61 releases
subscription / assets / js / admin / integrations-filter.js

integrations-filter.js in Subscriptions for WooCommerce with Stripe Recurring Payments 2.0.0, at assets/js/admin/integrations-filter.js

211 lines 6.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Live filtering for the Integrations page.
3 *
4 * Every card is already in the DOM — there are around fifteen — so filtering
5 * happens here rather than as a round trip per click, which would be slower and
6 * would throw away the scroll position for nothing.
7 *
8 * The filter rail ships with a `hidden` attribute and is revealed here, so a
9 * browser with no JavaScript shows the full, unfiltered list rather than
10 * controls that do nothing.
11 */
12 (function () {
13 "use strict";
14
15 var root = document.querySelector("[data-subscrpt-integration-filters]");
16
17 if (!root) {
18 return;
19 }
20
21 var cards = Array.prototype.slice.call(document.querySelectorAll("[data-subscrpt-int-card]"));
22
23 if (!cards.length) {
24 return;
25 }
26
27 var sections = Array.prototype.slice.call(document.querySelectorAll("[data-subscrpt-int-section]"));
28 var summary = root.querySelector("[data-subscrpt-int-summary]");
29 var reset = root.querySelector("[data-subscrpt-int-reset]");
30 var empty = document.querySelector("[data-subscrpt-int-empty]");
31 var chips = Array.prototype.slice.call(root.querySelectorAll("[data-subscrpt-int-chip]"));
32
33 var total = cards.length;
34
35 // Category and status are single-select: a card has exactly one of each, so
36 // picking two could only ever return nothing. Tags stay multi-select, because
37 // "show me Pro and Beta" is a reasonable thing to ask.
38 var SINGLE = ["category", "status"];
39
40 var state = { category: "", status: "", tag: [] };
41
42 function matches(card) {
43 if (state.category && card.dataset.category !== state.category) {
44 return false;
45 }
46
47 if (state.status && card.dataset.status !== state.status) {
48 return false;
49 }
50
51 if (state.tag.length) {
52 var tags = (card.dataset.tags || "").split(" ");
53 var hasAll = state.tag.every(function (t) {
54 return tags.indexOf(t) !== -1;
55 });
56 if (!hasAll) {
57 return false;
58 }
59 }
60
61 return true;
62 }
63
64 // How many cards a chip would show: the current filters, but with this chip's
65 // own facet swapped to its value (a facet never constrains its own counts;
66 // the other facets do). Powers the live count next to each chip.
67 function countFor(facet, value) {
68 return cards.filter(function (card) {
69 if (facet !== "category" && state.category && card.dataset.category !== state.category) {
70 return false;
71 }
72 if (facet !== "status" && state.status && card.dataset.status !== state.status) {
73 return false;
74 }
75
76 var tags = (card.dataset.tags || "").split(" ");
77
78 // Respect the other selected tags; for a tag chip, ignore its own state
79 // and instead require the card to carry this tag.
80 var otherTags = facet === "tag" ? state.tag.filter((t) => t !== value) : state.tag;
81 var hasOthers = otherTags.every(function (t) {
82 return tags.indexOf(t) !== -1;
83 });
84 if (!hasOthers) {
85 return false;
86 }
87 if (facet === "tag" && tags.indexOf(value) === -1) {
88 return false;
89 }
90
91 // Single-select chips (a specific value): the card must carry it.
92 if (facet === "category" && value && card.dataset.category !== value) {
93 return false;
94 }
95 if (facet === "status" && value && card.dataset.status !== value) {
96 return false;
97 }
98
99 return true;
100 }).length;
101 }
102
103 function updateCounts() {
104 chips.forEach(function (chip) {
105 var out = chip.querySelector(".subscrpt-int-chip__count");
106 if (!out) {
107 return;
108 }
109 var n = countFor(chip.dataset.facet, chip.dataset.value);
110 out.textContent = n;
111
112 // Dim (and disable) a chip that would return nothing, unless it's the
113 // currently active one — that must stay clickable to switch off.
114 var active = chip.classList.contains("is-active");
115 var dead = n === 0 && !active;
116 chip.style.opacity = dead ? "0.4" : "";
117 chip.style.pointerEvents = dead ? "none" : "";
118 });
119 }
120
121 function apply() {
122 var visible = 0;
123
124 cards.forEach(function (card) {
125 var show = matches(card);
126 card.hidden = !show;
127 if (show) {
128 visible += 1;
129 }
130 });
131
132 // A heading over nothing reads as a bug, so hide a section whose cards have
133 // all been filtered away.
134 sections.forEach(function (section) {
135 section.hidden = !section.querySelector("[data-subscrpt-int-card]:not([hidden])");
136 });
137
138 var filtering = !!state.category || !!state.status.length || !!state.tag.length;
139
140 if (summary) {
141 summary.textContent = filtering
142 ? summary.dataset.filtered.replace("%1$s", visible).replace("%2$s", total)
143 : summary.dataset.all.replace("%s", total);
144 }
145
146 if (reset) {
147 reset.hidden = !filtering;
148 }
149
150 if (empty) {
151 empty.hidden = visible !== 0;
152 }
153
154 updateCounts();
155 }
156
157 chips.forEach(function (chip) {
158 chip.addEventListener("click", function () {
159 var facet = chip.dataset.facet;
160 var value = chip.dataset.value;
161
162 if (SINGLE.indexOf(facet) !== -1) {
163 // Selecting one clears the other in the same group, and clicking the
164 // selected one again returns the group to "All".
165 state[facet] = state[facet] === value ? "" : value;
166 chips
167 .filter(function (c) {
168 return c.dataset.facet === facet;
169 })
170 .forEach(function (c) {
171 var on = c.dataset.value === state[facet];
172 c.classList.toggle("is-active", on);
173 if (c.hasAttribute("aria-pressed")) {
174 c.setAttribute("aria-pressed", on ? "true" : "false");
175 }
176 });
177 } else {
178 var list = state[facet];
179 var at = list.indexOf(value);
180 if (at === -1) {
181 list.push(value);
182 } else {
183 list.splice(at, 1);
184 }
185 var on = list.indexOf(value) !== -1;
186 chip.classList.toggle("is-active", on);
187 chip.setAttribute("aria-pressed", on ? "true" : "false");
188 }
189
190 apply();
191 });
192 });
193
194 if (reset) {
195 reset.addEventListener("click", function () {
196 state = { category: "", status: "", tag: [] };
197 chips.forEach(function (c) {
198 var isAll = SINGLE.indexOf(c.dataset.facet) !== -1 && c.dataset.value === "";
199 c.classList.toggle("is-active", isAll);
200 if (c.hasAttribute("aria-pressed")) {
201 c.setAttribute("aria-pressed", "false");
202 }
203 });
204 apply();
205 });
206 }
207
208 root.hidden = false;
209 apply();
210 })();
211