mirror of
https://github.com/purocean/yn.git
synced 2026-09-24 15:46:02 +08:00
feat: support tree node actions
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
+29
@@ -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
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
Reference in New Issue
Block a user