create-ng-tailwind 3.1.0 → 4.0.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 (47) hide show
  1. package/CHANGELOG.md +81 -350
  2. package/README.md +93 -157
  3. package/lib/cli/index.js +29 -3
  4. package/lib/cli/interactive.js +26 -1
  5. package/lib/managers/ProjectManager.js +0 -4
  6. package/lib/templates/base/components.js +243 -0
  7. package/lib/templates/base/index.js +207 -0
  8. package/lib/templates/base/infrastructure.js +314 -0
  9. package/lib/templates/base/linting.js +359 -0
  10. package/lib/templates/base/pwa.js +103 -0
  11. package/lib/templates/base/services.js +362 -0
  12. package/lib/templates/blog/app.js +250 -0
  13. package/lib/templates/blog/components.js +360 -0
  14. package/lib/templates/blog/i18n.js +77 -0
  15. package/lib/templates/blog/index.js +126 -0
  16. package/lib/templates/blog/pages.js +554 -0
  17. package/lib/templates/blog/services.js +390 -0
  18. package/lib/templates/dashboard/app.js +320 -0
  19. package/lib/templates/dashboard/charts.js +305 -0
  20. package/lib/templates/dashboard/components.js +410 -0
  21. package/lib/templates/dashboard/i18n.js +340 -0
  22. package/lib/templates/dashboard/index.js +141 -0
  23. package/lib/templates/dashboard/layout.js +310 -0
  24. package/lib/templates/dashboard/pages.js +681 -0
  25. package/lib/templates/ecommerce/app.js +315 -0
  26. package/lib/templates/ecommerce/components.js +496 -0
  27. package/lib/templates/ecommerce/i18n.js +389 -0
  28. package/lib/templates/ecommerce/index.js +152 -0
  29. package/lib/templates/ecommerce/layout.js +270 -0
  30. package/lib/templates/ecommerce/pages.js +969 -0
  31. package/lib/templates/ecommerce/services.js +300 -0
  32. package/lib/templates/index.js +12 -0
  33. package/lib/templates/landing/index.js +1117 -0
  34. package/lib/templates/portfolio/index.js +1160 -0
  35. package/lib/templates/saas/index.js +1371 -0
  36. package/lib/templates/starter/app.js +364 -0
  37. package/lib/templates/starter/i18n.js +856 -0
  38. package/lib/templates/starter/index.js +52 -4060
  39. package/lib/templates/starter/layout.js +852 -0
  40. package/lib/templates/starter/pages.js +1241 -0
  41. package/package.json +1 -1
  42. package/lib/templates/starter/features.js +0 -867
  43. package/lib/utils/ai-config.js +0 -641
  44. /package/lib/templates/{starter → base}/advanced-features.js +0 -0
  45. /package/lib/templates/{starter → base}/seo-assets.js +0 -0
  46. /package/lib/templates/{starter → base}/seo-features.js +0 -0
  47. /package/lib/templates/{starter → base}/ui-features.js +0 -0
