@kenlo/sds-design-system 0.9.9
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/README.md +129 -0
- package/components/Accordion/Accordion.css +186 -0
- package/components/Accordion/accordion-variants.js +17 -0
- package/components/Accordion/accordion.marko +264 -0
- package/components/AccordionGroup/AccordionGroup.css +35 -0
- package/components/AccordionGroup/accordion-group.marko +17 -0
- package/components/Alert/Alert.css +104 -0
- package/components/Alert/alert-variants.js +35 -0
- package/components/Alert/alert.marko +94 -0
- package/components/AttachmentList/AttachmentList.css +92 -0
- package/components/AttachmentList/attachment-list.marko +51 -0
- package/components/AttachmentList/attachment-row.marko +167 -0
- package/components/Avatar/Avatar.css +110 -0
- package/components/Avatar/avatar-variants.js +12 -0
- package/components/Avatar/avatar.marko +71 -0
- package/components/Badge/Badge.css +381 -0
- package/components/Badge/badge-dismissible.marko +143 -0
- package/components/Badge/badge-variants.js +35 -0
- package/components/Badge/badge.marko +134 -0
- package/components/BottomSheet/BottomSheet.css +300 -0
- package/components/BottomSheet/bottom-sheet.marko +340 -0
- package/components/BulkActionBar/BulkActionBar.css +88 -0
- package/components/BulkActionBar/bulk-action-bar.marko +186 -0
- package/components/Button/Button.css +340 -0
- package/components/Button/button-variants.js +25 -0
- package/components/Button/button.marko +139 -0
- package/components/Card/Card.css +391 -0
- package/components/Card/card-body.marko +5 -0
- package/components/Card/card-collapsible.marko +94 -0
- package/components/Card/card-footer.marko +12 -0
- package/components/Card/card-header.marko +30 -0
- package/components/Card/card-info-pair.marko +17 -0
- package/components/Card/card-info-row.marko +12 -0
- package/components/Card/card-section.marko +14 -0
- package/components/Card/card-variants.js +24 -0
- package/components/Card/card.marko +124 -0
- package/components/Checkbox/Checkbox.css +249 -0
- package/components/Checkbox/checkbox-variants.js +21 -0
- package/components/Checkbox/checkbox.marko +128 -0
- package/components/ColorPicker/ColorPicker.css +253 -0
- package/components/ColorPicker/color-maths.js +104 -0
- package/components/ColorPicker/color-picker.marko +474 -0
- package/components/DatePicker/DatePicker.css +429 -0
- package/components/DatePicker/date-logic.js +225 -0
- package/components/DatePicker/date-picker.marko +492 -0
- package/components/Drawer/Drawer.css +174 -0
- package/components/Drawer/drawer.marko +262 -0
- package/components/Floating/Floating.css +28 -0
- package/components/Floating/floating-position.js +251 -0
- package/components/Floating/floating.marko +291 -0
- package/components/Form/Form.css +15 -0
- package/components/Form/form-context.js +69 -0
- package/components/Form/form.marko +73 -0
- package/components/GradientPicker/GradientPicker.css +179 -0
- package/components/GradientPicker/gradient-picker.marko +398 -0
- package/components/Grid/Grid.css +161 -0
- package/components/Grid/col.marko +36 -0
- package/components/Grid/grid-variants.js +43 -0
- package/components/Grid/grid.marko +25 -0
- package/components/Header/Header.css +457 -0
- package/components/Header/header.marko +523 -0
- package/components/Icon/Icon.css +44 -0
- package/components/Icon/icon-colors.js +31 -0
- package/components/Icon/icon.marko +56 -0
- package/components/IconButton/IconButton.css +85 -0
- package/components/IconButton/icon-button-variants.js +18 -0
- package/components/IconButton/icon-button.marko +103 -0
- package/components/Incrementer/Incrementer.css +94 -0
- package/components/Incrementer/incrementer.marko +70 -0
- package/components/Input/Input.css +547 -0
- package/components/Input/input-countries.js +109 -0
- package/components/Input/input-masks.js +218 -0
- package/components/Input/input-types.js +194 -0
- package/components/Input/input.marko +530 -0
- package/components/Menu/Menu.css +651 -0
- package/components/Menu/menu-variants.js +89 -0
- package/components/Menu/menu.marko +560 -0
- package/components/Modal/Modal.css +160 -0
- package/components/Modal/modal.marko +296 -0
- package/components/Nav/Nav.css +298 -0
- package/components/Nav/nav-variants.js +23 -0
- package/components/Nav/nav.marko +164 -0
- package/components/Notification/Notification.css +78 -0
- package/components/Notification/notification-variants.js +30 -0
- package/components/Notification/notification.marko +55 -0
- package/components/Pagination/Pagination.css +78 -0
- package/components/Pagination/pagination-pages.js +66 -0
- package/components/Pagination/pagination.marko +141 -0
- package/components/Pill/Pill.css +193 -0
- package/components/Pill/pill-variants.js +16 -0
- package/components/Pill/pill.marko +165 -0
- package/components/Popover/Popover.css +121 -0
- package/components/Popover/popover.marko +96 -0
- package/components/Radio/Radio.css +230 -0
- package/components/Radio/radio-variants.js +16 -0
- package/components/Radio/radio.marko +93 -0
- package/components/RadioGroup/RadioGroup.css +32 -0
- package/components/RadioGroup/radio-group.marko +93 -0
- package/components/SearchBar/SearchBar.css +181 -0
- package/components/SearchBar/search-bar-variants.js +24 -0
- package/components/SearchBar/search-bar.marko +157 -0
- package/components/Select/Select.css +427 -0
- package/components/Select/select-options.js +163 -0
- package/components/Select/select.marko +449 -0
- package/components/Separator/Separator.css +22 -0
- package/components/Separator/separator.marko +14 -0
- package/components/Skeleton/Skeleton.css +100 -0
- package/components/Skeleton/skeleton-avatar.marko +28 -0
- package/components/Skeleton/skeleton-button.marko +22 -0
- package/components/Skeleton/skeleton-image.marko +20 -0
- package/components/Skeleton/skeleton-input.marko +22 -0
- package/components/Skeleton/skeleton-variants.js +16 -0
- package/components/Skeleton/skeleton.marko +53 -0
- package/components/Spinner/Spinner.css +173 -0
- package/components/Spinner/spinner-geometry.js +39 -0
- package/components/Spinner/spinner-overlay.marko +97 -0
- package/components/Spinner/spinner.marko +88 -0
- package/components/StatCard/StatCard.css +90 -0
- package/components/StatCard/stat-card-group.marko +5 -0
- package/components/StatCard/stat-card.marko +57 -0
- package/components/Switch/Switch.css +209 -0
- package/components/Switch/switch.marko +99 -0
- package/components/Table/Table.css +270 -0
- package/components/Table/table-cell.marko +28 -0
- package/components/Table/table-header.marko +27 -0
- package/components/Table/table-logic.js +129 -0
- package/components/Table/table.marko +372 -0
- package/components/Text/Text.css +79 -0
- package/components/Text/text-variants.js +25 -0
- package/components/Text/text.marko +48 -0
- package/components/TextEditor/TextEditor.css +78 -0
- package/components/TextEditor/safe-url.js +50 -0
- package/components/TextEditor/text-editor-commands.js +22 -0
- package/components/TextEditor/text-editor.marko +253 -0
- package/components/Toast/Toast.css +88 -0
- package/components/Toast/toast-api.js +19 -0
- package/components/Toast/toast-store.js +118 -0
- package/components/Toast/toast-variants.js +16 -0
- package/components/Toast/toast.marko +93 -0
- package/components/Toast/toaster.marko +70 -0
- package/components/Toggle/Toggle.css +169 -0
- package/components/Toggle/toggle.marko +93 -0
- package/components/Tooltip/Tooltip.css +115 -0
- package/components/Tooltip/tooltip.marko +77 -0
- package/components/Uploader/Uploader.css +180 -0
- package/components/Uploader/crop-geometry.js +161 -0
- package/components/Uploader/uploader.marko +269 -0
- package/foundations/Scrollbar/Scrollbar.css +26 -0
- package/foundations/attrs.js +27 -0
- package/foundations/breakpoints.js +27 -0
- package/foundations/contrast.js +74 -0
- package/foundations/custom-scheme.js +222 -0
- package/foundations/overlay.js +108 -0
- package/foundations/styles.css +284 -0
- package/foundations/text.js +22 -0
- package/foundations/theme-server.js +81 -0
- package/foundations/theme.js +42 -0
- package/foundations/tokens.css +1656 -0
- package/icons/custom/custom-video-horizontal-filled.marko +6 -0
- package/icons/custom/custom-video-horizontal-regular.marko +6 -0
- package/icons/custom/custom-video-tour-3d-filled.marko +7 -0
- package/icons/custom/custom-video-tour-3d-regular.marko +9 -0
- package/icons/custom/custom-video-vertical-filled.marko +6 -0
- package/icons/custom/custom-video-vertical-regular.marko +6 -0
- package/icons/generated/fluent-add-regular.marko +3 -0
- package/icons/generated/fluent-alert-regular.marko +3 -0
- package/icons/generated/fluent-apps-regular.marko +3 -0
- package/icons/generated/fluent-arrow-download-regular.marko +3 -0
- package/icons/generated/fluent-arrow-left-regular.marko +3 -0
- package/icons/generated/fluent-arrow-right-regular.marko +3 -0
- package/icons/generated/fluent-arrow-rotate-clockwise-regular.marko +3 -0
- package/icons/generated/fluent-arrow-rotate-counterclockwise-regular.marko +3 -0
- package/icons/generated/fluent-border-all-regular.marko +3 -0
- package/icons/generated/fluent-calendar-regular.marko +3 -0
- package/icons/generated/fluent-checkmark-circle-filled.marko +3 -0
- package/icons/generated/fluent-checkmark-regular.marko +3 -0
- package/icons/generated/fluent-chevron-down-regular.marko +3 -0
- package/icons/generated/fluent-chevron-left-regular.marko +3 -0
- package/icons/generated/fluent-chevron-right-regular.marko +3 -0
- package/icons/generated/fluent-chevron-up-regular.marko +3 -0
- package/icons/generated/fluent-circle-half-fill-regular.marko +3 -0
- package/icons/generated/fluent-clock-regular.marko +3 -0
- package/icons/generated/fluent-color-regular.marko +3 -0
- package/icons/generated/fluent-crop-regular.marko +3 -0
- package/icons/generated/fluent-dark-theme-regular.marko +3 -0
- package/icons/generated/fluent-delete-regular.marko +3 -0
- package/icons/generated/fluent-desktop-regular.marko +3 -0
- package/icons/generated/fluent-dismiss-circle-filled.marko +3 -0
- package/icons/generated/fluent-dismiss-regular.marko +3 -0
- package/icons/generated/fluent-document-pdf-regular.marko +3 -0
- package/icons/generated/fluent-document-regular.marko +3 -0
- package/icons/generated/fluent-eye-off-regular.marko +3 -0
- package/icons/generated/fluent-eye-regular.marko +3 -0
- package/icons/generated/fluent-eyedropper-regular.marko +3 -0
- package/icons/generated/fluent-grid-regular.marko +3 -0
- package/icons/generated/fluent-history-regular.marko +3 -0
- package/icons/generated/fluent-home-regular.marko +3 -0
- package/icons/generated/fluent-image-regular.marko +3 -0
- package/icons/generated/fluent-info-regular.marko +3 -0
- package/icons/generated/fluent-link-regular.marko +3 -0
- package/icons/generated/fluent-more-horizontal-regular.marko +3 -0
- package/icons/generated/fluent-navigation-regular.marko +3 -0
- package/icons/generated/fluent-paint-brush-regular.marko +3 -0
- package/icons/generated/fluent-question-circle-regular.marko +3 -0
- package/icons/generated/fluent-search-regular.marko +3 -0
- package/icons/generated/fluent-settings-regular.marko +3 -0
- package/icons/generated/fluent-square-hint-regular.marko +3 -0
- package/icons/generated/fluent-subtract-regular.marko +3 -0
- package/icons/generated/fluent-text-align-center-regular.marko +3 -0
- package/icons/generated/fluent-text-align-left-regular.marko +3 -0
- package/icons/generated/fluent-text-align-right-regular.marko +3 -0
- package/icons/generated/fluent-text-bold-regular.marko +3 -0
- package/icons/generated/fluent-text-bullet-list-regular.marko +3 -0
- package/icons/generated/fluent-text-font-regular.marko +3 -0
- package/icons/generated/fluent-text-font-size-regular.marko +3 -0
- package/icons/generated/fluent-text-italic-regular.marko +3 -0
- package/icons/generated/fluent-text-number-list-ltr-regular.marko +3 -0
- package/icons/generated/fluent-video-regular.marko +3 -0
- package/icons/generated/fluent-warning-filled.marko +3 -0
- package/icons/generated/fluent-weather-moon-regular.marko +3 -0
- package/icons/generated/fluent-weather-sunny-regular.marko +3 -0
- package/icons/generated/fluent-zoom-in-regular.marko +3 -0
- package/icons/generated/fluent-zoom-out-regular.marko +3 -0
- package/icons/manifest.json +61 -0
- package/icons/social/social-facebook-color.marko +7 -0
- package/icons/social/social-facebook.marko +6 -0
- package/icons/social/social-google-color.marko +20 -0
- package/icons/social/social-google.marko +6 -0
- package/icons/social/social-instagram-color.marko +16 -0
- package/icons/social/social-instagram.marko +6 -0
- package/icons/social/social-linkedin-color.marko +8 -0
- package/icons/social/social-linkedin.marko +8 -0
- package/icons/social/social-messenger-color.marko +15 -0
- package/icons/social/social-messenger.marko +6 -0
- package/icons/social/social-pinterest-color.marko +6 -0
- package/icons/social/social-pinterest.marko +6 -0
- package/icons/social/social-telegram-color.marko +7 -0
- package/icons/social/social-telegram.marko +6 -0
- package/icons/social/social-whatsapp-color.marko +8 -0
- package/icons/social/social-whatsapp.marko +7 -0
- package/icons/social/social-x-color.marko +7 -0
- package/icons/social/social-x.marko +6 -0
- package/icons/social/social-youtube-color.marko +7 -0
- package/icons/social/social-youtube.marko +6 -0
- package/marko.json +203 -0
- package/package.json +55 -0
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
<!-- sds-select — ported from kdls-design-system v2.5.1 (0f3edf8) components/Select/Select.tsx.
|
|
2
|
+
|
|
3
|
+
The component Rafael's build order was aiming at: it mirrors Input's field, the Menu's
|
|
4
|
+
trigger and the Menu's floating list, which is why those three came first.
|
|
5
|
+
|
|
6
|
+
Divergences, each with its reason:
|
|
7
|
+
|
|
8
|
+
No Floating. KDLS positions the panel by measuring the trigger in JavaScript. On a
|
|
9
|
+
server-rendered page that means the panel only learns where to sit after hydration, and a
|
|
10
|
+
panel that learns late is a panel that jumps. The list sits below the field, which CSS can
|
|
11
|
+
do — the same choice already made for Menu, and the same one that grows into Popover's
|
|
12
|
+
collision detection later.
|
|
13
|
+
|
|
14
|
+
No BottomSheet. On a narrow screen KDLS swaps the dropdown for a sheet sliding up from the
|
|
15
|
+
bottom. That is a real improvement on a phone and it needs BottomSheet, which is not
|
|
16
|
+
ported. Until it is, a phone gets the same dropdown a desktop gets — worse, but honest,
|
|
17
|
+
and recorded rather than quietly dropped.
|
|
18
|
+
|
|
19
|
+
Options arrive four ways and leave as one: plain strings, label/value objects, objects
|
|
20
|
+
under caller-chosen key names, or attribute tags. A select built from an API response and
|
|
21
|
+
one written out by hand are both real, and KDLS supported both.
|
|
22
|
+
|
|
23
|
+
Search ignores case AND accents, so "sao" finds "São Paulo". KDLS compared lowercased
|
|
24
|
+
strings only, which fails on exactly the words a Brazilian visitor types without accents. -->
|
|
25
|
+
|
|
26
|
+
import { normaliseOptions, filterOptions, toArray, toggleValue, isSelected,
|
|
27
|
+
displayLabel, pillLabel, hiddenChoiceCount, widestLabel, selectableValues, selectAllState,
|
|
28
|
+
assertTrigger, assertWidth }
|
|
29
|
+
from "./select-options.js";
|
|
30
|
+
import { assertFeedback as assertInputFeedback } from "../Input/input-types.js";
|
|
31
|
+
import chevronIcon from "../../icons/generated/fluent-chevron-down-regular.marko";
|
|
32
|
+
import clearIcon from "../../icons/generated/fluent-dismiss-regular.marko";
|
|
33
|
+
|
|
34
|
+
class {
|
|
35
|
+
onCreate(input) {
|
|
36
|
+
this.state = {
|
|
37
|
+
open: false,
|
|
38
|
+
query: "",
|
|
39
|
+
value: input.value ?? input.defaultValue ?? (input.multiple ? [] : undefined),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** sds-floating owns opening, closing and closing-on-outside-click; this follows it. */
|
|
44
|
+
setOpen(next) {
|
|
45
|
+
if (this.input.disabled) return;
|
|
46
|
+
this.state.open = next;
|
|
47
|
+
if (!next) this.state.query = "";
|
|
48
|
+
if (next) this.wantsFocus = true;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* An opened list puts the caret where the next keystroke belongs: the search box when
|
|
53
|
+
* there is one, the list itself otherwise, so the arrow keys work without a click.
|
|
54
|
+
*
|
|
55
|
+
* On update rather than on open, because the panel does not exist yet at the moment the
|
|
56
|
+
* state changes.
|
|
57
|
+
*/
|
|
58
|
+
onUpdate() {
|
|
59
|
+
if (!this.wantsFocus || !this.state.open) return;
|
|
60
|
+
this.wantsFocus = false;
|
|
61
|
+
|
|
62
|
+
const panel = this.getEl("panel");
|
|
63
|
+
if (!panel) return;
|
|
64
|
+
|
|
65
|
+
(panel.querySelector("input") ?? panel.querySelector("[role='listbox']"))?.focus();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
get currentValue() {
|
|
69
|
+
return this.input.value !== undefined ? this.input.value : this.state.value;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
get options() {
|
|
73
|
+
return normaliseOptions(this.input);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** The rows the reader can actually see — the same filter the render applies. */
|
|
77
|
+
get visibleOptions() {
|
|
78
|
+
const all = this.options;
|
|
79
|
+
return this.input.filterOption === false ? all : filterOptions(all, this.state.query);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
togglePanel() {
|
|
83
|
+
this.setOpen(!this.state.open);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
closePanel() {
|
|
87
|
+
this.state.open = false;
|
|
88
|
+
this.state.query = "";
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
commit(next) {
|
|
92
|
+
if (this.input.value === undefined) this.state.value = next;
|
|
93
|
+
this.input.onChange?.(next);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
choose(option, event) {
|
|
97
|
+
if (option.disabled) {
|
|
98
|
+
event?.preventDefault();
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (this.input.multiple) {
|
|
103
|
+
this.commit(toggleValue(this.currentValue, option.value));
|
|
104
|
+
// A multiple select stays open: choosing one thing usually means choosing another.
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
this.commit(option.value);
|
|
109
|
+
this.closePanel();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* "Selecionar todos" — todos of what is on screen.
|
|
114
|
+
*
|
|
115
|
+
* It read every option and replaced the whole selection with them, so with a search
|
|
116
|
+
* active it selected rows the reader could not see and threw away the ones they had
|
|
117
|
+
* already chosen outside the filter. Ticking a box should never remove a choice.
|
|
118
|
+
*
|
|
119
|
+
* So: the visible rows are what it acts on, and it merges rather than replaces. Ticking
|
|
120
|
+
* adds them to what is already chosen; unticking removes only them.
|
|
121
|
+
*/
|
|
122
|
+
toggleAll() {
|
|
123
|
+
const visible = selectableValues(this.visibleOptions);
|
|
124
|
+
const current = Array.isArray(this.currentValue) ? this.currentValue : [];
|
|
125
|
+
const { checked } = selectAllState(this.visibleOptions, this.currentValue);
|
|
126
|
+
this.commit(checked
|
|
127
|
+
? current.filter((value) => !visible.includes(value))
|
|
128
|
+
: [...current, ...visible.filter((value) => !current.includes(value))]);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Empty the selection.
|
|
133
|
+
*
|
|
134
|
+
* Three things were wrong here. It took an `event` and called `stopPropagation` on it,
|
|
135
|
+
* while one of its two call sites passes none — clicking the pill's ✕ threw a TypeError
|
|
136
|
+
* instead of clearing. It reported `undefined` for a single select, which is "no opinion"
|
|
137
|
+
* rather than "cleared", and a controlled caller could not tell the two apart; KDLS
|
|
138
|
+
* reports `null`. And it left the panel open, so clearing from the trigger dropped you
|
|
139
|
+
* into a list you had not asked to see.
|
|
140
|
+
*/
|
|
141
|
+
clear(event) {
|
|
142
|
+
event?.stopPropagation();
|
|
143
|
+
this.commit(this.input.multiple ? [] : null);
|
|
144
|
+
this.closePanel();
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
handleSearch(event) {
|
|
148
|
+
this.state.query = event.target.value;
|
|
149
|
+
this.input.onSearch?.(event.target.value);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
handleKey(event) {
|
|
153
|
+
if (event.key === "Escape") {
|
|
154
|
+
event.preventDefault();
|
|
155
|
+
this.closePanel();
|
|
156
|
+
this.getEl("trigger")?.focus();
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
if (!this.state.open && (event.key === "ArrowDown" || event.key === "Enter" || event.key === " ")) {
|
|
160
|
+
event.preventDefault();
|
|
161
|
+
this.state.open = true;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
$ {
|
|
167
|
+
const {
|
|
168
|
+
label,
|
|
169
|
+
placeholder = "Selecione",
|
|
170
|
+
trigger = "pill",
|
|
171
|
+
width = "hug",
|
|
172
|
+
multiple = false,
|
|
173
|
+
searchable = false,
|
|
174
|
+
searchPlaceholder = "Digite para buscar...",
|
|
175
|
+
clearable = true,
|
|
176
|
+
showSelectAll = false,
|
|
177
|
+
selectAllLabel = "Selecionar todos",
|
|
178
|
+
loading = false,
|
|
179
|
+
disabled = false,
|
|
180
|
+
required = false,
|
|
181
|
+
feedback = "none",
|
|
182
|
+
hint,
|
|
183
|
+
emptyMessage = "Nenhuma opção",
|
|
184
|
+
name,
|
|
185
|
+
// `component.id`, not a random string. Marko gives every stateful component an id that
|
|
186
|
+
// is the same on the server and in the browser; `Math.random()` inside the render block
|
|
187
|
+
// produced a different one on each side, so `aria-controls`, `aria-labelledby` and
|
|
188
|
+
// `aria-describedby` all pointed at elements that did not exist after hydration — and
|
|
189
|
+
// it changed again on every re-render.
|
|
190
|
+
id = `sds-select-${component.id}`,
|
|
191
|
+
class: extraClass = "",
|
|
192
|
+
} = input;
|
|
193
|
+
|
|
194
|
+
if (!label) throw new Error("sds-select needs a `label`.");
|
|
195
|
+
assertTrigger(trigger);
|
|
196
|
+
assertWidth(width);
|
|
197
|
+
// The feedback reaches the Input underneath, so it is the Input's vocabulary.
|
|
198
|
+
assertInputFeedback(feedback);
|
|
199
|
+
|
|
200
|
+
const open = state.open;
|
|
201
|
+
const value = component.currentValue;
|
|
202
|
+
const allOptions = component.options;
|
|
203
|
+
// `filterOption: false` hands filtering to the caller, for a search that hits a server.
|
|
204
|
+
const visible = input.filterOption === false ? allOptions : filterOptions(allOptions, state.query);
|
|
205
|
+
|
|
206
|
+
const selectedCount = toArray(value).length;
|
|
207
|
+
const text = displayLabel(allOptions, value, { placeholder });
|
|
208
|
+
// Reads the visible rows, because that is what ticking it will act on. Reading the whole
|
|
209
|
+
// list made the box say "all chosen" while the filtered rows in front of you were not.
|
|
210
|
+
const allState = selectAllState(visible, value);
|
|
211
|
+
|
|
212
|
+
const chipLabel = pillLabel(allOptions, value, { label });
|
|
213
|
+
// Reserved so the trigger never changes width when the choice changes. A control that
|
|
214
|
+
// resizes as you use it makes everything beside it in the toolbar jump.
|
|
215
|
+
const widest = widestLabel(allOptions, trigger === "pill" ? label : placeholder);
|
|
216
|
+
const hiddenChoices = multiple ? hiddenChoiceCount(value) : 0;
|
|
217
|
+
const hasValue = selectedCount > 0;
|
|
218
|
+
|
|
219
|
+
const listboxId = `${id}-listbox`;
|
|
220
|
+
const hintId = hint ? `${id}-hint` : null;
|
|
221
|
+
|
|
222
|
+
const wrapperClass = [
|
|
223
|
+
"sds-select-wrapper",
|
|
224
|
+
`sds-select-wrapper--${width}`,
|
|
225
|
+
open ? "sds-select-wrapper--open" : "",
|
|
226
|
+
disabled ? "sds-select-wrapper--disabled" : "",
|
|
227
|
+
loading ? "sds-select-wrapper--loading" : "",
|
|
228
|
+
trigger === "input" ? "sds-select-wrapper--trigger-input" : "",
|
|
229
|
+
extraClass,
|
|
230
|
+
].filter(Boolean).join(" ");
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
<div key="root" class=wrapperClass>
|
|
234
|
+
<!-- The panel floats; it does not sit in the flow. A dropdown that takes space pushes
|
|
235
|
+
everything below it down when it opens, which is the one thing a dropdown must never
|
|
236
|
+
do — KDLS wraps this in Floating for exactly that reason, and the rule for
|
|
237
|
+
.sds-select-wrapper--fill .sds-floating was carried over here before the component
|
|
238
|
+
existed to match it.
|
|
239
|
+
|
|
240
|
+
Controlled: sds-floating decides when to open and reports it, the Select keeps the
|
|
241
|
+
state because the rest of the component — the query, the keyboard, the value — is
|
|
242
|
+
already hung off it. -->
|
|
243
|
+
<sds-floating
|
|
244
|
+
placement="bottomLeft"
|
|
245
|
+
trigger="click"
|
|
246
|
+
gap=4
|
|
247
|
+
matchTriggerWidth=(trigger === "input")
|
|
248
|
+
open=open
|
|
249
|
+
onOpenChange=(next) => component.setOpen(next)
|
|
250
|
+
>
|
|
251
|
+
<if(trigger === "input")>
|
|
252
|
+
<div class="sds-select-input-wrap">
|
|
253
|
+
<sds-input
|
|
254
|
+
role="combobox"
|
|
255
|
+
aria-haspopup="listbox"
|
|
256
|
+
aria-expanded=(open ? "true" : "false")
|
|
257
|
+
aria-controls=(open ? listboxId : null)
|
|
258
|
+
label=label
|
|
259
|
+
value=text
|
|
260
|
+
placeholder=placeholder
|
|
261
|
+
disabled=disabled
|
|
262
|
+
required=required
|
|
263
|
+
feedback=feedback
|
|
264
|
+
hint=hint
|
|
265
|
+
isLoading=loading
|
|
266
|
+
width=width
|
|
267
|
+
readOnly
|
|
268
|
+
name=name
|
|
269
|
+
id=id
|
|
270
|
+
>
|
|
271
|
+
<@icon-right><fluent-chevron-down-regular/></@icon-right>
|
|
272
|
+
</sds-input>
|
|
273
|
+
</div>
|
|
274
|
+
</if>
|
|
275
|
+
<else-if(trigger === "pill")>
|
|
276
|
+
<!-- A chip, not a field: the filter row of a listing rather than a form. The pill is a
|
|
277
|
+
real <button>, and telling it that it opens a listbox is what stops it announcing
|
|
278
|
+
itself as a pressed switch — KDLS renders this trigger with no aria-haspopup and
|
|
279
|
+
no aria-expanded at all, so a screen reader is told it is a toggle. -->
|
|
280
|
+
<!-- No `active`. A pill has two jobs and they colour differently: as a filter you
|
|
281
|
+
toggle, being on is a state of the data and `--on` fills it with the theme's
|
|
282
|
+
colour; as the trigger of a list, being open or holding a value is a state of
|
|
283
|
+
the control, and filling it would say the filter is applied when it is only the
|
|
284
|
+
thing you press. KDLS passes `active` to a Pill in Pagination — where the pill
|
|
285
|
+
IS the current page — and nowhere else. -->
|
|
286
|
+
<sds-pill
|
|
287
|
+
key="trigger"
|
|
288
|
+
label=chipLabel
|
|
289
|
+
width=width
|
|
290
|
+
iconLeft=input.iconLeft
|
|
291
|
+
iconRight=(hasValue && clearable ? clearIcon : chevronIcon)
|
|
292
|
+
onClickIconRight=(hasValue && clearable ? () => component.clear() : undefined)
|
|
293
|
+
iconRightLabel=(hasValue && clearable ? "Limpar seleção" : undefined)
|
|
294
|
+
showNotification=(hiddenChoices > 0)
|
|
295
|
+
notificationCount=(hiddenChoices > 0 ? hiddenChoices : undefined)
|
|
296
|
+
isLoading=loading
|
|
297
|
+
disabled=disabled
|
|
298
|
+
aria-haspopup="listbox"
|
|
299
|
+
aria-expanded=(open ? "true" : "false")
|
|
300
|
+
aria-controls=(open ? listboxId : null)
|
|
301
|
+
aria-describedby=hintId
|
|
302
|
+
/>
|
|
303
|
+
</else-if>
|
|
304
|
+
<else>
|
|
305
|
+
<button
|
|
306
|
+
key="trigger"
|
|
307
|
+
type="button"
|
|
308
|
+
class=`sds-select__trigger--nav ${open ? "sds-select-wrapper--open" : ""}`.trim()
|
|
309
|
+
disabled=(disabled || null)
|
|
310
|
+
aria-expanded=(open ? "true" : "false")
|
|
311
|
+
aria-haspopup="listbox"
|
|
312
|
+
aria-controls=(open ? listboxId : null)
|
|
313
|
+
aria-describedby=hintId
|
|
314
|
+
aria-labelledby=`${id}-name ${id}-value`
|
|
315
|
+
on-keydown("handleKey")
|
|
316
|
+
>
|
|
317
|
+
<!-- The visible content of this trigger is the *value*, not the name: a screen
|
|
318
|
+
reader met "20, collapsed button" and had no way to know what 20 was. An
|
|
319
|
+
aria-label would fix the name and lose the value, so both are pointed at. -->
|
|
320
|
+
<span id=`${id}-name` class="sds-visually-hidden">${label}</span>
|
|
321
|
+
<span id=`${id}-value` class="sds-select__trigger-nav-label" data-widest=widest>${text}</span>
|
|
322
|
+
<span class="sds-select__trigger-nav-chevron" aria-hidden="true">
|
|
323
|
+
<sds-icon size="sm"><fluent-chevron-down-regular/></sds-icon>
|
|
324
|
+
</span>
|
|
325
|
+
</button>
|
|
326
|
+
</else>
|
|
327
|
+
|
|
328
|
+
<!-- Not for the pill: that trigger carries its own clear affordance in its right icon,
|
|
329
|
+
and two controls with the same name is a screen reader reading "Limpar seleção,
|
|
330
|
+
button" twice for one action. -->
|
|
331
|
+
<if(trigger !== "pill" && clearable && selectedCount > 0 && !disabled)>
|
|
332
|
+
<button type="button" class="sds-select__clear" aria-label="Limpar seleção" on-click("clear")>
|
|
333
|
+
<sds-icon size="sm"><fluent-dismiss-regular/></sds-icon>
|
|
334
|
+
</button>
|
|
335
|
+
</if>
|
|
336
|
+
|
|
337
|
+
<@content>
|
|
338
|
+
<div key="panel" class="sds-select__menu" on-keydown("handleKey")>
|
|
339
|
+
<if(searchable)>
|
|
340
|
+
<div class="sds-select__header">
|
|
341
|
+
<div class="sds-select__search-field">
|
|
342
|
+
<span class="sds-select__search-icon" aria-hidden="true">
|
|
343
|
+
<sds-icon size="sm"><fluent-search-regular/></sds-icon>
|
|
344
|
+
</span>
|
|
345
|
+
<input
|
|
346
|
+
class="sds-select__search-input"
|
|
347
|
+
type="search"
|
|
348
|
+
value=state.query
|
|
349
|
+
placeholder=searchPlaceholder
|
|
350
|
+
aria-label=searchPlaceholder
|
|
351
|
+
aria-controls=listboxId
|
|
352
|
+
on-input("handleSearch")
|
|
353
|
+
/>
|
|
354
|
+
</div>
|
|
355
|
+
</div>
|
|
356
|
+
</if>
|
|
357
|
+
|
|
358
|
+
<if(multiple && showSelectAll && visible.length)>
|
|
359
|
+
// The listener is on the wrapper, not on the checkbox. `sds-checkbox` is markup —
|
|
360
|
+
// it does not hydrate and emits nothing, so `on-click` on the tag itself was
|
|
361
|
+
// waiting for a component event that is never sent: the box ticked natively and
|
|
362
|
+
// nothing happened. The native `change` bubbles, so the wrapper hears it.
|
|
363
|
+
<div class="sds-select__header" on-change("toggleAll")>
|
|
364
|
+
<sds-checkbox
|
|
365
|
+
label=selectAllLabel
|
|
366
|
+
size="sm"
|
|
367
|
+
checked=allState.checked
|
|
368
|
+
indeterminate=allState.indeterminate
|
|
369
|
+
/>
|
|
370
|
+
</div>
|
|
371
|
+
</if>
|
|
372
|
+
|
|
373
|
+
<ul
|
|
374
|
+
class="sds-select__list sds-scroll sds-scroll--thin"
|
|
375
|
+
id=listboxId
|
|
376
|
+
role="listbox"
|
|
377
|
+
aria-label=label
|
|
378
|
+
aria-multiselectable=(multiple ? "true" : null)
|
|
379
|
+
// Focusable, not tabbable: an opened list with no search box takes the caret so
|
|
380
|
+
// the arrow keys work without a click, and the tab order is unchanged.
|
|
381
|
+
tabindex="-1"
|
|
382
|
+
>
|
|
383
|
+
<if(loading)>
|
|
384
|
+
<li class="sds-select__loading"><sds-spinner size="sm" label="Carregando opções"/></li>
|
|
385
|
+
</if>
|
|
386
|
+
<else-if(!visible.length)>
|
|
387
|
+
<li class="sds-select__empty">${emptyMessage}</li>
|
|
388
|
+
</else-if>
|
|
389
|
+
|
|
390
|
+
<for|option| of=(loading ? [] : visible)>
|
|
391
|
+
<if(option.section)>
|
|
392
|
+
<li class="sds-select__section-header" role="presentation">${option.label}</li>
|
|
393
|
+
</if>
|
|
394
|
+
<else>
|
|
395
|
+
<li role="none">
|
|
396
|
+
<button
|
|
397
|
+
type="button"
|
|
398
|
+
class=`sds-select__option ${isSelected(value, option.value) ? "sds-select__option--selected" : ""}`.trim()
|
|
399
|
+
role="option"
|
|
400
|
+
aria-selected=(isSelected(value, option.value) ? "true" : "false")
|
|
401
|
+
disabled=(option.disabled || null)
|
|
402
|
+
on-click("choose", option)
|
|
403
|
+
>
|
|
404
|
+
<if(multiple)>
|
|
405
|
+
<sds-checkbox size="sm" checked=isSelected(value, option.value)/>
|
|
406
|
+
</if>
|
|
407
|
+
<if(option.icon)>
|
|
408
|
+
<span class="sds-select__option-icon" aria-hidden="true">
|
|
409
|
+
<!-- `iconProps` because Marko has no pre-instantiated element. KDLS
|
|
410
|
+
passes `icon={<RadiusCornerIcon r={r} />}` — a React element that
|
|
411
|
+
already carries its props — and the nearest thing here is the
|
|
412
|
+
template plus the props to render it with. Same shape of
|
|
413
|
+
translation as children becoming attribute tags. -->
|
|
414
|
+
<sds-icon size="sm">
|
|
415
|
+
<${(option.icon && option.icon.renderBody) || option.icon} ...(option.iconProps ?? {})/>
|
|
416
|
+
</sds-icon>
|
|
417
|
+
</span>
|
|
418
|
+
</if>
|
|
419
|
+
<span>
|
|
420
|
+
<if(option.renderBody)><${option.renderBody}/></if>
|
|
421
|
+
<else>${option.label}</else>
|
|
422
|
+
</span>
|
|
423
|
+
</button>
|
|
424
|
+
</li>
|
|
425
|
+
</else>
|
|
426
|
+
</for>
|
|
427
|
+
</ul>
|
|
428
|
+
</div>
|
|
429
|
+
</@content>
|
|
430
|
+
</sds-floating>
|
|
431
|
+
|
|
432
|
+
<if(hint && trigger !== "input")>
|
|
433
|
+
<!-- The pill and the nav button point `aria-describedby` at this. They were pointing at
|
|
434
|
+
it before it existed: only the input trigger renders a hint, through sds-input, so
|
|
435
|
+
on the other two the reference dangled and the reader was told nothing. A dangling
|
|
436
|
+
reference is worse than none — it says there is a description and then has none. -->
|
|
437
|
+
<sds-text as="span" id=hintId variant="labelRegular" class="sds-select__hint">
|
|
438
|
+
${hint}
|
|
439
|
+
</sds-text>
|
|
440
|
+
</if>
|
|
441
|
+
|
|
442
|
+
<if(name)>
|
|
443
|
+
<!-- The value a form submits. The trigger is a button or a read-only field, neither of
|
|
444
|
+
which posts anything on its own. -->
|
|
445
|
+
<for|chosen| of=toArray(value)>
|
|
446
|
+
<input type="hidden" name=(multiple ? `${name}[]` : name) value=chosen/>
|
|
447
|
+
</for>
|
|
448
|
+
</if>
|
|
449
|
+
</div>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/* SDS Separator.
|
|
2
|
+
Ported from kdls-design-system v2.5.1 (0f3edf8) components/Separator/Separator.css
|
|
3
|
+
by scripts/port-css.mjs. Divergences beyond the mechanical rename are noted inline.
|
|
4
|
+
*//* ============================================================
|
|
5
|
+
SDS Separator
|
|
6
|
+
Orientations: horizontal | vertical
|
|
7
|
+
============================================================ */
|
|
8
|
+
|
|
9
|
+
.sds-separator {
|
|
10
|
+
border: none;
|
|
11
|
+
margin: 0;
|
|
12
|
+
background-color: var(--color-neutral-20);
|
|
13
|
+
flex-shrink: 0;
|
|
14
|
+
}
|
|
15
|
+
.sds-separator--horizontal {
|
|
16
|
+
width: 100%;
|
|
17
|
+
height: 1px;
|
|
18
|
+
}
|
|
19
|
+
.sds-separator--vertical {
|
|
20
|
+
width: 1px;
|
|
21
|
+
height: 100%;
|
|
22
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!-- sds-separator — ported from kdls-design-system v2.5.1 (0f3edf8)
|
|
2
|
+
components/Separator/Separator.tsx.
|
|
3
|
+
|
|
4
|
+
An <hr>, which already means "thematic break" to a screen reader. No state, no behaviour. -->
|
|
5
|
+
|
|
6
|
+
$ {
|
|
7
|
+
const { orientation = "horizontal", class: extraClass = "" } = input;
|
|
8
|
+
if (orientation !== "horizontal" && orientation !== "vertical") {
|
|
9
|
+
throw new Error(`Unknown separator orientation: "${orientation}". Allowed: horizontal, vertical`);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
<hr class=`sds-separator sds-separator--${orientation} ${extraClass}`.trim()
|
|
14
|
+
aria-orientation=orientation/>
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/* SDS Skeleton.
|
|
2
|
+
Ported from kdls-design-system v2.5.1 (0f3edf8) components/Skeleton/Skeleton.css
|
|
3
|
+
by scripts/port-css.mjs. Divergences beyond the mechanical rename are noted inline.
|
|
4
|
+
*//* ── Skeleton ──────────────────────────────────────────────────────────
|
|
5
|
+
Loading placeholder — title/paragraph block, with optional avatar,
|
|
6
|
+
plus the standalone blocks Skeleton.Avatar/Button/Input/Image.
|
|
7
|
+
BEM root: .sds-skeleton
|
|
8
|
+
─────────────────────────────────────────────────────────────────── */
|
|
9
|
+
|
|
10
|
+
.sds-skeleton {
|
|
11
|
+
display: flex;
|
|
12
|
+
align-items: flex-start;
|
|
13
|
+
gap: var(--spacing-4);
|
|
14
|
+
}
|
|
15
|
+
.sds-skeleton__content {
|
|
16
|
+
flex: 1;
|
|
17
|
+
min-width: 0;
|
|
18
|
+
display: flex;
|
|
19
|
+
flex-direction: column;
|
|
20
|
+
gap: var(--spacing-2);
|
|
21
|
+
}
|
|
22
|
+
/* ── Blocks — base color, overridden by --active (see end of file) ── */
|
|
23
|
+
.sds-skeleton__avatar,
|
|
24
|
+
.sds-skeleton__button,
|
|
25
|
+
.sds-skeleton__input,
|
|
26
|
+
.sds-skeleton__image,
|
|
27
|
+
.sds-skeleton__line {
|
|
28
|
+
background: var(--color-neutral-10);
|
|
29
|
+
}
|
|
30
|
+
/* ── Avatar — always round, no shape prop ── */
|
|
31
|
+
.sds-skeleton__avatar {
|
|
32
|
+
border-radius: var(--radius-full);
|
|
33
|
+
flex-shrink: 0;
|
|
34
|
+
}
|
|
35
|
+
.sds-skeleton__avatar--sm { width: var(--spacing-10); height: var(--spacing-10); }
|
|
36
|
+
.sds-skeleton__avatar--md { width: var(--spacing-12); height: var(--spacing-12); }
|
|
37
|
+
.sds-skeleton__avatar--lg { width: var(--spacing-14); height: var(--spacing-14); }
|
|
38
|
+
/* ── Title/paragraph lines ── */
|
|
39
|
+
.sds-skeleton__line {
|
|
40
|
+
width: 100%;
|
|
41
|
+
height: var(--spacing-4);
|
|
42
|
+
border-radius: var(--radius-sm);
|
|
43
|
+
}
|
|
44
|
+
.sds-skeleton__line--title {
|
|
45
|
+
width: 40%;
|
|
46
|
+
height: var(--spacing-5);
|
|
47
|
+
}
|
|
48
|
+
.sds-skeleton__line--last {
|
|
49
|
+
width: 60%;
|
|
50
|
+
}
|
|
51
|
+
/* ── Button — fixed width (there's no label to measure), project radius.
|
|
52
|
+
Heights match the real Button: sm = variant="pill" (40px), md = default (48px),
|
|
53
|
+
lg = variant="lg" (56px). 120px is hardcoded — the spacing scale stops at
|
|
54
|
+
--spacing-16 (64px), no token reaches that far. ── */
|
|
55
|
+
.sds-skeleton__button {
|
|
56
|
+
width: 120px;
|
|
57
|
+
border-radius: var(--radius-md);
|
|
58
|
+
}
|
|
59
|
+
.sds-skeleton__button--sm { height: var(--spacing-10); }
|
|
60
|
+
.sds-skeleton__button--md { height: var(--spacing-12); }
|
|
61
|
+
.sds-skeleton__button--lg { height: var(--spacing-14); }
|
|
62
|
+
/* ── Input — fluid width (form field), project radius.
|
|
63
|
+
lg (56px) is the default — matches the real Input's fixed height, which has no
|
|
64
|
+
size prop. sm/md are available to represent more compact fields. ── */
|
|
65
|
+
.sds-skeleton__input {
|
|
66
|
+
width: 100%;
|
|
67
|
+
border-radius: var(--radius-md);
|
|
68
|
+
}
|
|
69
|
+
.sds-skeleton__input--sm { height: var(--spacing-10); }
|
|
70
|
+
.sds-skeleton__input--md { height: var(--spacing-12); }
|
|
71
|
+
.sds-skeleton__input--lg { height: var(--spacing-14); }
|
|
72
|
+
/* ── Image — fixed block with a centered icon, never round, project radius.
|
|
73
|
+
fill=true swaps the fixed size for 100%/100% — use when the real container
|
|
74
|
+
(e.g. a Card's cover) has a variable size. ── */
|
|
75
|
+
.sds-skeleton__image {
|
|
76
|
+
width: var(--spacing-16);
|
|
77
|
+
height: var(--spacing-16);
|
|
78
|
+
flex-shrink: 0;
|
|
79
|
+
border-radius: var(--radius-md);
|
|
80
|
+
display: flex;
|
|
81
|
+
align-items: center;
|
|
82
|
+
justify-content: center;
|
|
83
|
+
color: var(--color-neutral-30);
|
|
84
|
+
}
|
|
85
|
+
.sds-skeleton__image--fill {
|
|
86
|
+
width: 100%;
|
|
87
|
+
height: 100%;
|
|
88
|
+
}
|
|
89
|
+
/* ── active — shimmer sweeping across the block. Same specificity as the base color;
|
|
90
|
+
needs to come later in the file to win the cascade (same weight, same
|
|
91
|
+
source order). ── */
|
|
92
|
+
@keyframes sds-skeleton-shimmer {
|
|
93
|
+
0% { background-position: 100% 50%; }
|
|
94
|
+
100% { background-position: 0 50%; }
|
|
95
|
+
}
|
|
96
|
+
.sds-skeleton--active {
|
|
97
|
+
background: linear-gradient(90deg, var(--color-neutral-10) 25%, var(--color-neutral-20) 37%, var(--color-neutral-10) 63%);
|
|
98
|
+
background-size: 400% 100%;
|
|
99
|
+
animation: sds-skeleton-shimmer 1.4s ease infinite;
|
|
100
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<!-- sds-skeleton-avatar — ported from kdls-design-system v2.5.1 (0f3edf8)
|
|
2
|
+
components/Skeleton/Skeleton.tsx, where this was Skeleton.Avatar.
|
|
3
|
+
|
|
4
|
+
A sibling tag rather than a nested one: it shares nothing with sds-skeleton but a
|
|
5
|
+
stylesheet, so nesting would be a namespace, not a relationship.
|
|
6
|
+
|
|
7
|
+
The circle already existed here — `sds-skeleton` draws one when `avatar` is set, and the
|
|
8
|
+
stylesheet has carried `.sds-skeleton__avatar` since the port. What was missing was the
|
|
9
|
+
standalone tag, so a caller who wanted the circle beside something that is not a
|
|
10
|
+
title-and-paragraph block had no way to ask for it. Always round, like the real Avatar,
|
|
11
|
+
and the only one of these with no shape of its own to choose. -->
|
|
12
|
+
|
|
13
|
+
import { assertSkeletonSize } from "./skeleton-variants.js";
|
|
14
|
+
|
|
15
|
+
$ {
|
|
16
|
+
const { size = "md", active = true, class: extraClass = "" } = input;
|
|
17
|
+
assertSkeletonSize(size);
|
|
18
|
+
|
|
19
|
+
const classes = [
|
|
20
|
+
"sds-skeleton",
|
|
21
|
+
active ? "sds-skeleton--active" : "",
|
|
22
|
+
"sds-skeleton__avatar",
|
|
23
|
+
`sds-skeleton__avatar--${size}`,
|
|
24
|
+
extraClass,
|
|
25
|
+
].filter(Boolean).join(" ");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
<span class=classes aria-hidden="true"/>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<!-- sds-skeleton-button — ported from kdls-design-system v2.5.1 (0f3edf8)
|
|
2
|
+
components/Skeleton/Skeleton.tsx, where this was Skeleton.Button.
|
|
3
|
+
|
|
4
|
+
A sibling tag rather than a nested one: it shares nothing with sds-skeleton but a
|
|
5
|
+
stylesheet, so nesting would be a namespace, not a relationship. -->
|
|
6
|
+
|
|
7
|
+
import { assertSkeletonSize } from "./skeleton-variants.js";
|
|
8
|
+
|
|
9
|
+
$ {
|
|
10
|
+
const { size = "md", active = true, class: extraClass = "" } = input;
|
|
11
|
+
assertSkeletonSize(size);
|
|
12
|
+
|
|
13
|
+
const classes = [
|
|
14
|
+
"sds-skeleton",
|
|
15
|
+
active ? "sds-skeleton--active" : "",
|
|
16
|
+
"sds-skeleton__button",
|
|
17
|
+
`sds-skeleton__button--${size}`,
|
|
18
|
+
extraClass,
|
|
19
|
+
].filter(Boolean).join(" ");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
<span class=classes aria-hidden="true"/>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<!-- sds-skeleton-image — ported from kdls-design-system v2.5.1 (0f3edf8)
|
|
2
|
+
components/Skeleton/Skeleton.tsx, where this was Skeleton.Image.
|
|
3
|
+
|
|
4
|
+
A sibling tag rather than a nested one: it shares nothing with sds-skeleton but a
|
|
5
|
+
stylesheet, so nesting would be a namespace, not a relationship. -->
|
|
6
|
+
|
|
7
|
+
$ {
|
|
8
|
+
// No `size`: an image skeleton is either its fixed block or the whole of its parent.
|
|
9
|
+
const { fill = false, active = true, class: extraClass = "" } = input;
|
|
10
|
+
|
|
11
|
+
const classes = [
|
|
12
|
+
"sds-skeleton",
|
|
13
|
+
active ? "sds-skeleton--active" : "",
|
|
14
|
+
"sds-skeleton__image",
|
|
15
|
+
fill ? "sds-skeleton__image--fill" : "",
|
|
16
|
+
extraClass,
|
|
17
|
+
].filter(Boolean).join(" ");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
<span class=classes aria-hidden="true"/>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<!-- sds-skeleton-input — ported from kdls-design-system v2.5.1 (0f3edf8)
|
|
2
|
+
components/Skeleton/Skeleton.tsx, where this was Skeleton.Input.
|
|
3
|
+
|
|
4
|
+
A sibling tag rather than a nested one: it shares nothing with sds-skeleton but a
|
|
5
|
+
stylesheet, so nesting would be a namespace, not a relationship. -->
|
|
6
|
+
|
|
7
|
+
import { assertSkeletonSize } from "./skeleton-variants.js";
|
|
8
|
+
|
|
9
|
+
$ {
|
|
10
|
+
const { size = "md", active = true, class: extraClass = "" } = input;
|
|
11
|
+
assertSkeletonSize(size);
|
|
12
|
+
|
|
13
|
+
const classes = [
|
|
14
|
+
"sds-skeleton",
|
|
15
|
+
active ? "sds-skeleton--active" : "",
|
|
16
|
+
"sds-skeleton__input",
|
|
17
|
+
`sds-skeleton__input--${size}`,
|
|
18
|
+
extraClass,
|
|
19
|
+
].filter(Boolean).join(" ");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
<span class=classes aria-hidden="true"/>
|