Browse Source

init edit entity dialog

Signed-off-by: ryjiang <jiangruiyi@gmail.com>
ryjiang 11 months ago
parent
commit
9ac40d7c5f

+ 3 - 1
client/src/i18n/cn/button.ts

@@ -49,10 +49,12 @@ const btnTrans = {
   deleteColTooltip: '删除所选的collection',
   duplicateTooltip: '复制collection,不包含数据',
   renameTooltip: '重命名collection',
+  editEntityTooltip: '编辑entity',
 
   // disable tooltip
-  downloadDisabledTooltip: '导出前请先选择数据',
+  downloadDisabledTooltip: '导出前请先选择数据',
   deleteDisableTooltip: '请至少选择一个要删除的项目。',
+  editEntityDisabledTooltip: '一次只能编辑一个entity。',
 };
 
 export default btnTrans;

+ 1 - 0
client/src/i18n/cn/dialog.ts

@@ -10,6 +10,7 @@ const dialogTrans = {
   compact: `压缩Collection {{type}}`,
   flush: `为 {{type}} 的数据落盘`,
   loadTitle: `加载 {{type}}`,
+  editEntityTitle: `编辑 Entity`,
 
   loadContent: `您正在尝试加载带有数据的 {{type}}。只有已加载的 {{type}} 可以被搜索。`,
   releaseContent: `您正在尝试发布带有数据的 {{type}}。请注意,数据将不再可用于搜索。`,

+ 2 - 0
client/src/i18n/en/button.ts

@@ -49,10 +49,12 @@ const btnTrans = {
   deleteColTooltip: 'Drop selected collection',
   duplicateTooltip: 'Duplicate selected collection without data',
   renameTooltip: 'Rename collection',
+  editEntityTooltip: 'Edit entity(JSON)',
 
   // disable tooltip
   downloadDisabledTooltip: 'Please select data before exporting',
   deleteDisableTooltip: 'Please select at least one item to delete.',
+  editEntityDisabledTooltip: 'Only one entity can be edited at a time.',
 };
 
 export default btnTrans;

+ 1 - 0
client/src/i18n/en/dialog.ts

@@ -10,6 +10,7 @@ const dialogTrans = {
   compact: `Compact collection {{type}}`,
   flush: `Flush data for {{type}}`,
   loadTitle: `Load {{type}}`,
+  editEntityTitle: `Edit Entity(JSON)`,
 
   loadContent: `You are trying to load a {{type}} with data. Only loaded {{type}} can be searched.`,
   releaseContent: `You are trying to release {{type}} with data. Please be aware that the data will no longer be available for search.`,

+ 27 - 0
client/src/pages/databases/collections/data/CollectionData.tsx

@@ -13,6 +13,7 @@ import Filter from '@/components/advancedSearch';
 import DeleteTemplate from '@/components/customDialog/DeleteDialogTemplate';
 import CustomToolBar from '@/components/grid/ToolBar';
 import InsertDialog from '@/pages/dialogs/insert/Dialog';
+import EditEntityDialog from '@/pages/dialogs/EditEntityDialog';
 import { getLabelDisplayedRows } from '@/pages/search/Utils';
 import { getQueryStyles } from './Styles';
 import {
@@ -251,6 +252,32 @@ const CollectionData = (props: CollectionDataProps) => {
       tooltip: btnTrans('emptyTooltip'),
       disabled: () => selectedData?.length > 0 ||  total == 0,
     },
+    {
+      type: 'button',
+      btnVariant: 'text',
+      onClick: () => {
+        setDialog({
+          open: true,
+          type: 'custom',
+          params: {
+            component: (
+              <EditEntityDialog
+                data={selectedData[0]}
+                collection={collection!}
+              />
+            ),
+          },
+        });
+      },
+      label: btnTrans('edit'),
+      icon: 'edit',
+      tooltip: btnTrans('editEntityTooltip'),
+      disabledTooltip: btnTrans('editEntityDisabledTooltip'),
+      disabled: () => selectedData?.length !== 1,
+      hideOnDisable() {
+        return selectedData?.length === 0;
+      },
+    },
     {
       type: 'button',
       btnVariant: 'text',

+ 8 - 7
client/src/pages/databases/collections/search/VectorInputBox.tsx

@@ -238,17 +238,18 @@ export default function VectorInputBox(props: VectorInputBoxProps) {
         ],
       });
 
-      editor.current = new EditorView({
+      const view = new EditorView({
         state: startState,
         parent: editorEl.current!,
       });
-    }
-    return () => {
-      if (editor.current) {
-        editor.current.destroy();
+
+      editor.current = view;
+
+      return () => {
+        view.destroy();
         editor.current = undefined;
-      }
-    };
+      };
+    }
   }, [JSON.stringify(field)]);
 
   return (

+ 127 - 0
client/src/pages/dialogs/EditEntityDialog.tsx

@@ -0,0 +1,127 @@
+import { FC, useContext, useEffect, useRef } from 'react';
+import { makeStyles, Theme } from '@material-ui/core';
+import { EditorState } from '@codemirror/state';
+import { EditorView, keymap } 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 { CollectionFullObject } from '@server/types';
+
+const useStyles = makeStyles((theme: Theme) => ({
+  code: {
+    backgroundColor: '#f5f5f5',
+    padding: 4,
+    width: '60vw',
+    minHeight: '60vh',
+    maxHeight: '60vh',
+    overflow: 'auto',
+  },
+}));
+
+type EditEntityDialogProps = {
+  data: { [key: string]: any };
+  collection: CollectionFullObject;
+};
+
+// json linter for cm
+const linterExtension = linter(jsonParseLinter());
+
+const EditEntityDialog: FC<EditEntityDialogProps> = props => {
+  // props
+  const { data, collection } = props;
+  // 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;
+  const sortedData: { [key: string]: any } = {};
+  schema.fields.forEach(field => {
+    if (data[field.name] !== undefined) {
+      sortedData[field.name] = data[field.name];
+    }
+  });
+
+  // create editor
+  useEffect(() => {
+    if (!editor.current) {
+      const startState = EditorState.create({
+        doc: JSON.stringify(sortedData, null, 4),
+        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': {
+              color: '#484D52',
+              fontSize: '12px',
+            },
+            '.cm-tooltip-lint': {
+              width: '80%',
+            },
+          }),
+
+          EditorView.lineWrapping,
+        ],
+      });
+
+      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 = () => {
+    console.log(JSON.parse(editor.current?.state.doc.toString()!));
+    handleCloseDialog();
+  };
+
+  return (
+    <DialogTemplate
+      title={dialogTrans('editEntityTitle')}
+      handleClose={handleCloseDialog}
+      children={
+        <div
+          className={`${classes.code} cm-editor`}
+          ref={editorEl}
+          onClick={() => {
+            if (editor.current) editor.current.focus();
+          }}
+        ></div>
+      }
+      confirmLabel={btnTrans('edit')}
+      handleConfirm={handleConfirm}
+      showCancel={true}
+    />
+  );
+};
+
+export default EditEntityDialog;