promote-email-templates 0.1.6 → 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.
Files changed (74) hide show
  1. package/dist/index.d.mts +8 -1
  2. package/dist/index.d.ts +8 -1
  3. package/dist/index.js +27 -3
  4. package/dist/index.mjs +20 -0
  5. package/package.json +17 -14
  6. package/.react-email/.eslintrc.js +0 -52
  7. package/.react-email/.prettierignore +0 -3
  8. package/.react-email/.prettierrc.js +0 -8
  9. package/.react-email/license.md +0 -7
  10. package/.react-email/next.config.js +0 -36
  11. package/.react-email/package.json +0 -1
  12. package/.react-email/postcss.config.js +0 -8
  13. package/.react-email/readme.md +0 -44
  14. package/.react-email/src/actions/get-email-path-from-slug.ts +0 -26
  15. package/.react-email/src/actions/get-emails-directory-metadata.spec.ts +0 -73
  16. package/.react-email/src/actions/get-emails-directory-metadata.ts +0 -91
  17. package/.react-email/src/actions/render-email-by-path.tsx +0 -59
  18. package/.react-email/src/app/favicon.ico +0 -0
  19. package/.react-email/src/app/globals.css +0 -35
  20. package/.react-email/src/app/inter.ts +0 -7
  21. package/.react-email/src/app/layout.tsx +0 -36
  22. package/.react-email/src/app/logo.png +0 -0
  23. package/.react-email/src/app/page.tsx +0 -47
  24. package/.react-email/src/app/preview/[...slug]/page.tsx +0 -65
  25. package/.react-email/src/app/preview/[...slug]/preview.tsx +0 -141
  26. package/.react-email/src/app/preview/[...slug]/rendering-error.tsx +0 -40
  27. package/.react-email/src/components/button.tsx +0 -90
  28. package/.react-email/src/components/code-container.tsx +0 -145
  29. package/.react-email/src/components/code.tsx +0 -112
  30. package/.react-email/src/components/heading.tsx +0 -113
  31. package/.react-email/src/components/icons/icon-arrow-down.tsx +0 -16
  32. package/.react-email/src/components/icons/icon-base.tsx +0 -24
  33. package/.react-email/src/components/icons/icon-button.tsx +0 -23
  34. package/.react-email/src/components/icons/icon-check.tsx +0 -19
  35. package/.react-email/src/components/icons/icon-clipboard.tsx +0 -40
  36. package/.react-email/src/components/icons/icon-download.tsx +0 -19
  37. package/.react-email/src/components/icons/icon-file.tsx +0 -19
  38. package/.react-email/src/components/icons/icon-folder-open.tsx +0 -19
  39. package/.react-email/src/components/icons/icon-folder.tsx +0 -18
  40. package/.react-email/src/components/icons/icon-hide-sidebar.tsx +0 -23
  41. package/.react-email/src/components/icons/icon-monitor.tsx +0 -19
  42. package/.react-email/src/components/icons/icon-phone.tsx +0 -26
  43. package/.react-email/src/components/icons/icon-source.tsx +0 -19
  44. package/.react-email/src/components/index.ts +0 -7
  45. package/.react-email/src/components/logo.tsx +0 -64
  46. package/.react-email/src/components/send.tsx +0 -135
  47. package/.react-email/src/components/shell.tsx +0 -115
  48. package/.react-email/src/components/sidebar/index.ts +0 -1
  49. package/.react-email/src/components/sidebar/sidebar-directory-children.tsx +0 -134
  50. package/.react-email/src/components/sidebar/sidebar-directory.tsx +0 -106
  51. package/.react-email/src/components/sidebar/sidebar.tsx +0 -45
  52. package/.react-email/src/components/text.tsx +0 -99
  53. package/.react-email/src/components/tooltip-content.tsx +0 -32
  54. package/.react-email/src/components/tooltip.tsx +0 -19
  55. package/.react-email/src/components/topbar.tsx +0 -161
  56. package/.react-email/src/contexts/emails.tsx +0 -127
  57. package/.react-email/src/hooks/use-hot-reload.ts +0 -35
  58. package/.react-email/src/hooks/use-rendering-metadata.ts +0 -36
  59. package/.react-email/src/utils/cn.ts +0 -6
  60. package/.react-email/src/utils/constants.ts +0 -6
  61. package/.react-email/src/utils/copy-text-to-clipboard.ts +0 -7
  62. package/.react-email/src/utils/emails-directory-absolute-path.ts +0 -34
  63. package/.react-email/src/utils/get-email-component.ts +0 -108
  64. package/.react-email/src/utils/improve-error-with-sourcemap.ts +0 -55
  65. package/.react-email/src/utils/index.ts +0 -5
  66. package/.react-email/src/utils/language-map.ts +0 -7
  67. package/.react-email/src/utils/static-node-modules-for-vm.ts +0 -92
  68. package/.react-email/src/utils/types/as.ts +0 -26
  69. package/.react-email/src/utils/types/email-template.ts +0 -8
  70. package/.react-email/src/utils/types/error-object.ts +0 -11
  71. package/.react-email/src/utils/types/hot-reload-change.ts +0 -6
  72. package/.react-email/src/utils/types/hot-reload-event.ts +0 -6
  73. package/.react-email/src/utils/unreachable.ts +0 -8
  74. package/.react-email/tailwind.config.ts +0 -94
