@aglyn/besigner-ui 1.0.0-beta.172 → 1.0.0-beta.174

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.
Files changed (56) hide show
  1. package/package.json +17 -17
  2. package/src/besigner.d.ts +2 -0
  3. package/src/besigner.js +3 -0
  4. package/src/besigner.js.map +1 -1
  5. package/src/lib/components/aside-panel.component.js +85 -3
  6. package/src/lib/components/aside-panel.component.js.map +1 -1
  7. package/src/lib/components/custom-css-form.component.d.ts +5 -0
  8. package/src/lib/components/custom-css-form.component.js +6 -3
  9. package/src/lib/components/custom-css-form.component.js.map +1 -1
  10. package/src/lib/components/dnd/draggable-droppable.js +26 -3
  11. package/src/lib/components/dnd/draggable-droppable.js.map +1 -1
  12. package/src/lib/components/element-preview.component.js +28 -19
  13. package/src/lib/components/element-preview.component.js.map +1 -1
  14. package/src/lib/components/element-props-form.component.d.ts +11 -1
  15. package/src/lib/components/element-props-form.component.js +58 -9
  16. package/src/lib/components/element-props-form.component.js.map +1 -1
  17. package/src/lib/components/element-styles-form.component.d.ts +7 -22
  18. package/src/lib/components/element-styles-form.component.js +250 -80
  19. package/src/lib/components/element-styles-form.component.js.map +1 -1
  20. package/src/lib/components/instance-attr-overrides.component.js +180 -124
  21. package/src/lib/components/instance-attr-overrides.component.js.map +1 -1
  22. package/src/lib/components/layout-style-picker.component.d.ts +43 -0
  23. package/src/lib/components/layout-style-picker.component.js +159 -0
  24. package/src/lib/components/layout-style-picker.component.js.map +1 -0
  25. package/src/lib/components/node-leaf.js +14 -4
  26. package/src/lib/components/node-leaf.js.map +1 -1
  27. package/src/lib/components/placement-parts-header.component.d.ts +52 -0
  28. package/src/lib/components/placement-parts-header.component.js +178 -0
  29. package/src/lib/components/placement-parts-header.component.js.map +1 -0
  30. package/src/lib/components/viewport-frame.component.d.ts +2 -1
  31. package/src/lib/components/viewport-frame.component.js +31 -16
  32. package/src/lib/components/viewport-frame.component.js.map +1 -1
  33. package/src/lib/contexts/layout-chrome-context.d.ts +7 -0
  34. package/src/lib/contexts/layout-chrome-context.js +6 -4
  35. package/src/lib/contexts/layout-chrome-context.js.map +1 -1
  36. package/src/lib/contexts/layout-style-selection.d.ts +52 -0
  37. package/src/lib/contexts/layout-style-selection.js +51 -0
  38. package/src/lib/contexts/layout-style-selection.js.map +1 -0
  39. package/src/lib/utils/attr-target.d.ts +25 -3
  40. package/src/lib/utils/attr-target.js +39 -11
  41. package/src/lib/utils/attr-target.js.map +1 -1
  42. package/src/lib/utils/docs-help.generated.d.ts +3 -3
  43. package/src/lib/utils/docs-help.generated.js +3 -0
  44. package/src/lib/utils/docs-help.generated.js.map +1 -1
  45. package/src/lib/utils/instance-leaf-hit.d.ts +23 -1
  46. package/src/lib/utils/instance-leaf-hit.js +37 -3
  47. package/src/lib/utils/instance-leaf-hit.js.map +1 -1
  48. package/src/lib/utils/placement-override-copy.d.ts +135 -0
  49. package/src/lib/utils/placement-override-copy.js +314 -0
  50. package/src/lib/utils/placement-override-copy.js.map +1 -0
  51. package/src/lib/utils/placement-part-pick.d.ts +52 -0
  52. package/src/lib/utils/placement-part-pick.js +47 -0
  53. package/src/lib/utils/placement-part-pick.js.map +1 -0
  54. package/src/lib/utils/style-target.d.ts +73 -2
  55. package/src/lib/utils/style-target.js +100 -6
  56. package/src/lib/utils/style-target.js.map +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../../libs/besigner/feature/designer/src/lib/components/custom-css-form.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type * as Aglyn from '@aglyn/aglyn'\nimport {\n canvas,\n collectThirdPartyAuthorCssUrlHosts,\n collectThirdPartyAuthorSxUrlHosts,\n} from '@aglyn/aglyn'\nimport { mdiClose, mdiPlus } from '@aglyn/shared-data-mdi'\nimport {\n canonicalSxProperties,\n expandSxAliases,\n isAtomicSxValue,\n} from '@aglyn/shared-data-enums'\nimport { MdiIcon } from '@aglyn/shared-ui-jsx'\nimport { FIELD_MUTED_STYLES, FieldMuteButton } from '@aglyn/shared-ui-jsx-forms'\nimport {\n Autocomplete,\n Button,\n IconButton,\n Stack,\n TextField,\n ToggleButton,\n ToggleButtonGroup,\n Typography,\n} from '@mui/material'\nimport { observer } from 'mobx-react-lite'\nimport { useCallback, useEffect, useMemo, useState } from 'react'\nimport useAglynBesignerFlag from '../hooks/use-aglyn-besigner-flag'\nimport { buildStyleMute, toggleMutedStyle } from '../utils/muted-styles'\nimport type { SxBreakpoint } from '../utils/responsive-sx'\nimport { readSxValue } from '../utils/responsive-sx'\nimport { applyStylePartialToSx } from '../utils/style-field-groups'\nimport { getNodeStyleTarget } from '../utils/style-target'\n\n/**\n * Curated, grouped suggestions for the builder's property picker\n * (freeSolo, so any CSS/sx property still works).\n */\nconst CSS_PROPERTY_SUGGESTIONS: Array<{ group: string; name: string }> = [\n ...[\n 'width',\n 'height',\n 'minWidth',\n 'maxWidth',\n 'minHeight',\n 'maxHeight',\n 'overflow',\n 'position',\n 'top',\n 'right',\n 'bottom',\n 'left',\n 'zIndex',\n ].map((name) => ({ group: 'Layout', name })),\n ...['margin', 'padding', 'gap', 'rowGap', 'columnGap'].map((name) => ({\n group: 'Spacing',\n name,\n })),\n ...[\n 'fontSize',\n 'fontWeight',\n 'fontFamily',\n 'lineHeight',\n 'letterSpacing',\n 'textTransform',\n 'textDecoration',\n 'whiteSpace',\n ].map((name) => ({ group: 'Typography', name })),\n ...[\n 'color',\n 'backgroundColor',\n 'background',\n 'backgroundImage',\n 'backgroundSize',\n 'backgroundPosition',\n 'opacity',\n ].map((name) => ({ group: 'Background & color', name })),\n ...[\n 'border',\n 'borderRadius',\n 'borderColor',\n 'borderWidth',\n 'borderStyle',\n 'outline',\n ].map((name) => ({ group: 'Border', name })),\n ...[\n 'boxShadow',\n 'transform',\n 'transition',\n 'filter',\n 'cursor',\n 'objectFit',\n 'aspectRatio',\n ].map((name) => ({ group: 'Effects', name })),\n]\n\nconst kebabToCamel = (name: string) =>\n name.replace(/-([a-z])/g, (match, letter: string) => letter.toUpperCase())\nconst camelToKebab = (name: string) =>\n name.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)\n\n/** Parses `prop: value;` CSS declarations into a JSS record (AGL-332). */\nexport function parseCssDeclarations(css: string): Record<string, string> {\n const result: Record<string, string> = {}\n for (const raw of css.split(';')) {\n const declaration = raw.trim()\n if (!declaration) continue\n const colon = declaration.indexOf(':')\n if (colon <= 0) continue\n const property = kebabToCamel(declaration.slice(0, colon).trim())\n const value = declaration.slice(colon + 1).trim()\n if (property && value) result[property] = value\n }\n return result\n}\n\n/**\n * The scalar declarations the Builder rows and the CSS tab show for `sx`,\n * named in the Styles panel's own spelling (AGL-2390).\n *\n * MUI honours two names for the same declaration, and every other control\n * in this panel is named for the CSS longhand (AGL-2207). Listing the raw\n * keys made this form the one place that said `bgcolor` where the Colors\n * group said `backgroundColor` and BoxStyler said `paddingTop` — the same\n * node, two vocabularies, and the author with no way to tell that the two\n * rows were one declaration. The expansion is a pure renaming of what\n * already renders (order preserved, non-atomic values left alone), and it\n * is what makes the row's × and the CSS tab's clear reach the key that is\n * actually painting.\n *\n * The JSS tab deliberately does NOT go through here: it edits the stored\n * document verbatim, which is the escape hatch for everything the named\n * controls cannot express.\n */\nexport function customCssDeclarations(\n sx: Record<string, any> | undefined,\n breakpoint: SxBreakpoint | null,\n): Array<{ property: string; value: string }> {\n const source = expandSxAliases((sx ?? {}) as Record<string, any>, {\n deep: true,\n })\n const out: Array<{ property: string; value: string }> = []\n for (const property of Object.keys(source)) {\n const value = readSxValue(source, property, breakpoint)\n if (value === undefined || value === null) continue\n if (typeof value === 'object') continue // selectors/nested — JSON tab\n out.push({ property, value: String(value) })\n }\n return out\n}\n\n/** Serializes scalar sx entries into CSS declarations for the CSS tab. */\nexport function serializeCssDeclarations(\n sx: Record<string, any> | undefined,\n breakpoint: SxBreakpoint | null,\n): string {\n return customCssDeclarations(sx, breakpoint)\n .map(({ property, value }) => `${camelToKebab(property)}: ${value};`)\n .join('\\n')\n}\n\n/**\n * The declaration(s) one named property writes, in the panel's spelling.\n *\n * The property field is free-solo and the CSS tab takes any declaration, so\n * an author can type `bgcolor` or `py` at any time — this form is the one\n * seam in the product where a fresh alias can still enter a document. It\n * resolves the name the same way {@link expandSxAliases} resolves a stored\n * one, so what the author typed and what the panel reads back agree.\n *\n * A value the expansion cannot carry per-side — `p: '10px 20px'`, the CSS\n * shorthand's own syntax — keeps the name the author gave it rather than\n * being copied onto four longhands CSS would drop. Clearing always expands,\n * because a cleared alias has to take every longhand it was painting with\n * it.\n */\nexport function canonicalCssPartial(\n property: string,\n value: string | undefined,\n): Record<string, unknown> {\n const canonical = canonicalSxProperties(property)\n const expandable =\n canonical.length === 1 || value === undefined || isAtomicSxValue(value)\n if (!canonical.length || !expandable) return { [property]: value }\n return Object.fromEntries(canonical.map((name) => [name, value]))\n}\n\n/**\n * Applies custom-CSS edits to `sx` — the write both the Builder rows and\n * the CSS tab's Apply go through (AGL-2390).\n *\n * Routed through the Styles panel's own write seam so this form cannot\n * drift from it again: {@link applyStylePartialToSx} expands the alias keys\n * the edit COLLIDES with before writing, which is what makes clearing a\n * declaration actually clear (deleting `backgroundColor` while a `bgcolor`\n * underneath keeps painting is AGL-2207's sharper failure) and what stops a\n * write leaving two spellings of one declaration in the record.\n *\n * Scheme-agnostic (`null`): this form has always written the base slice,\n * and the dark-preview scoping belongs to the named color fields.\n */\nexport function applyCustomCssEdits(\n sx: Record<string, any> | undefined,\n edits: Record<string, string | undefined>,\n breakpoint: SxBreakpoint | null,\n): Record<string, any> {\n const partial: Record<string, unknown> = {}\n for (const [property, value] of Object.entries(edits)) {\n if (!property) continue\n Object.assign(\n partial,\n canonicalCssPartial(property, value === '' ? undefined : value),\n )\n }\n return applyStylePartialToSx(sx, partial, breakpoint, null)\n}\n\n/**\n * Un-applied CSS / JSS text, held ACROSS an unmount of this form (AGL-2486).\n *\n * The JSS tab is the one control in the Styles panel whose work lives only\n * in React state until Apply — and this form is mounted under\n * `withLastSelectedNode`, which renders \"Select an element\" the instant the\n * canvas selection empties. Anything that clears the selection therefore\n * UNMOUNTS the editor, and a `useState('')` buffer takes the author's\n * half-typed sx document with it. Measured on a live besigner: a selection\n * held with no keyboard or pointer input at all lost the editor 46s in, so\n * this is not something the author can avoid by typing carefully — it is\n * why the wipe reads as random, and why it gets blamed on whichever key was\n * pressed last.\n *\n * Keyed by node AND style target, so a component instance's root and its\n * leaves each keep their own pending text, and re-selecting the element\n * hands the author back exactly what they had typed. An entry lives only\n * while the text DIFFERS from the stored document: applying it, or typing\n * it back to what sx already says, drops the entry, so this never resurrects\n * a draft the author has already resolved.\n *\n * Deliberately not persisted beyond the page: it is un-applied text, not a\n * document, and the crash-recovery draft store is the thing that owns\n * surviving a reload.\n */\nconst pendingDrafts = new Map<string, string>()\n\nconst draftKey = (\n nodeId: string,\n overrideKey: string,\n mode: 'css' | 'json',\n): string => [nodeId, overrideKey, mode].join('\\x00')\n\n/** Drops every pending draft — test seam, and a hard reset for the canvas. */\nexport function resetPendingCustomCssDrafts(): void {\n pendingDrafts.clear()\n}\n\n/**\n * Why a JSS buffer cannot be applied, or `null` when it is ready\n * (AGL-2486). Rendered live as the author types, so \"nothing happened when\n * I pressed Apply\" is never the first sign that the text does not parse.\n *\n * Mid-edit JSON is TRANSIENTLY invalid by nature — `{\"a\": 1,` is what every\n * new property looks like a keystroke before it is finished — so this is a\n * hint, never a block on typing and never a reason to touch the document.\n *\n * An empty buffer is called out rather than treated as `{}`: `JSON.parse`\n * would need a fallback to accept it at all, and silently reading \"\" as\n * \"delete every style on this element\" is the most destructive thing this\n * panel could do with an accidental Select-All. Clearing all styles stays\n * available, spelled `{}`, which nobody types by accident.\n */\nexport function jsonDraftProblem(text: string): string | null {\n const trimmed = (text ?? '').trim()\n if (!trimmed) {\n return 'Empty — type {} to clear every style on this element.'\n }\n let parsed: unknown\n try {\n parsed = JSON.parse(trimmed)\n } catch {\n return 'Not valid JSON yet — Apply will not change anything until it parses.'\n }\n // Explicit `=== null` and an array check: `strictNullChecks` is off\n // repo-wide, so `!parsed` would fold `null`, `0` and `\"\"` together, and\n // `typeof null` is `'object'`.\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n return 'The sx value must be a JSON object.'\n }\n return null\n}\n\nexport interface CustomCssFormProps {\n node?: Aglyn.NodeSchema\n /** Active artboard breakpoint; builder/CSS edits scope to it (AGL-333). */\n breakpoint: SxBreakpoint | null\n /**\n * Which `styleOverrides` slice to edit on a component instance\n * (AGL-1332) — the Styles panel's current style target. Ignored for a\n * plain node; defaults to the component root.\n */\n overrideKey?: string\n}\n\ntype BuilderRow = { property: string; value: string }\n\n/**\n * Custom CSS for the selected element's `sx` (AGL-332): a friendly\n * builder (property + value rows), a raw CSS mode, and a raw JSS/JSON\n * mode. Builder and CSS edits respect the active breakpoint scope; the\n * JSON mode edits the full sx document (including responsive objects and\n * nested selectors) verbatim.\n */\nexport const CustomCssForm = observer((props: CustomCssFormProps) => {\n const { node, breakpoint, overrideKey } = props\n // Same target the rest of the Styles panel writes (AGL-1306/1332): a\n // plain node's own sx, or the instance override slice the panel's style\n // target names — root, or one leaf inside the component.\n const target = useMemo(\n () => getNodeStyleTarget(node, overrideKey),\n [node, overrideKey],\n )\n const nodeSx = (target.sx ?? {}) as Record<string, any>\n const [mode, setMode] = useState<'builder' | 'css' | 'json'>('builder')\n const [cssDraft, setCssDraft] = useState('')\n const [jsonDraft, setJsonDraft] = useState('')\n const [error, setError] = useState<string | null>(null)\n\n const rows = useMemo<BuilderRow[]>(\n () => customCssDeclarations(nodeSx, breakpoint),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [JSON.stringify(nodeSx), breakpoint],\n )\n const [draftRow, setDraftRow] = useState<BuilderRow>({\n property: '',\n value: '',\n })\n\n // Declarations switched off for comparison (AGL-2486). Canvas only, and\n // the same flag every other row in the panel writes, so a property muted\n // here and the same property muted from its typed field are one state.\n // The builder lists BASE scalar declarations only — a state slice is a\n // nested object it skips — so these mutes carry no state scope.\n const [mutedStyles, setMutedStyles] = useAglynBesignerFlag('mutedStyles')\n const rowMute = useCallback(\n (property: string) =>\n buildStyleMute(property, property, {\n nodeId: node?.$id,\n state: null,\n breakpoint,\n // Every row the builder lists is, by construction, declared.\n scopeValues: { [property]: true },\n mutedStyles,\n onToggle: (target) =>\n setMutedStyles((current) => toggleMutedStyle(current, target)),\n }),\n [node, breakpoint, mutedStyles, setMutedStyles],\n )\n\n const nodeId = node?.$id ?? ''\n /** What the STORED document says, in each tab's spelling. */\n const storedCss = useMemo(\n () => serializeCssDeclarations(nodeSx, breakpoint),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [JSON.stringify(nodeSx), breakpoint],\n )\n const storedJson = useMemo(\n () => JSON.stringify(nodeSx, null, 2),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [JSON.stringify(nodeSx)],\n )\n\n /**\n * Records (or drops) the author's un-applied text for one tab, so an\n * unmount cannot take it (AGL-2486). Text equal to the stored document is\n * not a draft at all, so it clears the entry rather than pinning a value\n * that would then shadow a later change from elsewhere in the panel.\n */\n const rememberDraft = useCallback(\n (draftMode: 'css' | 'json', value: string, stored: string) => {\n const key = draftKey(nodeId, target.overrideKey, draftMode)\n if (value === stored) pendingDrafts.delete(key)\n else pendingDrafts.set(key, value)\n },\n [nodeId, target.overrideKey],\n )\n\n useEffect(() => {\n // A pending draft wins over the stored document — that is the whole\n // point of keeping it. `!== undefined` rather than a truthiness test:\n // `''` is a legitimate buffer the author emptied on purpose, and with\n // `strictNullChecks` off `if (!pending)` would silently discard it.\n const pendingCss = pendingDrafts.get(\n draftKey(nodeId, target.overrideKey, 'css'),\n )\n const pendingJson = pendingDrafts.get(\n draftKey(nodeId, target.overrideKey, 'json'),\n )\n setCssDraft(pendingCss !== undefined ? pendingCss : storedCss)\n setJsonDraft(pendingJson !== undefined ? pendingJson : storedJson)\n setError(null)\n // Re-seeded on the override target too (AGL-1332): switching from the\n // component root to a leaf must reload the drafts, or a stale CSS\n // buffer would be written back onto the newly picked target.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [mode, nodeId, target.overrideKey])\n\n /**\n * Live \"is this appliable yet\" verdict for the JSS tab (AGL-2486) — a\n * caption only. Nothing here writes, so a buffer that stops parsing\n * mid-property changes the hint and leaves the document alone.\n */\n const jsonProblem = useMemo(\n () => (mode === 'json' ? jsonDraftProblem(jsonDraft) : null),\n [mode, jsonDraft],\n )\n\n // Passive off-site url() hint (AGL-1737) — the \"warn\" half of AGL-1725's\n // warn-and-disclose, for the panel with no per-field description to carry\n // it. Names the hosts a url() in the CURRENT value would make every\n // visitor's browser contact: the live draft in the CSS and JSON modes\n // (ahead of Apply, so the author reads it while typing), the committed sx\n // in the builder (whose rows write live). A caption and nothing more — it\n // never blocks a value, and it deliberately does not touch how or when\n // this form commits (an attribute panel edit commits on blur, and that\n // behavior is load-bearing).\n const thirdPartyHosts = useMemo<string[]>(() => {\n if (mode === 'css') return collectThirdPartyAuthorCssUrlHosts(cssDraft)\n if (mode === 'json') {\n try {\n return collectThirdPartyAuthorSxUrlHosts(JSON.parse(jsonDraft || '{}'))\n } catch {\n // Mid-edit JSON — scan the raw text so the hint tracks typing.\n return collectThirdPartyAuthorCssUrlHosts(jsonDraft)\n }\n }\n return collectThirdPartyAuthorSxUrlHosts(nodeSx)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [mode, cssDraft, jsonDraft, JSON.stringify(nodeSx)])\n\n const writeProperty = useCallback(\n (property: string, value: string | undefined) => {\n if (!node || !property) return\n // Coalesced per property (AGL-1204): the builder rows write on every\n // keystroke, so typing one value is one undo step, and editing a\n // different property starts another. The write goes through the style\n // target (AGL-1306), so this is undoable on an instance's override\n // layer exactly as on a plain node's own sx.\n canvas.transact(\n () => {\n target.setSx(\n applyCustomCssEdits(\n (target.sx ?? {}) as Record<string, any>,\n { [property]: value },\n breakpoint,\n ),\n )\n },\n ['sx', node.$id, breakpoint ?? 'base', property].join(':'),\n )\n },\n [node, target, breakpoint],\n )\n\n const applyCss = useCallback(() => {\n if (!node) return\n const parsed = parseCssDeclarations(cssDraft)\n // Uncoalesced: pressing Apply is one deliberate commit, however many\n // declarations it carries (AGL-1204).\n canvas.transact(() => {\n // One edit map, applied once: declarations no longer in the draft\n // clear, the rest write. Both halves resolve their spelling through\n // `applyCustomCssEdits`, so a row the author deleted takes the alias\n // that was painting it with it, and a declaration they typed under\n // MUI's spelling lands where every other control can read it.\n const edits: Record<string, string | undefined> = {}\n for (const row of rows) {\n if (!(row.property in parsed)) edits[row.property] = undefined\n }\n Object.assign(edits, parsed)\n target.setSx(\n applyCustomCssEdits(\n (target.sx ?? {}) as Record<string, any>,\n edits,\n breakpoint,\n ),\n )\n })\n setError(null)\n // Applied text is no longer un-applied.\n pendingDrafts.delete(draftKey(nodeId, target.overrideKey, 'css'))\n }, [node, target, cssDraft, rows, breakpoint, nodeId])\n\n /**\n * Commits the JSS buffer — the ONLY path from this tab to the document\n * (AGL-2486).\n *\n * A buffer that does not parse to a JSON object is a NON-COMMIT: it sets\n * the inline error and returns, leaving both the document and the text\n * exactly as they are, so the author can keep editing from where they\n * were. There is no blur, change or unmount handler anywhere in this form\n * that reaches the document — Apply is the whole seam, which is what\n * makes a transiently invalid buffer harmless.\n */\n const applyJson = useCallback(() => {\n if (!node) return\n const problem = jsonDraftProblem(jsonDraft)\n if (problem !== null) {\n setError(problem)\n return\n }\n // Re-parsed rather than carried out of the guard: `jsonDraftProblem`\n // owns the verdict, and one function deciding while another commits is\n // how \"valid enough to pass, wrong shape to store\" gets in.\n const parsed = JSON.parse(jsonDraft.trim())\n // Replacing sx wholesale is the single most destructive edit this panel\n // offers, so it is the one that most needs to be undoable (AGL-1204).\n canvas.transact(() => {\n target.setSx(parsed)\n })\n setError(null)\n pendingDrafts.delete(draftKey(nodeId, target.overrideKey, 'json'))\n }, [node, target, jsonDraft, nodeId])\n\n return (\n <Stack spacing={1.5}>\n <ToggleButtonGroup\n size=\"small\"\n exclusive\n fullWidth\n value={mode}\n onChange={(event, value) => value && setMode(value)}\n >\n <ToggleButton value=\"builder\">{'Builder'}</ToggleButton>\n <ToggleButton value=\"css\">{'CSS'}</ToggleButton>\n <ToggleButton value=\"json\">{'JSS (sx)'}</ToggleButton>\n </ToggleButtonGroup>\n\n {mode === 'builder' ? (\n <Stack spacing={1}>\n {rows.map((row) => {\n const mute = rowMute(row.property)\n return (\n <Stack\n key={row.property}\n direction=\"row\"\n spacing={1}\n sx={[\n { alignItems: 'center' },\n mute?.muted ? FIELD_MUTED_STYLES : null,\n ]}\n >\n <TextField\n size=\"small\"\n value={row.property}\n disabled\n sx={{ flex: 1 }}\n />\n <TextField\n size=\"small\"\n value={row.value}\n onChange={(event) =>\n writeProperty(row.property, event.target.value)\n }\n sx={{ flex: 1 }}\n />\n {/* The eye stops the declaration applying and keeps its\n value; the ✕ beside it is still the only thing that\n takes the declaration off the element. */}\n <FieldMuteButton mute={mute} />\n <IconButton\n size=\"small\"\n aria-label={`Remove ${row.property}`}\n onClick={() => writeProperty(row.property, undefined)}\n >\n <MdiIcon path={mdiClose.path} size={0.7} />\n </IconButton>\n </Stack>\n )\n })}\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Autocomplete\n freeSolo\n size=\"small\"\n options={CSS_PROPERTY_SUGGESTIONS}\n groupBy={(option) =>\n typeof option === 'string' ? '' : option.group\n }\n getOptionLabel={(option) =>\n typeof option === 'string' ? option : option.name\n }\n inputValue={draftRow.property}\n onInputChange={(event, value) =>\n setDraftRow((prev) => ({ ...prev, property: value }))\n }\n renderInput={(params) => (\n <TextField {...params} placeholder=\"property\" />\n )}\n sx={{ flex: 1 }}\n />\n <TextField\n size=\"small\"\n placeholder=\"value\"\n value={draftRow.value}\n onChange={(event) =>\n setDraftRow((prev) => ({ ...prev, value: event.target.value }))\n }\n sx={{ flex: 1 }}\n />\n <IconButton\n size=\"small\"\n aria-label=\"Add property\"\n disabled={!draftRow.property.trim() || !draftRow.value.trim()}\n onClick={() => {\n writeProperty(\n kebabToCamel(draftRow.property.trim()),\n draftRow.value.trim(),\n )\n setDraftRow({ property: '', value: '' })\n }}\n >\n <MdiIcon path={mdiPlus.path} size={0.8} />\n </IconButton>\n </Stack>\n </Stack>\n ) : null}\n\n {mode === 'css' ? (\n <Stack spacing={1}>\n <TextField\n multiline\n minRows={5}\n value={cssDraft}\n onChange={(event) => {\n setCssDraft(event.target.value)\n rememberDraft('css', event.target.value, storedCss)\n }}\n placeholder={\n 'border-radius: 8px;\\nbox-shadow: 0 2px 8px rgba(0,0,0,0.2);'\n }\n slotProps={{\n htmlInput: {\n style: { fontFamily: 'monospace', fontSize: 12 },\n },\n }}\n />\n <Button size=\"small\" variant=\"outlined\" onClick={applyCss}>\n {'Apply CSS'}\n </Button>\n </Stack>\n ) : null}\n\n {mode === 'json' ? (\n <Stack spacing={1}>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {(target.isComposed\n ? 'The effective sx — supports responsive objects '\n : 'Full sx document — supports responsive objects ') +\n '({ xs, sm, … }) and nested selectors (\"&:hover\").'}\n </Typography>\n {target.isComposed ? (\n // The panel has to say this out loud (AGL-3218). Everything\n // below is correct and it is also not what the editor looks\n // like it is doing, and the author only finds out by reading\n // the stored node somewhere else — where the edit reads as\n // missing.\n <Typography variant=\"caption\" color=\"text.secondary\">\n {'This element stores styles in two places: a props.sx its ' +\n 'component or preset authored, and its own sx. The box ' +\n 'shows both merged, the way the page renders them. Apply ' +\n 'writes only what you changed into the element’s own sx ' +\n 'and leaves the rest where it is, so a property you edit ' +\n 'here will not appear in props.sx.'}\n </Typography>\n ) : null}\n <TextField\n multiline\n minRows={6}\n value={jsonDraft}\n onChange={(event) => {\n setJsonDraft(event.target.value)\n rememberDraft('json', event.target.value, storedJson)\n // A keystroke that fixes the JSON clears a stale Apply error;\n // it never raises one, so typing is not nagged mid-property.\n if (error && jsonDraftProblem(event.target.value) === null) {\n setError(null)\n }\n }}\n error={Boolean(error)}\n helperText={error ?? jsonProblem ?? undefined}\n slotProps={{\n htmlInput: {\n style: { fontFamily: 'monospace', fontSize: 12 },\n },\n }}\n />\n <Button size=\"small\" variant=\"outlined\" onClick={applyJson}>\n {'Apply JSS'}\n </Button>\n </Stack>\n ) : null}\n\n {thirdPartyHosts.length ? (\n // Same voice as the Custom HTML `css` description (AGL-1737), with\n // the host named back so the consequence is attached to the value\n // the author typed rather than left as general prose.\n <Typography variant=\"caption\" color=\"text.secondary\">\n {'A url() here points off your site — every visitor’s browser ' +\n `will contact ${thirdPartyHosts.join(', ')}, disclosing their ` +\n 'IP address and which page they are on. Insecure http:// URLs ' +\n 'are not loaded.'}\n </Typography>\n ) : null}\n </Stack>\n )\n})\nCustomCssForm.displayName = 'CustomCssForm'\n\nexport default CustomCssForm\n"],"names":["canvas","collectThirdPartyAuthorCssUrlHosts","collectThirdPartyAuthorSxUrlHosts","mdiClose","mdiPlus","canonicalSxProperties","expandSxAliases","isAtomicSxValue","MdiIcon","FIELD_MUTED_STYLES","FieldMuteButton","Autocomplete","Button","IconButton","Stack","TextField","ToggleButton","ToggleButtonGroup","Typography","observer","useCallback","useEffect","useMemo","useState","useAglynBesignerFlag","buildStyleMute","toggleMutedStyle","readSxValue","applyStylePartialToSx","getNodeStyleTarget","CSS_PROPERTY_SUGGESTIONS","map","name","group","kebabToCamel","replace","match","letter","toUpperCase","camelToKebab","toLowerCase","parseCssDeclarations","css","result","raw","split","declaration","trim","colon","indexOf","property","slice","value","customCssDeclarations","sx","breakpoint","source","deep","out","Object","keys","undefined","push","String","serializeCssDeclarations","join","canonicalCssPartial","canonical","expandable","length","fromEntries","applyCustomCssEdits","edits","partial","entries","assign","pendingDrafts","Map","draftKey","nodeId","overrideKey","mode","resetPendingCustomCssDrafts","clear","jsonDraftProblem","text","trimmed","parsed","JSON","parse","Array","isArray","CustomCssForm","props","target","error","node","nodeSx","setMode","cssDraft","setCssDraft","jsonDraft","setJsonDraft","setError","rows","stringify","draftRow","setDraftRow","mutedStyles","setMutedStyles","rowMute","$id","state","scopeValues","onToggle","current","storedCss","storedJson","rememberDraft","draftMode","stored","key","delete","set","pendingCss","get","pendingJson","jsonProblem","thirdPartyHosts","writeProperty","transact","setSx","applyCss","row","applyJson","problem","spacing","size","exclusive","fullWidth","onChange","event","mute","direction","alignItems","muted","disabled","flex","aria-label","onClick","path","freeSolo","options","groupBy","option","getOptionLabel","inputValue","onInputChange","prev","renderInput","params","placeholder","multiline","minRows","slotProps","htmlInput","style","fontFamily","fontSize","variant","color","isComposed","Boolean","helperText","displayName"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../../libs/besigner/feature/designer/src/lib/components/custom-css-form.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type * as Aglyn from '@aglyn/aglyn'\nimport {\n canvas,\n collectThirdPartyAuthorCssUrlHosts,\n collectThirdPartyAuthorSxUrlHosts,\n} from '@aglyn/aglyn'\nimport { mdiClose, mdiPlus } from '@aglyn/shared-data-mdi'\nimport {\n canonicalSxProperties,\n expandSxAliases,\n isAtomicSxValue,\n} from '@aglyn/shared-data-enums'\nimport { MdiIcon } from '@aglyn/shared-ui-jsx'\nimport { FIELD_MUTED_STYLES, FieldMuteButton } from '@aglyn/shared-ui-jsx-forms'\nimport {\n Autocomplete,\n Button,\n IconButton,\n Stack,\n TextField,\n ToggleButton,\n ToggleButtonGroup,\n Typography,\n} from '@mui/material'\nimport { observer } from 'mobx-react-lite'\nimport { useCallback, useEffect, useMemo, useState } from 'react'\nimport useAglynBesignerFlag from '../hooks/use-aglyn-besigner-flag'\nimport { buildStyleMute, toggleMutedStyle } from '../utils/muted-styles'\nimport type { SxBreakpoint } from '../utils/responsive-sx'\nimport { readSxValue } from '../utils/responsive-sx'\nimport { applyStylePartialToSx } from '../utils/style-field-groups'\nimport { getNodeStyleTarget } from '../utils/style-target'\n\n/**\n * Curated, grouped suggestions for the builder's property picker\n * (freeSolo, so any CSS/sx property still works).\n */\nconst CSS_PROPERTY_SUGGESTIONS: Array<{ group: string; name: string }> = [\n ...[\n 'width',\n 'height',\n 'minWidth',\n 'maxWidth',\n 'minHeight',\n 'maxHeight',\n 'overflow',\n 'position',\n 'top',\n 'right',\n 'bottom',\n 'left',\n 'zIndex',\n ].map((name) => ({ group: 'Layout', name })),\n ...['margin', 'padding', 'gap', 'rowGap', 'columnGap'].map((name) => ({\n group: 'Spacing',\n name,\n })),\n ...[\n 'fontSize',\n 'fontWeight',\n 'fontFamily',\n 'lineHeight',\n 'letterSpacing',\n 'textTransform',\n 'textDecoration',\n 'whiteSpace',\n ].map((name) => ({ group: 'Typography', name })),\n ...[\n 'color',\n 'backgroundColor',\n 'background',\n 'backgroundImage',\n 'backgroundSize',\n 'backgroundPosition',\n 'opacity',\n ].map((name) => ({ group: 'Background & color', name })),\n ...[\n 'border',\n 'borderRadius',\n 'borderColor',\n 'borderWidth',\n 'borderStyle',\n 'outline',\n ].map((name) => ({ group: 'Border', name })),\n ...[\n 'boxShadow',\n 'transform',\n 'transition',\n 'filter',\n 'cursor',\n 'objectFit',\n 'aspectRatio',\n ].map((name) => ({ group: 'Effects', name })),\n]\n\nconst kebabToCamel = (name: string) =>\n name.replace(/-([a-z])/g, (match, letter: string) => letter.toUpperCase())\nconst camelToKebab = (name: string) =>\n name.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)\n\n/** Parses `prop: value;` CSS declarations into a JSS record (AGL-332). */\nexport function parseCssDeclarations(css: string): Record<string, string> {\n const result: Record<string, string> = {}\n for (const raw of css.split(';')) {\n const declaration = raw.trim()\n if (!declaration) continue\n const colon = declaration.indexOf(':')\n if (colon <= 0) continue\n const property = kebabToCamel(declaration.slice(0, colon).trim())\n const value = declaration.slice(colon + 1).trim()\n if (property && value) result[property] = value\n }\n return result\n}\n\n/**\n * The scalar declarations the Builder rows and the CSS tab show for `sx`,\n * named in the Styles panel's own spelling (AGL-2390).\n *\n * MUI honours two names for the same declaration, and every other control\n * in this panel is named for the CSS longhand (AGL-2207). Listing the raw\n * keys made this form the one place that said `bgcolor` where the Colors\n * group said `backgroundColor` and BoxStyler said `paddingTop` — the same\n * node, two vocabularies, and the author with no way to tell that the two\n * rows were one declaration. The expansion is a pure renaming of what\n * already renders (order preserved, non-atomic values left alone), and it\n * is what makes the row's × and the CSS tab's clear reach the key that is\n * actually painting.\n *\n * The JSS tab deliberately does NOT go through here: it edits the stored\n * document verbatim, which is the escape hatch for everything the named\n * controls cannot express.\n */\nexport function customCssDeclarations(\n sx: Record<string, any> | undefined,\n breakpoint: SxBreakpoint | null,\n): Array<{ property: string; value: string }> {\n const source = expandSxAliases((sx ?? {}) as Record<string, any>, {\n deep: true,\n })\n const out: Array<{ property: string; value: string }> = []\n for (const property of Object.keys(source)) {\n const value = readSxValue(source, property, breakpoint)\n if (value === undefined || value === null) continue\n if (typeof value === 'object') continue // selectors/nested — JSON tab\n out.push({ property, value: String(value) })\n }\n return out\n}\n\n/** Serializes scalar sx entries into CSS declarations for the CSS tab. */\nexport function serializeCssDeclarations(\n sx: Record<string, any> | undefined,\n breakpoint: SxBreakpoint | null,\n): string {\n return customCssDeclarations(sx, breakpoint)\n .map(({ property, value }) => `${camelToKebab(property)}: ${value};`)\n .join('\\n')\n}\n\n/**\n * The declaration(s) one named property writes, in the panel's spelling.\n *\n * The property field is free-solo and the CSS tab takes any declaration, so\n * an author can type `bgcolor` or `py` at any time — this form is the one\n * seam in the product where a fresh alias can still enter a document. It\n * resolves the name the same way {@link expandSxAliases} resolves a stored\n * one, so what the author typed and what the panel reads back agree.\n *\n * A value the expansion cannot carry per-side — `p: '10px 20px'`, the CSS\n * shorthand's own syntax — keeps the name the author gave it rather than\n * being copied onto four longhands CSS would drop. Clearing always expands,\n * because a cleared alias has to take every longhand it was painting with\n * it.\n */\nexport function canonicalCssPartial(\n property: string,\n value: string | undefined,\n): Record<string, unknown> {\n const canonical = canonicalSxProperties(property)\n const expandable =\n canonical.length === 1 || value === undefined || isAtomicSxValue(value)\n if (!canonical.length || !expandable) return { [property]: value }\n return Object.fromEntries(canonical.map((name) => [name, value]))\n}\n\n/**\n * Applies custom-CSS edits to `sx` — the write both the Builder rows and\n * the CSS tab's Apply go through (AGL-2390).\n *\n * Routed through the Styles panel's own write seam so this form cannot\n * drift from it again: {@link applyStylePartialToSx} expands the alias keys\n * the edit COLLIDES with before writing, which is what makes clearing a\n * declaration actually clear (deleting `backgroundColor` while a `bgcolor`\n * underneath keeps painting is AGL-2207's sharper failure) and what stops a\n * write leaving two spellings of one declaration in the record.\n *\n * Scheme-agnostic (`null`): this form has always written the base slice,\n * and the dark-preview scoping belongs to the named color fields.\n */\nexport function applyCustomCssEdits(\n sx: Record<string, any> | undefined,\n edits: Record<string, string | undefined>,\n breakpoint: SxBreakpoint | null,\n): Record<string, any> {\n const partial: Record<string, unknown> = {}\n for (const [property, value] of Object.entries(edits)) {\n if (!property) continue\n Object.assign(\n partial,\n canonicalCssPartial(property, value === '' ? undefined : value),\n )\n }\n return applyStylePartialToSx(sx, partial, breakpoint, null)\n}\n\n/**\n * Un-applied CSS / JSS text, held ACROSS an unmount of this form (AGL-2486).\n *\n * The JSS tab is the one control in the Styles panel whose work lives only\n * in React state until Apply — and this form is mounted under\n * `withLastSelectedNode`, which renders \"Select an element\" the instant the\n * canvas selection empties. Anything that clears the selection therefore\n * UNMOUNTS the editor, and a `useState('')` buffer takes the author's\n * half-typed sx document with it. Measured on a live besigner: a selection\n * held with no keyboard or pointer input at all lost the editor 46s in, so\n * this is not something the author can avoid by typing carefully — it is\n * why the wipe reads as random, and why it gets blamed on whichever key was\n * pressed last.\n *\n * Keyed by node AND style target, so a component instance's root and its\n * leaves each keep their own pending text, and re-selecting the element\n * hands the author back exactly what they had typed. An entry lives only\n * while the text DIFFERS from the stored document: applying it, or typing\n * it back to what sx already says, drops the entry, so this never resurrects\n * a draft the author has already resolved.\n *\n * Deliberately not persisted beyond the page: it is un-applied text, not a\n * document, and the crash-recovery draft store is the thing that owns\n * surviving a reload.\n */\nconst pendingDrafts = new Map<string, string>()\n\nconst draftKey = (\n nodeId: string,\n overrideKey: string,\n mode: 'css' | 'json',\n): string => [nodeId, overrideKey, mode].join('\\x00')\n\n/** Drops every pending draft — test seam, and a hard reset for the canvas. */\nexport function resetPendingCustomCssDrafts(): void {\n pendingDrafts.clear()\n}\n\n/**\n * Why a JSS buffer cannot be applied, or `null` when it is ready\n * (AGL-2486). Rendered live as the author types, so \"nothing happened when\n * I pressed Apply\" is never the first sign that the text does not parse.\n *\n * Mid-edit JSON is TRANSIENTLY invalid by nature — `{\"a\": 1,` is what every\n * new property looks like a keystroke before it is finished — so this is a\n * hint, never a block on typing and never a reason to touch the document.\n *\n * An empty buffer is called out rather than treated as `{}`: `JSON.parse`\n * would need a fallback to accept it at all, and silently reading \"\" as\n * \"delete every style on this element\" is the most destructive thing this\n * panel could do with an accidental Select-All. Clearing all styles stays\n * available, spelled `{}`, which nobody types by accident.\n */\nexport function jsonDraftProblem(text: string): string | null {\n const trimmed = (text ?? '').trim()\n if (!trimmed) {\n return 'Empty — type {} to clear every style on this element.'\n }\n let parsed: unknown\n try {\n parsed = JSON.parse(trimmed)\n } catch {\n return 'Not valid JSON yet — Apply will not change anything until it parses.'\n }\n // Explicit `=== null` and an array check: `strictNullChecks` is off\n // repo-wide, so `!parsed` would fold `null`, `0` and `\"\"` together, and\n // `typeof null` is `'object'`.\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n return 'The sx value must be a JSON object.'\n }\n return null\n}\n\nexport interface CustomCssFormProps {\n node?: Aglyn.NodeSchema\n /** Active artboard breakpoint; builder/CSS edits scope to it (AGL-333). */\n breakpoint: SxBreakpoint | null\n /**\n * Which `styleOverrides` slice to edit on a component instance\n * (AGL-1332) — the Styles panel's current style target. Ignored for a\n * plain node; defaults to the component root.\n */\n overrideKey?: string\n /**\n * True when the node is a placed form whose design resolves, so its edits\n * land in the same override slice the Styles panel writes (AGL-3285).\n */\n placedFormResolves?: boolean\n}\n\ntype BuilderRow = { property: string; value: string }\n\n/**\n * Custom CSS for the selected element's `sx` (AGL-332): a friendly\n * builder (property + value rows), a raw CSS mode, and a raw JSS/JSON\n * mode. Builder and CSS edits respect the active breakpoint scope; the\n * JSON mode edits the full sx document (including responsive objects and\n * nested selectors) verbatim.\n */\nexport const CustomCssForm = observer((props: CustomCssFormProps) => {\n const { node, breakpoint, overrideKey, placedFormResolves } = props\n // Same target the rest of the Styles panel writes (AGL-1306/1332): a\n // plain node's own sx, or the instance override slice the panel's style\n // target names — root, or one leaf inside the component.\n const target = useMemo(\n () => getNodeStyleTarget(node, overrideKey, { placedFormResolves }),\n [node, overrideKey, placedFormResolves],\n )\n const nodeSx = (target.sx ?? {}) as Record<string, any>\n const [mode, setMode] = useState<'builder' | 'css' | 'json'>('builder')\n const [cssDraft, setCssDraft] = useState('')\n const [jsonDraft, setJsonDraft] = useState('')\n const [error, setError] = useState<string | null>(null)\n\n const rows = useMemo<BuilderRow[]>(\n () => customCssDeclarations(nodeSx, breakpoint),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [JSON.stringify(nodeSx), breakpoint],\n )\n const [draftRow, setDraftRow] = useState<BuilderRow>({\n property: '',\n value: '',\n })\n\n // Declarations switched off for comparison (AGL-2486). Canvas only, and\n // the same flag every other row in the panel writes, so a property muted\n // here and the same property muted from its typed field are one state.\n // The builder lists BASE scalar declarations only — a state slice is a\n // nested object it skips — so these mutes carry no state scope.\n const [mutedStyles, setMutedStyles] = useAglynBesignerFlag('mutedStyles')\n const rowMute = useCallback(\n (property: string) =>\n buildStyleMute(property, property, {\n nodeId: node?.$id,\n state: null,\n breakpoint,\n // Every row the builder lists is, by construction, declared.\n scopeValues: { [property]: true },\n mutedStyles,\n onToggle: (target) =>\n setMutedStyles((current) => toggleMutedStyle(current, target)),\n }),\n [node, breakpoint, mutedStyles, setMutedStyles],\n )\n\n const nodeId = node?.$id ?? ''\n /** What the STORED document says, in each tab's spelling. */\n const storedCss = useMemo(\n () => serializeCssDeclarations(nodeSx, breakpoint),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [JSON.stringify(nodeSx), breakpoint],\n )\n const storedJson = useMemo(\n () => JSON.stringify(nodeSx, null, 2),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [JSON.stringify(nodeSx)],\n )\n\n /**\n * Records (or drops) the author's un-applied text for one tab, so an\n * unmount cannot take it (AGL-2486). Text equal to the stored document is\n * not a draft at all, so it clears the entry rather than pinning a value\n * that would then shadow a later change from elsewhere in the panel.\n */\n const rememberDraft = useCallback(\n (draftMode: 'css' | 'json', value: string, stored: string) => {\n const key = draftKey(nodeId, target.overrideKey, draftMode)\n if (value === stored) pendingDrafts.delete(key)\n else pendingDrafts.set(key, value)\n },\n [nodeId, target.overrideKey],\n )\n\n useEffect(() => {\n // A pending draft wins over the stored document — that is the whole\n // point of keeping it. `!== undefined` rather than a truthiness test:\n // `''` is a legitimate buffer the author emptied on purpose, and with\n // `strictNullChecks` off `if (!pending)` would silently discard it.\n const pendingCss = pendingDrafts.get(\n draftKey(nodeId, target.overrideKey, 'css'),\n )\n const pendingJson = pendingDrafts.get(\n draftKey(nodeId, target.overrideKey, 'json'),\n )\n setCssDraft(pendingCss !== undefined ? pendingCss : storedCss)\n setJsonDraft(pendingJson !== undefined ? pendingJson : storedJson)\n setError(null)\n // Re-seeded on the override target too (AGL-1332): switching from the\n // component root to a leaf must reload the drafts, or a stale CSS\n // buffer would be written back onto the newly picked target.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [mode, nodeId, target.overrideKey])\n\n /**\n * Live \"is this appliable yet\" verdict for the JSS tab (AGL-2486) — a\n * caption only. Nothing here writes, so a buffer that stops parsing\n * mid-property changes the hint and leaves the document alone.\n */\n const jsonProblem = useMemo(\n () => (mode === 'json' ? jsonDraftProblem(jsonDraft) : null),\n [mode, jsonDraft],\n )\n\n // Passive off-site url() hint (AGL-1737) — the \"warn\" half of AGL-1725's\n // warn-and-disclose, for the panel with no per-field description to carry\n // it. Names the hosts a url() in the CURRENT value would make every\n // visitor's browser contact: the live draft in the CSS and JSON modes\n // (ahead of Apply, so the author reads it while typing), the committed sx\n // in the builder (whose rows write live). A caption and nothing more — it\n // never blocks a value, and it deliberately does not touch how or when\n // this form commits (an attribute panel edit commits on blur, and that\n // behavior is load-bearing).\n const thirdPartyHosts = useMemo<string[]>(() => {\n if (mode === 'css') return collectThirdPartyAuthorCssUrlHosts(cssDraft)\n if (mode === 'json') {\n try {\n return collectThirdPartyAuthorSxUrlHosts(JSON.parse(jsonDraft || '{}'))\n } catch {\n // Mid-edit JSON — scan the raw text so the hint tracks typing.\n return collectThirdPartyAuthorCssUrlHosts(jsonDraft)\n }\n }\n return collectThirdPartyAuthorSxUrlHosts(nodeSx)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [mode, cssDraft, jsonDraft, JSON.stringify(nodeSx)])\n\n const writeProperty = useCallback(\n (property: string, value: string | undefined) => {\n if (!node || !property) return\n // Coalesced per property (AGL-1204): the builder rows write on every\n // keystroke, so typing one value is one undo step, and editing a\n // different property starts another. The write goes through the style\n // target (AGL-1306), so this is undoable on an instance's override\n // layer exactly as on a plain node's own sx.\n canvas.transact(\n () => {\n target.setSx(\n applyCustomCssEdits(\n (target.sx ?? {}) as Record<string, any>,\n { [property]: value },\n breakpoint,\n ),\n )\n },\n ['sx', node.$id, breakpoint ?? 'base', property].join(':'),\n )\n },\n [node, target, breakpoint],\n )\n\n const applyCss = useCallback(() => {\n if (!node) return\n const parsed = parseCssDeclarations(cssDraft)\n // Uncoalesced: pressing Apply is one deliberate commit, however many\n // declarations it carries (AGL-1204).\n canvas.transact(() => {\n // One edit map, applied once: declarations no longer in the draft\n // clear, the rest write. Both halves resolve their spelling through\n // `applyCustomCssEdits`, so a row the author deleted takes the alias\n // that was painting it with it, and a declaration they typed under\n // MUI's spelling lands where every other control can read it.\n const edits: Record<string, string | undefined> = {}\n for (const row of rows) {\n if (!(row.property in parsed)) edits[row.property] = undefined\n }\n Object.assign(edits, parsed)\n target.setSx(\n applyCustomCssEdits(\n (target.sx ?? {}) as Record<string, any>,\n edits,\n breakpoint,\n ),\n )\n })\n setError(null)\n // Applied text is no longer un-applied.\n pendingDrafts.delete(draftKey(nodeId, target.overrideKey, 'css'))\n }, [node, target, cssDraft, rows, breakpoint, nodeId])\n\n /**\n * Commits the JSS buffer — the ONLY path from this tab to the document\n * (AGL-2486).\n *\n * A buffer that does not parse to a JSON object is a NON-COMMIT: it sets\n * the inline error and returns, leaving both the document and the text\n * exactly as they are, so the author can keep editing from where they\n * were. There is no blur, change or unmount handler anywhere in this form\n * that reaches the document — Apply is the whole seam, which is what\n * makes a transiently invalid buffer harmless.\n */\n const applyJson = useCallback(() => {\n if (!node) return\n const problem = jsonDraftProblem(jsonDraft)\n if (problem !== null) {\n setError(problem)\n return\n }\n // Re-parsed rather than carried out of the guard: `jsonDraftProblem`\n // owns the verdict, and one function deciding while another commits is\n // how \"valid enough to pass, wrong shape to store\" gets in.\n const parsed = JSON.parse(jsonDraft.trim())\n // Replacing sx wholesale is the single most destructive edit this panel\n // offers, so it is the one that most needs to be undoable (AGL-1204).\n canvas.transact(() => {\n target.setSx(parsed)\n })\n setError(null)\n pendingDrafts.delete(draftKey(nodeId, target.overrideKey, 'json'))\n }, [node, target, jsonDraft, nodeId])\n\n return (\n <Stack spacing={1.5}>\n <ToggleButtonGroup\n size=\"small\"\n exclusive\n fullWidth\n value={mode}\n onChange={(event, value) => value && setMode(value)}\n >\n <ToggleButton value=\"builder\">{'Builder'}</ToggleButton>\n <ToggleButton value=\"css\">{'CSS'}</ToggleButton>\n <ToggleButton value=\"json\">{'JSS (sx)'}</ToggleButton>\n </ToggleButtonGroup>\n\n {mode === 'builder' ? (\n <Stack spacing={1}>\n {rows.map((row) => {\n const mute = rowMute(row.property)\n return (\n <Stack\n key={row.property}\n direction=\"row\"\n spacing={1}\n sx={[\n { alignItems: 'center' },\n mute?.muted ? FIELD_MUTED_STYLES : null,\n ]}\n >\n <TextField\n size=\"small\"\n value={row.property}\n disabled\n sx={{ flex: 1 }}\n />\n <TextField\n size=\"small\"\n value={row.value}\n onChange={(event) =>\n writeProperty(row.property, event.target.value)\n }\n sx={{ flex: 1 }}\n />\n {/* The eye stops the declaration applying and keeps its\n value; the ✕ beside it is still the only thing that\n takes the declaration off the element. */}\n <FieldMuteButton mute={mute} />\n <IconButton\n size=\"small\"\n aria-label={`Remove ${row.property}`}\n onClick={() => writeProperty(row.property, undefined)}\n >\n <MdiIcon path={mdiClose.path} size={0.7} />\n </IconButton>\n </Stack>\n )\n })}\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Autocomplete\n freeSolo\n size=\"small\"\n options={CSS_PROPERTY_SUGGESTIONS}\n groupBy={(option) =>\n typeof option === 'string' ? '' : option.group\n }\n getOptionLabel={(option) =>\n typeof option === 'string' ? option : option.name\n }\n inputValue={draftRow.property}\n onInputChange={(event, value) =>\n setDraftRow((prev) => ({ ...prev, property: value }))\n }\n renderInput={(params) => (\n <TextField {...params} placeholder=\"property\" />\n )}\n sx={{ flex: 1 }}\n />\n <TextField\n size=\"small\"\n placeholder=\"value\"\n value={draftRow.value}\n onChange={(event) =>\n setDraftRow((prev) => ({ ...prev, value: event.target.value }))\n }\n sx={{ flex: 1 }}\n />\n <IconButton\n size=\"small\"\n aria-label=\"Add property\"\n disabled={!draftRow.property.trim() || !draftRow.value.trim()}\n onClick={() => {\n writeProperty(\n kebabToCamel(draftRow.property.trim()),\n draftRow.value.trim(),\n )\n setDraftRow({ property: '', value: '' })\n }}\n >\n <MdiIcon path={mdiPlus.path} size={0.8} />\n </IconButton>\n </Stack>\n </Stack>\n ) : null}\n\n {mode === 'css' ? (\n <Stack spacing={1}>\n <TextField\n multiline\n minRows={5}\n value={cssDraft}\n onChange={(event) => {\n setCssDraft(event.target.value)\n rememberDraft('css', event.target.value, storedCss)\n }}\n placeholder={\n 'border-radius: 8px;\\nbox-shadow: 0 2px 8px rgba(0,0,0,0.2);'\n }\n slotProps={{\n htmlInput: {\n style: { fontFamily: 'monospace', fontSize: 12 },\n },\n }}\n />\n <Button size=\"small\" variant=\"outlined\" onClick={applyCss}>\n {'Apply CSS'}\n </Button>\n </Stack>\n ) : null}\n\n {mode === 'json' ? (\n <Stack spacing={1}>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {(target.isComposed\n ? 'The effective sx — supports responsive objects '\n : 'Full sx document — supports responsive objects ') +\n '({ xs, sm, … }) and nested selectors (\"&:hover\").'}\n </Typography>\n {target.isComposed ? (\n // The panel has to say this out loud (AGL-3218). Everything\n // below is correct and it is also not what the editor looks\n // like it is doing, and the author only finds out by reading\n // the stored node somewhere else — where the edit reads as\n // missing.\n <Typography variant=\"caption\" color=\"text.secondary\">\n {'This element stores styles in two places: a props.sx its ' +\n 'component or preset authored, and its own sx. The box ' +\n 'shows both merged, the way the page renders them. Apply ' +\n 'writes only what you changed into the element’s own sx ' +\n 'and leaves the rest where it is, so a property you edit ' +\n 'here will not appear in props.sx.'}\n </Typography>\n ) : null}\n <TextField\n multiline\n minRows={6}\n value={jsonDraft}\n onChange={(event) => {\n setJsonDraft(event.target.value)\n rememberDraft('json', event.target.value, storedJson)\n // A keystroke that fixes the JSON clears a stale Apply error;\n // it never raises one, so typing is not nagged mid-property.\n if (error && jsonDraftProblem(event.target.value) === null) {\n setError(null)\n }\n }}\n error={Boolean(error)}\n helperText={error ?? jsonProblem ?? undefined}\n slotProps={{\n htmlInput: {\n style: { fontFamily: 'monospace', fontSize: 12 },\n },\n }}\n />\n <Button size=\"small\" variant=\"outlined\" onClick={applyJson}>\n {'Apply JSS'}\n </Button>\n </Stack>\n ) : null}\n\n {thirdPartyHosts.length ? (\n // Same voice as the Custom HTML `css` description (AGL-1737), with\n // the host named back so the consequence is attached to the value\n // the author typed rather than left as general prose.\n <Typography variant=\"caption\" color=\"text.secondary\">\n {'A url() here points off your site — every visitor’s browser ' +\n `will contact ${thirdPartyHosts.join(', ')}, disclosing their ` +\n 'IP address and which page they are on. Insecure http:// URLs ' +\n 'are not loaded.'}\n </Typography>\n ) : null}\n </Stack>\n )\n})\nCustomCssForm.displayName = 'CustomCssForm'\n\nexport default CustomCssForm\n"],"names":["canvas","collectThirdPartyAuthorCssUrlHosts","collectThirdPartyAuthorSxUrlHosts","mdiClose","mdiPlus","canonicalSxProperties","expandSxAliases","isAtomicSxValue","MdiIcon","FIELD_MUTED_STYLES","FieldMuteButton","Autocomplete","Button","IconButton","Stack","TextField","ToggleButton","ToggleButtonGroup","Typography","observer","useCallback","useEffect","useMemo","useState","useAglynBesignerFlag","buildStyleMute","toggleMutedStyle","readSxValue","applyStylePartialToSx","getNodeStyleTarget","CSS_PROPERTY_SUGGESTIONS","map","name","group","kebabToCamel","replace","match","letter","toUpperCase","camelToKebab","toLowerCase","parseCssDeclarations","css","result","raw","split","declaration","trim","colon","indexOf","property","slice","value","customCssDeclarations","sx","breakpoint","source","deep","out","Object","keys","undefined","push","String","serializeCssDeclarations","join","canonicalCssPartial","canonical","expandable","length","fromEntries","applyCustomCssEdits","edits","partial","entries","assign","pendingDrafts","Map","draftKey","nodeId","overrideKey","mode","resetPendingCustomCssDrafts","clear","jsonDraftProblem","text","trimmed","parsed","JSON","parse","Array","isArray","CustomCssForm","props","target","error","node","placedFormResolves","nodeSx","setMode","cssDraft","setCssDraft","jsonDraft","setJsonDraft","setError","rows","stringify","draftRow","setDraftRow","mutedStyles","setMutedStyles","rowMute","$id","state","scopeValues","onToggle","current","storedCss","storedJson","rememberDraft","draftMode","stored","key","delete","set","pendingCss","get","pendingJson","jsonProblem","thirdPartyHosts","writeProperty","transact","setSx","applyCss","row","applyJson","problem","spacing","size","exclusive","fullWidth","onChange","event","mute","direction","alignItems","muted","disabled","flex","aria-label","onClick","path","freeSolo","options","groupBy","option","getOptionLabel","inputValue","onInputChange","prev","renderInput","params","placeholder","multiline","minRows","slotProps","htmlInput","style","fontFamily","fontSize","variant","color","isComposed","Boolean","helperText","displayName"],"mappings":"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"}
@@ -14,7 +14,7 @@
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
16
  */ import { _ as _extends } from "@swc/helpers/_/_extends";
17
- import { canvas, FEATURE_FLAG, getInstanceEffectivePropText, inheritedMediaAlt, resolveInstanceLeafBinding, REUSABLE_INSTANCE_COMPONENT_ID, REUSABLE_INSTANCE_PROP_VALUES_KEY } from "@aglyn/aglyn";
17
+ import { canvas, FEATURE_FLAG, getInstanceEffectivePropText, inheritedMediaAlt, isDefinitionPlacement, placementDefinitionFor, resolveInstanceLeafBinding, REUSABLE_INSTANCE_COMPONENT_ID, REUSABLE_INSTANCE_PROP_VALUES_KEY } from "@aglyn/aglyn";
18
18
  import * as Besigner from "@aglyn/besigner";
19
19
  import { mergeRefs, useId } from "@aglyn/shared-ui-jsx";
20
20
  import { useDraggable, useDroppable } from "@dnd-kit/core";
@@ -27,7 +27,8 @@ import { MediaPickerContext } from "../../contexts/media-picker-context.js";
27
27
  import { findMarkdownAttributeName, inlineMarkdownEdit } from "../../utils/inline-markdown-edit.store.js";
28
28
  import { inlineTextEdit, isInlineEditWithin } from "../../utils/inline-text-edit.store.js";
29
29
  import { requestCanvasContextMenu } from "../../utils/canvas-context-menu.js";
30
- import { findInstanceLeafAtPoint } from "../../utils/instance-leaf-hit.js";
30
+ import { findInstanceLeafAtPoint, findPlacementPartAtPoint } from "../../utils/instance-leaf-hit.js";
31
+ import { pickPlacementPart } from "../../utils/placement-part-pick.js";
31
32
  import { findLeafTextElement } from "../../utils/in-place-edit-surface.js";
32
33
  export const DraggableDroppable = observer((props)=>{
33
34
  const { node, type, disableDragging, disableDropping, accept, children, idSuffix } = props;
@@ -85,14 +86,16 @@ export const DraggableDroppable = observer((props)=>{
85
86
  // definitions (hit-test) and the host's media picker (image props).
86
87
  // Read through a ref so the listener effect below keeps its `[node]`
87
88
  // dependency — context updates must not re-register DOM listeners.
88
- const { definitions } = useContext(ComponentPromotionContext);
89
+ const { definitions, formDesigns } = useContext(ComponentPromotionContext);
89
90
  const { onPickMedia } = useContext(MediaPickerContext);
90
91
  const instanceEditRef = useRef({
91
92
  definitions,
93
+ formDesigns,
92
94
  onPickMedia
93
95
  });
94
96
  instanceEditRef.current = {
95
97
  definitions,
98
+ formDesigns,
96
99
  onPickMedia
97
100
  };
98
101
  useEffect(()=>{
@@ -213,8 +216,28 @@ export const DraggableDroppable = observer((props)=>{
213
216
  Besigner.focus.rangeSelectNode(node);
214
217
  } else {
215
218
  Besigner.focus.handleNodeSelection(node, pointer.metaKey || pointer.ctrlKey);
219
+ if (!pointer.metaKey && !pointer.ctrlKey) pickPartUnder(pointer);
216
220
  }
217
221
  }
222
+ /**
223
+ * A plain click on a placement (a component instance or a placed form)
224
+ * also says WHICH part of it was under the pointer (AGL-3288), so the
225
+ * Styles and Attributes tabs aim "Which part?" at what the author just
226
+ * clicked. The parts themselves stay unselectable — they are drawn
227
+ * inert — so this is the only way a click can reach one.
228
+ */ function pickPartUnder(pointer) {
229
+ if (!isDefinitionPlacement(node)) return;
230
+ const container = ref.current;
231
+ if (!container || typeof pointer.clientX !== 'number') return;
232
+ const { definitions, formDesigns } = instanceEditRef.current;
233
+ const definition = placementDefinitionFor(node, {
234
+ definitions,
235
+ formDesigns
236
+ });
237
+ if (!definition) return;
238
+ const key = findPlacementPartAtPoint(container, pointer.clientX, pointer.clientY, node.$id, (graftedId)=>resolveInstanceLeafBinding(graftedId, node.$id, definition, 'children', definition.rootId), definition.rootId);
239
+ pickPlacementPart(node.$id, key != null ? key : '');
240
+ }
218
241
  function handleDoubleClick(e) {
219
242
  var _node_componentSchema_flags, _node_componentSchema, _node_props;
220
243
  // Prop-fed instance internals (AGL-1304): the rendered preview sits
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../../../libs/besigner/feature/designer/src/lib/components/dnd/draggable-droppable.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2023 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type * as Aglyn from '@aglyn/aglyn'\nimport {\n canvas,\n FEATURE_FLAG,\n getInstanceEffectivePropText,\n inheritedMediaAlt,\n resolveInstanceLeafBinding,\n REUSABLE_INSTANCE_COMPONENT_ID,\n REUSABLE_INSTANCE_PROP_VALUES_KEY,\n} from '@aglyn/aglyn'\nimport * as Besigner from '@aglyn/besigner'\nimport { mergeRefs, useId } from '@aglyn/shared-ui-jsx'\nimport { useDraggable, useDroppable } from '@dnd-kit/core'\nimport { CSS as css } from '@dnd-kit/utilities'\nimport { mergeProps } from '@react-aria/utils'\nimport { observer } from 'mobx-react-lite'\nimport {\n Children,\n cloneElement,\n CSSProperties,\n useContext,\n useEffect,\n useRef,\n} from 'react'\nimport ComponentPromotionContext from '../../contexts/component-promotion-context'\nimport { MediaPickerContext } from '../../contexts/media-picker-context'\nimport {\n findMarkdownAttributeName,\n inlineMarkdownEdit,\n} from '../../utils/inline-markdown-edit.store'\nimport {\n inlineTextEdit,\n isInlineEditWithin,\n} from '../../utils/inline-text-edit.store'\nimport { requestCanvasContextMenu } from '../../utils/canvas-context-menu'\nimport { findInstanceLeafAtPoint } from '../../utils/instance-leaf-hit'\nimport { findLeafTextElement } from '../../utils/in-place-edit-surface'\n\nexport interface DraggableDroppableProps<T extends Aglyn.NodeSchema<any>> {\n children: JSX.Element\n node: T\n type: Besigner.DragType\n accept: Besigner.DragType[]\n disableDragging?: boolean\n disableDropping?: boolean\n idSuffix?: string\n}\n\nexport const DraggableDroppable = observer(\n <T extends Aglyn.NodeSchema<any>>(props: DraggableDroppableProps<T>) => {\n const {\n node,\n type,\n disableDragging,\n disableDropping,\n accept,\n children,\n idSuffix,\n } = props\n const id = useId(node?.$id)\n\n const draggable = useDraggable({\n id: `${id}:${type}${idSuffix || ''}`,\n data: { type, node },\n disabled: disableDragging,\n })\n\n const droppable = useDroppable({\n id: `${id}:${type}${idSuffix || ''}`,\n data: { type, node, accept },\n disabled: disableDropping,\n })\n\n const transform = draggable.transform\n const isTransforming = Boolean(draggable.transform)\n const child = Children.only(children)\n const style: CSSProperties = {\n ...child.props.style,\n // cursor: 'move',\n outlineWidth: 2,\n outlineOffset: -1,\n outlineColor: 'grey',\n outlineStyle: 'dotted',\n // transform: css.Transform.toString(transform),\n // scale: css.Scale.toString(transform),\n translate: css.Translate.toString(transform),\n\n ...(isTransforming\n ? {\n // transform: `translate3d(${transform.x}px, ${transform.y}px, 0)`,\n // cursor: 'grab',\n }\n : {}),\n ...(droppable.isOver\n ? {\n outlineColor: 'lime',\n outlineStyle: 'solid',\n outlineOffset: '3',\n }\n : {}),\n ...(draggable.isDragging\n ? {\n outlineColor: 'grey',\n outlineStyle: 'double',\n outlineOffset: -1 as any,\n opacity: 0.5,\n cursor: 'move',\n }\n : {}),\n // Element-picker affordance (AGL-574): while the interaction builder\n // is picking a target, every leaf shows the crosshair and the hovered\n // one gets a distinct violet ring — deliberately unlike the normal\n // selection/drop outlines so \"pick this\" reads as its own mode.\n ...(Besigner.pick.isPicking() ? { cursor: 'crosshair' } : {}),\n ...(Besigner.pick.isPicking() && Besigner.focus.isNodeHovered(node)\n ? {\n outlineColor: '#7c4dff',\n outlineStyle: 'solid',\n outlineWidth: 2,\n outlineOffset: 2 as any,\n }\n : {}),\n }\n\n const ref = useRef<HTMLElement>(null)\n\n // Instance-internals double-click (AGL-1304) needs the component\n // definitions (hit-test) and the host's media picker (image props).\n // Read through a ref so the listener effect below keeps its `[node]`\n // dependency — context updates must not re-register DOM listeners.\n const { definitions } = useContext(ComponentPromotionContext)\n const { onPickMedia } = useContext(MediaPickerContext)\n const instanceEditRef = useRef({ definitions, onPickMedia })\n instanceEditRef.current = { definitions, onPickMedia }\n\n useEffect(() => {\n Besigner.refs.set(node.$id, ref)\n return () => {\n Besigner.refs.delete(node.$id)\n }\n }, [node?.$id])\n\n useEffect(() => {\n Besigner.handles.set(node.$id, {\n ...draggable.listeners,\n style: isTransforming ? { cursor: 'grab' } : { cursor: 'move' },\n })\n return () => {\n Besigner.handles.delete(node.$id)\n }\n }, [node.$id, draggable.listeners, isTransforming])\n\n useEffect(() => {\n const el = ref.current\n if (el) {\n el.addEventListener('mouseover', handleMouseOver)\n el.addEventListener('pointerover', handleMouseOver)\n el.addEventListener('mousedown', handleMouseDown)\n el.addEventListener('pointerdown', handleMouseDown)\n el.addEventListener('dblclick', handleDoubleClick)\n el.addEventListener('contextmenu', handleContextMenu)\n\n return () => {\n el.removeEventListener('mouseover', handleMouseOver)\n el.removeEventListener('pointerover', handleMouseOver)\n el.removeEventListener('mousedown', handleMouseDown)\n el.removeEventListener('pointerdown', handleMouseDown)\n el.removeEventListener('dblclick', handleDoubleClick)\n el.removeEventListener('contextmenu', handleContextMenu)\n }\n }\n /**\n * Right-click opens this element's ⋮ menu (AGL-1405).\n *\n * A native listener like its neighbours, not an `onContextMenu` prop:\n * React delegates at the root container and the canvas is a closed\n * shadow root, so a delegated handler never sees the event.\n *\n * Selects first. The menu is rendered by the overlay, which only mounts\n * for a selected or hovered node — and right-clicking an element is a\n * statement about THAT element, so leaving the previous selection in\n * place would open the wrong menu.\n */\n function handleContextMenu(e: Event) {\n if (isInlineEditWithin(el)) {\n // An open text editor keeps the browser's own menu: cut, paste and\n // spellcheck are the whole point of a right-click there.\n e.stopPropagation()\n return\n }\n e.preventDefault()\n e.stopPropagation()\n Besigner.focus.setSelectedNode(node)\n requestCanvasContextMenu(node?.$id)\n }\n function handleMouseOver(e: Event) {\n // Same stand-down, same reason to consume it: a hover repaint while\n // the author is typing is a re-render of the subtree they are\n // editing, and an ancestor leaf would repaint for them instead.\n if (isInlineEditWithin(el)) {\n e.stopPropagation()\n return\n }\n e.preventDefault()\n e.stopPropagation()\n Besigner.focus.setHoveredNode(node)\n }\n function handleMouseDown(e: Event) {\n // Stand down while an edit is open in this leaf OR ANY LEAF INSIDE\n // IT (AGL-2486) — but CONSUME the event rather than ignoring it.\n //\n // Two different mistakes produced the same symptom, an editor that\n // was open, styled and completely uninteractive. First, asking only\n // \"is this leaf the node being edited?\": a nested run — a Span\n // typography inside a Typography — silenced its OWN leaf, the event\n // bubbled to the ancestor leaf, and the ancestor called the\n // `preventDefault` that suppresses caret placement. Second, standing\n // down by simply returning: that also skipped `stopPropagation`, so\n // the gesture carried on up to the viewport, which treats a\n // mousedown on empty canvas as a deselect — and moving the selection\n // off the node closes the editor. A drag meant to select three words\n // tore the editor down instead.\n //\n // So: no `preventDefault`, because that is the caret; but still\n // `stopPropagation`, because nothing else on this canvas should see\n // a gesture aimed at text being edited. `stopPropagation` does not\n // suppress a default action, only delivery.\n if (isInlineEditWithin(el)) {\n e.stopPropagation()\n return\n }\n e.preventDefault()\n e.stopPropagation()\n // Element-picker capture (AGL-574): while the interaction builder is\n // picking, this click resolves the target and exits pick mode —\n // never the normal select/navigate, so the designer's selection and\n // the open interaction stay put.\n if (Besigner.pick.isPicking()) {\n Besigner.pick.handlePickClick(node.$id)\n return\n }\n // Canvas multi-selection modifiers (AGL-12), mirroring the\n // hierarchy panel: Shift ranges, Cmd/Ctrl toggles.\n const pointer = e as globalThis.MouseEvent\n if (pointer.shiftKey) {\n Besigner.focus.rangeSelectNode(node)\n } else {\n Besigner.focus.handleNodeSelection(\n node,\n pointer.metaKey || pointer.ctrlKey,\n )\n }\n }\n function handleDoubleClick(e: Event) {\n // Prop-fed instance internals (AGL-1304): the rendered preview sits\n // under `pointer-events: none`, so the double-click lands HERE, on\n // the instance — hit-test the preview geometrically, map the grafted\n // leaf back to the definition, and if a declared prop feeds it, edit\n // that prop inline. Leaves the component owns stay locked (the\n // selection this dblclick's mousedowns already made puts AGL-1303's\n // \"Edit component\" in the panel — nothing extra to build).\n if (\n node?.componentId === REUSABLE_INSTANCE_COMPONENT_ID &&\n Besigner.dnd.canDragNode(node)\n ) {\n if (handleInstanceDoubleClick(e as globalThis.MouseEvent)) {\n e.preventDefault()\n e.stopPropagation()\n }\n return\n }\n // Locked nodes (layout chrome in the screen besigner) stay read-only —\n // same gate the drag system uses — and while the interaction builder\n // is picking a target, a double-click is two picks, never an edit.\n if (!Besigner.dnd.canDragNode(node) || Besigner.pick.isPicking()) return\n // Inline text editing for components that declare textEditable.\n const flag = node?.componentSchema?.flags?.textEditable\n const editable =\n typeof flag === 'number' && (flag & FEATURE_FLAG.ENABLED) !== 0\n if (editable) {\n e.preventDefault()\n e.stopPropagation()\n const root = e.currentTarget as Element\n // The node's own `<aglyn-text>` whenever it renders one, and the\n // root only as a fallback (AGL-2556). In-place editing EMPTIES\n // whatever it is given, so handing it a composite's root deletes\n // the chrome that composite built — for Accordion Summary that is\n // the content div carrying MUI's `textAlign: 'start'`, without\n // which the UA stylesheet centres the label inside the `<button>`\n // for as long as the edit is open.\n const element = findLeafTextElement(root) ?? root\n const rect = element.getBoundingClientRect()\n inlineTextEdit.open(\n node,\n {\n left: rect.left,\n top: rect.top,\n width: rect.width,\n height: rect.height,\n },\n undefined,\n // The measured element travels with the rect so the overlay can\n // re-measure on scroll instead of drifting (AGL-1644).\n element,\n )\n return\n }\n // In-place markdown editing (AGL-1624): a component whose schema\n // declares a MARKDOWN attribute — the Markdown element's `content` is\n // the one that exists — opens the WYSIWYG over the element, editing\n // the whole document without a trip to the attributes panel.\n //\n // `stopPropagation` on the NATIVE event is what keeps the canvas still:\n // React delegates `onDoubleClick` at the root container, so an event\n // consumed here never reaches `ZoomablePanningComponent`'s\n // double-click-to-zoom. That is the same guarantee the textEditable\n // branch above has always relied on, not a new mechanism.\n const markdownAttribute = findMarkdownAttributeName(node)\n if (!markdownAttribute) return\n e.preventDefault()\n e.stopPropagation()\n const markdownElement = e.currentTarget as Element\n const markdownRect = markdownElement.getBoundingClientRect()\n const stored = (node.props as Record<string, unknown> | undefined)?.[\n markdownAttribute\n ]\n // The two mousedowns of a double-click TOGGLE selection, so by the time\n // this fires the node has been selected and then deselected again.\n // Editing an element is selecting it — the attributes panel should be\n // showing it — and the editor closes when the selection moves off the\n // node, so it has to be put back before the editor opens.\n Besigner.focus.setSelectedNode(node)\n inlineMarkdownEdit.open(\n node,\n {\n left: markdownRect.left,\n top: markdownRect.top,\n width: markdownRect.width,\n height: markdownRect.height,\n },\n markdownAttribute,\n typeof stored === 'string' ? stored : '',\n markdownElement,\n )\n }\n /** True when the double-click resolved to a prop edit it opened. */\n function handleInstanceDoubleClick(pointer: globalThis.MouseEvent) {\n const { definitions, onPickMedia } = instanceEditRef.current\n const refId = (node?.props as { refId?: string } | undefined)?.refId\n const definition = refId ? definitions?.[refId] : undefined\n const container = ref.current\n if (!definition || !container) return false\n const hit = findInstanceLeafAtPoint(\n container,\n pointer.clientX,\n pointer.clientY,\n )\n if (!hit) return false\n // Text first: a leaf whose `children` a declared prop feeds opens\n // the EXISTING inline editor, anchored on the clicked leaf, and the\n // commit writes propValues[prop] on the instance (one undo entry,\n // mirrored to peers like any node edit).\n const text = resolveInstanceLeafBinding(\n hit.graftedId,\n node.$id,\n definition,\n )\n if (text?.boundProp) {\n const rect = hit.element.getBoundingClientRect()\n inlineTextEdit.open(\n node,\n {\n left: rect.left,\n top: rect.top,\n width: rect.width,\n height: rect.height,\n },\n {\n propName: text.boundProp,\n initialText: getInstanceEffectivePropText(\n node.props,\n definition.props,\n text.boundProp,\n ),\n },\n // The GRAFTED leaf, not the instance node: it is what was measured,\n // and it is the one anchor `Besigner.refs` could never supply\n // because it is not a canvas node (AGL-1644).\n hit.element,\n )\n return true\n }\n // Image half: `src` fed by a declared prop opens the host's media\n // picker, committing the picked value to the same propValues slot —\n // the exact pipeline the Attributes panel's Browse button uses.\n const src = resolveInstanceLeafBinding(\n hit.graftedId,\n node.$id,\n definition,\n 'src',\n )\n if (src?.boundProp && onPickMedia) {\n const propName = src.boundProp\n // The SAME leaf's `alt`, if the component author bound one to a\n // declared prop too (AGL-1896). Resolvable here and nowhere else\n // in the instance pipeline: this path starts from a concrete leaf\n // in the definition tree, so \"which declared prop feeds this\n // image's alt\" is a lookup rather than a guess. The attributes\n // panel only ever sees a declared prop's NAME, and no convention\n // says which other prop is its alt — so it does not try.\n const altBinding = resolveInstanceLeafBinding(\n hit.graftedId,\n node.$id,\n definition,\n 'alt',\n )\n onPickMedia((value, asset) => {\n // Written through verbatim (media reference or raw URL — the\n // host app decides; AGL-1215). Snapshot via toJSON like the\n // panel: updateNodeProps REPLACES the props object.\n const current = (canvas.toJSON().nodes as Record<string, any>)[\n node.$id\n ]\n // What the instance renders for the alt prop TODAY — its own\n // override, else the component's default. Not the raw override:\n // a component whose default alt is already a real sentence must\n // not have it replaced just because this instance never set one.\n const altPropName = altBinding?.boundProp\n const effectiveAlt = altPropName\n ? getInstanceEffectivePropText(\n current?.props,\n definition.props,\n altPropName,\n )\n : undefined\n const inheritedAlt = altPropName\n ? inheritedMediaAlt({\n placementAlt: effectiveAlt,\n assetAlt: asset?.alt,\n })\n : undefined\n canvas.updateNodeProps(node, {\n ...current?.props,\n [REUSABLE_INSTANCE_PROP_VALUES_KEY]: {\n ...current?.props?.[REUSABLE_INSTANCE_PROP_VALUES_KEY],\n [propName]: value,\n ...(inheritedAlt && altPropName\n ? { [altPropName]: inheritedAlt }\n : {}),\n },\n })\n })\n return true\n }\n return false\n }\n }, [node])\n\n return cloneElement(\n child,\n mergeProps(child.props, draggable.attributes, {\n ref: mergeRefs(\n ref,\n child.props.ref,\n draggable.setNodeRef,\n droppable.setNodeRef,\n ),\n style,\n sx: {\n '&, &:hover, &:focus': {\n cursor: draggable.isDragging ? 'move' : 'initial',\n },\n },\n }),\n )\n },\n)\n\nDraggableDroppable.displayName = 'DraggableDroppable'\n\nexport default DraggableDroppable\n"],"names":["canvas","FEATURE_FLAG","getInstanceEffectivePropText","inheritedMediaAlt","resolveInstanceLeafBinding","REUSABLE_INSTANCE_COMPONENT_ID","REUSABLE_INSTANCE_PROP_VALUES_KEY","Besigner","mergeRefs","useId","useDraggable","useDroppable","CSS","css","mergeProps","observer","Children","cloneElement","useContext","useEffect","useRef","ComponentPromotionContext","MediaPickerContext","findMarkdownAttributeName","inlineMarkdownEdit","inlineTextEdit","isInlineEditWithin","requestCanvasContextMenu","findInstanceLeafAtPoint","findLeafTextElement","DraggableDroppable","props","node","type","disableDragging","disableDropping","accept","children","idSuffix","id","$id","draggable","data","disabled","droppable","transform","isTransforming","Boolean","child","only","style","outlineWidth","outlineOffset","outlineColor","outlineStyle","translate","Translate","toString","isOver","isDragging","opacity","cursor","pick","isPicking","focus","isNodeHovered","ref","definitions","onPickMedia","instanceEditRef","current","refs","set","delete","handles","listeners","el","addEventListener","handleMouseOver","handleMouseDown","handleDoubleClick","handleContextMenu","removeEventListener","e","stopPropagation","preventDefault","setSelectedNode","setHoveredNode","handlePickClick","pointer","shiftKey","rangeSelectNode","handleNodeSelection","metaKey","ctrlKey","componentId","dnd","canDragNode","handleInstanceDoubleClick","flag","componentSchema","flags","textEditable","editable","ENABLED","root","currentTarget","element","rect","getBoundingClientRect","open","left","top","width","height","undefined","markdownAttribute","markdownElement","markdownRect","stored","refId","definition","container","hit","clientX","clientY","text","graftedId","boundProp","propName","initialText","src","altBinding","value","asset","toJSON","nodes","altPropName","effectiveAlt","inheritedAlt","placementAlt","assetAlt","alt","updateNodeProps","attributes","setNodeRef","sx","displayName"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../../../libs/besigner/feature/designer/src/lib/components/dnd/draggable-droppable.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2023 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type * as Aglyn from '@aglyn/aglyn'\nimport {\n canvas,\n FEATURE_FLAG,\n getInstanceEffectivePropText,\n inheritedMediaAlt,\n isDefinitionPlacement,\n placementDefinitionFor,\n resolveInstanceLeafBinding,\n REUSABLE_INSTANCE_COMPONENT_ID,\n REUSABLE_INSTANCE_PROP_VALUES_KEY,\n} from '@aglyn/aglyn'\nimport * as Besigner from '@aglyn/besigner'\nimport { mergeRefs, useId } from '@aglyn/shared-ui-jsx'\nimport { useDraggable, useDroppable } from '@dnd-kit/core'\nimport { CSS as css } from '@dnd-kit/utilities'\nimport { mergeProps } from '@react-aria/utils'\nimport { observer } from 'mobx-react-lite'\nimport {\n Children,\n cloneElement,\n CSSProperties,\n useContext,\n useEffect,\n useRef,\n} from 'react'\nimport ComponentPromotionContext from '../../contexts/component-promotion-context'\nimport { MediaPickerContext } from '../../contexts/media-picker-context'\nimport {\n findMarkdownAttributeName,\n inlineMarkdownEdit,\n} from '../../utils/inline-markdown-edit.store'\nimport {\n inlineTextEdit,\n isInlineEditWithin,\n} from '../../utils/inline-text-edit.store'\nimport { requestCanvasContextMenu } from '../../utils/canvas-context-menu'\nimport {\n findInstanceLeafAtPoint,\n findPlacementPartAtPoint,\n} from '../../utils/instance-leaf-hit'\nimport { pickPlacementPart } from '../../utils/placement-part-pick'\nimport { findLeafTextElement } from '../../utils/in-place-edit-surface'\n\nexport interface DraggableDroppableProps<T extends Aglyn.NodeSchema<any>> {\n children: JSX.Element\n node: T\n type: Besigner.DragType\n accept: Besigner.DragType[]\n disableDragging?: boolean\n disableDropping?: boolean\n idSuffix?: string\n}\n\nexport const DraggableDroppable = observer(\n <T extends Aglyn.NodeSchema<any>>(props: DraggableDroppableProps<T>) => {\n const {\n node,\n type,\n disableDragging,\n disableDropping,\n accept,\n children,\n idSuffix,\n } = props\n const id = useId(node?.$id)\n\n const draggable = useDraggable({\n id: `${id}:${type}${idSuffix || ''}`,\n data: { type, node },\n disabled: disableDragging,\n })\n\n const droppable = useDroppable({\n id: `${id}:${type}${idSuffix || ''}`,\n data: { type, node, accept },\n disabled: disableDropping,\n })\n\n const transform = draggable.transform\n const isTransforming = Boolean(draggable.transform)\n const child = Children.only(children)\n const style: CSSProperties = {\n ...child.props.style,\n // cursor: 'move',\n outlineWidth: 2,\n outlineOffset: -1,\n outlineColor: 'grey',\n outlineStyle: 'dotted',\n // transform: css.Transform.toString(transform),\n // scale: css.Scale.toString(transform),\n translate: css.Translate.toString(transform),\n\n ...(isTransforming\n ? {\n // transform: `translate3d(${transform.x}px, ${transform.y}px, 0)`,\n // cursor: 'grab',\n }\n : {}),\n ...(droppable.isOver\n ? {\n outlineColor: 'lime',\n outlineStyle: 'solid',\n outlineOffset: '3',\n }\n : {}),\n ...(draggable.isDragging\n ? {\n outlineColor: 'grey',\n outlineStyle: 'double',\n outlineOffset: -1 as any,\n opacity: 0.5,\n cursor: 'move',\n }\n : {}),\n // Element-picker affordance (AGL-574): while the interaction builder\n // is picking a target, every leaf shows the crosshair and the hovered\n // one gets a distinct violet ring — deliberately unlike the normal\n // selection/drop outlines so \"pick this\" reads as its own mode.\n ...(Besigner.pick.isPicking() ? { cursor: 'crosshair' } : {}),\n ...(Besigner.pick.isPicking() && Besigner.focus.isNodeHovered(node)\n ? {\n outlineColor: '#7c4dff',\n outlineStyle: 'solid',\n outlineWidth: 2,\n outlineOffset: 2 as any,\n }\n : {}),\n }\n\n const ref = useRef<HTMLElement>(null)\n\n // Instance-internals double-click (AGL-1304) needs the component\n // definitions (hit-test) and the host's media picker (image props).\n // Read through a ref so the listener effect below keeps its `[node]`\n // dependency — context updates must not re-register DOM listeners.\n const { definitions, formDesigns } = useContext(ComponentPromotionContext)\n const { onPickMedia } = useContext(MediaPickerContext)\n const instanceEditRef = useRef({ definitions, formDesigns, onPickMedia })\n instanceEditRef.current = { definitions, formDesigns, onPickMedia }\n\n useEffect(() => {\n Besigner.refs.set(node.$id, ref)\n return () => {\n Besigner.refs.delete(node.$id)\n }\n }, [node?.$id])\n\n useEffect(() => {\n Besigner.handles.set(node.$id, {\n ...draggable.listeners,\n style: isTransforming ? { cursor: 'grab' } : { cursor: 'move' },\n })\n return () => {\n Besigner.handles.delete(node.$id)\n }\n }, [node.$id, draggable.listeners, isTransforming])\n\n useEffect(() => {\n const el = ref.current\n if (el) {\n el.addEventListener('mouseover', handleMouseOver)\n el.addEventListener('pointerover', handleMouseOver)\n el.addEventListener('mousedown', handleMouseDown)\n el.addEventListener('pointerdown', handleMouseDown)\n el.addEventListener('dblclick', handleDoubleClick)\n el.addEventListener('contextmenu', handleContextMenu)\n\n return () => {\n el.removeEventListener('mouseover', handleMouseOver)\n el.removeEventListener('pointerover', handleMouseOver)\n el.removeEventListener('mousedown', handleMouseDown)\n el.removeEventListener('pointerdown', handleMouseDown)\n el.removeEventListener('dblclick', handleDoubleClick)\n el.removeEventListener('contextmenu', handleContextMenu)\n }\n }\n /**\n * Right-click opens this element's ⋮ menu (AGL-1405).\n *\n * A native listener like its neighbours, not an `onContextMenu` prop:\n * React delegates at the root container and the canvas is a closed\n * shadow root, so a delegated handler never sees the event.\n *\n * Selects first. The menu is rendered by the overlay, which only mounts\n * for a selected or hovered node — and right-clicking an element is a\n * statement about THAT element, so leaving the previous selection in\n * place would open the wrong menu.\n */\n function handleContextMenu(e: Event) {\n if (isInlineEditWithin(el)) {\n // An open text editor keeps the browser's own menu: cut, paste and\n // spellcheck are the whole point of a right-click there.\n e.stopPropagation()\n return\n }\n e.preventDefault()\n e.stopPropagation()\n Besigner.focus.setSelectedNode(node)\n requestCanvasContextMenu(node?.$id)\n }\n function handleMouseOver(e: Event) {\n // Same stand-down, same reason to consume it: a hover repaint while\n // the author is typing is a re-render of the subtree they are\n // editing, and an ancestor leaf would repaint for them instead.\n if (isInlineEditWithin(el)) {\n e.stopPropagation()\n return\n }\n e.preventDefault()\n e.stopPropagation()\n Besigner.focus.setHoveredNode(node)\n }\n function handleMouseDown(e: Event) {\n // Stand down while an edit is open in this leaf OR ANY LEAF INSIDE\n // IT (AGL-2486) — but CONSUME the event rather than ignoring it.\n //\n // Two different mistakes produced the same symptom, an editor that\n // was open, styled and completely uninteractive. First, asking only\n // \"is this leaf the node being edited?\": a nested run — a Span\n // typography inside a Typography — silenced its OWN leaf, the event\n // bubbled to the ancestor leaf, and the ancestor called the\n // `preventDefault` that suppresses caret placement. Second, standing\n // down by simply returning: that also skipped `stopPropagation`, so\n // the gesture carried on up to the viewport, which treats a\n // mousedown on empty canvas as a deselect — and moving the selection\n // off the node closes the editor. A drag meant to select three words\n // tore the editor down instead.\n //\n // So: no `preventDefault`, because that is the caret; but still\n // `stopPropagation`, because nothing else on this canvas should see\n // a gesture aimed at text being edited. `stopPropagation` does not\n // suppress a default action, only delivery.\n if (isInlineEditWithin(el)) {\n e.stopPropagation()\n return\n }\n e.preventDefault()\n e.stopPropagation()\n // Element-picker capture (AGL-574): while the interaction builder is\n // picking, this click resolves the target and exits pick mode —\n // never the normal select/navigate, so the designer's selection and\n // the open interaction stay put.\n if (Besigner.pick.isPicking()) {\n Besigner.pick.handlePickClick(node.$id)\n return\n }\n // Canvas multi-selection modifiers (AGL-12), mirroring the\n // hierarchy panel: Shift ranges, Cmd/Ctrl toggles.\n const pointer = e as globalThis.MouseEvent\n if (pointer.shiftKey) {\n Besigner.focus.rangeSelectNode(node)\n } else {\n Besigner.focus.handleNodeSelection(\n node,\n pointer.metaKey || pointer.ctrlKey,\n )\n if (!pointer.metaKey && !pointer.ctrlKey) pickPartUnder(pointer)\n }\n }\n /**\n * A plain click on a placement (a component instance or a placed form)\n * also says WHICH part of it was under the pointer (AGL-3288), so the\n * Styles and Attributes tabs aim \"Which part?\" at what the author just\n * clicked. The parts themselves stay unselectable — they are drawn\n * inert — so this is the only way a click can reach one.\n */\n function pickPartUnder(pointer: globalThis.MouseEvent) {\n if (!isDefinitionPlacement(node)) return\n const container = ref.current\n if (!container || typeof pointer.clientX !== 'number') return\n const { definitions, formDesigns } = instanceEditRef.current\n const definition = placementDefinitionFor(node, {\n definitions,\n formDesigns,\n })\n if (!definition) return\n const key = findPlacementPartAtPoint(\n container,\n pointer.clientX,\n pointer.clientY,\n node.$id,\n (graftedId) =>\n resolveInstanceLeafBinding(\n graftedId,\n node.$id,\n definition,\n 'children',\n definition.rootId,\n ),\n definition.rootId,\n )\n pickPlacementPart(node.$id, key ?? '')\n }\n function handleDoubleClick(e: Event) {\n // Prop-fed instance internals (AGL-1304): the rendered preview sits\n // under `pointer-events: none`, so the double-click lands HERE, on\n // the instance — hit-test the preview geometrically, map the grafted\n // leaf back to the definition, and if a declared prop feeds it, edit\n // that prop inline. Leaves the component owns stay locked (the\n // selection this dblclick's mousedowns already made puts AGL-1303's\n // \"Edit component\" in the panel — nothing extra to build).\n if (\n node?.componentId === REUSABLE_INSTANCE_COMPONENT_ID &&\n Besigner.dnd.canDragNode(node)\n ) {\n if (handleInstanceDoubleClick(e as globalThis.MouseEvent)) {\n e.preventDefault()\n e.stopPropagation()\n }\n return\n }\n // Locked nodes (layout chrome in the screen besigner) stay read-only —\n // same gate the drag system uses — and while the interaction builder\n // is picking a target, a double-click is two picks, never an edit.\n if (!Besigner.dnd.canDragNode(node) || Besigner.pick.isPicking()) return\n // Inline text editing for components that declare textEditable.\n const flag = node?.componentSchema?.flags?.textEditable\n const editable =\n typeof flag === 'number' && (flag & FEATURE_FLAG.ENABLED) !== 0\n if (editable) {\n e.preventDefault()\n e.stopPropagation()\n const root = e.currentTarget as Element\n // The node's own `<aglyn-text>` whenever it renders one, and the\n // root only as a fallback (AGL-2556). In-place editing EMPTIES\n // whatever it is given, so handing it a composite's root deletes\n // the chrome that composite built — for Accordion Summary that is\n // the content div carrying MUI's `textAlign: 'start'`, without\n // which the UA stylesheet centres the label inside the `<button>`\n // for as long as the edit is open.\n const element = findLeafTextElement(root) ?? root\n const rect = element.getBoundingClientRect()\n inlineTextEdit.open(\n node,\n {\n left: rect.left,\n top: rect.top,\n width: rect.width,\n height: rect.height,\n },\n undefined,\n // The measured element travels with the rect so the overlay can\n // re-measure on scroll instead of drifting (AGL-1644).\n element,\n )\n return\n }\n // In-place markdown editing (AGL-1624): a component whose schema\n // declares a MARKDOWN attribute — the Markdown element's `content` is\n // the one that exists — opens the WYSIWYG over the element, editing\n // the whole document without a trip to the attributes panel.\n //\n // `stopPropagation` on the NATIVE event is what keeps the canvas still:\n // React delegates `onDoubleClick` at the root container, so an event\n // consumed here never reaches `ZoomablePanningComponent`'s\n // double-click-to-zoom. That is the same guarantee the textEditable\n // branch above has always relied on, not a new mechanism.\n const markdownAttribute = findMarkdownAttributeName(node)\n if (!markdownAttribute) return\n e.preventDefault()\n e.stopPropagation()\n const markdownElement = e.currentTarget as Element\n const markdownRect = markdownElement.getBoundingClientRect()\n const stored = (node.props as Record<string, unknown> | undefined)?.[\n markdownAttribute\n ]\n // The two mousedowns of a double-click TOGGLE selection, so by the time\n // this fires the node has been selected and then deselected again.\n // Editing an element is selecting it — the attributes panel should be\n // showing it — and the editor closes when the selection moves off the\n // node, so it has to be put back before the editor opens.\n Besigner.focus.setSelectedNode(node)\n inlineMarkdownEdit.open(\n node,\n {\n left: markdownRect.left,\n top: markdownRect.top,\n width: markdownRect.width,\n height: markdownRect.height,\n },\n markdownAttribute,\n typeof stored === 'string' ? stored : '',\n markdownElement,\n )\n }\n /** True when the double-click resolved to a prop edit it opened. */\n function handleInstanceDoubleClick(pointer: globalThis.MouseEvent) {\n const { definitions, onPickMedia } = instanceEditRef.current\n const refId = (node?.props as { refId?: string } | undefined)?.refId\n const definition = refId ? definitions?.[refId] : undefined\n const container = ref.current\n if (!definition || !container) return false\n const hit = findInstanceLeafAtPoint(\n container,\n pointer.clientX,\n pointer.clientY,\n )\n if (!hit) return false\n // Text first: a leaf whose `children` a declared prop feeds opens\n // the EXISTING inline editor, anchored on the clicked leaf, and the\n // commit writes propValues[prop] on the instance (one undo entry,\n // mirrored to peers like any node edit).\n const text = resolveInstanceLeafBinding(\n hit.graftedId,\n node.$id,\n definition,\n )\n if (text?.boundProp) {\n const rect = hit.element.getBoundingClientRect()\n inlineTextEdit.open(\n node,\n {\n left: rect.left,\n top: rect.top,\n width: rect.width,\n height: rect.height,\n },\n {\n propName: text.boundProp,\n initialText: getInstanceEffectivePropText(\n node.props,\n definition.props,\n text.boundProp,\n ),\n },\n // The GRAFTED leaf, not the instance node: it is what was measured,\n // and it is the one anchor `Besigner.refs` could never supply\n // because it is not a canvas node (AGL-1644).\n hit.element,\n )\n return true\n }\n // Image half: `src` fed by a declared prop opens the host's media\n // picker, committing the picked value to the same propValues slot —\n // the exact pipeline the Attributes panel's Browse button uses.\n const src = resolveInstanceLeafBinding(\n hit.graftedId,\n node.$id,\n definition,\n 'src',\n )\n if (src?.boundProp && onPickMedia) {\n const propName = src.boundProp\n // The SAME leaf's `alt`, if the component author bound one to a\n // declared prop too (AGL-1896). Resolvable here and nowhere else\n // in the instance pipeline: this path starts from a concrete leaf\n // in the definition tree, so \"which declared prop feeds this\n // image's alt\" is a lookup rather than a guess. The attributes\n // panel only ever sees a declared prop's NAME, and no convention\n // says which other prop is its alt — so it does not try.\n const altBinding = resolveInstanceLeafBinding(\n hit.graftedId,\n node.$id,\n definition,\n 'alt',\n )\n onPickMedia((value, asset) => {\n // Written through verbatim (media reference or raw URL — the\n // host app decides; AGL-1215). Snapshot via toJSON like the\n // panel: updateNodeProps REPLACES the props object.\n const current = (canvas.toJSON().nodes as Record<string, any>)[\n node.$id\n ]\n // What the instance renders for the alt prop TODAY — its own\n // override, else the component's default. Not the raw override:\n // a component whose default alt is already a real sentence must\n // not have it replaced just because this instance never set one.\n const altPropName = altBinding?.boundProp\n const effectiveAlt = altPropName\n ? getInstanceEffectivePropText(\n current?.props,\n definition.props,\n altPropName,\n )\n : undefined\n const inheritedAlt = altPropName\n ? inheritedMediaAlt({\n placementAlt: effectiveAlt,\n assetAlt: asset?.alt,\n })\n : undefined\n canvas.updateNodeProps(node, {\n ...current?.props,\n [REUSABLE_INSTANCE_PROP_VALUES_KEY]: {\n ...current?.props?.[REUSABLE_INSTANCE_PROP_VALUES_KEY],\n [propName]: value,\n ...(inheritedAlt && altPropName\n ? { [altPropName]: inheritedAlt }\n : {}),\n },\n })\n })\n return true\n }\n return false\n }\n }, [node])\n\n return cloneElement(\n child,\n mergeProps(child.props, draggable.attributes, {\n ref: mergeRefs(\n ref,\n child.props.ref,\n draggable.setNodeRef,\n droppable.setNodeRef,\n ),\n style,\n sx: {\n '&, &:hover, &:focus': {\n cursor: draggable.isDragging ? 'move' : 'initial',\n },\n },\n }),\n )\n },\n)\n\nDraggableDroppable.displayName = 'DraggableDroppable'\n\nexport default DraggableDroppable\n"],"names":["canvas","FEATURE_FLAG","getInstanceEffectivePropText","inheritedMediaAlt","isDefinitionPlacement","placementDefinitionFor","resolveInstanceLeafBinding","REUSABLE_INSTANCE_COMPONENT_ID","REUSABLE_INSTANCE_PROP_VALUES_KEY","Besigner","mergeRefs","useId","useDraggable","useDroppable","CSS","css","mergeProps","observer","Children","cloneElement","useContext","useEffect","useRef","ComponentPromotionContext","MediaPickerContext","findMarkdownAttributeName","inlineMarkdownEdit","inlineTextEdit","isInlineEditWithin","requestCanvasContextMenu","findInstanceLeafAtPoint","findPlacementPartAtPoint","pickPlacementPart","findLeafTextElement","DraggableDroppable","props","node","type","disableDragging","disableDropping","accept","children","idSuffix","id","$id","draggable","data","disabled","droppable","transform","isTransforming","Boolean","child","only","style","outlineWidth","outlineOffset","outlineColor","outlineStyle","translate","Translate","toString","isOver","isDragging","opacity","cursor","pick","isPicking","focus","isNodeHovered","ref","definitions","formDesigns","onPickMedia","instanceEditRef","current","refs","set","delete","handles","listeners","el","addEventListener","handleMouseOver","handleMouseDown","handleDoubleClick","handleContextMenu","removeEventListener","e","stopPropagation","preventDefault","setSelectedNode","setHoveredNode","handlePickClick","pointer","shiftKey","rangeSelectNode","handleNodeSelection","metaKey","ctrlKey","pickPartUnder","container","clientX","definition","key","clientY","graftedId","rootId","componentId","dnd","canDragNode","handleInstanceDoubleClick","flag","componentSchema","flags","textEditable","editable","ENABLED","root","currentTarget","element","rect","getBoundingClientRect","open","left","top","width","height","undefined","markdownAttribute","markdownElement","markdownRect","stored","refId","hit","text","boundProp","propName","initialText","src","altBinding","value","asset","toJSON","nodes","altPropName","effectiveAlt","inheritedAlt","placementAlt","assetAlt","alt","updateNodeProps","attributes","setNodeRef","sx","displayName"],"mappings":"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"}
@@ -18,9 +18,9 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
18
18
  import { aglyn, CanvasManager, NODE_ROOT_ID, ScreenLinkContext, SiteContext } from "@aglyn/aglyn";
19
19
  // Deep import, NOT the barrel (AGL-2486) — see `plugin-styles-ui.tsx`.
20
20
  import { PluginStyles } from "@aglyn/aglyn/plugin-manager/plugin-styles-ui";
21
- import { AglynNodeRenderer, useAglynSiteTheme } from "@aglyn/aglyn-node-renderer";
21
+ import { AglynNodeRenderer, useAglynSiteSchemeThemes, useAglynSiteTheme } from "@aglyn/aglyn-node-renderer";
22
22
  import { MuiShadowDom, useMuiShadowDomContext } from "@aglyn/shared-ui-jsx";
23
- import { ThemeProvider } from "@aglyn/shared-ui-theme";
23
+ import { SiteSchemeThemesContext, ThemeProvider } from "@aglyn/shared-ui-theme";
24
24
  import { Box, GlobalStyles, Typography } from "@mui/material";
25
25
  import { observer } from "mobx-react-lite";
26
26
  import { useResolvedHostThemeDocument } from "../utils/active-host-theme.js";
@@ -320,25 +320,34 @@ const PreviewShadowDom = MuiShadowDom.div;
320
320
  theme: hostThemeDoc,
321
321
  scheme: 'light'
322
322
  });
323
- return /*#__PURE__*/ _jsxs(ThemeProvider, {
324
- theme: theme,
325
- children: [
326
- /*#__PURE__*/ _jsx(GlobalStyles, {
327
- styles: {
328
- ':host': {
329
- all: 'initial'
323
+ // An "Always dark" element previews dark here too (AGL-3284).
324
+ const schemeThemes = useAglynSiteSchemeThemes({
325
+ container: shadowDom,
326
+ theme: hostThemeDoc,
327
+ active: theme
328
+ });
329
+ return /*#__PURE__*/ _jsx(SiteSchemeThemesContext.Provider, {
330
+ value: schemeThemes,
331
+ children: /*#__PURE__*/ _jsxs(ThemeProvider, {
332
+ theme: theme,
333
+ children: [
334
+ /*#__PURE__*/ _jsx(GlobalStyles, {
335
+ styles: {
336
+ ':host': {
337
+ all: 'initial'
338
+ }
330
339
  }
331
- }
332
- }),
333
- /*#__PURE__*/ _jsx(Box, {
334
- sx: _extends({}, theme.typography.body1, {
335
- color: 'text.primary',
336
- backgroundColor: 'background.default',
337
- width: 1
338
340
  }),
339
- children: children
340
- })
341
- ]
341
+ /*#__PURE__*/ _jsx(Box, {
342
+ sx: _extends({}, theme.typography.body1, {
343
+ color: 'text.primary',
344
+ backgroundColor: 'background.default',
345
+ width: 1
346
+ }),
347
+ children: children
348
+ })
349
+ ]
350
+ })
342
351
  });
343
352
  }
344
353
  export default ElementPreview;