@class-kit/core 0.1.0

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 (140) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/LICENSE +3 -0
  3. package/README.md +26 -0
  4. package/dist/business/ComicReaderController.d.ts +37 -0
  5. package/dist/business/ComicReaderController.d.ts.map +1 -0
  6. package/dist/business/DanmakuController.d.ts +45 -0
  7. package/dist/business/DanmakuController.d.ts.map +1 -0
  8. package/dist/business/LiveRoomController.d.ts +37 -0
  9. package/dist/business/LiveRoomController.d.ts.map +1 -0
  10. package/dist/business/NovelReaderController.d.ts +63 -0
  11. package/dist/business/NovelReaderController.d.ts.map +1 -0
  12. package/dist/config.d.ts +89 -0
  13. package/dist/config.d.ts.map +1 -0
  14. package/dist/data/TableController.d.ts +54 -0
  15. package/dist/data/TableController.d.ts.map +1 -0
  16. package/dist/data/UploadController.d.ts +27 -0
  17. package/dist/data/UploadController.d.ts.map +1 -0
  18. package/dist/display/BadgeController.d.ts +20 -0
  19. package/dist/display/BadgeController.d.ts.map +1 -0
  20. package/dist/display/CalendarController.d.ts +22 -0
  21. package/dist/display/CalendarController.d.ts.map +1 -0
  22. package/dist/display/CanvasEditorController.d.ts +87 -0
  23. package/dist/display/CanvasEditorController.d.ts.map +1 -0
  24. package/dist/display/CanvasImageController.d.ts +67 -0
  25. package/dist/display/CanvasImageController.d.ts.map +1 -0
  26. package/dist/display/CountdownController.d.ts +39 -0
  27. package/dist/display/CountdownController.d.ts.map +1 -0
  28. package/dist/display/DesignEffectController.d.ts +9 -0
  29. package/dist/display/DesignEffectController.d.ts.map +1 -0
  30. package/dist/display/FilePreviewController.d.ts +30 -0
  31. package/dist/display/FilePreviewController.d.ts.map +1 -0
  32. package/dist/display/GradientTextController.d.ts +8 -0
  33. package/dist/display/GradientTextController.d.ts.map +1 -0
  34. package/dist/display/HoneycombController.d.ts +18 -0
  35. package/dist/display/HoneycombController.d.ts.map +1 -0
  36. package/dist/display/ImagePreviewController.d.ts +17 -0
  37. package/dist/display/ImagePreviewController.d.ts.map +1 -0
  38. package/dist/display/LazyImageController.d.ts +11 -0
  39. package/dist/display/LazyImageController.d.ts.map +1 -0
  40. package/dist/display/MarqueeController.d.ts +17 -0
  41. package/dist/display/MarqueeController.d.ts.map +1 -0
  42. package/dist/display/ProgressController.d.ts +33 -0
  43. package/dist/display/ProgressController.d.ts.map +1 -0
  44. package/dist/display/RollingNumberController.d.ts +11 -0
  45. package/dist/display/RollingNumberController.d.ts.map +1 -0
  46. package/dist/display/SignatureController.d.ts +49 -0
  47. package/dist/display/SignatureController.d.ts.map +1 -0
  48. package/dist/display/SkeletonController.d.ts +16 -0
  49. package/dist/display/SkeletonController.d.ts.map +1 -0
  50. package/dist/display/SwiperController.d.ts +25 -0
  51. package/dist/display/SwiperController.d.ts.map +1 -0
  52. package/dist/display/TagController.d.ts +18 -0
  53. package/dist/display/TagController.d.ts.map +1 -0
  54. package/dist/display/TiltCardController.d.ts +10 -0
  55. package/dist/display/TiltCardController.d.ts.map +1 -0
  56. package/dist/display/TimelineController.d.ts +13 -0
  57. package/dist/display/TimelineController.d.ts.map +1 -0
  58. package/dist/display/TypewriterTextController.d.ts +17 -0
  59. package/dist/display/TypewriterTextController.d.ts.map +1 -0
  60. package/dist/display/VideoDetailTransitionController.d.ts +45 -0
  61. package/dist/display/VideoDetailTransitionController.d.ts.map +1 -0
  62. package/dist/display/VideoPlayerController.d.ts +18 -0
  63. package/dist/display/VideoPlayerController.d.ts.map +1 -0
  64. package/dist/display/types.d.ts +217 -0
  65. package/dist/display/types.d.ts.map +1 -0
  66. package/dist/feedback/AffixController.d.ts +11 -0
  67. package/dist/feedback/AffixController.d.ts.map +1 -0
  68. package/dist/feedback/BacktopController.d.ts +12 -0
  69. package/dist/feedback/BacktopController.d.ts.map +1 -0
  70. package/dist/feedback/FloatingBallController.d.ts +41 -0
  71. package/dist/feedback/FloatingBallController.d.ts.map +1 -0
  72. package/dist/feedback/ModalController.d.ts +13 -0
  73. package/dist/feedback/ModalController.d.ts.map +1 -0
  74. package/dist/feedback/PopupController.d.ts +24 -0
  75. package/dist/feedback/PopupController.d.ts.map +1 -0
  76. package/dist/feedback/ToastManager.d.ts +15 -0
  77. package/dist/feedback/ToastManager.d.ts.map +1 -0
  78. package/dist/feedback/TooltipController.d.ts +19 -0
  79. package/dist/feedback/TooltipController.d.ts.map +1 -0
  80. package/dist/feedback/placement.d.ts +5 -0
  81. package/dist/feedback/placement.d.ts.map +1 -0
  82. package/dist/feedback/types.d.ts +24 -0
  83. package/dist/feedback/types.d.ts.map +1 -0
  84. package/dist/form/ColorPickerController.d.ts +25 -0
  85. package/dist/form/ColorPickerController.d.ts.map +1 -0
  86. package/dist/form/DatePickerController.d.ts +39 -0
  87. package/dist/form/DatePickerController.d.ts.map +1 -0
  88. package/dist/form/DateRangePickerController.d.ts +56 -0
  89. package/dist/form/DateRangePickerController.d.ts.map +1 -0
  90. package/dist/form/FieldController.d.ts +16 -0
  91. package/dist/form/FieldController.d.ts.map +1 -0
  92. package/dist/form/FormController.d.ts +27 -0
  93. package/dist/form/FormController.d.ts.map +1 -0
  94. package/dist/form/MultiColumnPickerController.d.ts +37 -0
  95. package/dist/form/MultiColumnPickerController.d.ts.map +1 -0
  96. package/dist/form/PasswordInputController.d.ts +15 -0
  97. package/dist/form/PasswordInputController.d.ts.map +1 -0
  98. package/dist/form/SlideCaptchaController.d.ts +60 -0
  99. package/dist/form/SlideCaptchaController.d.ts.map +1 -0
  100. package/dist/form/VerificationCodeController.d.ts +29 -0
  101. package/dist/form/VerificationCodeController.d.ts.map +1 -0
  102. package/dist/form/VirtualSelectController.d.ts +34 -0
  103. package/dist/form/VirtualSelectController.d.ts.map +1 -0
  104. package/dist/form/emitter.d.ts +8 -0
  105. package/dist/form/emitter.d.ts.map +1 -0
  106. package/dist/form/index.d.ts +6 -0
  107. package/dist/form/index.d.ts.map +1 -0
  108. package/dist/form/path.d.ts +6 -0
  109. package/dist/form/path.d.ts.map +1 -0
  110. package/dist/form/types.d.ts +60 -0
  111. package/dist/form/types.d.ts.map +1 -0
  112. package/dist/form/validation.d.ts +3 -0
  113. package/dist/form/validation.d.ts.map +1 -0
  114. package/dist/index.cjs +4688 -0
  115. package/dist/index.cjs.map +1 -0
  116. package/dist/index.d.ts +104 -0
  117. package/dist/index.d.ts.map +1 -0
  118. package/dist/index.js +4624 -0
  119. package/dist/index.js.map +1 -0
  120. package/dist/interaction/DragController.d.ts +47 -0
  121. package/dist/interaction/DragController.d.ts.map +1 -0
  122. package/dist/interaction/DropController.d.ts +22 -0
  123. package/dist/interaction/DropController.d.ts.map +1 -0
  124. package/dist/interaction/GestureController.d.ts +34 -0
  125. package/dist/interaction/GestureController.d.ts.map +1 -0
  126. package/dist/navigation/BreadcrumbController.d.ts +17 -0
  127. package/dist/navigation/BreadcrumbController.d.ts.map +1 -0
  128. package/dist/navigation/PaginationController.d.ts +27 -0
  129. package/dist/navigation/PaginationController.d.ts.map +1 -0
  130. package/dist/navigation/TabsController.d.ts +13 -0
  131. package/dist/navigation/TabsController.d.ts.map +1 -0
  132. package/dist/virtualization/ChatVirtualListController.d.ts +17 -0
  133. package/dist/virtualization/ChatVirtualListController.d.ts.map +1 -0
  134. package/dist/virtualization/MasonryVirtualListController.d.ts +32 -0
  135. package/dist/virtualization/MasonryVirtualListController.d.ts.map +1 -0
  136. package/dist/virtualization/VirtualListController.d.ts +29 -0
  137. package/dist/virtualization/VirtualListController.d.ts.map +1 -0
  138. package/dist/virtualization/types.d.ts +67 -0
  139. package/dist/virtualization/types.d.ts.map +1 -0
  140. package/package.json +41 -0
