@kubex/zinc 1.1.184 → 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 +10344 -2583
- package/dist/vscode.html-custom-data.json +1102 -125
- package/dist/web-types.json +2217 -224
- package/dist/zn.d.ts +2306 -417
- package/dist/zn.min.css +1 -1
- package/dist/zn.min.js +2313 -1635
- 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/filter-wrapper.md +20 -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 +242 -11
- package/src/components/button/button.scss +133 -25
- 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 +17 -3
- package/src/components/cols/cols.scss +24 -13
- package/src/components/cols/cols.test.ts +33 -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 +364 -142
- 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/filter-wrapper/filter-wrapper.component.ts +45 -10
- package/src/components/filter-wrapper/filter-wrapper.test.ts +58 -2
- 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
|
@@ -1,10 +1,151 @@
|
|
|
1
1
|
import '../../../dist/zn.min.js';
|
|
2
2
|
import { expect, fixture, html } from '@open-wc/testing';
|
|
3
|
+
import type ZnStatsTile from './stat.component';
|
|
3
4
|
|
|
4
|
-
describe('<zn-
|
|
5
|
+
describe('<zn-stat>', () => {
|
|
5
6
|
it('should render a component', async () => {
|
|
6
|
-
const el = await fixture(html` <zn-
|
|
7
|
+
const el = await fixture(html` <zn-stat></zn-stat> `);
|
|
7
8
|
|
|
8
9
|
expect(el).to.exist;
|
|
10
|
+
expect(el.shadowRoot).to.exist;
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('renders a positive delta in the success text colour', async () => {
|
|
14
|
+
const el = await fixture<HTMLElement>(html`
|
|
15
|
+
<zn-stat amount="110" previous="100" show-delta style="--zn-color-success-fg: rgb(1, 2, 3)"></zn-stat>`);
|
|
16
|
+
const positive = el.shadowRoot!.querySelector('.positive')!;
|
|
17
|
+
|
|
18
|
+
expect(positive).to.exist;
|
|
19
|
+
expect(getComputedStyle(positive).color).to.equal('rgb(1, 2, 3)');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('renders the whole stat as a link with an arrow when given an href', async () => {
|
|
23
|
+
const el = await fixture<HTMLElement>(html`
|
|
24
|
+
<zn-stat layout="value-first" caption="Fails" amount="3" href="/fails" data-target="primary"></zn-stat>`);
|
|
25
|
+
await (el as unknown as {updateComplete: Promise<unknown>}).updateComplete;
|
|
26
|
+
const link = el.shadowRoot!.querySelector('a.stat-link')!;
|
|
27
|
+
|
|
28
|
+
expect(link).to.exist;
|
|
29
|
+
expect(link.getAttribute('href')).to.equal('/fails');
|
|
30
|
+
expect(link.getAttribute('data-target')).to.equal('primary');
|
|
31
|
+
expect(link.querySelector('.caption')!.textContent).to.equal('Fails');
|
|
32
|
+
expect(link.querySelector('zn-icon.stat-link__icon')).to.exist;
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('renders no link without an href', async () => {
|
|
36
|
+
const el = await fixture<HTMLElement>(html` <zn-stat amount="3"></zn-stat> `);
|
|
37
|
+
|
|
38
|
+
expect(el.shadowRoot!.querySelector('a')).to.not.exist;
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('compares amounts written with separators and symbols', async () => {
|
|
42
|
+
const el = await fixture<HTMLElement>(html`
|
|
43
|
+
<zn-stat layout="tile" amount="$2,941" previous="$2,875" show-delta></zn-stat>`);
|
|
44
|
+
await (el as unknown as {updateComplete: Promise<unknown>}).updateComplete;
|
|
45
|
+
const delta = el.shadowRoot!.querySelector('.delta')!;
|
|
46
|
+
|
|
47
|
+
expect(delta.classList.contains('delta--good')).to.be.true;
|
|
48
|
+
expect(delta.textContent!.trim()).to.equal('2.3%');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('shows the plain difference from a previous zero', async () => {
|
|
52
|
+
const el = await fixture<HTMLElement>(html`
|
|
53
|
+
<zn-stat layout="tile" amount="5" previous="0" show-delta invert-delta></zn-stat>`);
|
|
54
|
+
await (el as unknown as {updateComplete: Promise<unknown>}).updateComplete;
|
|
55
|
+
const delta = el.shadowRoot!.querySelector('.delta')!;
|
|
56
|
+
|
|
57
|
+
expect(delta.classList.contains('delta--bad')).to.be.true;
|
|
58
|
+
expect(delta.textContent!.trim()).to.equal('5');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('shows no change from a previous zero that stays zero', async () => {
|
|
62
|
+
const el = await fixture<HTMLElement>(html`
|
|
63
|
+
<zn-stat layout="tile" amount="0" previous="0" show-delta></zn-stat>`);
|
|
64
|
+
await (el as unknown as {updateComplete: Promise<unknown>}).updateComplete;
|
|
65
|
+
const delta = el.shadowRoot!.querySelector('.delta')!;
|
|
66
|
+
|
|
67
|
+
expect(delta.classList.contains('delta--flat')).to.be.true;
|
|
68
|
+
expect(delta.textContent!.trim()).to.equal('No change');
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('colours a rise as bad news with invert-delta', async () => {
|
|
72
|
+
const el = await fixture<HTMLElement>(html`
|
|
73
|
+
<zn-stat layout="tile" amount="148" previous="140" show-delta invert-delta></zn-stat>`);
|
|
74
|
+
await (el as unknown as {updateComplete: Promise<unknown>}).updateComplete;
|
|
75
|
+
|
|
76
|
+
expect(el.shadowRoot!.querySelector('.delta')!.classList.contains('delta--bad')).to.be.true;
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('draws a sparkline from the spark values', async () => {
|
|
80
|
+
const el = await fixture<HTMLElement>(html`<zn-stat layout="tile" amount="1" spark="1,3,2"></zn-stat>`);
|
|
81
|
+
await (el as unknown as {updateComplete: Promise<unknown>}).updateComplete;
|
|
82
|
+
|
|
83
|
+
expect(el.shadowRoot!.querySelectorAll('.spark path')).to.have.length(2);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('toggles a selectable tile', async () => {
|
|
87
|
+
const el = await fixture<HTMLElement & {selected: boolean; updateComplete: Promise<unknown>}>(html`
|
|
88
|
+
<zn-stat layout="tile" selectable caption="Declined" amount="148"></zn-stat>`);
|
|
89
|
+
await el.updateComplete;
|
|
90
|
+
const button = el.shadowRoot!.querySelector('button')!;
|
|
91
|
+
|
|
92
|
+
expect(button.getAttribute('aria-pressed')).to.equal('false');
|
|
93
|
+
button.click();
|
|
94
|
+
await el.updateComplete;
|
|
95
|
+
expect(el.selected).to.be.true;
|
|
96
|
+
expect(button.getAttribute('aria-pressed')).to.equal('true');
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
describe('<zn-stat> data-url', () => {
|
|
101
|
+
const original = window.fetch;
|
|
102
|
+
afterEach(() => { window.fetch = original; });
|
|
103
|
+
|
|
104
|
+
it('keeps its own attributes until the data loads, then takes the response', async () => {
|
|
105
|
+
let resolve!: (r: Response) => void;
|
|
106
|
+
window.fetch = (() => new Promise<Response>(r => { resolve = r; })) as typeof fetch;
|
|
107
|
+
const el = await fixture<ZnStatsTile>(html`
|
|
108
|
+
<zn-stat layout="tile" caption="Sent" amount="—" data-url="/metrics/stats"></zn-stat>`);
|
|
109
|
+
|
|
110
|
+
expect(el.amount).to.equal('—');
|
|
111
|
+
resolve(new Response(JSON.stringify({amount: '1,204', description: '80 in 24 hours', spark: '1,2,3'})));
|
|
112
|
+
await new Promise(r => setTimeout(r, 50));
|
|
113
|
+
await el.updateComplete;
|
|
114
|
+
|
|
115
|
+
expect(el.caption).to.equal('Sent');
|
|
116
|
+
expect(el.amount).to.equal('1,204');
|
|
117
|
+
expect(el.description).to.equal('80 in 24 hours');
|
|
118
|
+
expect(el.spark).to.equal('1,2,3');
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('shows a placeholder while loading and the sparkline once loaded', async () => {
|
|
122
|
+
let resolve!: (r: Response) => void;
|
|
123
|
+
window.fetch = (() => new Promise<Response>(r => { resolve = r; })) as typeof fetch;
|
|
124
|
+
const el = await fixture<ZnStatsTile>(html`
|
|
125
|
+
<zn-stat layout="tile" caption="Sent" data-url="/metrics/stats"></zn-stat>`);
|
|
126
|
+
|
|
127
|
+
expect(el.loading).to.equal(true);
|
|
128
|
+
expect(el.shadowRoot!.querySelector('.amount__skeleton')).to.exist;
|
|
129
|
+
expect(el.shadowRoot!.querySelector('.loading-text')).to.exist;
|
|
130
|
+
expect(el.shadowRoot!.querySelector('.spark__track')).to.exist;
|
|
131
|
+
|
|
132
|
+
resolve(new Response(JSON.stringify({amount: '12', spark: '1,2,3'})));
|
|
133
|
+
await new Promise(r => setTimeout(r, 50));
|
|
134
|
+
await el.updateComplete;
|
|
135
|
+
|
|
136
|
+
expect(el.loading).to.equal(false);
|
|
137
|
+
expect(el.shadowRoot!.querySelector('.amount__skeleton')).to.not.exist;
|
|
138
|
+
expect(el.shadowRoot!.querySelector('.spark svg')).to.exist;
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it('stops loading when the request fails', async () => {
|
|
142
|
+
window.fetch = (() => Promise.resolve(new Response('', {status: 500}))) as typeof fetch;
|
|
143
|
+
const el = await fixture<ZnStatsTile>(html`
|
|
144
|
+
<zn-stat layout="tile" caption="Sent" amount="0" data-url="/metrics/stats"></zn-stat>`);
|
|
145
|
+
await new Promise(r => setTimeout(r, 50));
|
|
146
|
+
await el.updateComplete;
|
|
147
|
+
|
|
148
|
+
expect(el.loading).to.equal(false);
|
|
149
|
+
expect(el.amount).to.equal('0');
|
|
9
150
|
});
|
|
10
151
|
});
|
|
@@ -1,65 +1,66 @@
|
|
|
1
1
|
@use "../../wc";
|
|
2
2
|
|
|
3
3
|
:host {
|
|
4
|
-
--zn-indicator
|
|
5
|
-
--zn-indicator-
|
|
6
|
-
--zn-indicator-
|
|
7
|
-
--zn-indicator-
|
|
8
|
-
--zn-indicator-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
--indicator-
|
|
4
|
+
// Read through fallbacks so a theme can set --zn-indicator-* on the root.
|
|
5
|
+
--indicator-info: var(--zn-indicator-info, 100, 117, 242);
|
|
6
|
+
--indicator-warning: var(--zn-indicator-warning, 255, 136, 0);
|
|
7
|
+
--indicator-error: var(--zn-indicator-error, 237, 84, 109);
|
|
8
|
+
--indicator-success: var(--zn-indicator-success, 43, 192, 145);
|
|
9
|
+
--indicator-disabled: var(--zn-indicator-disabled, 220, 220, 220);
|
|
10
|
+
|
|
11
|
+
--indicator-color: var(--indicator-disabled);
|
|
12
|
+
--indicator-alt-color: var(--indicator-disabled);
|
|
12
13
|
white-space: nowrap;
|
|
13
14
|
display: inline-block;
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
.indicator {
|
|
17
18
|
display: inline-block;
|
|
18
|
-
background-color: rgba(var(--indicator-color), 0.7);
|
|
19
|
+
background-color: rgba(var(--indicator-color), var(--zn-status-indicator-fill-alpha, 0.7));
|
|
19
20
|
border: 1px solid rgb(var(--indicator-color));
|
|
20
|
-
width: 10px;
|
|
21
|
-
height: 10px;
|
|
21
|
+
width: var(--zn-status-indicator-size, 10px);
|
|
22
|
+
height: var(--zn-status-indicator-size, 10px);
|
|
22
23
|
border-radius: 50%;
|
|
23
24
|
|
|
24
25
|
&--info {
|
|
25
|
-
--indicator-color: var(--
|
|
26
|
+
--indicator-color: var(--indicator-info);
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
&--error {
|
|
29
|
-
--indicator-color: var(--
|
|
30
|
+
--indicator-color: var(--indicator-error);
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
&--warning {
|
|
33
|
-
--indicator-color: var(--
|
|
34
|
+
--indicator-color: var(--indicator-warning);
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
&--success {
|
|
37
|
-
--indicator-color: var(--
|
|
38
|
+
--indicator-color: var(--indicator-success);
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
&--disabled {
|
|
41
|
-
--indicator-color: var(--
|
|
42
|
+
--indicator-color: var(--indicator-disabled);
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
// Alternate (secondary) colour, used by the alternating animation.
|
|
45
46
|
&--alt-info {
|
|
46
|
-
--indicator-alt-color: var(--
|
|
47
|
+
--indicator-alt-color: var(--indicator-info);
|
|
47
48
|
}
|
|
48
49
|
|
|
49
50
|
&--alt-error {
|
|
50
|
-
--indicator-alt-color: var(--
|
|
51
|
+
--indicator-alt-color: var(--indicator-error);
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
&--alt-warning {
|
|
54
|
-
--indicator-alt-color: var(--
|
|
55
|
+
--indicator-alt-color: var(--indicator-warning);
|
|
55
56
|
}
|
|
56
57
|
|
|
57
58
|
&--alt-success {
|
|
58
|
-
--indicator-alt-color: var(--
|
|
59
|
+
--indicator-alt-color: var(--indicator-success);
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
&--alt-disabled {
|
|
62
|
-
--indicator-alt-color: var(--
|
|
63
|
+
--indicator-alt-color: var(--indicator-disabled);
|
|
63
64
|
}
|
|
64
65
|
|
|
65
66
|
&--glow {
|
|
@@ -51,11 +51,11 @@
|
|
|
51
51
|
width: 100%;
|
|
52
52
|
left: 2px;
|
|
53
53
|
height: 2px;
|
|
54
|
-
background-color: rgb(var(--zn-border-color));
|
|
54
|
+
background-color: var(--zn-stepper-track-color, rgb(var(--zn-border-color)));
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
.step-progress {
|
|
58
|
-
background-color: rgb(var(--zn-primary));
|
|
58
|
+
background-color: var(--zn-stepper-progress-color, rgb(var(--zn-primary)));
|
|
59
59
|
height: 8px;
|
|
60
60
|
position: absolute;
|
|
61
61
|
top: 2px;
|
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
width: 17px;
|
|
78
78
|
height: 17px;
|
|
79
79
|
border-radius: 50%;
|
|
80
|
-
background-color: rgb(var(--zn-border-color));
|
|
80
|
+
background-color: var(--zn-stepper-track-color, rgb(var(--zn-border-color)));
|
|
81
81
|
display: flex;
|
|
82
82
|
align-items: center;
|
|
83
83
|
justify-content: center;
|
|
@@ -16,6 +16,7 @@ export default class ZnTab extends ZincElement {
|
|
|
16
16
|
static styles: CSSResultGroup = unsafeCSS(styles);
|
|
17
17
|
|
|
18
18
|
@property() caption: string;
|
|
19
|
+
@property() icon: string;
|
|
19
20
|
@property({type: Number}) priority: number;
|
|
20
21
|
@property() uri: string;
|
|
21
22
|
/** When present, zn-page opens this tab on load instead of the first tab. */
|
|
@@ -231,7 +231,8 @@ export default class ZnTable extends ZincElement {
|
|
|
231
231
|
'table-row--info': color === 'info',
|
|
232
232
|
'table-row--warning': color === 'warning',
|
|
233
233
|
'table-row--success': color === 'success',
|
|
234
|
-
|
|
234
|
+
'table-row--link': typeof uri === 'string' && uri !== '',
|
|
235
|
+
})}" @click="${this.handleRowClick}">${rowHtml}
|
|
235
236
|
</tr>`);
|
|
236
237
|
});
|
|
237
238
|
|
|
@@ -239,6 +240,16 @@ export default class ZnTable extends ZincElement {
|
|
|
239
240
|
<tbody>${rows}</tbody>`;
|
|
240
241
|
}
|
|
241
242
|
|
|
243
|
+
// A row with a uri opens it from anywhere in the row, through its caption link so the link's target applies.
|
|
244
|
+
// Clicks on the row's own controls, and drags that select text, are left to them.
|
|
245
|
+
private handleRowClick = (e: MouseEvent) => {
|
|
246
|
+
const row = e.currentTarget as HTMLElement;
|
|
247
|
+
if (!row.classList.contains('table-row--link') || window.getSelection()?.toString()) return;
|
|
248
|
+
const control = e.composedPath().find(el => el instanceof HTMLElement && el.matches('a, button, input, select, textarea, zn-dropdown, zn-button'));
|
|
249
|
+
if (control) return;
|
|
250
|
+
row.querySelector<HTMLAnchorElement>('td.capcol a')?.click();
|
|
251
|
+
};
|
|
252
|
+
|
|
242
253
|
columnContent(col: any) {
|
|
243
254
|
if (typeof col !== 'object' || col === null) {
|
|
244
255
|
if (this.boolIcons && (col.toString() === 'true' || col.toString() === 'false')) {
|
|
@@ -40,7 +40,7 @@ table, thead, tbody, tr, td, th {
|
|
|
40
40
|
|
|
41
41
|
tr td {
|
|
42
42
|
min-height: 40px;
|
|
43
|
-
height: 52px;
|
|
43
|
+
height: var(--zn-table-row-height, 52px);
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
tr.with-summary td {
|
|
@@ -49,27 +49,27 @@ tr.with-summary td {
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
.table-row.table-row--error {
|
|
52
|
-
color:
|
|
53
|
-
border-color:
|
|
54
|
-
background:
|
|
52
|
+
color: var(--zn-color-danger-fg);
|
|
53
|
+
border-color: var(--zn-color-danger-solid);
|
|
54
|
+
background: var(--zn-color-danger-bg);
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
.table-row.table-row--info {
|
|
58
|
-
color:
|
|
59
|
-
border-color:
|
|
60
|
-
background:
|
|
58
|
+
color: var(--zn-color-info-fg);
|
|
59
|
+
border-color: var(--zn-color-info-solid);
|
|
60
|
+
background: var(--zn-color-info-bg);
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
.table-row.table-row--warning {
|
|
64
|
-
color:
|
|
65
|
-
border-color:
|
|
66
|
-
background:
|
|
64
|
+
color: var(--zn-color-warning-fg);
|
|
65
|
+
border-color: var(--zn-color-warning-solid);
|
|
66
|
+
background: var(--zn-color-warning-bg);
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
.table-row.table-row--success {
|
|
70
|
-
color:
|
|
71
|
-
border-color:
|
|
72
|
-
background:
|
|
70
|
+
color: var(--zn-color-success-fg) !important;
|
|
71
|
+
border-color: var(--zn-color-success-solid);
|
|
72
|
+
background: var(--zn-color-success-bg);
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
.table-row.table-row--nested td:first-of-type {
|
|
@@ -204,3 +204,11 @@ zn-table[fixed] {
|
|
|
204
204
|
table-layout: fixed;
|
|
205
205
|
}
|
|
206
206
|
}
|
|
207
|
+
|
|
208
|
+
.table-row--link {
|
|
209
|
+
cursor: pointer;
|
|
210
|
+
|
|
211
|
+
&:hover td {
|
|
212
|
+
background-color: var(--zn-table-row-hover-background, rgba(var(--zn-primary), 0.05));
|
|
213
|
+
}
|
|
214
|
+
}
|
|
@@ -7,4 +7,21 @@ describe('<zn-table>', () => {
|
|
|
7
7
|
|
|
8
8
|
expect(el).to.exist;
|
|
9
9
|
});
|
|
10
|
+
|
|
11
|
+
it('opens a row with a uri from anywhere in the row through its caption link', async () => {
|
|
12
|
+
const data = {header: ['Type', 'Connector'], items: [{uri: '/tx/1', target: 'slide', caption: 'AUTH', data: ['sandbanx']}]};
|
|
13
|
+
const el = await fixture<HTMLElement>(html`<zn-table data="${JSON.stringify(data)}"></zn-table>`);
|
|
14
|
+
await (el as unknown as {updateComplete: Promise<unknown>}).updateComplete;
|
|
15
|
+
|
|
16
|
+
const link = el.shadowRoot!.querySelector<HTMLAnchorElement>('td.capcol a')!;
|
|
17
|
+
let clicks = 0;
|
|
18
|
+
link.addEventListener('click', e => { e.preventDefault(); clicks++; });
|
|
19
|
+
|
|
20
|
+
const row = el.shadowRoot!.querySelector<HTMLElement>('tr.table-row--link')!;
|
|
21
|
+
row.querySelectorAll('td')[1].click();
|
|
22
|
+
expect(clicks).to.equal(1);
|
|
23
|
+
|
|
24
|
+
link.click();
|
|
25
|
+
expect(clicks).to.equal(2);
|
|
26
|
+
});
|
|
10
27
|
});
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
replaceHistoryTab,
|
|
10
10
|
TAB_STORE_PREFIX
|
|
11
11
|
} from './tabs-navigation';
|
|
12
|
-
import {getScrolledAncestors,
|
|
12
|
+
import {getScrolledAncestors, revealTop} from "../../internal/scroll";
|
|
13
13
|
import {HasSlotController} from "../../internal/slot";
|
|
14
14
|
import {ifDefined} from "lit/directives/if-defined.js";
|
|
15
15
|
import {md5} from "../../utilities/md5";
|
|
@@ -17,15 +17,17 @@ import {MutationController} from '@lit-labs/observers/mutation-controller.js';
|
|
|
17
17
|
import {property} from 'lit/decorators.js';
|
|
18
18
|
import {Store} from "../../internal/storage";
|
|
19
19
|
import ZincElement from '../../internal/zinc-element';
|
|
20
|
+
import {onHoverIntent} from '../../internal/hover-intent';
|
|
21
|
+
import {pointerInitiated} from '../../internal/input-modality';
|
|
20
22
|
|
|
21
23
|
// Tabs load in such a way that the global sheet has to be re-applied inside the shadow root.
|
|
22
24
|
import bootStyles from '../../../scss/boot.scss';
|
|
23
25
|
import styles from './tabs.scss';
|
|
24
26
|
|
|
25
|
-
// Every element that manages its own tabs (ZnTabs and its subclasses)
|
|
26
|
-
// registration must stop at these so an outer
|
|
27
|
-
// container's tabs.
|
|
28
|
-
export const tabContainerSelector = 'zn-tabs, zn-page, zn-page-nav';
|
|
27
|
+
// Every element that manages its own tabs (ZnTabs and its subclasses), plus a navbar
|
|
28
|
+
// linked to a container elsewhere. Tab registration must stop at these so an outer
|
|
29
|
+
// container never claims a nested or linked container's tabs.
|
|
30
|
+
export const tabContainerSelector = 'zn-tabs, zn-page, zn-page-nav, zn-navbar[tabs]';
|
|
29
31
|
|
|
30
32
|
/**
|
|
31
33
|
* @summary Short summary of the component's intended use.
|
|
@@ -49,6 +51,7 @@ export default class ZnTabs extends ZincElement {
|
|
|
49
51
|
@property({attribute: 'master-id', reflect: true}) masterId: string;
|
|
50
52
|
@property({attribute: 'default-uri', reflect: true}) defaultUri = '';
|
|
51
53
|
@property({attribute: 'active', reflect: true}) _current = '';
|
|
54
|
+
private _hasSelected = false;
|
|
52
55
|
@property({attribute: 'split', type: Number, reflect: true}) _split: number;
|
|
53
56
|
@property({attribute: 'split-min', type: Number, reflect: true}) _splitMin = 60;
|
|
54
57
|
@property({attribute: 'split-min-secondary', type: Number, reflect: true}) _splitMinSecondary: number;
|
|
@@ -62,6 +65,14 @@ export default class ZnTabs extends ZincElement {
|
|
|
62
65
|
@property({attribute: 'store-key'}) storeKey: string;
|
|
63
66
|
@property({attribute: 'store-ttl', type: Number, reflect: true}) storeTtl = 0;
|
|
64
67
|
@property({attribute: 'padded', type: Boolean, reflect: true}) padded = false;
|
|
68
|
+
/**
|
|
69
|
+
* With `store-key`, opens a tab that has appeared since the page was last shown, such as one for an item just
|
|
70
|
+
* added, in place of the remembered tab.
|
|
71
|
+
*/
|
|
72
|
+
@property({attribute: 'select-added', type: Boolean, reflect: true}) selectAdded = false;
|
|
73
|
+
|
|
74
|
+
/** Spaces a left or right slot from the content, for side navigation laid out as its own card. */
|
|
75
|
+
@property({type: Boolean, reflect: true}) gap = false;
|
|
65
76
|
@property({attribute: 'fetch-style', type: String, reflect: true}) fetchStyle = "";
|
|
66
77
|
@property({attribute: 'full-width', type: Boolean, reflect: true}) fullWidth = false;
|
|
67
78
|
@property({attribute: 'padded-right', type: Boolean, reflect: true}) paddedRight = false;
|
|
@@ -232,9 +243,43 @@ export default class ZnTabs extends ZincElement {
|
|
|
232
243
|
}
|
|
233
244
|
|
|
234
245
|
this._prepareTab(stored);
|
|
246
|
+
// The stored tab can be gone, say after deleting what it showed; fall back to the tab the container starts on.
|
|
247
|
+
// A tab still on screen keeps its selection even before its panel loads.
|
|
248
|
+
const gone = !this._panels.has(stored) && !this._tabs.some(tab => tab.getAttribute('tab') === stored);
|
|
249
|
+
if (gone && this._defaultTab !== '' && this._panels.has(this._defaultTab)) {
|
|
250
|
+
this.setActiveTab(this._defaultTab, true, false);
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
235
253
|
this.setActiveTab(stored, true, false);
|
|
236
254
|
}
|
|
237
255
|
|
|
256
|
+
/**
|
|
257
|
+
* The tabs present now that were not when the page was last shown, oldest first, remembering the current set for
|
|
258
|
+
* next time. Empty without `select-added`, or on the first showing.
|
|
259
|
+
*/
|
|
260
|
+
private takeAddedTabs(): string[] {
|
|
261
|
+
const key = this.getTabStoreKey();
|
|
262
|
+
if (!this.selectAdded || key === null || !this._store) {
|
|
263
|
+
return [];
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const storeKey = locationScopedKey(`${key}:tabs`);
|
|
267
|
+
const current = this._tabs.map(tab => tab.getAttribute('tab')).filter((id): id is string => id !== null);
|
|
268
|
+
const known = this._store.get(storeKey);
|
|
269
|
+
this._store.set(storeKey, JSON.stringify(current));
|
|
270
|
+
if (known === null) {
|
|
271
|
+
return [];
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
let knownIds: Set<string>;
|
|
275
|
+
try {
|
|
276
|
+
knownIds = new Set(JSON.parse(known) as string[]);
|
|
277
|
+
} catch {
|
|
278
|
+
return [];
|
|
279
|
+
}
|
|
280
|
+
return current.filter(id => !knownIds.has(id));
|
|
281
|
+
}
|
|
282
|
+
|
|
238
283
|
/** Activates the tab the container starts on, ignoring any stored selection. */
|
|
239
284
|
protected activateDefaultTab() {
|
|
240
285
|
if (!this._panels.has(this._defaultTab) && this._tabs.length > 0) {
|
|
@@ -300,11 +345,43 @@ export default class ZnTabs extends ZincElement {
|
|
|
300
345
|
}
|
|
301
346
|
this._tabs.push(tab);
|
|
302
347
|
if (this.preload) {
|
|
303
|
-
tab
|
|
348
|
+
onHoverIntent(tab, () => this.fetchUriTab(tab));
|
|
304
349
|
}
|
|
305
350
|
tab.addEventListener('click', this._handleClick.bind(this));
|
|
351
|
+
|
|
352
|
+
// Plain elements (usually <li>) are not focusable; custom elements and tabs wrapping a control manage their own
|
|
353
|
+
const focusable = 'a[href], button, input, select, textarea, [tabindex]';
|
|
354
|
+
if (!tab.localName.includes('-') && !tab.matches(focusable) && !tab.querySelector(focusable)) {
|
|
355
|
+
tab.tabIndex = 0;
|
|
356
|
+
tab.addEventListener('keydown', this._handleTabKeyDown);
|
|
357
|
+
}
|
|
306
358
|
}
|
|
307
359
|
|
|
360
|
+
private readonly _handleTabKeyDown = (event: KeyboardEvent) => {
|
|
361
|
+
const tab = event.currentTarget as HTMLElement;
|
|
362
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
363
|
+
event.preventDefault();
|
|
364
|
+
tab.click();
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
const siblings = this._tabs.filter(t => t.parentElement === tab.parentElement && t.tabIndex >= 0);
|
|
369
|
+
const index = siblings.indexOf(tab);
|
|
370
|
+
const target = {
|
|
371
|
+
ArrowRight: index + 1,
|
|
372
|
+
ArrowDown: index + 1,
|
|
373
|
+
ArrowLeft: index - 1,
|
|
374
|
+
ArrowUp: index - 1,
|
|
375
|
+
Home: 0,
|
|
376
|
+
End: siblings.length - 1
|
|
377
|
+
}[event.key];
|
|
378
|
+
if (target === undefined) {
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
event.preventDefault();
|
|
382
|
+
siblings[(target + siblings.length) % siblings.length].focus();
|
|
383
|
+
};
|
|
384
|
+
|
|
308
385
|
reRegisterTabs = () => {
|
|
309
386
|
this._registerTabs();
|
|
310
387
|
}
|
|
@@ -314,6 +391,16 @@ export default class ZnTabs extends ZincElement {
|
|
|
314
391
|
setTimeout(() => {
|
|
315
392
|
this._registerTabs();
|
|
316
393
|
this._defaultTab = this._current || '';
|
|
394
|
+
// A linked navbar renders its items after this container can be ready
|
|
395
|
+
this._linkedNavbars().forEach(navbar => {
|
|
396
|
+
void (navbar as ZincElement).updateComplete?.then(this.reRegisterTabs);
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
const added = this.takeAddedTabs();
|
|
400
|
+
if (added.length > 0) {
|
|
401
|
+
this.setActiveTab(added[added.length - 1], true, false);
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
317
404
|
|
|
318
405
|
const storedValue = this.getStoredTab();
|
|
319
406
|
if (storedValue !== null) {
|
|
@@ -361,7 +448,8 @@ export default class ZnTabs extends ZincElement {
|
|
|
361
448
|
}
|
|
362
449
|
}
|
|
363
450
|
|
|
364
|
-
|
|
451
|
+
const uriTabs = [this, ...this._linkedNavbars()].flatMap(root => deepQuerySelectorAll("[tab-uri]", root, tabContainerSelector));
|
|
452
|
+
for (const uriTab of uriTabs) {
|
|
365
453
|
const uri: string = uriTab.getAttribute("tab-uri")!;
|
|
366
454
|
const eleTabId = this._uriToId(uri);
|
|
367
455
|
if (eleTabId === tabId) {
|
|
@@ -412,21 +500,14 @@ export default class ZnTabs extends ZincElement {
|
|
|
412
500
|
|
|
413
501
|
_handleClick(event: PointerEvent) {
|
|
414
502
|
// ts-ignore
|
|
415
|
-
|
|
503
|
+
// The listener is on the tab itself; event.target could be an icon or tooltip inside it
|
|
504
|
+
const target = (event.relatedTarget ?? event.currentTarget) as HTMLElement;
|
|
416
505
|
if (!target) {
|
|
417
506
|
return;
|
|
418
507
|
}
|
|
419
508
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
return;
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
const transition = document.startViewTransition(() => this.clickTab(target, event.altKey, true));
|
|
426
|
-
|
|
427
|
-
void transition.ready.catch(() => undefined);
|
|
428
|
-
void transition.finished.catch(() => undefined);
|
|
429
|
-
void transition.updateCallbackDone.catch(() => undefined);
|
|
509
|
+
// No view transition: a root transition snapshots the whole page, which costs Safari hundreds of ms per switch.
|
|
510
|
+
this.clickTab(target, event.altKey, true);
|
|
430
511
|
}
|
|
431
512
|
|
|
432
513
|
fetchUriTab(target: HTMLElement) {
|
|
@@ -555,15 +636,36 @@ export default class ZnTabs extends ZincElement {
|
|
|
555
636
|
this._current = tabName;
|
|
556
637
|
};
|
|
557
638
|
|
|
558
|
-
|
|
639
|
+
// Reading scroll positions forces a layout; the first selection has no reader's place to reset.
|
|
640
|
+
const firstSelection = !this._hasSelected;
|
|
641
|
+
const scrolled = sameTab || firstSelection ? [] : getScrolledAncestors(this._panel);
|
|
642
|
+
this._hasSelected = true;
|
|
559
643
|
|
|
560
644
|
updateTabs();
|
|
561
645
|
|
|
562
|
-
|
|
646
|
+
// Only a pointer press on these tabs animates: keyboard, history and first-render selections stay instant, as
|
|
647
|
+
// does a nested tab group picking its default while an outer tab is opened.
|
|
648
|
+
if (!sameTab && !firstSelection && pointerInitiated(this)) {
|
|
649
|
+
this._panels.get(tabName)?.forEach(panel => this._enterPanel(panel));
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
revealTop(scrolled, this);
|
|
563
653
|
|
|
564
654
|
return true;
|
|
565
655
|
}
|
|
566
656
|
|
|
657
|
+
// Started here rather than as a CSS animation, which would replay in every nested tab panel whenever an ancestor
|
|
658
|
+
// panel is shown again. Opacity and translate only, so the compositor runs it without restyling the page.
|
|
659
|
+
private _enterPanel(panel: Element) {
|
|
660
|
+
const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
661
|
+
// keep in sync with --zn-ease-out
|
|
662
|
+
// Offset upward: overflow past the top edge isn't scrollable, so a panel that just fits never flashes a scrollbar
|
|
663
|
+
panel.animate(reduceMotion ? [{opacity: 0}, {}] : [{opacity: 0, translate: '0 -4px'}, {}], {
|
|
664
|
+
duration: reduceMotion ? 150 : 160,
|
|
665
|
+
easing: 'cubic-bezier(0.23, 1, 0.32, 1)',
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
|
|
567
669
|
getActiveTab(): Element[] {
|
|
568
670
|
return this._panels.get(this._current) || [];
|
|
569
671
|
}
|
|
@@ -608,26 +710,37 @@ export default class ZnTabs extends ZincElement {
|
|
|
608
710
|
}
|
|
609
711
|
}
|
|
610
712
|
|
|
713
|
+
/** Navbars elsewhere in this container's root that drive it through `tabs="<this id>"`. */
|
|
714
|
+
private _linkedNavbars(): Element[] {
|
|
715
|
+
if (!this.id) {
|
|
716
|
+
return [];
|
|
717
|
+
}
|
|
718
|
+
const root = this.getRootNode() as Document | ShadowRoot;
|
|
719
|
+
return Array.from(root.querySelectorAll(`zn-navbar[tabs="${CSS.escape(this.id)}"]`));
|
|
720
|
+
}
|
|
721
|
+
|
|
611
722
|
_registerTabs = () => {
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
723
|
+
for (const root of [this, ...this._linkedNavbars()]) {
|
|
724
|
+
deepQuerySelectorAll('[tab]', root, tabContainerSelector).forEach(ele => {
|
|
725
|
+
this._addTab(ele as HTMLElement);
|
|
726
|
+
});
|
|
615
727
|
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
728
|
+
deepQuerySelectorAll('[tab-uri]', root, tabContainerSelector).forEach(ele => {
|
|
729
|
+
if (ele.getAttribute('tab-uri') === "") {
|
|
730
|
+
ele.setAttribute('tab', "");
|
|
731
|
+
ele.removeAttribute('tab-uri');
|
|
732
|
+
}
|
|
733
|
+
this._addTab(ele as HTMLElement);
|
|
734
|
+
});
|
|
623
735
|
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
736
|
+
deepQuerySelectorAll('[ref-tab-uri]', root, tabContainerSelector).forEach(ele => {
|
|
737
|
+
if (!ele.hasAttribute('ref-tab')) {
|
|
738
|
+
ele.setAttribute('ref-tab', this._uriToId(ele.getAttribute('ref-tab-uri')!));
|
|
739
|
+
ele.removeAttribute('ref-tab-uri');
|
|
740
|
+
this._actions.push(ele as HTMLElement);
|
|
741
|
+
}
|
|
742
|
+
});
|
|
743
|
+
}
|
|
631
744
|
}
|
|
632
745
|
|
|
633
746
|
render() {
|