create-template-html-css 2.1.0 → 2.2.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 (53) hide show
  1. package/CHANGELOG.md +131 -0
  2. package/CODE-SPLITTING-GUIDE.md +274 -0
  3. package/COMPONENTS-GALLERY.html +143 -8
  4. package/README.md +71 -3
  5. package/bin/cli.js +2 -0
  6. package/bin/commands/create.js +16 -0
  7. package/package.json +1 -1
  8. package/src/react-component-choices.js +53 -1
  9. package/src/react-component-templates.js +182 -0
  10. package/src/react-generator.js +15 -4
  11. package/src/react-templates.js +192 -124
  12. package/src/templates/basic-components-templates.js +157 -0
  13. package/src/templates/form-components-templates.js +194 -0
  14. package/src/templates/interactive-components-templates.js +139 -0
  15. package/templates-react/alert/Alert.css +158 -0
  16. package/templates-react/alert/Alert.example.jsx +106 -0
  17. package/templates-react/alert/Alert.jsx +61 -0
  18. package/templates-react/badge/Badge.css +196 -0
  19. package/templates-react/badge/Badge.example.jsx +182 -0
  20. package/templates-react/badge/Badge.jsx +44 -0
  21. package/templates-react/button/Button.example.jsx +1 -1
  22. package/templates-react/button/Button.jsx +1 -1
  23. package/templates-react/card/Card.example.jsx +1 -1
  24. package/templates-react/card/Card.jsx +1 -1
  25. package/templates-react/checkbox/Checkbox.css +217 -0
  26. package/templates-react/checkbox/Checkbox.example.jsx +141 -0
  27. package/templates-react/checkbox/Checkbox.jsx +82 -0
  28. package/templates-react/counter/Counter.example.jsx +1 -1
  29. package/templates-react/counter/Counter.jsx +1 -1
  30. package/templates-react/dropdown/Dropdown.css +237 -0
  31. package/templates-react/dropdown/Dropdown.example.jsx +98 -0
  32. package/templates-react/dropdown/Dropdown.jsx +154 -0
  33. package/templates-react/form/Form.example.jsx +0 -1
  34. package/templates-react/form/Form.jsx +1 -1
  35. package/templates-react/input/Input.css +113 -0
  36. package/templates-react/input/Input.example.jsx +82 -0
  37. package/templates-react/input/Input.jsx +87 -0
  38. package/templates-react/modal/Modal.example.jsx +1 -1
  39. package/templates-react/modal/Modal.jsx +1 -1
  40. package/templates-react/navbar/Navbar.css +139 -0
  41. package/templates-react/navbar/Navbar.example.jsx +37 -0
  42. package/templates-react/navbar/Navbar.jsx +62 -0
  43. package/templates-react/progress/Progress.css +247 -0
  44. package/templates-react/progress/Progress.example.jsx +244 -0
  45. package/templates-react/progress/Progress.jsx +79 -0
  46. package/templates-react/switch/Switch.css +244 -0
  47. package/templates-react/switch/Switch.example.jsx +221 -0
  48. package/templates-react/switch/Switch.jsx +98 -0
  49. package/templates-react/todo-list/TodoList.example.jsx +1 -1
  50. package/templates-react/todo-list/TodoList.jsx +1 -1
  51. package/templates-react/tooltip/Tooltip.css +165 -0
  52. package/templates-react/tooltip/Tooltip.example.jsx +166 -0
  53. package/templates-react/tooltip/Tooltip.jsx +176 -0
