@ethlete/components 1.0.0-next.78 → 1.0.0-next.79
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/CHANGELOG.md +113 -0
- package/design/accordion.ts +73 -0
- package/design/avatar.ts +90 -0
- package/design/badge.ts +48 -0
- package/design/banner.ts +77 -0
- package/design/bracket.ts +457 -0
- package/design/breadcrumb.ts +145 -0
- package/design/button.ts +49 -0
- package/design/calendar.ts +306 -0
- package/design/card.ts +92 -0
- package/design/carousel.ts +240 -0
- package/design/chart.ts +512 -0
- package/design/checkbox.ts +275 -0
- package/design/chip.ts +64 -0
- package/design/config.json +4 -1
- package/design/control.ts +19 -0
- package/design/copy-button.ts +59 -0
- package/design/description-list.ts +41 -0
- package/design/dialog.ts +214 -0
- package/design/divider.ts +27 -0
- package/design/empty-state.ts +45 -0
- package/design/floating-action.ts +55 -0
- package/design/grid.ts +148 -0
- package/design/input.ts +86 -0
- package/design/kbd.ts +19 -0
- package/design/loader.ts +152 -0
- package/design/masonry.ts +115 -0
- package/design/match.ts +131 -0
- package/design/menu.ts +143 -0
- package/design/notification.ts +129 -0
- package/design/otp-input.ts +107 -0
- package/design/pagination.ts +120 -0
- package/design/picture.ts +80 -0
- package/design/progress-steps.ts +79 -0
- package/design/query-error.ts +60 -0
- package/design/radio-group.ts +132 -0
- package/design/scheduler.ts +369 -0
- package/design/scrollable.ts +218 -0
- package/design/scrollbar.ts +47 -0
- package/design/sdk/assets/fallback.svg +1 -0
- package/design/sdk/assets/tall.svg +1 -0
- package/design/sdk/assets/wide.svg +1 -0
- package/design/sdk/components/accent-on-light/accent-on-light.css +35 -0
- package/design/sdk/components/accent-on-light/accent-on-light.ts +10 -0
- package/design/sdk/components/accent-on-light/shared/controls.ts +46 -0
- package/design/sdk/components/accordion/accordion.css +17 -0
- package/design/sdk/components/accordion/accordion.ts +64 -0
- package/design/sdk/components/avatar/avatar.css +14 -0
- package/design/sdk/components/avatar/avatar.ts +78 -0
- package/design/sdk/components/badge/badge.css +13 -0
- package/design/sdk/components/badge/badge.ts +52 -0
- package/design/sdk/components/banner/banner.css +18 -0
- package/design/sdk/components/banner/banner.ts +87 -0
- package/design/sdk/components/bracket/bracket.css +59 -0
- package/design/sdk/components/bracket/bracket.ts +143 -0
- package/design/sdk/components/breadcrumb/breadcrumb.css +25 -0
- package/design/sdk/components/breadcrumb/breadcrumb.ts +81 -0
- package/design/sdk/components/button/button.css +33 -0
- package/design/sdk/components/button/button.ts +50 -7
- package/design/sdk/components/calendar/calendar.css +33 -0
- package/design/sdk/components/calendar/calendar.ts +96 -0
- package/design/sdk/components/card/card.css +19 -0
- package/design/sdk/components/card/card.ts +61 -0
- package/design/sdk/components/carousel/carousel.css +44 -0
- package/design/sdk/components/carousel/carousel.ts +74 -0
- package/design/sdk/components/chart/chart.css +56 -0
- package/design/sdk/components/chart/chart.ts +98 -0
- package/design/sdk/components/chip/chip.css +18 -0
- package/design/sdk/components/chip/chip.ts +76 -0
- package/design/sdk/components/copy-button/copy-button.css +14 -0
- package/design/sdk/components/copy-button/copy-button.ts +59 -0
- package/design/sdk/components/description-list/description-list.css +7 -0
- package/design/sdk/components/description-list/description-list.ts +53 -0
- package/design/sdk/components/dialog/dialog.css +29 -0
- package/design/sdk/components/dialog/dialog.ts +78 -0
- package/design/sdk/components/divider/divider.css +26 -0
- package/design/sdk/components/divider/divider.ts +43 -0
- package/design/sdk/components/empty-state/empty-state.css +15 -0
- package/design/sdk/components/empty-state/empty-state.ts +56 -0
- package/design/sdk/components/floating-action/floating-action.css +51 -0
- package/design/sdk/components/floating-action/floating-action.ts +63 -0
- package/design/sdk/components/form-field/form-field.ts +23 -5
- package/design/sdk/components/grid/grid.css +17 -0
- package/design/sdk/components/grid/grid.ts +80 -0
- package/design/sdk/components/input/input.css +75 -0
- package/design/sdk/components/input/input.ts +79 -0
- package/design/sdk/components/kbd/kbd.css +25 -0
- package/design/sdk/components/kbd/kbd.ts +57 -0
- package/design/sdk/components/loader/loader.css +37 -0
- package/design/sdk/components/loader/loader.ts +68 -0
- package/design/sdk/components/masonry/masonry.css +7 -0
- package/design/sdk/components/masonry/masonry.ts +42 -0
- package/design/sdk/components/match/match.css +25 -0
- package/design/sdk/components/match/match.ts +76 -0
- package/design/sdk/components/menu/menu.css +22 -0
- package/design/sdk/components/menu/menu.ts +91 -0
- package/design/sdk/components/notification/notification.css +32 -0
- package/design/sdk/components/notification/notification.ts +106 -0
- package/design/sdk/components/otp-input/otp-input.css +35 -0
- package/design/sdk/components/otp-input/otp-input.ts +64 -0
- package/design/sdk/components/pagination/pagination.css +14 -0
- package/design/sdk/components/pagination/pagination.ts +75 -0
- package/design/sdk/components/picture/picture.css +37 -0
- package/design/sdk/components/picture/picture.ts +96 -0
- package/design/sdk/components/progress-steps/progress-steps.css +37 -0
- package/design/sdk/components/progress-steps/progress-steps.ts +91 -0
- package/design/sdk/components/query-error/query-error.css +33 -0
- package/design/sdk/components/query-error/query-error.ts +120 -0
- package/design/sdk/components/scheduler/scheduler.css +55 -0
- package/design/sdk/components/scheduler/scheduler.ts +239 -0
- package/design/sdk/components/scrollable/scrollable.css +58 -0
- package/design/sdk/components/scrollable/scrollable.ts +133 -0
- package/design/sdk/components/scrollbar/scrollbar.css +78 -0
- package/design/sdk/components/scrollbar/scrollbar.ts +123 -0
- package/design/sdk/components/select/select.ts +1 -1
- package/design/sdk/components/select/shared/select-part.ts +16 -1
- package/design/sdk/components/select-panel/select-panel.css +3 -0
- package/design/sdk/components/select-panel/select-panel.ts +124 -0
- package/design/sdk/components/skeleton/skeleton.css +22 -0
- package/design/sdk/components/skeleton/skeleton.ts +50 -0
- package/design/sdk/components/slider/slider.css +52 -0
- package/design/sdk/components/slider/slider.ts +86 -0
- package/design/sdk/components/standings/standings.css +33 -0
- package/design/sdk/components/standings/standings.ts +130 -0
- package/design/sdk/components/stat-tile/stat-tile.css +15 -0
- package/design/sdk/components/stat-tile/stat-tile.ts +84 -0
- package/design/sdk/components/stream/stream.css +48 -0
- package/design/sdk/components/stream/stream.ts +38 -0
- package/design/sdk/components/switch/switch.css +35 -0
- package/design/sdk/components/switch/switch.ts +63 -0
- package/design/sdk/components/table/table.css +67 -0
- package/design/sdk/components/table/table.ts +160 -0
- package/design/sdk/components/tabs/tabs.css +13 -0
- package/design/sdk/components/tabs/tabs.ts +74 -0
- package/design/sdk/components/textarea/textarea.css +116 -0
- package/design/sdk/components/textarea/textarea.ts +64 -0
- package/design/sdk/components/timeline/timeline.css +30 -0
- package/design/sdk/components/timeline/timeline.ts +87 -0
- package/design/sdk/components/toolbar/toolbar.css +24 -0
- package/design/sdk/components/toolbar/toolbar.ts +87 -0
- package/design/sdk/components/tooltip/tooltip.css +15 -0
- package/design/sdk/components/tooltip/tooltip.ts +28 -0
- package/design/sdk/components/tree/selected-tint.css +15 -0
- package/design/sdk/components/tree/tree.ts +70 -0
- package/design/sdk/foundations/colors/colors.css +108 -4
- package/design/sdk/foundations/elevation/elevation.css +6 -0
- package/design/sdk/foundations/elevation/elevation.ts +11 -0
- package/design/sdk/foundations/elevation/shared/elevation/scene.css +101 -0
- package/design/sdk/foundations/elevation/shared/elevation/scene.ts +74 -0
- package/design/sdk/foundations/icons/icons.ts +119 -0
- package/design/skeleton.ts +67 -0
- package/design/slider.ts +155 -0
- package/design/standings.ts +161 -0
- package/design/stat-tile.ts +109 -0
- package/design/stream.ts +92 -0
- package/design/switch.ts +83 -0
- package/design/table.ts +302 -0
- package/design/tabs.ts +75 -0
- package/design/textarea.ts +92 -0
- package/design/timeline.ts +52 -0
- package/design/toolbar.ts +95 -0
- package/design/tooltip.ts +87 -0
- package/fesm2022/ethlete-components.mjs +228 -187
- package/fesm2022/ethlete-components.mjs.map +1 -1
- package/package.json +1 -1
- package/src/lib/accordion/accordion.component.css +2 -1
- package/src/lib/avatar/avatar.component.css +10 -0
- package/src/lib/badge/badge.component.css +2 -2
- package/src/lib/banner/banner.component.css +3 -1
- package/src/lib/breadcrumb/breadcrumb-outlet.component.css +5 -0
- package/src/lib/breadcrumb/breadcrumb.component.css +5 -1
- package/src/lib/button/button.component.css +19 -0
- package/src/lib/button/icon-button.component.css +19 -20
- package/src/lib/button/split-button.component.css +5 -1
- package/src/lib/calendar/calendar.component.css +13 -3
- package/src/lib/card/card.component.css +4 -0
- package/src/lib/carousel/carousel-autoplay-styles.component.css +1 -1
- package/src/lib/carousel/carousel.component.css +2 -2
- package/src/lib/chart/chart-axis.component.css +3 -3
- package/src/lib/chip/chip.component.css +8 -2
- package/src/lib/description-list/description-list.component.css +2 -1
- package/src/lib/empty-state/empty-state.component.css +4 -0
- package/src/lib/floating-action/floating-action-styles.component.css +27 -0
- package/src/lib/focus-ring/focus-ring-styles.component.css +4 -1
- package/src/lib/forms/checkbox/checkbox.component.css +31 -5
- package/src/lib/forms/color-input/color-input.component.css +3 -1
- package/src/lib/forms/color-input/color-picker-panel.component.css +6 -2
- package/src/lib/forms/date-time/date-input/date-input.component.css +1 -1
- package/src/lib/forms/date-time/date-time-input/date-time-input.component.css +1 -1
- package/src/lib/forms/date-time/duration-input/duration-input.component.css +1 -1
- package/src/lib/forms/date-time/range-input-shell-styles.component.css +1 -1
- package/src/lib/forms/date-time/time-input/time-input.component.css +1 -1
- package/src/lib/forms/description/description.component.css +7 -0
- package/src/lib/forms/dropzone/dropzone.component.css +26 -0
- package/src/lib/forms/form-field/form-field-control-suffix-styles.component.css +4 -2
- package/src/lib/forms/form-field/form-field-text-shell-styles.component.css +54 -5
- package/src/lib/forms/form-field/form-field-textarea-styles.component.css +62 -0
- package/src/lib/forms/form-field/form-field.component.css +6 -0
- package/src/lib/forms/form-field/headless/label.directive.css +1 -1
- package/src/lib/forms/input/input.component.css +6 -2
- package/src/lib/forms/input/number-input.component.css +2 -2
- package/src/lib/forms/input/password-input.component.css +4 -3
- package/src/lib/forms/otp-input/otp-input.component.css +23 -4
- package/src/lib/forms/phone-input/phone-input.component.css +4 -3
- package/src/lib/forms/rating/rating.component.css +2 -1
- package/src/lib/forms/rich-text-editor/rich-text-editor.component.css +1 -1
- package/src/lib/forms/selection-card-styles.component.css +3 -2
- package/src/lib/forms/selection-list/checkbox-group/checkbox-group-select-all.component.css +4 -2
- package/src/lib/forms/selection-list/checkbox-group/checkbox-option.component.css +4 -2
- package/src/lib/forms/selection-list/radio-group/radio.component.css +5 -2
- package/src/lib/forms/selection-list/segmented-button-group/segmented-button.component.css +2 -1
- package/src/lib/forms/slider/range-slider.component.css +98 -6
- package/src/lib/forms/slider/slider.component.css +96 -4
- package/src/lib/forms/switch/switch.component.css +47 -4
- package/src/lib/forms/tag-input/tag-input.component.css +2 -2
- package/src/lib/forms/textarea/textarea-autosize-styles.component.css +2 -3
- package/src/lib/forms/textarea/textarea.component.css +7 -2
- package/src/lib/grid/grid-item-toolbar.component.css +10 -0
- package/src/lib/grid/grid-item.component.css +6 -1
- package/src/lib/loader/brand-loader/brand-loader.component.css +4 -1
- package/src/lib/loader/progress-bar/progress-bar.component.css +1 -1
- package/src/lib/loader/spinner/spinner-styles.component.css +1 -1
- package/src/lib/match/match-card.component.css +9 -3
- package/src/lib/match/match-score.component.css +4 -1
- package/src/lib/menu/menu-selection-item.component.css +1 -1
- package/src/lib/menu/menu.component.css +5 -0
- package/src/lib/overlay/overlay-container.component.css +4 -0
- package/src/lib/pagination/pagination.component.css +4 -0
- package/src/lib/progress-steps/progress-step.component.css +4 -4
- package/src/lib/query-error/query-error.component.css +4 -0
- package/src/lib/scheduler/scheduler-appointment-styles.component.css +21 -5
- package/src/lib/scheduler/scheduler-month-view.component.css +4 -2
- package/src/lib/scheduler/scheduler-time-grid-view.component.css +8 -2
- package/src/lib/scrollable/headless/scrollable-navigation.component.css +1 -1
- package/src/lib/standings/standings-pick.component.css +1 -1
- package/src/lib/standings/standings.component.css +10 -10
- package/src/lib/stat-tile/stat-tile-sparkline.component.css +1 -1
- package/src/lib/stream/consent/stream-consent.component.css +99 -0
- package/src/lib/stream/error/stream-player-error.component.css +100 -0
- package/src/lib/stream/loading/stream-player-loading.component.css +16 -0
- package/src/lib/stream/pip/pip-slot-placeholder.component.css +68 -0
- package/src/lib/stream/stream-player-slot-styles.component.css +4 -0
- package/src/lib/table/table-resize-grip.component.css +2 -1
- package/src/lib/table/table-select-cell.component.css +45 -15
- package/src/lib/table/table.component.css +15 -5
- package/src/lib/tabs/tab-bar-styles.component.css +1 -1
- package/src/lib/tabs/tabs/tab-group.component.css +111 -0
- package/src/lib/time-picker/time-picker.component.css +2 -1
- package/src/lib/timeline/timeline-item.component.css +5 -1
- package/src/lib/toolbar/toolbar.component.css +4 -0
- package/src/lib/tooltip/tooltip.component.css +14 -0
- package/types/ethlete-components.d.ts +11 -2
package/design/table.ts
ADDED
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import checkboxBase from '../src/lib/forms/checkbox/checkbox.component.css?inline';
|
|
2
|
+
import { ARROW_UP_ICON } from '../src/lib/icon/headless/arrow-up-icon';
|
|
3
|
+
import { CHEVRON_ICON } from '../src/lib/icon/headless/chevron-icon';
|
|
4
|
+
import iconBase from '../src/lib/icon/headless/icon-styles.component.css?inline';
|
|
5
|
+
import { TRIANGLE_EXCLAMATION_ICON } from '../src/lib/icon/headless/triangle-exclamation-icon';
|
|
6
|
+
import tableDetailBase from '../src/lib/table/table-detail-styles.component.css?inline';
|
|
7
|
+
import tableExpanderBase from '../src/lib/table/table-expander-cell.component.css?inline';
|
|
8
|
+
import tableGroupHeaderBase from '../src/lib/table/table-group-header-row.component.css?inline';
|
|
9
|
+
import tableSelectBase from '../src/lib/table/table-select-cell.component.css?inline';
|
|
10
|
+
import tableSkeletonRowsBase from '../src/lib/table/table-skeleton-rows.component.css?inline';
|
|
11
|
+
import tableStickyColumnsBase from '../src/lib/table/table-sticky-columns-styles.component.css?inline';
|
|
12
|
+
import tableBase from '../src/lib/table/table.component.css?inline';
|
|
13
|
+
import { iconMarkup } from './icon';
|
|
14
|
+
import { skeletonItem, skeletonStyles } from './skeleton';
|
|
15
|
+
|
|
16
|
+
export type TableColumnMock = {
|
|
17
|
+
header: string;
|
|
18
|
+
/** Left out, `start`. */
|
|
19
|
+
align?: 'start' | 'center' | 'end';
|
|
20
|
+
/** Draws the header label as the sort button. */
|
|
21
|
+
sortable?: boolean;
|
|
22
|
+
/** The column's sort direction. Left out, unsorted. */
|
|
23
|
+
sort?: 'asc' | 'desc';
|
|
24
|
+
/** Pins the column (`etTableStickyColumns`), with its `inset-inline-*` offset in px. */
|
|
25
|
+
sticky?: { side: 'start' | 'end'; offset: number };
|
|
26
|
+
/** Extra attributes on the sort button, e.g. design markers. */
|
|
27
|
+
labelAttributes?: string;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export type TableCellMock =
|
|
31
|
+
| string
|
|
32
|
+
| {
|
|
33
|
+
value: string;
|
|
34
|
+
/** `error` draws the built-in cell error mark before the value. */
|
|
35
|
+
state?: 'error';
|
|
36
|
+
/** The error message, drawn as the mark's label and title. */
|
|
37
|
+
message?: string;
|
|
38
|
+
/** Extra attributes on the cell, e.g. design markers. */
|
|
39
|
+
attributes?: string;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export type TableRowMock = {
|
|
43
|
+
cells: TableCellMock[];
|
|
44
|
+
/** Draws the row as selected (`etTableSelection`). */
|
|
45
|
+
selected?: boolean;
|
|
46
|
+
/** Draws the expander open and the detail row under the row, as HTML. */
|
|
47
|
+
detail?: string;
|
|
48
|
+
/** Leaves the expander out, as a row `canExpand` turns down. */
|
|
49
|
+
noExpander?: boolean;
|
|
50
|
+
/** Extra attributes on the row, e.g. design markers. */
|
|
51
|
+
attributes?: string;
|
|
52
|
+
/** Extra attributes on the expander button, e.g. design markers. */
|
|
53
|
+
expanderAttributes?: string;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export type TableMockOptions = {
|
|
57
|
+
columns: TableColumnMock[];
|
|
58
|
+
rows: TableRowMock[];
|
|
59
|
+
/** The `grid-template-columns` of the data columns, without the leading utility tracks. */
|
|
60
|
+
tracks: string;
|
|
61
|
+
/** Left out, `enclosed`, as `et-table` defaults to. */
|
|
62
|
+
appearance?: 'enclosed' | 'divided' | 'zebra' | 'grid' | 'bare';
|
|
63
|
+
/** Left out, `md`, as `et-table` defaults to. */
|
|
64
|
+
density?: 'sm' | 'md' | 'lg';
|
|
65
|
+
/** Draws the selection column; the value is the select-all checkbox state. */
|
|
66
|
+
selection?: 'unchecked' | 'checked' | 'mixed';
|
|
67
|
+
/** Draws the expander column (`etTableRowExpansion`). */
|
|
68
|
+
expandable?: boolean;
|
|
69
|
+
/** Draws the group header row (`etTableGroupHeaders`) over the data columns. */
|
|
70
|
+
groups?: { label?: string; span: number }[];
|
|
71
|
+
/** Draws the message row in place of the body: `empty` with the default label, `error` with the default icon and label. */
|
|
72
|
+
message?: 'empty' | 'error';
|
|
73
|
+
/** Draws this many skeleton rows in place of the body (`etTableSkeleton`), with the bone widths in % per column. */
|
|
74
|
+
placeholder?: { rows: number; widths: number[] };
|
|
75
|
+
/** Draws the busy bar over the first row, its sweep held at this fraction of its run from 0 to 1. */
|
|
76
|
+
busyAt?: number;
|
|
77
|
+
/** Rows with a click handler (`rowClick`), which take the pointer and the pressed tint. */
|
|
78
|
+
interactive?: boolean;
|
|
79
|
+
/** Cells take focus (`etTableKeyboardNav`). */
|
|
80
|
+
cellNavigation?: boolean;
|
|
81
|
+
/** The scroll edges with content past them: draws their shadow marks and scroll fades. */
|
|
82
|
+
scrolled?: { inlineStart?: boolean; inlineEnd?: boolean; blockStart?: boolean };
|
|
83
|
+
/** The error color theme name, the app's `type: 'error'` theme. */
|
|
84
|
+
errorColor: string;
|
|
85
|
+
/** Extra attributes on the host, e.g. a style or design markers. */
|
|
86
|
+
attributes?: string;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/** The stylesheets `table()` draws with: the real table, feature, checkbox, skeleton and icon CSS. */
|
|
90
|
+
export const tableStyles = /* @__PURE__ */ [
|
|
91
|
+
iconBase,
|
|
92
|
+
checkboxBase,
|
|
93
|
+
skeletonStyles,
|
|
94
|
+
tableBase,
|
|
95
|
+
tableStickyColumnsBase,
|
|
96
|
+
tableSelectBase,
|
|
97
|
+
tableExpanderBase,
|
|
98
|
+
tableGroupHeaderBase,
|
|
99
|
+
tableDetailBase,
|
|
100
|
+
tableSkeletonRowsBase,
|
|
101
|
+
`.et-table-busy-bar[data-et-busy-at]::after {
|
|
102
|
+
animation: none;
|
|
103
|
+
transform: translateX(calc(-100% + var(--et-busy-at) * 350%));
|
|
104
|
+
}
|
|
105
|
+
.et-table-row--placeholder .et-skeleton-item::after {
|
|
106
|
+
animation: none;
|
|
107
|
+
}`,
|
|
108
|
+
].join('\n');
|
|
109
|
+
|
|
110
|
+
const checkbox = (state: 'unchecked' | 'checked' | 'mixed', label: string) => {
|
|
111
|
+
const checked = state === 'checked' ? 'true' : state === 'mixed' ? 'mixed' : 'false';
|
|
112
|
+
|
|
113
|
+
return (
|
|
114
|
+
`<et-checkbox class="et-checkbox" role="checkbox" aria-checked="${checked}" aria-label="${label}" tabindex="0">` +
|
|
115
|
+
`<div class="et-checkbox-box">` +
|
|
116
|
+
`<svg class="et-checkbox-checkmark" viewBox="0 0 16 16" aria-hidden="true"><polyline class="et-checkbox-checkmark-path" points="3.5 8 6.5 11 12.5 5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></polyline></svg>` +
|
|
117
|
+
`<svg class="et-checkbox-indeterminate" viewBox="0 0 16 16" aria-hidden="true"><line class="et-checkbox-indeterminate-path" x1="4" y1="8" x2="12" y2="8" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"></line></svg>` +
|
|
118
|
+
`</div></et-checkbox>`
|
|
119
|
+
);
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const attr = (name: string, value: string | number | undefined) => (value === undefined ? '' : ` ${name}="${value}"`);
|
|
123
|
+
|
|
124
|
+
const styleAttr = (value: string) => ` style="${value}"`;
|
|
125
|
+
|
|
126
|
+
const stickyOf = (column: TableColumnMock | undefined) =>
|
|
127
|
+
column?.sticky
|
|
128
|
+
? {
|
|
129
|
+
className: ` et-table-sticky-${column.sticky.side}`,
|
|
130
|
+
style: styleAttr(`inset-inline-${column.sticky.side}: ${column.sticky.offset}px`),
|
|
131
|
+
}
|
|
132
|
+
: { className: '', style: '' };
|
|
133
|
+
|
|
134
|
+
const ariaSort = (column: TableColumnMock) =>
|
|
135
|
+
column.sort === 'asc' ? 'ascending' : column.sort === 'desc' ? 'descending' : column.sortable ? 'none' : undefined;
|
|
136
|
+
|
|
137
|
+
const headerCell = (column: TableColumnMock) => {
|
|
138
|
+
const sticky = stickyOf(column);
|
|
139
|
+
const text = `<span class="et-table-cell-text">${column.header}</span>`;
|
|
140
|
+
const indicator = column.sort
|
|
141
|
+
? iconMarkup(ARROW_UP_ICON, 'et-table-sort-indicator').replace('<i ', `<i data-direction="${column.sort}" `)
|
|
142
|
+
: '';
|
|
143
|
+
const label = column.sortable
|
|
144
|
+
? `<button class="et-table-header-label et-table-header-label--sortable" type="button"${column.labelAttributes ? ` ${column.labelAttributes}` : ''}><span class="et-table-header-label-box">${text}${indicator}</span></button>`
|
|
145
|
+
: `<span class="et-table-header-label"><span class="et-table-header-label-box">${text}</span></span>`;
|
|
146
|
+
|
|
147
|
+
return `<div class="et-table-header-cell${sticky.className}" role="columnheader"${attr('data-align', column.align)}${attr('aria-sort', ariaSort(column))}${sticky.style}>${label}</div>`;
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const bodyCell = (options: TableMockOptions, column: number) => (cell: TableCellMock) => {
|
|
151
|
+
const sticky = stickyOf(options.columns[column]);
|
|
152
|
+
const resolved = typeof cell === 'string' ? { value: cell } : cell;
|
|
153
|
+
const mark =
|
|
154
|
+
resolved.state === 'error'
|
|
155
|
+
? iconMarkup(TRIANGLE_EXCLAMATION_ICON, `et-table-cell-error-icon et-color--${options.errorColor}`).replace(
|
|
156
|
+
' aria-hidden="true"',
|
|
157
|
+
` role="img" aria-label="${resolved.message ?? ''}" title="${resolved.message ?? ''}"`,
|
|
158
|
+
)
|
|
159
|
+
: '';
|
|
160
|
+
const extra = resolved.attributes ? ` ${resolved.attributes}` : '';
|
|
161
|
+
|
|
162
|
+
return `<div class="et-table-cell${sticky.className}" role="cell"${attr('data-align', options.columns[column]?.align)}${attr('data-state', resolved.state)}${attr('tabindex', options.cellNavigation ? -1 : undefined)}${sticky.style}${extra}>${mark}<span class="et-table-cell-text">${resolved.value}</span></div>`;
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
const leadHeaderCells = (options: TableMockOptions) =>
|
|
166
|
+
[
|
|
167
|
+
options.selection
|
|
168
|
+
? `<div class="et-table-select-cell et-table-header-cell" role="columnheader"><et-table-select-all-cell>${checkbox(options.selection, 'Select all rows')}</et-table-select-all-cell></div>`
|
|
169
|
+
: '',
|
|
170
|
+
options.expandable ? `<div class="et-table-expander-cell et-table-header-cell" role="columnheader"></div>` : '',
|
|
171
|
+
].join('');
|
|
172
|
+
|
|
173
|
+
const leadBodyCells = (row: TableRowMock, options: TableMockOptions) => {
|
|
174
|
+
const expanded = row.detail !== undefined;
|
|
175
|
+
const expander =
|
|
176
|
+
row.noExpander === true
|
|
177
|
+
? ''
|
|
178
|
+
: `<button class="et-table-expander" type="button" aria-expanded="${expanded}" aria-label="${expanded ? 'Collapse row' : 'Expand row'}"${row.expanderAttributes ? ` ${row.expanderAttributes}` : ''}>${iconMarkup(CHEVRON_ICON, 'et-table-expander-icon').replace('<i ', `<i data-expanded="${expanded}" `)}</button>`;
|
|
179
|
+
|
|
180
|
+
return [
|
|
181
|
+
options.selection
|
|
182
|
+
? `<div class="et-table-select-cell et-table-cell" role="cell"><et-table-select-cell>${checkbox(row.selected ? 'checked' : 'unchecked', 'Select row')}</et-table-select-cell></div>`
|
|
183
|
+
: '',
|
|
184
|
+
options.expandable
|
|
185
|
+
? `<div class="et-table-expander-cell et-table-cell" role="cell"><et-table-expander-cell>${expander}</et-table-expander-cell></div>`
|
|
186
|
+
: '',
|
|
187
|
+
].join('');
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
const leadCount = (options: TableMockOptions) => (options.selection ? 1 : 0) + (options.expandable ? 1 : 0);
|
|
191
|
+
|
|
192
|
+
const groupRow = (options: TableMockOptions) =>
|
|
193
|
+
options.groups
|
|
194
|
+
? `<et-table-group-header-row class="et-table-group-row" role="row">${'<div class="et-table-group-cell" aria-hidden="true"></div>'.repeat(leadCount(options))}${options.groups
|
|
195
|
+
.map((group) =>
|
|
196
|
+
group.label
|
|
197
|
+
? `<div class="et-table-group-cell et-table-group-cell--labeled" role="columnheader" aria-colspan="${group.span}"${styleAttr(`grid-column: span ${group.span}`)}><span class="et-table-cell-text">${group.label}</span></div>`
|
|
198
|
+
: `<div class="et-table-group-cell" aria-hidden="true"${styleAttr(`grid-column: span ${group.span}`)}></div>`,
|
|
199
|
+
)
|
|
200
|
+
.join('')}</et-table-group-header-row>`
|
|
201
|
+
: '';
|
|
202
|
+
|
|
203
|
+
const bodyRow = (options: TableMockOptions) => (row: TableRowMock, index: number) => {
|
|
204
|
+
const last = index === options.rows.length - 1;
|
|
205
|
+
const classes = [
|
|
206
|
+
'et-table-row',
|
|
207
|
+
row.selected && 'et-table-row--selected',
|
|
208
|
+
index % 2 === 1 && 'et-table-row--stripe',
|
|
209
|
+
last && 'et-table-row--last',
|
|
210
|
+
options.interactive && 'et-table-row--interactive',
|
|
211
|
+
].filter(Boolean);
|
|
212
|
+
const cells = row.cells.map((cell, column) => bodyCell(options, column)(cell)).join('');
|
|
213
|
+
const detail =
|
|
214
|
+
row.detail === undefined
|
|
215
|
+
? ''
|
|
216
|
+
: `<et-table-row-detail class="et-table-detail-row" role="row"><div class="et-table-detail-cell" role="cell"><div class="et-table-detail-body">${row.detail}</div></div></et-table-row-detail>`;
|
|
217
|
+
|
|
218
|
+
return `<div class="${classes.join(' ')}" role="row"${attr('aria-selected', options.selection ? String(row.selected === true) : undefined)}${attr('tabindex', options.interactive ? 0 : undefined)}${row.attributes ? ` ${row.attributes}` : ''}>${leadBodyCells(row, options)}${cells}</div>${detail}`;
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
const placeholderRows = (options: TableMockOptions) => {
|
|
222
|
+
const { rows, widths } = options.placeholder ?? { rows: 0, widths: [] };
|
|
223
|
+
const lead = (className: string) => `<div class="${className} et-table-cell" role="cell"></div>`;
|
|
224
|
+
|
|
225
|
+
return `<et-table-skeleton-rows class="et-table-skeleton-rows">${Array.from(
|
|
226
|
+
{ length: rows },
|
|
227
|
+
(_, index) =>
|
|
228
|
+
`<div class="et-table-row et-table-row--placeholder et-skeleton--animated${index % 2 === 1 ? ' et-table-row--stripe' : ''}${index === rows - 1 ? ' et-table-row--last' : ''}" role="row" aria-hidden="true">` +
|
|
229
|
+
(options.selection ? lead('et-table-select-cell') : '') +
|
|
230
|
+
(options.expandable ? lead('et-table-expander-cell') : '') +
|
|
231
|
+
options.columns
|
|
232
|
+
.map(
|
|
233
|
+
(column, position) =>
|
|
234
|
+
`<div class="et-table-cell" role="cell"${attr('data-align', column.align)}>${skeletonItem({ style: `inline-size: ${widths[position] ?? 60}%` })}</div>`,
|
|
235
|
+
)
|
|
236
|
+
.join('') +
|
|
237
|
+
`</div>`,
|
|
238
|
+
).join('')}</et-table-skeleton-rows>`;
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
const messageRow = (options: TableMockOptions) =>
|
|
242
|
+
options.message === 'error'
|
|
243
|
+
? `<div class="et-table-message-row" role="row"><div class="et-table-error-cell" role="cell" aria-live="polite">${iconMarkup(TRIANGLE_EXCLAMATION_ICON, `et-table-error-icon et-color--${options.errorColor}`)}<span>Could not load data</span></div></div>`
|
|
244
|
+
: `<div class="et-table-message-row" role="row"><div class="et-table-empty-cell" role="cell">No data</div></div>`;
|
|
245
|
+
|
|
246
|
+
const body = (options: TableMockOptions) => {
|
|
247
|
+
if (options.message) return messageRow(options);
|
|
248
|
+
if (options.placeholder) return placeholderRows(options);
|
|
249
|
+
|
|
250
|
+
return options.rows.map(bodyRow(options)).join('');
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
const scrollFades = (options: TableMockOptions) => {
|
|
254
|
+
const pinned = (side: 'start' | 'end') => options.columns.some((column) => column.sticky?.side === side);
|
|
255
|
+
const fade = (side: 'start' | 'end', scrolled: boolean | undefined) =>
|
|
256
|
+
`<div class="et-table-scroll-fade et-table-scroll-fade--${side}${scrolled && !pinned(side) ? ' et-table-scroll-fade--visible' : ''}"${styleAttr(`inset-inline-${side}: 0px`)}></div>`;
|
|
257
|
+
|
|
258
|
+
return (
|
|
259
|
+
`<div class="et-table-scroll-fades" role="presentation" aria-hidden="true">` +
|
|
260
|
+
fade('start', options.scrolled?.inlineStart) +
|
|
261
|
+
fade('end', options.scrolled?.inlineEnd) +
|
|
262
|
+
`</div>`
|
|
263
|
+
);
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
/** Draws `<et-table>` with its header, body rows and the built-in features as a static snapshot of one state. */
|
|
267
|
+
export const table = (options: TableMockOptions) => {
|
|
268
|
+
const leads = [
|
|
269
|
+
options.selection && 'var(--et-table-select-width, 44px)',
|
|
270
|
+
options.expandable && 'var(--et-table-expander-width, 32px)',
|
|
271
|
+
];
|
|
272
|
+
const tracks = [...leads.filter(Boolean), options.tracks].join(' ');
|
|
273
|
+
const busy =
|
|
274
|
+
options.busyAt === undefined
|
|
275
|
+
? ''
|
|
276
|
+
: `<div class="et-table-busy-bar" role="presentation" data-et-busy-at${styleAttr(`--et-busy-at: ${options.busyAt}`)}></div>`;
|
|
277
|
+
const host = [
|
|
278
|
+
options.attributes,
|
|
279
|
+
`class="${[
|
|
280
|
+
'et-table-host',
|
|
281
|
+
options.scrolled?.inlineStart && 'et-table-host--scrolled-inline-start',
|
|
282
|
+
options.scrolled?.inlineEnd && 'et-table-host--scrolled-inline-end',
|
|
283
|
+
options.scrolled?.blockStart && 'et-table-host--scrolled-block-start',
|
|
284
|
+
]
|
|
285
|
+
.filter(Boolean)
|
|
286
|
+
.join(' ')}"`,
|
|
287
|
+
`data-appearance="${options.appearance ?? 'enclosed'}"`,
|
|
288
|
+
`data-density="${options.density ?? 'md'}"`,
|
|
289
|
+
options.placeholder || options.busyAt !== undefined ? 'aria-busy="true"' : '',
|
|
290
|
+
].filter(Boolean);
|
|
291
|
+
|
|
292
|
+
return (
|
|
293
|
+
`<et-table ${host.join(' ')}>` +
|
|
294
|
+
`<div class="et-table" role="table"${styleAttr(`grid-template-columns: ${tracks}`)}>` +
|
|
295
|
+
groupRow(options) +
|
|
296
|
+
`<div class="et-table-header-row" role="row">${leadHeaderCells(options)}${options.columns.map(headerCell).join('')}</div>` +
|
|
297
|
+
busy +
|
|
298
|
+
body(options) +
|
|
299
|
+
scrollFades(options) +
|
|
300
|
+
`</div></et-table>`
|
|
301
|
+
);
|
|
302
|
+
};
|
package/design/tabs.ts
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { interactiveStyles } from '@ethlete/core/design/interactive-styles';
|
|
2
|
+
import focusRing from '../src/lib/focus-ring/focus-ring-styles.component.css?inline';
|
|
3
|
+
import scrollable from '../src/lib/scrollable/scrollable.component.css?inline';
|
|
4
|
+
import tabBar from '../src/lib/tabs/tab-bar-styles.component.css?inline';
|
|
5
|
+
import tabScale from '../src/lib/tabs/tab-scale-styles.component.css?inline';
|
|
6
|
+
import tabGroup from '../src/lib/tabs/tabs/tab-group.component.css?inline';
|
|
7
|
+
|
|
8
|
+
export type TabsMockTab = {
|
|
9
|
+
label: string;
|
|
10
|
+
disabled?: boolean;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export type TabsMockOptions = {
|
|
14
|
+
tabs: TabsMockTab[];
|
|
15
|
+
/** The index of the selected tab. Defaults to 0. */
|
|
16
|
+
selected?: number;
|
|
17
|
+
/** Defaults to `md`. */
|
|
18
|
+
size?: 'sm' | 'md' | 'lg';
|
|
19
|
+
/** Defaults to `secondary`. */
|
|
20
|
+
variant?: 'primary' | 'secondary';
|
|
21
|
+
/** Defaults to `horizontal`. */
|
|
22
|
+
orientation?: 'horizontal' | 'vertical';
|
|
23
|
+
/** Defaults to `true`: the baseline under the bar. */
|
|
24
|
+
divider?: boolean;
|
|
25
|
+
/** The text of the selected tab's panel. Left out, the group draws no panel. */
|
|
26
|
+
panel?: string;
|
|
27
|
+
/** Unique per frame: the trigger and panel ids derive from it. */
|
|
28
|
+
id?: string;
|
|
29
|
+
/** Extra attributes on a trigger's element, by tab index, e.g. design markers. */
|
|
30
|
+
triggerAttributes?: Record<number, string>;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/** The stylesheets `tabs()` draws with: the real `et-tab-group` CSS. */
|
|
34
|
+
export const tabsStyles = /* @__PURE__ */ [interactiveStyles, focusRing, scrollable, tabScale, tabBar, tabGroup].join(
|
|
35
|
+
'\n',
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
/** Draws `<et-tab-group>` as HTML, with the DOM the component renders after its first frame. */
|
|
39
|
+
export const tabs = (options: TabsMockOptions) => {
|
|
40
|
+
const { id = 'tabs', selected = 0, orientation = 'horizontal' } = options;
|
|
41
|
+
|
|
42
|
+
const triggers = options.tabs
|
|
43
|
+
.map((tab, index) => {
|
|
44
|
+
const isSelected = index === selected;
|
|
45
|
+
const attributes = [
|
|
46
|
+
options.triggerAttributes?.[index],
|
|
47
|
+
`id="et-tab-${id}-${index}"`,
|
|
48
|
+
'role="tab"',
|
|
49
|
+
`aria-selected="${isSelected}"`,
|
|
50
|
+
`aria-controls="et-tab-panel-${id}-${index}"`,
|
|
51
|
+
tab.disabled && 'aria-disabled="true" disabled',
|
|
52
|
+
`tabindex="${isSelected ? 0 : -1}"`,
|
|
53
|
+
`class="${['et-tab-group__trigger', 'et-surface-interactive', 'et-focus-ring', isSelected && 'et-tab-group__trigger--active', tab.disabled && 'et-tab-group__trigger--disabled'].filter(Boolean).join(' ')}"`,
|
|
54
|
+
].filter(Boolean);
|
|
55
|
+
|
|
56
|
+
return `<button ${attributes.join(' ')}><span class="et-tab-group__trigger-content">${tab.label}</span><span class="et-tab-bar-underline${isSelected ? ' et-tab-bar-underline--active' : ''}"></span></button>`;
|
|
57
|
+
})
|
|
58
|
+
.join('');
|
|
59
|
+
|
|
60
|
+
const panel =
|
|
61
|
+
options.panel === undefined
|
|
62
|
+
? ''
|
|
63
|
+
: `<div class="et-tab-group__body"><div id="et-tab-panel-${id}-${selected}" aria-labelledby="et-tab-${id}-${selected}" class="et-tab-group__panel" role="tabpanel">${options.panel}</div></div>`;
|
|
64
|
+
|
|
65
|
+
const hostAttributes = [
|
|
66
|
+
'class="et-tab-group et-tab-scale"',
|
|
67
|
+
`data-orientation="${orientation}"`,
|
|
68
|
+
`data-size="${options.size ?? 'md'}"`,
|
|
69
|
+
'data-fit="auto"',
|
|
70
|
+
`data-divider="${options.divider ?? true}"`,
|
|
71
|
+
`data-variant="${options.variant ?? 'secondary'}"`,
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
return `<et-tab-group ${hostAttributes.join(' ')}><div aria-orientation="${orientation}" class="et-tab-group__header-shell" role="tablist"><et-scrollable class="et-scrollable et-tab-group__header-scrollable" direction="${orientation}" item-size="auto" role="presentation"><div class="et-scrollable-wrapper" role="presentation"><div class="et-scrollable-container et-tab-group__header" role="presentation">${triggers}</div></div></et-scrollable></div>${panel}</et-tab-group>`;
|
|
75
|
+
};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import description from '../src/lib/forms/description/description.component.css?inline';
|
|
2
|
+
import textareaFieldSheet from '../src/lib/forms/form-field/form-field-textarea-styles.component.css?inline';
|
|
3
|
+
import { formField, formFieldStyles, FormFieldMockOptions, textarea, TextareaMockOptions } from './form-field';
|
|
4
|
+
|
|
5
|
+
/** An interaction a frame forces with the `data-et-force` design marker. */
|
|
6
|
+
export type TextareaFieldMockForce = 'hover' | 'focus-visible';
|
|
7
|
+
|
|
8
|
+
export type TextareaFieldMockOptions = Omit<FormFieldMockOptions, 'control'> &
|
|
9
|
+
TextareaMockOptions & {
|
|
10
|
+
/** Caps the autosize growth at this many rows; the textarea scrolls past it. */
|
|
11
|
+
maxRows?: number;
|
|
12
|
+
/** An unresolved bulk-edit value: the raw value stays hidden and `mixedLabel` shows as the placeholder. */
|
|
13
|
+
mixed?: boolean;
|
|
14
|
+
mixedLabel?: string;
|
|
15
|
+
/** An `et-description` under the field, as HTML. */
|
|
16
|
+
description?: string;
|
|
17
|
+
/** Forced interactions: `hover` on the control frame, `focus-visible` on the native textarea. */
|
|
18
|
+
force?: TextareaFieldMockForce | TextareaFieldMockForce[];
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/** The stylesheets `textareaField()` draws with: the real `et-form-field`, `et-textarea` and `et-description` CSS. */
|
|
22
|
+
export const textareaFieldStyles = /* @__PURE__ */ [formFieldStyles, textareaFieldSheet, description].join('\n');
|
|
23
|
+
|
|
24
|
+
const forceList = (force: TextareaFieldMockForce | TextareaFieldMockForce[] | undefined) =>
|
|
25
|
+
force === undefined ? [] : Array.isArray(force) ? force : [force];
|
|
26
|
+
|
|
27
|
+
/** Draws `<et-form-field>` around `<et-textarea>` as HTML, with the DOM the components render. */
|
|
28
|
+
export const textareaField = (options: TextareaFieldMockOptions = {}) => {
|
|
29
|
+
const {
|
|
30
|
+
id = 'field',
|
|
31
|
+
value,
|
|
32
|
+
placeholder,
|
|
33
|
+
rows,
|
|
34
|
+
autosize = true,
|
|
35
|
+
minRows,
|
|
36
|
+
maxRows,
|
|
37
|
+
mixed,
|
|
38
|
+
mixedLabel = 'Mixed',
|
|
39
|
+
force,
|
|
40
|
+
...field
|
|
41
|
+
} = options;
|
|
42
|
+
const forced = forceList(force);
|
|
43
|
+
const descriptionId = `et-description-${id}`;
|
|
44
|
+
const control = textarea({
|
|
45
|
+
value: mixed ? undefined : value,
|
|
46
|
+
placeholder: mixed ? mixedLabel : placeholder,
|
|
47
|
+
rows,
|
|
48
|
+
autosize,
|
|
49
|
+
minRows,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
let html = formField({
|
|
53
|
+
...field,
|
|
54
|
+
id,
|
|
55
|
+
control: {
|
|
56
|
+
...control,
|
|
57
|
+
hasValue: !!mixed || control.hasValue,
|
|
58
|
+
html: (state) => {
|
|
59
|
+
const describedBy = [options.description !== undefined && descriptionId, state.describedBy]
|
|
60
|
+
.filter(Boolean)
|
|
61
|
+
.join(' ');
|
|
62
|
+
let markup = control.html({ ...state, describedBy: describedBy || null });
|
|
63
|
+
|
|
64
|
+
if (mixed)
|
|
65
|
+
markup = markup.replace('<et-textarea class="et-color-interactive et-textarea"', '$& data-mixed="true"');
|
|
66
|
+
if (autosize && maxRows !== undefined)
|
|
67
|
+
markup = markup.replace(
|
|
68
|
+
/(--et-textarea-min-rows: \d+)"/,
|
|
69
|
+
`$1; --et-textarea-max-block-size: calc(${maxRows} * 1lh)"`,
|
|
70
|
+
);
|
|
71
|
+
if (forced.includes('focus-visible'))
|
|
72
|
+
markup = markup.replace('<textarea ', '<textarea data-et-force="focus-visible" ');
|
|
73
|
+
|
|
74
|
+
return markup;
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
if (forced.includes('hover')) {
|
|
80
|
+
html = html.replace('<div class="et-form-field-control-frame"', '$& data-et-force="hover"');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (options.description !== undefined) {
|
|
84
|
+
const descriptionHtml = `<et-description class="et-description" id="${descriptionId}">${options.description}</et-description>`;
|
|
85
|
+
const supportStart = html.indexOf('<div class="et-color-interactive-exclude et-form-field-support"');
|
|
86
|
+
const at = supportStart === -1 ? html.lastIndexOf('</et-form-field>') : supportStart;
|
|
87
|
+
|
|
88
|
+
html = `${html.slice(0, at)}${descriptionHtml}${html.slice(at)}`;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return html;
|
|
92
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import iconBase from '../src/lib/icon/headless/icon-styles.component.css?inline';
|
|
2
|
+
import timelineItemBase from '../src/lib/timeline/timeline-item.component.css?inline';
|
|
3
|
+
import timelineBase from '../src/lib/timeline/timeline.component.css?inline';
|
|
4
|
+
|
|
5
|
+
export type TimelineMockItem = {
|
|
6
|
+
/** The item's body HTML, projected after the time. */
|
|
7
|
+
content: string;
|
|
8
|
+
/** The `[etTimelineTime]` text above the content. Left out, the item has no time. */
|
|
9
|
+
time?: string;
|
|
10
|
+
/** The `[etTimelineMarker]` HTML, e.g. an `iconMarkup()`. Left out, the item draws the default dot. */
|
|
11
|
+
marker?: string;
|
|
12
|
+
/** The color theme name the item's `color` input scopes. Left out, the item takes the theme around it. */
|
|
13
|
+
color?: string;
|
|
14
|
+
/** Extra attributes on the item, e.g. design markers. */
|
|
15
|
+
attributes?: string;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type TimelineMockOptions = {
|
|
19
|
+
items: TimelineMockItem[];
|
|
20
|
+
/** Extra attributes on the timeline, e.g. design markers. */
|
|
21
|
+
attributes?: string;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/** The stylesheets `timeline()` draws with: the real timeline, timeline-item and icon CSS. */
|
|
25
|
+
export const timelineStyles = /* @__PURE__ */ [iconBase, timelineBase, timelineItemBase].join('\n');
|
|
26
|
+
|
|
27
|
+
const item = ({ content, time, marker, color, attributes }: TimelineMockItem) => {
|
|
28
|
+
const hostAttributes = [
|
|
29
|
+
attributes,
|
|
30
|
+
`class="et-color--${color ?? 'inherited'} et-timeline-item"`,
|
|
31
|
+
'role="listitem"',
|
|
32
|
+
].filter(Boolean);
|
|
33
|
+
const markerSlot = marker ? marker.replace(/^<(\w+)/, '<$1 etTimelineMarker=""') : '';
|
|
34
|
+
const timeSlot = time ? `<span etTimelineTime="">${time}</span>` : '';
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
`<et-timeline-item ${hostAttributes.join(' ')}>` +
|
|
38
|
+
`<span class="et-timeline-item-rail"><span class="et-timeline-item-marker">${markerSlot}</span></span>` +
|
|
39
|
+
`<div class="et-timeline-item-content">${timeSlot}${content}</div>` +
|
|
40
|
+
`</et-timeline-item>`
|
|
41
|
+
);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Draws `<et-timeline>` with one `<et-timeline-item>` per item as HTML: the rail with its connector, the default
|
|
46
|
+
* dot or a projected marker, and the time above the content.
|
|
47
|
+
*/
|
|
48
|
+
export const timeline = (options: TimelineMockOptions) => {
|
|
49
|
+
const attributes = [options.attributes, 'class="et-timeline"', 'role="list"'].filter(Boolean);
|
|
50
|
+
|
|
51
|
+
return `<et-timeline ${attributes.join(' ')}>${options.items.map(item).join('')}</et-timeline>`;
|
|
52
|
+
};
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import iconButtonBase from '../src/lib/button/icon-button.component.css?inline';
|
|
2
|
+
import { IconDefinition } from '../src/lib/icon/headless/icon-definition';
|
|
3
|
+
import iconBase from '../src/lib/icon/headless/icon-styles.component.css?inline';
|
|
4
|
+
import toolbarBase from '../src/lib/toolbar/toolbar.component.css?inline';
|
|
5
|
+
import { buttonStyles } from './button';
|
|
6
|
+
import { divider, dividerStyles } from './divider';
|
|
7
|
+
import { iconMarkup } from './icon';
|
|
8
|
+
|
|
9
|
+
export type ToolbarMockItem =
|
|
10
|
+
| {
|
|
11
|
+
type: 'button';
|
|
12
|
+
icon: IconDefinition;
|
|
13
|
+
/** The button's `aria-label`. */
|
|
14
|
+
label: string;
|
|
15
|
+
/** Makes the button a toggle: `false` draws it unpressed, left out it is no toggle. */
|
|
16
|
+
pressed?: boolean;
|
|
17
|
+
disabled?: boolean;
|
|
18
|
+
/** Extra attributes on the button, e.g. design markers. */
|
|
19
|
+
attributes?: string;
|
|
20
|
+
}
|
|
21
|
+
| { type: 'divider' };
|
|
22
|
+
|
|
23
|
+
export type ToolbarMockOptions = {
|
|
24
|
+
items: ToolbarMockItem[];
|
|
25
|
+
orientation?: 'horizontal' | 'vertical';
|
|
26
|
+
/** The `aria-label` naming what the toolbar acts on. */
|
|
27
|
+
label?: string;
|
|
28
|
+
size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
|
|
29
|
+
/**
|
|
30
|
+
* The index into `items` of the button holding the toolbar's one tab stop. Left out, the first enabled
|
|
31
|
+
* button, as `[etToolbar]` picks it.
|
|
32
|
+
*/
|
|
33
|
+
tabStop?: number;
|
|
34
|
+
/** Extra attributes on the toolbar, e.g. design markers. */
|
|
35
|
+
attributes?: string;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/** The stylesheets `toolbar()` draws with: the real toolbar, icon-button, button, icon and divider CSS. */
|
|
39
|
+
export const toolbarStyles = /* @__PURE__ */ [buttonStyles, iconBase, iconButtonBase, dividerStyles, toolbarBase].join(
|
|
40
|
+
'\n',
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
const control = (item: Extract<ToolbarMockItem, { type: 'button' }>, options: { size: string; tabStop: boolean }) => {
|
|
44
|
+
const classes = [
|
|
45
|
+
'et-icon-button',
|
|
46
|
+
'et-color-interactive',
|
|
47
|
+
'et-focus-ring',
|
|
48
|
+
item.pressed ? 'et-color--inherited' : 'et-color--surface',
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
const attributes = [
|
|
52
|
+
item.attributes,
|
|
53
|
+
`class="${classes.join(' ')}"`,
|
|
54
|
+
'type="button"',
|
|
55
|
+
`aria-label="${item.label}"`,
|
|
56
|
+
item.disabled && 'disabled aria-disabled="true"',
|
|
57
|
+
item.pressed && 'data-pressed="true"',
|
|
58
|
+
item.pressed !== undefined && `aria-pressed="${item.pressed}"`,
|
|
59
|
+
'data-variant="transparent"',
|
|
60
|
+
`data-size="${options.size}"`,
|
|
61
|
+
item.pressed && 'data-pressed-variant="tonal"',
|
|
62
|
+
'data-et-toolbar-control=""',
|
|
63
|
+
`tabindex="${options.tabStop ? 0 : -1}"`,
|
|
64
|
+
].filter(Boolean);
|
|
65
|
+
|
|
66
|
+
return `<button ${attributes.join(' ')}><div class="et-icon-button-icon">${iconMarkup(item.icon)}</div></button>`;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Draws `<et-toolbar>` composed as its story shows it - transparent `et-icon-button`s in the surface's neutral
|
|
71
|
+
* colors that take the surrounding color theme while pressed, grouped by decorative `et-divider`s - as HTML,
|
|
72
|
+
* with the roving tab stop `[etToolbar]` sets.
|
|
73
|
+
*/
|
|
74
|
+
export const toolbar = (options: ToolbarMockOptions) => {
|
|
75
|
+
const { items, orientation = 'horizontal', size = 'sm' } = options;
|
|
76
|
+
const tabStop = options.tabStop ?? items.findIndex((item) => item.type === 'button' && !item.disabled);
|
|
77
|
+
|
|
78
|
+
const contents = items
|
|
79
|
+
.map((item, index) =>
|
|
80
|
+
item.type === 'divider'
|
|
81
|
+
? divider({ orientation: orientation === 'horizontal' ? 'vertical' : 'horizontal', decorative: true })
|
|
82
|
+
: control(item, { size, tabStop: index === tabStop }),
|
|
83
|
+
)
|
|
84
|
+
.join('');
|
|
85
|
+
|
|
86
|
+
const attributes = [
|
|
87
|
+
options.attributes,
|
|
88
|
+
'class="et-toolbar"',
|
|
89
|
+
'role="toolbar"',
|
|
90
|
+
`aria-orientation="${orientation}"`,
|
|
91
|
+
`aria-label="${options.label ?? 'Text formatting'}"`,
|
|
92
|
+
].filter(Boolean);
|
|
93
|
+
|
|
94
|
+
return `<et-toolbar ${attributes.join(' ')}>${contents}</et-toolbar>`;
|
|
95
|
+
};
|