@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.
Files changed (34) hide show
  1. package/dist/index.js +6 -5
  2. package/dist/index.js.map +1 -1
  3. package/dist/src/components/alert/Alert.astro +1 -1
  4. package/dist/src/components/avatar/Avatar.astro +1 -1
  5. package/dist/src/components/badge/Badge.astro +1 -1
  6. package/dist/src/components/breadcrumb/BreadcrumbLink.astro +10 -8
  7. package/dist/src/components/breadcrumb/BreadcrumbPage.astro +7 -7
  8. package/dist/src/components/breadcrumb/index.ts +2 -2
  9. package/dist/src/components/button/Button.astro +1 -1
  10. package/dist/src/components/checkbox/Checkbox.astro +1 -1
  11. package/dist/src/components/dialog/Dialog.astro +21 -5
  12. package/dist/src/components/dialog/DialogContent.astro +5 -5
  13. package/dist/src/components/dropdown/Dropdown.astro +25 -6
  14. package/dist/src/components/dropdown/DropdownContent.astro +6 -6
  15. package/dist/src/components/dropdown/DropdownTrigger.astro +2 -3
  16. package/dist/src/components/dropdown/index.ts +1 -1
  17. package/dist/src/components/input/Input.astro +1 -1
  18. package/dist/src/components/label/Label.astro +1 -1
  19. package/dist/src/components/select/Select.astro +31 -3
  20. package/dist/src/components/select/SelectContent.astro +20 -9
  21. package/dist/src/components/table/Table.astro +18 -0
  22. package/dist/src/components/table/TableBody.astro +16 -0
  23. package/dist/src/components/table/TableCaption.astro +16 -0
  24. package/dist/src/components/table/TableCell.astro +16 -0
  25. package/dist/src/components/table/TableFoot.astro +16 -0
  26. package/dist/src/components/table/TableHead.astro +16 -0
  27. package/dist/src/components/table/TableHeader.astro +16 -0
  28. package/dist/src/components/table/TableRow.astro +16 -0
  29. package/dist/src/components/table/index.ts +21 -0
  30. package/dist/src/components/tabs/Tabs.astro +25 -4
  31. package/dist/src/components/textarea/Textarea.astro +1 -1
  32. package/dist/src/components/tooltip/Tooltip.astro +2 -2
  33. package/dist/src/components/tooltip/TooltipContent.astro +2 -2
  34. 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.1", dependencies: [] },
18
- { name: "dropdown", type: "component", version: "1.0.1", dependencies: [] },
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.2.0", dependencies: [] },
22
+ { name: "select", type: "component", version: "1.3.1", dependencies: [] },
23
23
  { name: "switch", type: "component", version: "1.1.0", dependencies: [] },
24
- { name: "tabs", type: "component", version: "1.1.1", dependencies: [] },
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.1", dependencies: [] }
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\", \"dependencies\": [] },\n\t\t{ \"name\": \"dropdown\", \"type\": \"component\", \"version\": \"1.0.1\", \"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.2.0\", \"dependencies\": [] },\n\t\t{ \"name\": \"switch\", \"type\": \"component\", \"version\": \"1.1.0\", \"dependencies\": [] },\n\t\t{ \"name\": \"tabs\", \"type\": \"component\", \"version\": \"1.1.1\", \"dependencies\": [] },\n\t\t{ \"name\": \"textarea\", \"type\": \"component\", \"version\": \"1.1.1\", \"dependencies\": [] },\n\t\t{ \"name\": \"tooltip\", \"type\": \"component\", \"version\": \"1.1.1\", \"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,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;;;ADDA,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":[]}
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":[]}
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  import type { HTMLAttributes } from "astro/types";
3
- import { tv, type VariantProps } from "tailwind-variants";
3
+ import { type VariantProps, tv } from "tailwind-variants";
4
4
 
5
5
  type Props = HTMLAttributes<"div"> & VariantProps<typeof alert>;
6
6
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  import type { HTMLAttributes } from "astro/types";
3
- import { tv, type VariantProps } from "tailwind-variants";
3
+ import { type VariantProps, tv } from "tailwind-variants";
4
4
 
5
5
  interface Props extends HTMLAttributes<"div">, VariantProps<typeof avatar> {}
6
6
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  import type { HTMLAttributes } from "astro/types";
3
- import { tv, type VariantProps } from "tailwind-variants";
3
+ import { type VariantProps, tv } from "tailwind-variants";
4
4
 
