@kubex/zinc 1.1.185 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.browserslistrc +2 -0
- package/.github/workflows/js_build_and_deploy.yaml +54 -1
- package/.github/workflows/test.yaml +27 -0
- package/dist/chunks/zn.UWJLIXDT.js +1 -0
- package/dist/custom-elements.json +9998 -2345
- package/dist/vscode.html-custom-data.json +1022 -67
- package/dist/web-types.json +2129 -177
- package/dist/zn.d.ts +2293 -416
- package/dist/zn.min.css +1 -1
- package/dist/zn.min.js +2310 -1637
- package/docs/_includes/default.njk +4 -1
- package/docs/assets/scripts/docs.js +22 -6
- package/docs/assets/styles/docs.css +9 -2
- package/docs/data/report-table-day-compare.json +380 -0
- package/docs/data/report-table.json +314 -0
- package/docs/pages/components/avatar-group.md +57 -0
- package/docs/pages/components/badge.md +67 -0
- package/docs/pages/components/button.md +41 -1
- package/docs/pages/components/chat-message.md +16 -0
- package/docs/pages/components/checkbox.md +1 -1
- package/docs/pages/components/chip.md +25 -13
- package/docs/pages/components/color-picker.md +37 -0
- package/docs/pages/components/copy-button.md +2 -2
- package/docs/pages/components/data-table-filter.md +33 -50
- package/docs/pages/components/data-table-search.md +4 -4
- package/docs/pages/components/data-table.md +27 -3
- package/docs/pages/components/date-range.md +116 -0
- package/docs/pages/components/dialog.md +29 -37
- package/docs/pages/components/diff-row.md +28 -0
- package/docs/pages/components/diff.md +46 -0
- package/docs/pages/components/editor.md +55 -0
- package/docs/pages/components/icon.md +46 -1
- package/docs/pages/components/input.md +30 -11
- package/docs/pages/components/item.md +35 -0
- package/docs/pages/components/key-value.md +25 -0
- package/docs/pages/components/menu.md +74 -7
- package/docs/pages/components/nav-item.md +15 -0
- package/docs/pages/components/nav-list.md +132 -0
- package/docs/pages/components/navbar.md +100 -1
- package/docs/pages/components/page.md +242 -74
- package/docs/pages/components/priority-list.md +43 -5
- package/docs/pages/components/query-builder.md +23 -0
- package/docs/pages/components/radio-group.md +33 -1
- package/docs/pages/components/radio.md +1 -1
- package/docs/pages/components/report-table.md +202 -0
- package/docs/pages/components/stat.md +30 -0
- package/docs/pages/components/tile.md +11 -0
- package/docs/pages/components/tooltip.md +1 -1
- package/docs/pages/components/translation-grid.md +86 -0
- package/docs/pages/components/vertical-stepper.md +108 -64
- package/package.json +2 -1
- package/scss/_global-spacing.scss +2 -2
- package/scss/_root.scss +11 -171
- package/scss/boot.scss +14 -0
- package/scss/mixins/_helper-mixins.scss +24 -2
- package/scss/mixins/_motion-mixins.scss +126 -0
- package/scss/mixins/_spacing-mixins.scss +4 -3
- package/scss/mixins/_tab-indicator-mixins.scss +12 -0
- package/scss/mixins/_typography-mixins.scss +1 -1
- package/scss/mixins/index.scss +14 -0
- package/scss/shared/_base.scss +32 -1
- package/scss/shared/_button-mixin.scss +7 -5
- package/scss/shared/_table.scss +35 -4
- package/scss/shared/nav.scss +4 -0
- package/scss/shared/shimmer.scss +1 -1
- package/scss/table-controls.scss +206 -0
- package/scss/themes/_dark.scss +1 -3
- package/scss/themes/_index.scss +1 -1
- package/scss/themes/_light.scss +46 -6
- package/scss/themes/_modes.scss +11 -11
- package/scss/themes/_openbyte.scss +944 -0
- package/scss/themes/_typography.scss +4 -2
- package/scss/variables/index.scss +0 -9
- package/src/components/alert/alert.component.ts +14 -4
- package/src/components/alert/alert.scss +55 -69
- package/src/components/alert/alert.test.ts +8 -0
- package/src/components/animated-button/animated-button.component.ts +3 -2
- package/src/components/animated-button/animated-button.scss +6 -23
- package/src/components/animated-button/animated-button.test.ts +4 -3
- package/src/components/avatar-group/avatar-group.component.ts +84 -0
- package/src/components/avatar-group/avatar-group.scss +54 -0
- package/src/components/avatar-group/avatar-group.test.ts +59 -0
- package/src/components/avatar-group/index.ts +12 -0
- package/src/components/badge/badge.component.ts +49 -0
- package/src/components/badge/badge.scss +80 -0
- package/src/components/badge/badge.test.ts +34 -0
- package/src/components/badge/index.ts +12 -0
- package/src/components/bulk-actions/bulk-actions.component.ts +14 -2
- package/src/components/button/button.component.ts +228 -8
- package/src/components/button/button.scss +115 -32
- package/src/components/button/button.test.ts +122 -0
- package/src/components/button-menu/button-menu.component.ts +32 -11
- package/src/components/button-menu/button-menu.scss +5 -1
- package/src/components/channel-tile/channel-tile.component.ts +34 -27
- package/src/components/channel-tile/channel-tile.scss +76 -43
- package/src/components/chart/builders.test.ts +46 -0
- package/src/components/chart/builders.ts +27 -3
- package/src/components/chart/chart-live.test.ts +4 -6
- package/src/components/chart/chart.component.ts +125 -5
- package/src/components/chart/chart.scss +121 -0
- package/src/components/chart/chart.test.ts +81 -0
- package/src/components/chat-message/chat-message.component.ts +21 -1
- package/src/components/chat-message/chat-message.scss +13 -2
- package/src/components/chat-message/chat-message.test.ts +9 -0
- package/src/components/checkbox/checkbox.component.ts +61 -27
- package/src/components/checkbox/checkbox.scss +58 -25
- package/src/components/checkbox-group/checkbox-group.component.ts +7 -1
- package/src/components/chip/chip.component.ts +11 -1
- package/src/components/chip/chip.scss +91 -26
- package/src/components/chip/chip.test.ts +17 -0
- package/src/components/collapsible/collapsible.component.ts +17 -3
- package/src/components/collapsible/collapsible.scss +19 -10
- package/src/components/color-picker/color-picker.component.ts +277 -0
- package/src/components/color-picker/color-picker.scss +141 -0
- package/src/components/color-picker/color-picker.test.ts +57 -0
- package/src/components/color-picker/index.ts +12 -0
- package/src/components/cols/cols.component.ts +10 -0
- package/src/components/cols/cols.test.ts +17 -0
- package/src/components/confirm/confirm.component.ts +20 -6
- package/src/components/confirm/confirm.test.ts +52 -0
- package/src/components/content-block/content-block.component.ts +38 -9
- package/src/components/content-block/content-block.scss +11 -5
- package/src/components/content-block/content-block.test.ts +26 -0
- package/src/components/copy-button/copy-button.component.ts +2 -2
- package/src/components/copy-button/copy-button.scss +17 -4
- package/src/components/data-select/data-select.component.ts +3 -1
- package/src/components/data-table/data-table.component.ts +362 -140
- package/src/components/data-table/data-table.scss +133 -60
- package/src/components/data-table/data-table.test.ts +356 -4
- package/src/components/data-table-filter/data-table-filter.component.ts +426 -406
- package/src/components/data-table-filter/data-table-filter.scss +176 -148
- package/src/components/data-table-filter/data-table-filter.test.ts +271 -273
- package/src/components/data-table-search/data-table-search.component.ts +4 -2
- package/src/components/data-table-search/data-table-search.scss +8 -1
- package/src/components/data-table-search/data-table-search.test.ts +29 -0
- package/src/components/date-range/date-range-math.test.ts +78 -0
- package/src/components/date-range/date-range-math.ts +105 -0
- package/src/components/date-range/date-range.component.ts +375 -0
- package/src/components/date-range/date-range.scss +272 -0
- package/src/components/date-range/date-range.test.ts +247 -0
- package/src/components/date-range/index.ts +12 -0
- package/src/components/datepicker/datepicker.component.ts +44 -7
- package/src/components/datepicker/datepicker.scss +9 -0
- package/src/components/datepicker/datepicker.test.ts +2 -2
- package/src/components/dialog/dialog.component.ts +38 -19
- package/src/components/dialog/dialog.scss +65 -36
- package/src/components/dialog/dialog.test.ts +14 -0
- package/src/components/diff/diff.component.ts +55 -0
- package/src/components/diff/diff.scss +46 -0
- package/src/components/diff/diff.test.ts +15 -0
- package/src/components/diff/index.ts +12 -0
- package/src/components/diff-row/diff-row.component.ts +75 -0
- package/src/components/diff-row/diff-row.scss +70 -0
- package/src/components/diff-row/diff-row.test.ts +37 -0
- package/src/components/diff-row/index.ts +12 -0
- package/src/components/dropdown/dropdown.component.ts +12 -10
- package/src/components/dropdown/dropdown.scss +2 -0
- package/src/components/editor/editor.component.ts +159 -11
- package/src/components/editor/editor.scss +250 -1
- package/src/components/editor/editor.test.ts +31 -3
- package/src/components/editor/modules/ai/panel/ai-panel.scss +0 -1
- package/src/components/editor/modules/attachment/attachment.ts +53 -31
- package/src/components/editor/modules/context-menu/context-menu.ts +17 -0
- package/src/components/editor/modules/dialog/dialog.component.ts +23 -1
- package/src/components/editor/modules/dialog/dialog.test.ts +28 -0
- package/src/components/editor/modules/emoji/emoji.ts +4 -5
- package/src/components/editor/modules/merge-field/merge-field.ts +83 -0
- package/src/components/editor/modules/toolbar/toolbar.component.ts +121 -68
- package/src/components/editor/modules/toolbar/toolbar.scss +38 -0
- package/src/components/editor/modules/toolbar/toolbar.ts +25 -1
- package/src/components/empty-state/empty-state.component.ts +6 -1
- package/src/components/empty-state/empty-state.scss +21 -4
- package/src/components/expanding-action/expanding-action.component.ts +22 -34
- package/src/components/expanding-action/expanding-action.scss +3 -45
- package/src/components/expanding-action/expanding-action.test.ts +2 -1
- package/src/components/file/file.component.ts +30 -2
- package/src/components/file/file.scss +6 -0
- package/src/components/flow-builder/flow-builder.component.ts +36 -3
- package/src/components/flow-builder/flow-builder.scss +0 -6
- package/src/components/flow-builder/modules/flow-canvas/flow-canvas.component.ts +8 -0
- package/src/components/flow-builder/modules/flow-canvas/flow-canvas.scss +10 -5
- package/src/components/flow-builder/modules/flow-node/flow-node.component.ts +17 -0
- package/src/components/flow-builder/modules/flow-node/flow-node.scss +10 -0
- package/src/components/form-actions/form-actions.component.ts +48 -2
- package/src/components/form-actions/form-actions.scss +1 -1
- package/src/components/form-group/form-group.component.ts +10 -3
- package/src/components/form-group/form-group.scss +37 -4
- package/src/components/form-group/form-group.test.ts +10 -0
- package/src/components/header/header.component.ts +11 -4
- package/src/components/header/header.scss +47 -5
- package/src/components/hover-container/hover-container.component.ts +4 -5
- package/src/components/icon/icon.component.ts +95 -9
- package/src/components/icon/icon.scss +70 -6
- package/src/components/icon/icon.test.ts +55 -0
- package/src/components/icon-picker/icon-picker.component.ts +35 -1
- package/src/components/inline-edit/inline-edit.component.ts +38 -8
- package/src/components/inline-edit/inline-edit.scss +128 -19
- package/src/components/input/input.component.ts +173 -297
- package/src/components/input/input.scss +65 -43
- package/src/components/input/input.test.ts +86 -4
- package/src/components/item/item.component.ts +12 -0
- package/src/components/item/item.scss +102 -3
- package/src/components/item/item.test.ts +23 -0
- package/src/components/key/key.component.ts +2 -2
- package/src/components/key/key.scss +11 -0
- package/src/components/key-value/index.ts +12 -0
- package/src/components/key-value/key-value.component.ts +160 -0
- package/src/components/key-value/key-value.scss +20 -0
- package/src/components/key-value/key-value.test.ts +22 -0
- package/src/components/markdown-editor/markdown-editor.component.ts +8 -1
- package/src/components/menu/menu.component.ts +21 -2
- package/src/components/menu/menu.scss +43 -23
- package/src/components/menu/menu.test.ts +33 -0
- package/src/components/menu-item/menu-item.component.ts +13 -2
- package/src/components/menu-item/menu-item.scss +56 -23
- package/src/components/menu-item/menu-item.test.ts +31 -0
- package/src/components/menu-item/submenu-controller.ts +25 -15
- package/src/components/menu-label/index.ts +12 -0
- package/src/components/menu-label/menu-label.component.ts +30 -0
- package/src/components/menu-label/menu-label.scss +18 -0
- package/src/components/nav-item/index.ts +12 -0
- package/src/components/nav-item/nav-item.component.ts +83 -0
- package/src/components/nav-item/nav-item.scss +128 -0
- package/src/components/nav-list/index.ts +12 -0
- package/src/components/nav-list/nav-list.component.ts +313 -0
- package/src/components/nav-list/nav-list.scss +87 -0
- package/src/components/nav-list/nav-list.test.ts +250 -0
- package/src/components/navbar/navbar.component.ts +235 -171
- package/src/components/navbar/navbar.scss +333 -169
- package/src/components/navbar/navbar.test.ts +63 -143
- package/src/components/page/page.component.ts +114 -29
- package/src/components/page/page.scss +223 -95
- package/src/components/page/page.test.ts +214 -3
- package/src/components/page-builder/page-builder.component.ts +51 -5
- package/src/components/page-builder/page-builder.scss +6 -1
- package/src/components/page-nav/page-nav.component.ts +25 -3
- package/src/components/page-nav/page-nav.scss +10 -2
- package/src/components/pagination/pagination.scss +1 -1
- package/src/components/panel/panel.component.ts +14 -13
- package/src/components/panel/panel.scss +14 -10
- package/src/components/panel/panel.test.ts +42 -0
- package/src/components/popup/popup.component.ts +20 -13
- package/src/components/popup/popup.test.ts +21 -1
- package/src/components/priority-list/priority-list.component.ts +157 -172
- package/src/components/priority-list/priority-list.scss +113 -17
- package/src/components/priority-list/priority-list.test.ts +301 -3
- package/src/components/progress-bar/progress-bar.component.ts +1 -1
- package/src/components/progress-bar/progress-bar.scss +16 -4
- package/src/components/progress-tile/progress-tile.scss +1 -1
- package/src/components/query-builder/query-builder.component.ts +85 -10
- package/src/components/query-builder/query-builder.scss +7 -1
- package/src/components/query-builder/query-builder.test.ts +40 -0
- package/src/components/radio/radio.component.ts +57 -18
- package/src/components/radio/radio.scss +101 -26
- package/src/components/radio-group/radio-group.component.ts +45 -7
- package/src/components/radio-group/radio-group.scss +17 -0
- package/src/components/radio-group/radio-group.test.ts +63 -2
- package/src/components/rating/rating.component.ts +23 -0
- package/src/components/rating/rating.scss +1 -1
- package/src/components/remarkd-editor/remarkd-editor.component.ts +60 -5
- package/src/components/remarkd-editor/remarkd-editor.scss +12 -4
- package/src/components/report-table/index.ts +12 -0
- package/src/components/report-table/report-format.test.ts +86 -0
- package/src/components/report-table/report-format.ts +72 -0
- package/src/components/report-table/report-math.test.ts +74 -0
- package/src/components/report-table/report-math.ts +252 -0
- package/src/components/report-table/report-table.component.ts +681 -0
- package/src/components/report-table/report-table.scss +328 -0
- package/src/components/report-table/report-table.test.ts +702 -0
- package/src/components/reveal/reveal.component.ts +15 -1
- package/src/components/reveal/reveal.scss +5 -0
- package/src/components/schedule-builder/schedule-builder.component.ts +23 -8
- package/src/components/scroll-container/scroll-container.component.ts +19 -1
- package/src/components/scroll-container/scroll-container.scss +6 -1
- package/src/components/select/select.component.ts +52 -36
- package/src/components/select/select.scss +42 -25
- package/src/components/select/select.test.ts +14 -2
- package/src/components/settings-container/settings-container.scss +18 -5
- package/src/components/sidebar/sidebar.component.ts +8 -1
- package/src/components/sidebar/sidebar.scss +6 -1
- package/src/components/skeleton/skeleton.component.ts +3 -3
- package/src/components/skeleton/skeleton.scss +6 -6
- package/src/components/skeleton/skeleton.test.ts +9 -0
- package/src/components/slash-menu/slash-menu.scss +1 -1
- package/src/components/slideout/slideout.component.ts +20 -3
- package/src/components/slideout/slideout.scss +13 -6
- package/src/components/slideout/slideout.test.ts +19 -0
- package/src/components/split-button/split-button.component.ts +1 -1
- package/src/components/split-pane/split-pane.component.ts +71 -8
- package/src/components/split-pane/split-pane.scss +145 -41
- package/src/components/split-pane/split-pane.test.ts +23 -3
- package/src/components/stat/stat.component.ts +209 -14
- package/src/components/stat/stat.scss +305 -5
- package/src/components/stat/stat.test.ts +143 -2
- package/src/components/status-indicator/status-indicator.scss +22 -21
- package/src/components/stepper/stepper.scss +3 -3
- package/src/components/tab/tab.component.ts +1 -0
- package/src/components/table/table.component.ts +12 -1
- package/src/components/table/table.scss +21 -13
- package/src/components/table/table.test.ts +17 -0
- package/src/components/tabs/tabs.component.ts +150 -37
- package/src/components/tabs/tabs.scss +48 -7
- package/src/components/tabs/tabs.test.ts +169 -0
- package/src/components/textarea/textarea.component.ts +25 -3
- package/src/components/textarea/textarea.scss +10 -0
- package/src/components/textarea/textarea.test.ts +8 -4
- package/src/components/theme-editor/theme-editor.scss +0 -6
- package/src/components/tile/tile.component.ts +32 -2
- package/src/components/tile/tile.scss +40 -7
- package/src/components/tile/tile.test.ts +10 -0
- package/src/components/toggle/toggle.component.ts +23 -7
- package/src/components/toggle/toggle.scss +36 -29
- package/src/components/toggle/toggle.test.ts +17 -0
- package/src/components/tooltip/tooltip.component.ts +3 -0
- package/src/components/tooltip/tooltip.scss +11 -6
- package/src/components/tooltip/tooltip.test.ts +12 -0
- package/src/components/translation-field/index.ts +12 -0
- package/src/components/translation-field/translation-field.component.ts +55 -0
- package/src/components/translation-field/translation-field.scss +101 -0
- package/src/components/translation-fields/index.ts +12 -0
- package/src/components/translation-fields/translation-fields.component.ts +80 -0
- package/src/components/translation-fields/translation-fields.scss +60 -0
- package/src/components/translation-grid/index.ts +12 -0
- package/src/components/translation-grid/translation-grid.component.ts +157 -0
- package/src/components/translation-grid/translation-grid.scss +138 -0
- package/src/components/translation-grid/translation-grid.test.ts +28 -0
- package/src/components/vertical-stepper/vertical-stepper.component.ts +35 -9
- package/src/components/vertical-stepper/vertical-stepper.scss +110 -39
- package/src/components/vertical-stepper/vertical-stepper.test.ts +35 -0
- package/src/components/well/well.scss +2 -2
- package/src/events/events.ts +2 -0
- package/src/events/zn-chart-click.ts +13 -0
- package/src/events/zn-report-change.ts +7 -0
- package/src/form-control.scss +36 -3
- package/src/internal/anchored-popover.ts +175 -0
- package/src/internal/animate.ts +19 -5
- package/src/internal/cosmic-border.ts +15 -0
- package/src/internal/form.ts +1 -1
- package/src/internal/hover-intent.ts +15 -0
- package/src/internal/input-modality.ts +15 -0
- package/src/internal/pointer-intent.ts +37 -0
- package/src/internal/reorder.ts +625 -0
- package/src/internal/scroll.ts +51 -3
- package/src/internal/selection-card.ts +1 -1
- package/src/internal/tab-indicator.ts +92 -0
- package/src/internal/table-sort.ts +25 -0
- package/src/internal/table-tabs.ts +108 -0
- package/src/internal/test-drag.ts +35 -0
- package/src/internal/theme-openbyte.test.ts +126 -0
- package/src/internal/theme.ts +4 -4
- package/src/internal/zinc-element.ts +2 -2
- package/src/selection-card.scss +25 -0
- package/src/types/web-test-runner-commands.d.ts +1 -0
- package/src/utilities/color.test.ts +43 -0
- package/src/utilities/color.ts +189 -0
- package/src/zinc.ts +14 -0
- package/.github/workflows/eleventy_build.yml +0 -42
- package/scss/themes/_aura.scss +0 -114
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
&__caption {
|
|
18
|
+
font-family: var(--zn-text-h1-font-family, inherit);
|
|
18
19
|
font-weight: var(--zn-text-h1-font-weight);
|
|
19
20
|
font-size: var(--zn-text-h1-font-size);
|
|
20
21
|
text-transform: var(--zn-text-h1-transform, none);
|
|
@@ -84,7 +85,7 @@
|
|
|
84
85
|
}
|
|
85
86
|
|
|
86
87
|
zn-icon:hover {
|
|
87
|
-
color: rgb(var(--zn-primary));
|
|
88
|
+
color: var(--zn-color-primary-fg, rgb(var(--zn-primary)));
|
|
88
89
|
cursor: pointer;
|
|
89
90
|
}
|
|
90
91
|
}
|
|
@@ -131,8 +132,7 @@
|
|
|
131
132
|
flex-grow: 1;
|
|
132
133
|
flex-shrink: 1;
|
|
133
134
|
// Chrome ignores overflow-clip-margin unless both axes clip
|
|
134
|
-
|
|
135
|
-
overflow-clip-margin: var(--zn-focus-ring-outset);
|
|
135
|
+
@include wc.clip-focus-room;
|
|
136
136
|
min-width: 20px;
|
|
137
137
|
}
|
|
138
138
|
|
|
@@ -159,7 +159,7 @@
|
|
|
159
159
|
}
|
|
160
160
|
|
|
161
161
|
&::slotted(a:hover) {
|
|
162
|
-
color: rgb(var(--zn-primary));
|
|
162
|
+
color: var(--zn-color-primary-fg, rgb(var(--zn-primary)));
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
&::slotted(a)::after {
|
|
@@ -190,7 +190,7 @@
|
|
|
190
190
|
cursor: pointer;
|
|
191
191
|
|
|
192
192
|
&:hover {
|
|
193
|
-
color: rgb(var(--zn-primary));
|
|
193
|
+
color: var(--zn-color-primary-fg, rgb(var(--zn-primary)));
|
|
194
194
|
}
|
|
195
195
|
}
|
|
196
196
|
}
|
|
@@ -232,6 +232,12 @@
|
|
|
232
232
|
flex-wrap: nowrap;
|
|
233
233
|
}
|
|
234
234
|
|
|
235
|
+
// Controls in the actions row line up with the buttons beside them, without
|
|
236
|
+
// the label/help-text spacing a form field otherwise carries.
|
|
237
|
+
slot[name="actions"]::slotted(*) {
|
|
238
|
+
--zn-form-control-input-margin: 0;
|
|
239
|
+
}
|
|
240
|
+
|
|
235
241
|
slot[name="actions"]::slotted(zn-button-menu) {
|
|
236
242
|
flex-grow: 1;
|
|
237
243
|
flex-shrink: 1;
|
|
@@ -247,3 +253,39 @@
|
|
|
247
253
|
border-bottom: none !important;
|
|
248
254
|
}
|
|
249
255
|
}
|
|
256
|
+
|
|
257
|
+
.header__left:has(.header__description) {
|
|
258
|
+
display: flex;
|
|
259
|
+
flex-direction: column;
|
|
260
|
+
gap: var(--zn-spacing-2x-small);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.header--inline-description .header__left:has(.header__description) {
|
|
264
|
+
flex-direction: row;
|
|
265
|
+
flex-wrap: wrap;
|
|
266
|
+
justify-content: flex-start;
|
|
267
|
+
align-items: baseline;
|
|
268
|
+
gap: 0 var(--zn-spacing-x-small);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Narrow headers drop the actions onto their own row instead of squeezing the caption
|
|
272
|
+
.header .caption {
|
|
273
|
+
flex-wrap: wrap;
|
|
274
|
+
row-gap: var(--zn-spacing-small);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
.header__caption {
|
|
278
|
+
flex-shrink: 0;
|
|
279
|
+
min-width: min-content;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
.header__right {
|
|
283
|
+
min-width: min-content;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
.header__description {
|
|
287
|
+
font-size: var(--zn-header-description-font-size, var(--zn-font-size-x-small));
|
|
288
|
+
font-weight: normal;
|
|
289
|
+
line-height: var(--zn-line-height-normal, 1.45);
|
|
290
|
+
color: var(--zn-header-description-color, var(--zn-text-color-muted));
|
|
291
|
+
}
|
|
@@ -81,8 +81,9 @@ export default class ZnHoverContainer extends ZincElement {
|
|
|
81
81
|
return triggers.includes(triggerType);
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
|
|
85
|
-
|
|
84
|
+
// relatedTarget is retargeted to this host when focus moves into the shadow root, so contains() covers both trees
|
|
85
|
+
private handleBlur = (event: FocusEvent) => {
|
|
86
|
+
if (this.hasTrigger('focus') && !this.contains(event.relatedTarget as Node | null)) {
|
|
86
87
|
this.hide();
|
|
87
88
|
}
|
|
88
89
|
};
|
|
@@ -204,9 +205,7 @@ export default class ZnHoverContainer extends ZincElement {
|
|
|
204
205
|
hover-bridge>
|
|
205
206
|
<slot slot="anchor"></slot>
|
|
206
207
|
<div part="body" id="hover-container" class="hover-container__body"
|
|
207
|
-
|
|
208
|
-
aria-live=${this.open ? 'polite' : 'off'}
|
|
209
|
-
aria-label="hover-container">
|
|
208
|
+
aria-live=${this.open ? 'polite' : 'off'}>
|
|
210
209
|
<slot name="content">${this.content}</slot>
|
|
211
210
|
</div>
|
|
212
211
|
</zn-popup>`;
|
|
@@ -5,6 +5,7 @@ import {property} from 'lit/decorators.js';
|
|
|
5
5
|
import {sha256} from '../../utilities/sha256';
|
|
6
6
|
import {styleMap} from "lit/directives/style-map.js";
|
|
7
7
|
import {unsafeSVG} from 'lit/directives/unsafe-svg.js';
|
|
8
|
+
import lineIconStyles from './lni.scss';
|
|
8
9
|
import ZincElement from '../../internal/zinc-element';
|
|
9
10
|
import type {IconNode, SVGProps} from 'lucide';
|
|
10
11
|
|
|
@@ -28,9 +29,27 @@ function loadLucideIcons(): Promise<void> {
|
|
|
28
29
|
return lucidePromise;
|
|
29
30
|
}
|
|
30
31
|
|
|
32
|
+
// Country flags (3:2 SVGs keyed by ISO 3166 alpha-2 code) load lazily like lucide
|
|
33
|
+
let flagIcons: Record<string, string> | undefined;
|
|
34
|
+
let flagPromise: Promise<void> | undefined;
|
|
35
|
+
|
|
36
|
+
function loadFlagIcons(): Promise<void> {
|
|
37
|
+
flagPromise ??= import('country-flag-icons/string/3x2').then(m => {
|
|
38
|
+
flagIcons = m as unknown as Record<string, string>;
|
|
39
|
+
}).catch((error: unknown) => {
|
|
40
|
+
flagPromise = undefined;
|
|
41
|
+
console.warn('<zn-icon> failed to load the flag icon chunk', error);
|
|
42
|
+
});
|
|
43
|
+
return flagPromise;
|
|
44
|
+
}
|
|
45
|
+
|
|
31
46
|
export type IconLibrary = "src" | "material" | "material-outlined" | "material-round" | "material-sharp" |
|
|
32
47
|
"material-two-tone" | "material-symbols-outlined" | "gravatar" | "libravatar" | "avatar" | "brands" | "line" |
|
|
33
|
-
"lucide";
|
|
48
|
+
"lucide" | "flag";
|
|
49
|
+
|
|
50
|
+
export type AvatarPresence = 'online' | 'away' | 'busy' | 'meeting' | 'offline';
|
|
51
|
+
|
|
52
|
+
const namedSizes: Record<string, number> = {sm: 22, md: 28, lg: 40, xl: 56};
|
|
34
53
|
|
|
35
54
|
export type IconColor =
|
|
36
55
|
"default"
|
|
@@ -70,14 +89,24 @@ export type IconColor =
|
|
|
70
89
|
*
|
|
71
90
|
* @cssproperty --example - An example CSS custom property.
|
|
72
91
|
*/
|
|
92
|
+
// Lineicons' ~600 glyph rules are adopted only by icons that use them: every adopted rule is restyled per shadow
|
|
93
|
+
// root, and icons are the most numerous component on a page (over 1,000 on some views, seconds of style work in Safari).
|
|
94
|
+
const lineIcons = unsafeCSS(lineIconStyles);
|
|
95
|
+
|
|
73
96
|
export default class ZnIcon extends ZincElement {
|
|
74
97
|
static styles: CSSResultGroup = unsafeCSS(styles);
|
|
75
98
|
|
|
99
|
+
private _lineIconsAdopted = false;
|
|
100
|
+
|
|
76
101
|
@property({reflect: true}) src: string;
|
|
77
102
|
|
|
78
103
|
@property({reflect: true}) alt: string;
|
|
79
104
|
|
|
80
|
-
|
|
105
|
+
/** Size in pixels, or one of the avatar sizes `sm` (22), `md` (28), `lg` (40) and `xl` (56). */
|
|
106
|
+
@property({
|
|
107
|
+
reflect: true,
|
|
108
|
+
converter: {fromAttribute: (value: string | null) => namedSizes[value ?? ''] ?? (value === null ? 24 : Number(value))}
|
|
109
|
+
}) size: number = 24;
|
|
81
110
|
|
|
82
111
|
@property({type: Boolean, reflect: true}) round = false;
|
|
83
112
|
@property({type: Boolean, reflect: true}) tile = false;
|
|
@@ -94,6 +123,12 @@ export default class ZnIcon extends ZincElement {
|
|
|
94
123
|
|
|
95
124
|
@property({type: Boolean}) squared: boolean = false;
|
|
96
125
|
|
|
126
|
+
/** A live-status dot on an avatar: `online`, `away`, `busy`, `meeting` or `offline`. */
|
|
127
|
+
@property({reflect: true}) presence: AvatarPresence;
|
|
128
|
+
|
|
129
|
+
/** Draws an avatar as a solid circle even where the theme tints avatars. */
|
|
130
|
+
@property({type: Boolean, reflect: true}) solid = false;
|
|
131
|
+
|
|
97
132
|
private static readonly presetColors = new Set([
|
|
98
133
|
'default', 'primary', 'accent', 'info', 'warning', 'error', 'success',
|
|
99
134
|
'white', 'disabled', 'red', 'blue', 'green', 'orange', 'yellow',
|
|
@@ -148,6 +183,8 @@ export default class ZnIcon extends ZincElement {
|
|
|
148
183
|
case "lucide":
|
|
149
184
|
case "lu":
|
|
150
185
|
return "lucide";
|
|
186
|
+
case "flag":
|
|
187
|
+
return "flag";
|
|
151
188
|
case 'brand':
|
|
152
189
|
case 'brands':
|
|
153
190
|
return "brands";
|
|
@@ -191,6 +228,13 @@ export default class ZnIcon extends ZincElement {
|
|
|
191
228
|
await super.getUpdateComplete();
|
|
192
229
|
}
|
|
193
230
|
}
|
|
231
|
+
if (this.library === 'flag' && !flagIcons) {
|
|
232
|
+
await loadFlagIcons();
|
|
233
|
+
if (flagIcons) {
|
|
234
|
+
this.requestUpdate();
|
|
235
|
+
await super.getUpdateComplete();
|
|
236
|
+
}
|
|
237
|
+
}
|
|
194
238
|
return result;
|
|
195
239
|
}
|
|
196
240
|
|
|
@@ -273,6 +317,26 @@ export default class ZnIcon extends ZincElement {
|
|
|
273
317
|
return email.trim().toLowerCase();
|
|
274
318
|
}
|
|
275
319
|
|
|
320
|
+
protected override updated(changedProperties: PropertyValues<this>) {
|
|
321
|
+
super.updated(changedProperties);
|
|
322
|
+
if (this.library === 'line') {
|
|
323
|
+
this.adoptLineIcons();
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
private adoptLineIcons() {
|
|
328
|
+
const root = this.shadowRoot;
|
|
329
|
+
if (!root || this._lineIconsAdopted) return;
|
|
330
|
+
this._lineIconsAdopted = true;
|
|
331
|
+
if (lineIcons.styleSheet) {
|
|
332
|
+
root.adoptedStyleSheets = [...root.adoptedStyleSheets, lineIcons.styleSheet];
|
|
333
|
+
} else {
|
|
334
|
+
const style = document.createElement('style');
|
|
335
|
+
style.textContent = lineIcons.cssText;
|
|
336
|
+
root.appendChild(style);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
276
340
|
render() {
|
|
277
341
|
return html`
|
|
278
342
|
<div class="icon-wrapper">
|
|
@@ -300,6 +364,9 @@ export default class ZnIcon extends ZincElement {
|
|
|
300
364
|
'icon--pink': this.color === "pink",
|
|
301
365
|
'icon--grey': this.color === "grey",
|
|
302
366
|
'icon--avatar': this.library === "avatar",
|
|
367
|
+
'icon--avatar-solid': this.library === "avatar" && this.solid,
|
|
368
|
+
'icon--flag': this.library === "flag",
|
|
369
|
+
'icon--has-color': Boolean(this.color),
|
|
303
370
|
'icon--padded': this.padded,
|
|
304
371
|
'icon--blink': this.blink,
|
|
305
372
|
'icon--squared': this.squared,
|
|
@@ -309,7 +376,7 @@ export default class ZnIcon extends ZincElement {
|
|
|
309
376
|
'--icon-size': this.size + "px",
|
|
310
377
|
'--icon-color': (this.color && !this.isPresetColor(this.color)) ? this.color : null,
|
|
311
378
|
'--icon-fill': (this.fill && !this.isPresetColor(this.fill)) ? this.fill : null,
|
|
312
|
-
'--avatar-color': this.color ? null : (this.library === 'avatar' ? this.getColorForAvatar(this.
|
|
379
|
+
'--avatar-color': this.color ? null : (this.library === 'avatar' ? this.getColorForAvatar(this.src) : null)
|
|
313
380
|
})}">
|
|
314
381
|
${this.library && this.library !== "src" ? html`
|
|
315
382
|
${choose(this.library, [
|
|
@@ -321,12 +388,14 @@ export default class ZnIcon extends ZincElement {
|
|
|
321
388
|
["brands", () => html`<i part="icon" class="kb">${this.src}</i>`],
|
|
322
389
|
["line", () => html`<i part="icon" class="lni lni-${this.src}"></i>`],
|
|
323
390
|
["lucide", () => this.renderLucideIcon()],
|
|
391
|
+
["flag", () => this.renderFlagIcon()],
|
|
324
392
|
["material-symbols-outlined", () => html`<i part="icon" class="mi mi--symbol-outlined">${this.src}</i>`],
|
|
325
393
|
["gravatar", () => html`<img part="icon" alt="${this.alt}"
|
|
326
394
|
src="https://www.gravatar.com/avatar/${this.src}?s=${this.size}${this.gravatarOptions}"/>`],
|
|
327
395
|
["libravatar", () => html`<img part="icon" alt="${this.alt}"
|
|
328
396
|
src="https://seccdn.libravatar.org/avatar/${this.src}?s=${this.size}${this.gravatarOptions}"/>`],
|
|
329
|
-
["avatar", () => html`<span class="avatar__text"
|
|
397
|
+
["avatar", () => html`<span class="avatar__text" role="img"
|
|
398
|
+
aria-label="${this.alt || this.src}">${this.getAvatarInitials(this.src)}</span>`]
|
|
330
399
|
], () => html`Library not supported: ${this.library}`)}` : ''}
|
|
331
400
|
|
|
332
401
|
${(!this.library || this.library === 'src') && this.src ? html`
|
|
@@ -336,7 +405,10 @@ export default class ZnIcon extends ZincElement {
|
|
|
336
405
|
${!this.library && !this.src ? html`
|
|
337
406
|
<slot></slot>` : ''}
|
|
338
407
|
</div>
|
|
339
|
-
</div
|
|
408
|
+
</div>
|
|
409
|
+
${this.presence ? html`
|
|
410
|
+
<span part="presence" class="presence presence--${this.presence}" role="img"
|
|
411
|
+
aria-label="${this.presence}"></span>` : ''}`;
|
|
340
412
|
}
|
|
341
413
|
|
|
342
414
|
private getAvatarInitials(avatar: string) {
|
|
@@ -376,6 +448,21 @@ export default class ZnIcon extends ZincElement {
|
|
|
376
448
|
return unsafeSVG(this.getLucideSvg(icon));
|
|
377
449
|
}
|
|
378
450
|
|
|
451
|
+
private renderFlagIcon() {
|
|
452
|
+
if (!flagIcons) {
|
|
453
|
+
void loadFlagIcons().then(() => {
|
|
454
|
+
if (flagIcons) {
|
|
455
|
+
this.requestUpdate();
|
|
456
|
+
}
|
|
457
|
+
});
|
|
458
|
+
return html``;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
const code = (this.src ?? '').toUpperCase();
|
|
462
|
+
const svg = Object.prototype.hasOwnProperty.call(flagIcons, code) ? flagIcons[code] : undefined;
|
|
463
|
+
return svg ? html`<span part="icon" class="flag" role="img" aria-label="${this.alt || code}">${unsafeSVG(svg)}</span>` : html``;
|
|
464
|
+
}
|
|
465
|
+
|
|
379
466
|
private getLucideIcon(src = ''): IconNode | undefined {
|
|
380
467
|
if (!src) {
|
|
381
468
|
return undefined;
|
|
@@ -438,17 +525,16 @@ export default class ZnIcon extends ZincElement {
|
|
|
438
525
|
}
|
|
439
526
|
|
|
440
527
|
|
|
441
|
-
protected getColorForAvatar(
|
|
528
|
+
protected getColorForAvatar(name: string) {
|
|
442
529
|
const colors = [
|
|
443
530
|
'#00AA55', '#0882a8', '#ed55ed', '#5eae00',
|
|
444
531
|
'#09389e', '#d17300', '#5e0be2', '#5b7a13',
|
|
445
532
|
'#ddb100', '#d400c2', '#DC2A2A', '#a967ff',
|
|
446
533
|
];
|
|
447
534
|
|
|
448
|
-
//
|
|
449
|
-
const hash = Array.from(
|
|
535
|
+
// Order-sensitive, so names sharing initials or letters still spread across the palette
|
|
536
|
+
const hash = Array.from(name.trim().toLowerCase()).reduce((acc, char) => (acc * 31 + char.charCodeAt(0)) >>> 0, 0);
|
|
450
537
|
|
|
451
|
-
// Use the hash to select a color
|
|
452
538
|
return colors[hash % colors.length];
|
|
453
539
|
}
|
|
454
540
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
@use "../../wc";
|
|
2
|
-
@use "./lni";
|
|
3
2
|
|
|
4
3
|
$icon-colors: (
|
|
5
4
|
default: --zn-text,
|
|
@@ -30,6 +29,40 @@ $icon-colors: (
|
|
|
30
29
|
--icon-color: inherit;
|
|
31
30
|
}
|
|
32
31
|
|
|
32
|
+
:host([presence]) {
|
|
33
|
+
position: relative;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// A live-status dot on the avatar's corner, ringed in the surface colour so it reads against the avatar.
|
|
37
|
+
.presence {
|
|
38
|
+
position: absolute;
|
|
39
|
+
right: -1px;
|
|
40
|
+
bottom: -1px;
|
|
41
|
+
width: 28%;
|
|
42
|
+
height: 28%;
|
|
43
|
+
min-width: 7px;
|
|
44
|
+
min-height: 7px;
|
|
45
|
+
border: 2px solid var(--zn-avatar-ring-color, rgb(var(--zn-panel)));
|
|
46
|
+
border-radius: 50%;
|
|
47
|
+
background: rgb(var(--zn-color-disabled));
|
|
48
|
+
|
|
49
|
+
&--online {
|
|
50
|
+
background: rgb(var(--zn-color-success));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
&--away {
|
|
54
|
+
background: rgb(var(--zn-color-warning));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
&--busy {
|
|
58
|
+
background: rgb(var(--zn-color-error));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
&--meeting {
|
|
62
|
+
background: rgb(var(--zn-primary));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
33
66
|
:host([split-pane-focus]) .icon-wrapper {
|
|
34
67
|
cursor: pointer;
|
|
35
68
|
}
|
|
@@ -62,6 +95,19 @@ $icon-colors: (
|
|
|
62
95
|
font-size: var(--icon-size) !important;
|
|
63
96
|
}
|
|
64
97
|
|
|
98
|
+
&--flag {
|
|
99
|
+
display: inline-flex;
|
|
100
|
+
align-items: center;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.flag svg {
|
|
104
|
+
display: block;
|
|
105
|
+
width: var(--icon-size);
|
|
106
|
+
height: calc(var(--icon-size) * 2 / 3);
|
|
107
|
+
border-radius: 2px;
|
|
108
|
+
box-shadow: 0 0 0 1px rgb(0 0 0 / 0.08);
|
|
109
|
+
}
|
|
110
|
+
|
|
65
111
|
// Colors
|
|
66
112
|
|
|
67
113
|
&--default {
|
|
@@ -178,20 +224,38 @@ $icon-colors: (
|
|
|
178
224
|
animation: blink-animation 1.5s steps(5, start) infinite;
|
|
179
225
|
}
|
|
180
226
|
|
|
227
|
+
// Solid by default. A theme sets --zn-avatar-tint: 1 for a tinted circle with darker initials,
|
|
228
|
+
// and --zn-avatar-default-color to replace the per-name colour.
|
|
181
229
|
&--avatar {
|
|
182
|
-
|
|
183
|
-
|
|
230
|
+
--_avatar: var(--zn-avatar-default-color, var(--avatar-color));
|
|
231
|
+
--_tint: var(--zn-avatar-tint, 0);
|
|
232
|
+
--_solid-ink: hsl(from var(--_avatar) h s 95);
|
|
233
|
+
// Mixed towards the text colour so the initials stay readable in light and dark modes
|
|
234
|
+
--_tint-ink: color-mix(in srgb, var(--_avatar) 72%, rgb(var(--zn-text)));
|
|
235
|
+
|
|
236
|
+
// Mixed with the surface, not made translucent, so overlapping avatars in a group stay opaque
|
|
237
|
+
background-color: color-mix(in srgb, var(--_avatar) calc(100% - var(--_tint) * 86%), rgb(var(--zn-panel)));
|
|
238
|
+
color: color-mix(in srgb, var(--_tint-ink) calc(var(--_tint) * 100%), var(--_solid-ink));
|
|
184
239
|
display: flex;
|
|
185
240
|
justify-content: center;
|
|
186
241
|
align-items: center;
|
|
187
242
|
|
|
188
243
|
.avatar__text {
|
|
189
|
-
font-size: calc(
|
|
190
|
-
|
|
191
|
-
|
|
244
|
+
font-size: calc(var(--icon-size) * var(--zn-avatar-font-scale, 0.4167));
|
|
245
|
+
font-weight: var(--zn-avatar-font-weight, var(--zn-font-weight-semibold));
|
|
246
|
+
letter-spacing: .02em;
|
|
247
|
+
color: inherit;
|
|
192
248
|
}
|
|
193
249
|
}
|
|
194
250
|
|
|
251
|
+
&--avatar.icon--has-color {
|
|
252
|
+
--_avatar: var(--icon-color);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
&--avatar.icon--avatar-solid {
|
|
256
|
+
--_tint: 0;
|
|
257
|
+
}
|
|
258
|
+
|
|
195
259
|
&--padded {
|
|
196
260
|
padding: var(--zn-spacing-small);
|
|
197
261
|
}
|
|
@@ -31,6 +31,19 @@ describe('<zn-icon>', () => {
|
|
|
31
31
|
);
|
|
32
32
|
});
|
|
33
33
|
|
|
34
|
+
it('should style Lineicons glyphs only on line icons', async () => {
|
|
35
|
+
const hasLineRules = (el: Element) =>
|
|
36
|
+
el.shadowRoot!.adoptedStyleSheets.some(sheet => Array.from(sheet.cssRules).some(rule => rule.cssText.includes(".lni-home-2")));
|
|
37
|
+
|
|
38
|
+
const line = await fixture<ZnIcon>(html`<zn-icon src="home-2" library="line"></zn-icon>`);
|
|
39
|
+
const glyph = line.shadowRoot!.querySelector('.lni')!;
|
|
40
|
+
expect(getComputedStyle(glyph, '::before').content).to.not.equal('none');
|
|
41
|
+
expect(hasLineRules(line)).to.be.true;
|
|
42
|
+
|
|
43
|
+
const lucide = await fixture<ZnIcon>(html`<zn-icon src="home@lu"></zn-icon>`);
|
|
44
|
+
expect(hasLineRules(lucide)).to.be.false;
|
|
45
|
+
});
|
|
46
|
+
|
|
34
47
|
it('should render Lucide icons as inline SVGs', async () => {
|
|
35
48
|
const el = await fixture<ZnIcon>(html`
|
|
36
49
|
<zn-icon src="cloud-upload" library="lucide" size="48"></zn-icon>
|
|
@@ -55,6 +68,19 @@ describe('<zn-icon>', () => {
|
|
|
55
68
|
expect(svg).to.exist;
|
|
56
69
|
});
|
|
57
70
|
|
|
71
|
+
it('should render country flags from the flag library', async () => {
|
|
72
|
+
const el = await fixture<ZnIcon>(html`<zn-icon src="gb@flag" size="20"></zn-icon>`);
|
|
73
|
+
|
|
74
|
+
expect(el.library).to.equal('flag');
|
|
75
|
+
expect(el.shadowRoot?.querySelector('.flag[aria-label="GB"] svg')).to.exist;
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('should render nothing for an unknown flag', async () => {
|
|
79
|
+
const el = await fixture<ZnIcon>(html`<zn-icon src="zz@flag"></zn-icon>`);
|
|
80
|
+
|
|
81
|
+
expect(el.shadowRoot?.querySelector('.flag')).to.not.exist;
|
|
82
|
+
});
|
|
83
|
+
|
|
58
84
|
it('should remove a matching library indicator from the icon src', async () => {
|
|
59
85
|
const el = await fixture<ZnIcon>(html`
|
|
60
86
|
<zn-icon src="home@lu" library="lucide" size="48"></zn-icon>
|
|
@@ -75,4 +101,33 @@ describe('<zn-icon>', () => {
|
|
|
75
101
|
expect(el.library).to.equal('material-symbols-outlined');
|
|
76
102
|
expect(el.src).to.equal('home@lu');
|
|
77
103
|
});
|
|
104
|
+
|
|
105
|
+
it('should accept the named avatar sizes', async () => {
|
|
106
|
+
const el = await fixture<ZnIcon>(html`<zn-icon src="Fiona Hart" library="avatar" size="lg"></zn-icon>`);
|
|
107
|
+
expect(el.size).to.equal(40);
|
|
108
|
+
expect(el.shadowRoot!.querySelector<HTMLElement>('.icon')!.getBoundingClientRect().width).to.equal(40);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('should still accept a size in pixels', async () => {
|
|
112
|
+
const el = await fixture<ZnIcon>(html`<zn-icon src="home" size="18"></zn-icon>`);
|
|
113
|
+
expect(el.size).to.equal(18);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('should show a presence dot only when presence is set', async () => {
|
|
117
|
+
const el = await fixture<ZnIcon>(html`<zn-icon src="Ben Brooks" library="avatar" round></zn-icon>`);
|
|
118
|
+
expect(el.shadowRoot!.querySelector('[part="presence"]')).to.be.null;
|
|
119
|
+
|
|
120
|
+
el.presence = 'online';
|
|
121
|
+
await el.updateComplete;
|
|
122
|
+
const dot = el.shadowRoot!.querySelector('[part="presence"]')!;
|
|
123
|
+
expect(dot.classList.contains('presence--online')).to.be.true;
|
|
124
|
+
expect(dot.getAttribute('aria-label')).to.equal('online');
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('should name an avatar for assistive technology', async () => {
|
|
128
|
+
const el = await fixture<ZnIcon>(html`<zn-icon src="BB" alt="Ben Brooks" library="avatar"></zn-icon>`);
|
|
129
|
+
const text = el.shadowRoot!.querySelector('.avatar__text')!;
|
|
130
|
+
expect(text.getAttribute('role')).to.equal('img');
|
|
131
|
+
expect(text.getAttribute('aria-label')).to.equal('Ben Brooks');
|
|
132
|
+
});
|
|
78
133
|
});
|
|
@@ -60,6 +60,7 @@ export default class ZnIconPicker extends ZincElement implements ZincFormControl
|
|
|
60
60
|
@state() private _searchQuery = '';
|
|
61
61
|
@state() private _iconList: string[] = [];
|
|
62
62
|
@state() private _filteredIcons: string[] = [];
|
|
63
|
+
@state() private _gridFocusIcon = '';
|
|
63
64
|
|
|
64
65
|
// Pending selections (not committed until confirm)
|
|
65
66
|
@state() private _pendingIcon = '';
|
|
@@ -280,6 +281,35 @@ export default class ZnIconPicker extends ZincElement implements ZincFormControl
|
|
|
280
281
|
|
|
281
282
|
private handleIconSelect(iconName: string) {
|
|
282
283
|
this._pendingIcon = iconName;
|
|
284
|
+
this._gridFocusIcon = iconName;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
private _gridTabStop() {
|
|
288
|
+
if (this._filteredIcons.includes(this._gridFocusIcon)) return this._gridFocusIcon;
|
|
289
|
+
if (this._filteredIcons.includes(this._pendingIcon)) return this._pendingIcon;
|
|
290
|
+
return this._filteredIcons[0];
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
private _handleGridKeyDown(e: KeyboardEvent) {
|
|
294
|
+
const items = [...(e.currentTarget as HTMLElement).querySelectorAll<HTMLButtonElement>('.icon-picker__grid-item')];
|
|
295
|
+
const index = items.indexOf(e.target as HTMLButtonElement);
|
|
296
|
+
if (index === -1) return;
|
|
297
|
+
|
|
298
|
+
const columns = Math.max(1, items.filter(item => item.offsetTop === items[0].offsetTop).length);
|
|
299
|
+
let next: number;
|
|
300
|
+
switch (e.key) {
|
|
301
|
+
case 'ArrowRight': next = index + 1; break;
|
|
302
|
+
case 'ArrowLeft': next = index - 1; break;
|
|
303
|
+
case 'ArrowDown': next = index + columns; break;
|
|
304
|
+
case 'ArrowUp': next = index - columns; break;
|
|
305
|
+
case 'Home': next = 0; break;
|
|
306
|
+
case 'End': next = items.length - 1; break;
|
|
307
|
+
default: return;
|
|
308
|
+
}
|
|
309
|
+
e.preventDefault();
|
|
310
|
+
next = Math.max(0, Math.min(items.length - 1, next));
|
|
311
|
+
this._gridFocusIcon = this._filteredIcons[next];
|
|
312
|
+
items[next].focus();
|
|
283
313
|
}
|
|
284
314
|
|
|
285
315
|
private async handleLibraryChange(e: Event) {
|
|
@@ -326,6 +356,7 @@ export default class ZnIconPicker extends ZincElement implements ZincFormControl
|
|
|
326
356
|
}
|
|
327
357
|
|
|
328
358
|
private _handleTriggerKeyDown(e: KeyboardEvent) {
|
|
359
|
+
if (e.target !== e.currentTarget) return;
|
|
329
360
|
if (e.key === 'Enter' || e.key === ' ') {
|
|
330
361
|
e.preventDefault();
|
|
331
362
|
this._handleTriggerClick();
|
|
@@ -340,6 +371,7 @@ export default class ZnIconPicker extends ZincElement implements ZincFormControl
|
|
|
340
371
|
// auto-detection renders an <img> instead of a font ligature.
|
|
341
372
|
const triggerIcon = this._fileUrl ?? this.icon;
|
|
342
373
|
const triggerLibrary = hasValue && !this.isImageValue ? this.library : nothing;
|
|
374
|
+
const gridTabStop = this._gridTabStop();
|
|
343
375
|
const pendingPreviewUrl = this._pendingFileUrl ?? (this.isImageValue && !this._file ? this.icon : this._fileUrl);
|
|
344
376
|
|
|
345
377
|
return html`
|
|
@@ -503,7 +535,7 @@ export default class ZnIconPicker extends ZincElement implements ZincFormControl
|
|
|
503
535
|
<zn-icon slot="prefix" src="search" size="18"></zn-icon>
|
|
504
536
|
</zn-input>
|
|
505
537
|
|
|
506
|
-
<div class="icon-picker__grid">
|
|
538
|
+
<div class="icon-picker__grid" @keydown=${this._handleGridKeyDown}>
|
|
507
539
|
${this._filteredIcons.length === 0 ? html`
|
|
508
540
|
<div class="icon-picker__no-results">No icons found</div>
|
|
509
541
|
` : this._filteredIcons.map(iconName => html`
|
|
@@ -514,6 +546,8 @@ export default class ZnIconPicker extends ZincElement implements ZincFormControl
|
|
|
514
546
|
'icon-picker__grid-item--selected': iconName === this._pendingIcon
|
|
515
547
|
})}
|
|
516
548
|
title=${iconName}
|
|
549
|
+
aria-pressed=${iconName === this._pendingIcon ? 'true' : 'false'}
|
|
550
|
+
tabindex=${iconName === gridTabStop ? '0' : '-1'}
|
|
517
551
|
@click=${() => this.handleIconSelect(iconName)}>
|
|
518
552
|
<zn-icon
|
|
519
553
|
src=${iconName}
|