@svadmin/lite 0.2.4 → 0.3.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@svadmin/lite",
3
- "version": "0.2.4",
3
+ "version": "0.3.0",
4
4
  "description": "SSR-compatible lightweight admin UI for @svadmin — zero client-side JS, works in IE11",
5
5
  "type": "module",
6
6
  "files": [
@@ -21,12 +21,13 @@
21
21
  },
22
22
  "peerDependencies": {
23
23
  "svelte": "^5.0.0",
24
- "@svadmin/core": "^0.19.4",
24
+ "@svadmin/core": "^0.19.5",
25
25
  "@sveltejs/kit": "^2.0.0"
26
26
  },
27
27
  "dependencies": {
28
- "sveltekit-superforms": "^2.22.0",
29
- "zod": "^3.24.0"
28
+ "@lucide/svelte": "^1.7.0",
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="this.closest('.lite-array-item').remove()">
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 template = this.previousElementSibling?.previousElementSibling?.cloneNode(true);
96
- if (template) this.parentElement.insertBefore(template, this.previousElementSibling);
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="this.closest('details').removeAttribute('open')"
51
+ onclick={(e) => (e.currentTarget as HTMLElement).closest('details')?.removeAttribute('open')}
52
52
  >
53
53
  {cancelLabel}
54
54
  </button>
@@ -1,6 +1,6 @@
1
1
  <script lang="ts">
2
2
  import { t } from '@svadmin/core/i18n';
3
- import { Copy } from 'lucide-svelte';
3
+ import { Copy } from '@lucide/svelte';
4
4
 
5
5
  interface Props {
6
6
  resource: string;
@@ -1,6 +1,6 @@
1
1
  <script lang="ts">
2
2
  import { t } from '@svadmin/core/i18n';
3
- import { Plus } from 'lucide-svelte';
3
+ import { Plus } from '@lucide/svelte';
4
4
 
5
5
  interface Props {
6
6
  resource: string;
@@ -1,6 +1,6 @@
1
1
  <script lang="ts">
2
2
  import { t } from '@svadmin/core/i18n';
3
- import { Trash2 } from 'lucide-svelte';
3
+ import { Trash2 } from '@lucide/svelte';
4
4
 
5
5
  interface Props {
6
6
  resource: string;
@@ -45,7 +45,7 @@
45
45
  <button
46
46
  type="button"
47
47
  class="lite-btn lite-btn-sm"
48
- onclick="this.closest('details').removeAttribute('open')"
48
+ onclick={(e) => (e.currentTarget as HTMLElement).closest('details')?.removeAttribute('open')}
49
49
  >
50
50
  {t('common.cancel') || 'Cancel'}
51
51
  </button>
@@ -1,6 +1,6 @@
1
1
  <script lang="ts">
2
2
  import { t } from '@svadmin/core/i18n';
3
- import { Pencil } from 'lucide-svelte';
3
+ import { Pencil } from '@lucide/svelte';
4
4
 
5
5
  interface Props {
6
6
  resource: string;
@@ -1,6 +1,6 @@
1
1
  <script lang="ts">
2
2
  import { t } from '@svadmin/core/i18n';
3
- import { Download } from 'lucide-svelte';
3
+ import { Download } from '@lucide/svelte';
4
4
 
5
5
  interface Props {
6
6
  resource: string;
@@ -1,6 +1,6 @@
1
1
  <script lang="ts">
2
2
  import { t } from '@svadmin/core/i18n';
3
- import { Upload } from 'lucide-svelte';
3
+ import { Upload } from '@lucide/svelte';
4
4
 
5
5
  interface Props {
6
6
  resource: string;
@@ -37,7 +37,7 @@
37
37
  <button
38
38
  type="button"
39
39
  class="lite-btn lite-btn-sm"
40
- onclick="this.closest('details').removeAttribute('open')"
40
+ onclick={(e) => (e.currentTarget as HTMLElement).closest('details')?.removeAttribute('open')}
41
41
  >
42
42
  {t('common.cancel') || 'Cancel'}
43
43
  </button>
@@ -1,6 +1,6 @@
1
1
  <script lang="ts">
2
2
  import { t } from '@svadmin/core/i18n';
3
- import { List } from 'lucide-svelte';
3
+ import { List } from '@lucide/svelte';
4
4
 
5
5
  interface Props {
6
6
  resource: string;
@@ -1,6 +1,6 @@
1
1
  <script lang="ts">
2
2
  import { t } from '@svadmin/core/i18n';
3
- import { RefreshCw } from 'lucide-svelte';
3
+ import { RefreshCw } from '@lucide/svelte';
4
4
 
5
5
  interface Props {
6
6
  hideText?: boolean;
@@ -1,6 +1,6 @@
1
1
  <script lang="ts">
2
2
  import { t } from '@svadmin/core/i18n';
3
- import { Save } from 'lucide-svelte';
3
+ import { Save } from '@lucide/svelte';
4
4
 
5
5
  interface Props {
6
6
  hideText?: boolean;
@@ -1,6 +1,6 @@
1
1
  <script lang="ts">
2
2
  import { t } from '@svadmin/core/i18n';
3
- import { Eye } from 'lucide-svelte';
3
+ import { Eye } from '@lucide/svelte';
4
4
 
5
5
  interface Props {
6
6
  resource: string;
@@ -30,7 +30,7 @@
30
30
  <input type="hidden" name={field.key} value="false" />
31
31
 
32
32
  <label for={field.key}>
33
- {field.placeholder ?? (field.label || 'Yes')}
33
+ {(field as any).placeholder ?? (field.label || 'Yes')}
34
34
  </label>
35
35
  {#if hasError}
36
36
  {#each error as err}
@@ -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="&lbrace;&rbrace;"
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}
@@ -1,6 +1,6 @@
1
1
  <script lang="ts">
2
2
  import { t } from '@svadmin/core/i18n';
3
- import { AlertTriangle } from 'lucide-svelte';
3
+ import { AlertTriangle } from '@lucide/svelte';
4
4
 
5
5
  interface Props {
6
6
  title?: string;
@@ -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 defaultValue={currentSearch} placeholder={t('common.search') || 'Search...'} />
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
- perPage={pagination.perPage}
73
- {total}
72
+ totalPages={Math.ceil(total / pagination.perPage)}
74
73
  />
75
74
  {/if}
76
75
  </div>
@@ -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({ invalid_type_error: `${field.label} must be a 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
- { errorMap: () => ({ message: `${field.label} must be one of the options` }) },
39
+ { message: `${field.label} must be one of the options` },
40
40
  );
41
41
  } else {
42
42
  schema = z.string();