selective-ui 1.0.2 → 1.0.3
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 +6046 -6046
- 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 +1 -1
- package/dist/selective-ui.min.js.br +0 -0
- package/dist/selective-ui.umd.js +6046 -6046
- 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 +692 -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 +521 -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,40 +1,40 @@
|
|
|
1
|
-
import {Libs} from "../utils/libs.js";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @class
|
|
5
|
-
*/
|
|
6
|
-
export class Refresher {
|
|
7
|
-
/**
|
|
8
|
-
* Provides a utility to resize the Select UI view panel based on the bound <select> element
|
|
9
|
-
* and configuration options. Applies explicit width/height if configured; otherwise uses the
|
|
10
|
-
* select's current offset size. Ensures minimum width/height constraints are respected.
|
|
11
|
-
*
|
|
12
|
-
* @param {HTMLSelectElement} select - The native select element used to derive dimensions.
|
|
13
|
-
* @param {HTMLElement} view - The view panel element whose styles will be updated.
|
|
14
|
-
*/
|
|
15
|
-
static resizeBox(select, view) {
|
|
16
|
-
const
|
|
17
|
-
bindedMap = Libs.getBinderMap(select),
|
|
18
|
-
options = bindedMap.options
|
|
19
|
-
;
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
minWidth = options.minWidth,
|
|
23
|
-
minHeight = options.minHeight,
|
|
24
|
-
cfgWidth = parseInt(options.width, 10),
|
|
25
|
-
cfgHeight = parseInt(options.height, 10)
|
|
26
|
-
;
|
|
27
|
-
|
|
28
|
-
let width = `${select.offsetWidth}px`,
|
|
29
|
-
height = `${select.offsetHeight}px`;
|
|
30
|
-
|
|
31
|
-
if (cfgWidth > 0) {
|
|
32
|
-
width = options.width;
|
|
33
|
-
}
|
|
34
|
-
if (cfgHeight > 0) {
|
|
35
|
-
height = options.height;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
Libs.setStyle(view, {width, height, minWidth, minHeight});
|
|
39
|
-
}
|
|
1
|
+
import {Libs} from "../utils/libs.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @class
|
|
5
|
+
*/
|
|
6
|
+
export class Refresher {
|
|
7
|
+
/**
|
|
8
|
+
* Provides a utility to resize the Select UI view panel based on the bound <select> element
|
|
9
|
+
* and configuration options. Applies explicit width/height if configured; otherwise uses the
|
|
10
|
+
* select's current offset size. Ensures minimum width/height constraints are respected.
|
|
11
|
+
*
|
|
12
|
+
* @param {HTMLSelectElement} select - The native select element used to derive dimensions.
|
|
13
|
+
* @param {HTMLElement} view - The view panel element whose styles will be updated.
|
|
14
|
+
*/
|
|
15
|
+
static resizeBox(select, view) {
|
|
16
|
+
const
|
|
17
|
+
bindedMap = Libs.getBinderMap(select),
|
|
18
|
+
options = bindedMap.options
|
|
19
|
+
;
|
|
20
|
+
|
|
21
|
+
const
|
|
22
|
+
minWidth = options.minWidth,
|
|
23
|
+
minHeight = options.minHeight,
|
|
24
|
+
cfgWidth = parseInt(options.width, 10),
|
|
25
|
+
cfgHeight = parseInt(options.height, 10)
|
|
26
|
+
;
|
|
27
|
+
|
|
28
|
+
let width = `${select.offsetWidth}px`,
|
|
29
|
+
height = `${select.offsetHeight}px`;
|
|
30
|
+
|
|
31
|
+
if (cfgWidth > 0) {
|
|
32
|
+
width = options.width;
|
|
33
|
+
}
|
|
34
|
+
if (cfgHeight > 0) {
|
|
35
|
+
height = options.height;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
Libs.setStyle(view, {width, height, minWidth, minHeight});
|
|
39
|
+
}
|
|
40
40
|
}
|
|
@@ -1,152 +1,152 @@
|
|
|
1
|
-
|
|
2
|
-
/**
|
|
3
|
-
* @class
|
|
4
|
-
*/
|
|
5
|
-
export class ResizeObserverService {
|
|
6
|
-
isInit = false;
|
|
7
|
-
element = null;
|
|
8
|
-
/** @type {ResizeObserver} */
|
|
9
|
-
#resizeObserver = null;
|
|
10
|
-
#mutationObserver = null;
|
|
11
|
-
#boundUpdateChanged;
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Initializes the service and binds the internal update handler to `this`.
|
|
15
|
-
* Sets the service to an initialized state.
|
|
16
|
-
*/
|
|
17
|
-
constructor() {
|
|
18
|
-
this.isInit = true
|
|
19
|
-
this.#boundUpdateChanged = this.#updateChanged.bind(this);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Callback invoked when the observed element's metrics change.
|
|
24
|
-
* Override to react to size/position/style updates.
|
|
25
|
-
*
|
|
26
|
-
* @param {ElementMetrics} metrics - Calculated box metrics (size, position, padding, border, margin).
|
|
27
|
-
*/
|
|
28
|
-
onChanged(metrics) {}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Computes the current metrics of the bound element (bounding rect + computed styles)
|
|
32
|
-
* and forwards them to `onChanged(metrics)`.
|
|
33
|
-
*
|
|
34
|
-
* @returns {void}
|
|
35
|
-
*/
|
|
36
|
-
#updateChanged() {
|
|
37
|
-
// Guard: nếu element chưa sẵn sàng hoặc không đo được, trả về metrics mặc định
|
|
38
|
-
const el = this.element;
|
|
39
|
-
if (!el || typeof el.getBoundingClientRect !== 'function') {
|
|
40
|
-
/** @type {ElementMetrics} */
|
|
41
|
-
const defaultMetrics = {
|
|
42
|
-
width: 0,
|
|
43
|
-
height: 0,
|
|
44
|
-
top: 0,
|
|
45
|
-
left: 0,
|
|
46
|
-
padding: { top: 0, right: 0, bottom: 0, left: 0 },
|
|
47
|
-
border: { top: 0, right: 0, bottom: 0, left: 0 },
|
|
48
|
-
margin: { top: 0, right: 0, bottom: 0, left: 0 },
|
|
49
|
-
};
|
|
50
|
-
this.onChanged(defaultMetrics);
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const rect = el.getBoundingClientRect();
|
|
55
|
-
const style = (typeof window?.getComputedStyle === 'function')
|
|
56
|
-
? window.getComputedStyle(el)
|
|
57
|
-
: null;
|
|
58
|
-
|
|
59
|
-
/** @type {ElementMetrics} */
|
|
60
|
-
const metrics = {
|
|
61
|
-
width: rect?.width ?? 0,
|
|
62
|
-
height: rect?.height ?? 0,
|
|
63
|
-
top: rect?.top ?? 0,
|
|
64
|
-
left: rect?.left ?? 0,
|
|
65
|
-
|
|
66
|
-
padding: {
|
|
67
|
-
top: parseFloat(style?.paddingTop ?? '0'),
|
|
68
|
-
right: parseFloat(style?.paddingRight ?? '0'),
|
|
69
|
-
bottom: parseFloat(style?.paddingBottom ?? '0'),
|
|
70
|
-
left: parseFloat(style?.paddingLeft ?? '0'),
|
|
71
|
-
},
|
|
72
|
-
|
|
73
|
-
border: {
|
|
74
|
-
top: parseFloat(style?.borderTopWidth ?? '0'),
|
|
75
|
-
right: parseFloat(style?.borderRightWidth ?? '0'),
|
|
76
|
-
bottom: parseFloat(style?.borderBottomWidth ?? '0'),
|
|
77
|
-
left: parseFloat(style?.borderLeftWidth ?? '0'),
|
|
78
|
-
},
|
|
79
|
-
|
|
80
|
-
margin: {
|
|
81
|
-
top: parseFloat(style?.marginTop ?? '0'),
|
|
82
|
-
right: parseFloat(style?.marginRight ?? '0'),
|
|
83
|
-
bottom: parseFloat(style?.marginBottom ?? '0'),
|
|
84
|
-
left: parseFloat(style?.marginLeft ?? '0'),
|
|
85
|
-
}
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
this.onChanged(metrics);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Manually triggers a metrics computation and notification via `onChanged`.
|
|
93
|
-
*/
|
|
94
|
-
trigger() {
|
|
95
|
-
this.#updateChanged();
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Starts observing the provided element for resize and style/class mutations,
|
|
100
|
-
* and listens to window/visualViewport scroll/resize to detect layout changes.
|
|
101
|
-
*
|
|
102
|
-
* @param {Element} element - The element to observe; must be a valid DOM Element.
|
|
103
|
-
* @throws {Error} If `element` is not an instance of Element.
|
|
104
|
-
*/
|
|
105
|
-
connect(element) {
|
|
106
|
-
if (!(element instanceof Element)) {
|
|
107
|
-
throw new Error("Element không hợp lệ");
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
this.element = element;
|
|
111
|
-
|
|
112
|
-
this.#resizeObserver = new ResizeObserver(this.#boundUpdateChanged);
|
|
113
|
-
this.#resizeObserver.observe(element);
|
|
114
|
-
|
|
115
|
-
this.#mutationObserver = new MutationObserver(this.#boundUpdateChanged);
|
|
116
|
-
this.#mutationObserver.observe(element, {
|
|
117
|
-
attributes: true,
|
|
118
|
-
attributeFilter: ["style", "class"]
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
window.addEventListener("scroll", this.#boundUpdateChanged, true);
|
|
122
|
-
window.addEventListener("resize", this.#boundUpdateChanged);
|
|
123
|
-
|
|
124
|
-
if (window.visualViewport) {
|
|
125
|
-
window.visualViewport.addEventListener("resize", this.#boundUpdateChanged);
|
|
126
|
-
window.visualViewport.addEventListener("scroll", this.#boundUpdateChanged);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Stops all observations and event listeners, resets the change handler,
|
|
132
|
-
* and releases internal observer resources.
|
|
133
|
-
*/
|
|
134
|
-
disconnect() {
|
|
135
|
-
// Optional chaining để an toàn với mocks trong môi trường test
|
|
136
|
-
this.#resizeObserver?.disconnect?.();
|
|
137
|
-
this.#mutationObserver?.disconnect?.();
|
|
138
|
-
|
|
139
|
-
this.onChanged = (metrics) => {};
|
|
140
|
-
window.removeEventListener("scroll", this.#boundUpdateChanged, true);
|
|
141
|
-
window.removeEventListener("resize", this.#boundUpdateChanged);
|
|
142
|
-
|
|
143
|
-
if (window.visualViewport) {
|
|
144
|
-
window.visualViewport.removeEventListener("resize", this.#boundUpdateChanged);
|
|
145
|
-
window.visualViewport.removeEventListener("scroll", this.#boundUpdateChanged);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
this.#resizeObserver = null;
|
|
149
|
-
this.#mutationObserver = null;
|
|
150
|
-
this.element = null;
|
|
151
|
-
}
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* @class
|
|
4
|
+
*/
|
|
5
|
+
export class ResizeObserverService {
|
|
6
|
+
isInit = false;
|
|
7
|
+
element = null;
|
|
8
|
+
/** @type {ResizeObserver} */
|
|
9
|
+
#resizeObserver = null;
|
|
10
|
+
#mutationObserver = null;
|
|
11
|
+
#boundUpdateChanged;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Initializes the service and binds the internal update handler to `this`.
|
|
15
|
+
* Sets the service to an initialized state.
|
|
16
|
+
*/
|
|
17
|
+
constructor() {
|
|
18
|
+
this.isInit = true
|
|
19
|
+
this.#boundUpdateChanged = this.#updateChanged.bind(this);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Callback invoked when the observed element's metrics change.
|
|
24
|
+
* Override to react to size/position/style updates.
|
|
25
|
+
*
|
|
26
|
+
* @param {ElementMetrics} metrics - Calculated box metrics (size, position, padding, border, margin).
|
|
27
|
+
*/
|
|
28
|
+
onChanged(metrics) {}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Computes the current metrics of the bound element (bounding rect + computed styles)
|
|
32
|
+
* and forwards them to `onChanged(metrics)`.
|
|
33
|
+
*
|
|
34
|
+
* @returns {void}
|
|
35
|
+
*/
|
|
36
|
+
#updateChanged() {
|
|
37
|
+
// Guard: nếu element chưa sẵn sàng hoặc không đo được, trả về metrics mặc định
|
|
38
|
+
const el = this.element;
|
|
39
|
+
if (!el || typeof el.getBoundingClientRect !== 'function') {
|
|
40
|
+
/** @type {ElementMetrics} */
|
|
41
|
+
const defaultMetrics = {
|
|
42
|
+
width: 0,
|
|
43
|
+
height: 0,
|
|
44
|
+
top: 0,
|
|
45
|
+
left: 0,
|
|
46
|
+
padding: { top: 0, right: 0, bottom: 0, left: 0 },
|
|
47
|
+
border: { top: 0, right: 0, bottom: 0, left: 0 },
|
|
48
|
+
margin: { top: 0, right: 0, bottom: 0, left: 0 },
|
|
49
|
+
};
|
|
50
|
+
this.onChanged(defaultMetrics);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const rect = el.getBoundingClientRect();
|
|
55
|
+
const style = (typeof window?.getComputedStyle === 'function')
|
|
56
|
+
? window.getComputedStyle(el)
|
|
57
|
+
: null;
|
|
58
|
+
|
|
59
|
+
/** @type {ElementMetrics} */
|
|
60
|
+
const metrics = {
|
|
61
|
+
width: rect?.width ?? 0,
|
|
62
|
+
height: rect?.height ?? 0,
|
|
63
|
+
top: rect?.top ?? 0,
|
|
64
|
+
left: rect?.left ?? 0,
|
|
65
|
+
|
|
66
|
+
padding: {
|
|
67
|
+
top: parseFloat(style?.paddingTop ?? '0'),
|
|
68
|
+
right: parseFloat(style?.paddingRight ?? '0'),
|
|
69
|
+
bottom: parseFloat(style?.paddingBottom ?? '0'),
|
|
70
|
+
left: parseFloat(style?.paddingLeft ?? '0'),
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
border: {
|
|
74
|
+
top: parseFloat(style?.borderTopWidth ?? '0'),
|
|
75
|
+
right: parseFloat(style?.borderRightWidth ?? '0'),
|
|
76
|
+
bottom: parseFloat(style?.borderBottomWidth ?? '0'),
|
|
77
|
+
left: parseFloat(style?.borderLeftWidth ?? '0'),
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
margin: {
|
|
81
|
+
top: parseFloat(style?.marginTop ?? '0'),
|
|
82
|
+
right: parseFloat(style?.marginRight ?? '0'),
|
|
83
|
+
bottom: parseFloat(style?.marginBottom ?? '0'),
|
|
84
|
+
left: parseFloat(style?.marginLeft ?? '0'),
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
this.onChanged(metrics);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Manually triggers a metrics computation and notification via `onChanged`.
|
|
93
|
+
*/
|
|
94
|
+
trigger() {
|
|
95
|
+
this.#updateChanged();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Starts observing the provided element for resize and style/class mutations,
|
|
100
|
+
* and listens to window/visualViewport scroll/resize to detect layout changes.
|
|
101
|
+
*
|
|
102
|
+
* @param {Element} element - The element to observe; must be a valid DOM Element.
|
|
103
|
+
* @throws {Error} If `element` is not an instance of Element.
|
|
104
|
+
*/
|
|
105
|
+
connect(element) {
|
|
106
|
+
if (!(element instanceof Element)) {
|
|
107
|
+
throw new Error("Element không hợp lệ");
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
this.element = element;
|
|
111
|
+
|
|
112
|
+
this.#resizeObserver = new ResizeObserver(this.#boundUpdateChanged);
|
|
113
|
+
this.#resizeObserver.observe(element);
|
|
114
|
+
|
|
115
|
+
this.#mutationObserver = new MutationObserver(this.#boundUpdateChanged);
|
|
116
|
+
this.#mutationObserver.observe(element, {
|
|
117
|
+
attributes: true,
|
|
118
|
+
attributeFilter: ["style", "class"]
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
window.addEventListener("scroll", this.#boundUpdateChanged, true);
|
|
122
|
+
window.addEventListener("resize", this.#boundUpdateChanged);
|
|
123
|
+
|
|
124
|
+
if (window.visualViewport) {
|
|
125
|
+
window.visualViewport.addEventListener("resize", this.#boundUpdateChanged);
|
|
126
|
+
window.visualViewport.addEventListener("scroll", this.#boundUpdateChanged);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Stops all observations and event listeners, resets the change handler,
|
|
132
|
+
* and releases internal observer resources.
|
|
133
|
+
*/
|
|
134
|
+
disconnect() {
|
|
135
|
+
// Optional chaining để an toàn với mocks trong môi trường test
|
|
136
|
+
this.#resizeObserver?.disconnect?.();
|
|
137
|
+
this.#mutationObserver?.disconnect?.();
|
|
138
|
+
|
|
139
|
+
this.onChanged = (metrics) => {};
|
|
140
|
+
window.removeEventListener("scroll", this.#boundUpdateChanged, true);
|
|
141
|
+
window.removeEventListener("resize", this.#boundUpdateChanged);
|
|
142
|
+
|
|
143
|
+
if (window.visualViewport) {
|
|
144
|
+
window.visualViewport.removeEventListener("resize", this.#boundUpdateChanged);
|
|
145
|
+
window.visualViewport.removeEventListener("scroll", this.#boundUpdateChanged);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
this.#resizeObserver = null;
|
|
149
|
+
this.#mutationObserver = null;
|
|
150
|
+
this.element = null;
|
|
151
|
+
}
|
|
152
152
|
}
|
|
@@ -1,61 +1,61 @@
|
|
|
1
|
-
export class SelectObserver {
|
|
2
|
-
/** @type {MutationObserver} */
|
|
3
|
-
#observer;
|
|
4
|
-
|
|
5
|
-
/** @type {HTMLSelectElement} */
|
|
6
|
-
#select;
|
|
7
|
-
|
|
8
|
-
#debounceTimer = null;
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Observes a <select> element for option list and attribute changes, with debouncing.
|
|
12
|
-
* Detects modifications to children (options added/removed) and relevant attributes
|
|
13
|
-
* ("selected", "value", "disabled"). Emits updates via the overridable onChanged() hook.
|
|
14
|
-
*
|
|
15
|
-
* @param {HTMLSelectElement} select - The <select> element to monitor.
|
|
16
|
-
*/
|
|
17
|
-
constructor(select) {
|
|
18
|
-
this.#observer = new MutationObserver(() => {
|
|
19
|
-
clearTimeout(this.#debounceTimer);
|
|
20
|
-
this.#debounceTimer = setTimeout(() => {
|
|
21
|
-
this.onChanged(select);
|
|
22
|
-
}, 50);
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
this.#select = select;
|
|
26
|
-
|
|
27
|
-
select.addEventListener("options:changed", () => {
|
|
28
|
-
this.onChanged(select);
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Starts observing the select element for child list mutations and attribute changes.
|
|
34
|
-
* Uses a MutationObserver with a debounce to batch rapid updates.
|
|
35
|
-
*/
|
|
36
|
-
connect() {
|
|
37
|
-
this.#observer.observe(this.#select, {
|
|
38
|
-
childList: true,
|
|
39
|
-
subtree: false,
|
|
40
|
-
|
|
41
|
-
attributes: true,
|
|
42
|
-
attributeFilter: ["selected", "value", "disabled"]
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Hook invoked when the select's options or attributes change.
|
|
48
|
-
* Override to handle updates; receives the current HTMLCollection of options.
|
|
49
|
-
*
|
|
50
|
-
* @param {HTMLSelectElement} options - The Select element.
|
|
51
|
-
*/
|
|
52
|
-
onChanged(options) { }
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Stops observing the select element and clears any pending debounce timers.
|
|
56
|
-
*/
|
|
57
|
-
disconnect() {
|
|
58
|
-
clearTimeout(this.#debounceTimer);
|
|
59
|
-
this.#observer.disconnect();
|
|
60
|
-
}
|
|
1
|
+
export class SelectObserver {
|
|
2
|
+
/** @type {MutationObserver} */
|
|
3
|
+
#observer;
|
|
4
|
+
|
|
5
|
+
/** @type {HTMLSelectElement} */
|
|
6
|
+
#select;
|
|
7
|
+
|
|
8
|
+
#debounceTimer = null;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Observes a <select> element for option list and attribute changes, with debouncing.
|
|
12
|
+
* Detects modifications to children (options added/removed) and relevant attributes
|
|
13
|
+
* ("selected", "value", "disabled"). Emits updates via the overridable onChanged() hook.
|
|
14
|
+
*
|
|
15
|
+
* @param {HTMLSelectElement} select - The <select> element to monitor.
|
|
16
|
+
*/
|
|
17
|
+
constructor(select) {
|
|
18
|
+
this.#observer = new MutationObserver(() => {
|
|
19
|
+
clearTimeout(this.#debounceTimer);
|
|
20
|
+
this.#debounceTimer = setTimeout(() => {
|
|
21
|
+
this.onChanged(select);
|
|
22
|
+
}, 50);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
this.#select = select;
|
|
26
|
+
|
|
27
|
+
select.addEventListener("options:changed", () => {
|
|
28
|
+
this.onChanged(select);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Starts observing the select element for child list mutations and attribute changes.
|
|
34
|
+
* Uses a MutationObserver with a debounce to batch rapid updates.
|
|
35
|
+
*/
|
|
36
|
+
connect() {
|
|
37
|
+
this.#observer.observe(this.#select, {
|
|
38
|
+
childList: true,
|
|
39
|
+
subtree: false,
|
|
40
|
+
|
|
41
|
+
attributes: true,
|
|
42
|
+
attributeFilter: ["selected", "value", "disabled"]
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Hook invoked when the select's options or attributes change.
|
|
48
|
+
* Override to handle updates; receives the current HTMLCollection of options.
|
|
49
|
+
*
|
|
50
|
+
* @param {HTMLSelectElement} options - The Select element.
|
|
51
|
+
*/
|
|
52
|
+
onChanged(options) { }
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Stops observing the select element and clears any pending debounce timers.
|
|
56
|
+
*/
|
|
57
|
+
disconnect() {
|
|
58
|
+
clearTimeout(this.#debounceTimer);
|
|
59
|
+
this.#observer.disconnect();
|
|
60
|
+
}
|
|
61
61
|
}
|
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
|
|
2
|
-
/**
|
|
3
|
-
* @template {ModelContract<any, any>} TItem
|
|
4
|
-
* @typedef {Object} AdapterContract
|
|
5
|
-
*
|
|
6
|
-
* @property {TItem[]} items - List of items managed by the adapter.
|
|
7
|
-
* @property {string} adapterKey - Unique key identifier for the adapter.
|
|
8
|
-
*
|
|
9
|
-
* @property {(items: TItem[]) => void} setItems - Replace or update the list of items.
|
|
10
|
-
* @property {(items: TItem[]) => void} syncFromSource - Synchronize items from an external source.
|
|
11
|
-
* @property {() => number} itemCount - Get the number of items.
|
|
12
|
-
*
|
|
13
|
-
* @property {(parent: HTMLElement, item: TItem) => any} viewHolder
|
|
14
|
-
* - Create a viewer for the given item inside the parent container.
|
|
15
|
-
*
|
|
16
|
-
* @property {(item: TItem, viewer: any, position: number) => void} onViewHolder
|
|
17
|
-
* - Bind an item to its viewer at the specified position (render if not initialized, otherwise update).
|
|
18
|
-
*
|
|
19
|
-
* @property {(propName: string, callback: Function) => void} onPropChanging
|
|
20
|
-
* - Register a pre-change callback for a property.
|
|
21
|
-
* @property {(propName: string, callback: Function) => void} onPropChanged
|
|
22
|
-
* - Register a post-change callback for a property.
|
|
23
|
-
* @property {(propName: string, ...params: any[]) => void} changeProp
|
|
24
|
-
* - Trigger the post-change pipeline for a property.
|
|
25
|
-
* @property {(propName: string, ...params: any[]) => void} changingProp
|
|
26
|
-
* - Trigger the pre-change pipeline for a property.
|
|
27
|
-
*
|
|
28
|
-
* @property {(parent: HTMLElement) => void} updateRecyclerView
|
|
29
|
-
* - Ensure all items have viewers and bind them into the recycler container.
|
|
30
|
-
*
|
|
31
|
-
* @property {(items: TItem[]) => void} updateData
|
|
32
|
-
* - Update adapter data (override in subclasses for custom behavior).
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* @template {ModelContract<any, any>} TItem
|
|
4
|
+
* @typedef {Object} AdapterContract
|
|
5
|
+
*
|
|
6
|
+
* @property {TItem[]} items - List of items managed by the adapter.
|
|
7
|
+
* @property {string} adapterKey - Unique key identifier for the adapter.
|
|
8
|
+
*
|
|
9
|
+
* @property {(items: TItem[]) => void} setItems - Replace or update the list of items.
|
|
10
|
+
* @property {(items: TItem[]) => void} syncFromSource - Synchronize items from an external source.
|
|
11
|
+
* @property {() => number} itemCount - Get the number of items.
|
|
12
|
+
*
|
|
13
|
+
* @property {(parent: HTMLElement, item: TItem) => any} viewHolder
|
|
14
|
+
* - Create a viewer for the given item inside the parent container.
|
|
15
|
+
*
|
|
16
|
+
* @property {(item: TItem, viewer: any, position: number) => void} onViewHolder
|
|
17
|
+
* - Bind an item to its viewer at the specified position (render if not initialized, otherwise update).
|
|
18
|
+
*
|
|
19
|
+
* @property {(propName: string, callback: Function) => void} onPropChanging
|
|
20
|
+
* - Register a pre-change callback for a property.
|
|
21
|
+
* @property {(propName: string, callback: Function) => void} onPropChanged
|
|
22
|
+
* - Register a post-change callback for a property.
|
|
23
|
+
* @property {(propName: string, ...params: any[]) => void} changeProp
|
|
24
|
+
* - Trigger the post-change pipeline for a property.
|
|
25
|
+
* @property {(propName: string, ...params: any[]) => void} changingProp
|
|
26
|
+
* - Trigger the pre-change pipeline for a property.
|
|
27
|
+
*
|
|
28
|
+
* @property {(parent: HTMLElement) => void} updateRecyclerView
|
|
29
|
+
* - Ensure all items have viewers and bind them into the recycler container.
|
|
30
|
+
*
|
|
31
|
+
* @property {(items: TItem[]) => void} updateData
|
|
32
|
+
* - Update adapter data (override in subclasses for custom behavior).
|
|
33
33
|
*/
|
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @typedef {Object} SizeObject
|
|
3
|
-
* @property {number} width
|
|
4
|
-
* @property {number} height
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* @typedef {Object} DimensionObject
|
|
9
|
-
* @property {number} width
|
|
10
|
-
* @property {number} height
|
|
11
|
-
* @property {number} scrollHeight
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* @typedef {Object} EffectorInterface
|
|
16
|
-
* @property {(query: string | HTMLElement) => void} setElement
|
|
17
|
-
* @property {HTMLElement} element
|
|
18
|
-
* @property {(object: {}) => EffectorInterface} expand
|
|
19
|
-
* @property {() => EffectorInterface} cancel
|
|
20
|
-
* @property {(object: {}) => EffectorInterface} collapse
|
|
21
|
-
* @property {(object: {}) => EffectorInterface} resize
|
|
22
|
-
* @property {boolean} isAnimating
|
|
23
|
-
* @property {(string: "flex") => DimensionObject} getHiddenDimensions
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {Object} SizeObject
|
|
3
|
+
* @property {number} width
|
|
4
|
+
* @property {number} height
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @typedef {Object} DimensionObject
|
|
9
|
+
* @property {number} width
|
|
10
|
+
* @property {number} height
|
|
11
|
+
* @property {number} scrollHeight
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @typedef {Object} EffectorInterface
|
|
16
|
+
* @property {(query: string | HTMLElement) => void} setElement
|
|
17
|
+
* @property {HTMLElement} element
|
|
18
|
+
* @property {(object: {}) => EffectorInterface} expand
|
|
19
|
+
* @property {() => EffectorInterface} cancel
|
|
20
|
+
* @property {(object: {}) => EffectorInterface} collapse
|
|
21
|
+
* @property {(object: {}) => EffectorInterface} resize
|
|
22
|
+
* @property {boolean} isAnimating
|
|
23
|
+
* @property {(string: "flex") => DimensionObject} getHiddenDimensions
|
|
24
24
|
*/
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @typedef {Object} IEventCallback
|
|
3
|
-
* @property {() => void} stopPropagation
|
|
4
|
-
* @property {() => void} cancel
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* @typedef {Object} IEventToken
|
|
9
|
-
* @property {boolean} isContinue
|
|
10
|
-
* @property {boolean} isCancel
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {Object} IEventCallback
|
|
3
|
+
* @property {() => void} stopPropagation
|
|
4
|
+
* @property {() => void} cancel
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @typedef {Object} IEventToken
|
|
9
|
+
* @property {boolean} isContinue
|
|
10
|
+
* @property {boolean} isCancel
|
|
11
11
|
*/
|