From e31c1466c4ff81550e5cd1106d539f22c28eb615 Mon Sep 17 00:00:00 2001 From: Lero Date: Tue, 6 Oct 2026 12:20:55 +0300 Subject: [PATCH 1/5] chore: keep only the comments that name a trap --- .../src/components/JsonArray.tsx | 12 ------ .../src/components/JsonBuilder.tsx | 39 +------------------ .../src/components/JsonBuilderLevel.tsx | 5 --- .../src/components/JsonBuilderSettings.tsx | 9 ----- .../src/components/JsonCode.tsx | 6 --- .../src/components/JsonFolded.tsx | 4 -- .../src/components/JsonFormClient.tsx | 32 --------------- .../src/components/JsonFormField.tsx | 2 - .../src/components/JsonInput.tsx | 8 ---- .../src/components/JsonMediaInput.tsx | 19 --------- .../src/components/JsonNode.tsx | 12 ------ .../src/components/JsonRichText.tsx | 11 +----- .../src/components/JsonRowMenu.tsx | 5 --- .../src/components/JsonTabs.tsx | 2 - .../src/components/Tip.tsx | 5 --- .../src/components/icons/BracesIcon.tsx | 1 - .../src/components/icons/EyeIcon.tsx | 2 - .../src/field/anchorField.ts | 9 ++--- .../src/field/checks.ts | 28 +------------ .../src/field/deepMerge.ts | 3 -- .../src/field/htmlToLexical.ts | 31 --------------- .../src/field/kinds.ts | 2 - .../src/field/typedJson.ts | 22 ----------- .../src/shared.ts | 2 - 24 files changed, 9 insertions(+), 262 deletions(-) diff --git a/packages/payload-plugin-json-form-builder/src/components/JsonArray.tsx b/packages/payload-plugin-json-form-builder/src/components/JsonArray.tsx index 0b8252a5..be9c358a 100644 --- a/packages/payload-plugin-json-form-builder/src/components/JsonArray.tsx +++ b/packages/payload-plugin-json-form-builder/src/components/JsonArray.tsx @@ -32,8 +32,6 @@ const rowValue = (row: TypedNode[]) => .map((field) => ("value" in field && field.type === "text" ? String(field.value ?? "") : "")) .find(Boolean); -// An array laid out as Payload's array field. Each row gets an id that travels with it: an id per -// position would make a dropped row animate twice. export const JsonArray = ({ faults, drag, @@ -63,16 +61,10 @@ export const JsonArray = ({ if (rowIds.length !== rows.length) setIds(rows.map(newId)); const [open, setOpen] = useState([]); const singular = formatLabels(node.name ?? label).singular || t("general:row"); - // What Payload's own array field does with the two: the button goes at the ceiling, and the last - // rows down to the floor cannot be taken away. const atMax = Boolean(node.maxRows && rows.length >= node.maxRows); const atMin = Boolean(node.minRows && rows.length <= node.minRows); - // A list nobody has put fields into yet: a row of it would be an accordion with nothing in it, so - // there is nothing to add until the shape exists. const shapeless = !(node.rows?.[0] ?? node.fields ?? []).length; - // `fields` only exists while the list is empty: it is the shape of the row that left, and the - // next row to arrive takes it away again. const update = (nextIds: string[], nextRows: TypedNode[][]) => { setIds(nextIds); const { fields: _dropped, ...rest } = node; @@ -97,9 +89,7 @@ export const JsonArray = ({ const setRow = (index: number, row: TypedNode[]) => onChange({ ...node, rows: rows.map((entry, i) => (i === index ? row : entry)) }); - // Payload's own row actions, minus the clipboard ones, which need the form state. const rowMenu = (index: number, row: TypedNode[]): MenuItem[] => [ - // A row of one unnamed field is a plain value; a switch inside it would turn it into an object. ...(row.length === 1 && !row[0].name ? [] : [ @@ -230,8 +220,6 @@ export const JsonArray = ({ const header = (!top || rows.length > 0) && (
- {/* Inside a section the accordion above already carries the name, so the heading names - what is below it instead — and the wrap is `space-between`, so it cannot be dropped. */}

{top ? "Items" : label}