@@ -0,0 +1,154 @@
1
+
2
+ import './Dropdown.css';
3
+ import { useState, useRef, useEffect } from 'react';
4
+
5
+ function Dropdown({
6
+ options = [],
7
+ value,
8
+ onChange,
9
+ placeholder = 'Select an option',
10
+ disabled = false,
11
+ searchable = false,
12
+ label = '',
13
+ error = '',
14
+ multiple = false,
15
+ maxHeight = '200px'
16
+ }) {
17
+ const [isOpen, setIsOpen] = useState(false);
18
+ const [searchTerm, setSearchTerm] = useState('');
19
+ const [selectedValues, setSelectedValues] = useState(multiple ? (value || []) : null);
20
+ const dropdownRef = useRef(null);
21
+
22
+ useEffect(() => {
23
+ const handleClickOutside = (event) => {
24
+ if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
25
+ setIsOpen(false);
26
+ setSearchTerm('');
27
+ }
28
+ };
29
+
30
+ document.addEventListener('mousedown', handleClickOutside);
31
+ return () => document.removeEventListener('mousedown', handleClickOutside);
32
+ }, []);
33
+
34
+ const filteredOptions = searchable && searchTerm
35
+ ? options.filter(option =>
36
+ typeof option === 'string'
37
+ ? option.toLowerCase().includes(searchTerm.toLowerCase())
38
+ : option.label.toLowerCase().includes(searchTerm.toLowerCase())
39
+ )
40
+ : options;
41
+
42
+ const handleSelect = (option) => {
43
+ const optionValue = typeof option === 'string' ? option : option.value;
44
+
45
+ if (multiple) {
46
+ const newValues = selectedValues.includes(optionValue)
47
+ ? selectedValues.filter(v => v !== optionValue)
48
+ : [...selectedValues, optionValue];
49
+ setSelectedValues(newValues);
50
+ onChange?.(newValues);
51
+ } else {
52
+ setSelectedValues(optionValue);
53
+ onChange?.(optionValue);
54
+ setIsOpen(false);
55
+ setSearchTerm('');
56
+ }
57
+ };
58
+
59
+ const getDisplayValue = () => {
60
+ if (multiple && selectedValues.length > 0) {
61
+ return `${selectedValues.length} selected`;
62
+ }
63
+ if (!multiple && selectedValues) {
64
+ const selected = options.find(opt =>
65
+ (typeof opt === 'string' ? opt : opt.value) === selectedValues
66
+ );
67
+ return typeof selected === 'string' ? selected : selected?.label || selectedValues;
68
+ }
69
+ return placeholder;
70
+ };
71
+
72
+ const isSelected = (option) => {
73
+ const optionValue = typeof option === 'string' ? option : option.value;
74
+ return multiple
75
+ ? selectedValues.includes(optionValue)
76
+ : selectedValues === optionValue;
77
+ };
78
+
79
+ return (
80
+ <div className={`dropdown-container ${error ? 'dropdown-error' : ''}`}>
81
+ {label && <label className="dropdown-label">{label}</label>}
82
+
83
+ <div
84
+ ref={dropdownRef}
85
+ className={`dropdown ${isOpen ? 'open' : ''} ${disabled ? 'disabled' : ''}`}
86
+ >
87
+ <button
88
+ type="button"
89
+ className="dropdown-toggle"
90
+ onClick={() => !disabled && setIsOpen(!isOpen)}
91
+ disabled={disabled}
92
+ aria-haspopup="listbox"
93
+ aria-expanded={isOpen}
94
+ >
95
+ <span className="dropdown-value">{getDisplayValue()}</span>
96
+ <span className={`dropdown-arrow ${isOpen ? 'up' : 'down'}`}>▼</span>
97
+ </button>
98
+
99
+ {isOpen && (
100
+ <div className="dropdown-menu" style={{ maxHeight }}>
101
+ {searchable && (
102
+ <div className="dropdown-search">
103
+ <input
104
+ type="text"
105
+ placeholder="Search..."
106
+ value={searchTerm}
107
+ onChange={(e) => setSearchTerm(e.target.value)}
108
+ onClick={(e) => e.stopPropagation()}
109
+ className="dropdown-search-input"
110
+ />
111
+ </div>
112
+ )}
113
+
114
+ <ul className="dropdown-options" role="listbox">
115
+ {filteredOptions.length === 0 ? (
116
+ <li className="dropdown-option disabled">No options available</li>
117
+ ) : (
118
+ filteredOptions.map((option, index) => {
119
+ const optionValue = typeof option === 'string' ? option : option.value;
120
+ const optionLabel = typeof option === 'string' ? option : option.label;
121
+ const selected = isSelected(option);
122
+
123
+ return (
124
+ <li
125
+ key={optionValue || index}
126
+ className={`dropdown-option ${selected ? 'selected' : ''}`}
127
+ onClick={() => handleSelect(option)}
128
+ role="option"
129
+ aria-selected={selected}
130
+ >
131
+ {multiple && (
132
+ <input
133
+ type="checkbox"
134
+ checked={selected}
135
+ onChange={() => {}}
136
+ className="dropdown-checkbox"
137
+ />
138
+ )}
139
+ {optionLabel}
140
+ </li>
141
+ );
142
+ })
143
+ )}
144
+ </ul>
145
+ </div>
146
+ )}
147
+ </div>
148
+
149
+ {error && <span className="dropdown-error-message">{error}</span>}
150
+ </div>
151
+ );
152
+ }
153
+
154
+ export default Dropdown;
@@ -1,4 +1,3 @@
1
- import React from 'react';
2
1
  import Form from './Form';
