@aglyn/besigner-ui 1.0.0-beta.231 → 1.0.0-beta.232

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 (28) hide show
  1. package/package.json +19 -18
  2. package/src/lib/components/aside-panel.component.js +30 -3
  3. package/src/lib/components/aside-panel.component.js.map +1 -1
  4. package/src/lib/components/besigner-dnd-context.component.js +31 -2
  5. package/src/lib/components/besigner-dnd-context.component.js.map +1 -1
  6. package/src/lib/components/component-accordion-list.js +4 -2
  7. package/src/lib/components/component-accordion-list.js.map +1 -1
  8. package/src/lib/components/component-picker.js +42 -2
  9. package/src/lib/components/component-picker.js.map +1 -1
  10. package/src/lib/components/device-preview-controls.component.d.ts +2 -0
  11. package/src/lib/components/device-preview-controls.component.js +11 -5
  12. package/src/lib/components/device-preview-controls.component.js.map +1 -1
  13. package/src/lib/components/dnd/draggable-droppable.js +39 -0
  14. package/src/lib/components/dnd/draggable-droppable.js.map +1 -1
  15. package/src/lib/components/node-quick-actions.js +210 -174
  16. package/src/lib/components/node-quick-actions.js.map +1 -1
  17. package/src/lib/components/node-tree-view.js +11 -1
  18. package/src/lib/components/node-tree-view.js.map +1 -1
  19. package/src/lib/components/panel-controls.component.d.ts +6 -0
  20. package/src/lib/components/panel-controls.component.js +10 -6
  21. package/src/lib/components/panel-controls.component.js.map +1 -1
  22. package/src/lib/components/viewport-canvas.component.js +8 -0
  23. package/src/lib/components/viewport-canvas.component.js.map +1 -1
  24. package/src/lib/components/workspace-editor.component.js +131 -16
  25. package/src/lib/components/workspace-editor.component.js.map +1 -1
  26. package/src/lib/components/workspace-panel.component.d.ts +18 -0
  27. package/src/lib/components/workspace-panel.component.js +53 -3
  28. package/src/lib/components/workspace-panel.component.js.map +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../../libs/besigner/feature/designer/src/lib/components/component-picker.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 ICON_VARIANT_CLOSE,\n ICON_VARIANT_FILTER,\n} from '@aglyn/shared-data-enums'\nimport { MdiIcon } from '@aglyn/shared-ui-jsx'\nimport {\n AppBar,\n Box,\n Button,\n Collapse,\n Dialog,\n DialogContent,\n Divider,\n DialogProps,\n IconButton,\n Slide,\n Toolbar,\n Typography,\n} from '@mui/material'\nimport type { TransitionProps } from '@mui/material/transitions'\nimport { Observer, observer } from 'mobx-react-lite'\nimport { forwardRef, SyntheticEvent, useCallback, useState } from 'react'\nimport usePickerFilter from '../hooks/use-picker-filter'\nimport useVisibleComponentCategories from '../hooks/use-visible-component-categories'\nimport { describeElement } from '../utils/describe-element'\nimport AccordionListComponent from './accordion-list.component'\nimport ElementDetailView from './element-detail.component'\nimport EmptyResults from './empty-results'\nimport NodeCard from './node-card'\nimport PickerSearchField from './picker-search-field'\n\nconst Transition = forwardRef(function Transition(\n props: TransitionProps & {\n children: React.ReactElement\n },\n ref: React.Ref<unknown>,\n) {\n return <Slide direction=\"up\" ref={ref} {...props} />\n})\n\ntype PickerOption = Aglyn.ComponentSchema<any> | Aglyn.PresetSchema<any>\n\n/**\n * How the two panes share the dialog above the stacking breakpoint\n * (AGL-2486).\n *\n * An even split, because the preview is sized by the pane and nothing else:\n * `ElementPreview` composes at a fixed 1280px stage and scales by\n * `paneWidth / 1280`, so the pane's width IS the preview's size. A 420px\n * pane rendered at 0.30; half of a 1536px dialog renders at 0.57, which is\n * the \"make the preview bigger\" ask answered by arithmetic rather than by a\n * bigger `previewHeight` cap on the same small stage.\n *\n * The grid does not pay for it. At an even split the tiles keep their full\n * `MIN_TILE_WIDTH` — a 1944px screen runs four 166px tiles where the fixed\n * pane ran six 161px ones. Fewer columns, no narrower tiles.\n */\nconst PANE_SPLIT = '1 1 50%'\n\n/**\n * Narrowest a tile may be before its label stops fitting, in px (AGL-2486).\n *\n * The grid used to size its tiles off the VIEWPORT breakpoint (`sm` → 3/12 →\n * four columns) which is the wrong question to ask: the tiles live in a pane\n * whose width is the dialog minus the detail pane, and a viewport breakpoint\n * cannot see the pane. So the detail pane came out of the tiles — four\n * columns in 448px, 94px each, with `Announcement Bar` painting outside its\n * own card. `auto-fill` asks the pane instead, so the two panes stop trading:\n * widening the dialog adds columns, and the detail pane costs columns rather\n * than shrinking every tile.\n *\n * 150px is measured, not chosen: the widest single word in the catalogue\n * (`Announcement`) needs ~100px at `subtitle2`, and the tiles that survive\n * on one line at 150 are the ones that wrapped at 94.\n */\nconst MIN_TILE_WIDTH = 150\n\nexport interface ComponentPickerProps extends DialogProps {\n onSelectItem?: (e: SyntheticEvent, item?: { option: PickerOption }) => void\n}\n\nexport const ComponentPicker = observer(\n forwardRef<any, ComponentPickerProps>((props, forwardRef) => {\n const { open, onClose, onSelectItem, ...rest } = props\n const allItems = useVisibleComponentCategories()\n\n const [filterOpen, setFilterOpen] = useState(false)\n const [selected, setSelected] = useState<PickerOption>(null)\n\n const clearSelected = useCallback(() => setSelected(null), [])\n\n // The SAME search the Elements panel runs (AGL-2486). Both surfaces read\n // one hook so a query cannot mean two different things depending on\n // which picker you happened to open.\n const { filter, items, handleFilterChange } = usePickerFilter(\n allItems,\n clearSelected,\n )\n\n const handleConfirm = useCallback(\n (e: SyntheticEvent) => {\n onSelectItem?.(e, { option: selected })\n },\n [selected, onSelectItem],\n )\n\n const handleClose = useCallback(\n (e: object, reason = 'canceled') => {\n onClose?.(\n e,\n reason as Parameters<NonNullable<DialogProps['onClose']>>[1],\n )\n },\n [onClose],\n )\n\n const handleItemClick = useCallback((e, item: PickerOption) => {\n setSelected((prev) => {\n if (prev && prev?.$id === item?.$id) {\n return null\n }\n return item\n })\n }, [])\n\n return (\n <Dialog\n ref={forwardRef}\n onClose={handleClose}\n open={open}\n // Picking one of ~110 elements is a browsing task, not a confirm box\n // (AGL-2486). At `md` the paper was 900px on a 1944px screen and the\n // detail pane took 420 of it, leaving the grid narrower than the\n // whole dialog had been before the pane existed.\n //\n // `fullWidth` rather than the `width: '100%'` this carried before:\n // `100%` ignores the paper's own 32px margins, so the paper overflows\n // the viewport at any width below its cap. That was invisible while\n // the cap was 900 and would be a horizontal scrollbar on every laptop\n // at this one.\n maxWidth=\"xl\"\n fullWidth\n slots={{ transition: Transition }}\n {...rest}\n >\n {/* Shared app-bar treatment (AGL-704) — see the console's\n secondary-app-bar. enableColorOnDark is required or AppBar\n substitutes its own dark-mode colour. */}\n <AppBar position=\"relative\" color=\"surface\" enableColorOnDark>\n <Toolbar>\n <IconButton\n edge=\"start\"\n color=\"inherit\"\n onClick={handleClose}\n aria-label=\"close\"\n >\n <MdiIcon path={ICON_VARIANT_CLOSE.path} />\n </IconButton>\n <Typography\n variant=\"h6\"\n component=\"div\"\n noWrap\n sx={{\n textOverflow: 'ellipsis',\n ml: 2,\n flex: 1,\n }}\n >\n {'Choose element'}\n </Typography>\n <IconButton\n type=\"button\"\n color=\"inherit\"\n sx={{ p: '10px' }}\n aria-label=\"search\"\n onClick={() => setFilterOpen((prev) => !prev)}\n >\n <MdiIcon path={ICON_VARIANT_FILTER.path} />\n </IconButton>\n <Divider sx={{ height: 28, m: 0.5 }} orientation=\"vertical\" />\n <Button autoFocus color=\"inherit\" onClick={handleClose}>\n {'Close'}\n </Button>\n </Toolbar>\n\n <Collapse orientation=\"vertical\" in={filterOpen}>\n <Toolbar\n component=\"form\"\n variant=\"dense\"\n sx={{\n display: 'flex',\n alignItems: 'center',\n width: 1,\n borderTop: 1,\n borderColor: 'divider',\n }}\n >\n <PickerSearchField value={filter} onChange={handleFilterChange} />\n </Toolbar>\n </Collapse>\n </AppBar>\n {/* Grid left, detail right (AGL-2486). The detail used to be a strip\n under the grid, which gave a preview and five facts about a\n quarter of the height they need while the modal's width sat\n unused. A full-height pane is also the first place the rendered\n preview has room to be worth drawing. */}\n <DialogContent\n dividers\n sx={{\n p: 0,\n display: 'flex',\n // `md` is where an even split stops paying for itself, and the\n // number is derived rather than picked: at a 900px viewport the\n // paper is 836px, so each pane is 418. That is the LAST width at\n // which the grid still runs two full-width tiles (2×150 + a 24\n // gap + 32 of padding = 356) and the preview still composes at\n // the 0.30 scale it had in the old fixed 420px pane. Below it,\n // sharing a row makes both panes worse than stacking, where each\n // gets the dialog's whole width — so the preview gets BIGGER on\n // a tablet, not smaller.\n //\n // Stacking also keeps Confirm reachable. The pane used to be\n // `display: none` under `sm` and Confirm lives inside it, so the\n // dialog could be browsed and never used on a phone.\n flexDirection: { xs: 'column', md: 'row' },\n alignItems: 'stretch',\n minHeight: '60vh',\n }}\n >\n <Box\n sx={{\n flex: { xs: '1 1 auto', md: PANE_SPLIT },\n // Without this the tiles' intrinsic width becomes the pane's\n // floor and the split silently stops being even.\n minWidth: 0,\n // Stacked, the panes scroll together as one column; side by\n // side, the grid scrolls under a detail pane that stays put.\n overflowY: { xs: 'visible', md: 'auto' },\n }}\n >\n {!items?.length ? (\n <EmptyResults sx={{ minHeight: '40vh', height: 1 }} />\n ) : (\n <AccordionListComponent\n // No `key` remount to force the results group open\n // (AGL-2486) — see the Elements panel for why that could not\n // work. The list opens a group when it first appears.\n items={items}\n defaultExpanded={items.map((i) => i.$id)}\n getItemId={(item) => item?.$id}\n onRenderSummary={({ item }) => (\n <Observer>{() => <>{item?.label}</>}</Observer>\n )}\n AccordionDetailsProps={{\n sx: { overflowX: 'hidden' },\n }}\n onRenderDetail={({ item }) => (\n <Observer>\n {() => (\n <Box>\n {/* A CSS grid, not a 12-column one: the column count\n has to come from the pane's own width. See\n MIN_TILE_WIDTH. `1fr` as the max so the last\n column is not a ragged remainder. */}\n <Box\n data-testid=\"picker-element-grid\"\n sx={{\n display: 'grid',\n gridTemplateColumns: `repeat(auto-fill, minmax(${MIN_TILE_WIDTH}px, 1fr))`,\n gap: 3,\n minWidth: 0,\n }}\n >\n {item?.items?.map(\n (\n node: (typeof allItems)[number]['items'][number],\n index: number,\n ) => (\n <Observer key={node?.$id ?? index}>\n {() => (\n <NodeCard\n sx={[\n { cursor: 'pointer' },\n selected?.$id === node?.$id\n ? { borderColor: 'primary.main' }\n : null,\n ]}\n node={node as any}\n onClick={(e) => handleItemClick(e, node)}\n />\n )}\n </Observer>\n ),\n )}\n </Box>\n </Box>\n )}\n </Observer>\n )}\n />\n )}\n </Box>\n <Box\n sx={{\n flex: { xs: '0 0 auto', md: PANE_SPLIT },\n width: { xs: 1, md: 'auto' },\n minWidth: 0,\n display: 'flex',\n flexDirection: 'column',\n // Stacked, the divider that separates the panes is above it,\n // not beside it.\n borderLeft: { xs: 0, md: 1 },\n borderTop: { xs: 1, md: 0 },\n borderColor: 'divider',\n backgroundColor: 'surface.main',\n }}\n >\n <Box sx={{ flex: 1, minHeight: 0, overflowY: 'auto', p: 2 }}>\n {selected ? (\n /* The same content component the panel floats over the\n canvas — one description of an element, two placements. */\n <ElementDetailView\n detail={describeElement(selected)}\n node={selected}\n previewHeight={380}\n />\n ) : (\n <Typography variant=\"caption\" color=\"textSecondary\">\n {'Choose an element to see what it does.'}\n </Typography>\n )}\n </Box>\n {/* Anchored to the pane rather than the dialog: Confirm belongs\n with the thing being confirmed. */}\n <Box sx={{ p: 1.5, borderTop: 1, borderColor: 'divider' }}>\n <Button\n fullWidth\n variant=\"contained\"\n disabled={!selected}\n onClick={handleConfirm}\n >\n {'Confirm'}\n </Button>\n </Box>\n </Box>\n </DialogContent>\n </Dialog>\n )\n }),\n)\nComponentPicker.displayName = 'ComponentPicker'\n\nexport default ComponentPicker\n"],"names":["ICON_VARIANT_CLOSE","ICON_VARIANT_FILTER","MdiIcon","AppBar","Box","Button","Collapse","Dialog","DialogContent","Divider","IconButton","Slide","Toolbar","Typography","Observer","observer","forwardRef","useCallback","useState","usePickerFilter","useVisibleComponentCategories","describeElement","AccordionListComponent","ElementDetailView","EmptyResults","NodeCard","PickerSearchField","Transition","props","ref","direction","PANE_SPLIT","MIN_TILE_WIDTH","ComponentPicker","open","onClose","onSelectItem","rest","allItems","filterOpen","setFilterOpen","selected","setSelected","clearSelected","filter","items","handleFilterChange","handleConfirm","e","option","handleClose","reason","handleItemClick","item","prev","$id","maxWidth","fullWidth","slots","transition","position","color","enableColorOnDark","edge","onClick","aria-label","path","variant","component","noWrap","sx","textOverflow","ml","flex","type","p","height","m","orientation","autoFocus","in","display","alignItems","width","borderTop","borderColor","value","onChange","dividers","flexDirection","xs","md","minHeight","minWidth","overflowY","length","defaultExpanded","map","i","getItemId","onRenderSummary","label","AccordionDetailsProps","overflowX","onRenderDetail","data-testid","gridTemplateColumns","gap","node","index","cursor","borderLeft","backgroundColor","detail","previewHeight","disabled","displayName"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../../libs/besigner/feature/designer/src/lib/components/component-picker.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 ICON_VARIANT_CLOSE,\n ICON_VARIANT_FILTER,\n} from '@aglyn/shared-data-enums'\nimport { MdiIcon } from '@aglyn/shared-ui-jsx'\nimport {\n AppBar,\n Box,\n Button,\n Collapse,\n Dialog,\n DialogActions,\n DialogContent,\n Divider,\n DialogProps,\n IconButton,\n Slide,\n Toolbar,\n Typography,\n useMediaQuery,\n useTheme,\n} from '@mui/material'\nimport type { TransitionProps } from '@mui/material/transitions'\nimport { Observer, observer } from 'mobx-react-lite'\nimport { forwardRef, SyntheticEvent, useCallback, useState } from 'react'\nimport usePickerFilter from '../hooks/use-picker-filter'\nimport useVisibleComponentCategories from '../hooks/use-visible-component-categories'\nimport { describeElement } from '../utils/describe-element'\nimport AccordionListComponent from './accordion-list.component'\nimport ElementDetailView from './element-detail.component'\nimport EmptyResults from './empty-results'\nimport NodeCard from './node-card'\nimport PickerSearchField from './picker-search-field'\n\nconst Transition = forwardRef(function Transition(\n props: TransitionProps & {\n children: React.ReactElement\n },\n ref: React.Ref<unknown>,\n) {\n return <Slide direction=\"up\" ref={ref} {...props} />\n})\n\ntype PickerOption = Aglyn.ComponentSchema<any> | Aglyn.PresetSchema<any>\n\n/**\n * How the two panes share the dialog above the stacking breakpoint\n * (AGL-2486).\n *\n * An even split, because the preview is sized by the pane and nothing else:\n * `ElementPreview` composes at a fixed 1280px stage and scales by\n * `paneWidth / 1280`, so the pane's width IS the preview's size. A 420px\n * pane rendered at 0.30; half of a 1536px dialog renders at 0.57, which is\n * the \"make the preview bigger\" ask answered by arithmetic rather than by a\n * bigger `previewHeight` cap on the same small stage.\n *\n * The grid does not pay for it. At an even split the tiles keep their full\n * `MIN_TILE_WIDTH` — a 1944px screen runs four 166px tiles where the fixed\n * pane ran six 161px ones. Fewer columns, no narrower tiles.\n */\nconst PANE_SPLIT = '1 1 50%'\n\n/**\n * Narrowest a tile may be before its label stops fitting, in px (AGL-2486).\n *\n * The grid used to size its tiles off the VIEWPORT breakpoint (`sm` → 3/12 →\n * four columns) which is the wrong question to ask: the tiles live in a pane\n * whose width is the dialog minus the detail pane, and a viewport breakpoint\n * cannot see the pane. So the detail pane came out of the tiles — four\n * columns in 448px, 94px each, with `Announcement Bar` painting outside its\n * own card. `auto-fill` asks the pane instead, so the two panes stop trading:\n * widening the dialog adds columns, and the detail pane costs columns rather\n * than shrinking every tile.\n *\n * 150px is measured, not chosen: the widest single word in the catalogue\n * (`Announcement`) needs ~100px at `subtitle2`, and the tiles that survive\n * on one line at 150 are the ones that wrapped at 94.\n */\nconst MIN_TILE_WIDTH = 150\n\nexport interface ComponentPickerProps extends DialogProps {\n onSelectItem?: (e: SyntheticEvent, item?: { option: PickerOption }) => void\n}\n\nexport const ComponentPicker = observer(\n forwardRef<any, ComponentPickerProps>((props, forwardRef) => {\n const { open, onClose, onSelectItem, ...rest } = props\n const allItems = useVisibleComponentCategories()\n\n const [filterOpen, setFilterOpen] = useState(false)\n const [selected, setSelected] = useState<PickerOption>(null)\n // A phone has no room for the paper's margins: the picker takes the\n // whole screen there, with its close button where the toolbar puts it.\n const theme = useTheme()\n const fullScreen = useMediaQuery(theme.breakpoints.down('sm'))\n\n const clearSelected = useCallback(() => setSelected(null), [])\n\n // The SAME search the Elements panel runs (AGL-2486). Both surfaces read\n // one hook so a query cannot mean two different things depending on\n // which picker you happened to open.\n const { filter, items, handleFilterChange } = usePickerFilter(\n allItems,\n clearSelected,\n )\n\n const handleConfirm = useCallback(\n (e: SyntheticEvent) => {\n onSelectItem?.(e, { option: selected })\n },\n [selected, onSelectItem],\n )\n\n const handleClose = useCallback(\n (e: object, reason = 'canceled') => {\n onClose?.(\n e,\n reason as Parameters<NonNullable<DialogProps['onClose']>>[1],\n )\n },\n [onClose],\n )\n\n const handleItemClick = useCallback((e, item: PickerOption) => {\n setSelected((prev) => {\n if (prev && prev?.$id === item?.$id) {\n return null\n }\n return item\n })\n }, [])\n\n return (\n <Dialog\n ref={forwardRef}\n onClose={handleClose}\n open={open}\n // Picking one of ~110 elements is a browsing task, not a confirm box\n // (AGL-2486). At `md` the paper was 900px on a 1944px screen and the\n // detail pane took 420 of it, leaving the grid narrower than the\n // whole dialog had been before the pane existed.\n //\n // `fullWidth` rather than the `width: '100%'` this carried before:\n // `100%` ignores the paper's own 32px margins, so the paper overflows\n // the viewport at any width below its cap. That was invisible while\n // the cap was 900 and would be a horizontal scrollbar on every laptop\n // at this one.\n maxWidth=\"xl\"\n fullWidth\n fullScreen={fullScreen}\n slots={{ transition: Transition }}\n {...rest}\n >\n {/* Shared app-bar treatment (AGL-704) — see the console's\n secondary-app-bar. enableColorOnDark is required or AppBar\n substitutes its own dark-mode colour. */}\n <AppBar position=\"relative\" color=\"surface\" enableColorOnDark>\n <Toolbar>\n <IconButton\n edge=\"start\"\n color=\"inherit\"\n onClick={handleClose}\n aria-label=\"close\"\n >\n <MdiIcon path={ICON_VARIANT_CLOSE.path} />\n </IconButton>\n <Typography\n variant=\"h6\"\n component=\"div\"\n noWrap\n sx={{\n textOverflow: 'ellipsis',\n ml: 2,\n flex: 1,\n }}\n >\n {'Choose element'}\n </Typography>\n <IconButton\n type=\"button\"\n color=\"inherit\"\n sx={{ p: '10px' }}\n aria-label=\"search\"\n onClick={() => setFilterOpen((prev) => !prev)}\n >\n <MdiIcon path={ICON_VARIANT_FILTER.path} />\n </IconButton>\n <Divider sx={{ height: 28, m: 0.5 }} orientation=\"vertical\" />\n <Button autoFocus color=\"inherit\" onClick={handleClose}>\n {'Close'}\n </Button>\n </Toolbar>\n\n <Collapse orientation=\"vertical\" in={filterOpen}>\n <Toolbar\n component=\"form\"\n variant=\"dense\"\n sx={{\n display: 'flex',\n alignItems: 'center',\n width: 1,\n borderTop: 1,\n borderColor: 'divider',\n }}\n >\n <PickerSearchField value={filter} onChange={handleFilterChange} />\n </Toolbar>\n </Collapse>\n </AppBar>\n {/* Grid left, detail right (AGL-2486). The detail used to be a strip\n under the grid, which gave a preview and five facts about a\n quarter of the height they need while the modal's width sat\n unused. A full-height pane is also the first place the rendered\n preview has room to be worth drawing. */}\n <DialogContent\n dividers\n sx={{\n p: 0,\n display: 'flex',\n // `md` is where an even split stops paying for itself, and the\n // number is derived rather than picked: at a 900px viewport the\n // paper is 836px, so each pane is 418. That is the LAST width at\n // which the grid still runs two full-width tiles (2×150 + a 24\n // gap + 32 of padding = 356) and the preview still composes at\n // the 0.30 scale it had in the old fixed 420px pane. Below it,\n // sharing a row makes both panes worse than stacking, where each\n // gets the dialog's whole width — so the preview gets BIGGER on\n // a tablet, not smaller.\n //\n // Stacking also keeps Confirm reachable. The pane used to be\n // `display: none` under `sm` and Confirm lives inside it, so the\n // dialog could be browsed and never used on a phone.\n flexDirection: { xs: 'column', md: 'row' },\n alignItems: 'stretch',\n minHeight: '60vh',\n }}\n >\n <Box\n sx={{\n flex: { xs: '1 1 auto', md: PANE_SPLIT },\n // Without this the tiles' intrinsic width becomes the pane's\n // floor and the split silently stops being even.\n minWidth: 0,\n // Stacked, the panes scroll together as one column; side by\n // side, the grid scrolls under a detail pane that stays put.\n overflowY: { xs: 'visible', md: 'auto' },\n }}\n >\n {!items?.length ? (\n <EmptyResults sx={{ minHeight: '40vh', height: 1 }} />\n ) : (\n <AccordionListComponent\n // No `key` remount to force the results group open\n // (AGL-2486) — see the Elements panel for why that could not\n // work. The list opens a group when it first appears.\n items={items}\n defaultExpanded={items.map((i) => i.$id)}\n getItemId={(item) => item?.$id}\n onRenderSummary={({ item }) => (\n <Observer>{() => <>{item?.label}</>}</Observer>\n )}\n AccordionDetailsProps={{\n sx: { overflowX: 'hidden' },\n }}\n onRenderDetail={({ item }) => (\n <Observer>\n {() => (\n <Box>\n {/* A CSS grid, not a 12-column one: the column count\n has to come from the pane's own width. See\n MIN_TILE_WIDTH. `1fr` as the max so the last\n column is not a ragged remainder. */}\n <Box\n data-testid=\"picker-element-grid\"\n sx={{\n display: 'grid',\n gridTemplateColumns: `repeat(auto-fill, minmax(${MIN_TILE_WIDTH}px, 1fr))`,\n gap: 3,\n minWidth: 0,\n }}\n >\n {item?.items?.map(\n (\n node: (typeof allItems)[number]['items'][number],\n index: number,\n ) => (\n <Observer key={node?.$id ?? index}>\n {() => (\n <NodeCard\n sx={[\n { cursor: 'pointer' },\n selected?.$id === node?.$id\n ? { borderColor: 'primary.main' }\n : null,\n ]}\n node={node as any}\n onClick={(e) => handleItemClick(e, node)}\n />\n )}\n </Observer>\n ),\n )}\n </Box>\n </Box>\n )}\n </Observer>\n )}\n />\n )}\n </Box>\n <Box\n sx={{\n flex: { xs: '0 0 auto', md: PANE_SPLIT },\n width: { xs: 1, md: 'auto' },\n minWidth: 0,\n display: 'flex',\n flexDirection: 'column',\n // Stacked, the divider that separates the panes is above it,\n // not beside it.\n borderLeft: { xs: 0, md: 1 },\n borderTop: { xs: 1, md: 0 },\n borderColor: 'divider',\n backgroundColor: 'surface.main',\n }}\n >\n <Box sx={{ flex: 1, minHeight: 0, overflowY: 'auto', p: 2 }}>\n {selected ? (\n /* The same content component the panel floats over the\n canvas — one description of an element, two placements. */\n <ElementDetailView\n detail={describeElement(selected)}\n node={selected}\n previewHeight={380}\n />\n ) : (\n <Typography variant=\"caption\" color=\"textSecondary\">\n {'Choose an element to see what it does.'}\n </Typography>\n )}\n </Box>\n {/* Anchored to the pane rather than the dialog: Confirm belongs\n with the thing being confirmed. */}\n <Box\n sx={{\n p: 1.5,\n borderTop: 1,\n borderColor: 'divider',\n display: { xs: 'none', md: 'block' },\n }}\n >\n <Button\n fullWidth\n variant=\"contained\"\n disabled={!selected}\n onClick={handleConfirm}\n >\n {'Confirm'}\n </Button>\n </Box>\n </Box>\n </DialogContent>\n {/* Stacked below `md`, the pane above sits under the whole catalog,\n so Confirm moves to the dialog's own action bar — outside the\n scroll, on screen whichever tile was tapped — and names the\n choice, since its preview is out of sight. */}\n <DialogActions\n sx={{\n display: { xs: 'flex', md: 'none' },\n gap: 1,\n borderTop: 1,\n borderColor: 'divider',\n }}\n >\n <Typography variant=\"body2\" noWrap sx={{ flex: 1, minWidth: 0 }}>\n {selected ? describeElement(selected).name : 'Choose an element'}\n </Typography>\n <Button\n variant=\"contained\"\n disabled={!selected}\n onClick={handleConfirm}\n sx={{ flexShrink: 0 }}\n >\n {'Confirm'}\n </Button>\n </DialogActions>\n </Dialog>\n )\n }),\n)\nComponentPicker.displayName = 'ComponentPicker'\n\nexport default ComponentPicker\n"],"names":["ICON_VARIANT_CLOSE","ICON_VARIANT_FILTER","MdiIcon","AppBar","Box","Button","Collapse","Dialog","DialogActions","DialogContent","Divider","IconButton","Slide","Toolbar","Typography","useMediaQuery","useTheme","Observer","observer","forwardRef","useCallback","useState","usePickerFilter","useVisibleComponentCategories","describeElement","AccordionListComponent","ElementDetailView","EmptyResults","NodeCard","PickerSearchField","Transition","props","ref","direction","PANE_SPLIT","MIN_TILE_WIDTH","ComponentPicker","open","onClose","onSelectItem","rest","allItems","filterOpen","setFilterOpen","selected","setSelected","theme","fullScreen","breakpoints","down","clearSelected","filter","items","handleFilterChange","handleConfirm","e","option","handleClose","reason","handleItemClick","item","prev","$id","maxWidth","fullWidth","slots","transition","position","color","enableColorOnDark","edge","onClick","aria-label","path","variant","component","noWrap","sx","textOverflow","ml","flex","type","p","height","m","orientation","autoFocus","in","display","alignItems","width","borderTop","borderColor","value","onChange","dividers","flexDirection","xs","md","minHeight","minWidth","overflowY","length","defaultExpanded","map","i","getItemId","onRenderSummary","label","AccordionDetailsProps","overflowX","onRenderDetail","data-testid","gridTemplateColumns","gap","node","index","cursor","borderLeft","backgroundColor","detail","previewHeight","disabled","name","flexShrink","displayName"],"mappings":"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"}
@@ -16,6 +16,8 @@
16
16
  */
17
17
  import { MenuProps } from '@aglyn/shared-ui-jsx';
18
18
  export interface DevicePreviewControlsProps extends Partial<MenuProps> {
19
+ /** Show the active device's icon without its name, for a narrow toolbar. */
20
+ iconOnly?: boolean;
19
21
  }
20
22
  declare const DevicePreviewControlsComponent: import("react").ForwardRefExoticComponent<Omit<DevicePreviewControlsProps, "ref"> & import("react").RefAttributes<any>>;
21
23
  export { DevicePreviewControlsComponent };
@@ -1,4 +1,5 @@
1
1
  import { _ as _extends } from "@swc/helpers/_/_extends";
2
+ import { _ as _object_without_properties_loose } from "@swc/helpers/_/_object_without_properties_loose";
2
3
  import { jsx as _jsx } from "react/jsx-runtime";
3
4
  /**
4
5
  * @license
@@ -66,7 +67,9 @@ const devicePreviewOptions = [
66
67
  }
67
68
  ];
68
69
  const DevicePreviewControlsComponent = /*#__PURE__*/ forwardRef((props, ref)=>{
69
- const {} = props, rest = _extends({}, props);
70
+ const { iconOnly } = props, rest = _object_without_properties_loose(props, [
71
+ "iconOnly"
72
+ ]);
70
73
  const [devicePreview, setDevicePreview] = useAglynBesignerFlag('devicePreview');
71
74
  const [anchorEl, setAnchorEl] = useState(null);
72
75
  const [devicesMenuOpen, setDevicesMenuOpen] = useState(false);
@@ -127,16 +130,19 @@ const DevicePreviewControlsComponent = /*#__PURE__*/ forwardRef((props, ref)=>{
127
130
  fontSize: "inherit",
128
131
  path: ICON_VARIANT_MENU_DOWN.path
129
132
  }),
130
- sx: {
133
+ sx: _extends({
131
134
  borderColor: 'divider',
132
135
  '& .MuiButton-endIcon': {
133
136
  ml: 0.15
134
137
  },
135
138
  '& .MuiButton-startIcon': {
136
- mr: 0.85
139
+ mr: iconOnly ? 0 : 0.85
137
140
  }
138
- },
139
- children: activeDevice.children
141
+ }, iconOnly ? {
142
+ minWidth: 0,
143
+ px: 0.75
144
+ } : {}),
145
+ children: iconOnly ? null : activeDevice.children
140
146
  })
141
147
  })
