@svadmin/lite 0.10.0 → 0.12.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 (44) hide show
  1. package/README.md +0 -18
  2. package/dist/compatibility.js +29 -29
  3. package/dist/components/LiteArrayItem.svelte +2 -2
  4. package/dist/components/LiteCanvasAnnotation.svelte +3 -1
  5. package/dist/components/LiteFileInput.svelte +40 -0
  6. package/dist/components/LiteFileInput.svelte.d.ts +11 -0
  7. package/dist/components/LiteForm.svelte +2 -2
  8. package/dist/components/LiteImportWizard.svelte +2 -1
  9. package/dist/components/LiteJsonSchemaForm.svelte +11 -9
  10. package/dist/components/LiteMasterDetailView.svelte +2 -2
  11. package/dist/components/LiteMediaLibraryModal.svelte +5 -2
  12. package/dist/components/LiteSensitiveDataMask.svelte +2 -1
  13. package/dist/components/LiteShowField.svelte +6 -4
  14. package/dist/components/LiteSignaturePad.svelte +3 -1
  15. package/dist/components/advanced/LiteAutoSaveIndicator.svelte +2 -2
  16. package/dist/components/advanced/LiteToast.svelte +2 -2
  17. package/dist/components/advanced/LiteVirtualTable.svelte +6 -5
  18. package/dist/components/buttons/LiteCloneButton.svelte +3 -0
  19. package/dist/components/buttons/LiteCreateButton.svelte +3 -0
  20. package/dist/components/buttons/LiteDeleteButton.svelte +3 -0
  21. package/dist/components/buttons/LiteEditButton.svelte +3 -0
  22. package/dist/components/buttons/LiteExportButton.svelte +3 -0
  23. package/dist/components/buttons/LiteImportButton.svelte +5 -1
  24. package/dist/components/buttons/LiteListButton.svelte +3 -0
  25. package/dist/components/buttons/LiteRefreshButton.svelte +3 -0
  26. package/dist/components/buttons/LiteSaveButton.svelte +3 -0
  27. package/dist/components/buttons/LiteShowButton.svelte +3 -0
  28. package/dist/components/compatibility/LiteDirectoryUpload.svelte +4 -3
  29. package/dist/components/fields/LiteAvatarField.svelte +3 -3
  30. package/dist/components/fields/LiteCascader.svelte +4 -2
  31. package/dist/components/fields/LiteDateField.svelte +3 -0
  32. package/dist/components/fields/LiteDateRangeField.svelte +9 -4
  33. package/dist/components/fields/LiteFileField.svelte +2 -2
  34. package/dist/components/fields/LiteTreeSelect.svelte +4 -2
  35. package/dist/components/pages/LiteListPage.svelte +4 -2
  36. package/dist/index.d.ts +1 -0
  37. package/dist/index.js +1 -0
  38. package/dist/lite.css +86 -0
  39. package/dist/server-adapter.d.ts +15 -15
  40. package/dist/server-adapter.js +8 -7
  41. package/package.json +7 -14
  42. package/dist/polyfill-entry.d.ts +0 -2
  43. package/dist/polyfill-entry.js +0 -3
  44. package/dist/polyfill.js +0 -20707
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">
2
2
  import type { FieldDefinition } from '@svadmin/core';
3
+ import { useTranslation } from '@svadmin/core/i18n';
3
4
 
4
5
  interface Props {
5
6
  field: FieldDefinition;
@@ -9,6 +10,7 @@
9
10
  }
10
11
 
11
12
  let { field, value, error = [], mode = 'show' }: Props = $props();
13
+ const i18n = useTranslation();
12
14
  let hasError = $derived(error.length > 0);
13
15
 
