feat: use body element on new pages (#5222)

Ref https://github.com/webstudio-is/webstudio/issues/3632

Now new project and new page will have body element in the root instead
of body component.

<img width="422" alt="Screenshot 2025-05-20 at 00 15 37"
src="https://github.com/user-attachments/assets/aee568c5-f0e4-40f5-a5a3-e94fc765505d"
/>
This commit is contained in:
Bogdan Chadkin authored and GitHub committed 2025-05-20 12:24:27 +03:00
1 parent b50e053267
commit fa875ab5c1
7 files changed
+31 -34

No files matched your search

@@ -12,6 +12,7 @@ import {
} from "@webstudio-is/design-system";
import { ChevronLeftIcon, ExternalLinkIcon } from "@webstudio-is/icons";
import {
elementComponent,
ROOT_FOLDER_ID,
type Asset,
type Page,
@@ -50,9 +51,12 @@ const insertSection = ({
);
// remove body and use its children as root insrances
if (body) {
fragment.instances = fragment.instances.filter(
(instance) => instance.component !== "Body"
);
fragment.instances = fragment.instances.filter((instance) => {
const isBody =
instance.component === "Body" ||
(instance.component === elementComponent && instance.tag === "body");
return !isBody;
});
fragment.children = body.children;
}
const insertable = findClosestInsertable(fragment);
@@ -29,6 +29,7 @@ import {
isLiteralExpression,
documentTypes,
isRootFolder,
elementComponent,
} from "@webstudio-is/sdk";
import {
theme,
@@ -1307,7 +1308,8 @@ const createPage = (pageId: Page["id"], values: Values) => {
instances.set(rootInstanceId, {
type: "instance",
id: rootInstanceId,
component: "Body",
component: elementComponent,
tag: "body",
children: [],
});
registerFolderChildMutable(pages.folders, pageId, values.parentFolderId);
@@ -46,7 +46,11 @@ export const TagControl = ({ meta, prop }: ControlProps<"tag">) => {
const instanceTag = instance?.tag;
const defaultTag = meta.options[0];
const computedTag = instanceTag ?? propTag ?? defaultTag;
const satisfyingTags = useStore($satisfyingTags);
let satisfyingTags = useStore($satisfyingTags);
// forbid changing tag on body element
if (computedTag === "body") {
satisfyingTags = ["body"];
}
const options = meta.options.filter((tag) => satisfyingTags.includes(tag));
const [value, setValue] = useState<undefined | string>();
const updateTag = (value: string) => {
+1 -11
View File
@@ -71,7 +71,6 @@ import { subscribeSelected } from "./instance-selected";
import { subscribeScrollNewInstanceIntoView } from "./shared/scroll-new-instance-into-view";
import { $selectedPage } from "~/shared/awareness";
import { createInstanceElement } from "./elements";
import { Body } from "./shared/body";
import { subscribeScrollbarSize } from "./scrollbar-width";
import { compareMedia } from "@webstudio-is/css-engine";
import { builderApi } from "~/shared/builder-api";
@@ -248,15 +247,6 @@ export const Canvas = () => {
hooks: baseComponentHooks,
templates: baseComponentTemplates,
});
registerComponentLibrary({
components: {
// override only canvas specific body component
// not related to sdk-components-react-remix anymore
Body,
},
metas: {},
templates: {},
});
registerComponentLibrary({
namespace: "@webstudio-is/sdk-components-react-radix",
components: radixComponents,
@@ -321,7 +311,7 @@ export const Canvas = () => {
}, []);
if (components.size === 0 || instances.size === 0) {
return <Body />;
return;
}
return (
-14
View File
@@ -1,14 +0,0 @@
import { forwardRef, type ElementRef, type ComponentProps } from "react";
import { Scripts, ScrollRestoration } from "@remix-run/react";
export const Body = forwardRef<ElementRef<"body">, ComponentProps<"body">>(
({ children, ...props }, ref) => (
<body {...props} ref={ref}>
{children}
<Scripts />
<ScrollRestoration />
</body>
)
);
Body.displayName = "Body";
+12 -3
View File
@@ -1,8 +1,8 @@
import { lazy } from "react";
import { type LoaderFunctionArgs } from "@remix-run/server-runtime";
import type { LoaderFunctionArgs } from "@remix-run/server-runtime";
import { Scripts, ScrollRestoration } from "@remix-run/react";
import { isCanvas } from "~/shared/router-utils";
import { ClientOnly } from "~/shared/client-only";
import { Body } from "~/canvas/shared/body";
export { ErrorBoundary } from "~/shared/error/error-boundary";
@@ -22,7 +22,16 @@ const Canvas = lazy(async () => {
const CanvasRoute = () => {
return (
<ClientOnly fallback={<Body />}>
// this setup remix scripts on canvas and after rendering a website
// scripts will continue to work even though removed from dom
<ClientOnly
fallback={
<body>
<Scripts />
<ScrollRestoration />
</body>
}
>
<Canvas />
</ClientOnly>
);
+3 -1
View File
@@ -20,6 +20,7 @@ import {
type StyleDecl,
Pages,
initialBreakpoints,
elementComponent,
} from "@webstudio-is/sdk";
import type { Build, CompactBuild } from "../types";
import { parseDeployment } from "./deployment";
@@ -229,7 +230,8 @@ const createNewPageInstances = (): Build["instances"] => {
{
type: "instance",
id: instanceId,
component: "Body",
component: elementComponent,
tag: "body",
children: [],
},
],