react-email 1.0.11 → 1.1.1
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/.eslintrc.js +4 -0
- package/.prettierrc.js +3 -0
- package/dist/_preview/components.d.ts +4 -0
- package/dist/_preview/components.js +41 -0
- package/dist/_preview/pages.d.ts +9 -0
- package/dist/_preview/pages.js +22 -0
- package/dist/_preview/root.d.ts +4 -0
- package/dist/_preview/root.js +25 -0
- package/dist/_preview/styles.d.ts +4 -0
- package/dist/_preview/styles.js +9 -0
- package/dist/_preview/utils.d.ts +4 -0
- package/dist/_preview/utils.js +17 -0
- package/dist/commands/dev.d.ts +1 -0
- package/dist/commands/dev.js +135 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -29
- package/dist/utils/check-directory-exist.d.ts +1 -0
- package/dist/utils/check-directory-exist.js +9 -0
- package/dist/utils/check-empty-directory.d.ts +1 -0
- package/dist/utils/check-empty-directory.js +12 -0
- package/dist/utils/contants.d.ts +11 -0
- package/dist/utils/contants.js +24 -0
- package/dist/utils/create-directory.d.ts +1 -0
- package/dist/utils/create-directory.js +9 -0
- package/dist/utils/watcher.d.ts +2 -0
- package/dist/utils/watcher.js +22 -0
- package/package.json +6 -6
- package/preview/package.json +3 -18
- package/preview/src/components/layout.tsx +9 -44
- package/preview/src/components/sidebar.tsx +70 -66
- package/preview/src/components/topbar.tsx +44 -2
- package/preview/src/pages/_app.tsx +0 -6
- package/preview/src/pages/index.tsx +19 -4
- package/preview/src/pages/preview/[slug].tsx +13 -2
- package/scripts/prepare-preview.ts +177 -0
- package/source/_preview/components.ts +47 -0
- package/source/_preview/pages.ts +23 -0
- package/source/_preview/root.ts +27 -0
- package/source/_preview/styles.ts +6 -0
- package/source/_preview/utils.ts +16 -0
- package/source/commands/dev.ts +165 -0
- package/source/index.ts +16 -0
- package/source/utils/check-directory-exist.ts +4 -0
- package/source/utils/check-empty-directory.ts +6 -0
- package/source/utils/contants.ts +38 -0
- package/source/utils/create-directory.ts +4 -0
- package/source/utils/watcher.ts +22 -0
- package/tsconfig.json +36 -0
- package/preview/.next/BUILD_ID +0 -1
- package/preview/.next/build-manifest.json +0 -42
- package/preview/.next/cache/.tsbuildinfo +0 -1
- package/preview/.next/cache/next-server.js.nft.json +0 -290
- package/preview/.next/cache/webpack/client-development/0.pack +0 -0
- package/preview/.next/cache/webpack/client-development/1.pack +0 -0
- package/preview/.next/cache/webpack/client-development/10.pack +0 -0
- package/preview/.next/cache/webpack/client-development/11.pack +0 -0
- package/preview/.next/cache/webpack/client-development/12.pack +0 -0
- package/preview/.next/cache/webpack/client-development/13.pack +0 -0
- package/preview/.next/cache/webpack/client-development/14.pack +0 -0
- package/preview/.next/cache/webpack/client-development/15.pack +0 -0
- package/preview/.next/cache/webpack/client-development/16.pack +0 -0
- package/preview/.next/cache/webpack/client-development/2.pack +0 -0
- package/preview/.next/cache/webpack/client-development/3.pack +0 -0
- package/preview/.next/cache/webpack/client-development/4.pack +0 -0
- package/preview/.next/cache/webpack/client-development/5.pack +0 -0
- package/preview/.next/cache/webpack/client-development/6.pack +0 -0
- package/preview/.next/cache/webpack/client-development/7.pack +0 -0
- package/preview/.next/cache/webpack/client-development/8.pack +0 -0
- package/preview/.next/cache/webpack/client-development/9.pack +0 -0
- package/preview/.next/cache/webpack/client-development/index.pack +0 -0
- package/preview/.next/cache/webpack/client-development/index.pack.old +0 -0
- package/preview/.next/cache/webpack/client-production/0.pack +0 -0
- package/preview/.next/cache/webpack/client-production/index.pack +0 -0
- package/preview/.next/cache/webpack/server-development/0.pack +0 -0
- package/preview/.next/cache/webpack/server-development/1.pack +0 -0
- package/preview/.next/cache/webpack/server-development/10.pack +0 -0
- package/preview/.next/cache/webpack/server-development/11.pack +0 -0
- package/preview/.next/cache/webpack/server-development/12.pack +0 -0
- package/preview/.next/cache/webpack/server-development/13.pack +0 -0
- package/preview/.next/cache/webpack/server-development/14.pack +0 -0
- package/preview/.next/cache/webpack/server-development/15.pack +0 -0
- package/preview/.next/cache/webpack/server-development/16.pack +0 -0
- package/preview/.next/cache/webpack/server-development/2.pack +0 -0
- package/preview/.next/cache/webpack/server-development/3.pack +0 -0
- package/preview/.next/cache/webpack/server-development/4.pack +0 -0
- package/preview/.next/cache/webpack/server-development/5.pack +0 -0
- package/preview/.next/cache/webpack/server-development/6.pack +0 -0
- package/preview/.next/cache/webpack/server-development/7.pack +0 -0
- package/preview/.next/cache/webpack/server-development/8.pack +0 -0
- package/preview/.next/cache/webpack/server-development/9.pack +0 -0
- package/preview/.next/cache/webpack/server-development/index.pack +0 -0
- package/preview/.next/cache/webpack/server-development/index.pack.old +0 -0
- package/preview/.next/cache/webpack/server-production/0.pack +0 -0
- package/preview/.next/cache/webpack/server-production/index.pack +0 -0
- package/preview/.next/export-marker.json +0 -6
- package/preview/.next/images-manifest.json +0 -22
- package/preview/.next/next-server.js.nft.json +0 -290
- package/preview/.next/package.json +0 -3
- package/preview/.next/prerender-manifest.json +0 -24
- package/preview/.next/react-loadable-manifest.json +0 -1
- package/preview/.next/required-server-files.json +0 -113
- package/preview/.next/routes-manifest.json +0 -48
- package/preview/.next/server/chunks/664.js +0 -3836
- package/preview/.next/server/chunks/676.js +0 -35
- package/preview/.next/server/chunks/759.js +0 -1548
- package/preview/.next/server/chunks/font-manifest.json +0 -6
- package/preview/.next/server/font-loader-manifest.js +0 -4
- package/preview/.next/server/font-loader-manifest.json +0 -6
- package/preview/.next/server/font-manifest.json +0 -6
- package/preview/.next/server/middleware-build-manifest.js +0 -42
- package/preview/.next/server/middleware-manifest.json +0 -6
- package/preview/.next/server/middleware-react-loadable-manifest.js +0 -1
- package/preview/.next/server/pages/404.html +0 -12
- package/preview/.next/server/pages/500.html +0 -12
- package/preview/.next/server/pages/_app.js +0 -115
- package/preview/.next/server/pages/_app.js.nft.json +0 -29
- package/preview/.next/server/pages/_document.js +0 -1335
- package/preview/.next/server/pages/_document.js.nft.json +0 -33
- package/preview/.next/server/pages/_error.js +0 -195
- package/preview/.next/server/pages/_error.js.nft.json +0 -23
- package/preview/.next/server/pages/index.html +0 -1
- package/preview/.next/server/pages/index.js +0 -310
- package/preview/.next/server/pages/index.js.nft.json +0 -132
- package/preview/.next/server/pages/index.json +0 -1
- package/preview/.next/server/pages/preview/[slug].html +0 -1
- package/preview/.next/server/pages/preview/[slug].js +0 -388
- package/preview/.next/server/pages/preview/[slug].js.nft.json +0 -179
- package/preview/.next/server/pages-manifest.json +0 -8
- package/preview/.next/server/webpack-runtime.js +0 -213
- package/preview/.next/static/cY9PAzmXyKOoOW2gY4i5N/_buildManifest.js +0 -17
- package/preview/.next/static/cY9PAzmXyKOoOW2gY4i5N/_ssgManifest.js +0 -2
- package/preview/.next/static/chunks/727-0b09744222e89df8.js +0 -4402
- package/preview/.next/static/chunks/759-cdb5c7b41d03d871.js +0 -1135
- package/preview/.next/static/chunks/framework-3b5a00d5d7e8d93b.js +0 -9188
- package/preview/.next/static/chunks/main-50de763069eba4b2.js +0 -5078
- package/preview/.next/static/chunks/pages/_app-76f7305c0a91d681.js +0 -116
- package/preview/.next/static/chunks/pages/_error-8353112a01355ec2.js +0 -19
- package/preview/.next/static/chunks/pages/index-cd725955236a17bd.js +0 -44
- package/preview/.next/static/chunks/pages/preview/[slug]-e24c537be91754b2.js +0 -57
- package/preview/.next/static/chunks/polyfills-c67a75d1b6f99dc8.js +0 -6342
- package/preview/.next/static/chunks/webpack-0b5d8249fb15f5f3.js +0 -141
- package/preview/.next/static/css/8543b02f1dad7784.css +0 -3
- package/preview/.next/static/media/2aaf0723e720e8b9.p.woff2 +0 -0
- package/preview/.next/static/media/9c4f34569c9b36ca.woff2 +0 -0
- package/preview/.next/static/media/ae9ae6716d4f8bf8.woff2 +0 -0
- package/preview/.next/static/media/b1db3e28af9ef94a.woff2 +0 -0
- package/preview/.next/static/media/b967158bc7d7a9fb.woff2 +0 -0
- package/preview/.next/static/media/c0f5ec5bbf5913b7.woff2 +0 -0
- package/preview/.next/static/media/d1d9458b69004127.woff2 +0 -0
- package/preview/.next/trace +0 -7
- package/preview/yarn.lock +0 -1434
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { Topbar } from './topbar';
|
|
3
3
|
import { Sidebar } from './sidebar';
|
|
4
|
-
import * as ToggleGroup from '@radix-ui/react-toggle-group';
|
|
5
|
-
import classnames from 'classnames';
|
|
6
4
|
|
|
7
5
|
type LayoutElement = React.ElementRef<'div'>;
|
|
8
6
|
type RootProps = React.ComponentPropsWithoutRef<'div'>;
|
|
@@ -23,48 +21,15 @@ export const Layout = React.forwardRef<LayoutElement, Readonly<LayoutProps>>(
|
|
|
23
21
|
<div className="flex justify-between h-screen">
|
|
24
22
|
<Sidebar navItems={navItems} />
|
|
25
23
|
<main className="w-full bg-slate-2">
|
|
26
|
-
{title &&
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
if (!value) {
|
|
36
|
-
return setViewMode('desktop');
|
|
37
|
-
}
|
|
38
|
-
setViewMode(value);
|
|
39
|
-
}}
|
|
40
|
-
>
|
|
41
|
-
<ToggleGroup.Item
|
|
42
|
-
className={classnames(
|
|
43
|
-
'text-sm text-slate-11 rounded px-1.5 py-0.5',
|
|
44
|
-
{
|
|
45
|
-
'text-slate-12 bg-slate-3 font-medium':
|
|
46
|
-
viewMode === 'desktop',
|
|
47
|
-
},
|
|
48
|
-
)}
|
|
49
|
-
value="desktop"
|
|
50
|
-
>
|
|
51
|
-
Desktop
|
|
52
|
-
</ToggleGroup.Item>
|
|
53
|
-
<ToggleGroup.Item
|
|
54
|
-
className={classnames(
|
|
55
|
-
'text-sm text-slate-11 rounded px-1.5 py-0.5',
|
|
56
|
-
{
|
|
57
|
-
'text-slate-12 bg-slate-3 font-medium':
|
|
58
|
-
viewMode === 'source',
|
|
59
|
-
},
|
|
60
|
-
)}
|
|
61
|
-
value="source"
|
|
62
|
-
>
|
|
63
|
-
Source
|
|
64
|
-
</ToggleGroup.Item>
|
|
65
|
-
</ToggleGroup.Root>
|
|
66
|
-
)}
|
|
67
|
-
<div className="max-w-[600px] mx-auto">{children}</div>
|
|
24
|
+
{title && (
|
|
25
|
+
<Topbar
|
|
26
|
+
title={title}
|
|
27
|
+
viewMode={viewMode}
|
|
28
|
+
setViewMode={setViewMode}
|
|
29
|
+
/>
|
|
30
|
+
)}
|
|
31
|
+
<div className="relative h-[calc(100vh_-_70px)] overflow-auto">
|
|
32
|
+
<div className="mx-auto">{children}</div>
|
|
68
33
|
</div>
|
|
69
34
|
</main>
|
|
70
35
|
</div>
|
|
@@ -20,20 +20,20 @@ export const Sidebar = React.forwardRef<SidebarElement, Readonly<SidebarProps>>(
|
|
|
20
20
|
return (
|
|
21
21
|
<aside
|
|
22
22
|
ref={forwardedRef}
|
|
23
|
-
className="px-6 w-[275px] flex flex-col gap-4 border-r border-slate-6"
|
|
23
|
+
className="px-6 min-w-[275px] max-w-[275px] flex flex-col gap-4 border-r border-slate-6"
|
|
24
24
|
{...props}
|
|
25
25
|
>
|
|
26
26
|
<div className="h-[70px] flex items-center">
|
|
27
27
|
<Logo />
|
|
28
28
|
</div>
|
|
29
29
|
|
|
30
|
-
<Heading as="h2" color="gray" size="1" weight="medium">
|
|
31
|
-
Email
|
|
32
|
-
</Heading>
|
|
33
|
-
|
|
34
30
|
<nav className="flex flex-col gap-4">
|
|
35
31
|
<Collapsible.Root defaultOpen>
|
|
36
|
-
<Collapsible.Trigger
|
|
32
|
+
<Collapsible.Trigger
|
|
33
|
+
className={classnames('flex items-center gap-1', {
|
|
34
|
+
'cursor-default': navItems && navItems.length === 0,
|
|
35
|
+
})}
|
|
36
|
+
>
|
|
37
37
|
<svg
|
|
38
38
|
className="text-slate-11"
|
|
39
39
|
width="24"
|
|
@@ -62,71 +62,75 @@ export const Sidebar = React.forwardRef<SidebarElement, Readonly<SidebarProps>>(
|
|
|
62
62
|
<Heading as="h3" color="white" size="2" weight="medium">
|
|
63
63
|
All emails
|
|
64
64
|
</Heading>
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
65
|
+
{navItems && navItems.length > 0 && (
|
|
66
|
+
<svg
|
|
67
|
+
className="text-slate-11"
|
|
68
|
+
width="24"
|
|
69
|
+
height="24"
|
|
70
|
+
viewBox="0 0 24 24"
|
|
71
|
+
fill="none"
|
|
72
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
73
|
+
>
|
|
74
|
+
<path
|
|
75
|
+
d="M12 15L8.5359 9.75L15.4641 9.75L12 15Z"
|
|
76
|
+
fill="currentColor"
|
|
77
|
+
/>
|
|
78
|
+
</svg>
|
|
79
|
+
)}
|
|
78
80
|
</div>
|
|
79
81
|
</Collapsible.Trigger>
|
|
80
82
|
|
|
81
|
-
|
|
82
|
-
<
|
|
83
|
+
{navItems && navItems.length > 0 && (
|
|
84
|
+
<Collapsible.Content className="relative mt-3">
|
|
85
|
+
<div className="absolute left-2.5 w-px h-full bg-slate-6" />
|
|
83
86
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
>
|
|
97
|
-
{query.slug === item && (
|
|
98
|
-
<div className="h-5 bg-cyan-11 w-px absolute left-2.5" />
|
|
99
|
-
)}
|
|
100
|
-
<svg
|
|
101
|
-
width="24"
|
|
102
|
-
height="24"
|
|
103
|
-
viewBox="0 0 24 24"
|
|
104
|
-
fill="none"
|
|
105
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
87
|
+
<div className="py-2 flex flex-col gap-1.5 truncate">
|
|
88
|
+
{navItems &&
|
|
89
|
+
navItems.map((item) => (
|
|
90
|
+
<Link key={item} href={`/preview/${item}`}>
|
|
91
|
+
<span
|
|
92
|
+
className={classnames(
|
|
93
|
+
'text-[14px] flex items-center font-medium gap-2 h-8 w-full pl-4 pr-2 rounded-md text-slate-11',
|
|
94
|
+
{
|
|
95
|
+
'bg-cyan-3 text-cyan-11': query.slug === item,
|
|
96
|
+
'hover:text-slate-12': query.slug !== item,
|
|
97
|
+
},
|
|
98
|
+
)}
|
|
106
99
|
>
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
100
|
+
{query.slug === item && (
|
|
101
|
+
<div className="h-5 bg-cyan-11 w-px absolute left-2.5" />
|
|
102
|
+
)}
|
|
103
|
+
<svg
|
|
104
|
+
width="24"
|
|
105
|
+
height="24"
|
|
106
|
+
viewBox="0 0 24 24"
|
|
107
|
+
fill="none"
|
|
108
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
109
|
+
>
|
|
110
|
+
<path
|
|
111
|
+
d="M7.75 19.25H16.25C17.3546 19.25 18.25 18.3546 18.25 17.25V9L14 4.75H7.75C6.64543 4.75 5.75 5.64543 5.75 6.75V17.25C5.75 18.3546 6.64543 19.25 7.75 19.25Z"
|
|
112
|
+
stroke="currentColor"
|
|
113
|
+
strokeOpacity="0.927"
|
|
114
|
+
strokeWidth="1.5"
|
|
115
|
+
strokeLinecap="round"
|
|
116
|
+
strokeLinejoin="round"
|
|
117
|
+
/>
|
|
118
|
+
<path
|
|
119
|
+
d="M18 9.25H13.75V5"
|
|
120
|
+
stroke="currentColor"
|
|
121
|
+
strokeOpacity="0.927"
|
|
122
|
+
strokeWidth="1.5"
|
|
123
|
+
strokeLinecap="round"
|
|
124
|
+
strokeLinejoin="round"
|
|
125
|
+
/>
|
|
126
|
+
</svg>
|
|
127
|
+
{item}
|
|
128
|
+
</span>
|
|
129
|
+
</Link>
|
|
130
|
+
))}
|
|
131
|
+
</div>
|
|
132
|
+
</Collapsible.Content>
|
|
133
|
+
)}
|
|
130
134
|
</Collapsible.Root>
|
|
131
135
|
</nav>
|
|
132
136
|
</aside>
|
|
@@ -2,16 +2,19 @@ import * as React from 'react';
|
|
|
2
2
|
import classnames from 'classnames';
|
|
3
3
|
import { Heading } from './heading';
|
|
4
4
|
import { Text } from './text';
|
|
5
|
+
import * as ToggleGroup from '@radix-ui/react-toggle-group';
|
|
5
6
|
|
|
6
7
|
type TopbarElement = React.ElementRef<'header'>;
|
|
7
8
|
type RootProps = React.ComponentPropsWithoutRef<'header'>;
|
|
8
9
|
|
|
9
10
|
interface TopbarProps extends RootProps {
|
|
10
11
|
title: string;
|
|
12
|
+
viewMode?: string;
|
|
13
|
+
setViewMode?: (viewMode: string) => void;
|
|
11
14
|
}
|
|
12
15
|
|
|
13
16
|
export const Topbar = React.forwardRef<TopbarElement, Readonly<TopbarProps>>(
|
|
14
|
-
({ className, title, ...props }, forwardedRef) => {
|
|
17
|
+
({ className, title, viewMode, setViewMode, ...props }, forwardedRef) => {
|
|
15
18
|
return (
|
|
16
19
|
<header
|
|
17
20
|
ref={forwardedRef}
|
|
@@ -36,7 +39,7 @@ export const Topbar = React.forwardRef<TopbarElement, Readonly<TopbarProps>>(
|
|
|
36
39
|
<path
|
|
37
40
|
d="M10.75 8.75L14.25 12L10.75 15.25"
|
|
38
41
|
stroke="currentColor"
|
|
39
|
-
|
|
42
|
+
strokeOpacity="0.615"
|
|
40
43
|
strokeWidth="1.5"
|
|
41
44
|
strokeLinecap="round"
|
|
42
45
|
strokeLinejoin="round"
|
|
@@ -47,6 +50,45 @@ export const Topbar = React.forwardRef<TopbarElement, Readonly<TopbarProps>>(
|
|
|
47
50
|
{title}
|
|
48
51
|
</Heading>
|
|
49
52
|
</div>
|
|
53
|
+
|
|
54
|
+
{setViewMode && (
|
|
55
|
+
<ToggleGroup.Root
|
|
56
|
+
className="items-center bg-slate-2 p-1.5 flex gap-1 border border-slate-6 rounded-md absolute top-4 right-4"
|
|
57
|
+
type="single"
|
|
58
|
+
value={viewMode}
|
|
59
|
+
aria-label="View mode"
|
|
60
|
+
onValueChange={(value) => {
|
|
61
|
+
if (!value) {
|
|
62
|
+
return setViewMode('desktop');
|
|
63
|
+
}
|
|
64
|
+
setViewMode(value);
|
|
65
|
+
}}
|
|
66
|
+
>
|
|
67
|
+
<ToggleGroup.Item
|
|
68
|
+
className={classnames(
|
|
69
|
+
'text-sm text-slate-11 rounded px-1.5 py-0.5',
|
|
70
|
+
{
|
|
71
|
+
'text-slate-12 bg-slate-3 font-medium':
|
|
72
|
+
viewMode === 'desktop',
|
|
73
|
+
},
|
|
74
|
+
)}
|
|
75
|
+
value="desktop"
|
|
76
|
+
>
|
|
77
|
+
Desktop
|
|
78
|
+
</ToggleGroup.Item>
|
|
79
|
+
<ToggleGroup.Item
|
|
80
|
+
className={classnames(
|
|
81
|
+
'text-sm text-slate-11 rounded px-1.5 py-0.5',
|
|
82
|
+
{
|
|
83
|
+
'text-slate-12 bg-slate-3 font-medium': viewMode === 'source',
|
|
84
|
+
},
|
|
85
|
+
)}
|
|
86
|
+
value="source"
|
|
87
|
+
>
|
|
88
|
+
Source
|
|
89
|
+
</ToggleGroup.Item>
|
|
90
|
+
</ToggleGroup.Root>
|
|
91
|
+
)}
|
|
50
92
|
</header>
|
|
51
93
|
);
|
|
52
94
|
},
|
|
@@ -14,12 +14,6 @@ function MyApp({ Component, pageProps }: AppProps) {
|
|
|
14
14
|
<div className={classnames(inter.variable, 'font-sans')}>
|
|
15
15
|
<Head>
|
|
16
16
|
<title>React Email</title>
|
|
17
|
-
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
18
|
-
<link rel="preconnect" href="https://fonts.gstatic.com" />
|
|
19
|
-
<link
|
|
20
|
-
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500&display=swap"
|
|
21
|
-
rel="stylesheet"
|
|
22
|
-
/>
|
|
23
17
|
</Head>
|
|
24
18
|
<Component {...pageProps} />
|
|
25
19
|
</div>
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { promises as fs } from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
-
import { Heading } from '../components';
|
|
3
|
+
import { Button, Heading, Text } from '../components';
|
|
4
4
|
import { Layout } from '../components/layout';
|
|
5
|
+
import Link from 'next/link';
|
|
5
6
|
|
|
6
7
|
interface HomeProps {}
|
|
7
8
|
|
|
@@ -18,17 +19,31 @@ const getEmails = async () => {
|
|
|
18
19
|
export async function getStaticProps({ params }) {
|
|
19
20
|
try {
|
|
20
21
|
const emails = await getEmails();
|
|
21
|
-
return emails
|
|
22
|
+
return emails
|
|
23
|
+
? { props: { navItems: emails } }
|
|
24
|
+
: { props: { navItems: [] } };
|
|
22
25
|
} catch (error) {
|
|
23
26
|
console.error(error);
|
|
24
|
-
return {
|
|
27
|
+
return { props: { navItems: [] } };
|
|
25
28
|
}
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
const Home: React.FC<Readonly<HomeProps>> = ({ navItems }: any) => {
|
|
29
32
|
return (
|
|
30
33
|
<Layout navItems={navItems}>
|
|
31
|
-
<
|
|
34
|
+
<div className="max-w-md border border-slate-6 mx-auto mt-56 rounded-md p-8">
|
|
35
|
+
<Heading as="h2" weight="medium">
|
|
36
|
+
Welcome to the React Email preview!
|
|
37
|
+
</Heading>
|
|
38
|
+
<Text as="p" className="mt-2 mb-4">
|
|
39
|
+
To start developing your next email template, you can create a{' '}
|
|
40
|
+
<code>.jsx</code> or <code>.tsx</code> file under the "emails" folder.
|
|
41
|
+
</Text>
|
|
42
|
+
|
|
43
|
+
<Button asChild>
|
|
44
|
+
<Link href="https://react.email/docs">Check the docs</Link>
|
|
45
|
+
</Button>
|
|
46
|
+
</div>
|
|
32
47
|
</Layout>
|
|
33
48
|
);
|
|
34
49
|
};
|
|
@@ -5,6 +5,7 @@ import { GetStaticPaths } from 'next';
|
|
|
5
5
|
import { Layout } from '../../components/layout';
|
|
6
6
|
import * as React from 'react';
|
|
7
7
|
import { Code } from '../../components';
|
|
8
|
+
import Head from 'next/head';
|
|
8
9
|
|
|
9
10
|
interface PreviewProps {}
|
|
10
11
|
|
|
@@ -48,6 +49,7 @@ const Preview: React.FC<Readonly<PreviewProps>> = ({
|
|
|
48
49
|
slug,
|
|
49
50
|
}: any) => {
|
|
50
51
|
const [viewMode, setViewMode] = React.useState('desktop');
|
|
52
|
+
const title = `${slug} — React Email`;
|
|
51
53
|
|
|
52
54
|
return (
|
|
53
55
|
<Layout
|
|
@@ -56,10 +58,19 @@ const Preview: React.FC<Readonly<PreviewProps>> = ({
|
|
|
56
58
|
viewMode={viewMode}
|
|
57
59
|
setViewMode={setViewMode}
|
|
58
60
|
>
|
|
61
|
+
<Head>
|
|
62
|
+
<title>{title}</title>
|
|
63
|
+
</Head>
|
|
59
64
|
{viewMode === 'desktop' ? (
|
|
60
|
-
<iframe
|
|
65
|
+
<iframe
|
|
66
|
+
srcDoc={markup}
|
|
67
|
+
frameBorder="0"
|
|
68
|
+
className="w-full h-[calc(100vh_-_70px)]"
|
|
69
|
+
/>
|
|
61
70
|
) : (
|
|
62
|
-
<
|
|
71
|
+
<div className="max-w-[864px] mx-auto py-10">
|
|
72
|
+
<Code>{markup}</Code>
|
|
73
|
+
</div>
|
|
63
74
|
)}
|
|
64
75
|
</Layout>
|
|
65
76
|
);
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
const preparePreview = async () => {
|
|
5
|
+
Promise.all([
|
|
6
|
+
prepareRoot(),
|
|
7
|
+
prepareComponents(),
|
|
8
|
+
prepareStyles(),
|
|
9
|
+
prepareUtils(),
|
|
10
|
+
preparePages(),
|
|
11
|
+
]);
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const prepareRoot = async () => {
|
|
15
|
+
try {
|
|
16
|
+
const rootFiles = await fs.promises.readdir(path.join('preview'));
|
|
17
|
+
const files = rootFiles
|
|
18
|
+
.filter(
|
|
19
|
+
(file) => !['.next', 'yarn.lock', 'src', 'node_modules'].includes(file),
|
|
20
|
+
)
|
|
21
|
+
.map(async (rootFile) => {
|
|
22
|
+
return {
|
|
23
|
+
title: rootFile,
|
|
24
|
+
content: await fs.promises.readFile(path.join('preview', rootFile), {
|
|
25
|
+
encoding: 'utf-8',
|
|
26
|
+
}),
|
|
27
|
+
};
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const componentFiles = await Promise.all(files);
|
|
31
|
+
const data = `export const root = ${JSON.stringify(componentFiles)}`;
|
|
32
|
+
await fs.promises.writeFile(
|
|
33
|
+
path.join('source', '_preview', 'root.ts'),
|
|
34
|
+
data,
|
|
35
|
+
);
|
|
36
|
+
} catch (error) {
|
|
37
|
+
console.error({ error });
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const prepareComponents = async () => {
|
|
42
|
+
try {
|
|
43
|
+
const components = await fs.promises.readdir(
|
|
44
|
+
path.join('preview', 'src', 'components'),
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
const files = components.map(async (component) => {
|
|
48
|
+
return {
|
|
49
|
+
title: component,
|
|
50
|
+
content: await fs.promises.readFile(
|
|
51
|
+
path.join('preview', 'src', 'components', component),
|
|
52
|
+
{ encoding: 'utf-8' },
|
|
53
|
+
),
|
|
54
|
+
};
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const componentFiles = await Promise.all(files);
|
|
58
|
+
const data = `export const components = ${JSON.stringify(componentFiles)}`;
|
|
59
|
+
|
|
60
|
+
await fs.promises.writeFile(
|
|
61
|
+
path.join('source', '_preview', 'components.ts'),
|
|
62
|
+
data,
|
|
63
|
+
);
|
|
64
|
+
} catch (error) {
|
|
65
|
+
console.error({ error });
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const prepareStyles = async () => {
|
|
70
|
+
try {
|
|
71
|
+
const styles = await fs.promises.readdir(
|
|
72
|
+
path.join('preview', 'src', 'styles'),
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
const files = styles.map(async (style) => {
|
|
76
|
+
return {
|
|
77
|
+
title: style,
|
|
78
|
+
content: await fs.promises.readFile(
|
|
79
|
+
path.join('preview', 'src', 'styles', style),
|
|
80
|
+
{ encoding: 'utf-8' },
|
|
81
|
+
),
|
|
82
|
+
};
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
const styleFiles = await Promise.all(files);
|
|
86
|
+
const data = `export const styles = ${JSON.stringify(styleFiles)}`;
|
|
87
|
+
|
|
88
|
+
await fs.promises.writeFile(
|
|
89
|
+
path.join('source', '_preview', 'styles.ts'),
|
|
90
|
+
data,
|
|
91
|
+
);
|
|
92
|
+
} catch (error) {
|
|
93
|
+
console.error({ error });
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const prepareUtils = async () => {
|
|
98
|
+
try {
|
|
99
|
+
const utils = await fs.promises.readdir(
|
|
100
|
+
path.join('preview', 'src', 'utils'),
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
const files = utils.map(async (util) => {
|
|
104
|
+
return {
|
|
105
|
+
title: util,
|
|
106
|
+
content: await fs.promises.readFile(
|
|
107
|
+
path.join('preview', 'src', 'utils', util),
|
|
108
|
+
{ encoding: 'utf-8' },
|
|
109
|
+
),
|
|
110
|
+
};
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const utilFiles = await Promise.all(files);
|
|
114
|
+
const data = `export const utils = ${JSON.stringify(utilFiles)}`;
|
|
115
|
+
|
|
116
|
+
await fs.promises.writeFile(
|
|
117
|
+
path.join('source', '_preview', 'utils.ts'),
|
|
118
|
+
data,
|
|
119
|
+
);
|
|
120
|
+
} catch (error) {
|
|
121
|
+
console.error({ error });
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const preparePages = async () => {
|
|
126
|
+
try {
|
|
127
|
+
const pages = await fs.promises.readdir(
|
|
128
|
+
path.join('preview', 'src', 'pages'),
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
const pageFiles = pages.map(async (page) => {
|
|
132
|
+
const isDirectory =
|
|
133
|
+
fs.existsSync(page) && fs.lstatSync(page).isDirectory();
|
|
134
|
+
|
|
135
|
+
if (isDirectory) {
|
|
136
|
+
const pFiles = await fs.promises.readdir(
|
|
137
|
+
path.join('preview', 'src', 'pages', page),
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
const files = pFiles.map(async (file) => {
|
|
141
|
+
return {
|
|
142
|
+
dir: page,
|
|
143
|
+
title: file,
|
|
144
|
+
content: await fs.promises.readFile(
|
|
145
|
+
path.join('preview', 'src', 'pages', page, file),
|
|
146
|
+
{ encoding: 'utf-8' },
|
|
147
|
+
),
|
|
148
|
+
};
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
const [f] = await Promise.all(files);
|
|
152
|
+
|
|
153
|
+
return f;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return {
|
|
157
|
+
title: page,
|
|
158
|
+
content: await fs.promises.readFile(
|
|
159
|
+
path.join('preview', 'src', 'pages', page),
|
|
160
|
+
{ encoding: 'utf-8' },
|
|
161
|
+
),
|
|
162
|
+
};
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
const allPages = await Promise.all(pageFiles);
|
|
166
|
+
const data = `export const pages = ${JSON.stringify(allPages)}`;
|
|
167
|
+
|
|
168
|
+
await fs.promises.writeFile(
|
|
169
|
+
path.join('source', '_preview', 'pages.ts'),
|
|
170
|
+
data,
|
|
171
|
+
);
|
|
172
|
+
} catch (error) {
|
|
173
|
+
console.error({ error });
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
preparePreview();
|