@svadmin/ui 0.69.1 → 0.72.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.
Files changed (158) hide show
  1. package/dist/actions.js +1 -1
  2. package/dist/app.css +102 -63
  3. package/dist/app.theme.css +102 -63
  4. package/dist/component-registry.svelte.d.ts +33 -19
  5. package/dist/component-registry.svelte.js +6 -4
  6. package/dist/components/AdminApp.svelte +55 -53
  7. package/dist/components/AdminApp.svelte.d.ts +2 -2
  8. package/dist/components/ApiSettings.svelte +19 -1
  9. package/dist/components/AuditLogDrawer.svelte +14 -3
  10. package/dist/components/AutoForm.svelte +146 -48
  11. package/dist/components/AutoForm.svelte.d.ts +2 -0
  12. package/dist/components/AutoTable.svelte +358 -258
  13. package/dist/components/AutoTable.svelte.d.ts +1 -0
  14. package/dist/components/BoundRecordDetailDrawer.svelte +166 -0
  15. package/dist/components/BoundRecordDetailDrawer.svelte.d.ts +9 -0
  16. package/dist/components/Breadcrumbs.svelte +7 -4
  17. package/dist/components/CancelTaskButton.svelte +9 -5
  18. package/dist/components/ColumnHeaderFilter.svelte +4 -3
  19. package/dist/components/ColumnSettings.svelte +1 -0
  20. package/dist/components/ComboboxField.svelte +90 -30
  21. package/dist/components/ComboboxField.svelte.d.ts +2 -2
  22. package/dist/components/CreatePage.svelte +3 -1
  23. package/dist/components/DetailDrawer.svelte +4 -0
  24. package/dist/components/DetailDrawer.svelte.d.ts +1 -0
  25. package/dist/components/DevTools.svelte +1 -1
  26. package/dist/components/DraggableGrid.svelte +1 -0
  27. package/dist/components/DraggableHeader.svelte +5 -9
  28. package/dist/components/DraggableHeader.svelte.d.ts +43 -18
  29. package/dist/components/DraggableRowTable.svelte +2 -2
  30. package/dist/components/DynamicFormList.svelte +17 -17
  31. package/dist/components/DynamicFormList.svelte.d.ts +2 -1
  32. package/dist/components/EditPage.svelte +3 -1
  33. package/dist/components/EditableTable.svelte +4 -0
  34. package/dist/components/ErrorPage.svelte +3 -1
  35. package/dist/components/FieldDisplay.svelte +14 -0
  36. package/dist/components/FieldDisplay.svelte.d.ts +7 -0
  37. package/dist/components/FieldRenderer.svelte +9 -5
  38. package/dist/components/FilterBuilder.svelte +19 -8
  39. package/dist/components/FilterDropdown.svelte.d.ts +1 -1
  40. package/dist/components/Header.svelte +5 -6
  41. package/dist/components/ImportWizard.svelte +3 -2
  42. package/dist/components/InfiniteList.svelte +22 -8
  43. package/dist/components/InfiniteList.svelte.d.ts +5 -3
  44. package/dist/components/InlineEdit.svelte +114 -44
  45. package/dist/components/JsonSchemaForm.svelte +19 -14
  46. package/dist/components/Layout.svelte +19 -15
  47. package/dist/components/LazyTaskQueueDrawer.svelte +26 -0
  48. package/dist/components/LazyTaskQueueDrawer.svelte.d.ts +8 -0
  49. package/dist/components/ListPage.svelte +7 -6
  50. package/dist/components/MasterDetailView.svelte +3 -3
  51. package/dist/components/MediaLibraryModal.svelte +5 -2
  52. package/dist/components/MentionsInput.svelte +4 -3
  53. package/dist/components/NavigateToResource.svelte +3 -2
  54. package/dist/components/PermissionMatrix.svelte +4 -3
  55. package/dist/components/ProfilePage.svelte +2 -2
  56. package/dist/components/QuickEditDrawer.svelte +39 -0
  57. package/dist/components/QuickEditDrawer.svelte.d.ts +8 -0
  58. package/dist/components/RecordDetailDrawer.svelte +14 -108
  59. package/dist/components/RecordRowActions.svelte +41 -0
  60. package/dist/components/RecordRowActions.svelte.d.ts +14 -0
  61. package/dist/components/ResourceAccessGuard.svelte +7 -5
  62. package/dist/components/RetryTaskButton.svelte +9 -5
  63. package/dist/components/RowActions.svelte +5 -3
  64. package/dist/components/SensitiveDataMask.svelte +3 -3
  65. package/dist/components/SettingsPage.svelte +1 -1
  66. package/dist/components/ShowPage.svelte +44 -38
  67. package/dist/components/Sidebar.svelte +10 -8
  68. package/dist/components/SplitPaneLayout.svelte +4 -2
  69. package/dist/components/StepsForm.svelte +161 -44
  70. package/dist/components/StepsForm.svelte.d.ts +1 -0
  71. package/dist/components/TaskDetails.svelte +20 -7
  72. package/dist/components/TaskList.svelte +19 -7
  73. package/dist/components/TaskQueueDrawer.svelte +21 -22
  74. package/dist/components/Toast.svelte +4 -2
  75. package/dist/components/TooltipButton.svelte +1 -1
  76. package/dist/components/TreeSelect.svelte +4 -2
  77. package/dist/components/ValidatedField.svelte +27 -0
  78. package/dist/components/ValidatedField.svelte.d.ts +35 -0
  79. package/dist/components/account/ImportMembersPage.svelte +2 -2
  80. package/dist/components/account/SecurityLogPage.svelte +3 -1
  81. package/dist/components/account/SettingsEnterprisePage.svelte +7 -5
  82. package/dist/components/account/TeamMembersPage.svelte +1 -1
  83. package/dist/components/buttons/CloneButton.svelte +6 -4
  84. package/dist/components/buttons/DeleteButton.svelte +95 -29
  85. package/dist/components/buttons/DeleteButton.svelte.d.ts +2 -0
  86. package/dist/components/buttons/EditButton.svelte +6 -4
  87. package/dist/components/buttons/ExportButton.svelte +22 -4
  88. package/dist/components/buttons/ImportButton.svelte +83 -38
  89. package/dist/components/buttons/ImportButton.svelte.d.ts +6 -10
  90. package/dist/components/buttons/RefreshButton.svelte +48 -8
  91. package/dist/components/buttons/ShowButton.svelte +6 -4
  92. package/dist/components/content/ApiKeyList.svelte +3 -1
  93. package/dist/components/content/ContentPageShell.svelte +3 -1
  94. package/dist/components/content/FileList.svelte +3 -1
  95. package/dist/components/content/MemberList.svelte +3 -1
  96. package/dist/components/content/NetworkTable.svelte +3 -1
  97. package/dist/components/content/SecurityEventTable.svelte +3 -1
  98. package/dist/components/content/StatusTabs.svelte +2 -2
  99. package/dist/components/fieldComponentMap.d.ts +18 -13
  100. package/dist/components/fieldComponentMap.js +95 -47
  101. package/dist/components/fields/AvatarField.svelte +1 -1
  102. package/dist/components/fields/ChoiceField.svelte +16 -0
  103. package/dist/components/fields/ChoiceField.svelte.d.ts +10 -0
  104. package/dist/components/fields/CodeField.svelte +11 -12
  105. package/dist/components/fields/CodeField.svelte.d.ts +1 -1
  106. package/dist/components/fields/DateRangeField.svelte +2 -4
  107. package/dist/components/fields/ImageField.svelte +3 -1
  108. package/dist/components/fields/ImagesField.svelte +23 -0
  109. package/dist/components/fields/ImagesField.svelte.d.ts +6 -0
  110. package/dist/components/fields/JsonField.svelte +19 -13
  111. package/dist/components/fields/PasswordField.svelte +5 -0
  112. package/dist/components/fields/PasswordField.svelte.d.ts +6 -0
  113. package/dist/components/fields/json-value.d.ts +11 -0
  114. package/dist/components/fields/json-value.js +28 -0
  115. package/dist/components/profile/ActivityTimeline.svelte +2 -2
  116. package/dist/components/record-detail.svelte.d.ts +16 -0
  117. package/dist/components/record-detail.svelte.js +28 -0
  118. package/dist/components/saved-list-views.js +29 -29
  119. package/dist/components/table-contract.d.ts +13 -0
  120. package/dist/components/table-contract.js +42 -0
  121. package/dist/components/task-utils.js +4 -2
  122. package/dist/components/ui/alert/alert-variants.d.ts +2 -3
  123. package/dist/components/ui/alert/alert-variants.js +1 -2
  124. package/dist/components/ui/alert/alert.svelte +4 -5
  125. package/dist/components/ui/alert/alert.svelte.d.ts +3 -8
  126. package/dist/components/ui/alert/index.js +0 -2
  127. package/dist/components/ui/avatar/avatar-variants.d.ts +2 -3
  128. package/dist/components/ui/avatar/avatar-variants.js +1 -2
  129. package/dist/components/ui/avatar/avatar.svelte +5 -6
  130. package/dist/components/ui/avatar/avatar.svelte.d.ts +3 -8
  131. package/dist/components/ui/badge/badge-variants.d.ts +2 -3
  132. package/dist/components/ui/badge/badge-variants.js +1 -2
  133. package/dist/components/ui/badge/badge.svelte +4 -5
  134. package/dist/components/ui/badge/badge.svelte.d.ts +3 -8
  135. package/dist/components/ui/badge/index.js +0 -2
  136. package/dist/components/ui/button/button-variants.d.ts +2 -3
  137. package/dist/components/ui/button/button-variants.js +1 -2
  138. package/dist/components/ui/button/button.svelte +100 -44
  139. package/dist/components/ui/button/button.svelte.d.ts +8 -12
  140. package/dist/components/ui/button/index.js +0 -2
  141. package/dist/components/ui/command/command-dialog.svelte +3 -1
  142. package/dist/components/ui/command/command-dialog.svelte.d.ts +1 -1
  143. package/dist/components/ui/command/command-group.svelte +4 -2
  144. package/dist/components/ui/command/command-item.svelte +4 -2
  145. package/dist/components/ui/command/command-separator.svelte +3 -1
  146. package/dist/components/ui/command/index.d.ts +5 -5
  147. package/dist/components/ui/input/input.svelte +40 -20
  148. package/dist/components/ui/input/input.svelte.d.ts +5 -4
  149. package/dist/components/ui/sheet/sheet.svelte +9 -3
  150. package/dist/components/ui/sheet/sheet.svelte.d.ts +1 -0
  151. package/dist/components/ui/tooltip/tooltip.svelte +4 -2
  152. package/dist/index.d.ts +1 -1
  153. package/dist/inline-value.d.ts +5 -0
  154. package/dist/inline-value.js +24 -0
  155. package/dist/numeric-input.d.ts +2 -0
  156. package/dist/numeric-input.js +9 -0
  157. package/dist/types.d.ts +3 -3
  158. package/package.json +11 -5
