@redvars/peacock 3.5.0 → 3.6.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.
Files changed (314) hide show
  1. package/dist/BaseButton-BNFAYn-S.js +219 -0
  2. package/dist/BaseButton-BNFAYn-S.js.map +1 -0
  3. package/dist/BaseHyperlinkMixin-BNuwbiEf.js +65 -0
  4. package/dist/BaseHyperlinkMixin-BNuwbiEf.js.map +1 -0
  5. package/dist/BaseInput-14YmcfK7.js +27 -0
  6. package/dist/BaseInput-14YmcfK7.js.map +1 -0
  7. package/dist/assets/components.css +1 -1
  8. package/dist/assets/components.css.map +1 -1
  9. package/dist/assets/styles.css +1 -1
  10. package/dist/assets/styles.css.map +1 -1
  11. package/dist/banner.js +14 -30
  12. package/dist/banner.js.map +1 -1
  13. package/dist/{button-DMN1dPAg.js → button-colors-Ccys3hvS.js} +5 -468
  14. package/dist/button-colors-Ccys3hvS.js.map +1 -0
  15. package/dist/button-group.js +228 -8
  16. package/dist/button-group.js.map +1 -1
  17. package/dist/button.js +294 -8
  18. package/dist/button.js.map +1 -1
  19. package/dist/calendar-column-view.js +634 -0
  20. package/dist/calendar-column-view.js.map +1 -0
  21. package/dist/calendar-event-BrQ_SEKD.js +199 -0
  22. package/dist/calendar-event-BrQ_SEKD.js.map +1 -0
  23. package/dist/calendar-month-view.js +376 -0
  24. package/dist/calendar-month-view.js.map +1 -0
  25. package/dist/calendar.js +339 -0
  26. package/dist/calendar.js.map +1 -0
  27. package/dist/canvas.js +361 -0
  28. package/dist/canvas.js.map +1 -0
  29. package/dist/card.js +18 -73
  30. package/dist/card.js.map +1 -1
  31. package/dist/cb-compound-expression.js +125 -0
  32. package/dist/cb-compound-expression.js.map +1 -0
  33. package/dist/cb-divider.js +150 -0
  34. package/dist/cb-divider.js.map +1 -0
  35. package/dist/cb-expression.js +75 -0
  36. package/dist/cb-expression.js.map +1 -0
  37. package/dist/cb-predicate.js +137 -0
  38. package/dist/cb-predicate.js.map +1 -0
  39. package/dist/chart-bar.js.map +1 -1
  40. package/dist/chart-doughnut.js +2 -2
  41. package/dist/chart-doughnut.js.map +1 -1
  42. package/dist/chart-pie.js +2 -2
  43. package/dist/chart-pie.js.map +1 -1
  44. package/dist/chart-stacked-bar.js.map +1 -1
  45. package/dist/code-editor.js +2 -1
  46. package/dist/code-editor.js.map +1 -1
  47. package/dist/code-highlighter.js +2 -1
  48. package/dist/code-highlighter.js.map +1 -1
  49. package/dist/condition-builder.js +58 -0
  50. package/dist/condition-builder.js.map +1 -0
  51. package/dist/custom-elements-jsdocs.json +10860 -5567
  52. package/dist/custom-elements.json +16180 -7996
  53. package/dist/dropdown-button.js +216 -0
  54. package/dist/dropdown-button.js.map +1 -0
  55. package/dist/event-manager-D-QCmUgR.js +113 -0
  56. package/dist/event-manager-D-QCmUgR.js.map +1 -0
  57. package/dist/fab.js +421 -9
  58. package/dist/fab.js.map +1 -1
  59. package/dist/flow-designer-dZnLJOQT.js +1656 -0
  60. package/dist/flow-designer-dZnLJOQT.js.map +1 -0
  61. package/dist/flow-designer-node-XMe-jlKg.js +548 -0
  62. package/dist/flow-designer-node-XMe-jlKg.js.map +1 -0
  63. package/dist/flow-designer-node.js +4 -0
  64. package/dist/flow-designer-node.js.map +1 -0
  65. package/dist/flow-designer.js +16 -0
  66. package/dist/flow-designer.js.map +1 -0
  67. package/dist/html-editor.js +358 -0
  68. package/dist/html-editor.js.map +1 -0
  69. package/dist/icon-button-CK1ZuE-2.js +247 -0
  70. package/dist/icon-button-CK1ZuE-2.js.map +1 -0
  71. package/dist/index.js +31 -8
  72. package/dist/index.js.map +1 -1
  73. package/dist/{is-dark-mode-DicqGkCJ.js → is-dark-mode-DOcaw4Yq.js} +2 -27
  74. package/dist/is-dark-mode-DOcaw4Yq.js.map +1 -0
  75. package/dist/modal.js +418 -0
  76. package/dist/modal.js.map +1 -0
  77. package/dist/{select-4pl4XBj7.js → navigation-rail-DyO0oAZU.js} +2000 -2767
  78. package/dist/navigation-rail-DyO0oAZU.js.map +1 -0
  79. package/dist/notification-manager.js +268 -0
  80. package/dist/notification-manager.js.map +1 -0
  81. package/dist/notification.js +3 -2
  82. package/dist/notification.js.map +1 -1
  83. package/dist/peacock-loader.js +102 -14
  84. package/dist/peacock-loader.js.map +1 -1
  85. package/dist/popover-NC7b1lTq.js +1971 -0
  86. package/dist/popover-NC7b1lTq.js.map +1 -0
  87. package/dist/popover-content.js +125 -0
  88. package/dist/popover-content.js.map +1 -0
  89. package/dist/popover.js +4 -0
  90. package/dist/popover.js.map +1 -0
  91. package/dist/search.js +4 -0
  92. package/dist/search.js.map +1 -1
  93. package/dist/split-button.js +388 -0
  94. package/dist/split-button.js.map +1 -0
  95. package/dist/src/__controllers/floating-controller.d.ts +35 -0
  96. package/dist/src/__mixins/BaseButtonMixin.d.ts +20 -0
  97. package/dist/src/__mixins/BaseHyperlinkMixin.d.ts +18 -0
  98. package/dist/src/__mixins/MixinConstructor.d.ts +1 -0
  99. package/dist/src/banner/banner.d.ts +0 -4
  100. package/dist/src/button/BaseButton.d.ts +4 -47
  101. package/dist/src/button/button/button.d.ts +32 -3
  102. package/dist/src/button/button-group/button-group.d.ts +2 -2
  103. package/dist/src/button/icon-button/icon-button.d.ts +33 -8
  104. package/dist/src/calendar/base-event.d.ts +10 -0
  105. package/dist/src/calendar/calendar-column-view.d.ts +41 -0
  106. package/dist/src/calendar/calendar-event.d.ts +7 -0
  107. package/dist/src/calendar/calendar-month-view.d.ts +31 -0
  108. package/dist/src/calendar/calendar.d.ts +65 -0
  109. package/dist/src/calendar/event-manager.d.ts +17 -0
  110. package/dist/src/calendar/index.d.ts +4 -0
  111. package/dist/src/calendar/types.d.ts +13 -0
  112. package/dist/src/calendar/utils.d.ts +31 -0
  113. package/dist/src/canvas/canvas.d.ts +92 -0
  114. package/dist/src/canvas/index.d.ts +2 -0
  115. package/dist/src/card/card.d.ts +4 -15
  116. package/dist/src/condition-builder/cb-compound-expression.d.ts +31 -0
  117. package/dist/src/condition-builder/cb-divider.d.ts +26 -0
  118. package/dist/src/condition-builder/cb-expression.d.ts +31 -0
  119. package/dist/src/condition-builder/cb-predicate.d.ts +30 -0
  120. package/dist/src/condition-builder/condition-builder.d.ts +27 -0
  121. package/dist/src/condition-builder/index.d.ts +5 -0
  122. package/dist/src/dropdown-button/dropdown-button.d.ts +68 -0
  123. package/dist/src/dropdown-button/index.d.ts +1 -0
  124. package/dist/src/fab/fab.d.ts +4 -35
  125. package/dist/src/flow-designer/commands.d.ts +66 -0
  126. package/dist/src/flow-designer/flow-designer-node.d.ts +46 -0
  127. package/dist/src/flow-designer/flow-designer.d.ts +133 -0
  128. package/dist/src/flow-designer/index.d.ts +7 -0
  129. package/dist/src/flow-designer/layout.d.ts +30 -0
  130. package/dist/src/flow-designer/types.d.ts +142 -0
  131. package/dist/src/flow-designer/validation.d.ts +43 -0
  132. package/dist/src/flow-designer/workflow-utils.d.ts +40 -0
  133. package/dist/src/focus-ring/focus-ring.d.ts +11 -5
  134. package/dist/src/html-editor/html-editor.d.ts +56 -0
  135. package/dist/src/html-editor/index.d.ts +2 -0
  136. package/dist/src/index.d.ts +16 -1
  137. package/dist/src/link/link.d.ts +1 -1
  138. package/dist/src/menu/menu/menu.d.ts +5 -7
  139. package/dist/src/menu/menu-item/menu-item.d.ts +14 -13
  140. package/dist/src/modal/index.d.ts +1 -0
  141. package/dist/src/modal/modal.d.ts +63 -0
  142. package/dist/src/navigation-rail/index.d.ts +2 -0
  143. package/dist/src/navigation-rail/navigation-rail-item.d.ts +55 -0
  144. package/dist/src/navigation-rail/navigation-rail.d.ts +71 -0
  145. package/dist/src/notification-manager/index.d.ts +1 -0
  146. package/dist/src/notification-manager/notification-manager.d.ts +44 -0
  147. package/dist/src/popover/index.d.ts +2 -0
  148. package/dist/src/popover/popover-content.d.ts +29 -0
  149. package/dist/src/popover/popover.d.ts +62 -0
  150. package/dist/src/sidebar-menu/index.d.ts +3 -0
  151. package/dist/src/sidebar-menu/sidebar-menu-item.d.ts +58 -0
  152. package/dist/src/sidebar-menu/sidebar-menu.d.ts +38 -0
  153. package/dist/src/sidebar-menu/sidebar-sub-menu.d.ts +35 -0
  154. package/dist/src/split-button/index.d.ts +1 -0
  155. package/dist/src/split-button/split-button.d.ts +72 -0
  156. package/dist/src/toolbar/toolbar.d.ts +10 -10
  157. package/dist/src/tooltip/tooltip.d.ts +5 -15
  158. package/dist/src/url-field/index.d.ts +1 -0
  159. package/dist/src/url-field/url-field.d.ts +48 -0
  160. package/dist/test/flow-designer.test.d.ts +1 -0
  161. package/dist/test/sidebar-menu.test.d.ts +1 -0
  162. package/dist/toolbar.js +10 -10
  163. package/dist/toolbar.js.map +1 -1
  164. package/dist/tsconfig.tsbuildinfo +1 -1
  165. package/package.json +4 -2
  166. package/readme.md +73 -65
  167. package/scss/mixin.scss +16 -0
  168. package/src/__controllers/floating-controller.ts +237 -0
  169. package/src/__mixins/BaseButtonMixin.ts +83 -0
  170. package/src/__mixins/BaseHyperlinkMixin.ts +68 -0
  171. package/src/__mixins/MixinConstructor.ts +1 -0
  172. package/src/{__base_element → __mixins}/README.md +2 -2
  173. package/src/banner/banner.scss +20 -25
  174. package/src/banner/banner.ts +1 -7
  175. package/src/button/BaseButton.ts +11 -100
  176. package/src/button/button/button-sizes.scss +4 -2
  177. package/src/button/button/button.ts +77 -23
  178. package/src/button/button-group/button-group.ts +2 -2
  179. package/src/button/icon-button/icon-button.ts +75 -33
  180. package/src/calendar/base-event.ts +49 -0
  181. package/src/calendar/calendar-column-view.scss +326 -0
  182. package/src/calendar/calendar-column-view.ts +392 -0
  183. package/src/calendar/calendar-event.ts +20 -0
  184. package/src/calendar/calendar-month-view.scss +192 -0
  185. package/src/calendar/calendar-month-view.ts +244 -0
  186. package/src/calendar/calendar.scss +71 -0
  187. package/src/calendar/calendar.ts +298 -0
  188. package/src/calendar/event-manager.ts +117 -0
  189. package/src/calendar/index.ts +4 -0
  190. package/src/calendar/types.ts +14 -0
  191. package/src/calendar/utils.ts +180 -0
  192. package/src/canvas/canvas.scss +60 -0
  193. package/src/canvas/canvas.ts +391 -0
  194. package/src/canvas/index.ts +2 -0
  195. package/src/card/card.ts +11 -71
  196. package/src/chart-bar/chart-bar.ts +9 -14
  197. package/src/chart-bar/chart-stacked-bar.ts +12 -18
  198. package/src/chart-doughnut/chart-doughnut.ts +23 -27
  199. package/src/chart-pie/chart-pie.ts +19 -23
  200. package/src/checkbox/checkbox.scss +17 -34
  201. package/src/checkbox/checkbox.ts +3 -1
  202. package/src/code-highlighter/code-highlighter.scss +1 -0
  203. package/src/code-highlighter/code-highlighter.ts +1 -1
  204. package/src/condition-builder/cb-compound-expression.scss +37 -0
  205. package/src/condition-builder/cb-compound-expression.ts +80 -0
  206. package/src/condition-builder/cb-divider.scss +93 -0
  207. package/src/condition-builder/cb-divider.ts +56 -0
  208. package/src/condition-builder/cb-expression.scss +14 -0
  209. package/src/condition-builder/cb-expression.ts +49 -0
  210. package/src/condition-builder/cb-predicate.scss +35 -0
  211. package/src/condition-builder/cb-predicate.ts +102 -0
  212. package/src/condition-builder/condition-builder.scss +13 -0
  213. package/src/condition-builder/condition-builder.ts +38 -0
  214. package/src/condition-builder/index.ts +5 -0
  215. package/src/date-picker/date-picker.ts +1 -1
  216. package/src/dropdown-button/demo/index.html +110 -0
  217. package/src/dropdown-button/dropdown-button.scss +22 -0
  218. package/src/dropdown-button/dropdown-button.ts +206 -0
  219. package/src/dropdown-button/index.ts +1 -0
  220. package/src/elevation/elevation.scss +5 -5
  221. package/src/fab/fab.ts +29 -100
  222. package/src/flow-designer/DEMO.md +239 -0
  223. package/src/flow-designer/commands.ts +278 -0
  224. package/src/flow-designer/flow-designer-node.ts +172 -0
  225. package/src/flow-designer/flow-designer.scss +457 -0
  226. package/src/flow-designer/flow-designer.ts +611 -0
  227. package/src/flow-designer/index.ts +41 -0
  228. package/src/flow-designer/layout.ts +357 -0
  229. package/src/flow-designer/types.ts +166 -0
  230. package/src/flow-designer/validation.ts +284 -0
  231. package/src/flow-designer/workflow-utils.ts +282 -0
  232. package/src/focus-ring/focus-ring.ts +47 -40
  233. package/src/html-editor/html-editor.scss +146 -0
  234. package/src/html-editor/html-editor.ts +276 -0
  235. package/src/html-editor/index.ts +3 -0
  236. package/src/index.ts +28 -1
  237. package/src/input/input.ts +3 -1
  238. package/src/link/link.ts +2 -2
  239. package/src/menu/menu/menu.scss +2 -2
  240. package/src/menu/menu/menu.ts +91 -101
  241. package/src/menu/menu-item/menu-item.scss +4 -0
  242. package/src/menu/menu-item/menu-item.ts +85 -79
  243. package/src/modal/index.ts +1 -0
  244. package/src/modal/modal.scss +206 -0
  245. package/src/modal/modal.ts +201 -0
  246. package/src/navigation-rail/index.ts +2 -0
  247. package/src/navigation-rail/navigation-rail-item.scss +216 -0
  248. package/src/navigation-rail/navigation-rail-item.ts +223 -0
  249. package/src/navigation-rail/navigation-rail.scss +72 -0
  250. package/src/navigation-rail/navigation-rail.ts +149 -0
  251. package/src/notification/notification.ts +3 -2
  252. package/src/notification-manager/index.ts +1 -0
  253. package/src/notification-manager/notification-manager.scss +113 -0
  254. package/src/notification-manager/notification-manager.ts +199 -0
  255. package/src/number-field/number-field.ts +6 -4
  256. package/src/pagination/pagination.ts +6 -4
  257. package/src/peacock-loader.ts +93 -5
  258. package/src/popover/index.ts +2 -0
  259. package/src/popover/popover-content.scss +69 -0
  260. package/src/popover/popover-content.ts +51 -0
  261. package/src/popover/popover.scss +7 -0
  262. package/src/popover/popover.ts +170 -0
  263. package/src/search/search.ts +4 -0
  264. package/src/sidebar-menu/demo/index.html +68 -0
  265. package/src/sidebar-menu/index.ts +3 -0
  266. package/src/sidebar-menu/sidebar-menu-item.scss +102 -0
  267. package/src/sidebar-menu/sidebar-menu-item.ts +151 -0
  268. package/src/{tree-view/tree-view.scss → sidebar-menu/sidebar-menu.scss} +1 -1
  269. package/src/sidebar-menu/sidebar-menu.ts +182 -0
  270. package/src/sidebar-menu/sidebar-sub-menu.scss +130 -0
  271. package/src/sidebar-menu/sidebar-sub-menu.ts +160 -0
  272. package/src/skeleton/skeleton.scss +18 -24
  273. package/src/snackbar/snackbar.ts +1 -1
  274. package/src/split-button/index.ts +1 -0
  275. package/src/split-button/split-button-colors.scss +56 -0
  276. package/src/split-button/split-button-sizes.scss +28 -0
  277. package/src/split-button/split-button.scss +79 -0
  278. package/src/split-button/split-button.ts +236 -0
  279. package/src/table/table.ts +2 -2
  280. package/src/tabs/tab.ts +4 -3
  281. package/src/text/text.css-component.scss +7 -1
  282. package/src/time-picker/time-picker.ts +1 -1
  283. package/src/toolbar/toolbar.ts +10 -10
  284. package/src/tooltip/tooltip.scss +4 -3
  285. package/src/tooltip/tooltip.ts +64 -98
  286. package/src/url-field/index.ts +1 -0
  287. package/src/url-field/url-field.scss +50 -0
  288. package/src/url-field/url-field.ts +239 -0
  289. package/dist/button-DMN1dPAg.js.map +0 -1
  290. package/dist/button-group-CX9CUUXk.js +0 -435
  291. package/dist/button-group-CX9CUUXk.js.map +0 -1
  292. package/dist/fab-C5Nzxk0E.js +0 -497
  293. package/dist/fab-C5Nzxk0E.js.map +0 -1
  294. package/dist/is-dark-mode-DicqGkCJ.js.map +0 -1
  295. package/dist/select-4pl4XBj7.js.map +0 -1
  296. package/dist/spread-B5cgadZl.js +0 -32
  297. package/dist/spread-B5cgadZl.js.map +0 -1
  298. package/dist/src/__base_element/BaseHyperlink.d.ts +0 -20
  299. package/dist/src/menu/menu/MenuSurfaceController.d.ts +0 -18
  300. package/dist/src/tree-view/index.d.ts +0 -2
  301. package/dist/src/tree-view/tree-node.d.ts +0 -69
  302. package/dist/src/tree-view/tree-view.d.ts +0 -40
  303. package/dist/src/tree-view/wc-tree-view.d.ts +0 -6
  304. package/dist/test/tree-view.test.d.ts +0 -1
  305. package/dist/throttle-C7ZAPqtu.js +0 -24
  306. package/dist/throttle-C7ZAPqtu.js.map +0 -1
  307. package/src/__base_element/BaseHyperlink.ts +0 -42
  308. package/src/menu/menu/MenuSurfaceController.ts +0 -61
  309. package/src/tree-view/demo/index.html +0 -57
  310. package/src/tree-view/index.ts +0 -2
  311. package/src/tree-view/tree-node.scss +0 -101
  312. package/src/tree-view/tree-node.ts +0 -268
  313. package/src/tree-view/tree-view.ts +0 -182
  314. package/src/tree-view/wc-tree-view.ts +0 -9
