This is an automated email from the ASF dual-hosted git repository.

nicholasjiang pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-paimon-webui.git


The following commit(s) were added to refs/heads/main by this push:
     new 2509c30  [Feature] Introduce context menu (#52)
2509c30 is described below

commit 2509c30345c57601f2bb3afc692f38c700ef4cdc
Author: labbomb <[email protected]>
AuthorDate: Sat Oct 14 15:16:19 2023 +0800

    [Feature] Introduce context menu (#52)
---
 .../context-menu/index.module.scss}                |  22 +---
 .../src/components/context-menu/index.tsx          | 118 +++++++++++++++++++++
 .../src/locales/en/modules/playground.ts           |   8 ++
 .../src/locales/zh/modules/playground.ts           |   8 ++
 .../components/query/components/tabs/index.tsx     |  51 ++++++++-
 .../workbench/components/menu-tree/index.tsx       |  71 +++++++++----
 .../components/workbench/components/tabs/index.tsx |  51 ++++++++-
 7 files changed, 283 insertions(+), 46 deletions(-)

diff --git a/paimon-web-ui-new/src/locales/zh/modules/playground.ts 
b/paimon-web-ui-new/src/components/context-menu/index.module.scss
similarity index 63%
copy from paimon-web-ui-new/src/locales/zh/modules/playground.ts
copy to paimon-web-ui-new/src/components/context-menu/index.module.scss
index 2787b5b..7d9d27b 100644
--- a/paimon-web-ui-new/src/locales/zh/modules/playground.ts
+++ b/paimon-web-ui-new/src/components/context-menu/index.module.scss
@@ -15,23 +15,7 @@ KIND, either express or implied.  See the License for the
 specific language governing permissions and limitations
 under the License. */
 
-export default {
-  query: '查询',
-  workbench: '工作台',
-  task_operation_and_maintenance: '任务运维',
-  settings: '设置',
-  terminal: '终端',
-  git_branch: 'Git 分支',
-  data: '数据',
-  saved_query: '已保存查询',
-  query_record: '查询记录',
-  search: '搜索',
-  run: '运行',
-  format: '格式化',
-  save: '保存',
-  clear: '清空',
-  unfold: '展开',
-  collapse: '折叠',
-  logs: '日志',
-  result: '结果',
+.context-menu {
+  width: 200px;
+  height: 100%;
 }
diff --git a/paimon-web-ui-new/src/components/context-menu/index.tsx 
b/paimon-web-ui-new/src/components/context-menu/index.tsx
new file mode 100644
index 0000000..718475e
--- /dev/null
+++ b/paimon-web-ui-new/src/components/context-menu/index.tsx
@@ -0,0 +1,118 @@
+/* Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License. */
+
+import type { TreeOption } from 'naive-ui'
+import styles from './index.module.scss'
+
+export default defineComponent({
+  name: 'ContextMenu',
+  props: {
+    x: {
+      type: Number as PropType<number>,
+      default: 0
+    },
+    y: {
+      type: Number as PropType<number>,
+      default: 0
+    },
+    visible: {
+      type: Boolean as PropType<boolean>,
+      default: false
+    },
+    type: {
+      type: Array as PropType<string[]>,
+      default: () => []
+    }
+  },
+  emits: ['update:visible', 'select'],
+  setup(props, { emit }) {
+    const { t } = useLocaleHooks()
+
+    const options = computed(() => ([
+      {
+        label: t('playground.new_folder'),
+        key: 'new_folder',
+        show: props.type.includes('folder')
+      },
+      {
+        label: t('playground.new_file'),
+        key: 'new_file',
+        show: props.type.includes('file')
+      },
+      {
+        label: t('playground.delete'),
+        key: 'delete',
+        show: props.type.includes('delete')
+      },
+      {
+        label: t('playground.rename'),
+        key: 'rename',
+        show: props.type.includes('rename')
+      },
+      {
+        label: t('playground.close_left'),
+        key: 'close_left',
+        show: props.type.includes('close_left')
+      },
+      {
+        label: t('playground.close_right'),
+        key: 'close_right',
+        show: props.type.includes('close_right')
+      },
+      {
+        label: t('playground.close_others'),
+        key: 'close_others',
+        show: props.type.includes('close_others')
+      },
+      {
+        label: t('playground.close_all'),
+        key: 'close_all',
+        show: props.type.includes('close_all')
+      }
+    ]))
+
+    const handleSelect = (keys: Array<string | number>, option: 
Array<TreeOption | null>) => {
+      emit('select', keys, option)
+    }
+
+    const handleClickOutside = () => {
+      emit('update:visible', false)
+    }
+
+    return {
+      options,
+      handleSelect,
+      handleClickOutside
+    }
+  },
+  render() {
+    return (
+      <div class={styles['context-menu']}>
+        <n-dropdown
+          trigger="manual"
+          placement="bottom-start"
+          options={this.options}
+          show={this.visible}
+          x={this.x}
+          y={this.y}
+          on-select={this.handleSelect}
+          on-clickoutside={this.handleClickOutside}
+        />
+      </div>
+    )
+  }
+})
diff --git a/paimon-web-ui-new/src/locales/en/modules/playground.ts 
b/paimon-web-ui-new/src/locales/en/modules/playground.ts
index 9babf17..c505377 100644
--- a/paimon-web-ui-new/src/locales/en/modules/playground.ts
+++ b/paimon-web-ui-new/src/locales/en/modules/playground.ts
@@ -34,4 +34,12 @@ export default {
   collapse: 'Collapse',
   logs: 'Logs',
   result: 'Result',
+  new_folder: 'New Folder',
+  new_file: 'New File',
+  rename: 'Rename',
+  delete: 'Delete',
+  close_all: 'Close All',
+  close_others: 'Close Others',
+  close_right: 'Close Right',
+  close_left: 'Close Left',
 }
diff --git a/paimon-web-ui-new/src/locales/zh/modules/playground.ts 
b/paimon-web-ui-new/src/locales/zh/modules/playground.ts
index 2787b5b..7a52d1a 100644
--- a/paimon-web-ui-new/src/locales/zh/modules/playground.ts
+++ b/paimon-web-ui-new/src/locales/zh/modules/playground.ts
@@ -34,4 +34,12 @@ export default {
   collapse: '折叠',
   logs: '日志',
   result: '结果',
+  new_folder: '新建文件夹',
+  new_file: '新建文件',
+  rename: '重命名',
+  delete: '删除',
+  close_all: '关闭所有',
+  close_others: '关闭其他',
+  close_right: '关闭右侧',
+  close_left: '关闭左侧',
 }
diff --git 
a/paimon-web-ui-new/src/views/playground/components/query/components/tabs/index.tsx
 
b/paimon-web-ui-new/src/views/playground/components/query/components/tabs/index.tsx
index 706953c..9d1f4aa 100644
--- 
a/paimon-web-ui-new/src/views/playground/components/query/components/tabs/index.tsx
+++ 
b/paimon-web-ui-new/src/views/playground/components/query/components/tabs/index.tsx
@@ -16,6 +16,7 @@ specific language governing permissions and limitations
 under the License. */
 
 import styles from './index.module.scss'
+import ContextMenu from '@/components/context-menu';
 
 export default defineComponent({
   name: 'EditorTabs',
@@ -25,6 +26,7 @@ export default defineComponent({
     const tabVariables = reactive({
       chooseTab: '',
       panelsList: [] as any,
+      row: {} as any
     })
 
     const handleAdd = () => {
@@ -59,6 +61,40 @@ export default defineComponent({
       treeData.value = data
     })
 
+    const contextMenuVariables = reactive({
+      x: 0,
+      y: 0,
+      isShow: false
+    })
+
+    const openContextMenu = (e: MouseEvent, item: any) => {
+      e.preventDefault()
+      contextMenuVariables.x = e.pageX
+      contextMenuVariables.y = e.pageY
+      contextMenuVariables.isShow = true
+      tabVariables.row = item
+      tabVariables.chooseTab = tabVariables.row.key
+    }
+
+    const handleContextMenuSelect = (keys: string) => {
+      const index = tabVariables.panelsList.findIndex((item: any) => item.key 
=== tabVariables.row.key)
+      switch (keys) {
+        case 'close_left':
+          tabVariables.panelsList.splice(0, index)
+          break;
+        case 'close_right':
+          tabVariables.panelsList.splice(index + 1)
+          break;
+        case 'close_others':
+          tabVariables.panelsList = [tabVariables.row]
+          break;
+        case 'close_all':
+          tabVariables.panelsList = []
+          break;
+      }
+      contextMenuVariables.isShow = false
+    }
+
     onMounted(() => {
       mittBus.emit('initTabData', tabVariables)
     })
@@ -67,7 +103,10 @@ export default defineComponent({
       ...toRefs(tabVariables),
       handleAdd,
       handleClose,
-      changeTreeChoose
+      changeTreeChoose,
+      openContextMenu,
+      ...toRefs(contextMenuVariables),
+      handleContextMenuSelect
     }
   },
   render() {
@@ -92,7 +131,7 @@ export default defineComponent({
               <n-tab-pane name={item.key}
                 v-slots={{
                   tab: () => (
-                    <div class={styles.tabs}>
+                    <div class={styles.tabs} onContextmenu={(e: MouseEvent) => 
this.openContextMenu(e, item)}>
                       <div class={styles.dot}></div>
                       <div>{item.tableName}</div>
                       {!item.isSaved && <div class={styles.asterisk}>*</div>}
@@ -103,6 +142,14 @@ export default defineComponent({
             ))
           }
         </n-tabs>
+        <ContextMenu
+          x={this.x}
+          y={this.y}
+          visible={this.isShow}
+          type={['close_left', 'close_right', 'close_others', 'close_all']}
+          onUpdate:visible={() => this.isShow = false}
+          onSelect={this.handleContextMenuSelect}
+        />
       </div>
     );
   }
diff --git 
a/paimon-web-ui-new/src/views/playground/components/workbench/components/menu-tree/index.tsx
 
b/paimon-web-ui-new/src/views/playground/components/workbench/components/menu-tree/index.tsx
index 0cbc368..f1b581d 100644
--- 
a/paimon-web-ui-new/src/views/playground/components/workbench/components/menu-tree/index.tsx
+++ 
b/paimon-web-ui-new/src/views/playground/components/workbench/components/menu-tree/index.tsx
@@ -15,7 +15,7 @@ KIND, either express or implied.  See the License for the
 specific language governing permissions and limitations
 under the License. */
 
-import { CodeSlash, FileTrayFullOutline, Search, ServerOutline } from 
'@vicons/ionicons5';
+import { FileTrayFullOutline, Search, FolderOpenOutline } from 
'@vicons/ionicons5';
 import styles from './index.module.scss'
 import { NIcon, type TreeOption } from 'naive-ui';
 
@@ -29,22 +29,25 @@ export default defineComponent({
         {
           key: 'paimon2',
           label: 'paimon2',
+          type: 'folder',
           prefix: () =>
             h(NIcon, null, {
-              default: () => h(ServerOutline)
+              default: () => h(FolderOpenOutline)
             }),
           children: [
             {
               key: 'user',
               label: 'user',
+              type: 'folder',
               prefix: () =>
                 h(NIcon, null, {
-                  default: () => h(ServerOutline)
+                  default: () => h(FolderOpenOutline)
                 }),
               children: [
                 {
                   label: 'user_table',
                   key: '1',
+                  type: 'file',
                   content: 'select * from abc where abc.a="abc";select * from 
cba where cba.a="cba";',
                   prefix: () =>
                     h(NIcon, null, {
@@ -54,6 +57,7 @@ export default defineComponent({
                 {
                   label: 'people_table',
                   key: '2',
+                  type: 'file',
                   content: 'select * from abc where abc.a="abc";',
                   prefix: () =>
                     h(NIcon, null, {
@@ -61,25 +65,6 @@ export default defineComponent({
                     })
                 }
               ]
-            },
-            {
-              key: 'role',
-              label: 'role',
-              prefix: () =>
-                h(NIcon, null, {
-                  default: () => h(ServerOutline)
-                }),
-              children: [
-                {
-                  label: 'user_table',
-                  key: '3',
-                  content: 'select * from kkk;',
-                  prefix: () =>
-                    h(NIcon, null, {
-                      default: () => h(FileTrayFullOutline)
-                    })
-                },
-              ]
             }
           ]
         }
@@ -88,6 +73,22 @@ export default defineComponent({
       selectedKeys: []
     })
 
+    const contextMenuVariables = reactive({
+      x: 0,
+      y: 0,
+      isShow: false,
+      options: [
+        {
+          label: t('playground.new_folder'),
+          key: 'new_folder',
+        },
+        {
+          label: t('playground.new_file'),
+          key: 'new_file',
+        },
+      ]
+    })
+
     const nodeProps = ({ option }: { option: TreeOption }) => {
       return {
         onClick () {
@@ -104,6 +105,12 @@ export default defineComponent({
           })
           tabData.value.chooseTab = option.key
         },
+        onContextmenu (e: MouseEvent): void {
+          e.preventDefault()
+          contextMenuVariables.x = e.clientX
+          contextMenuVariables.y = e.clientY
+          contextMenuVariables.isShow = true
+        }
       }
     }
 
@@ -119,6 +126,12 @@ export default defineComponent({
       tabData.value = data
     })
 
+
+    const handleContextMenuSelect = () => {
+      contextMenuVariables.isShow = false
+    }
+
+
     onMounted(() => {
       mittBus.emit('initTreeData', treeVariables)
     })
@@ -127,7 +140,9 @@ export default defineComponent({
       t,
       ...toRefs(treeVariables),
       nodeProps,
-      handleTreeSelect
+      handleTreeSelect,
+      handleContextMenuSelect,
+      ...toRefs(contextMenuVariables),
     }
   },
   render() {
@@ -151,6 +166,16 @@ export default defineComponent({
               pattern={this.filterValue}
               node-props={this.nodeProps}
             />
+            <n-dropdown
+              trigger="manual"
+              placement="bottom-start"
+              options={this.options}
+              show={this.isShow}
+              x={this.x}
+              y={this.y}
+              on-select={this.handleContextMenuSelect}
+              on-clickoutside={() => this.isShow = false}
+            />
           </n-space>
         </n-card>
       </div>
diff --git 
a/paimon-web-ui-new/src/views/playground/components/workbench/components/tabs/index.tsx
 
b/paimon-web-ui-new/src/views/playground/components/workbench/components/tabs/index.tsx
index 706953c..9d1f4aa 100644
--- 
a/paimon-web-ui-new/src/views/playground/components/workbench/components/tabs/index.tsx
+++ 
b/paimon-web-ui-new/src/views/playground/components/workbench/components/tabs/index.tsx
@@ -16,6 +16,7 @@ specific language governing permissions and limitations
 under the License. */
 
 import styles from './index.module.scss'
+import ContextMenu from '@/components/context-menu';
 
 export default defineComponent({
   name: 'EditorTabs',
@@ -25,6 +26,7 @@ export default defineComponent({
     const tabVariables = reactive({
       chooseTab: '',
       panelsList: [] as any,
+      row: {} as any
     })
 
     const handleAdd = () => {
@@ -59,6 +61,40 @@ export default defineComponent({
       treeData.value = data
     })
 
+    const contextMenuVariables = reactive({
+      x: 0,
+      y: 0,
+      isShow: false
+    })
+
+    const openContextMenu = (e: MouseEvent, item: any) => {
+      e.preventDefault()
+      contextMenuVariables.x = e.pageX
+      contextMenuVariables.y = e.pageY
+      contextMenuVariables.isShow = true
+      tabVariables.row = item
+      tabVariables.chooseTab = tabVariables.row.key
+    }
+
+    const handleContextMenuSelect = (keys: string) => {
+      const index = tabVariables.panelsList.findIndex((item: any) => item.key 
=== tabVariables.row.key)
+      switch (keys) {
+        case 'close_left':
+          tabVariables.panelsList.splice(0, index)
+          break;
+        case 'close_right':
+          tabVariables.panelsList.splice(index + 1)
+          break;
+        case 'close_others':
+          tabVariables.panelsList = [tabVariables.row]
+          break;
+        case 'close_all':
+          tabVariables.panelsList = []
+          break;
+      }
+      contextMenuVariables.isShow = false
+    }
+
     onMounted(() => {
       mittBus.emit('initTabData', tabVariables)
     })
@@ -67,7 +103,10 @@ export default defineComponent({
       ...toRefs(tabVariables),
       handleAdd,
       handleClose,
-      changeTreeChoose
+      changeTreeChoose,
+      openContextMenu,
+      ...toRefs(contextMenuVariables),
+      handleContextMenuSelect
     }
   },
   render() {
@@ -92,7 +131,7 @@ export default defineComponent({
               <n-tab-pane name={item.key}
                 v-slots={{
                   tab: () => (
-                    <div class={styles.tabs}>
+                    <div class={styles.tabs} onContextmenu={(e: MouseEvent) => 
this.openContextMenu(e, item)}>
                       <div class={styles.dot}></div>
                       <div>{item.tableName}</div>
                       {!item.isSaved && <div class={styles.asterisk}>*</div>}
@@ -103,6 +142,14 @@ export default defineComponent({
             ))
           }
         </n-tabs>
+        <ContextMenu
+          x={this.x}
+          y={this.y}
+          visible={this.isShow}
+          type={['close_left', 'close_right', 'close_others', 'close_all']}
+          onUpdate:visible={() => this.isShow = false}
+          onSelect={this.handleContextMenuSelect}
+        />
       </div>
     );
   }

Reply via email to