@redvars/peacock 3.8.2 → 3.8.4

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 (225) hide show
  1. package/dist/accordion-item.js +15 -6
  2. package/dist/accordion-item.js.map +1 -1
  3. package/dist/accordion.js +12 -11
  4. package/dist/accordion.js.map +1 -1
  5. package/dist/alert.js +1 -0
  6. package/dist/alert.js.map +1 -1
  7. package/dist/app-bar.js +1 -0
  8. package/dist/app-bar.js.map +1 -1
  9. package/dist/assets/components.css +1 -1
  10. package/dist/assets/components.css.map +1 -1
  11. package/dist/assets/styles.css +1 -1
  12. package/dist/assets/styles.css.map +1 -1
  13. package/dist/avatar.js +1 -0
  14. package/dist/avatar.js.map +1 -1
  15. package/dist/babel-DBsfpl3B.js +18 -0
  16. package/dist/babel-DBsfpl3B.js.map +1 -0
  17. package/dist/badge.js +1 -0
  18. package/dist/badge.js.map +1 -1
  19. package/dist/bottom-sheet.js +1 -0
  20. package/dist/bottom-sheet.js.map +1 -1
  21. package/dist/breadcrumb-item.js +1 -0
  22. package/dist/breadcrumb-item.js.map +1 -1
  23. package/dist/breadcrumb.js +1 -0
  24. package/dist/breadcrumb.js.map +1 -1
  25. package/dist/button-group.js +5 -4
  26. package/dist/button-group.js.map +1 -1
  27. package/dist/button.js +9 -8
  28. package/dist/button.js.map +1 -1
  29. package/dist/calendar-column-view.js +1 -0
  30. package/dist/calendar-column-view.js.map +1 -1
  31. package/dist/calendar-month-view.js +1 -0
  32. package/dist/calendar-month-view.js.map +1 -1
  33. package/dist/calendar.js +1 -0
  34. package/dist/calendar.js.map +1 -1
  35. package/dist/canvas.js +1 -0
  36. package/dist/canvas.js.map +1 -1
  37. package/dist/card.js +23 -7
  38. package/dist/card.js.map +1 -1
  39. package/dist/cb-compound-expression.js +1 -0
  40. package/dist/cb-compound-expression.js.map +1 -1
  41. package/dist/cb-divider.js +1 -0
  42. package/dist/cb-divider.js.map +1 -1
  43. package/dist/cb-expression.js +1 -0
  44. package/dist/cb-expression.js.map +1 -1
  45. package/dist/cb-predicate.js +1 -0
  46. package/dist/cb-predicate.js.map +1 -1
  47. package/dist/{chart-bar-CYoGNXnK.js → chart-bar-CapLbc2e.js} +2 -1
  48. package/dist/{chart-bar-CYoGNXnK.js.map → chart-bar-CapLbc2e.js.map} +1 -1
  49. package/dist/chart-bar.js +1 -1
  50. package/dist/chart-doughnut.js +1 -0
  51. package/dist/chart-doughnut.js.map +1 -1
  52. package/dist/chart-pie.js +1 -0
  53. package/dist/chart-pie.js.map +1 -1
  54. package/dist/chart-stacked-bar.js +1 -1
  55. package/dist/checkbox.js +1 -0
  56. package/dist/checkbox.js.map +1 -1
  57. package/dist/chip.js +1 -0
  58. package/dist/chip.js.map +1 -1
  59. package/dist/clock.js +1 -0
  60. package/dist/clock.js.map +1 -1
  61. package/dist/code-editor.js +1 -0
  62. package/dist/code-editor.js.map +1 -1
  63. package/dist/code-highlighter.js +75 -11212
  64. package/dist/code-highlighter.js.map +1 -1
  65. package/dist/color-picker.js +701 -0
  66. package/dist/color-picker.js.map +1 -0
  67. package/dist/condition-builder.js +1 -0
  68. package/dist/condition-builder.js.map +1 -1
  69. package/dist/container.js +1 -0
  70. package/dist/container.js.map +1 -1
  71. package/dist/custom-elements-jsdocs.json +103 -10
  72. package/dist/custom-elements.json +1212 -586
  73. package/dist/divider.js +1 -0
  74. package/dist/divider.js.map +1 -1
  75. package/dist/dropdown-button.js +1 -0
  76. package/dist/dropdown-button.js.map +1 -1
  77. package/dist/elevation.js +1 -0
  78. package/dist/elevation.js.map +1 -1
  79. package/dist/empty-state.js +1 -0
  80. package/dist/empty-state.js.map +1 -1
  81. package/dist/estree-C2LDzX4U.js +47 -0
  82. package/dist/estree-C2LDzX4U.js.map +1 -0
  83. package/dist/fab.js +2 -3
  84. package/dist/fab.js.map +1 -1
  85. package/dist/field.js +1 -0
  86. package/dist/field.js.map +1 -1
  87. package/dist/{flow-designer-node-DsVwQTac.js → flow-designer-node-CGSm6cUH.js} +2 -1
  88. package/dist/{flow-designer-node-DsVwQTac.js.map → flow-designer-node-CGSm6cUH.js.map} +1 -1
  89. package/dist/flow-designer-node.js +1 -1
  90. package/dist/flow-designer.js +1 -1
  91. package/dist/html-D22sQuVy.js +27 -0
  92. package/dist/html-D22sQuVy.js.map +1 -0
  93. package/dist/html-editor.js +1 -0
  94. package/dist/html-editor.js.map +1 -1
  95. package/dist/icon-button.js +6 -5
  96. package/dist/icon-button.js.map +1 -1
  97. package/dist/icon.js +1 -0
  98. package/dist/icon.js.map +1 -1
  99. package/dist/index-_g_oLekF.js +14095 -0
  100. package/dist/index-_g_oLekF.js.map +1 -0
  101. package/dist/index.js +5 -4
  102. package/dist/index.js.map +1 -1
  103. package/dist/item.js +4 -2
  104. package/dist/item.js.map +1 -1
  105. package/dist/link.js +1 -0
  106. package/dist/link.js.map +1 -1
  107. package/dist/{list-D6JLh1uh.js → list-BBmnHm8f.js} +196 -20
  108. package/dist/list-BBmnHm8f.js.map +1 -0
  109. package/dist/list.js +2 -2
  110. package/dist/loader.js +6 -2
  111. package/dist/loader.js.map +1 -1
  112. package/dist/menu-item.js +104 -33
  113. package/dist/menu-item.js.map +1 -1
  114. package/dist/menu.js +5 -18
  115. package/dist/menu.js.map +1 -1
  116. package/dist/modal.js +1 -0
  117. package/dist/modal.js.map +1 -1
  118. package/dist/navigation-rail-item.js +22 -6
  119. package/dist/navigation-rail-item.js.map +1 -1
  120. package/dist/navigation-rail.js +23 -20
  121. package/dist/navigation-rail.js.map +1 -1
  122. package/dist/notification-manager.js +1 -0
  123. package/dist/notification-manager.js.map +1 -1
  124. package/dist/notification.js +1 -0
  125. package/dist/notification.js.map +1 -1
  126. package/dist/number-counter.js +1 -0
  127. package/dist/number-counter.js.map +1 -1
  128. package/dist/pagination.js +1 -0
  129. package/dist/pagination.js.map +1 -1
  130. package/dist/pierre-dark-DFWl0m-C.js +4 -0
  131. package/dist/pierre-dark-DFWl0m-C.js.map +1 -0
  132. package/dist/pierre-light-BEkAPImt.js +4 -0
  133. package/dist/pierre-light-BEkAPImt.js.map +1 -0
  134. package/dist/popover-content.js +1 -0
  135. package/dist/popover-content.js.map +1 -1
  136. package/dist/popover.js +1 -0
  137. package/dist/popover.js.map +1 -1
  138. package/dist/postcss-BhbitHaI.js +64 -0
  139. package/dist/postcss-BhbitHaI.js.map +1 -0
  140. package/dist/radio.js +1 -0
  141. package/dist/radio.js.map +1 -1
  142. package/dist/search.js +1 -0
  143. package/dist/search.js.map +1 -1
  144. package/dist/segmented-button-group.js +1 -0
  145. package/dist/segmented-button-group.js.map +1 -1
  146. package/dist/segmented-button.js +1 -0
  147. package/dist/segmented-button.js.map +1 -1
  148. package/dist/{select-Dwtk0RIU.js → select-D85kpjUt.js} +28 -7
  149. package/dist/{select-Dwtk0RIU.js.map → select-D85kpjUt.js.map} +1 -1
  150. package/dist/side-sheet.js +2 -1
  151. package/dist/side-sheet.js.map +1 -1
  152. package/dist/skeleton.js +1 -0
  153. package/dist/skeleton.js.map +1 -1
  154. package/dist/snackbar.js +1 -0
  155. package/dist/snackbar.js.map +1 -1
  156. package/dist/spinner.js +1 -0
  157. package/dist/spinner.js.map +1 -1
  158. package/dist/split-button.js +1 -0
  159. package/dist/split-button.js.map +1 -1
  160. package/dist/src/accordion/accordion-item.d.ts +1 -1
  161. package/dist/src/accordion/accordion.d.ts +3 -3
  162. package/dist/src/button/button/button.d.ts +2 -2
  163. package/dist/src/button/button-group/button-group.d.ts +4 -4
  164. package/dist/src/code-highlighter/code-highlighter.d.ts +2 -2
  165. package/dist/src/color-picker/color-picker.d.ts +85 -0
  166. package/dist/src/color-picker/index.d.ts +1 -0
  167. package/dist/src/index.d.ts +1 -0
  168. package/dist/src/item/item.d.ts +0 -1
  169. package/dist/src/list/list-item.d.ts +3 -1
  170. package/dist/src/list/list.d.ts +24 -0
  171. package/dist/src/menu/menu-item/menu-item.d.ts +1 -2
  172. package/dist/standalone-Ccq0tWwA.js +32 -0
  173. package/dist/standalone-Ccq0tWwA.js.map +1 -0
  174. package/dist/sub-menu.js +7 -1
  175. package/dist/sub-menu.js.map +1 -1
  176. package/dist/svg.js +1 -0
  177. package/dist/svg.js.map +1 -1
  178. package/dist/tab-group.js +1 -0
  179. package/dist/tab-group.js.map +1 -1
  180. package/dist/tab-panel.js +1 -0
  181. package/dist/tab-panel.js.map +1 -1
  182. package/dist/tab.js +1 -0
  183. package/dist/tab.js.map +1 -1
  184. package/dist/table.js +1 -0
  185. package/dist/table.js.map +1 -1
  186. package/dist/tabs.js +1 -0
  187. package/dist/tabs.js.map +1 -1
  188. package/dist/toolbar.js +1 -0
  189. package/dist/toolbar.js.map +1 -1
  190. package/dist/tooltip.js +1 -0
  191. package/dist/tooltip.js.map +1 -1
  192. package/dist/tsconfig.tsbuildinfo +1 -1
  193. package/package.json +1 -1
  194. package/readme.md +3 -3
  195. package/scss/mixin.scss +2 -0
  196. package/src/accordion/accordion-item.ts +16 -6
  197. package/src/accordion/accordion.scss +2 -2
  198. package/src/accordion/accordion.ts +7 -7
  199. package/src/button/button/button-base.scss +2 -1
  200. package/src/button/button/button-layers.scss +2 -6
  201. package/src/button/button/button.ts +3 -3
  202. package/src/button/button-group/button-group.ts +4 -4
  203. package/src/button/fab/fab.ts +0 -4
  204. package/src/card/card.scss +18 -5
  205. package/src/code-highlighter/code-highlighter.ts +94 -39
  206. package/src/color-picker/color-picker.scss +217 -0
  207. package/src/color-picker/color-picker.ts +478 -0
  208. package/src/color-picker/index.ts +1 -0
  209. package/src/index.ts +1 -0
  210. package/src/item/item.scss +3 -1
  211. package/src/item/item.ts +0 -1
  212. package/src/list/list-item.scss +5 -1
  213. package/src/list/list-item.ts +40 -14
  214. package/src/list/list.ts +164 -2
  215. package/src/loader.ts +4 -0
  216. package/src/menu/menu/menu.scss +4 -18
  217. package/src/menu/menu/menu.ts +0 -1
  218. package/src/menu/menu-item/menu-item.scss +73 -43
  219. package/src/menu/menu-item/menu-item.ts +60 -27
  220. package/src/menu/sub-menu/sub-menu.scss +5 -1
  221. package/src/navigation-rail/navigation-rail-item.scss +25 -8
  222. package/src/navigation-rail/navigation-rail.scss +25 -22
  223. package/src/side-sheet/side-sheet.ts +1 -1
  224. package/src/sidebar-menu/sidebar-menu-item.scss +14 -7
  225. package/dist/list-D6JLh1uh.js.map +0 -1
