@kubex/zinc 1.1.185 → 2.0.0
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/.browserslistrc +2 -0
- package/.github/workflows/js_build_and_deploy.yaml +54 -1
- package/.github/workflows/test.yaml +27 -0
- package/dist/chunks/zn.UWJLIXDT.js +1 -0
- package/dist/custom-elements.json +9998 -2345
- package/dist/vscode.html-custom-data.json +1022 -67
- package/dist/web-types.json +2129 -177
- package/dist/zn.d.ts +2293 -416
- package/dist/zn.min.css +1 -1
- package/dist/zn.min.js +2310 -1637
- package/docs/_includes/default.njk +4 -1
- package/docs/assets/scripts/docs.js +22 -6
- package/docs/assets/styles/docs.css +9 -2
- package/docs/data/report-table-day-compare.json +380 -0
- package/docs/data/report-table.json +314 -0
- package/docs/pages/components/avatar-group.md +57 -0
- package/docs/pages/components/badge.md +67 -0
- package/docs/pages/components/button.md +41 -1
- package/docs/pages/components/chat-message.md +16 -0
- package/docs/pages/components/checkbox.md +1 -1
- package/docs/pages/components/chip.md +25 -13
- package/docs/pages/components/color-picker.md +37 -0
- package/docs/pages/components/copy-button.md +2 -2
- package/docs/pages/components/data-table-filter.md +33 -50
- package/docs/pages/components/data-table-search.md +4 -4
- package/docs/pages/components/data-table.md +27 -3
- package/docs/pages/components/date-range.md +116 -0
- package/docs/pages/components/dialog.md +29 -37
- package/docs/pages/components/diff-row.md +28 -0
- package/docs/pages/components/diff.md +46 -0
- package/docs/pages/components/editor.md +55 -0
- package/docs/pages/components/icon.md +46 -1
- package/docs/pages/components/input.md +30 -11
- package/docs/pages/components/item.md +35 -0
- package/docs/pages/components/key-value.md +25 -0
- package/docs/pages/components/menu.md +74 -7
- package/docs/pages/components/nav-item.md +15 -0
- package/docs/pages/components/nav-list.md +132 -0
- package/docs/pages/components/navbar.md +100 -1
- package/docs/pages/components/page.md +242 -74
- package/docs/pages/components/priority-list.md +43 -5
- package/docs/pages/components/query-builder.md +23 -0
- package/docs/pages/components/radio-group.md +33 -1
- package/docs/pages/components/radio.md +1 -1
- package/docs/pages/components/report-table.md +202 -0
- package/docs/pages/components/stat.md +30 -0
- package/docs/pages/components/tile.md +11 -0
- package/docs/pages/components/tooltip.md +1 -1
- package/docs/pages/components/translation-grid.md +86 -0
- package/docs/pages/components/vertical-stepper.md +108 -64
- package/package.json +2 -1
- package/scss/_global-spacing.scss +2 -2
- package/scss/_root.scss +11 -171
- package/scss/boot.scss +14 -0
- package/scss/mixins/_helper-mixins.scss +24 -2
- package/scss/mixins/_motion-mixins.scss +126 -0
- package/scss/mixins/_spacing-mixins.scss +4 -3
- package/scss/mixins/_tab-indicator-mixins.scss +12 -0
- package/scss/mixins/_typography-mixins.scss +1 -1
- package/scss/mixins/index.scss +14 -0
- package/scss/shared/_base.scss +32 -1
- package/scss/shared/_button-mixin.scss +7 -5
- package/scss/shared/_table.scss +35 -4
- package/scss/shared/nav.scss +4 -0
- package/scss/shared/shimmer.scss +1 -1
- package/scss/table-controls.scss +206 -0
- package/scss/themes/_dark.scss +1 -3
- package/scss/themes/_index.scss +1 -1
- package/scss/themes/_light.scss +46 -6
- package/scss/themes/_modes.scss +11 -11
- package/scss/themes/_openbyte.scss +944 -0
- package/scss/themes/_typography.scss +4 -2
- package/scss/variables/index.scss +0 -9
- package/src/components/alert/alert.component.ts +14 -4
- package/src/components/alert/alert.scss +55 -69
- package/src/components/alert/alert.test.ts +8 -0
- package/src/components/animated-button/animated-button.component.ts +3 -2
- package/src/components/animated-button/animated-button.scss +6 -23
- package/src/components/animated-button/animated-button.test.ts +4 -3
- package/src/components/avatar-group/avatar-group.component.ts +84 -0
- package/src/components/avatar-group/avatar-group.scss +54 -0
- package/src/components/avatar-group/avatar-group.test.ts +59 -0
- package/src/components/avatar-group/index.ts +12 -0
- package/src/components/badge/badge.component.ts +49 -0
- package/src/components/badge/badge.scss +80 -0
- package/src/components/badge/badge.test.ts +34 -0
- package/src/components/badge/index.ts +12 -0
- package/src/components/bulk-actions/bulk-actions.component.ts +14 -2
- package/src/components/button/button.component.ts +228 -8
- package/src/components/button/button.scss +115 -32
- package/src/components/button/button.test.ts +122 -0
- package/src/components/button-menu/button-menu.component.ts +32 -11
- package/src/components/button-menu/button-menu.scss +5 -1
- package/src/components/channel-tile/channel-tile.component.ts +34 -27
- package/src/components/channel-tile/channel-tile.scss +76 -43
- package/src/components/chart/builders.test.ts +46 -0
- package/src/components/chart/builders.ts +27 -3
- package/src/components/chart/chart-live.test.ts +4 -6
- package/src/components/chart/chart.component.ts +125 -5
- package/src/components/chart/chart.scss +121 -0
- package/src/components/chart/chart.test.ts +81 -0
- package/src/components/chat-message/chat-message.component.ts +21 -1
- package/src/components/chat-message/chat-message.scss +13 -2
- package/src/components/chat-message/chat-message.test.ts +9 -0
- package/src/components/checkbox/checkbox.component.ts +61 -27
- package/src/components/checkbox/checkbox.scss +58 -25
- package/src/components/checkbox-group/checkbox-group.component.ts +7 -1
- package/src/components/chip/chip.component.ts +11 -1
- package/src/components/chip/chip.scss +91 -26
- package/src/components/chip/chip.test.ts +17 -0
- package/src/components/collapsible/collapsible.component.ts +17 -3
- package/src/components/collapsible/collapsible.scss +19 -10
- package/src/components/color-picker/color-picker.component.ts +277 -0
- package/src/components/color-picker/color-picker.scss +141 -0
- package/src/components/color-picker/color-picker.test.ts +57 -0
- package/src/components/color-picker/index.ts +12 -0
- package/src/components/cols/cols.component.ts +10 -0
- package/src/components/cols/cols.test.ts +17 -0
- package/src/components/confirm/confirm.component.ts +20 -6
- package/src/components/confirm/confirm.test.ts +52 -0
- package/src/components/content-block/content-block.component.ts +38 -9
- package/src/components/content-block/content-block.scss +11 -5
- package/src/components/content-block/content-block.test.ts +26 -0
- package/src/components/copy-button/copy-button.component.ts +2 -2
- package/src/components/copy-button/copy-button.scss +17 -4
- package/src/components/data-select/data-select.component.ts +3 -1
- package/src/components/data-table/data-table.component.ts +362 -140
- package/src/components/data-table/data-table.scss +133 -60
- package/src/components/data-table/data-table.test.ts +356 -4
- package/src/components/data-table-filter/data-table-filter.component.ts +426 -406
- package/src/components/data-table-filter/data-table-filter.scss +176 -148
- package/src/components/data-table-filter/data-table-filter.test.ts +271 -273
- package/src/components/data-table-search/data-table-search.component.ts +4 -2
- package/src/components/data-table-search/data-table-search.scss +8 -1
- package/src/components/data-table-search/data-table-search.test.ts +29 -0
- package/src/components/date-range/date-range-math.test.ts +78 -0
- package/src/components/date-range/date-range-math.ts +105 -0
- package/src/components/date-range/date-range.component.ts +375 -0
- package/src/components/date-range/date-range.scss +272 -0
- package/src/components/date-range/date-range.test.ts +247 -0
- package/src/components/date-range/index.ts +12 -0
- package/src/components/datepicker/datepicker.component.ts +44 -7
- package/src/components/datepicker/datepicker.scss +9 -0
- package/src/components/datepicker/datepicker.test.ts +2 -2
- package/src/components/dialog/dialog.component.ts +38 -19
- package/src/components/dialog/dialog.scss +65 -36
- package/src/components/dialog/dialog.test.ts +14 -0
- package/src/components/diff/diff.component.ts +55 -0
- package/src/components/diff/diff.scss +46 -0
- package/src/components/diff/diff.test.ts +15 -0
- package/src/components/diff/index.ts +12 -0
- package/src/components/diff-row/diff-row.component.ts +75 -0
- package/src/components/diff-row/diff-row.scss +70 -0
- package/src/components/diff-row/diff-row.test.ts +37 -0
- package/src/components/diff-row/index.ts +12 -0
- package/src/components/dropdown/dropdown.component.ts +12 -10
- package/src/components/dropdown/dropdown.scss +2 -0
- package/src/components/editor/editor.component.ts +159 -11
- package/src/components/editor/editor.scss +250 -1
- package/src/components/editor/editor.test.ts +31 -3
- package/src/components/editor/modules/ai/panel/ai-panel.scss +0 -1
- package/src/components/editor/modules/attachment/attachment.ts +53 -31
- package/src/components/editor/modules/context-menu/context-menu.ts +17 -0
- package/src/components/editor/modules/dialog/dialog.component.ts +23 -1
- package/src/components/editor/modules/dialog/dialog.test.ts +28 -0
- package/src/components/editor/modules/emoji/emoji.ts +4 -5
- package/src/components/editor/modules/merge-field/merge-field.ts +83 -0
- package/src/components/editor/modules/toolbar/toolbar.component.ts +121 -68
- package/src/components/editor/modules/toolbar/toolbar.scss +38 -0
- package/src/components/editor/modules/toolbar/toolbar.ts +25 -1
- package/src/components/empty-state/empty-state.component.ts +6 -1
- package/src/components/empty-state/empty-state.scss +21 -4
- package/src/components/expanding-action/expanding-action.component.ts +22 -34
- package/src/components/expanding-action/expanding-action.scss +3 -45
- package/src/components/expanding-action/expanding-action.test.ts +2 -1
- package/src/components/file/file.component.ts +30 -2
- package/src/components/file/file.scss +6 -0
- package/src/components/flow-builder/flow-builder.component.ts +36 -3
- package/src/components/flow-builder/flow-builder.scss +0 -6
- package/src/components/flow-builder/modules/flow-canvas/flow-canvas.component.ts +8 -0
- package/src/components/flow-builder/modules/flow-canvas/flow-canvas.scss +10 -5
- package/src/components/flow-builder/modules/flow-node/flow-node.component.ts +17 -0
- package/src/components/flow-builder/modules/flow-node/flow-node.scss +10 -0
- package/src/components/form-actions/form-actions.component.ts +48 -2
- package/src/components/form-actions/form-actions.scss +1 -1
- package/src/components/form-group/form-group.component.ts +10 -3
- package/src/components/form-group/form-group.scss +37 -4
- package/src/components/form-group/form-group.test.ts +10 -0
- package/src/components/header/header.component.ts +11 -4
- package/src/components/header/header.scss +47 -5
- package/src/components/hover-container/hover-container.component.ts +4 -5
- package/src/components/icon/icon.component.ts +95 -9
- package/src/components/icon/icon.scss +70 -6
- package/src/components/icon/icon.test.ts +55 -0
- package/src/components/icon-picker/icon-picker.component.ts +35 -1
- package/src/components/inline-edit/inline-edit.component.ts +38 -8
- package/src/components/inline-edit/inline-edit.scss +128 -19
- package/src/components/input/input.component.ts +173 -297
- package/src/components/input/input.scss +65 -43
- package/src/components/input/input.test.ts +86 -4
- package/src/components/item/item.component.ts +12 -0
- package/src/components/item/item.scss +102 -3
- package/src/components/item/item.test.ts +23 -0
- package/src/components/key/key.component.ts +2 -2
- package/src/components/key/key.scss +11 -0
- package/src/components/key-value/index.ts +12 -0
- package/src/components/key-value/key-value.component.ts +160 -0
- package/src/components/key-value/key-value.scss +20 -0
- package/src/components/key-value/key-value.test.ts +22 -0
- package/src/components/markdown-editor/markdown-editor.component.ts +8 -1
- package/src/components/menu/menu.component.ts +21 -2
- package/src/components/menu/menu.scss +43 -23
- package/src/components/menu/menu.test.ts +33 -0
- package/src/components/menu-item/menu-item.component.ts +13 -2
- package/src/components/menu-item/menu-item.scss +56 -23
- package/src/components/menu-item/menu-item.test.ts +31 -0
- package/src/components/menu-item/submenu-controller.ts +25 -15
- package/src/components/menu-label/index.ts +12 -0
- package/src/components/menu-label/menu-label.component.ts +30 -0
- package/src/components/menu-label/menu-label.scss +18 -0
- package/src/components/nav-item/index.ts +12 -0
- package/src/components/nav-item/nav-item.component.ts +83 -0
- package/src/components/nav-item/nav-item.scss +128 -0
- package/src/components/nav-list/index.ts +12 -0
- package/src/components/nav-list/nav-list.component.ts +313 -0
- package/src/components/nav-list/nav-list.scss +87 -0
- package/src/components/nav-list/nav-list.test.ts +250 -0
- package/src/components/navbar/navbar.component.ts +235 -171
- package/src/components/navbar/navbar.scss +333 -169
- package/src/components/navbar/navbar.test.ts +63 -143
- package/src/components/page/page.component.ts +114 -29
- package/src/components/page/page.scss +223 -95
- package/src/components/page/page.test.ts +214 -3
- package/src/components/page-builder/page-builder.component.ts +51 -5
- package/src/components/page-builder/page-builder.scss +6 -1
- package/src/components/page-nav/page-nav.component.ts +25 -3
- package/src/components/page-nav/page-nav.scss +10 -2
- package/src/components/pagination/pagination.scss +1 -1
- package/src/components/panel/panel.component.ts +14 -13
- package/src/components/panel/panel.scss +14 -10
- package/src/components/panel/panel.test.ts +42 -0
- package/src/components/popup/popup.component.ts +20 -13
- package/src/components/popup/popup.test.ts +21 -1
- package/src/components/priority-list/priority-list.component.ts +157 -172
- package/src/components/priority-list/priority-list.scss +113 -17
- package/src/components/priority-list/priority-list.test.ts +301 -3
- package/src/components/progress-bar/progress-bar.component.ts +1 -1
- package/src/components/progress-bar/progress-bar.scss +16 -4
- package/src/components/progress-tile/progress-tile.scss +1 -1
- package/src/components/query-builder/query-builder.component.ts +85 -10
- package/src/components/query-builder/query-builder.scss +7 -1
- package/src/components/query-builder/query-builder.test.ts +40 -0
- package/src/components/radio/radio.component.ts +57 -18
- package/src/components/radio/radio.scss +101 -26
- package/src/components/radio-group/radio-group.component.ts +45 -7
- package/src/components/radio-group/radio-group.scss +17 -0
- package/src/components/radio-group/radio-group.test.ts +63 -2
- package/src/components/rating/rating.component.ts +23 -0
- package/src/components/rating/rating.scss +1 -1
- package/src/components/remarkd-editor/remarkd-editor.component.ts +60 -5
- package/src/components/remarkd-editor/remarkd-editor.scss +12 -4
- package/src/components/report-table/index.ts +12 -0
- package/src/components/report-table/report-format.test.ts +86 -0
- package/src/components/report-table/report-format.ts +72 -0
- package/src/components/report-table/report-math.test.ts +74 -0
- package/src/components/report-table/report-math.ts +252 -0
- package/src/components/report-table/report-table.component.ts +681 -0
- package/src/components/report-table/report-table.scss +328 -0
- package/src/components/report-table/report-table.test.ts +702 -0
- package/src/components/reveal/reveal.component.ts +15 -1
- package/src/components/reveal/reveal.scss +5 -0
- package/src/components/schedule-builder/schedule-builder.component.ts +23 -8
- package/src/components/scroll-container/scroll-container.component.ts +19 -1
- package/src/components/scroll-container/scroll-container.scss +6 -1
- package/src/components/select/select.component.ts +52 -36
- package/src/components/select/select.scss +42 -25
- package/src/components/select/select.test.ts +14 -2
- package/src/components/settings-container/settings-container.scss +18 -5
- package/src/components/sidebar/sidebar.component.ts +8 -1
- package/src/components/sidebar/sidebar.scss +6 -1
- package/src/components/skeleton/skeleton.component.ts +3 -3
- package/src/components/skeleton/skeleton.scss +6 -6
- package/src/components/skeleton/skeleton.test.ts +9 -0
- package/src/components/slash-menu/slash-menu.scss +1 -1
- package/src/components/slideout/slideout.component.ts +20 -3
- package/src/components/slideout/slideout.scss +13 -6
- package/src/components/slideout/slideout.test.ts +19 -0
- package/src/components/split-button/split-button.component.ts +1 -1
- package/src/components/split-pane/split-pane.component.ts +71 -8
- package/src/components/split-pane/split-pane.scss +145 -41
- package/src/components/split-pane/split-pane.test.ts +23 -3
- package/src/components/stat/stat.component.ts +209 -14
- package/src/components/stat/stat.scss +305 -5
- package/src/components/stat/stat.test.ts +143 -2
- package/src/components/status-indicator/status-indicator.scss +22 -21
- package/src/components/stepper/stepper.scss +3 -3
- package/src/components/tab/tab.component.ts +1 -0
- package/src/components/table/table.component.ts +12 -1
- package/src/components/table/table.scss +21 -13
- package/src/components/table/table.test.ts +17 -0
- package/src/components/tabs/tabs.component.ts +150 -37
- package/src/components/tabs/tabs.scss +48 -7
- package/src/components/tabs/tabs.test.ts +169 -0
- package/src/components/textarea/textarea.component.ts +25 -3
- package/src/components/textarea/textarea.scss +10 -0
- package/src/components/textarea/textarea.test.ts +8 -4
- package/src/components/theme-editor/theme-editor.scss +0 -6
- package/src/components/tile/tile.component.ts +32 -2
- package/src/components/tile/tile.scss +40 -7
- package/src/components/tile/tile.test.ts +10 -0
- package/src/components/toggle/toggle.component.ts +23 -7
- package/src/components/toggle/toggle.scss +36 -29
- package/src/components/toggle/toggle.test.ts +17 -0
- package/src/components/tooltip/tooltip.component.ts +3 -0
- package/src/components/tooltip/tooltip.scss +11 -6
- package/src/components/tooltip/tooltip.test.ts +12 -0
- package/src/components/translation-field/index.ts +12 -0
- package/src/components/translation-field/translation-field.component.ts +55 -0
- package/src/components/translation-field/translation-field.scss +101 -0
- package/src/components/translation-fields/index.ts +12 -0
- package/src/components/translation-fields/translation-fields.component.ts +80 -0
- package/src/components/translation-fields/translation-fields.scss +60 -0
- package/src/components/translation-grid/index.ts +12 -0
- package/src/components/translation-grid/translation-grid.component.ts +157 -0
- package/src/components/translation-grid/translation-grid.scss +138 -0
- package/src/components/translation-grid/translation-grid.test.ts +28 -0
- package/src/components/vertical-stepper/vertical-stepper.component.ts +35 -9
- package/src/components/vertical-stepper/vertical-stepper.scss +110 -39
- package/src/components/vertical-stepper/vertical-stepper.test.ts +35 -0
- package/src/components/well/well.scss +2 -2
- package/src/events/events.ts +2 -0
- package/src/events/zn-chart-click.ts +13 -0
- package/src/events/zn-report-change.ts +7 -0
- package/src/form-control.scss +36 -3
- package/src/internal/anchored-popover.ts +175 -0
- package/src/internal/animate.ts +19 -5
- package/src/internal/cosmic-border.ts +15 -0
- package/src/internal/form.ts +1 -1
- package/src/internal/hover-intent.ts +15 -0
- package/src/internal/input-modality.ts +15 -0
- package/src/internal/pointer-intent.ts +37 -0
- package/src/internal/reorder.ts +625 -0
- package/src/internal/scroll.ts +51 -3
- package/src/internal/selection-card.ts +1 -1
- package/src/internal/tab-indicator.ts +92 -0
- package/src/internal/table-sort.ts +25 -0
- package/src/internal/table-tabs.ts +108 -0
- package/src/internal/test-drag.ts +35 -0
- package/src/internal/theme-openbyte.test.ts +126 -0
- package/src/internal/theme.ts +4 -4
- package/src/internal/zinc-element.ts +2 -2
- package/src/selection-card.scss +25 -0
- package/src/types/web-test-runner-commands.d.ts +1 -0
- package/src/utilities/color.test.ts +43 -0
- package/src/utilities/color.ts +189 -0
- package/src/zinc.ts +14 -0
- package/.github/workflows/eleventy_build.yml +0 -42
- package/scss/themes/_aura.scss +0 -114
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import {EASE_DRAWER, EASE_OUT, parseDuration, prefersReducedMotion} from './animate';
|
|
2
|
+
|
|
3
|
+
const SQUISH = 'tab-indicator-squish';
|
|
4
|
+
|
|
5
|
+
function token(style: CSSStyleDeclaration, name: string, fallback: string) {
|
|
6
|
+
return parseDuration(style.getPropertyValue(name).trim() || fallback);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Squishes a sliding tab indicator's height to `--zn-navbar-tab-indicator-squash` during
|
|
11
|
+
* `--zn-navbar-tab-indicator-delay`, then lets it recover as it travels. Does nothing unless a squash is set.
|
|
12
|
+
*/
|
|
13
|
+
export function squishTabIndicator(indicator: HTMLElement) {
|
|
14
|
+
const style = getComputedStyle(indicator);
|
|
15
|
+
const squash = parseFloat(style.getPropertyValue('--zn-navbar-tab-indicator-squash'));
|
|
16
|
+
if (!squash || squash === 1 || prefersReducedMotion()) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const delay = token(style, '--zn-navbar-tab-indicator-delay', '0ms');
|
|
21
|
+
const total = delay + token(style, '--zn-navbar-tab-indicator-duration', '300ms');
|
|
22
|
+
// A squish cut short by another tab change carries on from where it was rather than jumping back to full height
|
|
23
|
+
const from = style.scale === 'none' ? '1 1' : style.scale;
|
|
24
|
+
indicator.getAnimations().filter(animation => animation.id === SQUISH).forEach(animation => animation.cancel());
|
|
25
|
+
|
|
26
|
+
const animation = indicator.animate([
|
|
27
|
+
{scale: from, easing: EASE_OUT},
|
|
28
|
+
{scale: `1 ${squash}`, offset: delay > 0 ? delay / total : 0.25, easing: EASE_DRAWER},
|
|
29
|
+
{scale: '1 1'},
|
|
30
|
+
], {duration: total});
|
|
31
|
+
animation.id = SQUISH;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const SLIDE = 'tab-indicator-slide';
|
|
35
|
+
// Enough samples that the stretch reads as a curve, not steps
|
|
36
|
+
const SLIDE_FRAMES = 40;
|
|
37
|
+
|
|
38
|
+
export interface IndicatorEdges {
|
|
39
|
+
left: number;
|
|
40
|
+
right: number;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Eased progress of one edge at each sample time, as the browser itself evaluates the easing. */
|
|
44
|
+
function edgeProgress(easing: string, duration: number, delay: number) {
|
|
45
|
+
let effect: KeyframeEffect;
|
|
46
|
+
try {
|
|
47
|
+
effect = new KeyframeEffect(null, null, {duration, delay, easing, fill: 'both'});
|
|
48
|
+
} catch {
|
|
49
|
+
effect = new KeyframeEffect(null, null, {duration, delay, easing: 'ease', fill: 'both'});
|
|
50
|
+
}
|
|
51
|
+
const timing = new Animation(effect);
|
|
52
|
+
return (time: number) => {
|
|
53
|
+
timing.currentTime = time;
|
|
54
|
+
return effect.getComputedTiming().progress ?? 1;
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Slides an indicator, already placed at `to` by `left` and `width`, from the box `from` using only `transform`, so the
|
|
60
|
+
* browser can run it off the main thread while the new tab loads. The edge heading for the new tab moves on
|
|
61
|
+
* `--zn-navbar-tab-indicator-duration`/`-easing`/`-delay`, the other on the `-trail-` tokens, stretching the indicator mid-move.
|
|
62
|
+
*/
|
|
63
|
+
export function slideTabIndicator(indicator: HTMLElement, from: IndicatorEdges, to: IndicatorEdges) {
|
|
64
|
+
indicator.getAnimations().filter(animation => animation.id === SLIDE).forEach(animation => animation.cancel());
|
|
65
|
+
const width = to.right - to.left;
|
|
66
|
+
if (width <= 0 || prefersReducedMotion()) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const style = getComputedStyle(indicator);
|
|
71
|
+
const easing = style.getPropertyValue('--zn-navbar-tab-indicator-easing').trim() || 'cubic-bezier(0.4, 0, 0.2, 1)';
|
|
72
|
+
const leadDuration = token(style, '--zn-navbar-tab-indicator-duration', '300ms');
|
|
73
|
+
const leadDelay = token(style, '--zn-navbar-tab-indicator-delay', '0ms');
|
|
74
|
+
const trailDuration = token(style, '--zn-navbar-tab-indicator-trail-duration', `${leadDuration}ms`);
|
|
75
|
+
const trailDelay = token(style, '--zn-navbar-tab-indicator-trail-delay', `${leadDelay}ms`);
|
|
76
|
+
const trailEasing = style.getPropertyValue('--zn-navbar-tab-indicator-trail-easing').trim() || easing;
|
|
77
|
+
const lead = edgeProgress(easing, leadDuration, leadDelay);
|
|
78
|
+
const trail = edgeProgress(trailEasing, trailDuration, trailDelay);
|
|
79
|
+
const forward = to.left > from.left;
|
|
80
|
+
const total = Math.max(leadDelay + leadDuration, trailDelay + trailDuration);
|
|
81
|
+
|
|
82
|
+
const keyframes: Keyframe[] = [];
|
|
83
|
+
for (let frame = 0; frame <= SLIDE_FRAMES; frame++) {
|
|
84
|
+
const time = total * frame / SLIDE_FRAMES;
|
|
85
|
+
const left = from.left + (to.left - from.left) * (forward ? trail : lead)(time);
|
|
86
|
+
const right = from.right + (to.right - from.right) * (forward ? lead : trail)(time);
|
|
87
|
+
keyframes.push({transform: `translateX(${left - to.left}px) scaleX(${Math.max(right - left, 0) / width})`});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const animation = indicator.animate(keyframes, {duration: total});
|
|
91
|
+
animation.id = SLIDE;
|
|
92
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import {html, type TemplateResult} from 'lit';
|
|
2
|
+
|
|
3
|
+
export type SortState = 'ascending' | 'descending' | 'none';
|
|
4
|
+
|
|
5
|
+
export function renderSortIcon(state: SortState): TemplateResult {
|
|
6
|
+
if (state === 'none') {
|
|
7
|
+
return html`
|
|
8
|
+
<div class="table__head__sort">
|
|
9
|
+
<zn-icon src="chevrons_up_down@lu" size="14"></zn-icon>
|
|
10
|
+
</div>`;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return html`
|
|
14
|
+
<div class="table__head__sort table__head__sort--active">
|
|
15
|
+
<zn-icon src="${state === 'ascending' ? 'arrow-up@lu' : 'arrow-down@lu'}" size="14"></zn-icon>
|
|
16
|
+
</div>`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function sortKeyHandler(onSort: () => void): (e: KeyboardEvent) => void {
|
|
20
|
+
return (e: KeyboardEvent) => {
|
|
21
|
+
if (e.key !== 'Enter' && e.key !== ' ') return;
|
|
22
|
+
e.preventDefault();
|
|
23
|
+
onSort();
|
|
24
|
+
};
|
|
25
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import {classMap} from 'lit/directives/class-map.js';
|
|
2
|
+
import {html, nothing, type ReactiveController, type ReactiveControllerHost, type TemplateResult} from 'lit';
|
|
3
|
+
import {ref} from 'lit/directives/ref.js';
|
|
4
|
+
import {squishTabIndicator} from './tab-indicator';
|
|
5
|
+
|
|
6
|
+
export interface TableTab {
|
|
7
|
+
value: string;
|
|
8
|
+
label: string;
|
|
9
|
+
count?: number | string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export class TabRowController implements ReactiveController {
|
|
13
|
+
private readonly onSelect: (value: string) => void;
|
|
14
|
+
private indicator: HTMLElement | null = null;
|
|
15
|
+
private indicatorTab: HTMLElement | null = null;
|
|
16
|
+
private observer: ResizeObserver | null = null;
|
|
17
|
+
|
|
18
|
+
constructor(host: ReactiveControllerHost, opts: { onSelect: (value: string) => void }) {
|
|
19
|
+
this.onSelect = opts.onSelect;
|
|
20
|
+
host.addController(this);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
hostUpdated() {
|
|
24
|
+
this.syncTabIndicator();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
hostDisconnected() {
|
|
28
|
+
this.observer?.disconnect();
|
|
29
|
+
this.observer = null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
render(tabs: TableTab[], active: string, pill: boolean): TemplateResult | typeof nothing {
|
|
33
|
+
if (tabs.length === 0) return nothing;
|
|
34
|
+
|
|
35
|
+
return html`
|
|
36
|
+
<div class="${classMap({'table__tabs': true, 'table__tabs--pill': pill})}" role="tablist" ${ref(this.observeBar)}>
|
|
37
|
+
<span class="table__tabs__indicator" part="tab-indicator" aria-hidden="true" ${ref(this.setIndicator)}></span>
|
|
38
|
+
${tabs.map(tab => html`
|
|
39
|
+
<button role="tab"
|
|
40
|
+
aria-selected="${tab.value === active ? 'true' : 'false'}"
|
|
41
|
+
class="${classMap({'table__tab': true, 'table__tab--active': tab.value === active})}"
|
|
42
|
+
@click="${() => this.onSelect(tab.value)}">
|
|
43
|
+
${tab.label}
|
|
44
|
+
${tab.count !== undefined && tab.count !== null && tab.count !== '' ? html`
|
|
45
|
+
<span class="table__tab__count">${tab.count}</span>` : nothing}
|
|
46
|
+
</button>`)}
|
|
47
|
+
</div>`;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private setIndicator = (el: Element | undefined) => {
|
|
51
|
+
this.indicator = (el as HTMLElement) ?? null;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
private observeBar = (el: Element | undefined) => {
|
|
55
|
+
this.observer?.disconnect();
|
|
56
|
+
this.observer = null;
|
|
57
|
+
if (!el) return;
|
|
58
|
+
this.observer = new ResizeObserver(() => this.syncTabIndicator());
|
|
59
|
+
this.observer.observe(el);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* One indicator shared by every tab, positioned by its two edges as zn-navbar's is: on a tab
|
|
64
|
+
* change the edge nearest the destination leads and the other trails, so it slides across.
|
|
65
|
+
*/
|
|
66
|
+
syncTabIndicator() {
|
|
67
|
+
const indicator = this.indicator;
|
|
68
|
+
const bar = indicator?.parentElement;
|
|
69
|
+
const active = bar?.querySelector<HTMLElement>('.table__tab--active');
|
|
70
|
+
if (!indicator || !bar || !active || active.offsetWidth === 0) {
|
|
71
|
+
indicator?.classList.remove('table__tabs__indicator--shown');
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const left = active.offsetLeft;
|
|
76
|
+
const right = bar.clientWidth - left - active.offsetWidth;
|
|
77
|
+
const previousLeft = parseFloat(indicator.style.left);
|
|
78
|
+
const ready = indicator.classList.contains('table__tabs__indicator--ready');
|
|
79
|
+
const tabChanged = active !== this.indicatorTab;
|
|
80
|
+
if (ready && tabChanged && !Number.isNaN(previousLeft) && Math.abs(left - previousLeft) > 0.5) {
|
|
81
|
+
indicator.classList.toggle('table__tabs__indicator--forward', left > previousLeft);
|
|
82
|
+
indicator.classList.toggle('table__tabs__indicator--backward', left < previousLeft);
|
|
83
|
+
squishTabIndicator(indicator);
|
|
84
|
+
}
|
|
85
|
+
this.indicatorTab = active;
|
|
86
|
+
|
|
87
|
+
// Only a tab change slides; a resize under the same tab follows it instantly, unless a slide is still running
|
|
88
|
+
const follow = ready && !tabChanged && indicator.getAnimations().length === 0;
|
|
89
|
+
if (follow) {
|
|
90
|
+
indicator.style.transition = 'none';
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
indicator.style.left = `${left}px`;
|
|
94
|
+
indicator.style.right = `${right}px`;
|
|
95
|
+
indicator.classList.add('table__tabs__indicator--shown');
|
|
96
|
+
|
|
97
|
+
if (follow) {
|
|
98
|
+
// Commit the jump before transitions come back on
|
|
99
|
+
void indicator.offsetWidth;
|
|
100
|
+
indicator.style.removeProperty('transition');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// The first placement jumps into position; only later moves animate
|
|
104
|
+
if (!ready) {
|
|
105
|
+
requestAnimationFrame(() => indicator.classList.add('table__tabs__indicator--ready'));
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Drives the pointer drag of a ReorderController from a test.
|
|
2
|
+
|
|
3
|
+
const init = (x: number, y: number): PointerEventInit => ({
|
|
4
|
+
pointerId: 1,
|
|
5
|
+
pointerType: 'mouse',
|
|
6
|
+
isPrimary: true,
|
|
7
|
+
button: 0,
|
|
8
|
+
clientX: x,
|
|
9
|
+
clientY: y,
|
|
10
|
+
bubbles: true,
|
|
11
|
+
composed: true,
|
|
12
|
+
cancelable: true,
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
/** The centre of an element. */
|
|
16
|
+
export function centre(el: Element) {
|
|
17
|
+
const rect = el.getBoundingClientRect();
|
|
18
|
+
return {x: rect.left + rect.width / 2, y: rect.top + rect.height / 2};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Presses on an element and moves far enough to start dragging it. */
|
|
22
|
+
export function startDrag(el: Element) {
|
|
23
|
+
const {x, y} = centre(el);
|
|
24
|
+
el.dispatchEvent(new PointerEvent('pointerdown', init(x, y)));
|
|
25
|
+
window.dispatchEvent(new PointerEvent('pointermove', init(x, y + 1)));
|
|
26
|
+
window.dispatchEvent(new PointerEvent('pointermove', init(x + 6, y + 6)));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function dragTo(x: number, y: number) {
|
|
30
|
+
window.dispatchEvent(new PointerEvent('pointermove', init(x, y)));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function drop(x: number, y: number) {
|
|
34
|
+
window.dispatchEvent(new PointerEvent('pointerup', init(x, y)));
|
|
35
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import '../../dist/zn.min.js';
|
|
2
|
+
import { expect, fixture, html } from '@open-wc/testing';
|
|
3
|
+
|
|
4
|
+
const root = document.documentElement;
|
|
5
|
+
// The minifier strips the spaces inside RGB triplets.
|
|
6
|
+
const token = (el: Element, name: string) => getComputedStyle(el).getPropertyValue(name).replace(/\s+/g, '');
|
|
7
|
+
|
|
8
|
+
function setTheme(m = 'light', density?: string) {
|
|
9
|
+
root.removeAttribute('t');
|
|
10
|
+
root.setAttribute('m', m);
|
|
11
|
+
if (density) root.setAttribute('data-density', density);
|
|
12
|
+
else root.removeAttribute('data-density');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
describe('openbyte theme tokens (default)', () => {
|
|
16
|
+
before(async () => {
|
|
17
|
+
const link = document.createElement('link');
|
|
18
|
+
link.rel = 'stylesheet';
|
|
19
|
+
link.href = '/dist/zn.min.css';
|
|
20
|
+
const loaded = new Promise(resolve => link.addEventListener('load', resolve));
|
|
21
|
+
document.head.append(link);
|
|
22
|
+
await loaded;
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
afterEach(() => setTheme());
|
|
26
|
+
|
|
27
|
+
it('re-points the brand and derived tokens in light mode', () => {
|
|
28
|
+
setTheme();
|
|
29
|
+
expect(token(root, '--zn-color-primary')).to.equal('8,94,253');
|
|
30
|
+
expect(token(root, '--zn-primary')).to.equal('8,94,253');
|
|
31
|
+
expect(token(root, '--zn-border-color')).to.equal('214,224,235');
|
|
32
|
+
expect(token(root, '--zn-panel-border-radius')).to.equal('12px');
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('pins --zn-primary in dark mode over the base dark accent', () => {
|
|
36
|
+
setTheme('dark');
|
|
37
|
+
expect(token(root, '--zn-primary')).to.equal('53,106,212');
|
|
38
|
+
expect(token(root, '--zn-color-body')).to.equal('24,25,30');
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('applies comfortable density by default and compact on a nested container', async () => {
|
|
42
|
+
setTheme();
|
|
43
|
+
const el = await fixture(html`<div data-density="compact"><span></span></div>`);
|
|
44
|
+
expect(token(root, '--zn-input-height-medium')).to.equal('36px');
|
|
45
|
+
expect(token(el.firstElementChild!, '--zn-input-height-medium')).to.equal('28px');
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('switches the whole page to compact from the root', () => {
|
|
49
|
+
setTheme('light', 'compact');
|
|
50
|
+
expect(token(root, '--zn-input-height-medium')).to.equal('28px');
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('sizes plain table rows to the density row height', async () => {
|
|
54
|
+
setTheme();
|
|
55
|
+
const table = await fixture(html`<table><tbody><tr><td>One</td></tr></tbody></table>`);
|
|
56
|
+
const cell = table.querySelector('td')!;
|
|
57
|
+
expect(cell.getBoundingClientRect().height).to.equal(44);
|
|
58
|
+
expect(getComputedStyle(cell).paddingTop).to.equal('4px');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('sets a table cell meta line below its main text in muted small type', async () => {
|
|
62
|
+
setTheme();
|
|
63
|
+
const table = await fixture(html`<table><tbody><tr><td><a href="#">Name</a><span class="zn-table-meta">Meta</span></td></tr></tbody></table>`);
|
|
64
|
+
const meta = getComputedStyle(table.querySelector('.zn-table-meta')!);
|
|
65
|
+
expect(meta.display).to.equal('block');
|
|
66
|
+
expect(meta.fontSize).to.equal('12px');
|
|
67
|
+
expect(meta.color).to.equal('rgb(80, 97, 122)');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('gives chart labels the subtle text colour', () => {
|
|
71
|
+
setTheme();
|
|
72
|
+
expect(getComputedStyle(root).getPropertyValue('--zn-chart-text-color').trim().toLowerCase()).to.equal('#66768e');
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('puts a toggle\'s side label first with the switch at the far edge', () => {
|
|
76
|
+
setTheme();
|
|
77
|
+
expect(token(root, '--zn-toggle-side-label-direction')).to.equal('row');
|
|
78
|
+
expect(token(root, '--zn-toggle-side-label-justify')).to.equal('space-between');
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('keeps the secondary button border neutral when a host re-points the active border', async () => {
|
|
82
|
+
setTheme();
|
|
83
|
+
root.style.setProperty('--zn-color-border-active', '8, 94, 253');
|
|
84
|
+
try {
|
|
85
|
+
const button = await fixture(html`<zn-button color="secondary">Add</zn-button>`);
|
|
86
|
+
await (button as unknown as {updateComplete: Promise<unknown>}).updateComplete;
|
|
87
|
+
const base = button.shadowRoot!.querySelector('.button')!;
|
|
88
|
+
expect(getComputedStyle(base).borderTopColor).to.equal('rgb(186, 200, 217)');
|
|
89
|
+
} finally {
|
|
90
|
+
root.style.removeProperty('--zn-color-border-active');
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('gives a bordered icon button the secondary button edge', async () => {
|
|
95
|
+
setTheme();
|
|
96
|
+
const secondary = await fixture(html`<zn-button color="secondary">Add</zn-button>`);
|
|
97
|
+
const icon = await fixture(html`<zn-button icon="ellipsis" icon-button="small"></zn-button>`);
|
|
98
|
+
await (secondary as unknown as {updateComplete: Promise<unknown>}).updateComplete;
|
|
99
|
+
await (icon as unknown as {updateComplete: Promise<unknown>}).updateComplete;
|
|
100
|
+
const secondaryBase = getComputedStyle(secondary.shadowRoot!.querySelector('.button')!);
|
|
101
|
+
const iconBase = getComputedStyle(icon.shadowRoot!.querySelector('.button')!);
|
|
102
|
+
expect(iconBase.borderTopColor).to.equal(secondaryBase.borderTopColor);
|
|
103
|
+
expect(iconBase.boxShadow).to.equal(secondaryBase.boxShadow);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('sizes a modal page title as a dialog heading', async () => {
|
|
107
|
+
setTheme();
|
|
108
|
+
const page = await fixture<HTMLElement>(html`<zn-page modal caption="Add section"></zn-page>`);
|
|
109
|
+
await (page as unknown as {updateComplete: Promise<unknown>}).updateComplete;
|
|
110
|
+
const caption = page.shadowRoot!.querySelector('.header__caption')!;
|
|
111
|
+
expect(getComputedStyle(caption).fontSize).to.equal('16px');
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('draws the openbyte dark palette for a host that sets only the legacy t="dark"', () => {
|
|
115
|
+
root.removeAttribute('m');
|
|
116
|
+
root.setAttribute('t', 'dark');
|
|
117
|
+
expect(token(root, '--zn-primary')).to.equal('53,106,212');
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('keeps openbyte tokens on a component that reflects a legacy t', async () => {
|
|
121
|
+
root.setAttribute('t', 'light');
|
|
122
|
+
const button = await fixture(html`<zn-button>Add</zn-button>`);
|
|
123
|
+
expect(button.getAttribute('t')).to.equal('light');
|
|
124
|
+
expect(token(button, '--zn-color-primary')).to.equal('8,94,253');
|
|
125
|
+
});
|
|
126
|
+
});
|
package/src/internal/theme.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {signal, SignalWatcher} from '@lit-labs/signals';
|
|
2
2
|
|
|
3
|
-
// Theming
|
|
4
|
-
// `t` —
|
|
5
|
-
// `m` — the rendered mode ('light' | 'dark')
|
|
3
|
+
// Theming is driven by two attributes:
|
|
4
|
+
// `t` — a legacy theme name; OpenByte is the only theme and applies without it.
|
|
5
|
+
// `m` — the rendered mode ('light' | 'dark').
|
|
6
6
|
// The host shell owns both attributes on <html>; components mirror them so CSS
|
|
7
|
-
// selectors like `[m="dark"]`
|
|
7
|
+
// selectors like `[m="dark"]` keep working.
|
|
8
8
|
|
|
9
9
|
function readRootAttribute(name: string, fallback: string): string {
|
|
10
10
|
if (typeof document === 'undefined') return fallback;
|
|
@@ -19,13 +19,13 @@ const ZincElementBase: typeof LitElement & Constructor<SignalWatcherApi> = Signa
|
|
|
19
19
|
export default class ZincElement extends ZincElementBase {
|
|
20
20
|
@property() dir: string; // LTR or RTL direction
|
|
21
21
|
@property() lang: string; // Language
|
|
22
|
-
@property({reflect: true}) t: string; //
|
|
22
|
+
@property({reflect: true}) t: string; // Legacy theme name
|
|
23
23
|
@property({reflect: true}) m: string; // Rendered mode (light or dark)
|
|
24
24
|
|
|
25
25
|
protected override willUpdate(changed: PropertyValues): void {
|
|
26
26
|
// Reading these signals here subscribes this component to theme changes via
|
|
27
27
|
// the SignalWatcher mixin. The reflected attributes keep CSS selectors like
|
|
28
|
-
// `[m="dark"]`
|
|
28
|
+
// `[m="dark"]` working inside shadow roots.
|
|
29
29
|
this.t = themeSignal.get();
|
|
30
30
|
this.m = modeSignal.get();
|
|
31
31
|
super.willUpdate(changed);
|
package/src/selection-card.scss
CHANGED
|
@@ -183,6 +183,31 @@ $control-positions: (
|
|
|
183
183
|
}
|
|
184
184
|
}
|
|
185
185
|
|
|
186
|
+
/* A plain contained radio or checkbox shows its state through the container, so it drops the indicator too. */
|
|
187
|
+
:host([contained]) [part~="base"]:not(.selection-card) {
|
|
188
|
+
padding-inline-start: var(--zn-spacing-large);
|
|
189
|
+
|
|
190
|
+
.selection-card__control {
|
|
191
|
+
display: none;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.selection-card__content {
|
|
195
|
+
margin-inline-start: 0;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
&:has(input:focus-visible) {
|
|
199
|
+
outline: var(--zn-focus-ring);
|
|
200
|
+
outline-offset: var(--zn-focus-ring-offset);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/* `centered` centres a plain contained radio or checkbox's text, evening out the start padding left for the indicator. */
|
|
205
|
+
:host([contained][centered]) [part~="base"]:not(.selection-card) {
|
|
206
|
+
justify-content: center;
|
|
207
|
+
padding-inline: var(--zn-spacing-large);
|
|
208
|
+
text-align: center;
|
|
209
|
+
}
|
|
210
|
+
|
|
186
211
|
/* Contained radios and checkboxes are rounded by default; `square` opts out. */
|
|
187
212
|
:host([contained]) [part~="base"] {
|
|
188
213
|
border-radius: var(--zn-selection-card-border-radius, var(--zn-border-radius));
|
|
@@ -4,3 +4,4 @@
|
|
|
4
4
|
// return type. Runtime resolution (Vite/esbuild) is untouched - only `tsc`
|
|
5
5
|
// reads `paths`.
|
|
6
6
|
export function setViewport(viewport: {width: number; height: number}): Promise<void>;
|
|
7
|
+
export function emulateMedia(media: {reducedMotion?: 'reduce' | 'no-preference'}): Promise<void>;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import {detectColorFormat, formatColor, oklchParts, parseColor} from './color';
|
|
2
|
+
import {expect} from '@open-wc/testing';
|
|
3
|
+
|
|
4
|
+
describe('color utilities', () => {
|
|
5
|
+
it('parses every supported syntax', () => {
|
|
6
|
+
expect(parseColor('#085EFD')).to.deep.equal([8, 94, 253]);
|
|
7
|
+
expect(parseColor('085efd')).to.deep.equal([8, 94, 253]);
|
|
8
|
+
expect(parseColor('#abc')).to.deep.equal([170, 187, 204]);
|
|
9
|
+
expect(parseColor('rgb(8, 94, 253)')).to.deep.equal([8, 94, 253]);
|
|
10
|
+
expect(parseColor('8 94 253')).to.deep.equal([8, 94, 253]);
|
|
11
|
+
expect(parseColor('hsl(0, 100%, 50%)')).to.deep.equal([255, 0, 0]);
|
|
12
|
+
expect(parseColor('oklch(62.8% 0.258 29.2)')).to.deep.equal([255, 0, 0]);
|
|
13
|
+
expect(parseColor('oklch(0.628 0.258 29.2)')).to.deep.equal([255, 0, 0]);
|
|
14
|
+
expect(parseColor('rebeccapurple')).to.deep.equal([102, 51, 153]);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('rejects text that is not a colour', () => {
|
|
18
|
+
expect(parseColor('not a colour')).to.equal(null);
|
|
19
|
+
expect(parseColor('rgb(0, 0)')).to.equal(null);
|
|
20
|
+
expect(parseColor('')).to.equal(null);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('detects the format a colour is written in', () => {
|
|
24
|
+
expect(detectColorFormat('#085EFD')).to.equal('hex');
|
|
25
|
+
expect(detectColorFormat('rgb(8, 94, 253)')).to.equal('rgb');
|
|
26
|
+
expect(detectColorFormat('hsl(219, 98%, 51%)')).to.equal('hsl');
|
|
27
|
+
expect(detectColorFormat('oklch(58% 0.2 262)')).to.equal('oklch');
|
|
28
|
+
expect(detectColorFormat('rebeccapurple')).to.equal(null);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('writes a colour out in each format', () => {
|
|
32
|
+
const rgb = parseColor('#085EFD')!;
|
|
33
|
+
expect(formatColor(rgb, 'hex')).to.equal('#085EFD');
|
|
34
|
+
expect(formatColor(rgb, 'rgb')).to.equal('rgb(8, 94, 253)');
|
|
35
|
+
expect(formatColor(rgb, 'hsl')).to.equal('hsl(219, 98%, 51%)');
|
|
36
|
+
expect(formatColor(rgb, 'oklch')).to.equal('oklch(54.8% 0.247 262)');
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('computes OKLCH from the OKLab transform', () => {
|
|
40
|
+
expect(oklchParts([255, 0, 0])).to.deep.equal([62.8, 0.258, 29.2]);
|
|
41
|
+
expect(oklchParts([255, 255, 255])).to.deep.equal([100, 0, 0]);
|
|
42
|
+
});
|
|
43
|
+
});
|