@redvars/peacock 3.2.10 → 3.3.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 (197) hide show
  1. package/dist/{PeacockComponent-CxJc63xj.js → IndividualComponent-tDnXrOLV.js} +3 -3
  2. package/dist/IndividualComponent-tDnXrOLV.js.map +1 -0
  3. package/dist/{button-DaL4va7Q.js → button-BGFJfbT2.js} +22 -35
  4. package/dist/button-BGFJfbT2.js.map +1 -0
  5. package/dist/button-group.js +8 -8
  6. package/dist/button-group.js.map +1 -1
  7. package/dist/button.js +6 -5
  8. package/dist/button.js.map +1 -1
  9. package/dist/chart-donut.js +307 -0
  10. package/dist/chart-donut.js.map +1 -0
  11. package/dist/chart-doughnut.js +307 -0
  12. package/dist/chart-doughnut.js.map +1 -0
  13. package/dist/chart-pie.js +259 -0
  14. package/dist/chart-pie.js.map +1 -0
  15. package/dist/{class-map-BvQRv7eW.js → class-map-DpeNtqCn.js} +2 -2
  16. package/dist/{class-map-BvQRv7eW.js.map → class-map-DpeNtqCn.js.map} +1 -1
  17. package/dist/clock.js +5 -6
  18. package/dist/clock.js.map +1 -1
  19. package/dist/code-editor.js +37 -24
  20. package/dist/code-editor.js.map +1 -1
  21. package/dist/code-highlighter.js +21 -7
  22. package/dist/code-highlighter.js.map +1 -1
  23. package/dist/custom-elements-jsdocs.json +5377 -3122
  24. package/dist/custom-elements.json +5527 -3940
  25. package/dist/{dispatch-event-utils-vbdiOSeC.js → dispatch-event-utils-B4odODQf.js} +2 -15
  26. package/dist/dispatch-event-utils-B4odODQf.js.map +1 -0
  27. package/dist/index.js +13 -9
  28. package/dist/index.js.map +1 -1
  29. package/dist/number-counter.js +12 -10
  30. package/dist/number-counter.js.map +1 -1
  31. package/dist/{observe-theme-change-NneLARW8.js → observe-theme-change-BISF-Gl5.js} +2 -2
  32. package/dist/{observe-theme-change-NneLARW8.js.map → observe-theme-change-BISF-Gl5.js.map} +1 -1
  33. package/dist/peacock-loader.js +124 -62
  34. package/dist/peacock-loader.js.map +1 -1
  35. package/dist/query-QBcUV-L_.js +15 -0
  36. package/dist/query-QBcUV-L_.js.map +1 -0
  37. package/dist/{image-v3BujlY5.js → slider-Dk9CFWTG.js} +1606 -327
  38. package/dist/slider-Dk9CFWTG.js.map +1 -0
  39. package/dist/src/IndividualComponent.d.ts +1 -0
  40. package/dist/src/accordion/{accordion-item/accordion-item.d.ts → accordion-item.d.ts} +4 -4
  41. package/dist/src/accordion/{accordion/accordion.d.ts → accordion.d.ts} +6 -6
  42. package/dist/src/accordion/{accordion-item/index.d.ts → index.d.ts} +1 -0
  43. package/dist/src/avatar/avatar.d.ts +2 -2
  44. package/dist/src/badge/badge.d.ts +2 -2
  45. package/dist/src/breadcrumb/breadcrumb/breadcrumb.d.ts +7 -8
  46. package/dist/src/breadcrumb/breadcrumb-item/breadcrumb-item.d.ts +3 -3
  47. package/dist/src/button/button/button.d.ts +2 -2
  48. package/dist/src/button/button-group/button-group.d.ts +5 -5
  49. package/dist/src/button/icon-button/icon-button.d.ts +2 -2
  50. package/dist/src/chart-donut/chart-donut.d.ts +53 -0
  51. package/dist/src/chart-donut/index.d.ts +1 -0
  52. package/dist/src/chart-doughnut/chart-doughnut.d.ts +53 -0
  53. package/dist/src/chart-doughnut/index.d.ts +1 -0
  54. package/dist/src/chart-pie/chart-pie.d.ts +50 -0
  55. package/dist/src/chart-pie/index.d.ts +1 -0
  56. package/dist/src/checkbox/checkbox.d.ts +3 -6
  57. package/dist/src/chip/chip/chip.d.ts +4 -4
  58. package/dist/src/chip/tag/tag.d.ts +3 -3
  59. package/dist/src/clock/clock.d.ts +3 -4
  60. package/dist/src/code-editor/code-editor.d.ts +11 -9
  61. package/dist/src/container/container.d.ts +6 -11
  62. package/dist/src/date-picker/date-picker.d.ts +3 -3
  63. package/dist/src/divider/divider.d.ts +2 -2
  64. package/dist/src/elevation/elevation.d.ts +2 -2
  65. package/dist/src/empty-state/empty-state.d.ts +9 -2
  66. package/dist/src/field/field.d.ts +17 -0
  67. package/dist/src/focus-ring/focus-ring.d.ts +1 -1
  68. package/dist/src/icon/icon.d.ts +2 -2
  69. package/dist/src/image/image.d.ts +4 -12
  70. package/dist/src/index.d.ts +5 -1
  71. package/dist/src/input/input.d.ts +2 -2
  72. package/dist/src/link/link.d.ts +4 -5
  73. package/dist/src/menu/menu/menu.d.ts +16 -0
  74. package/dist/src/menu/menu-item/menu-item.d.ts +12 -0
  75. package/dist/src/menu/menu-list/menu-list.d.ts +15 -0
  76. package/dist/src/number-counter/number-counter.d.ts +9 -7
  77. package/dist/src/number-field/number-field.d.ts +1 -1
  78. package/dist/src/popover/index.d.ts +1 -1
  79. package/dist/src/progress/circular-progress/circular-progress.d.ts +3 -3
  80. package/dist/src/progress/linear-progress/linear-progress.d.ts +3 -3
  81. package/dist/src/ripple/ripple.d.ts +60 -4
  82. package/dist/src/skeleton/skeleton.d.ts +6 -5
  83. package/dist/src/slider/index.d.ts +1 -0
  84. package/dist/src/slider/slider.d.ts +52 -0
  85. package/dist/src/spinner/spinner.d.ts +2 -2
  86. package/dist/src/switch/switch.d.ts +2 -2
  87. package/dist/src/tabs/index.d.ts +4 -0
  88. package/dist/src/tabs/tab-group.d.ts +41 -0
  89. package/dist/src/tabs/tab-panel.d.ts +21 -0
  90. package/dist/src/tabs/tab.d.ts +58 -0
  91. package/dist/src/tabs/tabs.d.ts +27 -0
  92. package/dist/src/textarea/textarea.d.ts +3 -3
  93. package/dist/src/time-picker/time-picker.d.ts +3 -3
  94. package/dist/src/{popover/tooltip → tooltip}/tooltip.d.ts +6 -3
  95. package/dist/{state-B09bP3XH.js → state-8v48Exzh.js} +2 -2
  96. package/dist/{state-B09bP3XH.js.map → state-8v48Exzh.js.map} +1 -1
  97. package/dist/{style-map-B8xgVEc9.js → style-map-CfNHEkQp.js} +2 -2
  98. package/dist/{style-map-B8xgVEc9.js.map → style-map-CfNHEkQp.js.map} +1 -1
  99. package/dist/transform-DRuHEvar.js +3312 -0
  100. package/dist/transform-DRuHEvar.js.map +1 -0
  101. package/dist/tsconfig.tsbuildinfo +1 -1
  102. package/dist/{unsafe-html-B-dV3Jps.js → unsafe-html-CV6Je6HL.js} +2 -2
  103. package/dist/{unsafe-html-B-dV3Jps.js.map → unsafe-html-CV6Je6HL.js.map} +1 -1
  104. package/package.json +3 -1
  105. package/readme.md +2 -2
  106. package/src/{PeacockComponent.ts → IndividualComponent.ts} +1 -1
  107. package/src/accordion/{accordion-item/accordion-item.scss → accordion-item.scss} +1 -1
  108. package/src/accordion/{accordion-item/accordion-item.ts → accordion-item.ts} +5 -5
  109. package/src/accordion/{accordion/accordion.scss → accordion.scss} +2 -1
  110. package/src/accordion/{accordion/accordion.ts → accordion.ts} +6 -6
  111. package/src/accordion/{accordion-item/index.ts → index.ts} +2 -0
  112. package/src/avatar/avatar.ts +2 -2
  113. package/src/badge/badge.ts +2 -2
  114. package/src/breadcrumb/breadcrumb/breadcrumb.ts +7 -8
  115. package/src/breadcrumb/breadcrumb-item/breadcrumb-item.ts +3 -3
  116. package/src/button/BaseButton.ts +1 -1
  117. package/src/button/button/button.scss +9 -23
  118. package/src/button/button/button.ts +8 -8
  119. package/src/button/button-group/button-group.ts +7 -7
  120. package/src/button/icon-button/icon-button.ts +8 -8
  121. package/src/chart-donut/chart-donut.scss +37 -0
  122. package/src/chart-donut/chart-donut.ts +287 -0
  123. package/src/chart-donut/demo/index.html +51 -0
  124. package/src/chart-donut/index.ts +1 -0
  125. package/src/chart-doughnut/chart-donut.scss +37 -0
  126. package/src/chart-doughnut/chart-doughnut.ts +287 -0
  127. package/src/chart-doughnut/demo/index.html +51 -0
  128. package/src/chart-doughnut/index.ts +1 -0
  129. package/src/chart-pie/chart-pie.scss +27 -0
  130. package/src/chart-pie/chart-pie.ts +256 -0
  131. package/src/chart-pie/demo/index.html +51 -0
  132. package/src/chart-pie/index.ts +1 -0
  133. package/src/checkbox/checkbox.ts +3 -6
  134. package/src/chip/chip/chip.ts +6 -6
  135. package/src/chip/tag/tag.ts +6 -6
  136. package/src/clock/clock.ts +5 -6
  137. package/src/code-editor/code-editor.scss +3 -5
  138. package/src/code-editor/code-editor.ts +30 -15
  139. package/src/code-highlighter/code-highlighter.ts +19 -4
  140. package/src/container/container.ts +6 -11
  141. package/src/date-picker/date-picker.ts +7 -7
  142. package/src/divider/divider.ts +2 -2
  143. package/src/elevation/elevation.ts +2 -2
  144. package/src/empty-state/empty-state.ts +10 -3
  145. package/src/field/field.scss +4 -4
  146. package/src/field/field.ts +19 -2
  147. package/src/focus-ring/focus-ring.scss +2 -1
  148. package/src/focus-ring/focus-ring.ts +1 -1
  149. package/src/icon/icon.ts +2 -2
  150. package/src/icon/p-icon.ts +1 -1
  151. package/src/image/image.ts +4 -12
  152. package/src/index.ts +6 -2
  153. package/src/input/input.ts +6 -6
  154. package/src/link/link.ts +4 -5
  155. package/src/menu/menu/menu.ts +16 -0
  156. package/src/menu/menu-item/menu-item-colors.scss +2 -2
  157. package/src/menu/menu-item/menu-item.ts +14 -2
  158. package/src/menu/menu-list/menu-list.ts +16 -1
  159. package/src/number-counter/demo/index.html +1 -1
  160. package/src/number-counter/number-counter.ts +11 -9
  161. package/src/number-field/number-field.ts +7 -7
  162. package/src/peacock-loader.ts +71 -44
  163. package/src/popover/index.ts +1 -1
  164. package/src/progress/circular-progress/circular-progress.scss +1 -1
  165. package/src/progress/circular-progress/circular-progress.ts +3 -3
  166. package/src/progress/linear-progress/linear-progress.ts +3 -3
  167. package/src/ripple/ripple.ts +478 -94
  168. package/src/skeleton/skeleton.ts +6 -5
  169. package/src/slider/index.ts +1 -0
  170. package/src/slider/slider.scss +130 -0
  171. package/src/slider/slider.ts +178 -0
  172. package/src/spinner/spinner.ts +2 -2
  173. package/src/switch/switch.ts +4 -4
  174. package/src/tabs/index.ts +4 -0
  175. package/src/tabs/tab-group.scss +10 -0
  176. package/src/tabs/tab-group.ts +137 -0
  177. package/src/tabs/tab-panel.scss +12 -0
  178. package/src/tabs/tab-panel.ts +28 -0
  179. package/src/tabs/tab.scss +157 -0
  180. package/src/tabs/tab.ts +242 -0
  181. package/src/tabs/tabs.scss +18 -0
  182. package/src/tabs/tabs.ts +64 -0
  183. package/src/textarea/textarea.ts +5 -5
  184. package/src/time-picker/time-picker.ts +7 -7
  185. package/src/{popover/tooltip → tooltip}/tooltip.scss +1 -1
  186. package/src/{popover/tooltip → tooltip}/tooltip.ts +10 -6
  187. package/dist/PeacockComponent-CxJc63xj.js.map +0 -1
  188. package/dist/button-DaL4va7Q.js.map +0 -1
  189. package/dist/dispatch-event-utils-vbdiOSeC.js.map +0 -1
  190. package/dist/image-v3BujlY5.js.map +0 -1
  191. package/dist/src/PeacockComponent.d.ts +0 -1
  192. package/dist/src/accordion/accordion/index.d.ts +0 -1
  193. package/dist/src/avatar/p-avatar.d.ts +0 -3
  194. package/dist/src/badge/p-badge.d.ts +0 -3
  195. package/src/accordion/accordion/index.ts +0 -1
  196. package/src/avatar/p-avatar.ts +0 -5
  197. package/src/badge/p-badge.ts +0 -5
