@bettercms-ai/kit 2.0.0 → 2.0.2
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.
- package/dist/adapters/lossy.d.ts +14 -6
- package/dist/adapters/lossy.js +23 -11
- package/dist/adapters/lossy.js.map +1 -1
- package/dist/adapters/prod-types.d.ts +46 -14
- package/dist/adapters/prod-types.js +37 -35
- package/dist/adapters/prod-types.js.map +1 -1
- package/dist/adapters/schema.d.ts +39 -0
- package/dist/adapters/schema.js +281 -82
- package/dist/adapters/schema.js.map +1 -1
- package/dist/context.d.ts +39 -0
- package/dist/context.js +40 -1
- package/dist/context.js.map +1 -1
- package/dist/controls/form-picker-register.d.ts +7 -1
- package/dist/controls/form-picker-register.js +1 -5
- package/dist/controls/form-picker-register.js.map +1 -1
- package/dist/controls/icon.d.ts +1 -1
- package/dist/controls/icon.js +1 -1
- package/dist/controls/icon.js.map +1 -1
- package/dist/controls/link.d.ts +11 -4
- package/dist/controls/link.js +120 -202
- package/dist/controls/link.js.map +1 -1
- package/dist/controls/location.js +3 -8
- package/dist/controls/location.js.map +1 -1
- package/dist/controls/media.d.ts +0 -14
- package/dist/controls/media.js +52 -95
- package/dist/controls/media.js.map +1 -1
- package/dist/controls/reference-services.d.ts +23 -2
- package/dist/controls/reference.d.ts +2 -2
- package/dist/controls/reference.js +79 -28
- package/dist/controls/reference.js.map +1 -1
- package/dist/controls/rich-text/article-shell.d.ts +145 -0
- package/dist/controls/rich-text/article-shell.js +514 -0
- package/dist/controls/rich-text/article-shell.js.map +1 -0
- package/dist/controls/rich-text/author.d.ts +25 -0
- package/dist/controls/rich-text/author.js +107 -0
- package/dist/controls/rich-text/author.js.map +1 -0
- package/dist/controls/rich-text/blocks.d.ts +19 -2
- package/dist/controls/rich-text/blocks.js +275 -638
- package/dist/controls/rich-text/blocks.js.map +1 -1
- package/dist/controls/rich-text/button.d.ts +23 -0
- package/dist/controls/rich-text/button.js +81 -0
- package/dist/controls/rich-text/button.js.map +1 -0
- package/dist/controls/rich-text/catalog.d.ts +2 -5
- package/dist/controls/rich-text/catalog.js +121 -7
- package/dist/controls/rich-text/catalog.js.map +1 -1
- package/dist/controls/rich-text/checklist.d.ts +24 -0
- package/dist/controls/rich-text/checklist.js +115 -0
- package/dist/controls/rich-text/checklist.js.map +1 -0
- package/dist/controls/rich-text/codec.d.ts +15 -1
- package/dist/controls/rich-text/codec.js +11 -1
- package/dist/controls/rich-text/codec.js.map +1 -1
- package/dist/controls/rich-text/columns.d.ts +41 -0
- package/dist/controls/rich-text/columns.js +181 -0
- package/dist/controls/rich-text/columns.js.map +1 -0
- package/dist/controls/rich-text/control.js +1 -0
- package/dist/controls/rich-text/control.js.map +1 -1
- package/dist/controls/rich-text/dom.d.ts +84 -4
- package/dist/controls/rich-text/dom.js +171 -10
- package/dist/controls/rich-text/dom.js.map +1 -1
- package/dist/controls/rich-text/editor.d.ts +4 -2
- package/dist/controls/rich-text/editor.js +199 -26
- package/dist/controls/rich-text/editor.js.map +1 -1
- package/dist/controls/rich-text/embed.d.ts +28 -0
- package/dist/controls/rich-text/embed.js +210 -0
- package/dist/controls/rich-text/embed.js.map +1 -0
- package/dist/controls/rich-text/footnote.d.ts +41 -0
- package/dist/controls/rich-text/footnote.js +185 -0
- package/dist/controls/rich-text/footnote.js.map +1 -0
- package/dist/controls/rich-text/gallery.d.ts +28 -0
- package/dist/controls/rich-text/gallery.js +167 -0
- package/dist/controls/rich-text/gallery.js.map +1 -0
- package/dist/controls/rich-text/link-form.d.ts +13 -1
- package/dist/controls/rich-text/link-form.js +41 -7
- package/dist/controls/rich-text/link-form.js.map +1 -1
- package/dist/controls/rich-text/ops.js +6 -0
- package/dist/controls/rich-text/ops.js.map +1 -1
- package/dist/controls/rich-text/related.d.ts +49 -0
- package/dist/controls/rich-text/related.js +233 -0
- package/dist/controls/rich-text/related.js.map +1 -0
- package/dist/controls/rich-text/toc.d.ts +47 -0
- package/dist/controls/rich-text/toc.js +160 -0
- package/dist/controls/rich-text/toc.js.map +1 -0
- package/dist/controls/slot-register.d.ts +6 -1
- package/dist/controls/slot-register.js +1 -4
- package/dist/controls/slot-register.js.map +1 -1
- package/dist/core/core.d.ts +11 -3
- package/dist/core/core.js +24 -6
- package/dist/core/core.js.map +1 -1
- package/dist/core/facets.d.ts +16 -0
- package/dist/core/facets.js +17 -1
- package/dist/core/facets.js.map +1 -1
- package/dist/core/index.js +2 -2
- package/dist/index.js +16 -5
- package/dist/index.js.map +1 -1
- package/dist/ledgers/exclusions.v1.js +141 -0
- package/dist/ledgers/exclusions.v1.js.map +1 -0
- package/dist/library/coverage.d.ts +13 -1
- package/dist/library/coverage.js +12 -1
- package/dist/library/coverage.js.map +1 -1
- package/dist/library/kit-catalog.json +158 -154
- package/dist/schema/types.d.ts +17 -0
- package/dist/schema/types.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/testing/coverage-extras.d.ts +8 -0
- package/dist/testing/coverage-extras.js +44 -0
- package/dist/testing/coverage-extras.js.map +1 -0
- package/dist/testing/exclusions.d.ts +30 -0
- package/dist/testing/exclusions.js +13 -0
- package/dist/testing/exclusions.js.map +1 -0
- package/dist/ui/autocomplete.d.ts +29 -1
- package/dist/ui/autocomplete.js +102 -47
- package/dist/ui/autocomplete.js.map +1 -1
- package/dist/ui/button.js +3 -5
- package/dist/ui/button.js.map +1 -1
- package/dist/ui/index.js +2 -2
- package/dist/ui/overlay.d.ts +4 -1
- package/dist/ui/overlay.js +2 -1
- package/dist/ui/overlay.js.map +1 -1
- package/dist/ui/toast.js +1 -1
- package/package.json +8 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"link.js","names":[],"sources":["../../src/controls/link.tsx"],"sourcesContent":["/**\n * LINK — the ONE link element of the product (V2 HEAD kit/controls/link.tsx, DESIGN §8.7, CPO D43 · D48 · D57).\n *\n * - <LinkForm> Type (Page · CMS item · URL · Email · Phone · Section · File) → the target for that type (search over\n * the host's pages and CMS items, paste detection, URL / email / phone / #anchor validation; File =\n * the host's media library) → Section on the picked page → options (new tab · rel · email subject).\n * - <LinkPopoverBody> the link panel: what it links to now + LinkForm + Remove link · Cancel · Apply.\n * - <LinkField> a link at rest (one bordered row): click edits in place (quick edit), the sliders icon opens the panel.\n * - <LinkPicker> the target input inside all of the above.\n * - <LinkLine> how a link READS at rest: page icon + title + path · external icon + domain · mail / phone.\n *\n * Where the targets come from. The kit never fetches: the host hands its own link-target query in as a React hook,\n * `KitServices.useLinkTargets` (KitLinkServices below), and the media library as `KitServices.MediaPicker`. With no hook a\n * page link is typed as a path and the list says so in words; with no media picker the File type is not offered.\n * Section anchors come from the host that knows the page (SectionAnchorsProvider); without one a section is typed (#faq).\n *\n * Stored shape (prod `link`, adapters/values.ts linkToProd / linkFromProd). The kit value is what the field already\n * stores: a STRING href stays a string (legacy fields and the sites that read `href={link}`), an object stays an object.\n * An empty field takes `field.valueShape` and, with none, the string (prod LinkField's default). In the string shape the\n * options (new tab, rel) need the object, so they hide — V2's `valueShape=\"string\"`. An object never carries `target`\n * (derived from newTab), `rel: \"follow\"` (follow = no rel) or `newTab: false`, so it reads back exactly as written.\n *\n * Deviations from V2, each because prod has no source for it: no \"From this entry\" {{token}} targets (no binding store\n * behind them), no seeded page anchors, no fabricated CDN address for a file (a file without a public url is refused).\n */\nimport * as React from \"react\";\nimport {\n IconAdjustmentsHorizontal, IconArrowUpRight, IconAt, IconCheck, IconChevronDown, IconExternalLink, IconFile, IconFileDatabase,\n IconFolderOpen, IconHash, IconLink, IconMail, IconPaperclip, IconPhone, IconWorld, IconX,\n} from \"@tabler/icons-react\";\nimport { cn } from \"../lib/cn\";\nimport { isObj } from \"../lib/path\";\nimport { useKitEnv, useKitServices, type KitMediaAsset, type KitServices } from \"../context\";\nimport { Button, IconButton } from \"../ui/button\";\nimport { Input } from \"../ui/input\";\nimport { Switch } from \"../ui/controls\";\nimport { Select } from \"../ui/select\";\nimport { Menu, MenuContent, MenuItem, MenuTrigger, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, Tooltip } from \"../ui/overlay\";\nimport type { ControlProps } from \"../types\";\n\nexport type LinkType = \"page\" | \"url\" | \"section\" | \"email\" | \"phone\" | \"file\" | \"entry\";\nexport type LinkRel = \"follow\" | \"nofollow\" | \"ugc\" | \"sponsored\";\nexport type ButtonStyle = \"primary\" | \"secondary\" | \"outline\" | \"link\";\nexport interface LinkValue {\n type?: LinkType;\n href: string;\n /** button label / link text */\n label?: string;\n /** resolved title of the target (page or entry name) */\n title?: string;\n pageId?: string;\n entryId?: string;\n anchor?: string;\n assetId?: string;\n email?: string;\n subject?: string;\n phone?: string;\n /** legacy target: read as newTab, never written */\n target?: \"_self\" | \"_blank\";\n newTab?: boolean;\n rel?: LinkRel;\n /* button-only (controls/button.tsx) */\n style?: ButtonStyle;\n variant?: string;\n size?: \"sm\" | \"md\" | \"lg\";\n arrow?: boolean;\n icon?: string;\n fullWidth?: boolean;\n}\n\n/* ── Host services ───────────────────────────────────────────────────── */\n/** A place a link can point at, as the host lists it. */\nexport interface LinkTarget {\n id: string;\n title: string;\n /** the public path (\"/about\", \"/blog/hello\") or a file's url */\n href: string;\n kind: \"page\" | \"entry\" | \"file\" | \"section\";\n /** collection name for an entry, file kind for a file */\n meta?: string;\n /** list group: \"Pages\", the collection's name, \"Files\", \"On this page\" */\n group: string;\n /** draft · published · scheduled · archived … (shown when it is not live) */\n status?: string;\n /** \"#faq\" anchors on that page, when the host knows them */\n anchors?: string[];\n /** a section without an anchor yet gets it when picked */\n onChoose?: () => void;\n}\n/**\n * The link services a host provides next to KitServices (registerKitServices / KitServicesProvider take them as is).\n * `useLinkTargets` is a React HOOK (called on every render of a link control), so a host passes the same function for\n * the life of the form.\n */\nexport type KitLinkServices = Pick<KitServices, \"useLinkTargets\">;\ntype LinkHost = { targets: LinkTarget[]; loading: boolean; error?: string; unavailable: boolean };\nconst NO_TARGETS: LinkTarget[] = [];\nconst useNoTargets = () => ({ targets: NO_TARGETS, loading: false, error: undefined as string | undefined });\n\n/** The host's pages, CMS items (and files, when it lists them). */\nexport function useLinkTargets(): LinkHost {\n const svc = useKitServices();\n const useHost = svc.useLinkTargets ?? useNoTargets;\n const r = useHost();\n return { targets: r.targets ?? NO_TARGETS, loading: !!r.loading, error: r.error, unavailable: !svc.useLinkTargets };\n}\n\n/** A section on the page being edited that a link can jump to (`id` on the block). */\nexport interface SectionAnchor {\n /** \"#faq\" */\n anchor: string;\n /** what a person reads: the block's name */\n label: string;\n /** the block has no anchor yet: give it this one when a link picks it */\n ensure?: () => void;\n}\nconst SectionAnchorsCtx = React.createContext<SectionAnchor[] | null>(null);\n/** The host that knows the page being edited lists its section anchors, so the Section type is a pick. */\nexport function SectionAnchorsProvider({ anchors, children }: { anchors: SectionAnchor[]; children: React.ReactNode }) {\n return <SectionAnchorsCtx.Provider value={anchors}>{children}</SectionAnchorsCtx.Provider>;\n}\nfunction useSectionTargets(): LinkTarget[] {\n const anchors = React.useContext(SectionAnchorsCtx);\n return React.useMemo(\n () =>\n (anchors ?? []).map((a) => ({\n id: `section:${a.anchor}`,\n title: a.label,\n href: a.anchor.startsWith(\"#\") ? a.anchor : `#${a.anchor}`,\n kind: \"section\" as const,\n group: \"On this page\",\n onChoose: a.ensure,\n })),\n [anchors],\n );\n}\n\n/** A file from the media library as a link target, or the reason it can't be one. */\nexport function fileTarget(a: Pick<KitMediaAsset, \"id\" | \"name\" | \"url\">): LinkTarget | { problem: string } {\n if (!a.url || a.url.startsWith(\"blob:\")) return { problem: \"This file has no public address yet. Wait for the upload to finish, then pick it again.\" };\n const name = a.name || fileName(a.url);\n return { id: a.id, title: name, href: a.url, kind: \"file\", meta: name.split(\".\").pop()?.toUpperCase(), group: \"Files\" };\n}\n\n/* ── Value helpers ───────────────────────────────────────────────────── */\nexport const normLink = (v: unknown): LinkValue => {\n const o: LinkValue = typeof v === \"string\" ? { href: v } : isObj(v) ? ({ ...(v as object), href: typeof v.href === \"string\" ? v.href : \"\" } as LinkValue) : { href: \"\" };\n if (o.newTab === undefined && o.target === \"_blank\") o.newTab = true;\n if (!o.type) o.type = detectType(o.href);\n return o;\n};\n\nexport function detectType(raw: string): LinkType {\n const s = raw.trim();\n if (!s) return \"page\";\n if (s.startsWith(\"#\")) return \"section\";\n if (/^mailto:/i.test(s) || /^[^\\s@/]+@[^\\s@]+\\.[^\\s@]+$/.test(s)) return \"email\";\n if (/^tel:/i.test(s) || /^\\+?[\\d\\s().-]{7,}$/.test(s)) return \"phone\";\n if (/\\.(pdf|zip|docx?|csv)$/i.test(s)) return \"file\";\n if (/^https?:\\/\\//i.test(s) || /^www\\./i.test(s) || /^[a-z0-9-]+(\\.[a-z0-9-]+)+(\\/|$)/i.test(s)) return \"url\";\n return \"page\";\n}\n\nexport const LINK_TYPES: { type: LinkType; label: string; icon: React.ReactNode; placeholder: string; target: string }[] = [\n { type: \"page\", label: \"Page\", icon: <IconFile stroke={1.5} />, placeholder: \"Search pages and CMS items, or paste a link\", target: \"Page\" },\n { type: \"entry\", label: \"CMS item\", icon: <IconFileDatabase stroke={1.5} />, placeholder: \"Search CMS items\", target: \"CMS item\" },\n { type: \"url\", label: \"URL\", icon: <IconWorld stroke={1.5} />, placeholder: \"https://example.com\", target: \"Address\" },\n { type: \"email\", label: \"Email\", icon: <IconAt stroke={1.5} />, placeholder: \"name@company.com\", target: \"Email address\" },\n { type: \"phone\", label: \"Phone\", icon: <IconPhone stroke={1.5} />, placeholder: \"+1 541 555 0182\", target: \"Phone number\" },\n { type: \"section\", label: \"Section\", icon: <IconHash stroke={1.5} />, placeholder: \"#section-on-this-page\", target: \"Section on this page\" },\n { type: \"file\", label: \"File\", icon: <IconPaperclip stroke={1.5} />, placeholder: \"Search files\", target: \"File\" },\n];\nconst typeMeta = (t?: string) => LINK_TYPES.find((x) => x.type === t) ?? LINK_TYPES[0];\n\n/** The target a value points at: by id first, then by an exact path (\"/about\" typed by hand still reads as About). */\nexport function findTarget(targets: LinkTarget[], v: LinkValue): LinkTarget | undefined {\n const byId = targets.find((t) => (v.pageId && t.id === v.pageId) || (v.entryId && t.id === v.entryId) || (v.assetId && t.id === v.assetId));\n if (byId) return byId;\n if (v.type === \"section\") return v.href ? targets.find((t) => t.kind === \"section\" && t.href === (v.href.startsWith(\"#\") ? v.href : `#${v.href}`)) : undefined;\n if (!v.href || (v.type !== \"page\" && v.type !== \"entry\" && v.type !== \"file\")) return undefined;\n if (v.type === \"file\") {\n const f = targets.find((t) => t.kind === \"file\" && t.href === v.href);\n // a file picked from the library reads as the file by the name stored with it\n return f ?? (v.title ? { id: v.assetId ?? v.href, title: v.title, href: v.href, kind: \"file\", group: \"Files\" } : undefined);\n }\n const path = v.href.replace(/[?#].*$/, \"\").replace(/\\/$/, \"\") || \"/\";\n return targets.find((t) => t.kind !== \"file\" && t.kind !== \"section\" && t.href === path);\n}\n\n/** The final href for a link value. */\nexport function hrefOf(v: LinkValue): string {\n switch (v.type) {\n case \"email\": {\n const addr = (v.email ?? v.href).replace(/^mailto:/i, \"\").replace(/\\?.*$/, \"\");\n return addr ? `mailto:${addr}${v.subject ? `?subject=${encodeURIComponent(v.subject)}` : \"\"}` : \"\";\n }\n case \"phone\": {\n const n = (v.phone ?? v.href).replace(/^tel:/i, \"\").replace(/[^\\d+]/g, \"\");\n return n ? `tel:${n}` : \"\";\n }\n case \"section\":\n return v.href.startsWith(\"#\") ? v.href : v.href ? `#${v.href}` : \"\";\n default: {\n // idempotent: a stored href already carries its anchor, so a changed or removed anchor replaces it\n const base = v.anchor !== undefined ? v.href.replace(/#.*$/, \"\") : v.href;\n return `${base}${v.anchor && v.anchor !== \"none\" ? v.anchor : \"\"}`;\n }\n }\n}\n\nconst BAD_URL = \"Enter a full address, like https://example.com.\";\n/** Why a link can't be saved yet, in plain words. Undefined when it is fine (or empty). */\nexport function linkProblem(v: LinkValue): string | undefined {\n if (!hasLink(v)) return undefined;\n const raw = (v.type === \"email\" ? v.email ?? v.href : v.type === \"phone\" ? v.phone ?? v.href : v.href).trim();\n if (/^(javascript|data|vbscript):/i.test(raw.replace(/[\\u0000- ]/g, \"\"))) return \"Script links aren't allowed.\";\n switch (v.type) {\n case \"url\": {\n if (raw.startsWith(\"/\") || raw.startsWith(\"#\")) return undefined;\n try {\n const u = new URL(/^[a-z][a-z0-9+.-]*:/i.test(raw) ? raw : `https://${raw}`);\n if (!/^https?:$/.test(u.protocol)) return BAD_URL;\n return /\\.[a-z]{2,}$/i.test(u.hostname) || u.hostname === \"localhost\" ? undefined : BAD_URL;\n } catch {\n return BAD_URL;\n }\n }\n case \"email\":\n return /^[^\\s@]+@[^\\s@]+\\.[^\\s@]{2,}$/.test(raw.replace(/^mailto:/i, \"\").replace(/\\?.*$/, \"\")) ? undefined : \"Enter an email address, like name@company.com.\";\n case \"phone\":\n return raw.replace(/^tel:/i, \"\").replace(/\\D/g, \"\").length >= 7 ? undefined : \"Enter a phone number with at least 7 digits.\";\n case \"section\":\n return /^#?[A-Za-z][\\w-]*$/.test(raw) ? undefined : \"Use letters, numbers and dashes, like #faq.\";\n case \"page\":\n case \"entry\":\n return raw.startsWith(\"/\") || raw.startsWith(\"#\") || v.pageId || v.entryId ? undefined : \"Pick a page, or start a path with /, like /about.\";\n default:\n return undefined;\n }\n}\n\n/** One list for every link editor (dropdown, never a segmented row — D40). */\nexport const REL_ITEMS: { value: LinkRel; label: string; description: string }[] = [\n { value: \"follow\", label: \"Do-follow\", description: \"Normal link. Search engines follow it.\" },\n { value: \"nofollow\", label: \"No-follow\", description: \"Search engines don't follow or credit it.\" },\n { value: \"sponsored\", label: \"Sponsored\", description: \"Paid, affiliate or ad link.\" },\n { value: \"ugc\", label: \"UGC\", description: \"Link added by users, e.g. comments.\" },\n];\n\nexport const hasLink = (v: LinkValue) => !!(v.href || v.email || v.phone);\nexport const TARGET_KEYS = { title: undefined, pageId: undefined, entryId: undefined, assetId: undefined, anchor: undefined } as const;\n\n/** The value a picked target produces (keeps label, style and options). */\nexport function withTarget(v: LinkValue, t: LinkTarget): LinkValue {\n return {\n ...v,\n ...TARGET_KEYS,\n email: undefined,\n phone: undefined,\n subject: undefined,\n type: t.kind === \"file\" ? \"file\" : t.kind === \"entry\" ? \"entry\" : t.kind === \"section\" ? \"section\" : \"page\",\n href: t.href,\n title: t.title,\n pageId: t.kind === \"page\" ? t.id : undefined,\n entryId: t.kind === \"entry\" ? t.id : undefined,\n assetId: t.kind === \"file\" ? t.id : undefined,\n };\n}\n/** The value typed text produces (auto-detects URL / email / phone / #section unless the type was chosen). */\nexport function withText(v: LinkValue, s: string, type?: LinkType): LinkValue {\n const t = type ?? detectType(s);\n const n: LinkValue = { ...v, ...TARGET_KEYS, type: t, email: undefined, phone: undefined };\n if (t === \"email\") Object.assign(n, { email: s.replace(/^mailto:/i, \"\"), href: s });\n else if (t === \"phone\") Object.assign(n, { phone: s.replace(/^tel:/i, \"\"), href: s });\n else n.href = s;\n return n;\n}\n/** Clean a value before it is stored: add https:// to bare domains, refuse script URLs, store the href the site renders. */\nexport function finalizeLink(v: LinkValue): LinkValue | null {\n const out: LinkValue = { ...v };\n if (/^(javascript|data|vbscript):/i.test(out.href.replace(/[\\u0000- ]/g, \"\"))) return null;\n if (out.type === \"url\" && out.href && !/^(https?:|\\/|#)/.test(out.href)) out.href = `https://${out.href.replace(/^\\/+/, \"\")}`;\n // the stored href is the one the site renders (mailto:, tel:, #anchor); every consumer reads `href` as is\n if (hasLink(out)) out.href = hrefOf(out);\n return out;\n}\n/**\n * The object as prod stores it, keyed like the value it replaces (`original`), so an edit changes only what was edited:\n * no undefined keys; no `rel: \"follow\"` (follow is no rel: the backend normalises it away and a button refuses it); no\n * `newTab: false` and no `target` unless the stored value had that key (a stored `target` is kept in sync with newTab,\n * and is then the one that carries it).\n */\nexport function storedLink<T extends LinkValue>(v: T, original?: unknown): T {\n const had = (k: string) => isObj(original) && k in original;\n const out: Record<string, unknown> = {};\n for (const [k, x] of Object.entries(v)) {\n if (x === undefined || k === \"target\") continue;\n if (k === \"rel\" && x === \"follow\") continue;\n if (k === \"newTab\" && !had(\"newTab\") && (x === false || had(\"target\"))) continue;\n out[k] = x;\n }\n if (had(\"target\")) out.target = v.newTab ? \"_blank\" : \"_self\";\n return out as T;\n}\n/** Text the picker's input shows for a value that has no picked target. */\nconst inputText = (v: LinkValue) =>\n v.type === \"email\" ? (v.email ?? v.href.replace(/^mailto:/i, \"\")) : v.type === \"phone\" ? (v.phone ?? v.href.replace(/^tel:/i, \"\")) : v.href;\n\nconst STATUS_LABEL: Record<string, string> = { draft: \"Draft\", changed: \"Edited\", scheduled: \"Scheduled\", review: \"In review\", archived: \"Archived\" };\n\n/* ── How a link reads ───────────────────────────────────────────────── */\nexport function linkIcon(v: LinkValue, target?: LinkTarget): React.ReactNode {\n if (!hasLink(v)) return <IconLink stroke={1.5} />;\n if (target) return target.kind === \"entry\" ? <IconFileDatabase stroke={1.5} /> : target.kind === \"file\" ? <IconPaperclip stroke={1.5} /> : target.kind === \"section\" ? <IconHash stroke={1.5} /> : <IconFile stroke={1.5} />;\n switch (v.type) {\n case \"url\": return <IconExternalLink stroke={1.5} />;\n case \"email\": return <IconMail stroke={1.5} />;\n case \"phone\": return <IconPhone stroke={1.5} />;\n case \"section\": return <IconHash stroke={1.5} />;\n case \"file\": return <IconPaperclip stroke={1.5} />;\n case \"entry\": return <IconFileDatabase stroke={1.5} />;\n default: return <IconFile stroke={1.5} />;\n }\n}\n/** Primary + secondary text: page title + path · domain + rest of the URL · address · number. */\nexport function describeLink(v: LinkValue, target?: LinkTarget): { primary: string; secondary?: string } {\n if (!hasLink(v)) return { primary: \"\" };\n const anchor = v.anchor && v.anchor !== \"none\" ? v.anchor : \"\";\n if (target) return { primary: target.title, secondary: target.kind === \"section\" ? `${target.href} on this page` : target.kind === \"file\" ? fileKind(target) : `${target.href}${anchor}` };\n switch (v.type) {\n case \"url\": {\n try {\n const u = new URL(/^https?:/i.test(v.href) ? v.href : `https://${v.href}`);\n const rest = `${u.pathname === \"/\" ? \"\" : u.pathname}${u.search}${u.hash}`;\n return { primary: u.hostname.replace(/^www\\./, \"\"), secondary: rest || undefined };\n } catch {\n return { primary: v.href };\n }\n }\n case \"email\": return { primary: v.email ?? v.href.replace(/^mailto:/i, \"\").replace(/\\?.*$/, \"\"), secondary: v.subject ? `Subject: ${v.subject}` : undefined };\n case \"phone\": return { primary: v.phone ?? v.href.replace(/^tel:/i, \"\") };\n case \"section\": return { primary: v.href, secondary: \"On this page\" };\n // a stored href already carries its anchor (finalizeLink)\n default: return { primary: v.title || v.href, secondary: v.title ? v.href : undefined };\n }\n}\n/** \"PDF file\": the name is already the title */\nconst fileKind = (t: LinkTarget) => `${t.meta ?? (fileName(t.href).split(\".\").pop() ?? \"\").toUpperCase()} file`.trim();\nfunction fileName(href: string) {\n const last = href.replace(/[?#].*$/, \"\").split(\"/\").pop() ?? href;\n try {\n return decodeURIComponent(last);\n } catch {\n return last;\n }\n}\n/** What a person reads for a link (one string — menus, aria labels, history). */\nexport const linkText = (v: LinkValue) => (hasLink(v) ? v.title || describeLink(v).primary : \"\");\n\n/** A link at rest: icon · title (fg) · path (fg-3) · new-tab mark. Empty → \"Add link\". */\nexport function LinkLine({ v, target, placeholder = \"Add link\", className }: { v: LinkValue; target?: LinkTarget; placeholder?: string; className?: string }) {\n const d = describeLink(v, target);\n const empty = !hasLink(v);\n return (\n <span className={cn(\"flex min-w-0 flex-1 items-center gap-2\", className)}>\n <span className={cn(\"flex shrink-0 [&_svg]:size-4\", empty ? \"text-kit-fg-3\" : \"text-kit-fg-2\")} aria-hidden>{linkIcon(v, target)}</span>\n <span className={cn(\"min-w-0 shrink truncate text-kit-sm\", empty ? \"text-kit-fg-3\" : \"text-kit-fg\")}>{empty ? placeholder : d.primary}</span>\n {d.secondary ? <span className=\"min-w-0 flex-1 truncate text-kit-xs text-kit-fg-3\">{d.secondary}</span> : <span className=\"flex-1\" />}\n {!empty && v.newTab ? (\n <span className=\"inline-flex shrink-0 items-center gap-0.5 text-kit-2xs text-kit-fg-3\">\n <IconArrowUpRight className=\"size-3\" stroke={1.5} aria-hidden />\n New tab\n </span>\n ) : null}\n </span>\n );\n}\n\n/* ── The picker: ONE link input for the whole product ───────────────── */\ntype Row = { key: string; group: string; target: LinkTarget };\n\n/** While the media library is open from a picker, focus sits in its dialog: a quick edit must not close under it. */\nlet libraryOpen = false;\n/** Marks the floating results list, so a quick edit losing focus to it is not \"focus left the editor\". */\nconst LIST_ATTR = \"data-kit-link-list\";\n\nexport interface LinkPickerProps {\n value: LinkValue;\n /** every change: typed text, a picked target, a cleared value */\n onChange: (v: LinkValue) => void;\n /** a target was chosen from the list (quick editors commit here) */\n onPick?: (v: LinkValue) => void;\n /** Enter with no list row active */\n onEnter?: (v: LinkValue) => void;\n /** Escape with the list closed */\n onEscape?: () => void;\n autoFocus?: boolean;\n /** open straight into search (quick edit), even when a target is picked */\n startSearching?: boolean;\n id?: string;\n \"aria-label\"?: string;\n size?: \"sm\" | \"md\";\n /** no border of its own — it sits inside a unit that already draws one (button field) */\n bare?: boolean;\n /** the host shows its own type control (link panel): hide the inline type menu and search only this type */\n lockedType?: LinkType;\n /** results render in the flow under the input instead of a floating list — panels/popovers, never over them */\n inlineList?: boolean;\n /** the link types this place allows. Default: all of them */\n types?: LinkType[];\n /** the value can't be saved as it is (shown as the field's error state) */\n invalid?: boolean;\n \"aria-describedby\"?: string;\n className?: string;\n}\n\nexport function LinkPicker({\n value, onChange, onPick, onEnter, onEscape, autoFocus, startSearching, id, \"aria-label\": ariaLabel = \"Link to\", size = \"md\", bare, lockedType,\n inlineList, types, invalid, \"aria-describedby\": describedBy, className,\n}: LinkPickerProps) {\n const v = normLink(value);\n const host = useLinkTargets();\n const sections = useSectionTargets();\n const { MediaPicker } = useKitServices();\n const targets = React.useMemo(() => [...host.targets, ...sections], [host.targets, sections]);\n const picked = findTarget(targets, v);\n const [library, setLibraryState] = React.useState(false);\n const setLibrary = (o: boolean) => {\n libraryOpen = o;\n setLibraryState(o);\n };\n React.useEffect(() => () => void (libraryOpen = false), []);\n const [fileProblem, setFileProblem] = React.useState<string>();\n const [searching, setSearching] = React.useState(!!startSearching || !picked);\n const [text, setText] = React.useState(() => (picked ? \"\" : inputText(v)));\n const [manualTypeState, setManualType] = React.useState<LinkType | null>(null);\n const manualType = lockedType ?? manualTypeState;\n const [open, setOpen] = React.useState(false);\n const [active, setActive] = React.useState(-1);\n const inputRef = React.useRef<HTMLInputElement>(null);\n const wrapRef = React.useRef<HTMLDivElement>(null);\n const listId = React.useId();\n const type = manualType ?? v.type ?? \"page\";\n const meta = typeMeta(manualType ?? (searching && !text ? \"page\" : v.type));\n const showChip = !!picked && !searching;\n\n React.useEffect(() => {\n if (autoFocus)\n requestAnimationFrame(() => {\n inputRef.current?.focus({ preventScroll: true });\n inputRef.current?.select(); // quick edit: typing replaces, paste replaces\n });\n }, [autoFocus]);\n\n /* rows: the host's pages + every collection's entries (+ files when it lists them) */\n const q = text.trim().toLowerCase().replace(/^[/#]/, \"\");\n const searchable = type === \"page\" || type === \"entry\" || type === \"file\" || (type === \"section\" && sections.length > 0);\n const urlish = type !== \"section\" && (/^(https?:|www\\.|mailto:|tel:|#)/i.test(text.trim()) || (text.trim() !== \"\" && detectType(text) !== \"page\" && !manualType));\n const rows: Row[] = React.useMemo(() => {\n if (!searchable || urlish) return [];\n const pool = targets.filter((t) => (type === \"file\" ? t.kind === \"file\" : type === \"entry\" ? t.kind === \"entry\" : type === \"section\" ? t.kind === \"section\" : t.kind === \"page\" || t.kind === \"entry\"));\n const hit = (t: LinkTarget) => !q || t.title.toLowerCase().includes(q) || t.href.toLowerCase().includes(q) || (t.meta ?? \"\").toLowerCase().includes(q);\n const out: Row[] = [];\n const perGroup = new Map<string, number>();\n const cap = q ? 6 : type === \"page\" ? 4 : 6;\n for (const t of pool) {\n if (!hit(t)) continue;\n const n = perGroup.get(t.group) ?? 0;\n if (n >= cap) continue;\n perGroup.set(t.group, n + 1);\n out.push({ key: `${t.kind}:${t.id}`, group: t.group, target: t });\n }\n // keep each group together, groups in first-seen order (Pages first)\n const order = [...new Set(out.map((r) => r.group))];\n return out.sort((a, b) => order.indexOf(a.group) - order.indexOf(b.group)).slice(0, 24);\n }, [targets, type, q, searchable, urlish]);\n /* No silent empty list: when nothing can be listed, the list says why in one line. */\n const note: string | undefined = (() => {\n if (!searchable || urlish || rows.length) return undefined;\n if (type === \"section\") return undefined;\n if (type === \"file\") return MediaPicker ? \"Choose a file from the library with the folder button.\" : undefined;\n if (host.unavailable) return \"Page search isn't available here. Type a path, like /about.\";\n if (host.error) return `${host.error.replace(/\\.?$/, \".\")} Type a path, like /about.`;\n if (host.loading) return \"Loading pages…\";\n return q ? `Nothing matches “${text.trim()}”. Type a path, like /about.` : type === \"entry\" ? \"No CMS items yet.\" : \"No pages yet. Type a path, like /about.\";\n })();\n const listOpen = open && searching && (rows.length > 0 || !!note);\n /* Esc closes the results first, the surrounding panel only on the next Esc: the list claims the key on window, in the\n capture phase, while it is open. */\n React.useEffect(() => {\n if (!listOpen) return;\n const onKey = (e: KeyboardEvent) => {\n if (e.key !== \"Escape\" || !wrapRef.current?.contains(document.activeElement)) return;\n e.preventDefault();\n e.stopPropagation();\n setOpen(false);\n };\n window.addEventListener(\"keydown\", onKey, true);\n return () => window.removeEventListener(\"keydown\", onKey, true);\n }, [listOpen]);\n\n const choose = (t: LinkTarget) => {\n t.onChoose?.();\n const n = withTarget(v, t);\n setText(\"\");\n setManualType(null);\n setSearching(false);\n setOpen(false);\n setFileProblem(undefined);\n onChange(n);\n onPick?.(n);\n };\n const type_ = (s: string) => {\n setText(s);\n setOpen(true);\n setActive(s.trim() ? 0 : -1);\n onChange(withText(v, s, manualType ?? undefined));\n };\n const clear = () => {\n setText(\"\");\n setManualType(null);\n setSearching(true);\n onChange({ ...withText(v, \"\"), href: \"\" });\n requestAnimationFrame(() => inputRef.current?.focus());\n };\n const setType = (t: LinkType) => {\n setManualType(t);\n setText(\"\");\n setSearching(true);\n onChange({ ...withText(v, \"\", t), href: \"\" });\n setOpen(true);\n requestAnimationFrame(() => inputRef.current?.focus());\n };\n\n const h = size === \"sm\" ? \"h-8\" : \"h-9\";\n const list = (\n <div id={listId} role=\"listbox\" aria-label={type === \"file\" ? \"Files\" : type === \"section\" ? \"Sections on this page\" : \"Pages and CMS items\"} className=\"max-h-72 overflow-y-auto overscroll-contain\">\n {rows.map((r, i) => (\n <React.Fragment key={r.key}>\n {i === 0 || rows[i - 1].group !== r.group ? (\n <div role=\"presentation\" className=\"px-2 pb-1 pt-2 text-kit-xs font-medium text-kit-fg-3 first:pt-1\">\n {r.group}\n </div>\n ) : null}\n <div\n id={`${listId}-${i}`}\n role=\"option\"\n aria-selected={i === active}\n onMouseEnter={() => setActive(i)}\n onClick={() => choose(r.target)}\n className={cn(\"flex h-8 cursor-pointer items-center gap-2 rounded-kit-md px-2 text-kit-sm\", i === active && \"bg-kit-hover\")}\n >\n <span className=\"flex shrink-0 text-kit-fg-3 [&_svg]:size-4\" aria-hidden>\n {linkIcon({ href: r.target.href, type: r.target.kind }, r.target)}\n </span>\n <span className=\"min-w-0 shrink truncate text-kit-fg\">{r.target.title}</span>\n <span className=\"min-w-0 flex-1 truncate text-kit-xs text-kit-fg-3\">{r.target.kind === \"file\" ? fileName(r.target.href) : r.target.href}</span>\n {r.target.status && STATUS_LABEL[r.target.status] ? <span className=\"shrink-0 text-kit-2xs text-kit-fg-3\">{STATUS_LABEL[r.target.status]}</span> : null}\n </div>\n </React.Fragment>\n ))}\n {!rows.length && note ? (\n <p role=\"status\" className=\"px-2 py-1.5 text-kit-xs text-kit-fg-3\">\n {note}\n </p>\n ) : rows.length && host.error && type !== \"file\" && type !== \"section\" ? (\n /* some targets loaded and some did not (a collection's items): the gap is named under the list */\n <p role=\"status\" className=\"px-2 py-1.5 text-kit-xs text-kit-fg-3\">\n {host.error.replace(/\\.?$/, \".\")}\n </p>\n ) : null}\n </div>\n );\n const allowed = LINK_TYPES.filter((t) => (!types || types.includes(t.type)) && (t.type !== \"file\" || MediaPicker || v.type === \"file\"));\n const typeMenu = (\n <Menu>\n <MenuTrigger\n render={\n <button\n type=\"button\"\n aria-label={`Link type: ${meta.label}`}\n className=\"ml-0.5 flex h-7 shrink-0 items-center gap-0.5 rounded-kit-md px-1.5 text-kit-fg-3 hover:bg-kit-hover hover:text-kit-fg data-[popup-open]:bg-kit-hover [&_svg]:size-3.5\"\n />\n }\n >\n {meta.icon}\n <IconChevronDown className=\"!size-3\" stroke={1.5} />\n </MenuTrigger>\n <MenuContent className=\"min-w-48\">\n {allowed.map((t) => (\n <MenuItem key={t.type} icon={t.icon} onClick={() => setType(t.type)}>\n <span className=\"flex w-full items-center justify-between gap-3\">\n {t.label}\n {t.type === type ? <IconCheck className=\"size-3.5 text-kit-fg-2\" stroke={2} /> : null}\n </span>\n </MenuItem>\n ))}\n </MenuContent>\n </Menu>\n );\n\n return (\n <div className={cn(\"flex min-w-0 flex-col gap-1\", className)}>\n <Popover open={listOpen && !inlineList} onOpenChange={(o) => !o && setOpen(false)}>\n <div ref={wrapRef} className=\"min-w-0\">\n <PopoverAnchor\n className={cn(\n \"flex min-w-0 items-center\",\n bare\n ? \"bg-transparent\"\n : \"rounded-kit-md border border-kit-field-border bg-kit-field-bg shadow-kit-field transition-colors hover:border-kit-field-border-hover focus-within:border-kit-field-border-focus\",\n !bare && invalid && \"border-[var(--kit-field-invalid)] hover:border-[var(--kit-field-invalid)]\",\n h,\n )}\n >\n {showChip ? (\n <>\n <span className=\"ml-2 flex shrink-0 text-kit-fg-2 [&_svg]:size-4\" aria-hidden>\n {linkIcon(v, picked)}\n </span>\n <button\n id={id}\n type=\"button\"\n onClick={() => {\n setSearching(true);\n setOpen(true);\n requestAnimationFrame(() => inputRef.current?.focus());\n }}\n aria-label={`${ariaLabel}: ${picked!.title}, ${picked!.href}. Change`}\n className=\"flex h-full min-w-0 flex-1 items-center gap-2 px-2 text-left outline-none\"\n >\n <span className=\"min-w-0 shrink truncate text-kit-sm text-kit-fg\">{picked!.title}</span>\n <span className=\"min-w-0 flex-1 truncate text-kit-xs text-kit-fg-3\">{describeLink(v, picked).secondary}</span>\n </button>\n <IconButton label=\"Clear link\" size=\"xs\" className=\"mr-1\" onClick={clear}>\n <IconX stroke={1.5} />\n </IconButton>\n </>\n ) : (\n <>\n {lockedType !== undefined || inlineList ? (\n <span className=\"ml-2.5 flex shrink-0 text-kit-fg-3 [&_svg]:size-4\" aria-hidden>\n {meta.icon}\n </span>\n ) : (\n typeMenu\n )}\n <input\n ref={inputRef}\n id={id}\n role=\"combobox\"\n aria-label={ariaLabel}\n aria-invalid={invalid || undefined}\n aria-describedby={describedBy}\n aria-expanded={listOpen}\n aria-controls={listOpen ? listId : undefined}\n aria-autocomplete=\"list\"\n aria-activedescendant={listOpen && active >= 0 && rows[active] ? `${listId}-${active}` : undefined}\n value={text}\n onChange={(e) => type_(e.target.value)}\n onFocus={() => setOpen(true)}\n onBlur={(e) => {\n if (wrapRef.current?.contains(e.relatedTarget as Node)) return;\n if (inlineList) setOpen(false);\n // came from a chip: nothing typed → show the chip again\n if (picked && !text) setSearching(false);\n }}\n onKeyDown={(e) => {\n if (e.key === \"ArrowDown\" && rows.length) {\n e.preventDefault();\n setOpen(true);\n setActive((a) => (a + 1) % rows.length);\n } else if (e.key === \"ArrowUp\" && rows.length) {\n e.preventDefault();\n setActive((a) => (a <= 0 ? rows.length - 1 : a - 1));\n } else if (e.key === \"Enter\") {\n e.preventDefault();\n if (listOpen && active >= 0 && rows[active]) choose(rows[active].target);\n else onEnter?.(withText(v, text, manualType ?? undefined));\n } else if (e.key === \"Escape\") {\n if (listOpen) {\n e.preventDefault();\n e.stopPropagation();\n setOpen(false);\n } else if (picked && !text && !startSearching) {\n e.preventDefault();\n e.stopPropagation();\n setSearching(false);\n } else if (onEscape) {\n e.preventDefault();\n e.stopPropagation();\n onEscape();\n }\n }\n }}\n placeholder={picked && !text ? picked.title : meta.placeholder}\n autoComplete=\"off\"\n spellCheck={false}\n className=\"h-full min-w-0 flex-1 bg-transparent px-2 text-kit-sm text-kit-fg outline-none placeholder:text-kit-fg-3\"\n />\n {text ? (\n <IconButton label=\"Clear\" size=\"xs\" className=\"mr-1\" onMouseDown={(e) => e.preventDefault()} onClick={clear}>\n <IconX stroke={1.5} />\n </IconButton>\n ) : null}\n {type === \"file\" && MediaPicker ? (\n <IconButton\n label=\"Choose or upload a file\"\n size=\"xs\"\n className=\"mr-1 text-kit-fg-3\"\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => {\n setOpen(false);\n setLibrary(true);\n }}\n >\n <IconFolderOpen stroke={1.5} />\n </IconButton>\n ) : null}\n </>\n )}\n </PopoverAnchor>\n </div>\n <PopoverContent\n keepFocus\n align=\"start\"\n side=\"bottom\"\n sideOffset={4}\n data-kit-link-list=\"\"\n className=\"w-[max(var(--anchor-width),280px)] max-w-[min(420px,calc(100vw-24px))] !bg-kit-surface p-1 shadow-kit-lg\"\n >\n {list}\n </PopoverContent>\n </Popover>\n {inlineList && listOpen ? (\n <div className=\"rounded-kit-md border border-kit-line bg-kit-surface p-1 [&_[role=listbox]]:max-h-56\" onMouseDown={(e) => e.preventDefault()}>\n {list}\n </div>\n ) : null}\n {fileProblem ? (\n <p role=\"alert\" className=\"text-kit-xs text-kit-danger-fg\">\n {fileProblem}\n </p>\n ) : null}\n {/* File: the host's media library (search, folders, upload) is one click away; mounted only while open */}\n {library && MediaPicker ? (\n <MediaPicker\n open\n accept={[\"file\"]}\n onOpenChange={(o) => !o && setLibrary(false)}\n onPick={(a) => {\n if (!a[0]) return;\n const t = fileTarget(a[0]);\n if (\"problem\" in t) setFileProblem(t.problem);\n else choose(t);\n }}\n />\n ) : null}\n </div>\n );\n}\n\n/* ── Shared options (subject · open in new tab · rel) ───────────────── */\nexport function LinkOptions({ v, onChange, compact, disabled }: { v: LinkValue; onChange: (v: LinkValue) => void; compact?: boolean; disabled?: boolean }) {\n const tabId = React.useId();\n return (\n <div className={cn(\"flex flex-col\", compact ? \"gap-2\" : \"gap-2.5\")}>\n {v.type === \"email\" ? (\n <label className=\"flex items-center justify-between gap-3\">\n <span className=\"text-kit-xs text-kit-fg-2\">Subject</span>\n <Input size=\"sm\" value={v.subject ?? \"\"} onChange={(e) => onChange({ ...v, subject: e.target.value })} placeholder=\"Wholesale inquiry\" className=\"max-w-52\" />\n </label>\n ) : null}\n <label htmlFor={tabId} className=\"flex min-h-7 items-center justify-between gap-3\">\n <span className=\"text-kit-xs text-kit-fg-2\">Open in new tab</span>\n <Switch disabled={disabled} id={tabId} size=\"sm\" checked={!!v.newTab} onCheckedChange={(c) => onChange({ ...v, newTab: c, target: undefined })} />\n </label>\n <div className=\"flex items-center justify-between gap-3\">\n <span className=\"text-kit-xs text-kit-fg-2\">Rel</span>\n <Select disabled={disabled} aria-label=\"Rel\" size=\"sm\" className=\"w-[168px]\" value={v.rel ?? \"follow\"} onValueChange={(r) => onChange({ ...v, rel: r as LinkRel })} options={REL_ITEMS} />\n </div>\n </div>\n );\n}\n\n/* ── The link element ───────────────────────────────────────────────\n * CPO D48 + D57: the ONE way to edit a link, wherever a link is edited: Type (dropdown) → the target for that type\n * (results appear only once you focus or type, in the flow below the input, never over the panel; File adds the media\n * library) → the error in plain words when it can't be saved → Section on the picked page → the options as plain rows\n * (new tab, rel, subject). Hosts wrap it: the link panel (LinkPopoverBody), the button screen. Controlled: the host owns\n * the value; remount (key) to start over.\n */\nexport function LinkForm({\n value,\n onChange,\n onEnter,\n hideOptions,\n types,\n autoFocus,\n readOnly,\n className,\n}: {\n value: LinkValue;\n onChange: (v: LinkValue) => void;\n /** Enter in the target with a value that can be saved */\n onEnter?: (v: LinkValue) => void;\n /** string-only values: no new tab / rel */\n hideOptions?: boolean;\n /** the link types this place allows. Default: all */\n types?: LinkType[];\n /** focus the Type control on open (D48: never the search) */\n autoFocus?: boolean;\n /** the lens can't edit the link: every control is disabled */\n readOnly?: boolean;\n className?: string;\n}) {\n const v = normLink(value);\n const { MediaPicker } = useKitServices();\n const allowed = LINK_TYPES.filter((t) => (!types || types.includes(t.type)) && (t.type !== \"file\" || MediaPicker || v.type === \"file\"));\n const [kind, setKind] = React.useState<LinkType>(() => {\n const k = v.type === \"entry\" && hasLink(v) ? \"page\" : v.type ?? \"page\";\n return allowed.some((t) => t.type === k) ? k : allowed[0]?.type ?? \"page\";\n });\n const [resetKey, setResetKey] = React.useState(0);\n const [touched, setTouched] = React.useState(false);\n const { targets } = useLinkTargets();\n const anchors = findTarget(targets, v)?.anchors;\n const typeId = React.useId();\n const targetId = React.useId();\n const errId = React.useId();\n const problem = touched ? linkProblem(v) : undefined;\n // the type select follows what the target turned out to be (a picked CMS item, a pasted URL) while \"Page\" is searching\n const shownType: LinkType = kind === \"page\" && hasLink(v) ? v.type ?? \"page\" : kind;\n React.useEffect(() => {\n if (autoFocus) requestAnimationFrame(() => document.getElementById(typeId)?.focus({ preventScroll: true }));\n }, [autoFocus, typeId]);\n const changeType = (t: LinkType) => {\n setKind(t);\n setTouched(false);\n onChange({ ...withText(v, \"\", t === \"page\" ? undefined : t), href: \"\", type: t, anchor: undefined });\n setResetKey((k) => k + 1);\n requestAnimationFrame(() => document.getElementById(targetId)?.focus());\n };\n return (\n <div className={cn(\"flex min-w-0 flex-col gap-3\", className)}>\n <div className=\"flex items-center justify-between gap-3\">\n <label htmlFor={typeId} className=\"text-kit-xs text-kit-fg-2\">\n Type\n </label>\n <Select disabled={readOnly} id={typeId} size=\"sm\" className=\"w-[180px]\" value={shownType} onValueChange={(t) => changeType(t as LinkType)} options={allowed.map((t) => ({ value: t.type, label: t.label, icon: t.icon }))} />\n </div>\n <div\n className=\"flex flex-col gap-1.5\"\n onBlur={(e) => {\n if (!e.currentTarget.contains(e.relatedTarget as Node)) setTouched(true);\n }}\n >\n <label htmlFor={targetId} className=\"text-kit-xs text-kit-fg-2\">\n {typeMeta(kind).target}\n </label>\n <LinkPicker\n key={resetKey}\n id={targetId}\n size=\"sm\"\n value={v}\n onChange={onChange}\n onPick={() => setTouched(true)}\n onEnter={(n) => {\n setTouched(true);\n if (!linkProblem(n)) onEnter?.(n);\n }}\n lockedType={kind === \"page\" ? undefined : kind}\n inlineList\n types={types}\n invalid={!!problem}\n aria-describedby={problem ? errId : undefined}\n aria-label={typeMeta(kind).target}\n />\n {problem ? (\n <p id={errId} role=\"alert\" className=\"text-kit-xs text-kit-danger-fg\">\n {problem}\n </p>\n ) : null}\n </div>\n {anchors?.length ? (\n <div className=\"flex items-center justify-between gap-3\">\n <span className=\"text-kit-xs text-kit-fg-2\">Section</span>\n <Select disabled={readOnly} aria-label=\"Section\"\n size=\"sm\"\n className=\"w-[180px]\"\n value={v.anchor ?? \"none\"}\n onValueChange={(a) => onChange({ ...v, anchor: a })}\n options={[{ value: \"none\", label: \"Top of page\" }, ...anchors.map((a) => ({ value: a, label: a }))]}\n />\n </div>\n ) : null}\n {hideOptions ? null : <LinkOptions v={v} onChange={onChange} compact disabled={readOnly} />}\n </div>\n );\n}\n\n/* ── The link panel ─────────────────────────────────────────────────\n * What it links to now → the link element (LinkForm) → Remove link · Cancel · Apply (Apply only when something changed\n * and the link can be saved). Esc closes the results first, then the panel.\n */\nexport function LinkPopoverBody({\n initial,\n onSave,\n onDetach,\n onCancel,\n hideOptions,\n types,\n}: {\n initial: LinkValue;\n onSave: (v: LinkValue) => void;\n onDetach?: () => void;\n onCancel?: () => void;\n /** string-only fields: no new tab / rel */\n hideOptions?: boolean;\n /** the link types this place allows. Default: all */\n types?: LinkType[];\n}) {\n const start = normLink(initial);\n const [v, setV] = React.useState<LinkValue>(start);\n const { targets } = useLinkTargets();\n const startTarget = findTarget(targets, start);\n const fin = (x: LinkValue) => {\n const f = finalizeLink(x);\n return f ? JSON.stringify(storedLink(f)) : \"\";\n };\n const changed = fin(v) !== fin(start);\n const ok = hasLink(v) && !linkProblem(v);\n const apply = (n = v) => {\n const out = finalizeLink(n);\n if (out && hasLink(out) && !linkProblem(out)) onSave(out);\n };\n return (\n <form\n className=\"flex flex-col gap-3\"\n onSubmit={(e) => {\n e.preventDefault();\n e.stopPropagation();\n apply();\n }}\n >\n <div className=\"flex min-w-0 flex-col gap-1\">\n <span className=\"text-kit-sm font-medium text-kit-fg\">Link</span>\n {hasLink(start) ? (\n <span className=\"flex min-w-0 text-kit-fg-2\">\n <LinkLine v={start} target={startTarget} />\n </span>\n ) : (\n <span className=\"text-kit-xs text-kit-fg-3\">No link yet</span>\n )}\n </div>\n <LinkForm value={v} onChange={setV} onEnter={(n) => apply(n)} hideOptions={hideOptions} types={types} autoFocus />\n <div className=\"flex items-center gap-2 border-t border-kit-line pt-2.5\">\n {onDetach ? (\n <Button type=\"button\" size=\"xs\" variant=\"danger-ghost\" onClick={onDetach}>\n Remove link\n </Button>\n ) : null}\n <span className=\"flex-1\" />\n {onCancel ? (\n <Button type=\"button\" size=\"xs\" variant=\"ghost\" onClick={onCancel}>\n Cancel\n </Button>\n ) : null}\n <Button type=\"submit\" size=\"xs\" variant=\"primary\" disabled={!changed || !ok}>\n Apply\n </Button>\n </div>\n </form>\n );\n}\n\n/* ── Quick edit: the link row turns into the picker in place ───────── */\nexport function LinkQuickEdit({\n value,\n onCommit,\n onDone,\n ariaLabel,\n size,\n bare,\n types,\n}: {\n value: LinkValue;\n /** null = cleared */\n onCommit: (v: LinkValue | null) => void;\n onDone: () => void;\n ariaLabel: string;\n size?: \"sm\" | \"md\";\n bare?: boolean;\n types?: LinkType[];\n}) {\n const [draft, setDraft] = React.useState(value);\n const [problem, setProblem] = React.useState<string>();\n const done = React.useRef(false);\n const ref = React.useRef<HTMLDivElement>(null);\n const errId = React.useId();\n const commit = (n: LinkValue, strict = true) => {\n if (done.current) return;\n if (hasLink(n) && strict && linkProblem(n)) return setProblem(linkProblem(n));\n done.current = true;\n if (!hasLink(n)) onCommit(null);\n else {\n const out = finalizeLink(n);\n // a script link is dropped, never written (finalizeLink refuses it)\n if (out) onCommit(out);\n }\n onDone();\n };\n return (\n <div\n ref={ref}\n className=\"flex min-w-0 flex-col gap-1\"\n onBlur={(e) => {\n const next = e.relatedTarget as HTMLElement | null;\n if (libraryOpen || (next && (ref.current?.contains(next) || next.closest?.(`[${LIST_ATTR}],[role=menu]`)))) return;\n // focus left the editor: keep what was typed (nothing changed → nothing written)\n window.setTimeout(() => {\n if (libraryOpen || done.current || ref.current?.contains(document.activeElement)) return;\n if (hrefOf(draft) === hrefOf(value) && draft.pageId === value.pageId && draft.entryId === value.entryId) {\n done.current = true;\n onDone();\n } else commit(draft, false);\n }, 0);\n }}\n >\n <LinkPicker\n value={draft}\n onChange={(n) => {\n setDraft(n);\n setProblem(undefined);\n }}\n onPick={(n) => commit(n)}\n onEnter={(n) => commit(n)}\n onEscape={() => {\n done.current = true;\n onDone();\n }}\n autoFocus\n startSearching\n aria-label={ariaLabel}\n size={size}\n bare={bare}\n types={types}\n invalid={!!problem}\n aria-describedby={problem ? errId : undefined}\n />\n {problem ? (\n <p id={errId} role=\"alert\" className={cn(\"text-kit-xs text-kit-danger-fg\", bare && \"px-2.5 pb-1.5\")}>\n {problem}\n </p>\n ) : null}\n </div>\n );\n}\n\n/* ── Link field: one bordered row; click edits in place, the sliders icon opens the link panel ── */\nexport interface LinkFieldProps {\n /** a LinkValue or a plain href string */\n value: unknown;\n /** null = cleared (object shape); \"\" = cleared (string shape) */\n onChange: (v: LinkValue | string | null) => void;\n /** \"string\": the value is the href alone. New tab and rel need the object shape, so they hide. */\n valueShape?: \"string\" | \"object\";\n id?: string;\n \"aria-label\": string;\n readOnly?: boolean;\n size?: \"sm\" | \"md\";\n /** the link types this place allows. Default: all */\n types?: LinkType[];\n invalid?: boolean;\n placeholder?: string;\n className?: string;\n}\nexport function LinkField({ value, onChange: write, valueShape, id: idProp, \"aria-label\": label, readOnly, size = \"sm\", types, invalid, placeholder, className }: LinkFieldProps) {\n const v = normLink(value);\n const stringOnly = valueShape === \"string\";\n const onChange = (n: LinkValue | null) => write(stringOnly ? (n ? hrefOf(n) : \"\") : n ? storedLink(n, value) : null);\n const autoId = React.useId();\n const id = idProp ?? autoId;\n const [editing, setEditing] = React.useState(false);\n const [open, setOpen] = React.useState(false);\n const { targets } = useLinkTargets();\n const target = findTarget(targets, v);\n const has = hasLink(v);\n if (editing && !readOnly)\n return (\n <LinkQuickEdit\n value={v}\n ariaLabel={label}\n size={size}\n types={types}\n onCommit={onChange}\n onDone={() => {\n setEditing(false);\n requestAnimationFrame(() => document.getElementById(id)?.focus({ preventScroll: true }));\n }}\n />\n );\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverAnchor\n data-kit-link-field=\"\"\n className={cn(\n \"group/link relative flex min-w-0 items-center rounded-kit-md border border-kit-field-border bg-kit-field-bg shadow-kit-field transition-colors hover:border-kit-field-border-hover\",\n size === \"md\" ? \"h-9\" : \"h-8\",\n readOnly && \"hover:border-kit-field-border\",\n invalid && \"border-[var(--kit-field-invalid)]\",\n className,\n )}\n >\n <button\n id={id}\n type=\"button\"\n disabled={readOnly}\n onClick={() => setEditing(true)}\n // V2 link.tsx:977: the name reads the link's own text (its title, else its address), not the picked target's title\n aria-label={has ? `${label}: ${linkText(v)}. Edit link` : `Add ${label.toLowerCase()}`}\n aria-invalid={invalid || undefined}\n className=\"flex h-full min-w-0 flex-1 items-center px-2.5 text-left outline-none disabled:cursor-default\"\n >\n <LinkLine v={v} target={target} placeholder={placeholder} />\n </button>\n {readOnly ? null : (\n <span className=\"mr-1 flex shrink-0 items-center gap-0.5\">\n <Tooltip content=\"Link options\">\n <PopoverTrigger\n render={\n <IconButton label=\"Link options\" tooltip={false} size=\"xs\" className=\"text-kit-fg-3\">\n <IconAdjustmentsHorizontal stroke={1.5} />\n </IconButton>\n }\n />\n </Tooltip>\n {has ? (\n <IconButton\n label={`Clear ${label.toLowerCase()}`}\n size=\"xs\"\n className=\"hidden text-kit-fg-3 group-hover/link:inline-flex group-focus-within/link:inline-flex [@media(pointer:coarse)]:inline-flex\"\n onClick={() => onChange(null)}\n >\n <IconX stroke={1.5} />\n </IconButton>\n ) : null}\n </span>\n )}\n </PopoverAnchor>\n <PopoverContent className=\"w-[340px] p-3\" align=\"start\">\n <LinkPopoverBody\n initial={v}\n hideOptions={stringOnly}\n types={types}\n onSave={(n) => {\n onChange(n);\n setOpen(false);\n }}\n onDetach={\n has\n ? () => {\n onChange(null);\n setOpen(false);\n }\n : undefined\n }\n onCancel={() => setOpen(false)}\n />\n </PopoverContent>\n </Popover>\n );\n}\n\n/**\n * The shape a link field writes: the shape its value already has (a string stays a string, an object an object), and\n * for an empty field `field.valueShape`, else the string prod stores by default.\n */\nexport function linkShapeOf(value: unknown, valueShape?: \"string\" | \"object\"): \"string\" | \"object\" {\n if (isObj(value)) return \"object\";\n if (typeof value === \"string\" && value !== \"\") return \"string\";\n return valueShape ?? \"string\";\n}\n\n/** The Block Kit `link` field: the LinkField above, sized for the form it sits in. */\nexport function LinkControl({ value, onChange, readOnly, id, field, invalid }: ControlProps) {\n const { variant } = useKitEnv();\n return (\n <LinkField\n id={id}\n value={value}\n onChange={onChange}\n valueShape={linkShapeOf(value, field.valueShape)}\n aria-label={field.title}\n readOnly={readOnly}\n size={variant === \"document\" ? \"md\" : \"sm\"}\n invalid={invalid}\n />\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"link.js","names":[],"sources":["../../src/controls/link.tsx"],"sourcesContent":["/**\n * LINK — the ONE link element of the product (V2 HEAD kit/controls/link.tsx, DESIGN §8.7, CPO D43 · D48 · D57).\n *\n * - <LinkForm> Type (Page · CMS item · URL · Email · Phone · Section · File) → the target for that type (search over\n * the host's pages and CMS items, paste detection, URL / email / phone / #anchor validation; File =\n * the host's media library) → Section on the picked page → options (new tab · rel · email subject).\n * - <LinkPopoverBody> the link panel: what it links to now + LinkForm + Remove link · Cancel · Apply.\n * - <LinkField> a link at rest (one bordered row): click edits in place (quick edit), the sliders icon opens the panel.\n * - <LinkPicker> the target input inside all of the above.\n * - <LinkLine> how a link READS at rest: page icon + title + path · external icon + domain · mail / phone.\n *\n * Where the targets come from. The kit never fetches: the host hands its own link-target query in as a React hook,\n * `KitServices.useLinkTargets` (KitLinkServices below), and the media library as `KitServices.MediaPicker`. With no hook a\n * page link is typed as a path and the list says so in words; with no media picker the File type is not offered.\n * Section anchors come from the host that knows the page (SectionAnchorsProvider); without one a section is typed (#faq).\n *\n * Stored shape (prod `link`, adapters/values.ts linkToProd / linkFromProd). The kit value is what the field already\n * stores: a STRING href stays a string (legacy fields and the sites that read `href={link}`), an object stays an object.\n * An empty field takes `field.valueShape` and, with none, the string (prod LinkField's default). In the string shape the\n * options (new tab, rel) need the object, so they hide — V2's `valueShape=\"string\"`. An object never carries `target`\n * (derived from newTab), `rel: \"follow\"` (follow = no rel) or `newTab: false`, so it reads back exactly as written.\n *\n * Deviations from V2, each because prod has no source for it: no \"From this entry\" {{token}} targets (no binding store\n * behind them), no seeded page anchors, no fabricated CDN address for a file (a file without a public url is refused).\n */\nimport * as React from \"react\";\nimport {\n IconAdjustmentsHorizontal, IconArrowUpRight, IconAt, IconCheck, IconChevronDown, IconExternalLink, IconFile, IconFileDatabase,\n IconFolderOpen, IconHash, IconLink, IconMail, IconPaperclip, IconPhone, IconWorld, IconX,\n} from \"@tabler/icons-react\";\nimport { cn } from \"../lib/cn\";\nimport { isObj } from \"../lib/path\";\nimport { useKitEnv, useKitServices, type KitMediaAsset, type KitServices } from \"../context\";\nimport { Button, IconButton } from \"../ui/button\";\nimport { Input } from \"../ui/input\";\nimport { Switch } from \"../ui/controls\";\nimport { Autocomplete } from \"../ui/autocomplete\";\nimport { Select, type SelectOption } from \"../ui/select\";\nimport { Menu, MenuContent, MenuItem, MenuTrigger, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, Tooltip } from \"../ui/overlay\";\nimport type { ControlProps } from \"../types\";\n\nexport type LinkType = \"page\" | \"url\" | \"section\" | \"email\" | \"phone\" | \"file\" | \"entry\";\nexport type LinkRel = \"follow\" | \"nofollow\" | \"ugc\" | \"sponsored\";\nexport type ButtonStyle = \"primary\" | \"secondary\" | \"outline\" | \"link\";\nexport interface LinkValue {\n type?: LinkType;\n href: string;\n /** button label / link text */\n label?: string;\n /** resolved title of the target (page or entry name) */\n title?: string;\n pageId?: string;\n entryId?: string;\n anchor?: string;\n assetId?: string;\n email?: string;\n subject?: string;\n phone?: string;\n /** legacy target: read as newTab, never written */\n target?: \"_self\" | \"_blank\";\n newTab?: boolean;\n rel?: LinkRel;\n /* button-only (controls/button.tsx) */\n style?: ButtonStyle;\n variant?: string;\n size?: \"sm\" | \"md\" | \"lg\";\n arrow?: boolean;\n icon?: string;\n fullWidth?: boolean;\n}\n\n/* ── Host services ───────────────────────────────────────────────────── */\n/** A place a link can point at, as the host lists it. */\nexport interface LinkTarget {\n id: string;\n title: string;\n /** the public path (\"/about\", \"/blog/hello\") or a file's url */\n href: string;\n kind: \"page\" | \"entry\" | \"file\" | \"section\";\n /** collection name for an entry, file kind for a file */\n meta?: string;\n /** list group: \"Pages\", the collection's name, \"Files\", \"On this page\" */\n group: string;\n /** draft · published · scheduled · archived … (shown when it is not live) */\n status?: string;\n /** \"#faq\" anchors on that page, when the host knows them */\n anchors?: string[];\n /** a section without an anchor yet gets it when picked */\n onChoose?: () => void;\n}\n/**\n * The link services a host provides next to KitServices (registerKitServices / KitServicesProvider take them as is).\n * `useLinkTargets` is a React HOOK (called on every render of a link control), so a host passes the same function for\n * the life of the form.\n */\nexport type KitLinkServices = Pick<KitServices, \"useLinkTargets\">;\ntype LinkHost = { targets: LinkTarget[]; loading: boolean; error?: string; unavailable: boolean };\nconst NO_TARGETS: LinkTarget[] = [];\nconst useNoTargets = () => ({ targets: NO_TARGETS, loading: false, error: undefined as string | undefined });\n\n/** The host's pages, CMS items (and files, when it lists them). */\nexport function useLinkTargets(): LinkHost {\n const svc = useKitServices();\n const useHost = svc.useLinkTargets ?? useNoTargets;\n const r = useHost();\n return { targets: r.targets ?? NO_TARGETS, loading: !!r.loading, error: r.error, unavailable: !svc.useLinkTargets };\n}\n\n/** A section on the page being edited that a link can jump to (`id` on the block). */\nexport interface SectionAnchor {\n /** \"#faq\" */\n anchor: string;\n /** what a person reads: the block's name */\n label: string;\n /** the block has no anchor yet: give it this one when a link picks it */\n ensure?: () => void;\n}\nconst SectionAnchorsCtx = React.createContext<SectionAnchor[] | null>(null);\n/** The host that knows the page being edited lists its section anchors, so the Section type is a pick. */\nexport function SectionAnchorsProvider({ anchors, children }: { anchors: SectionAnchor[]; children: React.ReactNode }) {\n return <SectionAnchorsCtx.Provider value={anchors}>{children}</SectionAnchorsCtx.Provider>;\n}\nfunction useSectionTargets(): LinkTarget[] {\n const anchors = React.useContext(SectionAnchorsCtx);\n return React.useMemo(\n () =>\n (anchors ?? []).map((a) => ({\n id: `section:${a.anchor}`,\n title: a.label,\n href: a.anchor.startsWith(\"#\") ? a.anchor : `#${a.anchor}`,\n kind: \"section\" as const,\n group: \"On this page\",\n onChoose: a.ensure,\n })),\n [anchors],\n );\n}\n\n/** A file from the media library as a link target, or the reason it can't be one. */\nexport function fileTarget(a: Pick<KitMediaAsset, \"id\" | \"name\" | \"url\">): LinkTarget | { problem: string } {\n if (!a.url || a.url.startsWith(\"blob:\")) return { problem: \"This file has no public address yet. Wait for the upload to finish, then pick it again.\" };\n const name = a.name || fileName(a.url);\n return { id: a.id, title: name, href: a.url, kind: \"file\", meta: name.split(\".\").pop()?.toUpperCase(), group: \"Files\" };\n}\n\n/* ── Value helpers ───────────────────────────────────────────────────── */\nexport const normLink = (v: unknown): LinkValue => {\n const o: LinkValue = typeof v === \"string\" ? { href: v } : isObj(v) ? ({ ...(v as object), href: typeof v.href === \"string\" ? v.href : \"\" } as LinkValue) : { href: \"\" };\n if (o.newTab === undefined && o.target === \"_blank\") o.newTab = true;\n if (!o.type) o.type = detectType(o.href);\n return o;\n};\n\nexport function detectType(raw: string): LinkType {\n const s = raw.trim();\n if (!s) return \"page\";\n if (s.startsWith(\"#\")) return \"section\";\n if (/^mailto:/i.test(s) || /^[^\\s@/]+@[^\\s@]+\\.[^\\s@]+$/.test(s)) return \"email\";\n if (/^tel:/i.test(s) || /^\\+?[\\d\\s().-]{7,}$/.test(s)) return \"phone\";\n if (/\\.(pdf|zip|docx?|csv)$/i.test(s)) return \"file\";\n if (/^https?:\\/\\//i.test(s) || /^www\\./i.test(s) || /^[a-z0-9-]+(\\.[a-z0-9-]+)+(\\/|$)/i.test(s)) return \"url\";\n return \"page\";\n}\n\nexport const LINK_TYPES: { type: LinkType; label: string; icon: React.ReactNode; placeholder: string; target: string }[] = [\n { type: \"page\", label: \"Page\", icon: <IconFile stroke={1.5} />, placeholder: \"Search pages and CMS items, or paste a link\", target: \"Page\" },\n { type: \"entry\", label: \"CMS item\", icon: <IconFileDatabase stroke={1.5} />, placeholder: \"Search CMS items\", target: \"CMS item\" },\n { type: \"url\", label: \"URL\", icon: <IconWorld stroke={1.5} />, placeholder: \"https://example.com\", target: \"Address\" },\n { type: \"email\", label: \"Email\", icon: <IconAt stroke={1.5} />, placeholder: \"name@company.com\", target: \"Email address\" },\n { type: \"phone\", label: \"Phone\", icon: <IconPhone stroke={1.5} />, placeholder: \"+1 541 555 0182\", target: \"Phone number\" },\n { type: \"section\", label: \"Section\", icon: <IconHash stroke={1.5} />, placeholder: \"#section-on-this-page\", target: \"Section on this page\" },\n { type: \"file\", label: \"File\", icon: <IconPaperclip stroke={1.5} />, placeholder: \"Search files\", target: \"File\" },\n];\nconst typeMeta = (t?: string) => LINK_TYPES.find((x) => x.type === t) ?? LINK_TYPES[0];\n\n/** The target a value points at: by id first, then by an exact path (\"/about\" typed by hand still reads as About). */\nexport function findTarget(targets: LinkTarget[], v: LinkValue): LinkTarget | undefined {\n const byId = targets.find((t) => (v.pageId && t.id === v.pageId) || (v.entryId && t.id === v.entryId) || (v.assetId && t.id === v.assetId));\n if (byId) return byId;\n if (v.type === \"section\") return v.href ? targets.find((t) => t.kind === \"section\" && t.href === (v.href.startsWith(\"#\") ? v.href : `#${v.href}`)) : undefined;\n if (!v.href || (v.type !== \"page\" && v.type !== \"entry\" && v.type !== \"file\")) return undefined;\n if (v.type === \"file\") {\n const f = targets.find((t) => t.kind === \"file\" && t.href === v.href);\n // a file picked from the library reads as the file by the name stored with it\n return f ?? (v.title ? { id: v.assetId ?? v.href, title: v.title, href: v.href, kind: \"file\", group: \"Files\" } : undefined);\n }\n const path = v.href.replace(/[?#].*$/, \"\").replace(/\\/$/, \"\") || \"/\";\n return targets.find((t) => t.kind !== \"file\" && t.kind !== \"section\" && t.href === path);\n}\n\n/** The final href for a link value. */\nexport function hrefOf(v: LinkValue): string {\n switch (v.type) {\n case \"email\": {\n const addr = (v.email ?? v.href).replace(/^mailto:/i, \"\").replace(/\\?.*$/, \"\");\n return addr ? `mailto:${addr}${v.subject ? `?subject=${encodeURIComponent(v.subject)}` : \"\"}` : \"\";\n }\n case \"phone\": {\n const n = (v.phone ?? v.href).replace(/^tel:/i, \"\").replace(/[^\\d+]/g, \"\");\n return n ? `tel:${n}` : \"\";\n }\n case \"section\":\n return v.href.startsWith(\"#\") ? v.href : v.href ? `#${v.href}` : \"\";\n default: {\n // idempotent: a stored href already carries its anchor, so a changed or removed anchor replaces it\n const base = v.anchor !== undefined ? v.href.replace(/#.*$/, \"\") : v.href;\n return `${base}${v.anchor && v.anchor !== \"none\" ? v.anchor : \"\"}`;\n }\n }\n}\n\nconst BAD_URL = \"Enter a full address, like https://example.com.\";\n/** Why a link can't be saved yet, in plain words. Undefined when it is fine (or empty). */\nexport function linkProblem(v: LinkValue): string | undefined {\n if (!hasLink(v)) return undefined;\n const raw = (v.type === \"email\" ? v.email ?? v.href : v.type === \"phone\" ? v.phone ?? v.href : v.href).trim();\n if (/^(javascript|data|vbscript):/i.test(raw.replace(/[\\u0000- ]/g, \"\"))) return \"Script links aren't allowed.\";\n switch (v.type) {\n case \"url\": {\n if (raw.startsWith(\"/\") || raw.startsWith(\"#\")) return undefined;\n try {\n const u = new URL(/^[a-z][a-z0-9+.-]*:/i.test(raw) ? raw : `https://${raw}`);\n if (!/^https?:$/.test(u.protocol)) return BAD_URL;\n return /\\.[a-z]{2,}$/i.test(u.hostname) || u.hostname === \"localhost\" ? undefined : BAD_URL;\n } catch {\n return BAD_URL;\n }\n }\n case \"email\":\n return /^[^\\s@]+@[^\\s@]+\\.[^\\s@]{2,}$/.test(raw.replace(/^mailto:/i, \"\").replace(/\\?.*$/, \"\")) ? undefined : \"Enter an email address, like name@company.com.\";\n case \"phone\":\n return raw.replace(/^tel:/i, \"\").replace(/\\D/g, \"\").length >= 7 ? undefined : \"Enter a phone number with at least 7 digits.\";\n case \"section\":\n return /^#?[A-Za-z][\\w-]*$/.test(raw) ? undefined : \"Use letters, numbers and dashes, like #faq.\";\n case \"page\":\n case \"entry\":\n return raw.startsWith(\"/\") || raw.startsWith(\"#\") || v.pageId || v.entryId ? undefined : \"Pick a page, or start a path with /, like /about.\";\n default:\n return undefined;\n }\n}\n\n/** One list for every link editor (dropdown, never a segmented row — D40). */\nexport const REL_ITEMS: { value: LinkRel; label: string; description: string }[] = [\n { value: \"follow\", label: \"Do-follow\", description: \"Normal link. Search engines follow it.\" },\n { value: \"nofollow\", label: \"No-follow\", description: \"Search engines don't follow or credit it.\" },\n { value: \"sponsored\", label: \"Sponsored\", description: \"Paid, affiliate or ad link.\" },\n { value: \"ugc\", label: \"UGC\", description: \"Link added by users, e.g. comments.\" },\n];\n\nexport const hasLink = (v: LinkValue) => !!(v.href || v.email || v.phone);\nexport const TARGET_KEYS = { title: undefined, pageId: undefined, entryId: undefined, assetId: undefined, anchor: undefined } as const;\n\n/** The value a picked target produces (keeps label, style and options). */\nexport function withTarget(v: LinkValue, t: LinkTarget): LinkValue {\n return {\n ...v,\n ...TARGET_KEYS,\n email: undefined,\n phone: undefined,\n subject: undefined,\n type: t.kind === \"file\" ? \"file\" : t.kind === \"entry\" ? \"entry\" : t.kind === \"section\" ? \"section\" : \"page\",\n href: t.href,\n title: t.title,\n pageId: t.kind === \"page\" ? t.id : undefined,\n entryId: t.kind === \"entry\" ? t.id : undefined,\n assetId: t.kind === \"file\" ? t.id : undefined,\n };\n}\n/** The value typed text produces (auto-detects URL / email / phone / #section unless the type was chosen). */\nexport function withText(v: LinkValue, s: string, type?: LinkType): LinkValue {\n const t = type ?? detectType(s);\n const n: LinkValue = { ...v, ...TARGET_KEYS, type: t, email: undefined, phone: undefined };\n if (t === \"email\") Object.assign(n, { email: s.replace(/^mailto:/i, \"\"), href: s });\n else if (t === \"phone\") Object.assign(n, { phone: s.replace(/^tel:/i, \"\"), href: s });\n else n.href = s;\n return n;\n}\n/** Clean a value before it is stored: add https:// to bare domains, refuse script URLs, store the href the site renders. */\nexport function finalizeLink(v: LinkValue): LinkValue | null {\n const out: LinkValue = { ...v };\n if (/^(javascript|data|vbscript):/i.test(out.href.replace(/[\\u0000- ]/g, \"\"))) return null;\n if (out.type === \"url\" && out.href && !/^(https?:|\\/|#)/.test(out.href)) out.href = `https://${out.href.replace(/^\\/+/, \"\")}`;\n // the stored href is the one the site renders (mailto:, tel:, #anchor); every consumer reads `href` as is\n if (hasLink(out)) out.href = hrefOf(out);\n return out;\n}\n/**\n * The object as prod stores it, keyed like the value it replaces (`original`), so an edit changes only what was edited:\n * no undefined keys; no `rel: \"follow\"` (follow is no rel: the backend normalises it away and a button refuses it); no\n * `newTab: false` and no `target` unless the stored value had that key (a stored `target` is kept in sync with newTab,\n * and is then the one that carries it).\n */\nexport function storedLink<T extends LinkValue>(v: T, original?: unknown): T {\n const had = (k: string) => isObj(original) && k in original;\n const out: Record<string, unknown> = {};\n for (const [k, x] of Object.entries(v)) {\n if (x === undefined || k === \"target\") continue;\n if (k === \"rel\" && x === \"follow\") continue;\n if (k === \"newTab\" && !had(\"newTab\") && (x === false || had(\"target\"))) continue;\n out[k] = x;\n }\n if (had(\"target\")) out.target = v.newTab ? \"_blank\" : \"_self\";\n return out as T;\n}\n/** Text the picker's input shows for a value that has no picked target. */\nconst inputText = (v: LinkValue) =>\n v.type === \"email\" ? (v.email ?? v.href.replace(/^mailto:/i, \"\")) : v.type === \"phone\" ? (v.phone ?? v.href.replace(/^tel:/i, \"\")) : v.href;\n\nconst STATUS_LABEL: Record<string, string> = { draft: \"Draft\", changed: \"Edited\", scheduled: \"Scheduled\", review: \"In review\", archived: \"Archived\" };\n\n/* ── How a link reads ───────────────────────────────────────────────── */\nexport function linkIcon(v: LinkValue, target?: LinkTarget): React.ReactNode {\n if (!hasLink(v)) return <IconLink stroke={1.5} />;\n if (target) return target.kind === \"entry\" ? <IconFileDatabase stroke={1.5} /> : target.kind === \"file\" ? <IconPaperclip stroke={1.5} /> : target.kind === \"section\" ? <IconHash stroke={1.5} /> : <IconFile stroke={1.5} />;\n switch (v.type) {\n case \"url\": return <IconExternalLink stroke={1.5} />;\n case \"email\": return <IconMail stroke={1.5} />;\n case \"phone\": return <IconPhone stroke={1.5} />;\n case \"section\": return <IconHash stroke={1.5} />;\n case \"file\": return <IconPaperclip stroke={1.5} />;\n case \"entry\": return <IconFileDatabase stroke={1.5} />;\n default: return <IconFile stroke={1.5} />;\n }\n}\n/** Primary + secondary text: page title + path · domain + rest of the URL · address · number. */\nexport function describeLink(v: LinkValue, target?: LinkTarget): { primary: string; secondary?: string } {\n if (!hasLink(v)) return { primary: \"\" };\n const anchor = v.anchor && v.anchor !== \"none\" ? v.anchor : \"\";\n if (target) return { primary: target.title, secondary: target.kind === \"section\" ? `${target.href} on this page` : target.kind === \"file\" ? fileKind(target) : `${target.href}${anchor}` };\n switch (v.type) {\n case \"url\": {\n try {\n const u = new URL(/^https?:/i.test(v.href) ? v.href : `https://${v.href}`);\n const rest = `${u.pathname === \"/\" ? \"\" : u.pathname}${u.search}${u.hash}`;\n return { primary: u.hostname.replace(/^www\\./, \"\"), secondary: rest || undefined };\n } catch {\n return { primary: v.href };\n }\n }\n case \"email\": return { primary: v.email ?? v.href.replace(/^mailto:/i, \"\").replace(/\\?.*$/, \"\"), secondary: v.subject ? `Subject: ${v.subject}` : undefined };\n case \"phone\": return { primary: v.phone ?? v.href.replace(/^tel:/i, \"\") };\n case \"section\": return { primary: v.href, secondary: \"On this page\" };\n // a stored href already carries its anchor (finalizeLink)\n default: return { primary: v.title || v.href, secondary: v.title ? v.href : undefined };\n }\n}\n/** \"PDF file\": the name is already the title */\nconst fileKind = (t: LinkTarget) => `${t.meta ?? (fileName(t.href).split(\".\").pop() ?? \"\").toUpperCase()} file`.trim();\nfunction fileName(href: string) {\n const last = href.replace(/[?#].*$/, \"\").split(\"/\").pop() ?? href;\n try {\n return decodeURIComponent(last);\n } catch {\n return last;\n }\n}\n/** What a person reads for a link (one string — menus, aria labels, history). */\nexport const linkText = (v: LinkValue) => (hasLink(v) ? v.title || describeLink(v).primary : \"\");\n\n/** A link at rest: icon · title (fg) · path (fg-3) · new-tab mark. Empty → \"Add link\". */\nexport function LinkLine({ v, target, placeholder = \"Add link\", className }: { v: LinkValue; target?: LinkTarget; placeholder?: string; className?: string }) {\n const d = describeLink(v, target);\n const empty = !hasLink(v);\n return (\n <span className={cn(\"flex min-w-0 flex-1 items-center gap-2\", className)}>\n <span className={cn(\"flex shrink-0 [&_svg]:size-4\", empty ? \"text-kit-fg-3\" : \"text-kit-fg-2\")} aria-hidden>{linkIcon(v, target)}</span>\n <span className={cn(\"min-w-0 shrink truncate text-kit-sm\", empty ? \"text-kit-fg-3\" : \"text-kit-fg\")}>{empty ? placeholder : d.primary}</span>\n {d.secondary ? <span className=\"min-w-0 flex-1 truncate text-kit-xs text-kit-fg-3\">{d.secondary}</span> : <span className=\"flex-1\" />}\n {!empty && v.newTab ? (\n <span className=\"inline-flex shrink-0 items-center gap-0.5 text-kit-2xs text-kit-fg-3\">\n <IconArrowUpRight className=\"size-3\" stroke={1.5} aria-hidden />\n New tab\n </span>\n ) : null}\n </span>\n );\n}\n\n/* ── The picker: ONE link input for the whole product ───────────────── */\ntype Row = { key: string; group: string; target: LinkTarget };\n\n/** While the media library is open from a picker, focus sits in its dialog: a quick edit must not close under it. */\nlet libraryOpen = false;\n/** Marks the floating results list, so a quick edit losing focus to it is not \"focus left the editor\". */\nconst LIST_ATTR = \"data-kit-link-list\";\n\nexport interface LinkPickerProps {\n value: LinkValue;\n /** every change: typed text, a picked target, a cleared value */\n onChange: (v: LinkValue) => void;\n /** a target was chosen from the list (quick editors commit here) */\n onPick?: (v: LinkValue) => void;\n /** Enter with no list row active */\n onEnter?: (v: LinkValue) => void;\n /** Escape with the list closed */\n onEscape?: () => void;\n autoFocus?: boolean;\n /** open straight into search (quick edit), even when a target is picked */\n startSearching?: boolean;\n id?: string;\n \"aria-label\"?: string;\n size?: \"sm\" | \"md\";\n /** no border of its own — it sits inside a unit that already draws one (button field) */\n bare?: boolean;\n /** the host shows its own type control (link panel): hide the inline type menu and search only this type */\n lockedType?: LinkType;\n /** results render in the flow under the input instead of a floating list — panels/popovers, never over them */\n inlineList?: boolean;\n /** the link types this place allows. Default: all of them */\n types?: LinkType[];\n /** the value can't be saved as it is (shown as the field's error state) */\n invalid?: boolean;\n \"aria-describedby\"?: string;\n className?: string;\n}\n\nexport function LinkPicker({\n value, onChange, onPick, onEnter, onEscape, autoFocus, startSearching, id, \"aria-label\": ariaLabel = \"Link to\", size = \"md\", bare, lockedType,\n inlineList, types, invalid, \"aria-describedby\": describedBy, className,\n}: LinkPickerProps) {\n const v = normLink(value);\n const host = useLinkTargets();\n const sections = useSectionTargets();\n const { MediaPicker } = useKitServices();\n const targets = React.useMemo(() => [...host.targets, ...sections], [host.targets, sections]);\n const picked = findTarget(targets, v);\n const [library, setLibraryState] = React.useState(false);\n const setLibrary = (o: boolean) => {\n libraryOpen = o;\n setLibraryState(o);\n };\n React.useEffect(() => () => void (libraryOpen = false), []);\n const [fileProblem, setFileProblem] = React.useState<string>();\n const [searching, setSearching] = React.useState(!!startSearching || !picked);\n const [text, setText] = React.useState(() => (picked ? \"\" : inputText(v)));\n const [manualTypeState, setManualType] = React.useState<LinkType | null>(null);\n const manualType = lockedType ?? manualTypeState;\n const [open, setOpen] = React.useState(false);\n const inputRef = React.useRef<HTMLInputElement>(null);\n const wrapRef = React.useRef<HTMLDivElement>(null);\n const type = manualType ?? v.type ?? \"page\";\n const meta = typeMeta(manualType ?? (searching && !text ? \"page\" : v.type));\n const showChip = !!picked && !searching;\n\n React.useEffect(() => {\n if (autoFocus)\n requestAnimationFrame(() => {\n inputRef.current?.focus({ preventScroll: true });\n inputRef.current?.select(); // quick edit: typing replaces, paste replaces\n });\n }, [autoFocus]);\n\n /* rows: the host's pages + every collection's entries (+ files when it lists them) */\n const q = text.trim().toLowerCase().replace(/^[/#]/, \"\");\n const searchable = type === \"page\" || type === \"entry\" || type === \"file\" || (type === \"section\" && sections.length > 0);\n const urlish = type !== \"section\" && (/^(https?:|www\\.|mailto:|tel:|#)/i.test(text.trim()) || (text.trim() !== \"\" && detectType(text) !== \"page\" && !manualType));\n const rows: Row[] = React.useMemo(() => {\n if (!searchable || urlish) return [];\n const pool = targets.filter((t) => (type === \"file\" ? t.kind === \"file\" : type === \"entry\" ? t.kind === \"entry\" : type === \"section\" ? t.kind === \"section\" : t.kind === \"page\" || t.kind === \"entry\"));\n const hit = (t: LinkTarget) => !q || t.title.toLowerCase().includes(q) || t.href.toLowerCase().includes(q) || (t.meta ?? \"\").toLowerCase().includes(q);\n const out: Row[] = [];\n const perGroup = new Map<string, number>();\n const cap = q ? 6 : type === \"page\" ? 4 : 6;\n for (const t of pool) {\n if (!hit(t)) continue;\n const n = perGroup.get(t.group) ?? 0;\n if (n >= cap) continue;\n perGroup.set(t.group, n + 1);\n out.push({ key: `${t.kind}:${t.id}`, group: t.group, target: t });\n }\n // keep each group together, groups in first-seen order (Pages first)\n const order = [...new Set(out.map((r) => r.group))];\n return out.sort((a, b) => order.indexOf(a.group) - order.indexOf(b.group)).slice(0, 24);\n }, [targets, type, q, searchable, urlish]);\n /* No silent empty list: when nothing can be listed, the list says why in one line. */\n const note: string | undefined = (() => {\n if (!searchable || urlish || rows.length) return undefined;\n if (type === \"section\") return undefined;\n if (type === \"file\") return MediaPicker ? \"Choose a file from the library with the folder button.\" : undefined;\n if (host.unavailable) return \"Page search isn't available here. Type a path, like /about.\";\n if (host.error) return `${host.error.replace(/\\.?$/, \".\")} Type a path, like /about.`;\n if (host.loading) return \"Loading pages…\";\n return q ? `Nothing matches “${text.trim()}”. Type a path, like /about.` : type === \"entry\" ? \"No CMS items yet.\" : \"No pages yet. Type a path, like /about.\";\n })();\n const choose = (t: LinkTarget) => {\n t.onChoose?.();\n const n = withTarget(v, t);\n setText(\"\");\n setManualType(null);\n setSearching(false);\n setOpen(false);\n setFileProblem(undefined);\n onChange(n);\n onPick?.(n);\n };\n const type_ = (s: string) => {\n setText(s);\n setOpen(true);\n onChange(withText(v, s, manualType ?? undefined));\n };\n const clear = () => {\n setText(\"\");\n setManualType(null);\n setSearching(true);\n onChange({ ...withText(v, \"\"), href: \"\" });\n requestAnimationFrame(() => inputRef.current?.focus());\n };\n const setType = (t: LinkType) => {\n setManualType(t);\n setText(\"\");\n setSearching(true);\n onChange({ ...withText(v, \"\", t), href: \"\" });\n setOpen(true);\n requestAnimationFrame(() => inputRef.current?.focus());\n };\n\n const h = size === \"sm\" ? \"h-8\" : \"h-9\";\n /* the rows as the Autocomplete's options (V2 link.tsx:520-560: icon, title, href or file name, status) */\n const options: SelectOption[] = rows.map((r) => ({\n value: r.key,\n label: r.target.title,\n group: r.group,\n icon: linkIcon({ href: r.target.href, type: r.target.kind }, r.target),\n description: r.target.kind === \"file\" ? fileName(r.target.href) : r.target.href,\n meta: r.target.status ? STATUS_LABEL[r.target.status] : undefined,\n }));\n /* some targets loaded and some did not (a collection's items): the gap is named under the list */\n const listNote = host.error && type !== \"file\" && type !== \"section\" ? host.error.replace(/\\.?$/, \".\") : undefined;\n const allowed = LINK_TYPES.filter((t) => (!types || types.includes(t.type)) && (t.type !== \"file\" || MediaPicker || v.type === \"file\"));\n const typeMenu = (\n <Menu>\n <MenuTrigger\n render={\n <button\n type=\"button\"\n aria-label={`Link type: ${meta.label}`}\n className=\"ml-0.5 flex h-7 shrink-0 items-center gap-0.5 rounded-kit-md px-1.5 text-kit-fg-3 hover:bg-kit-hover hover:text-kit-fg data-[popup-open]:bg-kit-hover [&_svg]:size-3.5\"\n />\n }\n >\n {meta.icon}\n <IconChevronDown className=\"!size-3\" stroke={1.5} />\n </MenuTrigger>\n <MenuContent className=\"min-w-48\">\n {allowed.map((t) => (\n <MenuItem key={t.type} icon={t.icon} onClick={() => setType(t.type)}>\n <span className=\"flex w-full items-center justify-between gap-3\">\n {t.label}\n {t.type === type ? <IconCheck className=\"size-3.5 text-kit-fg-2\" stroke={2} /> : null}\n </span>\n </MenuItem>\n ))}\n </MenuContent>\n </Menu>\n );\n\n return (\n <div className={cn(\"flex min-w-0 flex-col gap-1\", className)}>\n <div\n ref={wrapRef}\n className=\"min-w-0\"\n onKeyDown={(e) => {\n // Escape with the list closed (the Autocomplete claims the one that closes it): back to the chip, else the host's\n if (e.key !== \"Escape\" || showChip) return;\n if (picked && !text && !startSearching) {\n e.preventDefault();\n e.stopPropagation();\n setSearching(false);\n } else if (onEscape) {\n e.preventDefault();\n e.stopPropagation();\n onEscape();\n }\n }}\n >\n {showChip ? (\n <div\n className={cn(\n \"flex min-w-0 items-center\",\n bare\n ? \"bg-transparent\"\n : \"rounded-kit-md border border-kit-field-border bg-kit-field-bg shadow-kit-field transition-colors hover:border-kit-field-border-hover focus-within:border-kit-field-border-focus\",\n !bare && invalid && \"border-[var(--kit-field-invalid)] hover:border-[var(--kit-field-invalid)]\",\n h,\n )}\n >\n <span className=\"ml-2 flex shrink-0 text-kit-fg-2 [&_svg]:size-4\" aria-hidden>\n {linkIcon(v, picked)}\n </span>\n <button\n id={id}\n type=\"button\"\n onClick={() => {\n setSearching(true);\n setOpen(true);\n requestAnimationFrame(() => inputRef.current?.focus());\n }}\n aria-label={`${ariaLabel}: ${picked!.title}, ${picked!.href}. Change`}\n className=\"flex h-full min-w-0 flex-1 items-center gap-2 px-2 text-left outline-none\"\n >\n <span className=\"min-w-0 shrink truncate text-kit-sm text-kit-fg\">{picked!.title}</span>\n <span className=\"min-w-0 flex-1 truncate text-kit-xs text-kit-fg-3\">{describeLink(v, picked).secondary}</span>\n </button>\n <IconButton label=\"Clear link\" size=\"xs\" className=\"mr-1\" onClick={clear}>\n <IconX stroke={1.5} />\n </IconButton>\n </div>\n ) : (\n /* search: the kit Autocomplete with V2 link.tsx's extras (one combobox implementation in the kit) */\n <Autocomplete\n id={id}\n aria-label={ariaLabel}\n aria-describedby={describedBy}\n invalid={invalid}\n size={size === \"sm\" ? \"md\" : \"lg\"}\n className={cn(\"shadow-none\", bare ? \"border-0 bg-transparent focus-within:shadow-none\" : \"shadow-kit-field focus-within:shadow-none\")}\n value={text}\n onValueChange={type_}\n options={options}\n filter={null}\n onSelect={(o) => {\n const r = rows.find((x) => x.key === o.value);\n if (r) choose(r.target);\n }}\n fillOnSelect={false}\n onEnter={() => onEnter?.(withText(v, text, manualType ?? undefined))}\n open={open && searching}\n onOpenChange={setOpen}\n inlineList={inlineList}\n emptyNote={note}\n listNote={listNote}\n highlightFirst\n highlightMatches={false}\n listLabel={type === \"file\" ? \"Files\" : type === \"section\" ? \"Sections on this page\" : \"Pages and CMS items\"}\n inputRef={inputRef}\n onBlur={(e) => {\n if (wrapRef.current?.contains(e.relatedTarget as Node)) return;\n if (inlineList) setOpen(false);\n // came from a chip: nothing typed → show the chip again\n if (picked && !text) setSearching(false);\n }}\n placeholder={picked && !text ? picked.title : meta.placeholder}\n leading={lockedType !== undefined || inlineList ? meta.icon : undefined}\n leadingSlot={lockedType !== undefined || inlineList ? undefined : typeMenu}\n onClear={clear}\n trailingSlot={\n type === \"file\" && MediaPicker ? (\n <IconButton\n label=\"Choose or upload a file\"\n size=\"xs\"\n className=\"mr-1 text-kit-fg-3\"\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => {\n setOpen(false);\n setLibrary(true);\n }}\n >\n <IconFolderOpen stroke={1.5} />\n </IconButton>\n ) : null\n }\n listAttrs={{ [LIST_ATTR]: \"\" }}\n menuClassName={inlineList ? \"mt-0 [&_[role=listbox]]:max-h-56\" : \"shadow-kit-lg\"}\n />\n )}\n </div>\n {fileProblem ? (\n <p role=\"alert\" className=\"text-kit-xs text-kit-danger-fg\">\n {fileProblem}\n </p>\n ) : null}\n {/* File: the host's media library (search, folders, upload) is one click away; mounted only while open */}\n {library && MediaPicker ? (\n <MediaPicker\n open\n accept={[\"file\"]}\n onOpenChange={(o) => !o && setLibrary(false)}\n onPick={(a) => {\n if (!a[0]) return;\n const t = fileTarget(a[0]);\n if (\"problem\" in t) setFileProblem(t.problem);\n else choose(t);\n }}\n />\n ) : null}\n </div>\n );\n}\n\n/* ── Shared options (subject · open in new tab · rel) ───────────────── */\nexport function LinkOptions({\n v, onChange, compact, disabled, hideNewTab,\n}: { v: LinkValue; onChange: (v: LinkValue) => void; compact?: boolean; disabled?: boolean; /** the place forbids a new tab (a rich-text field without allowTargetBlank) */ hideNewTab?: boolean }) {\n const tabId = React.useId();\n return (\n <div className={cn(\"flex flex-col\", compact ? \"gap-2\" : \"gap-2.5\")}>\n {v.type === \"email\" ? (\n <label className=\"flex items-center justify-between gap-3\">\n <span className=\"text-kit-xs text-kit-fg-2\">Subject</span>\n <Input size=\"sm\" value={v.subject ?? \"\"} onChange={(e) => onChange({ ...v, subject: e.target.value })} placeholder=\"Wholesale inquiry\" className=\"max-w-52\" />\n </label>\n ) : null}\n {hideNewTab ? null : (\n <label htmlFor={tabId} className=\"flex min-h-7 items-center justify-between gap-3\">\n <span className=\"text-kit-xs text-kit-fg-2\">Open in new tab</span>\n <Switch disabled={disabled} id={tabId} size=\"sm\" checked={!!v.newTab} onCheckedChange={(c) => onChange({ ...v, newTab: c, target: undefined })} />\n </label>\n )}\n <div className=\"flex items-center justify-between gap-3\">\n <span className=\"text-kit-xs text-kit-fg-2\">Rel</span>\n <Select disabled={disabled} aria-label=\"Rel\" size=\"sm\" className=\"w-[168px]\" value={v.rel ?? \"follow\"} onValueChange={(r) => onChange({ ...v, rel: r as LinkRel })} options={REL_ITEMS} />\n </div>\n </div>\n );\n}\n\n/* ── The link element ───────────────────────────────────────────────\n * CPO D48 + D57: the ONE way to edit a link, wherever a link is edited: Type (dropdown) → the target for that type\n * (results appear only once you focus or type, in the flow below the input, never over the panel; File adds the media\n * library) → the error in plain words when it can't be saved → Section on the picked page → the options as plain rows\n * (new tab, rel, subject). Hosts wrap it: the link panel (LinkPopoverBody), the button screen. Controlled: the host owns\n * the value; remount (key) to start over.\n */\nexport function LinkForm({\n value,\n onChange,\n onEnter,\n hideOptions,\n hideNewTab,\n types,\n autoFocus,\n readOnly,\n className,\n}: {\n value: LinkValue;\n onChange: (v: LinkValue) => void;\n /** Enter in the target with a value that can be saved */\n onEnter?: (v: LinkValue) => void;\n /** string-only values: no new tab / rel */\n hideOptions?: boolean;\n /** no new tab here (rel stays) */\n hideNewTab?: boolean;\n /** the link types this place allows. Default: all */\n types?: LinkType[];\n /** focus the Type control on open (D48: never the search) */\n autoFocus?: boolean;\n /** the lens can't edit the link: every control is disabled */\n readOnly?: boolean;\n className?: string;\n}) {\n const v = normLink(value);\n const { MediaPicker } = useKitServices();\n const allowed = LINK_TYPES.filter((t) => (!types || types.includes(t.type)) && (t.type !== \"file\" || MediaPicker || v.type === \"file\"));\n const [kind, setKind] = React.useState<LinkType>(() => {\n const k = v.type === \"entry\" && hasLink(v) ? \"page\" : v.type ?? \"page\";\n return allowed.some((t) => t.type === k) ? k : allowed[0]?.type ?? \"page\";\n });\n const [resetKey, setResetKey] = React.useState(0);\n const [touched, setTouched] = React.useState(false);\n const { targets } = useLinkTargets();\n const anchors = findTarget(targets, v)?.anchors;\n const typeId = React.useId();\n const targetId = React.useId();\n const errId = React.useId();\n const problem = touched ? linkProblem(v) : undefined;\n // the type select follows what the target turned out to be (a picked CMS item, a pasted URL) while \"Page\" is searching\n const shownType: LinkType = kind === \"page\" && hasLink(v) ? v.type ?? \"page\" : kind;\n React.useEffect(() => {\n if (autoFocus) requestAnimationFrame(() => document.getElementById(typeId)?.focus({ preventScroll: true }));\n }, [autoFocus, typeId]);\n const changeType = (t: LinkType) => {\n setKind(t);\n setTouched(false);\n onChange({ ...withText(v, \"\", t === \"page\" ? undefined : t), href: \"\", type: t, anchor: undefined });\n setResetKey((k) => k + 1);\n requestAnimationFrame(() => document.getElementById(targetId)?.focus());\n };\n return (\n <div className={cn(\"flex min-w-0 flex-col gap-3\", className)}>\n <div className=\"flex items-center justify-between gap-3\">\n <label htmlFor={typeId} className=\"text-kit-xs text-kit-fg-2\">\n Type\n </label>\n <Select disabled={readOnly} id={typeId} size=\"sm\" className=\"w-[180px]\" value={shownType} onValueChange={(t) => changeType(t as LinkType)} options={allowed.map((t) => ({ value: t.type, label: t.label, icon: t.icon }))} />\n </div>\n <div\n className=\"flex flex-col gap-1.5\"\n onBlur={(e) => {\n if (!e.currentTarget.contains(e.relatedTarget as Node)) setTouched(true);\n }}\n >\n <label htmlFor={targetId} className=\"text-kit-xs text-kit-fg-2\">\n {typeMeta(kind).target}\n </label>\n <LinkPicker\n key={resetKey}\n id={targetId}\n size=\"sm\"\n value={v}\n onChange={onChange}\n onPick={() => setTouched(true)}\n onEnter={(n) => {\n setTouched(true);\n if (!linkProblem(n)) onEnter?.(n);\n }}\n lockedType={kind === \"page\" ? undefined : kind}\n inlineList\n types={types}\n invalid={!!problem}\n aria-describedby={problem ? errId : undefined}\n aria-label={typeMeta(kind).target}\n />\n {problem ? (\n <p id={errId} role=\"alert\" className=\"text-kit-xs text-kit-danger-fg\">\n {problem}\n </p>\n ) : null}\n </div>\n {anchors?.length ? (\n <div className=\"flex items-center justify-between gap-3\">\n <span className=\"text-kit-xs text-kit-fg-2\">Section</span>\n <Select disabled={readOnly} aria-label=\"Section\"\n size=\"sm\"\n className=\"w-[180px]\"\n value={v.anchor ?? \"none\"}\n onValueChange={(a) => onChange({ ...v, anchor: a })}\n options={[{ value: \"none\", label: \"Top of page\" }, ...anchors.map((a) => ({ value: a, label: a }))]}\n />\n </div>\n ) : null}\n {hideOptions ? null : <LinkOptions v={v} onChange={onChange} compact disabled={readOnly} hideNewTab={hideNewTab} />}\n </div>\n );\n}\n\n/* ── The link panel ─────────────────────────────────────────────────\n * What it links to now → the link element (LinkForm) → Remove link · Cancel · Apply (Apply only when something changed\n * and the link can be saved). Esc closes the results first, then the panel.\n */\nexport function LinkPopoverBody({\n initial,\n onSave,\n onDetach,\n onCancel,\n hideOptions,\n hideNewTab,\n types,\n dirty = false,\n}: {\n initial: LinkValue;\n onSave: (v: LinkValue) => void;\n onDetach?: () => void;\n onCancel?: () => void;\n /** string-only fields: no new tab / rel */\n hideOptions?: boolean;\n /** no new tab here (rel stays): a rich-text field without allowTargetBlank */\n hideNewTab?: boolean;\n /** the link types this place allows. Default: all */\n types?: LinkType[];\n /** something the host draws beside the form changed (a button's label), so Apply has something to save */\n dirty?: boolean;\n}) {\n const start = normLink(initial);\n const [v, setV] = React.useState<LinkValue>(start);\n const { targets } = useLinkTargets();\n const startTarget = findTarget(targets, start);\n const fin = (x: LinkValue) => {\n const f = finalizeLink(x);\n return f ? JSON.stringify(storedLink(f)) : \"\";\n };\n const changed = dirty || fin(v) !== fin(start);\n const ok = hasLink(v) && !linkProblem(v);\n const apply = (n = v) => {\n const out = finalizeLink(n);\n if (out && hasLink(out) && !linkProblem(out)) onSave(out);\n };\n return (\n <form\n className=\"flex flex-col gap-3\"\n onSubmit={(e) => {\n e.preventDefault();\n e.stopPropagation();\n apply();\n }}\n >\n <div className=\"flex min-w-0 flex-col gap-1\">\n <span className=\"text-kit-sm font-medium text-kit-fg\">Link</span>\n {hasLink(start) ? (\n <span className=\"flex min-w-0 text-kit-fg-2\">\n <LinkLine v={start} target={startTarget} />\n </span>\n ) : (\n <span className=\"text-kit-xs text-kit-fg-3\">No link yet</span>\n )}\n </div>\n <LinkForm value={v} onChange={setV} onEnter={(n) => apply(n)} hideOptions={hideOptions} hideNewTab={hideNewTab} types={types} autoFocus />\n <div className=\"flex items-center gap-2 border-t border-kit-line pt-2.5\">\n {onDetach ? (\n <Button type=\"button\" size=\"xs\" variant=\"danger-ghost\" onClick={onDetach}>\n Remove link\n </Button>\n ) : null}\n <span className=\"flex-1\" />\n {onCancel ? (\n <Button type=\"button\" size=\"xs\" variant=\"ghost\" onClick={onCancel}>\n Cancel\n </Button>\n ) : null}\n <Button type=\"submit\" size=\"xs\" variant=\"primary\" disabled={!changed || !ok}>\n Apply\n </Button>\n </div>\n </form>\n );\n}\n\n/* ── Quick edit: the link row turns into the picker in place ───────── */\nexport function LinkQuickEdit({\n value,\n onCommit,\n onDone,\n ariaLabel,\n size,\n bare,\n types,\n}: {\n value: LinkValue;\n /** null = cleared */\n onCommit: (v: LinkValue | null) => void;\n onDone: () => void;\n ariaLabel: string;\n size?: \"sm\" | \"md\";\n bare?: boolean;\n types?: LinkType[];\n}) {\n const [draft, setDraft] = React.useState(value);\n const [problem, setProblem] = React.useState<string>();\n const done = React.useRef(false);\n const ref = React.useRef<HTMLDivElement>(null);\n const errId = React.useId();\n const commit = (n: LinkValue, strict = true) => {\n if (done.current) return;\n if (hasLink(n) && strict && linkProblem(n)) return setProblem(linkProblem(n));\n done.current = true;\n if (!hasLink(n)) onCommit(null);\n else {\n const out = finalizeLink(n);\n // a script link is dropped, never written (finalizeLink refuses it)\n if (out) onCommit(out);\n }\n onDone();\n };\n return (\n <div\n ref={ref}\n className=\"flex min-w-0 flex-col gap-1\"\n onBlur={(e) => {\n const next = e.relatedTarget as HTMLElement | null;\n if (libraryOpen || (next && (ref.current?.contains(next) || next.closest?.(`[${LIST_ATTR}],[role=menu]`)))) return;\n // focus left the editor: keep what was typed (nothing changed → nothing written)\n window.setTimeout(() => {\n if (libraryOpen || done.current || ref.current?.contains(document.activeElement)) return;\n if (hrefOf(draft) === hrefOf(value) && draft.pageId === value.pageId && draft.entryId === value.entryId) {\n done.current = true;\n onDone();\n } else commit(draft, false);\n }, 0);\n }}\n >\n <LinkPicker\n value={draft}\n onChange={(n) => {\n setDraft(n);\n setProblem(undefined);\n }}\n onPick={(n) => commit(n)}\n onEnter={(n) => commit(n)}\n onEscape={() => {\n done.current = true;\n onDone();\n }}\n autoFocus\n startSearching\n aria-label={ariaLabel}\n size={size}\n bare={bare}\n types={types}\n invalid={!!problem}\n aria-describedby={problem ? errId : undefined}\n />\n {problem ? (\n <p id={errId} role=\"alert\" className={cn(\"text-kit-xs text-kit-danger-fg\", bare && \"px-2.5 pb-1.5\")}>\n {problem}\n </p>\n ) : null}\n </div>\n );\n}\n\n/* ── Link field: one bordered row; click edits in place, the sliders icon opens the link panel ── */\nexport interface LinkFieldProps {\n /** a LinkValue or a plain href string */\n value: unknown;\n /** null = cleared (object shape); \"\" = cleared (string shape) */\n onChange: (v: LinkValue | string | null) => void;\n /** \"string\": the value is the href alone. New tab and rel need the object shape, so they hide. */\n valueShape?: \"string\" | \"object\";\n id?: string;\n \"aria-label\": string;\n readOnly?: boolean;\n size?: \"sm\" | \"md\";\n /** the link types this place allows. Default: all */\n types?: LinkType[];\n invalid?: boolean;\n placeholder?: string;\n className?: string;\n}\nexport function LinkField({ value, onChange: write, valueShape, id: idProp, \"aria-label\": label, readOnly, size = \"sm\", types, invalid, placeholder, className }: LinkFieldProps) {\n const v = normLink(value);\n const stringOnly = valueShape === \"string\";\n const onChange = (n: LinkValue | null) => write(stringOnly ? (n ? hrefOf(n) : \"\") : n ? storedLink(n, value) : null);\n const autoId = React.useId();\n const id = idProp ?? autoId;\n const [editing, setEditing] = React.useState(false);\n const [open, setOpen] = React.useState(false);\n const { targets } = useLinkTargets();\n const target = findTarget(targets, v);\n const has = hasLink(v);\n if (editing && !readOnly)\n return (\n <LinkQuickEdit\n value={v}\n ariaLabel={label}\n size={size}\n types={types}\n onCommit={onChange}\n onDone={() => {\n setEditing(false);\n requestAnimationFrame(() => document.getElementById(id)?.focus({ preventScroll: true }));\n }}\n />\n );\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverAnchor\n data-kit-link-field=\"\"\n className={cn(\n \"group/link relative flex min-w-0 items-center rounded-kit-md border border-kit-field-border bg-kit-field-bg shadow-kit-field transition-colors hover:border-kit-field-border-hover\",\n size === \"md\" ? \"h-9\" : \"h-8\",\n readOnly && \"hover:border-kit-field-border\",\n invalid && \"border-[var(--kit-field-invalid)]\",\n className,\n )}\n >\n <button\n id={id}\n type=\"button\"\n disabled={readOnly}\n onClick={() => setEditing(true)}\n // V2 link.tsx:977: the name reads the link's own text (its title, else its address), not the picked target's title\n aria-label={has ? `${label}: ${linkText(v)}. Edit link` : `Add ${label.toLowerCase()}`}\n aria-invalid={invalid || undefined}\n className=\"flex h-full min-w-0 flex-1 items-center px-2.5 text-left outline-none disabled:cursor-default\"\n >\n <LinkLine v={v} target={target} placeholder={placeholder} />\n </button>\n {readOnly ? null : (\n <span className=\"mr-1 flex shrink-0 items-center gap-0.5\">\n <Tooltip content=\"Link options\">\n <PopoverTrigger\n render={\n <IconButton label=\"Link options\" tooltip={false} size=\"xs\" className=\"text-kit-fg-3\">\n <IconAdjustmentsHorizontal stroke={1.5} />\n </IconButton>\n }\n />\n </Tooltip>\n {has ? (\n <IconButton\n label={`Clear ${label.toLowerCase()}`}\n size=\"xs\"\n className=\"hidden text-kit-fg-3 group-hover/link:inline-flex group-focus-within/link:inline-flex [@media(pointer:coarse)]:inline-flex\"\n onClick={() => onChange(null)}\n >\n <IconX stroke={1.5} />\n </IconButton>\n ) : null}\n </span>\n )}\n </PopoverAnchor>\n <PopoverContent className=\"w-[340px] p-3\" align=\"start\">\n <LinkPopoverBody\n initial={v}\n hideOptions={stringOnly}\n types={types}\n onSave={(n) => {\n onChange(n);\n setOpen(false);\n }}\n onDetach={\n has\n ? () => {\n onChange(null);\n setOpen(false);\n }\n : undefined\n }\n onCancel={() => setOpen(false)}\n />\n </PopoverContent>\n </Popover>\n );\n}\n\n/**\n * The shape a link field writes: the shape its value already has (a string stays a string, an object an object), and\n * for an empty field `field.valueShape`, else the string prod stores by default.\n */\nexport function linkShapeOf(value: unknown, valueShape?: \"string\" | \"object\"): \"string\" | \"object\" {\n if (isObj(value)) return \"object\";\n if (typeof value === \"string\" && value !== \"\") return \"string\";\n return valueShape ?? \"string\";\n}\n\n/** The Block Kit `link` field: the LinkField above, sized for the form it sits in. */\nexport function LinkControl({ value, onChange, readOnly, id, field, invalid }: ControlProps) {\n const { variant } = useKitEnv();\n return (\n <LinkField\n id={id}\n value={value}\n onChange={onChange}\n valueShape={linkShapeOf(value, field.valueShape)}\n aria-label={field.title}\n readOnly={readOnly}\n size={variant === \"document\" ? \"md\" : \"sm\"}\n invalid={invalid}\n />\n );\n}\n"],"mappings":"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"}
|
|
@@ -4,7 +4,7 @@ import { Input } from "../ui/input.js";
|
|
|
4
4
|
import { TEXT_FOCUS } from "./text.js";
|
|
5
5
|
import * as React from "react";
|
|
6
6
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
-
import {
|
|
7
|
+
import { IconMapPin } from "@tabler/icons-react";
|
|
8
8
|
//#region src/controls/location.tsx
|
|
9
9
|
/**
|
|
10
10
|
* LOCATION — the geopoint field (DESIGN 8.21), V2 GeoControl fitted to what prod stores.
|
|
@@ -141,17 +141,12 @@ function LocationControl({ field, value, onChange, readOnly, id, invalid }) {
|
|
|
141
141
|
href: `https://www.openstreetmap.org/?mlat=${shown.lat}&mlon=${shown.lng}#map=15/${shown.lat}/${shown.lng}`,
|
|
142
142
|
target: "_blank",
|
|
143
143
|
rel: "noreferrer noopener",
|
|
144
|
-
className:
|
|
144
|
+
className: "tabular-nums absolute bottom-2 right-2 rounded-kit-md bg-kit-surface/90 px-1.5 py-0.5 font-kit-mono text-kit-2xs text-kit-fg-2 shadow-kit-xs hover:text-kit-fg",
|
|
145
145
|
"aria-label": `Open ${shown.lat.toFixed(4)}, ${shown.lng.toFixed(4)} on OpenStreetMap (new tab)`,
|
|
146
146
|
children: [
|
|
147
147
|
shown.lat.toFixed(4),
|
|
148
148
|
", ",
|
|
149
|
-
shown.lng.toFixed(4)
|
|
150
|
-
/* @__PURE__ */ jsx(IconExternalLink, {
|
|
151
|
-
className: "size-3",
|
|
152
|
-
stroke: 1.5,
|
|
153
|
-
"aria-hidden": true
|
|
154
|
-
})
|
|
149
|
+
shown.lng.toFixed(4)
|
|
155
150
|
]
|
|
156
151
|
})] }) : /* @__PURE__ */ jsx("p", {
|
|
157
152
|
className: "absolute inset-0 flex items-center justify-center text-kit-xs text-kit-fg-3",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"location.js","names":[],"sources":["../../src/controls/location.tsx"],"sourcesContent":["/**\n * LOCATION — the geopoint field (DESIGN 8.21), V2 GeoControl fitted to what prod stores.\n *\n * Value (backend reference-validation `location`, the classic LocationField): `{ lat, lng, label? }`, lat −90…90 and\n * lng −180…180, finite numbers; null when empty. Keys the stored object carries beyond those are kept.\n *\n * A coordinate is written only when it is whole: both numbers, in range. Until then the field says what is missing\n * (\"Add the longitude too\") and keeps the stored value, because the backend refuses half a coordinate. Emptying all\n * three boxes clears the value. Pasting \"44.0582, -121.3153\" into either box fills both.\n *\n * The card above the inputs shows the pin and the coordinates, with a link that opens them on OpenStreetMap; it\n * draws no map tiles (no vendor, key or bundle).\n */\nimport * as React from \"react\";\nimport { IconExternalLink, IconMapPin } from \"@tabler/icons-react\";\nimport { cn } from \"../lib/cn\";\nimport { isObj } from \"../lib/path\";\nimport type { ControlProps } from \"../types\";\nimport { Input } from \"../ui/input\";\nimport { TEXT_FOCUS } from \"./text\";\n\ninterface Draft {\n lat: string;\n lng: string;\n label: string;\n}\nconst num = (v: unknown) => (typeof v === \"number\" && Number.isFinite(v) ? String(v) : \"\");\nconst draftOf = (v: unknown): Draft => (isObj(v) ? { lat: num(v.lat), lng: num(v.lng), label: typeof v.label === \"string\" ? v.label : \"\" } : { lat: \"\", lng: \"\", label: \"\" });\nconst PAIR = /^\\s*(-?\\d+(?:\\.\\d+)?)\\s*[,;\\s]\\s*(-?\\d+(?:\\.\\d+)?)\\s*$/;\n\n/** The value a draft writes, or the reason it can't be written yet. */\nexport function locationFromDraft(d: Draft, prev: unknown): { ok: true; value: Record<string, unknown> | null } | { ok: false; reason: string } {\n const lat = d.lat.trim();\n const lng = d.lng.trim();\n const label = d.label.trim();\n if (!lat && !lng && !label) return { ok: true, value: null };\n if (!lat || !lng) return { ok: false, reason: !lat && !lng ? \"Add a latitude and a longitude to save this location.\" : !lat ? \"Add the latitude too: a location needs both.\" : \"Add the longitude too: a location needs both.\" };\n const la = Number(lat);\n const lo = Number(lng);\n if (!Number.isFinite(la) || Math.abs(la) > 90) return { ok: false, reason: \"Latitude must be a number from −90 to 90.\" };\n if (!Number.isFinite(lo) || Math.abs(lo) > 180) return { ok: false, reason: \"Longitude must be a number from −180 to 180.\" };\n const out: Record<string, unknown> = { ...(isObj(prev) ? prev : {}), lat: la, lng: lo };\n if (label) out.label = label;\n else delete out.label;\n return { ok: true, value: out };\n}\n\nexport function LocationControl({ field, value, onChange, readOnly, id, invalid }: ControlProps) {\n const [draft, setDraft] = React.useState<Draft>(() => draftOf(value));\n const [seen, setSeen] = React.useState(value);\n const [mine, setMine] = React.useState<unknown>(undefined);\n // a value set from elsewhere (undo, another editor) replaces the draft; our own writes keep what the author typed\n if (value !== seen) {\n setSeen(value);\n if (value !== mine) setDraft(draftOf(value));\n }\n const result = locationFromDraft(draft, value);\n const reasonId = `${id}-reason`;\n\n const update = (patch: Partial<Draft>) => {\n const next = { ...draft, ...patch };\n setDraft(next);\n const r = locationFromDraft(next, value);\n if (!r.ok) return;\n const same = JSON.stringify(r.value) === JSON.stringify(value ?? null);\n if (same) return;\n setMine(r.value);\n onChange(r.value);\n };\n const coordinate = (key: \"lat\" | \"lng\") => (e: React.ChangeEvent<HTMLInputElement>) => {\n const pair = PAIR.exec(e.target.value);\n update(pair ? { lat: pair[1], lng: pair[2] } : ({ [key]: e.target.value } as Partial<Draft>));\n };\n\n const shown = isObj(value) && typeof value.lat === \"number\" && typeof value.lng === \"number\" ? { lat: value.lat, lng: value.lng } : null;\n return (\n <div className={cn(\"overflow-hidden rounded-kit-lg border border-kit-line\", invalid && \"border-[var(--kit-field-invalid)]\")}>\n <div className=\"relative h-28 overflow-hidden bg-kit-subtle\">\n {/* V2 `.grid-lines` (styles/index.css:1026), inlined: the dash has no such utility */}\n <div\n className=\"absolute inset-0 opacity-80\"\n style={{ backgroundImage: \"linear-gradient(var(--kit-line-subtle) 1px, transparent 1px), linear-gradient(90deg, var(--kit-line-subtle) 1px, transparent 1px)\", backgroundSize: \"24px 24px\" }}\n />\n <svg className=\"absolute inset-0 size-full text-kit-line-strong\" viewBox=\"0 0 400 112\" preserveAspectRatio=\"none\" aria-hidden>\n <path d=\"M0 76 C80 60 120 100 200 78 S320 40 400 60\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"6\" />\n <path d=\"M140 0 L170 112\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"3\" />\n </svg>\n {shown ? (\n <>\n <div className=\"absolute left-1/2 top-1/2 flex -translate-x-1/2 -translate-y-full flex-col items-center\" aria-hidden>\n <span className=\"flex size-7 items-center justify-center rounded-full bg-kit-inverse text-kit-fg-inverse shadow-kit-md\">\n <IconMapPin className=\"size-4\" stroke={1.5} />\n </span>\n <span className=\"size-2 -translate-y-0.5 rotate-45 bg-kit-inverse\" />\n </div>\n <a\n href={`https://www.openstreetmap.org/?mlat=${shown.lat}&mlon=${shown.lng}#map=15/${shown.lat}/${shown.lng}`}\n target=\"_blank\"\n rel=\"noreferrer noopener\"\n className={cn(\"tabular-nums absolute bottom-2 right-2 rounded-kit-md bg-kit-surface/90 px-1.5 py-0.5 font-kit-mono text-kit-2xs text-kit-fg-2 shadow-kit-xs\", \"flex items-center gap-1 hover:text-kit-fg\")}\n aria-label={`Open ${shown.lat.toFixed(4)}, ${shown.lng.toFixed(4)} on OpenStreetMap (new tab)`}\n >\n {shown.lat.toFixed(4)}, {shown.lng.toFixed(4)}\n <IconExternalLink className=\"size-3\" stroke={1.5} aria-hidden />\n </a>\n </>\n ) : (\n <p className=\"absolute inset-0 flex items-center justify-center text-kit-xs text-kit-fg-3\">No coordinates yet</p>\n )}\n </div>\n <div className=\"flex flex-col gap-2 p-2\">\n <Input\n id={id}\n value={draft.label}\n readOnly={readOnly}\n placeholder=\"Label, e.g. Head office\"\n aria-label={`${field.title} label`}\n leading={<IconMapPin stroke={1.5} />}\n onChange={(e) => update({ label: e.target.value })}\n className={TEXT_FOCUS}\n />\n <div className=\"grid grid-cols-2 gap-2\">\n <Input\n id={`${id}-lat`}\n size=\"sm\"\n inputMode=\"decimal\"\n value={draft.lat}\n readOnly={readOnly}\n aria-label={`${field.title} latitude`}\n aria-invalid={(!result.ok && /latitude/i.test(result.reason)) || undefined}\n aria-describedby={!result.ok ? reasonId : undefined}\n placeholder=\"−90 to 90\"\n onChange={coordinate(\"lat\")}\n leading={<span className=\"text-kit-2xs\">Lat</span>}\n />\n <Input\n id={`${id}-lng`}\n size=\"sm\"\n inputMode=\"decimal\"\n value={draft.lng}\n readOnly={readOnly}\n aria-label={`${field.title} longitude`}\n aria-invalid={(!result.ok && /longitude/i.test(result.reason)) || undefined}\n aria-describedby={!result.ok ? reasonId : undefined}\n placeholder=\"−180 to 180\"\n onChange={coordinate(\"lng\")}\n leading={<span className=\"text-kit-2xs\">Long</span>}\n />\n </div>\n {!result.ok ? (\n <p id={reasonId} role=\"status\" className=\"text-kit-xs text-kit-warning-fg\">\n {result.reason}\n </p>\n ) : null}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"location.js","names":[],"sources":["../../src/controls/location.tsx"],"sourcesContent":["/**\n * LOCATION — the geopoint field (DESIGN 8.21), V2 GeoControl fitted to what prod stores.\n *\n * Value (backend reference-validation `location`, the classic LocationField): `{ lat, lng, label? }`, lat −90…90 and\n * lng −180…180, finite numbers; null when empty. Keys the stored object carries beyond those are kept.\n *\n * A coordinate is written only when it is whole: both numbers, in range. Until then the field says what is missing\n * (\"Add the longitude too\") and keeps the stored value, because the backend refuses half a coordinate. Emptying all\n * three boxes clears the value. Pasting \"44.0582, -121.3153\" into either box fills both.\n *\n * The card above the inputs shows the pin and the coordinates, with a link that opens them on OpenStreetMap; it\n * draws no map tiles (no vendor, key or bundle).\n */\nimport * as React from \"react\";\nimport { IconMapPin } from \"@tabler/icons-react\";\nimport { cn } from \"../lib/cn\";\nimport { isObj } from \"../lib/path\";\nimport type { ControlProps } from \"../types\";\nimport { Input } from \"../ui/input\";\nimport { TEXT_FOCUS } from \"./text\";\n\ninterface Draft {\n lat: string;\n lng: string;\n label: string;\n}\nconst num = (v: unknown) => (typeof v === \"number\" && Number.isFinite(v) ? String(v) : \"\");\nconst draftOf = (v: unknown): Draft => (isObj(v) ? { lat: num(v.lat), lng: num(v.lng), label: typeof v.label === \"string\" ? v.label : \"\" } : { lat: \"\", lng: \"\", label: \"\" });\nconst PAIR = /^\\s*(-?\\d+(?:\\.\\d+)?)\\s*[,;\\s]\\s*(-?\\d+(?:\\.\\d+)?)\\s*$/;\n\n/** The value a draft writes, or the reason it can't be written yet. */\nexport function locationFromDraft(d: Draft, prev: unknown): { ok: true; value: Record<string, unknown> | null } | { ok: false; reason: string } {\n const lat = d.lat.trim();\n const lng = d.lng.trim();\n const label = d.label.trim();\n if (!lat && !lng && !label) return { ok: true, value: null };\n if (!lat || !lng) return { ok: false, reason: !lat && !lng ? \"Add a latitude and a longitude to save this location.\" : !lat ? \"Add the latitude too: a location needs both.\" : \"Add the longitude too: a location needs both.\" };\n const la = Number(lat);\n const lo = Number(lng);\n if (!Number.isFinite(la) || Math.abs(la) > 90) return { ok: false, reason: \"Latitude must be a number from −90 to 90.\" };\n if (!Number.isFinite(lo) || Math.abs(lo) > 180) return { ok: false, reason: \"Longitude must be a number from −180 to 180.\" };\n const out: Record<string, unknown> = { ...(isObj(prev) ? prev : {}), lat: la, lng: lo };\n if (label) out.label = label;\n else delete out.label;\n return { ok: true, value: out };\n}\n\nexport function LocationControl({ field, value, onChange, readOnly, id, invalid }: ControlProps) {\n const [draft, setDraft] = React.useState<Draft>(() => draftOf(value));\n const [seen, setSeen] = React.useState(value);\n const [mine, setMine] = React.useState<unknown>(undefined);\n // a value set from elsewhere (undo, another editor) replaces the draft; our own writes keep what the author typed\n if (value !== seen) {\n setSeen(value);\n if (value !== mine) setDraft(draftOf(value));\n }\n const result = locationFromDraft(draft, value);\n const reasonId = `${id}-reason`;\n\n const update = (patch: Partial<Draft>) => {\n const next = { ...draft, ...patch };\n setDraft(next);\n const r = locationFromDraft(next, value);\n if (!r.ok) return;\n const same = JSON.stringify(r.value) === JSON.stringify(value ?? null);\n if (same) return;\n setMine(r.value);\n onChange(r.value);\n };\n const coordinate = (key: \"lat\" | \"lng\") => (e: React.ChangeEvent<HTMLInputElement>) => {\n const pair = PAIR.exec(e.target.value);\n update(pair ? { lat: pair[1], lng: pair[2] } : ({ [key]: e.target.value } as Partial<Draft>));\n };\n\n const shown = isObj(value) && typeof value.lat === \"number\" && typeof value.lng === \"number\" ? { lat: value.lat, lng: value.lng } : null;\n return (\n <div className={cn(\"overflow-hidden rounded-kit-lg border border-kit-line\", invalid && \"border-[var(--kit-field-invalid)]\")}>\n <div className=\"relative h-28 overflow-hidden bg-kit-subtle\">\n {/* V2 `.grid-lines` (styles/index.css:1026), inlined: the dash has no such utility */}\n <div\n className=\"absolute inset-0 opacity-80\"\n style={{ backgroundImage: \"linear-gradient(var(--kit-line-subtle) 1px, transparent 1px), linear-gradient(90deg, var(--kit-line-subtle) 1px, transparent 1px)\", backgroundSize: \"24px 24px\" }}\n />\n <svg className=\"absolute inset-0 size-full text-kit-line-strong\" viewBox=\"0 0 400 112\" preserveAspectRatio=\"none\" aria-hidden>\n <path d=\"M0 76 C80 60 120 100 200 78 S320 40 400 60\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"6\" />\n <path d=\"M140 0 L170 112\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"3\" />\n </svg>\n {shown ? (\n <>\n <div className=\"absolute left-1/2 top-1/2 flex -translate-x-1/2 -translate-y-full flex-col items-center\" aria-hidden>\n <span className=\"flex size-7 items-center justify-center rounded-full bg-kit-inverse text-kit-fg-inverse shadow-kit-md\">\n <IconMapPin className=\"size-4\" stroke={1.5} />\n </span>\n <span className=\"size-2 -translate-y-0.5 rotate-45 bg-kit-inverse\" />\n </div>\n <a\n href={`https://www.openstreetmap.org/?mlat=${shown.lat}&mlon=${shown.lng}#map=15/${shown.lat}/${shown.lng}`}\n target=\"_blank\"\n rel=\"noreferrer noopener\"\n // D4: V2 (misc.tsx:195) draws the coordinates as a plain chip, no icon; the kit keeps the map link (its\n // accessible name says where it goes) and draws exactly V2's chip\n className=\"tabular-nums absolute bottom-2 right-2 rounded-kit-md bg-kit-surface/90 px-1.5 py-0.5 font-kit-mono text-kit-2xs text-kit-fg-2 shadow-kit-xs hover:text-kit-fg\"\n aria-label={`Open ${shown.lat.toFixed(4)}, ${shown.lng.toFixed(4)} on OpenStreetMap (new tab)`}\n >\n {shown.lat.toFixed(4)}, {shown.lng.toFixed(4)}\n </a>\n </>\n ) : (\n <p className=\"absolute inset-0 flex items-center justify-center text-kit-xs text-kit-fg-3\">No coordinates yet</p>\n )}\n </div>\n <div className=\"flex flex-col gap-2 p-2\">\n <Input\n id={id}\n value={draft.label}\n readOnly={readOnly}\n placeholder=\"Label, e.g. Head office\"\n aria-label={`${field.title} label`}\n leading={<IconMapPin stroke={1.5} />}\n onChange={(e) => update({ label: e.target.value })}\n className={TEXT_FOCUS}\n />\n <div className=\"grid grid-cols-2 gap-2\">\n <Input\n id={`${id}-lat`}\n size=\"sm\"\n inputMode=\"decimal\"\n value={draft.lat}\n readOnly={readOnly}\n aria-label={`${field.title} latitude`}\n aria-invalid={(!result.ok && /latitude/i.test(result.reason)) || undefined}\n aria-describedby={!result.ok ? reasonId : undefined}\n placeholder=\"−90 to 90\"\n onChange={coordinate(\"lat\")}\n leading={<span className=\"text-kit-2xs\">Lat</span>}\n />\n <Input\n id={`${id}-lng`}\n size=\"sm\"\n inputMode=\"decimal\"\n value={draft.lng}\n readOnly={readOnly}\n aria-label={`${field.title} longitude`}\n aria-invalid={(!result.ok && /longitude/i.test(result.reason)) || undefined}\n aria-describedby={!result.ok ? reasonId : undefined}\n placeholder=\"−180 to 180\"\n onChange={coordinate(\"lng\")}\n leading={<span className=\"text-kit-2xs\">Long</span>}\n />\n </div>\n {!result.ok ? (\n <p id={reasonId} role=\"status\" className=\"text-kit-xs text-kit-warning-fg\">\n {result.reason}\n </p>\n ) : null}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
package/dist/controls/media.d.ts
CHANGED
|
@@ -59,20 +59,6 @@ export declare function useUpload(accept: string, kind: string, onDone: (a: KitM
|
|
|
59
59
|
setError: React.Dispatch<React.SetStateAction<string | null>>;
|
|
60
60
|
run: ((files: File[]) => void) | undefined;
|
|
61
61
|
};
|
|
62
|
-
/**
|
|
63
|
-
* How a preview frames a picture of any shape: the frame follows the picture's ratio between square and 2:1, and the
|
|
64
|
-
* picture fits inside it WHOLE, filling the frame's width or height (a portrait is boxed at the sides, a panorama above
|
|
65
|
-
* and below, on the checker). With no known size (still loading, or an SVG with no intrinsic size) the frame is 16:9
|
|
66
|
-
* and the picture is contained. Before 2026-10-16 every picture was cropped to a fixed 16:9 (`object-cover`).
|
|
67
|
-
*/
|
|
68
|
-
export declare function frameFit(size: {
|
|
69
|
-
w: number;
|
|
70
|
-
h: number;
|
|
71
|
-
} | undefined): {
|
|
72
|
-
frame: number;
|
|
73
|
-
image: number | null;
|
|
74
|
-
fill: "width" | "height" | "contain";
|
|
75
|
-
};
|
|
76
62
|
declare const CUSTOM_CROP = "custom";
|
|
77
63
|
/** The crop a value shows: its preset, a stored rect read against the picture's size, or a rect that is none of them. */
|
|
78
64
|
export declare function cropChoice(crop: ImageValue["crop"], size?: {
|