@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,71 @@
|
|
|
1
|
+
<!-- sds-avatar — ported from kdls-design-system v2.5.1 (0f3edf8) components/Avatar/Avatar.tsx.
|
|
2
|
+
|
|
3
|
+
No state, which KDLS needed for one thing: an onError handler that swapped a broken image
|
|
4
|
+
for the initials. The initials are rendered underneath the image here and show through
|
|
5
|
+
when it paints nothing — see Avatar.css. That removes a serialised component per avatar,
|
|
6
|
+
and an avatar list is exactly where they repeat.
|
|
7
|
+
|
|
8
|
+
It is a <button> when it has a click handler and a plain <div> otherwise, rather than a
|
|
9
|
+
div wearing role="button" with a hand-written key handler as KDLS did. -->
|
|
10
|
+
|
|
11
|
+
import { assertAvatarSize } from "./avatar-variants.js";
|
|
12
|
+
|
|
13
|
+
$ {
|
|
14
|
+
const {
|
|
15
|
+
src,
|
|
16
|
+
initials,
|
|
17
|
+
alt,
|
|
18
|
+
size = "sm",
|
|
19
|
+
onClick,
|
|
20
|
+
class: extraClass = "",
|
|
21
|
+
} = input;
|
|
22
|
+
|
|
23
|
+
assertAvatarSize(size);
|
|
24
|
+
|
|
25
|
+
const interactive = Boolean(onClick);
|
|
26
|
+
const label = alt || initials || "Avatar";
|
|
27
|
+
|
|
28
|
+
const classes = [
|
|
29
|
+
"sds-avatar",
|
|
30
|
+
`sds-avatar--${size}`,
|
|
31
|
+
interactive ? "sds-avatar--clickable" : "",
|
|
32
|
+
extraClass,
|
|
33
|
+
].filter(Boolean).join(" ");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
<macro|{ }| name="content">
|
|
37
|
+
<!-- Underneath. Visible only where the image is not. -->
|
|
38
|
+
<span class="sds-avatar__placeholder" aria-hidden=(src ? "true" : null)>
|
|
39
|
+
${initials ?? "?"}
|
|
40
|
+
</span>
|
|
41
|
+
<if(src)>
|
|
42
|
+
<!-- `onerror` as an attribute, not a handler this component hydrates for. KDLS keeps a
|
|
43
|
+
`useState(imgFailed)` for exactly this, and one serialised component per avatar is
|
|
44
|
+
the cost an avatar list pays fifty times over. The attribute costs nothing and
|
|
45
|
+
does the same job: the broken image leaves the box and the initials underneath are
|
|
46
|
+
what remains. Under a strict CSP that forbids inline handlers it simply does not
|
|
47
|
+
run, which is the behaviour this had before — it degrades, it does not break.
|
|
48
|
+
|
|
49
|
+
The name: with a photo and no `alt`, nothing in here had an accessible name at all
|
|
50
|
+
— the image was silent, the placeholder hidden, and the root dropped its label. An
|
|
51
|
+
`alt` the caller wrote wins, including an empty one, which is how you say
|
|
52
|
+
"decorative". Only the absence of the prop falls back to the initials. -->
|
|
53
|
+
<img
|
|
54
|
+
class="sds-avatar__image"
|
|
55
|
+
src=src
|
|
56
|
+
alt=(alt === undefined ? (initials ?? "Avatar") : alt)
|
|
57
|
+
onerror="this.remove()"
|
|
58
|
+
/>
|
|
59
|
+
</if>
|
|
60
|
+
</macro>
|
|
61
|
+
|
|
62
|
+
<if(interactive)>
|
|
63
|
+
<button type="button" class=classes aria-label=label on-click(onClick)>
|
|
64
|
+
<content/>
|
|
65
|
+
</button>
|
|
66
|
+
</if>
|
|
67
|
+
<else>
|
|
68
|
+
<div class=classes role=(src ? null : "img") aria-label=(src ? null : label)>
|
|
69
|
+
<content/>
|
|
70
|
+
</div>
|
|
71
|
+
</else>
|
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
/* SDS Badge.
|
|
2
|
+
Ported from kdls-design-system v2.5.1 (0f3edf8) components/Badge/Badge.css.
|
|
3
|
+
|
|
4
|
+
Divergences:
|
|
5
|
+
- the primary, secondary, tertiary and lya colour blocks are removed with the
|
|
6
|
+
families they read. Fourteen colours remain.
|
|
7
|
+
- text and outline painted from the primary slot read --color-primary-N-solid.
|
|
8
|
+
`background` accepts a gradient; `color` and `box-shadow` do not, and
|
|
9
|
+
applyCustomGradient can put a gradient in that slot. */
|
|
10
|
+
|
|
11
|
+
/* ============================================================
|
|
12
|
+
SDS Badge
|
|
13
|
+
Sizes: 24px | 20px
|
|
14
|
+
Types: neutral | scheme-color |
|
|
15
|
+
success | alert | error |
|
|
16
|
+
leaf | sand | wood | clay | flower | dusk | sky | sea | lake
|
|
17
|
+
Appearance: outline | tone-10..tone-50 (progressive tint, tone-50 = full color)
|
|
18
|
+
Icons: left (optional) | right/dismiss (optional)
|
|
19
|
+
============================================================ */
|
|
20
|
+
|
|
21
|
+
.sds-badge {
|
|
22
|
+
display: inline-flex;
|
|
23
|
+
align-items: center;
|
|
24
|
+
border-radius: var(--radius-xs);
|
|
25
|
+
/* inset instead of border — the 1px outline never adds to the box size,
|
|
26
|
+
so tone (transparent) and outline (currentColor) close at the same height */
|
|
27
|
+
box-shadow: inset 0 0 0 1px transparent;
|
|
28
|
+
font-family: var(--font-family-base);
|
|
29
|
+
font-weight: var(--font-weight-medium);
|
|
30
|
+
width: fit-content;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/* width="fill" — only when explicitly requested.
|
|
34
|
+
Without iconRight: iconLeft+label are centered in the pill.
|
|
35
|
+
With iconRight: iconLeft+label go to the left (.sds-badge__content) and the
|
|
36
|
+
clickable iconRight goes to the right (.sds-badge__icon-right-wrap), one at each
|
|
37
|
+
end of the pill. */
|
|
38
|
+
.sds-badge--fill {
|
|
39
|
+
width: 100%;
|
|
40
|
+
justify-content: center;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.sds-badge--fill.sds-badge--has-icon-right {
|
|
44
|
+
justify-content: space-between;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.sds-badge__content {
|
|
48
|
+
display: inline-flex;
|
|
49
|
+
align-items: center;
|
|
50
|
+
gap: var(--spacing-1);
|
|
51
|
+
padding: var(--spacing-1) var(--spacing-2);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/* When there's an iconRight, the content's padding-right shrinks — the icon's
|
|
55
|
+
wrap already gives its own breathing room to the pill's edge (see
|
|
56
|
+
.sds-badge__icon-right-wrap), so it doesn't need both added together */
|
|
57
|
+
.sds-badge__content--has-icon-right {
|
|
58
|
+
padding-right: var(--spacing-0-5);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/* ── Sizes — spacing tweaks at lg ──────────────────────────────────────────────
|
|
62
|
+
The label's typography used to be redeclared here, once per badge size. Those three
|
|
63
|
+
rules were the Text scale written out by hand — label-sm at sm, body-sm at md, body-md
|
|
64
|
+
at lg — so the label now renders through <sds-text> with the matching variant and the
|
|
65
|
+
duplication is gone. It passes color="inherit" so the badge keeps deciding the colour,
|
|
66
|
+
which is where the contrast tokens live. */
|
|
67
|
+
|
|
68
|
+
.sds-badge--lg .sds-badge__content {
|
|
69
|
+
gap: var(--spacing-2);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.sds-badge--lg .sds-badge__content--has-icon-right {
|
|
73
|
+
padding-right: var(--spacing-1);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/* ── Icons ─────────────────────────────────── */
|
|
77
|
+
.sds-badge__icon {
|
|
78
|
+
display: inline-flex;
|
|
79
|
+
align-items: center;
|
|
80
|
+
font-size: 16px;
|
|
81
|
+
flex-shrink: 0;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.sds-badge__icon-right-wrap {
|
|
85
|
+
display: inline-flex;
|
|
86
|
+
align-items: center;
|
|
87
|
+
padding: var(--spacing-0-5) 6px var(--spacing-0-5) 0;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.sds-badge--md .sds-badge__icon-right-wrap {
|
|
91
|
+
padding-top: var(--spacing-1);
|
|
92
|
+
padding-bottom: var(--spacing-1);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.sds-badge--lg .sds-badge__icon-right-wrap {
|
|
96
|
+
padding-top: var(--spacing-1);
|
|
97
|
+
padding-right: var(--spacing-1);
|
|
98
|
+
padding-bottom: var(--spacing-1);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.sds-badge__icon--right--clickable {
|
|
102
|
+
cursor: pointer;
|
|
103
|
+
border-radius: min(4px, var(--radius-xs));
|
|
104
|
+
transition: background-color 120ms ease;
|
|
105
|
+
padding: var(--spacing-0-5);
|
|
106
|
+
}
|
|
107
|
+
.sds-badge__icon--right--clickable:hover { background-color: var(--color-opacity-05); }
|
|
108
|
+
.sds-badge__icon--right--clickable:active { background-color: var(--color-opacity-10); }
|
|
109
|
+
|
|
110
|
+
.sds-badge--lg .sds-badge__icon--right--clickable {
|
|
111
|
+
padding: var(--spacing-1);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/* Loading spinner in place of the icon — the Spinner's smallest size (sm) is
|
|
115
|
+
24px, larger than the badge icon (16px). The internal SVG comes with fixed width/height
|
|
116
|
+
as an HTML attribute (24px, computed in JS from size="sm"), not
|
|
117
|
+
just via the CSS variable — so it needs to override the <svg> itself
|
|
118
|
+
(.sds-spinner__svg), not just the outer <div>, otherwise the graphic keeps
|
|
119
|
+
rendering at a true 24px inside a smaller box. */
|
|
120
|
+
.sds-badge__icon--right--clickable .sds-spinner {
|
|
121
|
+
--sds-spinner-size: 16px;
|
|
122
|
+
}
|
|
123
|
+
.sds-badge__icon--right--clickable .sds-spinner__svg {
|
|
124
|
+
width: 16px;
|
|
125
|
+
height: 16px;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.sds-badge__icon--right--disabled {
|
|
129
|
+
cursor: not-allowed;
|
|
130
|
+
}
|
|
131
|
+
.sds-badge__icon--right--disabled:hover,
|
|
132
|
+
.sds-badge__icon--right--disabled:active {
|
|
133
|
+
background-color: transparent;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.sds-badge__icon--right--loading {
|
|
137
|
+
cursor: wait;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/* ══════════════════════════════════════════════
|
|
141
|
+
COLOR × APPEARANCE MATRIX
|
|
142
|
+
outline = transparent bg, border tied to the color
|
|
143
|
+
tone-10 = lightest tint
|
|
144
|
+
tone-20 = light tint
|
|
145
|
+
tone-30 = medium tint (default)
|
|
146
|
+
tone-40 = medium-strong tint
|
|
147
|
+
tone-50 = full color / full-saturation
|
|
148
|
+
══════════════════════════════════════════════ */
|
|
149
|
+
|
|
150
|
+
/* ── NEUTRAL ──────────────────────────────── */
|
|
151
|
+
.sds-badge--neutral.sds-badge--outline { background: transparent; color: var(--color-neutral-60); }
|
|
152
|
+
.sds-badge--neutral.sds-badge--tone-10 { background: var(--color-neutral-10); color: var(--color-neutral-10-on); }
|
|
153
|
+
.sds-badge--neutral.sds-badge--tone-20 { background: var(--color-neutral-20); color: var(--color-neutral-20-on); }
|
|
154
|
+
.sds-badge--neutral.sds-badge--tone-30 { background: var(--color-neutral-30); color: var(--color-neutral-30-on); }
|
|
155
|
+
.sds-badge--neutral.sds-badge--tone-40 { background: var(--color-neutral-40); color: var(--color-neutral-40-on); }
|
|
156
|
+
.sds-badge--neutral.sds-badge--tone-50 { background: var(--color-neutral-50); color: var(--color-neutral-50-on); }
|
|
157
|
+
|
|
158
|
+
/* ── SCHEME-COLOR — follows data-color-scheme; tone-50 label auto-adjusts for
|
|
159
|
+
contrast via --color-on-primary, same mechanism as Button/Toggle/Pill ── */
|
|
160
|
+
/* ── scheme-color outline — the gradient-capable ring ─────────────────────────
|
|
161
|
+
The primary slot may hold a gradient (applyCustomGradient), and box-shadow cannot
|
|
162
|
+
paint one. KDLS solved this for its Lya variant with a masked ::before, and that
|
|
163
|
+
technique moves here, to the one variant that can now receive a gradient.
|
|
164
|
+
|
|
165
|
+
No real `border`: a fit-content box grows by the border's width regardless of
|
|
166
|
+
box-sizing, which would make outline the only appearance measuring 2px taller than
|
|
167
|
+
its siblings. The ring is an absolutely positioned ::before instead, masked down to
|
|
168
|
+
its own 1px padding, so it paints over the box without taking layout space.
|
|
169
|
+
|
|
170
|
+
Divergence from KDLS, deliberate: KDLS put background-clip:text on the whole badge,
|
|
171
|
+
which sets color:transparent on everything inside — including any icon, which then
|
|
172
|
+
disappears. Here the clip is scoped to the label, and the badge keeps a solid
|
|
173
|
+
currentColor from the -solid companion so icons still paint.
|
|
174
|
+
|
|
175
|
+
In solid mode all of this renders exactly like a plain outline. */
|
|
176
|
+
.sds-badge--scheme-color.sds-badge--outline {
|
|
177
|
+
position: relative;
|
|
178
|
+
background: transparent;
|
|
179
|
+
color: var(--color-primary-50-solid);
|
|
180
|
+
box-shadow: none;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.sds-badge--scheme-color.sds-badge--outline .sds-badge__label {
|
|
184
|
+
background: var(--color-primary-50);
|
|
185
|
+
background-clip: text;
|
|
186
|
+
-webkit-background-clip: text;
|
|
187
|
+
color: transparent;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.sds-badge--scheme-color.sds-badge--outline::before {
|
|
191
|
+
content: "";
|
|
192
|
+
position: absolute;
|
|
193
|
+
inset: 0;
|
|
194
|
+
border-radius: inherit;
|
|
195
|
+
padding: 1px;
|
|
196
|
+
background: var(--color-primary-50);
|
|
197
|
+
-webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
|
|
198
|
+
-webkit-mask-composite: xor;
|
|
199
|
+
mask-composite: exclude;
|
|
200
|
+
pointer-events: none;
|
|
201
|
+
}
|
|
202
|
+
.sds-badge--scheme-color.sds-badge--tone-10 { background: var(--color-primary-10); color: var(--color-primary-10-on); }
|
|
203
|
+
.sds-badge--scheme-color.sds-badge--tone-20 { background: var(--color-primary-20); color: var(--color-primary-20-on); }
|
|
204
|
+
.sds-badge--scheme-color.sds-badge--tone-30 { background: var(--color-primary-30); color: var(--color-primary-30-on); }
|
|
205
|
+
.sds-badge--scheme-color.sds-badge--tone-40 { background: var(--color-primary-40); color: var(--color-primary-40-on); }
|
|
206
|
+
.sds-badge--scheme-color.sds-badge--tone-50 { background: var(--color-primary-50); color: var(--color-primary-50-on); }
|
|
207
|
+
|
|
208
|
+
/* ── Label colour ──────────────────────────────────────────────────────────────
|
|
209
|
+
Labels are not painted by hand. Each surface reads --color-<family>-<tone>-on,
|
|
210
|
+
generated by scripts/gen-contrast-tokens.mjs from the palette itself: the step of
|
|
211
|
+
the family that reads best on that surface, in that theme, falling back to white or
|
|
212
|
+
black only where the family cannot reach WCAG AA.
|
|
213
|
+
|
|
214
|
+
Of the 130 combinations, 113 are satisfied by the family itself and 17 need the
|
|
215
|
+
fallback. None is left below AA.
|
|
216
|
+
|
|
217
|
+
This replaces one hand-written declaration per family per tone per theme, plus a
|
|
218
|
+
block of dark-theme corrections. KDLS wrote those by eye and several were wrong in
|
|
219
|
+
ways only visible by squinting — tone-40 in the light theme read the -10 step across
|
|
220
|
+
every family, at ratios from 1.38 to 3.20 against a 4.5 minimum. */
|
|
221
|
+
|
|
222
|
+
/* ── SUCCESS ──────────────────────────────── */
|
|
223
|
+
.sds-badge--success.sds-badge--outline { background: transparent; color: var(--color-success-50); }
|
|
224
|
+
.sds-badge--success.sds-badge--tone-10 { background: var(--color-success-10); color: var(--color-success-10-on); }
|
|
225
|
+
.sds-badge--success.sds-badge--tone-20 { background: var(--color-success-20); color: var(--color-success-20-on); }
|
|
226
|
+
.sds-badge--success.sds-badge--tone-30 { background: var(--color-success-30); color: var(--color-success-30-on); }
|
|
227
|
+
.sds-badge--success.sds-badge--tone-40 { background: var(--color-success-40); color: var(--color-success-40-on); }
|
|
228
|
+
.sds-badge--success.sds-badge--tone-50 { background: var(--color-success-50); color: var(--color-success-50-on); }
|
|
229
|
+
|
|
230
|
+
/* ── ALERT ────────────────────────────────── */
|
|
231
|
+
.sds-badge--alert.sds-badge--outline { background: transparent; color: var(--color-alert-50); }
|
|
232
|
+
.sds-badge--alert.sds-badge--tone-10 { background: var(--color-alert-10); color: var(--color-alert-10-on); }
|
|
233
|
+
.sds-badge--alert.sds-badge--tone-20 { background: var(--color-alert-20); color: var(--color-alert-20-on); }
|
|
234
|
+
.sds-badge--alert.sds-badge--tone-30 { background: var(--color-alert-30); color: var(--color-alert-30-on); }
|
|
235
|
+
.sds-badge--alert.sds-badge--tone-40 { background: var(--color-alert-40); color: var(--color-alert-40-on); }
|
|
236
|
+
.sds-badge--alert.sds-badge--tone-50 { background: var(--color-alert-50); color: var(--color-alert-50-on); }
|
|
237
|
+
|
|
238
|
+
/* ── ERROR ────────────────────────────────── */
|
|
239
|
+
.sds-badge--error.sds-badge--outline { background: transparent; color: var(--color-error-50); }
|
|
240
|
+
.sds-badge--error.sds-badge--tone-10 { background: var(--color-error-10); color: var(--color-error-10-on); }
|
|
241
|
+
.sds-badge--error.sds-badge--tone-20 { background: var(--color-error-20); color: var(--color-error-20-on); }
|
|
242
|
+
.sds-badge--error.sds-badge--tone-30 { background: var(--color-error-30); color: var(--color-error-30-on); }
|
|
243
|
+
.sds-badge--error.sds-badge--tone-40 { background: var(--color-error-40); color: var(--color-error-40-on); }
|
|
244
|
+
.sds-badge--error.sds-badge--tone-50 { background: var(--color-error-50); color: var(--color-error-50-on); }
|
|
245
|
+
|
|
246
|
+
/* ── LEAF ── */
|
|
247
|
+
.sds-badge--leaf.sds-badge--outline { background: transparent; color: var(--color-leaf-70); }
|
|
248
|
+
.sds-badge--leaf.sds-badge--tone-10 { background: var(--color-leaf-10); color: var(--color-leaf-10-on); }
|
|
249
|
+
.sds-badge--leaf.sds-badge--tone-20 { background: var(--color-leaf-20); color: var(--color-leaf-20-on); }
|
|
250
|
+
.sds-badge--leaf.sds-badge--tone-30 { background: var(--color-leaf-30); color: var(--color-leaf-30-on); }
|
|
251
|
+
.sds-badge--leaf.sds-badge--tone-40 { background: var(--color-leaf-40); color: var(--color-leaf-40-on); }
|
|
252
|
+
/* tone-50 gets a dark-mode override below (§ Dark-mode text overrides) pinning it to the
|
|
253
|
+
same leaf-90 look as light — tone-40 needs none, its natural auto-invert already lands
|
|
254
|
+
on leaf-10 in dark, which is exactly what's wanted there. */
|
|
255
|
+
.sds-badge--leaf.sds-badge--tone-50 { background: var(--color-leaf-50); color: var(--color-leaf-50-on); }
|
|
256
|
+
|
|
257
|
+
/* ── SAND ── */
|
|
258
|
+
.sds-badge--sand.sds-badge--outline { background: transparent; color: var(--color-sand-70); }
|
|
259
|
+
.sds-badge--sand.sds-badge--tone-10 { background: var(--color-sand-10); color: var(--color-sand-10-on); }
|
|
260
|
+
.sds-badge--sand.sds-badge--tone-20 { background: var(--color-sand-20); color: var(--color-sand-20-on); }
|
|
261
|
+
.sds-badge--sand.sds-badge--tone-30 { background: var(--color-sand-30); color: var(--color-sand-30-on); }
|
|
262
|
+
.sds-badge--sand.sds-badge--tone-40 { background: var(--color-sand-40); color: var(--color-sand-40-on); }
|
|
263
|
+
/* Same reasoning as Leaf's tone-50 above — dark override lives in § Dark-mode text overrides. */
|
|
264
|
+
.sds-badge--sand.sds-badge--tone-50 { background: var(--color-sand-50); color: var(--color-sand-50-on); }
|
|
265
|
+
|
|
266
|
+
/* ── WOOD ── */
|
|
267
|
+
.sds-badge--wood.sds-badge--outline { background: transparent; color: var(--color-wood-50); }
|
|
268
|
+
.sds-badge--wood.sds-badge--tone-10 { background: var(--color-wood-10); color: var(--color-wood-10-on); }
|
|
269
|
+
.sds-badge--wood.sds-badge--tone-20 { background: var(--color-wood-20); color: var(--color-wood-20-on); }
|
|
270
|
+
.sds-badge--wood.sds-badge--tone-30 { background: var(--color-wood-30); color: var(--color-wood-30-on); }
|
|
271
|
+
.sds-badge--wood.sds-badge--tone-40 { background: var(--color-wood-40); color: var(--color-wood-40-on); }
|
|
272
|
+
.sds-badge--wood.sds-badge--tone-50 { background: var(--color-wood-50); color: var(--color-wood-50-on); }
|
|
273
|
+
|
|
274
|
+
/* ── CLAY ── */
|
|
275
|
+
.sds-badge--clay.sds-badge--outline { background: transparent; color: var(--color-clay-50); }
|
|
276
|
+
.sds-badge--clay.sds-badge--tone-10 { background: var(--color-clay-10); color: var(--color-clay-10-on); }
|
|
277
|
+
.sds-badge--clay.sds-badge--tone-20 { background: var(--color-clay-20); color: var(--color-clay-20-on); }
|
|
278
|
+
.sds-badge--clay.sds-badge--tone-30 { background: var(--color-clay-30); color: var(--color-clay-30-on); }
|
|
279
|
+
.sds-badge--clay.sds-badge--tone-40 { background: var(--color-clay-40); color: var(--color-clay-40-on); }
|
|
280
|
+
.sds-badge--clay.sds-badge--tone-50 { background: var(--color-clay-50); color: var(--color-clay-50-on); }
|
|
281
|
+
|
|
282
|
+
/* ── FLOWER ── */
|
|
283
|
+
.sds-badge--flower.sds-badge--outline { background: transparent; color: var(--color-flower-50); }
|
|
284
|
+
.sds-badge--flower.sds-badge--tone-10 { background: var(--color-flower-10); color: var(--color-flower-10-on); }
|
|
285
|
+
.sds-badge--flower.sds-badge--tone-20 { background: var(--color-flower-20); color: var(--color-flower-20-on); }
|
|
286
|
+
.sds-badge--flower.sds-badge--tone-30 { background: var(--color-flower-30); color: var(--color-flower-30-on); }
|
|
287
|
+
.sds-badge--flower.sds-badge--tone-40 { background: var(--color-flower-40); color: var(--color-flower-40-on); }
|
|
288
|
+
.sds-badge--flower.sds-badge--tone-50 { background: var(--color-flower-50); color: var(--color-flower-50-on); }
|
|
289
|
+
|
|
290
|
+
/* ── DUSK ── */
|
|
291
|
+
.sds-badge--dusk.sds-badge--outline { background: transparent; color: var(--color-dusk-50); }
|
|
292
|
+
.sds-badge--dusk.sds-badge--tone-10 { background: var(--color-dusk-10); color: var(--color-dusk-10-on); }
|
|
293
|
+
.sds-badge--dusk.sds-badge--tone-20 { background: var(--color-dusk-20); color: var(--color-dusk-20-on); }
|
|
294
|
+
.sds-badge--dusk.sds-badge--tone-30 { background: var(--color-dusk-30); color: var(--color-dusk-30-on); }
|
|
295
|
+
.sds-badge--dusk.sds-badge--tone-40 { background: var(--color-dusk-40); color: var(--color-dusk-40-on); }
|
|
296
|
+
.sds-badge--dusk.sds-badge--tone-50 { background: var(--color-dusk-50); color: var(--color-dusk-50-on); }
|
|
297
|
+
|
|
298
|
+
/* ── SKY ────────────────────────────────── */
|
|
299
|
+
.sds-badge--sky.sds-badge--outline { background: transparent; color: var(--color-sky-50); }
|
|
300
|
+
.sds-badge--sky.sds-badge--tone-10 { background: var(--color-sky-10); color: var(--color-sky-10-on); }
|
|
301
|
+
.sds-badge--sky.sds-badge--tone-20 { background: var(--color-sky-20); color: var(--color-sky-20-on); }
|
|
302
|
+
.sds-badge--sky.sds-badge--tone-30 { background: var(--color-sky-30); color: var(--color-sky-30-on); }
|
|
303
|
+
.sds-badge--sky.sds-badge--tone-40 { background: var(--color-sky-40); color: var(--color-sky-40-on); }
|
|
304
|
+
.sds-badge--sky.sds-badge--tone-50 { background: var(--color-sky-50); color: var(--color-sky-50-on); }
|
|
305
|
+
|
|
306
|
+
/* ── SEA ── */
|
|
307
|
+
.sds-badge--sea.sds-badge--outline { background: transparent; color: var(--color-sea-50); }
|
|
308
|
+
.sds-badge--sea.sds-badge--tone-10 { background: var(--color-sea-10); color: var(--color-sea-10-on); }
|
|
309
|
+
.sds-badge--sea.sds-badge--tone-20 { background: var(--color-sea-20); color: var(--color-sea-20-on); }
|
|
310
|
+
.sds-badge--sea.sds-badge--tone-30 { background: var(--color-sea-30); color: var(--color-sea-30-on); }
|
|
311
|
+
.sds-badge--sea.sds-badge--tone-40 { background: var(--color-sea-40); color: var(--color-sea-40-on); }
|
|
312
|
+
.sds-badge--sea.sds-badge--tone-50 { background: var(--color-sea-50); color: var(--color-sea-50-on); }
|
|
313
|
+
|
|
314
|
+
/* ── LAKE ── */
|
|
315
|
+
.sds-badge--lake.sds-badge--outline { background: transparent; color: var(--color-lake-50); }
|
|
316
|
+
.sds-badge--lake.sds-badge--tone-10 { background: var(--color-lake-10); color: var(--color-lake-10-on); }
|
|
317
|
+
.sds-badge--lake.sds-badge--tone-20 { background: var(--color-lake-20); color: var(--color-lake-20-on); }
|
|
318
|
+
.sds-badge--lake.sds-badge--tone-30 { background: var(--color-lake-30); color: var(--color-lake-30-on); }
|
|
319
|
+
.sds-badge--lake.sds-badge--tone-40 { background: var(--color-lake-40); color: var(--color-lake-40-on); }
|
|
320
|
+
.sds-badge--lake.sds-badge--tone-50 { background: var(--color-lake-50); color: var(--color-lake-50-on); }
|
|
321
|
+
|
|
322
|
+
/* ── Border radius variants ────────────── */
|
|
323
|
+
[data-radius="rounded"] .sds-badge { border-radius: var(--radius-full); }
|
|
324
|
+
|
|
325
|
+
/* ── Dark-mode text overrides — see tokens-reference.md § Mirror-Pin Technique
|
|
326
|
+
and component-rules.md § Badge text color rules for the full table ── */
|
|
327
|
+
|
|
328
|
+
/* ══════════════════════════════════════════════
|
|
329
|
+
BORDER — appearance="outline" always shows a border
|
|
330
|
+
(text color, via currentColor). tone-10..tone-50
|
|
331
|
+
keep the base's transparent border (reserves
|
|
332
|
+
space, avoids layout shift when switching appearance).
|
|
333
|
+
══════════════════════════════════════════════ */
|
|
334
|
+
|
|
335
|
+
.sds-badge--outline { box-shadow: inset 0 0 0 1px currentColor; }
|
|
336
|
+
|
|
337
|
+
/* The badge decides its own label weight, and needs to win over the Text variant's.
|
|
338
|
+
sds-text--labelRegular carries --font-weight-regular; a badge label is medium. Two
|
|
339
|
+
classes beat one, which is the whole trick. */
|
|
340
|
+
.sds-badge .sds-badge__label {
|
|
341
|
+
font-weight: var(--font-weight-medium);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/* ── The dismiss control ───────────────────────────────────────────────────────
|
|
345
|
+
It is a real <button> rather than a span with role="button", which buys keyboard
|
|
346
|
+
handling, focus and disabled from the browser. It is NOT an <sds-icon-button>: that
|
|
347
|
+
component is a fixed 32x32 at its smallest, and the badge's target is a 16px glyph
|
|
348
|
+
with 2px of padding inside a pill about 24px tall. Importing it would blow the badge
|
|
349
|
+
apart — which is why KDLS hand-rolled this too.
|
|
350
|
+
|
|
351
|
+
These reset the native chrome so the button carries no appearance of its own. */
|
|
352
|
+
.sds-badge__icon--right--clickable {
|
|
353
|
+
appearance: none;
|
|
354
|
+
background: none;
|
|
355
|
+
border: 0;
|
|
356
|
+
font: inherit;
|
|
357
|
+
color: inherit;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
/* The dismiss control takes focus, so it has to show it.
|
|
362
|
+
KDLS styles no focus state for it at all: a keyboard user tabbing through a row of
|
|
363
|
+
dismissible badges has no idea which one they are on, which is the difference between
|
|
364
|
+
removing the right filter and removing a random one.
|
|
365
|
+
|
|
366
|
+
The ring is inset rather than outside, because the button sits inside the badge's own
|
|
367
|
+
rounded box and an outline would be clipped by it. */
|
|
368
|
+
.sds-badge__icon:focus-visible {
|
|
369
|
+
outline: none;
|
|
370
|
+
box-shadow: var(--focus-ring-inset);
|
|
371
|
+
border-radius: var(--radius-xs);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/* Same reasoning as Text: a clipped background paints nothing once forced colours have
|
|
375
|
+
discarded backgrounds, and the label would vanish instead of changing colour. */
|
|
376
|
+
@media (forced-colors: active) {
|
|
377
|
+
.sds-badge--scheme-color.sds-badge--outline .sds-badge__label {
|
|
378
|
+
background: none;
|
|
379
|
+
color: CanvasText;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
<!-- sds-badge-dismissible — the interactive half of the Badge.
|
|
2
|
+
|
|
3
|
+
<sds-badge> is markup and costs nothing. This one hydrates: it listens for a click
|
|
4
|
+
and a keypress on the right icon, and it tracks its own loading state. Marko 5
|
|
5
|
+
serialises the entire input of a stateful component into the HTML, once per
|
|
6
|
+
instance, with no way to opt out — so this tag is the deliberate, visible choice to
|
|
7
|
+
pay that, and the name is what makes the choice visible at the call site.
|
|
8
|
+
|
|
9
|
+
Use it for a filter chip a person removes. Do not use it inside a repeated card:
|
|
10
|
+
twenty instances write twenty copies of their input into the page.
|
|
11
|
+
|
|
12
|
+
The dismiss control is a real <button>, not a span with role="button": keyboard handling,
|
|
13
|
+
focus and disabled come from the browser, so there is no key handler here to get wrong.
|
|
14
|
+
It is deliberately NOT an <sds-icon-button> — that component is a fixed 32x32 at its
|
|
15
|
+
smallest, and this target is a 16px glyph inside a pill about 24px tall.
|
|
16
|
+
|
|
17
|
+
Because it is already paying for hydration, the full KDLS behaviour comes back —
|
|
18
|
+
including the automatic spinner when onClickIconRight returns a Promise, which
|
|
19
|
+
<sds-badge> cannot offer and which the controlled `iconRightLoading` attribute only
|
|
20
|
+
approximates.
|
|
21
|
+
|
|
22
|
+
Ported from kdls-design-system v2.5.1 (0f3edf8) components/Badge/Badge.tsx. -->
|
|
23
|
+
|
|
24
|
+
import { assertVariant, assertAppearance, assertSize, assertWidth } from "./badge-variants.js";
|
|
25
|
+
|
|
26
|
+
class {
|
|
27
|
+
onCreate() {
|
|
28
|
+
this.state = { loading: false };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async handleClick(event) {
|
|
32
|
+
event.stopPropagation();
|
|
33
|
+
const { onClickIconRight, iconRightDisabled, iconRightLoading } = this.input;
|
|
34
|
+
// The guard has to read the same thing the render does. It read only the internal
|
|
35
|
+
// flag, so a caller driving `iconRightLoading` from outside showed a spinner and still
|
|
36
|
+
// took the click — the one moment the control exists to refuse. KDLS mirrors these two
|
|
37
|
+
// the same way.
|
|
38
|
+
if (iconRightDisabled || this.state.loading || iconRightLoading) return;
|
|
39
|
+
|
|
40
|
+
const result = onClickIconRight?.(event);
|
|
41
|
+
if (result instanceof Promise) {
|
|
42
|
+
this.state.loading = true;
|
|
43
|
+
try {
|
|
44
|
+
await result;
|
|
45
|
+
} finally {
|
|
46
|
+
this.state.loading = false;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
$ {
|
|
54
|
+
const {
|
|
55
|
+
label,
|
|
56
|
+
variant = "neutral",
|
|
57
|
+
appearance = "tone-30",
|
|
58
|
+
size = "sm",
|
|
59
|
+
width = "hug",
|
|
60
|
+
iconLeft,
|
|
61
|
+
iconRight,
|
|
62
|
+
iconRightLoading = false,
|
|
63
|
+
iconRightDisabled = false,
|
|
64
|
+
iconRightLabel = "Remover",
|
|
65
|
+
class: extraClass = "",
|
|
66
|
+
} = input;
|
|
67
|
+
|
|
68
|
+
assertVariant(variant);
|
|
69
|
+
assertAppearance(appearance);
|
|
70
|
+
assertSize(size);
|
|
71
|
+
assertWidth(width);
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
// An icon may arrive two ways: as an attribute tag (<@icon-left>…</@icon-left>, which
|
|
75
|
+
// is the Marko idiom and what a page writes) or as a template value (which is what a
|
|
76
|
+
// test or a loop passes). Normalising here means callers never have to care.
|
|
77
|
+
const renderable = (value) => (value && value.renderBody) || value;
|
|
78
|
+
|
|
79
|
+
const showSpinner = (state.loading || iconRightLoading) && !iconRightDisabled;
|
|
80
|
+
|
|
81
|
+
// The label's typography is the Text scale: label-sm at sm, body-sm at md, body-md at
|
|
82
|
+
// lg. It renders through <sds-text> so that scale lives in one place, and passes
|
|
83
|
+
// color="inherit" because the badge decides the colour — that is where the contrast
|
|
84
|
+
// tokens are applied.
|
|
85
|
+
const labelVariant = { sm: "labelRegular", md: "bodySm", lg: "bodyMd" }[size];
|
|
86
|
+
|
|
87
|
+
const badgeClass = [
|
|
88
|
+
"sds-badge",
|
|
89
|
+
`sds-badge--${variant}`,
|
|
90
|
+
`sds-badge--${appearance}`,
|
|
91
|
+
`sds-badge--${size}`,
|
|
92
|
+
width === "fill" ? "sds-badge--fill" : "",
|
|
93
|
+
"sds-badge--has-icon-right",
|
|
94
|
+
extraClass,
|
|
95
|
+
].filter(Boolean).join(" ");
|
|
96
|
+
|
|
97
|
+
const iconRightClass = [
|
|
98
|
+
"sds-badge__icon",
|
|
99
|
+
"sds-badge__icon--right",
|
|
100
|
+
"sds-badge__icon--right--clickable",
|
|
101
|
+
iconRightDisabled ? "sds-badge__icon--right--disabled" : "",
|
|
102
|
+
showSpinner ? "sds-badge__icon--right--loading" : "",
|
|
103
|
+
].filter(Boolean).join(" ");
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
<span class=badgeClass>
|
|
107
|
+
<span class="sds-badge__content sds-badge__content--has-icon-right">
|
|
108
|
+
<if(iconLeft)>
|
|
109
|
+
<span class="sds-badge__icon sds-badge__icon--left" aria-hidden="true">
|
|
110
|
+
<sds-icon size="sm">
|
|
111
|
+
<${renderable(iconLeft)}/>
|
|
112
|
+
</sds-icon>
|
|
113
|
+
</span>
|
|
114
|
+
</if>
|
|
115
|
+
<sds-text as="span" variant=labelVariant color="inherit" class="sds-badge__label">
|
|
116
|
+
${label}
|
|
117
|
+
</sds-text>
|
|
118
|
+
</span>
|
|
119
|
+
|
|
120
|
+
<span class="sds-badge__icon-right-wrap">
|
|
121
|
+
<button
|
|
122
|
+
type="button"
|
|
123
|
+
class=iconRightClass
|
|
124
|
+
disabled=(iconRightDisabled || null)
|
|
125
|
+
aria-label=iconRightLabel
|
|
126
|
+
aria-busy=(showSpinner ? "true" : null)
|
|
127
|
+
aria-disabled=(iconRightDisabled ? "true" : null)
|
|
128
|
+
on-click("handleClick")
|
|
129
|
+
>
|
|
130
|
+
<if(showSpinner)>
|
|
131
|
+
// No label. It was passed `iconRightLabel`, so the live region announced "Remover"
|
|
132
|
+
// — the button's own name repeated, never saying that anything was happening. The
|
|
133
|
+
// spinner's own default says "Carregando", which is the news.
|
|
134
|
+
<sds-spinner size="sm"/>
|
|
135
|
+
</if>
|
|
136
|
+
<else>
|
|
137
|
+
<sds-icon size="sm">
|
|
138
|
+
<${renderable(iconRight)}/>
|
|
139
|
+
</sds-icon>
|
|
140
|
+
</else>
|
|
141
|
+
</button>
|
|
142
|
+
</span>
|
|
143
|
+
</span>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Badge's vocabulary.
|
|
3
|
+
*
|
|
4
|
+
* Ported from kdls-design-system v2.5.1 (0f3edf8) components/Badge/Badge.tsx.
|
|
5
|
+
* Divergence: `primary`, `secondary`, `tertiary` and `lya` are removed — the first
|
|
6
|
+
* three read Kenlo brand families SDS does not carry, and `lya` was the gradient
|
|
7
|
+
* special case, which now reaches every component through applyCustomGradient.
|
|
8
|
+
*
|
|
9
|
+
* `scheme-color` is the one to reach for when a badge should follow the site's own
|
|
10
|
+
* colour: it reads the primary slot, so it picks up applyCustomColor and
|
|
11
|
+
* applyCustomGradient alike.
|
|
12
|
+
*/
|
|
13
|
+
export const BADGE_VARIANTS = [
|
|
14
|
+
'neutral', 'scheme-color',
|
|
15
|
+
'success', 'alert', 'error',
|
|
16
|
+
'leaf', 'sand', 'wood', 'clay', 'flower', 'dusk', 'sky', 'sea', 'lake',
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
export const BADGE_APPEARANCES = ['outline', 'tone-10', 'tone-20', 'tone-30', 'tone-40', 'tone-50'];
|
|
20
|
+
|
|
21
|
+
export const BADGE_SIZES = ['sm', 'md', 'lg'];
|
|
22
|
+
|
|
23
|
+
export const BADGE_WIDTHS = ['hug', 'fill'];
|
|
24
|
+
|
|
25
|
+
function assertOneOf(label, value, allowed) {
|
|
26
|
+
if (!allowed.includes(value)) {
|
|
27
|
+
throw new Error(`Unknown badge ${label}: "${value}". Allowed: ${allowed.join(', ')}`);
|
|
28
|
+
}
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const assertVariant = (v) => assertOneOf('variant', v, BADGE_VARIANTS);
|
|
33
|
+
export const assertAppearance = (v) => assertOneOf('appearance', v, BADGE_APPEARANCES);
|
|
34
|
+
export const assertSize = (v) => assertOneOf('size', v, BADGE_SIZES);
|
|
35
|
+
export const assertWidth = (v) => assertOneOf('width', v, BADGE_WIDTHS);
|