@kubex/zinc 1.1.185 → 2.0.1
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 +10319 -2666
- package/dist/vscode.html-custom-data.json +1012 -57
- package/dist/web-types.json +2097 -145
- package/dist/zn.d.ts +2294 -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 +136 -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 +632 -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
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import '../../../dist/zn.min.js';
|
|
2
|
-
import { expect, fixture, html } from '@open-wc/testing';
|
|
2
|
+
import { expect, fixture, html, waitUntil } from '@open-wc/testing';
|
|
3
3
|
import type ZnSplitPane from './split-pane.component';
|
|
4
4
|
|
|
5
|
+
// Narrow-parent merging is applied from the ResizeObserver, which delivers after layout and before paint.
|
|
6
|
+
const afterLayout = () => new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve)));
|
|
7
|
+
|
|
5
8
|
describe('<zn-split-pane>', () => {
|
|
6
9
|
it('should render a component', async () => {
|
|
7
10
|
const el = await fixture(html` <zn-split-pane></zn-split-pane> `);
|
|
@@ -71,6 +74,7 @@ describe('<zn-split-pane>', () => {
|
|
|
71
74
|
</zn-split-pane>
|
|
72
75
|
`);
|
|
73
76
|
await el.updateComplete;
|
|
77
|
+
await afterLayout();
|
|
74
78
|
const nested = el.querySelector<ZnSplitPane>('zn-split-pane')!;
|
|
75
79
|
await nested.updateComplete;
|
|
76
80
|
const navItems = Array.from(el.shadowRoot!.querySelectorAll('#split-nav li'));
|
|
@@ -99,6 +103,7 @@ describe('<zn-split-pane>', () => {
|
|
|
99
103
|
</zn-split-pane>
|
|
100
104
|
`);
|
|
101
105
|
await el.updateComplete;
|
|
106
|
+
await afterLayout();
|
|
102
107
|
const nested = el.querySelector<ZnSplitPane>('zn-split-pane')!;
|
|
103
108
|
await nested.updateComplete;
|
|
104
109
|
|
|
@@ -140,6 +145,7 @@ describe('<zn-split-pane>', () => {
|
|
|
140
145
|
</zn-split-pane>
|
|
141
146
|
`);
|
|
142
147
|
await el.updateComplete;
|
|
148
|
+
await afterLayout();
|
|
143
149
|
const nested = el.querySelector<ZnSplitPane>('zn-split-pane')!;
|
|
144
150
|
const deepNested = nested.querySelector<ZnSplitPane>('zn-split-pane')!;
|
|
145
151
|
await nested.updateComplete;
|
|
@@ -174,7 +180,7 @@ describe('<zn-split-pane>', () => {
|
|
|
174
180
|
window.dispatchEvent(new MouseEvent('mouseup', {bubbles: true}));
|
|
175
181
|
|
|
176
182
|
expect((el.shadowRoot?.querySelector('style')?.textContent ?? '')).to.contain('--min-secondary-panel-size: 25%');
|
|
177
|
-
expect(
|
|
183
|
+
expect(el.shadowRoot!.querySelector<HTMLElement>('#primary-pane')!.style.getPropertyValue('--zn-split-pane-size')).to.equal('75%');
|
|
178
184
|
});
|
|
179
185
|
|
|
180
186
|
it('should keep the secondary pane above the configured pixel minimum', async () => {
|
|
@@ -192,6 +198,20 @@ describe('<zn-split-pane>', () => {
|
|
|
192
198
|
window.dispatchEvent(new MouseEvent('mouseup', {bubbles: true}));
|
|
193
199
|
|
|
194
200
|
expect((el.shadowRoot?.querySelector('style')?.textContent ?? '')).to.contain('--min-secondary-panel-size: 125px');
|
|
195
|
-
expect(
|
|
201
|
+
expect(el.shadowRoot!.querySelector<HTMLElement>('#primary-pane')!.style.getPropertyValue('--zn-split-pane-size')).to.equal('275px');
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it('keeps a preferred secondary pane at its pixel size when the container resizes', async () => {
|
|
205
|
+
const el = await fixture<ZnSplitPane>(html`
|
|
206
|
+
<zn-split-pane vertical secondary pixels min-size="50" initial-size="100" style="display: flex; height: 400px;">
|
|
207
|
+
<div slot="primary">Primary content</div>
|
|
208
|
+
<div slot="secondary">Secondary content</div>
|
|
209
|
+
</zn-split-pane>
|
|
210
|
+
`);
|
|
211
|
+
const secondaryHeight = () => el.shadowRoot!.querySelector('#secondary-pane')!.getBoundingClientRect().height;
|
|
212
|
+
await waitUntil(() => Math.abs(secondaryHeight() - 100) < 2);
|
|
213
|
+
|
|
214
|
+
el.style.height = '600px';
|
|
215
|
+
await waitUntil(() => Math.abs(secondaryHeight() - 100) < 2, 'secondary pane stays at 100px');
|
|
196
216
|
});
|
|
197
217
|
});
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import {classMap} from "lit/directives/class-map.js";
|
|
2
|
-
import {type CSSResultGroup, html, unsafeCSS} from 'lit';
|
|
2
|
+
import {type CSSResultGroup, html, type PropertyValues, unsafeCSS} from 'lit';
|
|
3
3
|
import {ifDefined} from "lit/directives/if-defined.js";
|
|
4
|
-
import {
|
|
4
|
+
import {keyed} from "lit/directives/keyed.js";
|
|
5
|
+
import {property, state} from 'lit/decorators.js';
|
|
5
6
|
import ZincElement from '../../internal/zinc-element';
|
|
6
7
|
import type {IconColor} from '../icon';
|
|
7
8
|
|
|
@@ -28,8 +29,27 @@ export default class ZnStatsTile extends ZincElement {
|
|
|
28
29
|
@property() color: 'primary' | 'error' | 'info' | 'warning' | 'success' | 'neutral' = 'primary';
|
|
29
30
|
|
|
30
31
|
/** `value-first` puts the amount under the caption with the description beneath it, and tints the amount rather
|
|
31
|
-
* than the description with `color`.
|
|
32
|
-
|
|
32
|
+
* than the description with `color`. `tile` is a self-contained card: caption, amount, a footer with the delta and
|
|
33
|
+
* `description`, an optional sparkline, and the default slot beneath. */
|
|
34
|
+
@property({reflect: true}) layout: 'default' | 'value-first' | 'tile' = 'default';
|
|
35
|
+
|
|
36
|
+
/** Comma-separated values drawn as a sparkline under a `tile`. */
|
|
37
|
+
@property() spark = '';
|
|
38
|
+
|
|
39
|
+
/** The sparkline's colour: a tone (`primary`, `info`, `success`, `warning`, `danger`, `ai`) or any CSS colour. */
|
|
40
|
+
@property({attribute: 'spark-color'}) sparkColor = '';
|
|
41
|
+
|
|
42
|
+
/** Treats a rise as bad news, so an increase is coloured as a loss (declines, refunds, failures). */
|
|
43
|
+
@property({type: Boolean, attribute: 'invert-delta'}) invertDelta = false;
|
|
44
|
+
|
|
45
|
+
/** A coloured dot before a `tile`'s caption. */
|
|
46
|
+
@property({reflect: true}) tone: '' | 'info' | 'success' | 'warning' | 'danger' | 'neutral' = '';
|
|
47
|
+
|
|
48
|
+
/** Renders a `tile` as a toggle button, for tiles that filter the content below them. */
|
|
49
|
+
@property({type: Boolean}) selectable = false;
|
|
50
|
+
|
|
51
|
+
/** Whether a `selectable` tile is pressed, or a linked tile marks the view the page is showing. */
|
|
52
|
+
@property({type: Boolean, reflect: true}) selected = false;
|
|
33
53
|
|
|
34
54
|
/** How `show-delta` compares with `previous`: the relative change, or the difference in points. */
|
|
35
55
|
@property({attribute: 'delta-format'}) deltaFormat: 'percent' | 'points' = 'percent';
|
|
@@ -46,9 +66,62 @@ export default class ZnStatsTile extends ZincElement {
|
|
|
46
66
|
/** Pads the stat, for stats laid edge to edge in a flush container such as `zn-cols no-gap divide`. */
|
|
47
67
|
@property({type: Boolean}) padded = false;
|
|
48
68
|
|
|
69
|
+
/** Makes the whole stat a link, marked with an arrow in its corner. */
|
|
70
|
+
@property() href: string;
|
|
71
|
+
|
|
72
|
+
@property({attribute: 'data-target'}) dataTarget: string;
|
|
73
|
+
|
|
74
|
+
@property() gaid: string;
|
|
75
|
+
|
|
76
|
+
/** JSON fetched once to fill the stat: any of `caption`, `amount`, `description`, `previous`, `spark` and
|
|
77
|
+
* `sparkColor`. The stat's own attributes show until it loads. */
|
|
78
|
+
@property({attribute: 'data-url'}) dataUrl = '';
|
|
79
|
+
|
|
80
|
+
/** Shows a `tile` as a placeholder. Set while `data-url` loads; the sparkline draws in once it clears. */
|
|
81
|
+
@property({type: Boolean, reflect: true}) loading = false;
|
|
82
|
+
|
|
83
|
+
@state() private loaded = false;
|
|
84
|
+
|
|
85
|
+
private static readonly loadableKeys = ['caption', 'amount', 'description', 'previous', 'spark', 'sparkColor'] as const;
|
|
86
|
+
|
|
87
|
+
protected willUpdate(changed: PropertyValues) {
|
|
88
|
+
super.willUpdate(changed);
|
|
89
|
+
if (changed.has('dataUrl') && this.dataUrl) this.loading = true;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
protected updated(changed: PropertyValues) {
|
|
93
|
+
super.updated(changed);
|
|
94
|
+
if (changed.has('dataUrl') && this.dataUrl) void this.loadData(this.dataUrl);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
private async loadData(url: string) {
|
|
98
|
+
try {
|
|
99
|
+
const res = await fetch(url, {
|
|
100
|
+
credentials: 'same-origin',
|
|
101
|
+
headers: {Accept: 'application/json', 'x-kx-fetch-style': 'raw-json'},
|
|
102
|
+
});
|
|
103
|
+
if (url !== this.dataUrl) return;
|
|
104
|
+
if (res.ok) {
|
|
105
|
+
const body = await res.json() as Partial<Record<typeof ZnStatsTile.loadableKeys[number], unknown>>;
|
|
106
|
+
for (const key of ZnStatsTile.loadableKeys) {
|
|
107
|
+
if (body[key] !== undefined && body[key] !== null) this[key] = String(body[key]);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
} catch { /* fall back to the stat's own attributes */ }
|
|
111
|
+
if (url === this.dataUrl) {
|
|
112
|
+
this.loaded = true;
|
|
113
|
+
this.loading = false;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Amounts are display strings, so drop separators and symbols ("48,201", "$2,941") before comparing.
|
|
118
|
+
private parseNumber(value: string) {
|
|
119
|
+
return parseFloat(String(value).replace(/[^0-9.-]/g, ''));
|
|
120
|
+
}
|
|
121
|
+
|
|
49
122
|
calcPercentageDifference() {
|
|
50
|
-
const previous =
|
|
51
|
-
const amount =
|
|
123
|
+
const previous = this.parseNumber(this.previous);
|
|
124
|
+
const amount = this.parseNumber(this.amount);
|
|
52
125
|
|
|
53
126
|
let diff = ((amount - previous) / previous) * 100;
|
|
54
127
|
diff = Math.round(diff * 10) / 10;
|
|
@@ -105,8 +178,8 @@ export default class ZnStatsTile extends ZincElement {
|
|
|
105
178
|
diffIcon = '-';
|
|
106
179
|
}
|
|
107
180
|
|
|
108
|
-
if (this.deltaFormat === 'points') {
|
|
109
|
-
return html`<p class="diff">${this.pointsDelta()} on Previous Period</p>`;
|
|
181
|
+
if (this.deltaFormat === 'points' || this.fromZero()) {
|
|
182
|
+
return html`<p class="diff">${this.pointsDelta(this.deltaFormat === 'points' ? ' pts' : '')} on Previous Period</p>`;
|
|
110
183
|
}
|
|
111
184
|
|
|
112
185
|
return html`<p class="diff"><span class="${diffClass}">${diffIcon}${diff}%</span> on Previous Period</p>`;
|
|
@@ -115,13 +188,18 @@ export default class ZnStatsTile extends ZincElement {
|
|
|
115
188
|
return null;
|
|
116
189
|
}
|
|
117
190
|
|
|
118
|
-
|
|
119
|
-
|
|
191
|
+
// A change from zero has no percentage, so it shows as the plain difference.
|
|
192
|
+
private fromZero() {
|
|
193
|
+
return this.parseNumber(this.previous) === 0;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
private pointsDelta(unit = ' pts') {
|
|
197
|
+
const diff = Math.round((this.parseNumber(this.amount) - this.parseNumber(this.previous)) * 10) / 10;
|
|
120
198
|
if (diff > 0) {
|
|
121
|
-
return html`<span class="positive">+${diff}
|
|
199
|
+
return html`<span class="positive">+${diff}${unit}</span>`;
|
|
122
200
|
}
|
|
123
201
|
if (diff < 0) {
|
|
124
|
-
return html`<span class="negative">-${Math.abs(diff)}
|
|
202
|
+
return html`<span class="negative">-${Math.abs(diff)}${unit}</span>`;
|
|
125
203
|
}
|
|
126
204
|
return html`<span class="neutral">No change</span>`;
|
|
127
205
|
}
|
|
@@ -152,6 +230,9 @@ export default class ZnStatsTile extends ZincElement {
|
|
|
152
230
|
}
|
|
153
231
|
|
|
154
232
|
private percentDelta() {
|
|
233
|
+
if (this.fromZero()) {
|
|
234
|
+
return this.pointsDelta('');
|
|
235
|
+
}
|
|
155
236
|
const diff = this.calcPercentageDifference();
|
|
156
237
|
if (diff > 0) {
|
|
157
238
|
return html`<span class="positive">+${diff}%</span>`;
|
|
@@ -162,11 +243,125 @@ export default class ZnStatsTile extends ZincElement {
|
|
|
162
243
|
return html`<span class="neutral">No change</span>`;
|
|
163
244
|
}
|
|
164
245
|
|
|
246
|
+
private tileDelta() {
|
|
247
|
+
if (!this.showDelta) {
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const points = this.deltaFormat === 'points';
|
|
252
|
+
const absolute = points || this.fromZero();
|
|
253
|
+
const diff = absolute
|
|
254
|
+
? Math.round((this.parseNumber(this.amount) - this.parseNumber(this.previous)) * 10) / 10
|
|
255
|
+
: this.calcPercentageDifference();
|
|
256
|
+
const direction = diff > 0 ? 'up' : diff < 0 ? 'down' : 'flat';
|
|
257
|
+
const outcome = direction === 'flat' ? 'flat' : (direction === 'up') !== this.invertDelta ? 'good' : 'bad';
|
|
258
|
+
const text = direction === 'flat' && absolute ? 'No change'
|
|
259
|
+
: points ? `${Math.abs(diff)} pts` : absolute ? `${Math.abs(diff)}` : `${Math.abs(diff)}%`;
|
|
260
|
+
|
|
261
|
+
return html`
|
|
262
|
+
<span class="delta delta--${outcome}">
|
|
263
|
+
${direction === 'flat' ? null : html`
|
|
264
|
+
<zn-icon src="${direction === 'up' ? 'arrow-up-right@lu' : 'arrow-down-right@lu'}" size="14"></zn-icon>`}
|
|
265
|
+
${text}
|
|
266
|
+
</span>`;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
private sparkStroke() {
|
|
270
|
+
const tones: Record<string, string> = {
|
|
271
|
+
primary: 'var(--zn-chart-color-1, rgb(var(--zn-primary)))',
|
|
272
|
+
info: 'var(--zn-chart-color-1, rgb(var(--zn-color-info)))',
|
|
273
|
+
success: 'var(--zn-chart-color-2, rgb(var(--zn-color-success)))',
|
|
274
|
+
ai: 'var(--zn-chart-color-3, rgb(var(--zn-color-ai)))',
|
|
275
|
+
warning: 'var(--zn-chart-color-4, rgb(var(--zn-color-warning)))',
|
|
276
|
+
danger: 'var(--zn-chart-color-5, rgb(var(--zn-color-error)))',
|
|
277
|
+
error: 'var(--zn-chart-color-5, rgb(var(--zn-color-error)))',
|
|
278
|
+
};
|
|
279
|
+
return tones[this.sparkColor] ?? (this.sparkColor || tones.primary);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
private renderSpark() {
|
|
283
|
+
if (this.loading) {
|
|
284
|
+
return html`<div class="spark spark--loading" part="spark">
|
|
285
|
+
<span class="spark__track" style="--stat-spark: ${this.sparkStroke()}"></span>
|
|
286
|
+
</div>`;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const data = this.spark.split(',').map(v => parseFloat(v)).filter(v => !Number.isNaN(v));
|
|
290
|
+
if (data.length < 2) {
|
|
291
|
+
return null;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const width = 100;
|
|
295
|
+
const height = 28;
|
|
296
|
+
const max = Math.max(...data);
|
|
297
|
+
const min = Math.min(...data);
|
|
298
|
+
const range = max - min || 1;
|
|
299
|
+
const line = data
|
|
300
|
+
.map((v, i) => `${i ? 'L' : 'M'}${((i / (data.length - 1)) * width).toFixed(1)} ${(height - 3 - ((v - min) / range) * (height - 6)).toFixed(1)}`)
|
|
301
|
+
.join(' ');
|
|
302
|
+
const color = this.sparkStroke();
|
|
303
|
+
|
|
304
|
+
return html`
|
|
305
|
+
<div class="spark" part="spark">
|
|
306
|
+
<svg viewBox="0 0 ${width} ${height}" preserveAspectRatio="none" aria-hidden="true">
|
|
307
|
+
<defs>
|
|
308
|
+
<linearGradient id="spark-fill" x1="0" x2="0" y1="0" y2="1">
|
|
309
|
+
<stop offset="0" style="stop-color: ${color}; stop-opacity: .22"></stop>
|
|
310
|
+
<stop offset="1" style="stop-color: ${color}; stop-opacity: 0"></stop>
|
|
311
|
+
</linearGradient>
|
|
312
|
+
</defs>
|
|
313
|
+
<path d="${line} L${width} ${height} L0 ${height} Z" fill="url(#spark-fill)"></path>
|
|
314
|
+
<path d="${line}" fill="none" style="stroke: ${color}" stroke-width="1.75"
|
|
315
|
+
vector-effect="non-scaling-stroke" stroke-linejoin="round" stroke-linecap="round"></path>
|
|
316
|
+
</svg>
|
|
317
|
+
</div>`;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
private renderTile() {
|
|
321
|
+
const delta = this.loading ? null : this.tileDelta();
|
|
322
|
+
const description = this.description
|
|
323
|
+
|| (this.loading ? html`<span class="loading-text">Loading</span>` : '');
|
|
324
|
+
const classes = classMap({
|
|
325
|
+
'stat-tile': true,
|
|
326
|
+
'stat-tile--card': true,
|
|
327
|
+
'stat-tile--selectable': this.selectable,
|
|
328
|
+
'stat-tile--loaded': this.loaded,
|
|
329
|
+
[`stat-tile--tone-${this.tone}`]: !!this.tone,
|
|
330
|
+
});
|
|
331
|
+
// Keyed so the loaded content is new nodes, which replays its entrance animations
|
|
332
|
+
const body = keyed(this.loading, html`
|
|
333
|
+
<span class="caption" part="caption">${this.caption}</span>
|
|
334
|
+
<span class="amount" part="amount">${this.loading
|
|
335
|
+
? html`<span class="amount__skeleton"></span>`
|
|
336
|
+
: this.renderAmount()}</span>
|
|
337
|
+
${delta || description ? html`
|
|
338
|
+
<span class="foot" part="foot">${delta}${description ? html`<span>${description}</span>` : null}</span>` : null}
|
|
339
|
+
${this.renderSpark()}
|
|
340
|
+
<slot></slot>`);
|
|
341
|
+
|
|
342
|
+
return this.selectable
|
|
343
|
+
? html`<button type="button" part="base" class="${classes}" aria-pressed="${this.selected ? 'true' : 'false'}"
|
|
344
|
+
@click="${() => { this.selected = !this.selected; }}">${body}</button>`
|
|
345
|
+
: html`<div part="base" class="${classes}">${body}</div>`;
|
|
346
|
+
}
|
|
347
|
+
|
|
165
348
|
render() {
|
|
166
|
-
|
|
167
|
-
|
|
349
|
+
const tile = this.layout === 'tile'
|
|
350
|
+
? this.renderTile()
|
|
351
|
+
: this.layout === 'value-first' ? this.renderValueFirst() : this.renderDefault();
|
|
352
|
+
if (!this.href) {
|
|
353
|
+
return tile;
|
|
168
354
|
}
|
|
169
355
|
|
|
356
|
+
return html`
|
|
357
|
+
<a class="stat-link" part="link" href="${this.href}" data-target="${ifDefined(this.dataTarget)}"
|
|
358
|
+
gaid="${ifDefined(this.gaid)}">
|
|
359
|
+
${tile}
|
|
360
|
+
<zn-icon class="stat-link__icon" src="arrow-up-right@lu" size="${this.layout === 'tile' ? 18 : 16}" aria-hidden="true"></zn-icon>
|
|
361
|
+
</a>`;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
private renderDefault() {
|
|
170
365
|
return html`
|
|
171
366
|
<div class="${classMap({
|
|
172
367
|
'stat-tile': true,
|
|
@@ -6,10 +6,307 @@
|
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
// A box of its own lets zn-cols draw its dividers between value-first stats.
|
|
9
|
-
:host([layout="value-first"])
|
|
9
|
+
:host([layout="value-first"]),
|
|
10
|
+
:host([layout="tile"]) {
|
|
10
11
|
display: block;
|
|
11
12
|
}
|
|
12
13
|
|
|
14
|
+
// Doubled class out-ranks the default layout's `.stat-tile .x` rules, which the card also matches.
|
|
15
|
+
.stat-tile.stat-tile--card {
|
|
16
|
+
display: flex;
|
|
17
|
+
flex-direction: column;
|
|
18
|
+
gap: 5px;
|
|
19
|
+
box-sizing: border-box;
|
|
20
|
+
width: 100%;
|
|
21
|
+
height: 100%;
|
|
22
|
+
margin: 0;
|
|
23
|
+
padding: var(--zn-density-card-padding, var(--zn-spacing-medium));
|
|
24
|
+
background: rgba(var(--zn-panel), var(--zn-panel-opacity, 1));
|
|
25
|
+
border: 1px solid rgb(var(--zn-border-color));
|
|
26
|
+
border-radius: var(--zn-panel-border-radius, var(--zn-border-radius));
|
|
27
|
+
box-shadow: var(--zn-panel-shadow, none);
|
|
28
|
+
font: inherit;
|
|
29
|
+
color: inherit;
|
|
30
|
+
text-align: left;
|
|
31
|
+
|
|
32
|
+
.caption {
|
|
33
|
+
display: flex;
|
|
34
|
+
align-items: center;
|
|
35
|
+
gap: 6px;
|
|
36
|
+
margin: 0;
|
|
37
|
+
font-family: var(--zn-text-chart-minor-font-family, inherit);
|
|
38
|
+
font-size: var(--zn-text-chart-minor-font-size);
|
|
39
|
+
line-height: 1.2;
|
|
40
|
+
font-weight: var(--zn-text-chart-minor-font-weight);
|
|
41
|
+
color: var(--zn-text-chart-minor-color);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.amount {
|
|
45
|
+
display: block;
|
|
46
|
+
flex-grow: 0;
|
|
47
|
+
padding: 0;
|
|
48
|
+
font-family: var(--zn-text-chart-major-font-family, inherit);
|
|
49
|
+
font-size: var(--zn-text-chart-major-font-size);
|
|
50
|
+
font-weight: var(--zn-text-chart-major-font-weight);
|
|
51
|
+
line-height: 1.1;
|
|
52
|
+
letter-spacing: var(--zn-text-chart-major-letter-spacing, normal);
|
|
53
|
+
font-variant-numeric: tabular-nums;
|
|
54
|
+
color: var(--zn-text-chart-major-color);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.foot {
|
|
58
|
+
display: flex;
|
|
59
|
+
align-items: center;
|
|
60
|
+
gap: 8px;
|
|
61
|
+
font-size: var(--zn-font-size-x-small);
|
|
62
|
+
color: var(--zn-text-color-muted);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.spark {
|
|
66
|
+
height: 28px;
|
|
67
|
+
margin-top: 2px;
|
|
68
|
+
|
|
69
|
+
svg {
|
|
70
|
+
display: block;
|
|
71
|
+
width: 100%;
|
|
72
|
+
height: 100%;
|
|
73
|
+
overflow: visible;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Holds the line box of a loaded amount, so the tile doesn't jump when the value arrives
|
|
78
|
+
.amount__skeleton {
|
|
79
|
+
position: relative;
|
|
80
|
+
display: block;
|
|
81
|
+
width: 2.4em;
|
|
82
|
+
height: .85em;
|
|
83
|
+
margin: .125em 0;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.spark--loading {
|
|
87
|
+
display: flex;
|
|
88
|
+
align-items: flex-end;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.spark__track {
|
|
92
|
+
position: relative;
|
|
93
|
+
overflow: hidden;
|
|
94
|
+
width: 100%;
|
|
95
|
+
height: 3px;
|
|
96
|
+
border-radius: 2px;
|
|
97
|
+
background: var(--zn-skeleton-background, rgb(var(--zn-border-color)));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
101
|
+
// A soft glow with no edges, so the placeholder never reads as a box
|
|
102
|
+
.amount__skeleton::after {
|
|
103
|
+
content: "";
|
|
104
|
+
position: absolute;
|
|
105
|
+
inset: 0;
|
|
106
|
+
width: 60%;
|
|
107
|
+
background: radial-gradient(ellipse at center, rgb(var(--zn-border-color)) 0%, transparent 70%);
|
|
108
|
+
animation: skeleton-sweep 1.4s ease-in-out infinite;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.spark__track::after {
|
|
112
|
+
content: "";
|
|
113
|
+
position: absolute;
|
|
114
|
+
inset: 0;
|
|
115
|
+
width: 40%;
|
|
116
|
+
border-radius: inherit;
|
|
117
|
+
background: linear-gradient(90deg, transparent, var(--stat-spark), transparent);
|
|
118
|
+
animation: spark-sweep 1.4s ease-in-out infinite;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.loading-text::after {
|
|
122
|
+
content: "";
|
|
123
|
+
display: inline-block;
|
|
124
|
+
width: 1.2em;
|
|
125
|
+
text-align: left;
|
|
126
|
+
animation: loading-dots 1.2s steps(1) infinite;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
&.stat-tile--loaded {
|
|
130
|
+
.amount {
|
|
131
|
+
animation: stat-fade .4s ease-out both;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.foot {
|
|
135
|
+
animation: stat-rise .4s .1s var(--zn-ease-out, ease-out) both;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.spark svg {
|
|
139
|
+
animation: spark-draw .8s .15s var(--zn-ease-out, ease-out) both;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
@keyframes spark-draw {
|
|
146
|
+
from { clip-path: inset(-2px 100% -2px -2px); }
|
|
147
|
+
to { clip-path: inset(-2px); }
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Fades at both ends instead of being clipped, so the glow has no edge to cut off
|
|
151
|
+
@keyframes skeleton-sweep {
|
|
152
|
+
from { transform: translateX(-40%); opacity: 0; }
|
|
153
|
+
25%, 75% { opacity: 1; }
|
|
154
|
+
to { transform: translateX(140%); opacity: 0; }
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
@keyframes spark-sweep {
|
|
158
|
+
from { transform: translateX(-100%); }
|
|
159
|
+
to { transform: translateX(250%); }
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
@keyframes stat-fade {
|
|
163
|
+
from { opacity: 0; }
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
@keyframes stat-rise {
|
|
167
|
+
from { opacity: 0; transform: translateY(6px); }
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
@keyframes loading-dots {
|
|
171
|
+
0% { content: ""; }
|
|
172
|
+
25% { content: "."; }
|
|
173
|
+
50% { content: ".."; }
|
|
174
|
+
75% { content: "..."; }
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.stat-tile--tone-info { --stat-tone: var(--zn-chart-color-1, rgb(var(--zn-color-info))); }
|
|
178
|
+
.stat-tile--tone-success { --stat-tone: var(--zn-chart-color-2, rgb(var(--zn-color-success))); }
|
|
179
|
+
.stat-tile--tone-warning { --stat-tone: var(--zn-chart-color-4, rgb(var(--zn-color-warning))); }
|
|
180
|
+
.stat-tile--tone-danger { --stat-tone: var(--zn-chart-color-5, rgb(var(--zn-color-error))); }
|
|
181
|
+
|
|
182
|
+
[class*="stat-tile--tone-"] .caption::before {
|
|
183
|
+
content: "";
|
|
184
|
+
flex: none;
|
|
185
|
+
width: 8px;
|
|
186
|
+
height: 8px;
|
|
187
|
+
border-radius: 50%;
|
|
188
|
+
background: var(--stat-tone, var(--zn-color-neutral-400));
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
@mixin selected-tile {
|
|
192
|
+
border-color: rgb(var(--zn-primary));
|
|
193
|
+
background: var(--zn-color-primary-50);
|
|
194
|
+
// Inset, so a container clipping at the tile's edge can't cut the ring off
|
|
195
|
+
box-shadow: inset 0 0 0 1px rgb(var(--zn-primary));
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.stat-tile--selectable {
|
|
199
|
+
cursor: pointer;
|
|
200
|
+
|
|
201
|
+
&:hover {
|
|
202
|
+
border-color: var(--zn-color-primary-300);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
&:focus-visible {
|
|
206
|
+
outline: none;
|
|
207
|
+
box-shadow: 0 0 0 var(--zn-focus-ring-width) var(--zn-input-focus-ring-color);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
&[aria-pressed="true"] {
|
|
211
|
+
@include selected-tile;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// A linked tile marks the view it is showing, such as the filter a page is on.
|
|
216
|
+
:host([selected][href]) .stat-tile--card {
|
|
217
|
+
@include selected-tile;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.delta {
|
|
221
|
+
display: inline-flex;
|
|
222
|
+
align-items: center;
|
|
223
|
+
gap: 2px;
|
|
224
|
+
font-weight: var(--zn-font-weight-semibold);
|
|
225
|
+
font-variant-numeric: tabular-nums;
|
|
226
|
+
|
|
227
|
+
&--good { color: var(--zn-color-success-fg); }
|
|
228
|
+
&--bad { color: var(--zn-color-danger-fg); }
|
|
229
|
+
&--flat { color: var(--zn-text-color-muted); }
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
.stat-link {
|
|
233
|
+
position: relative;
|
|
234
|
+
display: block;
|
|
235
|
+
color: inherit;
|
|
236
|
+
text-decoration: none;
|
|
237
|
+
|
|
238
|
+
&:hover {
|
|
239
|
+
background-color: var(--zn-stat-hover-background, var(--zn-tile-hover-background, var(--zn-color-neutral-100)));
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
&:focus-visible {
|
|
243
|
+
outline: 2px solid rgb(var(--zn-primary));
|
|
244
|
+
outline-offset: -2px;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// A linked tile is a card in its own right, so it lifts rather than tinting the row.
|
|
249
|
+
:host([layout="tile"]) .stat-link {
|
|
250
|
+
height: 100%;
|
|
251
|
+
|
|
252
|
+
&:hover {
|
|
253
|
+
background-color: transparent;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Gated so a tap doesn't leave the lift stuck on touch screens.
|
|
257
|
+
@media (hover: hover) and (pointer: fine) {
|
|
258
|
+
&:hover .stat-tile--card {
|
|
259
|
+
border-color: var(--zn-color-primary-400);
|
|
260
|
+
box-shadow: var(--zn-shadow-medium);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
&:focus-visible {
|
|
265
|
+
outline: none;
|
|
266
|
+
|
|
267
|
+
.stat-tile--card {
|
|
268
|
+
border-color: rgb(var(--zn-primary));
|
|
269
|
+
box-shadow: 0 0 0 var(--zn-focus-ring-width) var(--zn-input-focus-ring-color);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
.stat-tile--card {
|
|
274
|
+
transition: border-color var(--zn-transition-fast), box-shadow var(--zn-transition-fast);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
@media (hover: hover) and (pointer: fine) {
|
|
279
|
+
:host([layout="tile"][selected]) .stat-link:hover .stat-tile--card {
|
|
280
|
+
border-color: rgb(var(--zn-primary));
|
|
281
|
+
box-shadow: inset 0 0 0 1px rgb(var(--zn-primary)), var(--zn-shadow-medium);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
.stat-link__icon {
|
|
286
|
+
position: absolute;
|
|
287
|
+
top: var(--zn-spacing-medium);
|
|
288
|
+
right: var(--zn-spacing-medium);
|
|
289
|
+
color: var(--zn-color-primary-fg, rgb(var(--zn-primary)));
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
:host([layout="tile"]) .stat-link__icon {
|
|
293
|
+
top: var(--zn-density-card-padding, var(--zn-spacing-medium));
|
|
294
|
+
right: var(--zn-density-card-padding, var(--zn-spacing-medium));
|
|
295
|
+
transition: transform var(--zn-transition-fast) var(--zn-ease-out);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
@media (hover: hover) and (pointer: fine) {
|
|
299
|
+
:host([layout="tile"]) .stat-link:hover .stat-link__icon {
|
|
300
|
+
transform: translate(1px, -1px);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
@media (prefers-reduced-motion: reduce) {
|
|
305
|
+
:host([layout="tile"]) .stat-link__icon {
|
|
306
|
+
transition: none;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
13
310
|
.stat-tile {
|
|
14
311
|
|
|
15
312
|
.caption, .description, .amount, .bottom {
|
|
@@ -40,9 +337,12 @@
|
|
|
40
337
|
|
|
41
338
|
.amount {
|
|
42
339
|
color: var(--zn-text-chart-major-color);
|
|
340
|
+
font-family: var(--zn-text-chart-major-font-family, inherit);
|
|
43
341
|
font-weight: var(--zn-text-chart-major-font-weight);
|
|
44
342
|
font-size: var(--zn-text-chart-major-font-size);
|
|
45
343
|
line-height: var(--zn-text-chart-major-line-height);
|
|
344
|
+
letter-spacing: var(--zn-text-chart-major-letter-spacing, normal);
|
|
345
|
+
font-variant-numeric: var(--zn-stat-numeric, normal);
|
|
46
346
|
display: flex;
|
|
47
347
|
flex-grow: 1;
|
|
48
348
|
align-items: center;
|
|
@@ -61,15 +361,15 @@
|
|
|
61
361
|
|
|
62
362
|
.diff {
|
|
63
363
|
.positive {
|
|
64
|
-
color:
|
|
364
|
+
color: var(--zn-color-success-fg);
|
|
65
365
|
}
|
|
66
366
|
|
|
67
367
|
.negative {
|
|
68
|
-
color:
|
|
368
|
+
color: var(--zn-color-danger-fg);
|
|
69
369
|
}
|
|
70
370
|
|
|
71
371
|
.neutral {
|
|
72
|
-
color:
|
|
372
|
+
color: var(--zn-color-warning-fg);
|
|
73
373
|
}
|
|
74
374
|
}
|
|
75
375
|
|
|
@@ -145,7 +445,7 @@
|
|
|
145
445
|
}
|
|
146
446
|
|
|
147
447
|
&--primary {
|
|
148
|
-
color: rgb(var(--zn-primary));
|
|
448
|
+
color: var(--zn-color-primary-fg, rgb(var(--zn-primary)));
|
|
149
449
|
}
|
|
150
450
|
|
|
151
451
|
&--error {
|