@svadmin/ui 0.76.0 → 0.77.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.
@@ -22,6 +22,7 @@ const registryEntries = [
22
22
  description: { kind: "string" },
23
23
  pageId: { kind: "string" },
24
24
  width: variant("contentPageRecipe", "width"),
25
+ density: variant("contentPageRecipe", "density"),
25
26
  class: { kind: "string" },
26
27
  },
27
28
  slots: { children: { kind: "snippet", required: true }, actions: { kind: "snippet" } },
@@ -37,6 +38,7 @@ const registryEntries = [
37
38
  eyebrow: { kind: "string" },
38
39
  description: { kind: "string" },
39
40
  breadcrumbs: { kind: "string[]" },
41
+ density: variant("contentHeaderRecipe", "density"),
40
42
  class: { kind: "string" },
41
43
  },
42
44
  slots: { actions: { kind: "snippet" } },
@@ -27,6 +27,7 @@
27
27
  import { Menu } from '@lucide/svelte';
28
28
  import { getComponentRegistry } from '../component-registry.svelte.js';
29
29
  import { Button } from './ui/button/index.js';
30
+ import { cn } from '../utils.js';
30
31
 
31
32
  const i18n = useTranslation();
32
33
 
@@ -34,7 +35,25 @@
34
35
  let shortcutsOpen = $state(false);
35
36
  let mobileMenuOpen = $state(false);
36
37
 
37
- let { children, title = 'Admin', menu, siteUrl, routeMode = 'auto', aiAssistant }: { children: Snippet; title?: string; menu?: MenuItem[]; siteUrl?: string; routeMode?: 'hash' | 'path' | 'auto'; aiAssistant?: Snippet<[LayoutAIAssistantProps]> } = $props();
38
+ interface Props {
39
+ children: Snippet;
40
+ title?: string;
41
+ menu?: MenuItem[];
42
+ siteUrl?: string;
43
+ routeMode?: 'hash' | 'path' | 'auto';
44
+ aiAssistant?: Snippet<[LayoutAIAssistantProps]>;
45
+ class?: string;
46
+ }
47
+
48
+ let {
49
+ children,
50
+ title = 'Admin',
51
+ menu,
52
+ siteUrl,
53
+ routeMode = 'auto',
54
+ aiAssistant,
55
+ class: className = '',
56
+ }: Props = $props();
38
57
  const layoutId = $props.id();
39
58
  const layoutScope = `svadmin-layout-${layoutId}`;
40
59
  const mainContentId = `${layoutScope}-main`;
@@ -150,7 +169,14 @@
150
169
  ontouchend={handleTouchEnd}
151
170
  />
