@studiocms/ui 0.3.2 → 0.4.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 (156) hide show
  1. package/dist/components/Accordion/Accordion.astro +23 -0
  2. package/dist/components/Accordion/Item.astro +21 -0
  3. package/dist/components/Accordion/accordion.css +64 -0
  4. package/dist/components/Accordion/accordion.d.ts +1 -0
  5. package/dist/components/Accordion/accordion.js +70 -0
  6. package/dist/components/Badge/Badge.astro +49 -0
  7. package/dist/components/Badge/badge.css +111 -0
  8. package/dist/components/Breadcrumbs/Breadcrumbs.astro +31 -0
  9. package/dist/components/Breadcrumbs/breadcrumbs.css +15 -0
  10. package/dist/components/Button/Button.astro +75 -0
  11. package/dist/components/Button/button.css +292 -0
  12. package/{src/components → dist/components/Card}/Card.astro +1 -48
  13. package/dist/components/Card/card.css +38 -0
  14. package/dist/components/Center/Center.astro +7 -0
  15. package/dist/components/Center/center.css +8 -0
  16. package/dist/components/Checkbox/Checkbox.astro +95 -0
  17. package/dist/components/Checkbox/checkbox.css +119 -0
  18. package/dist/components/Checkbox/checkbox.d.ts +2 -0
  19. package/dist/components/Checkbox/checkbox.js +20 -0
  20. package/{src/components → dist/components/Divider}/Divider.astro +2 -25
  21. package/dist/components/Divider/divider.css +21 -0
  22. package/dist/components/Dropdown/Dropdown.astro +116 -0
  23. package/dist/components/Dropdown/dropdown.css +180 -0
  24. package/dist/components/Dropdown/dropdown.d.ts +48 -0
  25. package/dist/components/Dropdown/dropdown.js +201 -0
  26. package/dist/components/Footer/Footer.astro +58 -0
  27. package/dist/components/Footer/footer.css +68 -0
  28. package/dist/components/Group/Group.astro +7 -0
  29. package/dist/components/Group/group.css +19 -0
  30. package/{src/utils → dist/components/Icon}/Icon.astro +1 -1
  31. package/{src/utils/iconType.ts → dist/components/Icon/iconType.d.ts} +0 -1
  32. package/dist/components/Icon/iconType.js +0 -0
  33. package/{src/components → dist/components/Input}/Input.astro +2 -48
  34. package/dist/components/Input/input.css +38 -0
  35. package/{src → dist}/components/Modal/Modal.astro +4 -122
  36. package/dist/components/Modal/modal.css +100 -0
  37. package/dist/components/Modal/modal.d.ts +48 -0
  38. package/dist/components/Modal/modal.js +129 -0
  39. package/dist/components/Progress/Progress.astro +21 -0
  40. package/dist/components/Progress/helper.d.ts +13 -0
  41. package/dist/components/Progress/helper.js +32 -0
  42. package/dist/components/Progress/progress.css +29 -0
  43. package/dist/components/Progress/progress.d.ts +1 -0
  44. package/dist/components/Progress/progress.js +10 -0
  45. package/dist/components/RadioGroup/RadioGroup.astro +124 -0
  46. package/dist/components/RadioGroup/radiogroup.css +96 -0
  47. package/dist/components/RadioGroup/radiogroup.d.ts +1 -0
  48. package/dist/components/RadioGroup/radiogroup.js +48 -0
  49. package/{src/components → dist/components/Row}/Row.astro +1 -24
  50. package/dist/components/Row/row.css +18 -0
  51. package/dist/components/SearchSelect/SearchSelect.astro +135 -0
  52. package/dist/components/SearchSelect/searchselect.css +95 -0
  53. package/dist/components/SearchSelect/searchselect.d.ts +6 -0
  54. package/dist/components/SearchSelect/searchselect.js +166 -0
  55. package/dist/components/Select/Select.astro +147 -0
  56. package/dist/components/Select/select.css +110 -0
  57. package/dist/components/Select/select.d.ts +1 -0
  58. package/dist/components/Select/select.js +143 -0
  59. package/dist/components/Sidebar/helpers.d.ts +76 -0
  60. package/dist/components/Sidebar/helpers.js +160 -0
  61. package/{src → dist}/components/Tabs/TabItem.astro +3 -3
  62. package/dist/components/Tabs/Tabs.astro +150 -0
  63. package/dist/components/Tabs/tabs.css +121 -0
  64. package/dist/components/Tabs/tabs.d.ts +1 -0
  65. package/dist/components/Tabs/tabs.js +82 -0
  66. package/{src/components → dist/components/Textarea}/Textarea.astro +2 -61
  67. package/dist/components/Textarea/textarea.css +49 -0
  68. package/dist/components/ThemeToggle/ThemeToggle.astro +21 -0
  69. package/dist/components/ThemeToggle/themetoggle.css +17 -0
  70. package/dist/components/ThemeToggle/themetoggle.d.ts +1 -0
  71. package/dist/components/ThemeToggle/themetoggle.js +4 -0
  72. package/dist/components/Toast/Toaster.astro +69 -0
  73. package/dist/components/Toast/toast.d.ts +8 -0
  74. package/dist/components/Toast/toast.js +9 -0
  75. package/dist/components/Toast/toaster.css +168 -0
  76. package/dist/components/Toast/toaster.d.ts +1 -0
  77. package/dist/components/Toast/toaster.js +160 -0
  78. package/dist/components/Toggle/Toggle.astro +84 -0
  79. package/dist/components/Toggle/toggle.css +93 -0
  80. package/dist/components/Toggle/toggle.d.ts +2 -0
  81. package/dist/components/Toggle/toggle.js +20 -0
  82. package/{src/components → dist/components/User}/User.astro +3 -43
  83. package/dist/components/User/user.css +35 -0
  84. package/dist/css/colors.css +95 -0
  85. package/dist/css/global.css +3 -0
  86. package/dist/css/radii.css +6 -0
  87. package/dist/css/resets.css +46 -0
  88. package/dist/index.d.ts +12 -0
  89. package/dist/index.js +379 -0
  90. package/dist/toolbar/ColorPicker.d.ts +7 -0
  91. package/dist/toolbar/ColorPicker.js +85 -0
  92. package/dist/toolbar/icon.d.ts +1 -0
  93. package/dist/toolbar/icon.js +4 -0
  94. package/dist/toolbar/index.d.ts +2 -0
  95. package/dist/toolbar/index.js +292 -0
  96. package/dist/types/index.d.ts +11 -0
  97. package/dist/types/index.js +0 -0
  98. package/dist/utils/ThemeHelper.d.ts +49 -0
  99. package/dist/utils/ThemeHelper.js +113 -0
  100. package/{src/utils/colors.ts → dist/utils/colors.d.ts} +1 -1
  101. package/dist/utils/colors.js +0 -0
  102. package/dist/utils/generateID.d.ts +2 -0
  103. package/dist/utils/generateID.js +6 -0
  104. package/dist/utils/headers.d.ts +43 -0
  105. package/dist/utils/headers.js +129 -0
  106. package/dist/utils/iconStrings.d.ts +4 -0
  107. package/dist/utils/iconStrings.js +13 -0
  108. package/dist/utils/integration-utils.d.ts +130 -0
  109. package/dist/utils/integration-utils.js +161 -0
  110. package/package.json +26 -10
  111. package/src/components/BaseHead.astro +0 -22
  112. package/src/components/Button.astro +0 -372
  113. package/src/components/Center.astro +0 -16
  114. package/src/components/Checkbox.astro +0 -250
  115. package/src/components/Dropdown/Dropdown.astro +0 -314
  116. package/src/components/Dropdown/dropdown.ts +0 -258
  117. package/src/components/Dropdown/index.ts +0 -2
  118. package/src/components/Footer.astro +0 -137
  119. package/src/components/Modal/index.ts +0 -2
  120. package/src/components/Modal/modal.ts +0 -163
  121. package/src/components/RadioGroup.astro +0 -299
  122. package/src/components/SearchSelect.astro +0 -486
  123. package/src/components/Select.astro +0 -467
  124. package/src/components/Sidebar/helpers.ts +0 -179
  125. package/src/components/Sidebar/index.ts +0 -3
  126. package/src/components/Tabs/Tabs.astro +0 -393
  127. package/src/components/Tabs/index.ts +0 -2
  128. package/src/components/ThemeToggle.astro +0 -46
  129. package/src/components/Toast/Toaster.astro +0 -470
  130. package/src/components/Toast/index.ts +0 -2
  131. package/src/components/Toast/toast.ts +0 -16
  132. package/src/components/Toggle.astro +0 -214
  133. package/src/components/index.ts +0 -27
  134. package/src/components.ts +0 -26
  135. package/src/css/colors.css +0 -106
  136. package/src/css/global.css +0 -2
  137. package/src/css/resets.css +0 -54
  138. package/src/env.d.ts +0 -15
  139. package/src/integration.ts +0 -31
  140. package/src/layouts/RootLayout.astro +0 -33
  141. package/src/layouts/index.ts +0 -2
  142. package/src/layouts.ts +0 -1
  143. package/src/types/index.ts +0 -11
  144. package/src/utils/ThemeHelper.ts +0 -145
  145. package/src/utils/create-resolver.ts +0 -30
  146. package/src/utils/generateID.ts +0 -5
  147. package/src/utils/headers.ts +0 -190
  148. package/src/utils/iconStrings.ts +0 -29
  149. package/src/utils/index.ts +0 -1
  150. package/src/utils/virtual-module-plugin-builder.ts +0 -37
  151. /package/{src → dist}/components/Sidebar/Double.astro +0 -0
  152. /package/{src → dist}/components/Sidebar/Single.astro +0 -0
  153. /package/{src → dist}/icons/Checkmark.astro +0 -0
  154. /package/{src → dist}/icons/ChevronUpDown.astro +0 -0
  155. /package/{src → dist}/icons/User.astro +0 -0
  156. /package/{src → dist}/icons/X-Mark.astro +0 -0