@@ -0,0 +1,478 @@
1
+ import { LitElement, html, nothing } from 'lit';
2
+ import { property, state, query } from 'lit/decorators.js';
3
+ import { styleMap } from 'lit/directives/style-map.js';
4
+ import styles from './color-picker.scss';
5
+ import IndividualComponent from '@/IndividualComponent.js';
6
+
7
+ // ── Color utilities ───────────────────────────────────────────────────────────
8
+
9
+ function hexToRgb(hex: string): { r: number; g: number; b: number } {
10
+ const clean = hex.replace('#', '');
11
+ const full = clean.length === 3
12
+ ? clean.split('').map(c => c + c).join('')
13
+ : clean.slice(0, 6);
14
+ const n = parseInt(full, 16);
15
+ return { r: (n >> 16) & 0xff, g: (n >> 8) & 0xff, b: n & 0xff };
16
+ }
17
+
18
+ function rgbToHex(r: number, g: number, b: number): string {
19
+ return '#' + [r, g, b].map(v => Math.round(v).toString(16).padStart(2, '0')).join('');
20
+ }
21
+
22
+ function rgbToHsv(r: number, g: number, b: number): { h: number; s: number; v: number } {
23
+ const r1 = r / 255, g1 = g / 255, b1 = b / 255;
24
+ const max = Math.max(r1, g1, b1);
25
+ const min = Math.min(r1, g1, b1);
26
+ const delta = max - min;
27
+
28
+ let h = 0;
29
+ if (delta !== 0) {
30
+ if (max === r1) h = ((g1 - b1) / delta) % 6;
31
+ else if (max === g1) h = (b1 - r1) / delta + 2;
32
+ else h = (r1 - g1) / delta + 4;
33
+ h = Math.round(h * 60);
34
+ if (h < 0) h += 360;
35
+ }
36
+
37
+ return { h, s: max === 0 ? 0 : delta / max, v: max };
38
+ }
39
+
40
+ function hsvToRgb(h: number, s: number, v: number): { r: number; g: number; b: number } {
41
+ const c = v * s;
42
+ const x = c * (1 - Math.abs((h / 60) % 2 - 1));
43
+ const m = v - c;
44
+ let r1 = 0, g1 = 0, b1 = 0;
45
+
46
+ if (h < 60) { r1 = c; g1 = x; }
47
+ else if (h < 120) { r1 = x; g1 = c; }
48
+ else if (h < 180) { g1 = c; b1 = x; }
49
+ else if (h < 240) { g1 = x; b1 = c; }
50
+ else if (h < 300) { r1 = x; b1 = c; }
51
+ else { r1 = c; b1 = x; }
52
+
53
+ return {
54
+ r: Math.round((r1 + m) * 255),
55
+ g: Math.round((g1 + m) * 255),
56
+ b: Math.round((b1 + m) * 255),
57
+ };
58
+ }
59
+
60
+ function isValidHex(s: string): boolean {
61
+ return /^[0-9a-fA-F]{6}$/.test(s);
62
+ }
63
+
64
+ // ── Component ─────────────────────────────────────────────────────────────────
65
+
66
+ /**
67
+ * @label Color Picker
68
+ * @tag wc-color-picker
69
+ * @rawTag color-picker
70
+ *
71
+ * @summary A color picker component for selecting colors via a hue/saturation/value interface.
72
+ *
73
+ * @overview
74
+ * <p>The color picker presents a 2D saturation–brightness panel with a hue slider below it.
75
+ * An optional alpha slider allows controlling opacity. The current hex value is displayed in
76
+ * an editable text field.</p>
77
+ *
78
+ * @cssprop --color-picker-width - Overall width of the component. Defaults to 240px.
79
+ * @cssprop --color-picker-sv-height - Height of the saturation/brightness panel. Defaults to 160px.
80
+ *
81
+ * @fires {CustomEvent<{value: string, alpha: number}>} input - Dispatched continuously while the color is being changed.
82
+ * @fires {CustomEvent<{value: string, alpha: number}>} change - Dispatched when a color change is committed (pointer up, key, or input blur).
83
+ *
84
+ * @example
85
+ * ```html
86
+ * <wc-color-picker value="#2352d5"></wc-color-picker>
87
+ * ```
88
+ *
89
+ * @example
90
+ * ```html
91
+ * <wc-color-picker value="#ff573380" alpha></wc-color-picker>
92
+ * ```
93
+ */
94
+ @IndividualComponent
95
+ export class ColorPicker extends LitElement {
96
+ // ── Static ────────────────────────────────────────────────────────────────
97
+
98
+ static styles = [styles];
99
+
100
+ // ── Properties ───────────────────────────────────────────────────────────
101
+
102
+ /**
103
+ * The current color as a 6-digit hex string (e.g. `"#ff5733"`).
104
+ */
105
+ @property({ type: String, reflect: true }) value = '#2352d5';
106
+
107
+ /**
108
+ * Whether to show the alpha (opacity) slider and input.
109
+ */
110
+ @property({ type: Boolean }) alpha = false;
111
+
112
+ /**
113
+ * Whether the color picker is disabled.
114
+ */
115
+ @property({ type: Boolean, reflect: true }) disabled = false;
116
+
117
+ // ── State ─────────────────────────────────────────────────────────────────
118
+
119
+ /** Current hue, 0–360. */
120
+ @state() private _hue = 0;
121
+
122
+ /** Current saturation, 0–1 (x-axis of the SV panel). */
123
+ @state() private _sat = 0;
124
+
125
+ /** Current brightness (value in HSV), 0–1 (y-axis, 1 = bright). */
126
+ @state() private _bri = 1;
127
+
128
+ /** Current opacity, 0–1. */
129
+ @state() private _opacity = 1;
130
+
131
+ /** Intermediate value for the hex text input (without `#`). */
132
+ @state() private _hexInput = '';
133
+
134
+ // ── Queries ───────────────────────────────────────────────────────────────
135
+
136
+ @query('.sv-panel') private _svPanel?: HTMLElement;
137
+ @query('.hue-track') private _hueTrack?: HTMLElement;
138
+ @query('.alpha-track') private _alphaTrack?: HTMLElement;
139
+
140
+ // ── Private fields ────────────────────────────────────────────────────────
141
+
142
+ /** Tracks which drag handle is currently active. */
143
+ private _dragging: 'sv' | 'hue' | 'alpha' | null = null;
144
+
145
+ /** The last `value` we set ourselves, used to skip redundant re-syncs in `updated`. */
146
+ private _lastEmittedValue = '';
147
+
148
+ // ── Lifecycle ─────────────────────────────────────────────────────────────
149
+
150
+ connectedCallback() {
151
+ super.connectedCallback();
152
+ this._syncFromValue();
153
+ this._lastEmittedValue = this.value;
154
+ }
155
+
156
+ updated(changed: Map<PropertyKey, unknown>) {
157
+ if (changed.has('value') && this.value !== this._lastEmittedValue) {
158
+ this._syncFromValue();
159
+ }
160
+ }
161
+
162
+ // ── Private methods ───────────────────────────────────────────────────────
163
+
164
+ private _syncFromValue() {
165
+ const clean = this.value.startsWith('#') ? this.value.slice(1) : this.value;
166
+ if (!isValidHex(clean)) return;
167
+ const { r, g, b } = hexToRgb('#' + clean);
168
+ const { h, s, v } = rgbToHsv(r, g, b);
169
+ this._hue = h;
170
+ this._sat = s;
171
+ this._bri = v;
172
+ this._hexInput = clean.toUpperCase();
173
+ }
174
+
175
+ private _computeHex(): string {
176
+ const { r, g, b } = hsvToRgb(this._hue, this._sat, this._bri);
177
+ return rgbToHex(r, g, b);
178
+ }
179
+
180
+ private _emitValue(type: 'input' | 'change') {
181
+ const hex = this._computeHex();
182
+ this._lastEmittedValue = hex;
183
+ this.value = hex;
184
+ this._hexInput = hex.slice(1).toUpperCase();
185
+ this.dispatchEvent(new CustomEvent(type, {
186
+ detail: { value: hex, alpha: this._opacity },
187
+ bubbles: true,
188
+ composed: true,
189
+ }));
190
+ }
191
+
192
+ // SV panel
193
+ private _onSvPointerDown(e: PointerEvent) {
194
+ if (this.disabled) return;
195
+ e.preventDefault();
196
+ this._dragging = 'sv';
197
+ (e.currentTarget as Element).setPointerCapture(e.pointerId);
198
+ this._updateSv(e);
199
+ this._emitValue('input');
200
+ }
201
+
202
+ private _onSvPointerMove(e: PointerEvent) {
203
+ if (this._dragging !== 'sv') return;
204
+ this._updateSv(e);
205
+ this._emitValue('input');
206
+ }
207
+
208
+ private _onSvPointerUp() {
209
+ if (this._dragging !== 'sv') return;
210
+ this._dragging = null;
211
+ this._emitValue('change');
212
+ }
213
+
214
+ private _updateSv(e: PointerEvent) {
215
+ const rect = this._svPanel!.getBoundingClientRect();
216
+ this._sat = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
217
+ this._bri = 1 - Math.max(0, Math.min(1, (e.clientY - rect.top) / rect.height));
218
+ }
219
+
220
+ private _onSvKeyDown(e: KeyboardEvent) {
221
+ if (this.disabled) return;
222
+ const step = e.shiftKey ? 0.1 : 0.01;
223
+ let changed = false;
224
+
225
+ if (e.key === 'ArrowRight') { this._sat = Math.min(1, this._sat + step); changed = true; }
226
+ else if (e.key === 'ArrowLeft') { this._sat = Math.max(0, this._sat - step); changed = true; }
227
+ else if (e.key === 'ArrowUp') { this._bri = Math.min(1, this._bri + step); changed = true; }
228
+ else if (e.key === 'ArrowDown') { this._bri = Math.max(0, this._bri - step); changed = true; }
229
+
230
+ if (changed) {
231
+ e.preventDefault();
232
+ this._emitValue('input');
233
+ this._emitValue('change');
234
+ }
235
+ }
236
+
237
+ // Hue slider
238
+ private _onHuePointerDown(e: PointerEvent) {
239
+ if (this.disabled) return;
240
+ e.preventDefault();
241
+ this._dragging = 'hue';
242
+ (e.currentTarget as Element).setPointerCapture(e.pointerId);
243
+ this._updateHue(e);
244
+ this._emitValue('input');
245
+ }
246
+
247
+ private _onHuePointerMove(e: PointerEvent) {
248
+ if (this._dragging !== 'hue') return;
249
+ this._updateHue(e);
250
+ this._emitValue('input');
251
+ }
252
+
253
+ private _onHuePointerUp() {
254
+ if (this._dragging !== 'hue') return;
255
+ this._dragging = null;
256
+ this._emitValue('change');
257
+ }
258
+
259
+ private _updateHue(e: PointerEvent) {
260
+ const rect = this._hueTrack!.getBoundingClientRect();
261
+ this._hue = Math.round(Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width)) * 360);
262
+ }
263
+
264
+ private _onHueKeyDown(e: KeyboardEvent) {
265
+ if (this.disabled) return;
266
+ const step = e.shiftKey ? 10 : 1;
267
+ let changed = false;
268
+
269
+ if (e.key === 'ArrowRight' || e.key === 'ArrowUp') { this._hue = Math.min(360, this._hue + step); changed = true; }
270
+ else if (e.key === 'ArrowLeft' || e.key === 'ArrowDown') { this._hue = Math.max(0, this._hue - step); changed = true; }
271
+
272
+ if (changed) {
273
+ e.preventDefault();
274
+ this._emitValue('input');
275
+ this._emitValue('change');
276
+ }
277
+ }
278
+
279
+ // Alpha slider
280
+ private _onAlphaPointerDown(e: PointerEvent) {
281
+ if (this.disabled) return;
282
+ e.preventDefault();
283
+ this._dragging = 'alpha';
284
+ (e.currentTarget as Element).setPointerCapture(e.pointerId);
285
+ this._updateAlpha(e);
286
+ this._emitValue('input');
287
+ }
288
+
289
+ private _onAlphaPointerMove(e: PointerEvent) {
290
+ if (this._dragging !== 'alpha') return;
291
+ this._updateAlpha(e);
292
+ this._emitValue('input');
293
+ }
294
+
295
+ private _onAlphaPointerUp() {
296
+ if (this._dragging !== 'alpha') return;
297
+ this._dragging = null;
298
+ this._emitValue('change');
299
+ }
300
+
301
+ private _updateAlpha(e: PointerEvent) {
302
+ const rect = this._alphaTrack!.getBoundingClientRect();
303
+ this._opacity = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
304
+ }
305
+
306
+ private _onAlphaKeyDown(e: KeyboardEvent) {
307
+ if (this.disabled) return;
308
+ const step = e.shiftKey ? 0.1 : 0.01;
309
+ let changed = false;
310
+
311
+ if (e.key === 'ArrowRight' || e.key === 'ArrowUp') { this._opacity = Math.min(1, this._opacity + step); changed = true; }
312
+ else if (e.key === 'ArrowLeft' || e.key === 'ArrowDown') { this._opacity = Math.max(0, this._opacity - step); changed = true; }
313
+
314
+ if (changed) {
315
+ e.preventDefault();
316
+ this._emitValue('input');
317
+ this._emitValue('change');
318
+ }
319
+ }
320
+
321
+ // Hex input
322
+ private _onHexInput(e: InputEvent) {
323
+ const raw = (e.target as HTMLInputElement).value.toUpperCase().replace(/[^0-9A-F]/g, '');
324
+ this._hexInput = raw;
325
+ if (isValidHex(raw)) {
326
+ const { r, g, b } = hexToRgb('#' + raw);
327
+ const { h, s, v } = rgbToHsv(r, g, b);
328
+ this._hue = h;
329
+ this._sat = s;
330
+ this._bri = v;
331
+ this._emitValue('input');
332
+ }
333
+ }
334
+
335
+ private _onHexChange() {
336
+ if (isValidHex(this._hexInput)) {
337
+ this._emitValue('change');
338
+ } else {
339
+ this._hexInput = this.value.slice(1).toUpperCase();
340
+ }
341
+ }
342
+
343
+ private _onAlphaNumberChange(e: Event) {
344
+ const raw = parseInt((e.target as HTMLInputElement).value, 10);
345
+ this._opacity = Math.max(0, Math.min(100, isNaN(raw) ? 100 : raw)) / 100;
346
+ this._emitValue('change');
347
+ }
348
+
349
+ // ── Render ────────────────────────────────────────────────────────────────
350
+
351
+ render() {
352
+ const hueColor = `hsl(${this._hue}, 100%, 50%)`;
353
+ const currentHex = this._computeHex();
354
+ const { r, g, b } = hsvToRgb(this._hue, this._sat, this._bri);
355
+ const previewColor = `rgba(${r}, ${g}, ${b}, ${this._opacity})`;
356
+
357
+ return html`
358
+ <div class="color-picker ${this.disabled ? 'disabled' : ''}">
359
+
360
+ <!-- Saturation / Brightness 2D panel -->
361
+ <div
362
+ class="sv-panel"
363
+ style=${styleMap({ '--hue-color': hueColor })}
364
+ @pointerdown=${this._onSvPointerDown}
365
+ @pointermove=${this._onSvPointerMove}
366
+ @pointerup=${this._onSvPointerUp}
367
+ @pointercancel=${this._onSvPointerUp}
368
+ >
369
+ <div
370
+ class="sv-cursor"
371
+ role="slider"
372
+ tabindex=${this.disabled ? '-1' : '0'}
373
+ aria-label="Saturation and brightness"
374
+ aria-valuetext="Saturation ${Math.round(this._sat * 100)}%, Brightness ${Math.round(this._bri * 100)}%"
375
+ style=${styleMap({
376
+ left: `${this._sat * 100}%`,
377
+ top: `${(1 - this._bri) * 100}%`,
378
+ })}
379
+ @keydown=${this._onSvKeyDown}
380
+ ></div>
381
+ </div>
382
+
383
+ <!-- Preview swatch + sliders -->
384
+ <div class="controls-row">
385
+ <div class="color-preview">
386
+ <div class="preview-inner" style=${styleMap({ background: previewColor })}></div>
387
+ </div>
388
+
389
+ <div class="sliders">
390
+ <!-- Hue slider -->
391
+ <div
392
+ class="slider-track hue-track"
393
+ @pointerdown=${this._onHuePointerDown}
394
+ @pointermove=${this._onHuePointerMove}
395
+ @pointerup=${this._onHuePointerUp}
396
+ @pointercancel=${this._onHuePointerUp}
397
+ >
398
+ <div
399
+ class="slider-thumb"
400
+ role="slider"
401
+ tabindex=${this.disabled ? '-1' : '0'}
402
+ aria-label="Hue"
403
+ aria-valuemin="0"
404
+ aria-valuemax="360"
405
+ aria-valuenow=${this._hue}
406
+ style=${styleMap({ left: `${(this._hue / 360) * 100}%` })}
407
+ @keydown=${this._onHueKeyDown}
408
+ ></div>
409
+ </div>
410
+
411
+ ${this.alpha ? html`
412
+ <!-- Alpha slider -->
413
+ <div
414
+ class="slider-track alpha-track"
415
+ style=${styleMap({ '--current-color': currentHex })}
416
+ @pointerdown=${this._onAlphaPointerDown}
417
+ @pointermove=${this._onAlphaPointerMove}
418
+ @pointerup=${this._onAlphaPointerUp}
419
+ @pointercancel=${this._onAlphaPointerUp}
420
+ >
421
+ <div
422
+ class="slider-thumb"
423
+ role="slider"
424
+ tabindex=${this.disabled ? '-1' : '0'}
425
+ aria-label="Opacity"
426
+ aria-valuemin="0"
427
+ aria-valuemax="100"
428
+ aria-valuenow=${Math.round(this._opacity * 100)}
429
+ style=${styleMap({ left: `${this._opacity * 100}%` })}
430
+ @keydown=${this._onAlphaKeyDown}
431
+ ></div>
432
+ </div>
433
+ ` : nothing}
434
+ </div>
435
+ </div>
436
+
437
+ <!-- Hex + alpha inputs -->
438
+ <div class="inputs-row">
439
+ <div class="hex-input-wrapper">
440
+ <span class="input-label">HEX</span>
441
+ <span class="hex-prefix">#</span>
442
+ <input
443
+ class="hex-input"
444
+ type="text"
445
+ inputmode="text"
446
+ maxlength="6"
447
+ spellcheck="false"
448
+ autocomplete="off"
449
+ .value=${this._hexInput}
450
+ ?disabled=${this.disabled}
451
+ aria-label="Hex color value"
452
+ @input=${this._onHexInput}
453
+ @change=${this._onHexChange}
454
+ />
455
+ </div>
456
+
457
+ ${this.alpha ? html`
458
+ <div class="alpha-input-wrapper">
459
+ <input
460
+ class="alpha-input"
461
+ type="number"
462
+ min="0"
463
+ max="100"
464
+ step="1"
465
+ .value=${String(Math.round(this._opacity * 100))}
466
+ ?disabled=${this.disabled}
467
+ aria-label="Opacity percentage"
468
+ @change=${this._onAlphaNumberChange}
469
+ />
470
+ <span class="input-label">%</span>
471
+ </div>
472
+ ` : nothing}
473
+ </div>
474
+
475
+ </div>
476
+ `;
477
+ }
478
+ }
@@ -0,0 +1 @@
1
+ export { ColorPicker } from './color-picker.js';
package/src/index.ts CHANGED
@@ -51,6 +51,7 @@ export { Image } from './image/index.js';
51
51
  export { Svg } from './svg/index.js';
