@starwind-ui/core 1.6.2 → 1.7.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 (101) hide show
  1. package/dist/index.js +8 -4
  2. package/dist/index.js.map +1 -1
  3. package/dist/src/components/accordion/Accordion.astro +224 -224
  4. package/dist/src/components/accordion/AccordionContent.astro +13 -13
  5. package/dist/src/components/accordion/AccordionItem.astro +6 -6
  6. package/dist/src/components/accordion/AccordionTrigger.astro +13 -13
  7. package/dist/src/components/accordion/index.ts +4 -4
  8. package/dist/src/components/alert/Alert.astro +15 -15
  9. package/dist/src/components/alert/AlertDescription.astro +1 -1
  10. package/dist/src/components/alert/AlertTitle.astro +2 -2
  11. package/dist/src/components/alert/index.ts +3 -3
  12. package/dist/src/components/avatar/Avatar.astro +16 -16
  13. package/dist/src/components/avatar/AvatarFallback.astro +3 -3
  14. package/dist/src/components/avatar/AvatarImage.astro +12 -12
  15. package/dist/src/components/avatar/index.ts +4 -4
  16. package/dist/src/components/badge/Badge.astro +33 -33
  17. package/dist/src/components/breadcrumb/Breadcrumb.astro +1 -1
  18. package/dist/src/components/breadcrumb/BreadcrumbEllipsis.astro +6 -6
  19. package/dist/src/components/breadcrumb/BreadcrumbItem.astro +1 -1
  20. package/dist/src/components/breadcrumb/BreadcrumbLink.astro +8 -8
  21. package/dist/src/components/breadcrumb/BreadcrumbList.astro +2 -2
  22. package/dist/src/components/breadcrumb/BreadcrumbPage.astro +6 -6
  23. package/dist/src/components/breadcrumb/BreadcrumbSeparator.astro +7 -7
  24. package/dist/src/components/breadcrumb/index.ts +14 -14
  25. package/dist/src/components/button/Button.astro +38 -38
  26. package/dist/src/components/card/Card.astro +1 -1
  27. package/dist/src/components/card/CardContent.astro +1 -1
  28. package/dist/src/components/card/CardDescription.astro +1 -1
  29. package/dist/src/components/card/CardFooter.astro +1 -1
  30. package/dist/src/components/card/CardHeader.astro +1 -1
  31. package/dist/src/components/card/CardTitle.astro +1 -1
  32. package/dist/src/components/card/index.ts +7 -7
  33. package/dist/src/components/checkbox/Checkbox.astro +89 -89
  34. package/dist/src/components/dialog/Dialog.astro +237 -178
  35. package/dist/src/components/dialog/DialogClose.astro +14 -14
  36. package/dist/src/components/dialog/DialogContent.astro +32 -32
  37. package/dist/src/components/dialog/DialogDescription.astro +1 -1
  38. package/dist/src/components/dialog/DialogFooter.astro +1 -1
  39. package/dist/src/components/dialog/DialogHeader.astro +1 -1
  40. package/dist/src/components/dialog/DialogTitle.astro +6 -6
  41. package/dist/src/components/dialog/DialogTrigger.astro +26 -20
  42. package/dist/src/components/dialog/index.ts +16 -16
  43. package/dist/src/components/dropdown/Dropdown.astro +359 -359
  44. package/dist/src/components/dropdown/DropdownContent.astro +63 -63
  45. package/dist/src/components/dropdown/DropdownItem.astro +31 -31
  46. package/dist/src/components/dropdown/DropdownLabel.astro +14 -14
  47. package/dist/src/components/dropdown/DropdownSeparator.astro +5 -5
  48. package/dist/src/components/dropdown/DropdownTrigger.astro +26 -26
  49. package/dist/src/components/dropdown/index.ts +12 -12
  50. package/dist/src/components/dropzone/Dropzone.astro +232 -0
  51. package/dist/src/components/dropzone/DropzoneFilesList.astro +25 -0
  52. package/dist/src/components/dropzone/DropzoneLoadingIndicator.astro +10 -0
  53. package/dist/src/components/dropzone/DropzoneUploadIndicator.astro +10 -0
  54. package/dist/src/components/dropzone/index.ts +13 -0
  55. package/dist/src/components/input/Input.astro +12 -12
  56. package/dist/src/components/label/Label.astro +8 -8
  57. package/dist/src/components/pagination/Pagination.astro +1 -1
  58. package/dist/src/components/pagination/PaginationContent.astro +3 -3
  59. package/dist/src/components/pagination/PaginationEllipsis.astro +2 -2
  60. package/dist/src/components/pagination/PaginationItem.astro +3 -3
  61. package/dist/src/components/pagination/PaginationLink.astro +27 -27
  62. package/dist/src/components/pagination/PaginationNext.astro +7 -6
  63. package/dist/src/components/pagination/PaginationPrevious.astro +7 -6
  64. package/dist/src/components/pagination/index.ts +14 -14
  65. package/dist/src/components/progress/Progress.astro +151 -0
  66. package/dist/src/components/progress/index.ts +5 -0
  67. package/dist/src/components/radio-group/RadioGroup.astro +156 -0
  68. package/dist/src/components/radio-group/RadioGroupItem.astro +125 -0
  69. package/dist/src/components/radio-group/RadioGroupTypes.ts +6 -0
  70. package/dist/src/components/radio-group/index.ts +10 -0
  71. package/dist/src/components/select/Select.astro +515 -475
  72. package/dist/src/components/select/SelectContent.astro +62 -62
  73. package/dist/src/components/select/SelectItem.astro +27 -27
  74. package/dist/src/components/select/SelectLabel.astro +1 -1
  75. package/dist/src/components/select/SelectTrigger.astro +28 -28
  76. package/dist/src/components/select/SelectTypes.ts +11 -5
  77. package/dist/src/components/select/SelectValue.astro +5 -5
  78. package/dist/src/components/select/index.ts +16 -16
  79. package/dist/src/components/skeleton/Skeleton.astro +14 -0
  80. package/dist/src/components/skeleton/index.ts +5 -0
  81. package/dist/src/components/switch/Switch.astro +150 -150
  82. package/dist/src/components/switch/SwitchTypes.ts +4 -4
  83. package/dist/src/components/table/Table.astro +5 -5
  84. package/dist/src/components/table/TableBody.astro +3 -3
  85. package/dist/src/components/table/TableCaption.astro +3 -3
  86. package/dist/src/components/table/TableCell.astro +3 -3
  87. package/dist/src/components/table/TableFoot.astro +3 -3
  88. package/dist/src/components/table/TableHead.astro +3 -3
  89. package/dist/src/components/table/TableHeader.astro +3 -3
  90. package/dist/src/components/table/TableRow.astro +3 -3
  91. package/dist/src/components/table/index.ts +8 -8
  92. package/dist/src/components/tabs/Tabs.astro +250 -250
  93. package/dist/src/components/tabs/TabsContent.astro +10 -10
  94. package/dist/src/components/tabs/TabsList.astro +2 -2
  95. package/dist/src/components/tabs/TabsTrigger.astro +15 -15
  96. package/dist/src/components/tabs/index.ts +4 -4
  97. package/dist/src/components/textarea/Textarea.astro +16 -16
  98. package/dist/src/components/tooltip/Tooltip.astro +217 -217
  99. package/dist/src/components/tooltip/TooltipContent.astro +81 -81
  100. package/dist/src/components/tooltip/index.ts +3 -3
  101. package/package.json +6 -6
