selective-ui 1.0.2 → 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/LICENSE +21 -21
- package/README.md +7 -2
- package/dist/selective-ui.css +567 -567
- package/dist/selective-ui.css.map +1 -1
- package/dist/selective-ui.esm.js +6186 -6047
- 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 +6186 -6047
- package/dist/selective-ui.umd.js.map +1 -1
- package/package.json +68 -68
- package/src/css/components/accessorybox.css +63 -63
- package/src/css/components/directive.css +19 -19
- package/src/css/components/empty-state.css +25 -25
- package/src/css/components/loading-state.css +25 -25
- package/src/css/components/optgroup.css +61 -61
- package/src/css/components/option-handle.css +33 -33
- package/src/css/components/option.css +129 -129
- package/src/css/components/placeholder.css +14 -14
- package/src/css/components/popup.css +38 -38
- package/src/css/components/searchbox.css +28 -28
- package/src/css/components/selectbox.css +53 -53
- package/src/css/index.css +74 -74
- package/src/js/adapter/mixed-adapter.js +434 -434
- package/src/js/components/accessorybox.js +124 -124
- package/src/js/components/directive.js +37 -37
- package/src/js/components/empty-state.js +67 -67
- package/src/js/components/loading-state.js +59 -59
- package/src/js/components/option-handle.js +113 -113
- package/src/js/components/placeholder.js +56 -56
- package/src/js/components/popup.js +470 -470
- package/src/js/components/searchbox.js +167 -167
- package/src/js/components/selectbox.js +749 -692
- package/src/js/core/base/adapter.js +162 -162
- package/src/js/core/base/model.js +59 -59
- package/src/js/core/base/recyclerview.js +82 -82
- package/src/js/core/base/view.js +62 -62
- package/src/js/core/model-manager.js +286 -286
- package/src/js/core/search-controller.js +603 -521
- package/src/js/index.js +136 -136
- package/src/js/models/group-model.js +142 -142
- package/src/js/models/option-model.js +236 -236
- package/src/js/services/dataset-observer.js +73 -73
- package/src/js/services/ea-observer.js +87 -87
- package/src/js/services/effector.js +403 -403
- package/src/js/services/refresher.js +39 -39
- package/src/js/services/resize-observer.js +151 -151
- package/src/js/services/select-observer.js +60 -60
- package/src/js/types/adapter.type.js +32 -32
- package/src/js/types/effector.type.js +23 -23
- package/src/js/types/ievents.type.js +10 -10
- package/src/js/types/libs.type.js +27 -27
- package/src/js/types/model.type.js +11 -11
- package/src/js/types/recyclerview.type.js +11 -11
- package/src/js/types/resize-observer.type.js +18 -18
- package/src/js/types/view.group.type.js +12 -12
- package/src/js/types/view.option.type.js +14 -14
- package/src/js/types/view.type.js +10 -10
- package/src/js/utils/guard.js +46 -46
- package/src/js/utils/ievents.js +83 -83
- package/src/js/utils/istorage.js +60 -60
- package/src/js/utils/libs.js +618 -618
- package/src/js/utils/selective.js +385 -385
- package/src/js/views/group-view.js +102 -102
- package/src/js/views/option-view.js +152 -152
|
@@ -1,237 +1,237 @@
|
|
|
1
|
-
import { Model } from "../core/base/model.js";
|
|
2
|
-
import { iEvents } from "../utils/ievents.js";
|
|
3
|
-
import { Libs } from "../utils/libs.js";
|
|
4
|
-
import { OptionView } from "../views/option-view.js";
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* @extends {Model<HTMLOptionElement, OptionViewTags, OptionView>}
|
|
8
|
-
*/
|
|
9
|
-
export class OptionModel extends Model {
|
|
10
|
-
#privOnSelected = [];
|
|
11
|
-
#privOnInternalSelected = [];
|
|
12
|
-
#privOnVisibilityChanged = [];
|
|
13
|
-
#visible = true;
|
|
14
|
-
#highlighted = false;
|
|
15
|
-
|
|
16
|
-
/** @type {import('./group-model').GroupModel} */
|
|
17
|
-
group = null
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Returns the image source from dataset (imgsrc or image), or an empty string if absent.
|
|
21
|
-
*
|
|
22
|
-
* @type {string}
|
|
23
|
-
*/
|
|
24
|
-
get imageSrc() {
|
|
25
|
-
return this.dataset?.imgsrc || this.dataset?.image || "";
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Indicates whether this option has an associated image source.
|
|
30
|
-
*
|
|
31
|
-
* @type {boolean}
|
|
32
|
-
*/
|
|
33
|
-
get hasImage() {
|
|
34
|
-
return !!this.imageSrc;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Gets the option's current value from the underlying <option> element.
|
|
39
|
-
*
|
|
40
|
-
* @type {string}
|
|
41
|
-
*/
|
|
42
|
-
get value() {
|
|
43
|
-
return this.targetElement.value;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Gets whether the option is currently selected (proxied to the <option> element).
|
|
48
|
-
*
|
|
49
|
-
* @type {boolean}
|
|
50
|
-
*/
|
|
51
|
-
get selected() {
|
|
52
|
-
return this.targetElement.selected;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Sets the selected state and triggers external selection listeners.
|
|
57
|
-
* Uses selectedNonTrigger internally to update DOM/ARIA without firing external side effects first.
|
|
58
|
-
*
|
|
59
|
-
* @type {boolean}
|
|
60
|
-
*/
|
|
61
|
-
set selected(value) {
|
|
62
|
-
this.selectedNonTrigger = value;
|
|
63
|
-
|
|
64
|
-
iEvents.callEvent([this, value], ...this.#privOnSelected);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Gets whether the option is currently visible in the UI.
|
|
69
|
-
*
|
|
70
|
-
* @type {boolean}
|
|
71
|
-
*/
|
|
72
|
-
get visible() {
|
|
73
|
-
return this.#visible;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Sets the visibility state; toggles "hide" class on the view and notifies visibility listeners.
|
|
78
|
-
*
|
|
79
|
-
* @type {boolean}
|
|
80
|
-
*/
|
|
81
|
-
set visible(value) {
|
|
82
|
-
if (this.#visible === value) return;
|
|
83
|
-
this.#visible = value;
|
|
84
|
-
|
|
85
|
-
const view = this.view?.getView();
|
|
86
|
-
if (view) {
|
|
87
|
-
view.classList.toggle("hide", !value);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
iEvents.callEvent([this, value], ...this.#privOnVisibilityChanged);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Gets the selected state without triggering external listeners (alias of selected).
|
|
95
|
-
*
|
|
96
|
-
* @type {boolean}
|
|
97
|
-
*/
|
|
98
|
-
get selectedNonTrigger() {
|
|
99
|
-
return this.selected;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Sets the selected state and updates input checked, CSS classes, ARIA attributes,
|
|
104
|
-
* and the underlying <option> 'selected' attribute. Notifies internal selection listeners.
|
|
105
|
-
*
|
|
106
|
-
* @type {boolean}
|
|
107
|
-
*/
|
|
108
|
-
set selectedNonTrigger(value) {
|
|
109
|
-
const tag = this.view?.getTag("OptionInput");
|
|
110
|
-
const view = this.view?.getView();
|
|
111
|
-
|
|
112
|
-
if (tag) {
|
|
113
|
-
tag.checked = value;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
if (view) {
|
|
117
|
-
view.classList.toggle("checked", !!value);
|
|
118
|
-
view.setAttribute("aria-selected", value ? "true" : "false");
|
|
119
|
-
this.targetElement.toggleAttribute("selected", !!value);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
this.targetElement.selected = value;
|
|
123
|
-
iEvents.callEvent([this, value], ...this.#privOnInternalSelected);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Returns the display text for the option, applying tag translation and optional HTML allowance.
|
|
128
|
-
* If allowHtml=false, returns stripped/sanitized text.
|
|
129
|
-
*
|
|
130
|
-
* @type {string}
|
|
131
|
-
*/
|
|
132
|
-
get text() {
|
|
133
|
-
const text = Libs.tagTranslate(this.dataset?.mask ?? this.targetElement.text);
|
|
134
|
-
return this.options.allowHtml ? text : Libs.stripHtml(text);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Returns a plain-text version of the display text (trimmed),
|
|
139
|
-
* stripping HTML if allowHtml is true, otherwise the raw text.
|
|
140
|
-
*
|
|
141
|
-
* @type {string}
|
|
142
|
-
*/
|
|
143
|
-
get textContent() {
|
|
144
|
-
return this.options.allowHtml ? Libs.stripHtml(this.text).trim() : this.text.trim();
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* Returns the dataset object of the underlying <option> element, or an empty object.
|
|
149
|
-
*
|
|
150
|
-
* @type {DOMStringMap|Record<string, string>}
|
|
151
|
-
*/
|
|
152
|
-
get dataset() {
|
|
153
|
-
return this.targetElement.dataset ?? {};
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Gets whether the option is currently highlighted (e.g., via keyboard navigation).
|
|
158
|
-
*
|
|
159
|
-
* @type {boolean}
|
|
160
|
-
*/
|
|
161
|
-
get highlighted() {
|
|
162
|
-
return this.#highlighted;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* Sets the highlighted state and toggles the "highlight" CSS class on the view.
|
|
168
|
-
* Always syncs the DOM class even if the state is unchanged.
|
|
169
|
-
*
|
|
170
|
-
* @type {boolean}
|
|
171
|
-
*/
|
|
172
|
-
set highlighted(value) {
|
|
173
|
-
const val = !!value;
|
|
174
|
-
const view = this.view?.getView?.();
|
|
175
|
-
|
|
176
|
-
if (this.#highlighted !== val) {
|
|
177
|
-
this.#highlighted = val;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
if (view) {
|
|
181
|
-
view.classList.toggle('highlight', val);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* Registers a listener invoked when external selection changes (via setter `selected`).
|
|
188
|
-
*
|
|
189
|
-
* @param {(evtToken: any, el: OptionModel, selected: boolean) => void} callback - Selection listener.
|
|
190
|
-
*/
|
|
191
|
-
onSelected(callback) {
|
|
192
|
-
this.#privOnSelected.push(callback);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Registers a listener invoked when internal selection changes (via setter `selectedNonTrigger`).
|
|
197
|
-
*
|
|
198
|
-
* @param {(evtToken: any, el: OptionModel, selected: boolean) => void} callback - Internal selection listener.
|
|
199
|
-
*/
|
|
200
|
-
onInternalSelected(callback) {
|
|
201
|
-
this.#privOnInternalSelected.push(callback);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
/**
|
|
205
|
-
* Registers a listener invoked when visibility changes (via setter `visible`).
|
|
206
|
-
*
|
|
207
|
-
* @param {(evtToken: any, model: OptionModel, visible: boolean) => void} callback - Visibility listener.
|
|
208
|
-
*/
|
|
209
|
-
onVisibilityChanged(callback) {
|
|
210
|
-
this.#privOnVisibilityChanged.push(callback);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* Hook called when the target <option> element changes.
|
|
215
|
-
* Updates label content (HTML or text), image src/alt if present,
|
|
216
|
-
* and synchronizes initial selected state to the view.
|
|
217
|
-
*/
|
|
218
|
-
onTargetChanged() {
|
|
219
|
-
const labelContent = this.view.getTag("LabelContent");
|
|
220
|
-
if (labelContent) {
|
|
221
|
-
if (this.options.allowHtml) {
|
|
222
|
-
labelContent.innerHTML = this.text;
|
|
223
|
-
}
|
|
224
|
-
else {
|
|
225
|
-
labelContent.textContent = this.textContent;
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
const imageTag = this.view.getTag("OptionImage");
|
|
230
|
-
if (imageTag && this.hasImage) {
|
|
231
|
-
imageTag.src = this.imageSrc;
|
|
232
|
-
imageTag.alt = this.text;
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
this.selectedNonTrigger = this.targetElement.selected;
|
|
236
|
-
}
|
|
1
|
+
import { Model } from "../core/base/model.js";
|
|
2
|
+
import { iEvents } from "../utils/ievents.js";
|
|
3
|
+
import { Libs } from "../utils/libs.js";
|
|
4
|
+
import { OptionView } from "../views/option-view.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @extends {Model<HTMLOptionElement, OptionViewTags, OptionView>}
|
|
8
|
+
*/
|
|
9
|
+
export class OptionModel extends Model {
|
|
10
|
+
#privOnSelected = [];
|
|
11
|
+
#privOnInternalSelected = [];
|
|
12
|
+
#privOnVisibilityChanged = [];
|
|
13
|
+
#visible = true;
|
|
14
|
+
#highlighted = false;
|
|
15
|
+
|
|
16
|
+
/** @type {import('./group-model').GroupModel} */
|
|
17
|
+
group = null
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Returns the image source from dataset (imgsrc or image), or an empty string if absent.
|
|
21
|
+
*
|
|
22
|
+
* @type {string}
|
|
23
|
+
*/
|
|
24
|
+
get imageSrc() {
|
|
25
|
+
return this.dataset?.imgsrc || this.dataset?.image || "";
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Indicates whether this option has an associated image source.
|
|
30
|
+
*
|
|
31
|
+
* @type {boolean}
|
|
32
|
+
*/
|
|
33
|
+
get hasImage() {
|
|
34
|
+
return !!this.imageSrc;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Gets the option's current value from the underlying <option> element.
|
|
39
|
+
*
|
|
40
|
+
* @type {string}
|
|
41
|
+
*/
|
|
42
|
+
get value() {
|
|
43
|
+
return this.targetElement.value;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Gets whether the option is currently selected (proxied to the <option> element).
|
|
48
|
+
*
|
|
49
|
+
* @type {boolean}
|
|
50
|
+
*/
|
|
51
|
+
get selected() {
|
|
52
|
+
return this.targetElement.selected;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Sets the selected state and triggers external selection listeners.
|
|
57
|
+
* Uses selectedNonTrigger internally to update DOM/ARIA without firing external side effects first.
|
|
58
|
+
*
|
|
59
|
+
* @type {boolean}
|
|
60
|
+
*/
|
|
61
|
+
set selected(value) {
|
|
62
|
+
this.selectedNonTrigger = value;
|
|
63
|
+
|
|
64
|
+
iEvents.callEvent([this, value], ...this.#privOnSelected);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Gets whether the option is currently visible in the UI.
|
|
69
|
+
*
|
|
70
|
+
* @type {boolean}
|
|
71
|
+
*/
|
|
72
|
+
get visible() {
|
|
73
|
+
return this.#visible;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Sets the visibility state; toggles "hide" class on the view and notifies visibility listeners.
|
|
78
|
+
*
|
|
79
|
+
* @type {boolean}
|
|
80
|
+
*/
|
|
81
|
+
set visible(value) {
|
|
82
|
+
if (this.#visible === value) return;
|
|
83
|
+
this.#visible = value;
|
|
84
|
+
|
|
85
|
+
const view = this.view?.getView();
|
|
86
|
+
if (view) {
|
|
87
|
+
view.classList.toggle("hide", !value);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
iEvents.callEvent([this, value], ...this.#privOnVisibilityChanged);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Gets the selected state without triggering external listeners (alias of selected).
|
|
95
|
+
*
|
|
96
|
+
* @type {boolean}
|
|
97
|
+
*/
|
|
98
|
+
get selectedNonTrigger() {
|
|
99
|
+
return this.selected;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Sets the selected state and updates input checked, CSS classes, ARIA attributes,
|
|
104
|
+
* and the underlying <option> 'selected' attribute. Notifies internal selection listeners.
|
|
105
|
+
*
|
|
106
|
+
* @type {boolean}
|
|
107
|
+
*/
|
|
108
|
+
set selectedNonTrigger(value) {
|
|
109
|
+
const tag = this.view?.getTag("OptionInput");
|
|
110
|
+
const view = this.view?.getView();
|
|
111
|
+
|
|
112
|
+
if (tag) {
|
|
113
|
+
tag.checked = value;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (view) {
|
|
117
|
+
view.classList.toggle("checked", !!value);
|
|
118
|
+
view.setAttribute("aria-selected", value ? "true" : "false");
|
|
119
|
+
this.targetElement.toggleAttribute("selected", !!value);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
this.targetElement.selected = value;
|
|
123
|
+
iEvents.callEvent([this, value], ...this.#privOnInternalSelected);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Returns the display text for the option, applying tag translation and optional HTML allowance.
|
|
128
|
+
* If allowHtml=false, returns stripped/sanitized text.
|
|
129
|
+
*
|
|
130
|
+
* @type {string}
|
|
131
|
+
*/
|
|
132
|
+
get text() {
|
|
133
|
+
const text = Libs.tagTranslate(this.dataset?.mask ?? this.targetElement.text);
|
|
134
|
+
return this.options.allowHtml ? text : Libs.stripHtml(text);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Returns a plain-text version of the display text (trimmed),
|
|
139
|
+
* stripping HTML if allowHtml is true, otherwise the raw text.
|
|
140
|
+
*
|
|
141
|
+
* @type {string}
|
|
142
|
+
*/
|
|
143
|
+
get textContent() {
|
|
144
|
+
return this.options.allowHtml ? Libs.stripHtml(this.text).trim() : this.text.trim();
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Returns the dataset object of the underlying <option> element, or an empty object.
|
|
149
|
+
*
|
|
150
|
+
* @type {DOMStringMap|Record<string, string>}
|
|
151
|
+
*/
|
|
152
|
+
get dataset() {
|
|
153
|
+
return this.targetElement.dataset ?? {};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Gets whether the option is currently highlighted (e.g., via keyboard navigation).
|
|
158
|
+
*
|
|
159
|
+
* @type {boolean}
|
|
160
|
+
*/
|
|
161
|
+
get highlighted() {
|
|
162
|
+
return this.#highlighted;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Sets the highlighted state and toggles the "highlight" CSS class on the view.
|
|
168
|
+
* Always syncs the DOM class even if the state is unchanged.
|
|
169
|
+
*
|
|
170
|
+
* @type {boolean}
|
|
171
|
+
*/
|
|
172
|
+
set highlighted(value) {
|
|
173
|
+
const val = !!value;
|
|
174
|
+
const view = this.view?.getView?.();
|
|
175
|
+
|
|
176
|
+
if (this.#highlighted !== val) {
|
|
177
|
+
this.#highlighted = val;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (view) {
|
|
181
|
+
view.classList.toggle('highlight', val);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Registers a listener invoked when external selection changes (via setter `selected`).
|
|
188
|
+
*
|
|
189
|
+
* @param {(evtToken: any, el: OptionModel, selected: boolean) => void} callback - Selection listener.
|
|
190
|
+
*/
|
|
191
|
+
onSelected(callback) {
|
|
192
|
+
this.#privOnSelected.push(callback);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Registers a listener invoked when internal selection changes (via setter `selectedNonTrigger`).
|
|
197
|
+
*
|
|
198
|
+
* @param {(evtToken: any, el: OptionModel, selected: boolean) => void} callback - Internal selection listener.
|
|
199
|
+
*/
|
|
200
|
+
onInternalSelected(callback) {
|
|
201
|
+
this.#privOnInternalSelected.push(callback);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Registers a listener invoked when visibility changes (via setter `visible`).
|
|
206
|
+
*
|
|
207
|
+
* @param {(evtToken: any, model: OptionModel, visible: boolean) => void} callback - Visibility listener.
|
|
208
|
+
*/
|
|
209
|
+
onVisibilityChanged(callback) {
|
|
210
|
+
this.#privOnVisibilityChanged.push(callback);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Hook called when the target <option> element changes.
|
|
215
|
+
* Updates label content (HTML or text), image src/alt if present,
|
|
216
|
+
* and synchronizes initial selected state to the view.
|
|
217
|
+
*/
|
|
218
|
+
onTargetChanged() {
|
|
219
|
+
const labelContent = this.view.getTag("LabelContent");
|
|
220
|
+
if (labelContent) {
|
|
221
|
+
if (this.options.allowHtml) {
|
|
222
|
+
labelContent.innerHTML = this.text;
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
labelContent.textContent = this.textContent;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const imageTag = this.view.getTag("OptionImage");
|
|
230
|
+
if (imageTag && this.hasImage) {
|
|
231
|
+
imageTag.src = this.imageSrc;
|
|
232
|
+
imageTag.alt = this.text;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
this.selectedNonTrigger = this.targetElement.selected;
|
|
236
|
+
}
|
|
237
237
|
}
|
|
@@ -1,73 +1,73 @@
|
|
|
1
|
-
export class DatasetObserver {
|
|
2
|
-
/** @type {MutationObserver} */
|
|
3
|
-
#observer;
|
|
4
|
-
|
|
5
|
-
/** @type {HTMLElement} */
|
|
6
|
-
#element;
|
|
7
|
-
|
|
8
|
-
#debounceTimer = null;
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Observes data-* attribute changes on a target element and debounces notifications.
|
|
12
|
-
* Sets up a MutationObserver to detect dataset mutations and a fallback custom event listener.
|
|
13
|
-
*
|
|
14
|
-
* @param {HTMLElement} element - The element whose dataset (data-* attributes) will be observed.
|
|
15
|
-
*/
|
|
16
|
-
constructor(element) {
|
|
17
|
-
this.#element = element;
|
|
18
|
-
|
|
19
|
-
this.#observer = new MutationObserver((mutations) => {
|
|
20
|
-
let datasetChanged = false;
|
|
21
|
-
|
|
22
|
-
for (const mutation of mutations) {
|
|
23
|
-
if (
|
|
24
|
-
mutation.type === "attributes" &&
|
|
25
|
-
mutation.attributeName?.startsWith("data-")
|
|
26
|
-
) {
|
|
27
|
-
datasetChanged = true;
|
|
28
|
-
break;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
if (!datasetChanged) return;
|
|
33
|
-
|
|
34
|
-
clearTimeout(this.#debounceTimer);
|
|
35
|
-
this.#debounceTimer = setTimeout(() => {
|
|
36
|
-
this.onChanged({ ...this.#element.dataset });
|
|
37
|
-
}, 50);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
element.addEventListener("dataset:changed", () => {
|
|
41
|
-
this.onChanged({ ...this.#element.dataset });
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Starts observing the element for attribute changes, including old values.
|
|
47
|
-
* Uses MutationObserver to track updates to data-* attributes.
|
|
48
|
-
*/
|
|
49
|
-
connect() {
|
|
50
|
-
this.#observer.observe(this.#element, {
|
|
51
|
-
attributes: true,
|
|
52
|
-
attributeOldValue: true
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Callback invoked when the element's dataset changes (debounced).
|
|
58
|
-
* Override in subclasses to handle dataset updates.
|
|
59
|
-
*
|
|
60
|
-
* @param {DOMStringMap} dataset - A shallow copy of the element's current dataset.
|
|
61
|
-
*/
|
|
62
|
-
onChanged(dataset) {
|
|
63
|
-
// override
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Stops observing the element and clears any pending debounce timers.
|
|
68
|
-
*/
|
|
69
|
-
disconnect() {
|
|
70
|
-
clearTimeout(this.#debounceTimer);
|
|
71
|
-
this.#observer.disconnect();
|
|
72
|
-
}
|
|
73
|
-
}
|
|
1
|
+
export class DatasetObserver {
|
|
2
|
+
/** @type {MutationObserver} */
|
|
3
|
+
#observer;
|
|
4
|
+
|
|
5
|
+
/** @type {HTMLElement} */
|
|
6
|
+
#element;
|
|
7
|
+
|
|
8
|
+
#debounceTimer = null;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Observes data-* attribute changes on a target element and debounces notifications.
|
|
12
|
+
* Sets up a MutationObserver to detect dataset mutations and a fallback custom event listener.
|
|
13
|
+
*
|
|
14
|
+
* @param {HTMLElement} element - The element whose dataset (data-* attributes) will be observed.
|
|
15
|
+
*/
|
|
16
|
+
constructor(element) {
|
|
17
|
+
this.#element = element;
|
|
18
|
+
|
|
19
|
+
this.#observer = new MutationObserver((mutations) => {
|
|
20
|
+
let datasetChanged = false;
|
|
21
|
+
|
|
22
|
+
for (const mutation of mutations) {
|
|
23
|
+
if (
|
|
24
|
+
mutation.type === "attributes" &&
|
|
25
|
+
mutation.attributeName?.startsWith("data-")
|
|
26
|
+
) {
|
|
27
|
+
datasetChanged = true;
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (!datasetChanged) return;
|
|
33
|
+
|
|
34
|
+
clearTimeout(this.#debounceTimer);
|
|
35
|
+
this.#debounceTimer = setTimeout(() => {
|
|
36
|
+
this.onChanged({ ...this.#element.dataset });
|
|
37
|
+
}, 50);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
element.addEventListener("dataset:changed", () => {
|
|
41
|
+
this.onChanged({ ...this.#element.dataset });
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Starts observing the element for attribute changes, including old values.
|
|
47
|
+
* Uses MutationObserver to track updates to data-* attributes.
|
|
48
|
+
*/
|
|
49
|
+
connect() {
|
|
50
|
+
this.#observer.observe(this.#element, {
|
|
51
|
+
attributes: true,
|
|
52
|
+
attributeOldValue: true
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Callback invoked when the element's dataset changes (debounced).
|
|
58
|
+
* Override in subclasses to handle dataset updates.
|
|
59
|
+
*
|
|
60
|
+
* @param {DOMStringMap} dataset - A shallow copy of the element's current dataset.
|
|
61
|
+
*/
|
|
62
|
+
onChanged(dataset) {
|
|
63
|
+
// override
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Stops observing the element and clears any pending debounce timers.
|
|
68
|
+
*/
|
|
69
|
+
disconnect() {
|
|
70
|
+
clearTimeout(this.#debounceTimer);
|
|
71
|
+
this.#observer.disconnect();
|
|
72
|
+
}
|
|
73
|
+
}
|