xegavnj 0.1.8
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/README.md +36 -0
- package/components/AppFooter.jsx +294 -0
- package/components/AppHeader.jsx +407 -0
- package/components/AppSidebar.jsx +405 -0
- package/components/Button.jsx +244 -0
- package/components/Card.jsx +388 -0
- package/components/DemoSidebar.jsx +112 -0
- package/components/ThemeSwitcher.jsx +29 -0
- package/index.js +8 -0
- package/package.json +45 -0
- package/theme/ThemeContext.js +42 -0
- package/theme.js +23 -0
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import styled, { css } from "styled-components";
|
|
4
|
+
import { Copy, Check, Code as CodeIcon, Sun, Moon } from "lucide-react";
|
|
5
|
+
import { useTheme } from "./ThemeSwitcher.jsx";
|
|
6
|
+
|
|
7
|
+
// =============================================================================
|
|
8
|
+
// REUSABLE HEADER COMPONENT
|
|
9
|
+
// =============================================================================
|
|
10
|
+
|
|
11
|
+
const HeaderWrapper = styled.header`
|
|
12
|
+
width: 100%;
|
|
13
|
+
transition: all 0.3s ease;
|
|
14
|
+
min-height: 90px; /* Ensure sufficient height */
|
|
15
|
+
|
|
16
|
+
/* Primary Variant */
|
|
17
|
+
${props => props.$variant === 'primary' && css`
|
|
18
|
+
background-color: #6366f1;
|
|
19
|
+
padding: 1.5rem 3rem;
|
|
20
|
+
display: flex;
|
|
21
|
+
align-items: center;
|
|
22
|
+
justify-content: space-between;
|
|
23
|
+
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
|
24
|
+
`}
|
|
25
|
+
|
|
26
|
+
/* Centered Variant */
|
|
27
|
+
${props => props.$variant === 'centered' && css`
|
|
28
|
+
background-color: #1f2937;
|
|
29
|
+
padding: 3rem 2rem;
|
|
30
|
+
display: flex;
|
|
31
|
+
flex-direction: column;
|
|
32
|
+
align-items: center;
|
|
33
|
+
gap: 2rem;
|
|
34
|
+
`}
|
|
35
|
+
|
|
36
|
+
/* Split Variant */
|
|
37
|
+
${props => props.$variant === 'split' && css`
|
|
38
|
+
background: linear-gradient(to right, #8b5cf6, #d946ef);
|
|
39
|
+
padding: 1.5rem 4rem;
|
|
40
|
+
display: flex;
|
|
41
|
+
align-items: center;
|
|
42
|
+
justify-content: space-between;
|
|
43
|
+
`}
|
|
44
|
+
`;
|
|
45
|
+
|
|
46
|
+
const Brand = styled.div`
|
|
47
|
+
font-weight: 800;
|
|
48
|
+
|
|
49
|
+
${props => props.$variant === 'primary' && css`
|
|
50
|
+
font-size: 1.5rem;
|
|
51
|
+
color: white;
|
|
52
|
+
`}
|
|
53
|
+
|
|
54
|
+
${props => props.$variant === 'centered' && css`
|
|
55
|
+
font-size: 2rem;
|
|
56
|
+
color: white;
|
|
57
|
+
text-transform: uppercase;
|
|
58
|
+
letter-spacing: 0.05em;
|
|
59
|
+
`}
|
|
60
|
+
|
|
61
|
+
${props => props.$variant === 'split' && css`
|
|
62
|
+
font-size: 1.4rem;
|
|
63
|
+
color: white;
|
|
64
|
+
`}
|
|
65
|
+
`;
|
|
66
|
+
|
|
67
|
+
const Nav = styled.nav`
|
|
68
|
+
display: flex;
|
|
69
|
+
align-items: center;
|
|
70
|
+
|
|
71
|
+
${props => props.$variant === 'primary' && css`
|
|
72
|
+
gap: 2rem;
|
|
73
|
+
`}
|
|
74
|
+
|
|
75
|
+
${props => props.$variant === 'centered' && css`
|
|
76
|
+
gap: 2.5rem;
|
|
77
|
+
`}
|
|
78
|
+
|
|
79
|
+
${props => props.$variant === 'split' && css`
|
|
80
|
+
gap: 2rem;
|
|
81
|
+
`}
|
|
82
|
+
`;
|
|
83
|
+
|
|
84
|
+
const NavLink = styled.a`
|
|
85
|
+
text-decoration: none;
|
|
86
|
+
transition: all 0.2s;
|
|
87
|
+
|
|
88
|
+
/* Primary Variant Links */
|
|
89
|
+
${props => props.$variant === 'primary' && css`
|
|
90
|
+
color: white;
|
|
91
|
+
font-weight: 500;
|
|
92
|
+
font-size: 0.95rem;
|
|
93
|
+
opacity: 0.9;
|
|
94
|
+
&:hover { opacity: 1; }
|
|
95
|
+
`}
|
|
96
|
+
|
|
97
|
+
/* Centered Variant Links */
|
|
98
|
+
${props => props.$variant === 'centered' && css`
|
|
99
|
+
color: white; /* Gray-300 */
|
|
100
|
+
font-size: 0.9rem;
|
|
101
|
+
font-weight: 600;
|
|
102
|
+
text-transform: uppercase;
|
|
103
|
+
letter-spacing: 0.05em;
|
|
104
|
+
position: relative;
|
|
105
|
+
opacity: 0.8;
|
|
106
|
+
|
|
107
|
+
&::after {
|
|
108
|
+
content: '';
|
|
109
|
+
position: absolute;
|
|
110
|
+
width: 0;
|
|
111
|
+
height: 2px;
|
|
112
|
+
background: #fbbf24;
|
|
113
|
+
bottom: -4px;
|
|
114
|
+
left: 0;
|
|
115
|
+
transition: width 0.3s ease;
|
|
116
|
+
}
|
|
117
|
+
&:hover { opacity: 1; }
|
|
118
|
+
&:hover::after { width: 100%; }
|
|
119
|
+
`}
|
|
120
|
+
|
|
121
|
+
/* Split Variant Links */
|
|
122
|
+
${props => props.$variant === 'split' && css`
|
|
123
|
+
color: white;
|
|
124
|
+
font-weight: 500;
|
|
125
|
+
font-size: 0.9rem;
|
|
126
|
+
&:hover { text-decoration: underline; }
|
|
127
|
+
`}
|
|
128
|
+
`;
|
|
129
|
+
|
|
130
|
+
const CtaButton = styled.button`
|
|
131
|
+
border: none;
|
|
132
|
+
cursor: pointer;
|
|
133
|
+
|
|
134
|
+
${props => props.$variant === 'split' && css`
|
|
135
|
+
background: white;
|
|
136
|
+
color: #9333ea;
|
|
137
|
+
padding: 0.6rem 1.5rem;
|
|
138
|
+
border-radius: 50px;
|
|
139
|
+
font-weight: 700;
|
|
140
|
+
font-size: 0.9rem;
|
|
141
|
+
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
|
142
|
+
transition: transform 0.2s;
|
|
143
|
+
|
|
144
|
+
&:hover {
|
|
145
|
+
transform: translateY(-2px);
|
|
146
|
+
}
|
|
147
|
+
`}
|
|
148
|
+
`;
|
|
149
|
+
|
|
150
|
+
const ToggleBtn = styled.button`
|
|
151
|
+
background: rgba(255, 255, 255, 0.2);
|
|
152
|
+
border: 1px solid rgba(255, 255, 255, 0.3);
|
|
153
|
+
width: 40px;
|
|
154
|
+
height: 40px;
|
|
155
|
+
border-radius: 50%;
|
|
156
|
+
display: flex;
|
|
157
|
+
align-items: center;
|
|
158
|
+
justify-content: center;
|
|
159
|
+
cursor: pointer;
|
|
160
|
+
color: white;
|
|
161
|
+
transition: all 0.2s;
|
|
162
|
+
margin-left: 1rem;
|
|
163
|
+
|
|
164
|
+
&:hover {
|
|
165
|
+
background: rgba(255, 255, 255, 0.3);
|
|
166
|
+
transform: scale(1.05);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
svg {
|
|
170
|
+
width: 20px;
|
|
171
|
+
height: 20px;
|
|
172
|
+
}
|
|
173
|
+
`;
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Reusable Header Component
|
|
177
|
+
*/
|
|
178
|
+
export function Header({
|
|
179
|
+
variant = 'primary',
|
|
180
|
+
logo = 'DapoerRasa',
|
|
181
|
+
menuItems = [],
|
|
182
|
+
ctaLabel,
|
|
183
|
+
onCtaClick,
|
|
184
|
+
showThemeToggle = true
|
|
185
|
+
}) {
|
|
186
|
+
const { theme, setTheme } = useTheme() || {}; // Handle safe access
|
|
187
|
+
|
|
188
|
+
const toggleTheme = () => {
|
|
189
|
+
if (setTheme) {
|
|
190
|
+
setTheme(theme === 'light' ? 'dark' : 'light');
|
|
191
|
+
} else {
|
|
192
|
+
console.warn("ThemeProvider not found. Theme toggle disabled.");
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
return (
|
|
197
|
+
<HeaderWrapper $variant={variant}>
|
|
198
|
+
<Brand $variant={variant}>{logo}</Brand>
|
|
199
|
+
|
|
200
|
+
<Nav $variant={variant}>
|
|
201
|
+
{menuItems.map((item, index) => (
|
|
202
|
+
<NavLink key={index} href={item.href} $variant={variant}>
|
|
203
|
+
{item.label}
|
|
204
|
+
</NavLink>
|
|
205
|
+
))}
|
|
206
|
+
|
|
207
|
+
{/* Theme Toggle Switch */}
|
|
208
|
+
{showThemeToggle && (
|
|
209
|
+
<ToggleBtn onClick={toggleTheme} title="Toggle Theme">
|
|
210
|
+
{theme === 'dark' ? <Moon /> : <Sun />}
|
|
211
|
+
</ToggleBtn>
|
|
212
|
+
)}
|
|
213
|
+
</Nav>
|
|
214
|
+
|
|
215
|
+
{variant === 'split' && ctaLabel && (
|
|
216
|
+
<CtaButton $variant={variant} onClick={onCtaClick}>
|
|
217
|
+
{ctaLabel}
|
|
218
|
+
</CtaButton>
|
|
219
|
+
)}
|
|
220
|
+
</HeaderWrapper>
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
// =============================================================================
|
|
226
|
+
// SHOWCASE & DOCUMENTATION WRAPPER
|
|
227
|
+
// =============================================================================
|
|
228
|
+
|
|
229
|
+
const ShowcaseContainer = styled.div`
|
|
230
|
+
width: 100%;
|
|
231
|
+
padding: 2rem 0;
|
|
232
|
+
display: flex;
|
|
233
|
+
flex-direction: column;
|
|
234
|
+
gap: 4rem;
|
|
235
|
+
background-color: #f8fafc;
|
|
236
|
+
min-height: 100vh;
|
|
237
|
+
`;
|
|
238
|
+
|
|
239
|
+
const Title = styled.h2`
|
|
240
|
+
text-align: center;
|
|
241
|
+
font-size: 2rem;
|
|
242
|
+
color: #1e293b;
|
|
243
|
+
margin-bottom: 1rem;
|
|
244
|
+
font-weight: 800;
|
|
245
|
+
`;
|
|
246
|
+
|
|
247
|
+
const HeaderDisplayWrapper = styled.div`
|
|
248
|
+
width: 100%;
|
|
249
|
+
margin-bottom: 2rem;
|
|
250
|
+
`;
|
|
251
|
+
|
|
252
|
+
const CodeControlBar = styled.div`
|
|
253
|
+
display: flex;
|
|
254
|
+
justify-content: center;
|
|
255
|
+
padding: 0.5rem;
|
|
256
|
+
margin-top: 1rem;
|
|
257
|
+
`;
|
|
258
|
+
|
|
259
|
+
const ActionButton = styled.button`
|
|
260
|
+
display: flex;
|
|
261
|
+
align-items: center;
|
|
262
|
+
gap: 0.5rem;
|
|
263
|
+
padding: 0.5rem 1rem;
|
|
264
|
+
border-radius: 6px;
|
|
265
|
+
border: 1px solid #cbd5e1;
|
|
266
|
+
background: white;
|
|
267
|
+
font-size: 0.85rem;
|
|
268
|
+
color: #475569;
|
|
269
|
+
cursor: pointer;
|
|
270
|
+
font-weight: 500;
|
|
271
|
+
transition: all 0.2s;
|
|
272
|
+
box-shadow: 0 1px 2px rgba(0,0,0,0.05);
|
|
273
|
+
|
|
274
|
+
&:hover {
|
|
275
|
+
background: #f8fafc;
|
|
276
|
+
border-color: #94a3b8;
|
|
277
|
+
}
|
|
278
|
+
`;
|
|
279
|
+
|
|
280
|
+
const CodeBlock = styled.div`
|
|
281
|
+
background: #1e1e1e;
|
|
282
|
+
color: #d4d4d4;
|
|
283
|
+
padding: 1rem;
|
|
284
|
+
overflow-x: auto;
|
|
285
|
+
font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
|
|
286
|
+
font-size: 0.85rem;
|
|
287
|
+
line-height: 1.5;
|
|
288
|
+
border-radius: 8px;
|
|
289
|
+
margin: 1rem auto;
|
|
290
|
+
max-width: 800px;
|
|
291
|
+
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
|
292
|
+
`;
|
|
293
|
+
|
|
294
|
+
function HeaderVariantDisplay({ title, code, children }) {
|
|
295
|
+
const [showCode, setShowCode] = useState(false);
|
|
296
|
+
const [copied, setCopied] = useState(false);
|
|
297
|
+
|
|
298
|
+
const handleCopy = () => {
|
|
299
|
+
navigator.clipboard.writeText(code);
|
|
300
|
+
setCopied(true);
|
|
301
|
+
setTimeout(() => setCopied(false), 2000);
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
return (
|
|
305
|
+
<HeaderDisplayWrapper>
|
|
306
|
+
<h3 style={{ textAlign: 'center', marginBottom: '1.5rem', color: '#64748b', textTransform: 'uppercase', letterSpacing: '0.1em', fontSize: '1rem' }}>{title}</h3>
|
|
307
|
+
|
|
308
|
+
<div style={{ boxShadow: "0 2px 4px rgba(0,0,0,0.05)" }}>
|
|
309
|
+
{children}
|
|
310
|
+
</div>
|
|
311
|
+
|
|
312
|
+
<CodeControlBar>
|
|
313
|
+
<div style={{ display: 'flex', gap: '0.5rem' }}>
|
|
314
|
+
<ActionButton onClick={() => setShowCode(!showCode)}>
|
|
315
|
+
<CodeIcon size={16} />
|
|
316
|
+
{showCode ? "Hide Code" : "View Use Code"}
|
|
317
|
+
</ActionButton>
|
|
318
|
+
{showCode && (
|
|
319
|
+
<ActionButton onClick={handleCopy}>
|
|
320
|
+
{copied ? <Check size={16} /> : <Copy size={16} />}
|
|
321
|
+
{copied ? "Copied!" : "Copy"}
|
|
322
|
+
</ActionButton>
|
|
323
|
+
)}
|
|
324
|
+
</div>
|
|
325
|
+
</CodeControlBar>
|
|
326
|
+
|
|
327
|
+
{showCode && (
|
|
328
|
+
<CodeBlock>
|
|
329
|
+
<pre>{code}</pre>
|
|
330
|
+
</CodeBlock>
|
|
331
|
+
)}
|
|
332
|
+
</HeaderDisplayWrapper>
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
const defaultMenuItems = [
|
|
337
|
+
{ label: 'Beranda', href: '#' },
|
|
338
|
+
{ label: 'Menu', href: '#' },
|
|
339
|
+
{ label: 'Tentang', href: '#' },
|
|
340
|
+
{ label: 'Kontak', href: '#' },
|
|
341
|
+
];
|
|
342
|
+
|
|
343
|
+
export function HeaderShowcase() {
|
|
344
|
+
return (
|
|
345
|
+
<ShowcaseContainer>
|
|
346
|
+
<Title>Reusable Header Component</Title>
|
|
347
|
+
<p style={{ textAlign: 'center', marginBottom: '2rem', opacity: 0.8 }}>
|
|
348
|
+
Komponen header yang fleksibel dengan props dan tema toggle.
|
|
349
|
+
</p>
|
|
350
|
+
|
|
351
|
+
<HeaderVariantDisplay
|
|
352
|
+
title="Primary Variant"
|
|
353
|
+
code={`<Header
|
|
354
|
+
variant="primary"
|
|
355
|
+
logo="DapoerRasa"
|
|
356
|
+
menuItems={[
|
|
357
|
+
{ label: 'Beranda', href: '#' },
|
|
358
|
+
{ label: 'Menu', href: '#' },
|
|
359
|
+
{ label: 'Tentang', href: '#' },
|
|
360
|
+
{ label: 'Kontak', href: '#' }
|
|
361
|
+
]}
|
|
362
|
+
/>`}
|
|
363
|
+
>
|
|
364
|
+
<Header variant="primary" logo="DapoerRasa" menuItems={defaultMenuItems} showThemeToggle={false} />
|
|
365
|
+
</HeaderVariantDisplay>
|
|
366
|
+
|
|
367
|
+
<HeaderVariantDisplay
|
|
368
|
+
title="Centered Variant"
|
|
369
|
+
code={`<Header
|
|
370
|
+
variant="centered"
|
|
371
|
+
logo="DapoerRasa"
|
|
372
|
+
menuItems={[
|
|
373
|
+
{ label: 'Beranda', href: '#' },
|
|
374
|
+
{ label: 'Menu', href: '#' },
|
|
375
|
+
{ label: 'Tentang', href: '#' },
|
|
376
|
+
{ label: 'Kontak', href: '#' }
|
|
377
|
+
]}
|
|
378
|
+
/>`}
|
|
379
|
+
>
|
|
380
|
+
<Header variant="centered" logo="DapoerRasa" menuItems={defaultMenuItems} showThemeToggle={false} />
|
|
381
|
+
</HeaderVariantDisplay>
|
|
382
|
+
|
|
383
|
+
<HeaderVariantDisplay
|
|
384
|
+
title="Split Variant"
|
|
385
|
+
code={`<Header
|
|
386
|
+
variant="split"
|
|
387
|
+
logo="DapoerRasa"
|
|
388
|
+
ctaLabel="Order Now"
|
|
389
|
+
menuItems={[
|
|
390
|
+
{ label: 'Beranda', href: '#' },
|
|
391
|
+
{ label: 'Menu', href: '#' },
|
|
392
|
+
{ label: 'Tentang', href: '#' },
|
|
393
|
+
{ label: 'Kontak', href: '#' }
|
|
394
|
+
]}
|
|
395
|
+
/>`}
|
|
396
|
+
>
|
|
397
|
+
<Header variant="split" logo="DapoerRasa" menuItems={defaultMenuItems} ctaLabel="Order Now" showThemeToggle={false} />
|
|
398
|
+
</HeaderVariantDisplay>
|
|
399
|
+
|
|
400
|
+
</ShowcaseContainer>
|
|
401
|
+
);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
export default function AppHeader(props) {
|
|
405
|
+
const items = props.menuItems || defaultMenuItems;
|
|
406
|
+
return <Header {...props} menuItems={items} />;
|
|
407
|
+
}
|