svelte-comp 1.3.3 → 1.3.6

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 (138) hide show
  1. package/LICENSE.md +21 -21
  2. package/README.md +101 -100
  3. package/dist/App.svelte +507 -507
  4. package/dist/Container.svelte +59 -59
  5. package/dist/app.css +234 -235
  6. package/dist/app.d.ts +10 -0
  7. package/dist/lib/Accordion.svelte +155 -155
  8. package/dist/lib/Badge.svelte +44 -44
  9. package/dist/lib/Button.svelte +185 -170
  10. package/dist/lib/Calendar.svelte +384 -384
  11. package/dist/lib/Card.svelte +103 -103
  12. package/dist/lib/Carousel.svelte +293 -293
  13. package/dist/lib/Carousel.svelte.d.ts +1 -1
  14. package/dist/lib/CheckBox.svelte +210 -210
  15. package/dist/lib/CodeView.svelte +308 -307
  16. package/dist/lib/ColorPicker.svelte +159 -159
  17. package/dist/lib/ContextMenu.svelte +328 -322
  18. package/dist/lib/DatePicker.svelte +246 -246
  19. package/dist/lib/Dialog.svelte +233 -233
  20. package/dist/lib/Field.svelte +299 -299
  21. package/dist/lib/FilePicker.svelte +295 -240
  22. package/dist/lib/FilePicker.svelte.d.ts +6 -1
  23. package/dist/lib/Form.svelte +438 -438
  24. package/dist/lib/Hamburger.svelte +217 -217
  25. package/dist/lib/InstallPWA.svelte +94 -94
  26. package/dist/lib/Menu.svelte +623 -623
  27. package/dist/lib/NoticeBase.svelte +140 -140
  28. package/dist/lib/PaginatedCard.svelte +73 -73
  29. package/dist/lib/Pagination.svelte +119 -119
  30. package/dist/lib/PrimaryColorSelect.svelte +111 -111
  31. package/dist/lib/ProgressBar.svelte +141 -141
  32. package/dist/lib/ProgressCircle.svelte +190 -190
  33. package/dist/lib/Radio.svelte +189 -189
  34. package/dist/lib/SearchInput.svelte +104 -104
  35. package/dist/lib/Select.svelte +524 -524
  36. package/dist/lib/Slider.svelte +253 -253
  37. package/dist/lib/Splitter.svelte +159 -150
  38. package/dist/lib/Switch.svelte +168 -167
  39. package/dist/lib/Table.svelte +299 -299
  40. package/dist/lib/Tabs.svelte +213 -213
  41. package/dist/lib/ThemeToggle.svelte +128 -127
  42. package/dist/lib/TimePicker.svelte +312 -312
  43. package/dist/lib/TimePickerNew.svelte +634 -0
  44. package/dist/lib/TimePickerNew.svelte.d.ts +49 -0
  45. package/dist/lib/Toast.svelte +123 -123
  46. package/dist/lib/Tooltip.svelte +110 -110
  47. package/dist/lib/Topbar.svelte +107 -107
  48. package/dist/lib/__tests__/Accordion.test.d.ts +1 -0
  49. package/dist/lib/__tests__/Accordion.test.js +171 -0
  50. package/dist/lib/__tests__/Badge.test.d.ts +1 -0
  51. package/dist/lib/__tests__/Badge.test.js +41 -0
  52. package/dist/lib/__tests__/Button.test.d.ts +1 -0
  53. package/dist/lib/__tests__/Button.test.js +269 -0
  54. package/dist/lib/__tests__/Calendar.test.d.ts +1 -0
  55. package/dist/lib/__tests__/Calendar.test.js +171 -0
  56. package/dist/lib/__tests__/Card.test.d.ts +1 -0
  57. package/dist/lib/__tests__/Card.test.js +148 -0
  58. package/dist/lib/__tests__/Carousel.test.d.ts +1 -0
  59. package/dist/lib/__tests__/Carousel.test.js +439 -0
  60. package/dist/lib/__tests__/CheckBox.test.d.ts +1 -0
  61. package/dist/lib/__tests__/CheckBox.test.js +152 -0
  62. package/dist/lib/__tests__/CodeView.test.d.ts +1 -0
  63. package/dist/lib/__tests__/CodeView.test.js +157 -0
  64. package/dist/lib/__tests__/ColorPicker.test.d.ts +1 -0
  65. package/dist/lib/__tests__/ColorPicker.test.js +93 -0
  66. package/dist/lib/__tests__/ContextMenu.test.d.ts +1 -0
  67. package/dist/lib/__tests__/ContextMenu.test.js +67 -0
  68. package/dist/lib/__tests__/DatePicker.test.d.ts +1 -0
  69. package/dist/lib/__tests__/DatePicker.test.js +108 -0
  70. package/dist/lib/__tests__/Dialog.test.d.ts +1 -0
  71. package/dist/lib/__tests__/Dialog.test.js +183 -0
  72. package/dist/lib/__tests__/Field.test.d.ts +1 -0
  73. package/dist/lib/__tests__/Field.test.js +190 -0
  74. package/dist/lib/__tests__/FilePicker.test.d.ts +1 -0
  75. package/dist/lib/__tests__/FilePicker.test.js +179 -0
  76. package/dist/lib/__tests__/Form.integration.test.d.ts +1 -0
  77. package/dist/lib/__tests__/Form.integration.test.js +158 -0
  78. package/dist/lib/__tests__/Form.test.d.ts +1 -0
  79. package/dist/lib/__tests__/Form.test.js +463 -0
  80. package/dist/lib/__tests__/Hamburger.test.d.ts +1 -0
  81. package/dist/lib/__tests__/Hamburger.test.js +161 -0
  82. package/dist/lib/__tests__/InstallPWA.test.d.ts +1 -0
  83. package/dist/lib/__tests__/InstallPWA.test.js +15 -0
  84. package/dist/lib/__tests__/Menu.test.d.ts +1 -0
  85. package/dist/lib/__tests__/Menu.test.js +285 -0
  86. package/dist/lib/__tests__/NoticeBase.test.d.ts +1 -0
  87. package/dist/lib/__tests__/NoticeBase.test.js +60 -0
  88. package/dist/lib/__tests__/PaginatedCard.test.d.ts +1 -0
  89. package/dist/lib/__tests__/PaginatedCard.test.js +89 -0
  90. package/dist/lib/__tests__/Pagination.test.d.ts +1 -0
  91. package/dist/lib/__tests__/Pagination.test.js +168 -0
  92. package/dist/lib/__tests__/PrimaryColorSelect.test.d.ts +1 -0
  93. package/dist/lib/__tests__/PrimaryColorSelect.test.js +92 -0
  94. package/dist/lib/__tests__/ProgressBar.test.d.ts +1 -0
  95. package/dist/lib/__tests__/ProgressBar.test.js +69 -0
  96. package/dist/lib/__tests__/ProgressCircle.test.d.ts +1 -0
  97. package/dist/lib/__tests__/ProgressCircle.test.js +71 -0
  98. package/dist/lib/__tests__/Radio.test.d.ts +1 -0
  99. package/dist/lib/__tests__/Radio.test.js +127 -0
  100. package/dist/lib/__tests__/SearchInput.test.d.ts +1 -0
  101. package/dist/lib/__tests__/SearchInput.test.js +80 -0
  102. package/dist/lib/__tests__/Select.test.d.ts +1 -0
  103. package/dist/lib/__tests__/Select.test.js +408 -0
  104. package/dist/lib/__tests__/Slider.test.d.ts +1 -0
  105. package/dist/lib/__tests__/Slider.test.js +213 -0
  106. package/dist/lib/__tests__/Splitter.test.d.ts +1 -0
  107. package/dist/lib/__tests__/Splitter.test.js +87 -0
  108. package/dist/lib/__tests__/Switch.test.d.ts +1 -0
  109. package/dist/lib/__tests__/Switch.test.js +97 -0
  110. package/dist/lib/__tests__/Table.test.d.ts +1 -0
  111. package/dist/lib/__tests__/Table.test.js +349 -0
  112. package/dist/lib/__tests__/Tabs.test.d.ts +1 -0
  113. package/dist/lib/__tests__/Tabs.test.js +262 -0
  114. package/dist/lib/__tests__/ThemeToggle.test.d.ts +1 -0
  115. package/dist/lib/__tests__/ThemeToggle.test.js +84 -0
  116. package/dist/lib/__tests__/TimePicker.test.d.ts +1 -0
  117. package/dist/lib/__tests__/TimePicker.test.js +146 -0
  118. package/dist/lib/__tests__/TimePickerNew.test.d.ts +1 -0
  119. package/dist/lib/__tests__/TimePickerNew.test.js +322 -0
  120. package/dist/lib/__tests__/Toast.test.d.ts +1 -0
  121. package/dist/lib/__tests__/Toast.test.js +135 -0
  122. package/dist/lib/__tests__/Tooltip.test.d.ts +1 -0
  123. package/dist/lib/__tests__/Tooltip.test.js +171 -0
  124. package/dist/lib/__tests__/Topbar.test.d.ts +1 -0
  125. package/dist/lib/__tests__/Topbar.test.js +25 -0
  126. package/dist/lib/__tests__/setupLangContext.d.ts +1 -0
  127. package/dist/lib/__tests__/setupLangContext.js +65 -0
  128. package/dist/lib/__tests__/storage.test.d.ts +1 -0
  129. package/dist/lib/__tests__/storage.test.js +124 -0
  130. package/dist/lib/__tests__/utils.test.d.ts +1 -0
  131. package/dist/lib/__tests__/utils.test.js +11 -0
  132. package/dist/lib/index.d.ts +1 -0
  133. package/dist/lib/index.js +1 -0
  134. package/dist/lib/lang.d.ts +4 -0
  135. package/dist/lib/lang.js +4 -0
  136. package/dist/styles.css +234 -232
  137. package/dist/utils/index.js +15 -4
  138. package/package.json +52 -52