@@ -0,0 +1,160 @@
1
+ class SingleSidebarHelper {
2
+ sidebar;
3
+ sidebarToggle;
4
+ /**
5
+ * A helper to manage the sidebar with.
6
+ * @param toggleID The ID of the element that should toggle the sidebar.
7
+ */
8
+ constructor(toggleID) {
9
+ const sidebarContainer = document.getElementById("sui-sidebar");
10
+ if (!sidebarContainer) {
11
+ throw new Error(
12
+ `No item with ID 'sui-sidebar' found. Please add the <Sidebar> component to this page.`
13
+ );
14
+ }
15
+ this.sidebar = sidebarContainer;
16
+ if (toggleID) {
17
+ this.toggleSidebarOnClick(toggleID);
18
+ }
19
+ }
20
+ /**
21
+ * A helper function register an element which should toggle the sidebar.
22
+ * @param elementID The ID of the element that should toggle the sidebar.
23
+ */
24
+ toggleSidebarOnClick = (elementID) => {
25
+ const navToggle = document.getElementById(elementID);
26
+ if (!navToggle) {
27
+ throw new Error(`No item with ID ${elementID} found.`);
28
+ }
29
+ this.sidebarToggle = navToggle;
30
+ this.sidebarToggle.addEventListener("click", () => {
31
+ this.sidebar.classList.toggle("active");
32
+ });
33
+ };
34
+ /**
35
+ * A helper function to hide the sidebar when an element is clicked.
36
+ * @param elementID The ID of the element that should hide the sidebar.
37
+ */
38
+ hideSidebarOnClick = (elementID) => {
39
+ const element = document.getElementById(elementID);
40
+ if (!element) {
41
+ throw new Error(`No item with ID ${elementID} found.`);
42
+ }
43
+ element.addEventListener("click", this.hideSidebar);
44
+ };
45
+ /**
46
+ * A helper function to show the sidebar when an element is clicked.
47
+ * @param elementID The ID of the element that should show the sidebar.
48
+ */
49
+ showSidebarOnClick = (elementID) => {
50
+ const element = document.getElementById(elementID);
51
+ if (!element) {
52
+ throw new Error(`No item with ID ${elementID} found.`);
53
+ }
54
+ element.addEventListener("click", this.showSidebar);
55
+ };
56
+ /**
57
+ * A function to hide the sidebar.
58
+ */
59
+ hideSidebar = () => {
60
+ this.sidebar.classList.remove("active");
61
+ };
62
+ /**
63
+ * A function to show the sidebar.
64
+ */
65
+ showSidebar = () => {
66
+ this.sidebar.classList.add("active");
67
+ };
68
+ }
69
+ class DoubleSidebarHelper {
70
+ sidebarsContainer;
71
+ /**
72
+ * A helper to manage the double sidebar with.
73
+ */
74
+ constructor() {
75
+ const sidebarsContainer = document.getElementById("sui-sidebars");
76
+ if (!sidebarsContainer) {
77
+ throw new Error(
78
+ `No item with ID 'sidebars' found. Please add the <DoubleSidebar> component to this page.`
79
+ );
80
+ }
81
+ this.sidebarsContainer = sidebarsContainer;
82
+ }
83
+ /**
84
+ * A helper function to hide the sidebar when an element is clicked.
85
+ * @param elementID The ID of the element that should hide the sidebar.
86
+ */
87
+ hideSidebarOnClick = (elementID) => {
88
+ const element = document.getElementById(elementID);
89
+ if (!element) {
90
+ throw new Error(`No item with ID ${elementID} found.`);
91
+ }
92
+ element.addEventListener("click", this.hideSidebar);
93
+ };
94
+ /**
95
+ * A helper function to show the outer sidebar when an element is clicked.
96
+ * @param elementID The ID of the element that should show the outer sidebar.
97
+ */
98
+ showOuterOnClick = (elementID) => {
99
+ const element = document.getElementById(elementID);
100
+ if (!element) {
101
+ throw new Error(`No item with ID ${elementID} found.`);
102
+ }
103
+ element.addEventListener("click", this.showOuterSidebar);
104
+ };
105
+ /**
106
+ * A helper function to show the inner sidebar when an element is clicked.
107
+ * @param elementID The ID of the element that should show the inner sidebar.
108
+ */
109
+ showInnerOnClick = (elementID) => {
110
+ const element = document.getElementById(elementID);
111
+ if (!element) {
112
+ throw new Error(`No item with ID ${elementID} found.`);
113
+ }
114
+ element.addEventListener("click", this.showInnerSidebar);
115
+ };
116
+ /**
117
+ * A helper function to toggle between the outer and inner sidebar when an element is clicked.
118
+ * @param elementID The ID of the element that should toggle the sidebar view.
119
+ */
120
+ toggleStateOnClick = (elementID) => {
121
+ const element = document.getElementById(elementID);
122
+ if (!element) {
123
+ throw new Error(`No item with ID ${elementID} found.`);
124
+ }
125
+ element.addEventListener("click", this.toggleSidebarState);
126
+ };
127
+ /**
128
+ * A function to show the inner sidebar.
129
+ */
130
+ showInnerSidebar = () => {
131
+ this.sidebarsContainer.classList.add("inner", "active");
132
+ };
133
+ /**
134
+ * A function to show the outer sidebar.
135
+ */
136
+ showOuterSidebar = () => {
137
+ this.sidebarsContainer.classList.add("active");
138
+ this.sidebarsContainer.classList.remove("inner");
139
+ };
140
+ /**
141
+ * A function to toggle between the outer and inner sidebar.
142
+ */
143
+ toggleSidebarState = () => {
144
+ if (this.sidebarsContainer.classList.contains("inner")) {
145
+ this.showOuterSidebar();
146
+ } else {
147
+ this.showInnerSidebar();
148
+ }
149
+ };
150
+ /**
151
+ * A function to hide the sidebar altogether.
152
+ */
153
+ hideSidebar = () => {
154
+ this.sidebarsContainer.classList.remove("inner", "active");
155
+ };
156
+ }
157
+ export {
158
+ DoubleSidebarHelper,
159
+ SingleSidebarHelper
160
+ };
@@ -1,7 +1,7 @@
1
1
  ---
