@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
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
:host {
|
|
4
4
|
display: block;
|
|
5
5
|
line-height: var(--zn-line-height-dense);
|
|
6
|
-
max-width: var(--zn-container-lg);
|
|
7
6
|
--zn-col-gap: calc(var(--zn-spacing-medium, 20px) * 3);
|
|
8
7
|
--zn-form-group-sticky-top: var(--zn-spacing-medium, 20px);
|
|
9
8
|
}
|
|
@@ -13,6 +12,7 @@
|
|
|
13
12
|
border: none;
|
|
14
13
|
padding: 0;
|
|
15
14
|
margin: 0;
|
|
15
|
+
max-width: var(--zn-container-lg);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
.form-control__label {
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
.form-control--has-label .form-control__label {
|
|
23
|
+
font-family: var(--zn-form-group-label-font-family, inherit);
|
|
23
24
|
font-weight: var(--zn-font-weight-bold);
|
|
24
25
|
font-size: var(--zn-font-size-large);
|
|
25
26
|
}
|
|
@@ -32,7 +33,7 @@
|
|
|
32
33
|
// label would scroll over the fields it labels. zn-cols wraps below the two columns' bases plus
|
|
33
34
|
// the gap, so 670px = --zn-col-basis * (1 + 2) + 70px — keep in step with both values below.
|
|
34
35
|
@container (min-width: 670px) {
|
|
35
|
-
.form-control__text {
|
|
36
|
+
.form-control:not(.form-control--stacked) .form-control__text {
|
|
36
37
|
position: sticky;
|
|
37
38
|
top: var(--zn-form-group-sticky-top);
|
|
38
39
|
// A stretched flex item is as tall as the row, leaving sticky nothing to travel through.
|
|
@@ -59,7 +60,7 @@
|
|
|
59
60
|
.form-control-input {
|
|
60
61
|
display: grid;
|
|
61
62
|
grid-template-columns: minmax(0, 1fr);
|
|
62
|
-
gap: var(--zn-spacing-medium) var(--zn-spacing-small);
|
|
63
|
+
gap: var(--zn-form-row-gap, var(--zn-spacing-medium)) var(--zn-spacing-small);
|
|
63
64
|
|
|
64
65
|
// `1fr` floors each track at its min-content width, so one long label steals width from
|
|
65
66
|
// the other tracks and the spans stop lining up.
|
|
@@ -110,8 +111,9 @@ zn-cols::part(base) {
|
|
|
110
111
|
gap: var(--zn-gap);
|
|
111
112
|
}
|
|
112
113
|
|
|
114
|
+
// layout="1" stacks the label above the fields at every width, so there is no gutter to keep.
|
|
113
115
|
@container (min-width: 670px) {
|
|
114
|
-
zn-cols::part(base) {
|
|
116
|
+
.form-control:not(.form-control--stacked) zn-cols::part(base) {
|
|
115
117
|
gap: 70px;
|
|
116
118
|
}
|
|
117
119
|
}
|
|
@@ -121,3 +123,34 @@ zn-cols::part(base) {
|
|
|
121
123
|
padding-top: var(--zn-base-gap);
|
|
122
124
|
padding-left: var(--zn-base-gap);
|
|
123
125
|
}
|
|
126
|
+
|
|
127
|
+
// Without a label or help text the group is only its card's field grid, so it fills the card.
|
|
128
|
+
// A compact stacked group labels its fields the way an input does, so it fills the same width.
|
|
129
|
+
.form-control--full,
|
|
130
|
+
.form-control--compact.form-control--stacked {
|
|
131
|
+
max-width: none;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.form-control--compact.form-control--has-label .form-control__label {
|
|
135
|
+
display: block;
|
|
136
|
+
margin-bottom: 0;
|
|
137
|
+
font-family: inherit;
|
|
138
|
+
font-weight: var(--zn-input-label-font-weight);
|
|
139
|
+
font-size: var(--zn-input-label-font-size-medium);
|
|
140
|
+
line-height: var(--zn-input-label-line-height);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.form-control--compact .form-control__help-text {
|
|
144
|
+
margin-top: var(--zn-input-label-spacing, var(--zn-spacing-2x-small));
|
|
145
|
+
font-size: var(--zn-input-help-text-font-size-medium);
|
|
146
|
+
line-height: var(--zn-input-help-text-line-height, 1.5);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.form-control--compact zn-cols::part(base) {
|
|
150
|
+
gap: var(--zn-form-row-gap, var(--zn-spacing-medium));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Stacked, the label sits over its fields as an input's does
|
|
154
|
+
.form-control--compact.form-control--stacked zn-cols::part(base) {
|
|
155
|
+
gap: var(--zn-input-label-spacing, var(--zn-spacing-2x-small));
|
|
156
|
+
}
|
|
@@ -142,4 +142,14 @@ describe('<zn-form-group>', () => {
|
|
|
142
142
|
const narrowCols = narrow.querySelector('zn-form-group')!.shadowRoot!.querySelector('zn-cols')!;
|
|
143
143
|
expect(getComputedStyle(narrowCols.shadowRoot!.querySelector('.cols')!).rowGap).to.not.equal('70px');
|
|
144
144
|
});
|
|
145
|
+
|
|
146
|
+
it('fills its container without a label, and stays capped with one', async () => {
|
|
147
|
+
const plain = await fixture<HTMLElement>(html`<zn-form-group><input></zn-form-group>`);
|
|
148
|
+
const labelled = await fixture<HTMLElement>(html`<zn-form-group label="Identity" style="--zn-container-lg: 1100px"><input></zn-form-group>`);
|
|
149
|
+
|
|
150
|
+
const fieldset = (el: HTMLElement) => el.shadowRoot!.querySelector('fieldset')!;
|
|
151
|
+
|
|
152
|
+
expect(getComputedStyle(fieldset(plain)).maxWidth).to.equal('none');
|
|
153
|
+
expect(getComputedStyle(fieldset(labelled)).maxWidth).to.equal("1100px");
|
|
154
|
+
});
|
|
145
155
|
});
|
|
@@ -40,6 +40,11 @@ export default class ZnHeader extends ZincElement {
|
|
|
40
40
|
@property({type: Boolean}) transparent: boolean = false;
|
|
41
41
|
|
|
42
42
|
@property() caption: string;
|
|
43
|
+
/** A line of muted text under the caption. */
|
|
44
|
+
@property() description: string;
|
|
45
|
+
|
|
46
|
+
/** Sets the description beside the caption rather than under it, e.g. a row count. */
|
|
47
|
+
@property({attribute: 'inline-description', type: Boolean}) inlineDescription = false;
|
|
43
48
|
@property() icon: string;
|
|
44
49
|
|
|
45
50
|
@property({type: Array}) navigation = [];
|
|
@@ -114,6 +119,7 @@ export default class ZnHeader extends ZincElement {
|
|
|
114
119
|
<div class="${classMap({
|
|
115
120
|
'header': true,
|
|
116
121
|
'header--transparent': this.transparent,
|
|
122
|
+
'header--inline-description': this.inlineDescription,
|
|
117
123
|
'header--full-width': this.fullWidth,
|
|
118
124
|
'header--has-nav': hasNavigation,
|
|
119
125
|
'header--has-breadcrumb': hasBreadcrumb,
|
|
@@ -139,10 +145,9 @@ export default class ZnHeader extends ZincElement {
|
|
|
139
145
|
|
|
140
146
|
<div class="content" part="content">
|
|
141
147
|
${hasPreviousPath ? html`
|
|
142
|
-
<
|
|
143
|
-
data-target="${this.previousTarget ? this.previousTarget : ''}"
|
|
144
|
-
|
|
145
|
-
</a>` : null}
|
|
148
|
+
<zn-button href="${this.previousPath}" class="caption__back" label="Back"
|
|
149
|
+
data-target="${this.previousTarget ? this.previousTarget : ''}"
|
|
150
|
+
icon="arrow-left@lu" icon-size="20" icon-button="small" plain></zn-button>` : null}
|
|
146
151
|
|
|
147
152
|
${hasDefaultSlot ? html`
|
|
148
153
|
<div class="actions">
|
|
@@ -158,6 +163,8 @@ export default class ZnHeader extends ZincElement {
|
|
|
158
163
|
<slot name="breadcrumb" class="breadcrumb"></slot>` : null}
|
|
159
164
|
<slot name="caption">${this.caption}</slot>
|
|
160
165
|
</span>
|
|
166
|
+
${this.description ? html`
|
|
167
|
+
<span class="header__description" part="description">${this.description}</span>` : null}
|
|
161
168
|
</div>
|
|
162
169
|
|
|
163
170
|
<div part="header-right" class="header__right">
|
|
@@ -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
|
});
|