@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,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the Table knows that has nothing to do with the DOM.
|
|
3
|
+
*
|
|
4
|
+
* Ported from kdls-design-system v2.5.1 (0f3edf8) components/Table/Table.tsx. The grid
|
|
5
|
+
* template, the sort cycle, the selection toggles and the local sort are all pure, and all
|
|
6
|
+
* of them have an edge that is easy to get wrong and hard to see: a column with both a
|
|
7
|
+
* width and a minWidth, a sort that should clear rather than reverse, a "select all" on an
|
|
8
|
+
* empty table.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/** The checkbox column, when there is one. */
|
|
12
|
+
export const CHECK_TRACK = '16px';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The names the selection checkboxes carry.
|
|
16
|
+
*
|
|
17
|
+
* sds-checkbox does not hydrate, so the table listens for the native change event once and
|
|
18
|
+
* reads which box fired out of the input's own name and value. They double as the field
|
|
19
|
+
* names a form would submit.
|
|
20
|
+
*/
|
|
21
|
+
export const SELECT_ALL_NAME = 'sds-table-select-all';
|
|
22
|
+
export const SELECT_ROW_NAME = 'sds-table-select-row';
|
|
23
|
+
|
|
24
|
+
/** The value the mobile sort picker uses for "no sorting". */
|
|
25
|
+
export const NONE_SORT = '__none__';
|
|
26
|
+
|
|
27
|
+
export const SORT_DIRECTIONS = [
|
|
28
|
+
{ label: 'Crescente', value: 'asc' },
|
|
29
|
+
{ label: 'Decrescente', value: 'desc' },
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The mobile layout triggers off the table's own width, not the viewport's.
|
|
34
|
+
*
|
|
35
|
+
* A table in a narrow column is a narrow table even on a wide screen, and that is the case
|
|
36
|
+
* the stacked layout exists for. The number lives in foundations/breakpoints.js, because
|
|
37
|
+
* the Header asks the same question and was importing it from here.
|
|
38
|
+
*/
|
|
39
|
+
export { OWN_WIDTH_MOBILE as MOBILE_BREAKPOINT } from '../../foundations/breakpoints.js';
|
|
40
|
+
|
|
41
|
+
/** One grid track for one column. */
|
|
42
|
+
export function trackFor(column) {
|
|
43
|
+
if (column.width != null) {
|
|
44
|
+
return typeof column.width === 'number' ? `${column.width}px` : column.width;
|
|
45
|
+
}
|
|
46
|
+
if (column.minWidth != null || column.maxWidth != null) {
|
|
47
|
+
const min = column.minWidth != null ? `${column.minWidth}px` : '0px';
|
|
48
|
+
const max = column.maxWidth != null ? `${column.maxWidth}px` : '1fr';
|
|
49
|
+
return `minmax(${min}, ${max})`;
|
|
50
|
+
}
|
|
51
|
+
return '1fr';
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function gridTemplate(columns, hasCheckbox) {
|
|
55
|
+
const tracks = hasCheckbox ? [CHECK_TRACK] : [];
|
|
56
|
+
for (const column of columns) tracks.push(trackFor(column));
|
|
57
|
+
return tracks.join(' ');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* The next sort state for a header that was clicked.
|
|
62
|
+
*
|
|
63
|
+
* Three steps, not two: ascending, descending, then none. A sort that can only be reversed
|
|
64
|
+
* is a sort a visitor cannot undo.
|
|
65
|
+
*/
|
|
66
|
+
export function nextSort(current, key) {
|
|
67
|
+
if (current?.key !== key) return { key, direction: 'asc' };
|
|
68
|
+
if (current.direction === 'asc') return { key, direction: 'desc' };
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* The rows in order.
|
|
74
|
+
*
|
|
75
|
+
* `sortable: true` means the server sorts and the rows arrive sorted — the component only
|
|
76
|
+
* reports the state. Only a comparator function makes it sort here, which is KDLS's rule
|
|
77
|
+
* and the right one: a component that guesses how to compare a column gets dates wrong.
|
|
78
|
+
*/
|
|
79
|
+
export function sortRows(rows, columns, sort) {
|
|
80
|
+
if (!sort) return rows;
|
|
81
|
+
const column = columns.find((c) => c.key === sort.key);
|
|
82
|
+
if (!column || typeof column.sortable !== 'function') return rows;
|
|
83
|
+
const result = [...rows].sort(column.sortable);
|
|
84
|
+
if (sort.direction === 'desc') result.reverse();
|
|
85
|
+
return result;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export const rowId = (row, keyField = 'id') => String(row?.[keyField]);
|
|
89
|
+
|
|
90
|
+
/** Selection after one row was toggled. */
|
|
91
|
+
export function toggleId(selected, id) {
|
|
92
|
+
return selected.includes(id) ? selected.filter((x) => x !== id) : [...selected, id];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Selection after the header checkbox was toggled. */
|
|
96
|
+
export function toggleAll(rows, selected, keyField = 'id') {
|
|
97
|
+
const all = rows.map((row) => rowId(row, keyField));
|
|
98
|
+
const everySelected = all.length > 0 && all.every((id) => selected.includes(id));
|
|
99
|
+
return everySelected ? [] : all;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** The header checkbox's state: checked, indeterminate, or neither. */
|
|
103
|
+
export function headerCheckState(rows, selected, keyField = 'id') {
|
|
104
|
+
const all = rows.map((row) => rowId(row, keyField));
|
|
105
|
+
// An empty table has nothing selected, not everything: `every` on an empty array is true,
|
|
106
|
+
// which would draw a ticked "select all" over no rows at all.
|
|
107
|
+
if (!all.length) return { checked: false, indeterminate: false };
|
|
108
|
+
const chosen = all.filter((id) => selected.includes(id));
|
|
109
|
+
if (chosen.length === all.length) return { checked: true, indeterminate: false };
|
|
110
|
+
return { checked: false, indeterminate: chosen.length > 0 };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** The options the mobile sort picker offers. */
|
|
114
|
+
export function sortFieldOptions(columns) {
|
|
115
|
+
return [
|
|
116
|
+
{ label: 'Nenhuma', value: NONE_SORT },
|
|
117
|
+
...columns.filter((c) => Boolean(c.sortable)).map((c) => ({
|
|
118
|
+
label: c.title || c.key,
|
|
119
|
+
value: c.key,
|
|
120
|
+
})),
|
|
121
|
+
];
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** What a column header should tell a screen reader about its own sorting. */
|
|
125
|
+
export function ariaSortFor(column, sort) {
|
|
126
|
+
if (!column.sortable) return null;
|
|
127
|
+
if (sort?.key !== column.key) return 'none';
|
|
128
|
+
return sort.direction === 'asc' ? 'ascending' : 'descending';
|
|
129
|
+
}
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
<!-- sds-table — ported from kdls-design-system v2.5.1 (0f3edf8) components/Table/Table.tsx.
|
|
2
|
+
|
|
3
|
+
A CSS-grid table: sortable headers, row selection, a stacked layout when the table
|
|
4
|
+
itself is narrow, and pagination. The arithmetic — the grid template, the sort cycle,
|
|
5
|
+
the selection toggles — is in table-logic.js and tested there.
|
|
6
|
+
|
|
7
|
+
The stacked layout triggers off the table's own width, not the viewport's, which is
|
|
8
|
+
KDLS's decision and the right one: a table inside a narrow column is a narrow table on
|
|
9
|
+
any screen.
|
|
10
|
+
|
|
11
|
+
Divergences:
|
|
12
|
+
|
|
13
|
+
Rows are <div>s here as in KDLS, because the layout is a CSS grid and a real <table>
|
|
14
|
+
cannot be one without losing the column alignment the design depends on. What is added
|
|
15
|
+
is the semantics the grid throws away: role="table", "row", "columnheader" and "cell",
|
|
16
|
+
so a screen reader is told how many columns there are and which is which. KDLS renders
|
|
17
|
+
bare divs, so a table of forty cells is forty unrelated pieces of text.
|
|
18
|
+
|
|
19
|
+
A sortable header says aria-sort, which is the only way a screen reader can tell
|
|
20
|
+
whether a column is sorted and which way. KDLS draws an arrow and says nothing.
|
|
21
|
+
|
|
22
|
+
The sort cycles ascending, descending, none — KDLS's behaviour, kept because a sort
|
|
23
|
+
that can only be reversed is one a visitor cannot undo.
|
|
24
|
+
|
|
25
|
+
A clickable row stays a <div role="button">, not a <button>: a real button cannot
|
|
26
|
+
contain the checkboxes, links and menus that live in these cells. The keyboard handling
|
|
27
|
+
KDLS writes by hand is kept for the same reason — this is one of the few places where
|
|
28
|
+
the hand-written version is the only version available. -->
|
|
29
|
+
|
|
30
|
+
import {
|
|
31
|
+
gridTemplate, nextSort, sortRows, rowId, toggleId, toggleAll, headerCheckState,
|
|
32
|
+
sortFieldOptions, ariaSortFor, SORT_DIRECTIONS, NONE_SORT, MOBILE_BREAKPOINT,
|
|
33
|
+
SELECT_ALL_NAME, SELECT_ROW_NAME,
|
|
34
|
+
} from "./table-logic.js";
|
|
35
|
+
import chevronUp from "../../icons/generated/fluent-chevron-up-regular.marko";
|
|
36
|
+
import chevronDown from "../../icons/generated/fluent-chevron-down-regular.marko";
|
|
37
|
+
|
|
38
|
+
class {
|
|
39
|
+
onCreate(input) {
|
|
40
|
+
this.state = { sort: input.defaultSort ?? null, narrow: false };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
onMount() {
|
|
44
|
+
const root = this.getEl("root");
|
|
45
|
+
if (!root) return;
|
|
46
|
+
const measure = (width) => { this.state.narrow = width > 0 && width <= MOBILE_BREAKPOINT; };
|
|
47
|
+
measure(root.getBoundingClientRect().width);
|
|
48
|
+
if (typeof ResizeObserver === "undefined") return;
|
|
49
|
+
this.observer = new ResizeObserver(([entry]) => measure(entry.contentRect.width));
|
|
50
|
+
this.observer.observe(root);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
onDestroy() {
|
|
54
|
+
this.observer?.disconnect();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
get sort() {
|
|
58
|
+
return this.input.sort !== undefined ? this.input.sort : this.state.sort;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
commitSort(next) {
|
|
62
|
+
if (this.input.sort === undefined) this.state.sort = next;
|
|
63
|
+
this.input.onSortChange?.(next);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
sortBy(column) {
|
|
67
|
+
if (!column.sortable) return;
|
|
68
|
+
this.commitSort(nextSort(this.sort, column.key));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
sortByKey(key) {
|
|
72
|
+
if (key === NONE_SORT) {
|
|
73
|
+
this.commitSort(null);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
this.commitSort({ key, direction: this.sort?.direction ?? "asc" });
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
sortDirection(direction) {
|
|
80
|
+
if (!this.sort) return;
|
|
81
|
+
this.commitSort({ key: this.sort.key, direction });
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* The checkboxes report here, not one by one.
|
|
86
|
+
*
|
|
87
|
+
* sds-checkbox does not hydrate on purpose — it takes no onChange and lets the native
|
|
88
|
+
* change event bubble, which is what keeps a table of forty rows from serialising forty
|
|
89
|
+
* components. So the table listens once and reads which box fired out of the input's own
|
|
90
|
+
* name and value, which it needs for form submission anyway.
|
|
91
|
+
*/
|
|
92
|
+
handleChange(event) {
|
|
93
|
+
const target = event.target;
|
|
94
|
+
if (target.type !== "checkbox") return;
|
|
95
|
+
const { rows = [], selectedIds = [], keyField = "id", onSelectionChange } = this.input;
|
|
96
|
+
if (target.name === SELECT_ALL_NAME) {
|
|
97
|
+
onSelectionChange?.(toggleAll(rows, selectedIds, keyField));
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (target.name === SELECT_ROW_NAME) {
|
|
101
|
+
onSelectionChange?.(toggleId(selectedIds, target.value));
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* A click that landed on a control inside the cell belongs to that control.
|
|
107
|
+
*
|
|
108
|
+
* The element comes in as the second argument because Marko delegates events:
|
|
109
|
+
* `event.currentTarget` is not the element the handler was written on.
|
|
110
|
+
*/
|
|
111
|
+
clickRow(event, element, row) {
|
|
112
|
+
if (event.target !== element
|
|
113
|
+
&& event.target.closest("button, a, input, select, textarea, label")) return;
|
|
114
|
+
this.input.onRowClick?.(row);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
rowKey(event, element, row) {
|
|
118
|
+
if (event.key !== "Enter" && event.key !== " ") return;
|
|
119
|
+
if (event.target !== element) return;
|
|
120
|
+
event.preventDefault();
|
|
121
|
+
this.input.onRowClick?.(row);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
$ {
|
|
126
|
+
const {
|
|
127
|
+
columns = [],
|
|
128
|
+
rows = [],
|
|
129
|
+
variant = "default",
|
|
130
|
+
bordered = false,
|
|
131
|
+
keyField = "id",
|
|
132
|
+
rowSelection = false,
|
|
133
|
+
selectedIds = [],
|
|
134
|
+
onRowClick,
|
|
135
|
+
pagination,
|
|
136
|
+
emptyMessage = "Nenhum resultado",
|
|
137
|
+
label = "Tabela",
|
|
138
|
+
class: extraClass = "",
|
|
139
|
+
} = input;
|
|
140
|
+
|
|
141
|
+
if (!columns.length) throw new Error("sds-table needs `columns`.");
|
|
142
|
+
if (variant !== "default" && variant !== "cardLines") {
|
|
143
|
+
throw new Error(`Unknown table variant: "${variant}". Allowed: default, cardLines`);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const sort = component.sort;
|
|
147
|
+
const ordered = sortRows(rows, columns, sort);
|
|
148
|
+
const narrow = state.narrow;
|
|
149
|
+
const check = headerCheckState(rows, selectedIds, keyField);
|
|
150
|
+
const template = gridTemplate(columns, rowSelection);
|
|
151
|
+
const rowStyle = narrow ? "grid-template-columns:1fr" : `grid-template-columns:${template}`;
|
|
152
|
+
const clickable = Boolean(onRowClick);
|
|
153
|
+
const sortable = columns.filter((c) => Boolean(c.sortable));
|
|
154
|
+
|
|
155
|
+
const renderable = (value) => (value && value.renderBody) || value;
|
|
156
|
+
const cellContent = (column, row, index) => (
|
|
157
|
+
column.render ? column.render(row[column.key], row, index) : row[column.key]
|
|
158
|
+
);
|
|
159
|
+
// A cell is usually text. `<${"Casa"}/>` renders an *element named Casa*, so the two
|
|
160
|
+
// cases cannot share a branch — the same trap the Popover's title hit.
|
|
161
|
+
const isMarkup = (value) => value !== null && typeof value === "object";
|
|
162
|
+
|
|
163
|
+
const rootClass = [
|
|
164
|
+
"sds-table",
|
|
165
|
+
variant !== "default" ? `sds-table--${variant}` : "",
|
|
166
|
+
bordered ? "sds-table--bordered" : "",
|
|
167
|
+
extraClass,
|
|
168
|
+
].filter(Boolean).join(" ");
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
<div key="root" class=rootClass role="table" aria-label=label aria-rowcount=String(ordered.length) on-change("handleChange")>
|
|
172
|
+
<if(input.header)>
|
|
173
|
+
<${input.header}/>
|
|
174
|
+
</if>
|
|
175
|
+
|
|
176
|
+
<div class="sds-table__list sds-scroll sds-scroll--thin">
|
|
177
|
+
<if(narrow)>
|
|
178
|
+
<!-- The label row is gone with the columns, so sorting moves into two pickers driven
|
|
179
|
+
by the same state. -->
|
|
180
|
+
<if(sortable.length)>
|
|
181
|
+
<div class="sds-table__sort-picker">
|
|
182
|
+
<!-- "Filtro" and not "Ordenar por", and no value while nothing is sorted.
|
|
183
|
+
|
|
184
|
+
This chip only exists on a narrow screen, where it stands in for the column
|
|
185
|
+
headers that have gone: to the reader it is the filter row of the listing,
|
|
186
|
+
which is what Rafael named it. And a pill with no value shows its own label,
|
|
187
|
+
so an unsorted table reads "Filtro" rather than "Nenhuma" — which was the
|
|
188
|
+
NONE row showing as the current choice, complete with a clear button for a
|
|
189
|
+
choice nobody had made. "Nenhuma" stays in the list, as the way back out.
|
|
190
|
+
|
|
191
|
+
Empty string and not `undefined` for the unsorted case: `undefined` is how
|
|
192
|
+
a Select is told it is uncontrolled, so it kept the last choice in its own
|
|
193
|
+
state and the chip went on saying "Cidade" after the sort was cleared. The
|
|
194
|
+
empty string is a value, and it is the one that means nothing is chosen. -->
|
|
195
|
+
<sds-select
|
|
196
|
+
trigger="pill"
|
|
197
|
+
label="Filtro"
|
|
198
|
+
width="hug"
|
|
199
|
+
options=sortFieldOptions(columns)
|
|
200
|
+
value=(sort ? sort.key : "")
|
|
201
|
+
onChange=(value) => component.sortByKey(value)
|
|
202
|
+
/>
|
|
203
|
+
<if(sort)>
|
|
204
|
+
<sds-select
|
|
205
|
+
trigger="pill"
|
|
206
|
+
label="Direção"
|
|
207
|
+
width="hug"
|
|
208
|
+
options=SORT_DIRECTIONS
|
|
209
|
+
value=sort.direction
|
|
210
|
+
onChange=(value) => component.sortDirection(value)
|
|
211
|
+
/>
|
|
212
|
+
</if>
|
|
213
|
+
</div>
|
|
214
|
+
</if>
|
|
215
|
+
</if>
|
|
216
|
+
<else>
|
|
217
|
+
<div class="sds-table__row sds-table__row--header" role="row" style=rowStyle>
|
|
218
|
+
<if(rowSelection)>
|
|
219
|
+
<div class="sds-table__col sds-table__col--check" role="columnheader">
|
|
220
|
+
<sds-checkbox
|
|
221
|
+
label="Selecionar todas as linhas"
|
|
222
|
+
hideLabel
|
|
223
|
+
name=SELECT_ALL_NAME
|
|
224
|
+
checked=check.checked
|
|
225
|
+
indeterminate=check.indeterminate
|
|
226
|
+
/>
|
|
227
|
+
</div>
|
|
228
|
+
</if>
|
|
229
|
+
|
|
230
|
+
<for|column| of=columns>
|
|
231
|
+
<div
|
|
232
|
+
class=[
|
|
233
|
+
"sds-table__col",
|
|
234
|
+
column.sortable ? "sds-table__col--sortable" : "",
|
|
235
|
+
column.align === "center" ? "sds-table__col--center" : "",
|
|
236
|
+
column.align === "end" ? "sds-table__col--end" : "",
|
|
237
|
+
].filter(Boolean).join(" ")
|
|
238
|
+
role="columnheader"
|
|
239
|
+
aria-sort=ariaSortFor(column, sort)
|
|
240
|
+
>
|
|
241
|
+
<if(column.sortable)>
|
|
242
|
+
<!-- A button, so the header can be reached and pressed from the keyboard.
|
|
243
|
+
aria-sort goes on the cell above it, which is where a reader looks. -->
|
|
244
|
+
<button type="button" class="sds-table__sort-button" on-click("sortBy", column)>
|
|
245
|
+
<span class="sds-table__col-title">${column.title ?? ""}</span>
|
|
246
|
+
<!-- Both arrows, with the live one coloured — KDLS's shape, and the
|
|
247
|
+
right one: a single arrow leaves a visitor guessing whether the
|
|
248
|
+
column is sorted at all or just sortable. -->
|
|
249
|
+
<span class="sds-table__sort" aria-hidden="true">
|
|
250
|
+
<sds-icon size="sm" color=(sort?.key === column.key && sort.direction === "asc" ? "primary" : undefined)>
|
|
251
|
+
<${chevronUp}/>
|
|
252
|
+
</sds-icon>
|
|
253
|
+
<sds-icon size="sm" color=(sort?.key === column.key && sort.direction === "desc" ? "primary" : undefined)>
|
|
254
|
+
<${chevronDown}/>
|
|
255
|
+
</sds-icon>
|
|
256
|
+
</span>
|
|
257
|
+
</button>
|
|
258
|
+
</if>
|
|
259
|
+
<else>
|
|
260
|
+
<span class="sds-table__col-title">${column.title ?? ""}</span>
|
|
261
|
+
</else>
|
|
262
|
+
</div>
|
|
263
|
+
</for>
|
|
264
|
+
</div>
|
|
265
|
+
</else>
|
|
266
|
+
|
|
267
|
+
<if(!ordered.length)>
|
|
268
|
+
<div class="sds-table__empty" role="row">
|
|
269
|
+
<div role="cell">${emptyMessage}</div>
|
|
270
|
+
</div>
|
|
271
|
+
</if>
|
|
272
|
+
|
|
273
|
+
<for|row, index| of=ordered>
|
|
274
|
+
<div
|
|
275
|
+
key=rowId(row, keyField)
|
|
276
|
+
class=[
|
|
277
|
+
"sds-table__row",
|
|
278
|
+
selectedIds.includes(rowId(row, keyField)) ? "sds-table__row--selected" : "",
|
|
279
|
+
clickable ? "sds-table__row--clickable" : "",
|
|
280
|
+
].filter(Boolean).join(" ")
|
|
281
|
+
role="row"
|
|
282
|
+
style=rowStyle
|
|
283
|
+
aria-selected=(rowSelection ? (selectedIds.includes(rowId(row, keyField)) ? "true" : "false") : null)
|
|
284
|
+
tabindex=(clickable ? 0 : null)
|
|
285
|
+
on-click(clickable ? (event, element) => component.clickRow(event, element, row) : null)
|
|
286
|
+
on-keydown(clickable ? (event, element) => component.rowKey(event, element, row) : null)
|
|
287
|
+
>
|
|
288
|
+
<if(rowSelection)>
|
|
289
|
+
<div class="sds-table__col sds-table__col--check" role="cell">
|
|
290
|
+
<sds-checkbox
|
|
291
|
+
label=`Selecionar linha ${index + 1}`
|
|
292
|
+
hideLabel
|
|
293
|
+
name=SELECT_ROW_NAME
|
|
294
|
+
value=rowId(row, keyField)
|
|
295
|
+
checked=selectedIds.includes(rowId(row, keyField))
|
|
296
|
+
/>
|
|
297
|
+
</div>
|
|
298
|
+
</if>
|
|
299
|
+
|
|
300
|
+
<if(narrow)>
|
|
301
|
+
<for|column| of=columns.filter((c) => c.mobileAction === "icon")>
|
|
302
|
+
<div class="sds-table__mobile-icon-actions" role="cell">
|
|
303
|
+
<if(isMarkup(cellContent(column, row, index)))>
|
|
304
|
+
<${renderable(cellContent(column, row, index))}/>
|
|
305
|
+
</if>
|
|
306
|
+
<else>${cellContent(column, row, index)}</else>
|
|
307
|
+
</div>
|
|
308
|
+
</for>
|
|
309
|
+
</if>
|
|
310
|
+
|
|
311
|
+
<for|column| of=(narrow ? columns.filter((c) => !c.mobileAction) : columns)>
|
|
312
|
+
<div
|
|
313
|
+
class=[
|
|
314
|
+
"sds-table__col",
|
|
315
|
+
!narrow && column.align === "center" ? "sds-table__col--center" : "",
|
|
316
|
+
!narrow && column.align === "end" ? "sds-table__col--end" : "",
|
|
317
|
+
column.ellipsis ? "sds-table__col--ellipsis" : "",
|
|
318
|
+
].filter(Boolean).join(" ")
|
|
319
|
+
role="cell"
|
|
320
|
+
>
|
|
321
|
+
<if(column.ellipsis)>
|
|
322
|
+
<sds-tooltip content=String(row[column.key] ?? "")>
|
|
323
|
+
<span class="sds-table__ellipsis">
|
|
324
|
+
<if(isMarkup(cellContent(column, row, index)))>
|
|
325
|
+
<${renderable(cellContent(column, row, index))}/>
|
|
326
|
+
</if>
|
|
327
|
+
<else>${cellContent(column, row, index)}</else>
|
|
328
|
+
</span>
|
|
329
|
+
</sds-tooltip>
|
|
330
|
+
</if>
|
|
331
|
+
<else>
|
|
332
|
+
<if(isMarkup(cellContent(column, row, index)))>
|
|
333
|
+
<${renderable(cellContent(column, row, index))}/>
|
|
334
|
+
</if>
|
|
335
|
+
<else>${cellContent(column, row, index)}</else>
|
|
336
|
+
</else>
|
|
337
|
+
</div>
|
|
338
|
+
</for>
|
|
339
|
+
|
|
340
|
+
<if(narrow && columns.some((c) => c.mobileAction === "link"))>
|
|
341
|
+
<div class=[
|
|
342
|
+
"sds-table__mobile-link-actions",
|
|
343
|
+
columns.filter((c) => c.mobileAction === "link").length > 1
|
|
344
|
+
? "sds-table__mobile-link-actions--split" : "",
|
|
345
|
+
].filter(Boolean).join(" ")>
|
|
346
|
+
<for|column| of=columns.filter((c) => c.mobileAction === "link")>
|
|
347
|
+
<div class="sds-table__mobile-link-action" role="cell">
|
|
348
|
+
<if(isMarkup(cellContent(column, row, index)))>
|
|
349
|
+
<${renderable(cellContent(column, row, index))}/>
|
|
350
|
+
</if>
|
|
351
|
+
<else>${cellContent(column, row, index)}</else>
|
|
352
|
+
</div>
|
|
353
|
+
</for>
|
|
354
|
+
</div>
|
|
355
|
+
</if>
|
|
356
|
+
</div>
|
|
357
|
+
</for>
|
|
358
|
+
</div>
|
|
359
|
+
|
|
360
|
+
<if(pagination)>
|
|
361
|
+
<sds-pagination
|
|
362
|
+
currentPage=pagination.currentPage
|
|
363
|
+
totalItems=pagination.totalItems
|
|
364
|
+
pageSize=pagination.pageSize
|
|
365
|
+
siblingCount=pagination.siblingCount
|
|
366
|
+
showSizeChanger=pagination.showSizeChanger
|
|
367
|
+
hideOnSinglePage=pagination.hideOnSinglePage
|
|
368
|
+
disabled=pagination.disabled
|
|
369
|
+
onChange=pagination.onChange
|
|
370
|
+
/>
|
|
371
|
+
</if>
|
|
372
|
+
</div>
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/* SDS Text.
|
|
2
|
+
Ported from kdls-design-system v2.5.1 (0f3edf8) components/Text/Text.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-text--primary
|
|
7
|
+
*//* ============================================================
|
|
8
|
+
SDS Text
|
|
9
|
+
Semantic typography: `variant` brings font-size + line-height +
|
|
10
|
+
weight + color (tokens); the tag (`as`) is the dev's choice, it doesn't change the style.
|
|
11
|
+
============================================================ */
|
|
12
|
+
|
|
13
|
+
.sds-text {
|
|
14
|
+
margin: 0;
|
|
15
|
+
font-family: var(--font-family-base);
|
|
16
|
+
}
|
|
17
|
+
/* ── Variants: font-size + line-height + weight (tokens) ── */
|
|
18
|
+
.sds-text--displayLg { font-size: var(--font-size-display-lg); line-height: var(--lh-display-lg); font-weight: var(--font-weight-bold); }
|
|
19
|
+
.sds-text--displayMd { font-size: var(--font-size-display-md); line-height: var(--lh-display-md); font-weight: var(--font-weight-bold); }
|
|
20
|
+
.sds-text--displaySm { font-size: var(--font-size-display-sm); line-height: var(--lh-display-sm); font-weight: var(--font-weight-bold); }
|
|
21
|
+
.sds-text--titleLg { font-size: var(--font-size-title-lg); line-height: var(--lh-title-lg); font-weight: var(--font-weight-bold); }
|
|
22
|
+
.sds-text--titleMd { font-size: var(--font-size-title-md); line-height: var(--lh-title-md); font-weight: var(--font-weight-semibold); }
|
|
23
|
+
.sds-text--titleSm { font-size: var(--font-size-title-sm); line-height: var(--lh-title-sm); font-weight: var(--font-weight-semibold); }
|
|
24
|
+
.sds-text--bodyMd { font-size: var(--font-size-body-md); line-height: var(--lh-body-md); font-weight: var(--font-weight-regular); }
|
|
25
|
+
.sds-text--bodySm { font-size: var(--font-size-body-sm); line-height: var(--lh-body-sm); font-weight: var(--font-weight-regular); }
|
|
26
|
+
.sds-text--labelSemiBold { font-size: var(--font-size-label-sm); line-height: var(--lh-label-sm); font-weight: var(--font-weight-semibold); }
|
|
27
|
+
.sds-text--labelRegular { font-size: var(--font-size-label-sm); line-height: var(--lh-label-sm); font-weight: var(--font-weight-regular); }
|
|
28
|
+
/* ── Default color per category — each variant group has its own token,
|
|
29
|
+
even where the value matches another group's today, so they can diverge later ── */
|
|
30
|
+
.sds-text--displayLg,
|
|
31
|
+
.sds-text--displayMd,
|
|
32
|
+
.sds-text--displaySm { color: var(--display-color); }
|
|
33
|
+
.sds-text--titleLg,
|
|
34
|
+
.sds-text--titleMd,
|
|
35
|
+
.sds-text--titleSm { color: var(--title-color); }
|
|
36
|
+
.sds-text--bodyMd,
|
|
37
|
+
.sds-text--bodySm { color: var(--body-color); }
|
|
38
|
+
.sds-text--labelSemiBold,
|
|
39
|
+
.sds-text--labelRegular { color: var(--label-color); }
|
|
40
|
+
/* ── Colors (overrides the default color via the color prop) ── */
|
|
41
|
+
.sds-text--inherit { color: inherit; }
|
|
42
|
+
.sds-text--primary { color: var(--color-primary-50-solid); }
|
|
43
|
+
.sds-text--error { color: var(--color-error-50); }
|
|
44
|
+
.sds-text--success { color: var(--color-success-50); }
|
|
45
|
+
.sds-text--alert { color: var(--color-alert-50); }
|
|
46
|
+
|
|
47
|
+
/* ── color="primary" under a gradient ──────────────────────────────────────────
|
|
48
|
+
KDLS had a rule making color="primary" paint as a gradient when the active scheme was
|
|
49
|
+
lya, because lya's primary WAS a gradient. That scheme is gone, but the capability is
|
|
50
|
+
not: applyCustomGradient can put a gradient in the same slot.
|
|
51
|
+
|
|
52
|
+
The clip is layered on top of a solid colour rather than replacing it, so a browser
|
|
53
|
+
without background-clip:text still renders visible text instead of a transparent hole.
|
|
54
|
+
In solid mode the clip paints the same colour the fallback already set. */
|
|
55
|
+
@supports (background-clip: text) or (-webkit-background-clip: text) {
|
|
56
|
+
.sds-text--primary {
|
|
57
|
+
background: var(--color-primary-50);
|
|
58
|
+
-webkit-background-clip: text;
|
|
59
|
+
background-clip: text;
|
|
60
|
+
color: transparent;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/* ── Forced colours ────────────────────────────────────────────────────────────
|
|
65
|
+
Windows High Contrast, and any other forced-colours mode, throws the page's colours
|
|
66
|
+
away and paints its own — including dropping backgrounds. `color: transparent` with
|
|
67
|
+
`background-clip: text` then has nothing left to show through it, and the text becomes
|
|
68
|
+
INVISIBLE rather than merely differently coloured.
|
|
69
|
+
|
|
70
|
+
The clip is how `color="primary"` paints a gradient when the slot holds one. It stops
|
|
71
|
+
being worth anything in a mode that has already discarded the gradient, so it is undone
|
|
72
|
+
here and the text goes back to being text. */
|
|
73
|
+
@media (forced-colors: active) {
|
|
74
|
+
.sds-text--primary {
|
|
75
|
+
background: none;
|
|
76
|
+
color: CanvasText;
|
|
77
|
+
forced-color-adjust: auto;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Text vocabulary.
|
|
3
|
+
*
|
|
4
|
+
* Ported from kdls-design-system v2.5.1 (0f3edf8) components/Text/Text.tsx.
|
|
5
|
+
* Divergence: none. Text carries no brand-specific value — `primary` is the slot, which
|
|
6
|
+
* is exactly what it was in KDLS.
|
|
7
|
+
*/
|
|
8
|
+
export const TEXT_VARIANTS = [
|
|
9
|
+
'displayLg', 'displayMd', 'displaySm',
|
|
10
|
+
'titleLg', 'titleMd', 'titleSm',
|
|
11
|
+
'bodyMd', 'bodySm',
|
|
12
|
+
'labelSemiBold', 'labelRegular',
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
export const TEXT_COLORS = ['default', 'inherit', 'primary', 'error', 'success', 'alert'];
|
|
16
|
+
|
|
17
|
+
function assertOneOf(label, value, allowed) {
|
|
18
|
+
if (!allowed.includes(value)) {
|
|
19
|
+
throw new Error(`Unknown text ${label}: "${value}". Allowed: ${allowed.join(', ')}`);
|
|
20
|
+
}
|
|
21
|
+
return value;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const assertTextVariant = (v) => assertOneOf('variant', v, TEXT_VARIANTS);
|
|
25
|
+
export const assertTextColor = (v) => assertOneOf('color', v, TEXT_COLORS);
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<!-- sds-text — ported from kdls-design-system v2.5.1 (0f3edf8) components/Text/Text.tsx.
|
|
2
|
+
|
|
3
|
+
Semantic typography: `variant` brings the size, line height and weight; `as` is the
|
|
4
|
+
element the developer chooses and changes nothing visual.
|
|
5
|
+
|
|
6
|
+
Divergences:
|
|
7
|
+
- `as` is required, as in KDLS, and is validated: rendering a fallback element
|
|
8
|
+
silently would put the wrong tag in the document outline, which is the one thing
|
|
9
|
+
this component exists to get right.
|
|
10
|
+
- no state, no forwardRef. Marko passes the element through directly.
|
|
11
|
+
- `htmlFor` does not exist here, and should not: it is React's name for the HTML
|
|
12
|
+
`for` attribute, invented because `for` is a reserved word in JSX. Marko has no
|
|
13
|
+
such problem, so a label writes `for` and it reaches the element through the
|
|
14
|
+
attribute spread like any other.
|
|
15
|
+
- color="primary" reaches a gradient through applyCustomGradient rather than
|
|
16
|
+
through a lya scheme — see Text.css. -->
|
|
17
|
+
|
|
18
|
+
import { assertTextVariant, assertTextColor } from "./text-variants.js";
|
|
19
|
+
import { rehyphenate } from "../../foundations/attrs.js";
|
|
20
|
+
|
|
21
|
+
$ {
|
|
22
|
+
const {
|
|
23
|
+
as: tag,
|
|
24
|
+
variant = "bodyMd",
|
|
25
|
+
color = "default",
|
|
26
|
+
class: extraClass = "",
|
|
27
|
+
...rest
|
|
28
|
+
} = input;
|
|
29
|
+
|
|
30
|
+
// Marko camelCases a hyphenated attribute written on a component tag, and spreading that
|
|
31
|
+
// straight onto an element writes `arialive` instead of `aria-live`. See foundations/attrs.js.
|
|
32
|
+
const attributes = rehyphenate(rest);
|
|
33
|
+
|
|
34
|
+
if (!tag) throw new Error('sds-text needs an `as` attribute — the element to render.');
|
|
35
|
+
assertTextVariant(variant);
|
|
36
|
+
assertTextColor(color);
|
|
37
|
+
|
|
38
|
+
const classes = [
|
|
39
|
+
"sds-text",
|
|
40
|
+
`sds-text--${variant}`,
|
|
41
|
+
color !== "default" ? `sds-text--${color}` : "",
|
|
42
|
+
extraClass,
|
|
43
|
+
].filter(Boolean).join(" ");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
<${tag} class=classes ...attributes>
|
|
47
|
+
<${input.renderBody}/>
|
|
48
|
+
</>
|