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.
- package/LICENSE.md +21 -21
- package/README.md +101 -100
- package/dist/App.svelte +507 -507
- package/dist/Container.svelte +59 -59
- package/dist/app.css +234 -235
- package/dist/app.d.ts +10 -0
- package/dist/lib/Accordion.svelte +155 -155
- package/dist/lib/Badge.svelte +44 -44
- package/dist/lib/Button.svelte +185 -170
- package/dist/lib/Calendar.svelte +384 -384
- package/dist/lib/Card.svelte +103 -103
- package/dist/lib/Carousel.svelte +293 -293
- package/dist/lib/Carousel.svelte.d.ts +1 -1
- package/dist/lib/CheckBox.svelte +210 -210
- package/dist/lib/CodeView.svelte +308 -307
- package/dist/lib/ColorPicker.svelte +159 -159
- package/dist/lib/ContextMenu.svelte +328 -322
- package/dist/lib/DatePicker.svelte +246 -246
- package/dist/lib/Dialog.svelte +233 -233
- package/dist/lib/Field.svelte +299 -299
- package/dist/lib/FilePicker.svelte +295 -240
- package/dist/lib/FilePicker.svelte.d.ts +6 -1
- package/dist/lib/Form.svelte +438 -438
- package/dist/lib/Hamburger.svelte +217 -217
- package/dist/lib/InstallPWA.svelte +94 -94
- package/dist/lib/Menu.svelte +623 -623
- package/dist/lib/NoticeBase.svelte +140 -140
- package/dist/lib/PaginatedCard.svelte +73 -73
- package/dist/lib/Pagination.svelte +119 -119
- package/dist/lib/PrimaryColorSelect.svelte +111 -111
- package/dist/lib/ProgressBar.svelte +141 -141
- package/dist/lib/ProgressCircle.svelte +190 -190
- package/dist/lib/Radio.svelte +189 -189
- package/dist/lib/SearchInput.svelte +104 -104
- package/dist/lib/Select.svelte +524 -524
- package/dist/lib/Slider.svelte +253 -253
- package/dist/lib/Splitter.svelte +159 -150
- package/dist/lib/Switch.svelte +168 -167
- package/dist/lib/Table.svelte +299 -299
- package/dist/lib/Tabs.svelte +213 -213
- package/dist/lib/ThemeToggle.svelte +128 -127
- package/dist/lib/TimePicker.svelte +312 -312
- package/dist/lib/TimePickerNew.svelte +634 -0
- package/dist/lib/TimePickerNew.svelte.d.ts +49 -0
- package/dist/lib/Toast.svelte +123 -123
- package/dist/lib/Tooltip.svelte +110 -110
- package/dist/lib/Topbar.svelte +107 -107
- package/dist/lib/__tests__/Accordion.test.d.ts +1 -0
- package/dist/lib/__tests__/Accordion.test.js +171 -0
- package/dist/lib/__tests__/Badge.test.d.ts +1 -0
- package/dist/lib/__tests__/Badge.test.js +41 -0
- package/dist/lib/__tests__/Button.test.d.ts +1 -0
- package/dist/lib/__tests__/Button.test.js +269 -0
- package/dist/lib/__tests__/Calendar.test.d.ts +1 -0
- package/dist/lib/__tests__/Calendar.test.js +171 -0
- package/dist/lib/__tests__/Card.test.d.ts +1 -0
- package/dist/lib/__tests__/Card.test.js +148 -0
- package/dist/lib/__tests__/Carousel.test.d.ts +1 -0
- package/dist/lib/__tests__/Carousel.test.js +439 -0
- package/dist/lib/__tests__/CheckBox.test.d.ts +1 -0
- package/dist/lib/__tests__/CheckBox.test.js +152 -0
- package/dist/lib/__tests__/CodeView.test.d.ts +1 -0
- package/dist/lib/__tests__/CodeView.test.js +157 -0
- package/dist/lib/__tests__/ColorPicker.test.d.ts +1 -0
- package/dist/lib/__tests__/ColorPicker.test.js +93 -0
- package/dist/lib/__tests__/ContextMenu.test.d.ts +1 -0
- package/dist/lib/__tests__/ContextMenu.test.js +67 -0
- package/dist/lib/__tests__/DatePicker.test.d.ts +1 -0
- package/dist/lib/__tests__/DatePicker.test.js +108 -0
- package/dist/lib/__tests__/Dialog.test.d.ts +1 -0
- package/dist/lib/__tests__/Dialog.test.js +183 -0
- package/dist/lib/__tests__/Field.test.d.ts +1 -0
- package/dist/lib/__tests__/Field.test.js +190 -0
- package/dist/lib/__tests__/FilePicker.test.d.ts +1 -0
- package/dist/lib/__tests__/FilePicker.test.js +179 -0
- package/dist/lib/__tests__/Form.integration.test.d.ts +1 -0
- package/dist/lib/__tests__/Form.integration.test.js +158 -0
- package/dist/lib/__tests__/Form.test.d.ts +1 -0
- package/dist/lib/__tests__/Form.test.js +463 -0
- package/dist/lib/__tests__/Hamburger.test.d.ts +1 -0
- package/dist/lib/__tests__/Hamburger.test.js +161 -0
- package/dist/lib/__tests__/InstallPWA.test.d.ts +1 -0
- package/dist/lib/__tests__/InstallPWA.test.js +15 -0
- package/dist/lib/__tests__/Menu.test.d.ts +1 -0
- package/dist/lib/__tests__/Menu.test.js +285 -0
- package/dist/lib/__tests__/NoticeBase.test.d.ts +1 -0
- package/dist/lib/__tests__/NoticeBase.test.js +60 -0
- package/dist/lib/__tests__/PaginatedCard.test.d.ts +1 -0
- package/dist/lib/__tests__/PaginatedCard.test.js +89 -0
- package/dist/lib/__tests__/Pagination.test.d.ts +1 -0
- package/dist/lib/__tests__/Pagination.test.js +168 -0
- package/dist/lib/__tests__/PrimaryColorSelect.test.d.ts +1 -0
- package/dist/lib/__tests__/PrimaryColorSelect.test.js +92 -0
- package/dist/lib/__tests__/ProgressBar.test.d.ts +1 -0
- package/dist/lib/__tests__/ProgressBar.test.js +69 -0
- package/dist/lib/__tests__/ProgressCircle.test.d.ts +1 -0
- package/dist/lib/__tests__/ProgressCircle.test.js +71 -0
- package/dist/lib/__tests__/Radio.test.d.ts +1 -0
- package/dist/lib/__tests__/Radio.test.js +127 -0
- package/dist/lib/__tests__/SearchInput.test.d.ts +1 -0
- package/dist/lib/__tests__/SearchInput.test.js +80 -0
- package/dist/lib/__tests__/Select.test.d.ts +1 -0
- package/dist/lib/__tests__/Select.test.js +408 -0
- package/dist/lib/__tests__/Slider.test.d.ts +1 -0
- package/dist/lib/__tests__/Slider.test.js +213 -0
- package/dist/lib/__tests__/Splitter.test.d.ts +1 -0
- package/dist/lib/__tests__/Splitter.test.js +87 -0
- package/dist/lib/__tests__/Switch.test.d.ts +1 -0
- package/dist/lib/__tests__/Switch.test.js +97 -0
- package/dist/lib/__tests__/Table.test.d.ts +1 -0
- package/dist/lib/__tests__/Table.test.js +349 -0
- package/dist/lib/__tests__/Tabs.test.d.ts +1 -0
- package/dist/lib/__tests__/Tabs.test.js +262 -0
- package/dist/lib/__tests__/ThemeToggle.test.d.ts +1 -0
- package/dist/lib/__tests__/ThemeToggle.test.js +84 -0
- package/dist/lib/__tests__/TimePicker.test.d.ts +1 -0
- package/dist/lib/__tests__/TimePicker.test.js +146 -0
- package/dist/lib/__tests__/TimePickerNew.test.d.ts +1 -0
- package/dist/lib/__tests__/TimePickerNew.test.js +322 -0
- package/dist/lib/__tests__/Toast.test.d.ts +1 -0
- package/dist/lib/__tests__/Toast.test.js +135 -0
- package/dist/lib/__tests__/Tooltip.test.d.ts +1 -0
- package/dist/lib/__tests__/Tooltip.test.js +171 -0
- package/dist/lib/__tests__/Topbar.test.d.ts +1 -0
- package/dist/lib/__tests__/Topbar.test.js +25 -0
- package/dist/lib/__tests__/setupLangContext.d.ts +1 -0
- package/dist/lib/__tests__/setupLangContext.js +65 -0
- package/dist/lib/__tests__/storage.test.d.ts +1 -0
- package/dist/lib/__tests__/storage.test.js +124 -0
- package/dist/lib/__tests__/utils.test.d.ts +1 -0
- package/dist/lib/__tests__/utils.test.js +11 -0
- package/dist/lib/index.d.ts +1 -0
- package/dist/lib/index.js +1 -0
- package/dist/lib/lang.d.ts +4 -0
- package/dist/lib/lang.js +4 -0
- package/dist/styles.css +234 -232
- package/dist/utils/index.js +15 -4
- package/package.json +52 -52
package/dist/lib/Radio.svelte
CHANGED
|
@@ -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>
|