@ngbase/adk 0.1.17 → 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.
- package/fesm2022/ngbase-adk-a11y.mjs +42 -42
- package/fesm2022/ngbase-adk-a11y.mjs.map +1 -1
- package/fesm2022/ngbase-adk-accordion.mjs +20 -26
- package/fesm2022/ngbase-adk-accordion.mjs.map +1 -1
- package/fesm2022/ngbase-adk-autocomplete.mjs +11 -11
- package/fesm2022/ngbase-adk-autocomplete.mjs.map +1 -1
- package/fesm2022/ngbase-adk-avatar.mjs +13 -13
- package/fesm2022/ngbase-adk-avatar.mjs.map +1 -1
- package/fesm2022/ngbase-adk-bidi.mjs +3 -3
- package/fesm2022/ngbase-adk-bidi.mjs.map +1 -1
- package/fesm2022/ngbase-adk-breadcrumb.mjs +14 -14
- package/fesm2022/ngbase-adk-breadcrumb.mjs.map +1 -1
- package/fesm2022/ngbase-adk-cache.mjs +3 -3
- package/fesm2022/ngbase-adk-cache.mjs.map +1 -1
- package/fesm2022/ngbase-adk-carousel.mjs +18 -18
- package/fesm2022/ngbase-adk-carousel.mjs.map +1 -1
- package/fesm2022/ngbase-adk-checkbox.mjs +15 -21
- package/fesm2022/ngbase-adk-checkbox.mjs.map +1 -1
- package/fesm2022/ngbase-adk-chip.mjs +12 -12
- package/fesm2022/ngbase-adk-chip.mjs.map +1 -1
- package/fesm2022/ngbase-adk-clipboard.mjs +7 -5
- package/fesm2022/ngbase-adk-clipboard.mjs.map +1 -1
- package/fesm2022/ngbase-adk-collections.mjs.map +1 -1
- package/fesm2022/ngbase-adk-color-picker.mjs +44 -53
- package/fesm2022/ngbase-adk-color-picker.mjs.map +1 -1
- package/fesm2022/ngbase-adk-cookies.mjs +3 -3
- package/fesm2022/ngbase-adk-cookies.mjs.map +1 -1
- package/fesm2022/ngbase-adk-datepicker.mjs +70 -89
- package/fesm2022/ngbase-adk-datepicker.mjs.map +1 -1
- package/fesm2022/ngbase-adk-dialog.mjs +17 -39
- package/fesm2022/ngbase-adk-dialog.mjs.map +1 -1
- package/fesm2022/ngbase-adk-drag.mjs +20 -20
- package/fesm2022/ngbase-adk-drag.mjs.map +1 -1
- package/fesm2022/ngbase-adk-form-field.mjs +65 -118
- package/fesm2022/ngbase-adk-form-field.mjs.map +1 -1
- package/fesm2022/ngbase-adk-hover-card.mjs +5 -5
- package/fesm2022/ngbase-adk-hover-card.mjs.map +1 -1
- package/fesm2022/ngbase-adk-icon.mjs +9 -11
- package/fesm2022/ngbase-adk-icon.mjs.map +1 -1
- package/fesm2022/ngbase-adk-inline-edit.mjs +27 -35
- package/fesm2022/ngbase-adk-inline-edit.mjs.map +1 -1
- package/fesm2022/ngbase-adk-jwt.mjs +6 -6
- package/fesm2022/ngbase-adk-jwt.mjs.map +1 -1
- package/fesm2022/ngbase-adk-keys.mjs +6 -6
- package/fesm2022/ngbase-adk-keys.mjs.map +1 -1
- package/fesm2022/ngbase-adk-layout.mjs.map +1 -1
- package/fesm2022/ngbase-adk-list.mjs +10 -10
- package/fesm2022/ngbase-adk-list.mjs.map +1 -1
- package/fesm2022/ngbase-adk-mask.mjs +8 -8
- package/fesm2022/ngbase-adk-mask.mjs.map +1 -1
- package/fesm2022/ngbase-adk-menu.mjs +69 -79
- package/fesm2022/ngbase-adk-menu.mjs.map +1 -1
- package/fesm2022/ngbase-adk-network.mjs +3 -3
- package/fesm2022/ngbase-adk-network.mjs.map +1 -1
- package/fesm2022/ngbase-adk-otp.mjs +24 -45
- package/fesm2022/ngbase-adk-otp.mjs.map +1 -1
- package/fesm2022/ngbase-adk-pagination.mjs +9 -9
- package/fesm2022/ngbase-adk-pagination.mjs.map +1 -1
- package/fesm2022/ngbase-adk-popover.mjs +120 -89
- package/fesm2022/ngbase-adk-popover.mjs.map +1 -1
- package/fesm2022/ngbase-adk-portal.mjs +134 -47
- package/fesm2022/ngbase-adk-portal.mjs.map +1 -1
- package/fesm2022/ngbase-adk-progress.mjs +7 -7
- package/fesm2022/ngbase-adk-progress.mjs.map +1 -1
- package/fesm2022/ngbase-adk-radio.mjs +20 -27
- package/fesm2022/ngbase-adk-radio.mjs.map +1 -1
- package/fesm2022/ngbase-adk-resizable.mjs +138 -48
- package/fesm2022/ngbase-adk-resizable.mjs.map +1 -1
- package/fesm2022/ngbase-adk-scroll-area.mjs +28 -20
- package/fesm2022/ngbase-adk-scroll-area.mjs.map +1 -1
- package/fesm2022/ngbase-adk-select.mjs +58 -80
- package/fesm2022/ngbase-adk-select.mjs.map +1 -1
- package/fesm2022/ngbase-adk-selectable.mjs +19 -30
- package/fesm2022/ngbase-adk-selectable.mjs.map +1 -1
- package/fesm2022/ngbase-adk-sheet.mjs +6 -20
- package/fesm2022/ngbase-adk-sheet.mjs.map +1 -1
- package/fesm2022/ngbase-adk-sidenav.mjs +65 -48
- package/fesm2022/ngbase-adk-sidenav.mjs.map +1 -1
- package/fesm2022/ngbase-adk-slider.mjs +40 -53
- package/fesm2022/ngbase-adk-slider.mjs.map +1 -1
- package/fesm2022/ngbase-adk-sonner.mjs +12 -19
- package/fesm2022/ngbase-adk-sonner.mjs.map +1 -1
- package/fesm2022/ngbase-adk-stepper.mjs +17 -25
- package/fesm2022/ngbase-adk-stepper.mjs.map +1 -1
- package/fesm2022/ngbase-adk-switch.mjs +25 -32
- package/fesm2022/ngbase-adk-switch.mjs.map +1 -1
- package/fesm2022/ngbase-adk-table.mjs +581 -83
- package/fesm2022/ngbase-adk-table.mjs.map +1 -1
- package/fesm2022/ngbase-adk-tabs.mjs +37 -35
- package/fesm2022/ngbase-adk-tabs.mjs.map +1 -1
- package/fesm2022/ngbase-adk-test.mjs.map +1 -1
- package/fesm2022/ngbase-adk-toggle-group.mjs +20 -34
- package/fesm2022/ngbase-adk-toggle-group.mjs.map +1 -1
- package/fesm2022/ngbase-adk-toggle.mjs +14 -19
- package/fesm2022/ngbase-adk-toggle.mjs.map +1 -1
- package/fesm2022/ngbase-adk-tooltip.mjs +12 -19
- package/fesm2022/ngbase-adk-tooltip.mjs.map +1 -1
- package/fesm2022/ngbase-adk-tour.mjs +52 -52
- package/fesm2022/ngbase-adk-tour.mjs.map +1 -1
- package/fesm2022/ngbase-adk-translate.mjs +8 -10
- package/fesm2022/ngbase-adk-translate.mjs.map +1 -1
- package/fesm2022/ngbase-adk-tree.mjs +20 -20
- package/fesm2022/ngbase-adk-tree.mjs.map +1 -1
- package/fesm2022/ngbase-adk-utils.mjs +30 -43
- package/fesm2022/ngbase-adk-utils.mjs.map +1 -1
- package/fesm2022/ngbase-adk-virtualizer.mjs +9 -9
- package/fesm2022/ngbase-adk-virtualizer.mjs.map +1 -1
- package/package.json +101 -101
- package/schematics/components/files/accordion/accordion.ts.template +8 -5
- package/schematics/components/files/audio/AudioPlayer.ts.template +245 -0
- package/schematics/components/files/audio/AudioRecorder.ts.template +377 -0
- package/schematics/components/files/audio/AudioVisualizer.ts.template +175 -0
- package/schematics/components/files/audio/index.ts.template +3 -0
- package/schematics/components/files/charts/area-chart.component.ts.template +278 -0
- package/schematics/components/files/charts/bar-chart.component.ts.template +262 -0
- package/schematics/components/files/charts/chart-tooltip.component.ts.template +168 -0
- package/schematics/components/files/charts/index.ts.template +4 -0
- package/schematics/components/files/charts/line-chart.component.ts.template +238 -0
- package/schematics/components/files/charts/pie-chart.component.ts.template +283 -0
- package/schematics/components/files/checkbox/checkbox.ts.template +2 -2
- package/schematics/components/files/color-picker/color-picker.ts.template +2 -2
- package/schematics/components/files/dialog/dialog.ts.template +18 -14
- package/schematics/components/files/drawer/drawer.ts.template +30 -27
- package/schematics/components/files/form-field/form-field.ts.template +49 -21
- package/schematics/components/files/pagination/pagination.ts.template +4 -4
- package/schematics/components/files/picasa/picasa-base.component.ts.template +15 -30
- package/schematics/components/files/popover/popover.ts.template +15 -4
- package/schematics/components/files/select/list-selection.ts.template +0 -2
- package/schematics/components/files/select/option.ts.template +1 -1
- package/schematics/components/files/selectable/selectable.ts.template +2 -2
- package/schematics/components/files/sheet/sheet.ts.template +26 -14
- package/schematics/components/files/sidenav/sidenav.ts.template +7 -5
- package/schematics/components/files/sonner/sonner.ts.template +1 -2
- package/schematics/components/files/stepper/stepper.ts.template +2 -4
- package/schematics/components/files/switch/switch.ts.template +2 -2
- package/schematics/components/files/table/table.ts.template +43 -3
- package/schematics/components/files/theme/theme.service.ts.template +3 -3
- package/schematics/components/files/toggle/toggle.ts.template +1 -1
- package/schematics/components/files/toggle-group/toggle-group.ts.template +1 -1
- package/schematics/components/files/tooltip/tooltip.ts.template +2 -3
- package/{accordion/index.d.ts → types/ngbase-adk-accordion.d.ts} +1 -3
- package/{autocomplete/index.d.ts → types/ngbase-adk-autocomplete.d.ts} +2 -7
- package/{checkbox/index.d.ts → types/ngbase-adk-checkbox.d.ts} +8 -14
- package/types/ngbase-adk-clipboard.d.ts +12 -0
- package/{color-picker/index.d.ts → types/ngbase-adk-color-picker.d.ts} +14 -26
- package/{datepicker/index.d.ts → types/ngbase-adk-datepicker.d.ts} +9 -18
- package/{dialog/index.d.ts → types/ngbase-adk-dialog.d.ts} +3 -8
- package/types/ngbase-adk-form-field.d.ts +88 -0
- package/{inline-edit/index.d.ts → types/ngbase-adk-inline-edit.d.ts} +8 -16
- package/{menu/index.d.ts → types/ngbase-adk-menu.d.ts} +6 -5
- package/{otp/index.d.ts → types/ngbase-adk-otp.d.ts} +8 -16
- package/{popover/index.d.ts → types/ngbase-adk-popover.d.ts} +14 -2
- package/{portal/index.d.ts → types/ngbase-adk-portal.d.ts} +29 -8
- package/{radio/index.d.ts → types/ngbase-adk-radio.d.ts} +9 -12
- package/{resizable/index.d.ts → types/ngbase-adk-resizable.d.ts} +4 -4
- package/{scroll-area/index.d.ts → types/ngbase-adk-scroll-area.d.ts} +2 -1
- package/{select/index.d.ts → types/ngbase-adk-select.d.ts} +8 -22
- package/{selectable/index.d.ts → types/ngbase-adk-selectable.d.ts} +6 -10
- package/{sheet/index.d.ts → types/ngbase-adk-sheet.d.ts} +4 -3
- package/{sidenav/index.d.ts → types/ngbase-adk-sidenav.d.ts} +7 -8
- package/{slider/index.d.ts → types/ngbase-adk-slider.d.ts} +8 -17
- package/{sonner/index.d.ts → types/ngbase-adk-sonner.d.ts} +1 -3
- package/{stepper/index.d.ts → types/ngbase-adk-stepper.d.ts} +1 -4
- package/{switch/index.d.ts → types/ngbase-adk-switch.d.ts} +7 -14
- package/{table/index.d.ts → types/ngbase-adk-table.d.ts} +126 -3
- package/{test/index.d.ts → types/ngbase-adk-test.d.ts} +1 -1
- package/{toggle-group/index.d.ts → types/ngbase-adk-toggle-group.d.ts} +5 -10
- package/types/ngbase-adk-toggle.d.ts +14 -0
- package/{tooltip/index.d.ts → types/ngbase-adk-tooltip.d.ts} +9 -11
- package/{tour/index.d.ts → types/ngbase-adk-tour.d.ts} +4 -6
- package/{utils/index.d.ts → types/ngbase-adk-utils.d.ts} +15 -11
- package/clipboard/index.d.ts +0 -11
- package/form-field/index.d.ts +0 -97
- package/toggle/index.d.ts +0 -16
- /package/{a11y/index.d.ts → types/ngbase-adk-a11y.d.ts} +0 -0
- /package/{avatar/index.d.ts → types/ngbase-adk-avatar.d.ts} +0 -0
- /package/{bidi/index.d.ts → types/ngbase-adk-bidi.d.ts} +0 -0
- /package/{breadcrumb/index.d.ts → types/ngbase-adk-breadcrumb.d.ts} +0 -0
- /package/{cache/index.d.ts → types/ngbase-adk-cache.d.ts} +0 -0
- /package/{carousel/index.d.ts → types/ngbase-adk-carousel.d.ts} +0 -0
- /package/{chip/index.d.ts → types/ngbase-adk-chip.d.ts} +0 -0
- /package/{collections/index.d.ts → types/ngbase-adk-collections.d.ts} +0 -0
- /package/{cookies/index.d.ts → types/ngbase-adk-cookies.d.ts} +0 -0
- /package/{drag/index.d.ts → types/ngbase-adk-drag.d.ts} +0 -0
- /package/{hover-card/index.d.ts → types/ngbase-adk-hover-card.d.ts} +0 -0
- /package/{icon/index.d.ts → types/ngbase-adk-icon.d.ts} +0 -0
- /package/{jwt/index.d.ts → types/ngbase-adk-jwt.d.ts} +0 -0
- /package/{keys/index.d.ts → types/ngbase-adk-keys.d.ts} +0 -0
- /package/{layout/index.d.ts → types/ngbase-adk-layout.d.ts} +0 -0
- /package/{list/index.d.ts → types/ngbase-adk-list.d.ts} +0 -0
- /package/{mask/index.d.ts → types/ngbase-adk-mask.d.ts} +0 -0
- /package/{network/index.d.ts → types/ngbase-adk-network.d.ts} +0 -0
- /package/{pagination/index.d.ts → types/ngbase-adk-pagination.d.ts} +0 -0
- /package/{progress/index.d.ts → types/ngbase-adk-progress.d.ts} +0 -0
- /package/{tabs/index.d.ts → types/ngbase-adk-tabs.d.ts} +0 -0
- /package/{translate/index.d.ts → types/ngbase-adk-translate.d.ts} +0 -0
- /package/{tree/index.d.ts → types/ngbase-adk-tree.d.ts} +0 -0
- /package/{virtualizer/index.d.ts → types/ngbase-adk-virtualizer.d.ts} +0 -0
- /package/{index.d.ts → types/ngbase-adk.d.ts} +0 -0
|
@@ -0,0 +1,238 @@
|
|
|
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 LineData {
|
|
15
|
+
label: string;
|
|
16
|
+
value: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
@Component({
|
|
20
|
+
selector: '<%= name %>-line-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 LineChartComponent {
|
|
37
|
+
readonly chartRef = viewChild.required<ElementRef<SVGElement>>('chart');
|
|
38
|
+
|
|
39
|
+
data = input.required<LineData[]>();
|
|
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-1 for line)
|
|
120
|
+
const lineColor = getComputedStyle(document.documentElement)
|
|
121
|
+
.getPropertyValue('--chart-1')
|
|
122
|
+
.trim();
|
|
123
|
+
|
|
124
|
+
// Create scales
|
|
125
|
+
const x = d3
|
|
126
|
+
.scalePoint()
|
|
127
|
+
.domain(data.map(d => d.label))
|
|
128
|
+
.range([0, width])
|
|
129
|
+
.padding(0.5);
|
|
130
|
+
|
|
131
|
+
const y = d3
|
|
132
|
+
.scaleLinear()
|
|
133
|
+
.domain([0, d3.max(data, d => d.value) || 100])
|
|
134
|
+
.nice()
|
|
135
|
+
.range([height, 0]);
|
|
136
|
+
|
|
137
|
+
// Add X axis
|
|
138
|
+
g.append('g')
|
|
139
|
+
.attr('transform', `translate(0,${height})`)
|
|
140
|
+
.call(d3.axisBottom(x))
|
|
141
|
+
.style('color', textColor)
|
|
142
|
+
.selectAll('text')
|
|
143
|
+
.style('font-size', '12px');
|
|
144
|
+
|
|
145
|
+
// Add Y axis (conditionally)
|
|
146
|
+
if (this.showYAxis()) {
|
|
147
|
+
g.append('g')
|
|
148
|
+
.call(d3.axisLeft(y))
|
|
149
|
+
.style('color', textColor)
|
|
150
|
+
.selectAll('text')
|
|
151
|
+
.style('font-size', '12px');
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Style axis lines
|
|
155
|
+
g.selectAll('.domain, .tick line').style('stroke', borderColor);
|
|
156
|
+
|
|
157
|
+
// Add grid lines
|
|
158
|
+
g.append('g')
|
|
159
|
+
.attr('class', 'grid')
|
|
160
|
+
.call(
|
|
161
|
+
d3
|
|
162
|
+
.axisLeft(y)
|
|
163
|
+
.tickSize(-width)
|
|
164
|
+
.tickFormat(() => ''),
|
|
165
|
+
)
|
|
166
|
+
.select('.domain')
|
|
167
|
+
.remove();
|
|
168
|
+
|
|
169
|
+
// Style grid lines
|
|
170
|
+
g.selectAll('.grid .tick line').style('stroke', 'rgb(229 229 229 / 50%)');
|
|
171
|
+
|
|
172
|
+
// Create line generator
|
|
173
|
+
const line = d3
|
|
174
|
+
.line<LineData>()
|
|
175
|
+
.x(d => x(d.label) || 0)
|
|
176
|
+
.y(d => y(d.value))
|
|
177
|
+
.curve(d3.curveMonotoneX);
|
|
178
|
+
|
|
179
|
+
// Add the line path
|
|
180
|
+
const path = g
|
|
181
|
+
.append('path')
|
|
182
|
+
.datum(data)
|
|
183
|
+
.attr('fill', 'none')
|
|
184
|
+
.attr('stroke', lineColor)
|
|
185
|
+
.attr('stroke-width', 2)
|
|
186
|
+
.attr('d', line);
|
|
187
|
+
|
|
188
|
+
// Animate line drawing
|
|
189
|
+
const totalLength = path.node()?.getTotalLength() || 0;
|
|
190
|
+
path
|
|
191
|
+
.attr('stroke-dasharray', `${totalLength} ${totalLength}`)
|
|
192
|
+
.attr('stroke-dashoffset', totalLength)
|
|
193
|
+
.transition()
|
|
194
|
+
.duration(1200)
|
|
195
|
+
.ease(d3.easeLinear)
|
|
196
|
+
.attr('stroke-dashoffset', 0);
|
|
197
|
+
|
|
198
|
+
// Add circles for data points
|
|
199
|
+
const circles = g
|
|
200
|
+
.selectAll('.dot')
|
|
201
|
+
.data(data)
|
|
202
|
+
.enter()
|
|
203
|
+
.append('circle')
|
|
204
|
+
.attr('class', 'dot')
|
|
205
|
+
.attr('cx', d => x(d.label) || 0)
|
|
206
|
+
.attr('cy', d => y(d.value))
|
|
207
|
+
.attr('r', 0)
|
|
208
|
+
.attr('fill', lineColor)
|
|
209
|
+
.attr('stroke', 'transparent')
|
|
210
|
+
.attr('stroke-width', 2)
|
|
211
|
+
.style('cursor', 'pointer');
|
|
212
|
+
|
|
213
|
+
// Add hover effects for tooltip
|
|
214
|
+
circles
|
|
215
|
+
.on('mouseenter', (event, d) => {
|
|
216
|
+
const circleX = x(d.label) || 0;
|
|
217
|
+
const circleY = y(d.value);
|
|
218
|
+
const svgRect = this.chartRef().nativeElement.getBoundingClientRect();
|
|
219
|
+
|
|
220
|
+
this.tooltipContent.set({ label: d.label, value: d.value });
|
|
221
|
+
this.tooltipX.set(svgRect.left + leftMargin + circleX);
|
|
222
|
+
this.tooltipY.set(svgRect.top + this.margin.top + circleY - 10);
|
|
223
|
+
this.tooltipVisible.set(true);
|
|
224
|
+
|
|
225
|
+
// Enlarge circle on hover
|
|
226
|
+
d3.select(event.currentTarget).transition().duration(200).attr('r', 7);
|
|
227
|
+
})
|
|
228
|
+
.on('mouseleave', event => {
|
|
229
|
+
this.tooltipVisible.set(false);
|
|
230
|
+
|
|
231
|
+
// Restore circle size
|
|
232
|
+
d3.select(event.currentTarget).transition().duration(200).attr('r', 4);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
// Animate circles
|
|
236
|
+
circles.transition().delay(1200).duration(400).attr('r', 4);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
@@ -0,0 +1,283 @@
|
|
|
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 PieData {
|
|
15
|
+
label: string;
|
|
16
|
+
value: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
@Component({
|
|
20
|
+
selector: '<%= name %>-pie-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: 'flex justify-center block relative',
|
|
34
|
+
},
|
|
35
|
+
})
|
|
36
|
+
export class PieChartComponent {
|
|
37
|
+
readonly chartRef = viewChild.required<ElementRef<SVGElement>>('chart');
|
|
38
|
+
|
|
39
|
+
data = input.required<PieData[]>();
|
|
40
|
+
|
|
41
|
+
height = input(300);
|
|
42
|
+
innerRadius = input(0); // Set to 0 for pie, >0 for donut
|
|
43
|
+
|
|
44
|
+
private svg!: d3.Selection<SVGElement, unknown, null, undefined>;
|
|
45
|
+
|
|
46
|
+
// Tooltip state
|
|
47
|
+
tooltipVisible = signal(false);
|
|
48
|
+
tooltipX = signal(0);
|
|
49
|
+
tooltipY = signal(0);
|
|
50
|
+
tooltipContent = signal<TooltipContent>({ label: '', value: 0 });
|
|
51
|
+
|
|
52
|
+
private resizeObserver: ResizeObserver | undefined;
|
|
53
|
+
|
|
54
|
+
constructor() {
|
|
55
|
+
afterNextRender(() => {
|
|
56
|
+
this.svg = d3.select(this.chartRef().nativeElement);
|
|
57
|
+
this.updateChart();
|
|
58
|
+
|
|
59
|
+
this.resizeObserver = new ResizeObserver(() => {
|
|
60
|
+
this.updateChart();
|
|
61
|
+
});
|
|
62
|
+
this.resizeObserver.observe(this.chartRef().nativeElement.parentElement!);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// Watch for input changes and update chart
|
|
66
|
+
effect(() => {
|
|
67
|
+
const data = this.data();
|
|
68
|
+
const _height = this.height();
|
|
69
|
+
const _innerRadius = this.innerRadius();
|
|
70
|
+
|
|
71
|
+
// Update chart if svg is initialized
|
|
72
|
+
if (this.svg && data) {
|
|
73
|
+
this.updateChart();
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
ngOnDestroy() {
|
|
79
|
+
if (this.resizeObserver) {
|
|
80
|
+
this.resizeObserver.disconnect();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
private updateChart(): void {
|
|
85
|
+
if (!this.svg) return;
|
|
86
|
+
|
|
87
|
+
const data = this.data();
|
|
88
|
+
// Get actual width from parent container
|
|
89
|
+
const containerWidth = this.chartRef().nativeElement.parentElement?.clientWidth || 0;
|
|
90
|
+
|
|
91
|
+
if (containerWidth === 0) return;
|
|
92
|
+
|
|
93
|
+
const width = containerWidth;
|
|
94
|
+
const height = this.height();
|
|
95
|
+
|
|
96
|
+
// Legend configuration
|
|
97
|
+
const itemWidth = 120; // Width for each legend item
|
|
98
|
+
const itemsPerRow = Math.max(1, Math.floor(width / itemWidth));
|
|
99
|
+
const legendPadding = 25; // Vertical spacing between rows
|
|
100
|
+
const numRows = Math.ceil(data.length / itemsPerRow);
|
|
101
|
+
const legendHeight = numRows * legendPadding; // Total calculated legend height
|
|
102
|
+
|
|
103
|
+
// Calculate available space for pie chart
|
|
104
|
+
// Reserve space for margins and legend
|
|
105
|
+
const chartMargin = 20;
|
|
106
|
+
const availableHeight = height - legendHeight - chartMargin;
|
|
107
|
+
const availableWidth = width;
|
|
108
|
+
|
|
109
|
+
// Calculate radius to fit in available space
|
|
110
|
+
const radius = Math.min(availableWidth, availableHeight) / 2 - 10;
|
|
111
|
+
|
|
112
|
+
// Calculate center efficiency
|
|
113
|
+
const centerY = availableHeight / 2 + chartMargin / 2;
|
|
114
|
+
const centerX = width / 2;
|
|
115
|
+
|
|
116
|
+
// Clear previous content
|
|
117
|
+
this.svg.selectAll('*').remove();
|
|
118
|
+
|
|
119
|
+
// Set SVG dimensions - width/height/viewBox must match
|
|
120
|
+
this.svg
|
|
121
|
+
.attr('width', containerWidth)
|
|
122
|
+
.attr('height', this.height())
|
|
123
|
+
.attr('viewBox', `0 0 ${containerWidth} ${this.height()}`);
|
|
124
|
+
|
|
125
|
+
// Get CSS variables for theming
|
|
126
|
+
const textColor = getComputedStyle(document.documentElement)
|
|
127
|
+
.getPropertyValue('--color-text')
|
|
128
|
+
.trim();
|
|
129
|
+
|
|
130
|
+
// Get chart colors
|
|
131
|
+
const chartColors = Array.from({ length: 5 }, (_, i) =>
|
|
132
|
+
getComputedStyle(document.documentElement)
|
|
133
|
+
.getPropertyValue(`--chart-${i + 1}`)
|
|
134
|
+
.trim(),
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
// Create color scale using chart colors
|
|
138
|
+
const colorScale = d3
|
|
139
|
+
.scaleOrdinal<string>()
|
|
140
|
+
.domain(data.map(d => d.label))
|
|
141
|
+
.range(chartColors);
|
|
142
|
+
|
|
143
|
+
// Create chart group
|
|
144
|
+
const g = this.svg.append('g').attr('transform', `translate(${centerX},${centerY})`);
|
|
145
|
+
|
|
146
|
+
// Create pie layout
|
|
147
|
+
const pie = d3
|
|
148
|
+
.pie<PieData>()
|
|
149
|
+
.value(d => d.value)
|
|
150
|
+
.sort(null);
|
|
151
|
+
|
|
152
|
+
// Create arc generator
|
|
153
|
+
const arc = d3
|
|
154
|
+
.arc<d3.PieArcDatum<PieData>>()
|
|
155
|
+
.innerRadius(this.innerRadius())
|
|
156
|
+
.outerRadius(radius);
|
|
157
|
+
|
|
158
|
+
// Create arc for hover effect
|
|
159
|
+
const arcHover = d3
|
|
160
|
+
.arc<d3.PieArcDatum<PieData>>()
|
|
161
|
+
.innerRadius(this.innerRadius())
|
|
162
|
+
.outerRadius(radius + 10);
|
|
163
|
+
|
|
164
|
+
// Create arcs
|
|
165
|
+
const arcs = g.selectAll('.arc').data(pie(data)).enter().append('g').attr('class', 'arc');
|
|
166
|
+
|
|
167
|
+
// Add paths with animation
|
|
168
|
+
const paths = arcs
|
|
169
|
+
.append('path')
|
|
170
|
+
.attr('fill', d => colorScale(d.data.label))
|
|
171
|
+
.style('cursor', 'pointer');
|
|
172
|
+
|
|
173
|
+
// Add hover effects for tooltip
|
|
174
|
+
paths
|
|
175
|
+
.on('mouseenter', (event, d) => {
|
|
176
|
+
const total = d3.sum(data, item => item.value);
|
|
177
|
+
const percentage = ((d.data.value / total) * 100).toFixed(1);
|
|
178
|
+
|
|
179
|
+
this.tooltipContent.set({
|
|
180
|
+
label: d.data.label,
|
|
181
|
+
value: d.data.value,
|
|
182
|
+
percentage: parseFloat(percentage),
|
|
183
|
+
});
|
|
184
|
+
this.tooltipX.set(event.clientX);
|
|
185
|
+
this.tooltipY.set(event.clientY);
|
|
186
|
+
this.tooltipVisible.set(true);
|
|
187
|
+
|
|
188
|
+
// Expand arc on hover
|
|
189
|
+
d3.select(event.currentTarget)
|
|
190
|
+
.transition()
|
|
191
|
+
.duration(200)
|
|
192
|
+
.attr('d', arcHover as any);
|
|
193
|
+
})
|
|
194
|
+
.on('mousemove', event => {
|
|
195
|
+
this.tooltipX.set(event.clientX);
|
|
196
|
+
this.tooltipY.set(event.clientY);
|
|
197
|
+
})
|
|
198
|
+
.on('mouseleave', event => {
|
|
199
|
+
this.tooltipVisible.set(false);
|
|
200
|
+
|
|
201
|
+
// Restore arc size
|
|
202
|
+
d3.select(event.currentTarget)
|
|
203
|
+
.transition()
|
|
204
|
+
.duration(200)
|
|
205
|
+
.attr('d', arc as any);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// Animate paths
|
|
209
|
+
paths
|
|
210
|
+
.transition()
|
|
211
|
+
.duration(800)
|
|
212
|
+
.attrTween('d', function (d) {
|
|
213
|
+
const interpolate = d3.interpolate({ startAngle: 0, endAngle: 0 }, d);
|
|
214
|
+
return function (t) {
|
|
215
|
+
return arc(interpolate(t) as any) || '';
|
|
216
|
+
};
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
// Add labels (only if slice is big enough)
|
|
220
|
+
arcs
|
|
221
|
+
.filter(d => d.endAngle - d.startAngle > 0.25)
|
|
222
|
+
.append('text')
|
|
223
|
+
.attr('transform', d => {
|
|
224
|
+
const [x, y] = arc.centroid(d);
|
|
225
|
+
return `translate(${x},${y})`;
|
|
226
|
+
})
|
|
227
|
+
.attr('text-anchor', 'middle')
|
|
228
|
+
.style('font-size', '12px')
|
|
229
|
+
.style('font-weight', '600')
|
|
230
|
+
.style('fill', 'white')
|
|
231
|
+
.style('opacity', 0)
|
|
232
|
+
.text(d => `${d.data.value}`)
|
|
233
|
+
.transition()
|
|
234
|
+
.delay(800)
|
|
235
|
+
.duration(400)
|
|
236
|
+
.style('opacity', 1);
|
|
237
|
+
|
|
238
|
+
// Add legend at the bottom with wrapping
|
|
239
|
+
const legendStartY = availableHeight + chartMargin; // Start logic below the chart area
|
|
240
|
+
const legend = this.svg.append('g').attr('transform', `translate(0, ${legendStartY})`);
|
|
241
|
+
|
|
242
|
+
const legendItems = legend
|
|
243
|
+
.selectAll('.legend-item')
|
|
244
|
+
.data(data)
|
|
245
|
+
.enter()
|
|
246
|
+
.append('g')
|
|
247
|
+
.attr('class', 'legend-item')
|
|
248
|
+
.attr('transform', (_d, i) => {
|
|
249
|
+
const row = Math.floor(i / itemsPerRow);
|
|
250
|
+
const col = i % itemsPerRow;
|
|
251
|
+
const itemsInCurrentRow = Math.min(itemsPerRow, data.length - row * itemsPerRow);
|
|
252
|
+
const rowWidth = itemsInCurrentRow * itemWidth;
|
|
253
|
+
const rowStartX = (width - rowWidth) / 2;
|
|
254
|
+
return `translate(${rowStartX + col * itemWidth}, ${row * legendPadding})`;
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
legendItems
|
|
258
|
+
.append('rect')
|
|
259
|
+
.attr('width', 8)
|
|
260
|
+
.attr('height', 8)
|
|
261
|
+
.attr('rx', 2)
|
|
262
|
+
.attr('fill', d => colorScale(d.label))
|
|
263
|
+
.style('opacity', 0)
|
|
264
|
+
.transition()
|
|
265
|
+
.delay((d, i) => 800 + i * 100)
|
|
266
|
+
.duration(400)
|
|
267
|
+
.style('opacity', 1);
|
|
268
|
+
|
|
269
|
+
legendItems
|
|
270
|
+
.append('text')
|
|
271
|
+
.attr('x', 14)
|
|
272
|
+
.attr('y', 4)
|
|
273
|
+
.attr('dy', '0.35em')
|
|
274
|
+
.style('font-size', '12px')
|
|
275
|
+
.style('fill', textColor)
|
|
276
|
+
.text(d => d.label)
|
|
277
|
+
.style('opacity', 0)
|
|
278
|
+
.transition()
|
|
279
|
+
.delay((d, i) => 800 + i * 100)
|
|
280
|
+
.duration(400)
|
|
281
|
+
.style('opacity', 1);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
@@ -12,13 +12,13 @@ import { CheckboxButton, NgbCheckbox, aliasCheckbox } from '@ngbase/adk/checkbox
|
|
|
12
12
|
<button
|
|
13
13
|
<%= name %>FocusStyle
|
|
14
14
|
ngbCheckboxButton
|
|
15
|
-
class="custom-checkbox relative flex h-4 w-4 flex-none items-center justify-center rounded border
|
|
15
|
+
class="custom-checkbox border-primary relative flex h-4 w-4 flex-none items-center justify-center rounded border transition-colors"
|
|
16
16
|
[class]="
|
|
17
17
|
disabled() ? '!border-muted-foreground bg-muted-foreground' : path() ? 'bg-primary' : ''
|
|
18
18
|
"
|
|
19
19
|
>
|
|
20
20
|
@if (path(); as d) {
|
|
21
|
-
<svg class="h-full w-full
|
|
21
|
+
<svg class="text-background h-full w-full" viewBox="0 0 24 24" aria-hidden="true">
|
|
22
22
|
<path [attr.d]="d" stroke="currentColor" stroke-width="2" fill="none" />
|
|
23
23
|
</svg>
|
|
24
24
|
}
|
|
@@ -31,7 +31,7 @@ import { InputBase } from '@ngbase/adk/form-field';
|
|
|
31
31
|
<div ngbColorSpectrum class="relative h-[160px] w-full overflow-hidden rounded-md border-b">
|
|
32
32
|
<button
|
|
33
33
|
ngbColorSpectrumSelector
|
|
34
|
-
class="pointer-events-none absolute -
|
|
34
|
+
class="pointer-events-none absolute -top-2 -left-2 h-4 w-4 cursor-pointer rounded-full border"
|
|
35
35
|
></button>
|
|
36
36
|
</div>
|
|
37
37
|
<div class="flex gap-4 p-3">
|
|
@@ -75,7 +75,7 @@ export class ColorPicker extends NgbColorPicker {}
|
|
|
75
75
|
@Component({
|
|
76
76
|
selector: '<%= name %>-color-input',
|
|
77
77
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
78
|
-
providers: [registerColorPicker(
|
|
78
|
+
providers: [registerColorPicker(ColorPicker)],
|
|
79
79
|
imports: [ColorPickerTrigger, InputBase],
|
|
80
80
|
template: `
|
|
81
81
|
<input
|
|
@@ -11,10 +11,8 @@ import {
|
|
|
11
11
|
NgbDialogContainer,
|
|
12
12
|
NgbDialogMain,
|
|
13
13
|
ngbDialogPortal,
|
|
14
|
-
viewAnimation,
|
|
15
14
|
} from '@ngbase/adk/dialog';
|
|
16
15
|
import { DragMove } from '@ngbase/adk/drag';
|
|
17
|
-
import { createHostAnimation, fadeAnimation } from '@ngbase/adk/utils';
|
|
18
16
|
|
|
19
17
|
@Component({
|
|
20
18
|
selector: '<%= name %>-dialog',
|
|
@@ -27,13 +25,14 @@ import { createHostAnimation, fadeAnimation } from '@ngbase/adk/utils';
|
|
|
27
25
|
<div
|
|
28
26
|
#myDialog
|
|
29
27
|
ngbDialogMain
|
|
30
|
-
[@viewAnimation]
|
|
31
28
|
class="{{
|
|
32
29
|
'pointer-events-auto relative flex max-w-[100vw] flex-col overflow-hidden border bg-background shadow-lg' +
|
|
33
30
|
(options().fullWindow
|
|
34
31
|
? ' h-screen w-screen border-none'
|
|
35
|
-
: ' max-w-[calc(100vw-30px)] rounded-lg')
|
|
32
|
+
: ' max-w-[calc(100vw-30px)] rounded-lg') +
|
|
33
|
+
(closing() ? ' dialog-content-leave' : ' dialog-content')
|
|
36
34
|
}}"
|
|
35
|
+
(animationend)="onAnimationEnd($event)"
|
|
37
36
|
>
|
|
38
37
|
@if (!isHideHeader) {
|
|
39
38
|
<div
|
|
@@ -55,25 +54,30 @@ import { createHostAnimation, fadeAnimation } from '@ngbase/adk/utils';
|
|
|
55
54
|
</div>
|
|
56
55
|
@if (showBackdrop()) {
|
|
57
56
|
<div
|
|
58
|
-
class="pointer-events-auto absolute top-0 -z-10 h-full w-full bg-black/30"
|
|
59
|
-
[@fadeAnimation]
|
|
60
57
|
ngbDialogBackdrop
|
|
58
|
+
(animationend)="onAnimationEnd($event)"
|
|
59
|
+
class="{{
|
|
60
|
+
'pointer-events-auto absolute top-0 -z-10 h-full w-full bg-black/30' +
|
|
61
|
+
(closing() ? ' dialog-backdrop-leave' : ' dialog-backdrop')
|
|
62
|
+
}}"
|
|
61
63
|
></div>
|
|
62
64
|
}
|
|
63
65
|
</div>
|
|
64
66
|
`,
|
|
65
67
|
host: {
|
|
66
68
|
class: 'fixed block top-0 bottom-0 left-0 right-0 overflow-auto pointer-events-none z-p',
|
|
67
|
-
'[@parentAnimation]': '',
|
|
68
|
-
'(@parentAnimation.done)': 'animationDone()',
|
|
69
69
|
},
|
|
70
|
-
animations: [
|
|
71
|
-
createHostAnimation(['@viewAnimation', '@fadeAnimation']),
|
|
72
|
-
fadeAnimation('200ms'),
|
|
73
|
-
viewAnimation,
|
|
74
|
-
],
|
|
75
70
|
})
|
|
76
|
-
export class DialogContainer extends NgbDialogContainer {
|
|
71
|
+
export class DialogContainer extends NgbDialogContainer {
|
|
72
|
+
protected override readonly hasAnimations = true;
|
|
73
|
+
|
|
74
|
+
onAnimationEnd(event: AnimationEvent) {
|
|
75
|
+
// Only count animation end once per element
|
|
76
|
+
if (event.target === event.currentTarget) {
|
|
77
|
+
this.animationDone();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
77
81
|
|
|
78
82
|
@Directive({
|
|
79
83
|
selector: '[<%= name %>DialogClose]',
|