@namroodinc/liquid-editorial 0.1.7 → 0.1.8

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 (96) hide show
  1. package/AGENTS.md +2 -0
  2. package/UPGRADING.md +25 -0
  3. package/dist/chunks/Board-BLhB5_xx.cjs +2 -0
  4. package/dist/chunks/Board-BLhB5_xx.cjs.map +1 -0
  5. package/dist/chunks/Board-BTFSzn6c.js +239 -0
  6. package/dist/chunks/Board-BTFSzn6c.js.map +1 -0
  7. package/dist/chunks/EntryEditor-CcyYtH74.js +191 -0
  8. package/dist/chunks/EntryEditor-CcyYtH74.js.map +1 -0
  9. package/dist/chunks/EntryEditor-DIpc1iPF.cjs +2 -0
  10. package/dist/chunks/EntryEditor-DIpc1iPF.cjs.map +1 -0
  11. package/dist/chunks/{RichTextEditor-DC_0oN1s.cjs → RichTextEditor-BxtlbjXj.cjs} +2 -2
  12. package/dist/chunks/RichTextEditor-BxtlbjXj.cjs.map +1 -0
  13. package/dist/chunks/{RichTextEditor-CSpjsL1z.js → RichTextEditor-Dnx4i7bQ.js} +104 -93
  14. package/dist/chunks/RichTextEditor-Dnx4i7bQ.js.map +1 -0
  15. package/dist/chunks/mergeRefs-Bc3qS0Tt.cjs +2 -0
  16. package/dist/chunks/mergeRefs-Bc3qS0Tt.cjs.map +1 -0
  17. package/dist/chunks/mergeRefs-DGM402mE.js +10 -0
  18. package/dist/chunks/mergeRefs-DGM402mE.js.map +1 -0
  19. package/dist/components/Board/index.cjs +1 -2
  20. package/dist/components/Board/index.d.cts.map +1 -1
  21. package/dist/components/Board/index.d.ts.map +1 -1
  22. package/dist/components/Board/index.js +1 -231
  23. package/dist/components/Board/keyboard.d.cts +7 -0
  24. package/dist/components/Board/keyboard.d.cts.map +1 -0
  25. package/dist/components/Board/keyboard.d.ts +7 -0
  26. package/dist/components/Board/keyboard.d.ts.map +1 -0
  27. package/dist/components/Board/keyboard.test.d.cts +2 -0
  28. package/dist/components/Board/keyboard.test.d.cts.map +1 -0
  29. package/dist/components/Board/keyboard.test.d.ts +2 -0
  30. package/dist/components/Board/keyboard.test.d.ts.map +1 -0
  31. package/dist/components/Board/moves.d.cts +5 -0
  32. package/dist/components/Board/moves.d.cts.map +1 -0
  33. package/dist/components/Board/moves.d.ts +5 -0
  34. package/dist/components/Board/moves.d.ts.map +1 -0
  35. package/dist/components/Board/moves.test.d.cts +2 -0
  36. package/dist/components/Board/moves.test.d.cts.map +1 -0
  37. package/dist/components/Board/moves.test.d.ts +2 -0
  38. package/dist/components/Board/moves.test.d.ts.map +1 -0
  39. package/dist/components/DatePicker/index.cjs +1 -1
  40. package/dist/components/DatePicker/index.cjs.map +1 -1
  41. package/dist/components/DatePicker/index.d.cts.map +1 -1
  42. package/dist/components/DatePicker/index.d.ts.map +1 -1
  43. package/dist/components/DatePicker/index.js +39 -36
  44. package/dist/components/DatePicker/index.js.map +1 -1
  45. package/dist/components/Drawer/index.cjs +1 -1
  46. package/dist/components/Drawer/index.cjs.map +1 -1
  47. package/dist/components/Drawer/index.d.cts.map +1 -1
  48. package/dist/components/Drawer/index.d.ts.map +1 -1
  49. package/dist/components/Drawer/index.js +69 -62
  50. package/dist/components/Drawer/index.js.map +1 -1
  51. package/dist/components/EntryEditor/index.cjs +1 -1
  52. package/dist/components/EntryEditor/index.d.cts.map +1 -1
  53. package/dist/components/EntryEditor/index.d.ts.map +1 -1
  54. package/dist/components/EntryEditor/index.js +1 -1
  55. package/dist/components/EntryEditor/model.d.cts.map +1 -1
  56. package/dist/components/EntryEditor/model.d.ts.map +1 -1
  57. package/dist/components/Pagination/index.cjs +1 -1
  58. package/dist/components/Pagination/index.cjs.map +1 -1
  59. package/dist/components/Pagination/index.js +1 -1
  60. package/dist/components/Pagination/index.js.map +1 -1
  61. package/dist/components/Pagination/style.css +0 -1
  62. package/dist/components/RichTextEditor/index.cjs +1 -1
  63. package/dist/components/RichTextEditor/index.d.cts.map +1 -1
  64. package/dist/components/RichTextEditor/index.d.ts.map +1 -1
  65. package/dist/components/RichTextEditor/index.js +1 -1
  66. package/dist/components/Select/index.cjs +1 -1
  67. package/dist/components/Select/index.cjs.map +1 -1
  68. package/dist/components/Select/index.d.cts.map +1 -1
  69. package/dist/components/Select/index.d.ts.map +1 -1
  70. package/dist/components/Select/index.js +32 -18
  71. package/dist/components/Select/index.js.map +1 -1
  72. package/dist/components/SortableList/index.cjs +1 -1
  73. package/dist/components/SortableList/index.cjs.map +1 -1
  74. package/dist/components/SortableList/index.d.cts.map +1 -1
  75. package/dist/components/SortableList/index.d.ts.map +1 -1
  76. package/dist/components/SortableList/index.js +49 -50
  77. package/dist/components/SortableList/index.js.map +1 -1
  78. package/dist/index.cjs +1 -1
  79. package/dist/index.js +4 -4
  80. package/dist/internal/mergeRefs.d.cts +4 -0
  81. package/dist/internal/mergeRefs.d.cts.map +1 -0
  82. package/dist/internal/mergeRefs.d.ts +4 -0
  83. package/dist/internal/mergeRefs.d.ts.map +1 -0
  84. package/dist/test/dragEvent.d.cts +3 -0
  85. package/dist/test/dragEvent.d.cts.map +1 -0
  86. package/dist/test/dragEvent.d.ts +3 -0
  87. package/dist/test/dragEvent.d.ts.map +1 -0
  88. package/package.json +2 -2
  89. package/dist/chunks/EntryEditor-C32lKKnV.cjs +0 -2
  90. package/dist/chunks/EntryEditor-C32lKKnV.cjs.map +0 -1
  91. package/dist/chunks/EntryEditor-xVuiqtFo.js +0 -181
  92. package/dist/chunks/EntryEditor-xVuiqtFo.js.map +0 -1
  93. package/dist/chunks/RichTextEditor-CSpjsL1z.js.map +0 -1
  94. package/dist/chunks/RichTextEditor-DC_0oN1s.cjs.map +0 -1
  95. package/dist/components/Board/index.cjs.map +0 -1
  96. package/dist/components/Board/index.js.map +0 -1
package/AGENTS.md CHANGED
@@ -4,6 +4,8 @@
4
4
 
5
5
  Add a decision document for every change going forward.
6
6
 
7
+ End every completed change handoff with a suggested Git commit subject and description in a copyable code block. Summarize the final change set and relevant verification.
8
+
7
9
  Render button controls through the shared `Button` component, including controls inside other components and examples. Native button markup belongs only in `src/components/Button` (isolated test fixtures may use native controls). Use Button's standard variants, sizes, icons, focus, and disabled styling instead of copying them into a component stylesheet. Specialized widgets may retain their semantic layout, shared Field skin, or switch track/knob geometry.
8
10
 
9
11
  Every releasable change set must update the `Unreleased` section in `UPGRADING.md` with changes, adoption steps, compatibility notes, and verification steps for agents in consuming projects. Related edits share that section. Leave version increments to `.github/workflows/release.yml`: every push to `main` automatically bumps the patch version, versions the upgrade notes, validates, commits the release files, and publishes to npm. Keep the manifest and lockfile versions aligned and include this file and `UPGRADING.md` in the package.
package/UPGRADING.md CHANGED
@@ -2,6 +2,31 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.1.8
6
+
7
+ ### Changes
8
+
9
+ - Consolidated sortable ref forwarding and repeated test setup. Split EntryEditor validation and control rendering, Board drop resolution and keyboard coordinates, Select keyboard handling, Drawer exit state, and rich-text toolbar rendering into focused helpers. Catalogue sections use a typed lookup, and showcase smoke checks use separate interaction helpers.
10
+ - Expanded `fallow:report` from dead-code analysis to dead code, duplication, and health. Reports include a recorded cleanup baseline, remaining health advisories, and coverage sources. `npm run fallow` now rejects duplicate groups as well as dead-code findings.
11
+ - Coverage now includes component helper modules and emits `coverage/coverage-final.json` for Fallow. Added Board drop, keyboard, cancellation, and forwarded-ref regression checks.
12
+ - Contributor handoffs now end with a suggested Git commit subject and description, including relevant verification.
13
+
14
+ ### Adoption
15
+
16
+ No application code changes are required. For library maintenance, run `npm run test:coverage && npm run fallow:report` after source changes to refresh coverage and both report artifacts. Automated readers of `reports/fallow-report.json` must use the combined schema: `check.total_issues`, `dupes.stats`, and `health.findings` replace the former dead-code-only root fields.
17
+
18
+ Contributing agents should finish each completed change handoff with the suggested commit subject and description in a copyable code block.
19
+
20
+ ### Compatibility
21
+
22
+ Public component props, emitted values, validation messages, control appearance, and package dependencies are unchanged. The report command fails for dead code, duplicate groups, or functions above cyclomatic/cognitive limits. CRAP change-risk findings remain advisory and are listed with measured or estimated coverage; estimates are not executed test coverage. Package versions remain aligned at 0.1.7 until the release workflow versions this change set.
23
+
24
+ ### Verification
25
+
26
+ Run the consuming project's typecheck, tests, and build. Exercise EntryEditor required and optional fields, scalar clearing, rich-text links, and single/multiple asset/reference selections. Check Select keyboard navigation and Escape inside a Drawer, navigation Drawer exit/reopening, and Board card/column dragging with pointer and keyboard. In this repository run lint, typecheck, coverage, Storybook build, package checks, `npm run fallow`, and `npm run fallow:report`; confirm the report contains no dead-code, duplicate-group, or cyclomatic/cognitive findings and review any remaining coverage-risk advisories.
27
+
28
+ For the handoff guidance, check the documentation diff and confirm the final response ends with a copyable commit subject and description covering the completed change and its verification.
29
+
5
30
  ## 0.1.7
