@svadmin/ui 0.86.0 → 0.88.1

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 (42) hide show
  1. package/README.md +3 -1
  2. package/dist/app.css +188 -5
  3. package/dist/app.theme.css +188 -5
  4. package/dist/component-registry.d.ts +1 -1
  5. package/dist/component-registry.js +2 -2
  6. package/dist/components/ApprovalActionCard.svelte +54 -31
  7. package/dist/components/ApprovalCenter.svelte +73 -31
  8. package/dist/components/AuthPageShell.svelte +13 -2
  9. package/dist/components/AutoTable.svelte +13 -3
  10. package/dist/components/CreatePage.svelte +22 -15
  11. package/dist/components/CreatePage.svelte.d.ts +4 -0
  12. package/dist/components/EditPage.svelte +23 -16
  13. package/dist/components/EditPage.svelte.d.ts +4 -0
  14. package/dist/components/ListPage.svelte +20 -14
  15. package/dist/components/ListPage.svelte.d.ts +2 -0
  16. package/dist/components/LoginPage.svelte +27 -11
  17. package/dist/components/LoginPage.svelte.d.ts +4 -0
  18. package/dist/components/NotificationsSettings.svelte +16 -8
  19. package/dist/components/PagePattern.svelte +2 -2
  20. package/dist/components/PagePattern.svelte.d.ts +1 -1
  21. package/dist/components/PasswordInput.svelte +36 -17
  22. package/dist/components/PasswordInput.svelte.d.ts +3 -0
  23. package/dist/components/ShowPage.svelte +20 -21
  24. package/dist/components/account/MemberDirectory.svelte +17 -4
  25. package/dist/components/account/SecurityLogPage.svelte +8 -1
  26. package/dist/components/content/AuditTimeline.svelte +32 -4
  27. package/dist/components/content/AuditTimeline.svelte.d.ts +9 -0
  28. package/dist/components/content/DataState.svelte +5 -4
  29. package/dist/components/content/DataState.svelte.d.ts +1 -1
  30. package/dist/components/content/FilterToolbar.svelte +7 -1
  31. package/dist/components/content/WorkspaceLayout.svelte +3 -1
  32. package/dist/components/content/WorkspaceLayout.svelte.d.ts +1 -0
  33. package/dist/components/network/TeamCrewTablePage.svelte +8 -1
  34. package/dist/components/network/UserCardsNFTPage.svelte +3 -1
  35. package/dist/components/profile/ProjectsGrid.svelte +1 -1
  36. package/dist/components/profile/TeamsShowcase.svelte +1 -1
  37. package/dist/design-contract.d.ts +1 -1
  38. package/dist/design-contract.js +1 -1
  39. package/dist/page-chrome.css +3 -3
  40. package/dist/recipes.d.ts +102 -69
  41. package/dist/recipes.js +40 -2
  42. package/package.json +1 -1
@@ -9,10 +9,19 @@ export interface TimelineItem {
9
9
  tag?: string;
10
10
  meta?: Record<string, unknown>;
11
11
  }
12
+ import type { Snippet } from 'svelte';
13
+ import type { DataStateKind } from './DataState.svelte';
12
14
  interface Props {
13
15
  items?: TimelineItem[];
14
16
  emptyTitle?: string;
15
17
  emptyDescription?: string;
18
+ state?: DataStateKind;
19
+ stateTitle?: string;
20
+ stateDescription?: string;
21
+ retry?: () => void;
22
+ retryLabel?: string;
23
+ loadingLabel?: string;
24
+ action?: Snippet;
16
25
  class?: string;
17
26
  }
18
27
  declare const AuditTimeline: import("svelte").Component<Props, {}, "">;
@@ -5,7 +5,7 @@
5
5
  import { Button } from '../ui/button/index.js';
6
6
  import * as Alert from '../ui/alert/index.js';
7
7
  import { Skeleton } from '../ui/skeleton/index.js';