@@ -2,6 +2,7 @@
2
2
  import { useTranslation } from '@svadmin/core/i18n';
3
3
  import { Button } from '../ui/button/index.js';
4
4
  import { Copy, Check, ChevronDown, ChevronRight } from '@lucide/svelte';
5
+ import { formatJsonValue } from './json-value';
5
6
 
6
7
  const i18n = useTranslation();
7
8
 
@@ -12,29 +13,34 @@
12
13
  let expanded = $state(false);
13
14
  let copied = $state(false);
14
15
  let copyTimer: ReturnType<typeof setTimeout> | undefined;
16
+ let destroyed = false;
15
17
 
16
18
  $effect(() => {
17
- return () => { if (copyTimer) clearTimeout(copyTimer); };
19
+ return () => { destroyed = true; if (copyTimer) clearTimeout(copyTimer); };
18
20
  });
19
21
 
20
- const formatted = $derived(
21
- value != null ? JSON.stringify(value, null, 2) : '—'
22
- );
22
+ const display = $derived(formatJsonValue(value));
23
23
 
24
- const preview = $derived(
25
- value != null ? JSON.stringify(value).slice(0, 80) + (JSON.stringify(value).length > 80 ? '...' : '') : '—'
26
- );
27
-
28
- function copyJson() {
29
- navigator.clipboard.writeText(formatted);
24
+ async function copyJson() {
25
+ if (display.status !== 'valid') return;
26
+ const clipboard = globalThis.navigator?.clipboard;
27
+ if (!clipboard?.writeText) return;
28
+ try {
29
+ await clipboard.writeText(display.formatted);
30
+ } catch {
31
+ return;
32
+ }
33
+ if (destroyed) return;
30
34
  copied = true;
31
35
  if (copyTimer) clearTimeout(copyTimer);
32
36
  copyTimer = setTimeout(() => { copied = false; }, 2000);
33
37
  }
34
38
  </script>
35
39
 
36
- {#if value == null}
40
+ {#if display.status === 'empty'}
37
41
  <span class="svadmin-u-bfa603190748">—</span>
42
+ {:else if display.status === 'invalid'}
43
+ <span role="status" data-svadmin-invalid-field>{i18n.t('validation.invalidFormat')}</span>
38
44
  {:else}
39
45
  <div class="svadmin-u-da7c36cd8867">
40
46
  <div class="svadmin-u-60fbb7713999 svadmin-u-3960ffc248d9 svadmin-u-77a2a20e90d4">
@@ -66,9 +72,9 @@
66
72
  </Button>
67
73
  </div>
68
74
  {#if expanded}
69
- <pre class="svadmin-u-5f22e64f2282 svadmin-u-2ef11f1cb219 svadmin-u-eb6e8b881acd svadmin-u-359090c2d529 svadmin-u-73fc3fb18ceb svadmin-u-8aee2b07b47d svadmin-u-0e65706bcccd">{formatted}</pre>
75
+ <pre class="svadmin-u-5f22e64f2282 svadmin-u-2ef11f1cb219 svadmin-u-eb6e8b881acd svadmin-u-359090c2d529 svadmin-u-73fc3fb18ceb svadmin-u-8aee2b07b47d svadmin-u-0e65706bcccd">{display.formatted}</pre>
70
76
  {:else}
71
- <code class="svadmin-u-359090c2d529 svadmin-u-0e65706bcccd svadmin-u-2ef11f1cb219 svadmin-u-45d828117213 svadmin-u-465609a240a8 svadmin-u-07389a777c1f">{preview}</code>
77
+ <code class="svadmin-u-359090c2d529 svadmin-u-0e65706bcccd svadmin-u-2ef11f1cb219 svadmin-u-45d828117213 svadmin-u-465609a240a8 svadmin-u-07389a777c1f">{display.preview}</code>
72
78
  {/if}
73
79
  </div>
74
80
  {/if}
@@ -0,0 +1,5 @@
1
+ <script lang="ts">
2
+ let { value }: { value: string | null | undefined } = $props();
3
+ </script>
4
+
5
+ <span>{value ? '********' : '—'}</span>
@@ -0,0 +1,6 @@
1
+ type $$ComponentProps = {
2
+ value: string | null | undefined;
3
+ };
4
+ declare const PasswordField: import("svelte").Component<$$ComponentProps, {}, "">;
5
+ type PasswordField = ReturnType<typeof PasswordField>;
6
+ export default PasswordField;
@@ -0,0 +1,11 @@
1
+ export declare const jsonValueSchema: import("@sinclair/typebox").TRecursive<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TNull, import("@sinclair/typebox").TBoolean, import("@sinclair/typebox").TNumber, import("@sinclair/typebox").TString, import("@sinclair/typebox").TArray<import("@sinclair/typebox").TThis>, import("@sinclair/typebox").TRecord<import("@sinclair/typebox").TString, import("@sinclair/typebox").TThis>]>>;
2
+ export type JsonDisplay = {
3
+ status: 'empty';
4
+ } | {
5
+ status: 'invalid';
6
+ } | {
7
+ status: 'valid';
8
+ formatted: string;
9
+ preview: string;
10
+ };
11
+ export declare function formatJsonValue(value: unknown): JsonDisplay;
@@ -0,0 +1,28 @@
1
+ import { Type } from '@sinclair/typebox';
2
+ import { createExactSchemaValidator } from '@svadmin/core/schema';
3
+ export const jsonValueSchema = Type.Recursive(Self => Type.Union([
4
+ Type.Null(), Type.Boolean(), Type.Number(), Type.String(),
5
+ Type.Array(Self), Type.Record(Type.String(), Self),
6
+ ]));
7
+ const validator = createExactSchemaValidator(jsonValueSchema);
8
+ export function formatJsonValue(value) {
9
+ if (value == null)
10
+ return { status: 'empty' };
11
+ try {
12
+ // Serialization rejects circular graphs before recursive schema traversal.
13
+ const compact = JSON.stringify(value);
14
+ if (typeof compact !== 'string' || !validator.Check(value))
15
+ return { status: 'invalid' };
16
+ const formatted = JSON.stringify(value, null, 2);
17
+ if (typeof formatted !== 'string')
18
+ return { status: 'invalid' };
19
+ return {
20
+ status: 'valid',
21
+ formatted,
22
+ preview: compact.slice(0, 80) + (compact.length > 80 ? '...' : ''),
23
+ };
24
+ }
25
+ catch {
26
+ return { status: 'invalid' };
27
+ }
28
+ }
@@ -12,7 +12,7 @@
12
12
  let { activities = [], showAutoRefresh = true }: Props = $props();
13
13
  let autoRefresh = $state(false);
14
14
  const i18n = useTranslation();
15
- const config: Record<string, { icon: Component; tone: string; action: string }> = {
15
+ const config: Record<ActivityItem['type'], { icon: Component; tone: string; action: string }> = {
16
16
  posted: { icon: MessageSquare, tone: 'svadmin-u-375dc44df6e9 svadmin-u-20aaf08a7ed1', action: i18n.t('publicProfile.activityPosted') },
17
17
  commented: { icon: MessageSquare, tone: 'svadmin-u-17a9f7af2265 svadmin-u-76747e5e02ff', action: i18n.t('publicProfile.activityCommented') },
18
18
  joined: { icon: UserPlus, tone: 'svadmin-u-2ef11f1cb219 svadmin-u-bfa603190748', action: i18n.t('publicProfile.activityJoined') },
@@ -23,5 +23,5 @@
23
23
 
24
24
  <section class="svadmin-u-3e7ce58d64fa">
25
25
  <div class="svadmin-u-60fbb7713999 svadmin-u-8dddea0773ed svadmin-u-1004c0c3954c svadmin-u-020ba687fa12 svadmin-u-64cac80d2e9a svadmin-u-3b9871a0bf93"><SectionHeader title={i18n.t("publicProfile.activity")} />{#if showAutoRefresh}<div class="svadmin-u-60fbb7713999 svadmin-u-3960ffc248d9 svadmin-u-44ee8ba0a421 svadmin-u-421ac2be5045 svadmin-u-ca6bcd4b6f3f svadmin-u-18049387f0af svadmin-u-eb6a3cef9686"><Button size="sm" variant={autoRefresh ? "default" : "ghost"} onclick={() => autoRefresh = true}>{i18n.t("common.on")}</Button><Button size="sm" variant={!autoRefresh ? "default" : "ghost"} onclick={() => autoRefresh = false}>{i18n.t("common.off")}</Button></div>{/if}</div>
26
- {#if activities.length === 0}<DataState state="empty" title={i18n.t('publicProfile.noActivity')} />{:else}<Card.Card><Card.CardContent class="svadmin-u-8a539c7fe216"><div class="svadmin-u-fa6acbf81d74 svadmin-u-e783642739e3">{#each activities as activity (activity.id)}{@const item = config[activity.type] ?? config.posted}<div class="svadmin-u-60fbb7713999 svadmin-u-60541e1e26f8 svadmin-u-1004c0c3954c svadmin-u-8e63407b5ceb">{#if activity.avatar}<Avatar src={activity.avatar} alt={activity.user} size="sm" />{:else}<span class={'svadmin-u-60fbb7713999 svadmin-u-d8f5213f0fe0 svadmin-u-012fbd121f37 svadmin-u-3960ffc248d9 svadmin-u-86843cf1e227 svadmin-u-ac204c108886 ' + item.tone}><item.icon class="svadmin-u-f7b5fa971871" /></span>{/if}<div class="svadmin-u-7e0b7cdf1a94 svadmin-u-36e579c0b41c"><p class="svadmin-u-fc7473ca09eb"><strong class="svadmin-u-d4108abe6359">{activity.user}</strong><span class="svadmin-u-bfa603190748"> {item.action} </span>{#if activity.target}<strong class="svadmin-u-d4108abe6359">{activity.target}</strong>{/if}</p>{#if activity.content}<p class="svadmin-u-b6b02c0ebef6 svadmin-u-fc7473ca09eb svadmin-u-bfa603190748">{activity.content}</p>{/if}<p class="svadmin-u-b6b02c0ebef6 svadmin-u-359090c2d529 svadmin-u-bfa603190748">{activity.timestamp}</p></div></div>{/each}</div></Card.CardContent></Card.Card>{/if}
26
+ {#if activities.length === 0}<DataState state="empty" title={i18n.t('publicProfile.noActivity')} />{:else}<Card.Card><Card.CardContent class="svadmin-u-8a539c7fe216"><div class="svadmin-u-fa6acbf81d74 svadmin-u-e783642739e3">{#each activities as activity (activity.id)}{@const item = config[activity.type] ?? config['posted']}<div class="svadmin-u-60fbb7713999 svadmin-u-60541e1e26f8 svadmin-u-1004c0c3954c svadmin-u-8e63407b5ceb">{#if activity.avatar}<Avatar src={activity.avatar} alt={activity.user} size="sm" />{:else}<span class={'svadmin-u-60fbb7713999 svadmin-u-d8f5213f0fe0 svadmin-u-012fbd121f37 svadmin-u-3960ffc248d9 svadmin-u-86843cf1e227 svadmin-u-ac204c108886 ' + item.tone}><item.icon class="svadmin-u-f7b5fa971871" /></span>{/if}<div class="svadmin-u-7e0b7cdf1a94 svadmin-u-36e579c0b41c"><p class="svadmin-u-fc7473ca09eb"><strong class="svadmin-u-d4108abe6359">{activity.user}</strong><span class="svadmin-u-bfa603190748"> {item.action} </span>{#if activity.target}<strong class="svadmin-u-d4108abe6359">{activity.target}</strong>{/if}</p>{#if activity.content}<p class="svadmin-u-b6b02c0ebef6 svadmin-u-fc7473ca09eb svadmin-u-bfa603190748">{activity.content}</p>{/if}<p class="svadmin-u-b6b02c0ebef6 svadmin-u-359090c2d529 svadmin-u-bfa603190748">{activity.timestamp}</p></div></div>{/each}</div></Card.CardContent></Card.Card>{/if}
27
27
  </section>
@@ -0,0 +1,16 @@
1
+ /** Views mount this only when they have an explicit record ID. */
2
+ export declare function useRecordDetail(options: () => {
3
+ resourceName: string;
4
+ id: string | number;
5
+ }): {
6
+ query: import("@tanstack/query-core").QueryObserverResult<import("@svadmin/core").GetOneResult<import("@svadmin/core").BaseRecord & {
7
+ id: string | number;
8
+ }>, unknown> & {
9
+ overtime: import("@svadmin/core").OvertimeResult;
10
+ };
11
+ readonly resource: import("@svadmin/core").ResourceDefinition;
12
+ readonly fields: import("@svadmin/core").FieldDefinition[];
13
+ readonly canRead: boolean;
14
+ readonly checkingPermission: boolean;
15
+ refresh(): Promise<void>;
16
+ };
@@ -0,0 +1,28 @@
1
+ import { captureAdminContext, useOne, useCan, useResourceContract } from '@svadmin/core';
2
+ /** Views mount this only when they have an explicit record ID. */
3
+ export function useRecordDetail(options) {
4
+ const context = captureAdminContext();
5
+ const binding = useResourceContract(() => options().resourceName);
6
+ const resource = $derived(context.getResource(options().resourceName));
7
+ const permission = useCan(() => ({
8
+ resource: options().resourceName, action: 'show', id: options().id,
9
+ queryOptions: { enabled: resource.canShow !== false },
10
+ }));
11
+ const canRead = $derived(resource.canShow !== false && permission.allowed === true);
12
+ const query = useOne(() => ({
13
+ resource: binding.resource, id: options().id, dataProviderName: binding.dataProviderName,
14
+ queryOptions: { enabled: canRead },
15
+ }));
16
+ return {
17
+ query,
18
+ get resource() { return resource; },
19
+ get fields() { return resource.fields.filter(field => field.showInShow !== false); },
20
+ get canRead() { return canRead; },
21
+ get checkingPermission() { return permission.isLoading; },
22
+ async refresh() {
23
+ if (!canRead || query.isFetching)
24
+ return;
25
+ await query.refetch();
26
+ },
27
+ };
28
+ }
@@ -15,31 +15,31 @@ function isPositiveInteger(value, max) {
15
15
  function parseFilter(value, allowedFieldIds, budget, depth = 0) {
16
16
  if (depth > 20 || budget.remaining-- <= 0 || !isRecord(value))
17
17
  return undefined;
18
- if (typeof value.field === 'string') {
19
- if (!allowedFieldIds.has(value.field) || typeof value.operator !== 'string' || !CRUD_OPERATORS.has(value.operator) || !('value' in value)) {
18
+ if (typeof value['field'] === 'string') {
19
+ if (!allowedFieldIds.has(value['field']) || typeof value['operator'] !== 'string' || !CRUD_OPERATORS.has(value['operator']) || !('value' in value)) {
20
20
  return undefined;
21
21
  }
22
22
  return {
23
- field: value.field,
24
- operator: value.operator,
25
- value: value.value,
23
+ field: value['field'],
24
+ operator: value['operator'],
25
+ value: value['value'],
26
26
  };
27
27
  }
28
- if ((value.operator !== 'and' && value.operator !== 'or') || !Array.isArray(value.value))
28
+ if ((value['operator'] !== 'and' && value['operator'] !== 'or') || !Array.isArray(value['value']))
29
29
  return undefined;
30
- const children = value.value.map((entry) => parseFilter(entry, allowedFieldIds, budget, depth + 1));
30
+ const children = value['value'].map((entry) => parseFilter(entry, allowedFieldIds, budget, depth + 1));
31
31
  if (children.some((entry) => entry === undefined))
32
32
  return undefined;
33
- return { operator: value.operator, value: children };
33
+ return { operator: value['operator'], value: children };
34
34
  }
35
35
  function parseSorters(value, allowedFieldIds) {
36
36
  const sorters = [];
37
37
  for (const sorter of value) {
38
- if (!isRecord(sorter) || typeof sorter.field !== 'string' || !allowedFieldIds.has(sorter.field))
38
+ if (!isRecord(sorter) || typeof sorter['field'] !== 'string' || !allowedFieldIds.has(sorter['field']))
39
39
  return undefined;
40
- if (sorter.order !== 'asc' && sorter.order !== 'desc')
40
+ if (sorter['order'] !== 'asc' && sorter['order'] !== 'desc')
41
41
  return undefined;
42
- sorters.push({ field: sorter.field, order: sorter.order });
42
+ sorters.push({ field: sorter['field'], order: sorter['order'] });
43
43
  }
44
44
  return sorters;
45
45
  }
@@ -62,30 +62,30 @@ function parseColumnOrder(value, allowedColumnIds) {
62
62
  function parseState(value, allowedColumnIds) {
63
63
  if (!isRecord(value))
64
64
  return undefined;
65
- if (typeof value.search !== 'string' || value.search.length > 1_000)
65
+ if (typeof value['search'] !== 'string' || value['search'].length > 1_000)
66
66
  return undefined;
67
- if (!Array.isArray(value.filters) || !Array.isArray(value.sorters))
67
+ if (!Array.isArray(value['filters']) || !Array.isArray(value['sorters']))
68
68
  return undefined;
69
- if (!isRecord(value.pagination) || !isPositiveInteger(value.pagination.current, 1_000_000) || !isPositiveInteger(value.pagination.pageSize, 1_000)) {
69
+ if (!isRecord(value['pagination']) || !isPositiveInteger(value['pagination']['current'], 1_000_000) || !isPositiveInteger(value['pagination']['pageSize'], 1_000)) {
70
70
  return undefined;
71
71
  }
72
72
  const allowedFieldIds = new Set([...allowedColumnIds].filter((columnId) => !columnId.startsWith('_')));
73
73
  const filterBudget = { remaining: MAX_FILTER_NODES };
74
- const filters = value.filters.map((entry) => parseFilter(entry, allowedFieldIds, filterBudget));
74
+ const filters = value['filters'].map((entry) => parseFilter(entry, allowedFieldIds, filterBudget));
75
75
  if (filters.some((entry) => entry === undefined))
76
76
  return undefined;
77
- const sorters = parseSorters(value.sorters, allowedFieldIds);
78
- const columnVisibility = parseColumnVisibility(value.columnVisibility, allowedColumnIds);
79
- const columnOrder = parseColumnOrder(value.columnOrder, allowedColumnIds);
77
+ const sorters = parseSorters(value['sorters'], allowedFieldIds);
78
+ const columnVisibility = parseColumnVisibility(value['columnVisibility'], allowedColumnIds);
79
+ const columnOrder = parseColumnOrder(value['columnOrder'], allowedColumnIds);
80
80
  if (!sorters || !columnVisibility || !columnOrder)
81
81
  return undefined;
82
82
  return {
83
- search: value.search,
83
+ search: value['search'],
84
84
  filters: filters,
85
85
  sorters,
86
86
  pagination: {
87
- current: value.pagination.current,
88
- pageSize: value.pagination.pageSize,
87
+ current: value['pagination']['current'],
88
+ pageSize: value['pagination']['pageSize'],
89
89
  },
90
90
  columnVisibility,
91
91
  columnOrder,
@@ -96,22 +96,22 @@ export function readSavedListViews(raw, allowedColumnIds) {
96
96
  return [];
97
97
  try {
98
98
  const parsed = JSON.parse(raw);
99
- if (!isRecord(parsed) || parsed.version !== STORAGE_VERSION || !Array.isArray(parsed.views))
99
+ if (!isRecord(parsed) || parsed['version'] !== STORAGE_VERSION || !Array.isArray(parsed['views']))
100
100
  return [];
101
101
  const views = [];
102
102
  const seenIds = new Set();
103
- for (const entry of parsed.views.slice(0, MAX_SAVED_VIEWS)) {
104
- if (!isRecord(entry) || typeof entry.id !== 'string' || !entry.id.trim() || entry.id.length > 120)
103
+ for (const entry of parsed['views'].slice(0, MAX_SAVED_VIEWS)) {
104
+ if (!isRecord(entry) || typeof entry['id'] !== 'string' || !entry['id'].trim() || entry['id'].length > 120)
105
105
  continue;
106
- if (seenIds.has(entry.id))
106
+ if (seenIds.has(entry['id']))
107
107
  continue;
108
- if (typeof entry.name !== 'string' || !entry.name.trim() || entry.name.length > 60)
108
+ if (typeof entry['name'] !== 'string' || !entry['name'].trim() || entry['name'].length > 60)
109
109
  continue;
110
- const state = parseState(entry.state, allowedColumnIds);
110
+ const state = parseState(entry['state'], allowedColumnIds);
111
111
  if (!state)
112
112
  continue;
113
- seenIds.add(entry.id);
114
- views.push({ id: entry.id, name: entry.name.trim(), state });
113
+ seenIds.add(entry['id']);
114
+ views.push({ id: entry['id'], name: entry['name'].trim(), state });
115
115
  }
116
116
  return views;
117
117
  }
@@ -0,0 +1,13 @@
1
+ import type { BaseRecord } from '@svadmin/core';
2
+ export type TableRecord = BaseRecord & {
3
+ id: string | number;
4
+ };
5
+ /** The list hook validates business fields; this adapter proves table identity and detaches UI reads. */
6
+ export declare function copyTableRecord(value: unknown): TableRecord;
7
+ export declare function tableRowKey(id: string | number): string;
8
+ export declare function checkedTableRows(rows: readonly unknown[]): TableRecord[];
9
+ /** Keep distinct display columns and neutralize spreadsheet expressions at this export boundary. */
10
+ export declare function tableExportRows(rows: readonly TableRecord[], fields: readonly {
11
+ key: string;
12
+ label: string;
13
+ }[]): Record<string, unknown>[];
@@ -0,0 +1,42 @@
1
+ import { Type } from '@sinclair/typebox';
2
+ import { checkExact, decodeBaseRecord, snapshotPlainData, rejectProviderResponse } from '@svadmin/core/schema';
3
+ const recordShape = Type.Intersect([
4
+ Type.Record(Type.String(), Type.Unknown()),
5
+ Type.Object({ id: Type.Union([Type.String(), Type.Number()]) }),
6
+ ]);
7
+ /** The list hook validates business fields; this adapter proves table identity and detaches UI reads. */
8
+ export function copyTableRecord(value) {
9
+ const record = decodeBaseRecord(snapshotPlainData(value));
10
+ if (!checkExact(recordShape, record))
11
+ return rejectProviderResponse();
12
+ return record;
13
+ }
14
+ export function tableRowKey(id) {
15
+ return JSON.stringify([typeof id, id]);
16
+ }
17
+ export function checkedTableRows(rows) {
18
+ const records = rows.map(copyTableRecord);
19
+ const keys = new Set(records.map(record => tableRowKey(record.id)));
20
+ if (keys.size !== records.length)
21
+ return rejectProviderResponse();
22
+ return records;
23
+ }
24
+ /** Keep distinct display columns and neutralize spreadsheet expressions at this export boundary. */
25
+ export function tableExportRows(rows, fields) {
26
+ const protect = (value) => typeof value === 'string' && (/^[\t\r\n]/.test(value) || /^\s*[=+@-]/.test(value))
27
+ ? `'${value}` : value;
28
+ const used = new Set();
29
+ const columns = fields.map(field => {
30
+ const base = String(protect(field.label));
31
+ let label = base;
32
+ let suffix = 1;
33
+ while (used.has(label))
34
+ label = `${base} (${suffix++})`;
35
+ used.add(label);
36
+ return { key: field.key, label };
37
+ });
38
+ return rows.map(row => {
39
+ const record = copyTableRecord(row);
40
+ return Object.fromEntries(columns.map(column => [column.label, protect(record[column.key] ?? null)]));
41
+ });
42
+ }
@@ -20,8 +20,10 @@ const STATUS_ALIASES = {
20
20
  aborted: 'cancelled',
21
21
  };
22
22
  export function normalizeTaskStatus(status) {
23
- const value = String(status ?? '').trim().toLowerCase();
24
- return STATUS_ALIASES[value] ?? 'unknown';
23
+ if (typeof status !== 'string')
24
+ return 'unknown';
25
+ const value = status.trim().toLowerCase();
26
+ return Object.hasOwn(STATUS_ALIASES, value) ? STATUS_ALIASES[value] ?? 'unknown' : 'unknown';
25
27
  }
26
28
  export function isTaskActive(task) {
27
29
  const lifecycle = normalizeTaskStatus(task.status);
@@ -1,7 +1,6 @@
1
1
  export type AlertVariant = "default" | "destructive" | "warning" | "success";
2
2
  export declare const alertVariants: ({ variant, class: className, className: extraClassName, }?: {
3
3
  variant?: AlertVariant | null;
4
- class?: Parameters<typeof clsx>[number];
5
- className?: Parameters<typeof clsx>[number];
4
+ class?: string;
5
+ className?: string;
6
6
  }) => string;
7
- import { clsx } from "cn/engine";
@@ -1,2 +1 @@
1
- export const alertVariants = ({ variant = "default", class: className = "", className: extraClassName = "", } = {}) => clsx("svadmin-alert", variant && `svadmin-alert--${variant}`, className, extraClassName);
2
- import { clsx } from "cn/engine";
1
+ export const alertVariants = ({ variant = "default", class: className = "", className: extraClassName = "", } = {}) => ["svadmin-alert", variant && `svadmin-alert--${variant}`, className, extraClassName].filter(Boolean).join(" ");
@@ -1,15 +1,14 @@
1
1
  <script lang="ts" module>
2
2
  import { cn, type WithElementRef } from "../../../utils.js";
3
3
  import type { HTMLAttributes } from "svelte/elements";
4
- import { alertVariants as variants, type AlertVariant as Variant } from "./alert-variants.js";
4
+ import { alertVariants as resolveAlertVariants, type AlertVariant as AlertVariantValue } from "./alert-variants.js";
5
5
 
6
- export const alertVariants = variants;
7
- export type AlertVariant = Variant;
6
+ export { alertVariants, type AlertVariant } from "./alert-variants.js";
8
7
  </script>
9
8
 
10
9
  <script lang="ts">
11
10
  type Props = WithElementRef<HTMLAttributes<HTMLDivElement>> & {
12
- variant?: AlertVariant;
11
+ variant?: AlertVariantValue;
13
12
  };
14
13
 
15
14
  let {
@@ -26,7 +25,7 @@
26
25
  data-slot="alert"
27
26
  role="alert"
28
27
  data-variant={variant}
29
- class={cn(alertVariants({ variant }), className)}
28
+ class={cn(resolveAlertVariants({ variant }), className)}
30
29
  {...restProps}
31
30
  >
32
31
  {@render children?.()}
@@ -1,14 +1,9 @@
1
1
  import { type WithElementRef } from "../../../utils.js";
2
2
  import type { HTMLAttributes } from "svelte/elements";
3
- import { type AlertVariant as Variant } from "./alert-variants.js";
4
- export declare const alertVariants: ({ variant, class: className, className: extraClassName, }?: {
5
- variant?: Variant | null;
6
- class?: Parameters<(...inputs: import("cn").ClassValue[]) => string>[number];
7
- className?: Parameters<(...inputs: import("cn").ClassValue[]) => string>[number];
8
- }) => string;
9
- export type AlertVariant = Variant;
3
+ import { type AlertVariant as AlertVariantValue } from "./alert-variants.js";
4
+ export { alertVariants, type AlertVariant } from "./alert-variants.js";
10
5
  type Props = WithElementRef<HTMLAttributes<HTMLDivElement>> & {
11
- variant?: AlertVariant;
6
+ variant?: AlertVariantValue;
12
7
  };
13
8
  declare const Alert: import("svelte").Component<Props, {}, "ref">;
14
9
  type Alert = ReturnType<typeof Alert>;
@@ -1,5 +1,3 @@
1
- /* eslint-disable @typescript-eslint/ban-ts-comment */
2
- // @ts-nocheck
3
1
  import Alert, { alertVariants } from "./alert.svelte";
4
2
  import AlertTitle from "./alert-title.svelte";
5
3
  import AlertDescription from "./alert-description.svelte";
@@ -1,7 +1,6 @@
1
1
  export type AvatarSize = "default" | "sm" | "lg" | "xl";
2
2
  export declare const avatarVariants: ({ size, class: className, className: extraClassName, }?: {
3
3
  size?: AvatarSize | null;
4
- class?: Parameters<typeof clsx>[number];
5
- className?: Parameters<typeof clsx>[number];
4
+ class?: string;
5
+ className?: string;
6
6
  }) => string;
7
- import { clsx } from "cn/engine";
@@ -1,2 +1 @@
1
- export const avatarVariants = ({ size = "default", class: className = "", className: extraClassName = "", } = {}) => clsx("svadmin-avatar", size && `svadmin-avatar-size--${size}`, className, extraClassName);
2
- import { clsx } from "cn/engine";
1
+ export const avatarVariants = ({ size = "default", class: className = "", className: extraClassName = "", } = {}) => ["svadmin-avatar", size && `svadmin-avatar-size--${size}`, className, extraClassName].filter(Boolean).join(" ");
@@ -1,8 +1,7 @@
1
1
  <script lang="ts" module>
2
- import { avatarVariants as variants, type AvatarSize as Size } from "./avatar-variants.js";
2
+ import { avatarVariants as resolveAvatarVariants, type AvatarSize as AvatarSizeValue } from "./avatar-variants.js";
3
3
 
4
- export const avatarVariants = variants;
5
- export type AvatarSize = Size;
4
+ export { avatarVariants, type AvatarSize } from "./avatar-variants.js";
6
5
  </script>
7
6
 
8
7
  <script lang="ts">
@@ -10,7 +9,7 @@
10
9
  import type { HTMLImgAttributes } from "svelte/elements";
11
10
 
12
11
  type Props = WithElementRef<HTMLImgAttributes, HTMLImageElement> & {
13
- size?: AvatarSize;
12
+ size?: AvatarSizeValue;
14
13
  fallback?: string;
15
14
  };
16
15
 
@@ -28,7 +27,7 @@
28
27
  </script>
29
28
 
30
29
  {#if src && !imgError}
31
- <span data-size={size} class={cn(avatarVariants({ size }), className)}>
30
+ <span data-size={size} class={cn(resolveAvatarVariants({ size }), className)}>
32
31
  <img
33
32
  bind:this={ref}
34
33
  data-slot="avatar"
@@ -44,7 +43,7 @@
44
43
  data-slot="avatar-fallback"
45
44
  data-size={size}
46
45
  class={cn(
47
- avatarVariants({ size }),
46
+ resolveAvatarVariants({ size }),
48
47
  "svadmin-avatar-fallback",
49
48
  className
50
49
  )}
@@ -1,14 +1,9 @@
1
- import { type AvatarSize as Size } from "./avatar-variants.js";
2
- export declare const avatarVariants: ({ size, class: className, className: extraClassName, }?: {
3
- size?: Size | null;
4
- class?: Parameters<(...inputs: import("cn").ClassValue[]) => string>[number];
5
- className?: Parameters<(...inputs: import("cn").ClassValue[]) => string>[number];
6
- }) => string;
7
- export type AvatarSize = Size;
1
+ import { type AvatarSize as AvatarSizeValue } from "./avatar-variants.js";
2
+ export { avatarVariants, type AvatarSize } from "./avatar-variants.js";
8
3
  import { type WithElementRef } from "../../../utils.js";
9
4
  import type { HTMLImgAttributes } from "svelte/elements";
10
5
  type Props = WithElementRef<HTMLImgAttributes, HTMLImageElement> & {
11
- size?: AvatarSize;
6
+ size?: AvatarSizeValue;
12
7
  fallback?: string;
13
8
  };
14
9
  declare const Avatar: import("svelte").Component<Props, {}, "ref">;
@@ -1,7 +1,6 @@
1
1
  export type BadgeVariant = "default" | "secondary" | "destructive" | "subtle" | "subtle-success" | "subtle-warning" | "subtle-destructive" | "subtle-pill" | "outline" | "ghost" | "link";
2
2
  export declare const badgeVariants: ({ variant, class: className, className: extraClassName, }?: {
3
3
  variant?: BadgeVariant | null;
4
- class?: Parameters<typeof clsx>[number];
5
- className?: Parameters<typeof clsx>[number];
4
+ class?: string;
5
+ className?: string;
6
6
  }) => string;
7
- import { clsx } from "cn/engine";
@@ -1,2 +1 @@
1
- export const badgeVariants = ({ variant = "default", class: className = "", className: extraClassName = "", } = {}) => clsx("svadmin-badge", variant && `svadmin-badge--${variant}`, className, extraClassName);
2
- import { clsx } from "cn/engine";
1
+ export const badgeVariants = ({ variant = "default", class: className = "", className: extraClassName = "", } = {}) => ["svadmin-badge", variant && `svadmin-badge--${variant}`, className, extraClassName].filter(Boolean).join(" ");
@@ -1,8 +1,7 @@
1
1
  <script lang="ts" module>
2
- import { badgeVariants as variants, type BadgeVariant as Variant } from "./badge-variants.js";
2
+ import { badgeVariants as resolveBadgeVariants, type BadgeVariant as BadgeVariantValue } from "./badge-variants.js";
3
3
 
4
- export const badgeVariants = variants;
5
- export type BadgeVariant = Variant;
4
+ export { badgeVariants, type BadgeVariant } from "./badge-variants.js";
6
5
  </script>
7
6
 
8
7
  <script lang="ts">
@@ -17,7 +16,7 @@
17
16
  children,
18
17
  ...restProps
19
18
  }: WithElementRef<HTMLAnchorAttributes> & {
20
- variant?: BadgeVariant;
19
+ variant?: BadgeVariantValue;
21
20
  } = $props();
22
21
  </script>
23
22
 
@@ -27,7 +26,7 @@
27
26
  data-slot="badge"
28
27
  data-variant={variant}
29
28
  {href}
30
- class={cn(badgeVariants({ variant }), className)}
29
+ class={cn(resolveBadgeVariants({ variant }), className)}
31
30
  {...restProps}
32
31
  >
33
32
  {@render children?.()}
@@ -1,14 +1,9 @@
1
- import { type BadgeVariant as Variant } from "./badge-variants.js";
2
- export declare const badgeVariants: ({ variant, class: className, className: extraClassName, }?: {
3
- variant?: Variant | null;
4
- class?: Parameters<(...inputs: import("cn").ClassValue[]) => string>[number];
5
- className?: Parameters<(...inputs: import("cn").ClassValue[]) => string>[number];
6
- }) => string;
7
- export type BadgeVariant = Variant;
1
+ import { type BadgeVariant as BadgeVariantValue } from "./badge-variants.js";
2
+ export { badgeVariants, type BadgeVariant } from "./badge-variants.js";
8
3
  import type { HTMLAnchorAttributes } from "svelte/elements";
9
4
  import { type WithElementRef } from "../../../utils.js";
10
5
  type $$ComponentProps = WithElementRef<HTMLAnchorAttributes> & {
11
- variant?: BadgeVariant;
6
+ variant?: BadgeVariantValue;
12
7
  };
13
8
  declare const Badge: import("svelte").Component<$$ComponentProps, {}, "ref">;
14
9
  type Badge = ReturnType<typeof Badge>;
@@ -1,4 +1,2 @@
1
- /* eslint-disable @typescript-eslint/ban-ts-comment */
2
- // @ts-nocheck
3
1
  export { default as Badge } from "./badge.svelte";
4
2
  export { badgeVariants } from "./badge.svelte";
@@ -3,7 +3,6 @@ export type ButtonSize = "default" | "xs" | "sm" | "lg" | "icon" | "icon-xs" | "
3
3
  export declare const buttonVariants: ({ variant, size, class: className, className: extraClassName, }?: {
4
4
  variant?: ButtonVariant | null;
5
5
  size?: ButtonSize | null;
6
- class?: Parameters<typeof clsx>[number];
7
- className?: Parameters<typeof clsx>[number];
6
+ class?: string;
7
+ className?: string;
8
8
  }) => string;
9
- import { clsx } from "cn/engine";
@@ -1,3 +1,2 @@
1
1
  // Keep the public helper for composition, while styles are owned by app.css.
2
- export const buttonVariants = ({ variant = "default", size = "default", class: className = "", className: extraClassName = "", } = {}) => clsx("svadmin-button", variant && `svadmin-button--${variant}`, size && `svadmin-button-size--${size}`, className, extraClassName);
3
- import { clsx } from "cn/engine";
2
+ export const buttonVariants = ({ variant = "default", size = "default", class: className = "", className: extraClassName = "", } = {}) => ["svadmin-button", variant && `svadmin-button--${variant}`, size && `svadmin-button-size--${size}`, className, extraClassName].filter(Boolean).join(" ");