6
31
 
7
32
  ### Changes
@@ -0,0 +1,2 @@
1
+ const e=require("./Icon-CWsA1W83.cjs"),t=require("../components/Button/index.cjs"),n=require("./mergeRefs-Bc3qS0Tt.cjs");let r=require("react"),i=require("react/jsx-runtime"),a=require("@dnd-kit/core"),o=require("@dnd-kit/sortable"),s=require("@dnd-kit/utilities");function c({active:e,over:t},n){if(!t)return;let r=t.data.current??{},i=r.type===`column`?String(t.id):String(r.columnId??``),a=n.indexOf(i);if(!(a<0||i===String(e.id)))return{columnId:String(e.id),toIndex:a}}function l({active:e,over:t}){if(!t)return;let n=String(e.data.current?.columnId??``),r=t.data.current??{},i=r.type===`card`,a=String(r.columnId??(i?``:t.id)),o=i?r.sortable?.index??0:(r.items??[]).length;if(a&&(n!==a||String(e.id)!==String(t.id)))return{cardId:String(e.id),fromColumnId:n,toColumnId:a,toIndex:o}}var u=(e,t)=>{let{currentCoordinates:n,context:r}=t;if(e.code===`ArrowLeft`||e.code===`ArrowRight`){let t=r.droppableContainers.getEnabled().filter(e=>e.data.current?.type===`column-body`).sort((e,t)=>(e.rect.current?.left??0)-(t.rect.current?.left??0)),i=r.active?.rect.current.translated;if(!i)return;let a=t[t.findIndex(e=>(e.rect.current?.left??0)<=i.left+1&&(e.rect.current?.right??0)>=i.left-1)+(e.code===`ArrowRight`?1:-1)]?.rect.current;return a?(e.preventDefault(),{x:a.left+a.width/2-i.width/2,y:n.y}):void 0}return(0,o.sortableKeyboardCoordinates)(e,t)},d=e=>{let t=e.active.data.current?.type===`column`,n=e.droppableContainers.filter(e=>{let n=e.data.current?.type;return t?n===`column`:n!==`column`}),r={...e,droppableContainers:n};if(t)return(0,a.closestCorners)(r);let i=(0,a.pointerWithin)(r);if(i.length>0){let e=e=>n.find(t=>t.id===e)?.data.current?.type===`card`?0:1;return[...i].sort((t,n)=>e(t.id)-e(n.id))}return(0,a.closestCorners)(r)},f=(0,r.createContext)({activeId:null,activeType:null,overColumnId:null}),p=(0,r.forwardRef)(function({columnIds:t,onMove:n,onColumnMove:s,renderDragOverlay:p,renderColumnDragOverlay:m,className:h,children:g,..._},v){let y=(0,r.useId)(),[b,x]=(0,r.useState)(null),[S,C]=(0,r.useState)(null),[w,T]=(0,r.useState)(null),E=(0,a.useSensors)((0,a.useSensor)(a.PointerSensor,{activationConstraint:{distance:4}}),(0,a.useSensor)(a.KeyboardSensor,{coordinateGetter:u})),D=(0,r.useCallback)(e=>{x(String(e.active.id)),C(e.active.data.current?.type===`column`?`column`:`card`)},[]),O=(0,r.useCallback)(e=>{let t=e.over;if(!t){T(null);return}let n=t.data.current??{},r=n.type===`column`?String(t.id):String(n.columnId??``);T(r||null)},[]),k=(0,r.useCallback)(()=>{x(null),C(null),T(null)},[]),A=(0,r.useCallback)(e=>{let{active:r,over:i}=e;if(k(),i){if(r.data.current?.type===`column`){let n=c(e,t??[]);n&&s?.(n)}else{let t=l(e);t&&n?.(t)}}},[t,s,n,k]),j=(0,r.useMemo)(()=>({activeId:b,activeType:S,overColumnId:w}),[b,S,w]),M=(0,i.jsx)(`div`,{ref:v,className:[`ds-board`,h].filter(Boolean).join(` `),..._,children:g});return(0,i.jsx)(f.Provider,{value:j,children:(0,i.jsxs)(a.DndContext,{id:`ds-board-${y}`,sensors:E,collisionDetection:d,measuring:{droppable:{strategy:a.MeasuringStrategy.Always}},onDragStart:D,onDragOver:O,onDragEnd:A,onDragCancel:k,children:[t?(0,i.jsx)(o.SortableContext,{items:t,strategy:o.horizontalListSortingStrategy,children:M}):M,(0,i.jsxs)(a.DragOverlay,{children:[b&&S===`card`?(0,i.jsxs)(`div`,{className:`ds-board-card`,"data-overlay":!0,children:[(0,i.jsx)(`span`,{className:`ds-board-card__handle`,"aria-hidden":`true`,children:(0,i.jsx)(e.t,{name:`grip-vertical`})}),(0,i.jsx)(`div`,{className:`ds-board-card__content`,children:p?.(b)})]}):null,b&&S===`column`?(0,i.jsx)(`div`,{className:`ds-board-column`,"data-overlay":!0,children:m?.(b)}):null]})]})})}),m=(0,r.forwardRef)(function({id:e,title:c,itemIds:l,count:u,action:d,isDraggable:p=!0,className:m,children:h,...g},_){let{activeType:v,overColumnId:y}=(0,r.useContext)(f),{setNodeRef:b}=(0,a.useDroppable)({id:`${e}::body`,data:{type:`column-body`,columnId:e,items:l}}),{attributes:x,listeners:S,setNodeRef:C,transform:w,transition:T,isDragging:E}=(0,o.useSortable)({id:e,disabled:!p,data:{type:`column`,columnId:e,items:l}});return(0,i.jsxs)(`section`,{ref:n.t(C,_),style:{transform:s.CSS.Translate.toString(w),transition:T},className:[`ds-board-column`,m].filter(Boolean).join(` `),"data-over":v===`card`&&y===e||void 0,"data-dragging":E||void 0,"aria-label":c,...g,children:[p&&(0,i.jsx)(t.Button,{variant:`simple`,size:`sm`,iconStart:`grip-horizontal`,className:`ds-board-column__grip`,"aria-label":`Reorder ${c}`,...x,...S}),(0,i.jsxs)(`header`,{className:`ds-board-column__head`,children:[(0,i.jsx)(`span`,{className:`ds-board-column__title`,children:c}),(0,i.jsx)(`span`,{className:`ds-board-column__count`,children:u??l.length}),d&&(0,i.jsx)(`span`,{className:`ds-board-column__action`,children:d})]}),(0,i.jsx)(`div`,{ref:b,className:`ds-board-column__body`,children:(0,i.jsx)(o.SortableContext,{items:l,strategy:o.verticalListSortingStrategy,children:h})})]})}),h=(0,r.forwardRef)(function({id:e,columnId:a,label:c,className:l,children:u,...d},p){let{attributes:m,listeners:h,setNodeRef:g,transform:_,transition:v,isDragging:y}=(0,o.useSortable)({id:e,data:{type:`card`,columnId:a}}),{activeId:b}=(0,r.useContext)(f);return(0,i.jsxs)(`div`,{ref:n.t(g,p),style:{transform:s.CSS.Translate.toString(_),transition:v},className:[`ds-board-card`,l].filter(Boolean).join(` `),"data-dragging":y||void 0,"data-idle":b&&b!==e?``:void 0,...d,children:[(0,i.jsx)(t.Button,{variant:`simple`,size:`sm`,isIconOnly:!0,iconStart:`grip-vertical`,className:`ds-board-card__handle`,"aria-label":c?`Move ${c}`:`Move card`,...m,...h}),(0,i.jsx)(`div`,{className:`ds-board-card__content`,children:u})]})});Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return h}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return m}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return p}});
2
+ //# sourceMappingURL=Board-BLhB5_xx.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Board-BLhB5_xx.cjs","names":["sortableKeyboardCoordinates","closestCorners","pointerWithin","createContext","forwardRef","useId","useState","useSensors","useSensor","PointerSensor","KeyboardSensor","useCallback","useMemo","DndContext","MeasuringStrategy","SortableContext","horizontalListSortingStrategy","DragOverlay","Icon","useContext","useDroppable","useSortable","mergeRefs","CSS","Button","verticalListSortingStrategy","require_Icon","require_components_Button_index","require_mergeRefs","react","react_jsx_runtime","_dnd_kit_core","_dnd_kit_sortable","_dnd_kit_utilities","columnMove","over","data","columnId","\"type\"","\"columnId\"","toIndex","columnIds","active","cardMove","fromColumnId","onCard","toColumnId","\"sortable\"","\"items\"","boardCoordinateGetter","args","event","containers","context","c","a","b","activeRect","rect","currentCoordinates","boardCollisionDetection","isColumn","type","container","scoped","within","rank","id","BoardContext","Board","rest","dndId","activeId","setActiveId","activeType","setActiveType","overColumnId","setOverColumnId","sensors","handleStart","handleOver","overData","reset","handleEnd","move","onColumnMove","onMove","value","board","className","renderDragOverlay","renderColumnDragOverlay","BoardColumn","setBodyRef","itemIds","setColumnRef","isDraggable","ref","transform","isDragging","title","attributes","listeners","count","action","BoardCard","setNodeRef","label"],"sources":["../../src/components/Board/moves.ts","../../src/components/Board/keyboard.ts","../../src/components/Board/index.tsx"],"sourcesContent":["import type { DragEndEvent } from \"@dnd-kit/core\";\nimport type { BoardColumnMove, BoardMove } from \".\";\n\nexport function columnMove({ active, over }: DragEndEvent, columnIds: string[]): BoardColumnMove | undefined {\n if (!over) return;\n const data = over.data.current ?? {};\n const columnId = data[\"type\"] === \"column\" ? String(over.id) : String(data[\"columnId\"] ?? \"\");\n const toIndex = columnIds.indexOf(columnId);\n if (toIndex < 0 || columnId === String(active.id)) return;\n return { columnId: String(active.id), toIndex };\n}\n\nexport function cardMove({ active, over }: DragEndEvent): BoardMove | undefined {\n if (!over) return;\n const fromColumnId = String(active.data.current?.[\"columnId\"] ?? \"\");\n const data = over.data.current ?? {};\n const onCard = data[\"type\"] === \"card\";\n const toColumnId = String(data[\"columnId\"] ?? (onCard ? \"\" : over.id));\n // A card occupies a precise slot; empty lane space appends to the lane.\n const toIndex = onCard\n ? (data[\"sortable\"] as { index: number } | undefined)?.index ?? 0\n : ((data[\"items\"] as string[] | undefined) ?? []).length;\n if (!toColumnId) return;\n if (fromColumnId === toColumnId && String(active.id) === String(over.id)) return;\n return { cardId: String(active.id), fromColumnId, toColumnId, toIndex };\n}\n","import type { KeyboardCoordinateGetter } from \"@dnd-kit/core\";\nimport { sortableKeyboardCoordinates } from \"@dnd-kit/sortable\";\n\n/**\n * Arrow up/down sorts inside a lane; left/right jumps lanes, which the stock\n * sortable getter does not do.\n */\nexport const boardCoordinateGetter: KeyboardCoordinateGetter = (event, args) => {\n const { currentCoordinates, context } = args;\n if (event.code === \"ArrowLeft\" || event.code === \"ArrowRight\") {\n const containers = context.droppableContainers\n .getEnabled()\n .filter((c) => c.data.current?.[\"type\"] === \"column-body\")\n .sort((a, b) => (a.rect.current?.left ?? 0) - (b.rect.current?.left ?? 0));\n const activeRect = context.active?.rect.current.translated;\n if (!activeRect) return undefined;\n const index = containers.findIndex(\n (c) => (c.rect.current?.left ?? 0) <= activeRect.left + 1 &&\n (c.rect.current?.right ?? 0) >= activeRect.left - 1,\n );\n const next = containers[index + (event.code === \"ArrowRight\" ? 1 : -1)];\n const rect = next?.rect.current;\n if (!rect) return undefined;\n event.preventDefault();\n return { x: rect.left + rect.width / 2 - activeRect.width / 2, y: currentCoordinates.y };\n }\n return sortableKeyboardCoordinates(event, args);\n};\n\n","import {\n createContext,\n forwardRef,\n useCallback,\n useContext,\n useId,\n useMemo,\n useState,\n type HTMLAttributes,\n type ReactNode,\n} from \"react\";\nimport {\n DndContext,\n DragOverlay,\n KeyboardSensor,\n PointerSensor,\n closestCorners,\n pointerWithin,\n MeasuringStrategy,\n\n useDroppable,\n useSensor,\n useSensors,\n type CollisionDetection,\n type DragEndEvent,\n type DragOverEvent,\n type DragStartEvent,\n} from \"@dnd-kit/core\";\nimport {\n SortableContext,\n horizontalListSortingStrategy,\n useSortable,\n verticalListSortingStrategy,\n} from \"@dnd-kit/sortable\";\nimport { CSS } from \"@dnd-kit/utilities\";\nimport { Button } from \"../Button\";\nimport { Icon } from \"../Icon\";\nimport { mergeRefs } from \"../../internal/mergeRefs\";\nimport { cardMove, columnMove } from \"./moves\";\nimport { boardCoordinateGetter } from \"./keyboard\";\nimport \"./style.css\";\n\nexport interface BoardMove {\n cardId: string;\n fromColumnId: string;\n toColumnId: string;\n /** Index the card lands on inside the destination column. */\n toIndex: number;\n}\n\nexport interface BoardColumnMove {\n columnId: string;\n toIndex: number;\n}\n\ninterface BoardContextValue {\n activeId: string | null;\n activeType: \"card\" | \"column\" | null;\n /** The lane the pointer is currently over, whatever it is actually over. */\n overColumnId: string | null;\n}\n\n/**\n * A lane's body sits inside the lane's frame, so a raw corner-distance\n * strategy lets the body steal the hit from the lane itself — most visibly at\n * the board ends, where the last lane then never shifts aside to open the\n * drop gap. Resolve by what is being dragged: a column only ever hits other\n * columns; a card hits the card under the pointer first, then the lane body\n * under it, then whatever is nearest.\n */\nconst boardCollisionDetection: CollisionDetection = (args) => {\n const isColumn = args.active.data.current?.[\"type\"] === \"column\";\n const containers = args.droppableContainers.filter((container) => {\n const type = container.data.current?.[\"type\"];\n return isColumn ? type === \"column\" : type !== \"column\";\n });\n const scoped = { ...args, droppableContainers: containers };\n if (isColumn) return closestCorners(scoped);\n const within = pointerWithin(scoped);\n if (within.length > 0) {\n const rank = (id: (typeof within)[number][\"id\"]) =>\n containers.find((c) => c.id === id)?.data.current?.[\"type\"] === \"card\" ? 0 : 1;\n return [...within].sort((a, b) => rank(a.id) - rank(b.id));\n }\n return closestCorners(scoped);\n};\n\nconst BoardContext = createContext<BoardContextValue>({\n activeId: null,\n activeType: null,\n overColumnId: null,\n});\n\nexport interface BoardProps extends Omit<HTMLAttributes<HTMLDivElement>, \"onDragEnd\"> {\n /** Column ids in render order. Required for columns to be re-orderable. */\n columnIds?: string[];\n /** Fired once a card is dropped somewhere new. Apply it to your own state. */\n onMove?: (move: BoardMove) => void;\n /** Fired once a column is dropped in a new position. */\n onColumnMove?: (move: BoardColumnMove) => void;\n /** Rendered inside the drag overlay while a card is in the air. */\n renderDragOverlay?: (cardId: string) => ReactNode;\n /** Rendered inside the drag overlay while a column is in the air. */\n renderColumnDragOverlay?: (columnId: string) => ReactNode;\n children?: ReactNode;\n}\n\n/**\n * A column board with pointer and keyboard dragging. The board owns the drag\n * mechanics only — the card data stays yours; react to `onMove`.\n */\nexport const Board = forwardRef<HTMLDivElement, BoardProps>(function Board(\n {\n columnIds,\n onMove,\n onColumnMove,\n renderDragOverlay,\n renderColumnDragOverlay,\n className,\n children,\n ...rest\n },\n ref,\n) {\n const dndId = useId();\n const [activeId, setActiveId] = useState<string | null>(null);\n const [activeType, setActiveType] = useState<\"card\" | \"column\" | null>(null);\n const [overColumnId, setOverColumnId] = useState<string | null>(null);\n const sensors = useSensors(\n useSensor(PointerSensor, { activationConstraint: { distance: 4 } }),\n useSensor(KeyboardSensor, { coordinateGetter: boardCoordinateGetter }),\n );\n\n const handleStart = useCallback((event: DragStartEvent) => {\n setActiveId(String(event.active.id));\n setActiveType(event.active.data.current?.[\"type\"] === \"column\" ? \"column\" : \"card\");\n }, []);\n\n /* Cards land on cards as often as on empty space, so the lane a drag is over\n * is always resolved back to a column id — one highlight, one meaning. */\n const handleOver = useCallback((event: DragOverEvent) => {\n const over = event.over;\n if (!over) {\n setOverColumnId(null);\n return;\n }\n const overData = over.data.current ?? {};\n const columnId =\n overData[\"type\"] === \"column\" ? String(over.id) : String(overData[\"columnId\"] ?? \"\");\n setOverColumnId(columnId || null);\n }, []);\n\n const reset = useCallback(() => {\n setActiveId(null);\n setActiveType(null);\n setOverColumnId(null);\n }, []);\n\n const handleEnd = useCallback(\n (event: DragEndEvent) => {\n const { active, over } = event;\n reset();\n if (!over) return;\n\n /* Columns reorder among themselves; a column never lands inside a lane. */\n if (active.data.current?.[\"type\"] === \"column\") {\n const move = columnMove(event, columnIds ?? []);\n if (move) onColumnMove?.(move);\n } else {\n const move = cardMove(event);\n if (move) onMove?.(move);\n }\n },\n [columnIds, onColumnMove, onMove, reset],\n );\n\n const value = useMemo(\n () => ({ activeId, activeType, overColumnId }),\n [activeId, activeType, overColumnId],\n );\n\n const board = (\n <div ref={ref} className={[\"ds-board\", className].filter(Boolean).join(\" \")} {...rest}>\n {children}\n </div>\n );\n\n return (\n <BoardContext.Provider value={value}>\n <DndContext\n id={`ds-board-${dndId}`}\n sensors={sensors}\n collisionDetection={boardCollisionDetection}\n /* Lanes scroll and change height mid-drag, so positions are re-read\n * continuously — without this the shift animation misfires. */\n measuring={{ droppable: { strategy: MeasuringStrategy.Always } }}\n\n onDragStart={handleStart}\n onDragOver={handleOver}\n onDragEnd={handleEnd}\n onDragCancel={reset}\n >\n {columnIds ? (\n <SortableContext items={columnIds} strategy={horizontalListSortingStrategy}>\n {board}\n </SortableContext>\n ) : (\n board\n )}\n <DragOverlay>\n {activeId && activeType === \"card\" ? (\n <div className=\"ds-board-card\" data-overlay>\n <span className=\"ds-board-card__handle\" aria-hidden=\"true\">\n <Icon name=\"grip-vertical\" />\n </span>\n <div className=\"ds-board-card__content\">{renderDragOverlay?.(activeId)}</div>\n </div>\n ) : null}\n {activeId && activeType === \"column\" ? (\n <div className=\"ds-board-column\" data-overlay>\n {renderColumnDragOverlay?.(activeId)}\n </div>\n ) : null}\n </DragOverlay>\n </DndContext>\n </BoardContext.Provider>\n );\n});\n\nexport interface BoardColumnProps extends HTMLAttributes<HTMLDivElement> {\n id: string;\n title: string;\n /** Card ids in render order — the sortable contract for this column. */\n itemIds: string[];\n /** Shown beside the title; defaults to the number of cards. */\n count?: number;\n /** A control in the column head, e.g. an add button or a menu. */\n action?: ReactNode;\n /** Set false to pin a lane in place. */\n isDraggable?: boolean;\n children?: ReactNode;\n}\n\n/**\n * One lane of the board. Heavy frame, hairline head rule, scrolling body, and\n * a grip on top of the head that drags the whole lane sideways.\n */\nexport const BoardColumn = forwardRef<HTMLElement, BoardColumnProps>(\n function BoardColumn(\n { id, title, itemIds, count, action, isDraggable = true, className, children, ...rest },\n ref,\n ) {\n const { activeType, overColumnId } = useContext(BoardContext);\n const { setNodeRef: setBodyRef } = useDroppable({\n id: `${id}::body`,\n data: { type: \"column-body\", columnId: id, items: itemIds },\n });\n const {\n attributes,\n listeners,\n setNodeRef: setColumnRef,\n transform,\n transition,\n isDragging,\n } = useSortable({\n id,\n disabled: !isDraggable,\n data: { type: \"column\", columnId: id, items: itemIds },\n });\n\n return (\n <section\n ref={mergeRefs(setColumnRef, ref)}\n style={{ transform: CSS.Translate.toString(transform), transition }}\n className={[\"ds-board-column\", className].filter(Boolean).join(\" \")}\n data-over={(activeType === \"card\" && overColumnId === id) || undefined}\n data-dragging={isDragging || undefined}\n\n aria-label={title}\n {...rest}\n >\n {isDraggable && (\n <Button\n variant=\"simple\"\n size=\"sm\"\n iconStart=\"grip-horizontal\"\n className=\"ds-board-column__grip\"\n aria-label={`Reorder ${title}`}\n {...attributes}\n {...listeners}\n />\n )}\n <header className=\"ds-board-column__head\">\n <span className=\"ds-board-column__title\">{title}</span>\n <span className=\"ds-board-column__count\">{count ?? itemIds.length}</span>\n {action && <span className=\"ds-board-column__action\">{action}</span>}\n </header>\n <div ref={setBodyRef} className=\"ds-board-column__body\">\n <SortableContext items={itemIds} strategy={verticalListSortingStrategy}>\n {children}\n </SortableContext>\n </div>\n </section>\n );\n },\n);\n\nexport interface BoardCardProps extends HTMLAttributes<HTMLDivElement> {\n id: string;\n columnId: string;\n /** Accessible name announced when the card is picked up. */\n label?: string;\n children?: ReactNode;\n}\n\n/**\n * A draggable card. The grip on the left is the handle, so the card body can\n * hold links and buttons; the grip is a real button for keyboard users:\n * space to pick up, arrows to move, space to drop.\n */\nexport const BoardCard = forwardRef<HTMLDivElement, BoardCardProps>(function BoardCard(\n { id, columnId, label, className, children, ...rest },\n ref,\n) {\n const { attributes, listeners, setNodeRef, transform, transition, isDragging } =\n useSortable({ id, data: { type: \"card\", columnId } });\n const { activeId } = useContext(BoardContext);\n\n return (\n <div\n ref={mergeRefs(setNodeRef, ref)}\n style={{ transform: CSS.Translate.toString(transform), transition }}\n className={[\"ds-board-card\", className].filter(Boolean).join(\" \")}\n data-dragging={isDragging || undefined}\n data-idle={activeId && activeId !== id ? \"\" : undefined}\n {...rest}\n >\n <Button\n variant=\"simple\"\n size=\"sm\"\n isIconOnly\n iconStart=\"grip-vertical\"\n className=\"ds-board-card__handle\"\n aria-label={label ? `Move ${label}` : \"Move card\"}\n {...attributes}\n {...listeners}\n />\n <div className=\"ds-board-card__content\">{children}</div>\n </div>\n );\n});\n"],"mappings":"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"}
@@ -0,0 +1,239 @@
1
+ import { t as e } from "./Icon-CGY1FNXT.js";
2
+ import { Button as t } from "../components/Button/index.js";
3
+ import { t as n } from "./mergeRefs-DGM402mE.js";
4
+ import { createContext as r, forwardRef as i, useCallback as a, useContext as o, useId as s, useMemo as c, useState as l } from "react";
5
+ import { jsx as u, jsxs as d } from "react/jsx-runtime";
6
+ import { DndContext as f, DragOverlay as p, KeyboardSensor as m, MeasuringStrategy as h, PointerSensor as g, closestCorners as _, pointerWithin as v, useDroppable as y, useSensor as b, useSensors as x } from "@dnd-kit/core";
7
+ import { SortableContext as S, horizontalListSortingStrategy as C, sortableKeyboardCoordinates as w, useSortable as T, verticalListSortingStrategy as E } from "@dnd-kit/sortable";
8
+ import { CSS as D } from "@dnd-kit/utilities";
9
+ //#region src/components/Board/moves.ts
10
+ function O({ active: e, over: t }, n) {
11
+ if (!t) return;
12
+ let r = t.data.current ?? {}, i = r.type === "column" ? String(t.id) : String(r.columnId ?? ""), a = n.indexOf(i);
13
+ if (!(a < 0 || i === String(e.id))) return {
14
+ columnId: String(e.id),
15
+ toIndex: a
16
+ };
17
+ }
18
+ function k({ active: e, over: t }) {
19
+ if (!t) return;
20
+ let n = String(e.data.current?.columnId ?? ""), r = t.data.current ?? {}, i = r.type === "card", a = String(r.columnId ?? (i ? "" : t.id)), o = i ? r.sortable?.index ?? 0 : (r.items ?? []).length;
21
+ if (a && (n !== a || String(e.id) !== String(t.id))) return {
22
+ cardId: String(e.id),
23
+ fromColumnId: n,
24
+ toColumnId: a,
25
+ toIndex: o
26
+ };
27
+ }
28
+ //#endregion
29
+ //#region src/components/Board/keyboard.ts
30
+ var A = (e, t) => {
31
+ let { currentCoordinates: n, context: r } = t;
32
+ if (e.code === "ArrowLeft" || e.code === "ArrowRight") {
33
+ let t = r.droppableContainers.getEnabled().filter((e) => e.data.current?.type === "column-body").sort((e, t) => (e.rect.current?.left ?? 0) - (t.rect.current?.left ?? 0)), i = r.active?.rect.current.translated;
34
+ if (!i) return;
35
+ let a = t[t.findIndex((e) => (e.rect.current?.left ?? 0) <= i.left + 1 && (e.rect.current?.right ?? 0) >= i.left - 1) + (e.code === "ArrowRight" ? 1 : -1)]?.rect.current;
36
+ return a ? (e.preventDefault(), {
37
+ x: a.left + a.width / 2 - i.width / 2,
38
+ y: n.y
39
+ }) : void 0;
40
+ }
41
+ return w(e, t);
42
+ }, j = (e) => {
43
+ let t = e.active.data.current?.type === "column", n = e.droppableContainers.filter((e) => {
44
+ let n = e.data.current?.type;
45
+ return t ? n === "column" : n !== "column";
46
+ }), r = {
47
+ ...e,
48
+ droppableContainers: n
49
+ };
50
+ if (t) return _(r);
51
+ let i = v(r);
52
+ if (i.length > 0) {
53
+ let e = (e) => n.find((t) => t.id === e)?.data.current?.type === "card" ? 0 : 1;
54
+ return [...i].sort((t, n) => e(t.id) - e(n.id));
55
+ }
56
+ return _(r);
57
+ }, M = r({
58
+ activeId: null,
59
+ activeType: null,
60
+ overColumnId: null
61
+ }), N = i(function({ columnIds: t, onMove: n, onColumnMove: r, renderDragOverlay: i, renderColumnDragOverlay: o, className: _, children: v, ...y }, w) {
62
+ let T = s(), [E, D] = l(null), [N, P] = l(null), [F, I] = l(null), L = x(b(g, { activationConstraint: { distance: 4 } }), b(m, { coordinateGetter: A })), R = a((e) => {
63
+ D(String(e.active.id)), P(e.active.data.current?.type === "column" ? "column" : "card");
64
+ }, []), z = a((e) => {
65
+ let t = e.over;
66
+ if (!t) {
67
+ I(null);
68
+ return;
69
+ }
70
+ let n = t.data.current ?? {}, r = n.type === "column" ? String(t.id) : String(n.columnId ?? "");
71
+ I(r || null);
72
+ }, []), B = a(() => {
73
+ D(null), P(null), I(null);
74
+ }, []), V = a((e) => {
75
+ let { active: i, over: a } = e;
76
+ if (B(), a) {
77
+ if (i.data.current?.type === "column") {
78
+ let n = O(e, t ?? []);
79
+ n && r?.(n);
80
+ } else {
81
+ let t = k(e);
82
+ t && n?.(t);
83
+ }
84
+ }
85
+ }, [
86
+ t,
87
+ r,
88
+ n,
89
+ B
90
+ ]), H = c(() => ({
91
+ activeId: E,
92
+ activeType: N,
93
+ overColumnId: F
94
+ }), [
95
+ E,
96
+ N,
97
+ F
98
+ ]), U = /* @__PURE__ */ u("div", {
99
+ ref: w,
100
+ className: ["ds-board", _].filter(Boolean).join(" "),
101
+ ...y,
102
+ children: v
103
+ });
104
+ return /* @__PURE__ */ u(M.Provider, {
105
+ value: H,
106
+ children: /* @__PURE__ */ d(f, {
107
+ id: `ds-board-${T}`,
108
+ sensors: L,
109
+ collisionDetection: j,
110
+ measuring: { droppable: { strategy: h.Always } },
111
+ onDragStart: R,
112
+ onDragOver: z,
113
+ onDragEnd: V,
114
+ onDragCancel: B,
115
+ children: [t ? /* @__PURE__ */ u(S, {
116
+ items: t,
117
+ strategy: C,
118
+ children: U
119
+ }) : U, /* @__PURE__ */ d(p, { children: [E && N === "card" ? /* @__PURE__ */ d("div", {
120
+ className: "ds-board-card",
121
+ "data-overlay": !0,
122
+ children: [/* @__PURE__ */ u("span", {
123
+ className: "ds-board-card__handle",
124
+ "aria-hidden": "true",
125
+ children: /* @__PURE__ */ u(e, { name: "grip-vertical" })
126
+ }), /* @__PURE__ */ u("div", {
127
+ className: "ds-board-card__content",
128
+ children: i?.(E)
129
+ })]
130
+ }) : null, E && N === "column" ? /* @__PURE__ */ u("div", {
131
+ className: "ds-board-column",
132
+ "data-overlay": !0,
133
+ children: o?.(E)
134
+ }) : null] })]
135
+ })
136
+ });
137
+ }), P = i(function({ id: e, title: r, itemIds: i, count: a, action: s, isDraggable: c = !0, className: l, children: f, ...p }, m) {
138
+ let { activeType: h, overColumnId: g } = o(M), { setNodeRef: _ } = y({
139
+ id: `${e}::body`,
140
+ data: {
141
+ type: "column-body",
142
+ columnId: e,
143
+ items: i
144
+ }
145
+ }), { attributes: v, listeners: b, setNodeRef: x, transform: C, transition: w, isDragging: O } = T({
146
+ id: e,
147
+ disabled: !c,
148
+ data: {
149
+ type: "column",
150
+ columnId: e,
151
+ items: i
152
+ }
153
+ });
154
+ return /* @__PURE__ */ d("section", {
155
+ ref: n(x, m),
156
+ style: {
157
+ transform: D.Translate.toString(C),
158
+ transition: w
159
+ },
160
+ className: ["ds-board-column", l].filter(Boolean).join(" "),
161
+ "data-over": h === "card" && g === e || void 0,
162
+ "data-dragging": O || void 0,
163
+ "aria-label": r,
164
+ ...p,
165
+ children: [
166
+ c && /* @__PURE__ */ u(t, {
167
+ variant: "simple",
168
+ size: "sm",
169
+ iconStart: "grip-horizontal",
170
+ className: "ds-board-column__grip",
171
+ "aria-label": `Reorder ${r}`,
172
+ ...v,
173
+ ...b
174
+ }),
175
+ /* @__PURE__ */ d("header", {
176
+ className: "ds-board-column__head",
177
+ children: [
178
+ /* @__PURE__ */ u("span", {
179
+ className: "ds-board-column__title",
180
+ children: r
181
+ }),
182
+ /* @__PURE__ */ u("span", {
183
+ className: "ds-board-column__count",
184
+ children: a ?? i.length
185
+ }),
186
+ s && /* @__PURE__ */ u("span", {
187
+ className: "ds-board-column__action",
188
+ children: s
189
+ })
190
+ ]
191
+ }),
192
+ /* @__PURE__ */ u("div", {
193
+ ref: _,
194
+ className: "ds-board-column__body",
195
+ children: /* @__PURE__ */ u(S, {
196
+ items: i,
197
+ strategy: E,
198
+ children: f
199
+ })
200
+ })
201
+ ]
202
+ });
203
+ }), F = i(function({ id: e, columnId: r, label: i, className: a, children: s, ...c }, l) {
204
+ let { attributes: f, listeners: p, setNodeRef: m, transform: h, transition: g, isDragging: _ } = T({
205
+ id: e,
206
+ data: {
207
+ type: "card",
208
+ columnId: r
209
+ }
210
+ }), { activeId: v } = o(M);
211
+ return /* @__PURE__ */ d("div", {
212
+ ref: n(m, l),
213
+ style: {
214
+ transform: D.Translate.toString(h),
215
+ transition: g
216
+ },
217
+ className: ["ds-board-card", a].filter(Boolean).join(" "),
218
+ "data-dragging": _ || void 0,
219
+ "data-idle": v && v !== e ? "" : void 0,
220
+ ...c,
221
+ children: [/* @__PURE__ */ u(t, {
222
+ variant: "simple",
223
+ size: "sm",
224
+ isIconOnly: !0,
225
+ iconStart: "grip-vertical",
226
+ className: "ds-board-card__handle",
227
+ "aria-label": i ? `Move ${i}` : "Move card",
228
+ ...f,
229
+ ...p
230
+ }), /* @__PURE__ */ u("div", {
231
+ className: "ds-board-card__content",
232
+ children: s
233
+ })]
234
+ });
235
+ });
236
+ //#endregion
237
+ export { F as n, P as r, N as t };
238
+
239
+ //# sourceMappingURL=Board-BTFSzn6c.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Board-BTFSzn6c.js","names":["Icon","mergeRefs","columnMove","over","data","columnId","\"type\"","\"columnId\"","toIndex","columnIds","active","cardMove","fromColumnId","onCard","toColumnId","\"sortable\"","\"items\"","boardCoordinateGetter","args","event","containers","context","c","a","b","activeRect","rect","currentCoordinates","sortableKeyboardCoordinates","boardCollisionDetection","isColumn","type","container","scoped","closestCorners","within","pointerWithin","rank","id","BoardContext","createContext","Board","forwardRef","rest","dndId","useId","activeId","setActiveId","useState","activeType","setActiveType","overColumnId","setOverColumnId","sensors","useSensors","useSensor","PointerSensor","KeyboardSensor","handleStart","useCallback","handleOver","overData","reset","handleEnd","move","onColumnMove","onMove","value","useMemo","board","className","DndContext","MeasuringStrategy","SortableContext","horizontalListSortingStrategy","DragOverlay","renderDragOverlay","renderColumnDragOverlay","BoardColumn","useContext","setBodyRef","useDroppable","itemIds","setColumnRef","useSortable","isDraggable","ref","CSS","transform","isDragging","title","Button","attributes","listeners","count","action","verticalListSortingStrategy","BoardCard","setNodeRef","label"],"sources":["../../src/components/Board/moves.ts","../../src/components/Board/keyboard.ts","../../src/components/Board/index.tsx"],"sourcesContent":["import type { DragEndEvent } from \"@dnd-kit/core\";\nimport type { BoardColumnMove, BoardMove } from \".\";\n\nexport function columnMove({ active, over }: DragEndEvent, columnIds: string[]): BoardColumnMove | undefined {\n if (!over) return;\n const data = over.data.current ?? {};\n const columnId = data[\"type\"] === \"column\" ? String(over.id) : String(data[\"columnId\"] ?? \"\");\n const toIndex = columnIds.indexOf(columnId);\n if (toIndex < 0 || columnId === String(active.id)) return;\n return { columnId: String(active.id), toIndex };\n}\n\nexport function cardMove({ active, over }: DragEndEvent): BoardMove | undefined {\n if (!over) return;\n const fromColumnId = String(active.data.current?.[\"columnId\"] ?? \"\");\n const data = over.data.current ?? {};\n const onCard = data[\"type\"] === \"card\";\n const toColumnId = String(data[\"columnId\"] ?? (onCard ? \"\" : over.id));\n // A card occupies a precise slot; empty lane space appends to the lane.\n const toIndex = onCard\n ? (data[\"sortable\"] as { index: number } | undefined)?.index ?? 0\n : ((data[\"items\"] as string[] | undefined) ?? []).length;\n if (!toColumnId) return;\n if (fromColumnId === toColumnId && String(active.id) === String(over.id)) return;\n return { cardId: String(active.id), fromColumnId, toColumnId, toIndex };\n}\n","import type { KeyboardCoordinateGetter } from \"@dnd-kit/core\";\nimport { sortableKeyboardCoordinates } from \"@dnd-kit/sortable\";\n\n/**\n * Arrow up/down sorts inside a lane; left/right jumps lanes, which the stock\n * sortable getter does not do.\n */\nexport const boardCoordinateGetter: KeyboardCoordinateGetter = (event, args) => {\n const { currentCoordinates, context } = args;\n if (event.code === \"ArrowLeft\" || event.code === \"ArrowRight\") {\n const containers = context.droppableContainers\n .getEnabled()\n .filter((c) => c.data.current?.[\"type\"] === \"column-body\")\n .sort((a, b) => (a.rect.current?.left ?? 0) - (b.rect.current?.left ?? 0));\n const activeRect = context.active?.rect.current.translated;\n if (!activeRect) return undefined;\n const index = containers.findIndex(\n (c) => (c.rect.current?.left ?? 0) <= activeRect.left + 1 &&\n (c.rect.current?.right ?? 0) >= activeRect.left - 1,\n );\n const next = containers[index + (event.code === \"ArrowRight\" ? 1 : -1)];\n const rect = next?.rect.current;\n if (!rect) return undefined;\n event.preventDefault();\n return { x: rect.left + rect.width / 2 - activeRect.width / 2, y: currentCoordinates.y };\n }\n return sortableKeyboardCoordinates(event, args);\n};\n\n","import {\n createContext,\n forwardRef,\n useCallback,\n useContext,\n useId,\n useMemo,\n useState,\n type HTMLAttributes,\n type ReactNode,\n} from \"react\";\nimport {\n DndContext,\n DragOverlay,\n KeyboardSensor,\n PointerSensor,\n closestCorners,\n pointerWithin,\n MeasuringStrategy,\n\n useDroppable,\n useSensor,\n useSensors,\n type CollisionDetection,\n type DragEndEvent,\n type DragOverEvent,\n type DragStartEvent,\n} from \"@dnd-kit/core\";\nimport {\n SortableContext,\n horizontalListSortingStrategy,\n useSortable,\n verticalListSortingStrategy,\n} from \"@dnd-kit/sortable\";\nimport { CSS } from \"@dnd-kit/utilities\";\nimport { Button } from \"../Button\";\nimport { Icon } from \"../Icon\";\nimport { mergeRefs } from \"../../internal/mergeRefs\";\nimport { cardMove, columnMove } from \"./moves\";\nimport { boardCoordinateGetter } from \"./keyboard\";\nimport \"./style.css\";\n\nexport interface BoardMove {\n cardId: string;\n fromColumnId: string;\n toColumnId: string;\n /** Index the card lands on inside the destination column. */\n toIndex: number;\n}\n\nexport interface BoardColumnMove {\n columnId: string;\n toIndex: number;\n}\n\ninterface BoardContextValue {\n activeId: string | null;\n activeType: \"card\" | \"column\" | null;\n /** The lane the pointer is currently over, whatever it is actually over. */\n overColumnId: string | null;\n}\n\n/**\n * A lane's body sits inside the lane's frame, so a raw corner-distance\n * strategy lets the body steal the hit from the lane itself — most visibly at\n * the board ends, where the last lane then never shifts aside to open the\n * drop gap. Resolve by what is being dragged: a column only ever hits other\n * columns; a card hits the card under the pointer first, then the lane body\n * under it, then whatever is nearest.\n */\nconst boardCollisionDetection: CollisionDetection = (args) => {\n const isColumn = args.active.data.current?.[\"type\"] === \"column\";\n const containers = args.droppableContainers.filter((container) => {\n const type = container.data.current?.[\"type\"];\n return isColumn ? type === \"column\" : type !== \"column\";\n });\n const scoped = { ...args, droppableContainers: containers };\n if (isColumn) return closestCorners(scoped);\n const within = pointerWithin(scoped);\n if (within.length > 0) {\n const rank = (id: (typeof within)[number][\"id\"]) =>\n containers.find((c) => c.id === id)?.data.current?.[\"type\"] === \"card\" ? 0 : 1;\n return [...within].sort((a, b) => rank(a.id) - rank(b.id));\n }\n return closestCorners(scoped);\n};\n\nconst BoardContext = createContext<BoardContextValue>({\n activeId: null,\n activeType: null,\n overColumnId: null,\n});\n\nexport interface BoardProps extends Omit<HTMLAttributes<HTMLDivElement>, \"onDragEnd\"> {\n /** Column ids in render order. Required for columns to be re-orderable. */\n columnIds?: string[];\n /** Fired once a card is dropped somewhere new. Apply it to your own state. */\n onMove?: (move: BoardMove) => void;\n /** Fired once a column is dropped in a new position. */\n onColumnMove?: (move: BoardColumnMove) => void;\n /** Rendered inside the drag overlay while a card is in the air. */\n renderDragOverlay?: (cardId: string) => ReactNode;\n /** Rendered inside the drag overlay while a column is in the air. */\n renderColumnDragOverlay?: (columnId: string) => ReactNode;\n children?: ReactNode;\n}\n\n/**\n * A column board with pointer and keyboard dragging. The board owns the drag\n * mechanics only — the card data stays yours; react to `onMove`.\n */\nexport const Board = forwardRef<HTMLDivElement, BoardProps>(function Board(\n {\n columnIds,\n onMove,\n onColumnMove,\n renderDragOverlay,\n renderColumnDragOverlay,\n className,\n children,\n ...rest\n },\n ref,\n) {\n const dndId = useId();\n const [activeId, setActiveId] = useState<string | null>(null);\n const [activeType, setActiveType] = useState<\"card\" | \"column\" | null>(null);\n const [overColumnId, setOverColumnId] = useState<string | null>(null);\n const sensors = useSensors(\n useSensor(PointerSensor, { activationConstraint: { distance: 4 } }),\n useSensor(KeyboardSensor, { coordinateGetter: boardCoordinateGetter }),\n );\n\n const handleStart = useCallback((event: DragStartEvent) => {\n setActiveId(String(event.active.id));\n setActiveType(event.active.data.current?.[\"type\"] === \"column\" ? \"column\" : \"card\");\n }, []);\n\n /* Cards land on cards as often as on empty space, so the lane a drag is over\n * is always resolved back to a column id — one highlight, one meaning. */\n const handleOver = useCallback((event: DragOverEvent) => {\n const over = event.over;\n if (!over) {\n setOverColumnId(null);\n return;\n }\n const overData = over.data.current ?? {};\n const columnId =\n overData[\"type\"] === \"column\" ? String(over.id) : String(overData[\"columnId\"] ?? \"\");\n setOverColumnId(columnId || null);\n }, []);\n\n const reset = useCallback(() => {\n setActiveId(null);\n setActiveType(null);\n setOverColumnId(null);\n }, []);\n\n const handleEnd = useCallback(\n (event: DragEndEvent) => {\n const { active, over } = event;\n reset();\n if (!over) return;\n\n /* Columns reorder among themselves; a column never lands inside a lane. */\n if (active.data.current?.[\"type\"] === \"column\") {\n const move = columnMove(event, columnIds ?? []);\n if (move) onColumnMove?.(move);\n } else {\n const move = cardMove(event);\n if (move) onMove?.(move);\n }\n },\n [columnIds, onColumnMove, onMove, reset],\n );\n\n const value = useMemo(\n () => ({ activeId, activeType, overColumnId }),\n [activeId, activeType, overColumnId],\n );\n\n const board = (\n <div ref={ref} className={[\"ds-board\", className].filter(Boolean).join(\" \")} {...rest}>\n {children}\n </div>\n );\n\n return (\n <BoardContext.Provider value={value}>\n <DndContext\n id={`ds-board-${dndId}`}\n sensors={sensors}\n collisionDetection={boardCollisionDetection}\n /* Lanes scroll and change height mid-drag, so positions are re-read\n * continuously — without this the shift animation misfires. */\n measuring={{ droppable: { strategy: MeasuringStrategy.Always } }}\n\n onDragStart={handleStart}\n onDragOver={handleOver}\n onDragEnd={handleEnd}\n onDragCancel={reset}\n >\n {columnIds ? (\n <SortableContext items={columnIds} strategy={horizontalListSortingStrategy}>\n {board}\n </SortableContext>\n ) : (\n board\n )}\n <DragOverlay>\n {activeId && activeType === \"card\" ? (\n <div className=\"ds-board-card\" data-overlay>\n <span className=\"ds-board-card__handle\" aria-hidden=\"true\">\n <Icon name=\"grip-vertical\" />\n </span>\n <div className=\"ds-board-card__content\">{renderDragOverlay?.(activeId)}</div>\n </div>\n ) : null}\n {activeId && activeType === \"column\" ? (\n <div className=\"ds-board-column\" data-overlay>\n {renderColumnDragOverlay?.(activeId)}\n </div>\n ) : null}\n </DragOverlay>\n </DndContext>\n </BoardContext.Provider>\n );\n});\n\nexport interface BoardColumnProps extends HTMLAttributes<HTMLDivElement> {\n id: string;\n title: string;\n /** Card ids in render order — the sortable contract for this column. */\n itemIds: string[];\n /** Shown beside the title; defaults to the number of cards. */\n count?: number;\n /** A control in the column head, e.g. an add button or a menu. */\n action?: ReactNode;\n /** Set false to pin a lane in place. */\n isDraggable?: boolean;\n children?: ReactNode;\n}\n\n/**\n * One lane of the board. Heavy frame, hairline head rule, scrolling body, and\n * a grip on top of the head that drags the whole lane sideways.\n */\nexport const BoardColumn = forwardRef<HTMLElement, BoardColumnProps>(\n function BoardColumn(\n { id, title, itemIds, count, action, isDraggable = true, className, children, ...rest },\n ref,\n ) {\n const { activeType, overColumnId } = useContext(BoardContext);\n const { setNodeRef: setBodyRef } = useDroppable({\n id: `${id}::body`,\n data: { type: \"column-body\", columnId: id, items: itemIds },\n });\n const {\n attributes,\n listeners,\n setNodeRef: setColumnRef,\n transform,\n transition,\n isDragging,\n } = useSortable({\n id,\n disabled: !isDraggable,\n data: { type: \"column\", columnId: id, items: itemIds },\n });\n\n return (\n <section\n ref={mergeRefs(setColumnRef, ref)}\n style={{ transform: CSS.Translate.toString(transform), transition }}\n className={[\"ds-board-column\", className].filter(Boolean).join(\" \")}\n data-over={(activeType === \"card\" && overColumnId === id) || undefined}\n data-dragging={isDragging || undefined}\n\n aria-label={title}\n {...rest}\n >\n {isDraggable && (\n <Button\n variant=\"simple\"\n size=\"sm\"\n iconStart=\"grip-horizontal\"\n className=\"ds-board-column__grip\"\n aria-label={`Reorder ${title}`}\n {...attributes}\n {...listeners}\n />\n )}\n <header className=\"ds-board-column__head\">\n <span className=\"ds-board-column__title\">{title}</span>\n <span className=\"ds-board-column__count\">{count ?? itemIds.length}</span>\n {action && <span className=\"ds-board-column__action\">{action}</span>}\n </header>\n <div ref={setBodyRef} className=\"ds-board-column__body\">\n <SortableContext items={itemIds} strategy={verticalListSortingStrategy}>\n {children}\n </SortableContext>\n </div>\n </section>\n );\n },\n);\n\nexport interface BoardCardProps extends HTMLAttributes<HTMLDivElement> {\n id: string;\n columnId: string;\n /** Accessible name announced when the card is picked up. */\n label?: string;\n children?: ReactNode;\n}\n\n/**\n * A draggable card. The grip on the left is the handle, so the card body can\n * hold links and buttons; the grip is a real button for keyboard users:\n * space to pick up, arrows to move, space to drop.\n */\nexport const BoardCard = forwardRef<HTMLDivElement, BoardCardProps>(function BoardCard(\n { id, columnId, label, className, children, ...rest },\n ref,\n) {\n const { attributes, listeners, setNodeRef, transform, transition, isDragging } =\n useSortable({ id, data: { type: \"card\", columnId } });\n const { activeId } = useContext(BoardContext);\n\n return (\n <div\n ref={mergeRefs(setNodeRef, ref)}\n style={{ transform: CSS.Translate.toString(transform), transition }}\n className={[\"ds-board-card\", className].filter(Boolean).join(\" \")}\n data-dragging={isDragging || undefined}\n data-idle={activeId && activeId !== id ? \"\" : undefined}\n {...rest}\n >\n <Button\n variant=\"simple\"\n size=\"sm\"\n isIconOnly\n iconStart=\"grip-vertical\"\n className=\"ds-board-card__handle\"\n aria-label={label ? `Move ${label}` : \"Move card\"}\n {...attributes}\n {...listeners}\n />\n <div className=\"ds-board-card__content\">{children}</div>\n </div>\n );\n});\n"],"mappings":"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"}
@@ -0,0 +1,191 @@
1
+ import { Field as e } from "../components/Field/index.js";
2
+ import { TextArea as t, TextField as n } from "../components/TextField/index.js";
3
+ import { n as r, r as i, t as a } from "./RichTextEditor-Dnx4i7bQ.js";
4
+ import { EntryPicker as o } from "../components/EntryPicker/index.js";
5
+ import { NumberField as s } from "../components/NumberField/index.js";
6
+ import { Switch as c } from "../components/Switch/index.js";
7
+ import { useState as l } from "react";
8
+ import { jsx as u } from "react/jsx-runtime";
9
+ //#region src/components/EntryEditor/model.ts
10
+ var d = /* @__PURE__ */ new Set([
11
+ "shortText",
12
+ "longText",
13
+ "richText",
14
+ "number",
15
+ "boolean",
16
+ "dateTime",
17
+ "asset",
18
+ "reference"
19
+ ]);
20
+ function f(e) {
21
+ let t = /* @__PURE__ */ new Set();
22
+ for (let n of e.fields) {
23
+ if (!n.id?.trim() || t.has(n.id)) throw Error("Content model field IDs must be nonempty and unique.");
24
+ if (!d.has(n.type)) throw Error(`Unsupported content field type: ${n.type}`);
25
+ t.add(n.id);
26
+ }
27
+ }
28
+ function p(e) {
29
+ if (typeof e != "string") return;
30
+ let t = /^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2})(?::(\d{2})(?:\.(\d{1,3}))?)?(?:Z|\+00:00)$/.exec(e);
31
+ if (!t) return;
32
+ let n = `${t[1]}:${t[2] ?? "00"}.${(t[3] ?? "").padEnd(3, "0")}Z`, r = new Date(n);
33
+ if (Number.isFinite(r.getTime()) && r.toISOString() === n) return n;
34
+ }
35
+ function m(e, t) {
36
+ return !!e && typeof e == "object" && t in e && typeof e[t] == "string" && e[t].trim().length > 0;
37
+ }
38
+ function h(e) {
39
+ return !!e.text?.trim() || e.type === "horizontalRule" || !!e.content?.some(h);
40
+ }
41
+ function g(e) {
42
+ return `${e.label} is required.`;
43
+ }
44
+ function _(e, t) {
45
+ if (typeof t != "string") return "Enter text.";
46
+ if (e.required && !t.trim()) return g(e);
47
+ }
48
+ function v(e, t) {
49
+ let n = r(t);
50
+ if (n) return n;
51
+ if (e.required && !h(t)) return g(e);
52
+ }
53
+ function y(e, t) {
54
+ let n = e.type === "asset" ? "assetId" : "entryId";
55
+ if (!(e.multiple ? Array.isArray(t) && t.every((e) => m(e, n)) : m(t, n))) return e.multiple ? `Choose a list of ${e.type === "asset" ? "assets" : "entries"}.` : `Choose an ${e.type === "asset" ? "asset" : "entry"}.`;
56
+ if (e.required && Array.isArray(t) && t.length === 0) return g(e);
57
+ }
58
+ var b = {
59
+ shortText: _,
60
+ longText: _,
61
+ richText: v,
62
+ number: (e, t) => typeof t == "number" && Number.isFinite(t) ? void 0 : "Enter a finite number.",
63
+ boolean: (e, t) => typeof t == "boolean" ? void 0 : "Choose a boolean value.",
64
+ dateTime: (e, t) => p(t) ? void 0 : "Enter a valid UTC date and time.",
65
+ asset: y,
66
+ reference: y
67
+ };
68
+ function x(e, t) {
69
+ return f(e), Object.fromEntries(e.fields.flatMap((e) => {
70
+ let n = Object.hasOwn(t, e.id) ? t[e.id] : void 0, r = n === void 0 ? e.required ? g(e) : void 0 : b[e.type](e, n);
71
+ return r ? [[e.id, r]] : [];
72
+ }));
73
+ }
74
+ //#endregion
75
+ //#region src/components/EntryEditor/index.tsx
76
+ function S(e, t) {
77
+ let n = e.type === "asset" ? "assetId" : "entryId";
78
+ return (Array.isArray(t) ? t : t === void 0 ? [] : [t]).flatMap((e) => {
79
+ if (!e || typeof e != "object" || !(n in e)) return [];
80
+ let t = e[n];
81
+ return typeof t == "string" ? [t] : [];
82
+ });
83
+ }
84
+ function C(e, t) {
85
+ if (e.type === "asset") {
86
+ let n = t.map((e) => ({ assetId: e }));
87
+ return e.multiple ? n : n[0];
88
+ }
89
+ let n = t.map((e) => ({ entryId: e }));
90
+ return e.multiple ? n : n[0];
91
+ }
92
+ function w({ field: e, stored: r, items: l, isDisabled: d, accessibility: { required: f, ...m }, onChange: h }) {
93
+ switch (e.type) {
94
+ case "shortText":
95
+ case "longText": {
96
+ let i = e.type === "shortText" ? n : t;
97
+ return /* @__PURE__ */ u(i, {
98
+ ...m,
99
+ required: f,
100
+ disabled: d,
101
+ placeholder: e.placeholder,
102
+ value: typeof r == "string" ? r : "",
103
+ onChange: (e) => h(e.target.value)
104
+ });
105
+ }
106
+ case "number": return /* @__PURE__ */ u(s, {
107
+ ...m,
108
+ required: f,
109
+ isDisabled: d,
110
+ placeholder: e.placeholder,
111
+ step: "any",
112
+ hasSteppers: !1,
113
+ value: typeof r == "number" && Number.isFinite(r) ? r : "",
114
+ onValueChange: (e) => {
115
+ (e === "" || Number.isFinite(e)) && h(e === "" ? void 0 : e);
116
+ }
117
+ });
118
+ case "boolean": return /* @__PURE__ */ u(c, {
119
+ ...m,
120
+ isDisabled: d,
121
+ isSelected: r === !0,
122
+ onSelectedChange: h
123
+ });
124
+ case "dateTime": return /* @__PURE__ */ u(n, {
125
+ ...m,
126
+ required: f,
127
+ disabled: d,
128
+ type: "datetime-local",
129
+ step: "0.001",
130
+ value: p(r)?.slice(0, -1) ?? "",
131
+ onChange: (e) => h(p(`${e.target.value}Z`))
132
+ });
133
+ case "richText": return /* @__PURE__ */ u(a, {
134
+ ...m,
135
+ label: e.label,
136
+ isRequired: f,
137
+ isDisabled: d,
138
+ placeholder: e.placeholder,
139
+ format: "json",
140
+ value: r === void 0 ? i : r,
141
+ onChange: h
142
+ });
143
+ case "asset":
144
+ case "reference": return /* @__PURE__ */ u("div", {
145
+ ...m,
146
+ role: "group",
147
+ children: /* @__PURE__ */ u(o, {
148
+ items: l,
149
+ value: S(e, r),
150
+ label: e.type === "asset" ? "Assets" : "Entries",
151
+ addLabel: `Choose ${e.label}`,
152
+ mode: e.multiple ? "multiple" : "single",
153
+ isDisabled: d,
154
+ onValueChange: (t) => h(C(e, t))
155
+ })
156
+ });
157
+ }
158
+ }
159
+ function T({ model: t, value: n, defaultValue: r = {}, onValueChange: i, pickerItems: a = {}, errors: o = {}, isDisabled: s = !1, showAllErrors: c = !1, className: d, ...f }) {
160
+ let [p, m] = l(r), [h, g] = l(() => /* @__PURE__ */ new Set()), _ = n ?? p, v = x(t, _), y = (e, t) => {
161
+ if (s) return;
162
+ let r = { ..._ };
163
+ t === void 0 ? delete r[e] : r[e] = t, n === void 0 && m(r), g((t) => new Set(t).add(e)), i?.(r);
164
+ };
165
+ return /* @__PURE__ */ u("div", {
166
+ className: ["ds-entry-editor", d].filter(Boolean).join(" "),
167
+ ...f,
168
+ children: t.fields.map((t) => {
169
+ let n = Object.hasOwn(_, t.id) ? _[t.id] : void 0, r = t.type === "asset" || t.type === "reference", i = t.type === "dateTime" ? `${t.label} (UTC)` : t.label, l = o[t.id] ?? (c || h.has(t.id) ? v[t.id] : void 0);
170
+ return /* @__PURE__ */ u(e, {
171
+ label: i,
172
+ hint: t.hint,
173
+ error: l,
174
+ isRequired: t.required,
175
+ isGroup: r,
176
+ children: (e) => /* @__PURE__ */ u(w, {
177
+ field: t,
178
+ stored: n,
179
+ items: a[t.id] ?? [],
180
+ isDisabled: s,
181
+ accessibility: e,
182
+ onChange: (e) => y(t.id, e)
183
+ })
184
+ }, t.id);
185
+ })
186
+ });
187
+ }
188
+ //#endregion
189
+ export { x as n, T as t };
190
+
191
+ //# sourceMappingURL=EntryEditor-CcyYtH74.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EntryEditor-CcyYtH74.js","names":["documentError","emptyDocument","RichTextEditor","fieldTypes","assertModel","ids","field","model","parseUtcDateTime","value","match","canonical","date","isLink","key","hasRichText","node","requiredError","textError","richTextError","error","linkError","item","validators","validateEntryValues","values","selectedIds","link","id","pickerValue","links","EntryControl","accessibility","Control","TextField","TextArea","isDisabled","stored","onChange","event","NumberField","next","Switch","required","EntryPicker","EntryEditor","rest","internal","setInternal","useState","defaultValue","changed","setChanged","current","validation","commit","previous","onValueChange","className","isPicker","label","errors","showAllErrors","Field","pickerItems"],"sources":["../../src/components/EntryEditor/model.ts","../../src/components/EntryEditor/index.tsx"],"sourcesContent":["import type { JSONContent } from \"@tiptap/react\";\nimport { documentError } from \"../RichTextEditor/document\";\nimport type { RichTextDocument } from \"../RichTextEditor\";\n\ninterface ContentFieldBase {\n id: string;\n label: string;\n hint?: string;\n placeholder?: string;\n required?: boolean;\n}\n\nexport type ContentField = ContentFieldBase & (\n | { type: \"shortText\" | \"longText\" | \"richText\" | \"number\" | \"boolean\" | \"dateTime\"; multiple?: never }\n | { type: \"asset\" | \"reference\"; multiple?: boolean }\n);\n\nexport interface ContentModel {\n fields: readonly ContentField[];\n}\n\nexport interface AssetValue { assetId: string }\nexport interface ReferenceValue { entryId: string }\nexport type EntryFieldValue = string | number | boolean | RichTextDocument | AssetValue | ReferenceValue | AssetValue[] | ReferenceValue[];\nexport type EntryValues = Record<string, EntryFieldValue | undefined>;\nexport type EntryErrors = Partial<Record<string, string>>;\n\nconst fieldTypes = new Set([\"shortText\", \"longText\", \"richText\", \"number\", \"boolean\", \"dateTime\", \"asset\", \"reference\"]);\n\nfunction assertModel(model: ContentModel) {\n const ids = new Set<string>();\n for (const field of model.fields) {\n if (!field.id?.trim() || ids.has(field.id)) throw new Error(\"Content model field IDs must be nonempty and unique.\");\n if (!fieldTypes.has(field.type)) throw new Error(`Unsupported content field type: ${field.type}`);\n ids.add(field.id);\n }\n}\n\n/** Accept UTC ISO strings without rolling invalid dates into the next month. */\nexport function parseUtcDateTime(value: unknown): string | undefined {\n if (typeof value !== \"string\") return;\n const match = /^(\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2})(?::(\\d{2})(?:\\.(\\d{1,3}))?)?(?:Z|\\+00:00)$/.exec(value);\n if (!match) return;\n const canonical = `${match[1]}:${match[2] ?? \"00\"}.${(match[3] ?? \"\").padEnd(3, \"0\")}Z`;\n const date = new Date(canonical);\n if (Number.isFinite(date.getTime()) && date.toISOString() === canonical) return canonical;\n}\n\nfunction isLink(value: unknown, key: \"assetId\" | \"entryId\"): boolean {\n return !!value && typeof value === \"object\" && key in value &&\n typeof (value as Record<string, unknown>)[key] === \"string\" &&\n ((value as Record<string, string>)[key]).trim().length > 0;\n}\n\nfunction hasRichText(node: JSONContent): boolean {\n return !!node.text?.trim() || node.type === \"horizontalRule\" || !!node.content?.some(hasRichText);\n}\n\nfunction requiredError(field: ContentField): string {\n return `${field.label} is required.`;\n}\n\nfunction textError(field: ContentField, value: unknown): string | undefined {\n if (typeof value !== \"string\") return \"Enter text.\";\n if (field.required && !value.trim()) return requiredError(field);\n}\n\nfunction richTextError(field: ContentField, value: unknown): string | undefined {\n const error = documentError(value);\n if (error) return error;\n if (field.required && !hasRichText(value as RichTextDocument)) return requiredError(field);\n}\n\nfunction linkError(field: ContentField, value: unknown): string | undefined {\n const key = field.type === \"asset\" ? \"assetId\" : \"entryId\";\n const valid = field.multiple\n ? Array.isArray(value) && value.every((item) => isLink(item, key))\n : isLink(value, key);\n if (!valid) {\n return field.multiple\n ? `Choose a list of ${field.type === \"asset\" ? \"assets\" : \"entries\"}.`\n : `Choose an ${field.type === \"asset\" ? \"asset\" : \"entry\"}.`;\n }\n if (field.required && Array.isArray(value) && value.length === 0) return requiredError(field);\n}\n\nconst validators: Record<ContentField[\"type\"], (field: ContentField, value: unknown) => string | undefined> = {\n shortText: textError,\n longText: textError,\n richText: richTextError,\n number: (_field, value) => typeof value === \"number\" && Number.isFinite(value) ? undefined : \"Enter a finite number.\",\n boolean: (_field, value) => typeof value === \"boolean\" ? undefined : \"Choose a boolean value.\",\n dateTime: (_field, value) => parseUtcDateTime(value) ? undefined : \"Enter a valid UTC date and time.\",\n asset: linkError,\n reference: linkError,\n};\n\n/** Validate data loaded from JSON as well as values emitted by EntryEditor. */\nexport function validateEntryValues(model: ContentModel, values: Record<string, unknown>): EntryErrors {\n assertModel(model);\n return Object.fromEntries(model.fields.flatMap((field): [string, string][] => {\n const value = Object.hasOwn(values, field.id) ? values[field.id] : undefined;\n const error = value === undefined\n ? field.required ? requiredError(field) : undefined\n : validators[field.type](field, value);\n return error ? [[field.id, error]] : [];\n }));\n}\n","import { useState, type HTMLAttributes } from \"react\";\nimport { EntryPicker, type EntryItem } from \"../EntryPicker\";\nimport { Field, type FieldProps } from \"../Field\";\nimport { NumberField } from \"../NumberField\";\nimport { RichTextEditor, type RichTextDocument } from \"../RichTextEditor\";\nimport { emptyDocument } from \"../RichTextEditor/document\";\nimport { Switch } from \"../Switch\";\nimport { TextArea, TextField } from \"../TextField\";\nimport { parseUtcDateTime, validateEntryValues, type ContentField, type ContentModel, type EntryErrors, type EntryFieldValue, type EntryValues } from \"./model\";\nimport \"./style.css\";\n\nexport { validateEntryValues } from \"./model\";\nexport type { AssetValue, ContentField, ContentModel, EntryErrors, EntryFieldValue, EntryValues, ReferenceValue } from \"./model\";\n\nexport interface EntryEditorProps extends Omit<HTMLAttributes<HTMLDivElement>, \"defaultValue\" | \"onChange\"> {\n model: ContentModel;\n value?: EntryValues;\n defaultValue?: EntryValues;\n onValueChange?: (values: EntryValues) => void;\n /** Available assets or entries, keyed by content field ID. */\n pickerItems?: Record<string, EntryItem[]>;\n /** App-provided messages take precedence over built-in validation. */\n errors?: EntryErrors;\n isDisabled?: boolean;\n /** Reveal validation for untouched fields, for example after a submit attempt. */\n showAllErrors?: boolean;\n}\n\nfunction selectedIds(field: ContentField, value: EntryFieldValue | undefined): string[] {\n const key = field.type === \"asset\" ? \"assetId\" : \"entryId\";\n const links = Array.isArray(value) ? value : value === undefined ? [] : [value];\n return links.flatMap((link) => {\n if (!link || typeof link !== \"object\" || !(key in link)) return [];\n const id = (link as unknown as Record<string, unknown>)[key];\n return typeof id === \"string\" ? [id] : [];\n });\n}\n\nfunction pickerValue(field: ContentField, ids: string[]): EntryFieldValue | undefined {\n if (field.type === \"asset\") {\n const links = ids.map((assetId) => ({ assetId }));\n return field.multiple ? links : links[0];\n }\n const links = ids.map((entryId) => ({ entryId }));\n return field.multiple ? links : links[0];\n}\n\nfunction EntryControl({ field, stored, items, isDisabled, accessibility: { required, ...accessibility }, onChange }: {\n field: ContentField;\n stored: EntryFieldValue | undefined;\n items: EntryItem[];\n isDisabled: boolean;\n accessibility: Parameters<FieldProps[\"children\"]>[0];\n onChange: (value: EntryFieldValue | undefined) => void;\n}) {\n switch (field.type) {\n case \"shortText\":\n case \"longText\": {\n const Control = field.type === \"shortText\" ? TextField : TextArea;\n return <Control {...accessibility} required={required} disabled={isDisabled} placeholder={field.placeholder}\n value={typeof stored === \"string\" ? stored : \"\"} onChange={(event) => onChange(event.target.value)} />;\n }\n case \"number\":\n return <NumberField {...accessibility} required={required} isDisabled={isDisabled} placeholder={field.placeholder} step=\"any\"\n hasSteppers={false} value={typeof stored === \"number\" && Number.isFinite(stored) ? stored : \"\"}\n onValueChange={(next) => { if (next === \"\" || Number.isFinite(next)) onChange(next === \"\" ? undefined : next); }} />;\n case \"boolean\":\n return <Switch {...accessibility} isDisabled={isDisabled} isSelected={stored === true} onSelectedChange={onChange} />;\n case \"dateTime\":\n return <TextField {...accessibility} required={required} disabled={isDisabled} type=\"datetime-local\" step=\"0.001\"\n value={parseUtcDateTime(stored)?.slice(0, -1) ?? \"\"}\n onChange={(event) => onChange(parseUtcDateTime(`${event.target.value}Z`))} />;\n case \"richText\":\n return <RichTextEditor {...accessibility} label={field.label} isRequired={required} isDisabled={isDisabled}\n placeholder={field.placeholder} format=\"json\" value={stored === undefined ? emptyDocument : stored as RichTextDocument}\n onChange={onChange} />;\n case \"asset\":\n case \"reference\":\n return <div {...accessibility} role=\"group\">\n <EntryPicker items={items} value={selectedIds(field, stored)}\n label={field.type === \"asset\" ? \"Assets\" : \"Entries\"} addLabel={`Choose ${field.label}`}\n mode={field.multiple ? \"multiple\" : \"single\"} isDisabled={isDisabled}\n onValueChange={(ids) => onChange(pickerValue(field, ids))} />\n </div>;\n }\n}\n\n/** An ordered set of content inputs; submission and persistence belong to the app. */\nexport function EntryEditor({\n model,\n value,\n defaultValue = {},\n onValueChange,\n pickerItems = {},\n errors = {},\n isDisabled = false,\n showAllErrors = false,\n className,\n ...rest\n}: EntryEditorProps) {\n const [internal, setInternal] = useState<EntryValues>(defaultValue);\n const [changed, setChanged] = useState<Set<string>>(() => new Set());\n const current = value ?? internal;\n const validation = validateEntryValues(model, current);\n\n const commit = (id: string, next: EntryFieldValue | undefined) => {\n if (isDisabled) return;\n const values = { ...current };\n if (next === undefined) delete values[id];\n else values[id] = next;\n if (value === undefined) setInternal(values);\n setChanged((previous) => new Set(previous).add(id));\n onValueChange?.(values);\n };\n\n return (\n <div className={[\"ds-entry-editor\", className].filter(Boolean).join(\" \")} {...rest}>\n {model.fields.map((field) => {\n const stored = Object.hasOwn(current, field.id) ? current[field.id] : undefined;\n const isPicker = field.type === \"asset\" || field.type === \"reference\";\n const label = field.type === \"dateTime\" ? `${field.label} (UTC)` : field.label;\n const error = errors[field.id] ?? (showAllErrors || changed.has(field.id) ? validation[field.id] : undefined);\n return (\n <Field key={field.id} label={label} hint={field.hint} error={error} isRequired={field.required} isGroup={isPicker}>\n {(accessibility) => <EntryControl field={field} stored={stored} items={pickerItems[field.id] ?? []}\n isDisabled={isDisabled} accessibility={accessibility} onChange={(next) => commit(field.id, next)} />}\n </Field>\n );\n })}\n </div>\n );\n}\n"],"mappings":"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"}