mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 04:50:36 +03:00
R: Distinguish undefined semantics vs null
This commit is contained in:
parent
9ef4d32376
commit
53b746f3c4
|
@ -31,7 +31,7 @@ const DlgUploadRSForm = React.lazy(() => import('@/features/rsform/dialogs/DlgUp
|
||||||
export const GlobalDialogs = () => {
|
export const GlobalDialogs = () => {
|
||||||
const active = useDialogsStore(state => state.active);
|
const active = useDialogsStore(state => state.active);
|
||||||
|
|
||||||
if (active === undefined) {
|
if (active === null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
switch (active) {
|
switch (active) {
|
||||||
|
|
|
@ -32,8 +32,6 @@ axiosInstance.interceptors.request.use(config => {
|
||||||
});
|
});
|
||||||
|
|
||||||
// ================ Data transfer types ================
|
// ================ Data transfer types ================
|
||||||
export type DataCallback<ResponseData = undefined> = (data: ResponseData) => void;
|
|
||||||
|
|
||||||
export interface IFrontRequest<RequestData, ResponseData> {
|
export interface IFrontRequest<RequestData, ResponseData> {
|
||||||
data?: RequestData;
|
data?: RequestData;
|
||||||
successMessage?: string | ((data: ResponseData) => string);
|
successMessage?: string | ((data: ResponseData) => string);
|
||||||
|
|
|
@ -5,10 +5,10 @@ import { KEYS } from './configuration';
|
||||||
|
|
||||||
export const useMutationErrors = () => {
|
export const useMutationErrors = () => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const [ignored, setIgnored] = useState<(Error | null)[]>([]);
|
const [ignored, setIgnored] = useState<Error[]>([]);
|
||||||
const mutationErrors = useMutationState({
|
const mutationErrors = useMutationState({
|
||||||
filters: { mutationKey: [KEYS.global_mutation], status: 'error' },
|
filters: { mutationKey: [KEYS.global_mutation], status: 'error' },
|
||||||
select: mutation => mutation.state.error
|
select: mutation => mutation.state.error!
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(queryClient.getMutationCache().getAll());
|
console.log(queryClient.getMutationCache().getAll());
|
||||||
|
|
|
@ -141,7 +141,7 @@ function DataTable<TData extends RowData>({
|
||||||
...restProps
|
...restProps
|
||||||
}: DataTableProps<TData>) {
|
}: DataTableProps<TData>) {
|
||||||
const [sorting, setSorting] = useState<SortingState>(initialSorting ? [initialSorting] : []);
|
const [sorting, setSorting] = useState<SortingState>(initialSorting ? [initialSorting] : []);
|
||||||
const [lastSelected, setLastSelected] = useState<string | undefined>(undefined);
|
const [lastSelected, setLastSelected] = useState<string | null>(null);
|
||||||
|
|
||||||
const [pagination, setPagination] = useState<PaginationState>({
|
const [pagination, setPagination] = useState<PaginationState>({
|
||||||
pageIndex: 0,
|
pageIndex: 0,
|
||||||
|
@ -198,7 +198,7 @@ function DataTable<TData extends RowData>({
|
||||||
enableRowSelection={enableRowSelection}
|
enableRowSelection={enableRowSelection}
|
||||||
enableSorting={enableSorting}
|
enableSorting={enableSorting}
|
||||||
headPosition={headPosition}
|
headPosition={headPosition}
|
||||||
resetLastSelected={() => setLastSelected(undefined)}
|
resetLastSelected={() => setLastSelected(null)}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { Checkbox } from '../Input';
|
||||||
|
|
||||||
interface SelectRowProps<TData> {
|
interface SelectRowProps<TData> {
|
||||||
row: Row<TData>;
|
row: Row<TData>;
|
||||||
onChangeLastSelected: (newValue: string | undefined) => void;
|
onChangeLastSelected: (newValue: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
function SelectRow<TData>({ row, onChangeLastSelected }: SelectRowProps<TData>) {
|
function SelectRow<TData>({ row, onChangeLastSelected }: SelectRowProps<TData>) {
|
||||||
|
|
|
@ -15,8 +15,8 @@ interface TableBodyProps<TData> {
|
||||||
enableRowSelection?: boolean;
|
enableRowSelection?: boolean;
|
||||||
conditionalRowStyles?: IConditionalStyle<TData>[];
|
conditionalRowStyles?: IConditionalStyle<TData>[];
|
||||||
|
|
||||||
lastSelected: string | undefined;
|
lastSelected: string | null;
|
||||||
onChangeLastSelected: (newValue: string | undefined) => void;
|
onChangeLastSelected: (newValue: string | null) => void;
|
||||||
|
|
||||||
onRowClicked?: (rowData: TData, event: CProps.EventMouse) => void;
|
onRowClicked?: (rowData: TData, event: CProps.EventMouse) => void;
|
||||||
onRowDoubleClicked?: (rowData: TData, event: CProps.EventMouse) => void;
|
onRowDoubleClicked?: (rowData: TData, event: CProps.EventMouse) => void;
|
||||||
|
@ -49,7 +49,7 @@ function TableBody<TData>({
|
||||||
newSelection[row.id] = !target.getIsSelected();
|
newSelection[row.id] = !target.getIsSelected();
|
||||||
});
|
});
|
||||||
table.setRowSelection(prev => ({ ...prev, ...newSelection }));
|
table.setRowSelection(prev => ({ ...prev, ...newSelection }));
|
||||||
onChangeLastSelected(undefined);
|
onChangeLastSelected(null);
|
||||||
} else {
|
} else {
|
||||||
onChangeLastSelected(target.id);
|
onChangeLastSelected(target.id);
|
||||||
target.toggleSelected(!target.getIsSelected());
|
target.toggleSelected(!target.getIsSelected());
|
||||||
|
@ -94,7 +94,7 @@ function TableBody<TData>({
|
||||||
width: noHeader && index === 0 ? `calc(var(--col-${cell.column.id}-size) * 1px)` : 'auto'
|
width: noHeader && index === 0 ? `calc(var(--col-${cell.column.id}-size) * 1px)` : 'auto'
|
||||||
}}
|
}}
|
||||||
onClick={event => handleRowClicked(row, event)}
|
onClick={event => handleRowClicked(row, event)}
|
||||||
onDoubleClick={event => (onRowDoubleClicked ? onRowDoubleClicked(row.original, event) : undefined)}
|
onDoubleClick={event => onRowDoubleClicked?.(row.original, event)}
|
||||||
>
|
>
|
||||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||||
</td>
|
</td>
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { isResponseHtml } from '@/utils/utils';
|
||||||
|
|
||||||
import { PrettyJson } from './View';
|
import { PrettyJson } from './View';
|
||||||
|
|
||||||
export type ErrorData = string | Error | AxiosError | ZodError | undefined | null;
|
export type ErrorData = string | Error | AxiosError | ZodError;
|
||||||
|
|
||||||
interface InfoErrorProps {
|
interface InfoErrorProps {
|
||||||
error: ErrorData;
|
error: ErrorData;
|
||||||
|
|
|
@ -53,7 +53,7 @@ export function SearchBar({
|
||||||
className={clsx('outline-none bg-transparent', !noIcon && 'pl-10')}
|
className={clsx('outline-none bg-transparent', !noIcon && 'pl-10')}
|
||||||
noBorder={noBorder}
|
noBorder={noBorder}
|
||||||
value={query}
|
value={query}
|
||||||
onChange={event => (onChangeQuery ? onChangeQuery(event.target.value) : undefined)}
|
onChange={event => onChangeQuery?.(event.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -86,7 +86,7 @@ export const schemaCloneLibraryItem = schemaLibraryItem
|
||||||
alias: z.string().nonempty(errorMsg.requiredField),
|
alias: z.string().nonempty(errorMsg.requiredField),
|
||||||
location: z.string().refine(data => validateLocation(data), { message: errorMsg.invalidLocation }),
|
location: z.string().refine(data => validateLocation(data), { message: errorMsg.invalidLocation }),
|
||||||
|
|
||||||
items: z.array(z.number()).optional()
|
items: z.array(z.number())
|
||||||
});
|
});
|
||||||
|
|
||||||
export const schemaCreateLibraryItem = z
|
export const schemaCreateLibraryItem = z
|
||||||
|
@ -138,5 +138,5 @@ export const schemaVersionUpdate = z.object({
|
||||||
export const schemaVersionCreate = z.object({
|
export const schemaVersionCreate = z.object({
|
||||||
version: z.string(),
|
version: z.string(),
|
||||||
description: z.string(),
|
description: z.string(),
|
||||||
items: z.array(z.number()).optional()
|
items: z.array(z.number())
|
||||||
});
|
});
|
||||||
|
|
|
@ -16,32 +16,29 @@ export function useApplyLibraryFilter(filter: ILibraryFilter) {
|
||||||
if (filter.folderMode && filter.location) {
|
if (filter.folderMode && filter.location) {
|
||||||
if (filter.subfolders) {
|
if (filter.subfolders) {
|
||||||
result = result.filter(
|
result = result.filter(
|
||||||
item => item.location == filter.location || item.location.startsWith(filter.location! + '/')
|
item => item.location == filter.location || item.location.startsWith(filter.location + '/')
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
result = result.filter(item => item.location == filter.location);
|
result = result.filter(item => item.location == filter.location);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (filter.type) {
|
if (filter.isVisible !== null) {
|
||||||
result = result.filter(item => item.item_type === filter.type);
|
|
||||||
}
|
|
||||||
if (filter.isVisible !== undefined) {
|
|
||||||
result = result.filter(item => filter.isVisible === item.visible);
|
result = result.filter(item => filter.isVisible === item.visible);
|
||||||
}
|
}
|
||||||
if (filter.isOwned !== undefined) {
|
if (filter.isOwned !== null) {
|
||||||
result = result.filter(item => filter.isOwned === (item.owner === user.id));
|
result = result.filter(item => filter.isOwned === (item.owner === user.id));
|
||||||
}
|
}
|
||||||
if (filter.isEditor !== undefined) {
|
if (filter.isEditor !== null) {
|
||||||
result = result.filter(item => filter.isEditor == user.editor.includes(item.id));
|
result = result.filter(item => filter.isEditor == user.editor.includes(item.id));
|
||||||
}
|
}
|
||||||
if (filter.filterUser !== undefined) {
|
if (filter.filterUser !== null) {
|
||||||
result = result.filter(item => filter.filterUser === item.owner);
|
result = result.filter(item => filter.filterUser === item.owner);
|
||||||
}
|
}
|
||||||
if (!filter.folderMode && filter.path) {
|
if (!filter.folderMode && !!filter.path) {
|
||||||
result = result.filter(item => matchLibraryItemLocation(item, filter.path!));
|
result = result.filter(item => matchLibraryItemLocation(item, filter.path));
|
||||||
}
|
}
|
||||||
if (filter.query) {
|
if (filter.query) {
|
||||||
result = result.filter(item => matchLibraryItem(item, filter.query!));
|
result = result.filter(item => matchLibraryItem(item, filter.query));
|
||||||
}
|
}
|
||||||
|
|
||||||
return { filtered: result };
|
return { filtered: result };
|
||||||
|
|
|
@ -116,7 +116,7 @@ export function EditorLibraryItem({ controller }: EditorLibraryItemProps) {
|
||||||
{ownerSelector.isOpen ? (
|
{ownerSelector.isOpen ? (
|
||||||
<SelectUser
|
<SelectUser
|
||||||
className='w-[25rem] sm:w-[26rem] text-sm'
|
className='w-[25rem] sm:w-[26rem] text-sm'
|
||||||
value={controller.schema.owner ?? undefined}
|
value={controller.schema.owner}
|
||||||
onChange={onSelectUser}
|
onChange={onSelectUser}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
|
@ -9,10 +9,10 @@ import { ILibraryItem } from '../backend/types';
|
||||||
import { matchLibraryItem } from '../models/libraryAPI';
|
import { matchLibraryItem } from '../models/libraryAPI';
|
||||||
|
|
||||||
interface SelectLibraryItemProps extends CProps.Styling {
|
interface SelectLibraryItemProps extends CProps.Styling {
|
||||||
items?: ILibraryItem[];
|
value: ILibraryItem | null;
|
||||||
value?: ILibraryItem;
|
onChange: (newValue: ILibraryItem | null) => void;
|
||||||
onChange: (newValue?: ILibraryItem) => void;
|
|
||||||
|
|
||||||
|
items?: ILibraryItem[];
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
noBorder?: boolean;
|
noBorder?: boolean;
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ export function SelectLibraryItem({
|
||||||
className={clsx('text-ellipsis', className)}
|
className={clsx('text-ellipsis', className)}
|
||||||
options={options}
|
options={options}
|
||||||
value={value ? { value: value.id, label: `${value.alias}: ${value.title}` } : null}
|
value={value ? { value: value.id, label: `${value.alias}: ${value.title}` } : null}
|
||||||
onChange={data => onChange(items?.find(cst => cst.id === data?.value))}
|
onChange={data => onChange(items?.find(cst => cst.id === data?.value) ?? null)}
|
||||||
filterOption={filter}
|
filterOption={filter}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
|
|
|
@ -10,10 +10,10 @@ import { IVersionInfo } from '../backend/types';
|
||||||
|
|
||||||
interface SelectVersionProps extends CProps.Styling {
|
interface SelectVersionProps extends CProps.Styling {
|
||||||
id?: string;
|
id?: string;
|
||||||
items?: IVersionInfo[];
|
value: number | undefined;
|
||||||
value?: number;
|
onChange: (newValue: number | undefined) => void;
|
||||||
onChange: (newValue?: number) => void;
|
|
||||||
|
|
||||||
|
items?: IVersionInfo[];
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
noBorder?: boolean;
|
noBorder?: boolean;
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,7 @@ function DlgCloneLibraryItem() {
|
||||||
read_only: false,
|
read_only: false,
|
||||||
access_policy: AccessPolicy.PUBLIC,
|
access_policy: AccessPolicy.PUBLIC,
|
||||||
location: initialLocation,
|
location: initialLocation,
|
||||||
items: undefined
|
items: []
|
||||||
},
|
},
|
||||||
mode: 'onChange',
|
mode: 'onChange',
|
||||||
reValidateMode: 'onChange'
|
reValidateMode: 'onChange'
|
||||||
|
@ -165,8 +165,8 @@ function DlgCloneLibraryItem() {
|
||||||
<Checkbox
|
<Checkbox
|
||||||
id='dlg_only_selected'
|
id='dlg_only_selected'
|
||||||
label={`Только выбранные конституенты [${selected.length} из ${totalCount}]`}
|
label={`Только выбранные конституенты [${selected.length} из ${totalCount}]`}
|
||||||
value={field.value !== undefined}
|
value={field.value.length > 0}
|
||||||
onChange={value => field.onChange(value ? selected : undefined)}
|
onChange={value => field.onChange(value ? selected : [])}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -32,7 +32,7 @@ function DlgCreateVersion() {
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
version: versions.length > 0 ? nextVersion(versions[versions.length - 1].version) : '1.0.0',
|
version: versions.length > 0 ? nextVersion(versions[versions.length - 1].version) : '1.0.0',
|
||||||
description: '',
|
description: '',
|
||||||
items: undefined
|
items: []
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const version = useWatch({ control, name: 'version' });
|
const version = useWatch({ control, name: 'version' });
|
||||||
|
@ -61,8 +61,8 @@ function DlgCreateVersion() {
|
||||||
<Checkbox
|
<Checkbox
|
||||||
id='dlg_only_selected'
|
id='dlg_only_selected'
|
||||||
label={`Только выбранные конституенты [${selected.length} из ${totalCount}]`}
|
label={`Только выбранные конституенты [${selected.length} из ${totalCount}]`}
|
||||||
value={field.value !== undefined}
|
value={field.value.length > 0}
|
||||||
onChange={value => field.onChange(value ? selected : undefined)}
|
onChange={value => field.onChange(value ? selected : [])}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -61,8 +61,8 @@ function DlgEditEditors() {
|
||||||
<div className='flex items-center gap-3'>
|
<div className='flex items-center gap-3'>
|
||||||
<Label text='Добавить' />
|
<Label text='Добавить' />
|
||||||
<SelectUser
|
<SelectUser
|
||||||
filter={id => !selected.includes(id)}
|
filter={id => !selected.includes(id)} //
|
||||||
value={undefined}
|
value={null}
|
||||||
onChange={onAddEditor}
|
onChange={onAddEditor}
|
||||||
className='w-[25rem]'
|
className='w-[25rem]'
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -9,12 +9,12 @@ export class FolderNode {
|
||||||
rank: number = 0;
|
rank: number = 0;
|
||||||
text: string;
|
text: string;
|
||||||
children: Map<string, FolderNode>;
|
children: Map<string, FolderNode>;
|
||||||
parent: FolderNode | undefined;
|
parent: FolderNode | null;
|
||||||
|
|
||||||
filesInside: number = 0;
|
filesInside: number = 0;
|
||||||
filesTotal: number = 0;
|
filesTotal: number = 0;
|
||||||
|
|
||||||
constructor(text: string, parent?: FolderNode) {
|
constructor(text: string, parent: FolderNode | null = null) {
|
||||||
this.text = text;
|
this.text = text;
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.children = new Map();
|
this.children = new Map();
|
||||||
|
@ -126,7 +126,7 @@ export class FolderTree {
|
||||||
}
|
}
|
||||||
|
|
||||||
private addNode(text: string, parent?: FolderNode): FolderNode {
|
private addNode(text: string, parent?: FolderNode): FolderNode {
|
||||||
if (parent === undefined) {
|
if (!parent) {
|
||||||
const newNode = new FolderNode(text);
|
const newNode = new FolderNode(text);
|
||||||
this.roots.set(text, newNode);
|
this.roots.set(text, newNode);
|
||||||
return newNode;
|
return newNode;
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
* Module: Models for LibraryItem.
|
* Module: Models for LibraryItem.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { LibraryItemType } from '../backend/types';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents valid location headers.
|
* Represents valid location headers.
|
||||||
*/
|
*/
|
||||||
|
@ -28,17 +26,16 @@ export interface ILibraryItemReference {
|
||||||
* Represents Library filter parameters.
|
* Represents Library filter parameters.
|
||||||
*/
|
*/
|
||||||
export interface ILibraryFilter {
|
export interface ILibraryFilter {
|
||||||
type?: LibraryItemType;
|
query: string;
|
||||||
query?: string;
|
|
||||||
|
|
||||||
folderMode?: boolean;
|
folderMode: boolean;
|
||||||
subfolders?: boolean;
|
subfolders: boolean;
|
||||||
path?: string;
|
path: string;
|
||||||
head?: LocationHead;
|
head: LocationHead | null;
|
||||||
location?: string;
|
location: string;
|
||||||
|
|
||||||
isVisible?: boolean;
|
isVisible: boolean | null;
|
||||||
isOwned?: boolean;
|
isOwned: boolean | null;
|
||||||
isEditor?: boolean;
|
isEditor: boolean | null;
|
||||||
filterUser?: number;
|
filterUser: number | null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,9 +54,9 @@ function ToolbarSearch({ total, filtered }: ToolbarSearchProps) {
|
||||||
const resetFilter = useLibrarySearchStore(state => state.resetFilter);
|
const resetFilter = useLibrarySearchStore(state => state.resetFilter);
|
||||||
const hasCustomFilter = useHasCustomFilter();
|
const hasCustomFilter = useHasCustomFilter();
|
||||||
|
|
||||||
const userActive = isOwned !== undefined || isEditor !== undefined || filterUser !== undefined;
|
const userActive = isOwned !== null || isEditor !== null || filterUser !== null;
|
||||||
|
|
||||||
function handleChange(newValue: LocationHead | undefined) {
|
function handleChange(newValue: LocationHead | null) {
|
||||||
headMenu.hide();
|
headMenu.hide();
|
||||||
setHead(newValue);
|
setHead(newValue);
|
||||||
}
|
}
|
||||||
|
@ -173,7 +173,7 @@ function ToolbarSearch({ total, filtered }: ToolbarSearchProps) {
|
||||||
<span>проводник...</span>
|
<span>проводник...</span>
|
||||||
</div>
|
</div>
|
||||||
</DropdownButton>
|
</DropdownButton>
|
||||||
<DropdownButton className='w-[10rem]' onClick={() => handleChange(undefined)}>
|
<DropdownButton className='w-[10rem]' onClick={() => handleChange(null)}>
|
||||||
<div className='inline-flex items-center gap-3'>
|
<div className='inline-flex items-center gap-3'>
|
||||||
<IconFolder size='1rem' className='clr-text-controls' />
|
<IconFolder size='1rem' className='clr-text-controls' />
|
||||||
<span>отображать все</span>
|
<span>отображать все</span>
|
||||||
|
|
|
@ -21,20 +21,20 @@ interface LibrarySearchStore {
|
||||||
location: string;
|
location: string;
|
||||||
setLocation: (value: string) => void;
|
setLocation: (value: string) => void;
|
||||||
|
|
||||||
head: LocationHead | undefined;
|
head: LocationHead | null;
|
||||||
setHead: (value: LocationHead | undefined) => void;
|
setHead: (value: LocationHead | null) => void;
|
||||||
|
|
||||||
isVisible: boolean | undefined;
|
isVisible: boolean | null;
|
||||||
toggleVisible: () => void;
|
toggleVisible: () => void;
|
||||||
|
|
||||||
isOwned: boolean | undefined;
|
isOwned: boolean | null;
|
||||||
toggleOwned: () => void;
|
toggleOwned: () => void;
|
||||||
|
|
||||||
isEditor: boolean | undefined;
|
isEditor: boolean | null;
|
||||||
toggleEditor: () => void;
|
toggleEditor: () => void;
|
||||||
|
|
||||||
filterUser: number | undefined;
|
filterUser: number | null;
|
||||||
setFilterUser: (value: number | undefined) => void;
|
setFilterUser: (value: number | null) => void;
|
||||||
|
|
||||||
resetFilter: () => void;
|
resetFilter: () => void;
|
||||||
}
|
}
|
||||||
|
@ -57,19 +57,19 @@ export const useLibrarySearchStore = create<LibrarySearchStore>()(
|
||||||
location: '',
|
location: '',
|
||||||
setLocation: value => set(!!value ? { location: value, folderMode: true } : { location: '' }),
|
setLocation: value => set(!!value ? { location: value, folderMode: true } : { location: '' }),
|
||||||
|
|
||||||
head: undefined,
|
head: null,
|
||||||
setHead: value => set({ head: value }),
|
setHead: value => set({ head: value }),
|
||||||
|
|
||||||
isVisible: true,
|
isVisible: true,
|
||||||
toggleVisible: () => set(state => ({ isVisible: toggleTristateFlag(state.isVisible) })),
|
toggleVisible: () => set(state => ({ isVisible: toggleTristateFlag(state.isVisible) })),
|
||||||
|
|
||||||
isOwned: undefined,
|
isOwned: null,
|
||||||
toggleOwned: () => set(state => ({ isOwned: toggleTristateFlag(state.isOwned) })),
|
toggleOwned: () => set(state => ({ isOwned: toggleTristateFlag(state.isOwned) })),
|
||||||
|
|
||||||
isEditor: undefined,
|
isEditor: null,
|
||||||
toggleEditor: () => set(state => ({ isEditor: toggleTristateFlag(state.isEditor) })),
|
toggleEditor: () => set(state => ({ isEditor: toggleTristateFlag(state.isEditor) })),
|
||||||
|
|
||||||
filterUser: undefined,
|
filterUser: null,
|
||||||
setFilterUser: value => set({ filterUser: value }),
|
setFilterUser: value => set({ filterUser: value }),
|
||||||
|
|
||||||
resetFilter: () =>
|
resetFilter: () =>
|
||||||
|
@ -77,11 +77,11 @@ export const useLibrarySearchStore = create<LibrarySearchStore>()(
|
||||||
query: '',
|
query: '',
|
||||||
path: '',
|
path: '',
|
||||||
location: '',
|
location: '',
|
||||||
head: undefined,
|
head: null,
|
||||||
isVisible: true,
|
isVisible: true,
|
||||||
isOwned: undefined,
|
isOwned: null,
|
||||||
isEditor: undefined,
|
isEditor: null,
|
||||||
filterUser: undefined
|
filterUser: null
|
||||||
}))
|
}))
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
|
@ -116,11 +116,11 @@ export function useHasCustomFilter(): boolean {
|
||||||
!!path ||
|
!!path ||
|
||||||
!!query ||
|
!!query ||
|
||||||
!!location ||
|
!!location ||
|
||||||
head !== undefined ||
|
head !== null ||
|
||||||
isEditor !== undefined ||
|
isEditor !== null ||
|
||||||
isOwned !== undefined ||
|
isOwned !== null ||
|
||||||
isVisible !== true ||
|
isVisible !== true ||
|
||||||
filterUser !== undefined
|
filterUser !== null
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ import { NoData } from '@/components/View';
|
||||||
|
|
||||||
import { IOperation } from '../models/oss';
|
import { IOperation } from '../models/oss';
|
||||||
|
|
||||||
import SelectOperation from './SelectOperation';
|
import { SelectOperation } from './SelectOperation';
|
||||||
|
|
||||||
interface PickMultiOperationProps extends CProps.Styling {
|
interface PickMultiOperationProps extends CProps.Styling {
|
||||||
value: number[];
|
value: number[];
|
||||||
|
@ -25,17 +25,17 @@ const columnHelper = createColumnHelper<IOperation>();
|
||||||
export function PickMultiOperation({ rows, items, value, onChange, className, ...restProps }: PickMultiOperationProps) {
|
export function PickMultiOperation({ rows, items, value, onChange, className, ...restProps }: PickMultiOperationProps) {
|
||||||
const selectedItems = value.map(itemID => items.find(item => item.id === itemID)!);
|
const selectedItems = value.map(itemID => items.find(item => item.id === itemID)!);
|
||||||
const nonSelectedItems = items.filter(item => !value.includes(item.id));
|
const nonSelectedItems = items.filter(item => !value.includes(item.id));
|
||||||
const [lastSelected, setLastSelected] = useState<IOperation | undefined>(undefined);
|
const [lastSelected, setLastSelected] = useState<IOperation | null>(null);
|
||||||
|
|
||||||
function handleDelete(operation: number) {
|
function handleDelete(operation: number) {
|
||||||
onChange(value.filter(item => item !== operation));
|
onChange(value.filter(item => item !== operation));
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSelect(operation?: IOperation) {
|
function handleSelect(operation: IOperation | null) {
|
||||||
if (operation) {
|
if (operation) {
|
||||||
setLastSelected(operation);
|
setLastSelected(operation);
|
||||||
onChange([...value, operation.id]);
|
onChange([...value, operation.id]);
|
||||||
setTimeout(() => setLastSelected(undefined), 1000);
|
setTimeout(() => setLastSelected(null), 1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,15 +9,15 @@ import { IOperation } from '../models/oss';
|
||||||
import { matchOperation } from '../models/ossAPI';
|
import { matchOperation } from '../models/ossAPI';
|
||||||
|
|
||||||
interface SelectOperationProps extends CProps.Styling {
|
interface SelectOperationProps extends CProps.Styling {
|
||||||
items?: IOperation[];
|
value: IOperation | null;
|
||||||
value?: IOperation;
|
onChange: (newValue: IOperation | null) => void;
|
||||||
onChange: (newValue?: IOperation) => void;
|
|
||||||
|
|
||||||
|
items?: IOperation[];
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
noBorder?: boolean;
|
noBorder?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
function SelectOperation({
|
export function SelectOperation({
|
||||||
className,
|
className,
|
||||||
items,
|
items,
|
||||||
value,
|
value,
|
||||||
|
@ -41,12 +41,10 @@ function SelectOperation({
|
||||||
className={clsx('text-ellipsis', className)}
|
className={clsx('text-ellipsis', className)}
|
||||||
options={options}
|
options={options}
|
||||||
value={value ? { value: value.id, label: `${value.alias}: ${value.title}` } : null}
|
value={value ? { value: value.id, label: `${value.alias}: ${value.title}` } : null}
|
||||||
onChange={data => onChange(items?.find(cst => cst.id === data?.value))}
|
onChange={data => onChange(items?.find(cst => cst.id === data?.value) ?? null)}
|
||||||
filterOption={filter}
|
filterOption={filter}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SelectOperation;
|
|
||||||
|
|
|
@ -69,7 +69,7 @@ function DlgDeleteOperation() {
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label='Удалить схему'
|
label='Удалить схему'
|
||||||
titleHtml={
|
titleHtml={
|
||||||
!target.is_owned || target.result === undefined
|
!target.is_owned || target.result === null
|
||||||
? 'Привязанную схему нельзя удалить'
|
? 'Привязанную схему нельзя удалить'
|
||||||
: 'Удалить схему вместе с операцией'
|
: 'Удалить схему вместе с операцией'
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,7 +54,6 @@ function DlgEditOperation() {
|
||||||
},
|
},
|
||||||
mode: 'onChange'
|
mode: 'onChange'
|
||||||
});
|
});
|
||||||
|
|
||||||
const [activeTab, setActiveTab] = useState(TabID.CARD);
|
const [activeTab, setActiveTab] = useState(TabID.CARD);
|
||||||
|
|
||||||
function onSubmit(data: IOperationUpdateDTO) {
|
function onSubmit(data: IOperationUpdateDTO) {
|
||||||
|
|
|
@ -46,11 +46,11 @@ function DlgRelocateConstituents() {
|
||||||
mode: 'onChange'
|
mode: 'onChange'
|
||||||
});
|
});
|
||||||
const destination = useWatch({ control, name: 'destination' });
|
const destination = useWatch({ control, name: 'destination' });
|
||||||
const destinationItem = destination ? libraryItems.find(item => item.id === destination) : undefined;
|
const destinationItem = destination ? libraryItems.find(item => item.id === destination) ?? null : null;
|
||||||
|
|
||||||
const [directionUp, setDirectionUp] = useState(true);
|
const [directionUp, setDirectionUp] = useState(true);
|
||||||
const [source, setSource] = useState<ILibraryItem | undefined>(
|
const [source, setSource] = useState<ILibraryItem | null>(
|
||||||
libraryItems.find(item => item.id === initialTarget?.result)
|
libraryItems.find(item => item.id === initialTarget?.result) ?? null
|
||||||
);
|
);
|
||||||
|
|
||||||
const operation = oss.items.find(item => item.result === source?.id);
|
const operation = oss.items.find(item => item.result === source?.id);
|
||||||
|
@ -80,13 +80,13 @@ function DlgRelocateConstituents() {
|
||||||
setValue('destination', null);
|
setValue('destination', null);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSelectSource(newValue: ILibraryItem | undefined) {
|
function handleSelectSource(newValue: ILibraryItem | null) {
|
||||||
setSource(newValue);
|
setSource(newValue);
|
||||||
setValue('destination', null);
|
setValue('destination', null);
|
||||||
setValue('items', []);
|
setValue('items', []);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSelectDestination(newValue: ILibraryItem | undefined) {
|
function handleSelectDestination(newValue: ILibraryItem | null) {
|
||||||
if (newValue) {
|
if (newValue) {
|
||||||
setValue('destination', newValue.id);
|
setValue('destination', newValue.id);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -19,7 +19,7 @@ interface BadgeConstituentaProps extends CProps.Styling {
|
||||||
/**
|
/**
|
||||||
* Displays a badge with a constituenta alias and information tooltip.
|
* Displays a badge with a constituenta alias and information tooltip.
|
||||||
*/
|
*/
|
||||||
function BadgeConstituenta({ value, prefixID, className, style }: BadgeConstituentaProps) {
|
export function BadgeConstituenta({ value, prefixID, className, style }: BadgeConstituentaProps) {
|
||||||
const setActiveCst = useTooltipsStore(state => state.setActiveCst);
|
const setActiveCst = useTooltipsStore(state => state.setActiveCst);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -46,5 +46,3 @@ function BadgeConstituenta({ value, prefixID, className, style }: BadgeConstitue
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default BadgeConstituenta;
|
|
||||||
|
|
|
@ -14,12 +14,12 @@ import { IConstituenta } from '../models/rsform';
|
||||||
import { matchConstituenta } from '../models/rsformAPI';
|
import { matchConstituenta } from '../models/rsformAPI';
|
||||||
import { CstMatchMode } from '../stores/cstSearch';
|
import { CstMatchMode } from '../stores/cstSearch';
|
||||||
|
|
||||||
import BadgeConstituenta from './BadgeConstituenta';
|
import { BadgeConstituenta } from './BadgeConstituenta';
|
||||||
|
|
||||||
interface PickConstituentaProps extends CProps.Styling {
|
interface PickConstituentaProps extends CProps.Styling {
|
||||||
id?: string;
|
id?: string;
|
||||||
items: IConstituenta[];
|
items: IConstituenta[];
|
||||||
value?: IConstituenta;
|
value: IConstituenta | null;
|
||||||
onChange: (newValue: IConstituenta) => void;
|
onChange: (newValue: IConstituenta) => void;
|
||||||
|
|
||||||
rows?: number;
|
rows?: number;
|
||||||
|
|
|
@ -14,7 +14,7 @@ import { IConstituenta, IRSForm } from '../models/rsform';
|
||||||
import { isBasicConcept, matchConstituenta } from '../models/rsformAPI';
|
import { isBasicConcept, matchConstituenta } from '../models/rsformAPI';
|
||||||
import { CstMatchMode } from '../stores/cstSearch';
|
import { CstMatchMode } from '../stores/cstSearch';
|
||||||
|
|
||||||
import BadgeConstituenta from './BadgeConstituenta';
|
import { BadgeConstituenta } from './BadgeConstituenta';
|
||||||
import ToolbarGraphSelection from './ToolbarGraphSelection';
|
import ToolbarGraphSelection from './ToolbarGraphSelection';
|
||||||
|
|
||||||
interface PickMultiConstituentaProps extends CProps.Styling {
|
interface PickMultiConstituentaProps extends CProps.Styling {
|
||||||
|
@ -112,7 +112,10 @@ export function PickMultiConstituenta({
|
||||||
/>
|
/>
|
||||||
<ToolbarGraphSelection
|
<ToolbarGraphSelection
|
||||||
graph={foldedGraph}
|
graph={foldedGraph}
|
||||||
isCore={cstID => isBasicConcept(schema.cstByID.get(cstID)?.cst_type)}
|
isCore={cstID => {
|
||||||
|
const cst = schema.cstByID.get(cstID);
|
||||||
|
return !!cst && isBasicConcept(cst.cst_type);
|
||||||
|
}}
|
||||||
isOwned={cstID => !schema.cstByID.get(cstID)?.is_inherited}
|
isOwned={cstID => !schema.cstByID.get(cstID)?.is_inherited}
|
||||||
value={value}
|
value={value}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
|
|
|
@ -17,8 +17,8 @@ import { errorMsg } from '@/utils/labels';
|
||||||
import { ICstSubstitute } from '../backend/types';
|
import { ICstSubstitute } from '../backend/types';
|
||||||
import { IConstituenta, IRSForm } from '../models/rsform';
|
import { IConstituenta, IRSForm } from '../models/rsform';
|
||||||
|
|
||||||
import BadgeConstituenta from './BadgeConstituenta';
|
import { BadgeConstituenta } from './BadgeConstituenta';
|
||||||
import SelectConstituenta from './SelectConstituenta';
|
import { SelectConstituenta } from './SelectConstituenta';
|
||||||
|
|
||||||
interface IMultiSubstitution {
|
interface IMultiSubstitution {
|
||||||
original_source: ILibraryItem;
|
original_source: ILibraryItem;
|
||||||
|
@ -54,15 +54,13 @@ export function PickSubstitutions({
|
||||||
className,
|
className,
|
||||||
...restProps
|
...restProps
|
||||||
}: PickSubstitutionsProps) {
|
}: PickSubstitutionsProps) {
|
||||||
const [leftArgument, setLeftArgument] = useState<ILibraryItem | undefined>(
|
const [leftArgument, setLeftArgument] = useState<ILibraryItem | null>(schemas.length === 1 ? schemas[0] : null);
|
||||||
schemas.length === 1 ? schemas[0] : undefined
|
const [rightArgument, setRightArgument] = useState<ILibraryItem | null>(
|
||||||
);
|
schemas.length === 1 && allowSelfSubstitution ? schemas[0] : null
|
||||||
const [rightArgument, setRightArgument] = useState<ILibraryItem | undefined>(
|
|
||||||
schemas.length === 1 && allowSelfSubstitution ? schemas[0] : undefined
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const [leftCst, setLeftCst] = useState<IConstituenta | undefined>(undefined);
|
const [leftCst, setLeftCst] = useState<IConstituenta | null>(null);
|
||||||
const [rightCst, setRightCst] = useState<IConstituenta | undefined>(undefined);
|
const [rightCst, setRightCst] = useState<IConstituenta | null>(null);
|
||||||
|
|
||||||
const [deleteRight, setDeleteRight] = useState(true);
|
const [deleteRight, setDeleteRight] = useState(true);
|
||||||
const toggleDelete = () => setDeleteRight(prev => !prev);
|
const toggleDelete = () => setDeleteRight(prev => !prev);
|
||||||
|
@ -135,8 +133,8 @@ export function PickSubstitutions({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onChange([...value, newSubstitution]);
|
onChange([...value, newSubstitution]);
|
||||||
setLeftCst(undefined);
|
setLeftCst(null);
|
||||||
setRightCst(undefined);
|
setRightCst(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleDeclineSuggestion(item: IMultiSubstitution) {
|
function handleDeclineSuggestion(item: IMultiSubstitution) {
|
||||||
|
|
|
@ -23,7 +23,7 @@ export const tooltipProducer = (schema: IRSForm, canClick?: boolean) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('entity' in parse.ref) {
|
if ('entity' in parse.ref) {
|
||||||
const cst = schema.cstByAlias.get(parse.ref.entity);
|
const cst = schema.cstByAlias.get(parse.ref.entity) ?? null;
|
||||||
return {
|
return {
|
||||||
pos: parse.start,
|
pos: parse.start,
|
||||||
end: parse.end,
|
end: parse.end,
|
||||||
|
@ -31,7 +31,7 @@ export const tooltipProducer = (schema: IRSForm, canClick?: boolean) => {
|
||||||
create: () => domTooltipEntityReference(parse.ref as IEntityReference, cst, canClick)
|
create: () => domTooltipEntityReference(parse.ref as IEntityReference, cst, canClick)
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
let masterText: string | undefined = undefined;
|
let masterText: string | null = null;
|
||||||
if (parse.ref.offset > 0) {
|
if (parse.ref.offset > 0) {
|
||||||
const entities = findContainedNodes(parse.end, view.state.doc.length, syntaxTree(view.state), [RefEntity]);
|
const entities = findContainedNodes(parse.end, view.state.doc.length, syntaxTree(view.state), [RefEntity]);
|
||||||
if (parse.ref.offset <= entities.length) {
|
if (parse.ref.offset <= entities.length) {
|
||||||
|
@ -62,11 +62,7 @@ export function refsHoverTooltip(schema: IRSForm, canClick?: boolean): Extension
|
||||||
/**
|
/**
|
||||||
* Create DOM tooltip for {@link IEntityReference}.
|
* Create DOM tooltip for {@link IEntityReference}.
|
||||||
*/
|
*/
|
||||||
function domTooltipEntityReference(
|
function domTooltipEntityReference(ref: IEntityReference, cst: IConstituenta | null, canClick?: boolean): TooltipView {
|
||||||
ref: IEntityReference,
|
|
||||||
cst: IConstituenta | undefined,
|
|
||||||
canClick?: boolean
|
|
||||||
): TooltipView {
|
|
||||||
const dom = document.createElement('div');
|
const dom = document.createElement('div');
|
||||||
dom.className = clsx(
|
dom.className = clsx(
|
||||||
'max-h-[25rem] max-w-[25rem] min-w-[10rem]',
|
'max-h-[25rem] max-w-[25rem] min-w-[10rem]',
|
||||||
|
@ -122,7 +118,7 @@ function domTooltipEntityReference(
|
||||||
*/
|
*/
|
||||||
function domTooltipSyntacticReference(
|
function domTooltipSyntacticReference(
|
||||||
ref: ISyntacticReference,
|
ref: ISyntacticReference,
|
||||||
masterRef: string | undefined,
|
masterRef: string | null,
|
||||||
canClick?: boolean
|
canClick?: boolean
|
||||||
): TooltipView {
|
): TooltipView {
|
||||||
const dom = document.createElement('div');
|
const dom = document.createElement('div');
|
||||||
|
|
|
@ -11,15 +11,15 @@ import { matchConstituenta } from '../models/rsformAPI';
|
||||||
import { CstMatchMode } from '../stores/cstSearch';
|
import { CstMatchMode } from '../stores/cstSearch';
|
||||||
|
|
||||||
interface SelectConstituentaProps extends CProps.Styling {
|
interface SelectConstituentaProps extends CProps.Styling {
|
||||||
value?: IConstituenta;
|
value: IConstituenta | null;
|
||||||
onChange: (newValue?: IConstituenta) => void;
|
onChange: (newValue: IConstituenta | null) => void;
|
||||||
|
|
||||||
items?: IConstituenta[];
|
items?: IConstituenta[];
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
noBorder?: boolean;
|
noBorder?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
function SelectConstituenta({
|
export function SelectConstituenta({
|
||||||
className,
|
className,
|
||||||
items,
|
items,
|
||||||
value,
|
value,
|
||||||
|
@ -43,12 +43,10 @@ function SelectConstituenta({
|
||||||
className={clsx('text-ellipsis', className)}
|
className={clsx('text-ellipsis', className)}
|
||||||
options={options}
|
options={options}
|
||||||
value={value ? { value: value.id, label: `${value.alias}: ${describeConstituentaTerm(value)}` } : null}
|
value={value ? { value: value.id, label: `${value.alias}: ${describeConstituentaTerm(value)}` } : null}
|
||||||
onChange={data => onChange(items?.find(cst => cst.id === data?.value))}
|
onChange={data => onChange(items?.find(cst => cst.id === data?.value) ?? null)}
|
||||||
filterOption={filter}
|
filterOption={filter}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SelectConstituenta;
|
|
||||||
|
|
|
@ -29,10 +29,8 @@ function TabArguments() {
|
||||||
const { args, onChangeArguments } = useTemplateContext();
|
const { args, onChangeArguments } = useTemplateContext();
|
||||||
const definition = useWatch({ control, name: 'definition_formal' });
|
const definition = useWatch({ control, name: 'definition_formal' });
|
||||||
|
|
||||||
const [selectedCst, setSelectedCst] = useState<IConstituenta | undefined>(undefined);
|
const [selectedCst, setSelectedCst] = useState<IConstituenta | null>(null);
|
||||||
const [selectedArgument, setSelectedArgument] = useState<IArgumentValue | undefined>(
|
const [selectedArgument, setSelectedArgument] = useState<IArgumentValue | null>(args.length > 0 ? args[0] : null);
|
||||||
args.length > 0 ? args[0] : undefined
|
|
||||||
);
|
|
||||||
|
|
||||||
const [argumentValue, setArgumentValue] = useState('');
|
const [argumentValue, setArgumentValue] = useState('');
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ function TabTemplate() {
|
||||||
} = useTemplateContext();
|
} = useTemplateContext();
|
||||||
|
|
||||||
const { templates } = useTemplatesSuspense();
|
const { templates } = useTemplatesSuspense();
|
||||||
const { schema: templateSchema } = useRSForm({ itemID: templateID });
|
const { schema: templateSchema } = useRSForm({ itemID: templateID ?? undefined });
|
||||||
|
|
||||||
if (!templateID) {
|
if (!templateID) {
|
||||||
onChangeTemplateID(templates[0].id);
|
onChangeTemplateID(templates[0].id);
|
||||||
|
@ -63,7 +63,7 @@ function TabTemplate() {
|
||||||
className='w-[12rem]'
|
className='w-[12rem]'
|
||||||
options={templateSelector}
|
options={templateSelector}
|
||||||
value={templateID ? { value: templateID, label: templates.find(item => item.id == templateID)!.title } : null}
|
value={templateID ? { value: templateID, label: templates.find(item => item.id == templateID)!.title } : null}
|
||||||
onChange={data => onChangeTemplateID(data ? data.value : undefined)}
|
onChange={data => onChangeTemplateID(data ? data.value : null)}
|
||||||
/>
|
/>
|
||||||
<SelectSingle
|
<SelectSingle
|
||||||
noBorder
|
noBorder
|
||||||
|
@ -79,7 +79,7 @@ function TabTemplate() {
|
||||||
}
|
}
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
onChange={data => onChangeFilterCategory(data ? templateSchema?.cstByID.get(data?.value) : undefined)}
|
onChange={data => onChangeFilterCategory(data ? templateSchema?.cstByID.get(data?.value) ?? null : null)}
|
||||||
isClearable
|
isClearable
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -15,14 +15,14 @@ import { DlgCstTemplateProps } from './DlgCstTemplate';
|
||||||
|
|
||||||
export interface ITemplateContext {
|
export interface ITemplateContext {
|
||||||
args: IArgumentValue[];
|
args: IArgumentValue[];
|
||||||
prototype?: IConstituenta;
|
prototype: IConstituenta | null;
|
||||||
templateID?: number;
|
templateID: number | null;
|
||||||
filterCategory?: IConstituenta;
|
filterCategory: IConstituenta | null;
|
||||||
|
|
||||||
onChangeArguments: (newArgs: IArgumentValue[]) => void;
|
onChangeArguments: (newArgs: IArgumentValue[]) => void;
|
||||||
onChangePrototype: (newPrototype: IConstituenta) => void;
|
onChangePrototype: (newPrototype: IConstituenta) => void;
|
||||||
onChangeTemplateID: (newTemplateID: number | undefined) => void;
|
onChangeTemplateID: (newTemplateID: number | null) => void;
|
||||||
onChangeFilterCategory: (newFilterCategory: IConstituenta | undefined) => void;
|
onChangeFilterCategory: (newFilterCategory: IConstituenta | null) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TemplateContext = createContext<ITemplateContext | null>(null);
|
const TemplateContext = createContext<ITemplateContext | null>(null);
|
||||||
|
@ -37,10 +37,10 @@ export const useTemplateContext = () => {
|
||||||
export const TemplateState = ({ children }: React.PropsWithChildren) => {
|
export const TemplateState = ({ children }: React.PropsWithChildren) => {
|
||||||
const { schema } = useDialogsStore(state => state.props as DlgCstTemplateProps);
|
const { schema } = useDialogsStore(state => state.props as DlgCstTemplateProps);
|
||||||
const { setValue } = useFormContext<ICstCreateDTO>();
|
const { setValue } = useFormContext<ICstCreateDTO>();
|
||||||
const [templateID, setTemplateID] = useState<number | undefined>(undefined);
|
const [templateID, setTemplateID] = useState<number | null>(null);
|
||||||
const [args, setArguments] = useState<IArgumentValue[]>([]);
|
const [args, setArguments] = useState<IArgumentValue[]>([]);
|
||||||
const [prototype, setPrototype] = useState<IConstituenta | undefined>(undefined);
|
const [prototype, setPrototype] = useState<IConstituenta | null>(null);
|
||||||
const [filterCategory, setFilterCategory] = useState<IConstituenta | undefined>(undefined);
|
const [filterCategory, setFilterCategory] = useState<IConstituenta | null>(null);
|
||||||
|
|
||||||
function onChangeArguments(newArgs: IArgumentValue[]) {
|
function onChangeArguments(newArgs: IArgumentValue[]) {
|
||||||
setArguments(newArgs);
|
setArguments(newArgs);
|
||||||
|
@ -70,9 +70,9 @@ export const TemplateState = ({ children }: React.PropsWithChildren) => {
|
||||||
setValue('definition_raw', newPrototype.definition_raw);
|
setValue('definition_raw', newPrototype.definition_raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onChangeTemplateID(newTemplateID: number | undefined) {
|
function onChangeTemplateID(newTemplateID: number | null) {
|
||||||
setTemplateID(newTemplateID);
|
setTemplateID(newTemplateID);
|
||||||
setPrototype(undefined);
|
setPrototype(null);
|
||||||
setArguments([]);
|
setArguments([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ export function TabEntityReference() {
|
||||||
const { setValue, control, register } = useFormContext<IEditReferenceState>();
|
const { setValue, control, register } = useFormContext<IEditReferenceState>();
|
||||||
const alias = useWatch({ control, name: 'entity.entity' });
|
const alias = useWatch({ control, name: 'entity.entity' });
|
||||||
|
|
||||||
const selectedCst = schema.cstByAlias.get(alias);
|
const selectedCst = schema.cstByAlias.get(alias) ?? null;
|
||||||
const term = selectedCst?.term_resolved ?? '';
|
const term = selectedCst?.term_resolved ?? '';
|
||||||
|
|
||||||
function handleSelectConstituenta(cst: IConstituenta) {
|
function handleSelectConstituenta(cst: IConstituenta) {
|
||||||
|
|
|
@ -20,7 +20,7 @@ export interface DlgShowASTProps {
|
||||||
|
|
||||||
function DlgShowAST() {
|
function DlgShowAST() {
|
||||||
const { syntaxTree, expression } = useDialogsStore(state => state.props as DlgShowASTProps);
|
const { syntaxTree, expression } = useDialogsStore(state => state.props as DlgShowASTProps);
|
||||||
const [hoverID, setHoverID] = useState<number | undefined>(undefined);
|
const [hoverID, setHoverID] = useState<number | null>(null);
|
||||||
const hoverNode = syntaxTree.find(node => node.uid === hoverID);
|
const hoverNode = syntaxTree.find(node => node.uid === hoverID);
|
||||||
|
|
||||||
const [isDragging, setIsDragging] = useState(false);
|
const [isDragging, setIsDragging] = useState(false);
|
||||||
|
@ -47,7 +47,7 @@ function DlgShowAST() {
|
||||||
<ASTFlow
|
<ASTFlow
|
||||||
data={syntaxTree}
|
data={syntaxTree}
|
||||||
onNodeEnter={node => setHoverID(Number(node.id))}
|
onNodeEnter={node => setHoverID(Number(node.id))}
|
||||||
onNodeLeave={() => setHoverID(undefined)}
|
onNodeLeave={() => setHoverID(null)}
|
||||||
onChangeDragging={setIsDragging}
|
onChangeDragging={setIsDragging}
|
||||||
/>
|
/>
|
||||||
</ReactFlowProvider>
|
</ReactFlowProvider>
|
||||||
|
|
|
@ -11,8 +11,7 @@ export function applyLayout(nodes: Node<ISyntaxTreeNode>[], edges: Edge[]) {
|
||||||
rankdir: 'TB',
|
rankdir: 'TB',
|
||||||
ranksep: 40,
|
ranksep: 40,
|
||||||
nodesep: 40,
|
nodesep: 40,
|
||||||
ranker: 'network-simplex',
|
ranker: 'network-simplex'
|
||||||
align: undefined
|
|
||||||
});
|
});
|
||||||
nodes.forEach(node => {
|
nodes.forEach(node => {
|
||||||
dagreGraph.setNode(node.id, { width: 2 * PARAMETER.graphNodeRadius, height: 2 * PARAMETER.graphNodeRadius });
|
dagreGraph.setNode(node.id, { width: 2 * PARAMETER.graphNodeRadius, height: 2 * PARAMETER.graphNodeRadius });
|
||||||
|
|
|
@ -17,8 +17,7 @@ export function applyLayout(nodes: Node<TMGraphNode>[], edges: Edge[]) {
|
||||||
rankdir: 'BT',
|
rankdir: 'BT',
|
||||||
ranksep: VERT_SEPARATION,
|
ranksep: VERT_SEPARATION,
|
||||||
nodesep: HOR_SEPARATION,
|
nodesep: HOR_SEPARATION,
|
||||||
ranker: 'network-simplex',
|
ranker: 'network-simplex'
|
||||||
align: undefined
|
|
||||||
});
|
});
|
||||||
nodes.forEach(node => {
|
nodes.forEach(node => {
|
||||||
dagreGraph.setNode(node.id, { width: NODE_WIDTH, height: NODE_HEIGHT });
|
dagreGraph.setNode(node.id, { width: NODE_WIDTH, height: NODE_HEIGHT });
|
||||||
|
|
|
@ -17,7 +17,7 @@ function DlgUploadRSForm() {
|
||||||
const { itemID } = useDialogsStore(state => state.props as DlgUploadRSFormProps);
|
const { itemID } = useDialogsStore(state => state.props as DlgUploadRSFormProps);
|
||||||
const { upload } = useUploadTRS();
|
const { upload } = useUploadTRS();
|
||||||
const [loadMetadata, setLoadMetadata] = useState(false);
|
const [loadMetadata, setLoadMetadata] = useState(false);
|
||||||
const [file, setFile] = useState<File | undefined>();
|
const [file, setFile] = useState<File | null>(null);
|
||||||
|
|
||||||
const handleSubmit = () => {
|
const handleSubmit = () => {
|
||||||
if (file) {
|
if (file) {
|
||||||
|
@ -34,7 +34,7 @@ function DlgUploadRSForm() {
|
||||||
if (event.target.files && event.target.files.length > 0) {
|
if (event.target.files && event.target.files.length > 0) {
|
||||||
setFile(event.target.files[0]);
|
setFile(event.target.files[0]);
|
||||||
} else {
|
} else {
|
||||||
setFile(undefined);
|
setFile(null);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@ export function describeConstituenta(cst: IConstituenta): string {
|
||||||
/**
|
/**
|
||||||
* Generates description for term of a given {@link IConstituenta}.
|
* Generates description for term of a given {@link IConstituenta}.
|
||||||
*/
|
*/
|
||||||
export function describeConstituentaTerm(cst?: IConstituenta): string {
|
export function describeConstituentaTerm(cst: IConstituenta | null): string {
|
||||||
if (!cst) {
|
if (!cst) {
|
||||||
return '!Конституента отсутствует!';
|
return '!Конституента отсутствует!';
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,7 @@ export function labelConstituenta(cst: IConstituenta) {
|
||||||
/**
|
/**
|
||||||
* Generates label for {@link IVersionInfo} of {@link IRSForm}.
|
* Generates label for {@link IVersionInfo} of {@link IRSForm}.
|
||||||
*/
|
*/
|
||||||
export function labelVersion(schema?: IRSForm) {
|
export function labelVersion(schema: IRSForm | undefined) {
|
||||||
const version = schema?.versions.find(ver => ver.id === schema?.version);
|
const version = schema?.versions.find(ver => ver.id === schema?.version);
|
||||||
return version ? version.version : 'актуальная';
|
return version ? version.version : 'актуальная';
|
||||||
}
|
}
|
||||||
|
|
|
@ -122,9 +122,9 @@ export class TMGraph {
|
||||||
this.nodeByAlias.set(alias, nodeToAnnotate);
|
this.nodeByAlias.set(alias, nodeToAnnotate);
|
||||||
}
|
}
|
||||||
|
|
||||||
private processArguments(args: IArgumentInfo[]): TMGraphNode | undefined {
|
private processArguments(args: IArgumentInfo[]): TMGraphNode | null {
|
||||||
if (args.length === 0) {
|
if (args.length === 0) {
|
||||||
return undefined;
|
return null;
|
||||||
}
|
}
|
||||||
const argsNodes = args.map(argument => this.parseToNode(argument.typification));
|
const argsNodes = args.map(argument => this.parseToNode(argument.typification));
|
||||||
if (args.length === 1) {
|
if (args.length === 1) {
|
||||||
|
@ -133,16 +133,16 @@ export class TMGraph {
|
||||||
return this.addCartesianNode(argsNodes.map(node => node.id));
|
return this.addCartesianNode(argsNodes.map(node => node.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
private processResult(result: string): TMGraphNode | undefined {
|
private processResult(result: string): TMGraphNode | null {
|
||||||
if (!result || result === PARAMETER.logicLabel) {
|
if (!result || result === PARAMETER.logicLabel) {
|
||||||
return undefined;
|
return null;
|
||||||
}
|
}
|
||||||
return this.parseToNode(result);
|
return this.parseToNode(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
private combineResults(result: TMGraphNode | undefined, args: TMGraphNode | undefined): TMGraphNode | undefined {
|
private combineResults(result: TMGraphNode | null, args: TMGraphNode | null): TMGraphNode | null {
|
||||||
if (!result && !args) {
|
if (!result && !args) {
|
||||||
return undefined;
|
return null;
|
||||||
}
|
}
|
||||||
if (!result) {
|
if (!result) {
|
||||||
return this.addBooleanNode(args!.id);
|
return this.addBooleanNode(args!.id);
|
||||||
|
|
|
@ -164,7 +164,7 @@ export function guessCstType(hint: string, defaultType: CstType = CstType.TERM):
|
||||||
/**
|
/**
|
||||||
* Evaluate if {@link CstType} is basic concept.
|
* Evaluate if {@link CstType} is basic concept.
|
||||||
*/
|
*/
|
||||||
export function isBasicConcept(type?: CstType): boolean {
|
export function isBasicConcept(type: CstType): boolean {
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case CstType.BASE: return true;
|
case CstType.BASE: return true;
|
||||||
|
@ -175,7 +175,6 @@ export function isBasicConcept(type?: CstType): boolean {
|
||||||
case CstType.FUNCTION: return false;
|
case CstType.FUNCTION: return false;
|
||||||
case CstType.PREDICATE: return false;
|
case CstType.PREDICATE: return false;
|
||||||
case CstType.THEOREM: return false;
|
case CstType.THEOREM: return false;
|
||||||
case undefined: return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ import { useRSEdit } from '../RSEditContext';
|
||||||
import { ViewConstituents } from '../ViewConstituents';
|
import { ViewConstituents } from '../ViewConstituents';
|
||||||
|
|
||||||
import EditorControls from './EditorControls';
|
import EditorControls from './EditorControls';
|
||||||
import FormConstituenta from './FormConstituenta';
|
import { FormConstituenta } from './FormConstituenta';
|
||||||
import ToolbarConstituenta from './ToolbarConstituenta';
|
import ToolbarConstituenta from './ToolbarConstituenta';
|
||||||
|
|
||||||
// Threshold window width to switch layout.
|
// Threshold window width to switch layout.
|
||||||
|
|
|
@ -23,7 +23,7 @@ import { RefsInput } from '../../../components/RefsInput';
|
||||||
import { labelCstTypification, labelTypification } from '../../../labels';
|
import { labelCstTypification, labelTypification } from '../../../labels';
|
||||||
import { IConstituenta, IRSForm } from '../../../models/rsform';
|
import { IConstituenta, IRSForm } from '../../../models/rsform';
|
||||||
import { isBaseSet, isBasicConcept, isFunctional } from '../../../models/rsformAPI';
|
import { isBaseSet, isBasicConcept, isFunctional } from '../../../models/rsformAPI';
|
||||||
import EditorRSExpression from '../EditorRSExpression';
|
import { EditorRSExpression } from '../EditorRSExpression';
|
||||||
|
|
||||||
interface FormConstituentaProps {
|
interface FormConstituentaProps {
|
||||||
id?: string;
|
id?: string;
|
||||||
|
@ -32,10 +32,10 @@ interface FormConstituentaProps {
|
||||||
|
|
||||||
activeCst: IConstituenta;
|
activeCst: IConstituenta;
|
||||||
schema: IRSForm;
|
schema: IRSForm;
|
||||||
onOpenEdit?: (cstID: number) => void;
|
onOpenEdit: (cstID: number) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
function FormConstituenta({ disabled, id, toggleReset, schema, activeCst, onOpenEdit }: FormConstituentaProps) {
|
export function FormConstituenta({ disabled, id, toggleReset, schema, activeCst, onOpenEdit }: FormConstituentaProps) {
|
||||||
const { cstUpdate } = useCstUpdate();
|
const { cstUpdate } = useCstUpdate();
|
||||||
const showTypification = useDialogsStore(state => state.showShowTypeGraph);
|
const showTypification = useDialogsStore(state => state.showShowTypeGraph);
|
||||||
const { isModified, setIsModified } = useModificationStore();
|
const { isModified, setIsModified } = useModificationStore();
|
||||||
|
@ -49,7 +49,7 @@ function FormConstituenta({ disabled, id, toggleReset, schema, activeCst, onOpen
|
||||||
formState: { isDirty }
|
formState: { isDirty }
|
||||||
} = useForm<ICstUpdateDTO>({ resolver: zodResolver(schemaCstUpdate) });
|
} = useForm<ICstUpdateDTO>({ resolver: zodResolver(schemaCstUpdate) });
|
||||||
|
|
||||||
const [localParse, setLocalParse] = useState<IExpressionParseDTO | undefined>(undefined);
|
const [localParse, setLocalParse] = useState<IExpressionParseDTO | null>(null);
|
||||||
|
|
||||||
const typification = useMemo(
|
const typification = useMemo(
|
||||||
() =>
|
() =>
|
||||||
|
@ -88,7 +88,7 @@ function FormConstituenta({ disabled, id, toggleReset, schema, activeCst, onOpen
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
setForceComment(false);
|
setForceComment(false);
|
||||||
setLocalParse(undefined);
|
setLocalParse(null);
|
||||||
}, [activeCst, schema, toggleReset, reset]);
|
}, [activeCst, schema, toggleReset, reset]);
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
|
@ -252,5 +252,3 @@ function FormConstituenta({ disabled, id, toggleReset, schema, activeCst, onOpen
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default FormConstituenta;
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ import { IConstituenta } from '../../../models/rsform';
|
||||||
import { RSTabID, useRSEdit } from '../RSEditContext';
|
import { RSTabID, useRSEdit } from '../RSEditContext';
|
||||||
|
|
||||||
interface ToolbarConstituentaProps {
|
interface ToolbarConstituentaProps {
|
||||||
activeCst?: IConstituenta;
|
activeCst: IConstituenta | null;
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
|
|
||||||
onSubmit: () => void;
|
onSubmit: () => void;
|
||||||
|
@ -103,7 +103,9 @@ function ToolbarConstituenta({
|
||||||
title='Создать конституенту после данной'
|
title='Создать конституенту после данной'
|
||||||
icon={<IconNewItem size='1.25rem' className='icon-green' />}
|
icon={<IconNewItem size='1.25rem' className='icon-green' />}
|
||||||
disabled={!controller.isContentEditable || isProcessing}
|
disabled={!controller.isContentEditable || isProcessing}
|
||||||
onClick={() => controller.createCst(activeCst?.cst_type, false)}
|
onClick={() =>
|
||||||
|
activeCst ? controller.createCst(activeCst.cst_type, false) : controller.createCstDefault()
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
titleHtml={isModified ? tooltipText.unsaved : prepareTooltip('Клонировать конституенту', 'Alt + V')}
|
titleHtml={isModified ? tooltipText.unsaved : prepareTooltip('Клонировать конституенту', 'Alt + V')}
|
||||||
|
|
|
@ -6,7 +6,6 @@ import { ReactCodeMirrorRef } from '@uiw/react-codemirror';
|
||||||
|
|
||||||
import { BadgeHelp, HelpTopic } from '@/features/help';
|
import { BadgeHelp, HelpTopic } from '@/features/help';
|
||||||
|
|
||||||
import { DataCallback } from '@/backend/apiTransport';
|
|
||||||
import { Overlay } from '@/components/Container';
|
import { Overlay } from '@/components/Container';
|
||||||
import { CProps } from '@/components/props';
|
import { CProps } from '@/components/props';
|
||||||
import { useDialogsStore } from '@/stores/dialogs';
|
import { useDialogsStore } from '@/stores/dialogs';
|
||||||
|
@ -24,10 +23,10 @@ import { getDefinitionPrefix } from '../../../models/rsformAPI';
|
||||||
import { transformAST } from '../../../models/rslangAPI';
|
import { transformAST } from '../../../models/rslangAPI';
|
||||||
import { useRSEdit } from '../RSEditContext';
|
import { useRSEdit } from '../RSEditContext';
|
||||||
|
|
||||||
import ParsingResult from './ParsingResult';
|
import { ParsingResult } from './ParsingResult';
|
||||||
import RSEditorControls from './RSEditControls';
|
import { RSEditorControls } from './RSEditControls';
|
||||||
import StatusBar from './StatusBar';
|
import { StatusBar } from './StatusBar';
|
||||||
import ToolbarRSExpression from './ToolbarRSExpression';
|
import { ToolbarRSExpression } from './ToolbarRSExpression';
|
||||||
|
|
||||||
interface EditorRSExpressionProps {
|
interface EditorRSExpressionProps {
|
||||||
id?: string;
|
id?: string;
|
||||||
|
@ -41,12 +40,12 @@ interface EditorRSExpressionProps {
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
toggleReset?: boolean;
|
toggleReset?: boolean;
|
||||||
|
|
||||||
onChangeLocalParse: (typification: IExpressionParseDTO | undefined) => void;
|
onChangeLocalParse: (typification: IExpressionParseDTO) => void;
|
||||||
onOpenEdit?: (cstID: number) => void;
|
onOpenEdit: (cstID: number) => void;
|
||||||
onShowTypeGraph: (event: CProps.EventMouse) => void;
|
onShowTypeGraph: (event: CProps.EventMouse) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
function EditorRSExpression({
|
export function EditorRSExpression({
|
||||||
activeCst,
|
activeCst,
|
||||||
disabled,
|
disabled,
|
||||||
value,
|
value,
|
||||||
|
@ -61,7 +60,7 @@ function EditorRSExpression({
|
||||||
|
|
||||||
const [isModified, setIsModified] = useState(false);
|
const [isModified, setIsModified] = useState(false);
|
||||||
const rsInput = useRef<ReactCodeMirrorRef>(null);
|
const rsInput = useRef<ReactCodeMirrorRef>(null);
|
||||||
const [parseData, setParseData] = useState<IExpressionParseDTO | undefined>(undefined);
|
const [parseData, setParseData] = useState<IExpressionParseDTO | null>(null);
|
||||||
|
|
||||||
const isProcessing = useMutatingRSForm();
|
const isProcessing = useMutatingRSForm();
|
||||||
const showControls = usePreferencesStore(state => state.showExpressionControls);
|
const showControls = usePreferencesStore(state => state.showExpressionControls);
|
||||||
|
@ -72,7 +71,7 @@ function EditorRSExpression({
|
||||||
function checkConstituenta(
|
function checkConstituenta(
|
||||||
expression: string,
|
expression: string,
|
||||||
activeCst: IConstituenta,
|
activeCst: IConstituenta,
|
||||||
onSuccess?: DataCallback<IExpressionParseDTO>
|
onSuccess?: (data: IExpressionParseDTO) => void
|
||||||
) {
|
) {
|
||||||
const data: ICheckConstituentaDTO = {
|
const data: ICheckConstituentaDTO = {
|
||||||
definition_formal: expression,
|
definition_formal: expression,
|
||||||
|
@ -87,7 +86,7 @@ function EditorRSExpression({
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setIsModified(false);
|
setIsModified(false);
|
||||||
setParseData(undefined);
|
setParseData(null);
|
||||||
}, [activeCst, toggleReset]);
|
}, [activeCst, toggleReset]);
|
||||||
|
|
||||||
function handleChange(newValue: string) {
|
function handleChange(newValue: string) {
|
||||||
|
@ -200,5 +199,3 @@ function EditorRSExpression({
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default EditorRSExpression;
|
|
||||||
|
|
|
@ -3,13 +3,13 @@ import { describeRSError } from '../../../labels';
|
||||||
import { getRSErrorPrefix } from '../../../models/rslangAPI';
|
import { getRSErrorPrefix } from '../../../models/rslangAPI';
|
||||||
|
|
||||||
interface ParsingResultProps {
|
interface ParsingResultProps {
|
||||||
data: IExpressionParseDTO | undefined;
|
data: IExpressionParseDTO | null;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
onShowError: (error: IRSErrorDescription) => void;
|
onShowError: (error: IRSErrorDescription) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
function ParsingResult({ isOpen, data, disabled, onShowError }: ParsingResultProps) {
|
export function ParsingResult({ isOpen, data, disabled, onShowError }: ParsingResultProps) {
|
||||||
const errorCount = data ? data.errors.reduce((total, error) => (error.isCritical ? total + 1 : total), 0) : 0;
|
const errorCount = data ? data.errors.reduce((total, error) => (error.isCritical ? total + 1 : total), 0) : 0;
|
||||||
const warningsCount = data ? data.errors.length - errorCount : 0;
|
const warningsCount = data ? data.errors.length - errorCount : 0;
|
||||||
|
|
||||||
|
@ -46,5 +46,3 @@ function ParsingResult({ isOpen, data, disabled, onShowError }: ParsingResultPro
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ParsingResult;
|
|
||||||
|
|
|
@ -87,7 +87,7 @@ interface RSEditorControlsProps {
|
||||||
onEdit: (id: TokenID, key?: string) => void;
|
onEdit: (id: TokenID, key?: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
function RSEditorControls({ isOpen, disabled, onEdit }: RSEditorControlsProps) {
|
export function RSEditorControls({ isOpen, disabled, onEdit }: RSEditorControlsProps) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={clsx(
|
className={clsx(
|
||||||
|
@ -149,5 +149,3 @@ function RSEditorControls({ isOpen, disabled, onEdit }: RSEditorControlsProps) {
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default RSEditorControls;
|
|
||||||
|
|
|
@ -15,14 +15,14 @@ import { ExpressionStatus, IConstituenta } from '../../../models/rsform';
|
||||||
import { inferStatus } from '../../../models/rsformAPI';
|
import { inferStatus } from '../../../models/rsformAPI';
|
||||||
|
|
||||||
interface StatusBarProps {
|
interface StatusBarProps {
|
||||||
processing?: boolean;
|
processing: boolean;
|
||||||
isModified?: boolean;
|
isModified: boolean;
|
||||||
parseData?: IExpressionParseDTO;
|
parseData: IExpressionParseDTO | null;
|
||||||
activeCst: IConstituenta;
|
activeCst: IConstituenta;
|
||||||
onAnalyze: () => void;
|
onAnalyze: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
function StatusBar({ isModified, processing, activeCst, parseData, onAnalyze }: StatusBarProps) {
|
export function StatusBar({ isModified, processing, activeCst, parseData, onAnalyze }: StatusBarProps) {
|
||||||
const status = (() => {
|
const status = (() => {
|
||||||
if (isModified) {
|
if (isModified) {
|
||||||
return ExpressionStatus.UNKNOWN;
|
return ExpressionStatus.UNKNOWN;
|
||||||
|
@ -66,5 +66,3 @@ function StatusBar({ isModified, processing, activeCst, parseData, onAnalyze }:
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default StatusBar;
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ interface ToolbarRSExpressionProps {
|
||||||
showTypeGraph: (event: CProps.EventMouse) => void;
|
showTypeGraph: (event: CProps.EventMouse) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
function ToolbarRSExpression({ disabled, showTypeGraph, showAST }: ToolbarRSExpressionProps) {
|
export function ToolbarRSExpression({ disabled, showTypeGraph, showAST }: ToolbarRSExpressionProps) {
|
||||||
const isProcessing = useMutatingRSForm();
|
const isProcessing = useMutatingRSForm();
|
||||||
const showControls = usePreferencesStore(state => state.showExpressionControls);
|
const showControls = usePreferencesStore(state => state.showExpressionControls);
|
||||||
const toggleControls = usePreferencesStore(state => state.toggleShowExpressionControls);
|
const toggleControls = usePreferencesStore(state => state.toggleShowExpressionControls);
|
||||||
|
@ -39,5 +39,3 @@ function ToolbarRSExpression({ disabled, showTypeGraph, showAST }: ToolbarRSExpr
|
||||||
</Overlay>
|
</Overlay>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ToolbarRSExpression;
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
export { default } from './EditorRSExpression';
|
export { EditorRSExpression } from './EditorRSExpression';
|
||||||
|
|
|
@ -30,7 +30,7 @@ function ToolbarVersioning({ blockReload }: ToolbarVersioningProps) {
|
||||||
if (!controller.schema.version || !window.confirm(promptText.restoreArchive)) {
|
if (!controller.schema.version || !window.confirm(promptText.restoreArchive)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
void versionRestore({ versionID: controller.schema.version }).then(() => controller.navigateVersion(undefined));
|
void versionRestore({ versionID: controller.schema.version }).then(() => controller.navigateVersion());
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleCreateVersion() {
|
function handleCreateVersion() {
|
||||||
|
@ -50,7 +50,7 @@ function ToolbarVersioning({ blockReload }: ToolbarVersioningProps) {
|
||||||
showEditVersions({
|
showEditVersions({
|
||||||
itemID: controller.schema.id,
|
itemID: controller.schema.id,
|
||||||
afterDelete: targetVersion => {
|
afterDelete: targetVersion => {
|
||||||
if (targetVersion === controller.activeVersion) controller.navigateVersion(undefined);
|
if (targetVersion === controller.activeVersion) controller.navigateVersion();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,7 +100,7 @@ function EditorRSList() {
|
||||||
}
|
}
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case 'Backquote': controller.createCst(undefined, false); return true;
|
case 'Backquote': controller.createCstDefault(); return true;
|
||||||
|
|
||||||
case 'Digit1': controller.createCst(CstType.BASE, true); return true;
|
case 'Digit1': controller.createCst(CstType.BASE, true); return true;
|
||||||
case 'Digit2': controller.createCst(CstType.STRUCTURED, true); return true;
|
case 'Digit2': controller.createCst(CstType.STRUCTURED, true); return true;
|
||||||
|
@ -150,7 +150,7 @@ function EditorRSList() {
|
||||||
selected={rowSelection}
|
selected={rowSelection}
|
||||||
setSelected={handleRowSelection}
|
setSelected={handleRowSelection}
|
||||||
onEdit={controller.navigateCst}
|
onEdit={controller.navigateCst}
|
||||||
onCreateNew={() => controller.createCst(undefined, false)}
|
onCreateNew={controller.createCstDefault}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -11,7 +11,7 @@ import useWindowSize from '@/hooks/useWindowSize';
|
||||||
import { PARAMETER, prefixes } from '@/utils/constants';
|
import { PARAMETER, prefixes } from '@/utils/constants';
|
||||||
import { truncateToSymbol } from '@/utils/utils';
|
import { truncateToSymbol } from '@/utils/utils';
|
||||||
|
|
||||||
import BadgeConstituenta from '../../../components/BadgeConstituenta';
|
import { BadgeConstituenta } from '../../../components/BadgeConstituenta';
|
||||||
import { labelCstTypification } from '../../../labels';
|
import { labelCstTypification } from '../../../labels';
|
||||||
import { IConstituenta } from '../../../models/rsform';
|
import { IConstituenta } from '../../../models/rsform';
|
||||||
|
|
||||||
|
|
|
@ -96,7 +96,7 @@ export function ToolbarRSList() {
|
||||||
titleHtml={prepareTooltip('Добавить новую конституенту...', 'Alt + `')}
|
titleHtml={prepareTooltip('Добавить новую конституенту...', 'Alt + `')}
|
||||||
icon={<IconNewItem size='1.25rem' className='icon-green' />}
|
icon={<IconNewItem size='1.25rem' className='icon-green' />}
|
||||||
disabled={isProcessing}
|
disabled={isProcessing}
|
||||||
onClick={() => controller.createCst(undefined, false)}
|
onClick={controller.createCstDefault}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
titleHtml={prepareTooltip('Клонировать конституенту', 'Alt + V')}
|
titleHtml={prepareTooltip('Клонировать конституенту', 'Alt + V')}
|
||||||
|
|
|
@ -68,12 +68,12 @@ function TGFlow() {
|
||||||
const [nodes, setNodes, onNodesChange] = useNodesState([]);
|
const [nodes, setNodes, onNodesChange] = useNodesState([]);
|
||||||
const [edges, setEdges] = useEdgesState([]);
|
const [edges, setEdges] = useEdgesState([]);
|
||||||
|
|
||||||
const [focusCst, setFocusCst] = useState<IConstituenta | undefined>(undefined);
|
const [focusCst, setFocusCst] = useState<IConstituenta | null>(null);
|
||||||
const filteredGraph = produceFilteredGraph(controller.schema, filter, focusCst);
|
const filteredGraph = produceFilteredGraph(controller.schema, filter, focusCst);
|
||||||
const [hidden, setHidden] = useState<number[]>([]);
|
const [hidden, setHidden] = useState<number[]>([]);
|
||||||
|
|
||||||
const [isDragging, setIsDragging] = useState(false);
|
const [isDragging, setIsDragging] = useState(false);
|
||||||
const [hoverID, setHoverID] = useState<number | undefined>(undefined);
|
const [hoverID, setHoverID] = useState<number | null>(null);
|
||||||
const hoverCst = hoverID && controller.schema.cstByID.get(hoverID);
|
const hoverCst = hoverID && controller.schema.cstByID.get(hoverID);
|
||||||
const [hoverCstDebounced] = useDebounce(hoverCst, PARAMETER.graphPopupDelay);
|
const [hoverCstDebounced] = useDebounce(hoverCst, PARAMETER.graphPopupDelay);
|
||||||
const [hoverLeft, setHoverLeft] = useState(true);
|
const [hoverLeft, setHoverLeft] = useState(true);
|
||||||
|
@ -102,7 +102,7 @@ function TGFlow() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
setHidden(newDismissed);
|
setHidden(newDismissed);
|
||||||
setHoverID(undefined);
|
setHoverID(null);
|
||||||
}, [controller.schema, filteredGraph]);
|
}, [controller.schema, filteredGraph]);
|
||||||
|
|
||||||
const resetNodes = useCallback(() => {
|
const resetNodes = useCallback(() => {
|
||||||
|
@ -227,7 +227,7 @@ function TGFlow() {
|
||||||
if (event.key === 'Escape') {
|
if (event.key === 'Escape') {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
setFocusCst(undefined);
|
setFocusCst(null);
|
||||||
handleSetSelected([]);
|
handleSetSelected([]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -252,13 +252,17 @@ function TGFlow() {
|
||||||
}, PARAMETER.graphRefreshDelay);
|
}, PARAMETER.graphRefreshDelay);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSetFocus(cstID: number | undefined) {
|
function handleSetFocus(cstID: number | null) {
|
||||||
const target = cstID !== undefined ? controller.schema.cstByID.get(cstID) : cstID;
|
if (cstID === null) {
|
||||||
setFocusCst(prev => (prev === target ? undefined : target));
|
setFocusCst(null);
|
||||||
|
} else {
|
||||||
|
const target = controller.schema.cstByID.get(cstID) ?? null;
|
||||||
|
setFocusCst(prev => (prev === target ? null : target));
|
||||||
if (target) {
|
if (target) {
|
||||||
controller.setSelected([]);
|
controller.setSelected([]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function handleNodeClick(event: CProps.EventMouse, cstID: number) {
|
function handleNodeClick(event: CProps.EventMouse, cstID: number) {
|
||||||
if (event.altKey) {
|
if (event.altKey) {
|
||||||
|
@ -304,7 +308,10 @@ function TGFlow() {
|
||||||
{!focusCst ? (
|
{!focusCst ? (
|
||||||
<ToolbarGraphSelection
|
<ToolbarGraphSelection
|
||||||
graph={controller.schema.graph}
|
graph={controller.schema.graph}
|
||||||
isCore={cstID => isBasicConcept(controller.schema.cstByID.get(cstID)?.cst_type)}
|
isCore={cstID => {
|
||||||
|
const cst = controller.schema.cstByID.get(cstID);
|
||||||
|
return !!cst && isBasicConcept(cst.cst_type);
|
||||||
|
}}
|
||||||
isOwned={
|
isOwned={
|
||||||
controller.schema.inheritance.length > 0
|
controller.schema.inheritance.length > 0
|
||||||
? cstID => !controller.schema.cstByID.get(cstID)?.is_inherited
|
? cstID => !controller.schema.cstByID.get(cstID)?.is_inherited
|
||||||
|
@ -318,7 +325,7 @@ function TGFlow() {
|
||||||
{focusCst ? (
|
{focusCst ? (
|
||||||
<ToolbarFocusedCst
|
<ToolbarFocusedCst
|
||||||
center={focusCst}
|
center={focusCst}
|
||||||
reset={() => handleSetFocus(undefined)}
|
reset={() => handleSetFocus(null)}
|
||||||
showInputs={filter.focusShowInputs}
|
showInputs={filter.focusShowInputs}
|
||||||
showOutputs={filter.focusShowOutputs}
|
showOutputs={filter.focusShowOutputs}
|
||||||
toggleShowInputs={() =>
|
toggleShowInputs={() =>
|
||||||
|
@ -395,7 +402,7 @@ function TGFlow() {
|
||||||
onNodeDragStart={() => setIsDragging(true)}
|
onNodeDragStart={() => setIsDragging(true)}
|
||||||
onNodeDragStop={() => setIsDragging(false)}
|
onNodeDragStop={() => setIsDragging(false)}
|
||||||
onNodeMouseEnter={(event, node) => handleNodeEnter(event, Number(node.id))}
|
onNodeMouseEnter={(event, node) => handleNodeEnter(event, Number(node.id))}
|
||||||
onNodeMouseLeave={() => setHoverID(undefined)}
|
onNodeMouseLeave={() => setHoverID(null)}
|
||||||
onNodeClick={(event, node) => handleNodeClick(event, Number(node.id))}
|
onNodeClick={(event, node) => handleNodeClick(event, Number(node.id))}
|
||||||
onNodeDoubleClick={(event, node) => handleNodeDoubleClick(event, Number(node.id))}
|
onNodeDoubleClick={(event, node) => handleNodeDoubleClick(event, Number(node.id))}
|
||||||
/>
|
/>
|
||||||
|
@ -408,7 +415,7 @@ function TGFlow() {
|
||||||
export default TGFlow;
|
export default TGFlow;
|
||||||
|
|
||||||
// ====== Internals =========
|
// ====== Internals =========
|
||||||
function produceFilteredGraph(schema: IRSForm, params: GraphFilterParams, focusCst: IConstituenta | undefined) {
|
function produceFilteredGraph(schema: IRSForm, params: GraphFilterParams, focusCst: IConstituenta | null) {
|
||||||
const filtered = schema.graph.clone();
|
const filtered = schema.graph.clone();
|
||||||
const allowedTypes: CstType[] = (() => {
|
const allowedTypes: CstType[] = (() => {
|
||||||
const result: CstType[] = [];
|
const result: CstType[] = [];
|
||||||
|
|
|
@ -11,8 +11,7 @@ export function applyLayout(nodes: Node<TGNodeData>[], edges: Edge[], subLabels?
|
||||||
rankdir: 'TB',
|
rankdir: 'TB',
|
||||||
ranksep: subLabels ? 60 : 40,
|
ranksep: subLabels ? 60 : 40,
|
||||||
nodesep: subLabels ? 100 : 20,
|
nodesep: subLabels ? 100 : 20,
|
||||||
ranker: 'network-simplex',
|
ranker: 'network-simplex'
|
||||||
align: undefined
|
|
||||||
});
|
});
|
||||||
nodes.forEach(node => {
|
nodes.forEach(node => {
|
||||||
dagreGraph.setNode(node.id, { width: 2 * PARAMETER.graphNodeRadius, height: 2 * PARAMETER.graphNodeRadius });
|
dagreGraph.setNode(node.id, { width: 2 * PARAMETER.graphNodeRadius, height: 2 * PARAMETER.graphNodeRadius });
|
||||||
|
|
|
@ -31,7 +31,7 @@ export enum RSTabID {
|
||||||
export interface IRSEditContext extends ILibraryItemEditor {
|
export interface IRSEditContext extends ILibraryItemEditor {
|
||||||
schema: IRSForm;
|
schema: IRSForm;
|
||||||
selected: number[];
|
selected: number[];
|
||||||
activeCst?: IConstituenta;
|
activeCst: IConstituenta | null;
|
||||||
activeVersion?: number;
|
activeVersion?: number;
|
||||||
|
|
||||||
isOwned: boolean;
|
isOwned: boolean;
|
||||||
|
@ -41,7 +41,7 @@ export interface IRSEditContext extends ILibraryItemEditor {
|
||||||
isAttachedToOSS: boolean;
|
isAttachedToOSS: boolean;
|
||||||
canDeleteSelected: boolean;
|
canDeleteSelected: boolean;
|
||||||
|
|
||||||
navigateVersion: (versionID: number | undefined) => void;
|
navigateVersion: (versionID?: number) => void;
|
||||||
navigateRSForm: ({ tab, activeID }: { tab: RSTabID; activeID?: number }) => void;
|
navigateRSForm: ({ tab, activeID }: { tab: RSTabID; activeID?: number }) => void;
|
||||||
navigateCst: (cstID: number) => void;
|
navigateCst: (cstID: number) => void;
|
||||||
navigateOss: (ossID: number, newTab?: boolean) => void;
|
navigateOss: (ossID: number, newTab?: boolean) => void;
|
||||||
|
@ -56,7 +56,8 @@ export interface IRSEditContext extends ILibraryItemEditor {
|
||||||
|
|
||||||
moveUp: () => void;
|
moveUp: () => void;
|
||||||
moveDown: () => void;
|
moveDown: () => void;
|
||||||
createCst: (type: CstType | undefined, skipDialog: boolean, definition?: string) => void;
|
createCst: (type: CstType, skipDialog: boolean, definition?: string) => void;
|
||||||
|
createCstDefault: () => void;
|
||||||
cloneCst: () => void;
|
cloneCst: () => void;
|
||||||
promptDeleteCst: () => void;
|
promptDeleteCst: () => void;
|
||||||
promptTemplate: () => void;
|
promptTemplate: () => void;
|
||||||
|
@ -103,7 +104,7 @@ export const RSEditState = ({
|
||||||
const [selected, setSelected] = useState<number[]>([]);
|
const [selected, setSelected] = useState<number[]>([]);
|
||||||
const canDeleteSelected = selected.length > 0 && selected.every(id => !schema.cstByID.get(id)?.is_inherited);
|
const canDeleteSelected = selected.length > 0 && selected.every(id => !schema.cstByID.get(id)?.is_inherited);
|
||||||
|
|
||||||
const activeCst = selected.length === 0 ? undefined : schema.cstByID.get(selected[selected.length - 1]);
|
const activeCst = selected.length === 0 ? null : schema.cstByID.get(selected[selected.length - 1])!;
|
||||||
|
|
||||||
const { cstCreate } = useCstCreate();
|
const { cstCreate } = useCstCreate();
|
||||||
const { cstMove } = useCstMove();
|
const { cstMove } = useCstMove();
|
||||||
|
@ -124,7 +125,7 @@ export const RSEditState = ({
|
||||||
[schema, adjustRole, isOwned, user, adminMode]
|
[schema, adjustRole, isOwned, user, adminMode]
|
||||||
);
|
);
|
||||||
|
|
||||||
function navigateVersion(versionID: number | undefined) {
|
function navigateVersion(versionID?: number) {
|
||||||
router.push(urls.schema(schema.id, versionID));
|
router.push(urls.schema(schema.id, versionID));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,7 +165,7 @@ export const RSEditState = ({
|
||||||
if (!window.confirm(promptText.deleteLibraryItem)) {
|
if (!window.confirm(promptText.deleteLibraryItem)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const ossID = schema.oss.length > 0 ? schema.oss[0].id : undefined;
|
const ossID = schema.oss.length > 0 ? schema.oss[0].id : null;
|
||||||
void deleteItem(schema.id).then(() => {
|
void deleteItem(schema.id).then(() => {
|
||||||
if (ossID) {
|
if (ossID) {
|
||||||
router.push(urls.oss(ossID));
|
router.push(urls.oss(ossID));
|
||||||
|
@ -242,7 +243,7 @@ export const RSEditState = ({
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function createCst(type: CstType | undefined, skipDialog: boolean, definition?: string) {
|
function createCst(type: CstType | null, skipDialog: boolean, definition?: string) {
|
||||||
const targetType = type ?? activeCst?.cst_type ?? CstType.BASE;
|
const targetType = type ?? activeCst?.cst_type ?? CstType.BASE;
|
||||||
const data: ICstCreateDTO = {
|
const data: ICstCreateDTO = {
|
||||||
insert_after: activeCst?.id ?? null,
|
insert_after: activeCst?.id ?? null,
|
||||||
|
@ -286,7 +287,7 @@ export const RSEditState = ({
|
||||||
selected: selected,
|
selected: selected,
|
||||||
afterDelete: (schema, deleted) => {
|
afterDelete: (schema, deleted) => {
|
||||||
const isEmpty = deleted.length === schema.items.length;
|
const isEmpty = deleted.length === schema.items.length;
|
||||||
const nextActive = isEmpty ? undefined : getNextActiveOnDelete(activeCst?.id, schema.items, deleted);
|
const nextActive = isEmpty ? null : getNextActiveOnDelete(activeCst?.id ?? null, schema.items, deleted);
|
||||||
setSelected(nextActive ? [nextActive] : []);
|
setSelected(nextActive ? [nextActive] : []);
|
||||||
if (!nextActive) {
|
if (!nextActive) {
|
||||||
navigateRSForm({ tab: RSTabID.CST_LIST });
|
navigateRSForm({ tab: RSTabID.CST_LIST });
|
||||||
|
@ -338,6 +339,7 @@ export const RSEditState = ({
|
||||||
moveUp,
|
moveUp,
|
||||||
moveDown,
|
moveDown,
|
||||||
createCst,
|
createCst,
|
||||||
|
createCstDefault: () => createCst(null, false),
|
||||||
cloneCst,
|
cloneCst,
|
||||||
promptDeleteCst,
|
promptDeleteCst,
|
||||||
|
|
||||||
|
@ -350,18 +352,14 @@ export const RSEditState = ({
|
||||||
};
|
};
|
||||||
|
|
||||||
// ====== Internals =========
|
// ====== Internals =========
|
||||||
function getNextActiveOnDelete(
|
function getNextActiveOnDelete(activeID: number | null, items: IConstituenta[], deleted: number[]): number | null {
|
||||||
activeID: number | undefined,
|
|
||||||
items: IConstituenta[],
|
|
||||||
deleted: number[]
|
|
||||||
): number | undefined {
|
|
||||||
if (items.length === deleted.length) {
|
if (items.length === deleted.length) {
|
||||||
return undefined;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
let activeIndex = items.findIndex(cst => cst.id === activeID);
|
let activeIndex = items.findIndex(cst => cst.id === activeID);
|
||||||
if (activeIndex === -1) {
|
if (activeIndex === -1) {
|
||||||
return undefined;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (activeIndex < items.length && deleted.find(id => id === items[activeIndex].id)) {
|
while (activeIndex < items.length && deleted.find(id => id === items[activeIndex].id)) {
|
||||||
|
|
|
@ -71,7 +71,7 @@ function applyGraphQuery(target: IRSForm, pivot: number, mode: DependencyMode):
|
||||||
if (mode === DependencyMode.ALL) {
|
if (mode === DependencyMode.ALL) {
|
||||||
return target.items;
|
return target.items;
|
||||||
}
|
}
|
||||||
const ids: number[] | undefined = (() => {
|
const ids = (() => {
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case DependencyMode.OUTPUTS: {
|
case DependencyMode.OUTPUTS: {
|
||||||
return target.graph.nodes.get(pivot)?.outputs;
|
return target.graph.nodes.get(pivot)?.outputs;
|
||||||
|
@ -86,7 +86,6 @@ function applyGraphQuery(target: IRSForm, pivot: number, mode: DependencyMode):
|
||||||
return target.graph.expandAllInputs([pivot]);
|
return target.graph.expandAllInputs([pivot]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return undefined;
|
|
||||||
})();
|
})();
|
||||||
if (ids) {
|
if (ids) {
|
||||||
return target.items.filter(cst => ids.find(id => id === cst.id));
|
return target.items.filter(cst => ids.find(id => id === cst.id));
|
||||||
|
|
|
@ -34,7 +34,7 @@ function SelectGraphFilter({ value, dense, onChange, ...restProps }: SelectGraph
|
||||||
hideTitle={menu.isOpen}
|
hideTitle={menu.isOpen}
|
||||||
className='h-full pr-2'
|
className='h-full pr-2'
|
||||||
icon={<DependencyIcon value={value} size='1rem' />}
|
icon={<DependencyIcon value={value} size='1rem' />}
|
||||||
text={dense || size.isSmall ? undefined : labelCstSource(value)}
|
text={!dense && !size.isSmall ? labelCstSource(value) : undefined}
|
||||||
onClick={menu.toggle}
|
onClick={menu.toggle}
|
||||||
/>
|
/>
|
||||||
<Dropdown stretchLeft isOpen={menu.isOpen}>
|
<Dropdown stretchLeft isOpen={menu.isOpen}>
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { NoData, TextContent } from '@/components/View';
|
||||||
import { APP_COLORS } from '@/styling/colors';
|
import { APP_COLORS } from '@/styling/colors';
|
||||||
import { PARAMETER, prefixes } from '@/utils/constants';
|
import { PARAMETER, prefixes } from '@/utils/constants';
|
||||||
|
|
||||||
import BadgeConstituenta from '../../../components/BadgeConstituenta';
|
import { BadgeConstituenta } from '../../../components/BadgeConstituenta';
|
||||||
import { describeConstituenta } from '../../../labels';
|
import { describeConstituenta } from '../../../labels';
|
||||||
import { IConstituenta } from '../../../models/rsform';
|
import { IConstituenta } from '../../../models/rsform';
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ const DESCRIPTION_MAX_SYMBOLS = 280;
|
||||||
|
|
||||||
interface TableSideConstituentsProps {
|
interface TableSideConstituentsProps {
|
||||||
items: IConstituenta[];
|
items: IConstituenta[];
|
||||||
activeCst?: IConstituenta;
|
activeCst: IConstituenta | null;
|
||||||
onOpenEdit: (cstID: number) => void;
|
onOpenEdit: (cstID: number) => void;
|
||||||
autoScroll?: boolean;
|
autoScroll?: boolean;
|
||||||
maxHeight: string;
|
maxHeight: string;
|
||||||
|
@ -54,7 +54,6 @@ function TableSideConstituents({
|
||||||
header: () => <span className='pl-3'>Имя</span>,
|
header: () => <span className='pl-3'>Имя</span>,
|
||||||
size: 65,
|
size: 65,
|
||||||
minSize: 65,
|
minSize: 65,
|
||||||
footer: undefined,
|
|
||||||
cell: props => (
|
cell: props => (
|
||||||
<BadgeConstituenta className='mr-[-0.5rem]' value={props.row.original} prefixID={prefixes.cst_side_table} />
|
<BadgeConstituenta className='mr-[-0.5rem]' value={props.row.original} prefixID={prefixes.cst_side_table} />
|
||||||
)
|
)
|
||||||
|
@ -81,19 +80,19 @@ function TableSideConstituents({
|
||||||
|
|
||||||
const conditionalRowStyles: IConditionalStyle<IConstituenta>[] = [
|
const conditionalRowStyles: IConditionalStyle<IConstituenta>[] = [
|
||||||
{
|
{
|
||||||
when: (cst: IConstituenta) => !!activeCst && cst.id === activeCst?.id,
|
when: (cst: IConstituenta) => !!activeCst && cst.id === activeCst.id,
|
||||||
style: {
|
style: {
|
||||||
backgroundColor: APP_COLORS.bgSelected
|
backgroundColor: APP_COLORS.bgSelected
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
when: (cst: IConstituenta) => !!activeCst && cst.spawner === activeCst?.id && cst.id !== activeCst?.id,
|
when: (cst: IConstituenta) => !!activeCst && cst.spawner === activeCst.id && cst.id !== activeCst.id,
|
||||||
style: {
|
style: {
|
||||||
backgroundColor: APP_COLORS.bgOrange50
|
backgroundColor: APP_COLORS.bgOrange50
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
when: (cst: IConstituenta) => activeCst?.id !== undefined && cst.spawn.includes(activeCst.id),
|
when: (cst: IConstituenta) => !!activeCst && cst.spawn.includes(activeCst.id),
|
||||||
style: {
|
style: {
|
||||||
backgroundColor: APP_COLORS.bgGreen50
|
backgroundColor: APP_COLORS.bgGreen50
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,7 @@ export function ViewConstituents({ isBottom, isMounted }: ViewConstituentsProps)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ConstituentsSearch
|
<ConstituentsSearch
|
||||||
dense={windowSize.width && windowSize.width < COLUMN_DENSE_SEARCH_THRESHOLD ? true : undefined}
|
dense={!!windowSize.width && windowSize.width < COLUMN_DENSE_SEARCH_THRESHOLD}
|
||||||
schema={schema}
|
schema={schema}
|
||||||
activeID={activeCst?.id}
|
activeID={activeCst?.id}
|
||||||
onChange={setFilteredData}
|
onChange={setFilteredData}
|
||||||
|
|
|
@ -10,7 +10,7 @@ import { useUsers } from '../backend/useUsers';
|
||||||
import { matchUser } from '../models/userAPI';
|
import { matchUser } from '../models/userAPI';
|
||||||
|
|
||||||
interface SelectUserProps extends CProps.Styling {
|
interface SelectUserProps extends CProps.Styling {
|
||||||
value?: number;
|
value: number | null;
|
||||||
onChange: (newValue: number) => void;
|
onChange: (newValue: number) => void;
|
||||||
filter?: (userID: number) => boolean;
|
filter?: (userID: number) => boolean;
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,7 @@ export interface GenericDialogProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DialogsStore {
|
interface DialogsStore {
|
||||||
active: DialogType | undefined;
|
active: DialogType | null;
|
||||||
props: unknown;
|
props: unknown;
|
||||||
hideDialog: () => void;
|
hideDialog: () => void;
|
||||||
|
|
||||||
|
@ -87,12 +87,12 @@ interface DialogsStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useDialogsStore = create<DialogsStore>()(set => ({
|
export const useDialogsStore = create<DialogsStore>()(set => ({
|
||||||
active: undefined,
|
active: null,
|
||||||
props: undefined,
|
props: null,
|
||||||
hideDialog: () => {
|
hideDialog: () => {
|
||||||
set(state => {
|
set(state => {
|
||||||
(state.props as GenericDialogProps | undefined)?.onHide?.();
|
(state.props as GenericDialogProps | null)?.onHide?.();
|
||||||
return { active: undefined, props: undefined };
|
return { active: null, props: null };
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -113,7 +113,7 @@ export const useDialogsStore = create<DialogsStore>()(set => ({
|
||||||
showCloneLibraryItem: props => set({ active: DialogType.CLONE_LIBRARY_ITEM, props: props }),
|
showCloneLibraryItem: props => set({ active: DialogType.CLONE_LIBRARY_ITEM, props: props }),
|
||||||
showCreateVersion: props => set({ active: DialogType.CREATE_VERSION, props: props }),
|
showCreateVersion: props => set({ active: DialogType.CREATE_VERSION, props: props }),
|
||||||
showDeleteOperation: props => set({ active: DialogType.DELETE_OPERATION, props: props }),
|
showDeleteOperation: props => set({ active: DialogType.DELETE_OPERATION, props: props }),
|
||||||
showGraphParams: () => set({ active: DialogType.GRAPH_PARAMETERS, props: undefined }),
|
showGraphParams: () => set({ active: DialogType.GRAPH_PARAMETERS, props: null }),
|
||||||
showRelocateConstituents: props => set({ active: DialogType.RELOCATE_CONSTITUENTS, props: props }),
|
showRelocateConstituents: props => set({ active: DialogType.RELOCATE_CONSTITUENTS, props: props }),
|
||||||
showRenameCst: props => set({ active: DialogType.RENAME_CONSTITUENTA, props: props }),
|
showRenameCst: props => set({ active: DialogType.RENAME_CONSTITUENTA, props: props }),
|
||||||
showQR: props => set({ active: DialogType.SHOW_QR_CODE, props: props }),
|
showQR: props => set({ active: DialogType.SHOW_QR_CODE, props: props }),
|
||||||
|
|
|
@ -3,11 +3,11 @@ import { create } from 'zustand';
|
||||||
import { IConstituenta } from '@/features/rsform/models/rsform';
|
import { IConstituenta } from '@/features/rsform/models/rsform';
|
||||||
|
|
||||||
interface TooltipsStore {
|
interface TooltipsStore {
|
||||||
activeCst: IConstituenta | undefined;
|
activeCst: IConstituenta | null;
|
||||||
setActiveCst: (value: IConstituenta | undefined) => void;
|
setActiveCst: (value: IConstituenta | null) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useTooltipsStore = create<TooltipsStore>()(set => ({
|
export const useTooltipsStore = create<TooltipsStore>()(set => ({
|
||||||
activeCst: undefined,
|
activeCst: null,
|
||||||
setActiveCst: value => set({ activeCst: value })
|
setActiveCst: value => set({ activeCst: value })
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -110,20 +110,20 @@ export function promptUnsaved(): boolean {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggle tristate flag: undefined - true - false.
|
* Toggle tristate flag: null - true - false.
|
||||||
*/
|
*/
|
||||||
export function toggleTristateFlag(prev: boolean | undefined): boolean | undefined {
|
export function toggleTristateFlag(prev: boolean | null): boolean | null {
|
||||||
if (prev === undefined) {
|
if (prev === null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return prev ? false : undefined;
|
return prev ? false : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggle tristate color: gray - green - red .
|
* Toggle tristate color: gray - green - red .
|
||||||
*/
|
*/
|
||||||
export function tripleToggleColor(value: boolean | undefined): string {
|
export function tripleToggleColor(value: boolean | null): string {
|
||||||
if (value === undefined) {
|
if (value === null) {
|
||||||
return 'clr-text-controls';
|
return 'clr-text-controls';
|
||||||
}
|
}
|
||||||
return value ? 'text-ok-600' : 'text-warn-600';
|
return value ? 'text-ok-600' : 'text-warn-600';
|
||||||
|
|
Loading…
Reference in New Issue
Block a user