Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 79 additions & 73 deletions docs/examples/MultiSelectSort.tsx
Original file line number Diff line number Diff line change
@@ -1,92 +1,98 @@
import React, { MouseEventHandler } from 'react';

import React, { MouseEventHandler, useCallback } from 'react';
import Select, {
components,
MultiValueGenericProps,
MultiValueProps,
MultiValueRemoveProps,
OnChangeValue,
Props,
} from 'react-select';
import {
SortableContainer,
SortableContainerProps,
SortableElement,
SortEndHandler,
SortableHandle,
} from 'react-sortable-hoc';
import { ColourOption, colourOptions } from '../data';
import { closestCenter, DndContext, DragEndEvent } from '@dnd-kit/core';
import { restrictToParentElement } from '@dnd-kit/modifiers';
import {
arrayMove,
horizontalListSortingStrategy,
SortableContext,
useSortable,
} from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';

function arrayMove<T>(array: readonly T[], from: number, to: number) {
const slicedArray = array.slice();
slicedArray.splice(
to < 0 ? array.length + to : to,
0,
slicedArray.splice(from, 1)[0]
);
return slicedArray;
}

const SortableMultiValue = SortableElement(
(props: MultiValueProps<ColourOption>) => {
// this prevents the menu from being opened/closed when the user clicks
// on a value to begin dragging it. ideally, detecting a click (instead of
// a drag) would still focus the control and toggle the menu, but that
// requires some magic with refs that are out of scope for this example
const onMouseDown: MouseEventHandler<HTMLDivElement> = (e) => {
e.preventDefault();
e.stopPropagation();
};
const innerProps = { ...props.innerProps, onMouseDown };
return <components.MultiValue {...props} innerProps={innerProps} />;
}
);
const MultiValue = (props: MultiValueProps<ColourOption>) => {
const onMouseDown: MouseEventHandler<HTMLDivElement> = (e) => {
e.preventDefault();
e.stopPropagation();
};
const innerProps = { ...props.innerProps, onMouseDown };
const { attributes, listeners, setNodeRef, transform, transition } =
useSortable({
id: props.data.value,
});
const style = {
transform: CSS.Transform.toString(transform),
transition,
};

const SortableMultiValueLabel = SortableHandle(
(props: MultiValueGenericProps) => <components.MultiValueLabel {...props} />
);
return (
<div style={style} ref={setNodeRef} {...attributes} {...listeners}>
<components.MultiValue {...props} innerProps={innerProps} />
</div>
);
};

const SortableSelect = SortableContainer(Select) as React.ComponentClass<
Props<ColourOption, true> & SortableContainerProps
>;
const MultiValueRemove = (props: MultiValueRemoveProps<ColourOption>) => {
return (
<components.MultiValueRemove
{...props}
innerProps={{
onPointerDown: (e) => e.stopPropagation(),
...props.innerProps,
}}
/>
);
};

