@svadmin/ui 0.56.0 → 0.58.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/dist/app.css +28 -0
- package/dist/app.theme.css +28 -0
- package/dist/components/AICommandBar.svelte +33 -10
- package/dist/components/AdminApp.svelte +16 -5
- package/dist/components/AutoForm.svelte +4 -4
- package/dist/components/AutoTable.svelte +866 -204
- package/dist/components/AutoTable.svelte.d.ts +1 -1
- package/dist/components/CommandPalette.svelte +39 -17
- package/dist/components/ConfirmDialog.svelte +4 -3
- package/dist/components/ConfirmDialog.svelte.d.ts +1 -0
- package/dist/components/CopilotPanel.svelte +3 -3
- package/dist/components/DraggableHeader.svelte +15 -36
- package/dist/components/DraggableHeader.svelte.d.ts +13 -4
- package/dist/components/EditPage.svelte +4 -4
- package/dist/components/EmptyState.svelte +1 -1
- package/dist/components/ErrorComponent.svelte +1 -1
- package/dist/components/FieldRenderer.svelte +2 -1
- package/dist/components/ListPage.svelte +4 -12
- package/dist/components/ListPage.svelte.d.ts +1 -1
- package/dist/components/RecordDetailDrawer.svelte +121 -0
- package/dist/components/RecordDetailDrawer.svelte.d.ts +9 -0
- package/dist/components/ResourceAccessGuard.svelte +68 -0
- package/dist/components/ResourceAccessGuard.svelte.d.ts +12 -0
- package/dist/components/ResourceOperationsPage.svelte +8 -3
- package/dist/components/ShowPage.svelte +5 -6
- package/dist/components/TaskQueueDrawer.svelte +1 -1
- package/dist/components/TenantSwitcher.svelte +1 -0
- package/dist/components/Toast.svelte +35 -1
- package/dist/components/UndoableNotification.svelte +12 -6
- package/dist/components/UndoableNotification.svelte.d.ts +2 -0
- package/dist/components/account/MembersStarterPage.svelte +1 -1
- package/dist/components/buttons/CloneButton.svelte +9 -2
- package/dist/components/buttons/CreateButton.svelte +9 -2
- package/dist/components/buttons/DeleteButton.svelte +9 -2
- package/dist/components/buttons/EditButton.svelte +9 -2
- package/dist/components/buttons/ListButton.svelte +1 -1
- package/dist/components/buttons/ShowButton.svelte +9 -2
- package/dist/components/content/DataState.svelte +1 -1
- package/dist/components/content/FilterToolbar.svelte +6 -2
- package/dist/components/content/MediaThumbnail.svelte +109 -0
- package/dist/components/content/MediaThumbnail.svelte.d.ts +22 -0
- package/dist/components/content/ProjectCard.svelte +2 -1
- package/dist/components/content/SystemErrorState.svelte +1 -1
- package/dist/components/content/index.d.ts +2 -0
- package/dist/components/content/index.js +1 -0
- package/dist/components/fields/ImageField.svelte +19 -4
- package/dist/components/fields/ImageField.svelte.d.ts +4 -0
- package/dist/components/saved-list-views.d.ts +35 -0
- package/dist/components/saved-list-views.js +168 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +3 -0
- package/package.json +2 -2
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Filter, Sort } from '@svadmin/core';
|
|
2
|
+
export interface SavedListViewState {
|
|
3
|
+
search: string;
|
|
4
|
+
filters: Filter[];
|
|
5
|
+
sorters: Sort[];
|
|
6
|
+
pagination: {
|
|
7
|
+
current: number;
|
|
8
|
+
pageSize: number;
|
|
9
|
+
};
|
|
10
|
+
columnVisibility: Record<string, boolean>;
|
|
11
|
+
columnOrder: string[];
|
|
12
|
+
}
|
|
13
|
+
export interface SavedListView {
|
|
14
|
+
id: string;
|
|
15
|
+
name: string;
|
|
16
|
+
state: SavedListViewState;
|
|
17
|
+
}
|
|
18
|
+
export interface ListPreferenceScope {
|
|
19
|
+
resourceName: string;
|
|
20
|
+
providerName: string;
|
|
21
|
+
tenantIdentity?: string | number;
|
|
22
|
+
}
|
|
23
|
+
export declare function readSavedListViews(raw: string | null, allowedColumnIds: Set<string>): SavedListView[];
|
|
24
|
+
export declare function serializeSavedListViews(views: SavedListView[]): string;
|
|
25
|
+
export declare function cloneSavedListViewState(state: SavedListViewState): SavedListViewState;
|
|
26
|
+
export declare function listPreferenceScopeId(scope: ListPreferenceScope): string;
|
|
27
|
+
export declare function canMigrateLegacyListPreferences(scope: ListPreferenceScope): boolean;
|
|
28
|
+
export declare function savedListViewsStorageKey(scope: ListPreferenceScope): string;
|
|
29
|
+
export declare function activeSavedListViewStorageKey(scope: ListPreferenceScope): string;
|
|
30
|
+
export declare function columnVisibilityStorageKey(scope: ListPreferenceScope): string;
|
|
31
|
+
export declare function columnOrderStorageKey(scope: ListPreferenceScope): string;
|
|
32
|
+
export declare function legacySavedListViewsStorageKey(resourceName: string): string;
|
|
33
|
+
export declare function legacyActiveSavedListViewStorageKey(resourceName: string): string;
|
|
34
|
+
export declare function legacyColumnVisibilityStorageKey(resourceName: string): string;
|
|
35
|
+
export declare function legacyColumnOrderStorageKey(resourceName: string): string;
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
const STORAGE_VERSION = 1;
|
|
2
|
+
const MAX_SAVED_VIEWS = 25;
|
|
3
|
+
const MAX_FILTER_NODES = 200;
|
|
4
|
+
const CRUD_OPERATORS = new Set([
|
|
5
|
+
'eq', 'ne', 'lt', 'gt', 'lte', 'gte',
|
|
6
|
+
'contains', 'ncontains', 'startswith', 'endswith',
|
|
7
|
+
'in', 'nin', 'null', 'nnull', 'between', 'nbetween',
|
|
8
|
+
]);
|
|
9
|
+
function isRecord(value) {
|
|
10
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
11
|
+
}
|
|
12
|
+
function isPositiveInteger(value, max) {
|
|
13
|
+
return typeof value === 'number' && Number.isInteger(value) && value > 0 && value <= max;
|
|
14
|
+
}
|
|
15
|
+
function parseFilter(value, allowedFieldIds, budget, depth = 0) {
|
|
16
|
+
if (depth > 20 || budget.remaining-- <= 0 || !isRecord(value))
|
|
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)) {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
return {
|
|
23
|
+
field: value.field,
|
|
24
|
+
operator: value.operator,
|
|
25
|
+
value: value.value,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
if ((value.operator !== 'and' && value.operator !== 'or') || !Array.isArray(value.value))
|
|
29
|
+
return undefined;
|
|
30
|
+
const children = value.value.map((entry) => parseFilter(entry, allowedFieldIds, budget, depth + 1));
|
|
31
|
+
if (children.some((entry) => entry === undefined))
|
|
32
|
+
return undefined;
|
|
33
|
+
return { operator: value.operator, value: children };
|
|
34
|
+
}
|
|
35
|
+
function parseSorters(value, allowedFieldIds) {
|
|
36
|
+
const sorters = [];
|
|
37
|
+
for (const sorter of value) {
|
|
38
|
+
if (!isRecord(sorter) || typeof sorter.field !== 'string' || !allowedFieldIds.has(sorter.field))
|
|
39
|
+
return undefined;
|
|
40
|
+
if (sorter.order !== 'asc' && sorter.order !== 'desc')
|
|
41
|
+
return undefined;
|
|
42
|
+
sorters.push({ field: sorter.field, order: sorter.order });
|
|
43
|
+
}
|
|
44
|
+
return sorters;
|
|
45
|
+
}
|
|
46
|
+
function parseColumnVisibility(value, allowedColumnIds) {
|
|
47
|
+
if (!isRecord(value))
|
|
48
|
+
return undefined;
|
|
49
|
+
const visibility = {};
|
|
50
|
+
for (const [columnId, visible] of Object.entries(value)) {
|
|
51
|
+
if (!allowedColumnIds.has(columnId) || typeof visible !== 'boolean')
|
|
52
|
+
continue;
|
|
53
|
+
visibility[columnId] = visible;
|
|
54
|
+
}
|
|
55
|
+
return visibility;
|
|
56
|
+
}
|
|
57
|
+
function parseColumnOrder(value, allowedColumnIds) {
|
|
58
|
+
if (!Array.isArray(value))
|
|
59
|
+
return undefined;
|
|
60
|
+
return [...new Set(value.filter((columnId) => (typeof columnId === 'string' && allowedColumnIds.has(columnId))))];
|
|
61
|
+
}
|
|
62
|
+
function parseState(value, allowedColumnIds) {
|
|
63
|
+
if (!isRecord(value))
|
|
64
|
+
return undefined;
|
|
65
|
+
if (typeof value.search !== 'string' || value.search.length > 1_000)
|
|
66
|
+
return undefined;
|
|
67
|
+
if (!Array.isArray(value.filters) || !Array.isArray(value.sorters))
|
|
68
|
+
return undefined;
|
|
69
|
+
if (!isRecord(value.pagination) || !isPositiveInteger(value.pagination.current, 1_000_000) || !isPositiveInteger(value.pagination.pageSize, 1_000)) {
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
const allowedFieldIds = new Set([...allowedColumnIds].filter((columnId) => !columnId.startsWith('_')));
|
|
73
|
+
const filterBudget = { remaining: MAX_FILTER_NODES };
|
|
74
|
+
const filters = value.filters.map((entry) => parseFilter(entry, allowedFieldIds, filterBudget));
|
|
75
|
+
if (filters.some((entry) => entry === undefined))
|
|
76
|
+
return undefined;
|
|
77
|
+
const sorters = parseSorters(value.sorters, allowedFieldIds);
|
|
78
|
+
const columnVisibility = parseColumnVisibility(value.columnVisibility, allowedColumnIds);
|
|
79
|
+
const columnOrder = parseColumnOrder(value.columnOrder, allowedColumnIds);
|
|
80
|
+
if (!sorters || !columnVisibility || !columnOrder)
|
|
81
|
+
return undefined;
|
|
82
|
+
return {
|
|
83
|
+
search: value.search,
|
|
84
|
+
filters: filters,
|
|
85
|
+
sorters,
|
|
86
|
+
pagination: {
|
|
87
|
+
current: value.pagination.current,
|
|
88
|
+
pageSize: value.pagination.pageSize,
|
|
89
|
+
},
|
|
90
|
+
columnVisibility,
|
|
91
|
+
columnOrder,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
export function readSavedListViews(raw, allowedColumnIds) {
|
|
95
|
+
if (!raw)
|
|
96
|
+
return [];
|
|
97
|
+
try {
|
|
98
|
+
const parsed = JSON.parse(raw);
|
|
99
|
+
if (!isRecord(parsed) || parsed.version !== STORAGE_VERSION || !Array.isArray(parsed.views))
|
|
100
|
+
return [];
|
|
101
|
+
const views = [];
|
|
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)
|
|
105
|
+
continue;
|
|
106
|
+
if (seenIds.has(entry.id))
|
|
107
|
+
continue;
|
|
108
|
+
if (typeof entry.name !== 'string' || !entry.name.trim() || entry.name.length > 60)
|
|
109
|
+
continue;
|
|
110
|
+
const state = parseState(entry.state, allowedColumnIds);
|
|
111
|
+
if (!state)
|
|
112
|
+
continue;
|
|
113
|
+
seenIds.add(entry.id);
|
|
114
|
+
views.push({ id: entry.id, name: entry.name.trim(), state });
|
|
115
|
+
}
|
|
116
|
+
return views;
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
if (error instanceof SyntaxError)
|
|
120
|
+
return [];
|
|
121
|
+
throw error;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
export function serializeSavedListViews(views) {
|
|
125
|
+
return JSON.stringify({ version: STORAGE_VERSION, views: views.slice(0, MAX_SAVED_VIEWS) });
|
|
126
|
+
}
|
|
127
|
+
export function cloneSavedListViewState(state) {
|
|
128
|
+
return JSON.parse(JSON.stringify(state));
|
|
129
|
+
}
|
|
130
|
+
function encodeScopeValue(value) {
|
|
131
|
+
if (value === undefined)
|
|
132
|
+
return 'u';
|
|
133
|
+
return `${typeof value === 'number' ? 'n' : 's'}:${encodeURIComponent(String(value))}`;
|
|
134
|
+
}
|
|
135
|
+
export function listPreferenceScopeId(scope) {
|
|
136
|
+
return [
|
|
137
|
+
`r:${encodeURIComponent(scope.resourceName)}`,
|
|
138
|
+
`p:${encodeURIComponent(scope.providerName)}`,
|
|
139
|
+
`t:${encodeScopeValue(scope.tenantIdentity)}`,
|
|
140
|
+
].join('|');
|
|
141
|
+
}
|
|
142
|
+
export function canMigrateLegacyListPreferences(scope) {
|
|
143
|
+
return scope.providerName === 'default' && scope.tenantIdentity === undefined;
|
|
144
|
+
}
|
|
145
|
+
export function savedListViewsStorageKey(scope) {
|
|
146
|
+
return `svadmin-list-views-v2-${listPreferenceScopeId(scope)}`;
|
|
147
|
+
}
|
|
148
|
+
export function activeSavedListViewStorageKey(scope) {
|
|
149
|
+
return `svadmin-list-view-active-v2-${listPreferenceScopeId(scope)}`;
|
|
150
|
+
}
|
|
151
|
+
export function columnVisibilityStorageKey(scope) {
|
|
152
|
+
return `svadmin-columns-v2-${listPreferenceScopeId(scope)}`;
|
|
153
|
+
}
|
|
154
|
+
export function columnOrderStorageKey(scope) {
|
|
155
|
+
return `svadmin-colorder-v2-${listPreferenceScopeId(scope)}`;
|
|
156
|
+
}
|
|
157
|
+
export function legacySavedListViewsStorageKey(resourceName) {
|
|
158
|
+
return `svadmin-list-views-${resourceName}`;
|
|
159
|
+
}
|
|
160
|
+
export function legacyActiveSavedListViewStorageKey(resourceName) {
|
|
161
|
+
return `svadmin-list-view-active-${resourceName}`;
|
|
162
|
+
}
|
|
163
|
+
export function legacyColumnVisibilityStorageKey(resourceName) {
|
|
164
|
+
return `svadmin-columns-${resourceName}`;
|
|
165
|
+
}
|
|
166
|
+
export function legacyColumnOrderStorageKey(resourceName) {
|
|
167
|
+
return `svadmin-colorder-${resourceName}`;
|
|
168
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -15,6 +15,8 @@ export { default as EmptyState } from './components/EmptyState.svelte';
|
|
|
15
15
|
export { default as StatsCard } from './components/StatsCard.svelte';
|
|
16
16
|
export { default as PageHeader } from './components/PageHeader.svelte';
|
|
17
17
|
export { default as ResourceOperationsPage } from './components/ResourceOperationsPage.svelte';
|
|
18
|
+
export { default as RecordDetailDrawer } from './components/RecordDetailDrawer.svelte';
|
|
19
|
+
export { default as ResourceAccessGuard } from './components/ResourceAccessGuard.svelte';
|
|
18
20
|
export { default as LoginPage } from './components/LoginPage.svelte';
|
|
19
21
|
export { default as AuthPageShell } from './components/AuthPageShell.svelte';
|
|
20
22
|
export { default as RegisterPage } from './components/RegisterPage.svelte';
|
|
@@ -227,3 +229,5 @@ export type { DateRangeFormat, DateRangeValue } from './components/fields/DateRa
|
|
|
227
229
|
export type { PercentTone } from './components/fields/PercentField.svelte';
|
|
228
230
|
export type { RatingSize } from './components/fields/RatingField.svelte';
|
|
229
231
|
export type { CurrencyTone } from './components/fields/CurrencyField.svelte';
|
|
232
|
+
export { default as MediaThumbnail } from './components/content/MediaThumbnail.svelte';
|
|
233
|
+
export type { MediaThumbnailSize, MediaThumbnailFit } from './components/content/MediaThumbnail.svelte';
|
package/dist/index.js
CHANGED
|
@@ -18,6 +18,8 @@ export { default as EmptyState } from './components/EmptyState.svelte';
|
|
|
18
18
|
export { default as StatsCard } from './components/StatsCard.svelte';
|
|
19
19
|
export { default as PageHeader } from './components/PageHeader.svelte';
|
|
20
20
|
export { default as ResourceOperationsPage } from './components/ResourceOperationsPage.svelte';
|
|
21
|
+
export { default as RecordDetailDrawer } from './components/RecordDetailDrawer.svelte';
|
|
22
|
+
export { default as ResourceAccessGuard } from './components/ResourceAccessGuard.svelte';
|
|
21
23
|
export { default as LoginPage } from './components/LoginPage.svelte';
|
|
22
24
|
export { default as AuthPageShell } from './components/AuthPageShell.svelte';
|
|
23
25
|
export { default as RegisterPage } from './components/RegisterPage.svelte';
|
|
@@ -214,3 +216,4 @@ export { default as DetailDrawer } from './components/DetailDrawer.svelte';
|
|
|
214
216
|
export { default as AuditTimeline } from './components/content/AuditTimeline.svelte';
|
|
215
217
|
export { default as FilterDropdown } from './components/FilterDropdown.svelte';
|
|
216
218
|
export { default as BatchActionBar } from './components/BatchActionBar.svelte';
|
|
219
|
+
export { default as MediaThumbnail } from './components/content/MediaThumbnail.svelte';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@svadmin/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.58.0",
|
|
4
4
|
"description": "Pre-built admin UI components — AdminApp, AutoTable, AutoForm, Sidebar, Layout",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": [
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"./components/*": "./dist/components/*"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
|
-
"@svadmin/core": "^0.
|
|
42
|
+
"@svadmin/core": "^0.42.0",
|
|
43
43
|
"@tanstack/svelte-query": "^6.1.38",
|
|
44
44
|
"highlight.js": "^11.11.1",
|
|
45
45
|
"isomorphic-dompurify": "^3.22.0",
|