From 19471c3f70920096106e23a5195fc7020b3408c6 Mon Sep 17 00:00:00 2001 From: undefined Date: Sun, 22 Jun 2025 04:19:31 +0800 Subject: [PATCH] ui: problem_main: support copy ids --- .../frontend/autocomplete/AutoComplete.tsx | 24 +++---------------- .../ui-default/pages/problem_main.page.tsx | 15 ++++++++++++ 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/packages/components/frontend/autocomplete/AutoComplete.tsx b/packages/components/frontend/autocomplete/AutoComplete.tsx index 0fcaae7d5..693bc2a18 100644 --- a/packages/components/frontend/autocomplete/AutoComplete.tsx +++ b/packages/components/frontend/autocomplete/AutoComplete.tsx @@ -38,17 +38,13 @@ export interface AutoCompleteProps { export interface AutoCompleteHandle { getSelectedItems: () => Item[]; getSelectedItemKeys: () => string[]; - getSelectedItemsAsString: () => string; setSelectedItems: (items: Item[]) => void; getQuery: () => string; setQuery: (query: string) => void; setSelectedKeys: (keys: string[]) => void; triggerQuery: () => any; closeList: () => void; - /** returns comma seperated value text */ getValue: () => string; - /** return value detail */ - getValueArray: () => Item[]; clear: () => void; focus: () => void; } @@ -94,7 +90,6 @@ const AutoComplete = forwardRef(function Impl(props: AutoCompleteProps, re const freeSoloConverter = freeSolo ? props.freeSoloConverter ?? ((i) => i) : ((i) => i); const [focused, setFocused] = useState(false); // is focused - const [selected, setSelected] = useState([]); // selected items const [selectedKeys, setSelectedKeys] = useState(props.selectedKeys || []); // keys of selected items const [itemList, setItemList] = useState([]); // items list const [currentItem, setCurrentItem] = useState(null); // index of current item (in item list) @@ -152,24 +147,17 @@ const AutoComplete = forwardRef(function Impl(props: AutoCompleteProps, re if (multi) { const idx = selectedKeys.indexOf(key); if (idx !== -1) { - setSelected((s) => { - const newSelected = [...s]; - newSelected.splice(idx, 1); - return newSelected; - }); setSelectedKeys((s) => { const newSelectedKeys = [...s]; newSelectedKeys.splice(idx, 1); return newSelectedKeys; }); } else { - setSelected((s) => [...s, item]); setSelectedKeys((s) => [...s, key]); } if (!preserve) inputRef.current.value = ''; inputRef.current.focus(); } else { - setSelected([item]); setSelectedKeys([key]); inputRef.current.value = key; } @@ -201,7 +189,6 @@ const AutoComplete = forwardRef(function Impl(props: AutoCompleteProps, re if (key === 'Backspace') { if (target.value.length) return; if (selectedKeys.length) { - setSelected((s) => s.slice(0, -1)); setSelectedKeys((s) => s.slice(0, -1)); } return; @@ -229,28 +216,24 @@ const AutoComplete = forwardRef(function Impl(props: AutoCompleteProps, re }; useImperativeHandle(ref, () => ({ - getSelectedItems: () => selected, + getSelectedItems: () => selectedKeys.map((key) => valueCache[key]), getSelectedItemKeys: () => [...selectedKeys, inputRef.current?.value].filter((v) => v?.trim().length), - getSelectedItemsAsString: () => [...selectedKeys, inputRef.current?.value].filter((v) => v?.trim().length).join(','), setSelectedItems: (items) => { - setSelected(items); setSelectedKeys(items.map((i) => itemKey(i))); if (!multi && inputRef.current) inputRef.current.value = items.map((i) => itemKey(i)).join(','); }, + setSelectedKeys, getQuery: () => inputRef.current?.value, setQuery: (query) => { if (inputRef.current) inputRef.current.value = query; }, - setSelectedKeys: (keys) => setSelectedKeys(keys), triggerQuery: () => queryList(inputRef.current?.value), closeList: () => { setItemList([]); setCurrentItem(null); }, getValue: () => (multi ? selectedKeys.join(',') : (inputRef.current.value ?? '')), - getValueArray: () => (multi ? selected : [inputRef.current?.value].filter((i) => !!i)), clear: () => { - setSelected([]); setSelectedKeys([]); if (inputRef.current) inputRef.current.value = ''; }, @@ -258,7 +241,7 @@ const AutoComplete = forwardRef(function Impl(props: AutoCompleteProps, re setFocused(true); inputRef.current?.focus(); }, - }), [selected, selectedKeys, inputRef, multi]); + }), [selectedKeys, inputRef, multi]); const move = (dragId: string, hoverId: string) => { if (dragId === hoverId || !draggable) return; @@ -309,7 +292,6 @@ const AutoComplete = forwardRef(function Impl(props: AutoCompleteProps, re if (!ids.length) return; const fetched = await props.fetchItems(ids); for (const item of fetched) valueCache[itemKey(item)] = item; - setSelected([...selected, ...fetched]); setSelectedKeys([...selectedKeys, ...fetched.map((val) => itemKey(val))]); }} placeholder={props.placeholder} diff --git a/packages/ui-default/pages/problem_main.page.tsx b/packages/ui-default/pages/problem_main.page.tsx index ea791ff5c..f870fbcc0 100644 --- a/packages/ui-default/pages/problem_main.page.tsx +++ b/packages/ui-default/pages/problem_main.page.tsx @@ -320,6 +320,18 @@ function ProblemSelectionDisplay(props) { // eslint-disable-line (window as any).__setPids = (newIds: string[]) => setPids(newIds); }, [pids]); + const problemSelectAutoCompleteRef = React.useRef(null); + const copyIds = React.useCallback(async () => { + await navigator.clipboard.writeText(pids.join(',')); + Notification.success(i18n('Problem ids copied to clipboard!')); + problemSelectAutoCompleteRef.current.getSelectedItems(); + }, [pids]); + const copyPids = React.useCallback(async () => { + const items = problemSelectAutoCompleteRef.current.getSelectedItems(); + await navigator.clipboard.writeText(items.map((i) => i.pid || i.docId).join(',')); + Notification.success(i18n('Problem ids copied to clipboard!')); + }, [pids]); + const updateCheckboxSelection = React.useCallback(() => { for (const i of $('[data-checkbox-group="problem"]:checked')) { if (!pids.includes(i.closest('tr').dataset.pid)) { @@ -357,6 +369,7 @@ function ProblemSelectionDisplay(props) { // eslint-disable-line
setPids(v.split(',').filter((i) => i.trim()))} selectedKeys={pids} /> @@ -367,6 +380,8 @@ function ProblemSelectionDisplay(props) { // eslint-disable-line
+ {' '} + {' '} {' '}