@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.
Files changed (258) hide show
  1. package/dist/BaseButton-DuASuVth.js +219 -0
  2. package/dist/BaseButton-DuASuVth.js.map +1 -0
  3. package/dist/BaseHyperlinkMixin-BNuwbiEf.js +65 -0
  4. package/dist/BaseHyperlinkMixin-BNuwbiEf.js.map +1 -0
  5. package/dist/assets/components.css +1 -1
  6. package/dist/assets/components.css.map +1 -1
  7. package/dist/assets/styles.css +1 -1
  8. package/dist/assets/styles.css.map +1 -1
  9. package/dist/banner.js +187 -0
  10. package/dist/banner.js.map +1 -0
  11. package/dist/bottom-sheet.js +2 -2
  12. package/dist/{button-COYCtuA8.js → button-DouvOfEU.js} +92 -283
  13. package/dist/button-DouvOfEU.js.map +1 -0
  14. package/dist/{button-group-DsXquZQn.js → button-group-CEdMwvJJ.js} +72 -48
  15. package/dist/button-group-CEdMwvJJ.js.map +1 -0
  16. package/dist/button-group.js +8 -5
  17. package/dist/button-group.js.map +1 -1
  18. package/dist/button.js +7 -4
  19. package/dist/button.js.map +1 -1
  20. package/dist/card.js +29 -74
  21. package/dist/card.js.map +1 -1
  22. package/dist/chart-bar.js +2 -2
  23. package/dist/chart-bar.js.map +1 -1
  24. package/dist/chart-doughnut.js +2 -2
  25. package/dist/chart-doughnut.js.map +1 -1
  26. package/dist/chart-pie.js +2 -2
  27. package/dist/chart-pie.js.map +1 -1
  28. package/dist/chart-stacked-bar.js +2 -2
  29. package/dist/chart-stacked-bar.js.map +1 -1
  30. package/dist/{class-map-3TAnCMAX.js → class-map-YU7g0o3B.js} +2 -2
  31. package/dist/{class-map-3TAnCMAX.js.map → class-map-YU7g0o3B.js.map} +1 -1
  32. package/dist/clock.js.map +1 -1
  33. package/dist/code-editor.js +4 -4
  34. package/dist/code-editor.js.map +1 -1
  35. package/dist/code-highlighter.js +5 -4
  36. package/dist/code-highlighter.js.map +1 -1
  37. package/dist/custom-elements-jsdocs.json +6627 -3477
  38. package/dist/custom-elements.json +10954 -7810
  39. package/dist/directive-ZPhl09Yt.js +9 -0
  40. package/dist/directive-ZPhl09Yt.js.map +1 -0
  41. package/dist/dispatch-event-utils-CuEqjlPT.js +127 -0
  42. package/dist/dispatch-event-utils-CuEqjlPT.js.map +1 -0
  43. package/dist/fab.js +423 -0
  44. package/dist/fab.js.map +1 -0
  45. package/dist/index.js +17 -9
  46. package/dist/index.js.map +1 -1
  47. package/dist/{observe-theme-change-DKAIv5BB.js → is-dark-mode-DicqGkCJ.js} +6 -2
  48. package/dist/is-dark-mode-DicqGkCJ.js.map +1 -0
  49. package/dist/{select-C3XAzenC.js → navigation-rail-Lxetd5-Z.js} +2426 -898
  50. package/dist/navigation-rail-Lxetd5-Z.js.map +1 -0
  51. package/dist/notification.js +418 -0
  52. package/dist/notification.js.map +1 -0
  53. package/dist/number-counter.js +2 -2
  54. package/dist/number-counter.js.map +1 -1
  55. package/dist/observe-slot-change-BGJfgg2E.js +31 -0
  56. package/dist/observe-slot-change-BGJfgg2E.js.map +1 -0
  57. package/dist/peacock-loader.js +48 -13
  58. package/dist/peacock-loader.js.map +1 -1
  59. package/dist/search.js +456 -0
  60. package/dist/search.js.map +1 -0
  61. package/dist/side-sheet.js +2 -2
  62. package/dist/src/__mixins/BaseButtonMixin.d.ts +20 -0
  63. package/dist/src/__mixins/BaseHyperlinkMixin.d.ts +18 -0
  64. package/dist/src/__mixins/MixinConstructor.d.ts +1 -0
  65. package/dist/src/__utils/cache-fetch.d.ts +1 -0
  66. package/dist/src/__utils/is-dark-mode.d.ts +1 -0
  67. package/dist/src/__utils/is-in-viewport.d.ts +1 -0
  68. package/dist/src/__utils/observe-slot-change.d.ts +1 -0
  69. package/dist/src/__utils/sanitize-svg.d.ts +1 -0
  70. package/dist/src/__utils/throttle.d.ts +4 -0
  71. package/dist/src/accordion/accordion-item.d.ts +33 -9
  72. package/dist/src/accordion/accordion.d.ts +21 -5
  73. package/dist/src/banner/banner.d.ts +43 -0
  74. package/dist/src/banner/index.d.ts +1 -0
  75. package/dist/src/button/BaseButton.d.ts +7 -57
  76. package/dist/src/button/button/button.d.ts +32 -3
  77. package/dist/src/button/button-group/button-group.d.ts +4 -4
  78. package/dist/src/button/icon-button/icon-button.d.ts +33 -8
  79. package/dist/src/card/card.d.ts +4 -15
  80. package/dist/src/empty-state/empty-state.d.ts +1 -1
  81. package/dist/src/fab/fab.d.ts +80 -0
  82. package/dist/src/fab/index.d.ts +1 -0
  83. package/dist/src/focus-ring/focus-ring.d.ts +11 -5
  84. package/dist/src/index.d.ts +8 -1
  85. package/dist/src/link/link.d.ts +3 -10
  86. package/dist/src/menu/menu/menu.d.ts +3 -2
  87. package/dist/src/menu/sub-menu/sub-menu.d.ts +1 -0
  88. package/dist/src/navigation-rail/index.d.ts +2 -0
  89. package/dist/src/navigation-rail/navigation-rail-item.d.ts +55 -0
  90. package/dist/src/navigation-rail/navigation-rail.d.ts +71 -0
  91. package/dist/src/notification/index.d.ts +1 -0
  92. package/dist/src/notification/notification.d.ts +69 -0
  93. package/dist/src/pagination/pagination.d.ts +8 -1
  94. package/dist/src/search/index.d.ts +1 -0
  95. package/dist/src/search/search.d.ts +76 -0
  96. package/dist/src/select/select.d.ts +3 -5
  97. package/dist/src/sidebar-menu/index.d.ts +3 -0
  98. package/dist/src/sidebar-menu/sidebar-menu-item.d.ts +58 -0
  99. package/dist/src/sidebar-menu/sidebar-menu.d.ts +38 -0
  100. package/dist/src/sidebar-menu/sidebar-sub-menu.d.ts +35 -0
  101. package/dist/src/slider/slider.d.ts +4 -0
  102. package/dist/src/snackbar/snackbar.d.ts +14 -1
  103. package/dist/src/toolbar/index.d.ts +1 -0
  104. package/dist/src/toolbar/toolbar.d.ts +86 -0
  105. package/dist/src/tooltip/tooltip.d.ts +3 -0
  106. package/dist/src/url-field/index.d.ts +1 -0
  107. package/dist/src/url-field/url-field.d.ts +48 -0
  108. package/dist/{style-map-CRFEoCEg.js → style-map-DVmWOuYy.js} +2 -2
  109. package/dist/{style-map-CRFEoCEg.js.map → style-map-DVmWOuYy.js.map} +1 -1
  110. package/dist/test/banner.test.d.ts +1 -0
  111. package/dist/test/search.test.d.ts +1 -0
  112. package/dist/test/sidebar-menu.test.d.ts +1 -0
  113. package/dist/test/toolbar.test.d.ts +1 -0
  114. package/dist/toolbar.js +306 -0
  115. package/dist/toolbar.js.map +1 -0
  116. package/dist/tsconfig.tsbuildinfo +1 -1
  117. package/dist/{unsafe-html-D3GHRaGQ.js → unsafe-html-BsGUjx94.js} +2 -2
  118. package/dist/{unsafe-html-D3GHRaGQ.js.map → unsafe-html-BsGUjx94.js.map} +1 -1
  119. package/package.json +1 -1
  120. package/readme.md +73 -65
  121. package/scss/mixin.scss +16 -0
  122. package/scss/styles.scss +4 -0
  123. package/src/__mixins/BaseButtonMixin.ts +83 -0
  124. package/src/__mixins/BaseHyperlinkMixin.ts +68 -0
  125. package/src/__mixins/MixinConstructor.ts +1 -0
  126. package/src/__mixins/README.md +19 -0
  127. package/src/__utils/cache-fetch.ts +65 -0
  128. package/src/__utils/is-dark-mode.ts +3 -0
  129. package/src/__utils/is-in-viewport.ts +6 -0
  130. package/src/__utils/observe-slot-change.ts +38 -0
  131. package/src/__utils/sanitize-svg.ts +27 -0
  132. package/src/__utils/throttle.ts +27 -0
  133. package/src/accordion/accordion-item.scss +136 -65
  134. package/src/accordion/accordion-item.ts +117 -44
  135. package/src/accordion/accordion.scss +24 -5
  136. package/src/accordion/accordion.ts +29 -23
  137. package/src/accordion/demo/index.html +74 -35
  138. package/src/banner/banner.scss +83 -0
  139. package/src/banner/banner.ts +101 -0
  140. package/src/banner/index.ts +1 -0
  141. package/src/button/BaseButton.ts +13 -115
  142. package/src/button/button/button-colors.scss +14 -14
  143. package/src/button/button/button-sizes.scss +4 -2
  144. package/src/button/button/button.ts +80 -26
  145. package/src/button/button-group/button-group.ts +5 -5
  146. package/src/button/icon-button/icon-button.ts +79 -44
  147. package/src/card/card.ts +50 -100
  148. package/src/chart-bar/chart-bar.ts +10 -15
  149. package/src/chart-bar/chart-stacked-bar.ts +15 -19
  150. package/src/chart-doughnut/chart-doughnut.ts +24 -28
  151. package/src/chart-pie/chart-pie.ts +20 -24
  152. package/src/checkbox/checkbox.scss +17 -34
  153. package/src/checkbox/checkbox.ts +4 -2
  154. package/src/clock/clock.ts +1 -1
  155. package/src/code-editor/code-editor.ts +4 -4
  156. package/src/code-highlighter/code-highlighter.scss +1 -0
  157. package/src/code-highlighter/code-highlighter.ts +3 -3
  158. package/src/date-picker/date-picker.ts +6 -3
  159. package/src/divider/divider.ts +3 -1
  160. package/src/elevation/elevation.scss +5 -5
  161. package/src/empty-state/empty-state.scss +7 -9
  162. package/src/empty-state/empty-state.ts +1 -1
  163. package/src/fab/fab-colors.scss +49 -0
  164. package/src/fab/fab-sizes.scss +47 -0
  165. package/src/fab/fab.scss +137 -0
  166. package/src/fab/fab.ts +214 -0
  167. package/src/fab/index.ts +1 -0
  168. package/src/field/field.ts +3 -1
  169. package/src/focus-ring/focus-ring.ts +47 -40
  170. package/src/icon/datasource.ts +1 -1
  171. package/src/icon/icon.ts +3 -1
  172. package/src/image/image.ts +3 -2
  173. package/src/index.ts +8 -1
  174. package/src/input/input.ts +8 -3
  175. package/src/link/link.ts +2 -15
  176. package/src/menu/menu/menu.scss +7 -0
  177. package/src/menu/menu/menu.ts +7 -4
  178. package/src/menu/menu-item/menu-item.ts +3 -1
  179. package/src/menu/sub-menu/sub-menu.ts +1 -0
  180. package/src/navigation-rail/index.ts +2 -0
  181. package/src/navigation-rail/navigation-rail-item.scss +216 -0
  182. package/src/navigation-rail/navigation-rail-item.ts +223 -0
  183. package/src/navigation-rail/navigation-rail.scss +72 -0
  184. package/src/navigation-rail/navigation-rail.ts +149 -0
  185. package/src/notification/index.ts +1 -0
  186. package/src/notification/notification.scss +201 -0
  187. package/src/notification/notification.ts +207 -0
  188. package/src/number-counter/number-counter.ts +3 -1
  189. package/src/number-field/number-field.ts +10 -6
  190. package/src/pagination/pagination.scss +33 -24
  191. package/src/pagination/pagination.ts +115 -60
  192. package/src/peacock-loader.ts +42 -5
  193. package/src/radio/radio.ts +3 -1
  194. package/src/search/index.ts +1 -0
  195. package/src/search/search-colors.scss +14 -0
  196. package/src/search/search.scss +204 -0
  197. package/src/search/search.ts +244 -0
  198. package/src/select/option.ts +1 -1
  199. package/src/select/select.scss +5 -0
  200. package/src/select/select.ts +71 -37
  201. package/src/sidebar-menu/demo/index.html +68 -0
  202. package/src/sidebar-menu/index.ts +3 -0
  203. package/src/sidebar-menu/sidebar-menu-item.scss +102 -0
  204. package/src/sidebar-menu/sidebar-menu-item.ts +151 -0
  205. package/src/{tree-view/tree-view.scss → sidebar-menu/sidebar-menu.scss} +1 -1
  206. package/src/sidebar-menu/sidebar-menu.ts +182 -0
  207. package/src/sidebar-menu/sidebar-sub-menu.scss +130 -0
  208. package/src/sidebar-menu/sidebar-sub-menu.ts +160 -0
  209. package/src/skeleton/skeleton.scss +18 -24
  210. package/src/slider/slider.scss +19 -0
  211. package/src/slider/slider.ts +30 -19
  212. package/src/snackbar/snackbar.scss +62 -31
  213. package/src/snackbar/snackbar.ts +91 -11
  214. package/src/switch/switch.ts +3 -1
  215. package/src/table/table.ts +3 -1
  216. package/src/tabs/tab.ts +10 -6
  217. package/src/text/text.css-component.scss +7 -1
  218. package/src/textarea/textarea.ts +4 -2
  219. package/src/time-picker/time-picker.ts +5 -3
  220. package/src/toolbar/index.ts +1 -0
  221. package/src/toolbar/toolbar-colors.scss +16 -0
  222. package/src/toolbar/toolbar.scss +165 -0
  223. package/src/toolbar/toolbar.ts +137 -0
  224. package/src/tooltip/tooltip.ts +24 -0
  225. package/src/url-field/index.ts +1 -0
  226. package/src/url-field/url-field.scss +50 -0
  227. package/src/url-field/url-field.ts +239 -0
  228. package/dist/button-COYCtuA8.js.map +0 -1
  229. package/dist/button-group-DsXquZQn.js.map +0 -1
  230. package/dist/directive-Cuw6h7YA.js +0 -9
  231. package/dist/directive-Cuw6h7YA.js.map +0 -1
  232. package/dist/dispatch-event-utils-B4odODQf.js +0 -277
  233. package/dist/dispatch-event-utils-B4odODQf.js.map +0 -1
  234. package/dist/observe-theme-change-DKAIv5BB.js.map +0 -1
  235. package/dist/select-C3XAzenC.js.map +0 -1
  236. package/dist/src/styleMixins.css.d.ts +0 -9
  237. package/dist/src/tree-view/index.d.ts +0 -2
  238. package/dist/src/tree-view/tree-node.d.ts +0 -69
  239. package/dist/src/tree-view/tree-view.d.ts +0 -40
  240. package/dist/src/tree-view/wc-tree-view.d.ts +0 -6
  241. package/dist/src/utils.d.ts +0 -9
  242. package/dist/test/tree-view.test.d.ts +0 -1
  243. package/src/styleMixins.css.ts +0 -55
  244. package/src/tree-view/demo/index.html +0 -57
  245. package/src/tree-view/index.ts +0 -2
  246. package/src/tree-view/tree-node.scss +0 -101
  247. package/src/tree-view/tree-node.ts +0 -268
  248. package/src/tree-view/tree-view.ts +0 -182
  249. package/src/tree-view/wc-tree-view.ts +0 -9
  250. package/src/utils.ts +0 -193
  251. /package/dist/src/{spread.d.ts → __directive/spread.d.ts} +0 -0
  252. /package/dist/src/{utils → __utils}/copy-to-clipboard.d.ts +0 -0
  253. /package/dist/src/{utils → __utils}/dispatch-event-utils.d.ts +0 -0
  254. /package/dist/src/{utils → __utils}/observe-theme-change.d.ts +0 -0
  255. /package/src/{spread.ts → __directive/spread.ts} +0 -0
  256. /package/src/{utils → __utils}/copy-to-clipboard.ts +0 -0
  257. /package/src/{utils → __utils}/dispatch-event-utils.ts +0 -0
  258. /package/src/{utils → __utils}/observe-theme-change.ts +0 -0
