@gcsa/console-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 (85) hide show
  1. package/dist/gcsaChat.d.ts +6 -0
  2. package/dist/gcsaChat.d.ts.map +1 -0
  3. package/dist/gcsaChat.js +31 -0
  4. package/dist/gcsaClipboard.d.ts +2 -0
  5. package/dist/gcsaClipboard.d.ts.map +1 -0
  6. package/dist/gcsaClipboard.js +30 -0
  7. package/dist/gcsaCssLength.d.ts +3 -0
  8. package/dist/gcsaCssLength.d.ts.map +1 -0
  9. package/dist/gcsaCssLength.js +6 -0
  10. package/dist/gcsaDate.d.ts +15 -0
  11. package/dist/gcsaDate.d.ts.map +1 -0
  12. package/dist/gcsaDate.js +12 -0
  13. package/dist/gcsaDateTimeValue.d.ts +8 -0
  14. package/dist/gcsaDateTimeValue.d.ts.map +1 -0
  15. package/dist/gcsaDateTimeValue.js +16 -0
  16. package/dist/gcsaLocale.d.ts +109 -0
  17. package/dist/gcsaLocale.d.ts.map +1 -0
  18. package/dist/gcsaLocale.js +320 -0
  19. package/dist/gcsaMarkdown.d.ts +2 -0
  20. package/dist/gcsaMarkdown.d.ts.map +1 -0
  21. package/dist/gcsaMarkdown.js +19 -0
  22. package/dist/gcsaMenu.d.ts +21 -0
  23. package/dist/gcsaMenu.d.ts.map +1 -0
  24. package/dist/gcsaMenu.js +20 -0
  25. package/dist/gcsaMessage.d.ts +3 -0
  26. package/dist/gcsaMessage.d.ts.map +1 -0
  27. package/dist/gcsaMessage.js +1 -0
  28. package/dist/gcsaPagination.d.ts +13 -0
  29. package/dist/gcsaPagination.d.ts.map +1 -0
  30. package/dist/gcsaPagination.js +52 -0
  31. package/dist/gcsaPopupCollision.d.ts +22 -0
  32. package/dist/gcsaPopupCollision.d.ts.map +1 -0
  33. package/dist/gcsaPopupCollision.js +66 -0
  34. package/dist/gcsaPopupPositioning.d.ts +30 -0
  35. package/dist/gcsaPopupPositioning.d.ts.map +1 -0
  36. package/dist/gcsaPopupPositioning.js +18 -0
  37. package/dist/gcsaPopupStyles.d.ts +51 -0
  38. package/dist/gcsaPopupStyles.d.ts.map +1 -0
  39. package/dist/gcsaPopupStyles.js +115 -0
  40. package/dist/gcsaSize.d.ts +9 -0
  41. package/dist/gcsaSize.d.ts.map +1 -0
  42. package/dist/gcsaSize.js +13 -0
  43. package/dist/gcsaTableEvents.d.ts +27 -0
  44. package/dist/gcsaTableEvents.d.ts.map +1 -0
  45. package/dist/gcsaTableEvents.js +13 -0
  46. package/dist/gcsaTableLayout.d.ts +23 -0
  47. package/dist/gcsaTableLayout.d.ts.map +1 -0
  48. package/dist/gcsaTableLayout.js +36 -0
  49. package/dist/gcsaTableMigration.d.ts +34 -0
  50. package/dist/gcsaTableMigration.d.ts.map +1 -0
  51. package/dist/gcsaTableMigration.js +65 -0
  52. package/dist/gcsaTablePreferences.d.ts +16 -0
  53. package/dist/gcsaTablePreferences.d.ts.map +1 -0
  54. package/dist/gcsaTablePreferences.js +53 -0
  55. package/dist/gcsaTableRows.d.ts +8 -0
  56. package/dist/gcsaTableRows.d.ts.map +1 -0
  57. package/dist/gcsaTableRows.js +29 -0
  58. package/dist/gcsaTime.d.ts +23 -0
  59. package/dist/gcsaTime.d.ts.map +1 -0
  60. package/dist/gcsaTime.js +63 -0
  61. package/dist/index.d.ts +163 -0
  62. package/dist/index.d.ts.map +1 -0
  63. package/dist/index.js +30 -0
  64. package/package.json +37 -0
  65. package/src/gcsaChat.ts +35 -0
  66. package/src/gcsaClipboard.ts +28 -0
  67. package/src/gcsaCssLength.ts +5 -0
  68. package/src/gcsaDate.ts +30 -0
  69. package/src/gcsaDateTimeValue.ts +18 -0
  70. package/src/gcsaLocale.ts +431 -0
  71. package/src/gcsaMarkdown.ts +19 -0
  72. package/src/gcsaMenu.ts +43 -0
  73. package/src/gcsaMessage.ts +3 -0
  74. package/src/gcsaPagination.ts +68 -0
  75. package/src/gcsaPopupCollision.ts +111 -0
  76. package/src/gcsaPopupPositioning.ts +57 -0
  77. package/src/gcsaPopupStyles.ts +137 -0
  78. package/src/gcsaSize.ts +29 -0
  79. package/src/gcsaTableEvents.ts +36 -0
  80. package/src/gcsaTableLayout.ts +69 -0
  81. package/src/gcsaTableMigration.ts +104 -0
  82. package/src/gcsaTablePreferences.ts +61 -0
  83. package/src/gcsaTableRows.ts +36 -0
  84. package/src/gcsaTime.ts +83 -0
  85. package/src/index.ts +237 -0
