@h-ai/ui 0.1.0-alpha.62 → 0.1.0-alpha.63
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/README.en.md +33 -7
- package/README.ja.md +33 -7
- package/README.md +33 -7
- package/auto-import.d.ts +1 -0
- package/auto-import.js +1 -0
- package/dist/components/compounds/Combobox.svelte +19 -6
- package/dist/components/compounds/Combobox.svelte.d.ts +2 -2
- package/dist/components/compounds/DatePicker.svelte +14 -5
- package/dist/components/compounds/DatePicker.svelte.d.ts +2 -2
- package/dist/components/compounds/Form.svelte +10 -6
- package/dist/components/compounds/FormField.svelte +78 -13
- package/dist/components/compounds/FormLayout.svelte +59 -0
- package/dist/components/compounds/FormLayout.svelte.d.ts +4 -0
- package/dist/components/compounds/TagInput.svelte +1 -1
- package/dist/components/compounds/index.d.ts +1 -0
- package/dist/components/compounds/index.js +1 -0
- package/dist/components/primitives/BareInput.svelte +7 -1
- package/dist/components/primitives/Checkbox.svelte +8 -2
- package/dist/components/primitives/Input.svelte +22 -11
- package/dist/components/primitives/Select.svelte +77 -18
- package/dist/components/primitives/Textarea.svelte +27 -13
- package/dist/components/primitives/Textarea.svelte.d.ts +1 -1
- package/dist/components/scenes/app/FeedbackModal.svelte +29 -19
- package/dist/components/scenes/app/FeedbackModal.svelte.d.ts +2 -2
- package/dist/components/scenes/crud/CrudEditPanel.svelte +148 -118
- package/dist/components/scenes/crud/CrudEditPanel.svelte.d.ts +4 -25
- package/dist/components/scenes/crud/CrudPage.svelte +5 -41
- package/dist/components/scenes/crud/CrudPage.svelte.d.ts +2 -53
- package/dist/components/scenes/crud/crud-controller.svelte.d.ts +4 -1
- package/dist/components/scenes/crud/crud-form-validation.d.ts +4 -0
- package/dist/components/scenes/crud/crud-form-validation.js +39 -0
- package/dist/components/scenes/crud/crud-types.d.ts +9 -42
- package/dist/components/scenes/iam/AuthPanel.svelte +55 -47
- package/dist/components/scenes/iam/ChangePasswordForm.svelte +57 -59
- package/dist/components/scenes/iam/ForgotPasswordForm.svelte +89 -84
- package/dist/components/scenes/iam/LoginForm.svelte +134 -132
- package/dist/components/scenes/iam/PasswordInput.svelte +25 -7
- package/dist/components/scenes/iam/PasswordInput.svelte.d.ts +2 -2
- package/dist/components/scenes/iam/RegisterForm.svelte +95 -97
- package/dist/components/scenes/iam/ResetPasswordForm.svelte +83 -85
- package/dist/components/scenes/iam/UserProfile.svelte +9 -5
- package/dist/components/scenes/storage/FileUpload.svelte +1 -1
- package/dist/components/scenes/storage/ImageUpload.svelte +1 -0
- package/dist/components/scenes/types.d.ts +9 -9
- package/dist/i18n.svelte.d.ts +2 -1
- package/dist/i18n.svelte.js +2 -1
- package/dist/internal/form-context.d.ts +12 -0
- package/dist/internal/form-context.js +9 -0
- package/dist/internal/form-validation.svelte.d.ts +16 -0
- package/dist/internal/form-validation.svelte.js +76 -0
- package/dist/internal/locale.d.ts +2 -0
- package/dist/internal/locale.js +22 -0
- package/dist/messages/en-US.json +13 -1
- package/dist/messages/zh-CN.json +13 -1
- package/dist/messages.js +4 -3
- package/dist/types.d.ts +57 -10
- package/package.json +7 -2
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
import { uiM } from '../../../messages.js'
|
|
9
9
|
import { cn, getDataAttributes } from '../../../utils.js'
|
|
10
10
|
import Alert from '../../compounds/Alert.svelte'
|
|
11
|
+
import FormField from '../../compounds/FormField.svelte'
|
|
12
|
+
import FormLayout from '../../compounds/FormLayout.svelte'
|
|
11
13
|
import Button from '../../primitives/Button.svelte'
|
|
12
14
|
import Input from '../../primitives/Input.svelte'
|
|
13
15
|
import AgreementNotice from './AgreementNotice.svelte'
|
|
@@ -16,6 +18,9 @@
|
|
|
16
18
|
|
|
17
19
|
const {
|
|
18
20
|
loading = false,
|
|
21
|
+
columns = 1,
|
|
22
|
+
labelPosition = 'top',
|
|
23
|
+
labelWidth = '8rem',
|
|
19
24
|
disabled = false,
|
|
20
25
|
showTitle = false,
|
|
21
26
|
showPasswordStrength = true,
|
|
@@ -165,114 +170,107 @@
|
|
|
165
170
|
</script>
|
|
166
171
|
|
|
167
172
|
<form {...dataAttributes} class={formClass} onsubmit={handleSubmit}>
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
<!-- 自定义头部 -->
|
|
174
|
-
{#if header}
|
|
175
|
-
<div class='register-form-header'>
|
|
176
|
-
{@render header()}
|
|
177
|
-
</div>
|
|
178
|
-
{/if}
|
|
179
|
-
|
|
180
|
-
{#if passwordPolicy}<p class='text-xs text-base-content/60'>{passwordPolicyHint(passwordPolicy)}</p>{/if}
|
|
181
|
-
{#if policyError}<p role='alert' class='text-sm text-error'>{policyError}</p>{/if}
|
|
173
|
+
<FormLayout {columns} {labelPosition} {labelWidth}>
|
|
174
|
+
<!-- 标题 -->
|
|
175
|
+
{#if showTitle}
|
|
176
|
+
<h2 class='text-xl font-semibold text-center mb-5'>{uiM('register_title')}</h2>
|
|
177
|
+
{/if}
|
|
182
178
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
<div class='space-y-1.5'>
|
|
188
|
-
<label class='text-sm font-medium text-base-content/70' for='register-password'>
|
|
189
|
-
{getFieldLabel('password')}
|
|
190
|
-
</label>
|
|
191
|
-
<PasswordInput
|
|
192
|
-
id='register-password'
|
|
193
|
-
bind:value={password}
|
|
194
|
-
placeholder={getFieldPlaceholder('password')}
|
|
195
|
-
autocomplete='new-password'
|
|
196
|
-
{disabled}
|
|
197
|
-
required
|
|
198
|
-
showStrength={showPasswordStrength}
|
|
199
|
-
minLength={minPasswordLength}
|
|
200
|
-
error={errors.password}
|
|
201
|
-
/>
|
|
179
|
+
<!-- 自定义头部 -->
|
|
180
|
+
{#if header}
|
|
181
|
+
<div class='register-form-header'>
|
|
182
|
+
{@render header()}
|
|
202
183
|
</div>
|
|
184
|
+
{/if}
|
|
203
185
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
186
|
+
{#if passwordPolicy}<p class='text-xs text-base-content/60'>{passwordPolicyHint(passwordPolicy)}</p>{/if}
|
|
187
|
+
{#if policyError}<p role='alert' class='text-sm text-error'>{policyError}</p>{/if}
|
|
188
|
+
|
|
189
|
+
<!-- 动态字段 -->
|
|
190
|
+
{#each fields as field (field)}
|
|
191
|
+
{#if field === 'password'}
|
|
192
|
+
<!-- 密码字段 -->
|
|
193
|
+
<FormField for='register-password' label={getFieldLabel('password')}>
|
|
210
194
|
<PasswordInput
|
|
211
|
-
id='register-
|
|
212
|
-
bind:value={
|
|
213
|
-
placeholder={getFieldPlaceholder('
|
|
195
|
+
id='register-password'
|
|
196
|
+
bind:value={password}
|
|
197
|
+
placeholder={getFieldPlaceholder('password')}
|
|
214
198
|
autocomplete='new-password'
|
|
215
199
|
{disabled}
|
|
216
200
|
required
|
|
217
|
-
showStrength={
|
|
218
|
-
|
|
201
|
+
showStrength={showPasswordStrength}
|
|
202
|
+
minLength={minPasswordLength}
|
|
203
|
+
error={errors.password}
|
|
204
|
+
/>
|
|
205
|
+
</FormField>
|
|
206
|
+
|
|
207
|
+
<!-- 确认密码字段 -->
|
|
208
|
+
{#if requireConfirmPassword}
|
|
209
|
+
<FormField for='register-confirm-password' label={getFieldLabel('confirmPassword')}>
|
|
210
|
+
<PasswordInput
|
|
211
|
+
id='register-confirm-password'
|
|
212
|
+
bind:value={confirmPassword}
|
|
213
|
+
placeholder={getFieldPlaceholder('confirmPassword')}
|
|
214
|
+
autocomplete='new-password'
|
|
215
|
+
{disabled}
|
|
216
|
+
required
|
|
217
|
+
showStrength={false}
|
|
218
|
+
error={confirmPasswordError}
|
|
219
|
+
/>
|
|
220
|
+
</FormField>
|
|
221
|
+
{/if}
|
|
222
|
+
{:else if field !== 'confirmPassword'}
|
|
223
|
+
<FormField for='register-{field}' label={getFieldLabel(field)}>
|
|
224
|
+
<Input
|
|
225
|
+
id={`register-${field}`}
|
|
226
|
+
type={getFieldType(field)}
|
|
227
|
+
name={field}
|
|
228
|
+
placeholder={getFieldPlaceholder(field)}
|
|
229
|
+
value={getFieldValue(field)}
|
|
230
|
+
oninput={(e: Event & { currentTarget: HTMLInputElement }) => setFieldValue(field, e.currentTarget.value)}
|
|
231
|
+
{disabled}
|
|
232
|
+
required={field === 'username' || field === 'email'}
|
|
233
|
+
autocomplete={field === 'username' ? 'username' : field === 'email' ? 'email' : undefined}
|
|
234
|
+
error={errors[field]}
|
|
219
235
|
/>
|
|
220
|
-
</
|
|
236
|
+
</FormField>
|
|
221
237
|
{/if}
|
|
222
|
-
{
|
|
223
|
-
<div class='space-y-1.5'>
|
|
224
|
-
<label class='text-sm font-medium text-base-content/70' for='register-{field}'>
|
|
225
|
-
{getFieldLabel(field)}
|
|
226
|
-
</label>
|
|
227
|
-
<Input
|
|
228
|
-
id={`register-${field}`}
|
|
229
|
-
type={getFieldType(field)}
|
|
230
|
-
name={field}
|
|
231
|
-
placeholder={getFieldPlaceholder(field)}
|
|
232
|
-
value={getFieldValue(field)}
|
|
233
|
-
oninput={(e: Event & { currentTarget: HTMLInputElement }) => setFieldValue(field, e.currentTarget.value)}
|
|
234
|
-
{disabled}
|
|
235
|
-
required={field === 'username' || field === 'email'}
|
|
236
|
-
autocomplete={field === 'username' ? 'username' : field === 'email' ? 'email' : undefined}
|
|
237
|
-
error={errors[field]}
|
|
238
|
-
/>
|
|
239
|
-
</div>
|
|
240
|
-
{/if}
|
|
241
|
-
{/each}
|
|
238
|
+
{/each}
|
|
242
239
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
240
|
+
<!-- 通用错误 -->
|
|
241
|
+
{#if extraFields}
|
|
242
|
+
<div class='register-form-extra-fields'>{@render extraFields()}</div>
|
|
243
|
+
{/if}
|
|
244
|
+
{#if errors.general}
|
|
245
|
+
<Alert variant='error'>
|
|
246
|
+
{errors.general}
|
|
247
|
+
</Alert>
|
|
248
|
+
{/if}
|
|
252
249
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
250
|
+
<!-- 提交按钮 -->
|
|
251
|
+
<Button
|
|
252
|
+
type='submit'
|
|
253
|
+
variant='primary'
|
|
254
|
+
class='w-full'
|
|
255
|
+
disabled={loading || disabled || !passwordsMatch || Boolean(policyError)}
|
|
256
|
+
loading={loading}
|
|
257
|
+
>
|
|
258
|
+
{submitText || uiM('register_submit')}
|
|
259
|
+
</Button>
|
|
263
260
|
|
|
264
|
-
|
|
265
|
-
|
|
261
|
+
<!-- 协议提示 -->
|
|
262
|
+
<AgreementNotice {agreements} />
|
|
266
263
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
264
|
+
<!-- 自定义底部 -->
|
|
265
|
+
{#if footer}
|
|
266
|
+
<div class='register-form-footer'>
|
|
267
|
+
{@render footer()}
|
|
268
|
+
</div>
|
|
269
|
+
{:else if showLoginLink}
|
|
270
|
+
<div class='text-center mt-4 text-sm text-base-content/60'>
|
|
271
|
+
<span>{uiM('register_has_account')}</span>
|
|
272
|
+
<a href={loginUrl} class='link link-primary ml-1'>{uiM('register_login_now')}</a>
|
|
273
|
+
</div>
|
|
274
|
+
{/if}
|
|
275
|
+
</FormLayout>
|
|
278
276
|
</form>
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
import { uiM } from '../../../messages.js'
|
|
9
9
|
import { cn, getDataAttributes } from '../../../utils.js'
|
|
10
10
|
import Alert from '../../compounds/Alert.svelte'
|
|
11
|
+
import FormField from '../../compounds/FormField.svelte'
|
|
12
|
+
import FormLayout from '../../compounds/FormLayout.svelte'
|
|
11
13
|
import Button from '../../primitives/Button.svelte'
|
|
12
14
|
import Input from '../../primitives/Input.svelte'
|
|
13
15
|
import { arePasswordsEqual, passwordPolicyError, passwordPolicyHint } from './password-utils.js'
|
|
@@ -15,6 +17,9 @@
|
|
|
15
17
|
|
|
16
18
|
const {
|
|
17
19
|
loading = false,
|
|
20
|
+
columns = 1,
|
|
21
|
+
labelPosition = 'top',
|
|
22
|
+
labelWidth = '8rem',
|
|
18
23
|
disabled = false,
|
|
19
24
|
showTitle = false,
|
|
20
25
|
showDescription = false,
|
|
@@ -72,91 +77,84 @@
|
|
|
72
77
|
</script>
|
|
73
78
|
|
|
74
79
|
<form {...dataAttributes} class={formClass} onsubmit={handleSubmit}>
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
<
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
80
|
+
<FormLayout {columns} {labelPosition} {labelWidth}>
|
|
81
|
+
<!-- 标题 -->
|
|
82
|
+
{#if showTitle}
|
|
83
|
+
<h2 class='text-xl font-semibold text-center mb-4'>{uiM('reset_password_title')}</h2>
|
|
84
|
+
{/if}
|
|
85
|
+
|
|
86
|
+
<!-- 描述 -->
|
|
87
|
+
{#if showDescription}
|
|
88
|
+
<p class='text-center text-base-content/45 text-sm mb-5'>
|
|
89
|
+
{uiM('reset_password_desc')}
|
|
90
|
+
</p>
|
|
91
|
+
{/if}
|
|
92
|
+
|
|
93
|
+
<!-- 验证码 -->
|
|
94
|
+
{#if showCode}
|
|
95
|
+
<FormField for='reset-code' label={uiM('reset_password_code_label')}>
|
|
96
|
+
<Input
|
|
97
|
+
id='reset-code'
|
|
98
|
+
name='code'
|
|
99
|
+
type='text'
|
|
100
|
+
placeholder={uiM('reset_password_code_placeholder')}
|
|
101
|
+
bind:value={code}
|
|
102
|
+
{disabled}
|
|
103
|
+
required
|
|
104
|
+
error={errors.code}
|
|
105
|
+
/>
|
|
106
|
+
</FormField>
|
|
107
|
+
{/if}
|
|
108
|
+
|
|
109
|
+
<!-- 新密码 -->
|
|
110
|
+
<FormField for='reset-new-password' label={uiM('reset_password_new_label')}>
|
|
111
|
+
<PasswordInput
|
|
112
|
+
bind:value={newPassword}
|
|
113
|
+
placeholder={uiM('reset_password_new_placeholder')}
|
|
99
114
|
{disabled}
|
|
100
|
-
|
|
101
|
-
|
|
115
|
+
error={errors.newPassword}
|
|
116
|
+
showStrength={showPasswordStrength}
|
|
117
|
+
minLength={minPasswordLength}
|
|
102
118
|
/>
|
|
103
|
-
</
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
<
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
<!-- 提交按钮 -->
|
|
146
|
-
<Button
|
|
147
|
-
type='submit'
|
|
148
|
-
variant='primary'
|
|
149
|
-
{loading}
|
|
150
|
-
disabled={loading || disabled || !canSubmit || Boolean(policyError)}
|
|
151
|
-
class='w-full'
|
|
152
|
-
>
|
|
153
|
-
{submitText || uiM('reset_password_submit')}
|
|
154
|
-
</Button>
|
|
155
|
-
|
|
156
|
-
<!-- 返回链接 -->
|
|
157
|
-
{#if showBackLink}
|
|
158
|
-
<div class='text-center mt-4 text-sm'>
|
|
159
|
-
<a href={loginUrl} class='link link-primary'>← {uiM('reset_password_back')}</a>
|
|
160
|
-
</div>
|
|
161
|
-
{/if}
|
|
119
|
+
</FormField>
|
|
120
|
+
|
|
121
|
+
<!-- 确认新密码 -->
|
|
122
|
+
<FormField for='reset-confirm-password' label={uiM('reset_password_confirm_label')}>
|
|
123
|
+
<PasswordInput
|
|
124
|
+
bind:value={confirmPassword}
|
|
125
|
+
placeholder={uiM('reset_password_confirm_placeholder')}
|
|
126
|
+
{disabled}
|
|
127
|
+
error={errors.confirmPassword || (!passwordsMatch && confirmPassword ? uiM('reset_password_mismatch') : '')}
|
|
128
|
+
showStrength={false}
|
|
129
|
+
/>
|
|
130
|
+
</FormField>
|
|
131
|
+
|
|
132
|
+
<!-- 通用错误 -->
|
|
133
|
+
{#if errors.general}
|
|
134
|
+
<Alert variant='error'>
|
|
135
|
+
{errors.general}
|
|
136
|
+
</Alert>
|
|
137
|
+
{/if}
|
|
138
|
+
|
|
139
|
+
{#if passwordPolicy}<p class='text-xs text-base-content/60'>{passwordPolicyHint(passwordPolicy)}</p>{/if}
|
|
140
|
+
{#if policyError}<p role='alert' class='text-sm text-error'>{policyError}</p>{/if}
|
|
141
|
+
|
|
142
|
+
<!-- 提交按钮 -->
|
|
143
|
+
<Button
|
|
144
|
+
type='submit'
|
|
145
|
+
variant='primary'
|
|
146
|
+
{loading}
|
|
147
|
+
disabled={loading || disabled || !canSubmit || Boolean(policyError)}
|
|
148
|
+
class='w-full'
|
|
149
|
+
>
|
|
150
|
+
{submitText || uiM('reset_password_submit')}
|
|
151
|
+
</Button>
|
|
152
|
+
|
|
153
|
+
<!-- 返回链接 -->
|
|
154
|
+
{#if showBackLink}
|
|
155
|
+
<div class='text-center mt-4 text-sm'>
|
|
156
|
+
<a href={loginUrl} class='link link-primary'>← {uiM('reset_password_back')}</a>
|
|
157
|
+
</div>
|
|
158
|
+
{/if}
|
|
159
|
+
</FormLayout>
|
|
162
160
|
</form>
|
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
import { uiM } from '../../../messages.js'
|
|
15
15
|
import { cn, getDataAttributes } from '../../../utils.js'
|
|
16
16
|
import Alert from '../../compounds/Alert.svelte'
|
|
17
|
+
import FormField from '../../compounds/FormField.svelte'
|
|
18
|
+
import FormLayout from '../../compounds/FormLayout.svelte'
|
|
17
19
|
import Avatar from '../../primitives/Avatar.svelte'
|
|
18
20
|
import BareInput from '../../primitives/BareInput.svelte'
|
|
19
21
|
import Button from '../../primitives/Button.svelte'
|
|
@@ -25,6 +27,9 @@
|
|
|
25
27
|
editable = false,
|
|
26
28
|
alwaysEditable = false,
|
|
27
29
|
loading = false,
|
|
30
|
+
columns = 1,
|
|
31
|
+
labelPosition = 'top',
|
|
32
|
+
labelWidth = '8rem',
|
|
28
33
|
fields = ['avatar', 'username', 'email', 'displayName', 'phone'],
|
|
29
34
|
avatarAccept = 'image/*',
|
|
30
35
|
avatarHint,
|
|
@@ -157,7 +162,7 @@
|
|
|
157
162
|
|
|
158
163
|
<div {...dataAttributes} class={containerClass}>
|
|
159
164
|
<form onsubmit={handleSubmit}>
|
|
160
|
-
<
|
|
165
|
+
<FormLayout {columns} {labelPosition} {labelWidth}>
|
|
161
166
|
{#each fields as field (field)}
|
|
162
167
|
{@const modelKey = getModelKey(field)}
|
|
163
168
|
{#if field === 'avatar'}
|
|
@@ -191,8 +196,7 @@
|
|
|
191
196
|
</div>
|
|
192
197
|
{:else}
|
|
193
198
|
<!-- 其他字段 -->
|
|
194
|
-
<
|
|
195
|
-
<legend class='fieldset-legend font-medium'>{getFieldLabel(field)}</legend>
|
|
199
|
+
<FormField label={getFieldLabel(field)} colSpan={field === 'bio' ? 2 : 'auto'}>
|
|
196
200
|
{#if editable && (editMode || alwaysEditable)}
|
|
197
201
|
{#if field === 'bio'}
|
|
198
202
|
<Textarea
|
|
@@ -216,7 +220,7 @@
|
|
|
216
220
|
{formData[modelKey] || '-'}
|
|
217
221
|
</p>
|
|
218
222
|
{/if}
|
|
219
|
-
</
|
|
223
|
+
</FormField>
|
|
220
224
|
{/if}
|
|
221
225
|
{/each}
|
|
222
226
|
|
|
@@ -259,6 +263,6 @@
|
|
|
259
263
|
{/if}
|
|
260
264
|
</div>
|
|
261
265
|
{/if}
|
|
262
|
-
</
|
|
266
|
+
</FormLayout>
|
|
263
267
|
</form>
|
|
264
268
|
</div>
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import type { HaiResult } from '@h-ai/core';
|
|
8
8
|
import type { Snippet } from 'svelte';
|
|
9
|
-
import type { DataAttributes, Size } from '../../types.js';
|
|
9
|
+
import type { DataAttributes, FormLayoutOptions, FormText, Size } from '../../types.js';
|
|
10
10
|
/**
|
|
11
11
|
* 密码输入框属性
|
|
12
12
|
*/
|
|
@@ -28,7 +28,7 @@ export interface PasswordInputProps extends DataAttributes {
|
|
|
28
28
|
/** 是否必填 */
|
|
29
29
|
required?: boolean;
|
|
30
30
|
/** 错误消息 */
|
|
31
|
-
error?:
|
|
31
|
+
error?: FormText;
|
|
32
32
|
/** 是否显示切换按钮 */
|
|
33
33
|
showToggle?: boolean;
|
|
34
34
|
/** 是否显示密码强度 */
|
|
@@ -74,7 +74,7 @@ export interface LoginFormData {
|
|
|
74
74
|
/**
|
|
75
75
|
* 登录表单属性
|
|
76
76
|
*/
|
|
77
|
-
export interface LoginFormProps extends DataAttributes {
|
|
77
|
+
export interface LoginFormProps extends FormLayoutOptions, DataAttributes {
|
|
78
78
|
/** 实际可用的方式;空数组表示无可用登录方式。 */
|
|
79
79
|
methods?: AuthLoginMethod[];
|
|
80
80
|
/** 验证码长度 */
|
|
@@ -136,7 +136,7 @@ export interface RegisterFormData {
|
|
|
136
136
|
/**
|
|
137
137
|
* 注册表单属性
|
|
138
138
|
*/
|
|
139
|
-
export interface RegisterFormProps extends DataAttributes {
|
|
139
|
+
export interface RegisterFormProps extends FormLayoutOptions, DataAttributes {
|
|
140
140
|
passwordPolicy?: PasswordPolicy;
|
|
141
141
|
/** 加载状态 */
|
|
142
142
|
loading?: boolean;
|
|
@@ -184,7 +184,7 @@ export interface ChangePasswordFormData {
|
|
|
184
184
|
/**
|
|
185
185
|
* 修改密码表单属性
|
|
186
186
|
*/
|
|
187
|
-
export interface ChangePasswordFormProps extends DataAttributes {
|
|
187
|
+
export interface ChangePasswordFormProps extends FormLayoutOptions, DataAttributes {
|
|
188
188
|
passwordPolicy?: PasswordPolicy;
|
|
189
189
|
/** 加载状态 */
|
|
190
190
|
loading?: boolean;
|
|
@@ -215,7 +215,7 @@ export interface ForgotPasswordFormData {
|
|
|
215
215
|
/**
|
|
216
216
|
* 找回密码表单属性
|
|
217
217
|
*/
|
|
218
|
-
export interface ForgotPasswordFormProps extends DataAttributes {
|
|
218
|
+
export interface ForgotPasswordFormProps extends FormLayoutOptions, DataAttributes {
|
|
219
219
|
/** 加载状态 */
|
|
220
220
|
loading?: boolean;
|
|
221
221
|
/** 禁用状态 */
|
|
@@ -254,7 +254,7 @@ export interface ResetPasswordFormData {
|
|
|
254
254
|
/**
|
|
255
255
|
* 重置密码表单属性
|
|
256
256
|
*/
|
|
257
|
-
export interface ResetPasswordFormProps extends DataAttributes {
|
|
257
|
+
export interface ResetPasswordFormProps extends FormLayoutOptions, DataAttributes {
|
|
258
258
|
passwordPolicy?: PasswordPolicy;
|
|
259
259
|
/** 加载状态 */
|
|
260
260
|
loading?: boolean;
|
|
@@ -355,7 +355,7 @@ export interface AuthPanelActions {
|
|
|
355
355
|
/** AuthPanel 的初始页面;未传独立路由时由组件内部导航。 */
|
|
356
356
|
export type AuthPanelMode = 'login' | 'register' | 'forgot' | 'reset';
|
|
357
357
|
/** 配置驱动认证场景;网络、通知和令牌存储由应用注入 actions。 */
|
|
358
|
-
export interface AuthPanelProps extends DataAttributes {
|
|
358
|
+
export interface AuthPanelProps extends FormLayoutOptions, DataAttributes {
|
|
359
359
|
/** 未取得配置时显示加载状态,只消费 IAM 的公开配置。 */
|
|
360
360
|
config: AuthPanelConfig | null;
|
|
361
361
|
/** 由 Kit 或 API client 提供标准动作,组件不处理底层传输。 */
|
|
@@ -408,7 +408,7 @@ export interface UserProfileSubmitData {
|
|
|
408
408
|
/**
|
|
409
409
|
* 用户个人信息属性
|
|
410
410
|
*/
|
|
411
|
-
export interface UserProfileProps extends DataAttributes {
|
|
411
|
+
export interface UserProfileProps extends FormLayoutOptions, DataAttributes {
|
|
412
412
|
/** 用户信息 */
|
|
413
413
|
user?: UserProfileData;
|
|
414
414
|
/** 是否可编辑 */
|
package/dist/i18n.svelte.d.ts
CHANGED
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
* @module i18n.svelte
|
|
6
6
|
*/
|
|
7
7
|
import type { Locale, LocaleInfo } from '@h-ai/core';
|
|
8
|
-
|
|
8
|
+
import { changeUILocale as setGlobalLocale } from './internal/locale.js';
|
|
9
|
+
declare const DEFAULT_LOCALE: string, DEFAULT_LOCALES: LocaleInfo[];
|
|
9
10
|
declare function isLocaleSupported(locale: Locale, supportedLocales?: LocaleInfo[]): boolean;
|
|
10
11
|
declare function resolveLocale(locale: Locale, supportedLocales?: LocaleInfo[], defaultLocale?: Locale): Locale;
|
|
11
12
|
declare function detectBrowserLocale(supportedLocales?: LocaleInfo[]): Locale | null;
|
package/dist/i18n.svelte.js
CHANGED
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { core } from '@h-ai/core';
|
|
8
8
|
import { readStoredValue, writeStoredValue } from './internal/browser-safety.js';
|
|
9
|
+
import { changeUILocale as setGlobalLocale } from './internal/locale.js';
|
|
9
10
|
// 从 core.i18n 解构常用函数
|
|
10
|
-
const { DEFAULT_LOCALE, DEFAULT_LOCALES,
|
|
11
|
+
const { DEFAULT_LOCALE, DEFAULT_LOCALES, } = core.i18n;
|
|
11
12
|
function isLocaleSupported(locale, supportedLocales = DEFAULT_LOCALES) {
|
|
12
13
|
const normalized = locale === 'en' ? 'en-US' : locale === 'zh' ? 'zh-CN' : locale;
|
|
13
14
|
return supportedLocales.some(item => item.code === normalized);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** 表单布局和字段提示的内部上下文,不要求应用转发控件属性。 */
|
|
2
|
+
import type { FormLayoutOptions } from '../types.js';
|
|
3
|
+
export declare const FORM_LAYOUT_CONTEXT: unique symbol;
|
|
4
|
+
export declare const FORM_FIELD_CONTEXT: unique symbol;
|
|
5
|
+
export interface FormFieldContext {
|
|
6
|
+
readonly controlId: string;
|
|
7
|
+
readonly labelId: string;
|
|
8
|
+
readonly messageId: string | undefined;
|
|
9
|
+
readonly error: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function getFormLayoutContext(): FormLayoutOptions | undefined;
|
|
12
|
+
export declare function getFormFieldContext(): FormFieldContext | undefined;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { getContext } from 'svelte';
|
|
2
|
+
export const FORM_LAYOUT_CONTEXT = Symbol('hai-form-layout');
|
|
3
|
+
export const FORM_FIELD_CONTEXT = Symbol('hai-form-field');
|
|
4
|
+
export function getFormLayoutContext() {
|
|
5
|
+
return getContext(FORM_LAYOUT_CONTEXT);
|
|
6
|
+
}
|
|
7
|
+
export function getFormFieldContext() {
|
|
8
|
+
return getContext(FORM_FIELD_CONTEXT);
|
|
9
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** 将原生校验状态保留为消息键,显示时按当前 UI 语言解析。 */
|
|
2
|
+
import type { UIMessageKey } from '../messages.js';
|
|
3
|
+
import type { FormText } from '../types.js';
|
|
4
|
+
export interface FormValidationIssue {
|
|
5
|
+
key: UIMessageKey;
|
|
6
|
+
params?: Record<string, string | number>;
|
|
7
|
+
}
|
|
8
|
+
type FormControl = HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement;
|
|
9
|
+
export declare function resolveFormText(text?: FormText): string;
|
|
10
|
+
export declare function createControlValidation(getOverride?: () => FormText | undefined, getValue?: () => unknown): {
|
|
11
|
+
readonly message: string;
|
|
12
|
+
invalid(control: FormControl): void;
|
|
13
|
+
input(control: FormControl): void;
|
|
14
|
+
reset: () => void;
|
|
15
|
+
};
|
|
16
|
+
export {};
|