8
- export type DataStateKind = 'loading' | 'empty' | 'error' | 'forbidden';
8
+ export type DataStateKind = 'loading' | 'empty' | 'filtered-empty' | 'error' | 'forbidden';
9
9
  interface Props {
10
10
  state: DataStateKind;
11
11
  title?: string;
@@ -31,6 +31,7 @@
31
31
  const defaults = $derived({
32
32
  loading: [i18n.t('common.loading'), i18n.t('common.loading')],
33
33
  empty: [i18n.t('empty.title'), i18n.t('empty.description')],
34
+ 'filtered-empty': [isZh ? '没有匹配结果' : 'No matching results', isZh ? '请调整或清除筛选条件。' : 'Adjust or clear your filters.'],
34
35
  error: [i18n.t('common.error'), i18n.t('common.retry')],
35
36
  forbidden: [isZh ? '无权访问' : 'Access restricted', isZh ? '你没有查看此内容的权限。' : 'You do not have permission to view this content.'],
36
37
  } as const);
@@ -41,11 +42,11 @@
41
42
  </script>
42
43
 
43
44
  {#if state === 'loading'}
44
- <div class={'svadmin-u-6ed543e2fbbb svadmin-u-5f22e64f2282 svadmin-u-ca6bcd4b6f3f svadmin-u-18049387f0af svadmin-u-cd0ad9a56558 svadmin-u-c07e54fd1439 ' + className} role="status" aria-live="polite" aria-label={resolvedLoadingLabel} aria-busy="true">
45
+ <div data-svadmin-data-state={state} class={'svadmin-u-6ed543e2fbbb svadmin-u-5f22e64f2282 svadmin-u-ca6bcd4b6f3f svadmin-u-18049387f0af svadmin-u-cd0ad9a56558 svadmin-u-c07e54fd1439 ' + className} role="status" aria-live="polite" aria-label={resolvedLoadingLabel} aria-busy="true">
45
46
  <Skeleton class="svadmin-u-cd0d9c512cdc svadmin-u-84789e8a20cd" /><Skeleton class="svadmin-u-11e59c6d5f6b svadmin-u-6da6a3c3f741 svadmin-u-9794ab45094d" /><Skeleton class="svadmin-u-0a769880db93 svadmin-u-6da6a3c3f741" />
46
47
  </div>
47
48
  {:else if state === 'error' || state === 'forbidden'}
48
- <Alert.Root variant={state === 'error' ? 'destructive' : 'warning'} class={className}>
49
+ <Alert.Root data-svadmin-data-state={state} variant={state === 'error' ? 'destructive' : 'warning'} class={className}>
49
50
  {#if state === 'error'}<AlertTriangle class="svadmin-u-f7b5fa971871" aria-hidden="true" />{:else}<LockKeyhole class="svadmin-u-f7b5fa971871" aria-hidden="true" />{/if}
50
51
  <Alert.Title>{resolvedTitle}</Alert.Title>
51
52
  <Alert.Description>{resolvedDescription}</Alert.Description>
@@ -53,7 +54,7 @@
53
54
  {#if action}<div class="svadmin-u-eccd13ef4f2f">{@render action()}</div>{/if}
54
55
  </Alert.Root>
55
56
  {:else}
56
- <div class={'empty-state ' + className} role="status" aria-live="polite">
57
+ <div data-svadmin-data-state={state} class={'empty-state ' + className} role="status" aria-live="polite">
57
58
  <span class="empty-state__icon" aria-hidden="true">
58
59
  <Inbox size={16} />
59
60
  </span>
@@ -1,5 +1,5 @@
1
1
  import type { Snippet } from 'svelte';
2
- export type DataStateKind = 'loading' | 'empty' | 'error' | 'forbidden';
2
+ export type DataStateKind = 'loading' | 'empty' | 'filtered-empty' | 'error' | 'forbidden';
3
3
  interface Props {
4
4
  state: DataStateKind;
5
5
  title?: string;
@@ -38,9 +38,14 @@
38
38
  class: className = '',
39
39
  }: Props = $props();
40
40
 
41
+ let searchInput = $state<HTMLInputElement | null>(null);
41
42
  const advancedPanelId = `svadmin-filter-toolbar-advanced-${uid}`;
42
43
  const i18n = useTranslation();
43
44
  let mobile = $state(false);
45
+ function clearSearch(): void {
46
+ query = '';
47
+ searchInput?.focus();
48
+ }
44
49
  onMount(() => {
45
50
  const media = window.matchMedia('(max-width: 767px)');
46
51
  const update = () => { mobile = media.matches; };
@@ -57,6 +62,7 @@
57
62
  <Search class="svadmin-filter-toolbar__search-icon" aria-hidden="true" />
58
63
  <Input
59
64
  bind:value={query}
65
+ bind:ref={searchInput}
60
66
  {placeholder}
61
67
  class="svadmin-filter-toolbar__input"
62
68
  aria-label={placeholder}
@@ -68,7 +74,7 @@
68
74
  class="svadmin-filter-toolbar__clear"
69
75
  aria-label={clearLabel}
70
76
  title={clearLabel}
71
- onclick={() => query = ''}
77
+ onclick={clearSearch}
72
78
  >
73
79
  <X class="svadmin-filter-toolbar__control-icon" aria-hidden="true" />
74
80
  </Button>
@@ -10,6 +10,7 @@
10
10
  secondaryCollapsed?: boolean;
11
11
  secondaryCollapsedWidth?: string;
12
12
  mobileOrder?: 'primary-first' | 'secondary-first';
13
+ label?: string;
13
14
  class?: string;
14
15
  }
15
16
 
@@ -21,6 +22,7 @@
21
22
  secondaryCollapsed = false,
22
23
  secondaryCollapsedWidth = '3rem',
23
24
  mobileOrder = 'primary-first',
25
+ label,
24
26
  class: className = '',
25
27
  }: Props = $props();
26
28
 
@@ -28,7 +30,7 @@
28
30
  const resolvedSecondaryWidth = $derived(secondaryCollapsed ? secondaryCollapsedWidth : secondaryWidth);
29
31
  </script>
30
32
 
31
- <div class={styles.root + ' ' + className} data-svadmin-workspace-layout data-secondary-collapsed={secondaryCollapsed} style:--workspace-secondary-width={resolvedSecondaryWidth}>
33
+ <div class={styles.root + ' ' + className} data-svadmin-workspace-layout data-workspace-order={mobileOrder} data-secondary-collapsed={secondaryCollapsed} {...(label ? { 'aria-label': label, role: 'region' } : {})} style:--workspace-secondary-width={resolvedSecondaryWidth}>
32
34
  {#if summary}<div class={styles.summary} data-svadmin-workspace-summary>{@render summary()}</div>{/if}
33
35
  <div class={styles.columns}>
34
36
  {#if mobileOrder === 'secondary-first' && secondary}<aside class={styles.secondary} data-svadmin-workspace-secondary>{@render secondary()}</aside>{/if}
@@ -7,6 +7,7 @@ interface Props {
7
7
  secondaryCollapsed?: boolean;
8
8
  secondaryCollapsedWidth?: string;
9
9
  mobileOrder?: 'primary-first' | 'secondary-first';
10
+ label?: string;
10
11
  class?: string;
11
12
  }
12
13
  declare const WorkspaceLayout: import("svelte").Component<Props, {}, "">;
@@ -5,6 +5,7 @@
5
5
  import ContentPageShell from '../content/ContentPageShell.svelte';
6
6
  import ContentPageHeader from '../content/ContentPageHeader.svelte';
7
7
  import FilterToolbar from '../content/FilterToolbar.svelte';
8
+ import DataState from '../content/DataState.svelte';
8
9
  import NetworkTable from '../content/NetworkTable.svelte';
9
10
  import type { NetworkColumn } from '../content/NetworkTable.types.js';
10
11
  import { Avatar } from '../ui/avatar/index.js';
@@ -38,5 +39,11 @@
38
39
  <ReferenceNotice />
39
40
  <div class="svadmin-u-60fbb7713999 svadmin-u-8dddea0773ed svadmin-u-1004c0c3954c svadmin-u-020ba687fa12 svadmin-u-64cac80d2e9a svadmin-u-3b9871a0bf93"><ContentPageHeader title={i18n.t('network.teamCrew')} description={i18n.t('network.teamCrewDescription')} /><div class="svadmin-u-60fbb7713999 svadmin-u-1eb5c6df38c1 svadmin-u-77a2a20e90d4"><Button variant="outline" size="sm" onclick={() => downloadRows('sample-team.json', filtered)}><Download class="svadmin-u-783b0d9d1e2c" />{i18n.t('common.export')}</Button><Button size="sm" onclick={() => context.navigate('/account/members/team-members')}><UserPlus class="svadmin-u-783b0d9d1e2c" />{i18n.t('account.inviteMember')}</Button></div></div>
40
41
  <FilterToolbar bind:query placeholder={i18n.t('common.search')} />
41
- <NetworkTable rows={filtered} {columns} {row} />
42
+ {#if rows.length === 0}
43
+ <DataState state="empty" title={i18n.t('account.member')} />
44
+ {:else if filtered.length === 0}
45
+ <DataState state="filtered-empty" title={i18n.t('account.member')} description={i18n.t('common.search')} />
46
+ {:else}
47
+ <NetworkTable rows={filtered} {columns} {row} />
48
+ {/if}
42
49
  </ContentPageShell>
@@ -28,8 +28,10 @@
28
28
  <ReferenceNotice />
29
29
  <ContentPageHeader title={i18n.t('network.userCards')} description={i18n.t('network.userCardsDescription')} />
30
30
  <FilterToolbar bind:query placeholder={i18n.t('common.search')} clearLabel={i18n.locale === 'zh-CN' ? '清除搜索' : 'Clear search'} />
31
- {#if filtered.length === 0}
31
+ {#if users.length === 0}
32
32
  <DataState state="empty" title={i18n.t('network.userCards')} />
33
+ {:else if filtered.length === 0}
34
+ <DataState state="filtered-empty" title={i18n.t('network.userCards')} description={i18n.t('common.search')} />
33
35
  {:else}
34
36
  <div class="svadmin-u-f3c543ad5fe9 svadmin-u-0c3bc98565dd svadmin-u-e4d6f343b9ff svadmin-u-b86f7f946cd8">
35
37
  {#each filtered as user (user.id)}
@@ -34,7 +34,7 @@
34
34
  {#if projects.length === 0}
35
35
  <DataState state="empty" title={i18n.t('publicProfile.noProjects')} />
36
36
  {:else if filtered.length === 0}
37
- <DataState state="empty" title={i18n.t('publicProfile.noProjects')} description={i18n.t('publicProfile.searchProjects')} />
37
+ <DataState state="filtered-empty" title={i18n.t('publicProfile.noProjects')} description={i18n.t('publicProfile.searchProjects')} />
38
38
  {:else}
39
39
  <div class={'svadmin-u-f3c543ad5fe9 svadmin-u-0c3bc98565dd ' + gridClass}>
40
40
  {#each filtered as project (project.id)}
@@ -13,5 +13,5 @@
13
13
  <section class="svadmin-u-3e7ce58d64fa">
14
14
  <div class="svadmin-u-60fbb7713999 svadmin-u-8dddea0773ed svadmin-u-1004c0c3954c svadmin-u-020ba687fa12 svadmin-u-9f76a62f4f44 svadmin-u-3b9871a0bf93"><div><h2 class="svadmin-u-4ee734926ff6 svadmin-u-e83a7042bc91 svadmin-u-d4108abe6359">{i18n.t('publicProfile.teamsCount', { count: filtered.length })}</h2><p class="svadmin-u-b6b02c0ebef6 svadmin-u-fc7473ca09eb svadmin-u-bfa603190748">{i18n.t('publicProfile.searchTeams')}</p></div></div>
15
15
  <FilterToolbar bind:query placeholder={i18n.t('publicProfile.searchTeams')} />
16
- {#if filtered.length === 0}<DataState state="empty" title={i18n.t('publicProfile.noTeams')} />{:else}<div class="svadmin-u-f3c543ad5fe9 svadmin-u-0c3bc98565dd svadmin-u-e4d6f343b9ff svadmin-u-b86f7f946cd8">{#each filtered as team (team.id)}<TeamCard {team} />{/each}</div>{/if}
16
+ {#if teams.length === 0}<DataState state="empty" title={i18n.t('publicProfile.noTeams')} />{:else if filtered.length === 0}<DataState state="filtered-empty" title={i18n.t('publicProfile.noTeams')} description={i18n.t('publicProfile.searchTeams')} />{:else}<div class="svadmin-u-f3c543ad5fe9 svadmin-u-0c3bc98565dd svadmin-u-e4d6f343b9ff svadmin-u-b86f7f946cd8">{#each filtered as team (team.id)}<TeamCard {team} />{/each}</div>{/if}
17
17
  </section>
@@ -9,7 +9,7 @@ export declare const surfaceDesignContract: {
9
9
  readonly density: readonly ["comfortable", "compact"];
10
10
  };
11
11
  readonly pageFamilies: readonly ["list", "form", "detail", "workspace", "dashboard"];
12
- readonly pageStates: readonly ["ready", "loading", "empty", "error", "forbidden"];
12
+ readonly pageStates: readonly ["ready", "loading", "empty", "filtered-empty", "error", "forbidden"];
13
13
  readonly composition: {
14
14
  readonly maxPrimaryActions: 1;
15
15
  readonly secondaryActions: "overflow";
@@ -9,7 +9,7 @@ export const surfaceDesignContract = {
9
9
  density: ['comfortable', 'compact'],
10
10
  },
11
11
  pageFamilies: ['list', 'form', 'detail', 'workspace', 'dashboard'],
12
- pageStates: ['ready', 'loading', 'empty', 'error', 'forbidden'],
12
+ pageStates: ['ready', 'loading', 'empty', 'filtered-empty', 'error', 'forbidden'],
13
13
  composition: {
14
14
  maxPrimaryActions: 1,
15
15
  secondaryActions: 'overflow',
@@ -108,7 +108,7 @@
108
108
  transform: translateY(-50%);
109
109
  pointer-events: none;
110
110
  }
111
- .svadmin-filter-toolbar .svadmin-filter-toolbar__input {
111
+ .svadmin-filter-toolbar .svadmin-filter-toolbar__input[data-slot="input"] {
112
112
  height: var(--svadmin-filter-control-height);
113
113
  padding-inline: calc(var(--svadmin-space-5) + var(--svadmin-space-3));
114
114
  font-size: var(--svadmin-filter-control-font);
@@ -116,9 +116,9 @@
116
116
  }
117
117
  .svadmin-filter-toolbar .svadmin-filter-toolbar__clear {
118
118
  position: absolute;
119
- top: 50%;
119
+ inset-block: 0;
120
120
  inset-inline-end: var(--svadmin-space-1);
121
- transform: translateY(-50%);
121
+ margin-block: auto;
122
122
  }
123
123
  .svadmin-filter-toolbar .svadmin-filter-toolbar__toggle {
124
124
  flex-shrink: 0;
package/dist/recipes.d.ts CHANGED
@@ -120,6 +120,12 @@ declare const contentPageRecipeStyles: import("tailwind-variants").TVReturnType<
120
120
  root: string;
121
121
  };
122
122
  };
123
+ density: {
124
+ compact: {
125
+ root: string;
126
+ };
127
+ comfortable: {};
128
+ };
123
129
  }, {
124
130
  root: string;
125
131
  }, undefined, {
@@ -134,6 +140,12 @@ declare const contentPageRecipeStyles: import("tailwind-variants").TVReturnType<
134
140
  root: string;
135
141
  };
136
142
  };
143
+ density: {
144
+ compact: {
145
+ root: string;
146
+ };
147
+ comfortable: {};
148
+ };
137
149
  }, {
138
150
  root: string;
139
151
  }, import("tailwind-variants").TVReturnTypeLike<{
@@ -148,38 +160,28 @@ declare const contentPageRecipeStyles: import("tailwind-variants").TVReturnType<
148
160
  root: string;
149
161
  };
150
162
  };
163
+ density: {
164
+ compact: {
165
+ root: string;
166
+ };
167
+ comfortable: {};
168
+ };
151
169
  }, {
152
170
  root: string;
153
171
  }>>;
154
172
  declare const contentHeaderRecipeStyles: import("tailwind-variants").TVReturnType<{
155
- [key: string]: {
156
- [key: string]: import("tailwind-variants").ClassValue | {
157
- title?: import("tailwind-variants").ClassValue;
158
- description?: import("tailwind-variants").ClassValue;
159
- root?: import("tailwind-variants").ClassValue;
160
- row?: import("tailwind-variants").ClassValue;
161
- actions?: import("tailwind-variants").ClassValue;
162
- heading?: import("tailwind-variants").ClassValue;
163
- breadcrumbs?: import("tailwind-variants").ClassValue;
164
- currentCrumb?: import("tailwind-variants").ClassValue;
165
- eyebrow?: import("tailwind-variants").ClassValue;
166
- };
167
- };
168
- } | {
169
- [x: string]: {
170
- [x: string]: import("tailwind-variants").ClassValue | {
171
- title?: import("tailwind-variants").ClassValue;
172
- description?: import("tailwind-variants").ClassValue;
173
- root?: import("tailwind-variants").ClassValue;
174
- row?: import("tailwind-variants").ClassValue;
175
- actions?: import("tailwind-variants").ClassValue;
176
- heading?: import("tailwind-variants").ClassValue;
177
- breadcrumbs?: import("tailwind-variants").ClassValue;
178
- currentCrumb?: import("tailwind-variants").ClassValue;
179
- eyebrow?: import("tailwind-variants").ClassValue;
173
+ density: {
174
+ compact: {
175
+ root: string;
176
+ row: string;
177
+ heading: string;
178
+ title: string;
179
+ description: string;
180
+ actions: string;
180
181
  };
182
+ comfortable: {};
181
183
  };
182
- } | {}, {
184
+ }, {
183
185
  root: string;
184
186
  breadcrumbs: string;
185
187
  currentCrumb: string;
@@ -190,20 +192,18 @@ declare const contentHeaderRecipeStyles: import("tailwind-variants").TVReturnTyp
190
192
  description: string;
191
193
  actions: string;
192
194
  }, undefined, {
193
- [key: string]: {
194
- [key: string]: import("tailwind-variants").ClassValue | {
195
- title?: import("tailwind-variants").ClassValue;
196
- description?: import("tailwind-variants").ClassValue;
197
- root?: import("tailwind-variants").ClassValue;
198
- row?: import("tailwind-variants").ClassValue;
199
- actions?: import("tailwind-variants").ClassValue;
200
- heading?: import("tailwind-variants").ClassValue;
201
- breadcrumbs?: import("tailwind-variants").ClassValue;
202
- currentCrumb?: import("tailwind-variants").ClassValue;
203
- eyebrow?: import("tailwind-variants").ClassValue;
195
+ density: {
196
+ compact: {
197
+ root: string;
198
+ row: string;
199
+ heading: string;
200
+ title: string;
201
+ description: string;
202
+ actions: string;
204
203
  };
204
+ comfortable: {};
205
205
  };
206
- } | {}, {
206
+ }, {
207
207
  root: string;
208
208
  breadcrumbs: string;
209
209
  currentCrumb: string;
@@ -213,7 +213,19 @@ declare const contentHeaderRecipeStyles: import("tailwind-variants").TVReturnTyp
213
213
  title: string;
214
214
  description: string;
215
215
  actions: string;
216
- }, import("tailwind-variants").TVReturnTypeLike<unknown, {
216
+ }, import("tailwind-variants").TVReturnTypeLike<{
217
+ density: {
218
+ compact: {
219
+ root: string;
220
+ row: string;
221
+ heading: string;
222
+ title: string;
223
+ description: string;
224
+ actions: string;
225
+ };
226
+ comfortable: {};
227
+ };
228
+ }, {
217
229
  root: string;
218
230
  breadcrumbs: string;
219
231
  currentCrumb: string;
@@ -611,6 +623,13 @@ declare const surfaceMetricRecipeStyles: import("tailwind-variants").TVReturnTyp
611
623
  state: string;
612
624
  };
613
625
  };
626
+ density: {
627
+ compact: {
628
+ card: string;
629
+ state: string;
630
+ };
631
+ comfortable: {};
632
+ };
614
633
  }, {
615
634
  root: string;
616
635
  card: string;
@@ -639,6 +658,13 @@ declare const surfaceMetricRecipeStyles: import("tailwind-variants").TVReturnTyp
639
658
  state: string;
640
659
  };
641
660
  };
661
+ density: {
662
+ compact: {
663
+ card: string;
664
+ state: string;
665
+ };
666
+ comfortable: {};
667
+ };
642
668
  }, {
643
669
  root: string;
644
670
  card: string;
@@ -667,6 +693,13 @@ declare const surfaceMetricRecipeStyles: import("tailwind-variants").TVReturnTyp
667
693
  state: string;
668
694
  };
669
695
  };
696
+ density: {
697
+ compact: {
698
+ card: string;
699
+ state: string;
700
+ };
701
+ comfortable: {};
702
+ };
670
703
  }, {
671
704
  root: string;
672
705
  card: string;
@@ -674,28 +707,17 @@ declare const surfaceMetricRecipeStyles: import("tailwind-variants").TVReturnTyp
674
707
  state: string;
675
708
  }>>;
676
709
  declare const surfaceTableRecipeStyles: import("tailwind-variants").TVReturnType<{
677
- [key: string]: {
678
- [key: string]: import("tailwind-variants").ClassValue | {
679
- state?: import("tailwind-variants").ClassValue;
680
- content?: import("tailwind-variants").ClassValue;
681
- head?: import("tailwind-variants").ClassValue;
682
- header?: import("tailwind-variants").ClassValue;
683
- root?: import("tailwind-variants").ClassValue;
684
- cell?: import("tailwind-variants").ClassValue;
685
- };
686
- };
687
- } | {
688
- [x: string]: {
689
- [x: string]: import("tailwind-variants").ClassValue | {
690
- state?: import("tailwind-variants").ClassValue;
691
- content?: import("tailwind-variants").ClassValue;
692
- head?: import("tailwind-variants").ClassValue;
693
- header?: import("tailwind-variants").ClassValue;
694
- root?: import("tailwind-variants").ClassValue;
695
- cell?: import("tailwind-variants").ClassValue;
710
+ density: {
711
+ compact: {
712
+ header: string;
713
+ content: string;
714
+ head: string;
715
+ cell: string;
716
+ state: string;
696
717
  };
718
+ comfortable: {};
697
719
  };
698
- } | {}, {
720
+ }, {
699
721
  root: string;
700
722
  header: string;
701
723
  content: string;
@@ -703,24 +725,35 @@ declare const surfaceTableRecipeStyles: import("tailwind-variants").TVReturnType
703
725
  cell: string;
704
726
  state: string;
705
727
  }, undefined, {
706
- [key: string]: {
707
- [key: string]: import("tailwind-variants").ClassValue | {
708
- state?: import("tailwind-variants").ClassValue;
709
- content?: import("tailwind-variants").ClassValue;
710
- head?: import("tailwind-variants").ClassValue;
711
- header?: import("tailwind-variants").ClassValue;
712
- root?: import("tailwind-variants").ClassValue;
713
- cell?: import("tailwind-variants").ClassValue;
728
+ density: {
729
+ compact: {
730
+ header: string;
731
+ content: string;
732
+ head: string;
733
+ cell: string;
734
+ state: string;
714
735
  };
736
+ comfortable: {};
715
737
  };
716
- } | {}, {
738
+ }, {
717
739
  root: string;
718
740
  header: string;
719
741
  content: string;
720
742
  head: string;
721
743
  cell: string;
722
744
  state: string;
723
- }, import("tailwind-variants").TVReturnTypeLike<unknown, {
745
+ }, import("tailwind-variants").TVReturnTypeLike<{
746
+ density: {
747
+ compact: {
748
+ header: string;
749
+ content: string;
750
+ head: string;
751
+ cell: string;
752
+ state: string;
753
+ };
754
+ comfortable: {};
755
+ };
756
+ }, {
724
757
  root: string;
725
758
  header: string;
726
759
  content: string;
package/dist/recipes.js CHANGED
@@ -62,8 +62,12 @@ const contentPageRecipeStyles = tv({
62
62
  default: { root: "max-w-[74rem]" },
63
63
  wide: { root: "max-w-[92rem]" },
64
64
  },
65
+ density: {
66
+ compact: { root: "space-y-[var(--svadmin-space-4)]" },
67
+ comfortable: {},
68
+ },
65
69
  },
66
- defaultVariants: { width: "default" },
70
+ defaultVariants: { width: "default", density: "comfortable" },
67
71
  });
68
72
  const contentHeaderRecipeStyles = tv({
69
73
  slots: {
@@ -77,6 +81,20 @@ const contentHeaderRecipeStyles = tv({
77
81
  description: "max-w-[var(--svadmin-measure)] text-[length:var(--svadmin-text-body)] leading-[var(--svadmin-leading-body)] text-muted-foreground",
78
82
  actions: "flex shrink-0 flex-wrap items-center gap-[var(--svadmin-space-2)]",
79
83
  },
84
+ variants: {
85
+ density: {
86
+ compact: {
87
+ root: "space-y-[var(--svadmin-space-2)]",
88
+ row: "gap-[var(--svadmin-space-3)]",
89
+ heading: "space-y-[var(--svadmin-space-1)]",
90
+ title: "text-[length:var(--svadmin-text-section)] leading-[var(--svadmin-leading-tight)]",
91
+ description: "text-[length:var(--svadmin-text-label)] leading-[var(--svadmin-leading-body)]",
92
+ actions: "gap-[var(--svadmin-space-1)]",
93
+ },
94
+ comfortable: {},
95
+ },
96
+ },
97
+ defaultVariants: { density: "comfortable" },
80
98
  });
81
99
  const metricBlockRecipeStyles = tv({
82
100
  slots: {
@@ -177,8 +195,15 @@ const surfaceMetricRecipeStyles = tv({
177
195
  danger: { card: "border-s-destructive", state: "[--svadmin-metric-state-accent:var(--destructive)]" },
178
196
  info: { card: "border-s-info", state: "[--svadmin-metric-state-accent:var(--info)]" },
179
197
  },
198
+ density: {
199
+ compact: {
200
+ card: "p-3",
201
+ state: "[--svadmin-metric-state-padding:var(--svadmin-space-3)] [--svadmin-metric-state-height:4.5rem]",
202
+ },
203
+ comfortable: {},
204
+ },
180
205
  },
181
- defaultVariants: { tone: "neutral" },
206
+ defaultVariants: { tone: "neutral", density: "comfortable" },
182
207
  });
183
208
  const surfaceTableRecipeStyles = tv({
184
209
  slots: {
@@ -189,6 +214,19 @@ const surfaceTableRecipeStyles = tv({
189
214
  cell: "surface-table__cell [&[data-slot=table-cell]]:py-[var(--svadmin-space-2)] [&[data-slot=table-cell]]:text-[length:var(--svadmin-text-body)]",
190
215
  state: "surface-table__state text-muted-foreground [--svadmin-table-state-height:var(--svadmin-table-state-min-h)]",
191
216
  },
217
+ variants: {
218
+ density: {
219
+ compact: {
220
+ header: "px-[var(--svadmin-space-3)]",
221
+ content: "px-[var(--svadmin-space-3)]",
222
+ head: "[&[data-slot=table-head]]:py-[var(--svadmin-space-1)] [&[data-slot=table-head]]:text-[length:var(--svadmin-text-caption)]",
223
+ cell: "[&[data-slot=table-cell]]:py-[var(--svadmin-space-1)] [&[data-slot=table-cell]]:text-[length:var(--svadmin-text-caption)]",
224
+ state: "[--svadmin-table-state-height:var(--svadmin-table-state-min-h-compact)]",
225
+ },
226
+ comfortable: {},
227
+ },
228
+ },
229
+ defaultVariants: { density: "comfortable" },
192
230
  });
193
231
  // undefined 使用公开默认值;null 显式禁用该变体,不能交给 tv 再补默认值。
194
232
  export const buttonRecipe = ({ variant = buttonRecipeDefaults.variant, size = buttonRecipeDefaults.size, ...props } = {}) => ({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@svadmin/ui",
3
- "version": "0.86.0",
3
+ "version": "0.88.1",
4
4
  "description": "Pre-built admin UI components — AdminApp, AutoTable, AutoForm, Sidebar, Layout",
5
5
  "type": "module",
6
6
  "sideEffects": [