selective-ui 1.0.2 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +7 -2
  3. package/dist/selective-ui.css +567 -567
  4. package/dist/selective-ui.css.map +1 -1
  5. package/dist/selective-ui.esm.js +6186 -6047
  6. package/dist/selective-ui.esm.js.map +1 -1
  7. package/dist/selective-ui.esm.min.js +1 -1
  8. package/dist/selective-ui.esm.min.js.br +0 -0
  9. package/dist/selective-ui.min.js +2 -2
  10. package/dist/selective-ui.min.js.br +0 -0
  11. package/dist/selective-ui.umd.js +6186 -6047
  12. package/dist/selective-ui.umd.js.map +1 -1
  13. package/package.json +68 -68
  14. package/src/css/components/accessorybox.css +63 -63
  15. package/src/css/components/directive.css +19 -19
  16. package/src/css/components/empty-state.css +25 -25
  17. package/src/css/components/loading-state.css +25 -25
  18. package/src/css/components/optgroup.css +61 -61
  19. package/src/css/components/option-handle.css +33 -33
  20. package/src/css/components/option.css +129 -129
  21. package/src/css/components/placeholder.css +14 -14
  22. package/src/css/components/popup.css +38 -38
  23. package/src/css/components/searchbox.css +28 -28
  24. package/src/css/components/selectbox.css +53 -53
  25. package/src/css/index.css +74 -74
  26. package/src/js/adapter/mixed-adapter.js +434 -434
  27. package/src/js/components/accessorybox.js +124 -124
  28. package/src/js/components/directive.js +37 -37
  29. package/src/js/components/empty-state.js +67 -67
  30. package/src/js/components/loading-state.js +59 -59
  31. package/src/js/components/option-handle.js +113 -113
  32. package/src/js/components/placeholder.js +56 -56
  33. package/src/js/components/popup.js +470 -470
  34. package/src/js/components/searchbox.js +167 -167
  35. package/src/js/components/selectbox.js +749 -692
  36. package/src/js/core/base/adapter.js +162 -162
  37. package/src/js/core/base/model.js +59 -59
  38. package/src/js/core/base/recyclerview.js +82 -82
  39. package/src/js/core/base/view.js +62 -62
  40. package/src/js/core/model-manager.js +286 -286
  41. package/src/js/core/search-controller.js +603 -521
  42. package/src/js/index.js +136 -136
  43. package/src/js/models/group-model.js +142 -142
  44. package/src/js/models/option-model.js +236 -236
  45. package/src/js/services/dataset-observer.js +73 -73
  46. package/src/js/services/ea-observer.js +87 -87
  47. package/src/js/services/effector.js +403 -403
  48. package/src/js/services/refresher.js +39 -39
  49. package/src/js/services/resize-observer.js +151 -151
  50. package/src/js/services/select-observer.js +60 -60
  51. package/src/js/types/adapter.type.js +32 -32
  52. package/src/js/types/effector.type.js +23 -23
  53. package/src/js/types/ievents.type.js +10 -10
  54. package/src/js/types/libs.type.js +27 -27
  55. package/src/js/types/model.type.js +11 -11
  56. package/src/js/types/recyclerview.type.js +11 -11
  57. package/src/js/types/resize-observer.type.js +18 -18
  58. package/src/js/types/view.group.type.js +12 -12
  59. package/src/js/types/view.option.type.js +14 -14
  60. package/src/js/types/view.type.js +10 -10
  61. package/src/js/utils/guard.js +46 -46
  62. package/src/js/utils/ievents.js +83 -83
  63. package/src/js/utils/istorage.js +60 -60
  64. package/src/js/utils/libs.js +618 -618
  65. package/src/js/utils/selective.js +385 -385
  66. package/src/js/views/group-view.js +102 -102
  67. package/src/js/views/option-view.js +152 -152
