@studiocms/ui 0.4.16 → 1.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/dist/components/Accordion/Accordion.astro +1 -0
  2. package/dist/components/Accordion/Item.astro +8 -6
  3. package/dist/components/Accordion/accordion.css +35 -21
  4. package/dist/components/Accordion/accordion.d.ts +46 -1
  5. package/dist/components/Accordion/accordion.js +95 -70
  6. package/dist/components/Badge/Badge.astro +2 -2
  7. package/dist/components/Badge/badge.css +32 -32
  8. package/dist/components/Breadcrumbs/breadcrumbs.css +1 -1
  9. package/dist/components/Button/button.css +93 -93
  10. package/dist/components/Card/card.css +4 -4
  11. package/dist/components/Checkbox/checkbox.css +26 -26
  12. package/dist/components/Divider/Divider.astro +1 -1
  13. package/dist/components/Divider/divider.css +2 -2
  14. package/dist/components/Dropdown/Dropdown.astro +2 -2
  15. package/dist/components/Dropdown/dropdown.css +26 -26
  16. package/dist/components/Footer/footer.css +4 -4
  17. package/dist/components/Icon/Icon.astro +114 -6
  18. package/dist/components/Icon/IconBase.astro +108 -7
  19. package/dist/components/Icon/errors.d.ts +29 -0
  20. package/dist/components/Icon/errors.js +25 -0
  21. package/dist/components/Input/input.css +9 -9
  22. package/dist/components/Modal/Modal.astro +1 -1
  23. package/dist/components/Modal/modal.css +4 -4
  24. package/dist/components/Progress/progress.css +7 -7
  25. package/dist/components/RadioGroup/radiogroup.css +21 -21
  26. package/dist/components/SearchSelect/SearchSelect.astro +29 -27
  27. package/dist/components/SearchSelect/searchselect.css +26 -15
  28. package/dist/components/SearchSelect/searchselect.js +29 -9
  29. package/dist/components/Select/Select.astro +28 -26
  30. package/dist/components/Select/select.css +32 -20
  31. package/dist/components/Select/select.js +1 -1
  32. package/dist/components/Sidebar/Double.astro +4 -4
  33. package/dist/components/Sidebar/Single.astro +2 -2
  34. package/dist/components/Skeleton/Skeleton.astro +61 -0
  35. package/dist/components/Skeleton/skeleton.css +111 -0
  36. package/dist/components/Skeleton/skeleton.d.ts +161 -0
  37. package/dist/components/Skeleton/skeleton.js +71 -0
  38. package/dist/components/Tabs/TabItem.astro +14 -9
  39. package/dist/components/Tabs/Tabs.astro +91 -54
  40. package/dist/components/Tabs/tabs.css +14 -14
  41. package/dist/components/Tabs/tabs.d.ts +17 -1
  42. package/dist/components/Tabs/tabs.js +99 -76
  43. package/dist/components/Textarea/textarea.css +8 -8
  44. package/dist/components/Toast/toaster.css +26 -23
  45. package/dist/components/Toast/toaster.js +4 -0
  46. package/dist/components/Toggle/toggle.css +13 -13
  47. package/dist/components/Tooltip/Tooltip.astro +119 -0
  48. package/dist/components/Tooltip/tooltip.css +187 -0
  49. package/dist/components/Tooltip/tooltip.d.ts +46 -0
  50. package/dist/components/Tooltip/tooltip.js +330 -0
  51. package/dist/components/User/User.astro +1 -1
  52. package/dist/components/User/user.css +3 -3
  53. package/dist/css/colors.css +85 -83
  54. package/dist/css/prose.css +360 -0
  55. package/dist/css/resets.css +3 -3
  56. package/dist/events.d.ts +104 -0
  57. package/dist/index.d.ts +25 -0
  58. package/dist/index.js +120 -313
  59. package/dist/toolbar/index.js +5 -5
  60. package/dist/types/index.d.ts +2 -0
  61. package/dist/utils/iconifyUtils.d.ts +1 -1
  62. package/dist/virtuals.d.js +0 -0
  63. package/dist/virtuals.d.ts +564 -0
  64. package/package.json +5 -1
  65. package/dist/components/Icon/iconType.d.ts +0 -2
  66. package/dist/components/ThemeToggle/ThemeToggle.astro +0 -21
  67. package/dist/components/ThemeToggle/themetoggle.css +0 -17
  68. package/dist/components/ThemeToggle/themetoggle.d.ts +0 -1
  69. package/dist/components/ThemeToggle/themetoggle.js +0 -4
  70. /package/dist/{components/Icon/iconType.js → events.d.js} +0 -0
