@commercetools/nimbus-mcp 2.11.0 → 3.1.0

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 (194) hide show
  1. package/data/docs/route-manifest.json +1421 -605
  2. package/data/docs/routes/components-data-display-card.json +71 -5
  3. package/data/docs/routes/components-data-display-data-table.json +66 -34
  4. package/data/docs/routes/components-data-display-draggable-list.json +62 -7
  5. package/data/docs/routes/components-feedback-toast.json +1 -1
  6. package/data/docs/routes/components-inputs-checkbox.json +2 -2
  7. package/data/docs/routes/components-layout-defaultpage.json +4 -4
  8. package/data/docs/routes/components-layout-modalpage.json +4 -4
  9. package/data/docs/routes/components-layout-scrollarea.json +3 -3
  10. package/data/docs/routes/components-layout-splitter.json +654 -0
  11. package/data/docs/routes/components-media-avatar.json +24 -2
  12. package/data/docs/routes/components-utilities-region.json +265 -0
  13. package/data/docs/routes/home-getting-started-bundler-plugins.json +248 -0
  14. package/data/docs/routes/hooks-usedraganddrop.json +310 -0
  15. package/data/docs/routes/patterns-actions-form-action-bar.json +412 -0
  16. package/data/docs/routes/patterns-actions.json +78 -0
  17. package/data/docs/routes/patterns-dialogs-confirmation-dialog.json +391 -0
  18. package/data/docs/routes/patterns-dialogs-form-dialog.json +358 -0
  19. package/data/docs/routes/patterns-dialogs-info-dialog.json +315 -0
  20. package/data/docs/routes/patterns-dialogs.json +78 -0
  21. package/data/docs/routes/patterns-pages-public-page-layout.json +371 -0
  22. package/data/docs/routes/patterns-pages.json +78 -0
  23. package/data/docs/search-index.json +1 -1
  24. package/data/docs/types/AccordionContent.json +32 -32
  25. package/data/docs/types/AccordionHeader.json +102 -102
  26. package/data/docs/types/AccordionItem.json +28 -28
  27. package/data/docs/types/AccordionRoot.json +15 -15
  28. package/data/docs/types/AlertDescription.json +8 -8
  29. package/data/docs/types/AlertDismissButton.json +89 -89
  30. package/data/docs/types/AlertTitle.json +8 -8
  31. package/data/docs/types/Avatar.json +8 -8
  32. package/data/docs/types/Badge.json +2 -2
  33. package/data/docs/types/Body.json +6 -6
  34. package/data/docs/types/Box.json +6 -6
  35. package/data/docs/types/Button.json +97 -97
  36. package/data/docs/types/Calendar.json +111 -65
  37. package/data/docs/types/Caption.json +6 -6
  38. package/data/docs/types/Card.json +1 -1
  39. package/data/docs/types/{CardContent.json → CardBody.json} +2 -2
  40. package/data/docs/types/CardFooter.json +27 -0
  41. package/data/docs/types/CardRoot.json +18 -48
  42. package/data/docs/types/Cell.json +20 -20
  43. package/data/docs/types/Checkbox.json +99 -99
  44. package/data/docs/types/Code.json +10 -10
  45. package/data/docs/types/CollapsibleMotionContent.json +2 -2
  46. package/data/docs/types/CollapsibleMotionRoot.json +2 -2
  47. package/data/docs/types/CollapsibleMotionTrigger.json +4 -4
  48. package/data/docs/types/Column.json +8 -8
  49. package/data/docs/types/ColumnGroup.json +8 -8
  50. package/data/docs/types/ColumnHeader.json +18 -18
  51. package/data/docs/types/ComboBoxListBox.json +80 -80
  52. package/data/docs/types/ComboBoxOption.json +77 -77
  53. package/data/docs/types/ComboBoxPopover.json +77 -77
  54. package/data/docs/types/ComboBoxRoot.json +8 -8
  55. package/data/docs/types/ComboBoxSection.json +29 -29
  56. package/data/docs/types/ComboBoxTrigger.json +6 -6
  57. package/data/docs/types/ConfirmationDialog.json +224 -0
  58. package/data/docs/types/Content.json +2 -2
  59. package/data/docs/types/DataTable.json +17 -2
  60. package/data/docs/types/DataTableBody.json +24 -24
  61. package/data/docs/types/DataTableHeader.json +31 -31
  62. package/data/docs/types/DataTableRoot.json +17 -2
  63. package/data/docs/types/DataTableTable.json +35 -20
  64. package/data/docs/types/DateInput.json +84 -84
  65. package/data/docs/types/DatePicker.json +65 -65
  66. package/data/docs/types/DateRangePicker.json +99 -99
  67. package/data/docs/types/DateRangePickerField.json +99 -99
  68. package/data/docs/types/DefaultPageBackLink.json +16 -16
  69. package/data/docs/types/DefaultPageRoot.json +2 -2
  70. package/data/docs/types/DialogCloseTrigger.json +87 -87
  71. package/data/docs/types/DialogTrigger.json +2 -2
  72. package/data/docs/types/DragAndDropItemData.json +9 -0
  73. package/data/docs/types/DragAndDropProps.json +9 -0
  74. package/data/docs/types/DraggableListField.json +159 -70
  75. package/data/docs/types/DraggableListItem.json +63 -63
  76. package/data/docs/types/DraggableListRoot.json +159 -70
  77. package/data/docs/types/DrawerCloseTrigger.json +87 -87
  78. package/data/docs/types/DrawerTrigger.json +2 -2
  79. package/data/docs/types/FieldErrors.json +2 -2
  80. package/data/docs/types/Flex.json +22 -22
  81. package/data/docs/types/Footer.json +6 -6
  82. package/data/docs/types/FormActionBar.json +200 -0
  83. package/data/docs/types/FormDialog.json +198 -0
  84. package/data/docs/types/FormFieldRoot.json +2 -2
  85. package/data/docs/types/Grid.json +24 -24
  86. package/data/docs/types/Group.json +12 -12
  87. package/data/docs/types/Header.json +6 -6
  88. package/data/docs/types/Heading.json +8 -8
  89. package/data/docs/types/Icon.json +4 -4
  90. package/data/docs/types/IconButton.json +97 -97
  91. package/data/docs/types/IconToggleButton.json +84 -84
  92. package/data/docs/types/Image.json +30 -30
  93. package/data/docs/types/Indicator.json +6 -6
  94. package/data/docs/types/InfoDialog.json +104 -0
  95. package/data/docs/types/InlineSvg.json +2 -2
  96. package/data/docs/types/Item.json +6 -6
  97. package/data/docs/types/Kbd.json +8 -8
  98. package/data/docs/types/Link.json +31 -31
  99. package/data/docs/types/ListIndicator.json +6 -6
  100. package/data/docs/types/ListItem.json +6 -6
  101. package/data/docs/types/ListRoot.json +10 -10
  102. package/data/docs/types/LoadingSpinner.json +2 -2
  103. package/data/docs/types/MakeElementFocusable.json +19 -19
  104. package/data/docs/types/MenuItem.json +75 -75
  105. package/data/docs/types/MenuRoot.json +63 -63
  106. package/data/docs/types/MenuSection.json +35 -54
  107. package/data/docs/types/MenuSubmenuTrigger.json +5 -5
  108. package/data/docs/types/MenuTrigger.json +102 -102
  109. package/data/docs/types/MultilineTextInput.json +134 -134
  110. package/data/docs/types/MultilineTextInputField.json +131 -131
  111. package/data/docs/types/NumberInput.json +100 -100
  112. package/data/docs/types/NumberInputField.json +95 -95
  113. package/data/docs/types/PageContentColumn.json +6 -6
  114. package/data/docs/types/PageContentRoot.json +6 -6
  115. package/data/docs/types/PasswordInput.json +129 -129
  116. package/data/docs/types/PasswordInputField.json +129 -129
  117. package/data/docs/types/ProgressBar.json +14 -14
  118. package/data/docs/types/PublicPageLayout.json +99 -0
  119. package/data/docs/types/RadioInputOption.json +64 -64
  120. package/data/docs/types/RadioInputRoot.json +55 -55
  121. package/data/docs/types/RangeCalendar.json +90 -71
  122. package/data/docs/types/Region.json +114 -0
  123. package/data/docs/types/RegionProvider.json +25 -0
  124. package/data/docs/types/RegionTarget.json +112 -0
  125. package/data/docs/types/RichTextInput.json +2 -2
  126. package/data/docs/types/Root.json +10 -10
  127. package/data/docs/types/Row.json +6 -6
  128. package/data/docs/types/SPLITTER_SIZE_TOKENS.json +9 -0
  129. package/data/docs/types/ScrollArea.json +6 -6
  130. package/data/docs/types/SearchInput.json +136 -136
  131. package/data/docs/types/SearchInputField.json +131 -131
  132. package/data/docs/types/SelectOption.json +66 -66
  133. package/data/docs/types/SelectOptionGroup.json +22 -22
  134. package/data/docs/types/SelectOptions.json +74 -74
  135. package/data/docs/types/SelectRoot.json +102 -102
  136. package/data/docs/types/Separator.json +4 -4
  137. package/data/docs/types/SimpleGrid.json +28 -28
  138. package/data/docs/types/SplitButton.json +12 -12
  139. package/data/docs/types/Splitter.json +12 -0
  140. package/data/docs/types/SplitterAside.json +27 -0
  141. package/data/docs/types/SplitterHandle.json +27 -0
  142. package/data/docs/types/SplitterMain.json +27 -0
  143. package/data/docs/types/SplitterRoot.json +271 -0
  144. package/data/docs/types/SplitterSizeToken.json +9 -0
  145. package/data/docs/types/Stack.json +2 -2
  146. package/data/docs/types/StepsNextTrigger.json +2 -2
  147. package/data/docs/types/StepsPrevTrigger.json +2 -2
  148. package/data/docs/types/StepsRoot.json +2 -2
  149. package/data/docs/types/StepsTrigger.json +2 -2
  150. package/data/docs/types/Switch.json +38 -38
  151. package/data/docs/types/TabNavItem.json +18 -18
  152. package/data/docs/types/TabNavRoot.json +2 -2
  153. package/data/docs/types/TableBody.json +6 -6
  154. package/data/docs/types/TableCaption.json +6 -6
  155. package/data/docs/types/TableCell.json +20 -20
  156. package/data/docs/types/TableColumn.json +8 -8
  157. package/data/docs/types/TableColumnGroup.json +8 -8
  158. package/data/docs/types/TableColumnHeader.json +18 -18
  159. package/data/docs/types/TableFooter.json +6 -6
  160. package/data/docs/types/TableHeader.json +6 -6
  161. package/data/docs/types/TableRoot.json +32 -32
  162. package/data/docs/types/TableRow.json +6 -6
  163. package/data/docs/types/TableScrollArea.json +6 -6
  164. package/data/docs/types/TabsTab.json +2 -2
  165. package/data/docs/types/TagGroupRoot.json +27 -27
  166. package/data/docs/types/TagGroupTag.json +68 -68
  167. package/data/docs/types/TagGroupTagList.json +18 -18
  168. package/data/docs/types/Text.json +8 -8
  169. package/data/docs/types/TextInput.json +132 -132
  170. package/data/docs/types/TextInputField.json +129 -129
  171. package/data/docs/types/TimeInput.json +78 -78
  172. package/data/docs/types/ToggleButton.json +86 -86
  173. package/data/docs/types/ToggleButtonGroupButton.json +33 -33
  174. package/data/docs/types/ToggleButtonGroupRoot.json +20 -20
  175. package/data/docs/types/Toolbar.json +12 -12
  176. package/data/docs/types/TooltipContent.json +31 -31
  177. package/data/docs/types/TooltipRoot.json +18 -18
  178. package/data/docs/types/Trigger.json +4 -4
  179. package/data/docs/types/UseDragAndDropOptions.json +9 -0
  180. package/data/docs/types/VisuallyHidden.json +7 -7
  181. package/data/docs/types/createArrayHandlers.json +12 -0
  182. package/data/docs/types/createItemsFromCsvDrop.json +833 -0
  183. package/data/docs/types/createItemsFromDirectoryDrop.json +833 -0
  184. package/data/docs/types/createItemsFromFileDrop.json +833 -0
  185. package/data/docs/types/createItemsFromImageDrop.json +833 -0
  186. package/data/docs/types/createItemsFromJsonDrop.json +833 -0
  187. package/data/docs/types/createItemsFromTextDrop.json +12 -0
  188. package/data/docs/types/createListDataHandlers.json +102 -0
  189. package/data/docs/types/manifest.json +32 -2
  190. package/data/docs/types/toast.json +0 -15
  191. package/data/docs/types/useDragAndDrop.json +174 -0
  192. package/data/docs/types/useRegion.json +1052 -0
  193. package/data/docs/types/useResponsiveSplitterSizes.json +143 -0
  194. package/package.json +7 -7