@@ -1,404 +1,404 @@
1
- /**
2
- * @returns {EffectorInterface}
3
- */
4
- export function Effector(query) {
5
- return new class {
6
- /**
7
- * @type {HTMLElement}
8
- */
9
- element;
10
- #timeOut = null;
11
- #resizeTimeout = null;
12
- #isAnimating = false;
13
-
14
- /**
15
- * Provides an effector utility that controls animations and resizing for a target element.
16
- * Supports setting the element by selector or node, canceling in-flight animations/timers,
17
- * and exposes methods (expand, collapse, resize) via the returned object instance.
18
- *
19
- * @param {string|HTMLElement} [query] - A CSS selector or the target element to control.
20
- */
21
- constructor(query = null) {
22
- if (query) {
23
- this.setElement(query);
24
- }
25
- }
26
-
27
- /**
28
- * Sets the target element to be controlled by the effector.
29
- * Accepts either a CSS selector or a direct HTMLElement reference.
30
- *
31
- * @param {string|HTMLElement} query - The element or selector to bind.
32
- */
33
- setElement(query) {
34
- if (typeof query === "string") {
35
- this.element = document.querySelector(query);
36
- } else {
37
- this.element = query;
38
- }
39
- }
40
-
41
- /**
42
- * Cancels any pending timeouts or resize triggers and resets the animation state.
43
- * Use this to stop ongoing expand/collapse/resize animations immediately.
44
- *
45
- * @returns {this} - The effector instance for chaining.
46
- */
47
- cancel() {
48
- if (this.#timeOut) {
49
- clearTimeout(this.#timeOut);
50
- this.#timeOut = null;
51
- }
52
- if (this.#resizeTimeout) {
53
- clearTimeout(this.#resizeTimeout);
54
- this.#resizeTimeout = null;
55
- }
56
- this.#isAnimating = false;
57
- return this;
58
- }
59
-
60
- /**
61
- * Get hidden dimensions
62
- * @param {string} display
63
- * @returns {{width: number, height: number, scrollHeight: number}}
64
- */
65
- getHiddenDimensions(display = "flex") {
66
- const originalStyles = {
67
- display: this.element.style.display,
68
- visibility: this.element.style.visibility,
69
- position: this.element.style.position,
70
- height: this.element.style.height,
71
- width: this.element.style.width,
72
- };
73
-
74
- Object.assign(this.element.style, {
75
- display: display,
76
- visibility: "hidden",
77
- position: "fixed",
78
- height: "fit-content",
79
- width: "fit-content"
80
- });
81
-
82
- const getComputedStyle = window.getComputedStyle(this.element);
83
- const borderTopWidth = parseFloat(getComputedStyle.borderTopWidth);
84
- const borderBottomWidth = parseFloat(getComputedStyle.borderBottomWidth);
85
-
86
- const scrollHeight = this.element.scrollHeight + borderTopWidth + borderBottomWidth;
87
-
88
- const rect = this.element.getBoundingClientRect();
89
-
90
- const dimensions = {
91
- width: rect.width,
92
- height: rect.height + borderTopWidth + borderBottomWidth,
93
- scrollHeight: scrollHeight
94
- };
95
-
96
- Object.assign(this.element.style, originalStyles);
97
-
98
- return dimensions;
99
- }
100
-
101
- /**
102
- * Expand animation (open popup)
103
- * @param {Object} config
104
- * @param {number} config.duration - Animation duration in ms
105
- * @param {string} config.display - Display type
106
- * @param {number} config.width - Target width
107
- * @param {number} config.left - Left position
108
- * @param {number} config.top - Top position
109
- * @param {number} config.maxHeight - Max height
110
- * @param {number} config.realHeight - Real height
111
- * @param {string} config.position - Position type (top/bottom)
112
- * @param {Function} config.onComplete - Callback when complete
113
- * @returns {this}
114
- */
115
- expand(config) {
116
- this.cancel();
117
- this.#isAnimating = true;
118
-
119
- const {
120
- duration = 200,
121
- display = "flex",
122
- width,
123
- left,
124
- top,
125
- maxHeight,
126
- realHeight,
127
- position = "bottom",
128
- onComplete
129
- } = config;
130
-
131
- const initialTop = position === "bottom"
132
- ? top
133
- : top + realHeight;
134
-
135
- Object.assign(this.element.style, {
136
- display: display,
137
- width: `${width}px`,
138
- left: `${left}px`,
139
- top: `${initialTop}px`,
140
- maxHeight: `${maxHeight}px`,
141
- height: "0px",
142
- opacity: "0",
143
- overflow: "hidden",
144
- transition: "none"
145
- });
146
-
147
- this.element.classList.toggle("position-top", position === "top");
148
- this.element.classList.toggle("position-bottom", position === "bottom");
149
-
150
- requestAnimationFrame(() => {
151
- const isScrollable = realHeight >= maxHeight;
152
-
153
- Object.assign(this.element.style, {
154
- transition: `top ${duration}ms, height ${duration}ms, opacity ${duration}ms`,
155
- top: `${top}px`,
156
- height: `${realHeight}px`,
157
- opacity: "1",
158
- overflow: isScrollable ? "auto" : "hidden"
159
- });
160
-
161
- this.#timeOut = setTimeout(() => {
162
- this.element.style.transition = "none";
163
- this.#isAnimating = false;
164
- onComplete && onComplete();
165
- }, duration);
166
- });
167
-
168
- return this;
169
- }
170
-
171
- /**
172
- * Collapse animation (close popup)
173
- * @param {Object} config
174
- * @param {number} config.duration - Animation duration in ms
175
- * @param {Function} config.onComplete - Callback when complete
176
- * @returns {this}
177
- */
178
- collapse(config) {
179
- this.cancel();
180
- this.#isAnimating = true;
181
-
182
- const {
183
- duration = 200,
184
- onComplete
185
- } = config;
186
-
187
- const currentHeight = this.element.offsetHeight;
188
- const currentTop = this.element.offsetTop;
189
- const position = this.element.classList.contains("position-top") ? "top" : "bottom";
190
- const isScrollable = (this.element.scrollHeight - this.element.offsetHeight) > 0;
191
-
192
- const finalTop = position === "top"
193
- ? currentTop + currentHeight
194
- : currentTop;
195
-
196
- requestAnimationFrame(() => {
197
- Object.assign(this.element.style, {
198
- transition: `height ${duration}ms, top ${duration}ms, opacity ${duration}ms`,
199
- height: "0px",
200
- top: `${finalTop}px`,
201
- opacity: "0",
202
- overflow: isScrollable ? "auto" : "hidden"
203
- });
204
-
205
- this.#timeOut = setTimeout(() => {
206
- Object.assign(this.element.style, {
207
- display: "none",
208
- transition: "none"
209
- });
210
- this.#isAnimating = false;
211
- onComplete && onComplete();
212
- }, duration);
213
- });
214
-
215
- return this;
216
- }
217
-
218
- /**
219
- * show Swipe animation (close element)
220
- * @param {Object} config
221
- * @param {number} config.duration - Animation duration in ms
222
- * @param {String} config.display - Display for element
223
- * @param {Function} config.onComplete - Callback when complete
224
- * @returns {this}
225
- */
226
- showSwipeWidth(config) {
227
- this.cancel();
228
- this.#isAnimating = true;
229
-
230
- const {
231
- duration = 200,
232
- display = "block",
233
- onComplete
234
- } = config;
235
-
236
- Object.assign(this.element.style, {
237
- transition: "none",
238
- display: display,
239
- width: "fit-content"
240
- });
241
-
242
- const maxWidth = this.getHiddenDimensions(display).width;
243
-
244
- Object.assign(this.element.style, {
245
- width: "0px"
246
- });
247
-
248
- requestAnimationFrame(() => {
249
- Object.assign(this.element.style, {
250
- transition: `width ${duration}ms`,
251
- width: `${maxWidth}px`,
252
- overflow: "hidden"
253
- });
254
- });
255
-
256
- this.#timeOut = setTimeout(() => {
257
- Object.assign(this.element.style, {
258
- width: null,
259
- overflow: null,
260
- transition: null
261
- });
262
- this.#isAnimating = false;
263
- onComplete && onComplete();
264
- }, duration);
265
-
266
- return this;
267
- }
268
-
269
- /**
270
- * hide Swipe animation (close element)
271
- * @param {Object} config
272
- * @param {number} config.duration - Animation duration in ms
273
- * @param {Function} config.onComplete - Callback when complete
274
- * @returns {this}
275
- */
276
- hideSwipeWidth(config) {
277
- this.cancel();
278
- this.#isAnimating = true;
279
-
280
- const {
281
- duration = 200,
282
- onComplete
283
- } = config;
284
-
285
- const maxWidth = this.getHiddenDimensions().width;
286
-
287
- Object.assign(this.element.style, {
288
- transition: "none",
289
- width: `${maxWidth}px`
290
- });
291
-
292
- requestAnimationFrame(() => {
293
- Object.assign(this.element.style, {
294
- transition: `width ${duration}ms`,
295
- width: `0px`,
296
- overflow: "hidden"
297
- });
298
- });
299
-
300
- this.#timeOut = setTimeout(() => {
301
- Object.assign(this.element.style, {
302
- width: null,
303
- overflow: null,
304
- transition: null,
305
- display: null
306
- });
307
- this.#isAnimating = false;
308
- onComplete && onComplete();
309
- }, duration);
310
-
311
- return this;
312
- }
313
-
314
- /**
315
- * Resize animation (when content changes)
316
- * @param {Object} config
317
- * @param {number} config.duration - Animation duration in ms
318
- * @param {number} config.width - Target width
319
- * @param {number} config.left - Left position
320
- * @param {number} config.top - Top position
321
- * @param {number} config.maxHeight - Max height
322
- * @param {number} config.realHeight - Real height
323
- * @param {string} config.position - Position type (top/bottom)
324
- * @param {boolean} config.animate - Whether to animate
325
- * @param {Function} config.onComplete - Callback when complete
326
- * @returns {this}
327
- */
328
- resize(config) {
329
- if (this.#resizeTimeout) {
330
- clearTimeout(this.#resizeTimeout);
331
- }
332
-
333
- const {
334
- duration = 200,
335
- width,
336
- left,
337
- top,
338
- maxHeight,
339
- realHeight,
340
- position = "bottom",
341
- animate = true,
342
- onComplete
343
- } = config;
344
-
345
- const currentPosition = this.element.classList.contains("position-top") ? "top" : "bottom";
346
- const isPositionChanged = currentPosition !== position;
347
- const isScrollable = this.element.scrollHeight > maxHeight;
348
-
349
- this.element.classList.toggle("position-top", position === "top");
350
- this.element.classList.toggle("position-bottom", position === "bottom");
351
-
352
- if (isPositionChanged) {
353
- this.element.style.transition = `top ${duration}ms ease-out, height ${duration}ms ease-out, max-height ${duration}ms ease-out;`;
354
- }
355
-
356
- requestAnimationFrame(() => {
357
- const curTop = this.element.offsetTop;
358
- const styles = {
359
- width: `${width}px`,
360
- left: `${left}px`,
361
- top: `${top}px`,
362
- maxHeight: `${maxHeight}px`,
363
- height: `${realHeight}px`,
364
- overflowY: isScrollable ? "auto" : "hidden"
365
- };
366
-
367
- if (animate && (isPositionChanged || Math.abs(this.element.offsetHeight - realHeight) > 5) ) {
368
- styles.transition = `height ${duration}ms, top ${duration}ms`;
369
- } else {
370
- this.#resizeTimeout = setTimeout(() => {
371
- this.element.style.transition = "none";
372
- }, duration);
373
- }
374
-
375
- Object.assign(this.element.style, styles);
376
-
377
- if (animate && (isPositionChanged || Math.abs(this.element.offsetHeight - realHeight) > 1)) {
378
- this.#resizeTimeout = setTimeout(() => {
379
- this.element.style.transition = "none";
380
- if (isPositionChanged) {
381
- delete this.element.style.transition;
382
- }
383
- onComplete && onComplete();
384
- }, duration);
385
- } else {
386
- if (isPositionChanged) {
387
- delete this.element.style.transition;
388
- }
389
- onComplete && onComplete();
390
- }
391
- });
392
-
393
- return this;
394
- }
395
-
396
- /**
397
- * Check if currently animating
398
- * @returns {boolean}
399
- */
400
- get isAnimating() {
401
- return this.#isAnimating;
402
- }
403
- }(query);
1
+ /**
2
+ * @returns {EffectorInterface}
3
+ */
4
+ export function Effector(query) {
5
+ return new class {
6
+ /**
7
+ * @type {HTMLElement}
8
+ */
9
+ element;
10
+ #timeOut = null;
11
+ #resizeTimeout = null;
12
+ #isAnimating = false;
13
+
14
+ /**
15
+ * Provides an effector utility that controls animations and resizing for a target element.
16
+ * Supports setting the element by selector or node, canceling in-flight animations/timers,
17
+ * and exposes methods (expand, collapse, resize) via the returned object instance.
18
+ *
19
+ * @param {string|HTMLElement} [query] - A CSS selector or the target element to control.
20
+ */
21
+ constructor(query = null) {
22
+ if (query) {
23
+ this.setElement(query);
24
+ }
25
+ }
26
+
27
+ /**
28
+ * Sets the target element to be controlled by the effector.
29
+ * Accepts either a CSS selector or a direct HTMLElement reference.
30
+ *
31
+ * @param {string|HTMLElement} query - The element or selector to bind.
32
+ */
33
+ setElement(query) {
34
+ if (typeof query === "string") {
35
+ this.element = document.querySelector(query);
36
+ } else {
37
+ this.element = query;
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Cancels any pending timeouts or resize triggers and resets the animation state.
43
+ * Use this to stop ongoing expand/collapse/resize animations immediately.
44
+ *
45
+ * @returns {this} - The effector instance for chaining.
46
+ */
47
+ cancel() {
48
+ if (this.#timeOut) {
49
+ clearTimeout(this.#timeOut);
50
+ this.#timeOut = null;
51
+ }
52
+ if (this.#resizeTimeout) {
53
+ clearTimeout(this.#resizeTimeout);
54
+ this.#resizeTimeout = null;
55
+ }
56
+ this.#isAnimating = false;
57
+ return this;
58
+ }
59
+
60
+ /**
61
+ * Get hidden dimensions
62
+ * @param {string} display
63
+ * @returns {{width: number, height: number, scrollHeight: number}}
64
+ */
65
+ getHiddenDimensions(display = "flex") {
66
+ const originalStyles = {
67
+ display: this.element.style.display,
68
+ visibility: this.element.style.visibility,
69
+ position: this.element.style.position,
70
+ height: this.element.style.height,
71
+ width: this.element.style.width,
72
+ };
73
+
74
+ Object.assign(this.element.style, {
75
+ display: display,
76
+ visibility: "hidden",
77
+ position: "fixed",
78
+ height: "fit-content",
79
+ width: "fit-content"
80
+ });
81
+
82
+ const getComputedStyle = window.getComputedStyle(this.element);
83
+ const borderTopWidth = parseFloat(getComputedStyle.borderTopWidth);
84
+ const borderBottomWidth = parseFloat(getComputedStyle.borderBottomWidth);
85
+
86
+ const scrollHeight = this.element.scrollHeight + borderTopWidth + borderBottomWidth;
87
+
88
+ const rect = this.element.getBoundingClientRect();
89
+
90
+ const dimensions = {
91
+ width: rect.width,
92
+ height: rect.height + borderTopWidth + borderBottomWidth,
93
+ scrollHeight: scrollHeight
94
+ };
95
+
96
+ Object.assign(this.element.style, originalStyles);
97
+
98
+ return dimensions;
99
+ }
100
+
101
+ /**
102
+ * Expand animation (open popup)
103
+ * @param {Object} config
104
+ * @param {number} config.duration - Animation duration in ms
105
+ * @param {string} config.display - Display type
106
+ * @param {number} config.width - Target width
107
+ * @param {number} config.left - Left position
108
+ * @param {number} config.top - Top position
109
+ * @param {number} config.maxHeight - Max height
110
+ * @param {number} config.realHeight - Real height
111
+ * @param {string} config.position - Position type (top/bottom)
112
+ * @param {Function} config.onComplete - Callback when complete
113
+ * @returns {this}
114
+ */
115
+ expand(config) {
116
+ this.cancel();
117
+ this.#isAnimating = true;
118
+
119
+ const {
120
+ duration = 200,
121
+ display = "flex",
122
+ width,
123
+ left,
124
+ top,
125
+ maxHeight,
126
+ realHeight,
127
+ position = "bottom",
128
+ onComplete
129
+ } = config;
130
+
131
+ const initialTop = position === "bottom"
132
+ ? top
133
+ : top + realHeight;
134
+
135
+ Object.assign(this.element.style, {
136
+ display: display,
137
+ width: `${width}px`,
138
+ left: `${left}px`,
139
+ top: `${initialTop}px`,
140
+ maxHeight: `${maxHeight}px`,
141
+ height: "0px",
142
+ opacity: "0",
143
+ overflow: "hidden",
144
+ transition: "none"
145
+ });
146
+
147
+ this.element.classList.toggle("position-top", position === "top");
148
+ this.element.classList.toggle("position-bottom", position === "bottom");
149
+
150
+ requestAnimationFrame(() => {
151
+ const isScrollable = realHeight >= maxHeight;
152
+
153
+ Object.assign(this.element.style, {
154
+ transition: `top ${duration}ms, height ${duration}ms, opacity ${duration}ms`,
155
+ top: `${top}px`,
156
+ height: `${realHeight}px`,
157
+ opacity: "1",
158
+ overflow: isScrollable ? "auto" : "hidden"
159
+ });
160
+
161
+ this.#timeOut = setTimeout(() => {
162
+ this.element.style.transition = "none";
163
+ this.#isAnimating = false;
164
+ onComplete && onComplete();
165
+ }, duration);
166
+ });
167
+
168
+ return this;
169
+ }
170
+
171
+ /**
172
+ * Collapse animation (close popup)
173
+ * @param {Object} config
174
+ * @param {number} config.duration - Animation duration in ms
175
+ * @param {Function} config.onComplete - Callback when complete
176
+ * @returns {this}
177
+ */
178
+ collapse(config) {
179
+ this.cancel();
180
+ this.#isAnimating = true;
181
+
182
+ const {
183
+ duration = 200,
184
+ onComplete
185
+ } = config;
186
+
187
+ const currentHeight = this.element.offsetHeight;
188
+ const currentTop = this.element.offsetTop;
189
+ const position = this.element.classList.contains("position-top") ? "top" : "bottom";
190
+ const isScrollable = (this.element.scrollHeight - this.element.offsetHeight) > 0;
191
+
192
+ const finalTop = position === "top"
193
+ ? currentTop + currentHeight
194
+ : currentTop;
195
+
196
+ requestAnimationFrame(() => {
197
+ Object.assign(this.element.style, {
198
+ transition: `height ${duration}ms, top ${duration}ms, opacity ${duration}ms`,
199
+ height: "0px",
200
+ top: `${finalTop}px`,
201
+ opacity: "0",
202
+ overflow: isScrollable ? "auto" : "hidden"
203
+ });
204
+
205
+ this.#timeOut = setTimeout(() => {
206
+ Object.assign(this.element.style, {
207
+ display: "none",
208
+ transition: "none"
209
+ });
210
+ this.#isAnimating = false;
211
+ onComplete && onComplete();
212
+ }, duration);
213
+ });
214
+
215
+ return this;
216
+ }
217
+
218
+ /**
219
+ * show Swipe animation (close element)
220
+ * @param {Object} config
221
+ * @param {number} config.duration - Animation duration in ms
222
+ * @param {String} config.display - Display for element
223
+ * @param {Function} config.onComplete - Callback when complete
224
+ * @returns {this}
225
+ */
226
+ showSwipeWidth(config) {
227
+ this.cancel();
228
+ this.#isAnimating = true;
229
+
230
+ const {
231
+ duration = 200,
232
+ display = "block",
233
+ onComplete
234
+ } = config;
235
+
236
+ Object.assign(this.element.style, {
237
+ transition: "none",
238
+ display: display,
239
+ width: "fit-content"
240
+ });
241
+
242
+ const maxWidth = this.getHiddenDimensions(display).width;
243
+
244
+ Object.assign(this.element.style, {
245
+ width: "0px"
246
+ });
247
+
248
+ requestAnimationFrame(() => {
249
+ Object.assign(this.element.style, {
250
+ transition: `width ${duration}ms`,
251
+ width: `${maxWidth}px`,
252
+ overflow: "hidden"
253
+ });
254
+ });
255
+
256
+ this.#timeOut = setTimeout(() => {
257
+ Object.assign(this.element.style, {
258
+ width: null,
259
+ overflow: null,
260
+ transition: null
261
+ });
262
+ this.#isAnimating = false;
263
+ onComplete && onComplete();
264
+ }, duration);
265
+
266
+ return this;
267
+ }
268
+
269
+ /**
270
+ * hide Swipe animation (close element)
271
+ * @param {Object} config
272
+ * @param {number} config.duration - Animation duration in ms
273
+ * @param {Function} config.onComplete - Callback when complete
274
+ * @returns {this}
275
+ */
276
+ hideSwipeWidth(config) {
277
+ this.cancel();
278
+ this.#isAnimating = true;
279
+
280
+ const {
281
+ duration = 200,
282
+ onComplete
283
+ } = config;
284
+
285
+ const maxWidth = this.getHiddenDimensions().width;
286
+
287
+ Object.assign(this.element.style, {
288
+ transition: "none",
289
+ width: `${maxWidth}px`
290
+ });
291
+
292
+ requestAnimationFrame(() => {
293
+ Object.assign(this.element.style, {
294
+ transition: `width ${duration}ms`,
295
+ width: `0px`,
296
+ overflow: "hidden"
297
+ });
298
+ });
299
+
300
+ this.#timeOut = setTimeout(() => {
301
+ Object.assign(this.element.style, {
302
+ width: null,
303
+ overflow: null,
304
+ transition: null,
305
+ display: null
306
+ });
307
+ this.#isAnimating = false;
308
+ onComplete && onComplete();
309
+ }, duration);
310
+
311
+ return this;
312
+ }
313
+
314
+ /**
315
+ * Resize animation (when content changes)
316
+ * @param {Object} config
317
+ * @param {number} config.duration - Animation duration in ms
318
+ * @param {number} config.width - Target width
319
+ * @param {number} config.left - Left position
320
+ * @param {number} config.top - Top position
321
+ * @param {number} config.maxHeight - Max height
322
+ * @param {number} config.realHeight - Real height
323
+ * @param {string} config.position - Position type (top/bottom)
324
+ * @param {boolean} config.animate - Whether to animate
325
+ * @param {Function} config.onComplete - Callback when complete
326
+ * @returns {this}
327
+ */
328
+ resize(config) {
329
+ if (this.#resizeTimeout) {
330
+ clearTimeout(this.#resizeTimeout);
331
+ }
332
+
333
+ const {
334
+ duration = 200,
335
+ width,
336
+ left,
337
+ top,
338
+ maxHeight,
339
+ realHeight,
340
+ position = "bottom",
341
+ animate = true,
342
+ onComplete
343
+ } = config;
344
+
345
+ const currentPosition = this.element.classList.contains("position-top") ? "top" : "bottom";
346
+ const isPositionChanged = currentPosition !== position;
347
+ const isScrollable = this.element.scrollHeight > maxHeight;
348
+
349
+ this.element.classList.toggle("position-top", position === "top");
350
+ this.element.classList.toggle("position-bottom", position === "bottom");
351
+
352
+ if (isPositionChanged) {
353
+ this.element.style.transition = `top ${duration}ms ease-out, height ${duration}ms ease-out, max-height ${duration}ms ease-out;`;
354
+ }
355
+
356
+ requestAnimationFrame(() => {
357
+ const curTop = this.element.offsetTop;
358
+ const styles = {
359
+ width: `${width}px`,
360
+ left: `${left}px`,
361
+ top: `${top}px`,
362
+ maxHeight: `${maxHeight}px`,
363
+ height: `${realHeight}px`,
364
+ overflowY: isScrollable ? "auto" : "hidden"
365
+ };
366
+
367
+ if (animate && (isPositionChanged || Math.abs(this.element.offsetHeight - realHeight) > 5) ) {
368
+ styles.transition = `height ${duration}ms, top ${duration}ms`;
369
+ } else {
370
+ this.#resizeTimeout = setTimeout(() => {
371
+ this.element.style.transition = "none";
372
+ }, duration);
373
+ }
374
+
375
+ Object.assign(this.element.style, styles);
376
+
377
+ if (animate && (isPositionChanged || Math.abs(this.element.offsetHeight - realHeight) > 1)) {
378
+ this.#resizeTimeout = setTimeout(() => {
379
+ this.element.style.transition = "none";
380
+ if (isPositionChanged) {
381
+ delete this.element.style.transition;
382
+ }
383
+ onComplete && onComplete();
384
+ }, duration);
385
+ } else {
386
+ if (isPositionChanged) {
387
+ delete this.element.style.transition;
388
+ }
389
+ onComplete && onComplete();
390
+ }
391
+ });
392
+
393
+ return this;
394
+ }
395
+
396
+ /**
397
+ * Check if currently animating
398
+ * @returns {boolean}
399
+ */
400
+ get isAnimating() {
401
+ return this.#isAnimating;
402
+ }
403
+ }(query);
404
404
  }