@namroodinc/liquid-editorial 0.1.6 → 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 (203) hide show
  1. package/AGENTS.md +4 -0
  2. package/README.md +42 -0
  3. package/UPGRADING.md +93 -11
  4. package/dist/chunks/Board-BLhB5_xx.cjs +2 -0
  5. package/dist/chunks/Board-BLhB5_xx.cjs.map +1 -0
  6. package/dist/chunks/Board-BTFSzn6c.js +239 -0
  7. package/dist/chunks/Board-BTFSzn6c.js.map +1 -0
  8. package/dist/chunks/EntryEditor-CcyYtH74.js +191 -0
  9. package/dist/chunks/EntryEditor-CcyYtH74.js.map +1 -0
  10. package/dist/chunks/EntryEditor-DIpc1iPF.cjs +2 -0
  11. package/dist/chunks/EntryEditor-DIpc1iPF.cjs.map +1 -0
  12. package/dist/chunks/{RichTextEditor-DC_0oN1s.cjs → RichTextEditor-BxtlbjXj.cjs} +2 -2
  13. package/dist/chunks/RichTextEditor-BxtlbjXj.cjs.map +1 -0
  14. package/dist/chunks/{RichTextEditor-CSpjsL1z.js → RichTextEditor-Dnx4i7bQ.js} +104 -93
  15. package/dist/chunks/RichTextEditor-Dnx4i7bQ.js.map +1 -0
  16. package/dist/chunks/mergeRefs-Bc3qS0Tt.cjs +2 -0
  17. package/dist/chunks/mergeRefs-Bc3qS0Tt.cjs.map +1 -0
  18. package/dist/chunks/mergeRefs-DGM402mE.js +10 -0
  19. package/dist/chunks/mergeRefs-DGM402mE.js.map +1 -0
  20. package/dist/components/AppShell/index.cjs +2 -0
  21. package/dist/components/AppShell/index.cjs.map +1 -0
  22. package/dist/components/AppShell/index.d.cts +15 -0
  23. package/dist/components/AppShell/index.d.cts.map +1 -0
  24. package/dist/components/AppShell/index.d.ts +15 -0
  25. package/dist/components/AppShell/index.d.ts.map +1 -0
  26. package/dist/components/AppShell/index.js +93 -0
  27. package/dist/components/AppShell/index.js.map +1 -0
  28. package/dist/components/AppShell/style.css +79 -0
  29. package/dist/components/AssetGrid/index.cjs +1 -1
  30. package/dist/components/AssetGrid/index.cjs.map +1 -1
  31. package/dist/components/AssetGrid/index.d.cts.map +1 -1
  32. package/dist/components/AssetGrid/index.d.ts.map +1 -1
  33. package/dist/components/AssetGrid/index.js +57 -59
  34. package/dist/components/AssetGrid/index.js.map +1 -1
  35. package/dist/components/AssetGrid/style.css +8 -16
  36. package/dist/components/Board/index.cjs +1 -2
  37. package/dist/components/Board/index.d.cts.map +1 -1
  38. package/dist/components/Board/index.d.ts.map +1 -1
  39. package/dist/components/Board/index.js +1 -227
  40. package/dist/components/Board/keyboard.d.cts +7 -0
  41. package/dist/components/Board/keyboard.d.cts.map +1 -0
  42. package/dist/components/Board/keyboard.d.ts +7 -0
  43. package/dist/components/Board/keyboard.d.ts.map +1 -0
  44. package/dist/components/Board/keyboard.test.d.cts +2 -0
  45. package/dist/components/Board/keyboard.test.d.cts.map +1 -0
  46. package/dist/components/Board/keyboard.test.d.ts +2 -0
  47. package/dist/components/Board/keyboard.test.d.ts.map +1 -0
  48. package/dist/components/Board/moves.d.cts +5 -0
  49. package/dist/components/Board/moves.d.cts.map +1 -0
  50. package/dist/components/Board/moves.d.ts +5 -0
  51. package/dist/components/Board/moves.d.ts.map +1 -0
  52. package/dist/components/Board/moves.test.d.cts +2 -0
  53. package/dist/components/Board/moves.test.d.cts.map +1 -0
  54. package/dist/components/Board/moves.test.d.ts +2 -0
  55. package/dist/components/Board/moves.test.d.ts.map +1 -0
  56. package/dist/components/Board/style.css +17 -28
  57. package/dist/components/Button/index.cjs +1 -1
  58. package/dist/components/Button/index.cjs.map +1 -1
  59. package/dist/components/Button/index.d.cts +3 -2
  60. package/dist/components/Button/index.d.cts.map +1 -1
  61. package/dist/components/Button/index.d.ts +3 -2
  62. package/dist/components/Button/index.d.ts.map +1 -1
  63. package/dist/components/Button/index.js +1 -1
  64. package/dist/components/Button/index.js.map +1 -1
  65. package/dist/components/Button/style.css +21 -15
  66. package/dist/components/DatePicker/index.cjs +1 -1
  67. package/dist/components/DatePicker/index.cjs.map +1 -1
  68. package/dist/components/DatePicker/index.d.cts.map +1 -1
  69. package/dist/components/DatePicker/index.d.ts.map +1 -1
  70. package/dist/components/DatePicker/index.js +76 -72
  71. package/dist/components/DatePicker/index.js.map +1 -1
  72. package/dist/components/DatePicker/style.css +4 -61
  73. package/dist/components/Drawer/index.cjs +1 -1
  74. package/dist/components/Drawer/index.cjs.map +1 -1
  75. package/dist/components/Drawer/index.d.cts +10 -1
  76. package/dist/components/Drawer/index.d.cts.map +1 -1
  77. package/dist/components/Drawer/index.d.ts +10 -1
  78. package/dist/components/Drawer/index.d.ts.map +1 -1
  79. package/dist/components/Drawer/index.js +78 -49
  80. package/dist/components/Drawer/index.js.map +1 -1
  81. package/dist/components/Drawer/style.css +106 -0
  82. package/dist/components/EntryEditor/index.cjs +1 -1
  83. package/dist/components/EntryEditor/index.d.cts.map +1 -1
  84. package/dist/components/EntryEditor/index.d.ts.map +1 -1
  85. package/dist/components/EntryEditor/index.js +1 -1
  86. package/dist/components/EntryEditor/model.d.cts.map +1 -1
  87. package/dist/components/EntryEditor/model.d.ts.map +1 -1
  88. package/dist/components/EntryPicker/index.cjs +1 -1
  89. package/dist/components/EntryPicker/index.cjs.map +1 -1
  90. package/dist/components/EntryPicker/index.d.cts.map +1 -1
  91. package/dist/components/EntryPicker/index.d.ts.map +1 -1
  92. package/dist/components/EntryPicker/index.js +12 -11
  93. package/dist/components/EntryPicker/index.js.map +1 -1
  94. package/dist/components/EntryPicker/style.css +3 -30
  95. package/dist/components/Field/style.css +11 -3
  96. package/dist/components/Menu/index.cjs +1 -1
  97. package/dist/components/Menu/index.cjs.map +1 -1
  98. package/dist/components/Menu/index.d.cts.map +1 -1
  99. package/dist/components/Menu/index.d.ts.map +1 -1
  100. package/dist/components/Menu/index.js +11 -16
  101. package/dist/components/Menu/index.js.map +1 -1
  102. package/dist/components/Menu/style.css +7 -41
  103. package/dist/components/Modal/index.cjs +1 -1
  104. package/dist/components/Modal/index.cjs.map +1 -1
  105. package/dist/components/Modal/index.d.cts.map +1 -1
  106. package/dist/components/Modal/index.d.ts.map +1 -1
  107. package/dist/components/Modal/index.js +12 -12
  108. package/dist/components/Modal/index.js.map +1 -1
  109. package/dist/components/NumberField/index.cjs +1 -1
  110. package/dist/components/NumberField/index.cjs.map +1 -1
  111. package/dist/components/NumberField/index.js +13 -17
  112. package/dist/components/NumberField/index.js.map +1 -1
  113. package/dist/components/NumberField/style.css +1 -41
  114. package/dist/components/Pagination/index.cjs +1 -1
  115. package/dist/components/Pagination/index.cjs.map +1 -1
  116. package/dist/components/Pagination/index.js +1 -1
  117. package/dist/components/Pagination/index.js.map +1 -1
  118. package/dist/components/Pagination/style.css +0 -1
  119. package/dist/components/Popover/index.cjs +1 -1
  120. package/dist/components/Popover/index.cjs.map +1 -1
  121. package/dist/components/Popover/index.js +4 -4
  122. package/dist/components/RichTextEditor/index.cjs +1 -1
  123. package/dist/components/RichTextEditor/index.d.cts.map +1 -1
  124. package/dist/components/RichTextEditor/index.d.ts.map +1 -1
  125. package/dist/components/RichTextEditor/index.js +1 -1
  126. package/dist/components/SearchField/index.cjs +1 -1
  127. package/dist/components/SearchField/index.cjs.map +1 -1
  128. package/dist/components/SearchField/index.d.cts.map +1 -1
  129. package/dist/components/SearchField/index.d.ts.map +1 -1
  130. package/dist/components/SearchField/index.js +26 -27
  131. package/dist/components/SearchField/index.js.map +1 -1
  132. package/dist/components/SearchField/style.css +0 -21
  133. package/dist/components/Select/index.cjs +1 -1
  134. package/dist/components/Select/index.cjs.map +1 -1
  135. package/dist/components/Select/index.d.cts.map +1 -1
  136. package/dist/components/Select/index.d.ts.map +1 -1
  137. package/dist/components/Select/index.js +101 -80
  138. package/dist/components/Select/index.js.map +1 -1
  139. package/dist/components/Select/style.css +5 -38
  140. package/dist/components/SideNav/index.cjs +1 -1
  141. package/dist/components/SideNav/index.cjs.map +1 -1
  142. package/dist/components/SideNav/index.d.cts.map +1 -1
  143. package/dist/components/SideNav/index.d.ts.map +1 -1
  144. package/dist/components/SideNav/index.js +52 -49
  145. package/dist/components/SideNav/index.js.map +1 -1
  146. package/dist/components/SideNav/style.css +2 -27
  147. package/dist/components/SortableList/index.cjs +1 -1
  148. package/dist/components/SortableList/index.cjs.map +1 -1
  149. package/dist/components/SortableList/index.d.cts.map +1 -1
  150. package/dist/components/SortableList/index.d.ts.map +1 -1
  151. package/dist/components/SortableList/index.js +55 -53
  152. package/dist/components/SortableList/index.js.map +1 -1
  153. package/dist/components/SortableList/style.css +9 -15
  154. package/dist/components/Stepper/index.cjs +1 -1
  155. package/dist/components/Stepper/index.cjs.map +1 -1
  156. package/dist/components/Stepper/index.d.cts.map +1 -1
  157. package/dist/components/Stepper/index.d.ts.map +1 -1
  158. package/dist/components/Stepper/index.js +48 -39
  159. package/dist/components/Stepper/index.js.map +1 -1
  160. package/dist/components/Stepper/style.css +24 -18
  161. package/dist/components/SubNav/index.cjs +1 -1
  162. package/dist/components/SubNav/index.cjs.map +1 -1
  163. package/dist/components/SubNav/index.d.cts.map +1 -1
  164. package/dist/components/SubNav/index.d.ts.map +1 -1
  165. package/dist/components/SubNav/index.js +27 -28
  166. package/dist/components/SubNav/index.js.map +1 -1
  167. package/dist/components/SubNav/style.css +13 -0
  168. package/dist/components/Switch/index.cjs +1 -1
  169. package/dist/components/Switch/index.cjs.map +1 -1
  170. package/dist/components/Switch/index.d.cts.map +1 -1
  171. package/dist/components/Switch/index.d.ts.map +1 -1
  172. package/dist/components/Switch/index.js +23 -21
  173. package/dist/components/Switch/index.js.map +1 -1
  174. package/dist/components/Switch/style.css +2 -28
  175. package/dist/components/TagsInput/index.cjs +1 -1
  176. package/dist/components/TagsInput/index.cjs.map +1 -1
  177. package/dist/components/TagsInput/index.js +8 -10
  178. package/dist/components/TagsInput/index.js.map +1 -1
  179. package/dist/components/TagsInput/style.css +2 -24
  180. package/dist/index.cjs +1 -1
  181. package/dist/index.d.cts +2 -0
  182. package/dist/index.d.cts.map +1 -1
  183. package/dist/index.d.ts +2 -0
  184. package/dist/index.d.ts.map +1 -1
  185. package/dist/index.js +39 -38
  186. package/dist/internal/mergeRefs.d.cts +4 -0
  187. package/dist/internal/mergeRefs.d.cts.map +1 -0
  188. package/dist/internal/mergeRefs.d.ts +4 -0
  189. package/dist/internal/mergeRefs.d.ts.map +1 -0
  190. package/dist/styles.css +1 -0
  191. package/dist/test/dragEvent.d.cts +3 -0
  192. package/dist/test/dragEvent.d.cts.map +1 -0
  193. package/dist/test/dragEvent.d.ts +3 -0
  194. package/dist/test/dragEvent.d.ts.map +1 -0
  195. package/package.json +2 -2
  196. package/dist/chunks/EntryEditor-C32lKKnV.cjs +0 -2
  197. package/dist/chunks/EntryEditor-C32lKKnV.cjs.map +0 -1
  198. package/dist/chunks/EntryEditor-xVuiqtFo.js +0 -181
  199. package/dist/chunks/EntryEditor-xVuiqtFo.js.map +0 -1
  200. package/dist/chunks/RichTextEditor-CSpjsL1z.js.map +0 -1
  201. package/dist/chunks/RichTextEditor-DC_0oN1s.cjs.map +0 -1
  202. package/dist/components/Board/index.cjs.map +0 -1
  203. package/dist/components/Board/index.js.map +0 -1