@@ -0,0 +1,305 @@
1
+ const fs = require("fs-extra");
2
+ const path = require("path");
3
+
4
+ /**
5
+ * Create chart components (Bar, Line, Donut)
6
+ */
7
+ async function createCharts(config) {
8
+ // Bar Chart Component (Pure CSS/SVG - no external dependencies)
9
+ const barChartComponent = `import { Component, Input, OnChanges } from '@angular/core';
10
+ import { DecimalPipe } from '@angular/common';
11
+
12
+ export interface BarChartData {
13
+ label: string;
14
+ value: number;
15
+ color?: string;
16
+ }
17
+
18
+ @Component({
19
+ selector: 'app-chart-bar',
20
+ standalone: true,
21
+ imports: [DecimalPipe],
22
+ template: \`
23
+ <div class="w-full">
24
+ @if (title) {
25
+ <h3 class="mb-4 text-lg font-semibold text-gray-900">{{ title }}</h3>
26
+ }
27
+ <div class="space-y-3">
28
+ @for (item of data; track item.label) {
29
+ <div class="flex items-center gap-4">
30
+ <span class="w-24 truncate text-sm text-gray-600">{{ item.label }}</span>
31
+ <div class="flex-1">
32
+ <div class="h-8 w-full overflow-hidden rounded-lg bg-gray-100">
33
+ <div
34
+ class="flex h-full items-center justify-end rounded-lg px-2 transition-all duration-500"
35
+ [style.width.%]="getPercentage(item.value)"
36
+ [style.backgroundColor]="item.color || defaultColor">
37
+ <span class="text-xs font-medium text-white">{{ item.value | number }}</span>
38
+ </div>
39
+ </div>
40
+ </div>
41
+ </div>
42
+ }
43
+ </div>
44
+ </div>
45
+ \`,
46
+ })
47
+ export class ChartBarComponent implements OnChanges {
48
+ @Input() data: BarChartData[] = [];
49
+ @Input() title?: string;
50
+ @Input() defaultColor = '#3b82f6';
51
+
52
+ private maxValue = 0;
53
+
54
+ ngOnChanges(): void {
55
+ this.maxValue = Math.max(...this.data.map((d) => d.value), 1);
56
+ }
57
+
58
+ getPercentage(value: number): number {
59
+ return (value / this.maxValue) * 100;
60
+ }
61
+ }`;
62
+
63
+ await fs.writeFile(
64
+ path.join(
65
+ config.fullPath,
66
+ "src/app/shared/components/chart-bar/chart-bar.component.ts"
67
+ ),
68
+ barChartComponent
69
+ );
70
+
71
+ // Line Chart Component (SVG-based)
72
+ const lineChartComponent = `import { Component, Input, OnChanges, ElementRef, ViewChild, AfterViewInit } from '@angular/core';
73
+
74
+ export interface LineChartData {
75
+ label: string;
76
+ value: number;
77
+ }
78
+
79
+ @Component({
80
+ selector: 'app-chart-line',
81
+ standalone: true,
82
+ template: \`
83
+ <div class="w-full">
84
+ @if (title) {
85
+ <h3 class="mb-4 text-lg font-semibold text-gray-900">{{ title }}</h3>
86
+ }
87
+ <div class="relative h-64 w-full">
88
+ <svg #chartSvg class="h-full w-full" [attr.viewBox]="'0 0 ' + width + ' ' + height">
89
+ <!-- Grid lines -->
90
+ @for (i of gridLines; track i) {
91
+ <line
92
+ [attr.x1]="padding"
93
+ [attr.y1]="padding + (i * chartHeight) / 4"
94
+ [attr.x2]="width - padding"
95
+ [attr.y2]="padding + (i * chartHeight) / 4"
96
+ stroke="#e5e7eb"
97
+ stroke-width="1"
98
+ />
99
+ }
100
+
101
+ <!-- Line path -->
102
+ <path
103
+ [attr.d]="linePath"
104
+ fill="none"
105
+ [attr.stroke]="lineColor"
106
+ stroke-width="3"
107
+ stroke-linecap="round"
108
+ stroke-linejoin="round"
109
+ />
110
+
111
+ <!-- Area fill -->
112
+ <path
113
+ [attr.d]="areaPath"
114
+ [attr.fill]="lineColor"
115
+ fill-opacity="0.1"
116
+ />
117
+
118
+ <!-- Data points -->
119
+ @for (point of points; track point.x) {
120
+ <circle
121
+ [attr.cx]="point.x"
122
+ [attr.cy]="point.y"
123
+ r="4"
124
+ [attr.fill]="lineColor"
125
+ class="transition-all hover:r-6"
126
+ />
127
+ }
128
+ </svg>
129
+
130
+ <!-- X-axis labels -->
131
+ <div class="mt-2 flex justify-between px-8 text-xs text-gray-500">
132
+ @for (item of data; track item.label) {
133
+ <span>{{ item.label }}</span>
134
+ }
135
+ </div>
136
+ </div>
137
+ </div>
138
+ \`,
139
+ })
140
+ export class ChartLineComponent implements OnChanges, AfterViewInit {
141
+ @ViewChild('chartSvg') chartSvg!: ElementRef;
142
+
143
+ @Input() data: LineChartData[] = [];
144
+ @Input() title?: string;
145
+ @Input() lineColor = '#3b82f6';
146
+
147
+ width = 400;
148
+ height = 200;
149
+ padding = 20;
150
+ chartHeight = 160;
151
+ gridLines = [0, 1, 2, 3, 4];
152
+
153
+ linePath = '';
154
+ areaPath = '';
155
+ points: { x: number; y: number }[] = [];
156
+
157
+ ngAfterViewInit(): void {
158
+ this.calculatePaths();
159
+ }
160
+
161
+ ngOnChanges(): void {
162
+ this.calculatePaths();
163
+ }
164
+
165
+ private calculatePaths(): void {
166
+ if (this.data.length === 0) return;
167
+
168
+ const maxValue = Math.max(...this.data.map((d) => d.value), 1);
169
+ const stepX = (this.width - this.padding * 2) / (this.data.length - 1 || 1);
170
+
171
+ this.points = this.data.map((item, index) => ({
172
+ x: this.padding + index * stepX,
173
+ y: this.padding + this.chartHeight - (item.value / maxValue) * this.chartHeight,
174
+ }));
175
+
176
+ if (this.points.length > 0) {
177
+ this.linePath = this.points
178
+ .map((p, i) => (i === 0 ? \`M \${p.x} \${p.y}\` : \`L \${p.x} \${p.y}\`))
179
+ .join(' ');
180
+
181
+ this.areaPath =
182
+ this.linePath +
183
+ \` L \${this.points[this.points.length - 1].x} \${this.padding + this.chartHeight}\` +
184
+ \` L \${this.points[0].x} \${this.padding + this.chartHeight} Z\`;
185
+ }
186
+ }
187
+ }`;
188
+
189
+ await fs.writeFile(
190
+ path.join(
191
+ config.fullPath,
192
+ "src/app/shared/components/chart-line/chart-line.component.ts"
193
+ ),
194
+ lineChartComponent
195
+ );
196
+
197
+ // Donut Chart Component (SVG-based)
198
+ const donutChartComponent = `import { Component, Input, OnChanges } from '@angular/core';
199
+ import { DecimalPipe } from '@angular/common';
200
+
201
+ export interface DonutChartData {
202
+ label: string;
203
+ value: number;
204
+ color: string;
205
+ }
206
+
207
+ @Component({
208
+ selector: 'app-chart-donut',
209
+ standalone: true,
210
+ imports: [DecimalPipe],
211
+ template: \`
212
+ <div class="flex flex-col items-center">
213
+ @if (title) {
214
+ <h3 class="mb-4 text-lg font-semibold text-gray-900">{{ title }}</h3>
215
+ }
216
+ <div class="relative">
217
+ <svg [attr.width]="size" [attr.height]="size" [attr.viewBox]="'0 0 ' + size + ' ' + size">
218
+ @for (segment of segments; track segment.label) {
219
+ <circle
220
+ [attr.cx]="center"
221
+ [attr.cy]="center"
222
+ [attr.r]="radius"
223
+ fill="none"
224
+ [attr.stroke]="segment.color"
225
+ [attr.stroke-width]="strokeWidth"
226
+ [attr.stroke-dasharray]="segment.dashArray"
227
+ [attr.stroke-dashoffset]="segment.dashOffset"
228
+ stroke-linecap="round"
229
+ class="transition-all duration-500"
230
+ [attr.transform]="'rotate(-90 ' + center + ' ' + center + ')'"
231
+ />
232
+ }
233
+ </svg>
234
+ <!-- Center text -->
235
+ <div class="absolute inset-0 flex flex-col items-center justify-center">
236
+ <span class="text-3xl font-bold text-gray-900">{{ total | number }}</span>
237
+ <span class="text-sm text-gray-500">Total</span>
238
+ </div>
239
+ </div>
240
+
241
+ <!-- Legend -->
242
+ <div class="mt-6 grid grid-cols-2 gap-4">
243
+ @for (item of data; track item.label) {
244
+ <div class="flex items-center gap-2">
245
+ <div class="h-3 w-3 rounded-full" [style.backgroundColor]="item.color"></div>
246
+ <span class="text-sm text-gray-600">{{ item.label }}</span>
247
+ <span class="text-sm font-medium text-gray-900">{{ item.value | number }}</span>
248
+ </div>
249
+ }
250
+ </div>
251
+ </div>
252
+ \`,
253
+ })
254
+ export class ChartDonutComponent implements OnChanges {
255
+ @Input() data: DonutChartData[] = [];
256
+ @Input() title?: string;
257
+ @Input() size = 200;
258
+ @Input() strokeWidth = 24;
259
+
260
+ center = 100;
261
+ radius = 80;
262
+ circumference = 2 * Math.PI * 80;
263
+ total = 0;
264
+ segments: {
265
+ label: string;
266
+ color: string;
267
+ dashArray: string;
268
+ dashOffset: number;
269
+ }[] = [];
270
+
271
+ ngOnChanges(): void {
272
+ this.center = this.size / 2;
273
+ this.radius = (this.size - this.strokeWidth) / 2;
274
+ this.circumference = 2 * Math.PI * this.radius;
275
+ this.total = this.data.reduce((sum, item) => sum + item.value, 0);
276
+ this.calculateSegments();
277
+ }
278
+
279
+ private calculateSegments(): void {
280
+ let offset = 0;
281
+ this.segments = this.data.map((item) => {
282
+ const percentage = this.total > 0 ? item.value / this.total : 0;
283
+ const dashArray = \`\${percentage * this.circumference} \${this.circumference}\`;
284
+ const segment = {
285
+ label: item.label,
286
+ color: item.color,
287
+ dashArray,
288
+ dashOffset: -offset,
289
+ };
290
+ offset += percentage * this.circumference;
291
+ return segment;
292
+ });
293
+ }
294
+ }`;
295
+
296
+ await fs.writeFile(
297
+ path.join(
298
+ config.fullPath,
299
+ "src/app/shared/components/chart-donut/chart-donut.component.ts"
300
+ ),
301
+ donutChartComponent
302
+ );
303
+ }
304
+
305
+ module.exports = { createCharts };