package/dist/index.cjs ADDED
@@ -0,0 +1,4688 @@
1
+ 'use strict';
2
+
3
+ function isEmpty(value) {
4
+ return (value == null ||
5
+ value === "" ||
6
+ (Array.isArray(value) && value.length === 0));
7
+ }
8
+ async function runCustomValidator(validate, context) {
9
+ return validate(context);
10
+ }
11
+ async function validateValue(context, rules) {
12
+ const errors = [];
13
+ for (const rule of rules) {
14
+ if (rule.required && isEmpty(context.value)) {
15
+ errors.push(rule.message ?? "This field is required.");
16
+ continue;
17
+ }
18
+ if (rule.pattern && !isEmpty(context.value) && !rule.pattern.test(String(context.value))) {
19
+ errors.push(rule.message ?? "The value does not match the required format.");
20
+ continue;
21
+ }
22
+ if (rule.minLength != null &&
23
+ !isEmpty(context.value) &&
24
+ String(context.value).length < rule.minLength) {
25
+ errors.push(rule.message ?? `Please enter at least ${rule.minLength} characters.`);
26
+ continue;
27
+ }
28
+ if (rule.maxLength != null &&
29
+ !isEmpty(context.value) &&
30
+ String(context.value).length > rule.maxLength) {
31
+ errors.push(rule.message ?? `Please enter no more than ${rule.maxLength} characters.`);
32
+ continue;
33
+ }
34
+ if (rule.validate) {
35
+ const customError = await runCustomValidator(rule.validate, context);
36
+ if (customError)
37
+ errors.push(customError);
38
+ }
39
+ }
40
+ return errors;
41
+ }
42
+
43
+ class FieldController {
44
+ constructor(name, options = {}) {
45
+ this.name = name;
46
+ this.rules = options.rules ?? [];
47
+ this.validateOn = options.validateOn ?? ["change", "blur", "submit"];
48
+ this.state = {
49
+ name,
50
+ value: options.initialValue,
51
+ initialValue: options.initialValue,
52
+ errors: [],
53
+ meta: {
54
+ dirty: false,
55
+ touched: false,
56
+ validating: false,
57
+ submitted: false
58
+ }
59
+ };
60
+ }
61
+ getState() {
62
+ return {
63
+ ...this.state,
64
+ errors: [...this.state.errors],
65
+ meta: { ...this.state.meta }
66
+ };
67
+ }
68
+ setValue(value) {
69
+ this.state = {
70
+ ...this.state,
71
+ value,
72
+ meta: {
73
+ ...this.state.meta,
74
+ dirty: !Object.is(value, this.state.initialValue)
75
+ }
76
+ };
77
+ }
78
+ markTouched() {
79
+ this.state = {
80
+ ...this.state,
81
+ meta: { ...this.state.meta, touched: true }
82
+ };
83
+ }
84
+ markSubmitted() {
85
+ this.state = {
86
+ ...this.state,
87
+ meta: { ...this.state.meta, submitted: true }
88
+ };
89
+ }
90
+ reset(value = this.state.initialValue) {
91
+ this.state = {
92
+ name: this.name,
93
+ value,
94
+ initialValue: value,
95
+ errors: [],
96
+ meta: {
97
+ dirty: false,
98
+ touched: false,
99
+ validating: false,
100
+ submitted: false
101
+ }
102
+ };
103
+ }
104
+ shouldValidate(trigger) {
105
+ return this.validateOn.includes(trigger);
106
+ }
107
+ async validate(values) {
108
+ this.state = {
109
+ ...this.state,
110
+ meta: { ...this.state.meta, validating: true }
111
+ };
112
+ const errors = await validateValue({ field: this.name, value: this.state.value, values }, this.rules);
113
+ this.state = {
114
+ ...this.state,
115
+ errors,
116
+ meta: { ...this.state.meta, validating: false }
117
+ };
118
+ return errors;
119
+ }
120
+ }
121
+
122
+ class StateEmitter {
123
+ constructor() {
124
+ this.listeners = new Set();
125
+ }
126
+ subscribe(listener) {
127
+ this.listeners.add(listener);
128
+ return () => {
129
+ this.listeners.delete(listener);
130
+ };
131
+ }
132
+ emit(state) {
133
+ this.listeners.forEach((listener) => listener(state));
134
+ }
135
+ clear() {
136
+ this.listeners.clear();
137
+ }
138
+ }
139
+
140
+ const pathPattern = /[^.[\]]+/g;
141
+ function toPath(path) {
142
+ return path.match(pathPattern) ?? [];
143
+ }
144
+ function getByPath(source, path) {
145
+ return toPath(path).reduce((current, key) => {
146
+ if (current == null || typeof current !== "object")
147
+ return undefined;
148
+ return current[key];
149
+ }, source);
150
+ }
151
+ function setByPath(source, path, value) {
152
+ const keys = toPath(path);
153
+ if (keys.length === 0)
154
+ return source;
155
+ const next = { ...source };
156
+ let cursor = next;
157
+ keys.forEach((key, index) => {
158
+ if (index === keys.length - 1) {
159
+ cursor[key] = value;
160
+ return;
161
+ }
162
+ const existing = cursor[key];
163
+ const child = existing != null && typeof existing === "object"
164
+ ? { ...existing }
165
+ : {};
166
+ cursor[key] = child;
167
+ cursor = child;
168
+ });
169
+ return next;
170
+ }
171
+ function cloneValues(values) {
172
+ if (typeof structuredClone === "function") {
173
+ try {
174
+ return structuredClone(values);
175
+ }
176
+ catch {
177
+ return cloneFallback(values);
178
+ }
179
+ }
180
+ return cloneFallback(values);
181
+ }
182
+ function cloneFallback(value, seen = new WeakMap()) {
183
+ if (value == null || typeof value !== "object")
184
+ return value;
185
+ if (value instanceof Date)
186
+ return new Date(value.getTime());
187
+ if (value instanceof RegExp)
188
+ return new RegExp(value.source, value.flags);
189
+ if (typeof File !== "undefined" && value instanceof File)
190
+ return value;
191
+ if (typeof Blob !== "undefined" && value instanceof Blob)
192
+ return value;
193
+ const objectValue = value;
194
+ if (seen.has(objectValue))
195
+ return seen.get(objectValue);
196
+ if (Array.isArray(value)) {
197
+ const next = [];
198
+ seen.set(objectValue, next);
199
+ value.forEach((item) => next.push(cloneFallback(item, seen)));
200
+ return next;
201
+ }
202
+ if (value instanceof Map) {
203
+ const next = new Map();
204
+ seen.set(objectValue, next);
205
+ value.forEach((mapValue, mapKey) => next.set(cloneFallback(mapKey, seen), cloneFallback(mapValue, seen)));
206
+ return next;
207
+ }
208
+ if (value instanceof Set) {
209
+ const next = new Set();
210
+ seen.set(objectValue, next);
211
+ value.forEach((item) => next.add(cloneFallback(item, seen)));
212
+ return next;
213
+ }
214
+ const next = {};
215
+ seen.set(objectValue, next);
216
+ Object.entries(value).forEach(([key, item]) => {
217
+ next[key] = cloneFallback(item, seen);
218
+ });
219
+ return next;
220
+ }
221
+
222
+ class FormController {
223
+ constructor(options = {}) {
224
+ this.emitter = new StateEmitter();
225
+ this.fields = new Map();
226
+ this.submitting = false;
227
+ this.submitted = false;
228
+ this.submitCount = 0;
229
+ this.initialValues = cloneValues(options.initialValues ?? {});
230
+ this.values = cloneValues(options.initialValues ?? {});
231
+ this.validateOn = options.validateOn ?? ["submit"];
232
+ this.onSubmit = options.onSubmit;
233
+ this.state = this.createState();
234
+ }
235
+ registerField(name, options = {}) {
236
+ const initialValue = options.initialValue !== undefined
237
+ ? options.initialValue
238
+ : getByPath(this.values, name);
239
+ const field = new FieldController(name, {
240
+ ...options,
241
+ initialValue,
242
+ validateOn: options.validateOn ?? this.validateOn,
243
+ });
244
+ this.fields.set(name, field);
245
+ if (initialValue !== undefined) {
246
+ this.values = setByPath(this.values, name, initialValue);
247
+ }
248
+ this.notify();
249
+ return () => {
250
+ this.fields.delete(name);
251
+ this.notify();
252
+ };
253
+ }
254
+ subscribe(listener) {
255
+ return this.emitter.subscribe(listener);
256
+ }
257
+ getState() {
258
+ return this.state;
259
+ }
260
+ getValue(name) {
261
+ return getByPath(this.values, name);
262
+ }
263
+ createState() {
264
+ const fieldStates = Object.fromEntries(Array.from(this.fields.entries()).map(([name, field]) => [
265
+ name,
266
+ field.getState(),
267
+ ]));
268
+ const errors = {};
269
+ Object.entries(fieldStates).forEach(([name, field]) => {
270
+ if (field.errors.length > 0)
271
+ errors[name] = field.errors;
272
+ });
273
+ const states = Object.values(fieldStates);
274
+ return {
275
+ values: cloneValues(this.values),
276
+ errors,
277
+ fields: fieldStates,
278
+ dirty: states.some((field) => field.meta.dirty),
279
+ touched: states.some((field) => field.meta.touched),
280
+ validating: states.some((field) => field.meta.validating),
281
+ submitting: this.submitting,
282
+ submitted: this.submitted,
283
+ valid: Object.keys(errors).length === 0,
284
+ submitCount: this.submitCount,
285
+ };
286
+ }
287
+ async setValue(name, value, trigger = "change") {
288
+ this.values = setByPath(this.values, name, value);
289
+ const field = this.fields.get(name);
290
+ field?.setValue(value);
291
+ if (field?.shouldValidate(trigger)) {
292
+ await field.validate(this.values);
293
+ }
294
+ this.notify();
295
+ }
296
+ async blur(name) {
297
+ const field = this.fields.get(name);
298
+ field?.markTouched();
299
+ if (field?.shouldValidate("blur")) {
300
+ await field.validate(this.values);
301
+ }
302
+ this.notify();
303
+ }
304
+ async validate() {
305
+ const tasks = Array.from(this.fields.values()).map((field) => field.validate(this.values));
306
+ this.notify();
307
+ await Promise.all(tasks);
308
+ this.notify();
309
+ return this.getState().valid;
310
+ }
311
+ async submit() {
312
+ this.submitting = true;
313
+ this.submitted = true;
314
+ this.submitCount += 1;
315
+ this.fields.forEach((field) => field.markSubmitted());
316
+ this.notify();
317
+ const valid = await this.validate();
318
+ const state = this.getState();
319
+ try {
320
+ if (valid && this.onSubmit) {
321
+ await this.onSubmit(state.values, state);
322
+ }
323
+ }
324
+ finally {
325
+ this.submitting = false;
326
+ this.notify();
327
+ }
328
+ return this.getState();
329
+ }
330
+ reset(values = this.initialValues) {
331
+ this.values = cloneValues(values);
332
+ this.submitting = false;
333
+ this.submitted = false;
334
+ this.submitCount = 0;
335
+ this.fields.forEach((field, name) => {
336
+ const nextValue = getByPath(this.values, name);
337
+ field.reset(nextValue);
338
+ });
339
+ this.notify();
340
+ }
341
+ destroy() {
342
+ this.fields.clear();
343
+ this.emitter.clear();
344
+ }
345
+ notify() {
346
+ this.state = this.createState();
347
+ this.emitter.emit(this.state);
348
+ }
349
+ }
350
+
351
+ const builtInThemeNames = [
352
+ "minimal",
353
+ "violet",
354
+ "emerald",
355
+ "sky",
356
+ "rose",
357
+ "amber",
358
+ "liquid-glass",
359
+ ];
360
+ const defaultLocale = {
361
+ backtopLabel: "回到顶部",
362
+ calendarNextMonth: "下个月",
363
+ calendarPreviousMonth: "上个月",
364
+ emptyText: "暂无数据",
365
+ loadingText: "加载中...",
366
+ countdownDayUnit: "天",
367
+ countdownHourUnit: "时",
368
+ countdownMinuteUnit: "分",
369
+ countdownSecondUnit: "秒",
370
+ colorPickerAlphaLabel: "透明度",
371
+ comicReaderLoadMoreText: "继续下滑加载",
372
+ comicReaderLoadingText: "加载中...",
373
+ comicReaderNoMoreText: "已到底部",
374
+ comicReaderNextLabel: "下一页",
375
+ comicReaderPreviousLabel: "上一页",
376
+ datePickerNextMonth: "下个月",
377
+ datePickerPlaceholder: "请选择日期",
378
+ datePickerPreviousMonth: "上个月",
379
+ datePickerWeekLabels: ["日", "一", "二", "三", "四", "五", "六"],
380
+ dateDayUnit: "日",
381
+ dateMonthUnit: "月",
382
+ dateYearUnit: "年",
383
+ dateRangeClear: "清空",
384
+ dateRangeEndPlaceholder: "结束日期",
385
+ dateRangePlaceholder: "请选择日期范围",
386
+ dateRangeSeparator: "至",
387
+ dateRangeStartPlaceholder: "开始日期",
388
+ inputClearLabel: "清空内容",
389
+ passwordClearLabel: "清空密码",
390
+ passwordHiddenAlt: "显示密码",
391
+ passwordHiddenIcon: "显示",
392
+ passwordToggleHiddenLabel: "显示密码",
393
+ passwordToggleVisibleLabel: "隐藏密码",
394
+ passwordVisibleAlt: "隐藏密码",
395
+ passwordVisibleIcon: "隐藏",
396
+ popconfirmCancelText: "取消",
397
+ popconfirmConfirmText: "确认",
398
+ popconfirmContent: "确认执行该操作?",
399
+ selectClearLabel: "清空选择",
400
+ selectEmptyText: "暂无匹配选项",
401
+ selectPlaceholder: "Select",
402
+ selectSearchPlaceholder: "搜索选项",
403
+ selectSelectedIcon: "",
404
+ signatureClearText: "一键清除",
405
+ signatureUndoText: "回退",
406
+ tableColumnSettingsLabel: "列设置",
407
+ tableEmptyText: "暂无表格数据",
408
+ tableLoadingText: "表格加载中...",
409
+ tableSelectAllLabel: "选择全部",
410
+ tableSelectRowLabel: "选择",
411
+ tabsEmptyText: "暂无内容",
412
+ tabsLoadingText: "内容加载中...",
413
+ uploadButtonText: "选择文件",
414
+ uploadDragText: "拖拽文件到这里",
415
+ verificationInputLabel: "验证码",
416
+ virtualListPullUpLoadingText: "正在加载更多...",
417
+ virtualListPullUpReadyText: "继续上拉加载",
418
+ novelReaderEmptyText: "暂无章节内容",
419
+ novelReaderNextLabel: "下一页",
420
+ novelReaderPreviousLabel: "上一页",
421
+ novelReaderTitle: "小说阅读",
422
+ liveRoomFavoriteLabel: "收藏",
423
+ liveRoomGiftLabel: "福袋",
424
+ liveRoomInputPlaceholder: "说点什么...",
425
+ liveRoomLikeLabel: "点赞",
426
+ liveRoomMoreLabel: "更多",
427
+ liveRoomSelfName: "我",
428
+ liveRoomSendLabel: "发送",
429
+ liveRoomShareLabel: "分享",
430
+ };
431
+ function cloneLocale(locale) {
432
+ return {
433
+ ...locale,
434
+ datePickerWeekLabels: [...locale.datePickerWeekLabels],
435
+ };
436
+ }
437
+ let globalConfig = {
438
+ locale: cloneLocale(defaultLocale),
439
+ theme: { name: "minimal" },
440
+ };
441
+ function resolveTheme(theme) {
442
+ const name = typeof theme === "string" ? theme : theme?.name;
443
+ return {
444
+ name: builtInThemeNames.includes(name)
445
+ ? name
446
+ : "minimal",
447
+ };
448
+ }
449
+ function configureClassComponents(config = {}) {
450
+ const nextLocale = {
451
+ ...globalConfig.locale,
452
+ ...config.locale,
453
+ };
454
+ globalConfig = {
455
+ locale: cloneLocale(nextLocale),
456
+ theme: config.theme
457
+ ? resolveTheme(config.theme)
458
+ : { ...globalConfig.theme },
459
+ };
460
+ return getClassComponentsConfig();
461
+ }
462
+ function getClassComponentsConfig() {
463
+ return {
464
+ locale: cloneLocale(globalConfig.locale),
465
+ theme: { ...globalConfig.theme },
466
+ };
467
+ }
468
+ function getClassComponentsLocale() {
469
+ return getClassComponentsConfig().locale;
470
+ }
471
+ function resetClassComponentsConfig() {
472
+ globalConfig = {
473
+ locale: cloneLocale(defaultLocale),
474
+ theme: { name: "minimal" },
475
+ };
476
+ return getClassComponentsConfig();
477
+ }
478
+
479
+ class MultiColumnPickerController {
480
+ constructor(options) {
481
+ this.dateSource = options.source;
482
+ this.sourceColumns = options.columns ?? this.createDateColumns(options.source, options.values);
483
+ this.cascade = options.cascade ?? false;
484
+ this.values = options.values ?? [];
485
+ }
486
+ updateOptions(options) {
487
+ if (options.columns) {
488
+ this.dateSource = undefined;
489
+ this.sourceColumns = options.columns;
490
+ }
491
+ if (options.source) {
492
+ this.dateSource = options.source;
493
+ this.sourceColumns = this.createDateColumns(options.source, options.values ?? this.values);
494
+ }
495
+ if (options.cascade !== undefined)
496
+ this.cascade = options.cascade;
497
+ if (options.values)
498
+ this.values = options.values;
499
+ return this.getState();
500
+ }
501
+ select(columnIndex, value) {
502
+ this.values = [...this.values.slice(0, columnIndex), value];
503
+ return this.getState();
504
+ }
505
+ selectByDelta(columnIndex, delta) {
506
+ const state = this.getState();
507
+ const column = state.columns[columnIndex] ?? [];
508
+ if (column.length === 0)
509
+ return state;
510
+ const currentIndex = Math.max(0, column.findIndex((item) => item.value === state.values[columnIndex]));
511
+ const nextStep = Math.trunc(delta);
512
+ const direction = nextStep >= 0 ? 1 : -1;
513
+ let nextIndex = Math.min(column.length - 1, Math.max(0, currentIndex + nextStep));
514
+ while (column[nextIndex]?.disabled && nextIndex > 0 && nextIndex < column.length - 1) {
515
+ nextIndex += direction;
516
+ }
517
+ const next = column[nextIndex];
518
+ return next && !next.disabled ? this.select(columnIndex, next.value) : state;
519
+ }
520
+ getState() {
521
+ if (this.dateSource)
522
+ this.sourceColumns = this.createDateColumns(this.dateSource, this.values);
523
+ const columns = [];
524
+ const selectedOptions = [];
525
+ if (!this.cascade) {
526
+ this.sourceColumns.forEach((column, index) => {
527
+ columns[index] = column;
528
+ const selected = column.find((item) => item.value === this.values[index]) ?? column.find((item) => !item.disabled) ?? column[0];
529
+ if (selected) {
530
+ selectedOptions[index] = selected;
531
+ this.values[index] = selected.value;
532
+ }
533
+ });
534
+ return { columns, selectedOptions, values: selectedOptions.map((item) => item.value) };
535
+ }
536
+ let currentColumn = this.sourceColumns[0] ?? [];
537
+ let index = 0;
538
+ while (currentColumn.length > 0) {
539
+ columns[index] = currentColumn;
540
+ const selected = currentColumn.find((item) => item.value === this.values[index]) ?? currentColumn.find((item) => !item.disabled) ?? currentColumn[0];
541
+ if (!selected)
542
+ break;
543
+ selectedOptions[index] = selected;
544
+ this.values[index] = selected.value;
545
+ currentColumn = selected.children ?? [];
546
+ index += 1;
547
+ }
548
+ return { columns, selectedOptions, values: selectedOptions.map((item) => item.value) };
549
+ }
550
+ createDateColumns(source, values = []) {
551
+ if (!source)
552
+ return [];
553
+ const currentYear = new Date().getFullYear();
554
+ const startYear = source.startYear ?? (source.endYear ? source.endYear - (source.yearCount ?? 100) + 1 : currentYear - 50);
555
+ const endYear = source.endYear ?? startYear + (source.yearCount ?? 100) - 1;
556
+ const selectedYear = Number(values[0] ?? startYear);
557
+ const selectedMonth = Number(values[1] ?? 1);
558
+ const days = new Date(selectedYear, selectedMonth, 0).getDate();
559
+ const locale = getClassComponentsLocale();
560
+ const years = this.range(startYear, endYear).map((year) => ({ label: `${year}${locale.dateYearUnit}`, value: year }));
561
+ const months = this.range(1, 12).map((month) => ({ label: `${month}${locale.dateMonthUnit}`, value: month }));
562
+ const dayItems = this.range(1, days).map((day) => ({ label: `${day}${locale.dateDayUnit}`, value: day }));
563
+ return [years, months, dayItems];
564
+ }
565
+ range(start, end) {
566
+ const result = [];
567
+ for (let value = start; value <= end; value += 1)
568
+ result.push(value);
569
+ return result;
570
+ }
571
+ }
572
+
573
+ class VirtualSelectController {
574
+ constructor(options) {
575
+ this.multiple = Boolean(options.multiple);
576
+ this.options = options.options;
577
+ this.query = normalizeQuery(options.query);
578
+ this.selectedValues = normalizeSelectedValues(options.value, this.multiple);
579
+ }
580
+ update(options) {
581
+ if (options.multiple !== undefined)
582
+ this.multiple = Boolean(options.multiple);
583
+ if (options.options !== undefined)
584
+ this.options = options.options;
585
+ if (options.query !== undefined)
586
+ this.query = normalizeQuery(options.query);
587
+ if (Object.prototype.hasOwnProperty.call(options, "value")) {
588
+ this.selectedValues = normalizeSelectedValues(options.value, this.multiple);
589
+ }
590
+ return this.getState();
591
+ }
592
+ clear() {
593
+ this.selectedValues = [];
594
+ return this.getValue();
595
+ }
596
+ getState() {
597
+ const availableValues = new Set(this.options.map((option) => option.value));
598
+ const selectedValues = this.selectedValues.filter((value) => availableValues.has(value));
599
+ if (selectedValues.length !== this.selectedValues.length)
600
+ this.selectedValues = selectedValues;
601
+ return {
602
+ filteredOptions: this.getFilteredOptions(),
603
+ multiple: this.multiple,
604
+ query: this.query,
605
+ selectedOptions: this.options.filter((option) => selectedValues.includes(option.value)),
606
+ selectedValues
607
+ };
608
+ }
609
+ getValue() {
610
+ if (this.multiple)
611
+ return [...this.selectedValues];
612
+ return this.selectedValues[0] ?? "";
613
+ }
614
+ isSelected(value) {
615
+ return this.selectedValues.includes(value);
616
+ }
617
+ toggle(option) {
618
+ if (option.disabled)
619
+ return this.getValue();
620
+ if (!this.multiple) {
621
+ this.selectedValues = [option.value];
622
+ return option.value;
623
+ }
624
+ this.selectedValues = this.isSelected(option.value)
625
+ ? this.selectedValues.filter((value) => value !== option.value)
626
+ : [...this.selectedValues, option.value];
627
+ return this.getValue();
628
+ }
629
+ getFilteredOptions() {
630
+ if (!this.query)
631
+ return this.options;
632
+ return this.options.filter((option) => option.label.toLocaleLowerCase().includes(this.query));
633
+ }
634
+ }
635
+ function normalizeQuery(query) {
636
+ return String(query ?? "").trim().toLocaleLowerCase();
637
+ }
638
+ function normalizeSelectedValues(value, multiple) {
639
+ if (Array.isArray(value))
640
+ return multiple ? [...value] : value.slice(0, 1);
641
+ if (value === "" || value === undefined)
642
+ return [];
643
+ return [value];
644
+ }
645
+
646
+ function clampChannel(value) {
647
+ return Math.max(0, Math.min(255, Math.round(Number.isFinite(value) ? value : 0)));
648
+ }
649
+ function clampAlpha(value) {
650
+ return Math.max(0, Math.min(1, Number.isFinite(value) ? value : 1));
651
+ }
652
+ function toHex(value) {
653
+ return clampChannel(value).toString(16).padStart(2, "0");
654
+ }
655
+ function parseHex(value) {
656
+ const raw = value.trim();
657
+ const short = raw.match(/^#([0-9a-f]{3})([0-9a-f])?$/i);
658
+ if (short) {
659
+ const rgb = short[1] ?? "000";
660
+ const alpha = short[2] ?? "f";
661
+ const red = parseInt(rgb.charAt(0) + rgb.charAt(0), 16);
662
+ const green = parseInt(rgb.charAt(1) + rgb.charAt(1), 16);
663
+ const blue = parseInt(rgb.charAt(2) + rgb.charAt(2), 16);
664
+ return { alpha: parseInt(alpha + alpha, 16) / 255, blue, green, hex: `#${toHex(red)}${toHex(green)}${toHex(blue)}`, red };
665
+ }
666
+ const full = raw.match(/^#([0-9a-f]{6})([0-9a-f]{2})?$/i);
667
+ if (!full)
668
+ return undefined;
669
+ const rgb = full[1] ?? "000000";
670
+ return {
671
+ alpha: full[2] ? parseInt(full[2], 16) / 255 : 1,
672
+ blue: parseInt(rgb.slice(4, 6), 16),
673
+ green: parseInt(rgb.slice(2, 4), 16),
674
+ hex: `#${rgb.toLowerCase()}`,
675
+ red: parseInt(rgb.slice(0, 2), 16)
676
+ };
677
+ }
678
+ function parseRgb(value) {
679
+ const match = value.trim().match(/^rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})(?:\s*,\s*([\d.]+))?\s*\)$/i);
680
+ if (!match)
681
+ return undefined;
682
+ const red = clampChannel(Number(match[1]));
683
+ const green = clampChannel(Number(match[2]));
684
+ const blue = clampChannel(Number(match[3]));
685
+ return { alpha: clampAlpha(Number(match[4] ?? 1)), blue, green, hex: `#${toHex(red)}${toHex(green)}${toHex(blue)}`, red };
686
+ }
687
+ function formatRgba(red, green, blue, alpha) {
688
+ return `rgba(${clampChannel(red)}, ${clampChannel(green)}, ${clampChannel(blue)}, ${Number(clampAlpha(alpha).toFixed(2))})`;
689
+ }
690
+ function getContrastColor(red, green, blue, alpha) {
691
+ const opacity = clampAlpha(alpha);
692
+ const blendedRed = clampChannel(red) * opacity + 255 * (1 - opacity);
693
+ const blendedGreen = clampChannel(green) * opacity + 255 * (1 - opacity);
694
+ const blendedBlue = clampChannel(blue) * opacity + 255 * (1 - opacity);
695
+ const luminance = (0.299 * blendedRed + 0.587 * blendedGreen + 0.114 * blendedBlue) / 255;
696
+ return luminance > 0.58 ? "#000000" : "#ffffff";
697
+ }
698
+ function rgbToHsv(red, green, blue) {
699
+ const r = clampChannel(red) / 255;
700
+ const g = clampChannel(green) / 255;
701
+ const b = clampChannel(blue) / 255;
702
+ const max = Math.max(r, g, b);
703
+ const min = Math.min(r, g, b);
704
+ const delta = max - min;
705
+ let hue = 0;
706
+ if (delta !== 0) {
707
+ if (max === r)
708
+ hue = ((g - b) / delta) % 6;
709
+ else if (max === g)
710
+ hue = (b - r) / delta + 2;
711
+ else
712
+ hue = (r - g) / delta + 4;
713
+ hue *= 60;
714
+ if (hue < 0)
715
+ hue += 360;
716
+ }
717
+ return {
718
+ brightness: max,
719
+ hue,
720
+ saturation: max === 0 ? 0 : delta / max
721
+ };
722
+ }
723
+ function hsvToRgb(hue, saturation, brightness) {
724
+ const h = ((Number.isFinite(hue) ? hue : 0) % 360 + 360) % 360;
725
+ const s = Math.max(0, Math.min(1, Number.isFinite(saturation) ? saturation : 0));
726
+ const v = Math.max(0, Math.min(1, Number.isFinite(brightness) ? brightness : 0));
727
+ const c = v * s;
728
+ const x = c * (1 - Math.abs((h / 60) % 2 - 1));
729
+ const m = v - c;
730
+ let r = 0;
731
+ let g = 0;
732
+ let b = 0;
733
+ if (h < 60)
734
+ [r, g, b] = [c, x, 0];
735
+ else if (h < 120)
736
+ [r, g, b] = [x, c, 0];
737
+ else if (h < 180)
738
+ [r, g, b] = [0, c, x];
739
+ else if (h < 240)
740
+ [r, g, b] = [0, x, c];
741
+ else if (h < 300)
742
+ [r, g, b] = [x, 0, c];
743
+ else
744
+ [r, g, b] = [c, 0, x];
745
+ return {
746
+ blue: clampChannel((b + m) * 255),
747
+ green: clampChannel((g + m) * 255),
748
+ red: clampChannel((r + m) * 255)
749
+ };
750
+ }
751
+ class ColorPickerController {
752
+ constructor(value = "#7c3aed") {
753
+ this.state = this.parse(value);
754
+ }
755
+ parse(value) {
756
+ const parsed = parseRgb(value) ?? parseHex(value) ?? parseHex("#7c3aed");
757
+ const hsv = rgbToHsv(parsed.red, parsed.green, parsed.blue);
758
+ const rgba = formatRgba(parsed.red, parsed.green, parsed.blue, parsed.alpha);
759
+ return { ...parsed, ...hsv, contrastColor: getContrastColor(parsed.red, parsed.green, parsed.blue, parsed.alpha), rgba, value: rgba };
760
+ }
761
+ update(value) {
762
+ this.state = this.parse(value);
763
+ return this.getState();
764
+ }
765
+ setHex(hex) {
766
+ const parsed = parseHex(hex) ?? this.state;
767
+ this.state = this.parse(formatRgba(parsed.red, parsed.green, parsed.blue, this.state.alpha));
768
+ return this.getState();
769
+ }
770
+ setAlpha(alpha) {
771
+ this.state = this.parse(formatRgba(this.state.red, this.state.green, this.state.blue, alpha));
772
+ return this.getState();
773
+ }
774
+ setHue(hue) {
775
+ const rgb = hsvToRgb(hue, this.state.saturation, this.state.brightness);
776
+ this.state = this.parse(formatRgba(rgb.red, rgb.green, rgb.blue, this.state.alpha));
777
+ return this.getState();
778
+ }
779
+ setSaturationBrightness(saturation, brightness) {
780
+ const rgb = hsvToRgb(this.state.hue, saturation, brightness);
781
+ this.state = this.parse(formatRgba(rgb.red, rgb.green, rgb.blue, this.state.alpha));
782
+ return this.getState();
783
+ }
784
+ getState() {
785
+ return { ...this.state };
786
+ }
787
+ }
788
+
789
+ function pad$2(value) {
790
+ return String(value).padStart(2, "0");
791
+ }
792
+ function toDateKey$1(date) {
793
+ return `${date.getFullYear()}-${pad$2(date.getMonth() + 1)}-${pad$2(date.getDate())}`;
794
+ }
795
+ function parseDate$1(value) {
796
+ if (!value || !/^\d{4}-\d{2}-\d{2}$/.test(value))
797
+ return undefined;
798
+ const [yearText, monthText, dayText] = value.split("-");
799
+ const year = Number(yearText);
800
+ const month = Number(monthText);
801
+ const day = Number(dayText);
802
+ if (!Number.isFinite(year) || !Number.isFinite(month) || !Number.isFinite(day))
803
+ return undefined;
804
+ const date = new Date(year, month - 1, day);
805
+ return date.getFullYear() === year && date.getMonth() === month - 1 && date.getDate() === day ? date : undefined;
806
+ }
807
+ function compareKey$1(a, b) {
808
+ return a.localeCompare(b);
809
+ }
810
+ const lunarFormatter = (() => {
811
+ try {
812
+ return new Intl.DateTimeFormat("zh-CN-u-ca-chinese", { day: "numeric", month: "long" });
813
+ }
814
+ catch {
815
+ return undefined;
816
+ }
817
+ })();
818
+ function formatLunar(date) {
819
+ if (!lunarFormatter)
820
+ return undefined;
821
+ try {
822
+ return lunarFormatter.format(date).replace(/\s/g, "");
823
+ }
824
+ catch {
825
+ return undefined;
826
+ }
827
+ }
828
+ class DatePickerController {
829
+ constructor(options = {}) {
830
+ this.limitDate = parseDate$1(options.limitDate) ? options.limitDate : undefined;
831
+ this.limitDirection = options.limitDirection === "after" || options.limitDirection === "before" ? options.limitDirection : undefined;
832
+ this.min = parseDate$1(options.min) ? options.min : undefined;
833
+ this.max = parseDate$1(options.max) ? options.max : undefined;
834
+ const base = parseDate$1(options.value) ?? new Date();
835
+ this.state = this.createState(base.getFullYear(), base.getMonth() + 1, options.value ?? "");
836
+ }
837
+ updateOptions(options = {}) {
838
+ this.limitDate = parseDate$1(options.limitDate) ? options.limitDate : undefined;
839
+ this.limitDirection = options.limitDirection === "after" || options.limitDirection === "before" ? options.limitDirection : undefined;
840
+ this.min = parseDate$1(options.min) ? options.min : undefined;
841
+ this.max = parseDate$1(options.max) ? options.max : undefined;
842
+ const base = parseDate$1(options.value) ?? new Date(this.state.year, this.state.month - 1, 1);
843
+ this.state = this.createState(base.getFullYear(), base.getMonth() + 1, options.value ?? "");
844
+ return this.getState();
845
+ }
846
+ moveMonth(delta) {
847
+ const date = new Date(this.state.year, this.state.month - 1 + delta, 1);
848
+ this.state = this.createState(date.getFullYear(), date.getMonth() + 1, this.state.value);
849
+ return this.getState();
850
+ }
851
+ select(value) {
852
+ if (!parseDate$1(value) || this.isDisabled(value))
853
+ return this.getState();
854
+ const date = parseDate$1(value);
855
+ this.state = this.createState(date.getFullYear(), date.getMonth() + 1, value);
856
+ return this.getState();
857
+ }
858
+ createYearOptions(start, end) {
859
+ const current = new Date().getFullYear();
860
+ const minYear = start ?? current - 50;
861
+ const maxYear = end ?? current + 50;
862
+ return Array.from({ length: Math.max(1, maxYear - minYear + 1) }, (_, index) => minYear + index);
863
+ }
864
+ getDaysInMonth(year, month) {
865
+ return new Date(year, month, 0).getDate();
866
+ }
867
+ getState() {
868
+ return { ...this.state, days: this.state.days.map((day) => ({ ...day })) };
869
+ }
870
+ isDisabled(value) {
871
+ return Boolean((this.min && compareKey$1(value, this.min) < 0) ||
872
+ (this.max && compareKey$1(value, this.max) > 0) ||
873
+ (this.limitDate && this.limitDirection === "before" && compareKey$1(value, this.limitDate) < 0) ||
874
+ (this.limitDate && this.limitDirection === "after" && compareKey$1(value, this.limitDate) > 0));
875
+ }
876
+ createState(year, month, value) {
877
+ const first = new Date(year, month - 1, 1);
878
+ const start = new Date(year, month - 1, 1 - first.getDay());
879
+ const today = toDateKey$1(new Date());
880
+ const days = Array.from({ length: 42 }, (_, index) => {
881
+ const date = new Date(start.getFullYear(), start.getMonth(), start.getDate() + index);
882
+ const key = toDateKey$1(date);
883
+ return {
884
+ date: key,
885
+ day: date.getDate(),
886
+ disabled: this.isDisabled(key),
887
+ inMonth: date.getMonth() === month - 1,
888
+ lunar: formatLunar(date),
889
+ selected: key === value,
890
+ today: key === today
891
+ };
892
+ });
893
+ return { days, month, value, year };
894
+ }
895
+ }
896
+
897
+ function pad$1(value) {
898
+ return String(value).padStart(2, "0");
899
+ }
900
+ function toDateKey(date) {
901
+ return `${date.getFullYear()}-${pad$1(date.getMonth() + 1)}-${pad$1(date.getDate())}`;
902
+ }
903
+ function parseDate(value) {
904
+ if (!value || !/^\d{4}-\d{2}-\d{2}$/.test(value))
905
+ return undefined;
906
+ const [yearText, monthText, dayText] = value.split("-");
907
+ const year = Number(yearText);
908
+ const month = Number(monthText);
909
+ const day = Number(dayText);
910
+ if (!Number.isFinite(year) || !Number.isFinite(month) || !Number.isFinite(day))
911
+ return undefined;
912
+ const date = new Date(year, month - 1, day);
913
+ return date.getFullYear() === year && date.getMonth() === month - 1 && date.getDate() === day ? date : undefined;
914
+ }
915
+ function compareKey(a, b) {
916
+ if (!a || !b)
917
+ return 0;
918
+ return a.localeCompare(b);
919
+ }
920
+ function normalizeRange(value, allowSameDay = true) {
921
+ const start = parseDate(value?.start) ? value?.start : undefined;
922
+ const end = parseDate(value?.end) ? value?.end : undefined;
923
+ if (!allowSameDay && start && end && compareKey(start, end) === 0)
924
+ return { start, end: undefined };
925
+ if (start && end && compareKey(start, end) > 0)
926
+ return { start: end, end: start };
927
+ return { start, end };
928
+ }
929
+ function addMonths(date, delta) {
930
+ return new Date(date.getFullYear(), date.getMonth() + delta, 1);
931
+ }
932
+ class DateRangePickerController {
933
+ constructor(options = {}) {
934
+ this.allowSameDay = options.allowSameDay ?? true;
935
+ this.limitDate = parseDate(options.limitDate) ? options.limitDate : undefined;
936
+ this.limitDirection = options.limitDirection === "after" || options.limitDirection === "before" ? options.limitDirection : undefined;
937
+ this.min = parseDate(options.min) ? options.min : undefined;
938
+ this.max = parseDate(options.max) ? options.max : undefined;
939
+ const value = normalizeRange(options.value, this.allowSameDay);
940
+ const base = parseDate(value.start) ?? parseDate(value.end) ?? new Date();
941
+ this.state = this.createState(base.getFullYear(), base.getMonth() + 1, value);
942
+ }
943
+ updateOptions(options = {}) {
944
+ this.allowSameDay = options.allowSameDay ?? this.allowSameDay;
945
+ this.limitDate = parseDate(options.limitDate) ? options.limitDate : undefined;
946
+ this.limitDirection = options.limitDirection === "after" || options.limitDirection === "before" ? options.limitDirection : undefined;
947
+ this.min = parseDate(options.min) ? options.min : undefined;
948
+ this.max = parseDate(options.max) ? options.max : undefined;
949
+ const value = normalizeRange(options.value, this.allowSameDay);
950
+ const base = parseDate(value.start) ?? parseDate(value.end) ?? new Date(this.state.leftYear, this.state.leftMonth - 1, 1);
951
+ this.state = this.createState(base.getFullYear(), base.getMonth() + 1, value);
952
+ return this.getState();
953
+ }
954
+ moveMonth(delta) {
955
+ const base = new Date(this.state.leftYear, this.state.leftMonth - 1 + delta, 1);
956
+ this.state = this.createState(base.getFullYear(), base.getMonth() + 1, { start: this.state.start, end: this.state.end });
957
+ return this.getState();
958
+ }
959
+ hoverDate(value) {
960
+ this.hover = value && parseDate(value) && !this.isDisabled(value) ? value : undefined;
961
+ this.state = this.createState(this.state.leftYear, this.state.leftMonth, { start: this.state.start, end: this.state.end });
962
+ return this.getState();
963
+ }
964
+ select(value) {
965
+ if (!parseDate(value) || this.isDisabled(value))
966
+ return this.getState();
967
+ const current = { start: this.state.start, end: this.state.end };
968
+ let next;
969
+ if (!current.start || current.end || compareKey(value, current.start) < 0) {
970
+ next = { start: value, end: undefined };
971
+ }
972
+ else if (!this.allowSameDay && compareKey(value, current.start) === 0) {
973
+ next = { start: current.start, end: undefined };
974
+ }
975
+ else {
976
+ next = normalizeRange({ start: current.start, end: value }, this.allowSameDay);
977
+ this.hover = undefined;
978
+ }
979
+ const base = parseDate(next.start) ?? new Date(this.state.leftYear, this.state.leftMonth - 1, 1);
980
+ this.state = this.createState(base.getFullYear(), base.getMonth() + 1, next);
981
+ return this.getState();
982
+ }
983
+ clear() {
984
+ this.hover = undefined;
985
+ this.state = this.createState(this.state.leftYear, this.state.leftMonth, {});
986
+ return this.getState();
987
+ }
988
+ getValue() {
989
+ return { start: this.state.start, end: this.state.end };
990
+ }
991
+ getState() {
992
+ return {
993
+ ...this.state,
994
+ leftDays: this.state.leftDays.map((day) => ({ ...day })),
995
+ rightDays: this.state.rightDays.map((day) => ({ ...day }))
996
+ };
997
+ }
998
+ isDisabled(value) {
999
+ return Boolean((this.min && compareKey(value, this.min) < 0) ||
1000
+ (this.max && compareKey(value, this.max) > 0) ||
1001
+ (this.limitDate && this.limitDirection === "before" && compareKey(value, this.limitDate) < 0) ||
1002
+ (this.limitDate && this.limitDirection === "after" && compareKey(value, this.limitDate) > 0));
1003
+ }
1004
+ createState(year, month, value) {
1005
+ const leftBase = new Date(year, month - 1, 1);
1006
+ const rightBase = addMonths(leftBase, 1);
1007
+ const range = normalizeRange(value, this.allowSameDay);
1008
+ const selecting = range.start && !range.end ? "end" : "start";
1009
+ return {
1010
+ end: range.end,
1011
+ leftDays: this.createMonthDays(leftBase.getFullYear(), leftBase.getMonth() + 1, range),
1012
+ leftMonth: leftBase.getMonth() + 1,
1013
+ leftYear: leftBase.getFullYear(),
1014
+ rightDays: this.createMonthDays(rightBase.getFullYear(), rightBase.getMonth() + 1, range),
1015
+ rightMonth: rightBase.getMonth() + 1,
1016
+ rightYear: rightBase.getFullYear(),
1017
+ selecting,
1018
+ start: range.start
1019
+ };
1020
+ }
1021
+ createMonthDays(year, month, value) {
1022
+ const first = new Date(year, month - 1, 1);
1023
+ const start = new Date(year, month - 1, 1 - first.getDay());
1024
+ const today = toDateKey(new Date());
1025
+ const previewEnd = value.start && !value.end && this.hover ? (compareKey(this.hover, value.start) >= 0 ? this.hover : value.start) : undefined;
1026
+ const previewStart = value.start && !value.end && this.hover ? (compareKey(this.hover, value.start) >= 0 ? value.start : this.hover) : undefined;
1027
+ return Array.from({ length: 42 }, (_, index) => {
1028
+ const date = new Date(start.getFullYear(), start.getMonth(), start.getDate() + index);
1029
+ const key = toDateKey(date);
1030
+ const selectingEndSameDay = !this.allowSameDay && Boolean(value.start && !value.end && key === value.start);
1031
+ const selected = key === value.start || key === value.end;
1032
+ const inRange = Boolean(value.start && value.end && compareKey(key, value.start) >= 0 && compareKey(key, value.end) <= 0);
1033
+ const preview = Boolean(previewStart && previewEnd && compareKey(key, previewStart) >= 0 && compareKey(key, previewEnd) <= 0);
1034
+ return {
1035
+ date: key,
1036
+ day: date.getDate(),
1037
+ disabled: this.isDisabled(key) || selectingEndSameDay,
1038
+ inMonth: date.getMonth() === month - 1,
1039
+ inRange,
1040
+ preview,
1041
+ rangeEnd: key === value.end,
1042
+ rangeStart: key === value.start,
1043
+ selected,
1044
+ today: key === today
1045
+ };
1046
+ });
1047
+ }
1048
+ }
1049
+
1050
+ class PasswordInputController {
1051
+ constructor(options = {}) {
1052
+ this.visible = options.visible ?? false;
1053
+ }
1054
+ setVisible(visible) {
1055
+ this.visible = visible;
1056
+ return this.getState();
1057
+ }
1058
+ toggle() {
1059
+ this.visible = !this.visible;
1060
+ return this.getState();
1061
+ }
1062
+ getState() {
1063
+ return {
1064
+ inputType: this.visible ? "text" : "password",
1065
+ visible: this.visible
1066
+ };
1067
+ }
1068
+ }
1069
+
1070
+ const DEFAULT_WIDTH = 320;
1071
+ const DEFAULT_HEIGHT = 260;
1072
+ const DEFAULT_PIECE_SIZE = 52;
1073
+ const DEFAULT_TOLERANCE = 6;
1074
+ function finiteNumber(value, fallback) {
1075
+ return Number.isFinite(value) ? Number(value) : fallback;
1076
+ }
1077
+ function clamp$c(value, min, max) {
1078
+ return Math.min(max, Math.max(min, value));
1079
+ }
1080
+ class SlideCaptchaController {
1081
+ constructor(options = {}) {
1082
+ this.offsetX = 0;
1083
+ this.offsetY = 0;
1084
+ this.status = "idle";
1085
+ this.width = Math.max(160, finiteNumber(options.width, DEFAULT_WIDTH));
1086
+ this.height = Math.max(160, finiteNumber(options.height, DEFAULT_HEIGHT));
1087
+ this.pieceWidth = Math.max(24, finiteNumber(options.pieceWidth, DEFAULT_PIECE_SIZE));
1088
+ this.pieceHeight = Math.max(24, finiteNumber(options.pieceHeight, DEFAULT_PIECE_SIZE));
1089
+ this.moveAxis = options.moveAxis === "xy" ? "xy" : "x";
1090
+ this.originY = this.normalizeOriginY(options.originY);
1091
+ this.tolerance = Math.max(0, finiteNumber(options.tolerance, DEFAULT_TOLERANCE));
1092
+ this.targetX = this.normalizeTargetX(options.targetX);
1093
+ this.targetY = this.normalizeTargetY(options.targetY);
1094
+ this.value = Boolean(options.value);
1095
+ if (this.value) {
1096
+ this.offsetX = this.targetX;
1097
+ this.offsetY = this.moveAxis === "xy" ? this.targetY - this.originY : 0;
1098
+ this.status = "success";
1099
+ }
1100
+ }
1101
+ updateOptions(options = {}) {
1102
+ if (options.width !== undefined)
1103
+ this.width = Math.max(160, finiteNumber(options.width, DEFAULT_WIDTH));
1104
+ if (options.height !== undefined)
1105
+ this.height = Math.max(160, finiteNumber(options.height, DEFAULT_HEIGHT));
1106
+ if (options.pieceWidth !== undefined)
1107
+ this.pieceWidth = Math.max(24, finiteNumber(options.pieceWidth, DEFAULT_PIECE_SIZE));
1108
+ if (options.pieceHeight !== undefined)
1109
+ this.pieceHeight = Math.max(24, finiteNumber(options.pieceHeight, DEFAULT_PIECE_SIZE));
1110
+ if (options.moveAxis !== undefined)
1111
+ this.moveAxis = options.moveAxis === "xy" ? "xy" : "x";
1112
+ if (options.originY !== undefined || options.height !== undefined || options.pieceHeight !== undefined) {
1113
+ this.originY = this.normalizeOriginY(options.originY ?? this.originY);
1114
+ }
1115
+ if (options.tolerance !== undefined)
1116
+ this.tolerance = Math.max(0, finiteNumber(options.tolerance, DEFAULT_TOLERANCE));
1117
+ if (options.targetX !== undefined || options.width !== undefined || options.pieceWidth !== undefined) {
1118
+ this.targetX = this.normalizeTargetX(options.targetX ?? this.targetX);
1119
+ }
1120
+ if (options.targetY !== undefined || options.originY !== undefined || options.height !== undefined || options.pieceHeight !== undefined) {
1121
+ this.targetY = this.normalizeTargetY(options.targetY ?? this.targetY);
1122
+ }
1123
+ if (options.value !== undefined) {
1124
+ this.value = Boolean(options.value);
1125
+ this.status = this.value ? "success" : "idle";
1126
+ this.offsetX = this.value ? this.targetX : 0;
1127
+ this.offsetY = this.value && this.moveAxis === "xy" ? this.targetY - this.originY : 0;
1128
+ }
1129
+ else {
1130
+ this.offsetX = clamp$c(this.offsetX, 0, this.getMaxOffset());
1131
+ this.offsetY =
1132
+ this.moveAxis === "xy"
1133
+ ? clamp$c(this.offsetY, this.getMinOffsetY(), this.getMaxOffsetY())
1134
+ : 0;
1135
+ }
1136
+ return this.getState();
1137
+ }
1138
+ start() {
1139
+ if (this.value)
1140
+ return this.getState();
1141
+ this.status = "dragging";
1142
+ return this.getState();
1143
+ }
1144
+ move(offsetX, offsetY = 0) {
1145
+ if (this.value)
1146
+ return this.getState();
1147
+ this.offsetX = clamp$c(finiteNumber(offsetX, 0), 0, this.getMaxOffset());
1148
+ this.offsetY =
1149
+ this.moveAxis === "xy"
1150
+ ? clamp$c(finiteNumber(offsetY, 0), this.getMinOffsetY(), this.getMaxOffsetY())
1151
+ : 0;
1152
+ this.status = "dragging";
1153
+ return this.getState();
1154
+ }
1155
+ verify() {
1156
+ const success = Math.abs(this.offsetX - this.targetX) <= this.tolerance &&
1157
+ (this.moveAxis === "x" ||
1158
+ Math.abs(this.originY + this.offsetY - this.targetY) <= this.tolerance);
1159
+ this.value = success;
1160
+ this.status = success ? "success" : "failed";
1161
+ if (success) {
1162
+ this.offsetX = this.targetX;
1163
+ this.offsetY = this.moveAxis === "xy" ? this.targetY - this.originY : 0;
1164
+ }
1165
+ return this.getState();
1166
+ }
1167
+ reset() {
1168
+ this.offsetX = 0;
1169
+ this.offsetY = 0;
1170
+ this.status = "idle";
1171
+ this.value = false;
1172
+ return this.getState();
1173
+ }
1174
+ getState() {
1175
+ const maxOffset = this.getMaxOffset();
1176
+ const minOffsetY = this.getMinOffsetY();
1177
+ const maxOffsetY = this.getMaxOffsetY();
1178
+ return {
1179
+ height: this.height,
1180
+ maxOffset,
1181
+ maxOffsetY,
1182
+ minOffsetY,
1183
+ moveAxis: this.moveAxis,
1184
+ offsetX: this.offsetX,
1185
+ offsetY: this.offsetY,
1186
+ originY: this.originY,
1187
+ pieceHeight: this.pieceHeight,
1188
+ pieceWidth: this.pieceWidth,
1189
+ progress: maxOffset > 0 ? this.offsetX / maxOffset : 0,
1190
+ status: this.status,
1191
+ targetX: this.targetX,
1192
+ targetY: this.targetY,
1193
+ tolerance: this.tolerance,
1194
+ value: this.value,
1195
+ width: this.width
1196
+ };
1197
+ }
1198
+ getMaxOffset() {
1199
+ return Math.max(0, this.width - this.pieceWidth);
1200
+ }
1201
+ getMinOffsetY() {
1202
+ return -this.originY;
1203
+ }
1204
+ getMaxOffsetY() {
1205
+ return Math.max(this.getMinOffsetY(), this.height - this.pieceHeight - this.originY);
1206
+ }
1207
+ normalizeOriginY(value) {
1208
+ return clamp$c(finiteNumber(value, 0), 0, Math.max(0, this.height - this.pieceHeight));
1209
+ }
1210
+ normalizeTargetX(value) {
1211
+ return clamp$c(finiteNumber(value, this.width * 0.62), 0, this.getMaxOffset());
1212
+ }
1213
+ normalizeTargetY(value) {
1214
+ return clamp$c(finiteNumber(value, this.originY), 0, Math.max(0, this.height - this.pieceHeight));
1215
+ }
1216
+ }
1217
+
1218
+ function normalizeLength(length) {
1219
+ return Math.min(12, Math.max(1, Math.floor(length ?? 6)));
1220
+ }
1221
+ function normalizeValue$1(value, length) {
1222
+ return value.replace(/\s+/g, "").slice(0, length);
1223
+ }
1224
+ class VerificationCodeController {
1225
+ constructor(options = {}) {
1226
+ this.length = normalizeLength(options.length);
1227
+ this.value = normalizeValue$1(options.value ?? "", this.length);
1228
+ }
1229
+ updateOptions(options = {}) {
1230
+ if (options.length !== undefined)
1231
+ this.length = normalizeLength(options.length);
1232
+ if (options.value !== undefined)
1233
+ this.value = normalizeValue$1(options.value, this.length);
1234
+ else
1235
+ this.value = normalizeValue$1(this.value, this.length);
1236
+ return this.getState();
1237
+ }
1238
+ getNextInputIndex(preferredIndex = 0) {
1239
+ const chars = this.getState().chars;
1240
+ const firstEmpty = chars.findIndex((item) => !item);
1241
+ if (firstEmpty >= 0)
1242
+ return firstEmpty;
1243
+ return Math.min(this.length - 1, Math.max(0, preferredIndex));
1244
+ }
1245
+ input(index, char) {
1246
+ return this.inputAtNextSlot(index, char).state;
1247
+ }
1248
+ inputAtNextSlot(index, char) {
1249
+ const chars = this.getState().chars;
1250
+ const writeIndex = this.getNextInputIndex(index);
1251
+ if (writeIndex < 0 || writeIndex >= this.length) {
1252
+ return { focusIndex: Math.min(this.length - 1, Math.max(0, index)), state: this.getState(), writeIndex };
1253
+ }
1254
+ chars[writeIndex] = char.slice(-1);
1255
+ this.value = normalizeValue$1(chars.join(""), this.length);
1256
+ return {
1257
+ focusIndex: Math.min(this.length - 1, writeIndex + 1),
1258
+ state: this.getState(),
1259
+ writeIndex
1260
+ };
1261
+ }
1262
+ paste(value, startIndex = 0) {
1263
+ const chars = this.getState().chars;
1264
+ normalizeValue$1(value, this.length).split("").forEach((char, offset) => {
1265
+ const index = startIndex + offset;
1266
+ if (index >= 0 && index < this.length)
1267
+ chars[index] = char;
1268
+ });
1269
+ this.value = normalizeValue$1(chars.join(""), this.length);
1270
+ return this.getState();
1271
+ }
1272
+ remove(index) {
1273
+ if (index < 0 || index >= this.length)
1274
+ return this.getState();
1275
+ const chars = this.getState().chars;
1276
+ chars[index] = "";
1277
+ this.value = normalizeValue$1(chars.join(""), this.length);
1278
+ return this.getState();
1279
+ }
1280
+ clear() {
1281
+ this.value = "";
1282
+ return this.getState();
1283
+ }
1284
+ getState() {
1285
+ const chars = Array.from({ length: this.length }, (_, index) => this.value[index] ?? "");
1286
+ return { chars, complete: chars.every(Boolean), length: this.length, value: chars.join("") };
1287
+ }
1288
+ }
1289
+
1290
+ class ModalController {
1291
+ constructor(open = false) {
1292
+ this.emitter = new StateEmitter();
1293
+ this.openState = open;
1294
+ }
1295
+ isOpen() {
1296
+ return this.openState;
1297
+ }
1298
+ open() {
1299
+ this.setOpen(true);
1300
+ }
1301
+ close() {
1302
+ this.setOpen(false);
1303
+ }
1304
+ toggle() {
1305
+ this.setOpen(!this.openState);
1306
+ }
1307
+ setOpen(open) {
1308
+ if (this.openState === open)
1309
+ return;
1310
+ this.openState = open;
1311
+ this.emitter.emit(this.openState);
1312
+ }
1313
+ subscribe(listener) {
1314
+ listener(this.openState);
1315
+ return this.emitter.subscribe(listener);
1316
+ }
1317
+ }
1318
+
1319
+ function clamp$b(value, min, max) {
1320
+ return Math.min(max, Math.max(min, value));
1321
+ }
1322
+ class FloatingBallController {
1323
+ constructor(options = {}) {
1324
+ this.adsorb = options.adsorb ?? true;
1325
+ this.adsorbAxis = options.adsorbAxis ?? "xy";
1326
+ this.boundary = options.boundary;
1327
+ this.gap = Math.max(0, options.gap ?? 16);
1328
+ this.moveAxis = options.moveAxis ?? "xy";
1329
+ this.position = options.initialPosition ?? { x: this.gap, y: this.gap };
1330
+ }
1331
+ updateOptions(options) {
1332
+ if (options.adsorb !== undefined)
1333
+ this.adsorb = options.adsorb;
1334
+ if (options.adsorbAxis !== undefined)
1335
+ this.adsorbAxis = options.adsorbAxis;
1336
+ if (options.boundary !== undefined)
1337
+ this.boundary = options.boundary;
1338
+ if (options.gap !== undefined)
1339
+ this.gap = Math.max(0, options.gap);
1340
+ if (options.moveAxis !== undefined)
1341
+ this.moveAxis = options.moveAxis;
1342
+ return this.getState();
1343
+ }
1344
+ move(position, viewport, size) {
1345
+ this.position = this.clampToViewport({
1346
+ x: this.moveAxis === "y" ? this.position.x : position.x,
1347
+ y: this.moveAxis === "x" ? this.position.y : position.y
1348
+ }, this.boundary ?? viewport, size);
1349
+ this.edge = undefined;
1350
+ return this.getState();
1351
+ }
1352
+ release(viewport, size) {
1353
+ const bounds = this.boundary ?? viewport;
1354
+ if (!this.adsorb) {
1355
+ this.position = this.clampToViewport(this.position, bounds, size);
1356
+ return this.getState();
1357
+ }
1358
+ const distances = {
1359
+ bottom: bounds.height - (this.position.y + size.height),
1360
+ left: this.position.x,
1361
+ right: bounds.width - (this.position.x + size.width),
1362
+ top: this.position.y
1363
+ };
1364
+ const adsorbEdges = this.adsorbAxis === "x"
1365
+ ? ["left", "right"]
1366
+ : this.adsorbAxis === "y"
1367
+ ? ["top", "bottom"]
1368
+ : ["left", "right", "top", "bottom"];
1369
+ this.edge = adsorbEdges.reduce((best, edge) => distances[edge] < distances[best] ? edge : best);
1370
+ const next = { ...this.position };
1371
+ if (this.edge === "left")
1372
+ next.x = this.gap;
1373
+ if (this.edge === "right")
1374
+ next.x = bounds.width - size.width - this.gap;
1375
+ if (this.edge === "top")
1376
+ next.y = this.gap;
1377
+ if (this.edge === "bottom")
1378
+ next.y = bounds.height - size.height - this.gap;
1379
+ this.position = this.clampToViewport(next, bounds, size);
1380
+ return this.getState();
1381
+ }
1382
+ getState() {
1383
+ return {
1384
+ adsorb: this.adsorb,
1385
+ adsorbAxis: this.adsorbAxis,
1386
+ edge: this.edge,
1387
+ gap: this.gap,
1388
+ moveAxis: this.moveAxis,
1389
+ position: { ...this.position }
1390
+ };
1391
+ }
1392
+ clampToViewport(position, viewport, size) {
1393
+ return {
1394
+ x: clamp$b(position.x, this.gap, Math.max(this.gap, viewport.width - size.width - this.gap)),
1395
+ y: clamp$b(position.y, this.gap, Math.max(this.gap, viewport.height - size.height - this.gap))
1396
+ };
1397
+ }
1398
+ }
1399
+
1400
+ class PopupController {
1401
+ constructor(open = false, placement = "bottom") {
1402
+ this.emitter = new StateEmitter();
1403
+ this.openState = open;
1404
+ this.placementState = placement;
1405
+ }
1406
+ isOpen() {
1407
+ return this.openState;
1408
+ }
1409
+ getPlacement() {
1410
+ return this.placementState;
1411
+ }
1412
+ getState() {
1413
+ return {
1414
+ open: this.openState,
1415
+ placement: this.placementState
1416
+ };
1417
+ }
1418
+ open() {
1419
+ this.setOpen(true);
1420
+ }
1421
+ close() {
1422
+ this.setOpen(false);
1423
+ }
1424
+ toggle() {
1425
+ this.setOpen(!this.openState);
1426
+ }
1427
+ setOpen(open) {
1428
+ if (this.openState === open)
1429
+ return;
1430
+ this.openState = open;
1431
+ this.emit();
1432
+ }
1433
+ setPlacement(placement) {
1434
+ if (this.placementState === placement)
1435
+ return;
1436
+ this.placementState = placement;
1437
+ this.emit();
1438
+ }
1439
+ update(state) {
1440
+ const nextOpen = state.open ?? this.openState;
1441
+ const nextPlacement = state.placement ?? this.placementState;
1442
+ if (nextOpen === this.openState && nextPlacement === this.placementState)
1443
+ return;
1444
+ this.openState = nextOpen;
1445
+ this.placementState = nextPlacement;
1446
+ this.emit();
1447
+ }
1448
+ subscribe(listener) {
1449
+ listener(this.getState());
1450
+ return this.emitter.subscribe(listener);
1451
+ }
1452
+ emit() {
1453
+ this.emitter.emit(this.getState());
1454
+ }
1455
+ }
1456
+
1457
+ class TooltipController {
1458
+ constructor(open = false) {
1459
+ this.emitter = new StateEmitter();
1460
+ this.openState = open;
1461
+ }
1462
+ isOpen() {
1463
+ return this.openState;
1464
+ }
1465
+ getState() {
1466
+ return { open: this.openState };
1467
+ }
1468
+ subscribe(listener) {
1469
+ listener(this.getState());
1470
+ return this.emitter.subscribe(listener);
1471
+ }
1472
+ setOpen(open) {
1473
+ if (this.openState === open)
1474
+ return;
1475
+ this.openState = open;
1476
+ this.emit();
1477
+ }
1478
+ open() {
1479
+ this.setOpen(true);
1480
+ }
1481
+ close() {
1482
+ this.setOpen(false);
1483
+ }
1484
+ toggle() {
1485
+ this.setOpen(!this.openState);
1486
+ }
1487
+ destroy() {
1488
+ this.emitter.clear();
1489
+ }
1490
+ emit() {
1491
+ this.emitter.emit(this.getState());
1492
+ }
1493
+ }
1494
+
1495
+ class AffixController {
1496
+ constructor(offsetTop = 0) {
1497
+ this.offsetTop = Math.max(0, Number.isFinite(offsetTop) ? offsetTop : 0);
1498
+ }
1499
+ update(offsetTop = 0) {
1500
+ this.offsetTop = Math.max(0, Number.isFinite(offsetTop) ? offsetTop : 0);
1501
+ return this.resolve(0);
1502
+ }
1503
+ resolve(top) {
1504
+ return { fixed: top <= this.offsetTop, offsetTop: this.offsetTop };
1505
+ }
1506
+ }
1507
+
1508
+ class BacktopController {
1509
+ constructor(visibilityHeight = 240) {
1510
+ this.visibilityHeight = Math.max(0, Number.isFinite(visibilityHeight) ? visibilityHeight : 240);
1511
+ this.hysteresis = this.resolveHysteresis();
1512
+ }
1513
+ update(visibilityHeight = 240) {
1514
+ this.visibilityHeight = Math.max(0, Number.isFinite(visibilityHeight) ? visibilityHeight : 240);
1515
+ this.hysteresis = this.resolveHysteresis();
1516
+ return this.resolve(0);
1517
+ }
1518
+ resolve(scrollTop, previousVisible = false) {
1519
+ const safeScrollTop = Math.max(0, Number.isFinite(scrollTop) ? scrollTop : 0);
1520
+ const threshold = previousVisible ? Math.max(0, this.visibilityHeight - this.hysteresis) : this.visibilityHeight;
1521
+ return { visible: safeScrollTop >= threshold };
1522
+ }
1523
+ resolveHysteresis() {
1524
+ return Math.min(32, Math.max(8, Math.round(this.visibilityHeight * 0.08)));
1525
+ }
1526
+ }
1527
+
1528
+ function createId() {
1529
+ return `toast-${Date.now()}-${Math.random().toString(16).slice(2)}`;
1530
+ }
1531
+ class ToastManager {
1532
+ constructor() {
1533
+ this.emitter = new StateEmitter();
1534
+ this.timers = new Map();
1535
+ this.items = [];
1536
+ }
1537
+ getToasts() {
1538
+ return [...this.items];
1539
+ }
1540
+ show(options) {
1541
+ const toast = {
1542
+ id: options.id ?? createId(),
1543
+ title: options.title,
1544
+ message: options.message,
1545
+ tone: options.tone ?? "info",
1546
+ duration: options.duration ?? 3000,
1547
+ position: options.position ?? "top-right",
1548
+ };
1549
+ this.items = [toast, ...this.items.filter((item) => item.id !== toast.id)];
1550
+ this.scheduleDismiss(toast);
1551
+ this.notify();
1552
+ return toast.id;
1553
+ }
1554
+ dismiss(id) {
1555
+ const timer = this.timers.get(id);
1556
+ if (timer)
1557
+ clearTimeout(timer);
1558
+ this.timers.delete(id);
1559
+ this.items = this.items.filter((item) => item.id !== id);
1560
+ this.notify();
1561
+ }
1562
+ clear() {
1563
+ this.timers.forEach((timer) => clearTimeout(timer));
1564
+ this.timers.clear();
1565
+ this.items = [];
1566
+ this.notify();
1567
+ }
1568
+ subscribe(listener) {
1569
+ listener(this.getToasts());
1570
+ return this.emitter.subscribe(listener);
1571
+ }
1572
+ scheduleDismiss(toast) {
1573
+ const existing = this.timers.get(toast.id);
1574
+ if (existing)
1575
+ clearTimeout(existing);
1576
+ if (toast.duration <= 0)
1577
+ return;
1578
+ this.timers.set(toast.id, setTimeout(() => this.dismiss(toast.id), toast.duration));
1579
+ }
1580
+ notify() {
1581
+ this.emitter.emit(this.getToasts());
1582
+ }
1583
+ }
1584
+
1585
+ function getSafeTooltipPlacement(triggerRect, tooltipRect, preferred, viewportWidth = window.innerWidth, viewportHeight = window.innerHeight, margin = 8) {
1586
+ const fits = {
1587
+ top: triggerRect.top >= tooltipRect.height + margin,
1588
+ bottom: viewportHeight - triggerRect.bottom >= tooltipRect.height + margin,
1589
+ left: triggerRect.left >= tooltipRect.width + margin,
1590
+ right: viewportWidth - triggerRect.right >= tooltipRect.width + margin
1591
+ };
1592
+ if (preferred.startsWith("top") && fits.top)
1593
+ return preferred;
1594
+ if (preferred.startsWith("bottom") && fits.bottom)
1595
+ return preferred;
1596
+ if (preferred.startsWith("left") && fits.left)
1597
+ return preferred;
1598
+ if (preferred.startsWith("right") && fits.right)
1599
+ return preferred;
1600
+ if (fits.top)
1601
+ return "top";
1602
+ if (fits.bottom)
1603
+ return "bottom";
1604
+ if (fits.right)
1605
+ return "right";
1606
+ if (fits.left)
1607
+ return "left";
1608
+ return preferred;
1609
+ }
1610
+
1611
+ class TabsController {
1612
+ constructor(tabs, activeId) {
1613
+ this.emitter = new StateEmitter();
1614
+ this.tabs = tabs;
1615
+ this.activeId = activeId ?? tabs.find((tab) => !tab.disabled)?.id ?? tabs[0]?.id ?? "";
1616
+ }
1617
+ getActiveId() {
1618
+ return this.activeId;
1619
+ }
1620
+ setTabs(tabs) {
1621
+ this.tabs = tabs;
1622
+ if (!this.tabs.some((tab) => tab.id === this.activeId && !tab.disabled)) {
1623
+ this.activeId = this.tabs.find((tab) => !tab.disabled)?.id ?? this.tabs[0]?.id ?? "";
1624
+ this.emitter.emit(this.activeId);
1625
+ }
1626
+ }
1627
+ activate(id) {
1628
+ const tab = this.tabs.find((item) => item.id === id);
1629
+ if (!tab || tab.disabled || this.activeId === id)
1630
+ return;
1631
+ this.activeId = id;
1632
+ this.emitter.emit(this.activeId);
1633
+ }
1634
+ subscribe(listener) {
1635
+ listener(this.activeId);
1636
+ return this.emitter.subscribe(listener);
1637
+ }
1638
+ }
1639
+
1640
+ class BreadcrumbController {
1641
+ constructor(items = []) {
1642
+ this.items = this.normalize(items);
1643
+ }
1644
+ update(items = []) {
1645
+ this.items = this.normalize(items);
1646
+ return this.getState();
1647
+ }
1648
+ getState() {
1649
+ return { items: this.items.map((item) => ({ ...item })) };
1650
+ }
1651
+ normalize(items) {
1652
+ return items.filter((item) => item && String(item.label ?? "").trim()).map((item, index) => ({ ...item, key: item.key ?? index }));
1653
+ }
1654
+ }
1655
+
1656
+ class PaginationController {
1657
+ constructor(options = {}) {
1658
+ this.state = this.normalize(options);
1659
+ }
1660
+ update(options = {}) {
1661
+ this.state = this.normalize(options);
1662
+ return this.getState();
1663
+ }
1664
+ setCurrent(page) {
1665
+ this.state = this.normalize({ ...this.state, current: page });
1666
+ return this.getState();
1667
+ }
1668
+ getState() {
1669
+ return { ...this.state, pages: [...this.state.pages] };
1670
+ }
1671
+ normalize(options) {
1672
+ const boundaryCount = Math.max(1, Math.floor(Number.isFinite(options.boundaryCount) ? Number(options.boundaryCount) : 1));
1673
+ const siblingCount = Math.max(0, Math.floor(Number.isFinite(options.siblingCount) ? Number(options.siblingCount) : 1));
1674
+ const total = Math.max(0, Math.floor(Number.isFinite(options.total) ? Number(options.total) : 0));
1675
+ const pageSize = Math.max(1, Math.floor(Number.isFinite(options.pageSize) ? Number(options.pageSize) : 10));
1676
+ const pageCount = Math.max(1, Math.ceil(total / pageSize));
1677
+ const current = Math.max(1, Math.min(pageCount, Math.floor(Number.isFinite(options.current) ? Number(options.current) : 1)));
1678
+ const pages = this.createPages(current, pageCount, boundaryCount, siblingCount);
1679
+ return { boundaryCount, current, pageCount, pageSize, pages, siblingCount, total };
1680
+ }
1681
+ createPages(current, pageCount, boundaryCount, siblingCount) {
1682
+ const allCount = boundaryCount * 2 + siblingCount * 2 + 3;
1683
+ if (pageCount <= allCount)
1684
+ return Array.from({ length: pageCount }, (_, index) => index + 1);
1685
+ const pages = [];
1686
+ const addPage = (page) => {
1687
+ if (!pages.includes(page))
1688
+ pages.push(page);
1689
+ };
1690
+ for (let page = 1; page <= Math.min(boundaryCount, pageCount); page += 1)
1691
+ addPage(page);
1692
+ const siblingStart = Math.max(boundaryCount + 1, Math.min(current - siblingCount, pageCount - boundaryCount - siblingCount * 2));
1693
+ const siblingEnd = Math.min(pageCount - boundaryCount, Math.max(current + siblingCount, boundaryCount + 1 + siblingCount * 2));
1694
+ if (siblingStart > boundaryCount + 1)
1695
+ pages.push("ellipsis");
1696
+ for (let page = siblingStart; page <= siblingEnd; page += 1)
1697
+ addPage(page);
1698
+ if (siblingEnd < pageCount - boundaryCount)
1699
+ pages.push("ellipsis");
1700
+ for (let page = Math.max(pageCount - boundaryCount + 1, 1); page <= pageCount; page += 1)
1701
+ addPage(page);
1702
+ return pages;
1703
+ }
1704
+ }
1705
+
1706
+ class BadgeController {
1707
+ constructor(options = {}) {
1708
+ this.state = this.normalize(options);
1709
+ }
1710
+ update(options = {}) {
1711
+ this.state = this.normalize(options);
1712
+ return this.getState();
1713
+ }
1714
+ getState() {
1715
+ return { ...this.state };
1716
+ }
1717
+ normalize(options) {
1718
+ const max = Number.isFinite(options.max) ? Math.max(1, Number(options.max)) : 99;
1719
+ const value = typeof options.value === "number" && options.value > max ? `${max}+` : String(options.value ?? "");
1720
+ return {
1721
+ displayValue: options.dot ? "" : value,
1722
+ dot: Boolean(options.dot),
1723
+ tone: options.tone ?? "danger"
1724
+ };
1725
+ }
1726
+ }
1727
+
1728
+ class CalendarController {
1729
+ constructor(options = {}) {
1730
+ this.controller = new DatePickerController(options);
1731
+ }
1732
+ updateOptions(options = {}) {
1733
+ this.controller.updateOptions(options);
1734
+ return this.getState();
1735
+ }
1736
+ moveMonth(delta) {
1737
+ this.controller.moveMonth(delta);
1738
+ return this.getState();
1739
+ }
1740
+ select(value) {
1741
+ this.controller.select(value);
1742
+ return this.getState();
1743
+ }
1744
+ getState() {
1745
+ const state = this.controller.getState();
1746
+ const locale = getClassComponentsLocale();
1747
+ return {
1748
+ days: state.days,
1749
+ month: state.month,
1750
+ title: `${state.year}${locale.dateYearUnit} ${state.month}${locale.dateMonthUnit}`,
1751
+ value: state.value,
1752
+ year: state.year
1753
+ };
1754
+ }
1755
+ }
1756
+
1757
+ const MIN_NODE_WIDTH = 32;
1758
+ const MAX_NODE_WIDTH = 320;
1759
+ const DEFAULT_NODE_WIDTH = 160;
1760
+ const DEFAULT_NODE_HEIGHT = 64;
1761
+ const DEFAULT_GAP_X = 220;
1762
+ const MIN_NODE_GAP_Y = 4;
1763
+ function clamp$a(value, min, max) {
1764
+ return Math.min(max, Math.max(min, value));
1765
+ }
1766
+ function finiteOr$3(value, fallback) {
1767
+ return typeof value === "number" && Number.isFinite(value) ? value : fallback;
1768
+ }
1769
+ function estimateNodeWidth(node) {
1770
+ if (node.width !== undefined)
1771
+ return clamp$a(finiteOr$3(node.width, DEFAULT_NODE_WIDTH), MIN_NODE_WIDTH, MAX_NODE_WIDTH);
1772
+ const title = String(node.title || " ");
1773
+ const contentLines = String(node.content ?? "").split(/\r?\n/);
1774
+ const longestContentLine = contentLines.reduce((max, line) => Math.max(max, line.length), 0);
1775
+ const longestLine = Math.max(title.length, longestContentLine);
1776
+ return clamp$a(Math.ceil(longestLine * 7.2 + 24), MIN_NODE_WIDTH, MAX_NODE_WIDTH);
1777
+ }
1778
+ function estimateNodeHeight(node) {
1779
+ const width = estimateNodeWidth(node);
1780
+ const charsPerLine = Math.max(1, Math.floor((width - 24) / 7.2));
1781
+ const titleLines = Math.max(1, Math.ceil(String(node.title || " ").length / charsPerLine));
1782
+ const content = String(node.content ?? "");
1783
+ const contentLines = content.trim() === ""
1784
+ ? 0
1785
+ : content
1786
+ .split(/\r?\n/)
1787
+ .reduce((total, line) => total + Math.max(1, Math.ceil((line.length || 1) / charsPerLine)), 0);
1788
+ const estimated = 20 + titleLines * 18 + contentLines * 16 + (contentLines > 0 ? 6 : 0);
1789
+ return Math.max(DEFAULT_NODE_HEIGHT, Math.ceil(estimated));
1790
+ }
1791
+ function createDefaultNodes() {
1792
+ return [
1793
+ { id: "root", title: "项目画布", content: "双模式编辑器初版", x: 0, y: 0, width: 200, height: 92, children: ["doc", "mind"] },
1794
+ { id: "doc", parentId: "root", title: "文档模式", content: "像 Word/幕布一样输入结构化内容。", level: 2, children: ["doc-a"] },
1795
+ { id: "doc-a", parentId: "doc", title: "基础段落", content: "支持标题、正文、层级和只读状态。", level: 3 },
1796
+ { id: "mind", parentId: "root", title: "脑图模式", content: "卡片、拖拽、连线、缩放和平移。", level: 2, children: ["mind-a", "mind-b"] },
1797
+ { id: "mind-a", parentId: "mind", title: "卡片 A", content: "可拖拽节点", level: 3 },
1798
+ { id: "mind-b", parentId: "mind", title: "卡片 B", content: "自动生成连线", level: 3 }
1799
+ ];
1800
+ }
1801
+ function normalizeViewport(viewport) {
1802
+ return {
1803
+ x: finiteOr$3(viewport?.x, 0),
1804
+ y: finiteOr$3(viewport?.y, 0),
1805
+ zoom: clamp$a(finiteOr$3(viewport?.zoom, 1), 0.35, 2.4)
1806
+ };
1807
+ }
1808
+ function normalizeMode(mode) {
1809
+ return mode === "mind" ? "mind" : "document";
1810
+ }
1811
+ function cloneNode(node) {
1812
+ return {
1813
+ ...node,
1814
+ children: node.children ? [...node.children] : undefined
1815
+ };
1816
+ }
1817
+ function normalizeNodes(nodes) {
1818
+ const source = nodes && nodes.length > 0 ? nodes : createDefaultNodes();
1819
+ const seen = new Set();
1820
+ const normalized = source
1821
+ .filter((node) => {
1822
+ if (!node.id || seen.has(node.id))
1823
+ return false;
1824
+ seen.add(node.id);
1825
+ return true;
1826
+ })
1827
+ .map(cloneNode);
1828
+ return reconcileHierarchy(normalized);
1829
+ }
1830
+ function reconcileHierarchy(nodes) {
1831
+ const nodeMap = new Map(nodes.map((node) => [node.id, node]));
1832
+ const childIds = new Set();
1833
+ nodes.forEach((node) => {
1834
+ const children = (node.children ?? []).filter((childId) => childId !== node.id && nodeMap.has(childId));
1835
+ if (children.length > 0) {
1836
+ node.children = Array.from(new Set(children));
1837
+ node.children.forEach((childId) => {
1838
+ childIds.add(childId);
1839
+ const child = nodeMap.get(childId);
1840
+ if (child && !child.parentId)
1841
+ child.parentId = node.id;
1842
+ });
1843
+ }
1844
+ else {
1845
+ delete node.children;
1846
+ }
1847
+ });
1848
+ nodes.forEach((node) => {
1849
+ const parent = node.parentId ? nodeMap.get(node.parentId) : undefined;
1850
+ if (!parent || parent.id === node.id)
1851
+ return;
1852
+ const children = parent.children ?? [];
1853
+ if (!children.includes(node.id))
1854
+ parent.children = [...children, node.id];
1855
+ childIds.add(node.id);
1856
+ });
1857
+ nodes.forEach((node) => {
1858
+ if (!node.parentId && childIds.has(node.id)) {
1859
+ const parent = nodes.find((candidate) => candidate.children?.includes(node.id));
1860
+ if (parent)
1861
+ node.parentId = parent.id;
1862
+ }
1863
+ });
1864
+ return nodes;
1865
+ }
1866
+ function getRootId(nodes, rootId) {
1867
+ if (rootId && nodes.some((node) => node.id === rootId))
1868
+ return rootId;
1869
+ const explicitRoot = nodes.find((node) => !node.parentId);
1870
+ return explicitRoot?.id ?? nodes[0]?.id ?? "root";
1871
+ }
1872
+ class CanvasEditorController {
1873
+ constructor(options = {}) {
1874
+ this.idSeed = 0;
1875
+ this.mode = normalizeMode(options.mode);
1876
+ this.nodes = normalizeNodes(options.nodes);
1877
+ this.rootId = getRootId(this.nodes, options.rootId);
1878
+ this.viewport = normalizeViewport(options.viewport);
1879
+ this.disabled = options.disabled ?? false;
1880
+ this.readOnly = options.readOnly ?? false;
1881
+ }
1882
+ updateOptions(options = {}) {
1883
+ if (options.mode)
1884
+ this.mode = normalizeMode(options.mode);
1885
+ if (options.nodes) {
1886
+ this.nodes = normalizeNodes(options.nodes);
1887
+ this.rootId = getRootId(this.nodes, options.rootId ?? this.rootId);
1888
+ }
1889
+ else if (options.rootId) {
1890
+ this.rootId = getRootId(this.nodes, options.rootId);
1891
+ }
1892
+ if (options.viewport)
1893
+ this.viewport = normalizeViewport({ ...this.viewport, ...options.viewport });
1894
+ if (typeof options.disabled === "boolean")
1895
+ this.disabled = options.disabled;
1896
+ if (typeof options.readOnly === "boolean")
1897
+ this.readOnly = options.readOnly;
1898
+ return this.getState();
1899
+ }
1900
+ setMode(mode) {
1901
+ this.mode = normalizeMode(mode);
1902
+ return this.getState();
1903
+ }
1904
+ select(id) {
1905
+ this.selectedId = id && this.nodes.some((node) => node.id === id) ? id : undefined;
1906
+ return this.getState();
1907
+ }
1908
+ updateNode(id, patch) {
1909
+ if (this.disabled || this.readOnly)
1910
+ return this.getState();
1911
+ this.nodes = this.nodes.map((node) => {
1912
+ if (node.id !== id)
1913
+ return node;
1914
+ const nextNode = { ...node, ...patch };
1915
+ return { ...nextNode, height: estimateNodeHeight(nextNode) };
1916
+ });
1917
+ return this.getState();
1918
+ }
1919
+ addNode(input = {}) {
1920
+ if (this.disabled || this.readOnly)
1921
+ return this.getState();
1922
+ const nodeMap = new Map(this.nodes.map((node) => [node.id, node]));
1923
+ const fallbackParentId = this.selectedId && nodeMap.has(this.selectedId) ? this.selectedId : this.rootId;
1924
+ const parentId = input.parentId && nodeMap.has(input.parentId) ? input.parentId : fallbackParentId;
1925
+ const id = this.createNodeId(input.id);
1926
+ const nextNode = {
1927
+ content: input.content ?? "补充节点说明",
1928
+ id,
1929
+ parentId,
1930
+ title: input.title ?? "新节点",
1931
+ ...(input.width !== undefined ? { width: input.width } : {}),
1932
+ ...(input.x !== undefined ? { x: input.x } : {}),
1933
+ ...(input.y !== undefined ? { y: input.y } : {})
1934
+ };
1935
+ nextNode.height = finiteOr$3(input.height, estimateNodeHeight(nextNode));
1936
+ this.nodes = this.nodes
1937
+ .map((node) => node.id === parentId ? { ...node, children: [...(node.children ?? []), id] } : node)
1938
+ .concat(nextNode);
1939
+ this.selectedId = id;
1940
+ return this.getState();
1941
+ }
1942
+ removeNode(id) {
1943
+ if (this.disabled || this.readOnly || id === this.rootId)
1944
+ return this.getState();
1945
+ const nodeMap = new Map(this.nodes.map((node) => [node.id, node]));
1946
+ if (!nodeMap.has(id))
1947
+ return this.getState();
1948
+ const removeIds = new Set();
1949
+ const collect = (nodeId) => {
1950
+ if (removeIds.has(nodeId))
1951
+ return;
1952
+ removeIds.add(nodeId);
1953
+ (nodeMap.get(nodeId)?.children ?? []).forEach(collect);
1954
+ };
1955
+ collect(id);
1956
+ this.nodes = this.nodes
1957
+ .filter((node) => !removeIds.has(node.id))
1958
+ .map((node) => ({
1959
+ ...node,
1960
+ children: node.children?.filter((childId) => !removeIds.has(childId))
1961
+ }));
1962
+ if (this.selectedId && removeIds.has(this.selectedId))
1963
+ this.selectedId = undefined;
1964
+ return this.getState();
1965
+ }
1966
+ moveNode(input) {
1967
+ if (this.disabled || this.readOnly)
1968
+ return this.getState();
1969
+ const zoom = this.viewport.zoom || 1;
1970
+ this.nodes = this.nodes.map((node) => node.id === input.id
1971
+ ? {
1972
+ ...node,
1973
+ x: finiteOr$3(node.x, 0) + input.deltaX / zoom,
1974
+ y: finiteOr$3(node.y, 0) + input.deltaY / zoom
1975
+ }
1976
+ : node);
1977
+ return this.getState();
1978
+ }
1979
+ pan(input) {
1980
+ if (this.disabled)
1981
+ return this.getState();
1982
+ this.viewport = {
1983
+ ...this.viewport,
1984
+ x: this.viewport.x + input.deltaX,
1985
+ y: this.viewport.y + input.deltaY
1986
+ };
1987
+ return this.getState();
1988
+ }
1989
+ zoom(delta) {
1990
+ if (this.disabled)
1991
+ return this.getState();
1992
+ this.viewport = {
1993
+ ...this.viewport,
1994
+ zoom: clamp$a(this.viewport.zoom + delta, 0.35, 2.4)
1995
+ };
1996
+ return this.getState();
1997
+ }
1998
+ resetViewport() {
1999
+ this.viewport = normalizeViewport();
2000
+ return this.getState();
2001
+ }
2002
+ createNodeId(preferredId) {
2003
+ if (preferredId && !this.nodes.some((node) => node.id === preferredId))
2004
+ return preferredId;
2005
+ let id = "";
2006
+ do {
2007
+ this.idSeed += 1;
2008
+ id = `node-${Date.now().toString(36)}-${this.idSeed}`;
2009
+ } while (this.nodes.some((node) => node.id === id));
2010
+ return id;
2011
+ }
2012
+ getState() {
2013
+ const nodes = this.getLayoutNodes();
2014
+ return {
2015
+ disabled: this.disabled,
2016
+ links: this.createLinks(nodes),
2017
+ mode: this.mode,
2018
+ nodes,
2019
+ readOnly: this.readOnly,
2020
+ rootId: this.rootId,
2021
+ selectedId: this.selectedId,
2022
+ viewport: { ...this.viewport }
2023
+ };
2024
+ }
2025
+ getLayoutNodes() {
2026
+ const rawNodes = this.nodes.map(cloneNode);
2027
+ const nodeMap = new Map(rawNodes.map((node) => [node.id, node]));
2028
+ const root = nodeMap.get(this.rootId) ?? rawNodes[0];
2029
+ if (!root)
2030
+ return [];
2031
+ const visited = new Set();
2032
+ const depthById = new Map();
2033
+ const result = [];
2034
+ const walk = (id, depth, siblingIndex, offsetY) => {
2035
+ const node = nodeMap.get(id);
2036
+ if (!node || visited.has(id))
2037
+ return offsetY;
2038
+ visited.add(id);
2039
+ depthById.set(id, depth);
2040
+ const children = (node.children ?? []).filter((childId) => nodeMap.has(childId));
2041
+ const width = estimateNodeWidth(node);
2042
+ const height = Math.max(finiteOr$3(node.height, DEFAULT_NODE_HEIGHT), estimateNodeHeight({ ...node, width }));
2043
+ const startY = offsetY;
2044
+ let nextY = offsetY;
2045
+ children.forEach((childId, index) => {
2046
+ nextY = walk(childId, depth + 1, index, nextY);
2047
+ });
2048
+ const endY = children.length > 0 ? nextY - MIN_NODE_GAP_Y : startY;
2049
+ const fallbackY = children.length > 0 ? (startY + endY) / 2 : startY;
2050
+ const y = finiteOr$3(node.y, children.length > 0 ? fallbackY - height / 2 : siblingIndex === 0 && depth === 0 ? 0 : offsetY);
2051
+ result.push({
2052
+ ...node,
2053
+ height,
2054
+ width,
2055
+ x: depth * DEFAULT_GAP_X,
2056
+ y
2057
+ });
2058
+ return Math.max(children.length > 0 ? nextY : offsetY, y + height + MIN_NODE_GAP_Y);
2059
+ };
2060
+ walk(root.id, 0, 0, 0);
2061
+ rawNodes.forEach((node) => {
2062
+ if (!visited.has(node.id)) {
2063
+ depthById.set(node.id, 0);
2064
+ const width = estimateNodeWidth(node);
2065
+ const height = Math.max(finiteOr$3(node.height, DEFAULT_NODE_HEIGHT), estimateNodeHeight({ ...node, width }));
2066
+ result.push({
2067
+ ...node,
2068
+ height,
2069
+ width,
2070
+ x: 0,
2071
+ y: finiteOr$3(node.y, result.length * (height + MIN_NODE_GAP_Y))
2072
+ });
2073
+ }
2074
+ });
2075
+ return this.separateOverlappingNodes(result, depthById);
2076
+ }
2077
+ separateOverlappingNodes(nodes, depthById) {
2078
+ const nextNodes = nodes.map(cloneNode);
2079
+ const groups = new Map();
2080
+ nextNodes.forEach((node) => {
2081
+ const depth = depthById.get(node.id) ?? 0;
2082
+ const group = groups.get(depth) ?? [];
2083
+ group.push(node);
2084
+ groups.set(depth, group);
2085
+ });
2086
+ groups.forEach((group) => {
2087
+ group
2088
+ .sort((a, b) => finiteOr$3(a.y, 0) - finiteOr$3(b.y, 0))
2089
+ .reduce((nextY, node) => {
2090
+ const currentY = finiteOr$3(node.y, nextY);
2091
+ const y = Math.max(currentY, nextY);
2092
+ node.y = y;
2093
+ return y + finiteOr$3(node.height, DEFAULT_NODE_HEIGHT) + MIN_NODE_GAP_Y;
2094
+ }, Number.NEGATIVE_INFINITY);
2095
+ });
2096
+ return nextNodes;
2097
+ }
2098
+ createLinks(nodes) {
2099
+ const nodeMap = new Map(nodes.map((node) => [node.id, node]));
2100
+ const links = [];
2101
+ nodes.forEach((node) => {
2102
+ (node.children ?? []).forEach((childId) => {
2103
+ const child = nodeMap.get(childId);
2104
+ if (!child)
2105
+ return;
2106
+ const fromX = finiteOr$3(node.x, 0) + finiteOr$3(node.width, DEFAULT_NODE_WIDTH);
2107
+ const fromY = finiteOr$3(node.y, 0) + finiteOr$3(node.height, DEFAULT_NODE_HEIGHT) / 2;
2108
+ const toX = finiteOr$3(child.x, 0);
2109
+ const toY = finiteOr$3(child.y, 0) + finiteOr$3(child.height, DEFAULT_NODE_HEIGHT) / 2;
2110
+ const curve = Math.max(48, Math.abs(toX - fromX) * 0.45);
2111
+ links.push({
2112
+ d: `M ${fromX} ${fromY} C ${fromX + curve} ${fromY}, ${toX - curve} ${toY}, ${toX} ${toY}`,
2113
+ from: node.id,
2114
+ to: child.id
2115
+ });
2116
+ });
2117
+ });
2118
+ return links;
2119
+ }
2120
+ }
2121
+
2122
+ function finiteOr$2(value, fallback) {
2123
+ return Number.isFinite(value) ? Number(value) : fallback;
2124
+ }
2125
+ function clamp$9(value, min, max) {
2126
+ return Math.min(max, Math.max(min, value));
2127
+ }
2128
+ function normalizeDpr(value) {
2129
+ return clamp$9(finiteOr$2(value, 1), 1, 4);
2130
+ }
2131
+ function roundedRect(ctx, x, y, width, height, radius = 0) {
2132
+ const r = clamp$9(radius, 0, Math.min(width, height) / 2);
2133
+ ctx.beginPath();
2134
+ ctx.moveTo(x + r, y);
2135
+ ctx.lineTo(x + width - r, y);
2136
+ ctx.quadraticCurveTo(x + width, y, x + width, y + r);
2137
+ ctx.lineTo(x + width, y + height - r);
2138
+ ctx.quadraticCurveTo(x + width, y + height, x + width - r, y + height);
2139
+ ctx.lineTo(x + r, y + height);
2140
+ ctx.quadraticCurveTo(x, y + height, x, y + height - r);
2141
+ ctx.lineTo(x, y + r);
2142
+ ctx.quadraticCurveTo(x, y, x + r, y);
2143
+ ctx.closePath();
2144
+ }
2145
+ function createFont(layer) {
2146
+ const style = layer.fontStyle ?? "normal";
2147
+ const weight = layer.fontWeight ?? 500;
2148
+ const size = finiteOr$2(layer.fontSize, 16);
2149
+ const family = layer.fontFamily ?? "Inter, system-ui, -apple-system, BlinkMacSystemFont, sans-serif";
2150
+ return `${style} ${weight} ${size}px ${family}`;
2151
+ }
2152
+ function wrapText(ctx, text, maxWidth, maxLines = Number.MAX_SAFE_INTEGER) {
2153
+ const characters = Array.from(text);
2154
+ const lines = [];
2155
+ let line = "";
2156
+ for (const char of characters) {
2157
+ const next = `${line}${char}`;
2158
+ if (line && ctx.measureText(next).width > maxWidth) {
2159
+ lines.push(line);
2160
+ line = char;
2161
+ if (lines.length >= maxLines)
2162
+ break;
2163
+ }
2164
+ else {
2165
+ line = next;
2166
+ }
2167
+ }
2168
+ if (line && lines.length < maxLines)
2169
+ lines.push(line);
2170
+ if (lines.length === maxLines && characters.join("").length > lines.join("").length) {
2171
+ const last = lines[lines.length - 1] ?? "";
2172
+ let trimmed = last;
2173
+ while (trimmed && ctx.measureText(`${trimmed}...`).width > maxWidth)
2174
+ trimmed = trimmed.slice(0, -1);
2175
+ lines[lines.length - 1] = `${trimmed}...`;
2176
+ }
2177
+ return lines;
2178
+ }
2179
+ function loadImage(src) {
2180
+ return new Promise((resolve, reject) => {
2181
+ const image = new Image();
2182
+ image.crossOrigin = "anonymous";
2183
+ image.onload = () => resolve(image);
2184
+ image.onerror = () => reject(new Error(`Failed to load image: ${src}`));
2185
+ image.src = src;
2186
+ });
2187
+ }
2188
+ class CanvasImageController {
2189
+ constructor(options) {
2190
+ this.options = { ...options, layers: [...(options.layers ?? [])] };
2191
+ }
2192
+ updateOptions(options) {
2193
+ this.options = { ...this.options, ...options, layers: options.layers ? [...options.layers] : this.options.layers };
2194
+ return this.getState();
2195
+ }
2196
+ getState() {
2197
+ return {
2198
+ background: this.options.background ?? "transparent",
2199
+ dpr: normalizeDpr(this.options.dpr),
2200
+ height: Math.max(1, finiteOr$2(this.options.height, 1)),
2201
+ layerCount: this.options.layers?.length ?? 0,
2202
+ width: Math.max(1, finiteOr$2(this.options.width, 1))
2203
+ };
2204
+ }
2205
+ async draw(canvas) {
2206
+ const state = this.getState();
2207
+ const ctx = canvas.getContext("2d");
2208
+ if (!ctx)
2209
+ return state;
2210
+ canvas.width = Math.round(state.width * state.dpr);
2211
+ canvas.height = Math.round(state.height * state.dpr);
2212
+ canvas.style.width = `${state.width}px`;
2213
+ canvas.style.height = `${state.height}px`;
2214
+ ctx.setTransform(state.dpr, 0, 0, state.dpr, 0, 0);
2215
+ ctx.clearRect(0, 0, state.width, state.height);
2216
+ if (state.background !== "transparent") {
2217
+ ctx.fillStyle = state.background;
2218
+ ctx.fillRect(0, 0, state.width, state.height);
2219
+ }
2220
+ for (const layer of this.options.layers ?? []) {
2221
+ await this.drawLayer(ctx, layer);
2222
+ }
2223
+ return state;
2224
+ }
2225
+ async drawLayer(ctx, layer) {
2226
+ ctx.save();
2227
+ ctx.globalAlpha = clamp$9(layer.opacity ?? 1, 0, 1);
2228
+ if (layer.rotate) {
2229
+ ctx.translate(layer.x, layer.y);
2230
+ ctx.rotate((layer.rotate * Math.PI) / 180);
2231
+ ctx.translate(-layer.x, -layer.y);
2232
+ }
2233
+ if (layer.type === "rect") {
2234
+ roundedRect(ctx, layer.x, layer.y, layer.width, layer.height, layer.radius);
2235
+ if (layer.fill) {
2236
+ ctx.fillStyle = layer.fill;
2237
+ ctx.fill();
2238
+ }
2239
+ if (layer.stroke && layer.strokeWidth) {
2240
+ ctx.strokeStyle = layer.stroke;
2241
+ ctx.lineWidth = layer.strokeWidth;
2242
+ ctx.stroke();
2243
+ }
2244
+ }
2245
+ if (layer.type === "text") {
2246
+ ctx.font = createFont(layer);
2247
+ ctx.fillStyle = layer.color ?? "#111827";
2248
+ ctx.textAlign = layer.textAlign ?? "left";
2249
+ ctx.textBaseline = layer.textBaseline ?? "top";
2250
+ const lineHeight = finiteOr$2(layer.lineHeight, finiteOr$2(layer.fontSize, 16) * 1.4);
2251
+ const lines = layer.wrap && layer.maxWidth
2252
+ ? wrapText(ctx, layer.text, layer.maxWidth, layer.maxLines)
2253
+ : layer.text.split("\n").slice(0, layer.maxLines ?? Number.MAX_SAFE_INTEGER);
2254
+ lines.forEach((line, index) => {
2255
+ if (!layer.letterSpacing) {
2256
+ ctx.fillText(line, layer.x, layer.y + index * lineHeight, layer.maxWidth);
2257
+ return;
2258
+ }
2259
+ let offset = 0;
2260
+ Array.from(line).forEach((char) => {
2261
+ ctx.fillText(char, layer.x + offset, layer.y + index * lineHeight);
2262
+ offset += ctx.measureText(char).width + (layer.letterSpacing ?? 0);
2263
+ });
2264
+ });
2265
+ }
2266
+ if (layer.type === "image") {
2267
+ const image = await loadImage(layer.src);
2268
+ const sourceRatio = image.width / image.height;
2269
+ const targetRatio = layer.width / layer.height;
2270
+ let sx = 0;
2271
+ let sy = 0;
2272
+ let sw = image.width;
2273
+ let sh = image.height;
2274
+ let dx = layer.x;
2275
+ let dy = layer.y;
2276
+ let dw = layer.width;
2277
+ let dh = layer.height;
2278
+ if (layer.fit === "cover") {
2279
+ if (sourceRatio > targetRatio) {
2280
+ sw = image.height * targetRatio;
2281
+ sx = (image.width - sw) / 2;
2282
+ }
2283
+ else {
2284
+ sh = image.width / targetRatio;
2285
+ sy = (image.height - sh) / 2;
2286
+ }
2287
+ }
2288
+ else if (layer.fit === "contain") {
2289
+ if (sourceRatio > targetRatio) {
2290
+ dh = layer.width / sourceRatio;
2291
+ dy = layer.y + (layer.height - dh) / 2;
2292
+ }
2293
+ else {
2294
+ dw = layer.height * sourceRatio;
2295
+ dx = layer.x + (layer.width - dw) / 2;
2296
+ }
2297
+ }
2298
+ if (layer.radius) {
2299
+ roundedRect(ctx, layer.x, layer.y, layer.width, layer.height, layer.radius);
2300
+ ctx.clip();
2301
+ }
2302
+ ctx.drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh);
2303
+ }
2304
+ ctx.restore();
2305
+ }
2306
+ }
2307
+
2308
+ function toTimestamp(value) {
2309
+ if (value instanceof Date)
2310
+ return value.getTime();
2311
+ if (typeof value === "number")
2312
+ return Number.isFinite(value) ? value : Date.now();
2313
+ if (typeof value === "string") {
2314
+ const time = new Date(value).getTime();
2315
+ return Number.isFinite(time) ? time : Date.now();
2316
+ }
2317
+ return Date.now();
2318
+ }
2319
+ function toDuration(value, unit = 1) {
2320
+ if (value === undefined)
2321
+ return undefined;
2322
+ const normalized = Number(value);
2323
+ if (!Number.isFinite(normalized))
2324
+ return undefined;
2325
+ return Math.max(0, normalized * unit);
2326
+ }
2327
+ function resolveTarget(options = {}) {
2328
+ if (options.target !== undefined)
2329
+ return toTimestamp(options.target);
2330
+ const now = Number.isFinite(options.now ?? NaN) ? Number(options.now) : Date.now();
2331
+ const duration = toDuration(options.duration) ??
2332
+ toDuration(options.minutes, 60000) ??
2333
+ toDuration(options.seconds, 1000);
2334
+ return now + (duration ?? 0);
2335
+ }
2336
+ function pad(value) {
2337
+ return String(value).padStart(2, "0");
2338
+ }
2339
+ class CountdownController {
2340
+ constructor(options = {}) {
2341
+ this.autoStart = options.autoStart ?? true;
2342
+ this.format = options.format ?? "unit";
2343
+ this.running = this.autoStart;
2344
+ this.sourceMode = options.minutes !== undefined ? "minutes" : options.seconds !== undefined ? "seconds" : "default";
2345
+ this.target = resolveTarget(options);
2346
+ }
2347
+ updateOptions(options = {}) {
2348
+ if (options.autoStart !== undefined)
2349
+ this.autoStart = options.autoStart;
2350
+ if (options.format)
2351
+ this.format = options.format;
2352
+ if (options.target !== undefined ||
2353
+ options.duration !== undefined ||
2354
+ options.minutes !== undefined ||
2355
+ options.seconds !== undefined) {
2356
+ this.sourceMode = options.minutes !== undefined ? "minutes" : options.seconds !== undefined ? "seconds" : "default";
2357
+ this.target = resolveTarget(options);
2358
+ }
2359
+ if (options.autoStart !== undefined)
2360
+ this.running = options.autoStart;
2361
+ return this.getState(options.now);
2362
+ }
2363
+ start(now) {
2364
+ this.running = true;
2365
+ return this.getState(now);
2366
+ }
2367
+ pause(now) {
2368
+ this.running = false;
2369
+ return this.getState(now);
2370
+ }
2371
+ reset(target, now) {
2372
+ if (target !== undefined)
2373
+ this.target = toTimestamp(target);
2374
+ this.running = this.autoStart;
2375
+ return this.getState(now);
2376
+ }
2377
+ getState(now = Date.now()) {
2378
+ const parts = this.createParts(Math.max(0, this.target - now));
2379
+ const done = parts.total <= 0;
2380
+ if (done)
2381
+ this.running = false;
2382
+ return {
2383
+ ...parts,
2384
+ display: this.formatParts(parts),
2385
+ done,
2386
+ running: this.running && !done
2387
+ };
2388
+ }
2389
+ createParts(total) {
2390
+ const secondsTotal = Math.floor(total / 1000);
2391
+ const days = Math.floor(secondsTotal / 86400);
2392
+ const hours = Math.floor((secondsTotal % 86400) / 3600);
2393
+ const minutes = Math.floor((secondsTotal % 3600) / 60);
2394
+ const seconds = secondsTotal % 60;
2395
+ return { days, hours, milliseconds: total % 1000, minutes, seconds, total };
2396
+ }
2397
+ formatParts(parts) {
2398
+ if (this.sourceMode === "minutes") {
2399
+ const totalMinutes = Math.floor(Math.floor(parts.total / 1000) / 60);
2400
+ return `${pad(totalMinutes)}:${pad(parts.seconds)}`;
2401
+ }
2402
+ if (this.sourceMode === "seconds")
2403
+ return pad(Math.floor(parts.total / 1000));
2404
+ if (this.format === "compact")
2405
+ return `${parts.days > 0 ? `${parts.days}d ` : ""}${pad(parts.hours)}:${pad(parts.minutes)}:${pad(parts.seconds)}`;
2406
+ if (this.format === "clock")
2407
+ return `${pad(parts.days * 24 + parts.hours)}:${pad(parts.minutes)}:${pad(parts.seconds)}`;
2408
+ const locale = getClassComponentsLocale();
2409
+ return `${parts.days}${locale.countdownDayUnit} ${pad(parts.hours)}${locale.countdownHourUnit} ${pad(parts.minutes)}${locale.countdownMinuteUnit} ${pad(parts.seconds)}${locale.countdownSecondUnit}`;
2410
+ }
2411
+ }
2412
+
2413
+ class SkeletonController {
2414
+ constructor(options = {}) {
2415
+ this.state = this.normalize(options);
2416
+ }
2417
+ update(options = {}) {
2418
+ this.state = this.normalize(options);
2419
+ return this.getState();
2420
+ }
2421
+ getState() {
2422
+ return { ...this.state, rows: [...this.state.rows] };
2423
+ }
2424
+ normalize(options) {
2425
+ const count = Math.max(1, Math.min(20, Math.floor(Number.isFinite(options.count) ? Number(options.count) : 3)));
2426
+ return { animated: options.animated ?? true, rows: Array.from({ length: count }, (_, index) => index) };
2427
+ }
2428
+ }
2429
+
2430
+ class TagController {
2431
+ constructor(options = {}) {
2432
+ this.state = this.normalize(options);
2433
+ }
2434
+ update(options = {}) {
2435
+ this.state = this.normalize(options);
2436
+ return this.getState();
2437
+ }
2438
+ getState() {
2439
+ return { ...this.state };
2440
+ }
2441
+ normalize(options) {
2442
+ return {
2443
+ checkable: Boolean(options.checkable),
2444
+ checked: Boolean(options.checked),
2445
+ closable: Boolean(options.closable),
2446
+ disabled: Boolean(options.disabled),
2447
+ tone: options.tone ?? "neutral"
2448
+ };
2449
+ }
2450
+ }
2451
+
2452
+ function normalizeValue(value) {
2453
+ if (!Number.isFinite(value))
2454
+ return 0;
2455
+ return Math.max(0, Math.floor(value));
2456
+ }
2457
+ function toDigits(value, minDigits) {
2458
+ const normalized = normalizeValue(value);
2459
+ const chars = String(normalized).padStart(minDigits, "0").split("");
2460
+ return chars.map((char) => Number(char));
2461
+ }
2462
+ class RollingNumberController {
2463
+ constructor(value = 0, options = {}) {
2464
+ this.minDigits = Math.max(1, options.minDigits ?? 1);
2465
+ this.previousValue = normalizeValue(value);
2466
+ this.previousDigits = toDigits(this.previousValue, this.minDigits);
2467
+ }
2468
+ getStableColumns(value = this.previousValue) {
2469
+ const digits = toDigits(value, this.minDigits);
2470
+ return digits.map((digit, index) => ({
2471
+ key: `${digits.length - index - 1}`,
2472
+ digit,
2473
+ offset: digit
2474
+ }));
2475
+ }
2476
+ update(value) {
2477
+ const nextValue = normalizeValue(value);
2478
+ const nextDigits = toDigits(nextValue, this.minDigits);
2479
+ const previousDigits = this.alignPreviousDigits(nextDigits.length);
2480
+ const increasing = nextValue >= this.previousValue;
2481
+ const columns = nextDigits.map((digit, index) => {
2482
+ const previousDigit = previousDigits[index] ?? 0;
2483
+ const offset = increasing && previousDigit === 9 && digit === 0 ? 10 : digit;
2484
+ return {
2485
+ key: `${nextDigits.length - index - 1}`,
2486
+ digit,
2487
+ offset
2488
+ };
2489
+ });
2490
+ this.previousValue = nextValue;
2491
+ this.previousDigits = nextDigits;
2492
+ return columns;
2493
+ }
2494
+ alignPreviousDigits(length) {
2495
+ const padding = Array.from({ length: Math.max(0, length - this.previousDigits.length) }, () => 0);
2496
+ return [...padding, ...this.previousDigits].slice(-length);
2497
+ }
2498
+ }
2499
+
2500
+ const DEFAULT_ROOT_MARGIN = "200px";
2501
+ function normalizeRootMargin(rootMargin) {
2502
+ return typeof rootMargin === "string" && rootMargin.trim() ? rootMargin : DEFAULT_ROOT_MARGIN;
2503
+ }
2504
+ function normalizeThreshold(threshold) {
2505
+ if (typeof threshold !== "number" || !Number.isFinite(threshold))
2506
+ return 0;
2507
+ return Math.min(1, Math.max(0, threshold));
2508
+ }
2509
+ function normalizeSrc(src) {
2510
+ return typeof src === "string" ? src.trim() : "";
2511
+ }
2512
+ class LazyImageController {
2513
+ constructor(options) {
2514
+ this.state = {
2515
+ currentSrc: options.eager ? normalizeSrc(options.src) : undefined,
2516
+ eager: Boolean(options.eager),
2517
+ fallbackSrc: normalizeSrc(options.fallbackSrc) || undefined,
2518
+ placeholderSrc: normalizeSrc(options.placeholderSrc) || undefined,
2519
+ rootMargin: normalizeRootMargin(options.rootMargin),
2520
+ src: normalizeSrc(options.src),
2521
+ status: options.eager ? "loading" : "idle",
2522
+ threshold: normalizeThreshold(options.threshold)
2523
+ };
2524
+ }
2525
+ updateOptions(options) {
2526
+ const nextSrc = options.src === undefined ? this.state.src : normalizeSrc(options.src);
2527
+ const srcChanged = nextSrc !== this.state.src;
2528
+ const eager = options.eager ?? this.state.eager;
2529
+ this.state = {
2530
+ ...this.state,
2531
+ eager,
2532
+ fallbackSrc: options.fallbackSrc === undefined
2533
+ ? this.state.fallbackSrc
2534
+ : normalizeSrc(options.fallbackSrc) || undefined,
2535
+ placeholderSrc: options.placeholderSrc === undefined
2536
+ ? this.state.placeholderSrc
2537
+ : normalizeSrc(options.placeholderSrc) || undefined,
2538
+ rootMargin: normalizeRootMargin(options.rootMargin ?? this.state.rootMargin),
2539
+ src: nextSrc,
2540
+ threshold: normalizeThreshold(options.threshold ?? this.state.threshold)
2541
+ };
2542
+ if (srcChanged) {
2543
+ this.state.status = eager ? "loading" : "idle";
2544
+ this.state.currentSrc = eager ? nextSrc : undefined;
2545
+ }
2546
+ else if (eager && this.state.status === "idle") {
2547
+ this.startLoading();
2548
+ }
2549
+ return this.getState();
2550
+ }
2551
+ startLoading() {
2552
+ if (!this.state.src) {
2553
+ this.state = { ...this.state, currentSrc: undefined, status: "error" };
2554
+ return this.getState();
2555
+ }
2556
+ if (this.state.status === "loaded" && this.state.currentSrc === this.state.src) {
2557
+ return this.getState();
2558
+ }
2559
+ this.state = { ...this.state, currentSrc: this.state.src, status: "loading" };
2560
+ return this.getState();
2561
+ }
2562
+ markLoaded() {
2563
+ this.state = { ...this.state, status: "loaded" };
2564
+ return this.getState();
2565
+ }
2566
+ markError() {
2567
+ if (this.state.fallbackSrc && this.state.currentSrc !== this.state.fallbackSrc) {
2568
+ this.state = { ...this.state, currentSrc: this.state.fallbackSrc, status: "loading" };
2569
+ return this.getState();
2570
+ }
2571
+ this.state = { ...this.state, status: "error" };
2572
+ return this.getState();
2573
+ }
2574
+ getState() {
2575
+ return { ...this.state };
2576
+ }
2577
+ }
2578
+
2579
+ const DEFAULT_DURATION = 18000;
2580
+ const DEFAULT_GAP$1 = "32px";
2581
+ const DEFAULT_ITEM_HEIGHT = "28px";
2582
+ const DEFAULT_STEP_ANIMATION_DURATION = 420;
2583
+ function normalizeDirection$1(direction) {
2584
+ return direction === "vertical" ? "vertical" : "horizontal";
2585
+ }
2586
+ function normalizeDuration$1(duration) {
2587
+ if (typeof duration !== "number" || !Number.isFinite(duration))
2588
+ return DEFAULT_DURATION;
2589
+ return Math.max(1000, Math.floor(duration));
2590
+ }
2591
+ function normalizeGap(gap) {
2592
+ if (typeof gap === "number")
2593
+ return `${Math.max(0, gap)}px`;
2594
+ if (typeof gap === "string" && gap.trim())
2595
+ return gap;
2596
+ return DEFAULT_GAP$1;
2597
+ }
2598
+ function normalizeItemHeight(itemHeight) {
2599
+ if (typeof itemHeight === "number")
2600
+ return `${Math.max(1, itemHeight)}px`;
2601
+ if (typeof itemHeight === "string" && itemHeight.trim())
2602
+ return itemHeight;
2603
+ return DEFAULT_ITEM_HEIGHT;
2604
+ }
2605
+ function normalizeVerticalMode(verticalMode) {
2606
+ return verticalMode === "step" ? "step" : "continuous";
2607
+ }
2608
+ function normalizeStepAnimation(stepAnimation) {
2609
+ if (stepAnimation === null)
2610
+ return null;
2611
+ return stepAnimation === "fade" || stepAnimation === "flip" ? stepAnimation : "slide";
2612
+ }
2613
+ function normalizeStepAnimationDuration(duration) {
2614
+ if (typeof duration !== "number" || !Number.isFinite(duration))
2615
+ return DEFAULT_STEP_ANIMATION_DURATION;
2616
+ return Math.max(0, Math.floor(duration));
2617
+ }
2618
+ function normalizeItems(items) {
2619
+ return items
2620
+ .map((item, index) => {
2621
+ if (typeof item === "object" && item !== null) {
2622
+ const text = String(item.text).trim();
2623
+ return {
2624
+ key: item.key?.trim() || `${index}-${text}`,
2625
+ text,
2626
+ value: item.value
2627
+ };
2628
+ }
2629
+ const text = String(item).trim();
2630
+ return {
2631
+ key: `${index}-${text}`,
2632
+ text
2633
+ };
2634
+ })
2635
+ .filter((item) => item.text.length > 0);
2636
+ }
2637
+ class MarqueeController {
2638
+ constructor(items = [], options = {}) {
2639
+ this.items = normalizeItems(items);
2640
+ this.direction = normalizeDirection$1(options.direction);
2641
+ this.duration = normalizeDuration$1(options.duration);
2642
+ this.gap = normalizeGap(options.gap);
2643
+ this.itemHeight = normalizeItemHeight(options.itemHeight);
2644
+ this.pauseOnHover = options.pauseOnHover ?? true;
2645
+ this.reverse = Boolean(options.reverse);
2646
+ this.stepAnimation = normalizeStepAnimation(options.stepAnimation);
2647
+ this.stepAnimationDuration = normalizeStepAnimationDuration(options.stepAnimationDuration);
2648
+ this.verticalMode = normalizeVerticalMode(options.verticalMode);
2649
+ }
2650
+ update(items, options = {}) {
2651
+ this.items = normalizeItems(items);
2652
+ this.direction = normalizeDirection$1(options.direction ?? this.direction);
2653
+ this.duration = normalizeDuration$1(options.duration ?? this.duration);
2654
+ this.gap = normalizeGap(options.gap ?? this.gap);
2655
+ this.itemHeight = normalizeItemHeight(options.itemHeight ?? this.itemHeight);
2656
+ this.pauseOnHover = options.pauseOnHover ?? this.pauseOnHover;
2657
+ this.reverse = options.reverse ?? this.reverse;
2658
+ this.stepAnimation = normalizeStepAnimation(Object.prototype.hasOwnProperty.call(options, "stepAnimation")
2659
+ ? options.stepAnimation
2660
+ : this.stepAnimation);
2661
+ this.stepAnimationDuration = normalizeStepAnimationDuration(options.stepAnimationDuration ?? this.stepAnimationDuration);
2662
+ this.verticalMode = normalizeVerticalMode(options.verticalMode ?? this.verticalMode);
2663
+ return this.getState();
2664
+ }
2665
+ getState() {
2666
+ return {
2667
+ direction: this.direction,
2668
+ duration: this.duration,
2669
+ gap: this.gap,
2670
+ itemHeight: this.itemHeight,
2671
+ items: this.items,
2672
+ pauseOnHover: this.pauseOnHover,
2673
+ reverse: this.reverse,
2674
+ shouldAnimate: this.items.length > 1,
2675
+ stepAnimation: this.stepAnimation,
2676
+ stepAnimationDuration: this.stepAnimationDuration,
2677
+ verticalMode: this.verticalMode
2678
+ };
2679
+ }
2680
+ }
2681
+
2682
+ function clamp$8(value, min, max) {
2683
+ return Math.min(max, Math.max(min, value));
2684
+ }
2685
+ function finiteOr$1(value, fallback) {
2686
+ return typeof value === "number" && Number.isFinite(value) ? value : fallback;
2687
+ }
2688
+ function normalizeOptions$2(options = {}) {
2689
+ return {
2690
+ disabled: options.disabled ?? false,
2691
+ enterTransitionDuration: clamp$8(finiteOr$1(options.enterTransitionDuration, 180), 0, 600),
2692
+ glare: options.glare ?? true,
2693
+ maxTilt: clamp$8(finiteOr$1(options.maxTilt, 12), 0, 45),
2694
+ moveTransitionDuration: clamp$8(finiteOr$1(options.moveTransitionDuration, 80), 0, 300),
2695
+ perspective: clamp$8(finiteOr$1(options.perspective, 900), 240, 2400),
2696
+ scale: clamp$8(finiteOr$1(options.scale, 1.02), 1, 1.18),
2697
+ transitionEasing: options.transitionEasing ?? "cubic-bezier(0.2, 0.8, 0.2, 1)",
2698
+ transitionDuration: clamp$8(finiteOr$1(options.transitionDuration, 180), 0, 1200)
2699
+ };
2700
+ }
2701
+ function createTransform(rotateX, rotateY, scale, perspective) {
2702
+ return `perspective(${perspective}px) rotateX(${rotateX.toFixed(3)}deg) rotateY(${rotateY.toFixed(3)}deg) scale(${scale.toFixed(3)})`;
2703
+ }
2704
+ class TiltCardController {
2705
+ constructor(options = {}) {
2706
+ this.options = normalizeOptions$2(options);
2707
+ }
2708
+ updateOptions(options = {}) {
2709
+ this.options = normalizeOptions$2({ ...this.options, ...options });
2710
+ return this.reset();
2711
+ }
2712
+ move(input) {
2713
+ const width = Math.max(1, finiteOr$1(input.width, 1));
2714
+ const height = Math.max(1, finiteOr$1(input.height, 1));
2715
+ if (this.options.disabled) {
2716
+ return this.reset();
2717
+ }
2718
+ const x = clamp$8(input.pointerX / width, 0, 1);
2719
+ const y = clamp$8(input.pointerY / height, 0, 1);
2720
+ const rotateY = (x - 0.5) * this.options.maxTilt * 2;
2721
+ const rotateX = (0.5 - y) * this.options.maxTilt * 2;
2722
+ return {
2723
+ glare: this.options.glare,
2724
+ glareOpacity: this.options.glare ? clamp$8(Math.abs(x - 0.5) + Math.abs(y - 0.5), 0.16, 0.58) : 0,
2725
+ glareX: x * 100,
2726
+ glareY: y * 100,
2727
+ isActive: true,
2728
+ rotateX,
2729
+ rotateY,
2730
+ scale: this.options.scale,
2731
+ transform: createTransform(rotateX, rotateY, this.options.scale, this.options.perspective),
2732
+ transitionDuration: input.phase === "enter" ? this.options.enterTransitionDuration : this.options.moveTransitionDuration,
2733
+ transitionEasing: this.options.transitionEasing
2734
+ };
2735
+ }
2736
+ reset() {
2737
+ return {
2738
+ glare: this.options.glare,
2739
+ glareOpacity: 0,
2740
+ glareX: 50,
2741
+ glareY: 50,
2742
+ isActive: false,
2743
+ rotateX: 0,
2744
+ rotateY: 0,
2745
+ scale: 1,
2746
+ transform: createTransform(0, 0, 1, this.options.perspective),
2747
+ transitionDuration: this.options.transitionDuration,
2748
+ transitionEasing: this.options.transitionEasing
2749
+ };
2750
+ }
2751
+ getOptions() {
2752
+ return { ...this.options };
2753
+ }
2754
+ }
2755
+
2756
+ function finiteOr(value, fallback) {
2757
+ return typeof value === "number" && Number.isFinite(value) ? value : fallback;
2758
+ }
2759
+ function clamp$7(value, min, max) {
2760
+ return Math.min(max, Math.max(min, value));
2761
+ }
2762
+ function normalizeOptions$1(options = {}) {
2763
+ return {
2764
+ cursor: options.cursor ?? true,
2765
+ delay: clamp$7(finiteOr(options.delay, 0), 0, 60000),
2766
+ disabled: options.disabled ?? false,
2767
+ loading: options.loading ?? false,
2768
+ loop: options.loop ?? false,
2769
+ speed: clamp$7(finiteOr(options.speed, 36), 1, 5000),
2770
+ startOnMount: options.startOnMount ?? true,
2771
+ text: options.text ?? ""
2772
+ };
2773
+ }
2774
+ class TypewriterTextController {
2775
+ constructor(options = {}) {
2776
+ this.options = normalizeOptions$1(options);
2777
+ this.index = this.options.disabled ? this.options.text.length : 0;
2778
+ this.running = !this.options.disabled && this.options.startOnMount && this.options.text.length > 0;
2779
+ }
2780
+ updateOptions(options = {}) {
2781
+ const previousText = this.options.text;
2782
+ this.options = normalizeOptions$1({ ...this.options, ...options });
2783
+ if (this.options.disabled) {
2784
+ this.index = this.options.text.length;
2785
+ this.running = false;
2786
+ return this.getState();
2787
+ }
2788
+ if (previousText !== this.options.text) {
2789
+ this.index = 0;
2790
+ this.running = this.options.startOnMount && this.options.text.length > 0;
2791
+ }
2792
+ else {
2793
+ this.index = clamp$7(this.index, 0, this.options.text.length);
2794
+ if (this.index >= this.options.text.length)
2795
+ this.running = false;
2796
+ }
2797
+ return this.getState();
2798
+ }
2799
+ start() {
2800
+ if (!this.options.disabled && this.index < this.options.text.length)
2801
+ this.running = true;
2802
+ return this.getState();
2803
+ }
2804
+ pause() {
2805
+ this.running = false;
2806
+ return this.getState();
2807
+ }
2808
+ reset() {
2809
+ this.index = this.options.disabled ? this.options.text.length : 0;
2810
+ this.running = !this.options.disabled && this.options.startOnMount && this.options.text.length > 0;
2811
+ return this.getState();
2812
+ }
2813
+ finish() {
2814
+ this.index = this.options.text.length;
2815
+ this.running = false;
2816
+ return this.getState();
2817
+ }
2818
+ tick() {
2819
+ if (!this.running || this.options.disabled)
2820
+ return this.getState();
2821
+ if (this.index < this.options.text.length)
2822
+ this.index += 1;
2823
+ if (this.index >= this.options.text.length) {
2824
+ if (this.options.loop && this.options.text.length > 0) {
2825
+ this.index = 0;
2826
+ this.running = true;
2827
+ }
2828
+ else {
2829
+ this.running = false;
2830
+ }
2831
+ }
2832
+ return this.getState();
2833
+ }
2834
+ getDelay() {
2835
+ return this.options.delay;
2836
+ }
2837
+ getSpeed() {
2838
+ return this.options.speed;
2839
+ }
2840
+ getState() {
2841
+ return {
2842
+ cursor: this.options.cursor,
2843
+ displayedText: this.options.text.slice(0, this.index),
2844
+ done: !this.running && this.index >= this.options.text.length,
2845
+ index: this.index,
2846
+ loading: this.options.loading && this.running,
2847
+ running: this.running,
2848
+ text: this.options.text
2849
+ };
2850
+ }
2851
+ }
2852
+
2853
+ const defaultColors = ["#7c3aed", "#ec4899", "#22d3ee"];
2854
+ const cssColorPattern$1 = /^(#|rgb\(|rgba\(|hsl\(|hsla\(|color\(|color-mix\(|var\(|[a-zA-Z])/;
2855
+ function normalizeColors$1(colors) {
2856
+ const next = (colors ?? [])
2857
+ .map((color) => String(color).trim())
2858
+ .filter((color) => color.length > 0 && cssColorPattern$1.test(color));
2859
+ return next.length >= 2 ? next : [...defaultColors];
2860
+ }
2861
+ function normalizeDirection(direction) {
2862
+ if (typeof direction === "number" && Number.isFinite(direction))
2863
+ return `${direction}deg`;
2864
+ const next = String(direction ?? "90deg").trim();
2865
+ return next.length > 0 ? next : "90deg";
2866
+ }
2867
+ function normalizeDuration(duration) {
2868
+ return Math.max(600, Number.isFinite(duration) ? Number(duration) : 3600);
2869
+ }
2870
+ function normalizeText(text) {
2871
+ return String(text ?? "");
2872
+ }
2873
+ function normalizeOptions(options = {}) {
2874
+ const colors = normalizeColors$1(options.colors);
2875
+ const direction = normalizeDirection(options.direction);
2876
+ return {
2877
+ animated: options.animated ?? false,
2878
+ backgroundImage: `linear-gradient(${direction}, ${colors.join(", ")})`,
2879
+ backgroundSize: String(options.backgroundSize ?? "200% 100%"),
2880
+ colors,
2881
+ direction,
2882
+ duration: normalizeDuration(options.duration),
2883
+ easing: String(options.easing ?? "cubic-bezier(0.22, 0.8, 0.28, 1)"),
2884
+ fontWeight: options.fontWeight ?? 700,
2885
+ text: normalizeText(options.text)
2886
+ };
2887
+ }
2888
+ class GradientTextController {
2889
+ constructor(options = {}) {
2890
+ this.state = normalizeOptions(options);
2891
+ }
2892
+ updateOptions(options = {}) {
2893
+ this.state = normalizeOptions(options);
2894
+ return this.getState();
2895
+ }
2896
+ getState() {
2897
+ return { ...this.state, colors: [...this.state.colors] };
2898
+ }
2899
+ }
2900
+
2901
+ const presetSet = new Set([
2902
+ "aurora",
2903
+ "border-glow",
2904
+ "frosted",
2905
+ "glass",
2906
+ "gradient",
2907
+ "holographic",
2908
+ "mesh",
2909
+ "neon",
2910
+ "noise",
2911
+ "scanline",
2912
+ "soft-grid",
2913
+ "spotlight"
2914
+ ]);
2915
+ const toneSet = new Set(["accent", "cool", "danger", "neutral", "success", "warm"]);
2916
+ const toneColors = {
2917
+ accent: ["#7c3aed", "#ec4899", "#22d3ee"],
2918
+ cool: ["#0ea5e9", "#22d3ee", "#14b8a6"],
2919
+ danger: ["#ef4444", "#f97316", "#facc15"],
2920
+ neutral: ["#334155", "#94a3b8", "#e2e8f0"],
2921
+ success: ["#16a34a", "#22c55e", "#84cc16"],
2922
+ warm: ["#f59e0b", "#f97316", "#fb7185"]
2923
+ };
2924
+ const cssColorPattern = /^(#|rgb\(|rgba\(|hsl\(|hsla\(|color\(|color-mix\(|var\(|[a-zA-Z])/;
2925
+ function clamp$6(value, min, max) {
2926
+ return Math.min(max, Math.max(min, value));
2927
+ }
2928
+ function normalizePreset(preset) {
2929
+ return presetSet.has(preset) ? preset : "glass";
2930
+ }
2931
+ function normalizeTone(tone) {
2932
+ return toneSet.has(tone) ? tone : "accent";
2933
+ }
2934
+ function normalizeRadius(radius) {
2935
+ if (typeof radius === "number" && Number.isFinite(radius))
2936
+ return `${Math.max(0, radius)}px`;
2937
+ const next = String(radius ?? "var(--cc-radius-lg)").trim();
2938
+ return next.length > 0 ? next : "var(--cc-radius-lg)";
2939
+ }
2940
+ function normalizeColors(colors, tone) {
2941
+ const next = (colors ?? [])
2942
+ .map((color) => String(color).trim())
2943
+ .filter((color) => color.length > 0 && cssColorPattern.test(color));
2944
+ return next.length >= 2 ? next : [...toneColors[tone]];
2945
+ }
2946
+ function normalizeIntensity(intensity) {
2947
+ return clamp$6(Number.isFinite(intensity) ? Number(intensity) : 0.72, 0, 1);
2948
+ }
2949
+ class DesignEffectController {
2950
+ constructor(options = {}) {
2951
+ this.state = this.normalize(options);
2952
+ }
2953
+ updateOptions(options = {}) {
2954
+ this.state = this.normalize(options);
2955
+ return this.getState();
2956
+ }
2957
+ getState() {
2958
+ return {
2959
+ ...this.state,
2960
+ colors: [...this.state.colors],
2961
+ styleVars: { ...this.state.styleVars }
2962
+ };
2963
+ }
2964
+ normalize(options) {
2965
+ const preset = normalizePreset(options.preset);
2966
+ const tone = normalizeTone(options.tone);
2967
+ const colors = normalizeColors(options.colors, tone);
2968
+ const intensity = normalizeIntensity(options.intensity);
2969
+ const radius = normalizeRadius(options.radius);
2970
+ const animated = options.animated ?? ["aurora", "gradient", "holographic", "scanline"].includes(preset);
2971
+ return {
2972
+ animated,
2973
+ className: `cc-design-effect cc-design-effect--${preset}`,
2974
+ colors,
2975
+ disabled: options.disabled ?? false,
2976
+ intensity,
2977
+ interactive: options.interactive ?? ["border-glow", "glass", "spotlight"].includes(preset),
2978
+ overlay: !["border-glow", "noise", "soft-grid"].includes(preset),
2979
+ preset,
2980
+ radius,
2981
+ styleVars: {
2982
+ "--cc-design-effect-alpha": intensity.toFixed(3),
2983
+ "--cc-design-effect-color-1": colors[0],
2984
+ "--cc-design-effect-color-2": colors[1],
2985
+ "--cc-design-effect-color-3": colors[2] ?? colors[0],
2986
+ "--cc-design-effect-radius": radius
2987
+ },
2988
+ tone
2989
+ };
2990
+ }
2991
+ }
2992
+
2993
+ function clamp$5(value, min, max) {
2994
+ return Math.min(max, Math.max(min, value));
2995
+ }
2996
+ class SwiperController {
2997
+ constructor(options) {
2998
+ this.count = Math.max(0, Math.floor(options.count));
2999
+ this.current = clamp$5(Math.floor(options.current ?? 0), 0, Math.max(0, this.count - 1));
3000
+ this.loop = options.loop ?? false;
3001
+ this.threshold = Math.max(1, options.threshold ?? 48);
3002
+ }
3003
+ updateOptions(options) {
3004
+ if (options.count !== undefined)
3005
+ this.count = Math.max(0, Math.floor(options.count));
3006
+ if (options.loop !== undefined)
3007
+ this.loop = options.loop;
3008
+ if (options.threshold !== undefined)
3009
+ this.threshold = Math.max(1, options.threshold);
3010
+ if (options.current !== undefined)
3011
+ this.current = clamp$5(Math.floor(options.current), 0, Math.max(0, this.count - 1));
3012
+ else
3013
+ this.current = clamp$5(this.current, 0, Math.max(0, this.count - 1));
3014
+ return this.getState();
3015
+ }
3016
+ next() {
3017
+ if (this.count <= 0)
3018
+ return this.getState();
3019
+ this.current = this.current >= this.count - 1 ? (this.loop ? 0 : this.count - 1) : this.current + 1;
3020
+ return this.getState();
3021
+ }
3022
+ previous() {
3023
+ if (this.count <= 0)
3024
+ return this.getState();
3025
+ this.current = this.current <= 0 ? (this.loop ? this.count - 1 : 0) : this.current - 1;
3026
+ return this.getState();
3027
+ }
3028
+ resolveSwipe(deltaX, velocityX = 0) {
3029
+ if (Math.abs(deltaX) < this.threshold && Math.abs(velocityX) < 360)
3030
+ return this.getState();
3031
+ return deltaX < 0 || velocityX < -360 ? this.next() : this.previous();
3032
+ }
3033
+ setCurrent(index) {
3034
+ this.current = clamp$5(index, 0, Math.max(0, this.count - 1));
3035
+ return this.getState();
3036
+ }
3037
+ getState() {
3038
+ return {
3039
+ count: this.count,
3040
+ current: this.current,
3041
+ loop: this.loop
3042
+ };
3043
+ }
3044
+ }
3045
+
3046
+ function clamp$4(value, min, max) {
3047
+ return Math.min(max, Math.max(min, value));
3048
+ }
3049
+ class ImagePreviewController {
3050
+ constructor(items = [], activeIndex = 0, open = false) {
3051
+ this.loading = false;
3052
+ this.items = items.filter((item) => item.src);
3053
+ this.activeIndex = clamp$4(activeIndex, 0, Math.max(0, this.items.length - 1));
3054
+ this.openState = open;
3055
+ }
3056
+ updateItems(items, activeIndex = this.activeIndex) {
3057
+ this.items = items.filter((item) => item.src);
3058
+ this.activeIndex = clamp$4(activeIndex, 0, Math.max(0, this.items.length - 1));
3059
+ this.loading = false;
3060
+ return this.getState();
3061
+ }
3062
+ open(index = this.activeIndex) {
3063
+ this.openState = true;
3064
+ this.activeIndex = clamp$4(index, 0, Math.max(0, this.items.length - 1));
3065
+ return this.getState();
3066
+ }
3067
+ close() {
3068
+ this.openState = false;
3069
+ return this.getState();
3070
+ }
3071
+ next() {
3072
+ if (this.items.length > 0)
3073
+ this.activeIndex = (this.activeIndex + 1) % this.items.length;
3074
+ this.loading = true;
3075
+ return this.getState();
3076
+ }
3077
+ previous() {
3078
+ if (this.items.length > 0)
3079
+ this.activeIndex = (this.activeIndex - 1 + this.items.length) % this.items.length;
3080
+ this.loading = true;
3081
+ return this.getState();
3082
+ }
3083
+ setActiveIndex(index) {
3084
+ this.activeIndex = clamp$4(index, 0, Math.max(0, this.items.length - 1));
3085
+ this.loading = true;
3086
+ return this.getState();
3087
+ }
3088
+ setLoading(loading) {
3089
+ this.loading = loading;
3090
+ return this.getState();
3091
+ }
3092
+ getState() {
3093
+ return {
3094
+ activeIndex: this.activeIndex,
3095
+ activeItem: this.items[this.activeIndex],
3096
+ count: this.items.length,
3097
+ items: this.items,
3098
+ loading: this.loading,
3099
+ open: this.openState
3100
+ };
3101
+ }
3102
+ }
3103
+
3104
+ class TimelineController {
3105
+ constructor(options) {
3106
+ this.items = options.items;
3107
+ this.direction = options.direction ?? "vertical";
3108
+ }
3109
+ updateOptions(options) {
3110
+ if (options.items)
3111
+ this.items = options.items;
3112
+ if (options.direction)
3113
+ this.direction = options.direction;
3114
+ return this.getState();
3115
+ }
3116
+ getState() {
3117
+ return { direction: this.direction, items: this.items };
3118
+ }
3119
+ }
3120
+
3121
+ function normalizeSources(input) {
3122
+ const list = Array.isArray(input) ? input : [input];
3123
+ return list
3124
+ .map((source) => (typeof source === "string" ? { src: source } : source))
3125
+ .filter((source) => Boolean(source.src));
3126
+ }
3127
+ class VideoPlayerController {
3128
+ constructor(options) {
3129
+ this.state = this.normalize(options);
3130
+ }
3131
+ updateOptions(options) {
3132
+ this.state = this.normalize({ ...this.state, ...options, sources: options.sources ?? this.state.sources });
3133
+ return this.getState();
3134
+ }
3135
+ getState() {
3136
+ return { ...this.state, sources: [...this.state.sources] };
3137
+ }
3138
+ normalize(options) {
3139
+ return {
3140
+ autoplay: options.autoplay ?? false,
3141
+ controls: options.controls ?? true,
3142
+ loop: options.loop ?? false,
3143
+ muted: options.muted ?? false,
3144
+ poster: options.poster,
3145
+ preload: options.preload ?? "metadata",
3146
+ sources: normalizeSources(options.sources)
3147
+ };
3148
+ }
3149
+ }
3150
+
3151
+ function cloneItem(item) {
3152
+ return {
3153
+ ...item,
3154
+ sources: Array.isArray(item.sources) ? [...item.sources] : item.sources
3155
+ };
3156
+ }
3157
+ function normalizeRect(rect) {
3158
+ if (!rect)
3159
+ return undefined;
3160
+ return {
3161
+ height: Math.max(1, rect.height),
3162
+ left: Number.isFinite(rect.left) ? rect.left : 0,
3163
+ top: Number.isFinite(rect.top) ? rect.top : 0,
3164
+ width: Math.max(1, rect.width)
3165
+ };
3166
+ }
3167
+ class VideoDetailTransitionController {
3168
+ constructor(options) {
3169
+ this.state = this.normalize(options);
3170
+ }
3171
+ updateOptions(options) {
3172
+ this.state = this.normalize({ ...this.state, ...options, items: options.items ?? this.state.items });
3173
+ return this.getState();
3174
+ }
3175
+ open(id, sourceRect) {
3176
+ const activeItem = this.state.items.find((item) => item.id === id);
3177
+ this.state = {
3178
+ ...this.state,
3179
+ activeId: activeItem?.id,
3180
+ activeItem,
3181
+ phase: activeItem ? "entering" : "closed",
3182
+ sourceRect: normalizeRect(sourceRect)
3183
+ };
3184
+ return this.getState();
3185
+ }
3186
+ entered() {
3187
+ if (!this.state.activeItem)
3188
+ return this.closeNow();
3189
+ this.state = { ...this.state, phase: "open" };
3190
+ return this.getState();
3191
+ }
3192
+ close() {
3193
+ if (!this.state.activeItem)
3194
+ return this.closeNow();
3195
+ this.state = { ...this.state, phase: "leaving" };
3196
+ return this.getState();
3197
+ }
3198
+ closeNow() {
3199
+ this.state = { ...this.state, activeId: undefined, activeItem: undefined, phase: "closed", sourceRect: undefined };
3200
+ return this.getState();
3201
+ }
3202
+ getState() {
3203
+ return {
3204
+ ...this.state,
3205
+ activeItem: this.state.activeItem ? cloneItem(this.state.activeItem) : undefined,
3206
+ items: this.state.items.map(cloneItem),
3207
+ sourceRect: this.state.sourceRect ? { ...this.state.sourceRect } : undefined
3208
+ };
3209
+ }
3210
+ normalize(options) {
3211
+ const items = options.items.map(cloneItem);
3212
+ const activeItem = options.activeId ? items.find((item) => item.id === options.activeId) : undefined;
3213
+ return {
3214
+ activeId: activeItem?.id,
3215
+ activeItem,
3216
+ animationDuration: Math.max(120, Math.min(900, options.animationDuration ?? 420)),
3217
+ backdropColor: options.backdropColor ?? "rgba(0, 0, 0, 0.96)",
3218
+ items,
3219
+ phase: activeItem ? options.phase ?? "open" : "closed",
3220
+ sourceRect: normalizeRect(options.sourceRect)
3221
+ };
3222
+ }
3223
+ }
3224
+
3225
+ const WORD_EXTENSIONS = new Set(["doc", "docx", "rtf"]);
3226
+ const EXCEL_EXTENSIONS = new Set(["csv", "xls", "xlsx"]);
3227
+ const PPT_EXTENSIONS = new Set(["ppt", "pptx"]);
3228
+ const DEFAULT_OFFICE_VIEWER = "https://view.officeapps.live.com/op/embed.aspx?src=";
3229
+ function clamp$3(value, min, max) {
3230
+ return Math.min(max, Math.max(min, value));
3231
+ }
3232
+ function getExtension(value) {
3233
+ const clean = value.split("?")[0]?.split("#")[0] ?? value;
3234
+ const filename = clean.split("/").pop() ?? clean;
3235
+ const extension = filename.includes(".") ? filename.split(".").pop() ?? "" : "";
3236
+ return extension.toLowerCase();
3237
+ }
3238
+ function inferType(src, fileName) {
3239
+ const extension = getExtension(fileName || src);
3240
+ if (extension === "pdf")
3241
+ return "pdf";
3242
+ if (WORD_EXTENSIONS.has(extension))
3243
+ return "word";
3244
+ if (EXCEL_EXTENSIONS.has(extension))
3245
+ return "excel";
3246
+ if (PPT_EXTENSIONS.has(extension))
3247
+ return "ppt";
3248
+ return "unknown";
3249
+ }
3250
+ function inferFileName(src, fileName) {
3251
+ if (fileName)
3252
+ return fileName;
3253
+ try {
3254
+ const url = new URL(src, "https://class-kit.local");
3255
+ const last = url.pathname.split("/").filter(Boolean).pop();
3256
+ return last ? decodeURIComponent(last) : "Document";
3257
+ }
3258
+ catch {
3259
+ const last = src.split("?")[0]?.split("#")[0]?.split("/").filter(Boolean).pop();
3260
+ return last ? decodeURIComponent(last) : "Document";
3261
+ }
3262
+ }
3263
+ function buildPdfUrl(src, page) {
3264
+ const clean = src.split("#")[0] ?? src;
3265
+ return `${clean}#page=${page}`;
3266
+ }
3267
+ function buildOfficeUrl(src, viewerBaseUrl) {
3268
+ const base = viewerBaseUrl || DEFAULT_OFFICE_VIEWER;
3269
+ return `${base}${encodeURIComponent(src)}`;
3270
+ }
3271
+ class FilePreviewController {
3272
+ constructor(options) {
3273
+ this.options = { ...options };
3274
+ }
3275
+ updateOptions(options) {
3276
+ this.options = { ...this.options, ...options };
3277
+ return this.getState();
3278
+ }
3279
+ goTo(page) {
3280
+ this.options.currentPage = page;
3281
+ return this.getState();
3282
+ }
3283
+ next() {
3284
+ return this.goTo(this.getState().currentPage + 1);
3285
+ }
3286
+ previous() {
3287
+ return this.goTo(this.getState().currentPage - 1);
3288
+ }
3289
+ getState() {
3290
+ const fileName = inferFileName(this.options.src, this.options.fileName);
3291
+ const type = this.options.type && this.options.type !== "unknown" ? this.options.type : inferType(this.options.src, fileName);
3292
+ const pageCount = this.options.pageCount && this.options.pageCount > 0 ? Math.floor(this.options.pageCount) : undefined;
3293
+ const currentPage = clamp$3(Math.floor(this.options.currentPage ?? 1), 1, pageCount ?? Number.MAX_SAFE_INTEGER);
3294
+ const mode = type === "pdf" ? "pdf" : type === "word" || type === "excel" || type === "ppt" ? "office" : "unsupported";
3295
+ const previewUrl = mode === "pdf"
3296
+ ? buildPdfUrl(this.options.src, currentPage)
3297
+ : mode === "office"
3298
+ ? buildOfficeUrl(this.options.src, this.options.viewerBaseUrl)
3299
+ : "";
3300
+ return {
3301
+ currentPage,
3302
+ fileName,
3303
+ mode,
3304
+ pageCount,
3305
+ previewUrl,
3306
+ src: this.options.src,
3307
+ title: fileName,
3308
+ type
3309
+ };
3310
+ }
3311
+ }
3312
+
3313
+ function clamp$2(value, min, max) {
3314
+ return Math.min(max, Math.max(min, value));
3315
+ }
3316
+ class ProgressController {
3317
+ constructor(options = {}) {
3318
+ this.min = options.min ?? 0;
3319
+ this.max = Math.max(this.min, options.max ?? 100);
3320
+ this.step = Math.max(0, options.step ?? 1);
3321
+ this.disabled = options.disabled ?? false;
3322
+ this.value = this.normalizeValue(options.value ?? this.min);
3323
+ }
3324
+ updateOptions(options) {
3325
+ if (options.min !== undefined)
3326
+ this.min = options.min;
3327
+ if (options.max !== undefined)
3328
+ this.max = Math.max(this.min, options.max);
3329
+ if (options.step !== undefined)
3330
+ this.step = Math.max(0, options.step);
3331
+ if (options.disabled !== undefined)
3332
+ this.disabled = options.disabled;
3333
+ if (options.value !== undefined)
3334
+ this.value = this.normalizeValue(options.value);
3335
+ return this.getState();
3336
+ }
3337
+ setValue(value) {
3338
+ if (!this.disabled)
3339
+ this.value = this.normalizeValue(value);
3340
+ return this.getState();
3341
+ }
3342
+ valueFromRatio(ratio) {
3343
+ return this.setValue(this.min + clamp$2(ratio, 0, 1) * (this.max - this.min));
3344
+ }
3345
+ valueFromClientX(clientX, rect, thumbWidth = 0) {
3346
+ if (rect.width <= 0)
3347
+ return this.getState();
3348
+ const travelWidth = Math.max(1, rect.width - Math.max(0, thumbWidth));
3349
+ const offset = clientX - rect.left - Math.max(0, thumbWidth) / 2;
3350
+ return this.valueFromRatio(offset / travelWidth);
3351
+ }
3352
+ getState() {
3353
+ const range = this.max - this.min;
3354
+ return {
3355
+ disabled: this.disabled,
3356
+ max: this.max,
3357
+ min: this.min,
3358
+ percent: range <= 0 ? 0 : ((this.value - this.min) / range) * 100,
3359
+ step: this.step,
3360
+ value: this.value
3361
+ };
3362
+ }
3363
+ normalizeValue(value) {
3364
+ const clamped = clamp$2(Number.isFinite(value) ? value : this.min, this.min, this.max);
3365
+ if (this.step <= 0)
3366
+ return clamped;
3367
+ const precision = Math.max(0, String(this.step).split(".")[1]?.length ?? 0);
3368
+ return Number((Math.round((clamped - this.min) / this.step) * this.step + this.min).toFixed(precision));
3369
+ }
3370
+ }
3371
+
3372
+ function clamp$1(value, min, max) {
3373
+ return Math.min(max, Math.max(min, value));
3374
+ }
3375
+ function normalizeLineWidth(value) {
3376
+ return clamp$1(Number.isFinite(value ?? NaN) ? Number(value) : 3, 0.5, 24);
3377
+ }
3378
+ function normalizePoint(x, y, size, pressure = 0.5) {
3379
+ const width = Math.max(1, size.width);
3380
+ const height = Math.max(1, size.height);
3381
+ return {
3382
+ pressure: clamp$1(Number.isFinite(pressure) ? pressure : 0.5, 0, 1),
3383
+ time: Date.now(),
3384
+ x: clamp$1(x / width, 0, 1),
3385
+ y: clamp$1(y / height, 0, 1)
3386
+ };
3387
+ }
3388
+ class SignatureController {
3389
+ constructor(options = {}) {
3390
+ this.isDrawing = false;
3391
+ this.strokes = [];
3392
+ this.disabled = options.disabled ?? false;
3393
+ this.lineWidth = normalizeLineWidth(options.lineWidth);
3394
+ this.penColor = options.penColor ?? "#111827";
3395
+ this.readOnly = options.readOnly ?? false;
3396
+ }
3397
+ updateOptions(options = {}) {
3398
+ if (options.disabled !== undefined)
3399
+ this.disabled = options.disabled;
3400
+ if (options.lineWidth !== undefined)
3401
+ this.lineWidth = normalizeLineWidth(options.lineWidth);
3402
+ if (options.penColor !== undefined)
3403
+ this.penColor = options.penColor;
3404
+ if (options.readOnly !== undefined)
3405
+ this.readOnly = options.readOnly;
3406
+ return this.getState();
3407
+ }
3408
+ beginStroke(x, y, size, pressure = 0.5) {
3409
+ if (this.disabled || this.readOnly)
3410
+ return this.getState();
3411
+ this.isDrawing = true;
3412
+ this.strokes.push({
3413
+ color: this.penColor,
3414
+ lineWidth: this.lineWidth,
3415
+ points: [normalizePoint(x, y, size, pressure)]
3416
+ });
3417
+ return this.getState();
3418
+ }
3419
+ addPoint(x, y, size, pressure = 0.5) {
3420
+ if (!this.isDrawing || this.disabled || this.readOnly)
3421
+ return this.getState();
3422
+ const stroke = this.strokes[this.strokes.length - 1];
3423
+ if (!stroke)
3424
+ return this.getState();
3425
+ stroke.points.push(normalizePoint(x, y, size, pressure));
3426
+ return this.getState();
3427
+ }
3428
+ endStroke() {
3429
+ this.isDrawing = false;
3430
+ const stroke = this.strokes[this.strokes.length - 1];
3431
+ if (stroke && stroke.points.length === 0)
3432
+ this.strokes.pop();
3433
+ return this.getState();
3434
+ }
3435
+ clear() {
3436
+ this.isDrawing = false;
3437
+ this.strokes = [];
3438
+ return this.getState();
3439
+ }
3440
+ undo() {
3441
+ if (!this.disabled && !this.readOnly)
3442
+ this.strokes.pop();
3443
+ return this.getState();
3444
+ }
3445
+ replaceStrokes(strokes = []) {
3446
+ this.strokes = strokes.map((stroke) => ({
3447
+ color: stroke.color,
3448
+ lineWidth: normalizeLineWidth(stroke.lineWidth),
3449
+ points: stroke.points.map((point) => ({
3450
+ pressure: clamp$1(point.pressure, 0, 1),
3451
+ time: point.time,
3452
+ x: clamp$1(point.x, 0, 1),
3453
+ y: clamp$1(point.y, 0, 1)
3454
+ }))
3455
+ }));
3456
+ this.isDrawing = false;
3457
+ return this.getState();
3458
+ }
3459
+ getState() {
3460
+ return {
3461
+ disabled: this.disabled,
3462
+ isDrawing: this.isDrawing,
3463
+ isEmpty: this.strokes.length === 0,
3464
+ lineWidth: this.lineWidth,
3465
+ penColor: this.penColor,
3466
+ readOnly: this.readOnly,
3467
+ strokeCount: this.strokes.length,
3468
+ strokes: this.strokes.map((stroke) => ({
3469
+ color: stroke.color,
3470
+ lineWidth: stroke.lineWidth,
3471
+ points: stroke.points.map((point) => ({ ...point }))
3472
+ }))
3473
+ };
3474
+ }
3475
+ }
3476
+
3477
+ const DEFAULT_MIN_WIDTH = 32;
3478
+ const DEFAULT_MAX_WIDTH = 1000;
3479
+ function normalizeMinWidth(width) {
3480
+ return Math.max(DEFAULT_MIN_WIDTH, Math.floor(Number.isFinite(width) ? Number(width) : DEFAULT_MIN_WIDTH));
3481
+ }
3482
+ function normalizeMaxWidth(width, minWidth = DEFAULT_MIN_WIDTH) {
3483
+ return Math.max(minWidth, Math.min(DEFAULT_MAX_WIDTH, Math.floor(Number.isFinite(width) ? Number(width) : DEFAULT_MAX_WIDTH)));
3484
+ }
3485
+ function clampWidth(width, minWidth, maxWidth, fallback = minWidth) {
3486
+ const next = Number.isFinite(width) ? Number(width) : fallback;
3487
+ return Math.max(minWidth, Math.min(maxWidth, Math.round(next)));
3488
+ }
3489
+ class TableController {
3490
+ constructor(options = {}) {
3491
+ this.options = this.normalize(options);
3492
+ }
3493
+ update(options = {}) {
3494
+ this.options = this.normalize({ ...this.options, ...options });
3495
+ return this.getState();
3496
+ }
3497
+ resizeColumn(key, delta, containerWidth = this.options.containerWidth) {
3498
+ if (!Number.isFinite(delta) || delta === 0)
3499
+ return this.getState();
3500
+ const columns = this.getNormalizedColumns();
3501
+ const availableContainerWidth = this.getColumnContainerWidth(containerWidth);
3502
+ const visibleIndexes = columns.map((column, index) => column.hidden ? -1 : index).filter((index) => index >= 0);
3503
+ const targetIndex = columns.findIndex((column) => column.key === key && !column.hidden);
3504
+ if (targetIndex < 0)
3505
+ return this.getState();
3506
+ const target = columns[targetIndex];
3507
+ if (!target)
3508
+ return this.getState();
3509
+ const currentTotal = visibleIndexes.reduce((sum, index) => sum + (columns[index]?.width ?? 0), 0);
3510
+ let desired = delta;
3511
+ if (desired > 0) {
3512
+ const targetGrow = target.maxWidth - target.width;
3513
+ const fillGap = Math.max(0, availableContainerWidth - currentTotal);
3514
+ const growWithoutShrink = Math.min(desired, targetGrow, fillGap);
3515
+ target.width += growWithoutShrink;
3516
+ desired -= growWithoutShrink;
3517
+ if (desired > 0) {
3518
+ const shrinkableIndexes = visibleIndexes.filter((index) => index !== targetIndex && index > targetIndex);
3519
+ const fallbackIndexes = visibleIndexes.filter((index) => index !== targetIndex && index < targetIndex).reverse();
3520
+ const shrinkable = [...shrinkableIndexes, ...fallbackIndexes];
3521
+ let shrinkNeed = Math.min(desired, target.maxWidth - target.width);
3522
+ for (const index of shrinkable) {
3523
+ if (shrinkNeed <= 0)
3524
+ break;
3525
+ const column = columns[index];
3526
+ if (!column)
3527
+ continue;
3528
+ const amount = Math.min(shrinkNeed, column.width - column.minWidth);
3529
+ column.width -= amount;
3530
+ shrinkNeed -= amount;
3531
+ target.width += amount;
3532
+ }
3533
+ }
3534
+ }
3535
+ else {
3536
+ const shrink = Math.min(Math.abs(desired), target.width - target.minWidth);
3537
+ target.width -= shrink;
3538
+ }
3539
+ target.autoWidth = false;
3540
+ this.options.columns = columns;
3541
+ return this.getState();
3542
+ }
3543
+ toggleColumn(key, visible, containerWidth = this.options.containerWidth) {
3544
+ const columns = this.options.columns;
3545
+ const target = columns.find((column) => column.key === key);
3546
+ if (!target)
3547
+ return this.getState();
3548
+ const nextHidden = visible === undefined ? !target.hidden : !visible;
3549
+ const visibleCount = columns.filter((column) => !column.hidden).length;
3550
+ if (nextHidden && !target.hidden && visibleCount <= 1)
3551
+ return this.getState();
3552
+ this.options.containerWidth = this.getColumnContainerWidth(containerWidth);
3553
+ this.options.columns = columns.map((column) => column.key === key ? { ...column, hidden: nextHidden } : column);
3554
+ return this.getState();
3555
+ }
3556
+ toggleRow(key, checked) {
3557
+ const set = new Set(this.options.selectedKeys);
3558
+ const nextChecked = checked ?? !set.has(key);
3559
+ if (nextChecked)
3560
+ set.add(key);
3561
+ else
3562
+ set.delete(key);
3563
+ this.options.selectedKeys = Array.from(set);
3564
+ return this.getState();
3565
+ }
3566
+ toggleAllRows(checked) {
3567
+ const keys = this.options.rows.map((row, index) => this.getRowKey(row, index));
3568
+ const selected = new Set(this.options.selectedKeys);
3569
+ const nextChecked = checked ?? this.getSelectionStatus(keys) !== "all";
3570
+ keys.forEach((key) => {
3571
+ if (nextChecked)
3572
+ selected.add(key);
3573
+ else
3574
+ selected.delete(key);
3575
+ });
3576
+ this.options.selectedKeys = Array.from(selected);
3577
+ return this.getState();
3578
+ }
3579
+ getRowKey(row, index) {
3580
+ return this.options.getRowKey(row, index);
3581
+ }
3582
+ getState() {
3583
+ const allColumns = this.getNormalizedColumns();
3584
+ const columns = allColumns.filter((column) => !column.hidden);
3585
+ const rowKeys = this.options.rows.map((row, index) => this.getRowKey(row, index));
3586
+ return {
3587
+ allColumns: allColumns.map((column) => this.toPublicColumn(column)),
3588
+ columns: columns.map((column) => this.toPublicColumn(column)),
3589
+ rows: [...this.options.rows],
3590
+ selectionStatus: this.getSelectionStatus(rowKeys),
3591
+ selectedKeys: [...this.options.selectedKeys],
3592
+ tableWidth: columns.reduce((sum, column) => sum + column.width, 0)
3593
+ };
3594
+ }
3595
+ normalize(options) {
3596
+ return {
3597
+ columns: (options.columns ?? []).map((column) => this.normalizeColumn(column)),
3598
+ containerWidth: Math.max(0, Number.isFinite(options.containerWidth) ? Number(options.containerWidth) : 0),
3599
+ getRowKey: options.getRowKey ?? ((_, index) => index),
3600
+ rows: [...(options.rows ?? [])],
3601
+ selectedKeys: [...(options.selectedKeys ?? [])]
3602
+ };
3603
+ }
3604
+ normalizeColumn(column) {
3605
+ const minWidth = normalizeMinWidth(column.minWidth);
3606
+ const maxWidth = normalizeMaxWidth(column.maxWidth, minWidth);
3607
+ const explicitWidth = Number.isFinite(column.width) || Number.isFinite(column.defaultWidth);
3608
+ return {
3609
+ ...column,
3610
+ autoWidth: "autoWidth" in column ? Boolean(column.autoWidth) : !explicitWidth,
3611
+ fixed: column.fixed === "left" || column.fixed === "right" ? column.fixed : undefined,
3612
+ hidden: Boolean(column.hidden),
3613
+ maxWidth,
3614
+ minWidth,
3615
+ width: clampWidth(column.width ?? column.defaultWidth, minWidth, maxWidth)
3616
+ };
3617
+ }
3618
+ getNormalizedColumns() {
3619
+ const columns = this.options.columns.map((column) => this.normalizeColumn(column));
3620
+ const visibleCount = columns.filter((column) => !column.hidden).length;
3621
+ const safeColumns = visibleCount > 0 && columns.length > 0
3622
+ ? columns
3623
+ : columns.map((column, index) => index === 0 ? { ...column, hidden: false } : column);
3624
+ return this.distributeDefaultWidths(safeColumns);
3625
+ }
3626
+ distributeDefaultWidths(columns) {
3627
+ const containerWidth = this.getColumnContainerWidth();
3628
+ if (containerWidth <= 0)
3629
+ return columns;
3630
+ const visible = columns.filter((column) => !column.hidden);
3631
+ const flexible = visible.filter((column) => column.autoWidth);
3632
+ if (flexible.length === 0)
3633
+ return this.ensureContainerFill(this.fillContainerGap(columns, containerWidth), containerWidth);
3634
+ const fixedTotal = visible
3635
+ .filter((column) => !flexible.includes(column))
3636
+ .reduce((sum, column) => sum + column.width, 0);
3637
+ const remaining = Math.max(0, containerWidth - fixedTotal);
3638
+ const base = remaining / flexible.length;
3639
+ let used = 0;
3640
+ const next = columns.map((column) => {
3641
+ if (column.hidden || !flexible.some((item) => item.key === column.key))
3642
+ return column;
3643
+ const width = clampWidth(base, column.minWidth, column.maxWidth, column.minWidth);
3644
+ used += width;
3645
+ return { ...column, width };
3646
+ });
3647
+ return this.ensureContainerFill(this.fillContainerGap(next, containerWidth, fixedTotal + used), containerWidth);
3648
+ }
3649
+ fillContainerGap(columns, containerWidth, knownTotal) {
3650
+ const next = columns.map((column) => ({ ...column }));
3651
+ const total = knownTotal ?? next.filter((column) => !column.hidden).reduce((sum, column) => sum + column.width, 0);
3652
+ let gap = containerWidth - total;
3653
+ if (gap === 0)
3654
+ return next;
3655
+ const visibleIndexes = next
3656
+ .map((column, index) => column.hidden ? -1 : index)
3657
+ .filter((index) => index >= 0)
3658
+ .reverse();
3659
+ if (gap > 0 && visibleIndexes.length > 0) {
3660
+ const base = Math.floor(gap / visibleIndexes.length);
3661
+ let remainder = gap - base * visibleIndexes.length;
3662
+ for (const index of visibleIndexes) {
3663
+ const column = next[index];
3664
+ if (!column)
3665
+ continue;
3666
+ const extra = base + (remainder > 0 ? 1 : 0);
3667
+ column.width += extra;
3668
+ remainder -= remainder > 0 ? 1 : 0;
3669
+ }
3670
+ return next;
3671
+ }
3672
+ for (const index of visibleIndexes) {
3673
+ if (gap === 0)
3674
+ break;
3675
+ const column = next[index];
3676
+ if (!column)
3677
+ continue;
3678
+ const delta = Math.min(Math.abs(gap), column.width - column.minWidth);
3679
+ column.width -= delta;
3680
+ gap += delta;
3681
+ }
3682
+ return next;
3683
+ }
3684
+ ensureContainerFill(columns, containerWidth) {
3685
+ if (containerWidth <= 0)
3686
+ return columns;
3687
+ const visibleIndexes = columns.map((column, index) => column.hidden ? -1 : index).filter((index) => index >= 0);
3688
+ if (visibleIndexes.length === 0)
3689
+ return columns;
3690
+ const total = visibleIndexes.reduce((sum, index) => sum + (columns[index]?.width ?? 0), 0);
3691
+ const gap = Math.ceil(containerWidth - total);
3692
+ if (gap <= 0)
3693
+ return columns;
3694
+ const next = columns.map((column) => ({ ...column }));
3695
+ const base = Math.floor(gap / visibleIndexes.length);
3696
+ let remainder = gap - base * visibleIndexes.length;
3697
+ for (const index of visibleIndexes) {
3698
+ const column = next[index];
3699
+ if (!column)
3700
+ continue;
3701
+ const extra = base + (remainder > 0 ? 1 : 0);
3702
+ column.width += extra;
3703
+ remainder -= remainder > 0 ? 1 : 0;
3704
+ }
3705
+ return next;
3706
+ }
3707
+ getColumnContainerWidth(containerWidth = this.options.containerWidth) {
3708
+ return Math.max(0, containerWidth);
3709
+ }
3710
+ toPublicColumn(column) {
3711
+ const publicColumn = { ...column };
3712
+ delete publicColumn.autoWidth;
3713
+ return publicColumn;
3714
+ }
3715
+ getSelectionStatus(keys) {
3716
+ if (keys.length === 0)
3717
+ return "none";
3718
+ const selected = new Set(this.options.selectedKeys);
3719
+ const selectedCount = keys.filter((key) => selected.has(key)).length;
3720
+ if (selectedCount === 0)
3721
+ return "none";
3722
+ if (selectedCount === keys.length)
3723
+ return "all";
3724
+ return "partial";
3725
+ }
3726
+ }
3727
+
3728
+ class UploadController {
3729
+ constructor(options = {}) {
3730
+ this.files = [];
3731
+ this.options = options;
3732
+ }
3733
+ updateOptions(options = {}) {
3734
+ this.options = options;
3735
+ return this.getState();
3736
+ }
3737
+ addFiles(files) {
3738
+ const next = files
3739
+ .filter((file) => !this.options.maxSize || file.size <= this.options.maxSize)
3740
+ .map((file) => ({ file, id: `${file.name}-${file.size}-${file.lastModified}-${Math.random().toString(36).slice(2)}`, name: file.name, size: file.size, status: "ready" }));
3741
+ this.files = this.options.multiple ? [...this.files, ...next] : next.slice(0, 1);
3742
+ return this.getState();
3743
+ }
3744
+ remove(id) {
3745
+ this.files = this.files.filter((file) => file.id !== id);
3746
+ return this.getState();
3747
+ }
3748
+ replaceFiles(files = []) {
3749
+ this.files = this.options.multiple ? files.map((file) => ({ ...file })) : files.slice(0, 1).map((file) => ({ ...file }));
3750
+ return this.getState();
3751
+ }
3752
+ clear() {
3753
+ this.files = [];
3754
+ return this.getState();
3755
+ }
3756
+ getState() {
3757
+ return { files: this.files.map((file) => ({ ...file })) };
3758
+ }
3759
+ }
3760
+
3761
+ function now() {
3762
+ return Date.now();
3763
+ }
3764
+ function resolveDirection(deltaX, deltaY, threshold) {
3765
+ if (Math.abs(deltaX) < threshold && Math.abs(deltaY) < threshold)
3766
+ return "none";
3767
+ if (Math.abs(deltaX) > Math.abs(deltaY))
3768
+ return deltaX > 0 ? "right" : "left";
3769
+ return deltaY > 0 ? "down" : "up";
3770
+ }
3771
+ class GestureController {
3772
+ constructor(options = {}) {
3773
+ this.current = { x: 0, y: 0, time: 0 };
3774
+ this.previous = { x: 0, y: 0, time: 0 };
3775
+ this.startPoint = { x: 0, y: 0, time: 0 };
3776
+ this.active = false;
3777
+ this.threshold = Math.max(0, options.threshold ?? 4);
3778
+ }
3779
+ start(point) {
3780
+ const time = point.time ?? now();
3781
+ this.active = true;
3782
+ this.startPoint = { ...point, time };
3783
+ this.previous = this.startPoint;
3784
+ this.current = this.startPoint;
3785
+ return this.getState();
3786
+ }
3787
+ move(point) {
3788
+ if (!this.active)
3789
+ return this.getState();
3790
+ this.previous = this.current;
3791
+ this.current = { ...point, time: point.time ?? now() };
3792
+ return this.getState();
3793
+ }
3794
+ end(point) {
3795
+ if (point)
3796
+ this.move(point);
3797
+ const state = this.getState();
3798
+ this.active = false;
3799
+ return { ...state, active: false };
3800
+ }
3801
+ getState() {
3802
+ const deltaX = this.current.x - this.startPoint.x;
3803
+ const deltaY = this.current.y - this.startPoint.y;
3804
+ const frameTime = Math.max(1, (this.current.time ?? 0) - (this.previous.time ?? 0));
3805
+ return {
3806
+ active: this.active,
3807
+ direction: resolveDirection(deltaX, deltaY, this.threshold),
3808
+ distance: Math.hypot(deltaX, deltaY),
3809
+ duration: Math.max(0, (this.current.time ?? 0) - (this.startPoint.time ?? 0)),
3810
+ startX: this.startPoint.x,
3811
+ startY: this.startPoint.y,
3812
+ velocityX: ((this.current.x - this.previous.x) / frameTime) * 1000,
3813
+ velocityY: ((this.current.y - this.previous.y) / frameTime) * 1000,
3814
+ x: deltaX,
3815
+ y: deltaY
3816
+ };
3817
+ }
3818
+ }
3819
+
3820
+ function clamp(value, min, max) {
3821
+ return Math.min(max, Math.max(min, value));
3822
+ }
3823
+ class DragController {
3824
+ constructor(options = {}) {
3825
+ this.dragOffset = { x: 0, y: 0 };
3826
+ this.axis = options.axis ?? "xy";
3827
+ this.bounds = options.bounds;
3828
+ this.disabled = options.disabled ?? false;
3829
+ this.position = options.initialPosition ?? { x: 0, y: 0 };
3830
+ this.restrictToBounds = options.restrictToBounds ?? true;
3831
+ this.gesture = new GestureController({ threshold: options.threshold ?? 2 });
3832
+ }
3833
+ updateOptions(options) {
3834
+ if (options.axis !== undefined)
3835
+ this.axis = options.axis;
3836
+ if (options.bounds !== undefined)
3837
+ this.bounds = options.bounds;
3838
+ if (options.disabled !== undefined)
3839
+ this.disabled = options.disabled;
3840
+ if (options.initialPosition !== undefined && !this.getState().dragging)
3841
+ this.position = options.initialPosition;
3842
+ if (options.restrictToBounds !== undefined)
3843
+ this.restrictToBounds = options.restrictToBounds;
3844
+ return this.getState();
3845
+ }
3846
+ start(point, elementPosition = this.position) {
3847
+ if (this.disabled)
3848
+ return this.getState();
3849
+ this.position = this.clampPosition(elementPosition);
3850
+ this.dragOffset = { x: point.x - this.position.x, y: point.y - this.position.y };
3851
+ this.gesture.start(point);
3852
+ return this.getState();
3853
+ }
3854
+ move(point) {
3855
+ if (!this.gesture.getState().active || this.disabled)
3856
+ return this.getState();
3857
+ this.gesture.move(point);
3858
+ const nextPosition = {
3859
+ x: this.axis === "y" ? this.position.x : point.x - this.dragOffset.x,
3860
+ y: this.axis === "x" ? this.position.y : point.y - this.dragOffset.y
3861
+ };
3862
+ this.position = this.clampPosition(nextPosition);
3863
+ return this.getState();
3864
+ }
3865
+ end(point) {
3866
+ if (point)
3867
+ this.move(point);
3868
+ this.gesture.end();
3869
+ return this.getState();
3870
+ }
3871
+ setPosition(position) {
3872
+ this.position = this.clampPosition(position);
3873
+ return this.getState();
3874
+ }
3875
+ isOverTarget(source, target) {
3876
+ if (!target)
3877
+ return false;
3878
+ return source.x < target.x + target.width
3879
+ && source.x + source.width > target.x
3880
+ && source.y < target.y + target.height
3881
+ && source.y + source.height > target.y;
3882
+ }
3883
+ getState() {
3884
+ return {
3885
+ dragging: this.gesture.getState().active,
3886
+ gesture: this.gesture.getState(),
3887
+ position: this.position
3888
+ };
3889
+ }
3890
+ clampPosition(position) {
3891
+ if (!this.bounds || !this.restrictToBounds)
3892
+ return position;
3893
+ return {
3894
+ x: clamp(position.x, 0, Math.max(0, this.bounds.width)),
3895
+ y: clamp(position.y, 0, Math.max(0, this.bounds.height))
3896
+ };
3897
+ }
3898
+ }
3899
+
3900
+ class DropController {
3901
+ constructor(items = []) {
3902
+ this.items = items;
3903
+ }
3904
+ updateItems(items) {
3905
+ this.items = items;
3906
+ return this.getState();
3907
+ }
3908
+ start(id) {
3909
+ const item = this.items.find((entry) => entry.id === id);
3910
+ if (!item || item.disabled)
3911
+ return this.getState();
3912
+ this.draggingId = id;
3913
+ this.overIndex = this.items.findIndex((entry) => entry.id === id);
3914
+ return this.getState();
3915
+ }
3916
+ moveToIndex(index) {
3917
+ if (!this.draggingId)
3918
+ return this.getState();
3919
+ this.overIndex = Math.max(0, Math.min(index, this.items.length - 1));
3920
+ return this.getState();
3921
+ }
3922
+ end() {
3923
+ if (!this.draggingId || this.overIndex === undefined) {
3924
+ this.draggingId = undefined;
3925
+ this.overIndex = undefined;
3926
+ return this.getState();
3927
+ }
3928
+ const fromIndex = this.items.findIndex((item) => item.id === this.draggingId);
3929
+ const toIndex = this.overIndex;
3930
+ if (fromIndex >= 0 && toIndex >= 0 && fromIndex !== toIndex) {
3931
+ const next = [...this.items];
3932
+ const [moving] = next.splice(fromIndex, 1);
3933
+ if (moving)
3934
+ next.splice(toIndex, 0, moving);
3935
+ this.items = next;
3936
+ }
3937
+ this.draggingId = undefined;
3938
+ this.overIndex = undefined;
3939
+ return this.getState();
3940
+ }
3941
+ cancel() {
3942
+ this.draggingId = undefined;
3943
+ this.overIndex = undefined;
3944
+ return this.getState();
3945
+ }
3946
+ getState() {
3947
+ return {
3948
+ draggingId: this.draggingId,
3949
+ items: this.items,
3950
+ overIndex: this.overIndex
3951
+ };
3952
+ }
3953
+ }
3954
+
3955
+ class ComicReaderController {
3956
+ constructor(options) {
3957
+ this.pages = [...options.pages];
3958
+ this.current = this.clamp(options.current ?? 0);
3959
+ this.hasMore = options.hasMore ?? false;
3960
+ this.loading = options.loading ?? false;
3961
+ }
3962
+ update(options) {
3963
+ if (options.pages)
3964
+ this.pages = [...options.pages];
3965
+ if (typeof options.current === "number")
3966
+ this.current = this.clamp(options.current);
3967
+ else
3968
+ this.current = this.clamp(this.current);
3969
+ if (typeof options.hasMore === "boolean")
3970
+ this.hasMore = options.hasMore;
3971
+ if (typeof options.loading === "boolean")
3972
+ this.loading = options.loading;
3973
+ return this.getState();
3974
+ }
3975
+ append(pages, hasMore = this.hasMore) {
3976
+ const existed = new Set(this.pages.map((item) => item.id));
3977
+ this.pages = [...this.pages, ...pages.filter((item) => !existed.has(item.id))];
3978
+ this.hasMore = hasMore;
3979
+ this.loading = false;
3980
+ return this.getState();
3981
+ }
3982
+ setLoading(loading) {
3983
+ this.loading = loading;
3984
+ return this.getState();
3985
+ }
3986
+ next() {
3987
+ this.current = this.clamp(this.current + 1);
3988
+ return this.getState();
3989
+ }
3990
+ previous() {
3991
+ this.current = this.clamp(this.current - 1);
3992
+ return this.getState();
3993
+ }
3994
+ goTo(index) {
3995
+ this.current = this.clamp(index);
3996
+ return this.getState();
3997
+ }
3998
+ getState() {
3999
+ const total = this.pages.length;
4000
+ return {
4001
+ canNext: this.current < total - 1,
4002
+ canPrevious: this.current > 0,
4003
+ current: this.current,
4004
+ hasMore: this.hasMore,
4005
+ loading: this.loading,
4006
+ page: this.pages[this.current] ?? null,
4007
+ pages: [...this.pages],
4008
+ total
4009
+ };
4010
+ }
4011
+ clamp(index) {
4012
+ if (this.pages.length === 0)
4013
+ return 0;
4014
+ return Math.min(Math.max(0, Math.trunc(index)), this.pages.length - 1);
4015
+ }
4016
+ }
4017
+
4018
+ class DanmakuController {
4019
+ constructor(items = [], options = {}) {
4020
+ this.trackCursor = 0;
4021
+ this.maxTracks = Math.max(1, Math.trunc(options.maxTracks ?? 6));
4022
+ this.defaultDuration = Math.max(1000, options.defaultDuration ?? 7600);
4023
+ this.defaultSpeed = this.normalizeSpeed(options.speed);
4024
+ this.defaultColor = options.color ?? "#ffffff";
4025
+ this.enabled = options.enabled ?? true;
4026
+ this.items = items.map((item) => this.normalize(item));
4027
+ }
4028
+ update(items, options = {}) {
4029
+ this.maxTracks = Math.max(1, Math.trunc(options.maxTracks ?? this.maxTracks));
4030
+ this.defaultDuration = Math.max(1000, options.defaultDuration ?? this.defaultDuration);
4031
+ this.defaultSpeed = typeof options.speed === "number" ? this.normalizeSpeed(options.speed) : this.defaultSpeed;
4032
+ this.defaultColor = options.color ?? this.defaultColor;
4033
+ this.enabled = options.enabled ?? this.enabled;
4034
+ this.items = items.map((item) => this.normalize(item));
4035
+ return this.getState();
4036
+ }
4037
+ add(item) {
4038
+ this.items = [...this.items, this.normalize({ ...item, id: item.id ?? `danmaku-${Date.now()}-${this.items.length}` })];
4039
+ return this.getState();
4040
+ }
4041
+ remove(id) {
4042
+ this.items = this.items.filter((item) => item.id !== id);
4043
+ return this.getState();
4044
+ }
4045
+ getState() {
4046
+ return { enabled: this.enabled, items: this.enabled ? [...this.items] : [], maxTracks: this.maxTracks };
4047
+ }
4048
+ normalize(item) {
4049
+ const track = item.track ?? this.trackCursor;
4050
+ this.trackCursor = (track + 1) % this.maxTracks;
4051
+ return {
4052
+ ...item,
4053
+ color: item.color ?? this.defaultColor,
4054
+ duration: this.resolveDuration(item),
4055
+ track: Math.min(Math.max(0, track), this.maxTracks - 1),
4056
+ type: item.type ?? (item.imageSrc ? "image" : item.icon ? "icon" : "text")
4057
+ };
4058
+ }
4059
+ normalizeSpeed(speed) {
4060
+ if (typeof speed !== "number" || !Number.isFinite(speed))
4061
+ return undefined;
4062
+ return Math.min(Math.max(speed, 24), 800);
4063
+ }
4064
+ resolveDuration(item) {
4065
+ if (typeof item.duration === "number" && Number.isFinite(item.duration))
4066
+ return Math.max(1000, item.duration);
4067
+ const speed = this.normalizeSpeed(item.speed) ?? this.defaultSpeed;
4068
+ if (!speed)
4069
+ return this.defaultDuration;
4070
+ return Math.round(Math.min(Math.max(900000 / speed, 1000), 30000));
4071
+ }
4072
+ }
4073
+
4074
+ class LiveRoomController {
4075
+ constructor(options) {
4076
+ this.state = {
4077
+ chats: [...(options.chats ?? [])],
4078
+ danmaku: [...(options.danmaku ?? [])],
4079
+ favorited: options.favorited ?? false,
4080
+ host: options.host,
4081
+ liked: options.liked ?? false,
4082
+ likeCount: Math.max(0, options.likeCount ?? 0)
4083
+ };
4084
+ }
4085
+ update(options) {
4086
+ this.state = {
4087
+ chats: options.chats ? [...options.chats] : this.state.chats,
4088
+ danmaku: options.danmaku ? [...options.danmaku] : this.state.danmaku,
4089
+ favorited: options.favorited ?? this.state.favorited,
4090
+ host: options.host ?? this.state.host,
4091
+ liked: options.liked ?? this.state.liked,
4092
+ likeCount: Math.max(0, options.likeCount ?? this.state.likeCount)
4093
+ };
4094
+ return this.getState();
4095
+ }
4096
+ addChat(message) {
4097
+ this.state = { ...this.state, chats: [...this.state.chats, message] };
4098
+ return this.getState();
4099
+ }
4100
+ addDanmaku(item) {
4101
+ this.state = { ...this.state, danmaku: [...this.state.danmaku, item] };
4102
+ return this.getState();
4103
+ }
4104
+ toggleFavorite() {
4105
+ this.state = { ...this.state, favorited: !this.state.favorited };
4106
+ return this.getState();
4107
+ }
4108
+ toggleLike() {
4109
+ const liked = !this.state.liked;
4110
+ this.state = { ...this.state, liked, likeCount: Math.max(0, this.state.likeCount + (liked ? 1 : -1)) };
4111
+ return this.getState();
4112
+ }
4113
+ getState() {
4114
+ return {
4115
+ ...this.state,
4116
+ chats: [...this.state.chats],
4117
+ danmaku: [...this.state.danmaku],
4118
+ host: { ...this.state.host }
4119
+ };
4120
+ }
4121
+ }
4122
+
4123
+ class NovelReaderController {
4124
+ constructor(options) {
4125
+ this.pages = [...options.pages];
4126
+ this.loop = options.loop ?? false;
4127
+ this.current = this.clamp(options.current ?? 0);
4128
+ }
4129
+ update(options) {
4130
+ if (options.pages)
4131
+ this.pages = [...options.pages];
4132
+ if (typeof options.loop === "boolean")
4133
+ this.loop = options.loop;
4134
+ if (typeof options.current === "number")
4135
+ this.current = this.clamp(options.current);
4136
+ else
4137
+ this.current = this.clamp(this.current);
4138
+ return this.getState();
4139
+ }
4140
+ next() {
4141
+ this.current = this.loop && this.current >= this.pages.length - 1 ? 0 : this.clamp(this.current + 1);
4142
+ return this.getState();
4143
+ }
4144
+ previous() {
4145
+ this.current = this.loop && this.current <= 0 ? this.pages.length - 1 : this.clamp(this.current - 1);
4146
+ return this.getState();
4147
+ }
4148
+ goTo(index) {
4149
+ this.current = this.clamp(index);
4150
+ return this.getState();
4151
+ }
4152
+ getTurnMetrics(input) {
4153
+ const width = Math.max(1, input.width);
4154
+ const deltaX = Number.isFinite(input.deltaX)
4155
+ ? input.deltaX
4156
+ : input.pointerX != null && input.startX != null
4157
+ ? input.pointerX - input.startX
4158
+ : 0;
4159
+ const direction = deltaX < 0 ? "next" : "previous";
4160
+ const distance = Math.abs(deltaX);
4161
+ const raw = distance / (width * 0.74);
4162
+ const progress = input.canTurn
4163
+ ? Math.min(0.995, this.easeOutCubic(raw))
4164
+ : Math.min(0.2, raw * 0.28);
4165
+ const signed = direction === "previous" ? 1 : -1;
4166
+ const curl = Math.sin(progress * Math.PI);
4167
+ const softness = Math.sin(Math.min(1, progress) * Math.PI * 0.72);
4168
+ const angle = signed * (2 + progress * 162 - curl * 10);
4169
+ const foldTranslate = 58 - progress * 92;
4170
+ const shadowTranslate = 22 - progress * 22;
4171
+ return {
4172
+ angle,
4173
+ canTurn: input.canTurn,
4174
+ clipEnd: 4 + progress * 13,
4175
+ clipStart: progress * 12,
4176
+ curl,
4177
+ direction,
4178
+ dragX: signed * Math.min(18, distance * 0.035),
4179
+ foldOpacity: Math.min(0.96, 0.16 + progress * 0.76 + curl * 0.12),
4180
+ foldScale: 0.36 + progress * 0.98 + softness * 0.08,
4181
+ foldSkew: signed * (8 + progress * 16 + curl * 5),
4182
+ foldTranslate,
4183
+ nextOpacity: Math.min(1, 0.42 + progress * 0.58),
4184
+ pageOpacity: Math.max(0.3, 1 - progress * 0.28),
4185
+ progress,
4186
+ release: input.canTurn && progress >= NovelReaderController.releaseThreshold,
4187
+ shadowOpacity: Math.min(0.62, 0.1 + progress * 0.38 + curl * 0.18),
4188
+ shadowScale: 0.76 + progress * 0.48,
4189
+ shadowTranslate,
4190
+ skew: signed * (progress * 3.2 + curl * 3.8),
4191
+ translate: signed * (progress * 8 + curl * 3)
4192
+ };
4193
+ }
4194
+ getState() {
4195
+ const total = this.pages.length;
4196
+ return {
4197
+ canNext: this.loop ? total > 1 : this.current < total - 1,
4198
+ canPrevious: this.loop ? total > 1 : this.current > 0,
4199
+ current: this.current,
4200
+ page: this.pages[this.current] ?? null,
4201
+ total
4202
+ };
4203
+ }
4204
+ clamp(index) {
4205
+ if (this.pages.length === 0)
4206
+ return 0;
4207
+ return Math.min(Math.max(0, Math.trunc(index)), this.pages.length - 1);
4208
+ }
4209
+ easeOutCubic(value) {
4210
+ const clamped = Math.min(Math.max(value, 0), 1);
4211
+ return 1 - Math.pow(1 - clamped, 3);
4212
+ }
4213
+ }
4214
+ NovelReaderController.releaseThreshold = 0.32;
4215
+
4216
+ const DEFAULT_OVERSCAN$1 = 4;
4217
+ class VirtualListController {
4218
+ constructor(options) {
4219
+ this.measuredSizes = new Map();
4220
+ this.offsets = [];
4221
+ this.totalSize = 0;
4222
+ this.count = Math.max(0, Math.floor(options.count));
4223
+ this.estimateSize = options.estimateSize;
4224
+ this.overscanBefore = Math.max(0, Math.floor(options.overscanBefore ?? options.overscan ?? DEFAULT_OVERSCAN$1));
4225
+ this.overscanAfter = Math.max(0, Math.floor(options.overscanAfter ?? options.overscan ?? DEFAULT_OVERSCAN$1));
4226
+ this.pullUpThreshold = Math.max(0, Math.floor(options.pullUpThreshold ?? 48));
4227
+ this.recalculate();
4228
+ }
4229
+ updateOptions(options) {
4230
+ if (options.count !== undefined) {
4231
+ const nextCount = Math.max(0, Math.floor(options.count));
4232
+ if (nextCount < this.count) {
4233
+ Array.from(this.measuredSizes.keys()).forEach((index) => {
4234
+ if (index >= nextCount)
4235
+ this.measuredSizes.delete(index);
4236
+ });
4237
+ }
4238
+ this.count = nextCount;
4239
+ }
4240
+ if (options.estimateSize !== undefined) {
4241
+ this.estimateSize = options.estimateSize;
4242
+ }
4243
+ if (options.overscan !== undefined || options.overscanBefore !== undefined) {
4244
+ this.overscanBefore = Math.max(0, Math.floor(options.overscanBefore ?? options.overscan ?? this.overscanBefore));
4245
+ }
4246
+ if (options.overscan !== undefined || options.overscanAfter !== undefined) {
4247
+ this.overscanAfter = Math.max(0, Math.floor(options.overscanAfter ?? options.overscan ?? this.overscanAfter));
4248
+ }
4249
+ if (options.pullUpThreshold !== undefined) {
4250
+ this.pullUpThreshold = Math.max(0, Math.floor(options.pullUpThreshold));
4251
+ }
4252
+ this.recalculate();
4253
+ }
4254
+ getState() {
4255
+ return {
4256
+ count: this.count,
4257
+ estimateSize: this.estimateSize,
4258
+ measuredCount: this.measuredSizes.size,
4259
+ overscan: Math.max(this.overscanBefore, this.overscanAfter),
4260
+ pullUpThreshold: this.pullUpThreshold
4261
+ };
4262
+ }
4263
+ getTotalSize() {
4264
+ return this.totalSize;
4265
+ }
4266
+ shouldTriggerPullUp(scrollOffset, viewportSize) {
4267
+ return scrollOffset + Math.max(0, viewportSize) >= this.totalSize - this.pullUpThreshold;
4268
+ }
4269
+ getItem(index) {
4270
+ const safeIndex = this.clampIndex(index);
4271
+ const start = this.offsets[safeIndex] ?? 0;
4272
+ const size = this.getSize(safeIndex);
4273
+ return {
4274
+ end: start + size,
4275
+ index: safeIndex,
4276
+ key: String(safeIndex),
4277
+ size,
4278
+ start
4279
+ };
4280
+ }
4281
+ getRange(scrollOffset, viewportSize) {
4282
+ if (this.count === 0) {
4283
+ return {
4284
+ endIndex: -1,
4285
+ items: [],
4286
+ offsetAfter: 0,
4287
+ offsetBefore: 0,
4288
+ startIndex: 0,
4289
+ totalSize: 0
4290
+ };
4291
+ }
4292
+ const safeViewportSize = Math.max(0, viewportSize);
4293
+ const maxScrollOffset = Math.max(0, this.totalSize - safeViewportSize);
4294
+ const safeScrollOffset = Math.min(Math.max(0, scrollOffset), maxScrollOffset);
4295
+ const visibleStart = this.findNearestIndex(safeScrollOffset);
4296
+ const visibleEnd = this.findNearestIndex(safeScrollOffset + safeViewportSize);
4297
+ const startIndex = Math.max(0, visibleStart - this.overscanBefore);
4298
+ const endIndex = Math.min(this.count - 1, visibleEnd + this.overscanAfter);
4299
+ const items = this.getItems(startIndex, endIndex);
4300
+ const offsetBefore = this.offsets[startIndex] ?? 0;
4301
+ const lastItem = items[items.length - 1];
4302
+ const offsetAfter = Math.max(0, this.totalSize - (lastItem?.end ?? 0));
4303
+ return {
4304
+ endIndex,
4305
+ items,
4306
+ offsetAfter,
4307
+ offsetBefore,
4308
+ startIndex,
4309
+ totalSize: this.totalSize
4310
+ };
4311
+ }
4312
+ measure(index, size) {
4313
+ if (index < 0 || index >= this.count || !Number.isFinite(size) || size <= 0)
4314
+ return false;
4315
+ const normalizedSize = Math.round(size);
4316
+ if (this.measuredSizes.get(index) === normalizedSize)
4317
+ return false;
4318
+ this.measuredSizes.set(index, normalizedSize);
4319
+ this.recalculate();
4320
+ return true;
4321
+ }
4322
+ resetMeasurements() {
4323
+ this.measuredSizes.clear();
4324
+ this.recalculate();
4325
+ }
4326
+ shiftMeasurements(offset) {
4327
+ if (offset === 0 || this.measuredSizes.size === 0)
4328
+ return;
4329
+ const nextMeasurements = new Map();
4330
+ this.measuredSizes.forEach((size, index) => {
4331
+ const nextIndex = index + offset;
4332
+ if (nextIndex >= 0 && nextIndex < this.count)
4333
+ nextMeasurements.set(nextIndex, size);
4334
+ });
4335
+ this.measuredSizes = nextMeasurements;
4336
+ this.recalculate();
4337
+ }
4338
+ scrollToIndex(index, align = "start", viewportSize = 0) {
4339
+ if (this.count === 0)
4340
+ return 0;
4341
+ const item = this.getItem(index);
4342
+ if (align === "center")
4343
+ return Math.max(0, item.start - viewportSize / 2 + item.size / 2);
4344
+ if (align === "end")
4345
+ return Math.max(0, item.end - viewportSize);
4346
+ return item.start;
4347
+ }
4348
+ getItems(startIndex, endIndex) {
4349
+ const items = [];
4350
+ for (let index = startIndex; index <= endIndex; index += 1) {
4351
+ items.push(this.getItem(index));
4352
+ }
4353
+ return items;
4354
+ }
4355
+ getSize(index) {
4356
+ return this.measuredSizes.get(index) ?? this.getEstimatedSize(index);
4357
+ }
4358
+ getEstimatedSize(index) {
4359
+ const size = typeof this.estimateSize === "function" ? this.estimateSize(index) : this.estimateSize;
4360
+ return Math.max(1, Number.isFinite(size) ? Math.round(size) : 1);
4361
+ }
4362
+ recalculate() {
4363
+ this.offsets = new Array(this.count);
4364
+ let offset = 0;
4365
+ for (let index = 0; index < this.count; index += 1) {
4366
+ this.offsets[index] = offset;
4367
+ offset += this.getSize(index);
4368
+ }
4369
+ this.totalSize = offset;
4370
+ }
4371
+ findNearestIndex(offset) {
4372
+ if (offset <= 0)
4373
+ return 0;
4374
+ if (offset >= this.totalSize)
4375
+ return Math.max(0, this.count - 1);
4376
+ let low = 0;
4377
+ let high = this.count - 1;
4378
+ let nearest = this.count - 1;
4379
+ while (low <= high) {
4380
+ const middle = Math.floor((low + high) / 2);
4381
+ const start = this.offsets[middle] ?? 0;
4382
+ const end = start + this.getSize(middle);
4383
+ if (offset >= end) {
4384
+ low = middle + 1;
4385
+ }
4386
+ else {
4387
+ nearest = middle;
4388
+ high = middle - 1;
4389
+ }
4390
+ }
4391
+ return this.clampIndex(nearest);
4392
+ }
4393
+ clampIndex(index) {
4394
+ return Math.min(Math.max(0, Math.floor(index)), Math.max(0, this.count - 1));
4395
+ }
4396
+ }
4397
+
4398
+ class ChatVirtualListController {
4399
+ constructor(options = {}) {
4400
+ this.bottomThreshold = Math.max(0, options.bottomThreshold ?? 48);
4401
+ this.topThreshold = Math.max(0, options.topThreshold ?? 80);
4402
+ }
4403
+ updateOptions(options) {
4404
+ if (options.bottomThreshold !== undefined) {
4405
+ this.bottomThreshold = Math.max(0, options.bottomThreshold);
4406
+ }
4407
+ if (options.topThreshold !== undefined) {
4408
+ this.topThreshold = Math.max(0, options.topThreshold);
4409
+ }
4410
+ }
4411
+ isNearBottom(scrollTop, viewportHeight, totalSize, scrollHeight = totalSize) {
4412
+ return (Math.max(totalSize, scrollHeight) - scrollTop - viewportHeight <=
4413
+ this.bottomThreshold);
4414
+ }
4415
+ isNearTop(scrollTop) {
4416
+ return scrollTop <= this.topThreshold;
4417
+ }
4418
+ getBottomScrollTop(totalSize, viewportHeight, scrollHeight = totalSize) {
4419
+ return Math.max(0, totalSize - viewportHeight, scrollHeight - viewportHeight);
4420
+ }
4421
+ getPrependCompensatedScrollTop(previousScrollTop, previousTotalSize, nextTotalSize) {
4422
+ return Math.max(0, previousScrollTop + Math.max(0, nextTotalSize - previousTotalSize));
4423
+ }
4424
+ detectPrependCount(previousKeys, nextKeys) {
4425
+ if (previousKeys.length === 0 || nextKeys.length === 0)
4426
+ return 0;
4427
+ const firstPreviousKey = previousKeys[0];
4428
+ const index = nextKeys.findIndex((key) => Object.is(key, firstPreviousKey));
4429
+ return index > 0 ? index : 0;
4430
+ }
4431
+ detectAppendCount(previousKeys, nextKeys) {
4432
+ if (previousKeys.length === 0 || nextKeys.length <= previousKeys.length)
4433
+ return 0;
4434
+ const previousKeysStillPrefix = previousKeys.every((key, index) => Object.is(key, nextKeys[index]));
4435
+ return previousKeysStillPrefix ? nextKeys.length - previousKeys.length : 0;
4436
+ }
4437
+ }
4438
+
4439
+ const DEFAULT_GAP = 16;
4440
+ const DEFAULT_MIN_COLUMN_WIDTH = 220;
4441
+ const DEFAULT_OVERSCAN = 320;
4442
+ class MasonryVirtualListController {
4443
+ constructor(options) {
4444
+ this.measuredSizes = new Map();
4445
+ this.items = [];
4446
+ this.totalSize = 0;
4447
+ this.resolvedColumns = 1;
4448
+ this.columnWidth = 0;
4449
+ this.columns = this.normalizeOptionalCount(options.columns);
4450
+ this.containerWidth = Math.max(0, options.containerWidth);
4451
+ this.count = Math.max(0, Math.floor(options.count));
4452
+ this.estimateSize = options.estimateSize;
4453
+ this.gap = Math.max(0, options.gap ?? DEFAULT_GAP);
4454
+ this.minColumnWidth = Math.max(1, options.minColumnWidth ?? DEFAULT_MIN_COLUMN_WIDTH);
4455
+ this.overscan = Math.max(0, options.overscan ?? DEFAULT_OVERSCAN);
4456
+ this.recalculate();
4457
+ }
4458
+ updateOptions(options) {
4459
+ if (options.columns !== undefined) {
4460
+ this.columns = this.normalizeOptionalCount(options.columns);
4461
+ }
4462
+ if (options.containerWidth !== undefined) {
4463
+ this.containerWidth = Math.max(0, options.containerWidth);
4464
+ }
4465
+ if (options.count !== undefined) {
4466
+ const nextCount = Math.max(0, Math.floor(options.count));
4467
+ if (nextCount < this.count) {
4468
+ Array.from(this.measuredSizes.keys()).forEach((index) => {
4469
+ if (index >= nextCount)
4470
+ this.measuredSizes.delete(index);
4471
+ });
4472
+ }
4473
+ this.count = nextCount;
4474
+ }
4475
+ if (options.estimateSize !== undefined) {
4476
+ this.estimateSize = options.estimateSize;
4477
+ }
4478
+ if (options.gap !== undefined) {
4479
+ this.gap = Math.max(0, options.gap);
4480
+ }
4481
+ if (options.minColumnWidth !== undefined) {
4482
+ this.minColumnWidth = Math.max(1, options.minColumnWidth);
4483
+ }
4484
+ if (options.overscan !== undefined) {
4485
+ this.overscan = Math.max(0, options.overscan);
4486
+ }
4487
+ this.recalculate();
4488
+ }
4489
+ getState() {
4490
+ return {
4491
+ columnWidth: this.columnWidth,
4492
+ columns: this.resolvedColumns,
4493
+ count: this.count,
4494
+ gap: this.gap,
4495
+ measuredCount: this.measuredSizes.size,
4496
+ overscan: this.overscan
4497
+ };
4498
+ }
4499
+ getTotalSize() {
4500
+ return this.totalSize;
4501
+ }
4502
+ getItem(index) {
4503
+ const safeIndex = this.clampIndex(index);
4504
+ return this.items[safeIndex] ?? this.createFallbackItem(safeIndex);
4505
+ }
4506
+ getRange(scrollOffset, viewportSize) {
4507
+ if (this.count === 0) {
4508
+ return {
4509
+ columnWidth: this.columnWidth,
4510
+ columns: this.resolvedColumns,
4511
+ endIndex: -1,
4512
+ items: [],
4513
+ startIndex: 0,
4514
+ totalSize: 0
4515
+ };
4516
+ }
4517
+ const safeViewportSize = Math.max(0, viewportSize);
4518
+ const maxScrollOffset = Math.max(0, this.totalSize - safeViewportSize);
4519
+ const safeScrollOffset = Math.min(Math.max(0, scrollOffset), maxScrollOffset);
4520
+ const rangeStart = Math.max(0, safeScrollOffset - this.overscan);
4521
+ const rangeEnd = safeScrollOffset + safeViewportSize + this.overscan;
4522
+ const items = this.items.filter((item) => item.end >= rangeStart && item.start <= rangeEnd);
4523
+ return {
4524
+ columnWidth: this.columnWidth,
4525
+ columns: this.resolvedColumns,
4526
+ endIndex: items[items.length - 1]?.index ?? -1,
4527
+ items,
4528
+ startIndex: items[0]?.index ?? 0,
4529
+ totalSize: this.totalSize
4530
+ };
4531
+ }
4532
+ measure(index, size) {
4533
+ if (index < 0 || index >= this.count || !Number.isFinite(size) || size <= 0)
4534
+ return false;
4535
+ const normalizedSize = Math.round(size);
4536
+ if (this.measuredSizes.get(index) === normalizedSize)
4537
+ return false;
4538
+ this.measuredSizes.set(index, normalizedSize);
4539
+ this.recalculate();
4540
+ return true;
4541
+ }
4542
+ resetMeasurements() {
4543
+ this.measuredSizes.clear();
4544
+ this.recalculate();
4545
+ }
4546
+ recalculate() {
4547
+ this.resolvedColumns = this.resolveColumns();
4548
+ this.columnWidth =
4549
+ this.resolvedColumns <= 1
4550
+ ? Math.max(0, this.containerWidth)
4551
+ : Math.max(1, (this.containerWidth - this.gap * (this.resolvedColumns - 1)) / this.resolvedColumns);
4552
+ const columnHeights = new Array(this.resolvedColumns).fill(0);
4553
+ this.items = new Array(this.count);
4554
+ for (let index = 0; index < this.count; index += 1) {
4555
+ const column = this.findShortestColumn(columnHeights);
4556
+ const x = column * (this.columnWidth + this.gap);
4557
+ const y = columnHeights[column] ?? 0;
4558
+ const height = this.getSize(index);
4559
+ this.items[index] = {
4560
+ column,
4561
+ end: y + height,
4562
+ height,
4563
+ index,
4564
+ key: String(index),
4565
+ start: y,
4566
+ width: this.columnWidth,
4567
+ x,
4568
+ y
4569
+ };
4570
+ columnHeights[column] = y + height + this.gap;
4571
+ }
4572
+ this.totalSize = Math.max(0, ...columnHeights.map((height) => Math.max(0, height - this.gap)));
4573
+ }
4574
+ resolveColumns() {
4575
+ if (this.columns !== undefined)
4576
+ return Math.max(1, this.columns);
4577
+ if (this.containerWidth <= 0)
4578
+ return 1;
4579
+ return Math.max(1, Math.floor((this.containerWidth + this.gap) / (this.minColumnWidth + this.gap)));
4580
+ }
4581
+ getSize(index) {
4582
+ return this.measuredSizes.get(index) ?? this.getEstimatedSize(index);
4583
+ }
4584
+ getEstimatedSize(index) {
4585
+ const size = typeof this.estimateSize === "function"
4586
+ ? this.estimateSize(index, this.columnWidth)
4587
+ : this.estimateSize;
4588
+ return Math.max(1, Number.isFinite(size) ? Math.round(size) : 1);
4589
+ }
4590
+ findShortestColumn(columnHeights) {
4591
+ let column = 0;
4592
+ let minHeight = columnHeights[0] ?? 0;
4593
+ for (let index = 1; index < columnHeights.length; index += 1) {
4594
+ const height = columnHeights[index] ?? 0;
4595
+ if (height < minHeight) {
4596
+ column = index;
4597
+ minHeight = height;
4598
+ }
4599
+ }
4600
+ return column;
4601
+ }
4602
+ createFallbackItem(index) {
4603
+ return {
4604
+ column: 0,
4605
+ end: 0,
4606
+ height: 0,
4607
+ index,
4608
+ key: String(index),
4609
+ start: 0,
4610
+ width: this.columnWidth,
4611
+ x: 0,
4612
+ y: 0
4613
+ };
4614
+ }
4615
+ normalizeOptionalCount(value) {
4616
+ if (value === undefined)
4617
+ return undefined;
4618
+ return Math.max(1, Math.floor(value));
4619
+ }
4620
+ clampIndex(index) {
4621
+ return Math.min(Math.max(0, Math.floor(index)), Math.max(0, this.count - 1));
4622
+ }
4623
+ }
4624
+
4625
+ exports.AffixController = AffixController;
4626
+ exports.BacktopController = BacktopController;
4627
+ exports.BadgeController = BadgeController;
4628
+ exports.BreadcrumbController = BreadcrumbController;
4629
+ exports.CalendarController = CalendarController;
4630
+ exports.CanvasEditorController = CanvasEditorController;
4631
+ exports.CanvasImageController = CanvasImageController;
4632
+ exports.ChatVirtualListController = ChatVirtualListController;
4633
+ exports.ColorPickerController = ColorPickerController;
4634
+ exports.ComicReaderController = ComicReaderController;
4635
+ exports.CountdownController = CountdownController;
4636
+ exports.DanmakuController = DanmakuController;
4637
+ exports.DatePickerController = DatePickerController;
4638
+ exports.DateRangePickerController = DateRangePickerController;
4639
+ exports.DesignEffectController = DesignEffectController;
4640
+ exports.DragController = DragController;
4641
+ exports.DropController = DropController;
4642
+ exports.FieldController = FieldController;
4643
+ exports.FilePreviewController = FilePreviewController;
4644
+ exports.FloatingBallController = FloatingBallController;
4645
+ exports.FormController = FormController;
4646
+ exports.GestureController = GestureController;
4647
+ exports.GradientTextController = GradientTextController;
4648
+ exports.ImagePreviewController = ImagePreviewController;
4649
+ exports.LazyImageController = LazyImageController;
4650
+ exports.LiveRoomController = LiveRoomController;
4651
+ exports.MarqueeController = MarqueeController;
4652
+ exports.MasonryVirtualListController = MasonryVirtualListController;
4653
+ exports.ModalController = ModalController;
4654
+ exports.MultiColumnPickerController = MultiColumnPickerController;
4655
+ exports.NovelReaderController = NovelReaderController;
4656
+ exports.PaginationController = PaginationController;
4657
+ exports.PasswordInputController = PasswordInputController;
4658
+ exports.PopupController = PopupController;
4659
+ exports.ProgressController = ProgressController;
4660
+ exports.RollingNumberController = RollingNumberController;
4661
+ exports.SignatureController = SignatureController;
4662
+ exports.SkeletonController = SkeletonController;
4663
+ exports.SlideCaptchaController = SlideCaptchaController;
4664
+ exports.StateEmitter = StateEmitter;
4665
+ exports.SwiperController = SwiperController;
4666
+ exports.TableController = TableController;
4667
+ exports.TabsController = TabsController;
4668
+ exports.TagController = TagController;
4669
+ exports.TiltCardController = TiltCardController;
4670
+ exports.TimelineController = TimelineController;
4671
+ exports.ToastManager = ToastManager;
4672
+ exports.TooltipController = TooltipController;
4673
+ exports.TypewriterTextController = TypewriterTextController;
4674
+ exports.UploadController = UploadController;
4675
+ exports.VerificationCodeController = VerificationCodeController;
4676
+ exports.VideoDetailTransitionController = VideoDetailTransitionController;
4677
+ exports.VideoPlayerController = VideoPlayerController;
4678
+ exports.VirtualListController = VirtualListController;
4679
+ exports.VirtualSelectController = VirtualSelectController;
4680
+ exports.cloneValues = cloneValues;
4681
+ exports.configureClassComponents = configureClassComponents;
4682
+ exports.getByPath = getByPath;
4683
+ exports.getClassComponentsConfig = getClassComponentsConfig;
4684
+ exports.getClassComponentsLocale = getClassComponentsLocale;
4685
+ exports.getSafeTooltipPlacement = getSafeTooltipPlacement;
4686
+ exports.resetClassComponentsConfig = resetClassComponentsConfig;
4687
+ exports.setByPath = setByPath;
4688
+ //# sourceMappingURL=index.cjs.map