14
16
  function formatDate(v: unknown): string {
@@ -36,6 +38,7 @@
36
38
  <div>
37
39
  <input
38
40
  type="datetime-local"
41
+ lang={i18n.locale}
39
42
  name={field.key}
40
43
  id={field.key}
41
44
  value={formatDate(value)}
@@ -9,6 +9,7 @@
9
9
 
10
10
  <script lang="ts">
11
11
  import type { FieldDefinition } from '@svadmin/core';
12
+ import { useTranslation } from '@svadmin/core/i18n';
12
13
 
13
14
  interface Props {
14
15
  field?: FieldDefinition;
@@ -41,12 +42,14 @@
41
42
  mode = 'show',
42
43
  }: Props = $props();
43
44
 
45
+ const i18n = useTranslation();
46
+
44
47
  const start = $derived.by(() => {
45
48
  if (startDate !== undefined) return startDate;
46
49
  if (Array.isArray(value)) return value[0];
47
50
  if (value && typeof value === 'object') {
48
51
  const obj = value as Record<string, unknown>;
49
- return (obj.start ?? obj.from) as string | number | Date | null | undefined;
52
+ return (obj['start'] ?? obj['from']) as string | number | Date | null | undefined;
50
53
  }
51
54
  return undefined;
52
55
  });
@@ -56,7 +59,7 @@
56
59
  if (Array.isArray(value)) return value[1];
57
60
  if (value && typeof value === 'object') {
58
61
  const obj = value as Record<string, unknown>;
59
- return (obj.end ?? obj.to) as string | number | Date | null | undefined;
62
+ return (obj['end'] ?? obj['to']) as string | number | Date | null | undefined;
60
63
  }
61
64
  return undefined;
62
65
  });
@@ -118,23 +121,25 @@
118
121
  <div class="lite-inline-sm" style="display: flex; align-items: center;">
119
122
  <input
120
123
  type="date"
124
+ lang={i18n.locale}
121
125
  name={startKey}
122
126
  id={startKey}
123
127
  value={toIsoDateInput(start)}
124
128
  class="lite-input {hasError ? 'lite-input-error' : ''}"
125
129
  style="flex: 1; min-width: 120px;"
126
- placeholder="Start date"
130
+ placeholder={i18n.t('common.startDate')}
127
131
  {...field?.required ? { required: true } : {}}
128
132
  />
129
133
  <span style="margin: 0 8px; color: #64748b;">{separator}</span>
130
134
  <input
131
135
  type="date"
136
+ lang={i18n.locale}
132
137
  name={endKey}
133
138
  id={endKey}
134
139
  value={toIsoDateInput(end)}
135
140
  class="lite-input {hasError ? 'lite-input-error' : ''}"
136
141
  style="flex: 1; min-width: 120px;"
137
- placeholder="End date"
142
+ placeholder={i18n.t('common.endDate')}
138
143
  {...field?.required ? { required: true } : {}}
139
144
  />
140
145
  </div>
@@ -1,6 +1,7 @@
1
1
  <script lang="ts">
2
2
  import type { FieldDefinition } from '@svadmin/core';
3
3
  import { toSafeHref } from '../../security';
4
+ import LiteFileInput from '../LiteFileInput.svelte';
4
5
 
