yaxa-svelte 1.9.0 → 1.10.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 (54) hide show
  1. package/dist/admin/components/AdminDashboard.svelte +6 -0
  2. package/dist/admin/components/DevSandbox.svelte +6 -2
  3. package/dist/admin/components/RecordDrawer.svelte +4 -0
  4. package/dist/components/elements/Icon.svelte +32 -1
  5. package/dist/components/forms/Checkbox.svelte +3 -0
  6. package/dist/components/forms/Checkbox.svelte.d.ts +1 -0
  7. package/dist/components/forms/ColorPicker.svelte +22 -2
  8. package/dist/components/forms/ColorPicker.svelte.d.ts +5 -0
  9. package/dist/components/forms/Combobox.svelte +3 -0
  10. package/dist/components/forms/Dropzone.svelte +11 -0
  11. package/dist/components/forms/Dropzone.svelte.d.ts +4 -0
  12. package/dist/components/forms/FormField.svelte +6 -1
  13. package/dist/components/forms/FormField.svelte.d.ts +1 -0
  14. package/dist/components/forms/InputOTP.svelte +6 -0
  15. package/dist/components/forms/InputOTP.svelte.d.ts +2 -0
  16. package/dist/components/forms/MultiSelect.svelte +3 -0
  17. package/dist/components/forms/NumberInput.svelte +11 -1
  18. package/dist/components/forms/NumberInput.svelte.d.ts +4 -0
  19. package/dist/components/forms/RadioGroup.svelte +2 -0
  20. package/dist/components/forms/Select.svelte +14 -1
  21. package/dist/components/forms/Select.svelte.d.ts +5 -0
  22. package/dist/components/forms/Slider.svelte +14 -1
  23. package/dist/components/forms/Slider.svelte.d.ts +4 -0
  24. package/dist/components/forms/Switch.svelte +14 -1
  25. package/dist/components/forms/Switch.svelte.d.ts +3 -0
  26. package/dist/components/layout/Footer.svelte +10 -15
  27. package/dist/components/layout/Header.svelte +5 -4
  28. package/dist/components/layout/YaxaApp.svelte +9 -7
  29. package/dist/components/legal/LegalDocument.svelte +3 -2
  30. package/dist/components/navigation/CommandPalette.svelte +3 -0
  31. package/dist/components/navigation/DropdownMenu.svelte +14 -11
  32. package/dist/components/overlays/Popover.svelte +5 -1
  33. package/dist/components/overlays/Tooltip.svelte +9 -5
  34. package/dist/components/saas/Gate.svelte +3 -2
  35. package/dist/components/saas/UserMenu.svelte +3 -3
  36. package/dist/components/seo/Favicons.svelte +37 -12
  37. package/dist/components/seo/Favicons.svelte.d.ts +4 -0
  38. package/dist/components/seo/OgImage.svelte +3 -2
  39. package/dist/components/seo/Seo.svelte +6 -4
  40. package/dist/composables/useGate.svelte.js +3 -2
  41. package/dist/composables/useLocale.svelte.js +8 -6
  42. package/dist/index.d.ts +1 -1
  43. package/dist/index.js +1 -1
  44. package/dist/site/config.d.ts +2 -0
  45. package/dist/site/context.d.ts +1 -30
  46. package/dist/site/context.js +1 -43
  47. package/dist/site/context.svelte.d.ts +37 -0
  48. package/dist/site/context.svelte.js +52 -0
  49. package/dist/site/context.test.d.ts +1 -0
  50. package/dist/site/context.test.js +29 -0
  51. package/dist/styles/yaxa.css +21 -0
  52. package/dist/vite/index.js +12 -4
  53. package/dist/vite/index.test.js +6 -6
  54. package/package.json +6 -12
@@ -284,8 +284,11 @@
284
284
  <div class="flex items-center gap-2">
285
285
  <form onsubmit={handleSearch} class="relative">
286
286
  <input
287
+ id="admin-search-input"
288
+ name="adminSearch"
287
289
  type="search"
288
290
  placeholder="Search records..."
291
+ aria-label="Search records"
289
292
  bind:value={searchQuery}
290
293
  class="w-48 rounded-lg border border-neutral-300 bg-white py-1.5 pr-3 pl-8 text-xs text-neutral-900 placeholder-neutral-400 transition-all focus:w-60 focus:border-primary-500 focus:ring-1 focus:ring-primary-500 focus:outline-none dark:border-neutral-700 dark:bg-neutral-900 dark:text-neutral-100"
