@guard-rail/core 0.7.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Fragment, jsx } from "react/jsx-runtime";
|
|
2
2
|
import React from "react";
|
|
3
3
|
import { listRepeaterItems } from "../../server/repeaterData";
|
|
4
|
-
import {
|
|
4
|
+
import { canEdit } from "../../server/access";
|
|
5
5
|
import { RepeaterClient } from "./client";
|
|
6
6
|
function itemHref(routed, slug) {
|
|
7
7
|
if (typeof routed !== "string" || !routed.startsWith("/") || !slug) return void 0;
|
|
@@ -14,8 +14,7 @@ async function GRepeater({ id, as: Item, routed, children }) {
|
|
|
14
14
|
);
|
|
15
15
|
}
|
|
16
16
|
const items = await listRepeaterItems(id);
|
|
17
|
-
|
|
18
|
-
if (viewer?.role) {
|
|
17
|
+
if (await canEdit()) {
|
|
19
18
|
const rendered = items.map((item) => ({
|
|
20
19
|
id: item.id,
|
|
21
20
|
href: itemHref(routed, item.slug),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/react/Repeater/index.tsx"],"sourcesContent":["import React from \"react\";\nimport { listRepeaterItems } from \"../../server/repeaterData\";\nimport {
|
|
1
|
+
{"version":3,"sources":["../../../src/react/Repeater/index.tsx"],"sourcesContent":["import React from \"react\";\nimport { listRepeaterItems } from \"../../server/repeaterData\";\nimport { canEdit } from \"../../server/access\";\nimport { RepeaterClient } from \"./client\";\nimport type { GRRepeaterItem } from \"../../core/types\";\n\nexport interface RepeaterItemProps {\n item: GRRepeaterItem;\n}\n\nexport interface GRepeaterProps {\n /** The repeater's own key. Item fields are namespaced under it: `${id}.${item.id}.*`. */\n id: string;\n /** The developer's own component — the one place the item's shape is defined. */\n as: React.ComponentType<RepeaterItemProps>;\n /**\n * Marks this repeater as routed: each item gets its own URL (a product, a\n * post), so adding one prompts for a slug. Omit for a plain row of items on\n * one page (service cards) — those need no slug at all.\n *\n * Pass the base path your dynamic route lives at (`routed=\"/products\"` for\n * `app/products/[slug]/page.tsx`) and each item's editing toolbar gets an\n * Open button to its page — without it, an editor could only reach an item's\n * page by typing the URL, since clicking its fields edits them. Guardrail\n * never owns routing, so it can't work that path out for itself; `true`\n * works as before, just without the button.\n */\n routed?: boolean | string;\n /** Shown in place of the list when it has no items yet. */\n children?: React.ReactNode;\n}\n\n/** `/products` + `wool-throw` -> `/products/wool-throw`; undefined when the path can't be built. */\nfunction itemHref(routed: GRepeaterProps[\"routed\"], slug?: string): string | undefined {\n if (typeof routed !== \"string\" || !routed.startsWith(\"/\") || !slug) return undefined;\n return `${routed.replace(/\\/+$/, \"\")}/${slug}`;\n}\n\n/**\n * Renders N instances of one developer-authored template component, with the\n * marketer controlling how many and in what order — not what shape they take.\n * That's the whole primitive: no schema, no field-type registry, no relations.\n * An item's own fields are just <GRail> fields living under the item's id,\n * identical in kind to every other field on the page.\n *\n * Server Component by default, same split as <GRail>: anonymous visitors get\n * the template rendered N times with no admin JS; the add/remove/reorder\n * chrome only loads for someone who can actually write.\n */\nexport async function GRepeater({ id, as: Item, routed, children }: GRepeaterProps) {\n if (typeof routed === \"string\" && !routed.startsWith(\"/\")) {\n console.warn(\n `Guardrail: <GRepeater id=\"${id}\" routed=\"${routed}\"> — the base path must start with \"/\" (e.g. \"/products\"); no Open button will be shown.`,\n );\n }\n const items = await listRepeaterItems(id);\n\n // canEdit(), not just \"has a role\": an editor who has exited edit mode is\n // still signed in, and must get the plain list a visitor sees — no\n // add/remove/reorder chrome, no admin JS — until they re-enter edit mode.\n if (await canEdit()) {\n // Rendered here, on the server, and handed down as already-resolved\n // elements — never as the Item component itself. Item almost certainly\n // renders <GRail>, an async Server Component; passing the function across\n // the client boundary would let RepeaterClient try to invoke it in the\n // browser, which cannot execute a Server Component at all. An element\n // (the *result* of calling it) is plain serializable RSC content and\n // composes into a Client Component exactly like `children` does.\n const rendered = items.map((item) => ({\n id: item.id,\n href: itemHref(routed, item.slug),\n node: <Item key={item.id} item={item} />,\n }));\n\n return (\n <RepeaterClient\n repeaterId={id}\n routed={routed !== undefined && routed !== false}\n items={rendered}\n >\n {children}\n </RepeaterClient>\n );\n }\n\n if (items.length === 0) return <>{children ?? null}</>;\n return (\n <>\n {items.map((item) => (\n <Item key={item.id} item={item} />\n ))}\n </>\n );\n}\n"],"mappings":"AAuEY,SAcqB,UAdrB;AAvEZ,OAAO,WAAW;AAClB,SAAS,yBAAyB;AAClC,SAAS,eAAe;AACxB,SAAS,sBAAsB;AA8B/B,SAAS,SAAS,QAAkC,MAAmC;AACrF,MAAI,OAAO,WAAW,YAAY,CAAC,OAAO,WAAW,GAAG,KAAK,CAAC,KAAM,QAAO;AAC3E,SAAO,GAAG,OAAO,QAAQ,QAAQ,EAAE,CAAC,IAAI,IAAI;AAC9C;AAaA,eAAsB,UAAU,EAAE,IAAI,IAAI,MAAM,QAAQ,SAAS,GAAmB;AAClF,MAAI,OAAO,WAAW,YAAY,CAAC,OAAO,WAAW,GAAG,GAAG;AACzD,YAAQ;AAAA,MACN,6BAA6B,EAAE,aAAa,MAAM;AAAA,IACpD;AAAA,EACF;AACA,QAAM,QAAQ,MAAM,kBAAkB,EAAE;AAKxC,MAAI,MAAM,QAAQ,GAAG;AAQnB,UAAM,WAAW,MAAM,IAAI,CAAC,UAAU;AAAA,MACpC,IAAI,KAAK;AAAA,MACT,MAAM,SAAS,QAAQ,KAAK,IAAI;AAAA,MAChC,MAAM,oBAAC,QAAmB,QAAT,KAAK,EAAgB;AAAA,IACxC,EAAE;AAEF,WACE;AAAA,MAAC;AAAA;AAAA,QACC,YAAY;AAAA,QACZ,QAAQ,WAAW,UAAa,WAAW;AAAA,QAC3C,OAAO;AAAA,QAEN;AAAA;AAAA,IACH;AAAA,EAEJ;AAEA,MAAI,MAAM,WAAW,EAAG,QAAO,gCAAG,sBAAY,MAAK;AACnD,SACE,gCACG,gBAAM,IAAI,CAAC,SACV,oBAAC,QAAmB,QAAT,KAAK,EAAgB,CACjC,GACH;AAEJ;","names":[]}
|