create-template-html-css 2.0.4 → 2.1.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 (66) hide show
  1. package/CHANGELOG.md +305 -0
  2. package/HTML-VS-REACT.md +289 -0
  3. package/QUICKSTART-REACT.md +293 -0
  4. package/REACT-SUPPORT-SUMMARY.md +235 -0
  5. package/README.md +193 -12
  6. package/bin/cli.js +98 -759
  7. package/bin/commands/create.js +272 -0
  8. package/bin/commands/gallery.js +42 -0
  9. package/bin/commands/insert.js +123 -0
  10. package/bin/commands/list.js +73 -0
  11. package/package.json +10 -3
  12. package/src/component-choices.js +7 -0
  13. package/src/components-registry.js +112 -0
  14. package/src/format-utils.js +49 -0
  15. package/src/generator.js +83 -594
  16. package/src/generators/color-schemes.js +78 -0
  17. package/src/generators/color-utils.js +108 -0
  18. package/src/generators/component-filters.js +151 -0
  19. package/src/generators/html-generators.js +180 -0
  20. package/src/generators/validation.js +43 -0
  21. package/src/index.js +2 -1
  22. package/src/inserter.js +55 -233
  23. package/src/inserters/backup-utils.js +20 -0
  24. package/src/inserters/component-loader.js +68 -0
  25. package/src/inserters/html-utils.js +31 -0
  26. package/src/inserters/indentation-utils.js +90 -0
  27. package/src/inserters/validation-utils.js +49 -0
  28. package/src/react-component-choices.js +45 -0
  29. package/src/react-file-operations.js +172 -0
  30. package/src/react-generator.js +208 -0
  31. package/src/react-templates.js +350 -0
  32. package/src/utils/file-utils.js +97 -0
  33. package/src/utils/path-utils.js +32 -0
  34. package/src/utils/string-utils.js +51 -0
  35. package/src/utils/template-loader.js +91 -0
  36. package/templates/_shared/PATTERNS.md +246 -0
  37. package/templates/_shared/README.md +74 -0
  38. package/templates/_shared/base.css +18 -0
  39. package/templates/blackjack/index.html +1 -1
  40. package/templates/breakout/index.html +1 -1
  41. package/templates/connect-four/index.html +1 -1
  42. package/templates/dice-game/index.html +1 -1
  43. package/templates/flappy-bird/index.html +1 -1
  44. package/templates/pong/index.html +1 -1
  45. package/templates/skeleton/index.html +4 -4
  46. package/templates/slot-machine/index.html +1 -1
  47. package/templates/tetris/index.html +1 -1
  48. package/templates-react/README.md +126 -0
  49. package/templates-react/button/Button.css +88 -0
  50. package/templates-react/button/Button.example.jsx +40 -0
  51. package/templates-react/button/Button.jsx +29 -0
  52. package/templates-react/card/Card.css +86 -0
  53. package/templates-react/card/Card.example.jsx +49 -0
  54. package/templates-react/card/Card.jsx +35 -0
  55. package/templates-react/counter/Counter.css +99 -0
  56. package/templates-react/counter/Counter.example.jsx +45 -0
  57. package/templates-react/counter/Counter.jsx +70 -0
  58. package/templates-react/form/Form.css +128 -0
  59. package/templates-react/form/Form.example.jsx +65 -0
  60. package/templates-react/form/Form.jsx +125 -0
  61. package/templates-react/modal/Modal.css +152 -0
  62. package/templates-react/modal/Modal.example.jsx +90 -0
  63. package/templates-react/modal/Modal.jsx +46 -0
  64. package/templates-react/todo-list/TodoList.css +236 -0
  65. package/templates-react/todo-list/TodoList.example.jsx +15 -0
  66. package/templates-react/todo-list/TodoList.jsx +84 -0
