Repository: clauderic/react-sortable-hoc Branch: master Commit: caf3c4f5bfb3 Files: 49 Total size: 165.4 KB Directory structure: gitextract_bc7ghwt0/ ├── .codesandbox/ │ └── ci.json ├── .editorconfig ├── .eslintrc.json ├── .gitignore ├── .npmignore ├── .prettierrc ├── .storybook/ │ ├── config.js │ ├── manager-head.html │ ├── theme.js │ └── webpack.config.js ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── examples/ │ ├── .eslintrc.json │ ├── basic.js │ ├── collections.js │ ├── drag-handle.js │ ├── react-infinite.js │ ├── react-virtualized-table-columns.js │ └── react-virtualized.js ├── package.json ├── rollup.config.js ├── src/ │ ├── .stories/ │ │ ├── Storybook.scss │ │ ├── grouping-items/ │ │ │ ├── Item/ │ │ │ │ ├── Item.js │ │ │ │ ├── Item.scss │ │ │ │ └── index.js │ │ │ ├── List/ │ │ │ │ ├── List.js │ │ │ │ ├── List.scss │ │ │ │ └── index.js │ │ │ ├── index.js │ │ │ └── utils.js │ │ ├── index.js │ │ └── interactive-elements-stress-test/ │ │ ├── Item/ │ │ │ ├── Item.js │ │ │ ├── Item.scss │ │ │ └── index.js │ │ ├── List.js │ │ └── index.js │ ├── AutoScroller/ │ │ └── index.js │ ├── Manager/ │ │ └── index.js │ ├── SortableContainer/ │ │ ├── defaultGetHelperDimensions.js │ │ ├── defaultShouldCancelStart.js │ │ ├── index.js │ │ └── props.js │ ├── SortableElement/ │ │ └── index.js │ ├── SortableHandle/ │ │ └── index.js │ ├── index.js │ └── utils.js └── types/ └── index.d.ts ================================================ FILE CONTENTS ================================================ ================================================ FILE: .codesandbox/ci.json ================================================ { "sandboxes": ["react", "o104x95y86"] } ================================================ FILE: .editorconfig ================================================ # editorconfig.org root = true [*] charset = utf-8 indent_size = 2 indent_style = space insert_final_newline = true trim_trailing_whitespace = true # Markdown syntax specifies that trailing whitespaces can be meaningful, # so let’s not trim those. e.g. 2 trailing spaces = linebreak (
) # See https://daringfireball.net/projects/markdown/syntax#p [*.md] trim_trailing_whitespace = false ================================================ FILE: .eslintrc.json ================================================ { "extends": ["plugin:shopify/react", "plugin:prettier/recommended"], "rules": { "no-process-env": "off", "no-lonely-if": "off", "no-undefined": "off", "no-param-reassign": "off", "no-mixed-operators": "off", "no-misleading-character-class": "off", "require-atomic-updates": "off", "prefer-object-spread": "off", "lines-around-comment": "off", "function-paren-newline": "off", "promise/catch-or-return": "off", "react/forbid-prop-types": "off", "react/jsx-filename-extension": "off", "react/no-unused-prop-types": "off", "react/no-string-refs": 1, "react/no-deprecated": 1, "shopify/binary-assignment-parens": "off", "sort-class-members/sort-class-members": "off" } } ================================================ FILE: .gitignore ================================================ *.DS_Store node_modules dist styles.min.css styles.min.css.map coverage npm-debug.log ================================================ FILE: .npmignore ================================================ .github .babelrc coverage src test .* *.md codecov.yml .travis.yml ================================================ FILE: .prettierrc ================================================ { "arrowParens": "always", "bracketSpacing": false, "singleQuote": true, "trailingComma": "all" } ================================================ FILE: .storybook/config.js ================================================ import {addParameters, configure} from '@storybook/react'; import theme from './theme'; addParameters({ options: { showAddonPanel: false, theme, }, }); function loadStories() { require('../src/.stories/index.js'); } configure(loadStories, module); ================================================ FILE: .storybook/manager-head.html ================================================ ================================================ FILE: .storybook/theme.js ================================================ import {create} from '@storybook/theming'; export default create({ base: 'light', colorSecondary: '#9276ff', // UI appBg: 'white', appContentBg: '#f9f9f9', appBorderColor: 'grey', appBorderRadius: 4, // Typography fontBase: '"Roboto", Helvetica Neue, Helvetica, Arial, sans-serif', fontCode: 'monospace', // Text colors textColor: '#364149', textInverseColor: 'rgba(255,255,255,0.9)', // Toolbar default and active colors barTextColor: '#FFF', barSelectedColor: '#FFF', barBg: '#9276ff', // Form colors inputBg: 'white', inputBorder: '#efefef', inputTextColor: '#364149', inputBorderRadius: 0, brandTitle: 'React Sortable HOC', brandUrl: 'https://github.com/clauderic/react-sortable-hoc', brandImage: 'https://user-images.githubusercontent.com/1416436/54170652-dfd59d80-444d-11e9-9c51-658638c0454b.png', }); ================================================ FILE: .storybook/webpack.config.js ================================================ module.exports = { module: { rules: [ { test: /(\.scss)$/, use: [ 'style-loader', { loader: 'css-loader', options: { modules: true, localIdentName: '[name]__[local]', }, }, { loader: 'postcss-loader', options: { plugins: [require('autoprefixer')], }, }, 'sass-loader', ], }, { test: /(\.css)$/, use: ['style-loader', 'css-loader'], }, ], }, }; ================================================ FILE: .travis.yml ================================================ language: node_js node_js: - 10 ================================================ FILE: CHANGELOG.md ================================================ # Change Log All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. # [2.0.0](https://github.com/clauderic/react-sortable-hoc/compare/v1.11.0...v2.0.0) (2021-03-18) ### Bug Fixes - Fixed React strict mode warnings by upgrading to new React context API and removing legacy refs ([#624](https://github.com/clauderic/react-sortable-hoc/pull/624)). Since the new context API was introduced in React ^16.3.0, the peer dependencies had to be upgraded accordingly. - Check if event is cancellable before calling `event.preventDefault()` [#752](https://github.com/clauderic/react-sortable-hoc/pull/752). - Fix touch events being lost by listening to event.target on mobile [#586](https://github.com/clauderic/react-sortable-hoc/pull/586). - Added `disableAutoscroll` prop to PropType definitions [#755](https://github.com/clauderic/react-sortable-hoc/pull/755). ### Dependencies - Updated minimum peer dependencies for `react` and `react-dom` to ^16.3.0. Added ^17.0.0 to list of supported peer dependencies. # [1.11.0](https://github.com/clauderic/react-sortable-hoc/compare/v1.10.1...v1.11.0) (2020-01-20) ### Bug Fixes - clear autoscroller when autoscroll is disabled ([#604](https://github.com/clauderic/react-sortable-hoc/issues/604)) ([3fd83f9](https://github.com/clauderic/react-sortable-hoc/commit/3fd83f9)) - Fix UMD path ([#611](https://github.com/clauderic/react-sortable-hoc/pull/611)) ([f61331d](https://github.com/clauderic/react-sortable-hoc/commit/f61331d)) ### Features - Add CSS Grid grid-gap support ([#657](https://github.com/clauderic/react-sortable-hoc/issues/657)) ([4efcaa2](https://github.com/clauderic/react-sortable-hoc/commit/4efcaa2)) ## [1.10.1](https://github.com/clauderic/react-sortable-hoc/compare/v1.10.0...v1.10.1) (2019-08-22) ### Bug Fixes - PropType definition for keyCodes was incorrect ([eaf5070](https://github.com/clauderic/react-sortable-hoc/commit/eaf5070)) # [1.10.0](https://github.com/clauderic/react-sortable-hoc/compare/v1.9.1...v1.10.0) (2019-08-22) ### Bug Fixes - don't spread the keysToOmit parameter in omit util ([#563](https://github.com/clauderic/react-sortable-hoc/issues/563)) ([1c69772](https://github.com/clauderic/react-sortable-hoc/commit/1c69772)) - Fix broken UMD build ([#587](https://github.com/clauderic/react-sortable-hoc/issues/587)) ([6cb7750](https://github.com/clauderic/react-sortable-hoc/commit/6cb7750)) - remove browser field with umd bundle ([#541](https://github.com/clauderic/react-sortable-hoc/issues/541)) ([d3b30fd](https://github.com/clauderic/react-sortable-hoc/commit/d3b30fd) ### Features - Add keyCodes prop to configure the keyboard shortcuts ([#588](https://github.com/clauderic/react-sortable-hoc/issues/588)) ([4c6d8dd](https://github.com/clauderic/react-sortable-hoc/commit/4c6d8dd)) ## [1.9.1](https://github.com/clauderic/react-sortable-hoc/compare/v1.9.0...v1.9.1) (2019-04-24) ### Bug Fixes - do not copy canvas context if it has neither width nor height ([#530](https://github.com/clauderic/react-sortable-hoc/issues/530)) ([3808437](https://github.com/clauderic/react-sortable-hoc/commit/3808437)) - pass isKeySorting to onSortOver and updateBeforeSortStart handler props ([#531](https://github.com/clauderic/react-sortable-hoc/issues/531)) ([763fd33](https://github.com/clauderic/react-sortable-hoc/commit/763fd33) # [1.9.0](https://github.com/clauderic/react-sortable-hoc/compare/v1.8.3...v1.9.0) (2019-04-23) ### Bug Fixes - issue with radio input name collision when cloning helper ([5337c97](https://github.com/clauderic/react-sortable-hoc/commit/5337c97)) ### Features - add support for keyboard sorting ([#501](https://github.com/clauderic/react-sortable-hoc/issues/501)) ([439b92f](https://github.com/clauderic/react-sortable-hoc/commit/439b92f)) - prevent sort start on contentEditable target ([d64c8cf](https://github.com/clauderic/react-sortable-hoc/commit/d64c8cf)) ## [1.8.3](https://github.com/clauderic/react-sortable-hoc/compare/v1.8.2...v1.8.3) (2019-03-20) ### Bug Fixes - issue with windowAsScrollContainer and translation offsets ([0391e62](https://github.com/clauderic/react-sortable-hoc/commit/0391e62)) ### Features - Add disableAutoscroll prop ([#484](https://github.com/clauderic/react-sortable-hoc/issues/484)) ([7845e76](https://github.com/clauderic/react-sortable-hoc/commit/7845e76)) - added helperContainer prop ([286eff4](https://github.com/clauderic/react-sortable-hoc/commit/286eff4)) - allow helperContainer prop to be a function returning an HTMLElement ([#489](https://github.com/clauderic/react-sortable-hoc/issues/489)) ([f4a9b4a](https://github.com/clauderic/react-sortable-hoc/commit/f4a9b4a)) - Detect scroll container automatically ([#507](https://github.com/clauderic/react-sortable-hoc/issues/507)) ([6572921](https://github.com/clauderic/react-sortable-hoc/commit/6572921)) ## [1.8.2](https://github.com/clauderic/react-sortable-hoc/compare/v1.8.1...v1.8.2) (2019-03-19) ### Bug Fixes - issue with getComputedStyle and getScrollingParent ([b104249](https://github.com/clauderic/react-sortable-hoc/commit/b104249)) ## [1.8.1](https://github.com/clauderic/react-sortable-hoc/compare/v1.8.0...v1.8.1) (2019-03-18) ### Bug Fixes - issue with cloning canvas context of dragged items ([#512](https://github.com/clauderic/react-sortable-hoc/issues/512)) ([4df34ad](https://github.com/clauderic/react-sortable-hoc/commit/4df34ad)) # [1.8.0](https://github.com/clauderic/react-sortable-hoc/compare/v1.1.0...v1.8.0) (2019-03-18) ### Bug Fixes - added prop-types to peerDependencies ([0e855c5](https://github.com/clauderic/react-sortable-hoc/commit/0e855c5)) - copy canvas content into cloned node ([43ad122](https://github.com/clauderic/react-sortable-hoc/commit/43ad122)) - get updated index after updateBeforeSortStart ([4471a0a](https://github.com/clauderic/react-sortable-hoc/commit/4471a0a)) - helperContainer PropType definition broke server-side rendering ([#471](https://github.com/clauderic/react-sortable-hoc/issues/471)) ([c0eef97](https://github.com/clauderic/react-sortable-hoc/commit/c0eef97)) - lock axis story should not use lockToContainerEdges ([db1d3a9](https://github.com/clauderic/react-sortable-hoc/commit/db1d3a9)) - omit disableAutoscroll prop ([#502](https://github.com/clauderic/react-sortable-hoc/issues/502)) ([e994e73](https://github.com/clauderic/react-sortable-hoc/commit/e994e73)) - omit spreading helperContainer prop ([#497](https://github.com/clauderic/react-sortable-hoc/issues/497)) ([12bafdf](https://github.com/clauderic/react-sortable-hoc/commit/12bafdf)) - overflow bug while dragging an item upwards in a grid ([1a2c87e](https://github.com/clauderic/react-sortable-hoc/commit/1a2c87e)) - replace process.env.NODE_ENV in UMD builds ([16135df](https://github.com/clauderic/react-sortable-hoc/commit/16135df)) - update helperContainer prop type definition ([#491](https://github.com/clauderic/react-sortable-hoc/issues/491)) ([fd30383](https://github.com/clauderic/react-sortable-hoc/commit/fd30383)) - updated the behaviour of disabled elements ([bd3d041](https://github.com/clauderic/react-sortable-hoc/commit/bd3d041)) - virtualized collection grid bug ([a57975c](https://github.com/clauderic/react-sortable-hoc/commit/a57975c)) ### Features - Add disableAutoscroll prop ([#484](https://github.com/clauderic/react-sortable-hoc/issues/484)) ([7845e76](https://github.com/clauderic/react-sortable-hoc/commit/7845e76)) - added helperContainer prop ([286eff4](https://github.com/clauderic/react-sortable-hoc/commit/286eff4)) - allow helperContainer prop to be a function returning an HTMLElement ([#489](https://github.com/clauderic/react-sortable-hoc/issues/489)) ([f4a9b4a](https://github.com/clauderic/react-sortable-hoc/commit/f4a9b4a)) - Detect scroll container automatically ([#507](https://github.com/clauderic/react-sortable-hoc/issues/507)) ([6572921](https://github.com/clauderic/react-sortable-hoc/commit/6572921)) ## [1.7.1](https://github.com/clauderic/react-sortable-hoc/compare/v1.1.0...v1.7.1) (2019-03-06) ### Bug Fixes - updated the behaviour of disabled elements ([bd3d041](https://github.com/clauderic/react-sortable-hoc/commit/bd3d041)) # [1.7.0](https://github.com/clauderic/react-sortable-hoc/compare/v1.1.0...v1.7.0) (2019-03-06) ### Bug Fixes - updated the behaviour of disabled elements ([bd3d041](https://github.com/clauderic/react-sortable-hoc/commit/bd3d041)) ## [1.6.1](https://github.com/clauderic/react-sortable-hoc/compare/v1.6.0...v1.6.1) (2019-02-11) ### Bug Fixes - omit disableAutoscroll prop ([#502](https://github.com/clauderic/react-sortable-hoc/issues/502)) ([e994e73](https://github.com/clauderic/react-sortable-hoc/commit/e994e73)) # [1.6.0](https://github.com/clauderic/react-sortable-hoc/compare/v1.5.3...v1.6.0) (2019-02-07) ### Features - Add disableAutoscroll prop ([#484](https://github.com/clauderic/react-sortable-hoc/issues/484)) ([7845e76](https://github.com/clauderic/react-sortable-hoc/commit/7845e76)) ## [1.5.4](https://github.com/clauderic/react-sortable-hoc/compare/v1.5.3...v1.5.4) (2019-02-07) ### Bug Fixes - overflow bug while dragging an item upwards in a grid ([1a2c87e](https://github.com/clauderic/react-sortable-hoc/commit/1a2c87e)) - virtualized collection grid bug ([a57975c](https://github.com/clauderic/react-sortable-hoc/commit/a57975c)) ## [1.5.3](https://github.com/clauderic/react-sortable-hoc/compare/v1.1.0...v1.5.3) (2019-01-25) ### Bug Fixes - omit spreading helperContainer prop on WrappedComponent ([#497](https://github.com/clauderic/react-sortable-hoc/issues/497)) ([12bafdf](https://github.com/clauderic/react-sortable-hoc/commit/12bafdf)) ## [1.5.2](https://github.com/clauderic/react-sortable-hoc/compare/v1.5.1...v1.5.2) (2019-01-22) ### Bug Fixes - invalid helperContainer PropType definition ([#493](https://github.com/clauderic/react-sortable-hoc/issues/493)) ([dc1d18f](https://github.com/clauderic/react-sortable-hoc/commit/dc1d18f)) ## [1.5.1](https://github.com/clauderic/react-sortable-hoc/compare/v1.5.0...v1.5.1) (2019-01-22) ### Bug Fixes - update helperContainer prop type definition ([#491](https://github.com/clauderic/react-sortable-hoc/issues/491)) ([fd30383](https://github.com/clauderic/react-sortable-hoc/commit/fd30383)) # [1.5.0](https://github.com/clauderic/react-sortable-hoc/compare/v1.4.0...v1.5.0) (2019-01-22) ### Features - allow helperContainer prop to be a function returning an HTMLElement ([f4a9b4a](https://github.com/clauderic/react-sortable-hoc/commit/f4a9b4a)) # [1.4.0](https://github.com/clauderic/react-sortable-hoc/compare/v1.3.0...v1.4.0) (2019-01-10) ### Bug Fixes - Fix CommonJS and UMD builds by using Rollup and Babel to generate the bundles ([#474](https://github.com/clauderic/react-sortable-hoc/issues/474)) # [1.3.0](https://github.com/clauderic/react-sortable-hoc/compare/v1.2.0...v1.3.0) (2019-01-08) ### Bug Fixes - helperContainer PropType definition broke server-side rendering ([#471](https://github.com/clauderic/react-sortable-hoc/issues/471)) ([c0eef97](https://github.com/clauderic/react-sortable-hoc/commit/c0eef97)) # [1.2.0](https://github.com/clauderic/react-sortable-hoc/compare/v1.1.0...v1.2.0) (2019-01-08) ### Features - added helperContainer prop ([286eff4](https://github.com/clauderic/react-sortable-hoc/commit/286eff4)) # [1.1.0](https://github.com/clauderic/react-sortable-hoc/compare/v1.0.0...v1.1.0) (2019-01-07) ### Features - added updateBeforeSortStart prop ([162857b](https://github.com/clauderic/react-sortable-hoc/commit/162857b)) # [1.0.0](https://github.com/clauderic/react-sortable-hoc/compare/v0.8.4...v1.0.0) (2019-01-07) ### BREAKING CHANGES - The UMD release no longer includes babel-polyfill, you will need to include your own polyfills in order to support older browsers. # 0.8.4 - Fix a bug when you use SortableHandle and distance prop [#447](https://github.com/clauderic/react-sortable-hoc/pull/447) # 0.8.3 - Fix: TouchEvent is undefined in certain browsers, such as Safari [#382](https://github.com/clauderic/react-sortable-hoc/issues/382) # 0.8.1 - Fix scrolling issues on mobile with anchor tag elements [#380](https://github.com/clauderic/react-sortable-hoc/pull/380) - Update TypeScript type definition for ContainerGetter to accept Promises that return HTMLElements # 0.8.0 - Allow `getContainer` to return a promise. This is useful when the container node is rendered by a parent component, since `componentDidMount` fires backwards (from child to parent) [#155](https://github.com/clauderic/react-sortable-hoc/pull/155/) # 0.7.4 - Fix typo in getLockPixelOffset helper # 0.7.3 - Fix issues with distance and pressThreshold props on mobile [#378](https://github.com/clauderic/react-sortable-hoc/pull/378) # 0.7.2 - Fix issues with TypeScript type definitions # 0.7.1 - Provide TypeScript type definitions out of the box [#377](https://github.com/clauderic/react-sortable-hoc/pull/377) - Fix potential issues with calling `removeEventListeners` on `componentWillUnmount` if the container node has already unmounted [#376](https://github.com/clauderic/react-sortable-hoc/pull/376) # 0.7.0 - [Breaking change] Removed lodash dependency. For users wishing to support Internet Explorer, a [polyfill](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find#Polyfill) for Array.prototype.find will be required - Added `onSortOver` prop that gets invoked when sorting over an element [#278](https://github.com/clauderic/react-sortable-hoc/pull/278) - Fix `useWindowAsScrollContainer` [#306](https://github.com/clauderic/react-sortable-hoc/pull/306) # 0.6.8 Update react and react-dom peerdependency requirements for React 16+ [#283](https://github.com/clauderic/react-sortable-hoc/pull/283). Thanks [@jnsdls](https://github.com/jnsdls)! # 0.6.7 Fixes issues with Jest Snapshot testing trying to serialize the `window` object and running out of memory [#249](https://github.com/clauderic/react-sortable-hoc/issues/249). Thanks [@cameronmcefee](https://github.com/cameronmcefee)! # 0.6.6 Fixes an issue with Internet Explorer 11 introduced in `0.6.5` [#248](https://github.com/clauderic/react-sortable-hoc/pull/248). Thanks [@humiston](https://github.com/humiston)! # 0.6.5 Fixes the position of the sortable helper when the page is scrolled [#213](https://github.com/clauderic/react-sortable-hoc/pull/213) # 0.6.4 Fix: when cloning the element that is being sorted, we no longer update the value of cloned file inputs [#232](https://github.com/clauderic/react-sortable-hoc/pull/232) # 0.6.3 Fixes issues caused by a disabled SortableElement being moved when `distance` is set to a value other than `0` # 0.6.2 Use `prop-types` package for PropType validation for compatibility with React ^15.5 # 0.6.1 Tweak: default to `pointerEvents: none` on sortable helper, this way the underlying view can still be scrolled using the trackpad/mousewheel while sorting [#160](https://github.com/clauderic/react-sortable-hoc/pull/160) # 0.6.0 Feature: added `pressThreshold` prop to make `pressDelay` fault tolerant [#159](https://github.com/clauderic/react-sortable-hoc/pull/159) # 0.5.0 Tweak: `button` elements are now included in the default `shouldCancelStart` implementation [#142](https://github.com/clauderic/react-sortable-hoc/pull/142). Fix: Omit `getHelperDimensions` before passing down props in `SortableContainer` # 0.4.12 Fix: This release fixes some issues caused by the `onSortEnd` callback being invoked before `setState` [#82](https://github.com/clauderic/react-sortable-hoc/issues/82). # 0.4.10 Fix: This version fixes issues with nested `SortableContainer` elements using drag handles from also dragging their parent [#112](https://github.com/clauderic/react-sortable-hoc/issues/112), #127(https://github.com/clauderic/react-sortable-hoc/pull/127). Thanks [@DeadHeadRussell](https://github.com/DeadHeadRussell)! # 0.4.9 Fix: This release fixes a bug introduced in `0.4.8` caused by calling the `forEach` method directly on a NodeList, which is undefined in a number of browsers [#125](https://github.com/clauderic/react-sortable-hoc/issues/125) # 0.4.8 Fix: Added logic to ensure that `select`, `input` and `textarea` fields in `SortableElement` always retain their `value` when the element is cloned (this happens when sorting begins) [#122](https://github.com/clauderic/react-sortable-hoc/issues/122) [#123](https://github.com/clauderic/react-sortable-hoc/pull/123). Thanks [@tomasztomys](https://github.com/tomasztomys)! # 0.4.7 Fix: This release fixes a bug in Firefox caused by active anchor tags preventing mousemove events from being fired [#118](https://github.com/clauderic/react-sortable-hoc/issues/118) # 0.4.5 Fix: getHelperDimensions height was not being used (Thanks [@SMenigat](https://github.com/SMenigat)!) # 0.4.4 Tweak: cherry-picking lodash methods instead of importing the entire bundle (slipped by in a PR, thanks for pointing this out [@arackaf](https://github.com/arackaf)!) # 0.4.3 Fixes an edge-case bug in Firefox where window.getComputedStyle() returns null inside an iframe with `display: none` [#106](https://github.com/clauderic/react-sortable-hoc/pull/106). Thanks [@funnel-mark](https://github.com/funnel-mark)! # 0.4.2 Fixes an issue when attempting to sort items while rapidly moving the mouse. By setting an immediate timer, we move the cancel event to the tail of the timer queue, and ensure that it is fired after the pressTimer [#80](https://github.com/clauderic/react-sortable-hoc/pull/80). Thanks [@v0lkan](https://github.com/v0lkan)! # 0.4.0 - Fix a timing issue in Chrome caused by setTimeout [#71](https://github.com/clauderic/react-sortable-hoc/pull/71) - Private props are no longer passed down to the wrapped component [#98](https://github.com/clauderic/react-sortable-hoc/pull/98) # 0.3.0 Added grid support for elements of equal widths / heights [#4](https://github.com/clauderic/react-sortable-hoc/issues/4) [#86](https://github.com/clauderic/react-sortable-hoc/pull/86). Huge shout-out to [@richmeij](https://github.com/richmeij) for making this happen! # 0.2.0 Add a `getHelperDimensions` prop to control SortableHelper size [#83](https://github.com/clauderic/react-sortable-hoc/issues/83). Thanks [@nervetattoo](https://github.com/nervetattoo)! # 0.1.1 Added `touchCancel` listener to properly handle canceled touches [#73](https://github.com/clauderic/react-sortable-hoc/pull/73) # 0.1.0 - Force `box-sizing: border-box` on sortable helper [#67](https://github.com/clauderic/react-sortable-hoc/issues/67) - Support changing an item's collection prop on the fly [#66](https://github.com/clauderic/react-sortable-hoc/pull/66) # 0.0.11 Utilize babel-plugin-transform-runtime to utilize `babelHelpers` without them being required in application code [#45](https://github.com/clauderic/react-sortable-hoc/issues/45) # 0.0.10 The `arrayMove` helper no longer mutates the array, it now returns a new array [#61](https://github.com/clauderic/react-sortable-hoc/issues/61) # 0.0.9 Server-side rendering bugfix: safeguard against `document` being undefined [#59](https://github.com/clauderic/react-sortable-hoc/pull/59) # 0.0.8 - Added `distance` prop ([#35](https://github.com/clauderic/react-sortable-hoc/issues/35)) - Added a `shouldCancelStart` ([#47](https://github.com/clauderic/react-sortable-hoc/issues/47), [#36](https://github.com/clauderic/react-sortable-hoc/issues/36), [#41](https://github.com/clauderic/react-sortable-hoc/issues/41)) prop to programatically cancel sorting before it begins. - Prevent right click from causing sort start ([#46](https://github.com/clauderic/react-sortable-hoc/issues/46)) # 0.0.7 Fixes server-side rendering (window undefined) ([#39](https://github.com/clauderic/react-sortable-hoc/issues/39)) # 0.0.6 - Added support for a custom container ([#37](https://github.com/clauderic/react-sortable-hoc/issues/37)) - Fix changing disable property while receiving props ([#34](https://github.com/clauderic/react-sortable-hoc/issues/34)) ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2016, Claudéric Demers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ > **Warning** > > This library is **no longer actively maintained**. It will continue to receive critical security updates, but there are no new features planned. > In future versions of React, the [`findDOMNode`](https://reactjs.org/docs/react-dom.html#finddomnode) method will be deprecated. This method is a critical piece of the architecture of `react-sortable-hoc`, and the library will stop working in the future when that method is removed from `react-dom`. > > All development efforts have been redirected towards [**@dnd-kit**](https://github.com/clauderic/dnd-kit). It provides feature parity, built with a modern and extensible architecture, supports complex use-cases and has accessibility features built-in. New consumers are strongly encouraged to adopt **@dnd-kit** instead of adopting `react-sortable-hoc`. Visit @dnd-kit github repository # React Sortable HOC > A set of higher-order components to turn any list into an animated, accessible and touch-friendly sortable list [![npm version](https://img.shields.io/npm/v/react-sortable-hoc.svg)](https://www.npmjs.com/package/react-sortable-hoc) [![npm downloads](https://img.shields.io/npm/dm/react-sortable-hoc.svg)](https://www.npmjs.com/package/react-sortable-hoc) [![license](https://img.shields.io/github/license/mashape/apistatus.svg?maxAge=2592000)](https://github.com/clauderic/react-sortable-hoc/blob/master/LICENSE) [![Gitter](https://badges.gitter.im/clauderic/react-sortable-hoc.svg)](https://gitter.im/clauderic/react-sortable-hoc) ![gzip size](http://img.badgesize.io/https://npmcdn.com/react-sortable-hoc/dist/react-sortable-hoc.umd.min.js?compression=gzip) ### Examples available here: http://clauderic.github.io/react-sortable-hoc/ ## Features - **Higher Order Components** – Integrates with your existing components - **Drag handle, auto-scrolling, locked axis, events, and more!** - **Suuuper smooth animations** – Chasing the 60FPS dream 🌈 - **Works with virtualization libraries: [react-virtualized](https://github.com/bvaughn/react-virtualized/), [react-tiny-virtual-list](https://github.com/clauderic/react-tiny-virtual-list), [react-infinite](https://github.com/seatgeek/react-infinite), etc.** - **Horizontal lists, vertical lists, or a grid** ↔ ↕ ⤡ - **Touch support** 👌 - **Accessible: supports keyboard sorting** ## Installation Using [npm](https://www.npmjs.com/package/react-sortable-hoc): $ npm install react-sortable-hoc --save Then, using a module bundler that supports either CommonJS or ES2015 modules, such as [webpack](https://github.com/webpack/webpack): ```js // Using an ES6 transpiler like Babel import {SortableContainer, SortableElement} from 'react-sortable-hoc'; // Not using an ES6 transpiler var Sortable = require('react-sortable-hoc'); var SortableContainer = Sortable.SortableContainer; var SortableElement = Sortable.SortableElement; ``` Alternatively, an UMD build is also available: ```html ``` ## Usage ### Basic Example ```js import React, {Component} from 'react'; import {render} from 'react-dom'; import {SortableContainer, SortableElement} from 'react-sortable-hoc'; import arrayMove from 'array-move'; const SortableItem = SortableElement(({value}) =>
  • {value}
  • ); const SortableList = SortableContainer(({items}) => { return ( ); }); class SortableComponent extends Component { state = { items: ['Item 1', 'Item 2', 'Item 3', 'Item 4', 'Item 5', 'Item 6'], }; onSortEnd = ({oldIndex, newIndex}) => { this.setState(({items}) => ({ items: arrayMove(items, oldIndex, newIndex), })); }; render() { return ; } } render(, document.getElementById('root')); ``` That's it! React Sortable does not come with any styles by default, since it's meant to enhance your existing components. More code examples are available [here](https://github.com/clauderic/react-sortable-hoc/blob/master/examples/). ## Why should I use this? There are already a number of great Drag & Drop libraries out there (for instance, [react-dnd](https://github.com/gaearon/react-dnd/) is fantastic). If those libraries fit your needs, you should definitely give them a try first. However, most of those libraries rely on the HTML5 Drag & Drop API, which has some severe limitations. For instance, things rapidly become tricky if you need to support touch devices, if you need to lock dragging to an axis, or want to animate the nodes as they're being sorted. React Sortable HOC aims to provide a simple set of higher-order components to fill those gaps. If you're looking for a dead-simple, mobile-friendly way to add sortable functionality to your lists, then you're in the right place. ### Prop Types #### SortableContainer HOC | Property | Type | Default | Description | | :-------------------------------- | :-------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | axis | String | `y` | Items can be sorted horizontally, vertically or in a grid. Possible values: `x`, `y` or `xy` | | lockAxis | String | | If you'd like, you can lock movement to an axis while sorting. This is not something that is possible with HTML5 Drag & Drop. Possible values: `x` or `y`. | | helperClass | String | | You can provide a class you'd like to add to the sortable helper to add some styles to it | | transitionDuration | Number | `300` | The duration of the transition when elements shift positions. Set this to `0` if you'd like to disable transitions | | keyboardSortingTransitionDuration | Number | `transitionDuration` | The duration of the transition when the helper is shifted during keyboard sorting. Set this to `0` if you'd like to disable transitions for the keyboard sorting helper. Defaults to the value set for `transitionDuration` if undefined | | keyCodes | Array | `{`
      `lift: [32],`
      `drop: [32],`
      `cancel: [27],`
      `up: [38, 37],`
      `down: [40, 39]`
    `}` | An object containing an array of keycodes for each keyboard-accessible action. | | pressDelay | Number | `0` | If you'd like elements to only become sortable after being pressed for a certain time, change this property. A good sensible default value for mobile is `200`. Cannot be used in conjunction with the `distance` prop. | | pressThreshold | Number | `5` | Number of pixels of movement to tolerate before ignoring a press event. | | distance | Number | `0` | If you'd like elements to only become sortable after being dragged a certain number of pixels. Cannot be used in conjunction with the `pressDelay` prop. | | shouldCancelStart | Function | [Function](https://github.com/clauderic/react-sortable-hoc/blob/master/src/SortableContainer/index.js#L48) | This function is invoked before sorting begins, and can be used to programatically cancel sorting before it begins. By default, it will cancel sorting if the event target is either an `input`, `textarea`, `select`, `option`, or `button`. | | updateBeforeSortStart | Function | | This function is invoked before sorting begins. It can return a promise, allowing you to run asynchronous updates (such as `setState`) before sorting begins. `function({node, index, collection, isKeySorting}, event)` | | onSortStart | Function | | Callback that is invoked when sorting begins. `function({node, index, collection, isKeySorting}, event)` | | onSortMove | Function | | Callback that is invoked during sorting as the cursor moves. `function(event)` | | onSortOver | Function | | Callback that is invoked when moving over an item. `function({index, oldIndex, newIndex, collection, isKeySorting}, e)` | | onSortEnd | Function | | Callback that is invoked when sorting ends. `function({oldIndex, newIndex, collection, isKeySorting}, e)` | | useDragHandle | Boolean | `false` | If you're using the `SortableHandle` HOC, set this to `true` | | useWindowAsScrollContainer | Boolean | `false` | If you want, you can set the `window` as the scrolling container | | hideSortableGhost | Boolean | `true` | Whether to auto-hide the ghost element. By default, as a convenience, React Sortable List will automatically hide the element that is currently being sorted. Set this to false if you would like to apply your own styling. | | lockToContainerEdges | Boolean | `false` | You can lock movement of the sortable element to it's parent `SortableContainer` | | lockOffset | `OffsetValue`\* | [`OffsetValue`\*, `OffsetValue`\*] | `"50%"` | When`lockToContainerEdges`is set to`true`, this controls the offset distance between the sortable helper and the top/bottom edges of it's parent`SortableContainer`. Percentage values are relative to the height of the item currently being sorted. If you wish to specify different behaviours for locking to the _top_ of the container vs the _bottom_, you may also pass in an`array`(For example:`["0%", "100%"]`). | | getContainer | Function | | Optional function to return the scrollable container element. This property defaults to the `SortableContainer` element itself or (if `useWindowAsScrollContainer` is true) the window. Use this function to specify a custom container object (eg this is useful for integrating with certain 3rd party components such as `FlexTable`). This function is passed a single parameter (the `wrappedInstance` React element) and it is expected to return a DOM element. | | getHelperDimensions | Function | [Function](https://github.com/clauderic/react-sortable-hoc/blob/master/src/SortableContainer/index.js#L74-L77) | Optional `function({node, index, collection})` that should return the computed dimensions of the SortableHelper. See [default implementation](https://github.com/clauderic/react-sortable-hoc/blob/master/src/SortableContainer/defaultGetHelperDimensions.js) for more details | | helperContainer | HTMLElement | Function | `document.body` | By default, the cloned sortable helper is appended to the document body. Use this prop to specify a different container for the sortable clone to be appended to. Accepts an `HTMLElement` or a function returning an `HTMLElement` that will be invoked before right before sorting begins | | disableAutoscroll | Boolean | `false` | Disables autoscrolling while dragging | \* `OffsetValue` can either be a finite `Number` or a `String` made up of a number and a unit (`px` or `%`). Examples: `10` (which is the same as `"10px"`), `"50%"` #### SortableElement HOC | Property | Type | Default | Required? | Description | | :--------- | :--------------- | :------ | :-------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | index | Number | | ✓ | This is the element's sortableIndex within it's collection. This prop is required. | | collection | Number or String | `0` | | The collection the element is part of. This is useful if you have multiple groups of sortable elements within the same `SortableContainer`. [Example](http://clauderic.github.io/react-sortable-hoc/#/basic-configuration/multiple-lists) | | disabled | Boolean | `false` | | Whether the element should be sortable or not | ## FAQ ### Running Examples In root folder, run the following commands to launch React Storybook: ``` $ npm install $ npm start ``` ### Accessibility React Sortable HOC supports keyboard sorting out of the box. To enable it, make sure your `SortableElement` or `SortableHandle` is focusable. This can be done by setting `tabIndex={0}` on the outermost HTML node rendered by the component you're enhancing with `SortableElement` or `SortableHandle`. Once an item is focused/tabbed to, pressing `SPACE` picks it up, `ArrowUp` or `ArrowLeft` moves it one place backward in the list, `ArrowDown` or `ArrowRight` moves items one place forward in the list, pressing `SPACE` again drops the item in its new position. Pressing `ESC` before the item is dropped will cancel the sort operations. ### Grid support Need to sort items in a grid? We've got you covered! Just set the `axis` prop to `xy`. Grid support is currently limited to a setup where all the cells in the grid have the same width and height, though we're working hard to get variable width support in the near future. ### Item disappearing when sorting / CSS issues Upon sorting, `react-sortable-hoc` creates a clone of the element you are sorting (the _sortable-helper_) and appends it to the end of the `` tag. The original element will still be in-place to preserve its position in the DOM until the end of the drag (with inline-styling to make it invisible). If the _sortable-helper_ gets messed up from a CSS standpoint, consider that maybe your selectors to the draggable item are dependent on a parent element which isn't present anymore (again, since the _sortable-helper_ is at the end of the ``). This can also be a `z-index` issue, for example, when using `react-sortable-hoc` within a Bootstrap modal, you'll need to increase the `z-index` of the SortableHelper so it is displayed on top of the modal (see [#87](https://github.com/clauderic/react-sortable-hoc/issues/87) for more details). ### Click events being swallowed By default, `react-sortable-hoc` is triggered immediately on `mousedown`. If you'd like to prevent this behaviour, there are a number of strategies readily available. You can use the `distance` prop to set a minimum distance (in pixels) to be dragged before sorting is enabled. You can also use the `pressDelay` prop to add a delay before sorting is enabled. Alternatively, you can also use the [SortableHandle](https://github.com/clauderic/react-sortable-hoc/blob/master/src/SortableHandle/index.js) HOC. ### Wrapper props not passed down to wrapped Component All props for `SortableContainer` and `SortableElement` listed above are intentionally consumed by the wrapper component and are **not** passed down to the wrapped component. To make them available pass down the desired prop again with a different name. E.g.: ```js const SortableItem = SortableElement(({value, sortIndex}) => (
  • {value} - #{sortIndex}
  • )); const SortableList = SortableContainer(({items}) => { return (
      {items.map((value, index) => ( ))}
    ); }); ``` ## Dependencies React Sortable HOC only depends on [invariant](https://github.com/zertosh/invariant). It has the following peerDependencies: `react`, `react-dom` ## Reporting Issues If believe you've found an issue, please [report it](https://github.com/clauderic/react-sortable-hoc/issues) along with any relevant details to reproduce it. The easiest way to do so is to fork the `react-sortable-hoc` basic setup sandbox on [CodeSandbox](https://codesandbox.io/s/o104x95y86): [![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/react-sortable-hoc-starter-o104x95y86) ## Asking for help Please do not use the issue tracker for personal support requests. Instead, use [Gitter](https://gitter.im/clauderic/react-sortable-hoc) or StackOverflow. ## Contributions Yes please! Feature requests / pull requests are welcome. ================================================ FILE: examples/.eslintrc.json ================================================ { "rules": { "import/no-unresolved": "off", "react/prop-types": "off", "react/no-array-index-key": "off" } } ================================================ FILE: examples/basic.js ================================================ import React, {Component} from 'react'; import {render} from 'react-dom'; import {sortableContainer, sortableElement} from 'react-sortable-hoc'; import arrayMove from 'array-move'; const SortableItem = sortableElement(({value}) =>
  • {value}
  • ); const SortableContainer = sortableContainer(({children}) => { return
      {children}
    ; }); class App extends Component { state = { items: ['Item 1', 'Item 2', 'Item 3', 'Item 4', 'Item 5', 'Item 6'], }; onSortEnd = ({oldIndex, newIndex}) => { this.setState(({items}) => ({ items: arrayMove(items, oldIndex, newIndex), })); }; render() { const {items} = this.state; return ( {items.map((value, index) => ( ))} ); } } render(, document.getElementById('root')); ================================================ FILE: examples/collections.js ================================================ import React, {Component} from 'react'; import {render} from 'react-dom'; import {sortableContainer, sortableElement} from 'react-sortable-hoc'; import arrayMove from 'array-move'; const SortableItem = sortableElement(({value}) =>
  • {value}
  • ); const SortableContainer = sortableContainer(({children}) => { return
    {children}
    ; }); class App extends Component { state = { collections: [[0, 1, 2], [0, 1, 2, 3, 4], [0, 1, 2]], }; onSortEnd = ({oldIndex, newIndex, collection}) => { this.setState(({collections}) => { const newCollections = [...collections]; newCollections[collection] = arrayMove( collections[collection], oldIndex, newIndex, ); return {collections: newCollections}; }); }; render() { const {collections} = this.state; return ( {collections.map((items, index) => ( LIST {index}
      {items.map((item, i) => ( ))}
    ))}
    ); } } render(, document.getElementById('root')); ================================================ FILE: examples/drag-handle.js ================================================ import React, {Component} from 'react'; import {render} from 'react-dom'; import { sortableContainer, sortableElement, sortableHandle, } from 'react-sortable-hoc'; import arrayMove from 'array-move'; const DragHandle = sortableHandle(() => ::); const SortableItem = sortableElement(({value}) => (
  • {value}
  • )); const SortableContainer = sortableContainer(({children}) => { return
      {children}
    ; }); class App extends Component { state = { items: ['Item 1', 'Item 2', 'Item 3', 'Item 4', 'Item 5', 'Item 6'], }; onSortEnd = ({oldIndex, newIndex}) => { this.setState(({items}) => ({ items: arrayMove(items, oldIndex, newIndex), })); }; render() { const {items} = this.state; return ( {items.map((value, index) => ( ))} ); } } render(, document.getElementById('root')); ================================================ FILE: examples/react-infinite.js ================================================ import React, {Component} from 'react'; import {render} from 'react-dom'; import {sortableContainer, sortableElement} from 'react-sortable-hoc'; import arrayMove from 'array-move'; import Infinite from 'react-infinite'; const SortableItem = sortableElement(({height, value}) => { return
  • {value}
  • ; }); const SortableInfiniteList = sortableContainer(({items}) => { return ( height)} > {items.map(({value, height}, index) => ( ))} ); }); class App extends Component { state = { items: [ {value: 'Item 1', height: 89}, {value: 'Item 2', height: 59}, {value: 'Item 3', height: 130}, {value: 'Item 4', height: 59}, {value: 'Item 5', height: 200}, {value: 'Item 6', height: 150}, ], }; onSortEnd = ({oldIndex, newIndex}) => { this.setState(({items}) => ({ items: arrayMove(items, oldIndex, newIndex), })); }; render() { const {items} = this.state; return ; } } render(, document.getElementById('root')); ================================================ FILE: examples/react-virtualized-table-columns.js ================================================ import React, {Component} from 'react'; import {render} from 'react-dom'; import {Table, Column} from 'react-virtualized'; import {sortableContainer, sortableElement} from 'react-sortable-hoc'; import arrayMove from 'array-move'; import 'react-virtualized/styles.css'; const ROW_HEIGHT = 30; const HEADER_ROW_HEIGHT = 20; const COL_WIDTH = 100; const SortableHeader = sortableElement(({children, ...props}) => React.cloneElement(children, props), ); const SortableHeaderRowRenderer = sortableContainer( ({className, columns, style}) => (
    {React.Children.map(columns, (column, index) => ( {column} ))}
    ), ); class TableWithSortableColumns extends Component { state = { cols: [ {dataKey: 'col1', label: 'Column 1'}, {dataKey: 'col2', label: 'Column 2'}, {dataKey: 'col3', label: 'Column 3'}, ], rows: [ {col1: 'row1 col1', col2: 'row1 col2', col3: 'row1 col3'}, {col1: 'row2 col1', col2: 'row2 col2', col3: 'row2 col3'}, {col1: 'row3 col1', col2: 'row3 col2', col3: 'row3 col3'}, ], }; onSortEnd = ({oldIndex, newIndex}) => { this.setState(({cols}) => ({ cols: arrayMove(cols, oldIndex, newIndex), })); }; getRow = ({index}) => { const {rows} = this.state; return rows[index]; }; renderHeaderRow = (params) => { return ( ); }; render() { const {rows, cols} = this.state; return ( {cols.map((col) => ( ))}
    ); } } render(, document.getElementById('root')); ================================================ FILE: examples/react-virtualized.js ================================================ import React, {Component} from 'react'; import {render} from 'react-dom'; import {sortableContainer, sortableElement} from 'react-sortable-hoc'; import arrayMove from 'array-move'; import {List} from 'react-virtualized'; const SortableItem = sortableElement(({value}) => { return
  • {value}
  • ; }); class VirtualList extends Component { renderRow = ({index}) => { const {items} = this.props; const {value} = items[index]; return ; }; getRowHeight = ({index}) => { const {items} = this.props; return items[index].height; }; render() { const {items, getRef} = this.props; return ( ); } } const SortableVirtualList = sortableContainer(VirtualList); class App extends Component { state = { items: [ {value: 'Item 1', height: 89}, {value: 'Item 2', height: 59}, {value: 'Item 3', height: 130}, {value: 'Item 4', height: 59}, {value: 'Item 5', height: 200}, {value: 'Item 6', height: 150}, ], }; registerListRef = (listInstance) => { this.List = listInstance; }; onSortEnd = ({oldIndex, newIndex}) => { if (oldIndex === newIndex) { return; } const {items} = this.state; this.setState({ items: arrayMove(items, oldIndex, newIndex), }); // We need to inform React Virtualized that the items have changed heights // This can either be done by imperatively calling the recomputeRowHeights and // forceUpdate instance methods on the `List` ref, or by passing an additional prop // to List that changes whenever the order changes to force it to re-render this.List.recomputeRowHeights(); this.List.forceUpdate(); }; render() { const {items} = this.state; return ( ); } } render(, document.getElementById('root')); ================================================ FILE: package.json ================================================ { "name": "react-sortable-hoc", "version": "2.0.0", "description": "Set of higher-order components to turn any list into a sortable, touch-friendly, animated list", "author": { "name": "Clauderic Demers", "email": "me@ced.io" }, "user": "clauderic", "homepage": "https://github.com/clauderic/react-sortable-hoc", "source": "src/index.js", "main": "dist/react-sortable-hoc.js", "umd:main": "dist/react-sortable-hoc.umd.js", "module": "dist/react-sortable-hoc.esm.js", "jsnext:main": "dist/react-sortable-hoc.esm.js", "types": "types/index.d.ts", "license": "MIT", "repository": { "type": "git", "url": "https://github.com/clauderic/react-sortable-hoc.git" }, "bugs": { "url": "https://github.com/clauderic/react-sortable-hoc/issues" }, "keywords": [ "react", "reactjs", "react-component", "sortable", "sortable-list", "list", "sortable list", "smooth", "animated", "hoc", "higher-order", "component" ], "scripts": { "start": "start-storybook -p 9001 -c .storybook", "build": "rollup -c", "test": "eslint src/** --ext .js --quiet", "release": "standard-version --no-verify" }, "husky": { "hooks": { "pre-commit": "pretty-quick --staged" } }, "dependencies": { "@babel/runtime": "^7.13.0", "invariant": "^2.2.4", "prop-types": "^15.5.7" }, "peerDependencies": { "prop-types": "^15.5.7", "react": "^16.3.0 || ^17.0.0", "react-dom": "^16.3.0 || ^17.0.0" }, "devDependencies": { "@babel/core": "^7.2.2", "@babel/plugin-proposal-class-properties": "^7.2.3", "@babel/plugin-transform-runtime": "^7.2.0", "@babel/preset-env": "^7.2.3", "@babel/preset-react": "^7.0.0", "@storybook/addon-options": "^5.1.11", "@storybook/react": "^5.1.11", "@storybook/theming": "^5.1.11", "array-move": "^1.0.0", "autoprefixer": "^6.3.6", "babel-loader": "^8.0.5", "babel-plugin-transform-async-to-promises": "^0.8.4", "classnames": "^2.2.5", "css-loader": "^2.1.0", "eslint": "^6.2.1", "eslint-config-prettier": "^6.1.0", "eslint-plugin-prettier": "^3.1.0", "eslint-plugin-shopify": "^30.0.1", "extract-text-webpack-plugin": "^1.0.1", "html-webpack-plugin": "^2.16.1", "husky": "^3.0.4", "lodash": "^4.12.0", "node-sass": "^4.11.0", "postcss": "^7.0.7", "postcss-loader": "^3.0.0", "prettier": "^1.18.2", "pretty-quick": "^1.11.1", "react": "^16.7.0", "react-addons-pure-render-mixin": "^15.0.2", "react-addons-shallow-compare": "^15.1.0", "react-addons-test-utils": "^15.1.0", "react-dom": "^16.7.0", "react-infinite": "^0.13.0", "react-inspector": "^3.0.2", "react-tiny-virtual-list": "^2.0.1", "react-virtualized": "^9.2.2", "react-window": "^1.6.2", "rollup": "^1.0.0", "rollup-plugin-babel": "^4.2.0", "rollup-plugin-commonjs": "^9.2.0", "rollup-plugin-filesize": "^6.0.0", "rollup-plugin-node-resolve": "^4.0.0", "rollup-plugin-replace": "^2.1.0", "rollup-plugin-uglify": "^6.0.0", "sass-loader": "^7.1.0", "standard-version": "^4.4.0", "style-loader": "^0.23.1" } } ================================================ FILE: rollup.config.js ================================================ import replace from 'rollup-plugin-replace'; import resolve from 'rollup-plugin-node-resolve'; import commonjs from 'rollup-plugin-commonjs'; import babel from 'rollup-plugin-babel'; import filesize from 'rollup-plugin-filesize'; import {uglify} from 'rollup-plugin-uglify'; import pkg from './package.json'; const external = (id) => !id.startsWith('.') && !id.startsWith('/'); const babelConfig = ( {useESModules, targets} = { useESModules: true, targets: {browsers: 'last 2 versions'}, }, ) => ({ comments: false, runtimeHelpers: true, presets: [ '@babel/preset-react', [ '@babel/preset-env', { targets, }, ], ], plugins: [ '@babel/plugin-proposal-class-properties', ['@babel/transform-runtime', {useESModules, regenerator: false}], ['babel-plugin-transform-async-to-promises', {inlineHelpers: true}], ], exclude: 'node_modules/**', }); const umdConfig = ({minify} = {}) => ({ input: pkg.source, external: ['react', 'react-dom', 'prop-types'], output: { name: 'SortableHOC', file: minify ? pkg["umd:main"].replace('.js', '.min.js') : pkg["umd:main"], format: 'umd', globals: { react: 'React', 'react-dom': 'ReactDOM', 'prop-types': 'PropTypes', }, }, plugins: [ resolve(), babel( babelConfig({ targets: {browsers: ['last 2 versions', 'safari >= 7']}, }), ), replace({ 'process.env.NODE_ENV': JSON.stringify( minify ? 'production' : 'development', ), }), commonjs(), minify ? uglify() : { }, filesize(), ], }); const rollupConfig = [ // Browser-friendly UMD builds umdConfig(), umdConfig({minify: true}), // CommonJS { input: pkg.source, external, output: [{file: pkg.main, format: 'cjs'}], plugins: [resolve(), babel(babelConfig({useESModules: false})), filesize()], }, // ES module { input: pkg.source, external, output: [{file: pkg.module, format: 'esm'}], plugins: [resolve(), babel(babelConfig()), filesize()], }, ]; export default rollupConfig; ================================================ FILE: src/.stories/Storybook.scss ================================================ @import url(https://fonts.googleapis.com/css?family=Montserrat:400); $focusedOutlineColor: #4c9ffe; .root { display: flex; height: 100%; box-sizing: border-box; flex-direction: column; justify-content: center; align-items: center; } // Base styles .list { width: 400px; height: 600px; overflow: auto; -webkit-overflow-scrolling: touch; border: 1px solid #999; } .item { position: relative; border-bottom: 1px solid #999; cursor: grab; touch-action: manipulation; &.sorting { pointer-events: none; } } .containsDragHandle { cursor: default; } // Stylized .stylizedList { position: relative; z-index: 0; background-color: #f3f3f3; border: 1px solid #efefef; border-radius: 3px; outline: none; } .stylizedItem { display: flex; align-items: center; width: 100%; padding: 0 20px; background-color: #fff; border-bottom: 1px solid #efefef; box-sizing: border-box; user-select: none; outline: none; color: #333; font-weight: 400; &:focus:not(.containsDragHandle) { text-indent: -2px; border: 2px solid $focusedOutlineColor; } } .disabled { cursor: not-allowed; opacity: 0.5; } // Drag handle .handleWrapper { width: 18px; height: 18px; outline: none; } .handle { display: block; width: 18px; height: 18px; margin-right: 20px; overflow: hidden; > svg { opacity: 0.3; } cursor: grab; } // Horizontal list .horizontalList { display: flex; width: 600px; height: 300px; white-space: nowrap; } .horizontalItem { display: flex; flex-shrink: 0; align-items: center; justify-content: center; width: 200px; border-right: 1px solid #efefef; border-bottom: 0; } // Grid .grid { display: grid; height: 130 * 3px + 20px; grid-gap: 10px; grid-template-columns: auto auto auto auto; width: auto; white-space: nowrap; border: 0; background-color: transparent; } .gridItem { width: 130px; height: 130px; padding: 0; border: none; background-color: transparent; .wrapper { display: flex; align-items: center; justify-content: center; width: 100%; height: 100%; background-color: #fff; transform: scale(1); font-size: 28px; span { display: none; } } } .gridVariableSized { .gridItem { &[data-index='0'] { width: auto !important; height: auto !important; grid-column-end: span 2; grid-row-end: span 2; } &.sorting { .wrapper { transition: transform 150ms ease-in-out; } } } } .gridItemVariableSized { &[data-index='0'] { .wrapper { font-size: 56px; } } } // Nested .category { height: auto; .categoryHeader { display: flex; flex-flow: row nowrap; align-items: center; padding: 10px 14px; background: #f9f9f9; border-bottom: 1px solid #efefef; user-select: none; } .categoryList { height: auto; } } // Divider .divider { padding: 10px 20px; background: #f9f9f9; border-bottom: 1px solid #efefef; text-transform: uppercase; font-size: 14px; color: #333; } // Helper styles .helper { box-shadow: 0 5px 5px -5px rgba(0, 0, 0, 0.2), 0 -5px 5px -5px rgba(0, 0, 0, 0.2); cursor: grabbing; } .stylizedHelper { &:not(.gridItem), &.gridItem .wrapper { border: 1px solid #efefef; box-shadow: 0 5px 5px -5px rgba(0, 0, 0, 0.2); background-color: rgba(255, 255, 255, 0.9); border-radius: 3px; &.horizontalItem { cursor: col-resize; } &:focus { box-shadow: 0 0px 5px 1px $focusedOutlineColor; } } &.gridItem .wrapper { transition: transform 150ms ease-in-out; } } .shrinkedHelper { height: 20px !important; } :global { body { font-family: 'Montserrat', 'Helvetica Neue', 'Helvetica', arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; background-color: rgba(#f4f5f9, 0.7); } html, body, #root { height: 100%; margin: 0; } } ================================================ FILE: src/.stories/grouping-items/Item/Item.js ================================================ import React from 'react'; import classNames from 'classnames'; import {sortableElement} from '../../../../src'; import styles from './Item.scss'; const ENTER_KEY = 13; function Item(props) { const { dragging, sorting, onClick, selected, selectedItemsCount, value, } = props; const shouldRenderItemCountBadge = dragging && selectedItemsCount > 1; return (
    onClick(value)} onKeyPress={(event) => { if (event.which === ENTER_KEY) { onClick(value); } }} tabIndex={0} > Item {value} {shouldRenderItemCountBadge ? : null}
    ); } function Badge(props) { return
    {props.count}
    ; } export default sortableElement(Item); ================================================ FILE: src/.stories/grouping-items/Item/Item.scss ================================================ $color: #333; $white: #fff; $backgroundColor: $white; $boxShadow: 0 5px 5px -5px rgba(0, 0, 0, 0.2); $fontWeight-regular: 400; $fontWeight-bold: 600; $borderRadius: 3px; $borderWidth: 1px; $borderColor: #efefef; $selectedColor: $white; $selectedBackgroundColor: rgba(216, 232, 251, 0.9); $selectedBorderColor: #bbcee8; $badgeColor: $white; $badgeBackgroundColor: #f75959; $badgeBorderColor: #da4553; $focusedOutlineColor: #4c9ffe; .Item { display: flex; align-items: center; width: 100%; height: 59px; padding: 0 20px; background-color: $backgroundColor; border-bottom: $borderWidth solid #efefef; box-sizing: border-box; user-select: none; outline: none; color: $color; font-weight: $fontWeight-regular; cursor: grab; &:last-child { border-bottom: none; } &.selected { background: $selectedBackgroundColor; border-bottom-color: $selectedBorderColor; &:focus { border-bottom-color: $focusedOutlineColor; } } &.sorting { pointer-events: none; } &.dragging { border-radius: $borderRadius; border: $borderWidth solid #efefef; box-shadow: $boxShadow; &:focus { box-shadow: 0 0px 5px 1px $focusedOutlineColor; } } &:focus { text-indent: -2px; border: 2px solid $focusedOutlineColor; } } .Badge { position: absolute; top: -8px; right: -8px; padding: 0.35em 0.5em; border-radius: 0.3rem; color: $badgeColor; font-size: 0.8em; font-weight: $fontWeight-bold; background-color: $badgeBackgroundColor; border: $borderWidth solid $badgeBorderColor; } ================================================ FILE: src/.stories/grouping-items/Item/index.js ================================================ import Item from './Item'; export default Item; ================================================ FILE: src/.stories/grouping-items/List/List.js ================================================ import React from 'react'; import {sortableContainer} from '../../../../src'; import Item from '../Item'; import styles from './List.scss'; function List({items, isSorting, selectedItems, sortingItemKey, onItemSelect}) { return (
    {items.map((value, index) => { const isSelected = selectedItems.includes(value); const itemIsBeingDragged = sortingItemKey === value; return ( ); })}
    ); } export default sortableContainer(List); ================================================ FILE: src/.stories/grouping-items/List/List.scss ================================================ $backgroundColor: #f3f3f3; $borderColor: #efefef; $borderWidth: 1px; .List { position: relative; width: 400px; height: 600px; overflow: auto; -webkit-overflow-scrolling: touch; z-index: 0; background-color: $backgroundColor; border: $borderWidth solid $borderColor; border-radius: 3px; outline: none; } ================================================ FILE: src/.stories/grouping-items/List/index.js ================================================ import List from './List'; export default List; ================================================ FILE: src/.stories/grouping-items/index.js ================================================ import React from 'react'; import arrayMove from 'array-move'; import {generateItems} from './utils'; import SortableList from './List'; class GroupedItems extends React.Component { state = { selectedItems: [], items: generateItems(50), }; render() { const {items, isSorting, selectedItems, sortingItemKey} = this.state; return ( ); } filterItems = (value) => { const {selectedItems, sortingItemKey, isSorting} = this.state; // Do not hide the ghost of the element currently being sorted if (sortingItemKey === value) { return true; } // Hide the other items that are selected if (isSorting && selectedItems.includes(value)) { return false; } // Do not hide any other items return true; }; handleUpdateBeforeSortStart = ({index}) => { return new Promise((resolve) => this.setState( ({items}) => ({ sortingItemKey: items[index], isSorting: true, }), resolve, ), ); }; handleSortStart() { document.body.style.cursor = 'grabbing'; } handleSortEnd = ({oldIndex, newIndex}) => { const {selectedItems} = this.state; let newItems; if (selectedItems.length) { const items = this.state.items.filter( (value) => !selectedItems.includes(value), ); newItems = [ ...items.slice(0, newIndex), ...selectedItems, ...items.slice(newIndex, items.length), ]; } else { newItems = arrayMove(this.state.items, oldIndex, newIndex); } this.setState({ items: newItems, isSorting: false, sortingItemKey: null, selectedItems: [], }); document.body.style.cursor = ''; }; handleItemSelect = (item) => { this.setState(({selectedItems}) => { if (selectedItems.includes(item)) { return { selectedItems: selectedItems.filter((value) => value !== item), }; } return { selectedItems: [...selectedItems, item], }; }); }; handleShouldCancelStart = (event) => { const {items, selectedItems} = this.state; const item = items[event.target.sortableInfo.index]; // Never cancel start if there are no selected items if (!selectedItems.length) { return false; } // If there are selected items, we want to cancel sorting // from starting when dragging elements that are not selected return !selectedItems.includes(item); }; } export default GroupedItems; ================================================ FILE: src/.stories/grouping-items/utils.js ================================================ export function generateItems(length) { return Array.from(Array(length), (_, index) => index.toString()); } ================================================ FILE: src/.stories/index.js ================================================ import React, {Component} from 'react'; import PropTypes from 'prop-types'; import ReactDOM from 'react-dom'; import {storiesOf} from '@storybook/react'; import style from './Storybook.scss'; import {SortableContainer, SortableElement, SortableHandle} from '../index'; import arrayMove from 'array-move'; import VirtualList from 'react-tiny-virtual-list'; import {FixedSizeList, VariableSizeList} from 'react-window'; import {defaultTableRowRenderer, Column, Table, List} from 'react-virtualized'; import '!style-loader!css-loader!react-virtualized/styles.css'; import Infinite from 'react-infinite'; import range from 'lodash/range'; import random from 'lodash/random'; import classNames from 'classnames'; import GroupedItems from './grouping-items'; import InteractiveElements from './interactive-elements-stress-test'; function getItems(count, height) { var heights = [65, 110, 140, 65, 90, 65]; return range(count).map((value) => { return { value, height: height == null ? heights[random(0, heights.length - 1)] : height, }; }); } const Handle = SortableHandle(({tabIndex}) => (
    )); const Item = SortableElement( ({ tabbable, className, isDisabled, height, style: propStyle, shouldUseDragHandle, value, itemIndex, isSorting, }) => { const bodyTabIndex = tabbable && !shouldUseDragHandle ? 0 : -1; const handleTabIndex = tabbable && shouldUseDragHandle ? 0 : -1; return (
    {shouldUseDragHandle && }
    Item {value}
    ); }, ); const SortableList = SortableContainer( ({ className, items, disabledItems = [], itemClass, isSorting, shouldUseDragHandle, type, }) => { return (
    {items.map(({value, height}, index) => { const disabled = disabledItems.includes(value); return ( ); })}
    ); }, ); class SortableListWithCustomContainer extends React.Component { state = { container: null, }; render() { const {container} = this.state; return (
    ); } setContainerNode = (node) => { this.setState({container: node}); }; } const Category = SortableElement((props) => { const tabIndex = props.tabbable ? 0 : -1; return (
    Category {props.value}
    ); }); class ListWrapper extends Component { state = { items: this.props.items, isSorting: false, }; static propTypes = { items: PropTypes.array, className: PropTypes.string, itemClass: PropTypes.string, width: PropTypes.number, height: PropTypes.number, onSortStart: PropTypes.func, onSortEnd: PropTypes.func, component: PropTypes.func, shouldUseDragHandle: PropTypes.bool, disabledItems: PropTypes.arrayOf(PropTypes.string), }; static defaultProps = { className: classNames(style.list, style.stylizedList), itemClass: classNames(style.item, style.stylizedItem), width: 400, height: 600, }; onSortStart = (sortEvent, nativeEvent) => { const {onSortStart} = this.props; this.setState({isSorting: true}); document.body.style.cursor = 'grabbing'; if (onSortStart) { onSortStart(sortEvent, nativeEvent, this.refs.component); } }; onSortEnd = (sortEvent, nativeEvent) => { const {onSortEnd} = this.props; const {oldIndex, newIndex} = sortEvent; const {items} = this.state; this.setState({ items: arrayMove(items, oldIndex, newIndex), isSorting: false, }); document.body.style.cursor = ''; if (onSortEnd) { onSortEnd(sortEvent, nativeEvent, this.refs.component); } }; render() { const Component = this.props.component; const {items, isSorting} = this.state; const props = { isSorting, items, onSortEnd: this.onSortEnd, onSortStart: this.onSortStart, ref: 'component', useDragHandle: this.props.shouldUseDragHandle, }; return ; } } const SortableReactWindow = (Component) => SortableContainer( class ReactWindowList extends React.Component { render() { const {className, items, itemHeight, height, width} = this.props; return ( items[index].height : itemHeight } itemCount={items.length} width={width} height={height} children={this.renderRow} /> ); } renderRow = ({index, style}) => { const {items, itemClass, isSorting} = this.props; const {value, height} = items[index]; return ( ); }; }, {withRef: true}, ); const SortableVirtualList = SortableContainer( ({className, items, height, width, itemHeight, itemClass, isSorting}) => { return ( items[index].height} estimatedItemSize={itemHeight} renderItem={({index, style}) => { const {value, height} = items[index]; return ( ); }} itemCount={items.length} width={width} height={height} /> ); }, ); // Function components cannot have refs, so we'll be using a class for React Virtualized class VirtualizedListWrapper extends Component { render() { const { className, items, height, width, itemHeight, itemClass, isSorting, } = this.props; return ( items[index].height} estimatedRowSize={itemHeight} rowRenderer={({index, style}) => { const {value, height} = items[index]; return ( ); }} rowCount={items.length} width={width} height={height} /> ); } } const SortableVirtualizedList = SortableContainer(VirtualizedListWrapper, { withRef: true, }); const SortableTable = SortableContainer(Table, {withRef: true}); const SortableRowRenderer = SortableElement(defaultTableRowRenderer); class TableWrapper extends Component { static propTypes = { items: PropTypes.array, className: PropTypes.string, helperClass: PropTypes.string, itemClass: PropTypes.string, width: PropTypes.number, height: PropTypes.number, itemHeight: PropTypes.number, onSortEnd: PropTypes.func, }; render() { const { className, height, helperClass, itemClass, itemHeight, items, onSortEnd, width, } = this.props; return ( ReactDOM.findDOMNode(wrappedInstance.Grid) } gridClassName={className} headerHeight={itemHeight} height={height} helperClass={helperClass} onSortEnd={onSortEnd} rowClassName={itemClass} rowCount={items.length} rowGetter={({index}) => items[index]} rowHeight={itemHeight} rowRenderer={(props) => } width={width} > ); } } const SortableInfiniteList = SortableContainer( ({className, items, itemClass, isSorting}) => { return ( height)} // for react-infinite, a larger preload is better for keyboard sorting preloadBatchSize={Infinite.containerHeightScaleFactor(2)} preloadAdditionalHeight={Infinite.containerHeightScaleFactor(2)} > {items.map(({value, height}, index) => ( ))} ); }, ); const ShrinkingSortableList = SortableContainer( ({className, isSorting, items, itemClass, shouldUseDragHandle}) => { return (
    {items.map(({value, height}, index) => ( ))}
    ); }, ); const NestedSortableList = SortableContainer( ({className, items, isSorting}) => { return (
    {items.map((value, index) => ( ))}
    ); }, ); storiesOf('General | Layout / Vertical list', module) .add('Basic setup', () => { return (
    ); }) .add('Variable heights', () => { return (
    ); }) .add('Nested Lists', () => { return (
    ); }); storiesOf('General | Layout / Horizontal list', module).add( 'Basic setup', () => { return (
    ); }, ); storiesOf('General | Layout / Grid', module) .add('Basic setup', () => { const transformOrigin = { x: 0, y: 0, }; return (
    ); }) .add('Large first item', () => { return (
    { const nodeBoundingClientRect = node.getBoundingClientRect(); const helperWrapperNode = helper.childNodes[0]; const transformOrigin = { x: ((event.clientX - nodeBoundingClientRect.left) / nodeBoundingClientRect.width) * 100, y: ((event.clientY - nodeBoundingClientRect.top) / nodeBoundingClientRect.height) * 100, }; helperWrapperNode.style.transformOrigin = `${transformOrigin.x}% ${transformOrigin.y}%`; }} onSortOver={({nodes, newIndex, index, helper}) => { const finalNodes = arrayMove(nodes, index, newIndex); const oldNode = nodes[index].node; const newNode = nodes[newIndex].node; const helperScale = newNode.offsetWidth / oldNode.offsetWidth; const helperWrapperNode = helper.childNodes[0]; helperWrapperNode.style.transform = `scale(${helperScale})`; finalNodes.forEach(({node}, i) => { const oldNode = nodes[i].node; const scale = oldNode.offsetWidth / node.offsetWidth; const wrapperNode = node.querySelector(`.${style.wrapper}`); wrapperNode.style.transform = `scale(${scale})`; wrapperNode.style.transformOrigin = newIndex > i ? '0 0' : '100% 0'; }); }} onSortEnd={({nodes}) => { nodes.forEach(({node}) => { const wrapperNode = node.querySelector(`.${style.wrapper}`); wrapperNode.style.transform = ''; }); }} />
    ); }); storiesOf('General | Configuration / Options', module) .add('Drag handle', () => { return (
    ); }) .add('Disabled items', () => { return (
    ); }) .add('Press delay (200ms)', () => { return (
    ); }) .add('Distance (20px)', () => { return (
    ); }) .add('Lock axis', () => { return (
    ); }) .add('Window as scroll container', () => { return ( ); }) .add('Custom sortable helper container', () => { return (
    ); }); storiesOf('General | Configuration / Customization', module) .add('Minimal styling', () => { return (
    ); }) .add('Transition duration', () => { return (
    ); }) .add('Disable transitions', () => { return (
    ); }); storiesOf( 'Advanced examples | Virtualization libraries / react-tiny-virtual-list', module, ) .add('Basic setup', () => { return (
    ); }) .add('Variable heights', () => { return (
    ); }); storiesOf('Advanced examples | Virtualization libraries / react-window', module) .add('Basic setup', () => { return (
    { // We need to inform React Window that the order of the items has changed const instance = ref.getWrappedInstance(); const list = instance.refs.VirtualList; list.forceUpdate(); }} />
    ); }) .add('Variable heights', () => { return (
    { // We need to inform React Window that the item heights have changed const instance = ref.getWrappedInstance(); const list = instance.refs.VirtualList; list.resetAfterIndex(0); }} />
    ); }); storiesOf( 'Advanced examples | Virtualization libraries / react-virtualized', module, ) .add('Basic setup', () => { return (
    ); }) .add('Variable heights', () => { return (
    { // We need to inform React Virtualized that the item heights have changed const instance = ref.getWrappedInstance(); const list = instance.refs.VirtualList; list.recomputeRowHeights(); instance.forceUpdate(); }} />
    ); }) .add('Table', () => { return (
    ); }); storiesOf( 'Advanced examples | Virtualization libraries / react-infinite', module, ) .add('Basic setup', () => { return (
    ); }) .add('Variable heights', () => { return (
    ); }); storiesOf('Advanced examples | Re-rendering before sorting', module) .add('Grouping items', () => (
    )) .add('Elements that shrink', () => { const getHelperDimensions = ({node}) => ({ height: 20, width: node.offsetWidth, }); return (
    ); }); storiesOf('Stress Testing | Nested elements', module).add( 'Interactive elements', () => (
    ), ); ================================================ FILE: src/.stories/interactive-elements-stress-test/Item/Item.js ================================================ import React from 'react'; import {sortableElement} from '../../../../src'; import styles from './Item.scss'; function Item(props) { const {children} = props; return (
    {children}
    ); } export default sortableElement(Item); ================================================ FILE: src/.stories/interactive-elements-stress-test/Item/Item.scss ================================================ $color: #333; $white: #fff; $backgroundColor: $white; $padding: 20px; $boxShadow: 0 5px 5px -5px rgba(0, 0, 0, 0.2); $fontWeight-regular: 400; $fontWeight-bold: 600; $borderRadius: 3px; $borderWidth: 1px; $borderColor: #efefef; $focusedOutlineColor: #4c9ffe; .root { display: block; width: 250px; padding: $padding; background-color: $backgroundColor; border-bottom: $borderWidth solid #efefef; box-sizing: border-box; user-select: none; color: $color; font-family: sans-serif; font-weight: $fontWeight-regular; > * { display: block; width: 100%; font-size: 14px; } > input, > textarea { padding: 5px; border: 1px solid #e0e0e0; box-sizing: border-box; } label { input { margin-right: 0.5em; } } &:focus { outline: none; padding: $padding - 2px; padding-bottom: $padding - 1px; border: 2px solid $focusedOutlineColor; } &.dragging { &:focus { box-shadow: 0 0px 5px 1px $focusedOutlineColor; } } } ================================================ FILE: src/.stories/interactive-elements-stress-test/Item/index.js ================================================ import Item from './Item'; export default Item; ================================================ FILE: src/.stories/interactive-elements-stress-test/List.js ================================================ import React from 'react'; import {sortableContainer} from '../../../src'; import Item from './Item'; function List({items}) { return (
    {items.map(([key, children], index) => { return ( {children} ); })}
    ); } export default sortableContainer(List); ================================================ FILE: src/.stories/interactive-elements-stress-test/index.js ================================================ import React from 'react'; import arrayMove from 'array-move'; import SortableList from './List'; import ItemStyles from './Item/Item.scss'; const items = { input: , textarea: