Skip to content
This repository has been archived by the owner on Feb 24, 2024. It is now read-only.

Commit

Permalink
feat: general rename
Browse files Browse the repository at this point in the history
  • Loading branch information
neko-para committed Aug 14, 2023
1 parent 24b08d0 commit 470d6b5
Show file tree
Hide file tree
Showing 11 changed files with 173 additions and 70 deletions.
31 changes: 31 additions & 0 deletions src/components/atomic/AutoFocusInput.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<script setup lang="ts">
import { useVModel } from '@vueuse/core'
import { NInput } from 'naive-ui'
import { onMounted, ref } from 'vue'
const props = defineProps<{
value: string
}>()
const emits = defineEmits<{
'update:value': [string]
blur: []
}>()
const value = useVModel(props, 'value', emits)
const el = ref<InstanceType<typeof NInput> | null>(null)
onMounted(() => {
el.value?.focus()
})
</script>

<template>
<NInput
ref="el"
v-model:value="value"
size="tiny"
class="outline-none bg-transparent"
@blur="emits('blur')"
></NInput>
</template>
1 change: 1 addition & 0 deletions src/components/tree/TaskTree.vue
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ const treeHeight = computed(() => {
block-line
selectable
expand-on-click
:keyboard="false"
:pattern="searchText"
:show-irrelevant-nodes="false"
:cancelable="false"
Expand Down
132 changes: 69 additions & 63 deletions src/components/tree/TaskTreeAction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,14 @@ import {
} from 'naive-ui'
import { computed, ref } from 'vue'

import { deleteTask, setTask, taskIndex } from '@/data'
import {
deleteTask,
filterTemplate,
renameInto,
renameKey,
setTask,
taskIndex
} from '@/data'
import { type PathKey, type PathSegments, fs, path, pool } from '@/filesystem'

export function onUploadImage(dialog: DialogApi, key: PathKey) {
Expand Down Expand Up @@ -69,70 +76,28 @@ export function onUploadImage(dialog: DialogApi, key: PathKey) {
})
}

export function onNewFolder(dialog: DialogApi, key: PathKey) {
const name = ref<string>('')
const p = computed(() => path.joinkey(key, name.value))
const pathExists = computed(() => {
return !!fs.tree.existsDir(p.value)
})

const dlg = dialog.create({
title: '创建目录',
content: () => (
<NInput
value={name.value}
onUpdateValue={v => (name.value = v)}
placeholder={'文件名'}
></NInput>
),
action: () => (
<NButton
disabled={pathExists.value}
onClick={() => {
if (!pathExists.value) {
fs.tree.touchDir(p.value)
dlg.destroy()
}
}}
>
确认
</NButton>
)
})
export function onNewFolder(key: PathKey) {
for (let i = 0; ; i++) {
const name = `__NewFolder${i}`
const to = path.joinkey(key, name)
if (fs.tree.existsDir(to)) {
continue
}
fs.tree.touchDir(to)
break
}
}

export function onNewJson(dialog: DialogApi, key: PathKey) {
const name = ref<string>('')
const nameWithSfx = computed(() =>
name.value.endsWith('.json') ? name.value : `${name.value}.json`
)
const to = computed(() => path.joinkey(key, nameWithSfx.value))
const pathExists = computed(() => {
return fs.tree.existsFile(to.value)
})
const dlg = dialog.create({
title: '创建json',
content: () => (
<NInput
value={name.value}
onUpdateValue={v => (name.value = v)}
placeholder={'文件名'}
></NInput>
),
action: () => (
<NButton
disabled={!name.value || pathExists.value}
onClick={() => {
if (!pathExists.value) {
fs.tree.writeFile(to.value, '{}')
dlg.destroy()
}
}}
>
确认
</NButton>
)
})
export function onNewJson(key: PathKey) {
for (let i = 0; ; i++) {
const name = `__NewJson${i}.json`
const to = path.joinkey(key, name)
if (fs.tree.existsFile(to)) {
continue
}
fs.tree.writeFile(to, '{}')
break
}
}

export function onNewTask(dir: PathSegments, file: string) {
Expand Down Expand Up @@ -160,3 +125,44 @@ export function onDeleteFile(dir: PathSegments, file: string) {
fs.tree.removeFile(p)
})
}

export function onEnterRename(key: PathKey) {
if (key === '/') {
return
}
const [dir, file, hash] = path.divide(key)
if (hash) {
// Not supported yet
return
}
renameKey.value = key
renameInto.value = file
}

export function onLeaveRename() {
const key = renameKey.value as PathKey | null
renameKey.value = null
if (!key) {
return
}
if (path.key_is_dir(key)) {
const [dir, file] = path.divide(key)
fs.tree.renameDir(key, path.joinkey(dir, renameInto.value!))
} else {
const [dir, file] = path.divide(key)
const to = path.joinkey(dir, renameInto.value!)

fs.scope(() => {
fs.tree.renameFile(key, to)

if (file.endsWith('.png')) {
const zf = path.seg_to_zip(path.to_seg(key))
const tf = path.seg_to_zip(path.join(dir, renameInto.value!))

filterTemplate(temp => {
return temp === zf ? tf : temp
})
}
})
}
}
26 changes: 23 additions & 3 deletions src/components/tree/TaskTreeRender.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,43 @@ import {
ImageOutlined,
InsertDriveFileOutlined
} from '@vicons/material'
import { NIcon, type TreeOption, useDialog } from 'naive-ui'
import { NIcon, NInput, type TreeOption, useDialog } from 'naive-ui'
import { ref } from 'vue'

import {
onDeleteFile,
onLeaveRename,
onNewFolder,
onNewJson,
onNewTask,
onUploadImage
} from './TaskTreeAction'

import { renameInto, renameKey } from '@/data'
import { type PathKey, path } from '@/filesystem'

import AutoFocusInput from '@/components/atomic/AutoFocusInput.vue'
import IconButton from '@/components/atomic/IconButton.vue'

export function renderLabel({ option }: { option: TreeOption }) {
const key = option.key as PathKey

if (renameKey.value === key) {
return (
<div class="flex items-center">
<AutoFocusInput
value={renameInto.value ?? ''}
onUpdate:value={v => {
renameInto.value = v
}}
onBlur={() => {
onLeaveRename()
}}
></AutoFocusInput>
</div>
)
}

if (!path.key_is_dir(key)) {
const [dir, file, hash] = path.divide(key)
if (hash) {
Expand Down Expand Up @@ -96,13 +116,13 @@ export function renderSuffix({ option }: { option: TreeOption }) {
<IconButton
icon={AddOutlined}
onClick={() => {
onNewJson(dialog, key)
onNewJson(key)
}}
></IconButton>
<IconButton
icon={CreateNewFolderOutlined}
onClick={() => {
onNewFolder(dialog, key)
onNewFolder(key)
}}
></IconButton>
</div>
Expand Down
5 changes: 4 additions & 1 deletion src/data/filesystem.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import type { TreeOption, TreeSelectOption } from 'naive-ui'
import { computed } from 'vue'
import { computed, ref } from 'vue'

import { type PathSegments, fs, path } from '@/filesystem'
import type { TaskData } from '@/types'

export const renameKey = ref<string | null>(null)
export const renameInto = ref<string | null>(null)

export const filesystemTree = computed<TreeOption>(() => {
const rootOption: TreeOption = {
key: '/',
Expand Down
1 change: 1 addition & 0 deletions src/data/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export * from './config'
export * from './filesystem'
export * from './history'
export * from './image'
export * from './refactor'
export * from './task'

export const active = computed(() => {
Expand Down
31 changes: 31 additions & 0 deletions src/data/refactor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { getTask, setTask, taskIndex } from '.'

import type { Task } from '@/types'

export function editAllTask(mapper: (task: Task) => Task) {
for (const name in taskIndex.value) {
const key = taskIndex.value[name]
const task = getTask(key)!
setTask(key, mapper(task))
}
}

export function filterTemplate(mapper: (from: string) => string) {
editAllTask(task => {
if (task.template) {
if (typeof task.template === 'string') {
return {
...task,
template: mapper(task.template)
}
} else {
return {
...task,
template: task.template.map(mapper)
}
}
} else {
return task
}
})
}
2 changes: 1 addition & 1 deletion src/data/task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export function delTask(p: PathKey | null) {
}

export function getTask(p: PathKey | null) {
if (!p) {
if (!p || p === '/') {
return
}
const [dir, file, hash] = path.divide(p)
Expand Down
3 changes: 1 addition & 2 deletions src/filesystem/history.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,7 @@ export function initFilesystem() {
function scope(action: () => void) {
history.pause()
action()
history.resume()
history.commit()
history.resume(true)
}

return { tree, history, loadZip, saveZip, scope }
Expand Down
4 changes: 4 additions & 0 deletions src/filesystem/path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ export function seg_to_path(seg: PathSegments): Path {
return ('/' + seg.join('/')).replace(/\/+/, '/') as Path
}

export function seg_to_zip(seg: PathSegments): PathZip {
return seg.join('/').replace(/\/+/, '/') as PathZip
}

export function dir_to_key(path: Path): PathKey {
return (path.replace(/\/+/, '/') + '/') as PathKey
}
Expand Down
7 changes: 7 additions & 0 deletions src/views/EditView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { NButton, NCard, NIcon } from 'naive-ui'
import { onMounted, onUnmounted, ref } from 'vue'
import { useRouter } from 'vue-router'
import { onEnterRename } from '@/components/tree/TaskTreeAction'
import { active, getTask, history, setTask } from '@/data'
import { type PathKey, fs } from '@/filesystem'
import { loadFS, saveCfg, saveFS } from '@/loader'
Expand All @@ -28,6 +29,12 @@ const router = useRouter()
const expands = ref<PathKey[]>(['/' as PathKey])
function handleKey(ev: KeyboardEvent) {
if (ev.key === 'F2') {
if (active.value) {
onEnterRename(active.value)
}
return
}
if (ev.ctrlKey) {
if (ev.key === 'z' || ev.key === 'Z') {
ev.stopPropagation()
Expand Down

0 comments on commit 470d6b5

Please sign in to comment.