@svadmin/lite 0.2.4 → 0.2.5
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/package.json +4 -3
- package/src/components/LiteArrayField.svelte +6 -5
- package/src/components/LiteConfirmDialog.svelte +1 -1
- package/src/components/buttons/LiteDeleteButton.svelte +1 -1
- package/src/components/buttons/LiteImportButton.svelte +1 -1
- package/src/components/fields/LiteBooleanField.svelte +1 -1
- package/src/components/fields/LiteEmailField.svelte +1 -1
- package/src/components/fields/LiteFileField.svelte +2 -2
- package/src/components/fields/LiteImageField.svelte +2 -2
- package/src/components/fields/LiteJsonField.svelte +1 -1
- package/src/components/fields/LiteNumberField.svelte +1 -1
- package/src/components/fields/LiteRelationField.svelte +1 -1
- package/src/components/fields/LiteTextField.svelte +1 -1
- package/src/components/fields/LiteUrlField.svelte +2 -2
- package/src/components/pages/LiteListPage.svelte +2 -3
- package/src/schema-generator.ts +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@svadmin/lite",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.5",
|
|
4
4
|
"description": "SSR-compatible lightweight admin UI for @svadmin — zero client-side JS, works in IE11",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -25,8 +25,9 @@
|
|
|
25
25
|
"@sveltejs/kit": "^2.0.0"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"
|
|
29
|
-
"
|
|
28
|
+
"lucide-svelte": "^1.0.1",
|
|
29
|
+
"sveltekit-superforms": "^2.30.1",
|
|
30
|
+
"zod": "^4.3.6"
|
|
30
31
|
},
|
|
31
32
|
"license": "MIT",
|
|
32
33
|
"publishConfig": {
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
<div class="lite-array-item" style="border:1px solid #e2e8f0;border-radius:6px;padding:12px;margin-bottom:8px;background:#f8fafc;">
|
|
35
35
|
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px;">
|
|
36
36
|
<span style="font-size:12px;font-weight:600;color:#94a3b8;">#{i + 1}</span>
|
|
37
|
-
<button type="button" class="lite-btn lite-btn-sm" style="color:#dc2626;border-color:#fecaca;" onclick=
|
|
37
|
+
<button type="button" class="lite-btn lite-btn-sm" style="color:#dc2626;border-color:#fecaca;" onclick={(e) => (e.currentTarget as HTMLElement).closest('.lite-array-item')?.remove()}>
|
|
38
38
|
Remove
|
|
39
39
|
</button>
|
|
40
40
|
</div>
|
|
@@ -91,10 +91,11 @@
|
|
|
91
91
|
<p style="text-align:center;padding:16px;color:#94a3b8;font-size:14px;">No items added yet.</p>
|
|
92
92
|
{/if}
|
|
93
93
|
|
|
94
|
-
<button type="button" class="lite-btn" style="margin-top:4px;" onclick=
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
94
|
+
<button type="button" class="lite-btn" style="margin-top:4px;" onclick={(e) => {
|
|
95
|
+
const target = e.currentTarget as HTMLElement;
|
|
96
|
+
const template = target.previousElementSibling?.previousElementSibling?.cloneNode(true);
|
|
97
|
+
if (template && target.parentElement) target.parentElement.insertBefore(template, target.previousElementSibling);
|
|
98
|
+
}}>
|
|
98
99
|
+ Add Item
|
|
99
100
|
</button>
|
|
100
101
|
</fieldset>
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
type="button"
|
|
49
49
|
class="lite-btn lite-btn-sm"
|
|
50
50
|
style="margin-right:8px;"
|
|
51
|
-
onclick=
|
|
51
|
+
onclick={(e) => (e.currentTarget as HTMLElement).closest('details')?.removeAttribute('open')}
|
|
52
52
|
>
|
|
53
53
|
{cancelLabel}
|
|
54
54
|
</button>
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
<button
|
|
46
46
|
type="button"
|
|
47
47
|
class="lite-btn lite-btn-sm"
|
|
48
|
-
onclick=
|
|
48
|
+
onclick={(e) => (e.currentTarget as HTMLElement).closest('details')?.removeAttribute('open')}
|
|
49
49
|
>
|
|
50
50
|
{t('common.cancel') || 'Cancel'}
|
|
51
51
|
</button>
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
<button
|
|
38
38
|
type="button"
|
|
39
39
|
class="lite-btn lite-btn-sm"
|
|
40
|
-
onclick=
|
|
40
|
+
onclick={(e) => (e.currentTarget as HTMLElement).closest('details')?.removeAttribute('open')}
|
|
41
41
|
>
|
|
42
42
|
{t('common.cancel') || 'Cancel'}
|
|
43
43
|
</button>
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
id={field.key}
|
|
29
29
|
value={String(value ?? '')}
|
|
30
30
|
class="lite-input {hasError ? 'lite-input-error' : ''}"
|
|
31
|
-
placeholder={field.placeholder ?? field.label}
|
|
31
|
+
placeholder={(field as any).placeholder ?? field.label}
|
|
32
32
|
{...field.required ? { required: true } : {}}
|
|
33
33
|
/>
|
|
34
34
|
{#if hasError}
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
{/each}
|
|
30
30
|
</div>
|
|
31
31
|
{:else}
|
|
32
|
+
{@const files = getFiles(value)}
|
|
32
33
|
<div>
|
|
33
|
-
{@const files = getFiles(value)}
|
|
34
34
|
{#if files.length > 0 && mode === 'edit'}
|
|
35
35
|
<div style="margin-bottom: 8px;">
|
|
36
36
|
<span style="font-size: 12px;">Current files: {files.length}</span>
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
name={field.key}
|
|
42
42
|
id={field.key}
|
|
43
43
|
class="lite-input {hasError ? 'lite-input-error' : ''}"
|
|
44
|
-
{...field.type === 'files' ? { multiple: true } : {}}
|
|
44
|
+
{...(field.type as string) === 'files' ? { multiple: true } : {}}
|
|
45
45
|
{...field.required && !files.length ? { required: true } : {}}
|
|
46
46
|
/>
|
|
47
47
|
{#if hasError}
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
{/each}
|
|
30
30
|
</div>
|
|
31
31
|
{:else}
|
|
32
|
+
{@const urls = getUrls(value)}
|
|
32
33
|
<div>
|
|
33
|
-
{@const urls = getUrls(value)}
|
|
34
34
|
{#if urls.length > 0 && mode === 'edit'}
|
|
35
35
|
<div style="margin-bottom: 8px; display:flex; gap: 8px;">
|
|
36
36
|
{#each urls as url}
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
id={field.key}
|
|
46
46
|
accept="image/*"
|
|
47
47
|
class="lite-input {hasError ? 'lite-input-error' : ''}"
|
|
48
|
-
{...field.type === 'images' ? { multiple: true } : {}}
|
|
48
|
+
{...(field.type as string) === 'images' ? { multiple: true } : {}}
|
|
49
49
|
{...field.required && !urls.length ? { required: true } : {}}
|
|
50
50
|
/>
|
|
51
51
|
{#if hasError}
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
id={field.key}
|
|
32
32
|
class="lite-input {hasError ? 'lite-input-error' : ''}"
|
|
33
33
|
style="min-height: 150px; font-family: monospace;"
|
|
34
|
-
placeholder="
|
|
34
|
+
placeholder="{}"
|
|
35
35
|
{...field.required ? { required: true } : {}}
|
|
36
36
|
>{getJsonString(value)}</textarea>
|
|
37
37
|
{#if hasError}
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
id={field.key}
|
|
23
23
|
value={value == null ? '' : Number(value)}
|
|
24
24
|
class="lite-input {hasError ? 'lite-input-error' : ''}"
|
|
25
|
-
placeholder={field.placeholder ?? field.label}
|
|
25
|
+
placeholder={(field as any).placeholder ?? field.label}
|
|
26
26
|
{...field.required ? { required: true } : {}}
|
|
27
27
|
/>
|
|
28
28
|
{#if hasError}
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
if (v == null) return '—';
|
|
19
19
|
if (typeof v === 'object' && v !== null && 'id' in v) {
|
|
20
20
|
// It's a populated relation object
|
|
21
|
-
const labelField = field.relation?.labelField || 'name';
|
|
21
|
+
const labelField = (field as any).relation?.labelField || 'name';
|
|
22
22
|
return String((v as Record<string, unknown>)[labelField] || (v as Record<string, unknown>).id);
|
|
23
23
|
}
|
|
24
24
|
// Try options
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
id={field.key}
|
|
23
23
|
value={String(value ?? '')}
|
|
24
24
|
class="lite-input {hasError ? 'lite-input-error' : ''}"
|
|
25
|
-
placeholder={field.placeholder ?? field.label}
|
|
25
|
+
placeholder={(field as any).placeholder ?? field.label}
|
|
26
26
|
{...field.required ? { required: true } : {}}
|
|
27
27
|
/>
|
|
28
28
|
{#if hasError}
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
{#if mode === 'show'}
|
|
16
16
|
<span>
|
|
17
17
|
{#if value}
|
|
18
|
-
<a href="{value}" target="_blank" rel="noopener noreferrer">{value}</a>
|
|
18
|
+
<a href="{String(value)}" target="_blank" rel="noopener noreferrer">{value}</a>
|
|
19
19
|
{:else}
|
|
20
20
|
—
|
|
21
21
|
{/if}
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
id={field.key}
|
|
29
29
|
value={String(value ?? '')}
|
|
30
30
|
class="lite-input {hasError ? 'lite-input-error' : ''}"
|
|
31
|
-
placeholder={field.placeholder ?? field.label}
|
|
31
|
+
placeholder={(field as any).placeholder ?? field.label}
|
|
32
32
|
{...field.required ? { required: true } : {}}
|
|
33
33
|
/>
|
|
34
34
|
{#if hasError}
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
|
|
50
50
|
<div class="lite-card" style="margin-bottom: 20px;">
|
|
51
51
|
<div style="padding: 16px; border-bottom: 1px solid #e2e8f0; display: flex; justify-content: space-between; align-items: center;">
|
|
52
|
-
<LiteSearch
|
|
52
|
+
<LiteSearch value={currentSearch} placeholder={t('common.search') || 'Search...'} />
|
|
53
53
|
<span style="font-size: 13px; color: #64748b;">
|
|
54
54
|
{t('common.total') || 'Total'}: {total}
|
|
55
55
|
</span>
|
|
@@ -69,8 +69,7 @@
|
|
|
69
69
|
{#if total > pagination.perPage}
|
|
70
70
|
<LitePagination
|
|
71
71
|
page={pagination.page}
|
|
72
|
-
|
|
73
|
-
{total}
|
|
72
|
+
totalPages={Math.ceil(total / pagination.perPage)}
|
|
74
73
|
/>
|
|
75
74
|
{/if}
|
|
76
75
|
</div>
|
package/src/schema-generator.ts
CHANGED
|
@@ -15,7 +15,7 @@ function fieldToZod(field: FieldDefinition): z.ZodTypeAny {
|
|
|
15
15
|
|
|
16
16
|
switch (field.type) {
|
|
17
17
|
case 'number':
|
|
18
|
-
schema = z.coerce.number({
|
|
18
|
+
schema = z.coerce.number({ message: `${field.label} must be a number` });
|
|
19
19
|
break;
|
|
20
20
|
case 'boolean':
|
|
21
21
|
schema = z.coerce.boolean();
|
|
@@ -36,7 +36,7 @@ function fieldToZod(field: FieldDefinition): z.ZodTypeAny {
|
|
|
36
36
|
if (field.options?.length) {
|
|
37
37
|
schema = z.enum(
|
|
38
38
|
field.options.map((o: { value: string | number }) => String(o.value)) as [string, ...string[]],
|
|
39
|
-
{
|
|
39
|
+
{ message: `${field.label} must be one of the options` },
|
|
40
40
|
);
|
|
41
41
|
} else {
|
|
42
42
|
schema = z.string();
|