152
171
  {#if loading}
153
- <div data-svadmin-layout-scope={layoutScope} class="svadmin-u-60fbb7713999 svadmin-u-ef114b5f5ad1" in:fade={{ duration: 150 }}>
172
+ <div
173
+ data-svadmin-layout-scope={layoutScope}
174
+ data-svadmin-layout
175
+ data-svadmin-layout-state="loading"
176
+ aria-busy="true"
177
+ class={cn('svadmin-u-60fbb7713999 svadmin-u-ef114b5f5ad1', className)}
178
+ in:fade={{ duration: 150 }}
179
+ >
154
180
  <div class="svadmin-u-99d72c7fc3e2 svadmin-u-9d60be3a6d80 svadmin-u-e2fe2012a697 svadmin-u-ac402894e356 svadmin-u-8e63407b5ceb svadmin-u-3e7ce58d64fa">
155
181
  <Skeleton class="svadmin-u-ed8a5df7b2fb svadmin-u-516b03df0b7c" />
156
182
  <div class="svadmin-u-6f7e013d6499 svadmin-u-31f2553311b6">
@@ -172,7 +198,14 @@
172
198
  <DevTools docked />
173
199
  </div>
174
200
  {:else}
175
- <div data-svadmin-layout-scope={layoutScope} class="svadmin-u-60fbb7713999 svadmin-u-ef114b5f5ad1 svadmin-u-e6f9e383a762" in:fade={{ duration: 200, delay: 50 }}>
201
+ <div
202
+ data-svadmin-layout-scope={layoutScope}
203
+ data-svadmin-layout
204
+ data-svadmin-layout-state="ready"
205
+ aria-busy="false"
206
+ class={cn('svadmin-u-60fbb7713999 svadmin-u-ef114b5f5ad1 svadmin-u-e6f9e383a762', className)}
207
+ in:fade={{ duration: 200, delay: 50 }}
208
+ >
176
209
  <button
177
210
  type="button"
178
211
  data-svadmin-skip-link={mainContentId}
@@ -241,7 +274,7 @@
241
274
 
242
275
  <!-- Content area: responsive padding + centered max-width container
243
276
  so wide screens don't stretch content indefinitely (avoids sparse layouts) -->
244
- <main id={mainContentId} tabindex="-1" data-svadmin-main class="svadmin-u-36e579c0b41c svadmin-u-92bf82f493b1 svadmin-u-2859c861d7de svadmin-u-f0faeb26d656 svadmin-u-c9b99cd93450 svadmin-u-cc06a6575385 svadmin-u-daf5dc5fac2b svadmin-u-e86fadb84483">
277
+ <main id={mainContentId} tabindex="-1" data-svadmin-main data-svadmin-layout-content class="svadmin-u-36e579c0b41c svadmin-u-92bf82f493b1 svadmin-u-2859c861d7de svadmin-u-f0faeb26d656 svadmin-u-c9b99cd93450 svadmin-u-cc06a6575385 svadmin-u-daf5dc5fac2b svadmin-u-e86fadb84483">
245
278
  <div class="svadmin-u-0e12dc7de920 svadmin-u-6da6a3c3f741 svadmin-u-9e3dc30c26a3">
246
279
  {#key getPath()}
247
280
  <!-- 路由切换立即释放旧页面,避免退出期间继续读取已失效的路由派生状态。 -->
@@ -252,7 +285,7 @@
252
285
  </div>
253
286
  </main>
254
287
 
255
- <footer class="svadmin-u-60fbb7713999 svadmin-u-0cfe3fb5e434 svadmin-u-012fbd121f37 svadmin-u-3960ffc248d9 svadmin-u-77c08e015d14 svadmin-u-77a2a20e90d4 svadmin-u-b950dda299d3 svadmin-u-05faf5c801ff svadmin-u-e6f9e383a762 svadmin-u-f0faeb26d656 svadmin-u-472f43d3a27b">
288
+ <footer data-svadmin-layout-footer class="svadmin-u-60fbb7713999 svadmin-u-0cfe3fb5e434 svadmin-u-012fbd121f37 svadmin-u-3960ffc248d9 svadmin-u-77c08e015d14 svadmin-u-77a2a20e90d4 svadmin-u-b950dda299d3 svadmin-u-05faf5c801ff svadmin-u-e6f9e383a762 svadmin-u-f0faeb26d656 svadmin-u-472f43d3a27b">
256
289
  <DevTools docked />
257
290
  {@render aiAssistant?.({ docked: true, scope: chatScope, ownerScope: layoutScope })}
258
291
  </footer>
@@ -5,14 +5,15 @@ export interface LayoutAIAssistantProps {
5
5
  }
6
6
  import type { Snippet } from 'svelte';
7
7
  import type { MenuItem } from '@svadmin/core';
8
- type $$ComponentProps = {
8
+ interface Props {
9
9
  children: Snippet;
10
10
  title?: string;
11
11
  menu?: MenuItem[];
12
12
  siteUrl?: string;
13
13
  routeMode?: 'hash' | 'path' | 'auto';
14
14
  aiAssistant?: Snippet<[LayoutAIAssistantProps]>;
15
- };
16
- declare const Layout: import("svelte").Component<$$ComponentProps, {}, "">;
15
+ class?: string;
16
+ }
17
+ declare const Layout: import("svelte").Component<Props, {}, "">;
17
18
  type Layout = ReturnType<typeof Layout>;
18
19
  export default Layout;
@@ -427,7 +427,7 @@
427
427
  <span class="svadmin-u-36e579c0b41c svadmin-u-2eba0d65d059">{i18n.t('common.toggleTheme')}</span>
428
428
  <span
429
429
  class="svadmin-u-6a60c09e6aaa svadmin-u-9cea05671a29 svadmin-u-ac204c108886 svadmin-u-3daca9af0861 svadmin-u-823f000e740a svadmin-u-6f8ab8abb126"
430
- style="background-color: {getColorThemes().find(c => c.id === getColorTheme())?.color ?? '#6366f1'}; --tw-ring-color: {getColorThemes().find(c => c.id === getColorTheme())?.color ?? '#6366f1'}"
430
+ style="background-color: {getColorThemes().find(c => c.id === getColorTheme())?.color ?? '#635bff'}; --tw-ring-color: {getColorThemes().find(c => c.id === getColorTheme())?.color ?? '#635bff'}"
431
431
  ></span>
432
432
  <!-- The dot previews the user-selected runtime theme swatch; the hex
433
433
  fallback only covers the case where no registered theme matches. -->
@@ -8,13 +8,27 @@
8
8
  description?: string;
9
9
  breadcrumbs?: string[];
10
10
  actions?: Snippet;
11
+ density?: 'compact' | 'comfortable';
11
12
  class?: string;
12
13
  }
13
- let { title, eyebrow, description, breadcrumbs = [], actions, class: className = '' }: Props = $props();
14
- const styles = contentHeader();
14
+ let {
15
+ title,
16
+ eyebrow,
17
+ description,
18
+ breadcrumbs = [],
19
+ actions,
20
+ density = 'comfortable',
21
+ class: className = '',
22
+ }: Props = $props();
23
+ const styles = $derived(contentHeader({ density }));
15
24
  </script>
16
25
 
17
- <header class={styles.root + ' ' + className} data-svadmin-page-header>
26
+ <header
27
+ class={styles.root + ' ' + className}
28
+ data-svadmin-page-header
29
+ data-svadmin-content-header
30
+ data-density={density}
31
+ >
18
32
  {#if breadcrumbs.length > 0}
19
33
  <nav aria-label="Breadcrumb" class={styles.breadcrumbs}>
20
34
  {#each breadcrumbs as crumb, index (crumb)}
@@ -5,6 +5,7 @@ interface Props {
5
5
  description?: string;
6
6
  breadcrumbs?: string[];
7
7
  actions?: Snippet;
8
+ density?: 'compact' | 'comfortable';
8
9
  class?: string;
9
10
  }
10
11
  declare const ContentPageHeader: import("svelte").Component<Props, {}, "">;
@@ -12,6 +12,7 @@
12
12
  children: Snippet;
13
13
  pageId?: string;
14
14
  width?: 'narrow' | 'default' | 'wide';
15
+ density?: 'compact' | 'comfortable';
15
16
  class?: string;
16
17
  }
17
18
 
@@ -23,16 +24,24 @@
23
24
  children,
24
25
  pageId = 'content',
25
26
  width = 'default',
27
+ density = 'comfortable',
26
28
  class: className = '',
27
29
  }: Props = $props();
28
30
 
29
31
  // 保留旧组件对非窄屏/宽屏输入的默认宽度回退。
32
+ const resolvedWidth = $derived(width === 'narrow' || width === 'wide' ? width : 'default');
30
33
  const styles = $derived(contentPage({
31
- width: width === 'narrow' || width === 'wide' ? width : 'default',
34
+ width: resolvedWidth,
35
+ density,
32
36
  }));
33
37
  </script>
34
38
 
35
- <div data-svadmin-content-page={pageId} class={styles.root + ' ' + className}>
36
- {#if title}<ContentPageHeader {title} {...definedOptions({ "eyebrow": eyebrow })} {...definedOptions({ "description": description })} {...definedOptions({ "actions": actions })} />{/if}
39
+ <div
40
+ data-svadmin-content-page={pageId}
41
+ data-svadmin-content-page-width={resolvedWidth}
42
+ data-density={density}
43
+ class={styles.root + ' ' + className}
44
+ >
45
+ {#if title}<ContentPageHeader {title} {density} {...definedOptions({ "eyebrow": eyebrow })} {...definedOptions({ "description": description })} {...definedOptions({ "actions": actions })} />{/if}
37
46
  {@render children()}
38
47
  </div>
@@ -7,6 +7,7 @@ interface Props {
7
7
  children: Snippet;
8
8
  pageId?: string;
9
9
  width?: 'narrow' | 'default' | 'wide';
10
+ density?: 'compact' | 'comfortable';
10
11
  class?: string;
11
12
  }
12
13
  declare const ContentPageShell: import("svelte").Component<Props, {}, "">;
@@ -35,7 +35,7 @@
35
35
  {#if loading}
36
36
  <Skeleton class={styles.skeleton} />
37
37
  {:else}
38
- <p class={styles.value}>{value}</p>
38
+ <p data-svadmin-metric-value class={styles.value}>{value}</p>
39
39
  {#if detail || trend}<div class={styles.meta}>{#if trend}<span class={styles.trend}>{trend}</span>{/if}{#if detail}<span class={styles.detail}>{detail}</span>{/if}</div>{/if}
40
40
  {/if}
41
41
  </div>
package/dist/recipes.d.ts CHANGED
@@ -120,6 +120,14 @@ 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
+ root: string;
129
+ };
130
+ };
123
131
  }, {
124
132
  root: string;
125
133
  }, undefined, {
@@ -134,6 +142,14 @@ declare const contentPageRecipeStyles: import("tailwind-variants").TVReturnType<
134
142
  root: string;
135
143
  };
136
144
  };
145
+ density: {
146
+ compact: {
147
+ root: string;
148
+ };
149
+ comfortable: {
150
+ root: string;
151
+ };
152
+ };
137
153
  }, {
138
154
  root: string;
139
155
  }, import("tailwind-variants").TVReturnTypeLike<{
@@ -148,38 +164,37 @@ declare const contentPageRecipeStyles: import("tailwind-variants").TVReturnType<
148
164
  root: string;
149
165
  };