@@ -3,8 +3,8 @@ import type { HTMLAttributes } from "astro/types";
3
3
  import { tv } from "tailwind-variants";
4
4
 
5
5
  interface Props extends HTMLAttributes<"div"> {
6
- defaultValue?: string;
7
- syncKey?: string;
6
+ defaultValue?: string;
7
+ syncKey?: string;
8
8
  }
9
9
 
10
10
  const tabs = tv({ base: "starwind-tabs" });
@@ -13,256 +13,256 @@ const { defaultValue, syncKey, class: className, ...rest } = Astro.props;
13
13
  ---
14
14
 
15
15
  <div
16
- class={tabs({ class: className })}
17
- data-default-value={defaultValue}
18
- data-sync-key={syncKey}
19
- {...rest}
16
+ class={tabs({ class: className })}
17
+ data-default-value={defaultValue}
18
+ data-sync-key={syncKey}
19
+ {...rest}
20
20
  >
21
- <slot />
21
+ <slot />
22
22
  </div>
23
23
 
24
24
  <script>
25
- type TabValue = string;
26
-
27
- interface TabsSyncEventDetail {
28
- value: TabValue;
29
- }
30
-
31
- interface TabsSyncEvent extends CustomEvent<TabsSyncEventDetail> {
32
- type: `starwind-tabs-sync:${string}`;
33
- }
34
-
35
- class TabsHandler {
36
- private tabs: HTMLElement;
37
- private triggers: HTMLButtonElement[];
38
- private contents: HTMLElement[];
39
- private currentTabIndex: number = 0;
40
- private tabsId: string;
41
- private syncKey?: string;
42
- private storageKey: string;
43
- private valueToTriggerMap: Map<string, HTMLButtonElement>;
44
- private valueToContentMap: Map<string, HTMLElement>;
45
- private parentHandler: TabsHandler | null = null;
46
-
47
- constructor(tabs: HTMLElement, idx: number, parentHandler: TabsHandler | null = null) {
48
- this.tabs = tabs;
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]"));
54
- this.tabsId = `starwind-tabs${idx}`;
55
- this.syncKey = tabs.dataset.syncKey;
56
- this.storageKey = this.syncKey
57
- ? `starwind-tabs-${this.syncKey}`
58
- : `starwind-tabs-${this.tabsId}`;
59
-
60
- // Create maps for faster lookups
61
- this.valueToTriggerMap = new Map(
62
- this.triggers.map((trigger) => [trigger.getAttribute("data-value") ?? "", trigger]),
63
- );
64
- this.valueToContentMap = new Map(
65
- this.contents.map((content) => [content.getAttribute("data-value") ?? "", content]),
66
- );
67
-
68
- this.setupIds();
69
- this.initializeTab();
70
- this.addEventListeners();
71
-
72
- if (this.syncKey) {
73
- this.setupSyncListener();
74
- }
75
- }
76
-
77
- private initializeTab(): void {
78
- const value = this.syncKey
79
- ? (localStorage.getItem(this.storageKey) ?? this.tabs.dataset.defaultValue)
80
- : this.tabs.dataset.defaultValue;
81
-
82
- if (value) {
83
- this.showTab(value);
84
- this.currentTabIndex = this.triggers.findIndex(
85
- (trigger) => trigger.getAttribute("data-value") === value,
86
- );
87
- this.setTabIndex();
88
- }
89
- }
90
-
91
- private setupSyncListener(): void {
92
- document.addEventListener(`starwind-tabs-sync:${this.syncKey}`, ((e: TabsSyncEvent) => {
93
- const value = e.detail.value;
94
- const trigger = this.valueToTriggerMap.get(value);
95
- const index = trigger ? this.triggers.indexOf(trigger) : -1;
96
-
97
- if (index !== -1) {
98
- this.showTab(value);
99
- this.currentTabIndex = index;
100
- this.setTabIndex();
101
- }
102
- }) as EventListener);
103
- }
104
-
105
- private setupIds(): void {
106
- this.triggers.forEach((trigger, idx) => {
107
- const triggerId = `${this.tabsId}-t${idx}`;
108
- const contentId = `${this.tabsId}-c${idx}`;
109
- const value = trigger.getAttribute("data-value");
110
-
111
- trigger.id = triggerId;
112
-
113
- if (value) {
114
- trigger.setAttribute("aria-controls", contentId);
115
- const content = this.valueToContentMap.get(value);
116
- if (content) {
117
- content.id = contentId;
118
- content.setAttribute("aria-labelledby", triggerId);
119
- }
120
- }
121
- });
122
- }
123
-
124
- private setTabIndex(): void {
125
- this.triggers.forEach((trigger, index) => {
126
- trigger.setAttribute("tabindex", index === this.currentTabIndex ? "0" : "-1");
127
- });
128
- }
129
-
130
- private dispatchSyncEvent(value: TabValue): void {
131
- if (!this.syncKey) return;
132
-
133
- document.dispatchEvent(
134
- new CustomEvent(`starwind-tabs-sync:${this.syncKey}`, { detail: { value } }),
135
- );
136
-
137
- localStorage.setItem(this.storageKey, value);
138
- }
139
-
140
- private handleKeyNavigation = (e: KeyboardEvent): void => {
141
- const key = e.key;
142
- let newIndex = this.currentTabIndex;
143
-
144
- switch (key) {
145
- case "ArrowRight": {
146
- for (let i = 1; i < this.triggers.length; i++) {
147
- const index = (this.currentTabIndex + i) % this.triggers.length;
148
- if (!this.triggers[index].disabled) {
149
- newIndex = index;
150
- break;
151
- }
152
- }
153
- break;
154
- }
155
- case "ArrowLeft": {
156
- for (let i = 1; i < this.triggers.length; i++) {
157
- const index = (this.currentTabIndex - i + this.triggers.length) % this.triggers.length;
158
- if (!this.triggers[index].disabled) {
159
- newIndex = index;
160
- break;
161
- }
162
- }
163
- break;
164
- }
165
- case "Home": {
166
- for (let i = 0; i < this.triggers.length; i++) {
167
- if (!this.triggers[i].disabled) {
168
- newIndex = i;
169
- break;
170
- }
171
- }
172
- break;
173
- }
174
- case "End": {
175
- for (let i = this.triggers.length - 1; i >= 0; i--) {
176
- if (!this.triggers[i].disabled) {
177
- newIndex = i;
178
- break;
179
- }
180
- }
181
- break;
182
- }
183
- default:
184
- return;
185
- }
186
-
187
- e.preventDefault();
188
- const newTrigger = this.triggers[newIndex];
189
- const value = newTrigger.getAttribute("data-value");
190
- if (value) {
191
- this.showTab(value);
192
- this.currentTabIndex = newIndex;
193
- this.setTabIndex();
194
- newTrigger.focus();
195
- this.dispatchSyncEvent(value);
196
- }
197
- };
198
-
199
- private handleClick = (trigger: HTMLElement, index: number): void => {
200
- const value = trigger.getAttribute("data-value");
201
- if (value) {
202
- this.showTab(value);
203
- this.currentTabIndex = index;
204
- this.setTabIndex();
205
- trigger.focus();
206
- this.dispatchSyncEvent(value);
207
- }
208
- };
209
-
210
- private addEventListeners(): void {
211
- this.triggers.forEach((trigger, index) => {
212
- trigger.addEventListener("click", () => this.handleClick(trigger, index));
213
- trigger.addEventListener("keydown", (e) => this.handleKeyNavigation(e));
214
- });
215
- }
216
-
217
- private showTab(value: TabValue): void {
218
- const trigger = this.valueToTriggerMap.get(value);
219
- const content = this.valueToContentMap.get(value);
220
-
221
- if (!trigger || !content) return;
222
-
223
- // Update all triggers and contents
224
- this.triggers.forEach((t) => {
225
- const isActive = t === trigger;
226
- t.setAttribute("data-state", isActive ? "active" : "inactive");
227
- t.setAttribute("aria-selected", isActive.toString());
228
- });
229
-
230
- this.contents.forEach((c) => {
231
- const isActive = c === content;
232
- c.setAttribute("data-state", isActive ? "active" : "inactive");
233
- c.hidden = !isActive;
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
- }
249
- }
250
- }
251
-
252
- // Store instances in a WeakMap to avoid memory leaks
253
- const tabInstances = new WeakMap<HTMLElement, TabsHandler>();
254
-
255
- const setupTabs = () => {
256
- // First handle top-level tabs
257
- document.querySelectorAll<HTMLElement>(".starwind-tabs").forEach((tabs, idx) => {
258
- // Skip tabs that are nested within other tab contents
259
- const isNested = !!tabs.closest("[data-tabs-content]");
260
- if (!isNested && !tabInstances.has(tabs)) {
261
- tabInstances.set(tabs, new TabsHandler(tabs, idx));
262
- }
263
- });
264
- };
265
-
266
- setupTabs();
267
- document.addEventListener("astro:after-swap", setupTabs);
25
+ type TabValue = string;
26
+
27
+ interface TabsSyncEventDetail {
28
+ value: TabValue;
29
+ }
30
+
31
+ interface TabsSyncEvent extends CustomEvent<TabsSyncEventDetail> {
32
+ type: `starwind-tabs-sync:${string}`;
33
+ }
34
+
35
+ class TabsHandler {
36
+ private tabs: HTMLElement;
37
+ private triggers: HTMLButtonElement[];
38
+ private contents: HTMLElement[];
39
+ private currentTabIndex: number = 0;
40
+ private tabsId: string;
41
+ private syncKey?: string;
42
+ private storageKey: string;
43
+ private valueToTriggerMap: Map<string, HTMLButtonElement>;
44
+ private valueToContentMap: Map<string, HTMLElement>;
45
+ private parentHandler: TabsHandler | null = null;
46
+
47
+ constructor(tabs: HTMLElement, idx: number, parentHandler: TabsHandler | null = null) {
48
+ this.tabs = tabs;
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]"));
54
+ this.tabsId = `starwind-tabs${idx}`;
55
+ this.syncKey = tabs.dataset.syncKey;
56
+ this.storageKey = this.syncKey
57
+ ? `starwind-tabs-${this.syncKey}`
58
+ : `starwind-tabs-${this.tabsId}`;
59
+
60
+ // Create maps for faster lookups
61
+ this.valueToTriggerMap = new Map(
62
+ this.triggers.map((trigger) => [trigger.getAttribute("data-value") ?? "", trigger]),
63
+ );
64
+ this.valueToContentMap = new Map(
65
+ this.contents.map((content) => [content.getAttribute("data-value") ?? "", content]),
66
+ );
67
+
68
+ this.setupIds();
69
+ this.initializeTab();
70
+ this.addEventListeners();
71
+
72
+ if (this.syncKey) {
73
+ this.setupSyncListener();
74
+ }
75
+ }
76
+
77
+ private initializeTab(): void {
78
+ const value = this.syncKey
79
+ ? (localStorage.getItem(this.storageKey) ?? this.tabs.dataset.defaultValue)
80
+ : this.tabs.dataset.defaultValue;
81
+
82
+ if (value) {
83
+ this.showTab(value);
84
+ this.currentTabIndex = this.triggers.findIndex(
85
+ (trigger) => trigger.getAttribute("data-value") === value,
86
+ );
87
+ this.setTabIndex();
88
+ }
89
+ }
90
+
91
+ private setupSyncListener(): void {
92
+ document.addEventListener(`starwind-tabs-sync:${this.syncKey}`, ((e: TabsSyncEvent) => {
93
+ const value = e.detail.value;
94
+ const trigger = this.valueToTriggerMap.get(value);
95
+ const index = trigger ? this.triggers.indexOf(trigger) : -1;
96
+
97
+ if (index !== -1) {
98
+ this.showTab(value);
99
+ this.currentTabIndex = index;
100
+ this.setTabIndex();
101
+ }
102
+ }) as EventListener);
103
+ }
104
+
105
+ private setupIds(): void {
106
+ this.triggers.forEach((trigger, idx) => {
107
+ const triggerId = `${this.tabsId}-t${idx}`;
108
+ const contentId = `${this.tabsId}-c${idx}`;
109
+ const value = trigger.getAttribute("data-value");
110
+
111
+ trigger.id = triggerId;
112
+
113
+ if (value) {
114
+ trigger.setAttribute("aria-controls", contentId);
115
+ const content = this.valueToContentMap.get(value);
116
+ if (content) {
117
+ content.id = contentId;
118
+ content.setAttribute("aria-labelledby", triggerId);
119
+ }
120
+ }
121
+ });
122
+ }
123
+
124
+ private setTabIndex(): void {
125
+ this.triggers.forEach((trigger, index) => {
126
+ trigger.setAttribute("tabindex", index === this.currentTabIndex ? "0" : "-1");
127
+ });
128
+ }
129
+
130
+ private dispatchSyncEvent(value: TabValue): void {
131
+ if (!this.syncKey) return;
132
+
133
+ document.dispatchEvent(
134
+ new CustomEvent(`starwind-tabs-sync:${this.syncKey}`, { detail: { value } }),
135
+ );
136
+
137
+ localStorage.setItem(this.storageKey, value);
138
+ }
139
+
140
+ private handleKeyNavigation = (e: KeyboardEvent): void => {
141
+ const key = e.key;
142
+ let newIndex = this.currentTabIndex;
143
+
144
+ switch (key) {
145
+ case "ArrowRight": {
146
+ for (let i = 1; i < this.triggers.length; i++) {
147
+ const index = (this.currentTabIndex + i) % this.triggers.length;
148
+ if (!this.triggers[index].disabled) {
149
+ newIndex = index;
150
+ break;
151
+ }
152
+ }
153
+ break;
154
+ }
155
+ case "ArrowLeft": {
156
+ for (let i = 1; i < this.triggers.length; i++) {
157
+ const index = (this.currentTabIndex - i + this.triggers.length) % this.triggers.length;
158
+ if (!this.triggers[index].disabled) {
159
+ newIndex = index;
160
+ break;
161
+ }
162
+ }
163
+ break;
164
+ }
165
+ case "Home": {
166
+ for (let i = 0; i < this.triggers.length; i++) {
167
+ if (!this.triggers[i].disabled) {
168
+ newIndex = i;
169
+ break;
170
+ }
171
+ }
172
+ break;
173
+ }
174
+ case "End": {
175
+ for (let i = this.triggers.length - 1; i >= 0; i--) {
176
+ if (!this.triggers[i].disabled) {
177
+ newIndex = i;
178
+ break;
179
+ }
180
+ }
181
+ break;
182
+ }
183
+ default:
184
+ return;
185
+ }
186
+
187
+ e.preventDefault();
188
+ const newTrigger = this.triggers[newIndex];
189
+ const value = newTrigger.getAttribute("data-value");
190
+ if (value) {
191
+ this.showTab(value);
192
+ this.currentTabIndex = newIndex;
193
+ this.setTabIndex();
194
+ newTrigger.focus();
195
+ this.dispatchSyncEvent(value);
196
+ }
197
+ };
198
+
199
+ private handleClick = (trigger: HTMLElement, index: number): void => {
200
+ const value = trigger.getAttribute("data-value");
201
+ if (value) {
202
+ this.showTab(value);
203
+ this.currentTabIndex = index;
204
+ this.setTabIndex();
205
+ trigger.focus();
206
+ this.dispatchSyncEvent(value);
207
+ }
208
+ };
209
+
210
+ private addEventListeners(): void {
211
+ this.triggers.forEach((trigger, index) => {
212
+ trigger.addEventListener("click", () => this.handleClick(trigger, index));
213
+ trigger.addEventListener("keydown", (e) => this.handleKeyNavigation(e));
214
+ });
215
+ }
216
+
217
+ private showTab(value: TabValue): void {
218
+ const trigger = this.valueToTriggerMap.get(value);
219
+ const content = this.valueToContentMap.get(value);
220
+
221
+ if (!trigger || !content) return;
222
+
223
+ // Update all triggers and contents
224
+ this.triggers.forEach((t) => {
225
+ const isActive = t === trigger;
226
+ t.setAttribute("data-state", isActive ? "active" : "inactive");
227
+ t.setAttribute("aria-selected", isActive.toString());
228
+ });
229
+
230
+ this.contents.forEach((c) => {
231
+ const isActive = c === content;
232
+ c.setAttribute("data-state", isActive ? "active" : "inactive");
233
+ c.hidden = !isActive;
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
+ }
249
+ }
250
+ }
251
+
252
+ // Store instances in a WeakMap to avoid memory leaks
253
+ const tabInstances = new WeakMap<HTMLElement, TabsHandler>();
254
+
255
+ const setupTabs = () => {
256
+ // First handle top-level tabs
257
+ document.querySelectorAll<HTMLElement>(".starwind-tabs").forEach((tabs, idx) => {
258
+ // Skip tabs that are nested within other tab contents
259
+ const isNested = !!tabs.closest("[data-tabs-content]");
260
+ if (!isNested && !tabInstances.has(tabs)) {
261
+ tabInstances.set(tabs, new TabsHandler(tabs, idx));
262
+ }
263
+ });
264
+ };
265
+
266
+ setupTabs();
267
+ document.addEventListener("astro:after-swap", setupTabs);
268
268
  </script>
