2024-02-04 23:45:49 +03:00
|
|
|
'use client';
|
|
|
|
|
|
|
|
import { AnimatePresence } from 'framer-motion';
|
|
|
|
import fileDownload from 'js-file-download';
|
|
|
|
import { createContext, useCallback, useContext, useLayoutEffect, useMemo, useState } from 'react';
|
|
|
|
import { toast } from 'react-toastify';
|
|
|
|
|
2024-04-01 21:45:10 +03:00
|
|
|
import { urls } from '@/app/urls';
|
2024-02-04 23:45:49 +03:00
|
|
|
import { useAccessMode } from '@/context/AccessModeContext';
|
|
|
|
import { useAuth } from '@/context/AuthContext';
|
2024-06-26 19:47:31 +03:00
|
|
|
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
2024-07-14 14:41:05 +03:00
|
|
|
import { useConceptNavigation } from '@/context/NavigationContext';
|
2024-02-04 23:45:49 +03:00
|
|
|
import { useRSForm } from '@/context/RSFormContext';
|
2024-06-02 23:41:46 +03:00
|
|
|
import DlgChangeLocation from '@/dialogs/DlgChangeLocation';
|
2024-02-04 23:45:49 +03:00
|
|
|
import DlgCloneLibraryItem from '@/dialogs/DlgCloneLibraryItem';
|
|
|
|
import DlgConstituentaTemplate from '@/dialogs/DlgConstituentaTemplate';
|
|
|
|
import DlgCreateCst from '@/dialogs/DlgCreateCst';
|
2024-03-08 18:39:08 +03:00
|
|
|
import DlgCreateVersion from '@/dialogs/DlgCreateVersion';
|
2024-02-04 23:45:49 +03:00
|
|
|
import DlgDeleteCst from '@/dialogs/DlgDeleteCst';
|
2024-05-27 20:42:34 +03:00
|
|
|
import DlgEditEditors from '@/dialogs/DlgEditEditors';
|
2024-03-08 18:39:08 +03:00
|
|
|
import DlgEditVersions from '@/dialogs/DlgEditVersions';
|
2024-02-04 23:45:49 +03:00
|
|
|
import DlgEditWordForms from '@/dialogs/DlgEditWordForms';
|
2024-03-18 16:22:27 +03:00
|
|
|
import DlgInlineSynthesis from '@/dialogs/DlgInlineSynthesis';
|
2024-02-04 23:45:49 +03:00
|
|
|
import DlgRenameCst from '@/dialogs/DlgRenameCst';
|
2024-11-20 00:33:25 +03:00
|
|
|
import DlgShowTypeGraph from '@/dialogs/DlgShowTypeGraph';
|
2024-03-01 18:19:33 +03:00
|
|
|
import DlgSubstituteCst from '@/dialogs/DlgSubstituteCst';
|
2024-02-04 23:45:49 +03:00
|
|
|
import DlgUploadRSForm from '@/dialogs/DlgUploadRSForm';
|
2024-08-01 00:36:06 +03:00
|
|
|
import {
|
|
|
|
AccessPolicy,
|
2024-08-05 23:53:41 +03:00
|
|
|
ILibraryItemEditor,
|
2024-08-01 00:36:06 +03:00
|
|
|
ILibraryUpdateData,
|
|
|
|
IVersionData,
|
|
|
|
LibraryItemID,
|
|
|
|
LocationHead,
|
|
|
|
VersionID
|
|
|
|
} from '@/models/library';
|
2024-07-21 15:19:57 +03:00
|
|
|
import { ICstSubstituteData } from '@/models/oss';
|
2024-02-04 23:45:49 +03:00
|
|
|
import {
|
2024-03-18 16:22:27 +03:00
|
|
|
ConstituentaID,
|
2024-02-04 23:45:49 +03:00
|
|
|
CstType,
|
|
|
|
IConstituenta,
|
|
|
|
IConstituentaMeta,
|
|
|
|
ICstCreateData,
|
|
|
|
ICstMovetoData,
|
|
|
|
ICstRenameData,
|
|
|
|
ICstUpdateData,
|
2024-03-23 21:42:50 +03:00
|
|
|
IInlineSynthesisData,
|
2024-02-04 23:45:49 +03:00
|
|
|
IRSForm,
|
2024-05-26 14:54:02 +03:00
|
|
|
ITargetCst,
|
2024-02-04 23:45:49 +03:00
|
|
|
TermForm
|
|
|
|
} from '@/models/rsform';
|
|
|
|
import { generateAlias } from '@/models/rsformAPI';
|
2024-05-27 20:42:34 +03:00
|
|
|
import { UserID, UserLevel } from '@/models/user';
|
2024-02-04 23:45:49 +03:00
|
|
|
import { EXTEOR_TRS_FILE } from '@/utils/constants';
|
2024-06-06 23:11:53 +03:00
|
|
|
import { information, prompts } from '@/utils/labels';
|
2024-05-17 11:43:42 +03:00
|
|
|
import { promptUnsaved } from '@/utils/utils';
|
2024-02-04 23:45:49 +03:00
|
|
|
|
2024-08-01 21:16:40 +03:00
|
|
|
import { RSTabID } from './RSTabs';
|
|
|
|
|
2024-08-05 23:53:41 +03:00
|
|
|
export interface IRSEditContext extends ILibraryItemEditor {
|
2024-02-04 23:45:49 +03:00
|
|
|
schema?: IRSForm;
|
2024-04-03 15:51:57 +03:00
|
|
|
selected: ConstituentaID[];
|
|
|
|
|
2024-02-04 23:45:49 +03:00
|
|
|
isMutable: boolean;
|
2024-03-08 18:39:08 +03:00
|
|
|
isContentEditable: boolean;
|
2024-03-08 19:37:36 +03:00
|
|
|
isProcessing: boolean;
|
2024-08-05 23:53:41 +03:00
|
|
|
isAttachedToOSS: boolean;
|
2024-03-15 12:34:41 +03:00
|
|
|
canProduceStructure: boolean;
|
2024-08-01 11:56:21 +03:00
|
|
|
canDeleteSelected: boolean;
|
2024-04-03 15:51:57 +03:00
|
|
|
|
2024-08-01 00:36:06 +03:00
|
|
|
updateSchema: (data: ILibraryUpdateData) => void;
|
|
|
|
|
2024-05-27 20:42:34 +03:00
|
|
|
setOwner: (newOwner: UserID) => void;
|
2024-06-02 23:41:46 +03:00
|
|
|
setAccessPolicy: (newPolicy: AccessPolicy) => void;
|
2024-05-27 20:42:34 +03:00
|
|
|
promptEditors: () => void;
|
2024-06-02 23:41:46 +03:00
|
|
|
promptLocation: () => void;
|
2024-05-27 20:42:34 +03:00
|
|
|
|
2024-04-04 20:03:41 +03:00
|
|
|
setSelected: React.Dispatch<React.SetStateAction<ConstituentaID[]>>;
|
2024-04-03 15:51:57 +03:00
|
|
|
select: (target: ConstituentaID) => void;
|
|
|
|
deselect: (target: ConstituentaID) => void;
|
|
|
|
toggleSelect: (target: ConstituentaID) => void;
|
|
|
|
deselectAll: () => void;
|
2024-03-08 18:39:08 +03:00
|
|
|
|
2024-08-01 00:36:06 +03:00
|
|
|
viewOSS: (target: LibraryItemID, newTab?: boolean) => void;
|
2024-05-27 20:42:34 +03:00
|
|
|
viewVersion: (version?: VersionID, newTab?: boolean) => void;
|
2024-08-01 21:16:40 +03:00
|
|
|
viewPredecessor: (target: ConstituentaID) => void;
|
2024-04-03 15:51:57 +03:00
|
|
|
createVersion: () => void;
|
2024-05-18 19:22:26 +03:00
|
|
|
restoreVersion: () => void;
|
2024-08-01 11:56:21 +03:00
|
|
|
promptEditVersions: () => void;
|
2024-02-04 23:45:49 +03:00
|
|
|
|
|
|
|
moveUp: () => void;
|
|
|
|
moveDown: () => void;
|
|
|
|
createCst: (type: CstType | undefined, skipDialog: boolean, definition?: string) => void;
|
|
|
|
renameCst: () => void;
|
|
|
|
cloneCst: () => void;
|
2024-08-01 11:56:21 +03:00
|
|
|
promptDeleteCst: () => void;
|
2024-02-04 23:45:49 +03:00
|
|
|
editTermForms: () => void;
|
|
|
|
|
|
|
|
promptTemplate: () => void;
|
|
|
|
promptClone: () => void;
|
|
|
|
promptUpload: () => void;
|
|
|
|
share: () => void;
|
|
|
|
download: () => void;
|
2024-04-03 15:51:57 +03:00
|
|
|
|
2024-02-04 23:45:49 +03:00
|
|
|
reindex: () => void;
|
2024-04-24 10:27:17 +03:00
|
|
|
reorder: () => void;
|
2024-03-15 12:34:41 +03:00
|
|
|
produceStructure: () => void;
|
2024-03-18 16:22:27 +03:00
|
|
|
inlineSynthesis: () => void;
|
2024-03-01 18:19:33 +03:00
|
|
|
substitute: () => void;
|
2024-11-20 00:33:25 +03:00
|
|
|
|
|
|
|
showTypeGraph: () => void;
|
2024-02-04 23:45:49 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
const RSEditContext = createContext<IRSEditContext | null>(null);
|
|
|
|
export const useRSEdit = () => {
|
|
|
|
const context = useContext(RSEditContext);
|
|
|
|
if (context === null) {
|
|
|
|
throw new Error('useRSEdit has to be used within <RSEditState.Provider>');
|
|
|
|
}
|
|
|
|
return context;
|
|
|
|
};
|
|
|
|
|
|
|
|
interface RSEditStateProps {
|
2024-03-18 16:22:27 +03:00
|
|
|
selected: ConstituentaID[];
|
2024-02-04 23:45:49 +03:00
|
|
|
isModified: boolean;
|
2024-03-18 16:22:27 +03:00
|
|
|
setSelected: React.Dispatch<React.SetStateAction<ConstituentaID[]>>;
|
2024-02-04 23:45:49 +03:00
|
|
|
activeCst?: IConstituenta;
|
|
|
|
|
|
|
|
onCreateCst?: (newCst: IConstituentaMeta) => void;
|
2024-03-18 16:22:27 +03:00
|
|
|
onDeleteCst?: (newActive?: ConstituentaID) => void;
|
2024-02-04 23:45:49 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
export const RSEditState = ({
|
|
|
|
selected,
|
|
|
|
setSelected,
|
|
|
|
activeCst,
|
|
|
|
isModified,
|
|
|
|
onCreateCst,
|
|
|
|
onDeleteCst,
|
|
|
|
children
|
2024-09-19 17:49:25 +03:00
|
|
|
}: React.PropsWithChildren<RSEditStateProps>) => {
|
2024-03-08 18:39:08 +03:00
|
|
|
const router = useConceptNavigation();
|
2024-02-04 23:45:49 +03:00
|
|
|
const { user } = useAuth();
|
2024-04-27 15:19:20 +03:00
|
|
|
const { adminMode } = useConceptOptions();
|
2024-05-27 20:42:34 +03:00
|
|
|
const { accessLevel, setAccessLevel } = useAccessMode();
|
2024-02-04 23:45:49 +03:00
|
|
|
const model = useRSForm();
|
|
|
|
|
2024-06-02 23:41:46 +03:00
|
|
|
const isMutable = useMemo(
|
|
|
|
() => accessLevel > UserLevel.READER && !model.schema?.read_only,
|
|
|
|
[accessLevel, model.schema?.read_only]
|
|
|
|
);
|
2024-03-08 18:39:08 +03:00
|
|
|
const isContentEditable = useMemo(() => isMutable && !model.isArchive, [isMutable, model.isArchive]);
|
2024-08-01 11:56:21 +03:00
|
|
|
const canDeleteSelected = useMemo(
|
2024-11-19 21:56:26 +03:00
|
|
|
() => selected.length > 0 && selected.every(id => !model.schema?.cstByID.get(id)?.is_inherited),
|
|
|
|
[selected, model.schema]
|
2024-08-01 11:56:21 +03:00
|
|
|
);
|
2024-08-05 23:53:41 +03:00
|
|
|
const isAttachedToOSS = useMemo(
|
|
|
|
() =>
|
|
|
|
!!model.schema &&
|
|
|
|
model.schema.oss.length > 0 &&
|
|
|
|
(model.schema.stats.count_inherited > 0 || model.schema.items.length === 0),
|
|
|
|
[model.schema]
|
|
|
|
);
|
2024-03-08 18:39:08 +03:00
|
|
|
|
2024-02-04 23:45:49 +03:00
|
|
|
const [showUpload, setShowUpload] = useState(false);
|
|
|
|
const [showClone, setShowClone] = useState(false);
|
|
|
|
const [showDeleteCst, setShowDeleteCst] = useState(false);
|
2024-05-27 20:42:34 +03:00
|
|
|
const [showEditEditors, setShowEditEditors] = useState(false);
|
2024-06-02 23:41:46 +03:00
|
|
|
const [showEditLocation, setShowEditLocation] = useState(false);
|
2024-03-01 18:19:33 +03:00
|
|
|
const [showEditTerm, setShowEditTerm] = useState(false);
|
|
|
|
const [showSubstitute, setShowSubstitute] = useState(false);
|
2024-03-08 18:39:08 +03:00
|
|
|
const [showCreateVersion, setShowCreateVersion] = useState(false);
|
|
|
|
const [showEditVersions, setShowEditVersions] = useState(false);
|
2024-03-18 16:22:27 +03:00
|
|
|
const [showInlineSynthesis, setShowInlineSynthesis] = useState(false);
|
2024-11-20 00:33:25 +03:00
|
|
|
const [showTypeGraph, setShowTypeGraph] = useState(false);
|
2024-02-04 23:45:49 +03:00
|
|
|
|
|
|
|
const [createInitialData, setCreateInitialData] = useState<ICstCreateData>();
|
|
|
|
const [showCreateCst, setShowCreateCst] = useState(false);
|
|
|
|
|
|
|
|
const [renameInitialData, setRenameInitialData] = useState<ICstRenameData>();
|
|
|
|
const [showRenameCst, setShowRenameCst] = useState(false);
|
|
|
|
|
2024-03-18 16:22:27 +03:00
|
|
|
const [insertCstID, setInsertCstID] = useState<ConstituentaID | undefined>(undefined);
|
2024-02-04 23:45:49 +03:00
|
|
|
const [showTemplates, setShowTemplates] = useState(false);
|
|
|
|
|
2024-11-20 00:33:25 +03:00
|
|
|
const typeInfo = useMemo(
|
|
|
|
() =>
|
|
|
|
model.schema
|
|
|
|
? model.schema.items.map(item => ({
|
|
|
|
alias: item.alias,
|
|
|
|
result: item.parse.typification,
|
|
|
|
args: item.parse.args
|
|
|
|
}))
|
|
|
|
: [],
|
|
|
|
[model.schema]
|
|
|
|
);
|
|
|
|
|
2024-02-04 23:45:49 +03:00
|
|
|
useLayoutEffect(
|
|
|
|
() =>
|
2024-05-27 20:42:34 +03:00
|
|
|
setAccessLevel(prev => {
|
|
|
|
if (
|
|
|
|
prev === UserLevel.EDITOR &&
|
|
|
|
(model.isOwned || user?.is_staff || (user && model.schema?.editors.includes(user.id)))
|
|
|
|
) {
|
|
|
|
return UserLevel.EDITOR;
|
|
|
|
} else if (user?.is_staff && (prev === UserLevel.ADMIN || adminMode)) {
|
|
|
|
return UserLevel.ADMIN;
|
2024-02-04 23:45:49 +03:00
|
|
|
} else if (model.isOwned) {
|
2024-05-27 20:42:34 +03:00
|
|
|
return UserLevel.OWNER;
|
|
|
|
} else if (user?.id && model.schema?.editors.includes(user?.id)) {
|
|
|
|
return UserLevel.EDITOR;
|
2024-02-04 23:45:49 +03:00
|
|
|
} else {
|
2024-05-27 20:42:34 +03:00
|
|
|
return UserLevel.READER;
|
2024-02-04 23:45:49 +03:00
|
|
|
}
|
|
|
|
}),
|
2024-05-27 20:42:34 +03:00
|
|
|
[model.schema, setAccessLevel, model.isOwned, user, adminMode]
|
2024-02-04 23:45:49 +03:00
|
|
|
);
|
|
|
|
|
2024-08-01 00:36:06 +03:00
|
|
|
const updateSchema = useCallback(
|
|
|
|
(data: ILibraryUpdateData) => model.update(data, () => toast.success(information.changesSaved)),
|
|
|
|
[model]
|
|
|
|
);
|
|
|
|
|
2024-03-08 18:39:08 +03:00
|
|
|
const viewVersion = useCallback(
|
2024-06-04 23:00:22 +03:00
|
|
|
(version?: VersionID, newTab?: boolean) => router.push(urls.schema(model.itemID, version), newTab),
|
2024-03-08 18:39:08 +03:00
|
|
|
[router, model]
|
|
|
|
);
|
|
|
|
|
2024-08-01 21:16:40 +03:00
|
|
|
const viewPredecessor = useCallback(
|
|
|
|
(target: ConstituentaID) =>
|
|
|
|
model.findPredecessor({ target: target }, reference =>
|
|
|
|
router.push(
|
|
|
|
urls.schema_props({
|
|
|
|
id: reference.schema,
|
|
|
|
active: reference.id,
|
|
|
|
tab: RSTabID.CST_EDIT
|
|
|
|
})
|
|
|
|
)
|
|
|
|
),
|
|
|
|
[router, model]
|
|
|
|
);
|
|
|
|
|
2024-08-01 00:36:06 +03:00
|
|
|
const viewOSS = useCallback(
|
|
|
|
(target: LibraryItemID, newTab?: boolean) => router.push(urls.oss(target), newTab),
|
|
|
|
[router]
|
|
|
|
);
|
|
|
|
|
2024-05-18 19:22:26 +03:00
|
|
|
const createVersion = useCallback(() => {
|
|
|
|
if (isModified && !promptUnsaved()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
setShowCreateVersion(true);
|
|
|
|
}, [isModified]);
|
|
|
|
|
|
|
|
const restoreVersion = useCallback(() => {
|
2024-06-06 23:11:53 +03:00
|
|
|
if (!model.versionID || !window.confirm(prompts.restoreArchive)) {
|
2024-05-18 19:22:26 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
model.versionRestore(model.versionID, () => {
|
2024-06-06 23:11:53 +03:00
|
|
|
toast.success(information.versionRestored);
|
2024-05-18 19:22:26 +03:00
|
|
|
viewVersion(undefined);
|
|
|
|
});
|
|
|
|
}, [model, viewVersion]);
|
|
|
|
|
2024-06-02 23:41:46 +03:00
|
|
|
function calculateCloneLocation(): string {
|
|
|
|
if (!model.schema) {
|
|
|
|
return LocationHead.USER;
|
|
|
|
}
|
|
|
|
const location = model.schema.location;
|
|
|
|
const head = model.schema.location.substring(0, 2) as LocationHead;
|
|
|
|
if (head === LocationHead.LIBRARY) {
|
|
|
|
return user?.is_staff ? location : LocationHead.USER;
|
|
|
|
}
|
|
|
|
if (model.schema.owner === user?.id) {
|
|
|
|
return location;
|
|
|
|
}
|
|
|
|
return head === LocationHead.USER ? LocationHead.USER : location;
|
|
|
|
}
|
|
|
|
|
2024-02-04 23:45:49 +03:00
|
|
|
const handleCreateCst = useCallback(
|
|
|
|
(data: ICstCreateData) => {
|
|
|
|
if (!model.schema) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
data.alias = data.alias || generateAlias(data.cst_type, model.schema);
|
|
|
|
model.cstCreate(data, newCst => {
|
2024-06-06 23:11:53 +03:00
|
|
|
toast.success(information.newConstituent(newCst.alias));
|
2024-02-04 23:45:49 +03:00
|
|
|
setSelected([newCst.id]);
|
|
|
|
if (onCreateCst) onCreateCst(newCst);
|
|
|
|
});
|
|
|
|
},
|
|
|
|
[model, setSelected, onCreateCst]
|
|
|
|
);
|
|
|
|
|
|
|
|
const handleRenameCst = useCallback(
|
|
|
|
(data: ICstRenameData) => {
|
2024-06-06 23:11:53 +03:00
|
|
|
const oldAlias = renameInitialData!.alias;
|
|
|
|
model.cstRename(data, () => toast.success(information.renameComplete(oldAlias, data.alias)));
|
2024-02-04 23:45:49 +03:00
|
|
|
},
|
|
|
|
[model, renameInitialData]
|
|
|
|
);
|
|
|
|
|
2024-03-01 18:19:33 +03:00
|
|
|
const handleSubstituteCst = useCallback(
|
|
|
|
(data: ICstSubstituteData) => {
|
2024-09-02 12:11:18 +03:00
|
|
|
model.cstSubstitute(data, () => {
|
|
|
|
setSelected(prev => prev.filter(id => !data.substitutions.find(sub => sub.original === id)));
|
|
|
|
toast.success(information.substituteSingle);
|
|
|
|
});
|
2024-03-01 18:19:33 +03:00
|
|
|
},
|
2024-09-02 12:11:18 +03:00
|
|
|
[model, setSelected]
|
2024-03-01 18:19:33 +03:00
|
|
|
);
|
|
|
|
|
2024-02-04 23:45:49 +03:00
|
|
|
const handleDeleteCst = useCallback(
|
2024-03-18 16:22:27 +03:00
|
|
|
(deleted: ConstituentaID[]) => {
|
2024-02-04 23:45:49 +03:00
|
|
|
if (!model.schema) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const data = {
|
|
|
|
items: deleted
|
|
|
|
};
|
|
|
|
|
2024-04-07 15:38:24 +03:00
|
|
|
const deletedNames = deleted.map(id => model.schema!.cstByID.get(id)!.alias).join(', ');
|
2024-02-04 23:45:49 +03:00
|
|
|
const isEmpty = deleted.length === model.schema.items.length;
|
|
|
|
const nextActive = isEmpty ? undefined : getNextActiveOnDelete(activeCst?.id, model.schema.items, deleted);
|
|
|
|
|
|
|
|
model.cstDelete(data, () => {
|
2024-06-06 23:11:53 +03:00
|
|
|
toast.success(information.constituentsDestroyed(deletedNames));
|
2024-02-04 23:45:49 +03:00
|
|
|
setSelected(nextActive ? [nextActive] : []);
|
2024-12-04 22:53:01 +03:00
|
|
|
onDeleteCst?.(nextActive);
|
2024-02-04 23:45:49 +03:00
|
|
|
});
|
|
|
|
},
|
|
|
|
[model, activeCst, onDeleteCst, setSelected]
|
|
|
|
);
|
|
|
|
|
|
|
|
const handleSaveWordforms = useCallback(
|
|
|
|
(forms: TermForm[]) => {
|
|
|
|
if (!activeCst) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const data: ICstUpdateData = {
|
2024-08-10 11:41:52 +03:00
|
|
|
target: activeCst.id,
|
|
|
|
item_data: { term_forms: forms }
|
2024-02-04 23:45:49 +03:00
|
|
|
};
|
2024-06-06 23:11:53 +03:00
|
|
|
model.cstUpdate(data, () => toast.success(information.changesSaved));
|
2024-02-04 23:45:49 +03:00
|
|
|
},
|
|
|
|
[model, activeCst]
|
|
|
|
);
|
|
|
|
|
2024-03-08 18:39:08 +03:00
|
|
|
const handleCreateVersion = useCallback(
|
|
|
|
(data: IVersionData) => {
|
|
|
|
if (!model.schema) {
|
|
|
|
return;
|
|
|
|
}
|
2024-08-24 12:29:38 +03:00
|
|
|
model.versionCreate(data, () => {
|
2024-06-06 23:11:53 +03:00
|
|
|
toast.success(information.newVersion(data.version));
|
2024-03-08 18:39:08 +03:00
|
|
|
});
|
|
|
|
},
|
2024-10-23 16:21:08 +03:00
|
|
|
[model]
|
2024-03-08 18:39:08 +03:00
|
|
|
);
|
|
|
|
|
|
|
|
const handleDeleteVersion = useCallback(
|
2024-05-27 20:42:34 +03:00
|
|
|
(versionID: VersionID) => {
|
2024-03-08 18:39:08 +03:00
|
|
|
if (!model.schema) {
|
|
|
|
return;
|
|
|
|
}
|
2024-05-18 19:22:26 +03:00
|
|
|
model.versionDelete(versionID, () => {
|
2024-06-06 23:11:53 +03:00
|
|
|
toast.success(information.versionDestroyed);
|
2024-05-18 19:22:26 +03:00
|
|
|
if (String(versionID) === model.versionID) {
|
|
|
|
viewVersion(undefined);
|
|
|
|
}
|
|
|
|
});
|
2024-03-08 18:39:08 +03:00
|
|
|
},
|
2024-05-18 19:22:26 +03:00
|
|
|
[model, viewVersion]
|
2024-03-08 18:39:08 +03:00
|
|
|
);
|
|
|
|
|
|
|
|
const handleUpdateVersion = useCallback(
|
2024-05-27 20:42:34 +03:00
|
|
|
(versionID: VersionID, data: IVersionData) => {
|
2024-03-08 18:39:08 +03:00
|
|
|
if (!model.schema) {
|
|
|
|
return;
|
|
|
|
}
|
2024-06-06 23:11:53 +03:00
|
|
|
model.versionUpdate(versionID, data, () => toast.success(information.changesSaved));
|
2024-03-08 18:39:08 +03:00
|
|
|
},
|
|
|
|
[model]
|
|
|
|
);
|
|
|
|
|
2024-06-02 23:41:46 +03:00
|
|
|
const handleSetLocation = useCallback(
|
|
|
|
(newLocation: string) => {
|
|
|
|
if (!model.schema) {
|
|
|
|
return;
|
|
|
|
}
|
2024-06-06 23:11:53 +03:00
|
|
|
model.setLocation(newLocation, () => toast.success(information.moveComplete));
|
2024-06-02 23:41:46 +03:00
|
|
|
},
|
|
|
|
[model]
|
|
|
|
);
|
|
|
|
|
2024-03-23 21:42:50 +03:00
|
|
|
const handleInlineSynthesis = useCallback(
|
|
|
|
(data: IInlineSynthesisData) => {
|
|
|
|
if (!model.schema) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const oldCount = model.schema.items.length;
|
2024-04-04 21:16:10 +03:00
|
|
|
model.inlineSynthesis(data, newSchema => {
|
|
|
|
setSelected([]);
|
2024-08-06 14:39:00 +03:00
|
|
|
toast.success(information.addedConstituents(newSchema.items.length - oldCount));
|
2024-04-04 21:16:10 +03:00
|
|
|
});
|
2024-03-23 21:42:50 +03:00
|
|
|
},
|
2024-04-04 21:16:10 +03:00
|
|
|
[model, setSelected]
|
2024-03-23 21:42:50 +03:00
|
|
|
);
|
|
|
|
|
2024-02-04 23:45:49 +03:00
|
|
|
const moveUp = useCallback(() => {
|
|
|
|
if (!model.schema?.items || selected.length === 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const currentIndex = model.schema.items.reduce((prev, cst, index) => {
|
|
|
|
if (!selected.includes(cst.id)) {
|
|
|
|
return prev;
|
|
|
|
} else if (prev === -1) {
|
|
|
|
return index;
|
|
|
|
}
|
|
|
|
return Math.min(prev, index);
|
|
|
|
}, -1);
|
2024-09-11 20:06:58 +03:00
|
|
|
const target = Math.max(0, currentIndex - 1);
|
|
|
|
const data: ICstMovetoData = {
|
2024-02-04 23:45:49 +03:00
|
|
|
items: selected,
|
|
|
|
move_to: target
|
|
|
|
};
|
|
|
|
model.cstMoveTo(data);
|
|
|
|
}, [model, selected]);
|
|
|
|
|
|
|
|
const moveDown = useCallback(() => {
|
|
|
|
if (!model.schema?.items || selected.length === 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
let count = 0;
|
|
|
|
const currentIndex = model.schema.items.reduce((prev, cst, index) => {
|
|
|
|
if (!selected.includes(cst.id)) {
|
|
|
|
return prev;
|
|
|
|
} else {
|
|
|
|
count += 1;
|
|
|
|
if (prev === -1) {
|
|
|
|
return index;
|
|
|
|
}
|
|
|
|
return Math.max(prev, index);
|
|
|
|
}
|
|
|
|
}, -1);
|
2024-09-11 20:06:58 +03:00
|
|
|
const target = Math.min(model.schema.items.length - 1, currentIndex - count + 2);
|
2024-02-04 23:45:49 +03:00
|
|
|
const data: ICstMovetoData = {
|
|
|
|
items: selected,
|
|
|
|
move_to: target
|
|
|
|
};
|
|
|
|
model.cstMoveTo(data);
|
|
|
|
}, [model, selected]);
|
|
|
|
|
|
|
|
const createCst = useCallback(
|
|
|
|
(type: CstType | undefined, skipDialog: boolean, definition?: string) => {
|
2024-08-29 12:42:26 +03:00
|
|
|
if (!model.schema) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const targetType = type ?? activeCst?.cst_type ?? CstType.BASE;
|
2024-02-04 23:45:49 +03:00
|
|
|
const data: ICstCreateData = {
|
|
|
|
insert_after: activeCst?.id ?? null,
|
2024-08-29 12:42:26 +03:00
|
|
|
cst_type: targetType,
|
|
|
|
alias: generateAlias(targetType, model.schema),
|
2024-02-04 23:45:49 +03:00
|
|
|
term_raw: '',
|
|
|
|
definition_formal: definition ?? '',
|
|
|
|
definition_raw: '',
|
|
|
|
convention: '',
|
|
|
|
term_forms: []
|
|
|
|
};
|
|
|
|
if (skipDialog) {
|
|
|
|
handleCreateCst(data);
|
|
|
|
} else {
|
|
|
|
setCreateInitialData(data);
|
|
|
|
setShowCreateCst(true);
|
|
|
|
}
|
|
|
|
},
|
2024-08-29 12:42:26 +03:00
|
|
|
[activeCst, handleCreateCst, model.schema]
|
2024-02-04 23:45:49 +03:00
|
|
|
);
|
|
|
|
|
|
|
|
const cloneCst = useCallback(() => {
|
2024-08-29 12:42:26 +03:00
|
|
|
if (!activeCst || !model.schema) {
|
2024-02-04 23:45:49 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
const data: ICstCreateData = {
|
|
|
|
insert_after: activeCst.id,
|
|
|
|
cst_type: activeCst.cst_type,
|
2024-08-29 12:42:26 +03:00
|
|
|
alias: generateAlias(activeCst.cst_type, model.schema),
|
2024-02-04 23:45:49 +03:00
|
|
|
term_raw: activeCst.term_raw,
|
|
|
|
definition_formal: activeCst.definition_formal,
|
|
|
|
definition_raw: activeCst.definition_raw,
|
|
|
|
convention: activeCst.convention,
|
|
|
|
term_forms: activeCst.term_forms
|
|
|
|
};
|
|
|
|
handleCreateCst(data);
|
2024-08-29 12:42:26 +03:00
|
|
|
}, [activeCst, handleCreateCst, model.schema]);
|
2024-02-04 23:45:49 +03:00
|
|
|
|
|
|
|
const renameCst = useCallback(() => {
|
|
|
|
if (!activeCst) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const data: ICstRenameData = {
|
2024-03-21 21:05:12 +03:00
|
|
|
target: activeCst.id,
|
2024-02-04 23:45:49 +03:00
|
|
|
alias: activeCst.alias,
|
|
|
|
cst_type: activeCst.cst_type
|
|
|
|
};
|
|
|
|
setRenameInitialData(data);
|
|
|
|
setShowRenameCst(true);
|
|
|
|
}, [activeCst]);
|
|
|
|
|
2024-03-01 18:19:33 +03:00
|
|
|
const substitute = useCallback(() => {
|
2024-05-17 11:43:42 +03:00
|
|
|
if (isModified && !promptUnsaved()) {
|
|
|
|
return;
|
|
|
|
}
|
2024-03-01 18:19:33 +03:00
|
|
|
setShowSubstitute(true);
|
2024-05-17 11:43:42 +03:00
|
|
|
}, [isModified]);
|
|
|
|
|
|
|
|
const inlineSynthesis = useCallback(() => {
|
|
|
|
if (isModified && !promptUnsaved()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
setShowInlineSynthesis(true);
|
|
|
|
}, [isModified]);
|
2024-03-01 18:19:33 +03:00
|
|
|
|
2024-02-04 23:45:49 +03:00
|
|
|
const editTermForms = useCallback(() => {
|
|
|
|
if (!activeCst) {
|
|
|
|
return;
|
|
|
|
}
|
2024-05-17 11:43:42 +03:00
|
|
|
if (isModified && !promptUnsaved()) {
|
|
|
|
return;
|
2024-02-04 23:45:49 +03:00
|
|
|
}
|
|
|
|
setShowEditTerm(true);
|
|
|
|
}, [isModified, activeCst]);
|
|
|
|
|
2024-06-06 23:11:53 +03:00
|
|
|
const reindex = useCallback(() => model.resetAliases(() => toast.success(information.reindexComplete)), [model]);
|
|
|
|
const reorder = useCallback(() => model.restoreOrder(() => toast.success(information.reorderComplete)), [model]);
|
2024-02-04 23:45:49 +03:00
|
|
|
|
2024-03-15 12:34:41 +03:00
|
|
|
const canProduceStructure = useMemo(() => {
|
|
|
|
return (
|
|
|
|
!!activeCst &&
|
|
|
|
!!activeCst.parse.typification &&
|
|
|
|
activeCst.cst_type !== CstType.BASE &&
|
|
|
|
activeCst.cst_type !== CstType.CONSTANT
|
|
|
|
);
|
|
|
|
}, [activeCst]);
|
|
|
|
|
|
|
|
const produceStructure = useCallback(() => {
|
|
|
|
if (!activeCst) {
|
|
|
|
return;
|
|
|
|
}
|
2024-05-17 11:43:42 +03:00
|
|
|
if (isModified && !promptUnsaved()) {
|
|
|
|
return;
|
|
|
|
}
|
2024-05-26 14:54:02 +03:00
|
|
|
const data: ITargetCst = {
|
2024-03-21 21:05:12 +03:00
|
|
|
target: activeCst.id
|
2024-03-15 12:34:41 +03:00
|
|
|
};
|
|
|
|
model.produceStructure(data, cstList => {
|
2024-06-06 23:11:53 +03:00
|
|
|
toast.success(information.addedConstituents(cstList.length));
|
2024-03-15 12:34:41 +03:00
|
|
|
if (cstList.length !== 0) {
|
|
|
|
setSelected(cstList);
|
|
|
|
}
|
|
|
|
});
|
2024-05-17 11:43:42 +03:00
|
|
|
}, [activeCst, setSelected, model, isModified]);
|
2024-03-15 12:34:41 +03:00
|
|
|
|
2024-02-04 23:45:49 +03:00
|
|
|
const promptTemplate = useCallback(() => {
|
2024-05-17 11:43:42 +03:00
|
|
|
if (isModified && !promptUnsaved()) {
|
|
|
|
return;
|
|
|
|
}
|
2024-02-04 23:45:49 +03:00
|
|
|
setInsertCstID(activeCst?.id);
|
|
|
|
setShowTemplates(true);
|
2024-05-17 11:43:42 +03:00
|
|
|
}, [activeCst, isModified]);
|
2024-02-04 23:45:49 +03:00
|
|
|
|
|
|
|
const promptClone = useCallback(() => {
|
2024-05-17 11:43:42 +03:00
|
|
|
if (isModified && !promptUnsaved()) {
|
|
|
|
return;
|
2024-02-04 23:45:49 +03:00
|
|
|
}
|
|
|
|
setShowClone(true);
|
|
|
|
}, [isModified]);
|
|
|
|
|
2024-05-27 20:42:34 +03:00
|
|
|
const promptEditors = useCallback(() => {
|
|
|
|
setShowEditEditors(true);
|
|
|
|
}, []);
|
|
|
|
|
2024-06-02 23:41:46 +03:00
|
|
|
const promptLocation = useCallback(() => {
|
|
|
|
setShowEditLocation(true);
|
|
|
|
}, []);
|
|
|
|
|
2024-02-04 23:45:49 +03:00
|
|
|
const download = useCallback(() => {
|
2024-05-17 11:43:42 +03:00
|
|
|
if (isModified && !promptUnsaved()) {
|
|
|
|
return;
|
2024-02-04 23:45:49 +03:00
|
|
|
}
|
|
|
|
const fileName = (model.schema?.alias ?? 'Schema') + EXTEOR_TRS_FILE;
|
|
|
|
model.download((data: Blob) => {
|
|
|
|
try {
|
|
|
|
fileDownload(data, fileName);
|
|
|
|
} catch (error) {
|
|
|
|
console.error(error);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}, [model, isModified]);
|
|
|
|
|
|
|
|
const share = useCallback(() => {
|
2024-03-08 18:39:08 +03:00
|
|
|
const currentRef = window.location.href;
|
|
|
|
const url = currentRef.includes('?') ? currentRef + '&share' : currentRef + '?share';
|
2024-02-04 23:45:49 +03:00
|
|
|
navigator.clipboard
|
|
|
|
.writeText(url)
|
2024-06-06 23:11:53 +03:00
|
|
|
.then(() => toast.success(information.linkReady))
|
2024-02-04 23:45:49 +03:00
|
|
|
.catch(console.error);
|
|
|
|
}, []);
|
|
|
|
|
2024-05-27 20:42:34 +03:00
|
|
|
const setOwner = useCallback(
|
|
|
|
(newOwner: UserID) => {
|
2024-06-06 23:11:53 +03:00
|
|
|
model.setOwner(newOwner, () => toast.success(information.changesSaved));
|
2024-05-27 20:42:34 +03:00
|
|
|
},
|
|
|
|
[model]
|
|
|
|
);
|
|
|
|
|
2024-06-02 23:41:46 +03:00
|
|
|
const setAccessPolicy = useCallback(
|
|
|
|
(newPolicy: AccessPolicy) => {
|
2024-06-06 23:11:53 +03:00
|
|
|
model.setAccessPolicy(newPolicy, () => toast.success(information.changesSaved));
|
2024-06-02 23:41:46 +03:00
|
|
|
},
|
|
|
|
[model]
|
|
|
|
);
|
|
|
|
|
2024-05-27 20:42:34 +03:00
|
|
|
const setEditors = useCallback(
|
|
|
|
(newEditors: UserID[]) => {
|
2024-06-06 23:11:53 +03:00
|
|
|
model.setEditors(newEditors, () => toast.success(information.changesSaved));
|
2024-05-27 20:42:34 +03:00
|
|
|
},
|
|
|
|
[model]
|
|
|
|
);
|
|
|
|
|
2024-02-04 23:45:49 +03:00
|
|
|
return (
|
|
|
|
<RSEditContext.Provider
|
|
|
|
value={{
|
|
|
|
schema: model.schema,
|
2024-08-01 00:36:06 +03:00
|
|
|
updateSchema,
|
2024-04-03 15:51:57 +03:00
|
|
|
selected,
|
2024-02-04 23:45:49 +03:00
|
|
|
isMutable,
|
2024-03-08 18:39:08 +03:00
|
|
|
isContentEditable,
|
2024-03-08 19:37:36 +03:00
|
|
|
isProcessing: model.processing,
|
2024-08-05 23:53:41 +03:00
|
|
|
isAttachedToOSS,
|
2024-03-15 12:34:41 +03:00
|
|
|
canProduceStructure,
|
2024-08-01 11:56:21 +03:00
|
|
|
canDeleteSelected,
|
2024-04-03 15:51:57 +03:00
|
|
|
|
2024-05-27 20:42:34 +03:00
|
|
|
setOwner,
|
2024-06-02 23:41:46 +03:00
|
|
|
setAccessPolicy,
|
2024-05-27 20:42:34 +03:00
|
|
|
promptEditors,
|
2024-06-02 23:41:46 +03:00
|
|
|
promptLocation,
|
2024-05-27 20:42:34 +03:00
|
|
|
|
2024-04-04 20:03:41 +03:00
|
|
|
setSelected: setSelected,
|
2024-04-03 15:51:57 +03:00
|
|
|
select: (target: ConstituentaID) => setSelected(prev => [...prev, target]),
|
|
|
|
deselect: (target: ConstituentaID) => setSelected(prev => prev.filter(id => id !== target)),
|
|
|
|
toggleSelect: (target: ConstituentaID) =>
|
|
|
|
setSelected(prev => (prev.includes(target) ? prev.filter(id => id !== target) : [...prev, target])),
|
|
|
|
deselectAll: () => setSelected([]),
|
2024-03-08 18:39:08 +03:00
|
|
|
|
2024-08-01 00:36:06 +03:00
|
|
|
viewOSS,
|
2024-03-08 18:39:08 +03:00
|
|
|
viewVersion,
|
2024-08-01 21:16:40 +03:00
|
|
|
viewPredecessor,
|
2024-05-18 19:22:26 +03:00
|
|
|
createVersion,
|
|
|
|
restoreVersion,
|
2024-08-01 11:56:21 +03:00
|
|
|
promptEditVersions: () => setShowEditVersions(true),
|
2024-02-04 23:45:49 +03:00
|
|
|
|
|
|
|
moveUp,
|
|
|
|
moveDown,
|
|
|
|
createCst,
|
|
|
|
cloneCst,
|
|
|
|
renameCst,
|
2024-08-01 11:56:21 +03:00
|
|
|
promptDeleteCst: () => setShowDeleteCst(true),
|
2024-02-04 23:45:49 +03:00
|
|
|
editTermForms,
|
|
|
|
|
|
|
|
promptTemplate,
|
|
|
|
promptClone,
|
|
|
|
promptUpload: () => setShowUpload(true),
|
|
|
|
download,
|
|
|
|
share,
|
2024-04-03 15:51:57 +03:00
|
|
|
|
2024-03-01 18:19:33 +03:00
|
|
|
reindex,
|
2024-04-24 10:27:17 +03:00
|
|
|
reorder,
|
2024-05-17 11:43:42 +03:00
|
|
|
inlineSynthesis,
|
2024-03-15 12:34:41 +03:00
|
|
|
produceStructure,
|
2024-11-20 00:33:25 +03:00
|
|
|
substitute,
|
|
|
|
|
|
|
|
showTypeGraph: () => setShowTypeGraph(true)
|
2024-02-04 23:45:49 +03:00
|
|
|
}}
|
|
|
|
>
|
|
|
|
{model.schema ? (
|
|
|
|
<AnimatePresence>
|
|
|
|
{showUpload ? <DlgUploadRSForm hideWindow={() => setShowUpload(false)} /> : null}
|
2024-04-04 14:34:25 +03:00
|
|
|
{showClone ? (
|
|
|
|
<DlgCloneLibraryItem
|
|
|
|
base={model.schema}
|
2024-06-02 23:41:46 +03:00
|
|
|
initialLocation={calculateCloneLocation()}
|
2024-04-04 14:34:25 +03:00
|
|
|
hideWindow={() => setShowClone(false)}
|
|
|
|
selected={selected}
|
|
|
|
totalCount={model.schema.items.length}
|
|
|
|
/>
|
|
|
|
) : null}
|
2024-02-04 23:45:49 +03:00
|
|
|
{showCreateCst ? (
|
|
|
|
<DlgCreateCst
|
|
|
|
hideWindow={() => setShowCreateCst(false)}
|
|
|
|
onCreate={handleCreateCst}
|
|
|
|
schema={model.schema}
|
|
|
|
initial={createInitialData}
|
|
|
|
/>
|
|
|
|
) : null}
|
2024-09-05 21:25:09 +03:00
|
|
|
{activeCst && showRenameCst && renameInitialData ? (
|
2024-02-04 23:45:49 +03:00
|
|
|
<DlgRenameCst
|
|
|
|
hideWindow={() => setShowRenameCst(false)}
|
|
|
|
onRename={handleRenameCst}
|
2024-09-05 21:25:09 +03:00
|
|
|
allowChangeType={!activeCst.is_inherited}
|
2024-02-04 23:45:49 +03:00
|
|
|
initial={renameInitialData}
|
|
|
|
/>
|
|
|
|
) : null}
|
2024-03-01 18:19:33 +03:00
|
|
|
{showSubstitute ? (
|
|
|
|
<DlgSubstituteCst
|
2024-07-30 16:00:09 +03:00
|
|
|
schema={model.schema}
|
2024-03-01 18:19:33 +03:00
|
|
|
hideWindow={() => setShowSubstitute(false)} // prettier: split lines
|
|
|
|
onSubstitute={handleSubstituteCst}
|
|
|
|
/>
|
|
|
|
) : null}
|
2024-02-04 23:45:49 +03:00
|
|
|
{showDeleteCst ? (
|
|
|
|
<DlgDeleteCst
|
|
|
|
schema={model.schema}
|
|
|
|
hideWindow={() => setShowDeleteCst(false)}
|
|
|
|
onDelete={handleDeleteCst}
|
|
|
|
selected={selected}
|
|
|
|
/>
|
|
|
|
) : null}
|
|
|
|
{showEditTerm && activeCst ? (
|
|
|
|
<DlgEditWordForms
|
|
|
|
hideWindow={() => setShowEditTerm(false)}
|
|
|
|
onSave={handleSaveWordforms}
|
|
|
|
target={activeCst}
|
|
|
|
/>
|
|
|
|
) : null}
|
|
|
|
{showTemplates ? (
|
|
|
|
<DlgConstituentaTemplate
|
|
|
|
schema={model.schema}
|
|
|
|
hideWindow={() => setShowTemplates(false)}
|
|
|
|
insertAfter={insertCstID}
|
|
|
|
onCreate={handleCreateCst}
|
|
|
|
/>
|
|
|
|
) : null}
|
2024-03-08 18:39:08 +03:00
|
|
|
{showCreateVersion ? (
|
|
|
|
<DlgCreateVersion
|
|
|
|
versions={model.schema.versions}
|
|
|
|
hideWindow={() => setShowCreateVersion(false)}
|
|
|
|
onCreate={handleCreateVersion}
|
2024-08-24 12:29:38 +03:00
|
|
|
selected={selected}
|
|
|
|
totalCount={model.schema.items.length}
|
2024-03-08 18:39:08 +03:00
|
|
|
/>
|
|
|
|
) : null}
|
|
|
|
{showEditVersions ? (
|
|
|
|
<DlgEditVersions
|
|
|
|
versions={model.schema.versions}
|
|
|
|
hideWindow={() => setShowEditVersions(false)}
|
|
|
|
onDelete={handleDeleteVersion}
|
|
|
|
onUpdate={handleUpdateVersion}
|
|
|
|
/>
|
|
|
|
) : null}
|
2024-05-27 20:42:34 +03:00
|
|
|
{showEditEditors ? (
|
|
|
|
<DlgEditEditors
|
|
|
|
hideWindow={() => setShowEditEditors(false)}
|
|
|
|
editors={model.schema.editors}
|
|
|
|
setEditors={setEditors}
|
|
|
|
/>
|
|
|
|
) : null}
|
2024-06-02 23:41:46 +03:00
|
|
|
{showEditLocation ? (
|
|
|
|
<DlgChangeLocation
|
|
|
|
hideWindow={() => setShowEditLocation(false)}
|
|
|
|
initial={model.schema.location}
|
|
|
|
onChangeLocation={handleSetLocation}
|
|
|
|
/>
|
|
|
|
) : null}
|
|
|
|
|
2024-03-18 16:22:27 +03:00
|
|
|
{showInlineSynthesis ? (
|
|
|
|
<DlgInlineSynthesis
|
|
|
|
receiver={model.schema}
|
|
|
|
hideWindow={() => setShowInlineSynthesis(false)}
|
2024-03-23 21:42:50 +03:00
|
|
|
onInlineSynthesis={handleInlineSynthesis}
|
2024-03-18 16:22:27 +03:00
|
|
|
/>
|
|
|
|
) : null}
|
2024-11-20 00:33:25 +03:00
|
|
|
|
|
|
|
{showTypeGraph ? <DlgShowTypeGraph items={typeInfo} hideWindow={() => setShowTypeGraph(false)} /> : null}
|
2024-02-04 23:45:49 +03:00
|
|
|
</AnimatePresence>
|
|
|
|
) : null}
|
|
|
|
|
2024-06-05 12:28:08 +03:00
|
|
|
{children}
|
2024-02-04 23:45:49 +03:00
|
|
|
</RSEditContext.Provider>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
// ====== Internals =========
|
|
|
|
function getNextActiveOnDelete(
|
2024-03-18 16:22:27 +03:00
|
|
|
activeID: ConstituentaID | undefined,
|
2024-02-04 23:45:49 +03:00
|
|
|
items: IConstituenta[],
|
2024-03-18 16:22:27 +03:00
|
|
|
deleted: ConstituentaID[]
|
|
|
|
): ConstituentaID | undefined {
|
2024-02-04 23:45:49 +03:00
|
|
|
if (items.length === deleted.length) {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
let activeIndex = items.findIndex(cst => cst.id === activeID);
|
|
|
|
if (activeIndex === -1) {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (activeIndex < items.length && deleted.find(id => id === items[activeIndex].id)) {
|
|
|
|
++activeIndex;
|
|
|
|
}
|
|
|
|
if (activeIndex >= items.length) {
|
|
|
|
activeIndex = items.length - 1;
|
|
|
|
while (activeIndex >= 0 && deleted.find(id => id === items[activeIndex].id)) {
|
|
|
|
--activeIndex;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return items[activeIndex].id;
|
|
|
|
}
|