@wui-js/plugins 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,493 @@
1
+ /*
2
+ * @file wuiplugin-selector-0.3.js
3
+ * @class WUIPluginSelector
4
+ * @version 0.3
5
+ * @author Sergio E. Belmar V. (wuijs.project@gmail.com)
6
+ * @copyright Sergio E. Belmar V. (wuijs.project@gmail.com)
7
+ */
8
+
9
+ class WUIPluginSelector extends WUIModal {
10
+
11
+ static version = "0.3";
12
+ static #defaults = {
13
+ value: "",
14
+ options: [],
15
+ multiple: false,
16
+ separatorValue: ",",
17
+ separatorText: ", ",
18
+ emptyText: "",
19
+ selecteableText: false,
20
+ acceptButton: null,
21
+ acceptVisible: true,
22
+ acceptData: {},
23
+ acceptText: "",
24
+ acceptOnClick: null,
25
+ cancelButton: null,
26
+ cancelVisible: true,
27
+ cancelData: {},
28
+ cancelText: "",
29
+ cancelOnClick: null,
30
+ onSelect: null
31
+ };
32
+
33
+ #properties = {};
34
+ #htmlElements = {
35
+ box: null,
36
+ options: null,
37
+ footer: null,
38
+ cancelButton: null,
39
+ acceptButton: null
40
+ };
41
+ #input;
42
+
43
+ constructor(properties = {}) {
44
+ super(properties);
45
+ const defaults = structuredClone(WUIPluginSelector.#defaults);
46
+ Object.entries(defaults).forEach(([name, value]) => {
47
+ this[name] = name in properties ? properties[name] : value;
48
+ });
49
+ this.#input = null;
50
+ }
51
+
52
+ get value() {
53
+ return this.#properties.value;
54
+ }
55
+
56
+ get options() {
57
+ return this.#properties.options;
58
+ }
59
+
60
+ get multiple() {
61
+ return this.#properties.multiple;
62
+ }
63
+
64
+ get separatorValue() {
65
+ return this.#properties.separatorValue;
66
+ }
67
+
68
+ get separatorText() {
69
+ return this.#properties.separatorText;
70
+ }
71
+
72
+ get emptyText() {
73
+ return this.#properties.emptyText;
74
+ }
75
+
76
+ get selecteableText() {
77
+ return this.#properties.selecteableText;
78
+ }
79
+
80
+ get acceptButton() {
81
+ return this.#properties.acceptButton;
82
+ }
83
+
84
+ get acceptVisible() {
85
+ return this.#properties.acceptVisible;
86
+ }
87
+
88
+ get acceptData() {
89
+ return this.#properties.acceptData;
90
+ }
91
+
92
+ get acceptText() {
93
+ return this.#properties.acceptText;
94
+ }
95
+
96
+ get acceptOnClick() {
97
+ return this.#properties.acceptOnClick;
98
+ }
99
+
100
+ get cancelButton() {
101
+ return this.#properties.cancelButton;
102
+ }
103
+
104
+ get cancelVisible() {
105
+ return this.#properties.cancelVisible;
106
+ }
107
+
108
+ get cancelData() {
109
+ return this.#properties.cancelData;
110
+ }
111
+
112
+ get cancelText() {
113
+ return this.#properties.cancelText;
114
+ }
115
+
116
+ get cancelOnClick() {
117
+ return this.#properties.cancelOnClick;
118
+ }
119
+
120
+ get onSelect() {
121
+ return this.#properties.onSelect;
122
+ }
123
+
124
+ set value(value) {
125
+ if (typeof (value) == "string") {
126
+ this.#properties.value = value;
127
+ }
128
+ }
129
+
130
+ set options(value) {
131
+ if (Array.isArray(value)) {
132
+ this.#properties.options = value;
133
+ }
134
+ }
135
+
136
+ set multiple(value) {
137
+ if (typeof (value) == "boolean") {
138
+ this.#properties.multiple = value;
139
+ }
140
+ }
141
+
142
+ set separatorValue(value) {
143
+ if (typeof (value) == "string") {
144
+ this.#properties.separatorValue = value;
145
+ }
146
+ }
147
+
148
+ set separatorText(value) {
149
+ if (typeof (value) == "string") {
150
+ this.#properties.separatorText = value;
151
+ }
152
+ }
153
+
154
+ set emptyText(value) {
155
+ if (typeof (value) == "string") {
156
+ this.#properties.emptyText = value;
157
+ }
158
+ }
159
+
160
+ set selecteableText(value) {
161
+ if (typeof (value) == "boolean") {
162
+ this.#properties.selecteableText = value;
163
+ }
164
+ }
165
+
166
+ set acceptButton(value) {
167
+ if (typeof (value) == "object" && value != null && value.constructor.name == "WUIButton") {
168
+ this.#properties.acceptButton = value;
169
+ }
170
+ }
171
+
172
+ set acceptVisible(value) {
173
+ if (typeof (value) == "boolean") {
174
+ this.#properties.acceptVisible = value;
175
+ }
176
+ }
177
+
178
+ set acceptData(value) {
179
+ if (typeof (value) == "object") {
180
+ this.#properties.acceptData = value;
181
+ }
182
+ }
183
+
184
+ set acceptText(value) {
185
+ if (typeof (value) == "string") {
186
+ this.#properties.acceptText = value;
187
+ }
188
+ }
189
+
190
+ set acceptOnClick(value) {
191
+ if (typeof (value) == "function" || value == null) {
192
+ this.#properties.acceptOnClick = value;
193
+ }
194
+ }
195
+
196
+ set cancelButton(value) {
197
+ if (typeof (value) == "object" && value != null && value.constructor.name == "WUIButton") {
198
+ this.#properties.cancelButton = value;
199
+ }
200
+ }
201
+
202
+ set cancelVisible(value) {
203
+ if (typeof (value) == "boolean") {
204
+ this.#properties.cancelVisible = value;
205
+ }
206
+ }
207
+
208
+ set cancelData(value) {
209
+ if (typeof (value) == "object") {
210
+ this.#properties.cancelData = value;
211
+ }
212
+ }
213
+
214
+ set cancelText(value) {
215
+ if (typeof (value) == "string") {
216
+ this.#properties.cancelText = value;
217
+ }
218
+ }
219
+
220
+ set cancelOnClick(value) {
221
+ if (typeof (value) == "function" || value == null) {
222
+ this.#properties.cancelOnClick = value;
223
+ }
224
+ }
225
+
226
+ set onSelect(value) {
227
+ if (typeof (value) == "function" || value == null) {
228
+ this.#properties.onSelect = value;
229
+ }
230
+ }
231
+
232
+ #initHTML() {
233
+ if (!document.querySelector(this.selector + " > .box")) {
234
+ this.#buildHTML();
235
+ } else {
236
+ this.#loadHTML();
237
+ }
238
+ }
239
+
240
+ #buildHTML() {
241
+ this.#htmlElements.box = document.createElement("div");
242
+ this.#htmlElements.options = document.createElement("div");
243
+ this.#htmlElements.footer = document.createElement("div");
244
+ this.#htmlElements.cancelButton = document.createElement("button");
245
+ this.#htmlElements.acceptButton = document.createElement("button");
246
+ this.getElement().classList.add("wui-modal", "wuiplugin-selector", "mobile", "priority");
247
+ this.getElement().appendChild(this.#htmlElements.box);
248
+ this.#htmlElements.box.classList.add("box");
249
+ this.#htmlElements.box.appendChild(this.#htmlElements.options);
250
+ this.#htmlElements.box.appendChild(this.#htmlElements.footer);
251
+ this.#htmlElements.options.classList.add("options");
252
+ this.#htmlElements.footer.classList.add("footer");
253
+ this.#htmlElements.footer.appendChild(this.#htmlElements.cancelButton);
254
+ this.#htmlElements.footer.appendChild(this.#htmlElements.acceptButton);
255
+ this.#htmlElements.cancelButton.classList.add("wui-button", "cancel", "wui-language", "flat");
256
+ this.#htmlElements.cancelButton.textContent = this.#properties.cancelText;
257
+ this.#htmlElements.acceptButton.classList.add("wui-button", "submit", "wui-language");
258
+ this.#htmlElements.acceptButton.textContent = this.#properties.acceptText;
259
+ Object.entries(this.#properties.cancelData).forEach(([key, value]) => {
260
+ this.#htmlElements.cancelButton.dataset[key] = value;
261
+ });
262
+ Object.entries(this.#properties.acceptData).forEach(([key, value]) => {
263
+ this.#htmlElements.acceptButton.dataset[key] = value;
264
+ });
265
+ }
266
+
267
+ #loadHTML() {
268
+ this.#htmlElements.box = document.querySelector(this.selector + " > .box");
269
+ this.#htmlElements.options = document.querySelector(this.selector + " > .box > .options");
270
+ this.#htmlElements.footer = document.querySelector(this.selector + " > .box > .footer");
271
+ this.#htmlElements.cancelButton = document.querySelector(this.selector + " > .box > .footer > button.cancel");
272
+ this.#htmlElements.acceptButton = document.querySelector(this.selector + " > .box > .footer > button.submit");
273
+ }
274
+
275
+ init() {
276
+ this.#initHTML();
277
+ super.init();
278
+ this.acceptButton = new WUIButton({ selector: this.selector + " > .box > .footer > button.submit" });
279
+ this.cancelButton = new WUIButton({ selector: this.selector + " > .box > .footer > button.cancel" });
280
+ this.acceptButton.onClick = () => {
281
+ let indexes = [];
282
+ let values = [];
283
+ let texts = [];
284
+ this.#htmlElements.options.querySelectorAll(".option.selected").forEach(option => {
285
+ indexes.push(option.dataset.index);
286
+ values.push(option.dataset.value);
287
+ texts.push(option.dataset.text);
288
+ });
289
+ this.value = values.join(this.separatorValue);
290
+ if (this.#input != null) {
291
+ if (this.multiple) {
292
+ Array.from(this.#input.options).forEach(option => {
293
+ option.selected = values.includes(option.value);
294
+ });
295
+ } else {
296
+ this.#input.value = this.value;
297
+ }
298
+ this.#input.dispatchEvent(new Event("change"));
299
+ }
300
+ if (typeof (this.acceptOnClick) == "function") {
301
+ this.acceptOnClick(indexes.join(this.separatorValue), this.value, texts.join(this.separatorText));
302
+ }
303
+ this.close();
304
+ };
305
+ this.acceptButton.init();
306
+ this.cancelButton.onClick = () => {
307
+ if (typeof (this.cancelOnClick) == "function") {
308
+ this.cancelOnClick();
309
+ }
310
+ this.close();
311
+ };
312
+ this.cancelButton.init();
313
+ }
314
+
315
+ prepareInput(input, options = {}) {
316
+ if (typeof (input) == "object" && input instanceof HTMLElement && input.tagName.toLowerCase() == "select") {
317
+ const defaults = {
318
+ emptyText: this.emptyText,
319
+ direction: "ltr",
320
+ force: false
321
+ };
322
+ Object.entries(defaults).forEach(([name, value]) => {
323
+ if (typeof (options[name]) == "undefined") {
324
+ options[name] = value;
325
+ }
326
+ });
327
+ input.style.position = "relative";
328
+ input.style.zIndex = 1;
329
+ input._touches = [];
330
+ input._drag = false;
331
+ input.addEventListener("touchstart", event => {
332
+ input._touches = event.touches || event.targetTouches;
333
+ input._drag = false;
334
+ });
335
+ input.addEventListener("touchmove", () => {
336
+ input._touches = [];
337
+ input._drag = true;
338
+ });
339
+ input.addEventListener("touchend", event => {
340
+ const mobile = Boolean(window.matchMedia("(max-width: 767px)").matches);
341
+ if (mobile || options.force) {
342
+ if (!input._drag) {
343
+ if (event.cancelable) {
344
+ event.preventDefault();
345
+ }
346
+ const values = input.value.split(",");
347
+ const rect = input.getBoundingClientRect();
348
+ const rightTouch = event.target.clientWidth - (input._touches[0].clientX - rect.left);
349
+ input.setAttribute("dir", options.direction);
350
+ input.querySelectorAll("option").forEach(option => {
351
+ option.style.display = "none";
352
+ });
353
+ if (rightTouch <= 30) {
354
+ this.#input = input;
355
+ this.value = input.value;
356
+ this.options = [];
357
+ this.selecteableText = false;
358
+ this.#input.querySelectorAll("option").forEach(option => {
359
+ this.options.push({
360
+ icon: null,
361
+ text: option.text || options.emptyText || "",
362
+ value: option.value || "",
363
+ selected: values.indexOf(option.value) > -1 ? true : false
364
+ });
365
+ });
366
+ this.acceptVisible = true;
367
+ this.cancelVisible = true;
368
+ this.open();
369
+ }
370
+ }
371
+ }
372
+ });
373
+ }
374
+ }
375
+
376
+ open() {
377
+ const options = this.#htmlElements.box.querySelector(".options");
378
+ let index = 0;
379
+ options.innerHTML = "";
380
+ if (Array.isArray(this.options)) {
381
+ this.options.forEach((opt, i) => {
382
+ const option = document.createElement("div");
383
+ const icon = document.createElement("div");
384
+ const text = document.createElement("div");
385
+ const enabled = typeof (opt.enabled) == "boolean" && !opt.enabled ? false : true;
386
+ const selected = Boolean(opt.selected);
387
+ icon.className = "icon " + (typeof (opt.icon) == "string" && opt.icon != "" ? opt.icon : "wui-icon check-line");
388
+ text.className = "text " + (this.selecteableText ? "selecteable" : "");
389
+ text.innerHTML = opt.value == "" ? "<i class='empty'>" + this.emptyText + "</i>" : opt.text;
390
+ option.className = "option" + (selected ? " selected" : "");
391
+ option.dataset.index = i;
392
+ option.dataset.value = opt.value;
393
+ option.dataset.text = opt.text;
394
+ option.addEventListener("click", () => {
395
+ if (enabled) {
396
+ const index = option.dataset.index;
397
+ const value = option.dataset.value;
398
+ options.querySelectorAll(".option").forEach((opt, j) => {
399
+ if (opt.dataset.value == value) {
400
+ opt.classList.add("selected");
401
+ this.options[j].selected = true;
402
+ } else {
403
+ opt.classList.remove("selected");
404
+ opt.dataset.selected = false;
405
+ }
406
+ });
407
+ if (typeof (this.onSelect) == "function") {
408
+ this.onSelect(value, index);
409
+ }
410
+ }
411
+ });
412
+ option.appendChild(icon);
413
+ option.appendChild(text);
414
+ if (!enabled) {
415
+ option.classList.add("disabled");
416
+ }
417
+ options.appendChild(option);
418
+ if (selected) {
419
+ index = i;
420
+ }
421
+ });
422
+ }
423
+ if (this.acceptVisible || this.cancelVisible) {
424
+ this.getFooter().classList.remove("hidden");
425
+ } else {
426
+ this.getFooter().classList.add("hidden");
427
+ }
428
+ if (this.acceptVisible) {
429
+ this.acceptButton.getElement().classList.remove("hidden");
430
+ } else {
431
+ this.acceptButton.getElement().classList.add("hidden");
432
+ }
433
+ if (this.cancelVisible) {
434
+ this.cancelButton.getElement().classList.remove("hidden");
435
+ } else {
436
+ this.cancelButton.getElement().classList.add("hidden");
437
+ }
438
+ super.open(() => {
439
+ const top = index * this.#htmlElements.options.querySelectorAll(".option")[index].offsetHeight;
440
+ options.scrollTop = top;
441
+ });
442
+ }
443
+
444
+ close() {
445
+ super.close();
446
+ if (this.#input != null) {
447
+ this.#input.setAttribute("dir", "ltr");
448
+ this.#input.querySelectorAll("option").forEach(opt => {
449
+ opt.style.display = "block";
450
+ });
451
+ }
452
+ this.#properties.acceptVisible = true;
453
+ this.#properties.acceptOnClick = null;
454
+ this.#properties.cancelVisible = true;
455
+ this.#properties.cancelOnClick = null;
456
+ this.onOpen = null;
457
+ this.onClose = null;
458
+ this.#properties.onSelect = null;
459
+ this.#input = null;
460
+ }
461
+
462
+ destroy() {
463
+ if (this.getElement() instanceof HTMLElement) {
464
+ Object.entries(this.#htmlElements).forEach(([key, element]) => {
465
+ if (element) {
466
+ element.remove();
467
+ }
468
+ this.#htmlElements[key] = null;
469
+ });
470
+ }
471
+ Object.keys(this.#properties).forEach(name => {
472
+ delete this.#properties[name];
473
+ });
474
+ this.#input = undefined;
475
+ super.destroy();
476
+ }
477
+ }
478
+
479
+ /*
480
+ HTML output:
481
+ <div class="wui-modal [priority] wuiplugin-selector">
482
+ <div class="box">
483
+ <div class="options">
484
+ <div class="option"></div>
485
+ [...]
486
+ </div>
487
+ <div class="footer">
488
+ <button class="wui-button cancel flat wui-language" data-key="buttons.cancel"></button>
489
+ <button class="wui-button submit wui-language" data-key="buttons.accept"></button>
490
+ </div>
491
+ </div>
492
+ </div>
493
+ */