diff --git a/packages/ui-default/components/autocomplete/components/AutoComplete.tsx b/packages/ui-default/components/autocomplete/components/AutoComplete.tsx index a3357fea..02dd6358 100644 --- a/packages/ui-default/components/autocomplete/components/AutoComplete.tsx +++ b/packages/ui-default/components/autocomplete/components/AutoComplete.tsx @@ -3,8 +3,10 @@ import React, { forwardRef, useState, useRef, useImperativeHandle, useEffect, } from 'react'; +import { DndProvider, useDrag, useDrop } from 'react-dnd'; +import { HTML5Backend } from 'react-dnd-html5-backend'; import PropTypes from 'prop-types'; -import { debounce } from 'lodash'; +import { debounce, uniqueId } from 'lodash'; import Icon from 'vj/components/react/IconComponent'; export interface AutoCompleteProps { @@ -25,6 +27,7 @@ export interface AutoCompleteProps { itemKey?: (item: Item) => string; onChange?: (value: string) => any; multi?: boolean; + draggable?: boolean; selectedKeys?: string[]; allowEmptyQuery?: boolean; freeSolo?: boolean; @@ -51,6 +54,30 @@ export interface AutoCompleteHandle { const superCache = {}; +function DraggableSelection({ + type, id, move, children, ...props +}) { + const ref = useRef(null); + const [isDragging, drag] = useDrag(() => ({ + type, + item: id, + collect: (m) => m.isDragging(), + })); + const [, drop] = useDrop({ + accept: type, + hover: (item: string) => { + if (!ref.current) return; + move(item, id); + }, + }); + drag(drop(ref)); + return ( +
+ {children} +
+ ); +} + // eslint-disable-next-line prefer-arrow-callback const AutoComplete = forwardRef(function Impl(props: AutoCompleteProps, ref: React.Ref>) { const width = props.width ?? '100%'; @@ -64,6 +91,7 @@ const AutoComplete = forwardRef(function Impl(props: AutoCompleteProps, re const itemKey = props.itemKey ?? itemText; const onChange = props.onChange ?? (() => { }); const multi = props.multi ?? false; + const draggable = props.draggable ?? props.multi; const allowEmptyQuery = props.allowEmptyQuery ?? false; const freeSolo = props.freeSolo ?? false; const freeSoloConverter = freeSolo ? props.freeSoloConverter ?? ((i) => i) : ((i) => i); @@ -74,6 +102,7 @@ const AutoComplete = forwardRef(function Impl(props: AutoCompleteProps, re const [itemList, setItemList] = useState([]); // items list const [currentItem, setCurrentItem] = useState(null); // index of current item (in item list) const [rerender, setRerender] = useState(false); + const [draggableId] = useState(uniqueId()); const inputRef = useRef(); const listRef = useRef(); @@ -224,45 +253,59 @@ const AutoComplete = forwardRef(function Impl(props: AutoCompleteProps, re }, }), [selected, selectedKeys, inputRef, multi]); + const move = (dragId: string, hoverId: string) => { + if (dragId === hoverId || !draggable) return; + const dragIndex = selectedKeys.indexOf(dragId); + const hoverIndex = selectedKeys.indexOf(hoverId); + setSelectedKeys((s) => { + const a = [...s]; + a.splice(dragIndex, 1); + a.splice(hoverIndex, 0, s[dragIndex]); + return a; + }); + }; + return (
- {multi && selectedKeys.map((key) => { - const item = valueCache[key]; - return ( -
-
{item ? itemText(item) : key}
- toggleItem(item, key)} /> -
- ); - })} - {disabled && ( + + {multi && selectedKeys.map((key) => { + const item = valueCache[key]; + return ( + +
{item ? itemText(item) : key}
+ toggleItem(item, key)} /> +
+ ); + })} { + dispatchChange(); + handleInputChange(e); + }} + onFocus={() => { + if (allowEmptyQuery) handleInputChange(); + setFocused(true); + }} + onBlur={() => setFocused(false)} + onKeyDown={handleInputKeyDown} + defaultValue={multi ? '' : selectedKeys.join(',')} /> - )} +
+
+ {disabled && ( { - dispatchChange(); - handleInputChange(e); - }} - onFocus={() => { - if (allowEmptyQuery) handleInputChange(); - setFocused(true); - }} - onBlur={() => setFocused(false)} - onKeyDown={handleInputKeyDown} - defaultValue={multi ? '' : selectedKeys.join(',')} + value={disabledHint} /> -
+ )} {focused && itemList.length > 0 && (
    e.preventDefault()}> {itemList.map((item, idx) => ( diff --git a/packages/ui-default/package.json b/packages/ui-default/package.json index 3d7558df..62e8320a 100644 --- a/packages/ui-default/package.json +++ b/packages/ui-default/package.json @@ -1,6 +1,6 @@ { "name": "@hydrooj/ui-default", - "version": "4.34.35", + "version": "4.34.36", "author": "undefined ", "license": "AGPL-3.0", "main": "hydro.js", @@ -77,6 +77,8 @@ "queue-microtask": "^1.2.3", "raw-loader": "^4.0.2", "react": "^17.0.2", + "react-dnd": "^16.0.0", + "react-dnd-html5-backend": "^16.0.0", "react-dom": "^17.0.2", "react-redux": "^7.2.8", "react-split-pane": "^0.1.92",