zabi-components 5.0.20 → 5.0.22
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 +21 -0
- package/README.md +355 -81
- package/dist/atoms/Button.svelte +1 -1
- package/dist/atoms/CardContent.svelte +1 -1
- package/dist/atoms/CardHeader.svelte +1 -1
- package/dist/atoms/Checkbox.svelte +1 -7
- package/dist/atoms/CodeBlock.svelte +6 -6
- package/dist/atoms/ColorPicker.svelte +80 -72
- package/dist/atoms/IconButton.svelte +88 -0
- package/dist/atoms/IconButton.svelte.d.ts +21 -0
- package/dist/atoms/Input.svelte +14 -10
- package/dist/atoms/Input.svelte.d.ts +2 -0
- package/dist/atoms/List.svelte +47 -0
- package/dist/atoms/List.svelte.d.ts +19 -0
- package/dist/atoms/ListItem.svelte +148 -0
- package/dist/atoms/ListItem.svelte.d.ts +35 -0
- package/dist/atoms/Progress.svelte +3 -5
- package/dist/atoms/Select.svelte +152 -19
- package/dist/atoms/Select.svelte.d.ts +23 -1
- package/dist/atoms/Textarea.svelte +11 -9
- package/dist/atoms/Textarea.svelte.d.ts +1 -0
- package/dist/atoms/Toggle.svelte +1 -7
- package/dist/atoms/Tooltip.svelte +8 -15
- package/dist/atoms/index.d.ts +3 -0
- package/dist/atoms/index.js +3 -0
- package/dist/components/atoms/index.d.ts +3 -0
- package/dist/components/atoms/index.d.ts.map +1 -1
- package/dist/components/index.d.ts +4 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/molecules/index.d.ts +1 -1
- package/dist/components/molecules/navigation-menu-context.d.ts +7 -0
- package/dist/components/molecules/navigation-menu-context.d.ts.map +1 -0
- package/dist/components/organisms/index.d.ts +2 -0
- package/dist/components/organisms/index.d.ts.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +5 -8
- package/dist/molecules/Alert.svelte +8 -3
- package/dist/molecules/ContactForm.svelte +63 -31
- package/dist/molecules/ContactForm.svelte.d.ts +1 -4
- package/dist/molecules/Dropdown.svelte +2 -1
- package/dist/molecules/ImageUpload.svelte +26 -3
- package/dist/molecules/ImageUpload.svelte.d.ts +1 -0
- package/dist/molecules/Modal.svelte +44 -43
- package/dist/molecules/NavigationMenu.svelte +7 -10
- package/dist/molecules/NavigationMenuContent.svelte +8 -8
- package/dist/molecules/NavigationMenuItem.svelte +7 -6
- package/dist/molecules/NavigationMenuLink.svelte +7 -5
- package/dist/molecules/NavigationMenuList.svelte +0 -7
- package/dist/molecules/NavigationMenuTrigger.svelte +7 -7
- package/dist/molecules/Section.svelte +158 -0
- package/dist/molecules/Section.svelte.d.ts +27 -0
- package/dist/molecules/SlideUp.svelte +30 -0
- package/dist/molecules/Tabs.svelte +71 -4
- package/dist/molecules/index.d.ts +1 -1
- package/dist/molecules/index.js +1 -1
- package/dist/molecules/navigation-menu-context.d.ts +6 -0
- package/dist/molecules/navigation-menu-context.js +1 -0
- package/dist/organisms/Navigation.svelte +9 -6
- package/dist/organisms/Navigation.svelte.d.ts +1 -0
- package/dist/organisms/SidebarNavigation.svelte +420 -0
- package/dist/organisms/SidebarNavigation.svelte.d.ts +44 -0
- package/dist/organisms/SidebarProjectPanel.svelte +174 -0
- package/dist/organisms/SidebarProjectPanel.svelte.d.ts +32 -0
- package/dist/organisms/index.d.ts +2 -0
- package/dist/organisms/index.js +2 -0
- package/dist/zabi-components.css +412 -0
- package/package.json +7 -8
- package/dist/molecules/Sidebar.svelte +0 -324
- package/dist/molecules/Sidebar.svelte.d.ts +0 -30
|
@@ -92,31 +92,31 @@
|
|
|
92
92
|
|
|
93
93
|
:global(.language-svelte .token.tag),
|
|
94
94
|
:global(.language-html .token.tag) {
|
|
95
|
-
color:
|
|
95
|
+
color: var(--color-error-text);
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
:global(.language-svelte .token.attr-name),
|
|
99
99
|
:global(.language-html .token.attr-name) {
|
|
100
|
-
color:
|
|
100
|
+
color: var(--color-success-text);
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
:global(.language-svelte .token.attr-value),
|
|
104
104
|
:global(.language-html .token.attr-value) {
|
|
105
|
-
color:
|
|
105
|
+
color: var(--color-warning-text);
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
:global(.language-javascript .token.keyword),
|
|
109
109
|
:global(.language-typescript .token.keyword) {
|
|
110
|
-
color:
|
|
110
|
+
color: var(--color-info-text);
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
:global(.language-javascript .token.string),
|
|
114
114
|
:global(.language-typescript .token.string) {
|
|
115
|
-
color:
|
|
115
|
+
color: var(--color-warning-text);
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
:global(.language-javascript .token.function),
|
|
119
119
|
:global(.language-typescript .token.function) {
|
|
120
|
-
color:
|
|
120
|
+
color: var(--color-success-text);
|
|
121
121
|
}
|
|
122
122
|
</style>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import Input from "./Input.svelte";
|
|
3
|
-
import { onMount, onDestroy } from "svelte";
|
|
3
|
+
import { onMount, onDestroy, tick } from "svelte";
|
|
4
4
|
|
|
5
5
|
interface Props {
|
|
6
6
|
value?: string;
|
|
@@ -20,11 +20,10 @@
|
|
|
20
20
|
}: Props = $props();
|
|
21
21
|
|
|
22
22
|
let isOpen = $state(false);
|
|
23
|
-
let pickerContainer: HTMLDivElement;
|
|
24
|
-
let colorMap: HTMLCanvasElement;
|
|
25
|
-
let colorMapContainer: HTMLDivElement;
|
|
26
|
-
let hueSlider: HTMLInputElement;
|
|
27
23
|
let isDragging = $state(false);
|
|
24
|
+
let pickerContainer: HTMLDivElement | undefined;
|
|
25
|
+
let colorMap: HTMLCanvasElement | undefined;
|
|
26
|
+
let colorMapContainer: HTMLDivElement | undefined;
|
|
28
27
|
|
|
29
28
|
// HSL values (0-360, 0-100, 0-100)
|
|
30
29
|
let hue = $state(0);
|
|
@@ -96,46 +95,56 @@
|
|
|
96
95
|
}
|
|
97
96
|
}
|
|
98
97
|
|
|
99
|
-
const variant = $derived(()
|
|
100
|
-
if (value && !isValidHex(value)) {
|
|
101
|
-
return "error";
|
|
102
|
-
}
|
|
103
|
-
return "default";
|
|
104
|
-
});
|
|
98
|
+
const variant = $derived(value && !isValidHex(value) ? "error" : "default");
|
|
105
99
|
|
|
106
|
-
const message = $derived(
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
});
|
|
100
|
+
const message = $derived(
|
|
101
|
+
value && !isValidHex(value)
|
|
102
|
+
? "Please enter a valid hex color (e.g., #ff0000 or #f00)"
|
|
103
|
+
: "",
|
|
104
|
+
);
|
|
112
105
|
|
|
113
106
|
function handleInput(event: Event) {
|
|
114
107
|
if (onchange) onchange(event);
|
|
115
108
|
updateFromHex(value);
|
|
116
109
|
}
|
|
117
110
|
|
|
118
|
-
function
|
|
119
|
-
|
|
120
|
-
let hexValue = target.value.trim();
|
|
111
|
+
function normalizeHexValue(rawValue: string): string {
|
|
112
|
+
let hexValue = rawValue.trim();
|
|
121
113
|
|
|
122
114
|
if (hexValue && !hexValue.startsWith("#")) {
|
|
123
115
|
hexValue = "#" + hexValue;
|
|
124
116
|
}
|
|
125
117
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
118
|
+
return hexValue;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function commitCurrentValue(event: Event) {
|
|
122
|
+
const normalizedHex = normalizeHexValue(value);
|
|
123
|
+
|
|
124
|
+
if (normalizedHex === "" || isValidHex(normalizedHex)) {
|
|
125
|
+
value = normalizedHex;
|
|
126
|
+
updateFromHex(normalizedHex);
|
|
129
127
|
if (onchange) onchange(event);
|
|
130
128
|
}
|
|
131
129
|
}
|
|
132
130
|
|
|
133
|
-
function
|
|
131
|
+
function handleBlur(event: Event) {
|
|
132
|
+
commitCurrentValue(event);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async function togglePicker() {
|
|
134
136
|
if (!disabled) {
|
|
137
|
+
if (isOpen) {
|
|
138
|
+
commitCurrentValue(new Event("change"));
|
|
139
|
+
}
|
|
135
140
|
isOpen = !isOpen;
|
|
136
141
|
if (isOpen && value && isValidHex(value)) {
|
|
137
142
|
updateFromHex(value);
|
|
138
143
|
}
|
|
144
|
+
if (isOpen) {
|
|
145
|
+
await tick();
|
|
146
|
+
drawColorMap();
|
|
147
|
+
}
|
|
139
148
|
}
|
|
140
149
|
}
|
|
141
150
|
|
|
@@ -178,6 +187,10 @@
|
|
|
178
187
|
}
|
|
179
188
|
}
|
|
180
189
|
|
|
190
|
+
function handleMouseUp() {
|
|
191
|
+
isDragging = false;
|
|
192
|
+
}
|
|
193
|
+
|
|
181
194
|
function handleHueChange(event: Event) {
|
|
182
195
|
const target = event.target as HTMLInputElement;
|
|
183
196
|
hue = parseInt(target.value);
|
|
@@ -185,26 +198,38 @@
|
|
|
185
198
|
drawColorMap();
|
|
186
199
|
}
|
|
187
200
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
201
|
+
function setPickerContainer(node: HTMLDivElement) {
|
|
202
|
+
pickerContainer = node;
|
|
203
|
+
return {
|
|
204
|
+
destroy() {
|
|
205
|
+
if (pickerContainer === node) {
|
|
206
|
+
pickerContainer = undefined;
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
};
|
|
210
|
+
}
|
|
196
211
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
212
|
+
function setColorMap(node: HTMLCanvasElement) {
|
|
213
|
+
colorMap = node;
|
|
214
|
+
return {
|
|
215
|
+
destroy() {
|
|
216
|
+
if (colorMap === node) {
|
|
217
|
+
colorMap = undefined;
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
};
|
|
221
|
+
}
|
|
202
222
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
223
|
+
function setColorMapContainer(node: HTMLDivElement) {
|
|
224
|
+
colorMapContainer = node;
|
|
225
|
+
return {
|
|
226
|
+
destroy() {
|
|
227
|
+
if (colorMapContainer === node) {
|
|
228
|
+
colorMapContainer = undefined;
|
|
229
|
+
}
|
|
230
|
+
},
|
|
231
|
+
};
|
|
232
|
+
}
|
|
208
233
|
|
|
209
234
|
function handleClickOutside(event: MouseEvent) {
|
|
210
235
|
if (
|
|
@@ -214,6 +239,7 @@
|
|
|
214
239
|
'[aria-label="Open color picker"]',
|
|
215
240
|
)
|
|
216
241
|
) {
|
|
242
|
+
commitCurrentValue(new Event("change"));
|
|
217
243
|
isOpen = false;
|
|
218
244
|
}
|
|
219
245
|
}
|
|
@@ -223,36 +249,19 @@
|
|
|
223
249
|
updateFromHex(value);
|
|
224
250
|
}
|
|
225
251
|
window.addEventListener("mousedown", handleClickOutside);
|
|
226
|
-
window.addEventListener("mouseup",
|
|
227
|
-
isDragging = false;
|
|
228
|
-
});
|
|
252
|
+
window.addEventListener("mouseup", handleMouseUp);
|
|
229
253
|
window.addEventListener("mousemove", handleColorMapDrag);
|
|
230
254
|
});
|
|
231
255
|
|
|
232
256
|
onDestroy(() => {
|
|
233
257
|
window.removeEventListener("mousedown", handleClickOutside);
|
|
234
|
-
window.removeEventListener("mouseup",
|
|
258
|
+
window.removeEventListener("mouseup", handleMouseUp);
|
|
235
259
|
window.removeEventListener("mousemove", handleColorMapDrag);
|
|
236
260
|
});
|
|
237
261
|
|
|
238
|
-
const displayColor = $derived(()
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
}
|
|
242
|
-
return placeholder;
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
const currentHslColor = $derived(() => {
|
|
246
|
-
return `hsl(${hue}, 100%, 50%)`;
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
const pickerIndicatorX = $derived(() => {
|
|
250
|
-
return `${(saturation / 100) * 100}%`;
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
const pickerIndicatorY = $derived(() => {
|
|
254
|
-
return `${100 - (lightness / 100) * 100}%`;
|
|
255
|
-
});
|
|
262
|
+
const displayColor = $derived(value && isValidHex(value) ? value : placeholder);
|
|
263
|
+
const pickerIndicatorX = $derived(`${saturation}%`);
|
|
264
|
+
const pickerIndicatorY = $derived(`${100 - lightness}%`);
|
|
256
265
|
</script>
|
|
257
266
|
|
|
258
267
|
<div {...restProps}>
|
|
@@ -263,8 +272,8 @@
|
|
|
263
272
|
{label}
|
|
264
273
|
{placeholder}
|
|
265
274
|
{disabled}
|
|
266
|
-
|
|
267
|
-
|
|
275
|
+
{variant}
|
|
276
|
+
{message}
|
|
268
277
|
oninput={handleInput}
|
|
269
278
|
onblur={handleBlur}
|
|
270
279
|
aria-label="Hex color input"
|
|
@@ -276,14 +285,14 @@
|
|
|
276
285
|
onclick={togglePicker}
|
|
277
286
|
{disabled}
|
|
278
287
|
class="w-11 h-11 rounded-lg border-2 border-card shrink-0 cursor-pointer hover:ring-2 hover:ring-brand-500 transition-colors disabled:cursor-not-allowed disabled:opacity-50"
|
|
279
|
-
style="background-color: {displayColor
|
|
288
|
+
style="background-color: {displayColor};"
|
|
280
289
|
aria-label="Open color picker"
|
|
281
290
|
aria-expanded={isOpen}
|
|
282
291
|
></button>
|
|
283
292
|
|
|
284
293
|
{#if isOpen}
|
|
285
294
|
<div
|
|
286
|
-
|
|
295
|
+
use:setPickerContainer
|
|
287
296
|
class="absolute top-12 right-0 z-50 bg-input rounded-2xl shadow-lg p-4 w-80"
|
|
288
297
|
role="dialog"
|
|
289
298
|
aria-label="Color picker"
|
|
@@ -291,7 +300,7 @@
|
|
|
291
300
|
<div class="space-y-4">
|
|
292
301
|
<!-- Color Map -->
|
|
293
302
|
<div
|
|
294
|
-
|
|
303
|
+
use:setColorMapContainer
|
|
295
304
|
class="relative w-full h-48 rounded-xl overflow-hidden cursor-crosshair"
|
|
296
305
|
onmousedown={(e) => {
|
|
297
306
|
isDragging = true;
|
|
@@ -301,7 +310,7 @@
|
|
|
301
310
|
tabindex="0"
|
|
302
311
|
>
|
|
303
312
|
<canvas
|
|
304
|
-
|
|
313
|
+
use:setColorMap
|
|
305
314
|
width={256}
|
|
306
315
|
height={192}
|
|
307
316
|
class="w-full h-full"
|
|
@@ -309,7 +318,7 @@
|
|
|
309
318
|
<!-- Color indicator -->
|
|
310
319
|
<div
|
|
311
320
|
class="absolute w-4 h-4 border-2 border-white rounded-full shadow-lg pointer-events-none transform -translate-x-1/2 -translate-y-1/2"
|
|
312
|
-
style="left: {pickerIndicatorX
|
|
321
|
+
style="left: {pickerIndicatorX}; top: {pickerIndicatorY};"
|
|
313
322
|
></div>
|
|
314
323
|
</div>
|
|
315
324
|
|
|
@@ -323,7 +332,6 @@
|
|
|
323
332
|
style="background: linear-gradient(to right, hsl(0,100%,50%), hsl(60,100%,50%), hsl(120,100%,50%), hsl(180,100%,50%), hsl(240,100%,50%), hsl(300,100%,50%), hsl(360,100%,50%));"
|
|
324
333
|
></div>
|
|
325
334
|
<input
|
|
326
|
-
bind:this={hueSlider}
|
|
327
335
|
type="range"
|
|
328
336
|
min="0"
|
|
329
337
|
max="360"
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from "svelte";
|
|
3
|
+
import type { ButtonVariant, SizeVariant } from "../../types/variants.js";
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
/** Visual style variant. */
|
|
7
|
+
variant?: ButtonVariant;
|
|
8
|
+
/** Size variant controlling padding and radius. */
|
|
9
|
+
size?: SizeVariant;
|
|
10
|
+
/** Whether the button is disabled. */
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
/** Button type attribute. */
|
|
13
|
+
type?: "button" | "submit" | "reset";
|
|
14
|
+
/** Accessible label for icon-only buttons. */
|
|
15
|
+
label?: string;
|
|
16
|
+
/** Click handler for native click events. */
|
|
17
|
+
onclick?: (event: MouseEvent) => void;
|
|
18
|
+
/** Icon content. */
|
|
19
|
+
children?: Snippet;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let {
|
|
23
|
+
variant = "primary",
|
|
24
|
+
size = "md",
|
|
25
|
+
disabled = false,
|
|
26
|
+
type = "button",
|
|
27
|
+
label = "",
|
|
28
|
+
onclick,
|
|
29
|
+
children,
|
|
30
|
+
...restProps
|
|
31
|
+
}: Props = $props();
|
|
32
|
+
|
|
33
|
+
const sizeClass = $derived(() => {
|
|
34
|
+
if (size === "sm") {
|
|
35
|
+
return {
|
|
36
|
+
padding: "p-2",
|
|
37
|
+
radius: "rounded-md",
|
|
38
|
+
};
|
|
39
|
+
} else if (size === "lg") {
|
|
40
|
+
return {
|
|
41
|
+
padding: "p-3",
|
|
42
|
+
radius: "rounded-xl",
|
|
43
|
+
};
|
|
44
|
+
} else {
|
|
45
|
+
return {
|
|
46
|
+
padding: "p-2.5",
|
|
47
|
+
radius: "rounded-lg",
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const variantClass = $derived(() => {
|
|
53
|
+
return variant === "primary"
|
|
54
|
+
? "bg-action-primary text-action-primary hover:bg-action-primary-hover active:bg-action-primary-active focus:ring-2 focus:ring-brand-500 focus:ring-offset-2"
|
|
55
|
+
: variant === "secondary"
|
|
56
|
+
? "bg-action-secondary text-headline hover:bg-action-secondary-hover active:bg-action-secondary-active focus:ring-2 focus:ring-brand-500 focus:ring-offset-2"
|
|
57
|
+
: variant === "danger"
|
|
58
|
+
? "bg-action-danger text-inverse hover:bg-action-danger-hover active:bg-action-danger-active focus:ring-2 focus:ring-red-500 focus:ring-offset-2"
|
|
59
|
+
: variant === "ghost"
|
|
60
|
+
? "bg-transparent text-headline hover:bg-base-100 active:bg-base-200 focus:ring-2 focus:ring-base-500 focus:ring-offset-2 disabled:text-disabled"
|
|
61
|
+
: variant === "outline"
|
|
62
|
+
? "bg-transparent border-2 border-action-primary text-headline hover:bg-action-secondary hover:text-action-primary active:bg-action-primary-active active:text-action-primary focus:ring-2 focus:ring-brand-500 focus:ring-offset-2 disabled:border-disabled disabled:text-disabled"
|
|
63
|
+
: variant === "link"
|
|
64
|
+
? "text-brand-700 active:text-brand-700 underline-offset-4 hover:underline focus:ring-2 focus:ring-brand-500 focus:ring-offset-2 disabled:text-disabled disabled:no-underline"
|
|
65
|
+
: "bg-action-primary text-action-primary hover:bg-action-primary-hover active:bg-action-primary-active focus:ring-2 focus:ring-brand-500 focus:ring-offset-2";
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const buttonClasses = $derived(() => {
|
|
69
|
+
const sizeStyles = sizeClass();
|
|
70
|
+
const baseClasses =
|
|
71
|
+
"inline-flex items-center justify-center transition-all duration-200 cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed focus:outline-none";
|
|
72
|
+
|
|
73
|
+
return `${baseClasses} ${sizeStyles.padding} ${sizeStyles.radius} ${variantClass()}`.trim();
|
|
74
|
+
});
|
|
75
|
+
</script>
|
|
76
|
+
|
|
77
|
+
<button
|
|
78
|
+
{type}
|
|
79
|
+
class={buttonClasses()}
|
|
80
|
+
{disabled}
|
|
81
|
+
{onclick}
|
|
82
|
+
aria-label={label || undefined}
|
|
83
|
+
{...restProps}
|
|
84
|
+
>
|
|
85
|
+
{#if children}
|
|
86
|
+
{@render children()}
|
|
87
|
+
{/if}
|
|
88
|
+
</button>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Snippet } from "svelte";
|
|
2
|
+
import type { ButtonVariant, SizeVariant } from "../../types/variants.js";
|
|
3
|
+
interface Props {
|
|
4
|
+
/** Visual style variant. */
|
|
5
|
+
variant?: ButtonVariant;
|
|
6
|
+
/** Size variant controlling padding and radius. */
|
|
7
|
+
size?: SizeVariant;
|
|
8
|
+
/** Whether the button is disabled. */
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
/** Button type attribute. */
|
|
11
|
+
type?: "button" | "submit" | "reset";
|
|
12
|
+
/** Accessible label for icon-only buttons. */
|
|
13
|
+
label?: string;
|
|
14
|
+
/** Click handler for native click events. */
|
|
15
|
+
onclick?: (event: MouseEvent) => void;
|
|
16
|
+
/** Icon content. */
|
|
17
|
+
children?: Snippet;
|
|
18
|
+
}
|
|
19
|
+
declare const IconButton: import("svelte").Component<Props, {}, "">;
|
|
20
|
+
type IconButton = ReturnType<typeof IconButton>;
|
|
21
|
+
export default IconButton;
|
package/dist/atoms/Input.svelte
CHANGED
|
@@ -1,21 +1,16 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { CheckCircle, AlertTriangle, AlertCircle } from "@lucide/svelte";
|
|
3
3
|
import type { SemanticVariant, SizeVariant } from "../../types/variants.js";
|
|
4
|
-
|
|
5
|
-
function generateId(prefix: string = "id"): string {
|
|
6
|
-
if (typeof window !== "undefined") {
|
|
7
|
-
return `${prefix}-${Math.random().toString(36).substr(2, 9)}`;
|
|
8
|
-
} else {
|
|
9
|
-
return `${prefix}-ssr-${Date.now()}`;
|
|
10
|
-
}
|
|
11
|
-
}
|
|
4
|
+
import { generateId } from "../../routes/lib/ssr-safe.js";
|
|
12
5
|
|
|
13
6
|
interface Props {
|
|
14
7
|
value?: string;
|
|
15
8
|
type?: string;
|
|
16
9
|
name?: string;
|
|
10
|
+
class?: string;
|
|
17
11
|
label?: string;
|
|
18
12
|
placeholder?: string;
|
|
13
|
+
required?: boolean;
|
|
19
14
|
disabled?: boolean;
|
|
20
15
|
size?: SizeVariant;
|
|
21
16
|
variant?: SemanticVariant;
|
|
@@ -29,8 +24,10 @@
|
|
|
29
24
|
value = $bindable(""),
|
|
30
25
|
type = "text",
|
|
31
26
|
name = "",
|
|
27
|
+
class: className = "",
|
|
32
28
|
label = "",
|
|
33
29
|
placeholder = "",
|
|
30
|
+
required = false,
|
|
34
31
|
disabled = false,
|
|
35
32
|
size = "md",
|
|
36
33
|
variant = "default",
|
|
@@ -79,7 +76,7 @@
|
|
|
79
76
|
const baseClasses =
|
|
80
77
|
"w-full min-w-48 bg-input hover:bg-input-hover focus:bg-input-focus disabled:bg-input-disabled rounded-lg transition-all duration-200 placeholder:text-description text-body focus:outline-none focus:ring-offset-0 disabled:opacity-50 disabled:cursor-not-allowed";
|
|
81
78
|
|
|
82
|
-
return `${baseClasses} ${sizeStyles.padding} ${sizeStyles.text} ${sizeStyles.leading} ${variantClass()}`.trim();
|
|
79
|
+
return `${baseClasses} ${sizeStyles.padding} ${sizeStyles.text} ${sizeStyles.leading} ${variantClass()} ${className}`.trim();
|
|
83
80
|
});
|
|
84
81
|
|
|
85
82
|
const labelClasses = $derived(
|
|
@@ -121,16 +118,23 @@
|
|
|
121
118
|
{name}
|
|
122
119
|
bind:value
|
|
123
120
|
{placeholder}
|
|
121
|
+
{required}
|
|
124
122
|
{disabled}
|
|
125
123
|
class={inputClasses()}
|
|
126
124
|
oninput={handleInput}
|
|
127
125
|
{onblur}
|
|
128
126
|
aria-invalid={variant === "error" ? "true" : undefined}
|
|
127
|
+
aria-required={required ? "true" : undefined}
|
|
129
128
|
aria-describedby={message ? `${inputId}-message` : undefined}
|
|
130
129
|
{...restProps}
|
|
131
130
|
/>
|
|
132
131
|
{#if message && variant !== "default"}
|
|
133
|
-
<p
|
|
132
|
+
<p
|
|
133
|
+
id={`${inputId}-message`}
|
|
134
|
+
class={messageClasses()}
|
|
135
|
+
role={variant === "error" ? "alert" : "status"}
|
|
136
|
+
aria-live={variant === "error" ? "assertive" : "polite"}
|
|
137
|
+
>
|
|
134
138
|
{#if getIcon()}
|
|
135
139
|
{@const Icon = getIcon()}
|
|
136
140
|
<Icon size={14} class="shrink-0" />
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import ListItemRow, { type ListItemData } from "./ListItem.svelte";
|
|
3
|
+
|
|
4
|
+
export type ListItem = ListItemData;
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
/** Collection of items rendered in the list. */
|
|
8
|
+
items: ListItemData[];
|
|
9
|
+
/** Accessible label for the list container. */
|
|
10
|
+
ariaLabel?: string;
|
|
11
|
+
/** Optional active item id for current state styling. */
|
|
12
|
+
selectedId?: string;
|
|
13
|
+
/** Whether to show the arrow icon for each row. */
|
|
14
|
+
showArrow?: boolean;
|
|
15
|
+
/** Optional custom class names for the list container. */
|
|
16
|
+
className?: string;
|
|
17
|
+
/** Item click callback. */
|
|
18
|
+
onclick?: (item: ListItem, event: MouseEvent) => void;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
let {
|
|
22
|
+
items,
|
|
23
|
+
ariaLabel = "List items",
|
|
24
|
+
selectedId = "",
|
|
25
|
+
showArrow = true,
|
|
26
|
+
className = "",
|
|
27
|
+
onclick,
|
|
28
|
+
...restProps
|
|
29
|
+
}: Props = $props();
|
|
30
|
+
|
|
31
|
+
const listClasses = $derived(
|
|
32
|
+
`space-y-1 overflow-hidden rounded-xl border border-border bg-surface p-1 ${className}`.trim(),
|
|
33
|
+
);
|
|
34
|
+
</script>
|
|
35
|
+
|
|
36
|
+
<ul class={listClasses} role="list" aria-label={ariaLabel} {...restProps}>
|
|
37
|
+
{#each items as item (item.id)}
|
|
38
|
+
<li>
|
|
39
|
+
<ListItemRow
|
|
40
|
+
{item}
|
|
41
|
+
selected={item.id === selectedId}
|
|
42
|
+
{showArrow}
|
|
43
|
+
{onclick}
|
|
44
|
+
/>
|
|
45
|
+
</li>
|
|
46
|
+
{/each}
|
|
47
|
+
</ul>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type ListItemData } from "./ListItem.svelte";
|
|
2
|
+
export type ListItem = ListItemData;
|
|
3
|
+
interface Props {
|
|
4
|
+
/** Collection of items rendered in the list. */
|
|
5
|
+
items: ListItemData[];
|
|
6
|
+
/** Accessible label for the list container. */
|
|
7
|
+
ariaLabel?: string;
|
|
8
|
+
/** Optional active item id for current state styling. */
|
|
9
|
+
selectedId?: string;
|
|
10
|
+
/** Whether to show the arrow icon for each row. */
|
|
11
|
+
showArrow?: boolean;
|
|
12
|
+
/** Optional custom class names for the list container. */
|
|
13
|
+
className?: string;
|
|
14
|
+
/** Item click callback. */
|
|
15
|
+
onclick?: (item: ListItem, event: MouseEvent) => void;
|
|
16
|
+
}
|
|
17
|
+
declare const List: import("svelte").Component<Props, {}, "">;
|
|
18
|
+
type List = ReturnType<typeof List>;
|
|
19
|
+
export default List;
|