@@ -3,7 +3,7 @@ import type { HTMLAttributes } from "astro/types";
3
3
  import { tv } from "tailwind-variants";
4
4
 
5
5
  interface Props extends Omit<HTMLAttributes<"div">, "id" | "role" | "tabindex" | "hidden"> {
6
- value: string;
6
+ value: string;
7
7
  }
8
8
 
9
9
  const tabsContent = tv({ base: "mt-2 focus-visible:outline-2 focus-visible:outline-offset-2" });
@@ -12,14 +12,14 @@ const { value, class: className, ...rest } = Astro.props;
12
12
  ---
13
13
 
14
14
  <div
15
- class={tabsContent({ class: className })}
16
- data-tabs-content
17
- data-value={value}
18
- data-state="inactive"
19
- role="tabpanel"
20
- tabindex="0"
21
- hidden
22
- {...rest}
15
+ class={tabsContent({ class: className })}
16
+ data-tabs-content
17
+ data-value={value}
18
+ data-state="inactive"
19
+ role="tabpanel"
20
+ tabindex="0"
21
+ hidden
22
+ {...rest}
23
23
  >
24
- <slot />
24
+ <slot />
25
25
  </div>
@@ -5,12 +5,12 @@ import { tv } from "tailwind-variants";
5
5
  type Props = Omit<HTMLAttributes<"div">, "role">;
