From 584ce59f2d61474e81c1b5e72a2bb9c4fd494743 Mon Sep 17 00:00:00 2001 From: Ivan <8611739+IRBorisov@users.noreply.github.com> Date: Sun, 25 Aug 2024 13:45:32 +0300 Subject: [PATCH] F: Add search feature to multiple cst selector --- .../frontend/src/components/DomainIcons.tsx | 31 +++++++- .../select/PickMultiConstituenta.tsx | 53 +++++++++---- .../src/components/select/PickSchema.tsx | 1 + .../frontend/src/components/ui/Dropdown.tsx | 2 +- .../frontend/src/components/ui/SearchBar.tsx | 2 +- .../frontend/src/components/ui/Tooltip.tsx | 2 +- .../RSFormPage/EditorRSList/EditorRSList.tsx | 77 +++++++++++-------- .../RSFormPage/EditorRSList/ToolbarRSList.tsx | 36 +++++---- .../frontend/src/pages/RSFormPage/RSTabs.tsx | 2 +- 9 files changed, 139 insertions(+), 67 deletions(-) diff --git a/rsconcept/frontend/src/components/DomainIcons.tsx b/rsconcept/frontend/src/components/DomainIcons.tsx index c017d332..015dba50 100644 --- a/rsconcept/frontend/src/components/DomainIcons.tsx +++ b/rsconcept/frontend/src/components/DomainIcons.tsx @@ -1,10 +1,18 @@ import { AccessPolicy, LibraryItemType, LocationHead } from '@/models/library'; import { CstMatchMode, DependencyMode } from '@/models/miscellaneous'; -import { ExpressionStatus } from '@/models/rsform'; +import { CstType, ExpressionStatus } from '@/models/rsform'; import { IconAlias, IconBusiness, + IconCstAxiom, + IconCstBaseSet, + IconCstConstSet, + IconCstFunction, + IconCstPredicate, + IconCstStructured, + IconCstTerm, + IconCstTheorem, IconFilter, IconFormula, IconGraphCollapse, @@ -132,3 +140,24 @@ export function StatusIcon({ value, size = '1.25rem', className }: DomIconProps< return ; } } + +export function CstTypeIcon({ value, size = '1.25rem', className }: DomIconProps) { + switch (value) { + case CstType.BASE: + return ; + case CstType.CONSTANT: + return ; + case CstType.STRUCTURED: + return ; + case CstType.TERM: + return ; + case CstType.AXIOM: + return ; + case CstType.FUNCTION: + return ; + case CstType.PREDICATE: + return ; + case CstType.THEOREM: + return ; + } +} diff --git a/rsconcept/frontend/src/components/select/PickMultiConstituenta.tsx b/rsconcept/frontend/src/components/select/PickMultiConstituenta.tsx index f0a4da4d..bebf4f02 100644 --- a/rsconcept/frontend/src/components/select/PickMultiConstituenta.tsx +++ b/rsconcept/frontend/src/components/select/PickMultiConstituenta.tsx @@ -5,12 +5,14 @@ import { useLayoutEffect, useMemo, useState } from 'react'; import DataTable, { createColumnHelper, RowSelectionState } from '@/components/ui/DataTable'; import { useConceptOptions } from '@/context/ConceptOptionsContext'; +import { CstMatchMode } from '@/models/miscellaneous'; import { ConstituentaID, IConstituenta, IRSForm } from '@/models/rsform'; -import { isBasicConcept } from '@/models/rsformAPI'; +import { isBasicConcept, matchConstituenta } from '@/models/rsformAPI'; import { describeConstituenta } from '@/utils/labels'; import BadgeConstituenta from '../info/BadgeConstituenta'; import NoData from '../ui/NoData'; +import SearchBar from '../ui/SearchBar'; import ToolbarGraphSelection from './ToolbarGraphSelection'; interface PickMultiConstituentaProps { @@ -28,18 +30,30 @@ const columnHelper = createColumnHelper(); function PickMultiConstituenta({ id, schema, prefixID, rows, selected, setSelected }: PickMultiConstituentaProps) { const { colors } = useConceptOptions(); const [rowSelection, setRowSelection] = useState({}); + const [filtered, setFiltered] = useState(schema?.items ?? []); + const [filterText, setFilterText] = useState(''); useLayoutEffect(() => { - if (!schema || selected.length === 0) { + if (filtered.length === 0) { setRowSelection({}); - } else { - const newRowSelection: RowSelectionState = {}; - schema.items.forEach((cst, index) => { - newRowSelection[String(index)] = selected.includes(cst.id); - }); - setRowSelection(newRowSelection); + return; } - }, [selected, schema]); + const newRowSelection: RowSelectionState = {}; + filtered.forEach((cst, index) => { + newRowSelection[String(index)] = selected.includes(cst.id); + }); + setRowSelection(newRowSelection); + }, [filtered, setRowSelection, selected]); + + useLayoutEffect(() => { + if (!schema || schema.items.length === 0) { + setFiltered([]); + } else if (filterText) { + setFiltered(schema.items.filter(cst => matchConstituenta(cst, filterText, CstMatchMode.ALL))); + } else { + setFiltered(schema.items); + } + }, [filterText, schema?.items, schema]); function handleRowSelection(updater: React.SetStateAction) { if (!schema) { @@ -47,12 +61,12 @@ function PickMultiConstituenta({ id, schema, prefixID, rows, selected, setSelect } else { const newRowSelection = typeof updater === 'function' ? updater(rowSelection) : updater; const newSelection: ConstituentaID[] = []; - schema.items.forEach((cst, index) => { + filtered.forEach((cst, index) => { if (newRowSelection[String(index)] === true) { newSelection.push(cst.id); } }); - setSelected(newSelection); + setSelected(prev => [...prev.filter(cst_id => !filtered.find(cst => cst.id === cst_id)), ...newSelection]); } } @@ -75,10 +89,17 @@ function PickMultiConstituenta({ id, schema, prefixID, rows, selected, setSelect return ( - - + + Выбраны {selected.length} из {schema?.items.length ?? 0} - + + {schema ? ( !schema.cstByID.get(cstID)?.is_inherited} setSelected={setSelected} emptySelection={selected.length === 0} - className='w-full ml-8' + className='w-fit' /> ) : null} @@ -97,7 +118,7 @@ function PickMultiConstituenta({ id, schema, prefixID, rows, selected, setSelect rows={rows} contentHeight='1.3rem' className={clsx('cc-scroll-y', 'border', 'text-sm', 'select-none')} - data={schema?.items ?? []} + data={filtered} columns={columns} headPosition='0rem' enableRowSelection diff --git a/rsconcept/frontend/src/components/select/PickSchema.tsx b/rsconcept/frontend/src/components/select/PickSchema.tsx index 0996d6f3..92db6825 100644 --- a/rsconcept/frontend/src/components/select/PickSchema.tsx +++ b/rsconcept/frontend/src/components/select/PickSchema.tsx @@ -96,6 +96,7 @@ function PickSchema({ setFilterText(newValue)} diff --git a/rsconcept/frontend/src/components/ui/Dropdown.tsx b/rsconcept/frontend/src/components/ui/Dropdown.tsx index be089f30..698072d9 100644 --- a/rsconcept/frontend/src/components/ui/Dropdown.tsx +++ b/rsconcept/frontend/src/components/ui/Dropdown.tsx @@ -18,7 +18,7 @@ function Dropdown({ isOpen, stretchLeft, stretchTop, className, children, ...res (onChange ? onChange(event.target.value) : undefined)} diff --git a/rsconcept/frontend/src/components/ui/Tooltip.tsx b/rsconcept/frontend/src/components/ui/Tooltip.tsx index 0f1e8702..bf504644 100644 --- a/rsconcept/frontend/src/components/ui/Tooltip.tsx +++ b/rsconcept/frontend/src/components/ui/Tooltip.tsx @@ -29,7 +29,7 @@ function Tooltip({ } return createPortal( ({}); const controller = useRSEdit(); + const [filtered, setFiltered] = useState(controller.schema?.items ?? []); + const [filterText, setFilterText] = useState(''); + useLayoutEffect(() => { - if (!controller.schema || controller.selected.length === 0) { + if (filtered.length === 0) { setRowSelection({}); - } else { - const newRowSelection: RowSelectionState = {}; - controller.schema.items.forEach((cst, index) => { - newRowSelection[String(index)] = controller.selected.includes(cst.id); - }); - setRowSelection(newRowSelection); + return; } - }, [controller.selected, controller.schema]); + const newRowSelection: RowSelectionState = {}; + filtered.forEach((cst, index) => { + newRowSelection[String(index)] = controller.selected.includes(cst.id); + }); + setRowSelection(newRowSelection); + }, [filtered, setRowSelection, controller.selected]); + + useLayoutEffect(() => { + if (!controller.schema || controller.schema.items.length === 0) { + setFiltered([]); + } else if (filterText) { + setFiltered(controller.schema.items.filter(cst => matchConstituenta(cst, filterText, CstMatchMode.ALL))); + } else { + setFiltered(controller.schema.items); + } + }, [filterText, controller.schema?.items, controller.schema]); const handleDownloadCSV = useCallback(() => { - if (!controller.schema || controller.schema.items.length === 0) { + if (!controller.schema || filtered.length === 0) { toast.error(information.noDataToExport); return; } - const blob = convertToCSV(controller.schema.items); + const blob = convertToCSV(filtered); try { fileDownload(blob, `${controller.schema.alias}.csv`, 'text/csv;charset=utf-8;'); } catch (error) { console.error(error); } - }, [controller]); + }, [filtered, controller]); function handleRowSelection(updater: React.SetStateAction) { if (!controller.schema) { @@ -60,12 +74,15 @@ function EditorRSList({ onOpenEdit }: EditorRSListProps) { } else { const newRowSelection = typeof updater === 'function' ? updater(rowSelection) : updater; const newSelection: ConstituentaID[] = []; - controller.schema.items.forEach((cst, index) => { + filtered.forEach((cst, index) => { if (newRowSelection[String(index)] === true) { newSelection.push(cst.id); } }); - controller.setSelected(newSelection); + controller.setSelected(prev => [ + ...prev.filter(cst_id => !filtered.find(cst => cst.id === cst_id)), + ...newSelection + ]); } } @@ -127,21 +144,21 @@ function EditorRSList({ onOpenEdit }: EditorRSListProps) { {controller.isContentEditable ? : null} {controller.isContentEditable ? ( - + + + Выбор {controller.selected.length} из {controller.schema?.stats?.count_all ?? 0} + + + ) : null} - - - + } @@ -150,7 +167,7 @@ function EditorRSList({ onOpenEdit }: EditorRSListProps) { + {controller.schema && controller.schema?.oss.length > 0 ? ( - } - disabled={controller.isProcessing || controller.selected.length !== 1} - onClick={controller.cloneCst} - /> - } - disabled={controller.isProcessing} - onClick={() => controller.createCst(undefined, false)} - /> - + {Object.values(CstType).map(typeStr => ( } onClick={() => controller.createCst(typeStr as CstType, true)} titleHtml={getCstTypeShortcut(typeStr as CstType)} /> ))} + } + disabled={controller.isProcessing} + onClick={() => controller.createCst(undefined, false)} + /> + } + disabled={controller.isProcessing || controller.selected.length !== 1} + onClick={controller.cloneCst} + /> } diff --git a/rsconcept/frontend/src/pages/RSFormPage/RSTabs.tsx b/rsconcept/frontend/src/pages/RSFormPage/RSTabs.tsx index 146bdee5..cebf54dd 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/RSTabs.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/RSTabs.tsx @@ -267,7 +267,7 @@ function RSTabs() { - + {cardPanel} {listPanel} {editorPanel}