142
148
  })
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../../libs/besigner/feature/designer/src/lib/components/device-preview-controls.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 { BesignerDeviceFlag } from '@aglyn/besigner'\nimport {\n ICON_VARIANT_FLUID_RESPONSIVE,\n ICON_VARIANT_LAPTOP,\n ICON_VARIANT_MENU_DOWN,\n ICON_VARIANT_MOBILE,\n ICON_VARIANT_MONITOR_LARGE,\n ICON_VARIANT_MONITOR_SMALL,\n ICON_VARIANT_SYMBOL_CONFIRMED,\n ICON_VARIANT_TABLET,\n} from '@aglyn/shared-data-enums'\nimport { MdiIcon, Menu, MenuProps } from '@aglyn/shared-ui-jsx'\nimport {\n Button as MuiButton,\n ButtonGroup as MuiButtonGroup,\n Tooltip as MuiTooltip,\n} from '@mui/material'\nimport {\n forwardRef,\n type MouseEvent,\n useCallback,\n useMemo,\n useState,\n} from 'react'\nimport useAglynBesignerFlag from '../hooks/use-aglyn-besigner-flag'\n\nconst devicePreviewOptions = [\n {\n children: 'Fluid Responsive',\n value: BesignerDeviceFlag.RESPONSIVE,\n icon: { path: ICON_VARIANT_FLUID_RESPONSIVE.path },\n },\n {\n children: 'XS - Mobile',\n value: BesignerDeviceFlag.XS,\n icon: { path: ICON_VARIANT_MOBILE.path },\n },\n {\n children: 'SM - Tablet',\n value: BesignerDeviceFlag.SM,\n icon: { path: ICON_VARIANT_TABLET.path },\n },\n {\n children: 'MD - Laptop',\n value: BesignerDeviceFlag.MD,\n icon: { path: ICON_VARIANT_LAPTOP.path },\n },\n {\n children: 'LG - Desktop',\n value: BesignerDeviceFlag.LG,\n icon: { path: ICON_VARIANT_MONITOR_SMALL.path },\n },\n {\n children: 'XL - Widescreen',\n value: BesignerDeviceFlag.XL,\n icon: { path: ICON_VARIANT_MONITOR_LARGE.path },\n },\n]\n\nexport interface DevicePreviewControlsProps extends Partial<MenuProps> {}\n\nconst DevicePreviewControlsComponent = forwardRef<\n any,\n DevicePreviewControlsProps\n>((props, ref) => {\n const { ...rest } = props\n\n const [devicePreview, setDevicePreview] =\n useAglynBesignerFlag('devicePreview')\n const [anchorEl, setAnchorEl] = useState<Element>(null)\n const [devicesMenuOpen, setDevicesMenuOpen] = useState(false)\n const activeDevice = useMemo(\n () =>\n devicePreviewOptions.find((i) => i.value === devicePreview) ||\n devicePreviewOptions[0],\n [devicePreview],\n )\n\n const handleMenuClose = useCallback((event: MouseEvent<HTMLElement>) => {\n setDevicesMenuOpen(false)\n }, [])\n const handleOpenMenu = useCallback((event: MouseEvent<HTMLElement>) => {\n setDevicesMenuOpen(true)\n }, [])\n const handleMenuClick =\n (device: BesignerDeviceFlag) => (event: MouseEvent<HTMLElement>) => {\n setDevicePreview(device)\n setDevicesMenuOpen(false)\n }\n\n return (\n <Menu\n ref={ref}\n id=\"aglyn:device-preview\"\n onClick={handleMenuClose}\n horizontalOrigin=\"right\"\n items={[\n {\n type: 'subheader',\n children: 'Artboard preview mode',\n sx: { lineHeight: (theme) => theme.typography.pxToRem(32) },\n },\n ...devicePreviewOptions.map((item) => ({\n key: item.value,\n selected: item.value === activeDevice.value,\n disabled: item.value === activeDevice.value,\n onClick: handleMenuClick(item.value),\n endIcon:\n item.value !== activeDevice.value\n ? undefined\n : {\n path: ICON_VARIANT_SYMBOL_CONFIRMED.path,\n },\n ...item,\n })),\n ]}\n {...rest}\n >\n <MuiButtonGroup size=\"small\" variant=\"outlined\" color=\"inherit\">\n <MuiTooltip title={'Device preview mode'}>\n <MuiButton\n aria-label=\"device preview mode\"\n aria-haspopup=\"menu\"\n aria-controls={devicesMenuOpen ? 'aglyn:device-preview' : undefined}\n aria-expanded={devicesMenuOpen ? 'true' : undefined}\n ref={setAnchorEl}\n onClick={handleOpenMenu}\n startIcon={<MdiIcon fontSize=\"inherit\" {...activeDevice.icon} />}\n endIcon={\n <MdiIcon fontSize=\"inherit\" path={ICON_VARIANT_MENU_DOWN.path} />\n }\n sx={{\n borderColor: 'divider',\n '& .MuiButton-endIcon': { ml: 0.15 },\n '& .MuiButton-startIcon': { mr: 0.85 },\n }}\n >\n {activeDevice.children}\n </MuiButton>\n </MuiTooltip>\n </MuiButtonGroup>\n </Menu>\n )\n})\nDevicePreviewControlsComponent.displayName = 'DevicePreviewControlsComponent'\nDevicePreviewControlsComponent.aglyn = true\n\nexport { DevicePreviewControlsComponent }\nexport default DevicePreviewControlsComponent\n"],"names":["BesignerDeviceFlag","ICON_VARIANT_FLUID_RESPONSIVE","ICON_VARIANT_LAPTOP","ICON_VARIANT_MENU_DOWN","ICON_VARIANT_MOBILE","ICON_VARIANT_MONITOR_LARGE","ICON_VARIANT_MONITOR_SMALL","ICON_VARIANT_SYMBOL_CONFIRMED","ICON_VARIANT_TABLET","MdiIcon","Menu","Button","MuiButton","ButtonGroup","MuiButtonGroup","Tooltip","MuiTooltip","forwardRef","useCallback","useMemo","useState","useAglynBesignerFlag","devicePreviewOptions","children","value","RESPONSIVE","icon","path","XS","SM","MD","LG","XL","DevicePreviewControlsComponent","props","ref","rest","devicePreview","setDevicePreview","anchorEl","setAnchorEl","devicesMenuOpen","setDevicesMenuOpen","activeDevice","find","i","handleMenuClose","event","handleOpenMenu","handleMenuClick","device","id","onClick","horizontalOrigin","items","type","sx","lineHeight","theme","typography","pxToRem","map","item","key","selected","disabled","endIcon","undefined","size","variant","color","title","aria-label","aria-haspopup","aria-controls","aria-expanded","startIcon","fontSize","borderColor","ml","mr","displayName","aglyn"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../../libs/besigner/feature/designer/src/lib/components/device-preview-controls.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 { BesignerDeviceFlag } from '@aglyn/besigner'\nimport {\n ICON_VARIANT_FLUID_RESPONSIVE,\n ICON_VARIANT_LAPTOP,\n ICON_VARIANT_MENU_DOWN,\n ICON_VARIANT_MOBILE,\n ICON_VARIANT_MONITOR_LARGE,\n ICON_VARIANT_MONITOR_SMALL,\n ICON_VARIANT_SYMBOL_CONFIRMED,\n ICON_VARIANT_TABLET,\n} from '@aglyn/shared-data-enums'\nimport { MdiIcon, Menu, MenuProps } from '@aglyn/shared-ui-jsx'\nimport {\n Button as MuiButton,\n ButtonGroup as MuiButtonGroup,\n Tooltip as MuiTooltip,\n} from '@mui/material'\nimport {\n forwardRef,\n type MouseEvent,\n useCallback,\n useMemo,\n useState,\n} from 'react'\nimport useAglynBesignerFlag from '../hooks/use-aglyn-besigner-flag'\n\nconst devicePreviewOptions = [\n {\n children: 'Fluid Responsive',\n value: BesignerDeviceFlag.RESPONSIVE,\n icon: { path: ICON_VARIANT_FLUID_RESPONSIVE.path },\n },\n {\n children: 'XS - Mobile',\n value: BesignerDeviceFlag.XS,\n icon: { path: ICON_VARIANT_MOBILE.path },\n },\n {\n children: 'SM - Tablet',\n value: BesignerDeviceFlag.SM,\n icon: { path: ICON_VARIANT_TABLET.path },\n },\n {\n children: 'MD - Laptop',\n value: BesignerDeviceFlag.MD,\n icon: { path: ICON_VARIANT_LAPTOP.path },\n },\n {\n children: 'LG - Desktop',\n value: BesignerDeviceFlag.LG,\n icon: { path: ICON_VARIANT_MONITOR_SMALL.path },\n },\n {\n children: 'XL - Widescreen',\n value: BesignerDeviceFlag.XL,\n icon: { path: ICON_VARIANT_MONITOR_LARGE.path },\n },\n]\n\nexport interface DevicePreviewControlsProps extends Partial<MenuProps> {\n /** Show the active device's icon without its name, for a narrow toolbar. */\n iconOnly?: boolean\n}\n\nconst DevicePreviewControlsComponent = forwardRef<\n any,\n DevicePreviewControlsProps\n>((props, ref) => {\n const { iconOnly, ...rest } = props\n\n const [devicePreview, setDevicePreview] =\n useAglynBesignerFlag('devicePreview')\n const [anchorEl, setAnchorEl] = useState<Element>(null)\n const [devicesMenuOpen, setDevicesMenuOpen] = useState(false)\n const activeDevice = useMemo(\n () =>\n devicePreviewOptions.find((i) => i.value === devicePreview) ||\n devicePreviewOptions[0],\n [devicePreview],\n )\n\n const handleMenuClose = useCallback((event: MouseEvent<HTMLElement>) => {\n setDevicesMenuOpen(false)\n }, [])\n const handleOpenMenu = useCallback((event: MouseEvent<HTMLElement>) => {\n setDevicesMenuOpen(true)\n }, [])\n const handleMenuClick =\n (device: BesignerDeviceFlag) => (event: MouseEvent<HTMLElement>) => {\n setDevicePreview(device)\n setDevicesMenuOpen(false)\n }\n\n return (\n <Menu\n ref={ref}\n id=\"aglyn:device-preview\"\n onClick={handleMenuClose}\n horizontalOrigin=\"right\"\n items={[\n {\n type: 'subheader',\n children: 'Artboard preview mode',\n sx: { lineHeight: (theme) => theme.typography.pxToRem(32) },\n },\n ...devicePreviewOptions.map((item) => ({\n key: item.value,\n selected: item.value === activeDevice.value,\n disabled: item.value === activeDevice.value,\n onClick: handleMenuClick(item.value),\n endIcon:\n item.value !== activeDevice.value\n ? undefined\n : {\n path: ICON_VARIANT_SYMBOL_CONFIRMED.path,\n },\n ...item,\n })),\n ]}\n {...rest}\n >\n <MuiButtonGroup size=\"small\" variant=\"outlined\" color=\"inherit\">\n <MuiTooltip title={'Device preview mode'}>\n <MuiButton\n aria-label=\"device preview mode\"\n aria-haspopup=\"menu\"\n aria-controls={devicesMenuOpen ? 'aglyn:device-preview' : undefined}\n aria-expanded={devicesMenuOpen ? 'true' : undefined}\n ref={setAnchorEl}\n onClick={handleOpenMenu}\n startIcon={<MdiIcon fontSize=\"inherit\" {...activeDevice.icon} />}\n endIcon={\n <MdiIcon fontSize=\"inherit\" path={ICON_VARIANT_MENU_DOWN.path} />\n }\n sx={{\n borderColor: 'divider',\n '& .MuiButton-endIcon': { ml: 0.15 },\n '& .MuiButton-startIcon': { mr: iconOnly ? 0 : 0.85 },\n ...(iconOnly ? { minWidth: 0, px: 0.75 } : {}),\n }}\n >\n {iconOnly ? null : activeDevice.children}\n </MuiButton>\n </MuiTooltip>\n </MuiButtonGroup>\n </Menu>\n )\n})\nDevicePreviewControlsComponent.displayName = 'DevicePreviewControlsComponent'\nDevicePreviewControlsComponent.aglyn = true\n\nexport { DevicePreviewControlsComponent }\nexport default DevicePreviewControlsComponent\n"],"names":["BesignerDeviceFlag","ICON_VARIANT_FLUID_RESPONSIVE","ICON_VARIANT_LAPTOP","ICON_VARIANT_MENU_DOWN","ICON_VARIANT_MOBILE","ICON_VARIANT_MONITOR_LARGE","ICON_VARIANT_MONITOR_SMALL","ICON_VARIANT_SYMBOL_CONFIRMED","ICON_VARIANT_TABLET","MdiIcon","Menu","Button","MuiButton","ButtonGroup","MuiButtonGroup","Tooltip","MuiTooltip","forwardRef","useCallback","useMemo","useState","useAglynBesignerFlag","devicePreviewOptions","children","value","RESPONSIVE","icon","path","XS","SM","MD","LG","XL","DevicePreviewControlsComponent","props","ref","iconOnly","rest","devicePreview","setDevicePreview","anchorEl","setAnchorEl","devicesMenuOpen","setDevicesMenuOpen","activeDevice","find","i","handleMenuClose","event","handleOpenMenu","handleMenuClick","device","id","onClick","horizontalOrigin","items","type","sx","lineHeight","theme","typography","pxToRem","map","item","key","selected","disabled","endIcon","undefined","size","variant","color","title","aria-label","aria-haspopup","aria-controls","aria-expanded","startIcon","fontSize","borderColor","ml","mr","minWidth","px","displayName","aglyn"],"mappings":"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"}
@@ -98,6 +98,9 @@ export const DraggableDroppable = observer((props)=>{
98
98
  formDesigns,
99
99
  onPickMedia
100
100
  };
101
+ // Same reason: the touch listener below reads the live dnd-kit handlers.
102
+ const listenersRef = useRef(draggable.listeners);
103
+ listenersRef.current = draggable.listeners;
101
104
  useEffect(()=>{
102
105
  Besigner.refs.set(node.$id, ref);
103
106
  return ()=>{
@@ -124,6 +127,11 @@ export const DraggableDroppable = observer((props)=>{
124
127
  ]);
125
128
  useEffect(()=>{
126
129
  const el = ref.current;
130
+ /**
131
+ * Whether this element was already selected when the latest press on
132
+ * it began. Read by the touch drag below, and reset by it, because the
133
+ * press that selects an element must not also pick it up.
134
+ */ let selectedAtPress = false;
127
135
  if (el) {
128
136
  el.addEventListener('mouseover', handleMouseOver);
129
137
  el.addEventListener('pointerover', handleMouseOver);
@@ -131,6 +139,7 @@ export const DraggableDroppable = observer((props)=>{
131
139
  el.addEventListener('pointerdown', handleMouseDown);
132
140
  el.addEventListener('dblclick', handleDoubleClick);
133
141
  el.addEventListener('contextmenu', handleContextMenu);
142
+ el.addEventListener('touchstart', handleTouchStart);
134
143
  return ()=>{
135
144
  el.removeEventListener('mouseover', handleMouseOver);
136
145
  el.removeEventListener('pointerover', handleMouseOver);
@@ -138,9 +147,36 @@ export const DraggableDroppable = observer((props)=>{
138
147
  el.removeEventListener('pointerdown', handleMouseDown);
139
148
  el.removeEventListener('dblclick', handleDoubleClick);
140
149
  el.removeEventListener('contextmenu', handleContextMenu);
150
+ el.removeEventListener('touchstart', handleTouchStart);
141
151
  };
142
152
  }
143
153
  /**
154
+ * Press and hold a selected element to drag it on a touch screen.
155
+ *
156
+ * The element itself has never been a drag source: a mouse drags it by
157
+ * the grip in its toolbar, and on a mouse that costs nothing, because a
158
+ * press on the body selects it and the page stays put. A finger pressing
159
+ * the body scrolls the page instead, which is what an author trying to
160
+ * move the element saw. Handing the press to dnd-kit's TouchSensor
161
+ * keeps a swipe a scroll — the sensor waits for a still hold before it
162
+ * takes over — and makes a hold a drag.
163
+ *
164
+ * Native, like its neighbours: the canvas is a closed shadow root, so a
165
+ * React `onTouchStart` would never fire. The sensor reads only
166
+ * `nativeEvent`, and marks it, so an ancestor that is also selected
167
+ * cannot claim the same touch.
168
+ */ function handleTouchStart(e) {
169
+ var _listenersRef_current_onTouchStart, _listenersRef_current;
170
+ const selected = selectedAtPress;
171
+ selectedAtPress = false;
172
+ if (!selected || isInlineEditWithin(el) || Besigner.pick.isPicking()) {
173
+ return;
174
+ }
175
+ (_listenersRef_current = listenersRef.current) == null ? void 0 : (_listenersRef_current_onTouchStart = _listenersRef_current.onTouchStart) == null ? void 0 : _listenersRef_current_onTouchStart.call(_listenersRef_current, {
176
+ nativeEvent: e
177
+ });
178
+ }
179
+ /**
144
180
  * Right-click opens this element's ⋮ menu (AGL-1405).
145
181
  *
146
182
  * A native listener like its neighbours, not an `onContextMenu` prop:
@@ -199,6 +235,9 @@ export const DraggableDroppable = observer((props)=>{
199
235
  e.stopPropagation();
200
236
  return;
201
237
  }
238
+ if (e.type === 'pointerdown') {
239
+ selectedAtPress = Besigner.focus.isNodeSelected(node);
240
+ }
202
241
  e.preventDefault();
203
242
  e.stopPropagation();
204
243
  // Element-picker capture (AGL-574): while the interaction builder is
@@ -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 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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"}
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 // Same reason: the touch listener below reads the live dnd-kit handlers.\n const listenersRef = useRef(draggable.listeners)\n listenersRef.current = draggable.listeners\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 /**\n * Whether this element was already selected when the latest press on\n * it began. Read by the touch drag below, and reset by it, because the\n * press that selects an element must not also pick it up.\n */\n let selectedAtPress = false\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 el.addEventListener('touchstart', handleTouchStart)\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 el.removeEventListener('touchstart', handleTouchStart)\n }\n }\n /**\n * Press and hold a selected element to drag it on a touch screen.\n *\n * The element itself has never been a drag source: a mouse drags it by\n * the grip in its toolbar, and on a mouse that costs nothing, because a\n * press on the body selects it and the page stays put. A finger pressing\n * the body scrolls the page instead, which is what an author trying to\n * move the element saw. Handing the press to dnd-kit's TouchSensor\n * keeps a swipe a scroll — the sensor waits for a still hold before it\n * takes over — and makes a hold a drag.\n *\n * Native, like its neighbours: the canvas is a closed shadow root, so a\n * React `onTouchStart` would never fire. The sensor reads only\n * `nativeEvent`, and marks it, so an ancestor that is also selected\n * cannot claim the same touch.\n */\n function handleTouchStart(e: TouchEvent) {\n const selected = selectedAtPress\n selectedAtPress = false\n if (!selected || isInlineEditWithin(el) || Besigner.pick.isPicking()) {\n return\n }\n listenersRef.current?.onTouchStart?.({ nativeEvent: e } as any)\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 if (e.type === 'pointerdown') {\n selectedAtPress = Besigner.focus.isNodeSelected(node)\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","listenersRef","listeners","refs","set","delete","handles","el","selectedAtPress","addEventListener","handleMouseOver","handleMouseDown","handleDoubleClick","handleContextMenu","handleTouchStart","removeEventListener","e","selected","onTouchStart","nativeEvent","stopPropagation","preventDefault","setSelectedNode","setHoveredNode","isNodeSelected","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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"}