|
@@ -1,174 +0,0 @@
|
|
|
-import { FC, useContext, useEffect, useRef, useState } from 'react';
|
|
|
-import { Theme, useTheme } from '@mui/material';
|
|
|
-import { EditorState, Compartment } from '@codemirror/state';
|
|
|
-import { EditorView, keymap, ViewUpdate } from '@codemirror/view';
|
|
|
-import { insertTab } from '@codemirror/commands';
|
|
|
-import { indentUnit } from '@codemirror/language';
|
|
|
-import { basicSetup } from 'codemirror';
|
|
|
-import { json, jsonParseLinter } from '@codemirror/lang-json';
|
|
|
-import { linter } from '@codemirror/lint';
|
|
|
-import { useTranslation } from 'react-i18next';
|
|
|
-import { rootContext } from '@/context';
|
|
|
-import DialogTemplate from '@/components/customDialog/DialogTemplate';
|
|
|
-import { DataService } from '@/http';
|
|
|
-import { DYNAMIC_FIELD } from '@/consts';
|
|
|
-import { makeStyles } from '@mui/styles';
|
|
|
-import { githubLight } from '@ddietr/codemirror-themes/github-light';
|
|
|
-import { githubDark } from '@ddietr/codemirror-themes/github-dark';
|
|
|
-import type { CollectionFullObject } from '@server/types';
|
|
|
-
|
|
|
-const useStyles = makeStyles((theme: Theme) => ({
|
|
|
- code: {
|
|
|
- border: `1px solid ${theme.palette.divider}`,
|
|
|
- overflow: 'auto',
|
|
|
- },
|
|
|
- tip: {
|
|
|
- fontSize: 14,
|
|
|
- marginBottom: 16,
|
|
|
- fontWeight: 'bold',
|
|
|
- color: theme.palette.warning.main,
|
|
|
- },
|
|
|
-}));
|
|
|
-
|
|
|
-type EditEntityDialogProps = {
|
|
|
- data: { [key: string]: any };
|
|
|
- collection: CollectionFullObject;
|
|
|
- cb?: (id: string) => void;
|
|
|
-};
|
|
|
-
|
|
|
-// json linter for cm
|
|
|
-const linterExtension = linter(jsonParseLinter());
|
|
|
-
|
|
|
-const EditEntityDialog: FC<EditEntityDialogProps> = props => {
|
|
|
- const theme = useTheme();
|
|
|
- const themeCompartment = new Compartment();
|
|
|
-
|
|
|
- // props
|
|
|
- const { data, collection } = props;
|
|
|
- // UI states
|
|
|
- const [disabled, setDisabled] = useState(true);
|
|
|
- // context
|
|
|
- const { handleCloseDialog } = useContext(rootContext);
|
|
|
- // translations
|
|
|
- const { t: btnTrans } = useTranslation('btn');
|
|
|
- const { t: dialogTrans } = useTranslation('dialog');
|
|
|
- // refs
|
|
|
- const editorEl = useRef<HTMLDivElement>(null);
|
|
|
- const editor = useRef<EditorView>();
|
|
|
- // styles
|
|
|
- const classes = useStyles();
|
|
|
-
|
|
|
- // sort data by collection schema order
|
|
|
- const schema = collection.schema;
|
|
|
- let sortedData: { [key: string]: any } = {};
|
|
|
- schema.fields.forEach(field => {
|
|
|
- if (data[field.name] !== undefined) {
|
|
|
- sortedData[field.name] = data[field.name];
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- // add dynamic fields if exist
|
|
|
- const isDynamicSchema = collection.schema.dynamicFields.length > 0;
|
|
|
- if (isDynamicSchema) {
|
|
|
- sortedData = { ...sortedData, ...data[DYNAMIC_FIELD] };
|
|
|
- }
|
|
|
-
|
|
|
- const originalData = JSON.stringify(sortedData, null, 2);
|
|
|
-
|
|
|
- // create editor
|
|
|
- useEffect(() => {
|
|
|
- if (!editor.current) {
|
|
|
- const startState = EditorState.create({
|
|
|
- doc: originalData,
|
|
|
- extensions: [
|
|
|
- basicSetup,
|
|
|
- json(),
|
|
|
- linterExtension,
|
|
|
- keymap.of([{ key: 'Tab', run: insertTab }]), // fix tab behaviour
|
|
|
- indentUnit.of(' '), // fix tab indentation
|
|
|
- EditorView.theme({
|
|
|
- '&.cm-editor': {
|
|
|
- '&.cm-focused': {
|
|
|
- outline: 'none',
|
|
|
- },
|
|
|
- },
|
|
|
- '.cm-content': {
|
|
|
- fontSize: '12px',
|
|
|
- },
|
|
|
- '.cm-tooltip-lint': {
|
|
|
- width: '80%',
|
|
|
- },
|
|
|
- }),
|
|
|
- themeCompartment.of(
|
|
|
- theme.palette.mode === 'light' ? githubLight : githubDark
|
|
|
- ),
|
|
|
- EditorView.lineWrapping,
|
|
|
- EditorView.updateListener.of((viewUpdate: ViewUpdate) => {
|
|
|
- if (viewUpdate.docChanged) {
|
|
|
- const d = jsonParseLinter()(view);
|
|
|
- if (d.length !== 0) {
|
|
|
- setDisabled(true);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- const doc = viewUpdate.state.doc;
|
|
|
- const value = doc.toString();
|
|
|
-
|
|
|
- setDisabled(value === originalData);
|
|
|
- }
|
|
|
- }),
|
|
|
- ],
|
|
|
- });
|
|
|
-
|
|
|
- const view = new EditorView({
|
|
|
- state: startState,
|
|
|
- parent: editorEl.current!,
|
|
|
- });
|
|
|
-
|
|
|
- editor.current = view;
|
|
|
-
|
|
|
- return () => {
|
|
|
- view.destroy();
|
|
|
- editor.current = undefined;
|
|
|
- };
|
|
|
- }
|
|
|
- }, [JSON.stringify(data)]);
|
|
|
-
|
|
|
- // handle confirm
|
|
|
- const handleConfirm = async () => {
|
|
|
- const result = (await DataService.upsert(collection.collection_name, {
|
|
|
- fields_data: [JSON.parse(editor.current?.state.doc.toString()!)],
|
|
|
- })) as any;
|
|
|
-
|
|
|
- const idField = result.IDs.id_field;
|
|
|
- const id = result.IDs[idField].data;
|
|
|
-
|
|
|
- props.cb && props.cb(id[0]);
|
|
|
- handleCloseDialog();
|
|
|
- };
|
|
|
-
|
|
|
- return (
|
|
|
- <DialogTemplate
|
|
|
- title={dialogTrans('editEntityTitle')}
|
|
|
- handleClose={handleCloseDialog}
|
|
|
- children={
|
|
|
- <>
|
|
|
- <div className={classes.tip}>{dialogTrans('editEntityInfo')}</div>
|
|
|
- <div
|
|
|
- className={`${classes.code} cm-editor`}
|
|
|
- ref={editorEl}
|
|
|
- onClick={() => {
|
|
|
- if (editor.current) editor.current.focus();
|
|
|
- }}
|
|
|
- ></div>
|
|
|
- </>
|
|
|
- }
|
|
|
- confirmDisabled={disabled}
|
|
|
- confirmLabel={btnTrans('edit')}
|
|
|
- handleConfirm={handleConfirm}
|
|
|
- showCancel={true}
|
|
|
- />
|
|
|
- );
|
|
|
-};
|
|
|
-
|
|
|
-export default EditEntityDialog;
|