@@ -0,0 +1,125 @@
1
+ import React, { useState } from 'react';
2
+ import './Form.css';
3
+
4
+ /**
5
+ * Form Component
6
+ * A flexible form component with validation
7
+ */
8
+ const Form = ({
9
+ title = 'Form',
10
+ fields = [],
11
+ onSubmit,
12
+ submitButtonText = 'Submit'
13
+ }) => {
14
+ const [formData, setFormData] = useState(() => {
15
+ const initialData = {};
16
+ fields.forEach(field => {
17
+ initialData[field.name] = field.defaultValue || '';
18
+ });
19
+ return initialData;
20
+ });
21
+
22
+ const [errors, setErrors] = useState({});
23
+
24
+ const handleChange = (e) => {
25
+ const { name, value, type, checked } = e.target;
26
+ setFormData(prev => ({
27
+ ...prev,
28
+ [name]: type === 'checkbox' ? checked : value
29
+ }));
30
+
31
+ // Clear error when user starts typing
32
+ if (errors[name]) {
33
+ setErrors(prev => ({ ...prev, [name]: '' }));
34
+ }
35
+ };
36
+
37
+ const validateField = (field, value) => {
38
+ if (field.required && !value) {
39
+ return `${field.label} is required`;
40
+ }
41
+
42
+ if (field.pattern && value && !field.pattern.test(value)) {
43
+ return field.errorMessage || `Invalid ${field.label.toLowerCase()}`;
44
+ }
45
+
46
+ if (field.minLength && value.length < field.minLength) {
47
+ return `${field.label} must be at least ${field.minLength} characters`;
48
+ }
49
+
50
+ return '';
51
+ };
52
+
53
+ const handleSubmit = (e) => {
54
+ e.preventDefault();
55
+
56
+ const newErrors = {};
57
+ fields.forEach(field => {
58
+ const error = validateField(field, formData[field.name]);
59
+ if (error) {
60
+ newErrors[field.name] = error;
61
+ }
62
+ });
63
+
64
+ if (Object.keys(newErrors).length > 0) {
65
+ setErrors(newErrors);
66
+ return;
67
+ }
68
+
69
+ onSubmit?.(formData);
70
+ };
71
+
72
+ const renderField = (field) => {
73
+ const commonProps = {
74
+ id: field.name,
75
+ name: field.name,
76
+ value: formData[field.name] || '',
77
+ onChange: handleChange,
78
+ required: field.required,
79
+ placeholder: field.placeholder
80
+ };
81
+
82
+ switch (field.type) {
83
+ case 'textarea':
84
+ return <textarea {...commonProps} rows={field.rows || 4} />;
85
+ case 'select':
86
+ return (
87
+ <select {...commonProps}>
88
+ <option value="">Select...</option>
89
+ {field.options?.map(option => (
90
+ <option key={option.value} value={option.value}>
91
+ {option.label}
92
+ </option>
93
+ ))}
94
+ </select>
95
+ );
96
+ default:
97
+ return <input type={field.type || 'text'} {...commonProps} />;
98
+ }
99
+ };
100
+
101
+ return (
102
+ <div className="form-container">
103
+ <h2 className="form-title">{title}</h2>
104
+ <form onSubmit={handleSubmit} className="form">
105
+ {fields.map(field => (
106
+ <div key={field.name} className="form-group">
107
+ <label htmlFor={field.name} className="form-label">
108
+ {field.label}
109
+ {field.required && <span className="required">*</span>}
110
+ </label>
111
+ {renderField(field)}
112
+ {errors[field.name] && (
113
+ <span className="form-error">{errors[field.name]}</span>
114
+ )}
115
+ </div>
116
+ ))}
117
+ <button type="submit" className="form-submit">
118
+ {submitButtonText}
119
+ </button>
120
+ </form>
121
+ </div>
122
+ );
123
+ };
124
+
125
+ export default Form;
@@ -0,0 +1,152 @@
1
+ /* Modal Styles */
2
+ .modal-overlay {
3
+ position: fixed;
4
+ top: 0;
5
+ left: 0;
6
+ right: 0;
7
+ bottom: 0;
8
+ background: rgba(0, 0, 0, 0.6);
9
+ display: flex;
10
+ align-items: center;
11
+ justify-content: center;
12
+ z-index: 1000;
13
+ animation: fadeIn 0.3s ease;
14
+ backdrop-filter: blur(4px);
15
+ }
16
+
17
+ @keyframes fadeIn {
18
+ from {
19
+ opacity: 0;
20
+ }
21
+ to {
22
+ opacity: 1;
23
+ }
24
+ }
25
+
26
+ .modal-content {
27
+ background: white;
28
+ border-radius: 16px;
29
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
30
+ max-height: 90vh;
31
+ display: flex;
32
+ flex-direction: column;
33
+ animation: slideUp 0.3s ease;
34
+ overflow: hidden;
35
+ }
36
+
37
+ @keyframes slideUp {
38
+ from {
39
+ transform: translateY(30px);
40
+ opacity: 0;
41
+ }
42
+ to {
43
+ transform: translateY(0);
44
+ opacity: 1;
45
+ }
46
+ }
47
+
48
+ .modal-small {
49
+ width: 90%;
50
+ max-width: 400px;
51
+ }
52
+
53
+ .modal-medium {
54
+ width: 90%;
55
+ max-width: 600px;
56
+ }
57
+
58
+ .modal-large {
59
+ width: 90%;
60
+ max-width: 900px;
61
+ }
62
+
63
+ .modal-header {
64
+ padding: 24px 32px;
65
+ border-bottom: 1px solid #e2e8f0;
66
+ display: flex;
67
+ align-items: center;
68
+ justify-content: space-between;
69
+ background: linear-gradient(135deg, {{primaryColor}}15 0%, {{secondaryColor}}15 100%);
70
+ }
71
+
72
+ .modal-title {
73
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
74
+ font-size: 24px;
75
+ font-weight: 700;
76
+ margin: 0;
77
+ background: linear-gradient(135deg, {{primaryColor}} 0%, {{secondaryColor}} 100%);
78
+ -webkit-background-clip: text;
79
+ -webkit-text-fill-color: transparent;
80
+ background-clip: text;
81
+ }
82
+
83
+ .modal-close {
84
+ font-size: 36px;
85
+ font-weight: 300;
86
+ background: none;
87
+ border: none;
88
+ cursor: pointer;
89
+ color: #718096;
90
+ padding: 0;
91
+ width: 36px;
92
+ height: 36px;
93
+ display: flex;
94
+ align-items: center;
95
+ justify-content: center;
96
+ border-radius: 50%;
97
+ transition: all 0.2s ease;
98
+ line-height: 1;
99
+ }
100
+
101
+ .modal-close:hover {
102
+ background: #f7fafc;
103
+ color: #2d3748;
104
+ }
105
+
106
+ .modal-body {
107
+ padding: 32px;
108
+ overflow-y: auto;
109
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
110
+ font-size: 16px;
111
+ line-height: 1.6;
112
+ color: #2d3748;
113
+ }
114
+
115
+ .modal-footer {
116
+ padding: 20px 32px;
117
+ border-top: 1px solid #e2e8f0;
118
+ display: flex;
119
+ gap: 12px;
120
+ justify-content: flex-end;
121
+ background: #f7fafc;
122
+ }
123
+
124
+ /* Dark Mode Support */
125
+ @media (prefers-color-scheme: dark) {
126
+ .modal-content {
127
+ background: #2d3748;
128
+ }
129
+
130
+ .modal-header {
131
+ border-bottom-color: #4a5568;
132
+ background: linear-gradient(135deg, {{primaryColor}}20 0%, {{secondaryColor}}20 100%);
133
+ }
134
+
135
+ .modal-body {
136
+ color: #e2e8f0;
137
+ }
138
+
139
+ .modal-footer {
140
+ background: #1a202c;
141
+ border-top-color: #4a5568;
142
+ }
143
+
144
+ .modal-close {
145
+ color: #cbd5e0;
146
+ }
147
+
148
+ .modal-close:hover {
149
+ background: #4a5568;
150
+ color: #e2e8f0;
151
+ }
152
+ }
@@ -0,0 +1,90 @@
1
+ import React, { useState } from 'react';
2
+ import Modal from './Modal';
3
+
4
+ /**
5
+ * Example usage of Modal component
6
+ */
7
+ const ModalExample = () => {
8
+ const [isSmallOpen, setIsSmallOpen] = useState(false);
9
+ const [isMediumOpen, setIsMediumOpen] = useState(false);
10
+ const [isLargeOpen, setIsLargeOpen] = useState(false);
11
+
12
+ return (
13
+ <div style={{ padding: '40px' }}>
14
+ <h2 style={{ marginBottom: '30px' }}>Modal Component Examples</h2>
15
+
16
+ <div style={{ display: 'flex', gap: '15px', flexWrap: 'wrap' }}>
17
+ <button
18
+ onClick={() => setIsSmallOpen(true)}
19
+ style={{ padding: '12px 24px', background: '#3182ce', color: 'white', border: 'none', borderRadius: '8px', cursor: 'pointer' }}
20
+ >
21
+ Open Small Modal
22
+ </button>
23
+
24
+ <button
25
+ onClick={() => setIsMediumOpen(true)}
26
+ style={{ padding: '12px 24px', background: '#3182ce', color: 'white', border: 'none', borderRadius: '8px', cursor: 'pointer' }}
27
+ >
28
+ Open Medium Modal
29
+ </button>
30
+
31
+ <button
32
+ onClick={() => setIsLargeOpen(true)}
33
+ style={{ padding: '12px 24px', background: '#3182ce', color: 'white', border: 'none', borderRadius: '8px', cursor: 'pointer' }}
34
+ >
35
+ Open Large Modal
36
+ </button>
37
+ </div>
38
+
39
+ <Modal
40
+ isOpen={isSmallOpen}
41
+ onClose={() => setIsSmallOpen(false)}
42
+ title="Small Modal"
43
+ size="small"
44
+ footer={
45
+ <>
46
+ <button
47
+ onClick={() => setIsSmallOpen(false)}
48
+ style={{ padding: '10px 20px', background: '#e2e8f0', border: 'none', borderRadius: '6px', cursor: 'pointer' }}
49
+ >
50
+ Cancel
51
+ </button>
52
+ <button
53
+ onClick={() => { console.log('Confirmed!'); setIsSmallOpen(false); }}
54
+ style={{ padding: '10px 20px', background: '#3182ce', color: 'white', border: 'none', borderRadius: '6px', cursor: 'pointer' }}
55
+ >
56
+ Confirm
57
+ </button>
58
+ </>
59
+ }
60
+ >
61
+ <p>This is a small modal perfect for confirmation dialogs or short messages.</p>
62
+ </Modal>
63
+
64
+ <Modal
65
+ isOpen={isMediumOpen}
66
+ onClose={() => setIsMediumOpen(false)}
67
+ title="Medium Modal"
68
+ size="medium"
69
+ >
70
+ <h3>Modal Content</h3>
71
+ <p>This is a medium-sized modal suitable for forms or detailed information.</p>
72
+ <p>You can include any React components or HTML elements inside the modal.</p>
73
+ </Modal>
74
+
75
+ <Modal
76
+ isOpen={isLargeOpen}
77
+ onClose={() => setIsLargeOpen(false)}
78
+ title="Large Modal"
79
+ size="large"
80
+ >
81
+ <h3>Large Modal Content</h3>
82
+ <p>This is a large modal ideal for complex interfaces or detailed content.</p>
83
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
84
+ <p>Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
85
+ </Modal>
86
+ </div>
87
+ );
88
+ };
89
+
90
+ export default ModalExample;
@@ -0,0 +1,46 @@
1
+ import React, { useState } from 'react';
2
+ import './Modal.css';
3
+
4
+ /**
5
+ * Modal Component
6
+ * A flexible modal dialog component
7
+ */
8
+ const Modal = ({
9
+ isOpen,
10
+ onClose,
11
+ title,
12
+ children,
13
+ footer,
14
+ showCloseButton = true,
15
+ closeOnOverlayClick = true,
16
+ size = 'medium'
17
+ }) => {
18
+ if (!isOpen) return null;
19
+
20
+ const handleOverlayClick = (e) => {
21
+ if (closeOnOverlayClick && e.target === e.currentTarget) {
22
+ onClose?.();
23
+ }
24
+ };
25
+
26
+ return (
27
+ <div className="modal-overlay" onClick={handleOverlayClick}>
28
+ <div className={`modal-content modal-${size}`}>
29
+ <div className="modal-header">
30
+ {title && <h2 className="modal-title">{title}</h2>}
31
+ {showCloseButton && (
32
+ <button className="modal-close" onClick={onClose} aria-label="Close">
33
+ ×
34
+ </button>
35
+ )}
36
+ </div>
37
+ <div className="modal-body">
38
+ {children}
39
+ </div>
40
+ {footer && <div className="modal-footer">{footer}</div>}
41
+ </div>
42
+ </div>
43
+ );
44
+ };
45
+
46
+ export default Modal;
@@ -0,0 +1,236 @@
1
+ /* Todo List Styles */
2
+ .todo-container {
3
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
4
+ background: white;
5
+ padding: 40px;
6
+ border-radius: 20px;
7
+ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.1);
8
+ max-width: 600px;
9
+ margin: 0 auto;
10
+ }
11
+
12
+ .todo-title {
13
+ font-size: 32px;
14
+ font-weight: 700;
15
+ margin: 0 0 30px 0;
16
+ text-align: center;
17
+ background: linear-gradient(135deg, {{primaryColor}} 0%, {{secondaryColor}} 100%);
18
+ -webkit-background-clip: text;
19
+ -webkit-text-fill-color: transparent;
20
+ background-clip: text;
21
+ }
22
+
23
+ .todo-form {
24
+ display: flex;
25
+ gap: 12px;
26
+ margin-bottom: 24px;
27
+ }
28
+
29
+ .todo-input {
30
+ flex: 1;
31
+ padding: 14px 20px;
32
+ font-size: 16px;
33
+ font-family: inherit;
34
+ border: 2px solid #e2e8f0;
35
+ border-radius: 12px;
36
+ transition: all 0.3s ease;
37
+ outline: none;
38
+ }
39
+
40
+ .todo-input:focus {
41
+ border-color: {{primaryColor}};
42
+ box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.1);
43
+ }
44
+
45
+ .todo-add-btn {
46
+ padding: 14px 32px;
47
+ font-size: 16px;
48
+ font-weight: 600;
49
+ font-family: inherit;
50
+ background: linear-gradient(135deg, {{primaryColor}} 0%, {{secondaryColor}} 100%);
51
+ color: white;
52
+ border: none;
53
+ border-radius: 12px;
54
+ cursor: pointer;
55
+ transition: all 0.3s ease;
56
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
57
+ }
58
+
59
+ .todo-add-btn:hover {
60
+ transform: translateY(-2px);
61
+ box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
62
+ }
63
+
64
+ .todo-add-btn:active {
65
+ transform: translateY(0);
66
+ }
67
+
68
+ .todo-stats {
69
+ display: flex;
70
+ justify-content: space-around;
71
+ margin-bottom: 24px;
72
+ padding: 16px;
73
+ background: #f7fafc;
74
+ border-radius: 12px;
75
+ }
76
+
77
+ .todo-stat {
78
+ font-size: 14px;
79
+ font-weight: 600;
80
+ color: #4a5568;
81
+ }
82
+
83
+ .todo-list {
84
+ list-style: none;
85
+ padding: 0;
86
+ margin: 0;
87
+ display: flex;
88
+ flex-direction: column;
89
+ gap: 12px;
90
+ }
91
+
92
+ .todo-empty {
93
+ padding: 40px 20px;
94
+ text-align: center;
95
+ color: #a0aec0;
96
+ font-size: 18px;
97
+ list-style: none;
98
+ }
99
+
100
+ .todo-item {
101
+ display: flex;
102
+ align-items: center;
103
+ justify-content: space-between;
104
+ padding: 16px 20px;
105
+ background: #f7fafc;
106
+ border-radius: 12px;
107
+ transition: all 0.3s ease;
108
+ animation: slideIn 0.3s ease;
109
+ }
110
+
111
+ @keyframes slideIn {
112
+ from {
113
+ opacity: 0;
114
+ transform: translateX(-20px);
115
+ }
116
+ to {
117
+ opacity: 1;
118
+ transform: translateX(0);
119
+ }
120
+ }
121
+
122
+ .todo-item:hover {
123
+ background: #edf2f7;
124
+ transform: translateX(5px);
125
+ }
126
+
127
+ .todo-item.completed {
128
+ opacity: 0.6;
129
+ }
130
+
131
+ .todo-content {
132
+ display: flex;
133
+ align-items: center;
134
+ gap: 16px;
135
+ flex: 1;
136
+ cursor: pointer;
137
+ user-select: none;
138
+ }
139
+
140
+ .todo-checkbox {
141
+ width: 24px;
142
+ height: 24px;
143
+ border: 2px solid {{primaryColor}};
144
+ border-radius: 6px;
145
+ display: flex;
146
+ align-items: center;
147
+ justify-content: center;
148
+ flex-shrink: 0;
149
+ transition: all 0.3s ease;
150
+ }
151
+
152
+ .todo-item.completed .todo-checkbox {
153
+ background: linear-gradient(135deg, {{primaryColor}} 0%, {{secondaryColor}} 100%);
154
+ border-color: transparent;
155
+ }
156
+
157
+ .todo-checkmark {
158
+ color: white;
159
+ font-size: 16px;
160
+ font-weight: bold;
161
+ }
162
+
163
+ .todo-text {
164
+ font-size: 16px;
165
+ color: #2d3748;
166
+ transition: all 0.3s ease;
167
+ }
168
+
169
+ .todo-item.completed .todo-text {
170
+ text-decoration: line-through;
171
+ color: #a0aec0;
172
+ }
173
+
174
+ .todo-delete-btn {
175
+ width: 32px;
176
+ height: 32px;
177
+ background: #fee;
178
+ color: #e53e3e;
179
+ border: none;
180
+ border-radius: 8px;
181
+ font-size: 24px;
182
+ line-height: 1;
183
+ cursor: pointer;
184
+ transition: all 0.3s ease;
185
+ display: flex;
186
+ align-items: center;
187
+ justify-content: center;
188
+ flex-shrink: 0;
189
+ }
190
+
191
+ .todo-delete-btn:hover {
192
+ background: #e53e3e;
193
+ color: white;
194
+ transform: scale(1.1);
195
+ }
196
+
197
+ /* Dark Mode Support */
198
+ @media (prefers-color-scheme: dark) {
199
+ .todo-container {
200
+ background: #2d3748;
201
+ }
202
+
203
+ .todo-input {
204
+ background: #1a202c;
205
+ color: #e2e8f0;
206
+ border-color: #4a5568;
207
+ }
208
+
209
+ .todo-input:focus {
210
+ border-color: {{primaryColor}};
211
+ }
212
+
213
+ .todo-stats {
214
+ background: #1a202c;
215
+ }
216
+
217
+ .todo-stat {
218
+ color: #cbd5e0;
219
+ }
220
+
221
+ .todo-item {
222
+ background: #1a202c;
223
+ }
224
+
225
+ .todo-item:hover {
226
+ background: #4a5568;
227
+ }
228
+
229
+ .todo-text {
230
+ color: #e2e8f0;
231
+ }
232
+
233
+ .todo-item.completed .todo-text {
234
+ color: #718096;
235
+ }
236
+ }
@@ -0,0 +1,15 @@
1
+ import React from 'react';
2
+ import TodoList from './TodoList';
3
+
4
+ /**
5
+ * Example usage of TodoList component
6
+ */
7
+ const TodoListExample = () => {
8
+ return (
9
+ <div style={{ padding: '40px', minHeight: '100vh', background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)' }}>
10
+ <TodoList />
11
+ </div>
12
+ );
13
+ };
14
+
15
+ export default TodoListExample;