@@ -0,0 +1,161 @@
1
+ import { z } from 'astro/zod';
2
+ export declare const SkeletonSchema: z.ZodUnion<[z.ZodObject<{
3
+ /**
4
+ * The variant of the skeleton.
5
+ */
6
+ variant: z.ZodLiteral<"text">;
7
+ /**
8
+ * The class of the skeleton. Defaults to `undefined`.
9
+ */
10
+ class: z.ZodOptional<z.ZodString>;
11
+ /**
12
+ * The radius of the skeleton. Defaults to `lg`.
13
+ */
14
+ radius: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<"none">, z.ZodLiteral<"sm">, z.ZodLiteral<"md">, z.ZodLiteral<"lg">]>>;
15
+ /**
16
+ * The animation of the skeleton. Defaults to `slide`.
17
+ */
18
+ animation: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<"none">, z.ZodLiteral<"slide">, z.ZodLiteral<"pulse">]>>;
19
+ /**
20
+ * The width of the skeleton. Defaults to `100%`.
21
+ */
22
+ width: z.ZodOptional<z.ZodUnion<[z.ZodType<`${number}px`, z.ZodTypeDef, `${number}px`>, z.ZodType<`${number}rem`, z.ZodTypeDef, `${number}rem`>, z.ZodType<`${number}%`, z.ZodTypeDef, `${number}%`>]>>;
23
+ /**
24
+ * The height of the skeleton. Defaults to `100%`.
25
+ */
26
+ height: z.ZodOptional<z.ZodUnion<[z.ZodType<`${number}px`, z.ZodTypeDef, `${number}px`>, z.ZodType<`${number}rem`, z.ZodTypeDef, `${number}rem`>, z.ZodType<`${number}%`, z.ZodTypeDef, `${number}%`>]>>;
27
+ }, "strip", z.ZodTypeAny, {
28
+ variant: "text";
29
+ width?: `${number}px` | `${number}rem` | `${number}%` | undefined;
30
+ height?: `${number}px` | `${number}rem` | `${number}%` | undefined;
31
+ class?: string | undefined;
32
+ radius?: "sm" | "none" | "md" | "lg" | undefined;
33
+ animation?: "none" | "slide" | "pulse" | undefined;
34
+ }, {
35
+ variant: "text";
36
+ width?: `${number}px` | `${number}rem` | `${number}%` | undefined;
37
+ height?: `${number}px` | `${number}rem` | `${number}%` | undefined;
38
+ class?: string | undefined;
39
+ radius?: "sm" | "none" | "md" | "lg" | undefined;
40
+ animation?: "none" | "slide" | "pulse" | undefined;
41
+ }>, z.ZodObject<z.objectUtil.extendShape<{
42
+ /**
43
+ * The variant of the skeleton.
44
+ */
45
+ variant: z.ZodLiteral<"text">;
46
+ /**
47
+ * The class of the skeleton. Defaults to `undefined`.
48
+ */
49
+ class: z.ZodOptional<z.ZodString>;
50
+ /**
51
+ * The radius of the skeleton. Defaults to `lg`.
52
+ */
53
+ radius: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<"none">, z.ZodLiteral<"sm">, z.ZodLiteral<"md">, z.ZodLiteral<"lg">]>>;
54
+ /**
55
+ * The animation of the skeleton. Defaults to `slide`.
56
+ */
57
+ animation: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<"none">, z.ZodLiteral<"slide">, z.ZodLiteral<"pulse">]>>;
58
+ /**
59
+ * The width of the skeleton. Defaults to `100%`.
60
+ */
61
+ width: z.ZodOptional<z.ZodUnion<[z.ZodType<`${number}px`, z.ZodTypeDef, `${number}px`>, z.ZodType<`${number}rem`, z.ZodTypeDef, `${number}rem`>, z.ZodType<`${number}%`, z.ZodTypeDef, `${number}%`>]>>;
62
+ /**
63
+ * The height of the skeleton. Defaults to `100%`.
64
+ */
65
+ height: z.ZodOptional<z.ZodUnion<[z.ZodType<`${number}px`, z.ZodTypeDef, `${number}px`>, z.ZodType<`${number}rem`, z.ZodTypeDef, `${number}rem`>, z.ZodType<`${number}%`, z.ZodTypeDef, `${number}%`>]>>;
66
+ }, {
67
+ /**
68
+ * The variant of the skeleton. `card` is used for cards which contain other Skeleton components.
69
+ */
70
+ variant: z.ZodLiteral<"card">;
71
+ /**
72
+ * The direction of the skeleton. Defaults to `column`.
73
+ */
74
+ direction: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<"row">, z.ZodLiteral<"column">]>>;
75
+ /**
76
+ * The horizontal alignment of the skeleton. Defaults to `center`.
77
+ */
78
+ hAlign: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<"start">, z.ZodLiteral<"center">, z.ZodLiteral<"end">]>>;
79
+ /**
80
+ * The vertical alignment of the skeleton. Defaults to `center`.
81
+ */
82
+ vAlign: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<"start">, z.ZodLiteral<"center">, z.ZodLiteral<"end">]>>;
83
+ /**
84
+ * The gap between the skeletons. Defaults to `0.5rem`.
85
+ */
86
+ gap: z.ZodOptional<z.ZodUnion<[z.ZodType<`${number}px`, z.ZodTypeDef, `${number}px`>, z.ZodType<`${number}rem`, z.ZodTypeDef, `${number}rem`>, z.ZodType<`${number}%`, z.ZodTypeDef, `${number}%`>]>>;
87
+ }>, "strip", z.ZodTypeAny, {
88
+ variant: "card";
89
+ width?: `${number}px` | `${number}rem` | `${number}%` | undefined;
90
+ height?: `${number}px` | `${number}rem` | `${number}%` | undefined;
91
+ class?: string | undefined;
92
+ radius?: "sm" | "none" | "md" | "lg" | undefined;
93
+ animation?: "none" | "slide" | "pulse" | undefined;
94
+ direction?: "row" | "column" | undefined;
95
+ hAlign?: "center" | "start" | "end" | undefined;
96
+ vAlign?: "center" | "start" | "end" | undefined;
97
+ gap?: `${number}px` | `${number}rem` | `${number}%` | undefined;
98
+ }, {
99
+ variant: "card";
100
+ width?: `${number}px` | `${number}rem` | `${number}%` | undefined;
101
+ height?: `${number}px` | `${number}rem` | `${number}%` | undefined;
102
+ class?: string | undefined;
103
+ radius?: "sm" | "none" | "md" | "lg" | undefined;
104
+ animation?: "none" | "slide" | "pulse" | undefined;
105
+ direction?: "row" | "column" | undefined;
106
+ hAlign?: "center" | "start" | "end" | undefined;
107
+ vAlign?: "center" | "start" | "end" | undefined;
108
+ gap?: `${number}px` | `${number}rem` | `${number}%` | undefined;
109
+ }>, z.ZodObject<z.objectUtil.extendShape<{
110
+ /**
111
+ * The variant of the skeleton.
112
+ */
113
+ variant: z.ZodLiteral<"text">;
114
+ /**
115
+ * The class of the skeleton. Defaults to `undefined`.
116
+ */
117
+ class: z.ZodOptional<z.ZodString>;
118
+ /**
119
+ * The radius of the skeleton. Defaults to `lg`.
120
+ */
121
+ radius: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<"none">, z.ZodLiteral<"sm">, z.ZodLiteral<"md">, z.ZodLiteral<"lg">]>>;
122
+ /**
123
+ * The animation of the skeleton. Defaults to `slide`.
124
+ */
125
+ animation: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<"none">, z.ZodLiteral<"slide">, z.ZodLiteral<"pulse">]>>;
126
+ /**
127
+ * The width of the skeleton. Defaults to `100%`.
128
+ */
129
+ width: z.ZodOptional<z.ZodUnion<[z.ZodType<`${number}px`, z.ZodTypeDef, `${number}px`>, z.ZodType<`${number}rem`, z.ZodTypeDef, `${number}rem`>, z.ZodType<`${number}%`, z.ZodTypeDef, `${number}%`>]>>;
130
+ /**
131
+ * The height of the skeleton. Defaults to `100%`.
132
+ */
133
+ height: z.ZodOptional<z.ZodUnion<[z.ZodType<`${number}px`, z.ZodTypeDef, `${number}px`>, z.ZodType<`${number}rem`, z.ZodTypeDef, `${number}rem`>, z.ZodType<`${number}%`, z.ZodTypeDef, `${number}%`>]>>;
134
+ }, {
135
+ /**
136
+ * The variant of the skeleton. `circle` is used for circles which contain other Skeleton components.
137
+ */
138
+ variant: z.ZodUnion<[z.ZodLiteral<"circle">, z.ZodLiteral<"block">]>;
139
+ /**
140
+ * The width of the skeleton.
141
+ */
142
+ width: z.ZodUnion<[z.ZodType<`${number}px`, z.ZodTypeDef, `${number}px`>, z.ZodType<`${number}rem`, z.ZodTypeDef, `${number}rem`>, z.ZodType<`${number}%`, z.ZodTypeDef, `${number}%`>]>;
143
+ /**
144
+ * The height of the skeleton.
145
+ */
146
+ height: z.ZodUnion<[z.ZodType<`${number}px`, z.ZodTypeDef, `${number}px`>, z.ZodType<`${number}rem`, z.ZodTypeDef, `${number}rem`>, z.ZodType<`${number}%`, z.ZodTypeDef, `${number}%`>]>;
147
+ }>, "strip", z.ZodTypeAny, {
148
+ width: `${number}px` | `${number}rem` | `${number}%`;
149
+ height: `${number}px` | `${number}rem` | `${number}%`;
150
+ variant: "circle" | "block";
151
+ class?: string | undefined;
152
+ radius?: "sm" | "none" | "md" | "lg" | undefined;
153
+ animation?: "none" | "slide" | "pulse" | undefined;
154
+ }, {
155
+ width: `${number}px` | `${number}rem` | `${number}%`;
156
+ height: `${number}px` | `${number}rem` | `${number}%`;
157
+ variant: "circle" | "block";
158
+ class?: string | undefined;
159
+ radius?: "sm" | "none" | "md" | "lg" | undefined;
160
+ animation?: "none" | "slide" | "pulse" | undefined;
161
+ }>]>;
@@ -0,0 +1,71 @@
1
+ import { z } from "astro/zod";
2
+ const measurementPx = z.custom();
3
+ const measurementRem = z.custom();
4
+ const measurementPercent = z.custom();
5
+ const measurement = z.union([measurementPx, measurementRem, measurementPercent]);
6
+ const BaseSkeletonSchema = z.object({
7
+ /**
8
+ * The variant of the skeleton.
9
+ */
10
+ variant: z.literal("text"),
11
+ /**
12
+ * The class of the skeleton. Defaults to `undefined`.
13
+ */
14
+ class: z.string().optional(),
15
+ /**
16
+ * The radius of the skeleton. Defaults to `lg`.
17
+ */
18
+ radius: z.union([z.literal("none"), z.literal("sm"), z.literal("md"), z.literal("lg")]).optional(),
19
+ /**
20
+ * The animation of the skeleton. Defaults to `slide`.
21
+ */
22
+ animation: z.union([z.literal("none"), z.literal("slide"), z.literal("pulse")]).optional(),
23
+ /**
24
+ * The width of the skeleton. Defaults to `100%`.
25
+ */
26
+ width: measurement.optional(),
27
+ /**
28
+ * The height of the skeleton. Defaults to `100%`.
29
+ */
30
+ height: measurement.optional()
31
+ });
32
+ const CardSkeletonSchema = BaseSkeletonSchema.extend({
33
+ /**
34
+ * The variant of the skeleton. `card` is used for cards which contain other Skeleton components.
35
+ */
36
+ variant: z.literal("card"),
37
+ /**
38
+ * The direction of the skeleton. Defaults to `column`.
39
+ */
40
+ direction: z.union([z.literal("row"), z.literal("column")]).optional(),
41
+ /**
42
+ * The horizontal alignment of the skeleton. Defaults to `center`.
43
+ */
44
+ hAlign: z.union([z.literal("start"), z.literal("center"), z.literal("end")]).optional(),
45
+ /**
46
+ * The vertical alignment of the skeleton. Defaults to `center`.
47
+ */
48
+ vAlign: z.union([z.literal("start"), z.literal("center"), z.literal("end")]).optional(),
49
+ /**
50
+ * The gap between the skeletons. Defaults to `0.5rem`.
51
+ */
52
+ gap: measurement.optional()
53
+ });
54
+ const BlockCircleSchema = BaseSkeletonSchema.extend({
55
+ /**
56
+ * The variant of the skeleton. `circle` is used for circles which contain other Skeleton components.
57
+ */
58
+ variant: z.union([z.literal("circle"), z.literal("block")]),
59
+ /**
60
+ * The width of the skeleton.
61
+ */
62
+ width: measurement,
63
+ /**
64
+ * The height of the skeleton.
65
+ */
66
+ height: measurement
67
+ });
68
+ const SkeletonSchema = z.union([BaseSkeletonSchema, CardSkeletonSchema, BlockCircleSchema]);
69
+ export {
70
+ SkeletonSchema
71
+ };
@@ -1,7 +1,7 @@
1
1
  ---
