mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 04:50:36 +03:00
M: Implement RSForm sorting for OSS and small UI fixes
Some checks are pending
Frontend CI / build (22.x) (push) Waiting to run
Some checks are pending
Frontend CI / build (22.x) (push) Waiting to run
This commit is contained in:
parent
72fe4a461f
commit
0b3fbd8d4f
|
@ -4,9 +4,8 @@ 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 { ILibraryItem, LibraryItemID, LibraryItemType } from '@/models/library';
|
||||
import { ILibraryFilter } from '@/models/miscellaneous';
|
||||
import { matchLibraryItem } from '@/models/libraryAPI';
|
||||
|
||||
import FlexColumn from '../ui/FlexColumn';
|
||||
|
||||
|
@ -15,6 +14,8 @@ interface PickSchemaProps {
|
|||
initialFilter?: string;
|
||||
rows?: number;
|
||||
|
||||
items: ILibraryItem[];
|
||||
itemType: LibraryItemType;
|
||||
value?: LibraryItemID;
|
||||
baseFilter?: (target: ILibraryItem) => boolean;
|
||||
onSelectValue: (newValue: LibraryItemID) => void;
|
||||
|
@ -22,31 +23,31 @@ interface PickSchemaProps {
|
|||
|
||||
const columnHelper = createColumnHelper<ILibraryItem>();
|
||||
|
||||
function PickSchema({ id, initialFilter = '', rows = 4, value, onSelectValue, baseFilter }: PickSchemaProps) {
|
||||
function PickSchema({
|
||||
id,
|
||||
initialFilter = '',
|
||||
rows = 4,
|
||||
items,
|
||||
itemType,
|
||||
value,
|
||||
onSelectValue,
|
||||
baseFilter
|
||||
}: PickSchemaProps) {
|
||||
const intl = useIntl();
|
||||
const { colors } = useConceptOptions();
|
||||
|
||||
const library = useLibrary();
|
||||
const [filterText, setFilterText] = useState(initialFilter);
|
||||
const [filter, setFilter] = useState<ILibraryFilter>({});
|
||||
const [items, setItems] = useState<ILibraryItem[]>([]);
|
||||
const [filtered, setFiltered] = useState<ILibraryItem[]>([]);
|
||||
const baseFiltered = useMemo(
|
||||
() => items.filter(item => item.item_type === itemType && (!baseFilter || baseFilter(item))),
|
||||
[items, itemType, baseFilter]
|
||||
);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
setFilter({
|
||||
query: filterText,
|
||||
type: LibraryItemType.RSFORM
|
||||
});
|
||||
const newFiltered = baseFiltered.filter(item => matchLibraryItem(item, filterText));
|
||||
setFiltered(newFiltered);
|
||||
}, [filterText]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const filtered = library.applyFilter(filter);
|
||||
if (baseFilter) {
|
||||
setItems(filtered.filter(baseFilter));
|
||||
} else {
|
||||
setItems(filtered);
|
||||
}
|
||||
}, [library, filter, filter.query, baseFilter]);
|
||||
|
||||
const columns = useMemo(
|
||||
() => [
|
||||
columnHelper.accessor('alias', {
|
||||
|
@ -106,7 +107,7 @@ function PickSchema({ id, initialFilter = '', rows = 4, value, onSelectValue, ba
|
|||
noHeader
|
||||
noFooter
|
||||
className='text-sm select-none cc-scroll-y'
|
||||
data={items}
|
||||
data={filtered}
|
||||
columns={columns}
|
||||
conditionalRowStyles={conditionalRowStyles}
|
||||
noDataComponent={
|
||||
|
|
|
@ -8,8 +8,10 @@ import PickSchema from '@/components/select/PickSchema';
|
|||
import Label from '@/components/ui/Label';
|
||||
import MiniButton from '@/components/ui/MiniButton';
|
||||
import Modal, { ModalProps } from '@/components/ui/Modal';
|
||||
import { ILibraryItem, LibraryItemID } from '@/models/library';
|
||||
import { useLibrary } from '@/context/LibraryContext';
|
||||
import { ILibraryItem, LibraryItemID, LibraryItemType } from '@/models/library';
|
||||
import { IOperation, IOperationSchema } from '@/models/oss';
|
||||
import { sortItemsForOSS } from '@/models/ossAPI';
|
||||
|
||||
interface DlgChangeInputSchemaProps extends Pick<ModalProps, 'hideWindow'> {
|
||||
oss: IOperationSchema;
|
||||
|
@ -19,6 +21,8 @@ interface DlgChangeInputSchemaProps extends Pick<ModalProps, 'hideWindow'> {
|
|||
|
||||
function DlgChangeInputSchema({ oss, hideWindow, target, onSubmit }: DlgChangeInputSchemaProps) {
|
||||
const [selected, setSelected] = useState<LibraryItemID | undefined>(target.result ?? undefined);
|
||||
const library = useLibrary();
|
||||
const sortedItems = useMemo(() => sortItemsForOSS(oss, library.items), [oss, library.items]);
|
||||
|
||||
const baseFilter = useCallback(
|
||||
(item: ILibraryItem) => !oss.schemas.includes(item.id) || item.id === selected || item.id === target.result,
|
||||
|
@ -55,6 +59,8 @@ function DlgChangeInputSchema({ oss, hideWindow, target, onSubmit }: DlgChangeIn
|
|||
</div>
|
||||
</div>
|
||||
<PickSchema
|
||||
items={sortedItems}
|
||||
itemType={LibraryItemType.RSFORM}
|
||||
value={selected} // prettier: split-line
|
||||
onSelectValue={handleSelectLocation}
|
||||
rows={8}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
'use client';
|
||||
|
||||
import { useCallback, useEffect } from 'react';
|
||||
import { useCallback, useEffect, useMemo } from 'react';
|
||||
|
||||
import { IconReset } from '@/components/Icons';
|
||||
import PickSchema from '@/components/select/PickSchema';
|
||||
|
@ -10,8 +10,10 @@ import MiniButton from '@/components/ui/MiniButton';
|
|||
import TextArea from '@/components/ui/TextArea';
|
||||
import TextInput from '@/components/ui/TextInput';
|
||||
import AnimateFade from '@/components/wrap/AnimateFade';
|
||||
import { ILibraryItem, LibraryItemID } from '@/models/library';
|
||||
import { useLibrary } from '@/context/LibraryContext';
|
||||
import { ILibraryItem, LibraryItemID, LibraryItemType } from '@/models/library';
|
||||
import { IOperationSchema } from '@/models/oss';
|
||||
import { sortItemsForOSS } from '@/models/ossAPI';
|
||||
import { limits, patterns } from '@/utils/constants';
|
||||
|
||||
interface TabInputOperationProps {
|
||||
|
@ -42,6 +44,8 @@ function TabInputOperation({
|
|||
setCreateSchema
|
||||
}: TabInputOperationProps) {
|
||||
const baseFilter = useCallback((item: ILibraryItem) => !oss.schemas.includes(item.id), [oss]);
|
||||
const library = useLibrary();
|
||||
const sortedItems = useMemo(() => sortItemsForOSS(oss, library.items), [oss, library.items]);
|
||||
|
||||
useEffect(() => {
|
||||
if (createSchema) {
|
||||
|
@ -102,7 +106,9 @@ function TabInputOperation({
|
|||
</div>
|
||||
{!createSchema ? (
|
||||
<PickSchema
|
||||
value={attachedID} // prettier: split-line
|
||||
items={sortedItems}
|
||||
value={attachedID}
|
||||
itemType={LibraryItemType.RSFORM}
|
||||
onSelectValue={setAttachedID}
|
||||
rows={8}
|
||||
baseFilter={baseFilter}
|
||||
|
|
|
@ -6,7 +6,7 @@ import PickSchema from '@/components/select/PickSchema';
|
|||
import TextInput from '@/components/ui/TextInput';
|
||||
import AnimateFade from '@/components/wrap/AnimateFade';
|
||||
import { useLibrary } from '@/context/LibraryContext';
|
||||
import { LibraryItemID } from '@/models/library';
|
||||
import { LibraryItemID, LibraryItemType } from '@/models/library';
|
||||
|
||||
interface TabSchemaProps {
|
||||
selected?: LibraryItemID;
|
||||
|
@ -33,6 +33,8 @@ function TabSchema({ selected, setSelected }: TabSchemaProps) {
|
|||
</div>
|
||||
<PickSchema
|
||||
id='dlg_schema_picker' // prettier: split lines
|
||||
items={library.items}
|
||||
itemType={LibraryItemType.RSFORM}
|
||||
rows={15}
|
||||
value={selected}
|
||||
onSelectValue={setSelected}
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
|
||||
import { TextMatcher } from '@/utils/utils';
|
||||
|
||||
import { IOperation } from './oss';
|
||||
import { ILibraryItem } from './library';
|
||||
import { IOperation, IOperationSchema } from './oss';
|
||||
|
||||
/**
|
||||
* Checks if a given target {@link IOperation} matches the specified query using.
|
||||
|
@ -16,3 +17,29 @@ export function matchOperation(target: IOperation, query: string): boolean {
|
|||
const matcher = new TextMatcher(query);
|
||||
return matcher.test(target.alias) || matcher.test(target.title);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts library items relevant for the specified {@link IOperationSchema}.
|
||||
*
|
||||
* @param oss - The {@link IOperationSchema} to be sorted.
|
||||
* @param items - The items to be sorted.
|
||||
*/
|
||||
export function sortItemsForOSS(oss: IOperationSchema, items: ILibraryItem[]): ILibraryItem[] {
|
||||
const result = items.filter(item => item.location === oss.location);
|
||||
for (const item of items) {
|
||||
if (item.visible && item.owner === oss.owner && !result.includes(item)) {
|
||||
result.push(item);
|
||||
}
|
||||
}
|
||||
for (const item of result) {
|
||||
if (item.visible && !result.includes(item)) {
|
||||
result.push(item);
|
||||
}
|
||||
}
|
||||
for (const item of result) {
|
||||
if (!result.includes(item)) {
|
||||
result.push(item);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -113,8 +113,8 @@ function NodeContextMenu({
|
|||
|
||||
{operation.result ? (
|
||||
<DropdownButton
|
||||
text={prepareTooltip('Открыть схему', 'Двойной клик')}
|
||||
title='Открыть привязанную КС'
|
||||
text='Открыть схему'
|
||||
titleHtml={prepareTooltip('Открыть привязанную КС', 'Двойной клик')}
|
||||
icon={<IconRSForm size='1rem' className='icon-green' />}
|
||||
disabled={controller.isProcessing}
|
||||
onClick={handleOpenSchema}
|
||||
|
|
|
@ -8,6 +8,7 @@ import { urls } from '@/app/urls';
|
|||
import { useAccessMode } from '@/context/AccessModeContext';
|
||||
import { useAuth } from '@/context/AuthContext';
|
||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
||||
import { useLibrary } from '@/context/LibraryContext';
|
||||
import { useConceptNavigation } from '@/context/NavigationContext';
|
||||
import { useOSS } from '@/context/OssContext';
|
||||
import DlgChangeInputSchema from '@/dialogs/DlgChangeInputSchema';
|
||||
|
@ -30,7 +31,7 @@ import {
|
|||
} from '@/models/oss';
|
||||
import { UserID, UserLevel } from '@/models/user';
|
||||
import { PARAMETER } from '@/utils/constants';
|
||||
import { information } from '@/utils/labels';
|
||||
import { errors, information } from '@/utils/labels';
|
||||
|
||||
export interface ICreateOperationPrompt {
|
||||
x: number;
|
||||
|
@ -95,6 +96,7 @@ export const OssEditState = ({ selected, setSelected, children }: OssEditStatePr
|
|||
const { adminMode } = useConceptOptions();
|
||||
const { accessLevel, setAccessLevel } = useAccessMode();
|
||||
const model = useOSS();
|
||||
const library = useLibrary();
|
||||
|
||||
const isMutable = useMemo(
|
||||
() => accessLevel > UserLevel.READER && !model.schema?.read_only,
|
||||
|
@ -307,12 +309,20 @@ export const OssEditState = ({ selected, setSelected, children }: OssEditStatePr
|
|||
|
||||
const createInput = useCallback(
|
||||
(target: OperationID, positions: IOperationPosition[]) => {
|
||||
const operation = model.schema?.operationByID.get(target);
|
||||
if (!model.schema || !operation) {
|
||||
return;
|
||||
}
|
||||
if (library.items.find(item => item.alias === operation.alias && item.location === model.schema!.location)) {
|
||||
toast.error(errors.inputAlreadyExists);
|
||||
return;
|
||||
}
|
||||
model.createInput({ target: target, positions: positions }, new_schema => {
|
||||
toast.success(information.newLibraryItem);
|
||||
router.push(urls.schema(new_schema.id));
|
||||
});
|
||||
},
|
||||
[model, router]
|
||||
[model, library.items, router]
|
||||
);
|
||||
|
||||
const promptEditInput = useCallback((target: OperationID, positions: IOperationPosition[]) => {
|
||||
|
|
|
@ -953,7 +953,8 @@ export const errors = {
|
|||
passwordsMismatch: 'Пароли не совпадают',
|
||||
imageFailed: 'Ошибка при создании изображения',
|
||||
reuseOriginal: 'Повторное использование удаляемой конституенты при отождествлении',
|
||||
substituteInherited: 'Нельзя удалять наследованные конституенты при отождествлении'
|
||||
substituteInherited: 'Нельзя удалять наследованные конституенты при отождествлении',
|
||||
inputAlreadyExists: 'Концептуальная схема с таким именем уже существует'
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue
Block a user