Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ When new backend features are discovered, update `ai-context/core-features-refer
## Building

- When type checking, use `yarn check -p <package-name>`, e.g., `yarn check -p @webiny/api-core`
- When building a single package, use `yarn build -p <package-name>`, e.g., `yarn build -p @webiny/api-core`.
- When building a single package, use `yarn build -p <package-name> --safe-replace`, e.g., `yarn build -p @webiny/api-core --safe-replace`. We use "--safe-replace" in order to not have our active bundling watch process break.
- To build all packages, simply run `yarn build`.
- To build all packages without caching, use `yarn build --no-cache `.

Expand Down
2 changes: 1 addition & 1 deletion packages/admin-ui/src/theme.css
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@
@utility fill-grid {
background-image: radial-gradient(var(--color-neutral-strong) 1px, transparent 0px);
background-size: 15px 15px;
@apply bg-neutral-subtle;
@apply bg-neutral-light;
}

@layer base {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import React, { useState, useEffect } from "react";
import { makeDecoratable, useDialogs, LeftPanel, RightPanel, SplitView } from "@webiny/app-admin";
import { makeDecoratable, useDialogs } from "@webiny/app-admin";
import { useRouter } from "@webiny/app";
import { i18n } from "@webiny/app/i18n/index.js";
import { Heading, OverlayLoader, Tabs, Text, TimeAgo } from "@webiny/admin-ui";
import { ReactComponent as EditIcon } from "@webiny/icons/edit.svg";
import { ReactComponent as PreviewIcon } from "@webiny/icons/fullscreen.svg";
import { IconButton, OverlayLoader, SegmentedControlPrimitive, Tag, Text } from "@webiny/admin-ui";
import { ReactComponent as ExpandSidebarIcon } from "@webiny/icons/left_panel_open.svg";
import { ReactComponent as ViewCompactAltIcon } from "@webiny/icons/view_compact_alt.svg";
import { ReactComponent as ViewDayIcon } from "@webiny/icons/view_day.svg";
import { FieldsSidebar } from "./FieldsSidebar.js";

const VIEW_ITEMS = [
{ id: "compact", value: "compact", label: "", icon: <ViewCompactAltIcon /> },
{ id: "full", value: "full", label: "", icon: <ViewDayIcon /> }
];
import { FieldEditor } from "../FieldEditor/index.js";
import { PreviewTab } from "./PreviewTab.js";
import Header from "./Header.js";
Expand All @@ -27,7 +33,7 @@ interface OnChangeParams {
}

export const ContentModelEditor = makeDecoratable("ContentModelEditor", () => {
const { data, setData, isPristine, contentModel } = useModelEditor();
const { data, setData, isPristine } = useModelEditor();
const router = useRouter();
const dialogs = useDialogs();

Expand Down Expand Up @@ -60,6 +66,8 @@ export const ContentModelEditor = makeDecoratable("ContentModelEditor", () => {
}, []);

const [activeTab, setActiveTab] = useState<string>("edit");
const [isSidebarOpen, setIsSidebarOpen] = useState(true);
const [activeView, setActiveView] = useState("compact");

const onChange = ({ fields, layout }: OnChangeParams) => {
setData(data => ({ ...data, fields, layout }));
Expand All @@ -73,75 +81,121 @@ export const ContentModelEditor = makeDecoratable("ContentModelEditor", () => {

return (
<div className={"content-model-editor flex-1"}>
<Header />
<Header activeTab={activeTab} onTabChange={setActiveTab} />
<div className={"w-full overflow-y-auto h-main-content"}>
<SplitView>
<LeftPanel span={4} className={"bg-neutral-light"}>
<div className={"px-lg py-md h-[calc(100vh-98px)] overflow-y-scroll"}>
<div className={"flex h-full"}>
<div
className={
"shrink-0 bg-neutral-base border-r border-neutral-dimmed overflow-hidden h-main-content relative transition-[width] duration-200 ease-in-out"
}
style={{ width: isSidebarOpen ? 200 : 45 }}
>
{/* Expanded content */}
<div
className={
"transition-opacity duration-150 " +
(isSidebarOpen
? "opacity-100 delay-75"
: "opacity-0 pointer-events-none")
}
>
<FieldsSidebar
onFieldDragStart={() => {
setActiveTab("edit");
}}
onFieldDragStart={() => setActiveTab("edit")}
onCollapse={() => setIsSidebarOpen(false)}
/>
</div>
{/* Collapsed strip */}
<div
className={
"absolute inset-0 flex flex-col items-center pt-md gap-md transition-opacity duration-150 " +
(!isSidebarOpen
? "opacity-100 delay-75"
: "opacity-0 pointer-events-none")
}
>
<IconButton
variant={"ghost"}
size={"xs"}
icon={<ExpandSidebarIcon />}
onClick={() => setIsSidebarOpen(true)}
/>
<span
className={
"text-sm font-semibold text-neutral-strong [writing-mode:vertical-rl]"
}
>
Fields
</span>
</div>
</LeftPanel>
<RightPanel span={8} className={"bg-neutral-base"}>
<div className={"h-full overflow-y-scroll"}>
{contentModel && (
<div className={"px-xl pt-lg pb-md-extra"}>
<Heading level={4}>{contentModel.name}</Heading>
<Text size={"sm"} className={"text-neutral-muted"}>
{`Created by ${contentModel.createdBy.displayName}. Last modified: `}
<TimeAgo datetime={contentModel.savedOn} />.
</div>
<div
className={
"flex-1 overflow-y-scroll h-main-content " +
(activeTab === "edit" ? "fill-grid" : "bg-neutral-subtle")
}
>
<div
className={
"sticky top-0 left-0 z-10 p-md flex items-center justify-between"
}
>
<div>
{activeTab === "edit" && (
<Text
as="div"
size={"sm"}
className={"font-semibold text-neutral-primary"}
>
Model editor
</Text>
</div>
)}
<Tabs
size={"md"}
spacing={"xl"}
separator={true}
value={String(activeTab)}
onValueChange={setActiveTab}
tabs={[
<Tabs.Tab
key={"edit"}
value={"edit"}
trigger={"Edit"}
icon={<EditIcon />}
data-testid={"cms.editor.tab.edit"}
content={
<div className={"relative mb-lg"}>
<FieldEditor
fields={data.fields}
layout={data.layout || []}
onChange={onChange}
/>
</div>
}
/>,
<Tabs.Tab
key={"preview"}
value={"preview"}
trigger={"Preview"}
icon={<PreviewIcon />}
data-testid={"cms.editor.tab.preview"}
content={
<ContentEntryEditorWithConfig>
<ContentEntriesProvider contentModel={data}>
<ContentEntryProvider readonly={true}>
<PreviewTab
activeTab={activeTab === "preview"}
/>
</ContentEntryProvider>
</ContentEntriesProvider>
</ContentEntryEditorWithConfig>
}
/>
]}
)}
{activeTab === "preview" && (
<Tag content={"Preview"} variant={"warning"} />
)}
</div>
<SegmentedControlPrimitive
variant={"light"}
value={activeView}
onChange={setActiveView}
items={VIEW_ITEMS}
/>
</div>
</RightPanel>
</SplitView>
<div className={"pb-lg"}>
<div
className={" transition-[width] duration-200 ease-in-out"}
style={{
width: activeView === "compact" ? "704px" : "90%",
marginLeft: "auto",
marginRight: "auto"
}}
>
{activeTab === "edit" && (
<div className={"relative"} data-testid={"cms.editor.tab.edit"}>
<FieldEditor
fields={data.fields}
layout={data.layout || []}
onChange={onChange}
/>
</div>
)}
{activeTab === "preview" && (
<div
className={"bg-neutral-base px-xxl py-lg"}
data-testid={"cms.editor.tab.preview"}
>
<ContentEntryEditorWithConfig>
<ContentEntriesProvider contentModel={data}>
<ContentEntryProvider readonly={true}>
<PreviewTab activeTab={true} />
</ContentEntryProvider>
</ContentEntriesProvider>
</ContentEntryEditorWithConfig>
</div>
)}
</div>
</div>
</div>
</div>
<DragPreview />
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React from "react";

interface FieldsGridProps {
children: React.ReactNode;
}

const FieldsGrid = ({ children }: FieldsGridProps) => (
<div className={"flex flex-wrap gap-xs-plus py-sm px-md"}>{children}</div>
);

export { FieldsGrid, type FieldsGridProps };
Loading
Loading