juxscript 1.0.20 → 1.0.21

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 (76) hide show
  1. package/bin/cli.js +121 -72
  2. package/lib/components/alert.ts +143 -92
  3. package/lib/components/badge.ts +93 -94
  4. package/lib/components/base/BaseComponent.ts +397 -0
  5. package/lib/components/base/FormInput.ts +322 -0
  6. package/lib/components/button.ts +40 -131
  7. package/lib/components/card.ts +57 -79
  8. package/lib/components/charts/areachart.ts +315 -0
  9. package/lib/components/charts/barchart.ts +421 -0
  10. package/lib/components/charts/doughnutchart.ts +263 -0
  11. package/lib/components/charts/lib/BaseChart.ts +402 -0
  12. package/lib/components/{chart-types.ts → charts/lib/chart-types.ts} +1 -1
  13. package/lib/components/{chart-utils.ts → charts/lib/chart-utils.ts} +1 -1
  14. package/lib/components/{chart.ts → charts/lib/chart.ts} +3 -3
  15. package/lib/components/checkbox.ts +255 -204
  16. package/lib/components/code.ts +31 -78
  17. package/lib/components/container.ts +113 -130
  18. package/lib/components/data.ts +37 -5
  19. package/lib/components/datepicker.ts +180 -147
  20. package/lib/components/dialog.ts +218 -221
  21. package/lib/components/divider.ts +63 -87
  22. package/lib/components/docs-data.json +498 -2404
  23. package/lib/components/dropdown.ts +191 -236
  24. package/lib/components/element.ts +196 -145
  25. package/lib/components/fileupload.ts +253 -167
  26. package/lib/components/guard.ts +92 -0
  27. package/lib/components/heading.ts +31 -97
  28. package/lib/components/helpers.ts +13 -6
  29. package/lib/components/hero.ts +51 -114
  30. package/lib/components/icon.ts +33 -120
  31. package/lib/components/icons.ts +2 -1
  32. package/lib/components/include.ts +76 -3
  33. package/lib/components/input.ts +155 -407
  34. package/lib/components/kpicard.ts +16 -16
  35. package/lib/components/list.ts +358 -261
  36. package/lib/components/loading.ts +142 -211
  37. package/lib/components/menu.ts +63 -152
  38. package/lib/components/modal.ts +42 -129
  39. package/lib/components/nav.ts +79 -101
  40. package/lib/components/paragraph.ts +38 -102
  41. package/lib/components/progress.ts +108 -166
  42. package/lib/components/radio.ts +283 -234
  43. package/lib/components/script.ts +19 -87
  44. package/lib/components/select.ts +189 -199
  45. package/lib/components/sidebar.ts +110 -141
  46. package/lib/components/style.ts +19 -82
  47. package/lib/components/switch.ts +254 -183
  48. package/lib/components/table.ts +1078 -208
  49. package/lib/components/tabs.ts +42 -106
  50. package/lib/components/theme-toggle.ts +73 -165
  51. package/lib/components/tooltip.ts +85 -316
  52. package/lib/components/write.ts +108 -127
  53. package/lib/jux.ts +67 -41
  54. package/machinery/build.js +466 -0
  55. package/machinery/compiler.js +354 -105
  56. package/machinery/server.js +23 -100
  57. package/machinery/watcher.js +153 -130
  58. package/package.json +1 -1
  59. package/presets/base.css +1166 -0
  60. package/presets/notion.css +2 -1975
  61. package/lib/adapters/base-adapter.js +0 -35
  62. package/lib/adapters/index.js +0 -33
  63. package/lib/adapters/mysql-adapter.js +0 -65
  64. package/lib/adapters/postgres-adapter.js +0 -70
  65. package/lib/adapters/sqlite-adapter.js +0 -56
  66. package/lib/components/areachart.ts +0 -1128
  67. package/lib/components/areachartsmooth.ts +0 -1380
  68. package/lib/components/barchart.ts +0 -1322
  69. package/lib/components/doughnutchart.ts +0 -1259
  70. package/lib/components/footer.ts +0 -165
  71. package/lib/components/header.ts +0 -187
  72. package/lib/components/layout.ts +0 -239
  73. package/lib/components/main.ts +0 -137
  74. package/lib/layouts/default.jux +0 -8
  75. package/lib/layouts/figma.jux +0 -0
  76. /package/lib/{themes → components/charts/lib}/charts.js +0 -0
