@ngbase/adk 0.1.16 → 0.1.18

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 (203) hide show
  1. package/fesm2022/ngbase-adk-a11y.mjs +42 -42
  2. package/fesm2022/ngbase-adk-a11y.mjs.map +1 -1
  3. package/fesm2022/ngbase-adk-accordion.mjs +20 -26
  4. package/fesm2022/ngbase-adk-accordion.mjs.map +1 -1
  5. package/fesm2022/ngbase-adk-autocomplete.mjs +11 -11
  6. package/fesm2022/ngbase-adk-autocomplete.mjs.map +1 -1
  7. package/fesm2022/ngbase-adk-avatar.mjs +13 -13
  8. package/fesm2022/ngbase-adk-avatar.mjs.map +1 -1
  9. package/fesm2022/ngbase-adk-bidi.mjs +3 -3
  10. package/fesm2022/ngbase-adk-bidi.mjs.map +1 -1
  11. package/fesm2022/ngbase-adk-breadcrumb.mjs +14 -14
  12. package/fesm2022/ngbase-adk-breadcrumb.mjs.map +1 -1
  13. package/fesm2022/ngbase-adk-cache.mjs +3 -3
  14. package/fesm2022/ngbase-adk-cache.mjs.map +1 -1
  15. package/fesm2022/ngbase-adk-carousel.mjs +18 -18
  16. package/fesm2022/ngbase-adk-carousel.mjs.map +1 -1
  17. package/fesm2022/ngbase-adk-checkbox.mjs +15 -21
  18. package/fesm2022/ngbase-adk-checkbox.mjs.map +1 -1
  19. package/fesm2022/ngbase-adk-chip.mjs +12 -12
  20. package/fesm2022/ngbase-adk-chip.mjs.map +1 -1
  21. package/fesm2022/ngbase-adk-clipboard.mjs +7 -5
  22. package/fesm2022/ngbase-adk-clipboard.mjs.map +1 -1
  23. package/fesm2022/ngbase-adk-collections.mjs.map +1 -1
  24. package/fesm2022/ngbase-adk-color-picker.mjs +44 -53
  25. package/fesm2022/ngbase-adk-color-picker.mjs.map +1 -1
  26. package/fesm2022/ngbase-adk-cookies.mjs +3 -3
  27. package/fesm2022/ngbase-adk-cookies.mjs.map +1 -1
  28. package/fesm2022/ngbase-adk-datepicker.mjs +70 -89
  29. package/fesm2022/ngbase-adk-datepicker.mjs.map +1 -1
  30. package/fesm2022/ngbase-adk-dialog.mjs +17 -39
  31. package/fesm2022/ngbase-adk-dialog.mjs.map +1 -1
  32. package/fesm2022/ngbase-adk-drag.mjs +20 -20
  33. package/fesm2022/ngbase-adk-drag.mjs.map +1 -1
  34. package/fesm2022/ngbase-adk-form-field.mjs +65 -118
  35. package/fesm2022/ngbase-adk-form-field.mjs.map +1 -1
  36. package/fesm2022/ngbase-adk-hover-card.mjs +5 -5
  37. package/fesm2022/ngbase-adk-hover-card.mjs.map +1 -1
  38. package/fesm2022/ngbase-adk-icon.mjs +9 -11
  39. package/fesm2022/ngbase-adk-icon.mjs.map +1 -1
  40. package/fesm2022/ngbase-adk-inline-edit.mjs +27 -35
  41. package/fesm2022/ngbase-adk-inline-edit.mjs.map +1 -1
  42. package/fesm2022/ngbase-adk-jwt.mjs +319 -41
  43. package/fesm2022/ngbase-adk-jwt.mjs.map +1 -1
  44. package/fesm2022/ngbase-adk-keys.mjs +6 -6
  45. package/fesm2022/ngbase-adk-keys.mjs.map +1 -1
  46. package/fesm2022/ngbase-adk-layout.mjs.map +1 -1
  47. package/fesm2022/ngbase-adk-list.mjs +10 -10
  48. package/fesm2022/ngbase-adk-list.mjs.map +1 -1
  49. package/fesm2022/ngbase-adk-mask.mjs +8 -8
  50. package/fesm2022/ngbase-adk-mask.mjs.map +1 -1
  51. package/fesm2022/ngbase-adk-menu.mjs +69 -79
  52. package/fesm2022/ngbase-adk-menu.mjs.map +1 -1
  53. package/fesm2022/ngbase-adk-network.mjs +3 -3
  54. package/fesm2022/ngbase-adk-network.mjs.map +1 -1
  55. package/fesm2022/ngbase-adk-otp.mjs +24 -45
  56. package/fesm2022/ngbase-adk-otp.mjs.map +1 -1
  57. package/fesm2022/ngbase-adk-pagination.mjs +9 -9
  58. package/fesm2022/ngbase-adk-pagination.mjs.map +1 -1
  59. package/fesm2022/ngbase-adk-popover.mjs +120 -89
  60. package/fesm2022/ngbase-adk-popover.mjs.map +1 -1
  61. package/fesm2022/ngbase-adk-portal.mjs +134 -47
  62. package/fesm2022/ngbase-adk-portal.mjs.map +1 -1
  63. package/fesm2022/ngbase-adk-progress.mjs +7 -7
  64. package/fesm2022/ngbase-adk-progress.mjs.map +1 -1
  65. package/fesm2022/ngbase-adk-radio.mjs +20 -27
  66. package/fesm2022/ngbase-adk-radio.mjs.map +1 -1
  67. package/fesm2022/ngbase-adk-resizable.mjs +138 -48
  68. package/fesm2022/ngbase-adk-resizable.mjs.map +1 -1
  69. package/fesm2022/ngbase-adk-scroll-area.mjs +28 -20
  70. package/fesm2022/ngbase-adk-scroll-area.mjs.map +1 -1
  71. package/fesm2022/ngbase-adk-select.mjs +58 -80
  72. package/fesm2022/ngbase-adk-select.mjs.map +1 -1
  73. package/fesm2022/ngbase-adk-selectable.mjs +19 -30
  74. package/fesm2022/ngbase-adk-selectable.mjs.map +1 -1
  75. package/fesm2022/ngbase-adk-sheet.mjs +6 -20
  76. package/fesm2022/ngbase-adk-sheet.mjs.map +1 -1
  77. package/fesm2022/ngbase-adk-sidenav.mjs +65 -46
  78. package/fesm2022/ngbase-adk-sidenav.mjs.map +1 -1
  79. package/fesm2022/ngbase-adk-slider.mjs +40 -53
  80. package/fesm2022/ngbase-adk-slider.mjs.map +1 -1
  81. package/fesm2022/ngbase-adk-sonner.mjs +12 -19
  82. package/fesm2022/ngbase-adk-sonner.mjs.map +1 -1
  83. package/fesm2022/ngbase-adk-stepper.mjs +17 -25
  84. package/fesm2022/ngbase-adk-stepper.mjs.map +1 -1
  85. package/fesm2022/ngbase-adk-switch.mjs +25 -32
  86. package/fesm2022/ngbase-adk-switch.mjs.map +1 -1
  87. package/fesm2022/ngbase-adk-table.mjs +581 -83
  88. package/fesm2022/ngbase-adk-table.mjs.map +1 -1
  89. package/fesm2022/ngbase-adk-tabs.mjs +37 -35
  90. package/fesm2022/ngbase-adk-tabs.mjs.map +1 -1
  91. package/fesm2022/ngbase-adk-test.mjs.map +1 -1
  92. package/fesm2022/ngbase-adk-toggle-group.mjs +20 -34
  93. package/fesm2022/ngbase-adk-toggle-group.mjs.map +1 -1
  94. package/fesm2022/ngbase-adk-toggle.mjs +14 -19
  95. package/fesm2022/ngbase-adk-toggle.mjs.map +1 -1
  96. package/fesm2022/ngbase-adk-tooltip.mjs +12 -19
  97. package/fesm2022/ngbase-adk-tooltip.mjs.map +1 -1
  98. package/fesm2022/ngbase-adk-tour.mjs +52 -52
  99. package/fesm2022/ngbase-adk-tour.mjs.map +1 -1
  100. package/fesm2022/ngbase-adk-translate.mjs +8 -10
  101. package/fesm2022/ngbase-adk-translate.mjs.map +1 -1
  102. package/fesm2022/ngbase-adk-tree.mjs +20 -20
  103. package/fesm2022/ngbase-adk-tree.mjs.map +1 -1
  104. package/fesm2022/ngbase-adk-utils.mjs +30 -43
  105. package/fesm2022/ngbase-adk-utils.mjs.map +1 -1
  106. package/fesm2022/ngbase-adk-virtualizer.mjs +9 -9
  107. package/fesm2022/ngbase-adk-virtualizer.mjs.map +1 -1
  108. package/package.json +91 -91
  109. package/schematics/components/files/accordion/accordion.ts.template +9 -6
  110. package/schematics/components/files/audio/AudioPlayer.ts.template +245 -0
  111. package/schematics/components/files/audio/AudioRecorder.ts.template +377 -0
  112. package/schematics/components/files/audio/AudioVisualizer.ts.template +175 -0
  113. package/schematics/components/files/audio/index.ts.template +3 -0
  114. package/schematics/components/files/button/button-llm.md.template +3 -2
  115. package/schematics/components/files/charts/area-chart.component.ts.template +278 -0
  116. package/schematics/components/files/charts/bar-chart.component.ts.template +262 -0
  117. package/schematics/components/files/charts/chart-tooltip.component.ts.template +168 -0
  118. package/schematics/components/files/charts/index.ts.template +4 -0
  119. package/schematics/components/files/charts/line-chart.component.ts.template +238 -0
  120. package/schematics/components/files/charts/pie-chart.component.ts.template +283 -0
  121. package/schematics/components/files/checkbox/checkbox.ts.template +2 -2
  122. package/schematics/components/files/color-picker/color-picker.ts.template +2 -2
  123. package/schematics/components/files/dialog/dialog.ts.template +18 -14
  124. package/schematics/components/files/drawer/drawer.ts.template +30 -27
  125. package/schematics/components/files/form-field/form-field.ts.template +51 -23
  126. package/schematics/components/files/pagination/pagination.ts.template +4 -4
  127. package/schematics/components/files/picasa/picasa-base.component.ts.template +15 -30
  128. package/schematics/components/files/popover/popover.ts.template +15 -4
  129. package/schematics/components/files/select/list-selection.ts.template +0 -2
  130. package/schematics/components/files/select/option.ts.template +1 -1
  131. package/schematics/components/files/selectable/selectable.ts.template +2 -2
  132. package/schematics/components/files/sheet/sheet.ts.template +26 -14
  133. package/schematics/components/files/sidenav/sidenav.ts.template +7 -5
  134. package/schematics/components/files/sonner/sonner.ts.template +1 -2
  135. package/schematics/components/files/stepper/stepper.ts.template +2 -4
  136. package/schematics/components/files/switch/switch.ts.template +2 -2
  137. package/schematics/components/files/table/table.ts.template +43 -3
  138. package/schematics/components/files/tabs/tab.ts.template +3 -3
  139. package/schematics/components/files/theme/theme.service.ts.template +3 -3
  140. package/schematics/components/files/toggle/toggle.ts.template +1 -1
  141. package/schematics/components/files/toggle-group/toggle-group.ts.template +1 -1
  142. package/schematics/components/files/tooltip/tooltip.ts.template +2 -3
  143. package/schematics/components/schema.json +2 -0
  144. package/{accordion/index.d.ts → types/ngbase-adk-accordion.d.ts} +1 -3
  145. package/{autocomplete/index.d.ts → types/ngbase-adk-autocomplete.d.ts} +2 -7
  146. package/{checkbox/index.d.ts → types/ngbase-adk-checkbox.d.ts} +8 -14
  147. package/types/ngbase-adk-clipboard.d.ts +12 -0
  148. package/{color-picker/index.d.ts → types/ngbase-adk-color-picker.d.ts} +14 -26
  149. package/{datepicker/index.d.ts → types/ngbase-adk-datepicker.d.ts} +9 -18
  150. package/{dialog/index.d.ts → types/ngbase-adk-dialog.d.ts} +3 -8
  151. package/types/ngbase-adk-form-field.d.ts +88 -0
  152. package/{inline-edit/index.d.ts → types/ngbase-adk-inline-edit.d.ts} +8 -16
  153. package/types/ngbase-adk-jwt.d.ts +64 -0
  154. package/{menu/index.d.ts → types/ngbase-adk-menu.d.ts} +6 -5
  155. package/{otp/index.d.ts → types/ngbase-adk-otp.d.ts} +8 -16
  156. package/{popover/index.d.ts → types/ngbase-adk-popover.d.ts} +14 -2
  157. package/{portal/index.d.ts → types/ngbase-adk-portal.d.ts} +29 -8
  158. package/{radio/index.d.ts → types/ngbase-adk-radio.d.ts} +9 -12
  159. package/{resizable/index.d.ts → types/ngbase-adk-resizable.d.ts} +4 -4
  160. package/{scroll-area/index.d.ts → types/ngbase-adk-scroll-area.d.ts} +2 -1
  161. package/{select/index.d.ts → types/ngbase-adk-select.d.ts} +8 -22
  162. package/{selectable/index.d.ts → types/ngbase-adk-selectable.d.ts} +6 -10
  163. package/{sheet/index.d.ts → types/ngbase-adk-sheet.d.ts} +4 -3
  164. package/{sidenav/index.d.ts → types/ngbase-adk-sidenav.d.ts} +6 -6
  165. package/{slider/index.d.ts → types/ngbase-adk-slider.d.ts} +8 -17
  166. package/{sonner/index.d.ts → types/ngbase-adk-sonner.d.ts} +1 -3
  167. package/{stepper/index.d.ts → types/ngbase-adk-stepper.d.ts} +1 -4
  168. package/{switch/index.d.ts → types/ngbase-adk-switch.d.ts} +7 -14
  169. package/{table/index.d.ts → types/ngbase-adk-table.d.ts} +126 -3
  170. package/{test/index.d.ts → types/ngbase-adk-test.d.ts} +1 -1
  171. package/{toggle-group/index.d.ts → types/ngbase-adk-toggle-group.d.ts} +5 -10
  172. package/types/ngbase-adk-toggle.d.ts +14 -0
  173. package/{tooltip/index.d.ts → types/ngbase-adk-tooltip.d.ts} +9 -11
  174. package/{tour/index.d.ts → types/ngbase-adk-tour.d.ts} +4 -6
  175. package/{utils/index.d.ts → types/ngbase-adk-utils.d.ts} +15 -11
  176. package/clipboard/index.d.ts +0 -11
  177. package/form-field/index.d.ts +0 -97
  178. package/jwt/index.d.ts +0 -20
  179. package/toggle/index.d.ts +0 -16
  180. /package/{a11y/index.d.ts → types/ngbase-adk-a11y.d.ts} +0 -0
  181. /package/{avatar/index.d.ts → types/ngbase-adk-avatar.d.ts} +0 -0
  182. /package/{bidi/index.d.ts → types/ngbase-adk-bidi.d.ts} +0 -0
  183. /package/{breadcrumb/index.d.ts → types/ngbase-adk-breadcrumb.d.ts} +0 -0
  184. /package/{cache/index.d.ts → types/ngbase-adk-cache.d.ts} +0 -0
  185. /package/{carousel/index.d.ts → types/ngbase-adk-carousel.d.ts} +0 -0
  186. /package/{chip/index.d.ts → types/ngbase-adk-chip.d.ts} +0 -0
  187. /package/{collections/index.d.ts → types/ngbase-adk-collections.d.ts} +0 -0
  188. /package/{cookies/index.d.ts → types/ngbase-adk-cookies.d.ts} +0 -0
  189. /package/{drag/index.d.ts → types/ngbase-adk-drag.d.ts} +0 -0
  190. /package/{hover-card/index.d.ts → types/ngbase-adk-hover-card.d.ts} +0 -0
  191. /package/{icon/index.d.ts → types/ngbase-adk-icon.d.ts} +0 -0
  192. /package/{keys/index.d.ts → types/ngbase-adk-keys.d.ts} +0 -0
  193. /package/{layout/index.d.ts → types/ngbase-adk-layout.d.ts} +0 -0
  194. /package/{list/index.d.ts → types/ngbase-adk-list.d.ts} +0 -0
  195. /package/{mask/index.d.ts → types/ngbase-adk-mask.d.ts} +0 -0
  196. /package/{network/index.d.ts → types/ngbase-adk-network.d.ts} +0 -0
  197. /package/{pagination/index.d.ts → types/ngbase-adk-pagination.d.ts} +0 -0
  198. /package/{progress/index.d.ts → types/ngbase-adk-progress.d.ts} +0 -0
  199. /package/{tabs/index.d.ts → types/ngbase-adk-tabs.d.ts} +0 -0
  200. /package/{translate/index.d.ts → types/ngbase-adk-translate.d.ts} +0 -0
  201. /package/{tree/index.d.ts → types/ngbase-adk-tree.d.ts} +0 -0
  202. /package/{virtualizer/index.d.ts → types/ngbase-adk-virtualizer.d.ts} +0 -0
  203. /package/{index.d.ts → types/ngbase-adk.d.ts} +0 -0