150
166
  };
167
+ density: {
168
+ compact: {
169
+ root: string;
170
+ };
171
+ comfortable: {
172
+ root: string;
173
+ };
174
+ };
151
175
  }, {
152
176
  root: string;
153
177
  }>>;
154
178
  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
- heading?: import("tailwind-variants").ClassValue;
162
- breadcrumbs?: import("tailwind-variants").ClassValue;
163
- currentCrumb?: import("tailwind-variants").ClassValue;
164
- eyebrow?: import("tailwind-variants").ClassValue;
165
- actions?: import("tailwind-variants").ClassValue;
179
+ density: {
180
+ compact: {
181
+ root: string;
182
+ row: string;
183
+ heading: string;
184
+ title: string;
185
+ description: string;
186
+ actions: string;
166
187
  };
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
- heading?: import("tailwind-variants").ClassValue;
176
- breadcrumbs?: import("tailwind-variants").ClassValue;
177
- currentCrumb?: import("tailwind-variants").ClassValue;
178
- eyebrow?: import("tailwind-variants").ClassValue;
179
- actions?: import("tailwind-variants").ClassValue;
188
+ comfortable: {
189
+ root: string;
190
+ row: string;
191
+ heading: string;
192
+ title: string;
193
+ description: string;
194
+ actions: string;
180
195
  };
181
196
  };