2
+ import type { AvailableIcons } from 'studiocms:ui/icons';
2
3
  import type { StudioCMSColorway } from '../../utils/colors.js';
3
4
  import { generateID } from '../../utils/generateID.js';
4
- import type { HeroIconName } from '../Icon/iconType.js';
5
5
 
6
6
  /**
7
7
  * The props for the TabItem component.
@@ -10,7 +10,7 @@ interface Props {
10
10
  /**
11
11
  * The icon to display next to the tab.
12
12
  */
13
- icon?: HeroIconName;
13
+ icon?: AvailableIcons;
14
14
  /**
15
15
  * The label of the tab.
16
16
  */
@@ -19,19 +19,24 @@ interface Props {
19
19
  * The color of the tab. Defaults to `primary`.
20
20
  */
21
21
  color?: Exclude<StudioCMSColorway, 'default'>;
22
+ /**
23
+ * Whether the tab should be active by default.
24
+ */
25
+ active?: boolean;
22
26
  }
23
27
 
24
28
  const id = generateID('tab');
25
29
 
26
- const { icon, label, color = 'primary' } = Astro.props;
30
+ const { icon, label, color = 'primary', active = false } = Astro.props;
27
31
  ---
28
- <sui-tab-item
29
- data-icon={icon}
30
- data-label={label}
31
- data-color={color}
32
- data-tab-id={id}
32
+ <sui-tab-item
33
+ data-icon={icon}
34
+ data-label={label}
35
+ data-color={color}
36
+ data-tab-id={id}
37
+ data-active={active ? 'true' : undefined}
33
38
  >