@@ -0,0 +1,278 @@
1
+ import {
2
+ ChangeDetectionStrategy,
3
+ Component,
4
+ ElementRef,
5
+ afterNextRender,
6
+ input,
7
+ viewChild,
8
+ effect,
9
+ signal,
10
+ } from '@angular/core';
11
+ import * as d3 from 'd3';
12
+ import { ChartTooltipComponent, TooltipContent } from './chart-tooltip.component';
13
+
14
+ interface AreaData {
15
+ label: string;
16
+ value: number;
17
+ }
18
+
19
+ @Component({
20
+ selector: '<%= name %>-area-chart',
21
+ changeDetection: ChangeDetectionStrategy.OnPush,
22
+ imports: [ChartTooltipComponent],
23
+ template: `
24
+ <svg #chart class="w-full"></svg>
25
+ <<%= name %>-chart-tooltip
26
+ [visible]="tooltipVisible()"
27
+ [x]="tooltipX()"
28
+ [y]="tooltipY()"
29
+ [content]="tooltipContent()"
30
+ />
31
+ `,
32
+ host: {
33
+ class: 'block relative',
34
+ },
35
+ })
36
+ export class AreaChartComponent {
37
+ readonly chartRef = viewChild.required<ElementRef<SVGElement>>('chart');
38
+
39
+ data = input.required<AreaData[]>();
40
+
41
+ height = input(250);
42
+ showYAxis = input(false);
43
+
44
+ private svg!: d3.Selection<SVGElement, unknown, null, undefined>;
45
+ private margin = { top: 20, right: 20, bottom: 50, left: 50 };
46
+
47
+ // Tooltip state
48
+ tooltipVisible = signal(false);
49
+ tooltipX = signal(0);
50
+ tooltipY = signal(0);
51
+ tooltipContent = signal<TooltipContent>({ label: '', value: 0 });
52
+
53
+ private resizeObserver: ResizeObserver | undefined;
54
+
55
+ constructor() {
56
+ afterNextRender(() => {
57
+ this.svg = d3.select(this.chartRef().nativeElement);
58
+ this.updateChart();
59
+
60
+ this.resizeObserver = new ResizeObserver(() => {
61
+ this.updateChart();
62
+ });
63
+ this.resizeObserver.observe(this.chartRef().nativeElement.parentElement!);
64
+ });
65
+
66
+ // Watch for input changes and update chart
67
+ effect(() => {
68
+ const data = this.data();
69
+ const _height = this.height();
70
+ const _showYAxis = this.showYAxis();
71
+
72
+ // Update chart if svg is initialized
73
+ if (this.svg && data) {
74
+ this.updateChart();
75
+ }
76
+ });
77
+ }
78
+
79
+ ngOnDestroy() {
80
+ if (this.resizeObserver) {
81
+ this.resizeObserver.disconnect();
82
+ }
83
+ }
84
+
85
+ private updateChart(): void {
86
+ if (!this.svg) return;
87
+
88
+ const data = this.data();
89
+ // Get actual width from parent container
90
+ const containerWidth = this.chartRef().nativeElement.parentElement?.clientWidth || 0;
91
+
92
+ if (containerWidth === 0) return;
93
+
94
+ // Adjust left margin based on showYAxis
95
+ const leftMargin = this.showYAxis() ? this.margin.left : 20;
96
+ const width = containerWidth - leftMargin - this.margin.right;
97
+ const height = this.height() - this.margin.top - this.margin.bottom;
98
+
99
+ // Clear previous content
100
+ this.svg.selectAll('*').remove();
101
+
102
+ // Set SVG dimensions - width/height/viewBox must match
103
+ this.svg
104
+ .attr('width', containerWidth)
105
+ .attr('height', this.height())
106
+ .attr('viewBox', `0 0 ${containerWidth} ${this.height()}`);
107
+
108
+ // Create chart group
109
+ const g = this.svg.append('g').attr('transform', `translate(${leftMargin},${this.margin.top})`);
110
+
111
+ // Get CSS variables for theming
112
+ const textColor = getComputedStyle(document.documentElement)
113
+ .getPropertyValue('--color-text')
114
+ .trim();
115
+ const borderColor = getComputedStyle(document.documentElement)
116
+ .getPropertyValue('--color-border')
117
+ .trim();
118
+
119
+ // Get chart color (using chart-2 for area)
120
+ const areaColor = getComputedStyle(document.documentElement)
121
+ .getPropertyValue('--chart-2')
122
+ .trim();
123
+
124
+ // Create gradient for area fill
125
+ const gradient = this.svg
126
+ .append('defs')
127
+ .append('linearGradient')
128
+ .attr('id', 'area-gradient')
129
+ .attr('x1', '0%')
130
+ .attr('y1', '0%')
131
+ .attr('x2', '0%')
132
+ .attr('y2', '100%');
133
+
134
+ gradient
135
+ .append('stop')
136
+ .attr('offset', '0%')
137
+ .attr('stop-color', areaColor)
138
+ .attr('stop-opacity', 0.8);
139
+
140
+ gradient
141
+ .append('stop')
142
+ .attr('offset', '100%')
143
+ .attr('stop-color', areaColor)
144
+ .attr('stop-opacity', 0.1);
145
+
146
+ // Create scales
147
+ const x = d3
148
+ .scalePoint()
149
+ .domain(data.map(d => d.label))
150
+ .range([0, width])
151
+ .padding(0.5);
152
+
153
+ const y = d3
154
+ .scaleLinear()
155
+ .domain([0, d3.max(data, d => d.value) || 100])
156
+ .nice()
157
+ .range([height, 0]);
158
+
159
+ // Add X axis
160
+ g.append('g')
161
+ .attr('transform', `translate(0,${height})`)
162
+ .call(d3.axisBottom(x))
163
+ .style('color', textColor)
164
+ .selectAll('text')
165
+ .style('font-size', '12px');
166
+
167
+ // Add Y axis (conditionally)
168
+ if (this.showYAxis()) {
169
+ g.append('g')
170
+ .call(d3.axisLeft(y))
171
+ .style('color', textColor)
172
+ .selectAll('text')
173
+ .style('font-size', '12px');
174
+ }
175
+
176
+ // Style axis lines
177
+ g.selectAll('.domain, .tick line').style('stroke', borderColor);
178
+
179
+ // Add grid lines
180
+ g.append('g')
181
+ .attr('class', 'grid')
182
+ .call(
183
+ d3
184
+ .axisLeft(y)
185
+ .tickSize(-width)
186
+ .tickFormat(() => ''),
187
+ )
188
+ .select('.domain')
189
+ .remove();
190
+
191
+ // Style grid lines
192
+ g.selectAll('.grid .tick line').style('stroke', 'rgb(229 229 229 / 50%)');
193
+
194
+ // Create area generator
195
+ const area = d3
196
+ .area<AreaData>()
197
+ .x(d => x(d.label) || 0)
198
+ .y0(height)
199
+ .y1(d => y(d.value))
200
+ .curve(d3.curveMonotoneX);
201
+
202
+ // Create line generator
203
+ const line = d3
204
+ .line<AreaData>()
205
+ .x(d => x(d.label) || 0)
206
+ .y(d => y(d.value))
207
+ .curve(d3.curveMonotoneX);
208
+
209
+ // Add the area with animation
210
+ const areaPath = g
211
+ .append('path')
212
+ .datum(data)
213
+ .attr('fill', 'url(#area-gradient)')
214
+ .attr('d', area)
215
+ .attr('opacity', 0);
216
+
217
+ areaPath.transition().duration(1000).attr('opacity', 0.4);
218
+
219
+ // Add the line path
220
+ const linePath = g
221
+ .append('path')
222
+ .datum(data)
223
+ .attr('fill', 'none')
224
+ .attr('stroke', areaColor)
225
+ .attr('stroke-width', 1)
226
+ .attr('d', line);
227
+
228
+ // Animate line drawing
229
+ const totalLength = linePath.node()?.getTotalLength() || 0;
230
+ linePath
231
+ .attr('stroke-dasharray', `${totalLength} ${totalLength}`)
232
+ .attr('stroke-dashoffset', totalLength)
233
+ .transition()
234
+ .duration(1200)
235
+ .ease(d3.easeLinear)
236
+ .attr('stroke-dashoffset', 0);
237
+
238
+ // Add circles for data points
239
+ const circles = g
240
+ .selectAll('.dot')
241
+ .data(data)
242
+ .enter()
243
+ .append('circle')
244
+ .attr('class', 'dot')
245
+ .attr('cx', d => x(d.label) || 0)
246
+ .attr('cy', d => y(d.value))
247
+ .attr('r', 0)
248
+ .attr('fill', areaColor)
249
+ .attr('stroke', 'transparent')
250
+ .attr('stroke-width', 2)
251
+ .style('cursor', 'pointer');
252
+
253
+ // Add hover effects for tooltip
254
+ circles
255
+ .on('mouseenter', (event, d) => {
256
+ const circleX = x(d.label) || 0;
257
+ const circleY = y(d.value);
258
+ const svgRect = this.chartRef().nativeElement.getBoundingClientRect();
259
+
260
+ this.tooltipContent.set({ label: d.label, value: d.value });
261
+ this.tooltipX.set(svgRect.left + leftMargin + circleX);
262
+ this.tooltipY.set(svgRect.top + this.margin.top + circleY - 10);
263
+ this.tooltipVisible.set(true);
264
+
265
+ // Enlarge circle on hover
266
+ d3.select(event.currentTarget).transition().duration(200).attr('r', 6);
267
+ })
268
+ .on('mouseleave', event => {
269
+ this.tooltipVisible.set(false);
270
+
271
+ // Restore circle size
272
+ d3.select(event.currentTarget).transition().duration(200).attr('r', 4);
273
+ });
274
+
275
+ // Animate circles
276
+ circles.transition().delay(1200).duration(400).attr('r', 4);
277
+ }
278
+ }
@@ -0,0 +1,262 @@
1
+ import {
2
+ ChangeDetectionStrategy,
3
+ Component,
4
+ ElementRef,
5
+ afterNextRender,
6
+ input,
7
+ viewChild,
8
+ effect,
9
+ signal,
10
+ } from '@angular/core';
11
+ import * as d3 from 'd3';
12
+ import { ChartTooltipComponent, TooltipContent } from './chart-tooltip.component';
13
+
14
+ interface BarData {
15
+ label: string;
16
+ value: number;
17
+ }
18
+
19
+ @Component({
20
+ selector: '<%= name %>-bar-chart',
21
+ changeDetection: ChangeDetectionStrategy.OnPush,
22
+ imports: [ChartTooltipComponent],
23
+ template: `
24
+ <svg #chart class="w-full"></svg>
25
+ <<%= name %>-chart-tooltip
26
+ [visible]="tooltipVisible()"
27
+ [x]="tooltipX()"
28
+ [y]="tooltipY()"
29
+ [content]="tooltipContent()"
30
+ />
31
+ `,
32
+ host: {
33
+ class: 'block relative',
34
+ },
35
+ })
36
+ export class BarChartComponent {
37
+ readonly chartRef = viewChild.required<ElementRef<SVGElement>>('chart');
38
+
39
+ data = input.required<BarData[]>();
40
+
41
+ height = input(250);
42
+ showYAxis = input(false);
43
+ showGrid = input(true);
44
+ color = input<string>();
45
+ colors = input<string[]>();
46
+ xAxisLabel = input<string>();
47
+ yAxisLabel = input<string>();
48
+
49
+ private svg!: d3.Selection<SVGElement, unknown, null, undefined>;
50
+ private margin = { top: 20, right: 20, bottom: 50, left: 50 };
51
+
52
+ // Tooltip state
53
+ tooltipVisible = signal(false);
54
+ tooltipX = signal(0);
55
+ tooltipY = signal(0);
56
+ tooltipContent = signal<TooltipContent>({ label: '', value: 0 });
57
+
58
+ private resizeObserver: ResizeObserver | undefined;
59
+
60
+ constructor() {
61
+ afterNextRender(() => {
62
+ this.svg = d3.select(this.chartRef().nativeElement);
63
+ this.updateChart();
64
+
65
+ // meaningful ResizeObserver to handle container size changes
66
+ this.resizeObserver = new ResizeObserver(() => {
67
+ this.updateChart();
68
+ });
69
+ this.resizeObserver.observe(this.chartRef().nativeElement.parentElement!);
70
+ });
71
+
72
+ // Watch for input changes and update chart
73
+ effect(() => {
74
+ const data = this.data();
75
+ const _height = this.height();
76
+ const _showYAxis = this.showYAxis();
77
+ const _colors = this.colors();
78
+ const _color = this.color();
79
+ const _showGrid = this.showGrid();
80
+
81
+ // Update chart if svg is initialized
82
+ if (this.svg && data) {
83
+ this.updateChart();
84
+ }
85
+ });
86
+ }
87
+
88
+ ngOnDestroy() {
89
+ if (this.resizeObserver) {
90
+ this.resizeObserver.disconnect();
91
+ }
92
+ }
93
+
94
+ private updateChart(): void {
95
+ if (!this.svg) return;
96
+
97
+ const data = this.data();
98
+ // Get actual width from parent container
99
+ const containerWidth = this.chartRef().nativeElement.parentElement?.clientWidth || 0;
100
+
101
+ if (containerWidth === 0) return;
102
+
103
+ // Calculate margins based on labels
104
+ const marginBottom = this.xAxisLabel() ? this.margin.bottom + 20 : this.margin.bottom;
105
+ const marginLeft = this.yAxisLabel()
106
+ ? this.margin.left + 20
107
+ : this.showYAxis()
108
+ ? this.margin.left
109
+ : 20;
110
+
111
+ const width = containerWidth - marginLeft - this.margin.right;
112
+ const height = this.height() - this.margin.top - marginBottom;
113
+
114
+ // Clear previous content
115
+ this.svg.selectAll('*').remove();
116
+
117
+ // Set SVG dimensions - width/height/viewBox must match
118
+ this.svg
119
+ .attr('width', containerWidth)
120
+ .attr('height', this.height())
121
+ .attr('viewBox', `0 0 ${containerWidth} ${this.height()}`);
122
+
123
+ // Create chart group
124
+ const g = this.svg.append('g').attr('transform', `translate(${marginLeft},${this.margin.top})`);
125
+
126
+ // Get CSS variables for theming
127
+ const textColor = getComputedStyle(document.documentElement)
128
+ .getPropertyValue('--color-text')
129
+ .trim();
130
+ const borderColor = getComputedStyle(document.documentElement)
131
+ .getPropertyValue('--color-border')
132
+ .trim();
133
+
134
+ // Get chart colors
135
+ let chartColors: string[] = [];
136
+
137
+ if (this.colors()) {
138
+ chartColors = this.colors()!;
139
+ } else if (this.color()) {
140
+ chartColors = [this.color()!];
141
+ } else {
142
+ chartColors = Array.from({ length: 5 }, (_, i) =>
143
+ getComputedStyle(document.documentElement)
144
+ .getPropertyValue(`--chart-${i + 1}`)
145
+ .trim(),
146
+ );
147
+ }
148
+
149
+ // Create scales
150
+ const x = d3
151
+ .scaleBand()
152
+ .domain(data.map(d => d.label))
153
+ .range([0, width])
154
+ .padding(0.2);
155
+
156
+ const y = d3
157
+ .scaleLinear()
158
+ .domain([0, d3.max(data, d => d.value) || 100])
159
+ .nice()
160
+ .range([height, 0]);
161
+
162
+ // Add X axis
163
+ g.append('g')
164
+ .attr('transform', `translate(0,${height})`)
165
+ .call(d3.axisBottom(x))
166
+ .style('color', textColor)
167
+ .selectAll('text')
168
+ .style('font-size', '12px');
169
+
170
+ // Add Y axis (conditionally)
171
+ if (this.showYAxis()) {
172
+ g.append('g')
173
+ .call(d3.axisLeft(y))
174
+ .style('color', textColor)
175
+ .selectAll('text')
176
+ .style('font-size', '12px');
177
+ }
178
+
179
+ // Style axis lines
180
+ g.selectAll('.domain, .tick line').style('stroke', borderColor);
181
+
182
+ // Add axis labels
183
+ if (this.xAxisLabel()) {
184
+ g.append('text')
185
+ .attr('class', 'x-label')
186
+ .attr('text-anchor', 'middle')
187
+ .attr('x', width / 2)
188
+ .attr('y', height + 40)
189
+ .style('fill', textColor)
190
+ .style('font-size', '12px')
191
+ .text(this.xAxisLabel()!);
192
+ }
193
+
194
+ if (this.yAxisLabel() && this.showYAxis()) {
195
+ g.append('text')
196
+ .attr('class', 'y-label')
197
+ .attr('text-anchor', 'middle')
198
+ .attr('transform', 'rotate(-90)')
199
+ .attr('y', -40)
200
+ .attr('x', -height / 2)
201
+ .style('fill', textColor)
202
+ .style('font-size', '12px')
203
+ .text(this.yAxisLabel()!);
204
+ }
205
+
206
+ // Add grid lines
207
+ if (this.showGrid()) {
208
+ g.append('g')
209
+ .attr('class', 'grid')
210
+ .call(
211
+ d3
212
+ .axisLeft(y)
213
+ .tickSize(-width)
214
+ .tickFormat(() => ''),
215
+ )
216
+ .select('.domain')
217
+ .remove();
218
+
219
+ // Style grid lines
220
+ g.selectAll('.grid .tick line').style('stroke', 'rgb(229 229 229 / 50%)');
221
+ }
222
+
223
+ // Add bars
224
+ const bars = g
225
+ .selectAll('.bar')
226
+ .data(data)
227
+ .enter()
228
+ .append('rect')
229
+ .attr('class', 'bar')
230
+ .attr('x', d => x(d.label) || 0)
231
+ .attr('width', x.bandwidth())
232
+ .attr('y', height)
233
+ .attr('height', 0)
234
+ .attr('fill', (d, i) => chartColors[i % chartColors.length])
235
+ .attr('rx', 8)
236
+ .attr('ry', 8)
237
+ .style('cursor', 'pointer');
238
+
239
+ // Add hover effects for tooltip
240
+ bars
241
+ .on('mouseenter', (event, d) => {
242
+ const barX = (x(d.label) || 0) + x.bandwidth() / 2;
243
+ const barY = y(d.value);
244
+ const svgRect = this.chartRef().nativeElement.getBoundingClientRect();
245
+
246
+ this.tooltipContent.set({ label: d.label, value: d.value });
247
+ this.tooltipX.set(svgRect.left + marginLeft + barX);
248
+ this.tooltipY.set(svgRect.top + this.margin.top + barY - 10);
249
+ this.tooltipVisible.set(true);
250
+ })
251
+ .on('mouseleave', () => {
252
+ this.tooltipVisible.set(false);
253
+ });
254
+
255
+ // Animate bars
256
+ bars
257
+ .transition()
258
+ .duration(800)
259
+ .attr('y', d => y(d.value))
260
+ .attr('height', d => height - y(d.value));
261
+ }
262
+ }
@@ -0,0 +1,168 @@
1
+ import {
2
+ ChangeDetectionStrategy,
3
+ Component,
4
+ computed,
5
+ input,
6
+ viewChild,
7
+ ElementRef,
8
+ effect,
9
+ signal,
10
+ } from '@angular/core';
11
+
12
+ export interface TooltipContent {
13
+ label: string;
14
+ value: number;
15
+ percentage?: number;
16
+ }
17
+
18
+ @Component({
19
+ selector: '<%= name %>-chart-tooltip',
20
+ changeDetection: ChangeDetectionStrategy.OnPush,
21
+ template: `
22
+ <div
23
+ #tooltipElement
24
+ class="chart-tooltip"
25
+ [style.left.px]="finalX()"
26
+ [style.top.px]="finalY()"
27
+ [class.visible]="visible()"
28
+ >
29
+ <div class="tooltip-content">
30
+ <div class="tooltip-label">{{ content().label }}</div>
31
+ <div class="tooltip-value">{{ content().value }}</div>
32
+ @if (content().percentage !== undefined) {
33
+ <div class="tooltip-percentage">{{ content().percentage }}%</div>
34
+ }
35
+ </div>
36
+ </div>
37
+ `,
38
+ styles: `
39
+ :host {
40
+ display: contents;
41
+ }
42
+
43
+ .chart-tooltip {
44
+ position: fixed;
45
+ pointer-events: none;
46
+ z-index: 9999;
47
+ opacity: 0;
48
+ transform: translateY(-8px) scale(0.95);
49
+ transition:
50
+ opacity 150ms ease-out,
51
+ transform 150ms ease-out;
52
+ }
53
+
54
+ .chart-tooltip.visible {
55
+ opacity: 1;
56
+ transform: translateY(0) scale(1);
57
+ }
58
+
59
+ .tooltip-content {
60
+ background: var(--color-background);
61
+ border: 1px solid var(--color-border);
62
+ border-radius: 8px;
63
+ padding: 8px 12px;
64
+ box-shadow:
65
+ 0 10px 15px -3px rgb(0 0 0 / 0.1),
66
+ 0 4px 6px -4px rgb(0 0 0 / 0.1);
67
+ font-size: 12px;
68
+ color: var(--color-text);
69
+ white-space: nowrap;
70
+ }
71
+
72
+ .tooltip-label {
73
+ font-weight: 500;
74
+ margin-bottom: 2px;
75
+ color: var(--color-text-muted);
76
+ }
77
+
78
+ .tooltip-value {
79
+ font-weight: 600;
80
+ font-size: 14px;
81
+ }
82
+
83
+ .tooltip-percentage {
84
+ font-weight: 500;
85
+ margin-top: 2px;
86
+ color: var(--color-text-muted);
87
+ font-size: 11px;
88
+ }
89
+ `,
90
+ })
91
+ export class ChartTooltipComponent {
92
+ readonly tooltipElement = viewChild<ElementRef<HTMLDivElement>>('tooltipElement');
93
+
94
+ visible = input(false);
95
+ x = input(0);
96
+ y = input(0);
97
+ content = input<TooltipContent>({ label: '', value: 0 });
98
+
99
+ // Signals to store measured dimensions
100
+ private tooltipWidth = signal(0);
101
+ private tooltipHeight = signal(0);
102
+
103
+ // Default spacing from cursor
104
+ private readonly CURSOR_OFFSET_X = 12;
105
+ private readonly CURSOR_OFFSET_Y = 12;
106
+
107
+ // Computed final positions based on dimensions and viewport
108
+ finalX = computed(() => {
109
+ const baseX = this.x();
110
+ const width = this.tooltipWidth();
111
+ const viewportWidth = window.innerWidth;
112
+
113
+ // Position to the right of cursor by default
114
+ let finalX = baseX + this.CURSOR_OFFSET_X;
115
+
116
+ // If tooltip would go off right edge, position to the left of cursor
117
+ if (finalX + width > viewportWidth) {
118
+ finalX = baseX - width - this.CURSOR_OFFSET_X;
119
+ }
120
+
121
+ // Ensure it doesn't go off left edge
122
+ if (finalX < 0) {
123
+ finalX = this.CURSOR_OFFSET_X;
124
+ }
125
+
126
+ return finalX;
127
+ });
128
+
129
+ finalY = computed(() => {
130
+ const baseY = this.y();
131
+ const height = this.tooltipHeight();
132
+ const viewportHeight = window.innerHeight;
133
+
134
+ // Position above cursor by default
135
+ let finalY = baseY - height - this.CURSOR_OFFSET_Y;
136
+
137
+ // If tooltip would go off top edge, position below cursor
138
+ if (finalY < 0) {
139
+ finalY = baseY + this.CURSOR_OFFSET_Y;
140
+ }
141
+
142
+ // Ensure it doesn't go off bottom edge
143
+ if (finalY + height > viewportHeight) {
144
+ finalY = viewportHeight - height - this.CURSOR_OFFSET_Y;
145
+ }
146
+
147
+ return finalY;
148
+ });
149
+
150
+ constructor() {
151
+ // Measure tooltip dimensions when content changes or it becomes visible
152
+ effect(() => {
153
+ // Subscribe to changes
154
+ this.content();
155
+ this.visible();
156
+
157
+ const element = this.tooltipElement()?.nativeElement;
158
+ if (element) {
159
+ // Use requestAnimationFrame to ensure DOM has updated
160
+ requestAnimationFrame(() => {
161
+ const rect = element.getBoundingClientRect();
162
+ this.tooltipWidth.set(rect.width);
163
+ this.tooltipHeight.set(rect.height);
164
+ });
165
+ }
166
+ });
167
+ }
168
+ }
@@ -0,0 +1,4 @@
1
+ export * from './area-chart.component';
2
+ export * from './bar-chart.component';
3
+ export * from './line-chart.component';
4
+ export * from './pie-chart.component';