3
2
 
4
3
  /**
@@ -1,4 +1,4 @@
1
- import React, { useState } from 'react';
1
+ import { useState } from 'react';
2
2
  import './Form.css';
3
3
 
4
4
  /**
@@ -0,0 +1,113 @@
1
+ .input-wrapper {
2
+ margin-bottom: 1.5rem;
3
+ width: 100%;
4
+ }
5
+
6
+ .input-label {
7
+ display: block;
8
+ margin-bottom: 0.5rem;
9
+ font-size: 0.875rem;
10
+ font-weight: 600;
11
+ color: #374151;
12
+ }
13
+
14
+ .input-required {
15
+ color: #ef4444;
16
+ margin-left: 0.25rem;
17
+ }
18
+
19
+ .input-container {
20
+ position: relative;
21
+ display: flex;
22
+ align-items: center;
23
+ border: 2px solid #e5e7eb;
24
+ border-radius: 0.5rem;
25
+ background: white;
26
+ transition: all 0.2s ease;
27
+ }
28
+
29
+ .input-container:hover {
30
+ border-color: #d1d5db;
31
+ }
32
+
33
+ .input-container.focused {
34
+ border-color: ##PRIMARY_COLOR##;
35
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
36
+ }
37
+
38
+ .input-container.error {
39
+ border-color: #ef4444;
40
+ }
41
+
42
+ .input-container.error.focused {
43
+ box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.1);
44
+ }
45
+
46
+ .input-container.disabled {
47
+ background: #f3f4f6;
48
+ cursor: not-allowed;
49
+ opacity: 0.6;
50
+ }
51
+
52
+ .input-icon {
53
+ padding: 0 0.75rem;
54
+ color: #6b7280;
55
+ display: flex;
56
+ align-items: center;
57
+ }
58
+
59
+ .input-field {
60
+ flex: 1;
61
+ padding: 0.75rem 1rem;
62
+ border: none;
63
+ outline: none;
64
+ font-size: 1rem;
65
+ background: transparent;
66
+ color: #1f2937;
67
+ }
68
+
69
+ .input-field::placeholder {
70
+ color: #9ca3af;
71
+ }
72
+
73
+ .input-field:disabled {
74
+ cursor: not-allowed;
75
+ }
76
+
77
+ .input-message {
78
+ margin-top: 0.5rem;
79
+ font-size: 0.875rem;
80
+ color: #6b7280;
81
+ }
82
+
83
+ .input-message.error {
84
+ color: #ef4444;
85
+ }
86
+
87
+ /* Dark mode support */
88
+ @media (prefers-color-scheme: dark) {
89
+ .input-label {
90
+ color: #e5e7eb;
91
+ }
92
+
93
+ .input-container {
94
+ background: #1f2937;
95
+ border-color: #374151;
96
+ }
97
+
98
+ .input-container:hover {
99
+ border-color: #4b5563;
100
+ }
101
+
102
+ .input-field {
103
+ color: #f3f4f6;
104
+ }
105
+
106
+ .input-field::placeholder {
107
+ color: #6b7280;
108
+ }
109
+
110
+ .input-container.disabled {
111
+ background: #111827;
112
+ }
113
+ }
@@ -0,0 +1,82 @@
1
+ import { useState } from 'react';
2
+ import Input from './Input';
3
+
4
+ function InputExample() {
5
+ const [email, setEmail] = useState('');
6
+ const [password, setPassword] = useState('');
7
+ const [emailError, setEmailError] = useState('');
8
+
9
+ const validateEmail = (value) => {
10
+ if (!value) {
11
+ setEmailError('Email is required');
12
+ } else if (!/\S+@\S+\.\S+/.test(value)) {
13
+ setEmailError('Email is invalid');
14
+ } else {
15
+ setEmailError('');
16
+ }
17
+ };
18
+
19
+ const handleEmailChange = (e) => {
20
+ setEmail(e.target.value);
21
+ validateEmail(e.target.value);
22
+ };
23
+
24
+ return (
25
+ <div style={{ maxWidth: '400px', padding: '2rem' }}>
26
+ <h2>Input Component Examples</h2>
27
+
28
+ {/* Basic Input */}
29
+ <Input
30
+ label="Name"
31
+ placeholder="Enter your name"
32
+ required
33
+ />
34
+
35
+ {/* Email with validation */}
36
+ <Input
37
+ type="email"
38
+ label="Email"
39
+ placeholder="your@email.com"
40
+ value={email}
41
+ onChange={handleEmailChange}
42
+ error={emailError}
43
+ required
44
+ />
45
+
46
+ {/* Password Input */}
47
+ <Input
48
+ type="password"
49
+ label="Password"
50
+ placeholder="Enter password"
51
+ value={password}
52
+ onChange={(e) => setPassword(e.target.value)}
53
+ helperText="Must be at least 8 characters"
54
+ required
55
+ />
56
+
57
+ {/* With Icon */}
58
+ <Input
59
+ label="Search"
60
+ placeholder="Search..."
61
+ icon="🔍"
62
+ />
63
+
64
+ {/* Disabled Input */}
65
+ <Input
66
+ label="Disabled"
67
+ value="This is disabled"
68
+ disabled
69
+ />
70
+
71
+ {/* With max length */}
72
+ <Input
73
+ label="Username"
74
+ placeholder="Max 20 characters"
75
+ maxLength={20}
76
+ helperText="Choose a unique username"
77
+ />
78
+ </div>
79
+ );
80
+ }
81
+
82
+ export default InputExample;
@@ -0,0 +1,87 @@
1
+
2
+ import { useState } from 'react';
3
+
4
+ function Input({
5
+ type = 'text',
6
+ label,
7
+ placeholder,
8
+ value: controlledValue,
9
+ onChange,
10
+ onBlur,
11
+ error,
12
+ helperText,
13
+ required = false,
14
+ disabled = false,
15
+ maxLength,
16
+ pattern,
17
+ icon,
18
+ className = ''
19
+ }) {
20
+ const [value, setValue] = useState(controlledValue || '');
21
+ const [isFocused, setIsFocused] = useState(false);
22
+ const [touched, setTouched] = useState(false);
23
+
24
+ const isControlled = controlledValue !== undefined;
25
+ const currentValue = isControlled ? controlledValue : value;
26
+
27
+ const handleChange = (e) => {
28
+ const newValue = e.target.value;
29
+ if (!isControlled) {
30
+ setValue(newValue);
31
+ }
32
+ if (onChange) {
33
+ onChange(e);
34
+ }
35
+ };
36
+
37
+ const handleFocus = () => {
38
+ setIsFocused(true);
39
+ };
40
+
41
+ const handleBlur = (e) => {
42
+ setIsFocused(false);
43
+ setTouched(true);
44
+ if (onBlur) {
45
+ onBlur(e);
46
+ }
47
+ };
48
+
49
+ const showError = touched && error;
50
+
51
+ return (
52
+ <div className={`input-wrapper ${className}`}>
53
+ {label && (
54
+ <label className="input-label">
55
+ {label}
56
+ {required && <span className="input-required">*</span>}
57
+ </label>
58
+ )}
59
+
60
+ <div className={`input-container ${isFocused ? 'focused' : ''} ${showError ? 'error' : ''} ${disabled ? 'disabled' : ''}`}>
61
+ {icon && <span className="input-icon">{icon}</span>}
62
+
63
+ <input
64
+ type={type}
65
+ className="input-field"
66
+ placeholder={placeholder}
67
+ value={currentValue}
68
+ onChange={handleChange}
69
+ onFocus={handleFocus}
70
+ onBlur={handleBlur}
71
+ disabled={disabled}
72
+ required={required}
73
+ maxLength={maxLength}
74
+ pattern={pattern}
75
+ />
76
+ </div>
77
+
78
+ {(showError || helperText) && (
79
+ <div className={`input-message ${showError ? 'error' : ''}`}>
80
+ {showError ? error : helperText}
81
+ </div>
82
+ )}
83
+ </div>
84
+ );
85
+ }
86
+
87
+ export default Input;
@@ -1,4 +1,4 @@
1
- import React, { useState } from 'react';
1
+ import { useState } from 'react';
2
2
  import Modal from './Modal';