@@ -1,91 +0,0 @@
1
- 'use server';
2
- /* eslint-disable @typescript-eslint/no-non-null-assertion */
3
- import fs from 'node:fs';
4
- import path from 'node:path';
5
-
6
- const isFileAnEmail = (fullPath: string): boolean => {
7
- const stat = fs.statSync(fullPath);
8
-
9
- if (stat.isDirectory()) return false;
10
-
11
- const { ext } = path.parse(fullPath);
12
-
13
- if (!['.js', '.tsx', '.jsx'].includes(ext)) return false;
14
-
15
- // check with a heuristic to see if the file has at least
16
- // a default export
17
- const fileContents = fs.readFileSync(fullPath, 'utf8');
18
-
19
- return /\bexport\s+default\b/gm.test(fileContents);
20
- };
21
-
22
- export interface EmailsDirectory {
23
- absolutePath: string;
24
- directoryName: string;
25
- emailFilenames: string[];
26
- subDirectories: EmailsDirectory[];
27
- }
28
-
29
- const mergeDirectoriesWithSubDirectories = (
30
- emailsDirectoryMetadata: EmailsDirectory,
31
- ): EmailsDirectory => {
32
- let currentResultingMergedDirectory: EmailsDirectory =
33
- emailsDirectoryMetadata;
34
-
35
- while (
36
- currentResultingMergedDirectory.emailFilenames.length === 0 &&
37
- currentResultingMergedDirectory.subDirectories.length === 1
38
- ) {
39
- const onlySubDirectory = currentResultingMergedDirectory.subDirectories[0]!;
40
- currentResultingMergedDirectory = {
41
- subDirectories: onlySubDirectory.subDirectories,
42
- emailFilenames: onlySubDirectory.emailFilenames,
43
- absolutePath: onlySubDirectory.absolutePath,
44
- directoryName: path.join(
45
- currentResultingMergedDirectory.directoryName,
46
- onlySubDirectory.directoryName,
47
- ),
48
- };
49
- }
50
-
51
- return currentResultingMergedDirectory;
52
- };
53
-
54
- export const getEmailsDirectoryMetadata = async (
55
- absolutePathToEmailsDirectory: string,
56
- ): Promise<EmailsDirectory | undefined> => {
57
- if (!fs.existsSync(absolutePathToEmailsDirectory)) return;
58
-
59
- const dirents = await fs.promises.readdir(absolutePathToEmailsDirectory, {
60
- withFileTypes: true,
61
- });
62
-
63
- const emailFilenames = dirents
64
- .filter((dirent) =>
65
- isFileAnEmail(path.join(absolutePathToEmailsDirectory, dirent.name)),
66
- )
67
- .map((dirent) => dirent.name.replace(path.extname(dirent.name), ''));
68
-
69
- const subDirectories = await Promise.all(
70
- dirents
71
- .filter(
72
- (dirent) =>
73
- dirent.isDirectory() &&
74
- !dirent.name.startsWith('_') &&
75
- dirent.name !== 'static',
76
- )
77
- .map(
78
- (dirent) =>
79
- getEmailsDirectoryMetadata(
80
- path.join(absolutePathToEmailsDirectory, dirent.name),
81
- ) as Promise<EmailsDirectory>,
82
- ),
83
- );
84
-
85
- return mergeDirectoriesWithSubDirectories({
86
- absolutePath: absolutePathToEmailsDirectory,
87
- directoryName: absolutePathToEmailsDirectory.split(path.sep).pop()!,
88
- emailFilenames,
89
- subDirectories,
90
- });
91
- };
@@ -1,59 +0,0 @@
1
- 'use server';
2
- import fs from 'node:fs';
3
- import { renderAsync } from '@react-email/render';
4
- import { getEmailComponent } from '../utils/get-email-component';
5
- import type { ErrorObject } from '../utils/types/error-object';
6
- import { improveErrorWithSourceMap } from '../utils/improve-error-with-sourcemap';
7
-
8
- export interface RenderedEmailMetadata {
9
- markup: string;
10
- plainText: string;
11
- reactMarkup: string;
12
- }
13
-
14
- export type EmailRenderingResult =
15
- | RenderedEmailMetadata
16
- | {
17
- error: ErrorObject;
18
- };
19
-
20
- export const renderEmailByPath = async (
21
- emailPath: string,
22
- ): Promise<EmailRenderingResult> => {
23
- const result = await getEmailComponent(emailPath);
24
-
25
- if ('error' in result) {
26
- return { error: result.error };
27
- }
28
-
29
- const { emailComponent: Email, sourceMapToOriginalFile } = result;
30
-
31
- const previewProps = Email.PreviewProps || {};
32
- const EmailComponent = Email as React.FC;
33
- try {
34
- const markup = await renderAsync(<EmailComponent {...previewProps} />, {
35
- pretty: true,
36
- });
37
- const plainText = await renderAsync(<EmailComponent {...previewProps} />, {
38
- plainText: true,
39
- });
40
-
41
- const reactMarkup = await fs.promises.readFile(emailPath, 'utf-8');
42
-
43
- return {
44
- markup,
45
- plainText,
46
- reactMarkup,
47
- };
48
- } catch (exception) {
49
- const error = exception as Error;
50
-
51
- return {
52
- error: improveErrorWithSourceMap(
53
- error,
54
- emailPath,
55
- sourceMapToOriginalFile,
56
- ),
57
- };
58
- }
59
- };
Binary file
@@ -1,35 +0,0 @@
1
- @tailwind base;
2
- @tailwind components;
3
- @tailwind utilities;
4
-
5
- :root {
6
- --foreground-rgb: 0, 0, 0;
7
- --background-start-rgb: 214, 219, 220;
8
- --background-end-rgb: 255, 255, 255;
9
- }
10
-
11
- @media (prefers-color-scheme: dark) {
12
- :root {
13
- --foreground-rgb: 255, 255, 255;
14
- --background-start-rgb: 0, 0, 0;
15
- --background-end-rgb: 0, 0, 0;
16
- }
17
- }
18
-
19
- body {
20
- color: rgb(var(--foreground-rgb));
21
- background: linear-gradient(
22
- to bottom,
23
- transparent,
24
- rgb(var(--background-end-rgb))
25
- )
26
- rgb(var(--background-start-rgb));
27
- }
28
-
29
- .popup-open iframe {
30
- pointer-events: none;
31
- }
32
-
33
- nav > div > div > .line {
34
- display: none;
35
- }
@@ -1,7 +0,0 @@
1
- import { Inter } from 'next/font/google';
2
-
3
- export const inter = Inter({
4
- subsets: ['latin'],
5
- variable: '--font-inter',
6
- display: 'swap',
7
- });
@@ -1,36 +0,0 @@
1
- import type { Metadata } from 'next';
2
- import './globals.css';
3
- import { getEmailsDirectoryMetadata } from '../actions/get-emails-directory-metadata';
4
- import { emailsDirectoryAbsolutePath } from '../utils/emails-directory-absolute-path';
5
- import { EmailsProvider } from '../contexts/emails';
6
- import { inter } from './inter';
7
-
8
- export const metadata: Metadata = {
9
- title: 'React Email',
10
- };
11
-
12
- const RootLayout = async ({ children }: { children: React.ReactNode }) => {
13
- const emailsDirectoryMetadata = await getEmailsDirectoryMetadata(
14
- emailsDirectoryAbsolutePath,
15
- );
16
-
17
- if (typeof emailsDirectoryMetadata === 'undefined') {
18
- throw new Error(
19
- `Could not find the emails directory specified under ${emailsDirectoryAbsolutePath}!`,
20
- );
21
- }
22
-
23
- return (
24
- <html lang="en">
25
- <body className={inter.className}>
26
- <EmailsProvider
27
- initialEmailsDirectoryMetadata={emailsDirectoryMetadata}
28
- >
29
- {children}
30
- </EmailsProvider>
31
- </body>
32
- </html>
33
- );
34
- };
35
-
36
- export default RootLayout;
Binary file
@@ -1,47 +0,0 @@
1
- import path from 'node:path';
2
- import Link from 'next/link';
3
- import Image from 'next/image';
4
- import { Button, Heading, Text } from '../components';
5
- import { Shell } from '../components/shell';
6
- import { emailsDirectoryAbsolutePath } from '../utils/emails-directory-absolute-path';
7
- import logo from './logo.png';
8
-
9
- const Home = () => {
10
- const baseEmailsDirectoryName = path.basename(emailsDirectoryAbsolutePath);
11
-
12
- return (
13
- <Shell>
14
- <div className="relative max-w-lg mx-auto p-8 flex items-center justify-center h-[inherit]">
15
- <div className="relative z-10 flex flex-col text-center items-center">
16
- <Image
17
- alt="React Email Icon"
18
- className="mb-8"
19
- height={144}
20
- src={logo}
21
- style={{
22
- borderRadius: 34,
23
- boxShadow: '0px 10px 200px 20px #2B7CA080',
24
- }}
25
- width={141}
26
- />
27
- <Heading as="h2" size="6" weight="medium">
28
- Welcome to React Email
29
- </Heading>
30
- <Text as="p" className="mt-2 mb-4">
31
- To start developing your emails, you can create a<br />
32
- <code className="text-slate-12">.jsx</code> or{' '}
33
- <code className="text-slate-12">.tsx</code> file under your{' '}
34
- <code className="text-slate-12">{baseEmailsDirectoryName}</code>{' '}
35
- folder.
36
- </Text>
37
-
38
- <Button asChild size="3">
39
- <Link href="https://react.email/docs">Check the docs</Link>
40
- </Button>
41
- </div>
42
- </div>
43
- </Shell>
44
- );
45
- };
46
-
47
- export default Home;
@@ -1,65 +0,0 @@
1
- import { Suspense } from 'react';
2
- import { getEmailPathFromSlug } from '../../../actions/get-email-path-from-slug';
3
- import { getEmailsDirectoryMetadata } from '../../../actions/get-emails-directory-metadata';
4
- import { renderEmailByPath } from '../../../actions/render-email-by-path';
5
- import { emailsDirectoryAbsolutePath } from '../../../utils/emails-directory-absolute-path';
6
- import Home from '../../page';
7
- import Preview from './preview';
8
-
9
- export const dynamicParams = true;
10
-
11
- export interface PreviewParams {
12
- slug: string[];
13
- }
14
-
15
- export default async function Page({ params }: { params: PreviewParams }) {
16
- // will come in here as segments of a relative path to the email
17
- // ex: ['authentication', 'verify-password.tsx']
18
- const slug = params.slug.join('/');
19
- const emailsDirMetadata = await getEmailsDirectoryMetadata(
20
- emailsDirectoryAbsolutePath,
21
- );
22
-
23
- if (typeof emailsDirMetadata === 'undefined') {
24
- throw new Error(
25
- `Could not find the emails directory specified under ${emailsDirectoryAbsolutePath}!
26
-
27
- This is most likely not an issue with the preview server. Maybe there was a typo on the "--dir" flag?`,
28
- );
29
- }
30
-
31
- const emailPath = await getEmailPathFromSlug(slug);
32
-
33
- const emailRenderingResult = await renderEmailByPath(emailPath);
34
-
35
- if (
36
- 'error' in emailRenderingResult &&
37
- process.env.NEXT_PUBLIC_IS_BUILDING === 'true'
38
- ) {
39
- throw new Error(emailRenderingResult.error.message, {
40
- cause: emailRenderingResult.error,
41
- });
42
- }
43
-
44
- return (
45
- // This suspense is so that this page doesn't throw warnings
46
- // on the build of the preview server de-opting into
47
- // client-side rendering on build
48
- <Suspense fallback={<Home />}>
49
- <Preview
50
- emailPath={emailPath}
51
- renderingResult={emailRenderingResult}
52
- slug={slug}
53
- />
54
- </Suspense>
55
- );
56
- }
57
-
58
- export function generateMetadata({ params }: { params: PreviewParams }) {
59
- return { title: `${params.slug.join('/')} — React Email` };
60
- }
61
-
62
-
63
- export async function generateStaticParams() {
64
- return [{"slug":["admin","abort-order-request"]},{"slug":["admin","revert-payment-request-admin"]},{"slug":["all","new-message-notification"]},{"slug":["all","welcome"]},{"slug":["brand","evidences-accepted-brand"]},{"slug":["brand","evidences-submitted-brand"]},{"slug":["brand","new-job-application-received-brand"]},{"slug":["brand","new-job-created-brand"]},{"slug":["brand","new-order-created-brand"]},{"slug":["brand","order-accepted-brand"]},{"slug":["brand","order-cancelled-brand"]},{"slug":["brand","order-rejected-brand"]},{"slug":["creator","evidences-approved-creator"]},{"slug":["creator","evidences-rejected-creator"]},{"slug":["creator","new-job-application-unapproved-creator"]},{"slug":["creator","new-order-created-creator"]},{"slug":["creator","order-cancelled-creator"]},{"slug":["creator","order-payment-creator"]},{"slug":["shared","components","base-head"]},{"slug":["shared","components","comment-component"]},{"slug":["shared","components","footer"]},{"slug":["shared","components","header"]},{"slug":["shared","components","new-order-info"]},{"slug":["shared","components","payment-amount"]},{"slug":["shared","components","user-Info"]}];
65
- }
@@ -1,141 +0,0 @@
1
- 'use client';
2
-
3
- import { usePathname, useRouter, useSearchParams } from 'next/navigation';
4
- import React from 'react';
5
- import { Toaster } from 'sonner';
6
- import { useHotreload } from '../../../hooks/use-hot-reload';
7
- import type { EmailRenderingResult } from '../../../actions/render-email-by-path';
8
- import { CodeContainer } from '../../../components/code-container';
9
- import { Shell } from '../../../components/shell';
10
- import { Tooltip } from '../../../components/tooltip';
11
- import { useEmails } from '../../../contexts/emails';
12
- import { useRenderingMetadata } from '../../../hooks/use-rendering-metadata';
13
- import { RenderingError } from './rendering-error';
14
-
15
- interface PreviewProps {
16
- slug: string;
17
- emailPath: string;
18
- renderingResult: EmailRenderingResult;
19
- }
20
-
21
- const Preview = ({
22
- slug,
23
- emailPath,
24
- renderingResult: initialRenderingResult,
25
- }: PreviewProps) => {
26
- const router = useRouter();
27
- const pathname = usePathname();
28
- const searchParams = useSearchParams();
29
-
30
- const activeView = searchParams.get('view') ?? 'desktop';
31
- const activeLang = searchParams.get('lang') ?? 'jsx';
32
- const { useEmailRenderingResult } = useEmails();
33
-
34
- const renderingResult = useEmailRenderingResult(
35
- emailPath,
36
- initialRenderingResult,
37
- );
38
-
39
- const renderedEmailMetadata = useRenderingMetadata(
40
- emailPath,
41
- renderingResult,
42
- initialRenderingResult,
43
- );
44
-
45
- if (process.env.NEXT_PUBLIC_IS_BUILDING !== 'true') {
46
- // this will not change on runtime so it doesn't violate
47
- // the rules of hooks
48
- // eslint-disable-next-line react-hooks/rules-of-hooks
49
- useHotreload((changes) => {
50
- const changeForThisEmail = changes.find((change) =>
51
- change.filename.includes(slug),
52
- );
53
-
54
- if (typeof changeForThisEmail !== 'undefined') {
55
- if (changeForThisEmail.event === 'unlink') {
56
- router.push('/');
57
- }
58
- }
59
- });
60
- }
61
-
62
- const handleViewChange = (view: string) => {
63
- const params = new URLSearchParams(searchParams);
64
- params.set('view', view);
65
- router.push(`${pathname}?${params.toString()}`);
66
- };
67
-
68
- const handleLangChange = (lang: string) => {
69
- const params = new URLSearchParams(searchParams);
70
- params.set('view', 'source');
71
- params.set('lang', lang);
72
- router.push(`${pathname}?${params.toString()}`);
73
- };
74
-
75
- const hasNoErrors = typeof renderedEmailMetadata !== 'undefined';
76
-
77
- return (
78
- <Shell
79
- activeView={hasNoErrors ? activeView : undefined}
80
- currentEmailOpenSlug={slug}
81
- markup={renderedEmailMetadata?.markup}
82
- setActiveView={hasNoErrors ? handleViewChange : undefined}
83
- >
84
- {/* This relative is so that when there is any error the user can still switch between emails */}
85
- <div className="relative h-full">
86
- {'error' in renderingResult ? (
87
- <RenderingError error={renderingResult.error} />
88
- ) : null}
89
-
90
- {/* If this is undefined means that the initial server render of the email had errors */}
91
- {hasNoErrors ? (
92
- <>
93
- {activeView === 'desktop' && (
94
- <iframe
95
- className="w-full bg-white h-[calc(100vh_-_140px)] lg:h-[calc(100vh_-_70px)]"
96
- srcDoc={renderedEmailMetadata.markup}
97
- title={slug}
98
- />
99
- )}
100
-
101
- {activeView === 'mobile' && (
102
- <iframe
103
- className="w-[360px] bg-white h-[calc(100vh_-_140px)] lg:h-[calc(100vh_-_70px)] mx-auto"
104
- srcDoc={renderedEmailMetadata.markup}
105
- title={slug}
106
- />
107
- )}
108
-
109
- {activeView === 'source' && (
110
- <div className="flex gap-6 mx-auto p-6 max-w-3xl">
111
- <Tooltip.Provider>
112
- <CodeContainer
113
- activeLang={activeLang}
114
- markups={[
115
- {
116
- language: 'jsx',
117
- content: renderedEmailMetadata.reactMarkup,
118
- },
119
- {
120
- language: 'markup',
121
- content: renderedEmailMetadata.markup,
122
- },
123
- {
124
- language: 'markdown',
125
- content: renderedEmailMetadata.plainText,
126
- },
127
- ]}
128
- setActiveLang={handleLangChange}
129
- />
130
- </Tooltip.Provider>
131
- </div>
132
- )}
133
- </>
134
- ) : null}
135
- <Toaster />
136
- </div>
137
- </Shell>
138
- );
139
- };
140
-
141
- export default Preview;
@@ -1,40 +0,0 @@
1
- 'use client';
2
- import type { ErrorObject } from '../../../utils/types/error-object';
3
-
4
- export const RenderingError = (props: { error: ErrorObject }) => {
5
- return (
6
- <>
7
- <div className="absolute inset-0 z-50 bg-black/80" />
8
- <div className="md:max-w-[568px] lg:max-w-[968px] absolute left-[50%] top-[50%] min-h-[50vh] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-t-4 bg-white text-black p-6 shadow-lg duration-200 sm:rounded-lg rounded-t-sm">
9
- <div className="flex flex-col max-w-full min-w-0 space-y-1.5">
10
- <h2 className="text-lg flex items-center flex-shrink gap-4 font-semibold pb-2 leading-none tracking-tight">
11
- <svg
12
- className="h-6 w-6 text-red-600 font-extrabold"
13
- fill="none"
14
- height="24"
15
- stroke="currentColor"
16
- strokeLinecap="round"
17
- strokeLinejoin="round"
18
- strokeWidth="2"
19
- viewBox="0 0 24 24"
20
- width="24"
21
- xmlns="http://www.w3.org/2000/svg"
22
- >
23
- <path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z" />
24
- <path d="M12 9v4" />
25
- <path d="M12 17h.01" />
26
- </svg>
27
- {props.error.name}: {props.error.message}
28
- </h2>
29
- {props.error.stack ? (
30
- <div className="text-sm p-2 flex-grow scroll-px-4 overflow-x-auto bg-red-500 rounded-lg text-gray-100">
31
- <pre className="font-mono w-full min-w-0 leading-7">
32
- {props.error.stack}
33
- </pre>
34
- </div>
35
- ) : undefined}
36
- </div>
37
- </div>
38
- </>
39
- );
40
- };
@@ -1,90 +0,0 @@
1
- import * as SlotPrimitive from '@radix-ui/react-slot';
2
- import * as React from 'react';
3
- import { unreachable } from '../utils/unreachable';
4
- import { cn } from '../utils/cn';
5
-
6
- type ButtonElement = React.ElementRef<'button'>;
7
- type RootProps = React.ComponentPropsWithoutRef<'button'>;
8
-
9
- type Appearance = 'white' | 'gradient';
10
- type Size = '1' | '2' | '3' | '4';
11
-
12
- interface ButtonProps extends RootProps {
13
- asChild?: boolean;
14
- appearance?: Appearance;
15
- size?: Size;
16
- }
17
-
18
- export const Button = React.forwardRef<ButtonElement, Readonly<ButtonProps>>(
19
- (
20
- {
21
- asChild,
22
- appearance = 'white',
23
- className,
24
- children,
25
- size = '2',
26
- ...props
27
- },
28
- forwardedRef,
29
- ) => {
30
- const classNames = cn(
31
- getSize(size),
32
- getAppearance(appearance),
33
- 'inline-flex items-center justify-center border font-medium',
34
- className,
35
- );
36
-
37
- return asChild ? (
38
- <SlotPrimitive.Slot ref={forwardedRef} {...props} className={classNames}>
39
- <SlotPrimitive.Slottable>{children}</SlotPrimitive.Slottable>
40
- </SlotPrimitive.Slot>
41
- ) : (
42
- <button
43
- className={classNames}
44
- ref={forwardedRef}
45
- type="button"
46
- {...props}
47
- >
48
- {children}
49
- </button>
50
- );
51
- },
52
- );
53
-
54
- Button.displayName = 'Button';
55
-
56
- const getAppearance = (appearance: Appearance | undefined) => {
57
- switch (appearance) {
58
- case undefined:
59
- case 'white':
60
- return [
61
- 'bg-white text-black',
62
- 'hover:bg-white/90',
63
- 'focus:ring-2 focus:ring-white/20 focus:outline-none focus:bg-white/90',
64
- ];
65
- case 'gradient':
66
- return [
67
- 'bg-gradient backdrop-blur-[20px] border-[#34343A]',
68
- 'hover:bg-gradientHover',
69
- 'focus:ring-2 focus:ring-white/20 focus:outline-none focus:bg-gradientHover',
70
- ];
71
- default:
72
- unreachable(appearance);
73
- }
74
- };
75
-
76
- const getSize = (size: Size | undefined) => {
77
- switch (size) {
78
- case '1':
79
- return '';
80
- case undefined:
81
- case '2':
82
- return 'text-[14px] h-8 px-3 rounded-md gap-2';
83
- case '3':
84
- return 'text-[14px] h-10 px-4 rounded-md gap-2';
85
- case '4':
86
- return 'text-base h-11 px-4 rounded-md gap-2';
87
- default:
88
- unreachable(size);
89
- }
90
- };