@@ -0,0 +1,170 @@
1
+ import { html, LitElement } from 'lit';
2
+ import { property } from 'lit/decorators.js';
3
+ import type { Placement } from '@floating-ui/dom';
4
+ import IndividualComponent from '@/IndividualComponent.js';
5
+ import { FloatingController } from '../__controllers/floating-controller.js';
6
+ import styles from './popover.scss';
7
+ import type { PopoverContent } from './popover-content.js';
8
+
9
+ /**
10
+ * @label Popover
11
+ * @tag wc-popover
12
+ * @rawTag popover
13
+ * @summary Displays additional information in a floating panel anchored to a trigger element.
14
+ * @overview
15
+ * <p>The Popover component wraps a trigger element and a <code>wc-popover-content</code> child. It uses
16
+ * floating-ui to compute position, keeping the panel visible inside the viewport even on scroll.</p>
17
+ * @tags display
18
+ *
19
+ * @fires {CustomEvent} wc-popover--open - Fired when the popover opens.
20
+ * @fires {CustomEvent} wc-popover--close - Fired when the popover closes.
21
+ *
22
+ * @example
23
+ * ```html
24
+ * <wc-popover trigger="click">
25
+ * <wc-button>Open popover</wc-button>
26
+ * <wc-popover-content>
27
+ * <p>Popover body text goes here.</p>
28
+ * </wc-popover-content>
29
+ * </wc-popover>
30
+ * ```
31
+ */
32
+ @IndividualComponent
33
+ export class Popover extends LitElement {
34
+ static styles = [styles];
35
+
36
+ /**
37
+ * Determines how the popover is triggered.
38
+ * Possible values are `"click"`, `"hover"`, `"manual"`.
39
+ */
40
+ @property({ reflect: true }) trigger: 'click' | 'hover' | 'manual' = 'click';
41
+
42
+ /**
43
+ * Preferred placement of the popover relative to the trigger element.
44
+ * Accepts any floating-ui `Placement` string such as `"bottom"`, `"top-start"`, `"right"`, etc.
45
+ */
46
+ @property({ reflect: true }) placement: Placement = 'bottom';
47
+
48
+ /**
49
+ * Whether the popover is open.
50
+ */
51
+ @property({ type: Boolean, reflect: true }) open = false;
52
+
53
+ /**
54
+ * Distance in pixels between the trigger element and the popover panel.
55
+ */
56
+ @property({ type: Number }) offset = 8;
57
+
58
+ private _floating: FloatingController | null = null;
59
+
60
+ private _contentEl: PopoverContent | null = null;
61
+
62
+ private _triggerEl: HTMLElement | null = null;
63
+
64
+ private _setupFloating() {
65
+ // Tear down any existing controller
66
+ if (this._floating) {
67
+ this._floating = null;
68
+ }
69
+
70
+ // Resolve the content element
71
+ this._contentEl = this.querySelector<PopoverContent>('wc-popover-content');
72
+
73
+ // Resolve the trigger element: first light-DOM child that is NOT wc-popover-content
74
+ this._triggerEl =
75
+ (Array.from(this.children).find(
76
+ (c) => c.tagName.toLowerCase() !== 'wc-popover-content',
77
+ ) as HTMLElement) ?? null;
78
+
79
+ if (!this._triggerEl || !this._contentEl) return;
80
+
81
+ const triggerMode =
82
+ this.trigger === 'manual'
83
+ ? 'manual'
84
+ : (this.trigger as 'click' | 'hover');
85
+
86
+ this._floating = new FloatingController(this, {
87
+ placement: this.placement,
88
+ strategy: 'fixed',
89
+ offset: this.offset,
90
+ trigger: triggerMode,
91
+ closeOnClickOutside: true,
92
+ onOpenChange: (isOpen) => {
93
+ this.open = isOpen;
94
+ if (this._contentEl) {
95
+ this._contentEl.open = isOpen;
96
+ }
97
+ this.dispatchEvent(
98
+ new CustomEvent(isOpen ? 'wc-popover--open' : 'wc-popover--close', {
99
+ bubbles: true,
100
+ composed: true,
101
+ }),
102
+ );
103
+ },
104
+ });
105
+
106
+ this._floating.setElements(
107
+ this._triggerEl,
108
+ this._contentEl as unknown as HTMLElement,
109
+ );
110
+
111
+ if (this.open) {
112
+ this._floating.open();
113
+ this._contentEl.open = true;
114
+ }
115
+ }
116
+
117
+ connectedCallback() {
118
+ super.connectedCallback();
119
+ }
120
+
121
+ firstUpdated() {
122
+ this._setupFloating();
123
+ }
124
+
125
+ updated(changedProps: Map<string, unknown>) {
126
+ if (
127
+ changedProps.has('trigger') ||
128
+ changedProps.has('placement') ||
129
+ changedProps.has('offset')
130
+ ) {
131
+ this._setupFloating();
132
+ }
133
+
134
+ if (changedProps.has('open') && this._floating) {
135
+ if (this.open && !this._floating.isOpen) {
136
+ this._floating.open();
137
+ if (this._contentEl) this._contentEl.open = true;
138
+ } else if (!this.open && this._floating.isOpen) {
139
+ this._floating.close();
140
+ if (this._contentEl) this._contentEl.open = false;
141
+ }
142
+ }
143
+ }
144
+
145
+ /**
146
+ * Programmatically opens the popover.
147
+ */
148
+ show() {
149
+ if (this._floating && !this._floating.isOpen) {
150
+ this._floating.open();
151
+ } else if (!this._floating) {
152
+ this.open = true;
153
+ }
154
+ }
155
+
156
+ /**
157
+ * Programmatically closes the popover.
158
+ */
159
+ hide() {
160
+ if (this._floating && this._floating.isOpen) {
161
+ this._floating.close();
162
+ } else if (!this._floating) {
163
+ this.open = false;
164
+ }
165
+ }
166
+
167
+ render() {
168
+ return html`<slot></slot>`;
169
+ }
170
+ }
@@ -110,6 +110,8 @@ export class Search extends LitElement {
110
110
  private __handleInput(event: InputEvent) {
111
111
  const input = event.target as HTMLInputElement;
112
112
  this.value = input.value;
113
+ // Prevent the native input event from escaping in addition to our API event.
114
+ event.stopPropagation();
113
115
  this.dispatchEvent(
114
116
  new CustomEvent('input', {
115
117
  detail: { value: this.value },
@@ -122,6 +124,8 @@ export class Search extends LitElement {
122
124
  private __handleChange(event: Event) {
123
125
  const input = event.target as HTMLInputElement;
124
126
  this.value = input.value;
127
+ // Prevent the native change event from escaping in addition to our API event.
128
+ event.stopPropagation();
125
129
  this.dispatchEvent(
126
130
  new CustomEvent('change', {
127
131
  detail: { value: this.value },
@@ -0,0 +1,68 @@
1
+ <!doctype html>
2
+ <html lang='en-GB'>
3
+ <head>
4
+ <meta charset='utf-8'>
5
+ <meta name='viewport' content='width=device-width, initial-scale=1.0, viewport-fit=cover' />
6
+ <link rel='stylesheet' href='/dist/assets/styles/tokens.css' />
7
+ <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+Mono:wght@100..900&family=Noto+Sans:ital,wght@0,100..900;1,100..900&display=swap" rel="stylesheet">
8
+
9
+ <style>
10
+ body {
11
+ background: #fafafa;
12
+ padding: 2rem;
13
+ font-family: 'Noto Sans', sans-serif;
14
+ }
15
+ h2 {
16
+ margin-top: 2rem;
17
+ margin-bottom: 0.5rem;
18
+ }
19
+ </style>
20
+ </head>
21
+ <body>
22
+
23
+ <h2>Basic Sidebar Menu</h2>
24
+ <wc-sidebar-menu>
25
+ <wc-sidebar-sub-menu label="Documents" icon="folder" expanded>
26
+ <wc-sidebar-sub-menu label="Work" icon="folder" expanded>
27
+ <wc-sidebar-menu-item label="Project A" icon="description"></wc-sidebar-menu-item>
28
+ <wc-sidebar-menu-item label="Project B" icon="description"></wc-sidebar-menu-item>
29
+ </wc-sidebar-sub-menu>
30
+ <wc-sidebar-menu-item label="Personal" icon="folder"></wc-sidebar-menu-item>
31
+ </wc-sidebar-sub-menu>
32
+ <wc-sidebar-sub-menu label="Pictures" icon="image" expanded>
33
+ <wc-sidebar-menu-item label="Vacation 2024" icon="image"></wc-sidebar-menu-item>
34
+ <wc-sidebar-menu-item label="Family" icon="image"></wc-sidebar-menu-item>
35
+ </wc-sidebar-sub-menu>
36
+ </wc-sidebar-menu>
37
+
38
+ <h2>Sidebar Menu without icons</h2>
39
+ <wc-sidebar-menu>
40
+ <wc-sidebar-sub-menu label="Category 1" expanded>
41
+ <wc-sidebar-menu-item label="Subcategory 1.1"></wc-sidebar-menu-item>
42
+ <wc-sidebar-menu-item label="Subcategory 1.2"></wc-sidebar-menu-item>
43
+ </wc-sidebar-sub-menu>
44
+ <wc-sidebar-menu-item label="Category 2"></wc-sidebar-menu-item>
45
+ </wc-sidebar-menu>
46
+
47
+ <h2>Disabled items</h2>
48
+ <wc-sidebar-menu>
49
+ <wc-sidebar-sub-menu label="Enabled" expanded>
50
+ <wc-sidebar-menu-item label="Child 1"></wc-sidebar-menu-item>
51
+ <wc-sidebar-menu-item label="Child 2" disabled></wc-sidebar-menu-item>
52
+ </wc-sidebar-sub-menu>
53
+ <wc-sidebar-menu-item label="Disabled" disabled></wc-sidebar-menu-item>
54
+ </wc-sidebar-menu>
55
+
56
+ <script type='module'>
57
+ import '/dist/index.es2017.js';
58
+
59
+ const menu = document.querySelector('wc-sidebar-menu');
60
+ if (menu) {
61
+ menu.addEventListener('sidebar-menu:change', (e) => {
62
+ console.log('Selected item:', e.detail);
63
+ });
64
+ }
65
+ </script>
66
+
67
+ </body>
68
+ </html>
@@ -0,0 +1,3 @@
1
+ export { SidebarMenuItem } from './sidebar-menu-item.js';
2
+ export { SidebarSubMenu } from './sidebar-sub-menu.js';
3
+ export { SidebarMenu } from './sidebar-menu.js';
@@ -0,0 +1,102 @@
1
+ @use '../../scss/mixin';
2
+
3
+ @include mixin.base-styles;
4
+
5
+ :host {
6
+ display: block;
7
+
8
+ // M3 sidebar menu item sizing
9
+ --sidebar-menu-item-height: 3rem;
10
+ --sidebar-menu-item-icon-size: 1.25rem;
11
+ --sidebar-menu-item-border-radius: var(--global-shape-corner-full, 9999px);
12
+
13
+ // M3 color tokens
14
+ --sidebar-menu-item-label-color: var(--color-on-surface);
15
+ --sidebar-menu-item-icon-color: var(--color-on-surface-variant);
16
+ --sidebar-menu-item-selected-background: var(--color-secondary-container);
17
+ --sidebar-menu-item-selected-color: var(--color-on-secondary-container);
18
+ --sidebar-menu-item-focus-ring-color: var(--color-primary);
19
+
20
+ // M3 animation timing
21
+ --sidebar-menu-item-transition-duration: 200ms;
22
+ --sidebar-menu-item-transition-easing: cubic-bezier(0.4, 0, 0.2, 1);
23
+ }
24
+
25
+ .sidebar-menu-item {
26
+ position: relative;
27
+ display: flex;
28
+ align-items: center;
29
+ min-height: var(--sidebar-menu-item-height);
30
+ border-radius: var(--sidebar-menu-item-border-radius);
31
+ color: var(--_label-color);
32
+ cursor: pointer;
33
+ user-select: none;
34
+ text-decoration: none;
35
+ outline: none;
36
+
37
+ --_container-color: transparent;
38
+ --_container-opacity: 1;
39
+ --_label-color: var(--sidebar-menu-item-label-color);
40
+
41
+ @include mixin.get-typography(label-large);
42
+
43
+ &.selected {
44
+ --_container-color: var(--sidebar-menu-item-selected-background);
45
+ --_label-color: var(--sidebar-menu-item-selected-color);
46
+ }
47
+
48
+ &.disabled {
49
+ cursor: not-allowed;
50
+ opacity: 0.6;
51
+
52
+ .ripple {
53
+ display: none;
54
+ }
55
+ }
56
+ }
57
+
58
+ .sidebar-menu-item-content {
59
+ display: flex;
60
+ align-items: center;
61
+ gap: 0.25rem;
62
+ flex: 1;
63
+ min-height: var(--sidebar-menu-item-height);
64
+ padding-inline: 0.75rem;
65
+ color: inherit;
66
+ z-index: 1;
67
+ }
68
+
69
+ .background {
70
+ position: absolute;
71
+ inset: 0;
72
+ background-color: var(--_container-color);
73
+ opacity: var(--_container-opacity);
74
+ border-radius: inherit;
75
+ pointer-events: none;
76
+ }
77
+
78
+ .focus-ring {
79
+ z-index: 2;
80
+ --focus-ring-color: var(--sidebar-menu-item-focus-ring-color);
81
+ --focus-ring-container-shape-start-start: var(--sidebar-menu-item-border-radius);
82
+ --focus-ring-container-shape-start-end: var(--sidebar-menu-item-border-radius);
83
+ --focus-ring-container-shape-end-start: var(--sidebar-menu-item-border-radius);
84
+ --focus-ring-container-shape-end-end: var(--sidebar-menu-item-border-radius);
85
+ }
86
+
87
+ .ripple {
88
+ --ripple-pressed-color: var(--color-on-surface);
89
+ border-radius: inherit;
90
+ }
91
+
92
+ .sidebar-menu-item.selected .ripple {
93
+ --ripple-pressed-color: var(--sidebar-menu-item-selected-color);
94
+ }
95
+
96
+ .sidebar-menu-item-label {
97
+ flex: 1;
98
+ overflow: hidden;
99
+ text-overflow: ellipsis;
100
+ white-space: nowrap;
101
+ }
102
+
@@ -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
+ }