@studiocms/ui 0.3.2 → 0.4.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 (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 +25 -9
  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,4 @@
1
+ const studiocmsLogo = `<svg width="755" height="792" viewBox="0 0 755 792" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="295" width="460" height="466" rx="32" fill="white"/><path d="M272 434V166H180C162.327 166 148 180.327 148 198V597C148 614.673 162.327 629 180 629H577.5C595.173 629 609.5 614.673 609.5 597V490H328C297.072 490 272 464.928 272 434Z" fill="white"/><path d="M124 597V329H32C14.3269 329 0 343.327 0 361V760C0 777.673 14.3269 792 32 792H429.5C447.173 792 461.5 777.673 461.5 760V653H180C149.072 653 124 627.928 124 597Z" fill="white"/></svg>`;
2
+ export {
3
+ studiocmsLogo
4
+ };
@@ -0,0 +1,2 @@
1
+ declare const _default: import("astro").DevToolbarApp;
2
+ export default _default;
@@ -0,0 +1,292 @@
1
+ import { defineToolbarApp } from "astro/toolbar";
2
+ import DevToolbarColorPicker from "./ColorPicker.js";
3
+ const map = {
4
+ light: {},
5
+ dark: {}
6
+ };
7
+ function createRows(variables) {
8
+ const body = getComputedStyle(document.body);
9
+ const rows = variables.map((variable) => {
10
+ const row = document.createElement("tr");
11
+ const cssVariable = document.createElement("td");
12
+ const colorPicker = document.createElement("td");
13
+ const reset = document.createElement("td");
14
+ const colorPickerEl = document.createElement(
15
+ "dev-toolbar-color-picker"
16
+ );
17
+ colorPickerEl.dataset.variable = variable;
18
+ const initialColor = body.getPropertyValue(variable);
19
+ colorPickerEl.dataset.color = initialColor;
20
+ const resetButton = document.createElement("button");
21
+ resetButton.textContent = "Reset";
22
+ resetButton.disabled = true;
23
+ const codeEl = document.createElement("code");
24
+ codeEl.textContent = variable;
25
+ cssVariable.appendChild(codeEl);
26
+ colorPicker.appendChild(colorPickerEl);
27
+ reset.appendChild(resetButton);
28
+ row.appendChild(cssVariable);
29
+ row.appendChild(colorPicker);
30
+ row.appendChild(reset);
31
+ colorPickerEl.shadowRoot?.firstElementChild?.addEventListener("input", () => {
32
+ const color = colorPickerEl.getColor();
33
+ const theme = document.documentElement.dataset.theme ?? "dark";
34
+ document.documentElement.style.setProperty(variable, color);
35
+ map[theme][variable] = color;
36
+ resetButton.disabled = false;
37
+ });
38
+ resetButton.addEventListener("click", () => {
39
+ const theme = document.documentElement.dataset.theme ?? "dark";
40
+ document.documentElement.style.setProperty(variable, initialColor);
41
+ colorPickerEl.setColor(initialColor);
42
+ delete map[theme][variable];
43
+ resetButton.disabled = true;
44
+ });
45
+ return row;
46
+ });
47
+ const observer = new MutationObserver((mutations) => {
48
+ mutations.map((m) => {
49
+ if (m.type !== "attributes" || m.attributeName !== "data-theme") return;
50
+ rows.map((row) => {
51
+ const theme = document.documentElement.dataset.theme ?? "dark";
52
+ const picker = row.children[1]?.firstElementChild;
53
+ const variable = picker.dataset.variable;
54
+ const value = map[theme][variable];
55
+ if (!value) document.documentElement.style.removeProperty(variable);
56
+ else document.documentElement.style.setProperty(variable, value);
57
+ const color = body.getPropertyValue(variable);
58
+ picker.dataset.color = color;
59
+ picker.setColor(color);
60
+ });
61
+ });
62
+ });
63
+ observer.observe(document.documentElement, { attributes: true });
64
+ return rows;
65
+ }
66
+ function createStyles() {
67
+ const style = document.createElement("style");
68
+ style.textContent = `
69
+ button {
70
+ position: relative;
71
+ gap: 0.5rem;
72
+ outline: none;
73
+ border: none;
74
+ font-weight: 400;
75
+ border-radius: var(--radius-md);
76
+ transition: transform 0.15s, background-color 0.15s, border-color 0.15s, color 0.15s;
77
+ transition-timing-function: ease;
78
+ cursor: pointer;
79
+ background-color: hsl(var(--primary-base));
80
+ border-color: hsl(var(--primary-base));
81
+ color: hsl(var(--text-inverted));
82
+ min-width: fit-content;
83
+ will-change: transform;
84
+ text-decoration: none;
85
+ height: 32px;
86
+ padding: 0.5rem 0.75rem;
87
+ font-size: 0.825em;
88
+ text-align: center !important;
89
+ }
90
+
91
+ button:hover {
92
+ background-color: hsl(var(--primary-hover));
93
+ }
94
+
95
+ button:active {
96
+ background-color: hsl(var(--primary-active));
97
+ }
98
+
99
+ button:disabled {
100
+ opacity: 0.5;
101
+ cursor: not-allowed;
102
+ pointer-events: none;
103
+ }
104
+
105
+ table {
106
+ border-collapse: collapse;
107
+ margin-top: 1rem;
108
+ width: 100%;
109
+ }
110
+
111
+ tr {
112
+ text-align: left;
113
+ color: #fff;
114
+ }
115
+
116
+ details {
117
+ margin-bottom: 1rem;
118
+ }
119
+ `;
120
+ return style;
121
+ }
122
+ function createColorsTable() {
123
+ const table = document.createElement("table");
124
+ const thead = document.createElement("thead");
125
+ const tr = document.createElement("tr");
126
+ const th1 = document.createElement("th");
127
+ const th2 = document.createElement("th");
128
+ const th3 = document.createElement("th");
129
+ th1.textContent = "CSS Variable";
130
+ th2.textContent = "Color Picker";
131
+ th3.textContent = "Reset";
132
+ tr.appendChild(th1);
133
+ tr.appendChild(th2);
134
+ tr.appendChild(th3);
135
+ thead.appendChild(tr);
136
+ table.appendChild(thead);
137
+ const tbody = document.createElement("tbody");
138
+ const editableCSSVariables = [
139
+ "--background-base",
140
+ "--background-step-1",
141
+ "--background-step-2",
142
+ "--background-step-3",
143
+ "--text-normal",
144
+ "--text-muted",
145
+ "--text-inverted",
146
+ "--border",
147
+ "--shadow",
148
+ "--default-base",
149
+ "--default-hover",
150
+ "--default-active",
151
+ "--primary-base",
152
+ "--primary-hover",
153
+ "--primary-active",
154
+ "--success-base",
155
+ "--success-hover",
156
+ "--success-active",
157
+ "--warning-base",
158
+ "--warning-hover",
159
+ "--warning-active",
160
+ "--danger-base",
161
+ "--danger-hover",
162
+ "--danger-active",
163
+ "--info-base",
164
+ "--info-hover",
165
+ "--info-active",
166
+ "--mono-base",
167
+ "--mono-hover",
168
+ "--mono-active"
169
+ ];
170
+ const rows = createRows(editableCSSVariables);
171
+ for (const row of rows) {
172
+ tbody.appendChild(row);
173
+ }
174
+ table.appendChild(tbody);
175
+ return {
176
+ table,
177
+ variables: editableCSSVariables
178
+ };
179
+ }
180
+ function createDetails(title, table) {
181
+ const details = document.createElement("details");
182
+ const summary = document.createElement("summary");
183
+ summary.textContent = title;
184
+ details.appendChild(summary);
185
+ details.appendChild(table);
186
+ return details;
187
+ }
188
+ function createRadiiTable() {
189
+ const radiiTable = document.createElement("table");
190
+ const radiiThead = document.createElement("thead");
191
+ const radiiTr = document.createElement("tr");
192
+ const radiiTh1 = document.createElement("th");
193
+ const radiiTh2 = document.createElement("th");
194
+ const radiiTh3 = document.createElement("th");
195
+ radiiTh1.textContent = "CSS Variable";
196
+ radiiTh2.textContent = "Value (px)";
197
+ radiiTh3.textContent = "Reset";
198
+ radiiTr.appendChild(radiiTh1);
199
+ radiiTr.appendChild(radiiTh2);
200
+ radiiTr.appendChild(radiiTh3);
201
+ radiiThead.appendChild(radiiTr);
202
+ radiiTable.appendChild(radiiThead);
203
+ const radiiTbody = document.createElement("tbody");
204
+ const editableRadiiCSSVariables = ["--radius-sm", "--radius-md", "--radius-lg", "--radius-full"];
205
+ for (const variable of editableRadiiCSSVariables) {
206
+ const row = document.createElement("tr");
207
+ const cssVariable = document.createElement("td");
208
+ const value = document.createElement("td");
209
+ const reset = document.createElement("td");
210
+ const initialValue = getComputedStyle(document.body).getPropertyValue(variable);
211
+ const codeEl = document.createElement("code");
212
+ codeEl.textContent = variable;
213
+ cssVariable.appendChild(codeEl);
214
+ const resetButton = document.createElement("button");
215
+ resetButton.textContent = "Reset";
216
+ resetButton.disabled = true;
217
+ resetButton.addEventListener("click", () => {
218
+ document.documentElement.style.setProperty(variable, initialValue);
219
+ numberInput.value = (initialValue.includes("rem") ? Number.parseFloat(initialValue.split("rem")[0]) * 16 : Number.parseInt(initialValue.split("px")[0])).toString();
220
+ delete map.dark[variable];
221
+ resetButton.disabled = true;
222
+ });
223
+ reset.appendChild(resetButton);
224
+ const numberInput = document.createElement("input");
225
+ numberInput.type = "number";
226
+ numberInput.min = "0";
227
+ numberInput.step = "1";
228
+ numberInput.value = (initialValue.includes("rem") ? Number.parseFloat(initialValue.split("rem")[0]) * 16 : Number.parseInt(initialValue.split("px")[0])).toString();
229
+ numberInput.addEventListener("input", () => {
230
+ const size = `${numberInput.value}px`;
231
+ document.documentElement.style.setProperty(variable, `${numberInput.value}px`);
232
+ map.dark[variable] = size;
233
+ resetButton.disabled = false;
234
+ });
235
+ value.appendChild(numberInput);
236
+ row.appendChild(cssVariable);
237
+ row.appendChild(value);
238
+ row.appendChild(reset);
239
+ radiiTbody.appendChild(row);
240
+ }
241
+ radiiTable.appendChild(radiiTbody);
242
+ return {
243
+ table: radiiTable,
244
+ variables: editableRadiiCSSVariables
245
+ };
246
+ }
247
+ var toolbar_default = defineToolbarApp({
248
+ init(canvas) {
249
+ const myWindow = document.createElement("astro-dev-toolbar-window");
250
+ myWindow.style.overflow = "auto";
251
+ const header = document.createElement("h1");
252
+ header.textContent = "StudioCMS UI Theme Editor";
253
+ header.style.marginBottom = "1rem";
254
+ header.style.marginTop = "0";
255
+ myWindow.appendChild(header);
256
+ const style = createStyles();
257
+ const { table: colorsTable } = createColorsTable();
258
+ const colorDetails = createDetails("Colors", colorsTable);
259
+ const { table: radiiTable } = createRadiiTable();
260
+ const radiiDetails = createDetails("Border Radii", radiiTable);
261
+ myWindow.appendChild(header);
262
+ myWindow.appendChild(colorDetails);
263
+ myWindow.appendChild(radiiDetails);
264
+ myWindow.appendChild(style);
265
+ const exportButton = document.createElement("button");
266
+ exportButton.textContent = "Copy to clipboard";
267
+ exportButton.style.marginTop = "1rem";
268
+ exportButton.addEventListener("click", () => {
269
+ function getVariables(theme) {
270
+ return Object.entries(map[theme]).map(([variable, value]) => {
271
+ return ` ${variable}: ${value};`;
272
+ }).join("\n");
273
+ }
274
+ const darkVariables = getVariables("dark");
275
+ const lightVariables = getVariables("light");
276
+ const string = `${darkVariables ? `:root {
277
+ ${darkVariables}
278
+ }
279
+ ` : ""}${lightVariables ? `
280
+ [data-theme="light"] {
281
+ ${lightVariables}
282
+ }` : ""}`;
283
+ navigator.clipboard.writeText(string || "/* No changes made */");
284
+ });
285
+ myWindow.appendChild(exportButton);
286
+ canvas.appendChild(myWindow);
287
+ }
288
+ });
289
+ customElements.define("dev-toolbar-color-picker", DevToolbarColorPicker);
290
+ export {
291
+ toolbar_default as default
292
+ };
@@ -0,0 +1,11 @@
1
+ export interface ToastProps {
2
+ title: string;
3
+ /**
4
+ * This will get passed to the component as unsanitized HTML. DO NOT PUT USER-GENERATED CONTENT HERE!
5
+ */
6
+ description?: string;
7
+ type: 'success' | 'warning' | 'danger' | 'info';
8
+ duration?: number;
9
+ persistent?: boolean;
10
+ closeButton?: boolean;
11
+ }
File without changes
@@ -0,0 +1,49 @@
1
+ type Theme = 'dark' | 'light' | 'system';
2
+ type ThemeChangeCallback = (newTheme: Theme, oldTheme: Theme) => void;
3
+ /**
4
+ * A helper to toggle, set and get the current StudioCMS UI theme.
5
+ */
6
+ declare class ThemeHelper {
7
+ private themeManagerElement;
8
+ private observer;
9
+ private themeChangeCallbacks;
10
+ /**
11
+ * A helper to toggle, set and get the current StudioCMS UI theme.
12
+ * @param themeProvider The element that should carry the data-theme attribute (replaces the document root)
13
+ */
14
+ constructor(themeProvider?: HTMLElement);
15
+ /**
16
+ * Get the current theme.
17
+ * @param {boolean} resolveSystemTheme Whether to resolve the `system` theme to the actual theme (`dark` or `light`)
18
+ * @returns {Theme} The current theme.
19
+ */
20
+ getTheme: <T extends boolean>(resolveSystemTheme?: T) => T extends true ? "dark" | "light" : Theme;
21
+ /**
22
+ * Sets the current theme.
23
+ * @param theme The new theme. One of `dark`, `light` or `system`.
24
+ */
25
+ setTheme: (theme: Theme) => void;
26
+ /**
27
+ * Toggles the current theme.
28
+ *
29
+ * If the theme is set to `system` (or no theme is set via the root element),
30
+ * the theme is set depending on the user's color scheme preference (set in the browser).
31
+ */
32
+ toggleTheme: () => void;
33
+ /**
34
+ * Register an element to act as a toggle! When clicked, it will toggle the theme.
35
+ * @param toggle The HTML element that should act as the toggle
36
+ */
37
+ registerToggle: (toggle: HTMLElement | null) => void;
38
+ /**
39
+ * Allows for adding a callback that gets called whenever the theme changes.
40
+ * @param callback The callback to be executed
41
+ */
42
+ onThemeChange: (callback: ThemeChangeCallback) => void;
43
+ /**
44
+ * Simply gets the first mutation and calls all registered callbacks.
45
+ * @param mutations The mutations array from the observer. Due to the specified options, this will always be a 1-length array,
46
+ */
47
+ private themeManagerMutationHandler;
48
+ }
49
+ export { ThemeHelper, type Theme };
@@ -0,0 +1,113 @@
1
+ class ThemeHelper {
2
+ themeManagerElement;
3
+ observer;
4
+ themeChangeCallbacks = [];
5
+ /**
6
+ * A helper to toggle, set and get the current StudioCMS UI theme.
7
+ * @param themeProvider The element that should carry the data-theme attribute (replaces the document root)
8
+ */
9
+ constructor(themeProvider) {
10
+ this.themeManagerElement = themeProvider || document.documentElement;
11
+ this.themeManagerElement.dataset.theme = this.getTheme(true);
12
+ }
13
+ /**
14
+ * Get the current theme.
15
+ * @param {boolean} resolveSystemTheme Whether to resolve the `system` theme to the actual theme (`dark` or `light`)
16
+ * @returns {Theme} The current theme.
17
+ */
18
+ getTheme = (resolveSystemTheme) => {
19
+ const theme = this.themeManagerElement.dataset.theme || "system";
20
+ if (!resolveSystemTheme) {
21
+ return theme;
22
+ }
23
+ if ((this.themeManagerElement.dataset.theme ?? "system") !== "system") {
24
+ return this.themeManagerElement.dataset.theme;
25
+ }
26
+ if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
27
+ return "dark";
28
+ }
29
+ if (window.matchMedia("(prefers-color-scheme: light)").matches) {
30
+ return "light";
31
+ }
32
+ throw new Error(
33
+ "Unable to resolve theme. (Most likely cause: window.matchMedia is not supported by the browser)"
34
+ );
35
+ };
36
+ /**
37
+ * Sets the current theme.
38
+ * @param theme The new theme. One of `dark`, `light` or `system`.
39
+ */
40
+ setTheme = (theme) => {
41
+ this.themeManagerElement.dataset.theme = theme;
42
+ if (typeof localStorage.getItem("starlight-theme") === "string") {
43
+ localStorage.setItem("starlight-theme", theme === "system" ? "" : theme);
44
+ }
45
+ };
46
+ /**
47
+ * Toggles the current theme.
48
+ *
49
+ * If the theme is set to `system` (or no theme is set via the root element),
50
+ * the theme is set depending on the user's color scheme preference (set in the browser).
51
+ */
52
+ toggleTheme = () => {
53
+ const theme = this.getTheme();
54
+ if (theme && theme === "dark") {
55
+ this.setTheme("light");
56
+ return;
57
+ }
58
+ if (theme && theme === "light") {
59
+ this.setTheme("dark");
60
+ return;
61
+ }
62
+ if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
63
+ this.setTheme("light");
64
+ return;
65
+ }
66
+ if (window.matchMedia("(prefers-color-scheme: light)").matches) {
67
+ this.setTheme("dark");
68
+ return;
69
+ }
70
+ };
71
+ /**
72
+ * Register an element to act as a toggle! When clicked, it will toggle the theme.
73
+ * @param toggle The HTML element that should act as the toggle
74
+ */
75
+ registerToggle = (toggle) => {
76
+ if (!toggle) {
77
+ console.error("Element passed to toggle registration does not exist.");
78
+ return;
79
+ }
80
+ if (!toggle.dataset.suiThemeToggle) {
81
+ toggle.addEventListener("click", this.toggleTheme);
82
+ toggle.dataset.suiThemeToggle = "true";
83
+ }
84
+ };
85
+ /**
86
+ * Allows for adding a callback that gets called whenever the theme changes.
87
+ * @param callback The callback to be executed
88
+ */
89
+ onThemeChange = (callback) => {
90
+ if (!this.observer) {
91
+ this.observer = new MutationObserver(this.themeManagerMutationHandler);
92
+ this.observer.observe(this.themeManagerElement, {
93
+ attributes: true,
94
+ attributeOldValue: true,
95
+ attributeFilter: ["data-theme"]
96
+ });
97
+ }
98
+ this.themeChangeCallbacks.push(callback);
99
+ };
100
+ /**
101
+ * Simply gets the first mutation and calls all registered callbacks.
102
+ * @param mutations The mutations array from the observer. Due to the specified options, this will always be a 1-length array,
103
+ */
104
+ themeManagerMutationHandler = (mutations) => {
105
+ if (!mutations[0]) return;
106
+ for (const callback of this.themeChangeCallbacks) {
107
+ callback(this.getTheme(), mutations[0].oldValue || "system");
108
+ }
109
+ };
110
+ }
111
+ export {
112
+ ThemeHelper
113
+ };
@@ -1 +1 @@
1
- export type StudioCMSColorway = 'default' | 'primary' | 'success' | 'warning' | 'danger';
1
+ export type StudioCMSColorway = 'default' | 'primary' | 'success' | 'warning' | 'danger' | 'info' | 'mono';
File without changes
@@ -0,0 +1,2 @@
1
+ declare function generateID(prefix: string): string;
2
+ export { generateID };
@@ -0,0 +1,6 @@
1
+ function generateID(prefix) {
2
+ return `${prefix}-${Math.random().toString(16).slice(2)}`;
3
+ }
4
+ export {
5
+ generateID
6
+ };
@@ -0,0 +1,43 @@
1
+ import type { AstroGlobalPartial } from 'astro';
2
+ import { z } from 'astro/zod';
3
+ export declare const HeadConfigSchema: () => z.ZodDefault<z.ZodArray<z.ZodObject<{
4
+ /** Name of the HTML tag to add to `<head>`, e.g. `'meta'`, `'link'`, or `'script'`. */
5
+ tag: z.ZodEnum<["title", "base", "link", "style", "meta", "script", "noscript", "template"]>;
6
+ /** Attributes to set on the tag, e.g. `{ rel: 'stylesheet', href: '/custom.css' }`. */
7
+ attrs: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodString, z.ZodBoolean, z.ZodUndefined]>>>;
8
+ /** Content to place inside the tag (optional). */
9
+ content: z.ZodDefault<z.ZodString>;
10
+ }, "strip", z.ZodTypeAny, {
11
+ content: string;
12
+ tag: "base" | "link" | "meta" | "noscript" | "script" | "style" | "template" | "title";
13
+ attrs: Record<string, string | boolean | undefined>;
14
+ }, {
15
+ tag: "base" | "link" | "meta" | "noscript" | "script" | "style" | "template" | "title";
16
+ content?: string | undefined;
17
+ attrs?: Record<string, string | boolean | undefined> | undefined;
18
+ }>, "many">>;
19
+ /**
20
+ * Default Head Tags for use with createHead() helper
21
+ *
22
+ * @param title
23
+ * @param description
24
+ * @param lang
25
+ * @param Astro
26
+ * @param favicon
27
+ * @param ogImage
28
+ * @param canonical
29
+ * @returns
30
+ */
31
+ export declare const headDefaults: (title: string, description: string, Astro: AstroGlobalPartial, ogImage: string | undefined, canonical: URL | undefined) => {
32
+ tag: "base" | "link" | "meta" | "noscript" | "script" | "style" | "template" | "title";
33
+ content?: string | undefined;
34
+ attrs?: Record<string, string | boolean | undefined> | undefined;
35
+ }[];
36
+ export type HeadUserConfig = z.input<ReturnType<typeof HeadConfigSchema>>;
37
+ export type HeadConfig = z.output<ReturnType<typeof HeadConfigSchema>>;
38
+ /** Create a fully parsed, merged, and sorted head entry array from multiple sources. */
39
+ export declare function createHead(defaultHeaders: HeadUserConfig, ...heads: HeadConfig[]): {
40
+ content: string;
41
+ tag: "base" | "link" | "meta" | "noscript" | "script" | "style" | "template" | "title";
42
+ attrs: Record<string, string | boolean | undefined>;
43
+ }[];
@@ -0,0 +1,129 @@
1
+ import { z } from "astro/zod";
2
+ const HeadConfigSchema = () => z.array(
3
+ z.object({
4
+ /** Name of the HTML tag to add to `<head>`, e.g. `'meta'`, `'link'`, or `'script'`. */
5
+ tag: z.enum(["title", "base", "link", "style", "meta", "script", "noscript", "template"]),
6
+ /** Attributes to set on the tag, e.g. `{ rel: 'stylesheet', href: '/custom.css' }`. */
7
+ attrs: z.record(z.union([z.string(), z.boolean(), z.undefined()])).default({}),
8
+ /** Content to place inside the tag (optional). */
9
+ content: z.string().default("")
10
+ })
11
+ ).default([]);
12
+ const headDefaults = (title, description, Astro, ogImage, canonical) => {
13
+ const headDefaults2 = [
14
+ { tag: "meta", attrs: { charset: "utf-8" } },
15
+ {
16
+ tag: "meta",
17
+ attrs: { name: "viewport", content: "width=device-width, initial-scale=1" }
18
+ },
19
+ { tag: "title", content: `${title}` },
20
+ { tag: "meta", attrs: { name: "title", content: title } },
21
+ { tag: "meta", attrs: { name: "description", content: description } },
22
+ { tag: "link", attrs: { rel: "canonical", href: canonical?.href } },
23
+ { tag: "meta", attrs: { name: "generator", content: Astro.generator } },
24
+ // Favicon
25
+ {
26
+ tag: "link",
27
+ attrs: { rel: "apple-touch-icon", href: "/apple-touch-icon.png", sizes: "180x180" }
28
+ },
29
+ {
30
+ tag: "link",
31
+ attrs: { rel: "icon", href: "/favicon-32x32.png", type: "image/png", sizes: "32x32" }
32
+ },
33
+ {
34
+ tag: "link",
35
+ attrs: { rel: "icon", href: "/favicon-16x16.png", type: "image/png", sizes: "16x16" }
36
+ },
37
+ { tag: "link", attrs: { rel: "icon", href: "/favicon.png", type: "image/png" } },
38
+ { tag: "link", attrs: { rel: "manifest", href: "/site.webmanifest" } },
39
+ { tag: "link", attrs: { rel: "mask-icon", href: "/safari-pinned-tab.svg", color: "#5bbad5" } },
40
+ { tag: "link", attrs: { rel: "shortcut icon", href: "/favicon.ico" } },
41
+ { tag: "meta", attrs: { name: "msapplication-TileColor", content: "#da532c" } },
42
+ { tag: "meta", attrs: { name: "msapplication-config", content: "/browserconfig.xml" } },
43
+ { tag: "meta", attrs: { name: "theme-color", content: "#aa87f4" } },
44
+ // OpenGraph Tags
45
+ { tag: "meta", attrs: { property: "og:title", content: title } },
46
+ { tag: "meta", attrs: { property: "og:type", content: "website" } },
47
+ { tag: "meta", attrs: { property: "og:url", content: canonical?.href } },
48
+ { tag: "meta", attrs: { property: "og:description", content: description } },
49
+ { tag: "meta", attrs: { property: "og:site_name", content: title } },
50
+ // Twitter Tags
51
+ {
52
+ tag: "meta",
53
+ attrs: { name: "twitter:card", content: "summary_large_image" }
54
+ },
55
+ { tag: "meta", attrs: { name: "twitter:url", content: canonical?.href } },
56
+ { tag: "meta", attrs: { name: "twitter:title", content: title } },
57
+ { tag: "meta", attrs: { name: "twitter:description", content: description } }
58
+ ];
59
+ if (ogImage) {
60
+ headDefaults2.push(
61
+ { tag: "meta", attrs: { property: "og:image", content: ogImage } },
62
+ { tag: "meta", attrs: { name: "twitter:image", content: ogImage } }
63
+ );
64
+ }
65
+ return headDefaults2;
66
+ };
67
+ const HeadSchema = HeadConfigSchema();
68
+ function hasTag(head, entry) {
69
+ switch (entry.tag) {
70
+ case "title":
71
+ return head.some(({ tag }) => tag === "title");
72
+ case "meta":
73
+ return hasOneOf(head, entry, ["name", "property", "http-equiv"]);
74
+ default:
75
+ return false;
76
+ }
77
+ }
78
+ function hasOneOf(head, entry, keys) {
79
+ const attr = getAttr(keys, entry);
80
+ if (!attr) return false;
81
+ const [key, val] = attr;
82
+ return head.some(({ tag, attrs }) => tag === entry.tag && attrs[key] === val);
83
+ }
84
+ function getAttr(keys, entry) {
85
+ let attr;
86
+ for (const key of keys) {
87
+ const val = entry.attrs[key];
88
+ if (val) {
89
+ attr = [key, val];
90
+ break;
91
+ }
92
+ }
93
+ return attr;
94
+ }
95
+ function mergeHead(oldHead, newHead) {
96
+ return [...oldHead.filter((tag) => !hasTag(newHead, tag)), ...newHead];
97
+ }
98
+ function sortHead(head) {
99
+ return head.sort((a, b) => {
100
+ const aImportance = getImportance(a);
101
+ const bImportance = getImportance(b);
102
+ return aImportance > bImportance ? -1 : bImportance > aImportance ? 1 : 0;
103
+ });
104
+ }
105
+ function getImportance(entry) {
106
+ if (entry.tag === "meta" && ("charset" in entry.attrs || "http-equiv" in entry.attrs || entry.attrs.name === "viewport")) {
107
+ return 100;
108
+ }
109
+ if (entry.tag === "title") return 90;
110
+ if (entry.tag !== "meta") {
111
+ if (entry.tag === "link" && "rel" in entry.attrs && entry.attrs.rel === "shortcut icon") {
112
+ return 70;
113
+ }
114
+ return 80;
115
+ }
116
+ return 0;
117
+ }
118
+ function createHead(defaultHeaders, ...heads) {
119
+ let head = HeadSchema.parse(defaultHeaders);
120
+ for (const next of heads) {
121
+ head = mergeHead(head, next);
122
+ }
123
+ return sortHead(head);
124
+ }
125
+ export {
126
+ HeadConfigSchema,
127
+ createHead,
128
+ headDefaults
129
+ };
@@ -0,0 +1,4 @@
1
+ type ValidIconString = 'check-circle' | 'exclamation-triangle' | 'exclamation-circle' | 'information-circle' | 'x-mark';
2
+ declare function getIconString(icon: ValidIconString, classes: string, width: number, height: number): string;
3
+ export { getIconString };
4
+ export type { ValidIconString };