Repository: atlassian/react-beautiful-dnd Branch: master Commit: 2baf5c6344cb Files: 780 Total size: 2.6 MB Directory structure: gitextract__olebuq9/ ├── .browserlistrc ├── .circleci/ │ └── config.yml ├── .eslintignore ├── .eslintrc.js ├── .flowconfig ├── .github/ │ └── ISSUE_TEMPLATE/ │ ├── bug-report.md │ ├── feature-request.md │ └── tree-issue.md ├── .gitignore ├── .nvmrc ├── .prettierignore ├── .prettierrc ├── .size-snapshot.json ├── .storybook/ │ ├── .babelrc │ ├── babel-setup.md │ ├── config.js │ ├── custom-decorators/ │ │ └── global-styles.jsx │ ├── main.js │ └── preview-head.html ├── .stylelintrc.json ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── a11y-audit-parse.js ├── babel.config.js ├── browser-test-harness.js ├── csp-server/ │ ├── .eslintrc.js │ ├── app.jsx │ ├── client.js │ ├── main.js │ ├── server.js │ ├── start.sh │ └── webpack.config.js ├── cypress/ │ ├── .eslintrc.js │ ├── fixtures/ │ │ └── .gitkeep │ ├── integration/ │ │ ├── content-security-policy.spec.js │ │ ├── focus.spec.js │ │ ├── move-between-lists.spec.js │ │ ├── reorder-lists.spec.js │ │ ├── reorder-virtual.spec.js │ │ ├── reorder.spec.js │ │ └── util.js │ ├── plugins/ │ │ └── index.js │ └── support/ │ ├── commands.js │ └── index.js ├── cypress.json ├── docs/ │ ├── about/ │ │ ├── accessibility.md │ │ ├── animations.md │ │ ├── browser-support.md │ │ ├── design-principles.md │ │ ├── examples.md │ │ └── installation.md │ ├── api/ │ │ ├── drag-drop-context.md │ │ ├── draggable.md │ │ ├── droppable.md │ │ └── reset-server-context.md │ ├── guides/ │ │ ├── auto-scrolling.md │ │ ├── avoiding-image-flickering.md │ │ ├── browser-focus.md │ │ ├── changes-while-dragging.md │ │ ├── combining.md │ │ ├── common-setup-issues.md │ │ ├── content-security-policy.md │ │ ├── doctype.md │ │ ├── dragging-svgs.md │ │ ├── drop-animation.md │ │ ├── how-we-detect-scroll-containers.md │ │ ├── how-we-use-dom-events.md │ │ ├── identifiers.md │ │ ├── preset-styles.md │ │ ├── reparenting.md │ │ ├── responders.md │ │ ├── screen-reader.md │ │ ├── setup-problem-detection-and-error-recovery.md │ │ ├── types.md │ │ └── using-inner-ref.md │ ├── patterns/ │ │ ├── multi-drag.md │ │ ├── tables.md │ │ └── virtual-lists.md │ ├── sensors/ │ │ ├── keyboard.md │ │ ├── mouse.md │ │ ├── sensor-api.md │ │ └── touch.md │ └── support/ │ ├── community-and-addons.md │ ├── engineering-health.md │ ├── media.md │ └── upgrading.md ├── flow-typed/ │ ├── custom/ │ │ ├── cypress.js │ │ └── raf.js │ └── npm/ │ ├── @atlaskit/ │ │ ├── css-reset_vx.x.x.js │ │ └── theme_vx.x.x.js │ ├── @babel/ │ │ ├── core_vx.x.x.js │ │ ├── plugin-proposal-class-properties_vx.x.x.js │ │ ├── plugin-transform-modules-commonjs_vx.x.x.js │ │ ├── plugin-transform-object-assign_vx.x.x.js │ │ ├── plugin-transform-runtime_vx.x.x.js │ │ ├── preset-env_vx.x.x.js │ │ ├── preset-flow_vx.x.x.js │ │ ├── preset-react_vx.x.x.js │ │ └── runtime_vx.x.x.js │ ├── @emotion/ │ │ └── babel-preset-css-prop_vx.x.x.js │ ├── @storybook/ │ │ ├── react_v5.x.x.js │ │ └── theming_vx.x.x.js │ ├── @testing-library/ │ │ └── react_v9.x.x.js │ ├── babel-core_vx.x.x.js │ ├── babel-eslint_vx.x.x.js │ ├── babel-jest_vx.x.x.js │ ├── babel-loader_vx.x.x.js │ ├── babel-plugin-dev-expression_vx.x.x.js │ ├── cross-env_vx.x.x.js │ ├── cypress_vx.x.x.js │ ├── enzyme-adapter-react-16_vx.x.x.js │ ├── enzyme_v3.x.x.js │ ├── eslint-config-airbnb_vx.x.x.js │ ├── eslint-config-prettier_vx.x.x.js │ ├── eslint-plugin-cypress_vx.x.x.js │ ├── eslint-plugin-es5_vx.x.x.js │ ├── eslint-plugin-flowtype_vx.x.x.js │ ├── eslint-plugin-import_vx.x.x.js │ ├── eslint-plugin-jest_vx.x.x.js │ ├── eslint-plugin-jsx-a11y_vx.x.x.js │ ├── eslint-plugin-prettier_vx.x.x.js │ ├── eslint-plugin-react-hooks_vx.x.x.js │ ├── eslint-plugin-react_vx.x.x.js │ ├── eslint_vx.x.x.js │ ├── flow-bin_v0.x.x.js │ ├── fs-extra_vx.x.x.js │ ├── globby_vx.x.x.js │ ├── jest-axe_vx.x.x.js │ ├── jest-junit_vx.x.x.js │ ├── jest-watch-typeahead_vx.x.x.js │ ├── jest_vx.x.x.js │ ├── lighthouse_vx.x.x.js │ ├── markdown-it_vx.x.x.js │ ├── prettier_v1.x.x.js │ ├── react-redux_v7.x.x.js │ ├── react-test-renderer_v16.x.x.js │ ├── redux_v4.x.x.js │ ├── require-from-string_vx.x.x.js │ ├── rimraf_vx.x.x.js │ ├── rollup-plugin-babel_vx.x.x.js │ ├── rollup-plugin-commonjs_vx.x.x.js │ ├── rollup-plugin-json_vx.x.x.js │ ├── rollup-plugin-node-resolve_vx.x.x.js │ ├── rollup-plugin-replace_vx.x.x.js │ ├── rollup-plugin-size-snapshot_vx.x.x.js │ ├── rollup-plugin-strip_vx.x.x.js │ ├── rollup-plugin-terser_vx.x.x.js │ ├── rollup_vx.x.x.js │ ├── styled-components_vx.x.x.js │ ├── stylelint-config-prettier_vx.x.x.js │ ├── stylelint-config-recommended_vx.x.x.js │ ├── stylelint-config-standard_vx.x.x.js │ ├── stylelint-config-styled-components_vx.x.x.js │ ├── stylelint-processor-styled-components_vx.x.x.js │ ├── stylelint_vx.x.x.js │ ├── wait-port_vx.x.x.js │ └── webpack_v4.x.x.js ├── jest.config.js ├── package.json ├── renovate.json ├── rollup.config.js ├── server-ports.js ├── src/ │ ├── animation.js │ ├── debug/ │ │ ├── middleware/ │ │ │ ├── action-timing-average.js │ │ │ ├── action-timing.js │ │ │ ├── log.js │ │ │ └── user-timing.js │ │ └── timings.js │ ├── dev-warning.js │ ├── empty.js │ ├── index.js │ ├── invariant.js │ ├── native-with-fallback.js │ ├── screen-reader-message-preset.js │ ├── state/ │ │ ├── action-creators.js │ │ ├── auto-scroller/ │ │ │ ├── auto-scroller-types.js │ │ │ ├── can-scroll.js │ │ │ ├── fluid-scroller/ │ │ │ │ ├── config.js │ │ │ │ ├── did-start-in-scrollable-area.js │ │ │ │ ├── get-best-scrollable-droppable.js │ │ │ │ ├── get-droppable-scroll-change.js │ │ │ │ ├── get-percentage.js │ │ │ │ ├── get-scroll/ │ │ │ │ │ ├── adjust-for-size-limits.js │ │ │ │ │ ├── get-scroll-on-axis/ │ │ │ │ │ │ ├── dampen-value-by-time.js │ │ │ │ │ │ ├── get-distance-thresholds.js │ │ │ │ │ │ ├── get-value-from-distance.js │ │ │ │ │ │ ├── get-value.js │ │ │ │ │ │ ├── index.js │ │ │ │ │ │ └── min-scroll.js │ │ │ │ │ └── index.js │ │ │ │ ├── get-window-scroll-change.js │ │ │ │ ├── index.js │ │ │ │ └── scroll.js │ │ │ ├── index.js │ │ │ └── jump-scroller.js │ │ ├── axis.js │ │ ├── calculate-drag-impact/ │ │ │ └── calculate-reorder-impact.js │ │ ├── can-start-drag.js │ │ ├── create-store.js │ │ ├── did-start-after-critical.js │ │ ├── dimension-marshal/ │ │ │ ├── dimension-marshal-types.js │ │ │ ├── dimension-marshal.js │ │ │ ├── get-initial-publish.js │ │ │ └── while-dragging-publisher.js │ │ ├── dimension-structures.js │ │ ├── droppable/ │ │ │ ├── get-droppable.js │ │ │ ├── is-home-of.js │ │ │ ├── scroll-droppable.js │ │ │ ├── should-use-placeholder.js │ │ │ ├── util/ │ │ │ │ ├── clip.js │ │ │ │ └── get-subject.js │ │ │ ├── what-is-dragged-over-from-result.js │ │ │ ├── what-is-dragged-over.js │ │ │ └── with-placeholder.js │ │ ├── get-center-from-impact/ │ │ │ ├── get-client-border-box-center/ │ │ │ │ ├── get-client-from-page-border-box-center.js │ │ │ │ └── index.js │ │ │ ├── get-page-border-box-center/ │ │ │ │ ├── index.js │ │ │ │ ├── when-combining.js │ │ │ │ └── when-reordering.js │ │ │ └── move-relative-to.js │ │ ├── get-combined-item-displacement.js │ │ ├── get-displaced-by.js │ │ ├── get-displacement-groups.js │ │ ├── get-drag-impact/ │ │ │ ├── get-combine-impact.js │ │ │ ├── get-reorder-impact.js │ │ │ └── index.js │ │ ├── get-draggables-inside-droppable.js │ │ ├── get-droppable-over.js │ │ ├── get-frame.js │ │ ├── get-home-location.js │ │ ├── get-impact-location.js │ │ ├── get-is-displaced.js │ │ ├── get-lift-effect.js │ │ ├── get-max-scroll.js │ │ ├── is-movement-allowed.js │ │ ├── is-within.js │ │ ├── middleware/ │ │ │ ├── auto-scroll.js │ │ │ ├── dimension-marshal-stopper.js │ │ │ ├── drop/ │ │ │ │ ├── drop-animation-finish-middleware.js │ │ │ │ ├── drop-animation-flush-on-scroll-middleware.js │ │ │ │ ├── drop-middleware.js │ │ │ │ ├── get-drop-duration.js │ │ │ │ ├── get-drop-impact.js │ │ │ │ ├── get-new-home-client-offset.js │ │ │ │ └── index.js │ │ │ ├── focus.js │ │ │ ├── lift.js │ │ │ ├── pending-drop.js │ │ │ ├── responders/ │ │ │ │ ├── async-marshal.js │ │ │ │ ├── expiring-announce.js │ │ │ │ ├── index.js │ │ │ │ ├── is-equal.js │ │ │ │ ├── publisher.js │ │ │ │ └── responders-middleware.js │ │ │ ├── scroll-listener.js │ │ │ ├── style.js │ │ │ └── util/ │ │ │ └── validate-dimensions.js │ │ ├── move-in-direction/ │ │ │ ├── index.js │ │ │ ├── move-cross-axis/ │ │ │ │ ├── get-best-cross-axis-droppable.js │ │ │ │ ├── get-closest-draggable.js │ │ │ │ ├── index.js │ │ │ │ ├── move-to-new-droppable.js │ │ │ │ └── without-starting-displacement.js │ │ │ ├── move-in-direction-types.js │ │ │ └── move-to-next-place/ │ │ │ ├── index.js │ │ │ ├── is-totally-visible-in-new-location.js │ │ │ ├── move-to-next-combine/ │ │ │ │ └── index.js │ │ │ └── move-to-next-index/ │ │ │ ├── from-combine.js │ │ │ ├── from-reorder.js │ │ │ └── index.js │ │ ├── no-impact.js │ │ ├── patch-dimension-map.js │ │ ├── patch-droppable-map.js │ │ ├── position.js │ │ ├── post-reducer/ │ │ │ └── when-moving/ │ │ │ ├── refresh-snap.js │ │ │ └── update.js │ │ ├── publish-while-dragging-in-virtual/ │ │ │ ├── adjust-additions-for-scroll-changes.js │ │ │ ├── index.js │ │ │ └── offset-draggable.js │ │ ├── recompute-placeholders.js │ │ ├── rect.js │ │ ├── reducer.js │ │ ├── registry/ │ │ │ ├── create-registry.js │ │ │ ├── registry-types.js │ │ │ └── use-registry.js │ │ ├── remove-draggable-from-list.js │ │ ├── scroll-viewport.js │ │ ├── spacing.js │ │ ├── store-types.js │ │ ├── update-displacement-visibility/ │ │ │ ├── recompute.js │ │ │ └── speculatively-increase.js │ │ ├── visibility/ │ │ │ ├── is-partially-visible-through-frame.js │ │ │ ├── is-position-in-frame.js │ │ │ ├── is-totally-visible-through-frame-on-axis.js │ │ │ ├── is-totally-visible-through-frame.js │ │ │ └── is-visible.js │ │ └── with-scroll-change/ │ │ ├── with-all-displacement.js │ │ ├── with-droppable-displacement.js │ │ ├── with-droppable-scroll.js │ │ └── with-viewport-displacement.js │ ├── types.js │ └── view/ │ ├── animate-in-out/ │ │ ├── animate-in-out.jsx │ │ └── index.js │ ├── check-is-valid-inner-ref.js │ ├── context/ │ │ ├── app-context.js │ │ ├── droppable-context.js │ │ └── store-context.js │ ├── data-attributes.js │ ├── drag-drop-context/ │ │ ├── app.jsx │ │ ├── check-doctype.js │ │ ├── check-react-version.js │ │ ├── drag-drop-context-types.js │ │ ├── drag-drop-context.jsx │ │ ├── error-boundary.jsx │ │ ├── index.js │ │ ├── use-startup-validation.js │ │ └── use-unique-context-id.js │ ├── draggable/ │ │ ├── connected-draggable.js │ │ ├── draggable-api.jsx │ │ ├── draggable-types.js │ │ ├── draggable.jsx │ │ ├── get-style.js │ │ ├── index.js │ │ └── use-validation.js │ ├── droppable/ │ │ ├── connected-droppable.js │ │ ├── droppable-types.js │ │ ├── droppable.jsx │ │ ├── index.js │ │ └── use-validation.js │ ├── event-bindings/ │ │ ├── bind-events.js │ │ └── event-types.js │ ├── get-body-element.js │ ├── get-border-box-center-position.js │ ├── get-document-element.js │ ├── get-elements/ │ │ ├── find-drag-handle.js │ │ └── find-draggable.js │ ├── is-strict-equal.js │ ├── is-type-of-element/ │ │ ├── is-element.js │ │ ├── is-html-element.js │ │ └── is-svg-element.js │ ├── key-codes.js │ ├── placeholder/ │ │ ├── index.js │ │ ├── placeholder-types.js │ │ └── placeholder.jsx │ ├── scroll-listener.js │ ├── throw-if-invalid-inner-ref.js │ ├── use-announcer/ │ │ ├── index.js │ │ └── use-announcer.js │ ├── use-dev-setup-warning.js │ ├── use-dev.js │ ├── use-draggable-publisher/ │ │ ├── get-dimension.js │ │ ├── index.js │ │ └── use-draggable-publisher.js │ ├── use-droppable-publisher/ │ │ ├── check-for-nested-scroll-container.js │ │ ├── get-closest-scrollable.js │ │ ├── get-dimension.js │ │ ├── get-env.js │ │ ├── get-listener-options.js │ │ ├── get-scroll.js │ │ ├── index.js │ │ ├── is-in-fixed-container.js │ │ └── use-droppable-publisher.js │ ├── use-focus-marshal/ │ │ ├── focus-marshal-types.js │ │ ├── index.js │ │ └── use-focus-marshal.js │ ├── use-hidden-text-element/ │ │ ├── index.js │ │ └── use-hidden-text-element.js │ ├── use-isomorphic-layout-effect.js │ ├── use-previous-ref.js │ ├── use-required-context.js │ ├── use-sensor-marshal/ │ │ ├── closest.js │ │ ├── find-closest-draggable-id-from-event.js │ │ ├── index.js │ │ ├── is-event-in-interactive-element.js │ │ ├── lock.js │ │ ├── sensors/ │ │ │ ├── use-keyboard-sensor.js │ │ │ ├── use-mouse-sensor.js │ │ │ ├── use-touch-sensor.js │ │ │ └── util/ │ │ │ ├── prevent-standard-key-events.js │ │ │ └── supported-page-visibility-event-name.js │ │ ├── use-sensor-marshal.js │ │ └── use-validate-sensor-hooks.js │ ├── use-style-marshal/ │ │ ├── get-styles.js │ │ ├── index.js │ │ ├── style-marshal-types.js │ │ └── use-style-marshal.js │ ├── use-unique-id.js │ ├── visually-hidden-style.js │ └── window/ │ ├── get-max-window-scroll.js │ ├── get-viewport.js │ ├── get-window-from-el.js │ ├── get-window-scroll.js │ └── scroll-window.js ├── stories/ │ ├── .eslintrc.js │ ├── 1-single-vertical-list.stories.js │ ├── 10-table.stories.js │ ├── 11-portal.stories.js │ ├── 12-dynamic.stories.js │ ├── 15-on-before-capture.stories.js │ ├── 2-single-horizontal.stories.js │ ├── 20-super-simple.stories.js │ ├── 21-change-on-drag-start.stories.js │ ├── 25-fixed-list.stories.js │ ├── 3-board.stories.stories.js │ ├── 30-custom-drop.stories.js │ ├── 35-function-component.stories.js │ ├── 4-complex-vertical-list.stories.js │ ├── 40-programmatic.stories.js │ ├── 45-virtual.stories.js │ ├── 5-multiple-vertical-lists.stories.js │ ├── 50-multiple-contexts.stories.js │ ├── 55-mixed-sizes.stories.js │ ├── 6-multiple-horizontal-lists.stories.js │ ├── 7-interactive-elements.stories.js │ ├── 8-accessibility.stories.js │ ├── 9-multi-drag.stories.js │ ├── 99-debug.stories.js │ └── src/ │ ├── accessible/ │ │ ├── blur-context.js │ │ ├── data.js │ │ ├── task-app.jsx │ │ ├── task-list.jsx │ │ └── task.jsx │ ├── board/ │ │ ├── board.jsx │ │ └── column.jsx │ ├── constants.js │ ├── custom-drop/ │ │ ├── funny-drop.jsx │ │ └── no-drop.jsx │ ├── data.js │ ├── dynamic/ │ │ ├── lazy-loading.jsx │ │ └── with-controls.jsx │ ├── fixed-list/ │ │ └── fixed-sidebar.jsx │ ├── function-component/ │ │ └── quote-app.jsx │ ├── horizontal/ │ │ └── author-app.jsx │ ├── interactive-elements/ │ │ └── interactive-elements-app.jsx │ ├── mixed-sizes/ │ │ ├── mixed-size-items.jsx │ │ ├── mixed-size-lists-experiment.jsx │ │ └── mixed-size-lists.jsx │ ├── multi-drag/ │ │ ├── column.jsx │ │ ├── data.js │ │ ├── task-app.jsx │ │ ├── task.jsx │ │ ├── types.js │ │ └── utils.js │ ├── multiple-horizontal/ │ │ └── quote-app.jsx │ ├── multiple-vertical/ │ │ └── quote-app.jsx │ ├── on-before-capture/ │ │ └── adding-things.jsx │ ├── portal/ │ │ └── portal-app.jsx │ ├── primatives/ │ │ ├── author-item.jsx │ │ ├── author-list.jsx │ │ ├── quote-item.jsx │ │ ├── quote-list.jsx │ │ └── title.jsx │ ├── programmatic/ │ │ ├── multiple-contexts.jsx │ │ ├── runsheet.jsx │ │ └── with-controls.jsx │ ├── reorder.js │ ├── simple/ │ │ ├── simple-mixed-spacing.jsx │ │ ├── simple-scrollable.jsx │ │ └── simple.jsx │ ├── table/ │ │ ├── with-clone.jsx │ │ ├── with-dimension-locking.jsx │ │ ├── with-fixed-columns.jsx │ │ └── with-portal.jsx │ ├── types.js │ ├── vertical/ │ │ └── quote-app.jsx │ ├── vertical-grouped/ │ │ └── quote-app.jsx │ ├── vertical-nested/ │ │ ├── quote-app.jsx │ │ ├── quote-list.jsx │ │ └── types.js │ └── virtual/ │ ├── quote-count-chooser.jsx │ ├── react-virtualized/ │ │ ├── board.jsx │ │ ├── list.jsx │ │ └── window-list.jsx │ └── react-window/ │ ├── board.jsx │ └── list.jsx └── test/ ├── .eslintrc.js ├── env-setup.js ├── test-flow-types.js ├── test-setup.js ├── unit/ │ ├── dev-warning.spec.js │ ├── docs/ │ │ ├── content.spec.js │ │ └── no-broken-links.spec.js │ ├── health/ │ │ ├── drop-dev-warnings-for-prod.spec.js │ │ └── src-file-name-convention.spec.js │ ├── integration/ │ │ ├── accessibility/ │ │ │ └── axe-audit.spec.js │ │ ├── body-removal-before-unmount.spec.js │ │ ├── combine-on-start.spec.js │ │ ├── disable-on-start.spec.js │ │ ├── drag-drop-context/ │ │ │ ├── check-doctype.spec.js │ │ │ ├── check-react-version.spec.js │ │ │ ├── clashing-with-consumers-redux.spec.js │ │ │ ├── error-handling/ │ │ │ │ ├── error-in-react-tree.spec.js │ │ │ │ └── error-on-window.spec.js │ │ │ ├── on-before-capture/ │ │ │ │ ├── additions.spec.js │ │ │ │ └── removals.spec.js │ │ │ ├── reset-server-context.spec.js │ │ │ └── unmount.spec.js │ │ ├── drag-handle/ │ │ │ ├── keyboard-sensor/ │ │ │ │ ├── directional-movement.spec.js │ │ │ │ ├── no-click-blocking.spec.js │ │ │ │ ├── prevent-keyboard-scroll.spec.js │ │ │ │ ├── prevent-standard-keys-while-dragging.spec.js │ │ │ │ ├── starting-a-drag.spec.js │ │ │ │ └── stopping-a-drag.spec.js │ │ │ ├── mouse-sensor/ │ │ │ │ ├── cancel-while-pending.spec.js │ │ │ │ ├── click-blocking.spec.js │ │ │ │ ├── force-press.spec.js │ │ │ │ ├── prevent-standard-keys-while-dragging.spec.js │ │ │ │ ├── starting-a-dragging.spec.js │ │ │ │ └── stopping-a-drag.spec.js │ │ │ ├── sensor-marshal/ │ │ │ │ ├── click-blocking.spec.js │ │ │ │ ├── force-releasing-locks.spec.js │ │ │ │ ├── is-lock-claimed.spec.js │ │ │ │ ├── lock-context-isolation.spec.js │ │ │ │ ├── move-throttling.spec.js │ │ │ │ ├── no-double-lift.spec.js │ │ │ │ ├── obtaining-lock.spec.js │ │ │ │ └── outdated-locks.spec.js │ │ │ ├── shared-behaviours/ │ │ │ │ ├── abort-on-error.spec.js │ │ │ │ ├── cancel-while-dragging.spec.js │ │ │ │ ├── cannot-start-when-disabled.spec.js │ │ │ │ ├── cannot-start-when-something-else-has-lock.spec.js │ │ │ │ ├── cannot-start-when-unmounted.spec.js │ │ │ │ ├── cleanup.spec.js │ │ │ │ ├── contenteditable.spec.js │ │ │ │ ├── disable-default-sensors.spec.js │ │ │ │ ├── interactive-elements.spec.js │ │ │ │ ├── lock-released-mid-drag.spec.js │ │ │ │ ├── lock-released-pre-drag.spec.js │ │ │ │ ├── nested-handles.spec.js │ │ │ │ ├── no-dragging-svgs.spec.js │ │ │ │ ├── parent-rendering-should-not-kill-drag.spec.js │ │ │ │ └── validate-controls.spec.js │ │ │ └── touch-sensor/ │ │ │ ├── cancel-while-pending.spec.js │ │ │ ├── click-blocking.spec.js │ │ │ ├── context-menu-opt-out.spec.js │ │ │ ├── force-press.spec.js │ │ │ ├── starting-a-drag.spec.js │ │ │ ├── stopping-a-drag.spec.js │ │ │ └── unmounted-while-pending-timer-running.spec.js │ │ ├── draggable/ │ │ │ ├── combined-with.spec.js │ │ │ ├── dragging.spec.js │ │ │ ├── dropping.spec.js │ │ │ ├── moving-out-of-the-way.spec.js │ │ │ ├── portal.spec.js │ │ │ ├── resting.spec.js │ │ │ └── validation.spec.js │ │ ├── droppable/ │ │ │ ├── clone.spec.js │ │ │ └── placeholder.spec.js │ │ ├── reorder-render-sync.spec.js │ │ ├── responders-integration.spec.js │ │ ├── responders-timing.spec.js │ │ ├── server-side-rendering/ │ │ │ ├── __snapshots__/ │ │ │ │ └── server-rendering.spec.js.snap │ │ │ ├── client-hydration.spec.js │ │ │ └── server-rendering.spec.js │ │ └── util/ │ │ ├── app.jsx │ │ ├── board.jsx │ │ ├── controls.js │ │ ├── expanded-mouse.js │ │ └── helpers.js │ ├── state/ │ │ ├── auto-scroll/ │ │ │ ├── can-scroll.spec.js │ │ │ ├── choosing-the-right-scroller.spec.js │ │ │ ├── fluid-scroller/ │ │ │ │ ├── big-draggables.spec.js │ │ │ │ ├── droppable-scrolling.spec.js │ │ │ │ ├── lifecycle.spec.js │ │ │ │ ├── time-dampening.spec.js │ │ │ │ ├── util/ │ │ │ │ │ ├── drag-to.js │ │ │ │ │ ├── for-each.js │ │ │ │ │ ├── get-args-mock.js │ │ │ │ │ ├── get-droppable.js │ │ │ │ │ └── viewport.js │ │ │ │ ├── window-before-droppable.spec.js │ │ │ │ └── window-scrolling.spec.js │ │ │ └── jump-scroller.spec.js │ │ ├── can-start-drag.spec.js │ │ ├── dimension-structures.spec.js │ │ ├── droppable/ │ │ │ ├── clip.spec.js │ │ │ ├── get-droppable.spec.js │ │ │ ├── get-subject.spec.js │ │ │ ├── is-home-of.spec.js │ │ │ ├── scroll-droppable.spec.js │ │ │ ├── what-is-dragged-over.spec.js │ │ │ └── with-placeholder.spec.js │ │ ├── get-center-from-impact/ │ │ │ ├── get-client-border-box-center.spec.js │ │ │ ├── get-client-from-page-border-box-center.spec.js │ │ │ ├── get-page-border-box-center/ │ │ │ │ ├── combine/ │ │ │ │ │ ├── when-combining.spec.js │ │ │ │ │ └── with-droppable-scroll.spec.js │ │ │ │ ├── in-home-location.spec.js │ │ │ │ ├── over-nothing.spec.js │ │ │ │ └── reorder/ │ │ │ │ ├── in-empty-list.spec.js │ │ │ │ ├── nothing-displaced.spec.js │ │ │ │ ├── there-is-displacement.spec.js │ │ │ │ └── with-droppable-scroll.spec.js │ │ │ └── move-relative-to.spec.js │ │ ├── get-displacement-groups/ │ │ │ ├── get-displacement-groups.spec.js │ │ │ └── use-initial-position-not-displaced.spec.js │ │ ├── get-drag-impact/ │ │ │ ├── combine/ │ │ │ │ ├── is-combine-disabled.spec.js │ │ │ │ ├── should-not-combine-with-home-draggable.spec.js │ │ │ │ ├── started-after-critical.spec.js │ │ │ │ ├── started-before-critical.spec.js │ │ │ │ └── with-droppable-scroll.spec.js │ │ │ ├── is-disabled.spec.js │ │ │ ├── over-nothing.spec.js │ │ │ ├── reorder/ │ │ │ │ ├── over-foreign-list/ │ │ │ │ │ ├── did-not-start-displaced.spec.js │ │ │ │ │ ├── move-backward-from-last-item.spec.js │ │ │ │ │ └── move-past-last-item.spec.js │ │ │ │ └── over-home-list/ │ │ │ │ ├── displacement-visibility.spec.js │ │ │ │ ├── move-past-last-item.spec.js │ │ │ │ ├── started-after-critical.spec.js │ │ │ │ ├── started-before-critical.spec.js │ │ │ │ └── with-droppable-scroll.spec.js │ │ │ └── util/ │ │ │ ├── get-combine-threshold.js │ │ │ └── get-offset-for-edge.js │ │ ├── get-draggables-inside-droppable.spec.js │ │ ├── get-droppable-over/ │ │ │ ├── center-is-over.spec.js │ │ │ ├── is-disabled.spec.js │ │ │ ├── is-not-visible.spec.js │ │ │ ├── is-over-nothing.spec.js │ │ │ ├── item-edge-is-over-list-center.spec.js │ │ │ ├── item-is-totally-over.spec.js │ │ │ └── preferencing.spec.js │ │ ├── get-lift-effect/ │ │ │ └── get-lift-effect.spec.js │ │ ├── is-within.spec.js │ │ ├── middleware/ │ │ │ ├── auto-scroll.spec.js │ │ │ ├── dimension-marshal-stopper.spec.js │ │ │ ├── drop/ │ │ │ │ ├── conditionally-animate-drop.spec.js │ │ │ │ ├── drop-animation-finish-middleware.spec.js │ │ │ │ ├── drop-animation-flush-on-scroll-middleware.spec.js │ │ │ │ ├── drop-impact.spec.js │ │ │ │ ├── drop-position.spec.js │ │ │ │ ├── get-drop-duration.spec.js │ │ │ │ ├── result-impact-mismatch.spec.js │ │ │ │ └── timing.spec.js │ │ │ ├── lift.spec.js │ │ │ ├── pending-drop.spec.js │ │ │ ├── responders/ │ │ │ │ ├── abort.spec.js │ │ │ │ ├── announcements.spec.js │ │ │ │ ├── drop.spec.js │ │ │ │ ├── flushing.spec.js │ │ │ │ ├── repeated-use.spec.js │ │ │ │ ├── start.spec.js │ │ │ │ ├── update.spec.js │ │ │ │ └── util/ │ │ │ │ ├── get-announce-stub.js │ │ │ │ ├── get-completed-with-result.js │ │ │ │ └── get-responders-stub.js │ │ │ ├── style.spec.js │ │ │ ├── util/ │ │ │ │ ├── create-store.js │ │ │ │ └── pass-through-middleware.js │ │ │ └── validate-indexes.spec.js │ │ ├── move-in-direction/ │ │ │ ├── move-cross-axis/ │ │ │ │ ├── get-best-cross-axis-droppable.spec.js │ │ │ │ ├── get-closest-draggable/ │ │ │ │ │ ├── with-starting-displacement.spec.js │ │ │ │ │ └── without-starting-displacement.spec.js │ │ │ │ ├── move-to-new-droppable/ │ │ │ │ │ ├── to-foreign-list.spec.js │ │ │ │ │ └── to-home-list.spec.js │ │ │ │ └── no-visible-targets-in-list.spec.js │ │ │ ├── move-in-direction.spec.js │ │ │ └── move-to-next-place/ │ │ │ ├── move-to-next-combine/ │ │ │ │ ├── in-foreign-list.legacy.spec.js │ │ │ │ └── in-home-list.legacy.spec.js │ │ │ ├── move-to-next-index/ │ │ │ │ ├── from-combine/ │ │ │ │ │ ├── did-not-start-after-critical.spec.js │ │ │ │ │ └── started-after-critical.spec.js │ │ │ │ └── from-reorder/ │ │ │ │ ├── in-foreign-list.spec.js │ │ │ │ └── in-home-list.spec.js │ │ │ └── moving-to-invisible-place/ │ │ │ ├── not-visible-in-droppable.spec.js │ │ │ └── not-visible-in-viewport.spec.js │ │ ├── position.spec.js │ │ ├── post-reducer/ │ │ │ └── .gitkeep │ │ ├── publish-while-dragging/ │ │ │ ├── adjust-additions-for-scroll-change.spec.js │ │ │ ├── displacement-animation.spec.js │ │ │ ├── droppable-scroll-change.spec.js │ │ │ ├── nothing-changed.spec.js │ │ │ ├── phase-change.spec.js │ │ │ ├── recompute-after-critical.spec.js │ │ │ └── util.js │ │ ├── recompute-placeholders.spec.js │ │ ├── registry/ │ │ │ ├── cleanup.spec.js │ │ │ ├── draggable-registration.spec.js │ │ │ ├── droppable-registration.spec.js │ │ │ ├── event-listeners.spec.js │ │ │ ├── queries.spec.js │ │ │ └── use-registry.spec.js │ │ ├── scroll-viewport.spec.js │ │ ├── spacing.spec.js │ │ ├── update-displacement-visibility/ │ │ │ ├── recompute.spec.js │ │ │ └── speculative-displacement.spec.js │ │ └── visibility/ │ │ ├── is-partially-visible-through-frame.spec.js │ │ ├── is-partially-visible.spec.js │ │ ├── is-position-in-frame.spec.js │ │ ├── is-totally-visible-on-axis.spec.js │ │ ├── is-totally-visible-through-frame.spec.js │ │ └── is-totally-visible.spec.js │ └── view/ │ ├── animate-in-out/ │ │ ├── animate-in-out.spec.js │ │ └── child-rendering.spec.js │ ├── announcer.spec.js │ ├── connected-draggable/ │ │ ├── child-render-behaviour.spec.js │ │ ├── combine-target-for.spec.js │ │ ├── combine-with.spec.js │ │ ├── dragging.spec.js │ │ ├── dropping-something-else.spec.js │ │ ├── dropping-with-result-mismatch.spec.js │ │ ├── dropping.spec.js │ │ ├── nothing-is-dragging.spec.js │ │ ├── selector-isolation.spec.js │ │ ├── something-else-dragging-in-virtual.spec.js │ │ ├── something-else-is-dragging.spec.js │ │ └── util/ │ │ ├── get-dragging-map-props.js │ │ ├── get-own-props.js │ │ ├── get-secondary-map-props.js │ │ └── get-snapshot.js │ ├── connected-droppable/ │ │ ├── child-render-behaviour.spec.js │ │ ├── disabled.spec.js │ │ ├── dragging.spec.js │ │ ├── dropping.spec.js │ │ ├── post-drop.spec.js │ │ ├── selector-isolation.spec.js │ │ └── util/ │ │ ├── get-own-props.js │ │ ├── resting-props.js │ │ └── with-combine-impact.js │ ├── dimension-marshal/ │ │ ├── droppable-passthrough.spec.js │ │ ├── initial-publish.spec.js │ │ ├── publish-while-dragging.spec.js │ │ └── util.js │ ├── drag-drop-context/ │ │ └── content-security-protection-nonce.spec.js │ ├── droppable/ │ │ ├── home-list-placeholder-cleanup.spec.js │ │ ├── inner-ref-validation.spec.js │ │ ├── own-props-validation.spec.js │ │ ├── pass-through-snapshot.spec.js │ │ ├── placeholder-setup-warning.spec.js │ │ ├── placeholder.spec.js │ │ ├── update-max-window-scroll.spec.js │ │ └── util/ │ │ ├── get-props.js │ │ ├── get-stubber.js │ │ └── mount.js │ ├── is-type-of-element/ │ │ ├── is-element.spec.js │ │ ├── is-html-element.spec.js │ │ ├── is-svg-element.spec.js │ │ └── util/ │ │ └── get-svg.js │ ├── placeholder/ │ │ ├── animated-mount.spec.js │ │ ├── on-close.spec.js │ │ ├── on-transition-end.spec.js │ │ └── util/ │ │ ├── data.js │ │ ├── expect.js │ │ ├── get-placeholder-style.js │ │ └── placeholder-with-class.js │ ├── style-marshal/ │ │ ├── get-styles.spec.js │ │ └── style-marshal.spec.js │ ├── use-draggable-publisher.spec.js │ └── use-droppable-publisher/ │ ├── forced-scroll.spec.js │ ├── is-combined-enabled-change.spec.js │ ├── is-element-scrollable.spec.js │ ├── is-enabled-change.spec.js │ ├── publishing.spec.js │ ├── recollection.spec.js │ ├── registration.spec.js │ ├── scroll-watching.spec.js │ └── util/ │ └── shared.js └── util/ ├── after-point.js ├── before-point.js ├── cause-runtime-error.js ├── clone-impact.js ├── console.js ├── create-ref.js ├── dimension-marshal.js ├── dimension.js ├── dragging-state.js ├── force-update.js ├── get-simple-state-preset.js ├── impact.js ├── no-after-critical.js ├── pass-through-props.jsx ├── preset-action-args.js ├── registry.js ├── reorder.js ├── set-window-scroll-size.js ├── set-window-scroll.js ├── spacing.js ├── try-clean-prototype-stubs.js ├── user-input-util.js └── viewport.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .browserlistrc ================================================ ie >= 11 last 1 Edge version last 1 Firefox version last 1 Chrome version last 1 Safari version last 1 iOS version last 1 Android version last 1 ChromeAndroid version ================================================ FILE: .circleci/config.yml ================================================ version: 2 jobs: install: docker: - image: circleci/node:10.24.0-browsers working_directory: ~/repo steps: # Fetch Code - checkout - restore_cache: keys: # Restore cached node_modules - v12-dependencies-{{ checksum "yarn.lock" }} # fallback to using the latest cache if no exact match is found - v12-dependencies- - run: name: Add CI global modules command: yarn global add greenkeeper-lockfile@1 - run: name: Install Dependencies command: yarn - run: name: Update Lockfile command: $(yarn global bin)/greenkeeper-lockfile-update - run: name: Upload Lockfile command: $(yarn global bin)/greenkeeper-lockfile-upload - run: name: Validate Yarn Lock File command: | if [[ "$(git status -s)" != "" ]] && [[ "$CIRCLE_BRANCH" != greenkeeper/* ]]; then echo "Your yarn.lock was modified during install, please check in any changes to the yarn.lock file" exit 1 fi # Save the node_modules cache - save_cache: paths: - node_modules key: v12-dependencies-{{ checksum "yarn.lock" }} validate: docker: - image: circleci/node:10.24.0-browsers working_directory: ~/repo steps: - checkout - restore_cache: keys: - v12-dependencies-{{ checksum "yarn.lock" }} - run: # PR's from forks cannot use the dependency cache for performance reasons name: 'Forked PR dependency install' command: yarn - run: name: Lint + Typecheck command: yarn validate test-unit: docker: - image: circleci/node:10.24.0-browsers working_directory: ~/repo steps: - checkout - restore_cache: keys: - v12-dependencies-{{ checksum "yarn.lock" }} - run: # PR's from forks cannot use the dependency cache for performance reasons name: 'Forked PR dependency install' command: yarn - run: name: Jest Suite command: yarn test:ci environment: JEST_JUNIT_OUTPUT: 'test-reports/junit/js-test-results.xml' - store_test_results: path: test-reports/junit test-bundle: docker: - image: circleci/node:10.24.0-browsers working_directory: ~/repo steps: - checkout - restore_cache: keys: - v12-dependencies-{{ checksum "yarn.lock" }} # PR's from forks cannot use the dependency cache for performance reasons - run: name: 'Forked PR dependency install' command: yarn - run: name: Check Bundle Size command: yarn run bundle-size:check test-browser: docker: # Single Docker container with Node 10 and Cypress dependencies # https://github.com/cypress-io/circleci-orb/blob/master/src/orb.yml - image: cypress/base:10.22.0 working_directory: ~/repo steps: - checkout - restore_cache: keys: - v12-dependencies-{{ checksum "yarn.lock" }} # PR's from forks cannot use the dependency cache for performance reasons - run: name: 'Forked PR dependency install' command: yarn - run: name: 'Run cypress' command: node browser-test-harness.js yarn test:browser:ci # store videos and screenshots (if any) as CI artifacts - store_artifacts: path: cypress/videos - store_artifacts: path: cypress/screenshots test-a11y: docker: - image: circleci/node:10.24.0-browsers working_directory: ~/repo steps: - checkout - restore_cache: keys: - v12-dependencies-{{ checksum "yarn.lock" }} # PR's from forks cannot use the dependency cache for performance reasons - run: name: 'Forked PR dependency install' command: yarn - run: name: Accessibility Audit command: node browser-test-harness.js yarn test:accessibility - store_artifacts: path: test-reports/lighthouse - store_test_results: path: test-reports/lighthouse workflows: version: 2 build: jobs: - install - validate: requires: - install - test-unit: requires: - install - test-bundle: requires: - install - test-browser: requires: - install # disabling ally test due to issue with lighthouse # not sure why lighthouse starting breaking, but could # be worth looking into upgrading browsers (docker) + lighthouse # - test-a11y: # requires: # - install ================================================ FILE: .eslintignore ================================================ # Generated files dist/ flow-typed/ site/ coverage/ babel.config.js ================================================ FILE: .eslintrc.js ================================================ module.exports = { extends: [ 'prettier', 'airbnb', 'plugin:flowtype/recommended', 'prettier/react', 'prettier/flowtype', 'plugin:jest/recommended', 'plugin:prettier/recommended', ], parser: 'babel-eslint', plugins: [ 'prettier', 'flowtype', 'emotion', 'react', 'react-hooks', 'import', 'jest', 'es5', ], env: { es6: true, browser: true, node: true, 'jest/globals': true, }, globals: { // flow globals TimeoutID: true, IntervalID: true, AnimationFrameID: true, }, rules: { // Error on prettier violations 'prettier/prettier': 'error', // New eslint style rules that is not disabled by prettier: 'lines-between-class-members': 'off', // Allowing warning and error console logging // use `invariant` and `warning` 'no-console': ['error'], // Opting out of prefer destructuring (nicer with flow in lots of cases) 'prefer-destructuring': 'off', // Disallowing the use of variables starting with `_` unless it called on `this`. // Allowed: `this._secret = Symbol()` // Not allowed: `const _secret = Symbol()` 'no-underscore-dangle': ['error', { allowAfterThis: true }], // Cannot reassign function parameters but allowing modification 'no-param-reassign': ['error', { props: false }], // Named exports are kewl 'import/prefer-default-export': 'off', // Don't tell me what to do! 'max-classes-per-file': 'off', // Allowing ++ on numbers 'no-plusplus': 'off', // Always enforcing the use of curly braces for if statements curly: ['error', 'all'], 'no-restricted-syntax': [ // Nicer booleans #1 // Disabling the use of !! to cast to boolean 'error', { selector: 'UnaryExpression[operator="!"] > UnaryExpression[operator="!"]', message: '!! to cast to boolean relies on a double negative. Use Boolean() instead', }, // Nicer booleans #2 // Avoiding accidental `new Boolean()` calls // (also covered by `no-new-wrappers` but i am having fun) { selector: 'NewExpression[callee.name="Boolean"]', message: 'Avoid using constructor: `new Boolean(value)` as it creates a Boolean object. Did you mean `Boolean(value)`?', }, // We are using a useLayoutEffect / useEffect switch to avoid SSR warnings for useLayoutEffect // We want to ensure we use `import useEffect from '*use-isomorphic-layout-effect'` // to ensure we still get the benefits of `eslint-plugin-react-hooks` { selector: 'ImportDeclaration[source.value=/use-isomorphic-layout-effect/] > ImportDefaultSpecifier[local.name!="useLayoutEffect"]', message: 'Must use `useLayoutEffect` as the name of the import from `*use-isomorphic-layout-effect` to leverage `eslint-plugin-react-hooks`', }, // No Array.from as it pulls in a large amount of babel helpers { selector: 'MemberExpression[object.name="Array"][property.name="from"]', message: 'Not allowing using of Array.from to save kbs. Please use native-with-fallback/from', }, // No usage of `tiny-invariant`. Must use our own invariant for error flow { selector: 'ImportDeclaration[source.value="tiny-invariant"]', message: 'Please use our own invariant function (src/invariant.js) to ensure correct error flow', }, // Must use invariant to throw { selector: 'ThrowStatement', message: 'Please use invariant (src/invariant.js) for throwing. This is to ensure correct error flows', }, ], // Allowing Math.pow rather than forcing `**` 'no-restricted-properties': [ 'off', { object: 'Math', property: 'pow', }, ], 'no-restricted-imports': [ 'error', { paths: [ // Forcing use of useMemoOne { name: 'react', importNames: ['useMemo', 'useCallback'], message: '`useMemo` and `useCallback` are subject to cache busting. Please use `useMemoOne`', }, // Forcing use aliased imports from useMemoOne { name: 'use-memo-one', importNames: ['useMemoOne', 'useCallbackOne'], message: 'use-memo-one exports `useMemo` and `useCallback` which work nicer with `eslint-plugin-react-hooks`', }, // Disabling using of useLayoutEffect from react { name: 'react', importNames: ['useLayoutEffect'], message: '`useLayoutEffect` causes a warning in SSR. Use `useIsomorphicLayoutEffect`', }, ], }, ], // Allowing jsx in files with any file extension (old components have jsx but not the extension) 'react/jsx-filename-extension': 'off', // Not requiring default prop declarations all the time 'react/require-default-props': 'off', // Opt out of preferring stateless functions 'react/prefer-stateless-function': 'off', // Allowing files to have multiple components in it 'react/no-multi-comp': 'off', // Sometimes we use the PropTypes.object PropType for simplicity 'react/forbid-prop-types': 'off', // Allowing the non function setState approach 'react/no-access-state-in-setstate': 'off', // Opting out of this 'react/destructuring-assignment': 'off', // Adding 'skipShapeProps' as the rule has issues with correctly handling PropTypes.shape 'react/no-unused-prop-types': ['error', { skipShapeProps: true }], // Having issues with this rule not working correctly 'react/default-props-match-prop-types': 'off', // Allowing functions to be passed as props 'react/jsx-no-bind': 'off', // Require // @flow at the top of files 'flowtype/require-valid-file-annotation': [ 'error', 'always', { annotationStyle: 'line' }, ], // Allowing importing from dev deps (for stories and tests) 'import/no-extraneous-dependencies': 'off', // Enforce rules of hooks 'react-hooks/rules-of-hooks': 'error', // Second argument to hook functions 'react-hooks/exhaustive-deps': 'error', 'react/jsx-props-no-spreading': 'off', // using is fine 'react/jsx-fragments': 'off', // all good to declare static class members in the class 'react/static-property-placement': 'off', // don't need to initialize state in a constructor 'react/state-in-constructor': 'off', 'jest/expect-expect': [ 'error', { assertFunctionNames: [ 'expect', // these functions will run expect internally 'withWarn', 'withError', 'withoutError', 'withoutWarn', ], }, ], }, overrides: [ // Forbid using not es5 methods { files: 'src/**/*.js', rules: { 'es5/no-es6-methods': 'error', 'es5/no-es6-static-methods': [ 'error', { exceptMethods: ['Object.assign'] }, ], }, }, ], }; ================================================ FILE: .flowconfig ================================================ [untyped] # Issue with atlaskit/theme typing .*/node_modules/@atlaskit/theme [ignore] # Creating lots of invalid files .*/node_modules/jsonlint-mod/.* [libs] ./flow-typed/custom/ [options] # Provides a way to suppress flow errors in the following line. # Example: // $FlowFixMe: This following line is borked because of reasons. # Example: // $ExpectError: This following line is correctly ignored by flow. suppress_comment= \\(.\\|\n\\)*\\$FlowFixMe suppress_comment= \\(.\\|\n\\)*\\$ExpectError include_warnings=true # Fixing issue with CircleCI where flow would hang # I suspect this is caused by incorrect advertisement of virtual cores server.max_workers=1 ================================================ FILE: .github/ISSUE_TEMPLATE/bug-report.md ================================================ --- name: 🐛 Bug Report about: Bugs, missing documentation, or unexpected behavior 🤔. labels: "unconfirmed-bug, untriaged" --- ### Expected behavior ### Actual behavior ### Steps to reproduce ### Suggested solution? ### What version of `React` are you using? ### What version of `react-beautiful-dnd` are you running? ### What browser are you using? ### Demo ================================================ FILE: .github/ISSUE_TEMPLATE/feature-request.md ================================================ --- name: 💡Feature request about: Ideas and suggestions labels: "idea \U0001F914, untriaged" --- ### Description ================================================ FILE: .github/ISSUE_TEMPLATE/tree-issue.md ================================================ --- name: 🌲@atlaskit/tree about: Bugs and feature requests for @atlaskit/tree labels: 'wontfix ☠️' --- Please head to the [`@atlaskit/tree` issue tracker](https://ecosystem.atlassian.net/servicedesk/customer/portal/24/create/236). We do not track `@atlaskit/tree` issues in the `react-beautiful-dnd` project ================================================ FILE: .gitignore ================================================ # editors .idea .vscode # library node_modules/ # MacOS .DS_Store # generated files dist/ # generated site site/ # coverage reports coverage/ # test reports test-reports/ # test outputs cypress/videos/ cypress/screenshots/ # storybook .storybook.out .cache/ # logs yarn-error.log npm-debug.log npm-debug.log.* ================================================ FILE: .nvmrc ================================================ 10.24.0 ================================================ FILE: .prettierignore ================================================ /node_modules/* ================================================ FILE: .prettierrc ================================================ { "trailingComma": "all", "semi": true, "tabWidth": 2, "useTabs": false, "singleQuote": true } ================================================ FILE: .size-snapshot.json ================================================ { "dist/react-beautiful-dnd.js": { "bundled": 364998, "minified": 133574, "gzipped": 39437 }, "dist/react-beautiful-dnd.min.js": { "bundled": 306810, "minified": 108365, "gzipped": 31340 }, "dist/react-beautiful-dnd.esm.js": { "bundled": 240910, "minified": 125371, "gzipped": 32650, "treeshaked": { "rollup": { "code": 21121, "import_statements": 503 }, "webpack": { "code": 24005 } } } } ================================================ FILE: .storybook/.babelrc ================================================ { "presets": [ "@babel/react", "@babel/flow", ["@babel/env", { "modules": false, "loose": true }], "@emotion/babel-preset-css-prop" ], "plugins": [ "emotion", ["@babel/proposal-class-properties", { "loose": true }], ["@babel/proposal-object-rest-spread", { "loose": true }] ], "comments": false } ================================================ FILE: .storybook/babel-setup.md ================================================ # Babel storybook looks for a root `.babelrc` in the project for its babel config. However, we are using `.babelrc.js` which is not supported. Rather than putting effort into this we are just having a custom `.babelrc` in this folder which is the same as `.babelrc.js`. This is lame, but we are looking to move away from storybook in the short term anyway. - [Storybook babel docs](https://storybook.js.org/configurations/custom-babel-config/) - [Storybook issue for supporting `babelrc.js`](https://github.com/storybooks/storybook/issues/2582) ================================================ FILE: .storybook/config.js ================================================ import React from 'react'; import { addParameters, configure, addDecorator } from '@storybook/react'; import { create } from '@storybook/theming'; import { withPerformance } from 'storybook-addon-performance'; import GlobalStylesDecorator from './custom-decorators/global-styles'; // adding css reset - storybook includes a css loader import '@atlaskit/css-reset'; import { colors } from '@atlaskit/theme'; import logo from './compressed-logo-rbd.svg'; import { version } from '../package.json'; const theme = create({ brandImage: logo, brandName: 'react-beautiful-dnd', brandUrl: 'https://github.com/atlassian/react-beautiful-dnd', }); addParameters({ options: { // currently not using any addons showPanel: false, theme, }, }); // Using theme would be good for this, but it looks like theme is just for the chrome around the story addDecorator(GlobalStylesDecorator); addDecorator(withPerformance); // automatically import all files ending in *.stories.js const req = require.context('../stories/', true, /.stories.js$/); function loadStories() { req.keys().forEach(filename => req(filename)); } configure(loadStories, module); // Doing this more complex check as console.table || console.log makes CI cry const table = Object.prototype.hasOwnProperty.call(console, 'table') ? console.table : console.log; // Generated by: http://patorjk.com/software/taag/#p=display&f=ANSI%20Shadow&t=rbd console.log( `%c ██████╗ ██████╗ ██████╗ ██╔══██╗██╔══██╗██╔══██╗ ██████╔╝██████╔╝██║ ██║ ██╔══██╗██╔══██╗██║ ██║ ██║ ██║██████╔╝██████╔╝ ╚═╝ ╚═╝╚═════╝ ╚═════╝ %cBeautiful and accessible drag and drop `, `color: ${colors.G200}; font-size: 1.2em; font-weight: bold;`, `color: ${colors.P200}; font-size: 1.2em; font-weight: bold;`, ); table([ ['react-beautiful-dnd version', version], ['react version', React.version], ['process.env.NODE_ENV', process.env.NODE_ENV], ]); ================================================ FILE: .storybook/custom-decorators/global-styles.jsx ================================================ // @flow import React from 'react'; import styled from '@emotion/styled'; import { colors } from '@atlaskit/theme'; import { grid } from '../../stories/src/constants'; // $ExpectError - not sure why const GlobalStyles = styled.div` min-height: 100vh; color: ${colors.N900}; `; const GlobalStylesDecorator = (storyFn: Function) => ( {storyFn()} ); export default GlobalStylesDecorator; ================================================ FILE: .storybook/main.js ================================================ module.exports = { addons: ['storybook-addon-performance/register','@storybook/addon-storysource'], }; ================================================ FILE: .storybook/preview-head.html ================================================ ================================================ FILE: .stylelintrc.json ================================================ { "processors": [ [ "stylelint-processor-styled-components", { "moduleName": "@emotion/styled" } ] ], "extends": [ "stylelint-config-standard", "stylelint-config-styled-components", "stylelint-config-prettier" ], "rules": { "declaration-empty-line-before": null, "comment-empty-line-before": null, "block-no-empty": null, "value-keyword-case": null } } ================================================ FILE: CHANGELOG.md ================================================ # Changelog This project adheres to [Semantic Versioning 2.0](http://semver.org/). All release notes and upgrade notes can be found on our [Github Releases](https://github.com/atlassian/react-beautiful-dnd/releases) page. ================================================ FILE: CODE_OF_CONDUCT.md ================================================ # Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: - Using welcoming and inclusive language - Being respectful of differing viewpoints and experiences - Gracefully accepting constructive criticism - Focusing on what is best for the community - Showing empathy towards other community members Examples of unacceptable behavior by participants include: - The use of sexualized language or imagery and unwelcome sexual attention or advances - Trolling, insulting/derogatory comments, and personal or political attacks - Public or private harassment - Publishing others' private information, such as a physical or electronic address, without explicit permission - Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at areardon@atlassian.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] [homepage]: http://contributor-covenant.org [version]: http://contributor-covenant.org/version/1/4/ ================================================ FILE: CONTRIBUTING.md ================================================ # Contributing Thanks for considering contributing to `react-beautiful-dnd`! ❤️ There are a few categories of contribution so we'll go through them individually. ## Documentation If you think the docs could be improved - please feel free to raise a pull request! ## Bug If you spot a bug you are welcome to raise it on our issue page. You are also welcome to take a crack at fixing it if you like! When you create an issue you will be prompted with the details we would like you to provide. ## Feature request If you would like to see a feature added to the library, here is what you do: 1. Have a read of `README.md` to understand the motivations of this library. It is fairly opinionated and is not intended to be a universal drag and drop library. As such, it will not support every drag and drop interaction. 2. Have a search through the [open and closed issues](https://github.com/atlassian/react-beautiful-dnd/issues?utf8=%E2%9C%93&q=is%3Aissue) to see if the feature you are requesting as already been requested. 3. Have a clear and general purpose keyboard story for any feature request 4. Please [create an issue](https://github.com/atlassian/react-beautiful-dnd/issues/new) to discuss it. **Please do not raise a pull request directly**. There may be reasons why we will not add every feature to this library. ## Large contributions If you are interested in making a large contribution to this library there is some recommended reading / training we suggest. There is a large amount of different libraries, techniques and tools used in `react-beautiful-dnd` and we have created a list with resources about them. Not everything in the list will be applicable to everyone. But it is a great reference and starting point for those who do not know where to start. The online courses listed are no free - feel free to seek out alternatives if you want to. We recommend the egghead.io courses because they are quite comprehensive. ### General knowledge - [You Don't Know JS](https://github.com/getify/You-Dont-Know-JS): This is an amazing resource that I recommend all the time. It is great for having a deeper understanding of the JavaScript language. ### Technologies #### `React` This is a `React` project so getting familiar with `React` is a must! - [`react`](https://facebook.github.io/react/) - [An intro to using React](https://egghead.io/courses/start-using-react-to-build-web-applications) #### `Redux` This project uses `redux` for its state management. If you have not used `redux` before it is worth getting familiar with it. - [`redux`](http://redux.js.org/docs/introduction/): the library itself has really great docs - [Getting Started with Redux](https://egghead.io/courses/getting-started-with-redux): the whole course - [Building React Applications with Idiomatic Redux](https://egghead.io/courses/building-react-applications-with-idiomatic-redux): no need to do the lessons on react router or data fetching - [`react-redux`](https://github.com/reactjs/react-redux): `react` bindings for `redux` - [`reselect`](https://github.com/reactjs/reselect): we use `reselect` heavily to ensure that state selectors are as fast as they can be. Please have a read of its main page, especially the [sharing Selectors with Props Across Multiple Components](https://github.com/reactjs/reselect#sharing-selectors-with-props-across-multiple-components) section. #### Testing We test our application very thoroughly. Changes will not be accepted without tests - [`jest`](https://facebook.github.io/jest/): We use the jest test runner. It is worth getting familiar with it - [Test JavaScript with Jest](https://egghead.io/lessons/javascript-test-javascript-with-jest) - [React Testing Cookbook](https://egghead.io/courses/react-testing-cookbook) #### Performance Performance is **critical** to this project. Please get familiar with React performance considerations. Changes that break core performance characteristics will not be accepted. - [Performance optimisations for React applications](https://medium.com/@alexandereardon/performance-optimisations-for-react-applications-b453c597b191) - [Performance optimisations for React applications round 2](https://medium.com/@alexandereardon/performance-optimisations-for-react-applications-round-2-2042e5c9af97) - [React performance tools](https://facebook.github.io/react/docs/perf.html) - [React performance documentation](https://facebook.github.io/react/docs/optimizing-performance.html) - [React is slow, React is fast](https://marmelab.com/blog/2017/02/06/react-is-slow-react-is-fast.html) #### Flow This codebase is typed with [`flow`](https://flow.org/). Changes will not be merged without correct flow typing. If you are not sure about a particular use case let flow break the build and it can be discussed in the pull request. - [`flow`](https://flow.org/en/docs/getting-started/): the `flow` docs are great - [Up and Running with Facebook Flow for Typed JavaScript](https://egghead.io/lessons/javascript-up-and-running-with-facebook-flow-for-typed-javascript): a small primer for running flow ### Drag and drop problem space #### HTML5 drag and drop How this library performs dragging is an implementation detail. The api is what users interact with. That said, this library does not use the html5 drag and drop api. The main reason is that html5 drag and drop does not allow the level of control we need to create our powerful and beautiful experiences. I could go into detail but this is not the right forum. Here is some general reading about html5 drag and drop. It is worth having a read to get familiar with its ideas and api - [HTML5 drag and drop api](https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API) - [HTML5 Rocks - dnd basics](https://www.html5rocks.com/en/tutorials/dnd/basics/) - [The HTML5 drag and drop disaster](https://www.quirksmode.org/blog/archives/2009/09/the_html5_drag.html) - [HTML5 drag and drop browser inconsistencies](http://mereskin.github.io/dnd/) #### Prior work It is worth looking at other libraries out there to see how they do drag and drop. Things to look at is their philosophy and api. `react-beautiful-dnd` is an opinionated, higher level abstraction than most drag and drop libraries. We do not need to support every use case. We need to find the right level of control while still maintaining a beautiful experience for the user, flexibility of use and a clean, powerful api. - [`react-dnd`](https://react-dnd.github.io/react-dnd/) - `react-beautiful-dnd` draws a fair amount of inspiration from `react-dnd`. Something to keep in mind is that `react-dnd` is designed to provide a set of drag and drop primitives which is a different set of goals to this project. - [`react-sortable-hoc`](https://github.com/clauderic/react-sortable-hoc/) - on the surface this library looks similar to `react-beautiful-dnd`. I created a [comparison blog](https://medium.com/@alexandereardon/thanks-for-reaching-out-dimitar-nestorov-8c0bf9abe19) that explains the differences - [`jQuery sortable`](http://jqueryui.com/sortable/) - the king of drag and drop for a long time ================================================ FILE: LICENSE ================================================ Copyright 2019 Atlassian Pty Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: README.md ================================================ ## 🔒 Archived This project is now [archived](https://docs.github.com/en/repositories/archiving-a-github-repository/archiving-repositories) and is [deprecated on `npm`](https://www.npmjs.com/package/react-beautiful-dnd). If you are still using `react-beautiful-dnd`, we have put together some [resources to help you move forward](https://github.com/atlassian/react-beautiful-dnd/issues/2672). To see our ongoing work in the drag and drop problem space, head to [Pragmatic drag and drop](https://github.com/atlassian/pragmatic-drag-and-drop). We are so grateful to everybody who contributed in big and small ways to this project. Cheers
---

