diff --git a/package.json b/package.json index 9636a22..750d919 100644 --- a/package.json +++ b/package.json @@ -60,6 +60,9 @@ "@commercetools-uikit/text-input": "^18.4.0", "@commercetools-uikit/time-input": "^18.4.0", "@commercetools/sync-actions": "^5.15.0", + "@dnd-kit/core": "^6.3.1", + "@dnd-kit/modifiers": "^9.0.0", + "@dnd-kit/sortable": "^10.0.0", "@formatjs/cli": "^6.2.7", "@jest/types": "27.5.1", "@manypkg/cli": "^0.21.3", diff --git a/src/components/custom-object-form/attribute-field.tsx b/src/components/custom-object-form/attribute-field.tsx index cb601c6..d4e3745 100644 --- a/src/components/custom-object-form/attribute-field.tsx +++ b/src/components/custom-object-form/attribute-field.tsx @@ -9,11 +9,27 @@ import Card from '@commercetools-uikit/card'; import Constraints from '@commercetools-uikit/constraints'; import { BinLinearIcon, PlusBoldIcon } from '@commercetools-uikit/icons'; import Spacings from '@commercetools-uikit/spacings'; +import { + closestCenter, + DndContext, + DragEndEvent, + KeyboardSensor, + PointerSensor, + useSensor, + useSensors, +} from '@dnd-kit/core'; +import { + arrayMove, + SortableContext, + sortableKeyboardCoordinates, + verticalListSortingStrategy, +} from '@dnd-kit/sortable'; import { TYPES } from '../../constants'; import { getValueByType } from '../../helpers'; import AttributeLabel from './attribute-label'; import AttributeInput from './attribute-input'; import messages from './messages'; +import { SortableItem } from './sortable-item'; import styles from './attribute-field.module.css'; type Props = { @@ -67,73 +83,110 @@ const AttributeField: FC = ({ const selectOptions = type === TYPES.LocalizedEnum ? options?.map((option) => { - return { - value: option.value, - label: option.label[dataLocale], - }; - }) + return { + value: option.value, + label: option.label[dataLocale], + }; + }) : options; + const sensors = useSensors( + useSensor(PointerSensor), + useSensor(KeyboardSensor, { + coordinateGetter: sortableKeyboardCoordinates, + }) + ); + return ( <> {isSet ? ( ( - - - - } - label={intl.formatMessage(messages.addLabel)} - onClick={() => push(emptyValue)} + render={({ push, remove, form }) => { + const handleDragEnd = (event: DragEndEvent) => { + const { active, over } = event; + if (active.id !== over?.id) { + const oldIndex = active.data.current?.sortable.index; + const newIndex = over?.data.current?.sortable.index; + + if ( + typeof oldIndex === 'number' && + typeof newIndex === 'number' + ) { + const updated = arrayMove(value, oldIndex, newIndex); + form.setFieldValue(name, updated); + } + } + }; + + return ( + + - - {value.map((val: any, index: number) => ( - + } + label={intl.formatMessage(messages.addLabel)} + onClick={() => push(emptyValue)} + /> + + - -
- -
- } - label={intl.formatMessage(messages.removeLabel)} - isDisabled={index === 0 && value.length === 1} - onClick={() => remove(index)} - /> -
-
- ))} -
- )} + `${i}`)} + strategy={verticalListSortingStrategy} + > + {value.map((val: any, index: number) => ( + + + +
+ +
+ } + label={intl.formatMessage(messages.removeLabel)} + isDisabled={index === 0 && value.length === 1} + onClick={() => remove(index)} + /> +
+
+
+ ))} +
+ + + ); + }} /> ) : ( diff --git a/src/components/custom-object-form/sortable-item.tsx b/src/components/custom-object-form/sortable-item.tsx new file mode 100644 index 0000000..ea9287d --- /dev/null +++ b/src/components/custom-object-form/sortable-item.tsx @@ -0,0 +1,30 @@ +import { useSortable } from '@dnd-kit/sortable'; +import { CSS } from '@dnd-kit/utilities'; +import { ViewGridPlusIcon } from '@commercetools-uikit/icons'; + +type SortableItemProps = { + id: string; + children: React.ReactNode; +}; + +export const SortableItem = ({ id, children }: SortableItemProps) => { + const { attributes, listeners, setNodeRef, transform } = + useSortable({ id }); + + const style = { + transform: CSS.Transform.toString(transform), + transition: 'transform 200ms ease', + width: '100%', + }; + + return ( +
+
+ + + +
{children}
+
+
+ ); +}; diff --git a/yarn.lock b/yarn.lock index a44d9d6..ae39eb4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3070,6 +3070,45 @@ resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== +"@dnd-kit/accessibility@^3.1.1": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@dnd-kit/accessibility/-/accessibility-3.1.1.tgz#3b4202bd6bb370a0730f6734867785919beac6af" + integrity sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw== + dependencies: + tslib "^2.0.0" + +"@dnd-kit/core@^6.3.1": + version "6.3.1" + resolved "https://registry.yarnpkg.com/@dnd-kit/core/-/core-6.3.1.tgz#4c36406a62c7baac499726f899935f93f0e6d003" + integrity sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ== + dependencies: + "@dnd-kit/accessibility" "^3.1.1" + "@dnd-kit/utilities" "^3.2.2" + tslib "^2.0.0" + +"@dnd-kit/modifiers@^9.0.0": + version "9.0.0" + resolved "https://registry.yarnpkg.com/@dnd-kit/modifiers/-/modifiers-9.0.0.tgz#96a0280c77b10c716ef79d9792ce7ad04370771d" + integrity sha512-ybiLc66qRGuZoC20wdSSG6pDXFikui/dCNGthxv4Ndy8ylErY0N3KVxY2bgo7AWwIbxDmXDg3ylAFmnrjcbVvw== + dependencies: + "@dnd-kit/utilities" "^3.2.2" + tslib "^2.0.0" + +"@dnd-kit/sortable@^10.0.0": + version "10.0.0" + resolved "https://registry.yarnpkg.com/@dnd-kit/sortable/-/sortable-10.0.0.tgz#1f9382b90d835cd5c65d92824fa9dafb78c4c3e8" + integrity sha512-+xqhmIIzvAYMGfBYYnbKuNicfSsk4RksY2XdmJhT+HAC01nix6fHCztU68jooFiMUB01Ky3F0FyOvhG/BZrWkg== + dependencies: + "@dnd-kit/utilities" "^3.2.2" + tslib "^2.0.0" + +"@dnd-kit/utilities@^3.2.2": + version "3.2.2" + resolved "https://registry.yarnpkg.com/@dnd-kit/utilities/-/utilities-3.2.2.tgz#5a32b6af356dc5f74d61b37d6f7129a4040ced7b" + integrity sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg== + dependencies: + tslib "^2.0.0" + "@emotion/babel-plugin-jsx-pragmatic@^0.3.0": version "0.3.0" resolved "https://registry.yarnpkg.com/@emotion/babel-plugin-jsx-pragmatic/-/babel-plugin-jsx-pragmatic-0.3.0.tgz#12bde56c351f5981e5de66c99e62c371df6c42ca"