Add React editor project

This commit is contained in:
Rami Bitar
2026-08-09 16:00:10 -04:00
parent 909d99251a
commit 63ecc5e284
212 changed files with 20655 additions and 11571 deletions
+29 -123
View File
@@ -1,132 +1,38 @@
import { DefaultRootProps, RootConfig } from "@reacteditor/core";
import { createFieldGoogleFonts } from "@reacteditor/field-google-fonts";
import { ThemeProvider, ThemeProps } from "@/components/theme-provider";
import { RootConfig } from '@reacteditor/core';
export type RootProps = DefaultRootProps &
ThemeProps & {
description?: string;
ogImage?: string;
};
/**
* Root props are the page's *content*, not its design. Colours, fonts, radius
* and spacing all live in `app/globals.css` and in the components themselves —
* the editor only owns the words and the imagery. See `editor.config.tsx` for
* the same rule applied to every block.
*
* The field names below are the ones `pageMetadata()` reads, so editing a page
* in the editor is what changes its `<head>` tags.
*/
export type RootProps = {
title?: string;
description?: string;
ogImage?: string;
};
const headerFontField = createFieldGoogleFonts() as any;
const bodyFontField = createFieldGoogleFonts() as any;
export const Root: RootConfig<{
props: RootProps;
fields: {
userField: { type: "userField"; option: boolean };
};
}> = {
export const Root: RootConfig<{ props: RootProps }> = {
// Root props default to being shared across pages. These are per-page SEO
// tags — `pageMetadata()` reads them straight off each `page.json` — so
// opting out keeps every route's title and description its own. The header
// and footer blocks are the things that stay global.
global: false,
defaultProps: {
title: "Untitled",
headerFont: "Inter",
headerFontWeight: "600",
bodyFont: "Inter",
// Hex defaults so the color picker reads them and any non-picker
// input (typed hex, AI-set value, etc.) is round-trip compatible.
primaryColor: "#0a0a0a",
secondaryColor: "#64748B",
accentColor: "#f5f5f5",
bgColor: "#ffffff",
fgColor: "#0a0a0a",
mutedColor: "#f5f5f5",
radius: "md",
shadow: "sm",
maxWidth: "lg",
title: 'Shop',
description: '',
ogImage: '',
},
fields: {
title: { label: "Page title", type: "text" },
description: { label: "Description", type: "textarea" },
ogImage: { label: "OG image", type: "image" },
headerFont: { label: "Header font", ...headerFontField },
headerFontWeight: {
label: "Header font weight",
type: "select",
options: [
{ label: "300 — Light", value: "300" },
{ label: "400 — Regular", value: "400" },
{ label: "500 — Medium", value: "500" },
{ label: "600 — Semibold", value: "600" },
{ label: "700 — Bold", value: "700" },
{ label: "800 — Extrabold", value: "800" },
{ label: "900 — Black", value: "900" },
],
},
bodyFont: { label: "Body font", ...bodyFontField },
primaryColor: { label: "Primary color", type: "color", placeholder: "#0a0a0a" },
secondaryColor: { label: "Secondary color", type: "color", placeholder: "#64748B" },
accentColor: { label: "Accent color", type: "color", placeholder: "#f5f5f5" },
bgColor: { label: "Background color", type: "color", placeholder: "#ffffff" },
fgColor: { label: "Foreground color", type: "color", placeholder: "#0a0a0a" },
mutedColor: { label: "Muted color", type: "color", placeholder: "#f5f5f5" },
radius: {
label: "Radius",
type: "select",
options: [
{ label: "None (square)", value: "none" },
{ label: "Small", value: "sm" },
{ label: "Medium", value: "md" },
{ label: "Large", value: "lg" },
{ label: "Extra large", value: "xl" },
],
},
shadow: {
label: "Shadow",
type: "select",
options: [
{ label: "None", value: "none" },
{ label: "Small", value: "sm" },
{ label: "Medium", value: "md" },
{ label: "Large", value: "lg" },
{ label: "Extra large", value: "xl" },
],
},
maxWidth: {
label: "Max width",
type: "select",
options: [
{ label: "Small", value: "sm" },
{ label: "Medium", value: "md" },
{ label: "Large", value: "lg" },
{ label: "Extra large", value: "xl" },
{ label: "Full bleed", value: "full" },
],
},
},
render: ({
children,
headerFont,
headerFontWeight,
bodyFont,
primaryColor,
secondaryColor,
accentColor,
bgColor,
fgColor,
mutedColor,
radius,
shadow,
maxWidth,
}) => {
return (
<ThemeProvider
headerFont={headerFont}
headerFontWeight={headerFontWeight}
bodyFont={bodyFont}
primaryColor={primaryColor}
secondaryColor={secondaryColor}
accentColor={accentColor}
bgColor={bgColor}
fgColor={fgColor}
mutedColor={mutedColor}
radius={radius}
shadow={shadow}
maxWidth={maxWidth}
>
{children}
</ThemeProvider>
);
title: { label: 'Page title', type: 'text' },
description: { label: 'Meta description', type: 'textarea' },
ogImage: { label: 'Social share image', type: 'image' },
},
// No wrapper: the storefront's own layout and stylesheet own the chrome.
render: ({ children }) => <>{children}</>,
};
export default Root;