selective-ui 1.2.4 → 1.2.6
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/README.md +7 -0
- package/dist/selective-ui.css +64 -58
- package/dist/selective-ui.css.map +1 -1
- package/dist/selective-ui.esm.js +4396 -1344
- package/dist/selective-ui.esm.js.map +1 -1
- package/dist/selective-ui.esm.min.js +2 -2
- package/dist/selective-ui.esm.min.js.br +0 -0
- package/dist/selective-ui.min.css +1 -1
- package/dist/selective-ui.min.css.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 +4401 -1345
- package/dist/selective-ui.umd.js.map +1 -1
- package/package.json +3 -3
- package/src/css/components/accessorybox.css +1 -1
- package/src/css/components/directive.css +2 -2
- package/src/css/components/option-handle.css +4 -4
- package/src/css/components/placeholder.css +1 -1
- package/src/css/components/popup/empty-state.css +3 -3
- package/src/css/components/popup/loading-state.css +3 -3
- package/src/css/components/popup/popup.css +5 -5
- package/src/css/components/searchbox.css +2 -2
- package/src/css/components/selectbox.css +7 -7
- package/src/css/views/group-view.css +8 -8
- package/src/css/views/option-view.css +22 -22
- package/src/ts/adapter/mixed-adapter.ts +248 -92
- package/src/ts/components/accessorybox.ts +170 -73
- package/src/ts/components/directive.ts +55 -26
- package/src/ts/components/option-handle.ts +127 -60
- package/src/ts/components/placeholder.ts +73 -35
- package/src/ts/components/popup/empty-state.ts +71 -35
- package/src/ts/components/popup/loading-state.ts +73 -33
- package/src/ts/components/popup/popup.ts +19 -39
- package/src/ts/components/searchbox.ts +189 -50
- package/src/ts/components/selectbox.ts +401 -40
- package/src/ts/core/base/adapter.ts +160 -79
- package/src/ts/core/base/fenwick.ts +147 -0
- package/src/ts/core/base/lifecycle.ts +118 -35
- package/src/ts/core/base/model.ts +94 -36
- package/src/ts/core/base/recyclerview.ts +0 -1
- package/src/ts/core/base/view.ts +54 -23
- package/src/ts/core/base/virtual-recyclerview.ts +365 -283
- package/src/ts/core/model-manager.ts +172 -92
- package/src/ts/core/search-controller.ts +166 -93
- package/src/ts/global.ts +26 -5
- package/src/ts/index.ts +22 -3
- package/src/ts/models/group-model.ts +138 -32
- package/src/ts/models/option-model.ts +197 -53
- package/src/ts/services/dataset-observer.ts +72 -10
- package/src/ts/services/ea-observer.ts +87 -10
- package/src/ts/services/effector.ts +181 -32
- package/src/ts/services/refresher.ts +32 -7
- package/src/ts/services/resize-observer.ts +136 -19
- package/src/ts/services/select-observer.ts +115 -50
- package/src/ts/types/core/base/view.type.ts +3 -3
- package/src/ts/types/core/base/virtual-recyclerview.type.ts +1 -1
- package/src/ts/types/plugins/plugin.type.ts +46 -0
- package/src/ts/types/utils/ievents.type.ts +6 -1
- package/src/ts/types/utils/istorage.type.ts +8 -4
- package/src/ts/types/utils/libs.type.ts +2 -2
- package/src/ts/types/utils/selective.type.ts +14 -1
- package/src/ts/utils/callback-scheduler.ts +115 -37
- package/src/ts/utils/ievents.ts +91 -29
- package/src/ts/utils/libs.ts +41 -65
- package/src/ts/utils/selective.ts +412 -79
- package/src/ts/views/group-view.ts +142 -31
- package/src/ts/views/option-view.ts +272 -60
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
import { View } from "../core/base/view";
|
|
3
2
|
import { Libs } from "../utils/libs";
|
|
4
3
|
import type {
|
|
@@ -9,52 +8,144 @@ import type {
|
|
|
9
8
|
} from "../types/views/view.option.type";
|
|
10
9
|
|
|
11
10
|
/**
|
|
12
|
-
*
|
|
11
|
+
* OptionView
|
|
12
|
+
*
|
|
13
|
+
* View implementation for a single selectable option with reactive configuration.
|
|
14
|
+
*
|
|
15
|
+
* ### Responsibility
|
|
16
|
+
* - Renders an option with input (radio/checkbox) + optional image + label.
|
|
17
|
+
* - Supports **reactive configuration** via Proxy-based change tracking.
|
|
18
|
+
* - Applies **incremental DOM updates** for configuration changes (no full re-render).
|
|
19
|
+
* - Manages input type switching (radio ↔ checkbox) based on selection mode.
|
|
20
|
+
* - Dynamically creates/removes image elements when {@link hasImage} changes.
|
|
21
|
+
*
|
|
22
|
+
* ### Structure
|
|
23
|
+
* ```
|
|
24
|
+
* OptionView (root, role="option")
|
|
25
|
+
* ├─ OptionInput (<input type="radio|checkbox">)
|
|
26
|
+
* ├─ OptionImage (<img>, conditional)
|
|
27
|
+
* └─ OptionLabel (<label>)
|
|
28
|
+
* └─ LabelContent (<div>)
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* ### Lifecycle (View-based FSM)
|
|
32
|
+
* - **Construction**: Calls {@link initialize}, sets up config Proxy, transitions `NEW → INITIALIZED`.
|
|
33
|
+
* - **{@link mount}**: Creates DOM structure based on current config, transitions `INITIALIZED → MOUNTED`.
|
|
34
|
+
* - **Reactive updates**: After mount, config changes trigger {@link applyPartialChange} (targeted DOM updates).
|
|
35
|
+
* - **{@link destroy}**: Removes DOM nodes, transitions to `DESTROYED`.
|
|
36
|
+
*
|
|
37
|
+
* ### Reactive configuration strategy
|
|
38
|
+
* - **{@link config}**: Internal target object (should not be mutated directly).
|
|
39
|
+
* - **{@link configProxy}**: Proxy wrapper; assignments trigger {@link applyPartialChange}.
|
|
40
|
+
* - **{@link isRendered}**: Gates partial updates (no DOM changes before initial {@link mount}).
|
|
41
|
+
* - **Batch updates**: {@link optionConfig} setter applies multiple changes efficiently (only diffed properties).
|
|
13
42
|
*
|
|
14
|
-
*
|
|
15
|
-
* -
|
|
16
|
-
* -
|
|
17
|
-
* -
|
|
43
|
+
* ### Partial update semantics
|
|
44
|
+
* - **`isMultiple`**: Toggles `"multiple"` class, switches input `type` (radio ↔ checkbox).
|
|
45
|
+
* - **`hasImage`**: Toggles `"has-image"` class, creates/removes `<img>` element.
|
|
46
|
+
* - **`imagePosition`**: Replaces `image-{position}` class (top/right/bottom/left).
|
|
47
|
+
* - **`imageWidth/Height/BorderRadius`**: Mutates `<img>` inline styles.
|
|
48
|
+
* - **`labelValign/Halign`**: Replaces label alignment classes.
|
|
18
49
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
50
|
+
* ### Image lifecycle
|
|
51
|
+
* - Created on-demand via {@link createImage} when `hasImage = true`.
|
|
52
|
+
* - Removed via `remove()` when `hasImage = false`.
|
|
53
|
+
* - Reference stored in `view.tags.OptionImage` (nulled after removal).
|
|
54
|
+
*
|
|
55
|
+
* ### Accessibility
|
|
56
|
+
* - Root: `role="option"`, `aria-selected="false"` (managed externally), `tabindex="-1"`.
|
|
57
|
+
* - Input: Associated with label via unique `id` / `htmlFor`.
|
|
58
|
+
* - Label: Clickable, triggers input selection.
|
|
59
|
+
*
|
|
60
|
+
* ### DOM side effects
|
|
61
|
+
* - {@link mount} creates and appends full structure.
|
|
62
|
+
* - {@link applyPartialChange} mutates classes, attributes, styles, or child nodes.
|
|
63
|
+
* - {@link createImage} inserts `<img>` element.
|
|
64
|
+
* - Setters ({@link isMultiple}, {@link hasImage}) trigger Proxy → DOM updates.
|
|
65
|
+
*
|
|
66
|
+
* ### No-op / Idempotency
|
|
67
|
+
* - {@link applyPartialChange} is no-op if view not mounted (early return guard).
|
|
68
|
+
* - {@link createImage} is no-op if image already exists.
|
|
69
|
+
* - {@link optionConfig} setter only assigns diffed properties (avoids redundant Proxy triggers).
|
|
70
|
+
* - Safe to call setters multiple times with same value (Proxy guards against no-op updates).
|
|
21
71
|
*
|
|
22
72
|
* @extends View<OptionViewTags>
|
|
73
|
+
* @template OptionViewTags - Type descriptor for the option's DOM structure.
|
|
74
|
+
* @see {@link OptionViewResult}
|
|
75
|
+
* @see {@link OptionConfig}
|
|
76
|
+
* @see {@link View}
|
|
23
77
|
*/
|
|
24
78
|
export class OptionView extends View<OptionViewTags> {
|
|
25
79
|
|
|
26
80
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
81
|
+
* Strongly-typed reference to the mounted option view structure.
|
|
82
|
+
*
|
|
83
|
+
* Structure:
|
|
84
|
+
* - **view**: Root container element.
|
|
85
|
+
* - **tags**: Named references to input, image (conditional), label, label content.
|
|
86
|
+
*
|
|
87
|
+
* Lifecycle:
|
|
88
|
+
* - `null` until {@link mount} completes.
|
|
89
|
+
* - Cleared during {@link destroy}.
|
|
90
|
+
*
|
|
91
|
+
* @public
|
|
29
92
|
*/
|
|
30
93
|
public view: OptionViewResult | null = null;
|
|
31
94
|
|
|
32
95
|
/**
|
|
33
|
-
* Internal configuration
|
|
34
|
-
*
|
|
96
|
+
* Internal configuration object (Proxy target).
|
|
97
|
+
*
|
|
98
|
+
* Lifecycle:
|
|
99
|
+
* - Initialized during {@link initialize} with default values.
|
|
100
|
+
* - Mutated via {@link configProxy} Proxy trap.
|
|
101
|
+
*
|
|
102
|
+
* Notes:
|
|
103
|
+
* - **Should not be mutated directly**; use {@link configProxy} or typed setters.
|
|
104
|
+
* - Contains default values for layout, image, and alignment.
|
|
105
|
+
*
|
|
106
|
+
* @private
|
|
35
107
|
*/
|
|
36
108
|
private config: OptionConfig | null = null;
|
|
37
109
|
|
|
38
110
|
/**
|
|
39
|
-
* Proxy wrapper around
|
|
40
|
-
*
|
|
41
|
-
*
|
|
111
|
+
* Reactive Proxy wrapper around {@link config}.
|
|
112
|
+
*
|
|
113
|
+
* Behavior:
|
|
114
|
+
* - Intercepts property assignments via `set` trap.
|
|
115
|
+
* - Triggers {@link applyPartialChange} for diffed values when {@link isRendered} is `true`.
|
|
116
|
+
* - Prevents redundant DOM updates when value hasn't changed.
|
|
117
|
+
*
|
|
118
|
+
* Usage:
|
|
119
|
+
* - Accessed via {@link optionConfig} getter or typed setters ({@link isMultiple}, {@link hasImage}).
|
|
120
|
+
*
|
|
121
|
+
* @private
|
|
42
122
|
*/
|
|
43
123
|
private configProxy: OptionConfig | null = null;
|
|
44
124
|
|
|
45
125
|
/**
|
|
46
|
-
*
|
|
47
|
-
*
|
|
126
|
+
* Flag indicating whether the initial render has completed.
|
|
127
|
+
*
|
|
128
|
+
* Lifecycle:
|
|
129
|
+
* - `false` until {@link mount} finishes.
|
|
130
|
+
* - `true` afterward (enables partial updates).
|
|
131
|
+
*
|
|
132
|
+
* Purpose:
|
|
133
|
+
* - Gates {@link applyPartialChange} to prevent DOM mutations before structure exists.
|
|
134
|
+
*
|
|
135
|
+
* @private
|
|
48
136
|
*/
|
|
49
137
|
private isRendered = false;
|
|
50
138
|
|
|
51
139
|
/**
|
|
52
140
|
* Creates a new OptionView bound to the given parent element.
|
|
53
141
|
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
142
|
+
* Initialization flow:
|
|
143
|
+
* 1. Calls `super(parent)` (View base constructor).
|
|
144
|
+
* 2. Calls {@link initialize} to set up config and Proxy.
|
|
145
|
+
* 3. Transitions `NEW → INITIALIZED` via `this.init()` inside {@link initialize}.
|
|
56
146
|
*
|
|
57
|
-
* @
|
|
147
|
+
* @public
|
|
148
|
+
* @param {HTMLElement} parent - Container element that will host this option view.
|
|
58
149
|
*/
|
|
59
150
|
public constructor(parent: HTMLElement) {
|
|
60
151
|
super(parent);
|
|
@@ -62,13 +153,31 @@ export class OptionView extends View<OptionViewTags> {
|
|
|
62
153
|
}
|
|
63
154
|
|
|
64
155
|
/**
|
|
65
|
-
* Initializes the default configuration
|
|
156
|
+
* Initializes the default configuration and sets up reactive Proxy.
|
|
157
|
+
*
|
|
158
|
+
* Configuration defaults:
|
|
159
|
+
* - `isMultiple`: `false` (radio mode)
|
|
160
|
+
* - `hasImage`: `false` (no image)
|
|
161
|
+
* - `imagePosition`: `"right"`
|
|
162
|
+
* - `imageWidth/Height`: `"60px"`
|
|
163
|
+
* - `imageBorderRadius`: `"4px"`
|
|
164
|
+
* - `labelValign/Halign`: `"center"` / `"left"`
|
|
165
|
+
*
|
|
166
|
+
* Proxy behavior:
|
|
167
|
+
* - **`set` trap**: Compares old vs new value; if different:
|
|
168
|
+
* 1. Updates {@link config} target.
|
|
169
|
+
* 2. Calls {@link applyPartialChange} if {@link isRendered} is `true`.
|
|
170
|
+
* - Returns `true` to indicate success.
|
|
66
171
|
*
|
|
67
|
-
*
|
|
68
|
-
* -
|
|
69
|
-
* -
|
|
172
|
+
* Postcondition:
|
|
173
|
+
* - {@link config} and {@link configProxy} are initialized.
|
|
174
|
+
* - Transitions `NEW → INITIALIZED` via `this.init()`.
|
|
70
175
|
*
|
|
71
|
-
*
|
|
176
|
+
* Notes:
|
|
177
|
+
* - No DOM mutations occur until {@link mount} is called.
|
|
178
|
+
*
|
|
179
|
+
* @public
|
|
180
|
+
* @returns {void}
|
|
72
181
|
*/
|
|
73
182
|
public initialize(): void {
|
|
74
183
|
const self = this;
|
|
@@ -108,8 +217,12 @@ export class OptionView extends View<OptionViewTags> {
|
|
|
108
217
|
/**
|
|
109
218
|
* Indicates whether the option supports multiple selection.
|
|
110
219
|
*
|
|
111
|
-
*
|
|
112
|
-
* - `
|
|
220
|
+
* Semantics:
|
|
221
|
+
* - `false`: Single selection mode (radio input).
|
|
222
|
+
* - `true`: Multiple selection mode (checkbox input).
|
|
223
|
+
*
|
|
224
|
+
* @public
|
|
225
|
+
* @returns {boolean} Current selection mode.
|
|
113
226
|
*/
|
|
114
227
|
public get isMultiple(): boolean {
|
|
115
228
|
return this.config!.isMultiple;
|
|
@@ -118,49 +231,88 @@ export class OptionView extends View<OptionViewTags> {
|
|
|
118
231
|
/**
|
|
119
232
|
* Enables or disables multiple selection mode.
|
|
120
233
|
*
|
|
121
|
-
*
|
|
122
|
-
* - Toggles
|
|
123
|
-
* - Switches
|
|
234
|
+
* Side effects (when rendered):
|
|
235
|
+
* - Toggles `"multiple"` CSS class on root element.
|
|
236
|
+
* - Switches input `type` attribute (`"radio"` ↔ `"checkbox"`).
|
|
237
|
+
*
|
|
238
|
+
* Notes:
|
|
239
|
+
* - Assignments trigger Proxy → {@link applyPartialChange}.
|
|
240
|
+
* - No-op if value hasn't changed (Proxy guards).
|
|
241
|
+
*
|
|
242
|
+
* @public
|
|
243
|
+
* @param {boolean} value - `true` for multiple selection; `false` for single.
|
|
124
244
|
*/
|
|
125
245
|
public set isMultiple(value: boolean) {
|
|
126
|
-
|
|
246
|
+
this.configProxy.isMultiple = !!value;
|
|
127
247
|
}
|
|
128
248
|
|
|
129
249
|
/**
|
|
130
|
-
* Indicates whether the option displays an image
|
|
250
|
+
* Indicates whether the option displays an image.
|
|
251
|
+
*
|
|
252
|
+
* @public
|
|
253
|
+
* @returns {boolean} `true` if image is visible; `false` otherwise.
|
|
131
254
|
*/
|
|
132
255
|
public get hasImage(): boolean {
|
|
133
256
|
return this.config!.hasImage;
|
|
134
257
|
}
|
|
135
258
|
|
|
136
259
|
/**
|
|
137
|
-
* Shows or hides the image
|
|
260
|
+
* Shows or hides the option's image element.
|
|
261
|
+
*
|
|
262
|
+
* Side effects (when rendered):
|
|
263
|
+
* - **`true`**: Toggles `"has-image"` class, adds `image-{position}` class, calls {@link createImage}.
|
|
264
|
+
* - **`false`**: Removes `"has-image"` and `image-*` classes, removes `<img>` element, nulls reference.
|
|
138
265
|
*
|
|
139
|
-
*
|
|
140
|
-
* -
|
|
141
|
-
* -
|
|
266
|
+
* Notes:
|
|
267
|
+
* - Assignments trigger Proxy → {@link applyPartialChange}.
|
|
268
|
+
* - Image is created on-demand (not pre-rendered).
|
|
269
|
+
*
|
|
270
|
+
* @public
|
|
271
|
+
* @param {boolean} value - `true` to show image; `false` to hide.
|
|
142
272
|
*/
|
|
143
273
|
public set hasImage(value: boolean) {
|
|
144
|
-
|
|
274
|
+
this.configProxy.hasImage = !!value;
|
|
145
275
|
}
|
|
146
276
|
|
|
147
277
|
/**
|
|
148
278
|
* Provides reactive access to the full option configuration.
|
|
149
279
|
*
|
|
150
|
-
*
|
|
151
|
-
*
|
|
280
|
+
* Usage:
|
|
281
|
+
* - **Getter**: Returns {@link configProxy} for direct property access.
|
|
282
|
+
* - **Setter**: Applies batch configuration changes (see setter docs).
|
|
283
|
+
*
|
|
284
|
+
* Notes:
|
|
285
|
+
* - Mutating properties on the returned object triggers incremental DOM updates.
|
|
286
|
+
* - Safe to read/write after {@link initialize} completes.
|
|
287
|
+
*
|
|
288
|
+
* @public
|
|
289
|
+
* @returns {OptionConfig} Reactive configuration Proxy.
|
|
152
290
|
*/
|
|
153
291
|
public get optionConfig(): OptionConfig {
|
|
154
292
|
return this.configProxy as OptionConfig;
|
|
155
293
|
}
|
|
156
294
|
|
|
157
295
|
/**
|
|
158
|
-
* Applies a batch of configuration changes.
|
|
296
|
+
* Applies a batch of configuration changes efficiently.
|
|
297
|
+
*
|
|
298
|
+
* Optimization strategy:
|
|
299
|
+
* 1. Compares each incoming property against current {@link config} value.
|
|
300
|
+
* 2. Builds a `changes` object containing **only diffed properties**.
|
|
301
|
+
* 3. Assigns `changes` to {@link configProxy} via `Object.assign` (triggers Proxy traps).
|
|
302
|
+
*
|
|
303
|
+
* Diffed properties:
|
|
304
|
+
* - `imageWidth`, `imageHeight`, `imageBorderRadius`
|
|
305
|
+
* - `imagePosition`
|
|
306
|
+
* - `labelValign`, `labelHalign`
|
|
159
307
|
*
|
|
160
|
-
*
|
|
161
|
-
*
|
|
308
|
+
* Notes:
|
|
309
|
+
* - No-op if `config` is `null`, or no properties differ.
|
|
310
|
+
* - Prevents redundant Proxy triggers for unchanged values.
|
|
311
|
+
* - Each changed property triggers {@link applyPartialChange} individually.
|
|
162
312
|
*
|
|
163
|
-
* @
|
|
313
|
+
* @public
|
|
314
|
+
* @param {OptionConfigPatch | null} config - Partial configuration patch; `null` is no-op.
|
|
315
|
+
* @returns {void}
|
|
164
316
|
*/
|
|
165
317
|
public set optionConfig(config: OptionConfigPatch | null) {
|
|
166
318
|
if (!config || !this.configProxy || !this.config) return;
|
|
@@ -193,12 +345,33 @@ export class OptionView extends View<OptionViewTags> {
|
|
|
193
345
|
/**
|
|
194
346
|
* Performs the initial render of the option view.
|
|
195
347
|
*
|
|
196
|
-
*
|
|
197
|
-
*
|
|
198
|
-
*
|
|
348
|
+
* Rendering flow:
|
|
349
|
+
* 1. Generates unique option ID (7-character random string).
|
|
350
|
+
* 2. Builds CSS classes based on current {@link config} (`multiple`, `has-image`, `image-{position}`).
|
|
351
|
+
* 3. Constructs child structure:
|
|
352
|
+
* - **OptionInput**: `<input type="radio|checkbox">` with unique ID.
|
|
353
|
+
* - **OptionImage** (conditional): `<img>` with inline styles (width/height/borderRadius).
|
|
354
|
+
* - **OptionLabel**: `<label htmlFor="{inputID}">` with alignment classes.
|
|
355
|
+
* - **LabelContent**: `<div>` (content placeholder).
|
|
356
|
+
* 4. Creates DOM via {@link Libs.mountNode}.
|
|
357
|
+
* 5. Appends root to {@link parent}.
|
|
358
|
+
* 6. Sets {@link isRendered} to `true` (enables reactive updates).
|
|
359
|
+
* 7. Transitions `INITIALIZED → MOUNTED` via `super.mount()`.
|
|
360
|
+
*
|
|
361
|
+
* Accessibility setup:
|
|
362
|
+
* - Root: `role="option"`, `aria-selected="false"`, `tabindex="-1"`.
|
|
363
|
+
* - Input/Label association via `id` / `htmlFor`.
|
|
364
|
+
*
|
|
365
|
+
* Postcondition:
|
|
366
|
+
* - {@link view} is populated with typed DOM references.
|
|
367
|
+
* - Reactive updates are now enabled.
|
|
368
|
+
*
|
|
369
|
+
* @public
|
|
370
|
+
* @returns {void}
|
|
371
|
+
* @override
|
|
199
372
|
*/
|
|
200
373
|
public override mount(): void {
|
|
201
|
-
const viewClass: string[] = ["
|
|
374
|
+
const viewClass: string[] = ["seui-option-view"];
|
|
202
375
|
const opt_id = Libs.randomString(7);
|
|
203
376
|
const inputID = `option_${opt_id}`;
|
|
204
377
|
|
|
@@ -245,7 +418,7 @@ export class OptionView extends View<OptionViewTags> {
|
|
|
245
418
|
},
|
|
246
419
|
};
|
|
247
420
|
|
|
248
|
-
this.view = Libs.
|
|
421
|
+
this.view = Libs.mountNode<OptionViewResult>({
|
|
249
422
|
OptionView: {
|
|
250
423
|
tag: {
|
|
251
424
|
node: "div",
|
|
@@ -257,7 +430,7 @@ export class OptionView extends View<OptionViewTags> {
|
|
|
257
430
|
},
|
|
258
431
|
child: childStructure,
|
|
259
432
|
},
|
|
260
|
-
})
|
|
433
|
+
});
|
|
261
434
|
|
|
262
435
|
this.parent!.appendChild(this.view.view);
|
|
263
436
|
this.isRendered = true;
|
|
@@ -268,9 +441,33 @@ export class OptionView extends View<OptionViewTags> {
|
|
|
268
441
|
/**
|
|
269
442
|
* Applies a targeted DOM update for a single configuration change.
|
|
270
443
|
*
|
|
271
|
-
*
|
|
272
|
-
*
|
|
273
|
-
*
|
|
444
|
+
* Implementation strategy:
|
|
445
|
+
* - Retrieves DOM references from {@link view}.
|
|
446
|
+
* - Switches on `prop` to determine update type.
|
|
447
|
+
* - Mutates **only** the affected DOM nodes (classes, attributes, styles, or child structure).
|
|
448
|
+
*
|
|
449
|
+
* Update rules:
|
|
450
|
+
* - **`isMultiple`**: Toggle `"multiple"` class, switch input `type` (radio ↔ checkbox).
|
|
451
|
+
* - **`hasImage`**: Toggle `"has-image"` class, create/remove `<img>` element, manage `image-*` classes.
|
|
452
|
+
* - **`imagePosition`**: Replace `image-{position}` class (top/right/bottom/left).
|
|
453
|
+
* - **`imageWidth/Height/BorderRadius`**: Mutate `<img>` inline styles.
|
|
454
|
+
* - **`labelValign/Halign`**: Replace label alignment classes.
|
|
455
|
+
*
|
|
456
|
+
* No-op conditions:
|
|
457
|
+
* - If {@link view} is `null` (not mounted yet).
|
|
458
|
+
* - If affected element doesn't exist (e.g., image removed).
|
|
459
|
+
*
|
|
460
|
+
* Notes:
|
|
461
|
+
* - Called by Proxy `set` trap when {@link isRendered} is `true`.
|
|
462
|
+
* - Avoids full re-render; updates are incremental and efficient.
|
|
463
|
+
* - `oldValue` parameter is unused (reserved for future diffing logic).
|
|
464
|
+
*
|
|
465
|
+
* @private
|
|
466
|
+
* @template K - Key of {@link OptionConfig}.
|
|
467
|
+
* @param {K} prop - Property name that changed.
|
|
468
|
+
* @param {OptionConfig[K]} newValue - New value for the property.
|
|
469
|
+
* @param {OptionConfig[K]} oldValue - Previous value (currently unused).
|
|
470
|
+
* @returns {void}
|
|
274
471
|
*/
|
|
275
472
|
private applyPartialChange<K extends keyof OptionConfig>(
|
|
276
473
|
prop: K,
|
|
@@ -281,8 +478,8 @@ export class OptionView extends View<OptionViewTags> {
|
|
|
281
478
|
if (!v || !v.view) return;
|
|
282
479
|
|
|
283
480
|
const root = v.view;
|
|
284
|
-
const input = v.tags?.OptionInput
|
|
285
|
-
const label = v.tags?.OptionLabel
|
|
481
|
+
const input = v.tags?.OptionInput;
|
|
482
|
+
const label = v.tags?.OptionLabel;
|
|
286
483
|
|
|
287
484
|
switch (prop) {
|
|
288
485
|
case "isMultiple": {
|
|
@@ -307,7 +504,7 @@ export class OptionView extends View<OptionViewTags> {
|
|
|
307
504
|
.replace(/image-(top|right|bottom|left)/g, "")
|
|
308
505
|
.trim();
|
|
309
506
|
|
|
310
|
-
const img = v.tags?.OptionImage
|
|
507
|
+
const img = v.tags?.OptionImage;
|
|
311
508
|
img?.remove();
|
|
312
509
|
v.tags.OptionImage = null;
|
|
313
510
|
}
|
|
@@ -327,7 +524,7 @@ export class OptionView extends View<OptionViewTags> {
|
|
|
327
524
|
case "imageWidth":
|
|
328
525
|
case "imageHeight":
|
|
329
526
|
case "imageBorderRadius": {
|
|
330
|
-
const img = v.tags?.OptionImage
|
|
527
|
+
const img = v.tags?.OptionImage;
|
|
331
528
|
if (img) {
|
|
332
529
|
const styleProp =
|
|
333
530
|
prop === "imageWidth" ? "width" :
|
|
@@ -357,9 +554,24 @@ export class OptionView extends View<OptionViewTags> {
|
|
|
357
554
|
/**
|
|
358
555
|
* Creates and inserts the `<img>` element for the option on demand.
|
|
359
556
|
*
|
|
360
|
-
*
|
|
361
|
-
*
|
|
362
|
-
*
|
|
557
|
+
* Creation flow:
|
|
558
|
+
* 1. Checks if image already exists (early return if present).
|
|
559
|
+
* 2. Creates `<img>` element with:
|
|
560
|
+
* - Class: `"option-image"`
|
|
561
|
+
* - Inline styles: `width`, `height`, `borderRadius` from {@link config}.
|
|
562
|
+
* 3. Inserts image before {@link OptionLabel} (if label exists), otherwise appends to root.
|
|
563
|
+
* 4. Stores reference in `view.tags.OptionImage`.
|
|
564
|
+
*
|
|
565
|
+
* No-op conditions:
|
|
566
|
+
* - If {@link view} is `null` (not mounted yet).
|
|
567
|
+
* - If image already exists in `view.tags.OptionImage`.
|
|
568
|
+
*
|
|
569
|
+
* Notes:
|
|
570
|
+
* - Called by {@link applyPartialChange} when `hasImage` transitions to `true`.
|
|
571
|
+
* - Insertion order ensures proper layout (image before label).
|
|
572
|
+
*
|
|
573
|
+
* @private
|
|
574
|
+
* @returns {void}
|
|
363
575
|
*/
|
|
364
576
|
private createImage(): void {
|
|
365
577
|
const v = this.view;
|
|
@@ -368,7 +580,7 @@ export class OptionView extends View<OptionViewTags> {
|
|
|
368
580
|
if (v.tags?.OptionImage) return;
|
|
369
581
|
|
|
370
582
|
const root = v.view;
|
|
371
|
-
const label = v.tags?.OptionLabel
|
|
583
|
+
const label = v.tags?.OptionLabel;
|
|
372
584
|
|
|
373
585
|
const image = document.createElement("img");
|
|
374
586
|
image.className = "option-image";
|