52
52
  export { Tab, TabGroup, TabPanel, Tabs } from './tabs/index.js';
53
53
  export { Slider } from './slider/index.js';
54
+ export { ColorPicker } from './color-picker/index.js';
54
55
  export { ChartDoughnut } from './chart-doughnut/index.js';
55
56
  export { ChartPie } from './chart-pie/index.js';
56
57
  export { ChartBar, ChartStackedBar } from './chart-bar/index.js';
@@ -6,6 +6,7 @@
6
6
  --item-container-color: transparent;
7
7
  --item-height: 3rem;
8
8
  --item-multiline-height: 4rem;
9
+ --item-label-text-color: var(--color-on-surface);
9
10
  }
10
11
 
11
12
  :host {
@@ -25,7 +26,8 @@
25
26
 
26
27
  overflow: hidden;
27
28
 
28
- color: var(--item-label-text-color, var(--color-on-surface));
29
+ color: var(--item-label-text-color);
30
+ --icon-color: var(--item-label-text-color);
29
31
  opacity: var(--item-label-text-opacity, 1);
30
32
 
31
33
  font-family: var(
package/src/item/item.ts CHANGED
@@ -19,7 +19,6 @@ import { hasMeaningfulContent } from '@/__internal/utils/observe-slot-change.js'
19
19
  * <wc-item>
20
20
  * <wc-icon slot="start" name="notifications"></wc-icon>
21
21
  *
22
- * <span slot="overline">Settings</span>
23
22
  * <span slot="headline">Notifications</span>
24
23
  * <span slot="supporting-text">Manage alerts and reminders</span>
25
24
  *
@@ -19,7 +19,6 @@
19
19
  .list-item {
20
20
  position: relative;
21
21
  width: 100%;
22
- cursor: pointer;
23
22
  text-align: start;
24
23
 
25
24
  .list-item-content {
@@ -81,6 +80,11 @@
81
80
  }
82
81
  }
83
82
 
83
+ :host([actionable]:not([disabled]):not([soft-disabled])),
84
+ :host([href]:not([disabled]):not([soft-disabled])) {
85
+ cursor: pointer;
86
+ }
87
+
84
88
  :host(:not([skeleton])) {
85
89
  .skeleton {
86
90
  display: none;
@@ -49,6 +49,9 @@ export class ListItem extends mixinBaseButton(
49
49
  /** When true, renders the list-item in a loading skeleton state. */
50
50
  @property({ type: Boolean, reflect: true }) skeleton: boolean = false;
51
51
 
52
+ @property({ type: Boolean, reflect: true })
53
+ actionable: boolean = false;
54
+
52
55
  @query('#list-item') readonly itemElement!: HTMLElement | null;
53
56
 
54
57
  constructor() {
@@ -99,8 +102,13 @@ export class ListItem extends mixinBaseButton(
99
102
  );
100
103
  }
101
104
 
105
+ isClickable() {
106
+ return this.actionable || isLink(this);
107
+ }
108
+
102
109
  render() {
103
110
  const isElementLink = isLink(this);
111
+ const clickable = this.isClickable();
104
112
 
105
113
  const cssClasses = {
106
114
  'list-item': true,
@@ -111,6 +119,18 @@ export class ListItem extends mixinBaseButton(
111
119
  // Needed for closure conformance
112
120
  const { ariaLabel } = this as ARIAMixinStrict;
113
121
 
122
+ if (!clickable) {
123
+ return html`
124
+ <div
125
+ id="list-item"
126
+ class=${classMap(cssClasses)}
127
+ aria-label="${ariaLabel || nothing}"
128
+ >
129
+ ${this.renderContent(clickable)}
130
+ </div>
131
+ `;
132
+ }
133
+
114
134
  if (!isElementLink) {
115
135
  return html`
116
136
  <button
@@ -121,7 +141,7 @@ export class ListItem extends mixinBaseButton(
121
141
  ?aria-disabled=${this.softDisabled}
122
142
  @click=${this.__dispatchClick}
123
143
  >
124
- ${this.renderContent()}
144
+ ${this.renderContent(clickable)}
125
145
  </button>
126
146
  `;
127
147
  }
@@ -132,18 +152,18 @@ export class ListItem extends mixinBaseButton(
132
152
  class=${classMap(cssClasses)}
133
153
  href=${this.href}
134
154
  target=${this.target}
135
- rel=${ifDefined(this.rel)}
136
- download=${ifDefined(this.download)}
155
+ ?rel=${this.rel}
156
+ ?download=${this.download}
137
157
  tabindex=${this.disabled ? '-1' : '0'}
138
158
  aria-disabled=${String(this.disabled || this.softDisabled)}
139
159
  @click=${this.__dispatchClick}
140
160
  >
141
- ${this.renderContent()}
161
+ ${this.renderContent(clickable)}
142
162
  </a>
143
163
  `;
144
164
  }
145
165
 
146
- renderContent() {
166
+ renderContent(clickable: boolean) {
147
167
  const hasLeading = this.__hasNamedSlot('leading');
148
168
  const hasTrailingSupportingText = this.__hasNamedSlot(
149
169
  'trailing-supporting-text',
@@ -151,15 +171,21 @@ export class ListItem extends mixinBaseButton(
151
171
  const hasTrailing = this.__hasNamedSlot('trailing');
152
172
 
153
173
  return html`
154
- <wc-item class="list-item-content">
155
- <wc-focus-ring
156
- class="focus-ring"
157
- for="list-item"
158
- slot="container"
159
- ></wc-focus-ring>
160
- <div class="background" slot="container"></div>
161
- <wc-ripple class="ripple" for="list-item" slot="container"></wc-ripple>
162
- <wc-skeleton class="skeleton" slot="container"></wc-skeleton>
174
+ <wc-item class="list-item-content" ?inert=${clickable}>
175
+ ${clickable
176
+ ? html`<wc-focus-ring
177
+ class="focus-ring"
178
+ for="list-item"
179
+ slot="container"
180
+ ></wc-focus-ring>
181
+ <div class="background" slot="container"></div>
182
+ <wc-ripple
183
+ class="ripple"
184
+ for="list-item"
185
+ slot="container"
186
+ ></wc-ripple>
187
+ <wc-skeleton class="skeleton" slot="container"></wc-skeleton> `
188
+ : null}
163
189
 
164
190
  <slot name="leading" slot="start" ?hidden=${!hasLeading}></slot>
165
191
  <slot name="overline" slot="overline"></slot>