3
3
 
4
4
  /**
@@ -1,4 +1,4 @@
1
- import React, { useState } from 'react';
1
+ import { useState } from 'react';
2
2
  import './Modal.css';
3
3
 
4
4
  /**
@@ -0,0 +1,139 @@
1
+ .navbar {
2
+ background: linear-gradient(135deg, ##PRIMARY_COLOR## 0%, ##SECONDARY_COLOR## 100%);
3
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
4
+ position: sticky;
5
+ top: 0;
6
+ width: 100%;
7
+ z-index: 1000;
8
+ }
9
+
10
+ .navbar-container {
11
+ max-width: 1200px;
12
+ margin: 0 auto;
13
+ padding: 1rem 2rem;
14
+ display: flex;
15
+ justify-content: space-between;
16
+ align-items: center;
17
+ }
18
+
19
+ .navbar-logo {
20
+ font-size: 1.5rem;
21
+ font-weight: bold;
22
+ color: white;
23
+ cursor: pointer;
24
+ user-select: none;
25
+ }
26
+
27
+ .navbar-toggle {
28
+ display: none;
29
+ flex-direction: column;
30
+ background: none;
31
+ border: none;
32
+ cursor: pointer;
33
+ padding: 0.5rem;
34
+ z-index: 1001;
35
+ }
36
+
37
+ .navbar-toggle span {
38
+ width: 25px;
39
+ height: 3px;
40
+ background: white;
41
+ margin: 3px 0;
42
+ transition: all 0.3s ease;
43
+ border-radius: 3px;
44
+ }
45
+
46
+ .navbar-toggle.active span:nth-child(1) {
47
+ transform: rotate(45deg) translate(8px, 8px);
48
+ }
49
+
50
+ .navbar-toggle.active span:nth-child(2) {
51
+ opacity: 0;
52
+ }
53
+
54
+ .navbar-toggle.active span:nth-child(3) {
55
+ transform: rotate(-45deg) translate(7px, -7px);
56
+ }
57
+
58
+ .navbar-menu {
59
+ display: flex;
60
+ list-style: none;
61
+ margin: 0;
62
+ padding: 0;
63
+ gap: 2rem;
64
+ }
65
+
66
+ .navbar-item {
67
+ position: relative;
68
+ }
69
+
70
+ .navbar-link {
71
+ color: white;
72
+ text-decoration: none;
73
+ font-size: 1rem;
74
+ font-weight: 500;
75
+ padding: 0.5rem 0;
76
+ transition: opacity 0.3s ease;
77
+ position: relative;
78
+ }
79
+
80
+ .navbar-link::after {
81
+ content: '';
82
+ position: absolute;
83
+ bottom: 0;
84
+ left: 0;
85
+ width: 0;
86
+ height: 2px;
87
+ background: white;
88
+ transition: width 0.3s ease;
89
+ }
90
+
91
+ .navbar-link:hover {
92
+ opacity: 0.8;
93
+ }
94
+
95
+ .navbar-link:hover::after {
96
+ width: 100%;
97
+ }
98
+
99
+ /* Mobile Responsive */
100
+ @media (max-width: 768px) {
101
+ .navbar-toggle {
102
+ display: flex;
103
+ }
104
+
105
+ .navbar-menu {
106
+ position: fixed;
107
+ top: 0;
108
+ right: -100%;
109
+ height: 100vh;
110
+ width: 70%;
111
+ max-width: 300px;
112
+ background: linear-gradient(135deg, ##PRIMARY_COLOR## 0%, ##SECONDARY_COLOR## 100%);
113
+ flex-direction: column;
114
+ padding: 5rem 2rem 2rem;
115
+ gap: 1.5rem;
116
+ transition: right 0.3s ease;
117
+ box-shadow: -5px 0 15px rgba(0, 0, 0, 0.2);
118
+ }
119
+
120
+ .navbar-menu.active {
121
+ right: 0;
122
+ }
123
+
124
+ .navbar-link {
125
+ font-size: 1.1rem;
126
+ padding: 0.5rem 0;
127
+ }
128
+
129
+ .navbar-link::after {
130
+ display: none;
131
+ }
132
+ }
133
+
134
+ /* Dark mode support */
135
+ @media (prefers-color-scheme: dark) {
136
+ .navbar {
137
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
138
+ }
139
+ }
@@ -0,0 +1,37 @@
1
+ import Navbar from './Navbar';
2
+
3
+ function NavbarExample() {
4
+ const customLinks = [
5
+ { label: 'Home', href: '#home' },
6
+ { label: 'Features', href: '#features' },
7
+ { label: 'Pricing', href: '#pricing' },
8
+ { label: 'About', href: '#about' },
9
+ { label: 'Contact', href: '#contact' }
10
+ ];
11
+
12
+ const handleLinkClick = (link) => {
13
+ console.log('Clicked:', link.label);
14
+ };
15
+
16
+ return (
17
+ <div>
18
+ {/* Basic usage */}
19
+ <Navbar />
20
+
21
+ {/* With custom logo and links */}
22
+ <Navbar
23
+ logo="MyApp"
24
+ links={customLinks}
25
+ onLinkClick={handleLinkClick}
26
+ />
27
+
28
+ {/* With logo as component */}
29
+ <Navbar
30
+ logo={<img src="/logo.png" alt="Logo" style={{ height: '30px' }} />}
31
+ links={customLinks}
32
+ />
33
+ </div>
34
+ );
35
+ }
36
+
37
+ export default NavbarExample;
@@ -0,0 +1,62 @@
1
+
2
+ import { useState } from 'react';
3
+
4
+ function Navbar({
5
+ logo = 'Logo',
6
+ links = [
7
+ { label: 'Home', href: '#home' },
8
+ { label: 'About', href: '#about' },
9
+ { label: 'Services', href: '#services' },
10
+ { label: 'Contact', href: '#contact' }
11
+ ],
12
+ onLinkClick
13
+ }) {
14
+ const [isOpen, setIsOpen] = useState(false);
15
+
16
+ const toggleMenu = () => {
17
+ setIsOpen(!isOpen);
18
+ };
19
+
20
+ const handleLinkClick = (link) => {
21
+ setIsOpen(false);
22
+ if (onLinkClick) {
23
+ onLinkClick(link);
24
+ }
25
+ };
26
+
27
+ return (
28
+ <nav className="navbar">
29
+ <div className="navbar-container">
30
+ <div className="navbar-logo">
31
+ {logo}
32
+ </div>
33
+
34
+ <button
35
+ className={`navbar-toggle ${isOpen ? 'active' : ''}`}
36
+ onClick={toggleMenu}
37
+ aria-label="Toggle menu"
38
+ >
39
+ <span></span>
40
+ <span></span>
41
+ <span></span>
42
+ </button>
43
+
44
+ <ul className={`navbar-menu ${isOpen ? 'active' : ''}`}>
45
+ {links.map((link, index) => (
46
+ <li key={index} className="navbar-item">
47
+ <a
48
+ href={link.href}
49
+ className="navbar-link"
50
+ onClick={() => handleLinkClick(link)}
51
+ >
52
+ {link.label}
53
+ </a>
54
+ </li>
55
+ ))}
56
+ </ul>
57
+ </div>
58
+ </nav>
59
+ );
60
+ }
61
+
62
+ export default Navbar;