34
- <slot />
39
+ <slot />
35
40
  </sui-tab-item>
36
41
  <style>
37
42
  sui-tab-item {
@@ -1,15 +1,16 @@
1
1
  ---
2
+ import type { AvailableIcons } from 'studiocms:ui/icons';
2
3
  import type { StudioCMSColorway } from '../../utils/colors.js';
3
4
  import { generateID } from '../../utils/generateID.js';
4
5
  import Icon from '../Icon/Icon.astro';
5
- import type { HeroIconName } from '../Icon/iconType.js';
6
6
  import './tabs.css';
7
7
 
8
8
  interface Tab {
9
- icon?: HeroIconName;
9
+ icon?: AvailableIcons;
10
10
  label: string;
11
11
  color: Exclude<StudioCMSColorway, 'default'>;
12
12
  tabId: string;
13
+ active?: boolean;
13
14
  }
14
15
 
15
16
  /**
@@ -35,63 +36,94 @@ interface Props {
35
36
  align?: 'left' | 'center' | 'right';
36
37
  }
37
38
 
38
- const extractTabInfoWithRegex = (input: string) => {
39
- const tabItemRegex = /<sui-tab-item([^>]*)>/g;
40
-
39
+ /**
40
+ * Parses data attributes from an HTML tag string.
41
+ * @param tagContent The string content of the HTML tag.
42
+ * @returns An object of key-value pairs for the parsed attributes.
43
+ */
44
+ const getAttributes = (tagContent: string) => {
45
+ const attributes: { [key: string]: string | boolean } = {};
41
46
  const attributeRegex = /data-([\w-]+)="([^"]*)"/g;
