mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-08-14 04:40:36 +03:00
F: Implement association editing UI and fix dialogs caching
This commit is contained in:
parent
41e0ba64ba
commit
03ade4fee1
|
@ -1,5 +1,7 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
|
import assert from 'assert';
|
||||||
|
|
||||||
import { useRef, useState } from 'react';
|
import { useRef, useState } from 'react';
|
||||||
import { ChevronDownIcon } from 'lucide-react';
|
import { ChevronDownIcon } from 'lucide-react';
|
||||||
|
|
||||||
|
@ -9,20 +11,32 @@ import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, Command
|
||||||
import { Popover, PopoverContent, PopoverTrigger } from '../ui/popover';
|
import { Popover, PopoverContent, PopoverTrigger } from '../ui/popover';
|
||||||
import { cn } from '../utils';
|
import { cn } from '../utils';
|
||||||
|
|
||||||
interface ComboMultiProps<Option> extends Styling {
|
interface ComboMultiPropsBase<Option> extends Styling {
|
||||||
id?: string;
|
id?: string;
|
||||||
items?: Option[];
|
items?: Option[];
|
||||||
value: Option[];
|
value: Option[];
|
||||||
onChange: (newValue: Option[]) => void;
|
|
||||||
|
|
||||||
idFunc: (item: Option) => string;
|
idFunc: (item: Option) => string;
|
||||||
labelValueFunc: (item: Option) => string;
|
labelValueFunc: (item: Option) => string;
|
||||||
labelOptionFunc: (item: Option) => string;
|
labelOptionFunc: (item: Option) => string;
|
||||||
|
|
||||||
|
disabled?: boolean;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
noSearch?: boolean;
|
noSearch?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ComboMultiPropsFull<Option> extends ComboMultiPropsBase<Option> {
|
||||||
|
onChange: (newValue: Option[]) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ComboMultiPropsSplit<Option> extends ComboMultiPropsBase<Option> {
|
||||||
|
onClear: () => void;
|
||||||
|
onAdd: (item: Option) => void;
|
||||||
|
onRemove: (item: Option) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
type ComboMultiProps<Option> = ComboMultiPropsFull<Option> | ComboMultiPropsSplit<Option>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Displays a combo-box component with multiple selection.
|
* Displays a combo-box component with multiple selection.
|
||||||
*/
|
*/
|
||||||
|
@ -30,14 +44,15 @@ export function ComboMulti<Option>({
|
||||||
id,
|
id,
|
||||||
items,
|
items,
|
||||||
value,
|
value,
|
||||||
onChange,
|
|
||||||
labelValueFunc,
|
labelValueFunc,
|
||||||
labelOptionFunc,
|
labelOptionFunc,
|
||||||
idFunc,
|
idFunc,
|
||||||
placeholder,
|
placeholder,
|
||||||
className,
|
className,
|
||||||
style,
|
style,
|
||||||
noSearch
|
disabled,
|
||||||
|
noSearch,
|
||||||
|
...restProps
|
||||||
}: ComboMultiProps<Option>) {
|
}: ComboMultiProps<Option>) {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [popoverWidth, setPopoverWidth] = useState<number | undefined>(undefined);
|
const [popoverWidth, setPopoverWidth] = useState<number | undefined>(undefined);
|
||||||
|
@ -54,19 +69,34 @@ export function ComboMulti<Option>({
|
||||||
if (value.includes(newValue)) {
|
if (value.includes(newValue)) {
|
||||||
handleRemoveValue(newValue);
|
handleRemoveValue(newValue);
|
||||||
} else {
|
} else {
|
||||||
onChange([...value, newValue]);
|
if ('onAdd' in restProps && typeof restProps.onAdd === 'function') {
|
||||||
|
restProps.onAdd(newValue);
|
||||||
|
} else {
|
||||||
|
assert('onChange' in restProps);
|
||||||
|
restProps.onChange([...value, newValue]);
|
||||||
|
}
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleRemoveValue(delValue: Option) {
|
function handleRemoveValue(delValue: Option) {
|
||||||
onChange(value.filter(v => v !== delValue));
|
if ('onRemove' in restProps && typeof restProps.onRemove === 'function') {
|
||||||
|
restProps.onRemove(delValue);
|
||||||
|
} else {
|
||||||
|
assert('onChange' in restProps);
|
||||||
|
restProps.onChange(value.filter(v => v !== delValue));
|
||||||
|
}
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleClear(event: React.MouseEvent<SVGElement>) {
|
function handleClear(event: React.MouseEvent<SVGElement>) {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
onChange([]);
|
if ('onClear' in restProps && typeof restProps.onClear === 'function') {
|
||||||
|
restProps.onClear();
|
||||||
|
} else {
|
||||||
|
assert('onChange' in restProps);
|
||||||
|
restProps.onChange([]);
|
||||||
|
}
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +111,7 @@ export function ComboMulti<Option>({
|
||||||
className={cn(
|
className={cn(
|
||||||
'relative h-9',
|
'relative h-9',
|
||||||
'flex gap-2 px-3 py-2 items-center justify-between',
|
'flex gap-2 px-3 py-2 items-center justify-between',
|
||||||
'bg-input disabled:opacity-50',
|
'bg-input disabled:bg-transparent',
|
||||||
'cursor-pointer disabled:cursor-auto',
|
'cursor-pointer disabled:cursor-auto',
|
||||||
'whitespace-nowrap',
|
'whitespace-nowrap',
|
||||||
'focus-outline border',
|
'focus-outline border',
|
||||||
|
@ -91,32 +121,39 @@ export function ComboMulti<Option>({
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
style={style}
|
style={style}
|
||||||
|
disabled={disabled}
|
||||||
>
|
>
|
||||||
<div className='flex flex-wrap gap-1 items-center'>
|
<div className='flex flex-wrap gap-2 items-center'>
|
||||||
{value.length === 0 ? <div className='text-muted-foreground'>{placeholder}</div> : null}
|
{value.length === 0 ? <div className='text-muted-foreground'>{placeholder}</div> : null}
|
||||||
{value.map(item => (
|
{value.map(item => (
|
||||||
<div key={idFunc(item)} className='flex px-1 items-center border rounded-lg bg-accent text-sm'>
|
<div key={idFunc(item)} className='flex px-1 items-center border rounded-lg bg-accent text-sm'>
|
||||||
{labelValueFunc(item)}
|
{labelValueFunc(item)}
|
||||||
<IconRemove
|
{!disabled ? (
|
||||||
tabIndex={-1}
|
<IconRemove
|
||||||
size='1rem'
|
tabIndex={-1}
|
||||||
className='cc-remove cc-hover-pulse'
|
size='1rem'
|
||||||
onClick={event => {
|
className='cc-remove cc-hover-pulse'
|
||||||
event.stopPropagation();
|
onClick={
|
||||||
handleRemoveValue(item);
|
disabled
|
||||||
}}
|
? undefined
|
||||||
/>
|
: event => {
|
||||||
|
event.stopPropagation();
|
||||||
|
handleRemoveValue(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ChevronDownIcon className={cn('text-muted-foreground', !!value && 'opacity-0')} />
|
<ChevronDownIcon className={cn('text-muted-foreground', !!value && 'opacity-0')} />
|
||||||
{!!value ? (
|
{!!value && !disabled ? (
|
||||||
<IconRemove
|
<IconRemove
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
size='1rem'
|
size='1rem'
|
||||||
className='cc-remove absolute pointer-events-auto right-3 cc-hover-pulse hover:text-primary'
|
className='cc-remove absolute pointer-events-auto right-3 cc-hover-pulse hover:text-primary'
|
||||||
onClick={handleClear}
|
onClick={value.length === 0 ? undefined : handleClear}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</button>
|
</button>
|
||||||
|
@ -127,16 +164,19 @@ export function ComboMulti<Option>({
|
||||||
<CommandList>
|
<CommandList>
|
||||||
<CommandEmpty>Список пуст</CommandEmpty>
|
<CommandEmpty>Список пуст</CommandEmpty>
|
||||||
<CommandGroup>
|
<CommandGroup>
|
||||||
{items?.map(item => (
|
{items
|
||||||
<CommandItem
|
?.filter(item => !value.includes(item))
|
||||||
key={idFunc(item)}
|
.map(item => (
|
||||||
value={labelOptionFunc(item)}
|
<CommandItem
|
||||||
onSelect={() => handleAddValue(item)}
|
key={idFunc(item)}
|
||||||
className={cn(value === item && 'bg-selected text-selected-foreground')}
|
value={labelOptionFunc(item)}
|
||||||
>
|
onSelect={() => handleAddValue(item)}
|
||||||
{labelOptionFunc(item)}
|
disabled={disabled}
|
||||||
</CommandItem>
|
className={cn(value === item && 'bg-selected text-selected-foreground')}
|
||||||
))}
|
>
|
||||||
|
{labelOptionFunc(item)}
|
||||||
|
</CommandItem>
|
||||||
|
))}
|
||||||
</CommandGroup>
|
</CommandGroup>
|
||||||
</CommandList>
|
</CommandList>
|
||||||
</Command>
|
</Command>
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
import { ReactFlowProvider } from 'reactflow';
|
import { ReactFlowProvider } from 'reactflow';
|
||||||
|
|
||||||
import { urls, useConceptNavigation } from '@/app';
|
import { urls, useConceptNavigation } from '@/app';
|
||||||
import { type IRSForm } from '@/features/rsform';
|
import { useRSFormSuspense } from '@/features/rsform/backend/use-rsform';
|
||||||
import { RSTabID } from '@/features/rsform/pages/rsform-page/rsedit-context';
|
import { RSTabID } from '@/features/rsform/pages/rsform-page/rsedit-context';
|
||||||
|
|
||||||
import { MiniButton } from '@/components/control';
|
import { MiniButton } from '@/components/control';
|
||||||
|
@ -14,11 +14,12 @@ import { useDialogsStore } from '@/stores/dialogs';
|
||||||
import { TGReadonlyFlow } from './tg-readonly-flow';
|
import { TGReadonlyFlow } from './tg-readonly-flow';
|
||||||
|
|
||||||
export interface DlgShowTermGraphProps {
|
export interface DlgShowTermGraphProps {
|
||||||
schema: IRSForm;
|
schemaID: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DlgShowTermGraph() {
|
export function DlgShowTermGraph() {
|
||||||
const { schema } = useDialogsStore(state => state.props as DlgShowTermGraphProps);
|
const { schemaID } = useDialogsStore(state => state.props as DlgShowTermGraphProps);
|
||||||
|
const { schema } = useRSFormSuspense({ itemID: schemaID });
|
||||||
const hideDialog = useDialogsStore(state => state.hideDialog);
|
const hideDialog = useDialogsStore(state => state.hideDialog);
|
||||||
const router = useConceptNavigation();
|
const router = useConceptNavigation();
|
||||||
|
|
||||||
|
|
|
@ -98,7 +98,7 @@ export function ToolbarSchema({
|
||||||
convention: '',
|
convention: '',
|
||||||
term_forms: []
|
term_forms: []
|
||||||
};
|
};
|
||||||
showCreateCst({ schema: schema, onCreate: onCreateCst, initial: data });
|
showCreateCst({ schemaID: schema.id, onCreate: onCreateCst, initial: data });
|
||||||
}
|
}
|
||||||
|
|
||||||
function cloneCst() {
|
function cloneCst() {
|
||||||
|
@ -126,7 +126,7 @@ export function ToolbarSchema({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
showDeleteCst({
|
showDeleteCst({
|
||||||
schema: schema,
|
schemaID: schema.id,
|
||||||
selected: [activeCst.id],
|
selected: [activeCst.id],
|
||||||
afterDelete: resetActive
|
afterDelete: resetActive
|
||||||
});
|
});
|
||||||
|
@ -192,7 +192,7 @@ export function ToolbarSchema({
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleShowTermGraph() {
|
function handleShowTermGraph() {
|
||||||
showTermGraph({ schema: schema });
|
showTermGraph({ schemaID: schema.id });
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleReindex() {
|
function handleReindex() {
|
||||||
|
|
|
@ -33,7 +33,7 @@ export function ViewSchema({ schemaID, isMutable }: ViewSchemaProps) {
|
||||||
}, [schema, setCurrentSchema]);
|
}, [schema, setCurrentSchema]);
|
||||||
|
|
||||||
function handleEditCst(cst: IConstituenta) {
|
function handleEditCst(cst: IConstituenta) {
|
||||||
showEditCst({ schema: schema, target: cst });
|
showEditCst({ schemaID: schema.id, targetID: cst.id });
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { DELAYS, KEYS } from '@/backend/configuration';
|
||||||
import { infoMsg } from '@/utils/labels';
|
import { infoMsg } from '@/utils/labels';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
type IAssociationDataDTO,
|
type IAssociation,
|
||||||
type IAssociationTargetDTO,
|
type IAssociationTargetDTO,
|
||||||
type ICheckConstituentaDTO,
|
type ICheckConstituentaDTO,
|
||||||
type IConstituentaCreatedResponse,
|
type IConstituentaCreatedResponse,
|
||||||
|
@ -154,8 +154,8 @@ export const rsformsApi = {
|
||||||
request: { data: data }
|
request: { data: data }
|
||||||
}),
|
}),
|
||||||
|
|
||||||
createAssociation: ({ itemID, data }: { itemID: number; data: IAssociationDataDTO }) =>
|
createAssociation: ({ itemID, data }: { itemID: number; data: IAssociation }) =>
|
||||||
axiosPost<IAssociationDataDTO, IRSFormDTO>({
|
axiosPost<IAssociation, IRSFormDTO>({
|
||||||
schema: schemaRSForm,
|
schema: schemaRSForm,
|
||||||
endpoint: `/api/rsforms/${itemID}/create-association`,
|
endpoint: `/api/rsforms/${itemID}/create-association`,
|
||||||
request: {
|
request: {
|
||||||
|
@ -163,8 +163,8 @@ export const rsformsApi = {
|
||||||
successMessage: infoMsg.changesSaved
|
successMessage: infoMsg.changesSaved
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
deleteAssociation: ({ itemID, data }: { itemID: number; data: IAssociationDataDTO }) =>
|
deleteAssociation: ({ itemID, data }: { itemID: number; data: IAssociation }) =>
|
||||||
axiosPatch<IAssociationDataDTO, IRSFormDTO>({
|
axiosPatch<IAssociation, IRSFormDTO>({
|
||||||
schema: schemaRSForm,
|
schema: schemaRSForm,
|
||||||
endpoint: `/api/rsforms/${itemID}/delete-association`,
|
endpoint: `/api/rsforms/${itemID}/delete-association`,
|
||||||
request: {
|
request: {
|
||||||
|
|
|
@ -21,6 +21,8 @@ import { CstType, type IRSFormDTO, ParsingStatus, ValueClass } from './types';
|
||||||
export class RSFormLoader {
|
export class RSFormLoader {
|
||||||
private schema: IRSForm;
|
private schema: IRSForm;
|
||||||
private graph: Graph = new Graph();
|
private graph: Graph = new Graph();
|
||||||
|
private association_graph: Graph = new Graph();
|
||||||
|
private full_graph: Graph = new Graph();
|
||||||
private cstByAlias = new Map<string, IConstituenta>();
|
private cstByAlias = new Map<string, IConstituenta>();
|
||||||
private cstByID = new Map<number, IConstituenta>();
|
private cstByID = new Map<number, IConstituenta>();
|
||||||
|
|
||||||
|
@ -39,6 +41,8 @@ export class RSFormLoader {
|
||||||
result.graph = this.graph;
|
result.graph = this.graph;
|
||||||
result.cstByAlias = this.cstByAlias;
|
result.cstByAlias = this.cstByAlias;
|
||||||
result.cstByID = this.cstByID;
|
result.cstByID = this.cstByID;
|
||||||
|
result.association_graph = this.association_graph;
|
||||||
|
result.full_graph = this.full_graph;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,6 +51,8 @@ export class RSFormLoader {
|
||||||
this.cstByAlias.set(cst.alias, cst);
|
this.cstByAlias.set(cst.alias, cst);
|
||||||
this.cstByID.set(cst.id, cst);
|
this.cstByID.set(cst.id, cst);
|
||||||
this.graph.addNode(cst.id);
|
this.graph.addNode(cst.id);
|
||||||
|
this.association_graph.addNode(cst.id);
|
||||||
|
this.full_graph.addNode(cst.id);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,6 +63,7 @@ export class RSFormLoader {
|
||||||
const source = this.cstByAlias.get(alias);
|
const source = this.cstByAlias.get(alias);
|
||||||
if (source) {
|
if (source) {
|
||||||
this.graph.addEdge(source.id, cst.id);
|
this.graph.addEdge(source.id, cst.id);
|
||||||
|
this.full_graph.addEdge(source.id, cst.id);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -83,6 +90,7 @@ export class RSFormLoader {
|
||||||
cst.is_template = inferTemplate(cst.definition_formal);
|
cst.is_template = inferTemplate(cst.definition_formal);
|
||||||
cst.cst_class = inferClass(cst.cst_type, cst.is_template);
|
cst.cst_class = inferClass(cst.cst_type, cst.is_template);
|
||||||
cst.spawn = [];
|
cst.spawn = [];
|
||||||
|
cst.associations = [];
|
||||||
cst.spawn_alias = [];
|
cst.spawn_alias = [];
|
||||||
cst.parent_schema = schemaByCst.get(cst.id);
|
cst.parent_schema = schemaByCst.get(cst.id);
|
||||||
cst.parent_schema_index = cst.parent_schema ? parents.indexOf(cst.parent_schema) + 1 : 0;
|
cst.parent_schema_index = cst.parent_schema ? parents.indexOf(cst.parent_schema) + 1 : 0;
|
||||||
|
@ -102,6 +110,12 @@ export class RSFormLoader {
|
||||||
parent.spawn_alias.push(cst.alias);
|
parent.spawn_alias.push(cst.alias);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
this.schema.association.forEach(assoc => {
|
||||||
|
const container = this.cstByID.get(assoc.container)!;
|
||||||
|
container.associations.push(assoc.associate);
|
||||||
|
this.full_graph.addEdge(container.id, assoc.associate);
|
||||||
|
this.association_graph.addEdge(container.id, assoc.associate);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private inferSimpleExpression(target: IConstituenta): boolean {
|
private inferSimpleExpression(target: IConstituenta): boolean {
|
||||||
|
|
|
@ -95,7 +95,7 @@ export interface ICheckConstituentaDTO {
|
||||||
export type ISubstitutionsDTO = z.infer<typeof schemaSubstitutions>;
|
export type ISubstitutionsDTO = z.infer<typeof schemaSubstitutions>;
|
||||||
|
|
||||||
/** Represents data for creating or deleting an association. */
|
/** Represents data for creating or deleting an association. */
|
||||||
export type IAssociationDataDTO = z.infer<typeof schemaAssociationData>;
|
export type IAssociation = z.infer<typeof schemaAssociation>;
|
||||||
|
|
||||||
/** Represents data for clearing all associations for a target constituenta. */
|
/** Represents data for clearing all associations for a target constituenta. */
|
||||||
export type IAssociationTargetDTO = z.infer<typeof schemaAssociationTarget>;
|
export type IAssociationTargetDTO = z.infer<typeof schemaAssociationTarget>;
|
||||||
|
@ -308,6 +308,11 @@ export const schemaConstituenta = schemaConstituentaBasics.extend({
|
||||||
.optional()
|
.optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const schemaAssociation = z.strictObject({
|
||||||
|
container: z.number(),
|
||||||
|
associate: z.number()
|
||||||
|
});
|
||||||
|
|
||||||
export const schemaRSForm = schemaLibraryItem.extend({
|
export const schemaRSForm = schemaLibraryItem.extend({
|
||||||
editors: z.array(z.number()),
|
editors: z.array(z.number()),
|
||||||
|
|
||||||
|
@ -315,7 +320,7 @@ export const schemaRSForm = schemaLibraryItem.extend({
|
||||||
versions: z.array(schemaVersionInfo),
|
versions: z.array(schemaVersionInfo),
|
||||||
|
|
||||||
items: z.array(schemaConstituenta),
|
items: z.array(schemaConstituenta),
|
||||||
association: z.array(z.strictObject({ container: z.number(), associate: z.number() })),
|
association: z.array(schemaAssociation),
|
||||||
inheritance: z.array(
|
inheritance: z.array(
|
||||||
z.strictObject({
|
z.strictObject({
|
||||||
child: z.number(),
|
child: z.number(),
|
||||||
|
@ -392,11 +397,6 @@ export const schemaSubstitutions = z.strictObject({
|
||||||
substitutions: z.array(schemaSubstituteConstituents).min(1, { message: errorMsg.emptySubstitutions })
|
substitutions: z.array(schemaSubstituteConstituents).min(1, { message: errorMsg.emptySubstitutions })
|
||||||
});
|
});
|
||||||
|
|
||||||
export const schemaAssociationData = z.strictObject({
|
|
||||||
container: z.number(),
|
|
||||||
associate: z.number()
|
|
||||||
});
|
|
||||||
|
|
||||||
export const schemaAssociationTarget = z.strictObject({
|
export const schemaAssociationTarget = z.strictObject({
|
||||||
target: z.number()
|
target: z.number()
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { useUpdateTimestamp } from '@/features/library/backend/use-update-timest
|
||||||
import { KEYS } from '@/backend/configuration';
|
import { KEYS } from '@/backend/configuration';
|
||||||
|
|
||||||
import { rsformsApi } from './api';
|
import { rsformsApi } from './api';
|
||||||
import { type IAssociationDataDTO } from './types';
|
import { type IAssociation } from './types';
|
||||||
|
|
||||||
export const useCreateAssociation = () => {
|
export const useCreateAssociation = () => {
|
||||||
const client = useQueryClient();
|
const client = useQueryClient();
|
||||||
|
@ -24,6 +24,6 @@ export const useCreateAssociation = () => {
|
||||||
onError: () => client.invalidateQueries()
|
onError: () => client.invalidateQueries()
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
createAssociation: (data: { itemID: number; data: IAssociationDataDTO }) => mutation.mutateAsync(data)
|
createAssociation: (data: { itemID: number; data: IAssociation }) => mutation.mutateAsync(data)
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { useUpdateTimestamp } from '@/features/library/backend/use-update-timest
|
||||||
import { KEYS } from '@/backend/configuration';
|
import { KEYS } from '@/backend/configuration';
|
||||||
|
|
||||||
import { rsformsApi } from './api';
|
import { rsformsApi } from './api';
|
||||||
import { type IAssociationDataDTO } from './types';
|
import { type IAssociation } from './types';
|
||||||
|
|
||||||
export const useDeleteAssociation = () => {
|
export const useDeleteAssociation = () => {
|
||||||
const client = useQueryClient();
|
const client = useQueryClient();
|
||||||
|
@ -24,6 +24,6 @@ export const useDeleteAssociation = () => {
|
||||||
onError: () => client.invalidateQueries()
|
onError: () => client.invalidateQueries()
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
deleteAssociation: (data: { itemID: number; data: IAssociationDataDTO }) => mutation.mutateAsync(data)
|
deleteAssociation: (data: { itemID: number; data: IAssociation }) => mutation.mutateAsync(data)
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -175,7 +175,7 @@ export const RefsInput = forwardRef<ReactCodeMirrorRef, RefsInputInputProps>(
|
||||||
|
|
||||||
setIsEditing(true);
|
setIsEditing(true);
|
||||||
showEditReference({
|
showEditReference({
|
||||||
schema: schema,
|
schemaID: schema.id,
|
||||||
initial: data,
|
initial: data,
|
||||||
onCancel: () => {
|
onCancel: () => {
|
||||||
setIsEditing(false);
|
setIsEditing(false);
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
import { ComboMulti } from '@/components/input/combo-multi';
|
||||||
|
import { type Styling } from '@/components/props';
|
||||||
|
|
||||||
|
import { labelConstituenta } from '../labels';
|
||||||
|
import { type IConstituenta } from '../models/rsform';
|
||||||
|
|
||||||
|
interface SelectMultiCstProps extends Styling {
|
||||||
|
id?: string;
|
||||||
|
value: IConstituenta[];
|
||||||
|
items: IConstituenta[];
|
||||||
|
onClear: () => void;
|
||||||
|
onAdd: (item: IConstituenta) => void;
|
||||||
|
onRemove: (item: IConstituenta) => void;
|
||||||
|
|
||||||
|
placeholder?: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SelectMultiConstituenta({ value, items, onClear, onAdd, onRemove, ...restProps }: SelectMultiCstProps) {
|
||||||
|
return (
|
||||||
|
<ComboMulti
|
||||||
|
noSearch
|
||||||
|
items={items}
|
||||||
|
value={value}
|
||||||
|
onClear={onClear}
|
||||||
|
onAdd={onAdd}
|
||||||
|
onRemove={onRemove}
|
||||||
|
idFunc={cst => String(cst.id)}
|
||||||
|
labelOptionFunc={cst => labelConstituenta(cst)}
|
||||||
|
labelValueFunc={cst => cst.alias}
|
||||||
|
{...restProps}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
|
@ -14,20 +14,21 @@ import {
|
||||||
schemaCreateConstituenta
|
schemaCreateConstituenta
|
||||||
} from '../../backend/types';
|
} from '../../backend/types';
|
||||||
import { useCreateConstituenta } from '../../backend/use-create-constituenta';
|
import { useCreateConstituenta } from '../../backend/use-create-constituenta';
|
||||||
import { type IRSForm } from '../../models/rsform';
|
import { useRSFormSuspense } from '../../backend/use-rsform';
|
||||||
import { validateNewAlias } from '../../models/rsform-api';
|
import { validateNewAlias } from '../../models/rsform-api';
|
||||||
|
|
||||||
import { FormCreateCst } from './form-create-cst';
|
import { FormCreateCst } from './form-create-cst';
|
||||||
|
|
||||||
export interface DlgCreateCstProps {
|
export interface DlgCreateCstProps {
|
||||||
initial: ICreateConstituentaDTO;
|
initial: ICreateConstituentaDTO;
|
||||||
schema: IRSForm;
|
schemaID: number;
|
||||||
onCreate: (data: RO<IConstituentaBasicsDTO>) => void;
|
onCreate: (data: RO<IConstituentaBasicsDTO>) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DlgCreateCst() {
|
export function DlgCreateCst() {
|
||||||
const { initial, schema, onCreate } = useDialogsStore(state => state.props as DlgCreateCstProps);
|
const { initial, schemaID, onCreate } = useDialogsStore(state => state.props as DlgCreateCstProps);
|
||||||
const { createConstituenta: cstCreate } = useCreateConstituenta();
|
const { createConstituenta: cstCreate } = useCreateConstituenta();
|
||||||
|
const { schema } = useRSFormSuspense({ itemID: schemaID });
|
||||||
|
|
||||||
const methods = useForm<ICreateConstituentaDTO>({
|
const methods = useForm<ICreateConstituentaDTO>({
|
||||||
resolver: zodResolver(schemaCreateConstituenta),
|
resolver: zodResolver(schemaCreateConstituenta),
|
||||||
|
|
|
@ -19,7 +19,7 @@ import {
|
||||||
schemaCreateConstituenta
|
schemaCreateConstituenta
|
||||||
} from '../../backend/types';
|
} from '../../backend/types';
|
||||||
import { useCreateConstituenta } from '../../backend/use-create-constituenta';
|
import { useCreateConstituenta } from '../../backend/use-create-constituenta';
|
||||||
import { type IRSForm } from '../../models/rsform';
|
import { useRSFormSuspense } from '../../backend/use-rsform';
|
||||||
import { generateAlias, validateNewAlias } from '../../models/rsform-api';
|
import { generateAlias, validateNewAlias } from '../../models/rsform-api';
|
||||||
import { FormCreateCst } from '../dlg-create-cst/form-create-cst';
|
import { FormCreateCst } from '../dlg-create-cst/form-create-cst';
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ import { TabTemplate } from './tab-template';
|
||||||
import { TemplateState } from './template-state';
|
import { TemplateState } from './template-state';
|
||||||
|
|
||||||
export interface DlgCstTemplateProps {
|
export interface DlgCstTemplateProps {
|
||||||
schema: IRSForm;
|
schemaID: number;
|
||||||
onCreate: (data: RO<IConstituentaBasicsDTO>) => void;
|
onCreate: (data: RO<IConstituentaBasicsDTO>) => void;
|
||||||
insertAfter?: number;
|
insertAfter?: number;
|
||||||
}
|
}
|
||||||
|
@ -41,8 +41,9 @@ export const TabID = {
|
||||||
export type TabID = (typeof TabID)[keyof typeof TabID];
|
export type TabID = (typeof TabID)[keyof typeof TabID];
|
||||||
|
|
||||||
export function DlgCstTemplate() {
|
export function DlgCstTemplate() {
|
||||||
const { schema, onCreate, insertAfter } = useDialogsStore(state => state.props as DlgCstTemplateProps);
|
const { schemaID, onCreate, insertAfter } = useDialogsStore(state => state.props as DlgCstTemplateProps);
|
||||||
const { createConstituenta: cstCreate } = useCreateConstituenta();
|
const { createConstituenta: cstCreate } = useCreateConstituenta();
|
||||||
|
const { schema } = useRSFormSuspense({ itemID: schemaID });
|
||||||
|
|
||||||
const methods = useForm<ICreateConstituentaDTO>({
|
const methods = useForm<ICreateConstituentaDTO>({
|
||||||
resolver: zodResolver(schemaCreateConstituenta),
|
resolver: zodResolver(schemaCreateConstituenta),
|
||||||
|
@ -92,7 +93,7 @@ export function DlgCstTemplate() {
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
|
|
||||||
<TabPanel>
|
<TabPanel>
|
||||||
<TabArguments />
|
<TabArguments schema={schema} />
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
|
|
||||||
<TabPanel>
|
<TabPanel>
|
||||||
|
|
|
@ -8,21 +8,22 @@ import { MiniButton } from '@/components/control';
|
||||||
import { DataTable, type IConditionalStyle } from '@/components/data-table';
|
import { DataTable, type IConditionalStyle } from '@/components/data-table';
|
||||||
import { IconAccept, IconRemove, IconReset } from '@/components/icons';
|
import { IconAccept, IconRemove, IconReset } from '@/components/icons';
|
||||||
import { NoData } from '@/components/view';
|
import { NoData } from '@/components/view';
|
||||||
import { useDialogsStore } from '@/stores/dialogs';
|
|
||||||
|
|
||||||
import { type ICreateConstituentaDTO } from '../../backend/types';
|
import { type ICreateConstituentaDTO } from '../../backend/types';
|
||||||
import { PickConstituenta } from '../../components/pick-constituenta';
|
import { PickConstituenta } from '../../components/pick-constituenta';
|
||||||
import { RSInput } from '../../components/rs-input';
|
import { RSInput } from '../../components/rs-input';
|
||||||
import { type IConstituenta } from '../../models/rsform';
|
import { type IConstituenta, type IRSForm } from '../../models/rsform';
|
||||||
import { type IArgumentValue } from '../../models/rslang';
|
import { type IArgumentValue } from '../../models/rslang';
|
||||||
|
|
||||||
import { type DlgCstTemplateProps } from './dlg-cst-template';
|
|
||||||
import { useTemplateContext } from './template-context';
|
import { useTemplateContext } from './template-context';
|
||||||
|
|
||||||
const argumentsHelper = createColumnHelper<IArgumentValue>();
|
const argumentsHelper = createColumnHelper<IArgumentValue>();
|
||||||
|
|
||||||
export function TabArguments() {
|
interface TabArgumentsProps {
|
||||||
const { schema } = useDialogsStore(state => state.props as DlgCstTemplateProps);
|
schema: IRSForm;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function TabArguments({ schema }: TabArgumentsProps) {
|
||||||
const { control } = useFormContext<ICreateConstituentaDTO>();
|
const { control } = useFormContext<ICreateConstituentaDTO>();
|
||||||
const { args, onChangeArguments } = useTemplateContext();
|
const { args, onChangeArguments } = useTemplateContext();
|
||||||
const definition = useWatch({ control, name: 'definition_formal' });
|
const definition = useWatch({ control, name: 'definition_formal' });
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { useFormContext } from 'react-hook-form';
|
||||||
import { useDialogsStore } from '@/stores/dialogs';
|
import { useDialogsStore } from '@/stores/dialogs';
|
||||||
|
|
||||||
import { type ICreateConstituentaDTO } from '../../backend/types';
|
import { type ICreateConstituentaDTO } from '../../backend/types';
|
||||||
|
import { useRSFormSuspense } from '../../backend/use-rsform';
|
||||||
import { type IConstituenta } from '../../models/rsform';
|
import { type IConstituenta } from '../../models/rsform';
|
||||||
import { generateAlias } from '../../models/rsform-api';
|
import { generateAlias } from '../../models/rsform-api';
|
||||||
import { type IArgumentValue } from '../../models/rslang';
|
import { type IArgumentValue } from '../../models/rslang';
|
||||||
|
@ -15,7 +16,8 @@ import { type DlgCstTemplateProps } from './dlg-cst-template';
|
||||||
import { TemplateContext } from './template-context';
|
import { TemplateContext } from './template-context';
|
||||||
|
|
||||||
export const TemplateState = ({ children }: React.PropsWithChildren) => {
|
export const TemplateState = ({ children }: React.PropsWithChildren) => {
|
||||||
const { schema } = useDialogsStore(state => state.props as DlgCstTemplateProps);
|
const { schemaID } = useDialogsStore(state => state.props as DlgCstTemplateProps);
|
||||||
|
const { schema } = useRSFormSuspense({ itemID: schemaID });
|
||||||
const { setValue } = useFormContext<ICreateConstituentaDTO>();
|
const { setValue } = useFormContext<ICreateConstituentaDTO>();
|
||||||
const [templateID, setTemplateID] = useState<number | null>(null);
|
const [templateID, setTemplateID] = useState<number | null>(null);
|
||||||
const [args, setArguments] = useState<IArgumentValue[]>([]);
|
const [args, setArguments] = useState<IArgumentValue[]>([]);
|
||||||
|
|
|
@ -8,19 +8,21 @@ import { useDialogsStore } from '@/stores/dialogs';
|
||||||
import { prefixes } from '@/utils/constants';
|
import { prefixes } from '@/utils/constants';
|
||||||
|
|
||||||
import { useDeleteConstituents } from '../../backend/use-delete-constituents';
|
import { useDeleteConstituents } from '../../backend/use-delete-constituents';
|
||||||
|
import { useRSFormSuspense } from '../../backend/use-rsform';
|
||||||
import { type IRSForm } from '../../models/rsform';
|
import { type IRSForm } from '../../models/rsform';
|
||||||
|
|
||||||
import { ListConstituents } from './list-constituents';
|
import { ListConstituents } from './list-constituents';
|
||||||
|
|
||||||
export interface DlgDeleteCstProps {
|
export interface DlgDeleteCstProps {
|
||||||
schema: IRSForm;
|
schemaID: number;
|
||||||
selected: number[];
|
selected: number[];
|
||||||
afterDelete?: (initialSchema: IRSForm, deleted: number[]) => void;
|
afterDelete?: (initialSchema: IRSForm, deleted: number[]) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DlgDeleteCst() {
|
export function DlgDeleteCst() {
|
||||||
const { selected, schema, afterDelete } = useDialogsStore(state => state.props as DlgDeleteCstProps);
|
const { selected, schemaID, afterDelete } = useDialogsStore(state => state.props as DlgDeleteCstProps);
|
||||||
const { deleteConstituents: cstDelete } = useDeleteConstituents();
|
const { deleteConstituents: cstDelete } = useDeleteConstituents();
|
||||||
|
const { schema } = useRSFormSuspense({ itemID: schemaID });
|
||||||
|
|
||||||
const [expandOut, setExpandOut] = useState(false);
|
const [expandOut, setExpandOut] = useState(false);
|
||||||
const expansion: number[] = schema.graph.expandAllOutputs(selected);
|
const expansion: number[] = schema.graph.expandAllOutputs(selected);
|
||||||
|
@ -31,7 +33,7 @@ export function DlgDeleteCst() {
|
||||||
|
|
||||||
function handleSubmit() {
|
function handleSubmit() {
|
||||||
const deleted = expandOut ? selected.concat(expansion) : selected;
|
const deleted = expandOut ? selected.concat(expansion) : selected;
|
||||||
void cstDelete({ itemID: schema.id, data: { items: deleted } }).then(() => afterDelete?.(schema, deleted));
|
void cstDelete({ itemID: schemaID, data: { items: deleted } }).then(() => afterDelete?.(schema, deleted));
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -13,20 +13,22 @@ import { useDialogsStore } from '@/stores/dialogs';
|
||||||
import { errorMsg } from '@/utils/labels';
|
import { errorMsg } from '@/utils/labels';
|
||||||
|
|
||||||
import { type IUpdateConstituentaDTO, schemaUpdateConstituenta } from '../../backend/types';
|
import { type IUpdateConstituentaDTO, schemaUpdateConstituenta } from '../../backend/types';
|
||||||
|
import { useRSFormSuspense } from '../../backend/use-rsform';
|
||||||
import { useUpdateConstituenta } from '../../backend/use-update-constituenta';
|
import { useUpdateConstituenta } from '../../backend/use-update-constituenta';
|
||||||
import { type IConstituenta, type IRSForm } from '../../models/rsform';
|
|
||||||
import { validateNewAlias } from '../../models/rsform-api';
|
import { validateNewAlias } from '../../models/rsform-api';
|
||||||
import { RSTabID } from '../../pages/rsform-page/rsedit-context';
|
import { RSTabID } from '../../pages/rsform-page/rsedit-context';
|
||||||
|
|
||||||
import { FormEditCst } from './form-edit-cst';
|
import { FormEditCst } from './form-edit-cst';
|
||||||
|
|
||||||
export interface DlgEditCstProps {
|
export interface DlgEditCstProps {
|
||||||
schema: IRSForm;
|
schemaID: number;
|
||||||
target: IConstituenta;
|
targetID: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DlgEditCst() {
|
export function DlgEditCst() {
|
||||||
const { schema, target } = useDialogsStore(state => state.props as DlgEditCstProps);
|
const { schemaID, targetID } = useDialogsStore(state => state.props as DlgEditCstProps);
|
||||||
|
const { schema } = useRSFormSuspense({ itemID: schemaID });
|
||||||
|
const target = schema.cstByID.get(targetID)!;
|
||||||
const hideDialog = useDialogsStore(state => state.hideDialog);
|
const hideDialog = useDialogsStore(state => state.hideDialog);
|
||||||
const { updateConstituenta } = useUpdateConstituenta();
|
const { updateConstituenta } = useUpdateConstituenta();
|
||||||
const router = useConceptNavigation();
|
const router = useConceptNavigation();
|
||||||
|
|
|
@ -5,12 +5,16 @@ import { HelpTopic } from '@/features/help';
|
||||||
import { BadgeHelp } from '@/features/help/components/badge-help';
|
import { BadgeHelp } from '@/features/help/components/badge-help';
|
||||||
|
|
||||||
import { MiniButton } from '@/components/control';
|
import { MiniButton } from '@/components/control';
|
||||||
import { TextArea, TextInput } from '@/components/input';
|
import { Label, TextArea, TextInput } from '@/components/input';
|
||||||
|
|
||||||
import { CstType, type IUpdateConstituentaDTO } from '../../backend/types';
|
import { CstType, type IUpdateConstituentaDTO } from '../../backend/types';
|
||||||
|
import { useClearAssociations } from '../../backend/use-clear-associations';
|
||||||
|
import { useCreateAssociation } from '../../backend/use-create-association';
|
||||||
|
import { useDeleteAssociation } from '../../backend/use-delete-association';
|
||||||
import { IconCrucialValue } from '../../components/icon-crucial-value';
|
import { IconCrucialValue } from '../../components/icon-crucial-value';
|
||||||
import { RSInput } from '../../components/rs-input';
|
import { RSInput } from '../../components/rs-input';
|
||||||
import { SelectCstType } from '../../components/select-cst-type';
|
import { SelectCstType } from '../../components/select-cst-type';
|
||||||
|
import { SelectMultiConstituenta } from '../../components/select-multi-constituenta';
|
||||||
import { getRSDefinitionPlaceholder, labelCstTypification, labelRSExpression } from '../../labels';
|
import { getRSDefinitionPlaceholder, labelCstTypification, labelRSExpression } from '../../labels';
|
||||||
import { type IConstituenta, type IRSForm } from '../../models/rsform';
|
import { type IConstituenta, type IRSForm } from '../../models/rsform';
|
||||||
import { generateAlias, isBaseSet, isBasicConcept } from '../../models/rsform-api';
|
import { generateAlias, isBaseSet, isBasicConcept } from '../../models/rsform-api';
|
||||||
|
@ -21,6 +25,10 @@ interface FormEditCstProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function FormEditCst({ target, schema }: FormEditCstProps) {
|
export function FormEditCst({ target, schema }: FormEditCstProps) {
|
||||||
|
const { createAssociation } = useCreateAssociation();
|
||||||
|
const { deleteAssociation } = useDeleteAssociation();
|
||||||
|
const { clearAssociations } = useClearAssociations();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
setValue,
|
setValue,
|
||||||
control,
|
control,
|
||||||
|
@ -36,6 +44,7 @@ export function FormEditCst({ target, schema }: FormEditCstProps) {
|
||||||
const isBasic = isBasicConcept(cst_type) || cst_type === CstType.NOMINAL;
|
const isBasic = isBasicConcept(cst_type) || cst_type === CstType.NOMINAL;
|
||||||
const isElementary = isBaseSet(cst_type);
|
const isElementary = isBaseSet(cst_type);
|
||||||
const showConvention = !!convention || forceComment || isBasic;
|
const showConvention = !!convention || forceComment || isBasic;
|
||||||
|
const associations = target.associations.map(id => schema.cstByID.get(id)!);
|
||||||
|
|
||||||
function handleTypeChange(newValue: CstType) {
|
function handleTypeChange(newValue: CstType) {
|
||||||
setValue('item_data.cst_type', newValue);
|
setValue('item_data.cst_type', newValue);
|
||||||
|
@ -47,6 +56,35 @@ export function FormEditCst({ target, schema }: FormEditCstProps) {
|
||||||
setValue('item_data.crucial', !crucial);
|
setValue('item_data.crucial', !crucial);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleAddAssociation(item: IConstituenta) {
|
||||||
|
void createAssociation({
|
||||||
|
itemID: schema.id,
|
||||||
|
data: {
|
||||||
|
container: target.id,
|
||||||
|
associate: item.id
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleRemoveAssociation(item: IConstituenta) {
|
||||||
|
void deleteAssociation({
|
||||||
|
itemID: schema.id,
|
||||||
|
data: {
|
||||||
|
container: target.id,
|
||||||
|
associate: item.id
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleClearAssociations() {
|
||||||
|
void clearAssociations({
|
||||||
|
itemID: schema.id,
|
||||||
|
data: {
|
||||||
|
target: target.id
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className='flex items-center self-center gap-3'>
|
<div className='flex items-center self-center gap-3'>
|
||||||
|
@ -83,6 +121,20 @@ export function FormEditCst({ target, schema }: FormEditCstProps) {
|
||||||
error={errors.item_data?.term_raw}
|
error={errors.item_data?.term_raw}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{target.cst_type === CstType.NOMINAL || target.associations.length > 0 ? (
|
||||||
|
<div className='flex flex-col gap-1'>
|
||||||
|
<Label text='Ассоциируемые конституенты' />
|
||||||
|
<SelectMultiConstituenta
|
||||||
|
items={schema.items.filter(item => item.id !== target.id)}
|
||||||
|
value={associations}
|
||||||
|
onAdd={handleAddAssociation}
|
||||||
|
onClear={handleClearAssociations}
|
||||||
|
onRemove={handleRemoveAssociation}
|
||||||
|
placeholder={'Выберите конституенты'}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
|
||||||
{cst_type !== CstType.NOMINAL ? (
|
{cst_type !== CstType.NOMINAL ? (
|
||||||
<TextArea
|
<TextArea
|
||||||
id='cst_typification'
|
id='cst_typification'
|
||||||
|
|
|
@ -20,7 +20,6 @@ import {
|
||||||
supportedGrammemes
|
supportedGrammemes
|
||||||
} from '../../models/language';
|
} from '../../models/language';
|
||||||
import { parseEntityReference, parseGrammemes, parseSyntacticReference } from '../../models/language-api';
|
import { parseEntityReference, parseGrammemes, parseSyntacticReference } from '../../models/language-api';
|
||||||
import { type IRSForm } from '../../models/rsform';
|
|
||||||
|
|
||||||
import { TabEntityReference } from './tab-entity-reference';
|
import { TabEntityReference } from './tab-entity-reference';
|
||||||
import { TabSyntacticReference } from './tab-syntactic-reference';
|
import { TabSyntacticReference } from './tab-syntactic-reference';
|
||||||
|
@ -51,7 +50,7 @@ const schemaEditReferenceState = z
|
||||||
export type IEditReferenceState = z.infer<typeof schemaEditReferenceState>;
|
export type IEditReferenceState = z.infer<typeof schemaEditReferenceState>;
|
||||||
|
|
||||||
export interface DlgEditReferenceProps {
|
export interface DlgEditReferenceProps {
|
||||||
schema: IRSForm;
|
schemaID: number;
|
||||||
initial: IReferenceInputState;
|
initial: IReferenceInputState;
|
||||||
onSave: (newRef: IReference) => void;
|
onSave: (newRef: IReference) => void;
|
||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { Controller, useFormContext, useWatch } from 'react-hook-form';
|
||||||
import { Label, TextInput } from '@/components/input';
|
import { Label, TextInput } from '@/components/input';
|
||||||
import { useDialogsStore } from '@/stores/dialogs';
|
import { useDialogsStore } from '@/stores/dialogs';
|
||||||
|
|
||||||
|
import { useRSFormSuspense } from '../../backend/use-rsform';
|
||||||
import { PickConstituenta } from '../../components/pick-constituenta';
|
import { PickConstituenta } from '../../components/pick-constituenta';
|
||||||
import { SelectMultiGrammeme } from '../../components/select-multi-grammeme';
|
import { SelectMultiGrammeme } from '../../components/select-multi-grammeme';
|
||||||
import { SelectWordForm } from '../../components/select-word-form';
|
import { SelectWordForm } from '../../components/select-word-form';
|
||||||
|
@ -15,7 +16,8 @@ import { CstMatchMode } from '../../stores/cst-search';
|
||||||
import { type DlgEditReferenceProps, type IEditReferenceState } from './dlg-edit-reference';
|
import { type DlgEditReferenceProps, type IEditReferenceState } from './dlg-edit-reference';
|
||||||
|
|
||||||
export function TabEntityReference() {
|
export function TabEntityReference() {
|
||||||
const { schema, initial } = useDialogsStore(state => state.props as DlgEditReferenceProps);
|
const { schemaID, initial } = useDialogsStore(state => state.props as DlgEditReferenceProps);
|
||||||
|
const { schema } = useRSFormSuspense({ itemID: schemaID });
|
||||||
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' });
|
||||||
|
|
||||||
|
|
|
@ -15,21 +15,23 @@ import { useGenerateLexeme } from '../../backend/cctext/use-generate-lexeme';
|
||||||
import { useInflectText } from '../../backend/cctext/use-inflect-text';
|
import { useInflectText } from '../../backend/cctext/use-inflect-text';
|
||||||
import { useIsProcessingCctext } from '../../backend/cctext/use-is-processing-cctext';
|
import { useIsProcessingCctext } from '../../backend/cctext/use-is-processing-cctext';
|
||||||
import { useParseText } from '../../backend/cctext/use-parse-text';
|
import { useParseText } from '../../backend/cctext/use-parse-text';
|
||||||
|
import { useRSFormSuspense } from '../../backend/use-rsform';
|
||||||
import { useUpdateConstituenta } from '../../backend/use-update-constituenta';
|
import { useUpdateConstituenta } from '../../backend/use-update-constituenta';
|
||||||
import { SelectMultiGrammeme } from '../../components/select-multi-grammeme';
|
import { SelectMultiGrammeme } from '../../components/select-multi-grammeme';
|
||||||
import { type Grammeme, type IWordForm, supportedGrammemes } from '../../models/language';
|
import { type Grammeme, type IWordForm, supportedGrammemes } from '../../models/language';
|
||||||
import { parseGrammemes, wordFormEquals } from '../../models/language-api';
|
import { parseGrammemes, wordFormEquals } from '../../models/language-api';
|
||||||
import { type IConstituenta } from '../../models/rsform';
|
|
||||||
|
|
||||||
import { TableWordForms } from './table-word-forms';
|
import { TableWordForms } from './table-word-forms';
|
||||||
|
|
||||||
export interface DlgEditWordFormsProps {
|
export interface DlgEditWordFormsProps {
|
||||||
itemID: number;
|
itemID: number;
|
||||||
target: IConstituenta;
|
targetID: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DlgEditWordForms() {
|
export function DlgEditWordForms() {
|
||||||
const { itemID, target } = useDialogsStore(state => state.props as DlgEditWordFormsProps);
|
const { itemID, targetID } = useDialogsStore(state => state.props as DlgEditWordFormsProps);
|
||||||
|
const { schema } = useRSFormSuspense({ itemID: itemID });
|
||||||
|
const target = schema.cstByID.get(targetID)!;
|
||||||
const { updateConstituenta: cstUpdate } = useUpdateConstituenta();
|
const { updateConstituenta: cstUpdate } = useUpdateConstituenta();
|
||||||
|
|
||||||
const isProcessing = useIsProcessingCctext();
|
const isProcessing = useIsProcessingCctext();
|
||||||
|
|
|
@ -11,14 +11,14 @@ import { useDialogsStore } from '@/stores/dialogs';
|
||||||
|
|
||||||
import { type IInlineSynthesisDTO, schemaInlineSynthesis } from '../../backend/types';
|
import { type IInlineSynthesisDTO, schemaInlineSynthesis } from '../../backend/types';
|
||||||
import { useInlineSynthesis } from '../../backend/use-inline-synthesis';
|
import { useInlineSynthesis } from '../../backend/use-inline-synthesis';
|
||||||
import { type IRSForm } from '../../models/rsform';
|
import { useRSFormSuspense } from '../../backend/use-rsform';
|
||||||
|
|
||||||
import { TabConstituents } from './tab-constituents';
|
import { TabConstituents } from './tab-constituents';
|
||||||
import { TabSource } from './tab-source';
|
import { TabSource } from './tab-source';
|
||||||
import { TabSubstitutions } from './tab-substitutions';
|
import { TabSubstitutions } from './tab-substitutions';
|
||||||
|
|
||||||
export interface DlgInlineSynthesisProps {
|
export interface DlgInlineSynthesisProps {
|
||||||
receiver: IRSForm;
|
receiverID: number;
|
||||||
onSynthesis: () => void;
|
onSynthesis: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,9 +30,10 @@ export const TabID = {
|
||||||
export type TabID = (typeof TabID)[keyof typeof TabID];
|
export type TabID = (typeof TabID)[keyof typeof TabID];
|
||||||
|
|
||||||
export function DlgInlineSynthesis() {
|
export function DlgInlineSynthesis() {
|
||||||
const { receiver, onSynthesis } = useDialogsStore(state => state.props as DlgInlineSynthesisProps);
|
const { receiverID, onSynthesis } = useDialogsStore(state => state.props as DlgInlineSynthesisProps);
|
||||||
const [activeTab, setActiveTab] = useState<TabID>(TabID.SCHEMA);
|
const [activeTab, setActiveTab] = useState<TabID>(TabID.SCHEMA);
|
||||||
const { inlineSynthesis } = useInlineSynthesis();
|
const { inlineSynthesis } = useInlineSynthesis();
|
||||||
|
const { schema: receiver } = useRSFormSuspense({ itemID: receiverID });
|
||||||
|
|
||||||
const methods = useForm<IInlineSynthesisDTO>({
|
const methods = useForm<IInlineSynthesisDTO>({
|
||||||
resolver: zodResolver(schemaInlineSynthesis),
|
resolver: zodResolver(schemaInlineSynthesis),
|
||||||
|
@ -81,7 +82,7 @@ export function DlgInlineSynthesis() {
|
||||||
|
|
||||||
<FormProvider {...methods}>
|
<FormProvider {...methods}>
|
||||||
<TabPanel>
|
<TabPanel>
|
||||||
<TabSource />
|
<TabSource receiver={receiver} />
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
|
|
||||||
<TabPanel>
|
<TabPanel>
|
||||||
|
@ -95,7 +96,7 @@ export function DlgInlineSynthesis() {
|
||||||
<TabPanel>
|
<TabPanel>
|
||||||
{!!sourceID ? (
|
{!!sourceID ? (
|
||||||
<Suspense fallback={<Loader />}>
|
<Suspense fallback={<Loader />}>
|
||||||
<TabSubstitutions />
|
<TabSubstitutions receiver={receiver} />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
) : null}
|
) : null}
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
|
|
|
@ -7,16 +7,17 @@ import { useLibrary } from '@/features/library/backend/use-library';
|
||||||
import { PickSchema } from '@/features/library/components/pick-schema';
|
import { PickSchema } from '@/features/library/components/pick-schema';
|
||||||
|
|
||||||
import { TextInput } from '@/components/input';
|
import { TextInput } from '@/components/input';
|
||||||
import { useDialogsStore } from '@/stores/dialogs';
|
|
||||||
|
|
||||||
import { type IInlineSynthesisDTO } from '../../backend/types';
|
import { type IInlineSynthesisDTO } from '../../backend/types';
|
||||||
|
import { type IRSForm } from '../../models/rsform';
|
||||||
import { sortItemsForInlineSynthesis } from '../../models/rsform-api';
|
import { sortItemsForInlineSynthesis } from '../../models/rsform-api';
|
||||||
|
|
||||||
import { type DlgInlineSynthesisProps } from './dlg-inline-synthesis';
|
interface TabSourceProps {
|
||||||
|
receiver: IRSForm;
|
||||||
|
}
|
||||||
|
|
||||||
export function TabSource() {
|
export function TabSource({ receiver }: TabSourceProps) {
|
||||||
const { items: libraryItems } = useLibrary();
|
const { items: libraryItems } = useLibrary();
|
||||||
const { receiver } = useDialogsStore(state => state.props as DlgInlineSynthesisProps);
|
|
||||||
const { setValue, control } = useFormContext<IInlineSynthesisDTO>();
|
const { setValue, control } = useFormContext<IInlineSynthesisDTO>();
|
||||||
const sourceID = useWatch({ control, name: 'source' });
|
const sourceID = useWatch({ control, name: 'source' });
|
||||||
|
|
||||||
|
|
|
@ -2,16 +2,16 @@
|
||||||
|
|
||||||
import { Controller, useFormContext, useWatch } from 'react-hook-form';
|
import { Controller, useFormContext, useWatch } from 'react-hook-form';
|
||||||
|
|
||||||
import { useDialogsStore } from '@/stores/dialogs';
|
|
||||||
|
|
||||||
import { type IInlineSynthesisDTO } from '../../backend/types';
|
import { type IInlineSynthesisDTO } from '../../backend/types';
|
||||||
import { useRSFormSuspense } from '../../backend/use-rsform';
|
import { useRSFormSuspense } from '../../backend/use-rsform';
|
||||||
import { PickSubstitutions } from '../../components/pick-substitutions';
|
import { PickSubstitutions } from '../../components/pick-substitutions';
|
||||||
|
import { type IRSForm } from '../../models/rsform';
|
||||||
|
|
||||||
import { type DlgInlineSynthesisProps } from './dlg-inline-synthesis';
|
interface TabSubstitutionsProps {
|
||||||
|
receiver: IRSForm;
|
||||||
|
}
|
||||||
|
|
||||||
export function TabSubstitutions() {
|
export function TabSubstitutions({ receiver }: TabSubstitutionsProps) {
|
||||||
const { receiver } = useDialogsStore(state => state.props as DlgInlineSynthesisProps);
|
|
||||||
const { control } = useFormContext<IInlineSynthesisDTO>();
|
const { control } = useFormContext<IInlineSynthesisDTO>();
|
||||||
const sourceID = useWatch({ control, name: 'source' });
|
const sourceID = useWatch({ control, name: 'source' });
|
||||||
const selected = useWatch({ control, name: 'items' });
|
const selected = useWatch({ control, name: 'items' });
|
||||||
|
|
|
@ -10,24 +10,26 @@ import { ModalForm } from '@/components/modal';
|
||||||
import { useDialogsStore } from '@/stores/dialogs';
|
import { useDialogsStore } from '@/stores/dialogs';
|
||||||
|
|
||||||
import { type CstType, type IUpdateConstituentaDTO, schemaUpdateConstituenta } from '../backend/types';
|
import { type CstType, type IUpdateConstituentaDTO, schemaUpdateConstituenta } from '../backend/types';
|
||||||
|
import { useRSFormSuspense } from '../backend/use-rsform';
|
||||||
import { useUpdateConstituenta } from '../backend/use-update-constituenta';
|
import { useUpdateConstituenta } from '../backend/use-update-constituenta';
|
||||||
import { SelectCstType } from '../components/select-cst-type';
|
import { SelectCstType } from '../components/select-cst-type';
|
||||||
import { type IConstituenta, type IRSForm } from '../models/rsform';
|
|
||||||
import { generateAlias, validateNewAlias } from '../models/rsform-api';
|
import { generateAlias, validateNewAlias } from '../models/rsform-api';
|
||||||
|
|
||||||
export interface DlgRenameCstProps {
|
export interface DlgRenameCstProps {
|
||||||
schema: IRSForm;
|
schemaID: number;
|
||||||
target: IConstituenta;
|
targetID: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DlgRenameCst() {
|
export function DlgRenameCst() {
|
||||||
const { schema, target } = useDialogsStore(state => state.props as DlgRenameCstProps);
|
const { schemaID, targetID } = useDialogsStore(state => state.props as DlgRenameCstProps);
|
||||||
const { updateConstituenta: cstUpdate } = useUpdateConstituenta();
|
const { updateConstituenta: cstUpdate } = useUpdateConstituenta();
|
||||||
|
const { schema } = useRSFormSuspense({ itemID: schemaID });
|
||||||
|
const target = schema.cstByID.get(targetID)!;
|
||||||
|
|
||||||
const { register, setValue, handleSubmit, control } = useForm<IUpdateConstituentaDTO>({
|
const { register, setValue, handleSubmit, control } = useForm<IUpdateConstituentaDTO>({
|
||||||
resolver: zodResolver(schemaUpdateConstituenta),
|
resolver: zodResolver(schemaUpdateConstituenta),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
target: target.id,
|
target: targetID,
|
||||||
item_data: {
|
item_data: {
|
||||||
alias: target.alias,
|
alias: target.alias,
|
||||||
cst_type: target.cst_type
|
cst_type: target.cst_type
|
||||||
|
@ -39,7 +41,7 @@ export function DlgRenameCst() {
|
||||||
const isValid = alias !== target.alias && validateNewAlias(alias, cst_type, schema);
|
const isValid = alias !== target.alias && validateNewAlias(alias, cst_type, schema);
|
||||||
|
|
||||||
function onSubmit(data: IUpdateConstituentaDTO) {
|
function onSubmit(data: IUpdateConstituentaDTO) {
|
||||||
return cstUpdate({ itemID: schema.id, data: data });
|
return cstUpdate({ itemID: schemaID, data: data });
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleChangeType(newType: CstType) {
|
function handleChangeType(newType: CstType) {
|
||||||
|
|
|
@ -12,18 +12,19 @@ import { ModalForm } from '@/components/modal';
|
||||||
import { useDialogsStore } from '@/stores/dialogs';
|
import { useDialogsStore } from '@/stores/dialogs';
|
||||||
|
|
||||||
import { type ISubstitutionsDTO, schemaSubstitutions } from '../backend/types';
|
import { type ISubstitutionsDTO, schemaSubstitutions } from '../backend/types';
|
||||||
|
import { useRSFormSuspense } from '../backend/use-rsform';
|
||||||
import { useSubstituteConstituents } from '../backend/use-substitute-constituents';
|
import { useSubstituteConstituents } from '../backend/use-substitute-constituents';
|
||||||
import { PickSubstitutions } from '../components/pick-substitutions';
|
import { PickSubstitutions } from '../components/pick-substitutions';
|
||||||
import { type IRSForm } from '../models/rsform';
|
|
||||||
|
|
||||||
export interface DlgSubstituteCstProps {
|
export interface DlgSubstituteCstProps {
|
||||||
schema: IRSForm;
|
schemaID: number;
|
||||||
onSubstitute: (data: ISubstitutionsDTO) => void;
|
onSubstitute: (data: ISubstitutionsDTO) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DlgSubstituteCst() {
|
export function DlgSubstituteCst() {
|
||||||
const { onSubstitute, schema } = useDialogsStore(state => state.props as DlgSubstituteCstProps);
|
const { onSubstitute, schemaID } = useDialogsStore(state => state.props as DlgSubstituteCstProps);
|
||||||
const { substituteConstituents: cstSubstitute } = useSubstituteConstituents();
|
const { substituteConstituents: cstSubstitute } = useSubstituteConstituents();
|
||||||
|
const { schema } = useRSFormSuspense({ itemID: schemaID });
|
||||||
|
|
||||||
const {
|
const {
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
|
|
|
@ -11,7 +11,7 @@ import {
|
||||||
|
|
||||||
import { type Graph } from '@/models/graph';
|
import { type Graph } from '@/models/graph';
|
||||||
|
|
||||||
import { CstType, type ParsingStatus, type ValueClass } from '../backend/types';
|
import { CstType, type IAssociation, type ParsingStatus, type ValueClass } from '../backend/types';
|
||||||
|
|
||||||
import { type IArgumentInfo } from './rslang';
|
import { type IArgumentInfo } from './rslang';
|
||||||
|
|
||||||
|
@ -58,6 +58,7 @@ export interface IConstituenta {
|
||||||
term_raw: string;
|
term_raw: string;
|
||||||
term_resolved: string;
|
term_resolved: string;
|
||||||
term_forms: TermForm[];
|
term_forms: TermForm[];
|
||||||
|
associations: number[];
|
||||||
|
|
||||||
parse?: {
|
parse?: {
|
||||||
status: ParsingStatus;
|
status: ParsingStatus;
|
||||||
|
@ -141,10 +142,13 @@ export interface IRSForm extends ILibraryItemData {
|
||||||
|
|
||||||
items: IConstituenta[];
|
items: IConstituenta[];
|
||||||
inheritance: IInheritanceInfo[];
|
inheritance: IInheritanceInfo[];
|
||||||
|
association: IAssociation[];
|
||||||
oss: ILibraryItemReference[];
|
oss: ILibraryItemReference[];
|
||||||
|
|
||||||
stats: IRSFormStats;
|
stats: IRSFormStats;
|
||||||
graph: Graph;
|
graph: Graph;
|
||||||
|
association_graph: Graph;
|
||||||
|
full_graph: Graph;
|
||||||
cstByAlias: Map<string, IConstituenta>;
|
cstByAlias: Map<string, IConstituenta>;
|
||||||
cstByID: Map<number, IConstituenta>;
|
cstByID: Map<number, IConstituenta>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,8 +25,17 @@ const SIDELIST_LAYOUT_THRESHOLD = 1000; // px
|
||||||
const COLUMN_DENSE_SEARCH_THRESHOLD = 1100;
|
const COLUMN_DENSE_SEARCH_THRESHOLD = 1100;
|
||||||
|
|
||||||
export function EditorConstituenta() {
|
export function EditorConstituenta() {
|
||||||
const { schema, activeCst, isContentEditable, selected, setSelected, moveUp, moveDown, cloneCst, navigateCst } =
|
const {
|
||||||
useRSEdit();
|
schema, //
|
||||||
|
activeCst,
|
||||||
|
isContentEditable,
|
||||||
|
selected,
|
||||||
|
setSelected,
|
||||||
|
moveUp,
|
||||||
|
moveDown,
|
||||||
|
cloneCst,
|
||||||
|
navigateCst
|
||||||
|
} = useRSEdit();
|
||||||
const windowSize = useWindowSize();
|
const windowSize = useWindowSize();
|
||||||
const mainHeight = useMainHeight();
|
const mainHeight = useMainHeight();
|
||||||
|
|
||||||
|
|
|
@ -6,13 +6,10 @@ import { Controller, useForm } from 'react-hook-form';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
|
|
||||||
import { useUpdateCrucial } from '@/features/rsform/backend/use-update-crucial';
|
|
||||||
import { IconCrucialValue } from '@/features/rsform/components/icon-crucial-value';
|
|
||||||
|
|
||||||
import { MiniButton, SubmitButton } from '@/components/control';
|
import { MiniButton, SubmitButton } from '@/components/control';
|
||||||
import { TextButton } from '@/components/control/text-button';
|
import { TextButton } from '@/components/control/text-button';
|
||||||
import { IconChild, IconPredecessor, IconSave } from '@/components/icons';
|
import { IconChild, IconPredecessor, IconSave } from '@/components/icons';
|
||||||
import { TextArea } from '@/components/input';
|
import { Label, TextArea } from '@/components/input';
|
||||||
import { Indicator } from '@/components/view';
|
import { Indicator } from '@/components/view';
|
||||||
import { useDialogsStore } from '@/stores/dialogs';
|
import { useDialogsStore } from '@/stores/dialogs';
|
||||||
import { useModificationStore } from '@/stores/modification';
|
import { useModificationStore } from '@/stores/modification';
|
||||||
|
@ -27,9 +24,15 @@ import {
|
||||||
ParsingStatus,
|
ParsingStatus,
|
||||||
schemaUpdateConstituenta
|
schemaUpdateConstituenta
|
||||||
} from '../../../backend/types';
|
} from '../../../backend/types';
|
||||||
|
import { useClearAssociations } from '../../../backend/use-clear-associations';
|
||||||
|
import { useCreateAssociation } from '../../../backend/use-create-association';
|
||||||
|
import { useDeleteAssociation } from '../../../backend/use-delete-association';
|
||||||
import { useMutatingRSForm } from '../../../backend/use-mutating-rsform';
|
import { useMutatingRSForm } from '../../../backend/use-mutating-rsform';
|
||||||
import { useUpdateConstituenta } from '../../../backend/use-update-constituenta';
|
import { useUpdateConstituenta } from '../../../backend/use-update-constituenta';
|
||||||
|
import { useUpdateCrucial } from '../../../backend/use-update-crucial';
|
||||||
|
import { IconCrucialValue } from '../../../components/icon-crucial-value';
|
||||||
import { RefsInput } from '../../../components/refs-input';
|
import { RefsInput } from '../../../components/refs-input';
|
||||||
|
import { SelectMultiConstituenta } from '../../../components/select-multi-constituenta';
|
||||||
import {
|
import {
|
||||||
getRSDefinitionPlaceholder,
|
getRSDefinitionPlaceholder,
|
||||||
labelCstTypification,
|
labelCstTypification,
|
||||||
|
@ -57,6 +60,9 @@ export function FormConstituenta({ disabled, id, toggleReset, schema, activeCst,
|
||||||
|
|
||||||
const { updateConstituenta } = useUpdateConstituenta();
|
const { updateConstituenta } = useUpdateConstituenta();
|
||||||
const { updateCrucial } = useUpdateCrucial();
|
const { updateCrucial } = useUpdateCrucial();
|
||||||
|
const { createAssociation } = useCreateAssociation();
|
||||||
|
const { deleteAssociation } = useDeleteAssociation();
|
||||||
|
const { clearAssociations } = useClearAssociations();
|
||||||
const showTypification = useDialogsStore(state => state.showShowTypeGraph);
|
const showTypification = useDialogsStore(state => state.showShowTypeGraph);
|
||||||
const showEditTerm = useDialogsStore(state => state.showEditWordForms);
|
const showEditTerm = useDialogsStore(state => state.showEditWordForms);
|
||||||
const showRenameCst = useDialogsStore(state => state.showRenameCst);
|
const showRenameCst = useDialogsStore(state => state.showRenameCst);
|
||||||
|
@ -106,6 +112,11 @@ export function FormConstituenta({ disabled, id, toggleReset, schema, activeCst,
|
||||||
[activeCst, localParse]
|
[activeCst, localParse]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const associations = useMemo(
|
||||||
|
() => activeCst.associations.map(id => schema.cstByID.get(id)!),
|
||||||
|
[activeCst.associations, schema.cstByID]
|
||||||
|
);
|
||||||
|
|
||||||
const isBasic = isBasicConcept(activeCst.cst_type) || activeCst.cst_type === CstType.NOMINAL;
|
const isBasic = isBasicConcept(activeCst.cst_type) || activeCst.cst_type === CstType.NOMINAL;
|
||||||
const isElementary = isBaseSet(activeCst.cst_type);
|
const isElementary = isBaseSet(activeCst.cst_type);
|
||||||
const showConvention = !!activeCst.convention || forceComment || isBasic;
|
const showConvention = !!activeCst.convention || forceComment || isBasic;
|
||||||
|
@ -168,11 +179,11 @@ export function FormConstituenta({ disabled, id, toggleReset, schema, activeCst,
|
||||||
if (isModified && !promptUnsaved()) {
|
if (isModified && !promptUnsaved()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
showEditTerm({ itemID: schema.id, target: activeCst });
|
showEditTerm({ itemID: schema.id, targetID: activeCst.id });
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleRenameCst() {
|
function handleRenameCst() {
|
||||||
showRenameCst({ schema: schema, target: activeCst });
|
showRenameCst({ schemaID: schema.id, targetID: activeCst.id });
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleToggleCrucial() {
|
function handleToggleCrucial() {
|
||||||
|
@ -185,6 +196,35 @@ export function FormConstituenta({ disabled, id, toggleReset, schema, activeCst,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleAddAssociation(item: IConstituenta) {
|
||||||
|
void createAssociation({
|
||||||
|
itemID: schema.id,
|
||||||
|
data: {
|
||||||
|
container: activeCst.id,
|
||||||
|
associate: item.id
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleRemoveAssociation(item: IConstituenta) {
|
||||||
|
void deleteAssociation({
|
||||||
|
itemID: schema.id,
|
||||||
|
data: {
|
||||||
|
container: activeCst.id,
|
||||||
|
associate: item.id
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleClearAssociations() {
|
||||||
|
void clearAssociations({
|
||||||
|
itemID: schema.id,
|
||||||
|
data: {
|
||||||
|
target: activeCst.id
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
id={id}
|
id={id}
|
||||||
|
@ -239,6 +279,21 @@ export function FormConstituenta({ disabled, id, toggleReset, schema, activeCst,
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{activeCst.cst_type === CstType.NOMINAL || activeCst.associations.length > 0 ? (
|
||||||
|
<div className='flex flex-col gap-1'>
|
||||||
|
<Label text='Ассоциируемые конституенты' />
|
||||||
|
<SelectMultiConstituenta
|
||||||
|
items={schema.items.filter(item => item.id !== activeCst.id)}
|
||||||
|
value={associations}
|
||||||
|
onAdd={handleAddAssociation}
|
||||||
|
onClear={handleClearAssociations}
|
||||||
|
onRemove={handleRemoveAssociation}
|
||||||
|
disabled={disabled || isModified}
|
||||||
|
placeholder={disabled ? '' : 'Выберите конституенты'}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
|
||||||
{activeCst.cst_type !== CstType.NOMINAL ? (
|
{activeCst.cst_type !== CstType.NOMINAL ? (
|
||||||
<TextArea
|
<TextArea
|
||||||
id='cst_typification'
|
id='cst_typification'
|
||||||
|
|
|
@ -60,7 +60,7 @@ export function MenuEditSchema() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
showSubstituteCst({
|
showSubstituteCst({
|
||||||
schema: schema,
|
schemaID: schema.id,
|
||||||
onSubstitute: data => setSelected(prev => prev.filter(id => !data.substitutions.find(sub => sub.original === id)))
|
onSubstitute: data => setSelected(prev => prev.filter(id => !data.substitutions.find(sub => sub.original === id)))
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -94,7 +94,7 @@ export function MenuEditSchema() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
showInlineSynthesis({
|
showInlineSynthesis({
|
||||||
receiver: schema,
|
receiverID: schema.id,
|
||||||
onSynthesis: () => deselectAll()
|
onSynthesis: () => deselectAll()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -231,7 +231,7 @@ export const RSEditState = ({
|
||||||
if (skipDialog) {
|
if (skipDialog) {
|
||||||
void cstCreate({ itemID: schema.id, data }).then(onCreateCst);
|
void cstCreate({ itemID: schema.id, data }).then(onCreateCst);
|
||||||
} else {
|
} else {
|
||||||
showCreateCst({ schema: schema, onCreate: onCreateCst, initial: data });
|
showCreateCst({ schemaID: schema.id, onCreate: onCreateCst, initial: data });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -260,7 +260,7 @@ export const RSEditState = ({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
showDeleteCst({
|
showDeleteCst({
|
||||||
schema: schema,
|
schemaID: schema.id,
|
||||||
selected: selected,
|
selected: selected,
|
||||||
afterDelete: (schema, deleted) => {
|
afterDelete: (schema, deleted) => {
|
||||||
const isEmpty = deleted.length === schema.items.length;
|
const isEmpty = deleted.length === schema.items.length;
|
||||||
|
@ -281,7 +281,7 @@ export const RSEditState = ({
|
||||||
if (isModified && !promptUnsaved()) {
|
if (isModified && !promptUnsaved()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
showCstTemplate({ schema: schema, onCreate: onCreateCst, insertAfter: activeCst?.id });
|
showCstTemplate({ schemaID: schema.id, onCreate: onCreateCst, insertAfter: activeCst?.id });
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
Loading…
Reference in New Issue
Block a user