@websline/system-components 1.0.11 → 1.0.12
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/dist/components/atoms/actions/button/button.variants.d.ts +0 -9
- package/dist/components/atoms/actions/button/button.variants.js +10 -13
- package/dist/components/atoms/actions/closeButton/CloseButton.svelte +1 -1
- package/dist/components/atoms/actions/iconButton/iconButton.variants.js +3 -3
- package/dist/components/atoms/avatar/avatar.variants.js +2 -2
- package/dist/components/atoms/badge/badge.variants.js +1 -1
- package/dist/components/atoms/chip/chip.variants.js +2 -2
- package/dist/components/atoms/feedback/progressBar/progressBar.variants.js +2 -2
- package/dist/components/atoms/helperText/helperText.variants.d.ts +2 -2
- package/dist/components/atoms/helperText/helperText.variants.js +1 -1
- package/dist/components/atoms/icon/Icon.svelte +1 -1
- package/dist/components/atoms/input/input.variants.js +4 -4
- package/dist/components/atoms/label/label.variants.d.ts +2 -2
- package/dist/components/atoms/label/label.variants.js +1 -1
- package/dist/components/atoms/select/Select.svelte +0 -1
- package/dist/components/atoms/select/select.variants.js +2 -2
- package/dist/components/molecules/notification/notification.variants.js +4 -4
- package/dist/components/molecules/pickers/colorSwatch/colorSwatch.variants.js +2 -2
- package/dist/components/molecules/richTextEditor/richTextEditor.variants.js +1 -1
- package/dist/components/molecules/selectorCard/selectorCard.variants.js +6 -5
- package/dist/components/molecules/tagSelector/Dropdown.svelte +111 -0
- package/dist/components/molecules/tagSelector/Dropdown.svelte.d.ts +29 -0
- package/dist/components/molecules/tagSelector/TagSelector.svelte +241 -0
- package/dist/components/molecules/tagSelector/TagSelector.svelte.d.ts +87 -0
- package/dist/components/molecules/tagSelector/ValueList.svelte +52 -0
- package/dist/components/molecules/tagSelector/ValueList.svelte.d.ts +29 -0
- package/dist/components/molecules/tagSelector/tagSelector.variants.d.ts +71 -0
- package/dist/components/molecules/tagSelector/tagSelector.variants.js +36 -0
- package/dist/components/molecules/toggleGroup/ToggleGroupItem.svelte +16 -6
- package/dist/components/molecules/toggleGroup/ToggleGroupItem.svelte.d.ts +8 -0
- package/dist/components/molecules/toggleGroup/toggleGroup.variants.js +3 -3
- package/dist/components/molecules/toggleGroup/toggleGroupItem.variants.d.ts +6 -0
- package/dist/components/molecules/toggleGroup/toggleGroupItem.variants.js +11 -10
- package/dist/components/organisms/modal/modal.variants.js +4 -3
- package/dist/components/organisms/popover/popover.variants.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/package.json +2 -2
|
@@ -34,9 +34,6 @@ export const buttonVariants: import("tailwind-variants").TVReturnType<{
|
|
|
34
34
|
};
|
|
35
35
|
};
|
|
36
36
|
variant: {
|
|
37
|
-
filled: {
|
|
38
|
-
base: string;
|
|
39
|
-
};
|
|
40
37
|
outlined: {
|
|
41
38
|
base: string;
|
|
42
39
|
};
|
|
@@ -84,9 +81,6 @@ export const buttonVariants: import("tailwind-variants").TVReturnType<{
|
|
|
84
81
|
};
|
|
85
82
|
};
|
|
86
83
|
variant: {
|
|
87
|
-
filled: {
|
|
88
|
-
base: string;
|
|
89
|
-
};
|
|
90
84
|
outlined: {
|
|
91
85
|
base: string;
|
|
92
86
|
};
|
|
@@ -134,9 +128,6 @@ export const buttonVariants: import("tailwind-variants").TVReturnType<{
|
|
|
134
128
|
};
|
|
135
129
|
};
|
|
136
130
|
variant: {
|
|
137
|
-
filled: {
|
|
138
|
-
base: string;
|
|
139
|
-
};
|
|
140
131
|
outlined: {
|
|
141
132
|
base: string;
|
|
142
133
|
};
|
|
@@ -2,7 +2,7 @@ import { tv } from "tailwind-variants";
|
|
|
2
2
|
|
|
3
3
|
const buttonVariants = tv({
|
|
4
4
|
slots: {
|
|
5
|
-
base: "inline-flex cursor-pointer items-center rounded align-middle transition duration-200 select-none focus:outline-offset-2",
|
|
5
|
+
base: "inline-flex cursor-pointer items-center rounded-lg border border-transparent align-middle transition duration-200 select-none focus:outline-offset-2",
|
|
6
6
|
icon: "flex h-lh items-center",
|
|
7
7
|
spinner: "flex h-lh items-center justify-center",
|
|
8
8
|
},
|
|
@@ -29,27 +29,24 @@ const buttonVariants = tv({
|
|
|
29
29
|
},
|
|
30
30
|
size: {
|
|
31
31
|
small: {
|
|
32
|
-
base: "gap-2 px-3 py-1.25 button-small",
|
|
32
|
+
base: "gap-2 px-3 py-1.25 ui-button-small",
|
|
33
33
|
spinner: "w-4",
|
|
34
34
|
},
|
|
35
35
|
medium: {
|
|
36
|
-
base: "gap-2 px-3 py-
|
|
36
|
+
base: "gap-2 px-3 py-[7px] ui-button-default",
|
|
37
37
|
spinner: "w-5",
|
|
38
38
|
},
|
|
39
39
|
large: {
|
|
40
|
-
base: "gap-3 px-3 py-
|
|
40
|
+
base: "gap-3 px-3 py-[7px] ui-button-default leading-6",
|
|
41
41
|
spinner: "w-5",
|
|
42
42
|
},
|
|
43
43
|
},
|
|
44
44
|
variant: {
|
|
45
|
-
filled: {
|
|
46
|
-
base: "border-1 border-transparent",
|
|
47
|
-
},
|
|
48
45
|
outlined: {
|
|
49
|
-
base: "border-
|
|
46
|
+
base: "border-current",
|
|
50
47
|
},
|
|
51
48
|
text: {
|
|
52
|
-
base: "relative
|
|
49
|
+
base: "relative px-0! after:absolute after:bottom-0 after:left-0 after:h-[1.5px] after:w-0 after:transition-all after:duration-300 after:content-[''] hover:after:w-full",
|
|
53
50
|
},
|
|
54
51
|
},
|
|
55
52
|
},
|
|
@@ -65,7 +62,7 @@ const buttonVariants = tv({
|
|
|
65
62
|
color: "primary",
|
|
66
63
|
variant: "outlined",
|
|
67
64
|
class: {
|
|
68
|
-
base: "
|
|
65
|
+
base: "text-blue-500 hover:border-blue-600 hover:bg-blue-600 hover:text-white dark:text-blue-400 dark:hover:border-blue-500 dark:hover:bg-blue-500",
|
|
69
66
|
},
|
|
70
67
|
},
|
|
71
68
|
{
|
|
@@ -78,7 +75,7 @@ const buttonVariants = tv({
|
|
|
78
75
|
color: "secondary",
|
|
79
76
|
variant: "outlined",
|
|
80
77
|
class: {
|
|
81
|
-
base: "text-neutral-900 hover:border-neutral-700 hover:bg-neutral-700 hover:text-white dark:
|
|
78
|
+
base: "text-neutral-900 hover:border-neutral-700 hover:bg-neutral-700 hover:text-white dark:text-neutral-200 dark:hover:border-neutral-500 dark:hover:bg-neutral-500 dark:hover:text-neutral-900",
|
|
82
79
|
},
|
|
83
80
|
},
|
|
84
81
|
{
|
|
@@ -92,7 +89,7 @@ const buttonVariants = tv({
|
|
|
92
89
|
color: "success",
|
|
93
90
|
variant: "outlined",
|
|
94
91
|
class: {
|
|
95
|
-
base: "
|
|
92
|
+
base: "text-green-500 hover:border-green-700 hover:bg-green-700 hover:text-white dark:text-green-500 dark:hover:border-green-700 dark:hover:bg-green-700",
|
|
96
93
|
},
|
|
97
94
|
},
|
|
98
95
|
{
|
|
@@ -106,7 +103,7 @@ const buttonVariants = tv({
|
|
|
106
103
|
color: "error",
|
|
107
104
|
variant: "outlined",
|
|
108
105
|
class: {
|
|
109
|
-
base: "
|
|
106
|
+
base: "text-red-500 hover:border-red-700 hover:bg-red-700 hover:text-white dark:text-red-500 dark:hover:border-red-700 dark:hover:bg-red-700",
|
|
110
107
|
},
|
|
111
108
|
},
|
|
112
109
|
{
|
|
@@ -19,9 +19,9 @@ const iconButtonVariants = tv({
|
|
|
19
19
|
large: "p-2.75",
|
|
20
20
|
},
|
|
21
21
|
variant: {
|
|
22
|
-
filled: "border
|
|
23
|
-
outlined: "border
|
|
24
|
-
transparent: "border
|
|
22
|
+
filled: "border border-transparent",
|
|
23
|
+
outlined: "border",
|
|
24
|
+
transparent: "border border-transparent",
|
|
25
25
|
},
|
|
26
26
|
},
|
|
27
27
|
compoundVariants: [
|
|
@@ -5,7 +5,7 @@ const avatarVariants = tv({
|
|
|
5
5
|
base: "flex overflow-hidden",
|
|
6
6
|
image: "h-full w-full object-cover",
|
|
7
7
|
fallback:
|
|
8
|
-
"flex h-full w-full items-center justify-center bg-blue-100
|
|
8
|
+
"flex h-full w-full items-center justify-center bg-blue-100 ui-title-2 text-neutral-900",
|
|
9
9
|
},
|
|
10
10
|
variants: {
|
|
11
11
|
as: {
|
|
@@ -13,7 +13,7 @@ const avatarVariants = tv({
|
|
|
13
13
|
},
|
|
14
14
|
shape: {
|
|
15
15
|
circle: "rounded-full",
|
|
16
|
-
square: "rounded",
|
|
16
|
+
square: "rounded-sm",
|
|
17
17
|
},
|
|
18
18
|
},
|
|
19
19
|
});
|
|
@@ -2,7 +2,7 @@ import { tv } from "tailwind-variants";
|
|
|
2
2
|
|
|
3
3
|
const badgeVariants = tv({
|
|
4
4
|
slots: {
|
|
5
|
-
base: "absolute inline-flex items-center justify-center rounded-full
|
|
5
|
+
base: "absolute inline-flex items-center justify-center rounded-full ui-caption-helper select-none",
|
|
6
6
|
wrapper: "relative inline-flex gap-0.5",
|
|
7
7
|
},
|
|
8
8
|
variants: {
|
|
@@ -2,7 +2,7 @@ import { tv } from "tailwind-variants";
|
|
|
2
2
|
|
|
3
3
|
const chipVariants = tv({
|
|
4
4
|
base: [
|
|
5
|
-
"relative inline-flex items-center gap-1 rounded border align-middle select-none [button]:cursor-pointer",
|
|
5
|
+
"relative inline-flex items-center gap-1 rounded-sm border align-middle select-none [button]:cursor-pointer",
|
|
6
6
|
"ring-blue-500 ring-offset-1 ring-offset-transparent outline-0 focus-visible:ring-1 dark:ring-blue-400",
|
|
7
7
|
],
|
|
8
8
|
variants: {
|
|
@@ -15,7 +15,7 @@ const chipVariants = tv({
|
|
|
15
15
|
warning: "bg-yellow-500 text-neutral-900",
|
|
16
16
|
},
|
|
17
17
|
size: {
|
|
18
|
-
small: "min-h-[20px] px-2 py-0.5
|
|
18
|
+
small: "min-h-[20px] px-2 py-0.5 ui-tag-badge",
|
|
19
19
|
big: "min-h-[28px] px-3 py-1.5 body-small",
|
|
20
20
|
},
|
|
21
21
|
iconPosition: {
|
|
@@ -2,9 +2,9 @@ import { tv } from "tailwind-variants";
|
|
|
2
2
|
|
|
3
3
|
const progressBarVariants = tv({
|
|
4
4
|
slots: {
|
|
5
|
-
bar: "h-1 w-full rounded-sm bg-neutral-100 after:block after:h-full after:w-
|
|
5
|
+
bar: "h-1 w-full rounded-sm bg-neutral-100 after:block after:h-full after:w-(--progress) after:rounded-sm after:bg-neutral-900 after:transition-[width] after:duration-300 after:ease-in-out dark:bg-neutral-900 dark:after:bg-neutral-100",
|
|
6
6
|
base: "flex items-center gap-4",
|
|
7
|
-
label: "
|
|
7
|
+
label: "ui-select-label text-nowrap text-neutral-500 tabular-nums",
|
|
8
8
|
},
|
|
9
9
|
});
|
|
10
10
|
|
|
@@ -2,7 +2,7 @@ export const helperTextVariants: import("tailwind-variants").TVReturnType<{
|
|
|
2
2
|
error: {
|
|
3
3
|
true: string;
|
|
4
4
|
};
|
|
5
|
-
}, undefined, "
|
|
5
|
+
}, undefined, "ui-caption-helper text-neutral-500", {
|
|
6
6
|
error: {
|
|
7
7
|
true: string;
|
|
8
8
|
};
|
|
@@ -10,4 +10,4 @@ export const helperTextVariants: import("tailwind-variants").TVReturnType<{
|
|
|
10
10
|
error: {
|
|
11
11
|
true: string;
|
|
12
12
|
};
|
|
13
|
-
}, undefined, "
|
|
13
|
+
}, undefined, "ui-caption-helper text-neutral-500", unknown, unknown, undefined>>;
|
|
@@ -6,7 +6,7 @@ import { tv } from "tailwind-variants";
|
|
|
6
6
|
const inputBaseVariant = tv({
|
|
7
7
|
slots: {
|
|
8
8
|
base: [
|
|
9
|
-
"w-full rounded
|
|
9
|
+
"w-full rounded-lg px-4 body-small",
|
|
10
10
|
"text-neutral-900 placeholder-neutral-500 dark:text-neutral-200",
|
|
11
11
|
"bg-white dark:bg-neutral-800",
|
|
12
12
|
"outline-transparent transition-[border,color,outline] duration-300",
|
|
@@ -29,9 +29,9 @@ const inputBaseVariant = tv({
|
|
|
29
29
|
error: false,
|
|
30
30
|
class: {
|
|
31
31
|
base: [
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"dark:
|
|
32
|
+
"focus-within:ring-1 hover:ring-1",
|
|
33
|
+
"focus-within:border-blue-500 focus-within:ring-blue-500 hover:border-blue-500 hover:ring-blue-500",
|
|
34
|
+
"dark:focus-within:border-blue-400 dark:focus-within:ring-blue-400 dark:hover:border-blue-400 dark:hover:ring-blue-400",
|
|
35
35
|
],
|
|
36
36
|
},
|
|
37
37
|
},
|
|
@@ -5,7 +5,7 @@ export const labelVariants: import("tailwind-variants").TVReturnType<{
|
|
|
5
5
|
hidden: {
|
|
6
6
|
true: string;
|
|
7
7
|
};
|
|
8
|
-
}, undefined, "
|
|
8
|
+
}, undefined, "ui-select-label text-neutral-500", {
|
|
9
9
|
error: {
|
|
10
10
|
true: string;
|
|
11
11
|
};
|
|
@@ -19,4 +19,4 @@ export const labelVariants: import("tailwind-variants").TVReturnType<{
|
|
|
19
19
|
hidden: {
|
|
20
20
|
true: string;
|
|
21
21
|
};
|
|
22
|
-
}, undefined, "
|
|
22
|
+
}, undefined, "ui-select-label text-neutral-500", unknown, unknown, undefined>>;
|
|
@@ -6,7 +6,7 @@ const selectVariants = tv({
|
|
|
6
6
|
slots: {
|
|
7
7
|
base: [
|
|
8
8
|
"flex items-center",
|
|
9
|
-
"[&::picker(select)]:my-1 [&::picker(select)]:rounded [&::picker(select)]:border-0 [&::picker(select)]:p-1",
|
|
9
|
+
"[&::picker(select)]:my-1 [&::picker(select)]:rounded-sm [&::picker(select)]:border-0 [&::picker(select)]:p-1",
|
|
10
10
|
"[&::picker-icon]:hidden",
|
|
11
11
|
"[&::picker(select)]:bg-white [&::picker(select)]:shadow-sm dark:[&::picker(select)]:bg-neutral-800",
|
|
12
12
|
"bg-none after:bg-current after:bg-size-[100%] after:bg-center after:bg-no-repeat",
|
|
@@ -14,7 +14,7 @@ const selectVariants = tv({
|
|
|
14
14
|
"[&:open]:after:rotate-180",
|
|
15
15
|
],
|
|
16
16
|
option: [
|
|
17
|
-
"flex rounded p-2
|
|
17
|
+
"flex rounded-sm p-2 ui-select-label",
|
|
18
18
|
"bg-white dark:bg-neutral-800",
|
|
19
19
|
"bg-linear-to-r to-transparent",
|
|
20
20
|
"[&::checkmark]:order-1 [&::checkmark]:ml-auto [&::checkmark]:shrink-0",
|
|
@@ -2,12 +2,12 @@ import { tv } from "tailwind-variants";
|
|
|
2
2
|
|
|
3
3
|
const notificationVariants = tv({
|
|
4
4
|
slots: {
|
|
5
|
-
base: "grid grid-cols-[40px_1fr_24px] gap-2 rounded-lg border
|
|
6
|
-
close: "col-
|
|
5
|
+
base: "grid grid-cols-[40px_1fr_24px] gap-2 rounded-lg border border-neutral-300 bg-white p-2 dark:border-neutral-700 dark:bg-neutral-900",
|
|
6
|
+
close: "col-3 self-start",
|
|
7
7
|
icon: "flex h-10 w-10 items-center justify-center rounded-sm text-white",
|
|
8
8
|
content: "flex flex-col justify-center",
|
|
9
|
-
message: "
|
|
10
|
-
title: "body-
|
|
9
|
+
message: "ui-caption-helper text-neutral-500",
|
|
10
|
+
title: "body-small dark:text-white",
|
|
11
11
|
},
|
|
12
12
|
variants: {
|
|
13
13
|
variant: {
|
|
@@ -2,8 +2,8 @@ import { tv } from "tailwind-variants";
|
|
|
2
2
|
|
|
3
3
|
const colorSwatchVariants = tv({
|
|
4
4
|
slots: {
|
|
5
|
-
base: "flex cursor-pointer items-center justify-center gap-1 rounded transition duration-200 select-none hover:bg-neutral-300 dark:hover:bg-neutral-700",
|
|
6
|
-
iconText: "flex h-4 w-4 items-center justify-center rounded",
|
|
5
|
+
base: "flex cursor-pointer items-center justify-center gap-1 rounded-sm transition duration-200 select-none hover:bg-neutral-300 dark:hover:bg-neutral-700",
|
|
6
|
+
iconText: "flex h-4 w-4 items-center justify-center rounded-sm",
|
|
7
7
|
iconArrow: "dark:text-white",
|
|
8
8
|
},
|
|
9
9
|
variants: {
|
|
@@ -2,7 +2,7 @@ import { tv } from "tailwind-variants";
|
|
|
2
2
|
|
|
3
3
|
const richTextEditorVariants = tv({
|
|
4
4
|
slots: {
|
|
5
|
-
base: "body-
|
|
5
|
+
base: "body-small text-neutral-800 dark:text-white",
|
|
6
6
|
toolbar:
|
|
7
7
|
"pointer flex gap-0.5 rounded-t-sm border border-b-0 border-neutral-300 px-5 py-2 dark:border-neutral-700",
|
|
8
8
|
field: [
|
|
@@ -2,11 +2,12 @@ import { tv } from "tailwind-variants";
|
|
|
2
2
|
|
|
3
3
|
const selectorCardVariants = tv({
|
|
4
4
|
slots: {
|
|
5
|
-
base: "flex cursor-pointer gap-3 rounded-
|
|
6
|
-
content: "
|
|
7
|
-
helperText: "
|
|
5
|
+
base: "flex cursor-pointer gap-3 rounded-lg border border-neutral-300 bg-white px-4 py-3 dark:border-neutral-700 dark:bg-neutral-800",
|
|
6
|
+
content: "",
|
|
7
|
+
helperText: "ui-caption-helper text-neutral-500",
|
|
8
8
|
input: "shrink-0",
|
|
9
|
-
label:
|
|
9
|
+
label:
|
|
10
|
+
"mb-0.5 cursor-[inherit] ui-title-2 text-neutral-900 dark:text-neutral-200",
|
|
10
11
|
},
|
|
11
12
|
variants: {
|
|
12
13
|
disabled: {
|
|
@@ -16,7 +17,7 @@ const selectorCardVariants = tv({
|
|
|
16
17
|
},
|
|
17
18
|
error: {
|
|
18
19
|
true: {
|
|
19
|
-
base: "
|
|
20
|
+
base: "border-red-500! outline-1 outline-red-500!",
|
|
20
21
|
label: "text-red-500 dark:text-red-500",
|
|
21
22
|
},
|
|
22
23
|
},
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { Icon } from "../../../index.js";
|
|
3
|
+
import { onDestroy, onMount } from "svelte";
|
|
4
|
+
import { tagSelectorVariants } from "./tagSelector.variants.js";
|
|
5
|
+
|
|
6
|
+
let {
|
|
7
|
+
filtered,
|
|
8
|
+
highlighted,
|
|
9
|
+
inputRef,
|
|
10
|
+
labelCreate,
|
|
11
|
+
localValues,
|
|
12
|
+
onCreateItem,
|
|
13
|
+
onSelectItem,
|
|
14
|
+
query,
|
|
15
|
+
showCreate,
|
|
16
|
+
value = [],
|
|
17
|
+
...rest
|
|
18
|
+
} = $props();
|
|
19
|
+
|
|
20
|
+
let styles = $derived(tagSelectorVariants());
|
|
21
|
+
|
|
22
|
+
let dropdownPosition = $state("bottom");
|
|
23
|
+
let dropdownMaxHeight = $state(200);
|
|
24
|
+
|
|
25
|
+
const attachHighlightedItem = (item) => {
|
|
26
|
+
if (inputRef) {
|
|
27
|
+
// so the screen reader can read the highlighted option aloud
|
|
28
|
+
if (item.id) inputRef.setAttribute("aria-activedescendant", item.id);
|
|
29
|
+
else inputRef.removeAttribute("aria-activedescendant");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// maybe scroll item into view
|
|
33
|
+
if (item.offsetTop < item.offsetParent.scrollTop)
|
|
34
|
+
item.scrollIntoView({ block: "nearest" });
|
|
35
|
+
else if (
|
|
36
|
+
item.offsetTop + item.offsetHeight >
|
|
37
|
+
item.offsetParent.scrollTop + item.offsetParent.offsetHeight
|
|
38
|
+
)
|
|
39
|
+
item.scrollIntoView({ block: "nearest" });
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const updateDropdownPosition = () => {
|
|
43
|
+
if (!inputRef) return;
|
|
44
|
+
|
|
45
|
+
const rect = inputRef.getBoundingClientRect();
|
|
46
|
+
const spaceBelow = window.innerHeight - rect.bottom;
|
|
47
|
+
const spaceAbove = rect.top;
|
|
48
|
+
|
|
49
|
+
dropdownPosition = spaceBelow > spaceAbove ? "bottom" : "top";
|
|
50
|
+
|
|
51
|
+
dropdownMaxHeight =
|
|
52
|
+
dropdownPosition === "bottom"
|
|
53
|
+
? Math.min(300, spaceBelow - 24)
|
|
54
|
+
: Math.min(300, spaceAbove - 24);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const getOptionId = (itemId) =>
|
|
58
|
+
`${localValues?.id ?? "tagselector"}-option-${itemId}`;
|
|
59
|
+
|
|
60
|
+
onMount(() => updateDropdownPosition());
|
|
61
|
+
onDestroy(() => {
|
|
62
|
+
inputRef?.removeAttribute("aria-activedescendant");
|
|
63
|
+
});
|
|
64
|
+
</script>
|
|
65
|
+
|
|
66
|
+
<svelte:window onscroll={updateDropdownPosition} onresize={updateDropdownPosition} />
|
|
67
|
+
|
|
68
|
+
<div
|
|
69
|
+
class={styles.dropdown({ dropdownPosition })}
|
|
70
|
+
id={localValues.id}
|
|
71
|
+
role="listbox"
|
|
72
|
+
style={`max-height: ${dropdownMaxHeight}px`}
|
|
73
|
+
tabindex="-1"
|
|
74
|
+
{...rest}>
|
|
75
|
+
{#snippet option({ highlighted, id, label, selected, ...rest })}
|
|
76
|
+
<button
|
|
77
|
+
aria-selected={selected}
|
|
78
|
+
{@attach highlighted ? attachHighlightedItem : null}
|
|
79
|
+
class={styles.dropdownItem()}
|
|
80
|
+
data-highlighted={highlighted}
|
|
81
|
+
{id}
|
|
82
|
+
role="option"
|
|
83
|
+
tabindex="-1"
|
|
84
|
+
{...rest}>
|
|
85
|
+
{label}
|
|
86
|
+
{#if selected}
|
|
87
|
+
<Icon class={styles.dropdownCheckmark()} name="check" size={16} />
|
|
88
|
+
{/if}
|
|
89
|
+
</button>
|
|
90
|
+
{/snippet}
|
|
91
|
+
|
|
92
|
+
{#each filtered as item, i (item.id)}
|
|
93
|
+
{@render option({
|
|
94
|
+
highlighted: highlighted === i,
|
|
95
|
+
id: getOptionId(item.id),
|
|
96
|
+
label: item.label,
|
|
97
|
+
selected: value.includes(item.id),
|
|
98
|
+
onclick: () => onSelectItem(item.id),
|
|
99
|
+
})}
|
|
100
|
+
{/each}
|
|
101
|
+
|
|
102
|
+
{#if showCreate}
|
|
103
|
+
{@render option({
|
|
104
|
+
highlighted: highlighted === filtered.length,
|
|
105
|
+
id: getOptionId("create"),
|
|
106
|
+
label: `${labelCreate}: "${query}"`,
|
|
107
|
+
selected: false,
|
|
108
|
+
onclick: onCreateItem,
|
|
109
|
+
})}
|
|
110
|
+
{/if}
|
|
111
|
+
</div>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export default Dropdown;
|
|
2
|
+
type Dropdown = {
|
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
|
+
};
|
|
6
|
+
declare const Dropdown: import("svelte").Component<{
|
|
7
|
+
filtered: any;
|
|
8
|
+
highlighted: any;
|
|
9
|
+
inputRef: any;
|
|
10
|
+
labelCreate: any;
|
|
11
|
+
localValues: any;
|
|
12
|
+
onCreateItem: any;
|
|
13
|
+
onSelectItem: any;
|
|
14
|
+
query: any;
|
|
15
|
+
showCreate: any;
|
|
16
|
+
value?: any[];
|
|
17
|
+
} & Record<string, any>, {}, "">;
|
|
18
|
+
type $$ComponentProps = {
|
|
19
|
+
filtered: any;
|
|
20
|
+
highlighted: any;
|
|
21
|
+
inputRef: any;
|
|
22
|
+
labelCreate: any;
|
|
23
|
+
localValues: any;
|
|
24
|
+
onCreateItem: any;
|
|
25
|
+
onSelectItem: any;
|
|
26
|
+
query: any;
|
|
27
|
+
showCreate: any;
|
|
28
|
+
value?: any[];
|
|
29
|
+
} & Record<string, any>;
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { getContext } from "svelte";
|
|
3
|
+
import Dropdown from "./Dropdown.svelte";
|
|
4
|
+
import ValueList from "./ValueList.svelte";
|
|
5
|
+
import { tagSelectorVariants } from "./tagSelector.variants.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @typedef {Object} Props
|
|
9
|
+
* @property {boolean} [autofocus=false] Whether the input should be autofocused on mount
|
|
10
|
+
* @property {string} [class=""] Additional CSS classes to apply to the component
|
|
11
|
+
* @property {boolean} [disabled=false] Whether the component is disabled
|
|
12
|
+
* @property {string} [id=""] The ID of the input element
|
|
13
|
+
* @property {string} [labelCreate=""] The label for the create new tag option
|
|
14
|
+
* @property {string} [name] The name of the input, used for form submission
|
|
15
|
+
* @property {Array<{id: string | number, label: string}>} [options=[]] The available tag options
|
|
16
|
+
* @property {string} [placeholder=""] The placeholder text for the input
|
|
17
|
+
* @property {Array<string | number>} [value=[]] The currently selected tags, bound to the component
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/** @type {Props} */
|
|
21
|
+
let {
|
|
22
|
+
autofocus = false,
|
|
23
|
+
class: className = "",
|
|
24
|
+
disabled = false,
|
|
25
|
+
id = "",
|
|
26
|
+
labelCreate = "",
|
|
27
|
+
name,
|
|
28
|
+
options = [],
|
|
29
|
+
placeholder = "",
|
|
30
|
+
value = $bindable([]),
|
|
31
|
+
...rest
|
|
32
|
+
} = $props();
|
|
33
|
+
|
|
34
|
+
let store = getContext("form-field-store");
|
|
35
|
+
|
|
36
|
+
let localValues = $derived.by(() => {
|
|
37
|
+
if (store) {
|
|
38
|
+
return {
|
|
39
|
+
...store(),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
disabled,
|
|
45
|
+
id,
|
|
46
|
+
};
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
let open = $state(false);
|
|
50
|
+
let query = $state("");
|
|
51
|
+
let highlighted = $state(-1);
|
|
52
|
+
|
|
53
|
+
let inputRef = $state();
|
|
54
|
+
let rootRef = $state();
|
|
55
|
+
|
|
56
|
+
let normalized = $derived(query.trim().toLowerCase());
|
|
57
|
+
|
|
58
|
+
let filtered = $derived(
|
|
59
|
+
options.filter((i) => i.label.toLowerCase().includes(normalized)),
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
let showCreate = $derived(
|
|
63
|
+
normalized.length > 0 &&
|
|
64
|
+
!filtered.some((i) => i.label.toLowerCase() === normalized),
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
$effect(() => {
|
|
68
|
+
if (autofocus && inputRef) inputRef.focus();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const openDropdown = () => {
|
|
72
|
+
open = true;
|
|
73
|
+
highlighted = -1;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const closeDropdown = () => {
|
|
77
|
+
open = false;
|
|
78
|
+
highlighted = -1;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const dispatchChange = () => {
|
|
82
|
+
rootRef.dispatchEvent(new CustomEvent("change", { detail: value }));
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const dispatchCreate = (item) => {
|
|
86
|
+
rootRef.dispatchEvent(new CustomEvent("create", { detail: item }));
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const refocusInput = () => {
|
|
90
|
+
query = "";
|
|
91
|
+
open = true;
|
|
92
|
+
requestAnimationFrame(() => inputRef?.focus());
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const removeItem = (id) => {
|
|
96
|
+
value = value.filter((vId) => vId !== id);
|
|
97
|
+
dispatchChange();
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const selectItem = (id) => {
|
|
101
|
+
if (value.includes(id)) {
|
|
102
|
+
removeItem(id);
|
|
103
|
+
} else {
|
|
104
|
+
value = [...value, id];
|
|
105
|
+
dispatchChange();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
refocusInput();
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const handleFocusOut = (e) => {
|
|
112
|
+
const next = e.relatedTarget;
|
|
113
|
+
|
|
114
|
+
if (next && rootRef.contains(next)) return;
|
|
115
|
+
if (document.activeElement === inputRef) return;
|
|
116
|
+
|
|
117
|
+
closeDropdown();
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const createItem = () => {
|
|
121
|
+
const label = query.trim();
|
|
122
|
+
if (label === "") return; // also reset here?
|
|
123
|
+
|
|
124
|
+
const lower = label.toLowerCase();
|
|
125
|
+
|
|
126
|
+
const preset = options.find((i) => i.label.toLowerCase() === lower);
|
|
127
|
+
|
|
128
|
+
if (preset) {
|
|
129
|
+
const alreadySelected = value.includes(preset.id);
|
|
130
|
+
|
|
131
|
+
if (!alreadySelected) {
|
|
132
|
+
value = [...value, preset.id];
|
|
133
|
+
dispatchChange();
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
refocusInput();
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const selectedMatch = value.find(
|
|
141
|
+
(id) => typeof id === "string" && id.toLowerCase() === lower,
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
if (!selectedMatch) {
|
|
145
|
+
value = [...value, label];
|
|
146
|
+
dispatchChange();
|
|
147
|
+
dispatchCreate(label);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
refocusInput();
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
const handleKey = (e) => {
|
|
154
|
+
if (!open) return;
|
|
155
|
+
|
|
156
|
+
const total = filtered.length + (showCreate ? 1 : 0);
|
|
157
|
+
const presetHighlighted = highlighted >= 0 && highlighted < filtered.length;
|
|
158
|
+
|
|
159
|
+
if (e.key === "ArrowDown") {
|
|
160
|
+
e.preventDefault();
|
|
161
|
+
highlighted = (highlighted + 1) % total;
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (e.key === "ArrowUp") {
|
|
166
|
+
e.preventDefault();
|
|
167
|
+
highlighted = (highlighted - 1 + total) % total;
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (e.key === "Escape") {
|
|
172
|
+
e.preventDefault();
|
|
173
|
+
if (normalized === "") open = false;
|
|
174
|
+
else refocusInput();
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (e.key === "Enter" || (e.key === " " && presetHighlighted)) {
|
|
179
|
+
e.preventDefault();
|
|
180
|
+
|
|
181
|
+
if (presetHighlighted) {
|
|
182
|
+
selectItem(filtered[highlighted].id);
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (showCreate && highlighted === filtered.length) {
|
|
187
|
+
createItem();
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (normalized !== "") {
|
|
192
|
+
createItem();
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
let styles = $derived(tagSelectorVariants({ disabled: localValues.disabled }));
|
|
199
|
+
</script>
|
|
200
|
+
|
|
201
|
+
<div
|
|
202
|
+
bind:this={rootRef}
|
|
203
|
+
aria-expanded={open}
|
|
204
|
+
class={styles.base({ class: className })}
|
|
205
|
+
onfocusout={handleFocusOut}
|
|
206
|
+
role="combobox"
|
|
207
|
+
{...open
|
|
208
|
+
? {
|
|
209
|
+
"aria-controls": localValues.id,
|
|
210
|
+
"aria-haspopup": "listbox",
|
|
211
|
+
"aria-owns": localValues.id,
|
|
212
|
+
}
|
|
213
|
+
: {}}
|
|
214
|
+
{...rest}>
|
|
215
|
+
<input {name} readonly type="hidden" value={value.join(",")} />
|
|
216
|
+
<ValueList
|
|
217
|
+
bind:inputRef
|
|
218
|
+
{localValues}
|
|
219
|
+
onKeydown={handleKey}
|
|
220
|
+
onOpenDropdown={openDropdown}
|
|
221
|
+
onRemoveItem={removeItem}
|
|
222
|
+
{open}
|
|
223
|
+
{options}
|
|
224
|
+
{placeholder}
|
|
225
|
+
bind:query
|
|
226
|
+
{value} />
|
|
227
|
+
|
|
228
|
+
{#if open}
|
|
229
|
+
<Dropdown
|
|
230
|
+
{filtered}
|
|
231
|
+
{highlighted}
|
|
232
|
+
{inputRef}
|
|
233
|
+
{labelCreate}
|
|
234
|
+
{localValues}
|
|
235
|
+
onCreateItem={createItem}
|
|
236
|
+
onSelectItem={selectItem}
|
|
237
|
+
{query}
|
|
238
|
+
{showCreate}
|
|
239
|
+
{value} />
|
|
240
|
+
{/if}
|
|
241
|
+
</div>
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
export default TagSelector;
|
|
2
|
+
type TagSelector = {
|
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
+
$set?(props: Partial<Props>): void;
|
|
5
|
+
};
|
|
6
|
+
declare const TagSelector: import("svelte").Component<{
|
|
7
|
+
/**
|
|
8
|
+
* Whether the input should be autofocused on mount
|
|
9
|
+
*/
|
|
10
|
+
autofocus?: boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Additional CSS classes to apply to the component
|
|
13
|
+
*/
|
|
14
|
+
class?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Whether the component is disabled
|
|
17
|
+
*/
|
|
18
|
+
disabled?: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* The ID of the input element
|
|
21
|
+
*/
|
|
22
|
+
id?: string;
|
|
23
|
+
/**
|
|
24
|
+
* The label for the create new tag option
|
|
25
|
+
*/
|
|
26
|
+
labelCreate?: string;
|
|
27
|
+
/**
|
|
28
|
+
* The name of the input, used for form submission
|
|
29
|
+
*/
|
|
30
|
+
name?: string;
|
|
31
|
+
/**
|
|
32
|
+
* The available tag options
|
|
33
|
+
*/
|
|
34
|
+
options?: Array<{
|
|
35
|
+
id: string | number;
|
|
36
|
+
label: string;
|
|
37
|
+
}>;
|
|
38
|
+
/**
|
|
39
|
+
* The placeholder text for the input
|
|
40
|
+
*/
|
|
41
|
+
placeholder?: string;
|
|
42
|
+
/**
|
|
43
|
+
* The currently selected tags, bound to the component
|
|
44
|
+
*/
|
|
45
|
+
value?: Array<string | number>;
|
|
46
|
+
}, {}, "value">;
|
|
47
|
+
type Props = {
|
|
48
|
+
/**
|
|
49
|
+
* Whether the input should be autofocused on mount
|
|
50
|
+
*/
|
|
51
|
+
autofocus?: boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Additional CSS classes to apply to the component
|
|
54
|
+
*/
|
|
55
|
+
class?: string;
|
|
56
|
+
/**
|
|
57
|
+
* Whether the component is disabled
|
|
58
|
+
*/
|
|
59
|
+
disabled?: boolean;
|
|
60
|
+
/**
|
|
61
|
+
* The ID of the input element
|
|
62
|
+
*/
|
|
63
|
+
id?: string;
|
|
64
|
+
/**
|
|
65
|
+
* The label for the create new tag option
|
|
66
|
+
*/
|
|
67
|
+
labelCreate?: string;
|
|
68
|
+
/**
|
|
69
|
+
* The name of the input, used for form submission
|
|
70
|
+
*/
|
|
71
|
+
name?: string;
|
|
72
|
+
/**
|
|
73
|
+
* The available tag options
|
|
74
|
+
*/
|
|
75
|
+
options?: Array<{
|
|
76
|
+
id: string | number;
|
|
77
|
+
label: string;
|
|
78
|
+
}>;
|
|
79
|
+
/**
|
|
80
|
+
* The placeholder text for the input
|
|
81
|
+
*/
|
|
82
|
+
placeholder?: string;
|
|
83
|
+
/**
|
|
84
|
+
* The currently selected tags, bound to the component
|
|
85
|
+
*/
|
|
86
|
+
value?: Array<string | number>;
|
|
87
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { Chip } from "../../../index.js";
|
|
3
|
+
import { tagSelectorVariants } from "./tagSelector.variants.js";
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
inputRef = $bindable(),
|
|
7
|
+
localValues,
|
|
8
|
+
onKeydown,
|
|
9
|
+
onOpenDropdown,
|
|
10
|
+
onRemoveItem,
|
|
11
|
+
open = false,
|
|
12
|
+
options = [],
|
|
13
|
+
placeholder,
|
|
14
|
+
query = $bindable(),
|
|
15
|
+
value = [],
|
|
16
|
+
} = $props();
|
|
17
|
+
|
|
18
|
+
let styles = $derived(tagSelectorVariants());
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<div class={styles.valueList()}>
|
|
22
|
+
{#each value as s (s)}
|
|
23
|
+
{@const opt = options.find((i) => i.id === s)}
|
|
24
|
+
<Chip
|
|
25
|
+
class={styles.item()}
|
|
26
|
+
clickable
|
|
27
|
+
icon="closeSmall"
|
|
28
|
+
onclick={() => onRemoveItem(s)}>
|
|
29
|
+
{opt?.label ?? s}
|
|
30
|
+
</Chip>
|
|
31
|
+
{/each}
|
|
32
|
+
|
|
33
|
+
{#if !localValues.disabled}
|
|
34
|
+
<Chip class={styles.searchField()} icon="add" variant="outline">
|
|
35
|
+
<input
|
|
36
|
+
bind:this={inputRef}
|
|
37
|
+
bind:value={query}
|
|
38
|
+
autocomplete="off"
|
|
39
|
+
class={styles.searchInput()}
|
|
40
|
+
onfocus={onOpenDropdown}
|
|
41
|
+
oninput={onOpenDropdown}
|
|
42
|
+
onkeydown={onKeydown}
|
|
43
|
+
{...open
|
|
44
|
+
? {
|
|
45
|
+
"aria-controls": localValues.id,
|
|
46
|
+
}
|
|
47
|
+
: {}}
|
|
48
|
+
{placeholder}
|
|
49
|
+
role="searchbox" />
|
|
50
|
+
</Chip>
|
|
51
|
+
{/if}
|
|
52
|
+
</div>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export default ValueList;
|
|
2
|
+
type ValueList = {
|
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
|
+
};
|
|
6
|
+
declare const ValueList: import("svelte").Component<{
|
|
7
|
+
inputRef?: any;
|
|
8
|
+
localValues: any;
|
|
9
|
+
onKeydown: any;
|
|
10
|
+
onOpenDropdown: any;
|
|
11
|
+
onRemoveItem: any;
|
|
12
|
+
open?: boolean;
|
|
13
|
+
options?: any[];
|
|
14
|
+
placeholder: any;
|
|
15
|
+
query?: any;
|
|
16
|
+
value?: any[];
|
|
17
|
+
}, {}, "inputRef" | "query">;
|
|
18
|
+
type $$ComponentProps = {
|
|
19
|
+
inputRef?: any;
|
|
20
|
+
localValues: any;
|
|
21
|
+
onKeydown: any;
|
|
22
|
+
onOpenDropdown: any;
|
|
23
|
+
onRemoveItem: any;
|
|
24
|
+
open?: boolean;
|
|
25
|
+
options?: any[];
|
|
26
|
+
placeholder: any;
|
|
27
|
+
query?: any;
|
|
28
|
+
value?: any[];
|
|
29
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
export const tagSelectorVariants: import("tailwind-variants").TVReturnType<{
|
|
2
|
+
dropdownPosition: {
|
|
3
|
+
bottom: {
|
|
4
|
+
dropdown: string;
|
|
5
|
+
};
|
|
6
|
+
top: {
|
|
7
|
+
dropdown: string;
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
}, {
|
|
11
|
+
base: string;
|
|
12
|
+
valueList: string;
|
|
13
|
+
item: string;
|
|
14
|
+
searchField: string;
|
|
15
|
+
searchInput: string;
|
|
16
|
+
dropdown: string;
|
|
17
|
+
dropdownCheckmark: string;
|
|
18
|
+
dropdownItem: string[];
|
|
19
|
+
}, undefined, {
|
|
20
|
+
disabled: {
|
|
21
|
+
true: {
|
|
22
|
+
base: string;
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
error: {
|
|
26
|
+
true: {
|
|
27
|
+
base: string;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
}, {
|
|
31
|
+
base: string[];
|
|
32
|
+
}, import("tailwind-variants").TVReturnType<{
|
|
33
|
+
disabled: {
|
|
34
|
+
true: {
|
|
35
|
+
base: string;
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
error: {
|
|
39
|
+
true: {
|
|
40
|
+
base: string;
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
}, {
|
|
44
|
+
base: string[];
|
|
45
|
+
}, undefined, {
|
|
46
|
+
disabled: {
|
|
47
|
+
true: {
|
|
48
|
+
base: string;
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
error: {
|
|
52
|
+
true: {
|
|
53
|
+
base: string;
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
}, {
|
|
57
|
+
base: string[];
|
|
58
|
+
}, import("tailwind-variants").TVReturnType<{
|
|
59
|
+
disabled: {
|
|
60
|
+
true: {
|
|
61
|
+
base: string;
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
error: {
|
|
65
|
+
true: {
|
|
66
|
+
base: string;
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
}, {
|
|
70
|
+
base: string[];
|
|
71
|
+
}, undefined, unknown, unknown, undefined>>>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { tv } from "tailwind-variants";
|
|
2
|
+
import { inputBaseVariant } from "../../atoms/input/input.variants.js";
|
|
3
|
+
|
|
4
|
+
const tagSelectorVariants = tv({
|
|
5
|
+
extend: inputBaseVariant,
|
|
6
|
+
slots: {
|
|
7
|
+
base: "relative min-h-10 p-1",
|
|
8
|
+
valueList: "flex flex-wrap gap-1",
|
|
9
|
+
item: "h-7.5",
|
|
10
|
+
searchField: "relative h-7.5 w-30",
|
|
11
|
+
searchInput:
|
|
12
|
+
"absolute inset-0 appearance-none border-0 bg-transparent pr-1 pl-8 ui-tag-badge placeholder-current focus:ring-0",
|
|
13
|
+
dropdown:
|
|
14
|
+
"absolute w-full max-w-100 overflow-y-auto bg-white p-1 pr-5 shadow-sm dark:border-neutral-700 dark:bg-neutral-800",
|
|
15
|
+
dropdownCheckmark: "ml-auto shrink-0",
|
|
16
|
+
dropdownItem: [
|
|
17
|
+
"flex w-full grow items-center gap-2 rounded-sm p-2 ui-select-label",
|
|
18
|
+
"bg-white dark:bg-neutral-800",
|
|
19
|
+
"bg-linear-to-r to-transparent",
|
|
20
|
+
"cursor-pointer text-neutral-900 hover:from-black/15 dark:text-neutral-200 dark:hover:from-white/15",
|
|
21
|
+
"data-[highlighted=true]:from-black/15 dark:data-[highlighted=true]:from-white/15",
|
|
22
|
+
],
|
|
23
|
+
},
|
|
24
|
+
variants: {
|
|
25
|
+
dropdownPosition: {
|
|
26
|
+
bottom: {
|
|
27
|
+
dropdown: "top-full mt-1",
|
|
28
|
+
},
|
|
29
|
+
top: {
|
|
30
|
+
dropdown: "bottom-full mb-1",
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
export { tagSelectorVariants };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import { getContext } from "svelte";
|
|
3
|
+
import { Icon } from "../../../index.js";
|
|
3
4
|
import {
|
|
4
5
|
toggleGroupItemVariants,
|
|
5
6
|
toggleGroupItemTabVariants,
|
|
@@ -9,15 +10,16 @@
|
|
|
9
10
|
* @typedef {Object} Props
|
|
10
11
|
* @property {import('svelte').Snippet} [children] Children content – can be text, HTML, or other Svelte components
|
|
11
12
|
* @property {string} [class=""] Additional CSS classes to apply to the component
|
|
13
|
+
* @property {string} [icon] The name of the icon to display alongside the content
|
|
12
14
|
* @property {string} [value=""] The value of the toggle group item
|
|
13
15
|
*/
|
|
14
16
|
|
|
15
17
|
/** @type {Props} */
|
|
16
|
-
let { children, class: className = "", value: localeValue } = $props();
|
|
18
|
+
let { children, class: className = "", icon, value: localeValue } = $props();
|
|
17
19
|
|
|
18
|
-
let store = getContext("toggle-group-store");
|
|
19
|
-
let { appearance, send, receive, size, variant } = $derived(store
|
|
20
|
-
let isActive = $derived(store
|
|
20
|
+
let store = $derived(getContext("toggle-group-store")());
|
|
21
|
+
let { appearance, send, receive, size, variant } = $derived(store);
|
|
22
|
+
let isActive = $derived(store.value === localeValue);
|
|
21
23
|
|
|
22
24
|
let stylesByVariant = $derived.by(() => {
|
|
23
25
|
if (variant === "tabs") {
|
|
@@ -33,10 +35,18 @@
|
|
|
33
35
|
class={stylesByVariant.button({ appearance, class: className, isActive, size })}
|
|
34
36
|
role="tab"
|
|
35
37
|
onclick={() => {
|
|
36
|
-
store
|
|
38
|
+
store.update(localeValue);
|
|
37
39
|
}}
|
|
38
40
|
type="button">
|
|
39
|
-
<span class={stylesByVariant.span()}>
|
|
41
|
+
<span class={stylesByVariant.span()}>
|
|
42
|
+
{#if icon}
|
|
43
|
+
<Icon
|
|
44
|
+
class={stylesByVariant.icon()}
|
|
45
|
+
name={icon}
|
|
46
|
+
size={variant === "tabs" || size === "small" ? 16 : 24} />
|
|
47
|
+
{/if}
|
|
48
|
+
{@render children?.()}
|
|
49
|
+
</span>
|
|
40
50
|
{#if isActive}
|
|
41
51
|
<div
|
|
42
52
|
class={stylesByVariant.trigger({ appearance })}
|
|
@@ -12,6 +12,10 @@ declare const ToggleGroupItem: import("svelte").Component<{
|
|
|
12
12
|
* Additional CSS classes to apply to the component
|
|
13
13
|
*/
|
|
14
14
|
class?: string;
|
|
15
|
+
/**
|
|
16
|
+
* The name of the icon to display alongside the content
|
|
17
|
+
*/
|
|
18
|
+
icon?: string;
|
|
15
19
|
/**
|
|
16
20
|
* The value of the toggle group item
|
|
17
21
|
*/
|
|
@@ -26,6 +30,10 @@ type Props = {
|
|
|
26
30
|
* Additional CSS classes to apply to the component
|
|
27
31
|
*/
|
|
28
32
|
class?: string;
|
|
33
|
+
/**
|
|
34
|
+
* The name of the icon to display alongside the content
|
|
35
|
+
*/
|
|
36
|
+
icon?: string;
|
|
29
37
|
/**
|
|
30
38
|
* The value of the toggle group item
|
|
31
39
|
*/
|
|
@@ -2,7 +2,7 @@ import { tv } from "tailwind-variants";
|
|
|
2
2
|
|
|
3
3
|
const toggleGroupVariants = tv({
|
|
4
4
|
slots: {
|
|
5
|
-
base: "overflow-hidden rounded-
|
|
5
|
+
base: "overflow-hidden rounded-lg bg-current",
|
|
6
6
|
tabs: "flex gap-1",
|
|
7
7
|
},
|
|
8
8
|
variants: {
|
|
@@ -12,7 +12,7 @@ const toggleGroupVariants = tv({
|
|
|
12
12
|
},
|
|
13
13
|
size: {
|
|
14
14
|
small: {
|
|
15
|
-
tabs: "h-
|
|
15
|
+
tabs: "h-9 p-1",
|
|
16
16
|
},
|
|
17
17
|
medium: {
|
|
18
18
|
tabs: "h-10 p-1",
|
|
@@ -24,7 +24,7 @@ const toggleGroupVariants = tv({
|
|
|
24
24
|
const toggleGroupTabVariants = tv({
|
|
25
25
|
slots: {
|
|
26
26
|
base: "",
|
|
27
|
-
tabs: "flex h-
|
|
27
|
+
tabs: "flex h-9 gap-4 border-b border-neutral-300 dark:border-neutral-700",
|
|
28
28
|
},
|
|
29
29
|
});
|
|
30
30
|
|
|
@@ -18,6 +18,7 @@ export const toggleGroupItemVariants: import("tailwind-variants").TVReturnType<{
|
|
|
18
18
|
}, {
|
|
19
19
|
base: string;
|
|
20
20
|
button: string;
|
|
21
|
+
icon: string;
|
|
21
22
|
span: string;
|
|
22
23
|
trigger: string;
|
|
23
24
|
}, undefined, {
|
|
@@ -40,6 +41,7 @@ export const toggleGroupItemVariants: import("tailwind-variants").TVReturnType<{
|
|
|
40
41
|
}, {
|
|
41
42
|
base: string;
|
|
42
43
|
button: string;
|
|
44
|
+
icon: string;
|
|
43
45
|
span: string;
|
|
44
46
|
trigger: string;
|
|
45
47
|
}, import("tailwind-variants").TVReturnType<{
|
|
@@ -62,6 +64,7 @@ export const toggleGroupItemVariants: import("tailwind-variants").TVReturnType<{
|
|
|
62
64
|
}, {
|
|
63
65
|
base: string;
|
|
64
66
|
button: string;
|
|
67
|
+
icon: string;
|
|
65
68
|
span: string;
|
|
66
69
|
trigger: string;
|
|
67
70
|
}, undefined, unknown, unknown, undefined>>;
|
|
@@ -77,6 +80,7 @@ export const toggleGroupItemTabVariants: import("tailwind-variants").TVReturnTyp
|
|
|
77
80
|
}, {
|
|
78
81
|
base: string;
|
|
79
82
|
button: string;
|
|
83
|
+
icon: string;
|
|
80
84
|
span: string;
|
|
81
85
|
trigger: string;
|
|
82
86
|
}, undefined, {
|
|
@@ -91,6 +95,7 @@ export const toggleGroupItemTabVariants: import("tailwind-variants").TVReturnTyp
|
|
|
91
95
|
}, {
|
|
92
96
|
base: string;
|
|
93
97
|
button: string;
|
|
98
|
+
icon: string;
|
|
94
99
|
span: string;
|
|
95
100
|
trigger: string;
|
|
96
101
|
}, import("tailwind-variants").TVReturnType<{
|
|
@@ -105,6 +110,7 @@ export const toggleGroupItemTabVariants: import("tailwind-variants").TVReturnTyp
|
|
|
105
110
|
}, {
|
|
106
111
|
base: string;
|
|
107
112
|
button: string;
|
|
113
|
+
icon: string;
|
|
108
114
|
span: string;
|
|
109
115
|
trigger: string;
|
|
110
116
|
}, undefined, unknown, unknown, undefined>>;
|
|
@@ -4,8 +4,9 @@ const toggleGroupItemVariants = tv({
|
|
|
4
4
|
slots: {
|
|
5
5
|
base: "relative h-full w-full whitespace-nowrap",
|
|
6
6
|
button:
|
|
7
|
-
"block h-full w-full cursor-pointer rounded-sm bg-transparent button-small transition-[background,color]",
|
|
8
|
-
|
|
7
|
+
"block h-full w-full cursor-pointer rounded-sm bg-transparent ui-button-small transition-[background,color]",
|
|
8
|
+
icon: "shrink-0",
|
|
9
|
+
span: "relative z-2 flex items-center justify-center gap-2",
|
|
9
10
|
trigger: "absolute inset-0 z-1 rounded-sm bg-white shadow-sm",
|
|
10
11
|
},
|
|
11
12
|
variants: {
|
|
@@ -19,7 +20,7 @@ const toggleGroupItemVariants = tv({
|
|
|
19
20
|
},
|
|
20
21
|
size: {
|
|
21
22
|
small: {
|
|
22
|
-
button: "px-1
|
|
23
|
+
button: "px-1",
|
|
23
24
|
},
|
|
24
25
|
medium: {
|
|
25
26
|
button: "px-3",
|
|
@@ -48,19 +49,19 @@ const toggleGroupItemVariants = tv({
|
|
|
48
49
|
|
|
49
50
|
const toggleGroupItemTabVariants = tv({
|
|
50
51
|
slots: {
|
|
51
|
-
base: "relative h-full w-full
|
|
52
|
-
button: "h-full w-full cursor-pointer button-default transition-[color]",
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
base: "relative h-full w-full whitespace-nowrap text-neutral-900 dark:text-neutral-200",
|
|
53
|
+
button: "h-full w-full cursor-pointer ui-button-default transition-[color]",
|
|
54
|
+
icon: "shrink-0",
|
|
55
|
+
span: "flex items-center justify-center gap-1",
|
|
56
|
+
trigger: "absolute -bottom-px left-0 h-0.5 w-full rounded-t-xs bg-current",
|
|
56
57
|
},
|
|
57
58
|
variants: {
|
|
58
59
|
isActive: {
|
|
59
60
|
true: {
|
|
60
|
-
button: "text-
|
|
61
|
+
button: "text-current",
|
|
61
62
|
},
|
|
62
63
|
false: {
|
|
63
|
-
button: "text-neutral-500 hover:text-
|
|
64
|
+
button: "text-neutral-500 hover:text-current",
|
|
64
65
|
},
|
|
65
66
|
},
|
|
66
67
|
},
|
|
@@ -4,11 +4,12 @@ const modalVariants = tv({
|
|
|
4
4
|
slots: {
|
|
5
5
|
close: "ml-auto",
|
|
6
6
|
container:
|
|
7
|
-
"my-4 grid max-h-[calc(100vh-32px)] w-[calc(100vw-32px)] min-w-xs grid-rows-[auto_1fr] overflow-hidden rounded-
|
|
8
|
-
header:
|
|
7
|
+
"my-4 grid max-h-[calc(100vh-32px)] w-[calc(100vw-32px)] min-w-xs grid-rows-[auto_1fr] overflow-hidden rounded-lg bg-white body-small shadow-sm outline-8 outline-white/90 dark:bg-neutral-800 dark:text-neutral-200 dark:outline-neutral-800/90",
|
|
8
|
+
header:
|
|
9
|
+
"flex items-end border-b border-neutral-300 px-4 py-3 ui-title-2 dark:border-neutral-700",
|
|
9
10
|
body: "h-full overflow-auto",
|
|
10
11
|
overlay: "fixed inset-0 z-modal-backdrop bg-black/25",
|
|
11
|
-
positioner: "fixed inset-0 z-
|
|
12
|
+
positioner: "fixed inset-0 z-modal grid items-center justify-items-center",
|
|
12
13
|
trigger: "cursor-pointer",
|
|
13
14
|
},
|
|
14
15
|
variants: {
|
|
@@ -7,7 +7,7 @@ const popoverVariants = tv({
|
|
|
7
7
|
content:
|
|
8
8
|
"flex flex-col-reverse rounded-2xl bg-white shadow-sm dark:bg-neutral-800",
|
|
9
9
|
header:
|
|
10
|
-
"flex items-center justify-between gap-3 border-b border-gray-300
|
|
10
|
+
"flex items-center justify-between gap-3 border-b border-gray-300 ui-title-2 dark:text-neutral-200",
|
|
11
11
|
trigger: "cursor-pointer",
|
|
12
12
|
},
|
|
13
13
|
variants: {
|
package/dist/index.d.ts
CHANGED
|
@@ -25,6 +25,7 @@ export { default as FormLayout } from "./components/molecules/formLayout/FormLay
|
|
|
25
25
|
export { default as Notification } from "./components/molecules/notification/Notification.svelte";
|
|
26
26
|
export { default as RichTextEditor } from "./components/molecules/richTextEditor/RichTextEditor.svelte";
|
|
27
27
|
export { default as SelectorCard } from "./components/molecules/selectorCard/SelectorCard.svelte";
|
|
28
|
+
export { default as TagSelector } from "./components/molecules/tagSelector/TagSelector.svelte";
|
|
28
29
|
export { default as ToggleGroup } from "./components/molecules/toggleGroup/ToggleGroup.svelte";
|
|
29
30
|
export { default as ToggleGroupItem } from "./components/molecules/toggleGroup/ToggleGroupItem.svelte";
|
|
30
31
|
export * as Dialog from "./components/organisms/dialog/index.js";
|
package/dist/index.js
CHANGED
|
@@ -35,6 +35,7 @@ export { default as FormLayout } from "./components/molecules/formLayout/FormLay
|
|
|
35
35
|
export { default as Notification } from "./components/molecules/notification/Notification.svelte";
|
|
36
36
|
export { default as RichTextEditor } from "./components/molecules/richTextEditor/RichTextEditor.svelte";
|
|
37
37
|
export { default as SelectorCard } from "./components/molecules/selectorCard/SelectorCard.svelte";
|
|
38
|
+
export { default as TagSelector } from "./components/molecules/tagSelector/TagSelector.svelte";
|
|
38
39
|
export { default as ToggleGroup } from "./components/molecules/toggleGroup/ToggleGroup.svelte";
|
|
39
40
|
export { default as ToggleGroupItem } from "./components/molecules/toggleGroup/ToggleGroupItem.svelte";
|
|
40
41
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@websline/system-components",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.12",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"@sveltejs/kit": "^2.49.2",
|
|
54
54
|
"@sveltejs/package": "^2.5.7",
|
|
55
55
|
"@sveltejs/vite-plugin-svelte": "^6.2.1",
|
|
56
|
-
"@tailwindcss/forms": "^0.5.
|
|
56
|
+
"@tailwindcss/forms": "^0.5.11",
|
|
57
57
|
"@tailwindcss/typography": "^0.5.19",
|
|
58
58
|
"@tailwindcss/vite": "^4.1.18",
|
|
59
59
|
"@types/node": "^25.0.3",
|