@@ -1,5 +1,9 @@
1
- import { getOrCreateContainer } from './helpers.js';
2
- import { State } from '../reactivity/state.js';
1
+ import { BaseComponent } from './base/BaseComponent.js';
2
+ import { renderIcon } from './icons.js';
3
+
4
+ // Event definitions - Card is display-only, no interactive events
5
+ const TRIGGER_EVENTS = [] as const;
6
+ const CALLBACK_EVENTS = ['click'] as const; // ✅ Can be clicked for navigation/selection
3
7
 
4
8
  export interface CardOptions {
5
9
  title?: string;
@@ -7,6 +11,7 @@ export interface CardOptions {
7
11
  footer?: string;
8
12
  variant?: string;
9
13
  hoverable?: boolean;
14
+ icon?: string;
10
15
  style?: string;
11
16
  class?: string;
12
17
  }
@@ -17,40 +22,39 @@ type CardState = {
17
22
  footer: string;
18
23
  variant: string;
19
24
  hoverable: boolean;
25
+ icon: string;
20
26
  style: string;
21
27
  class: string;
22
28
  };
23
29
 
24
- export class Card {
25
- state: CardState;
26
- container: HTMLElement | null = null;
27
- _id: string;
28
- id: string;
29
-
30
- // CRITICAL: Store bind/sync instructions for deferred wiring
31
- private _bindings: Array<{ event: string, handler: Function }> = [];
32
- private _syncBindings: Array<{
33
- property: string,
34
- stateObj: State<any>,
35
- toState?: Function,
36
- toComponent?: Function
37
- }> = [];
38
-
30
+ export class Card extends BaseComponent<CardState> {
39
31
  constructor(id: string, options: CardOptions = {}) {
40
- this._id = id;
41
- this.id = id;
42
-
43
- this.state = {
32
+ super(id, {
44
33
  title: options.title ?? '',
45
34
  content: options.content ?? '',
46
35
  footer: options.footer ?? '',
47
36
  variant: options.variant ?? 'default',
48
37
  hoverable: options.hoverable ?? false,
38
+ icon: options.icon ?? '',
49
39
  style: options.style ?? '',
50
40
  class: options.class ?? ''
51
- };
41
+ });
42
+ }
43
+
44
+ protected getTriggerEvents(): readonly string[] {
45
+ return TRIGGER_EVENTS;
46
+ }
47
+
48
+ protected getCallbackEvents(): readonly string[] {
49
+ return CALLBACK_EVENTS;
52
50
  }
53
51
 
52
+ /* ═════════════════════════════════════════════════════════════════
53
+ * FLUENT API
54
+ * ═════════════════════════════════════════════════════════════════ */
55
+
56
+ // ✅ Inherited from BaseComponent
57
+
54
58
  title(value: string): this {
55
59
  this.state.title = value;
56
60
  return this;
@@ -66,47 +70,20 @@ export class Card {
66
70
  return this;
67
71
  }
68
72
 
69
- style(value: string): this {
70
- this.state.style = value;
71
- return this;
72
- }
73
-
74
- class(value: string): this {
75
- this.state.class = value;
73
+ icon(value: string): this {
74
+ this.state.icon = value;
76
75
  return this;
77
76
  }
78
77
 
79
- bind(event: string, handler: Function): this {
80
- this._bindings.push({ event, handler });
81
- return this;
82
- }
83
-
84
- sync(property: string, stateObj: State<any>, toState?: Function, toComponent?: Function): this {
85
- if (!stateObj || typeof stateObj.subscribe !== 'function') {
86
- throw new Error(`Card.sync: Expected a State object for property "${property}"`);
87
- }
88
- this._syncBindings.push({ property, stateObj, toState, toComponent });
89
- return this;
90
- }
78
+ /* ═════════════════════════════════════════════════════════════════
79
+ * RENDER
80
+ * ═════════════════════════════════════════════════════════════════ */
91
81
 
92
82
  render(targetId?: string): this {
93
- // === 1. SETUP: Get or create container ===
94
- let container: HTMLElement;
95
- if (targetId) {
96
- const target = document.querySelector(targetId);
97
- if (!target || !(target instanceof HTMLElement)) {
98
- throw new Error(`Card: Target "${targetId}" not found`);
99
- }
100
- container = target;
101
- } else {
102
- container = getOrCreateContainer(this._id);
103
- }
104
- this.container = container;
83
+ const container = this._setupContainer(targetId);
105
84
 
106
- // === 2. PREPARE: Destructure state ===
107
- const { title, content, footer, variant, hoverable, style, class: className } = this.state;
85
+ const { title, content, footer, variant, hoverable, icon, style, class: className } = this.state;
108
86
 
109
- // === 3. BUILD: Create DOM elements ===
110
87
  const card = document.createElement('div');
111
88
  card.className = `jux-card jux-card-${variant}`;
112
89
  card.id = this._id;
@@ -114,10 +91,24 @@ export class Card {
114
91
  if (className) card.className += ` ${className}`;
115
92
  if (style) card.setAttribute('style', style);
116
93
 
117
- if (title) {
94
+ if (title || icon) {
118
95
  const header = document.createElement('div');
119
96
  header.className = 'jux-card-header';
120
- header.textContent = title;
97
+
98
+ if (icon) {
99
+ const iconEl = document.createElement('span');
100
+ iconEl.className = 'jux-card-icon';
101
+ iconEl.appendChild(renderIcon(icon));
102
+ header.appendChild(iconEl);
103
+ }
104
+
105
+ if (title) {
106
+ const titleEl = document.createElement('span');
107
+ titleEl.className = 'jux-card-title';
108
+ titleEl.textContent = title;
109
+ header.appendChild(titleEl);
110
+ }
111
+
121
112
  card.appendChild(header);
122
113
  }
123
114
 
@@ -133,20 +124,15 @@ export class Card {
133
124
  card.appendChild(footerEl);
134
125
  }
135
126
 
136
- // === 4. WIRE: Attach event listeners and sync bindings ===
137
-
138
- // Wire custom bindings from .bind() calls
139
- this._bindings.forEach(({ event, handler }) => {
140
- card.addEventListener(event, handler as EventListener);
141
- });
127
+ this._wireStandardEvents(card);
142
128
 
143
- // Wire sync bindings from .sync() calls
129
+ // Wire sync bindings
144
130
  this._syncBindings.forEach(({ property, stateObj, toState, toComponent }) => {
145
131
  if (property === 'title') {
146
- const transformToComponent = toComponent || ((v: any) => String(v));
132
+ const transform = toComponent || ((v: any) => String(v));
147
133
 
148
134
  stateObj.subscribe((val: any) => {
149
- const transformed = transformToComponent(val);
135
+ const transformed = transform(val);
150
136
  const header = card.querySelector('.jux-card-header');
151
137
  if (header) {
152
138
  header.textContent = transformed;
@@ -155,19 +141,19 @@ export class Card {
155
141
  });
156
142
  }
157
143
  else if (property === 'content') {
158
- const transformToComponent = toComponent || ((v: any) => String(v));
144
+ const transform = toComponent || ((v: any) => String(v));
159
145
 
160
146
  stateObj.subscribe((val: any) => {
161
- const transformed = transformToComponent(val);
147
+ const transformed = transform(val);
162
148
  body.innerHTML = transformed;
163
149
  this.state.content = transformed;
164
150
  });
165
151
  }
166
152
  else if (property === 'footer') {
167
- const transformToComponent = toComponent || ((v: any) => String(v));
153
+ const transform = toComponent || ((v: any) => String(v));
168
154
 
169
155
  stateObj.subscribe((val: any) => {
170
- const transformed = transformToComponent(val);
156
+ const transformed = transform(val);
171
157
  const footerEl = card.querySelector('.jux-card-footer');
172
158
  if (footerEl) {
173
159
  footerEl.innerHTML = transformed;
@@ -177,17 +163,9 @@ export class Card {
177
163
  }
178
164
  });
179
165
 
180
- // === 5. RENDER: Append to DOM and finalize ===
181
166
  container.appendChild(card);
182
167
  return this;
183
168
  }
184
-
185
- renderTo(juxComponent: any): this {
186
- if (!juxComponent?._id) {
187
- throw new Error('Card.renderTo: Invalid component');
188
- }
189
- return this.render(`#${juxComponent._id}`);
190
- }
191
169
  }
192
170
 
193
171
  export function card(id: string, options: CardOptions = {}): Card {
@@ -0,0 +1,315 @@
1
+ import { BaseChart, BaseChartState, ChartDataPoint } from './lib/BaseChart.js';
2
+
3
+ export interface AreaChartOptions {
4
+ data?: ChartDataPoint[];
5
+ title?: string;
6
+ subtitle?: string;
7
+ xAxisLabel?: string;
8
+ yAxisLabel?: string;
9
+ showTicksX?: boolean;
10
+ showTicksY?: boolean;
11
+ showScaleX?: boolean;
12
+ showScaleY?: boolean;
13
+ scaleXUnit?: string;
14
+ scaleYUnit?: string;
15
+ showLegend?: boolean;
16
+ legendOrientation?: 'horizontal' | 'vertical';
17
+ showDataTable?: boolean;
18
+ showDataLabels?: boolean;
19
+ animate?: boolean;
20
+ animationDuration?: number;
21
+ width?: number;
22
+ height?: number;
23
+ colors?: string[];
24
+ class?: string;
25
+ style?: string;
26
+ theme?: 'google' | 'seriesa' | 'hr' | 'figma' | 'notion' | 'chalk' | 'mint';
27
+ styleMode?: 'default' | 'gradient' | 'outline' | 'dashed' | 'glow' | 'glass';
28
+ borderRadius?: number;
29
+ }
30
+
31
+ interface AreaChartState extends BaseChartState {
32
+ data: ChartDataPoint[];
33
+ }
34
+
35
+ export class AreaChart extends BaseChart<AreaChartState> {
36
+ constructor(id: string, options: AreaChartOptions = {}) {
37
+ const defaultColors = ['#3b82f6', '#ef4444', '#10b981', '#f59e0b', '#8b5cf6'];
38
+ const randomColor = defaultColors[Math.floor(Math.random() * defaultColors.length)];
39
+
40
+ super(id, {
41
+ data: options.data ?? [],
42
+ title: options.title ?? '',
43
+ subtitle: options.subtitle ?? '',
44
+ xAxisLabel: options.xAxisLabel ?? '',
45
+ yAxisLabel: options.yAxisLabel ?? '',
46
+ showTicksX: options.showTicksX ?? true,
47
+ showTicksY: options.showTicksY ?? true,
48
+ showScaleX: options.showScaleX ?? true,
49
+ showScaleY: options.showScaleY ?? true,
50
+ scaleXUnit: options.scaleXUnit ?? '',
51
+ scaleYUnit: options.scaleYUnit ?? '',
52
+ showLegend: options.showLegend ?? false,
53
+ legendOrientation: options.legendOrientation ?? 'horizontal',
54
+ showDataTable: options.showDataTable ?? false,
55
+ showDataLabels: options.showDataLabels ?? true,
56
+ animate: options.animate ?? true,
57
+ animationDuration: options.animationDuration ?? 800,
58
+ width: options.width ?? 600,
59
+ height: options.height ?? 400,
60
+ colors: options.colors ?? [randomColor],
61
+ class: options.class ?? '',
62
+ style: options.style ?? '',
63
+ theme: options.theme,
64
+ styleMode: options.styleMode ?? 'default',
65
+ borderRadius: options.borderRadius ?? 4
66
+ });
67
+ }
68
+
69
+ protected _getChartClassName(): string {
70
+ return 'jux-areachart';
71
+ }
72
+
73
+ protected _createSVG(): SVGSVGElement {
74
+ const { data, width, height, colors, animate, animationDuration } = this.state;
75
+
76
+ const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
77
+ svg.setAttribute('width', width.toString());
78
+ svg.setAttribute('height', height.toString());
79
+ svg.setAttribute('class', 'jux-areachart-svg');
80
+
81
+ if (!data.length) return svg;
82
+
83
+ if (animate) this._addAnimationStyles(svg);
84
+
85
+ const padding = { top: 40, right: 40, bottom: 60, left: 60 };
86
+ const chartWidth = width - padding.left - padding.right;
87
+ const chartHeight = height - padding.top - padding.bottom;
88
+ const maxValue = Math.max(...data.map(d => d.value));
89
+
90
+ this._renderAreaChart(svg, padding, chartWidth, chartHeight, maxValue);
91
+
92
+ return svg;
93
+ }
94
+
95
+ protected _getBaseStyles(): string {
96
+ return `
97
+ .jux-areachart {
98
+ font-family: var(--chart-font-family, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif);
99
+ display: inline-block;
100
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
101
+ border-radius: 12px;
102
+ background: white;
103
+ padding: 24px;
104
+ }
105
+ .jux-areachart-title {
106
+ margin: 0 0 0.5rem 0;
107
+ font-size: 1.25rem;
108
+ font-weight: 600;
109
+ }
110
+ .jux-areachart-subtitle {
111
+ margin: 0 0 1rem 0;
112
+ font-size: 0.875rem;
113
+ color: #6b7280;
114
+ }
115
+ .jux-areachart-legend {
116
+ display: flex;
117
+ flex-wrap: wrap;
118
+ gap: 1rem;
119
+ margin-top: 1rem;
120
+ justify-content: center;
121
+ }
122
+ .jux-areachart-legend-item {
123
+ display: flex;
124
+ align-items: center;
125
+ gap: 0.5rem;
126
+ }
127
+ .jux-areachart-legend-swatch {
128
+ width: 12px;
129
+ height: 12px;
130
+ border-radius: 2px;
131
+ }
132
+ .jux-areachart-legend-label {
133
+ font-size: 0.875rem;
134
+ color: #374151;
135
+ }
136
+ .jux-areachart-table {
137
+ width: 100%;
138
+ margin-top: 1rem;
139
+ border-collapse: collapse;
140
+ font-size: 0.875rem;
141
+ }
142
+ .jux-areachart-table thead th {
143
+ text-align: center;
144
+ padding: 0.5rem;
145
+ border-bottom: 2px solid #e5e7eb;
146
+ font-weight: 600;
147
+ }
148
+ .jux-areachart-table tbody td {
149
+ padding: 0.5rem;
150
+ border-bottom: 1px solid #f3f4f6;
151
+ text-align: center;
152
+ }
153
+ .jux-areachart-svg {
154
+ font-family: inherit;
155
+ }
156
+ `;
157
+ }
158
+
159
+ private _addAnimationStyles(svg: SVGSVGElement): void {
160
+ const { animationDuration } = this.state;
161
+ const animationId = `area-grow-${this._id}`;
162
+ const style = document.createElementNS('http://www.w3.org/2000/svg', 'style');
163
+
164
+ style.textContent = `
165
+ @keyframes ${animationId} {
166
+ from { opacity: 0; transform: scaleY(0); }
167
+ to { opacity: 1; transform: scaleY(1); }
168
+ }
169
+ .jux-area-animated {
170
+ transform-origin: bottom;
171
+ animation: ${animationId} ${animationDuration}ms cubic-bezier(0.4, 0, 0.2, 1) forwards;
172
+ }
173
+ `;
174
+ svg.appendChild(style);
175
+ }
176
+
177
+ private _renderAreaChart(svg: SVGSVGElement, padding: any, chartWidth: number, chartHeight: number, maxValue: number): void {
178
+ const { data, width, height, colors, showDataLabels, animate, xAxisLabel, yAxisLabel, showTicksX, showTicksY, showScaleX, showScaleY, scaleYUnit } = this.state;
179
+
180
+ const yScale = chartHeight / maxValue;
181
+ const xStep = chartWidth / (data.length - 1 || 1);
182
+ const color = colors[0];
183
+
184
+ // Axes
185
+ if (showScaleY) {
186
+ this._renderAxis(svg, padding.left, padding.top, padding.left, height - padding.bottom);
187
+ if (yAxisLabel && showTicksY) {
188
+ this._renderAxisLabel(svg, yAxisLabel, 20, padding.top + chartHeight / 2, -90);
189
+ }
190
+ if (showTicksY) {
191
+ this._renderYTicks(svg, maxValue, yScale, padding, width, height, scaleYUnit, chartHeight);
192
+ }
193
+ }
194
+
195
+ if (showScaleX) {
196
+ this._renderAxis(svg, padding.left, height - padding.bottom, width - padding.right, height - padding.bottom);
197
+ if (xAxisLabel && showTicksX) {
198
+ this._renderAxisLabel(svg, xAxisLabel, padding.left + chartWidth / 2, height - 15, 0);
199
+ }
200
+ }
201
+
202
+ // Build area path
203
+ let pathData = `M ${padding.left} ${height - padding.bottom}`;
204
+ data.forEach((point, i) => {
205
+ const x = padding.left + (i * xStep);
206
+ const y = height - padding.bottom - (point.value * yScale);
207
+ pathData += ` L ${x} ${y}`;
208
+ });
209
+ pathData += ` L ${padding.left + ((data.length - 1) * xStep)} ${height - padding.bottom} Z`;
210
+
211
+ const areaPath = document.createElementNS('http://www.w3.org/2000/svg', 'path');
212
+ areaPath.setAttribute('d', pathData);
213
+ areaPath.setAttribute('fill', color);
214
+ areaPath.setAttribute('fill-opacity', '0.3');
215
+ if (animate) areaPath.classList.add('jux-area-animated');
216
+ svg.appendChild(areaPath);
217
+
218
+ // Line path
219
+ let lineData = '';
220
+ data.forEach((point, i) => {
221
+ const x = padding.left + (i * xStep);
222
+ const y = height - padding.bottom - (point.value * yScale);
223
+ lineData += (i === 0 ? `M ${x} ${y}` : ` L ${x} ${y}`);
224
+ });
225
+
226
+ const linePath = document.createElementNS('http://www.w3.org/2000/svg', 'path');
227
+ linePath.setAttribute('d', lineData);
228
+ linePath.setAttribute('stroke', color);
229
+ linePath.setAttribute('stroke-width', '3');
230
+ linePath.setAttribute('fill', 'none');
231
+ svg.appendChild(linePath);
232
+
233
+ // Data points
234
+ data.forEach((point, i) => {
235
+ const x = padding.left + (i * xStep);
236
+ const y = height - padding.bottom - (point.value * yScale);
237
+
238
+ const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
239
+ circle.setAttribute('cx', x.toString());
240
+ circle.setAttribute('cy', y.toString());
241
+ circle.setAttribute('r', '4');
242
+ circle.setAttribute('fill', color);
243
+ circle.setAttribute('stroke', 'white');
244
+ circle.setAttribute('stroke-width', '2');
245
+ svg.appendChild(circle);
246
+
247
+ if (showDataLabels) {
248
+ const label = document.createElementNS('http://www.w3.org/2000/svg', 'text');
249
+ label.setAttribute('x', x.toString());
250
+ label.setAttribute('y', (y - 10).toString());
251
+ label.setAttribute('text-anchor', 'middle');
252
+ label.setAttribute('fill', '#374151');
253
+ label.setAttribute('font-size', '11');
254
+ label.setAttribute('font-weight', '600');
255
+ label.textContent = point.value.toString();
256
+ svg.appendChild(label);
257
+ }
258
+ });
259
+ }
260
+
261
+ private _renderAxis(svg: SVGSVGElement, x1: number, y1: number, x2: number, y2: number): void {
262
+ const axis = document.createElementNS('http://www.w3.org/2000/svg', 'line');
263
+ axis.setAttribute('x1', x1.toString());
264
+ axis.setAttribute('y1', y1.toString());
265
+ axis.setAttribute('x2', x2.toString());
266
+ axis.setAttribute('y2', y2.toString());
267
+ axis.setAttribute('stroke', '#9ca3af');
268
+ axis.setAttribute('stroke-width', '2');
269
+ svg.appendChild(axis);
270
+ }
271
+
272
+ private _renderAxisLabel(svg: SVGSVGElement, text: string, x: number, y: number, rotate: number): void {
273
+ const label = document.createElementNS('http://www.w3.org/2000/svg', 'text');
274
+ label.setAttribute('x', x.toString());
275
+ label.setAttribute('y', y.toString());
276
+ label.setAttribute('text-anchor', 'middle');
277
+ if (rotate !== 0) label.setAttribute('transform', `rotate(${rotate}, ${x}, ${y})`);
278
+ label.setAttribute('fill', '#6b7280');
279
+ label.setAttribute('font-size', '12');
280
+ label.setAttribute('font-weight', '500');
281
+ label.textContent = text;
282
+ svg.appendChild(label);
283
+ }
284
+
285
+ private _renderYTicks(svg: SVGSVGElement, maxValue: number, yScale: number, padding: any, width: number, height: number, unit: string, chartHeight: number): void {
286
+ const numTicks = 5;
287
+ for (let i = 0; i <= numTicks; i++) {
288
+ const value = (maxValue / numTicks) * i;
289
+ const y = height - padding.bottom - (value * yScale);
290
+
291
+ const gridLine = document.createElementNS('http://www.w3.org/2000/svg', 'line');
292
+ gridLine.setAttribute('x1', padding.left.toString());
293
+ gridLine.setAttribute('y1', y.toString());
294
+ gridLine.setAttribute('x2', (width - padding.right).toString());
295
+ gridLine.setAttribute('y2', y.toString());
296
+ gridLine.setAttribute('stroke', '#e5e7eb');
297
+ gridLine.setAttribute('stroke-width', '1');
298
+ gridLine.setAttribute('stroke-dasharray', '4,4');
299
+ svg.appendChild(gridLine);
300
+
301
+ const tickLabel = document.createElementNS('http://www.w3.org/2000/svg', 'text');
302
+ tickLabel.setAttribute('x', (padding.left - 10).toString());
303
+ tickLabel.setAttribute('y', (y + 4).toString());
304
+ tickLabel.setAttribute('text-anchor', 'end');
305
+ tickLabel.setAttribute('fill', '#6b7280');
306
+ tickLabel.setAttribute('font-size', '11');
307
+ tickLabel.textContent = Math.round(value).toString() + (unit || '');
308
+ svg.appendChild(tickLabel);
309
+ }
310
+ }
311
+ }
312
+
313
+ export function areachart(id: string, options: AreaChartOptions = {}): AreaChart {
314
+ return new AreaChart(id, options);
315
+ }