package/AGENTS.md CHANGED
@@ -4,6 +4,10 @@
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
+
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.
10
+
7
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.
8
12
 
9
13
  ## Using the installed package
package/README.md CHANGED
@@ -35,6 +35,16 @@ Every family also has a direct entry point:
35
35
  import { Button } from "@namroodinc/liquid-editorial/components/Button";
36
36
  ```
37
37
 
38
+ Use `Button` for action controls, including controls rendered inside other components. Its variants, sizes, icons, focus rings, and disabled states supply the shared appearance. Use `size="xs"` for compact actions inside a field or chip, such as clear and remove. For a custom square glyph, omit `iconStart` and provide visual children plus an accessible name:
39
+
40
+ ```tsx
41
+ <Button variant="simple" isIconOnly aria-label="Account: Ada Lovelace">
42
+ <Avatar name="Ada Lovelace" size={32} aria-hidden="true" />
43
+ </Button>
44
+ ```
45
+
46
+ Pending Buttons replace custom glyphs with the shared spinner. Button-backed selects and date fields retain the shared Field skin; Switch retains its track and knob while composing Button.
47
+
38
48
  If an application only needs the design tokens, import `@namroodinc/liquid-editorial/tokens.css`. A small global normalization is available separately at `@namroodinc/liquid-editorial/reset.css`; it is never included automatically.
39
49
 
40
50
  ## Content models
@@ -84,6 +94,38 @@ Links open a URL modal from the toolbar or by clicking an existing body link. Sa
84
94
 
85
95
  Empty `TableBody` children automatically show “No data found.” across the table; override the text with `Table.emptyMessage`. Use `isEmpty` when custom row components determine whether there are results, or set it to `false` to suppress the message while the UI loads data. Loading indicators and skeleton rows are composed by the consuming UI. Standalone `TableEmpty` uses the same default message and supports a custom `title`. See the [upgrade guide example](UPGRADING.md#013).
86
96
 
97
+ ## App shell
98
+
99
+ `AppShell` places a full-width `TopNav` above the desktop `SideNav` and a scrolling `main` landmark. Below 768px of **shell width**, a hamburger opens navigation in a right-side drawer. Its default height is `100dvh`; native `style`, `className`, div attributes, and a forwarded ref allow it to fit an embedded frame.
100
+
101
+ ```tsx
102
+ import { AppShell, Avatar, SideNavItem, SideNavSection } from "@namroodinc/liquid-editorial";
103
+
104
+ <AppShell
105
+ brand={<a href="/">Liquid</a>}
106
+ navigation={<>
107
+ <SideNavSection label="Workspace">
108
+ <SideNavItem icon="home" label="Home" href="/" />
109
+ <SideNavItem icon="file-text" label="Stories" href="/stories" isActive />
110
+ </SideNavSection>
111
+ </>}
112
+ actions={(location) => <>
113
+ <Avatar name="Ada Lovelace" size={32} />
114
+ {location === "drawer" && <span>Ada Lovelace</span>}
115
+ </>}
116
+ >
117
+ <h1>Stories</h1>
118
+ </AppShell>
119
+ ```
120
+
121
+ Pass sections/items as `navigation`, without an enclosing SideNav, header, footer, or collapse control. Applications own destinations and `isActive`; AppShell closes the drawer when a navigation link is activated. `navigationLabel` defaults to `"Main"`. Keep shared application state outside the responsive slots and use React `useId` inside custom slot components that need IDs, since the desktop and drawer presentations can coexist during transitions.
122
+
123
+ The optional `actions` render function receives `"topnav"` or `"drawer"`. Return a compact desktop control and an expanded mobile presentation; return nothing to omit the drawer footer. Keep this function pure. The component supplies no account, menu destinations, centre navigation, or space picker.
124
+
125
+ The drawer traps focus and locks body scroll. Escape, Close, the scrim, and navigation links dismiss it and return focus to the hamburger. A popup inside it, such as a Menu in the actions footer, closes first on Escape. Modifier-key clicks that open a link elsewhere leave the drawer open. Widening the shell to desktop closes it and moves focus to the active desktop link when focus was inside the drawer. Reduced motion skips the transition.
126
+
127
+ For other navigation compositions, `Drawer` supports `variant="navigation"`, a React-node `title`, `closeLabel`, and `portalContainer`. By default the drawer portals to `document.body` and covers the viewport; pass a positioned element to keep it inside that element. Supply `aria-label` for a dialog name independent of its visual brand. This variant retains an inert, accessibility-hidden panel during its closing transition; the default Drawer keeps its existing appearance and immediate unmounting.
128
+
87
129
  ## Image cropping
88
130
 
89
131
  `ImageCropper` uses the library's custom engine with a fixed crop frame, diagonal background, shared `Slider` with a `Field` label/hint, and optional quarter-turn controls. Shared `Stack` components put the rotate/Reset buttons to the right of the zoom slider and wrap them below on narrow layouts. Supply one positive, finite `aspect` (width / height): `1` for an avatar, `16 / 9` for a cover, or `4 / 5` for a portrait.
package/UPGRADING.md CHANGED
@@ -2,9 +2,72 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
- ## 0.1.6
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
+
30
+ ## 0.1.7
31
+
32
+ ### Changes
6
33
 
7
- ## 0.1.5
34
+ - Added `AppShell`, composing a full-width TopNav above SideNav and a scrolling main landmark. Below 768px of shell width, navigation opens in a right-side drawer with a branded header and an optional actions footer.
35
+ - Added Drawer `variant="navigation"`, React-node titles, `closeLabel`, and `portalContainer`. The navigation variant focuses Close first, slides/fades over `--duration-panel`, and retains an inert, accessibility-hidden panel until its exit completes. Explicit `aria-label` names the dialog instead of the visual title.
36
+ - Rebuilt the combined navigation example to match the App Shell handoff, with fullscreen, narrow, no-actions, and long-content stories. Standalone navigation components remain available.
37
+ - Fixed mobile navigation moving the underlying page during its slide-in. Navigation drawers clip their off-screen panel and move/restore focus without scrolling the shell.
38
+ - Audited the library and examples and replaced custom button markup with shared `Button`: shell/drawer/sidebar controls, field clear/remove/step actions, drag handles, menus, selection rows, calendar/Select options, date/select triggers, Switch, and interactive Stepper/SubNav controls. Removed duplicate action-button styling. A contributor lint rule rejects raw buttons outside Button and test fixtures.
39
+ - Icon-only Button now supports custom visual children when `iconStart` is absent, including Avatar and numeric calendar/progress glyphs. Pending state replaces those children with the shared spinner. Select/DateField retain the shared Field skin; Switch retains its track/knob mechanics.
40
+ - Added Button `size="xs"` (24px) for compact actions inside fields and chips. SearchField clear and TagsInput remove use it, so tags no longer make an md TagsInput taller and the clear button fits a small SearchField.
41
+ - Navigation Drawer covers the viewport, above page chrome, unless `portalContainer` is supplied. With a container, it stays inside that positioned element (AppShell does this). `side="start"` and right-to-left documents now slide out through the panel's own edge. Reduced motion applies to the panel, and panel content that does not scroll itself can scroll.
42
+ - Drawer lets popups inside it handle Escape first. Menu, Select, DateField, and Modal mark the Escape they handle with `preventDefault()`, so closing a menu in AppShell's drawer footer leaves the drawer open.
43
+ - AppShell renders without `ResizeObserver`. Modifier-key, non-primary-button, and `target="_blank"` link clicks no longer close its drawer.
44
+ - Fixed regressions from the Button migration. AssetTile hover and press no longer paint over the thumbnail. Interactive Stepper markers match the rail's marker size. Calendar days outside the shown month are muted again. MenuItem labels wrap, and danger shortcuts use the item's text colour. Board handles show the grab cursor with the aggregate stylesheet. SubNavButton keeps `.ds-subnav__item` and the SubNav rail. Drag overlays draw a decorative grip instead of a hidden button.
45
+
46
+ ### Adoption
47
+
48
+ Import `AppShell` from the root or `@namroodinc/liquid-editorial/components/AppShell`, retaining the existing aggregate CSS import. Move the product brand into `brand`, pass SideNavSection/SideNavItem content as `navigation`, and put page content directly inside AppShell. Do not wrap navigation in another SideNav or add a SideNavHeader/collapse toggle. The shell supplies the main landmark, so remove any enclosing/nested main from the replaced layout.
49
+
50
+ Keep routes and active links in application state. Supply `actions={(location) => ...}` to render compact `"topnav"` controls and expanded `"drawer"` content. Omit the prop, or return nothing for the drawer, to omit its footer. Use `navigationLabel` to change the default `"Main"` name. Keep render callbacks pure and shared state outside slots; use React `useId` inside custom components instead of fixed IDs in content rendered in both locations.
51
+
52
+ AppShell defaults to `100dvh`; override its height through native style/className for embedded frames. It responds to its own width with CSS container queries. The library owns drawer state and dismisses it on link activation and when the shell reaches 768px; no router dependency or prototype viewport prop is needed. The page-movement fix requires no prop changes; remove any local shell-scroll reset workaround added for the drawer animation.
53
+
54
+ The Button cleanup needs no changes to existing component props. Remove local copies of button appearance and selectors for removed `.ds-search__clear`, `.ds-tags__remove`, and `.ds-asset-tile__hit-label` markup. Use accessible names in tests. Compose custom square controls as `<Button isIconOnly aria-label="Account">...</Button>` with no `iconStart`, using `aria-label` or `aria-labelledby` for their accessible name. New library controls and examples must compose Button; the existing variants/sizes supply their appearance. Shared field skin and specialized widget layout remain supported. Use `size="xs"` for a Button placed inside a field or chip.
55
+
56
+ Use `Drawer variant="navigation"` without `portalContainer` to cover the viewport. To contain it in a layout region, pass a positioned element (for example, `position: relative`) as `portalContainer`. If a custom popup inside a Drawer handles Escape, call `event.preventDefault()` so the drawer stays open. Remove any ResizeObserver polyfill that was added only for AppShell.
57
+
58
+ ### Compatibility
59
+
60
+ The AppShell/Drawer props are additive. The Button cleanup intentionally standardizes action appearance and sizes: shell navigation controls use large 48px Buttons; clear/remove actions use extra-small 24px Buttons and drag handles use small Buttons; numeric steppers follow their field's standard Button size. Menu danger actions use the shared danger variant. SubNavButton composes Button but keeps the SubNav rail and its `.ds-subnav__item` class. Interactive Stepper markers are square Buttons at the rail's marker size, while read-only markers remain indicators. `ButtonSize` now includes `"xs"`; TypeScript maps keyed by every `ButtonSize` need an `xs` entry. Drawer handles Escape and Tab on `window`, so keyboard events dispatched on `document` or on any element still reach it. An Escape already marked with `preventDefault()` no longer closes it. Select now ignores Escape while its listbox is closed, so an enclosing Drawer can close. Select option rows are now disabled-capable button elements with `role="option"`; selection and keyboard contracts are unchanged. Custom icon-only children that were previously hidden now render when no `iconStart` is supplied. Default Drawer dismissal timing and string titles remain supported. Navigation Drawer exits remain in the DOM briefly but are immediately inert and hidden from accessibility queries; tests that require actual removal should await the transition. Closed shells render on the server without accessing browser globals. Supported React versions and package versions are unchanged; the release workflow owns version increments. There is no new runtime dependency.
61
+
62
+ ### Verification
63
+
64
+ Check 390px, 767px, 768px, and 1280px shell widths, including a narrow shell inside a wide viewport. Confirm the top bar spans the whole frame, the desktop rail has no brand/collapse header, content and navigation scroll independently, and the mobile footer appears only with actions. During opening and closing, confirm only the panel moves: the background frame and shell scroll offsets must stay fixed, including when main content is already scrolled. Test Escape, Close, scrim and link dismissal, initial Close focus, Tab/Shift+Tab trapping, focus restoration, body scroll restoration, desktop resize while open, reduced motion, and reopening during exit. Check multiple shell IDs and the consuming application's route/active-state behavior. Run the consuming project's typecheck, tests, and build after replacing its layout.
65
+
66
+ Open the navigation drawer in a narrow AppShell. Press Escape inside an actions Menu or Select and confirm only the popup closes. Cmd/Ctrl-click a navigation link and confirm the drawer stays open. Outside AppShell, open `Drawer variant="navigation"` on a scrolled page without `portalContainer`, and also with `side="start"`, `dir="rtl"`, reduced motion, and content taller than the panel. Run consuming tests that render AppShell without a ResizeObserver polyfill.
67
+
68
+ Compare the converted ordinary actions with standalone Buttons of the same size/variant, including keyboard focus and disabled state. Hover and press a selectable AssetTile and confirm the thumbnail stays visible. Check navigable Steppers at both sizes: connectors should run through marker centres, with labels level. Check calendar spill-over days, wrapped and danger MenuItems, Board grab cursors with the built aggregate CSS, SubNavs that mix links and buttons, tag chips in md TagsInput, and the clear button in a small SearchField. Check field clear/remove/step behavior, select/date validation and selection, disabled options, Switch state, menu actions, SubNav/Stepper activation, and pointer/keyboard dragging. Verify tag wrapping, calendar layout, and long selection rows at narrow widths and in every theme. Run library lint to enforce Button composition.
69
+
70
+ ## 0.1.6
8
71
 
9
72
  ### Changes
10
73
 
@@ -27,7 +90,11 @@ To adopt pagination, import `Pagination` from the package root or `@namroodinc/l
27
90
  ```tsx
28
91
  const [pagination, setPagination] = useState({ page: 1, pageSize: 25 });
29
92
 
30
- <Pagination totalResults={totalResults} {...pagination} onChange={setPagination} />
93
+ <Pagination
94
+ totalResults={totalResults}
95
+ {...pagination}
96
+ onChange={setPagination}
97
+ />;
31
98
  ```
