@redvars/peacock 3.4.0 → 3.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/BaseButton-DuASuVth.js +219 -0
- package/dist/BaseButton-DuASuVth.js.map +1 -0
- package/dist/BaseHyperlinkMixin-BNuwbiEf.js +65 -0
- package/dist/BaseHyperlinkMixin-BNuwbiEf.js.map +1 -0
- package/dist/assets/components.css +1 -1
- package/dist/assets/components.css.map +1 -1
- package/dist/assets/styles.css +1 -1
- package/dist/assets/styles.css.map +1 -1
- package/dist/banner.js +187 -0
- package/dist/banner.js.map +1 -0
- package/dist/bottom-sheet.js +2 -2
- package/dist/{button-COYCtuA8.js → button-DouvOfEU.js} +92 -283
- package/dist/button-DouvOfEU.js.map +1 -0
- package/dist/{button-group-DsXquZQn.js → button-group-CEdMwvJJ.js} +72 -48
- package/dist/button-group-CEdMwvJJ.js.map +1 -0
- package/dist/button-group.js +8 -5
- package/dist/button-group.js.map +1 -1
- package/dist/button.js +7 -4
- package/dist/button.js.map +1 -1
- package/dist/card.js +29 -74
- package/dist/card.js.map +1 -1
- package/dist/chart-bar.js +2 -2
- package/dist/chart-bar.js.map +1 -1
- package/dist/chart-doughnut.js +2 -2
- package/dist/chart-doughnut.js.map +1 -1
- package/dist/chart-pie.js +2 -2
- package/dist/chart-pie.js.map +1 -1
- package/dist/chart-stacked-bar.js +2 -2
- package/dist/chart-stacked-bar.js.map +1 -1
- package/dist/{class-map-3TAnCMAX.js → class-map-YU7g0o3B.js} +2 -2
- package/dist/{class-map-3TAnCMAX.js.map → class-map-YU7g0o3B.js.map} +1 -1
- package/dist/clock.js.map +1 -1
- package/dist/code-editor.js +4 -4
- package/dist/code-editor.js.map +1 -1
- package/dist/code-highlighter.js +5 -4
- package/dist/code-highlighter.js.map +1 -1
- package/dist/custom-elements-jsdocs.json +6627 -3477
- package/dist/custom-elements.json +10954 -7810
- package/dist/directive-ZPhl09Yt.js +9 -0
- package/dist/directive-ZPhl09Yt.js.map +1 -0
- package/dist/dispatch-event-utils-CuEqjlPT.js +127 -0
- package/dist/dispatch-event-utils-CuEqjlPT.js.map +1 -0
- package/dist/fab.js +423 -0
- package/dist/fab.js.map +1 -0
- package/dist/index.js +17 -9
- package/dist/index.js.map +1 -1
- package/dist/{observe-theme-change-DKAIv5BB.js → is-dark-mode-DicqGkCJ.js} +6 -2
- package/dist/is-dark-mode-DicqGkCJ.js.map +1 -0
- package/dist/{select-C3XAzenC.js → navigation-rail-Lxetd5-Z.js} +2426 -898
- package/dist/navigation-rail-Lxetd5-Z.js.map +1 -0
- package/dist/notification.js +418 -0
- package/dist/notification.js.map +1 -0
- package/dist/number-counter.js +2 -2
- package/dist/number-counter.js.map +1 -1
- package/dist/observe-slot-change-BGJfgg2E.js +31 -0
- package/dist/observe-slot-change-BGJfgg2E.js.map +1 -0
- package/dist/peacock-loader.js +48 -13
- package/dist/peacock-loader.js.map +1 -1
- package/dist/search.js +456 -0
- package/dist/search.js.map +1 -0
- package/dist/side-sheet.js +2 -2
- package/dist/src/__mixins/BaseButtonMixin.d.ts +20 -0
- package/dist/src/__mixins/BaseHyperlinkMixin.d.ts +18 -0
- package/dist/src/__mixins/MixinConstructor.d.ts +1 -0
- package/dist/src/__utils/cache-fetch.d.ts +1 -0
- package/dist/src/__utils/is-dark-mode.d.ts +1 -0
- package/dist/src/__utils/is-in-viewport.d.ts +1 -0
- package/dist/src/__utils/observe-slot-change.d.ts +1 -0
- package/dist/src/__utils/sanitize-svg.d.ts +1 -0
- package/dist/src/__utils/throttle.d.ts +4 -0
- package/dist/src/accordion/accordion-item.d.ts +33 -9
- package/dist/src/accordion/accordion.d.ts +21 -5
- package/dist/src/banner/banner.d.ts +43 -0
- package/dist/src/banner/index.d.ts +1 -0
- package/dist/src/button/BaseButton.d.ts +7 -57
- package/dist/src/button/button/button.d.ts +32 -3
- package/dist/src/button/button-group/button-group.d.ts +4 -4
- package/dist/src/button/icon-button/icon-button.d.ts +33 -8
- package/dist/src/card/card.d.ts +4 -15
- package/dist/src/empty-state/empty-state.d.ts +1 -1
- package/dist/src/fab/fab.d.ts +80 -0
- package/dist/src/fab/index.d.ts +1 -0
- package/dist/src/focus-ring/focus-ring.d.ts +11 -5
- package/dist/src/index.d.ts +8 -1
- package/dist/src/link/link.d.ts +3 -10
- package/dist/src/menu/menu/menu.d.ts +3 -2
- package/dist/src/menu/sub-menu/sub-menu.d.ts +1 -0
- package/dist/src/navigation-rail/index.d.ts +2 -0
- package/dist/src/navigation-rail/navigation-rail-item.d.ts +55 -0
- package/dist/src/navigation-rail/navigation-rail.d.ts +71 -0
- package/dist/src/notification/index.d.ts +1 -0
- package/dist/src/notification/notification.d.ts +69 -0
- package/dist/src/pagination/pagination.d.ts +8 -1
- package/dist/src/search/index.d.ts +1 -0
- package/dist/src/search/search.d.ts +76 -0
- package/dist/src/select/select.d.ts +3 -5
- package/dist/src/sidebar-menu/index.d.ts +3 -0
- package/dist/src/sidebar-menu/sidebar-menu-item.d.ts +58 -0
- package/dist/src/sidebar-menu/sidebar-menu.d.ts +38 -0
- package/dist/src/sidebar-menu/sidebar-sub-menu.d.ts +35 -0
- package/dist/src/slider/slider.d.ts +4 -0
- package/dist/src/snackbar/snackbar.d.ts +14 -1
- package/dist/src/toolbar/index.d.ts +1 -0
- package/dist/src/toolbar/toolbar.d.ts +86 -0
- package/dist/src/tooltip/tooltip.d.ts +3 -0
- package/dist/src/url-field/index.d.ts +1 -0
- package/dist/src/url-field/url-field.d.ts +48 -0
- package/dist/{style-map-CRFEoCEg.js → style-map-DVmWOuYy.js} +2 -2
- package/dist/{style-map-CRFEoCEg.js.map → style-map-DVmWOuYy.js.map} +1 -1
- package/dist/test/banner.test.d.ts +1 -0
- package/dist/test/search.test.d.ts +1 -0
- package/dist/test/sidebar-menu.test.d.ts +1 -0
- package/dist/test/toolbar.test.d.ts +1 -0
- package/dist/toolbar.js +306 -0
- package/dist/toolbar.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/{unsafe-html-D3GHRaGQ.js → unsafe-html-BsGUjx94.js} +2 -2
- package/dist/{unsafe-html-D3GHRaGQ.js.map → unsafe-html-BsGUjx94.js.map} +1 -1
- package/package.json +1 -1
- package/readme.md +73 -65
- package/scss/mixin.scss +16 -0
- package/scss/styles.scss +4 -0
- package/src/__mixins/BaseButtonMixin.ts +83 -0
- package/src/__mixins/BaseHyperlinkMixin.ts +68 -0
- package/src/__mixins/MixinConstructor.ts +1 -0
- package/src/__mixins/README.md +19 -0
- package/src/__utils/cache-fetch.ts +65 -0
- package/src/__utils/is-dark-mode.ts +3 -0
- package/src/__utils/is-in-viewport.ts +6 -0
- package/src/__utils/observe-slot-change.ts +38 -0
- package/src/__utils/sanitize-svg.ts +27 -0
- package/src/__utils/throttle.ts +27 -0
- package/src/accordion/accordion-item.scss +136 -65
- package/src/accordion/accordion-item.ts +117 -44
- package/src/accordion/accordion.scss +24 -5
- package/src/accordion/accordion.ts +29 -23
- package/src/accordion/demo/index.html +74 -35
- package/src/banner/banner.scss +83 -0
- package/src/banner/banner.ts +101 -0
- package/src/banner/index.ts +1 -0
- package/src/button/BaseButton.ts +13 -115
- package/src/button/button/button-colors.scss +14 -14
- package/src/button/button/button-sizes.scss +4 -2
- package/src/button/button/button.ts +80 -26
- package/src/button/button-group/button-group.ts +5 -5
- package/src/button/icon-button/icon-button.ts +79 -44
- package/src/card/card.ts +50 -100
- package/src/chart-bar/chart-bar.ts +10 -15
- package/src/chart-bar/chart-stacked-bar.ts +15 -19
- package/src/chart-doughnut/chart-doughnut.ts +24 -28
- package/src/chart-pie/chart-pie.ts +20 -24
- package/src/checkbox/checkbox.scss +17 -34
- package/src/checkbox/checkbox.ts +4 -2
- package/src/clock/clock.ts +1 -1
- package/src/code-editor/code-editor.ts +4 -4
- package/src/code-highlighter/code-highlighter.scss +1 -0
- package/src/code-highlighter/code-highlighter.ts +3 -3
- package/src/date-picker/date-picker.ts +6 -3
- package/src/divider/divider.ts +3 -1
- package/src/elevation/elevation.scss +5 -5
- package/src/empty-state/empty-state.scss +7 -9
- package/src/empty-state/empty-state.ts +1 -1
- package/src/fab/fab-colors.scss +49 -0
- package/src/fab/fab-sizes.scss +47 -0
- package/src/fab/fab.scss +137 -0
- package/src/fab/fab.ts +214 -0
- package/src/fab/index.ts +1 -0
- package/src/field/field.ts +3 -1
- package/src/focus-ring/focus-ring.ts +47 -40
- package/src/icon/datasource.ts +1 -1
- package/src/icon/icon.ts +3 -1
- package/src/image/image.ts +3 -2
- package/src/index.ts +8 -1
- package/src/input/input.ts +8 -3
- package/src/link/link.ts +2 -15
- package/src/menu/menu/menu.scss +7 -0
- package/src/menu/menu/menu.ts +7 -4
- package/src/menu/menu-item/menu-item.ts +3 -1
- package/src/menu/sub-menu/sub-menu.ts +1 -0
- package/src/navigation-rail/index.ts +2 -0
- package/src/navigation-rail/navigation-rail-item.scss +216 -0
- package/src/navigation-rail/navigation-rail-item.ts +223 -0
- package/src/navigation-rail/navigation-rail.scss +72 -0
- package/src/navigation-rail/navigation-rail.ts +149 -0
- package/src/notification/index.ts +1 -0
- package/src/notification/notification.scss +201 -0
- package/src/notification/notification.ts +207 -0
- package/src/number-counter/number-counter.ts +3 -1
- package/src/number-field/number-field.ts +10 -6
- package/src/pagination/pagination.scss +33 -24
- package/src/pagination/pagination.ts +115 -60
- package/src/peacock-loader.ts +42 -5
- package/src/radio/radio.ts +3 -1
- package/src/search/index.ts +1 -0
- package/src/search/search-colors.scss +14 -0
- package/src/search/search.scss +204 -0
- package/src/search/search.ts +244 -0
- package/src/select/option.ts +1 -1
- package/src/select/select.scss +5 -0
- package/src/select/select.ts +71 -37
- package/src/sidebar-menu/demo/index.html +68 -0
- package/src/sidebar-menu/index.ts +3 -0
- package/src/sidebar-menu/sidebar-menu-item.scss +102 -0
- package/src/sidebar-menu/sidebar-menu-item.ts +151 -0
- package/src/{tree-view/tree-view.scss → sidebar-menu/sidebar-menu.scss} +1 -1
- package/src/sidebar-menu/sidebar-menu.ts +182 -0
- package/src/sidebar-menu/sidebar-sub-menu.scss +130 -0
- package/src/sidebar-menu/sidebar-sub-menu.ts +160 -0
- package/src/skeleton/skeleton.scss +18 -24
- package/src/slider/slider.scss +19 -0
- package/src/slider/slider.ts +30 -19
- package/src/snackbar/snackbar.scss +62 -31
- package/src/snackbar/snackbar.ts +91 -11
- package/src/switch/switch.ts +3 -1
- package/src/table/table.ts +3 -1
- package/src/tabs/tab.ts +10 -6
- package/src/text/text.css-component.scss +7 -1
- package/src/textarea/textarea.ts +4 -2
- package/src/time-picker/time-picker.ts +5 -3
- package/src/toolbar/index.ts +1 -0
- package/src/toolbar/toolbar-colors.scss +16 -0
- package/src/toolbar/toolbar.scss +165 -0
- package/src/toolbar/toolbar.ts +137 -0
- package/src/tooltip/tooltip.ts +24 -0
- package/src/url-field/index.ts +1 -0
- package/src/url-field/url-field.scss +50 -0
- package/src/url-field/url-field.ts +239 -0
- package/dist/button-COYCtuA8.js.map +0 -1
- package/dist/button-group-DsXquZQn.js.map +0 -1
- package/dist/directive-Cuw6h7YA.js +0 -9
- package/dist/directive-Cuw6h7YA.js.map +0 -1
- package/dist/dispatch-event-utils-B4odODQf.js +0 -277
- package/dist/dispatch-event-utils-B4odODQf.js.map +0 -1
- package/dist/observe-theme-change-DKAIv5BB.js.map +0 -1
- package/dist/select-C3XAzenC.js.map +0 -1
- package/dist/src/styleMixins.css.d.ts +0 -9
- package/dist/src/tree-view/index.d.ts +0 -2
- package/dist/src/tree-view/tree-node.d.ts +0 -69
- package/dist/src/tree-view/tree-view.d.ts +0 -40
- package/dist/src/tree-view/wc-tree-view.d.ts +0 -6
- package/dist/src/utils.d.ts +0 -9
- package/dist/test/tree-view.test.d.ts +0 -1
- package/src/styleMixins.css.ts +0 -55
- package/src/tree-view/demo/index.html +0 -57
- package/src/tree-view/index.ts +0 -2
- package/src/tree-view/tree-node.scss +0 -101
- package/src/tree-view/tree-node.ts +0 -268
- package/src/tree-view/tree-view.ts +0 -182
- package/src/tree-view/wc-tree-view.ts +0 -9
- package/src/utils.ts +0 -193
- /package/dist/src/{spread.d.ts → __directive/spread.d.ts} +0 -0
- /package/dist/src/{utils → __utils}/copy-to-clipboard.d.ts +0 -0
- /package/dist/src/{utils → __utils}/dispatch-event-utils.d.ts +0 -0
- /package/dist/src/{utils → __utils}/observe-theme-change.d.ts +0 -0
- /package/src/{spread.ts → __directive/spread.ts} +0 -0
- /package/src/{utils → __utils}/copy-to-clipboard.ts +0 -0
- /package/src/{utils → __utils}/dispatch-event-utils.ts +0 -0
- /package/src/{utils → __utils}/observe-theme-change.ts +0 -0
|
@@ -1,111 +1,182 @@
|
|
|
1
1
|
@use '../../scss/mixin';
|
|
2
2
|
|
|
3
|
-
|
|
4
3
|
@include mixin.base-styles;
|
|
5
4
|
|
|
5
|
+
// ─── Host ────────────────────────────────────────────────────────────────────
|
|
6
|
+
|
|
6
7
|
:host {
|
|
7
8
|
display: block;
|
|
8
|
-
|
|
9
|
-
--accordion-item-title-align: start;
|
|
10
9
|
}
|
|
11
10
|
|
|
12
|
-
|
|
11
|
+
// ─── Expansion Panel ─────────────────────────────────────────────────────────
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
background
|
|
13
|
+
.expansion-panel {
|
|
14
|
+
border: var(--_accordion-item-border, 1px solid var(--color-outline-variant));
|
|
15
|
+
border-radius: var(--shape-corner-medium); // 12dp — M3 medium shape
|
|
16
|
+
background-color: var(--_accordion-item-background, var(--color-surface-container-low));
|
|
17
|
+
overflow: hidden; // clip children to border-radius
|
|
18
|
+
transition:
|
|
19
|
+
background-color var(--duration-medium1) var(--easing-standard),
|
|
20
|
+
border-color var(--duration-medium1) var(--easing-standard);
|
|
21
|
+
|
|
22
|
+
// ─── Header Button ─────────────────────────────────────────────────────────
|
|
21
23
|
|
|
24
|
+
.header-button {
|
|
25
|
+
position: relative;
|
|
22
26
|
display: flex;
|
|
23
|
-
flex-direction: row-reverse;
|
|
24
27
|
align-items: center;
|
|
28
|
+
width: 100%;
|
|
29
|
+
min-height: 3rem; // 48dp — grows with description
|
|
30
|
+
padding: 0 var(--spacing-300); // 0 24dp
|
|
31
|
+
gap: var(--spacing-200); // 16dp between label and icon
|
|
32
|
+
border: none;
|
|
33
|
+
border-radius: 0;
|
|
34
|
+
background: transparent;
|
|
35
|
+
cursor: pointer;
|
|
36
|
+
text-align: start;
|
|
25
37
|
color: var(--color-on-surface);
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
38
|
+
overflow: hidden;
|
|
39
|
+
|
|
40
|
+
// M3 state layer
|
|
41
|
+
&::before {
|
|
42
|
+
content: '';
|
|
43
|
+
position: absolute;
|
|
44
|
+
inset: 0;
|
|
45
|
+
background-color: var(--color-on-surface);
|
|
46
|
+
opacity: 0;
|
|
47
|
+
pointer-events: none;
|
|
48
|
+
transition: opacity var(--duration-short4) var(--easing-standard);
|
|
49
|
+
}
|
|
29
50
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
text-align: var(--accordion-item-title-align);
|
|
51
|
+
&:not(:disabled):hover::before {
|
|
52
|
+
opacity: 0.08;
|
|
33
53
|
}
|
|
34
54
|
|
|
35
55
|
&:focus-visible {
|
|
56
|
+
outline: none;
|
|
36
57
|
@include mixin.focus-ring();
|
|
37
58
|
}
|
|
38
59
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
--icon-color: currentColor;
|
|
42
|
-
transition: transform var(--duration-short2) var(--easing-standard);
|
|
60
|
+
&:focus-visible::before {
|
|
61
|
+
opacity: 0.12;
|
|
43
62
|
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ─── Header Label (title + description column) ──────────────────────────────
|
|
44
66
|
|
|
67
|
+
.header-label {
|
|
68
|
+
flex: 1;
|
|
69
|
+
display: flex;
|
|
70
|
+
flex-direction: column;
|
|
71
|
+
justify-content: center;
|
|
72
|
+
gap: 2px;
|
|
73
|
+
padding: var(--spacing-150) 0; // 12dp vertical padding
|
|
74
|
+
min-width: 0;
|
|
45
75
|
}
|
|
46
76
|
|
|
47
|
-
.
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
77
|
+
.panel-title {
|
|
78
|
+
@include mixin.get-typography(body-large);
|
|
79
|
+
color: var(--color-on-surface);
|
|
80
|
+
white-space: nowrap;
|
|
81
|
+
overflow: hidden;
|
|
82
|
+
text-overflow: ellipsis;
|
|
53
83
|
}
|
|
54
84
|
|
|
55
|
-
|
|
56
|
-
|
|
85
|
+
.panel-description {
|
|
86
|
+
@include mixin.get-typography(body-small);
|
|
87
|
+
color: var(--color-on-surface-variant);
|
|
88
|
+
white-space: nowrap;
|
|
89
|
+
overflow: hidden;
|
|
90
|
+
text-overflow: ellipsis;
|
|
91
|
+
|
|
92
|
+
&[hidden] {
|
|
93
|
+
display: none;
|
|
94
|
+
}
|
|
57
95
|
}
|
|
58
96
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
97
|
+
// ─── Header Actions slot ────────────────────────────────────────────────────
|
|
98
|
+
|
|
99
|
+
::slotted([slot='header-actions']) {
|
|
100
|
+
display: flex;
|
|
101
|
+
align-items: center;
|
|
102
|
+
flex-shrink: 0;
|
|
103
|
+
gap: var(--spacing-100);
|
|
62
104
|
}
|
|
63
105
|
|
|
64
|
-
|
|
65
|
-
.item-section {
|
|
66
|
-
height: 100%;
|
|
67
|
-
pointer-events: auto;
|
|
68
|
-
opacity: 1;
|
|
69
|
-
padding: var(--spacing-100) var(--spacing-800) var(--spacing-300) var(--spacing-200);
|
|
70
|
-
}
|
|
106
|
+
// ─── Toggle Icon ────────────────────────────────────────────────────────────
|
|
71
107
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
108
|
+
.toggle-icon {
|
|
109
|
+
--icon-size: 1.5rem;
|
|
110
|
+
--icon-color: var(--color-on-surface-variant);
|
|
111
|
+
flex-shrink: 0;
|
|
112
|
+
transition: transform var(--duration-medium1) var(--easing-standard);
|
|
75
113
|
}
|
|
76
114
|
|
|
77
|
-
|
|
115
|
+
// ─── Expandable Content ─────────────────────────────────────────────────────
|
|
78
116
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
117
|
+
.panel-content {
|
|
118
|
+
display: grid;
|
|
119
|
+
grid-template-rows: 0fr;
|
|
120
|
+
transition: grid-template-rows var(--duration-medium1) var(--easing-standard);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.content-inner {
|
|
124
|
+
overflow: hidden;
|
|
125
|
+
min-height: 0;
|
|
126
|
+
@include mixin.get-typography(body-medium);
|
|
127
|
+
color: var(--color-on-surface-variant);
|
|
85
128
|
}
|
|
86
|
-
}
|
|
87
129
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
130
|
+
// ─── Disabled State ─────────────────────────────────────────────────────────
|
|
131
|
+
|
|
132
|
+
&.disabled {
|
|
133
|
+
background-color: color-mix(in srgb, var(--color-surface-container-low) 38%, transparent);
|
|
134
|
+
border-color: color-mix(in srgb, var(--color-outline-variant) 38%, transparent);
|
|
135
|
+
|
|
136
|
+
.header-button {
|
|
137
|
+
cursor: not-allowed;
|
|
138
|
+
pointer-events: none;
|
|
139
|
+
opacity: 0.38;
|
|
92
140
|
}
|
|
93
141
|
}
|
|
94
|
-
}
|
|
95
142
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
143
|
+
// ─── Expanded / Open State (must come after disabled so .open wins) ─────────
|
|
144
|
+
|
|
145
|
+
&.open {
|
|
146
|
+
background-color: var(--color-surface-container);
|
|
147
|
+
border-color: transparent; // border fades out when expanded, matching M3
|
|
148
|
+
|
|
149
|
+
.panel-content {
|
|
150
|
+
grid-template-rows: 1fr;
|
|
151
|
+
border-block-start: 1px solid var(--color-outline-variant); // header–content divider
|
|
100
152
|
}
|
|
153
|
+
|
|
154
|
+
.content-inner {
|
|
155
|
+
padding: 0 var(--spacing-300) var(--spacing-200); // 0 24dp 16dp
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.toggle-icon {
|
|
159
|
+
transform: rotate(180deg);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// ─── Disabled State (border + bg dimmed) ────────────────────────────────────
|
|
164
|
+
|
|
165
|
+
&.disabled {
|
|
166
|
+
background-color: color-mix(in srgb, var(--color-surface-container-low) 38%, transparent);
|
|
167
|
+
border-color: color-mix(in srgb, var(--color-outline-variant) 38%, transparent);
|
|
101
168
|
}
|
|
102
169
|
}
|
|
103
170
|
|
|
171
|
+
// ─── Toggle position: before ─────────────────────────────────────────────────
|
|
104
172
|
|
|
105
|
-
:host
|
|
106
|
-
.
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
173
|
+
:host([toggle-position='before']) {
|
|
174
|
+
.header-button {
|
|
175
|
+
flex-direction: row-reverse;
|
|
176
|
+
justify-content: flex-end;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.header-label {
|
|
180
|
+
flex: 1;
|
|
110
181
|
}
|
|
111
182
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { html, LitElement } from 'lit';
|
|
2
|
-
import { property, query } from 'lit/decorators.js';
|
|
1
|
+
import { html, LitElement, nothing } from 'lit';
|
|
2
|
+
import { property, query, state } from 'lit/decorators.js';
|
|
3
3
|
import { classMap } from 'lit/directives/class-map.js';
|
|
4
4
|
import styles from './accordion-item.scss';
|
|
5
5
|
|
|
@@ -7,13 +7,27 @@ import styles from './accordion-item.scss';
|
|
|
7
7
|
* @label Accordion Item
|
|
8
8
|
* @tag wc-accordion-item
|
|
9
9
|
* @rawTag accordion-item
|
|
10
|
-
* @summary An
|
|
10
|
+
* @summary An expansion panel with a header that reveals or hides associated content. Follows Material Design 3 expansion panel guidelines.
|
|
11
11
|
* @parentRawTag accordion
|
|
12
|
-
*
|
|
12
|
+
*
|
|
13
|
+
* @slot - The body content revealed when the panel is expanded.
|
|
14
|
+
* @slot heading - The panel title. Renders as `body-large` text.
|
|
15
|
+
* @slot description - Optional subtitle rendered below the title. Renders as `body-small` text.
|
|
16
|
+
* @slot header-actions - Actions (e.g. icon buttons) placed at the trailing end of the header, before the toggle icon.
|
|
17
|
+
*
|
|
18
|
+
* @part header - The header `<button>` element.
|
|
19
|
+
* @part title - The title text container.
|
|
20
|
+
* @part description - The description text container.
|
|
21
|
+
* @part content - The expandable content region wrapper.
|
|
22
|
+
*
|
|
23
|
+
* @fires {CustomEvent<{ open: boolean }>} accordion-item:toggle - Fired when the panel is expanded or collapsed.
|
|
24
|
+
*
|
|
13
25
|
* @example
|
|
14
26
|
* ```html
|
|
15
27
|
* <wc-accordion-item>
|
|
16
|
-
*
|
|
28
|
+
* <span slot="heading">Personal information</span>
|
|
29
|
+
* <span slot="description">Fill in your details</span>
|
|
30
|
+
* <p>Content goes here.</p>
|
|
17
31
|
* </wc-accordion-item>
|
|
18
32
|
* ```
|
|
19
33
|
* @tags display
|
|
@@ -24,24 +38,38 @@ export class AccordionItem extends LitElement {
|
|
|
24
38
|
#id = crypto.randomUUID();
|
|
25
39
|
|
|
26
40
|
/**
|
|
27
|
-
*
|
|
28
|
-
*/
|
|
29
|
-
@property({ type: String, reflect: true })
|
|
30
|
-
heading: string = '';
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* If true, the user cannot interact with the button. Defaults to `false`.
|
|
41
|
+
* Whether the user cannot interact with the panel.
|
|
34
42
|
*/
|
|
35
43
|
@property({ type: Boolean, reflect: true })
|
|
36
44
|
disabled: boolean = false;
|
|
37
45
|
|
|
38
46
|
/**
|
|
39
|
-
*
|
|
47
|
+
* Whether the panel is expanded.
|
|
40
48
|
*/
|
|
41
49
|
@property({ type: Boolean, reflect: true })
|
|
42
50
|
open: boolean = false;
|
|
43
51
|
|
|
44
|
-
|
|
52
|
+
/**
|
|
53
|
+
* Whether to hide the expand/collapse toggle indicator icon.
|
|
54
|
+
*/
|
|
55
|
+
@property({ type: Boolean, reflect: true, attribute: 'hide-toggle' })
|
|
56
|
+
hideToggle: boolean = false;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Position of the toggle icon relative to the panel title.
|
|
60
|
+
* `'after'` places it at the trailing end (default, matches M3).
|
|
61
|
+
* `'before'` places it at the leading start.
|
|
62
|
+
*/
|
|
63
|
+
@property({ type: String, reflect: true, attribute: 'toggle-position' })
|
|
64
|
+
togglePosition: 'before' | 'after' = 'after';
|
|
65
|
+
|
|
66
|
+
@state()
|
|
67
|
+
private _hasDescriptionSlot = false;
|
|
68
|
+
|
|
69
|
+
@state()
|
|
70
|
+
private _hasHeadingSlot = false;
|
|
71
|
+
|
|
72
|
+
@query('.header-button')
|
|
45
73
|
private readonly buttonElement!: HTMLElement | null;
|
|
46
74
|
|
|
47
75
|
override focus() {
|
|
@@ -52,48 +80,93 @@ export class AccordionItem extends LitElement {
|
|
|
52
80
|
this.buttonElement?.blur();
|
|
53
81
|
}
|
|
54
82
|
|
|
55
|
-
private
|
|
83
|
+
private _handleToggle() {
|
|
56
84
|
if (this.disabled) return;
|
|
57
85
|
this.open = !this.open;
|
|
58
86
|
this.dispatchEvent(
|
|
59
|
-
new CustomEvent('accordion-item
|
|
87
|
+
new CustomEvent('accordion-item:toggle', {
|
|
60
88
|
bubbles: true,
|
|
61
89
|
composed: true,
|
|
62
|
-
detail: { open: this.open
|
|
90
|
+
detail: { open: this.open },
|
|
63
91
|
}),
|
|
64
92
|
);
|
|
65
93
|
}
|
|
66
94
|
|
|
95
|
+
private static _onSlotChange(e: Event, setter: (v: boolean) => void) {
|
|
96
|
+
const slot = e.target as HTMLSlotElement;
|
|
97
|
+
const nodes = slot.assignedNodes({ flatten: true });
|
|
98
|
+
setter(
|
|
99
|
+
nodes.some(n =>
|
|
100
|
+
n.nodeType === Node.TEXT_NODE
|
|
101
|
+
? (n.textContent?.trim() ?? '') !== ''
|
|
102
|
+
: true,
|
|
103
|
+
),
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
private _renderToggleIcon() {
|
|
108
|
+
if (this.hideToggle) return nothing;
|
|
109
|
+
return html`<wc-icon
|
|
110
|
+
class="toggle-icon"
|
|
111
|
+
name="keyboard_arrow_down"
|
|
112
|
+
aria-hidden="true"
|
|
113
|
+
></wc-icon>`;
|
|
114
|
+
}
|
|
115
|
+
|
|
67
116
|
render() {
|
|
68
|
-
return html
|
|
69
|
-
class=${classMap({
|
|
70
|
-
'accordion-item': true,
|
|
71
|
-
open: this.open,
|
|
72
|
-
disabled: this.disabled,
|
|
73
|
-
})}
|
|
74
|
-
>
|
|
75
|
-
<button
|
|
76
|
-
id=${`accordion-heading-${this.#id}`}
|
|
77
|
-
tabindex="0"
|
|
78
|
-
aria-controls=${`accordion-control-${this.#id}`}
|
|
79
|
-
class="accordion-heading"
|
|
80
|
-
aria-disabled=${this.disabled}
|
|
81
|
-
@click=${this.__handleToggle}
|
|
82
|
-
aria-expanded=${this.open}
|
|
83
|
-
>
|
|
84
|
-
<wc-icon class="accordion-icon" name="keyboard_arrow_down"></wc-icon>
|
|
85
|
-
<div part="title" class="accordion-title">
|
|
86
|
-
<slot name="heading">${this.heading}</slot>
|
|
87
|
-
</div>
|
|
88
|
-
</button>
|
|
117
|
+
return html`
|
|
89
118
|
<div
|
|
90
|
-
class
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
119
|
+
class=${classMap({
|
|
120
|
+
'expansion-panel': true,
|
|
121
|
+
open: this.open,
|
|
122
|
+
disabled: this.disabled,
|
|
123
|
+
})}
|
|
94
124
|
>
|
|
95
|
-
<
|
|
125
|
+
<button
|
|
126
|
+
id=${`panel-header-${this.#id}`}
|
|
127
|
+
part="header"
|
|
128
|
+
class="header-button"
|
|
129
|
+
tabindex=${this.disabled ? '-1' : '0'}
|
|
130
|
+
aria-controls=${`panel-content-${this.#id}`}
|
|
131
|
+
aria-disabled=${this.disabled}
|
|
132
|
+
aria-expanded=${this.open}
|
|
133
|
+
?disabled=${this.disabled}
|
|
134
|
+
@click=${this._handleToggle}
|
|
135
|
+
>
|
|
136
|
+
${this.togglePosition === 'before' ? this._renderToggleIcon() : nothing}
|
|
137
|
+
<span class="header-label">
|
|
138
|
+
<span part="title" class="panel-title">
|
|
139
|
+
<slot
|
|
140
|
+
name="heading"
|
|
141
|
+
@slotchange=${(e: Event) => AccordionItem._onSlotChange(e, v => { this._hasHeadingSlot = v; })}
|
|
142
|
+
></slot>
|
|
143
|
+
</span>
|
|
144
|
+
<span
|
|
145
|
+
part="description"
|
|
146
|
+
class="panel-description"
|
|
147
|
+
?hidden=${!this._hasDescriptionSlot}
|
|
148
|
+
>
|
|
149
|
+
<slot
|
|
150
|
+
name="description"
|
|
151
|
+
@slotchange=${(e: Event) => AccordionItem._onSlotChange(e, v => { this._hasDescriptionSlot = v; })}
|
|
152
|
+
></slot>
|
|
153
|
+
</span>
|
|
154
|
+
</span>
|
|
155
|
+
<slot name="header-actions" class="header-actions"></slot>
|
|
156
|
+
${this.togglePosition === 'after' ? this._renderToggleIcon() : nothing}
|
|
157
|
+
</button>
|
|
158
|
+
<div
|
|
159
|
+
id=${`panel-content-${this.#id}`}
|
|
160
|
+
part="content"
|
|
161
|
+
class="panel-content"
|
|
162
|
+
role="region"
|
|
163
|
+
aria-labelledby=${`panel-header-${this.#id}`}
|
|
164
|
+
>
|
|
165
|
+
<div class="content-inner">
|
|
166
|
+
<slot></slot>
|
|
167
|
+
</div>
|
|
168
|
+
</div>
|
|
96
169
|
</div>
|
|
97
|
-
|
|
170
|
+
`;
|
|
98
171
|
}
|
|
99
172
|
}
|
|
@@ -2,15 +2,34 @@
|
|
|
2
2
|
|
|
3
3
|
@include mixin.base-styles;
|
|
4
4
|
|
|
5
|
+
// ─── Host ────────────────────────────────────────────────────────────────────
|
|
6
|
+
|
|
5
7
|
:host {
|
|
6
8
|
display: block;
|
|
7
9
|
}
|
|
8
10
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
+
// ─── Default display mode ────────────────────────────────────────────────────
|
|
12
|
+
// Each item is a fully-bordered rounded container (M3 expansion panel shape).
|
|
13
|
+
// Items are stacked with a gap; no shared dividers.
|
|
14
|
+
|
|
15
|
+
.accordion {
|
|
16
|
+
display: flex;
|
|
17
|
+
flex-direction: column;
|
|
18
|
+
gap: var(--spacing-100); // 8dp gap between panels
|
|
11
19
|
}
|
|
12
20
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
21
|
+
// ─── Flat display mode ───────────────────────────────────────────────────────
|
|
22
|
+
// Removes the gap and the per-item border/background — for use inside cards.
|
|
23
|
+
|
|
24
|
+
:host([display-mode='flat']) {
|
|
25
|
+
.accordion {
|
|
26
|
+
gap: 0;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Push overrides into each item via inherited CSS custom properties.
|
|
30
|
+
// wc-accordion-item reads --_accordion-item-border / --_accordion-item-background.
|
|
31
|
+
slot::slotted(wc-accordion-item) {
|
|
32
|
+
--_accordion-item-border: none;
|
|
33
|
+
--_accordion-item-background: transparent;
|
|
34
|
+
}
|
|
16
35
|
}
|
|
@@ -7,14 +7,20 @@ import { AccordionItem } from './accordion-item.js';
|
|
|
7
7
|
* @label Accordion
|
|
8
8
|
* @tag wc-accordion
|
|
9
9
|
* @rawTag accordion
|
|
10
|
-
* @summary
|
|
10
|
+
* @summary A vertically stacked set of expansion panels. Follows Material Design 3 expansion panel guidelines.
|
|
11
11
|
*
|
|
12
12
|
* @example
|
|
13
13
|
* ```html
|
|
14
14
|
* <wc-accordion>
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* </
|
|
15
|
+
* <wc-accordion-item>
|
|
16
|
+
* <span slot="heading">Panel 1</span>
|
|
17
|
+
* <span slot="description">Summary text</span>
|
|
18
|
+
* Content
|
|
19
|
+
* </wc-accordion-item>
|
|
20
|
+
* <wc-accordion-item>
|
|
21
|
+
* <span slot="heading">Panel 2</span>
|
|
22
|
+
* Content
|
|
23
|
+
* </wc-accordion-item>
|
|
18
24
|
* </wc-accordion>
|
|
19
25
|
* ```
|
|
20
26
|
* @tags display
|
|
@@ -22,10 +28,22 @@ import { AccordionItem } from './accordion-item.js';
|
|
|
22
28
|
export class Accordion extends LitElement {
|
|
23
29
|
static styles = [styles];
|
|
24
30
|
|
|
25
|
-
|
|
26
|
-
|
|
31
|
+
/**
|
|
32
|
+
* Whether multiple panels can be expanded simultaneously.
|
|
33
|
+
* When `false` (default), expanding one panel collapses all others.
|
|
34
|
+
*/
|
|
35
|
+
@property({ type: Boolean, reflect: true })
|
|
36
|
+
multi = false;
|
|
27
37
|
|
|
28
|
-
|
|
38
|
+
/**
|
|
39
|
+
* Display mode for the accordion.
|
|
40
|
+
* `'default'` renders panels with a subtle background on expand and dividers between items.
|
|
41
|
+
* `'flat'` renders panels without borders or background changes — suitable for use inside cards.
|
|
42
|
+
*/
|
|
43
|
+
@property({ type: String, reflect: true, attribute: 'display-mode' })
|
|
44
|
+
displayMode: 'default' | 'flat' = 'default';
|
|
45
|
+
|
|
46
|
+
@queryAssignedElements({ selector: 'wc-accordion-item' })
|
|
29
47
|
items!: Array<AccordionItem>;
|
|
30
48
|
|
|
31
49
|
connectedCallback() {
|
|
@@ -39,7 +57,6 @@ export class Accordion extends LitElement {
|
|
|
39
57
|
disconnectedCallback() {
|
|
40
58
|
super.disconnectedCallback();
|
|
41
59
|
// @ts-ignore
|
|
42
|
-
// eslint-disable-next-line no-undef
|
|
43
60
|
this.removeEventListener('accordion-item:toggle', this._onItemToggle);
|
|
44
61
|
this.removeEventListener('keydown', this._onKeyDown);
|
|
45
62
|
}
|
|
@@ -47,11 +64,10 @@ export class Accordion extends LitElement {
|
|
|
47
64
|
private _onItemToggle(e: CustomEvent) {
|
|
48
65
|
const targetItem = e.target as AccordionItem;
|
|
49
66
|
|
|
50
|
-
//
|
|
51
|
-
// We check if the target item is a direct child of *this* accordion
|
|
67
|
+
// Ignore events from nested accordions — only handle direct children
|
|
52
68
|
if (targetItem.parentElement !== this) return;
|
|
53
69
|
|
|
54
|
-
if (!this.
|
|
70
|
+
if (!this.multi && targetItem.open) {
|
|
55
71
|
this.items.forEach(item => {
|
|
56
72
|
if (item !== targetItem && item.open) {
|
|
57
73
|
// eslint-disable-next-line no-param-reassign
|
|
@@ -62,17 +78,11 @@ export class Accordion extends LitElement {
|
|
|
62
78
|
}
|
|
63
79
|
|
|
64
80
|
private _onKeyDown(e: KeyboardEvent) {
|
|
65
|
-
// 1. Find which item currently has its HEADER focused.
|
|
66
|
-
// We check the shadowRoot of each item to see if the internal <button> is the active element.
|
|
67
81
|
const focusedItemIndex = this.items.findIndex(item => {
|
|
68
|
-
// Access the Shadow DOM of the item
|
|
69
82
|
const root = item.shadowRoot;
|
|
70
|
-
|
|
71
|
-
return root?.activeElement?.classList.contains('accordion-heading');
|
|
83
|
+
return root?.activeElement?.classList.contains('header-button');
|
|
72
84
|
});
|
|
73
85
|
|
|
74
|
-
// 2. If no header is focused (e.g., focus is on body content or outside), do nothing.
|
|
75
|
-
// This prevents stealing focus when the user is typing in a form inside the accordion.
|
|
76
86
|
if (focusedItemIndex === -1) return;
|
|
77
87
|
|
|
78
88
|
let nextIndex = -1;
|
|
@@ -81,12 +91,10 @@ export class Accordion extends LitElement {
|
|
|
81
91
|
switch (e.key) {
|
|
82
92
|
case 'ArrowDown':
|
|
83
93
|
e.preventDefault();
|
|
84
|
-
// Cycle next
|
|
85
94
|
nextIndex = (focusedItemIndex + 1) % this.items.length;
|
|
86
95
|
break;
|
|
87
96
|
case 'ArrowUp':
|
|
88
97
|
e.preventDefault();
|
|
89
|
-
// Cycle previous
|
|
90
98
|
nextIndex =
|
|
91
99
|
(focusedItemIndex - 1 + this.items.length) % this.items.length;
|
|
92
100
|
break;
|
|
@@ -100,12 +108,10 @@ export class Accordion extends LitElement {
|
|
|
100
108
|
break;
|
|
101
109
|
}
|
|
102
110
|
|
|
103
|
-
// 3. Apply Focus
|
|
104
111
|
if (nextIndex !== -1) {
|
|
105
112
|
const itemToFocus = this.items[nextIndex];
|
|
106
|
-
// Select the button inside the Shadow DOM of the target item
|
|
107
113
|
const button = itemToFocus.shadowRoot?.querySelector(
|
|
108
|
-
'.
|
|
114
|
+
'.header-button',
|
|
109
115
|
) as HTMLElement;
|
|
110
116
|
button?.focus();
|
|
111
117
|
}
|