@@ -0,0 +1,151 @@
1
+ import { html, LitElement } from 'lit';
2
+ import { property, query } from 'lit/decorators.js';
3
+ import { classMap } from 'lit/directives/class-map.js';
4
+ import styles from './sidebar-menu-item.scss';
5
+
6
+ /**
7
+ * @label Sidebar Menu Item
8
+ * @tag wc-sidebar-menu-item
9
+ * @rawTag sidebar-menu-item
10
+ * @parentRawTag sidebar-menu
11
+ * @summary A sidebar menu item represents a selectable leaf item in the sidebar navigation tree.
12
+ *
13
+ * @example
14
+ * ```html
15
+ * <wc-sidebar-menu>
16
+ * <wc-sidebar-sub-menu label="Parent" expanded>
17
+ * <wc-sidebar-menu-item label="Child"></wc-sidebar-menu-item>
18
+ * </wc-sidebar-sub-menu>
19
+ * </wc-sidebar-menu>
20
+ * ```
21
+ * @tags navigation
22
+ */
23
+ export class SidebarMenuItem extends LitElement {
24
+ static styles = [styles];
25
+
26
+ /**
27
+ * The value used to identify this item when selected.
28
+ */
29
+ @property({ type: String, reflect: true })
30
+ value: string = '';
31
+
32
+ /**
33
+ * The display label for this item.
34
+ */
35
+ @property({ type: String, reflect: true })
36
+ label: string = '';
37
+
38
+ /**
39
+ * Optional icon name to display before the label.
40
+ */
41
+ @property({ type: String, reflect: true })
42
+ icon: string = '';
43
+
44
+ /**
45
+ * Optional hyperlink to navigate to on click.
46
+ */
47
+ @property({ type: String, reflect: true })
48
+ href: string = '';
49
+
50
+ /**
51
+ * Sets or retrieves the window or frame at which to target content.
52
+ */
53
+ @property({ type: String, reflect: true })
54
+ target: string = '_self';
55
+
56
+ /**
57
+ * If true, the user cannot interact with the item.
58
+ */
59
+ @property({ type: Boolean, reflect: true })
60
+ disabled: boolean = false;
61
+
62
+ /**
63
+ * Whether the item is currently selected.
64
+ */
65
+ @property({ type: Boolean, reflect: true })
66
+ selected: boolean = false;
67
+
68
+ /**
69
+ * The nesting depth level (set automatically by the parent sidebar-menu).
70
+ */
71
+ @property({ type: Number, reflect: true })
72
+ level: number = 0;
73
+
74
+ @query('.sidebar-menu-item-content')
75
+ private readonly _nativeElement!: HTMLElement | null;
76
+
77
+ override focus() {
78
+ this._nativeElement?.focus();
79
+ }
80
+
81
+ override blur() {
82
+ this._nativeElement?.blur();
83
+ }
84
+
85
+ private _onClick = () => {
86
+ if (this.disabled) return;
87
+
88
+ this.dispatchEvent(
89
+ new CustomEvent('sidebar-menu-item:click', {
90
+ bubbles: true,
91
+ composed: true,
92
+ detail: { value: this.value, label: this.label },
93
+ }),
94
+ );
95
+ };
96
+
97
+ override render() {
98
+ const classes = classMap({
99
+ 'sidebar-menu-item': true,
100
+ disabled: this.disabled,
101
+ selected: this.selected,
102
+ });
103
+
104
+ const content = html`
105
+ <wc-focus-ring class="focus-ring" for="item"></wc-focus-ring>
106
+ <div class="background"></div>
107
+ <wc-ripple class="ripple"></wc-ripple>
108
+ <div class="sidebar-menu-item-content">
109
+ ${this.icon ? html`<wc-icon name="${this.icon}"></wc-icon>` : ''}
110
+ <span class="sidebar-menu-item-label">${this.label}</span>
111
+ </div>
112
+ `;
113
+
114
+ if (this.href) {
115
+ return html`
116
+ <a
117
+ id="item"
118
+ href="${this.href}"
119
+ target="${this.target}"
120
+ class="${classes}"
121
+ aria-disabled="${this.disabled}"
122
+ tabindex="${this.disabled ? -1 : 0}"
123
+ @click="${this._onClick}"
124
+ >
125
+ ${content}
126
+ </a>
127
+ `;
128
+ }
129
+
130
+ return html`
131
+ <div
132
+ id="item"
133
+ class="${classes}"
134
+ role="treeitem"
135
+ aria-label="${this.label}"
136
+ aria-selected="${String(this.selected)}"
137
+ aria-disabled="${this.disabled}"
138
+ tabindex="${this.disabled ? -1 : 0}"
139
+ @click="${this._onClick}"
140
+ @keydown="${(e: KeyboardEvent) => {
141
+ if (e.key === 'Enter' || e.key === ' ') {
142
+ e.preventDefault();
143
+ this._onClick();
144
+ }
145
+ }}"
146
+ >
147
+ ${content}
148
+ </div>
149
+ `;
150
+ }
151
+ }
@@ -6,7 +6,7 @@
6
6
  display: block;