182
- } | {}, {
197
+ }, {
183
198
  root: string;
184
199
  breadcrumbs: string;
185
200
  currentCrumb: string;
@@ -190,20 +205,25 @@ declare const contentHeaderRecipeStyles: import("tailwind-variants").TVReturnTyp
190
205
  description: string;
191
206
  actions: string;
192
207
  }, 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
- heading?: import("tailwind-variants").ClassValue;
200
- breadcrumbs?: import("tailwind-variants").ClassValue;
201
- currentCrumb?: import("tailwind-variants").ClassValue;
202
- eyebrow?: import("tailwind-variants").ClassValue;
203
- actions?: import("tailwind-variants").ClassValue;
208
+ density: {
209
+ compact: {
210
+ root: string;
211
+ row: string;
212
+ heading: string;
213
+ title: string;
214
+ description: string;
215
+ actions: string;
216
+ };
217
+ comfortable: {
218
+ root: string;
219
+ row: string;
220
+ heading: string;
221
+ title: string;
222
+ description: string;
223
+ actions: string;
204
224
  };
205
225
  };
206
- } | {}, {
226
+ }, {
207
227
  root: string;
208
228
  breadcrumbs: string;
209
229
  currentCrumb: string;
@@ -213,7 +233,26 @@ declare const contentHeaderRecipeStyles: import("tailwind-variants").TVReturnTyp
213
233
  title: string;
214
234
  description: string;
215
235
  actions: string;
216
- }, import("tailwind-variants").TVReturnTypeLike<unknown, {
236
+ }, import("tailwind-variants").TVReturnTypeLike<{
237
+ density: {
238
+ compact: {
239
+ root: string;
240
+ row: string;
241
+ heading: string;
242
+ title: string;
243
+ description: string;
244
+ actions: string;
245
+ };
246
+ comfortable: {
247
+ root: string;
248
+ row: string;
249
+ heading: string;
250
+ title: string;
251
+ description: string;
252
+ actions: string;
253
+ };
254
+ };
255
+ }, {
217
256
  root: string;
218
257
  breadcrumbs: string;
219
258
  currentCrumb: string;
package/dist/recipes.js CHANGED
@@ -55,28 +55,53 @@ const buttonRecipeDefaults = { variant: "default", size: "default" };
55
55
  const badgeRecipeDefaults = { variant: "default" };
56
56
  const textareaRecipeStyles = tv({ base: "svadmin-textarea" });
57
57
  const contentPageRecipeStyles = tv({
58
- slots: { root: `${semantic.page} svadmin-content-page space-y-6` },
58
+ slots: { root: `${semantic.page} svadmin-content-page` },
59
59
  variants: {
60
60
  width: {
61
61
  narrow: { root: "max-w-3xl" },
62
62
  default: { root: "max-w-[74rem]" },
63
63
  wide: { root: "max-w-[92rem]" },
64
64
  },
65
+ density: {
66
+ compact: { root: "space-y-4" },
67
+ comfortable: { root: "space-y-6" },
68
+ },
65
69
  },
66
- defaultVariants: { width: "default" },
70
+ defaultVariants: { width: "default", density: "comfortable" },
67
71
  });
68
72
  const contentHeaderRecipeStyles = tv({
69
73
  slots: {
70
- root: "svadmin-content-header space-y-3",
74
+ root: "svadmin-content-header",
71
75
  breadcrumbs: `${semantic.group} text-xs text-muted-foreground`,
72
76
  currentCrumb: "text-foreground",
73
- row: "flex flex-col gap-6 sm:flex-row sm:items-start sm:justify-between",
74
- heading: "min-w-0 space-y-1",
77
+ row: "flex flex-col sm:flex-row sm:items-start sm:justify-between",
78
+ heading: "min-w-0",
75
79
  eyebrow: "text-xs font-medium text-muted-foreground",
76
- title: "text-xl font-semibold leading-tight text-foreground",
77
- description: "max-w-2xl text-sm leading-6 text-muted-foreground",
78
- actions: "flex shrink-0 flex-wrap items-center gap-2",
80
+ title: "font-semibold leading-tight text-foreground",
81
+ description: "max-w-2xl text-muted-foreground",
82
+ actions: "flex shrink-0 flex-wrap items-center",
79
83
  },
84
+ variants: {
85
+ density: {
86
+ compact: {
87
+ root: "space-y-2",
88
+ row: "gap-3",
89
+ heading: "space-y-0.5",
90
+ title: "text-lg leading-tight",
91
+ description: "text-[0.8125rem] leading-5",
92
+ actions: "gap-1.5",
93
+ },
94
+ comfortable: {
95
+ root: "space-y-3",
96
+ row: "gap-6",
97
+ heading: "space-y-1",
98
+ title: "text-xl leading-tight",
99
+ description: "text-sm leading-6",
100
+ actions: "gap-2",
101
+ },
102
+ },
103
+ },
104
+ defaultVariants: { density: "comfortable" },
80
105
  });
81
106
  const metricBlockRecipeStyles = tv({
82
107
  slots: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@svadmin/ui",
3
- "version": "0.76.0",
3
+ "version": "0.77.0",
4
4
  "description": "Pre-built admin UI components — AdminApp, AutoTable, AutoForm, Sidebar, Layout",
5
5
  "type": "module",
6
6
  "sideEffects": [
@@ -88,7 +88,7 @@
88
88
  "@codemirror/lang-json": "^6.0.0",
89
89
  "@codemirror/lang-markdown": "^6.0.0",
90
90
  "@codemirror/lang-sql": "^6.0.0",
91
- "@svadmin/core": "^0.56.0",
91
+ "@svadmin/core": "^0.57.0",
92
92
  "@tanstack/svelte-query": "^6.1.48",
93
93
  "codemirror": "^6.0.0",
94
94
  "isomorphic-dompurify": "^3.23.0",
@@ -175,7 +175,7 @@
175
175
  "@codemirror/lang-markdown": "^6.5.2",
176
176
  "@codemirror/lang-sql": "^6.10.0",
177
177
  "@happy-dom/global-registrator": "^20.11.12",
178
- "@svadmin/ai-elements": "^0.10.1",
178
+ "@svadmin/ai-elements": "^0.10.2",
179
179
  "@sveltejs/vite-plugin-svelte": "^7.3.0",
180
180
  "@tailwindcss/cli": "4.3.3",
181
181
  "@testing-library/dom": "^10.4.1",