diff --git a/packages/payload-plugin-json-form-builder/src/components/JsonBuilder.tsx b/packages/payload-plugin-json-form-builder/src/components/JsonBuilder.tsx index c4c15884..1e8bc5a9 100644 --- a/packages/payload-plugin-json-form-builder/src/components/JsonBuilder.tsx +++ b/packages/payload-plugin-json-form-builder/src/components/JsonBuilder.tsx @@ -37,11 +37,6 @@ import { useJsonFormConfig } from "../useJsonFormConfig.js"; import { blankNode, TYPES } from "../field/typedJson.js"; import type { NodeType, TypedNode, TypedRoot } from "../field/typedJson.js"; -// A strip of tabs holds tabs and nothing else, and a tab is worth nothing anywhere else — so it is -// offered where it belongs and nowhere besides. -// A strip of tabs holds tabs and nothing else; everywhere else a tab is not a thing to add. And a -// kind this installation has no home for — an upload with no collection, rich text with no editor — -// is not offered, rather than offered and then unable to draw itself. const holds = (node: TypedNode | undefined, missing: readonly NodeType[]): readonly NodeType[] => node?.type === "tabs" ? ["tab"] @@ -58,21 +53,13 @@ export const JsonBuilder = ({ onChange: (root: TypedRoot) => void; slug: string; }) => { - // Everything here edits a copy. The field's own value travels through the form's state, which - // answers a render late, and every edit read back from it arrived behind the one before it. const [draft, setDraft] = useState(root); const [current, setCurrent] = useState(""); - // Where the canvas stands. What it shows is what this spot holds, one level at a time. const [at, setAt] = useState([]); - // Where the node being edited sits, or nothing at all for one that does not exist yet. const [picked, setPicked] = useState(null); - // The drawer works on a copy and writes it back on the button. Editing the tree as it is typed - // renames and re-seats the very node the form is standing on, which takes the cursor with it. const [edited, setEdited] = useState(null); const [key, setKey] = useState(""); const [hovered, setHovered] = useState(""); - // What the drawer is making, if it is making anything. A kind is answered once, here: changing it - // later moves the values into a shape that cannot hold them. const [making, setMaking] = useState<"field" | "section" | null>(null); const { anchor: richTextAnchor, uploads } = useJsonFormConfig(); const missing: NodeType[] = [ @@ -83,31 +70,23 @@ export const JsonBuilder = ({ const leaveSlug = useDrawerSlug("json-builder-leave"); const { closeModal, isModalOpen, openModal } = useModal(); - // `body-scroll-lock` restores the page the moment any one modal gives up its lock, with no regard - // for the modals still open, so closing the settings drawer frees the page behind the builder. - // This holds it for as long as the builder itself is open. const showing = isModalOpen(slug); + // `body-scroll-lock` frees the page as soon as any one modal gives up its lock, with no regard + // for the modals still open; this class holds it while the builder is. useEffect(() => { if (!showing) return; document.body.classList.add("json-builder-open"); return () => document.body.classList.remove("json-builder-open"); }, [showing]); - // A copy, taken fresh every time the drawer opens. Keeping an unsaved one across a closing would - // mean telling "built here and not saved" apart from "filled in over there", and both read the - // same: the copy differs from the field. So a closing drops the structure, which is at least - // seen at once — where a kept copy quietly wrote its own stale values back on the next save. const dirty = JSON.stringify(draft) !== JSON.stringify(root); useEffect(() => { if (!showing) return; setDraft(root); - // Opened from "Add section": the form a new section is answered in, rather than the canvas of - // one that already exists. if (adding) start("section", "collapsible"); // biome-ignore lint/correctness/useExhaustiveDependencies: the stored value is read on opening only }, [showing]); - // Already in order: the root is a list, and a list is kept the way it is written. const sections = draft; const faults = schemaErrors(draft); const section = draft.find((node) => node.name === current); @@ -118,11 +97,7 @@ export const JsonBuilder = ({ const isSection = making === "section" || Boolean(picked && !picked.at.length); - // Who else stands at this level, which is what the key has to be free of. const neighbours = () => { - // Sections are a list, so their names are read off the nodes. A section being renamed does not - // count its own name as taken; one being created counts every name there is, including the one - // standing selected behind the drawer. if (isSection) { const taken = draft.map((node) => node.name ?? ""); return making ? taken : taken.filter((name) => name !== current); @@ -132,7 +107,6 @@ export const JsonBuilder = ({ }; const fault = edited ? keyFault(key, neighbours()) : ""; - // The way back, one step for every door walked through. const trail = at.map((index, depth) => ({ at: at.slice(0, depth + 1), name: @@ -164,8 +138,6 @@ export const JsonBuilder = ({ setEdited(null); }; - // One write for the whole drawer. A section is named by its key in the root, so renaming it moves - // it there rather than giving it a `name` of its own. const save = () => { if (!edited) return shut(); if (making === "section") { @@ -181,7 +153,6 @@ export const JsonBuilder = ({ shut(); }; - // `fields` is an array, so the order it sits in is the order that is stored and drawn. const reorder = (next: TypedNode[]) => setDraft(editAt(draft, here, (node) => withShape(node, next))); @@ -252,8 +223,6 @@ export const JsonBuilder = ({
- {/* Nothing here writes a fault, so one came in by hand or from a script — and the shape - on screen is not to be trusted until it is gone. */} {faults.length ? {faults.join("; ")} : null} {section ? ( <> @@ -273,7 +242,6 @@ export const JsonBuilder = ({ ))} - {/* They act on where you stand, which is the section itself until you walk into it. */} { remove(here); - // Standing in what was just removed is standing nowhere. if (at.length) setAt(at.slice(0, -1)); else setCurrent(""); }} @@ -374,7 +341,6 @@ export const JsonBuilder = ({ > {edited ? (
- {/* Written on the button: a section is its key, so renaming one moves it in the root. */} ) => setKey(event.target.value)} @@ -404,7 +370,6 @@ export const JsonBuilder = ({ className="json-builder__quiet" onClick={() => { if (picked) remove(picked); - // Standing inside what was just removed is standing nowhere. if (picked?.at.length && picked.at.length <= at.length) setAt(picked.at.slice(0, -1)); shut(); diff --git a/packages/payload-plugin-json-form-builder/src/components/JsonBuilderLevel.tsx b/packages/payload-plugin-json-form-builder/src/components/JsonBuilderLevel.tsx index dec67bde..75a4b109 100644 --- a/packages/payload-plugin-json-form-builder/src/components/JsonBuilderLevel.tsx +++ b/packages/payload-plugin-json-form-builder/src/components/JsonBuilderLevel.tsx @@ -17,7 +17,6 @@ import { KINDS } from "../field/kinds.js"; import { holdsFields } from "../field/typedJson.js"; import type { NodeType, TypedNode } from "../field/typedJson.js"; -// What the node was told to do. Not what kind it is — the tile already says that. const marks = (node: TypedNode) => [ node.required && "required", @@ -34,10 +33,7 @@ type Hands = { onOrder: (fields: TypedNode[]) => void; }; -// One level at a time: a field that holds fields is a door, not a drawer within a drawer. export const JsonBuilderLevel = ({ fields, hands }: { fields: TypedNode[]; hands: Hands }) => { - // `fields` is an array, so its order is the stored order — and a name is unique within a level, - // which makes it an id without a second list to keep in step. const ids = fields.map((node, index) => node.name || `#${index + 1}`); return ( @@ -102,7 +98,6 @@ export const JsonBuilderLevel = ({ fields, hands }: { fields: TypedNode[]; hands > - {/* A mark, not a control: the door is the whole row. */} {holds ? ( diff --git a/packages/payload-plugin-json-form-builder/src/components/JsonBuilderSettings.tsx b/packages/payload-plugin-json-form-builder/src/components/JsonBuilderSettings.tsx index 59ef003c..afa85898 100644 --- a/packages/payload-plugin-json-form-builder/src/components/JsonBuilderSettings.tsx +++ b/packages/payload-plugin-json-form-builder/src/components/JsonBuilderSettings.tsx @@ -18,8 +18,6 @@ const without = (node: TypedNode, key: string) => { const put = (node: TypedNode, key: string, value: unknown) => ({ ...node, [key]: value }) as TypedNode; -// The date a field starts on, which is simply the date it holds until someone picks another. A date -// is the one kind nobody can type a sensible value into, so it is the one worth answering here. const Starts = ({ node, onChange }: Settings) => ( ( /> ); -// An emptied box takes the key away rather than storing a blank one: no `min` at all is what a -// field with no floor says. const Limit = ({ node, onChange, name }: Settings & { name: string }) => ( ( /> ); -// What a select offers is its schema, so it is written here and the form only picks from it. A -// select with nothing to offer is a broken one, which is why the last row cannot be taken away. const Options = ({ node, onChange }: Settings) => { const options = ("options" in node && node.options) || []; const write = (next: string[]) => onChange(put(node, "options", next)); @@ -76,12 +70,10 @@ const Options = ({ node, onChange }: Settings) => { }; export const JsonBuilderSettings = ({ node, onChange }: Settings) => { - // Filling in is a thing a field does, so a group, a tab or a list is asked neither. const fills = !holdsFields(node); return ( <> - {/* A container wears its name as a heading, so it is worth spelling out in words. */} {fills ? null : ( { onChange(node.required ? without(node, "required") : put(node, "required", true)) } /> - {/* Not for an upload or a rich text: their editors are not asked to be read-only here. */} {node.type === "upload" || node.type === "richText" ? null : ( (value == null ? "" : JSON.stringify(value, null, 2)); -// Payload's own json field keeps the raw text as the value while it does not parse, and that string -// then travels: the form holds it, the server is asked to store it, and a jsonb column is handed a -// sentence. Here the text stays here. The field keeps the last value that parsed, so nothing -// downstream ever sees anything but json — no refused save, and no form left looking saved. export const JsonCode = ({ maxHeight, onChange, @@ -22,8 +18,6 @@ export const JsonCode = ({ }) => { const [text, setText] = useState(() => written(value)); const [fault, setFault] = useState(""); - // What was last sent up, so a value changed elsewhere — the builder, a section deleted — redraws - // the editor, while our own writes do not fight the caret. const sent = useRef(text); if (!readOnly) { diff --git a/packages/payload-plugin-json-form-builder/src/components/JsonFolded.tsx b/packages/payload-plugin-json-form-builder/src/components/JsonFolded.tsx index 1fd8393b..1e9f33ce 100644 --- a/packages/payload-plugin-json-form-builder/src/components/JsonFolded.tsx +++ b/packages/payload-plugin-json-form-builder/src/components/JsonFolded.tsx @@ -9,10 +9,6 @@ type Props = Pick< "actions" | "className" | "dragHandleProps" | "header" | "initCollapsed" > & { children: ReactNode }; -// Payload's Collapsible renders a closed accordion's contents at height 0, so the whole value — -// every section, every row, a Lexical editor per rich text — would mount before anything is opened. -// A section waits for its first opening and then keeps what it mounted; inside it everything is -// eager again, so an accordion nested in an open section has nothing left to load. export const JsonFolded = ({ children, ...props }: Props) => { const [open, setOpen] = useState(false); const [mounted, setMounted] = useState(false); diff --git a/packages/payload-plugin-json-form-builder/src/components/JsonFormClient.tsx b/packages/payload-plugin-json-form-builder/src/components/JsonFormClient.tsx index b8d59872..c6cc3225 100644 --- a/packages/payload-plugin-json-form-builder/src/components/JsonFormClient.tsx +++ b/packages/payload-plugin-json-form-builder/src/components/JsonFormClient.tsx @@ -30,14 +30,8 @@ import type { TypedNode, TypedRoot } from "../field/typedJson.js"; type Props = ComponentProps & { mayBuild: boolean }; -// A typed json value (src/fields/jsonField/typedJson.ts) laid out as admin fields. The builder owns -// the shape and Code only shows it — except while the json is broken, which is the one thing the -// builder cannot mend. Both gates are decided on the server, in JsonFormField. export const JsonFormClient = ({ mayBuild, ...props }: Props) => { const { path, field } = props; - // The same rule the field's own `validate` runs on the server. It cannot stop a submit — the edit - // view turns client validation off there (`disableValidationOnSubmit`) — but it is what marks the - // field invalid as the value changes, which is what the message below is read from. const validate = useCallback( (next: unknown, options: { required?: boolean }) => jsonErrors(next, options?.required), [] @@ -46,41 +40,25 @@ export const JsonFormClient = ({ mayBuild, ...props }: Props) => { const { openModal } = useModal(); const builderSlug = useDrawerSlug("json-builder"); const dropSlug = useDrawerSlug("json-form-drop"); - // Which section the confirmation is about. One modal serves them all. const [dropping, setDropping] = useState(""); - // Whether the builder opens on its canvas or straight on the form a new section is answered in. const [adding, setAdding] = useState(false); const build = (fresh: boolean) => { setAdding(fresh); openModal(builderSlug); }; - // Anything that is not a list of sections opens in Code, to be mended by hand. const valid = value == null || Array.isArray(value); const [code, setCode] = useState(!valid); const root: TypedRoot = isTyped(value) ? value : []; const broken = schemaErrors(root).length > 0; const asCode = code || broken; - // Code is open to whoever may build, and read-only to everyone else: it is the same value the form - // shows, and the one way to mend json the form cannot draw. const locked = props.readOnly || !mayBuild; const set = (name: string, node: TypedNode) => setValue(root.map((entry) => (entry.name === name ? node : entry))); - // Deleting a section is the editor's to make: it is the one piece of the shape that is also a - // piece of the page, and an edition that does not run it has no use for its fields either. const drop = (name: string) => setValue(root.filter((entry) => entry.name !== name)); - // Content that was never converted: it has keys but no kinds, so the form cannot draw it and the - // builder would have nothing to open. const untyped = Boolean(value) && !isTyped(value) && Object.keys(value as object).length > 0; - // A list keeps the order it is written in, so a section sits where it was put. const sections = root.filter((node) => visible(node, root)); - // Nothing built yet: both controls in the strip act on sections, and there are none — so the only - // thing offered is the button that makes the first one. const bare = !sections.length && !untyped && !asCode; - // After a refused save: Payload's own pill is pinned to the top right of the field, which is where - // this one keeps its controls, so the one line naming what is missing is unreadable there. Said - // again here, under the sections it is about, and only once a save has actually been turned down. const unfilled = showError ? faults(root) : []; - // Sections are the keys a template reads, which is the one count worth carrying in the foot. const kept = root.length; return ( @@ -100,8 +78,6 @@ export const JsonFormClient = ({ mayBuild, ...props }: Props) => { )} - {/* Two named ways of looking at one value, so which one is open is readable at a glance — - an icon that toggles only says so once you have guessed what it does. */} {mayBuild && !bare && (
- {/* Directly under the header, so what is wrong is read before the thing it is wrong in. All of - them, numbered: a count alone sends the editor hunting. A row opens the json. */} {unfilled.length > 0 ? (
    {unfilled.map((fault, at) => ( @@ -156,7 +130,6 @@ export const JsonFormClient = ({ mayBuild, ...props }: Props) => { ))}
) : null} - {/* The editor fills the card itself; only the form needs room around it. */}
{asCode ? ( <> @@ -177,9 +150,6 @@ export const JsonFormClient = ({ mayBuild, ...props }: Props) => { ) : ( <> - {/* Dragged where they are read, not behind the gear: moving a section is the editor's - work, not a change to the shape. The move is made on the stored list by name rather - than by the seat on screen — a section hidden by a condition is still in the list. */} {sections.length > 0 && ( { No fields yet — someone with the builder adds them.

)} - {/* What a list and a blocks field offer under their rows, so adding a section is where - the hand already goes looking. */} {mayBuild && !untyped && (
); } - // A strip of tabs already says what it holds, so it is drawn bare. A section and a row's own - // field keep the fold: that is where their header and their menu live. if (node.type === "tabs" && !drag && !top) { return ( + } + slug={slug} + > +
+ {shared === null &&

Reading the library…

} + {shared?.length === 0 && ( +

The library has no sections yet.

+ )} + {(shared ?? []).map((node, at) => { + const name = node.name ?? `#${at + 1}`; + const used = here.has(node.name); + return ( + + ); + })} +
+ + ); +}; diff --git a/packages/payload-plugin-json-form-builder/src/components/jsonForm.scss b/packages/payload-plugin-json-form-builder/src/components/jsonForm.scss index ab127ed2..5f01ca1f 100644 --- a/packages/payload-plugin-json-form-builder/src/components/jsonForm.scss +++ b/packages/payload-plugin-json-form-builder/src/components/jsonForm.scss @@ -30,14 +30,7 @@ stroke: currentColor; stroke-width: 1.2px; } - -/* JsonFormField is not one of Payload's own fields, so it is drawn as its own card: a hairline box - with a header strip, a tag by the name, and everything that belongs to it inside. */ .json-form { - /* The four colours the palette cannot give: a muted red for the ring and the banner, where - Payload's scale only offers the signal red, and a green for the status dot, where its - `--theme-success` is blue. Light is the base and dark overrides it, the way Payload builds its - own palette — so a theme Payload has not resolved yet still lands on the readable half. */ --json-form-alarm-bg: var(--theme-error-100); --json-form-alarm-ring: var(--theme-error-250); --json-form-good: #15803d; @@ -57,15 +50,11 @@ html[data-theme="dark"] .json-form { .json-form__bar { align-items: center; height: calc(var(--base) * 2.2); - /* An accordion's header is `--theme-elevation-50`; this is that, a touch deeper. Mixed towards - black rather than taken a step up the scale, which goes lighter as it rises and so would - brighten the strip in the dark theme instead. */ background: color-mix(in srgb, var(--theme-elevation-50), black 8%); border-bottom: 1px solid var(--theme-elevation-100); display: flex; gap: calc(var(--base) / 2); justify-content: space-between; - /* No vertical padding of its own: the strip is sized by `height`, and what sits in it centres. */ padding: 0 calc(var(--base) * 0.5) 0 var(--base); } @@ -75,8 +64,6 @@ html[data-theme="dark"] .json-form { gap: 10px; margin-left: auto; } - -/* Between the one action and the choice of view: they are different kinds of thing. */ .json-form__split { background: var(--theme-elevation-100); height: 20px; @@ -87,9 +74,6 @@ html[data-theme="dark"] .json-form { font-size: 1.05rem; margin: 0; } - -/* Quiet until the cursor is on them. The ground has to be named again under `:hover`: the plain - `.icon-button:hover` weighs the same as this rule and loses only by standing above it. */ .json-form__tools .icon-button { background: var(--theme-elevation-0); border: 1px solid var(--theme-elevation-100); @@ -117,13 +101,8 @@ html[data-theme="dark"] .json-form { .json-form__tools .icon-button svg .stroke { stroke-width: 1.8px; } - -/* The format, said once and quietly — a stamp set into the card's bottom corner rather than a - badge beside the title. Only the inner corner is rounded; the card's own radius clips the rest. */ -/* A status line, the way an editor reads one: what the value is and how big it has grown. */ .json-form__foot { align-items: center; - /* No ground of its own: the card's shows through, and only the line separates them. */ border-top: 1px solid var(--theme-elevation-100); color: var(--theme-elevation-500); display: flex; @@ -133,13 +112,9 @@ html[data-theme="dark"] .json-form { height: calc(var(--base) * 1.7); padding: 0 var(--base); } - -/* Said in the status line rather than over the editor: it is a property of the view, not a notice. */ .json-form__note { margin-left: auto; } - -/* The dot before the word, drawn rather than written. */ .json-form__state::before { border-radius: 50%; content: ""; @@ -156,9 +131,6 @@ html[data-theme="dark"] .json-form { .json-form__state--on::before { background: var(--json-form-good-dot); } - -/* `-600` rather than `-400`: the scale is mirrored between the themes, so this one step reads as a - dark red on white and a light red on black. `-400` is dark in both. */ .json-form__state--off { color: var(--theme-error-600); } @@ -166,16 +138,9 @@ html[data-theme="dark"] .json-form { .json-form__state--off::before { background: var(--theme-error-500); } - -/* The ring around the card, and only the ring: the strip's own line under the header stays the - line it always was. */ .json-form--alarmed { border-color: var(--json-form-alarm-ring); } - -/* What is wrong, said between the header and the thing it is wrong in. The words on this ground - are taken from the high end of the error scale, which Payload mirrors between the themes — so - one step is dark on the light banner and light on the dark one. */ .json-form__alarm { background: var(--json-form-alarm-bg); border-bottom: 1px solid color-mix(in srgb, var(--theme-error-500), transparent 70%); @@ -210,8 +175,6 @@ html[data-theme="dark"] .json-form { font-weight: 600; line-height: 1.4; } - -/* The way to the fault: muted, so the message above it reads first. */ .json-form__crumbs { color: var(--theme-elevation-600); display: flex; @@ -225,16 +188,10 @@ html[data-theme="dark"] .json-form { color: var(--theme-elevation-400); margin-right: calc(var(--base) * 0.25); } - -/* The failing step, told apart by colour alone — a wavy rule under it read as a border in the text. */ .json-form__crumb--bad { color: var(--theme-error-700); } - -/* The format, said once beside the name of the field. */ .json-form__badge { - /* The same ground and line as the switch across the strip: both are chips set into the header - rather than drawn on it. */ background: var(--theme-elevation-0); border: 1px solid var(--theme-elevation-100); border-radius: var(--style-radius-s); @@ -244,9 +201,6 @@ html[data-theme="dark"] .json-form { line-height: 1.6; padding: 1px 6px; } - -/* Two named views rather than one icon that toggles: which one is open is then readable, not - guessed. Built as a segmented control, the shape the admin uses for a choice of one. */ .json-form__views { background: var(--theme-elevation-0); border: 1px solid var(--theme-elevation-100); @@ -284,9 +238,6 @@ html[data-theme="dark"] .json-form { background: var(--theme-elevation-150); color: var(--theme-elevation-1000); } - -/* Payload's buttons carry a vertical margin meant for a form's foot; in the header strip it would - push the box open. */ .json-form__bar .btn { margin-block: 0; } @@ -294,8 +245,6 @@ html[data-theme="dark"] .json-form { .json-form__body { padding: var(--base); } - -/* `.code-editor` is styled as an input, so its own border would sit just inside the card's. */ .json-form__body--code { padding: 0; } @@ -304,29 +253,18 @@ html[data-theme="dark"] .json-form { border: 0; border-radius: 0; } - -/* The editor runs edge to edge, so anything said under it has to carry its own room — the body has - none to give. */ .json-form__body--code > p { margin: 0; padding: calc(var(--base) * 0.5) var(--base); } - -/* Lexical draws no error state of its own, so the box it sits in takes one. */ .json-form__faulty .rich-text-lexical { border-color: var(--theme-error-500); } - -/* What is wrong with one field, under that field. Payload's own `field-error` is positioned for the - top right of a box; this is a line in the flow, where the eye is already reading. */ .json-form__fault { color: var(--theme-error-500); font-size: 12px; margin: calc(var(--base) * 0.2) 0 0; } - -/* What a refused save was about, read at the top of the field rather than hunted for down the - sections. Each field says it again under itself. */ .json-form__notes { color: var(--theme-error-500); display: grid; @@ -342,9 +280,6 @@ html[data-theme="dark"] .json-form { gap: calc(var(--base) * 0.4); margin: 0; } - -/* Numbered, and lined up under the words of the lead rather than under the pill. Not a grid: a - list laid out that way loses its markers. */ .json-form__notes ol { list-style: decimal; margin: 0; @@ -354,23 +289,14 @@ html[data-theme="dark"] .json-form { .json-form__notes li + li { margin-top: calc(var(--base) * 0.15); } - -/* The spacing Payload puts between any two fields, so the form breathes like the rest. */ .json-form__fields { display: flex; flex-direction: column; gap: var(--spacing-field, var(--base)); } - -/* The same gap again under the last section, so the button is not welded to it. Said by what else - is in the body rather than by what stands next to the button: the sections are wrapped by their - own sortable, and an empty field leaves the button alone, where no gap is wanted. */ .json-form__body > .json-form__add-row.btn:not(:only-child) { margin-top: var(--spacing-field, var(--base)); } - -/* The "⋯" trigger, which `array-actions__button` would have styled had we been able to keep that - class. `buttonSize` on the Popup gives it its padding. */ .json-form__menu { border-radius: 100px; cursor: pointer; @@ -379,9 +305,6 @@ html[data-theme="dark"] .json-form { .json-form__menu:hover { background: var(--theme-elevation-0); } - -/* A switched-off key greyed and struck through — the class sits on the node itself, so this is an - accordion's header, a list's heading or a group's. */ .json-form__hidden > .collapsible__toggle-wrap, .json-form__hidden > .array-field__header, .json-form__hidden > .group-field__header { @@ -389,8 +312,6 @@ html[data-theme="dark"] .json-form { opacity: 0.5; text-decoration: line-through; } - -/* The top-level sections sit closer together than fields do: they are folded headers, not inputs. */ .json-form__body > .json-form__fields { gap: calc(var(--base) / 2); } @@ -405,19 +326,12 @@ html[data-theme="dark"] .json-form { margin: 0; padding: calc(var(--base) * 0.5) calc(var(--base) * 0.75); } - -/* A drawer over a drawer: `body-scroll-lock` restores the page as soon as one of them unlocks, so - the page behind the builder has to be held by hand. */ body.json-builder-open { overflow: hidden !important; } - -/* In the drawer's own header, where the title already sits: it never scrolls away. */ .json-builder__bar { gap: calc(var(--base) * 0.5); } - -/* The cross is the way out; the button that writes should not sit against it. */ .json-builder__bar .json-builder__close { margin-left: calc(var(--base) * 0.7); } @@ -427,9 +341,6 @@ body.json-builder-open { --btn-padding-inline-start: calc(var(--base) * 1.1); margin: 0; } - -/* The drawer's own scroller holds the header above us, so the grid takes what is left of the screen - and each column scrolls on its own. */ .json-builder__shell .drawer__content-children { display: flex; flex-direction: column; @@ -443,8 +354,6 @@ body.json-builder-open { grid-template-columns: 19rem minmax(0, 1fr) 23rem; min-height: 0; } - -/* Payload's buttons carry a bottom margin for a form; here they sit in grids that own the spacing. */ .json-builder .btn, .json-builder__options .btn, .json-builder__buttons .btn { @@ -465,8 +374,6 @@ body.json-builder-open { border-right: 1px solid var(--theme-elevation-100); padding-left: 0; } - -/* Adding a section is not one of them, so it stands apart from the list. */ .json-builder__sections .btn { margin-top: calc(var(--base) * 0.6); } @@ -481,8 +388,6 @@ body.json-builder-open { .json-builder__canvas { min-width: 0; } - -/* A pill carries its own colours, its hover and its picked state; only the row shape is ours. */ .json-builder__section { --pill-padding-block-end: calc(var(--base) * 0.4); --pill-padding-block-start: calc(var(--base) * 0.4); @@ -525,8 +430,6 @@ body.json-builder-open { background: var(--theme-elevation-50); border-color: var(--theme-elevation-200); } - -/* A row that holds fields is a door: it says so by the hand it shows. */ .json-builder__card--holds .json-builder__card-open { cursor: pointer; } @@ -534,9 +437,6 @@ body.json-builder-open { .json-builder__card--holds:hover { border-color: var(--theme-elevation-300); } - -/* The icon is drawn in a 20px box with room around it, so the pull closes the gap it leaves to its - right without moving the handle itself. */ .json-builder__grip { align-items: center; color: var(--theme-elevation-400); @@ -550,8 +450,6 @@ body.json-builder-open { .json-builder__grip:active { cursor: grabbing; } - -/* The row is the door into what it holds, so the whole of it is the handle. */ .json-builder__card-open { align-items: center; background: none; @@ -605,8 +503,6 @@ body.json-builder-open { background: var(--theme-elevation-50); color: var(--theme-elevation-1000); } - -/* Quiet until the card is under the cursor, so a long list reads as content, not as controls. */ .json-builder__card-actions .icon-button { opacity: 0.45; } @@ -638,9 +534,6 @@ body.json-builder-open { gap: calc(var(--base) * 0.2); min-width: 0; } - -/* The collapsible's toggle is an absolute layer over its whole header, so anything meant to be - clicked there has to be lifted above it. */ .json-builder__card-actions { align-items: center; display: flex; @@ -674,8 +567,6 @@ body.json-builder-open { flex-wrap: wrap; gap: 4px; } - -/* A pill is sized for a row of a table; here it is a footnote under a name. */ .json-builder__marks .pill { --pill-padding-inline-end: calc(var(--base) * 0.3); --pill-padding-inline-start: calc(var(--base) * 0.3); @@ -744,15 +635,11 @@ body.json-builder-open { .tip { display: inline-flex; } - -/* Payload's tooltip is built for two words; ours carries a sentence. */ .tip-bubble { line-height: 1.4; max-width: 250px; white-space: normal; } - -/* Payload draws its caret under the tooltip; beside the anchor it has to point the other way. */ .tip-bubble--left::after { border-bottom: 6px solid transparent; border-left: 6px solid var(--theme-elevation-800); @@ -788,8 +675,6 @@ body.json-builder-open { font-size: 1.05rem; font-weight: 600; } - -/* Payload sizes a drawer inline, by depth, so narrowing one needs the heavier hand. */ .json-builder__drawer .drawer__content { width: min(600px, 100%) !important; } @@ -830,9 +715,6 @@ body.json-builder-open { gap: var(--base); margin-top: calc(var(--base) * 0.5); } - -/* Beside the one action that lands, these are ways out: plain text that says so on hover. Payload - paints a button through `--color`, read on an inner element, so plain `color` never reaches it. */ .json-builder__quiet { --color: var(--theme-elevation-600); --hover-color: var(--theme-elevation-1000); @@ -843,3 +725,65 @@ body.json-builder-open { .json-builder__quiet:hover { text-decoration: underline; } + +.json-form__badge--shared { + border-color: var(--theme-warning-250); + color: var(--theme-warning-650); + font-family: var(--font-body); + margin-inline-start: var(--base-025); + text-transform: uppercase; +} + +.json-form__adders { + display: flex; + gap: var(--base-050); +} + +.json-library__bar { + align-items: center; + display: flex; + gap: var(--base-050); + + .drawer__header__title { + flex: 1; + } +} + +.json-library__list { + display: grid; + gap: var(--base-050); + grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); + padding: var(--base); +} + +.json-library__card { + background: var(--theme-elevation-50); + border: 1px solid var(--theme-elevation-100); + border-radius: var(--style-radius-m); + cursor: pointer; + display: flex; + flex-direction: column; + gap: var(--base-025); + padding: var(--base-050) var(--base-075); + text-align: start; + + &--picked { + border-color: var(--theme-success-400); + box-shadow: inset 0 0 0 1px var(--theme-success-400); + } + + &--used { + cursor: not-allowed; + opacity: 0.5; + } +} + +.json-library__name { + color: var(--theme-elevation-1000); +} + +.json-library__key { + color: var(--theme-elevation-500); + font-family: var(--font-mono); + font-size: 12px; +} diff --git a/packages/payload-plugin-json-form-builder/src/field/checks.ts b/packages/payload-plugin-json-form-builder/src/field/checks.ts index a20b41f1..cc9cc979 100644 --- a/packages/payload-plugin-json-form-builder/src/field/checks.ts +++ b/packages/payload-plugin-json-form-builder/src/field/checks.ts @@ -122,6 +122,7 @@ const KEYS: Record = { required: { kind: "boolean" }, rows: { kind: "array", needed: ["array"], only: ["array"] }, showIf: { kind: "object" }, + source: { kind: "string" }, type: { kind: "string" }, value: { kind: "any", needed: LEAVES, only: LEAVES, reads: "a field, not a group or a list" }, }; diff --git a/packages/payload-plugin-json-form-builder/src/field/typedJson.ts b/packages/payload-plugin-json-form-builder/src/field/typedJson.ts index 6527b093..6f4431a7 100644 --- a/packages/payload-plugin-json-form-builder/src/field/typedJson.ts +++ b/packages/payload-plugin-json-form-builder/src/field/typedJson.ts @@ -10,6 +10,7 @@ export type LeafType = export type Condition = { field: string } & ({ equals: unknown } | { notEquals: unknown }); type Base = { name?: string; + source?: string; label?: string; required?: boolean; readOnly?: boolean; diff --git a/packages/payload-plugin-json-form-builder/src/library/reconcileSection.ts b/packages/payload-plugin-json-form-builder/src/library/reconcileSection.ts index e114a8ed..1f648a48 100644 --- a/packages/payload-plugin-json-form-builder/src/library/reconcileSection.ts +++ b/packages/payload-plugin-json-form-builder/src/library/reconcileSection.ts @@ -2,15 +2,10 @@ import { dressed, shapeOf } from "../field/edits.js"; import { isTyped } from "../field/typedJson.js"; import type { ArrayNode, Container, TypedNode, TypedRoot } from "../field/typedJson.js"; -export const SOURCE = "source"; - -const sourceOf = (node: TypedNode) => { - const named = (node as Record)[SOURCE]; - return typeof named === "string" ? named : ""; -}; +const sourceOf = (node: TypedNode) => node.source ?? ""; const reshape = (own: TypedNode, source: TypedNode): TypedNode => { - const head = { ...source, name: own.name, hidden: own.hidden, [SOURCE]: sourceOf(own) }; + const head = { ...source, name: own.name, hidden: own.hidden, source: own.source }; if (source.type === "array") { const { fields: _shape, ...rest } = head as ArrayNode; @@ -42,5 +37,9 @@ export const reconcile = (value: unknown, library: unknown): unknown => { }); }; +export const followed = (node: TypedNode): boolean => sourceOf(node) !== ""; + +export const following = (node: TypedNode, name: string): TypedNode => ({ ...node, source: name }); + export const follows = (value: unknown): boolean => isTyped(value) && (value as TypedRoot).some((node) => sourceOf(node) !== ""); diff --git a/packages/payload-plugin-json-form-builder/src/plugin.ts b/packages/payload-plugin-json-form-builder/src/plugin.ts index ea57954d..412a1cde 100644 --- a/packages/payload-plugin-json-form-builder/src/plugin.ts +++ b/packages/payload-plugin-json-form-builder/src/plugin.ts @@ -92,7 +92,10 @@ export const jsonFormPlugin = ...json.admin, components: { ...json.admin?.components, - Field: { path: FIELD, serverProps: { build: own.build ?? build } }, + Field: { + path: FIELD, + serverProps: { build: own.build ?? build, library: Boolean(own.library) }, + }, }, }; }); From 0ba2b97a2609db6f7b43e3148ea05b8aed1efb5e Mon Sep 17 00:00:00 2001 From: Lero Date: Tue, 6 Oct 2026 12:30:49 +0300 Subject: [PATCH 4/5] docs: lead with the builder and describe shared sections --- .../README.md | 52 +++++++++++++++--- .../docs/builder.png | Bin 0 -> 94583 bytes .../docs/form.png | Bin 0 -> 45287 bytes 3 files changed, 45 insertions(+), 7 deletions(-) create mode 100644 packages/payload-plugin-json-form-builder/docs/builder.png create mode 100644 packages/payload-plugin-json-form-builder/docs/form.png diff --git a/packages/payload-plugin-json-form-builder/README.md b/packages/payload-plugin-json-form-builder/README.md index cf67e1c5..d477ccef 100644 --- a/packages/payload-plugin-json-form-builder/README.md +++ b/packages/payload-plugin-json-form-builder/README.md @@ -1,6 +1,10 @@ # @focus-reactive/payload-plugin-json-form-builder -Turn a Payload CMS v3 `json` field into real admin fields — text, number, date, select, checkbox, upload, rich text, lists, groups and tabs — and let an admin build that form from inside the admin panel, in a drawer, without touching code. +**A `json` field becomes real admin fields — and an admin can add new ones without a deploy.** + +Text, number, date, select, checkbox, upload, rich text, lists, groups and tabs: the editor fills in a form, not a textarea. And when a field is missing, nobody writes a migration — it is added from inside the admin panel, in a builder, and it is there on the next save. + +![The form a json field becomes](https://raw.githubusercontent.com/focusreactive/payload-plugins/main/packages/payload-plugin-json-form-builder/docs/form.png) The unusual part: **there is no schema in your repository.** The shape lives inside the value itself. Every node says what it is, so the form can be drawn from the data alone, and changing the shape is a content edit rather than a deploy. Your frontend never sees that bookkeeping: everything outside the app reads plain values by key. @@ -27,12 +31,23 @@ Libraries like `rjsf` and `jsonforms` render a form **from a schema you write in ## What you get -- **A form instead of a textarea.** Thirteen kinds, nested as deep as you like. -- **A builder.** A drawer behind the gear: pick a kind from the palette, name the key, set it required, give it a minimum. One level at a time. -- **A JSON view, always.** The same value as code, for reading, diffing and the occasional hand fix. Editable by whoever may build. -- **Validation that behaves like Payload's.** `required`, `min`/`max`, `minRows`/`maxRows` are judged on the server, the save is refused, the field is marked, and the message names the path. -- **Rich text that round-trips.** Stored as html, written by Payload's own converters and read back by a parser kept in step with them. -- **Plain values for the site.** No tree-walking in your templates. +**A form instead of a textarea.** Thirteen kinds, nested as deep as you like, drawn with Payload's own inputs — the upload field picks from your Media collection, rich text is the lexical editor your project configured, a list is a list of cards you drag. + +**A builder, in the admin.** Behind the gear: a sidebar of sections, a canvas of the one you are on, and a palette of kinds on the right. Pick a kind, name the key, set it required, give it a minimum. A new field reaches every row of a list that already has rows, and no deploy is involved. + +![The builder](https://raw.githubusercontent.com/focusreactive/payload-plugins/main/packages/payload-plugin-json-form-builder/docs/builder.png) + +**Sections of your own.** A section is a named group at the root of the value, and it is what a template reads: `settings.hero.title`. Add one, drag it, hide it from the site without deleting it. + +**Shared sections.** Build a section once, in the plugin's own global, and point many documents at it. Each keeps its own values; the shape stays in one place and every document follows it. Switched on by one option — see [`library`](#library). + +**A JSON view, always.** The same value as code, for reading, diffing and the occasional hand fix. Editable by whoever may build. + +**Validation that behaves like Payload's.** `required`, `min`/`max`, `minRows`/`maxRows` are judged on the server, the save is refused, the field is marked, and the message names the path. + +**Rich text that round-trips.** Stored as html, written by Payload's own converters and read back by a parser kept in step with them. + +**Plain values for the site.** No tree-walking in your templates. ## Requirements @@ -103,11 +118,14 @@ That is all. Styles travel with the components; there is nothing to import. jsonFormPlugin({ build: true, global: { slug: 'json-form', fields: [] }, + library: true, richText: { editor: lexicalEditor(), holds: { headings: true, quotes: true, rules: true } }, uploads: 'media', }) ``` +Most projects set two of these: `richText`, because a rich text node needs an editor to be drawn with, and `library`, when sections are worth sharing. The rest have defaults that fit. + ### `build` Who may open the builder and edit the JSON by hand. Everyone, by default — the field already sits behind the document's own update access, so this is a second gate, not the first one. @@ -136,6 +154,26 @@ global: false // no global — and An empty global is `admin.hidden`, since the only field in it is one nobody can see. Put fields in it and it appears; `admin`, `access`, `label` and `hooks` are passed straight through if you want to say otherwise. `false` leaves the config without the global, which also leaves rich text nowhere to anchor. +### `library` + +Shared sections: one field in the plugin's own [`global`](#global) holding sections that documents follow. + +```ts +library: false // the default: no shared sections +library: true // a `shared` field, labelled "Shared sections" +library: { name: 'blocks', label: 'Blocks', description: 'Built once, used everywhere' } +``` + +A section built there is attached to a document from **From library**, next to *Add section*. The document gets its own copy of the values and a note of where the shape came from, so: + +- **values are the document's.** Changing them in the library later changes nothing anywhere; +- **the shape is the library's.** A field added there appears in every document that follows the section, one removed disappears, one retyped is retyped. It is read on the way out, so a document is never stale; +- **keys are frozen.** Once a section exists in the library, its keys and its fields' keys cannot be renamed — only added to or removed. Renaming would leave every document that followed it pointing at nothing; +- **a section that follows one is not editable in the builder.** It is left out of the builder's sidebar and carries a `global` chip in the form, because its shape is not this document's to change; +- **removing it from the library is safe.** The documents keep what they have; those sections simply become their own again. + +A document may not follow two sections with the same key, so one whose key is already taken is offered but not selectable. + ### `richText` ```ts diff --git a/packages/payload-plugin-json-form-builder/docs/builder.png b/packages/payload-plugin-json-form-builder/docs/builder.png new file mode 100644 index 0000000000000000000000000000000000000000..045fc68d7477fa1b481e553f88c5237395cb2a43 GIT binary patch literal 94583 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∨2tEfz1>z8Hfy_O( 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 literal 0 HcmV?d00001 From 7d560dabde4064d7da1b55343e2982963649b70f Mon Sep 17 00:00:00 2001 From: Lero Date: Tue, 6 Oct 2026 14:22:57 +0300 Subject: [PATCH 5/5] feat: attach a shared section from the builder's sidebar --- .../README.md | 30 +- .../src/components/JsonBuilder.tsx | 111 ++++- .../src/components/JsonFormClient.tsx | 428 +++++++++--------- .../src/components/JsonFormField.tsx | 3 + .../src/components/LibraryPicker.tsx | 99 ---- .../src/components/jsonForm.scss | 93 ++-- .../src/field/index.ts | 6 +- .../src/index.ts | 1 + .../src/library/jsonLibraryField.ts | 2 +- .../src/library/reconcileSection.ts | 4 +- .../src/plugin.ts | 6 +- 11 files changed, 393 insertions(+), 390 deletions(-) delete mode 100644 packages/payload-plugin-json-form-builder/src/components/LibraryPicker.tsx diff --git a/packages/payload-plugin-json-form-builder/README.md b/packages/payload-plugin-json-form-builder/README.md index d477ccef..f050b7d3 100644 --- a/packages/payload-plugin-json-form-builder/README.md +++ b/packages/payload-plugin-json-form-builder/README.md @@ -39,7 +39,7 @@ Libraries like `rjsf` and `jsonforms` render a form **from a schema you write in **Sections of your own.** A section is a named group at the root of the value, and it is what a template reads: `settings.hero.title`. Add one, drag it, hide it from the site without deleting it. -**Shared sections.** Build a section once, in the plugin's own global, and point many documents at it. Each keeps its own values; the shape stays in one place and every document follows it. Switched on by one option — see [`library`](#library). +**Shared sections.** Build a section once and point many documents at it. Each keeps its own values; the shape stays in one place and every document follows it — add a field there and it appears in all of them. See [`library`](#library). **A JSON view, always.** The same value as code, for reading, diffing and the occasional hand fix. Editable by whoever may build. @@ -156,23 +156,33 @@ An empty global is `admin.hidden`, since the only field in it is one nobody can ### `library` -Shared sections: one field in the plugin's own [`global`](#global) holding sections that documents follow. +Shared sections: one json field holding sections that documents elsewhere follow. ```ts -library: false // the default: no shared sections -library: true // a `shared` field, labelled "Shared sections" +library: false // the default: no shared sections +library: true // the field goes into the plugin's own global library: { name: 'blocks', label: 'Blocks', description: 'Built once, used everywhere' } ``` -A section built there is attached to a document from **From library**, next to *Add section*. The document gets its own copy of the values and a note of where the shape came from, so: +Or mount it yourself, wherever it belongs — beside the field it shares with, in a global of your own: + +```ts +import { jsonLibraryField } from '@focus-reactive/payload-plugin-json-form-builder' + +fields: [ + { type: 'group', label: 'Components', fields: [jsonField({ name: 'components' })] }, + { type: 'group', label: 'Shared sections', fields: [jsonLibraryField()] }, +] +``` + +A field offers the library when it asks to: `jsonField({ shares: true })`. Open its builder and the sidebar grows two groups under *Add section* — **Attached sections**, each with a `×` that detaches it, and **Shared sections**, each with a `+` that attaches it. In the form, an attached section carries a `global` chip. - **values are the document's.** Changing them in the library later changes nothing anywhere; -- **the shape is the library's.** A field added there appears in every document that follows the section, one removed disappears, one retyped is retyped. It is read on the way out, so a document is never stale; -- **keys are frozen.** Once a section exists in the library, its keys and its fields' keys cannot be renamed — only added to or removed. Renaming would leave every document that followed it pointing at nothing; -- **a section that follows one is not editable in the builder.** It is left out of the builder's sidebar and carries a `global` chip in the form, because its shape is not this document's to change; -- **removing it from the library is safe.** The documents keep what they have; those sections simply become their own again. +- **the shape is the library's.** A field added there appears in every document that follows the section, one removed disappears, one retyped is retyped — read on the way out, so nothing is stale; +- **keys are frozen.** Once a section exists in the library, its keys and its fields' keys cannot be renamed, only added to or removed. Renaming would leave every document that followed it pointing at nothing; +- **removing a section from the library takes nothing away.** Every document keeps it, with its content, and simply stops following: the chip goes and the shape is theirs to edit again. -A document may not follow two sections with the same key, so one whose key is already taken is offered but not selectable. +One caveat worth knowing: a document stores its own copy of the shape and rewrites it when it is saved. So after changing a shape in the library, save the documents that follow it — until they are saved, their copy is the one from last time, and that is the shape they fall back to if they ever stop following. ### `richText` diff --git a/packages/payload-plugin-json-form-builder/src/components/JsonBuilder.tsx b/packages/payload-plugin-json-form-builder/src/components/JsonBuilder.tsx index b7bb6f97..f23b75cd 100644 --- a/packages/payload-plugin-json-form-builder/src/components/JsonBuilder.tsx +++ b/packages/payload-plugin-json-form-builder/src/components/JsonBuilder.tsx @@ -9,16 +9,17 @@ import { ConfirmationModal, Drawer, EditIcon, - Pill, TextInput, Tooltip, useDrawerSlug, + useConfig, useModal, XIcon, } from "@payloadcms/ui"; import { IconButton } from "./IconButton.js"; import { JsonBuilderLevel } from "./JsonBuilderLevel.js"; -import { followed } from "../library/reconcileSection.js"; +import { followed, following } from "../library/reconcileSection.js"; +import { isTyped } from "../field/typedJson.js"; import { JsonBuilderSettings } from "./JsonBuilderSettings.js"; import { labelOf } from "./JsonNode.js"; import { schemaErrors } from "../field/checks.js"; @@ -44,16 +45,16 @@ const holds = (node: TypedNode | undefined, missing: readonly NodeType[]): reado : TYPES.filter((type) => type !== "tab" && !missing.includes(type)); export const JsonBuilder = ({ - adding, library, root, onChange, + shares, slug, }: { - adding?: boolean; library?: boolean; root: TypedRoot; onChange: (root: TypedRoot) => void; + shares?: boolean; slug: string; }) => { const [draft, setDraft] = useState(root); @@ -64,7 +65,7 @@ export const JsonBuilder = ({ const [key, setKey] = useState(""); const [hovered, setHovered] = useState(""); const [making, setMaking] = useState<"field" | "section" | null>(null); - const { anchor: richTextAnchor, uploads } = useJsonFormConfig(); + const { anchor: richTextAnchor, library: held, uploads } = useJsonFormConfig(); const missing: NodeType[] = [ ...(uploads ? [] : ["upload" as NodeType]), ...(richTextAnchor ? [] : ["richText" as NodeType]), @@ -86,11 +87,44 @@ export const JsonBuilder = ({ useEffect(() => { if (!showing) return; setDraft(root); - if (adding) start("section", "collapsible"); // biome-ignore lint/correctness/useExhaustiveDependencies: the stored value is read on opening only }, [showing]); const sections = draft; + const [shared, setShared] = useState([]); + const { config } = useConfig(); + + useEffect(() => { + if (!(showing && shares && held)) return; + const request = new AbortController(); + fetch(`${config.routes.api}/globals/${held.global}?depth=0`, { + credentials: "include", + signal: request.signal, + }) + .then((answer) => answer.json()) + .then((doc) => { + const value = (doc as Record)?.[held.field]; + setShared(isTyped(value) ? (value as TypedRoot) : []); + }) + .catch(() => setShared([])); + return () => request.abort(); + }, [held, config.routes.api, shares, showing]); + + const take = (node: TypedNode) => setDraft([...draft, following(node, node.name ?? "")]); + + const dropSection = (spot: Spot) => { + remove(spot); + setCurrent(""); + setAt([]); + }; + + const drop = (entry: string) => { + setDraft(draft.filter((node) => (node.name || "") !== entry)); + if (current === entry) setCurrent(""); + }; + + const free = shared.filter((node) => !draft.some((own) => own.name === node.name)); + const kept = draft.filter(followed); const faults = schemaErrors(draft); const section = draft.find((node) => node.name === current); const here: Spot = { section: current, at }; @@ -206,15 +240,18 @@ export const JsonBuilder = ({ const entry = held.name || `#${index + 1}`; if (followed(held)) return null; return ( - walk(entry)} - pillStyle={entry === current ? "dark" : "light"} + type="button" > {held.label || entry} - {shapeOf(held).length} - + ); })} + + {shares && kept.length > 0 && ( + <> +
Attached sections
+ {kept.map((node, index) => { + const entry = node.name || `#${index + 1}`; + return ( +
+ {node.label || entry} + +
+ ); + })} + + )} + + {shares && free.length > 0 && ( + <> +
Shared sections
+ {free.map((node, index) => { + const entry = node.name || `#${index + 1}`; + return ( +
+ {node.label || entry} + +
+ ); + })} + + )}
@@ -257,9 +338,9 @@ export const JsonBuilder = ({ className="json-builder__drop" label={at.length ? "Remove this field" : "Remove this section"} onClick={() => { + if (!at.length) return dropSection(here); remove(here); - if (at.length) setAt(at.slice(0, -1)); - else setCurrent(""); + setAt(at.slice(0, -1)); }} > @@ -374,6 +455,10 @@ export const JsonBuilder = ({ buttonStyle="none" className="json-builder__quiet" onClick={() => { + if (picked && isSection) { + shut(); + return dropSection(picked); + } if (picked) remove(picked); if (picked?.at.length && picked.at.length <= at.length) setAt(picked.at.slice(0, -1)); diff --git a/packages/payload-plugin-json-form-builder/src/components/JsonFormClient.tsx b/packages/payload-plugin-json-form-builder/src/components/JsonFormClient.tsx index debbe9f3..ed4f13ae 100644 --- a/packages/payload-plugin-json-form-builder/src/components/JsonFormClient.tsx +++ b/packages/payload-plugin-json-form-builder/src/components/JsonFormClient.tsx @@ -10,6 +10,7 @@ import { ConfirmationModal, DraggableSortable, DraggableSortableItem, + FieldDescription, FieldLabel, GearIcon, ListViewIcon, @@ -25,7 +26,6 @@ import { JsonBuilder } from "./JsonBuilder.js"; import { JsonCode } from "./JsonCode.js"; import { hideItem, JsonNode, labelOf } from "./JsonNode.js"; import { followed } from "../library/reconcileSection.js"; -import { LibraryPicker } from "./LibraryPicker.js"; import { useJsonFormConfig } from "../useJsonFormConfig.js"; import { faults, jsonErrors, schemaErrors } from "../field/checks.js"; import { isObject, isTyped, visible } from "../field/typedJson.js"; @@ -34,9 +34,10 @@ import type { TypedNode, TypedRoot } from "../field/typedJson.js"; type Props = ComponentProps & { library?: boolean; mayBuild: boolean; + shares?: boolean; }; -export const JsonFormClient = ({ library, mayBuild, ...props }: Props) => { +export const JsonFormClient = ({ library, mayBuild, shares: offered, ...props }: Props) => { const { path, field } = props; const validate = useCallback( (next: unknown, options: { required?: boolean }) => jsonErrors(next, options?.required), @@ -46,20 +47,15 @@ export const JsonFormClient = ({ library, mayBuild, ...props }: Props) => { const { openModal } = useModal(); const builderSlug = useDrawerSlug("json-builder"); const dropSlug = useDrawerSlug("json-form-drop"); - const librarySlug = useDrawerSlug("json-form-library"); const [dropping, setDropping] = useState(""); - const [adding, setAdding] = useState(false); - const build = (fresh: boolean) => { - setAdding(fresh); - openModal(builderSlug); - }; + const build = () => openModal(builderSlug); const valid = value == null || Array.isArray(value); const [code, setCode] = useState(!valid); const root: TypedRoot = isTyped(value) ? value : []; const broken = schemaErrors(root).length > 0; const asCode = code || broken; const locked = props.readOnly || !mayBuild; - const shares = Boolean(useJsonFormConfig().library) && !library; + const shares = Boolean(offered) && Boolean(useJsonFormConfig().library) && !library; const set = (name: string, node: TypedNode) => setValue(root.map((entry) => (entry.name === name ? node : entry))); const drop = (name: string) => setValue(root.filter((entry) => entry.name !== name)); @@ -70,236 +66,222 @@ export const JsonFormClient = ({ library, mayBuild, ...props }: Props) => { const kept = root.length; return ( -
0 && "json-form--alarmed")}> -
-

- -

- json + <> + {field.admin?.description ? ( +
+ +
+ ) : null} +
0 && "json-form--alarmed")}> +
+

+ +

+ json -
- {mayBuild && !bare && ( - <> - build(false)}> - - - - - )} - {mayBuild && !bare && ( -
- - -
- )} +
+ {mayBuild && !bare && ( + <> + + + + + + )} + {mayBuild && !bare && ( +
+ + +
+ )} +
-
- {unfilled.length > 0 ? ( -
    - {unfilled.map((fault, at) => ( -
  1. - - {String(at + 1).padStart(2, "0")} - - {fault.message} - - {fault.path.map((step, depth) => ( - - {depth > 0 ? → : null} - {step} - - ))} + {unfilled.length > 0 ? ( +
      + {unfilled.map((fault, at) => ( +
    1. + + {String(at + 1).padStart(2, "0")} + + {fault.message} + + {fault.path.map((step, depth) => ( + + {depth > 0 ? → : null} + {step} + + ))} + - -
    2. - ))} -
    - ) : null} -
    - {asCode ? ( - <> - - {broken && !locked ? ( -

    - The form cannot draw this json, so it stays in Code. The builder lists what is - wrong. -

    - ) : null} - - ) : ( - <> - {sections.length > 0 && ( - node.name || `#${at + 1}`)} - onDragEnd={({ moveFromIndex, moveToIndex }) => { - const from = root.findIndex( - (node) => node.name === sections[moveFromIndex]?.name - ); - const to = root.findIndex((node) => node.name === sections[moveToIndex]?.name); - if (from < 0 || to < 0) return; - setValue(root.toSpliced(from, 1).toSpliced(to, 0, root[from])); - }} - > - {sections.map((node, at) => { - const key = node.name || `#${at + 1}`; - return ( - - {({ - attributes, - isDragging, - listeners, - setNodeRef, - transform, - transition, - }) => ( -
    -
  2. + ))} +
+ ) : null} +
+ {asCode ? ( + <> + + {broken && !locked ? ( +

+ The form cannot draw this json, so it stays in Code. The builder lists what is + wrong. +

+ ) : null} + + ) : ( + <> + {sections.length > 0 && ( + node.name || `#${at + 1}`)} + onDragEnd={({ moveFromIndex, moveToIndex }) => { + const from = root.findIndex( + (node) => node.name === sections[moveFromIndex]?.name + ); + const to = root.findIndex((node) => node.name === sections[moveToIndex]?.name); + if (from < 0 || to < 0) return; + setValue(root.toSpliced(from, 1).toSpliced(to, 0, root[from])); + }} + > + {sections.map((node, at) => { + const key = node.name || `#${at + 1}`; + return ( + + {({ + attributes, + isDragging, + listeners, + setNodeRef, + transform, + transition, + }) => ( +
+
- )} -
- ); - })} -
- )} - {untyped && ( -

- This json has no field types yet — it is converted by the typed-json script. -

- )} - {!sections.length && !untyped && !mayBuild && ( -

- No fields yet — someone with the builder adds them. -

- )} - {mayBuild && !untyped && ( -
+ ]} + node={node} + onChange={(next) => set(key, next)} + top + /> +
+ )} + + ); + })} + + )} + {untyped && ( +

+ This json has no field types yet — it is converted by the typed-json script. +

+ )} + {!sections.length && !untyped && !mayBuild && ( +

+ No fields yet — someone with the builder adds them. +

+ )} + {mayBuild && !untyped && ( - {shares && ( - - )} -
+ )} + + )} +
+
+ + > + {broken + ? "invalid" + : unfilled.length + ? `${unfilled.length} ${unfilled.length === 1 ? "error" : "errors"}` + : "valid"} + + {`${kept} ${kept === 1 ? "section" : "sections"}`} + {asCode && locked ? ( + read only — the gear builds the sections + ) : null} +
+ {mayBuild && ( + )} -
-
- - {broken - ? "invalid" - : unfilled.length - ? `${unfilled.length} ${unfilled.length === 1 ? "error" : "errors"}` - : "valid"} - - {`${kept} ${kept === 1 ? "section" : "sections"}`} - {asCode && locked ? ( - read only — the gear builds the sections - ) : null} -
- {mayBuild && shares && ( - setValue([...root, ...nodes])} - root={root} - slug={librarySlug} + node.name === dropping), + dropping + )}" and everything filled in under it.`} + confirmLabel="Delete" + heading="Delete section" + modalSlug={dropSlug} + onConfirm={() => drop(dropping)} /> - )} - {mayBuild && ( - - )} - node.name === dropping), - dropping - )}" and everything filled in under it.`} - confirmLabel="Delete" - heading="Delete section" - modalSlug={dropSlug} - onConfirm={() => drop(dropping)} - /> -
+
+ ); }; diff --git a/packages/payload-plugin-json-form-builder/src/components/JsonFormField.tsx b/packages/payload-plugin-json-form-builder/src/components/JsonFormField.tsx index 4bdda888..6bb51486 100644 --- a/packages/payload-plugin-json-form-builder/src/components/JsonFormField.tsx +++ b/packages/payload-plugin-json-form-builder/src/components/JsonFormField.tsx @@ -7,11 +7,13 @@ import { JsonFormClient } from "./JsonFormClient.js"; type Props = ComponentProps & { build?: BuildGate; library?: boolean; + shares?: boolean; }; export const JsonFormField = ({ build, library, + shares, clientField, path, permissions, @@ -23,6 +25,7 @@ export const JsonFormField = ({ field={clientField} library={Boolean(library)} mayBuild={allows(build, req.user)} + shares={Boolean(shares)} path={path} permissions={permissions} readOnly={readOnly} diff --git a/packages/payload-plugin-json-form-builder/src/components/LibraryPicker.tsx b/packages/payload-plugin-json-form-builder/src/components/LibraryPicker.tsx deleted file mode 100644 index 19779822..00000000 --- a/packages/payload-plugin-json-form-builder/src/components/LibraryPicker.tsx +++ /dev/null @@ -1,99 +0,0 @@ -"use client"; - -import { clsx as cn } from "clsx"; -import { useEffect, useState } from "react"; -import { Button, Drawer, useConfig, useModal } from "@payloadcms/ui"; -import { following } from "../library/reconcileSection.js"; -import { isTyped } from "../field/typedJson.js"; -import type { TypedNode, TypedRoot } from "../field/typedJson.js"; -import { useJsonFormConfig } from "../useJsonFormConfig.js"; -import { labelOf } from "./JsonNode.js"; - -const taken = (root: TypedRoot) => new Set(root.map((node) => node.name)); - -export const LibraryPicker = ({ - onAdd, - root, - slug, -}: { - onAdd: (nodes: TypedNode[]) => void; - root: TypedRoot; - slug: string; -}) => { - const { config } = useConfig(); - const { library } = useJsonFormConfig(); - const { closeModal } = useModal(); - const [shared, setShared] = useState(null); - const [picked, setPicked] = useState([]); - - useEffect(() => { - if (!library) return; - const request = new AbortController(); - fetch(`${config.routes.api}/globals/${library.global}?depth=0`, { - credentials: "include", - signal: request.signal, - }) - .then((answer) => answer.json()) - .then((doc) => { - const value = (doc as Record)?.[library.field]; - setShared(isTyped(value) ? (value as TypedRoot) : []); - }) - .catch(() => setShared([])); - return () => request.abort(); - }, [config.routes.api, library]); - - const here = taken(root); - const pick = (name: string) => - setPicked((held) => - held.includes(name) ? held.filter((own) => own !== name) : [...held, name] - ); - - const add = () => { - const chosen = (shared ?? []).filter((node) => picked.includes(node.name ?? "")); - onAdd(chosen.map((node) => following(node, node.name ?? ""))); - setPicked([]); - closeModal(slug); - }; - - return ( - -

Shared sections

- - - } - slug={slug} - > -
- {shared === null &&

Reading the library…

} - {shared?.length === 0 && ( -

The library has no sections yet.

- )} - {(shared ?? []).map((node, at) => { - const name = node.name ?? `#${at + 1}`; - const used = here.has(node.name); - return ( - - ); - })} -
-
- ); -}; diff --git a/packages/payload-plugin-json-form-builder/src/components/jsonForm.scss b/packages/payload-plugin-json-form-builder/src/components/jsonForm.scss index 5f01ca1f..d18a171d 100644 --- a/packages/payload-plugin-json-form-builder/src/components/jsonForm.scss +++ b/packages/payload-plugin-json-form-builder/src/components/jsonForm.scss @@ -408,9 +408,7 @@ body.json-builder-open { white-space: nowrap; } -.json-builder__section-count { - opacity: 0.6; -} + .json-builder__cards { display: grid; @@ -730,60 +728,75 @@ body.json-builder-open { border-color: var(--theme-warning-250); color: var(--theme-warning-650); font-family: var(--font-body); - margin-inline-start: var(--base-025); - text-transform: uppercase; + margin-inline-start: 10px; } -.json-form__adders { - display: flex; - gap: var(--base-050); -} -.json-library__bar { - align-items: center; - display: flex; - gap: var(--base-050); - .drawer__header__title { - flex: 1; - } -} -.json-library__list { - display: grid; - gap: var(--base-050); - grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); - padding: var(--base); + + + + + +.json-form__said { + margin-block-end: calc(var(--base) * 0.6); } -.json-library__card { +.json-builder__row { + align-items: center; background: var(--theme-elevation-50); - border: 1px solid var(--theme-elevation-100); border-radius: var(--style-radius-m); - cursor: pointer; display: flex; - flex-direction: column; - gap: var(--base-025); - padding: var(--base-050) var(--base-075); + font-size: 1.05rem; + gap: calc(var(--base) * 0.3); + justify-content: space-between; + padding: calc(var(--base) * 0.4) calc(var(--base) * 0.6); +} + +.json-builder__row--act { + border: 0; + cursor: pointer; + font-family: inherit; + inline-size: 100%; text-align: start; - &--picked { - border-color: var(--theme-success-400); - box-shadow: inset 0 0 0 1px var(--theme-success-400); + &:hover { + background: var(--theme-elevation-100); } +} + +.json-builder__row--held { + background: var(--theme-elevation-1000); + color: var(--theme-elevation-0); + + .json-builder__mark { + color: var(--theme-elevation-200); - &--used { - cursor: not-allowed; - opacity: 0.5; + &:hover { + color: var(--theme-elevation-0); + } } } -.json-library__name { - color: var(--theme-elevation-1000); +.json-builder__mark { + background: none; + border: 0; + color: var(--theme-elevation-500); + cursor: pointer; + font-size: 20px; + line-height: 1; + min-inline-size: 20px; + padding: 0; + text-align: end; + + &:hover { + color: var(--theme-elevation-800); + } } -.json-library__key { - color: var(--theme-elevation-500); - font-family: var(--font-mono); - font-size: 12px; +.json-builder__group--shared { + border-top: 1px solid var(--theme-elevation-100); + margin-block-start: 32px; + padding-block-start: 32px; } diff --git a/packages/payload-plugin-json-form-builder/src/field/index.ts b/packages/payload-plugin-json-form-builder/src/field/index.ts index dc196d2e..624af623 100644 --- a/packages/payload-plugin-json-form-builder/src/field/index.ts +++ b/packages/payload-plugin-json-form-builder/src/field/index.ts @@ -9,12 +9,14 @@ export const MARKER = "jsonFormBuilder"; export type JsonFieldOptions = { build?: BuildGate; + shares?: boolean; }; -export type Mark = { build?: BuildGate; library?: boolean }; +export type Mark = { build?: BuildGate; library?: boolean; shares?: boolean }; export const jsonField = ({ build, + shares, ...overrides }: JsonFieldOptions & Partial = {}): JSONField => deepMerge( @@ -25,7 +27,7 @@ export const jsonField = ({ // Replaces Payload's own, so `jsonError` — its report for json that does not parse — is kept. validate: (value, { jsonError, required }) => jsonError || jsonErrors(value, required), admin: { editorOptions: { insertSpaces: true, tabSize: 3 }, maxHeight: 600 }, - custom: { [MARKER]: { build } }, + custom: { [MARKER]: { build, shares } }, }, overrides ); diff --git a/packages/payload-plugin-json-form-builder/src/index.ts b/packages/payload-plugin-json-form-builder/src/index.ts index d2fb1e7f..f3f9f1d7 100644 --- a/packages/payload-plugin-json-form-builder/src/index.ts +++ b/packages/payload-plugin-json-form-builder/src/index.ts @@ -2,5 +2,6 @@ export { jsonFormPlugin } from "./plugin.js"; export type { JsonFormGlobalConfig, JsonFormPluginConfig } from "./plugin.js"; export { jsonField } from "./field/index.js"; export type { BuildGate, JsonFieldOptions } from "./field/index.js"; +export { jsonLibraryField } from "./library/jsonLibraryField.js"; export type { JsonLibraryOptions } from "./library/jsonLibraryField.js"; export type { JsonFormClientConfig } from "./config.js"; diff --git a/packages/payload-plugin-json-form-builder/src/library/jsonLibraryField.ts b/packages/payload-plugin-json-form-builder/src/library/jsonLibraryField.ts index ae7cd768..bf126bc9 100644 --- a/packages/payload-plugin-json-form-builder/src/library/jsonLibraryField.ts +++ b/packages/payload-plugin-json-form-builder/src/library/jsonLibraryField.ts @@ -11,7 +11,7 @@ export type JsonLibraryOptions = { export const jsonLibraryField = ({ name = "shared", label = "Shared sections", - description = "Sections built once and followed by many. A document gets its own copy of the values; the shape stays here.", + description = "Reusable sections. Build one here and any document can attach it, fill in its own values and follow this shape — the form is written once, the content stays theirs. Removing one here leaves it in the documents that had it, no longer following.", }: JsonLibraryOptions = {}): JSONField => deepMerge(jsonField({ name, label }), { admin: { description }, diff --git a/packages/payload-plugin-json-form-builder/src/library/reconcileSection.ts b/packages/payload-plugin-json-form-builder/src/library/reconcileSection.ts index 1f648a48..cdbcb482 100644 --- a/packages/payload-plugin-json-form-builder/src/library/reconcileSection.ts +++ b/packages/payload-plugin-json-form-builder/src/library/reconcileSection.ts @@ -33,7 +33,9 @@ export const reconcile = (value: unknown, library: unknown): unknown => { const key = sourceOf(node); if (!key) return node; const source = shared.find((entry) => entry.name === key); - return source ? reshape(node, source) : node; + if (source) return reshape(node, source); + const { source: _gone, ...own } = node; + return own as TypedNode; }); }; diff --git a/packages/payload-plugin-json-form-builder/src/plugin.ts b/packages/payload-plugin-json-form-builder/src/plugin.ts index 412a1cde..736020b1 100644 --- a/packages/payload-plugin-json-form-builder/src/plugin.ts +++ b/packages/payload-plugin-json-form-builder/src/plugin.ts @@ -94,7 +94,11 @@ export const jsonFormPlugin = ...json.admin?.components, Field: { path: FIELD, - serverProps: { build: own.build ?? build, library: Boolean(own.library) }, + serverProps: { + build: own.build ?? build, + library: Boolean(own.library), + shares: Boolean(own.shares), + }, }, }, };