selective-ui 1.2.3 → 1.2.5
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.css.map +1 -1
- package/dist/selective-ui.esm.js +5462 -1043
- 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.js +2 -2
- package/dist/selective-ui.min.js.br +0 -0
- package/dist/selective-ui.umd.js +5463 -1044
- package/dist/selective-ui.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/ts/adapter/mixed-adapter.ts +312 -65
- package/src/ts/components/accessorybox.ts +248 -28
- package/src/ts/components/directive.ts +91 -11
- package/src/ts/components/option-handle.ts +191 -28
- package/src/ts/components/placeholder.ts +111 -16
- package/src/ts/components/popup/empty-state.ts +162 -0
- package/src/ts/components/popup/loading-state.ts +160 -0
- package/src/ts/components/{popup.ts → popup/popup.ts} +167 -71
- package/src/ts/components/searchbox.ts +225 -20
- package/src/ts/components/selectbox.ts +498 -120
- package/src/ts/core/base/adapter.ts +200 -53
- package/src/ts/core/base/fenwick.ts +147 -0
- package/src/ts/core/base/lifecycle.ts +258 -0
- package/src/ts/core/base/model.ts +120 -31
- package/src/ts/core/base/recyclerview.ts +55 -18
- package/src/ts/core/base/view.ts +87 -19
- package/src/ts/core/base/virtual-recyclerview.ts +475 -202
- package/src/ts/core/model-manager.ts +166 -85
- package/src/ts/core/search-controller.ts +236 -38
- package/src/ts/global.ts +6 -6
- package/src/ts/index.ts +6 -6
- package/src/ts/models/group-model.ts +159 -32
- package/src/ts/models/option-model.ts +213 -54
- package/src/ts/services/dataset-observer.ts +72 -10
- package/src/ts/services/ea-observer.ts +92 -15
- package/src/ts/services/effector.ts +181 -32
- package/src/ts/services/refresher.ts +30 -6
- package/src/ts/services/resize-observer.ts +132 -15
- package/src/ts/services/select-observer.ts +115 -50
- package/src/ts/types/components/searchbox.type.ts +1 -1
- package/src/ts/types/core/base/adapter.type.ts +2 -1
- package/src/ts/types/core/base/lifecycle.type.ts +62 -0
- package/src/ts/types/core/base/model.type.ts +3 -1
- package/src/ts/types/core/base/recyclerview.type.ts +2 -8
- package/src/ts/types/core/base/view.type.ts +36 -24
- package/src/ts/types/utils/ievents.type.ts +6 -1
- package/src/ts/utils/callback-scheduler.ts +112 -34
- package/src/ts/utils/ievents.ts +91 -29
- package/src/ts/utils/istorage.ts +1 -1
- package/src/ts/utils/selective.ts +474 -88
- package/src/ts/views/group-view.ts +170 -21
- package/src/ts/views/option-view.ts +349 -68
- package/src/ts/components/empty-state.ts +0 -68
- package/src/ts/components/loading-state.ts +0 -66
- /package/src/css/components/{empty-state.css → popup/empty-state.css} +0 -0
- /package/src/css/components/{loading-state.css → popup/loading-state.css} +0 -0
- /package/src/css/components/{popup.css → popup/popup.css} +0 -0
- /package/src/css/{components/optgroup.css → views/group-view.css} +0 -0
- /package/src/css/{components/option.css → views/option-view.css} +0 -0
|
@@ -1,21 +1,58 @@
|
|
|
1
|
-
|
|
2
1
|
import { GroupModel } from "../models/group-model";
|
|
3
2
|
import { OptionModel } from "../models/option-model";
|
|
3
|
+
import { LifecycleState } from "../types/core/base/lifecycle.type";
|
|
4
|
+
import { MixedItem } from "../types/core/base/mixed-adapter.type";
|
|
4
5
|
import { ModelContract } from "../types/core/base/model.type";
|
|
5
6
|
import { RecyclerViewContract } from "../types/core/base/recyclerview.type";
|
|
6
7
|
import { ViewContract } from "../types/core/base/view.type";
|
|
7
8
|
import { SelectiveOptions } from "../types/utils/selective.type";
|
|
8
9
|
import { Adapter } from "./base/adapter";
|
|
10
|
+
import { Lifecycle } from "./base/lifecycle";
|
|
9
11
|
|
|
10
12
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
+
* Headless orchestrator for model creation/reconciliation and wiring of the view layer.
|
|
14
|
+
*
|
|
15
|
+
* ### Responsibilities
|
|
16
|
+
* - Build and maintain an ordered list of models ({@link GroupModel} / {@link OptionModel})
|
|
17
|
+
* from raw `<optgroup>` / `<option>` elements.
|
|
18
|
+
* - Own the {@link Adapter} and {@link RecyclerViewContract} instances and propagate updates/refreshes.
|
|
19
|
+
* - Provide a small event pipeline surface by delegating to adapter pre-/post-change hooks.
|
|
20
|
+
*
|
|
21
|
+
* **Lifecycle (Strict FSM)**
|
|
22
|
+
* - `NEW` → `INITIALIZED` (via constructor which calls `init()`).
|
|
23
|
+
* - `MOUNTED` is entered automatically on the first `createModelResources()` when state is `INITIALIZED`.
|
|
24
|
+
* - Subsequent calls to `refresh()`/`updateModel()` drive the `UPDATED` phase.
|
|
25
|
+
* - `DESTROYED` releases resources; further calls become **no-ops** where specified.
|
|
26
|
+
*
|
|
27
|
+
* **Idempotency / No-ops**
|
|
28
|
+
* - `createModelResources()` recreates the internal list deterministically for the given input.
|
|
29
|
+
* - `notify()`/`refresh()` are **no-ops** if required handles are not initialized.
|
|
30
|
+
* - `destroy()` is idempotent once the object is `DESTROYED`.
|
|
31
|
+
*
|
|
32
|
+
* **Relationships**
|
|
33
|
+
* - Consumes raw DOM-derived inputs, produces {@link GroupModel}/{@link OptionModel}.
|
|
34
|
+
* - Feeds the models into an {@link Adapter} which is set on a {@link RecyclerViewContract}.
|
|
35
|
+
* - Does not touch DOM directly; DOM side-effects are handled by the recycler view/renderer.
|
|
36
|
+
*
|
|
37
|
+
* **Events / Hooks**
|
|
38
|
+
* - Exposes `triggerChanging()` and `triggerChanged()` which delegate to adapter pipelines
|
|
39
|
+
* (`Adapter#changingProp`, `Adapter#changeProp`) for external observers.
|
|
40
|
+
* - Uses `skipEvent()` to temporarily suppress adapter event propagation (internal batch updates).
|
|
41
|
+
*
|
|
42
|
+
* @template TModel extends ModelContract<any, any> - Concrete model type used by the adapter.
|
|
43
|
+
* @template TAdapter extends Adapter<TModel, ViewContract<any>> - Concrete adapter that consumes the models.
|
|
44
|
+
* @extends Lifecycle
|
|
45
|
+
* @see {@link Adapter}
|
|
46
|
+
* @see {@link RecyclerViewContract}
|
|
47
|
+
* @see {@link GroupModel}
|
|
48
|
+
* @see {@link OptionModel}
|
|
49
|
+
* @see {@link Lifecycle}
|
|
13
50
|
*/
|
|
14
51
|
export class ModelManager<
|
|
15
52
|
TModel extends ModelContract<any, any>,
|
|
16
53
|
TAdapter extends Adapter<TModel, ViewContract<any>>
|
|
17
|
-
> {
|
|
18
|
-
private privModelList: Array<
|
|
54
|
+
> extends Lifecycle {
|
|
55
|
+
private privModelList: Array<MixedItem> = [];
|
|
19
56
|
|
|
20
57
|
private privAdapter!: new (...args: any[]) => TAdapter;
|
|
21
58
|
|
|
@@ -25,25 +62,29 @@ export class ModelManager<
|
|
|
25
62
|
|
|
26
63
|
private privRecyclerViewHandle: RecyclerViewContract<TAdapter> | null = null;
|
|
27
64
|
|
|
28
|
-
private lastFingerprint: string | null = null;
|
|
29
|
-
|
|
30
65
|
private options: SelectiveOptions = null;
|
|
31
66
|
|
|
32
67
|
private oldPosition = 0;
|
|
33
68
|
|
|
34
69
|
/**
|
|
35
70
|
* Constructs a ModelManager with configuration options used by created models and components.
|
|
71
|
+
* Transitions lifecycle `NEW → INITIALIZED` via {@link Lifecycle.init}.
|
|
36
72
|
*
|
|
37
|
-
* @param {
|
|
73
|
+
* @param {SelectiveOptions} options - Configuration object passed to {@link GroupModel}/{@link OptionModel}
|
|
74
|
+
* and to view infrastructure through adapter/recycler.
|
|
38
75
|
*/
|
|
39
76
|
public constructor(options: SelectiveOptions) {
|
|
77
|
+
super();
|
|
40
78
|
this.options = options;
|
|
79
|
+
this.init();
|
|
41
80
|
}
|
|
42
81
|
|
|
43
82
|
/**
|
|
44
83
|
* Registers the adapter class to be used for rendering and managing models.
|
|
84
|
+
* Must be called before {@link load}.
|
|
45
85
|
*
|
|
46
|
-
* @param {new TAdapter} adapter - The adapter constructor (class) to instantiate.
|
|
86
|
+
* @param {new (...args: any[]) => TAdapter} adapter - The adapter constructor (class) to instantiate.
|
|
87
|
+
* @returns {void}
|
|
47
88
|
*/
|
|
48
89
|
public setupAdapter(adapter: new (...args: any[]) => TAdapter): void {
|
|
49
90
|
this.privAdapter = adapter;
|
|
@@ -51,67 +92,32 @@ export class ModelManager<
|
|
|
51
92
|
|
|
52
93
|
/**
|
|
53
94
|
* Registers the RecyclerView class responsible for hosting and updating item views.
|
|
95
|
+
* Must be called before {@link load}.
|
|
54
96
|
*
|
|
55
|
-
* @param {new RecyclerViewContract<TAdapter>} recyclerView - The recycler view constructor.
|
|
97
|
+
* @param {new (...args: any[]) => RecyclerViewContract<TAdapter>} recyclerView - The recycler view constructor.
|
|
98
|
+
* @returns {void}
|
|
56
99
|
*/
|
|
57
100
|
public setupRecyclerView(recyclerView: new (...args: any[]) => RecyclerViewContract<TAdapter>): void {
|
|
58
101
|
this.privRecyclerView = recyclerView;
|
|
59
102
|
}
|
|
60
103
|
|
|
61
104
|
/**
|
|
62
|
-
*
|
|
63
|
-
* Computes a new fingerprint and compares it to the previous one; if different,
|
|
64
|
-
* updates the stored fingerprint and returns true, otherwise returns false.
|
|
65
|
-
*
|
|
66
|
-
* @param {Array<HTMLOptionElement|HTMLOptGroupElement>} modelData - The current model data (options/optgroups).
|
|
67
|
-
* @returns {boolean} True if there are real changes; false otherwise.
|
|
68
|
-
*/
|
|
69
|
-
private hasRealChanges(modelData: Array<HTMLOptionElement | HTMLOptGroupElement>): boolean {
|
|
70
|
-
const newFingerprint = this.createFingerprint(modelData);
|
|
71
|
-
const hasChanges = newFingerprint !== this.lastFingerprint;
|
|
72
|
-
|
|
73
|
-
if (hasChanges) this.lastFingerprint = newFingerprint;
|
|
74
|
-
|
|
75
|
-
return hasChanges;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Produces a stable string fingerprint for the given model data.
|
|
80
|
-
* For <optgroup>, includes the label and a pipe-joined hash of its child options
|
|
81
|
-
* (value:text:selected). For plain <option>, includes its value, text, and selected state.
|
|
82
|
-
* The entire list is joined by '\n\n' to form the final fingerprint.
|
|
83
|
-
*
|
|
84
|
-
* @param {Array<HTMLOptionElement|HTMLOptGroupElement>} modelData - The current model data to fingerprint.
|
|
85
|
-
* @returns {string} A deterministic fingerprint representing the structure and selection state.
|
|
86
|
-
*/
|
|
87
|
-
private createFingerprint(modelData: Array<HTMLOptionElement | HTMLOptGroupElement>): string {
|
|
88
|
-
return modelData
|
|
89
|
-
.map((item) => {
|
|
90
|
-
if (item.tagName === "OPTGROUP") {
|
|
91
|
-
const group = item as HTMLOptGroupElement;
|
|
92
|
-
const optionsHash = Array.from(group.children)
|
|
93
|
-
.map((opt) => {
|
|
94
|
-
const o = opt as HTMLOptionElement;
|
|
95
|
-
return `${o.value}:${o.text}:${o.selected}`;
|
|
96
|
-
})
|
|
97
|
-
.join("\n");
|
|
98
|
-
return `G:${group.label}:${optionsHash}`;
|
|
99
|
-
} else {
|
|
100
|
-
const oItem = item as HTMLOptionElement;
|
|
101
|
-
return `O:${oItem.value}:${oItem.text}:${oItem.selected}`;
|
|
102
|
-
}
|
|
103
|
-
})
|
|
104
|
-
.join("\n\n");
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Builds model instances (GroupModel/OptionModel) from raw <optgroup>/<option> elements.
|
|
105
|
+
* Builds model instances ({@link GroupModel}/{@link OptionModel}) from raw `<optgroup>`/`<option>` elements.
|
|
109
106
|
* Preserves grouping relationships and returns the structured list.
|
|
110
107
|
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
108
|
+
* **Behavior**
|
|
109
|
+
* - When called while state is `INITIALIZED`, this method performs a one-time `mount()` (auto-mount).
|
|
110
|
+
* - Uses a simple in-order traversal; the current group is the last seen `<optgroup>`.
|
|
111
|
+
* - For options, the parent is inferred via `__parentGroup` identity when available.
|
|
112
|
+
*
|
|
113
|
+
* @param {Array<HTMLOptGroupElement | HTMLOptionElement>} modelData - Parsed DOM elements from the source `<select>`.
|
|
114
|
+
* @returns {Array<GroupModel | OptionModel>} The ordered list of group and option models.
|
|
113
115
|
*/
|
|
114
116
|
public createModelResources(modelData: Array<HTMLOptGroupElement | HTMLOptionElement>): Array<GroupModel | OptionModel> {
|
|
117
|
+
if (this.is(LifecycleState.INITIALIZED)) {
|
|
118
|
+
this.mount();
|
|
119
|
+
}
|
|
120
|
+
|
|
115
121
|
this.privModelList = [];
|
|
116
122
|
let currentGroup: GroupModel | null = null;
|
|
117
123
|
|
|
@@ -142,10 +148,15 @@ export class ModelManager<
|
|
|
142
148
|
* Replaces the current model list with new data and syncs it into the adapter,
|
|
143
149
|
* then refreshes the view to reflect changes.
|
|
144
150
|
*
|
|
145
|
-
*
|
|
151
|
+
* **Notes**
|
|
152
|
+
* - If the adapter is not yet initialized, syncing is skipped (safe no-op).
|
|
153
|
+
* - After sync, calls {@link refresh} with `isUpdate = false`.
|
|
154
|
+
*
|
|
155
|
+
* @param {Array<HTMLOptGroupElement | HTMLOptionElement>} modelData - New source elements to rebuild models from.
|
|
156
|
+
* @returns {Promise<void>} Resolves when the adapter (if any) completes syncing.
|
|
157
|
+
* @see Adapter#syncFromSource
|
|
146
158
|
*/
|
|
147
159
|
public async replace(modelData: Array<HTMLOptGroupElement | HTMLOptionElement>): Promise<void> {
|
|
148
|
-
this.lastFingerprint = null;
|
|
149
160
|
this.createModelResources(modelData);
|
|
150
161
|
|
|
151
162
|
if (this.privAdapterHandle) {
|
|
@@ -159,6 +170,9 @@ export class ModelManager<
|
|
|
159
170
|
/**
|
|
160
171
|
* Requests a view refresh if an adapter has been initialized,
|
|
161
172
|
* typically used after external updates to model data.
|
|
173
|
+
* **No-op** if the adapter is absent.
|
|
174
|
+
*
|
|
175
|
+
* @returns {void}
|
|
162
176
|
*/
|
|
163
177
|
public notify(): void {
|
|
164
178
|
if (!this.privAdapterHandle) return;
|
|
@@ -167,9 +181,22 @@ export class ModelManager<
|
|
|
167
181
|
|
|
168
182
|
/**
|
|
169
183
|
* Initializes adapter and recycler view instances, attaches them to a container element,
|
|
170
|
-
* and applies optional configuration overrides for adapter and recyclerView.
|
|
184
|
+
* and applies optional configuration overrides for adapter and recyclerView (via `Object.assign`).
|
|
185
|
+
*
|
|
186
|
+
* **Requirements**
|
|
187
|
+
* - Call {@link setupAdapter} and {@link setupRecyclerView} beforehand to provide constructors.
|
|
188
|
+
* - The current `privModelList` becomes the initial dataset for the adapter.
|
|
189
|
+
*
|
|
190
|
+
* **Side effects**
|
|
191
|
+
* - Sets the adapter on the recycler via `recycler.setAdapter(adapter)`.
|
|
192
|
+
*
|
|
193
|
+
* @template TExtra extends object
|
|
194
|
+
* @param {HTMLElement} viewElement - Host element for the recycler view.
|
|
195
|
+
* @param {Partial<TAdapter>} [adapterOpt={}] - Shallow overrides applied to the adapter instance.
|
|
196
|
+
* @param {Partial<RecyclerViewContract<TAdapter>> & TExtra} [recyclerViewOpt={}] - Shallow overrides applied to the recycler instance.
|
|
197
|
+
* @returns {void}
|
|
198
|
+
* @see RecyclerViewContract#setAdapter
|
|
171
199
|
*/
|
|
172
|
-
|
|
173
200
|
public load<TExtra extends object = {}>(
|
|
174
201
|
viewElement: HTMLElement,
|
|
175
202
|
adapterOpt: Partial<TAdapter> = {},
|
|
@@ -186,13 +213,25 @@ export class ModelManager<
|
|
|
186
213
|
}
|
|
187
214
|
|
|
188
215
|
/**
|
|
189
|
-
* Diffs existing models against new
|
|
216
|
+
* Diffs existing models against new `<optgroup>`/`<option>` data to update in place:
|
|
190
217
|
* reuses existing models when possible, updates positions and group membership,
|
|
191
218
|
* removes stale views, and notifies adapter and listeners about updates.
|
|
219
|
+
*
|
|
220
|
+
* **Diffing strategy**
|
|
221
|
+
* - Groups are keyed by `label`.
|
|
222
|
+
* - Options are keyed by `${value}::${text}`.
|
|
223
|
+
* - Removed groups/options are destroyed.
|
|
224
|
+
* - Per-item `position` is recomputed sequentially.
|
|
225
|
+
*
|
|
226
|
+
* **Refresh semantics**
|
|
227
|
+
* - Computes `isUpdate`: `false` on the first run and when removals occur; `true` otherwise.
|
|
228
|
+
* - Calls `adapter.updateData()` and then {@link refresh} with the computed flag.
|
|
229
|
+
*
|
|
230
|
+
* @param {Array<HTMLOptGroupElement | HTMLOptionElement>} modelData - Source elements to reconcile against.
|
|
231
|
+
* @returns {void}
|
|
232
|
+
* @see Adapter#updateData
|
|
192
233
|
*/
|
|
193
|
-
public
|
|
194
|
-
if (!this.hasRealChanges(modelData)) return;
|
|
195
|
-
|
|
234
|
+
public updateModel(modelData: Array<HTMLOptGroupElement | HTMLOptionElement>): void {
|
|
196
235
|
const oldModels = this.privModelList;
|
|
197
236
|
const newModels: Array<GroupModel | OptionModel> = [];
|
|
198
237
|
|
|
@@ -220,7 +259,7 @@ export class ModelManager<
|
|
|
220
259
|
// Label is used as key; keep original behavior.
|
|
221
260
|
const hasLabelChange = existingGroup.label !== dataVset.label;
|
|
222
261
|
if (hasLabelChange) {
|
|
223
|
-
existingGroup.
|
|
262
|
+
existingGroup.updateTarget(dataVset)
|
|
224
263
|
}
|
|
225
264
|
|
|
226
265
|
existingGroup.position = position;
|
|
@@ -242,7 +281,7 @@ export class ModelManager<
|
|
|
242
281
|
const existingOption = oldOptionMap.get(key);
|
|
243
282
|
|
|
244
283
|
if (existingOption) {
|
|
245
|
-
existingOption.
|
|
284
|
+
existingOption.updateTarget(dataVset);
|
|
246
285
|
existingOption.position = position;
|
|
247
286
|
|
|
248
287
|
const parentGroup = dataVset["__parentGroup"] as HTMLOptGroupElement | undefined;
|
|
@@ -282,12 +321,12 @@ export class ModelManager<
|
|
|
282
321
|
|
|
283
322
|
oldGroupMap.forEach((removedGroup) => {
|
|
284
323
|
isUpdate = false;
|
|
285
|
-
removedGroup.
|
|
324
|
+
removedGroup.destroy();
|
|
286
325
|
});
|
|
287
326
|
|
|
288
327
|
oldOptionMap.forEach((removedOption) => {
|
|
289
328
|
isUpdate = false;
|
|
290
|
-
removedOption.
|
|
329
|
+
removedOption.destroy();
|
|
291
330
|
});
|
|
292
331
|
|
|
293
332
|
this.privModelList = newModels;
|
|
@@ -296,43 +335,73 @@ export class ModelManager<
|
|
|
296
335
|
this.privAdapterHandle.updateData(this.privModelList as unknown as TModel[]);
|
|
297
336
|
}
|
|
298
337
|
|
|
299
|
-
// this.onUpdated();
|
|
300
338
|
this.refresh(isUpdate);
|
|
301
339
|
}
|
|
302
340
|
|
|
303
|
-
/**
|
|
304
|
-
* Hook invoked after the manager completes an update or refresh cycle.
|
|
305
|
-
* Override to run side effects (e.g., layout adjustments or analytics).
|
|
306
|
-
*/
|
|
307
|
-
public onUpdated(): void { }
|
|
308
|
-
|
|
309
341
|
/**
|
|
310
342
|
* Instructs the adapter to temporarily skip event handling (e.g., during batch updates).
|
|
311
343
|
*
|
|
312
|
-
* @param {boolean} value -
|
|
344
|
+
* @param {boolean} value - `true` to skip events; `false` to restore normal behavior.
|
|
345
|
+
* @returns {void}
|
|
313
346
|
*/
|
|
314
347
|
public skipEvent(value: boolean): void {
|
|
315
348
|
if (this.privAdapterHandle) this.privAdapterHandle.isSkipEvent = value;
|
|
316
349
|
}
|
|
317
350
|
|
|
318
351
|
/**
|
|
319
|
-
* Re-renders the recycler view if present and invokes the
|
|
320
|
-
* No-op if the recycler view is not initialized.
|
|
321
|
-
*
|
|
322
|
-
* @param isUpdate - Indicates if this refresh
|
|
352
|
+
* Re-renders the recycler view if present and invokes the lifecycle update hook.
|
|
353
|
+
* **No-op** if the recycler view is not initialized.
|
|
354
|
+
*
|
|
355
|
+
* @param {boolean} isUpdate - Indicates if this refresh follows an "update" operation (vs. full replace).
|
|
356
|
+
* @returns {void}
|
|
357
|
+
* @see Lifecycle#update
|
|
323
358
|
*/
|
|
324
359
|
public refresh(isUpdate: boolean): void {
|
|
325
360
|
if (!this.privRecyclerViewHandle) return;
|
|
326
361
|
this.privRecyclerViewHandle.refresh(isUpdate);
|
|
327
|
-
this.
|
|
362
|
+
this.update();
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Releases adapter and recycler resources and clears all references.
|
|
367
|
+
* Transitions to `DESTROYED`; subsequent calls are idempotent.
|
|
368
|
+
*
|
|
369
|
+
* **Important**
|
|
370
|
+
* - Assumes handles were created via {@link load}; calling `destroy()` before `load()` may depend
|
|
371
|
+
* on the underlying implementations' null-tolerance.
|
|
372
|
+
*
|
|
373
|
+
* @returns {void}
|
|
374
|
+
*/
|
|
375
|
+
public override destroy(): void {
|
|
376
|
+
if (this.is(LifecycleState.DESTROYED)) {
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
this.privAdapterHandle.destroy();
|
|
381
|
+
this.privRecyclerViewHandle.destroy();
|
|
382
|
+
|
|
383
|
+
this.privModelList = [];
|
|
384
|
+
this.privAdapter = null;
|
|
385
|
+
this.privAdapterHandle = null;
|
|
386
|
+
this.privRecyclerView = null;
|
|
387
|
+
this.privRecyclerViewHandle = null;
|
|
388
|
+
this.options = null;
|
|
389
|
+
this.oldPosition = 0;
|
|
390
|
+
|
|
391
|
+
super.destroy();
|
|
328
392
|
}
|
|
329
393
|
|
|
330
394
|
/**
|
|
331
395
|
* Returns handles to the current resources, including the model list,
|
|
332
396
|
* adapter instance, and recycler view instance.
|
|
397
|
+
*
|
|
398
|
+
* **Note**: The returned `adapter`/`recyclerView` may be `null` at runtime if {@link load} has not been called.
|
|
399
|
+
*
|
|
400
|
+
* @returns {{ modelList: Array<MixedItem>; adapter: TAdapter; recyclerView: RecyclerViewContract<TAdapter>; }}
|
|
401
|
+
* The current resource references.
|
|
333
402
|
*/
|
|
334
403
|
public getResources(): {
|
|
335
|
-
modelList: Array<
|
|
404
|
+
modelList: Array<MixedItem>;
|
|
336
405
|
adapter: TAdapter;
|
|
337
406
|
recyclerView: RecyclerViewContract<TAdapter>;
|
|
338
407
|
} {
|
|
@@ -346,6 +415,12 @@ export class ModelManager<
|
|
|
346
415
|
/**
|
|
347
416
|
* Triggers the adapter's pre-change pipeline for a named event,
|
|
348
417
|
* enabling observers to react before a change is applied.
|
|
418
|
+
*
|
|
419
|
+
* **Delegates** to {@link Adapter.changingProp}.
|
|
420
|
+
*
|
|
421
|
+
* @param {string} event_name - Logical event name (consumer-defined).
|
|
422
|
+
* @returns {Promise<void> | undefined} The adapter's promise, or `undefined` if the adapter is not initialized.
|
|
423
|
+
* @fires changing
|
|
349
424
|
*/
|
|
350
425
|
public triggerChanging(event_name: string): Promise<void> {
|
|
351
426
|
return this.privAdapterHandle?.changingProp(event_name);
|
|
@@ -354,6 +429,12 @@ export class ModelManager<
|
|
|
354
429
|
/**
|
|
355
430
|
* Triggers the adapter's post-change pipeline for a named event,
|
|
356
431
|
* notifying observers after a change has been applied.
|
|
432
|
+
*
|
|
433
|
+
* **Delegates** to {@link Adapter.changeProp}.
|
|
434
|
+
*
|
|
435
|
+
* @param {string} event_name - Logical event name (consumer-defined).
|
|
436
|
+
* @returns {Promise<void> | undefined} The adapter's promise, or `undefined` if the adapter is not initialized.
|
|
437
|
+
* @fires changed
|
|
357
438
|
*/
|
|
358
439
|
public triggerChanged(event_name: string): Promise<void> {
|
|
359
440
|
return this.privAdapterHandle?.changeProp(event_name);
|