5
6
  interface Props {
6
7
  field: FieldDefinition;
@@ -42,8 +43,7 @@
42
43
  <span style="font-size: 12px;">Current files: {files.length}</span>
43
44
  </div>
44
45
  {/if}
45
- <input
46
- type="file"
46
+ <LiteFileInput
47
47
  name={field.key}
48
48
  id={field.key}
49
49
  class="lite-input {hasError ? 'lite-input-error' : ''}"
@@ -1,4 +1,6 @@
1
1
  <script lang="ts">
2
+ import { definedOptions } from '@svadmin/core/options';
3
+
2
4
  import type { FieldDefinition } from '@svadmin/core';
3
5
  import { t } from '@svadmin/core/i18n';
4
6
 
@@ -49,12 +51,12 @@
49
51
  function flattenTree(nodes: TreeSelectOption[], level = 0): FlatTreeOption[] {
50
52
  const list: FlatTreeOption[] = [];
51
53
  for (const node of nodes) {
52
- list.push({
54
+ list.push(definedOptions({
53
55
  value: node.value,
54
56
  label: node.label,
55
57
  level,
56
58
  disabled: node.disabled,
57
- });
59
+ }));
58
60
  if (node.children && node.children.length > 0) {
59
61
  list.push(...flattenTree(node.children, level + 1));
60
62
  }
@@ -1,4 +1,6 @@
1
1
  <script lang="ts">
2
+ import { definedOptions } from '@svadmin/core/options';
3
+
2
4
  import type { ResourceDefinition } from "@svadmin/core";
3
5
  import { t } from "@svadmin/core/i18n";
4
6
  import LiteTable from "../LiteTable.svelte";
@@ -134,9 +136,9 @@
134
136
  <LiteTable
135
137
  {records}
136
138
  {resource}
137
- {currentSort}
139
+ {...definedOptions({ "currentSort": currentSort })}
138
140
  {currentOrder}
139
- {currentSearch}
141
+ {...definedOptions({ "currentSearch": currentSearch })}
140
142
  {basePath}
141
143
  canShow={showView}
142
144
  canEdit={showEdit}
package/dist/index.d.ts CHANGED
@@ -8,6 +8,7 @@ export { default as LiteLayout } from './components/LiteLayout.svelte';
8
8
  export { default as LiteTable } from './components/LiteTable.svelte';
9
9
  export { default as LitePagination } from './components/LitePagination.svelte';
10
10
  export { default as LiteForm } from './components/LiteForm.svelte';
11
+ export { default as LiteFileInput } from './components/LiteFileInput.svelte';
11
12
  export { default as LiteLogin } from './components/LiteLogin.svelte';
12
13
  export { default as LiteShow } from './components/LiteShow.svelte';
13
14
  export { default as LiteSearch } from './components/LiteSearch.svelte';
package/dist/index.js CHANGED
@@ -12,6 +12,7 @@ export { default as LiteLayout } from './components/LiteLayout.svelte';
12
12
  export { default as LiteTable } from './components/LiteTable.svelte';
13
13
  export { default as LitePagination } from './components/LitePagination.svelte';
14
14
  export { default as LiteForm } from './components/LiteForm.svelte';
15
+ export { default as LiteFileInput } from './components/LiteFileInput.svelte';
15
16
  export { default as LiteLogin } from './components/LiteLogin.svelte';
16
17
  export { default as LiteShow } from './components/LiteShow.svelte';
17
18
  export { default as LiteSearch } from './components/LiteSearch.svelte';
package/dist/lite.css CHANGED
@@ -471,6 +471,92 @@ a:hover {
471
471
  box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.12);
472
472
  }
473
473
 
474
+ /* File inputs keep the native form control but replace its browser-localized
475
+ * visible copy with application-controlled labels. */
476
+ .lite-file-picker {
477
+ position: relative;
478
+ display: inline-flex;
479
+ align-items: center;
480
+ min-width: 0;
481
+ min-height: 38px;
482
+ color: #64748b;
483
+ vertical-align: middle;
484
+ }
485
+
486
+ .lite-file-picker.lite-input {
487
+ display: flex;
488
+ width: 100%;
489
+ padding: 0;
490
+ border: 0;
491
+ background: transparent;
492
+ }
493
+
494
+ .lite-file-picker__native {
495
+ position: absolute;
496
+ z-index: 1;
497
+ top: 0;
498
+ right: 0;
499
+ bottom: 0;
500
+ left: 0;
501
+ width: 100%;
502
+ height: 100%;
503
+ margin: 0;
504
+ opacity: 0;
505
+ cursor: pointer;
506
+ }
507
+
508
+ .lite-file-picker__visual {
509
+ display: flex;
510
+ align-items: center;
511
+ width: 100%;
512
+ min-width: 0;
513
+ min-height: 38px;
514
+ overflow: hidden;
515
+ border: 1px solid #cbd5e1;
516
+ border-radius: 6px;
517
+ padding: 6px 10px 6px 6px;
518
+ background: #fff;
519
+ pointer-events: none;
520
+ }
521
+
522
+ .lite-file-picker__visual > * + * {
523
+ margin-left: 10px;
524
+ }
525
+
526
+ .lite-file-picker__button {
527
+ flex: 0 0 auto;
528
+ display: inline-flex;
529
+ align-items: center;
530
+ min-height: 26px;
531
+ border: 1px solid #cbd5e1;
532
+ border-radius: 4px;
533
+ padding: 0 10px;
534
+ background: #f8fafc;
535
+ color: #334155;
536
+ font-size: 12px;
537
+ white-space: nowrap;
538
+ }
539
+
540
+ .lite-file-picker__name {
541
+ min-width: 0;
542
+ overflow: hidden;
543
+ text-overflow: ellipsis;
544
+ white-space: nowrap;
545
+ }
546
+
547
+ .lite-file-picker[data-disabled="true"] .lite-file-picker__visual {
548
+ opacity: 0.5;
549
+ }
550
+
551
+ .lite-file-picker.lite-input-error .lite-file-picker__visual {
552
+ border-color: #ef4444;
553
+ }
554
+
555
+ .lite-file-picker__native:focus + .lite-file-picker__visual {
556
+ border-color: #4f46e5;
557
+ box-shadow: 0 0 0 1px #fff, 0 0 0 3px rgba(79, 70, 229, 0.3);
558
+ }
559
+
474
560
  .lite-error-text {
475
561
  color: #ef4444;
476
562
  font-size: 12px;
@@ -55,18 +55,18 @@ export declare function createCrudActions(dp: DataProvider, resource: ResourceDe
55
55
  } | {
56
56
  success: boolean;
57
57
  error: string;
58
- id?: undefined;
59
- values?: undefined;
58
+ id?: never;
59
+ values?: never;
60
60
  } | {
61
61
  success: boolean;
62
62
  id: unknown;
63
- error?: undefined;
64
- values?: undefined;
63
+ error?: never;
64
+ values?: never;
65
65
  } | {
66
66
  success: boolean;
67
67
  error: string;
68
68
  values: Record<string, unknown>;
69
- id?: undefined;
69
+ id?: never;
70
70
  }>;
71
71
  update: ({ request }: RequestEvent) => Promise<{
72
72
  success: false;
@@ -76,11 +76,11 @@ export declare function createCrudActions(dp: DataProvider, resource: ResourceDe
76
76
  } | {
77
77
  success: boolean;
78
78
  error: string;
79
- values?: undefined;
79
+ values?: never;
80
80
  } | {
81
81
  success: boolean;
82
- error?: undefined;
83
- values?: undefined;
82
+ error?: never;
83
+ values?: never;
84
84
  } | {
85
85
  success: boolean;
86
86
  error: string;
@@ -91,14 +91,14 @@ export declare function createCrudActions(dp: DataProvider, resource: ResourceDe
91
91
  error: string;
92
92
  } | {
93
93
  success: boolean;
94
- error?: undefined;
94
+ error?: never;
95
95
  }>;
96
96
  batchDelete: ({ request }: RequestEvent) => Promise<{
97
97
  success: boolean;
98
98
  error: string;
99
99
  } | {
100
100
  success: boolean;
101
- error?: undefined;
101
+ error?: never;
102
102
  }>;
103
103
  };
104
104
  /**
@@ -122,35 +122,35 @@ export declare function createAuthActions(authProvider: AuthProvider): {
122
122
  error: string;
123
123
  } | {
124
124
  success: boolean;
125
- error?: undefined;
125
+ error?: never;
126
126
  }>;
127
127
  register: ({ request }: RequestEvent) => Promise<{
128
128
  success: boolean;
129
129
  error: string;
130
130
  } | {
131
131
  success: boolean;
132
- error?: undefined;
132
+ error?: never;
133
133
  }>;
134
134
  forgot_password: ({ request }: RequestEvent) => Promise<{
135
135
  success: boolean;
136
136
  error: string;
137
137
  } | {
138
138
  success: boolean;
139
- error?: undefined;
139
+ error?: never;
140
140
  }>;
141
141
  update_password: ({ request }: RequestEvent) => Promise<{
142
142
  success: boolean;
143
143
  error: string;
144
144
  } | {
145
145
  success: boolean;
146
- error?: undefined;
146
+ error?: never;
147
147
  }>;
148
148
  update_profile: ({ request }: RequestEvent) => Promise<{
149
149
  success: boolean;
150
150
  error: string;
151
151
  } | {
152
152
  success: boolean;
153
- error?: undefined;
153
+ error?: never;
154
154
  }>;
155
155
  };
156
156
  /**
@@ -1,3 +1,4 @@
1
+ import { definedOptions } from '@svadmin/core/options';
1
2
  import { redirect, isRedirect } from '@sveltejs/kit';
2
3
  import { resourceToTypeBoxSchema } from './schema-generator';
3
4
  import { parseExplicitBoolean } from './value-normalization';
@@ -17,7 +18,7 @@ function listRequestState(url, resource) {
17
18
  const order = sortableField
18
19
  ? requestedOrder === 'asc' || requestedOrder === 'desc' ? requestedOrder : 'asc'
19
20
  : resource.defaultSort?.order ?? 'asc';
20
- return { page, pageSize, sort, order, search: url.searchParams.get('q') ?? undefined };
21
+ return definedOptions({ page, pageSize, sort, order, search: url.searchParams.get('q') ?? undefined });
21
22
  }
22
23
  function listRequestFilterValues(resource, url) {
23
24
  const values = {};
@@ -41,7 +42,7 @@ function listRequestFilters(resource, url) {
41
42
  value: search,
42
43
  }));
43
44
  if (searchFilters.length === 1) {
44
- filters.push(searchFilters[0]);
45
+ filters.push(...searchFilters);
45
46
  }
46
47
  else if (searchFilters.length > 1) {
47
48
  filters.push({ operator: 'or', value: searchFilters });
@@ -73,7 +74,7 @@ export function createListLoader(dp, resource) {
73
74
  sorters,
74
75
  filters: listRequestFilters(resource, url),
75
76
  });
76
- return {
77
+ return definedOptions({
77
78
  records: listResponse.data,
78
79
  total: listResponse.total,
79
80
  page,
@@ -88,7 +89,7 @@ export function createListLoader(dp, resource) {
88
89
  currentSearch: search,
89
90
  currentFilters: listRequestFilterValues(resource, url),
90
91
  resource,
91
- };
92
+ });
92
93
  };
93
94
  }
94
95
  // ─── Detail Loader ────────────────────────────────────────────
@@ -260,7 +261,7 @@ export function createAuthActions(authProvider) {
260
261
  }
261
262
  function validatePasswordConfirmation(authParams) {
262
263
  const { confirmPassword, ...providerParams } = authParams;
263
- const password = authParams.password;
264
+ const password = authParams['password'];
264
265
  if (typeof password !== 'string'
265
266
  || password.length === 0
266
267
  || typeof confirmPassword !== 'string'
@@ -363,13 +364,13 @@ function pathWithinPrefix(pathname, prefix) {
363
364
  }
364
365
  function redirectOptions(options) {
365
366
  const configured = typeof options === 'string' ? { litePrefix: options } : options;
366
- return {
367
+ return definedOptions({
367
368
  litePrefix: normalizeRoutePrefix(configured.litePrefix ?? '/lite'),
368
369
  spaPrefix: normalizeRoutePrefix(configured.spaPrefix ?? '/'),
369
370
  exclude: (configured.exclude ?? []).map(normalizeRoutePrefix),
370
371
  mapPath: configured.mapPath,
371
372
  status: configured.status ?? 302,
372
- };
373
+ });
373
374
  }
374
375
  /**
375
376
  * Computes a legacy redirect without importing or modifying the modern SPA.
package/package.json CHANGED
@@ -1,12 +1,11 @@
1
1
  {
2
2
  "name": "@svadmin/lite",
3
- "version": "0.10.0",
3
+ "version": "0.12.0",
4
4
  "description": "SSR-first lightweight admin UI for @svadmin with optional progressive enhancement",
5
5
  "type": "module",
6
6
  "sideEffects": [
7
7
  "./dist/lite.css",
8
- "./dist/enhance.js",
9
- "./dist/polyfill.js"
8
+ "./dist/enhance.js"
10
9
  ],
11
10
  "files": [
12
11
  "dist",
@@ -35,10 +34,6 @@
35
34
  "import": "./dist/enhance.js",
36
35
  "default": "./dist/enhance.js"
37
36
  },
38
- "./polyfill.js": {
39
- "import": "./dist/polyfill.js",
40
- "default": "./dist/polyfill.js"
41
- },
42
37
  "./lite.css": {
43
38
  "svelte": "./dist/lite.css",
44
39
  "style": "./dist/lite.css",
@@ -54,17 +49,15 @@
54
49
  },
55
50
  "peerDependencies": {
56
51
  "svelte": "^5.56.10",
57
- "@svadmin/core": ">=0.34.2 <0.50.0",
52
+ "@svadmin/core": ">=0.34.2 <0.53.0",
58
53
  "@sveltejs/kit": "^2.70.3"
59
54
  },
60
55
  "dependencies": {
61
56
  "@lucide/svelte": "^1.35.0",
62
- "@sinclair/typebox": "^0.34.52",
63
- "core-js": "^3.50.0",
64
- "whatwg-fetch": "^3.6.20"
57
+ "@sinclair/typebox": "^0.34.52"
65
58
  },
66
59
  "devDependencies": {
67
- "@types/bun": "^1.4.0",
60
+ "@types/bun": "^1.4.2",
68
61
  "@happy-dom/global-registrator": "^20.11.12",
69
62
  "@sveltejs/vite-plugin-svelte": "^7.3.0",
70
63
  "@testing-library/dom": "^10.4.1",
@@ -73,7 +66,7 @@
73
66
  "vitest": "^4.1.11"
74
67
  },
75
68
  "scripts": {
76
- "build": "node ../../node_modules/@sveltejs/package/svelte-package.js -i src -o dist && node ../../node_modules/esbuild/bin/esbuild --bundle src/polyfill-entry.ts --target=es5 --outfile=dist/polyfill.js && bun ../../scripts/copy-package-assets.ts static/enhance.js dist/enhance.js",
69
+ "build": "node ../../node_modules/@sveltejs/package/svelte-package.js -i src -o dist && bun ../../scripts/copy-package-assets.ts static/enhance.js dist/enhance.js",
77
70
  "test": "bun run test:components && bun run test:server && bun run test:security",
78
71
  "test:components": "vitest run --config ./vitest.config.ts",
79
72
  "test:server": "bun test ./src/schema-generator.test.ts ./src/server-adapter.test.ts ./src/fragment-id.test.ts ./src/compatibility.test.ts ./src/ie11-ssr-contract.test.ts",
@@ -88,7 +81,7 @@
88
81
  "author": "zuohuadong",
89
82
  "repository": {
90
83
  "type": "git",
91
- "url": "https://github.com/vibeunion/svadmin.git",
84
+ "url": "git+https://github.com/vibeunion/svadmin.git",
92
85
  "directory": "packages/lite"
93
86
  }
94
87
  }
@@ -1,2 +0,0 @@
1
- import 'core-js/stable';
2
- import 'whatwg-fetch';
@@ -1,3 +0,0 @@
1
- // IE11 baseline for optional Lite enhancements.
2
- import 'core-js/stable';
3
- import 'whatwg-fetch';