@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,98 @@
1
+ <!--
2
+ @h-ai/ui — PullRefresh(下拉刷新)
3
+
4
+ 用法:
5
+ <PullRefresh onrefresh={async () => await loadData()} >
6
+ <div>列表内容</div>
7
+ </PullRefresh>
8
+ -->
9
+ <script lang='ts'>
10
+ import type { Snippet } from 'svelte'
11
+ import { cn } from '../../utils.js'
12
+
13
+ interface Props {
14
+ /** 刷新回调(需返回 Promise) */
15
+ onrefresh?: () => Promise<void>
16
+ /** 下拉触发距离(px) */
17
+ threshold?: number
18
+ /** 是否禁用 */
19
+ disabled?: boolean
20
+ /** 额外 CSS 类 */
21
+ class?: string
22
+ /** 内容 */
23
+ children: Snippet
24
+ }
25
+
26
+ const {
27
+ onrefresh,
28
+ threshold = 80,
29
+ disabled = false,
30
+ class: className,
31
+ children,
32
+ }: Props = $props()
33
+
34
+ let pulling = $state(false)
35
+ let refreshing = $state(false)
36
+ let pullDistance = $state(0)
37
+ let startY = 0
38
+
39
+ function handleTouchStart(e: TouchEvent) {
40
+ if (disabled || refreshing)
41
+ return
42
+ startY = e.touches[0].clientY
43
+ pulling = true
44
+ }
45
+
46
+ function handleTouchMove(e: TouchEvent) {
47
+ if (!pulling || disabled || refreshing)
48
+ return
49
+ const diff = e.touches[0].clientY - startY
50
+ if (diff > 0) {
51
+ pullDistance = Math.min(diff * 0.5, threshold * 1.5) // 阻尼
52
+ }
53
+ }
54
+
55
+ async function handleTouchEnd() {
56
+ if (!pulling || disabled)
57
+ return
58
+ pulling = false
59
+
60
+ if (pullDistance >= threshold && onrefresh) {
61
+ refreshing = true
62
+ try {
63
+ await onrefresh()
64
+ }
65
+ finally {
66
+ refreshing = false
67
+ }
68
+ }
69
+
70
+ pullDistance = 0
71
+ }
72
+ </script>
73
+
74
+ <div
75
+ class={cn('relative overflow-hidden', className)}
76
+ ontouchstart={handleTouchStart}
77
+ ontouchmove={handleTouchMove}
78
+ ontouchend={handleTouchEnd}
79
+ role='region'
80
+ >
81
+ <!-- 刷新指示器 -->
82
+ <div
83
+ class='flex items-center justify-center text-sm text-base-content/60 overflow-hidden transition-all'
84
+ style='height: {pullDistance}px'
85
+ >
86
+ {#if refreshing}
87
+ <span class='loading loading-spinner loading-sm mr-2'></span>
88
+ Refreshing...
89
+ {:else if pullDistance >= threshold}
90
+ Release to refresh
91
+ {:else if pullDistance > 0}
92
+ Pull to refresh
93
+ {/if}
94
+ </div>
95
+
96
+ <!-- 内容区域 -->
97
+ {@render children()}
98
+ </div>
@@ -0,0 +1,16 @@
1
+ import type { Snippet } from 'svelte';
2
+ interface Props {
3
+ /** 刷新回调(需返回 Promise) */
4
+ onrefresh?: () => Promise<void>;
5
+ /** 下拉触发距离(px) */
6
+ threshold?: number;
7
+ /** 是否禁用 */
8
+ disabled?: boolean;
9
+ /** 额外 CSS 类 */
10
+ class?: string;
11
+ /** 内容 */
12
+ children: Snippet;
13
+ }
14
+ declare const PullRefresh: import("svelte").Component<Props, {}, "">;
15
+ type PullRefresh = ReturnType<typeof PullRefresh>;
16
+ export default PullRefresh;
@@ -0,0 +1,84 @@
1
+ <!--
2
+ =============================================================================
3
+ @h-ai/ui - Result 组件
4
+ =============================================================================
5
+ 结果页展示组件,用于操作反馈
6
+
7
+ 使用 Svelte 5 Runes ($props, $derived)
8
+ =============================================================================
9
+ -->
10
+ <script lang='ts'>
11
+ import type { ResultProps } from '../../types.js'
12
+ import { cn } from '../../utils.js'
13
+
14
+ const {
15
+ status = 'info',
16
+ title = '',
17
+ description = '',
18
+ class: className = '',
19
+ icon,
20
+ actions,
21
+ children,
22
+ }: ResultProps = $props()
23
+
24
+ const containerClass = $derived(
25
+ cn(
26
+ 'flex flex-col items-center justify-center py-12 text-center',
27
+ className,
28
+ ),
29
+ )
30
+
31
+ const iconColor = $derived({
32
+ success: 'text-success',
33
+ error: 'text-error',
34
+ warning: 'text-warning',
35
+ info: 'text-info',
36
+ }[status])
37
+ </script>
38
+
39
+ <div class={containerClass}>
40
+ <!-- 图标区域 -->
41
+ <div class='w-20 h-20 mb-6 {iconColor}'>
42
+ {#if icon}
43
+ {@render icon()}
44
+ {:else if status === 'success'}
45
+ <svg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='currentColor' class='w-full h-full'>
46
+ <path stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z' />
47
+ </svg>
48
+ {:else if status === 'error'}
49
+ <svg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='currentColor' class='w-full h-full'>
50
+ <path stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M9.75 9.75l4.5 4.5m0-4.5l-4.5 4.5M21 12a9 9 0 11-18 0 9 9 0 0118 0z' />
51
+ </svg>
52
+ {:else if status === 'warning'}
53
+ <svg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='currentColor' class='w-full h-full'>
54
+ <path stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z' />
55
+ </svg>
56
+ {:else}
57
+ <svg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='currentColor' class='w-full h-full'>
58
+ <path stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9-3.75h.008v.008H12V8.25z' />
59
+ </svg>
60
+ {/if}
61
+ </div>
62
+
63
+ <!-- 标题 -->
64
+ <h2 class='text-2xl font-semibold text-base-content'>{title}</h2>
65
+
66
+ <!-- 描述 -->
67
+ {#if description}
68
+ <p class='mt-2 text-base-content/70 max-w-md'>{description}</p>
69
+ {/if}
70
+
71
+ <!-- 自定义内容 -->
72
+ {#if children}
73
+ <div class='mt-4'>
74
+ {@render children()}
75
+ </div>
76
+ {/if}
77
+
78
+ <!-- 操作按钮区域 -->
79
+ {#if actions}
80
+ <div class='mt-6 flex gap-3'>
81
+ {@render actions()}
82
+ </div>
83
+ {/if}
84
+ </div>
@@ -0,0 +1,4 @@
1
+ import type { ResultProps } from '../../types.js';
2
+ declare const Result: import("svelte").Component<ResultProps, {}, "">;
3
+ type Result = ReturnType<typeof Result>;
4
+ export default Result;
@@ -0,0 +1,37 @@
1
+ <!--
2
+ @h-ai/ui — SafeArea(安全区域包裹组件)
3
+
4
+ 用法:
5
+ <SafeArea position="top">
6
+ <AppBar />
7
+ </SafeArea>
8
+ -->
9
+ <script lang='ts'>
10
+ import type { Snippet } from 'svelte'
11
+ import { cn } from '../../utils.js'
12
+
13
+ interface Props {
14
+ /** 安全区域位置 */
15
+ position?: 'top' | 'bottom' | 'all'
16
+ /** 额外 CSS 类 */
17
+ class?: string
18
+ /** 内容 */
19
+ children: Snippet
20
+ }
21
+
22
+ const {
23
+ position = 'all',
24
+ class: className,
25
+ children,
26
+ }: Props = $props()
27
+
28
+ const positionClasses = $derived({
29
+ top: 'hai-safe-top',
30
+ bottom: 'hai-safe-bottom',
31
+ all: 'hai-safe-all',
32
+ }[position])
33
+ </script>
34
+
35
+ <div class={cn(positionClasses, className)}>
36
+ {@render children()}
37
+ </div>
@@ -0,0 +1,12 @@
1
+ import type { Snippet } from 'svelte';
2
+ interface Props {
3
+ /** 安全区域位置 */
4
+ position?: 'top' | 'bottom' | 'all';
5
+ /** 额外 CSS 类 */
6
+ class?: string;
7
+ /** 内容 */
8
+ children: Snippet;
9
+ }
10
+ declare const SafeArea: import("svelte").Component<Props, {}, "">;
11
+ type SafeArea = ReturnType<typeof SafeArea>;
12
+ export default SafeArea;
@@ -0,0 +1,75 @@
1
+ <!--
2
+ @component Skeleton
3
+ 骨架屏占位组件,用于加载状态展示。
4
+ -->
5
+ <script lang='ts'>
6
+ import type { SkeletonProps } from '../../types.js'
7
+ import { cn } from '../../utils.js'
8
+
9
+ const {
10
+ variant = 'text',
11
+ width = '',
12
+ height = '',
13
+ circle = false,
14
+ animation = true,
15
+ count = 1,
16
+ class: className = '',
17
+ }: SkeletonProps = $props()
18
+
19
+ const baseClass = $derived(
20
+ cn(
21
+ 'skeleton',
22
+ animation && 'animate-pulse',
23
+ circle && 'rounded-full',
24
+ variant === 'text' && 'h-4',
25
+ variant === 'title' && 'h-6 w-3/4',
26
+ variant === 'avatar' && 'w-10 h-10 rounded-full',
27
+ variant === 'thumbnail' && 'w-24 h-24 rounded-lg',
28
+ variant === 'button' && 'h-10 w-24 rounded-lg',
29
+ variant === 'input' && 'h-10 w-full rounded-lg',
30
+ className,
31
+ ),
32
+ )
33
+
34
+ const style = $derived(
35
+ [
36
+ width && `width: ${width}`,
37
+ height && `height: ${height}`,
38
+ ].filter(Boolean).join('; '),
39
+ )
40
+ </script>
41
+
42
+ {#if count > 1}
43
+ <div class='space-y-2'>
44
+ {#each Array.from({ length: count }) as _, i (i)}
45
+ <div class={baseClass} {style}></div>
46
+ {/each}
47
+ </div>
48
+ {:else}
49
+ <div class={baseClass} {style}></div>
50
+ {/if}
51
+
52
+ <style>
53
+ .skeleton {
54
+ background: linear-gradient(
55
+ 90deg,
56
+ oklch(var(--b3)) 25%,
57
+ oklch(var(--b2)) 50%,
58
+ oklch(var(--b3)) 75%
59
+ );
60
+ background-size: 200% 100%;
61
+ }
62
+
63
+ .skeleton.animate-pulse {
64
+ animation: skeleton-pulse 1.5s ease-in-out infinite;
65
+ }
66
+
67
+ @keyframes skeleton-pulse {
68
+ 0% {
69
+ background-position: 200% 0;
70
+ }
71
+ 100% {
72
+ background-position: -200% 0;
73
+ }
74
+ }
75
+ </style>
@@ -0,0 +1,8 @@
1
+ import type { SkeletonProps } from '../../types.js';
2
+ /**
3
+ * Skeleton
4
+ * 骨架屏占位组件,用于加载状态展示。
5
+ */
6
+ declare const Skeleton: import("svelte").Component<SkeletonProps, {}, "">;
7
+ type Skeleton = ReturnType<typeof Skeleton>;
8
+ export default Skeleton;
@@ -0,0 +1,111 @@
1
+ <!--
2
+ =============================================================================
3
+ @h-ai/ui - Steps 组件
4
+ =============================================================================
5
+ 步骤条组件
6
+
7
+ 使用 Svelte 5 Runes ($props, $derived)
8
+ =============================================================================
9
+ -->
10
+ <script lang='ts'>
11
+ import type { StepsProps } from '../../types.js'
12
+ import { cn } from '../../utils.js'
13
+
14
+ const {
15
+ items = [],
16
+ current = 0,
17
+ direction = 'horizontal',
18
+ size = 'md',
19
+ clickable = false,
20
+ class: className = '',
21
+ onchange,
22
+ }: StepsProps = $props()
23
+
24
+ const containerClass = $derived(
25
+ cn(
26
+ 'steps',
27
+ direction === 'vertical' && 'steps-vertical',
28
+ className,
29
+ ),
30
+ )
31
+
32
+ const stepSizeClass = $derived({
33
+ xs: 'step-xs',
34
+ sm: 'step-sm',
35
+ md: '',
36
+ lg: 'step-lg',
37
+ }[size] || '')
38
+
39
+ function getStepStatus(index: number): 'completed' | 'current' | 'pending' {
40
+ if (index < current)
41
+ return 'completed'
42
+ if (index === current)
43
+ return 'current'
44
+ return 'pending'
45
+ }
46
+
47
+ function getStepClass(index: number) {
48
+ const status = getStepStatus(index)
49
+ return cn(
50
+ 'step',
51
+ stepSizeClass,
52
+ status === 'completed' && 'step-primary',
53
+ status === 'current' && 'step-primary',
54
+ clickable && 'cursor-pointer hover:opacity-80',
55
+ )
56
+ }
57
+
58
+ function handleClick(index: number) {
59
+ if (clickable) {
60
+ onchange?.(index)
61
+ }
62
+ }
63
+ </script>
64
+
65
+ <ul class={containerClass}>
66
+ {#each items as item, index (index)}
67
+ {#if clickable}
68
+ <!-- svelte-ignore a11y_no_noninteractive_element_to_interactive_role -->
69
+ <li
70
+ class={getStepClass(index)}
71
+ data-content={item.icon || (index < current ? '✓' : index + 1)}
72
+ onclick={() => handleClick(index)}
73
+ onkeydown={e => e.key === 'Enter' && handleClick(index)}
74
+ role='button'
75
+ tabindex='0'
76
+ >
77
+ <div class='step-content'>
78
+ <span class='font-medium'>{item.title}</span>
79
+ {#if item.description}
80
+ <span class='text-xs text-base-content/60'>{item.description}</span>
81
+ {/if}
82
+ </div>
83
+ </li>
84
+ {:else}
85
+ <li
86
+ class={getStepClass(index)}
87
+ data-content={item.icon || (index < current ? '✓' : index + 1)}
88
+ >
89
+ <div class='step-content'>
90
+ <span class='font-medium'>{item.title}</span>
91
+ {#if item.description}
92
+ <span class='text-xs text-base-content/60'>{item.description}</span>
93
+ {/if}
94
+ </div>
95
+ </li>
96
+ {/if}
97
+ {/each}
98
+ </ul>
99
+
100
+ <style>
101
+ .step-content {
102
+ display: flex;
103
+ flex-direction: column;
104
+ align-items: flex-start;
105
+ text-align: left;
106
+ }
107
+
108
+ .steps-vertical .step-content {
109
+ margin-left: 0.5rem;
110
+ }
111
+ </style>
@@ -0,0 +1,4 @@
1
+ import type { StepsProps } from '../../types.js';
2
+ declare const Steps: import("svelte").Component<StepsProps, {}, "">;
3
+ type Steps = ReturnType<typeof Steps>;
4
+ export default Steps;
@@ -0,0 +1,115 @@
1
+ <!--
2
+ @h-ai/ui — SwipeCell(滑动操作 — 列表项左滑删除等)
3
+
4
+ 用法:
5
+ <SwipeCell
6
+ actions={[
7
+ { id: 'delete', label: '删除', variant: 'error' },
8
+ ]}
9
+ onaction={(id) => handleAction(id)}
10
+ >
11
+ <div class="p-4">列表项内容</div>
12
+ </SwipeCell>
13
+ -->
14
+ <script lang='ts'>
15
+ import type { Snippet } from 'svelte'
16
+ import type { SwipeCellAction } from './swipe-cell-types.js'
17
+ import { cn } from '../../utils.js'
18
+
19
+ // eslint-disable-next-line no-import-assign -- type re-export
20
+ export type { SwipeCellAction }
21
+
22
+ interface Props {
23
+ /** 右侧操作列表 */
24
+ actions: SwipeCellAction[]
25
+ /** 操作回调 */
26
+ onaction?: (id: string) => void
27
+ /** 是否禁用 */
28
+ disabled?: boolean
29
+ /** 额外 CSS 类 */
30
+ class?: string
31
+ /** 内容 */
32
+ children: Snippet
33
+ }
34
+
35
+ const {
36
+ actions,
37
+ onaction,
38
+ disabled = false,
39
+ class: className,
40
+ children,
41
+ }: Props = $props()
42
+
43
+ let offsetX = $state(0)
44
+ let startX = 0
45
+ let swiping = false
46
+
47
+ const totalActionWidth = $derived(actions.reduce((sum, a) => sum + (a.width ?? 72), 0))
48
+
49
+ function handleTouchStart(e: TouchEvent) {
50
+ if (disabled)
51
+ return
52
+ startX = e.touches[0].clientX
53
+ swiping = true
54
+ }
55
+
56
+ function handleTouchMove(e: TouchEvent) {
57
+ if (!swiping || disabled)
58
+ return
59
+ const diff = startX - e.touches[0].clientX
60
+ // 只允许左滑
61
+ offsetX = Math.max(0, Math.min(diff, totalActionWidth))
62
+ }
63
+
64
+ function handleTouchEnd() {
65
+ if (!swiping)
66
+ return
67
+ swiping = false
68
+ // 超过一半则展开,否则收回
69
+ offsetX = offsetX > totalActionWidth / 2 ? totalActionWidth : 0
70
+ }
71
+
72
+ function handleAction(id: string) {
73
+ onaction?.(id)
74
+ offsetX = 0
75
+ }
76
+
77
+ const variantBg: Record<string, string> = {
78
+ primary: 'bg-primary text-primary-content',
79
+ error: 'bg-error text-error-content',
80
+ warning: 'bg-warning text-warning-content',
81
+ info: 'bg-info text-info-content',
82
+ }
83
+ </script>
84
+
85
+ <div class={cn('relative overflow-hidden', className)}>
86
+ <!-- 内容层 -->
87
+ <div
88
+ class='relative transition-transform'
89
+ style='transform: translateX(-{offsetX}px)'
90
+ ontouchstart={handleTouchStart}
91
+ ontouchmove={handleTouchMove}
92
+ ontouchend={handleTouchEnd}
93
+ role='row'
94
+ tabindex='0'
95
+ >
96
+ {@render children()}
97
+ </div>
98
+
99
+ <!-- 操作按钮层 -->
100
+ <div class='absolute top-0 right-0 h-full flex' style='transform: translateX({totalActionWidth - offsetX}px)'>
101
+ {#each actions as action (action.id)}
102
+ <button
103
+ type='button'
104
+ class={cn(
105
+ 'flex items-center justify-center h-full text-sm font-medium',
106
+ variantBg[action.variant ?? 'primary'],
107
+ )}
108
+ style='width: {action.width ?? 72}px'
109
+ onclick={() => handleAction(action.id)}
110
+ >
111
+ {action.label}
112
+ </button>
113
+ {/each}
114
+ </div>
115
+ </div>
@@ -0,0 +1,19 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { SwipeCellAction } from './swipe-cell-types.js';
3
+ interface Props {
4
+ /** 右侧操作列表 */
5
+ actions: SwipeCellAction[];
6
+ /** 操作回调 */
7
+ onaction?: (id: string) => void;
8
+ /** 是否禁用 */
9
+ disabled?: boolean;
10
+ /** 额外 CSS 类 */
11
+ class?: string;
12
+ /** 内容 */
13
+ children: Snippet;
14
+ }
15
+ declare const SwipeCell: import("svelte").Component<Props, {
16
+ SwipeCellAction: typeof SwipeCellAction;
17
+ }, "">;
18
+ type SwipeCell = ReturnType<typeof SwipeCell>;
19
+ export default SwipeCell;
@@ -0,0 +1,79 @@
1
+ <!--
2
+ @component Tabs
3
+ 标签页组件,支持 line/card/pills 三种样式。
4
+ -->
5
+ <script lang='ts'>
6
+ import type { TabsProps } from '../../types.js'
7
+ import { cn } from '../../utils.js'
8
+ import BareButton from '../primitives/BareButton.svelte'
9
+
10
+ let {
11
+ items,
12
+ active = $bindable(''),
13
+ size = 'md',
14
+ type = 'line',
15
+ class: className = '',
16
+ onchange,
17
+ children,
18
+ }: TabsProps = $props()
19
+
20
+ // 默认选中第一个
21
+ $effect(() => {
22
+ if (!active && items.length > 0) {
23
+ active = items[0].key
24
+ }
25
+ })
26
+
27
+ const typeMap = {
28
+ line: 'tabs-border',
29
+ card: 'tabs-box',
30
+ pills: 'tabs-box',
31
+ }
32
+
33
+ const sizeMap = {
34
+ 'xs': 'tabs-xs',
35
+ 'sm': 'tabs-sm',
36
+ 'md': '',
37
+ 'lg': 'tabs-lg',
38
+ 'xl': 'tabs-lg',
39
+ '2xl': 'tabs-lg',
40
+ '3xl': 'tabs-lg',
41
+ '4xl': 'tabs-lg',
42
+ }
43
+
44
+ const tabsClass = $derived(
45
+ cn(
46
+ 'tabs',
47
+ typeMap[type],
48
+ sizeMap[size],
49
+ className,
50
+ ),
51
+ )
52
+
53
+ function handleSelect(key: string) {
54
+ active = key
55
+ onchange?.(key)
56
+ }
57
+ </script>
58
+
59
+ <div role='tablist' class={tabsClass}>
60
+ {#each items as item}
61
+ <BareButton
62
+ role='tab'
63
+ class={cn('tab', active === item.key && 'tab-active')}
64
+ disabled={item.disabled}
65
+ onclick={() => handleSelect(item.key)}
66
+ >
67
+ {#if item.icon}
68
+ <span class='mr-1'>{item.icon}</span>
69
+ {/if}
70
+ {item.label}
71
+ </BareButton>
72
+ {/each}
73
+ </div>
74
+
75
+ {#if children}
76
+ <div class='py-4'>
77
+ {@render children()}
78
+ </div>
79
+ {/if}
@@ -0,0 +1,8 @@
1
+ import type { TabsProps } from '../../types.js';
2
+ /**
3
+ * Tabs
4
+ * 标签页组件,支持 line/card/pills 三种样式。
5
+ */
6
+ declare const Tabs: import("svelte").Component<TabsProps, {}, "active">;
7
+ type Tabs = ReturnType<typeof Tabs>;
8
+ export default Tabs;