@ethlete/components 1.0.0-next.77 → 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 +119 -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 +22 -0
- 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/form-field.ts +40 -1
- package/design/grid.ts +148 -0
- package/design/icon-set.ts +19 -0
- package/design/icon.ts +9 -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 +82 -0
- 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 +118 -0
- 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.css +5 -0
- package/design/sdk/components/select/select.ts +16 -0
- package/design/sdk/components/select/shared/select-part.ts +75 -0
- 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/fixtures.ts +0 -0
- package/design/sdk/foundations/colors/colors.css +632 -0
- package/design/sdk/foundations/colors/colors.ts +188 -0
- package/design/sdk/foundations/colors/overrides.json +19 -0
- package/design/sdk/foundations/colors/shared/contrast.ts +120 -0
- 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/select.ts +113 -28
- 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/design/tree.ts +106 -0
- package/fesm2022/ethlete-components.mjs +240 -199
- 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 +74 -17
- 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/select/select-option-styles.component.css +3 -2
- 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/icon/headless/align-icons.ts +30 -0
- package/src/lib/icon/headless/arrow-out-up-right-icon.ts +17 -0
- package/src/lib/icon/headless/arrow-right-icon.ts +23 -0
- package/src/lib/icon/headless/arrow-up-icon.ts +30 -0
- package/src/lib/icon/headless/arrows-left-right-icon.ts +31 -0
- package/src/lib/icon/headless/bold-icon.ts +11 -0
- package/src/lib/icon/headless/calendar-icon.ts +21 -0
- package/src/lib/icon/headless/check-icon.ts +14 -0
- package/src/lib/icon/headless/chevron-icon.ts +14 -0
- package/src/lib/icon/headless/circle-check-icon.ts +14 -0
- package/src/lib/icon/headless/circle-info-icon.ts +14 -0
- package/src/lib/icon/headless/clipboard-check-icon.ts +28 -0
- package/src/lib/icon/headless/clock-icon.ts +21 -0
- package/src/lib/icon/headless/code-block-icon.ts +15 -0
- package/src/lib/icon/headless/code-icon.ts +14 -0
- package/src/lib/icon/headless/ellipsis-icon.ts +13 -0
- package/src/lib/icon/headless/ellipsis-vertical-icon.ts +13 -0
- package/src/lib/icon/headless/eye-icons.ts +27 -0
- package/src/lib/icon/headless/eyedropper-icon.ts +22 -0
- package/src/lib/icon/headless/file-icon.ts +16 -0
- package/src/lib/icon/headless/filter-icon.ts +11 -0
- package/src/lib/icon/headless/floppy-disk-icon.ts +27 -0
- package/src/lib/icon/headless/focus-frame-icon.ts +16 -0
- package/src/lib/icon/headless/grid-2x2-icon.ts +17 -0
- package/src/lib/icon/headless/heading-1-icon.ts +11 -0
- package/src/lib/icon/headless/heading-2-icon.ts +11 -0
- package/src/lib/icon/headless/heading-3-icon.ts +11 -0
- package/src/lib/icon/headless/icon-definition.ts +14 -0
- package/src/lib/icon/headless/image-icon.ts +14 -0
- package/src/lib/icon/headless/italic-icon.ts +19 -0
- package/src/lib/icon/headless/link-icon.ts +17 -0
- package/src/lib/icon/headless/list-bulleted-icon.ts +14 -0
- package/src/lib/icon/headless/list-numbered-icon.ts +14 -0
- package/src/lib/icon/headless/location-icon.ts +21 -0
- package/src/lib/icon/headless/lock-icon.ts +14 -0
- package/src/lib/icon/headless/minus-icon.ts +14 -0
- package/src/lib/icon/headless/paragraph-icon.ts +11 -0
- package/src/lib/icon/headless/pause-icon.ts +11 -0
- package/src/lib/icon/headless/pencil-icon.ts +21 -0
- package/src/lib/icon/headless/play-icon.ts +10 -0
- package/src/lib/icon/headless/plus-icon.ts +21 -0
- package/src/lib/icon/headless/quote-icon.ts +15 -0
- package/src/lib/icon/headless/rotate-right-icon.ts +16 -0
- package/src/lib/icon/headless/star-icon.ts +14 -0
- package/src/lib/icon/headless/strikethrough-icon.ts +18 -0
- package/src/lib/icon/headless/table-icon.ts +15 -0
- package/src/lib/icon/headless/thumbtack-icon.ts +18 -0
- package/src/lib/icon/headless/times-icon.ts +14 -0
- package/src/lib/icon/headless/trash-icon.ts +24 -0
- package/src/lib/icon/headless/triangle-exclamation-icon.ts +14 -0
- package/src/lib/icon/headless/trophy-icon.ts +24 -0
- package/src/lib/icon/headless/underline-icon.ts +12 -0
- package/src/lib/icon/headless/undo-redo-icons.ts +24 -0
- package/src/lib/icon/headless/upload-icon.ts +16 -0
- 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/src/lib/tree/tree.component.css +2 -2
- package/types/ethlete-components.d.ts +62 -53
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
|
+
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { Placement } from '@floating-ui/dom';
|
|
2
|
+
import overlayContainer from '../src/lib/overlay/overlay-container.component.css?inline';
|
|
3
|
+
import floatingTip from '../src/lib/overlay/floating-tip-styles.component.css?inline';
|
|
4
|
+
import overlayArrow from '../src/lib/overlay/strategies/overlay-arrow-styles.component.css?inline';
|
|
5
|
+
import tooltipBase from '../src/lib/tooltip/tooltip.component.css?inline';
|
|
6
|
+
|
|
7
|
+
export type TooltipMockOptions = {
|
|
8
|
+
/** The tooltip text, as HTML. Drawn like a string `etTooltip`. */
|
|
9
|
+
text?: string;
|
|
10
|
+
/** Template content, as HTML. Drawn like a `TemplateRef` `etTooltip`; wins over `text`. */
|
|
11
|
+
content?: string;
|
|
12
|
+
/** The side the pane opened on; the arrow points back at the trigger. Defaults to `top`. */
|
|
13
|
+
placement?: Placement;
|
|
14
|
+
/** A surface theme name the app registers. */
|
|
15
|
+
surface?: string;
|
|
16
|
+
/** The tooltip id, which the trigger's `aria-describedby` points at. */
|
|
17
|
+
id?: string;
|
|
18
|
+
/** Extra attributes on the `<et-tooltip>`, e.g. design markers. */
|
|
19
|
+
attributes?: string;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/** The stylesheets `tooltip()` draws with: the real `et-tooltip`, floating tip, overlay pane and arrow CSS. */
|
|
23
|
+
export const tooltipStyles = /* @__PURE__ */ [
|
|
24
|
+
overlayContainer,
|
|
25
|
+
overlayArrow,
|
|
26
|
+
floatingTip,
|
|
27
|
+
tooltipBase,
|
|
28
|
+
`@layer components {
|
|
29
|
+
.et-tooltip-mock-frame > .et-tooltip-panel {
|
|
30
|
+
--_et-overlay-arrow-pane-border-width: 1px;
|
|
31
|
+
inline-size: max-content;
|
|
32
|
+
}
|
|
33
|
+
.et-tooltip-mock-frame > .et-tooltip-panel:is([data-overlay-placement^='top'], [data-overlay-placement^='bottom']) > .et-overlay-arrow {
|
|
34
|
+
left: calc(50% - 10px);
|
|
35
|
+
}
|
|
36
|
+
.et-tooltip-mock-frame > .et-tooltip-panel:is([data-overlay-placement^='left'], [data-overlay-placement^='right']) > .et-overlay-arrow {
|
|
37
|
+
top: calc(50% - 10px);
|
|
38
|
+
}
|
|
39
|
+
.et-tooltip-mock-frame > .et-tooltip-panel:is([data-overlay-placement^='top-start'], [data-overlay-placement^='bottom-start']) > .et-overlay-arrow {
|
|
40
|
+
left: 20px;
|
|
41
|
+
}
|
|
42
|
+
.et-tooltip-mock-frame > .et-tooltip-panel:is([data-overlay-placement^='top-end'], [data-overlay-placement^='bottom-end']) > .et-overlay-arrow {
|
|
43
|
+
left: calc(100% - 40px);
|
|
44
|
+
}
|
|
45
|
+
.et-tooltip-mock-frame > .et-tooltip-panel:is([data-overlay-placement^='left-start'], [data-overlay-placement^='right-start']) > .et-overlay-arrow {
|
|
46
|
+
top: 20px;
|
|
47
|
+
}
|
|
48
|
+
.et-tooltip-mock-frame > .et-tooltip-panel:is([data-overlay-placement^='left-end'], [data-overlay-placement^='right-end']) > .et-overlay-arrow {
|
|
49
|
+
top: calc(100% - 40px);
|
|
50
|
+
}
|
|
51
|
+
}`,
|
|
52
|
+
].join('\n');
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Draws an open `et-tooltip` in its overlay pane, with the arrow, as HTML. The pane sits in flow,
|
|
56
|
+
* wrapped in a frame that centers the arrow on its side, instead of where the overlay runtime would
|
|
57
|
+
* position it, so place it next to the drawn trigger.
|
|
58
|
+
*/
|
|
59
|
+
export const tooltip = (options: TooltipMockOptions = {}) => {
|
|
60
|
+
const { placement = 'top', id = 'et-tooltip-1' } = options;
|
|
61
|
+
const surface = `et-surface--${options.surface ?? 'inherited'}`;
|
|
62
|
+
const hasTemplate = options.content !== undefined;
|
|
63
|
+
|
|
64
|
+
const paneClasses = [
|
|
65
|
+
'et-overlay',
|
|
66
|
+
'et-overlay-runtime-pane',
|
|
67
|
+
'et-tooltip-panel',
|
|
68
|
+
'et-overlay--tooltip',
|
|
69
|
+
'et-floating-panel',
|
|
70
|
+
'et-color--inherited',
|
|
71
|
+
surface,
|
|
72
|
+
'et-with-default-animation',
|
|
73
|
+
'et-animation-enter-done',
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
const tooltipAttributes = [
|
|
77
|
+
options.attributes,
|
|
78
|
+
'role="tooltip"',
|
|
79
|
+
`class="et-tooltip et-color--inherited ${surface}"`,
|
|
80
|
+
`id="${id}"`,
|
|
81
|
+
hasTemplate && 'data-has-template="true"',
|
|
82
|
+
].filter(Boolean);
|
|
83
|
+
|
|
84
|
+
const body = hasTemplate ? options.content : `<p class="et-tooltip__text">${options.text ?? ''}</p>`;
|
|
85
|
+
|
|
86
|
+
return `<div class="et-tooltip-mock-frame"><div class="${paneClasses.join(' ')}" data-overlay-placement="${placement}"><et-tooltip ${tooltipAttributes.join(' ')}><div class="et-tooltip__surface"><div class="et-tooltip__content">${body}</div></div></et-tooltip><div et-floating-arrow class="et-overlay-arrow"></div></div></div>`;
|
|
87
|
+
};
|
package/design/tree.ts
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import iconStyles from '../src/lib/icon/headless/icon-styles.component.css?inline';
|
|
2
|
+
import treeBase from '../src/lib/tree/tree.component.css?inline';
|
|
3
|
+
import { CHEVRON_ICON } from '../src/lib/icon/headless/chevron-icon';
|
|
4
|
+
import { iconMarkup } from './icon';
|
|
5
|
+
|
|
6
|
+
export type TreeMockItem = {
|
|
7
|
+
label: string;
|
|
8
|
+
/** Defaults to the slash-joined labels of the item and its parents. */
|
|
9
|
+
value?: string;
|
|
10
|
+
/** Left out, the item is a leaf. */
|
|
11
|
+
children?: TreeMockItem[];
|
|
12
|
+
disabled?: boolean;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type TreeMockOptions = {
|
|
16
|
+
items: TreeMockItem[];
|
|
17
|
+
/** The tree's `aria-label`. */
|
|
18
|
+
label?: string;
|
|
19
|
+
/** The values of the expanded branches. */
|
|
20
|
+
expanded?: string[];
|
|
21
|
+
/** The selected value, or values in `multiple` mode. */
|
|
22
|
+
value?: string | string[];
|
|
23
|
+
/** Defaults to `single`. */
|
|
24
|
+
selectionMode?: 'none' | 'single' | 'multiple';
|
|
25
|
+
/** The value of the row that holds the tab stop. Defaults to the first row. */
|
|
26
|
+
active?: string;
|
|
27
|
+
disabled?: boolean;
|
|
28
|
+
/** Extra attributes on a row's element, by row value, e.g. design markers. */
|
|
29
|
+
rowAttributes?: Record<string, string>;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
type MockRow = { item: TreeMockItem; value: string; level: number; posInSet: number; setSize: number };
|
|
33
|
+
|
|
34
|
+
/** The stylesheets `tree()` draws with: the real `et-tree` CSS. */
|
|
35
|
+
export const treeStyles = /* @__PURE__ */ [iconStyles, treeBase].join('\n');
|
|
36
|
+
|
|
37
|
+
const valueOf = (item: TreeMockItem, parent: string | null) =>
|
|
38
|
+
item.value ?? (parent ? `${parent}/${item.label}` : item.label);
|
|
39
|
+
|
|
40
|
+
const visibleRows = (
|
|
41
|
+
items: TreeMockItem[],
|
|
42
|
+
{ expanded, parent, level }: { expanded: Set<string>; parent: string | null; level: number },
|
|
43
|
+
): MockRow[] =>
|
|
44
|
+
items.flatMap((item, index) => {
|
|
45
|
+
const value = valueOf(item, parent);
|
|
46
|
+
const row = { item, value, level, posInSet: index + 1, setSize: items.length };
|
|
47
|
+
const children =
|
|
48
|
+
item.children && expanded.has(value)
|
|
49
|
+
? visibleRows(item.children, { expanded, parent: value, level: level + 1 })
|
|
50
|
+
: [];
|
|
51
|
+
|
|
52
|
+
return [row, ...children];
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const marker = (isBranch: boolean) =>
|
|
56
|
+
`<et-tree-marker aria-hidden="true" class="et-tree-node-marker">${
|
|
57
|
+
isBranch ? iconMarkup(CHEVRON_ICON, 'et-tree-node-chevron') : ''
|
|
58
|
+
}</et-tree-marker>`;
|
|
59
|
+
|
|
60
|
+
/** Draws `<et-tree>` with the rows its `visibleRows()` would render for `items`. */
|
|
61
|
+
export const tree = (options: TreeMockOptions) => {
|
|
62
|
+
const mode = options.selectionMode ?? 'single';
|
|
63
|
+
const expanded = new Set(options.expanded ?? []);
|
|
64
|
+
const selected = new Set(
|
|
65
|
+
options.value === undefined ? [] : Array.isArray(options.value) ? options.value : [options.value],
|
|
66
|
+
);
|
|
67
|
+
const rows = visibleRows(options.items, { expanded, parent: null, level: 1 });
|
|
68
|
+
const active = rows.some((row) => row.value === options.active) ? options.active : rows[0]?.value;
|
|
69
|
+
|
|
70
|
+
const rowHtml = (row: MockRow) => {
|
|
71
|
+
const isBranch = row.item.children !== undefined;
|
|
72
|
+
const isExpanded = isBranch && expanded.has(row.value);
|
|
73
|
+
const isSelected = mode !== 'none' && selected.has(row.value);
|
|
74
|
+
const isDisabled = !!row.item.disabled || !!options.disabled;
|
|
75
|
+
const attributes = [
|
|
76
|
+
options.rowAttributes?.[row.value],
|
|
77
|
+
`style="--_et-tree-node-depth: ${row.level - 1};"`,
|
|
78
|
+
'class="et-tree-node"',
|
|
79
|
+
'role="treeitem"',
|
|
80
|
+
`aria-level="${row.level}"`,
|
|
81
|
+
`aria-posinset="${row.posInSet}"`,
|
|
82
|
+
`aria-setsize="${row.setSize}"`,
|
|
83
|
+
isBranch && `aria-expanded="${isExpanded}"`,
|
|
84
|
+
mode !== 'none' && `aria-selected="${isSelected}"`,
|
|
85
|
+
isDisabled && 'aria-disabled="true"',
|
|
86
|
+
`tabindex="${row.value === active ? 0 : -1}"`,
|
|
87
|
+
isSelected && 'data-selected="true"',
|
|
88
|
+
isExpanded && 'data-expanded="true"',
|
|
89
|
+
isBranch && 'data-branch="true"',
|
|
90
|
+
isDisabled && 'data-disabled="true"',
|
|
91
|
+
].filter(Boolean);
|
|
92
|
+
const check = mode === 'multiple' ? '<span aria-hidden="true" class="et-tree-node-check"></span>' : '';
|
|
93
|
+
|
|
94
|
+
return `<div ${attributes.join(' ')}>${marker(isBranch)}${check}<span class="et-tree-node-label">${row.item.label}</span></div>`;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const hostAttributes = [
|
|
98
|
+
'class="et-tree"',
|
|
99
|
+
'role="tree"',
|
|
100
|
+
options.label && `aria-label="${options.label}"`,
|
|
101
|
+
mode === 'multiple' && 'aria-multiselectable="true"',
|
|
102
|
+
options.disabled && 'data-disabled="true"',
|
|
103
|
+
].filter(Boolean);
|
|
104
|
+
|
|
105
|
+
return `<et-tree ${hostAttributes.join(' ')}>${rows.map(rowHtml).join('')}</et-tree>`;
|
|
106
|
+
};
|