create-template-html-css 1.6.4 → 1.7.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 +50 -0
- package/README.md +80 -13
- package/bin/cli.js +144 -1
- package/package.json +1 -1
- package/src/generator.js +309 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,56 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.7.0] - 2026-02-01
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **Customizable Navigation Items**: Choose exactly which menu items to include when creating navigation components
|
|
12
|
+
- Interactive prompt: "Enter navigation items (comma-separated)"
|
|
13
|
+
- Default: "Home, About, Services, Portfolio, Contact"
|
|
14
|
+
- Automatically generates navigation links and matching page sections
|
|
15
|
+
- Example: "Home, Blog, Shop, About Us, Contact" creates 5 custom navigation items
|
|
16
|
+
|
|
17
|
+
- **Customizable Form Fields**: Select and customize form fields during creation
|
|
18
|
+
- **Standard Fields**: Choose from 6 predefined fields (Name, Email, Phone, Subject, Message, Terms)
|
|
19
|
+
- **Custom Fields**: Add unlimited custom fields with format `type:label`
|
|
20
|
+
- Supports all HTML5 input types: text, email, url, date, number, tel, password, color, etc.
|
|
21
|
+
- Special field types: textarea, checkbox, select
|
|
22
|
+
- Examples:
|
|
23
|
+
- `text:Age` → text input for age
|
|
24
|
+
- `url:Website` → URL input
|
|
25
|
+
- `date:Birth Date` → date picker
|
|
26
|
+
- `number:Quantity` → number input
|
|
27
|
+
- `textarea:Comments` → text area
|
|
28
|
+
- `checkbox:Subscribe` → checkbox field
|
|
29
|
+
- `select:Country` → dropdown menu
|
|
30
|
+
|
|
31
|
+
- **Component Variation Selection**: Choose specific variations for components with multiple styles
|
|
32
|
+
- **Button Component**: Select from 6 button types or choose specific ones
|
|
33
|
+
- Primary, Secondary, Success, Danger, Outlined, Disabled
|
|
34
|
+
- Option: "All Buttons (6 variations)" or "Select Specific Buttons"
|
|
35
|
+
- **Card Component**: Select from 6 card variations or choose specific ones
|
|
36
|
+
- Modern Card (Featured), Premium Card (Price), Blog Card (Tags)
|
|
37
|
+
- Minimal Card, User Profile Card, Interactive Card
|
|
38
|
+
- Option: "All Cards (6 variations)" or "Select Specific Cards"
|
|
39
|
+
- **Spinner Component**: Select from 5 spinner types or choose specific ones
|
|
40
|
+
- Circle Spinner, Bouncing Dots, Pulse Loader, Bar Loader, Gradient Ring
|
|
41
|
+
- Option: "All Spinners (5 types)" or "Select Specific Spinners"
|
|
42
|
+
|
|
43
|
+
### Improved
|
|
44
|
+
- Enhanced CLI prompts with better validation and user guidance
|
|
45
|
+
- More interactive component creation experience
|
|
46
|
+
- Clear format instructions for custom fields
|
|
47
|
+
- Cleaner generated HTML when selecting specific variations
|
|
48
|
+
|
|
49
|
+
### Developer Notes
|
|
50
|
+
- Added `generateNavigationItems()` function to dynamically create nav menus
|
|
51
|
+
- Added `generateFormFields()` function to handle both standard and custom form fields
|
|
52
|
+
- Added `filterButtonVariations()` function to filter button HTML
|
|
53
|
+
- Added `filterCardVariations()` function to filter card HTML
|
|
54
|
+
- Added `filterSpinnerVariations()` function to filter spinner HTML
|
|
55
|
+
- Updated CLI to include conditional prompts based on component type
|
|
56
|
+
- Generator now accepts `navItems`, `formFields`, `customFormFields`, and variation parameters
|
|
57
|
+
|
|
8
58
|
## [1.6.2] - 2026-02-01
|
|
9
59
|
|
|
10
60
|
### Added
|
package/README.md
CHANGED
|
@@ -41,8 +41,35 @@ npm install -g create-template-html-css
|
|
|
41
41
|
- ✨ **Prettier Formatting** - All generated code is automatically formatted with Prettier (v1.6.2+)
|
|
42
42
|
- 📂 **Organized Structure** - CSS and JS files automatically organized in folders (v1.6.2+)
|
|
43
43
|
- 💾 **Simple Backups** - Clean backup naming without timestamps (v1.6.2+)
|
|
44
|
+
- 🎯 **Customizable Components** - Choose navigation items and form fields during creation (v1.7.0+)
|
|
44
45
|
|
|
45
|
-
## 🆕 What's New in v1.
|
|
46
|
+
## 🆕 What's New in v1.7.0
|
|
47
|
+
|
|
48
|
+
### 🎯 Customizable Navigation
|
|
49
|
+
When creating a navigation component, you can now specify exactly which menu items you want:
|
|
50
|
+
- Enter comma-separated items: "Home, Blog, Shop, Contact"
|
|
51
|
+
- Automatically generates matching navigation links and page sections
|
|
52
|
+
- Default: "Home, About, Services, Portfolio, Contact"
|
|
53
|
+
|
|
54
|
+
### 📝 Customizable Form Fields
|
|
55
|
+
Create forms with exactly the fields you need:
|
|
56
|
+
- **Standard Fields**: Select from Name, Email, Phone, Subject, Message, Terms
|
|
57
|
+
- **Custom Fields**: Add any field with format `type:label`
|
|
58
|
+
- Examples: `text:Age`, `url:Website`, `date:Birth Date`, `number:Quantity`
|
|
59
|
+
- Supports all HTML5 input types: text, email, url, date, number, tel, password, color, etc.
|
|
60
|
+
- Also supports: textarea, checkbox, select
|
|
61
|
+
|
|
62
|
+
### 🎨 Component Variation Selection
|
|
63
|
+
Choose exactly which variations you want for multi-style components:
|
|
64
|
+
- **Button**: Select from 6 button types (Primary, Secondary, Success, Danger, Outlined, Disabled)
|
|
65
|
+
- **Card**: Choose from 6 card variations (Modern, Premium, Blog, Minimal, User Profile, Interactive)
|
|
66
|
+
- **Spinner**: Pick from 5 spinner types (Circle, Dots, Pulse, Bars, Gradient)
|
|
67
|
+
- Option to select "All Variations" or choose specific ones
|
|
68
|
+
|
|
69
|
+
### 🚀 Interactive CLI Experience
|
|
70
|
+
Enhanced prompts guide you through component customization with clear instructions and validation.
|
|
71
|
+
|
|
72
|
+
## 🎨 What Was Added in v1.6.2
|
|
46
73
|
|
|
47
74
|
### ✨ Prettier Code Formatting
|
|
48
75
|
All generated and inserted files are automatically formatted with [Prettier](https://prettier.io/) for beautiful, professional-looking code.
|
|
@@ -96,9 +123,12 @@ create-template create
|
|
|
96
123
|
Follow the interactive prompts:
|
|
97
124
|
1. **Select Component Type** - Choose from Button, Card, Form, Navigation, Modal, Footer, or Hero
|
|
98
125
|
2. **Enter Component Name** - Give your component a meaningful name
|
|
99
|
-
3. **
|
|
126
|
+
3. **Customize Component** (v1.7.0+):
|
|
127
|
+
- **Navigation**: Choose menu items (e.g., "Home, Blog, Shop, Contact")
|
|
128
|
+
- **Form**: Select standard fields and add custom fields (e.g., "text:Age, url:Website")
|
|
129
|
+
4. **Include JavaScript** - Decide if you want to include the script file
|
|
100
130
|
|
|
101
|
-
**Example:**
|
|
131
|
+
**Example (Basic):**
|
|
102
132
|
|
|
103
133
|
```bash
|
|
104
134
|
$ create-template create
|
|
@@ -109,6 +139,29 @@ $ create-template create
|
|
|
109
139
|
Location: ./my-awesome-button/
|
|
110
140
|
```
|
|
111
141
|
|
|
142
|
+
**Example (Navigation with Custom Items):**
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
$ create-template create
|
|
146
|
+
? Choose a component type: Navigation
|
|
147
|
+
? Enter a name for your component: my-nav
|
|
148
|
+
? Enter navigation items (comma-separated): Home, Blog, Shop, About Us, Contact
|
|
149
|
+
✓ Template created successfully!
|
|
150
|
+
Location: ./my-nav/
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
**Example (Form with Custom Fields):**
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
$ create-template create
|
|
157
|
+
? Choose a component type: Form
|
|
158
|
+
? Enter a name for your component: contact-form
|
|
159
|
+
? Select form fields to include: Name, Email, Message
|
|
160
|
+
? Add custom fields: text:Age, url:Website, date:Birth Date
|
|
161
|
+
✓ Template created successfully!
|
|
162
|
+
Location: ./contact-form/
|
|
163
|
+
```
|
|
164
|
+
|
|
112
165
|
**Result:**
|
|
113
166
|
```
|
|
114
167
|
my-awesome-button/
|
|
@@ -207,31 +260,45 @@ Responsive card component with 6 professional variations:
|
|
|
207
260
|
|
|
208
261
|
### 3. Form
|
|
209
262
|
|
|
210
|
-
Complete contact form with validation:
|
|
263
|
+
Complete contact form with validation and customizable fields (v1.7.0+):
|
|
211
264
|
|
|
212
|
-
**
|
|
213
|
-
-
|
|
214
|
-
-
|
|
215
|
-
-
|
|
216
|
-
-
|
|
217
|
-
- Textarea
|
|
218
|
-
- Checkboxes
|
|
265
|
+
**Customization Options:**
|
|
266
|
+
- **Standard Fields**: Name, Email, Phone, Subject, Message, Terms Checkbox
|
|
267
|
+
- **Custom Fields**: Add any field with `type:label` format
|
|
268
|
+
- Supported types: text, email, url, date, number, tel, password, color, textarea, checkbox, select
|
|
269
|
+
- Examples: `text:Age`, `url:Website`, `date:Birth Date`, `number:Quantity`
|
|
219
270
|
|
|
220
271
|
**Features:**
|
|
221
272
|
- Real-time validation
|
|
222
273
|
- Focus effects
|
|
223
274
|
- Error messages
|
|
224
275
|
- Modern styling
|
|
276
|
+
- Fully customizable field selection
|
|
225
277
|
|
|
226
278
|
### 4. Navigation
|
|
227
279
|
|
|
228
|
-
Responsive navigation bar
|
|
280
|
+
Responsive navigation bar with customizable menu items (v1.7.0+):
|
|
281
|
+
|
|
282
|
+
**Customization:**
|
|
283
|
+
- Choose exactly which menu items to include
|
|
284
|
+
- Enter comma-separated items: "Home, Blog, Shop, Contact"
|
|
285
|
+
- Automatically generates matching page sections
|
|
286
|
+
- Default items: "Home, About, Services, Portfolio, Contact"
|
|
229
287
|
|
|
230
288
|
**Components:**
|
|
231
289
|
- Logo section
|
|
232
|
-
-
|
|
290
|
+
- Customizable navigation menu
|
|
233
291
|
- Mobile hamburger menu
|
|
234
292
|
- Smooth scrolling
|
|
293
|
+
- Section highlighting on scroll
|
|
294
|
+
|
|
295
|
+
**Features:**
|
|
296
|
+
- Fully customizable menu structure
|
|
297
|
+
- Mobile-responsive design
|
|
298
|
+
- Sticky navigation
|
|
299
|
+
- Active link highlighting
|
|
300
|
+
- Animated hamburger menu
|
|
301
|
+
- Smooth scroll to sections
|
|
235
302
|
|
|
236
303
|
**Features:**
|
|
237
304
|
- Fixed header option
|
package/bin/cli.js
CHANGED
|
@@ -13,7 +13,7 @@ program
|
|
|
13
13
|
.description(
|
|
14
14
|
chalk.cyan("🎨 Create HTML/CSS UI component templates in seconds"),
|
|
15
15
|
)
|
|
16
|
-
.version("1.
|
|
16
|
+
.version("1.7.0");
|
|
17
17
|
|
|
18
18
|
// Add intro message
|
|
19
19
|
program.on("--help", () => {
|
|
@@ -166,6 +166,149 @@ program
|
|
|
166
166
|
return true;
|
|
167
167
|
},
|
|
168
168
|
},
|
|
169
|
+
{
|
|
170
|
+
type: "input",
|
|
171
|
+
name: "navItems",
|
|
172
|
+
message: "Enter navigation items (comma-separated):",
|
|
173
|
+
default: "Home, About, Services, Portfolio, Contact",
|
|
174
|
+
when: (answers) => answers.component === "navigation",
|
|
175
|
+
validate: (input) => {
|
|
176
|
+
if (!input || input.trim().length === 0) {
|
|
177
|
+
return "Please enter at least one navigation item";
|
|
178
|
+
}
|
|
179
|
+
return true;
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
type: "checkbox",
|
|
184
|
+
name: "formFields",
|
|
185
|
+
message: "Select form fields to include:",
|
|
186
|
+
choices: [
|
|
187
|
+
{ name: "Name", value: "name", checked: true },
|
|
188
|
+
{ name: "Email", value: "email", checked: true },
|
|
189
|
+
{ name: "Phone", value: "phone", checked: false },
|
|
190
|
+
{ name: "Subject", value: "subject", checked: false },
|
|
191
|
+
{ name: "Message", value: "message", checked: true },
|
|
192
|
+
{ name: "Terms Checkbox", value: "terms", checked: false },
|
|
193
|
+
],
|
|
194
|
+
when: (answers) => answers.component === "form",
|
|
195
|
+
validate: (input) => {
|
|
196
|
+
if (input.length === 0) {
|
|
197
|
+
return "Please select at least one form field";
|
|
198
|
+
}
|
|
199
|
+
return true;
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
type: "list",
|
|
204
|
+
name: "buttonVariations",
|
|
205
|
+
message: "Which button variations do you want?",
|
|
206
|
+
choices: [
|
|
207
|
+
{ name: "All Buttons (6 variations)", value: "all" },
|
|
208
|
+
{ name: "Select Specific Buttons", value: "select" },
|
|
209
|
+
],
|
|
210
|
+
when: (answers) => answers.component === "button",
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
type: "checkbox",
|
|
214
|
+
name: "selectedButtons",
|
|
215
|
+
message: "Select button variations:",
|
|
216
|
+
choices: [
|
|
217
|
+
{ name: "Primary Button", value: "primary", checked: true },
|
|
218
|
+
{ name: "Secondary Button", value: "secondary", checked: true },
|
|
219
|
+
{ name: "Success Button", value: "success", checked: true },
|
|
220
|
+
{ name: "Danger Button", value: "danger", checked: true },
|
|
221
|
+
{ name: "Outlined Button", value: "outlined", checked: true },
|
|
222
|
+
{ name: "Disabled Button", value: "disabled", checked: true },
|
|
223
|
+
],
|
|
224
|
+
when: (answers) => answers.component === "button" && answers.buttonVariations === "select",
|
|
225
|
+
validate: (input) => {
|
|
226
|
+
if (input.length === 0) {
|
|
227
|
+
return "Please select at least one button variation";
|
|
228
|
+
}
|
|
229
|
+
return true;
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
type: "list",
|
|
234
|
+
name: "cardVariations",
|
|
235
|
+
message: "Which card variations do you want?",
|
|
236
|
+
choices: [
|
|
237
|
+
{ name: "All Cards (6 variations)", value: "all" },
|
|
238
|
+
{ name: "Select Specific Cards", value: "select" },
|
|
239
|
+
],
|
|
240
|
+
when: (answers) => answers.component === "card",
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
type: "checkbox",
|
|
244
|
+
name: "selectedCards",
|
|
245
|
+
message: "Select card variations:",
|
|
246
|
+
choices: [
|
|
247
|
+
{ name: "Modern Card (Featured)", value: "modern", checked: true },
|
|
248
|
+
{ name: "Premium Card (Price)", value: "premium", checked: true },
|
|
249
|
+
{ name: "Blog Card (Tags)", value: "blog", checked: true },
|
|
250
|
+
{ name: "Minimal Card", value: "minimal", checked: true },
|
|
251
|
+
{ name: "User Profile Card", value: "user", checked: true },
|
|
252
|
+
{ name: "Interactive Card", value: "interactive", checked: true },
|
|
253
|
+
],
|
|
254
|
+
when: (answers) => answers.component === "card" && answers.cardVariations === "select",
|
|
255
|
+
validate: (input) => {
|
|
256
|
+
if (input.length === 0) {
|
|
257
|
+
return "Please select at least one card variation";
|
|
258
|
+
}
|
|
259
|
+
return true;
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
type: "list",
|
|
264
|
+
name: "spinnerVariations",
|
|
265
|
+
message: "Which spinner variations do you want?",
|
|
266
|
+
choices: [
|
|
267
|
+
{ name: "All Spinners (5 types)", value: "all" },
|
|
268
|
+
{ name: "Select Specific Spinners", value: "select" },
|
|
269
|
+
],
|
|
270
|
+
when: (answers) => answers.component === "spinner",
|
|
271
|
+
},
|
|
272
|
+
{
|
|
273
|
+
type: "checkbox",
|
|
274
|
+
name: "selectedSpinners",
|
|
275
|
+
message: "Select spinner types:",
|
|
276
|
+
choices: [
|
|
277
|
+
{ name: "Circle Spinner", value: "circle", checked: true },
|
|
278
|
+
{ name: "Bouncing Dots", value: "dots", checked: true },
|
|
279
|
+
{ name: "Pulse Loader", value: "pulse", checked: true },
|
|
280
|
+
{ name: "Bar Loader", value: "bars", checked: true },
|
|
281
|
+
{ name: "Gradient Ring", value: "gradient", checked: true },
|
|
282
|
+
],
|
|
283
|
+
when: (answers) => answers.component === "spinner" && answers.spinnerVariations === "select",
|
|
284
|
+
validate: (input) => {
|
|
285
|
+
if (input.length === 0) {
|
|
286
|
+
return "Please select at least one spinner type";
|
|
287
|
+
}
|
|
288
|
+
return true;
|
|
289
|
+
},
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
type: "input",
|
|
293
|
+
name: "customFormFields",
|
|
294
|
+
message: "Add custom fields (format: type:label, e.g., 'text:Age, url:Website'):",
|
|
295
|
+
when: (answers) => answers.component === "form",
|
|
296
|
+
default: "",
|
|
297
|
+
validate: (input) => {
|
|
298
|
+
if (!input || input.trim().length === 0) {
|
|
299
|
+
return true; // Optional field
|
|
300
|
+
}
|
|
301
|
+
// Basic validation for format
|
|
302
|
+
const fields = input.split(',');
|
|
303
|
+
for (const field of fields) {
|
|
304
|
+
const trimmed = field.trim();
|
|
305
|
+
if (trimmed && !trimmed.includes(':')) {
|
|
306
|
+
return "Invalid format. Use 'type:label' (e.g., 'text:Age, url:Website')";
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
return true;
|
|
310
|
+
},
|
|
311
|
+
},
|
|
169
312
|
]);
|
|
170
313
|
|
|
171
314
|
// Always include JavaScript file
|
package/package.json
CHANGED
package/src/generator.js
CHANGED
|
@@ -81,8 +81,291 @@ function sanitizeFilename(filename) {
|
|
|
81
81
|
return sanitized.replace(/[<>:"|?*]/g, "").trim();
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
+
/**
|
|
85
|
+
* Filter button variations based on user selection
|
|
86
|
+
*/
|
|
87
|
+
function filterButtonVariations(htmlContent, buttonVariations, selectedButtons) {
|
|
88
|
+
if (buttonVariations === "all" || !selectedButtons || selectedButtons.length === 0) {
|
|
89
|
+
return htmlContent; // Return all buttons
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Map selection to button classes
|
|
93
|
+
const buttonMap = {
|
|
94
|
+
primary: "btn-primary",
|
|
95
|
+
secondary: "btn-secondary",
|
|
96
|
+
success: "btn-success",
|
|
97
|
+
danger: "btn-danger",
|
|
98
|
+
outlined: "btn-outlined",
|
|
99
|
+
disabled: "disabled"
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
let filteredHtml = htmlContent;
|
|
103
|
+
|
|
104
|
+
// Remove buttons that weren't selected
|
|
105
|
+
for (const [key, className] of Object.entries(buttonMap)) {
|
|
106
|
+
if (!selectedButtons.includes(key)) {
|
|
107
|
+
// Remove button and its comment
|
|
108
|
+
const patterns = [
|
|
109
|
+
new RegExp(`\\s*<!-- ${key.charAt(0).toUpperCase() + key.slice(1)} Button -->\\s*\\n\\s*<button[^>]*${className}[^>]*>.*?<\\/button>\\s*\\n`, 'gis'),
|
|
110
|
+
new RegExp(`\\s*<button[^>]*${className}[^>]*>.*?<\\/button>\\s*\\n`, 'gis')
|
|
111
|
+
];
|
|
112
|
+
|
|
113
|
+
patterns.forEach(pattern => {
|
|
114
|
+
filteredHtml = filteredHtml.replace(pattern, '\n ');
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return filteredHtml;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Filter card variations based on user selection
|
|
124
|
+
*/
|
|
125
|
+
function filterCardVariations(htmlContent, cardVariations, selectedCards) {
|
|
126
|
+
if (cardVariations === "all" || !selectedCards || selectedCards.length === 0) {
|
|
127
|
+
return htmlContent; // Return all cards
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const cardComments = {
|
|
131
|
+
modern: "Card 1 - Basic",
|
|
132
|
+
premium: "Card 2 - With Price",
|
|
133
|
+
blog: "Card 3 - With Tags",
|
|
134
|
+
minimal: "Card 4 - Minimal",
|
|
135
|
+
user: "Card 5 - With Avatar",
|
|
136
|
+
interactive: "Card 6 - Interactive"
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
let filteredHtml = htmlContent;
|
|
140
|
+
|
|
141
|
+
// Remove cards that weren't selected
|
|
142
|
+
for (const [key, comment] of Object.entries(cardComments)) {
|
|
143
|
+
if (!selectedCards.includes(key)) {
|
|
144
|
+
// Remove entire card div including comment
|
|
145
|
+
const pattern = new RegExp(`\\s*<!-- ${comment.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')} -->\\s*\\n\\s*<div class="card[^>]*>.*?<\\/div>\\s*\\n\\s*<\\/div>\\s*\\n`, 'gis');
|
|
146
|
+
filteredHtml = filteredHtml.replace(pattern, '\n ');
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return filteredHtml;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Filter spinner variations based on user selection
|
|
155
|
+
*/
|
|
156
|
+
function filterSpinnerVariations(htmlContent, spinnerVariations, selectedSpinners) {
|
|
157
|
+
if (spinnerVariations === "all" || !selectedSpinners || selectedSpinners.length === 0) {
|
|
158
|
+
return htmlContent; // Return all spinners
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const spinnerTypes = {
|
|
162
|
+
circle: "Circle Spinner",
|
|
163
|
+
dots: "Bouncing Dots",
|
|
164
|
+
pulse: "Pulse Loader",
|
|
165
|
+
bars: "Bar Loader",
|
|
166
|
+
gradient: "Gradient Ring"
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
let filteredHtml = htmlContent;
|
|
170
|
+
|
|
171
|
+
// Remove spinners that weren't selected
|
|
172
|
+
for (const [key, title] of Object.entries(spinnerTypes)) {
|
|
173
|
+
if (!selectedSpinners.includes(key)) {
|
|
174
|
+
// Remove entire spinner-group div
|
|
175
|
+
const pattern = new RegExp(`\\s*<!-- ${title} Spinner -->\\s*\\n\\s*<div class="spinner-group">.*?<\\/div>\\s*\\n\\s*<\\/div>\\s*\\n`, 'gis');
|
|
176
|
+
filteredHtml = filteredHtml.replace(pattern, '\n ');
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return filteredHtml;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Generate custom navigation items based on user input
|
|
185
|
+
*/
|
|
186
|
+
function generateNavigationItems(htmlContent, navItems) {
|
|
187
|
+
// Parse the comma-separated navigation items
|
|
188
|
+
const items = navItems.split(',').map(item => item.trim()).filter(item => item.length > 0);
|
|
189
|
+
|
|
190
|
+
// Generate navigation HTML
|
|
191
|
+
let navHtml = '';
|
|
192
|
+
items.forEach((item, index) => {
|
|
193
|
+
const itemId = item.toLowerCase().replace(/\s+/g, '-');
|
|
194
|
+
const activeClass = index === 0 ? ' active' : '';
|
|
195
|
+
navHtml += ` <li class="nav-item">
|
|
196
|
+
<a href="#${itemId}" class="nav-link${activeClass}">${item}</a>
|
|
197
|
+
</li>\n`;
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// Replace the navigation items in the HTML
|
|
201
|
+
const navMenuRegex = /<ul class="nav-menu"[^>]*>[\s\S]*?<\/ul>/;
|
|
202
|
+
const replacement = `<ul class="nav-menu" id="navMenu">
|
|
203
|
+
${navHtml} </ul>`;
|
|
204
|
+
|
|
205
|
+
htmlContent = htmlContent.replace(navMenuRegex, replacement);
|
|
206
|
+
|
|
207
|
+
// Generate sections for each navigation item
|
|
208
|
+
let sectionsHtml = '';
|
|
209
|
+
items.forEach(item => {
|
|
210
|
+
const itemId = item.toLowerCase().replace(/\s+/g, '-');
|
|
211
|
+
sectionsHtml += ` <section id="${itemId}" class="section">
|
|
212
|
+
<h1>${item}</h1>
|
|
213
|
+
<p>Content for ${item} section</p>
|
|
214
|
+
</section>
|
|
215
|
+
|
|
216
|
+
`;
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
// Replace the sections in the HTML
|
|
220
|
+
const mainContentRegex = /<main class="main-content">[\s\S]*?<\/main>/;
|
|
221
|
+
const sectionsReplacement = `<main class="main-content">
|
|
222
|
+
${sectionsHtml} </main>`;
|
|
223
|
+
|
|
224
|
+
htmlContent = htmlContent.replace(mainContentRegex, sectionsReplacement);
|
|
225
|
+
|
|
226
|
+
return htmlContent;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Generate custom form fields based on user selection
|
|
231
|
+
*/
|
|
232
|
+
function generateFormFields(htmlContent, formFields, customFormFields) {
|
|
233
|
+
let fieldsHtml = '';
|
|
234
|
+
|
|
235
|
+
// Add standard fields
|
|
236
|
+
formFields.forEach(field => {
|
|
237
|
+
switch(field) {
|
|
238
|
+
case 'name':
|
|
239
|
+
fieldsHtml += ` <div class="form-group">
|
|
240
|
+
<label for="name">Full Name</label>
|
|
241
|
+
<input type="text" id="name" name="name" required>
|
|
242
|
+
</div>
|
|
243
|
+
|
|
244
|
+
`;
|
|
245
|
+
break;
|
|
246
|
+
case 'email':
|
|
247
|
+
fieldsHtml += ` <div class="form-group">
|
|
248
|
+
<label for="email">Email</label>
|
|
249
|
+
<input type="email" id="email" name="email" required>
|
|
250
|
+
</div>
|
|
251
|
+
|
|
252
|
+
`;
|
|
253
|
+
break;
|
|
254
|
+
case 'phone':
|
|
255
|
+
fieldsHtml += ` <div class="form-group">
|
|
256
|
+
<label for="phone">Phone</label>
|
|
257
|
+
<input type="tel" id="phone" name="phone">
|
|
258
|
+
</div>
|
|
259
|
+
|
|
260
|
+
`;
|
|
261
|
+
break;
|
|
262
|
+
case 'subject':
|
|
263
|
+
fieldsHtml += ` <div class="form-group">
|
|
264
|
+
<label for="subject">Subject</label>
|
|
265
|
+
<select id="subject" name="subject" required>
|
|
266
|
+
<option value="">Select a subject</option>
|
|
267
|
+
<option value="support">Technical Support</option>
|
|
268
|
+
<option value="sales">Sales</option>
|
|
269
|
+
<option value="general">General</option>
|
|
270
|
+
</select>
|
|
271
|
+
</div>
|
|
272
|
+
|
|
273
|
+
`;
|
|
274
|
+
break;
|
|
275
|
+
case 'message':
|
|
276
|
+
fieldsHtml += ` <div class="form-group">
|
|
277
|
+
<label for="message">Message</label>
|
|
278
|
+
<textarea id="message" name="message" rows="5" required></textarea>
|
|
279
|
+
</div>
|
|
280
|
+
|
|
281
|
+
`;
|
|
282
|
+
break;
|
|
283
|
+
case 'terms':
|
|
284
|
+
fieldsHtml += ` <div class="form-group checkbox-group">
|
|
285
|
+
<input type="checkbox" id="terms" name="terms" required>
|
|
286
|
+
<label for="terms">I agree to the terms of service</label>
|
|
287
|
+
</div>
|
|
288
|
+
|
|
289
|
+
`;
|
|
290
|
+
break;
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
// Add custom fields if provided
|
|
295
|
+
if (customFormFields && customFormFields.trim().length > 0) {
|
|
296
|
+
const customFields = customFormFields.split(',').map(f => f.trim()).filter(f => f.length > 0);
|
|
297
|
+
|
|
298
|
+
customFields.forEach(field => {
|
|
299
|
+
const [type, label] = field.split(':').map(s => s.trim());
|
|
300
|
+
if (type && label) {
|
|
301
|
+
const fieldId = label.toLowerCase().replace(/\s+/g, '-');
|
|
302
|
+
|
|
303
|
+
if (type === 'textarea') {
|
|
304
|
+
fieldsHtml += ` <div class="form-group">
|
|
305
|
+
<label for="${fieldId}">${label}</label>
|
|
306
|
+
<textarea id="${fieldId}" name="${fieldId}" rows="5"></textarea>
|
|
307
|
+
</div>
|
|
308
|
+
|
|
309
|
+
`;
|
|
310
|
+
} else if (type === 'checkbox') {
|
|
311
|
+
fieldsHtml += ` <div class="form-group checkbox-group">
|
|
312
|
+
<input type="checkbox" id="${fieldId}" name="${fieldId}">
|
|
313
|
+
<label for="${fieldId}">${label}</label>
|
|
314
|
+
</div>
|
|
315
|
+
|
|
316
|
+
`;
|
|
317
|
+
} else if (type === 'select') {
|
|
318
|
+
fieldsHtml += ` <div class="form-group">
|
|
319
|
+
<label for="${fieldId}">${label}</label>
|
|
320
|
+
<select id="${fieldId}" name="${fieldId}">
|
|
321
|
+
<option value="">Select an option</option>
|
|
322
|
+
<option value="option1">Option 1</option>
|
|
323
|
+
<option value="option2">Option 2</option>
|
|
324
|
+
</select>
|
|
325
|
+
</div>
|
|
326
|
+
|
|
327
|
+
`;
|
|
328
|
+
} else {
|
|
329
|
+
// Default to input with specified type
|
|
330
|
+
fieldsHtml += ` <div class="form-group">
|
|
331
|
+
<label for="${fieldId}">${label}</label>
|
|
332
|
+
<input type="${type}" id="${fieldId}" name="${fieldId}">
|
|
333
|
+
</div>
|
|
334
|
+
|
|
335
|
+
`;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// Replace the form fields in the HTML
|
|
342
|
+
const formRegex = /<form class="form"[^>]*>[\s\S]*?<button type="submit"/;
|
|
343
|
+
const replacement = `<form class="form" id="contactForm">
|
|
344
|
+
<h1 class="form-title">Contact Us</h1>
|
|
345
|
+
<p class="form-subtitle">Fill in the details and we'll get back to you soon</p>
|
|
346
|
+
|
|
347
|
+
${fieldsHtml} <button type="submit"`;
|
|
348
|
+
|
|
349
|
+
htmlContent = htmlContent.replace(formRegex, replacement);
|
|
350
|
+
|
|
351
|
+
return htmlContent;
|
|
352
|
+
}
|
|
353
|
+
|
|
84
354
|
async function generateTemplate(options) {
|
|
85
|
-
const {
|
|
355
|
+
const {
|
|
356
|
+
component,
|
|
357
|
+
name,
|
|
358
|
+
includeJs,
|
|
359
|
+
navItems,
|
|
360
|
+
formFields,
|
|
361
|
+
customFormFields,
|
|
362
|
+
buttonVariations,
|
|
363
|
+
selectedButtons,
|
|
364
|
+
cardVariations,
|
|
365
|
+
selectedCards,
|
|
366
|
+
spinnerVariations,
|
|
367
|
+
selectedSpinners
|
|
368
|
+
} = options;
|
|
86
369
|
|
|
87
370
|
// Security: Validate component name
|
|
88
371
|
if (!VALID_COMPONENTS.includes(component)) {
|
|
@@ -117,6 +400,31 @@ async function generateTemplate(options) {
|
|
|
117
400
|
|
|
118
401
|
// Replace placeholder name
|
|
119
402
|
htmlContent = htmlContent.replace(/{{name}}/g, safeName);
|
|
403
|
+
|
|
404
|
+
// Filter button variations
|
|
405
|
+
if (component === "button") {
|
|
406
|
+
htmlContent = filterButtonVariations(htmlContent, buttonVariations, selectedButtons);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// Filter card variations
|
|
410
|
+
if (component === "card") {
|
|
411
|
+
htmlContent = filterCardVariations(htmlContent, cardVariations, selectedCards);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// Filter spinner variations
|
|
415
|
+
if (component === "spinner") {
|
|
416
|
+
htmlContent = filterSpinnerVariations(htmlContent, spinnerVariations, selectedSpinners);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// Custom navigation items
|
|
420
|
+
if (component === "navigation" && navItems) {
|
|
421
|
+
htmlContent = generateNavigationItems(htmlContent, navItems);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// Custom form fields
|
|
425
|
+
if (component === "form" && formFields) {
|
|
426
|
+
htmlContent = generateFormFields(htmlContent, formFields, customFormFields);
|
|
427
|
+
}
|
|
120
428
|
|
|
121
429
|
// Add script tag if JavaScript is included (pointing to js/ folder)
|
|
122
430
|
if (includeJs) {
|