create-template-html-css 2.0.4 → 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 (99) hide show
  1. package/CHANGELOG.md +436 -0
  2. package/CODE-SPLITTING-GUIDE.md +274 -0
  3. package/COMPONENTS-GALLERY.html +143 -8
  4. package/HTML-VS-REACT.md +289 -0
  5. package/QUICKSTART-REACT.md +293 -0
  6. package/REACT-SUPPORT-SUMMARY.md +235 -0
  7. package/README.md +261 -12
  8. package/bin/cli.js +100 -759
  9. package/bin/commands/create.js +288 -0
  10. package/bin/commands/gallery.js +42 -0
  11. package/bin/commands/insert.js +123 -0
  12. package/bin/commands/list.js +73 -0
  13. package/package.json +10 -3
  14. package/src/component-choices.js +7 -0
  15. package/src/components-registry.js +112 -0
  16. package/src/format-utils.js +49 -0
  17. package/src/generator.js +83 -594
  18. package/src/generators/color-schemes.js +78 -0
  19. package/src/generators/color-utils.js +108 -0
  20. package/src/generators/component-filters.js +151 -0
  21. package/src/generators/html-generators.js +180 -0
  22. package/src/generators/validation.js +43 -0
  23. package/src/index.js +2 -1
  24. package/src/inserter.js +55 -233
  25. package/src/inserters/backup-utils.js +20 -0
  26. package/src/inserters/component-loader.js +68 -0
  27. package/src/inserters/html-utils.js +31 -0
  28. package/src/inserters/indentation-utils.js +90 -0
  29. package/src/inserters/validation-utils.js +49 -0
  30. package/src/react-component-choices.js +97 -0
  31. package/src/react-component-templates.js +182 -0
  32. package/src/react-file-operations.js +172 -0
  33. package/src/react-generator.js +219 -0
  34. package/src/react-templates.js +418 -0
  35. package/src/templates/basic-components-templates.js +157 -0
  36. package/src/templates/form-components-templates.js +194 -0
  37. package/src/templates/interactive-components-templates.js +139 -0
  38. package/src/utils/file-utils.js +97 -0
  39. package/src/utils/path-utils.js +32 -0
  40. package/src/utils/string-utils.js +51 -0
  41. package/src/utils/template-loader.js +91 -0
  42. package/templates/_shared/PATTERNS.md +246 -0
  43. package/templates/_shared/README.md +74 -0
  44. package/templates/_shared/base.css +18 -0
  45. package/templates/blackjack/index.html +1 -1
  46. package/templates/breakout/index.html +1 -1
  47. package/templates/connect-four/index.html +1 -1
  48. package/templates/dice-game/index.html +1 -1
  49. package/templates/flappy-bird/index.html +1 -1
  50. package/templates/pong/index.html +1 -1
  51. package/templates/skeleton/index.html +4 -4
  52. package/templates/slot-machine/index.html +1 -1
  53. package/templates/tetris/index.html +1 -1
  54. package/templates-react/README.md +126 -0
  55. package/templates-react/alert/Alert.css +158 -0
  56. package/templates-react/alert/Alert.example.jsx +106 -0
  57. package/templates-react/alert/Alert.jsx +61 -0
  58. package/templates-react/badge/Badge.css +196 -0
  59. package/templates-react/badge/Badge.example.jsx +182 -0
  60. package/templates-react/badge/Badge.jsx +44 -0
  61. package/templates-react/button/Button.css +88 -0
  62. package/templates-react/button/Button.example.jsx +40 -0
  63. package/templates-react/button/Button.jsx +29 -0
  64. package/templates-react/card/Card.css +86 -0
  65. package/templates-react/card/Card.example.jsx +49 -0
  66. package/templates-react/card/Card.jsx +35 -0
  67. package/templates-react/checkbox/Checkbox.css +217 -0
  68. package/templates-react/checkbox/Checkbox.example.jsx +141 -0
  69. package/templates-react/checkbox/Checkbox.jsx +82 -0
  70. package/templates-react/counter/Counter.css +99 -0
  71. package/templates-react/counter/Counter.example.jsx +45 -0
  72. package/templates-react/counter/Counter.jsx +70 -0
  73. package/templates-react/dropdown/Dropdown.css +237 -0
  74. package/templates-react/dropdown/Dropdown.example.jsx +98 -0
  75. package/templates-react/dropdown/Dropdown.jsx +154 -0
  76. package/templates-react/form/Form.css +128 -0
  77. package/templates-react/form/Form.example.jsx +64 -0
  78. package/templates-react/form/Form.jsx +125 -0
  79. package/templates-react/input/Input.css +113 -0
  80. package/templates-react/input/Input.example.jsx +82 -0
  81. package/templates-react/input/Input.jsx +87 -0
  82. package/templates-react/modal/Modal.css +152 -0
  83. package/templates-react/modal/Modal.example.jsx +90 -0
  84. package/templates-react/modal/Modal.jsx +46 -0
  85. package/templates-react/navbar/Navbar.css +139 -0
  86. package/templates-react/navbar/Navbar.example.jsx +37 -0
  87. package/templates-react/navbar/Navbar.jsx +62 -0
  88. package/templates-react/progress/Progress.css +247 -0
  89. package/templates-react/progress/Progress.example.jsx +244 -0
  90. package/templates-react/progress/Progress.jsx +79 -0
  91. package/templates-react/switch/Switch.css +244 -0
  92. package/templates-react/switch/Switch.example.jsx +221 -0
  93. package/templates-react/switch/Switch.jsx +98 -0
  94. package/templates-react/todo-list/TodoList.css +236 -0
  95. package/templates-react/todo-list/TodoList.example.jsx +15 -0
  96. package/templates-react/todo-list/TodoList.jsx +84 -0
  97. package/templates-react/tooltip/Tooltip.css +165 -0
  98. package/templates-react/tooltip/Tooltip.example.jsx +166 -0
  99. package/templates-react/tooltip/Tooltip.jsx +176 -0
@@ -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;
@@ -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 { 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 { 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,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;