@@ -0,0 +1,431 @@
1
+ /** BCP 47,与 GCSA 主站语言一致 */
2
+ export type GcsaConsoleLocale = 'zh-CN' | 'zh-TW' | 'en-US';
3
+
4
+ export const GCSA_DEFAULT_LOCALE: GcsaConsoleLocale = 'zh-CN';
5
+
6
+ export type GcsaDatePickerLocaleStrings = {
7
+ placeholder: string;
8
+ placeholderDateTime: string;
9
+ selectTime: string;
10
+ now: string;
11
+ placeholderStart: string;
12
+ placeholderEnd: string;
13
+ placeholderMonth: string;
14
+ placeholderStartMonth: string;
15
+ placeholderEndMonth: string;
16
+ rangeSeparator: string;
17
+ openCalendar: string;
18
+ prevMonth: string;
19
+ nextMonth: string;
20
+ prevYear: string;
21
+ nextYear: string;
22
+ today: string;
23
+ };
24
+
25
+ export type GcsaConsoleLocaleMessages = {
26
+ close: string;
27
+ remove: string;
28
+ open: string;
29
+ menu: string;
30
+ hoverToView: string;
31
+ confirm: string;
32
+ cancel: string;
33
+ delete: string;
34
+ confirmAction: string;
35
+ validationFailed: string;
36
+ formValidationFailed: string;
37
+ datePicker: GcsaDatePickerLocaleStrings;
38
+ uploadSelectFile: string;
39
+ uploadRemoveFile: string;
40
+ uploadDragHint: string;
41
+ uploadUploading: string;
42
+ uploadFailed: string;
43
+ uploadRetry: string;
44
+ inputNumberDecrease: string;
45
+ inputNumberIncrease: string;
46
+ shellCloseNav: string;
47
+ shellOpenNav: string;
48
+ shellExpandSider: string;
49
+ shellCollapseSider: string;
50
+ chatPlaceholder: string;
51
+ chatSend: string;
52
+ chatSending: string;
53
+ chatEmpty: string;
54
+ chatCopy: string;
55
+ chatCopySuccess: string;
56
+ chatCopyError: string;
57
+ formSubmit: string;
58
+ formReset: string;
59
+ paginationLabel: string;
60
+ paginationPrev: string;
61
+ paginationNext: string;
62
+ paginationPageSize: string;
63
+ paginationPageSizeAria: string;
64
+ paginationJumper: string;
65
+ selectListHint: string;
66
+ selectPlaceholder: string;
67
+ searchPlaceholder: string;
68
+ noOptions: string;
69
+ emptyDefault: string;
70
+ loading: string;
71
+ retry: string;
72
+ dataTableEmpty: string;
73
+ dataTableLoading: string;
74
+ dataTableTotal: string;
75
+ dataTableSelected: string;
76
+ dataTableRange: string;
77
+ dataTableFilter: string;
78
+ dataTableSummary: string;
79
+ dataTableSelectAll: string;
80
+ dataTableSelectRow: string;
81
+ dataTableResizeColumn: string;
82
+ dataTableExpandRow: string;
83
+ dataTableCollapseRow: string;
84
+ dataTableLoadChildren: string;
85
+ dataTableRetryChildren: string;
86
+ dataTableTreeLoading: string;
87
+ dataTableTreeLoadError: string;
88
+ timePickerPlaceholder: string;
89
+ timePickerOpen: string;
90
+ timePickerClear: string;
91
+ timePickerSelect: string;
92
+ timeHour: string;
93
+ timeMinute: string;
94
+ timeSecond: string;
95
+ transferSourceTitle: string;
96
+ transferTargetTitle: string;
97
+ transferMoveRight: string;
98
+ transferMoveLeft: string;
99
+ imagePreview: string;
100
+ imagePreviewClose: string;
101
+ stepsLabel: string;
102
+ timelineLabel: string;
103
+ breadcrumbLabel: string;
104
+ consoleNavLabel: string;
105
+ };
106
+
107
+ const LOCALE_MESSAGES: Record<GcsaConsoleLocale, GcsaConsoleLocaleMessages> = {
108
+ 'zh-CN': {
109
+ close: '关闭',
110
+ remove: '移除',
111
+ open: '打开',
112
+ menu: '菜单',
113
+ hoverToView: '悬停查看',
114
+ confirm: '确认',
115
+ cancel: '取消',
116
+ delete: '删除',
117
+ confirmAction: '确认操作?',
118
+ validationFailed: '校验失败',
119
+ formValidationFailed: '表单校验失败',
120
+ datePicker: {
121
+ placeholder: '选择日期',
122
+ placeholderDateTime: '选择日期时间',
123
+ selectTime: '选择时间',
124
+ now: '此刻',
125
+ placeholderStart: '开始日期',
126
+ placeholderEnd: '结束日期',
127
+ placeholderMonth: '选择月份',
128
+ placeholderStartMonth: '开始月份',
129
+ placeholderEndMonth: '结束月份',
130
+ rangeSeparator: '至',
131
+ openCalendar: '打开日历',
132
+ prevMonth: '上一月',
133
+ nextMonth: '下一月',
134
+ prevYear: '上一年',
135
+ nextYear: '下一年',
136
+ today: '今天'
137
+ },
138
+ uploadSelectFile: '选择文件',
139
+ uploadRemoveFile: '移除文件',
140
+ uploadDragHint: '拖拽文件到此处,或点击选择',
141
+ uploadUploading: '上传中…',
142
+ uploadFailed: '上传失败',
143
+ uploadRetry: '重试',
144
+ inputNumberDecrease: '减少',
145
+ inputNumberIncrease: '增加',
146
+ shellCloseNav: '关闭导航',
147
+ shellOpenNav: '打开导航',
148
+ shellExpandSider: '展开侧栏',
149
+ shellCollapseSider: '收起侧栏',
150
+ chatPlaceholder: '输入消息…',
151
+ chatSend: '发送',
152
+ chatSending: '发送中…',
153
+ chatEmpty: '开始对话吧',
154
+ chatCopy: '复制',
155
+ chatCopySuccess: '已复制',
156
+ chatCopyError: '复制失败',
157
+ formSubmit: '保存',
158
+ formReset: '重置',
159
+ paginationLabel: '分页',
160
+ paginationPrev: '上一页',
161
+ paginationNext: '下一页',
162
+ paginationPageSize: '每页',
163
+ paginationPageSizeAria: '每页条数',
164
+ paginationJumper: '跳至',
165
+ selectListHint: '共 {count} 项,可滚动浏览',
166
+ selectPlaceholder: '请选择',
167
+ searchPlaceholder: '搜索',
168
+ noOptions: '暂无选项',
169
+ emptyDefault: '暂无数据',
170
+ loading: '加载中…',
171
+ retry: '重试',
172
+ dataTableEmpty: '暂无数据',
173
+ dataTableLoading: '加载中…',
174
+ dataTableTotal: '共 {count} 条',
175
+ dataTableSelected: '已选 {count}',
176
+ dataTableRange: '第 {start}-{end} 条,共 {total} 条',
177
+ dataTableFilter: '筛选',
178
+ dataTableSummary: '合计',
179
+ dataTableSelectAll: '选择当前页全部行',
180
+ dataTableSelectRow: '选择此行',
181
+ dataTableResizeColumn: '调整列宽',
182
+ dataTableExpandRow: '展开行',
183
+ dataTableCollapseRow: '收起行',
184
+ dataTableLoadChildren: '加载子节点',
185
+ dataTableRetryChildren: '重试加载子节点',
186
+ dataTableTreeLoading: '加载中…',
187
+ dataTableTreeLoadError: '加载失败,重试',
188
+ timePickerPlaceholder: '请选择时间',
189
+ timePickerOpen: '打开时间选择面板',
190
+ timePickerClear: '清空时间',
191
+ timePickerSelect: '选择时间',
192
+ timeHour: '小时',
193
+ timeMinute: '分钟',
194
+ timeSecond: '秒',
195
+ transferSourceTitle: '可选项',
196
+ transferTargetTitle: '已选项',
197
+ transferMoveRight: '移入已选',
198
+ transferMoveLeft: '移回可选',
199
+ imagePreview: '图片预览',
200
+ imagePreviewClose: '关闭预览',
201
+ stepsLabel: '步骤',
202
+ timelineLabel: '时间线',
203
+ breadcrumbLabel: '面包屑',
204
+ consoleNavLabel: '控制台导航'
205
+ },
206
+ 'zh-TW': {
207
+ close: '關閉',
208
+ remove: '移除',
209
+ open: '打開',
210
+ menu: '選單',
211
+ hoverToView: '懸停查看',
212
+ confirm: '確認',
213
+ cancel: '取消',
214
+ delete: '刪除',
215
+ confirmAction: '確認操作?',
216
+ validationFailed: '校驗失敗',
217
+ formValidationFailed: '表單校驗失敗',
218
+ datePicker: {
219
+ placeholder: '選擇日期',
220
+ placeholderDateTime: '選擇日期時間',
221
+ selectTime: '選擇時間',
222
+ now: '此刻',
223
+ placeholderStart: '開始日期',
224
+ placeholderEnd: '結束日期',
225
+ placeholderMonth: '選擇月份',
226
+ placeholderStartMonth: '開始月份',
227
+ placeholderEndMonth: '結束月份',
228
+ rangeSeparator: '至',
229
+ openCalendar: '打開日曆',
230
+ prevMonth: '上一月',
231
+ nextMonth: '下一月',
232
+ prevYear: '上一年',
233
+ nextYear: '下一年',
234
+ today: '今天'
235
+ },
236
+ uploadSelectFile: '選擇檔案',
237
+ uploadRemoveFile: '移除檔案',
238
+ uploadDragHint: '拖曳檔案到此處,或點擊選擇',
239
+ uploadUploading: '上傳中…',
240
+ uploadFailed: '上傳失敗',
241
+ uploadRetry: '重試',
242
+ inputNumberDecrease: '減少',
243
+ inputNumberIncrease: '增加',
244
+ shellCloseNav: '關閉導覽',
245
+ shellOpenNav: '打開導覽',
246
+ shellExpandSider: '展開側欄',
247
+ shellCollapseSider: '收起側欄',
248
+ chatPlaceholder: '輸入訊息…',
249
+ chatSend: '傳送',
250
+ chatSending: '傳送中…',
251
+ chatEmpty: '開始對話吧',
252
+ chatCopy: '複製',
253
+ chatCopySuccess: '已複製',
254
+ chatCopyError: '複製失敗',
255
+ formSubmit: '儲存',
256
+ formReset: '重置',
257
+ paginationLabel: '分頁',
258
+ paginationPrev: '上一頁',
259
+ paginationNext: '下一頁',
260
+ paginationPageSize: '每頁',
261
+ paginationPageSizeAria: '每頁筆數',
262
+ paginationJumper: '跳至',
263
+ selectListHint: '共 {count} 項,可捲動瀏覽',
264
+ selectPlaceholder: '請選擇',
265
+ searchPlaceholder: '搜尋',
266
+ noOptions: '暫無選項',
267
+ emptyDefault: '暫無資料',
268
+ loading: '載入中…',
269
+ retry: '重試',
270
+ dataTableEmpty: '暫無資料',
271
+ dataTableLoading: '載入中…',
272
+ dataTableTotal: '共 {count} 條',
273
+ dataTableSelected: '已選 {count}',
274
+ dataTableRange: '第 {start}-{end} 條,共 {total} 條',
275
+ dataTableFilter: '篩選',
276
+ dataTableSummary: '合計',
277
+ dataTableSelectAll: '選擇目前頁全部列',
278
+ dataTableSelectRow: '選擇此列',
279
+ dataTableResizeColumn: '調整欄寬',
280
+ dataTableExpandRow: '展開列',
281
+ dataTableCollapseRow: '收起列',
282
+ dataTableLoadChildren: '載入子節點',
283
+ dataTableRetryChildren: '重試載入子節點',
284
+ dataTableTreeLoading: '載入中…',
285
+ dataTableTreeLoadError: '載入失敗,重試',
286
+ timePickerPlaceholder: '請選擇時間',
287
+ timePickerOpen: '打開時間選擇面板',
288
+ timePickerClear: '清空時間',
289
+ timePickerSelect: '選擇時間',
290
+ timeHour: '小時',
291
+ timeMinute: '分鐘',
292
+ timeSecond: '秒',
293
+ transferSourceTitle: '可選項',
294
+ transferTargetTitle: '已選項',
295
+ transferMoveRight: '移入已選',
296
+ transferMoveLeft: '移回可選',
297
+ imagePreview: '圖片預覽',
298
+ imagePreviewClose: '關閉預覽',
299
+ stepsLabel: '步驟',
300
+ timelineLabel: '時間線',
301
+ breadcrumbLabel: '麵包屑',
302
+ consoleNavLabel: '控制台導覽'
303
+ },
304
+ 'en-US': {
305
+ close: 'Close',
306
+ remove: 'Remove',
307
+ open: 'Open',
308
+ menu: 'Menu',
309
+ hoverToView: 'Hover to view',
310
+ confirm: 'Confirm',
311
+ cancel: 'Cancel',
312
+ delete: 'Delete',
313
+ confirmAction: 'Confirm this action?',
314
+ validationFailed: 'Validation failed',
315
+ formValidationFailed: 'Form validation failed',
316
+ datePicker: {
317
+ placeholder: 'Select date',
318
+ placeholderDateTime: 'Select date and time',
319
+ selectTime: 'Select time',
320
+ now: 'Now',
321
+ placeholderStart: 'Start date',
322
+ placeholderEnd: 'End date',
323
+ placeholderMonth: 'Select month',
324
+ placeholderStartMonth: 'Start month',
325
+ placeholderEndMonth: 'End month',
326
+ rangeSeparator: 'to',
327
+ openCalendar: 'Open calendar',
328
+ prevMonth: 'Previous month',
329
+ nextMonth: 'Next month',
330
+ prevYear: 'Previous year',
331
+ nextYear: 'Next year',
332
+ today: 'Today'
333
+ },
334
+ uploadSelectFile: 'Choose file',
335
+ uploadRemoveFile: 'Remove file',
336
+ uploadDragHint: 'Drop files here, or click to browse',
337
+ uploadUploading: 'Uploading…',
338
+ uploadFailed: 'Upload failed',
339
+ uploadRetry: 'Retry',
340
+ inputNumberDecrease: 'Decrease',
341
+ inputNumberIncrease: 'Increase',
342
+ shellCloseNav: 'Close navigation',
343
+ shellOpenNav: 'Open navigation',
344
+ shellExpandSider: 'Expand sidebar',
345
+ shellCollapseSider: 'Collapse sidebar',
346
+ chatPlaceholder: 'Type a message…',
347
+ chatSend: 'Send',
348
+ chatSending: 'Sending…',
349
+ chatEmpty: 'Start a conversation',
350
+ chatCopy: 'Copy',
351
+ chatCopySuccess: 'Copied',
352
+ chatCopyError: 'Copy failed',
353
+ formSubmit: 'Save',
354
+ formReset: 'Reset',
355
+ paginationLabel: 'Pagination',
356
+ paginationPrev: 'Previous',
357
+ paginationNext: 'Next',
358
+ paginationPageSize: 'Per page',
359
+ paginationPageSizeAria: 'Items per page',
360
+ paginationJumper: 'Go to',
361
+ selectListHint: '{count} items — scroll to browse',
362
+ selectPlaceholder: 'Please select',
363
+ searchPlaceholder: 'Search',
364
+ noOptions: 'No options',
365
+ emptyDefault: 'No data',
366
+ loading: 'Loading…',
367
+ retry: 'Retry',
368
+ dataTableEmpty: 'No data',
369
+ dataTableLoading: 'Loading…',
370
+ dataTableTotal: '{count} items',
371
+ dataTableSelected: '{count} selected',
372
+ dataTableRange: '{start}-{end} of {total}',
373
+ dataTableFilter: 'Filter',
374
+ dataTableSummary: 'Total',
375
+ dataTableSelectAll: 'Select all rows on this page',
376
+ dataTableSelectRow: 'Select this row',
377
+ dataTableResizeColumn: 'Resize column',
378
+ dataTableExpandRow: 'Expand row',
379
+ dataTableCollapseRow: 'Collapse row',
380
+ dataTableLoadChildren: 'Loading children',
381
+ dataTableRetryChildren: 'Retry loading children',
382
+ dataTableTreeLoading: 'Loading…',
383
+ dataTableTreeLoadError: 'Failed to load — retry',
384
+ timePickerPlaceholder: 'Select time',
385
+ timePickerOpen: 'Open time picker',
386
+ timePickerClear: 'Clear time',
387
+ timePickerSelect: 'Select time',
388
+ timeHour: 'Hour',
389
+ timeMinute: 'Minute',
390
+ timeSecond: 'Second',
391
+ transferSourceTitle: 'Available',
392
+ transferTargetTitle: 'Selected',
393
+ transferMoveRight: 'Move to selected',
394
+ transferMoveLeft: 'Move to available',
395
+ imagePreview: 'Image preview',
396
+ imagePreviewClose: 'Close preview',
397
+ stepsLabel: 'Steps',
398
+ timelineLabel: 'Timeline',
399
+ breadcrumbLabel: 'Breadcrumb',
400
+ consoleNavLabel: 'Console navigation'
401
+ }
402
+ };
403
+
404
+ export function normalizeGcsaLocale(locale?: string): GcsaConsoleLocale {
405
+ if (locale === 'zh-CN' || locale === 'zh-TW' || locale === 'en-US') return locale;
406
+ if (locale?.startsWith('zh')) {
407
+ return locale.includes('TW') || locale.includes('Hant') ? 'zh-TW' : 'zh-CN';
408
+ }
409
+ if (locale?.startsWith('en')) return 'en-US';
410
+ return GCSA_DEFAULT_LOCALE;
411
+ }
412
+
413
+ export function getGcsaLocaleMessages(locale: GcsaConsoleLocale): GcsaConsoleLocaleMessages {
414
+ return LOCALE_MESSAGES[locale] ?? LOCALE_MESSAGES[GCSA_DEFAULT_LOCALE];
415
+ }
416
+
417
+ export function getGcsaDatePickerMessages(locale: GcsaConsoleLocale): GcsaDatePickerLocaleStrings {
418
+ return getGcsaLocaleMessages(locale).datePicker;
419
+ }
420
+
421
+ /** 周起始日:中文周一,英文周日 */
422
+ export function getGcsaStartOfWeek(locale: GcsaConsoleLocale): 0 | 1 {
423
+ return locale === 'en-US' ? 0 : 1;
424
+ }
425
+
426
+ export function formatGcsaLocaleMessage(
427
+ template: string,
428
+ vars: Record<string, string | number>
429
+ ): string {
430
+ return template.replace(/\{(\w+)\}/g, (_, key: string) => String(vars[key] ?? ''));
431
+ }
@@ -0,0 +1,19 @@
1
+ import DOMPurify from 'dompurify';
2
+ import { marked } from 'marked';
3
+
4
+ let configured = false;
5
+
6
+ function ensureMarked() {
7
+ if (configured) return;
8
+ marked.setOptions({ breaks: true, gfm: true });
9
+ configured = true;
10
+ }
11
+
12
+ export function renderGcsaMarkdown(content: string): string {
13
+ const raw = content || '';
14
+ if (!raw.trim()) return '';
15
+ ensureMarked();
16
+ const parsed = marked.parse(raw, { async: false }) as string;
17
+ if (typeof window === 'undefined') return parsed;
18
+ return DOMPurify.sanitize(parsed, { USE_PROFILES: { html: true } });
19
+ }
@@ -0,0 +1,43 @@
1
+ export type GcsaMenuIconKey = 'dashboard' | 'alert' | 'assets' | 'settings' | 'users' | 'chat';
2
+
3
+ export type GcsaMenuItem = {
4
+ key: string;
5
+ label: string;
6
+ iconKey?: GcsaMenuIconKey;
7
+ /** 自定义图标节点(React/Vue 插槽);侧栏可暂用 @ant-design/icons */
8
+ icon?: unknown;
9
+ href?: string;
10
+ disabled?: boolean;
11
+ type?: 'item' | 'group' | 'divider';
12
+ children?: GcsaMenuItem[];
13
+ };
14
+
15
+ export function navItemsToMenuItems(
16
+ items: Array<{
17
+ key: string;
18
+ label: string;
19
+ iconKey?: GcsaMenuIconKey;
20
+ href?: string;
21
+ }>
22
+ ): GcsaMenuItem[] {
23
+ return items.map(item => ({
24
+ key: item.key,
25
+ label: item.label,
26
+ iconKey: item.iconKey,
27
+ href: item.href
28
+ }));
29
+ }
30
+
31
+ export type GcsaMenuMode = 'vertical' | 'horizontal' | 'inline';
32
+
33
+ export function collectGcsaMenuBranchKeys(items: GcsaMenuItem[]): string[] {
34
+ const keys: string[] = [];
35
+ for (const item of items) {
36
+ if (item.type === 'group' || item.type === 'divider') continue;
37
+ if (item.children?.length) {
38
+ keys.push(item.key);
39
+ keys.push(...collectGcsaMenuBranchKeys(item.children));
40
+ }
41
+ }
42
+ return keys;
43
+ }
@@ -0,0 +1,3 @@
1
+ export type GcsaMessageType = 'success' | 'error' | 'info';
2
+
3
+ export const GCSA_MESSAGE_DURATION_MS = 3200;
@@ -0,0 +1,68 @@
1
+ export type GcsaPaginationItem = number | 'ellipsis-start' | 'ellipsis-end';
2
+
3
+ export type GcsaPaginationAlign = 'left' | 'center' | 'right';
4
+
5
+ export type GcsaPaginationLayoutPart = 'total' | 'sizes' | 'prev' | 'pager' | 'next' | 'jumper';
6
+
7
+ export const GCSA_PAGINATION_LAYOUT_PARTS = [
8
+ 'total',
9
+ 'sizes',
10
+ 'prev',
11
+ 'pager',
12
+ 'next',
13
+ 'jumper'
14
+ ] as const satisfies readonly GcsaPaginationLayoutPart[];
15
+
16
+ const paginationLayoutPartSet = new Set<string>(GCSA_PAGINATION_LAYOUT_PARTS);
17
+
18
+ export function parseGcsaPaginationLayout(
19
+ layout: string | readonly GcsaPaginationLayoutPart[]
20
+ ): GcsaPaginationLayoutPart[] {
21
+ const parts =
22
+ typeof layout === 'string'
23
+ ? layout
24
+ .split(',')
25
+ .map(part => part.trim())
26
+ .filter(Boolean)
27
+ : [...layout];
28
+
29
+ return parts.filter((part): part is GcsaPaginationLayoutPart => paginationLayoutPartSet.has(part));
30
+ }
31
+
32
+ export function resolveGcsaPaginationLayout(options: {
33
+ layout?: string | readonly GcsaPaginationLayoutPart[];
34
+ showPageSize?: boolean;
35
+ showJumper?: boolean;
36
+ }): GcsaPaginationLayoutPart[] {
37
+ if (options.layout) {
38
+ return parseGcsaPaginationLayout(options.layout);
39
+ }
40
+
41
+ const parts: GcsaPaginationLayoutPart[] = ['prev', 'pager', 'next'];
42
+ if (options.showPageSize) parts.push('sizes');
43
+ if (options.showJumper) parts.push('jumper');
44
+ return parts;
45
+ }
46
+
47
+ export function getGcsaPaginationAlignClass(align: GcsaPaginationAlign = 'right'): string {
48
+ return `gcsa-pagination--align-${align}`;
49
+ }
50
+
51
+ export function getGcsaPaginationItems(page: number, totalPages: number, maxVisible = 7): GcsaPaginationItem[] {
52
+ const count = Math.max(1, Math.floor(totalPages));
53
+ const current = Math.min(Math.max(1, Math.floor(page)), count);
54
+ const visible = Math.max(5, Math.floor(maxVisible));
55
+ if (count <= visible) return Array.from({ length: count }, (_, index) => index + 1);
56
+
57
+ const innerCount = visible - 2;
58
+ let start = Math.max(2, current - Math.floor(innerCount / 2));
59
+ let end = Math.min(count - 1, start + innerCount - 1);
60
+ start = Math.max(2, end - innerCount + 1);
61
+
62
+ const items: GcsaPaginationItem[] = [1];
63
+ if (start > 2) items.push('ellipsis-start');
64
+ for (let value = start; value <= end; value += 1) items.push(value);
65
+ if (end < count - 1) items.push('ellipsis-end');
66
+ items.push(count);
67
+ return items;
68
+ }
@@ -0,0 +1,111 @@
1
+ import {
2
+ computePosition,
3
+ flip,
4
+ limitShift,
5
+ offset,
6
+ shift,
7
+ size,
8
+ type Middleware,
9
+ type Placement
10
+ } from '@floating-ui/dom';
11
+ import {
12
+ createGcsaPopupPositioning,
13
+ type GcsaPopupPositioningOptions
14
+ } from './gcsaPopupPositioning';
15
+
16
+ export type GcsaPopupRect = {
17
+ x: number;
18
+ y: number;
19
+ width: number;
20
+ height: number;
21
+ };
22
+
23
+ export type GcsaViewportRect = {
24
+ width: number;
25
+ height: number;
26
+ };
27
+
28
+ /** 与 Zag popper get-placement 对齐的 middleware 组合 */
29
+ export function buildGcsaPopupMiddleware(
30
+ options: ReturnType<typeof createGcsaPopupPositioning>
31
+ ): Middleware[] {
32
+ const middleware: Middleware[] = [
33
+ offset(options.gutter),
34
+ flip({ padding: options.overflowPadding }),
35
+ shift({
36
+ padding: options.overflowPadding,
37
+ mainAxis: options.slide,
38
+ crossAxis: options.overlap,
39
+ limiter: limitShift()
40
+ }),
41
+ size({
42
+ padding: options.overflowPadding,
43
+ apply({ elements, rects, availableWidth, availableHeight }) {
44
+ const floating = elements.floating as HTMLElement;
45
+ floating.style.setProperty('--reference-width', `${Math.round(rects.reference.width)}px`);
46
+ floating.style.setProperty('--reference-height', `${Math.round(rects.reference.height)}px`);
47
+ floating.style.setProperty('--available-width', `${Math.floor(availableWidth)}px`);
48
+ floating.style.setProperty('--available-height', `${Math.floor(availableHeight)}px`);
49
+ if (options.sameWidth) {
50
+ floating.style.width = `${Math.round(rects.reference.width)}px`;
51
+ }
52
+ if (options.fitViewport) {
53
+ floating.style.maxWidth = `${Math.floor(availableWidth)}px`;
54
+ }
55
+ }
56
+ })
57
+ ];
58
+ return middleware;
59
+ }
60
+
61
+ export async function computeGcsaPopupPosition(
62
+ reference: Element,
63
+ floating: HTMLElement,
64
+ options: GcsaPopupPositioningOptions = {}
65
+ ) {
66
+ const config = createGcsaPopupPositioning(options);
67
+ return computePosition(reference, floating, {
68
+ placement: config.placement as Placement,
69
+ strategy: config.strategy,
70
+ middleware: buildGcsaPopupMiddleware(config)
71
+ });
72
+ }
73
+
74
+ export function getFloatingBox(
75
+ position: { x: number; y: number },
76
+ floating: Element
77
+ ): GcsaPopupRect {
78
+ const rect = floating.getBoundingClientRect();
79
+ return {
80
+ x: position.x,
81
+ y: position.y,
82
+ width: rect.width,
83
+ height: rect.height
84
+ };
85
+ }
86
+
87
+ /** 断言浮层在视口内并保留 overflowPadding */
88
+ export function assertFloatingWithinViewport(
89
+ box: GcsaPopupRect,
90
+ viewport: GcsaViewportRect,
91
+ overflowPadding = 8
92
+ ) {
93
+ const errors: string[] = [];
94
+ if (box.x < overflowPadding) {
95
+ errors.push(`left overflow: x=${box.x} < padding=${overflowPadding}`);
96
+ }
97
+ if (box.y < overflowPadding) {
98
+ errors.push(`top overflow: y=${box.y} < padding=${overflowPadding}`);
99
+ }
100
+ if (box.x + box.width > viewport.width - overflowPadding) {
101
+ errors.push(
102
+ `right overflow: x+w=${box.x + box.width} > viewport-padding=${viewport.width - overflowPadding}`
103
+ );
104
+ }
105
+ if (box.y + box.height > viewport.height - overflowPadding) {
106
+ errors.push(
107
+ `bottom overflow: y+h=${box.y + box.height} > viewport-padding=${viewport.height - overflowPadding}`
108
+ );
109
+ }
110
+ return errors;
111
+ }