diff --git a/packages/pl-fe/src/components/ui/hstack/hstack.tsx b/packages/pl-fe/src/components/ui/hstack/hstack.tsx index 65a5b03d2..d97a5dd7a 100644 --- a/packages/pl-fe/src/components/ui/hstack/hstack.tsx +++ b/packages/pl-fe/src/components/ui/hstack/hstack.tsx @@ -31,7 +31,10 @@ const spaces = { 8: 'gap-8', }; -interface IHStack extends Pick, 'children' | 'className' | 'onClick' | 'style' | 'title'> { +interface IHStack extends Pick< + React.HTMLAttributes, + 'children' | 'className' | 'draggable' | 'onClick' | 'onDragEnd' | 'onDragEnter' | 'onDragStart' | 'style' | 'title' +> { /** Vertical alignment of children. */ alignItems?: keyof typeof alignItemsOptions; /** Horizontal alignment of children. */ diff --git a/packages/pl-fe/src/components/ui/streamfield/streamfield.tsx b/packages/pl-fe/src/components/ui/streamfield/streamfield.tsx index 1345d33bb..66fee9649 100644 --- a/packages/pl-fe/src/components/ui/streamfield/streamfield.tsx +++ b/packages/pl-fe/src/components/ui/streamfield/streamfield.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useRef } from 'react'; import { useIntl, defineMessages } from 'react-intl'; import Button from '../button/button'; @@ -39,6 +39,8 @@ interface IStreamfield { minItems?: number; /** Maximum number of allowed inputs. */ maxItems?: number; + /** Allow changing order of the items. */ + draggable?: boolean; } /** List of inputs that can be added or removed. */ @@ -52,9 +54,29 @@ const Streamfield: React.FC = ({ component: Component, maxItems = Infinity, minItems = 0, + draggable, }) => { const intl = useIntl(); + const dragItem = useRef(); + const dragOverItem = useRef(); + + const handleDragStart = (i: number) => () => { + dragItem.current = i; + }; + + const handleDragEnter = (i: number) => () => { + dragOverItem.current = i; + }; + + const handleDragEnd = () => { + const newData = [...values]; + const item = newData.splice(dragItem.current!, 1)[0]; + newData.splice(dragOverItem.current!, 0, item); + + onChange(newData); + }; + const handleChange = (i: number) => (value: any) => { const newData = [...values]; newData[i] = value; @@ -71,7 +93,15 @@ const Streamfield: React.FC = ({ {(values.length > 0) && ( {values.map((value, i) => value?._destroy ? null : ( - + { onRemoveItem={handleRemoveField} component={ProfileField} maxItems={maxFields} + draggable /> )} diff --git a/packages/pl-fe/src/features/pl-fe-config/index.tsx b/packages/pl-fe/src/features/pl-fe-config/index.tsx index d528ac9b6..f682691c7 100644 --- a/packages/pl-fe/src/features/pl-fe-config/index.tsx +++ b/packages/pl-fe/src/features/pl-fe-config/index.tsx @@ -288,6 +288,7 @@ const PlFeConfig: React.FC = () => { onChange={handleStreamItemChange(['promoPanel', 'items'])} onAddItem={addStreamItem(['promoPanel', 'items'], templates.promoPanel)} onRemoveItem={deleteStreamItem(['promoPanel', 'items'])} + draggable /> { onChange={handleStreamItemChange(['navlinks', 'homeFooter'])} onAddItem={addStreamItem(['navlinks', 'homeFooter'], templates.footerItem)} onRemoveItem={deleteStreamItem(['navlinks', 'homeFooter'])} + draggable /> @@ -347,6 +349,7 @@ const PlFeConfig: React.FC = () => { onChange={handleStreamItemChange(['cryptoAddresses'])} onAddItem={addStreamItem(['cryptoAddresses'], templates.cryptoAddress)} onRemoveItem={deleteStreamItem(['cryptoAddresses'])} + draggable />