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.
- package/CHANGELOG.md +81 -350
- package/README.md +93 -157
- package/lib/cli/index.js +29 -3
- package/lib/cli/interactive.js +26 -1
- package/lib/managers/ProjectManager.js +0 -4
- package/lib/templates/base/components.js +243 -0
- package/lib/templates/base/index.js +207 -0
- package/lib/templates/base/infrastructure.js +314 -0
- package/lib/templates/base/linting.js +359 -0
- package/lib/templates/base/pwa.js +103 -0
- package/lib/templates/base/services.js +362 -0
- package/lib/templates/blog/app.js +250 -0
- package/lib/templates/blog/components.js +360 -0
- package/lib/templates/blog/i18n.js +77 -0
- package/lib/templates/blog/index.js +126 -0
- package/lib/templates/blog/pages.js +554 -0
- package/lib/templates/blog/services.js +390 -0
- package/lib/templates/dashboard/app.js +320 -0
- package/lib/templates/dashboard/charts.js +305 -0
- package/lib/templates/dashboard/components.js +410 -0
- package/lib/templates/dashboard/i18n.js +340 -0
- package/lib/templates/dashboard/index.js +141 -0
- package/lib/templates/dashboard/layout.js +310 -0
- package/lib/templates/dashboard/pages.js +681 -0
- package/lib/templates/ecommerce/app.js +315 -0
- package/lib/templates/ecommerce/components.js +496 -0
- package/lib/templates/ecommerce/i18n.js +389 -0
- package/lib/templates/ecommerce/index.js +152 -0
- package/lib/templates/ecommerce/layout.js +270 -0
- package/lib/templates/ecommerce/pages.js +969 -0
- package/lib/templates/ecommerce/services.js +300 -0
- package/lib/templates/index.js +12 -0
- package/lib/templates/landing/index.js +1117 -0
- package/lib/templates/portfolio/index.js +1160 -0
- package/lib/templates/saas/index.js +1371 -0
- package/lib/templates/starter/app.js +364 -0
- package/lib/templates/starter/i18n.js +856 -0
- package/lib/templates/starter/index.js +52 -4060
- package/lib/templates/starter/layout.js +852 -0
- package/lib/templates/starter/pages.js +1241 -0
- package/package.json +1 -1
- package/lib/templates/starter/features.js +0 -867
- package/lib/utils/ai-config.js +0 -641
- /package/lib/templates/{starter → base}/advanced-features.js +0 -0
- /package/lib/templates/{starter → base}/seo-assets.js +0 -0
- /package/lib/templates/{starter → base}/seo-features.js +0 -0
- /package/lib/templates/{starter → base}/ui-features.js +0 -0
package/README.md
CHANGED
|
@@ -4,218 +4,154 @@
|
|
|
4
4
|
|
|
5
5
|
[](https://badge.fury.io/js/create-ng-tailwind)
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
7
|
-
[](https://nodejs.org/)
|
|
8
7
|
[](https://www.npmjs.com/package/create-ng-tailwind)
|
|
9
8
|
|
|
10
|
-
|
|
9
|
+
**Production-ready Angular + Tailwind CSS starter templates**
|
|
11
10
|
|
|
12
11
|
</div>
|
|
13
12
|
|
|
14
13
|
---
|
|
15
14
|
|
|
16
|
-
## ✨ Features
|
|
17
|
-
|
|
18
|
-
- 🎯 **Angular 20+** - Latest version with standalone components and signals
|
|
19
|
-
- 🎨 **Tailwind CSS v4** - Utility-first CSS framework preconfigured
|
|
20
|
-
- 🚀 **SEO Optimized** - Meta tags, Open Graph, Twitter Cards, structured data, robots.txt
|
|
21
|
-
- 🌍 **i18n Translation** - English & Arabic with RTL support
|
|
22
|
-
- 🎮 **Interactive UI** - Toast notifications, Modal dialogs, and components
|
|
23
|
-
- 🏗️ **Zero Configuration** - Everything set up and ready to go
|
|
24
|
-
|
|
25
15
|
## Quick Start
|
|
26
16
|
|
|
27
|
-
### Using npx (Recommended)
|
|
28
|
-
|
|
29
|
-
Run the following command and follow the prompts:
|
|
30
|
-
|
|
31
|
-
```bash
|
|
32
|
-
npx create-ng-tailwind my-awesome-app
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
### Using npm
|
|
36
|
-
|
|
37
|
-
Install globally and use:
|
|
38
|
-
|
|
39
17
|
```bash
|
|
40
|
-
|
|
41
|
-
create-ng-tailwind my-awesome-app
|
|
18
|
+
npx create-ng-tailwind my-app
|
|
42
19
|
```
|
|
43
20
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
The CLI will guide you through:
|
|
47
|
-
1. Choosing a template (Minimal or Starter)
|
|
48
|
-
2. Configuring Server-Side Rendering (optional)
|
|
49
|
-
3. Setting up zoneless application (optional)
|
|
50
|
-
4. Selecting AI tools integration (optional)
|
|
51
|
-
|
|
52
|
-
After setup completes:
|
|
21
|
+
Then follow the prompts to choose your template.
|
|
53
22
|
|
|
54
23
|
```bash
|
|
55
|
-
cd my-
|
|
24
|
+
cd my-app
|
|
56
25
|
npm install
|
|
57
26
|
ng serve
|
|
58
27
|
```
|
|
59
28
|
|
|
60
|
-
Your app
|
|
61
|
-
|
|
62
|
-
## 🏗️ Project Structure
|
|
63
|
-
|
|
64
|
-
```
|
|
65
|
-
my-app/
|
|
66
|
-
├── src/
|
|
67
|
-
│ ├── app/
|
|
68
|
-
│ │ ├── core/ # Core services and interceptors
|
|
69
|
-
│ │ │ ├── services/ # Auth, API, SEO, Toast, Modal, etc.
|
|
70
|
-
│ │ │ ├── interceptors/ # HTTP interceptors
|
|
71
|
-
│ │ │ ├── guards/ # Route guards
|
|
72
|
-
│ │ │ ├── i18n/ # Translation service
|
|
73
|
-
│ │ │ └── utils/ # Structured data utilities
|
|
74
|
-
│ │ ├── shared/ # Shared components and utilities
|
|
75
|
-
│ │ │ ├── components/ # Button, Card, Toast, Modal
|
|
76
|
-
│ │ │ ├── pipes/ # Truncate, TimeAgo
|
|
77
|
-
│ │ │ ├── directives/ # ClickOutside, Tooltip
|
|
78
|
-
│ │ │ └── models/ # TypeScript interfaces
|
|
79
|
-
│ │ ├── features/ # Feature modules
|
|
80
|
-
│ │ │ ├── home/ # Home page
|
|
81
|
-
│ │ │ ├── about/ # About page
|
|
82
|
-
│ │ │ ├── contact/ # Contact page
|
|
83
|
-
│ │ │ └── auth/ # Authentication pages
|
|
84
|
-
│ │ ├── layout/ # Layout components
|
|
85
|
-
│ │ │ ├── header/ # Navigation header
|
|
86
|
-
│ │ │ └── footer/ # Footer component
|
|
87
|
-
│ │ ├── app.config.ts # App configuration
|
|
88
|
-
│ │ ├── app.routes.ts # Routing configuration
|
|
89
|
-
│ │ └── app.ts # Main app component
|
|
90
|
-
│ ├── styles.css # Tailwind CSS imports
|
|
91
|
-
│ └── main.ts # Bootstrap
|
|
92
|
-
├── public/
|
|
93
|
-
│ ├── assets/
|
|
94
|
-
│ │ ├── i18n/ # Translation files (en.json, ar.json)
|
|
95
|
-
│ │ └── images/ # OG images, logos
|
|
96
|
-
│ ├── robots.txt # SEO robots file
|
|
97
|
-
│ ├── favicon.svg # Favicon
|
|
98
|
-
│ └── android-chrome-*.svg # PWA icons
|
|
99
|
-
├── .postcssrc.json # PostCSS configuration
|
|
100
|
-
├── package.json
|
|
101
|
-
└── ...
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
## 🎯 Starter Template Features
|
|
105
|
-
|
|
106
|
-
The **Starter Template** includes 25+ production-ready features:
|
|
107
|
-
|
|
108
|
-
### Core Services
|
|
29
|
+
Your app runs at `http://localhost:4200`
|
|
109
30
|
|
|
110
|
-
|
|
111
|
-
- **ApiService** - Centralized HTTP request handling
|
|
112
|
-
- **SeoService** - Comprehensive SEO management (meta tags, Open Graph, Twitter Cards, structured data)
|
|
113
|
-
- **ToastService** - Notification system (success, error, warning, info)
|
|
114
|
-
- **ModalService** - Dialog system with confirm & alert
|
|
115
|
-
- **LoadingService** - Global loading state with signals
|
|
116
|
-
- **CacheService** - Response caching with TTL
|
|
117
|
-
- **StorageService** - LocalStorage wrapper with type safety
|
|
118
|
-
- **i18nService** - Internationalization management
|
|
119
|
-
|
|
120
|
-
### SEO Optimization (Simple & Ready to Use)
|
|
31
|
+
---
|
|
121
32
|
|
|
122
|
-
|
|
123
|
-
- **Meta Tags** - Title, description, keywords (auto-configured on all pages)
|
|
124
|
-
- **Social Sharing** - Open Graph & Twitter Cards (pre-configured)
|
|
125
|
-
- **Structured Data** - Organization & WebSite schemas (included)
|
|
126
|
-
- **Canonical URLs** - Auto-updated on route changes
|
|
127
|
-
- **Assets** - robots.txt, favicon set, OG image placeholder
|
|
128
|
-
- **Extensible** - Easy to add more schemas as needed
|
|
33
|
+
## Templates
|
|
129
34
|
|
|
130
|
-
###
|
|
35
|
+
### Starter (Recommended)
|
|
131
36
|
|
|
132
|
-
|
|
133
|
-
- **CardComponent** - Flexible container with title & shadow
|
|
134
|
-
- **LoadingSpinnerComponent** - Animated loading indicator
|
|
135
|
-
- **ToastComponent** - Auto-dismiss notifications
|
|
136
|
-
- **ModalComponent** - Accessible dialog with sizes
|
|
37
|
+
Professional foundation with everything you need:
|
|
137
38
|
|
|
138
|
-
|
|
39
|
+
- Header, Footer, Auth pages (Login, Register, Forgot Password)
|
|
40
|
+
- i18n (English & Arabic with RTL)
|
|
41
|
+
- Toast & Modal systems
|
|
42
|
+
- SEO optimization
|
|
43
|
+
- HTTP interceptors (Auth, Error, Loading, Cache)
|
|
44
|
+
- PWA support
|
|
139
45
|
|
|
140
|
-
|
|
141
|
-
- **Directives** - ClickOutside, Tooltip
|
|
142
|
-
- **HTTP Interceptors** - Auth, Error handling, Loading, Caching
|
|
143
|
-
- **TypeScript Path Aliases** - Clean imports with `@core/*`, `@shared/*`, `@features/*`
|
|
46
|
+
### Dashboard
|
|
144
47
|
|
|
145
|
-
|
|
48
|
+
Admin dashboard with:
|
|
146
49
|
|
|
147
|
-
-
|
|
148
|
-
-
|
|
149
|
-
-
|
|
50
|
+
- Collapsible sidebar navigation
|
|
51
|
+
- Stats cards with icons and trends
|
|
52
|
+
- Chart components (Bar, Line, Donut)
|
|
53
|
+
- Data table with sorting & pagination
|
|
54
|
+
- 5 pages: Overview, Analytics, Users, Orders, Settings
|
|
55
|
+
- i18n support (EN/AR) with RTL
|
|
56
|
+
- Dark mode ready
|
|
150
57
|
|
|
151
|
-
###
|
|
58
|
+
### Minimal
|
|
152
59
|
|
|
153
|
-
|
|
154
|
-
- **Authentication Pages** - Login, Register, Forgot Password
|
|
155
|
-
- **Example Pages** - Home, About, Contact with reactive forms
|
|
156
|
-
- **PWA Support** - Service worker configuration
|
|
157
|
-
- **Linting & Formatting** - ESLint + Prettier + simple-git-hooks (pre-commit linting)
|
|
158
|
-
- **Modern Stack** - Tailwind v4, TypeScript, Signals
|
|
60
|
+
Clean slate - just Angular + Tailwind CSS configured.
|
|
159
61
|
|
|
160
|
-
|
|
62
|
+
---
|
|
161
63
|
|
|
162
|
-
|
|
64
|
+
## Features Comparison
|
|
65
|
+
|
|
66
|
+
| Feature | Minimal | Starter | Dashboard |
|
|
67
|
+
| ------------------ | ------- | ------- | --------- |
|
|
68
|
+
| Tailwind CSS v4 | ✅ | ✅ | ✅ |
|
|
69
|
+
| Routing | ✅ | ✅ | ✅ |
|
|
70
|
+
| i18n (EN/AR + RTL) | - | ✅ | ✅ |
|
|
71
|
+
| Auth UI | - | ✅ | - |
|
|
72
|
+
| HTTP Interceptors | - | ✅ | ✅ |
|
|
73
|
+
| Toast/Modal | - | ✅ | ✅ |
|
|
74
|
+
| SEO Service | - | ✅ | ✅ |
|
|
75
|
+
| PWA | - | ✅ | ✅ |
|
|
76
|
+
| ESLint + Prettier | ✅ | ✅ | ✅ |
|
|
77
|
+
| Sidebar Layout | - | - | ✅ |
|
|
78
|
+
| Charts | - | - | ✅ |
|
|
79
|
+
| Data Tables | - | - | ✅ |
|
|
163
80
|
|
|
164
|
-
|
|
165
|
-
- ⚡ **LightningCSS** - Fast CSS processing
|
|
166
|
-
- 🎨 **Modern CSS Features** - Nesting, variables, and more built-in
|
|
167
|
-
- 🚀 **Simplified Build** - No preprocessors needed
|
|
81
|
+
---
|
|
168
82
|
|
|
169
|
-
|
|
83
|
+
## Project Structure
|
|
170
84
|
|
|
171
|
-
|
|
85
|
+
```
|
|
86
|
+
src/app/
|
|
87
|
+
├── core/ # Services, guards, interceptors, i18n
|
|
88
|
+
├── shared/ # Components, pipes, directives
|
|
89
|
+
├── features/ # Page components (home, about, auth...)
|
|
90
|
+
├── layout/ # Header, footer, sidebar
|
|
91
|
+
├── app.config.ts # App configuration
|
|
92
|
+
├── app.routes.ts # Routing
|
|
93
|
+
└── app.ts # Root component
|
|
94
|
+
```
|
|
172
95
|
|
|
173
|
-
|
|
96
|
+
---
|
|
174
97
|
|
|
175
|
-
|
|
176
|
-
- `primary` - Main brand color (with full 50-950 scale)
|
|
177
|
-
- `secondary` - Secondary brand color (with full 50-950 scale)
|
|
178
|
-
- `accent` - Accent/highlight color (with full 50-950 scale)
|
|
179
|
-
- `success` - Success states (green)
|
|
180
|
-
- `danger` - Error/danger states (red)
|
|
181
|
-
- `warning` - Warning states (yellow/orange)
|
|
182
|
-
- `info` - Informational states (blue)
|
|
98
|
+
## Theming
|
|
183
99
|
|
|
184
|
-
|
|
185
|
-
Simply edit `src/styles.css` and change the color values in the `@theme` block. All components automatically update!
|
|
100
|
+
Edit `src/styles.css` to customize colors:
|
|
186
101
|
|
|
187
102
|
```css
|
|
188
103
|
@theme {
|
|
189
|
-
--color-primary-500: #3b82f6;
|
|
104
|
+
--color-primary-500: #3b82f6;
|
|
190
105
|
--color-secondary-500: #06b6d4;
|
|
191
106
|
--color-accent-500: #a855f7;
|
|
192
|
-
/* ... complete color scales included */
|
|
193
107
|
}
|
|
194
108
|
```
|
|
195
109
|
|
|
196
|
-
|
|
110
|
+
Use in templates:
|
|
111
|
+
|
|
197
112
|
```html
|
|
198
|
-
<
|
|
199
|
-
<div class="bg-secondary-500">Secondary element</div>
|
|
200
|
-
<p class="text-danger-600">Error message</p>
|
|
113
|
+
<button class="bg-primary-500 text-white">Click me</button>
|
|
201
114
|
```
|
|
202
115
|
|
|
203
|
-
|
|
116
|
+
---
|
|
204
117
|
|
|
205
|
-
##
|
|
118
|
+
## CLI Options
|
|
206
119
|
|
|
207
|
-
|
|
120
|
+
```bash
|
|
121
|
+
# Interactive (default)
|
|
122
|
+
npx create-ng-tailwind my-app
|
|
208
123
|
|
|
209
|
-
|
|
124
|
+
# With options
|
|
125
|
+
npx create-ng-tailwind my-app --template=starter --ssr --zoneless
|
|
210
126
|
|
|
211
|
-
|
|
127
|
+
# Available templates
|
|
128
|
+
--template=<minimal|starter|dashboard>
|
|
212
129
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
130
|
+
# Other flags
|
|
131
|
+
--ssr # Enable Server-Side Rendering
|
|
132
|
+
--zoneless # Create zoneless application
|
|
133
|
+
--ai-config # Configure AI tools (Claude, Cursor, etc.)
|
|
134
|
+
```
|
|
216
135
|
|
|
217
136
|
---
|
|
218
137
|
|
|
138
|
+
## Requirements
|
|
139
|
+
|
|
140
|
+
- Node.js 18+
|
|
141
|
+
- npm 9+
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## License
|
|
146
|
+
|
|
147
|
+
MIT
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## Support
|
|
152
|
+
|
|
153
|
+
- Contact: [tehseen_ullah786@hotmail.com](mailto:tehseen_ullah786@hotmail.com)
|
|
154
|
+
|
|
219
155
|
<div align="center">
|
|
220
156
|
|
|
221
157
|
[](https://www.npmjs.com/package/create-ng-tailwind)
|
package/lib/cli/index.js
CHANGED
|
@@ -15,7 +15,7 @@ class CLIHandler {
|
|
|
15
15
|
.description("Create Angular projects with Tailwind CSS")
|
|
16
16
|
.version(require("../../package.json").version)
|
|
17
17
|
.argument("[project-name]", "Project name")
|
|
18
|
-
.option("-t, --template <template>", "Template to use (minimal, starter)")
|
|
18
|
+
.option("-t, --template <template>", "Template to use (minimal, starter, dashboard)")
|
|
19
19
|
.option("--routing", "Enable Angular routing")
|
|
20
20
|
.option("--no-routing", "Disable Angular routing")
|
|
21
21
|
.option(
|
|
@@ -64,15 +64,41 @@ class CLIHandler {
|
|
|
64
64
|
type: "list",
|
|
65
65
|
name: "template",
|
|
66
66
|
message: "Which template would you like to use?",
|
|
67
|
+
pageSize: 10,
|
|
67
68
|
choices: [
|
|
68
69
|
{
|
|
69
|
-
name: "Minimal - Clean Angular + Tailwind CSS (zero features
|
|
70
|
+
name: "Minimal - Clean Angular + Tailwind CSS (zero features)",
|
|
70
71
|
value: "minimal",
|
|
71
72
|
},
|
|
72
73
|
{
|
|
73
|
-
name: "Starter - Professional foundation (routing, i18n, auth
|
|
74
|
+
name: "Starter - Professional foundation (routing, i18n, auth, services)",
|
|
74
75
|
value: "starter",
|
|
75
76
|
},
|
|
77
|
+
{
|
|
78
|
+
name: "Dashboard - Admin panel with sidebar, charts, and analytics",
|
|
79
|
+
value: "dashboard",
|
|
80
|
+
},
|
|
81
|
+
// TODO: Enable these templates in future releases
|
|
82
|
+
// {
|
|
83
|
+
// name: "Ecommerce - Online store with products, cart, and checkout",
|
|
84
|
+
// value: "ecommerce",
|
|
85
|
+
// },
|
|
86
|
+
// {
|
|
87
|
+
// name: "Blog - CMS with posts, categories, tags, and comments",
|
|
88
|
+
// value: "blog",
|
|
89
|
+
// },
|
|
90
|
+
// {
|
|
91
|
+
// name: "SaaS - Pricing, subscriptions, and user dashboard",
|
|
92
|
+
// value: "saas",
|
|
93
|
+
// },
|
|
94
|
+
// {
|
|
95
|
+
// name: "Portfolio - Projects showcase, skills, and resume",
|
|
96
|
+
// value: "portfolio",
|
|
97
|
+
// },
|
|
98
|
+
// {
|
|
99
|
+
// name: "Landing - Marketing page with hero, features, and pricing",
|
|
100
|
+
// value: "landing",
|
|
101
|
+
// },
|
|
76
102
|
],
|
|
77
103
|
default: "starter",
|
|
78
104
|
},
|
package/lib/cli/interactive.js
CHANGED
|
@@ -6,15 +6,40 @@ async function getInteractiveOptions() {
|
|
|
6
6
|
type: "list",
|
|
7
7
|
name: "template",
|
|
8
8
|
message: "Which template would you like to use?",
|
|
9
|
+
pageSize: 10,
|
|
9
10
|
choices: [
|
|
10
11
|
{
|
|
11
12
|
name: "Minimal - Clean Angular + Tailwind CSS (zero features, build your own way)",
|
|
12
13
|
value: "minimal",
|
|
13
14
|
},
|
|
14
15
|
{
|
|
15
|
-
name: "Starter - Professional foundation (routing, i18n, auth UI, components, services
|
|
16
|
+
name: "Starter - Professional foundation (routing, i18n, auth UI, components, services)",
|
|
16
17
|
value: "starter",
|
|
17
18
|
},
|
|
19
|
+
{
|
|
20
|
+
name: "Dashboard - Admin dashboard with sidebar, charts, tables, and analytics",
|
|
21
|
+
value: "dashboard",
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
name: "Ecommerce - Online store with products, cart, checkout, and wishlist",
|
|
25
|
+
value: "ecommerce",
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
name: "Blog - Content management with posts, categories, tags, and comments",
|
|
29
|
+
value: "blog",
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: "SaaS - Pricing page, subscription management, and user dashboard",
|
|
33
|
+
value: "saas",
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: "Portfolio - Projects showcase, skills, experience timeline, and resume",
|
|
37
|
+
value: "portfolio",
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: "Landing - Marketing page with hero, features, testimonials, pricing, FAQ",
|
|
41
|
+
value: "landing",
|
|
42
|
+
},
|
|
18
43
|
],
|
|
19
44
|
default: "starter",
|
|
20
45
|
},
|
|
@@ -2,7 +2,6 @@ const fs = require('fs-extra');
|
|
|
2
2
|
const execa = require('execa');
|
|
3
3
|
const TailwindManager = require('./TailwindManager');
|
|
4
4
|
const TemplateManager = require('./TemplateManager');
|
|
5
|
-
const { createAIConfigs } = require('../utils/ai-config');
|
|
6
5
|
|
|
7
6
|
class ProjectManager {
|
|
8
7
|
constructor(config, logger) {
|
|
@@ -40,9 +39,6 @@ class ProjectManager {
|
|
|
40
39
|
|
|
41
40
|
// Apply template
|
|
42
41
|
await this.templateManager.apply();
|
|
43
|
-
|
|
44
|
-
// Create AI configuration files (CLAUDE.md + tool-specific)
|
|
45
|
-
await createAIConfigs(this.config.fullPath, this.config.projectName, this.config.aiConfig);
|
|
46
42
|
}
|
|
47
43
|
|
|
48
44
|
async createAngularProject() {
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Create Button Component
|
|
6
|
+
*/
|
|
7
|
+
async function createButtonComponent(config) {
|
|
8
|
+
const buttonComponent = `import { Component, Input, Output, EventEmitter } from '@angular/core';
|
|
9
|
+
|
|
10
|
+
export type ButtonVariant = 'primary' | 'secondary' | 'outline' | 'ghost' | 'danger';
|
|
11
|
+
export type ButtonSize = 'sm' | 'md' | 'lg';
|
|
12
|
+
|
|
13
|
+
@Component({
|
|
14
|
+
selector: 'app-button',
|
|
15
|
+
standalone: true,
|
|
16
|
+
imports: [],
|
|
17
|
+
template: \`
|
|
18
|
+
<button
|
|
19
|
+
[class]="buttonClasses"
|
|
20
|
+
[disabled]="disabled || loading"
|
|
21
|
+
(click)="handleClick($event)"
|
|
22
|
+
[type]="type">
|
|
23
|
+
|
|
24
|
+
@if (loading) {
|
|
25
|
+
<svg class="animate-spin -ml-1 mr-3 h-4 w-4" fill="none" viewBox="0 0 24 24">
|
|
26
|
+
<circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" class="opacity-25"></circle>
|
|
27
|
+
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
|
28
|
+
</svg>
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
<ng-content></ng-content>
|
|
32
|
+
</button>
|
|
33
|
+
\`
|
|
34
|
+
})
|
|
35
|
+
export class ButtonComponent {
|
|
36
|
+
@Input() variant: ButtonVariant = 'primary';
|
|
37
|
+
@Input() size: ButtonSize = 'md';
|
|
38
|
+
@Input() disabled = false;
|
|
39
|
+
@Input() loading = false;
|
|
40
|
+
@Input() type: 'button' | 'submit' | 'reset' = 'button';
|
|
41
|
+
@Input() fullWidth = false;
|
|
42
|
+
|
|
43
|
+
@Output() clicked = new EventEmitter<Event>();
|
|
44
|
+
|
|
45
|
+
get buttonClasses(): string {
|
|
46
|
+
const baseClasses = 'inline-flex items-center justify-center font-medium rounded-lg transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50';
|
|
47
|
+
|
|
48
|
+
const sizeClasses = {
|
|
49
|
+
sm: 'text-sm px-3 py-2 h-9',
|
|
50
|
+
md: 'text-sm px-4 py-2 h-10',
|
|
51
|
+
lg: 'text-base px-8 py-3 h-11'
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const variantClasses = {
|
|
55
|
+
primary: 'bg-primary-600 text-white hover:bg-primary-700 focus:ring-primary-500',
|
|
56
|
+
secondary: 'bg-gray-200 text-gray-900 hover:bg-gray-300 focus:ring-gray-400',
|
|
57
|
+
outline: 'border-2 border-primary-600 text-primary-600 hover:bg-primary-600 hover:text-white focus:ring-primary-500',
|
|
58
|
+
ghost: 'hover:bg-gray-100 hover:text-gray-900 focus:ring-gray-400',
|
|
59
|
+
danger: 'bg-danger-600 text-white hover:bg-danger-700 focus:ring-danger-500'
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const widthClass = this.fullWidth ? 'w-full' : '';
|
|
63
|
+
|
|
64
|
+
return \`\${baseClasses} \${sizeClasses[this.size]} \${variantClasses[this.variant]} \${widthClass}\`.trim();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
handleClick(event: Event): void {
|
|
68
|
+
if (!this.disabled && !this.loading) {
|
|
69
|
+
this.clicked.emit(event);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}`;
|
|
73
|
+
|
|
74
|
+
await fs.writeFile(
|
|
75
|
+
path.join(config.fullPath, 'src/app/shared/components/button/button.component.ts'),
|
|
76
|
+
buttonComponent
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Create Card Component
|
|
82
|
+
*/
|
|
83
|
+
async function createCardComponent(config) {
|
|
84
|
+
const cardComponent = `import { Component, Input } from '@angular/core';
|
|
85
|
+
|
|
86
|
+
@Component({
|
|
87
|
+
selector: 'app-card',
|
|
88
|
+
standalone: true,
|
|
89
|
+
imports: [],
|
|
90
|
+
template: \`
|
|
91
|
+
<div [class]="cardClasses">
|
|
92
|
+
@if (title || subtitle) {
|
|
93
|
+
<div class="px-6 py-4 border-b border-gray-200">
|
|
94
|
+
@if (title) {
|
|
95
|
+
<h3 class="text-lg font-semibold text-gray-900">
|
|
96
|
+
{{ title }}
|
|
97
|
+
</h3>
|
|
98
|
+
}
|
|
99
|
+
@if (subtitle) {
|
|
100
|
+
<p class="text-sm text-gray-600 mt-1">
|
|
101
|
+
{{ subtitle }}
|
|
102
|
+
</p>
|
|
103
|
+
}
|
|
104
|
+
</div>
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
<div [class]="contentClasses">
|
|
108
|
+
<ng-content></ng-content>
|
|
109
|
+
</div>
|
|
110
|
+
|
|
111
|
+
@if (hasFooter) {
|
|
112
|
+
<div class="px-6 py-4 border-t border-gray-200 bg-gray-50">
|
|
113
|
+
<ng-content select="[slot=footer]"></ng-content>
|
|
114
|
+
</div>
|
|
115
|
+
}
|
|
116
|
+
</div>
|
|
117
|
+
\`
|
|
118
|
+
})
|
|
119
|
+
export class CardComponent {
|
|
120
|
+
@Input() title?: string;
|
|
121
|
+
@Input() subtitle?: string;
|
|
122
|
+
@Input() padding = true;
|
|
123
|
+
@Input() shadow = true;
|
|
124
|
+
@Input() hover = false;
|
|
125
|
+
@Input() hasFooter = false;
|
|
126
|
+
|
|
127
|
+
get cardClasses(): string {
|
|
128
|
+
const baseClasses = 'bg-white border border-gray-200 rounded-lg overflow-hidden';
|
|
129
|
+
const shadowClasses = this.shadow ? 'shadow-sm' : '';
|
|
130
|
+
const hoverClasses = this.hover ? 'hover:shadow-md transition-shadow duration-200' : '';
|
|
131
|
+
|
|
132
|
+
return \`\${baseClasses} \${shadowClasses} \${hoverClasses}\`.trim();
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
get contentClasses(): string {
|
|
136
|
+
return this.padding ? 'p-6' : '';
|
|
137
|
+
}
|
|
138
|
+
}`;
|
|
139
|
+
|
|
140
|
+
await fs.writeFile(
|
|
141
|
+
path.join(config.fullPath, 'src/app/shared/components/card/card.component.ts'),
|
|
142
|
+
cardComponent
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Create Loading Spinner Component
|
|
148
|
+
*/
|
|
149
|
+
async function createLoadingSpinnerComponent(config) {
|
|
150
|
+
const spinnerComponent = `import { Component, Input } from '@angular/core';
|
|
151
|
+
|
|
152
|
+
export type SpinnerSize = 'sm' | 'md' | 'lg' | 'xl';
|
|
153
|
+
|
|
154
|
+
@Component({
|
|
155
|
+
selector: 'app-loading-spinner',
|
|
156
|
+
standalone: true,
|
|
157
|
+
imports: [],
|
|
158
|
+
template: \`
|
|
159
|
+
<div [class]="containerClasses">
|
|
160
|
+
<svg [class]="spinnerClasses" fill="none" viewBox="0 0 24 24">
|
|
161
|
+
<circle
|
|
162
|
+
cx="12" cy="12" r="10"
|
|
163
|
+
stroke="currentColor"
|
|
164
|
+
stroke-width="4"
|
|
165
|
+
class="opacity-25">
|
|
166
|
+
</circle>
|
|
167
|
+
<path
|
|
168
|
+
class="opacity-75"
|
|
169
|
+
fill="currentColor"
|
|
170
|
+
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z">
|
|
171
|
+
</path>
|
|
172
|
+
</svg>
|
|
173
|
+
@if (text) {
|
|
174
|
+
<span [class]="textClasses">{{ text }}</span>
|
|
175
|
+
}
|
|
176
|
+
</div>
|
|
177
|
+
\`
|
|
178
|
+
})
|
|
179
|
+
export class LoadingSpinnerComponent {
|
|
180
|
+
@Input() size: SpinnerSize = 'md';
|
|
181
|
+
@Input() text?: string;
|
|
182
|
+
@Input() centered = false;
|
|
183
|
+
@Input() color = 'primary';
|
|
184
|
+
|
|
185
|
+
private readonly sizeClasses = {
|
|
186
|
+
sm: 'h-4 w-4',
|
|
187
|
+
md: 'h-8 w-8',
|
|
188
|
+
lg: 'h-12 w-12',
|
|
189
|
+
xl: 'h-16 w-16'
|
|
190
|
+
} as const;
|
|
191
|
+
|
|
192
|
+
private readonly colorClasses = {
|
|
193
|
+
primary: 'text-primary-600',
|
|
194
|
+
secondary: 'text-secondary-600',
|
|
195
|
+
accent: 'text-accent-600',
|
|
196
|
+
success: 'text-success-600',
|
|
197
|
+
danger: 'text-danger-600',
|
|
198
|
+
warning: 'text-warning-600',
|
|
199
|
+
info: 'text-info-600',
|
|
200
|
+
gray: 'text-gray-600',
|
|
201
|
+
white: 'text-white'
|
|
202
|
+
} as const;
|
|
203
|
+
|
|
204
|
+
protected readonly textClasses = 'text-sm text-gray-600';
|
|
205
|
+
|
|
206
|
+
get containerClasses(): string {
|
|
207
|
+
const baseClasses = 'flex items-center';
|
|
208
|
+
const centerClasses = this.centered ? 'justify-center' : '';
|
|
209
|
+
const directionClasses = this.text ? 'flex-col space-y-2' : '';
|
|
210
|
+
|
|
211
|
+
return \`\${baseClasses} \${centerClasses} \${directionClasses}\`.trim();
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
get spinnerClasses(): string {
|
|
215
|
+
const baseClasses = 'animate-spin';
|
|
216
|
+
return \`\${baseClasses} \${this.sizeClasses[this.size]} \${this.colorClasses[this.color as keyof typeof this.colorClasses] || this.colorClasses.primary}\`.trim();
|
|
217
|
+
}
|
|
218
|
+
}`;
|
|
219
|
+
|
|
220
|
+
await fs.writeFile(
|
|
221
|
+
path.join(
|
|
222
|
+
config.fullPath,
|
|
223
|
+
'src/app/shared/components/loading-spinner/loading-spinner.component.ts'
|
|
224
|
+
),
|
|
225
|
+
spinnerComponent
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Create all shared components
|
|
231
|
+
*/
|
|
232
|
+
async function createSharedComponents(config) {
|
|
233
|
+
await createButtonComponent(config);
|
|
234
|
+
await createCardComponent(config);
|
|
235
|
+
await createLoadingSpinnerComponent(config);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
module.exports = {
|
|
239
|
+
createButtonComponent,
|
|
240
|
+
createCardComponent,
|
|
241
|
+
createLoadingSpinnerComponent,
|
|
242
|
+
createSharedComponents,
|
|
243
|
+
};
|