2
- import type { StudioCMSColorway } from '../../utils/colors';
3
- import { generateID } from '../../utils/generateID';
4
- import type { HeroIconName } from '../../utils/iconType';
2
+ import type { StudioCMSColorway } from '../../utils/colors.js';
3
+ 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.
@@ -0,0 +1,150 @@
1
+ ---
2
+ import type { StudioCMSColorway } from '../../utils/colors.js';
3
+ import { generateID } from '../../utils/generateID.js';
4
+ import Icon from '../Icon/Icon.astro';
5
+ import type { HeroIconName } from '../Icon/iconType.js';
6
+ import './tabs.css';
7
+
8
+ interface Tab {
9
+ icon?: HeroIconName;
10
+ label: string;
11
+ color: Exclude<StudioCMSColorway, 'default'>;
12
+ tabId: string;
13
+ }
14
+
15
+ /**
16
+ * The props for the Tabs component.
17
+ */
18
+ interface Props {
19
+ /**
20
+ * The sync key for the tabs. If provided, the active tab will be synced across all instances of the tabs with the same sync key.
21
+ * Additionally, the active tab will be stored session- or local storage depending on the `storage` prop.
22
+ */
23
+ syncKey?: string;
24
+ /**
25
+ * The storage type for the tabs. Defaults to `session`.
26
+ */
27
+ storage?: 'session' | 'persistent';
28
+ /**
29
+ * The variant of the tabs. Defaults to `default`.
30
+ */
31
+ variant?: 'default' | 'starlight';
32
+ /**
33
+ * The alignment of the tabs. Defaults to `left`.
34
+ */
35
+ align?: 'left' | 'center' | 'right';
36
+ }
37
+
38
+ const extractTabInfoWithRegex = (input: string) => {
39
+ const tabItemRegex = /<sui-tab-item([^>]*)>/g;
40
+
41
+ const attributeRegex = /data-([\w-]+)="([^"]*)"/g;
42
+
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];
64
+ }
65
+
66
+ if (attributeMatch[1] === 'tab-id') {
67
+ attributes.tabId = attributeMatch[2];
68
+ }
69
+ }
70
+
71
+ tabs.push(attributes as unknown as Tab);
72
+ }
73
+
74
+ return tabs;
75
+ };
76
+
77
+ const markTabAsActive = (tabId: string, html: string): string => {
78
+ if (!tabId) return html;
79
+
80
+ const updatedHtml = html.replace(
81
+ /<sui-tab-item[^>]*data-tab-id="([^"]*)"[^>]*>/g,
82
+ (match, tabIdValue) => {
83
+ // Check if the tabId matches
84
+ if (tabIdValue === tabId) {
85
+ // Check if the element already has a class attribute
86
+ if (match.includes('class="')) {
87
+ // If class attribute exists, add 'active' to the class attribute
88
+ return match.replace(/(class="[^"]*)"/, '$1 active"');
89
+ }
90
+
91
+ // If class attribute does not exist, add it
92
+ return match.replace(/(<sui-tab-item[^>]*data-tab-id="[^"]*")/, '$1 class="active"');
93
+ }
94
+ return match; // Return original if the tabId does not match
95
+ }
96
+ );
97
+
98
+ return updatedHtml;
99
+ };
100
+
101
+ const uniqueId = generateID('sui-tabs-container');
102
+
103
+ const {
104
+ syncKey: originalSyncKey,
105
+ storage = 'session',
106
+ variant = 'default',
107
+ align = 'left',
108
+ } = Astro.props;
109
+
110
+ const syncKey = originalSyncKey ? `sui-tabs-${originalSyncKey}` : undefined;
111
+
112
+ const tabContents = await Astro.slots.render('default');
113
+ const tabs = extractTabInfoWithRegex(tabContents);
114
+ const finalizedTabContents = markTabAsActive(tabs[0]?.tabId || '', tabContents);
115
+ const containerId = generateID('sui-tabs-container');
116
+ ---
117
+
118
+ <div
119
+ class="sui-tabs-container"
120
+ id={containerId}
121
+ data-sync-key={syncKey}
122
+ data-unique-id={uniqueId}
123
+ data-storage-strategy={storage}
124
+ class:list={[variant, align]}
125
+ >
126
+ <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
+ ))}
143
+ </div>
144
+ <div class="sui-tabs-content">
145
+ <Fragment set:html={finalizedTabContents} />
146
+ </div>
147
+ </div>
148
+ <script>
149
+ import "studiocms:ui/scripts/tabs"
150
+ </script>
@@ -0,0 +1,121 @@
1
+ .sui-tabs-container {
2
+ width: 100%;
3
+ }
4
+ .sui-tabs-list {
5
+ display: flex;
6
+ flex-direction: row;
7
+ gap: 1rem;
8
+ align-items: center;
9
+ width: 100%;
10
+ overflow-x: auto;
11
+ overflow-y: visible;
12
+ position: relative;
13
+ }
14
+ .default .sui-tabs-list {
15
+ margin-top: -4px;
16
+ margin-bottom: calc(2rem - 4px);
17
+ padding: 4px 4px;
18
+ margin-left: -4px;
19
+ }
20
+ .center .sui-tabs-list {
21
+ justify-content: center;
22
+ }
23
+ .right .sui-tabs-list {
24
+ justify-content: flex-end;
25
+ }
26
+ .sui-tab-header {
27
+ margin-top: 0 !important;
28
+ display: flex;
29
+ flex-direction: row;
30
+ gap: .5rem;
31
+ cursor: pointer;
32
+ position: relative;
33
+ min-width: fit-content;
34
+ }
35
+ .default .sui-tab-header {
36
+ border-radius: var(--radius-md);
37
+ height: 40px;
38
+ padding: 0.5rem 0.75rem;
39
+ transition: all .15s ease;
40
+ font-size: 0.875em;
41
+ outline: 2px solid transparent;
42
+ outline-offset: 2px;
43
+ display: flex;
44
+ align-items: center;
45
+ justify-content: center;
46
+ }
47
+ .sui-tab-header * {
48
+ pointer-events: none;
49
+ }
50
+ .default .sui-tab-header:focus-visible {
51
+ outline: 2px solid hsl(var(--text-normal));
52
+ outline-offset: 2px;
53
+ }
54
+ .default .sui-tab-header:hover {
55
+ background-color: hsla(var(--default-flat-active)) !important;
56
+ }
57
+ .default .sui-tab-header.active {
58
+ background-color: hsla(var(--primary-flat-active)) !important;
59
+ }
60
+ .default .sui-tab-header.success.active {
61
+ background-color: hsla(var(--success-flat-active)) !important;
62
+ }
63
+ .default .sui-tab-header.warning.active {
64
+ background-color: hsla(var(--warning-flat-active)) !important;
65
+ }
66
+ .default .sui-tab-header.danger.active {
67
+ background-color: hsla(var(--danger-flat-active)) !important;
68
+ }
69
+ .default .sui-tab-header.danger.info {
70
+ background-color: hsla(var(--info-flat-active)) !important;
71
+ }
72
+ .default .sui-tab-header.danger.mono {
73
+ background-color: hsla(var(--mono-flat-active)) !important;
74
+ }
75
+ .starlight .sui-tabs-list {
76
+ margin-bottom: 1rem;
77
+ gap: 0;
78
+ }
79
+ .starlight .sui-tabs-list::before {
80
+ content: "";
81
+ position: absolute;
82
+ bottom: 0;
83
+ left: 0;
84
+ width: 100%;
85
+ height: 2px;
86
+ background-color: hsl(var(--border));
87
+ }
88
+ .starlight .sui-tab-header {
89
+ padding: 0.25rem 1.25rem;
90
+ color: hsl(var(--text-muted));
91
+ }
92
+ .starlight .sui-tab-header.active {
93
+ font-weight: 600;
94
+ color: hsl(var(--text-normal));
95
+ }
96
+ .starlight .sui-tab-header.active::after {
97
+ content: "";
98
+ width: 100%;
99
+ height: 2px;
100
+ background-color: hsl(var(--primary-base));
101
+ position: absolute;
102
+ bottom: 0;
103
+ left: 0;
104
+ z-index: 15;
105
+ }
106
+ .starlight .sui-tab-header:focus-visible::after {
107
+ height: calc(100% - 2px);
108
+ width: calc(100% - 2px);
109
+ bottom: 1px;
110
+ left: 1px;
111
+ border: 2px solid hsl(var(--primary-base));
112
+ background-color: transparent;
113
+ outline: 1px solid hsl(var(--text-normal));
114
+ }
115
+ .default .sui-tab-header.active {
116
+ background-color: hsla(var(--primary-flat-active)) !important;
117
+ }
118
+ .sui-tabs-content {
119
+ width: 100%;
120
+ margin: 0 !important;
121
+ }
@@ -0,0 +1 @@
1
+ declare const allTabContainers: NodeListOf<HTMLDivElement>;
@@ -0,0 +1,82 @@
1
+ const allTabContainers = document.querySelectorAll(".sui-tabs-container");
2
+ for (const tabContainer of allTabContainers) {
3
+ const storage = tabContainer.dataset.storageStrategy;
4
+ const syncKey = tabContainer.dataset.syncKey;
5
+ const storageLayer = storage === "session" ? sessionStorage : localStorage;
6
+ const constructCustomEvent = (tabIndex, uniqueId) => {
7
+ return new CustomEvent(`sui-tab-switch:${syncKey}`, {
8
+ detail: {
9
+ tabIndex,
10
+ uniqueId
11
+ }
12
+ });
13
+ };
14
+ const switchTab = (target, container, originatedFromSync = false) => {
15
+ const activeChildren = container.querySelectorAll(".active");
16
+ for (const child of activeChildren) {
17
+ child.tabIndex = -1;
18
+ child.classList.remove("active");
19
+ }
20
+ const newActiveTab = target;
21
+ newActiveTab.classList.add("active");
22
+ newActiveTab.tabIndex = 0;
23
+ const newActiveTabContentId = newActiveTab.dataset.tabChild;
24
+ const newActiveTabContent = container.querySelector(
25
+ `sui-tab-item[data-tab-id="${newActiveTabContentId}"]`
26
+ );
27
+ newActiveTabContent.classList.add("active");
28
+ if (syncKey && !originatedFromSync) {
29
+ const tabIndex = Array.prototype.indexOf.call(
30
+ newActiveTab.parentElement.children,
31
+ newActiveTab
32
+ );
33
+ storageLayer.setItem(syncKey, tabIndex.toString());
34
+ document.dispatchEvent(constructCustomEvent(tabIndex, container.dataset.uniqueId));
35
+ }
36
+ };
37
+ const tabHeaders = tabContainer.querySelectorAll(".sui-tab-header");
38
+ for (const tab of tabHeaders) {
39
+ tab.addEventListener("click", (e) => switchTab(e.target, tabContainer));
40
+ tab.addEventListener("keydown", (e) => {
41
+ if (e.key === "ArrowLeft" || e.key === "ArrowRight") {
42
+ const activeTabIndex = Array.prototype.indexOf.call(tab.parentElement.children, tab);
43
+ const nextTabIndex = e.key === "ArrowLeft" ? activeTabIndex - 1 : activeTabIndex + 1;
44
+ if (nextTabIndex >= 0 && nextTabIndex < tab.parentElement.children.length) {
45
+ tab.tabIndex = -1;
46
+ const nextTab = tab.parentElement.children[nextTabIndex];
47
+ nextTab.tabIndex = 0;
48
+ nextTab.click();
49
+ nextTab.focus();
50
+ } else if (nextTabIndex < 0) {
51
+ tab.tabIndex = -1;
52
+ const lastTab = tab.parentElement.children[tab.parentElement.children.length - 1];
53
+ lastTab.tabIndex = 0;
54
+ lastTab.click();
55
+ lastTab.focus();
56
+ } else {
57
+ tab.tabIndex = -1;
58
+ const firstTab = tab.parentElement.children[0];
59
+ firstTab.tabIndex = 0;
60
+ firstTab.click();
61
+ firstTab.focus();
62
+ }
63
+ }
64
+ });
65
+ }
66
+ if (syncKey) {
67
+ const activeTabIndex = storageLayer.getItem(syncKey);
68
+ if (activeTabIndex) {
69
+ const activeTab = tabContainer.querySelector(`#${syncKey}-${activeTabIndex}`);
70
+ if (activeTab) {
71
+ activeTab.click();
72
+ }
73
+ }
74
+ document.addEventListener(`sui-tab-switch:${syncKey}`, (e) => {
75
+ const event = e;
76
+ const { tabIndex, uniqueId } = event.detail;
77
+ if (uniqueId === tabContainer.dataset.uniqueId) return;
78
+ const newTab = tabContainer.querySelector(`#${syncKey}-${tabIndex}`);
79
+ switchTab(newTab, tabContainer, true);
80
+ });
81
+ }
82
+ }
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  import type { HTMLAttributes } from 'astro/types';
3
- import { generateID } from '../utils/generateID';
3
+ import { generateID } from '../../utils/generateID.js';
4
+ import './textarea.css';
4
5
 
