@starwind-ui/core 1.5.1 → 1.6.1
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 +6 -5
- package/dist/index.js.map +1 -1
- package/dist/src/components/alert/Alert.astro +1 -1
- package/dist/src/components/avatar/Avatar.astro +1 -1
- package/dist/src/components/badge/Badge.astro +1 -1
- package/dist/src/components/breadcrumb/BreadcrumbLink.astro +10 -8
- package/dist/src/components/breadcrumb/BreadcrumbPage.astro +7 -7
- package/dist/src/components/breadcrumb/index.ts +2 -2
- package/dist/src/components/button/Button.astro +1 -1
- package/dist/src/components/checkbox/Checkbox.astro +1 -1
- package/dist/src/components/dialog/Dialog.astro +21 -5
- package/dist/src/components/dialog/DialogContent.astro +5 -5
- package/dist/src/components/dropdown/Dropdown.astro +25 -6
- package/dist/src/components/dropdown/DropdownContent.astro +6 -6
- package/dist/src/components/dropdown/DropdownTrigger.astro +2 -3
- package/dist/src/components/dropdown/index.ts +1 -1
- package/dist/src/components/input/Input.astro +1 -1
- package/dist/src/components/label/Label.astro +1 -1
- package/dist/src/components/select/Select.astro +31 -3
- package/dist/src/components/select/SelectContent.astro +20 -9
- package/dist/src/components/table/Table.astro +18 -0
- package/dist/src/components/table/TableBody.astro +16 -0
- package/dist/src/components/table/TableCaption.astro +16 -0
- package/dist/src/components/table/TableCell.astro +16 -0
- package/dist/src/components/table/TableFoot.astro +16 -0
- package/dist/src/components/table/TableHead.astro +16 -0
- package/dist/src/components/table/TableHeader.astro +16 -0
- package/dist/src/components/table/TableRow.astro +16 -0
- package/dist/src/components/table/index.ts +21 -0
- package/dist/src/components/tabs/Tabs.astro +25 -4
- package/dist/src/components/textarea/Textarea.astro +1 -1
- package/dist/src/components/tooltip/Tooltip.astro +2 -2
- package/dist/src/components/tooltip/TooltipContent.astro +2 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -14,16 +14,17 @@ var registry_default = {
|
|
|
14
14
|
{ name: "button", type: "component", version: "2.0.1", dependencies: [] },
|
|
15
15
|
{ name: "card", type: "component", version: "1.1.0", dependencies: [] },
|
|
16
16
|
{ name: "checkbox", type: "component", version: "1.2.0", dependencies: [] },
|
|
17
|
-
{ name: "dialog", type: "component", version: "1.1.
|
|
18
|
-
{ name: "dropdown", type: "component", version: "1.0.
|
|
17
|
+
{ name: "dialog", type: "component", version: "1.1.3", dependencies: [] },
|
|
18
|
+
{ name: "dropdown", type: "component", version: "1.0.3", dependencies: [] },
|
|
19
19
|
{ name: "input", type: "component", version: "1.1.1", dependencies: [] },
|
|
20
20
|
{ name: "label", type: "component", version: "1.1.1", dependencies: [] },
|
|
21
21
|
{ name: "pagination", type: "component", version: "2.0.1", dependencies: [] },
|
|
22
|
-
{ name: "select", type: "component", version: "1.
|
|
22
|
+
{ name: "select", type: "component", version: "1.3.1", dependencies: [] },
|
|
23
23
|
{ name: "switch", type: "component", version: "1.1.0", dependencies: [] },
|
|
24
|
-
{ name: "
|
|
24
|
+
{ name: "table", type: "component", version: "1.0.0", dependencies: [] },
|
|
25
|
+
{ name: "tabs", type: "component", version: "1.2.0", dependencies: [] },
|
|
25
26
|
{ name: "textarea", type: "component", version: "1.1.1", dependencies: [] },
|
|
26
|
-
{ name: "tooltip", type: "component", version: "1.1.
|
|
27
|
+
{ name: "tooltip", type: "component", version: "1.1.2", dependencies: [] }
|
|
27
28
|
]
|
|
28
29
|
};
|
|
29
30
|
|
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\";\nimport componentRegistry from \"./registry.json\" with { type: \"json\" };\n\n/**\n * Component metadata interface describing a Starwind UI component\n */\nexport interface ComponentMeta {\n\tname: string;\n\tversion: string;\n\ttype: \"component\";\n\tdependencies: string[];\n}\n\n/**\n * Registry interface containing all available components\n */\nexport interface Registry {\n\tcomponents: 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\t// In production (when installed as a dependency), the components will be in dist/src/components\n\t// In development, they will be in src/components\n\tconst componentsDir = __dirname.includes(\"dist\") ? \"src/components\" : \"src/components\";\n\treturn 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\t\"$schema\": \"https://starwind.dev/registry-schema.json\",\n\t\"components\": [\n\t\t{ \"name\": \"accordion\", \"type\": \"component\", \"version\": \"1.1.0\", \"dependencies\": [] },\n\t\t{ \"name\": \"alert\", \"type\": \"component\", \"version\": \"1.1.1\", \"dependencies\": [] },\n\t\t{ \"name\": \"avatar\", \"type\": \"component\", \"version\": \"1.1.1\", \"dependencies\": [] },\n\t\t{ \"name\": \"badge\", \"type\": \"component\", \"version\": \"1.1.1\", \"dependencies\": [] },\n\t\t{ \"name\": \"breadcrumb\", \"type\": \"component\", \"version\": \"1.0.0\", \"dependencies\": [] },\n\t\t{ \"name\": \"button\", \"type\": \"component\", \"version\": \"2.0.1\", \"dependencies\": [] },\n\t\t{ \"name\": \"card\", \"type\": \"component\", \"version\": \"1.1.0\", \"dependencies\": [] },\n\t\t{ \"name\": \"checkbox\", \"type\": \"component\", \"version\": \"1.2.0\", \"dependencies\": [] },\n\t\t{ \"name\": \"dialog\", \"type\": \"component\", \"version\": \"1.1.
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/registry.json"],"sourcesContent":["import { join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport componentRegistry from \"./registry.json\" with { type: \"json\" };\n\n/**\n * Component metadata interface describing a Starwind UI component\n */\nexport interface ComponentMeta {\n\tname: string;\n\tversion: string;\n\ttype: \"component\";\n\tdependencies: string[];\n}\n\n/**\n * Registry interface containing all available components\n */\nexport interface Registry {\n\tcomponents: 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\t// In production (when installed as a dependency), the components will be in dist/src/components\n\t// In development, they will be in src/components\n\tconst componentsDir = __dirname.includes(\"dist\") ? \"src/components\" : \"src/components\";\n\treturn 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\t\"$schema\": \"https://starwind.dev/registry-schema.json\",\n\t\"components\": [\n\t\t{ \"name\": \"accordion\", \"type\": \"component\", \"version\": \"1.1.0\", \"dependencies\": [] },\n\t\t{ \"name\": \"alert\", \"type\": \"component\", \"version\": \"1.1.1\", \"dependencies\": [] },\n\t\t{ \"name\": \"avatar\", \"type\": \"component\", \"version\": \"1.1.1\", \"dependencies\": [] },\n\t\t{ \"name\": \"badge\", \"type\": \"component\", \"version\": \"1.1.1\", \"dependencies\": [] },\n\t\t{ \"name\": \"breadcrumb\", \"type\": \"component\", \"version\": \"1.0.0\", \"dependencies\": [] },\n\t\t{ \"name\": \"button\", \"type\": \"component\", \"version\": \"2.0.1\", \"dependencies\": [] },\n\t\t{ \"name\": \"card\", \"type\": \"component\", \"version\": \"1.1.0\", \"dependencies\": [] },\n\t\t{ \"name\": \"checkbox\", \"type\": \"component\", \"version\": \"1.2.0\", \"dependencies\": [] },\n\t\t{ \"name\": \"dialog\", \"type\": \"component\", \"version\": \"1.1.3\", \"dependencies\": [] },\n\t\t{ \"name\": \"dropdown\", \"type\": \"component\", \"version\": \"1.0.3\", \"dependencies\": [] },\n\t\t{ \"name\": \"input\", \"type\": \"component\", \"version\": \"1.1.1\", \"dependencies\": [] },\n\t\t{ \"name\": \"label\", \"type\": \"component\", \"version\": \"1.1.1\", \"dependencies\": [] },\n\t\t{ \"name\": \"pagination\", \"type\": \"component\", \"version\": \"2.0.1\", \"dependencies\": [] },\n\t\t{ \"name\": \"select\", \"type\": \"component\", \"version\": \"1.3.1\", \"dependencies\": [] },\n\t\t{ \"name\": \"switch\", \"type\": \"component\", \"version\": \"1.1.0\", \"dependencies\": [] },\n\t\t{ \"name\": \"table\", \"type\": \"component\", \"version\": \"1.0.0\", \"dependencies\": [] },\n\t\t{ \"name\": \"tabs\", \"type\": \"component\", \"version\": \"1.2.0\", \"dependencies\": [] },\n\t\t{ \"name\": \"textarea\", \"type\": \"component\", \"version\": \"1.1.1\", \"dependencies\": [] },\n\t\t{ \"name\": \"tooltip\", \"type\": \"component\", \"version\": \"1.1.2\", \"dependencies\": [] }\n\t]\n}\n"],"mappings":";AAAA,SAAS,YAAY;AACrB,SAAS,qBAAqB;;;ACD9B;AAAA,EACC,SAAW;AAAA,EACX,YAAc;AAAA,IACb,EAAE,MAAQ,aAAa,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IACnF,EAAE,MAAQ,SAAS,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IAC/E,EAAE,MAAQ,UAAU,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IAChF,EAAE,MAAQ,SAAS,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IAC/E,EAAE,MAAQ,cAAc,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IACpF,EAAE,MAAQ,UAAU,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IAChF,EAAE,MAAQ,QAAQ,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IAC9E,EAAE,MAAQ,YAAY,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IAClF,EAAE,MAAQ,UAAU,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IAChF,EAAE,MAAQ,YAAY,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IAClF,EAAE,MAAQ,SAAS,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IAC/E,EAAE,MAAQ,SAAS,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IAC/E,EAAE,MAAQ,cAAc,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IACpF,EAAE,MAAQ,UAAU,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IAChF,EAAE,MAAQ,UAAU,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IAChF,EAAE,MAAQ,SAAS,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IAC/E,EAAE,MAAQ,QAAQ,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IAC9E,EAAE,MAAQ,YAAY,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,IAClF,EAAE,MAAQ,WAAW,MAAQ,aAAa,SAAW,SAAS,cAAgB,CAAC,EAAE;AAAA,EAClF;AACD;;;ADFA,IAAM,YAAY,cAAc,IAAI,IAAI,KAAK,YAAY,GAAG,CAAC;AAQtD,IAAM,mBAAmB,CAAC,eAAuB,aAA6B;AAGpF,QAAM,gBAAgB,UAAU,SAAS,MAAM,IAAI,mBAAmB;AACtE,SAAO,KAAK,WAAW,eAAe,eAAe,QAAQ;AAC9D;AAKO,IAAM,WAAW,iBAAkB;","names":[]}
|
|
@@ -3,7 +3,7 @@ import type { HTMLAttributes } from "astro/types";
|
|
|
3
3
|
import { tv } from "tailwind-variants";
|
|
4
4
|
|
|
5
5
|
type Props = HTMLAttributes<"a"> & {
|
|
6
|
-
|
|
6
|
+
asChild?: boolean;
|
|
7
7
|
};
|
|
8
8
|
|
|
9
9
|
const breadcrumbLink = tv({ base: "hover:text-foreground transition-colors" });
|
|
@@ -11,10 +11,12 @@ const breadcrumbLink = tv({ base: "hover:text-foreground transition-colors" });
|
|
|
11
11
|
const { class: className, asChild = false, ...rest } = Astro.props;
|
|
12
12
|
---
|
|
13
13
|
|
|
14
|
-
{
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
14
|
+
{
|
|
15
|
+
asChild ? (
|
|
16
|
+
<slot />
|
|
17
|
+
) : (
|
|
18
|
+
<a class={breadcrumbLink({ class: className })} {...rest}>
|
|
19
|
+
<slot />
|
|
20
|
+
</a>
|
|
21
|
+
)
|
|
22
|
+
}
|
|
@@ -9,12 +9,12 @@ const breadcrumbPage = tv({ base: "text-foreground font-normal" });
|
|
|
9
9
|
const { class: className, ...rest } = Astro.props;
|
|
10
10
|
---
|
|
11
11
|
|
|
12
|
-
<span
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
12
|
+
<span
|
|
13
|
+
role="link"
|
|
14
|
+
aria-disabled="true"
|
|
15
|
+
aria-current="page"
|
|
16
|
+
class={breadcrumbPage({ class: className })}
|
|
17
|
+
{...rest}
|
|
18
18
|
>
|
|
19
|
-
|
|
19
|
+
<slot />
|
|
20
20
|
</span>
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import Breadcrumb from "./Breadcrumb.astro";
|
|
2
|
-
import BreadcrumbList from "./BreadcrumbList.astro";
|
|
3
2
|
import BreadcrumbEllipsis from "./BreadcrumbEllipsis.astro";
|
|
4
3
|
import BreadcrumbItem from "./BreadcrumbItem.astro";
|
|
5
4
|
import BreadcrumbLink from "./BreadcrumbLink.astro";
|
|
6
|
-
import
|
|
5
|
+
import BreadcrumbList from "./BreadcrumbList.astro";
|
|
7
6
|
import BreadcrumbPage from "./BreadcrumbPage.astro";
|
|
7
|
+
import BreadcrumbSeparator from "./BreadcrumbSeparator.astro";
|
|
8
8
|
|
|
9
9
|
export {
|
|
10
10
|
Breadcrumb,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
import Check from "@tabler/icons/outline/check.svg";
|
|
3
3
|
import type { HTMLAttributes } from "astro/types";
|
|
4
|
-
import {
|
|
4
|
+
import { type VariantProps, tv } from "tailwind-variants";
|
|
5
5
|
|
|
6
6
|
type Props = Omit<HTMLAttributes<"input">, "type"> &
|
|
7
7
|
VariantProps<typeof checkbox> & {
|
|
@@ -90,7 +90,11 @@ const { class: className, ...rest } = Astro.props;
|
|
|
90
90
|
// Add click handlers to all close buttons
|
|
91
91
|
this.closeButtons?.forEach((button) => {
|
|
92
92
|
button.addEventListener("click", () => {
|
|
93
|
-
this
|
|
93
|
+
// Only close if this is the topmost dialog
|
|
94
|
+
const openDialogs = document.querySelectorAll("dialog[open]");
|
|
95
|
+
if (openDialogs.length > 0 && openDialogs[openDialogs.length - 1] === this.dialog) {
|
|
96
|
+
this.close();
|
|
97
|
+
}
|
|
94
98
|
});
|
|
95
99
|
});
|
|
96
100
|
|
|
@@ -104,7 +108,11 @@ const { class: className, ...rest } = Astro.props;
|
|
|
104
108
|
e.clientY <= dialogDimensions.bottom;
|
|
105
109
|
|
|
106
110
|
if (!clickedInDialog) {
|
|
107
|
-
this
|
|
111
|
+
// Only close if this is the topmost dialog
|
|
112
|
+
const openDialogs = document.querySelectorAll("dialog[open]");
|
|
113
|
+
if (openDialogs.length > 0 && openDialogs[openDialogs.length - 1] === this.dialog) {
|
|
114
|
+
this.close();
|
|
115
|
+
}
|
|
108
116
|
}
|
|
109
117
|
});
|
|
110
118
|
|
|
@@ -113,7 +121,11 @@ const { class: className, ...rest } = Astro.props;
|
|
|
113
121
|
if (e.key === "Escape") {
|
|
114
122
|
// prevent default dialog closing behavior so we can add closing animation
|
|
115
123
|
e.preventDefault();
|
|
116
|
-
this
|
|
124
|
+
// Only close if this is the topmost dialog
|
|
125
|
+
const openDialogs = document.querySelectorAll("dialog[open]");
|
|
126
|
+
if (openDialogs.length > 0 && openDialogs[openDialogs.length - 1] === this.dialog) {
|
|
127
|
+
this.close();
|
|
128
|
+
}
|
|
117
129
|
}
|
|
118
130
|
});
|
|
119
131
|
|
|
@@ -130,7 +142,11 @@ const { class: className, ...rest } = Astro.props;
|
|
|
130
142
|
*/
|
|
131
143
|
if (form.method === "dialog") {
|
|
132
144
|
e.preventDefault();
|
|
133
|
-
this
|
|
145
|
+
// Only close if this is the topmost dialog
|
|
146
|
+
const openDialogs = document.querySelectorAll("dialog[open]");
|
|
147
|
+
if (openDialogs.length > 0 && openDialogs[openDialogs.length - 1] === this.dialog) {
|
|
148
|
+
this.close();
|
|
149
|
+
}
|
|
134
150
|
}
|
|
135
151
|
});
|
|
136
152
|
});
|
|
@@ -153,7 +169,7 @@ const { class: className, ...rest } = Astro.props;
|
|
|
153
169
|
setTimeout(() => {
|
|
154
170
|
this.backdrop.classList.add("hidden");
|
|
155
171
|
this.dialog.close();
|
|
156
|
-
}, this.animationDuration
|
|
172
|
+
}, this.animationDuration);
|
|
157
173
|
}
|
|
158
174
|
}
|
|
159
175
|
|
|
@@ -13,8 +13,8 @@ type Props = HTMLAttributes<"dialog"> & {
|
|
|
13
13
|
const dialogBackdrop = tv({
|
|
14
14
|
base: [
|
|
15
15
|
"starwind-dialog-backdrop fixed inset-0 top-0 left-0 z-50 hidden h-screen w-screen bg-black/80",
|
|
16
|
-
"data-[state=open]:animate-in
|
|
17
|
-
"data-[state=closed]:animate-out data-[state=closed]:fade-out
|
|
16
|
+
"data-[state=open]:animate-in fade-in",
|
|
17
|
+
"data-[state=closed]:animate-out data-[state=closed]:fill-mode-forwards fade-out",
|
|
18
18
|
],
|
|
19
19
|
});
|
|
20
20
|
|
|
@@ -22,9 +22,9 @@ const dialogContent = tv({
|
|
|
22
22
|
base: [
|
|
23
23
|
"fixed top-16 left-[50%] z-50 translate-x-[-50%] sm:top-[50%] sm:translate-y-[-50%]",
|
|
24
24
|
"bg-background w-full max-w-md border p-8 shadow-lg sm:rounded-lg",
|
|
25
|
-
"data-[state=open]:animate-in data-[state=closed]:animate-out",
|
|
26
|
-
"
|
|
27
|
-
"
|
|
25
|
+
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fill-mode-forwards",
|
|
26
|
+
"fade-in zoom-in-95 slide-in-from-bottom-2",
|
|
27
|
+
"fade-out zoom-out-95 slide-out-to-bottom-2",
|
|
28
28
|
],
|
|
29
29
|
});
|
|
30
30
|
|
|
@@ -35,10 +35,13 @@ const { class: className, openOnHover = false, closeDelay = 200, ...rest } = Ast
|
|
|
35
35
|
private items: HTMLElement[] = [];
|
|
36
36
|
private currentFocusIndex: number = -1;
|
|
37
37
|
private isOpen: boolean = false;
|
|
38
|
+
private isClosing: boolean = false;
|
|
38
39
|
private animationDuration = 150;
|
|
39
40
|
private openOnHover: boolean;
|
|
40
41
|
private closeDelay: number;
|
|
41
42
|
private closeTimerRef: number | null = null;
|
|
43
|
+
private lastOpenSource: "keyboard" | "mouse" = "keyboard";
|
|
44
|
+
private lastCloseSource: "keyboard" | "mouse" = "keyboard";
|
|
42
45
|
|
|
43
46
|
constructor(dropdown: HTMLElement, dropdownIdx: number) {
|
|
44
47
|
this.dropdown = dropdown;
|
|
@@ -93,6 +96,7 @@ const { class: className, openOnHover = false, closeDelay = 200, ...rest } = Ast
|
|
|
93
96
|
// Handle trigger click
|
|
94
97
|
this.trigger.addEventListener("click", (e) => {
|
|
95
98
|
e.preventDefault();
|
|
99
|
+
this.lastOpenSource = e.detail === 0 ? "keyboard" : "mouse";
|
|
96
100
|
this.toggleDropdown();
|
|
97
101
|
});
|
|
98
102
|
|
|
@@ -100,12 +104,15 @@ const { class: className, openOnHover = false, closeDelay = 200, ...rest } = Ast
|
|
|
100
104
|
this.trigger.addEventListener("keydown", (e) => {
|
|
101
105
|
if (e.key === "Enter" || e.key === " ") {
|
|
102
106
|
e.preventDefault();
|
|
107
|
+
this.lastOpenSource = "keyboard";
|
|
103
108
|
this.toggleDropdown();
|
|
104
109
|
} else if (e.key === "Escape" && this.isOpen) {
|
|
105
110
|
e.preventDefault();
|
|
111
|
+
this.lastCloseSource = "keyboard";
|
|
106
112
|
this.closeDropdown();
|
|
107
113
|
} else if (this.isOpen && (e.key === "ArrowDown" || e.key === "ArrowUp")) {
|
|
108
114
|
e.preventDefault();
|
|
115
|
+
this.lastOpenSource = "keyboard";
|
|
109
116
|
this.updateDropdownItems();
|
|
110
117
|
if (e.key === "ArrowDown") {
|
|
111
118
|
this.focusItem(0); // Focus first item when opening with arrow down
|
|
@@ -178,7 +185,9 @@ const { class: className, openOnHover = false, closeDelay = 200, ...rest } = Ast
|
|
|
178
185
|
if (this.openOnHover) {
|
|
179
186
|
this.trigger.addEventListener("pointerenter", (e) => {
|
|
180
187
|
if (e.pointerType !== "mouse") return;
|
|
188
|
+
if (this.isClosing) return;
|
|
181
189
|
if (!this.isOpen) {
|
|
190
|
+
this.lastOpenSource = "mouse";
|
|
182
191
|
this.openDropdown();
|
|
183
192
|
} else {
|
|
184
193
|
// If the dropdown is already open, make sure to clear any close timer
|
|
@@ -189,6 +198,7 @@ const { class: className, openOnHover = false, closeDelay = 200, ...rest } = Ast
|
|
|
189
198
|
this.dropdown.addEventListener("pointerleave", (e) => {
|
|
190
199
|
if (e.pointerType !== "mouse") return;
|
|
191
200
|
if (this.isOpen) {
|
|
201
|
+
this.lastCloseSource = "mouse";
|
|
192
202
|
this.closeDropdownDelayed();
|
|
193
203
|
}
|
|
194
204
|
});
|
|
@@ -264,6 +274,7 @@ const { class: className, openOnHover = false, closeDelay = 200, ...rest } = Ast
|
|
|
264
274
|
}
|
|
265
275
|
|
|
266
276
|
private openDropdown() {
|
|
277
|
+
if (this.isClosing) return;
|
|
267
278
|
if (!this.content || !this.trigger || this.trigger.disabled) return;
|
|
268
279
|
|
|
269
280
|
this.isOpen = true;
|
|
@@ -283,20 +294,28 @@ const { class: className, openOnHover = false, closeDelay = 200, ...rest } = Ast
|
|
|
283
294
|
private closeDropdown() {
|
|
284
295
|
if (!this.content || !this.trigger) return;
|
|
285
296
|
|
|
297
|
+
this.isClosing = true;
|
|
286
298
|
this.isOpen = false;
|
|
287
299
|
this.content.setAttribute("data-state", "closed");
|
|
288
300
|
|
|
289
|
-
// Set focus back on trigger
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
this.
|
|
293
|
-
|
|
301
|
+
// Set focus back on trigger only if opened or closed by keyboard
|
|
302
|
+
if (
|
|
303
|
+
!this.openOnHover ||
|
|
304
|
+
this.lastOpenSource === "keyboard" ||
|
|
305
|
+
this.lastCloseSource === "keyboard"
|
|
306
|
+
) {
|
|
307
|
+
requestAnimationFrame(() => {
|
|
308
|
+
if (!this.trigger) return;
|
|
309
|
+
this.trigger.focus();
|
|
310
|
+
});
|
|
311
|
+
}
|
|
294
312
|
|
|
295
313
|
// Give the content time to animate before hiding
|
|
296
314
|
setTimeout(() => {
|
|
297
315
|
if (!this.content) return;
|
|
298
316
|
this.content.style.display = "none";
|
|
299
|
-
|
|
317
|
+
this.isClosing = false;
|
|
318
|
+
}, this.animationDuration);
|
|
300
319
|
|
|
301
320
|
this.trigger.setAttribute("aria-expanded", "false");
|
|
302
321
|
|
|
@@ -29,19 +29,19 @@ const dropdownContent = tv({
|
|
|
29
29
|
base: [
|
|
30
30
|
"starwind-dropdown-content",
|
|
31
31
|
"bg-popover text-popover-foreground z-50 min-w-[9rem] overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md",
|
|
32
|
-
"animate-in fade-in
|
|
33
|
-
"data-[state=closed]:animate-out data-[state=closed]:fade-out
|
|
32
|
+
"data-[state=open]:animate-in fade-in zoom-in-95",
|
|
33
|
+
"data-[state=closed]:animate-out data-[state=closed]:fill-mode-forwards fade-out zoom-out-95",
|
|
34
34
|
"absolute will-change-transform",
|
|
35
35
|
],
|
|
36
36
|
variants: {
|
|
37
37
|
side: {
|
|
38
|
-
bottom: "slide-in-from-top-2
|
|
39
|
-
top: "slide-in-from-bottom-2
|
|
38
|
+
bottom: "slide-in-from-top-2 slide-out-to-top-2 top-full",
|
|
39
|
+
top: "slide-in-from-bottom-2 slide-out-to-bottom-2 bottom-full",
|
|
40
40
|
},
|
|
41
41
|
align: {
|
|
42
|
-
start: "slide-in-from-left-1
|
|
42
|
+
start: "slide-in-from-left-1 slide-out-to-left-1 left-0",
|
|
43
43
|
center: "left-1/2 -translate-x-1/2",
|
|
44
|
-
end: "slide-in-from-right-1
|
|
44
|
+
end: "slide-in-from-right-1 slide-out-to-right-1 right-0",
|
|
45
45
|
},
|
|
46
46
|
},
|
|
47
47
|
defaultVariants: {
|
|
@@ -13,7 +13,7 @@ const dropdownTrigger = tv({
|
|
|
13
13
|
base: [
|
|
14
14
|
"starwind-dropdown-trigger",
|
|
15
15
|
"inline-flex items-center justify-center",
|
|
16
|
-
"focus-visible:
|
|
16
|
+
"focus-visible:ring-ring focus-visible:ring-2 focus-visible:outline-none",
|
|
17
17
|
],
|
|
18
18
|
});
|
|
19
19
|
|
|
@@ -28,14 +28,13 @@ if (Astro.slots.has("default")) {
|
|
|
28
28
|
|
|
29
29
|
{
|
|
30
30
|
asChild && hasChildren ? (
|
|
31
|
-
<div class=
|
|
31
|
+
<div class={`starwind-dropdown-trigger ${className}`} data-as-child>
|
|
32
32
|
<slot />
|
|
33
33
|
</div>
|
|
34
34
|
) : (
|
|
35
35
|
<button
|
|
36
36
|
class={dropdownTrigger({ class: className })}
|
|
37
37
|
type="button"
|
|
38
|
-
role="button"
|
|
39
38
|
aria-haspopup="true"
|
|
40
39
|
aria-expanded="false"
|
|
41
40
|
data-state="closed"
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import Dropdown from "./Dropdown.astro";
|
|
2
|
-
import DropdownTrigger from "./DropdownTrigger.astro";
|
|
3
2
|
import DropdownContent from "./DropdownContent.astro";
|
|
4
3
|
import DropdownItem from "./DropdownItem.astro";
|
|
5
4
|
import DropdownLabel from "./DropdownLabel.astro";
|
|
6
5
|
import DropdownSeparator from "./DropdownSeparator.astro";
|
|
6
|
+
import DropdownTrigger from "./DropdownTrigger.astro";
|
|
7
7
|
|
|
8
8
|
export {
|
|
9
9
|
Dropdown,
|
|
@@ -2,15 +2,27 @@
|
|
|
2
2
|
import type { HTMLAttributes } from "astro/types";
|
|
3
3
|
|
|
4
4
|
type Props = HTMLAttributes<"div"> & {
|
|
5
|
+
/**
|
|
6
|
+
* The name of the select field for form handling
|
|
7
|
+
*/
|
|
5
8
|
name?: string;
|
|
9
|
+
/**
|
|
10
|
+
* The value of the item that should be selected by default
|
|
11
|
+
*/
|
|
12
|
+
defaultValue?: string;
|
|
6
13
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
7
14
|
children: any;
|
|
8
15
|
};
|
|
9
16
|
|
|
10
|
-
const { class: className, name, ...rest } = Astro.props;
|
|
17
|
+
const { class: className, name, defaultValue, ...rest } = Astro.props;
|
|
11
18
|
---
|
|
12
19
|
|
|
13
|
-
<div
|
|
20
|
+
<div
|
|
21
|
+
class:list={["starwind-select", "relative", className]}
|
|
22
|
+
data-name={name}
|
|
23
|
+
data-value={defaultValue}
|
|
24
|
+
{...rest}
|
|
25
|
+
>
|
|
14
26
|
<slot />
|
|
15
27
|
</div>
|
|
16
28
|
|
|
@@ -50,6 +62,7 @@ const { class: className, name, ...rest } = Astro.props;
|
|
|
50
62
|
this.setupAccessibility(selectIdx);
|
|
51
63
|
this.setupEvents();
|
|
52
64
|
this.setupSelectField();
|
|
65
|
+
this.setInitialState();
|
|
53
66
|
}
|
|
54
67
|
|
|
55
68
|
private setupSelectField() {
|
|
@@ -355,7 +368,7 @@ const { class: className, name, ...rest } = Astro.props;
|
|
|
355
368
|
setTimeout(() => {
|
|
356
369
|
if (!this.content) return;
|
|
357
370
|
this.content.style.display = "none";
|
|
358
|
-
}, this.animationDuration
|
|
371
|
+
}, this.animationDuration);
|
|
359
372
|
|
|
360
373
|
this.trigger.setAttribute("aria-expanded", "false");
|
|
361
374
|
}
|
|
@@ -398,6 +411,21 @@ const { class: className, name, ...rest } = Astro.props;
|
|
|
398
411
|
this.closeSelect();
|
|
399
412
|
}
|
|
400
413
|
|
|
414
|
+
/**
|
|
415
|
+
* Sets the initial state based on the default value attribute
|
|
416
|
+
*/
|
|
417
|
+
private setInitialState(): void {
|
|
418
|
+
const defaultValue = this.select.dataset.value;
|
|
419
|
+
if (defaultValue) {
|
|
420
|
+
const item = this.content?.querySelector(`[data-value="${defaultValue}"]`);
|
|
421
|
+
|
|
422
|
+
if (item && item instanceof HTMLElement) {
|
|
423
|
+
this.handleSelection(item);
|
|
424
|
+
this.selectedItem = item;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
401
429
|
/**
|
|
402
430
|
* TODO: add position logic to avoid collisions with window boundary
|
|
403
431
|
* It will need to switch to top or bottom depending on space available
|
|
@@ -8,6 +8,11 @@ type Props = HTMLAttributes<"div"> & {
|
|
|
8
8
|
* @default bottom
|
|
9
9
|
*/
|
|
10
10
|
side?: "top" | "bottom";
|
|
11
|
+
/**
|
|
12
|
+
* Alignment of the dropdown
|
|
13
|
+
* @default start
|
|
14
|
+
*/
|
|
15
|
+
align?: "start" | "center" | "end";
|
|
11
16
|
/**
|
|
12
17
|
* Offset distance in pixels
|
|
13
18
|
* @default 4
|
|
@@ -24,18 +29,21 @@ const selectContent = tv({
|
|
|
24
29
|
base: [
|
|
25
30
|
"starwind-select-content",
|
|
26
31
|
"bg-popover text-popover-foreground absolute z-50 min-w-[8rem] rounded-md border shadow-md",
|
|
27
|
-
"fade-in
|
|
28
|
-
"data-[state=closed]:
|
|
29
|
-
"left-0",
|
|
32
|
+
"data-[state=open]:animate-in fade-in zoom-in-95 overflow-hidden will-change-transform",
|
|
33
|
+
"data-[state=closed]:animate-out data-[state=closed]:fill-mode-forwards fade-out zoom-out-95",
|
|
30
34
|
],
|
|
31
35
|
variants: {
|
|
32
36
|
side: {
|
|
33
|
-
bottom:
|
|
34
|
-
|
|
35
|
-
|
|
37
|
+
bottom: "slide-in-from-top-2 slide-out-to-top-2 top-full",
|
|
38
|
+
top: "slide-in-from-bottom-2 slide-out-to-bottom-2 bottom-full",
|
|
39
|
+
},
|
|
40
|
+
align: {
|
|
41
|
+
start: "slide-in-from-left-1 slide-out-to-left-1 left-0",
|
|
42
|
+
center: "left-1/2 -translate-x-1/2",
|
|
43
|
+
end: "slide-in-from-right-1 slide-out-to-right-1 right-0",
|
|
36
44
|
},
|
|
37
45
|
},
|
|
38
|
-
defaultVariants: { side: "bottom" },
|
|
46
|
+
defaultVariants: { side: "bottom", align: "start" },
|
|
39
47
|
});
|
|
40
48
|
|
|
41
49
|
const selectContentInner = tv({
|
|
@@ -45,6 +53,7 @@ const selectContentInner = tv({
|
|
|
45
53
|
const {
|
|
46
54
|
class: className,
|
|
47
55
|
side = "bottom",
|
|
56
|
+
align = "start",
|
|
48
57
|
sideOffset = 4,
|
|
49
58
|
animationDuration = 150,
|
|
50
59
|
...rest
|
|
@@ -52,16 +61,18 @@ const {
|
|
|
52
61
|
---
|
|
53
62
|
|
|
54
63
|
<div
|
|
55
|
-
class={selectContent({ side, class: className })}
|
|
64
|
+
class={selectContent({ side, align, class: className })}
|
|
56
65
|
role="listbox"
|
|
57
66
|
data-side={side}
|
|
67
|
+
data-align={align}
|
|
58
68
|
data-state="closed"
|
|
59
69
|
tabindex="-1"
|
|
60
70
|
style={{
|
|
61
|
-
"--select-content-offset": `calc(100% + ${sideOffset}px)`,
|
|
62
71
|
// hide the content initially. Script will remove this
|
|
63
72
|
display: "none",
|
|
64
73
|
animationDuration: `${animationDuration}ms`,
|
|
74
|
+
marginTop: side === "bottom" ? `${sideOffset}px` : undefined,
|
|
75
|
+
marginBottom: side === "top" ? `${sideOffset}px` : undefined,
|
|
65
76
|
}}
|
|
66
77
|
{...rest}
|
|
67
78
|
>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from "astro/types";
|
|
3
|
+
import { type VariantProps, tv } from "tailwind-variants";
|
|
4
|
+
|
|
5
|
+
type Props = HTMLAttributes<"table"> & VariantProps<typeof table>;
|
|
6
|
+
|
|
7
|
+
const table = tv({
|
|
8
|
+
base: "w-full caption-bottom text-sm",
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const { class: className, ...rest } = Astro.props;
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
<div data-table-container class="relative w-full overflow-x-auto">
|
|
15
|
+
<table data-table class={table({ class: className })} {...rest} role="table">
|
|
16
|
+
<slot />
|
|
17
|
+
</table>
|
|
18
|
+
</div>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from "astro/types";
|
|
3
|
+
import { type VariantProps, tv } from "tailwind-variants";
|
|
4
|
+
|
|
5
|
+
type Props = HTMLAttributes<"tbody"> & VariantProps<typeof tableBody>;
|
|
6
|
+
|
|
7
|
+
const tableBody = tv({
|
|
8
|
+
base: "[&_tr:last-child]:border-0",
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const { class: className, ...rest } = Astro.props;
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
<tbody data-table-body class={tableBody({ class: className })} {...rest}>
|
|
15
|
+
<slot />
|
|
16
|
+
</tbody>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from "astro/types";
|
|
3
|
+
import { type VariantProps, tv } from "tailwind-variants";
|
|
4
|
+
|
|
5
|
+
type Props = HTMLAttributes<"caption"> & VariantProps<typeof tableCaption>;
|
|
6
|
+
|
|
7
|
+
const tableCaption = tv({
|
|
8
|
+
base: "text-muted-foreground mt-4 text-sm",
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const { class: className, ...rest } = Astro.props;
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
<caption data-table-caption class={tableCaption({ class: className })} {...rest}>
|
|
15
|
+
<slot />
|
|
16
|
+
</caption>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from "astro/types";
|
|
3
|
+
import { type VariantProps, tv } from "tailwind-variants";
|
|
4
|
+
|
|
5
|
+
type Props = HTMLAttributes<"td"> & VariantProps<typeof tableCell>;
|
|
6
|
+
|
|
7
|
+
const tableCell = tv({
|
|
8
|
+
base: "p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const { class: className, ...rest } = Astro.props;
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
<td data-table-cell class={tableCell({ class: className })} {...rest}>
|
|
15
|
+
<slot />
|
|
16
|
+
</td>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from "astro/types";
|
|
3
|
+
import { type VariantProps, tv } from "tailwind-variants";
|
|
4
|
+
|
|
5
|
+
type Props = HTMLAttributes<"tfoot"> & VariantProps<typeof tableFoot>;
|
|
6
|
+
|
|
7
|
+
const tableFoot = tv({
|
|
8
|
+
base: "bg-muted/50 border-t font-medium [&>tr]:last:border-b-0",
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const { class: className, ...rest } = Astro.props;
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
<tfoot data-table-foot class={tableFoot({ class: className })} {...rest}>
|
|
15
|
+
<slot />
|
|
16
|
+
</tfoot>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from "astro/types";
|
|
3
|
+
import { type VariantProps, tv } from "tailwind-variants";
|
|
4
|
+
|
|
5
|
+
type Props = HTMLAttributes<"th"> & VariantProps<typeof tableHead>;
|
|
6
|
+
|
|
7
|
+
const tableHead = tv({
|
|
8
|
+
base: "text-muted-foreground h-10 px-2 text-left align-middle font-medium whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const { class: className, ...rest } = Astro.props;
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
<th data-table-head class={tableHead({ class: className })} {...rest} role="columnheader">
|
|
15
|
+
<slot />
|
|
16
|
+
</th>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from "astro/types";
|
|
3
|
+
import { type VariantProps, tv } from "tailwind-variants";
|
|
4
|
+
|
|
5
|
+
type Props = HTMLAttributes<"thead"> & VariantProps<typeof tableHeader>;
|
|
6
|
+
|
|
7
|
+
const tableHeader = tv({
|
|
8
|
+
base: "[&_tr]:border-b",
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const { class: className, ...rest } = Astro.props;
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
<thead data-table-header class={tableHeader({ class: className })} {...rest}>
|
|
15
|
+
<slot />
|
|
16
|
+
</thead>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from "astro/types";
|
|
3
|
+
import { type VariantProps, tv } from "tailwind-variants";
|
|
4
|
+
|
|
5
|
+
type Props = HTMLAttributes<"tr"> & VariantProps<typeof tableRow>;
|
|
6
|
+
|
|
7
|
+
const tableRow = tv({
|
|
8
|
+
base: "hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors",
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const { class: className, ...rest } = Astro.props;
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
<tr data-table-row class={tableRow({ class: className })} {...rest} role="row">
|
|
15
|
+
<slot />
|
|
16
|
+
</tr>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import Table from "./Table.astro";
|
|
2
|
+
import TableBody from "./TableBody.astro";
|
|
3
|
+
import TableCaption from "./TableCaption.astro";
|
|
4
|
+
import TableCell from "./TableCell.astro";
|
|
5
|
+
import TableFoot from "./TableFoot.astro";
|
|
6
|
+
import TableHead from "./TableHead.astro";
|
|
7
|
+
import TableHeader from "./TableHeader.astro";
|
|
8
|
+
import TableRow from "./TableRow.astro";
|
|
9
|
+
|
|
10
|
+
export { Table, TableBody, TableCaption, TableCell, TableFoot, TableHead, TableHeader, TableRow };
|
|
11
|
+
|
|
12
|
+
export default {
|
|
13
|
+
Root: Table,
|
|
14
|
+
Body: TableBody,
|
|
15
|
+
Caption: TableCaption,
|
|
16
|
+
Cell: TableCell,
|
|
17
|
+
Foot: TableFoot,
|
|
18
|
+
Head: TableHead,
|
|
19
|
+
Header: TableHeader,
|
|
20
|
+
Row: TableRow,
|
|
21
|
+
};
|
|
@@ -42,11 +42,15 @@ const { defaultValue, syncKey, class: className, ...rest } = Astro.props;
|
|
|
42
42
|
private storageKey: string;
|
|
43
43
|
private valueToTriggerMap: Map<string, HTMLButtonElement>;
|
|
44
44
|
private valueToContentMap: Map<string, HTMLElement>;
|
|
45
|
+
private parentHandler: TabsHandler | null = null;
|
|
45
46
|
|
|
46
|
-
constructor(tabs: HTMLElement, idx: number) {
|
|
47
|
+
constructor(tabs: HTMLElement, idx: number, parentHandler: TabsHandler | null = null) {
|
|
47
48
|
this.tabs = tabs;
|
|
48
|
-
this.
|
|
49
|
-
this.
|
|
49
|
+
this.parentHandler = parentHandler;
|
|
50
|
+
this.triggers = Array.from(
|
|
51
|
+
tabs.querySelectorAll(":scope > [data-tabs-list] > [data-tabs-trigger]"),
|
|
52
|
+
);
|
|
53
|
+
this.contents = Array.from(tabs.querySelectorAll(":scope > [data-tabs-content]"));
|
|
50
54
|
this.tabsId = `starwind-tabs${idx}`;
|
|
51
55
|
this.syncKey = tabs.dataset.syncKey;
|
|
52
56
|
this.storageKey = this.syncKey
|
|
@@ -228,6 +232,20 @@ const { defaultValue, syncKey, class: className, ...rest } = Astro.props;
|
|
|
228
232
|
c.setAttribute("data-state", isActive ? "active" : "inactive");
|
|
229
233
|
c.hidden = !isActive;
|
|
230
234
|
});
|
|
235
|
+
|
|
236
|
+
// Initialize any nested tabs in the active content
|
|
237
|
+
if (content.hasAttribute("data-state") && content.getAttribute("data-state") === "active") {
|
|
238
|
+
const nestedTabs = content.querySelectorAll<HTMLElement>(".starwind-tabs");
|
|
239
|
+
|
|
240
|
+
nestedTabs.forEach((nestedTab, nestedIdx) => {
|
|
241
|
+
// Skip tabs that already have instances
|
|
242
|
+
if (!tabInstances.has(nestedTab)) {
|
|
243
|
+
const uniqueIdx = 1000 + nestedIdx;
|
|
244
|
+
const handler = new TabsHandler(nestedTab, uniqueIdx, this);
|
|
245
|
+
tabInstances.set(nestedTab, handler);
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
}
|
|
231
249
|
}
|
|
232
250
|
}
|
|
233
251
|
|
|
@@ -235,8 +253,11 @@ const { defaultValue, syncKey, class: className, ...rest } = Astro.props;
|
|
|
235
253
|
const tabInstances = new WeakMap<HTMLElement, TabsHandler>();
|
|
236
254
|
|
|
237
255
|
const setupTabs = () => {
|
|
256
|
+
// First handle top-level tabs
|
|
238
257
|
document.querySelectorAll<HTMLElement>(".starwind-tabs").forEach((tabs, idx) => {
|
|
239
|
-
|
|
258
|
+
// Skip tabs that are nested within other tab contents
|
|
259
|
+
const isNested = !!tabs.closest("[data-tabs-content]");
|
|
260
|
+
if (!isNested && !tabInstances.has(tabs)) {
|
|
240
261
|
tabInstances.set(tabs, new TabsHandler(tabs, idx));
|
|
241
262
|
}
|
|
242
263
|
});
|
|
@@ -162,7 +162,7 @@ const {
|
|
|
162
162
|
setTimeout(() => {
|
|
163
163
|
if (!this.content) return;
|
|
164
164
|
this.content.style.display = "none";
|
|
165
|
-
}, this.animationDuration
|
|
165
|
+
}, this.animationDuration);
|
|
166
166
|
this.content.setAttribute("data-state", "closed");
|
|
167
167
|
return;
|
|
168
168
|
}
|
|
@@ -173,7 +173,7 @@ const {
|
|
|
173
173
|
setTimeout(() => {
|
|
174
174
|
if (!this.content) return;
|
|
175
175
|
this.content.style.display = "none";
|
|
176
|
-
}, this.animationDuration
|
|
176
|
+
}, this.animationDuration);
|
|
177
177
|
if (!this.content) return;
|
|
178
178
|
this.content.setAttribute("data-state", "closed");
|
|
179
179
|
this.closeTimerRef = null;
|
|
@@ -35,8 +35,8 @@ const tooltipContent = tv({
|
|
|
35
35
|
"starwind-tooltip-content",
|
|
36
36
|
"absolute z-50 hidden px-3 py-1.5 whitespace-nowrap shadow-sm will-change-transform",
|
|
37
37
|
"bg-popover text-popover-foreground rounded-md border",
|
|
38
|
-
"animate-in fade-in
|
|
39
|
-
"data-[state=closed]:animate-out data-[state=closed]:fade-out
|
|
38
|
+
"animate-in fade-in zoom-in-95",
|
|
39
|
+
"data-[state=closed]:animate-out data-[state=closed]:fill-mode-forwards fade-out zoom-out-95",
|
|
40
40
|
],
|
|
41
41
|
variants: {
|
|
42
42
|
side: {
|