selective-ui 1.3.1 → 1.4.1
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 +0 -6
- package/dist/selective-ui.css.map +1 -1
- package/dist/selective-ui.esm.js +271 -559
- 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 +273 -561
- package/dist/selective-ui.umd.js.map +1 -1
- package/package.json +12 -12
- package/src/ts/adapter/mixed-adapter.ts +147 -68
- package/src/ts/components/accessorybox.ts +14 -11
- package/src/ts/components/directive.ts +1 -1
- package/src/ts/components/option-handle.ts +12 -9
- package/src/ts/components/placeholder.ts +5 -5
- package/src/ts/components/popup/empty-state.ts +5 -5
- package/src/ts/components/popup/loading-state.ts +5 -5
- package/src/ts/components/popup/popup.ts +138 -76
- package/src/ts/components/searchbox.ts +17 -13
- package/src/ts/components/selectbox.ts +258 -83
- package/src/ts/core/base/adapter.ts +39 -14
- package/src/ts/core/base/fenwick.ts +3 -2
- package/src/ts/core/base/lifecycle.ts +14 -4
- package/src/ts/core/base/model.ts +17 -15
- package/src/ts/core/base/recyclerview.ts +7 -5
- package/src/ts/core/base/view.ts +10 -5
- package/src/ts/core/base/virtual-recyclerview.ts +89 -37
- package/src/ts/core/model-manager.ts +48 -21
- package/src/ts/core/search-controller.ts +174 -56
- package/src/ts/global.ts +5 -8
- package/src/ts/index.ts +2 -2
- package/src/ts/models/group-model.ts +33 -8
- package/src/ts/models/option-model.ts +60 -19
- package/src/ts/services/dataset-observer.ts +6 -3
- package/src/ts/services/ea-observer.ts +1 -1
- package/src/ts/services/effector.ts +22 -12
- package/src/ts/services/refresher.ts +7 -3
- package/src/ts/services/resize-observer.ts +24 -11
- package/src/ts/services/select-observer.ts +2 -2
- package/src/ts/types/components/popup.type.ts +18 -1
- package/src/ts/types/components/searchbox.type.ts +43 -30
- package/src/ts/types/components/state.box.type.ts +1 -1
- package/src/ts/types/core/base/adapter.type.ts +13 -5
- package/src/ts/types/core/base/lifecycle.type.ts +1 -2
- package/src/ts/types/core/base/model.type.ts +3 -3
- package/src/ts/types/core/base/recyclerview.type.ts +7 -5
- package/src/ts/types/core/base/view.type.ts +6 -6
- package/src/ts/types/core/base/virtual-recyclerview.type.ts +45 -46
- package/src/ts/types/core/search-controller.type.ts +18 -2
- package/src/ts/types/css.d.ts +1 -0
- package/src/ts/types/plugins/plugin.type.ts +2 -2
- package/src/ts/types/services/effector.type.ts +25 -25
- package/src/ts/types/services/resize-observer.type.ts +23 -12
- package/src/ts/types/utils/callback-scheduler.type.ts +2 -2
- package/src/ts/types/utils/ievents.type.ts +1 -1
- package/src/ts/types/utils/istorage.type.ts +62 -60
- package/src/ts/types/utils/libs.type.ts +19 -17
- package/src/ts/types/utils/selective.type.ts +6 -3
- package/src/ts/types/views/view.group.type.ts +9 -5
- package/src/ts/types/views/view.option.type.ts +39 -17
- package/src/ts/utils/callback-scheduler.ts +12 -7
- package/src/ts/utils/ievents.ts +12 -5
- package/src/ts/utils/istorage.ts +5 -3
- package/src/ts/utils/libs.ts +122 -43
- package/src/ts/utils/selective.ts +15 -8
- package/src/ts/views/group-view.ts +11 -9
- package/src/ts/views/option-view.ts +37 -18
|
@@ -42,7 +42,7 @@ export class EmptyState extends Lifecycle {
|
|
|
42
42
|
* - Intended to be appended by the parent container (component does not auto-attach).
|
|
43
43
|
* - Removed from DOM during {@link destroy}.
|
|
44
44
|
*/
|
|
45
|
-
public node
|
|
45
|
+
public node?: HTMLDivElement;
|
|
46
46
|
|
|
47
47
|
/**
|
|
48
48
|
* Configuration source for empty state messages.
|
|
@@ -51,7 +51,7 @@ export class EmptyState extends Lifecycle {
|
|
|
51
51
|
* - `textNoData` (for `"nodata"`)
|
|
52
52
|
* - `textNotFound` (for `"notfound"`)
|
|
53
53
|
*/
|
|
54
|
-
public options
|
|
54
|
+
public options?: SelectiveOptions;
|
|
55
55
|
|
|
56
56
|
/**
|
|
57
57
|
* Creates a new {@link EmptyState}.
|
|
@@ -59,9 +59,9 @@ export class EmptyState extends Lifecycle {
|
|
|
59
59
|
* If `options` are provided, initialization runs immediately (creates {@link node} and
|
|
60
60
|
* transitions to `INITIALIZED`).
|
|
61
61
|
*
|
|
62
|
-
* @param {SelectiveOptions
|
|
62
|
+
* @param {SelectiveOptions} [options=null] - Configuration containing empty state messages.
|
|
63
63
|
*/
|
|
64
|
-
public constructor(options
|
|
64
|
+
public constructor(options?: SelectiveOptions) {
|
|
65
65
|
super();
|
|
66
66
|
if (options) this.initialize(options);
|
|
67
67
|
}
|
|
@@ -159,4 +159,4 @@ export class EmptyState extends Lifecycle {
|
|
|
159
159
|
|
|
160
160
|
super.destroy();
|
|
161
161
|
}
|
|
162
|
-
}
|
|
162
|
+
}
|
|
@@ -41,7 +41,7 @@ export class LoadingState extends Lifecycle {
|
|
|
41
41
|
* - Intended to be appended by the parent container (component does not auto-attach).
|
|
42
42
|
* - Removed from DOM during {@link destroy}.
|
|
43
43
|
*/
|
|
44
|
-
public node
|
|
44
|
+
public node?: HTMLDivElement;
|
|
45
45
|
|
|
46
46
|
/**
|
|
47
47
|
* Configuration source for loading message text.
|
|
@@ -49,7 +49,7 @@ export class LoadingState extends Lifecycle {
|
|
|
49
49
|
* Expected to provide:
|
|
50
50
|
* - `textLoading` (displayed while loading is active)
|
|
51
51
|
*/
|
|
52
|
-
public options
|
|
52
|
+
public options?: SelectiveOptions;
|
|
53
53
|
|
|
54
54
|
/**
|
|
55
55
|
* Creates a new {@link LoadingState}.
|
|
@@ -57,9 +57,9 @@ export class LoadingState extends Lifecycle {
|
|
|
57
57
|
* If `options` are provided, initialization runs immediately (creates {@link node} and
|
|
58
58
|
* transitions to `INITIALIZED`).
|
|
59
59
|
*
|
|
60
|
-
* @param {SelectiveOptions
|
|
60
|
+
* @param {SelectiveOptions} [options=null] - Configuration containing the loading message text.
|
|
61
61
|
*/
|
|
62
|
-
public constructor(options
|
|
62
|
+
public constructor(options?: SelectiveOptions) {
|
|
63
63
|
super();
|
|
64
64
|
if (options) this.initialize(options);
|
|
65
65
|
}
|
|
@@ -157,4 +157,4 @@ export class LoadingState extends Lifecycle {
|
|
|
157
157
|
|
|
158
158
|
super.destroy();
|
|
159
159
|
}
|
|
160
|
-
}
|
|
160
|
+
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { Libs } from "../../utils/libs";
|
|
2
2
|
import { OptionHandle } from "../option-handle";
|
|
3
|
-
import type {
|
|
3
|
+
import type {
|
|
4
|
+
EffectorInterface,
|
|
5
|
+
DimensionObject,
|
|
6
|
+
} from "../../types/services/effector.type";
|
|
4
7
|
import { ModelManager } from "../../core/model-manager";
|
|
5
8
|
import { EmptyState } from "./empty-state";
|
|
6
9
|
import { LoadingState } from "./loading-state";
|
|
@@ -8,9 +11,17 @@ import { MixedAdapter } from "../../adapter/mixed-adapter";
|
|
|
8
11
|
import type { RecyclerViewContract } from "../../types/core/base/recyclerview.type";
|
|
9
12
|
import { ResizeObserverService } from "../../services/resize-observer";
|
|
10
13
|
import { ElementMetrics } from "../../types/services/resize-observer.type";
|
|
11
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
MixedItem,
|
|
16
|
+
VisibilityStats,
|
|
17
|
+
} from "../../types/core/base/mixed-adapter.type";
|
|
12
18
|
import { SelectiveOptions } from "../../types/utils/selective.type";
|
|
13
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
ParentBinderMapLike,
|
|
21
|
+
PopupLocaltion,
|
|
22
|
+
PopupPosition,
|
|
23
|
+
VirtualRecyclerOptions,
|
|
24
|
+
} from "../../types/components/popup.type";
|
|
14
25
|
import { Lifecycle } from "../../core/base/lifecycle";
|
|
15
26
|
import { LifecycleState } from "../../types/core/base/lifecycle.type";
|
|
16
27
|
import { MountViewResult } from "src/ts/types/utils/libs.type";
|
|
@@ -36,49 +47,50 @@ import { MountViewResult } from "src/ts/types/utils/libs.type";
|
|
|
36
47
|
*/
|
|
37
48
|
export class Popup extends Lifecycle {
|
|
38
49
|
/** ModelManager reference used to provide adapter and recycler view resources */
|
|
39
|
-
private modelManager
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
50
|
+
private modelManager?: ModelManager<
|
|
51
|
+
MixedItem,
|
|
52
|
+
MixedAdapter
|
|
53
|
+
>; /** Active configuration for the popup behavior and text labels */
|
|
54
|
+
private options?: SelectiveOptions;
|
|
43
55
|
|
|
44
56
|
/** Indicates whether the popup DOM has been attached to the document body at least once */
|
|
45
57
|
private isCreated = false;
|
|
46
58
|
|
|
47
59
|
/** Mixed adapter handling items/models and visibility stats */
|
|
48
|
-
public optionAdapter
|
|
60
|
+
public optionAdapter?: MixedAdapter;
|
|
49
61
|
|
|
50
62
|
/** Root popup container (the floating panel) */
|
|
51
|
-
public node
|
|
63
|
+
public node?: HTMLDivElement;
|
|
52
64
|
|
|
53
65
|
/** Effector service used to measure/animate the popup */
|
|
54
|
-
private effSvc
|
|
66
|
+
private effSvc?: EffectorInterface;
|
|
55
67
|
|
|
56
68
|
/** Resize observer to react to parent panel size changes */
|
|
57
|
-
private resizeObser
|
|
69
|
+
private resizeObser?: ResizeObserverService;
|
|
58
70
|
|
|
59
71
|
/** Binder map for parent elements (anchors to compute placement from) */
|
|
60
|
-
private parent
|
|
72
|
+
private parent?: ParentBinderMapLike;
|
|
61
73
|
|
|
62
74
|
/** Header control exposing "Select All / Deselect All" actions */
|
|
63
|
-
public optionHandle
|
|
75
|
+
public optionHandle?: OptionHandle;
|
|
64
76
|
|
|
65
77
|
/** "Empty / Not found" feedback component */
|
|
66
|
-
public emptyState
|
|
78
|
+
public emptyState?: EmptyState;
|
|
67
79
|
|
|
68
80
|
/** Loading indicator component */
|
|
69
|
-
public loadingState
|
|
81
|
+
public loadingState?: LoadingState;
|
|
70
82
|
|
|
71
83
|
/** Virtualized recycler view for performant lists */
|
|
72
|
-
public recyclerView
|
|
84
|
+
public recyclerView?: RecyclerViewContract<MixedAdapter>;
|
|
73
85
|
|
|
74
86
|
/** Container that holds the list of options */
|
|
75
|
-
private optionsContainer
|
|
87
|
+
private optionsContainer?: HTMLDivElement;
|
|
76
88
|
|
|
77
89
|
/** Scroll handler used by infinite scroll */
|
|
78
|
-
private scrollListener
|
|
90
|
+
private scrollListener?: () => Promise<void>;
|
|
79
91
|
|
|
80
92
|
/** Handle to defer hiding the loading indicator */
|
|
81
|
-
private hideLoadHandle
|
|
93
|
+
private hideLoadHandle?: ReturnType<typeof setTimeout>;
|
|
82
94
|
|
|
83
95
|
/** Default virtual scroll configuration (tuned for typical option heights) */
|
|
84
96
|
private virtualScrollConfig = {
|
|
@@ -87,7 +99,7 @@ export class Popup extends Lifecycle {
|
|
|
87
99
|
/** Number of extra items to render above/below the viewport */
|
|
88
100
|
overscan: 8,
|
|
89
101
|
/** Whether the list contains items with dynamic (non-uniform) heights */
|
|
90
|
-
dynamicHeights: true
|
|
102
|
+
dynamicHeights: true,
|
|
91
103
|
};
|
|
92
104
|
|
|
93
105
|
/**
|
|
@@ -100,9 +112,9 @@ export class Popup extends Lifecycle {
|
|
|
100
112
|
* @param modelManager - Model manager that supplies the adapter and recycler view.
|
|
101
113
|
*/
|
|
102
114
|
public constructor(
|
|
103
|
-
select
|
|
104
|
-
options
|
|
105
|
-
modelManager
|
|
115
|
+
select?: HTMLSelectElement,
|
|
116
|
+
options?: SelectiveOptions,
|
|
117
|
+
modelManager?: ModelManager<MixedItem, MixedAdapter>,
|
|
106
118
|
) {
|
|
107
119
|
super();
|
|
108
120
|
this.modelManager = modelManager;
|
|
@@ -123,8 +135,12 @@ export class Popup extends Lifecycle {
|
|
|
123
135
|
* @param options - Panel configuration (dimensions, IDs, labels, flags).
|
|
124
136
|
* @throws Error if a ModelManager is not provided.
|
|
125
137
|
*/
|
|
126
|
-
private initialize(
|
|
127
|
-
|
|
138
|
+
private initialize(
|
|
139
|
+
select: HTMLSelectElement,
|
|
140
|
+
options: SelectiveOptions,
|
|
141
|
+
): void {
|
|
142
|
+
if (!this.modelManager)
|
|
143
|
+
throw new Error("Popup requires a ModelManager instance.");
|
|
128
144
|
|
|
129
145
|
this.optionHandle = new OptionHandle(options);
|
|
130
146
|
this.emptyState = new EmptyState(options);
|
|
@@ -153,27 +169,32 @@ export class Popup extends Lifecycle {
|
|
|
153
169
|
},
|
|
154
170
|
},
|
|
155
171
|
},
|
|
156
|
-
null
|
|
172
|
+
null,
|
|
157
173
|
);
|
|
158
174
|
|
|
159
175
|
this.node = nodeMounted.view as HTMLDivElement;
|
|
160
|
-
this.optionsContainer = nodeMounted.tags
|
|
176
|
+
this.optionsContainer = nodeMounted.tags
|
|
177
|
+
.OptionsContainer as HTMLDivElement;
|
|
161
178
|
|
|
162
179
|
this.parent = Libs.getBinderMap<ParentBinderMapLike>(select);
|
|
163
180
|
this.options = options;
|
|
164
181
|
this.init();
|
|
165
|
-
|
|
166
|
-
const recyclerViewOpt = options.virtualScroll
|
|
167
|
-
? {
|
|
168
|
-
scrollEl: this.node,
|
|
169
|
-
estimateItemHeight: this.virtualScrollConfig.estimateItemHeight,
|
|
170
|
-
overscan: this.virtualScrollConfig.overscan,
|
|
171
|
-
dynamicHeights: this.virtualScrollConfig.dynamicHeights }
|
|
172
|
-
: {}
|
|
173
|
-
;
|
|
174
182
|
|
|
183
|
+
const recyclerViewOpt = options.virtualScroll
|
|
184
|
+
? {
|
|
185
|
+
scrollEl: this.node,
|
|
186
|
+
estimateItemHeight:
|
|
187
|
+
this.virtualScrollConfig.estimateItemHeight,
|
|
188
|
+
overscan: this.virtualScrollConfig.overscan,
|
|
189
|
+
dynamicHeights: this.virtualScrollConfig.dynamicHeights,
|
|
190
|
+
}
|
|
191
|
+
: {};
|
|
175
192
|
// Load ModelManager resources into the list container
|
|
176
|
-
this.modelManager.load<VirtualRecyclerOptions>(
|
|
193
|
+
this.modelManager.load<VirtualRecyclerOptions>(
|
|
194
|
+
this.optionsContainer,
|
|
195
|
+
{ isMultiple: options.multiple, options: options },
|
|
196
|
+
recyclerViewOpt,
|
|
197
|
+
);
|
|
177
198
|
|
|
178
199
|
const MMResources = this.modelManager.getResources();
|
|
179
200
|
|
|
@@ -203,7 +224,14 @@ export class Popup extends Lifecycle {
|
|
|
203
224
|
* - Triggers a resize to accommodate layout changes
|
|
204
225
|
*/
|
|
205
226
|
public async showLoading(): Promise<void> {
|
|
206
|
-
if (
|
|
227
|
+
if (
|
|
228
|
+
!this.options ||
|
|
229
|
+
!this.loadingState ||
|
|
230
|
+
!this.optionHandle ||
|
|
231
|
+
!this.optionAdapter ||
|
|
232
|
+
!this.modelManager
|
|
233
|
+
)
|
|
234
|
+
return;
|
|
207
235
|
|
|
208
236
|
if (this.hideLoadHandle) clearTimeout(this.hideLoadHandle);
|
|
209
237
|
this.modelManager.skipEvent(true);
|
|
@@ -211,7 +239,9 @@ export class Popup extends Lifecycle {
|
|
|
211
239
|
if (Libs.string2Boolean(this.options.loadingfield) === false) return;
|
|
212
240
|
|
|
213
241
|
this.emptyState.hide();
|
|
214
|
-
this.loadingState.show(
|
|
242
|
+
this.loadingState.show(
|
|
243
|
+
this.optionAdapter.getVisibilityStats().hasVisible,
|
|
244
|
+
);
|
|
215
245
|
this.triggerResize();
|
|
216
246
|
}
|
|
217
247
|
|
|
@@ -222,7 +252,13 @@ export class Popup extends Lifecycle {
|
|
|
222
252
|
* Debounce: Uses `animationtime` as a short delay before hiding the loading indicator.
|
|
223
253
|
*/
|
|
224
254
|
public async hideLoading(): Promise<void> {
|
|
225
|
-
if (
|
|
255
|
+
if (
|
|
256
|
+
!this.options ||
|
|
257
|
+
!this.loadingState ||
|
|
258
|
+
!this.optionAdapter ||
|
|
259
|
+
!this.modelManager
|
|
260
|
+
)
|
|
261
|
+
return;
|
|
226
262
|
|
|
227
263
|
if (this.hideLoadHandle) clearTimeout(this.hideLoadHandle);
|
|
228
264
|
|
|
@@ -268,7 +304,13 @@ export class Popup extends Lifecycle {
|
|
|
268
304
|
* @param stats - Optionally provide precomputed visibility stats.
|
|
269
305
|
*/
|
|
270
306
|
private updateEmptyState(stats?: VisibilityStats): void {
|
|
271
|
-
if (
|
|
307
|
+
if (
|
|
308
|
+
!this.optionAdapter ||
|
|
309
|
+
!this.emptyState ||
|
|
310
|
+
!this.optionHandle ||
|
|
311
|
+
!this.optionsContainer
|
|
312
|
+
)
|
|
313
|
+
return;
|
|
272
314
|
|
|
273
315
|
const s = stats ?? this.optionAdapter.getVisibilityStats();
|
|
274
316
|
|
|
@@ -293,7 +335,10 @@ export class Popup extends Lifecycle {
|
|
|
293
335
|
* @param propName - Adapter property name to observe.
|
|
294
336
|
* @param callback - Handler invoked before the property changes.
|
|
295
337
|
*/
|
|
296
|
-
public onAdapterPropChanging(
|
|
338
|
+
public onAdapterPropChanging(
|
|
339
|
+
propName: string,
|
|
340
|
+
callback: (...args: unknown[]) => void,
|
|
341
|
+
): void {
|
|
297
342
|
this.optionAdapter?.onPropChanging(propName, callback);
|
|
298
343
|
}
|
|
299
344
|
|
|
@@ -303,7 +348,10 @@ export class Popup extends Lifecycle {
|
|
|
303
348
|
* @param propName - Adapter property name to observe.
|
|
304
349
|
* @param callback - Handler invoked after the property changes.
|
|
305
350
|
*/
|
|
306
|
-
public onAdapterPropChanged(
|
|
351
|
+
public onAdapterPropChanged(
|
|
352
|
+
propName: string,
|
|
353
|
+
callback: (...args: unknown[]) => void,
|
|
354
|
+
): void {
|
|
307
355
|
this.optionAdapter?.onPropChanged(propName, callback);
|
|
308
356
|
}
|
|
309
357
|
|
|
@@ -357,8 +405,15 @@ export class Popup extends Lifecycle {
|
|
|
357
405
|
* @param callback - Optional callback invoked when the opening animation completes.
|
|
358
406
|
* @param isShowEmptyState - If true, applies the empty/not-found state before animation.
|
|
359
407
|
*/
|
|
360
|
-
public open(callback
|
|
361
|
-
if (
|
|
408
|
+
public open(callback?: () => void, isShowEmptyState?: boolean): void {
|
|
409
|
+
if (
|
|
410
|
+
!this.node ||
|
|
411
|
+
!this.options ||
|
|
412
|
+
!this.optionHandle ||
|
|
413
|
+
!this.parent ||
|
|
414
|
+
!this.effSvc
|
|
415
|
+
)
|
|
416
|
+
return;
|
|
362
417
|
|
|
363
418
|
// Ensure one-time initialization
|
|
364
419
|
this.load();
|
|
@@ -373,7 +428,8 @@ export class Popup extends Lifecycle {
|
|
|
373
428
|
|
|
374
429
|
// Compute placement based on parent anchor
|
|
375
430
|
const location = this.getParentLocation();
|
|
376
|
-
const { position, top, maxHeight, realHeight } =
|
|
431
|
+
const { position, top, maxHeight, realHeight } =
|
|
432
|
+
this.calculatePosition(location);
|
|
377
433
|
|
|
378
434
|
// Run expand animation
|
|
379
435
|
this.effSvc.expand({
|
|
@@ -415,8 +471,14 @@ export class Popup extends Lifecycle {
|
|
|
415
471
|
*
|
|
416
472
|
* @param callback - Optional callback invoked when the closing animation completes.
|
|
417
473
|
*/
|
|
418
|
-
public close(callback
|
|
419
|
-
if (
|
|
474
|
+
public close(callback?: () => void): void {
|
|
475
|
+
if (
|
|
476
|
+
!this.isCreated ||
|
|
477
|
+
!this.options ||
|
|
478
|
+
!this.resizeObser ||
|
|
479
|
+
!this.effSvc
|
|
480
|
+
)
|
|
481
|
+
return;
|
|
420
482
|
const rv: any = this.recyclerView;
|
|
421
483
|
rv?.suspend?.();
|
|
422
484
|
|
|
@@ -445,10 +507,14 @@ export class Popup extends Lifecycle {
|
|
|
445
507
|
*/
|
|
446
508
|
public setupInfiniteScroll(
|
|
447
509
|
searchController: {
|
|
448
|
-
getPaginationState(): {
|
|
510
|
+
getPaginationState(): {
|
|
511
|
+
isPaginationEnabled: boolean;
|
|
512
|
+
isLoading: boolean;
|
|
513
|
+
hasMore: boolean;
|
|
514
|
+
};
|
|
449
515
|
loadMore(): Promise<{ success: boolean; message?: string }>;
|
|
450
516
|
},
|
|
451
|
-
_options?: SelectiveOptions
|
|
517
|
+
_options?: SelectiveOptions,
|
|
452
518
|
): void {
|
|
453
519
|
if (!this.node) return;
|
|
454
520
|
|
|
@@ -475,7 +541,7 @@ export class Popup extends Lifecycle {
|
|
|
475
541
|
|
|
476
542
|
this.node.addEventListener("scroll", this.scrollListener);
|
|
477
543
|
}
|
|
478
|
-
|
|
544
|
+
|
|
479
545
|
/**
|
|
480
546
|
* Completely tears down the popup and releases all resources.
|
|
481
547
|
*
|
|
@@ -522,7 +588,7 @@ export class Popup extends Lifecycle {
|
|
|
522
588
|
this.node.remove();
|
|
523
589
|
}
|
|
524
590
|
}
|
|
525
|
-
|
|
591
|
+
|
|
526
592
|
this.node = null;
|
|
527
593
|
this.optionsContainer = null;
|
|
528
594
|
this.modelManager = null;
|
|
@@ -544,14 +610,7 @@ export class Popup extends Lifecycle {
|
|
|
544
610
|
* Computes the parent panel's location and box metrics
|
|
545
611
|
* (size, position, padding, border). Accounts for iOS visual viewport offsets.
|
|
546
612
|
*/
|
|
547
|
-
private getParentLocation(): {
|
|
548
|
-
width: number;
|
|
549
|
-
height: number;
|
|
550
|
-
top: number;
|
|
551
|
-
left: number;
|
|
552
|
-
padding: { top: number; right: number; bottom: number; left: number };
|
|
553
|
-
border: { top: number; right: number; bottom: number; left: number };
|
|
554
|
-
} {
|
|
613
|
+
private getParentLocation(): PopupLocaltion {
|
|
555
614
|
const viewPanel = this.parent!.container.tags.ViewPanel;
|
|
556
615
|
const rect = viewPanel.getBoundingClientRect();
|
|
557
616
|
const style = window.getComputedStyle(viewPanel);
|
|
@@ -584,13 +643,7 @@ export class Popup extends Lifecycle {
|
|
|
584
643
|
*
|
|
585
644
|
* Returns the final placement, top offset, and computed heights.
|
|
586
645
|
*/
|
|
587
|
-
private calculatePosition(location:
|
|
588
|
-
position: "top" | "bottom";
|
|
589
|
-
top: number;
|
|
590
|
-
maxHeight: number;
|
|
591
|
-
realHeight: number;
|
|
592
|
-
contentHeight: number;
|
|
593
|
-
} {
|
|
646
|
+
private calculatePosition(location: PopupLocaltion): PopupPosition {
|
|
594
647
|
const vv = window.visualViewport;
|
|
595
648
|
const is_ios = Libs.IsIOS();
|
|
596
649
|
|
|
@@ -599,13 +652,17 @@ export class Popup extends Lifecycle {
|
|
|
599
652
|
const gap = 3;
|
|
600
653
|
const safeMargin = 15;
|
|
601
654
|
|
|
602
|
-
const dimensions: DimensionObject =
|
|
655
|
+
const dimensions: DimensionObject =
|
|
656
|
+
this.effSvc!.getHiddenDimensions("flex");
|
|
603
657
|
const contentHeight = dimensions.scrollHeight;
|
|
604
658
|
|
|
605
|
-
const configMaxHeight =
|
|
606
|
-
|
|
659
|
+
const configMaxHeight =
|
|
660
|
+
parseFloat(this.options?.panelHeight ?? "220") || 220;
|
|
661
|
+
const configMinHeight =
|
|
662
|
+
parseFloat(this.options?.panelMinHeight ?? "100") || 100;
|
|
607
663
|
|
|
608
|
-
const spaceBelow =
|
|
664
|
+
const spaceBelow =
|
|
665
|
+
viewportHeight - (location.top + location.height) - gap;
|
|
609
666
|
const spaceAbove = location.top - gap;
|
|
610
667
|
|
|
611
668
|
let position: "top" | "bottom" = "bottom";
|
|
@@ -614,7 +671,11 @@ export class Popup extends Lifecycle {
|
|
|
614
671
|
|
|
615
672
|
const heightOri = spaceBelow - safeMargin;
|
|
616
673
|
|
|
617
|
-
if (
|
|
674
|
+
if (
|
|
675
|
+
realHeight >= configMinHeight
|
|
676
|
+
? heightOri >= configMinHeight
|
|
677
|
+
: heightOri >= realHeight
|
|
678
|
+
) {
|
|
618
679
|
position = "bottom";
|
|
619
680
|
maxHeight = Math.min(spaceBelow - safeMargin, configMaxHeight);
|
|
620
681
|
} else if (spaceAbove >= Math.max(realHeight, configMinHeight)) {
|
|
@@ -633,7 +694,7 @@ export class Popup extends Lifecycle {
|
|
|
633
694
|
realHeight = Math.min(contentHeight, maxHeight);
|
|
634
695
|
|
|
635
696
|
const viewportOffsetY = vv && is_ios ? vv.offsetTop : 0;
|
|
636
|
-
|
|
697
|
+
|
|
637
698
|
const top =
|
|
638
699
|
position === "bottom"
|
|
639
700
|
? location.top + location.height + gap + viewportOffsetY
|
|
@@ -646,10 +707,11 @@ export class Popup extends Lifecycle {
|
|
|
646
707
|
* Handles parent resize by recalculating placement and dimensions,
|
|
647
708
|
* then animates the popup to the new size and position.
|
|
648
709
|
*/
|
|
649
|
-
private handleResize(location:
|
|
710
|
+
private handleResize(location: PopupLocaltion): void {
|
|
650
711
|
if (!this.options || !this.effSvc) return;
|
|
651
712
|
|
|
652
|
-
const { position, top, maxHeight, realHeight } =
|
|
713
|
+
const { position, top, maxHeight, realHeight } =
|
|
714
|
+
this.calculatePosition(location);
|
|
653
715
|
|
|
654
716
|
this.effSvc.resize({
|
|
655
717
|
duration: this.options.animationtime,
|
|
@@ -662,4 +724,4 @@ export class Popup extends Lifecycle {
|
|
|
662
724
|
animate: true,
|
|
663
725
|
});
|
|
664
726
|
}
|
|
665
|
-
}
|
|
727
|
+
}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { Libs } from "../utils/libs";
|
|
2
2
|
import { MountViewResult } from "../types/utils/libs.type";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
NavigateHandler,
|
|
5
|
+
SearchBoxTags,
|
|
6
|
+
SearchHandler,
|
|
7
|
+
} from "../types/components/searchbox.type";
|
|
4
8
|
import { SelectiveOptions } from "../types/utils/selective.type";
|
|
5
9
|
import { Lifecycle } from "../core/base/lifecycle";
|
|
6
10
|
import { LifecycleState } from "../types/core/base/lifecycle.type";
|
|
@@ -59,7 +63,7 @@ export class SearchBox extends Lifecycle {
|
|
|
59
63
|
*
|
|
60
64
|
* @param options - Configuration such as placeholder, accessibility IDs, and flags.
|
|
61
65
|
*/
|
|
62
|
-
constructor(options
|
|
66
|
+
constructor(options?: SelectiveOptions) {
|
|
63
67
|
super();
|
|
64
68
|
this.options = options;
|
|
65
69
|
if (options) this.initialize(options);
|
|
@@ -73,7 +77,7 @@ export class SearchBox extends Lifecycle {
|
|
|
73
77
|
*
|
|
74
78
|
* @internal
|
|
75
79
|
*/
|
|
76
|
-
private nodeMounted
|
|
80
|
+
private nodeMounted?: MountViewResult<SearchBoxTags>;
|
|
77
81
|
|
|
78
82
|
/**
|
|
79
83
|
* Root container node of this component.
|
|
@@ -81,7 +85,7 @@ export class SearchBox extends Lifecycle {
|
|
|
81
85
|
* Created during {@link initialize} and removed during {@link destroy}.
|
|
82
86
|
* Visibility is controlled by adding/removing the `hide` class.
|
|
83
87
|
*/
|
|
84
|
-
public node
|
|
88
|
+
public node?: HTMLDivElement;
|
|
85
89
|
|
|
86
90
|
/**
|
|
87
91
|
* The `<input type="search">` element used to capture user queries.
|
|
@@ -91,7 +95,7 @@ export class SearchBox extends Lifecycle {
|
|
|
91
95
|
*
|
|
92
96
|
* @internal
|
|
93
97
|
*/
|
|
94
|
-
private SearchInput
|
|
98
|
+
private SearchInput?: HTMLInputElement;
|
|
95
99
|
|
|
96
100
|
/**
|
|
97
101
|
* External "search changed" hook.
|
|
@@ -102,7 +106,7 @@ export class SearchBox extends Lifecycle {
|
|
|
102
106
|
* Ownership:
|
|
103
107
|
* - Implementations typically filter adapter/model state and refresh the list.
|
|
104
108
|
*/
|
|
105
|
-
public onSearch
|
|
109
|
+
public onSearch?: SearchHandler;
|
|
106
110
|
|
|
107
111
|
/**
|
|
108
112
|
* Options snapshot used for behavior toggles and attributes.
|
|
@@ -116,7 +120,7 @@ export class SearchBox extends Lifecycle {
|
|
|
116
120
|
*
|
|
117
121
|
* @internal
|
|
118
122
|
*/
|
|
119
|
-
private options
|
|
123
|
+
private options?: SelectiveOptions;
|
|
120
124
|
|
|
121
125
|
/**
|
|
122
126
|
* External navigation hook for list traversal.
|
|
@@ -127,21 +131,21 @@ export class SearchBox extends Lifecycle {
|
|
|
127
131
|
*
|
|
128
132
|
* Typical consumers update highlight/active option in Adapter/RecyclerView.
|
|
129
133
|
*/
|
|
130
|
-
public onNavigate
|
|
134
|
+
public onNavigate?: NavigateHandler;
|
|
131
135
|
|
|
132
136
|
/**
|
|
133
137
|
* External "commit" hook (Enter key).
|
|
134
138
|
*
|
|
135
139
|
* Typical consumers confirm selection of the highlighted option or submit the current state.
|
|
136
140
|
*/
|
|
137
|
-
public onEnter
|
|
138
|
-
|
|
141
|
+
public onEnter?: () => void;
|
|
142
|
+
|
|
139
143
|
/**
|
|
140
144
|
* External "cancel" hook (Escape key).
|
|
141
145
|
*
|
|
142
146
|
* Typical consumers close the popup, clear highlight, or reset interaction mode.
|
|
143
147
|
*/
|
|
144
|
-
public onEsc
|
|
148
|
+
public onEsc?: () => void;
|
|
145
149
|
|
|
146
150
|
/**
|
|
147
151
|
* Initializes DOM, ARIA attributes, and interaction listeners.
|
|
@@ -353,7 +357,7 @@ export class SearchBox extends Lifecycle {
|
|
|
353
357
|
if (this.is(LifecycleState.DESTROYED)) {
|
|
354
358
|
return;
|
|
355
359
|
}
|
|
356
|
-
|
|
360
|
+
|
|
357
361
|
this.node?.remove();
|
|
358
362
|
this.nodeMounted = null;
|
|
359
363
|
this.node = null;
|
|
@@ -366,4 +370,4 @@ export class SearchBox extends Lifecycle {
|
|
|
366
370
|
|
|
367
371
|
super.destroy();
|
|
368
372
|
}
|
|
369
|
-
}
|
|
373
|
+
}
|