@redvars/peacock 3.3.3 → 3.5.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 (280) hide show
  1. package/dist/IndividualComponent-DUINtMGK.js +67 -0
  2. package/dist/IndividualComponent-DUINtMGK.js.map +1 -0
  3. package/dist/assets/images/empty-state/no-document.svg +11 -12
  4. package/dist/assets/images/empty-state/page.svg +15 -9
  5. package/dist/assets/styles.css +1 -1
  6. package/dist/assets/styles.css.map +1 -1
  7. package/dist/banner.js +202 -0
  8. package/dist/banner.js.map +1 -0
  9. package/dist/bottom-sheet.js +238 -0
  10. package/dist/bottom-sheet.js.map +1 -0
  11. package/dist/{button-ClzS8JLq.js → button-DMN1dPAg.js} +358 -218
  12. package/dist/button-DMN1dPAg.js.map +1 -0
  13. package/dist/button-group-CX9CUUXk.js +435 -0
  14. package/dist/button-group-CX9CUUXk.js.map +1 -0
  15. package/dist/button-group.js +11 -6
  16. package/dist/button-group.js.map +1 -1
  17. package/dist/button.js +10 -5
  18. package/dist/button.js.map +1 -1
  19. package/dist/card-content.js +29 -0
  20. package/dist/card-content.js.map +1 -0
  21. package/dist/card.js +428 -44
  22. package/dist/card.js.map +1 -1
  23. package/dist/{chart-bar-DbnXQgvS.js → chart-bar-cn6rrna-.js} +2 -2
  24. package/dist/{chart-bar-DbnXQgvS.js.map → chart-bar-cn6rrna-.js.map} +1 -1
  25. package/dist/chart-bar.js +5 -4
  26. package/dist/chart-bar.js.map +1 -1
  27. package/dist/chart-doughnut.js +2 -1
  28. package/dist/chart-doughnut.js.map +1 -1
  29. package/dist/chart-pie.js +2 -1
  30. package/dist/chart-pie.js.map +1 -1
  31. package/dist/chart-stacked-bar.js +5 -4
  32. package/dist/chart-stacked-bar.js.map +1 -1
  33. package/dist/{class-map-59YGWLnx.js → class-map-YU7g0o3B.js} +4 -10
  34. package/dist/class-map-YU7g0o3B.js.map +1 -0
  35. package/dist/clock.js +2 -1
  36. package/dist/clock.js.map +1 -1
  37. package/dist/code-editor.js +8 -6
  38. package/dist/code-editor.js.map +1 -1
  39. package/dist/code-highlighter.js +6 -4
  40. package/dist/code-highlighter.js.map +1 -1
  41. package/dist/custom-elements-jsdocs.json +6270 -5026
  42. package/dist/custom-elements.json +5763 -2049
  43. package/dist/directive-ZPhl09Yt.js +9 -0
  44. package/dist/directive-ZPhl09Yt.js.map +1 -0
  45. package/dist/dispatch-event-utils-CuEqjlPT.js +127 -0
  46. package/dist/dispatch-event-utils-CuEqjlPT.js.map +1 -0
  47. package/dist/fab-C5Nzxk0E.js +497 -0
  48. package/dist/fab-C5Nzxk0E.js.map +1 -0
  49. package/dist/fab.js +11 -0
  50. package/dist/fab.js.map +1 -0
  51. package/dist/index.js +24 -12
  52. package/dist/index.js.map +1 -1
  53. package/dist/{observe-theme-change-pALI5fmV.js → is-dark-mode-DicqGkCJ.js} +8 -3
  54. package/dist/is-dark-mode-DicqGkCJ.js.map +1 -0
  55. package/dist/notification.js +417 -0
  56. package/dist/notification.js.map +1 -0
  57. package/dist/number-counter.js +4 -3
  58. package/dist/number-counter.js.map +1 -1
  59. package/dist/observe-slot-change-BGJfgg2E.js +31 -0
  60. package/dist/observe-slot-change-BGJfgg2E.js.map +1 -0
  61. package/dist/peacock-loader.js +59 -10
  62. package/dist/peacock-loader.js.map +1 -1
  63. package/dist/property-1psGvXOq.js +10 -0
  64. package/dist/property-1psGvXOq.js.map +1 -0
  65. package/dist/search.js +452 -0
  66. package/dist/search.js.map +1 -0
  67. package/dist/{radio-b70_Ie9n.js → select-4pl4XBj7.js} +2439 -521
  68. package/dist/select-4pl4XBj7.js.map +1 -0
  69. package/dist/side-sheet.js +186 -0
  70. package/dist/side-sheet.js.map +1 -0
  71. package/dist/spread-B5cgadZl.js +32 -0
  72. package/dist/spread-B5cgadZl.js.map +1 -0
  73. package/dist/src/__base_element/BaseHyperlink.d.ts +20 -0
  74. package/dist/src/__utils/cache-fetch.d.ts +1 -0
  75. package/dist/src/__utils/is-dark-mode.d.ts +1 -0
  76. package/dist/src/__utils/is-in-viewport.d.ts +1 -0
  77. package/dist/src/__utils/observe-slot-change.d.ts +1 -0
  78. package/dist/src/__utils/sanitize-svg.d.ts +1 -0
  79. package/dist/src/__utils/throttle.d.ts +4 -0
  80. package/dist/src/accordion/accordion-item.d.ts +33 -9
  81. package/dist/src/accordion/accordion.d.ts +21 -5
  82. package/dist/src/banner/banner.d.ts +47 -0
  83. package/dist/src/banner/index.d.ts +1 -0
  84. package/dist/src/bottom-sheet/bottom-sheet.d.ts +42 -0
  85. package/dist/src/bottom-sheet/index.d.ts +1 -0
  86. package/dist/src/button/BaseButton.d.ts +7 -13
  87. package/dist/src/button/button/button.d.ts +4 -0
  88. package/dist/src/button/button-group/button-group.d.ts +32 -3
  89. package/dist/src/button/icon-button/icon-button.d.ts +4 -0
  90. package/dist/src/card/card-content.d.ts +15 -0
  91. package/dist/src/card/card.d.ts +37 -3
  92. package/dist/src/card/index.d.ts +1 -0
  93. package/dist/src/container/container.d.ts +1 -1
  94. package/dist/src/empty-state/empty-state.d.ts +1 -1
  95. package/dist/src/fab/fab.d.ts +111 -0
  96. package/dist/src/fab/index.d.ts +1 -0
  97. package/dist/src/focus-ring/focus-ring.d.ts +4 -1
  98. package/dist/src/index.d.ts +11 -1
  99. package/dist/src/link/link.d.ts +3 -10
  100. package/dist/src/menu/menu/menu.d.ts +4 -2
  101. package/dist/src/menu/menu-item/menu-item.d.ts +0 -1
  102. package/dist/src/menu/sub-menu/sub-menu.d.ts +1 -0
  103. package/dist/src/notification/index.d.ts +1 -0
  104. package/dist/src/notification/notification.d.ts +69 -0
  105. package/dist/src/pagination/pagination.d.ts +8 -1
  106. package/dist/src/ripple/ripple.d.ts +19 -3
  107. package/dist/src/search/index.d.ts +1 -0
  108. package/dist/src/search/search.d.ts +76 -0
  109. package/dist/src/segmented-button/index.d.ts +2 -0
  110. package/dist/src/segmented-button/segmented-button-group.d.ts +46 -0
  111. package/dist/src/segmented-button/segmented-button.d.ts +65 -0
  112. package/dist/src/select/index.d.ts +3 -0
  113. package/dist/src/select/option.d.ts +55 -0
  114. package/dist/src/select/select.d.ts +114 -0
  115. package/dist/src/side-sheet/index.d.ts +1 -0
  116. package/dist/src/side-sheet/side-sheet.d.ts +41 -0
  117. package/dist/src/slider/slider.d.ts +4 -0
  118. package/dist/src/snackbar/snackbar.d.ts +14 -1
  119. package/dist/src/tabs/tab-group.d.ts +0 -1
  120. package/dist/src/tabs/tab.d.ts +8 -2
  121. package/dist/src/tabs/tabs.d.ts +13 -1
  122. package/dist/src/toolbar/index.d.ts +1 -0
  123. package/dist/src/toolbar/toolbar.d.ts +86 -0
  124. package/dist/state-DwbEjqVk.js +10 -0
  125. package/dist/state-DwbEjqVk.js.map +1 -0
  126. package/dist/{style-map-DcB52w-l.js → style-map-DVmWOuYy.js} +3 -3
  127. package/dist/{style-map-DcB52w-l.js.map → style-map-DVmWOuYy.js.map} +1 -1
  128. package/dist/test/search.test.d.ts +1 -0
  129. package/dist/test/toolbar.test.d.ts +1 -0
  130. package/dist/throttle-C7ZAPqtu.js +24 -0
  131. package/dist/throttle-C7ZAPqtu.js.map +1 -0
  132. package/dist/toolbar.js +306 -0
  133. package/dist/toolbar.js.map +1 -0
  134. package/dist/tsconfig.tsbuildinfo +1 -1
  135. package/dist/{unsafe-html-C2r3PyzF.js → unsafe-html-BsGUjx94.js} +3 -3
  136. package/dist/{unsafe-html-C2r3PyzF.js.map → unsafe-html-BsGUjx94.js.map} +1 -1
  137. package/package.json +1 -1
  138. package/readme.md +2 -2
  139. package/scss/styles.scss +4 -0
  140. package/src/__base_element/BaseHyperlink.ts +42 -0
  141. package/src/__base_element/README.md +19 -0
  142. package/src/__utils/cache-fetch.ts +65 -0
  143. package/src/{utils → __utils}/dispatch-event-utils.ts +1 -0
  144. package/src/__utils/is-dark-mode.ts +3 -0
  145. package/src/__utils/is-in-viewport.ts +6 -0
  146. package/src/__utils/observe-slot-change.ts +38 -0
  147. package/src/__utils/sanitize-svg.ts +27 -0
  148. package/src/__utils/throttle.ts +27 -0
  149. package/src/accordion/accordion-item.scss +136 -65
  150. package/src/accordion/accordion-item.ts +117 -44
  151. package/src/accordion/accordion.scss +24 -5
  152. package/src/accordion/accordion.ts +29 -23
  153. package/src/accordion/demo/index.html +74 -35
  154. package/src/banner/banner.scss +87 -0
  155. package/src/banner/banner.ts +107 -0
  156. package/src/banner/index.ts +1 -0
  157. package/src/bottom-sheet/bottom-sheet.scss +88 -0
  158. package/src/bottom-sheet/bottom-sheet.ts +135 -0
  159. package/src/bottom-sheet/index.ts +1 -0
  160. package/src/button/BaseButton.ts +26 -30
  161. package/src/button/button/button-colors.scss +90 -19
  162. package/src/button/button/button-sizes.scss +39 -19
  163. package/src/button/button/button.scss +117 -116
  164. package/src/button/button/button.ts +29 -6
  165. package/src/button/button-group/button-group.scss +25 -22
  166. package/src/button/button-group/button-group.ts +122 -5
  167. package/src/button/icon-button/icon-button-sizes.scss +35 -15
  168. package/src/button/icon-button/icon-button.ts +25 -12
  169. package/src/card/card-colors.scss +10 -0
  170. package/src/card/card-content.ts +26 -0
  171. package/src/card/card.scss +221 -41
  172. package/src/card/card.ts +251 -8
  173. package/src/card/index.ts +1 -0
  174. package/src/chart-bar/chart-bar.ts +1 -1
  175. package/src/chart-bar/chart-stacked-bar.ts +3 -1
  176. package/src/chart-doughnut/chart-doughnut.ts +1 -1
  177. package/src/chart-pie/chart-pie.ts +1 -1
  178. package/src/checkbox/checkbox.ts +1 -1
  179. package/src/clock/clock.ts +1 -1
  180. package/src/code-editor/code-editor.ts +5 -5
  181. package/src/code-highlighter/code-highlighter.ts +2 -2
  182. package/src/container/container.ts +1 -1
  183. package/src/date-picker/date-picker.ts +5 -2
  184. package/src/divider/divider.ts +3 -1
  185. package/src/empty-state/empty-state.scss +9 -3
  186. package/src/empty-state/empty-state.ts +2 -2
  187. package/src/fab/fab-colors.scss +49 -0
  188. package/src/fab/fab-sizes.scss +47 -0
  189. package/src/fab/fab.scss +137 -0
  190. package/src/fab/fab.ts +285 -0
  191. package/src/fab/index.ts +1 -0
  192. package/src/field/field.ts +3 -1
  193. package/src/focus-ring/focus-ring.ts +37 -19
  194. package/src/icon/datasource.ts +1 -1
  195. package/src/icon/icon.ts +3 -1
  196. package/src/image/image.ts +3 -2
  197. package/src/index.ts +12 -1
  198. package/src/input/input.ts +5 -2
  199. package/src/link/link.ts +2 -15
  200. package/src/menu/menu/menu.scss +31 -3
  201. package/src/menu/menu/menu.ts +30 -6
  202. package/src/menu/menu-item/menu-item.scss +1 -0
  203. package/src/menu/menu-item/menu-item.ts +1 -9
  204. package/src/menu/sub-menu/sub-menu.ts +1 -0
  205. package/src/notification/index.ts +1 -0
  206. package/src/notification/notification.scss +201 -0
  207. package/src/notification/notification.ts +206 -0
  208. package/src/number-counter/number-counter.ts +3 -1
  209. package/src/number-field/number-field.ts +4 -2
  210. package/src/pagination/pagination.scss +33 -24
  211. package/src/pagination/pagination.ts +113 -60
  212. package/src/peacock-loader.ts +48 -0
  213. package/src/radio/radio.ts +3 -1
  214. package/src/ripple/ripple.ts +19 -3
  215. package/src/search/index.ts +1 -0
  216. package/src/search/search-colors.scss +14 -0
  217. package/src/search/search.scss +204 -0
  218. package/src/search/search.ts +240 -0
  219. package/src/segmented-button/index.ts +2 -0
  220. package/src/segmented-button/segmented-button-group.scss +21 -0
  221. package/src/segmented-button/segmented-button-group.ts +110 -0
  222. package/src/segmented-button/segmented-button.scss +115 -0
  223. package/src/segmented-button/segmented-button.ts +175 -0
  224. package/src/select/index.ts +3 -0
  225. package/src/select/option.ts +109 -0
  226. package/src/select/select.scss +125 -0
  227. package/src/select/select.ts +520 -0
  228. package/src/side-sheet/index.ts +1 -0
  229. package/src/side-sheet/side-sheet.scss +79 -0
  230. package/src/side-sheet/side-sheet.ts +100 -0
  231. package/src/slider/slider.scss +19 -1
  232. package/src/slider/slider.ts +30 -19
  233. package/src/snackbar/snackbar.scss +62 -31
  234. package/src/snackbar/snackbar.ts +92 -12
  235. package/src/switch/switch.ts +3 -1
  236. package/src/table/table.ts +3 -1
  237. package/src/tabs/demo/index.html +90 -0
  238. package/src/tabs/tab-group.ts +0 -3
  239. package/src/tabs/tab.scss +237 -25
  240. package/src/tabs/tab.ts +91 -14
  241. package/src/tabs/tabs.scss +37 -3
  242. package/src/tabs/tabs.ts +118 -2
  243. package/src/textarea/textarea.ts +4 -2
  244. package/src/time-picker/time-picker.ts +4 -2
  245. package/src/toolbar/index.ts +1 -0
  246. package/src/toolbar/toolbar-colors.scss +16 -0
  247. package/src/toolbar/toolbar.scss +165 -0
  248. package/src/toolbar/toolbar.ts +137 -0
  249. package/dist/IndividualComponent-Dt5xirYG.js +0 -73
  250. package/dist/IndividualComponent-Dt5xirYG.js.map +0 -1
  251. package/dist/button-ClzS8JLq.js.map +0 -1
  252. package/dist/button-group-BMS5WvaF.js +0 -292
  253. package/dist/button-group-BMS5WvaF.js.map +0 -1
  254. package/dist/chart-donut.js +0 -309
  255. package/dist/chart-donut.js.map +0 -1
  256. package/dist/class-map-59YGWLnx.js.map +0 -1
  257. package/dist/directive-Cuw6h7YA.js +0 -9
  258. package/dist/directive-Cuw6h7YA.js.map +0 -1
  259. package/dist/dispatch-event-utils-B4odODQf.js +0 -277
  260. package/dist/dispatch-event-utils-B4odODQf.js.map +0 -1
  261. package/dist/observe-theme-change-pALI5fmV.js.map +0 -1
  262. package/dist/radio-b70_Ie9n.js.map +0 -1
  263. package/dist/src/chart-donut/chart-donut.d.ts +0 -53
  264. package/dist/src/chart-donut/index.d.ts +0 -1
  265. package/dist/src/styleMixins.css.d.ts +0 -9
  266. package/dist/src/utils.d.ts +0 -9
  267. package/src/chart-donut/chart-donut.scss +0 -37
  268. package/src/chart-donut/chart-donut.ts +0 -287
  269. package/src/chart-donut/demo/index.html +0 -51
  270. package/src/chart-donut/index.ts +0 -1
  271. package/src/styleMixins.css.ts +0 -55
  272. package/src/utils.ts +0 -193
  273. /package/dist/src/{spread.d.ts → __directive/spread.d.ts} +0 -0
  274. /package/dist/src/{utils → __utils}/copy-to-clipboard.d.ts +0 -0
  275. /package/dist/src/{utils → __utils}/dispatch-event-utils.d.ts +0 -0
  276. /package/dist/src/{utils → __utils}/observe-theme-change.d.ts +0 -0
  277. /package/dist/test/{card.test.d.ts → banner.test.d.ts} +0 -0
  278. /package/src/{spread.ts → __directive/spread.ts} +0 -0
  279. /package/src/{utils → __utils}/copy-to-clipboard.ts +0 -0
  280. /package/src/{utils → __utils}/observe-theme-change.ts +0 -0
