feat: support tree node actions

This commit is contained in:
purocean
2024-07-10 14:39:49 +08:00
parent a86a506623
commit 601f7b024f
5 changed files with 82 additions and 16 deletions
+16 -13
View File
@@ -1,5 +1,5 @@
<template>
<div class="tree-node">
<div class="tree-node" @mouseenter.self="onMouseEnter" @mouseleave="onMouseLeave">
<details
v-if="itemNode.type === 'dir'"
:class="{name: true, 'drag-over': dragOver}"
@@ -24,8 +24,7 @@
{{ itemNode.name }} <span class="count">({{itemNode.children ? itemNode.children.length : 0}})</span>
</div>
<div v-if="!FLAG_READONLY" class="item-action">
<svg-icon class="icon" name="folder-plus-solid" @click.exact.stop.prevent="createFolder()" :title="$t('tree.context-menu.create-dir')"></svg-icon>
<svg-icon class="icon" name="plus" @click.exact.stop.prevent="createFile()" :title="$t('tree.context-menu.create-doc')"></svg-icon>
<svg-icon v-for="btn in actions" :key="btn.id" class="icon" :name="btn.icon" @click.exact.stop.prevent="btn.onClick" :title="btn.title" />
</div>
</div>
</summary>
@@ -53,10 +52,10 @@
</template>
<script lang="ts">
import { computed, defineComponent, h, nextTick, PropType, ref, watch } from 'vue'
import { computed, defineComponent, h, nextTick, PropType, ref, shallowRef, watch } from 'vue'
import { useContextMenu } from '@fe/support/ui/context-menu'
import { triggerHook } from '@fe/core/hook'
import { getContextMenuItems } from '@fe/services/tree'
import { getContextMenuItems, getNodeActionButtons } from '@fe/services/tree'
import type { Components } from '@fe/types'
import { createDir, createDoc, deleteDoc, duplicateDoc, isMarkdownFile, isMarked, moveDoc, switchDoc } from '@fe/services/document'
import { useI18n } from '@fe/services/i18n'
@@ -84,6 +83,7 @@ export default defineComponent({
const refFile = ref<any>(null)
const localMarked = ref<boolean | null>(null)
const dragOver = ref<boolean>(false)
const actions = shallowRef<Components.Tree.NodeActionBtn[]>([])
const itemNode = computed(() => ({ ...props.item, marked: isMarked(props.item) }))
const open = ref(itemNode.value.path === '/')
@@ -94,12 +94,14 @@ export default defineComponent({
const currentFile = computed(() => store.state.currentFile)
async function createFile () {
await createDoc({ repo: props.item.repo }, props.item)
function onMouseEnter () {
if (props.item.type === 'dir') {
actions.value = getNodeActionButtons(props.item)
}
}
async function createFolder () {
await createDir({ repo: props.item.repo }, props.item)
function onMouseLeave () {
actions.value = []
}
function onTreeNodeDblClick (node: Components.Tree.Node) {
@@ -347,12 +349,11 @@ export default defineComponent({
refFile,
fileTitle,
selected,
actions,
onTreeNodeDblClick,
marked,
showContextMenu,
select,
createFile,
createFolder,
dragOver,
onDragEnter,
onDragOver,
@@ -360,6 +361,8 @@ export default defineComponent({
onDragExit,
onDrop,
onDragStart,
onMouseEnter,
onMouseLeave,
shouldOpen,
isMarkdownFile,
FLAG_READONLY,
@@ -456,12 +459,12 @@ summary > .item {
height: 20px;
width: 20px;
border-radius: 50%;
color: var(--g-color-45);
color: var(--g-color-30);
}
.item-action .icon:hover {
background: var(--g-color-70);
color: var(--g-color-25);
color: var(--g-color-20);
}
.item:hover .item-action {
+2 -2
View File
@@ -3,7 +3,7 @@ import customStyles from '@fe/plugins/custom-styles'
import customKeybindings from '@fe/plugins/custom-keybindings'
import electronZoom from '@fe/plugins/electron-zoom'
import historyStack from '@fe/plugins/history-stack'
import fileTreeContextMenu from '@fe/plugins/file-tree-context-menu'
import fileTreeFunctions from '@fe/plugins/file-tree-functions'
import statusBarSetting from '@fe/plugins/status-bar-setting'
import statusBarRepositorySwitch from '@fe/plugins/status-bar-repository-switch'
import statusBarView from '@fe/plugins/status-bar-view'
@@ -79,7 +79,7 @@ export default [
customKeybindings,
electronZoom,
historyStack,
fileTreeContextMenu,
fileTreeFunctions,
statusBarSetting,
statusBarRepositorySwitch,
statusBarView,
@@ -109,5 +109,34 @@ export default {
items.push(...getItems(doc, 'tabs'))
})
async function createFile (node: Components.Tree.Node) {
await ctx.doc.createDoc({ repo: node.repo }, node)
}
async function createFolder (node: Components.Tree.Node) {
await ctx.doc.createDir({ repo: node.repo }, node)
}
ctx.tree.tapNodeActionButtons((btns, node) => {
if (node.type !== 'dir') {
return
}
btns.push(
{
id: 'create-folder',
title: ctx.i18n.t('tree.context-menu.create-dir'),
icon: 'folder-plus-solid',
onClick: () => createFolder(node)
},
{
id: 'create-file',
title: ctx.i18n.t('tree.context-menu.create-doc'),
icon: 'plus',
onClick: () => createFile(node)
}
)
})
}
} as Plugin
+26
View File
@@ -1,6 +1,7 @@
import { nextTick, Ref } from 'vue'
import type { Components } from '@fe/types'
import { getActionHandler, registerAction } from '@fe/core/action'
import * as ioc from '@fe/core/ioc'
import store from '@fe/support/store'
import { useToast } from '@fe/support/ui/toast'
import * as api from '@fe/support/api'
@@ -36,6 +37,31 @@ export function getContextMenuItems (node: Components.Tree.Node, vueCtx: VueCtx)
return items
}
/**
* Add a node action buttons processor.
* @param fun
*/
export function tapNodeActionButtons (fun: (
btns: Components.Tree.NodeActionBtn[],
currentNode: Components.Tree.Node,
) => void) {
ioc.register('TREE_NODE_ACTION_BTN_TAPPERS', fun)
}
/**
* Get node action buttons.
*/
export function getNodeActionButtons (currentNode: Components.Tree.Node) {
const btns: Components.Tree.NodeActionBtn[] = []
const tappers = ioc.get('TREE_NODE_ACTION_BTN_TAPPERS')
tappers.forEach((tapper) => {
tapper(btns, currentNode)
})
return btns
}
/**
* Refresh file tree.
*/
+9 -1
View File
@@ -181,6 +181,13 @@ export namespace Components {
children?: Node[];
level: number;
}
export type NodeActionBtn = {
id: string,
icon: string,
title: string,
onClick: (e: MouseEvent) => void,
}
}
export namespace QuickFilter {
@@ -567,7 +574,8 @@ export type BuildInIOCTypes = { [key in keyof BuildInHookTypes]: any; } & {
EDITOR_CUSTOM_EDITOR: CustomEditor,
RENDERERS: Renderer,
CODE_RUNNER: CodeRunner;
DOC_CATEGORIES: DocCategory
DOC_CATEGORIES: DocCategory;
TREE_NODE_ACTION_BTN_TAPPERS: (btns: Components.Tree.NodeActionBtn[], currentNode: Components.Tree.Node) => void;
}
export type FrontMatterAttrs = {