@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
@@ -12,13 +12,13 @@
12
12
  .sui-search-select-label.disabled {
13
13
  opacity: 0.5;
14
14
  pointer-events: none;
15
- color: hsl(var(--text-muted));
15
+ color: var(--text-muted);
16
16
  }
17
17
  .label {
18
18
  font-size: 14px;
19
19
  }
20
20
  .req-star {
21
- color: hsl(var(--danger-base));
21
+ color: var(--danger-base);
22
22
  font-weight: 700;
23
23
  }
24
24
  .sui-search-select-dropdown-container {
@@ -32,24 +32,36 @@
32
32
  .sui-search-select-dropdown {
33
33
  position: absolute;
34
34
  width: 100%;
35
- border: 1px solid hsl(var(--border));
35
+ border: 1px solid var(--border);
36
36
  list-style: none;
37
37
  margin: 0;
38
38
  padding: 0;
39
39
  flex-direction: column;
40
40
  border-radius: var(--radius-md);
41
- background-color: hsl(var(--background-step-2));
41
+ background-color: var(--background-step-2);
42
42
  overflow: hidden;
43
43
  top: calc(100% + 0.25rem);
44
44
  left: 0;
45
45
  display: none;
46
+ max-height: 300px;
47
+ border: 1px solid var(--border);
48
+ border-radius: var(--radius-md);
49
+ background-color: var(--background-step-2);
46
50
  z-index: 90;
47
- box-shadow: 0px 4px 8px hsl(var(--shadow), 0.5);
51
+ box-shadow: 0px 4px 8px var(--shadow);
48
52
  }
49
53
  .sui-search-select-dropdown.above {
50
54
  top: auto;
51
55
  bottom: calc(100% - 1.5rem);
52
56
  }
57
+ .sui-search-select-dropdown-list {
58
+ list-style: none;
59
+ margin: 0;
60
+ padding: 0;
61
+ display: flex;
62
+ flex-direction: column;
63
+ width: 100%;
64
+ }
53
65
  .sui-search-select-option,
54
66
  .empty-search-results {
55
67
  padding: 0.5rem;
@@ -58,25 +70,25 @@
58
70
  transition: all 0.15s ease;
59
71
  }
60
72
  .empty-search-results:hover {
61
- background-color: hsl(var(--background-step-2));
73
+ background-color: var(--background-step-2);
62
74
  cursor: default;
63
75
  }
64
76
  .sui-search-select-option.disabled {
65
77
  pointer-events: none;
66
- color: hsl(var(--text-muted));
78
+ color: var(--text-muted);
67
79
  }
68
80
  .sui-search-select-option:hover,
69
81
  .sui-search-select-option.focused {
70
- background-color: hsl(var(--background-step-3));
82
+ background-color: var(--background-step-3);
71
83
  }
72
84
  .sui-search-select-label[data-multiple=true] .sui-search-select-option.selected:hover,
73
85
  .sui-search-select-label[data-multiple=true] .sui-search-select-option.selected:focus,
74
86
  .sui-search-select-label[data-multiple=true] .sui-search-select-option.selected.focused {
75
- background-color: hsl(var(--primary-hover));
87
+ background-color: var(--primary-hover);
76
88
  }
77
89
  .sui-search-select-option.selected {
78
- background-color: hsl(var(--primary-base));
79
- color: hsl(var(--text-inverted));
90
+ background-color: var(--primary-base);
91
+ color: var(--text-inverted);
80
92
  cursor: default;
81
93
  }
82
94
  .sui-search-select-label[data-multiple=true] .sui-search-select-option.selected {
@@ -105,7 +117,6 @@
105
117
  position: absolute;
106
118
  bottom: .675rem;
107
119
  right: .675rem;
108
- pointer-events: none;
109
120
  }
110
121
  .sui-search-input-wrapper:has(input:focus) + .sui-search-select-dropdown {
111
122
  display: flex;
@@ -141,7 +152,7 @@
141
152
  aspect-ratio: 1 / 1;
142
153
  border-radius: 999px;
143
154
  cursor: pointer;
144
- outline: 1px solid hsla(var(--border), 0);
155
+ outline: 1px solid transparent;
145
156
  transition: background-color 0.15s ease, outline 0.15s ease;
146
157
  }
147
158
  .sui-search-select-badge-container .sui-search-select-badge.sui-badge svg:hover {
@@ -150,9 +161,9 @@
150
161
  .sui-search-select-badge-container .sui-search-select-badge.sui-badge svg:active,
151
162
  .sui-search-select-badge-container .sui-search-select-badge.sui-badge svg:focus {
152
163
  background-color: hsla(100, 100%, 95%, 0.2);
153
- outline: 1px solid hsl(var(--border));
164
+ outline: 1px solid var(--border);
154
165
  }
155
166
  .sui-search-select-max-span {
156
167
  font-size: 0.875em;
157
- color: hsl(var(--text-muted));
168
+ color: var(--text-muted);
158
169
  }
@@ -82,7 +82,7 @@ function loadSearchSelects() {
82
82
  );
83
83
  if (option) {
84
84
  option.classList.toggle("selected", forceState ?? !isCurrentlySelected);
85
- if (container && container.select) {
85
+ if (container?.select) {
86
86
  container.select.value = option.getAttribute("value");
87
87
  }
88
88
  }
@@ -150,6 +150,26 @@ function loadSearchSelects() {
150
150
  if (!target.closest("input")) {
151
151
  e.preventDefault();
152
152
  }
153
+ if (container.input?.value.length === 0) {
154
+ reconstructOptions(state2.optionsMap[container.dataset.id] ?? [], state2, container);
155
+ }
156
+ if (target.closest(".sui-search-select-indicator")) {
157
+ if (container.dropdown?.parentElement?.classList.contains("active")) {
158
+ container.dropdown?.parentElement?.classList.remove("active", "above");
159
+ container.input?.blur();
160
+ container.input.value = "";
161
+ } else {
162
+ container.dropdown?.parentElement?.classList.add("active");
163
+ container.input?.focus();
164
+ container.input.value = "";
165
+ }
166
+ return;
167
+ }
168
+ if (target.closest(".sui-search-select-badge-container")) {
169
+ container.dropdown?.parentElement?.classList.remove("active", "above");
170
+ container.input?.blur();
171
+ container.input.value = "";
172
+ }
153
173
  state2.isSelectingOption = true;
154
174
  setTimeout(() => {
155
175
  state2.isSelectingOption = false;
@@ -182,7 +202,7 @@ function loadSearchSelects() {
182
202
  }
183
203
  updateOptionSelection(opt.dataset.value, container, state2, true);
184
204
  updateLabel(false, state2, container);
185
- container.dropdown?.classList.remove("active", "above");
205
+ container.dropdown?.parentElement?.classList.remove("active", "above");
186
206
  container.input?.blur();
187
207
  container.input.value = "";
188
208
  }
@@ -191,7 +211,7 @@ function loadSearchSelects() {
191
211
  const focusedElement = document.activeElement;
192
212
  if (e.key === "Escape" || e.key === "Tab") {
193
213
  container.input?.blur();
194
- container.dropdown?.classList.remove("active", "above");
214
+ container.dropdown?.parentElement?.classList.remove("active", "above");
195
215
  return;
196
216
  }
197
217
  if ((e.key === "Enter" || e.key === " ") && focusedElement?.tagName.toLowerCase() === "svg") {
@@ -278,7 +298,7 @@ function loadSearchSelects() {
278
298
  }
279
299
  updateOptionSelection(value, container, state2, true);
280
300
  updateLabel(false, state2, container);
281
- container.dropdown?.classList.remove("active", "above");
301
+ container.dropdown?.parentElement?.classList.remove("active", "above");
282
302
  container.input.value = "";
283
303
  }
284
304
  }
@@ -301,20 +321,20 @@ function loadSearchSelects() {
301
321
  if (state2.isSelectingOption) return;
302
322
  container.input.value = "";
303
323
  reconstructOptions(state2.optionsMap[container.dataset.id] ?? [], state2, container);
304
- container.dropdown?.classList.remove("active", "above");
324
+ container.dropdown?.parentElement?.classList.remove("active", "above");
305
325
  };
306
326
  const handleContainerFocusIn = (state2, container) => {
307
- const allDropdowns = document.querySelectorAll(".sui-search-select-dropdown");
327
+ const allDropdowns = document.querySelectorAll(".sui-search-select-dropdown-list");
308
328
  for (const dropdown of allDropdowns) {
309
329
  if (dropdown !== container.dropdown) {
310
- dropdown.classList.remove("active", "above");
330
+ dropdown.parentElement?.classList.remove("active", "above");
311
331
  }
312
332
  }
313
333
  const { isAbove } = getDropdownPosition(
314
334
  container.input,
315
335
  state2.optionsMap[container.dataset.id]?.length ?? 0
316
336
  );
317
- container.dropdown?.classList.add("active", ...isAbove ? [] : ["above"]);
337
+ container.dropdown?.parentElement?.classList.add("active", ...isAbove ? [] : ["above"]);
318
338
  };
319
339
  const state = {
320
340
  optionsMap: {},
@@ -330,7 +350,7 @@ function loadSearchSelects() {
330
350
  const id = container.dataset.id;
331
351
  const specialContainer = Object.assign(container, {
332
352
  input: container.querySelector("input"),
333
- dropdown: container.querySelector(".sui-search-select-dropdown"),
353
+ dropdown: container.querySelector(".sui-search-select-dropdown-list"),
334
354
  select: container.querySelector("select")
335
355
  });
336
356
  const selectedOptions = Array.from(
@@ -160,36 +160,38 @@ const defaultLabel = selected
160
160
  >
161
161
  <span class="sui-select-value-span" id={`${name}-value-span`}>
162
162
  {Array.isArray(selected)
163
- ? <div class="sui-select-badge-container">{selected.map((s) => s && <Badge class="sui-select-badge" data-value={s.value} size="sm" label={s.label} iconPosition="right" icon="x-mark" />)}</div>
163
+ ? <div class="sui-select-badge-container">{selected.map((s) => s && <Badge class="sui-select-badge" data-value={s.value} size="sm" label={s.label} iconPosition="right" icon="heroicons:x-mark" />)}</div>
164
164
  : selected?.label ?? defaultLabel
165
165
  }
166
166
  </span>
167
- <Icon name="chevron-up-down" width={24} height={24} class="sui-select-chevron" />
167
+ <Icon name="heroicons:chevron-up-down" width={24} height={24} class="sui-select-chevron" />
168
168
  </button>
169
- <ul class="sui-select-dropdown" role="listbox" id={`${name}-dropdown`}>
170
- {
171
- options.map((x, i) => {
172
- const isSelected = Array.isArray(selected)
173
- ? selected.map((y) => y && y.value).includes(x.value)
174
- : selected?.value === x.value;
175
- return (
176
- <li
177
- class="sui-select-option"
178
- role="option"
179
- value={x.value}
180
- class:list={[
181
- isSelected && `selected`,
182
- x.disabled && "disabled",
183
- ]}
184
- id={isSelected ? `${name}-selected` : ""}
185
- data-option-index={i}
186
- >
187
- {x.label}
188
- </li>
189
- )
190
- })
191
- }
192
- </ul>
169
+ <div class="sui-select-dropdown">
170
+ <ul class="sui-select-dropdown-list" role="listbox" id={`${name}-dropdown`}>
171
+ {
172
+ options.map((x, i) => {
173
+ const isSelected = Array.isArray(selected)
174
+ ? selected.map((y) => y && y.value).includes(x.value)
175
+ : selected?.value === x.value;
176
+ return (
177
+ <li
178
+ class="sui-select-option"
179
+ role="option"
180
+ value={x.value}
181
+ class:list={[
182
+ isSelected && `selected`,
183
+ x.disabled && "disabled",
184
+ ]}
185
+ id={isSelected ? `${name}-selected` : ""}
186
+ data-option-index={i}
187
+ >
188
+ {x.label}
189
+ </li>
190
+ )
191
+ })
192
+ }
193
+ </ul>
194
+ </div>
193
195
  </div>
194
196
  <select class="sui-hidden-select" id={name} name={name} required={isRequired} multiple={multiple ? "" : undefined} hidden tabindex="-1">
195
197
  <option value={""}> Select </option>
@@ -14,21 +14,21 @@
14
14
  .sui-select-label.disabled {
15
15
  opacity: 0.5;
16
16
  pointer-events: none;
17
- color: hsl(var(--text-muted));
17
+ color: var(--text-muted);
18
18
  }
19
19
  .label {
20
20
  font-size: 14px;
21
21
  }
22
22
  .req-star {
23
- color: hsl(var(--danger-base));
23
+ color: var(--danger-base);
24
24
  font-weight: 700;
25
25
  }
26
26
  .sui-select-button {
27
27
  padding: 0.5rem 0.75rem 0.5rem 0.75rem;
28
28
  border-radius: var(--radius-md);
29
- border: 1px solid hsl(var(--border));
30
- background: hsl(var(--background-step-2));
31
- color: hsl(var(--text-normal));
29
+ border: 1px solid var(--border);
30
+ background: var(--background-step-2);
31
+ color: var(--text-normal);
32
32
  transition: background, border 0.15s ease;
33
33
  display: flex;
34
34
  flex-direction: row;
@@ -38,17 +38,17 @@
38
38
  gap: 1rem;
39
39
  }
40
40
  .sui-select-button:focus {
41
- border: 1px solid hsl(var(--primary-base));
41
+ border: 1px solid var(--primary-base);
42
42
  }
43
43
  .sui-select-button:hover,
44
44
  .sui-select-button:focus {
45
- background: hsl(var(--background-step-3));
45
+ background: var(--background-step-3);
46
46
  }
47
47
  .sui-select-button.active,
48
48
  .sui-select-button:active,
49
49
  .sui-select-button:has(+ .sui-select-dropdown.active) {
50
- border: 1px solid hsl(var(--primary-base));
51
- background: hsl(var(--background-step-2));
50
+ border: 1px solid var(--primary-base);
51
+ background: var(--background-step-2);
52
52
  }
53
53
  .sui-select-button:has(.sui-select-badge-container) {
54
54
  padding: 0.5rem 0.75rem 0.5rem 0.5rem;
@@ -70,19 +70,23 @@
70
70
  .sui-select-dropdown {
71
71
  position: absolute;
72
72
  width: 100%;
73
- border: 1px solid hsl(var(--border));
73
+ border: 1px solid var(--border);
74
74
  list-style: none;
75
75
  margin: 0;
76
76
  padding: 0;
77
77
  flex-direction: column;
78
78
  border-radius: var(--radius-md);
79
- background-color: hsl(var(--background-step-2));
79
+ background-color: var(--background-step-2);
80
80
  overflow: hidden;
81
81
  top: calc(100% + 0.25rem);
82
82
  left: 0;
83
83
  display: none;
84
+ max-height: 300px;
85
+ border: 1px solid var(--border);
86
+ border-radius: var(--radius-md);
87
+ background-color: var(--background-step-2);
84
88
  z-index: 90;
85
- box-shadow: 0px 4px 8px hsl(var(--shadow), 0.5);
89
+ box-shadow: 0px 4px 8px var(--shadow);
86
90
  }
87
91
  .sui-select-dropdown.active {
88
92
  display: flex;
@@ -91,6 +95,14 @@
91
95
  top: auto;
92
96
  bottom: calc(100% + 0.25rem);
93
97
  }
98
+ .sui-select-dropdown-list {
99
+ list-style: none;
100
+ margin: 0;
101
+ padding: 0;
102
+ display: flex;
103
+ flex-direction: column;
104
+ width: 100%;
105
+ }
94
106
  .sui-select-option {
95
107
  padding: 0.5rem;
96
108
  cursor: pointer;
@@ -99,25 +111,25 @@
99
111
  }
100
112
  .sui-select-option.disabled {
101
113
  pointer-events: none;
102
- color: hsl(var(--text-muted));
114
+ color: var(--text-muted);
103
115
  }
104
116
  .sui-select-option:hover,
105
117
  .sui-select-option:focus,
106
118
  .sui-select-option.focused {
107
- background-color: hsl(var(--background-step-3));
119
+ background-color: var(--background-step-3);
108
120
  }
109
121
  .sui-select-label[data-multiple=true] .sui-select-option.selected:hover,
110
122
  .sui-select-label[data-multiple=true] .sui-select-option.selected:focus,
111
123
  .sui-select-label[data-multiple=true] .sui-select-option.selected.focused {
112
- background-color: hsl(var(--primary-hover));
124
+ background-color: var(--primary-hover);
113
125
  }
114
126
  .sui-select-option:focus {
115
127
  outline: none;
116
128
  border: none;
117
129
  }
118
130
  .sui-select-option.selected {
119
- background-color: hsl(var(--primary-base));
120
- color: hsl(var(--text-inverted));
131
+ background-color: var(--primary-base);
132
+ color: var(--text-inverted);
121
133
  cursor: default;
122
134
  }
123
135
  .sui-select-label[data-multiple=true] .sui-select-option.selected {
@@ -166,7 +178,7 @@
166
178
  aspect-ratio: 1 / 1;
167
179
  border-radius: 999px;
168
180
  cursor: pointer;
169
- outline: 1px solid hsla(var(--border), 0);
181
+ outline: 1px solid transparent;
170
182
  transition: background-color 0.15s ease, outline 0.15s ease;
171
183
  }
172
184
  .sui-select-badge-container .sui-select-badge.sui-badge svg:hover {
@@ -175,9 +187,9 @@
175
187
  .sui-select-badge-container .sui-select-badge.sui-badge svg:active,
176
188
  .sui-select-badge-container .sui-select-badge.sui-badge svg:focus {
177
189
  background-color: hsla(100, 100%, 95%, 0.2);
178
- outline: 1px solid hsl(var(--border));
190
+ outline: 1px solid var(--border);
179
191
  }
180
192
  .sui-select-max-span {
181
193
  font-size: 0.875em;
182
- color: hsl(var(--text-muted));
194
+ color: var(--text-muted);
183
195
  }
@@ -191,7 +191,7 @@ function loadSelects() {
191
191
  }
192
192
  if (option) {
193
193
  option.classList.add("selected");
194
- if (container && container.select) {
194
+ if (container?.select) {
195
195
  container.select.value = option.getAttribute("value");
196
196
  }
197
197
  updateLabel(state2, container);
@@ -27,8 +27,8 @@
27
27
  height: 100%;
28
28
  min-width: 280px;
29
29
  width: 280px;
30
- background-color: hsl(var(--background-step-1));
31
- border-right: 1px solid hsl(var(--border));
30
+ background-color: var(--background-step-1);
31
+ border-right: 1px solid var(--border);
32
32
  gap: 1rem;
33
33
  display: flex;
34
34
  flex-direction: column;
@@ -43,8 +43,8 @@
43
43
  min-width: 280px;
44
44
  width: 280px;
45
45
  height: 100%;
46
- background-color: hsl(var(--background-step-2));
47
- border-right: 1px solid hsl(var(--border));
46
+ background-color: var(--background-step-2);
47
+ border-right: 1px solid var(--border);
48
48
  display: flex;
49
49
  flex-direction: column;
50
50
  gap: 1rem;
@@ -13,8 +13,8 @@ const props = Astro.props;
13
13
  height: 100%;
14
14
  min-width: 280px;
15
15
  width: 280px;
16
- background-color: hsl(var(--background-step-1));
17
- border-right: 1px solid hsl(var(--border));
16
+ background-color: var(--background-step-1);
17
+ border-right: 1px solid var(--border);
18
18
  gap: 1rem;
19
19
  display: flex;
20
20
  flex-direction: column;
@@ -0,0 +1,61 @@
1
+ ---
2
+ import type { z } from 'astro/zod';
3
+ import './skeleton.css';
4
+ import { SkeletonSchema } from './skeleton.js';
5
+
6
+ type Props = z.infer<typeof SkeletonSchema>;
7
+
8
+ const {
9
+ variant,
10
+ width,
11
+ height,
12
+ radius = 'lg',
13
+ animation = 'slide',
14
+ class: className,
15
+ } = Astro.props;
16
+
17
+ const gap = variant === 'card' ? (Astro.props.gap ?? '0.5rem') : undefined;
18
+
19
+ const styles = [width && `width: ${width};`, height && `height: ${height};`, gap && `gap: ${gap};`]
20
+ .filter(Boolean)
21
+ .join(' ');
22
+
23
+ function getClasses() {
24
+ const classes = ['sui-skeleton'];
25
+
26
+ if (variant) {
27
+ classes.push(`sui-skeleton-${variant}`);
28
+
29
+ if (variant === 'card') {
30
+ const direction = Astro.props.direction ?? 'column';
31
+ const hAlign = Astro.props.hAlign ?? 'center';
32
+ const vAlign = Astro.props.vAlign ?? 'center';
33
+
34
+ classes.push(`sui-skeleton-${direction}`);
35
+
36
+ const hAlignProp = direction === 'row' ? 'justify' : 'align';
37
+ const vAlignProp = direction === 'row' ? 'items' : 'justify';
38
+
39
+ classes.push(`sui-skeleton-${hAlignProp}-${hAlign}`);
40
+ classes.push(`sui-skeleton-${vAlignProp}-${vAlign}`);
41
+ }
42
+ }
43
+
44
+ if (radius !== 'none' && variant !== 'circle') {
45
+ classes.push(`sui-skeleton-radii-${radius}`);
46
+ }
47
+
48
+ if (animation !== 'none' && variant !== 'card') {
49
+ classes.push(`sui-skeleton-anim-${animation}`);
50
+ }
51
+ if (className) {
52
+ classes.push(className);
53
+ }
54
+
55
+ return classes;
56
+ }
57
+ ---
58
+
59
+ <div class:list={getClasses()} style={styles}>
60
+ <slot />
61
+ </div>
@@ -0,0 +1,111 @@
1
+ @keyframes sui-skeleton-slide {
2
+ 0% {
3
+ background-position: 175% 0;
4
+ }
5
+ 100% {
6
+ background-position: -75% 0;
7
+ }
8
+ }
9
+ @keyframes sui-skeleton-pulse {
10
+ 0% {
11
+ background-color: var(--background-step-2);
12
+ }
13
+ 50% {
14
+ background-color: var(--background-step-3);
15
+ }
16
+ 100% {
17
+ background-color: var(--background-step-2);
18
+ }
19
+ }
20
+ .sui-skeleton {
21
+ display: inline-block;
22
+ position: relative;
23
+ width: 100%;
24
+ height: 100%;
25
+ overflow: hidden;
26
+ background-color: var(--background-step-2);
27
+ }
28
+ .sui-skeleton-card {
29
+ display: flex;
30
+ flex-direction: column;
31
+ background-color: var(--background-step-1);
32
+ padding: 1rem;
33
+ gap: 0.5rem;
34
+ height: 100%;
35
+ overflow: hidden;
36
+ }
37
+ .sui-skeleton-card.sui-skeleton-row {
38
+ flex-direction: row;
39
+ }
40
+ .sui-skeleton-justify-start,
41
+ .sui-skeleton-align-start {
42
+ justify-content: flex-start;
43
+ }
44
+ .sui-skeleton-justify-center,
45
+ .sui-skeleton-align-center {
46
+ justify-content: center;
47
+ }
48
+ .sui-skeleton-justify-end,
49
+ .sui-skeleton-align-end {
50
+ justify-content: flex-end;
51
+ }
52
+ .sui-skeleton-items-start,
53
+ .sui-skeleton-justify-start {
54
+ align-items: flex-start;
55
+ }
56
+ .sui-skeleton-items-center,
57
+ .sui-skeleton-justify-center {
58
+ align-items: center;
59
+ }
60
+ .sui-skeleton-items-end,
61
+ .sui-skeleton-justify-end {
62
+ align-items: flex-end;
63
+ }
64
+ .sui-skeleton-text {
65
+ display: inline-block;
66
+ background-color: var(--background-step-2);
67
+ height: 1rem;
68
+ }
69
+ .sui-skeleton-block {
70
+ display: block;
71
+ background-color: var(--background-step-2);
72
+ height: 1rem;
73
+ }
74
+ .sui-skeleton-circle {
75
+ display: inline-block;
76
+ background-color: var(--background-step-2);
77
+ width: 100%;
78
+ height: 100%;
79
+ border-radius: var(--radius-full);
80
+ }
81
+ .sui-skeleton-radii-sm {
82
+ border-radius: var(--radius-sm);
83
+ }
84
+ .sui-skeleton-radii-md {
85
+ border-radius: var(--radius-md);
86
+ }
87
+ .sui-skeleton-radii-lg {
88
+ border-radius: var(--radius-lg);
89
+ }
90
+ .sui-skeleton-anim-none {
91
+ animation: none;
92
+ }
93
+ .sui-skeleton-anim-slide {
94
+ @media (prefers-reduced-motion: no-preference) {
95
+ background-image:
96
+ linear-gradient(
97
+ 90deg,
98
+ var(--background-step-2) 25%,
99
+ var(--background-step-3) 50%,
100
+ var(--background-step-2) 75%);
101
+ background-position: 175% 0;
102
+ background-size: 200% 100%;
103
+ background-repeat: no-repeat;
104
+ animation: sui-skeleton-slide 2s ease-in-out infinite;
105
+ }
106
+ }
107
+ .sui-skeleton-anim-pulse {
108
+ @media (prefers-reduced-motion: no-preference) {
109
+ animation: sui-skeleton-pulse 2s linear infinite;
110
+ }
111
+ }