291
294
  />
@@ -424,6 +427,7 @@
424
427
  {#if col.dataType === 'boolean'}
425
428
  <input
426
429
  id={`create-${col.name}`}
430
+ name={col.name}
427
431
  type="checkbox"
428
432
  class="h-4 w-4 rounded border-neutral-300 text-primary-600 focus:ring-primary-500"
429
433
  checked={Boolean(newRecordData[col.name])}
@@ -432,6 +436,7 @@
432
436
  {:else if col.dataType === 'number' || col.dataType === 'integer'}
433
437
  <input
434
438
  id={`create-${col.name}`}
439
+ name={col.name}
435
440
  type="number"
436
441
  placeholder={col.primaryKey ? '(Auto-generated if empty)' : ''}
437
442
  class="w-full rounded-md border border-neutral-300 bg-white px-3 py-1.5 text-xs dark:border-neutral-700 dark:bg-neutral-900"
@@ -443,6 +448,7 @@
443
448
  {:else}
444
449
  <input
445
450
  id={`create-${col.name}`}
451
+ name={col.name}
446
452
  type="text"
447
453
  placeholder={col.primaryKey ? '(Auto-generated UUID if empty)' : ''}
448
454
  class="w-full rounded-md border border-neutral-300 bg-white px-3 py-1.5 text-xs dark:border-neutral-700 dark:bg-neutral-900"
@@ -5,7 +5,7 @@
5
5
  import Icon from '../../components/elements/Icon.svelte';
6
6
  import Progress from '../../components/elements/Progress.svelte';
7
7
  import { useUpload } from '../../composables/useUpload.svelte';
8
- import { getAuthUserContext } from '../../site/context';
8
+ import { useYaxa } from '../../site/context';
9
9
 
10
10
  interface Props {
11
11
  apiPrefix?: string;
@@ -15,7 +15,8 @@
15
15
  let { apiPrefix = '/admin/api', class: className = '' }: Props = $props();
16
16
 
17
17
  // Auth & Context info
18
- const user = $derived(getAuthUserContext());
18
+ const yaxa = useYaxa();
19
+ const user = $derived(yaxa.user);
19
20
 
20
21
  // Email Sandbox State
21
22
  let emailTo = $state('developer@example.com');
@@ -96,6 +97,7 @@
96
97
  >
97
98
  <input
98
99
  id="email-to"
100
+ name="emailTo"
99
101
  type="email"
100
102
  bind:value={emailTo}
101
103
  placeholder="you@example.com"
@@ -109,6 +111,7 @@
109
111
  >
110
112
  <select
111
113
  id="email-template"
114
+ name="emailTemplate"
112
115
  bind:value={emailTemplate}
113
116
  class="w-full rounded-md border border-neutral-300 bg-white px-3 py-1.5 text-xs text-neutral-900 focus:border-primary-500 focus:ring-1 focus:ring-primary-500 focus:outline-none dark:border-neutral-700 dark:bg-neutral-900 dark:text-neutral-100"
114
117
  >
@@ -172,6 +175,7 @@
172
175
  >
173
176
  <input
174
177
  id="storage-file"
178
+ name="storageFile"
175
179
  type="file"
176
180
  onchange={(e) => {
177
181
  const files = e.currentTarget.files;
@@ -138,6 +138,7 @@
138
138
  <div class="flex items-center gap-2 pt-1">
139
139
  <input
140
140
  id={`field-${col.name}`}
141
+ name={col.name}
141
142
  type="checkbox"
142
143
  class="h-4 w-4 rounded border-neutral-300 text-primary-600 focus:ring-primary-500 dark:border-neutral-700 dark:bg-neutral-800"
143
144
  checked={Boolean(formData[col.name])}
@@ -150,6 +151,7 @@
150
151
  {:else if col.dataType === 'number' || col.dataType === 'integer'}
151
152
  <input
152
153
  id={`field-${col.name}`}
154
+ name={col.name}
153
155
  type="number"
154
156
  class="w-full rounded-md border border-neutral-300 bg-white px-3 py-1.5 text-sm text-neutral-900 placeholder-neutral-400 focus:border-primary-500 focus:ring-1 focus:ring-primary-500 focus:outline-none disabled:opacity-50 dark:border-neutral-700 dark:bg-neutral-900 dark:text-neutral-100"
155
157
  disabled={col.primaryKey}
@@ -162,6 +164,7 @@
162
164
  {:else if String(formData[col.name] ?? '').length > 60 || col.name.includes('description') || col.name.includes('body')}
163
165
  <textarea
164
166
  id={`field-${col.name}`}
167
+ name={col.name}
165
168
  rows={3}
166
169
  class="w-full rounded-md border border-neutral-300 bg-white px-3 py-1.5 font-mono text-sm text-xs text-neutral-900 placeholder-neutral-400 focus:border-primary-500 focus:ring-1 focus:ring-primary-500 focus:outline-none disabled:opacity-50 dark:border-neutral-700 dark:bg-neutral-900 dark:text-neutral-100"
167
170
  disabled={col.primaryKey}
@@ -172,6 +175,7 @@
172
175
  {:else}
173
176
  <input
174
177
  id={`field-${col.name}`}
178
+ name={col.name}
175
179
  type="text"
176
180
  class="w-full rounded-md border border-neutral-300 bg-white px-3 py-1.5 text-sm text-neutral-900 placeholder-neutral-400 focus:border-primary-500 focus:ring-1 focus:ring-primary-500 focus:outline-none disabled:opacity-50 dark:border-neutral-700 dark:bg-neutral-900 dark:text-neutral-100"
177
181
  disabled={col.primaryKey}
@@ -126,7 +126,38 @@
126
126
  '<circle cx="12" cy="12" r="1.5" fill="currentColor"/><circle cx="12" cy="5" r="1.5" fill="currentColor"/><circle cx="12" cy="19" r="1.5" fill="currentColor"/>',
127
127
  user: '<path d="M20 21v-2a4 4 0 00-4-4H8a4 4 0 00-4 4v2" stroke="currentColor" stroke-width="2" stroke-linecap="round" fill="none"/><circle cx="12" cy="7" r="4" stroke="currentColor" stroke-width="2" fill="none"/>',
128
128
  users:
129
- '<path d="M17 21v-2a4 4 0 00-3-3.87M9 21v-2a4 4 0 013-3.87M16 3.13a4 4 0 010 7.75M8 3.13a4 4 0 000 7.75" stroke="currentColor" stroke-width="2" stroke-linecap="round" fill="none"/>'
129
+ '<path d="M17 21v-2a4 4 0 00-3-3.87M9 21v-2a4 4 0 013-3.87M16 3.13a4 4 0 010 7.75M8 3.13a4 4 0 000 7.75" stroke="currentColor" stroke-width="2" stroke-linecap="round" fill="none"/>',
130
+ list: '<path d="M8 6h13M8 12h13M8 18h13M3 6h.01M3 12h.01M3 18h.01" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="none"/>',
131
+ 'help-circle':
132
+ '<circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2" fill="none"/><path d="M9.09 9a3 3 0 015.83 1c0 2-3 3-3 3m.08 4h.01" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>',
133
+ help: '<circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2" fill="none"/><path d="M9.09 9a3 3 0 015.83 1c0 2-3 3-3 3m.08 4h.01" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>',
134
+ bolt: '<path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="none"/>',
135
+ cube: '<path d="M21 16V8a2 2 0 00-1-1.73l-7-4a2 2 0 00-2 0l-7 4A2 2 0 003 8v8a2 2 0 001 1.73l7 4a2 2 0 002 0l7-4A2 2 0 0021 16z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="none"/><polyline points="3.27 6.96 12 12.01 20.73 6.96" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="none"/><line x1="12" y1="22.08" x2="12" y2="12" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>',
136
+ swatch:
137
+ '<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10c.88 0 1.6-.72 1.6-1.6 0-.41-.17-.79-.44-1.06-.27-.28-.44-.65-.44-1.06 0-.88.72-1.6 1.6-1.6h1.88c3.15 0 5.72-2.57 5.72-5.72C21.32 6.16 17.14 2 12 2z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="none"/><circle cx="6.5" cy="11.5" r="1.5" fill="currentColor"/><circle cx="9.5" cy="7.5" r="1.5" fill="currentColor"/><circle cx="14.5" cy="7.5" r="1.5" fill="currentColor"/><circle cx="17.5" cy="11.5" r="1.5" fill="currentColor"/>',
138
+ star: '<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="none"/>',
139
+ photo:
140
+ '<rect x="3" y="3" width="18" height="18" rx="2" stroke="currentColor" stroke-width="2" fill="none"/><circle cx="8.5" cy="8.5" r="1.5" fill="currentColor"/><polyline points="21 15 16 10 5 21" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="none"/>',
141
+ 'document-text':
142
+ '<path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="none"/><polyline points="14 2 14 8 20 8" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="none"/><line x1="8" y1="13" x2="16" y2="13" stroke="currentColor" stroke-width="2" stroke-linecap="round"/><line x1="8" y1="17" x2="13" y2="17" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>',
143
+ 'clipboard-document':
144
+ '<path d="M16 4h2a2 2 0 012 2v14a2 2 0 01-2 2H6a2 2 0 01-2-2V6a2 2 0 012-2h2" stroke="currentColor" stroke-width="2" stroke-linecap="round" fill="none"/><rect x="8" y="2" width="8" height="4" rx="1" stroke="currentColor" stroke-width="2" fill="none"/><line x1="9" y1="12" x2="15" y2="12" stroke="currentColor" stroke-width="2" stroke-linecap="round"/><line x1="9" y1="16" x2="13" y2="16" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>',
145
+ 'arrow-path':
146
+ '<path d="M21.5 2v6h-6M21.34 15.57a10 10 0 11-.57-8.38l5.67-5.19" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="none"/>',
147
+ 'code-bracket':
148
+ '<polyline points="16 18 22 12 16 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="none"/><polyline points="8 6 2 12 8 18" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="none"/>',
149
+ 'squares-2x2':
150
+ '<rect x="3" y="3" width="7" height="7" rx="1" stroke="currentColor" stroke-width="2" fill="none"/><rect x="14" y="3" width="7" height="7" rx="1" stroke="currentColor" stroke-width="2" fill="none"/><rect x="14" y="14" width="7" height="7" rx="1" stroke="currentColor" stroke-width="2" fill="none"/><rect x="3" y="14" width="7" height="7" rx="1" stroke="currentColor" stroke-width="2" fill="none"/>',
151
+ 'squares-plus':
152
+ '<rect x="3" y="3" width="7" height="7" rx="1" stroke="currentColor" stroke-width="2" fill="none"/><rect x="14" y="3" width="7" height="7" rx="1" stroke="currentColor" stroke-width="2" fill="none"/><rect x="3" y="14" width="7" height="7" rx="1" stroke="currentColor" stroke-width="2" fill="none"/><line x1="17.5" y1="14" x2="17.5" y2="21" stroke="currentColor" stroke-width="2" stroke-linecap="round"/><line x1="14" y1="17.5" x2="21" y2="17.5" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>',
153
+ 'arrow-top-right-on-square':
154
+ '<path d="M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6M15 3h6v6M10 14L21 3" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="none"/>',
155
+ sidebar:
156
+ '<rect x="3" y="3" width="18" height="18" rx="2" stroke="currentColor" stroke-width="2" fill="none"/><line x1="9" y1="3" x2="9" y2="21" stroke="currentColor" stroke-width="2"/>',
157
+ 'layout-top':
158
+ '<rect x="3" y="3" width="18" height="18" rx="2" stroke="currentColor" stroke-width="2" fill="none"/><line x1="3" y1="9" x2="21" y2="9" stroke="currentColor" stroke-width="2"/>',
159
+ 'panel-right':
160
+ '<rect x="3" y="3" width="18" height="18" rx="2" stroke="currentColor" stroke-width="2" fill="none"/><line x1="15" y1="3" x2="15" y2="21" stroke="currentColor" stroke-width="2"/>'
130
161
  };
131
162
  </script>
132
163
 
@@ -3,6 +3,7 @@
3
3
  import Icon from '../elements/Icon.svelte';
4
4
 
5
5
  interface Props {
6
+ id?: string;
6
7
  checked?: boolean;
7
8
  label?: string;
8
9
  description?: string;
@@ -17,6 +18,7 @@
17
18
  }
18
19
 
19
20
  let {
21
+ id,
20
22
  checked = $bindable(false),
21
23
  label,
22
24
  description,
@@ -38,6 +40,7 @@
38
40
  >
39
41
  <div class="flex h-5 items-center">
40
42
  <input
43
+ id={id || name}
41
44
  type="checkbox"
42
45
  bind:checked
43
46
  aria-label={ariaLabelAttr || ariaLabel || label}
@@ -1,5 +1,6 @@
1
1
  import type { Snippet } from 'svelte';
2
2
  interface Props {
3
+ id?: string;
3
4
  checked?: boolean;
4
5
  label?: string;
5
6
  description?: string;
@@ -3,6 +3,11 @@
3
3
  import Icon from '../elements/Icon.svelte';
4
4
 
5
5
  interface Props {
6
+ id?: string;
7
+ name?: string;
8
+ label?: string;
9
+ 'aria-label'?: string;
10
+ ariaLabel?: string;
6
11
  value?: string;
7
12
  presets?: string[];
8
13
  disabled?: boolean;
@@ -23,6 +28,11 @@
23
28
  ];
24
29
 
25
30
  let {
31
+ id,
32
+ name,
33
+ label,
34
+ 'aria-label': ariaLabelAttr,
35
+ ariaLabel,
26
36
  value = $bindable('#ff3e00'),
27
37
  presets = defaultPresets,
28
38
  disabled = false,
@@ -62,18 +72,23 @@
62
72
  style="background-color: {value};"
63
73
  >
64
74
  <input
75
+ id={id ? `${id}-color` : name ? `${name}-color` : undefined}
76
+ name={name ? `${name}-color` : 'colorPickerValue'}
65
77
  type="color"
66
78
  bind:value
67
79
  {disabled}
68
80
  class="absolute inset-0 h-full w-full cursor-pointer opacity-0"
69
- aria-label="Pick color"
81
+ aria-label={ariaLabelAttr || ariaLabel || label || 'Pick color'}
70
82
  />
71
83
  </label>
72
84
  <input
85
+ id={id ? `${id}-hex` : name ? `${name}-hex` : undefined}
86
+ name={name || 'colorHex'}
73
87
  type="text"
74
88
  bind:value
75
89
  {disabled}
76
90
  placeholder="#ff3e00"
91
+ aria-label="Hex color code"
77
92
  class="h-8 w-full rounded-md border border-neutral-200 bg-neutral-50 px-2.5 font-mono text-sm text-neutral-800 focus:border-primary-500 focus:outline-none dark:border-neutral-800 dark:bg-neutral-950 dark:text-neutral-200"
78
93
  />
79
94
  </div>
@@ -124,16 +139,21 @@
124
139
  style="background-color: {value};"
125
140
  >
126
141
  <input
142
+ id={id ? `${id}-popover-color` : name ? `${name}-popover-color` : undefined}
143
+ name={name ? `${name}-popover-color` : 'colorPickerValue'}
127
144
  type="color"
128
145
  bind:value
129
146
  class="absolute inset-0 h-full w-full cursor-pointer opacity-0"
130
- aria-label="Pick color"
147
+ aria-label={ariaLabelAttr || ariaLabel || label || 'Pick color'}
131
148
  />
132
149
  </label>
133
150
  <input
151
+ id={id ? `${id}-popover-hex` : name ? `${name}-popover-hex` : undefined}
152
+ name={name || 'colorHex'}
134
153
  type="text"
135
154
  bind:value
136
155
  placeholder="#ff3e00"
156
+ aria-label="Hex color code"
137
157
  class="h-8 w-full rounded-md border border-neutral-200 bg-neutral-50 px-2.5 font-mono text-sm text-neutral-800 focus:border-primary-500 focus:outline-none dark:border-neutral-800 dark:bg-neutral-950 dark:text-neutral-200"
138
158
  />
139
159
  </div>
@@ -1,4 +1,9 @@
1
1
  interface Props {
2
+ id?: string;
3
+ name?: string;
4
+ label?: string;
5
+ 'aria-label'?: string;
6
+ ariaLabel?: string;
2
7
  value?: string;
3
8
  presets?: string[];
4
9
  disabled?: boolean;
@@ -180,9 +180,12 @@
180
180
  >
181
181
  <Icon name="search" size="xs" class="mr-2 shrink-0 text-neutral-400" />
182
182
  <input
183
+ id={name ? `${name}-search` : undefined}
184
+ name={name ? `${name}-search` : 'comboboxSearch'}
183
185
  type="text"
184
186
  bind:value={query}
185
187
  placeholder={searchPlaceholder}
188
+ aria-label={searchPlaceholder || 'Search options'}
186
189
  onkeydown={handleKeydown}
187
190
  class="h-8 w-full bg-transparent text-xs text-neutral-900 placeholder:text-neutral-400 focus:outline-none dark:text-white"
188
191
  />
@@ -1,5 +1,9 @@
1
1
  <script module lang="ts">
2
2
  export interface DropzoneProps {
3
+ id?: string;
4
+ name?: string;
5
+ 'aria-label'?: string;
6
+ ariaLabel?: string;
3
7
  accept?: string;
4
8
  multiple?: boolean;
5
9
  maxSize?: number; // in bytes
@@ -23,6 +27,10 @@
23
27
  import { useUpload } from '../../composables/useUpload.svelte';
24
28
 
25
29
  let {
30
+ id,
31
+ name = 'dropzoneFiles',
32
+ 'aria-label': ariaLabelAttr,
33
+ ariaLabel,
26
34
  accept = '*/*',
27
35
  multiple = false,
28
36
  maxSize = 10 * 1024 * 1024, // 10MB default
@@ -166,11 +174,14 @@
166
174
  aria-disabled={disabled}
167
175
  >
168
176
  <input
177
+ id={id || name || 'dropzone-file-input'}
178
+ name={name || 'dropzoneFiles'}
169
179
  bind:this={fileInputEl}
170
180
  type="file"
171
181
  {accept}
172
182
  {multiple}
173
183
  {disabled}
184
+ aria-label={ariaLabelAttr || ariaLabel || label || 'Upload files'}
174
185
  class="hidden"
175
186
  onchange={handleInputChange}
176
187
  />
@@ -1,4 +1,8 @@
1
1
  export interface DropzoneProps {
2
+ id?: string;
3
+ name?: string;
4
+ 'aria-label'?: string;
5
+ ariaLabel?: string;
2
6
  accept?: string;
3
7
  multiple?: boolean;
4
8
  maxSize?: number;
@@ -2,6 +2,7 @@
2
2
  import type { Snippet } from 'svelte';
3
3
 
4
4
  interface Props {
5
+ id?: string;
5
6
  label?: string;
6
7
  description?: string;
7
8
  error?: string;
@@ -13,6 +14,7 @@
13
14
  }
14
15
 
15
16
  let {
17
+ id,
16
18
  label,
17
19
  description,
18
20
  error,
@@ -28,7 +30,10 @@
28
30
  {#if label || hint}
29
31
  <div class="flex items-center justify-between">
30
32
  {#if label}
31
- <label for={name} class="block text-sm font-medium text-neutral-700 dark:text-neutral-300">
33
+ <label
34
+ for={id || name}
35
+ class="block text-sm font-medium text-neutral-700 dark:text-neutral-300"
36
+ >
32
37
  {label}
33
38
  {#if required}
34
39
  <span class="ml-0.5 text-rose-500">*</span>
@@ -1,5 +1,6 @@
1
1
  import type { Snippet } from 'svelte';
2
2
  interface Props {
3
+ id?: string;
3
4
  label?: string;
4
5
  description?: string;
5
6
  error?: string;
@@ -2,6 +2,8 @@
2
2
  import { cn } from '../../utils/cn';
3
3
 
4
4
  interface Props {
5
+ id?: string;
6
+ name?: string;
5
7
  length?: number;
6
8
  value?: string;
7
9
  type?: 'number' | 'text' | 'password';
@@ -17,6 +19,8 @@
17
19
  }
18
20
 
19
21
  let {
22
+ id,
23
+ name = 'otp',
20
24
  length = 6,
21
25
  value = $bindable(''),
22
26
  type = 'number',
@@ -139,6 +143,8 @@
139
143
  {/if}
140
144
 
141
145
  <input
146
+ id={`${id || name}-${index + 1}`}
147
+ name={`${name}-${index + 1}`}
142
148
  bind:this={inputRefs[index]}
143
149
  type={type === 'password' ? 'password' : 'text'}
144
150
  inputmode={type === 'number' ? 'numeric' : 'text'}
@@ -1,4 +1,6 @@
1
1
  interface Props {
2
+ id?: string;
3
+ name?: string;
2
4
  length?: number;
3
5
  value?: string;
4
6
  type?: 'number' | 'text' | 'password';
@@ -180,9 +180,12 @@
180
180
  >
181
181
  <Icon name="search" size="xs" class="mr-2 shrink-0 text-neutral-400" />
182
182
  <input
183
+ id={name ? `${name}-search` : undefined}
184
+ name={name ? `${name}-search` : 'multiselectSearch'}
183
185
  type="text"
184
186
  bind:value={searchQuery}
185
187
  placeholder={searchPlaceholder}
188
+ aria-label={searchPlaceholder || 'Search options'}
186
189
  onkeydown={handleKeyDown}
187
190
  class="h-8 w-full bg-transparent text-xs text-neutral-900 placeholder:text-neutral-400 focus:outline-none dark:text-white"
188
191
  />
@@ -23,6 +23,7 @@
23
23
  });
24
24
 
25
25
  export type NumberInputProps = VariantProps<typeof numberInputVariants> & {
26
+ id?: string;
26
27
  value?: number;
27
28
  min?: number;
28
29
  max?: number;
@@ -32,6 +33,9 @@
32
33
  placeholder?: string;
33
34
  disabled?: boolean;
34
35
  name?: string;
36
+ label?: string;
37
+ 'aria-label'?: string;
38
+ ariaLabel?: string;
35
39
  class?: string;
36
40
  };
37
41
  </script>
@@ -40,6 +44,7 @@
40
44
  import Icon from '../elements/Icon.svelte';
41
45
 
42
46
  let {
47
+ id,
43
48
  value = $bindable(0),
44
49
  min,
45
50
  max,
@@ -49,6 +54,9 @@
49
54
  placeholder,
50
55
  disabled = false,
51
56
  name,
57
+ label,
58
+ 'aria-label': ariaLabelAttr,
59
+ ariaLabel,
52
60
  size = 'md',
53
61
  status = 'default',
54
62
  class: className = ''
@@ -110,14 +118,16 @@
110
118
  {/if}
111
119
 
112
120
  <input
121
+ id={id || name}
113
122
  type="number"
114
- {name}
123
+ name={name || id || 'number-input'}
115
124
  {min}
116
125
  {max}
117
126
  {step}
118
127
  {disabled}
119
128
  {placeholder}
120
129
  {value}
130
+ aria-label={ariaLabelAttr || ariaLabel || label || placeholder || name || 'Number input'}
121
131
  oninput={handleInput}
122
132
  onkeydown={handleKeydown}
123
133
  class="w-full [appearance:textfield] bg-transparent text-center font-mono focus:outline-none [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none"
@@ -31,6 +31,7 @@ export declare const numberInputVariants: import("tailwind-variants").TVReturnTy
31
31
  };
32
32
  }, undefined>>;
33
33
  export type NumberInputProps = VariantProps<typeof numberInputVariants> & {
34
+ id?: string;
34
35
  value?: number;
35
36
  min?: number;
36
37
  max?: number;
@@ -40,6 +41,9 @@ export type NumberInputProps = VariantProps<typeof numberInputVariants> & {
40
41
  placeholder?: string;
41
42
  disabled?: boolean;
42
43
  name?: string;
44
+ label?: string;
45
+ 'aria-label'?: string;
46
+ ariaLabel?: string;
43
47
  class?: string;
44
48
  };
45
49
  declare const NumberInput: import("svelte").Component<NumberInputProps, {}, "value">;
@@ -45,11 +45,13 @@
45
45
  : ''}"
46
46
  >
47
47
  <input
48
+ id={`${name}-${opt.value}`}
48
49
  type="radio"
49
50
  {name}
50
51
  value={opt.value}
51
52
  disabled={opt.disabled}
52
53
  checked={isSelected}
54
+ aria-label={opt.label}
53
55
  onchange={() => (value = opt.value)}
54
56
  class="sr-only"
55
57
  />
@@ -31,13 +31,18 @@
31
31
  });
32
32
 
33
33
  export type SelectProps = VariantProps<typeof selectVariants> & {
34
+ id?: string;
34
35
  value?: string;
35
36
  options?: SelectOption[];
36
37
  placeholder?: string;
37
38
  icon?: IconSource;
38
39
  disabled?: boolean;
39
40
  name?: string;
41
+ label?: string;
42
+ 'aria-label'?: string;
43
+ ariaLabel?: string;
40
44
  class?: string;
45
+ [key: string]: any;
41
46
  };
42
47
  </script>
43
48
 
@@ -45,15 +50,20 @@
45
50
  import Icon from '../elements/Icon.svelte';
46
51
 
47
52
  let {
53
+ id,
48
54
  value = $bindable(''),
49
55
  options = [],
50
56
  placeholder = 'Select an option...',
51
57
  icon,
52
58
  disabled = false,
53
59
  name,
60
+ label,
61
+ 'aria-label': ariaLabelAttr,
62
+ ariaLabel,
54
63
  size = 'md',
55
64
  status = 'default',
56
- class: className = ''
65
+ class: className = '',
66
+ ...restProps
57
67
  }: SelectProps = $props();
58
68
  </script>
59
69
 
@@ -65,14 +75,17 @@
65
75
  {/if}
66
76
 
67
77
  <select
78
+ id={id || name}
68
79
  bind:value
69
80
  {disabled}
70
81
  {name}
82
+ aria-label={ariaLabelAttr || ariaLabel || label || placeholder || name || 'Select option'}
71
83
  class="{selectVariants({ size, status, class: className })} {icon
72
84
  ? size === 'sm'
73
85
  ? 'pl-8'
74
86
  : 'pl-10'
75
87
  : ''}"
88
+ {...restProps}
76
89
  >
77
90
  {#if placeholder}
78
91
  <option value="" disabled selected={!value}>
@@ -38,13 +38,18 @@ export declare const selectVariants: import("tailwind-variants").TVReturnType<{
38
38
  };
39
39
  }, undefined>>;
40
40
  export type SelectProps = VariantProps<typeof selectVariants> & {
41
+ id?: string;
41
42
  value?: string;
42
43
  options?: SelectOption[];
43
44
  placeholder?: string;
44
45
  icon?: IconSource;
45
46
  disabled?: boolean;
46
47
  name?: string;
48
+ label?: string;
49
+ 'aria-label'?: string;
50
+ ariaLabel?: string;
47
51
  class?: string;
52
+ [key: string]: any;
48
53
  };
49
54
  declare const Select: import("svelte").Component<SelectProps, {}, "value">;
50
55
  type Select = ReturnType<typeof Select>;
@@ -1,21 +1,29 @@
1
1
  <script lang="ts">
2
2
  interface Props {
3
+ id?: string;
3
4
  value?: number;
4
5
  min?: number;
5
6
  max?: number;
6
7
  step?: number;
7
8
  disabled?: boolean;
8
9
  name?: string;
10
+ label?: string;
11
+ 'aria-label'?: string;
12
+ ariaLabel?: string;
9
13
  class?: string;
10
14
  }
11
15
 
12
16
  let {
17
+ id,
13
18
  value = $bindable(50),
14
19
  min = 0,
15
20
  max = 100,
16
21
  step = 1,
17
22
  disabled = false,
18
- name,
23
+ name = 'slider',
24
+ label,
25
+ 'aria-label': ariaLabelAttr,
26
+ ariaLabel,
19
27
  class: className = ''
20
28
  }: Props = $props();
21
29
 
@@ -24,6 +32,7 @@
24
32
 
25
33
  <div class="relative flex w-full touch-none items-center select-none {className}">
26
34
  <input
35
+ id={id || name}
27
36
  type="range"
28
37
  bind:value
29
38
  {min}
@@ -31,6 +40,10 @@
31
40
  {step}
32
41
  {disabled}
33
42
  {name}
43
+ aria-label={ariaLabelAttr || ariaLabel || label || 'Slider'}
44
+ aria-valuenow={value}
45
+ aria-valuemin={min}
46
+ aria-valuemax={max}
34
47
  class="h-2 w-full cursor-pointer appearance-none rounded-lg bg-neutral-200 accent-primary-600 disabled:cursor-not-allowed disabled:opacity-50 dark:bg-neutral-800"
35
48
  style="background: linear-gradient(to right, rgb(37 99 235) {percentage}%, rgb(228 228 231) {percentage}%);"
36
49
  />
@@ -1,10 +1,14 @@
1
1
  interface Props {
2
+ id?: string;
2
3
  value?: number;
3
4
  min?: number;
4
5
  max?: number;
5
6
  step?: number;
6
7
  disabled?: boolean;
7
8
  name?: string;
9
+ label?: string;
10
+ 'aria-label'?: string;
11
+ ariaLabel?: string;
8
12
  class?: string;
9
13
  }
10
14
  declare const Slider: import("svelte").Component<Props, {}, "value">;