32
99
 
33
100
  Use the emitted page and page size together for requests or local slicing. Changing size preserves the first visible result (page 3 at size 25 becomes page 2 at size 50). Keep canonical state in bounds when filtering changes the total; prop changes do not emit corrections. Supply valid counts and positive integer sizes, with a nonempty `pageSizeOptions` containing the current size. The defaults are `[10, 25, 50, 100]`. The component handles no data loading or slicing; compose it beneath a table or another result view. The existing aggregate CSS import includes its styles. Give multiple pagination bars distinct `aria-label` values.
@@ -40,16 +107,28 @@ Update every `ImageCropper` call to supply a positive, finite `aspect` (width /
40
107
 
41
108
  ```tsx
42
109
  import { useState } from "react";
43
- import { cropToBlob, type CropRect, type CropState } from "@namroodinc/liquid-editorial";
44
-
45
- const [selection, setSelection] = useState<{ crop: CropRect; state: CropState } | null>(null);
46
- <ImageCropper src={src} aspect={16 / 9}
47
- onChange={(crop, state) => setSelection({ crop, state })} />
110
+ import {
111
+ cropToBlob,
112
+ type CropRect,
113
+ type CropState,
114
+ } from "@namroodinc/liquid-editorial";
115
+
116
+ const [selection, setSelection] = useState<{
117
+ crop: CropRect;
118
+ state: CropState;
119
+ } | null>(null);
120
+ <ImageCropper
121
+ src={src}
122
+ aspect={16 / 9}
123
+ onChange={(crop, state) => setSelection({ crop, state })}
124
+ />;
48
125
 
49
126
  // In your save handler:
50
127
  if (selection) {
51
128
  const blob = await cropToBlob(src, selection.crop, {
52
- outputWidth: 1600, rotation: selection.state.rotation, type: "image/jpeg",
129
+ outputWidth: 1600,
130
+ rotation: selection.state.rotation,
131
+ type: "image/jpeg",
53
132
  });
54
133
  uploadCrop(blob);
55
134
  }
@@ -101,8 +180,11 @@ Use the shared empty message while keeping loading indicators in your applicatio
101
180
 
102
181
  ```tsx
103
182
  <TableFrame>
104
- <Table emptyMessage="No matching articles."
105
- isEmpty={!isLoading && articles.length === 0} aria-busy={isLoading}>
183
+ <Table
184
+ emptyMessage="No matching articles."
185
+ isEmpty={!isLoading && articles.length === 0}
186
+ aria-busy={isLoading}
187
+ >
106
188
  <TableHead>
107
189
  <TableRow>
108
190
  <TableHeaderCell>Title</TableHeaderCell>
@@ -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":"A,yQAGA,SAAgBkC,EAAW,CAAE,SAAQ,QAAsB,EAAkD,CAC3G,GAAI,CAACC,EAAM,OACX,IAAMC,EAAOD,EAAK,KAAK,SAAW,CAAC,EAC7BE,EAAWD,EAAKE,OAAY,SAAW,OAAOH,EAAK,EAAE,EAAI,OAAOC,EAAKG,UAAe,EAAE,EACtFC,EAAUC,EAAU,QAAQJ,CAAQ,EAC1C,GAAI,IAAU,GAAKA,IAAa,OAAOK,EAAO,EAAE,GAChD,MAAO,CAAE,SAAU,OAAOA,EAAO,EAAE,EAAG,SAAQ,CAChD,CAEA,SAAgBC,EAAS,CAAE,SAAQ,QAA6C,CAC9E,GAAI,CAACR,EAAM,OACX,IAAMS,EAAe,OAAOF,EAAO,KAAK,SAAUH,UAAe,EAAE,EAC7DH,EAAOD,EAAK,KAAK,SAAW,CAAC,EAC7BU,EAAST,EAAKE,OAAY,OAC1BQ,EAAa,OAAOV,EAAKG,WAAgBM,EAAS,GAAKV,EAAK,GAAG,EAE/DK,EAAUK,EACXT,EAAKW,UAA+C,OAAS,GAC5DX,EAAKY,OAAqC,CAAC,EAAA,CAAG,OACpD,GAAKF,IACDF,IAAiBE,GAAc,OAAOJ,EAAO,EAAE,IAAM,OAAOP,EAAK,EAAE,GACvE,MAAO,CAAE,OAAQ,OAAOO,EAAO,EAAE,EAAG,eAAc,aAAY,SAAQ,CACxE,CClBA,IAAaO,GAAmD,EAAO,IAAS,CAC9E,GAAM,CAAE,qBAAoB,WAAYC,EACxC,GAAIC,EAAM,OAAS,aAAeA,EAAM,OAAS,aAAc,CAC7D,IAAMC,EAAaC,EAAQ,oBACxB,WAAW,CAAC,CACZ,OAAQ,GAAMC,EAAE,KAAK,SAAUhB,OAAY,aAAa,CAAC,CACzD,MAAM,EAAG,KAAOiB,EAAE,KAAK,SAAS,MAAQ,IAAMC,EAAE,KAAK,SAAS,MAAQ,EAAE,EACrEC,EAAaJ,EAAQ,QAAQ,KAAK,QAAQ,WAChD,GAAI,CAACI,EAAY,OAMjB,IAAMC,EADON,EAJCA,EAAW,UACtB,IAAOE,EAAE,KAAK,SAAS,MAAQ,IAAMG,EAAW,KAAO,IACrDH,EAAE,KAAK,SAAS,OAAS,IAAMG,EAAW,KAAO,CAE9B,GAASN,EAAM,OAAS,aAAe,EAAI,IACtD,EAAM,KAAK,QAGxB,OAFKO,GACLP,EAAM,eAAe,EACd,CAAE,EAAGO,EAAK,KAAOA,EAAK,MAAQ,EAAID,EAAW,MAAQ,EAAG,EAAGE,EAAmB,CAAE,GAF5E,MAGb,CACA,OAAA,EAAO3D,EAAAA,4BAAAA,CAA4BmD,EAAOD,CAAI,CAChD,EC2CMU,EAA+C,GAAS,CAC5D,IAAMC,EAAWX,EAAK,OAAO,KAAK,SAAUZ,OAAY,SAClDc,EAAaF,EAAK,oBAAoB,OAAQ,GAAc,CAChE,IAAMY,EAAOC,EAAU,KAAK,SAAUzB,KACtC,OAAOuB,EAAWC,IAAS,SAAWA,IAAS,QACjD,CAAC,EACKE,EAAS,CAAE,GAAGd,EAAM,oBAAqBE,CAAW,EAC1D,GAAIS,EAAU,OAAA,EAAO5D,EAAAA,eAAAA,CAAe+D,CAAM,EAC1C,IAAMC,GAAA,EAAS/D,EAAAA,cAAAA,CAAc8D,CAAM,EACnC,GAAIC,EAAO,OAAS,EAAG,CACrB,IAAMC,EAAQ,GACZd,EAAW,KAAM,GAAME,EAAE,KAAOa,CAAE,CAAC,EAAE,KAAK,SAAU7B,OAAY,OAAS,EAAI,EAC/E,MAAO,CAAC,GAAG2B,CAAM,CAAC,CAAC,MAAM,EAAG,IAAMC,EAAKX,EAAE,EAAE,EAAIW,EAAKV,EAAE,EAAE,CAAC,CAC3D,CACA,OAAA,EAAOvD,EAAAA,eAAAA,CAAe+D,CAAM,CAC9B,EAEMI,GAAA,EAAejE,EAAAA,cAAAA,CAAiC,CACpD,SAAU,KACV,WAAY,KACZ,aAAc,IAChB,CAAC,EAoBYkE,GAAA,EAAQjE,EAAAA,WAAAA,CAAuC,SAC1D,CACE,YACA,SACA,eACA,oBACA,0BACA,YACA,WACA,GAAGkE,GAEL,EACA,CACA,IAAMC,GAAA,EAAQlE,EAAAA,MAAAA,CAAM,EACd,CAACmE,EAAUC,IAAA,EAAenE,EAAAA,SAAAA,CAAwB,IAAI,EACtD,CAACoE,EAAYC,IAAA,EAAiBrE,EAAAA,SAAAA,CAAmC,IAAI,EACrE,CAACsE,EAAcC,IAAA,EAAmBvE,EAAAA,SAAAA,CAAwB,IAAI,EAC9DwE,GAAA,EAAUvE,EAAAA,WAAAA,EAAAA,EACdC,EAAAA,UAAAA,CAAUC,EAAAA,cAAe,CAAE,qBAAsB,CAAE,SAAU,CAAE,CAAE,CAAC,GAAA,EAClED,EAAAA,UAAAA,CAAUE,EAAAA,eAAgB,CAAE,iBAAkBuC,CAAsB,CAAC,CACvE,EAEM8B,GAAA,EAAcpE,EAAAA,YAAAA,CAAa,GAA0B,CACzD8D,EAAY,OAAOtB,EAAM,OAAO,EAAE,CAAC,EACnCwB,EAAcxB,EAAM,OAAO,KAAK,SAAUb,OAAY,SAAW,SAAW,MAAM,CACpF,EAAG,CAAC,CAAC,EAIC0C,GAAA,EAAarE,EAAAA,YAAAA,CAAa,GAAyB,CACvD,IAAMwB,EAAOgB,EAAM,KACnB,GAAI,CAAChB,EAAM,CACT0C,EAAgB,IAAI,EACpB,MACF,CACA,IAAMI,EAAW9C,EAAK,KAAK,SAAW,CAAC,EACjCE,EACJ4C,EAAS3C,OAAY,SAAW,OAAOH,EAAK,EAAE,EAAI,OAAO8C,EAAS1C,UAAe,EAAE,EACrFsC,EAAgBxC,GAAY,IAAI,CAClC,EAAG,CAAC,CAAC,EAEC6C,GAAA,EAAQvE,EAAAA,YAAAA,KAAkB,CAC9B8D,EAAY,IAAI,EAChBE,EAAc,IAAI,EAClBE,EAAgB,IAAI,CACtB,EAAG,CAAC,CAAC,EAECM,GAAA,EAAYxE,EAAAA,YAAAA,CACf,GAAwB,CACvB,GAAM,CAAE,SAAQ,QAASwC,EAEzB,GADA+B,EAAM,EACD/C,EAGL,IAAIO,EAAO,KAAK,SAAUJ,OAAY,SAAU,CAC9C,IAAM8C,EAAOlD,EAAWiB,EAAOV,GAAa,CAAC,CAAC,EAC1C2C,GAAMC,IAAeD,CAAI,CAC/B,KAAO,CACL,IAAMA,EAAOzC,EAASQ,CAAK,EACvBiC,GAAME,IAASF,CAAI,CACzB,EACF,EACA,CAAC3C,EAAW4C,EAAcC,EAAQJ,CAAK,CACzC,EAEMK,GAAA,EAAQ3E,EAAAA,QAAAA,MACL,CAAE,WAAU,aAAY,cAAa,GAC5C,CAAC4D,EAAUE,EAAYE,CAAY,CACrC,EAEMY,GACJ,EAAA1D,EAAA,IAAA,CAAC,MAAD,CAAU,MAAK,UAAW,CAAC,WAAY2D,CAAS,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG,EAAG,GAAInB,EAC9E,UACE,CAAA,EAGP,OACE,EAAAxC,EAAA,IAAA,CAACsC,EAAa,SAAd,CAA8B,QAC5B,UAAA,EAAAtC,EAAA,KAAA,CAACjB,EAAAA,WAAD,CACE,GAAI,YAAY0D,IACP,UACT,mBAAoBX,EAGpB,UAAW,CAAE,UAAW,CAAE,SAAU9C,EAAAA,kBAAkB,MAAO,CAAE,EAE/D,YAAaiE,EACb,WAAYC,EACZ,UAAWG,EACX,aAAcD,EAXhB,SAAA,CAaGzC,GACC,EAAAX,EAAA,IAAA,CAACf,EAAAA,gBAAD,CAAiB,MAAO0B,EAAW,SAAUzB,EAAAA,8BAC1C,SAAAwE,CACc,CAAA,EAEjBA,GAEF,EAAA1D,EAAA,KAAA,CAACb,EAAAA,YAAD,CAAA,SAAA,CACGuD,GAAYE,IAAe,QAC1B,EAAA5C,EAAA,KAAA,CAAC,MAAD,CAAK,UAAU,gBAAgB,eAAA,GAA/B,SAAA,EACE,EAAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAU,wBAAwB,cAAY,OAClD,UAAA,EAAAA,EAAA,IAAA,CAACZ,EAAAA,EAAD,CAAM,KAAK,eAAiB,CAAA,CACxB,CAAA,GACN,EAAAY,EAAA,IAAA,CAAC,MAAD,CAAK,UAAU,yBAA0B,SAAA4D,IAAoBlB,CAAQ,CAAO,CAAA,CACzE,CACH,CAAA,EAAA,KACHA,GAAYE,IAAe,UAC1B,EAAA5C,EAAA,IAAA,CAAC,MAAD,CAAK,UAAU,kBAAkB,eAAA,GAC9B,SAAA6D,IAA0BnB,CAAQ,CAChC,CAAA,EACH,IACO,CAAA,CAAA,CACH,C,EACS,CAAA,CAE3B,CAAC,EAoBYoB,GAAA,EAAcxF,EAAAA,WAAAA,CACzB,SACE,CAAE,KAAI,QAAO,UAAS,QAAO,SAAQ,cAAc,GAAM,YAAW,WAAU,GAAGkE,GACjF,EACA,CACA,GAAM,CAAE,aAAY,iBAAA,EAAiBnD,EAAAA,WAAAA,CAAWiD,CAAY,EACtD,CAAE,WAAYyB,IAAA,EAAezE,EAAAA,aAAAA,CAAa,CAC9C,GAAI,GAAG+C,EAAG,QACV,KAAM,CAAE,KAAM,cAAe,SAAUA,EAAI,MAAO2B,CAAQ,CAC5D,CAAC,EACK,CACJ,aACA,YACA,WAAYC,EACZ,YACA,aACA,eAAA,EACE1E,EAAAA,YAAAA,CAAY,CACd,KACA,SAAU,CAAC2E,EACX,KAAM,CAAE,KAAM,SAAU,SAAU7B,EAAI,MAAO2B,CAAQ,CACvD,CAAC,EAED,OACE,EAAAhE,EAAA,KAAA,CAAC,UAAD,CACE,IAAKR,EAAAA,EAAUyE,EAAcE,CAAG,EAChC,MAAO,CAAE,UAAW1E,EAAAA,IAAI,UAAU,SAAS2E,CAAS,EAAG,YAAW,EAClE,UAAW,CAAC,kBAAmBT,CAAS,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG,EAClE,YAAYf,IAAe,QAAUE,IAAiBT,GAAO,IAAA,GAC7D,gBAAegC,GAAc,IAAA,GAE7B,aAAYC,EACZ,GAAI9B,EARN,SAAA,CAUG0B,IACC,EAAAlE,EAAA,IAAA,CAACN,EAAAA,OAAD,CACE,QAAQ,SACR,KAAK,KACL,UAAU,kBACV,UAAU,wBACV,aAAY,WAAW4E,IACvB,GAAIC,EACJ,GAAIC,CACL,CAAA,GAEH,EAAAxE,EAAA,KAAA,CAAC,SAAD,CAAQ,UAAU,wBAAlB,SAAA,EACE,EAAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAU,yBAA0B,SAAAsE,CAAY,CAAA,GACtD,EAAAtE,EAAA,IAAA,CAAC,OAAD,CAAM,UAAU,yBAA0B,SAAAyE,GAAST,EAAQ,MAAa,CAAA,EACvEU,IAAU,EAAA1E,EAAA,IAAA,CAAC,OAAD,CAAM,UAAU,0BAA2B,SAAA0E,CAAa,CAAA,CAC7D,C,IACR,EAAA1E,EAAA,IAAA,CAAC,MAAD,CAAK,IAAK+D,EAAY,UAAU,wBAC9B,UAAA,EAAA/D,EAAA,IAAA,CAACf,EAAAA,gBAAD,CAAiB,MAAO+E,EAAS,SAAUrE,EAAAA,4BACxC,UACc,CAAA,CACd,CAAA,CACE,C,EAEb,CACF,EAeagF,GAAA,EAAYrG,EAAAA,WAAAA,CAA2C,SAClE,CAAE,KAAI,WAAU,QAAO,YAAW,WAAU,GAAGkE,GAC/C,EACA,CACA,GAAM,CAAE,aAAY,YAAW,aAAY,YAAW,aAAY,eAAA,EAChEjD,EAAAA,YAAAA,CAAY,CAAE,KAAI,KAAM,CAAE,KAAM,OAAQ,UAAS,CAAE,CAAC,EAChD,CAAE,aAAA,EAAaF,EAAAA,WAAAA,CAAWiD,CAAY,EAE5C,OACE,EAAAtC,EAAA,KAAA,CAAC,MAAD,CACE,IAAKR,EAAAA,EAAUoF,EAAYT,CAAG,EAC9B,MAAO,CAAE,UAAW1E,EAAAA,IAAI,UAAU,SAAS2E,CAAS,EAAG,YAAW,EAClE,UAAW,CAAC,gBAAiBT,CAAS,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG,EAChE,gBAAeU,GAAc,IAAA,GAC7B,YAAW3B,GAAYA,IAAaL,EAAK,GAAK,IAAA,GAC9C,GAAIG,EANN,SAAA,EAQE,EAAAxC,EAAA,IAAA,CAACN,EAAAA,OAAD,CACE,QAAQ,SACR,KAAK,KACL,WAAA,GACA,UAAU,gBACV,UAAU,wBACV,aAAYmF,EAAQ,QAAQA,IAAU,YACtC,GAAIN,EACJ,GAAIC,CACL,CAAA,GACD,EAAAxE,EAAA,IAAA,CAAC,MAAD,CAAK,UAAU,yBAA0B,UAAc,CAAA,CACpD,C,EAET,CAAC,E"}
@@ -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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"}