@@ -1,189 +1,189 @@
1
- <!-- src/lib/Radio.svelte -->
2
- <script lang="ts">
3
- /**
4
- * @component Radio
5
- * @description Single choice input with optional label, custom sizing and theme variants.
6
- *
7
- * @prop label {string} - Text label shown next to the radio
8
- *
9
- * @prop children {Snippet} - Custom label content
10
- *
11
- * @prop sz {SizeKey} - Size option
12
- * @options xs|sm|md|lg|xl
13
- * @default md
14
- *
15
- * @prop variant {ComponentVariant} - Visual style preset
16
- * @options default|neutral
17
- * @default default
18
- *
19
- * @prop checked {boolean} - Controlled checked state
20
- * @default false
21
- *
22
- * @prop group {unknown} - Native radio group binding (`bind:group`)
23
- *
24
- * @prop onChange {(checked: boolean) => void} - Fired when the radio toggles
25
- *
26
- * @prop class {string} - Extra classes applied to the root container
27
- * @default ""
28
- *
29
- * @prop describedBy {string} - ID of helper or error text for accessibility
30
- *
31
- * @prop value {string} - Radio value
32
- * @default "on"
33
- *
34
- * @note Fully supports native radio grouping through `bind:group`
35
- * @note Works as a controlled component when `checked` and `onChange` are used together
36
- * @note `children` takes priority over `label`
37
- * @note Size and variant affect circle size, dot size and colors
38
- * @note Hidden native input is focusable and exposes full accessibility attributes
39
- * @note Uses data-state attributes to enable custom styling
40
- */
41
- import type { Snippet } from "svelte";
42
- import type { HTMLInputAttributes } from "svelte/elements";
43
- import { type SizeKey, type ComponentVariant, TEXT } from "./types";
44
- import { cx, uid } from "../utils";
45
-
46
- type Props = HTMLInputAttributes & {
47
- label?: string;
48
- children?: Snippet;
49
- sz?: SizeKey;
50
- variant?: ComponentVariant;
51
- checked?: boolean;
52
- group?: unknown;
53
- onChange?: (checked: boolean) => void;
54
- class?: string;
55
- describedBy?: string;
56
- };
57
-
58
- let {
59
- label,
60
- children,
61
- sz = "md",
62
- variant = "default",
63
- checked = $bindable(false),
64
- group = $bindable<unknown>(undefined),
65
- onChange,
66
- class: externalClass = "",
67
- describedBy,
68
- value = "on",
69
- ...rest
70
- }: Props = $props();
71
-
72
- const inputId = $derived(rest.id ?? uid("rd-"));
73
-
74
- const sizeClasses = {
75
- xs: "w-3 h-3",
76
- sm: "w-3.5 h-3.5",
77
- md: "w-4 h-4",
78
- lg: "w-[18px] h-[18px]",
79
- xl: "w-5 h-5",
80
- } as const;
81
-
82
- const dotSizes: Record<SizeKey, string> = {
83
- xs: "w-1.5 h-1.5",
84
- sm: "w-2 h-2",
85
- md: "w-2.5 h-2.5",
86
- lg: "w-3 h-3",
87
- xl: "w-3.5 h-3.5",
88
- };
89
-
90
- const gapBySize: Record<SizeKey, string> = {
91
- xs: "gap-1.5",
92
- sm: "gap-2",
93
- md: "gap-2.5",
94
- lg: "gap-3",
95
- xl: "gap-3.5",
96
- };
97
-
98
- const variants = $derived(
99
- variant === "neutral"
100
- ? {
101
- checked: "bg-transparent border-[var(--border-color-strong)]",
102
- unchecked: "bg-transparent border-[var(--border-color-default)]",
103
- }
104
- : {
105
- checked: "bg-transparent border-[var(--color-bg-primary)]",
106
- unchecked: "bg-transparent border-[var(--border-color-default)]",
107
- }
108
- );
109
-
110
- const baseCircle =
111
- "rounded-full border border-solid cursor-pointer transition-all duration-[var(--transition-fast)] ease-[var(--timing-default)] flex items-center justify-center";
112
-
113
- const focusFromPeer =
114
- "peer-focus-visible:ring-2 peer-focus-visible:ring-[var(--border-color-focus)] peer-focus-visible:border-[var(--border-color-focus)]";
115
-
116
- const dotColor = $derived(
117
- variant === "neutral"
118
- ? "bg-[var(--border-color-strong)]"
119
- : "bg-[var(--color-bg-primary)]"
120
- );
121
-
122
- const isUsingGroup = $derived(typeof group !== "undefined");
123
- const isChecked = $derived(isUsingGroup ? group === value : checked);
124
- const state = $derived(isChecked ? "checked" : "unchecked");
125
-
126
- const rootClass = $derived(
127
- cx(
128
- "inline-flex items-center cursor-pointer select-none",
129
- gapBySize[sz],
130
- externalClass
131
- )
132
- );
133
-
134
- const radioClass = $derived(
135
- cx(
136
- baseCircle,
137
- focusFromPeer,
138
- sizeClasses[sz],
139
- isChecked && variants.checked,
140
- !isChecked && variants.unchecked,
141
- "peer-disabled:opacity-[var(--opacity-disabled)] peer-disabled:cursor-not-allowed"
142
- )
143
- );
144
-
145
- const dotClass = $derived(
146
- cx(
147
- "rounded-full transition-transform duration-[var(--transition-fast)] ease-[var(--timing-default)]",
148
- dotSizes[sz],
149
- dotColor,
150
- isChecked ? "scale-100 opacity-100" : "scale-50 opacity-0"
151
- )
152
- );
153
-
154
- const labelClass = $derived(
155
- cx(
156
- TEXT[sz],
157
- "[color:var(--color-text-muted)] font-medium peer-disabled:cursor-not-allowed"
158
- )
159
- );
160
- </script>
161
-
162
- <label class={rootClass} for={inputId}>
163
- <input
164
- id={inputId}
165
- type="radio"
166
- {value}
167
- bind:group
168
- checked={isChecked}
169
- {...rest}
170
- class="sr-only peer"
171
- aria-checked={isChecked}
172
- aria-describedby={describedBy}
173
- onchange={(event) => {
174
- const next = (event.currentTarget as HTMLInputElement).checked;
175
- checked = next;
176
- onChange?.(next);
177
- }}
178
- />
179
-
180
- <span data-state={state} class={radioClass} aria-hidden="true">
181
- <span class={dotClass}></span>
182
- </span>
183
-
184
- {#if children}
185
- <span class={labelClass}>{@render children?.()}</span>
186
- {:else if label}
187
- <span class={labelClass}>{label}</span>
188
- {/if}
189
- </label>
1
+ <!-- src/lib/Radio.svelte -->
2
+ <script lang="ts">
3
+ /**
4
+ * @component Radio
5
+ * @description Single choice input with optional label, custom sizing and theme variants.
6
+ *
7
+ * @prop label {string} - Text label shown next to the radio
8
+ *
9
+ * @prop children {Snippet} - Custom label content
10
+ *
11
+ * @prop sz {SizeKey} - Size option
12
+ * @options xs|sm|md|lg|xl
13
+ * @default md
14
+ *
15
+ * @prop variant {ComponentVariant} - Visual style preset
16
+ * @options default|neutral
17
+ * @default default
18
+ *
19
+ * @prop checked {boolean} - Controlled checked state
20
+ * @default false
21
+ *
22
+ * @prop group {unknown} - Native radio group binding (`bind:group`)
23
+ *
24
+ * @prop onChange {(checked: boolean) => void} - Fired when the radio toggles
25
+ *
26
+ * @prop class {string} - Extra classes applied to the root container
27
+ * @default ""
28
+ *
29
+ * @prop describedBy {string} - ID of helper or error text for accessibility
30
+ *
31
+ * @prop value {string} - Radio value
32
+ * @default "on"
33
+ *
34
+ * @note Fully supports native radio grouping through `bind:group`
35
+ * @note Works as a controlled component when `checked` and `onChange` are used together
36
+ * @note `children` takes priority over `label`
37
+ * @note Size and variant affect circle size, dot size and colors
38
+ * @note Hidden native input is focusable and exposes full accessibility attributes
39
+ * @note Uses data-state attributes to enable custom styling
40
+ */
41
+ import type { Snippet } from "svelte";
42
+ import type { HTMLInputAttributes } from "svelte/elements";
43
+ import { type SizeKey, type ComponentVariant, TEXT } from "./types";
44
+ import { cx, uid } from "../utils";
45
+
46
+ type Props = HTMLInputAttributes & {
47
+ label?: string;
48
+ children?: Snippet;
49
+ sz?: SizeKey;
50
+ variant?: ComponentVariant;
51
+ checked?: boolean;
52
+ group?: unknown;
53
+ onChange?: (checked: boolean) => void;
54
+ class?: string;
55
+ describedBy?: string;
56
+ };
57
+
58
+ let {
59
+ label,
60
+ children,
61
+ sz = "md",
62
+ variant = "default",
63
+ checked = $bindable(false),
64
+ group = $bindable<unknown>(undefined),
65
+ onChange,
66
+ class: externalClass = "",
67
+ describedBy,
68
+ value = "on",
69
+ ...rest
70
+ }: Props = $props();
71
+
72
+ const inputId = $derived(rest.id ?? uid("rd-"));
73
+
74
+ const sizeClasses = {
75
+ xs: "w-3 h-3",
76
+ sm: "w-3.5 h-3.5",
77
+ md: "w-4 h-4",
78
+ lg: "w-[18px] h-[18px]",
79
+ xl: "w-5 h-5",
80
+ } as const;
81
+
82
+ const dotSizes: Record<SizeKey, string> = {
83
+ xs: "w-1.5 h-1.5",
84
+ sm: "w-2 h-2",
85
+ md: "w-2.5 h-2.5",
86
+ lg: "w-3 h-3",
87
+ xl: "w-3.5 h-3.5",
88
+ };
89
+
90
+ const gapBySize: Record<SizeKey, string> = {
91
+ xs: "gap-1.5",
92
+ sm: "gap-2",
93
+ md: "gap-2.5",
94
+ lg: "gap-3",
95
+ xl: "gap-3.5",
96
+ };
97
+
98
+ const variants = $derived(
99
+ variant === "neutral"
100
+ ? {
101
+ checked: "bg-transparent border-[var(--border-color-strong)]",
102
+ unchecked: "bg-transparent border-[var(--border-color-default)]",
103
+ }
104
+ : {
105
+ checked: "bg-transparent border-[var(--color-bg-primary)]",
106
+ unchecked: "bg-transparent border-[var(--border-color-default)]",
107
+ }
108
+ );
109
+
110
+ const baseCircle =
111
+ "rounded-full border border-solid cursor-pointer transition-all duration-[var(--transition-fast)] ease-[var(--timing-default)] flex items-center justify-center";
112
+
113
+ const focusFromPeer =
114
+ "peer-focus-visible:ring-2 peer-focus-visible:ring-[var(--border-color-focus)] peer-focus-visible:border-[var(--border-color-focus)]";
115
+
116
+ const dotColor = $derived(
117
+ variant === "neutral"
118
+ ? "bg-[var(--border-color-strong)]"
119
+ : "bg-[var(--color-bg-primary)]"
120
+ );
121
+
122
+ const isUsingGroup = $derived(typeof group !== "undefined");
123
+ const isChecked = $derived(isUsingGroup ? group === value : checked);
124
+ const state = $derived(isChecked ? "checked" : "unchecked");
125
+
126
+ const rootClass = $derived(
127
+ cx(
128
+ "inline-flex items-center cursor-pointer select-none",
129
+ gapBySize[sz],
130
+ externalClass
131
+ )
132
+ );
133
+
134
+ const radioClass = $derived(
135
+ cx(
136
+ baseCircle,
137
+ focusFromPeer,
138
+ sizeClasses[sz],
139
+ isChecked && variants.checked,
140
+ !isChecked && variants.unchecked,
141
+ "peer-disabled:opacity-[var(--opacity-disabled)] peer-disabled:cursor-not-allowed"
142
+ )
143
+ );
144
+
145
+ const dotClass = $derived(
146
+ cx(
147
+ "rounded-full transition-transform duration-[var(--transition-fast)] ease-[var(--timing-default)]",
148
+ dotSizes[sz],
149
+ dotColor,
150
+ isChecked ? "scale-100 opacity-100" : "scale-50 opacity-0"
151
+ )
152
+ );
153
+
154
+ const labelClass = $derived(
155
+ cx(
156
+ TEXT[sz],
157
+ "[color:var(--color-text-muted)] font-medium peer-disabled:cursor-not-allowed"
158
+ )
159
+ );
160
+ </script>
161
+
162
+ <label class={rootClass} for={inputId}>
163
+ <input
164
+ id={inputId}
165
+ type="radio"
166
+ {value}
167
+ bind:group
168
+ checked={isChecked}
169
+ {...rest}
170
+ class="sr-only peer"
171
+ aria-checked={isChecked}
172
+ aria-describedby={describedBy}
173
+ onchange={(event) => {
174
+ const next = (event.currentTarget as HTMLInputElement).checked;
175
+ checked = next;
176
+ onChange?.(next);
177
+ }}
178
+ />
179
+
180
+ <span data-state={state} class={radioClass} aria-hidden="true">
181
+ <span class={dotClass}></span>
182
+ </span>
183
+
184
+ {#if children}
185
+ <span class={labelClass}>{@render children?.()}</span>
186
+ {:else if label}
187
+ <span class={labelClass}>{label}</span>
188
+ {/if}
189
+ </label>
@@ -1,104 +1,104 @@
1
- <script lang="ts">
2
- /**
3
- * @component SearchInput
4
- * @description Search input field with a leading search icon.
5
- *
6
- * @prop label {string} - Label text rendered above the field
7
- *
8
- * @prop placeholder {string} - Placeholder text (localized by default)
9
- *
10
- * @prop value {string} - Controlled field value (bindable)
11
- * @default ""
12
- *
13
- * @prop sz {SizeKey} - Size preset for spacing and typography
14
- * @options xs|sm|md|lg|xl
15
- * @default sm
16
- *
17
- * @prop variant {FieldVariant} - Visual style variant
18
- * @options default|filled|neutral
19
- * @default filled
20
- *
21
- * @prop class {string} - Additional classes applied to the Field root
22
- * @default ""
23
- *
24
- * @note Renders a leading search icon and uses `Field` with `type="search"` and `clearable`.
25
- */
26
- import Field from "./Field.svelte";
27
- import type { FieldVariant, SizeKey } from "./types";
28
- import { getComponentText, getLangContext, getLangKey } from "./lang-context";
29
-
30
- type Props = {
31
- label?: string;
32
- placeholder?: string;
33
- value?: string;
34
- sz?: SizeKey;
35
- variant?: FieldVariant;
36
- class?: string;
37
- [key: string]: unknown;
38
- };
39
-
40
- let {
41
- label,
42
- placeholder,
43
- value = $bindable(''),
44
- sz = 'sm',
45
- variant = 'filled',
46
- class: externalClass = '',
47
- ...rest
48
- }: Props = $props();
49
-
50
- const langCtx = getLangContext();
51
- const langKey = $derived(getLangKey(langCtx));
52
- const L = $derived(getComponentText("searchInput", langKey));
53
-
54
- const placeholderFinal = $derived(placeholder ?? L.placeholder);
55
- </script>
56
-
57
- {#snippet leading()}
58
- <span class="ml-1 inline-flex h-6 w-6 items-center justify-center text-[var(--color-text-muted)]">
59
- <svg
60
- xmlns="http://www.w3.org/2000/svg"
61
- width="20"
62
- height="20"
63
- viewBox="0 0 24 24"
64
- fill="none"
65
- stroke="currentColor"
66
- stroke-width="2"
67
- stroke-linecap="round"
68
- stroke-linejoin="round"
69
- class="lucide lucide-search-icon lucide-search"
70
- >
71
- <path d="m21 21-4.34-4.34" />
72
- <circle cx="11" cy="11" r="8" />
73
- </svg>
74
- </span>
75
- {/snippet}
76
-
77
- <Field
78
- {label}
79
- bind:value
80
- {sz}
81
- {variant}
82
- type="search"
83
- clearable={true}
84
- {leading}
85
- placeholder={placeholderFinal}
86
- class={`search-input w-full max-w-[520px] [&_input]:pl-10 ${externalClass}`}
87
- {...rest}
88
- />
89
-
90
- <style>
91
- :global(.search-input input[type="search"]) {
92
- -webkit-appearance: none;
93
- appearance: none;
94
- }
95
-
96
- :global(.search-input input[type="search"]::-webkit-search-cancel-button),
97
- :global(.search-input input[type="search"]::-webkit-search-decoration),
98
- :global(.search-input input[type="search"]::-webkit-search-results-button),
99
- :global(.search-input input[type="search"]::-webkit-search-results-decoration) {
100
- -webkit-appearance: none;
101
- appearance: none;
102
- display: none;
103
- }
104
- </style>
1
+ <script lang="ts">
2
+ /**
3
+ * @component SearchInput
4
+ * @description Search input field with a leading search icon.
5
+ *
6
+ * @prop label {string} - Label text rendered above the field
7
+ *
8
+ * @prop placeholder {string} - Placeholder text (localized by default)
9
+ *
10
+ * @prop value {string} - Controlled field value (bindable)
11
+ * @default ""
12
+ *
13
+ * @prop sz {SizeKey} - Size preset for spacing and typography
14
+ * @options xs|sm|md|lg|xl
15
+ * @default sm
16
+ *
17
+ * @prop variant {FieldVariant} - Visual style variant
18
+ * @options default|filled|neutral
19
+ * @default filled
20
+ *
21
+ * @prop class {string} - Additional classes applied to the Field root
22
+ * @default ""
23
+ *
24
+ * @note Renders a leading search icon and uses `Field` with `type="search"` and `clearable`.
25
+ */
26
+ import Field from "./Field.svelte";
27
+ import type { FieldVariant, SizeKey } from "./types";
28
+ import { getComponentText, getLangContext, getLangKey } from "./lang-context";
29
+
30
+ type Props = {
31
+ label?: string;
32
+ placeholder?: string;
33
+ value?: string;
34
+ sz?: SizeKey;
35
+ variant?: FieldVariant;
36
+ class?: string;
37
+ [key: string]: unknown;
38
+ };
39
+
40
+ let {
41
+ label,
42
+ placeholder,
43
+ value = $bindable(''),
44
+ sz = 'sm',
45
+ variant = 'filled',
46
+ class: externalClass = '',
47
+ ...rest
48
+ }: Props = $props();
49
+
50
+ const langCtx = getLangContext();
51
+ const langKey = $derived(getLangKey(langCtx));
52
+ const L = $derived(getComponentText("searchInput", langKey));
53
+
54
+ const placeholderFinal = $derived(placeholder ?? L.placeholder);
55
+ </script>
56
+
57
+ {#snippet leading()}
58
+ <span class="ml-1 inline-flex h-6 w-6 items-center justify-center text-[var(--color-text-muted)]">
59
+ <svg
60
+ xmlns="http://www.w3.org/2000/svg"
61
+ width="20"
62
+ height="20"
63
+ viewBox="0 0 24 24"
64
+ fill="none"
65
+ stroke="currentColor"
66
+ stroke-width="2"
67
+ stroke-linecap="round"
68
+ stroke-linejoin="round"
69
+ class="lucide lucide-search-icon lucide-search"
70
+ >
71
+ <path d="m21 21-4.34-4.34" />
72
+ <circle cx="11" cy="11" r="8" />
73
+ </svg>
74
+ </span>
75
+ {/snippet}
76
+
77
+ <Field
78
+ {label}
79
+ bind:value
80
+ {sz}
81
+ {variant}
82
+ type="search"
83
+ clearable={true}
84
+ {leading}
85
+ placeholder={placeholderFinal}
86
+ class={`search-input w-full max-w-[520px] [&_input]:pl-10 ${externalClass}`}
87
+ {...rest}
88
+ />
89
+
90
+ <style>
91
+ :global(.search-input input[type="search"]) {
92
+ -webkit-appearance: none;
93
+ appearance: none;
94
+ }
95
+
96
+ :global(.search-input input[type="search"]::-webkit-search-cancel-button),
97
+ :global(.search-input input[type="search"]::-webkit-search-decoration),
98
+ :global(.search-input input[type="search"]::-webkit-search-results-button),
99
+ :global(.search-input input[type="search"]::-webkit-search-results-decoration) {
100
+ -webkit-appearance: none;
101
+ appearance: none;
102
+ display: none;
103
+ }
104
+ </style>