create-react-scaffold-cli 1.0.4 → 1.0.5
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/package.json +1 -1
- package/templates/base/index.html +3 -3
- package/templates/base/public/icons/react.svg +1 -0
- package/templates/base/src/app/App.jsx +4 -2
- package/templates/base/src/app/Router.jsx +11 -3
- package/templates/base/src/app/index.css +36 -0
- package/templates/base/src/app/main.jsx +1 -1
- package/templates/base/src/app/routes.registry.js +2 -0
- package/templates/base/src/features/sample/constants/index.js +3 -0
- package/templates/base/src/features/sample/pages/index.js +2 -0
- package/templates/base/src/features/sample/sample.routes.js +2 -3
- package/templates/base/src/features/welcome/components/CodeLine.jsx +54 -0
- package/templates/base/src/features/welcome/components/Divider.jsx +7 -0
- package/templates/base/src/features/welcome/components/Footer.jsx +78 -0
- package/templates/base/src/features/welcome/components/Hero.jsx +131 -0
- package/templates/base/src/features/welcome/components/IconLink.jsx +18 -0
- package/templates/base/src/features/welcome/components/QuickStartPanel.jsx +63 -0
- package/templates/base/src/features/welcome/components/RingSoft.jsx +16 -0
- package/templates/base/src/features/welcome/components/StorySections.jsx +63 -0
- package/templates/base/src/features/welcome/components/WhatYouGet.jsx +49 -0
- package/templates/base/src/features/welcome/components/index.js +5 -0
- package/templates/base/src/features/welcome/constants/index.js +2 -0
- package/templates/base/src/features/welcome/constants/welcome.constants.js +21 -0
- package/templates/base/src/features/welcome/constants/welcome.navigations.js +3 -0
- package/templates/base/src/features/welcome/index.js +1 -0
- package/templates/base/src/features/welcome/pages/WelcomePage.jsx +28 -0
- package/templates/base/src/features/welcome/pages/index.js +3 -0
- package/templates/base/src/features/welcome/welcome.routes.js +12 -0
- package/templates/base/src/shared/theme/theme.js +26 -15
- package/templates/base/src/shared/ui/Box.jsx +1 -14
- package/templates/base/src/shared/ui/Button.jsx +4 -5
- package/templates/base/src/shared/ui/DropdownMenu.jsx +57 -91
- package/templates/base/src/shared/ui/GridItem.jsx +1 -0
- package/templates/base/src/shared/ui/Modal.jsx +1 -0
- package/templates/base/src/shared/ui/Text.jsx +3 -3
- package/templates/base/src/shared/ui/index.js +17 -0
- package/templates/base/vercel.json +3 -0
package/package.json
CHANGED
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
<html lang="en">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
|
-
<link rel="icon" type="image/svg+xml" href="/
|
|
5
|
+
<link rel="icon" type="image/svg+xml" href="/public/icons/react.svg" />
|
|
6
6
|
<meta
|
|
7
7
|
name="viewport"
|
|
8
8
|
content="width=device-width, initial-scale=1, viewport-fit=cover, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
|
|
9
9
|
/>
|
|
10
|
-
<title>
|
|
10
|
+
<title>React Scaffold App</title>
|
|
11
11
|
</head>
|
|
12
12
|
<body>
|
|
13
13
|
<div id="root"></div>
|
|
14
|
-
<script type="module" src="/src/main.jsx"></script>
|
|
14
|
+
<script type="module" src="/src/app/main.jsx"></script>
|
|
15
15
|
</body>
|
|
16
16
|
</html>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 32 32"><g transform="matrix(.05696 0 0 .05696 .647744 2.43826)" fill="none" fill-rule="evenodd"><circle r="50.167" cy="237.628" cx="269.529" fill="#00d8ff"/><g stroke="#00d8ff" stroke-width="24"><path d="M269.53 135.628c67.356 0 129.928 9.665 177.107 25.907 56.844 19.57 91.794 49.233 91.794 76.093 0 27.99-37.04 59.503-98.083 79.728-46.15 15.29-106.88 23.272-170.818 23.272-65.554 0-127.63-7.492-174.3-23.44-59.046-20.182-94.61-52.103-94.61-79.56 0-26.642 33.37-56.076 89.415-75.616 47.355-16.51 111.472-26.384 179.486-26.384z"/><path d="M180.736 186.922c33.65-58.348 73.28-107.724 110.92-140.48C337.006 6.976 380.163-8.48 403.43 4.937c24.248 13.983 33.042 61.814 20.067 124.796-9.8 47.618-33.234 104.212-65.176 159.6-32.75 56.788-70.25 106.82-107.377 139.272-46.98 41.068-92.4 55.93-116.185 42.213-23.08-13.3-31.906-56.92-20.834-115.233 9.355-49.27 32.832-109.745 66.8-168.664z"/><path d="M180.82 289.482C147.075 231.2 124.1 172.195 114.51 123.227c-11.544-59-3.382-104.11 19.864-117.566 24.224-14.024 70.055 2.244 118.14 44.94 36.356 32.28 73.688 80.837 105.723 136.173 32.844 56.733 57.46 114.21 67.036 162.582 12.117 61.213 2.31 107.984-21.453 121.74-23.057 13.348-65.25-.784-110.24-39.5-38.013-32.71-78.682-83.253-112.76-142.115z"/></g></g></svg>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Toaster } from '@/shared/ui';
|
|
1
|
+
import { Scrollable, Toaster } from '@/shared/ui';
|
|
2
2
|
import { QueryProvider } from './providers';
|
|
3
3
|
import { Router } from './Router';
|
|
4
4
|
import { memo } from '@/shared/utils';
|
|
@@ -6,7 +6,9 @@ import { memo } from '@/shared/utils';
|
|
|
6
6
|
export const App = memo(() => {
|
|
7
7
|
return (
|
|
8
8
|
<QueryProvider>
|
|
9
|
-
<
|
|
9
|
+
<Scrollable>
|
|
10
|
+
<Router />
|
|
11
|
+
</Scrollable>
|
|
10
12
|
<Toaster />
|
|
11
13
|
</QueryProvider>
|
|
12
14
|
);
|
|
@@ -3,6 +3,7 @@ import { featureRoutes } from './routes.registry';
|
|
|
3
3
|
import { AuthMiddleware } from './middlewares';
|
|
4
4
|
import { Layouts } from '@/shared/constants';
|
|
5
5
|
import { memo } from '@/shared/utils';
|
|
6
|
+
import React from 'react';
|
|
6
7
|
|
|
7
8
|
const layoutMap = {
|
|
8
9
|
dashboard: null,
|
|
@@ -15,13 +16,20 @@ export const Router = memo(() => {
|
|
|
15
16
|
<Routes>
|
|
16
17
|
{featureRoutes.map((route, index) => {
|
|
17
18
|
const Layout = layoutMap[route.layout ?? Layouts.None];
|
|
19
|
+
const Page = route.element;
|
|
18
20
|
|
|
19
|
-
let element =
|
|
21
|
+
let element = (
|
|
22
|
+
<Layout>
|
|
23
|
+
<Page />
|
|
24
|
+
</Layout>
|
|
25
|
+
);
|
|
20
26
|
|
|
21
27
|
if (route.protected) {
|
|
22
28
|
element = (
|
|
23
29
|
<AuthMiddleware>
|
|
24
|
-
<Layout>
|
|
30
|
+
<Layout>
|
|
31
|
+
<Page />
|
|
32
|
+
</Layout>
|
|
25
33
|
</AuthMiddleware>
|
|
26
34
|
);
|
|
27
35
|
}
|
|
@@ -29,7 +37,7 @@ export const Router = memo(() => {
|
|
|
29
37
|
return <Route key={index} path={route.path} element={element} />;
|
|
30
38
|
})}
|
|
31
39
|
|
|
32
|
-
<Route path="/*" element={<Navigate to="/
|
|
40
|
+
<Route path="/*" element={<Navigate to="/welcome" replace />} />
|
|
33
41
|
</Routes>
|
|
34
42
|
</BrowserRouter>
|
|
35
43
|
);
|
|
@@ -1 +1,37 @@
|
|
|
1
1
|
@import 'tailwindcss';
|
|
2
|
+
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:ital,wght@0,200..800;1,200..800&display=swap');
|
|
3
|
+
@import url('https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap');
|
|
4
|
+
|
|
5
|
+
@theme {
|
|
6
|
+
/* Colors */
|
|
7
|
+
--color-primary: #3452ff;
|
|
8
|
+
--color-primary-foreground: #ffffff;
|
|
9
|
+
--color-badge: #fef1a7;
|
|
10
|
+
|
|
11
|
+
--color-bg: #ffffff;
|
|
12
|
+
--color-surface: #f7f7f7;
|
|
13
|
+
|
|
14
|
+
--color-ink: #03003e;
|
|
15
|
+
--color-muted: #797979;
|
|
16
|
+
|
|
17
|
+
--color-ring: color-mix(in oklab, var(--color-primary) 35%, transparent);
|
|
18
|
+
--color-border: color-mix(in oklab, var(--color-ink) 10%, transparent);
|
|
19
|
+
--color-soft: color-mix(in oklab, var(--color-primary) 10%, transparent);
|
|
20
|
+
|
|
21
|
+
/* Fonts */
|
|
22
|
+
--font-plus-jakarta-sans:
|
|
23
|
+
'Plus Jakarta Sans', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Arial, sans-serif;
|
|
24
|
+
--font-inter: 'Inter', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Arial, sans-serif;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
:root {
|
|
28
|
+
color: var(--color-ink);
|
|
29
|
+
background: var(--color-bg);
|
|
30
|
+
text-rendering: optimizeLegibility;
|
|
31
|
+
-webkit-font-smoothing: antialiased;
|
|
32
|
+
-moz-osx-font-smoothing: grayscale;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
body {
|
|
36
|
+
font-family: var(--font-inter);
|
|
37
|
+
}
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
1
|
import { Layouts } from '@/shared/constants';
|
|
3
|
-
import { SampleNavigation } from './constants/sample.navigations';
|
|
4
2
|
import { SamplePage } from './pages';
|
|
3
|
+
import { SampleNavigation } from './constants';
|
|
5
4
|
|
|
6
5
|
export const sampleRoutes = [
|
|
7
6
|
{
|
|
8
7
|
path: SampleNavigation.Sample,
|
|
9
|
-
element:
|
|
8
|
+
element: SamplePage,
|
|
10
9
|
protected: true,
|
|
11
10
|
layout: Layouts.None,
|
|
12
11
|
},
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { PiCopySimpleBold, PiCheckBold } from 'react-icons/pi';
|
|
3
|
+
import { Flex } from '@/shared/ui/Flex';
|
|
4
|
+
import { Text } from '@/shared/ui/Text';
|
|
5
|
+
import { useToggleState } from '@/shared/hooks';
|
|
6
|
+
import { Box, FlexItem } from '@/shared/ui';
|
|
7
|
+
import { cn } from '@/shared/libs';
|
|
8
|
+
|
|
9
|
+
export const CodeLine = React.memo(({ value, isStorySection }) => {
|
|
10
|
+
const [copied, toggleCopied] = useToggleState();
|
|
11
|
+
|
|
12
|
+
const onCopy = React.useCallback(async () => {
|
|
13
|
+
try {
|
|
14
|
+
await navigator.clipboard.writeText(value);
|
|
15
|
+
} catch {
|
|
16
|
+
const ta = document.createElement('textarea');
|
|
17
|
+
ta.value = value;
|
|
18
|
+
document.body.appendChild(ta);
|
|
19
|
+
ta.select();
|
|
20
|
+
document.execCommand('copy');
|
|
21
|
+
document.body.removeChild(ta);
|
|
22
|
+
}
|
|
23
|
+
toggleCopied();
|
|
24
|
+
window.setTimeout(() => toggleCopied(), 1200);
|
|
25
|
+
}, [toggleCopied]);
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<Box
|
|
29
|
+
radius="xl"
|
|
30
|
+
padding={{ x: 4, y: 2 }}
|
|
31
|
+
className={cn('ring-1 ring-border/50', isStorySection ? 'bg-surface' : 'bg-bg/80 ')}
|
|
32
|
+
>
|
|
33
|
+
<Flex gap={3} align="center" justify="between">
|
|
34
|
+
<FlexItem className="min-w-0">
|
|
35
|
+
<Text size="sm" color="ink" truncate>
|
|
36
|
+
{value}
|
|
37
|
+
</Text>
|
|
38
|
+
</FlexItem>
|
|
39
|
+
|
|
40
|
+
<FlexItem>
|
|
41
|
+
<button
|
|
42
|
+
type="button"
|
|
43
|
+
onClick={onCopy}
|
|
44
|
+
className="shrink-0 inline-flex size-9 items-center justify-center rounded-lg bg-black/5 text-ink hover:bg-black/10 transition-colors cursor-pointer"
|
|
45
|
+
aria-label={copied ? 'Copied' : 'Copy command'}
|
|
46
|
+
title={copied ? 'Copied' : 'Copy'}
|
|
47
|
+
>
|
|
48
|
+
{copied ? <PiCheckBold className="size-4" /> : <PiCopySimpleBold className="size-4" />}
|
|
49
|
+
</button>
|
|
50
|
+
</FlexItem>
|
|
51
|
+
</Flex>
|
|
52
|
+
</Box>
|
|
53
|
+
);
|
|
54
|
+
});
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Box, Flex, FlexItem, Text } from '@/shared/ui';
|
|
3
|
+
import { Links } from '../constants';
|
|
4
|
+
import { Divider } from './Divider';
|
|
5
|
+
|
|
6
|
+
export const Footer = React.memo(() => {
|
|
7
|
+
return (
|
|
8
|
+
<Box className="mt-12 pt-8">
|
|
9
|
+
<Divider className="mb-6" />
|
|
10
|
+
|
|
11
|
+
<Flex
|
|
12
|
+
gap={4}
|
|
13
|
+
align={{ md: 'center' }}
|
|
14
|
+
justify={{ md: 'between' }}
|
|
15
|
+
direction={{ base: 'column', md: 'row' }}
|
|
16
|
+
>
|
|
17
|
+
<FlexItem>
|
|
18
|
+
<Text size="sm" color="muted">
|
|
19
|
+
<span className="font-semibold text-ink">React Scaffold</span>
|
|
20
|
+
<>
|
|
21
|
+
<span className="mx-2">•</span>
|
|
22
|
+
Created by{' '}
|
|
23
|
+
<a
|
|
24
|
+
href={Links.CreatorGithub}
|
|
25
|
+
target="_blank"
|
|
26
|
+
rel="noreferrer"
|
|
27
|
+
className="font-semibold text-ink hover:text-primary transition-colors"
|
|
28
|
+
>
|
|
29
|
+
{Links.CreatorName}
|
|
30
|
+
</a>
|
|
31
|
+
</>
|
|
32
|
+
</Text>
|
|
33
|
+
</FlexItem>
|
|
34
|
+
|
|
35
|
+
<FlexItem>
|
|
36
|
+
<Flex gap={5} className="flex-wrap text-sm font-medium">
|
|
37
|
+
<a
|
|
38
|
+
className="text-muted hover:text-primary"
|
|
39
|
+
href={Links.AppGithub}
|
|
40
|
+
target="_blank"
|
|
41
|
+
rel="noreferrer"
|
|
42
|
+
>
|
|
43
|
+
Project Repo
|
|
44
|
+
</a>
|
|
45
|
+
<a
|
|
46
|
+
className="text-muted hover:text-primary"
|
|
47
|
+
href={Links.CliGithub}
|
|
48
|
+
target="_blank"
|
|
49
|
+
rel="noreferrer"
|
|
50
|
+
>
|
|
51
|
+
CLI Repo
|
|
52
|
+
</a>
|
|
53
|
+
<a
|
|
54
|
+
className="text-muted hover:text-primary"
|
|
55
|
+
href={Links.NpmPackage}
|
|
56
|
+
target="_blank"
|
|
57
|
+
rel="noreferrer"
|
|
58
|
+
>
|
|
59
|
+
NPM Package
|
|
60
|
+
</a>
|
|
61
|
+
|
|
62
|
+
<>
|
|
63
|
+
<span className="text-border">|</span>
|
|
64
|
+
<a
|
|
65
|
+
className="text-muted hover:text-primary"
|
|
66
|
+
href={Links.CreatorLinkedIn}
|
|
67
|
+
target="_blank"
|
|
68
|
+
rel="noreferrer"
|
|
69
|
+
>
|
|
70
|
+
Creator
|
|
71
|
+
</a>
|
|
72
|
+
</>
|
|
73
|
+
</Flex>
|
|
74
|
+
</FlexItem>
|
|
75
|
+
</Flex>
|
|
76
|
+
</Box>
|
|
77
|
+
);
|
|
78
|
+
});
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Box, Button, Flex, FlexItem, Grid, Text } from '@/shared/ui';
|
|
3
|
+
import { FaGithub, FaLinkedin, FaNpm } from 'react-icons/fa';
|
|
4
|
+
import { QuickStartPanel } from './QuickStartPanel';
|
|
5
|
+
import { Links } from '../constants';
|
|
6
|
+
import { IconLink } from './IconLink';
|
|
7
|
+
|
|
8
|
+
export const Hero = React.memo(() => {
|
|
9
|
+
return (
|
|
10
|
+
<Grid
|
|
11
|
+
align="start"
|
|
12
|
+
gap={{ base: 10, md: 12 }}
|
|
13
|
+
className="md:grid-cols-[1.25fr_0.75fr] md:gap-12"
|
|
14
|
+
>
|
|
15
|
+
<Flex gap={5} direction="column">
|
|
16
|
+
<Box padding={{ x: 3, y: 1 }} width="fit" className="rounded-full bg-badge">
|
|
17
|
+
<Flex gap={2} align="center">
|
|
18
|
+
<FlexItem>
|
|
19
|
+
<Box radius="full" className="size-1.5 bg-primary" />
|
|
20
|
+
</FlexItem>
|
|
21
|
+
<FlexItem>
|
|
22
|
+
<Text size="sm" weight="semibold">
|
|
23
|
+
React Scaffold
|
|
24
|
+
</Text>
|
|
25
|
+
</FlexItem>
|
|
26
|
+
</Flex>
|
|
27
|
+
</Box>
|
|
28
|
+
|
|
29
|
+
<Flex gap={2} align="center" className="-mt-2">
|
|
30
|
+
<FlexItem>
|
|
31
|
+
<Text size="sm" color="muted">
|
|
32
|
+
Created by
|
|
33
|
+
</Text>
|
|
34
|
+
</FlexItem>
|
|
35
|
+
<FlexItem>
|
|
36
|
+
<a
|
|
37
|
+
href={Links.CreatorGithub}
|
|
38
|
+
target="_blank"
|
|
39
|
+
rel="noreferrer"
|
|
40
|
+
className="font-semibold text-ink hover:text-primary transition-colors"
|
|
41
|
+
>
|
|
42
|
+
{Links.CreatorName}
|
|
43
|
+
</a>
|
|
44
|
+
</FlexItem>
|
|
45
|
+
</Flex>
|
|
46
|
+
|
|
47
|
+
<FlexItem>
|
|
48
|
+
<Text
|
|
49
|
+
weight="black"
|
|
50
|
+
font="plus-jakarta-sans"
|
|
51
|
+
size={{ base: '4xl', lg: '5xl', '2xl': '6xl' }}
|
|
52
|
+
className="tracking-tight bg-linear-to-r from-ink to-primary bg-clip-text text-transparent"
|
|
53
|
+
>
|
|
54
|
+
Start building with React Scaffold
|
|
55
|
+
</Text>
|
|
56
|
+
</FlexItem>
|
|
57
|
+
|
|
58
|
+
<FlexItem>
|
|
59
|
+
<Text size="base" color="muted" className="max-w-xl text-base leading-relaxed">
|
|
60
|
+
A modern, opinionated starter that keeps your codebase clean as it grows — feature-first
|
|
61
|
+
structure, shared UI primitives, and best-practice tooling out of the box.
|
|
62
|
+
</Text>
|
|
63
|
+
</FlexItem>
|
|
64
|
+
|
|
65
|
+
<FlexItem>
|
|
66
|
+
<Text size="sm" color="muted" className="max-w-xl">
|
|
67
|
+
This is the default starter page. Replace it with your product UI when you’re ready.
|
|
68
|
+
</Text>
|
|
69
|
+
</FlexItem>
|
|
70
|
+
|
|
71
|
+
<FlexItem>
|
|
72
|
+
<Flex gap={3} direction={{ base: 'column', md: 'row' }} className="mt-2">
|
|
73
|
+
<FlexItem>
|
|
74
|
+
<Button
|
|
75
|
+
className="rounded-full"
|
|
76
|
+
onClick={() => window.open(Links.AppGithub, '_blank', 'noopener,noreferrer')}
|
|
77
|
+
>
|
|
78
|
+
Open Project Repo
|
|
79
|
+
</Button>
|
|
80
|
+
</FlexItem>
|
|
81
|
+
|
|
82
|
+
<FlexItem>
|
|
83
|
+
<Button
|
|
84
|
+
variant="outline"
|
|
85
|
+
className="rounded-full"
|
|
86
|
+
onClick={() => window.open(Links.NpmPackage, '_blank', 'noopener,noreferrer')}
|
|
87
|
+
>
|
|
88
|
+
View NPM Package
|
|
89
|
+
</Button>
|
|
90
|
+
</FlexItem>
|
|
91
|
+
</Flex>
|
|
92
|
+
</FlexItem>
|
|
93
|
+
|
|
94
|
+
<FlexItem>
|
|
95
|
+
<Flex gap={2} align="center">
|
|
96
|
+
<FlexItem>
|
|
97
|
+
<IconLink
|
|
98
|
+
href={Links.AppGithub}
|
|
99
|
+
label="App Repo"
|
|
100
|
+
icon={<FaGithub className="size-4" />}
|
|
101
|
+
/>
|
|
102
|
+
</FlexItem>
|
|
103
|
+
<FlexItem>
|
|
104
|
+
<IconLink
|
|
105
|
+
href={Links.CliGithub}
|
|
106
|
+
label="CLI Repo"
|
|
107
|
+
icon={<FaGithub className="size-4" />}
|
|
108
|
+
/>
|
|
109
|
+
</FlexItem>
|
|
110
|
+
<FlexItem>
|
|
111
|
+
<IconLink href={Links.NpmPackage} label="NPM" icon={<FaNpm className="size-4" />} />
|
|
112
|
+
</FlexItem>
|
|
113
|
+
|
|
114
|
+
<>
|
|
115
|
+
<span className="text-border -mt-1">•</span>
|
|
116
|
+
<FlexItem>
|
|
117
|
+
<IconLink
|
|
118
|
+
href={Links.CreatorLinkedIn}
|
|
119
|
+
label="Creator"
|
|
120
|
+
icon={<FaLinkedin className="size-4" />}
|
|
121
|
+
/>
|
|
122
|
+
</FlexItem>
|
|
123
|
+
</>
|
|
124
|
+
</Flex>
|
|
125
|
+
</FlexItem>
|
|
126
|
+
</Flex>
|
|
127
|
+
|
|
128
|
+
<QuickStartPanel />
|
|
129
|
+
</Grid>
|
|
130
|
+
);
|
|
131
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Text } from '@/shared/ui';
|
|
3
|
+
|
|
4
|
+
export const IconLink = React.memo(({ href, icon, label }) => {
|
|
5
|
+
return (
|
|
6
|
+
<a
|
|
7
|
+
href={href}
|
|
8
|
+
target="_blank"
|
|
9
|
+
rel="noreferrer"
|
|
10
|
+
className="inline-flex items-center gap-2 text-muted hover:text-primary transition-colors"
|
|
11
|
+
>
|
|
12
|
+
<span className="text-primary">{icon}</span>
|
|
13
|
+
<Text size="sm" className="leading-none">
|
|
14
|
+
{label}
|
|
15
|
+
</Text>
|
|
16
|
+
</a>
|
|
17
|
+
);
|
|
18
|
+
});
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Box, Flex, FlexItem, Text } from '@/shared/ui';
|
|
3
|
+
import { RingSoft } from './RingSoft';
|
|
4
|
+
import { CodeLine } from './CodeLine';
|
|
5
|
+
import { Commands } from '../constants';
|
|
6
|
+
|
|
7
|
+
function StepLight({ label, children }) {
|
|
8
|
+
return (
|
|
9
|
+
<Box>
|
|
10
|
+
<Text className="text-sm font-semibold text-ink">{label}</Text>
|
|
11
|
+
<Box className="mt-2">{children}</Box>
|
|
12
|
+
</Box>
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const QuickStartPanel = React.memo(() => {
|
|
17
|
+
return (
|
|
18
|
+
<Box className="relative overflow-hidden rounded-2xl bg-surface/70 p-6 md:p-7 ring-1 ring-border">
|
|
19
|
+
<Box className="pointer-events-none absolute inset-0 opacity-100">
|
|
20
|
+
<RingSoft className="right-[-7rem] top-[-6rem]" size="22rem" />
|
|
21
|
+
<RingSoft className="right-[-5rem] top-[9rem]" size="14rem" />
|
|
22
|
+
<RingSoft className="right-[-3.5rem] top-[16rem]" size="6rem" />
|
|
23
|
+
</Box>
|
|
24
|
+
|
|
25
|
+
<Box className="relative z-10 space-y-6">
|
|
26
|
+
<Flex gap={1} direction="column">
|
|
27
|
+
<FlexItem>
|
|
28
|
+
<Text
|
|
29
|
+
size="base"
|
|
30
|
+
weight="bold"
|
|
31
|
+
font="plus-jakarta-sans"
|
|
32
|
+
className="tracking-tight bg-linear-to-r from-primary to-ink bg-clip-text text-transparent"
|
|
33
|
+
>
|
|
34
|
+
Quick start
|
|
35
|
+
</Text>
|
|
36
|
+
</FlexItem>
|
|
37
|
+
<FlexItem>
|
|
38
|
+
<Text size="sm" color="muted">
|
|
39
|
+
Create an app in seconds — clean architecture, ready to scale.
|
|
40
|
+
</Text>
|
|
41
|
+
</FlexItem>
|
|
42
|
+
</Flex>
|
|
43
|
+
|
|
44
|
+
<Flex gap={4} direction="column">
|
|
45
|
+
<FlexItem>
|
|
46
|
+
<StepLight label="1) Scaffold">
|
|
47
|
+
<CodeLine value={Commands.Scaffold} variant="light" />
|
|
48
|
+
</StepLight>
|
|
49
|
+
</FlexItem>
|
|
50
|
+
<FlexItem>
|
|
51
|
+
<StepLight label="2) Run">
|
|
52
|
+
<CodeLine value={Commands.Run} variant="light" />
|
|
53
|
+
</StepLight>
|
|
54
|
+
</FlexItem>
|
|
55
|
+
</Flex>
|
|
56
|
+
|
|
57
|
+
<Text size="sm" color="muted">
|
|
58
|
+
Tip: add your first feature inside your feature modules and delete this page when ready.
|
|
59
|
+
</Text>
|
|
60
|
+
</Box>
|
|
61
|
+
</Box>
|
|
62
|
+
);
|
|
63
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Box } from '@/shared/ui/Box';
|
|
3
|
+
import { cn } from '@/shared/libs';
|
|
4
|
+
|
|
5
|
+
export const RingSoft = React.memo(({ className, size }) => {
|
|
6
|
+
return (
|
|
7
|
+
<Box
|
|
8
|
+
className={cn('absolute rounded-full', className)}
|
|
9
|
+
style={{
|
|
10
|
+
width: size,
|
|
11
|
+
height: size,
|
|
12
|
+
border: '1px solid rgba(52, 82, 255, 0.22)',
|
|
13
|
+
}}
|
|
14
|
+
/>
|
|
15
|
+
);
|
|
16
|
+
});
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Divider } from './Divider';
|
|
3
|
+
import { CodeLine } from './CodeLine';
|
|
4
|
+
import { Box, Flex, FlexItem, Grid, GridItem, Text } from '@/shared/ui';
|
|
5
|
+
import { Commands } from '../constants';
|
|
6
|
+
|
|
7
|
+
function Block({ title, body }) {
|
|
8
|
+
return (
|
|
9
|
+
<Box>
|
|
10
|
+
<Text className="text-sm font-bold tracking-tight">{title}</Text>
|
|
11
|
+
<Text className="mt-2 text-muted leading-relaxed">{body}</Text>
|
|
12
|
+
</Box>
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function StorySections() {
|
|
17
|
+
return (
|
|
18
|
+
<Grid gap={{ base: 10, md: 12 }} columns={{ base: 1, md: 2 }}>
|
|
19
|
+
<GridItem>
|
|
20
|
+
<Flex gap={2} direction="column">
|
|
21
|
+
<FlexItem>
|
|
22
|
+
<Text
|
|
23
|
+
weight="black"
|
|
24
|
+
font="plus-jakarta-sans"
|
|
25
|
+
size={{ base: '2xl', md: '3xl' }}
|
|
26
|
+
className="tracking-tight bg-linear-to-r from-primary to-ink bg-clip-text text-transparent"
|
|
27
|
+
>
|
|
28
|
+
Why this exists
|
|
29
|
+
</Text>
|
|
30
|
+
</FlexItem>
|
|
31
|
+
|
|
32
|
+
<FlexItem className="max-w-2xl">
|
|
33
|
+
<Text size="base" color="muted">
|
|
34
|
+
Less starter bloat, more clarity. You get patterns that keep apps maintainable as
|
|
35
|
+
teams grow.
|
|
36
|
+
</Text>
|
|
37
|
+
</FlexItem>
|
|
38
|
+
</Flex>
|
|
39
|
+
</GridItem>
|
|
40
|
+
<GridItem className="space-y-7">
|
|
41
|
+
<Block
|
|
42
|
+
title="Project purpose"
|
|
43
|
+
body="React Scaffold helps teams start real apps fast—with structure, shared UI primitives, and a clean foundation for long-term maintainability."
|
|
44
|
+
/>
|
|
45
|
+
<Divider />
|
|
46
|
+
<Block
|
|
47
|
+
title="Problem it solves"
|
|
48
|
+
body="Most starters don’t scale. This scaffold enforces patterns so features stay isolated, UI stays consistent, and refactoring remains safe."
|
|
49
|
+
/>
|
|
50
|
+
<Divider />
|
|
51
|
+
<Box>
|
|
52
|
+
<Text size="sm" className="font-bold tracking-tight">
|
|
53
|
+
How to start
|
|
54
|
+
</Text>
|
|
55
|
+
<Box className="mt-3 space-y-3">
|
|
56
|
+
<CodeLine isStorySection value={Commands.Scaffold} />
|
|
57
|
+
<CodeLine isStorySection value={Commands.Run} />
|
|
58
|
+
</Box>
|
|
59
|
+
</Box>
|
|
60
|
+
</GridItem>
|
|
61
|
+
</Grid>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { SOLUTIONS } from '../constants';
|
|
3
|
+
import { Flex, FlexItem, Text } from '@/shared/ui';
|
|
4
|
+
|
|
5
|
+
export const WhatYouGet = React.memo(() => {
|
|
6
|
+
return (
|
|
7
|
+
<Flex gap={2} direction="column">
|
|
8
|
+
<FlexItem>
|
|
9
|
+
<Text
|
|
10
|
+
weight="black"
|
|
11
|
+
font="plus-jakarta-sans"
|
|
12
|
+
size={{ base: '2xl', md: '3xl' }}
|
|
13
|
+
className="tracking-tight bg-linear-to-r from-primary to-ink bg-clip-text text-transparent"
|
|
14
|
+
>
|
|
15
|
+
What you get
|
|
16
|
+
</Text>
|
|
17
|
+
</FlexItem>
|
|
18
|
+
|
|
19
|
+
<FlexItem className="max-w-2xl">
|
|
20
|
+
<Text size="base" color="muted">
|
|
21
|
+
A real-world scaffold: structure + UI foundations + modern stack.
|
|
22
|
+
</Text>
|
|
23
|
+
</FlexItem>
|
|
24
|
+
<FlexItem>
|
|
25
|
+
<Flex gap={2} className="mt-4 flex-wrap">
|
|
26
|
+
{SOLUTIONS.map((x) => (
|
|
27
|
+
<Flex
|
|
28
|
+
gap={2}
|
|
29
|
+
key={x.label}
|
|
30
|
+
align="center"
|
|
31
|
+
className="inline-flex items-center gap-2 rounded-full bg-surface/80 px-4 py-2"
|
|
32
|
+
>
|
|
33
|
+
<FlexItem>
|
|
34
|
+
<Text size="sm" color="muted">
|
|
35
|
+
{x.label}:
|
|
36
|
+
</Text>
|
|
37
|
+
</FlexItem>
|
|
38
|
+
<FlexItem>
|
|
39
|
+
<Text color="ink" weight="semibold">
|
|
40
|
+
{x.value}
|
|
41
|
+
</Text>
|
|
42
|
+
</FlexItem>
|
|
43
|
+
</Flex>
|
|
44
|
+
))}
|
|
45
|
+
</Flex>
|
|
46
|
+
</FlexItem>
|
|
47
|
+
</Flex>
|
|
48
|
+
);
|
|
49
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export const Links = {
|
|
2
|
+
CliGithub: 'https://github.com/arsalanirshad57/create-react-scaffold-cli',
|
|
3
|
+
AppGithub: 'https://github.com/arsalanirshad57/react-scaffold-app',
|
|
4
|
+
NpmPackage: 'https://www.npmjs.com/package/create-react-scaffold-cli',
|
|
5
|
+
|
|
6
|
+
CreatorName: 'Muhammad Arsalan',
|
|
7
|
+
CreatorGithub: 'https://github.com/arsalanirshad57',
|
|
8
|
+
CreatorLinkedIn: 'https://www.linkedin.com/in/heyarsalanhere/',
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const Commands = {
|
|
12
|
+
Scaffold: 'npx create-react-scaffold-cli',
|
|
13
|
+
Run: 'npm run dev',
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const SOLUTIONS = [
|
|
17
|
+
{ label: 'Architecture', value: 'Feature-first' },
|
|
18
|
+
{ label: 'UI System', value: 'Primitives + Radix' },
|
|
19
|
+
{ label: 'Data', value: 'React Query + Axios' },
|
|
20
|
+
{ label: 'Tooling', value: 'ESLint + Prettier + Husky' },
|
|
21
|
+
];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { welcomeRoutes } from './welcome.routes';
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Box } from '@/shared/ui';
|
|
3
|
+
import { Divider, Footer, Hero, StorySections, WhatYouGet } from '../components';
|
|
4
|
+
|
|
5
|
+
const WelcomePage = React.memo(() => {
|
|
6
|
+
return (
|
|
7
|
+
<Box className="min-h-screen bg-bg text-ink ">
|
|
8
|
+
<Box
|
|
9
|
+
className="mx-auto max-w-6xl"
|
|
10
|
+
padding={{ x: { base: 4, md: 6 }, y: { base: 10, md: 14 } }}
|
|
11
|
+
>
|
|
12
|
+
<Hero />
|
|
13
|
+
|
|
14
|
+
<Divider className="my-10 md:my-12" />
|
|
15
|
+
|
|
16
|
+
<WhatYouGet />
|
|
17
|
+
|
|
18
|
+
<Divider className="my-10 md:my-12" />
|
|
19
|
+
|
|
20
|
+
<StorySections />
|
|
21
|
+
|
|
22
|
+
<Footer />
|
|
23
|
+
</Box>
|
|
24
|
+
</Box>
|
|
25
|
+
);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
export default WelcomePage;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Layouts } from '@/shared/constants';
|
|
2
|
+
import { WelcomePage } from './pages';
|
|
3
|
+
import { WelcomeNavigation } from './constants';
|
|
4
|
+
|
|
5
|
+
export const welcomeRoutes = [
|
|
6
|
+
{
|
|
7
|
+
path: WelcomeNavigation.Root,
|
|
8
|
+
element: WelcomePage,
|
|
9
|
+
protected: false,
|
|
10
|
+
layout: Layouts.None,
|
|
11
|
+
},
|
|
12
|
+
];
|
|
@@ -2073,7 +2073,8 @@ export const size2xlLookup = {
|
|
|
2073
2073
|
|
|
2074
2074
|
export const colorLookup = {
|
|
2075
2075
|
primary: 'text-primary',
|
|
2076
|
-
|
|
2076
|
+
ink: 'text-ink',
|
|
2077
|
+
muted: 'text-muted',
|
|
2077
2078
|
success: 'text-green-500',
|
|
2078
2079
|
danger: 'text-red-500',
|
|
2079
2080
|
white: 'text-white',
|
|
@@ -2082,7 +2083,8 @@ export const colorLookup = {
|
|
|
2082
2083
|
|
|
2083
2084
|
export const color2XlLookup = {
|
|
2084
2085
|
primary: '2xl:text-primary',
|
|
2085
|
-
|
|
2086
|
+
ink: '2xl:text-ink',
|
|
2087
|
+
muted: 'xl:text-muted',
|
|
2086
2088
|
success: '2xl:text-green-500',
|
|
2087
2089
|
danger: '2xl:text-red-500',
|
|
2088
2090
|
white: '2xl:text-white',
|
|
@@ -2091,7 +2093,8 @@ export const color2XlLookup = {
|
|
|
2091
2093
|
|
|
2092
2094
|
export const colorXlLookup = {
|
|
2093
2095
|
primary: 'xl:text-primary',
|
|
2094
|
-
|
|
2096
|
+
ink: 'xl:text-ink',
|
|
2097
|
+
muted: 'xl:text-muted',
|
|
2095
2098
|
success: 'xl:text-green-500',
|
|
2096
2099
|
danger: 'xl:text-red-500',
|
|
2097
2100
|
white: 'xl:text-white',
|
|
@@ -2100,7 +2103,8 @@ export const colorXlLookup = {
|
|
|
2100
2103
|
|
|
2101
2104
|
export const colorLgLookup = {
|
|
2102
2105
|
primary: 'lg:text-primary',
|
|
2103
|
-
|
|
2106
|
+
ink: 'lg:text-ink',
|
|
2107
|
+
muted: 'lg:text-muted',
|
|
2104
2108
|
success: 'lg:text-green-500',
|
|
2105
2109
|
danger: 'lg:text-red-500',
|
|
2106
2110
|
white: 'lg:text-white',
|
|
@@ -2109,7 +2113,8 @@ export const colorLgLookup = {
|
|
|
2109
2113
|
|
|
2110
2114
|
export const colorMdLookup = {
|
|
2111
2115
|
primary: 'md:text-primary',
|
|
2112
|
-
|
|
2116
|
+
ink: 'md:text-ink',
|
|
2117
|
+
muted: 'md:text-muted',
|
|
2113
2118
|
success: 'md:text-green-500',
|
|
2114
2119
|
danger: 'md:text-red-500',
|
|
2115
2120
|
white: 'md:text-white',
|
|
@@ -2123,16 +2128,22 @@ export const bgColorLookup = {
|
|
|
2123
2128
|
danger: 'bg-red-500',
|
|
2124
2129
|
};
|
|
2125
2130
|
|
|
2126
|
-
export const fontFamilyLookup = {
|
|
2131
|
+
export const fontFamilyLookup = {
|
|
2132
|
+
'plus-jakarta-sans': 'font-plus-jakarta-sans',
|
|
2133
|
+
inter: 'font-inter',
|
|
2134
|
+
};
|
|
2127
2135
|
|
|
2128
|
-
export const fontFamilyMdLookup =
|
|
2129
|
-
|
|
2130
|
-
|
|
2136
|
+
export const fontFamilyMdLookup = {
|
|
2137
|
+
'plus-jakarta-sans': 'md:font-plus-jakarta-sans',
|
|
2138
|
+
inter: 'md:font-inter',
|
|
2139
|
+
};
|
|
2131
2140
|
|
|
2132
|
-
export const fontFamilyLgLookup =
|
|
2133
|
-
|
|
2134
|
-
|
|
2141
|
+
export const fontFamilyLgLookup = {
|
|
2142
|
+
'plus-jakarta-sans': 'lg:font-plus-jakarta-sans',
|
|
2143
|
+
inter: 'lg:font-inter',
|
|
2144
|
+
};
|
|
2135
2145
|
|
|
2136
|
-
export const fontFamilyXlLookup =
|
|
2137
|
-
|
|
2138
|
-
|
|
2146
|
+
export const fontFamilyXlLookup = {
|
|
2147
|
+
'plus-jakarta-sans': 'xl:font-plus-jakarta-sans',
|
|
2148
|
+
inter: 'xl:font-inter',
|
|
2149
|
+
};
|
|
@@ -27,10 +27,6 @@ import {
|
|
|
27
27
|
paddingXXlLookup,
|
|
28
28
|
paddingYXlLookup,
|
|
29
29
|
marginXlgLookup,
|
|
30
|
-
AnimationLookup,
|
|
31
|
-
AnimationMdLookup,
|
|
32
|
-
AnimationLgLookup,
|
|
33
|
-
AnimationXlLookup,
|
|
34
30
|
radiusLookup,
|
|
35
31
|
radiusMdLookup,
|
|
36
32
|
radiusLgLookup,
|
|
@@ -122,15 +118,11 @@ import { cn } from '../libs';
|
|
|
122
118
|
* | Record<Axis, Record<Breakpoint, Padding>>} [padding]
|
|
123
119
|
* @property {Width | Record<Breakpoint, Width>} [width]
|
|
124
120
|
* @property {Radius} [radius]
|
|
125
|
-
* @property {Animation} [animation]
|
|
126
121
|
* @param {React.ComponentProps<'div'> & ComponentProps} props
|
|
127
122
|
* @returns {JSX.Element}
|
|
128
123
|
*/
|
|
129
124
|
|
|
130
|
-
const Component = (
|
|
131
|
-
{ hide, grow, margin, radius, padding, width, animation, className, ...rest },
|
|
132
|
-
ref
|
|
133
|
-
) => {
|
|
125
|
+
const Component = ({ hide, grow, margin, radius, padding, width, className, ...rest }, ref) => {
|
|
134
126
|
return (
|
|
135
127
|
<div
|
|
136
128
|
ref={ref}
|
|
@@ -180,11 +172,6 @@ const Component = (
|
|
|
180
172
|
getClassName(width?.base, widthLookup),
|
|
181
173
|
getClassName(width?.md, widthMdLookup),
|
|
182
174
|
getClassName(width?.lg, widthLgLookup),
|
|
183
|
-
typeof font !== 'object' && getClassName(animation, AnimationLookup),
|
|
184
|
-
getClassName(animation?.base, AnimationLookup),
|
|
185
|
-
getClassName(animation?.md, AnimationMdLookup),
|
|
186
|
-
getClassName(animation?.lg, AnimationLgLookup),
|
|
187
|
-
getClassName(animation?.xl, AnimationXlLookup),
|
|
188
175
|
typeof radius !== 'object' && getClassName(radius, radiusLookup),
|
|
189
176
|
getClassName(radius?.base, radiusLookup),
|
|
190
177
|
getClassName(radius?.md, radiusMdLookup),
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { PiSpinnerBold } from 'react-icons/pi';
|
|
3
3
|
import {
|
|
4
|
-
AnimationLookup,
|
|
5
4
|
colorLookup,
|
|
6
5
|
fieldSize2XlLookup,
|
|
7
6
|
fieldSizeLgLookup,
|
|
@@ -25,13 +24,14 @@ import {
|
|
|
25
24
|
radiusXlLookup,
|
|
26
25
|
} from '../theme';
|
|
27
26
|
import { getClassName, memo } from '../utils';
|
|
27
|
+
import { cn } from '../libs';
|
|
28
28
|
|
|
29
29
|
const variantLookup = {
|
|
30
30
|
primary:
|
|
31
31
|
'text-primary-foreground bg-primary hover:bg-primary/90 disabled:text-primary-foreground/70 disabled:bg-primary/50',
|
|
32
32
|
outline:
|
|
33
33
|
'border text-primary bg-primary-foreground hover:bg-neutral-50 disabled:text-primary/70 disabled:bg-neutral-50/50',
|
|
34
|
-
pill: 'text-primary-foreground bg-primary hover:bg-primary/90 disabled:text-primary-foreground/70 disabled:bg-primary/50
|
|
34
|
+
pill: 'text-primary-foreground bg-primary hover:bg-primary/90 disabled:text-primary-foreground/70 disabled:bg-primary/50',
|
|
35
35
|
};
|
|
36
36
|
|
|
37
37
|
/**
|
|
@@ -97,7 +97,6 @@ const Component = (
|
|
|
97
97
|
color,
|
|
98
98
|
isLoading,
|
|
99
99
|
className,
|
|
100
|
-
animation,
|
|
101
100
|
type = 'button',
|
|
102
101
|
children,
|
|
103
102
|
...rest
|
|
@@ -108,7 +107,8 @@ const Component = (
|
|
|
108
107
|
<button
|
|
109
108
|
ref={ref}
|
|
110
109
|
className={cn(
|
|
111
|
-
'h-
|
|
110
|
+
'h-14 px-8 inline-flex w-full items-center justify-center gap-2 transition-colors disabled:cursor-not-allowed text-primary-foreground cursor-pointer!',
|
|
111
|
+
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30',
|
|
112
112
|
typeof fontSize !== 'object' && getClassName(fontSize, fontSizeLookup),
|
|
113
113
|
getClassName(fontSize?.base, fontSizeLookup),
|
|
114
114
|
getClassName(fontSize?.md, fontSizeMdLookup),
|
|
@@ -135,7 +135,6 @@ const Component = (
|
|
|
135
135
|
variantLookup[variant],
|
|
136
136
|
fontFamilyLookup[font],
|
|
137
137
|
colorLookup[color],
|
|
138
|
-
AnimationLookup[animation],
|
|
139
138
|
className
|
|
140
139
|
)}
|
|
141
140
|
type={type}
|
|
@@ -1,46 +1,40 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
|
|
3
3
|
import { FaChevronRight } from 'react-icons/fa';
|
|
4
|
-
import { memo } from '
|
|
5
|
-
import { cn } from '
|
|
4
|
+
import { memo } from '../utils';
|
|
5
|
+
import { cn } from '../libs';
|
|
6
6
|
|
|
7
7
|
const DropdownMenuRoot = DropdownMenuPrimitive.Root;
|
|
8
8
|
|
|
9
9
|
const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
|
|
10
10
|
|
|
11
|
-
const DropdownMenuSubTrigger = React.forwardRef(
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
);
|
|
27
|
-
DropdownMenuSubTrigger.displayName =
|
|
28
|
-
DropdownMenuPrimitive.SubTrigger.displayName;
|
|
11
|
+
const DropdownMenuSubTrigger = React.forwardRef(({ className, inset, children, ...props }, ref) => (
|
|
12
|
+
<DropdownMenuPrimitive.SubTrigger
|
|
13
|
+
ref={ref}
|
|
14
|
+
className={cn(
|
|
15
|
+
'flex cursor-default gap-2 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
|
|
16
|
+
inset && 'pl-8',
|
|
17
|
+
className
|
|
18
|
+
)}
|
|
19
|
+
{...props}
|
|
20
|
+
>
|
|
21
|
+
{children}
|
|
22
|
+
<FaChevronRight className="ml-auto" />
|
|
23
|
+
</DropdownMenuPrimitive.SubTrigger>
|
|
24
|
+
));
|
|
25
|
+
DropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName;
|
|
29
26
|
|
|
30
|
-
const DropdownMenuSubContent = React.forwardRef(
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
);
|
|
42
|
-
DropdownMenuSubContent.displayName =
|
|
43
|
-
DropdownMenuPrimitive.SubContent.displayName;
|
|
27
|
+
const DropdownMenuSubContent = React.forwardRef(({ className, ...props }, ref) => (
|
|
28
|
+
<DropdownMenuPrimitive.SubContent
|
|
29
|
+
ref={ref}
|
|
30
|
+
className={cn(
|
|
31
|
+
'z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
|
32
|
+
className
|
|
33
|
+
)}
|
|
34
|
+
{...props}
|
|
35
|
+
/>
|
|
36
|
+
));
|
|
37
|
+
DropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName;
|
|
44
38
|
|
|
45
39
|
const DropdownMenuContent = React.forwardRef(
|
|
46
40
|
({ className, sideOffset = 4, contentClassName, ...props }, ref) => (
|
|
@@ -61,53 +55,40 @@ const DropdownMenuContent = React.forwardRef(
|
|
|
61
55
|
);
|
|
62
56
|
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
|
|
63
57
|
|
|
64
|
-
const DropdownMenuItem = React.forwardRef(
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
)
|
|
76
|
-
);
|
|
58
|
+
const DropdownMenuItem = React.forwardRef(({ className, inset, ...props }, ref) => (
|
|
59
|
+
<DropdownMenuPrimitive.Item
|
|
60
|
+
ref={ref}
|
|
61
|
+
className={cn(
|
|
62
|
+
'relative flex select-none items-center gap-2 font-inter cursor-pointer rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0',
|
|
63
|
+
inset && 'pl-8',
|
|
64
|
+
className
|
|
65
|
+
)}
|
|
66
|
+
{...props}
|
|
67
|
+
/>
|
|
68
|
+
));
|
|
77
69
|
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
|
|
78
70
|
|
|
79
|
-
const DropdownMenuLabel = React.forwardRef(
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
className
|
|
87
|
-
)}
|
|
88
|
-
{...props}
|
|
89
|
-
/>
|
|
90
|
-
)
|
|
91
|
-
);
|
|
71
|
+
const DropdownMenuLabel = React.forwardRef(({ className, inset, ...props }, ref) => (
|
|
72
|
+
<DropdownMenuPrimitive.Label
|
|
73
|
+
ref={ref}
|
|
74
|
+
className={cn('px-2 py-1.5 text-sm font-semibold font-inter', inset && 'pl-8', className)}
|
|
75
|
+
{...props}
|
|
76
|
+
/>
|
|
77
|
+
));
|
|
92
78
|
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
|
|
93
79
|
|
|
94
|
-
const DropdownMenuSeparator = React.forwardRef(
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
)
|
|
102
|
-
);
|
|
80
|
+
const DropdownMenuSeparator = React.forwardRef(({ className, ...props }, ref) => (
|
|
81
|
+
<DropdownMenuPrimitive.Separator
|
|
82
|
+
ref={ref}
|
|
83
|
+
className={cn('-mx-1 my-1 h-px bg-muted', className)}
|
|
84
|
+
{...props}
|
|
85
|
+
/>
|
|
86
|
+
));
|
|
103
87
|
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
|
|
104
88
|
|
|
105
89
|
const DropdownMenuShortcut = ({ className, ...props }) => {
|
|
106
90
|
return (
|
|
107
|
-
<span
|
|
108
|
-
className={cn('ml-auto text-xs tracking-widest opacity-60', className)}
|
|
109
|
-
{...props}
|
|
110
|
-
/>
|
|
91
|
+
<span className={cn('ml-auto text-xs tracking-widest opacity-60', className)} {...props} />
|
|
111
92
|
);
|
|
112
93
|
};
|
|
113
94
|
DropdownMenuShortcut.displayName = 'DropdownMenuShortcut';
|
|
@@ -121,19 +102,9 @@ DropdownMenuShortcut.displayName = 'DropdownMenuShortcut';
|
|
|
121
102
|
* @returns {JSX.Element}
|
|
122
103
|
*/
|
|
123
104
|
|
|
124
|
-
const Component = ({
|
|
125
|
-
open,
|
|
126
|
-
onOpenChange,
|
|
127
|
-
content,
|
|
128
|
-
children,
|
|
129
|
-
contentClassName,
|
|
130
|
-
}) => {
|
|
105
|
+
const Component = ({ open, onOpenChange, content, children, contentClassName }) => {
|
|
131
106
|
return (
|
|
132
|
-
<DropdownMenuRoot
|
|
133
|
-
className="border w-full "
|
|
134
|
-
open={open}
|
|
135
|
-
onOpenChange={onOpenChange}
|
|
136
|
-
>
|
|
107
|
+
<DropdownMenuRoot className="border w-full " open={open} onOpenChange={onOpenChange}>
|
|
137
108
|
<DropdownMenuTrigger className="w-full">{children}</DropdownMenuTrigger>
|
|
138
109
|
<DropdownMenuContent align="end" contentClassName={contentClassName}>
|
|
139
110
|
{content}
|
|
@@ -144,9 +115,4 @@ const Component = ({
|
|
|
144
115
|
|
|
145
116
|
const DropdownMenu = memo(Component);
|
|
146
117
|
|
|
147
|
-
export {
|
|
148
|
-
DropdownMenu,
|
|
149
|
-
DropdownMenuLabel,
|
|
150
|
-
DropdownMenuSeparator,
|
|
151
|
-
DropdownMenuItem,
|
|
152
|
-
};
|
|
118
|
+
export { DropdownMenu, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuItem };
|
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
colorXlLookup,
|
|
25
25
|
} from '../theme';
|
|
26
26
|
import { cn } from '../libs';
|
|
27
|
-
import { getClassName } from '../utils';
|
|
27
|
+
import { getClassName, memo } from '../utils';
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
30
|
* @typedef {'base' | 'md' | 'lg' | 'xl' | '2xl'} Breakpoint
|
|
@@ -57,9 +57,9 @@ import { getClassName } from '../utils';
|
|
|
57
57
|
* | 'black'} Weight
|
|
58
58
|
*
|
|
59
59
|
*
|
|
60
|
-
* @typedef {''} Font
|
|
60
|
+
* @typedef {'plus-jakarta-sans' | 'inter'} Font
|
|
61
61
|
*
|
|
62
|
-
* @typedef {'primary' | '
|
|
62
|
+
* @typedef {'primary' | 'ink' | 'muted' | 'success' | 'danger' | 'white' | 'black'} Color
|
|
63
63
|
*
|
|
64
64
|
* @typedef {object} ComponentProps
|
|
65
65
|
* @property {Size | Record<Breakpoint, Size>} [size]
|
|
@@ -1 +1,18 @@
|
|
|
1
|
+
export { Box } from './Box';
|
|
2
|
+
export { Button } from './Button';
|
|
3
|
+
export { Checkbox } from './Checkbox';
|
|
4
|
+
export { Flex } from './Flex';
|
|
5
|
+
export { FlexItem } from './FlexItem';
|
|
6
|
+
export { FormField } from './FormField';
|
|
7
|
+
export { Grid } from './Grid';
|
|
8
|
+
export { GridItem } from './GridItem';
|
|
9
|
+
export { Modal } from './Modal';
|
|
1
10
|
export { Toaster } from './Toaster';
|
|
11
|
+
export { Text } from './Text';
|
|
12
|
+
export { Scrollable } from './Scrollable';
|
|
13
|
+
export {
|
|
14
|
+
DropdownMenu,
|
|
15
|
+
DropdownMenuItem,
|
|
16
|
+
DropdownMenuLabel,
|
|
17
|
+
DropdownMenuSeparator,
|
|
18
|
+
} from './DropdownMenu';
|