@h-ai/ui 0.1.0-alpha.11 → 0.1.0-alpha.12
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.
- package/dist/components/compounds/Form.svelte +2 -0
- package/dist/components/compounds/Modal.svelte +251 -59
- package/dist/components/primitives/Button.svelte +2 -0
- package/dist/components/scenes/ai/AiDocumentEditor.svelte +352 -46
- package/dist/components/scenes/ai/MarkdownRenderer.svelte +2 -2
- package/dist/components/scenes/ai/document-download.js +249 -75
- package/dist/components/scenes/ai/document-parse.d.ts +8 -0
- package/dist/components/scenes/ai/document-parse.js +21 -6
- package/dist/components/scenes/ai/document-types.d.ts +4 -0
- package/dist/components/scenes/ai/index.d.ts +2 -0
- package/dist/components/scenes/ai/index.js +1 -0
- package/dist/components/scenes/ai/table/AiTableEditor.svelte +1640 -0
- package/dist/components/scenes/ai/table/AiTableEditor.svelte.d.ts +6 -0
- package/dist/components/scenes/ai/table/table-types.d.ts +101 -0
- package/dist/components/scenes/ai/table/table-types.js +1 -0
- package/dist/messages/en-US.json +8 -0
- package/dist/messages/zh-CN.json +8 -0
- package/dist/types.d.ts +10 -0
- package/package.json +4 -2
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
import { cn } from '../../utils.js'
|
|
13
13
|
|
|
14
14
|
const {
|
|
15
|
+
id = '',
|
|
15
16
|
class: className = '',
|
|
16
17
|
loading = false,
|
|
17
18
|
disabled = false,
|
|
@@ -86,6 +87,7 @@
|
|
|
86
87
|
|
|
87
88
|
<form
|
|
88
89
|
bind:this={formElement}
|
|
90
|
+
id={id || undefined}
|
|
89
91
|
class={formClass}
|
|
90
92
|
onsubmit={handleSubmit}
|
|
91
93
|
onreset={handleReset}
|
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
import type { ModalProps } from '../../types.js'
|
|
7
7
|
import { uiM } from '../../messages.js'
|
|
8
8
|
import { cn } from '../../utils.js'
|
|
9
|
-
import IconButton from '../primitives/IconButton.svelte'
|
|
10
9
|
|
|
11
10
|
let {
|
|
12
11
|
open = $bindable(false),
|
|
@@ -15,6 +14,9 @@
|
|
|
15
14
|
closeOnBackdrop = true,
|
|
16
15
|
closeOnEscape = true,
|
|
17
16
|
showClose = true,
|
|
17
|
+
width,
|
|
18
|
+
height,
|
|
19
|
+
bodyClass = '',
|
|
18
20
|
class: className = '',
|
|
19
21
|
onclose,
|
|
20
22
|
header,
|
|
@@ -22,22 +24,43 @@
|
|
|
22
24
|
children,
|
|
23
25
|
}: ModalProps = $props()
|
|
24
26
|
|
|
27
|
+
// modalElement 持有原生 dialog 引用;
|
|
28
|
+
// 只有调用 `showModal()` 进入 top-layer 后,弹框才不会被父级布局和 overflow 裁剪。
|
|
29
|
+
let modalElement: HTMLDialogElement | undefined = $state()
|
|
30
|
+
|
|
31
|
+
// 只要存在标题、自定义 header 或关闭按钮,就渲染顶部栏位,保证弹框结构稳定。
|
|
32
|
+
const hasHeader = $derived(Boolean(header || title || showClose))
|
|
25
33
|
const sizeMap: Record<string, string> = {
|
|
26
|
-
'xs': '
|
|
27
|
-
'sm': '
|
|
28
|
-
'md': '
|
|
29
|
-
'lg': '
|
|
30
|
-
'xl': '
|
|
31
|
-
'2xl': '
|
|
32
|
-
'3xl': '
|
|
33
|
-
'4xl': '
|
|
34
|
-
'full': '
|
|
34
|
+
'xs': 'hai-modal__panel--xs',
|
|
35
|
+
'sm': 'hai-modal__panel--sm',
|
|
36
|
+
'md': 'hai-modal__panel--md',
|
|
37
|
+
'lg': 'hai-modal__panel--lg',
|
|
38
|
+
'xl': 'hai-modal__panel--xl',
|
|
39
|
+
'2xl': 'hai-modal__panel--2xl',
|
|
40
|
+
'3xl': 'hai-modal__panel--3xl',
|
|
41
|
+
'4xl': 'hai-modal__panel--4xl',
|
|
42
|
+
'full': 'hai-modal__panel--full',
|
|
35
43
|
}
|
|
36
44
|
|
|
45
|
+
// width / height 允许业务侧按需传入 CSS 值,覆盖尺寸预设。
|
|
46
|
+
const panelStyle = $derived.by(() => {
|
|
47
|
+
const styles: string[] = []
|
|
48
|
+
|
|
49
|
+
if (typeof width === 'string' && width.trim().length > 0) {
|
|
50
|
+
styles.push(`--hai-modal-width: ${width.trim()}`)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (typeof height === 'string' && height.trim().length > 0) {
|
|
54
|
+
styles.push(`--hai-modal-height: ${height.trim()}`)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return styles.length > 0 ? styles.join('; ') : undefined
|
|
58
|
+
})
|
|
59
|
+
|
|
37
60
|
const modalBoxClass = $derived(
|
|
38
61
|
cn(
|
|
39
|
-
'
|
|
40
|
-
'
|
|
62
|
+
'hai-modal__panel flex min-h-0 flex-col overflow-hidden',
|
|
63
|
+
'bg-base-100 p-0',
|
|
41
64
|
sizeMap[size],
|
|
42
65
|
className,
|
|
43
66
|
),
|
|
@@ -48,76 +71,245 @@
|
|
|
48
71
|
onclose?.()
|
|
49
72
|
}
|
|
50
73
|
|
|
51
|
-
function
|
|
52
|
-
if (closeOnBackdrop) {
|
|
74
|
+
function handleDialogClick(event: MouseEvent) {
|
|
75
|
+
if (event.target === event.currentTarget && closeOnBackdrop) {
|
|
53
76
|
handleClose()
|
|
54
77
|
}
|
|
55
78
|
}
|
|
56
79
|
|
|
57
|
-
function
|
|
58
|
-
|
|
80
|
+
function handleDialogCancel(event: Event) {
|
|
81
|
+
// 原生 dialog 在按下 Escape 时会先触发 cancel;
|
|
82
|
+
// 这里统一接管关闭逻辑,避免浏览器直接 close 后跳过组件自己的 onclose 回调。
|
|
83
|
+
event.preventDefault()
|
|
84
|
+
if (closeOnEscape) {
|
|
59
85
|
handleClose()
|
|
60
86
|
}
|
|
61
87
|
}
|
|
62
88
|
|
|
63
|
-
// ESC 键处理
|
|
64
89
|
$effect(() => {
|
|
90
|
+
if (!modalElement) {
|
|
91
|
+
return
|
|
92
|
+
}
|
|
93
|
+
|
|
65
94
|
if (open) {
|
|
66
|
-
|
|
67
|
-
|
|
95
|
+
if (!modalElement.open) {
|
|
96
|
+
modalElement.showModal()
|
|
97
|
+
}
|
|
98
|
+
return
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (modalElement.open) {
|
|
102
|
+
modalElement.close()
|
|
68
103
|
}
|
|
69
|
-
return undefined
|
|
70
104
|
})
|
|
71
105
|
</script>
|
|
72
106
|
|
|
73
|
-
<dialog
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
107
|
+
<dialog
|
|
108
|
+
bind:this={modalElement}
|
|
109
|
+
class='hai-modal'
|
|
110
|
+
oncancel={handleDialogCancel}
|
|
111
|
+
onclick={handleDialogClick}
|
|
112
|
+
>
|
|
113
|
+
<div class={modalBoxClass} style={panelStyle}>
|
|
114
|
+
{#if hasHeader}
|
|
115
|
+
<!-- 顶部栏单独固定,保证滚动时标题和关闭操作始终可见。 -->
|
|
116
|
+
<div class='hai-modal__header flex flex-none items-start justify-between gap-4 px-6 py-5 sm:px-7'>
|
|
117
|
+
{#if header}
|
|
118
|
+
<div class='flex-1 font-semibold text-[1.05rem] text-base-content/92'>
|
|
119
|
+
{@render header()}
|
|
120
|
+
</div>
|
|
121
|
+
{:else if title}
|
|
122
|
+
<h3 class='flex-1 font-semibold text-[1.05rem] tracking-tight text-base-content/92'>
|
|
123
|
+
{title}
|
|
124
|
+
</h3>
|
|
125
|
+
{:else}
|
|
126
|
+
<div class='flex-1'></div>
|
|
127
|
+
{/if}
|
|
86
128
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
129
|
+
{#if showClose}
|
|
130
|
+
<button
|
|
131
|
+
type='button'
|
|
132
|
+
class='hai-modal__close'
|
|
133
|
+
aria-label={uiM('common_close')}
|
|
134
|
+
onclick={handleClose}
|
|
135
|
+
>
|
|
136
|
+
<svg viewBox='0 0 24 24' class='hai-modal__close-icon' aria-hidden='true'>
|
|
137
|
+
<path
|
|
138
|
+
d='M6 6l12 12M18 6L6 18'
|
|
139
|
+
fill='none'
|
|
140
|
+
stroke='currentColor'
|
|
141
|
+
stroke-width='1.9'
|
|
142
|
+
stroke-linecap='round'
|
|
143
|
+
></path>
|
|
144
|
+
</svg>
|
|
145
|
+
</button>
|
|
146
|
+
{/if}
|
|
147
|
+
</div>
|
|
148
|
+
{/if}
|
|
101
149
|
|
|
102
|
-
|
|
150
|
+
<!-- 主体区域独立滚动,避免长内容把头部和底部一起顶走。 -->
|
|
151
|
+
<div class={cn('min-h-0 flex-1 overflow-y-auto px-6 py-5 sm:px-7', bodyClass)}>
|
|
103
152
|
{#if children}
|
|
104
153
|
{@render children()}
|
|
105
154
|
{/if}
|
|
106
155
|
</div>
|
|
107
156
|
|
|
108
157
|
{#if footer}
|
|
109
|
-
|
|
110
|
-
|
|
158
|
+
<!-- 底栏固定在面板底部,用于承接确认/取消或状态摘要。 -->
|
|
159
|
+
<div class='hai-modal__footer flex flex-none items-center bg-base-100 px-6 py-4 sm:px-7'>
|
|
160
|
+
<div class='flex w-full items-center justify-end gap-3'>
|
|
161
|
+
{@render footer()}
|
|
162
|
+
</div>
|
|
111
163
|
</div>
|
|
112
164
|
{/if}
|
|
113
165
|
</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
166
|
</dialog>
|
|
167
|
+
|
|
168
|
+
<style>
|
|
169
|
+
dialog.hai-modal {
|
|
170
|
+
inset: 0;
|
|
171
|
+
width: 100vw;
|
|
172
|
+
min-width: 0;
|
|
173
|
+
max-width: none;
|
|
174
|
+
height: 100vh;
|
|
175
|
+
height: 100dvh;
|
|
176
|
+
max-height: none;
|
|
177
|
+
margin: 0;
|
|
178
|
+
padding: clamp(1rem, 2.6vw, 2rem);
|
|
179
|
+
border: 0;
|
|
180
|
+
background: transparent;
|
|
181
|
+
outline: none;
|
|
182
|
+
overflow: visible;
|
|
183
|
+
box-sizing: border-box;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
dialog.hai-modal[open] {
|
|
187
|
+
display: grid;
|
|
188
|
+
place-items: center;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
dialog.hai-modal::backdrop {
|
|
192
|
+
background: rgb(15 23 42 / 0.26);
|
|
193
|
+
background: color-mix(in srgb, var(--color-base-content) 22%, transparent);
|
|
194
|
+
backdrop-filter: blur(2px);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.hai-modal__panel {
|
|
198
|
+
width: var(--hai-modal-width, min(100%, 44rem));
|
|
199
|
+
max-width: min(100%, calc(100vw - clamp(2rem, 5.2vw, 4rem)));
|
|
200
|
+
height: var(--hai-modal-height, auto);
|
|
201
|
+
max-height: calc(100vh - clamp(2rem, 5.2vw, 4rem));
|
|
202
|
+
max-height: calc(100dvh - clamp(2rem, 5.2vw, 4rem));
|
|
203
|
+
border-radius: 1.4rem;
|
|
204
|
+
color: var(--color-base-content);
|
|
205
|
+
outline: none;
|
|
206
|
+
box-shadow:
|
|
207
|
+
0 34px 84px -42px color-mix(in srgb, var(--color-base-content) 24%, transparent),
|
|
208
|
+
0 14px 28px -22px color-mix(in srgb, var(--color-primary) 12%, transparent);
|
|
209
|
+
animation: hai-modal-enter 0.22s cubic-bezier(0.16, 1, 0.3, 1);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.hai-modal__header {
|
|
213
|
+
box-shadow: inset 0 -1px 0 color-mix(in srgb, var(--color-base-content) 6%, transparent);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.hai-modal__footer {
|
|
217
|
+
box-shadow: inset 0 1px 0 color-mix(in srgb, var(--color-base-content) 6%, transparent);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.hai-modal__close {
|
|
221
|
+
display: inline-flex;
|
|
222
|
+
width: 2.25rem;
|
|
223
|
+
height: 2.25rem;
|
|
224
|
+
flex: none;
|
|
225
|
+
align-items: center;
|
|
226
|
+
justify-content: center;
|
|
227
|
+
border: 0;
|
|
228
|
+
border-radius: 999px;
|
|
229
|
+
background: color-mix(in srgb, var(--color-base-content) 4%, transparent);
|
|
230
|
+
color: color-mix(in srgb, var(--color-base-content) 54%, transparent);
|
|
231
|
+
cursor: pointer;
|
|
232
|
+
transition:
|
|
233
|
+
background-color 160ms ease,
|
|
234
|
+
color 160ms ease,
|
|
235
|
+
transform 160ms ease;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.hai-modal__close:hover {
|
|
239
|
+
background: color-mix(in srgb, var(--color-primary) 10%, transparent);
|
|
240
|
+
color: var(--color-primary);
|
|
241
|
+
transform: translateY(-1px);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
.hai-modal__close:focus-visible {
|
|
245
|
+
outline: 2px solid color-mix(in srgb, var(--color-primary) 22%, transparent);
|
|
246
|
+
outline-offset: 2px;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
.hai-modal__close-icon {
|
|
250
|
+
width: 1rem;
|
|
251
|
+
height: 1rem;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
.hai-modal__panel--xs {
|
|
255
|
+
--hai-modal-width: min(100%, 24rem);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
.hai-modal__panel--sm {
|
|
259
|
+
--hai-modal-width: min(100%, 32rem);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
.hai-modal__panel--md {
|
|
263
|
+
--hai-modal-width: min(100%, 40rem);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
.hai-modal__panel--lg {
|
|
267
|
+
--hai-modal-width: min(100%, 48rem);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
.hai-modal__panel--xl {
|
|
271
|
+
--hai-modal-width: min(100%, 64rem);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
.hai-modal__panel--2xl {
|
|
275
|
+
--hai-modal-width: min(100%, 72rem);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
.hai-modal__panel--3xl {
|
|
279
|
+
--hai-modal-width: min(100%, 80rem);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
.hai-modal__panel--4xl {
|
|
283
|
+
--hai-modal-width: min(100%, 88rem);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
.hai-modal__panel--full {
|
|
287
|
+
--hai-modal-width: min(96vw, 96rem);
|
|
288
|
+
--hai-modal-height: min(92vh, 72rem);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
@keyframes hai-modal-enter {
|
|
292
|
+
from {
|
|
293
|
+
opacity: 0;
|
|
294
|
+
transform: translateY(10px) scale(0.985);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
to {
|
|
298
|
+
opacity: 1;
|
|
299
|
+
transform: translateY(0) scale(1);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
@media (max-width: 768px) {
|
|
304
|
+
dialog.hai-modal {
|
|
305
|
+
padding: 0.85rem;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
.hai-modal__panel {
|
|
309
|
+
max-width: 100%;
|
|
310
|
+
max-height: calc(100vh - 1.7rem);
|
|
311
|
+
max-height: calc(100dvh - 1.7rem);
|
|
312
|
+
border-radius: 1rem;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
</style>
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
const {
|
|
10
10
|
variant = 'default',
|
|
11
11
|
size = 'md',
|
|
12
|
+
form = '',
|
|
12
13
|
ariaLabel = '',
|
|
13
14
|
disabled = false,
|
|
14
15
|
loading = false,
|
|
@@ -46,6 +47,7 @@
|
|
|
46
47
|
|
|
47
48
|
<button
|
|
48
49
|
{type}
|
|
50
|
+
form={form || undefined}
|
|
49
51
|
class={buttonClass}
|
|
50
52
|
disabled={disabled || loading}
|
|
51
53
|
aria-label={ariaLabel || undefined}
|