@studiocms/ui 0.1.0 → 0.3.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.
- package/package.json +6 -4
- package/src/components/Button.astro +302 -269
- package/src/components/Card.astro +27 -3
- package/src/components/Checkbox.astro +72 -3
- package/src/components/Divider.astro +8 -1
- package/src/components/Dropdown/Dropdown.astro +55 -2
- package/src/components/Dropdown/dropdown.ts +104 -17
- package/src/components/Footer.astro +21 -4
- package/src/components/Input.astro +27 -0
- package/src/components/Modal/Modal.astro +31 -1
- package/src/components/Modal/modal.ts +33 -0
- package/src/components/RadioGroup.astro +132 -8
- package/src/components/Row.astro +9 -0
- package/src/components/SearchSelect.astro +249 -197
- package/src/components/Select.astro +229 -105
- package/src/components/Sidebar/helpers.ts +46 -0
- package/src/components/Tabs/TabItem.astro +47 -0
- package/src/components/Tabs/Tabs.astro +376 -0
- package/src/components/Tabs/index.ts +2 -0
- package/src/components/Textarea.astro +30 -0
- package/src/components/ThemeToggle.astro +6 -3
- package/src/components/Toast/Toaster.astro +140 -1
- package/src/components/Toggle.astro +77 -9
- package/src/components/User.astro +20 -2
- package/src/components/index.ts +1 -0
- package/src/components.ts +1 -0
- package/src/css/colors.css +8 -8
- package/src/css/resets.css +0 -1
- package/src/integration.ts +31 -0
- package/src/layouts/RootLayout.astro +0 -1
- package/src/utils/ThemeHelper.ts +8 -1
- package/src/utils/create-resolver.ts +30 -0
- package/src/utils/virtual-module-plugin-builder.ts +37 -0
|
@@ -2,21 +2,63 @@
|
|
|
2
2
|
import Icon from '../utils/Icon.astro';
|
|
3
3
|
import { generateID } from '../utils/generateID';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* The props for the select component.
|
|
7
|
+
*/
|
|
5
8
|
interface Option {
|
|
9
|
+
/**
|
|
10
|
+
* The label of the option.
|
|
11
|
+
*/
|
|
6
12
|
label: string;
|
|
13
|
+
/**
|
|
14
|
+
* The value of the option.
|
|
15
|
+
*/
|
|
7
16
|
value: string;
|
|
17
|
+
/**
|
|
18
|
+
* Whether the option is disabled. Defaults to `false`.
|
|
19
|
+
*/
|
|
8
20
|
disabled?: boolean;
|
|
9
21
|
}
|
|
10
22
|
|
|
23
|
+
/**
|
|
24
|
+
* The props for the select component.
|
|
25
|
+
*/
|
|
11
26
|
interface Props {
|
|
27
|
+
/**
|
|
28
|
+
* The label of the select.
|
|
29
|
+
*/
|
|
12
30
|
label?: string;
|
|
31
|
+
/**
|
|
32
|
+
* The default value of the select.
|
|
33
|
+
*/
|
|
13
34
|
defaultValue?: string;
|
|
35
|
+
/**
|
|
36
|
+
* Additional classes to apply to the select.
|
|
37
|
+
*/
|
|
14
38
|
class?: string;
|
|
39
|
+
/**
|
|
40
|
+
* The name of the select. Required because of the helper.
|
|
41
|
+
*/
|
|
15
42
|
name?: string;
|
|
43
|
+
/**
|
|
44
|
+
* Whether the select is required. Defaults to `false`.
|
|
45
|
+
*/
|
|
16
46
|
isRequired?: boolean;
|
|
47
|
+
/**
|
|
48
|
+
* The options to display in the select.
|
|
49
|
+
*/
|
|
17
50
|
options: Option[];
|
|
51
|
+
/**
|
|
52
|
+
* Whether the select is disabled. Defaults to `false`.
|
|
53
|
+
*/
|
|
18
54
|
disabled?: boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Whether the select is full width. Defaults to `false`.
|
|
57
|
+
*/
|
|
19
58
|
fullWidth?: boolean;
|
|
59
|
+
/**
|
|
60
|
+
* The placeholder of the select. Defaults to `Select`.
|
|
61
|
+
*/
|
|
20
62
|
placeholder?: string;
|
|
21
63
|
}
|
|
22
64
|
|
|
@@ -29,7 +71,7 @@ const {
|
|
|
29
71
|
options = [],
|
|
30
72
|
disabled,
|
|
31
73
|
fullWidth,
|
|
32
|
-
placeholder,
|
|
74
|
+
placeholder = 'Select',
|
|
33
75
|
} = Astro.props;
|
|
34
76
|
---
|
|
35
77
|
|
|
@@ -37,6 +79,8 @@ const {
|
|
|
37
79
|
id={`${name}-container`}
|
|
38
80
|
class="sui-select-label"
|
|
39
81
|
class:list={[disabled && "disabled", className, fullWidth && "full"]}
|
|
82
|
+
data-options={JSON.stringify(options)}
|
|
83
|
+
data-id={name}
|
|
40
84
|
>
|
|
41
85
|
{label && (
|
|
42
86
|
<label class="label" for={`${name}-select-btn`}>
|
|
@@ -51,12 +95,14 @@ const {
|
|
|
51
95
|
aria-expanded="false"
|
|
52
96
|
id={`${name}-select-btn`}
|
|
53
97
|
type="button"
|
|
98
|
+
aria-label={placeholder}
|
|
99
|
+
title={placeholder}
|
|
54
100
|
>
|
|
55
|
-
<span id={`${name}-value-span`}>
|
|
101
|
+
<span class="sui-select-value-span" id={`${name}-value-span`}>
|
|
56
102
|
{
|
|
57
103
|
defaultValue
|
|
58
104
|
? options.find((x) => x.value === defaultValue)?.label
|
|
59
|
-
: placeholder
|
|
105
|
+
: placeholder
|
|
60
106
|
}
|
|
61
107
|
</span>
|
|
62
108
|
<Icon name="chevron-up-down" width={24} height={24} />
|
|
@@ -74,14 +120,13 @@ const {
|
|
|
74
120
|
]}
|
|
75
121
|
id={defaultValue === x.value ? `${name}-selected` : ""}
|
|
76
122
|
data-option-index={i}
|
|
77
|
-
tabindex={x.disabled ? -1 : 0}
|
|
78
123
|
>
|
|
79
124
|
{x.label}
|
|
80
125
|
</li>
|
|
81
126
|
))
|
|
82
127
|
}
|
|
83
128
|
</ul>
|
|
84
|
-
<select class="sui-hidden-select" id={name} name={name} required={isRequired}>
|
|
129
|
+
<select class="sui-hidden-select" id={name} name={name} required={isRequired} hidden tabindex="-1">
|
|
85
130
|
<option value={""}> Select </option>
|
|
86
131
|
{
|
|
87
132
|
options.map((x) => (
|
|
@@ -96,129 +141,204 @@ const {
|
|
|
96
141
|
}
|
|
97
142
|
</select>
|
|
98
143
|
</div>
|
|
99
|
-
<script
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
const
|
|
115
|
-
const margin = 4;
|
|
116
|
-
|
|
117
|
-
const dropdownHeight =
|
|
118
|
-
options.length * optionHeight + totalBorderSize + margin;
|
|
119
|
-
|
|
120
|
-
const CustomRect = {
|
|
121
|
-
top: bottom + margin,
|
|
122
|
-
left,
|
|
123
|
-
right,
|
|
124
|
-
bottom: bottom + margin + dropdownHeight,
|
|
125
|
-
width,
|
|
126
|
-
height: dropdownHeight,
|
|
127
|
-
x,
|
|
128
|
-
y: y + height + margin,
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
if (active) {
|
|
132
|
-
button.ariaExpanded = false;
|
|
144
|
+
<script>
|
|
145
|
+
const allSelects = document.querySelectorAll<HTMLDivElement>(".sui-select-label");
|
|
146
|
+
// id, options
|
|
147
|
+
|
|
148
|
+
for (const container of allSelects) {
|
|
149
|
+
const hiddenSelect = container.querySelector('select')!;
|
|
150
|
+
const button = container.querySelector('button')!;
|
|
151
|
+
const valueSpan = container.querySelector('.sui-select-value-span')!;
|
|
152
|
+
const dropdown = container.querySelector('.sui-select-dropdown')!;
|
|
153
|
+
const optionElements = container.querySelectorAll<HTMLLIElement>('.sui-select-option');
|
|
154
|
+
|
|
155
|
+
const options = JSON.parse(container.dataset.options!);
|
|
156
|
+
const id = container.dataset.id!;
|
|
157
|
+
let active = false;
|
|
158
|
+
|
|
159
|
+
const closeDropdown = () => {
|
|
133
160
|
dropdown.classList.remove("active", "above");
|
|
134
161
|
active = false;
|
|
135
|
-
|
|
162
|
+
button.ariaExpanded = 'false';
|
|
163
|
+
focusIndex = -1;
|
|
164
|
+
|
|
165
|
+
for (const entry of optionElements) {
|
|
166
|
+
entry.classList.remove('focused');
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
const openDropdown = (toggle: boolean) => {
|
|
171
|
+
const { bottom, left, right, width, x, y, height } =
|
|
172
|
+
button.getBoundingClientRect();
|
|
173
|
+
|
|
174
|
+
const optionHeight = 36;
|
|
175
|
+
const totalBorderSize = 2;
|
|
176
|
+
const margin = 4;
|
|
177
|
+
|
|
178
|
+
const dropdownHeight =
|
|
179
|
+
options.length * optionHeight + totalBorderSize + margin;
|
|
180
|
+
|
|
181
|
+
const CustomRect = {
|
|
182
|
+
top: bottom + margin,
|
|
183
|
+
left,
|
|
184
|
+
right,
|
|
185
|
+
bottom: bottom + margin + dropdownHeight,
|
|
186
|
+
width,
|
|
187
|
+
height: dropdownHeight,
|
|
188
|
+
x,
|
|
189
|
+
y: y + height + margin,
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
if (active && toggle) {
|
|
193
|
+
closeDropdown();
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
active = true;
|
|
198
|
+
button.ariaExpanded = 'true';
|
|
199
|
+
|
|
200
|
+
// Set focusIndex to currently selected option
|
|
201
|
+
focusIndex = Array.from(optionElements).findIndex((x) =>
|
|
202
|
+
x.classList.contains("selected")
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
if (
|
|
206
|
+
CustomRect.top >= 0 &&
|
|
207
|
+
CustomRect.left >= 0 &&
|
|
208
|
+
CustomRect.bottom <=
|
|
209
|
+
(window.innerHeight || document.documentElement.clientHeight) &&
|
|
210
|
+
CustomRect.right <=
|
|
211
|
+
(window.innerWidth || document.documentElement.clientWidth)
|
|
212
|
+
) {
|
|
213
|
+
dropdown.classList.add("active");
|
|
214
|
+
} else {
|
|
215
|
+
dropdown.classList.add("active", "above");
|
|
216
|
+
}
|
|
136
217
|
}
|
|
137
218
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
} else {
|
|
151
|
-
dropdown.classList.add("active", "above");
|
|
219
|
+
button.addEventListener("click", () => openDropdown(true));
|
|
220
|
+
|
|
221
|
+
let focusIndex = -1;
|
|
222
|
+
|
|
223
|
+
const recomputeOptions = () => {
|
|
224
|
+
for (const entry of optionElements) {
|
|
225
|
+
if (Number.parseInt(entry.dataset.optionIndex!) == focusIndex) {
|
|
226
|
+
entry.classList.add('focused');
|
|
227
|
+
} else {
|
|
228
|
+
entry.classList.remove('focused');
|
|
229
|
+
}
|
|
230
|
+
}
|
|
152
231
|
}
|
|
153
|
-
});
|
|
154
232
|
|
|
155
|
-
|
|
156
|
-
|
|
233
|
+
button.addEventListener('keydown', (e) => {
|
|
234
|
+
if (e.key === 'Tab' || e.key === 'Escape') {
|
|
235
|
+
closeDropdown();
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (e.key === ' ' && !active) openDropdown(false);
|
|
240
|
+
|
|
241
|
+
if (e.key === 'Enter') {
|
|
242
|
+
let currentlyFocused = container.querySelector<HTMLElement>('.focused');
|
|
243
|
+
if (currentlyFocused) {
|
|
244
|
+
currentlyFocused.classList.remove('focused');
|
|
245
|
+
currentlyFocused.click();
|
|
246
|
+
|
|
247
|
+
// Stop dropdown from immediately reopening
|
|
248
|
+
e.preventDefault();
|
|
249
|
+
e.stopImmediatePropagation();
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
e.preventDefault();
|
|
157
256
|
e.stopImmediatePropagation();
|
|
158
|
-
if (option.id === `${id}-selected` || !id) return;
|
|
159
257
|
|
|
160
|
-
const
|
|
258
|
+
const neighbor = (offset: number) => {
|
|
259
|
+
return optionElements.item((Array.from(optionElements).findIndex((x) => x.classList.contains("selected")) ?? -1) + offset)
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
if (e.key === "ArrowUp" && (focusIndex > 0 || !active)) {
|
|
263
|
+
if (!active) return neighbor(-1)?.click();
|
|
264
|
+
focusIndex--;
|
|
265
|
+
recomputeOptions();
|
|
266
|
+
}
|
|
161
267
|
|
|
268
|
+
if (e.key === "ArrowDown" && focusIndex + 1 < optionElements.length) {
|
|
269
|
+
if (!active) return neighbor(1)?.click();
|
|
270
|
+
focusIndex++;
|
|
271
|
+
recomputeOptions();
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
if (e.key === 'PageUp') {
|
|
275
|
+
focusIndex = 0;
|
|
276
|
+
if (!active) return optionElements.item(focusIndex)?.click();
|
|
277
|
+
recomputeOptions();
|
|
278
|
+
}
|
|
279
|
+
if (e.key === 'PageDown') {
|
|
280
|
+
focusIndex = optionElements.length - 1;
|
|
281
|
+
if (!active) return optionElements.item(focusIndex)?.click();
|
|
282
|
+
recomputeOptions();
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
const handleSelection = (e: MouseEvent, option: HTMLElement) => {
|
|
287
|
+
e.stopImmediatePropagation();
|
|
288
|
+
if (option.id === `${id}-selected` || !id) return;
|
|
289
|
+
const currentlySelected = document.getElementById(`${id}-selected`);
|
|
162
290
|
if (currentlySelected) {
|
|
163
291
|
currentlySelected.classList.remove("selected");
|
|
164
292
|
currentlySelected.id = "";
|
|
165
293
|
}
|
|
166
|
-
|
|
167
294
|
option.id = `${id}-selected`;
|
|
168
295
|
option.classList.add("selected");
|
|
169
|
-
|
|
170
|
-
const opt = options[parseInt(option.dataset.optionIndex)];
|
|
296
|
+
const opt = options[parseInt(option.dataset.optionIndex!)];
|
|
171
297
|
hiddenSelect.value = opt.value;
|
|
172
|
-
|
|
173
298
|
valueSpan.textContent = opt.label;
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
active = false;
|
|
299
|
+
closeDropdown();
|
|
177
300
|
}
|
|
178
301
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
if (e.key === 'Enter') {
|
|
182
|
-
handleSelection(e);
|
|
183
|
-
}
|
|
184
|
-
});
|
|
185
|
-
});
|
|
302
|
+
optionElements.forEach((option) => {
|
|
303
|
+
const handleSelectionForOption = (e: MouseEvent) => handleSelection(e, option)
|
|
186
304
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
active = false;
|
|
190
|
-
});
|
|
305
|
+
option.addEventListener("click", handleSelectionForOption);
|
|
306
|
+
});
|
|
191
307
|
|
|
192
|
-
|
|
308
|
+
window.addEventListener("scroll", closeDropdown);
|
|
193
309
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
!element.contains(event.target) &&
|
|
198
|
-
isVisible(element) &&
|
|
199
|
-
active === true
|
|
200
|
-
) {
|
|
201
|
-
// or use: event.target.closest(selector) === null
|
|
202
|
-
dropdown.classList.remove("active", "above");
|
|
203
|
-
active = false;
|
|
310
|
+
document.addEventListener("keydown", (e) => {
|
|
311
|
+
if (e.key === "Escape" && dropdown.classList.contains("active")) {
|
|
312
|
+
closeDropdown();
|
|
204
313
|
}
|
|
205
|
-
};
|
|
314
|
+
});
|
|
206
315
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
316
|
+
hideOnClickOutside(container);
|
|
317
|
+
|
|
318
|
+
function hideOnClickOutside(element: HTMLElement) {
|
|
319
|
+
const outsideClickListener = (event: MouseEvent) => {
|
|
320
|
+
if (
|
|
321
|
+
!element.contains(event.target as HTMLElement) &&
|
|
322
|
+
isVisible(element) &&
|
|
323
|
+
active === true
|
|
324
|
+
) {
|
|
325
|
+
// or use: event.target.closest(selector) === null
|
|
326
|
+
closeDropdown();
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
document.addEventListener("click", outsideClickListener);
|
|
331
|
+
}
|
|
210
332
|
|
|
211
|
-
|
|
333
|
+
// source (2018-03-11): https://github.com/jquery/jquery/blob/master/src/css/hiddenVisibleSelectors.js
|
|
334
|
+
const isVisible = (elem: HTMLElement) =>
|
|
335
|
+
!!elem &&
|
|
336
|
+
!!(
|
|
337
|
+
elem.offsetWidth ||
|
|
338
|
+
elem.offsetHeight ||
|
|
339
|
+
elem.getClientRects().length
|
|
340
|
+
);
|
|
212
341
|
}
|
|
213
|
-
|
|
214
|
-
// source (2018-03-11): https://github.com/jquery/jquery/blob/master/src/css/hiddenVisibleSelectors.js
|
|
215
|
-
const isVisible = (elem) =>
|
|
216
|
-
!!elem &&
|
|
217
|
-
!!(
|
|
218
|
-
elem.offsetWidth ||
|
|
219
|
-
elem.offsetHeight ||
|
|
220
|
-
elem.getClientRects().length
|
|
221
|
-
);
|
|
222
342
|
</script>
|
|
223
343
|
<style>
|
|
224
344
|
.sui-select-label {
|
|
@@ -256,7 +376,7 @@ const {
|
|
|
256
376
|
border: 1px solid hsl(var(--border));
|
|
257
377
|
background: hsl(var(--background-step-2));
|
|
258
378
|
color: hsl(var(--text-normal));
|
|
259
|
-
transition:
|
|
379
|
+
transition: background border 0.15s ease;
|
|
260
380
|
display: flex;
|
|
261
381
|
flex-direction: row;
|
|
262
382
|
align-items: center;
|
|
@@ -265,7 +385,11 @@ const {
|
|
|
265
385
|
gap: 1rem;
|
|
266
386
|
}
|
|
267
387
|
|
|
268
|
-
.sui-select-button:
|
|
388
|
+
.sui-select-button:focus {
|
|
389
|
+
border: 1px solid hsl(var(--primary-base));
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
.sui-select-button:hover, .sui-select-button:focus {
|
|
269
393
|
background: hsl(var(--background-step-3));
|
|
270
394
|
}
|
|
271
395
|
|
|
@@ -315,7 +439,7 @@ const {
|
|
|
315
439
|
color: hsl(var(--text-muted));
|
|
316
440
|
}
|
|
317
441
|
|
|
318
|
-
.sui-select-option:hover, .sui-select-option:focus {
|
|
442
|
+
.sui-select-option:hover, .sui-select-option:focus, .sui-select-option.focused {
|
|
319
443
|
background-color: hsl(var(--background-step-3));
|
|
320
444
|
}
|
|
321
445
|
|
|
@@ -2,6 +2,10 @@ class SingleSidebarHelper {
|
|
|
2
2
|
private sidebar: HTMLElement;
|
|
3
3
|
private sidebarToggle?: HTMLElement | undefined;
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* A helper to manage the sidebar with.
|
|
7
|
+
* @param toggleID The ID of the element that should toggle the sidebar.
|
|
8
|
+
*/
|
|
5
9
|
constructor(toggleID?: string) {
|
|
6
10
|
const sidebarContainer = document.getElementById('sui-sidebar');
|
|
7
11
|
|
|
@@ -28,6 +32,10 @@ class SingleSidebarHelper {
|
|
|
28
32
|
}
|
|
29
33
|
}
|
|
30
34
|
|
|
35
|
+
/**
|
|
36
|
+
* A helper function register an element which should toggle the sidebar.
|
|
37
|
+
* @param elementID The ID of the element that should toggle the sidebar.
|
|
38
|
+
*/
|
|
31
39
|
public toggleSidebarOnClick = (elementID: string) => {
|
|
32
40
|
const navToggle = document.getElementById(elementID);
|
|
33
41
|
|
|
@@ -42,6 +50,10 @@ class SingleSidebarHelper {
|
|
|
42
50
|
});
|
|
43
51
|
};
|
|
44
52
|
|
|
53
|
+
/**
|
|
54
|
+
* A helper function to hide the sidebar when an element is clicked.
|
|
55
|
+
* @param elementID The ID of the element that should hide the sidebar.
|
|
56
|
+
*/
|
|
45
57
|
public hideSidebarOnClick = (elementID: string) => {
|
|
46
58
|
const element = document.getElementById(elementID);
|
|
47
59
|
|
|
@@ -52,6 +64,10 @@ class SingleSidebarHelper {
|
|
|
52
64
|
element.addEventListener('click', this.hideSidebar);
|
|
53
65
|
};
|
|
54
66
|
|
|
67
|
+
/**
|
|
68
|
+
* A helper function to show the sidebar when an element is clicked.
|
|
69
|
+
* @param elementID The ID of the element that should show the sidebar.
|
|
70
|
+
*/
|
|
55
71
|
public showSidebarOnClick = (elementID: string) => {
|
|
56
72
|
const element = document.getElementById(elementID);
|
|
57
73
|
|
|
@@ -62,10 +78,16 @@ class SingleSidebarHelper {
|
|
|
62
78
|
element.addEventListener('click', this.showSidebar);
|
|
63
79
|
};
|
|
64
80
|
|
|
81
|
+
/**
|
|
82
|
+
* A function to hide the sidebar.
|
|
83
|
+
*/
|
|
65
84
|
public hideSidebar = () => {
|
|
66
85
|
this.sidebar.classList.remove('active');
|
|
67
86
|
};
|
|
68
87
|
|
|
88
|
+
/**
|
|
89
|
+
* A function to show the sidebar.
|
|
90
|
+
*/
|
|
69
91
|
public showSidebar = () => {
|
|
70
92
|
this.sidebar.classList.add('active');
|
|
71
93
|
};
|
|
@@ -74,6 +96,9 @@ class SingleSidebarHelper {
|
|
|
74
96
|
class DoubleSidebarHelper {
|
|
75
97
|
private sidebarsContainer: HTMLElement;
|
|
76
98
|
|
|
99
|
+
/**
|
|
100
|
+
* A helper to manage the double sidebar with.
|
|
101
|
+
*/
|
|
77
102
|
constructor() {
|
|
78
103
|
const sidebarsContainer = document.getElementById('sui-sidebars');
|
|
79
104
|
|
|
@@ -86,6 +111,10 @@ class DoubleSidebarHelper {
|
|
|
86
111
|
this.sidebarsContainer = sidebarsContainer;
|
|
87
112
|
}
|
|
88
113
|
|
|
114
|
+
/**
|
|
115
|
+
* A helper function to hide the sidebar when an element is clicked.
|
|
116
|
+
* @param elementID The ID of the element that should hide the sidebar.
|
|
117
|
+
*/
|
|
89
118
|
public hideSidebarOnClick = (elementID: string) => {
|
|
90
119
|
const element = document.getElementById(elementID);
|
|
91
120
|
|
|
@@ -96,6 +125,10 @@ class DoubleSidebarHelper {
|
|
|
96
125
|
element.addEventListener('click', this.hideSidebar);
|
|
97
126
|
};
|
|
98
127
|
|
|
128
|
+
/**
|
|
129
|
+
* A helper function to show the outer sidebar when an element is clicked.
|
|
130
|
+
* @param elementID The ID of the element that should show the outer sidebar.
|
|
131
|
+
*/
|
|
99
132
|
public showOuterOnClick = (elementID: string) => {
|
|
100
133
|
const element = document.getElementById(elementID);
|
|
101
134
|
|
|
@@ -106,6 +139,10 @@ class DoubleSidebarHelper {
|
|
|
106
139
|
element.addEventListener('click', this.showOuterSidebar);
|
|
107
140
|
};
|
|
108
141
|
|
|
142
|
+
/**
|
|
143
|
+
* A helper function to show the inner sidebar when an element is clicked.
|
|
144
|
+
* @param elementID The ID of the element that should show the inner sidebar.
|
|
145
|
+
*/
|
|
109
146
|
public showInnerOnClick = (elementID: string) => {
|
|
110
147
|
const element = document.getElementById(elementID);
|
|
111
148
|
|
|
@@ -116,15 +153,24 @@ class DoubleSidebarHelper {
|
|
|
116
153
|
element.addEventListener('click', this.showInnerSidebar);
|
|
117
154
|
};
|
|
118
155
|
|
|
156
|
+
/**
|
|
157
|
+
* A function to show the inner sidebar.
|
|
158
|
+
*/
|
|
119
159
|
public showInnerSidebar = () => {
|
|
120
160
|
this.sidebarsContainer.classList.add('inner', 'active');
|
|
121
161
|
};
|
|
122
162
|
|
|
163
|
+
/**
|
|
164
|
+
* A function to show the outer sidebar.
|
|
165
|
+
*/
|
|
123
166
|
public showOuterSidebar = () => {
|
|
124
167
|
this.sidebarsContainer.classList.add('active');
|
|
125
168
|
this.sidebarsContainer.classList.remove('inner');
|
|
126
169
|
};
|
|
127
170
|
|
|
171
|
+
/**
|
|
172
|
+
* A function to hide the sidebar altogether.
|
|
173
|
+
*/
|
|
128
174
|
public hideSidebar = () => {
|
|
129
175
|
this.sidebarsContainer.classList.remove('inner', 'active');
|
|
130
176
|
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { StudioCMSColorway } from '../../utils/colors';
|
|
3
|
+
import { generateID } from '../../utils/generateID';
|
|
4
|
+
import type { HeroIconName } from '../../utils/iconType';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The props for the TabItem component.
|
|
8
|
+
*/
|
|
9
|
+
interface Props {
|
|
10
|
+
/**
|
|
11
|
+
* The icon to display next to the tab.
|
|
12
|
+
*/
|
|
13
|
+
icon?: HeroIconName;
|
|
14
|
+
/**
|
|
15
|
+
* The label of the tab.
|
|
16
|
+
*/
|
|
17
|
+
label: string;
|
|
18
|
+
/**
|
|
19
|
+
* The color of the tab. Defaults to `primary`.
|
|
20
|
+
*/
|
|
21
|
+
color?: Exclude<StudioCMSColorway, 'default'>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const id = generateID('tab');
|
|
25
|
+
|
|
26
|
+
const { icon, label, color = 'primary' } = Astro.props;
|
|
27
|
+
---
|
|
28
|
+
<sui-tab-item
|
|
29
|
+
data-icon={icon}
|
|
30
|
+
data-label={label}
|
|
31
|
+
data-color={color}
|
|
32
|
+
data-tab-id={id}
|
|
33
|
+
class=""
|
|
34
|
+
>
|
|
35
|
+
<slot />
|
|
36
|
+
</sui-tab-item>
|
|
37
|
+
<style>
|
|
38
|
+
sui-tab-item {
|
|
39
|
+
display: none;
|
|
40
|
+
width: 100%;
|
|
41
|
+
height: auto;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
sui-tab-item.active {
|
|
45
|
+
display: block;
|
|
46
|
+
}
|
|
47
|
+
</style>
|