@h-ai/ui 0.1.0-alpha5

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 (234) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +488 -0
  3. package/auto-import.d.ts +96 -0
  4. package/auto-import.js +259 -0
  5. package/dist/components/compounds/Accordion.svelte +142 -0
  6. package/dist/components/compounds/Accordion.svelte.d.ts +25 -0
  7. package/dist/components/compounds/ActionSheet.svelte +116 -0
  8. package/dist/components/compounds/ActionSheet.svelte.d.ts +22 -0
  9. package/dist/components/compounds/Alert.svelte +67 -0
  10. package/dist/components/compounds/Alert.svelte.d.ts +8 -0
  11. package/dist/components/compounds/AppBar.svelte +77 -0
  12. package/dist/components/compounds/AppBar.svelte.d.ts +20 -0
  13. package/dist/components/compounds/BottomNav.svelte +75 -0
  14. package/dist/components/compounds/BottomNav.svelte.d.ts +18 -0
  15. package/dist/components/compounds/Breadcrumb.svelte +49 -0
  16. package/dist/components/compounds/Breadcrumb.svelte.d.ts +4 -0
  17. package/dist/components/compounds/Calendar.svelte +116 -0
  18. package/dist/components/compounds/Calendar.svelte.d.ts +47 -0
  19. package/dist/components/compounds/Card.svelte +77 -0
  20. package/dist/components/compounds/Card.svelte.d.ts +8 -0
  21. package/dist/components/compounds/Combobox.svelte +327 -0
  22. package/dist/components/compounds/Combobox.svelte.d.ts +69 -0
  23. package/dist/components/compounds/Confirm.svelte +90 -0
  24. package/dist/components/compounds/Confirm.svelte.d.ts +4 -0
  25. package/dist/components/compounds/DataTable.svelte +133 -0
  26. package/dist/components/compounds/DataTable.svelte.d.ts +69 -0
  27. package/dist/components/compounds/DatePicker.svelte +135 -0
  28. package/dist/components/compounds/DatePicker.svelte.d.ts +46 -0
  29. package/dist/components/compounds/Drawer.svelte +113 -0
  30. package/dist/components/compounds/Drawer.svelte.d.ts +4 -0
  31. package/dist/components/compounds/Dropdown.svelte +91 -0
  32. package/dist/components/compounds/Dropdown.svelte.d.ts +8 -0
  33. package/dist/components/compounds/Empty.svelte +83 -0
  34. package/dist/components/compounds/Empty.svelte.d.ts +4 -0
  35. package/dist/components/compounds/Form.svelte +96 -0
  36. package/dist/components/compounds/Form.svelte.d.ts +7 -0
  37. package/dist/components/compounds/FormField.svelte +56 -0
  38. package/dist/components/compounds/FormField.svelte.d.ts +4 -0
  39. package/dist/components/compounds/InfiniteScroll.svelte +82 -0
  40. package/dist/components/compounds/InfiniteScroll.svelte.d.ts +22 -0
  41. package/dist/components/compounds/Modal.svelte +123 -0
  42. package/dist/components/compounds/Modal.svelte.d.ts +8 -0
  43. package/dist/components/compounds/PageHeader.svelte +44 -0
  44. package/dist/components/compounds/PageHeader.svelte.d.ts +28 -0
  45. package/dist/components/compounds/Pagination.svelte +157 -0
  46. package/dist/components/compounds/Pagination.svelte.d.ts +4 -0
  47. package/dist/components/compounds/Popover.svelte +137 -0
  48. package/dist/components/compounds/Popover.svelte.d.ts +4 -0
  49. package/dist/components/compounds/PullRefresh.svelte +98 -0
  50. package/dist/components/compounds/PullRefresh.svelte.d.ts +16 -0
  51. package/dist/components/compounds/Result.svelte +84 -0
  52. package/dist/components/compounds/Result.svelte.d.ts +4 -0
  53. package/dist/components/compounds/SafeArea.svelte +37 -0
  54. package/dist/components/compounds/SafeArea.svelte.d.ts +12 -0
  55. package/dist/components/compounds/Skeleton.svelte +75 -0
  56. package/dist/components/compounds/Skeleton.svelte.d.ts +8 -0
  57. package/dist/components/compounds/Steps.svelte +111 -0
  58. package/dist/components/compounds/Steps.svelte.d.ts +4 -0
  59. package/dist/components/compounds/SwipeCell.svelte +115 -0
  60. package/dist/components/compounds/SwipeCell.svelte.d.ts +19 -0
  61. package/dist/components/compounds/Tabs.svelte +79 -0
  62. package/dist/components/compounds/Tabs.svelte.d.ts +8 -0
  63. package/dist/components/compounds/TagInput.svelte +108 -0
  64. package/dist/components/compounds/TagInput.svelte.d.ts +4 -0
  65. package/dist/components/compounds/Timeline.svelte +167 -0
  66. package/dist/components/compounds/Timeline.svelte.d.ts +25 -0
  67. package/dist/components/compounds/ToastContainer.svelte +59 -0
  68. package/dist/components/compounds/ToastContainer.svelte.d.ts +3 -0
  69. package/dist/components/compounds/Tooltip.svelte +41 -0
  70. package/dist/components/compounds/Tooltip.svelte.d.ts +4 -0
  71. package/dist/components/compounds/accordion-types.d.ts +16 -0
  72. package/dist/components/compounds/accordion-types.js +5 -0
  73. package/dist/components/compounds/action-sheet-types.d.ts +14 -0
  74. package/dist/components/compounds/action-sheet-types.js +5 -0
  75. package/dist/components/compounds/bottom-nav-types.d.ts +17 -0
  76. package/dist/components/compounds/bottom-nav-types.js +5 -0
  77. package/dist/components/compounds/index.d.ts +44 -0
  78. package/dist/components/compounds/index.js +54 -0
  79. package/dist/components/compounds/swipe-cell-types.d.ts +14 -0
  80. package/dist/components/compounds/swipe-cell-types.js +5 -0
  81. package/dist/components/compounds/timeline-types.d.ts +20 -0
  82. package/dist/components/compounds/timeline-types.js +5 -0
  83. package/dist/components/index.d.ts +9 -0
  84. package/dist/components/index.js +12 -0
  85. package/dist/components/primitives/Avatar.svelte +102 -0
  86. package/dist/components/primitives/Avatar.svelte.d.ts +8 -0
  87. package/dist/components/primitives/Badge.svelte +33 -0
  88. package/dist/components/primitives/Badge.svelte.d.ts +8 -0
  89. package/dist/components/primitives/BareButton.svelte +45 -0
  90. package/dist/components/primitives/BareButton.svelte.d.ts +4 -0
  91. package/dist/components/primitives/BareInput.svelte +101 -0
  92. package/dist/components/primitives/BareInput.svelte.d.ts +4 -0
  93. package/dist/components/primitives/Button.svelte +60 -0
  94. package/dist/components/primitives/Button.svelte.d.ts +8 -0
  95. package/dist/components/primitives/Checkbox.svelte +58 -0
  96. package/dist/components/primitives/Checkbox.svelte.d.ts +4 -0
  97. package/dist/components/primitives/IconButton.svelte +105 -0
  98. package/dist/components/primitives/IconButton.svelte.d.ts +4 -0
  99. package/dist/components/primitives/Input.svelte +130 -0
  100. package/dist/components/primitives/Input.svelte.d.ts +8 -0
  101. package/dist/components/primitives/Progress.svelte +58 -0
  102. package/dist/components/primitives/Progress.svelte.d.ts +4 -0
  103. package/dist/components/primitives/Radio.svelte +60 -0
  104. package/dist/components/primitives/Radio.svelte.d.ts +25 -0
  105. package/dist/components/primitives/Range.svelte +117 -0
  106. package/dist/components/primitives/Range.svelte.d.ts +27 -0
  107. package/dist/components/primitives/Rating.svelte +148 -0
  108. package/dist/components/primitives/Rating.svelte.d.ts +27 -0
  109. package/dist/components/primitives/Select.svelte +95 -0
  110. package/dist/components/primitives/Select.svelte.d.ts +25 -0
  111. package/dist/components/primitives/Spinner.svelte +54 -0
  112. package/dist/components/primitives/Spinner.svelte.d.ts +4 -0
  113. package/dist/components/primitives/Switch.svelte +53 -0
  114. package/dist/components/primitives/Switch.svelte.d.ts +4 -0
  115. package/dist/components/primitives/Tag.svelte +64 -0
  116. package/dist/components/primitives/Tag.svelte.d.ts +4 -0
  117. package/dist/components/primitives/Textarea.svelte +92 -0
  118. package/dist/components/primitives/Textarea.svelte.d.ts +4 -0
  119. package/dist/components/primitives/ToggleCheckbox.svelte +36 -0
  120. package/dist/components/primitives/ToggleCheckbox.svelte.d.ts +4 -0
  121. package/dist/components/primitives/ToggleInput.svelte +36 -0
  122. package/dist/components/primitives/ToggleInput.svelte.d.ts +4 -0
  123. package/dist/components/primitives/ToggleRadio.svelte +36 -0
  124. package/dist/components/primitives/ToggleRadio.svelte.d.ts +4 -0
  125. package/dist/components/primitives/index.d.ts +26 -0
  126. package/dist/components/primitives/index.js +30 -0
  127. package/dist/components/scenes/ai/AiDocumentDownloadMenu.svelte +387 -0
  128. package/dist/components/scenes/ai/AiDocumentDownloadMenu.svelte.d.ts +38 -0
  129. package/dist/components/scenes/ai/AiDocumentEditor.svelte +2259 -0
  130. package/dist/components/scenes/ai/AiDocumentEditor.svelte.d.ts +4 -0
  131. package/dist/components/scenes/ai/MarkdownRenderer.svelte +460 -0
  132. package/dist/components/scenes/ai/MarkdownRenderer.svelte.d.ts +4 -0
  133. package/dist/components/scenes/ai/document-download.d.ts +40 -0
  134. package/dist/components/scenes/ai/document-download.js +300 -0
  135. package/dist/components/scenes/ai/document-parse.d.ts +28 -0
  136. package/dist/components/scenes/ai/document-parse.js +243 -0
  137. package/dist/components/scenes/ai/document-types.d.ts +164 -0
  138. package/dist/components/scenes/ai/document-types.js +1 -0
  139. package/dist/components/scenes/ai/index.d.ts +17 -0
  140. package/dist/components/scenes/ai/index.js +13 -0
  141. package/dist/components/scenes/ai/markdown-parse.d.ts +32 -0
  142. package/dist/components/scenes/ai/markdown-parse.js +161 -0
  143. package/dist/components/scenes/app/FeedbackModal.svelte +160 -0
  144. package/dist/components/scenes/app/FeedbackModal.svelte.d.ts +47 -0
  145. package/dist/components/scenes/app/LanguageSwitch.svelte +136 -0
  146. package/dist/components/scenes/app/LanguageSwitch.svelte.d.ts +50 -0
  147. package/dist/components/scenes/app/SettingsModal.svelte +174 -0
  148. package/dist/components/scenes/app/SettingsModal.svelte.d.ts +31 -0
  149. package/dist/components/scenes/app/ThemeSelector.svelte +176 -0
  150. package/dist/components/scenes/app/ThemeSelector.svelte.d.ts +31 -0
  151. package/dist/components/scenes/app/ThemeToggle.svelte +76 -0
  152. package/dist/components/scenes/app/ThemeToggle.svelte.d.ts +29 -0
  153. package/dist/components/scenes/app/index.d.ts +11 -0
  154. package/dist/components/scenes/app/index.js +11 -0
  155. package/dist/components/scenes/crud/CrudDeleteConfirm.svelte +39 -0
  156. package/dist/components/scenes/crud/CrudDeleteConfirm.svelte.d.ts +18 -0
  157. package/dist/components/scenes/crud/CrudDetailDrawer.svelte +155 -0
  158. package/dist/components/scenes/crud/CrudDetailDrawer.svelte.d.ts +38 -0
  159. package/dist/components/scenes/crud/CrudEditDrawer.svelte +222 -0
  160. package/dist/components/scenes/crud/CrudEditDrawer.svelte.d.ts +48 -0
  161. package/dist/components/scenes/crud/CrudFilterBar.svelte +96 -0
  162. package/dist/components/scenes/crud/CrudFilterBar.svelte.d.ts +31 -0
  163. package/dist/components/scenes/crud/CrudPage.svelte +499 -0
  164. package/dist/components/scenes/crud/CrudPage.svelte.d.ts +114 -0
  165. package/dist/components/scenes/crud/crud-types.d.ts +135 -0
  166. package/dist/components/scenes/crud/crud-types.js +7 -0
  167. package/dist/components/scenes/crud/index.d.ts +12 -0
  168. package/dist/components/scenes/crud/index.js +11 -0
  169. package/dist/components/scenes/crypto/EncryptedInput.svelte +156 -0
  170. package/dist/components/scenes/crypto/EncryptedInput.svelte.d.ts +32 -0
  171. package/dist/components/scenes/crypto/HashDisplay.svelte +97 -0
  172. package/dist/components/scenes/crypto/HashDisplay.svelte.d.ts +4 -0
  173. package/dist/components/scenes/crypto/SignatureDisplay.svelte +161 -0
  174. package/dist/components/scenes/crypto/SignatureDisplay.svelte.d.ts +4 -0
  175. package/dist/components/scenes/crypto/index.d.ts +9 -0
  176. package/dist/components/scenes/crypto/index.js +11 -0
  177. package/dist/components/scenes/iam/ChangePasswordForm.svelte +134 -0
  178. package/dist/components/scenes/iam/ChangePasswordForm.svelte.d.ts +8 -0
  179. package/dist/components/scenes/iam/ForgotPasswordForm.svelte +147 -0
  180. package/dist/components/scenes/iam/ForgotPasswordForm.svelte.d.ts +4 -0
  181. package/dist/components/scenes/iam/LoginForm.svelte +191 -0
  182. package/dist/components/scenes/iam/LoginForm.svelte.d.ts +8 -0
  183. package/dist/components/scenes/iam/PasswordInput.svelte +203 -0
  184. package/dist/components/scenes/iam/PasswordInput.svelte.d.ts +49 -0
  185. package/dist/components/scenes/iam/PermGuard.svelte +69 -0
  186. package/dist/components/scenes/iam/PermGuard.svelte.d.ts +16 -0
  187. package/dist/components/scenes/iam/RegisterForm.svelte +269 -0
  188. package/dist/components/scenes/iam/RegisterForm.svelte.d.ts +8 -0
  189. package/dist/components/scenes/iam/ResetPasswordForm.svelte +153 -0
  190. package/dist/components/scenes/iam/ResetPasswordForm.svelte.d.ts +8 -0
  191. package/dist/components/scenes/iam/UserProfile.svelte +251 -0
  192. package/dist/components/scenes/iam/UserProfile.svelte.d.ts +4 -0
  193. package/dist/components/scenes/iam/index.d.ts +16 -0
  194. package/dist/components/scenes/iam/index.js +18 -0
  195. package/dist/components/scenes/iam/password-utils.d.ts +15 -0
  196. package/dist/components/scenes/iam/password-utils.js +19 -0
  197. package/dist/components/scenes/iam/permission-context.svelte.d.ts +90 -0
  198. package/dist/components/scenes/iam/permission-context.svelte.js +114 -0
  199. package/dist/components/scenes/index.d.ts +13 -0
  200. package/dist/components/scenes/index.js +20 -0
  201. package/dist/components/scenes/storage/AvatarUpload.svelte +218 -0
  202. package/dist/components/scenes/storage/AvatarUpload.svelte.d.ts +4 -0
  203. package/dist/components/scenes/storage/FileList.svelte +260 -0
  204. package/dist/components/scenes/storage/FileList.svelte.d.ts +4 -0
  205. package/dist/components/scenes/storage/FileUpload.svelte +400 -0
  206. package/dist/components/scenes/storage/FileUpload.svelte.d.ts +7 -0
  207. package/dist/components/scenes/storage/ImageUpload.svelte +285 -0
  208. package/dist/components/scenes/storage/ImageUpload.svelte.d.ts +4 -0
  209. package/dist/components/scenes/storage/index.d.ts +10 -0
  210. package/dist/components/scenes/storage/index.js +12 -0
  211. package/dist/components/scenes/types.d.ts +583 -0
  212. package/dist/components/scenes/types.js +7 -0
  213. package/dist/i18n.svelte.d.ts +44 -0
  214. package/dist/i18n.svelte.js +108 -0
  215. package/dist/index.d.ts +13 -0
  216. package/dist/index.js +19 -0
  217. package/dist/messages/en-US.json +242 -0
  218. package/dist/messages/zh-CN.json +242 -0
  219. package/dist/messages.d.ts +29 -0
  220. package/dist/messages.js +42 -0
  221. package/dist/styles/design-tokens.css +58 -0
  222. package/dist/styles/global.css +218 -0
  223. package/dist/styles/mobile.css +105 -0
  224. package/dist/theme-config.d.ts +92 -0
  225. package/dist/theme-config.js +122 -0
  226. package/dist/toast.svelte.d.ts +45 -0
  227. package/dist/toast.svelte.js +77 -0
  228. package/dist/types.d.ts +944 -0
  229. package/dist/types.js +7 -0
  230. package/dist/utils/platform.svelte.d.ts +54 -0
  231. package/dist/utils/platform.svelte.js +87 -0
  232. package/dist/utils.d.ts +46 -0
  233. package/dist/utils.js +100 -0
  234. package/package.json +69 -0
