@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,398 @@
|
|
|
1
|
+
<!-- sds-gradient-picker — designed for SDS; KDLS has no counterpart.
|
|
2
|
+
|
|
3
|
+
KDLS's palette has no gradient to pick: its one gradient was the Lya brand value,
|
|
4
|
+
fixed, and chosen by selecting the `lya` colour scheme. SDS removed those values and
|
|
5
|
+
kept the machinery, so the gradient became something a site chooses — and choosing it
|
|
6
|
+
needs a control. This is that control, built to the design at
|
|
7
|
+
figma.com/design/HefwQxdw5h6wrhjzC9FHtf?node-id=11362-100508.
|
|
8
|
+
|
|
9
|
+
Two stops, side by side, each with a hue rail, a brightness rail and a hex field;
|
|
10
|
+
under them the gradient itself, at the size it is actually judged at; then Apply.
|
|
11
|
+
|
|
12
|
+
Two rails rather than the ColorPicker's saturation canvas, because that is what the
|
|
13
|
+
design asks for and because a gradient is read as two hues, not as two points in a
|
|
14
|
+
colour space. Saturation is still carried — a hex typed into the field keeps whatever
|
|
15
|
+
it has, and the rails move without flattening it — so nothing a caller sets is lost
|
|
16
|
+
to a control that cannot express it.
|
|
17
|
+
|
|
18
|
+
`onChange` fires on Apply, not on every drag: it repaints the whole site, and doing
|
|
19
|
+
that on each pixel of a drag is a strobe rather than a preview.
|
|
20
|
+
|
|
21
|
+
`overlay` takes the panel out of the flow entirely: a backdrop over the page with the
|
|
22
|
+
panel centred in it, instead of a panel anchored to the trigger. It is for one situation —
|
|
23
|
+
this control living inside another panel, where anchoring would put a panel on top of the
|
|
24
|
+
panel it came from — and for a phone, where there is no room beside a trigger to anchor
|
|
25
|
+
anything. Whatever opened this is told through `onOpenChange`, so it can get out of the
|
|
26
|
+
way.
|
|
27
|
+
-->
|
|
28
|
+
|
|
29
|
+
import { hsvToRgb, rgbToHex, hexToRgb, rgbToHsv, startingRgb } from "../ColorPicker/color-maths.js";
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* A stop remembers the hex it came in as, and gives it back untouched until it is moved.
|
|
33
|
+
*
|
|
34
|
+
* hex → HSV → hex does not round-trip: #BF71E5 comes back as #BE70E6, a whole point off
|
|
35
|
+
* on each channel. Harmless once, and not once — the portal applies, the value returns as
|
|
36
|
+
* the picker's input, and the next apply rounds it again. A colour nobody touched would
|
|
37
|
+
* walk. So `source` is the answer until an edit makes it stale.
|
|
38
|
+
*/
|
|
39
|
+
static function readStop(value, fallback) {
|
|
40
|
+
const source = value ?? fallback;
|
|
41
|
+
const [r, g, b] = startingRgb(source);
|
|
42
|
+
const [h, s, v] = rgbToHsv(r, g, b);
|
|
43
|
+
return { h, s, v, source };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
class {
|
|
47
|
+
onCreate() {
|
|
48
|
+
this.state = { open: false, stops: this.stopsFrom(undefined), hexDraft: [null, null] };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
onInput(input) {
|
|
52
|
+
if (this.seeded) return;
|
|
53
|
+
this.state.stops = this.stopsFrom(input.value);
|
|
54
|
+
this.seeded = true;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
stopsFrom(value) {
|
|
58
|
+
const [a, b] = Array.isArray(value) ? value : [];
|
|
59
|
+
return [readStop(a, "#77A6E0"), readStop(b, "#BF71E5")];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
hexAt(index) {
|
|
63
|
+
const { h, s, v, source } = this.state.stops[index];
|
|
64
|
+
return source ?? rgbToHex(...hsvToRgb(h, s, v));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
get gradient() {
|
|
68
|
+
return `linear-gradient(91deg, ${this.hexAt(0)} 0%, ${this.hexAt(1)} 100%)`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
toggle() {
|
|
72
|
+
if (this.input.disabled) return;
|
|
73
|
+
this.setOpen(!this.state.open);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// The backdrop dismisses, the panel does not: a drag that starts on a slider and ends
|
|
77
|
+
// outside it is still a drag. Escape is listened for on the document because the
|
|
78
|
+
// backdrop only receives key events while something inside it holds focus.
|
|
79
|
+
onMount() {
|
|
80
|
+
this.escape = (event) => this.closeOnEscape(event);
|
|
81
|
+
document.addEventListener("keydown", this.escape);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
onDestroy() {
|
|
85
|
+
document.removeEventListener("keydown", this.escape);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
closeFromBackdrop(event, element) {
|
|
89
|
+
if (event.target !== element) return;
|
|
90
|
+
this.setOpen(false);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
closeOnEscape(event) {
|
|
94
|
+
if (event.key !== "Escape") return;
|
|
95
|
+
if (!this.input.overlay || !(this.input.open ?? this.state.open)) return;
|
|
96
|
+
event.preventDefault();
|
|
97
|
+
this.setOpen(false);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
setOpen(next) {
|
|
101
|
+
// The Floating reports its own open/close too, so the disabled check belongs here and
|
|
102
|
+
// not only on the trigger's click: a disabled <button> swallows the click, but the
|
|
103
|
+
// Floating around it does not, and the panel opened anyway.
|
|
104
|
+
if (next && this.input.disabled) return;
|
|
105
|
+
if (this.input.open === undefined) this.state.open = next;
|
|
106
|
+
this.input.onOpenChange?.(next);
|
|
107
|
+
// Reopening starts from what is applied, not from where the last drag was left.
|
|
108
|
+
if (next) {
|
|
109
|
+
this.state.stops = this.stopsFrom(this.input.value);
|
|
110
|
+
this.state.hexDraft = [null, null];
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
setStop(index, part, amount) {
|
|
115
|
+
// Moving a rail is the edit that makes `source` stale.
|
|
116
|
+
const stops = this.state.stops.map((stop, i) =>
|
|
117
|
+
(i === index ? { ...stop, [part]: amount, source: null } : stop));
|
|
118
|
+
this.state.stops = stops;
|
|
119
|
+
this.state.hexDraft = this.state.hexDraft.map((draft, i) => (i === index ? null : draft));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** Shared by both rails: a press anywhere on the rail moves the thumb there. */
|
|
123
|
+
track(element, read, event) {
|
|
124
|
+
if (!element) return;
|
|
125
|
+
const apply = (moveEvent) => {
|
|
126
|
+
const rect = element.getBoundingClientRect();
|
|
127
|
+
read(Math.max(0, Math.min(1, (moveEvent.clientX - rect.left) / rect.width)));
|
|
128
|
+
};
|
|
129
|
+
apply(event);
|
|
130
|
+
const move = (moveEvent) => apply(moveEvent);
|
|
131
|
+
const up = () => {
|
|
132
|
+
window.removeEventListener("mousemove", move);
|
|
133
|
+
window.removeEventListener("mouseup", up);
|
|
134
|
+
};
|
|
135
|
+
window.addEventListener("mousemove", move);
|
|
136
|
+
window.addEventListener("mouseup", up);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
dragHue(index, event) {
|
|
140
|
+
this.track(this.getEl(`hue-${index}`), (x) => this.setStop(index, "h", Math.round(x * 360)), event);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
dragBrightness(index, event) {
|
|
144
|
+
this.track(this.getEl(`bri-${index}`), (x) => this.setStop(index, "v", Math.round(x * 100)), event);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** Arrows on a rail, Shift for ten at a time — the ColorPicker's rule, same reason. */
|
|
148
|
+
nudge(index, part, event) {
|
|
149
|
+
const direction = { ArrowLeft: -1, ArrowDown: -1, ArrowRight: 1, ArrowUp: 1 }[event.key];
|
|
150
|
+
if (direction === undefined) return;
|
|
151
|
+
event.preventDefault();
|
|
152
|
+
|
|
153
|
+
const max = part === "h" ? 360 : 100;
|
|
154
|
+
const step = event.shiftKey ? 10 : 1;
|
|
155
|
+
const next = this.state.stops[index][part] + direction * step;
|
|
156
|
+
this.setStop(index, part, Math.max(0, Math.min(max, next)));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
typeHex(index, raw) {
|
|
160
|
+
const draft = this.state.hexDraft.map((value, i) => (i === index ? raw : value));
|
|
161
|
+
this.state.hexDraft = draft;
|
|
162
|
+
|
|
163
|
+
const rgb = hexToRgb(raw.startsWith("#") ? raw : `#${raw}`);
|
|
164
|
+
if (!rgb) return;
|
|
165
|
+
|
|
166
|
+
const [h, s, v] = rgbToHsv(...rgb);
|
|
167
|
+
// A typed hex is exact by definition, so it becomes the new source.
|
|
168
|
+
const source = rgbToHex(...rgb);
|
|
169
|
+
this.state.stops = this.state.stops.map((stop, i) => (i === index ? { h, s, v, source } : stop));
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
apply() {
|
|
173
|
+
this.input.onChange?.([this.hexAt(0), this.hexAt(1)]);
|
|
174
|
+
this.state.open = false;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
$ {
|
|
179
|
+
const {
|
|
180
|
+
label,
|
|
181
|
+
width = "hug",
|
|
182
|
+
zIndex,
|
|
183
|
+
disabled = false,
|
|
184
|
+
applyLabel = "Aplicar",
|
|
185
|
+
overlay = false,
|
|
186
|
+
open: openInput,
|
|
187
|
+
showTrigger = true,
|
|
188
|
+
class: extraClass = "",
|
|
189
|
+
} = input;
|
|
190
|
+
|
|
191
|
+
if (!label) throw new Error("sds-gradient-picker needs a `label`.");
|
|
192
|
+
if (width !== "hug" && width !== "fill") {
|
|
193
|
+
throw new Error(`Unknown gradient picker width: "${width}". Allowed: hug, fill`);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Controlled when the caller passes `open`. An overlay picker usually has to be: it is
|
|
197
|
+
// opened from somewhere that then gets out of the way, and a picker that owns its own
|
|
198
|
+
// open state dies with whatever it was rendered inside.
|
|
199
|
+
const open = openInput !== undefined ? openInput : state.open;
|
|
200
|
+
const stops = state.stops;
|
|
201
|
+
const gradient = component.gradient;
|
|
202
|
+
|
|
203
|
+
const shown = (index) => state.hexDraft[index] ?? component.hexAt(index).replace("#", "").toUpperCase();
|
|
204
|
+
// The brightness rail runs from black to this stop's hue at full strength, so its own
|
|
205
|
+
// colour says what moving it would do.
|
|
206
|
+
// Black to this stop's hue at full strength: the rail's own colour says what moving it
|
|
207
|
+
// does. `hsl(h 100% 0%)` rather than a literal black, for the same reason the hue rail
|
|
208
|
+
// is written in hue angles — this is the axis, not a palette value.
|
|
209
|
+
const briRail = (stop) =>
|
|
210
|
+
`linear-gradient(to right, hsl(${stop.h}, 100%, 0%) 0%, ${rgbToHex(...hsvToRgb(stop.h, stop.s, 100))} 100%)`;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
<div class=[
|
|
214
|
+
"sds-gradient-picker",
|
|
215
|
+
showTrigger ? "" : "sds-gradient-picker--headless",
|
|
216
|
+
width === "fill" ? "sds-gradient-picker--fill" : "",
|
|
217
|
+
extraClass,
|
|
218
|
+
].filter(Boolean).join(" ")>
|
|
219
|
+
<if(showTrigger)>
|
|
220
|
+
<sds-floating
|
|
221
|
+
open=open
|
|
222
|
+
onOpenChange=(next) => component.setOpen(next)
|
|
223
|
+
placement="bottomLeft"
|
|
224
|
+
zIndex=zIndex
|
|
225
|
+
>
|
|
226
|
+
<@content>
|
|
227
|
+
<if(!overlay)>
|
|
228
|
+
<div class="sds-gradient-picker__panel">
|
|
229
|
+
<div class="sds-gradient-picker__stops">
|
|
230
|
+
<for|stop, index| of=stops>
|
|
231
|
+
<if(index > 0)>
|
|
232
|
+
<div class="sds-gradient-picker__divider" aria-hidden="true"/>
|
|
233
|
+
</if>
|
|
234
|
+
|
|
235
|
+
<div class="sds-gradient-picker__stop">
|
|
236
|
+
<div
|
|
237
|
+
key=`hue-${index}`
|
|
238
|
+
class="sds-gradient-picker__rail sds-gradient-picker__rail--hue"
|
|
239
|
+
role="slider"
|
|
240
|
+
aria-label=`Matiz da parada ${index + 1}`
|
|
241
|
+
aria-valuemin="0"
|
|
242
|
+
aria-valuemax="360"
|
|
243
|
+
aria-valuenow=String(stop.h)
|
|
244
|
+
tabindex="0"
|
|
245
|
+
on-mousedown("dragHue", index)
|
|
246
|
+
on-keydown("nudge", index, "h")
|
|
247
|
+
>
|
|
248
|
+
<div class="sds-gradient-picker__thumb" style=`left:${(stop.h / 360) * 100}%`/>
|
|
249
|
+
</div>
|
|
250
|
+
|
|
251
|
+
<div
|
|
252
|
+
key=`bri-${index}`
|
|
253
|
+
class="sds-gradient-picker__rail"
|
|
254
|
+
style=`background:${briRail(stop)}`
|
|
255
|
+
role="slider"
|
|
256
|
+
aria-label=`Brilho da parada ${index + 1}`
|
|
257
|
+
aria-valuemin="0"
|
|
258
|
+
aria-valuemax="100"
|
|
259
|
+
aria-valuenow=String(stop.v)
|
|
260
|
+
tabindex="0"
|
|
261
|
+
on-mousedown("dragBrightness", index)
|
|
262
|
+
on-keydown("nudge", index, "v")
|
|
263
|
+
>
|
|
264
|
+
<div class="sds-gradient-picker__thumb" style=`left:${stop.v}%`/>
|
|
265
|
+
</div>
|
|
266
|
+
|
|
267
|
+
<sds-input
|
|
268
|
+
label="HEX"
|
|
269
|
+
width="fill"
|
|
270
|
+
value=shown(index)
|
|
271
|
+
onChange=(event, value) => component.typeHex(index, value)
|
|
272
|
+
/>
|
|
273
|
+
</div>
|
|
274
|
+
</for>
|
|
275
|
+
</div>
|
|
276
|
+
|
|
277
|
+
<!-- The gradient as a band. A swatch the size of a chip cannot show where two
|
|
278
|
+
hues meet, which is the only thing being decided here; 40px can, and keeps
|
|
279
|
+
the panel short enough for the places that open it. -->
|
|
280
|
+
<div
|
|
281
|
+
class="sds-gradient-picker__preview"
|
|
282
|
+
style=`background:${gradient}`
|
|
283
|
+
role="img"
|
|
284
|
+
aria-label=`Prévia: ${component.hexAt(0)} a ${component.hexAt(1)}`
|
|
285
|
+
/>
|
|
286
|
+
|
|
287
|
+
<!-- `lg` is 56px, which is the height the design gives this button. -->
|
|
288
|
+
<sds-button
|
|
289
|
+
variant="primary"
|
|
290
|
+
size="lg"
|
|
291
|
+
width="fill"
|
|
292
|
+
label=applyLabel
|
|
293
|
+
onClick=() => component.apply()
|
|
294
|
+
/>
|
|
295
|
+
</div>
|
|
296
|
+
</if>
|
|
297
|
+
</@content>
|
|
298
|
+
|
|
299
|
+
<!-- A real button, with its own click: the Floating positions the panel, it does not
|
|
300
|
+
open it. The ColorPicker's trigger carries the same `on-click`, and leaving it off
|
|
301
|
+
here produced a control that looked identical and did nothing. -->
|
|
302
|
+
<button
|
|
303
|
+
type="button"
|
|
304
|
+
class="sds-gradient-picker__trigger"
|
|
305
|
+
disabled=(disabled || null)
|
|
306
|
+
aria-haspopup="dialog"
|
|
307
|
+
aria-expanded=(open ? "true" : "false")
|
|
308
|
+
on-click("toggle")
|
|
309
|
+
>
|
|
310
|
+
<span class="sds-gradient-picker__swatch" style=`background:${gradient}` aria-hidden="true"/>
|
|
311
|
+
<span class="sds-gradient-picker__field">
|
|
312
|
+
<sds-text as="span" variant="labelRegular">${label}</sds-text>
|
|
313
|
+
</span>
|
|
314
|
+
</button>
|
|
315
|
+
</sds-floating>
|
|
316
|
+
</if>
|
|
317
|
+
|
|
318
|
+
<!-- Centred, over everything, when `overlay` is on. Escape and the backdrop both close
|
|
319
|
+
it; the panel itself does not, so a drag that ends outside the panel is still a
|
|
320
|
+
drag rather than a dismissal. -->
|
|
321
|
+
<if(overlay && open)>
|
|
322
|
+
<div
|
|
323
|
+
class="sds-gradient-picker__backdrop"
|
|
324
|
+
style=(zIndex !== undefined ? `z-index:${zIndex}` : null)
|
|
325
|
+
on-click("closeFromBackdrop")
|
|
326
|
+
on-keydown("closeOnEscape")
|
|
327
|
+
>
|
|
328
|
+
<div class="sds-gradient-picker__panel">
|
|
329
|
+
<div class="sds-gradient-picker__stops">
|
|
330
|
+
<for|stop, index| of=stops>
|
|
331
|
+
<if(index > 0)>
|
|
332
|
+
<div class="sds-gradient-picker__divider" aria-hidden="true"/>
|
|
333
|
+
</if>
|
|
334
|
+
|
|
335
|
+
<div class="sds-gradient-picker__stop">
|
|
336
|
+
<div
|
|
337
|
+
key=`hue-${index}`
|
|
338
|
+
class="sds-gradient-picker__rail sds-gradient-picker__rail--hue"
|
|
339
|
+
role="slider"
|
|
340
|
+
aria-label=`Matiz da parada ${index + 1}`
|
|
341
|
+
aria-valuemin="0"
|
|
342
|
+
aria-valuemax="360"
|
|
343
|
+
aria-valuenow=String(stop.h)
|
|
344
|
+
tabindex="0"
|
|
345
|
+
on-mousedown("dragHue", index)
|
|
346
|
+
on-keydown("nudge", index, "h")
|
|
347
|
+
>
|
|
348
|
+
<div class="sds-gradient-picker__thumb" style=`left:${(stop.h / 360) * 100}%`/>
|
|
349
|
+
</div>
|
|
350
|
+
|
|
351
|
+
<div
|
|
352
|
+
key=`bri-${index}`
|
|
353
|
+
class="sds-gradient-picker__rail"
|
|
354
|
+
style=`background:${briRail(stop)}`
|
|
355
|
+
role="slider"
|
|
356
|
+
aria-label=`Brilho da parada ${index + 1}`
|
|
357
|
+
aria-valuemin="0"
|
|
358
|
+
aria-valuemax="100"
|
|
359
|
+
aria-valuenow=String(stop.v)
|
|
360
|
+
tabindex="0"
|
|
361
|
+
on-mousedown("dragBrightness", index)
|
|
362
|
+
on-keydown("nudge", index, "v")
|
|
363
|
+
>
|
|
364
|
+
<div class="sds-gradient-picker__thumb" style=`left:${stop.v}%`/>
|
|
365
|
+
</div>
|
|
366
|
+
|
|
367
|
+
<sds-input
|
|
368
|
+
label="HEX"
|
|
369
|
+
width="fill"
|
|
370
|
+
value=shown(index)
|
|
371
|
+
onChange=(event, value) => component.typeHex(index, value)
|
|
372
|
+
/>
|
|
373
|
+
</div>
|
|
374
|
+
</for>
|
|
375
|
+
</div>
|
|
376
|
+
|
|
377
|
+
<!-- The gradient as a band. A swatch the size of a chip cannot show where two
|
|
378
|
+
hues meet, which is the only thing being decided here; 40px can, and keeps
|
|
379
|
+
the panel short enough for the places that open it. -->
|
|
380
|
+
<div
|
|
381
|
+
class="sds-gradient-picker__preview"
|
|
382
|
+
style=`background:${gradient}`
|
|
383
|
+
role="img"
|
|
384
|
+
aria-label=`Prévia: ${component.hexAt(0)} a ${component.hexAt(1)}`
|
|
385
|
+
/>
|
|
386
|
+
|
|
387
|
+
<!-- `lg` is 56px, which is the height the design gives this button. -->
|
|
388
|
+
<sds-button
|
|
389
|
+
variant="primary"
|
|
390
|
+
size="lg"
|
|
391
|
+
width="fill"
|
|
392
|
+
label=applyLabel
|
|
393
|
+
onClick=() => component.apply()
|
|
394
|
+
/>
|
|
395
|
+
</div>
|
|
396
|
+
</div>
|
|
397
|
+
</if>
|
|
398
|
+
</div>
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/* SDS Grid.
|
|
2
|
+
Ported from kdls-design-system v2.5.1 (0f3edf8) components/Grid/Grid.css
|
|
3
|
+
by scripts/port-css.mjs. Divergences beyond the mechanical rename are noted inline.
|
|
4
|
+
*//* ============================================================
|
|
5
|
+
GRID — sds-grid / sds-col
|
|
6
|
+
12-column responsive grid system
|
|
7
|
+
xs: < 800px | sm: 800px+ | md: 1024px+ | lg: 1280px+ | xl: 1440px+
|
|
8
|
+
============================================================ */
|
|
9
|
+
|
|
10
|
+
/* ── Grid container ── */
|
|
11
|
+
.sds-grid {
|
|
12
|
+
display: grid;
|
|
13
|
+
grid-template-columns: repeat(12, 1fr);
|
|
14
|
+
gap: var(--spacing-4);
|
|
15
|
+
width: 100%;
|
|
16
|
+
}
|
|
17
|
+
/* ── Column variants ── */
|
|
18
|
+
.sds-grid--cols-1 { grid-template-columns: repeat(1, 1fr); }
|
|
19
|
+
.sds-grid--cols-2 { grid-template-columns: repeat(2, 1fr); }
|
|
20
|
+
.sds-grid--cols-3 { grid-template-columns: repeat(3, 1fr); }
|
|
21
|
+
.sds-grid--cols-4 { grid-template-columns: repeat(4, 1fr); }
|
|
22
|
+
.sds-grid--cols-6 { grid-template-columns: repeat(6, 1fr); }
|
|
23
|
+
.sds-grid--cols-12 { grid-template-columns: repeat(12, 1fr); }
|
|
24
|
+
/* ── Gap variants (spacing scale) ── */
|
|
25
|
+
.sds-grid--gap-1 { gap: var(--spacing-1); }
|
|
26
|
+
.sds-grid--gap-2 { gap: var(--spacing-2); }
|
|
27
|
+
.sds-grid--gap-3 { gap: var(--spacing-3); }
|
|
28
|
+
.sds-grid--gap-4 { gap: var(--spacing-4); }
|
|
29
|
+
.sds-grid--gap-5 { gap: var(--spacing-5); }
|
|
30
|
+
.sds-grid--gap-6 { gap: var(--spacing-6); }
|
|
31
|
+
.sds-grid--gap-7 { gap: var(--spacing-7); }
|
|
32
|
+
.sds-grid--gap-8 { gap: var(--spacing-8); }
|
|
33
|
+
.sds-grid--gap-9 { gap: var(--spacing-9); }
|
|
34
|
+
.sds-grid--gap-10 { gap: var(--spacing-10); }
|
|
35
|
+
.sds-grid--gap-12 { gap: var(--spacing-12); }
|
|
36
|
+
.sds-grid--gap-14 { gap: var(--spacing-14); }
|
|
37
|
+
.sds-grid--gap-16 { gap: var(--spacing-16); }
|
|
38
|
+
/* ── Row gap variants ── */
|
|
39
|
+
.sds-grid--row-gap-1 { row-gap: var(--spacing-1); }
|
|
40
|
+
.sds-grid--row-gap-2 { row-gap: var(--spacing-2); }
|
|
41
|
+
.sds-grid--row-gap-3 { row-gap: var(--spacing-3); }
|
|
42
|
+
.sds-grid--row-gap-4 { row-gap: var(--spacing-4); }
|
|
43
|
+
.sds-grid--row-gap-5 { row-gap: var(--spacing-5); }
|
|
44
|
+
.sds-grid--row-gap-6 { row-gap: var(--spacing-6); }
|
|
45
|
+
.sds-grid--row-gap-7 { row-gap: var(--spacing-7); }
|
|
46
|
+
.sds-grid--row-gap-8 { row-gap: var(--spacing-8); }
|
|
47
|
+
.sds-grid--row-gap-9 { row-gap: var(--spacing-9); }
|
|
48
|
+
.sds-grid--row-gap-10 { row-gap: var(--spacing-10); }
|
|
49
|
+
.sds-grid--row-gap-12 { row-gap: var(--spacing-12); }
|
|
50
|
+
.sds-grid--row-gap-14 { row-gap: var(--spacing-14); }
|
|
51
|
+
.sds-grid--row-gap-16 { row-gap: var(--spacing-16); }
|
|
52
|
+
/* ── Col gap variants ── */
|
|
53
|
+
.sds-grid--col-gap-1 { column-gap: var(--spacing-1); }
|
|
54
|
+
.sds-grid--col-gap-2 { column-gap: var(--spacing-2); }
|
|
55
|
+
.sds-grid--col-gap-3 { column-gap: var(--spacing-3); }
|
|
56
|
+
.sds-grid--col-gap-4 { column-gap: var(--spacing-4); }
|
|
57
|
+
.sds-grid--col-gap-5 { column-gap: var(--spacing-5); }
|
|
58
|
+
.sds-grid--col-gap-6 { column-gap: var(--spacing-6); }
|
|
59
|
+
.sds-grid--col-gap-7 { column-gap: var(--spacing-7); }
|
|
60
|
+
.sds-grid--col-gap-8 { column-gap: var(--spacing-8); }
|
|
61
|
+
.sds-grid--col-gap-9 { column-gap: var(--spacing-9); }
|
|
62
|
+
.sds-grid--col-gap-10 { column-gap: var(--spacing-10); }
|
|
63
|
+
.sds-grid--col-gap-12 { column-gap: var(--spacing-12); }
|
|
64
|
+
.sds-grid--col-gap-14 { column-gap: var(--spacing-14); }
|
|
65
|
+
.sds-grid--col-gap-16 { column-gap: var(--spacing-16); }
|
|
66
|
+
/* ── Align variants ── */
|
|
67
|
+
.sds-grid--align-start { align-items: start; }
|
|
68
|
+
.sds-grid--align-center { align-items: center; }
|
|
69
|
+
.sds-grid--align-end { align-items: end; }
|
|
70
|
+
/* ── Col base ── */
|
|
71
|
+
.sds-col {
|
|
72
|
+
min-width: 0;
|
|
73
|
+
}
|
|
74
|
+
/* ── Span classes (base / xs default) ── */
|
|
75
|
+
.sds-col--span-1 { grid-column: span 1; }
|
|
76
|
+
.sds-col--span-2 { grid-column: span 2; }
|
|
77
|
+
.sds-col--span-3 { grid-column: span 3; }
|
|
78
|
+
.sds-col--span-4 { grid-column: span 4; }
|
|
79
|
+
.sds-col--span-5 { grid-column: span 5; }
|
|
80
|
+
.sds-col--span-6 { grid-column: span 6; }
|
|
81
|
+
.sds-col--span-7 { grid-column: span 7; }
|
|
82
|
+
.sds-col--span-8 { grid-column: span 8; }
|
|
83
|
+
.sds-col--span-9 { grid-column: span 9; }
|
|
84
|
+
.sds-col--span-10 { grid-column: span 10; }
|
|
85
|
+
.sds-col--span-11 { grid-column: span 11; }
|
|
86
|
+
.sds-col--span-12 { grid-column: span 12; }
|
|
87
|
+
/* ── xs — < 800px ── */
|
|
88
|
+
@media (max-width: 799px) {
|
|
89
|
+
.sds-col--xs-1 { grid-column: span 1; }
|
|
90
|
+
.sds-col--xs-2 { grid-column: span 2; }
|
|
91
|
+
.sds-col--xs-3 { grid-column: span 3; }
|
|
92
|
+
.sds-col--xs-4 { grid-column: span 4; }
|
|
93
|
+
.sds-col--xs-5 { grid-column: span 5; }
|
|
94
|
+
.sds-col--xs-6 { grid-column: span 6; }
|
|
95
|
+
.sds-col--xs-7 { grid-column: span 7; }
|
|
96
|
+
.sds-col--xs-8 { grid-column: span 8; }
|
|
97
|
+
.sds-col--xs-9 { grid-column: span 9; }
|
|
98
|
+
.sds-col--xs-10 { grid-column: span 10; }
|
|
99
|
+
.sds-col--xs-11 { grid-column: span 11; }
|
|
100
|
+
.sds-col--xs-12 { grid-column: span 12; }
|
|
101
|
+
}
|
|
102
|
+
/* ── sm — 800px+ ── */
|
|
103
|
+
@media (min-width: 800px) {
|
|
104
|
+
.sds-col--sm-1 { grid-column: span 1; }
|
|
105
|
+
.sds-col--sm-2 { grid-column: span 2; }
|
|
106
|
+
.sds-col--sm-3 { grid-column: span 3; }
|
|
107
|
+
.sds-col--sm-4 { grid-column: span 4; }
|
|
108
|
+
.sds-col--sm-5 { grid-column: span 5; }
|
|
109
|
+
.sds-col--sm-6 { grid-column: span 6; }
|
|
110
|
+
.sds-col--sm-7 { grid-column: span 7; }
|
|
111
|
+
.sds-col--sm-8 { grid-column: span 8; }
|
|
112
|
+
.sds-col--sm-9 { grid-column: span 9; }
|
|
113
|
+
.sds-col--sm-10 { grid-column: span 10; }
|
|
114
|
+
.sds-col--sm-11 { grid-column: span 11; }
|
|
115
|
+
.sds-col--sm-12 { grid-column: span 12; }
|
|
116
|
+
}
|
|
117
|
+
/* ── md — 1024px+ ── */
|
|
118
|
+
@media (min-width: 1024px) {
|
|
119
|
+
.sds-col--md-1 { grid-column: span 1; }
|
|
120
|
+
.sds-col--md-2 { grid-column: span 2; }
|
|
121
|
+
.sds-col--md-3 { grid-column: span 3; }
|
|
122
|
+
.sds-col--md-4 { grid-column: span 4; }
|
|
123
|
+
.sds-col--md-5 { grid-column: span 5; }
|
|
124
|
+
.sds-col--md-6 { grid-column: span 6; }
|
|
125
|
+
.sds-col--md-7 { grid-column: span 7; }
|
|
126
|
+
.sds-col--md-8 { grid-column: span 8; }
|
|
127
|
+
.sds-col--md-9 { grid-column: span 9; }
|
|
128
|
+
.sds-col--md-10 { grid-column: span 10; }
|
|
129
|
+
.sds-col--md-11 { grid-column: span 11; }
|
|
130
|
+
.sds-col--md-12 { grid-column: span 12; }
|
|
131
|
+
}
|
|
132
|
+
/* ── lg — 1280px+ ── */
|
|
133
|
+
@media (min-width: 1280px) {
|
|
134
|
+
.sds-col--lg-1 { grid-column: span 1; }
|
|
135
|
+
.sds-col--lg-2 { grid-column: span 2; }
|
|
136
|
+
.sds-col--lg-3 { grid-column: span 3; }
|
|
137
|
+
.sds-col--lg-4 { grid-column: span 4; }
|
|
138
|
+
.sds-col--lg-5 { grid-column: span 5; }
|
|
139
|
+
.sds-col--lg-6 { grid-column: span 6; }
|
|
140
|
+
.sds-col--lg-7 { grid-column: span 7; }
|
|
141
|
+
.sds-col--lg-8 { grid-column: span 8; }
|
|
142
|
+
.sds-col--lg-9 { grid-column: span 9; }
|
|
143
|
+
.sds-col--lg-10 { grid-column: span 10; }
|
|
144
|
+
.sds-col--lg-11 { grid-column: span 11; }
|
|
145
|
+
.sds-col--lg-12 { grid-column: span 12; }
|
|
146
|
+
}
|
|
147
|
+
/* ── xl — 1440px+ ── */
|
|
148
|
+
@media (min-width: 1440px) {
|
|
149
|
+
.sds-col--xl-1 { grid-column: span 1; }
|
|
150
|
+
.sds-col--xl-2 { grid-column: span 2; }
|
|
151
|
+
.sds-col--xl-3 { grid-column: span 3; }
|
|
152
|
+
.sds-col--xl-4 { grid-column: span 4; }
|
|
153
|
+
.sds-col--xl-5 { grid-column: span 5; }
|
|
154
|
+
.sds-col--xl-6 { grid-column: span 6; }
|
|
155
|
+
.sds-col--xl-7 { grid-column: span 7; }
|
|
156
|
+
.sds-col--xl-8 { grid-column: span 8; }
|
|
157
|
+
.sds-col--xl-9 { grid-column: span 9; }
|
|
158
|
+
.sds-col--xl-10 { grid-column: span 10; }
|
|
159
|
+
.sds-col--xl-11 { grid-column: span 11; }
|
|
160
|
+
.sds-col--xl-12 { grid-column: span 12; }
|
|
161
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<!-- sds-col — ported from kdls-design-system v2.5.1 (0f3edf8) components/Grid/Grid.css.
|
|
2
|
+
|
|
3
|
+
A column inside sds-grid. `span` is its width at every size; xs, sm, md, lg and xl
|
|
4
|
+
override it from that breakpoint up — xl being 1440px and over.
|
|
5
|
+
|
|
6
|
+
The tag is named for the class the stylesheet already defines — .sds-col, not
|
|
7
|
+
.sds-grid__item. An earlier draft invented the latter and it matched nothing: a column
|
|
8
|
+
with no rule spans one track and the whole layout quietly collapses. -->
|
|
9
|
+
|
|
10
|
+
import { BREAKPOINTS } from "./grid-variants.js";
|
|
11
|
+
|
|
12
|
+
$ {
|
|
13
|
+
// `span = 12`, which is KDLS's default and the one this file's own header warns about:
|
|
14
|
+
// a column with no span rule falls to grid auto-placement and takes ONE track of twelve,
|
|
15
|
+
// so a <sds-col> written with no span at all — the obvious way to write "a full-width
|
|
16
|
+
// column" — came out a twelfth of the width.
|
|
17
|
+
//
|
|
18
|
+
// The five breakpoints are destructured rather than read as `input[bp]`. They were read
|
|
19
|
+
// dynamically, which works at runtime and is invisible to everything else: the portal
|
|
20
|
+
// builds its props table and its controls by parsing what a component destructures, so
|
|
21
|
+
// `xs` through `xl` appeared in neither. A prop nobody can see is a prop nobody uses.
|
|
22
|
+
const { span = 12, xs, sm, md, lg, xl, class: extraClass = "" } = input;
|
|
23
|
+
const breakpoints = { xs, sm, md, lg, xl };
|
|
24
|
+
const classes = [
|
|
25
|
+
"sds-col",
|
|
26
|
+
`sds-col--span-${span}`,
|
|
27
|
+
...BREAKPOINTS
|
|
28
|
+
.filter((bp) => breakpoints[bp] !== undefined)
|
|
29
|
+
.map((bp) => `sds-col--${bp}-${breakpoints[bp]}`),
|
|
30
|
+
extraClass,
|
|
31
|
+
].filter(Boolean).join(" ");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
<div class=classes>
|
|
35
|
+
<${input.renderBody}/>
|
|
36
|
+
</div>
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Grid vocabulary.
|
|
3
|
+
* Ported from kdls-design-system v2.5.1 (0f3edf8) components/Grid/Grid.tsx.
|
|
4
|
+
*/
|
|
5
|
+
export const GRID_COLS = [1, 2, 3, 4, 6, 12];
|
|
6
|
+
export const GRID_ALIGNMENTS = ['start', 'center', 'end', 'stretch'];
|
|
7
|
+
export const BREAKPOINTS = ['xs', 'sm', 'md', 'lg', 'xl'];
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Where each breakpoint starts, in pixels of viewport width.
|
|
11
|
+
*
|
|
12
|
+
* `Grid.css` is where these are enforced — CSS cannot read a custom property inside a
|
|
13
|
+
* `@media` condition, so the numbers have to be written there as literals. This map is
|
|
14
|
+
* for everything that needs to *know* them without being CSS: the portal's breakpoint
|
|
15
|
+
* strip, and any code deciding what is on screen.
|
|
16
|
+
*
|
|
17
|
+
* `tests/grid-breakpoints.test.js` parses Grid.css and fails if the two disagree. That
|
|
18
|
+
* test exists because they already did: the portal's strip was drawn at 600/900/1200,
|
|
19
|
+
* numbers from nowhere, so between 600px and 799px it announced `sm` while the grid was
|
|
20
|
+
* still laying out `xs`.
|
|
21
|
+
*/
|
|
22
|
+
export const BREAKPOINT_MIN_WIDTH = {
|
|
23
|
+
xs: 0,
|
|
24
|
+
sm: 800,
|
|
25
|
+
md: 1024,
|
|
26
|
+
lg: 1280,
|
|
27
|
+
xl: 1440,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export function assertCols(cols) {
|
|
31
|
+
if (!GRID_COLS.includes(cols)) {
|
|
32
|
+
throw new Error(`Unknown grid cols: "${cols}". Allowed: ${GRID_COLS.join(', ')}`);
|
|
33
|
+
}
|
|
34
|
+
return cols;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function assertAlign(align) {
|
|
38
|
+
if (align === undefined) return align;
|
|
39
|
+
if (!GRID_ALIGNMENTS.includes(align)) {
|
|
40
|
+
throw new Error(`Unknown grid align: "${align}". Allowed: ${GRID_ALIGNMENTS.join(', ')}`);
|
|
41
|
+
}
|
|
42
|
+
return align;
|
|
43
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<!-- sds-grid — ported from kdls-design-system v2.5.1 (0f3edf8) components/Grid/Grid.tsx.
|
|
2
|
+
|
|
3
|
+
Layout only, no semantics: a grid is not a list and should not claim to be one. -->
|
|
4
|
+
|
|
5
|
+
import { assertCols, assertAlign } from "./grid-variants.js";
|
|
6
|
+
|
|
7
|
+
$ {
|
|
8
|
+
const { cols = 12, gap, rowGap, colGap, align, class: extraClass = "" } = input;
|
|
9
|
+
assertCols(cols);
|
|
10
|
+
assertAlign(align);
|
|
11
|
+
|
|
12
|
+
const classes = [
|
|
13
|
+
"sds-grid",
|
|
14
|
+
`sds-grid--cols-${cols}`,
|
|
15
|
+
gap !== undefined ? `sds-grid--gap-${gap}` : "",
|
|
16
|
+
rowGap !== undefined ? `sds-grid--row-gap-${rowGap}` : "",
|
|
17
|
+
colGap !== undefined ? `sds-grid--col-gap-${colGap}` : "",
|
|
18
|
+
align ? `sds-grid--align-${align}` : "",
|
|
19
|
+
extraClass,
|
|
20
|
+
].filter(Boolean).join(" ");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
<div class=classes>
|
|
24
|
+
<${input.renderBody}/>
|
|
25
|
+
</div>
|