blob: 034889c7500fb18f1495b35d46bf98f66b19a59b [file] [log] [blame]
/*
* 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 {
defineComponent,
PropType,
h,
VNodeChild,
Ref,
ref,
onMounted,
onUnmounted,
nextTick
} from 'vue'
import { NTree, NInput, NDropdown } from 'naive-ui'
import { FILE_TYPES_SUFFIX } from '@/constants/file'
import { getNameByType } from '@/utils/file'
import { useLocale } from '@/hooks'
import styles from './index.module.scss'
import type { IFileRecord, TreeOption } from '@/types/file'
const props = {
data: {
type: Array as PropType<IFileRecord[]>,
default: []
},
inputRef: {
type: Object as PropType<Ref>
},
searchValueRef: {
type: Object as PropType<Ref>
}
}
export const Files = defineComponent({
name: 'files',
props,
emits: ['select', 'inputBlur', 'delete', 'rename', 'doubleClick'],
setup(props, { emit, expose }) {
const keyRef = ref()
const treeRef = ref()
const xRef = ref(0)
const yRef = ref(0)
const showDropdownRef = ref(false)
const { t } = useLocale()
let id: any
const onSelect = (keys: string[]) => {
keys[0] && emit('select', keys[0])
}
const onBlur = (ev: FocusEvent) => {
emit('inputBlur', (ev.target as HTMLInputElement)?.value)
}
const renderLabel = (info: { option: TreeOption }): VNodeChild => {
const { isEditing, name, type, id } = info.option as IFileRecord
return !isEditing
? h('div', { 'data-id': id }, type ? getNameByType(type, name) : name)
: h(
NInput,
{
size: 'tiny',
autofocus: true,
onBlur: onBlur,
ref: props.inputRef
},
{
suffix: () => (type ? '.' + FILE_TYPES_SUFFIX[type] : '')
}
)
}
const onContextMenu = (ev: MouseEvent) => {
ev.preventDefault()
id = (ev.target as HTMLElement)?.dataset.id
if (id) {
xRef.value = ev.clientX
yRef.value = ev.clientY
showDropdownRef.value = true
}
}
const onContextMenuSelect = (key: 'delete' | 'rename') => {
showDropdownRef.value = false
emit(key, Number(id))
}
const onClickoutside = () => {
showDropdownRef.value = false
}
const onDoubleClick = (ev: MouseEvent) => {
id = (ev.target as HTMLElement)?.dataset.id
if (id) emit('doubleClick', Number(id))
}
const bindEvents = () => {
treeRef.value?.selfElRef.addEventListener('contextmenu', onContextMenu)
treeRef.value?.selfElRef.addEventListener('dblclick', onDoubleClick)
}
const unbindEvents = () => {
treeRef.value?.selfElRef.removeEventListener('contextmenu', onContextMenu)
treeRef.value?.selfElRef.removeEventListener('dblclick', onDoubleClick)
}
const refresh = async () => {
keyRef.value = Date.now()
await nextTick()
bindEvents()
}
expose({ refresh })
onMounted(() => {
bindEvents()
})
onUnmounted(() => {
unbindEvents()
})
return () => (
<div>
<NTree
block-line
selectable
data={props.data}
on-update:selected-keys={onSelect}
class={styles['files']}
renderLabel={renderLabel}
key={keyRef.value}
defaultExpandAll
expand-on-click
labelField='name'
keyField='id'
ref={treeRef}
showIrrelevantNodes={false}
pattern={props.searchValueRef?.value}
></NTree>
<NDropdown
placement='bottom-start'
trigger='manual'
x={xRef.value}
y={yRef.value}
options={[
{ key: 'delete', label: t('delete') },
{ key: 'rename', label: t('rename') }
]}
show={showDropdownRef.value}
onClickoutside={onClickoutside}
onSelect={onContextMenuSelect}
/>
</div>
)
}
})