@svadmin/lite 0.3.27 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +106 -1
- package/dist/compatibility.d.ts +23 -0
- package/dist/compatibility.js +95 -0
- package/dist/components/LiteForm.svelte +1 -1
- package/dist/components/LiteForm.svelte.d.ts +1 -1
- package/dist/components/LiteShowField.svelte +32 -22
- package/dist/components/LiteShowField.svelte.d.ts +3 -2
- package/dist/components/LiteTable.svelte +90 -37
- package/dist/components/LiteTable.svelte.d.ts +3 -2
- package/dist/components/compatibility/LiteCapabilityBoundary.svelte +51 -0
- package/dist/components/compatibility/LiteCapabilityBoundary.svelte.d.ts +13 -0
- package/dist/components/compatibility/LiteClipboardFallback.svelte +16 -0
- package/dist/components/compatibility/LiteClipboardFallback.svelte.d.ts +8 -0
- package/dist/components/compatibility/LiteComputeFallback.svelte +50 -0
- package/dist/components/compatibility/LiteComputeFallback.svelte.d.ts +14 -0
- package/dist/components/compatibility/LiteDirectoryUpload.svelte +44 -0
- package/dist/components/compatibility/LiteDirectoryUpload.svelte.d.ts +11 -0
- package/dist/components/compatibility/LiteOrderedList.svelte +47 -0
- package/dist/components/compatibility/LiteOrderedList.svelte.d.ts +14 -0
- package/dist/components/compatibility/LiteRealtimeStatus.svelte +43 -0
- package/dist/components/compatibility/LiteRealtimeStatus.svelte.d.ts +11 -0
- package/dist/components/compatibility/LiteVisualFallback.svelte +80 -0
- package/dist/components/compatibility/LiteVisualFallback.svelte.d.ts +18 -0
- package/dist/components/compatibility/index.d.ts +7 -0
- package/dist/components/compatibility/index.js +7 -0
- package/dist/components/pages/LiteCreatePage.svelte +15 -6
- package/dist/components/pages/LiteCreatePage.svelte.d.ts +1 -1
- package/dist/components/pages/LiteEditPage.svelte +18 -9
- package/dist/components/pages/LiteEditPage.svelte.d.ts +1 -1
- package/dist/components/pages/LiteListPage.svelte +86 -16
- package/dist/components/pages/LiteListPage.svelte.d.ts +4 -2
- package/dist/components/pages/LiteShowPage.svelte +19 -10
- package/dist/components/pages/LiteShowPage.svelte.d.ts +1 -1
- package/dist/index.d.ts +7 -3
- package/dist/index.js +8 -3
- package/dist/lite.css +269 -0
- package/dist/schema-generator.d.ts +49 -13
- package/dist/schema-generator.js +381 -199
- package/dist/server-adapter.d.ts +25 -1
- package/dist/server-adapter.js +132 -33
- package/dist/value-normalization.d.ts +1 -0
- package/dist/value-normalization.js +18 -0
- package/package.json +10 -10
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import type { ResourceDefinition } from
|
|
3
|
-
import { t } from
|
|
4
|
-
import LiteTable from
|
|
5
|
-
import LitePagination from
|
|
6
|
-
import
|
|
7
|
-
import LiteCreateButton from
|
|
8
|
-
import LiteRefreshButton from
|
|
2
|
+
import type { ResourceDefinition } from "@svadmin/core";
|
|
3
|
+
import { t } from "@svadmin/core/i18n";
|
|
4
|
+
import LiteTable from "../LiteTable.svelte";
|
|
5
|
+
import LitePagination from "../LitePagination.svelte";
|
|
6
|
+
import LiteBreadcrumbs from "../LiteBreadcrumbs.svelte";
|
|
7
|
+
import LiteCreateButton from "../buttons/LiteCreateButton.svelte";
|
|
8
|
+
import LiteRefreshButton from "../buttons/LiteRefreshButton.svelte";
|
|
9
9
|
|
|
10
10
|
interface Props {
|
|
11
11
|
resource: ResourceDefinition;
|
|
@@ -13,13 +13,15 @@
|
|
|
13
13
|
total: number;
|
|
14
14
|
pagination: { page: number; perPage: number };
|
|
15
15
|
currentSort?: string;
|
|
16
|
-
currentOrder?:
|
|
16
|
+
currentOrder?: "asc" | "desc";
|
|
17
17
|
currentSearch?: string;
|
|
18
|
+
currentFilters?: Record<string, string>;
|
|
18
19
|
basePath?: string;
|
|
19
20
|
canCreate?: boolean;
|
|
20
21
|
canShow?: boolean;
|
|
21
22
|
canEdit?: boolean;
|
|
22
23
|
canDelete?: boolean;
|
|
24
|
+
enableBatch?: boolean;
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
let {
|
|
@@ -28,24 +30,43 @@
|
|
|
28
30
|
total,
|
|
29
31
|
pagination,
|
|
30
32
|
currentSort,
|
|
31
|
-
currentOrder =
|
|
33
|
+
currentOrder = "asc",
|
|
32
34
|
currentSearch,
|
|
33
|
-
|
|
35
|
+
currentFilters = {},
|
|
36
|
+
basePath = "/lite",
|
|
34
37
|
canCreate,
|
|
35
38
|
canShow,
|
|
36
39
|
canEdit,
|
|
37
40
|
canDelete,
|
|
41
|
+
enableBatch = true,
|
|
38
42
|
}: Props = $props();
|
|
39
43
|
|
|
40
44
|
const showCreate = $derived(canCreate ?? resource.canCreate !== false);
|
|
41
45
|
const showView = $derived(canShow ?? resource.canShow !== false);
|
|
42
46
|
const showEdit = $derived(canEdit ?? resource.canEdit !== false);
|
|
43
47
|
const showDelete = $derived(canDelete ?? resource.canDelete !== false);
|
|
48
|
+
|
|
49
|
+
const filterableFields = $derived(
|
|
50
|
+
resource.fields
|
|
51
|
+
.filter((f) => f.type === "select" && f.options && f.options.length > 0 && f.filterable !== false)
|
|
52
|
+
.slice(0, 3)
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
const hasActiveFilters = $derived(
|
|
56
|
+
Boolean(currentSearch) || Object.values(currentFilters).some((v) => Boolean(v))
|
|
57
|
+
);
|
|
44
58
|
</script>
|
|
45
59
|
|
|
46
60
|
<div class="lite-page">
|
|
61
|
+
<LiteBreadcrumbs
|
|
62
|
+
items={[
|
|
63
|
+
{ label: t("common.dashboard") || "Dashboard", href: basePath },
|
|
64
|
+
{ label: resource.label || resource.name },
|
|
65
|
+
]}
|
|
66
|
+
/>
|
|
67
|
+
|
|
47
68
|
<div class="lite-page-header">
|
|
48
|
-
<h1 class="lite-page-title">{resource.label || resource.name} {t(
|
|
69
|
+
<h1 class="lite-page-title">{resource.label || resource.name} {t("common.list") || "List"}</h1>
|
|
49
70
|
<div class="lite-page-actions">
|
|
50
71
|
{#if showCreate}
|
|
51
72
|
<LiteCreateButton resource={resource.name} {basePath} />
|
|
@@ -55,10 +76,57 @@
|
|
|
55
76
|
</div>
|
|
56
77
|
|
|
57
78
|
<div class="lite-card" style="margin-bottom: 20px;">
|
|
58
|
-
<div style="padding: 16px; border-bottom: 1px solid #e2e8f0; display: flex; justify-content: space-between; align-items: center;">
|
|
59
|
-
<
|
|
79
|
+
<div style="padding: 16px; border-bottom: 1px solid #e2e8f0; display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap;">
|
|
80
|
+
<form method="GET" class="lite-filter-bar" style="margin: 0;">
|
|
81
|
+
<input
|
|
82
|
+
type="text"
|
|
83
|
+
name="q"
|
|
84
|
+
value={currentSearch ?? ""}
|
|
85
|
+
placeholder={t("common.search") || "Search..."}
|
|
86
|
+
class="lite-input"
|
|
87
|
+
style="width: 200px; display: inline-block;"
|
|
88
|
+
/>
|
|
89
|
+
|
|
90
|
+
{#each filterableFields as field (field.key)}
|
|
91
|
+
<select
|
|
92
|
+
name={field.key}
|
|
93
|
+
class="lite-select"
|
|
94
|
+
style="width: 150px; display: inline-block;"
|
|
95
|
+
>
|
|
96
|
+
<option value="">All {field.label}</option>
|
|
97
|
+
{#if field.options}
|
|
98
|
+
{#each field.options as opt (opt.value)}
|
|
99
|
+
<option
|
|
100
|
+
value={String(opt.value)}
|
|
101
|
+
selected={String(currentFilters[field.key] ?? "") === String(opt.value)}
|
|
102
|
+
>
|
|
103
|
+
{opt.label}
|
|
104
|
+
</option>
|
|
105
|
+
{/each}
|
|
106
|
+
{/if}
|
|
107
|
+
</select>
|
|
108
|
+
{/each}
|
|
109
|
+
|
|
110
|
+
{#if currentSort}
|
|
111
|
+
<input type="hidden" name="sort" value={currentSort} />
|
|
112
|
+
{/if}
|
|
113
|
+
{#if currentOrder}
|
|
114
|
+
<input type="hidden" name="order" value={currentOrder} />
|
|
115
|
+
{/if}
|
|
116
|
+
|
|
117
|
+
<button type="submit" class="lite-btn lite-btn-sm lite-btn-primary">
|
|
118
|
+
{t("common.filter") || "Filter"}
|
|
119
|
+
</button>
|
|
120
|
+
|
|
121
|
+
{#if hasActiveFilters}
|
|
122
|
+
<a href={basePath + "/" + resource.name} class="lite-btn lite-btn-sm">
|
|
123
|
+
{t("common.reset") || "Reset"}
|
|
124
|
+
</a>
|
|
125
|
+
{/if}
|
|
126
|
+
</form>
|
|
127
|
+
|
|
60
128
|
<span style="font-size: 13px; color: #64748b;">
|
|
61
|
-
{t(
|
|
129
|
+
{t("common.total") || "Total"}: <strong>{total}</strong>
|
|
62
130
|
</span>
|
|
63
131
|
</div>
|
|
64
132
|
|
|
@@ -72,8 +140,9 @@
|
|
|
72
140
|
canShow={showView}
|
|
73
141
|
canEdit={showEdit}
|
|
74
142
|
canDelete={showDelete}
|
|
143
|
+
{enableBatch}
|
|
75
144
|
/>
|
|
76
|
-
|
|
145
|
+
|
|
77
146
|
{#if total > pagination.perPage}
|
|
78
147
|
<LitePagination
|
|
79
148
|
page={pagination.page}
|
|
@@ -81,7 +150,8 @@
|
|
|
81
150
|
preserveParams={{
|
|
82
151
|
...(currentSort ? { sort: currentSort } : {}),
|
|
83
152
|
...(currentOrder ? { order: currentOrder } : {}),
|
|
84
|
-
...(currentSearch ? { q: currentSearch } : {})
|
|
153
|
+
...(currentSearch ? { q: currentSearch } : {}),
|
|
154
|
+
...currentFilters,
|
|
85
155
|
}}
|
|
86
156
|
/>
|
|
87
157
|
{/if}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ResourceDefinition } from
|
|
1
|
+
import type { ResourceDefinition } from "@svadmin/core";
|
|
2
2
|
interface Props {
|
|
3
3
|
resource: ResourceDefinition;
|
|
4
4
|
records: Record<string, unknown>[];
|
|
@@ -8,13 +8,15 @@ interface Props {
|
|
|
8
8
|
perPage: number;
|
|
9
9
|
};
|
|
10
10
|
currentSort?: string;
|
|
11
|
-
currentOrder?:
|
|
11
|
+
currentOrder?: "asc" | "desc";
|
|
12
12
|
currentSearch?: string;
|
|
13
|
+
currentFilters?: Record<string, string>;
|
|
13
14
|
basePath?: string;
|
|
14
15
|
canCreate?: boolean;
|
|
15
16
|
canShow?: boolean;
|
|
16
17
|
canEdit?: boolean;
|
|
17
18
|
canDelete?: boolean;
|
|
19
|
+
enableBatch?: boolean;
|
|
18
20
|
}
|
|
19
21
|
declare const LiteListPage: import("svelte").Component<Props, {}, "">;
|
|
20
22
|
type LiteListPage = ReturnType<typeof LiteListPage>;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import type { ResourceDefinition } from
|
|
3
|
-
import { t } from
|
|
4
|
-
import LiteShowField from
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
2
|
+
import type { ResourceDefinition } from "@svadmin/core";
|
|
3
|
+
import { t } from "@svadmin/core/i18n";
|
|
4
|
+
import LiteShowField from "../LiteShowField.svelte";
|
|
5
|
+
import LiteBreadcrumbs from "../LiteBreadcrumbs.svelte";
|
|
6
|
+
import LiteListButton from "../buttons/LiteListButton.svelte";
|
|
7
|
+
import LiteEditButton from "../buttons/LiteEditButton.svelte";
|
|
8
|
+
import LiteDeleteButton from "../buttons/LiteDeleteButton.svelte";
|
|
8
9
|
|
|
9
10
|
interface Props {
|
|
10
11
|
resource: ResourceDefinition;
|
|
@@ -17,12 +18,12 @@
|
|
|
17
18
|
let {
|
|
18
19
|
resource,
|
|
19
20
|
record,
|
|
20
|
-
basePath =
|
|
21
|
+
basePath = "/lite",
|
|
21
22
|
canEdit,
|
|
22
23
|
canDelete,
|
|
23
24
|
}: Props = $props();
|
|
24
25
|
|
|
25
|
-
let pk = $derived(resource.primaryKey ??
|
|
26
|
+
let pk = $derived(resource.primaryKey ?? "id");
|
|
26
27
|
let idStr = $derived(String(record[pk]));
|
|
27
28
|
const showEdit = $derived(canEdit ?? resource.canEdit !== false);
|
|
28
29
|
const showDelete = $derived(canDelete ?? resource.canDelete !== false);
|
|
@@ -33,8 +34,16 @@
|
|
|
33
34
|
</script>
|
|
34
35
|
|
|
35
36
|
<div class="lite-page">
|
|
37
|
+
<LiteBreadcrumbs
|
|
38
|
+
items={[
|
|
39
|
+
{ label: t("common.dashboard") || "Dashboard", href: basePath },
|
|
40
|
+
{ label: resource.label || resource.name, href: `${basePath}/${resource.name}` },
|
|
41
|
+
{ label: `#${idStr}` },
|
|
42
|
+
]}
|
|
43
|
+
/>
|
|
44
|
+
|
|
36
45
|
<div class="lite-page-header">
|
|
37
|
-
<h1 class="lite-page-title">{t(
|
|
46
|
+
<h1 class="lite-page-title">{t("common.show") || "Show"} {resource.label || resource.name} #{idStr}</h1>
|
|
38
47
|
<div class="lite-page-actions">
|
|
39
48
|
{#if showEdit}
|
|
40
49
|
<LiteEditButton resource={resource.name} recordItemId={idStr} {basePath} />
|
|
@@ -54,7 +63,7 @@
|
|
|
54
63
|
{field.label}
|
|
55
64
|
</div>
|
|
56
65
|
<div style="color: #0f172a; font-size: 14px;">
|
|
57
|
-
<LiteShowField {field} value={record[field.key]} />
|
|
66
|
+
<LiteShowField {field} value={record[field.key]} {basePath} />
|
|
58
67
|
</div>
|
|
59
68
|
</div>
|
|
60
69
|
{/each}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
export { createListLoader, createDetailLoader, createCrudActions, createAuthGuard, createAuthActions, createLegacyRedirectHook, isLegacyBrowser, } from './server-adapter';
|
|
2
|
-
export type { ListLoaderResult } from './server-adapter';
|
|
3
|
-
export {
|
|
1
|
+
export { createListLoader, createDetailLoader, createCrudActions, createAuthGuard, createAuthActions, createLegacyRedirectHook, getLegacyRedirectLocation, isLegacyBrowser, } from './server-adapter';
|
|
2
|
+
export type { LegacyRedirectOptions, ListLoaderResult } from './server-adapter';
|
|
3
|
+
export { LITE_COMPATIBILITY_CATALOG, detectLiteCapabilities, resolveLiteCompatibility, } from './compatibility';
|
|
4
|
+
export type { LiteCapability, LiteCapabilitySupport, LiteCompatibilityDescriptor, LiteCompatibilityResolution, LiteFallbackKind, } from './compatibility';
|
|
5
|
+
export { fieldsToTypeBoxSchema, resourceToTypeBoxSchema, fieldsToZodSchema, resourceToZodSchema, fieldToInputType, fieldToPlaceholder, } from './schema-generator';
|
|
6
|
+
export { getStatusBadgeClass, parseExplicitBoolean, isExplicitBooleanTrue } from './value-normalization';
|
|
4
7
|
export { default as LiteLayout } from './components/LiteLayout.svelte';
|
|
5
8
|
export { default as LiteTable } from './components/LiteTable.svelte';
|
|
6
9
|
export { default as LitePagination } from './components/LitePagination.svelte';
|
|
@@ -20,6 +23,7 @@ export { default as LiteTabs } from './components/LiteTabs.svelte';
|
|
|
20
23
|
export { default as LiteShowField } from './components/LiteShowField.svelte';
|
|
21
24
|
export { default as LiteMediaThumbnail } from './components/LiteMediaThumbnail.svelte';
|
|
22
25
|
export { default as LiteChatDialog } from './components/LiteChatDialog.svelte';
|
|
26
|
+
export * from './components/compatibility/index';
|
|
23
27
|
export * from './components/buttons/index';
|
|
24
28
|
export * from './components/fields/index';
|
|
25
29
|
export * from './components/pages/index';
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
// @svadmin/lite — Lightweight SSR Admin UI
|
|
2
2
|
// Server-rendered by default, with optional progressive enhancement.
|
|
3
3
|
// Server utilities (use in +page.server.ts / hooks.server.ts)
|
|
4
|
-
export { createListLoader, createDetailLoader, createCrudActions, createAuthGuard, createAuthActions, createLegacyRedirectHook, isLegacyBrowser, } from './server-adapter';
|
|
5
|
-
//
|
|
6
|
-
export {
|
|
4
|
+
export { createListLoader, createDetailLoader, createCrudActions, createAuthGuard, createAuthActions, createLegacyRedirectHook, getLegacyRedirectLocation, isLegacyBrowser, } from './server-adapter';
|
|
5
|
+
// Optional browser capabilities. The SSR baseline does not import browser globals.
|
|
6
|
+
export { LITE_COMPATIBILITY_CATALOG, detectLiteCapabilities, resolveLiteCompatibility, } from './compatibility';
|
|
7
|
+
// Schema generator (TypeBox schemas used by Lite actions and client forms)
|
|
8
|
+
export { fieldsToTypeBoxSchema, resourceToTypeBoxSchema, fieldsToZodSchema, resourceToZodSchema, fieldToInputType, fieldToPlaceholder, } from './schema-generator';
|
|
7
9
|
// UI Components (use in +page.svelte with csr = false)
|
|
10
|
+
export { getStatusBadgeClass, parseExplicitBoolean, isExplicitBooleanTrue } from './value-normalization';
|
|
8
11
|
export { default as LiteLayout } from './components/LiteLayout.svelte';
|
|
9
12
|
export { default as LiteTable } from './components/LiteTable.svelte';
|
|
10
13
|
export { default as LitePagination } from './components/LitePagination.svelte';
|
|
@@ -24,6 +27,8 @@ export { default as LiteTabs } from './components/LiteTabs.svelte';
|
|
|
24
27
|
export { default as LiteShowField } from './components/LiteShowField.svelte';
|
|
25
28
|
export { default as LiteMediaThumbnail } from './components/LiteMediaThumbnail.svelte';
|
|
26
29
|
export { default as LiteChatDialog } from './components/LiteChatDialog.svelte';
|
|
30
|
+
// Compatibility fallbacks for browser-only and third-party UI capabilities.
|
|
31
|
+
export * from './components/compatibility/index';
|
|
27
32
|
// Action Buttons
|
|
28
33
|
export * from './components/buttons/index';
|
|
29
34
|
// Fields
|
package/dist/lite.css
CHANGED
|
@@ -174,6 +174,21 @@ a:hover {
|
|
|
174
174
|
background: #f8fafc;
|
|
175
175
|
}
|
|
176
176
|
|
|
177
|
+
.lite-page-header {
|
|
178
|
+
display: flex;
|
|
179
|
+
align-items: flex-start;
|
|
180
|
+
justify-content: space-between;
|
|
181
|
+
margin-bottom: 20px;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.lite-page-title {
|
|
185
|
+
margin: 0;
|
|
186
|
+
font-size: 24px;
|
|
187
|
+
line-height: 1.25;
|
|
188
|
+
font-weight: 600;
|
|
189
|
+
color: #0f172a;
|
|
190
|
+
}
|
|
191
|
+
|
|
177
192
|
.lite-header {
|
|
178
193
|
display: flex;
|
|
179
194
|
align-items: center;
|
|
@@ -189,6 +204,40 @@ a:hover {
|
|
|
189
204
|
letter-spacing: -0.025em;
|
|
190
205
|
}
|
|
191
206
|
|
|
207
|
+
|
|
208
|
+
/* ─── Breadcrumbs ──────────────────────────────────────────── */
|
|
209
|
+
.lite-breadcrumbs {
|
|
210
|
+
margin-bottom: 16px;
|
|
211
|
+
}
|
|
212
|
+
.lite-breadcrumbs ol {
|
|
213
|
+
display: flex;
|
|
214
|
+
align-items: center;
|
|
215
|
+
flex-wrap: wrap;
|
|
216
|
+
list-style: none;
|
|
217
|
+
padding: 0;
|
|
218
|
+
margin: 0;
|
|
219
|
+
}
|
|
220
|
+
.lite-breadcrumbs li {
|
|
221
|
+
display: flex;
|
|
222
|
+
align-items: center;
|
|
223
|
+
font-size: 13px;
|
|
224
|
+
color: #64748b;
|
|
225
|
+
}
|
|
226
|
+
.lite-breadcrumbs a {
|
|
227
|
+
color: #64748b;
|
|
228
|
+
}
|
|
229
|
+
.lite-breadcrumbs a:hover {
|
|
230
|
+
color: #0f172a;
|
|
231
|
+
}
|
|
232
|
+
.lite-breadcrumbs .current {
|
|
233
|
+
color: #0f172a;
|
|
234
|
+
font-weight: 500;
|
|
235
|
+
}
|
|
236
|
+
.lite-breadcrumb-sep {
|
|
237
|
+
margin: 0 8px;
|
|
238
|
+
color: #cbd5e1;
|
|
239
|
+
}
|
|
240
|
+
|
|
192
241
|
/* ─── Cards ────────────────────────────────────────────────── */
|
|
193
242
|
.lite-card {
|
|
194
243
|
background: #fff;
|
|
@@ -747,6 +796,174 @@ textarea.lite-input {
|
|
|
747
796
|
justify-content: flex-end;
|
|
748
797
|
}
|
|
749
798
|
|
|
799
|
+
/* ─── Compatibility Fallbacks ─────────────────────────────── */
|
|
800
|
+
.lite-compatibility {
|
|
801
|
+
border: 1px solid #e2e8f0;
|
|
802
|
+
border-radius: 8px;
|
|
803
|
+
background: #fff;
|
|
804
|
+
margin-bottom: 16px;
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
.lite-compatibility-header,
|
|
808
|
+
.lite-section-header,
|
|
809
|
+
.lite-realtime-status {
|
|
810
|
+
display: flex;
|
|
811
|
+
align-items: flex-start;
|
|
812
|
+
justify-content: space-between;
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
.lite-compatibility-header {
|
|
816
|
+
padding: 14px 16px;
|
|
817
|
+
border-bottom: 1px solid #e2e8f0;
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
.lite-compatibility-header h2,
|
|
821
|
+
.lite-section-header h2,
|
|
822
|
+
.lite-ordered-list h2 {
|
|
823
|
+
margin: 0;
|
|
824
|
+
font-size: 15px;
|
|
825
|
+
font-weight: 600;
|
|
826
|
+
color: #0f172a;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
.lite-compatibility-header p,
|
|
830
|
+
.lite-section-header p {
|
|
831
|
+
margin: 4px 0 0;
|
|
832
|
+
color: #64748b;
|
|
833
|
+
font-size: 12px;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
.lite-compatibility-body {
|
|
837
|
+
padding: 16px;
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
.lite-visual-fallback {
|
|
841
|
+
padding: 0;
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
.lite-visual-fallback .lite-section-header {
|
|
845
|
+
padding: 14px 16px;
|
|
846
|
+
border-bottom: 1px solid #e2e8f0;
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
.lite-visual-snapshot {
|
|
850
|
+
padding: 16px;
|
|
851
|
+
border-bottom: 1px solid #e2e8f0;
|
|
852
|
+
text-align: center;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
.lite-visual-snapshot img {
|
|
856
|
+
display: inline-block;
|
|
857
|
+
max-width: 100%;
|
|
858
|
+
height: auto;
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
.lite-table-scroll {
|
|
862
|
+
overflow-x: auto;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
.lite-muted {
|
|
866
|
+
color: #64748b;
|
|
867
|
+
font-size: 12px;
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
.lite-realtime-status {
|
|
871
|
+
align-items: center;
|
|
872
|
+
padding: 10px 12px;
|
|
873
|
+
border: 1px solid #e2e8f0;
|
|
874
|
+
border-radius: 6px;
|
|
875
|
+
background: #fff;
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
.lite-realtime-status > div > * + * {
|
|
879
|
+
margin-left: 8px;
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
.lite-directory-upload {
|
|
883
|
+
border: 1px solid #e2e8f0;
|
|
884
|
+
border-radius: 8px;
|
|
885
|
+
padding: 16px;
|
|
886
|
+
background: #fff;
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
.lite-directory-upload legend {
|
|
890
|
+
padding: 0 6px;
|
|
891
|
+
font-size: 14px;
|
|
892
|
+
font-weight: 600;
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
.lite-ordered-list {
|
|
896
|
+
padding: 16px;
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
.lite-ordered-list ol {
|
|
900
|
+
margin: 12px 0 0;
|
|
901
|
+
padding-left: 24px;
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
.lite-ordered-list li {
|
|
905
|
+
padding: 10px 0 10px 4px;
|
|
906
|
+
border-bottom: 1px solid #f1f5f9;
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
.lite-ordered-list li > div:first-child {
|
|
910
|
+
margin-bottom: 8px;
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
.lite-ordered-list li span {
|
|
914
|
+
display: block;
|
|
915
|
+
margin-top: 2px;
|
|
916
|
+
color: #64748b;
|
|
917
|
+
font-size: 12px;
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
.lite-compute-fallback {
|
|
921
|
+
padding: 16px;
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
.lite-clipboard-fallback {
|
|
925
|
+
margin-bottom: 0;
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
.lite-dashboard-stats,
|
|
929
|
+
.lite-dashboard-split,
|
|
930
|
+
.lite-resource-links {
|
|
931
|
+
display: flex;
|
|
932
|
+
flex-wrap: wrap;
|
|
933
|
+
margin-left: -8px;
|
|
934
|
+
margin-right: -8px;
|
|
935
|
+
margin-bottom: 24px;
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
.lite-dashboard-stats > *,
|
|
939
|
+
.lite-dashboard-split > *,
|
|
940
|
+
.lite-resource-links > * {
|
|
941
|
+
margin-left: 8px;
|
|
942
|
+
margin-right: 8px;
|
|
943
|
+
margin-bottom: 16px;
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
.lite-dashboard-stats > * {
|
|
947
|
+
flex: 1 1 220px;
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
.lite-dashboard-split > *:first-child {
|
|
951
|
+
flex: 2 1 480px;
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
.lite-dashboard-split > *:last-child {
|
|
955
|
+
flex: 1 1 240px;
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
.lite-resource-links {
|
|
959
|
+
padding: 16px 8px 0;
|
|
960
|
+
margin-bottom: 0;
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
.lite-resource-links > * {
|
|
964
|
+
flex: 1 1 200px;
|
|
965
|
+
}
|
|
966
|
+
|
|
750
967
|
/* ─── Login Page ───────────────────────────────────────────── */
|
|
751
968
|
.lite-login-wrapper {
|
|
752
969
|
display: flex;
|
|
@@ -791,6 +1008,36 @@ textarea.lite-input {
|
|
|
791
1008
|
border-color: #fecaca;
|
|
792
1009
|
}
|
|
793
1010
|
|
|
1011
|
+
.lite-badge-info {
|
|
1012
|
+
background: #eff6ff;
|
|
1013
|
+
color: #1d4ed8;
|
|
1014
|
+
border-color: #bfdbfe;
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
.lite-badge-indigo {
|
|
1018
|
+
background: #eef2ff;
|
|
1019
|
+
color: #4338ca;
|
|
1020
|
+
border-color: #c7d2fe;
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
.lite-filter-bar {
|
|
1024
|
+
display: flex;
|
|
1025
|
+
align-items: center;
|
|
1026
|
+
flex-wrap: wrap;
|
|
1027
|
+
}
|
|
1028
|
+
.lite-filter-bar > * + * {
|
|
1029
|
+
margin-left: 8px;
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
.lite-batch-bar {
|
|
1033
|
+
display: flex;
|
|
1034
|
+
align-items: center;
|
|
1035
|
+
justify-content: space-between;
|
|
1036
|
+
padding: 10px 16px;
|
|
1037
|
+
background: #f8fafc;
|
|
1038
|
+
border-top: 1px solid #e2e8f0;
|
|
1039
|
+
}
|
|
1040
|
+
|
|
794
1041
|
.lite-badge-warning {
|
|
795
1042
|
background: #fffbeb;
|
|
796
1043
|
color: #92400e;
|
|
@@ -830,6 +1077,11 @@ textarea.lite-input {
|
|
|
830
1077
|
.lite-mobile-nav {
|
|
831
1078
|
display: block;
|
|
832
1079
|
}
|
|
1080
|
+
.lite-mobile-nav-links {
|
|
1081
|
+
max-height: 240px;
|
|
1082
|
+
overflow-y: auto;
|
|
1083
|
+
border-top: 1px solid #f1f5f9;
|
|
1084
|
+
}
|
|
833
1085
|
.lite-main {
|
|
834
1086
|
padding: 16px;
|
|
835
1087
|
}
|
|
@@ -843,6 +1095,23 @@ textarea.lite-input {
|
|
|
843
1095
|
.lite-header h1 {
|
|
844
1096
|
margin-bottom: 12px;
|
|
845
1097
|
}
|
|
1098
|
+
.lite-page-header {
|
|
1099
|
+
display: block;
|
|
1100
|
+
}
|
|
1101
|
+
.lite-page-header > .lite-btn {
|
|
1102
|
+
margin-top: 12px;
|
|
1103
|
+
}
|
|
1104
|
+
.lite-compatibility-header,
|
|
1105
|
+
.lite-section-header,
|
|
1106
|
+
.lite-realtime-status {
|
|
1107
|
+
display: block;
|
|
1108
|
+
}
|
|
1109
|
+
.lite-compatibility-header .lite-badge,
|
|
1110
|
+
.lite-section-header > .lite-badge,
|
|
1111
|
+
.lite-section-header > .lite-btn,
|
|
1112
|
+
.lite-realtime-status > .lite-btn {
|
|
1113
|
+
margin-top: 10px;
|
|
1114
|
+
}
|
|
846
1115
|
}
|
|
847
1116
|
|
|
848
1117
|
/* ─── Print ────────────────────────────────────────────────── */
|
|
@@ -1,26 +1,62 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @svadmin/lite — Schema Generator
|
|
3
3
|
*
|
|
4
|
-
* Auto-generates
|
|
5
|
-
* Used by
|
|
4
|
+
* Auto-generates TypeBox schemas from @svadmin/core FieldDefinitions.
|
|
5
|
+
* Used by Lite server actions and high-speed JIT form validation.
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
8
|
-
import type { FieldDefinition, ResourceDefinition } from
|
|
7
|
+
import { type TObject } from "@sinclair/typebox";
|
|
8
|
+
import type { FieldDefinition, ResourceDefinition } from "@svadmin/core";
|
|
9
|
+
export interface SchemaValidationIssue {
|
|
10
|
+
path: (string | number)[];
|
|
11
|
+
message: string;
|
|
12
|
+
}
|
|
13
|
+
export type SchemaValidationResult<T = Record<string, unknown>> = {
|
|
14
|
+
success: true;
|
|
15
|
+
data: T;
|
|
16
|
+
error?: never;
|
|
17
|
+
} | {
|
|
18
|
+
success: false;
|
|
19
|
+
error: {
|
|
20
|
+
issues: SchemaValidationIssue[];
|
|
21
|
+
};
|
|
22
|
+
data?: never;
|
|
23
|
+
};
|
|
24
|
+
export type TypeBoxEnhancedSchema<T = Record<string, unknown>> = TObject & {
|
|
25
|
+
parse: (values: unknown) => T;
|
|
26
|
+
safeParse: (values: unknown) => SchemaValidationResult<T>;
|
|
27
|
+
Check: (values: unknown) => boolean;
|
|
28
|
+
"~standard": {
|
|
29
|
+
version: 1;
|
|
30
|
+
vendor: "svadmin";
|
|
31
|
+
validate: (values: unknown) => {
|
|
32
|
+
value: T;
|
|
33
|
+
} | {
|
|
34
|
+
issues: Array<{
|
|
35
|
+
message: string;
|
|
36
|
+
path?: (string | number)[];
|
|
37
|
+
}>;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
};
|
|
9
41
|
/**
|
|
10
|
-
* Generate a
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
42
|
+
* Generate a TypeBox object schema from a list of FieldDefinitions.
|
|
43
|
+
*/
|
|
44
|
+
export declare function fieldsToTypeBoxSchema(fields: FieldDefinition[], mode?: "create" | "edit"): TypeBoxEnhancedSchema;
|
|
45
|
+
/**
|
|
46
|
+
* Generate a TypeBox schema from a ResourceDefinition.
|
|
47
|
+
* Convenience wrapper around fieldsToTypeBoxSchema.
|
|
48
|
+
*/
|
|
49
|
+
export declare function resourceToTypeBoxSchema(resource: ResourceDefinition, mode?: "create" | "edit"): TypeBoxEnhancedSchema;
|
|
50
|
+
/**
|
|
51
|
+
* Backward compatibility alias for fieldsToTypeBoxSchema
|
|
14
52
|
*/
|
|
15
|
-
export declare
|
|
53
|
+
export declare const fieldsToZodSchema: typeof fieldsToTypeBoxSchema;
|
|
16
54
|
/**
|
|
17
|
-
*
|
|
18
|
-
* Convenience wrapper around fieldsToZodSchema.
|
|
55
|
+
* Backward compatibility alias for resourceToTypeBoxSchema
|
|
19
56
|
*/
|
|
20
|
-
export declare
|
|
57
|
+
export declare const resourceToZodSchema: typeof resourceToTypeBoxSchema;
|
|
21
58
|
/**
|
|
22
59
|
* Determine a conservative HTML input type for server-rendered forms.
|
|
23
|
-
* Text fallbacks avoid inconsistent native validation and date widgets.
|
|
24
60
|
*/
|
|
25
61
|
export declare function fieldToInputType(field: FieldDefinition): string;
|
|
26
62
|
/**
|