@@ -0,0 +1,248 @@
1
+ {
2
+ "meta": {
3
+ "id": "Home-BundlerPlugins-1749667200000",
4
+ "title": "Bundler Plugins",
5
+ "description": "Webpack and Vite plugins for optional Nimbus dependency resolution",
6
+ "order": 7,
7
+ "repoPath": "packages/nimbus/src/docs/home-bundler-plugins.mdx",
8
+ "menu": [
9
+ "Home",
10
+ "Getting Started",
11
+ "Bundler Plugins"
12
+ ],
13
+ "route": "home/getting-started/bundler-plugins",
14
+ "tags": [
15
+ "guide",
16
+ "plugins",
17
+ "webpack",
18
+ "vite",
19
+ "build",
20
+ "optional-dependency"
21
+ ],
22
+ "toc": [
23
+ {
24
+ "value": "When to use",
25
+ "href": "#when-to-use",
26
+ "depth": 2,
27
+ "numbering": [
28
+ 1,
29
+ 1
30
+ ],
31
+ "parent": "root"
32
+ },
33
+ {
34
+ "value": "Entry points",
35
+ "href": "#entry-points",
36
+ "depth": 2,
37
+ "numbering": [
38
+ 1,
39
+ 2
40
+ ],
41
+ "parent": "root"
42
+ },
43
+ {
44
+ "value": "Vite",
45
+ "href": "#vite",
46
+ "depth": 2,
47
+ "numbering": [
48
+ 1,
49
+ 3
50
+ ],
51
+ "parent": "root"
52
+ },
53
+ {
54
+ "value": "Webpack",
55
+ "href": "#webpack",
56
+ "depth": 2,
57
+ "numbering": [
58
+ 1,
59
+ 4
60
+ ],
61
+ "parent": "root"
62
+ },
63
+ {
64
+ "value": "How detection works",
65
+ "href": "#how-detection-works",
66
+ "depth": 2,
67
+ "numbering": [
68
+ 1,
69
+ 5
70
+ ],
71
+ "parent": "root"
72
+ },
73
+ {
74
+ "value": "What gets stubbed",
75
+ "href": "#what-gets-stubbed",
76
+ "depth": 2,
77
+ "numbering": [
78
+ 1,
79
+ 6
80
+ ],
81
+ "parent": "root"
82
+ },
83
+ {
84
+ "value": "Writing safe shared code",
85
+ "href": "#writing-safe-shared-code",
86
+ "depth": 2,
87
+ "numbering": [
88
+ 1,
89
+ 7
90
+ ],
91
+ "parent": "root"
92
+ },
93
+ {
94
+ "value": "Guard with a runtime check",
95
+ "href": "#guard-with-a-runtime-check",
96
+ "depth": 3,
97
+ "numbering": [
98
+ 1,
99
+ 7,
100
+ 1
101
+ ],
102
+ "parent": "root"
103
+ },
104
+ {
105
+ "value": "Guard with lazy imports",
106
+ "href": "#guard-with-lazy-imports",
107
+ "depth": 3,
108
+ "numbering": [
109
+ 1,
110
+ 7,
111
+ 2
112
+ ],
113
+ "parent": "root"
114
+ },
115
+ {
116
+ "value": "Feature flags",
117
+ "href": "#feature-flags",
118
+ "depth": 3,
119
+ "numbering": [
120
+ 1,
121
+ 7,
122
+ 3
123
+ ],
124
+ "parent": "root"
125
+ }
126
+ ],
127
+ "icon": "Extension",
128
+ "layout": "app-frame",
129
+ "tabs": [
130
+ {
131
+ "key": "overview",
132
+ "title": "Overview",
133
+ "order": 0
134
+ }
135
+ ]
136
+ },
137
+ "mdx": "\n# Bundler Plugins\n\nNimbus ships Vite and webpack plugins that let build tools treat\n`@commercetools/nimbus` as an **optional dependency**. When Nimbus is installed,\nthe plugins are no-ops. When it is not installed, they stub every Nimbus import\nso the build succeeds and zero Nimbus code lands in the bundle.\n\n**The plugins solve build errors, not runtime errors.** Stubbed imports resolve\nto `undefined` — any code that renders a Nimbus component or calls a Nimbus\nfunction will fail at runtime unless it is guarded. See\n[Writing safe shared code](#writing-safe-shared-code) for patterns.\n\n## When to use\n\nUse these plugins in **shared build tooling** that produces bundles for\napplications that may or may not depend on Nimbus. Without the plugins, any\n`import … from '@commercetools/nimbus'` in shared code causes a build failure\nfor apps that haven't installed Nimbus.\n\nThe typical scenario: a shared package imports Nimbus components but is consumed\nby both Nimbus-enabled apps and non-Nimbus apps. The plugins let the shared\npackage compile in both environments. The shared code itself is responsible for\nchecking whether Nimbus is available before rendering anything from it.\n\n## Entry points\n\n| Entry point | Export |\n| --------------------------------------- | -------------------------------- |\n| `@commercetools/nimbus/plugins/vite` | `UNSAFE_nimbusOptionalDependency` |\n| `@commercetools/nimbus/plugins/webpack` | `UNSAFE_NimbusOptionalDependencyPlugin` |\n| `@commercetools/nimbus/plugins/stub` | _(empty CJS module)_ |\n\nAll entry points are standalone — they do **not** import the Nimbus runtime.\n\n## Vite\n\n```ts\n// vite.config.ts\nimport { UNSAFE_nimbusOptionalDependency } from \"@commercetools/nimbus/plugins/vite\";\n\nexport default defineConfig({\n plugins: [UNSAFE_nimbusOptionalDependency()],\n});\n```\n\nThe plugin runs with `enforce: \"pre\"` so its `resolveId` hook fires before\nVite's default resolver (required in monorepos where Vite would otherwise follow\nthe workspace symlink). Matching imports are redirected via `resolveId` + `load`\nto a virtual CJS module (`module.exports = {}`). The `.cjs` extension on the\nvirtual ID triggers Rolldown's CJS-to-ESM interop, allowing named imports to\nresolve to `undefined` at runtime instead of failing with `MISSING_EXPORT`. No\nphysical file is written to disk.\n\n## Webpack\n\n```js\n// webpack.config.js\nconst {\n UNSAFE_NimbusOptionalDependencyPlugin,\n} = require(\"@commercetools/nimbus/plugins/webpack\");\n\nmodule.exports = {\n plugins: [new UNSAFE_NimbusOptionalDependencyPlugin()],\n};\n```\n\nThe plugin accesses webpack's built-in `NormalModuleReplacementPlugin` via\n`compiler.webpack` (webpack 5+). It redirects matching imports to the physical\n`@commercetools/nimbus/plugins/stub` entry point (`module.exports = {}`).\nWebpack's CJS-to-ESM interop is natively lenient — unmatched named imports\nresolve to `undefined` at runtime without a build error.\n\n## How detection works\n\nAt build startup, the plugin calls:\n\n```js\nrequire.resolve(\"@commercetools/nimbus\", { paths: [process.cwd()] });\n```\n\n- **Resolves** → Nimbus is installed. The plugin becomes a **no-op**.\n- **Throws** → Nimbus is not installed. The plugin activates and stubs all\n matching imports.\n\n`{ paths: [process.cwd()] }` checks from the **application root**, not the\nplugin's own `node_modules`. This matters in monorepos where the build tool may\nhave Nimbus while the app being built does not.\n\nBoth plugins accept a `{ cwd }` option to override detection, and\n`{ UNSAFE_forceStub: true }` to bypass detection entirely — useful when\ndependency hoisting causes Nimbus to resolve from a parent `node_modules` even\nthough the app never declared it.\n\n## What gets stubbed\n\n| Import | Stubbed? |\n| --------------------------------------------- | -------- |\n| `@commercetools/nimbus` | Yes |\n| `@commercetools/nimbus/components/Button` | Yes |\n| `@commercetools/nimbus/setup-jsdom-polyfills` | Yes |\n| `@commercetools/nimbus/plugins/webpack` | No |\n| `@commercetools/nimbus/plugins/vite` | No |\n| `@commercetools/nimbus/plugins/stub` | No |\n| `@commercetools/nimbus/plugins` | No |\n| `@commercetools/nimbus-icons` | No |\n| `@commercetools/nimbus-tokens` | No |\n\nThe `/plugins` and `/plugins/*` subpaths are excluded to avoid circular\nreplacement.\n\n## Writing safe shared code\n\nWhen stubbing is active, every named import from `@commercetools/nimbus`\n(`Button`, `NimbusProvider`, etc.) is `undefined`. Default and namespace imports\n(`import Nimbus from …` or `import * as Nimbus from …`) resolve to the truthy\n`{}` stub object — guard with individual named imports, not `if (!Nimbus)`.\n\nCode that uses these values without a guard will crash at runtime. The build will not warn you — from the\nbundler's perspective, the import succeeded.\n\n### Guard with a runtime check\n\n```tsx\nimport { Button } from \"@commercetools/nimbus\";\n\nfunction MyAction({ label, onClick }) {\n // Button is undefined when Nimbus is stubbed out\n if (!Button) {\n return <button onClick={onClick}>{label}</button>;\n }\n return <Button onPress={onClick}>{label}</Button>;\n}\n```\n\n### Guard with lazy imports\n\n```tsx\nconst NimbusButton = lazy(() =>\n import(\"@commercetools/nimbus\").then((m) => ({ default: m.Button }))\n);\n\nfunction MyAction({ label, onClick }) {\n return (\n <Suspense fallback={<button onClick={onClick}>{label}</button>}>\n <NimbusButton onPress={onClick}>{label}</NimbusButton>\n </Suspense>\n );\n}\n```\n\n### Feature flags\n\nIf your app already has a feature-flag system, gate the entire Nimbus code path\nbehind a flag rather than checking individual imports:\n\n```tsx\nif (flags.nimbusEnabled) {\n return <NimbusButton onPress={onClick}>{label}</NimbusButton>;\n}\nreturn <button onClick={onClick}>{label}</button>;\n```\n\nThe key principle: **the plugins make the build pass, your code makes the app\nwork.** Any path that touches a Nimbus import must handle the possibility that\nit resolved to `undefined`.\n",
138
+ "views": {
139
+ "overview": {
140
+ "mdx": "\n# Bundler Plugins\n\nNimbus ships Vite and webpack plugins that let build tools treat\n`@commercetools/nimbus` as an **optional dependency**. When Nimbus is installed,\nthe plugins are no-ops. When it is not installed, they stub every Nimbus import\nso the build succeeds and zero Nimbus code lands in the bundle.\n\n**The plugins solve build errors, not runtime errors.** Stubbed imports resolve\nto `undefined` — any code that renders a Nimbus component or calls a Nimbus\nfunction will fail at runtime unless it is guarded. See\n[Writing safe shared code](#writing-safe-shared-code) for patterns.\n\n## When to use\n\nUse these plugins in **shared build tooling** that produces bundles for\napplications that may or may not depend on Nimbus. Without the plugins, any\n`import … from '@commercetools/nimbus'` in shared code causes a build failure\nfor apps that haven't installed Nimbus.\n\nThe typical scenario: a shared package imports Nimbus components but is consumed\nby both Nimbus-enabled apps and non-Nimbus apps. The plugins let the shared\npackage compile in both environments. The shared code itself is responsible for\nchecking whether Nimbus is available before rendering anything from it.\n\n## Entry points\n\n| Entry point | Export |\n| --------------------------------------- | -------------------------------- |\n| `@commercetools/nimbus/plugins/vite` | `UNSAFE_nimbusOptionalDependency` |\n| `@commercetools/nimbus/plugins/webpack` | `UNSAFE_NimbusOptionalDependencyPlugin` |\n| `@commercetools/nimbus/plugins/stub` | _(empty CJS module)_ |\n\nAll entry points are standalone — they do **not** import the Nimbus runtime.\n\n## Vite\n\n```ts\n// vite.config.ts\nimport { UNSAFE_nimbusOptionalDependency } from \"@commercetools/nimbus/plugins/vite\";\n\nexport default defineConfig({\n plugins: [UNSAFE_nimbusOptionalDependency()],\n});\n```\n\nThe plugin runs with `enforce: \"pre\"` so its `resolveId` hook fires before\nVite's default resolver (required in monorepos where Vite would otherwise follow\nthe workspace symlink). Matching imports are redirected via `resolveId` + `load`\nto a virtual CJS module (`module.exports = {}`). The `.cjs` extension on the\nvirtual ID triggers Rolldown's CJS-to-ESM interop, allowing named imports to\nresolve to `undefined` at runtime instead of failing with `MISSING_EXPORT`. No\nphysical file is written to disk.\n\n## Webpack\n\n```js\n// webpack.config.js\nconst {\n UNSAFE_NimbusOptionalDependencyPlugin,\n} = require(\"@commercetools/nimbus/plugins/webpack\");\n\nmodule.exports = {\n plugins: [new UNSAFE_NimbusOptionalDependencyPlugin()],\n};\n```\n\nThe plugin accesses webpack's built-in `NormalModuleReplacementPlugin` via\n`compiler.webpack` (webpack 5+). It redirects matching imports to the physical\n`@commercetools/nimbus/plugins/stub` entry point (`module.exports = {}`).\nWebpack's CJS-to-ESM interop is natively lenient — unmatched named imports\nresolve to `undefined` at runtime without a build error.\n\n## How detection works\n\nAt build startup, the plugin calls:\n\n```js\nrequire.resolve(\"@commercetools/nimbus\", { paths: [process.cwd()] });\n```\n\n- **Resolves** → Nimbus is installed. The plugin becomes a **no-op**.\n- **Throws** → Nimbus is not installed. The plugin activates and stubs all\n matching imports.\n\n`{ paths: [process.cwd()] }` checks from the **application root**, not the\nplugin's own `node_modules`. This matters in monorepos where the build tool may\nhave Nimbus while the app being built does not.\n\nBoth plugins accept a `{ cwd }` option to override detection, and\n`{ UNSAFE_forceStub: true }` to bypass detection entirely — useful when\ndependency hoisting causes Nimbus to resolve from a parent `node_modules` even\nthough the app never declared it.\n\n## What gets stubbed\n\n| Import | Stubbed? |\n| --------------------------------------------- | -------- |\n| `@commercetools/nimbus` | Yes |\n| `@commercetools/nimbus/components/Button` | Yes |\n| `@commercetools/nimbus/setup-jsdom-polyfills` | Yes |\n| `@commercetools/nimbus/plugins/webpack` | No |\n| `@commercetools/nimbus/plugins/vite` | No |\n| `@commercetools/nimbus/plugins/stub` | No |\n| `@commercetools/nimbus/plugins` | No |\n| `@commercetools/nimbus-icons` | No |\n| `@commercetools/nimbus-tokens` | No |\n\nThe `/plugins` and `/plugins/*` subpaths are excluded to avoid circular\nreplacement.\n\n## Writing safe shared code\n\nWhen stubbing is active, every named import from `@commercetools/nimbus`\n(`Button`, `NimbusProvider`, etc.) is `undefined`. Default and namespace imports\n(`import Nimbus from …` or `import * as Nimbus from …`) resolve to the truthy\n`{}` stub object — guard with individual named imports, not `if (!Nimbus)`.\n\nCode that uses these values without a guard will crash at runtime. The build will not warn you — from the\nbundler's perspective, the import succeeded.\n\n### Guard with a runtime check\n\n```tsx\nimport { Button } from \"@commercetools/nimbus\";\n\nfunction MyAction({ label, onClick }) {\n // Button is undefined when Nimbus is stubbed out\n if (!Button) {\n return <button onClick={onClick}>{label}</button>;\n }\n return <Button onPress={onClick}>{label}</Button>;\n}\n```\n\n### Guard with lazy imports\n\n```tsx\nconst NimbusButton = lazy(() =>\n import(\"@commercetools/nimbus\").then((m) => ({ default: m.Button }))\n);\n\nfunction MyAction({ label, onClick }) {\n return (\n <Suspense fallback={<button onClick={onClick}>{label}</button>}>\n <NimbusButton onPress={onClick}>{label}</NimbusButton>\n </Suspense>\n );\n}\n```\n\n### Feature flags\n\nIf your app already has a feature-flag system, gate the entire Nimbus code path\nbehind a flag rather than checking individual imports:\n\n```tsx\nif (flags.nimbusEnabled) {\n return <NimbusButton onPress={onClick}>{label}</NimbusButton>;\n}\nreturn <button onClick={onClick}>{label}</button>;\n```\n\nThe key principle: **the plugins make the build pass, your code makes the app\nwork.** Any path that touches a Nimbus import must handle the possibility that\nit resolved to `undefined`.\n",
141
+ "toc": [
142
+ {
143
+ "value": "When to use",
144
+ "href": "#when-to-use",
145
+ "depth": 2,
146
+ "numbering": [
147
+ 1,
148
+ 1
149
+ ],
150
+ "parent": "root"
151
+ },
152
+ {
153
+ "value": "Entry points",
154
+ "href": "#entry-points",
155
+ "depth": 2,
156
+ "numbering": [
157
+ 1,
158
+ 2
159
+ ],
160
+ "parent": "root"
161
+ },
162
+ {
163
+ "value": "Vite",
164
+ "href": "#vite",
165
+ "depth": 2,
166
+ "numbering": [
167
+ 1,
168
+ 3
169
+ ],
170
+ "parent": "root"
171
+ },
172
+ {
173
+ "value": "Webpack",
174
+ "href": "#webpack",
175
+ "depth": 2,
176
+ "numbering": [
177
+ 1,
178
+ 4
179
+ ],
180
+ "parent": "root"
181
+ },
182
+ {
183
+ "value": "How detection works",
184
+ "href": "#how-detection-works",
185
+ "depth": 2,
186
+ "numbering": [
187
+ 1,
188
+ 5
189
+ ],
190
+ "parent": "root"
191
+ },
192
+ {
193
+ "value": "What gets stubbed",
194
+ "href": "#what-gets-stubbed",
195
+ "depth": 2,
196
+ "numbering": [
197
+ 1,
198
+ 6
199
+ ],
200
+ "parent": "root"
201
+ },
202
+ {
203
+ "value": "Writing safe shared code",
204
+ "href": "#writing-safe-shared-code",
205
+ "depth": 2,
206
+ "numbering": [
207
+ 1,
208
+ 7
209
+ ],
210
+ "parent": "root"
211
+ },
212
+ {
213
+ "value": "Guard with a runtime check",
214
+ "href": "#guard-with-a-runtime-check",
215
+ "depth": 3,
216
+ "numbering": [
217
+ 1,
218
+ 7,
219
+ 1
220
+ ],
221
+ "parent": "root"
222
+ },
223
+ {
224
+ "value": "Guard with lazy imports",
225
+ "href": "#guard-with-lazy-imports",
226
+ "depth": 3,
227
+ "numbering": [
228
+ 1,
229
+ 7,
230
+ 2
231
+ ],
232
+ "parent": "root"
233
+ },
234
+ {
235
+ "value": "Feature flags",
236
+ "href": "#feature-flags",
237
+ "depth": 3,
238
+ "numbering": [
239
+ 1,
240
+ 7,
241
+ 3
242
+ ],
243
+ "parent": "root"
244
+ }
245
+ ]
246
+ }
247
+ }
248
+ }
@@ -0,0 +1,310 @@
1
+ {
2
+ "meta": {
3
+ "id": "useDragAndDrop",
4
+ "title": "useDragAndDrop",
5
+ "description": "shared drag-and-drop hook for Nimbus collection components",
6
+ "order": 999,
7
+ "repoPath": "packages/nimbus/src/hooks/use-drag-and-drop/use-drag-and-drop.mdx",
8
+ "menu": [
9
+ "Hooks",
10
+ "useDragAndDrop"
11
+ ],
12
+ "route": "hooks/usedraganddrop",
13
+ "tags": [
14
+ "drag",
15
+ "drop",
16
+ "reorder",
17
+ "dnd"
18
+ ],
19
+ "toc": [
20
+ {
21
+ "value": "Getting started",
22
+ "href": "#getting-started",
23
+ "depth": 2,
24
+ "numbering": [
25
+ 1,
26
+ 1
27
+ ],
28
+ "parent": "root"
29
+ },
30
+ {
31
+ "value": "Basic reorderable DataTable",
32
+ "href": "#basic-reorderable-datatable",
33
+ "depth": 3,
34
+ "numbering": [
35
+ 1,
36
+ 1,
37
+ 1
38
+ ],
39
+ "parent": "root"
40
+ },
41
+ {
42
+ "value": "Namespace isolation",
43
+ "href": "#namespace-isolation",
44
+ "depth": 2,
45
+ "numbering": [
46
+ 1,
47
+ 2
48
+ ],
49
+ "parent": "root"
50
+ },
51
+ {
52
+ "value": "External drops",
53
+ "href": "#external-drops",
54
+ "depth": 2,
55
+ "numbering": [
56
+ 1,
57
+ 3
58
+ ],
59
+ "parent": "root"
60
+ },
61
+ {
62
+ "value": "Helper functions",
63
+ "href": "#helper-functions",
64
+ "depth": 3,
65
+ "numbering": [
66
+ 1,
67
+ 3,
68
+ 1
69
+ ],
70
+ "parent": "root"
71
+ },
72
+ {
73
+ "value": "Outgoing formats",
74
+ "href": "#outgoing-formats",
75
+ "depth": 2,
76
+ "numbering": [
77
+ 1,
78
+ 4
79
+ ],
80
+ "parent": "root"
81
+ },
82
+ {
83
+ "value": "State handler factories",
84
+ "href": "#state-handler-factories",
85
+ "depth": 2,
86
+ "numbering": [
87
+ 1,
88
+ 5
89
+ ],
90
+ "parent": "root"
91
+ },
92
+ {
93
+ "value": "createListDataHandlers(list)",
94
+ "href": "#createlistdatahandlerslist",
95
+ "depth": 3,
96
+ "numbering": [
97
+ 1,
98
+ 5,
99
+ 1
100
+ ],
101
+ "parent": "root"
102
+ },
103
+ {
104
+ "value": "createArrayHandlers(setItems, getKey?)",
105
+ "href": "#createarrayhandlerssetitems-getkey",
106
+ "depth": 3,
107
+ "numbering": [
108
+ 1,
109
+ 5,
110
+ 2
111
+ ],
112
+ "parent": "root"
113
+ },
114
+ {
115
+ "value": "Using with DataTable",
116
+ "href": "#using-with-datatable",
117
+ "depth": 2,
118
+ "numbering": [
119
+ 1,
120
+ 6
121
+ ],
122
+ "parent": "root"
123
+ },
124
+ {
125
+ "value": "API reference",
126
+ "href": "#api-reference",
127
+ "depth": 2,
128
+ "numbering": [
129
+ 1,
130
+ 7
131
+ ],
132
+ "parent": "root"
133
+ },
134
+ {
135
+ "value": "DragAndDropProps<T>",
136
+ "href": "#draganddroppropst",
137
+ "depth": 3,
138
+ "numbering": [
139
+ 1,
140
+ 7,
141
+ 1
142
+ ],
143
+ "parent": "root"
144
+ },
145
+ {
146
+ "value": "UseDragAndDropOptions<T>",
147
+ "href": "#usedraganddropoptionst",
148
+ "depth": 3,
149
+ "numbering": [
150
+ 1,
151
+ 7,
152
+ 2
153
+ ],
154
+ "parent": "root"
155
+ }
156
+ ],
157
+ "layout": "app-frame",
158
+ "tabs": [
159
+ {
160
+ "key": "overview",
161
+ "title": "Overview",
162
+ "order": 0
163
+ }
164
+ ]
165
+ },
166
+ "mdx": "\n# useDragAndDrop\n\nA shared hook that wraps React Aria's drag-and-drop system with namespace isolation,\nexternal drop support, outgoing format serialization, and state handler factories.\nReturns `{ dragAndDropHooks }` to pass to any supported React Aria collection component.\n\n## Getting started\n\n```tsx\nimport { useDragAndDrop, createArrayHandlers } from \"@commercetools/nimbus\";\n```\n\n### Basic reorderable DataTable\n\n```tsx\nimport { DataTable, useDragAndDrop, createArrayHandlers } from \"@commercetools/nimbus\";\n\nconst [rows, setRows] = useState([\n { id: \"1\", name: \"Alice\", role: \"Admin\" },\n { id: \"2\", name: \"Bob\", role: \"User\" },\n { id: \"3\", name: \"Carol\", role: \"Manager\" },\n]);\n\nconst columns = [\n { id: \"name\", header: \"Name\", accessor: (row) => row.name },\n { id: \"role\", header: \"Role\", accessor: (row) => row.role },\n];\n\nconst { dragAndDropHooks } = useDragAndDrop({\n ...createArrayHandlers(setRows, (row) => row.id),\n});\n\n<DataTable columns={columns} rows={rows} dragAndDropHooks={dragAndDropHooks} />\n```\n\n> [!NOTE]\n> DraggableList uses `useDragAndDrop` internally — you don't need to call it yourself.\n> Use the hook directly when adding drag-and-drop to other components like DataTable.\n\n## Namespace isolation\n\nUse `dragNamespace` to prevent drag-and-drop between unrelated lists on the same page:\n\n```tsx\nconst { dragAndDropHooks: fruitHooks } = useDragAndDrop({\n dragNamespace: \"fruits\",\n ...createArrayHandlers(setFruits),\n});\n\nconst { dragAndDropHooks: veggieHooks } = useDragAndDrop({\n dragNamespace: \"vegetables\",\n ...createArrayHandlers(setVeggies),\n});\n```\n\nLists sharing a namespace can exchange items. Lists with different namespaces (or one\nnamespaced and one not) reject each other's drops. Omitting `dragNamespace` preserves\nthe default behavior where all lists interoperate.\n\n## External drops\n\nAccept drops from outside Nimbus (plain text, files, directories) using `onExternalDrop`\nand `acceptExternalTypes`:\n\n```tsx\nimport { useDragAndDrop, createItemsFromFileDrop } from \"@commercetools/nimbus\";\nimport { DIRECTORY_DRAG_TYPE } from \"react-aria-components\";\n\nconst { dragAndDropHooks } = useDragAndDrop({\n onExternalDrop: createItemsFromFileDrop,\n acceptExternalTypes: [\"image/png\", \"image/jpeg\", \"application/pdf\"],\n ...createArrayHandlers(setItems),\n});\n```\n\n### Helper functions\n\nSix composable helpers for common `onExternalDrop` patterns:\n\n| Helper | Accepts | Returns |\n|--------|---------|---------|\n| `createItemsFromTextDrop(items, type?)` | `TextDropItem` | `{ key, label }` |\n| `createItemsFromFileDrop(items)` | `FileDropItem` | `{ key, label, fileName, fileType, file }` |\n| `createItemsFromDirectoryDrop(items)` | `DirectoryDropItem` | `{ key, label, fileName, fileType, file, directory }` |\n| `createItemsFromJsonDrop(items)` | `TextDropItem` with `application/json` | `{ key, label, data }` |\n| `createItemsFromImageDrop(items)` | `FileDropItem` with `image/*` | `{ key, label, fileName, fileType, file, objectUrl }` |\n| `createItemsFromCsvDrop(items)` | `TextDropItem` with `text/csv` | `{ key, label, row, headers }` |\n\nCompose multiple helpers for mixed drop types:\n\n```tsx\nonExternalDrop={async (items) => [\n ...(await createItemsFromTextDrop(items)),\n ...(await createItemsFromFileDrop(items)),\n ...(await createItemsFromDirectoryDrop(items)),\n]}\n```\n\n> [!NOTE]\n> Avoid combining `createItemsFromFileDrop` with `createItemsFromImageDrop` — file drop\n> processes all files including images, so image files would appear twice. Use one or the other.\n\n> [!WARNING]\n> `createItemsFromImageDrop` creates object URLs via `URL.createObjectURL()`.\n> Consumers must call `URL.revokeObjectURL(item.objectUrl)` when items are removed.\n\n## Outgoing formats\n\nUse `serializeDragItem` to make items droppable into non-Nimbus targets:\n\n```tsx\nconst { dragAndDropHooks } = useDragAndDrop({\n serializeDragItem: (item) => ({\n \"text/plain\": item.label,\n \"text/html\": `<li>${item.label}</li>`,\n }),\n ...createArrayHandlers(setItems),\n});\n```\n\n## State handler factories\n\nTwo factories that return all four state mutation callbacks (`onInsertItems`,\n`onAppendItems`, `onReorder`, `onRemoveItems`):\n\n### `createListDataHandlers(list)`\n\nFor React Aria's `useListData`:\n\n```tsx\nimport { useListData } from \"react-stately\";\n\nconst list = useListData({ initialItems: items, getKey });\nconst { dragAndDropHooks } = useDragAndDrop({\n ...createListDataHandlers(list),\n});\n```\n\n### `createArrayHandlers(setItems, getKey?)`\n\nFor plain `useState` arrays:\n\n```tsx\nconst [items, setItems] = useState(initialItems);\nconst { dragAndDropHooks } = useDragAndDrop({\n ...createArrayHandlers(setItems, (item) => item.key),\n});\n```\n\nIf you need to customize state handling beyond what these factories provide — for example,\npersisting the new list order to an API or combining drag-and-drop with undo/redo — see the\n[React Aria drag and drop documentation](https://react-aria.adobe.com/dnd) for guidance on\nbuilding custom state handlers.\n\n## Using with DataTable\n\nDataTable accepts `dragAndDropHooks` as an optional passthrough prop:\n\n```tsx\nimport { DataTable, useDragAndDrop, createArrayHandlers } from \"@commercetools/nimbus\";\n\nconst [rows, setRows] = useState(initialRows);\nconst { dragAndDropHooks } = useDragAndDrop({\n dragNamespace: \"my-table\",\n ...createArrayHandlers(setRows, (row) => row.id),\n});\n\n<DataTable columns={columns} rows={rows} dragAndDropHooks={dragAndDropHooks} />\n```\n\nThe `dragAndDropHooks` prop is also available on `DataTable.Table` for the compound component API.\n\n## API reference\n\n### `DragAndDropProps<T>`\n\nConsumer-facing props, intended to be intersected into component prop types:\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `dragNamespace` | `string` | — | Isolate DnD to collections sharing the same namespace |\n| `onExternalDrop` | `(items: DropItem[]) => T[] \\| Promise<T[]>` | — | Convert external drops into collection items |\n| `acceptExternalTypes` | `Array<string \\| symbol>` | — | Drag types to accept from external sources |\n| `externalDropOperation` | `\"copy\" \\| \"move\" \\| \"link\"` | `\"copy\"` | Drop operation for external items |\n| `serializeDragItem` | `(item: T) => Record<string, string>` | — | Additional format representations for outgoing drags |\n\n### `UseDragAndDropOptions<T>`\n\nFull hook configuration, extends `DragAndDropProps<T>`:\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `dataFormatBase` | `string` | `\"nimbus-collection-item\"` | Override the base data format string |\n| `onInsertItems` | `(items: T[], target: ItemDropTarget) => void` | — | Insert items before/after a target |\n| `onAppendItems` | `(items: T[]) => void` | — | Append items (drop on root) |\n| `onReorder` | `(keys: Set<Key>, target: ItemDropTarget) => void` | — | Reorder items within the collection |\n| `onRemoveItems` | `(keys: Set<Key>) => void` | — | Remove items after external move |\n",
167
+ "views": {
168
+ "overview": {
169
+ "mdx": "\n# useDragAndDrop\n\nA shared hook that wraps React Aria's drag-and-drop system with namespace isolation,\nexternal drop support, outgoing format serialization, and state handler factories.\nReturns `{ dragAndDropHooks }` to pass to any supported React Aria collection component.\n\n## Getting started\n\n```tsx\nimport { useDragAndDrop, createArrayHandlers } from \"@commercetools/nimbus\";\n```\n\n### Basic reorderable DataTable\n\n```tsx\nimport { DataTable, useDragAndDrop, createArrayHandlers } from \"@commercetools/nimbus\";\n\nconst [rows, setRows] = useState([\n { id: \"1\", name: \"Alice\", role: \"Admin\" },\n { id: \"2\", name: \"Bob\", role: \"User\" },\n { id: \"3\", name: \"Carol\", role: \"Manager\" },\n]);\n\nconst columns = [\n { id: \"name\", header: \"Name\", accessor: (row) => row.name },\n { id: \"role\", header: \"Role\", accessor: (row) => row.role },\n];\n\nconst { dragAndDropHooks } = useDragAndDrop({\n ...createArrayHandlers(setRows, (row) => row.id),\n});\n\n<DataTable columns={columns} rows={rows} dragAndDropHooks={dragAndDropHooks} />\n```\n\n> [!NOTE]\n> DraggableList uses `useDragAndDrop` internally — you don't need to call it yourself.\n> Use the hook directly when adding drag-and-drop to other components like DataTable.\n\n## Namespace isolation\n\nUse `dragNamespace` to prevent drag-and-drop between unrelated lists on the same page:\n\n```tsx\nconst { dragAndDropHooks: fruitHooks } = useDragAndDrop({\n dragNamespace: \"fruits\",\n ...createArrayHandlers(setFruits),\n});\n\nconst { dragAndDropHooks: veggieHooks } = useDragAndDrop({\n dragNamespace: \"vegetables\",\n ...createArrayHandlers(setVeggies),\n});\n```\n\nLists sharing a namespace can exchange items. Lists with different namespaces (or one\nnamespaced and one not) reject each other's drops. Omitting `dragNamespace` preserves\nthe default behavior where all lists interoperate.\n\n## External drops\n\nAccept drops from outside Nimbus (plain text, files, directories) using `onExternalDrop`\nand `acceptExternalTypes`:\n\n```tsx\nimport { useDragAndDrop, createItemsFromFileDrop } from \"@commercetools/nimbus\";\nimport { DIRECTORY_DRAG_TYPE } from \"react-aria-components\";\n\nconst { dragAndDropHooks } = useDragAndDrop({\n onExternalDrop: createItemsFromFileDrop,\n acceptExternalTypes: [\"image/png\", \"image/jpeg\", \"application/pdf\"],\n ...createArrayHandlers(setItems),\n});\n```\n\n### Helper functions\n\nSix composable helpers for common `onExternalDrop` patterns:\n\n| Helper | Accepts | Returns |\n|--------|---------|---------|\n| `createItemsFromTextDrop(items, type?)` | `TextDropItem` | `{ key, label }` |\n| `createItemsFromFileDrop(items)` | `FileDropItem` | `{ key, label, fileName, fileType, file }` |\n| `createItemsFromDirectoryDrop(items)` | `DirectoryDropItem` | `{ key, label, fileName, fileType, file, directory }` |\n| `createItemsFromJsonDrop(items)` | `TextDropItem` with `application/json` | `{ key, label, data }` |\n| `createItemsFromImageDrop(items)` | `FileDropItem` with `image/*` | `{ key, label, fileName, fileType, file, objectUrl }` |\n| `createItemsFromCsvDrop(items)` | `TextDropItem` with `text/csv` | `{ key, label, row, headers }` |\n\nCompose multiple helpers for mixed drop types:\n\n```tsx\nonExternalDrop={async (items) => [\n ...(await createItemsFromTextDrop(items)),\n ...(await createItemsFromFileDrop(items)),\n ...(await createItemsFromDirectoryDrop(items)),\n]}\n```\n\n> [!NOTE]\n> Avoid combining `createItemsFromFileDrop` with `createItemsFromImageDrop` — file drop\n> processes all files including images, so image files would appear twice. Use one or the other.\n\n> [!WARNING]\n> `createItemsFromImageDrop` creates object URLs via `URL.createObjectURL()`.\n> Consumers must call `URL.revokeObjectURL(item.objectUrl)` when items are removed.\n\n## Outgoing formats\n\nUse `serializeDragItem` to make items droppable into non-Nimbus targets:\n\n```tsx\nconst { dragAndDropHooks } = useDragAndDrop({\n serializeDragItem: (item) => ({\n \"text/plain\": item.label,\n \"text/html\": `<li>${item.label}</li>`,\n }),\n ...createArrayHandlers(setItems),\n});\n```\n\n## State handler factories\n\nTwo factories that return all four state mutation callbacks (`onInsertItems`,\n`onAppendItems`, `onReorder`, `onRemoveItems`):\n\n### `createListDataHandlers(list)`\n\nFor React Aria's `useListData`:\n\n```tsx\nimport { useListData } from \"react-stately\";\n\nconst list = useListData({ initialItems: items, getKey });\nconst { dragAndDropHooks } = useDragAndDrop({\n ...createListDataHandlers(list),\n});\n```\n\n### `createArrayHandlers(setItems, getKey?)`\n\nFor plain `useState` arrays:\n\n```tsx\nconst [items, setItems] = useState(initialItems);\nconst { dragAndDropHooks } = useDragAndDrop({\n ...createArrayHandlers(setItems, (item) => item.key),\n});\n```\n\nIf you need to customize state handling beyond what these factories provide — for example,\npersisting the new list order to an API or combining drag-and-drop with undo/redo — see the\n[React Aria drag and drop documentation](https://react-aria.adobe.com/dnd) for guidance on\nbuilding custom state handlers.\n\n## Using with DataTable\n\nDataTable accepts `dragAndDropHooks` as an optional passthrough prop:\n\n```tsx\nimport { DataTable, useDragAndDrop, createArrayHandlers } from \"@commercetools/nimbus\";\n\nconst [rows, setRows] = useState(initialRows);\nconst { dragAndDropHooks } = useDragAndDrop({\n dragNamespace: \"my-table\",\n ...createArrayHandlers(setRows, (row) => row.id),\n});\n\n<DataTable columns={columns} rows={rows} dragAndDropHooks={dragAndDropHooks} />\n```\n\nThe `dragAndDropHooks` prop is also available on `DataTable.Table` for the compound component API.\n\n## API reference\n\n### `DragAndDropProps<T>`\n\nConsumer-facing props, intended to be intersected into component prop types:\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `dragNamespace` | `string` | — | Isolate DnD to collections sharing the same namespace |\n| `onExternalDrop` | `(items: DropItem[]) => T[] \\| Promise<T[]>` | — | Convert external drops into collection items |\n| `acceptExternalTypes` | `Array<string \\| symbol>` | — | Drag types to accept from external sources |\n| `externalDropOperation` | `\"copy\" \\| \"move\" \\| \"link\"` | `\"copy\"` | Drop operation for external items |\n| `serializeDragItem` | `(item: T) => Record<string, string>` | — | Additional format representations for outgoing drags |\n\n### `UseDragAndDropOptions<T>`\n\nFull hook configuration, extends `DragAndDropProps<T>`:\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `dataFormatBase` | `string` | `\"nimbus-collection-item\"` | Override the base data format string |\n| `onInsertItems` | `(items: T[], target: ItemDropTarget) => void` | — | Insert items before/after a target |\n| `onAppendItems` | `(items: T[]) => void` | — | Append items (drop on root) |\n| `onReorder` | `(keys: Set<Key>, target: ItemDropTarget) => void` | — | Reorder items within the collection |\n| `onRemoveItems` | `(keys: Set<Key>) => void` | — | Remove items after external move |\n",
170
+ "toc": [
171
+ {
172
+ "value": "Getting started",
173
+ "href": "#getting-started",
174
+ "depth": 2,
175
+ "numbering": [
176
+ 1,
177
+ 1
178
+ ],
179
+ "parent": "root"
180
+ },
181
+ {
182
+ "value": "Basic reorderable DataTable",
183
+ "href": "#basic-reorderable-datatable",
184
+ "depth": 3,
185
+ "numbering": [
186
+ 1,
187
+ 1,
188
+ 1
189
+ ],
190
+ "parent": "root"
191
+ },
192
+ {
193
+ "value": "Namespace isolation",
194
+ "href": "#namespace-isolation",
195
+ "depth": 2,
196
+ "numbering": [
197
+ 1,
198
+ 2
199
+ ],
200
+ "parent": "root"
201
+ },
202
+ {
203
+ "value": "External drops",
204
+ "href": "#external-drops",
205
+ "depth": 2,
206
+ "numbering": [
207
+ 1,
208
+ 3
209
+ ],
210
+ "parent": "root"
211
+ },
212
+ {
213
+ "value": "Helper functions",
214
+ "href": "#helper-functions",
215
+ "depth": 3,
216
+ "numbering": [
217
+ 1,
218
+ 3,
219
+ 1
220
+ ],
221
+ "parent": "root"
222
+ },
223
+ {
224
+ "value": "Outgoing formats",
225
+ "href": "#outgoing-formats",
226
+ "depth": 2,
227
+ "numbering": [
228
+ 1,
229
+ 4
230
+ ],
231
+ "parent": "root"
232
+ },
233
+ {
234
+ "value": "State handler factories",
235
+ "href": "#state-handler-factories",
236
+ "depth": 2,
237
+ "numbering": [
238
+ 1,
239
+ 5
240
+ ],
241
+ "parent": "root"
242
+ },
243
+ {
244
+ "value": "createListDataHandlers(list)",
245
+ "href": "#createlistdatahandlerslist",
246
+ "depth": 3,
247
+ "numbering": [
248
+ 1,
249
+ 5,
250
+ 1
251
+ ],
252
+ "parent": "root"
253
+ },
254
+ {
255
+ "value": "createArrayHandlers(setItems, getKey?)",
256
+ "href": "#createarrayhandlerssetitems-getkey",
257
+ "depth": 3,
258
+ "numbering": [
259
+ 1,
260
+ 5,
261
+ 2
262
+ ],
263
+ "parent": "root"
264
+ },
265
+ {
266
+ "value": "Using with DataTable",
267
+ "href": "#using-with-datatable",
268
+ "depth": 2,
269
+ "numbering": [
270
+ 1,
271
+ 6
272
+ ],
273
+ "parent": "root"
274
+ },
275
+ {
276
+ "value": "API reference",
277
+ "href": "#api-reference",
278
+ "depth": 2,
279
+ "numbering": [
280
+ 1,
281
+ 7
282
+ ],
283
+ "parent": "root"
284
+ },
285
+ {
286
+ "value": "DragAndDropProps<T>",
287
+ "href": "#draganddroppropst",
288
+ "depth": 3,
289
+ "numbering": [
290
+ 1,
291
+ 7,
292
+ 1
293
+ ],
294
+ "parent": "root"
295
+ },
296
+ {
297
+ "value": "UseDragAndDropOptions<T>",
298
+ "href": "#usedraganddropoptionst",
299
+ "depth": 3,
300
+ "numbering": [
301
+ 1,
302
+ 7,
303
+ 2
304
+ ],
305
+ "parent": "root"
306
+ }
307
+ ]
308
+ }
309
+ }
310
+ }