47
+ for (const match of tagContent.matchAll(attributeRegex)) {
48
+ const [, key, value] = match;
49
+ if (!key || !value) continue;
50
+ if (key === 'icon' || key === 'label' || key === 'color') {
51
+ attributes[key] = value;
52
+ } else if (key === 'tab-id') {
53
+ attributes.tabId = value;
54
+ } else if (key === 'active') {
55
+ attributes.active = value === 'true';
56
+ }
57
+ }
58
+ return attributes;
59
+ };
42
60
 
43
- const tabs: Tab[] = [];
44
- let tabMatch: RegExpExecArray | null;
45
-
46
- // biome-ignore lint/suspicious/noAssignInExpressions: Nop
47
- while ((tabMatch = tabItemRegex.exec(input)) !== null) {
48
- let attributes: { [key: string]: string } = {};
49
-
50
- let attributeMatch: RegExpExecArray | null;
51
-
52
- if (!tabMatch[1]) continue;
53
-
54
- // biome-ignore lint/suspicious/noAssignInExpressions: Nop
55
- while ((attributeMatch = attributeRegex.exec(tabMatch[1])) !== null) {
56
- if (!attributeMatch[1] || !attributeMatch[2]) continue;
57
-
58
- if (
59
- attributeMatch[1] === 'icon' ||
60
- attributeMatch[1] === 'label' ||
61
- attributeMatch[1] === 'color'
62
- ) {
63
- attributes[attributeMatch[1]] = attributeMatch[2];
61
+ /**
62
+ * Extracts direct child Tab info from an HTML string, ignoring nested tabs.
63
+ * This function uses a two-pass approach to handle nesting.
64
+ * 1. It first scans for all nested `sui-tabs-container` elements and records their start and end positions.
65
+ * 2. It then finds all `sui-tab-item` elements and includes only those that do not fall within the nested ranges.
66
+ * @param html The raw HTML string from the rendered slot.
67
+ * @returns An array of Tab objects for direct children only.
68
+ */
69
+ const extractTabInfoWithRegex = (html: string): Tab[] => {
70
+ const nestedContainerRanges: { start: number; end: number }[] = [];
71
+ const containerRegex = /<div[^>]*?class="[^"]*?\bsui-tabs-container\b[^"]*?".*?>/gi;
72
+ for (const containerMatch of html.matchAll(containerRegex)) {
73
+ const startIndex = containerMatch.index;
74
+ let openCount = 0;
75
+ const searchArea = html.substring(startIndex);
76
+ const tagRegex = /<\/?div/gi;
77
+ for (const tagMatch of searchArea.matchAll(tagRegex)) {
78
+ if (tagMatch[0].toLowerCase() === '<div') {
79
+ openCount++;
80
+ } else {
81
+ openCount--;
64
82
  }
65
-
66
- if (attributeMatch[1] === 'tab-id') {
67
- attributes.tabId = attributeMatch[2];
83
+ if (openCount === 0) {
84
+ const endIndex = startIndex + tagMatch.index + tagMatch[0].length;
85
+ nestedContainerRanges.push({ start: startIndex, end: endIndex });
86
+ break;
68
87
  }
69
88
  }
70
-
71
- tabs.push(attributes as unknown as Tab);
72
89
  }
73
90
 
91
+ const tabs: Tab[] = [];
92
+ const tabItemRegex = /<sui-tab-item([^>]*?)>/gi;
93
+ for (const tabMatch of html.matchAll(tabItemRegex)) {
94
+ const tabIndex = tabMatch.index;
95
+ const isNested = nestedContainerRanges.some(
96
+ (range) => tabIndex > range.start && tabIndex < range.end
97
+ );
98
+ if (!isNested) {
99
+ const attributes = getAttributes(tabMatch[1]);
100
+ if (attributes.tabId) {
101
+ tabs.push(attributes as unknown as Tab);
102
+ }
103
+ }
104
+ }
74
105
  return tabs;
75
106
  };
76
107
 
108
+ /**
109
+ * Injects the `active` class into the specified tab item in an HTML string.
110
+ * @param tabId The `data-tab-id` of the tab to mark as active.
111
+ * @param html The raw HTML string to modify.
112
+ * @returns The modified HTML string with the active tab.
113
+ */
77
114
  const markTabAsActive = (tabId: string, html: string): string => {
78
115
  if (!tabId) return html;
79
116
 
80
117
  const updatedHtml = html.replace(
81
118
  /<sui-tab-item[^>]*data-tab-id="([^"]*)"[^>]*>/g,
82
119
  (match, tabIdValue) => {
83
- // Check if the tabId matches
84
120
  if (tabIdValue === tabId) {
85
- // Check if the element already has a class attribute
86
121
  if (match.includes('class="')) {
87
- // If class attribute exists, add 'active' to the class attribute
88
122
  return match.replace(/(class="[^"]*)"/, '$1 active"');
89
123
  }
90
-
91
- // If class attribute does not exist, add it
92
124
  return match.replace(/(<sui-tab-item[^>]*data-tab-id="[^"]*")/, '$1 class="active"');
93
125
  }
94
- return match; // Return original if the tabId does not match
126
+ return match;
95
127
  }
96
128
  );
97
129
 
@@ -111,7 +143,8 @@ const syncKey = originalSyncKey ? `sui-tabs-${originalSyncKey}` : undefined;
111
143
 
112
144
  const tabContents = await Astro.slots.render('default');
113
145
  const tabs = extractTabInfoWithRegex(tabContents);
114
- const finalizedTabContents = markTabAsActive(tabs[0]?.tabId || '', tabContents);
146
+ const defaultActiveTab = tabs.find((tab) => tab.active) ?? tabs[0];
147
+ const finalizedTabContents = markTabAsActive(defaultActiveTab?.tabId || '', tabContents);
115
148
  const containerId = generateID('sui-tabs-container');
116
149
  ---
117
150
 
@@ -124,27 +157,31 @@ const containerId = generateID('sui-tabs-container');
124
157
  class:list={[variant, align]}
125
158
  >
126
159
  <div class="sui-tabs-list" role="tablist">
127
- {tabs.map((tab, i) => (
128
- <button
129
- role="tab"
130
- type="button"
131
- class="sui-tab-header"
132
- id={syncKey ? `${syncKey}-${i}` : undefined}
133
- tabindex={i === 0 ? 0 : -1}
134
- data-tab-child={tab.tabId}
135
- class:list={[i === 0 && "active", tab.color, syncKey && `${syncKey}:${i}`]}
136
- >
137
- {tab.icon && (
138
- <Icon name={tab.icon} width={24} height={24} />
139
- )}
140
- <span>{tab.label}</span>
141
- </button>
142
- ))}
160
+ {tabs.map((tab, i) => {
161
+ const isActive = tab.tabId === defaultActiveTab?.tabId;
162
+ return (
163
+ <button
164
+ role="tab"
165
+ type="button"
166
+ class="sui-tab-header"
167
+ id={syncKey ? `${syncKey}-${i}` : undefined}
168
+ tabindex={isActive ? 0 : -1}
169
+ data-tab-child={tab.tabId}
170
+ class:list={[isActive && "active", tab.color, syncKey && `${syncKey}:${i}`]}
171
+ >
172
+ {tab.icon && (
173
+ <Icon name={tab.icon} width={24} height={24} />
174
+ )}
175
+ <span>{tab.label}</span>
176
+ </button>
177
+ )
178
+ })}
143
179
  </div>
144
180
  <div class="sui-tabs-content">
145
181
  <Fragment set:html={finalizedTabContents} />
146
182
  </div>
147
183
  </div>
184
+
148
185
  <script>
149
186
  import "studiocms:ui/scripts/tabs"
150
187
  </script>
@@ -48,29 +48,29 @@
48
48
  pointer-events: none;
49
49
  }
50
50
  .sui-tabs-container.default .sui-tab-header:focus-visible {
51
- outline: 2px solid hsl(var(--text-normal));
51
+ outline: 2px solid var(--text-normal);
52
52
  outline-offset: 2px;
53
53
  }
54
54
  .sui-tabs-container.default .sui-tab-header:hover {
55
- background-color: hsla(var(--default-flat-active)) !important;
55
+ background-color: var(--default-flat-active) !important;
56
56
  }
57
57
  .sui-tabs-container.default .sui-tab-header.active {
58
- background-color: hsla(var(--primary-flat-active)) !important;
58
+ background-color: var(--primary-flat-active) !important;
59
59
  }
60
60
  .sui-tabs-container.default .sui-tab-header.success.active {
61
- background-color: hsla(var(--success-flat-active)) !important;
61
+ background-color: var(--success-flat-active) !important;
62
62
  }
63
63
  .sui-tabs-container.default .sui-tab-header.warning.active {
64
- background-color: hsla(var(--warning-flat-active)) !important;
64
+ background-color: var(--warning-flat-active) !important;
65
65
  }
66
66
  .sui-tabs-container.default .sui-tab-header.danger.active {
67
- background-color: hsla(var(--danger-flat-active)) !important;
67
+ background-color: var(--danger-flat-active) !important;
68
68
  }
69
69
  .sui-tabs-container.default .sui-tab-header.danger.info {
70
- background-color: hsla(var(--info-flat-active)) !important;
70
+ background-color: var(--info-flat-active) !important;
71
71
  }
72
72
  .sui-tabs-container.default .sui-tab-header.danger.mono {
73
- background-color: hsla(var(--mono-flat-active)) !important;
73
+ background-color: var(--mono-flat-active) !important;
74
74
  }
75
75
  .sui-tabs-container.starlight .sui-tabs-list {
76
76
  margin-bottom: 1rem;
@@ -83,21 +83,21 @@
83
83
  left: 0;
84
84
  width: 100%;
85
85
  height: 2px;
86
- background-color: hsl(var(--border));
86
+ background-color: var(--border);
87
87
  }
88
88
  .sui-tabs-container.starlight .sui-tab-header {
89
89
  padding: 0.25rem 1.25rem;
90
- color: hsl(var(--text-muted));
90
+ color: var(--text-muted);
91
91
  }
92
92
  .sui-tabs-container.starlight .sui-tab-header.active {
93
93
  font-weight: 600;
94
- color: hsl(var(--text-normal));
94
+ color: var(--text-normal);
95
95
  }
96
96
  .sui-tabs-container.starlight .sui-tab-header.active::after {
97
97
  content: "";
98
98
  width: 100%;
99
99
  height: 2px;
100
- background-color: hsl(var(--primary-base));
100
+ background-color: var(--primary-base);
101
101
  position: absolute;
102
102
  bottom: 0;
103
103
  left: 0;
@@ -108,9 +108,9 @@
108
108
  width: calc(100% - 2px);
109
109
  bottom: 1px;
110
110
  left: 1px;
111
- border: 2px solid hsl(var(--primary-base));
111
+ border: 2px solid var(--primary-base);
112
112
  background-color: transparent;
113
- outline: 1px solid hsl(var(--text-normal));
113
+ outline: 1px solid var(--text-normal);
114
114
  }
115
115
  .sui-tabs-content {
116
116
  width: 100%;
@@ -1 +1,17 @@
1
- declare function loadTabs(): void;
1
+ /**
2
+ * Handles the logic of switching tabs, updating ARIA attributes, and syncing state.
3
+ * @param tabContainer The main container of the tab system.
4
+ * @param newActiveHeader The header element of the tab to be activated.
5
+ * @param originatedFromSync A flag to prevent cyclic updates with synced tabs.
6
+ */
7
+ declare const switchTab: (tabContainer: HTMLDivElement, newActiveHeader: HTMLElement, originatedFromSync?: boolean) => void;
8
+ /**
9
+ * Sets up a single tab container, wiring up ARIA attributes and event listeners.
10
+ * @param tabContainer The main container element of a tab system to set up.
11
+ */
12
+ declare const setupTabContainer: (tabContainer: HTMLDivElement) => void;
13
+ /**
14
+ * Initializes all tab systems on the page.
15
+ * Finds all tab containers and applies the setup logic to each.
16
+ */
17
+ declare const loadTabs: () => void;