@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,104 @@
|
|
|
1
|
+
/* SDS Alert.
|
|
2
|
+
Ported from kdls-design-system v2.5.1 (0f3edf8) components/Alert/Alert.css
|
|
3
|
+
by scripts/port-css.mjs. Divergences beyond the mechanical rename are noted inline.
|
|
4
|
+
*/.sds-alert {
|
|
5
|
+
display: flex;
|
|
6
|
+
align-items: center;
|
|
7
|
+
width: 100%;
|
|
8
|
+
border-radius: var(--radius-md);
|
|
9
|
+
}
|
|
10
|
+
.sds-alert__content {
|
|
11
|
+
flex: 1;
|
|
12
|
+
display: flex;
|
|
13
|
+
align-items: center;
|
|
14
|
+
gap: var(--spacing-4);
|
|
15
|
+
padding: var(--spacing-3) var(--spacing-4);
|
|
16
|
+
}
|
|
17
|
+
.sds-alert--hug { width: fit-content; }
|
|
18
|
+
.sds-alert--fill { width: 100%; }
|
|
19
|
+
.sds-alert__icon {
|
|
20
|
+
display: inline-flex;
|
|
21
|
+
align-items: center;
|
|
22
|
+
justify-content: center;
|
|
23
|
+
width: 24px;
|
|
24
|
+
height: 24px;
|
|
25
|
+
flex-shrink: 0;
|
|
26
|
+
font-size: 24px;
|
|
27
|
+
}
|
|
28
|
+
.sds-alert__message {
|
|
29
|
+
flex: 1;
|
|
30
|
+
}
|
|
31
|
+
.sds-alert__action {
|
|
32
|
+
margin-left: var(--spacing-1);
|
|
33
|
+
flex-shrink: 0;
|
|
34
|
+
}
|
|
35
|
+
.sds-alert__dismiss {
|
|
36
|
+
display: flex;
|
|
37
|
+
align-items: center;
|
|
38
|
+
flex-shrink: 0;
|
|
39
|
+
padding-right: var(--spacing-2);
|
|
40
|
+
}
|
|
41
|
+
.sds-alert--sm .sds-alert__dismiss {
|
|
42
|
+
padding-top: var(--spacing-1);
|
|
43
|
+
padding-bottom: var(--spacing-1);
|
|
44
|
+
padding-right: var(--spacing-1);
|
|
45
|
+
}
|
|
46
|
+
/* Alert variants — icon inherits the same text color as the container.
|
|
47
|
+
|
|
48
|
+
The background is `--surface-<family>`, not `--color-<family>-10`. They are the same
|
|
49
|
+
thing in the light theme; in the dark theme the surface stops one step short of the
|
|
50
|
+
palette's darkest tone, because the darkest tone next to a near-black page reads as a
|
|
51
|
+
hole rather than a tinted panel. The reasoning is at the token, in foundations. */
|
|
52
|
+
.sds-alert--success {
|
|
53
|
+
background: var(--surface-success);
|
|
54
|
+
color: var(--color-success-10-on);
|
|
55
|
+
}
|
|
56
|
+
.sds-alert--alert {
|
|
57
|
+
background: var(--surface-alert);
|
|
58
|
+
color: var(--color-alert-10-on);
|
|
59
|
+
}
|
|
60
|
+
.sds-alert--error {
|
|
61
|
+
background: var(--surface-error);
|
|
62
|
+
color: var(--color-error-10-on);
|
|
63
|
+
}
|
|
64
|
+
.sds-alert--info {
|
|
65
|
+
background: var(--surface-info);
|
|
66
|
+
color: var(--color-primary-90-solid);
|
|
67
|
+
}
|
|
68
|
+
/* Neutral variant (fallback) */
|
|
69
|
+
.sds-alert--neutral {
|
|
70
|
+
background: var(--surface-neutral);
|
|
71
|
+
color: var(--color-neutral-10-on);
|
|
72
|
+
}
|
|
73
|
+
/* ── Md size — icon 20px ── */
|
|
74
|
+
.sds-alert--md .sds-alert__icon,
|
|
75
|
+
.sds-alert--md .sds-alert__icon .sds-icon,
|
|
76
|
+
.sds-alert--md .sds-alert__icon svg {
|
|
77
|
+
width: 20px;
|
|
78
|
+
height: 20px;
|
|
79
|
+
font-size: 20px;
|
|
80
|
+
line-height: 1;
|
|
81
|
+
}
|
|
82
|
+
/* ── Small size ── */
|
|
83
|
+
.sds-alert--sm .sds-alert__content {
|
|
84
|
+
min-height: unset;
|
|
85
|
+
padding: var(--spacing-2) var(--spacing-3);
|
|
86
|
+
}
|
|
87
|
+
.sds-alert--sm .sds-alert__icon {
|
|
88
|
+
width: 16px;
|
|
89
|
+
height: 16px;
|
|
90
|
+
font-size: 16px;
|
|
91
|
+
}
|
|
92
|
+
.sds-alert--neutral.sds-alert--bordered { border: 1px solid var(--color-neutral-30); }
|
|
93
|
+
.sds-alert--success.sds-alert--bordered { border: 1px solid var(--color-success-30); }
|
|
94
|
+
.sds-alert--alert.sds-alert--bordered { border: 1px solid var(--color-alert-30); }
|
|
95
|
+
.sds-alert--error.sds-alert--bordered { border: 1px solid var(--color-error-30); }
|
|
96
|
+
.sds-alert--info.sds-alert--bordered { border: 1px solid var(--color-primary-30-solid); }
|
|
97
|
+
/* ── Dark mode ── */
|
|
98
|
+
|
|
99
|
+
/* A neutral alert paints itself in the same neutral the page uses, so on a neutral ground
|
|
100
|
+
the two merge and the alert reads as loose text. The other variants carry their own
|
|
101
|
+
hue and separate themselves. */
|
|
102
|
+
.sds-alert--neutral:not(.sds-alert--bordered) {
|
|
103
|
+
border: 1px solid var(--border-static-color);
|
|
104
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Alert vocabulary.
|
|
3
|
+
* Ported from kdls-design-system v2.5.1 (0f3edf8) components/Alert/Alert.tsx.
|
|
4
|
+
*/
|
|
5
|
+
export const ALERT_VARIANTS = ['info', 'success', 'alert', 'error', 'neutral'];
|
|
6
|
+
export const ALERT_SIZES = ['sm', 'md', 'lg'];
|
|
7
|
+
export const ALERT_WIDTHS = ['hug', 'fill'];
|
|
8
|
+
|
|
9
|
+
/** The Text variant each alert size reads at, so the scale lives in one place. */
|
|
10
|
+
export const ALERT_TEXT_VARIANT = { lg: 'bodyMd', md: 'bodySm', sm: 'labelRegular' };
|
|
11
|
+
|
|
12
|
+
function assertOneOf(label, value, allowed) {
|
|
13
|
+
if (!allowed.includes(value)) {
|
|
14
|
+
throw new Error(`Unknown alert ${label}: "${value}". Allowed: ${allowed.join(', ')}`);
|
|
15
|
+
}
|
|
16
|
+
return value;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const assertAlertVariant = (v) => assertOneOf('variant', v, ALERT_VARIANTS);
|
|
20
|
+
export const assertAlertSize = (v) => assertOneOf('size', v, ALERT_SIZES);
|
|
21
|
+
export const assertAlertWidth = (v) => assertOneOf('width', v, ALERT_WIDTHS);
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Which live region an alert belongs in.
|
|
25
|
+
*
|
|
26
|
+
* KDLS put role="alert" on every one. That is right for an error — it interrupts a screen
|
|
27
|
+
* reader immediately — and wrong for an informational notice, which then also interrupts.
|
|
28
|
+
* Anything that is not a problem is announced politely instead, when the user is between
|
|
29
|
+
* things.
|
|
30
|
+
*/
|
|
31
|
+
export function liveRegionFor(variant) {
|
|
32
|
+
return variant === 'error' || variant === 'alert'
|
|
33
|
+
? { role: 'alert', 'aria-live': 'assertive' }
|
|
34
|
+
: { role: 'status', 'aria-live': 'polite' };
|
|
35
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
<!-- sds-alert — ported from kdls-design-system v2.5.1 (0f3edf8) components/Alert/Alert.tsx.
|
|
2
|
+
|
|
3
|
+
No state. KDLS held useState for one thing: a spinner on the action button while an async
|
|
4
|
+
onAction resolved. That is the Badge's case again — the base component stays markup and
|
|
5
|
+
the caller, which is the thing that knows about its own async work, drives `actionLoading`
|
|
6
|
+
from outside.
|
|
7
|
+
|
|
8
|
+
Divergences:
|
|
9
|
+
|
|
10
|
+
Only an error or a warning takes role="alert". KDLS put it on every variant, so an
|
|
11
|
+
informational notice interrupted a screen reader mid-sentence exactly as an error does.
|
|
12
|
+
Anything that is not a problem is announced politely instead — see liveRegionFor.
|
|
13
|
+
|
|
14
|
+
`actionDisabledTooltip` and `actionDisabledTooltipPosition` are here now that Tooltip is.
|
|
15
|
+
They reach sds-button, which does the wrapping — a disabled button dispatches no mouse
|
|
16
|
+
events, so the tooltip has to sit outside it. -->
|
|
17
|
+
|
|
18
|
+
import { assertPlacement } from "../Floating/floating-position.js";
|
|
19
|
+
import { assertAlertVariant, assertAlertSize, assertAlertWidth, ALERT_TEXT_VARIANT,
|
|
20
|
+
liveRegionFor } from "./alert-variants.js";
|
|
21
|
+
|
|
22
|
+
$ {
|
|
23
|
+
const {
|
|
24
|
+
message,
|
|
25
|
+
variant = "info",
|
|
26
|
+
size = "lg",
|
|
27
|
+
width = "fill",
|
|
28
|
+
bordered = false,
|
|
29
|
+
icon,
|
|
30
|
+
actionLabel,
|
|
31
|
+
onAction,
|
|
32
|
+
actionLoading = false,
|
|
33
|
+
actionDisabled = false,
|
|
34
|
+
actionDisabledTooltip,
|
|
35
|
+
actionDisabledTooltipPosition = "top",
|
|
36
|
+
onDismiss,
|
|
37
|
+
dismissLabel = "Fechar",
|
|
38
|
+
class: extraClass = "",
|
|
39
|
+
} = input;
|
|
40
|
+
|
|
41
|
+
assertPlacement(actionDisabledTooltipPosition);
|
|
42
|
+
|
|
43
|
+
if (!message) throw new Error("sds-alert needs a `message`.");
|
|
44
|
+
assertAlertVariant(variant);
|
|
45
|
+
assertAlertSize(size);
|
|
46
|
+
assertAlertWidth(width);
|
|
47
|
+
|
|
48
|
+
const renderable = (value) => (value && value.renderBody) || value;
|
|
49
|
+
const live = liveRegionFor(variant);
|
|
50
|
+
|
|
51
|
+
const classes = [
|
|
52
|
+
"sds-alert",
|
|
53
|
+
`sds-alert--${variant}`,
|
|
54
|
+
size !== "lg" ? `sds-alert--${size}` : "",
|
|
55
|
+
`sds-alert--${width}`,
|
|
56
|
+
bordered ? "sds-alert--bordered" : "",
|
|
57
|
+
extraClass,
|
|
58
|
+
].filter(Boolean).join(" ");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
<div class=classes role=live.role aria-live=live["aria-live"]>
|
|
62
|
+
<div class="sds-alert__content">
|
|
63
|
+
<if(icon)>
|
|
64
|
+
<span class="sds-alert__icon" aria-hidden="true">
|
|
65
|
+
<sds-icon size=(size === "sm" ? "sm" : "md")><${renderable(icon)}/></sds-icon>
|
|
66
|
+
</span>
|
|
67
|
+
</if>
|
|
68
|
+
|
|
69
|
+
<sds-text as="span" class="sds-alert__message" color="inherit" variant=ALERT_TEXT_VARIANT[size]>
|
|
70
|
+
${message}
|
|
71
|
+
</sds-text>
|
|
72
|
+
|
|
73
|
+
<if(actionLabel)>
|
|
74
|
+
<sds-button
|
|
75
|
+
variant="link"
|
|
76
|
+
label=actionLabel
|
|
77
|
+
onClick=onAction
|
|
78
|
+
isLoading=actionLoading
|
|
79
|
+
disabled=actionDisabled
|
|
80
|
+
disabledTooltip=actionDisabledTooltip
|
|
81
|
+
disabledTooltipPosition=actionDisabledTooltipPosition
|
|
82
|
+
class="sds-alert__action"
|
|
83
|
+
/>
|
|
84
|
+
</if>
|
|
85
|
+
</div>
|
|
86
|
+
|
|
87
|
+
<if(onDismiss)>
|
|
88
|
+
<div class="sds-alert__dismiss">
|
|
89
|
+
<sds-icon-button variant="neutral" aria-label=dismissLabel onClick=onDismiss>
|
|
90
|
+
<@icon><fluent-dismiss-regular/></@icon>
|
|
91
|
+
</sds-icon-button>
|
|
92
|
+
</div>
|
|
93
|
+
</if>
|
|
94
|
+
</div>
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/* SDS AttachmentList.
|
|
2
|
+
Ported from kdls-design-system v2.5.1 (0f3edf8) components/AttachmentList/AttachmentList.css
|
|
3
|
+
by scripts/port-css.mjs. Divergences beyond the mechanical rename are noted inline.
|
|
4
|
+
*//* ── AttachmentList ─────────────────────────────────────────────────────────
|
|
5
|
+
List of uploaded/attached files with icon, name+meta, and action buttons.
|
|
6
|
+
BEM root: .sds-attachment-list / .sds-attachment-row
|
|
7
|
+
─────────────────────────────────────────────────────────────────────────── */
|
|
8
|
+
|
|
9
|
+
/* Divergence from KDLS: the surface is declared.
|
|
10
|
+
|
|
11
|
+
KDLS draws the stroke and the radius and leaves the fill to whatever is behind, which
|
|
12
|
+
works only while the thing behind is the component surface. Dropped on a page ground,
|
|
13
|
+
or on a stage with an alternate background, the list takes that colour and the frame
|
|
14
|
+
reads as an empty outline rather than a panel. A bordered container states its own
|
|
15
|
+
fill. */
|
|
16
|
+
.sds-attachment-list {
|
|
17
|
+
width: 100%;
|
|
18
|
+
background: var(--component-bg-color);
|
|
19
|
+
border-radius: var(--radius-md);
|
|
20
|
+
overflow: hidden;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/* The frame is a choice, not a fixture: a bordered container separates itself from a
|
|
24
|
+
ground of its own colour, and on a different ground the stroke is one line of noise.
|
|
25
|
+
Same rule the Card and the Accordion follow. */
|
|
26
|
+
.sds-attachment-list--bordered {
|
|
27
|
+
border: 1px solid var(--border-static-color);
|
|
28
|
+
}
|
|
29
|
+
/* ── Row ── */
|
|
30
|
+
.sds-attachment-row {
|
|
31
|
+
display: flex;
|
|
32
|
+
align-items: center;
|
|
33
|
+
gap: var(--spacing-4);
|
|
34
|
+
padding: var(--spacing-3) var(--spacing-4);
|
|
35
|
+
min-height: var(--spacing-16);
|
|
36
|
+
}
|
|
37
|
+
/* The divider. It is written against the <li>, not the row: KDLS stacks bare divs, so
|
|
38
|
+
`.row + .row` was adjacent there — here every row is wrapped in its own list item, and
|
|
39
|
+
two rows are never siblings. The selector matched nothing and the lines ran together. */
|
|
40
|
+
.sds-attachment-list > li + li > .sds-attachment-row {
|
|
41
|
+
border-top: 1px solid var(--border-static-color);
|
|
42
|
+
}
|
|
43
|
+
/* ── File type icon ── */
|
|
44
|
+
.sds-attachment-row__icon {
|
|
45
|
+
display: inline-flex;
|
|
46
|
+
font-size: 24px;
|
|
47
|
+
flex-shrink: 0;
|
|
48
|
+
}
|
|
49
|
+
/* ── File info ── */
|
|
50
|
+
.sds-attachment-row__info {
|
|
51
|
+
flex: 1;
|
|
52
|
+
display: flex;
|
|
53
|
+
flex-direction: column;
|
|
54
|
+
gap: var(--spacing-0-5);
|
|
55
|
+
min-width: 0;
|
|
56
|
+
}
|
|
57
|
+
.sds-attachment-row__name {
|
|
58
|
+
min-width: 200px;
|
|
59
|
+
}
|
|
60
|
+
.sds-attachment-row__name--truncate {
|
|
61
|
+
display: block;
|
|
62
|
+
white-space: nowrap;
|
|
63
|
+
overflow: hidden;
|
|
64
|
+
text-overflow: ellipsis;
|
|
65
|
+
}
|
|
66
|
+
/* Tooltip.wrapper is inline-flex by default (shrinks to content) — force it to take up
|
|
67
|
+
the available width so the name truncates correctly inside it */
|
|
68
|
+
.sds-attachment-row__name-tooltip {
|
|
69
|
+
display: block;
|
|
70
|
+
width: 100%;
|
|
71
|
+
min-width: 0;
|
|
72
|
+
}
|
|
73
|
+
/* No real overflow on the name — turns off the tooltip hover via CSS (without unmounting) */
|
|
74
|
+
.sds-attachment-row__name-tooltip--inactive {
|
|
75
|
+
pointer-events: none;
|
|
76
|
+
cursor: default;
|
|
77
|
+
}
|
|
78
|
+
/* ── Actions ── */
|
|
79
|
+
.sds-attachment-row__actions {
|
|
80
|
+
display: flex;
|
|
81
|
+
align-items: center;
|
|
82
|
+
gap: var(--spacing-4);
|
|
83
|
+
flex-shrink: 0;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/* The list is a <ul>, which brings bullets and indentation the design does not want —
|
|
87
|
+
these four lines are the whole price of a screen reader saying "list, 4 items". */
|
|
88
|
+
.sds-attachment-list {
|
|
89
|
+
list-style: none;
|
|
90
|
+
margin: 0;
|
|
91
|
+
padding: 0;
|
|
92
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
<!-- sds-attachment-list — ported from kdls-design-system v2.5.1 (0f3edf8)
|
|
2
|
+
components/AttachmentList/AttachmentList.tsx.
|
|
3
|
+
|
|
4
|
+
A list of files, each an sds-attachment-row. It holds no state of its own: the handlers
|
|
5
|
+
are the caller's and it only binds the document's id into them.
|
|
6
|
+
|
|
7
|
+
Divergence: it is a <ul> of <li>, not a stack of divs. A screen reader then says "list,
|
|
8
|
+
4 items" and can move between them; KDLS's divs are four unrelated rows, and a visitor
|
|
9
|
+
who cannot see the page has no way to know how many attachments there are or when the
|
|
10
|
+
list ends. -->
|
|
11
|
+
|
|
12
|
+
$ {
|
|
13
|
+
const {
|
|
14
|
+
documents = [],
|
|
15
|
+
onDelete,
|
|
16
|
+
onDownload,
|
|
17
|
+
onViewDetails,
|
|
18
|
+
truncateName = true,
|
|
19
|
+
// Default true: the list draws a surface, and a bordered container is what it has
|
|
20
|
+
// always looked like. A caller dropping it on a different ground turns it off.
|
|
21
|
+
bordered = true,
|
|
22
|
+
label = "Anexos",
|
|
23
|
+
class: extraClass = "",
|
|
24
|
+
} = input;
|
|
25
|
+
|
|
26
|
+
const classes = [
|
|
27
|
+
"sds-attachment-list",
|
|
28
|
+
bordered ? "sds-attachment-list--bordered" : "",
|
|
29
|
+
extraClass,
|
|
30
|
+
].filter(Boolean).join(" ");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
<ul class=classes aria-label=label>
|
|
34
|
+
<for|doc| of=documents>
|
|
35
|
+
<li key=doc.id>
|
|
36
|
+
<sds-attachment-row
|
|
37
|
+
name=doc.name
|
|
38
|
+
icon=doc.icon
|
|
39
|
+
meta=doc.meta
|
|
40
|
+
badge=doc.badge
|
|
41
|
+
truncateName=truncateName
|
|
42
|
+
onDelete=(onDelete ? () => onDelete(doc.id) : undefined)
|
|
43
|
+
onDownload=(onDownload ? () => onDownload(doc.id) : undefined)
|
|
44
|
+
onViewDetails=(onViewDetails ? () => onViewDetails(doc.id) : undefined)
|
|
45
|
+
disabledDelete=doc.disabledDelete
|
|
46
|
+
disabledDownload=doc.disabledDownload
|
|
47
|
+
disabledViewDetails=doc.disabledViewDetails
|
|
48
|
+
/>
|
|
49
|
+
</li>
|
|
50
|
+
</for>
|
|
51
|
+
</ul>
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
<!-- sds-attachment-row — ported from kdls-design-system v2.5.1 (0f3edf8)
|
|
2
|
+
components/AttachmentList/AttachmentList.tsx.
|
|
3
|
+
|
|
4
|
+
One file in a list: an icon, a name, a line of metadata, and up to three actions.
|
|
5
|
+
|
|
6
|
+
Divergences:
|
|
7
|
+
|
|
8
|
+
The name's tooltip only exists when the name is actually cut off. KDLS renders it
|
|
9
|
+
always and hides it with a class when it is not needed — which leaves a `cursor: help`
|
|
10
|
+
and a tooltip trigger on every row whose name fits, promising an explanation there is
|
|
11
|
+
no need for. The measurement is the same ResizeObserver; what changes is that the
|
|
12
|
+
result decides whether to render rather than how to style.
|
|
13
|
+
|
|
14
|
+
`onDelete` and `onDownload` follow a promise when the caller returns one, as KDLS
|
|
15
|
+
does, which is what lets a row show its own spinner without being told to. -->
|
|
16
|
+
|
|
17
|
+
import deleteIcon from "../../icons/generated/fluent-delete-regular.marko";
|
|
18
|
+
import downloadIcon from "../../icons/generated/fluent-arrow-download-regular.marko";
|
|
19
|
+
import detailsIcon from "../../icons/generated/fluent-chevron-right-regular.marko";
|
|
20
|
+
|
|
21
|
+
class {
|
|
22
|
+
onCreate() {
|
|
23
|
+
this.state = { deleting: false, downloading: false, truncated: false };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Is the name actually cut off? Only then is it worth a tooltip.
|
|
28
|
+
*
|
|
29
|
+
* This used to live entirely inside `onMount`, which made it a one-time answer to a
|
|
30
|
+
* question that changes: renaming a file left the tooltip showing the old name, and
|
|
31
|
+
* turning `truncateName` on after mount applied the ellipsis with no tooltip behind it,
|
|
32
|
+
* so the cut-off name became unreadable with no way to see it.
|
|
33
|
+
*/
|
|
34
|
+
measure() {
|
|
35
|
+
const name = this.getEl("name");
|
|
36
|
+
if (!name) return;
|
|
37
|
+
this.state.truncated = this.input.truncateName !== false
|
|
38
|
+
&& name.scrollWidth > name.clientWidth;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
onMount() {
|
|
42
|
+
this.measure();
|
|
43
|
+
this.watched = this.input.name;
|
|
44
|
+
// The measurement above is the answer for a row that never changes width. The observer
|
|
45
|
+
// keeps it true when the column is resized — and is guarded because it does not exist
|
|
46
|
+
// in jsdom, or in a browser old enough to matter to nobody but which would otherwise
|
|
47
|
+
// take the whole row down with it.
|
|
48
|
+
if (typeof ResizeObserver === "undefined") return;
|
|
49
|
+
const name = this.getEl("name");
|
|
50
|
+
if (!name) return;
|
|
51
|
+
this.observer = new ResizeObserver(() => this.measure());
|
|
52
|
+
this.observer.observe(name);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
onUpdate() {
|
|
56
|
+
// Re-measure when the thing being measured changed. Guarded on the name so that the
|
|
57
|
+
// state write inside `measure` does not re-enter this on every render.
|
|
58
|
+
if (this.watched === this.input.name) return;
|
|
59
|
+
this.watched = this.input.name;
|
|
60
|
+
this.measure();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
onDestroy() {
|
|
64
|
+
this.observer?.disconnect();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async run(which, handler) {
|
|
68
|
+
const result = handler?.();
|
|
69
|
+
if (!(result instanceof Promise)) return;
|
|
70
|
+
this.state[which] = true;
|
|
71
|
+
try {
|
|
72
|
+
await result;
|
|
73
|
+
} finally {
|
|
74
|
+
this.state[which] = false;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
handleDelete() { return this.run("deleting", this.input.onDelete); }
|
|
79
|
+
handleDownload() { return this.run("downloading", this.input.onDownload); }
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
$ {
|
|
83
|
+
const {
|
|
84
|
+
name,
|
|
85
|
+
icon,
|
|
86
|
+
meta,
|
|
87
|
+
badge,
|
|
88
|
+
onDelete,
|
|
89
|
+
onDownload,
|
|
90
|
+
onViewDetails,
|
|
91
|
+
disabledDelete = false,
|
|
92
|
+
disabledDownload = false,
|
|
93
|
+
disabledViewDetails = false,
|
|
94
|
+
truncateName = true,
|
|
95
|
+
deleteLabel = "Excluir",
|
|
96
|
+
downloadLabel = "Baixar",
|
|
97
|
+
detailsLabel = "Ver detalhes",
|
|
98
|
+
class: extraClass = "",
|
|
99
|
+
} = input;
|
|
100
|
+
|
|
101
|
+
if (!name) throw new Error("sds-attachment-row needs a `name`.");
|
|
102
|
+
|
|
103
|
+
const renderable = (value) => (value && value.renderBody) || value;
|
|
104
|
+
const hasActions = Boolean(badge || onDelete || onDownload || onViewDetails);
|
|
105
|
+
|
|
106
|
+
const nameClass = [
|
|
107
|
+
"sds-attachment-row__name",
|
|
108
|
+
truncateName ? "sds-attachment-row__name--truncate" : "",
|
|
109
|
+
].filter(Boolean).join(" ");
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
<div class=`sds-attachment-row ${extraClass}`.trim()>
|
|
113
|
+
<if(icon)>
|
|
114
|
+
<span class="sds-attachment-row__icon" aria-hidden="true">
|
|
115
|
+
<sds-icon><${renderable(icon)}/></sds-icon>
|
|
116
|
+
</span>
|
|
117
|
+
</if>
|
|
118
|
+
|
|
119
|
+
<div class="sds-attachment-row__info">
|
|
120
|
+
<macro|{ }| name="theName">
|
|
121
|
+
<sds-text key="name" as="span" variant="bodyMd" class=nameClass>${name}</sds-text>
|
|
122
|
+
</macro>
|
|
123
|
+
|
|
124
|
+
<if(truncateName && state.truncated)>
|
|
125
|
+
<sds-tooltip content=name class="sds-attachment-row__name-tooltip">
|
|
126
|
+
<theName/>
|
|
127
|
+
</sds-tooltip>
|
|
128
|
+
</if>
|
|
129
|
+
<else>
|
|
130
|
+
<theName/>
|
|
131
|
+
</else>
|
|
132
|
+
|
|
133
|
+
<if(meta)>
|
|
134
|
+
<sds-text as="span" variant="bodySm" class="sds-attachment-row__meta">${meta}</sds-text>
|
|
135
|
+
</if>
|
|
136
|
+
</div>
|
|
137
|
+
|
|
138
|
+
<if(hasActions)>
|
|
139
|
+
<div class="sds-attachment-row__actions">
|
|
140
|
+
<if(badge)>
|
|
141
|
+
// A badge arrives two ways: as content to render, or as a plain status word like
|
|
142
|
+
// "Enviado". A string handed to `<${...}/>` is read as a TAG NAME, so the word
|
|
143
|
+
// vanished with no error — the same guard Popover and Table already apply.
|
|
144
|
+
<if(typeof badge === "string")>${badge}</if>
|
|
145
|
+
<else><${renderable(badge)}/></else>
|
|
146
|
+
</if>
|
|
147
|
+
<if(onDelete)>
|
|
148
|
+
<sds-icon-button aria-label=deleteLabel onClick=() => component.handleDelete()
|
|
149
|
+
isLoading=state.deleting disabled=disabledDelete>
|
|
150
|
+
<@icon><${deleteIcon}/></@icon>
|
|
151
|
+
</sds-icon-button>
|
|
152
|
+
</if>
|
|
153
|
+
<if(onDownload)>
|
|
154
|
+
<sds-icon-button aria-label=downloadLabel onClick=() => component.handleDownload()
|
|
155
|
+
isLoading=state.downloading disabled=disabledDownload>
|
|
156
|
+
<@icon><${downloadIcon}/></@icon>
|
|
157
|
+
</sds-icon-button>
|
|
158
|
+
</if>
|
|
159
|
+
<if(onViewDetails)>
|
|
160
|
+
<sds-icon-button variant="neutral" aria-label=detailsLabel onClick=onViewDetails
|
|
161
|
+
disabled=disabledViewDetails>
|
|
162
|
+
<@icon><${detailsIcon}/></@icon>
|
|
163
|
+
</sds-icon-button>
|
|
164
|
+
</if>
|
|
165
|
+
</div>
|
|
166
|
+
</if>
|
|
167
|
+
</div>
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/* SDS Avatar.
|
|
2
|
+
Ported from kdls-design-system v2.5.1 (0f3edf8) components/Avatar/Avatar.css
|
|
3
|
+
by scripts/port-css.mjs. Divergences beyond the mechanical rename are noted inline.
|
|
4
|
+
|
|
5
|
+
Rules removed with the brand families they targeted:
|
|
6
|
+
[data-color-scheme="lya"] .sds-avatar--clickable
|
|
7
|
+
[data-color-scheme="lya"] .sds-avatar--clickable::before
|
|
8
|
+
[data-color-scheme="lya"] .sds-avatar--clickable:hover
|
|
9
|
+
[data-color-scheme="lya"] .sds-avatar--clickable:hover::before
|
|
10
|
+
[data-color-scheme="lya"] .sds-avatar--clickable:active
|
|
11
|
+
[data-color-scheme="lya"] .sds-avatar--clickable:active::before
|
|
12
|
+
[data-color-scheme="lya"] .sds-avatar__placeholder
|
|
13
|
+
*/.sds-avatar {
|
|
14
|
+
position: relative;
|
|
15
|
+
display: inline-flex;
|
|
16
|
+
align-items: center;
|
|
17
|
+
justify-content: center;
|
|
18
|
+
border-radius: var(--radius-full);
|
|
19
|
+
overflow: visible;
|
|
20
|
+
flex-shrink: 0;
|
|
21
|
+
width: var(--avatar-size, 40px);
|
|
22
|
+
height: var(--avatar-size, 40px);
|
|
23
|
+
/* The root is a <button> whenever it has a click handler, and SDS ships no element
|
|
24
|
+
reset — so a clickable avatar was wearing the browser's button chrome: a 2px outset
|
|
25
|
+
border, a #efefef ButtonFace ground and 1px/6px of padding, measured in Chrome. That
|
|
26
|
+
squeezes the photo inside the circle and paints a grey ring around it. IconButton and
|
|
27
|
+
Badge already reset this; Avatar did not. */
|
|
28
|
+
appearance: none;
|
|
29
|
+
border: none;
|
|
30
|
+
padding: 0;
|
|
31
|
+
background: transparent;
|
|
32
|
+
font: inherit;
|
|
33
|
+
color: inherit;
|
|
34
|
+
}
|
|
35
|
+
/* ── Sizes ── */
|
|
36
|
+
.sds-avatar--sm { --avatar-size: var(--spacing-10); }
|
|
37
|
+
.sds-avatar--md { --avatar-size: var(--spacing-12); }
|
|
38
|
+
.sds-avatar--lg { --avatar-size: var(--spacing-14); }
|
|
39
|
+
.sds-avatar__image {
|
|
40
|
+
position: relative; /* stays above the lya gradient ring ::before (position:absolute
|
|
41
|
+
always stacks above static content, even when it comes first in the DOM) */
|
|
42
|
+
width: 100%;
|
|
43
|
+
height: 100%;
|
|
44
|
+
border-radius: var(--radius-full);
|
|
45
|
+
object-fit: cover;
|
|
46
|
+
display: block;
|
|
47
|
+
}
|
|
48
|
+
.sds-avatar__placeholder {
|
|
49
|
+
position: relative; /* same reason as __image above */
|
|
50
|
+
width: 100%;
|
|
51
|
+
height: 100%;
|
|
52
|
+
border-radius: var(--radius-full);
|
|
53
|
+
background: var(--color-primary-50);
|
|
54
|
+
color: var(--color-on-primary);
|
|
55
|
+
display: flex;
|
|
56
|
+
align-items: center;
|
|
57
|
+
justify-content: center;
|
|
58
|
+
font-family: var(--font-family-base);
|
|
59
|
+
font-weight: var(--font-weight-semibold);
|
|
60
|
+
font-size: calc(var(--avatar-size) * 0.46); /* proporcional ao size */
|
|
61
|
+
user-select: none;
|
|
62
|
+
}
|
|
63
|
+
/* ── Interactive states (onClick provided) ── */
|
|
64
|
+
.sds-avatar--clickable {
|
|
65
|
+
cursor: pointer;
|
|
66
|
+
outline: none;
|
|
67
|
+
transition: filter 120ms ease, box-shadow 120ms ease;
|
|
68
|
+
}
|
|
69
|
+
.sds-avatar--clickable:hover {
|
|
70
|
+
filter: brightness(0.92);
|
|
71
|
+
box-shadow: 0 0 0 2px var(--component-bg-color), 0 0 0 4px var(--color-primary-40-solid);
|
|
72
|
+
}
|
|
73
|
+
.sds-avatar--clickable:active {
|
|
74
|
+
filter: brightness(0.78);
|
|
75
|
+
box-shadow: 0 0 0 2px var(--component-bg-color), 0 0 0 4px var(--color-primary-50-solid);
|
|
76
|
+
}
|
|
77
|
+
.sds-avatar--clickable:focus-visible {
|
|
78
|
+
box-shadow: var(--focus-ring);
|
|
79
|
+
}
|
|
80
|
+
/* Eleven rules used to sit here (and in ten other stylesheets) guarding on
|
|
81
|
+
`[data-color-scheme="neutral"][data-theme="dark"]`, repainting the text on a filled
|
|
82
|
+
primary surface. Not one of them could ever match: `neutral` is the install default, and
|
|
83
|
+
a value equal to the default is written by REMOVING the attribute, never by setting it.
|
|
84
|
+
They were the patch for `--color-on-primary` being a fixed white while
|
|
85
|
+
`--color-primary-50` inverts — which is fixed at the token now, so the patch is gone. */
|
|
86
|
+
/* ── Image and fallback, without JavaScript ────────────────────────────────────
|
|
87
|
+
KDLS tracked a broken image in component state: an onError handler set imgFailed and
|
|
88
|
+
the initials rendered instead. That is one serialised component per avatar — and an
|
|
89
|
+
avatar list is exactly the place that repeats.
|
|
90
|
+
|
|
91
|
+
Both are rendered here instead, the initials underneath and the image over them. A
|
|
92
|
+
broken <img> paints nothing, so the initials show through on their own. No handler, no
|
|
93
|
+
state, and it works before any JavaScript arrives — which is also when a slow image is
|
|
94
|
+
most likely to be missing. */
|
|
95
|
+
.sds-avatar__placeholder {
|
|
96
|
+
position: absolute;
|
|
97
|
+
inset: 0;
|
|
98
|
+
display: inline-flex;
|
|
99
|
+
align-items: center;
|
|
100
|
+
justify-content: center;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.sds-avatar__image {
|
|
104
|
+
position: relative;
|
|
105
|
+
z-index: 1;
|
|
106
|
+
width: 100%;
|
|
107
|
+
height: 100%;
|
|
108
|
+
object-fit: cover;
|
|
109
|
+
border-radius: inherit;
|
|
110
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Avatar vocabulary.
|
|
3
|
+
* Ported from kdls-design-system v2.5.1 (0f3edf8) components/Avatar/Avatar.tsx.
|
|
4
|
+
*/
|
|
5
|
+
export const AVATAR_SIZES = ['sm', 'md', 'lg'];
|
|
6
|
+
|
|
7
|
+
export function assertAvatarSize(size) {
|
|
8
|
+
if (!AVATAR_SIZES.includes(size)) {
|
|
9
|
+
throw new Error(`Unknown avatar size: "${size}". Allowed: ${AVATAR_SIZES.join(', ')}`);
|
|
10
|
+
}
|
|
11
|
+
return size;
|
|
12
|
+
}
|