@starwind-ui/core 1.11.1 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +13 -6
- package/dist/index.js.map +1 -1
- package/dist/src/components/alert-dialog/AlertDialogAction.astro +1 -1
- package/dist/src/components/alert-dialog/AlertDialogCancel.astro +1 -1
- package/dist/src/components/button-group/ButtonGroup.astro +62 -0
- package/dist/src/components/button-group/ButtonGroupSeparator.astro +27 -0
- package/dist/src/components/button-group/ButtonGroupText.astro +19 -0
- package/dist/src/components/button-group/index.ts +17 -0
- package/dist/src/components/dropdown/DropdownTrigger.astro +1 -1
- package/dist/src/components/pagination/PaginationEllipsis.astro +15 -5
- package/dist/src/components/pagination/PaginationNext.astro +6 -2
- package/dist/src/components/pagination/PaginationPrevious.astro +6 -2
- package/dist/src/components/select/Select.astro +235 -73
- package/dist/src/components/select/SelectContent.astro +13 -2
- package/dist/src/components/select/SelectItem.astro +8 -6
- package/dist/src/components/select/SelectLabel.astro +1 -1
- package/dist/src/components/select/SelectSearch.astro +49 -0
- package/dist/src/components/select/SelectTrigger.astro +25 -15
- package/dist/src/components/select/index.ts +4 -0
- package/dist/src/components/toggle/Toggle.astro +172 -0
- package/dist/src/components/toggle/ToggleTypes.ts +14 -0
- package/dist/src/components/toggle/index.ts +8 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -11,13 +11,19 @@ var registry_default = {
|
|
|
11
11
|
{
|
|
12
12
|
name: "alert-dialog",
|
|
13
13
|
type: "component",
|
|
14
|
-
version: "1.0.
|
|
14
|
+
version: "1.0.2",
|
|
15
15
|
dependencies: ["@starwind-ui/core/button@^2.1.0"]
|
|
16
16
|
},
|
|
17
17
|
{ name: "aspect-ratio", type: "component", version: "1.0.0", dependencies: [] },
|
|
18
|
-
{ name: "avatar", type: "component", version: "1.2.
|
|
18
|
+
{ name: "avatar", type: "component", version: "1.2.1", dependencies: [] },
|
|
19
19
|
{ name: "badge", type: "component", version: "1.3.0", dependencies: [] },
|
|
20
20
|
{ name: "breadcrumb", type: "component", version: "1.1.1", dependencies: [] },
|
|
21
|
+
{
|
|
22
|
+
name: "button-group",
|
|
23
|
+
type: "component",
|
|
24
|
+
version: "1.0.0",
|
|
25
|
+
dependencies: ["@starwind-ui/core/separator@^1.0.0"]
|
|
26
|
+
},
|
|
21
27
|
{ name: "button", type: "component", version: "2.2.0", dependencies: [] },
|
|
22
28
|
{ name: "card", type: "component", version: "1.3.0", dependencies: [] },
|
|
23
29
|
{
|
|
@@ -28,7 +34,7 @@ var registry_default = {
|
|
|
28
34
|
},
|
|
29
35
|
{ name: "checkbox", type: "component", version: "1.4.0", dependencies: [] },
|
|
30
36
|
{ name: "dialog", type: "component", version: "1.4.1", dependencies: [] },
|
|
31
|
-
{ name: "dropdown", type: "component", version: "1.2.
|
|
37
|
+
{ name: "dropdown", type: "component", version: "1.2.2", dependencies: [] },
|
|
32
38
|
{ name: "dropzone", type: "component", version: "1.2.0", dependencies: [] },
|
|
33
39
|
{ name: "input", type: "component", version: "1.3.0", dependencies: [] },
|
|
34
40
|
{
|
|
@@ -39,10 +45,10 @@ var registry_default = {
|
|
|
39
45
|
},
|
|
40
46
|
{ name: "kbd", type: "component", version: "1.0.0", dependencies: [] },
|
|
41
47
|
{ name: "label", type: "component", version: "1.2.0", dependencies: [] },
|
|
42
|
-
{ name: "pagination", type: "component", version: "3.0.
|
|
48
|
+
{ name: "pagination", type: "component", version: "3.0.2", dependencies: [] },
|
|
43
49
|
{ name: "progress", type: "component", version: "1.1.0", dependencies: [] },
|
|
44
|
-
{ name: "radio-group", type: "component", version: "1.2.
|
|
45
|
-
{ name: "select", type: "component", version: "1.
|
|
50
|
+
{ name: "radio-group", type: "component", version: "1.2.2", dependencies: [] },
|
|
51
|
+
{ name: "select", type: "component", version: "1.7.0", dependencies: [] },
|
|
46
52
|
{ name: "separator", type: "component", version: "1.0.0", dependencies: [] },
|
|
47
53
|
{
|
|
48
54
|
name: "sheet",
|
|
@@ -56,6 +62,7 @@ var registry_default = {
|
|
|
56
62
|
{ name: "table", type: "component", version: "1.1.0", dependencies: [] },
|
|
57
63
|
{ name: "tabs", type: "component", version: "1.4.0", dependencies: [] },
|
|
58
64
|
{ name: "textarea", type: "component", version: "1.3.0", dependencies: [] },
|
|
65
|
+
{ name: "toggle", type: "component", version: "1.0.0", dependencies: [] },
|
|
59
66
|
{ name: "tooltip", type: "component", version: "1.3.0", dependencies: [] }
|
|
60
67
|
]
|
|
61
68
|
};
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/registry.json"],"sourcesContent":["import { join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nimport componentRegistry from \"./registry.json\" with { type: \"json\" };\n\n/**\n * Component metadata interface describing a Starwind UI component\n */\nexport interface ComponentMeta {\n name: string;\n version: string;\n type: \"component\";\n dependencies: string[];\n}\n\n/**\n * Registry interface containing all available components\n */\nexport interface Registry {\n components: ComponentMeta[];\n}\n\nconst __dirname = fileURLToPath(new URL(\".\", import.meta.url));\n\n/**\n * Get the absolute path to a component file\n * @param {string} componentName - The name of the component\n * @param {string} fileName - The name of the file within the component\n * @returns {string} The absolute path to the component file\n */\nexport const getComponentPath = (componentName: string, fileName: string): string => {\n // In production (when installed as a dependency), the components will be in dist/src/components\n // In development, they will be in src/components\n const componentsDir = __dirname.includes(\"dist\") ? \"src/components\" : \"src/components\";\n return join(__dirname, componentsDir, componentName, fileName);\n};\n\n/**\n * Map of all components and their metadata from registry\n */\nexport const registry = componentRegistry.components as ComponentMeta[];\n","{\n \"$schema\": \"https://starwind.dev/registry-schema.json\",\n \"components\": [\n { \"name\": \"accordion\", \"type\": \"component\", \"version\": \"1.3.2\", \"dependencies\": [] },\n { \"name\": \"alert\", \"type\": \"component\", \"version\": \"1.3.0\", \"dependencies\": [] },\n {\n \"name\": \"alert-dialog\",\n \"type\": \"component\",\n \"version\": \"1.0.
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/registry.json"],"sourcesContent":["import { join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nimport componentRegistry from \"./registry.json\" with { type: \"json\" };\n\n/**\n * Component metadata interface describing a Starwind UI component\n */\nexport interface ComponentMeta {\n name: string;\n version: string;\n type: \"component\";\n dependencies: string[];\n}\n\n/**\n * Registry interface containing all available components\n */\nexport interface Registry {\n components: ComponentMeta[];\n}\n\nconst __dirname = fileURLToPath(new URL(\".\", import.meta.url));\n\n/**\n * Get the absolute path to a component file\n * @param {string} componentName - The name of the component\n * @param {string} fileName - The name of the file within the component\n * @returns {string} The absolute path to the component file\n */\nexport const getComponentPath = (componentName: string, fileName: string): string => {\n // In production (when installed as a dependency), the components will be in dist/src/components\n // In development, they will be in src/components\n const componentsDir = __dirname.includes(\"dist\") ? \"src/components\" : \"src/components\";\n return join(__dirname, componentsDir, componentName, fileName);\n};\n\n/**\n * Map of all components and their metadata from registry\n */\nexport const registry = componentRegistry.components as ComponentMeta[];\n","{\n \"$schema\": \"https://starwind.dev/registry-schema.json\",\n \"components\": [\n { \"name\": \"accordion\", \"type\": \"component\", \"version\": \"1.3.2\", \"dependencies\": [] },\n { \"name\": \"alert\", \"type\": \"component\", \"version\": \"1.3.0\", \"dependencies\": [] },\n {\n \"name\": \"alert-dialog\",\n \"type\": \"component\",\n \"version\": \"1.0.2\",\n \"dependencies\": [\"@starwind-ui/core/button@^2.1.0\"]\n },\n { \"name\": \"aspect-ratio\", \"type\": \"component\", \"version\": \"1.0.0\", \"dependencies\": [] },\n { \"name\": \"avatar\", \"type\": \"component\", \"version\": \"1.2.1\", \"dependencies\": [] },\n { \"name\": \"badge\", \"type\": \"component\", \"version\": \"1.3.0\", \"dependencies\": [] },\n { \"name\": \"breadcrumb\", \"type\": \"component\", \"version\": \"1.1.1\", \"dependencies\": [] },\n {\n \"name\": \"button-group\",\n \"type\": \"component\",\n \"version\": \"1.0.0\",\n \"dependencies\": [\"@starwind-ui/core/separator@^1.0.0\"]\n },\n { \"name\": \"button\", \"type\": \"component\", \"version\": \"2.2.0\", \"dependencies\": [] },\n { \"name\": \"card\", \"type\": \"component\", \"version\": \"1.3.0\", \"dependencies\": [] },\n {\n \"name\": \"carousel\",\n \"type\": \"component\",\n \"version\": \"1.0.1\",\n \"dependencies\": [\"@starwind-ui/core/button@^2.1.0\", \"embla-carousel@^8.6.0\"]\n },\n { \"name\": \"checkbox\", \"type\": \"component\", \"version\": \"1.4.0\", \"dependencies\": [] },\n { \"name\": \"dialog\", \"type\": \"component\", \"version\": \"1.4.1\", \"dependencies\": [] },\n { \"name\": \"dropdown\", \"type\": \"component\", \"version\": \"1.2.2\", \"dependencies\": [] },\n { \"name\": \"dropzone\", \"type\": \"component\", \"version\": \"1.2.0\", \"dependencies\": [] },\n { \"name\": \"input\", \"type\": \"component\", \"version\": \"1.3.0\", \"dependencies\": [] },\n {\n \"name\": \"item\",\n \"type\": \"component\",\n \"version\": \"1.0.0\",\n \"dependencies\": [\"@starwind-ui/core/separator@^1.0.0\"]\n },\n { \"name\": \"kbd\", \"type\": \"component\", \"version\": \"1.0.0\", \"dependencies\": [] },\n { \"name\": \"label\", \"type\": \"component\", \"version\": \"1.2.0\", \"dependencies\": [] },\n { \"name\": \"pagination\", \"type\": \"component\", \"version\": \"3.0.2\", \"dependencies\": [] },\n { \"name\": \"progress\", \"type\": \"component\", \"version\": \"1.1.0\", \"dependencies\": [] },\n { \"name\": \"radio-group\", \"type\": \"component\", \"version\": \"1.2.2\", \"dependencies\": [] },\n { \"name\": \"select\", \"type\": \"component\", \"version\": \"1.7.0\", \"dependencies\": [] },\n { \"name\": \"separator\", \"type\": \"component\", \"version\": \"1.0.0\", \"dependencies\": [] },\n {\n \"name\": \"sheet\",\n \"type\": \"component\",\n \"version\": \"1.1.1\",\n \"dependencies\": [\"@starwind-ui/core/dialog@^1.3.0\"]\n },\n { \"name\": \"skeleton\", \"type\": \"component\", \"version\": \"1.2.0\", \"dependencies\": [] },\n { \"name\": \"spinner\", \"type\": \"component\", \"version\": \"1.0.0\", \"dependencies\": [] },\n { \"name\": \"switch\", \"type\": \"component\", \"version\": \"1.3.0\", \"dependencies\": [] },\n { \"name\": \"table\", \"type\": \"component\", \"version\": \"1.1.0\", \"dependencies\": [] },\n { \"name\": \"tabs\", \"type\": \"component\", \"version\": \"1.4.0\", \"dependencies\": [] },\n { \"name\": \"textarea\", \"type\": \"component\", \"version\": \"1.3.0\", \"dependencies\": [] },\n { \"name\": \"toggle\", \"type\": \"component\", \"version\": \"1.0.0\", \"dependencies\": [] },\n { \"name\": \"tooltip\", \"type\": \"component\", \"version\": \"1.3.0\", \"dependencies\": [] }\n ]\n}\n"],"mappings":"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","names":[]}
|
|
@@ -33,7 +33,7 @@ if (Astro.slots.has("default")) {
|
|
|
33
33
|
type="button"
|
|
34
34
|
class={ButtonVariants.button({
|
|
35
35
|
variant: "default",
|
|
36
|
-
class: `starwind-alert-dialog-action ${className}`,
|
|
36
|
+
class: `starwind-alert-dialog-action ${className || ""}`,
|
|
37
37
|
})}
|
|
38
38
|
data-slot="alert-dialog-action"
|
|
39
39
|
{...rest}
|
|
@@ -34,7 +34,7 @@ if (Astro.slots.has("default")) {
|
|
|
34
34
|
type="button"
|
|
35
35
|
class={ButtonVariants.button({
|
|
36
36
|
variant: "outline",
|
|
37
|
-
class: `starwind-alert-dialog-close ${className}`,
|
|
37
|
+
class: `starwind-alert-dialog-close ${className || ""}`,
|
|
38
38
|
})}
|
|
39
39
|
data-slot="alert-dialog-cancel"
|
|
40
40
|
{...rest}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from "astro/types";
|
|
3
|
+
import { tv } from "tailwind-variants";
|
|
4
|
+
|
|
5
|
+
type Props = HTMLAttributes<"div"> & {
|
|
6
|
+
/**
|
|
7
|
+
* The orientation of the button group.
|
|
8
|
+
* @default "horizontal"
|
|
9
|
+
*/
|
|
10
|
+
orientation?: "horizontal" | "vertical";
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const buttonGroup = tv({
|
|
14
|
+
base: [
|
|
15
|
+
"flex w-fit items-stretch",
|
|
16
|
+
"[&>*]:focus-visible:relative [&>*]:focus-visible:z-10",
|
|
17
|
+
"has-[>[data-slot=button-group]]:gap-2 [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1",
|
|
18
|
+
],
|
|
19
|
+
variants: {
|
|
20
|
+
orientation: {
|
|
21
|
+
horizontal: [
|
|
22
|
+
"[&>*:not(:first-child)]:rounded-l-none",
|
|
23
|
+
"[&>*:not(:first-child)]:border-l-0",
|
|
24
|
+
"[&>*:not(:last-child):not(:has(+_script:last-child))]:rounded-r-none",
|
|
25
|
+
"[&>*:not(:first-child)_>_[data-as-child]_>_*]:rounded-l-none",
|
|
26
|
+
"[&>*:not(:first-child)_>_[data-as-child]_>_*]:border-l-0",
|
|
27
|
+
"[&>*:not(:last-child):not(:has(+_script:last-child))_>_[data-as-child]_>_*]:rounded-r-none",
|
|
28
|
+
"[&>*:not(:first-child)_>_[data-slot=select-trigger]]:rounded-l-none",
|
|
29
|
+
"[&>*:not(:first-child)_>_[data-slot=select-trigger]]:border-l-0",
|
|
30
|
+
"[&>*:not(:last-child):not(:has(+_script:last-child))_>_[data-slot=select-trigger]]:rounded-r-none",
|
|
31
|
+
],
|
|
32
|
+
vertical: [
|
|
33
|
+
"flex-col",
|
|
34
|
+
"[&>*:not(:first-child)]:rounded-t-none",
|
|
35
|
+
"[&>*:not(:first-child)]:border-t-0",
|
|
36
|
+
"[&>*:not(:last-child):not(:has(+_script:last-child))]:rounded-b-none",
|
|
37
|
+
"[&>*:not(:first-child)_>_[data-as-child]_>_*]:rounded-t-none",
|
|
38
|
+
"[&>*:not(:first-child)_>_[data-as-child]_>_*]:border-t-0",
|
|
39
|
+
"[&>*:not(:last-child):not(:has(+_script:last-child))_>_[data-as-child]_>_*]:rounded-b-none",
|
|
40
|
+
"[&>*:not(:first-child)_>_[data-slot=select-trigger]]:rounded-t-none",
|
|
41
|
+
"[&>*:not(:first-child)_>_[data-slot=select-trigger]]:border-t-0",
|
|
42
|
+
"[&>*:not(:last-child):not(:has(+_script:last-child))_>_[data-slot=select-trigger]]:rounded-b-none",
|
|
43
|
+
],
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
defaultVariants: {
|
|
47
|
+
orientation: "horizontal",
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const { class: className, orientation = "horizontal", ...rest } = Astro.props;
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
<div
|
|
55
|
+
role="group"
|
|
56
|
+
data-slot="button-group"
|
|
57
|
+
data-orientation={orientation}
|
|
58
|
+
class={buttonGroup({ orientation, class: className })}
|
|
59
|
+
{...rest}
|
|
60
|
+
>
|
|
61
|
+
<slot />
|
|
62
|
+
</div>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from "astro/types";
|
|
3
|
+
import { tv } from "tailwind-variants";
|
|
4
|
+
|
|
5
|
+
import { Separator } from "@/components/starwind/separator";
|
|
6
|
+
|
|
7
|
+
type Props = HTMLAttributes<"div"> & {
|
|
8
|
+
/**
|
|
9
|
+
* The orientation of the separator.
|
|
10
|
+
* @default "vertical"
|
|
11
|
+
*/
|
|
12
|
+
orientation?: "horizontal" | "vertical";
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const buttonGroupSeparator = tv({
|
|
16
|
+
base: ["bg-input relative m-0! self-stretch data-[orientation=vertical]:h-auto"],
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const { class: className, orientation = "vertical", ...rest } = Astro.props;
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
<Separator
|
|
23
|
+
data-slot="button-group-separator"
|
|
24
|
+
orientation={orientation}
|
|
25
|
+
class={buttonGroupSeparator({ class: className })}
|
|
26
|
+
{...rest}
|
|
27
|
+
/>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from "astro/types";
|
|
3
|
+
import { tv } from "tailwind-variants";
|
|
4
|
+
|
|
5
|
+
type Props = HTMLAttributes<"div">;
|
|
6
|
+
|
|
7
|
+
export const buttonGroupText = tv({
|
|
8
|
+
base: [
|
|
9
|
+
"bg-muted flex items-center gap-2 rounded-md border px-4 text-sm font-medium shadow-xs",
|
|
10
|
+
"[&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4",
|
|
11
|
+
],
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
const { class: className, ...rest } = Astro.props;
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
<div class:list={[buttonGroupText(), className]} data-slot="button-group-text" {...rest}>
|
|
18
|
+
<slot />
|
|
19
|
+
</div>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import ButtonGroup, { buttonGroup } from "./ButtonGroup.astro";
|
|
2
|
+
import ButtonGroupSeparator, { buttonGroupSeparator } from "./ButtonGroupSeparator.astro";
|
|
3
|
+
import ButtonGroupText, { buttonGroupText } from "./ButtonGroupText.astro";
|
|
4
|
+
|
|
5
|
+
const ButtonGroupVariants = {
|
|
6
|
+
buttonGroup,
|
|
7
|
+
buttonGroupSeparator,
|
|
8
|
+
buttonGroupText,
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export { ButtonGroup, ButtonGroupSeparator, ButtonGroupText, ButtonGroupVariants };
|
|
12
|
+
|
|
13
|
+
export default {
|
|
14
|
+
Root: ButtonGroup,
|
|
15
|
+
Separator: ButtonGroupSeparator,
|
|
16
|
+
Text: ButtonGroupText,
|
|
17
|
+
};
|
|
@@ -1,18 +1,28 @@
|
|
|
1
1
|
---
|
|
2
2
|
import Dots from "@tabler/icons/outline/dots.svg";
|
|
3
3
|
import type { HTMLAttributes } from "astro/types";
|
|
4
|
-
import { tv } from "tailwind-variants";
|
|
4
|
+
import { tv, type VariantProps } from "tailwind-variants";
|
|
5
5
|
|
|
6
|
-
type Props = HTMLAttributes<"span">;
|
|
6
|
+
type Props = HTMLAttributes<"span"> & VariantProps<typeof paginationEllipsis>;
|
|
7
7
|
|
|
8
|
-
export const paginationEllipsis = tv({
|
|
8
|
+
export const paginationEllipsis = tv({
|
|
9
|
+
base: "flex items-center justify-center",
|
|
10
|
+
variants: {
|
|
11
|
+
size: {
|
|
12
|
+
"icon-sm": "size-9",
|
|
13
|
+
icon: "size-11",
|
|
14
|
+
"icon-lg": "size-12",
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
defaultVariants: { size: "icon" },
|
|
18
|
+
});
|
|
9
19
|
|
|
10
|
-
const { class: className, ...rest } = Astro.props;
|
|
20
|
+
const { class: className, size, ...rest } = Astro.props;
|
|
11
21
|
---
|
|
12
22
|
|
|
13
23
|
<span
|
|
14
24
|
aria-hidden
|
|
15
|
-
class={paginationEllipsis({ class: className })}
|
|
25
|
+
class={paginationEllipsis({ size, class: className })}
|
|
16
26
|
data-slot="pagination-ellipsis"
|
|
17
27
|
{...rest}
|
|
18
28
|
>
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
---
|
|
2
2
|
import ChevronRight from "@tabler/icons/outline/chevron-right.svg";
|
|
3
3
|
import type { HTMLAttributes } from "astro/types";
|
|
4
|
-
import { tv } from "tailwind-variants";
|
|
4
|
+
import { tv, type VariantProps } from "tailwind-variants";
|
|
5
|
+
|
|
6
|
+
import { ButtonVariants } from "@/components/starwind/button";
|
|
5
7
|
|
|
6
8
|
import PaginationLink from "./PaginationLink.astro";
|
|
7
9
|
|
|
8
|
-
type Props = HTMLAttributes<"a"> & {
|
|
10
|
+
type Props = HTMLAttributes<"a"> & {
|
|
11
|
+
size?: VariantProps<typeof ButtonVariants.button>["size"];
|
|
12
|
+
};
|
|
9
13
|
|
|
10
14
|
export const paginationNext = tv({ base: "group gap-1" });
|
|
11
15
|
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
---
|
|
2
2
|
import ChevronLeft from "@tabler/icons/outline/chevron-left.svg";
|
|
3
3
|
import type { HTMLAttributes } from "astro/types";
|
|
4
|
-
import { tv } from "tailwind-variants";
|
|
4
|
+
import { tv, type VariantProps } from "tailwind-variants";
|
|
5
|
+
|
|
6
|
+
import { ButtonVariants } from "@/components/starwind/button";
|
|
5
7
|
|
|
6
8
|
import PaginationLink from "./PaginationLink.astro";
|
|
7
9
|
|
|
8
|
-
type Props = HTMLAttributes<"a"> & {
|
|
10
|
+
type Props = HTMLAttributes<"a"> & {
|
|
11
|
+
size?: VariantProps<typeof ButtonVariants.button>["size"];
|
|
12
|
+
};
|
|
9
13
|
|
|
10
14
|
export const paginationPrevious = tv({ base: "group gap-1" });
|
|
11
15
|
|
|
@@ -34,8 +34,11 @@ const { class: className, name, defaultValue, ...rest } = Astro.props;
|
|
|
34
34
|
private select: HTMLElement;
|
|
35
35
|
private trigger: HTMLButtonElement | null;
|
|
36
36
|
private content: HTMLElement | null;
|
|
37
|
+
private searchInput: HTMLInputElement | null = null;
|
|
38
|
+
private emptyElement: HTMLElement | null = null;
|
|
37
39
|
private isOpen: boolean = false;
|
|
38
40
|
private selectedItem: HTMLElement | null = null;
|
|
41
|
+
private activeItem: HTMLElement | null = null;
|
|
39
42
|
private animationDuration = 150;
|
|
40
43
|
private typeaheadTimerRef: number | null = null;
|
|
41
44
|
private typeaheadSearch = "";
|
|
@@ -45,6 +48,8 @@ const { class: className, name, defaultValue, ...rest } = Astro.props;
|
|
|
45
48
|
this.select = select;
|
|
46
49
|
this.trigger = select.querySelector(".starwind-select-trigger");
|
|
47
50
|
this.content = select.querySelector(".starwind-select-content");
|
|
51
|
+
this.searchInput = select.querySelector('[data-slot="select-search"]');
|
|
52
|
+
this.emptyElement = select.querySelector('[data-slot="select-empty"]');
|
|
48
53
|
|
|
49
54
|
if (!this.trigger || !this.content) return;
|
|
50
55
|
|
|
@@ -118,11 +123,32 @@ const { class: className, name, defaultValue, ...rest } = Astro.props;
|
|
|
118
123
|
// Set up additional ARIA attributes
|
|
119
124
|
this.trigger.setAttribute("aria-controls", this.content.id);
|
|
120
125
|
this.content.setAttribute("aria-labelledby", this.trigger.id);
|
|
126
|
+
|
|
127
|
+
// If search input exists, add IDs to all items for aria-activedescendant
|
|
128
|
+
if (this.searchInput) {
|
|
129
|
+
if (!this.searchInput.id) {
|
|
130
|
+
this.searchInput.id = `starwind-select${selectIdx}-search`;
|
|
131
|
+
}
|
|
132
|
+
// Link search input to the listbox it filters
|
|
133
|
+
this.searchInput.setAttribute("aria-controls", this.content.id);
|
|
134
|
+
|
|
135
|
+
const items = this.content.querySelectorAll('[role="option"]');
|
|
136
|
+
items.forEach((item, index) => {
|
|
137
|
+
if (!item.id) {
|
|
138
|
+
item.id = `starwind-select${selectIdx}-option${index}`;
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
}
|
|
121
142
|
}
|
|
122
143
|
|
|
123
144
|
private setupEvents() {
|
|
124
145
|
if (!this.trigger || !this.content) return;
|
|
125
146
|
|
|
147
|
+
// Handle search input if it exists
|
|
148
|
+
if (this.searchInput) {
|
|
149
|
+
this.setupSearchInput();
|
|
150
|
+
}
|
|
151
|
+
|
|
126
152
|
// Handle pointerdown
|
|
127
153
|
this.trigger.addEventListener("pointerdown", (e) => {
|
|
128
154
|
// prevent implicit pointer capture
|
|
@@ -164,11 +190,18 @@ const { class: className, name, defaultValue, ...rest } = Astro.props;
|
|
|
164
190
|
|
|
165
191
|
// Handle keyboard navigation inside select content
|
|
166
192
|
this.content.addEventListener("keydown", (e) => {
|
|
193
|
+
// Check if the event originated from the search input
|
|
194
|
+
const isFromSearchInput = e.target === this.searchInput;
|
|
195
|
+
|
|
167
196
|
if (e.key === "Enter" || e.key === " ") {
|
|
168
|
-
//
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
197
|
+
// Only handle selection if not typing in search input
|
|
198
|
+
if (!isFromSearchInput) {
|
|
199
|
+
// set element based on current focused element
|
|
200
|
+
const activeElement = document.activeElement;
|
|
201
|
+
this.returnFocusOnClose = true;
|
|
202
|
+
this.handleSelection(activeElement as HTMLElement);
|
|
203
|
+
}
|
|
204
|
+
// If from search input, don't handle it here - let the input handle it naturally
|
|
172
205
|
} else if (e.key === "Escape" && this.isOpen) {
|
|
173
206
|
this.returnFocusOnClose = true;
|
|
174
207
|
this.closeSelect();
|
|
@@ -188,7 +221,7 @@ const { class: className, name, defaultValue, ...rest } = Astro.props;
|
|
|
188
221
|
// select should not be navigated using tab key so we prevent it
|
|
189
222
|
if (e.key === "Tab") e.preventDefault();
|
|
190
223
|
|
|
191
|
-
if (!isModifierKey && e.key.length === 1) {
|
|
224
|
+
if (!isModifierKey && e.key.length === 1 && !this.searchInput) {
|
|
192
225
|
this.handleTypeahead(e.key);
|
|
193
226
|
}
|
|
194
227
|
}
|
|
@@ -199,7 +232,7 @@ const { class: className, name, defaultValue, ...rest } = Astro.props;
|
|
|
199
232
|
const target = e.target as HTMLElement;
|
|
200
233
|
const option = target.closest('[role="option"]');
|
|
201
234
|
if (option && option instanceof HTMLElement && this.isOpen === true) {
|
|
202
|
-
|
|
235
|
+
this.setActiveItem(option);
|
|
203
236
|
}
|
|
204
237
|
});
|
|
205
238
|
|
|
@@ -259,50 +292,181 @@ const { class: className, name, defaultValue, ...rest } = Astro.props;
|
|
|
259
292
|
});
|
|
260
293
|
}
|
|
261
294
|
|
|
262
|
-
private
|
|
295
|
+
private setActiveItem(item: HTMLElement | null) {
|
|
263
296
|
if (!this.content) return;
|
|
264
|
-
const items = this.content.querySelectorAll('[role="option"]');
|
|
265
297
|
|
|
266
|
-
//
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
298
|
+
// Remove active state from previous item
|
|
299
|
+
if (this.activeItem) {
|
|
300
|
+
this.activeItem.removeAttribute("data-active");
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Set new active item
|
|
304
|
+
this.activeItem = item;
|
|
305
|
+
if (item) {
|
|
306
|
+
item.setAttribute("data-active", "true");
|
|
307
|
+
|
|
308
|
+
// Scroll item into view if needed
|
|
309
|
+
item.scrollIntoView({ block: "nearest" });
|
|
310
|
+
|
|
311
|
+
// For search mode, set aria-activedescendant for assistive technologies
|
|
312
|
+
if (this.searchInput) {
|
|
313
|
+
// Item should already have an ID from setupAccessibility
|
|
314
|
+
this.searchInput.setAttribute("aria-activedescendant", item.id);
|
|
315
|
+
} else {
|
|
316
|
+
// For non-search mode, set focus for keyboard accessibility
|
|
317
|
+
item.focus();
|
|
275
318
|
}
|
|
276
|
-
|
|
319
|
+
} else if (this.searchInput) {
|
|
320
|
+
// Clear aria-activedescendant when no item is active
|
|
321
|
+
this.searchInput.removeAttribute("aria-activedescendant");
|
|
277
322
|
}
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
private findNavigableItem(
|
|
326
|
+
items: NodeListOf<Element>,
|
|
327
|
+
startIndex: number,
|
|
328
|
+
direction: "forward" | "backward",
|
|
329
|
+
): HTMLElement | null {
|
|
330
|
+
const step = direction === "forward" ? 1 : -1;
|
|
331
|
+
const end = direction === "forward" ? items.length : -1;
|
|
332
|
+
|
|
333
|
+
for (let i = startIndex; i !== end; i += step) {
|
|
334
|
+
const item = items[i] as HTMLElement;
|
|
335
|
+
if (
|
|
336
|
+
item.getAttribute("data-disabled") !== "true" &&
|
|
337
|
+
item.getAttribute("data-filtered") !== "true"
|
|
338
|
+
) {
|
|
339
|
+
return item;
|
|
284
340
|
}
|
|
285
|
-
return;
|
|
286
341
|
}
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
342
|
+
return null;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
private handleNavigationKeys(e: KeyboardEvent) {
|
|
346
|
+
if (!this.content) return;
|
|
347
|
+
const items = this.content.querySelectorAll('[role="option"]');
|
|
348
|
+
const currentIndex = Array.from(items).indexOf(this.activeItem as HTMLElement);
|
|
349
|
+
|
|
350
|
+
let targetItem: HTMLElement | null = null;
|
|
351
|
+
|
|
352
|
+
switch (e.key) {
|
|
353
|
+
case "Home":
|
|
354
|
+
targetItem = this.findNavigableItem(items, 0, "forward");
|
|
355
|
+
break;
|
|
356
|
+
|
|
357
|
+
case "End":
|
|
358
|
+
targetItem = this.findNavigableItem(items, items.length - 1, "backward");
|
|
359
|
+
break;
|
|
360
|
+
|
|
361
|
+
case "ArrowUp":
|
|
362
|
+
if (currentIndex > 0) {
|
|
363
|
+
targetItem = this.findNavigableItem(items, currentIndex - 1, "backward");
|
|
364
|
+
}
|
|
365
|
+
break;
|
|
366
|
+
|
|
367
|
+
case "ArrowDown":
|
|
368
|
+
if (currentIndex < items.length - 1) {
|
|
369
|
+
targetItem = this.findNavigableItem(items, currentIndex + 1, "forward");
|
|
370
|
+
}
|
|
371
|
+
break;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
if (targetItem) {
|
|
375
|
+
this.setActiveItem(targetItem);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
private setupSearchInput() {
|
|
380
|
+
if (!this.searchInput || !this.content) return;
|
|
381
|
+
|
|
382
|
+
this.searchInput.addEventListener("input", (e) => {
|
|
383
|
+
const target = e.target as HTMLInputElement;
|
|
384
|
+
const searchValue = target.value.toLowerCase().trim();
|
|
385
|
+
this.filterItems(searchValue);
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
// Handle keyboard navigation from search input
|
|
389
|
+
this.searchInput.addEventListener("keydown", (e) => {
|
|
390
|
+
if (e.key === "Escape") {
|
|
391
|
+
e.stopPropagation();
|
|
392
|
+
this.returnFocusOnClose = true;
|
|
393
|
+
this.closeSelect();
|
|
394
|
+
}
|
|
395
|
+
// Allow arrow keys to navigate to items
|
|
396
|
+
else if (["ArrowUp", "ArrowDown", "Home", "End"].includes(e.key)) {
|
|
397
|
+
e.preventDefault();
|
|
398
|
+
e.stopPropagation();
|
|
399
|
+
this.handleNavigationKeys(e);
|
|
400
|
+
}
|
|
401
|
+
// Handle Enter to select the active or first visible item
|
|
402
|
+
else if (e.key === "Enter") {
|
|
403
|
+
e.preventDefault();
|
|
404
|
+
const itemToSelect = this.activeItem || this.getFirstVisibleItem();
|
|
405
|
+
if (itemToSelect) {
|
|
406
|
+
this.returnFocusOnClose = true;
|
|
407
|
+
this.handleSelection(itemToSelect);
|
|
293
408
|
}
|
|
294
409
|
}
|
|
295
|
-
|
|
410
|
+
// Prevent space from scrolling the page, but allow it to be typed
|
|
411
|
+
else if (e.key === " ") {
|
|
412
|
+
e.stopPropagation();
|
|
413
|
+
}
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
private getFirstVisibleItem(): HTMLElement | null {
|
|
418
|
+
if (!this.content) return null;
|
|
419
|
+
|
|
420
|
+
const items = this.content.querySelectorAll('[role="option"]');
|
|
421
|
+
for (const item of items) {
|
|
422
|
+
if (
|
|
423
|
+
item.getAttribute("data-disabled") !== "true" &&
|
|
424
|
+
item.getAttribute("data-filtered") !== "true"
|
|
425
|
+
) {
|
|
426
|
+
return item as HTMLElement;
|
|
427
|
+
}
|
|
296
428
|
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
429
|
+
return null;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
private filterItems(searchValue: string) {
|
|
433
|
+
if (!this.content) return;
|
|
434
|
+
|
|
435
|
+
const items = this.content.querySelectorAll('[role="option"]');
|
|
436
|
+
let visibleCount = 0;
|
|
437
|
+
let firstVisibleItem: HTMLElement | null = null;
|
|
438
|
+
|
|
439
|
+
items.forEach((item) => {
|
|
440
|
+
const itemText = item.textContent?.toLowerCase().trim() || "";
|
|
441
|
+
const matches = itemText.includes(searchValue);
|
|
442
|
+
|
|
443
|
+
if (matches || searchValue === "") {
|
|
444
|
+
item.classList.remove("starwind-sr-only");
|
|
445
|
+
item.removeAttribute("data-filtered");
|
|
446
|
+
visibleCount++;
|
|
447
|
+
if (!firstVisibleItem) {
|
|
448
|
+
firstVisibleItem = item as HTMLElement;
|
|
303
449
|
}
|
|
450
|
+
} else {
|
|
451
|
+
item.classList.add("starwind-sr-only");
|
|
452
|
+
item.setAttribute("data-filtered", "true");
|
|
453
|
+
}
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
// Update active item to first visible item after filtering
|
|
457
|
+
if (this.searchInput && firstVisibleItem) {
|
|
458
|
+
this.setActiveItem(firstVisibleItem);
|
|
459
|
+
} else if (this.searchInput && visibleCount === 0) {
|
|
460
|
+
this.setActiveItem(null);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// Show/hide empty message
|
|
464
|
+
if (this.emptyElement) {
|
|
465
|
+
if (visibleCount === 0 && searchValue !== "") {
|
|
466
|
+
this.emptyElement.classList.remove("hidden");
|
|
467
|
+
} else {
|
|
468
|
+
this.emptyElement.classList.add("hidden");
|
|
304
469
|
}
|
|
305
|
-
return;
|
|
306
470
|
}
|
|
307
471
|
}
|
|
308
472
|
|
|
@@ -311,12 +475,12 @@ const { class: className, name, defaultValue, ...rest } = Astro.props;
|
|
|
311
475
|
const search = this.typeaheadSearch + key;
|
|
312
476
|
const items = this.content.querySelectorAll('[role="option"]');
|
|
313
477
|
|
|
314
|
-
// find and
|
|
478
|
+
// find and set active the first matching option
|
|
315
479
|
const matches = Array.from(items).filter((item) =>
|
|
316
480
|
item.textContent?.toLowerCase().trim().startsWith(search.toLowerCase()),
|
|
317
481
|
) as HTMLElement[];
|
|
318
482
|
if (matches.length > 0) {
|
|
319
|
-
matches[0]
|
|
483
|
+
this.setActiveItem(matches[0]);
|
|
320
484
|
}
|
|
321
485
|
|
|
322
486
|
// update the typeahead search and reset the timer
|
|
@@ -361,18 +525,32 @@ const { class: className, name, defaultValue, ...rest } = Astro.props;
|
|
|
361
525
|
this.trigger.setAttribute("aria-expanded", "true");
|
|
362
526
|
this.content.style.removeProperty("display");
|
|
363
527
|
|
|
364
|
-
//
|
|
365
|
-
if (this.
|
|
366
|
-
this.
|
|
528
|
+
// If search input exists, focus it and clear any previous search
|
|
529
|
+
if (this.searchInput) {
|
|
530
|
+
this.searchInput.value = "";
|
|
531
|
+
this.filterItems("");
|
|
532
|
+
|
|
533
|
+
// Set the selected item or first item as active
|
|
534
|
+
const initialItem = this.selectedItem || this.getFirstVisibleItem();
|
|
535
|
+
if (initialItem) {
|
|
536
|
+
this.setActiveItem(initialItem);
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
requestAnimationFrame(() => {
|
|
540
|
+
this.searchInput?.focus();
|
|
541
|
+
});
|
|
367
542
|
} else {
|
|
368
|
-
//
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
543
|
+
// set active on the current selected item
|
|
544
|
+
if (this.selectedItem) {
|
|
545
|
+
this.setActiveItem(this.selectedItem);
|
|
546
|
+
} else {
|
|
547
|
+
// if no item is selected, set active on the first item
|
|
548
|
+
const firstItem = this.content.querySelector('[role="option"]') as HTMLElement;
|
|
549
|
+
if (firstItem) {
|
|
550
|
+
this.setActiveItem(firstItem);
|
|
551
|
+
}
|
|
372
552
|
}
|
|
373
553
|
}
|
|
374
|
-
|
|
375
|
-
this.positionContent();
|
|
376
554
|
}
|
|
377
555
|
|
|
378
556
|
private closeSelect() {
|
|
@@ -399,6 +577,13 @@ const { class: className, name, defaultValue, ...rest } = Astro.props;
|
|
|
399
577
|
setTimeout(() => {
|
|
400
578
|
if (!this.content) return;
|
|
401
579
|
this.content.style.display = "none";
|
|
580
|
+
|
|
581
|
+
// Clear search and show all items after animation completes
|
|
582
|
+
if (this.searchInput) {
|
|
583
|
+
this.searchInput.value = "";
|
|
584
|
+
this.filterItems("");
|
|
585
|
+
this.setActiveItem(null);
|
|
586
|
+
}
|
|
402
587
|
}, this.animationDuration);
|
|
403
588
|
|
|
404
589
|
this.trigger.setAttribute("aria-expanded", "false");
|
|
@@ -478,29 +663,6 @@ const { class: className, name, defaultValue, ...rest } = Astro.props;
|
|
|
478
663
|
this.handleSelection(item);
|
|
479
664
|
}
|
|
480
665
|
}
|
|
481
|
-
|
|
482
|
-
/**
|
|
483
|
-
* TODO: add position logic to avoid collisions with window boundary
|
|
484
|
-
* It will need to switch to top or bottom depending on space available
|
|
485
|
-
* It will also need to set the content max height so it doesn't overflow the viewport
|
|
486
|
-
*/
|
|
487
|
-
private positionContent() {
|
|
488
|
-
// if (!this.content || !this.trigger) return;
|
|
489
|
-
// const triggerRect = this.trigger.getBoundingClientRect();
|
|
490
|
-
// const contentRect = this.content.getBoundingClientRect();
|
|
491
|
-
// const viewportHeight = window.innerHeight;
|
|
492
|
-
// // Position the content below the trigger by default
|
|
493
|
-
// let top = triggerRect.bottom;
|
|
494
|
-
// // If there's not enough space below, position it above
|
|
495
|
-
// if (top + contentRect.height > viewportHeight) {
|
|
496
|
-
// top = triggerRect.top - contentRect.height;
|
|
497
|
-
// }
|
|
498
|
-
// this.content.style.position = "absolute";
|
|
499
|
-
// this.content.style.top = `${top}px`;
|
|
500
|
-
// this.content.style.left = `${triggerRect.left}px`;
|
|
501
|
-
// this.content.style.width = `${triggerRect.width}px`;
|
|
502
|
-
// this.content.style.zIndex = "50";
|
|
503
|
-
}
|
|
504
666
|
}
|
|
505
667
|
|
|
506
668
|
// Store instances in a WeakMap to avoid memory leaks
|
|
@@ -23,6 +23,11 @@ type Props = HTMLAttributes<"div"> & {
|
|
|
23
23
|
* @default 150
|
|
24
24
|
*/
|
|
25
25
|
animationDuration?: number;
|
|
26
|
+
/**
|
|
27
|
+
* Size of the select content
|
|
28
|
+
* @default md
|
|
29
|
+
*/
|
|
30
|
+
size?: "sm" | "md" | "lg";
|
|
26
31
|
};
|
|
27
32
|
|
|
28
33
|
export const selectContent = tv({
|
|
@@ -42,8 +47,13 @@ export const selectContent = tv({
|
|
|
42
47
|
center: "left-1/2 -translate-x-1/2",
|
|
43
48
|
end: "slide-in-from-right-1 slide-out-to-right-1 right-0",
|
|
44
49
|
},
|
|
50
|
+
size: {
|
|
51
|
+
sm: "text-sm [&_[data-slot=select-label]]:text-xs",
|
|
52
|
+
md: "text-base [&_[data-slot=select-label]]:text-sm",
|
|
53
|
+
lg: "text-lg [&_[data-slot=select-label]]:text-base",
|
|
54
|
+
},
|
|
45
55
|
},
|
|
46
|
-
defaultVariants: { side: "bottom", align: "start" },
|
|
56
|
+
defaultVariants: { side: "bottom", align: "start", size: "md" },
|
|
47
57
|
});
|
|
48
58
|
|
|
49
59
|
export const selectContentInner = tv({
|
|
@@ -54,6 +64,7 @@ const {
|
|
|
54
64
|
class: className,
|
|
55
65
|
side = "bottom",
|
|
56
66
|
align = "start",
|
|
67
|
+
size = "md",
|
|
57
68
|
sideOffset = 4,
|
|
58
69
|
animationDuration = 150,
|
|
59
70
|
...rest
|
|
@@ -61,7 +72,7 @@ const {
|
|
|
61
72
|
---
|
|
62
73
|
|
|
63
74
|
<div
|
|
64
|
-
class={selectContent({ side, align, class: className })}
|
|
75
|
+
class={selectContent({ side, align, size, class: className })}
|
|
65
76
|
role="listbox"
|
|
66
77
|
data-side={side}
|
|
67
78
|
data-align={align}
|
|
@@ -16,14 +16,16 @@ type Props = HTMLAttributes<"div"> & {
|
|
|
16
16
|
|
|
17
17
|
export const selectItem = tv({
|
|
18
18
|
base: [
|
|
19
|
-
"relative flex w-full cursor-default items-center rounded-sm py-1.5 pr-
|
|
20
|
-
"
|
|
19
|
+
"relative flex w-full cursor-default items-center rounded-sm py-1.5 pr-8 pl-2 outline-none select-none",
|
|
20
|
+
"data-[active]:bg-accent data-[active]:text-accent-foreground",
|
|
21
21
|
"data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
22
22
|
"not-aria-selected:[&_svg]:hidden aria-selected:[&_svg]:flex",
|
|
23
23
|
],
|
|
24
24
|
});
|
|
25
25
|
|
|
26
|
-
export const selectItemIcon = tv({
|
|
26
|
+
export const selectItemIcon = tv({
|
|
27
|
+
base: "absolute right-2 flex size-4 items-center justify-center",
|
|
28
|
+
});
|
|
27
29
|
|
|
28
30
|
const { class: className, value, disabled, ...rest } = Astro.props;
|
|
29
31
|
---
|
|
@@ -38,12 +40,12 @@ const { class: className, value, disabled, ...rest } = Astro.props;
|
|
|
38
40
|
tabindex="0"
|
|
39
41
|
{...rest}
|
|
40
42
|
>
|
|
43
|
+
<span>
|
|
44
|
+
<slot />
|
|
45
|
+
</span>
|
|
41
46
|
<span class={selectItemIcon()}>
|
|
42
47
|
<slot name="icon">
|
|
43
48
|
<Check class="size-4" />
|
|
44
49
|
</slot>
|
|
45
50
|
</span>
|
|
46
|
-
<span>
|
|
47
|
-
<slot />
|
|
48
|
-
</span>
|
|
49
51
|
</div>
|
|
@@ -4,7 +4,7 @@ import { tv } from "tailwind-variants";
|
|
|
4
4
|
|
|
5
5
|
type Props = HTMLAttributes<"div">;
|
|
6
6
|
|
|
7
|
-
export const selectLabel = tv({ base: "text-muted-foreground py-1.5 pr-
|
|
7
|
+
export const selectLabel = tv({ base: "text-muted-foreground py-1.5 pr-8 pl-2" });
|
|
8
8
|
|
|
9
9
|
const { class: className, ...rest } = Astro.props;
|
|
10
10
|
---
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
---
|
|
2
|
+
import Search from "@tabler/icons/outline/search.svg";
|
|
3
|
+
import type { HTMLAttributes } from "astro/types";
|
|
4
|
+
import { tv } from "tailwind-variants";
|
|
5
|
+
|
|
6
|
+
type Props = Omit<HTMLAttributes<"input">, "type" | "autocomplete"> & {
|
|
7
|
+
/**
|
|
8
|
+
* The text to display when no results are found
|
|
9
|
+
* @default "No results found."
|
|
10
|
+
*/
|
|
11
|
+
emptyText?: string;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const selectSearch = tv({
|
|
15
|
+
base: [
|
|
16
|
+
"placeholder:text-muted-foreground flex w-full border-0 bg-transparent px-0 py-2.5",
|
|
17
|
+
"ring-0 outline-none disabled:cursor-not-allowed disabled:opacity-50",
|
|
18
|
+
],
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const {
|
|
22
|
+
class: className,
|
|
23
|
+
placeholder = "Search...",
|
|
24
|
+
emptyText = "No results found.",
|
|
25
|
+
...rest
|
|
26
|
+
} = Astro.props;
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
<div
|
|
30
|
+
data-slot="select-search-wrapper"
|
|
31
|
+
class="-mx-1 -mt-1 mb-1 flex items-center gap-2 border-b px-3"
|
|
32
|
+
>
|
|
33
|
+
<slot name="icon">
|
|
34
|
+
<Search class="text-muted-foreground size-4.5 shrink-0" />
|
|
35
|
+
</slot>
|
|
36
|
+
<input
|
|
37
|
+
type="text"
|
|
38
|
+
class={selectSearch({ class: className })}
|
|
39
|
+
data-slot="select-search"
|
|
40
|
+
placeholder={placeholder}
|
|
41
|
+
autocomplete="off"
|
|
42
|
+
aria-label={placeholder}
|
|
43
|
+
{...rest}
|
|
44
|
+
/>
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
<div class="text-muted-foreground hidden py-6 text-center text-sm" data-slot="select-empty">
|
|
48
|
+
<slot name="empty">{emptyText}</slot>
|
|
49
|
+
</div>
|
|
@@ -1,34 +1,44 @@
|
|
|
1
1
|
---
|
|
2
2
|
import ChevronDown from "@tabler/icons/outline/chevron-down.svg";
|
|
3
3
|
import type { HTMLAttributes } from "astro/types";
|
|
4
|
-
import { tv } from "tailwind-variants";
|
|
4
|
+
import { tv, type VariantProps } from "tailwind-variants";
|
|
5
5
|
|
|
6
|
-
type Props = Omit<HTMLAttributes<"button">, "role" | "type"> &
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
};
|
|
6
|
+
type Props = Omit<HTMLAttributes<"button">, "role" | "type"> &
|
|
7
|
+
VariantProps<typeof selectTrigger> & {
|
|
8
|
+
/**
|
|
9
|
+
* The content to be rendered inside the select trigger
|
|
10
|
+
*/
|
|
11
|
+
children: any;
|
|
12
|
+
/**
|
|
13
|
+
* Whether the select field is required in a form context
|
|
14
|
+
*/
|
|
15
|
+
required?: boolean;
|
|
16
|
+
};
|
|
17
17
|
|
|
18
18
|
export const selectTrigger = tv({
|
|
19
19
|
base: [
|
|
20
20
|
"starwind-select-trigger",
|
|
21
|
-
"border-input dark:bg-input/30 text-foreground ring-offset-background flex
|
|
21
|
+
"border-input dark:bg-input/30 text-foreground ring-offset-background flex items-center justify-between gap-2 rounded-md border bg-transparent shadow-xs",
|
|
22
22
|
"focus-visible:border-outline focus-visible:ring-outline/50 transition-[color,box-shadow] outline-none focus-visible:ring-3",
|
|
23
23
|
"disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
|
|
24
24
|
],
|
|
25
|
+
variants: {
|
|
26
|
+
size: {
|
|
27
|
+
sm: "h-9 px-2 text-sm",
|
|
28
|
+
md: "h-11 px-3 text-base",
|
|
29
|
+
lg: "h-12 px-4 text-lg",
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
defaultVariants: {
|
|
33
|
+
size: "md",
|
|
34
|
+
},
|
|
25
35
|
});
|
|
26
36
|
|
|
27
|
-
const { class: className, required = false, ...rest } = Astro.props;
|
|
37
|
+
const { class: className, size, required = false, ...rest } = Astro.props;
|
|
28
38
|
---
|
|
29
39
|
|
|
30
40
|
<button
|
|
31
|
-
class={selectTrigger({ class: className })}
|
|
41
|
+
class={selectTrigger({ size, class: className })}
|
|
32
42
|
type="button"
|
|
33
43
|
role="combobox"
|
|
34
44
|
aria-label="Select field"
|
|
@@ -3,6 +3,7 @@ import SelectContent, { selectContent, selectContentInner } from "./SelectConten
|
|
|
3
3
|
import SelectGroup from "./SelectGroup.astro";
|
|
4
4
|
import SelectItem, { selectItem, selectItemIcon } from "./SelectItem.astro";
|
|
5
5
|
import SelectLabel, { selectLabel } from "./SelectLabel.astro";
|
|
6
|
+
import SelectSearch, { selectSearch } from "./SelectSearch.astro";
|
|
6
7
|
import SelectSeparator, { selectSeparator } from "./SelectSeparator.astro";
|
|
7
8
|
import SelectTrigger, { selectTrigger } from "./SelectTrigger.astro";
|
|
8
9
|
import type { SelectChangeEvent, SelectEvent } from "./SelectTypes";
|
|
@@ -14,6 +15,7 @@ const SelectVariants = {
|
|
|
14
15
|
selectItem,
|
|
15
16
|
selectItemIcon,
|
|
16
17
|
selectLabel,
|
|
18
|
+
selectSearch,
|
|
17
19
|
selectSeparator,
|
|
18
20
|
selectTrigger,
|
|
19
21
|
selectValue,
|
|
@@ -27,6 +29,7 @@ export {
|
|
|
27
29
|
SelectGroup,
|
|
28
30
|
SelectItem,
|
|
29
31
|
SelectLabel,
|
|
32
|
+
SelectSearch,
|
|
30
33
|
SelectSeparator,
|
|
31
34
|
SelectTrigger,
|
|
32
35
|
SelectValue,
|
|
@@ -40,6 +43,7 @@ export default {
|
|
|
40
43
|
Content: SelectContent,
|
|
41
44
|
Group: SelectGroup,
|
|
42
45
|
Label: SelectLabel,
|
|
46
|
+
Search: SelectSearch,
|
|
43
47
|
Item: SelectItem,
|
|
44
48
|
Separator: SelectSeparator,
|
|
45
49
|
};
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from "astro/types";
|
|
3
|
+
import { tv, type VariantProps } from "tailwind-variants";
|
|
4
|
+
|
|
5
|
+
export const toggle = tv({
|
|
6
|
+
base: [
|
|
7
|
+
"inline-flex items-center justify-center gap-2 rounded-md font-medium whitespace-nowrap",
|
|
8
|
+
"disabled:pointer-events-none disabled:opacity-50",
|
|
9
|
+
"data-[state=on]:bg-accent data-[state=on]:text-accent-foreground",
|
|
10
|
+
"[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
11
|
+
"focus-visible:border-outline focus-visible:ring-outline/50 focus-visible:ring-3",
|
|
12
|
+
"transition-colors outline-none",
|
|
13
|
+
"aria-invalid:ring-error/20 dark:aria-invalid:ring-error/40 aria-invalid:border-error",
|
|
14
|
+
],
|
|
15
|
+
variants: {
|
|
16
|
+
variant: {
|
|
17
|
+
default: "hover:bg-muted hover:text-muted-foreground bg-transparent",
|
|
18
|
+
outline:
|
|
19
|
+
"border-input hover:bg-accent hover:text-accent-foreground border bg-transparent shadow-xs",
|
|
20
|
+
},
|
|
21
|
+
size: {
|
|
22
|
+
sm: "h-9 min-w-9 px-2 text-sm",
|
|
23
|
+
md: "h-11 min-w-11 px-2.5 text-base",
|
|
24
|
+
lg: "h-12 min-w-12 px-3 text-lg",
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
defaultVariants: {
|
|
28
|
+
variant: "default",
|
|
29
|
+
size: "md",
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
type Props = VariantProps<typeof toggle> &
|
|
34
|
+
HTMLAttributes<"button"> & {
|
|
35
|
+
/**
|
|
36
|
+
* The pressed state of the toggle when initially rendered
|
|
37
|
+
*/
|
|
38
|
+
defaultPressed?: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Optional sync group name. When set, all toggles with the same sync group will mirror each other's state
|
|
41
|
+
*/
|
|
42
|
+
syncGroup?: string;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const { class: className, defaultPressed = false, syncGroup, variant, size, ...rest } = Astro.props;
|
|
46
|
+
|
|
47
|
+
const dataState = defaultPressed ? "on" : "off";
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
<button
|
|
51
|
+
type="button"
|
|
52
|
+
class={toggle({ variant, size, class: `starwind-toggle ${className || ""}` })}
|
|
53
|
+
data-slot="toggle"
|
|
54
|
+
data-state={dataState}
|
|
55
|
+
data-sync-group={syncGroup}
|
|
56
|
+
aria-pressed={defaultPressed}
|
|
57
|
+
{...rest}
|
|
58
|
+
>
|
|
59
|
+
<slot />
|
|
60
|
+
</button>
|
|
61
|
+
|
|
62
|
+
<script>
|
|
63
|
+
import type { ToggleChangeEvent, ToggleSyncEvent } from "./ToggleTypes";
|
|
64
|
+
|
|
65
|
+
class ToggleHandler {
|
|
66
|
+
private toggle: HTMLButtonElement;
|
|
67
|
+
private syncGroup?: string;
|
|
68
|
+
|
|
69
|
+
constructor(toggle: HTMLButtonElement, idx: number) {
|
|
70
|
+
this.toggle = toggle;
|
|
71
|
+
this.syncGroup = toggle.dataset.syncGroup;
|
|
72
|
+
|
|
73
|
+
if (!this.toggle.id) {
|
|
74
|
+
this.toggle.id = `starwind-toggle${idx}`;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
this.setupEventListeners();
|
|
78
|
+
|
|
79
|
+
if (this.syncGroup) {
|
|
80
|
+
this.setupSyncListener();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
private setupEventListeners(): void {
|
|
85
|
+
this.toggle.addEventListener("click", () => this.handleToggle());
|
|
86
|
+
this.toggle.addEventListener("keydown", (event) => this.handleKeyDown(event));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
private setupSyncListener(): void {
|
|
90
|
+
if (!this.syncGroup) return;
|
|
91
|
+
|
|
92
|
+
document.addEventListener(`starwind-toggle-sync:${this.syncGroup}`, ((e: ToggleSyncEvent) => {
|
|
93
|
+
// Don't sync if this toggle triggered the event
|
|
94
|
+
if (e.detail.sourceId === this.toggle.id) return;
|
|
95
|
+
|
|
96
|
+
const newPressed = e.detail.pressed;
|
|
97
|
+
this.updateState(newPressed, false); // false = don't dispatch sync event
|
|
98
|
+
}) as EventListener);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
private handleToggle(): void {
|
|
102
|
+
if (this.isDisabled()) return;
|
|
103
|
+
|
|
104
|
+
const isPressed = this.toggle.getAttribute("aria-pressed") === "true";
|
|
105
|
+
const newPressed = !isPressed;
|
|
106
|
+
|
|
107
|
+
this.updateState(newPressed, true);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
private handleKeyDown(event: KeyboardEvent): void {
|
|
111
|
+
if (this.isDisabled()) return;
|
|
112
|
+
|
|
113
|
+
if (event.key === " " || event.key === "Enter") {
|
|
114
|
+
event.preventDefault();
|
|
115
|
+
this.handleToggle();
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
private isDisabled(): boolean {
|
|
120
|
+
return this.toggle.disabled;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
private updateState(pressed: boolean, dispatchSync: boolean): void {
|
|
124
|
+
const newState = pressed ? "on" : "off";
|
|
125
|
+
|
|
126
|
+
this.toggle.setAttribute("aria-pressed", pressed.toString());
|
|
127
|
+
this.toggle.setAttribute("data-state", newState);
|
|
128
|
+
|
|
129
|
+
// Dispatch change event (always fired for user to listen to)
|
|
130
|
+
const changeEvent = new CustomEvent<ToggleChangeEvent["detail"]>("starwind-toggle:change", {
|
|
131
|
+
detail: {
|
|
132
|
+
pressed,
|
|
133
|
+
toggleId: this.toggle.id,
|
|
134
|
+
syncGroup: this.syncGroup,
|
|
135
|
+
},
|
|
136
|
+
bubbles: true,
|
|
137
|
+
cancelable: true,
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
this.toggle.dispatchEvent(changeEvent);
|
|
141
|
+
|
|
142
|
+
// Dispatch sync event if in a sync group and requested
|
|
143
|
+
if (this.syncGroup && dispatchSync) {
|
|
144
|
+
const syncEvent = new CustomEvent<ToggleSyncEvent["detail"]>(
|
|
145
|
+
`starwind-toggle-sync:${this.syncGroup}`,
|
|
146
|
+
{
|
|
147
|
+
detail: {
|
|
148
|
+
pressed,
|
|
149
|
+
sourceId: this.toggle.id,
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
document.dispatchEvent(syncEvent);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Store instances in a WeakMap to avoid memory leaks
|
|
160
|
+
const toggleInstances = new WeakMap<HTMLElement, ToggleHandler>();
|
|
161
|
+
|
|
162
|
+
const setupToggles = () => {
|
|
163
|
+
document.querySelectorAll<HTMLButtonElement>(".starwind-toggle").forEach((toggle, idx) => {
|
|
164
|
+
if (!toggleInstances.has(toggle)) {
|
|
165
|
+
toggleInstances.set(toggle, new ToggleHandler(toggle, idx));
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
setupToggles();
|
|
171
|
+
document.addEventListener("astro:after-swap", setupToggles);
|
|
172
|
+
</script>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface ToggleChangeEvent extends CustomEvent {
|
|
2
|
+
detail: {
|
|
3
|
+
pressed: boolean;
|
|
4
|
+
toggleId: string;
|
|
5
|
+
syncGroup?: string;
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface ToggleSyncEvent extends CustomEvent {
|
|
10
|
+
detail: {
|
|
11
|
+
pressed: boolean;
|
|
12
|
+
sourceId: string;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import Toggle, { toggle } from "./Toggle.astro";
|
|
2
|
+
import type { ToggleChangeEvent, ToggleSyncEvent } from "./ToggleTypes";
|
|
3
|
+
|
|
4
|
+
const ToggleVariants = { toggle };
|
|
5
|
+
|
|
6
|
+
export { Toggle, type ToggleChangeEvent, type ToggleSyncEvent, ToggleVariants };
|
|
7
|
+
|
|
8
|
+
export default Toggle;
|