@@ -0,0 +1,82 @@
1
+ <!--
2
+ @h-ai/ui — InfiniteScroll(无限滚动 / 上拉加载更多)
3
+
4
+ 用法:
5
+ <InfiniteScroll onloadmore={async () => await loadNextPage()} hasMore={hasMoreData}>
6
+ {#each items as item}
7
+ <div>{item.name}</div>
8
+ {/each}
9
+ </InfiniteScroll>
10
+ -->
11
+ <script lang='ts'>
12
+ import type { Snippet } from 'svelte'
13
+ import { cn } from '../../utils.js'
14
+
15
+ interface Props {
16
+ /** 加载更多回调 */
17
+ onloadmore?: () => Promise<void>
18
+ /** 是否还有更多数据 */
19
+ hasMore?: boolean
20
+ /** 触发距离(距底部 px) */
21
+ threshold?: number
22
+ /** 是否禁用 */
23
+ disabled?: boolean
24
+ /** 加载中文本 */
25
+ loadingText?: string
26
+ /** 无更多数据文本 */
27
+ noMoreText?: string
28
+ /** 额外 CSS 类 */
29
+ class?: string
30
+ /** 列表内容 */
31
+ children: Snippet
32
+ }
33
+
34
+ const {
35
+ onloadmore,
36
+ hasMore = true,
37
+ threshold = 100,
38
+ disabled = false,
39
+ loadingText = 'Loading...',
40
+ noMoreText = 'No more data',
41
+ class: className,
42
+ children,
43
+ }: Props = $props()
44
+
45
+ let loading = $state(false)
46
+ let containerRef: HTMLDivElement | undefined = $state()
47
+
48
+ async function checkAndLoad() {
49
+ if (loading || disabled || !hasMore || !containerRef || !onloadmore)
50
+ return
51
+
52
+ const { scrollTop, scrollHeight, clientHeight } = containerRef
53
+ if (scrollHeight - scrollTop - clientHeight < threshold) {
54
+ loading = true
55
+ try {
56
+ await onloadmore()
57
+ }
58
+ finally {
59
+ loading = false
60
+ }
61
+ }
62
+ }
63
+ </script>
64
+
65
+ <div
66
+ bind:this={containerRef}
67
+ class={cn('overflow-y-auto', className)}
68
+ onscroll={checkAndLoad}
69
+ role='feed'
70
+ >
71
+ {@render children()}
72
+
73
+ <!-- 底部状态 -->
74
+ <div class='flex items-center justify-center py-4 text-sm text-base-content/50'>
75
+ {#if loading}
76
+ <span class='loading loading-spinner loading-sm mr-2'></span>
77
+ {loadingText}
78
+ {:else if !hasMore}
79
+ {noMoreText}
80
+ {/if}
81
+ </div>
82
+ </div>
@@ -0,0 +1,22 @@
1
+ import type { Snippet } from 'svelte';
2
+ interface Props {
3
+ /** 加载更多回调 */
4
+ onloadmore?: () => Promise<void>;
5
+ /** 是否还有更多数据 */
6
+ hasMore?: boolean;
7
+ /** 触发距离(距底部 px) */
8
+ threshold?: number;
9
+ /** 是否禁用 */
10
+ disabled?: boolean;
11
+ /** 加载中文本 */
12
+ loadingText?: string;
13
+ /** 无更多数据文本 */
14
+ noMoreText?: string;
15
+ /** 额外 CSS 类 */
16
+ class?: string;
17
+ /** 列表内容 */
18
+ children: Snippet;
19
+ }
20
+ declare const InfiniteScroll: import("svelte").Component<Props, {}, "">;
21
+ type InfiniteScroll = ReturnType<typeof InfiniteScroll>;
22
+ export default InfiniteScroll;
@@ -0,0 +1,123 @@
1
+ <!--
2
+ @component Modal
3
+ 模态框组件,支持 backdrop-blur 和 scale-in 动画。
4
+ -->
5
+ <script lang='ts'>
6
+ import type { ModalProps } from '../../types.js'
7
+ import { uiM } from '../../messages.js'
8
+ import { cn } from '../../utils.js'
9
+ import IconButton from '../primitives/IconButton.svelte'
10
+
11
+ let {
12
+ open = $bindable(false),
13
+ title = '',
14
+ size = 'md',
15
+ closeOnBackdrop = true,
16
+ closeOnEscape = true,
17
+ showClose = true,
18
+ class: className = '',
19
+ onclose,
20
+ header,
21
+ footer,
22
+ children,
23
+ }: ModalProps = $props()
24
+
25
+ const sizeMap: Record<string, string> = {
26
+ 'xs': 'max-w-xs',
27
+ 'sm': 'max-w-sm',
28
+ 'md': 'max-w-md',
29
+ 'lg': 'max-w-lg',
30
+ 'xl': 'max-w-xl',
31
+ '2xl': 'max-w-2xl',
32
+ '3xl': 'max-w-3xl',
33
+ '4xl': 'max-w-4xl',
34
+ 'full': 'max-w-full h-full',
35
+ }
36
+
37
+ const modalBoxClass = $derived(
38
+ cn(
39
+ 'modal-box border border-base-content/6 shadow-xl',
40
+ 'animate-[hai-scale-in_0.2s_cubic-bezier(0.16,1,0.3,1)]',
41
+ sizeMap[size],
42
+ className,
43
+ ),
44
+ )
45
+
46
+ function handleClose() {
47
+ open = false
48
+ onclose?.()
49
+ }
50
+
51
+ function handleBackdropClick() {
52
+ if (closeOnBackdrop) {
53
+ handleClose()
54
+ }
55
+ }
56
+
57
+ function handleKeydown(e: KeyboardEvent) {
58
+ if (closeOnEscape && e.key === 'Escape') {
59
+ handleClose()
60
+ }
61
+ }
62
+
63
+ // ESC 键处理
64
+ $effect(() => {
65
+ if (open) {
66
+ document.addEventListener('keydown', handleKeydown)
67
+ return () => document.removeEventListener('keydown', handleKeydown)
68
+ }
69
+ return undefined
70
+ })
71
+ </script>
72
+
73
+ <dialog class='modal backdrop-blur-xs' class:modal-open={open}>
74
+ <div class={modalBoxClass}>
75
+ <!-- 头部:标题 + 关闭按钮 -->
76
+ <div class='flex items-start justify-between gap-4'>
77
+ {#if header}
78
+ <div class='font-semibold text-base flex-1'>
79
+ {@render header()}
80
+ </div>
81
+ {:else if title}
82
+ <h3 class='font-semibold text-base flex-1'>{title}</h3>
83
+ {:else}
84
+ <div class='flex-1'></div>
85
+ {/if}
86
+
87
+ {#if showClose}
88
+ <IconButton
89
+ size='sm'
90
+ variant='ghost'
91
+ label={uiM('common_close')}
92
+ onclick={handleClose}
93
+ class='-mt-1 -mr-2'
94
+ >
95
+ {#snippet children()}
96
+ <span class='icon-[tabler--x] size-4.5'></span>
97
+ {/snippet}
98
+ </IconButton>
99
+ {/if}
100
+ </div>
101
+
102
+ <div class='py-4'>
103
+ {#if children}
104
+ {@render children()}
105
+ {/if}
106
+ </div>
107
+
108
+ {#if footer}
109
+ <div class='modal-action'>
110
+ {@render footer()}
111
+ </div>
112
+ {/if}
113
+ </div>
114
+
115
+ <button
116
+ type='button'
117
+ class='modal-backdrop'
118
+ aria-label={uiM('modal_close')}
119
+ onclick={handleBackdropClick}
120
+ >
121
+ {uiM('common_close')}
122
+ </button>
123
+ </dialog>
@@ -0,0 +1,8 @@
1
+ import type { ModalProps } from '../../types.js';
2
+ /**
3
+ * Modal
4
+ * 模态框组件,支持 backdrop-blur 和 scale-in 动画。
5
+ */
6
+ declare const Modal: import("svelte").Component<ModalProps, {}, "open">;
7
+ type Modal = ReturnType<typeof Modal>;
8
+ export default Modal;
@@ -0,0 +1,44 @@
1
+ <!--
2
+ @component PageHeader
3
+ 页面头部组件,包含标题、描述和操作按钮区域。
4
+
5
+ @prop {string} title - 页面标题
6
+ @prop {string} description - 页面描述
7
+ @prop {Snippet} actions - 右侧操作按钮插槽
8
+
9
+ @example
10
+ <PageHeader title="用户管理" description="管理系统用户">
11
+ {#snippet actions()}
12
+ <Button onclick={createUser}>
13
+ <span class='icon-[tabler--plus] size-4'></span>
14
+ 新建用户
15
+ </Button>
16
+ {/snippet}
17
+ </PageHeader>
18
+ -->
19
+ <script lang='ts'>
20
+ import type { Snippet } from 'svelte'
21
+
22
+ interface Props {
23
+ title: string
24
+ description?: string
25
+ actions?: Snippet
26
+ class?: string
27
+ }
28
+
29
+ const { title, description, actions, class: className = '' }: Props = $props()
30
+ </script>
31
+
32
+ <div class='flex flex-col sm:flex-row sm:items-center justify-between gap-3 mb-5 {className}'>
33
+ <div>
34
+ <h1 class='text-xl font-semibold tracking-tight'>{title}</h1>
35
+ {#if description}
36
+ <p class='text-sm text-base-content/45 mt-0.5'>{description}</p>
37
+ {/if}
38
+ </div>
39
+ {#if actions}
40
+ <div class='flex items-center gap-2'>
41
+ {@render actions()}
42
+ </div>
43
+ {/if}
44
+ </div>
@@ -0,0 +1,28 @@
1
+ import type { Snippet } from 'svelte';
2
+ interface Props {
3
+ title: string;
4
+ description?: string;
5
+ actions?: Snippet;
6
+ class?: string;
7
+ }
8
+ /**
9
+ * PageHeader
10
+ * 页面头部组件,包含标题、描述和操作按钮区域。
11
+ *
12
+ * @prop {string} title - 页面标题
13
+ * @prop {string} description - 页面描述
14
+ * @prop {Snippet} actions - 右侧操作按钮插槽
15
+ *
16
+ * @example
17
+ * <PageHeader title="用户管理" description="管理系统用户">
18
+ * {#snippet actions()}
19
+ * <Button onclick={createUser}>
20
+ * <span class='icon-[tabler--plus] size-4'></span>
21
+ * 新建用户
22
+ * </Button>
23
+ * {/snippet}
24
+ * </PageHeader>
25
+ */
26
+ declare const PageHeader: import("svelte").Component<Props, {}, "">;
27
+ type PageHeader = ReturnType<typeof PageHeader>;
28
+ export default PageHeader;
@@ -0,0 +1,157 @@
1
+ <!--
2
+ =============================================================================
3
+ @h-ai/ui - Pagination 组件
4
+ =============================================================================
5
+ 分页组件
6
+
7
+ 使用 Svelte 5 Runes ($props, $derived, $bindable)
8
+ =============================================================================
9
+ -->
10
+ <script lang='ts'>
11
+ import type { PaginationProps } from '../../types.js'
12
+ import { uiM } from '../../messages.js'
13
+ import { cn, getSizeClass } from '../../utils.js'
14
+ import BareButton from '../primitives/BareButton.svelte'
15
+
16
+ import Input from '../primitives/Input.svelte'
17
+
18
+ let {
19
+ page = $bindable(1),
20
+ total,
21
+ pageSize = 10,
22
+ size = 'md',
23
+ showTotal = true,
24
+ showJumper = false,
25
+ labels = {},
26
+ class: className = '',
27
+ onchange,
28
+ }: PaginationProps = $props()
29
+
30
+ // labels 优先,缺省回退到内置消息 uiM(...)
31
+
32
+ // 计算总页数
33
+ const totalPages = $derived(Math.ceil(total / pageSize))
34
+
35
+ // 生成页码列表
36
+ const pages = $derived(() => {
37
+ const result: (number | string)[] = []
38
+ const maxVisible = 7
39
+
40
+ if (totalPages <= maxVisible) {
41
+ for (let i = 1; i <= totalPages; i++) {
42
+ result.push(i)
43
+ }
44
+ }
45
+ else {
46
+ // 始终显示第一页
47
+ result.push(1)
48
+
49
+ if (page > 3) {
50
+ result.push('...')
51
+ }
52
+
53
+ // 中间页码
54
+ const start = Math.max(2, page - 1)
55
+ const end = Math.min(totalPages - 1, page + 1)
56
+
57
+ for (let i = start; i <= end; i++) {
58
+ result.push(i)
59
+ }
60
+
61
+ if (page < totalPages - 2) {
62
+ result.push('...')
63
+ }
64
+
65
+ // 始终显示最后一页
66
+ result.push(totalPages)
67
+ }
68
+
69
+ return result
70
+ })
71
+
72
+ const joinClass = $derived(
73
+ cn(
74
+ 'join',
75
+ className,
76
+ ),
77
+ )
78
+
79
+ const btnClass = $derived(
80
+ cn(
81
+ 'join-item btn',
82
+ getSizeClass(size),
83
+ ),
84
+ )
85
+
86
+ function goToPage(p: number) {
87
+ if (p >= 1 && p <= totalPages && p !== page) {
88
+ page = p
89
+ onchange?.(p)
90
+ }
91
+ }
92
+
93
+ let jumperValue = $state('')
94
+
95
+ function handleJump() {
96
+ const p = Number.parseInt(jumperValue, 10)
97
+ if (!Number.isNaN(p)) {
98
+ goToPage(p)
99
+ jumperValue = ''
100
+ }
101
+ }
102
+ </script>
103
+
104
+ <div class='flex items-center gap-4'>
105
+ {#if showTotal}
106
+ <span class='text-sm text-base-content/70'>
107
+ {(labels.total ?? uiM('pagination_total')).replace('{count}', String(total))}
108
+ </span>
109
+ {/if}
110
+
111
+ <div class={joinClass}>
112
+ <BareButton
113
+ class={btnClass}
114
+ disabled={page === 1}
115
+ onclick={() => goToPage(page - 1)}
116
+ >
117
+ «
118
+ </BareButton>
119
+
120
+ {#each pages() as p}
121
+ {#if typeof p === 'number'}
122
+ <BareButton
123
+ class={cn(btnClass, page === p && 'btn-active')}
124
+ onclick={() => goToPage(p)}
125
+ >
126
+ {p}
127
+ </BareButton>
128
+ {:else}
129
+ <BareButton class={cn(btnClass, 'btn-disabled')} disabled>...</BareButton>
130
+ {/if}
131
+ {/each}
132
+
133
+ <BareButton
134
+ class={btnClass}
135
+ disabled={page === totalPages}
136
+ onclick={() => goToPage(page + 1)}
137
+ >
138
+ »
139
+ </BareButton>
140
+ </div>
141
+
142
+ {#if showJumper}
143
+ <div class='flex items-center gap-2'>
144
+ <span class='text-sm'>{labels.jumpTo ?? uiM('pagination_jump_to')}</span>
145
+ <Input
146
+ type='number'
147
+ size='sm'
148
+ class='w-16'
149
+ min={1}
150
+ max={totalPages}
151
+ bind:value={jumperValue}
152
+ onkeydown={(e: KeyboardEvent & { currentTarget: HTMLInputElement }) => e.key === 'Enter' && handleJump()}
153
+ />
154
+ <span class='text-sm'>{labels.page ?? uiM('pagination_page')}</span>
155
+ </div>
156
+ {/if}
157
+ </div>
@@ -0,0 +1,4 @@
1
+ import type { PaginationProps } from '../../types.js';
2
+ declare const Pagination: import("svelte").Component<PaginationProps, {}, "page">;
3
+ type Pagination = ReturnType<typeof Pagination>;
4
+ export default Pagination;
@@ -0,0 +1,137 @@
1
+ <!--
2
+ =============================================================================
3
+ @h-ai/ui - Popover 组件
4
+ =============================================================================
5
+ 弹出层组件
6
+
7
+ 使用 Svelte 5 Runes ($props, $state, $effect)
8
+ =============================================================================
9
+ -->
10
+ <script lang='ts'>
11
+ import type { PopoverProps } from '../../types.js'
12
+ import { cn } from '../../utils.js'
13
+
14
+ let {
15
+ open = $bindable(false),
16
+ position = 'bottom',
17
+ trigger = 'click',
18
+ offset = 8,
19
+ class: className = '',
20
+ onopen,
21
+ onclose,
22
+ triggerContent,
23
+ children,
24
+ }: PopoverProps = $props()
25
+
26
+ let triggerElement = $state<HTMLDivElement | null>(null)
27
+ let popoverElement = $state<HTMLDivElement | null>(null)
28
+
29
+ const positionClass = $derived({
30
+ top: 'bottom-full left-1/2 -translate-x-1/2 mb-2',
31
+ bottom: 'top-full left-1/2 -translate-x-1/2 mt-2',
32
+ left: 'right-full top-1/2 -translate-y-1/2 mr-2',
33
+ right: 'left-full top-1/2 -translate-y-1/2 ml-2',
34
+ }[position])
35
+
36
+ const arrowClass = $derived({
37
+ top: 'top-full left-1/2 -translate-x-1/2 border-t-base-100 border-l-transparent border-r-transparent border-b-transparent',
38
+ bottom: 'bottom-full left-1/2 -translate-x-1/2 border-b-base-100 border-l-transparent border-r-transparent border-t-transparent',
39
+ left: 'left-full top-1/2 -translate-y-1/2 border-l-base-100 border-t-transparent border-r-transparent border-b-transparent',
40
+ right: 'right-full top-1/2 -translate-y-1/2 border-r-base-100 border-t-transparent border-l-transparent border-b-transparent',
41
+ }[position])
42
+
43
+ function handleOpen() {
44
+ open = true
45
+ onopen?.()
46
+ }
47
+
48
+ function handleClose() {
49
+ open = false
50
+ onclose?.()
51
+ }
52
+
53
+ function handleTriggerClick() {
54
+ if (trigger === 'click') {
55
+ if (open) {
56
+ handleClose()
57
+ }
58
+ else {
59
+ handleOpen()
60
+ }
61
+ }
62
+ }
63
+
64
+ function handleMouseEnter() {
65
+ if (trigger === 'hover') {
66
+ handleOpen()
67
+ }
68
+ }
69
+
70
+ function handleMouseLeave() {
71
+ if (trigger === 'hover') {
72
+ handleClose()
73
+ }
74
+ }
75
+
76
+ function handleClickOutside(e: MouseEvent) {
77
+ if (
78
+ open
79
+ && triggerElement
80
+ && popoverElement
81
+ && !triggerElement.contains(e.target as Node)
82
+ && !popoverElement.contains(e.target as Node)
83
+ ) {
84
+ handleClose()
85
+ }
86
+ }
87
+
88
+ $effect(() => {
89
+ if (open) {
90
+ document.addEventListener('click', handleClickOutside)
91
+ }
92
+ return () => {
93
+ document.removeEventListener('click', handleClickOutside)
94
+ }
95
+ })
96
+ </script>
97
+
98
+ <div
99
+ class={cn('relative inline-block', className)}
100
+ role='group'
101
+ onmouseenter={handleMouseEnter}
102
+ onmouseleave={handleMouseLeave}
103
+ >
104
+ <!-- 触发器 -->
105
+ <div
106
+ bind:this={triggerElement}
107
+ class='inline-block'
108
+ onclick={handleTriggerClick}
109
+ role='button'
110
+ tabindex='0'
111
+ onkeydown={e => e.key === 'Enter' && handleTriggerClick()}
112
+ >
113
+ {#if triggerContent}
114
+ {@render triggerContent()}
115
+ {/if}
116
+ </div>
117
+
118
+ <!-- 弹出内容 -->
119
+ {#if open}
120
+ <div
121
+ bind:this={popoverElement}
122
+ class={cn(
123
+ 'absolute z-50 bg-base-100 rounded-box shadow-lg border border-base-200 p-4',
124
+ positionClass,
125
+ )}
126
+ style='--offset: {offset}px'
127
+ role='dialog'
128
+ >
129
+ <!-- 箭头 -->
130
+ <div class={cn('absolute w-0 h-0 border-8', arrowClass)}></div>
131
+
132
+ {#if children}
133
+ {@render children()}
134
+ {/if}
135
+ </div>
136
+ {/if}
137
+ </div>
@@ -0,0 +1,4 @@
1
+ import type { PopoverProps } from '../../types.js';
2
+ declare const Popover: import("svelte").Component<PopoverProps, {}, "open">;
3
+ type Popover = ReturnType<typeof Popover>;
4
+ export default Popover;