6
6
 
7
7
  const tabsList = tv({
8
- base: "bg-muted text-muted-foreground inline-flex w-full items-center justify-center rounded-md p-1",
8
+ base: "bg-muted text-muted-foreground inline-flex w-full items-center justify-center rounded-md p-1",
9
9
  });
10
10
 
11
11
  const { class: className, ...rest } = Astro.props;
12
12
  ---
13
13
 
14
14
  <div class={tabsList({ class: className })} data-tabs-list role="tablist" {...rest}>
15
- <slot />
15
+ <slot />
16
16
  </div>
@@ -3,29 +3,29 @@ import type { HTMLAttributes } from "astro/types";
3
3
  import { tv } from "tailwind-variants";
4
4
 
5
5
  interface Props extends Omit<HTMLAttributes<"button">, "type" | "id" | "role"> {
6
- value: string;
6
+ value: string;
7
7
  }
8
8
 
9
9
  const tabsTrigger = tv({
10
- base: [
11
- "starwind-transition-colors inline-flex grow items-center justify-center rounded-sm px-3 py-1.5 font-medium whitespace-nowrap",
12
- "data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm",
13
- "focus-visible:outline-outline focus-visible:outline-2 focus-visible:outline-offset-2",
14
- "disabled:pointer-events-none disabled:opacity-50",
15
- ],
10
+ base: [
11
+ "starwind-transition-colors inline-flex grow items-center justify-center rounded-sm px-3 py-1.5 font-medium whitespace-nowrap",
12
+ "data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm",
13
+ "focus-visible:outline-outline focus-visible:outline-2 focus-visible:outline-offset-2",
14
+ "disabled:pointer-events-none disabled:opacity-50",
15
+ ],
16
16
  });
17
17
 
18
18
  const { value, class: className, ...rest } = Astro.props;
19
19
  ---
20
20
 
21
21
  <button
22
- class={tabsTrigger({ class: className })}
23
- data-tabs-trigger
24
- data-value={value}
25
- data-state="inactive"
26
- role="tab"
27
- aria-selected="false"
28
- {...rest}
22
+ class={tabsTrigger({ class: className })}
23
+ data-tabs-trigger
24
+ data-value={value}
25
+ data-state="inactive"
26
+ role="tab"
27
+ aria-selected="false"
28
+ {...rest}
29
29
  >
30
- <slot />
30
+ <slot />
31
31
  </button>
@@ -6,8 +6,8 @@ import TabsTrigger from "./TabsTrigger.astro";
6
6
  export { Tabs, TabsContent, TabsList, TabsTrigger };
7
7
 
8
8
  export default {
9
- Root: Tabs,
10
- Content: TabsContent,
11
- List: TabsList,
12
- Trigger: TabsTrigger,
9
+ Root: Tabs,
10
+ Content: TabsContent,
11
+ List: TabsList,
12
+ Trigger: TabsTrigger,
13
13
  };