@svadmin/ui 0.85.0 → 0.88.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -0
- package/dist/app-shell.css +216 -0
- package/dist/app.css +659 -165
- package/dist/app.theme.css +659 -165
- package/dist/component-registry.d.ts +7292 -529
- package/dist/component-registry.js +32 -3
- package/dist/components/AppShellPage.svelte +172 -0
- package/dist/components/AppShellPage.svelte.d.ts +30 -0
- package/dist/components/ApprovalActionCard.svelte +54 -31
- package/dist/components/ApprovalCenter.svelte +73 -31
- package/dist/components/AuthPageShell.svelte +13 -2
- package/dist/components/AutoForm.svelte +7 -12
- package/dist/components/AutoForm.svelte.d.ts +0 -1
- package/dist/components/AutoTable.svelte +15 -3
- package/dist/components/AutoTable.svelte.d.ts +2 -0
- package/dist/components/ConfigurationPage.svelte +0 -3
- package/dist/components/ConfigurationPage.svelte.d.ts +0 -1
- package/dist/components/CreatePage.svelte +22 -19
- package/dist/components/CreatePage.svelte.d.ts +4 -1
- package/dist/components/EditPage.svelte +23 -20
- package/dist/components/EditPage.svelte.d.ts +4 -1
- package/dist/components/FieldRenderer.svelte +2 -10
- package/dist/components/FieldRenderer.svelte.d.ts +0 -1
- package/dist/components/ListPage.svelte +21 -18
- package/dist/components/ListPage.svelte.d.ts +2 -1
- package/dist/components/LoginPage.svelte +27 -11
- package/dist/components/LoginPage.svelte.d.ts +4 -0
- package/dist/components/NotificationsSettings.svelte +16 -8
- package/dist/components/PageHeader.svelte +1 -3
- package/dist/components/PageHeader.svelte.d.ts +0 -1
- package/dist/components/PagePattern.svelte +36 -13
- package/dist/components/PagePattern.svelte.d.ts +7 -0
- package/dist/components/PasswordInput.svelte +36 -17
- package/dist/components/PasswordInput.svelte.d.ts +3 -0
- package/dist/components/QuickEditDrawer.svelte +0 -1
- package/dist/components/ShowPage.svelte +24 -29
- package/dist/components/ShowPage.svelte.d.ts +0 -1
- package/dist/components/SvarAutoTable.svelte +2 -1
- package/dist/components/SvarDataGrid.svelte +8 -4
- package/dist/components/SvarDataGrid.svelte.d.ts +3 -0
- package/dist/components/SvarResourceTable.svelte +0 -1
- package/dist/components/SvarResourceTable.svelte.d.ts +0 -1
- package/dist/components/SvarResourceTableView.svelte +2 -2
- package/dist/components/account/MemberDirectory.svelte +17 -4
- package/dist/components/account/SecurityLogPage.svelte +8 -1
- package/dist/components/auto-table-grid.d.ts +1 -0
- package/dist/components/content/AuditTimeline.svelte +32 -4
- package/dist/components/content/AuditTimeline.svelte.d.ts +9 -0
- package/dist/components/content/ContentPageHeader.svelte +2 -1
- package/dist/components/content/ContentPageHeader.svelte.d.ts +1 -0
- package/dist/components/content/ContentPageShell.svelte +1 -1
- package/dist/components/content/ContentPageShell.svelte.d.ts +1 -0
- package/dist/components/content/DataPanel.svelte +58 -0
- package/dist/components/content/DataPanel.svelte.d.ts +16 -0
- package/dist/components/content/DataState.svelte +5 -4
- package/dist/components/content/DataState.svelte.d.ts +1 -1
- package/dist/components/content/DescriptionList.svelte +5 -8
- package/dist/components/content/DescriptionList.svelte.d.ts +0 -1
- package/dist/components/content/FilterToolbar.svelte +9 -6
- package/dist/components/content/FilterToolbar.svelte.d.ts +0 -1
- package/dist/components/content/NetworkTable.svelte +3 -3
- package/dist/components/content/NetworkTable.svelte.d.ts +0 -1
- package/dist/components/content/PageSection.svelte +1 -1
- package/dist/components/content/StatusTabs.svelte +2 -7
- package/dist/components/content/StatusTabs.svelte.d.ts +0 -1
- package/dist/components/content/WorkspaceLayout.svelte +3 -1
- package/dist/components/content/WorkspaceLayout.svelte.d.ts +1 -0
- package/dist/components/content/index.d.ts +1 -0
- package/dist/components/content/index.js +1 -0
- package/dist/components/network/TeamCrewTablePage.svelte +8 -1
- package/dist/components/network/UserCardsNFTPage.svelte +3 -1
- package/dist/components/profile/ProjectsGrid.svelte +1 -1
- package/dist/components/profile/TeamsShowcase.svelte +1 -1
- package/dist/components/ui/table/index.d.ts +0 -1
- package/dist/components/ui/table/table.svelte +1 -0
- package/dist/components/ui/table/table.svelte.d.ts +1 -0
- package/dist/components.css +1 -11
- package/dist/data-panel.css +88 -0
- package/dist/design-contract.d.ts +11 -0
- package/dist/design-contract.js +8 -0
- package/dist/design-system.d.ts +3 -2
- package/dist/design-system.js +3 -2
- package/dist/index.d.ts +4 -2
- package/dist/index.js +2 -0
- package/dist/page-chrome.css +42 -64
- package/dist/recipes.d.ts +12 -66
- package/dist/recipes.js +52 -50
- package/dist/styles/recipes.css +15 -48
- package/package.json +4 -4
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { ChevronLeft, ChevronRight,
|
|
2
|
+
import { ChevronLeft, ChevronRight, RefreshCw, Search } from '@lucide/svelte';
|
|
3
3
|
import { onDestroy, untrack } from 'svelte';
|
|
4
4
|
import { captureAdminContext, captureAuthSession, createEnterpriseRequestContext } from '@svadmin/core';
|
|
5
5
|
import {
|
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
import { Badge } from './ui/badge/index.js';
|
|
13
13
|
import { Button } from './ui/button/index.js';
|
|
14
14
|
import { Input } from './ui/input/index.js';
|
|
15
|
+
import * as Dialog from './ui/dialog/index.js';
|
|
16
|
+
import DataState from './content/DataState.svelte';
|
|
15
17
|
|
|
16
18
|
type ApprovalView = 'pending' | 'history';
|
|
17
19
|
interface Props {
|
|
@@ -37,6 +39,7 @@
|
|
|
37
39
|
let comment = $state('');
|
|
38
40
|
let targetId = $state('');
|
|
39
41
|
let actionDialog = $state<string>();
|
|
42
|
+
let actionOpen = $state(false);
|
|
40
43
|
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
41
44
|
let mounted = true;
|
|
42
45
|
let epoch = 0;
|
|
@@ -48,6 +51,15 @@
|
|
|
48
51
|
const activeAction = $derived(
|
|
49
52
|
activeRecord?.allowedActions.find(action => action.id === actionDialog),
|
|
50
53
|
);
|
|
54
|
+
const centerState = $derived(
|
|
55
|
+
loading
|
|
56
|
+
? 'loading'
|
|
57
|
+
: error && records.length === 0
|
|
58
|
+
? 'error'
|
|
59
|
+
: records.length === 0
|
|
60
|
+
? (appliedSearch ? 'filtered-empty' : 'empty')
|
|
61
|
+
: 'ready',
|
|
62
|
+
);
|
|
51
63
|
const normalizedPageSize = $derived(Number.isSafeInteger(pageSize) && pageSize > 0 && pageSize <= 200 ? pageSize : 20);
|
|
52
64
|
const totalPages = $derived(Math.max(1, Math.ceil(total / normalizedPageSize)));
|
|
53
65
|
|
|
@@ -74,6 +86,8 @@
|
|
|
74
86
|
records = result.data;
|
|
75
87
|
total = result.total;
|
|
76
88
|
selectedId = result.data.some(record => record.id === selectedId) ? selectedId : result.data[0]?.id;
|
|
89
|
+
actionDialog = undefined;
|
|
90
|
+
actionOpen = false;
|
|
77
91
|
} catch (cause) {
|
|
78
92
|
if (!mounted || currentEpoch !== epoch) return;
|
|
79
93
|
error = cause instanceof ApprovalContractError ? 'invalid' : 'load';
|
|
@@ -95,6 +109,13 @@
|
|
|
95
109
|
}, 250);
|
|
96
110
|
}
|
|
97
111
|
|
|
112
|
+
function clearSearch(): void {
|
|
113
|
+
clearTimeout(timer);
|
|
114
|
+
search = '';
|
|
115
|
+
appliedSearch = '';
|
|
116
|
+
page = 1;
|
|
117
|
+
}
|
|
118
|
+
|
|
98
119
|
function switchView(next: ApprovalView): void {
|
|
99
120
|
if (view === next) return;
|
|
100
121
|
view = next;
|
|
@@ -105,6 +126,7 @@
|
|
|
105
126
|
function openAction(action: string): void {
|
|
106
127
|
if (!activeRecord?.allowedActions.some(candidate => candidate.id === action)) return;
|
|
107
128
|
actionDialog = action;
|
|
129
|
+
actionOpen = true;
|
|
108
130
|
comment = '';
|
|
109
131
|
targetId = '';
|
|
110
132
|
}
|
|
@@ -139,6 +161,7 @@
|
|
|
139
161
|
return;
|
|
140
162
|
}
|
|
141
163
|
actionDialog = undefined;
|
|
164
|
+
actionOpen = false;
|
|
142
165
|
records = records.map(candidate => candidate.id === receipt.record.id ? receipt.record : candidate);
|
|
143
166
|
selectedId = receipt.record.id;
|
|
144
167
|
} catch (cause) {
|
|
@@ -166,14 +189,15 @@
|
|
|
166
189
|
records = [];
|
|
167
190
|
selectedId = undefined;
|
|
168
191
|
actionDialog = undefined;
|
|
192
|
+
actionOpen = false;
|
|
169
193
|
actionPending = false;
|
|
170
194
|
void load();
|
|
171
195
|
});
|
|
172
196
|
});
|
|
173
197
|
</script>
|
|
174
198
|
|
|
175
|
-
<section class="svadmin-approval-center" aria-label={title ?? i18n.t('approval.title')}>
|
|
176
|
-
<header class="svadmin-approval-center__header">
|
|
199
|
+
<section class="svadmin-approval-center" aria-label={title ?? i18n.t('approval.title')} data-svadmin-approval-center data-svadmin-approval-view={view} data-svadmin-approval-state={centerState}>
|
|
200
|
+
<header class="svadmin-approval-center__header" data-svadmin-approval-header>
|
|
177
201
|
<div>
|
|
178
202
|
<h2>{title ?? i18n.t('approval.title')}</h2>
|
|
179
203
|
</div>
|
|
@@ -181,7 +205,7 @@
|
|
|
181
205
|
<RefreshCw class={loading ? 'svadmin-approval-center__spin' : ''} />
|
|
182
206
|
</Button>
|
|
183
207
|
</header>
|
|
184
|
-
<div class="svadmin-approval-center__toolbar">
|
|
208
|
+
<div class="svadmin-approval-center__toolbar" data-svadmin-approval-toolbar>
|
|
185
209
|
<Button type="button" aria-pressed={view === 'pending'} variant={view === 'pending' ? 'default' : 'outline'} onclick={() => switchView('pending')}>{i18n.t('approval.pending')}</Button>
|
|
186
210
|
<Button type="button" aria-pressed={view === 'history'} variant={view === 'history' ? 'default' : 'outline'} onclick={() => switchView('history')}>{i18n.t('approval.history')}</Button>
|
|
187
211
|
<label class="svadmin-approval-center__search">
|
|
@@ -189,15 +213,28 @@
|
|
|
189
213
|
<Input value={search} oninput={scheduleSearch} placeholder={i18n.t('approval.search')} aria-label={i18n.t('approval.search')} />
|
|
190
214
|
</label>
|
|
191
215
|
</div>
|
|
192
|
-
{#if error}
|
|
216
|
+
{#if error && records.length > 0}
|
|
193
217
|
<div role="alert">{i18n.t(`approval.${error}`)}</div>
|
|
194
218
|
{/if}
|
|
195
|
-
<div class="svadmin-approval-center__body">
|
|
196
|
-
<div class="svadmin-approval-center__list">
|
|
219
|
+
<div class="svadmin-approval-center__body" data-svadmin-approval-body>
|
|
220
|
+
<div class="svadmin-approval-center__list" data-svadmin-approval-list>
|
|
197
221
|
{#if loading}
|
|
198
|
-
<
|
|
222
|
+
<DataState state="loading" />
|
|
223
|
+
{:else if records.length === 0 && error}
|
|
224
|
+
<DataState
|
|
225
|
+
state="error"
|
|
226
|
+
title={error === 'invalid' ? i18n.t('approval.invalid') : i18n.t('approval.load')}
|
|
227
|
+
description={i18n.locale === 'zh-CN' ? '请刷新后重试。' : 'Refresh and retry.'}
|
|
228
|
+
retry={() => void load()}
|
|
229
|
+
/>
|
|
199
230
|
{:else if records.length === 0}
|
|
200
|
-
<
|
|
231
|
+
<DataState
|
|
232
|
+
state={appliedSearch ? 'filtered-empty' : 'empty'}
|
|
233
|
+
{...(appliedSearch ? {} : { title: i18n.t('approval.empty') })}
|
|
234
|
+
/>
|
|
235
|
+
{#if appliedSearch}
|
|
236
|
+
<Button type="button" variant="outline" size="sm" onclick={clearSearch}>{i18n.t('common.clear')}</Button>
|
|
237
|
+
{/if}
|
|
201
238
|
{:else}
|
|
202
239
|
{#each records as record (record.id)}
|
|
203
240
|
<button type="button" aria-pressed={record.id === selectedId} class:selected={record.id === selectedId} onclick={() => selectedId = record.id}>
|
|
@@ -207,7 +244,7 @@
|
|
|
207
244
|
{/each}
|
|
208
245
|
{/if}
|
|
209
246
|
</div>
|
|
210
|
-
<div class="svadmin-approval-center__detail">
|
|
247
|
+
<div class="svadmin-approval-center__detail" data-svadmin-approval-detail>
|
|
211
248
|
{#if activeRecord}
|
|
212
249
|
<h3>{activeRecord.title}</h3>
|
|
213
250
|
<Badge variant="outline">{activeRecord.status}</Badge>
|
|
@@ -230,7 +267,7 @@
|
|
|
230
267
|
{/if}
|
|
231
268
|
</div>
|
|
232
269
|
</div>
|
|
233
|
-
<footer class="svadmin-approval-center__footer">
|
|
270
|
+
<footer class="svadmin-approval-center__footer" data-svadmin-approval-pagination>
|
|
234
271
|
<Button type="button" aria-label={i18n.t('common.prev')} title={i18n.t('common.prev')} variant="outline" disabled={page <= 1 || loading} onclick={() => { page -= 1; }}>
|
|
235
272
|
<ChevronLeft />
|
|
236
273
|
</Button>
|
|
@@ -241,25 +278,30 @@
|
|
|
241
278
|
</footer>
|
|
242
279
|
</section>
|
|
243
280
|
|
|
244
|
-
{
|
|
245
|
-
|
|
246
|
-
<
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
<label>{i18n.t('approval.
|
|
252
|
-
<
|
|
281
|
+
<Dialog.Root bind:open={actionOpen}>
|
|
282
|
+
{#if actionDialog && activeRecord}
|
|
283
|
+
<Dialog.Content class="svadmin-approval-center__dialog" aria-label={activeAction?.label ?? i18n.t('common.confirmAction')}>
|
|
284
|
+
<Dialog.Header>
|
|
285
|
+
<Dialog.Title>{activeAction?.label}</Dialog.Title>
|
|
286
|
+
<Dialog.Description>{i18n.t('approval.comment')}</Dialog.Description>
|
|
287
|
+
</Dialog.Header>
|
|
288
|
+
<label>{i18n.t('approval.comment')}
|
|
289
|
+
<textarea bind:value={comment} required={activeAction?.commentRequired} maxlength={10000}></textarea>
|
|
253
290
|
</label>
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
{
|
|
291
|
+
{#if activeAction?.targetRequired}
|
|
292
|
+
<label>{i18n.t('approval.target')}
|
|
293
|
+
<Input bind:value={targetId} required maxlength={200} />
|
|
294
|
+
</label>
|
|
295
|
+
{/if}
|
|
296
|
+
<Dialog.Footer>
|
|
297
|
+
<Button type="button" variant="outline" onclick={() => { actionOpen = false; actionDialog = undefined; }}>{i18n.t('common.cancel')}</Button>
|
|
298
|
+
<Button type="button" disabled={actionPending || !activeAction || !canSubmit(activeAction)} onclick={() => void submitAction()}>
|
|
299
|
+
{actionPending ? i18n.t('common.processing') : i18n.t('common.confirm')}
|
|
300
|
+
</Button>
|
|
301
|
+
</Dialog.Footer>
|
|
302
|
+
</Dialog.Content>
|
|
303
|
+
{/if}
|
|
304
|
+
</Dialog.Root>
|
|
263
305
|
|
|
264
306
|
<style>
|
|
265
307
|
.svadmin-approval-center { display: grid; gap: 1rem; min-width: 0; }
|
|
@@ -275,8 +317,8 @@
|
|
|
275
317
|
.svadmin-approval-center__list span { color: var(--muted-foreground); font-size: 0.875rem; }
|
|
276
318
|
.svadmin-approval-center__detail { display: grid; gap: 0.75rem; padding: 1rem; min-width: 0; }
|
|
277
319
|
.svadmin-approval-center__detail ol, .svadmin-approval-center__detail ul { margin: 0; padding-inline-start: 1.25rem; }
|
|
278
|
-
.svadmin-approval-center__dialog {
|
|
279
|
-
.svadmin-approval-center__dialog textarea { min-height: 6rem; width: 100%; }
|
|
320
|
+
:global(.svadmin-approval-center__dialog) { display: grid; gap: 1rem; width: min(28rem, calc(100vw - 2rem)); }
|
|
321
|
+
:global(.svadmin-approval-center__dialog textarea) { min-height: 6rem; width: 100%; }
|
|
280
322
|
:global(.svadmin-approval-center__spin) { animation: svadmin-approval-spin 1s linear infinite; }
|
|
281
323
|
@keyframes svadmin-approval-spin { to { transform: rotate(360deg); } }
|
|
282
324
|
@media (max-width: 640px) { .svadmin-approval-center__body { grid-template-columns: 1fr; } .svadmin-approval-center__list { border-inline-end: 0; border-block-end: 1px solid var(--border); max-height: 15rem; overflow: auto; } }
|
|
@@ -12,6 +12,9 @@
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
let { brand, title, description, children, footer, brandMark }: Props = $props();
|
|
15
|
+
const generatedId = $props.id();
|
|
16
|
+
const titleId = `svadmin-auth-title-${generatedId}`;
|
|
17
|
+
const descriptionId = `${titleId}-description`;
|
|
15
18
|
</script>
|
|
16
19
|
|
|
17
20
|
<div class="svadmin-u-f3c543ad5fe9 svadmin-u-793346c7362c svadmin-u-967d113a1451 svadmin-u-5d17652eacdf" data-svadmin-auth-page>
|
|
@@ -29,8 +32,16 @@
|
|
|
29
32
|
{#if brandMark}{@render brandMark()}{:else}<span data-svadmin-auth-default-mark><SvadminLogo size={36} /></span>{/if}
|
|
30
33
|
<span class="svadmin-u-4ee734926ff6 svadmin-u-e83a7042bc91 svadmin-u-d4108abe6359">{brand}</span>
|
|
31
34
|
</div>
|
|
32
|
-
<section
|
|
33
|
-
|
|
35
|
+
<section
|
|
36
|
+
class="svadmin-u-5f22e64f2282 svadmin-u-ca6bcd4b6f3f svadmin-u-18049387f0af svadmin-u-cd0ad9a56558 svadmin-u-c07e54fd1439 svadmin-u-438b2237b8d6 svadmin-u-e299d4635ea7"
|
|
37
|
+
data-svadmin-auth-card
|
|
38
|
+
aria-labelledby={titleId}
|
|
39
|
+
aria-describedby={description ? descriptionId : undefined}
|
|
40
|
+
>
|
|
41
|
+
<header class="svadmin-u-b6777c6db914 svadmin-u-6f7e013d6499">
|
|
42
|
+
<h1 id={titleId} class="svadmin-u-d5c9b0001e7e svadmin-u-e83a7042bc91 svadmin-u-e9fadafbd4a2 svadmin-u-d4108abe6359">{title}</h1>
|
|
43
|
+
{#if description}<p id={descriptionId} class="svadmin-u-fc7473ca09eb svadmin-u-18550d5945ae svadmin-u-bfa603190748">{description}</p>{/if}
|
|
44
|
+
</header>
|
|
34
45
|
{@render children()}
|
|
35
46
|
</section>
|
|
36
47
|
{#if footer}<div class="svadmin-u-ca6bf63030aa" data-svadmin-auth-footer>{@render footer()}</div>{/if}
|
|
@@ -29,7 +29,6 @@
|
|
|
29
29
|
rendering?: ResourceRendering | undefined;
|
|
30
30
|
id?: string | number;
|
|
31
31
|
mode?: 'create' | 'edit' | 'clone' | 'show';
|
|
32
|
-
density?: 'compact' | 'comfortable';
|
|
33
32
|
columns?: 1 | 2 | 3 | 4;
|
|
34
33
|
showHeader?: boolean;
|
|
35
34
|
fieldRenderer?: Snippet<[{ field: FieldDefinition; value: unknown; onchange: (v: unknown) => void }]>;
|
|
@@ -47,7 +46,6 @@
|
|
|
47
46
|
rendering,
|
|
48
47
|
id,
|
|
49
48
|
mode = 'create',
|
|
50
|
-
density = 'comfortable',
|
|
51
49
|
columns = 1,
|
|
52
50
|
showHeader = true,
|
|
53
51
|
fieldRenderer,
|
|
@@ -64,7 +62,6 @@
|
|
|
64
62
|
const binding = useResourceContract(() => resourceName);
|
|
65
63
|
const activeRendering = $derived(bindResourceRendering(rendering, binding.resource));
|
|
66
64
|
const isReadonly = $derived(mode === 'show');
|
|
67
|
-
const isCompact = $derived(density === 'compact');
|
|
68
65
|
const instanceId = $props.id();
|
|
69
66
|
|
|
70
67
|
// ─── Resource metadata ────────────────────────────────────────────
|
|
@@ -315,7 +312,7 @@
|
|
|
315
312
|
});
|
|
316
313
|
</script>
|
|
317
314
|
|
|
318
|
-
<div class=
|
|
315
|
+
<div class="svadmin-u-b3542e058833" data-svadmin-rendering-resource={activeRendering?.resource.name} data-svadmin-rendering-kind={activeRendering ? 'form' : undefined}>
|
|
319
316
|
{#if showHeader}
|
|
320
317
|
<div class="svadmin-u-60fbb7713999 svadmin-u-3960ffc248d9 svadmin-u-0c3bc98565dd">
|
|
321
318
|
<TooltipButton
|
|
@@ -378,11 +375,11 @@
|
|
|
378
375
|
{#each groups as group, _i (_i)}
|
|
379
376
|
<Card.Root class="svadmin-u-6ee2d41e2d2d svadmin-u-438b2237b8d6">
|
|
380
377
|
{#if group.name}
|
|
381
|
-
<Card.Header
|
|
378
|
+
<Card.Header>
|
|
382
379
|
<Card.Title class="svadmin-u-42536e69e639">{group.name}</Card.Title>
|
|
383
380
|
</Card.Header>
|
|
384
381
|
{/if}
|
|
385
|
-
<Card.Content class=
|
|
382
|
+
<Card.Content class="svadmin-u-f0faeb26d656 svadmin-u-7a9aabfcd059 svadmin-u-9fcd8a13827e svadmin-u-050494726fba svadmin-u-9335c39f6eff">
|
|
386
383
|
<div class={gridClass}>
|
|
387
384
|
{#each group.fields as field (field.key)}
|
|
388
385
|
<div data-svadmin-field-key={field.key} class={cn(columns > 1 && isFullWidthField(field) && 'svadmin-u-2c955d1b45df', !!form.errors[field.key] && 'svadmin-u-ee1a5af3aa10')}>
|
|
@@ -393,7 +390,6 @@
|
|
|
393
390
|
{field}
|
|
394
391
|
value={renderedDraft.values[field.key]}
|
|
395
392
|
onchange={fieldChange(field.key)}
|
|
396
|
-
{density}
|
|
397
393
|
invalid={!!form.errors[field.key]}
|
|
398
394
|
{...definedOptions({ "errorId": form.errors[field.key] ? fieldErrorId(field.key) : undefined })}
|
|
399
395
|
disabled={isReadonly}
|
|
@@ -410,8 +406,8 @@
|
|
|
410
406
|
{/each}
|
|
411
407
|
{:else}
|
|
412
408
|
<Card.Root class="svadmin-u-6ee2d41e2d2d svadmin-u-438b2237b8d6">
|
|
413
|
-
<Card.Content class=
|
|
414
|
-
<div class={gridClass} data-svadmin-form-grid data-columns={columns}
|
|
409
|
+
<Card.Content class="svadmin-u-f0faeb26d656 svadmin-u-52be28846b5f svadmin-u-9fcd8a13827e svadmin-u-7a9aabfcd059 svadmin-u-0a58453f3755 svadmin-u-050494726fba">
|
|
410
|
+
<div class={gridClass} data-svadmin-form-grid data-columns={columns}>
|
|
415
411
|
{#each formFields as field (field.key)}
|
|
416
412
|
<div data-svadmin-field-key={field.key} class={cn(columns > 1 && isFullWidthField(field) && 'svadmin-u-2c955d1b45df', !!form.errors[field.key] && 'svadmin-u-ee1a5af3aa10')}>
|
|
417
413
|
{#if fieldRenderer}
|
|
@@ -421,7 +417,6 @@
|
|
|
421
417
|
{field}
|
|
422
418
|
value={renderedDraft.values[field.key]}
|
|
423
419
|
onchange={fieldChange(field.key)}
|
|
424
|
-
{density}
|
|
425
420
|
invalid={!!form.errors[field.key]}
|
|
426
421
|
{...definedOptions({ "errorId": form.errors[field.key] ? fieldErrorId(field.key) : undefined })}
|
|
427
422
|
disabled={isReadonly}
|
|
@@ -441,7 +436,7 @@
|
|
|
441
436
|
{#if formActions && !isReadonly}
|
|
442
437
|
{@render formActions({ isLoading: form.submitting, onSubmit: handleSubmit })}
|
|
443
438
|
{:else if !isReadonly}
|
|
444
|
-
<Button type="submit" size=
|
|
439
|
+
<Button type="submit" size="default" disabled={form.submitting}>
|
|
445
440
|
{#if form.submitting}
|
|
446
441
|
<Loader2 class="svadmin-u-11e59c6d5f6b svadmin-u-dc7972ebf3f3 svadmin-u-afbdd13a380e" data-icon="inline-start" aria-hidden="true" />
|
|
447
442
|
{:else}
|
|
@@ -452,7 +447,7 @@
|
|
|
452
447
|
<Button
|
|
453
448
|
type="button"
|
|
454
449
|
variant="outline"
|
|
455
|
-
size=
|
|
450
|
+
size="default"
|
|
456
451
|
onclick={back}
|
|
457
452
|
>
|
|
458
453
|
{i18n.t('common.cancel')}
|
|
@@ -112,10 +112,12 @@
|
|
|
112
112
|
selectable?: boolean;
|
|
113
113
|
/** 仅在业务回调支持按查询执行服务端命令时启用。 */
|
|
114
114
|
allowSelectAllMatching?: boolean;
|
|
115
|
+
/** @deprecated Retained for compatibility; comfortable is the shared default. */
|
|
115
116
|
density?: 'compact' | 'comfortable';
|
|
116
117
|
showHeader?: boolean;
|
|
117
118
|
title?: string;
|
|
118
119
|
showFilterTags?: boolean;
|
|
120
|
+
/** @deprecated Retained for compatibility; use the shared appearance baseline. */
|
|
119
121
|
showDensitySwitcher?: boolean;
|
|
120
122
|
showRefresh?: boolean;
|
|
121
123
|
/** Custom header actions (right side) */
|
|
@@ -788,8 +790,18 @@
|
|
|
788
790
|
detailOpenedInHistory = false;
|
|
789
791
|
localDetailId = undefined;
|
|
790
792
|
quickEditId = undefined;
|
|
791
|
-
|
|
792
|
-
|
|
793
|
+
const queryScopeChanged = previousTableScope.contract !== scope.contract
|
|
794
|
+
|| previousTableScope.resourceName !== scope.resourceName
|
|
795
|
+
|| previousTableScope.provider !== scope.provider
|
|
796
|
+
|| previousTableScope.meta !== scope.meta
|
|
797
|
+
|| previousTableScope.tenant !== scope.tenant
|
|
798
|
+
|| previousTableScope.auth !== scope.auth
|
|
799
|
+
|| previousTableScope.router !== scope.router
|
|
800
|
+
|| previousTableScope.permissionProvider !== scope.permissionProvider;
|
|
801
|
+
if (queryScopeChanged) {
|
|
802
|
+
listState.cancelSearch();
|
|
803
|
+
if (previousTableScope.resourceName !== scope.resourceName) resetToDefaultListState();
|
|
804
|
+
}
|
|
793
805
|
}
|
|
794
806
|
previousTableScope = scope;
|
|
795
807
|
});
|
|
@@ -1042,7 +1054,7 @@
|
|
|
1042
1054
|
|
|
1043
1055
|
{#snippet defaultEmptyState()}
|
|
1044
1056
|
{@const hasCriteria = !!(searchText.trim() || appliedSearchText.trim() || activeFilterCount > 0)}
|
|
1045
|
-
<DataState state=
|
|
1057
|
+
<DataState state={hasCriteria ? 'filtered-empty' : 'empty'} description={i18n.t(hasCriteria ? 'empty.description' : 'common.noDataHint')}>
|
|
1046
1058
|
{#snippet action()}
|
|
1047
1059
|
{#if hasCriteria}
|
|
1048
1060
|
<Button variant="outline" size="sm" onclick={clearFilters}>
|
|
@@ -15,10 +15,12 @@ interface Props {
|
|
|
15
15
|
selectable?: boolean;
|
|
16
16
|
/** 仅在业务回调支持按查询执行服务端命令时启用。 */
|
|
17
17
|
allowSelectAllMatching?: boolean;
|
|
18
|
+
/** @deprecated Retained for compatibility; comfortable is the shared default. */
|
|
18
19
|
density?: 'compact' | 'comfortable';
|
|
19
20
|
showHeader?: boolean;
|
|
20
21
|
title?: string;
|
|
21
22
|
showFilterTags?: boolean;
|
|
23
|
+
/** @deprecated Retained for compatibility; use the shared appearance baseline. */
|
|
22
24
|
showDensitySwitcher?: boolean;
|
|
23
25
|
showRefresh?: boolean;
|
|
24
26
|
/** Custom header actions (right side) */
|
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
footer?: Snippet;
|
|
12
12
|
children: Snippet;
|
|
13
13
|
width?: 'narrow' | 'default' | 'wide';
|
|
14
|
-
density?: 'compact' | 'comfortable';
|
|
15
14
|
class?: string;
|
|
16
15
|
}
|
|
17
16
|
|
|
@@ -23,7 +22,6 @@
|
|
|
23
22
|
footer,
|
|
24
23
|
children,
|
|
25
24
|
width = 'default',
|
|
26
|
-
density = 'comfortable',
|
|
27
25
|
class: className = '',
|
|
28
26
|
}: Props = $props();
|
|
29
27
|
</script>
|
|
@@ -32,7 +30,6 @@
|
|
|
32
30
|
{title}
|
|
33
31
|
{...definedOptions({ description, eyebrow, actions })}
|
|
34
32
|
{width}
|
|
35
|
-
{density}
|
|
36
33
|
class={className}
|
|
37
34
|
>
|
|
38
35
|
<div class="configuration-page__content">
|
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
resourceName: string;
|
|
16
16
|
rendering?: ResourceRendering | undefined;
|
|
17
17
|
title?: string;
|
|
18
|
-
density?: 'compact' | 'comfortable';
|
|
19
18
|
columns?: 1 | 2 | 3 | 4;
|
|
20
19
|
headerActions?: Snippet;
|
|
20
|
+
formActions?: Snippet<[{ isLoading: boolean; onSubmit: () => void }]>;
|
|
21
21
|
onSuccess?: () => void;
|
|
22
22
|
class?: string;
|
|
23
23
|
}
|
|
@@ -26,9 +26,9 @@
|
|
|
26
26
|
resourceName,
|
|
27
27
|
rendering,
|
|
28
28
|
title,
|
|
29
|
-
density = 'comfortable',
|
|
30
29
|
columns = 1,
|
|
31
30
|
headerActions,
|
|
31
|
+
formActions,
|
|
32
32
|
onSuccess,
|
|
33
33
|
class: className = '',
|
|
34
34
|
}: Props = $props();
|
|
@@ -39,28 +39,31 @@
|
|
|
39
39
|
const pageTitle = $derived(title ?? `${i18n.t('common.create')}${resource.label}`);
|
|
40
40
|
</script>
|
|
41
41
|
|
|
42
|
-
<div class="
|
|
43
|
-
<
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
42
|
+
<div class="svadmin-u-b3542e058833 {className}" data-svadmin-crud-page="create" data-svadmin-resource={resourceName}>
|
|
43
|
+
<div data-svadmin-crud-page-header>
|
|
44
|
+
<PageHeader
|
|
45
|
+
title={pageTitle}
|
|
46
|
+
onBack={() => navigateGuard(() => adminContext.navigate(`/${resourceName}`))}
|
|
47
|
+
>
|
|
48
48
|
{#snippet actions()}
|
|
49
49
|
<ListButton resource={resourceName} hideText onBeforeNavigate={navigateGuard} />
|
|
50
50
|
{#if headerActions}
|
|
51
51
|
{@render headerActions()}
|
|
52
52
|
{/if}
|
|
53
53
|
{/snippet}
|
|
54
|
-
|
|
54
|
+
</PageHeader>
|
|
55
|
+
</div>
|
|
55
56
|
|
|
56
|
-
<
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
57
|
+
<div data-svadmin-crud-page-form>
|
|
58
|
+
<AutoForm
|
|
59
|
+
{rendering}
|
|
60
|
+
{resourceName}
|
|
61
|
+
mode="create"
|
|
62
|
+
{columns}
|
|
63
|
+
showHeader={false}
|
|
64
|
+
{...definedOptions({ formActions })}
|
|
65
|
+
onNavigationGuardReady={(guard) => navigateGuard = guard}
|
|
66
|
+
{...definedOptions({ "onSuccess": onSuccess })}
|
|
67
|
+
/>
|
|
68
|
+
</div>
|
|
66
69
|
</div>
|
|
@@ -4,9 +4,12 @@ interface Props {
|
|
|
4
4
|
resourceName: string;
|
|
5
5
|
rendering?: ResourceRendering | undefined;
|
|
6
6
|
title?: string;
|
|
7
|
-
density?: 'compact' | 'comfortable';
|
|
8
7
|
columns?: 1 | 2 | 3 | 4;
|
|
9
8
|
headerActions?: Snippet;
|
|
9
|
+
formActions?: Snippet<[{
|
|
10
|
+
isLoading: boolean;
|
|
11
|
+
onSubmit: () => void;
|
|
12
|
+
}]>;
|
|
10
13
|
onSuccess?: () => void;
|
|
11
14
|
class?: string;
|
|
12
15
|
}
|
|
@@ -20,10 +20,10 @@
|
|
|
20
20
|
rendering?: ResourceRendering | undefined;
|
|
21
21
|
id: string | number;
|
|
22
22
|
title?: string;
|
|
23
|
-
density?: 'compact' | 'comfortable';
|
|
24
23
|
columns?: 1 | 2 | 3 | 4;
|
|
25
24
|
canDelete?: boolean;
|
|
26
25
|
headerActions?: Snippet;
|
|
26
|
+
formActions?: Snippet<[{ isLoading: boolean; onSubmit: () => void }]>;
|
|
27
27
|
onSuccess?: () => void;
|
|
28
28
|
class?: string;
|
|
29
29
|
}
|
|
@@ -33,10 +33,10 @@
|
|
|
33
33
|
rendering,
|
|
34
34
|
id,
|
|
35
35
|
title,
|
|
36
|
-
density = 'comfortable',
|
|
37
36
|
columns = 1,
|
|
38
37
|
canDelete,
|
|
39
38
|
headerActions,
|
|
39
|
+
formActions,
|
|
40
40
|
onSuccess,
|
|
41
41
|
class: className = '',
|
|
42
42
|
}: Props = $props();
|
|
@@ -48,12 +48,12 @@
|
|
|
48
48
|
const showDelete = $derived(canDelete ?? resource.canDelete !== false);
|
|
49
49
|
</script>
|
|
50
50
|
|
|
51
|
-
<div class="
|
|
52
|
-
<
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
51
|
+
<div class="svadmin-u-b3542e058833 {className}" data-svadmin-crud-page="edit" data-svadmin-resource={resourceName} data-svadmin-record-id={id}>
|
|
52
|
+
<div data-svadmin-crud-page-header>
|
|
53
|
+
<PageHeader
|
|
54
|
+
title={pageTitle}
|
|
55
|
+
onBack={() => navigateGuard(() => navigation.list(resourceName))}
|
|
56
|
+
>
|
|
57
57
|
{#snippet actions()}
|
|
58
58
|
<ListButton resource={resourceName} hideText onBeforeNavigate={navigateGuard} />
|
|
59
59
|
<ShowButton resource={resourceName} recordItemId={id} hideText onBeforeNavigate={navigateGuard} />
|
|
@@ -65,17 +65,20 @@
|
|
|
65
65
|
{@render headerActions()}
|
|
66
66
|
{/if}
|
|
67
67
|
{/snippet}
|
|
68
|
-
|
|
68
|
+
</PageHeader>
|
|
69
|
+
</div>
|
|
69
70
|
|
|
70
|
-
<
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
71
|
+
<div data-svadmin-crud-page-form>
|
|
72
|
+
<AutoForm
|
|
73
|
+
{rendering}
|
|
74
|
+
{resourceName}
|
|
75
|
+
mode="edit"
|
|
76
|
+
{id}
|
|
77
|
+
{columns}
|
|
78
|
+
showHeader={false}
|
|
79
|
+
{...definedOptions({ formActions })}
|
|
80
|
+
onNavigationGuardReady={(guard) => navigateGuard = guard}
|
|
81
|
+
{...definedOptions({ "onSuccess": onSuccess })}
|
|
82
|
+
/>
|
|
83
|
+
</div>
|
|
81
84
|
</div>
|
|
@@ -5,10 +5,13 @@ interface Props {
|
|
|
5
5
|
rendering?: ResourceRendering | undefined;
|
|
6
6
|
id: string | number;
|
|
7
7
|
title?: string;
|
|
8
|
-
density?: 'compact' | 'comfortable';
|
|
9
8
|
columns?: 1 | 2 | 3 | 4;
|
|
10
9
|
canDelete?: boolean;
|
|
11
10
|
headerActions?: Snippet;
|
|
11
|
+
formActions?: Snippet<[{
|
|
12
|
+
isLoading: boolean;
|
|
13
|
+
onSubmit: () => void;
|
|
14
|
+
}]>;
|
|
12
15
|
onSuccess?: () => void;
|
|
13
16
|
class?: string;
|
|
14
17
|
}
|
|
@@ -35,25 +35,21 @@
|
|
|
35
35
|
import { Plus, X } from '@lucide/svelte';
|
|
36
36
|
import type { Snippet } from 'svelte';
|
|
37
37
|
import { getRichTextEditor } from '../editor-config.svelte.js';
|
|
38
|
-
import { cn } from '../utils.js';
|
|
39
38
|
import { numericInputValue } from '../numeric-input.js';
|
|
40
39
|
|
|
41
40
|
type FieldOption = NonNullable<FieldDefinition['options']>[number];
|
|
42
41
|
const i18n = useTranslation();
|
|
43
42
|
|
|
44
|
-
let { field, value, onchange, disabled, invalid = false, errorId,
|
|
43
|
+
let { field, value, onchange, disabled, invalid = false, errorId, children } = $props<{
|
|
45
44
|
field: FieldDefinition;
|
|
46
45
|
value: unknown;
|
|
47
46
|
onchange: (val: unknown) => void;
|
|
48
47
|
disabled?: boolean;
|
|
49
48
|
invalid?: boolean;
|
|
50
49
|
errorId?: string;
|
|
51
|
-
density?: 'compact' | 'comfortable';
|
|
52
50
|
children?: Snippet;
|
|
53
51
|
}>();
|
|
54
52
|
|
|
55
|
-
const isCompact = $derived(density === 'compact');
|
|
56
|
-
|
|
57
53
|
// Typed accessors
|
|
58
54
|
const strVal = $derived((value as string) ?? '');
|
|
59
55
|
const numVal = $derived(numericInputValue(value));
|
|
@@ -111,13 +107,9 @@
|
|
|
111
107
|
</script>
|
|
112
108
|
|
|
113
109
|
<div
|
|
114
|
-
class=
|
|
115
|
-
'svadmin-u-5a2508227c6a',
|
|
116
|
-
isCompact && 'svadmin-u-da7c36cd8867 svadmin-u-01e043b9ca05 svadmin-u-5e375759a547 svadmin-u-0144d38a6921 svadmin-u-910198b850a8 svadmin-u-220cd47c030c svadmin-u-84a68a4bfa38 svadmin-u-2b201b2c12c7 svadmin-u-334c9da7fe2b svadmin-u-f7194f86de09 svadmin-u-a281d7e98ab9 svadmin-u-6cddb40447d9 svadmin-u-150c3670da1d svadmin-u-f01a063e8052 svadmin-u-3e2aa4ec6454'
|
|
117
|
-
)}
|
|
110
|
+
class="svadmin-u-5a2508227c6a"
|
|
118
111
|
data-svadmin-field
|
|
119
112
|
data-svadmin-field-key={field.key}
|
|
120
|
-
data-density={density}
|
|
121
113
|
>
|
|
122
114
|
<Label for={field.key} id="label-{field.key}" data-svadmin-field-label>
|
|
123
115
|
{field.label}
|