react beautiful dnd logo

react-beautiful-dnd (rbd)

**Beautiful** and **accessible** drag and drop for lists with [`React`](https://facebook.github.io/react/) [![CircleCI branch](https://img.shields.io/circleci/project/github/atlassian/react-beautiful-dnd/master.svg)](https://circleci.com/gh/atlassian/react-beautiful-dnd/tree/master) [![npm](https://img.shields.io/npm/v/react-beautiful-dnd.svg)](https://www.npmjs.com/package/react-beautiful-dnd) ![quote application example](https://user-images.githubusercontent.com/2182637/53614150-efbed780-3c2c-11e9-9204-a5d2e746faca.gif) [Play with this example if you want!](https://react-beautiful-dnd.netlify.app/iframe.html?selectedKind=board&selectedStory=simple)
## Core characteristics - Beautiful and [natural movement](/docs/about/animations.md) of items 💐 - [Accessible](/docs/about/accessibility.md): powerful keyboard and screen reader support ♿️ - [Extremely performant](/docs/support/media.md) 🚀 - Clean and powerful api which is simple to get started with - Plays extremely well with standard browser interactions - [Unopinionated styling](/docs/guides/preset-styles.md) - No creation of additional wrapper dom nodes - flexbox and focus management friendly! ## Get started 👩‍🏫 We have created [a free course on `egghead.io` 🥚](https://egghead.io/courses/beautiful-and-accessible-drag-and-drop-with-react-beautiful-dnd) to help you get started with `react-beautiful-dnd` as quickly as possible. [![course-logo](https://user-images.githubusercontent.com/2182637/43372837-8c72d3f8-93e8-11e8-9d92-a82adde7718f.png)](https://egghead.io/courses/beautiful-and-accessible-drag-and-drop-with-react-beautiful-dnd) ## Currently supported feature set ✅ - Vertical lists ↕ - Horizontal lists ↔ - Movement between lists (▤ ↔ ▤) - [Virtual list support 👾](/docs/patterns/virtual-lists.md) - unlocking 10,000 items @ 60fps - [Combining items](/docs/guides/combining.md) - Mouse 🐭, keyboard 🎹♿️ and touch 👉📱 (mobile, tablet and so on) support - [Multi drag support](/docs/patterns/multi-drag.md) - Incredible screen reader support ♿️ - we provide an amazing experience for english screen readers out of the box 📦. We also provide complete customisation control and internationalisation support for those who need it 💖 - [Conditional dragging](/docs/api/draggable.md#optional-props) and [conditional dropping](/docs/api/droppable.md#conditionally-dropping) - Multiple independent lists on the one page - Flexible item sizes - the draggable items can have different heights (vertical lists) or widths (horizontal lists) - [Add and remove items during a drag](/docs/guides/changes-while-dragging.md) - Compatible with semantic `` reordering - [table pattern](/docs/patterns/tables.md) - [Auto scrolling](/docs/guides/auto-scrolling.md) - automatically scroll containers and the window as required during a drag (even with keyboard 🔥) - Custom drag handles - you can drag a whole item by just a part of it - Able to move the dragging item to another element while dragging (clone, portal) - [Reparenting your ``](/docs/guides/reparenting.md) - [Create scripted drag and drop experiences 🎮](/docs/sensors/sensor-api.md) - Allows extensions to support for [any input type you like 🕹](/docs/sensors/sensor-api.md) - 🌲 Tree support through the [`@atlaskit/tree`](https://atlaskit.atlassian.com/packages/confluence/tree) package - A `` list can be a scroll container (without a scrollable parent) or be the child of a scroll container (that also does not have a scrollable parent) - Independent nested lists - a list can be a child of another list, but you cannot drag items from the parent list into a child list - Server side rendering (SSR) compatible - see [resetServerContext()](/docs/api/reset-server-context.md) - Plays well with [nested interactive elements](/docs/api/draggable.md#interactive-child-elements-within-a-draggable-) by default ## Motivation 🤔 `react-beautiful-dnd` exists to create beautiful drag and drop for lists that anyone can use - even people who cannot see. For a good overview of the history and motivations of the project you can take a look at these external resources: - 📖 [Rethinking drag and drop](https://medium.com/@alexandereardon/rethinking-drag-and-drop-d9f5770b4e6b) - 🎧 [React podcast: fast, accessible and beautiful drag and drop](https://reactpodcast.simplecast.fm/17) ## Not for everyone ✌️ There are a lot of libraries out there that allow for drag and drop interactions within React. Most notable of these is the amazing [`react-dnd`](https://github.com/react-dnd/react-dnd). It does an incredible job at providing a great set of drag and drop primitives which work especially well with the [wildly inconsistent](https://www.quirksmode.org/blog/archives/2009/09/the_html5_drag.html) html5 drag and drop feature. `react-beautiful-dnd` is a higher level abstraction specifically built for lists (vertical, horizontal, movement between lists, nested lists and so on). Within that subset of functionality `react-beautiful-dnd` offers a powerful, natural and beautiful drag and drop experience. However, it does not provide the breadth of functionality offered by `react-dnd`. So `react-beautiful-dnd` might not be for you depending on what your use case is. ## Documentation 📖 ### About 👋 - [Installation](/docs/about/installation.md) - [Examples and samples](/docs/about/examples.md) - [Get started](https://egghead.io/courses/beautiful-and-accessible-drag-and-drop-with-react-beautiful-dnd) - [Design principles](/docs/about/design-principles.md) - [Animations](/docs/about/animations.md) - [Accessibility](/docs/about/accessibility.md) - [Browser support](/docs/about/browser-support.md) ### Sensors 🔉 > The ways in which somebody can start and control a drag - [Mouse dragging 🐭](/docs/sensors/mouse.md) - [Touch dragging 👉📱](/docs/sensors/touch.md) - [Keyboard dragging 🎹♿️](/docs/sensors/keyboard.md) - [Create your own sensor](/docs/sensors/sensor-api.md) (allows for any input type as well as scripted experiences) ### API 🏋️‍ ![diagram](https://user-images.githubusercontent.com/2182637/53607406-c8f3a780-3c12-11e9-979c-7f3b5bd1bfbd.gif) - [``](/docs/api/drag-drop-context.md) - _Wraps the part of your application you want to have drag and drop enabled for_ - [``](/docs/api/droppable.md) - _An area that can be dropped into. Contains ``s_ - [``](/docs/api/draggable.md) - _What can be dragged around_ - [`resetServerContext()`](/docs/api/reset-server-context.md) - _Utility for server side rendering (SSR)_ ### Guides 🗺 - [`` responders](/docs/guides/responders.md) - _`onDragStart`, `onDragUpdate`, `onDragEnd` and `onBeforeDragStart`_ - [Combining ``s](/docs/guides/combining.md) - [Common setup issues](/docs/guides/common-setup-issues.md) - [Using `innerRef`](/docs/guides/using-inner-ref.md) - [Setup problem detection and error recovery](/docs/guides/setup-problem-detection-and-error-recovery.md) - [Rules for `draggableId` and `droppableId`s](/docs/guides/identifiers.md) - [Browser focus retention](/docs/guides/browser-focus.md) - [Customising or skipping the drop animation](/docs/guides/drop-animation.md) - [Auto scrolling](/docs/guides/auto-scrolling.md) - [Controlling the screen reader](/docs/guides/screen-reader.md) - [Use the html5 `doctype`](/docs/guides/doctype.md) - [`TypeScript` and `flow`: type information](/docs/guides/types.md) - [Dragging ``s](/docs/guides/dragging-svgs.md) - [Avoiding image flickering](/docs/guides/avoiding-image-flickering.md) - [Non-visible preset styles](/docs/guides/preset-styles.md) - [How we detect scroll containers](/docs/guides/how-we-detect-scroll-containers.md) - [How we use dom events](/docs/guides/how-we-use-dom-events.md) - _Useful if you need to build on top of `react-beautiful-dnd`_ - [Adding ``s during a drag (11.x behaviour)](/docs/guides/changes-while-dragging.md) - _⚠️ Advanced_ - [Setting up Content Security Policy](/docs/guides/content-security-policy.md) ### Patterns 👷‍ - [Virtual lists 👾](/docs/patterns/virtual-lists.md) - [Multi drag](/docs/patterns/multi-drag.md) - [Tables](/docs/patterns/tables.md) - [Reparenting a ``](/docs/guides/reparenting.md) - _Using our cloning API or your own portal_ ### Support 👩‍⚕️ - [Engineering health](/docs/support/engineering-health.md) - [Community and addons](/docs/support/community-and-addons.md) - [Release notes and changelog](https://github.com/atlassian/react-beautiful-dnd/releases) - [Upgrading](/docs/support/upgrading.md) - [Road map](https://github.com/atlassian/react-beautiful-dnd/issues) - [Media](/docs/support/media.md) ## Read this in other languages 🌎 - [![kr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/South-Korea.png) **한글/Korean**](https://github.com/LeeHyungGeun/react-beautiful-dnd-kr) - [![ru](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Russia.png) **На русском/Russian**](https://github.com/vtereshyn/react-beautiful-dnd-ru) - [![pt](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Brazil.png) **Português/Portuguese**](https://github.com/dudestein/react-beautiful-dnd-pt) - [![gr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Greece.png) **Ελληνικά/Greek**](https://github.com/milvard/react-beautiful-dnd-gr) - [![ja](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Japan.png) **日本語/Japanese**](https://github.com/eltociear/react-beautiful-dnd-ja) ## Creator ✍️ Alex Reardon [@alexandereardon](https://x.com/alexandereardon) > Alex is no longer personally maintaining this project. The other wonderful maintainers are carrying this project forward. ## Maintainers - [Daniel Del Core](https://x.com/danieldelcore) - Many other [@Atlassian](https://x.com/Atlassian)'s! ## Collaborators 🤝 - Bogdan Chadkin [@IAmTrySound](https://x.com/IAmTrySound) ================================================ FILE: a11y-audit-parse.js ================================================ /* eslint-disable flowtype/require-valid-file-annotation */ /* eslint-disable import/no-unresolved */ /* eslint-disable global-require */ /* eslint-disable no-console */ try { // I disabled flow for this file because of this line. // Sometimes the file exists, sometimes it doesn't. // It depends on if you have run the accessibility test or not. // Given this conditional I thought it best to simply disable flow for the file const a11yReport = require('./test-reports/lighthouse/a11y.report.json'); const a11yScore = a11yReport.categories.accessibility.score; const a11yScoreFormatted = `${a11yScore ? a11yScore * 100 : 0}%`; console.log('*************************'); console.log('Lighthouse accessibility score: ', a11yScoreFormatted); console.log('*************************'); if (a11yScore === 1) { // success! process.exit(0); } else { // fail build console.log( '\nNOTE: Lighthouse accessibility audit score must be 100% to pass this build step.\n\n', ); process.exit(1); } } catch (e) { console.error(e); process.exit(1); } ================================================ FILE: babel.config.js ================================================ module.exports = { presets: ['@babel/react', '@babel/flow', ['@babel/env', { loose: true }]], plugins: [ '@babel/transform-object-assign', ['@babel/proposal-class-properties', { loose: true }], // used for stripping out the `invariant` messages in production builds 'dev-expression', ], comments: false, }; ================================================ FILE: browser-test-harness.js ================================================ // @flow const childProcess = require('child_process'); const path = require('path'); const waitPort = require('wait-port'); const ports = require('./server-ports'); const storybook = childProcess.spawn(process.execPath, [ path.join('node_modules', '.bin', 'start-storybook'), '-p', `${ports.storybook}`, ]); const cspServer = childProcess.spawn(process.execPath, [ path.join('csp-server', 'start.sh'), `${ports.cspServer}`, ]); process.on('exit', () => { storybook.kill(); cspServer.kill(); }); Promise.all([ waitPort({ host: 'localhost', port: ports.storybook, timeout: 60000, }), waitPort({ host: 'localhost', port: ports.cspServer, timeout: 60000, }), ]) .then(() => { if (!process.argv[2]) { // eslint-disable-next-line no-console console.warn('Started servers but no command supplied to run after'); process.exit(); } const child = childProcess.spawn(process.argv[2], process.argv.slice(3), { stdio: 'inherit', }); process.on('exit', () => { child.kill(); }); child.on('exit', (code) => { process.exit(code); }); }) .catch((error) => { // eslint-disable-next-line no-console console.error('Unable to spin up standalone servers'); // eslint-disable-next-line no-console console.error(error); storybook.kill(); cspServer.kill(); process.exit(1); }); ================================================ FILE: csp-server/.eslintrc.js ================================================ module.exports = { rules: { // allowing console.warn / console.error // this is because we often mock console.warn and console.error and adding this rul // avoids needing to constantly be opting out of the rule 'no-console': ['error', { allow: ['warn', 'error'] }], // allowing useMemo and useCallback in tests 'no-restricted-imports': 'off', // Allowing Array.from 'no-restricted-syntax': 'off', }, }; ================================================ FILE: csp-server/app.jsx ================================================ // disabling flowtype to keep this example super simple // It matches /* eslint-disable flowtype/require-valid-file-annotation */ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { DragDropContext, Droppable, Draggable } from '../src'; // fake data generator const getItems = (count) => Array.from({ length: count }, (v, k) => k).map((k) => ({ id: `item-${k}`, content: `item ${k}`, })); // a little function to help us with reordering the result const reorder = (list, startIndex, endIndex) => { const result = Array.from(list); const [removed] = result.splice(startIndex, 1); result.splice(endIndex, 0, removed); return result; }; export default class App extends Component { constructor(props, context) { super(props, context); this.state = { items: getItems(10), cspErrors: [], }; this.onDragEnd = this.onDragEnd.bind(this); } componentDidMount() { document.addEventListener( 'securitypolicyviolation', this.onSecurityPolicyViolation, ); } componentWillUnmount() { document.removeEventListener( 'securitypolicyviolation', this.onSecurityPolicyViolation, ); } onDragEnd(result) { // dropped outside the list if (!result.destination) { return; } const items = reorder( this.state.items, result.source.index, result.destination.index, ); this.setState({ items, }); } onSecurityPolicyViolation = (e) => { this.setState((state) => { return { cspErrors: [...state.cspErrors, e] }; }); }; // Normally you would want to split things out into separate components. // But in this example everything is just done in one place for simplicity render() { return (

Content-Security-Policy error count:{' '} {this.state.cspErrors.length}

{(droppableProvided) => (
{this.state.items.map((item, index) => ( {(draggableProvided) => (
{item.content}
)}
))} {droppableProvided.placeholder}
)}
); } } App.propTypes = { nonce: PropTypes.string, }; ================================================ FILE: csp-server/client.js ================================================ // @flow /* eslint-env browser */ import React from 'react'; import { hydrate } from 'react-dom'; import Sample from './app'; const root: Element | null = document.getElementById('root'); let nonce = null; const cspEl = document.getElementById('csp-nonce'); if (cspEl) { nonce = cspEl.getAttribute('content'); } if (root) { hydrate(, root); } ================================================ FILE: csp-server/main.js ================================================ /* eslint-disable id-length */ // @flow const webpack = require('webpack'); const requireFromString = require('require-from-string'); const MemoryFS = require('memory-fs'); const path = require('path'); const config = require('./webpack.config'); const ports = require('../server-ports'); const fs = new MemoryFS(); const compiler = webpack(config); // $ExpectError compiler.outputFileSystem = fs; // $ExpectError const outputPath = compiler.compilers.find((cfg) => cfg.name === 'client') .outputPath; compiler.run(() => { const content = fs.readFileSync(path.resolve(outputPath, 'server.js')); const server = requireFromString(content.toString()).default; server(process.argv[2] || ports.cspServer, outputPath, fs); }); ================================================ FILE: csp-server/server.js ================================================ // @flow import express from 'express'; import React from 'react'; import { renderToString } from 'react-dom/server'; import { resolve } from 'path'; import App from './app'; import { resetServerContext } from '../src'; let count = 0; function getNonce(): string { return `ThisShouldBeACryptographicallySecurePseudoRandomNumber-${count++}`; } function renderHtml(policy?: string, nonce?: string) { resetServerContext(); let meta = ''; if (nonce) { meta += ``; } if (policy) { meta += ``; } return `${meta}
${renderToString( , )}
`; } export default (port: string, outputPath: string, fs: any) => { const server = express(); server.get('/client.js', (req, res) => { res.header('content-type', 'text/javascript'); res.end(fs.readFileSync(resolve(outputPath, 'client.js'))); }); function render(res: any, policy?: string, nonce?: string) { if (policy) { res.header('Content-Security-Policy', policy); } res.header('content-type', 'text/html'); res.end(renderHtml(policy, nonce)); } server.get('/', (req, res) => { render(res); }); server.get('/unsafe-inline', (req, res) => { render(res, "style-src 'unsafe-inline'"); }); server.get('/nonce', (req, res) => { const nonce = getNonce(); render(res, `style-src 'nonce-${nonce}'`, nonce); }); server.get('/wrong-nonce', (req, res) => { const nonce = getNonce(); const wrongNonce = getNonce(); render(res, `style-src 'nonce-${nonce}'`, wrongNonce); }); server.listen(port, () => { // eslint-disable-next-line no-console console.log('csp server listening on port', port); }); }; ================================================ FILE: csp-server/start.sh ================================================ #!/usr/bin/env node require('./main'); ================================================ FILE: csp-server/webpack.config.js ================================================ // @flow const path = require('path'); const common = { context: path.resolve(__dirname, '..'), mode: 'development', entry: path.resolve(__dirname, 'main.js'), target: 'web', output: { filename: 'client.js', path: path.resolve(__dirname, 'dist'), }, resolve: { extensions: ['.ts', '.tsx', '.js', '.jsx', '.mjs'], }, module: { rules: [ { test: /\.(png|jpg|gif)$/i, use: [ { loader: 'url-loader', }, ], }, { test: /jsx?$/, exclude: [/node_modules/], use: [ { loader: 'babel-loader', }, ], }, ], }, externals: [ { express: 'express', fs: 'fs', 'convert-source-map': 'convert-source-map', }, ], }; module.exports = [ { ...common, entry: path.resolve(__dirname, 'client.js'), name: 'client' }, { ...common, entry: path.resolve(__dirname, 'server.js'), name: 'server', target: 'node', output: { filename: 'server.js', path: path.resolve(__dirname, 'dist'), libraryTarget: 'commonjs2', }, }, ]; ================================================ FILE: cypress/.eslintrc.js ================================================ module.exports = { plugins: ['cypress'], env: { 'cypress/globals': true, }, extends: ['plugin:cypress/recommended'], rules: { // Allowing Array.from 'no-restricted-syntax': 'off', // not using jest expects 'jest/expect-expect': 'off', }, }; ================================================ FILE: cypress/fixtures/.gitkeep ================================================ ================================================ FILE: cypress/integration/content-security-policy.spec.js ================================================ // @flow import * as keyCodes from '../../src/view/key-codes'; import { timings } from '../../src/animation'; import { getHandleSelector } from './util'; import ports from '../../server-ports'; function commonTest(url: string, cspTest: string) { cy.visit(url); cy.get(getHandleSelector()).eq(0).as('first').should('contain', 'item 0'); cy.get(getHandleSelector()).eq(1).should('contain', 'item 1'); // reorder operation cy.get('@first') .focus() .trigger('keydown', { keyCode: keyCodes.space }) // need to re-query for a clone .get('@first') .trigger('keydown', { keyCode: keyCodes.arrowDown, force: true }) // finishing before the movement time is fine - but this looks nice .wait(timings.outOfTheWay * 1000) .trigger('keydown', { keyCode: keyCodes.space, force: true }); // order now 2, 1 // note: not using get aliases as they where returning incorrect results cy.get(getHandleSelector()).eq(0).should('contain', 'item 1'); cy.get(getHandleSelector()).eq(1).should('contain', 'item 0'); // element should maintain focus post drag cy.focused().should('contain', 'item 0'); cy.get('#cspErrors').should(cspTest, '0'); } describe('content security policy', () => { it('should reorder a list without a nonce', () => { commonTest(`http://localhost:${ports.cspServer}`, 'contain'); }); it('should reorder a list with a nonce', () => { commonTest(`http://localhost:${ports.cspServer}/nonce`, 'contain'); }); it('should reorder a list with a wrong nonce', () => { commonTest( `http://localhost:${ports.cspServer}/wrong-nonce`, 'not.contain', ); }); }); ================================================ FILE: cypress/integration/focus.spec.js ================================================ // @flow import * as keyCodes from '../../src/view/key-codes'; import { getHandleSelector, getDraggableSelector } from './util'; describe('focus', () => { it('should not steal focus if not already focused when lifting', () => { cy.visit('/iframe.html?id=board--dragging-a-clone'); // focusing on another handle cy.get(getHandleSelector('1')).focus(); cy.focused().should('contain', 'id:1'); cy.get(getHandleSelector('2')) .as('id:2') .trigger('mousedown', { button: 0 }) .trigger('mousemove', { button: 0, clientX: 200, clientY: 300, force: true, }); // asserting id:2 is now dragging cy.get(getHandleSelector('2')).should( 'have.attr', 'data-is-dragging', 'true', ); // focus not stolen cy.focused().should('contain', 'id:1'); cy.get(getHandleSelector('2')) .trigger('mouseup', { force: true }) // clone will be unmounting during drop .should('not.exist'); // getting post clone handle cy.get(getHandleSelector('2')).should( 'have.attr', 'data-is-dragging', 'false', ); // focus not stolen cy.focused().should('contain', 'id:1'); }); it('should maintain focus if dragging a clone', () => { cy.visit('/iframe.html?id=board--dragging-a-clone'); // focusing on another handle cy.get(getHandleSelector('2')).focus(); cy.focused().should('contain', 'id:2'); cy.get(getHandleSelector('2')).trigger('keydown', { keyCode: keyCodes.space, }); // asserting id:2 is now dragging cy.get(getHandleSelector('2')).should( 'have.attr', 'data-is-dragging', 'true', ); // focus maintained cy.focused().should('contain', 'id:2'); cy.get(getHandleSelector('2')) .trigger('keydown', { keyCode: keyCodes.arrowRight, force: true }) .trigger('keydown', { keyCode: keyCodes.space, force: true }) // clone will be unmounting during drop .should('not.exist'); // getting post clone handle cy.get(getHandleSelector('2')) // no longer dragging .should('have.attr', 'data-is-dragging', 'false') // is in the second column (normally would loose focus moving between lists) .closest(getDraggableSelector('BMO')); // focus maintained cy.focused().should('contain', 'id:2'); }); it('should give focus to a combine target', () => { cy.visit('/iframe.html?id=board--with-combining-and-cloning'); cy.get(getHandleSelector('2')).focus(); cy.focused().should('contain', 'id:2'); cy.get(getHandleSelector('2')).trigger('keydown', { keyCode: keyCodes.space, }); // asserting id:2 is now dragging cy.get(getHandleSelector('2')).should( 'have.attr', 'data-is-dragging', 'true', ); // focus maintained cy.focused().should('contain', 'id:2'); cy.get(getHandleSelector('2')) .trigger('keydown', { keyCode: keyCodes.arrowRight, force: true }) // combining with item:1 .trigger('keydown', { keyCode: keyCodes.arrowUp, force: true }) // dropping .trigger('keydown', { keyCode: keyCodes.space, force: true }) // clone will be unmounting during drop .should('not.exist'); // focus giving to item:1 the combine target cy.focused().should('contain', 'id:1'); }); it('should not give focus to a combine target if source did not have focus at start of drag', () => { cy.visit('/iframe.html?id=board--with-combining-and-cloning'); // focusing on something unrelated to the drag cy.get(getHandleSelector('3')).focus(); cy.get(getHandleSelector('2')).trigger('keydown', { keyCode: keyCodes.space, }); // asserting id:2 is now dragging cy.get(getHandleSelector('2')).should( 'have.attr', 'data-is-dragging', 'true', ); // focus not stolen cy.focused().should('contain', 'id:3'); cy.get(getHandleSelector('2')) .trigger('keydown', { keyCode: keyCodes.arrowRight, force: true }) // combining with item:1 .trigger('keydown', { keyCode: keyCodes.arrowUp, force: true }) // dropping .trigger('keydown', { keyCode: keyCodes.space, force: true }) // clone will be unmounting during drop .should('not.exist'); // focus not given to the combine target cy.focused().should('contain', 'id:3'); }); }); ================================================ FILE: cypress/integration/move-between-lists.spec.js ================================================ // @flow import * as keyCodes from '../../src/view/key-codes'; import { timings } from '../../src/animation'; import { getDroppableSelector, getHandleSelector } from './util'; describe('move between lists', () => { beforeEach(() => { cy.visit('/iframe.html?id=board--simple'); }); it('should move between lists', () => { // first list has item with id:2 cy.get(getDroppableSelector()) .eq(1) .as('first-list') .should('contain', 'id:2'); // second list does not have item with id:2 cy.get(getDroppableSelector()) .eq(2) .as('second-list') .should('not.contain', 'id:2'); cy.get('@first-list') .find(getHandleSelector()) .first() .should('contain', 'id:2') .focus() .trigger('keydown', { keyCode: keyCodes.space }) .trigger('keydown', { keyCode: keyCodes.arrowRight, force: true }) // finishing before the movement time is fine - but this looks nice .wait(timings.outOfTheWay * 1000) .trigger('keydown', { keyCode: keyCodes.space, force: true }); // no longer in the first list cy.get('@first-list').should('not.contain', 'id:2'); // now in the second list cy.get('@second-list').should('contain', 'id:2'); }); }); ================================================ FILE: cypress/integration/reorder-lists.spec.js ================================================ // @flow import * as keyCodes from '../../src/view/key-codes'; import { timings } from '../../src/animation'; import { getHandleSelector } from './util'; describe('reorder lists', () => { beforeEach(() => { cy.visit('/iframe.html?id=board--simple'); }); it('should reorder lists', () => { // order: Jake, BMO cy.get('h4').eq(0).as('first').should('contain', 'Jake'); cy.get('h4').eq(1).should('contain', 'BMO'); // reorder operation cy.get('@first') .closest(getHandleSelector()) .focus() .trigger('keydown', { keyCode: keyCodes.space }) .trigger('keydown', { keyCode: keyCodes.arrowRight, force: true }) // finishing before the movement time is fine - but this looks nice .wait(timings.outOfTheWay * 1000) .trigger('keydown', { keyCode: keyCodes.space, force: true }); // order now 2, 1 // note: not using get aliases as they where returning incorrect results cy.get('h4').eq(0).should('contain', 'BMO'); // index of the drag handle has changed cy.get('h4').eq(1).should('contain', 'Jake'); }); }); ================================================ FILE: cypress/integration/reorder-virtual.spec.js ================================================ // @flow import * as keyCodes from '../../src/view/key-codes'; import { timings } from '../../src/animation'; import { getHandleSelector } from './util'; describe('reorder: virtual', () => { beforeEach(() => { cy.visit('/iframe.html?id=virtual-react-window--list'); }); it('should reorder within a list', () => { const movements: number = 12; cy.get(getHandleSelector()).first().as('item'); cy.get('@item').invoke('attr', 'data-testid').as('item-id'); cy.get('@item') .invoke('attr', 'data-index') .as('item-index') .should('equal', '0'); // lift cy.get('@item') .focus() .trigger('keydown', { keyCode: keyCodes.space }) // need to re-query for a clone .get('@item'); cy.wrap(Array.from({ length: movements })).each(() => { cy.get('@item') .trigger('keydown', { keyCode: keyCodes.arrowDown, force: true }) // finishing before the movement time is fine - but this looks nice // waiting longer than we should (timings.outOfTheWay * 1000) as electron is being strange .wait(timings.outOfTheWay * 1000 * 2); }); // drop cy.get('@item').trigger('keydown', { keyCode: keyCodes.space, force: true, }); // This is setting up a chain of commands and this test will not wait // for a 'promise' to resolve. Linting is getting confused by .then // eslint-disable-next-line jest/valid-expect-in-promise cy.get('@item-id').then((id) => { cy.get(getHandleSelector(id)) .invoke('attr', 'data-index') .should('equal', `${movements}`); }); }); }); ================================================ FILE: cypress/integration/reorder.spec.js ================================================ // @flow import * as keyCodes from '../../src/view/key-codes'; import { timings } from '../../src/animation'; import { getHandleSelector } from './util'; describe('reorder', () => { beforeEach(() => { cy.visit('/iframe.html?id=single-vertical-list--basic'); }); it('should reorder within a list', () => { // order: 1, 2 cy.get(getHandleSelector()).eq(0).as('first').should('contain', 'id:1'); cy.get(getHandleSelector()).eq(1).should('contain', 'id:2'); // reorder operation cy.get('@first') .focus() .trigger('keydown', { keyCode: keyCodes.space }) // need to re-query for a clone .get('@first') .trigger('keydown', { keyCode: keyCodes.arrowDown, force: true }) // finishing before the movement time is fine - but this looks nice .wait(timings.outOfTheWay * 1000) .trigger('keydown', { keyCode: keyCodes.space, force: true }); // order now 2, 1 // note: not using get aliases as they where returning incorrect results cy.get(getHandleSelector()).eq(0).should('contain', 'id:2'); cy.get(getHandleSelector()).eq(1).should('contain', 'id:1'); // element should maintain focus post drag cy.focused().should('contain', 'id:1'); }); }); ================================================ FILE: cypress/integration/util.js ================================================ // @flow import * as dataAttr from '../../src/view/data-attributes'; export function getDroppableSelector(droppableId?: string) { if (droppableId) { return `[${dataAttr.droppable.id}="${droppableId}"]`; } return `[${dataAttr.droppable.id}]`; } export function getHandleSelector(draggableId?: string) { if (draggableId) { return `[${dataAttr.dragHandle.draggableId}="${draggableId}"]`; } return `[${dataAttr.dragHandle.draggableId}]`; } export function getDraggableSelector(draggableId?: string) { if (draggableId) { return `[${dataAttr.draggable.id}="${draggableId}"]`; } return `[${dataAttr.draggable.id}]`; } ================================================ FILE: cypress/plugins/index.js ================================================ /* eslint-disable no-unused-vars */ /* eslint-disable flowtype/require-valid-file-annotation */ // *********************************************************** // This example plugins/index.js can be used to load plugins // // You can change the location of this file or turn off loading // the plugins file with the 'pluginsFile' configuration option. // // You can read more here: // https://on.cypress.io/plugins-guide // *********************************************************** // This function is called when a project is opened or re-opened (e.g. due to // the project's config changing) module.exports = (on, config) => { // `on` is used to hook into various events Cypress emits // `config` is the resolved Cypress config }; ================================================ FILE: cypress/support/commands.js ================================================ // @flow // *********************************************** // This example commands.js shows you how to // create various custom commands and overwrite // existing commands. // // For more comprehensive examples of custom // commands please read more here: // https://on.cypress.io/custom-commands // *********************************************** // // // -- This is a parent command -- // Cypress.Commands.add("login", (email, password) => { ... }) // // // -- This is a child command -- // Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) // // // -- This is a dual command -- // Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) // // // -- This is will overwrite an existing command -- // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })// @flow ================================================ FILE: cypress/support/index.js ================================================ // @flow // *********************************************************** // This example support/index.js is processed and // loaded automatically before your test files. // // This is a great place to put global configuration and // behavior that modifies Cypress. // // You can change the location of this file or turn off // automatically serving support files with the // 'supportFile' configuration option. // // You can read more here: // https://on.cypress.io/configuration // *********************************************************** // Import commands.js using ES2015 syntax: import './commands'; // Alternatively you can use CommonJS syntax: // require('./commands') ================================================ FILE: cypress.json ================================================ { "baseUrl": "http://localhost:9002" } ================================================ FILE: docs/about/accessibility.md ================================================ # Accessibility ♿️ Traditionally drag and drop interactions have been exclusively a mouse or touch interaction. This library has invested a huge amount of effort to ensure that everybody has access to drag and drop interactions ## What we do to include everyone - [Full keyboard support](/docs/sensors/keyboard.md) (reordering, combining, moving between lists) - [Keyboard multi drag support](/docs/patterns/multi-drag.md) - Keyboard [auto scrolling](/docs/guides/auto-scrolling.md) - Fantastic [screen reader support](/docs/guides/screen-reader.md) - _We ship with english messaging out of the box 📦_ - Smart management of [browser focus](/docs/guides/browser-focus.md) - A [Google lighthouse](https://developers.google.com/web/tools/lighthouse) automated build to ensure perfect accessibility scores (at least according to [Google](https://developers.google.com/web/tools/lighthouse/v3/scoring#a11y)) ![screen-reader-text](https://user-images.githubusercontent.com/2182637/36571009-d326d82a-1888-11e8-9a1d-e44f8b969c2f.gif) > Example screen reader announcement [← Back to documentation](/README.md#documentation-) ================================================ FILE: docs/about/animations.md ================================================ # Carefully designed animations With things moving a lot it would be easy for the user to become distracted by the animations or for them to get in the way. We have tweaked the various animations to ensure the right balance of guidance, performance and interactivity. ## Dropping We have designed a drop animation that feels weighted and physical. It is based on a [`spring`](https://developer.android.com/guide/topics/graphics/spring-animation) and uses a CSS animation with a dynamic duration to achieve the effect. ![result-curve](https://user-images.githubusercontent.com/2182637/48235467-1ce34200-e412-11e8-8c69-2060a0c2f61a.png) > Animation curve used when dropping. Duration is dynamic based on distance to travel You can tweak the drop animation if you would like to. We have created a guide: [drop animation](/docs/guides/drop-animation.md) ## Moving out of the way Items that are moving out of the way of a dragging item do so with a CSS transition rather than physics. This is to maximise performance by allowing the GPU to handle the movement. The CSS animation curve has been designed to communicate getting out of the way. How it is composed: 1. A warm up period to mimic a natural response time 2. A small phase to quickly move out of the way 3. A long tail so that people can read any text that is being animated in the second half of the animation ![animation curve](https://raw.githubusercontent.com/alexreardon/files/master/resources/dnd-ease-in-out-small.png?raw=true) > Animation curve used when moving out of the way [← Back to documentation](/README.md#documentation-) ================================================ FILE: docs/about/browser-support.md ================================================ # Browser support 🌍 This library supports the standard [Atlassian supported browsers](https://confluence.atlassian.com/cloud/supported-browsers-744721663.html) for desktop: | Desktop | Version | | ------------------------------------ | ---------------------------------------------------- | | Microsoft Internet Explorer(Windows) | Version 11 | | Microsoft Edge | Latest stable version supported | | Mozilla Firefox (all platforms) | Latest stable version supported | | Google Chrome (Windows and Mac) | Latest stable version supported | | Safari (Mac) | Latest stable version on latest OS release supported | | Mobile | Version | | ------------------------ | --------------------------------------------------------- | | Chrome (Android and iOS) | Latest stable version supported | | Mobile Safari (iOS) | Latest stable version supported | | Android (Android) | The default browser on Android 4.0.3 (Ice Cream Sandwich) | [← Back to documentation](/README.md#documentation-) ================================================ FILE: docs/about/design-principles.md ================================================ # Design principles 📖 This page goes over the design and interaction thinking behind `react-beautiful-dnd`. ## Foundational idea: physicality The core design idea of `react-beautiful-dnd` is physicality: we want users to feel like they are moving physical objects around ### Application 1: no instant movement (no snapping) It is a fairly standard drag and drop pattern for things to disappear and reappear in response to the users drag. For a more natural drag we animate the movement of items as they need to move out of the way while dragging to more clearly show a drags effect. We also animate the drop of an item so that it animates into its new home position. At no point is an item instantly moved anywhere — regardless of whether it is dragging or not. ### Application 2: knowing when to move It is quite common for drag and drop interactions to be based on the position that user started the drag from. In `react-beautiful-dnd` a dragging items impact is based on its centre of gravity — regardless of where a user grabs an item from. A dragging items impact follows similar rules to a set of scales ⚖️. Here are some rules that are followed to allow for a natural drag experience even with items of flexible height: - A list is _dragged over_ when the centre position of a dragging item goes over one of the boundaries of the list - A resting drag item will move out of the way of a dragging item when the centre position of the dragging item goes over the edge of the resting item. Put another way: once the centre position of an item (A) goes over the edge of another item (B), B moves out of the way. ### Application 3: movement to communicate positioning > No support for drop shadows or line markings > _Drop shadow: putting a clone or 'shadow' of the dragging item in the drop location_ `react-beautiful-dnd` relies on movement to communicate positioning. It is trying to create a system that is based on physical metaphores. Drop shadows, lines and other affordances are useful in drag and drop contexts where natural movement is not possible. Drop shadows pose a number of confusing design moments if combined with a natural movement system, including: - Where is the shadow when you are not over a list? - How should it move between items? - How should it appear as you enter a new list? The answer to these is often: snapping (where something just appears in the right spot). We are trying hard to avoid any snapping as it breaks the physicality we are trying to model. ### Application 4: maximise interactivity `react-beautiful-dnd` works really hard to avoid as many periods of non-interactivity as possible. The user should feel like they are in control of the interface and not waiting for an animation to finish before they can continue to interact with the interface. However, there is a balance that needs to be made between correctness and power in order to make everybody's lives more sane. Here are the only situations where some things are not interactive: 1. From when a user cancels a drag to when the drop animation completes. On cancel there are lots of things moving back to where they should be. If you grab an item in a location that is not its true home then the following drag will be incorrect. 2. Starting a drag on an item that is animating its own drop. For simplicity this is the case - it is actually quite hard to grab something while it is animating home. It could be coded around - but it seems like an edge case that would add a lot of complexity. Keep in mind that these periods of inactivity may not always exist. ### Application 5: no drag axis locking For now, the library does not support drag axis locking (aka drag rails). This is where the user is restricted to only dragging along one axis. The current thinking is this breaks the physical metaphor we are going for and sends a message to the user that they are interacting with a piece of software rather than moving physical objects around. It is possible to ensure that a user can only drop in a single list by using props `type` and `isDropDisabled`. You can also do some visual treatment to the list `onDragStart` to show the user that this is the only place they can interact with. ### Application 6: natural cross list movement Rather than using an index based approach for keyboard movement between lists, `react-beautiful-dnd` performs cross list movement based on **inertia, gravity and collisions**. You can find out more about how this works by reading the blog ["Natural keyboard movement between lists"](https://medium.com/@alexandereardon/friction-gravity-and-collisions-3adac3a94e19). ![example](https://raw.githubusercontent.com/alexreardon/files/master/resources/collision.gif?raw=true) [← Back to documentation](/README.md#documentation-) ================================================ FILE: docs/about/examples.md ================================================ # Examples 🎉 See how beautiful it is for yourself! ## Viewing on a desktop [All the examples!](https://react-beautiful-dnd.netlify.app) ## Viewing on a mobile or tablet - [Simple list](https://react-beautiful-dnd.netlify.app/iframe.html?id=single-vertical-list--basic) - [Board](https://react-beautiful-dnd.netlify.app/iframe.html?id=board--simple) - best viewed in landscape > We provide different links for touch devices as [storybook](https://github.com/storybooks/storybook) runs examples in an iframe which can result in a strange auto scroll experience ## Basic samples We have created some basic examples on `codesandbox` for you to play with directly: - [Simple vertical list](https://codesandbox.io/s/k260nyxq9v) - [Simple horizontal list](https://codesandbox.io/s/mmrp44okvj) - [Using with function components](https://codesandbox.io/s/zqwz5n5p9x) - [Simple DnD between two lists](https://codesandbox.io/s/ql08j35j3q) - _Community made_ - [Simple DnD between a dynamic number of lists (with function components) and ability to delete items](https://codesandbox.io/s/-w5szl) - _Community made_ [← Back to documentation](/README.md#documentation-) ================================================ FILE: docs/about/installation.md ================================================ # Installation [![module formats: umd, cjs, and esm](https://img.shields.io/badge/module%20formats-umd%2c%20cjs%2c%20esm-green.svg?style=flat)](https://unpkg.com/react-beautiful-dnd/dist/) ## General 1. Add the `react-beautiful-dnd` package ```bash # yarn yarn add react-beautiful-dnd # npm npm install react-beautiful-dnd --save ``` 2. Use the package ```js import { DragDropContext } from 'react-beautiful-dnd'; ``` 3. Profit 🕺 ## `React` environment In order to use `react-beautiful-dnd` you will probably want to have a `React` environment set up. - [Add react to a website](https://reactjs.org/docs/add-react-to-a-website.html) - official `React` docs - [Setup a react environment with `create-react-app`](https://egghead.io/lessons/react-set-up-a-react-environment-with-create-react-app) - from our [free getting started course](https://egghead.io/courses/beautiful-and-accessible-drag-and-drop-with-react-beautiful-dnd) ## Distribution bundle A [universal module definition](https://github.com/umdjs/umd) bundle is published on `npm` under the `/dist` folder for consumption . We publish the following files: - `dist/react-beautiful-dnd.js` - `dist/react-beautiful-dnd.min.js` (minified bundle) These bundles list `react` as an external which needs to be provided. This is done to reduce the size of the bundle and prevent consumers from loading `react` multiple times. You can provide `react` through your module system or simply by having `react` on the `window`. You can use the UMD to run `react-beautiful-dnd` directly in the browser. ```html ``` There is also an [example codepen](https://codepen.io/alexreardon/project/editor/ZyNMPo) you can use to play with this installation method. ## [`ClojureScript`](https://clojurescript.org/) You can consume `react-beautiful-dnd` from within `ClojureScript` using [CLJSJS](https://cljsjs.github.io/)! [← Back to documentation](/README.md#documentation-) ================================================ FILE: docs/api/drag-drop-context.md ================================================ # `` In order to use drag and drop, you need to have the part of your `React` tree that you want to be able to use drag and drop in wrapped in a ``. It is advised to just wrap your entire application in a ``. Having nested ``'s is _not_ supported. You will be able to achieve your desired conditional dragging and dropping using the props of `` and ``. You can think of `` as having a similar purpose to the [react-redux Provider component](https://react-redux.js.org/api/provider). A content-security-protection nonce attribute is added to the injected style tags if provided. ## Props ```js type Responders = {| // optional onBeforeCapture?: OnBeforeCaptureResponder onBeforeDragStart?: OnBeforeDragStartResponder, onDragStart?: OnDragStartResponder, onDragUpdate?: OnDragUpdateResponder, // required onDragEnd: OnDragEndResponder, |}; import type { Node } from 'react'; type Props = {| ...Responders, // We do not technically need any children for this component children: Node | null, // Read out by screen readers when focusing on a drag handle dragHandleUsageInstructions?: string, // Used for strict content security policies nonce?: string, // Used for custom sensors sensors?: Sensor[], enableDefaultSensors?: ?boolean, |}; ``` - `dragHandleUsageInstructions`: What is read out to screen reader users when a _drag handle_ is given browser focus. See our [screen reader guide](/docs/guides/screen-reader.md) - `nonce`: Used for strict content security policy setups. See our [content security policy guide](/docs/guides/content-security-policy.md) - `sensors`: Used to pass in your own `sensor`s for a ``. See our [sensor api documentation](/docs/sensors/sensor-api.md) - `enableDefaultSensors`: Whether or not the default sensors ([mouse](/docs/sensors/mouse.md), [keyboard](/docs/sensors/keyboard.md), and [touch](/docs/sensors/touch.md)) are enabled. You can also import them separately as `useMouseSensor`, `useKeyboardSensor`, or `useTouchSensor` and reuse just some of them via `sensors` prop. See our [sensor api documentation](/docs/sensors/sensor-api.md) > See our [type guide](/docs/guides/types.md) for more details ## Basic usage ### Using a `class` component ```js import React from 'react'; import { DragDropContext } from 'react-beautiful-dnd'; class App extends React.Component { onBeforeCapture = () => { /*...*/ }; onBeforeDragStart = () => { /*...*/ }; onDragStart = () => { /*...*/ }; onDragUpdate = () => { /*...*/ }; onDragEnd = () => { // the only one that is required }; render() { return (
Hello world
); } } ``` ### Using a `function` component ```js import React from 'react'; import { DragDropContext } from 'react-beautiful-dnd'; function App() { // using useCallback is optional const onBeforeCapture = useCallback(() => { /*...*/ }, []); const onBeforeDragStart = useCallback(() => { /*...*/ }, []); const onDragStart = useCallback(() => { /*...*/ }, []); const onDragUpdate = useCallback(() => { /*...*/ }, []); const onDragEnd = useCallback(() => { // the only one that is required }, []); return (
Hello world
); } ``` ## `Responders` > `Responders` were previously known as `Hooks` Responders are top level application events that you can use to perform your own state updates, style updates, as well as to make screen reader announcements. [Please see our Responders guide](/docs/guides/responders.md) for detailed information about responders ❤️ [← Back to documentation](/README.md#documentation-) ================================================ FILE: docs/api/draggable.md ================================================ # `` `` components can be dragged around and dropped onto ``s. A `` must always be contained within a ``. It is **possible** to reorder a `` within its home `` or move to another ``. It is **possible** because a `` is free to control what it allows to be dropped on it. Every `` has a _drag handle_. A _drag handle_ is the element that the user interacts with in order to drag a ``. A _drag handle_ can be the `` element itself, or a child of the ``. Note that by default a _drag handle_ cannot be an interactive element, since [event handlers are blocked on nested interactive elements](#interactive-child-elements-within-a-draggable-). Proper semantics for accessibility are added to the _drag handle_ element. If you wish to use an interactive element, `disableInteractiveElementBlocking` must be set. ```js import { Draggable } from 'react-beautiful-dnd'; {(provided, snapshot) => (

My draggable

)}
; ``` ## Draggable Props ```js import type { Node } from 'react'; type Props = {| // required draggableId: DraggableId, index: number, children: DraggableChildrenFn, // optional isDragDisabled: ?boolean, disableInteractiveElementBlocking: ?boolean, shouldRespectForcePress: ?boolean, |}; ``` ### Required props > `react-beautiful-dnd` will throw an error if a required prop is not provided - `draggableId`: A _required_ `DraggableId(string)`. See our [identifiers guide](/docs/guides/identifiers.md) for more information. - `index`: A _required_ `number` that matches the order of the `` in the ``. It is simply the index of the `` in the list. `index` rule: - Must be unique within a `` (no duplicates) - Must be consecutive. `[0, 1, 2]` and not `[1, 2, 8]` Indexes do not need to start from `0` (this is often the case in [virtual lists](/docs/patterns/virtual-lists.md)). In development mode we will log warnings to the `console` if any of these rules are violated. See [Setup problem detection and error recovery](/docs/guides/setup-problem-detection-and-error-recovery.md) Typically the `index` value will simply be the `index` provided by a `Array.prototype.map` function: ```js { this.props.items.map((item, index) => ( {(provided, snapshot) => (
{item.content}
)}
)); } ``` ### Optional props - `isDragDisabled`: A flag to control whether or not the `` is permitted to drag. You can use this to implement your own conditional drag logic. It will default to `false`. - `disableInteractiveElementBlocking`: A flag to opt out of blocking a drag from interactive elements. For more information refer to the section _Interactive child elements within a ``_ - `shouldRespectForcePress`: Whether or not the _drag handle_ should respect force press interactions. See [Force press](#force-press). ## Children function (render props / function as child) The `React` children of a `` must be a function that returns a `ReactNode`. ```js {(provided, snapshot) => (
Drag me!
)}
``` ```js type DraggableChildrenFn = ( DraggableProvided, DraggableStateSnapshot, DraggableRubric, ) => Node; ``` The function is provided with three arguments: ### 1. provided: (DraggableProvided) ```js type DraggableProvided = {| innerRef: (HTMLElement) => void, draggableProps: DraggableProps, // will be null if the draggable is disabled dragHandleProps: ?DragHandleProps, |}; ``` > For more type information please see [our types guide](/docs/guides/types.md). Everything within the _provided_ object must be applied for the `` to function correctly. - `provided.innerRef (innerRef: (HTMLElement) => void)`: In order for the `` to function correctly, **you must** bind the `innerRef` function to the `ReactElement` that you want to be considered the `` node. We do this in order to avoid needing to use `ReactDOM` to look up your DOM node. > For more information on using `innerRef` see our [using `innerRef` guide](/docs/guides/using-inner-ref.md) #### `innerRef` Example ```js {(provided, snapshot) =>
Drag me!
}
// Note: this will not work directly as we are not applying draggableProps or dragHandleProps ``` - `provided.draggableProps (DraggableProps)`: This is an Object that contains a `data` attribute and an inline `style`. This Object needs to be applied to the same node that you apply `provided.innerRef` to. This controls the movement of the draggable when it is dragging and not dragging. You are welcome to add your own styles to `DraggableProps.style` – but please do not remove or replace any of the properties. #### `draggableProps` type information ```js // Props that can be spread onto the element directly export type DraggableProps = {| // inline style style: ?DraggableStyle, // used for shared global styles 'data-rbd-draggable-context-id': string, 'data-rbd-draggable-id': string, // used to know when a transition ends onTransitionEnd: ?(event: TransitionEvent) => void, |}; ``` > For more type information please see [our types guide](/docs/guides/types.md). #### `draggableProps` Example ```js {(provided, snapshot) => (
Drag me!
)}
// Note: this will not work directly as we are not applying dragHandleProps ``` #### `key`s for a list of `` If you are rendering a list of ``s then it is important that you add a `key` prop to each ``. ```js return items.map((item, index) => ( {(provided, snapshot) => (
{item.content}
)}
)); ``` Rules: - Your `key` needs to be unique within the list - Your `key` should not include the `index` of the item Usually you will want to just use the `draggableId` as the `key` `React` will warn you if your list is missing `keys`. It will not warn you if you are using `index` as a part of your `key`. Not using `keys` correctly will cause really bad times 💥 [`React` docs about `keys`](https://reactjs.org/docs/lists-and-keys.html) #### Positioning ownership It is a contract of this library that it owns the positioning logic of the dragging element. This includes properties such as `top`, `right`, `bottom`, `left` and `transform`. The library may change how it positions things and which properties it uses without performing a major version bump. It is also recommended that you do not apply your own `transition` property to the dragging element. #### Warning: `position: fixed` `react-beautiful-dnd` uses `position: fixed` to position the dragging element. This is quite robust and allows for you to have `position: relative | absolute | fixed` parents. However, unfortunately `position:fixed` is [impacted by `transform`](http://meyerweb.com/eric/thoughts/2011/09/12/un-fixing-fixed-elements-with-css-transforms/) (such as `transform: rotate(10deg);`). This means that if you have a `transform: *` on one of the parents of a `` then the positioning logic will be incorrect while dragging. Lame! For most consumers this will not be an issue. To get around this you can [reparent your ](/docs/guides/reparenting.md). We do not enable this functionality by default as it has performance problems. #### Force press > Safari only In Safari, it is possible for a user to perform a force press action. This is possible with a touch device (`touchforcechange`) and with a mouse (`webkitmouseforcechanged`). We have found that in order to give the most consistent drag and drop experience we need to _opt out_ of force press interactions on a _drag handle_. However, it is possible to have `react-beautiful-dnd` work while also respecting force press interactions. The trade off is that if we register a force press interaction a drag will be cancelled. In order to control this behaviour you set the `shouldRespectForcePress` prop on a ``. By default we set this value to `false` to prevent heavy presses from cancelling a drag. ##### Enabling `shouldRespectForcePress` If you set `shouldRespectForcePress` to `true` then the following will occur: ###### Touch dragging If the user force presses on the element before they have moved the element (even if a drag has already started) then the drag is cancelled and the standard force press action occurs. For an anchor this is a website preview. ###### Mouse dragging Any force press action will cancel an existing or pending drag #### Focus retention See [our focus guide](/docs/guides/browser-focus.md) #### Extending `DraggableProps.style` If you are using inline styles you are welcome to extend the `DraggableProps.style` object. You are also welcome to apply the `DraggableProps.style` object using inline styles and use your own styling solution for the component itself. You can use anything you like to style the `` such as - css-in-js such as [`styled-components`](https://www.styled-components.com) or [`emotion`](https://emotion.sh) - sass, less - vanilla css If you are overriding inline styles be sure to do it after you spread the `provided.draggableProps` or the spread will override your inline style. ```js {(provided, snapshot) => { // extending the DraggableStyle with our own inline styles const style = { ...provided.draggableProps.style, backgroundColor: snapshot.isDragging ? 'blue' : 'white', fontSize: 18, }; return (
Drag me!
); }}
``` #### Unsupported `margin` setups Avoid margin collapsing between ``s. [margin collapsing](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Box_Model/Mastering_margin_collapsing) is one of those really hard parts of CSS. For our purposes, if you have one `` with a `margin-bottom: 10px` and the next `` has a `margin-top: 12px` these margins will _collapse_ and the resulting space between the elements will be the greater of the two: `12px`. When we do our calculations we are currently not accounting for margin collapsing. If you do want to have a margin on the siblings, wrap them both in a `div` and apply the margin to the inner `div` so they are not direct siblings. #### ``s should be visible siblings It is an assumption that ``s are _visible siblings_ of one another. There can be other elements in between, but these elements should not take up any additional space. You probably will not do this anyway, but just calling it out to be super clear. ```js // Direct siblings ✅ {() => {}} {() => {}} // Not direct siblings, but are visible siblings ✅
{() => {}}
{() => {}}
// Spacer elements ❌ {() => {}}