5
6
  /**
6
7
  * Props for the textarea component
@@ -81,63 +82,3 @@ const {
81
82
 
82
83
  >{defaultValue}</textarea>
83
84
  </label>
84
- <style>
85
- .sui-textarea-label {
86
- display: flex;
87
- flex-direction: column;
88
- gap: .25rem;
89
- max-width: 80ch;
90
- }
91
-
92
- .sui-textarea-label.disabled {
93
- opacity: 0.5;
94
- pointer-events: none;
95
- color: hsl(var(--text-muted));
96
- }
97
-
98
- .sui-textarea-label.full-width {
99
- width: 100%;
100
- max-width: none;
101
- }
102
-
103
- .sui-textarea-label.full-height {
104
- height: 100%;
105
- }
106
-
107
- .label {
108
- font-size: 14px;
109
- }
110
-
111
- .sui-textarea {
112
- padding: .65rem;
113
- border-radius: 8px;
114
- border: 1px solid hsl(var(--border));
115
- background: hsl(var(--background-step-2));
116
- color: hsl(var(--text-normal));
117
- transition: all .15s ease;
118
- resize: none;
119
- min-height: 12ch;
120
- width: 100%;
121
- height: 100%;
122
- }
123
-
124
- .sui-textarea:hover {
125
- background: hsl(var(--background-step-3));
126
- }
127
-
128
- .resize .sui-textarea {
129
- resize: both;
130
- }
131
-
132
- .sui-textarea:active,
133
- .sui-textarea:focus {
134
- border: 1px solid hsl(var(--primary-base));
135
- outline: none;
136
- background: hsl(var(--background-step-2));
137
- }
138
-
139
- .req-star {
140
- color: hsl(var(--danger-base));
141
- font-weight: 700;
142
- }
143
- </style>
@@ -0,0 +1,49 @@
1
+ .sui-textarea-label {
2
+ display: flex;
3
+ flex-direction: column;
4
+ gap: .25rem;
5
+ max-width: 80ch;
6
+ }
7
+ .sui-textarea-label.disabled {
8
+ opacity: 0.5;
9
+ pointer-events: none;
10
+ color: hsl(var(--text-muted));
11
+ }
12
+ .sui-textarea-label.full-width {
13
+ width: 100%;
14
+ max-width: none;
15
+ }
16
+ .sui-textarea-label.full-height {
17
+ height: 100%;
18
+ }
19
+ .label {
20
+ font-size: 14px;
21
+ }
22
+ .sui-textarea {
23
+ padding: .65rem;
24
+ border-radius: var(--radius-md);
25
+ border: 1px solid hsl(var(--border));
26
+ background: hsl(var(--background-step-2));
27
+ color: hsl(var(--text-normal));
28
+ transition: all .15s ease;
29
+ resize: none;
30
+ min-height: 12ch;
31
+ width: 100%;
32
+ height: 100%;
33
+ }
34
+ .sui-textarea:hover {
35
+ background: hsl(var(--background-step-3));
36
+ }
37
+ .resize .sui-textarea {
38
+ resize: both;
39
+ }
40
+ .sui-textarea:active,
41
+ .sui-textarea:focus {
42
+ border: 1px solid hsl(var(--primary-base));
43
+ outline: none;
44
+ background: hsl(var(--background-step-2));
45
+ }
46
+ .req-star {
47
+ color: hsl(var(--danger-base));
48
+ font-weight: 700;
49
+ }
@@ -0,0 +1,21 @@
1
+ ---
2
+ import Button, { type Props as ButtonProps } from '../Button/Button.astro';
3
+ import './themetoggle.css';
4
+
5
+ type Props = ButtonProps;
6
+
7
+ const props = Astro.props;
8
+ ---
9
+
10
+ <Button id='sui-theme-toggle' {...props}>
11
+ <div id="dark-content">
12
+ <slot name="dark" />
13
+ </div>
14
+ <div id="light-content">
15
+ <slot name="light" />
16
+ </div>
17
+ </Button>
18
+
19
+ <script>
20
+ import "studiocms:ui/scripts/themetoggle"
21
+ </script>
@@ -0,0 +1,17 @@
1
+ #sui-theme-toggle,
2
+ #sui-theme-toggle * {
3
+ color: hsl(var(--text-normal));
4
+ }
5
+ #sui-theme-toggle #dark-content,
6
+ #sui-theme-toggle #light-content {
7
+ display: none;
8
+ width: fit-content;
9
+ height: fit-content;
10
+ max-height: 100%;
11
+ }
12
+ [data-theme=dark] #sui-theme-toggle #dark-content {
13
+ display: block;
14
+ }
15
+ [data-theme=light] #sui-theme-toggle #light-content {
16
+ display: block;
17
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,4 @@
1
+ import { ThemeHelper } from "../../utils/ThemeHelper.js";
2
+ const themeToggle = document.getElementById("sui-theme-toggle");
3
+ const themeHelper = new ThemeHelper();
4
+ themeHelper.registerToggle(themeToggle);