@@ -0,0 +1,240 @@
1
+ import { LitElement, html, nothing } from 'lit';
2
+ import { property, query, state } from 'lit/decorators.js';
3
+ import { classMap } from 'lit/directives/class-map.js';
4
+ import { live } from 'lit/directives/live.js';
5
+ import IndividualComponent from '@/IndividualComponent.js';
6
+ import styles from './search.scss';
7
+ import colorStyles from './search-colors.scss';
8
+ import { observerSlotChangesWithCallback } from '@/__utils/observe-slot-change.js';
9
+
10
+ /**
11
+ * @label Search
12
+ * @tag wc-search
13
+ * @rawTag search
14
+ *
15
+ * @summary A Material 3 search bar for filtering and finding content.
16
+ * @overview
17
+ * <p>The search component provides a text input designed for search interactions.
18
+ * It supports outlined and filled variants, an optional clear button, and leading/trailing icon slots.</p>
19
+ *
20
+ * @cssprop --search-container-shape - Border radius of the search bar. Defaults to full (pill shape).
21
+ * @cssprop --search-container-color - Background color of the search container.
22
+ * @cssprop --search-input-text-color - Color of the input text.
23
+ * @cssprop --search-placeholder-color - Color of the placeholder text.
24
+ * @cssprop --search-icon-color - Color of the leading and trailing icons.
25
+ * @cssprop --search-outline-color - Border color for the outlined variant.
26
+ * @cssprop --search-outline-width - Border width for the outlined variant.
27
+ *
28
+ * @fires {CustomEvent} input - Dispatched when the search value changes.
29
+ * @fires {CustomEvent} change - Dispatched when the search input loses focus or Enter is pressed.
30
+ * @fires {CustomEvent} clear - Dispatched when the clear button is activated.
31
+ * @fires {CustomEvent} search - Dispatched when the user submits the search (presses Enter).
32
+ *
33
+ * @example
34
+ * ```html
35
+ * <wc-search placeholder="Search..."></wc-search>
36
+ * ```
37
+ * @tags form
38
+ */
39
+ @IndividualComponent
40
+ export class Search extends LitElement {
41
+ static styles = [styles, colorStyles];
42
+
43
+ /**
44
+ * Visual style variant.
45
+ * Possible values: `"outlined"`, `"filled"`. Defaults to `"filled"`.
46
+ */
47
+ @property({ type: String, reflect: true })
48
+ variant: 'outlined' | 'filled' = 'filled';
49
+
50
+ /**
51
+ * Placeholder text shown when the input is empty.
52
+ */
53
+ @property({ type: String })
54
+ placeholder: string = 'Search';
55
+
56
+ /**
57
+ * Current search value.
58
+ */
59
+ @property({ type: String })
60
+ value: string = '';
61
+
62
+ /**
63
+ * Whether the search bar is disabled.
64
+ */
65
+ @property({ type: Boolean, reflect: true })
66
+ disabled: boolean = false;
67
+
68
+ /**
69
+ * Whether a clear button is shown when the input has a value.
70
+ */
71
+ @property({ type: Boolean })
72
+ clearable: boolean = true;
73
+
74
+ /**
75
+ * Size of the search bar.
76
+ * Possible values: `"sm"`, `"md"`, `"lg"`. Defaults to `"md"`.
77
+ */
78
+ @property({ type: String, reflect: true })
79
+ size: 'sm' | 'md' | 'lg' = 'md';
80
+
81
+ @state()
82
+ private focused: boolean = false;
83
+
84
+ @state()
85
+ private leadingSlotHasContent: boolean = false;
86
+
87
+ @query('.search-input')
88
+ private inputElement?: HTMLInputElement;
89
+
90
+ override firstUpdated() {
91
+ observerSlotChangesWithCallback(
92
+ this.renderRoot.querySelector('slot[name="leading"]'),
93
+ hasContent => {
94
+ this.leadingSlotHasContent = hasContent;
95
+ this.requestUpdate();
96
+ },
97
+ );
98
+ }
99
+
100
+ /** Focuses the internal input element. */
101
+ override focus() {
102
+ this.inputElement?.focus();
103
+ }
104
+
105
+ /** Blurs the internal input element. */
106
+ override blur() {
107
+ this.inputElement?.blur();
108
+ }
109
+
110
+ private __handleInput(event: InputEvent) {
111
+ const input = event.target as HTMLInputElement;
112
+ this.value = input.value;
113
+ this.dispatchEvent(
114
+ new CustomEvent('input', {
115
+ detail: { value: this.value },
116
+ bubbles: true,
117
+ composed: true,
118
+ }),
119
+ );
120
+ }
121
+
122
+ private __handleChange(event: Event) {
123
+ const input = event.target as HTMLInputElement;
124
+ this.value = input.value;
125
+ this.dispatchEvent(
126
+ new CustomEvent('change', {
127
+ detail: { value: this.value },
128
+ bubbles: true,
129
+ composed: true,
130
+ }),
131
+ );
132
+ }
133
+
134
+ private __handleKeydown(event: KeyboardEvent) {
135
+ if (event.key === 'Enter') {
136
+ this.dispatchEvent(
137
+ new CustomEvent('search', {
138
+ detail: { value: this.value },
139
+ bubbles: true,
140
+ composed: true,
141
+ }),
142
+ );
143
+ }
144
+ if (event.key === 'Escape') {
145
+ this.__clearValue();
146
+ }
147
+ }
148
+
149
+ private __handleFocus() {
150
+ this.focused = true;
151
+ }
152
+
153
+ private __handleBlur() {
154
+ this.focused = false;
155
+ }
156
+
157
+ private __clearValue() {
158
+ this.value = '';
159
+ this.inputElement?.focus();
160
+ this.dispatchEvent(
161
+ new CustomEvent('clear', {
162
+ bubbles: true,
163
+ composed: true,
164
+ }),
165
+ );
166
+ this.dispatchEvent(
167
+ new CustomEvent('input', {
168
+ detail: { value: '' },
169
+ bubbles: true,
170
+ composed: true,
171
+ }),
172
+ );
173
+ }
174
+
175
+ private __renderClearButton() {
176
+ if (!this.clearable || !this.value) return nothing;
177
+
178
+ return html`
179
+ <button
180
+ class="clear-button"
181
+ aria-label="Clear search"
182
+ tabindex="-1"
183
+ @click=${this.__clearValue}
184
+ ?disabled=${this.disabled}
185
+ >
186
+ <wc-icon name="close"></wc-icon>
187
+ </button>
188
+ `;
189
+ }
190
+
191
+ private __renderLeadingIcon() {
192
+ return html`
193
+ <div class="leading-icon ${this.leadingSlotHasContent ? 'has-slot' : ''}">
194
+ <slot name="leading">
195
+ <wc-icon name="search"></wc-icon>
196
+ </slot>
197
+ </div>
198
+ `;
199
+ }
200
+
201
+ override render() {
202
+ const cssClasses = {
203
+ search: true,
204
+ [`variant-${this.variant}`]: true,
205
+ [`size-${this.size}`]: true,
206
+ focused: this.focused,
207
+ disabled: this.disabled,
208
+ 'has-value': !!this.value,
209
+ };
210
+
211
+ return html`
212
+ <div class=${classMap(cssClasses)} role="search">
213
+ <div class="background"></div>
214
+ <div class="outline"></div>
215
+
216
+ ${this.__renderLeadingIcon()}
217
+
218
+ <input
219
+ class="search-input"
220
+ type="search"
221
+ role="searchbox"
222
+ .value=${live(this.value)}
223
+ placeholder=${this.placeholder}
224
+ ?disabled=${this.disabled}
225
+ aria-label=${this.placeholder}
226
+ @input=${this.__handleInput}
227
+ @change=${this.__handleChange}
228
+ @keydown=${this.__handleKeydown}
229
+ @focus=${this.__handleFocus}
230
+ @blur=${this.__handleBlur}
231
+ />
232
+
233
+ <div class="trailing-actions">
234
+ ${this.__renderClearButton()}
235
+ <slot name="trailing"></slot>
236
+ </div>
237
+ </div>
238
+ `;
239
+ }
240
+ }
@@ -0,0 +1,2 @@
1
+ export { SegmentedButton } from './segmented-button.js';
2
+ export { SegmentedButtonGroup } from './segmented-button-group.js';
@@ -0,0 +1,21 @@
1
+ @use '../../scss/mixin';
2
+
3
+ @include mixin.base-styles;
4
+
5
+ :host {
6
+ display: inline-flex;
7
+ --_segmented-button-group-shape: var(--shape-corner-full, 9999px);
8
+ }
9
+
10
+ .segmented-button-group {
11
+ display: flex;
12
+ align-items: stretch;
13
+ border-radius: var(--segmented-button-group-shape, var(--_segmented-button-group-shape));
14
+ border: 1px solid var(--segmented-button-outline-color, var(--color-outline, #79747e));
15
+ overflow: hidden;
16
+ min-width: max-content;
17
+
18
+ ::slotted(wc-segmented-button) {
19
+ flex: 1;
20
+ }
21
+ }
@@ -0,0 +1,110 @@
1
+ import { html, LitElement } from 'lit';
2
+ import { property } from 'lit/decorators.js';
3
+ import styles from './segmented-button-group.scss';
4
+ import { SegmentedButton } from './segmented-button.js';
5
+
6
+ /**
7
+ * @label Segmented Button Group
8
+ * @tag wc-segmented-button-group
9
+ * @rawTag segmented-button-group
10
+ * @summary A container for segmented buttons following Material Design 3.
11
+ * @overview
12
+ * <p>Segmented buttons help people select options, switch views, or sort elements. They follow the Material Design 3 specification.</p>
13
+ * <p>Use <code>multi-select</code> to allow more than one segment to be selected at a time. By default only one segment can be active (single-select).</p>
14
+ *
15
+ * @cssprop --segmented-button-group-shape: Border-radius of the group container.
16
+ *
17
+ * @fires {CustomEvent} change - Dispatched when the selection changes. Detail contains <code>{ value, values }</code>.
18
+ *
19
+ * @example
20
+ * ```html
21
+ * <wc-segmented-button-group>
22
+ * <wc-segmented-button value="day">Day</wc-segmented-button>
23
+ * <wc-segmented-button value="week" selected>Week</wc-segmented-button>
24
+ * <wc-segmented-button value="month">Month</wc-segmented-button>
25
+ * </wc-segmented-button-group>
26
+ * ```
27
+ *
28
+ * @tags controls
29
+ */
30
+ export class SegmentedButtonGroup extends LitElement {
31
+ static styles = [styles];
32
+
33
+ static SegmentedButton = SegmentedButton;
34
+
35
+ /**
36
+ * When true, multiple segments can be selected simultaneously.
37
+ * Defaults to single-select mode.
38
+ */
39
+ @property({ type: Boolean, reflect: true, attribute: 'multi-select' })
40
+ multiSelect: boolean = false;
41
+
42
+ /**
43
+ * When true, in single-select mode the currently selected segment can be
44
+ * deselected by clicking it again (allowing an empty selection).
45
+ * Defaults to `false`.
46
+ */
47
+ @property({ type: Boolean, reflect: true })
48
+ nullable: boolean = false;
49
+
50
+ connectedCallback() {
51
+ super.connectedCallback();
52
+ this.addEventListener(
53
+ 'segmented-button--change',
54
+ this._onSegmentChange as EventListener,
55
+ );
56
+ }
57
+
58
+ disconnectedCallback() {
59
+ super.disconnectedCallback();
60
+ this.removeEventListener(
61
+ 'segmented-button--change',
62
+ this._onSegmentChange as EventListener,
63
+ );
64
+ }
65
+
66
+ private _getSegments(): SegmentedButton[] {
67
+ return Array.from(this.querySelectorAll<SegmentedButton>('wc-segmented-button'));
68
+ }
69
+
70
+ private _onSegmentChange = (ev: CustomEvent) => {
71
+ ev.stopPropagation();
72
+ const target = ev.composedPath()[0] as SegmentedButton;
73
+ const segments = this._getSegments();
74
+
75
+ if (this.multiSelect) {
76
+ target.selected = !target.selected;
77
+ } else {
78
+ if (target.selected && this.nullable) {
79
+ target.selected = false;
80
+ } else {
81
+ segments.forEach(seg => {
82
+ seg.selected = seg === target;
83
+ });
84
+ }
85
+ }
86
+
87
+ const selectedValues = segments
88
+ .filter(s => s.selected)
89
+ .map(s => s.value || s.textContent?.trim() || '');
90
+
91
+ this.dispatchEvent(
92
+ new CustomEvent('change', {
93
+ detail: {
94
+ value: selectedValues[0] ?? null,
95
+ values: selectedValues,
96
+ },
97
+ bubbles: true,
98
+ composed: true,
99
+ }),
100
+ );
101
+ };
102
+
103
+ render() {
104
+ return html`
105
+ <div class="segmented-button-group" role="group">
106
+ <slot></slot>
107
+ </div>
108
+ `;
109
+ }
110
+ }
@@ -0,0 +1,115 @@
1
+ @use '../../scss/mixin';
2
+
3
+ @include mixin.base-styles;
4
+
5
+ :host {
6
+ display: contents;
7
+
8
+ --_segmented-button-height: 2.5rem;
9
+ --_segmented-button-outline-color: var(--color-outline, #79747e);
10
+ --_segmented-button-selected-container-color: var(--color-secondary-container, #e8def8);
11
+ --_segmented-button-selected-label-text-color: var(--color-on-secondary-container, #1d192b);
12
+ --_segmented-button-unselected-label-text-color: var(--color-on-surface, #1c1b1f);
13
+ --_segmented-button-disabled-opacity: 0.38;
14
+ --_segmented-button-state-layer-color: var(--color-on-surface, #1c1b1f);
15
+ --_segmented-button-selected-state-layer-color: var(--color-on-secondary-container, #1d192b);
16
+ }
17
+
18
+ .segment {
19
+ position: relative;
20
+ display: flex;
21
+ align-items: center;
22
+ justify-content: center;
23
+ height: var(--segmented-button-height, var(--_segmented-button-height));
24
+ padding: 0 1.5rem;
25
+ cursor: pointer;
26
+ flex: 1;
27
+ min-width: 0;
28
+ outline: none;
29
+ user-select: none;
30
+ -webkit-user-select: none;
31
+ background: transparent;
32
+ overflow: hidden;
33
+ transition:
34
+ background-color 200ms ease,
35
+ color 200ms ease;
36
+
37
+ .content {
38
+ position: relative;
39
+ z-index: 1;
40
+ display: flex;
41
+ align-items: center;
42
+ gap: 0.5rem;
43
+ @include mixin.get-typography(label-large);
44
+ color: var(--segmented-button-unselected-label-text-color, var(--_segmented-button-unselected-label-text-color));
45
+
46
+ .check-icon,
47
+ .leading-icon {
48
+ --icon-size: 1.125rem;
49
+ --icon-color: currentColor;
50
+ flex-shrink: 0;
51
+ }
52
+
53
+ .label {
54
+ overflow: hidden;
55
+ text-overflow: ellipsis;
56
+ white-space: nowrap;
57
+ }
58
+ }
59
+
60
+ .state-layer {
61
+ position: absolute;
62
+ inset: 0;
63
+ background: var(--_segmented-button-state-layer-color);
64
+ opacity: 0;
65
+ pointer-events: none;
66
+ transition: opacity 200ms ease;
67
+ }
68
+
69
+ /* right-side divider between segments */
70
+ .segment-outline {
71
+ position: absolute;
72
+ top: 0;
73
+ right: 0;
74
+ width: 1px;
75
+ height: 100%;
76
+ background: var(--segmented-button-outline-color, var(--_segmented-button-outline-color));
77
+ pointer-events: none;
78
+ }
79
+
80
+ &:hover:not(.disabled) .state-layer {
81
+ opacity: 0.08;
82
+ }
83
+
84
+ &.has-focus:not(.disabled) .state-layer {
85
+ opacity: 0.12;
86
+ }
87
+
88
+ &.active:not(.disabled) .state-layer {
89
+ opacity: 0.16;
90
+ }
91
+
92
+ /* Selected state */
93
+ &.selected {
94
+ background-color: var(--segmented-button-selected-container-color, var(--_segmented-button-selected-container-color));
95
+
96
+ .content {
97
+ color: var(--segmented-button-selected-label-text-color, var(--_segmented-button-selected-label-text-color));
98
+ }
99
+
100
+ .state-layer {
101
+ background: var(--_segmented-button-selected-state-layer-color);
102
+ }
103
+ }
104
+
105
+ /* Disabled state */
106
+ &.disabled {
107
+ cursor: not-allowed;
108
+ opacity: var(--segmented-button-disabled-opacity, var(--_segmented-button-disabled-opacity));
109
+ pointer-events: none;
110
+ }
111
+ }
112
+
113
+ :host(:last-child) .segment-outline {
114
+ display: none;
115
+ }
@@ -0,0 +1,175 @@
1
+ import { html, LitElement, nothing } from 'lit';
2
+ import { property, state } from 'lit/decorators.js';
3
+ import { classMap } from 'lit/directives/class-map.js';
4
+ import styles from './segmented-button.scss';
5
+
6
+ /**
7
+ * @label Segmented Button
8
+ * @tag wc-segmented-button
9
+ * @rawTag segmented-button
10
+ * @summary An individual segment within a segmented button group.
11
+ * @parentRawTag segmented-button-group
12
+ * @overview
13
+ * <p>Segmented buttons help people select options, switch views, or sort elements. They are used within a <code>wc-segmented-button-group</code>.</p>
14
+ *
15
+ * @cssprop --segmented-button-height: Height of the segmented button.
16
+ * @cssprop --segmented-button-selected-container-color: Background color when the segment is selected.
17
+ * @cssprop --segmented-button-selected-label-text-color: Text color when the segment is selected.
18
+ * @cssprop --segmented-button-unselected-label-text-color: Text color when the segment is unselected.
19
+ * @cssprop --segmented-button-outline-color: Outline / border color.
20
+ * @cssprop --segmented-button-disabled-opacity: Opacity when the segment is disabled.
21
+ *
22
+ * @fires {CustomEvent} segmented-button--change - Dispatched when the selected state changes.
23
+ *
24
+ * @example
25
+ * ```html
26
+ * <wc-segmented-button-group>
27
+ * <wc-segmented-button value="day">Day</wc-segmented-button>
28
+ * <wc-segmented-button value="week" selected>Week</wc-segmented-button>
29
+ * <wc-segmented-button value="month">Month</wc-segmented-button>
30
+ * </wc-segmented-button-group>
31
+ * ```
32
+ *
33
+ * @tags controls
34
+ */
35
+ export class SegmentedButton extends LitElement {
36
+ static styles = [styles];
37
+
38
+ /**
39
+ * The value associated with this segment.
40
+ */
41
+ @property({ type: String, reflect: true })
42
+ value: string = '';
43
+
44
+ /**
45
+ * Whether this segment is currently selected.
46
+ */
47
+ @property({ type: Boolean, reflect: true })
48
+ selected: boolean = false;
49
+
50
+ /**
51
+ * If true, the user cannot interact with this segment.
52
+ */
53
+ @property({ type: Boolean, reflect: true })
54
+ disabled: boolean = false;
55
+
56
+ /**
57
+ * Optional icon name to display (uses wc-icon).
58
+ */
59
+ @property({ type: String })
60
+ icon?: string;
61
+
62
+ @state()
63
+ private hasFocus = false;
64
+
65
+ @state()
66
+ private isActive = false;
67
+
68
+ connectedCallback() {
69
+ super.connectedCallback();
70
+ window.addEventListener('mouseup', this._windowMouseUp);
71
+ }
72
+
73
+ disconnectedCallback() {
74
+ super.disconnectedCallback();
75
+ window.removeEventListener('mouseup', this._windowMouseUp);
76
+ }
77
+
78
+ private _windowMouseUp = () => {
79
+ if (this.isActive) {
80
+ this.isActive = false;
81
+ }
82
+ };
83
+
84
+ private _mouseDownHandler = () => {
85
+ this.isActive = true;
86
+ };
87
+
88
+ private _keyDownHandler = (evt: KeyboardEvent) => {
89
+ if (evt.key === ' ' || evt.key === 'Enter') {
90
+ evt.preventDefault();
91
+ this.isActive = true;
92
+ this._toggle(evt);
93
+ }
94
+ };
95
+
96
+ private _clickHandler = (ev: MouseEvent) => {
97
+ this._toggle(ev);
98
+ };
99
+
100
+ private _toggle(ev: MouseEvent | KeyboardEvent) {
101
+ if (this.disabled) return;
102
+ this.dispatchEvent(
103
+ new CustomEvent('segmented-button--change', {
104
+ detail: {
105
+ value: this.value || this.textContent?.trim(),
106
+ selected: !this.selected,
107
+ originalEvent: ev,
108
+ },
109
+ bubbles: true,
110
+ composed: true,
111
+ }),
112
+ );
113
+ }
114
+
115
+ private _blurHandler = (ev: FocusEvent) => {
116
+ this.hasFocus = false;
117
+ this.dispatchEvent(
118
+ new CustomEvent('blur', { detail: ev, bubbles: true, composed: true }),
119
+ );
120
+ };
121
+
122
+ private _focusHandler = (ev: FocusEvent) => {
123
+ this.hasFocus = true;
124
+ this.dispatchEvent(
125
+ new CustomEvent('focus', { detail: ev, bubbles: true, composed: true }),
126
+ );
127
+ };
128
+
129
+ /** Sets focus on the segment. */
130
+ focus() {
131
+ this.renderRoot.querySelector<HTMLElement>('.segment')?.focus();
132
+ }
133
+
134
+ /** Removes focus from the segment. */
135
+ blur() {
136
+ this.renderRoot.querySelector<HTMLElement>('.segment')?.blur();
137
+ }
138
+
139
+ render() {
140
+ const cssClasses = {
141
+ segment: true,
142
+ selected: this.selected,
143
+ disabled: this.disabled,
144
+ 'has-focus': this.hasFocus,
145
+ active: this.isActive,
146
+ 'has-icon': !!this.icon,
147
+ };
148
+
149
+ return html`
150
+ <div
151
+ class=${classMap(cssClasses)}
152
+ role="button"
153
+ tabindex=${this.disabled ? -1 : 0}
154
+ aria-pressed=${this.selected}
155
+ aria-disabled=${this.disabled}
156
+ @click=${this._clickHandler}
157
+ @mousedown=${this._mouseDownHandler}
158
+ @keydown=${this._keyDownHandler}
159
+ @blur=${this._blurHandler}
160
+ @focus=${this._focusHandler}
161
+ >
162
+ <div class="state-layer"></div>
163
+ <div class="content">
164
+ ${this.selected
165
+ ? html`<wc-icon class="check-icon" name="check"></wc-icon>`
166
+ : this.icon
167
+ ? html`<wc-icon class="leading-icon" name=${this.icon}></wc-icon>`
168
+ : nothing}
169
+ <span class="label"><slot></slot></span>
170
+ </div>
171
+ <div class="segment-outline"></div>
172
+ </div>
173
+ `;
174
+ }
175
+ }
@@ -0,0 +1,3 @@
1
+ export { Select } from './select.js';
2
+ export type { SelectOption } from './select.js';
3
+ export { SelectOptionElement } from './option.js';