I will break things!

{() => {}} // Spacing on non sibling wrappers ❌
{() => {}}
{() => {}}
``` - `provided.dragHandleProps (?DragHandleProps)` every `` has a _drag handle_. This is what is used to drag the whole ``. Often this will be the same node as the ``, but sometimes it can be a child of the ``. `DragHandleProps` need to be applied to the node that you want to be the drag handle. This is a number of props that need to be applied to the `` node. The simplest approach is to spread the props onto the draggable node (`{...provided.dragHandleProps}`). However, you are also welcome to [monkey patch](https://davidwalsh.name/monkey-patching) these props if you also need to respond to them. DragHandleProps will be `null` when `isDragDisabled` is set to `true`. #### `dragHandleProps` Type information ```js type DragHandleProps = {| // what draggable the handle belongs to 'data-rbd-drag-handle-draggable-id': DraggableId, // What DragDropContext the drag handle is in 'data-rbd-drag-handle-context-id': ContextId, // Id of hidden element that contains the lift instruction (nicer screen reader text) 'aria-labelledby': ElementId, // Allow tabbing to this element tabIndex: number, // Stop html5 drag and drop draggable: boolean, onDragStart: (event: DragEvent) => void, |}; ``` #### `dragHandleProps` Example: standard ```js {(provided, snapshot) => (
Drag me!
)}
``` #### `dragHandleProps` example: custom drag handle Controlling a whole draggable by just a part of it ```js {(provided, snapshot) => (

Hello there

Drag handle
)}
``` ### 2. Snapshot: (DraggableStateSnapshot) ```js type DraggableStateSnapshot = {| // Set to true if a Draggable is being actively dragged, or if it is drop animating // Both active dragging and the drop animation are considered part of the drag // *Generally this is the only property you will be using* isDragging: boolean, // Set to true if a Draggable is drop animating. Not every drag and drop interaction // as a drop animation. There is no drop animation when a Draggable is already in its final // position when dropped. This is commonly the case when dragging with a keyboard isDropAnimating: boolean, // Information about a drop animation dropAnimation: ?DropAnimation // What Droppable (if any) the Draggable is currently over draggingOver: ?DroppableId, // the id of a draggable that you are combining with combineWith: ?DraggableId, // if something else is dragging and you are a combine target, then this is the id of the item that is dragging combineTargetFor: ?DraggableId, // There are two modes that a drag can be in // 'FLUID': everything is done in response to highly granular input (eg mouse) // 'SNAP': items snap between positions (eg keyboard); mode: ?MovementMode, |}; ``` > See our [type guide](/docs/guides/types.md) for more details The `children` function is also provided with a small amount of state relating to the current drag state. This can be optionally used to enhance your component. A common use case is changing the appearance of a `` while it is being dragged. Note: if you want to change the cursor to something like `grab` you will need to add the style to the draggable. (See [Extending `DraggableProps.style`](#extending-draggableprops-style) above) ```js {(provided, snapshot) => { const style = { backgroundColor: snapshot.isDragging ? 'blue' : 'grey', ...provided.draggableProps.style, }; return (
Drag me!
); }}
``` ### 3. rubric: (DraggableRubric) ```js type DraggableRubric = {| draggableId: DraggableId, type: TypeId, source: DraggableLocation, |}; ``` `rubric` represents all of the information associated with a ``. `rubric` is helpful for looking up the data associated with your `` when it is not available in the current scope. This is useful when using the ` | renderClone` API. The `rubric` is the same lookup information that is provided to the [`Responder`s](/docs/guides/responders.md). ## Adding an `onClick` handler to a `` or a _drag handle_ You are welcome to add your own `onClick` handler to a `` or a _drag handle_ (which might be the same element). `onClick` events handlers will always be called if a click occurred. If we are preventing the click, then the `event.defaultPrevented` property will be set to `true`. We prevent click events from occurring when the user was dragging an item. See [sloppy clicks and click prevention](/docs/sensors/mouse.md#sloppy-clicks-and-click-prevention-) for more information. ## Interactive child elements within a `` It is possible for your `` to contain interactive elements. By default we block dragging on these elements. By doing this we allow those elements to function in the usual way. Here is the list of interactive elements that we block dragging from by default: - `input` - `button` - `textarea` - `select` - `option` - `optgroup` - `video` - `audio` - [`contenteditable`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/contenteditable) (any elements that are `contenteditable` or are within a `contenteditable` container) You can opt out of this behavior by adding the `disableInteractiveElementBlocking` prop to a ``. However, it is questionable as to whether you should be doing so because it will render the interactive element unusable. If you need to _conditionally_ block dragging from interactive elements you can add the `disableInteractiveElementBlocking` prop to opt out of the default blocking and monkey patch the `dragHandleProps (DragHandleProps)` event handlers to disable dragging as required. [← Back to documentation](/README.md#documentation-) ================================================ FILE: docs/api/droppable.md ================================================ # `` `` components can be **dropped on by a ``**. They also **contain** ``s. A `` must be contained within a ``. ```js import { Droppable } from 'react-beautiful-dnd'; {(provided, snapshot) => (

I am a droppable!

{provided.placeholder}
)}
; ``` ## Droppable props ```js import type { Node } from 'react'; type Props = {| // required droppableId: DroppableId, // optional type?: TypeId, mode?: DroppableMode, isDropDisabled?: boolean, isCombineEnabled?: boolean, direction?: Direction, ignoreContainerClipping?: boolean, renderClone?: DraggableChildrenFn, getContainerForClone?: () => HTMLElement, children: (DroppableProvided, DroppableStateSnapshot) => Node, |}; type DroppableMode = 'standard' | 'virtual'; type Direction = 'horizontal' | 'vertical'; ``` ### Required props > `react-beautiful-dnd` will throw an error if a required prop is not provided - `droppableId`: A _required_ `DroppableId(string)`. See our [identifiers guide](/docs/guides/identifiers.md) for more information. ### Optional props - `type`: A `TypeId(string)` that can be used to simply accept only the specified class of ``. ``s always inherit type from the `` they are defined in. For example, if you use the type `PERSON` then it will only allow ``s of type `PERSON` to be dropped on itself. ``s of type `TASK` would not be able to be dropped on a `` with type `PERSON`. If no `type` is provided, it will be set to `'DEFAULT'`. - `isDropDisabled`: A flag to control whether or not dropping is currently allowed on the ``. You can use this to implement your own conditional dropping logic. It will default to `false`. - `isCombineEnabled`: A flag to control whether or not _all_ the `Draggables` in the list will be able to be **combined** with. It will default to `false`. - `direction`: The direction in which items flow in this droppable. Options are `vertical` (default) and `horizontal`. - `ignoreContainerClipping`: When a `` is inside a scrollable container its area is constrained so that you can only drop on the part of the `` that you can see. Setting this prop opts out of this behavior, allowing you to drop anywhere on a `` even if it's visually hidden by a scrollable parent. The default behavior is suitable for most cases so odds are you'll never need to use this prop, but it can be useful if you've got very long ``s inside a short scroll container. Keep in mind that it might cause some unexpected behavior if you have multiple ``s inside scroll containers on the same page. - `mode`: `standard` (default) or `virtual`. Used to designate a list as a virtual list. See our [virtual lists pattern](/docs/patterns/virtual-lists.md) - `renderClone`: used to render a clone (replacement) of the dragging `` while a drag is occurring. See our [reparenting guide](/docs/guides/reparenting.md) for usage details. **A clone must be used for [virtual lists](/docs/patterns/virtual-lists.md).** You can use a clone without using virtual lists - `getContainerForClone`: a function that returns the containing element (parent element) for a clone during a drag. See our [reparenting guide](/docs/guides/reparenting.md). ## Children function The `React` children of a `` must be a function that returns a [`ReactElement`](https://tylermcginnis.com/react-elements-vs-react-components/). ```js {(provided, snapshot) => ({ /*...*/ })} ``` The function is provided with two arguments: ### 1. provided: (DroppableProvided) ```js import type { Node } from 'react'; type DroppableProvided = {| innerRef: (?HTMLElement) => void, droppableProps: DroppableProps, placeholder: ?Node, |}; type DroppableProps = {| // used for shared global styles 'data-rbd-droppable-context-id': ContextId, // Used to lookup. Currently not used for drag and drop lifecycle 'data-rbd-droppable-id': DroppableId, |}; ``` - `provided.innerRef`: In order for the droppable to function correctly, **you must** bind the `provided.innerRef` to the highest possible DOM node in the `ReactElement`. We do this in order to avoid needing to use `ReactDOM` to look up your DOM node. > For more information on using `innerRef` see our [using `innerRef` guide](/docs/guides/using-inner-ref.md) - `provided.placeholder`: This is used to create space in the `` as needed during a drag. This space is needed when a user is dragging over a list that is not the home list. Please be sure to put the placeholder inside of the component for which you have provided the ref. We need to increase the size of the `` itself. - `provided.droppableProps (DroppableProps)`: This is an Object that contains properties that need to be applied to a Droppable element. It needs to be applied to the same element that you apply `provided.innerRef` to. It currently contains `data` attributes that we use for styling and lookups. ```js {(provided, snapshot) => (
Good to go {provided.placeholder}
)}
``` ### 2. snapshot: (DroppableStateSnapshot) ```js type DroppableStateSnapshot = {| // Is the Droppable being dragged over? isDraggingOver: boolean, // What is the id of the draggable that is dragging over the Droppable? draggingOverWith: ?DraggableId, // What is the id of the draggable that is dragging from this list? // Useful for styling the home list when not being dragged over draggingFromThisWith: ?DraggableId, // Whether or not the placeholder is actively being used. // This is useful information when working with virtual lists // (See our virtual list pattern) isUsingPlaceholder: boolean, |}; ``` The `children` function is also provided with a small amount of state relating to the current drag state. This can be optionally used to enhance your component. A common use case is changing the appearance of a `` while it is being dragged over. ```js {(provided, snapshot) => (
I am a droppable! {provided.placeholder}
)}
``` ## Combining `react-beautiful-dnd` supports the combining of ``s 🤩 ![combining](https://user-images.githubusercontent.com/2182637/48045145-318dc300-e1e3-11e8-83bd-22c9bd44c442.gif) You can enable a _combining_ mode for a `` by setting `isCombineEnabled` to `true` on a ``. We have created a [combining guide](/docs/guides/combining.md) to help you implement combining in your lists. ## Adding and removing ``s while dragging It is possible to change the ``s in a `` for a limited set of circumstances. We have created a comprehensive [changes while dragging guide](/docs/guides/changes-while-dragging.md) ## Conditionally dropping - ``s can only be dropped on by ``s who share the same `type`. This is a simple way of allowing conditional dropping. If you do not provide a `type` for the ``, then it will only accept ``s which also have the default type. ``s and ``s both will have their `types` set to `'DEFAULT'` when none is provided. There is currently no way to set multiple `types`, or a `type` wildcard that will accept ``s of multiple any types. This could be added if there is a valid use case. - Using the `isDropDisabled` prop you can conditionally allow dropping. This allows you to do arbitrarily complex conditional transitions. This will only be considered if the `type` of the `` matches the `type` of the currently dragging ``. - You can disable dropping on a `` altogether by always setting `isDropDisabled` to `true`. You can do this to create a list that is never able to be dropped on, but contains ``s. - Technically you do not need to use `type` and do all of your conditional drop logic with the `isDropDisabled` function. The `type` parameter is a convenient shortcut for a common use case. ## Scroll containers This library supports dragging within scroll containers (DOM elements that have `overflow: auto;` or `overflow: scroll;`). The **only** supported use cases are: 1. The `` can itself be a scroll container with **no scrollable parents** 2. The `` has **one scrollable parent** where a _scrollable parent_ refers to a scroll container that is not the window itself. For more information see [how we detect scroll containers guide](/docs/guides/how-we-detect-scroll-containers.md) > We currently only support a single scroll parent. We plan on adding support for [nested scroll containers](https://github.com/atlassian/react-beautiful-dnd/issues/131) ## Empty ``s It is recommended that you put a `min-height` on a vertical `` or a `min-width` on a horizontal ``. Otherwise when the `` is empty there may not be enough of a target for `` being dragged with touch or mouse inputs to be _over_ the ``. ## Fixed ``s `react-beautiful-dnd` has partial support for `` lists that use `position: fixed`. When you start a drag and _any_ list of the same type is `position:fixed` then auto window scrolling will be disabled. This is because our virtual model assumes that when the page scroll changes the position of a `` will shift too. If a manual window scroll is detected then the scroll will be aborted. Scroll container scroll is still allowed. We could improve this support, but it would just be a big effort. Please raise an issue if you would be keen to be a part of this effort ❤️ ## Recommended 🏠 home list styling We recommend you style the home list when it is not being dragged over. This makes it easy for a user to see where an item is dragging from. You can use the `snapshot.draggingFromThisWith` value for this. This will be populated in the home list. In this example we set the `background-color` of the home list to `pink` when we are dragging over the list. We set the `background-color` of the home list to `blue` when not dragging over the home list. ![no-placeholder-when-over-no-list](https://user-images.githubusercontent.com/2182637/54155390-251ebd00-4498-11e9-8748-ab441795d19f.gif) ```js const getBackgroundColor = (snapshot: DroppableStateSnapshot): string => { // Giving isDraggingOver preference if (snapshot.isDraggingOver) { return 'pink'; } // If it is the home list but not dragging over if (snapshot.draggingFromThisWith) { return 'blue'; } // Otherwise use our default background return 'white'; }; ``` ## Recommended `` performance optimisation > 📺 This optimisation is covered in a [free lesson of our getting started course](https://egghead.io/lessons/react-optimize-performance-in-react-beautiful-dnd-with-shouldcomponentupdate-and-purecomponent) When a user drags over, or stops dragging over, a `` we re-render the `` with an updated `DroppableStateSnapshot > isDraggingOver` value. This is useful for styling the ``. However, by default this will cause a render of all of the children of the `` - which might be 100's of ``s! This can result in a noticeable frame rate drop. To avoid this problem we recommend that you create a component that is the child of a `` whose responsibility it is to avoid rendering children if it is not required. Here is an example of how you could do this using `class` components: ```js import React, { Component } from 'react'; class Student extends Component<{ student: Person }> { render() { // Renders out a draggable student } } class InnerList extends Component<{ students: Person[] }> { // do not re-render if the students list has not changed shouldComponentUpdate(nextProps: Props) { if (this.props.students === nextProps.students) { return false; } return true; } // You could also not do your own shouldComponentUpdate check and just // extend from React.PureComponent render() { return this.props.students.map((student: Person) => ( )); } } class Students extends Component<{ students: Person[] }> { render() { return ( {(provided: DroppableProvided, snapshot: DroppableStateSnapshot) => (
{provided.placeholder}
)}
); } } ``` Here is an example of how you could do this using `function` components: ```js import React from 'react'; function Student (props: { student: Person }) { // Renders out a draggable student } // do not re-render if the students list reference has not changed const InnerList = React.memo(function InnerList(props: students: Person[]) { return props.students.map((student: Person) => ( )); }); function Students(props: { students: Person[] }) { return ( {(provided: DroppableProvided, snapshot: DroppableStateSnapshot) => (
{/* only re-render if the students array reference changes */} {provided.placeholder}
)}
); } ``` By using the approach you are able to make style changes to a `` when it is being dragged over, but you avoid re-rendering all of the children unnecessarily. When moving into a new list, the visible `Draggables` will have their `render` function called directly even with this optimisation. This is because we need to move those `Draggables` out of the way. The `InnerList` optimisation will prevent the `` from calling `render` on the whole list from the top down. This optimisation will prevent the non-visible `Draggables` from having their render function called. Unfortunately we are [unable to apply this optimisation for you](https://medium.com/merrickchristensen/function-as-child-components-5f3920a9ace9). It is a byproduct of using the render-props pattern. [← Back to documentation](/README.md#documentation-) ================================================ FILE: docs/api/reset-server-context.md ================================================ # `resetServerContext` The `resetServerContext` function should be used when server side rendering (SSR). It ensures context state does not persist across multiple renders on the server which would result in client/server markup mismatches after multiple requests are rendered on the server. Use it before calling the server side render method: ```js import { resetServerContext } from 'react-beautiful-dnd'; import { renderToString } from 'react-dom/server'; // ... resetServerContext(); renderToString(...); ``` [← Back to documentation](/README.md#documentation-) ================================================ FILE: docs/guides/auto-scrolling.md ================================================ # Auto scrolling When a user drags a `` near the edge of a _container_ we automatically scroll the container as we are able to in order make room for the ``. > A _container_ is either a `` that is scrollable or has a scroll parent - or the `window`. | Mouse and touch | Keyboard | | ------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | | ![auto-scroll-mouse](https://user-images.githubusercontent.com/2182637/36520373-c9e2cb7e-17e4-11e8-9e93-4d2389d51fa4.gif) | ![auto-scroll-keyboard](https://user-images.githubusercontent.com/2182637/36520375-cc1aa45c-17e4-11e8-842d-94aed694428a.gif) | It also works in multi list configurations with all input types | Mouse and touch | Keyboard | | ------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | | ![auto-scroll-board-mouse](https://user-images.githubusercontent.com/2182637/36520670-57752526-17e6-11e8-95b3-b5a3978a5312.gif) | ![auto-scroll-board-keyboard](https://user-images.githubusercontent.com/2182637/36520650-3d3638f8-17e6-11e8-9cba-1fb439070285.gif) | ## For mouse and touch inputs 🐭📱 When the center of a `` gets within a small distance from the edge of a container we start auto scrolling. As the user gets closer to the edge of the container we increase the speed of the auto scroll. This acceleration uses an easing function to exponentially increase the rate of acceleration the closer we move towards the edge. We reach a maximum rate of acceleration a small distance from the true edge of a container so that the user does not need to be extremely precise to obtain the maximum scroll speed. This logic applies for any edge that is scrollable. The distances required for auto scrolling are based on a percentage of the height or width of the container for vertical and horizontal scrolling respectively. By using percentages rather than raw pixel values we are able to have a great experience regardless of the size and shape of your containers. ### Mouse wheel and trackpads In addition to auto scrolling we also allow users to scroll the window or a `` manually using their _mouse wheel_ or _trackpad_ 👌 ### A note about big ``s If the `` is bigger than a container on the axis you are trying to scroll - we will not permit scrolling on that axis. For example, if you have a `` that is longer than the height of the window we will not auto scroll vertically. However, we will still permit scrolling to occur horizontally. ### iOS auto scroll shake 📱🤕 When auto scrolling on an iOS browser (webkit) the `` noticeably shakes. This is due to a [bug with webkit](https://bugs.webkit.org/show_bug.cgi?id=181954) that has no known work around. We tried for a long time to work around the issue! If you are interesting in seeing this improved please engage with the [webkit issue](https://bugs.webkit.org/show_bug.cgi?id=181954). ## For keyboard dragging 🎹♿️ We also correctly update the scroll position as required when keyboard dragging. In order to move a `` into the correct position we can do a combination of a `` scroll, `window` scroll and manual movements to ensure the `` ends up in the correct position in response to user movement instructions. This is boss 🔥. This is amazing for users with visual impairments as they can correctly move items around in big lists without needing to use mouse positioning. [← Back to documentation](/README.md#documentation-) ================================================ FILE: docs/guides/avoiding-image-flickering.md ================================================ # Avoiding image flickering > Often all you need to do is close your browsers dev tools! See 'HTTP cache headers below' If your `` has a image inside of it, then you might notice that sometimes it flashes sometimes. Why is that? Some behaviours will cause a `` to be **recreated**. This is where the original DOM element is destroyed, and a new DOM element is created. When the new DOM element is inserted into the DOM then the browser will try to load the image from scratch. Image flashing is caused by the gap between the new element being inserted into the DOM and the image being loaded. These are the actions that can cause a `` to be recreated: - [Reparenting](/docs/guides/reparenting.md) a `` (using the cloning api, or using your own portal) - Moving `` into a new list. React will not shift the original element. It will recreate one. ## How can you prevent image flickering? The big idea is you want to allow the browser to load the image **instantly** when it is recreated. Here are some ways you can do that: ### HTTP cache headers > When you open devtools, it can disable HTTP caching. So simply closing devtools might make your image flickering go away! 🤘 Generally speaking, a browser will not request an image that it already has cached and it will load instantly. You can use the [HTTP cache headers](https://devcenter.heroku.com/articles/increasing-application-performance-with-http-cache-headers) to tell a browser that an image can be cached. Ultimately the browser can decide to re-request the image if it wants to, but that would be an edge case. We put together an [example on Glitch](https://glitch.com/~image-flickering) that shows off the impact of using HTTP cache headers. ### Inline your images [Base64 encode](https://stackoverflow.com/questions/201479/what-is-base-64-encoding-used-for) your images and use that as the source. That way there is no need to talk to the server to get the source. ```diff - + ``` You can use the [webpack `url-loader`](https://github.com/webpack-contrib/url-loader) to help. #### Drawbacks of this approach - If the same image is used in multiple places, they all need to be downloaded independently - The browser cannot defer image loading You will want to keep your image sizes fairly small. ### Really anything else! The big idea is that you don't want to be calling the server to refetch an image that has already been fetched. So anything you can use to do client side caching of images is fine. You could even use [service workers](https://developers.google.com/web/ilt/pwa/caching-files-with-service-worker) if you want! [← Back to documentation](/README.md#documentation-) ================================================ FILE: docs/guides/browser-focus.md ================================================ # Browser focus > "You got to focus on what's real, man" - [Jake from Adventure time](https://www.youtube.com/watch?v=TFGz6Qvg1CE) `react-beautiful-dnd` includes logic to maintain browser focus for _drag handles_. This especially important for [keyboard dragging](/docs/sensors/keyboard.md) which requires the dragging item to be focused. ## Terminology reminder 📖 A `` has a _drag handle_. A _drag handle_ is the part of the `` that controls the dragging of the whole ``. A _drag handle_ can be the same element as the `` ## Drag handle not focused at drag start If the _drag handle_ is not focused when a drag starts then **focus is not given** to the dragging item. This is a mirror of the native HTML5 drag and drop behaviour which does not give focus to an item just because it is dragging. You are welcome to call `HTMLElement.focus()` when a drag starts to give it focus, but that is up to you. ## Drag handle is focused at drag start If a _drag handle_ has browser focus when a drag starts then `rbd` will try to give focus to the _drag handle_ during a drag and just after a drag ends. Here is what is done: - Give focus to a _drag handle_ with a matching `DraggableId` after the drag starts. This might be a different element to the original _drag handle_ if you are [reparenting your ``](/docs/guides/reparenting.md). - Give focus to a _drag handle_ with a matching `DraggableId` after the drag ends. Sometimes the original _drag handle_ element is lost during a drag, such as when [reparenting your ``](/docs/guides/reparenting.md), or when moving a `` from one list to another as `React` will recreate the element. - If [combining](/docs/guides/combining.md) then focus is given to the combine target after a drag ends. This allows keyboard users to continue to engage with the application without needing to get the focus back to where they where the last interaction was ## Browser testing Fun fact: we test this behaviour using [`cypress.io`](http://cypress.io) to ensure that focus management behaves as we expect [← Back to documentation](/README.md#documentation-) ================================================ FILE: docs/guides/changes-while-dragging.md ================================================ # Changes while dragging > For virtual list support see our [virtual list pattern](/docs/patterns/virtual-lists.md) > ❌ **This behaviour is only supported in `11.x`**. We do plan on supporting this type of behaviour again in a future `minor` release. We needed to cut this existing behaviour order to get `12.x` across the line. Going forward, tree behaviour will be supported on the latest version. We know this sucks, but we thought it better to move things forward. `react-beautiful-dnd` supports the addition and removal of ``s during a drag. ## What behaviours does this unlock? ### Lazy loading of list items > In this example we are adding more ``s to a list we scroll closer to the bottom of the list ![lazy-loading 2018-11-01 17_01_21](https://user-images.githubusercontent.com/2182637/47835395-ec8b1a80-ddf7-11e8-88e6-848848ab4af1.gif) ### Collapsing and expanding groups > We recommend you use the [`@atlaskit/tree`](https://atlaskit.atlassian.com/packages/core/tree) component for this behaviour ![hover_to_expand](https://user-images.githubusercontent.com/2182637/45996092-3d637100-c0de-11e8-8837-8d66e7cc73b8.gif) ## Rules > We attempt to print helpful debug information to the `console` if you do not follow these rules in development builds - You are allowed to add or remove `Draggables` during a drag - You can only add or remove `Draggables` that are of the same `type` as the dragging item. - Any changes must occur within a `` that is a _scroll container_ (has `overflow: auto` or `overflow: scroll`). _This is prevent accidental shifts to other `Droppables` on the page_ - The size of the internal content of the _scroll container_ can change, but the outer bounds of the _scroll container_ itself cannot change. - You cannot modify the sizes of any existing `` or `` during a drag - You cannot add or remove a `` during a drag. _We did this to avoid accidental shifting of other ``s_ - When an item is removed or added it must be done instantly. You cannot animate the size of the item. You are welcome to animate a property when adding a `` that does not impact the size of the item, such as `opacity` ## ` > onDragUpdate` behavior - `onDragUpdate` will be called if the `DragUpdate > source > index` of the dragging item has changed as the result of `Draggables` being added or removed before it. - `onDragUpdate` will be called if the `DragUpdate > destination` of the dragging item has changed as a result of the addition or removal. ## ` > onDragEnd` behavior `onDragEnd` will be called with values that are adjusted for any additions or removals of `Draggables` during a drag. This can mean that the `onDragStart: DragStart > source > index` can be different from the `onDragEnd: DropResult > source > index`. ### Sample `onDragEnd` flow > What is important to note is that the `source` property can change during a drag as a result of dynamic changes. 1. A drag starts. `onDragStart` is called with: ```js { draggableId: 'item-1',, type: 'TYPE', source: { droppableId: 'droppable', index: 1, }, } ``` 2. The first `` in the list (`item-0`) is removed. `onDragUpdate` is called with `DragUpdate`: ```diff { draggableId: 'item-1',, type: 'TYPE', source: { droppableId: 'droppable', + // item-1 is now in index 0 as item-0 is gone + index: 0, }, // adjusted destination destination: null, } ``` 3. The drag ends `onDragEnd` is called with `DropResult`: ```diff { draggableId: 'item-1',, type: 'TYPE', source: { droppableId: 'droppable', + // the source reflects the change + index: 0, }, destination: null, reason: 'DROP', } ``` ## Drag end while we are patching the virtual model If a drag ends after a `` has been added or removed, but we have not finished collecting and patching the _virtual dimension model_ then we will delay the drop until the patch is finished. This is usually only a single frame. The `onDropEnd` callback will be called with a `DropResult` that is correct after the patch. [← Back to documentation](/README.md#documentation-) ================================================ FILE: docs/guides/combining.md ================================================ # Combining > 👶 This feature is still quite young. We wanted to get it out there for people to play with `react-beautiful-dnd` supports the combining of ``s 🤩 ![combining](https://user-images.githubusercontent.com/2182637/48045145-318dc300-e1e3-11e8-83bd-22c9bd44c442.gif) > 🌲 If you are looking to build a tree view, we have built one already! [@atlaskit/tree](https://atlaskit.atlassian.com/packages/confluence/tree) ## Setup In order to enable combining you need to set `isCombineEnabled` to `true` on a `` and you are good to go! ```js ... ``` ## Behaviour When `isCombineEnabled` is set on a list _any_ item in the list can be combine with. You can toggle `isCombineEnabled` during a drag. `react-beautiful-dnd` works hard to ensure that users are able to combine and reorder within the same list in a way that feels intuitive and natural. ## Current limitations - No granular control over which items can be combined with within the list. We could move to the `isCombineEnabled` prop from a `` to a `` to allow this sort of customisation. However, in order to ship this huge feature we went a bit simplier to start with - A list must be reorderable to also have items that can be combined with. It is not possible for a list to be 'combine only' at this stage ## `` > `DraggableStateSnapshot` ```diff type DraggableStateSnapshot = {| isDragging: boolean, isDropAnimating: boolean, dropAnimation: ?DropAnimation, draggingOver: ?DroppableId, + combineWith: ?DraggableId, + combineTargetFor: ?DraggableId, mode: ?MovementMode, |}; ``` If you are dragging a `` over another `` in combine mode then the id of the `` being dragged over will be populated in `combineWith` If a `` is being dragged over in combine mode then the id of the `` being dragged will be populated in `combineTargetFor` ## `` > `Responders` `onDragUpdate` and `onDragEnd` will be updated with any changes to a `combine` > See our [type guide](/docs/guides/types.md) for more details ## Persisting a `combine` A `combine` result might signify different operations depending on your problem domain. When combining, a simple operation is to just remove the item that was dragging ```js function onDragEnd(result) { // combining item if (result.combine) { // super simple: just removing the dragging item const items: Quote[] = [...this.state.items]; items.splice(result.source.index, 1); setState({ items }); return; } } ``` ## Drop animation One of the goals of `react-beautiful-dnd` is to create a drag and drop experience that feels physical. This is a bit tricky to achieve in a generic way when it comes to combining two things. What we have gone for out of the box in the following animation: - move the dragging item onto the center of the item being grouped with - fade the opacity of the dragging item down to `0` - scale the dragging item down This animation attempts to communicate one item _moving into_ another item in a fairly generic way. ![combining](https://user-images.githubusercontent.com/2182637/48045145-318dc300-e1e3-11e8-83bd-22c9bd44c442.gif) You are welcome to customise this animation using the information found in our [drop animation guide](/docs/guides/drop-animation.md) [← Back to documentation](/README.md#documentation-) ================================================ FILE: docs/guides/common-setup-issues.md ================================================ # Common setup issues This is a little guide to help you with some common setup issues ## Check your `console` For detectable setup issues we try to log information in the `console` for `development` builds of `react-beautiful-dnd`. If things are not working, first thing to do is check your `console`. ## React version Please ensure that you meet our peer dependency version of `React`. Your React version needs to be greater than or equal to `16.8.5`. If you want to know what React version you are on take a look at your [`package.json`](https://docs.npmjs.com/files/package.json) or use `console.log(React.version)`. If you are not sure if your `package.json` version satisfies `16.8.5` have a read of [npm: about semantic versioning](https://docs.npmjs.com/about-semantic-versioning) and try out the [npm sermver calculator](https://semver.npmjs.com/) ## No duplicate ids `draggableId` and `droppableId` values must be unique for the whole `` and not just a list. More information: [identifiers guide](/docs/guides/identifiers.md) ## `` indexes Rules: - Must be unique within a `` (no duplicates) - Must be consecutive. `[0, 1, 2]` and not `[1, 2, 8]` Indexes do not need to start from `0` (this is often the case in [virtual lists](/docs/patterns/virtual-lists.md)) [More information](/docs/api/draggable.md#draggable-props) ## No margin collapsing between ``s This can happen if you have a `margin-top` as well as a `margin-bottom` on a ``. [More information](/docs/api/draggable.md#unsupported-margin-setups) ## `key`s for a list of `` If you are rendering a list of ``s then it is important that you add a [`key`](https://reactjs.org/docs/lists-and-keys.html) prop to each ``. ```js return items.map((item, index) => ( {(provided, snapshot) => (
{item.content}
)}
)); ``` [More information](/docs/api/draggable.md) ## Avoid empty lists We recommend you set a `min-height` or `min-width` on a `` to ensure that there is a visible drop target when a list is empty We go over this in our [Get started with `react-beautiful-dnd` course](https://egghead.io/lessons/react-move-items-between-columns-with-react-beautiful-dnd-using-ondragend) ## Image flickering in a `` See our [avoiding image flickering guide](/docs/guides/avoiding-image-flickering.md) [← Back to documentation](/README.md#documentation-) ================================================ FILE: docs/guides/content-security-policy.md ================================================ # Content Security Policy > This page is to help you get around the CSP error: "Refused to apply inline style because it violates the following Content Security Policy directive: "style-src 'self'"." > A huge thankyou to [@Zweder](https://github.com/Zweder) for driving this effort Content Security Policy (CSP) is a way of controlling where a browser can download assets from, as well as what those assets are allowed to do. Background reading on CSP - [Google guide](https://developer.chrome.com/extensions/contentSecurityPolicy) - [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) - [Helmetjs guide](https://helmetjs.github.io/docs/csp/) `react-beautiful-dnd` creates a `