diff --git a/rsconcept/frontend/src/components/select/PickSchema.tsx b/rsconcept/frontend/src/components/select/PickSchema.tsx index 92db6825..e9f5f279 100644 --- a/rsconcept/frontend/src/components/select/PickSchema.tsx +++ b/rsconcept/frontend/src/components/select/PickSchema.tsx @@ -1,13 +1,21 @@ -import { useLayoutEffect, useMemo, useState } from 'react'; +import { useCallback, useLayoutEffect, useMemo, useState } from 'react'; import { useIntl } from 'react-intl'; import DataTable, { createColumnHelper, IConditionalStyle } from '@/components/ui/DataTable'; import SearchBar from '@/components/ui/SearchBar'; import { useConceptOptions } from '@/context/ConceptOptionsContext'; +import { useLibrary } from '@/context/LibraryContext'; +import useDropdown from '@/hooks/useDropdown'; import { ILibraryItem, LibraryItemID, LibraryItemType } from '@/models/library'; import { matchLibraryItem } from '@/models/libraryAPI'; +import { prefixes } from '@/utils/constants'; +import { IconClose, IconFolderTree } from '../Icons'; +import { CProps } from '../props'; +import Dropdown from '../ui/Dropdown'; import FlexColumn from '../ui/FlexColumn'; +import MiniButton from '../ui/MiniButton'; +import SelectLocation from './SelectLocation'; interface PickSchemaProps { id?: string; @@ -35,18 +43,27 @@ function PickSchema({ }: PickSchemaProps) { const intl = useIntl(); const { colors } = useConceptOptions(); + const { folders } = useLibrary(); const [filterText, setFilterText] = useState(initialFilter); + const [filterLocation, setFilterLocation] = useState(''); const [filtered, setFiltered] = useState([]); const baseFiltered = useMemo( () => items.filter(item => item.item_type === itemType && (!baseFilter || baseFilter(item))), [items, itemType, baseFilter] ); + const locationMenu = useDropdown(); + useLayoutEffect(() => { - const newFiltered = baseFiltered.filter(item => matchLibraryItem(item, filterText)); + let newFiltered = baseFiltered.filter(item => matchLibraryItem(item, filterText)); + if (filterLocation.length > 0) { + newFiltered = newFiltered.filter( + item => item.location === filterLocation || item.location.startsWith(`${filterLocation}/`) + ); + } setFiltered(newFiltered); - }, [filterText]); + }, [filterText, filterLocation]); const columns = useMemo( () => [ @@ -92,15 +109,51 @@ function PickSchema({ [value, colors] ); + const handleLocationClick = useCallback( + (event: CProps.EventMouse, newValue: string) => { + event.preventDefault(); + event.stopPropagation(); + locationMenu.hide(); + setFilterLocation(newValue); + }, + [locationMenu] + ); + return (
- setFilterText(newValue)} - /> +
+ setFilterText(newValue)} + /> +
+ } + title='Фильтр по расположению' + className='mt-1' + onClick={() => locationMenu.toggle()} + /> + + handleLocationClick(event, target.getPath())} + /> + +
+ {filterLocation.length > 0 ? ( + } + title='Сбросить фильтр' + onClick={() => setFilterLocation('')} + /> + ) : null} +
void; } -function SelectLocationContext({ value, folderTree, onChange, className, style }: SelectLocationContextProps) { +function SelectLocationContext({ + value, + title = 'Проводник...', + folderTree, + onChange, + className, + style +}: SelectLocationContextProps) { const menu = useDropdown(); const handleClick = useCallback( @@ -37,7 +45,7 @@ function SelectLocationContext({ value, folderTree, onChange, className, style } return (
} onClick={() => menu.toggle()} diff --git a/rsconcept/frontend/src/dialogs/DlgChangeInputSchema.tsx b/rsconcept/frontend/src/dialogs/DlgChangeInputSchema.tsx index d4e7f679..42b00310 100644 --- a/rsconcept/frontend/src/dialogs/DlgChangeInputSchema.tsx +++ b/rsconcept/frontend/src/dialogs/DlgChangeInputSchema.tsx @@ -63,7 +63,7 @@ function DlgChangeInputSchema({ oss, hideWindow, target, onSubmit }: DlgChangeIn itemType={LibraryItemType.RSFORM} value={selected} // prettier: split-line onSelectValue={handleSelectLocation} - rows={8} + rows={14} baseFilter={baseFilter} /> diff --git a/rsconcept/frontend/src/dialogs/DlgInlineSynthesis/DlgInlineSynthesis.tsx b/rsconcept/frontend/src/dialogs/DlgInlineSynthesis/DlgInlineSynthesis.tsx index 73fcd344..b0e789f2 100644 --- a/rsconcept/frontend/src/dialogs/DlgInlineSynthesis/DlgInlineSynthesis.tsx +++ b/rsconcept/frontend/src/dialogs/DlgInlineSynthesis/DlgInlineSynthesis.tsx @@ -58,7 +58,7 @@ function DlgInlineSynthesis({ hideWindow, receiver, onInlineSynthesis }: DlgInli const schemaPanel = useMemo( () => ( - + ), [donorID] diff --git a/rsconcept/frontend/src/dialogs/DlgInlineSynthesis/TabSchema.tsx b/rsconcept/frontend/src/dialogs/DlgInlineSynthesis/TabSchema.tsx index 22e2295a..786ba542 100644 --- a/rsconcept/frontend/src/dialogs/DlgInlineSynthesis/TabSchema.tsx +++ b/rsconcept/frontend/src/dialogs/DlgInlineSynthesis/TabSchema.tsx @@ -7,15 +7,19 @@ import TextInput from '@/components/ui/TextInput'; import AnimateFade from '@/components/wrap/AnimateFade'; import { useLibrary } from '@/context/LibraryContext'; import { LibraryItemID, LibraryItemType } from '@/models/library'; +import { IRSForm } from '@/models/rsform'; +import { sortItemsForInlineSynthesis } from '@/models/rsformAPI'; interface TabSchemaProps { selected?: LibraryItemID; setSelected: (newValue: LibraryItemID) => void; + receiver: IRSForm; } -function TabSchema({ selected, setSelected }: TabSchemaProps) { +function TabSchema({ selected, receiver, setSelected }: TabSchemaProps) { const library = useLibrary(); const selectedInfo = useMemo(() => library.items.find(item => item.id === selected), [selected, library.items]); + const sortedItems = useMemo(() => sortItemsForInlineSynthesis(receiver, library.items), [receiver, library.items]); return ( @@ -33,7 +37,7 @@ function TabSchema({ selected, setSelected }: TabSchemaProps) {
item.location === oss.location); diff --git a/rsconcept/frontend/src/models/rsformAPI.ts b/rsconcept/frontend/src/models/rsformAPI.ts index 68adabb7..0d05d075 100644 --- a/rsconcept/frontend/src/models/rsformAPI.ts +++ b/rsconcept/frontend/src/models/rsformAPI.ts @@ -4,6 +4,7 @@ import { TextMatcher } from '@/utils/utils'; +import { BASIC_SCHEMAS, ILibraryItem } from './library'; import { CstMatchMode } from './miscellaneous'; import { CATEGORY_CST_TYPE, @@ -308,3 +309,31 @@ export function generateAlias(type: CstType, schema: IRSForm, takenAliases: stri } return alias; } + +/** + * Sorts library items relevant for InlineSynthesis with specified {@link IRSForm}. + */ +export function sortItemsForInlineSynthesis(receiver: IRSForm, items: ILibraryItem[]): ILibraryItem[] { + const result = items.filter(item => item.location === receiver.location); + for (const item of items) { + if (item.visible && item.owner === item.owner && !result.includes(item)) { + result.push(item); + } + } + for (const item of items) { + if (!result.includes(item) && item.location.startsWith(BASIC_SCHEMAS)) { + result.push(item); + } + } + for (const item of items) { + if (item.visible && !result.includes(item)) { + result.push(item); + } + } + for (const item of items) { + if (!result.includes(item)) { + result.push(item); + } + } + return result; +} diff --git a/rsconcept/frontend/src/pages/LibraryPage/ViewSideLocation.tsx b/rsconcept/frontend/src/pages/LibraryPage/ViewSideLocation.tsx index 121f0db0..d0ec81a3 100644 --- a/rsconcept/frontend/src/pages/LibraryPage/ViewSideLocation.tsx +++ b/rsconcept/frontend/src/pages/LibraryPage/ViewSideLocation.tsx @@ -40,6 +40,21 @@ function ViewSideLocation({ const { user } = useAuth(); const { items } = useLibrary(); const windowSize = useWindowSize(); + + const canRename = useMemo(() => { + if (active.length <= 3 || !user) { + return false; + } + if (user.is_staff) { + return true; + } + const owned = items.filter(item => item.owner == user.id); + const located = owned.filter(item => item.location == active || item.location.startsWith(`${active}/`)); + return located.length !== 0; + }, [active, user, items]); + + const animations = useMemo(() => animateSideMinWidth(windowSize.isSmall ? '10rem' : '15rem'), [windowSize]); + const handleClickFolder = useCallback( (event: CProps.EventMouse, target: FolderNode) => { event.preventDefault(); @@ -56,20 +71,6 @@ function ViewSideLocation({ [setActive] ); - const canRename = useMemo(() => { - if (active.length <= 3 || !user) { - return false; - } - if (user.is_staff) { - return true; - } - const owned = items.filter(item => item.owner == user.id); - const located = owned.filter(item => item.location == active || item.location.startsWith(`${active}/`)); - return located.length !== 0; - }, [active, user, items]); - - const animations = useMemo(() => animateSideMinWidth(windowSize.isSmall ? '10rem' : '15rem'), [windowSize]); - return ( } - disabled={controller.isProcessing} + disabled={!controller.isMutable || controller.isProcessing} onClick={handleEditOperation} /> diff --git a/rsconcept/frontend/src/styling/styles.css b/rsconcept/frontend/src/styling/styles.css index 3d9bd351..0f279b5a 100644 --- a/rsconcept/frontend/src/styling/styles.css +++ b/rsconcept/frontend/src/styling/styles.css @@ -132,6 +132,7 @@ .clr-btn-nav, .clr-btn-clear { color: var(--cl-fg-80); + background-color: transparent; &:disabled { color: var(--cl-fg-60); }