5
5
  interface Props
6
6
  extends HTMLAttributes<"div">,
@@ -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
- asChild?: boolean;
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
- {asChild ? (
15
- <slot />
16
- ) : (
17
- <a class={breadcrumbLink({ class: className })} {...rest}>
18
- <slot />
19
- </a>
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
- role="link"
14
- aria-disabled="true"
15
- aria-current="page"
16
- class={breadcrumbPage({ class: className })}
17
- {...rest}
12
+ <span
13
+ role="link"
14
+ aria-disabled="true"
15
+ aria-current="page"
16
+ class={breadcrumbPage({ class: className })}
17
+ {...rest}
18
18
  >
19
- <slot />
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 BreadcrumbSeparator from "./BreadcrumbSeparator.astro";
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,6 +1,6 @@
1
1
  ---
2
2
  import type { HTMLAttributes } from "astro/types";
3
- import { tv, type VariantProps } from "tailwind-variants";
3
+ import { type VariantProps, tv } from "tailwind-variants";
4
4
 
5
5
  interface Props
6
6
  extends HTMLAttributes<"button">,
@@ -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 { tv, type VariantProps } from "tailwind-variants";
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.close();
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.close();
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.close();
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.close();
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 - 10);
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 data-[state=open]:fade-in-0",
17
- "data-[state=closed]:animate-out data-[state=closed]:fade-out-0",
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
- "data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 data-[state=open]:slide-in-from-bottom-2",
27
- "data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=closed]:slide-out-to-bottom-2",
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
- requestAnimationFrame(() => {
291
- if (!this.trigger) return;
292
- this.trigger.focus();
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
- }, this.animationDuration - 10);
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-0 zoom-in-95",
33
- "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
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 data-[state=closed]:slide-out-to-top-2 top-full",
39
- top: "slide-in-from-bottom-2 data-[state=closed]:slide-out-to-bottom-2 bottom-full",
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 data-[state=closed]:slide-out-to-left-1 left-0",
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 data-[state=closed]:slide-out-to-right-1 right-0",
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:outline-none focus-visible:ring-2 focus-visible:ring-ring",
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="starwind-dropdown-trigger" data-as-child>
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,
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  import type { HTMLAttributes } from "astro/types";
3
- import { tv, type VariantProps } from "tailwind-variants";
3
+ import { type VariantProps, tv } from "tailwind-variants";
4
4
 
5
5
  type Props = HTMLAttributes<"input"> & VariantProps<typeof input>;
6
6
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  import type { HTMLAttributes } from "astro/types";
3
- import { tv, type VariantProps } from "tailwind-variants";
3
+ import { type VariantProps, tv } from "tailwind-variants";
4
4
 
5
5
  type Props = HTMLAttributes<"label"> & VariantProps<typeof label>;
6
6
 
@@ -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 class:list={["starwind-select", "relative", className]} data-name={name} {...rest}>
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 - 10);
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-0 zoom-in-95 animate-in overflow-hidden will-change-transform",
28
- "data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=closed]:animate-out",
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
- "data-[side=bottom]:slide-in-from-top-4 data-[side=bottom]:top-(--select-content-offset)",
35
- top: "data-[side=top]:slide-in-from-bottom-4 data-[side=top]:bottom-(--select-content-offset)",
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.triggers = Array.from(tabs.querySelectorAll("[data-tabs-trigger]"));
49
- this.contents = Array.from(tabs.querySelectorAll("[data-tabs-content]"));
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
- if (!tabInstances.has(tabs)) {
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
  });
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  import type { HTMLAttributes } from "astro/types";
3
- import { tv, type VariantProps } from "tailwind-variants";
3
+ import { type VariantProps, tv } from "tailwind-variants";
4
4
 
5
5
  type Props = HTMLAttributes<"textarea"> & VariantProps<typeof textarea>;
6
6
 
@@ -162,7 +162,7 @@ const {
162
162
  setTimeout(() => {
163
163
  if (!this.content) return;
164
164
  this.content.style.display = "none";
165
- }, this.animationDuration - 10);
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 - 10);
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-0 zoom-in-95",
39
- "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
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: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@starwind-ui/core",
3
- "version": "1.5.1",
3
+ "version": "1.6.1",
4
4
  "description": "Starwind UI core components and registry",
5
5
  "license": "MIT",
6
6
  "author": {