7
7
  }
8
8
 
9
- .tree-view {
9
+ .sidebar-menu {
10
10
  display: block;
11
11
  padding: var(--spacing-100, 0.5rem) 0;
12
12
  }
@@ -0,0 +1,182 @@
1
+ import { html, LitElement } from 'lit';
2
+ import { property } from 'lit/decorators.js';
3
+ import styles from './sidebar-menu.scss';
4
+ import { SidebarMenuItem } from './sidebar-menu-item.js';
5
+ import { SidebarSubMenu } from './sidebar-sub-menu.js';
6
+
7
+ type SidebarNode = SidebarMenuItem | SidebarSubMenu;
8
+
9
+ /**
10
+ * @label Sidebar Menu
11
+ * @tag wc-sidebar-menu
12
+ * @rawTag sidebar-menu
13
+ * @summary A sidebar menu is a hierarchical structure that provides nested levels of navigation. It supports keyboard navigation, single/multi select, and expandable items.
14
+ *
15
+ * @example
16
+ * ```html
17
+ * <wc-sidebar-menu>
18
+ * <wc-sidebar-sub-menu label="Parent" expanded>
19
+ * <wc-sidebar-menu-item label="Child 1"></wc-sidebar-menu-item>
20
+ * <wc-sidebar-menu-item label="Child 2"></wc-sidebar-menu-item>
21
+ * </wc-sidebar-sub-menu>
22
+ * </wc-sidebar-menu>
23
+ * ```
24
+ * @tags navigation
25
+ */
26
+ export class SidebarMenu extends LitElement {
27
+ static styles = [styles];
28
+
29
+ /**
30
+ * The value of the currently selected item.
31
+ */
32
+ @property({ type: String, attribute: 'selected-item', reflect: true })
33
+ selectedItem: string = '';
34
+
35
+ connectedCallback() {
36
+ super.connectedCallback();
37
+ this.addEventListener('sidebar-menu-item:click', this._onItemClick as EventListener);
38
+ this.addEventListener('keydown', this._onKeyDown);
39
+ this.setAttribute('role', 'tree');
40
+ }
41
+
42
+ updated(changedProps: Map<string, unknown>) {
43
+ super.updated(changedProps);
44
+
45
+ if (changedProps.has('selectedItem')) {
46
+ this._syncSelectedStateFromProperty();
47
+ }
48
+ }
49
+
50
+ disconnectedCallback() {
51
+ super.disconnectedCallback();
52
+ this.removeEventListener('sidebar-menu-item:click', this._onItemClick as EventListener);
53
+ this.removeEventListener('keydown', this._onKeyDown);
54
+ }
55
+
56
+ private _isSidebarNode(element: Element): element is SidebarNode {
57
+ const tag = element.tagName.toLowerCase();
58
+ return tag === 'wc-sidebar-menu-item' || tag === 'wc-sidebar-sub-menu';
59
+ }
60
+
61
+ private _getTopLevelItems(): SidebarNode[] {
62
+ return Array.from(this.children).filter(
63
+ el => this._isSidebarNode(el),
64
+ ) as SidebarNode[];
65
+ }
66
+
67
+ private _getChildNodes(item: SidebarSubMenu): SidebarNode[] {
68
+ return Array.from(item.children).filter(
69
+ child => this._isSidebarNode(child),
70
+ ) as SidebarNode[];
71
+ }
72
+
73
+ private _getAllVisibleItems(): SidebarNode[] {
74
+ const result: SidebarNode[] = [];
75
+ const collect = (items: SidebarNode[]) => {
76
+ items.forEach(item => {
77
+ result.push(item);
78
+ if (item instanceof SidebarSubMenu && item.expanded) {
79
+ collect(this._getChildNodes(item));
80
+ }
81
+ });
82
+ };
83
+ collect(this._getTopLevelItems());
84
+ return result;
85
+ }
86
+
87
+ private _onItemClick = (event: CustomEvent) => {
88
+ const item = event.target as SidebarNode;
89
+ if (item.disabled) return;
90
+
91
+ const value = event.detail?.value ?? item.value ?? item.label;
92
+ this.selectedItem = value;
93
+
94
+ // Update selected state on all items
95
+ this._updateSelectedState(value);
96
+
97
+ this.dispatchEvent(
98
+ new CustomEvent('sidebar-menu:change', {
99
+ bubbles: true,
100
+ composed: true,
101
+ detail: { value, item },
102
+ }),
103
+ );
104
+ };
105
+
106
+ private _updateSelectedState(selectedValue: string) {
107
+ const allItems = this._collectAllItems(this._getTopLevelItems());
108
+ allItems.forEach(item => {
109
+ const itemValue = item.value || item.label;
110
+ // eslint-disable-next-line no-param-reassign
111
+ item.selected = itemValue === selectedValue;
112
+ });
113
+ }
114
+
115
+ private _collectAllItems(items: SidebarNode[]): SidebarNode[] {
116
+ const result: SidebarNode[] = [];
117
+ items.forEach(item => {
118
+ result.push(item);
119
+ if (item instanceof SidebarSubMenu) {
120
+ result.push(...this._collectAllItems(this._getChildNodes(item)));
121
+ }
122
+ });
123
+ return result;
124
+ }
125
+
126
+ private _syncSelectedStateFromProperty() {
127
+ const allItems = this._collectAllItems(this._getTopLevelItems());
128
+ allItems.forEach(item => {
129
+ const itemValue = item.value || item.label;
130
+ item.selected = itemValue === this.selectedItem;
131
+ });
132
+ }
133
+
134
+ private _onKeyDown = (event: KeyboardEvent) => {
135
+ const allVisible = this._getAllVisibleItems();
136
+ const currentElement = event.composedPath().find(
137
+ target => target instanceof SidebarMenuItem || target instanceof SidebarSubMenu,
138
+ ) as SidebarNode | undefined;
139
+
140
+ if (!currentElement) {
141
+ return;
142
+ }
143
+
144
+ if (!allVisible.includes(currentElement)) {
145
+ return;
146
+ }
147
+
148
+ const currentIndex = allVisible.indexOf(currentElement);
149
+
150
+ if (event.key === 'ArrowDown') {
151
+ event.preventDefault();
152
+ const nextIndex = (currentIndex + 1) % allVisible.length;
153
+ allVisible[nextIndex].focus();
154
+ } else if (event.key === 'ArrowUp') {
155
+ event.preventDefault();
156
+ const prevIndex = currentIndex === 0 ? allVisible.length - 1 : currentIndex - 1;
157
+ allVisible[prevIndex].focus();
158
+ } else if (event.key === 'Enter' || event.key === ' ') {
159
+ event.preventDefault();
160
+ currentElement.click();
161
+ } else if (event.key === 'ArrowRight') {
162
+ event.preventDefault();
163
+ if (currentElement instanceof SidebarSubMenu && !currentElement.expanded) {
164
+ currentElement.expanded = true;
165
+ } else if (currentElement instanceof SidebarSubMenu) {
166
+ const children = this._getChildNodes(currentElement);
167
+ if (children.length > 0) {
168
+ children[0].focus();
169
+ }
170
+ }
171
+ } else if (event.key === 'ArrowLeft') {
172
+ event.preventDefault();
173
+ if (currentElement instanceof SidebarSubMenu && currentElement.expanded) {
174
+ currentElement.expanded = false;
175
+ }
176
+ }
177
+ };
178
+
179
+ override render() {
180
+ return html` <div class="sidebar-menu"><slot></slot></div> `;
181
+ }
182
+ }
@@ -0,0 +1,130 @@
1
+ @use '../../scss/mixin';
2
+
3
+ @include mixin.base-styles;
4
+
5
+ :host {
6
+ display: block;
7
+
8
+ --sidebar-menu-item-height: 3rem;
9
+ --sidebar-menu-item-border-radius: var(--global-shape-corner-full, 9999px);
10
+ --sidebar-menu-item-label-color: var(--color-on-surface);
11
+ --sidebar-menu-item-icon-color: var(--color-on-surface-variant);
12
+ --sidebar-menu-item-selected-background: var(--color-secondary-container);
13
+ --sidebar-menu-item-selected-color: var(--color-on-secondary-container);
14
+ --sidebar-menu-item-focus-ring-color: var(--color-primary);
15
+ }
16
+
17
+ .sidebar-sub-menu {
18
+ display: block;
19
+ }
20
+
21
+ .sidebar-sub-menu-inner {
22
+ position: relative;
23
+ display: flex;
24
+ align-items: center;
25
+ gap: 0.5rem;
26
+ min-height: var(--sidebar-menu-item-height);
27
+ cursor: pointer;
28
+ border-radius: var(--sidebar-menu-item-border-radius);
29
+ outline: none;
30
+ padding-inline-end: 0.75rem;
31
+ color: var(--_label-color);
32
+
33
+ --_container-color: transparent;
34
+ --_container-opacity: 1;
35
+ --_label-color: var(--sidebar-menu-item-label-color);
36
+
37
+ @include mixin.get-typography(label-large);
38
+
39
+ &.selected {
40
+ --_container-color: var(--sidebar-menu-item-selected-background);
41
+ --_label-color: var(--sidebar-menu-item-selected-color);
42
+ }
43
+
44
+ &.disabled {
45
+ cursor: not-allowed;
46
+ opacity: 0.6;
47
+
48
+ .ripple {
49
+ display: none;
50
+ }
51
+ }
52
+ }
53
+
54
+ .sidebar-sub-menu-content {
55
+ display: flex;
56
+ align-items: center;
57
+ gap: 0.25rem;
58
+ flex: 1;
59
+ min-height: var(--sidebar-menu-item-height);
60
+ color: inherit;
61
+ user-select: none;
62
+ text-decoration: none;
63
+ outline: none;
64
+ padding-inline: 0.75rem;
65
+ z-index: 1;
66
+ }
67
+
68
+ .background {
69
+ position: absolute;
70
+ inset: 0;
71
+ background-color: var(--_container-color);
72
+ opacity: var(--_container-opacity);
73
+ border-radius: inherit;
74
+ pointer-events: none;
75
+ }
76
+
77
+ .focus-ring {
78
+ z-index: 2;
79
+ --focus-ring-color: var(--sidebar-menu-item-focus-ring-color);
80
+ --focus-ring-container-shape-start-start: var(--sidebar-menu-item-border-radius);
81
+ --focus-ring-container-shape-start-end: var(--sidebar-menu-item-border-radius);
82
+ --focus-ring-container-shape-end-start: var(--sidebar-menu-item-border-radius);
83
+ --focus-ring-container-shape-end-end: var(--sidebar-menu-item-border-radius);
84
+ }
85
+
86
+ .ripple {
87
+ --ripple-pressed-color: var(--color-on-surface);
88
+ border-radius: inherit;
89
+ }
90
+
91
+ .sidebar-sub-menu-inner.selected .ripple {
92
+ --ripple-pressed-color: var(--sidebar-menu-item-selected-color);
93
+ }
94
+
95
+ .sidebar-sub-menu-label {
96
+ flex: 1;
97
+ overflow: hidden;
98
+ text-overflow: ellipsis;
99
+ white-space: nowrap;
100
+ }
101
+
102
+ .expand-icon {
103
+ display: flex;
104
+ align-items: center;
105
+ justify-content: center;
106
+ color: var(--sidebar-menu-item-icon-color);
107
+ flex-shrink: 0;
108
+ width: var(--sidebar-menu-item-height);
109
+ height: var(--sidebar-menu-item-height);
110
+ transition: transform var(--duration-medium1) var(--easing-standard);
111
+ }
112
+
113
+ :host([expanded]) .expand-icon {
114
+ transform: rotate(180deg);
115
+ }
116
+
117
+ .sidebar-sub-menu-children {
118
+ display: block;
119
+ overflow: hidden;
120
+ margin-left: var(--spacing-200);
121
+ max-height: 9999px;
122
+ opacity: 1;
123
+ transition: max-height var(--duration-medium1) var(--easing-standard),
124
+ opacity var(--duration-medium1) var(--easing-standard);
125
+ }
126
+
127
+ .sidebar-sub-menu-children.hidden {
128
+ max-height: 0;
129
+ opacity: 0;
130
+ }
@@ -0,0 +1,160 @@
1
+ import { html, LitElement } from 'lit';
2
+ import { property, query } from 'lit/decorators.js';
3
+ import { classMap } from 'lit/directives/class-map.js';
4
+ import { styleMap } from 'lit/directives/style-map.js';
5
+ import styles from './sidebar-sub-menu.scss';
6
+
7
+ /**
8
+ * @label Sidebar Sub Menu
9
+ * @tag wc-sidebar-sub-menu
10
+ * @rawTag sidebar-sub-menu
11
+ * @parentRawTag sidebar-menu
12
+ * @summary A sidebar sub menu groups sidebar menu items and handles expand/collapse behavior.
13
+ *
14
+ * @example
15
+ * ```html
16
+ * <wc-sidebar-sub-menu label="Parent" expanded>
17
+ * <wc-sidebar-menu-item label="Child"></wc-sidebar-menu-item>
18
+ * </wc-sidebar-sub-menu>
19
+ * ```
20
+ * @tags navigation
21
+ */
22
+ export class SidebarSubMenu extends LitElement {
23
+ static styles = [styles];
24
+
25
+ @property({ type: String, reflect: true })
26
+ value: string = '';
27
+
28
+ @property({ type: String, reflect: true })
29
+ label: string = '';
30
+
31
+ @property({ type: String, reflect: true })
32
+ icon: string = '';
33
+
34
+ @property({ type: Boolean, reflect: true })
35
+ disabled: boolean = false;
36
+
37
+ @property({ type: Boolean, reflect: true })
38
+ selected: boolean = false;
39
+
40
+ @property({ type: Boolean, reflect: true })
41
+ expanded: boolean = false;
42
+
43
+ @property({ type: Number, reflect: true })
44
+ level: number = 0;
45
+
46
+ @query('.sidebar-sub-menu-inner')
47
+ private readonly _nativeElement!: HTMLElement | null;
48
+
49
+ override focus() {
50
+ this._nativeElement?.focus();
51
+ }
52
+
53
+ override blur() {
54
+ this._nativeElement?.blur();
55
+ }
56
+
57
+ connectedCallback() {
58
+ super.connectedCallback();
59
+ this._updateChildLevels();
60
+ }
61
+
62
+ updated(changedProps: Map<string, unknown>) {
63
+ super.updated(changedProps);
64
+
65
+ if (changedProps.has('expanded')) {
66
+ this.setAttribute('aria-expanded', String(this.expanded));
67
+ if (this.expanded) {
68
+ this.setAttribute('data-expanded', '');
69
+ } else {
70
+ this.removeAttribute('data-expanded');
71
+ }
72
+ }
73
+ }
74
+
75
+ private _getChildNodes(): Element[] {
76
+ return Array.from(this.children).filter(el => {
77
+ const tag = el.tagName.toLowerCase();
78
+ return tag === 'wc-sidebar-menu-item' || tag === 'wc-sidebar-sub-menu';
79
+ });
80
+ }
81
+
82
+ private _updateChildLevels = () => {
83
+ this._getChildNodes().forEach(child => {
84
+ if ('level' in child) {
85
+ // eslint-disable-next-line no-param-reassign
86
+ (child as { level: number }).level = this.level + 1;
87
+ }
88
+ });
89
+ };
90
+
91
+ private _onClick = () => {
92
+ if (this.disabled) return;
93
+
94
+ if (this._getChildNodes().length > 0) {
95
+ this.expanded = !this.expanded;
96
+ }
97
+
98
+ this.dispatchEvent(
99
+ new CustomEvent('sidebar-menu-item:click', {
100
+ bubbles: true,
101
+ composed: true,
102
+ detail: { value: this.value, label: this.label },
103
+ }),
104
+ );
105
+ };
106
+
107
+ override render() {
108
+ const hasChildren = this._getChildNodes().length > 0;
109
+ const wrapperClasses = classMap({
110
+ 'sidebar-sub-menu': true,
111
+ });
112
+ const innerClasses = classMap({
113
+ 'sidebar-sub-menu-inner': true,
114
+ disabled: this.disabled,
115
+ selected: this.selected,
116
+ });
117
+
118
+ const inlineStyles = styleMap({
119
+ paddingLeft: `calc(var(--sidebar-menu-item-height, 2.5rem) * ${this.level})`,
120
+ });
121
+
122
+ return html`
123
+ <div class="${wrapperClasses}" style="${inlineStyles}">
124
+ <div
125
+ id="item"
126
+ class="${innerClasses}"
127
+ role="treeitem"
128
+ aria-label="${this.label}"
129
+ aria-selected="${String(this.selected)}"
130
+ aria-disabled="${this.disabled}"
131
+ aria-expanded="${this.expanded}"
132
+ @click="${this._onClick}"
133
+ @keydown="${(e: KeyboardEvent) => {
134
+ if (e.key === 'Enter' || e.key === ' ') {
135
+ e.preventDefault();
136
+ this._onClick();
137
+ }
138
+ }}"
139
+ tabindex="${this.disabled ? -1 : 0}"
140
+ >
141
+ <wc-focus-ring class="focus-ring" for="item"></wc-focus-ring>
142
+ <div class="background"></div>
143
+ <wc-ripple class="ripple"></wc-ripple>
144
+ <div class="sidebar-sub-menu-content">
145
+ ${this.icon ? html`<wc-icon name="${this.icon}"></wc-icon>` : ''}
146
+ <span class="sidebar-sub-menu-label">${this.label}</span>
147
+ </div>
148
+ ${hasChildren
149
+ ? html`
150
+ <wc-icon class="expand-icon" name="arrow_drop_down" aria-hidden="true"></wc-icon>
151
+ `
152
+ : ''}
153
+ </div>
154
+ <div class="sidebar-sub-menu-children ${(!hasChildren || !this.expanded) ? 'hidden' : ''}">
155
+ <slot @slotchange="${this._updateChildLevels}"></slot>
156
+ </div>
157
+ </div>
158
+ `;
159
+ }
160
+ }
@@ -1,43 +1,37 @@
1
+ @use '../../scss/mixin';
2
+
3
+ @include mixin.base-styles;
4
+
5
+
6
+
7
+
1
8
  :host {
9
+ position: relative;
10
+ inset: 0;
2
11
  --skeleton-container-color: var(--color-surface-container);
3
12
  --skeleton-element: var(--color-on-surface);
4
- display: inline-block;
5
- height: 3rem;
6
- width: 10rem;
13
+ display: flex;
14
+ pointer-events: none;
15
+ }
16
+
17
+ .skeleton,
18
+ .skeleton::before {
19
+ inset: 0;
20
+ position: absolute;
21
+ @include mixin.apply-container-shape(skeleton);
7
22
  }
8
23
 
9
24
  .skeleton {
10
- position: relative;
11
- padding: 0;
12
- border: none;
13
25
  background: var(--skeleton-container-color);
14
- box-shadow: none;
15
26
  overflow: hidden;
16
- pointer-events: none;
17
- width: 100%;
18
- height: 100%;
19
- border-start-start-radius: var(--skeleton-container-shape-start-start);
20
- border-start-end-radius: var(--skeleton-container-shape-start-end);
21
- border-end-start-radius: var(--skeleton-container-shape-end-start);
22
- border-end-end-radius: var(--skeleton-container-shape-end-end);
23
- corner-shape: var(--skeleton-container-shape-variant);
24
27
 
25
28
  &::before {
26
- position: absolute;
27
- left: 0;
28
- top: 0;
29
29
  animation: 3s ease-in-out skeleton infinite;
30
30
  background: var(--skeleton-element);
31
31
  block-size: 100%;
32
32
  content: "";
33
33
  inline-size: 100%;
34
34
  will-change: transform-origin, transform, opacity;
35
-
36
- border-start-start-radius: var(--skeleton-container-shape-start-start);
37
- border-start-end-radius: var(--skeleton-container-shape-start-end);
38
- border-end-start-radius: var(--skeleton-container-shape-end-start);
39
- border-end-end-radius: var(--skeleton-container-shape-end-end);
40
- corner-shape: var(--skeleton-container-shape-variant);
41
35
  }
42
36
  }
43
37