@@ -0,0 +1,307 @@
1
+ import { i, _ as __decorate, n, I as IndividualComponent, a as i$1, b } from './IndividualComponent-tDnXrOLV.js';
2
+ import { e } from './query-QBcUV-L_.js';
3
+ import { p as pie, o as ordinal, s as select, a as arc, c as cubicInOut, i as interpolateNumber } from './transform-DRuHEvar.js';
4
+
5
+ var css_248z = i`* {
6
+ box-sizing: border-box;
7
+ }
8
+
9
+ .screen-reader-only {
10
+ display: none !important;
11
+ }
12
+
13
+ :host {
14
+ display: inline-block;
15
+ }
16
+
17
+ .title {
18
+ font-family: var(--typography-title-large-emphasized-font-family) !important;
19
+ font-size: var(--typography-title-large-emphasized-font-size) !important;
20
+ font-weight: var(--typography-title-large-emphasized-font-weight) !important;
21
+ line-height: var(--typography-title-large-emphasized-line-height) !important;
22
+ letter-spacing: var(--typography-title-large-emphasized-letter-spacing) !important;
23
+ fill: var(--color-on-surface);
24
+ }
25
+
26
+ .label {
27
+ font-family: var(--typography-title-medium-font-family) !important;
28
+ font-size: var(--typography-title-medium-font-size) !important;
29
+ font-weight: var(--typography-title-medium-font-weight) !important;
30
+ line-height: var(--typography-title-medium-line-height) !important;
31
+ letter-spacing: var(--typography-title-medium-letter-spacing) !important;
32
+ fill: var(--color-on-surface);
33
+ }
34
+
35
+ .arc {
36
+ cursor: pointer;
37
+ transition: filter 150ms ease;
38
+ }
39
+ .arc:hover {
40
+ filter: brightness(1.2);
41
+ }
42
+
43
+ .item-polyline {
44
+ fill: none;
45
+ stroke-width: 1;
46
+ stroke: var(--color-on-surface);
47
+ }
48
+
49
+ .item-label {
50
+ fill: var(--color-on-surface);
51
+ font-family: var(--typography-label-medium-font-family) !important;
52
+ font-size: var(--typography-label-medium-font-size) !important;
53
+ font-weight: var(--typography-label-medium-font-weight) !important;
54
+ line-height: var(--typography-label-medium-line-height) !important;
55
+ letter-spacing: var(--typography-label-medium-letter-spacing) !important;
56
+ }`;
57
+
58
+ const chartColors = [];
59
+ ['purple', 'blue', 'red', 'green', 'yellow', 'orange'].forEach(colorName => {
60
+ chartColors.push({
61
+ color: `var(--color-${colorName})`,
62
+ });
63
+ });
64
+ function debounce(fn, wait) {
65
+ let timer;
66
+ return ((...args) => {
67
+ clearTimeout(timer);
68
+ timer = setTimeout(() => fn(...args), wait);
69
+ });
70
+ }
71
+ /**
72
+ * @label Chart Donut
73
+ * @tag wc-chart-donut
74
+ * @rawTag chart-donut
75
+ * @summary A donut chart is a circular chart with a blank center. The area in the center can be used to display information.
76
+ * @tags charts
77
+ *
78
+ * @example
79
+ * ```html
80
+ * <wc-chart-donut width="400" label="Total"></wc-chart-donut>
81
+ * <script>
82
+ * document.querySelector('wc-chart-donut').data = [
83
+ * { name: 'A', value: 30, label: 'Category A' },
84
+ * { name: 'B', value: 50, label: 'Category B' },
85
+ * { name: 'C', value: 20, label: 'Category C' },
86
+ * ];
87
+ * </script>
88
+ * ```
89
+ */
90
+ let ChartDonut = class ChartDonut extends i$1 {
91
+ constructor() {
92
+ super(...arguments);
93
+ /** Width (and height) of the chart in pixels. */
94
+ this.width = 0;
95
+ /** Margin around the chart. */
96
+ this.margin = 10;
97
+ /** Whether to show labels outside the chart. */
98
+ this.showLabels = true;
99
+ /** Chart data array. Each item should have name, value, and optional label and color. */
100
+ this.data = [];
101
+ this._initialized = false;
102
+ this._debouncedRenderChart = debounce(() => {
103
+ this._renderChart(true);
104
+ }, 300);
105
+ }
106
+ firstUpdated() {
107
+ this._renderChart(false);
108
+ }
109
+ updated(changedProperties) {
110
+ if (!this._initialized) {
111
+ this._initialized = true;
112
+ return;
113
+ }
114
+ const watchedProps = ['width', 'margin', 'showLabels', 'data'];
115
+ const hasChanged = watchedProps.some(prop => changedProperties.has(prop));
116
+ if (hasChanged) {
117
+ this._debouncedRenderChart();
118
+ }
119
+ }
120
+ _getRadius() {
121
+ return this.width / 2 - this.margin - 100;
122
+ }
123
+ _getTotal() {
124
+ return this.data.reduce((total, d) => total + d.value, 0);
125
+ }
126
+ _getPieData() {
127
+ const pie$1 = pie()
128
+ .sort(null)
129
+ .value(d => d.value);
130
+ return pie$1(this.data);
131
+ }
132
+ _getColorScale() {
133
+ return ordinal()
134
+ .domain(this.data.map(d => d.name))
135
+ .range(chartColors);
136
+ }
137
+ _renderChart(animate) {
138
+ if (!this.svgElement)
139
+ return;
140
+ const DURATION = 500;
141
+ const radius = this._getRadius();
142
+ const pieData = this._getPieData();
143
+ const colorScale = this._getColorScale();
144
+ const total = this._getTotal();
145
+ const svg = select(this.svgElement);
146
+ const doughnutArc = arc()
147
+ .innerRadius(radius * 0.72)
148
+ .outerRadius(radius);
149
+ const labelsArc = arc()
150
+ .innerRadius(radius + 10)
151
+ .outerRadius(radius + 10);
152
+ // Update SVG dimensions and center transform
153
+ svg.attr('width', this.width).attr('height', this.width);
154
+ svg
155
+ .select('.chart-container')
156
+ .attr('transform', `translate(${this.width / 2},${this.width / 2})`);
157
+ // Arc paths — keyed by name so D3 matches elements across updates
158
+ const $paths = svg
159
+ .select('.arc-container')
160
+ .selectAll('.arc')
161
+ .data(pieData, d => d.data.name)
162
+ .join('path')
163
+ .attr('class', 'arc')
164
+ .style('fill', d => d.data.color || colorScale(d.data.name).color);
165
+ if (animate) {
166
+ $paths
167
+ .transition()
168
+ .duration(DURATION)
169
+ .ease(cubicInOut)
170
+ .attrTween('d', function (d) {
171
+ const self = this;
172
+ // Interpolate from the last rendered angles to the new ones.
173
+ // New (entering) arcs start collapsed at their startAngle.
174
+ const prev = self._prevDatum ?? {
175
+ startAngle: d.startAngle,
176
+ endAngle: d.startAngle,
177
+ };
178
+ self._prevDatum = d;
179
+ const iStart = interpolateNumber(prev.startAngle, d.startAngle);
180
+ const iEnd = interpolateNumber(prev.endAngle, d.endAngle);
181
+ return (t) => doughnutArc({ ...d, startAngle: iStart(t), endAngle: iEnd(t) }) ??
182
+ '';
183
+ });
184
+ }
185
+ else {
186
+ // Initial render: draw immediately and seed previous-datum for later tweens
187
+ $paths
188
+ .each(function (d) {
189
+ this._prevDatum = d;
190
+ })
191
+ .attr('d', d => doughnutArc(d) ?? '');
192
+ }
193
+ // Animate the central total counter
194
+ const $title = svg.select('.title');
195
+ if (animate) {
196
+ $title
197
+ .transition()
198
+ .duration(DURATION)
199
+ .ease(cubicInOut)
200
+ .tween('text', function () {
201
+ const sel = select(this);
202
+ const start = parseFloat(sel.text()) || 0;
203
+ const interp = interpolateNumber(start, total);
204
+ return function (t) {
205
+ sel.text(Math.round(interp(t)));
206
+ };
207
+ });
208
+ }
209
+ else {
210
+ $title.text(total);
211
+ }
212
+ // Label polylines and text
213
+ const $chartContainer = svg.select('.chart-container');
214
+ if (this.showLabels) {
215
+ const pointsFn = (d) => {
216
+ const posA = doughnutArc.centroid(d);
217
+ const posB = labelsArc.centroid(d);
218
+ const posC = posB.slice();
219
+ const midAngle = d.startAngle + (d.endAngle - d.startAngle) / 2;
220
+ posC[0] = radius * (midAngle < Math.PI ? 1 : -1);
221
+ return [posA, posB, posC].map(p => p.join(',')).join(' ');
222
+ };
223
+ const transformFn = (d) => {
224
+ const pos = labelsArc.centroid(d);
225
+ const midAngle = d.startAngle + (d.endAngle - d.startAngle) / 2;
226
+ pos[0] = radius * (midAngle < Math.PI ? 1 : -1);
227
+ return `translate(${pos})`;
228
+ };
229
+ const anchorFn = (d) => {
230
+ const midAngle = d.startAngle + (d.endAngle - d.startAngle) / 2;
231
+ return midAngle < Math.PI ? 'start' : 'end';
232
+ };
233
+ const $polylines = $chartContainer
234
+ .selectAll('.item-polyline')
235
+ .data(pieData, d => d.data.name)
236
+ .join('polyline')
237
+ .attr('class', 'item-polyline');
238
+ const $labels = $chartContainer
239
+ .selectAll('.item-label')
240
+ .data(pieData, d => d.data.name)
241
+ .join('text')
242
+ .attr('class', 'item-label')
243
+ .text(d => d.data.label ?? '');
244
+ if (animate) {
245
+ $polylines
246
+ .transition()
247
+ .duration(DURATION)
248
+ .ease(cubicInOut)
249
+ .attr('points', pointsFn);
250
+ $labels
251
+ .transition()
252
+ .duration(DURATION)
253
+ .ease(cubicInOut)
254
+ .attr('transform', transformFn)
255
+ .style('text-anchor', anchorFn);
256
+ }
257
+ else {
258
+ $polylines.attr('points', pointsFn);
259
+ $labels.attr('transform', transformFn).style('text-anchor', anchorFn);
260
+ }
261
+ }
262
+ else {
263
+ $chartContainer.selectAll('.item-polyline').remove();
264
+ $chartContainer.selectAll('.item-label').remove();
265
+ }
266
+ }
267
+ render() {
268
+ return b `
269
+ <div class="chart">
270
+ <svg>
271
+ <g class="chart-container">
272
+ <g class="arc-container"></g>
273
+ <text class="title" text-anchor="middle"></text>
274
+ <text class="label" text-anchor="middle" y="16">
275
+ ${this.label}
276
+ </text>
277
+ </g>
278
+ </svg>
279
+ </div>
280
+ `;
281
+ }
282
+ };
283
+ ChartDonut.styles = [css_248z];
284
+ __decorate([
285
+ e('svg')
286
+ ], ChartDonut.prototype, "svgElement", void 0);
287
+ __decorate([
288
+ n({ type: Number, reflect: true })
289
+ ], ChartDonut.prototype, "width", void 0);
290
+ __decorate([
291
+ n({ type: Number, reflect: true })
292
+ ], ChartDonut.prototype, "margin", void 0);
293
+ __decorate([
294
+ n({ type: Boolean, reflect: true, attribute: 'show-labels' })
295
+ ], ChartDonut.prototype, "showLabels", void 0);
296
+ __decorate([
297
+ n({ type: Array })
298
+ ], ChartDonut.prototype, "data", void 0);
299
+ __decorate([
300
+ n({ type: String })
301
+ ], ChartDonut.prototype, "label", void 0);
302
+ ChartDonut = __decorate([
303
+ IndividualComponent
304
+ ], ChartDonut);
305
+
306
+ export { ChartDonut };
307
+ //# sourceMappingURL=chart-donut.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chart-donut.js","sources":["../../src/chart-donut/chart-donut.ts"],"sourcesContent":["import { html, LitElement, PropertyValues } from 'lit';\nimport { property, query } from 'lit/decorators.js';\nimport IndividualComponent from 'src/IndividualComponent.js';\nimport * as d3 from 'd3';\nimport styles from './chart-donut.scss';\n\nexport type ChartDonutColor = {\n color: string;\n};\n\nexport type ChartDonutItem = {\n name: string;\n value: number;\n label?: string;\n color?: string;\n};\n\nconst chartColors: ChartDonutColor[] = [];\n['purple', 'blue', 'red', 'green', 'yellow', 'orange'].forEach(colorName => {\n chartColors.push({\n color: `var(--color-${colorName})`,\n });\n});\n\n/** SVGPathElement augmented with the last rendered arc datum for smooth tween interpolation. */\ninterface ArcPathElement extends SVGPathElement {\n _prevDatum?: d3.PieArcDatum<ChartDonutItem>;\n}\n\nfunction debounce<T extends (...args: any[]) => void>(fn: T, wait: number): T {\n let timer: ReturnType<typeof setTimeout>;\n return ((...args: any[]) => {\n clearTimeout(timer);\n timer = setTimeout(() => fn(...args), wait);\n }) as T;\n}\n\n/**\n * @label Chart Donut\n * @tag wc-chart-donut\n * @rawTag chart-donut\n * @summary A donut chart is a circular chart with a blank center. The area in the center can be used to display information.\n * @tags charts\n *\n * @example\n * ```html\n * <wc-chart-donut width=\"400\" label=\"Total\"></wc-chart-donut>\n * <script>\n * document.querySelector('wc-chart-donut').data = [\n * { name: 'A', value: 30, label: 'Category A' },\n * { name: 'B', value: 50, label: 'Category B' },\n * { name: 'C', value: 20, label: 'Category C' },\n * ];\n * </script>\n * ```\n */\n@IndividualComponent\nexport class ChartDonut extends LitElement {\n static styles = [styles];\n\n @query('svg')\n private svgElement?: SVGElement;\n\n /** Width (and height) of the chart in pixels. */\n @property({ type: Number, reflect: true }) width: number = 0;\n\n /** Margin around the chart. */\n @property({ type: Number, reflect: true }) margin: number = 10;\n\n /** Whether to show labels outside the chart. */\n @property({ type: Boolean, reflect: true, attribute: 'show-labels' })\n showLabels: boolean = true;\n\n /** Chart data array. Each item should have name, value, and optional label and color. */\n @property({ type: Array }) data: ChartDonutItem[] = [];\n\n /** Label displayed in the center of the donut. */\n @property({ type: String }) label?: string;\n\n private _initialized = false;\n\n private _debouncedRenderChart = debounce(() => {\n this._renderChart(true);\n }, 300);\n\n firstUpdated() {\n this._renderChart(false);\n }\n\n updated(changedProperties: PropertyValues) {\n if (!this._initialized) {\n this._initialized = true;\n return;\n }\n const watchedProps = ['width', 'margin', 'showLabels', 'data'];\n const hasChanged = watchedProps.some(prop => changedProperties.has(prop));\n if (hasChanged) {\n this._debouncedRenderChart();\n }\n }\n\n private _getRadius(): number {\n return this.width / 2 - this.margin - 100;\n }\n\n private _getTotal(): number {\n return this.data.reduce((total, d) => total + d.value, 0);\n }\n\n private _getPieData() {\n const pie = d3\n .pie<ChartDonutItem>()\n .sort(null)\n .value(d => d.value);\n return pie(this.data);\n }\n\n private _getColorScale() {\n return d3\n .scaleOrdinal<string, ChartDonutColor>()\n .domain(this.data.map(d => d.name))\n .range(chartColors);\n }\n\n private _renderChart(animate: boolean) {\n if (!this.svgElement) return;\n\n const DURATION = 500;\n const radius = this._getRadius();\n const pieData = this._getPieData();\n const colorScale = this._getColorScale();\n const total = this._getTotal();\n\n const svg = d3.select(this.svgElement);\n\n const doughnutArc = d3\n .arc<d3.PieArcDatum<ChartDonutItem>>()\n .innerRadius(radius * 0.72)\n .outerRadius(radius);\n\n const labelsArc = d3\n .arc<d3.PieArcDatum<ChartDonutItem>>()\n .innerRadius(radius + 10)\n .outerRadius(radius + 10);\n\n // Update SVG dimensions and center transform\n svg.attr('width', this.width).attr('height', this.width);\n svg\n .select('.chart-container')\n .attr('transform', `translate(${this.width / 2},${this.width / 2})`);\n\n // Arc paths — keyed by name so D3 matches elements across updates\n const $paths = svg\n .select('.arc-container')\n .selectAll<SVGPathElement, d3.PieArcDatum<ChartDonutItem>>('.arc')\n .data(pieData, d => d.data.name)\n .join('path')\n .attr('class', 'arc')\n .style('fill', d => d.data.color || colorScale(d.data.name).color);\n\n if (animate) {\n $paths\n .transition()\n .duration(DURATION)\n .ease(d3.easeCubicInOut)\n .attrTween('d', function (this: SVGPathElement, d) {\n const self = this as ArcPathElement;\n // Interpolate from the last rendered angles to the new ones.\n // New (entering) arcs start collapsed at their startAngle.\n const prev: { startAngle: number; endAngle: number } =\n self._prevDatum ?? {\n startAngle: d.startAngle,\n endAngle: d.startAngle,\n };\n self._prevDatum = d;\n const iStart = d3.interpolateNumber(prev.startAngle, d.startAngle);\n const iEnd = d3.interpolateNumber(prev.endAngle, d.endAngle);\n return (t: number) =>\n doughnutArc({ ...d, startAngle: iStart(t), endAngle: iEnd(t) }) ??\n '';\n });\n } else {\n // Initial render: draw immediately and seed previous-datum for later tweens\n $paths\n .each(function (this: SVGPathElement, d) {\n (this as ArcPathElement)._prevDatum = d;\n })\n .attr('d', d => doughnutArc(d) ?? '');\n }\n\n // Animate the central total counter\n const $title = svg.select('.title');\n if (animate) {\n $title\n .transition()\n .duration(DURATION)\n .ease(d3.easeCubicInOut)\n .tween('text', function (this: d3.BaseType) {\n const sel = d3.select(this as SVGTextElement);\n const start = parseFloat(sel.text()) || 0;\n const interp = d3.interpolateNumber(start, total);\n return function (t: number) {\n sel.text(Math.round(interp(t)));\n };\n });\n } else {\n $title.text(total);\n }\n\n // Label polylines and text\n const $chartContainer = svg.select('.chart-container');\n\n if (this.showLabels) {\n const pointsFn = (d: d3.PieArcDatum<ChartDonutItem>) => {\n const posA = doughnutArc.centroid(d);\n const posB = labelsArc.centroid(d);\n const posC = posB.slice() as [number, number];\n const midAngle = d.startAngle + (d.endAngle - d.startAngle) / 2;\n posC[0] = radius * (midAngle < Math.PI ? 1 : -1);\n return [posA, posB, posC].map(p => p.join(',')).join(' ');\n };\n\n const transformFn = (d: d3.PieArcDatum<ChartDonutItem>) => {\n const pos = labelsArc.centroid(d);\n const midAngle = d.startAngle + (d.endAngle - d.startAngle) / 2;\n pos[0] = radius * (midAngle < Math.PI ? 1 : -1);\n return `translate(${pos})`;\n };\n\n const anchorFn = (d: d3.PieArcDatum<ChartDonutItem>) => {\n const midAngle = d.startAngle + (d.endAngle - d.startAngle) / 2;\n return midAngle < Math.PI ? 'start' : 'end';\n };\n\n const $polylines = $chartContainer\n .selectAll<SVGPolylineElement, d3.PieArcDatum<ChartDonutItem>>(\n '.item-polyline',\n )\n .data(pieData, d => d.data.name)\n .join('polyline')\n .attr('class', 'item-polyline');\n\n const $labels = $chartContainer\n .selectAll<SVGTextElement, d3.PieArcDatum<ChartDonutItem>>('.item-label')\n .data(pieData, d => d.data.name)\n .join('text')\n .attr('class', 'item-label')\n .text(d => d.data.label ?? '');\n\n if (animate) {\n $polylines\n .transition()\n .duration(DURATION)\n .ease(d3.easeCubicInOut)\n .attr('points', pointsFn);\n $labels\n .transition()\n .duration(DURATION)\n .ease(d3.easeCubicInOut)\n .attr('transform', transformFn)\n .style('text-anchor', anchorFn);\n } else {\n $polylines.attr('points', pointsFn);\n $labels.attr('transform', transformFn).style('text-anchor', anchorFn);\n }\n } else {\n $chartContainer.selectAll('.item-polyline').remove();\n $chartContainer.selectAll('.item-label').remove();\n }\n }\n\n render() {\n return html`\n <div class=\"chart\">\n <svg>\n <g class=\"chart-container\">\n <g class=\"arc-container\"></g>\n <text class=\"title\" text-anchor=\"middle\"></text>\n <text class=\"label\" text-anchor=\"middle\" y=\"16\">\n ${this.label}\n </text>\n </g>\n </svg>\n </div>\n `;\n }\n}\n"],"names":["LitElement","pie","d3\n .pie","d3\n .scaleOrdinal","d3.select","d3\n .arc","d3.easeCubicInOut","d3.interpolateNumber","html","styles","query","property"],"mappings":"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"}
@@ -0,0 +1,307 @@
1
+ import { i, _ as __decorate, n, I as IndividualComponent, a as i$1, b } from './IndividualComponent-tDnXrOLV.js';
2
+ import { e } from './query-QBcUV-L_.js';
3
+ import { p as pie, o as ordinal, s as select, a as arc, c as cubicInOut, i as interpolateNumber } from './transform-DRuHEvar.js';
4
+
5
+ var css_248z = i`* {
6
+ box-sizing: border-box;
7
+ }
8
+
9
+ .screen-reader-only {
10
+ display: none !important;
11
+ }
12
+
13
+ :host {
14
+ display: inline-block;
15
+ }
16
+
17
+ .title {
18
+ font-family: var(--typography-title-large-emphasized-font-family) !important;
19
+ font-size: var(--typography-title-large-emphasized-font-size) !important;
20
+ font-weight: var(--typography-title-large-emphasized-font-weight) !important;
21
+ line-height: var(--typography-title-large-emphasized-line-height) !important;
22
+ letter-spacing: var(--typography-title-large-emphasized-letter-spacing) !important;
23
+ fill: var(--color-on-surface);
24
+ }
25
+
26
+ .label {
27
+ font-family: var(--typography-title-medium-font-family) !important;
28
+ font-size: var(--typography-title-medium-font-size) !important;
29
+ font-weight: var(--typography-title-medium-font-weight) !important;
30
+ line-height: var(--typography-title-medium-line-height) !important;
31
+ letter-spacing: var(--typography-title-medium-letter-spacing) !important;
32
+ fill: var(--color-on-surface);
33
+ }
34
+
35
+ .arc {
36
+ cursor: pointer;
37
+ transition: filter 150ms ease;
38
+ }
39
+ .arc:hover {
40
+ filter: brightness(1.2);
41
+ }
42
+
43
+ .item-polyline {
44
+ fill: none;
45
+ stroke-width: 1;
46
+ stroke: var(--color-on-surface);
47
+ }
48
+
49
+ .item-label {
50
+ fill: var(--color-on-surface);
51
+ font-family: var(--typography-label-medium-font-family) !important;
52
+ font-size: var(--typography-label-medium-font-size) !important;
53
+ font-weight: var(--typography-label-medium-font-weight) !important;
54
+ line-height: var(--typography-label-medium-line-height) !important;
55
+ letter-spacing: var(--typography-label-medium-letter-spacing) !important;
56
+ }`;
57
+
58
+ const chartColors = [];
59
+ ['purple', 'blue', 'red', 'green', 'yellow', 'orange'].forEach(colorName => {
60
+ chartColors.push({
61
+ color: `var(--color-${colorName})`,
62
+ });
63
+ });
64
+ function debounce(fn, wait) {
65
+ let timer;
66
+ return ((...args) => {
67
+ clearTimeout(timer);
68
+ timer = setTimeout(() => fn(...args), wait);
69
+ });
70
+ }
71
+ /**
72
+ * @label Chart Doughnut
73
+ * @tag wc-chart-doughnut
74
+ * @rawTag chart-doughnut
75
+ * @summary A doughnut chart is a circular chart with a blank center. The area in the center can be used to display information.
76
+ * @tags charts
77
+ *
78
+ * @example
79
+ * ```html
80
+ * <wc-chart-doughnut width="400" label="Total"></wc-chart-doughnut>
81
+ * <script>
82
+ * document.querySelector('wc-chart-doughnut').data = [
83
+ * { name: 'A', value: 30, label: 'Category A' },
84
+ * { name: 'B', value: 50, label: 'Category B' },
85
+ * { name: 'C', value: 20, label: 'Category C' },
86
+ * ];
87
+ * </script>
88
+ * ```
89
+ */
90
+ let ChartDoughnut = class ChartDoughnut extends i$1 {
91
+ constructor() {
92
+ super(...arguments);
93
+ /** Width (and height) of the chart in pixels. */
94
+ this.width = 0;
95
+ /** Margin around the chart. */
96
+ this.margin = 10;
97
+ /** Whether to show labels outside the chart. */
98
+ this.showLabels = true;
99
+ /** Chart data array. Each item should have name, value, and optional label and color. */
100
+ this.data = [];
101
+ this._initialized = false;
102
+ this._debouncedRenderChart = debounce(() => {
103
+ this._renderChart(true);
104
+ }, 300);
105
+ }
106
+ firstUpdated() {
107
+ this._renderChart(false);
108
+ }
109
+ updated(changedProperties) {
110
+ if (!this._initialized) {
111
+ this._initialized = true;
112
+ return;
113
+ }
114
+ const watchedProps = ['width', 'margin', 'showLabels', 'data'];
115
+ const hasChanged = watchedProps.some(prop => changedProperties.has(prop));
116
+ if (hasChanged) {
117
+ this._debouncedRenderChart();
118
+ }
119
+ }
120
+ _getRadius() {
121
+ return this.width / 2 - this.margin - 100;
122
+ }
123
+ _getTotal() {
124
+ return this.data.reduce((total, d) => total + d.value, 0);
125
+ }
126
+ _getPieData() {
127
+ const pie$1 = pie()
128
+ .sort(null)
129
+ .value(d => d.value);
130
+ return pie$1(this.data);
131
+ }
132
+ _getColorScale() {
133
+ return ordinal()
134
+ .domain(this.data.map(d => d.name))
135
+ .range(chartColors);
136
+ }
137
+ _renderChart(animate) {
138
+ if (!this.svgElement)
139
+ return;
140
+ const DURATION = 500;
141
+ const radius = this._getRadius();
142
+ const pieData = this._getPieData();
143
+ const colorScale = this._getColorScale();
144
+ const total = this._getTotal();
145
+ const svg = select(this.svgElement);
146
+ const doughnutArc = arc()
147
+ .innerRadius(radius * 0.72)
148
+ .outerRadius(radius);
149
+ const labelsArc = arc()
150
+ .innerRadius(radius + 10)
151
+ .outerRadius(radius + 10);
152
+ // Update SVG dimensions and center transform
153
+ svg.attr('width', this.width).attr('height', this.width);
154
+ svg
155
+ .select('.chart-container')
156
+ .attr('transform', `translate(${this.width / 2},${this.width / 2})`);
157
+ // Arc paths — keyed by name so D3 matches elements across updates
158
+ const $paths = svg
159
+ .select('.arc-container')
160
+ .selectAll('.arc')
161
+ .data(pieData, d => d.data.name)
162
+ .join('path')
163
+ .attr('class', 'arc')
164
+ .style('fill', d => d.data.color || colorScale(d.data.name).color);
165
+ if (animate) {
166
+ $paths
167
+ .transition()
168
+ .duration(DURATION)
169
+ .ease(cubicInOut)
170
+ .attrTween('d', function (d) {
171
+ const self = this;
172
+ // Interpolate from the last rendered angles to the new ones.
173
+ // New (entering) arcs start collapsed at their startAngle.
174
+ const prev = self._prevDatum ?? {
175
+ startAngle: d.startAngle,
176
+ endAngle: d.startAngle,
177
+ };
178
+ self._prevDatum = d;
179
+ const iStart = interpolateNumber(prev.startAngle, d.startAngle);
180
+ const iEnd = interpolateNumber(prev.endAngle, d.endAngle);
181
+ return (t) => doughnutArc({ ...d, startAngle: iStart(t), endAngle: iEnd(t) }) ??
182
+ '';
183
+ });
184
+ }
185
+ else {
186
+ // Initial render: draw immediately and seed previous-datum for later tweens
187
+ $paths
188
+ .each(function (d) {
189
+ this._prevDatum = d;
190
+ })
191
+ .attr('d', d => doughnutArc(d) ?? '');
192
+ }
193
+ // Animate the central total counter
194
+ const $title = svg.select('.title');
195
+ if (animate) {
196
+ $title
197
+ .transition()
198
+ .duration(DURATION)
199
+ .ease(cubicInOut)
200
+ .tween('text', function () {
201
+ const sel = select(this);
202
+ const start = parseFloat(sel.text()) || 0;
203
+ const interp = interpolateNumber(start, total);
204
+ return function (t) {
205
+ sel.text(Math.round(interp(t)));
206
+ };
207
+ });
208
+ }
209
+ else {
210
+ $title.text(total);
211
+ }
212
+ // Label polylines and text
213
+ const $chartContainer = svg.select('.chart-container');
214
+ if (this.showLabels) {
215
+ const pointsFn = (d) => {
216
+ const posA = doughnutArc.centroid(d);
217
+ const posB = labelsArc.centroid(d);
218
+ const posC = posB.slice();
219
+ const midAngle = d.startAngle + (d.endAngle - d.startAngle) / 2;
220
+ posC[0] = radius * (midAngle < Math.PI ? 1 : -1);
221
+ return [posA, posB, posC].map(p => p.join(',')).join(' ');
222
+ };
223
+ const transformFn = (d) => {
224
+ const pos = labelsArc.centroid(d);
225
+ const midAngle = d.startAngle + (d.endAngle - d.startAngle) / 2;
226
+ pos[0] = radius * (midAngle < Math.PI ? 1 : -1);
227
+ return `translate(${pos})`;
228
+ };
229
+ const anchorFn = (d) => {
230
+ const midAngle = d.startAngle + (d.endAngle - d.startAngle) / 2;
231
+ return midAngle < Math.PI ? 'start' : 'end';
232
+ };
233
+ const $polylines = $chartContainer
234
+ .selectAll('.item-polyline')
235
+ .data(pieData, d => d.data.name)
236
+ .join('polyline')
237
+ .attr('class', 'item-polyline');
238
+ const $labels = $chartContainer
239
+ .selectAll('.item-label')
240
+ .data(pieData, d => d.data.name)
241
+ .join('text')
242
+ .attr('class', 'item-label')
243
+ .text(d => d.data.label ?? '');
244
+ if (animate) {
245
+ $polylines
246
+ .transition()
247
+ .duration(DURATION)
248
+ .ease(cubicInOut)
249
+ .attr('points', pointsFn);
250
+ $labels
251
+ .transition()
252
+ .duration(DURATION)
253
+ .ease(cubicInOut)
254
+ .attr('transform', transformFn)
255
+ .style('text-anchor', anchorFn);
256
+ }
257
+ else {
258
+ $polylines.attr('points', pointsFn);
259
+ $labels.attr('transform', transformFn).style('text-anchor', anchorFn);
260
+ }
261
+ }
262
+ else {
263
+ $chartContainer.selectAll('.item-polyline').remove();
264
+ $chartContainer.selectAll('.item-label').remove();
265
+ }
266
+ }
267
+ render() {
268
+ return b `
269
+ <div class="chart">
270
+ <svg>
271
+ <g class="chart-container">
272
+ <g class="arc-container"></g>
273
+ <text class="title" text-anchor="middle"></text>
274
+ <text class="label" text-anchor="middle" y="16">
275
+ ${this.label}
276
+ </text>
277
+ </g>
278
+ </svg>
279
+ </div>
280
+ `;
281
+ }
282
+ };
283
+ ChartDoughnut.styles = [css_248z];
284
+ __decorate([
285
+ e('svg')
286
+ ], ChartDoughnut.prototype, "svgElement", void 0);
287
+ __decorate([
288
+ n({ type: Number, reflect: true })
289
+ ], ChartDoughnut.prototype, "width", void 0);
290
+ __decorate([
291
+ n({ type: Number, reflect: true })
292
+ ], ChartDoughnut.prototype, "margin", void 0);
293
+ __decorate([
294
+ n({ type: Boolean, reflect: true, attribute: 'show-labels' })
295
+ ], ChartDoughnut.prototype, "showLabels", void 0);
296
+ __decorate([
297
+ n({ type: Array })
298
+ ], ChartDoughnut.prototype, "data", void 0);
299
+ __decorate([
300
+ n({ type: String })
301
+ ], ChartDoughnut.prototype, "label", void 0);
302
+ ChartDoughnut = __decorate([
303
+ IndividualComponent
304
+ ], ChartDoughnut);
305
+
306
+ export { ChartDoughnut };
307
+ //# sourceMappingURL=chart-doughnut.js.map