export default function MultiSelectSort() {
const [selected, setSelected] = React.useState<readonly ColourOption[]>([
const MultiSelectSort = () => {
const [selected, setSelected] = React.useState<ColourOption[]>([
colourOptions[4],
colourOptions[5],
]);

const onChange = (selectedOptions: OnChangeValue<ColourOption, true>) =>
setSelected(selectedOptions);
setSelected([...selectedOptions]);

const onSortEnd: SortEndHandler = ({ oldIndex, newIndex }) => {
const newValue = arrayMove(selected, oldIndex, newIndex);
setSelected(newValue);
console.log(
'Values sorted:',
newValue.map((i) => i.value)
);
};
const onDragEnd = useCallback((event: DragEndEvent) => {
const { active, over } = event;

if (!active || !over) return;

setSelected((items) => {
const oldIndex = items.findIndex((item) => item.value === active.id);
const newIndex = items.findIndex((item) => item.value === over.id);
return arrayMove(items, oldIndex, newIndex);
});
}, [setSelected]);

return (
<SortableSelect
useDragHandle
// react-sortable-hoc props:
axis="xy"
onSortEnd={onSortEnd}
distance={4}
// small fix for https://github.com/clauderic/react-sortable-hoc/pull/352:
getHelperDimensions={({ node }) => node.getBoundingClientRect()}
// react-select props:
isMulti
options={colourOptions}
value={selected}
onChange={onChange}
components={{
// @ts-ignore We're failing to provide a required index prop to SortableElement
MultiValue: SortableMultiValue,
MultiValueLabel: SortableMultiValueLabel,
}}
closeMenuOnSelect={false}
/>
<DndContext modifiers={[restrictToParentElement]} onDragEnd={onDragEnd} collisionDetection={closestCenter}>
<SortableContext
items={selected.map((o) => o.value)}
strategy={horizontalListSortingStrategy}
>
<Select
distance={4}
isMulti
options={colourOptions}
value={selected}
onChange={onChange}
components={{
// @ts-ignore We're failing to provide a required index prop to SortableElement
MultiValue,
MultiValueRemove,
}}
closeMenuOnSelect={false}
/>
</SortableContext>
</DndContext>
);
}
};

export default MultiSelectSort;
2 changes: 1 addition & 1 deletion docs/pages/advanced/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export default function Advanced() {
)}

## Sortable MultiSelect
Using the [react-sortable-hoc](https://www.npmjs.com/package/react-sortable-hoc) package we can easily allow sorting of MultiSelect values by drag and drop.
Using the [DnD-Kit](https://docs.dndkit.com) package we can allow sorting of MultiSelect values by drag and drop.

${(
<ExampleWrapper
Expand Down
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,12 @@
"@babel/runtime": "^7.12.0",
"@changesets/cli": "^2.17.0",
"@changesets/get-github-info": "^0.5.0",
"@dnd-kit/core": "^6.0.3",
"@dnd-kit/modifiers": "^6.0.0",
"@dnd-kit/sortable": "^7.0.0",
"@dnd-kit/utilities": "^3.2.0",
"@emotion/babel-plugin": "^11.10.2",
"@emotion/cache": "^11.4.0",
"@emotion/jest": "^11.1.0",
"@manypkg/cli": "^0.19.2",
"@preconstruct/cli": "^2.6.2",
Expand Down
39 changes: 39 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1571,6 +1571,45 @@
resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70"
integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==

"@dnd-kit/accessibility@^3.0.0":
version "3.0.1"
resolved "https://registry.yarnpkg.com/@dnd-kit/accessibility/-/accessibility-3.0.1.tgz#3ccbefdfca595b0a23a5dc57d3de96bc6935641c"
integrity sha512-HXRrwS9YUYQO9lFRc/49uO/VICbM+O+ZRpFDe9Pd1rwVv2PCNkRiTZRdxrDgng/UkvdC3Re9r2vwPpXXrWeFzg==
dependencies:
tslib "^2.0.0"

"@dnd-kit/core@^6.0.3":
version "6.0.7"
resolved "https://registry.yarnpkg.com/@dnd-kit/core/-/core-6.0.7.tgz#eb9bcb2ab5cfba3c5e8d5dc64b61e5e9cc5567dc"
integrity sha512-qcLBTVTjmLuLqC0RHQ+dFKN5neWmAI56H9xZ+he9WEJEkAvR76YAcz7DSWDJfjErepfG2H3Fkb9lYiX7cPR62g==
dependencies:
"@dnd-kit/accessibility" "^3.0.0"
"@dnd-kit/utilities" "^3.2.1"
tslib "^2.0.0"

"@dnd-kit/modifiers@^6.0.0":
version "6.0.1"
resolved "https://registry.yarnpkg.com/@dnd-kit/modifiers/-/modifiers-6.0.1.tgz#9e39b25fd6e323659604cc74488fe044d33188c8"
integrity sha512-rbxcsg3HhzlcMHVHWDuh9LCjpOVAgqbV78wLGI8tziXY3+qcMQ61qVXIvNKQFuhj75dSfD+o+PYZQ/NUk2A23A==
dependencies:
"@dnd-kit/utilities" "^3.2.1"
tslib "^2.0.0"

"@dnd-kit/sortable@^7.0.0":
version "7.0.2"
resolved "https://registry.yarnpkg.com/@dnd-kit/sortable/-/sortable-7.0.2.tgz#791d550872457f3f3c843e00d159b640f982011c"
integrity sha512-wDkBHHf9iCi1veM834Gbk1429bd4lHX4RpAwT0y2cHLf246GAvU2sVw/oxWNpPKQNQRQaeGXhAVgrOl1IT+iyA==
dependencies:
"@dnd-kit/utilities" "^3.2.0"
tslib "^2.0.0"

"@dnd-kit/utilities@^3.2.0", "@dnd-kit/utilities@^3.2.1":
version "3.2.1"
resolved "https://registry.yarnpkg.com/@dnd-kit/utilities/-/utilities-3.2.1.tgz#53f9e2016fd2506ec49e404c289392cfff30332a"
integrity sha512-OOXqISfvBw/1REtkSK2N3Fi2EQiLMlWUlqnOK/UpOISqBZPWpE6TqL+jcPtMOkE8TqYGiURvRdPSI9hltNUjEA==
dependencies:
tslib "^2.0.0"

"@dsherret/to-absolute-glob@^2.0.2":
version "2.0.2"
resolved "https://registry.yarnpkg.com/@dsherret/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz#1f6475dc8bd974cea07a2daf3864b317b1dd332c"
Expand Down