F: Implement Constituenta editing from OSS
This commit is contained in:
parent
3feadb6f06
commit
d901094d8e
|
@ -27,7 +27,7 @@ export function ApplicationLayout() {
|
|||
<NavigationState>
|
||||
<div className='min-w-80 antialiased h-full max-w-480 mx-auto'>
|
||||
<ToasterThemed
|
||||
className={clsx('sm:text-[14px]/[20px] text-[12px]/[16px]', noNavigationAnimation ? 'mt-10' : 'mt-18')}
|
||||
className={clsx('sm:text-[14px]/[20px] text-[12px]/[16px]', noNavigationAnimation ? 'mt-9' : 'mt-17')}
|
||||
aria-label='Оповещения'
|
||||
autoClose={3000}
|
||||
draggable={false}
|
||||
|
|
|
@ -128,6 +128,9 @@ const DlgOssSettings = React.lazy(() =>
|
|||
default: module.DlgOssSettings
|
||||
}))
|
||||
);
|
||||
const DlgEditCst = React.lazy(() =>
|
||||
import('@/features/rsform/dialogs/dlg-edit-cst').then(module => ({ default: module.DlgEditCst }))
|
||||
);
|
||||
|
||||
export const GlobalDialogs = () => {
|
||||
const active = useDialogsStore(state => state.active);
|
||||
|
@ -188,5 +191,7 @@ export const GlobalDialogs = () => {
|
|||
return <DlgSubstituteCst />;
|
||||
case DialogType.UPLOAD_RSFORM:
|
||||
return <DlgUploadRSForm />;
|
||||
case DialogType.EDIT_CONSTITUENTA:
|
||||
return <DlgEditCst />;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -44,8 +44,7 @@ export function SidePanel({ isMounted, className }: SidePanelProps) {
|
|||
style={{ height: sidePanelHeight }}
|
||||
>
|
||||
<MiniButton
|
||||
titleHtml='Закрыть панель'
|
||||
aria-label='Закрыть'
|
||||
title='Закрыть панель'
|
||||
noPadding
|
||||
icon={<IconClose size='1.25rem' />}
|
||||
className={clsx(
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import { useState } from 'react';
|
||||
|
||||
import { type IConstituenta } from '@/features/rsform';
|
||||
import { useRSFormSuspense } from '@/features/rsform/backend/use-rsform';
|
||||
import { RSFormStats } from '@/features/rsform/components/rsform-stats';
|
||||
import { ViewConstituents } from '@/features/rsform/components/view-constituents';
|
||||
|
||||
import { useFitHeight } from '@/stores/app-layout';
|
||||
import { notImplemented } from '@/utils/utils';
|
||||
import { useDialogsStore } from '@/stores/dialogs';
|
||||
|
||||
import { ToolbarConstituents } from './toolbar-constituents';
|
||||
|
||||
|
@ -18,9 +19,14 @@ export function ViewSchema({ schemaID, isMutable }: ViewSchemaProps) {
|
|||
const { schema } = useRSFormSuspense({ itemID: schemaID });
|
||||
const [activeID, setActiveID] = useState<number | null>(null);
|
||||
const activeCst = activeID ? schema.cstByID.get(activeID) ?? null : null;
|
||||
const showEditCst = useDialogsStore(state => state.showEditCst);
|
||||
|
||||
const listHeight = useFitHeight('14.5rem', '10rem');
|
||||
|
||||
function handleEditCst(cst: IConstituenta) {
|
||||
showEditCst({ schema: schema, target: cst });
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='grid h-full relative cc-fade-in mt-5' style={{ gridTemplateRows: '1fr auto' }}>
|
||||
<ToolbarConstituents
|
||||
|
@ -28,7 +34,7 @@ export function ViewSchema({ schemaID, isMutable }: ViewSchemaProps) {
|
|||
schema={schema}
|
||||
activeCst={activeCst}
|
||||
isMutable={isMutable}
|
||||
onEditActive={notImplemented}
|
||||
onEditActive={() => handleEditCst(activeCst!)}
|
||||
setActive={setActiveID}
|
||||
resetActive={() => setActiveID(null)}
|
||||
/>
|
||||
|
@ -41,6 +47,7 @@ export function ViewSchema({ schemaID, isMutable }: ViewSchemaProps) {
|
|||
activeCst={activeCst}
|
||||
onActivate={cst => setActiveID(cst.id)}
|
||||
maxListHeight={listHeight}
|
||||
onDoubleClick={isMutable ? handleEditCst : undefined}
|
||||
/>
|
||||
|
||||
<RSFormStats className='pr-4 py-2 ml-[-1rem]' stats={schema.stats} />
|
||||
|
|
|
@ -18,6 +18,7 @@ interface TableSideConstituentsProps {
|
|||
schema: IRSForm;
|
||||
activeCst?: IConstituenta | null;
|
||||
onActivate?: (cst: IConstituenta) => void;
|
||||
onDoubleClick?: (cst: IConstituenta) => void;
|
||||
|
||||
maxHeight?: string;
|
||||
autoScroll?: boolean;
|
||||
|
@ -29,6 +30,7 @@ export function TableSideConstituents({
|
|||
schema,
|
||||
activeCst,
|
||||
onActivate,
|
||||
onDoubleClick,
|
||||
maxHeight,
|
||||
autoScroll = true
|
||||
}: TableSideConstituentsProps) {
|
||||
|
@ -102,6 +104,7 @@ export function TableSideConstituents({
|
|||
</NoData>
|
||||
}
|
||||
onRowClicked={onActivate ? cst => onActivate(cst) : undefined}
|
||||
onRowDoubleClicked={onDoubleClick}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ interface ViewConstituentsProps {
|
|||
schema: IRSForm;
|
||||
activeCst?: IConstituenta | null;
|
||||
onActivate?: (cst: IConstituenta) => void;
|
||||
onDoubleClick?: (cst: IConstituenta) => void;
|
||||
|
||||
className?: string;
|
||||
maxListHeight?: string;
|
||||
|
@ -23,6 +24,7 @@ export function ViewConstituents({
|
|||
schema,
|
||||
activeCst,
|
||||
onActivate,
|
||||
onDoubleClick,
|
||||
|
||||
className,
|
||||
maxListHeight,
|
||||
|
@ -43,6 +45,7 @@ export function ViewConstituents({
|
|||
onActivate={onActivate}
|
||||
maxHeight={maxListHeight}
|
||||
autoScroll={autoScroll}
|
||||
onDoubleClick={onDoubleClick}
|
||||
/>
|
||||
</aside>
|
||||
);
|
||||
|
|
|
@ -11,6 +11,7 @@ import { TextArea, TextInput } from '@/components/input';
|
|||
import { CstType, type ICreateConstituentaDTO } from '../../backend/types';
|
||||
import { RSInput } from '../../components/rs-input';
|
||||
import { SelectCstType } from '../../components/select-cst-type';
|
||||
import { getRSDefinitionPlaceholder } from '../../labels';
|
||||
import { type IRSForm } from '../../models/rsform';
|
||||
import { generateAlias, isBaseSet, isBasicConcept, isFunctional } from '../../models/rsform-api';
|
||||
|
||||
|
@ -85,9 +86,7 @@ export function FormCreateCst({ schema }: FormCreateCstProps) {
|
|||
? 'Определение функции'
|
||||
: 'Формальное определение'
|
||||
}
|
||||
placeholder={
|
||||
cst_type !== CstType.STRUCTURED ? 'Родоструктурное выражение' : 'Типизация родовой структуры'
|
||||
}
|
||||
placeholder={getRSDefinitionPlaceholder(cst_type)}
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
schema={schema}
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
import { FormProvider, useForm, useWatch } from 'react-hook-form';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
|
||||
import { urls, useConceptNavigation } from '@/app';
|
||||
import { useFindPredecessor } from '@/features/oss/backend/use-find-predecessor';
|
||||
|
||||
import { MiniButton } from '@/components/control';
|
||||
import { IconChild, IconRSForm } from '@/components/icons';
|
||||
import { ModalForm } from '@/components/modal';
|
||||
import { useDialogsStore } from '@/stores/dialogs';
|
||||
import { errorMsg } from '@/utils/labels';
|
||||
|
||||
import { type IUpdateConstituentaDTO, schemaUpdateConstituenta } from '../../backend/types';
|
||||
import { useUpdateConstituenta } from '../../backend/use-update-constituenta';
|
||||
import { type IConstituenta, type IRSForm } from '../../models/rsform';
|
||||
import { validateNewAlias } from '../../models/rsform-api';
|
||||
import { RSTabID } from '../../pages/rsform-page/rsedit-context';
|
||||
|
||||
import { FormEditCst } from './form-edit-cst';
|
||||
|
||||
export interface DlgEditCstProps {
|
||||
schema: IRSForm;
|
||||
target: IConstituenta;
|
||||
}
|
||||
|
||||
export function DlgEditCst() {
|
||||
const { schema, target } = useDialogsStore(state => state.props as DlgEditCstProps);
|
||||
const hideDialog = useDialogsStore(state => state.hideDialog);
|
||||
const { updateConstituenta } = useUpdateConstituenta();
|
||||
const router = useConceptNavigation();
|
||||
const { findPredecessor } = useFindPredecessor();
|
||||
|
||||
const methods = useForm<IUpdateConstituentaDTO>({
|
||||
resolver: zodResolver(schemaUpdateConstituenta),
|
||||
defaultValues: {
|
||||
target: target.id,
|
||||
item_data: {
|
||||
alias: target.alias,
|
||||
cst_type: target.cst_type,
|
||||
convention: target.convention,
|
||||
definition_formal: target.definition_formal,
|
||||
definition_raw: target.definition_raw,
|
||||
term_raw: target.term_raw,
|
||||
term_forms: target.term_forms
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const alias = useWatch({ control: methods.control, name: 'item_data.alias' })!;
|
||||
const cst_type = useWatch({ control: methods.control, name: 'item_data.cst_type' })!;
|
||||
const isValid = (alias === target.alias && cst_type == target.cst_type) || validateNewAlias(alias, cst_type, schema);
|
||||
|
||||
function onSubmit(data: IUpdateConstituentaDTO) {
|
||||
return updateConstituenta({ itemID: schema.id, data });
|
||||
}
|
||||
|
||||
function navigateToTarget() {
|
||||
hideDialog();
|
||||
router.push({
|
||||
path: urls.schema_props({
|
||||
id: schema.id,
|
||||
tab: RSTabID.CST_EDIT,
|
||||
active: target.id
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
function editSource() {
|
||||
hideDialog();
|
||||
void findPredecessor(target.id).then(reference =>
|
||||
router.push({
|
||||
path: urls.schema_props({
|
||||
id: reference.schema,
|
||||
active: reference.id,
|
||||
tab: RSTabID.CST_EDIT
|
||||
})
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<ModalForm
|
||||
header='Редактирование конституенты'
|
||||
canSubmit={isValid}
|
||||
onSubmit={event => void methods.handleSubmit(onSubmit)(event)}
|
||||
submitInvalidTooltip={errorMsg.aliasInvalid}
|
||||
submitText='Сохранить'
|
||||
className='cc-column w-140 max-h-120 py-2 px-6'
|
||||
>
|
||||
<div className='cc-icons absolute z-pop left-2 top-2'>
|
||||
<MiniButton
|
||||
title='Редактировать в КС'
|
||||
noPadding
|
||||
icon={<IconRSForm size='1.25rem' className='text-primary' />}
|
||||
className=''
|
||||
onClick={navigateToTarget}
|
||||
/>
|
||||
<MiniButton
|
||||
title='Перейти к предку'
|
||||
noPadding
|
||||
icon={<IconChild size='1.25rem' className={target.is_inherited ? 'text-primary' : 'text-foreground-muted'} />}
|
||||
disabled={!target.is_inherited}
|
||||
onClick={editSource}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<FormProvider {...methods}>
|
||||
<FormEditCst target={target} schema={schema} />
|
||||
</FormProvider>
|
||||
</ModalForm>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,156 @@
|
|||
import { useState } from 'react';
|
||||
import { Controller, useFormContext, useWatch } from 'react-hook-form';
|
||||
|
||||
import { TextArea, TextInput } from '@/components/input';
|
||||
|
||||
import { CstType, type IUpdateConstituentaDTO } from '../../backend/types';
|
||||
import { SelectCstType } from '../../components/select-cst-type';
|
||||
import { getRSDefinitionPlaceholder, labelCstTypification } from '../../labels';
|
||||
import { type IConstituenta, type IRSForm } from '../../models/rsform';
|
||||
import { generateAlias, isBaseSet, isBasicConcept, isFunctional } from '../../models/rsform-api';
|
||||
|
||||
interface FormEditCstProps {
|
||||
schema: IRSForm;
|
||||
target: IConstituenta;
|
||||
}
|
||||
|
||||
export function FormEditCst({ target, schema }: FormEditCstProps) {
|
||||
const {
|
||||
setValue,
|
||||
control,
|
||||
register,
|
||||
formState: { errors }
|
||||
} = useFormContext<IUpdateConstituentaDTO>();
|
||||
|
||||
const [forceComment, setForceComment] = useState(false);
|
||||
|
||||
const cst_type = useWatch({ control, name: 'item_data.cst_type' }) ?? CstType.BASE;
|
||||
const convention = useWatch({ control, name: 'item_data.convention' });
|
||||
const isBasic = isBasicConcept(cst_type);
|
||||
const isElementary = isBaseSet(cst_type);
|
||||
const isFunction = isFunctional(cst_type);
|
||||
const showConvention = !!convention || forceComment || isBasic;
|
||||
|
||||
function handleTypeChange(newValue: CstType) {
|
||||
setValue('item_data.cst_type', newValue);
|
||||
setValue('item_data.alias', generateAlias(newValue, schema), { shouldValidate: true });
|
||||
setForceComment(false);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className='flex items-center self-center gap-3'>
|
||||
<SelectCstType
|
||||
id='dlg_cst_type' //
|
||||
value={cst_type}
|
||||
onChange={handleTypeChange}
|
||||
disabled={target.is_inherited}
|
||||
/>
|
||||
<TextInput
|
||||
id='dlg_cst_alias'
|
||||
dense
|
||||
label='Имя'
|
||||
className='w-28'
|
||||
{...register('item_data.alias')}
|
||||
error={errors.item_data?.alias}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<TextArea
|
||||
id='dlg_cst_term'
|
||||
fitContent
|
||||
spellCheck
|
||||
label='Термин'
|
||||
className='max-h-15'
|
||||
{...register('item_data.term_raw')}
|
||||
error={errors.item_data?.term_raw}
|
||||
/>
|
||||
|
||||
<TextArea
|
||||
id='cst_typification'
|
||||
fitContent
|
||||
dense
|
||||
noResize
|
||||
noBorder
|
||||
noOutline
|
||||
transparent
|
||||
readOnly
|
||||
label='Типизация'
|
||||
value={labelCstTypification(target)}
|
||||
className='cursor-default'
|
||||
/>
|
||||
|
||||
<Controller
|
||||
control={control}
|
||||
name='item_data.definition_formal'
|
||||
render={({ field }) =>
|
||||
!!field.value || (!isElementary && !target.is_inherited) ? (
|
||||
<TextArea
|
||||
id='dlg_cst_expression'
|
||||
fitContent
|
||||
label={
|
||||
cst_type === CstType.STRUCTURED
|
||||
? 'Область определения'
|
||||
: isFunction
|
||||
? 'Определение функции'
|
||||
: 'Формальное определение'
|
||||
}
|
||||
placeholder={getRSDefinitionPlaceholder(cst_type)}
|
||||
className='max-h-15'
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
error={errors.item_data?.definition_formal}
|
||||
disabled={target.is_inherited}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)
|
||||
}
|
||||
/>
|
||||
|
||||
<Controller
|
||||
control={control}
|
||||
name='item_data.definition_raw'
|
||||
render={({ field }) =>
|
||||
!!field.value || !isElementary ? (
|
||||
<TextArea
|
||||
id='dlg_edit_cst_definition_raw'
|
||||
fitContent
|
||||
spellCheck
|
||||
label='Текстовое определение'
|
||||
className='max-h-15'
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
error={errors.item_data?.definition_raw}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)
|
||||
}
|
||||
/>
|
||||
|
||||
{!showConvention ? (
|
||||
<button
|
||||
id='dlg_cst_show_comment'
|
||||
tabIndex={-1}
|
||||
type='button'
|
||||
className='self-start cc-label text-primary hover:underline select-none'
|
||||
onClick={() => setForceComment(true)}
|
||||
>
|
||||
Добавить комментарий
|
||||
</button>
|
||||
) : (
|
||||
<TextArea
|
||||
id='dlg_edit_cst_convention'
|
||||
fitContent
|
||||
spellCheck
|
||||
label={isBasic ? 'Конвенция' : 'Комментарий'}
|
||||
className='max-h-20'
|
||||
{...register('item_data.convention')}
|
||||
error={errors.item_data?.convention}
|
||||
disabled={isBasic && target.is_inherited}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
export { DlgEditCst } from './dlg-edit-cst';
|
|
@ -15,6 +15,7 @@ import { type DlgRelocateConstituentsProps } from '@/features/oss/dialogs/dlg-re
|
|||
import { type DlgCreateCstProps } from '@/features/rsform/dialogs/dlg-create-cst/dlg-create-cst';
|
||||
import { type DlgCstTemplateProps } from '@/features/rsform/dialogs/dlg-cst-template/dlg-cst-template';
|
||||
import { type DlgDeleteCstProps } from '@/features/rsform/dialogs/dlg-delete-cst/dlg-delete-cst';
|
||||
import { type DlgEditCstProps } from '@/features/rsform/dialogs/dlg-edit-cst/dlg-edit-cst';
|
||||
import { type DlgEditReferenceProps } from '@/features/rsform/dialogs/dlg-edit-reference/dlg-edit-reference';
|
||||
import { type DlgEditWordFormsProps } from '@/features/rsform/dialogs/dlg-edit-word-forms/dlg-edit-word-forms';
|
||||
import { type DlgInlineSynthesisProps } from '@/features/rsform/dialogs/dlg-inline-synthesis/dlg-inline-synthesis';
|
||||
|
@ -45,6 +46,7 @@ export const DialogType = {
|
|||
CHANGE_INPUT_SCHEMA: 11,
|
||||
RELOCATE_CONSTITUENTS: 12,
|
||||
OSS_SETTINGS: 26,
|
||||
EDIT_CONSTITUENTA: 27,
|
||||
|
||||
CLONE_LIBRARY_ITEM: 13,
|
||||
UPLOAD_RSFORM: 14,
|
||||
|
@ -98,6 +100,7 @@ interface DialogsStore {
|
|||
showQR: (props: DlgShowQRProps) => void;
|
||||
showSubstituteCst: (props: DlgSubstituteCstProps) => void;
|
||||
showUploadRSForm: (props: DlgUploadRSFormProps) => void;
|
||||
showEditCst: (props: DlgEditCstProps) => void;
|
||||
}
|
||||
|
||||
export const useDialogsStore = create<DialogsStore>()(set => ({
|
||||
|
@ -135,5 +138,6 @@ export const useDialogsStore = create<DialogsStore>()(set => ({
|
|||
showRenameCst: props => set({ active: DialogType.RENAME_CONSTITUENTA, props: props }),
|
||||
showQR: props => set({ active: DialogType.SHOW_QR_CODE, props: props }),
|
||||
showSubstituteCst: props => set({ active: DialogType.SUBSTITUTE_CONSTITUENTS, props: props }),
|
||||
showUploadRSForm: props => set({ active: DialogType.UPLOAD_RSFORM, props: props })
|
||||
showUploadRSForm: props => set({ active: DialogType.UPLOAD_RSFORM, props: props }),
|
||||
showEditCst: props => set({ active: DialogType.EDIT_CONSTITUENTA, props: props })
|
||||
}));
|
||||
|
|
Loading…
Reference in New Issue
Block a user