selective-ui 1.0.3 → 1.0.4
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.
- package/dist/selective-ui.esm.js +153 -14
- package/dist/selective-ui.esm.js.map +1 -1
- package/dist/selective-ui.esm.min.js +1 -1
- package/dist/selective-ui.esm.min.js.br +0 -0
- package/dist/selective-ui.min.js +2 -2
- package/dist/selective-ui.min.js.br +0 -0
- package/dist/selective-ui.umd.js +153 -14
- package/dist/selective-ui.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/js/components/selectbox.js +69 -12
- package/src/js/core/search-controller.js +82 -0
- package/src/js/index.js +1 -1
package/package.json
CHANGED
|
@@ -137,13 +137,7 @@ export class SelectBox {
|
|
|
137
137
|
tag: {
|
|
138
138
|
node: "div",
|
|
139
139
|
classList: "selective-ui-view",
|
|
140
|
-
tabIndex: 0,
|
|
141
|
-
role: "combobox",
|
|
142
|
-
ariaExpanded: "false",
|
|
143
|
-
ariaLabelledby: options.SEID_HOLDER,
|
|
144
|
-
ariaControls: options.SEID_LIST,
|
|
145
|
-
ariaHaspopup: "true",
|
|
146
|
-
ariaMultiselectable: options.multiple ? "true" : "false",
|
|
140
|
+
tabIndex: 0,
|
|
147
141
|
onkeydown: (e) => {
|
|
148
142
|
if (e.key === "Enter" || e.key === " " || e.key === "ArrowDown") {
|
|
149
143
|
e.preventDefault();
|
|
@@ -463,10 +457,21 @@ export class SelectBox {
|
|
|
463
457
|
},
|
|
464
458
|
setValue(evtToken = null, value, trigger = true, force = false) {
|
|
465
459
|
!Array.isArray(value) && (value = [value]);
|
|
460
|
+
|
|
461
|
+
value = value.filter(v => v !== "" && v != null);
|
|
462
|
+
|
|
463
|
+
if (value.length === 0) {
|
|
464
|
+
superThis.getModelOption().forEach(modelOption => {
|
|
465
|
+
modelOption["selectedNonTrigger"] = false;
|
|
466
|
+
});
|
|
467
|
+
this.change(false, trigger);
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
466
470
|
|
|
467
471
|
if (bindedOptions.multiple && bindedOptions.maxSelected > 0) {
|
|
468
472
|
if (value.length > bindedOptions.maxSelected) {
|
|
469
|
-
|
|
473
|
+
console.warn(`Cannot select more than ${bindedOptions.maxSelected} items`);
|
|
474
|
+
return;
|
|
470
475
|
}
|
|
471
476
|
}
|
|
472
477
|
|
|
@@ -474,12 +479,58 @@ export class SelectBox {
|
|
|
474
479
|
return;
|
|
475
480
|
}
|
|
476
481
|
|
|
482
|
+
if (container.searchController?.isAjax()) {
|
|
483
|
+
const { existing, missing } = container.searchController.checkMissingValues(value);
|
|
484
|
+
|
|
485
|
+
if (missing.length > 0) {
|
|
486
|
+
console.log(`Loading ${missing.length} missing values from server...`);
|
|
487
|
+
|
|
488
|
+
(async () => {
|
|
489
|
+
if (bindedOptions.loadingfield) {
|
|
490
|
+
container.popup?.showLoading();
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
try {
|
|
494
|
+
const result = await container.searchController.loadByValues(missing);
|
|
495
|
+
|
|
496
|
+
if (result.success && result.items.length > 0) {
|
|
497
|
+
result.items.forEach(item => {
|
|
498
|
+
if (missing.includes(item.value)) {
|
|
499
|
+
item.selected = true;
|
|
500
|
+
}
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
container.searchController['#applyAjaxResult'](
|
|
504
|
+
result.items,
|
|
505
|
+
true,
|
|
506
|
+
true
|
|
507
|
+
);
|
|
508
|
+
|
|
509
|
+
setTimeout(() => {
|
|
510
|
+
superThis.getModelOption().forEach(modelOption => {
|
|
511
|
+
modelOption["selectedNonTrigger"] = value.some(v => v == modelOption["value"]);
|
|
512
|
+
});
|
|
513
|
+
this.change(false, false);
|
|
514
|
+
}, 100);
|
|
515
|
+
} else if (missing.length > 0) {
|
|
516
|
+
console.warn(`Could not load ${missing.length} values:`, missing);
|
|
517
|
+
}
|
|
518
|
+
} catch (error) {
|
|
519
|
+
console.error("Error loading missing values:", error);
|
|
520
|
+
} finally {
|
|
521
|
+
if (bindedOptions.loadingfield) {
|
|
522
|
+
container.popup?.hideLoading();
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
})();
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
477
529
|
if (trigger) {
|
|
478
530
|
const beforeChangeToken = iEvents.callEvent([this], ...bindedOptions.on.beforeChange);
|
|
479
531
|
if (beforeChangeToken.isCancel) {
|
|
480
532
|
return;
|
|
481
533
|
}
|
|
482
|
-
|
|
483
534
|
superThis.oldValue = this.value;
|
|
484
535
|
}
|
|
485
536
|
|
|
@@ -487,7 +538,7 @@ export class SelectBox {
|
|
|
487
538
|
modelOption["selectedNonTrigger"] = value.some(v => v == modelOption["value"]);
|
|
488
539
|
});
|
|
489
540
|
|
|
490
|
-
if (!bindedOptions.multiple){
|
|
541
|
+
if (!bindedOptions.multiple && value.length > 0) {
|
|
491
542
|
container.targetElement.value = value[0];
|
|
492
543
|
}
|
|
493
544
|
|
|
@@ -539,8 +590,14 @@ export class SelectBox {
|
|
|
539
590
|
|
|
540
591
|
container.popup.open();
|
|
541
592
|
container.searchbox.show();
|
|
542
|
-
|
|
543
|
-
|
|
593
|
+
const ViewPanel = /** @type {HTMLElement} */ (container.tags.ViewPanel);
|
|
594
|
+
ViewPanel.setAttribute("aria-expanded", "true");
|
|
595
|
+
ViewPanel.setAttribute("aria-controls", bindedOptions.SEID_LIST);
|
|
596
|
+
ViewPanel.setAttribute("aria-haspopup", "listbox");
|
|
597
|
+
ViewPanel.setAttribute("aria-labelledby", bindedOptions.SEID_HOLDER);
|
|
598
|
+
if (bindedOptions.multiple) {
|
|
599
|
+
ViewPanel.setAttribute("aria-multiselectable", "true");
|
|
600
|
+
}
|
|
544
601
|
|
|
545
602
|
iEvents.callEvent([this], ...bindedOptions.on.show);
|
|
546
603
|
|
|
@@ -46,6 +46,88 @@ export class SearchController {
|
|
|
46
46
|
return !(!this.#ajaxConfig);
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Load specific options by their values from server
|
|
51
|
+
* @param {string|string[]} values - Values to load
|
|
52
|
+
* @returns {Promise<{success: boolean, items: Array, message?: string}>}
|
|
53
|
+
*/
|
|
54
|
+
async loadByValues(values) {
|
|
55
|
+
if (!this.#ajaxConfig) {
|
|
56
|
+
return { success: false, items: [], message: "Ajax not configured" };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const valuesArray = Array.isArray(values) ? values : [values];
|
|
60
|
+
if (valuesArray.length === 0) {
|
|
61
|
+
return { success: true, items: [] };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
const cfg = this.#ajaxConfig;
|
|
66
|
+
|
|
67
|
+
let payload;
|
|
68
|
+
if (typeof cfg.dataByValues === "function") {
|
|
69
|
+
payload = cfg.dataByValues(valuesArray);
|
|
70
|
+
} else {
|
|
71
|
+
payload = {
|
|
72
|
+
values: valuesArray.join(","),
|
|
73
|
+
load_by_values: "1",
|
|
74
|
+
...(typeof cfg.data === "function" ? cfg.data("", 0) : (cfg.data || {}))
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
let response;
|
|
79
|
+
if (cfg.method === "POST") {
|
|
80
|
+
const formData = new URLSearchParams();
|
|
81
|
+
Object.keys(payload).forEach(key => {
|
|
82
|
+
formData.append(key, payload[key]);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
response = await fetch(cfg.url, {
|
|
86
|
+
method: "POST",
|
|
87
|
+
body: formData,
|
|
88
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" }
|
|
89
|
+
});
|
|
90
|
+
} else {
|
|
91
|
+
const params = new URLSearchParams(payload).toString();
|
|
92
|
+
response = await fetch(`${cfg.url}?${params}`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (!response.ok) {
|
|
96
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const data = await response.json();
|
|
100
|
+
const result = this.#parseResponse(data);
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
success: true,
|
|
104
|
+
items: result.items
|
|
105
|
+
};
|
|
106
|
+
} catch (error) {
|
|
107
|
+
console.error("Load by values error:", error);
|
|
108
|
+
return {
|
|
109
|
+
success: false,
|
|
110
|
+
message: error.message,
|
|
111
|
+
items: []
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Check if values exist in current options
|
|
118
|
+
* @param {string[]} values - Values to check
|
|
119
|
+
* @returns {{existing: string[], missing: string[]}}
|
|
120
|
+
*/
|
|
121
|
+
checkMissingValues(values) {
|
|
122
|
+
const allOptions = Array.from(this.#select.options);
|
|
123
|
+
const existingValues = allOptions.map(opt => opt.value);
|
|
124
|
+
|
|
125
|
+
const existing = values.filter(v => existingValues.includes(v));
|
|
126
|
+
const missing = values.filter(v => !existingValues.includes(v));
|
|
127
|
+
|
|
128
|
+
return { existing, missing };
|
|
129
|
+
}
|
|
130
|
+
|
|
49
131
|
/**
|
|
50
132
|
* Configures AJAX settings used for remote searching and pagination.
|
|
51
133
|
*
|
package/src/js/index.js
CHANGED
|
@@ -26,7 +26,7 @@ import { checkDuplicate, markLoaded } from "./utils/guard";
|
|
|
26
26
|
import { Libs } from "./utils/libs";
|
|
27
27
|
import { Effector } from "./services/effector";
|
|
28
28
|
|
|
29
|
-
export const version = "1.0.
|
|
29
|
+
export const version = "1.0.4";
|
|
30
30
|
export const name = "SelectiveUI";
|
|
31
31
|
|
|
32
32
|
const alreadyLoaded = checkDuplicate(name);
|