mirror of https://github.com/nocodb/nocodb
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
197 lines
5.1 KiB
197 lines
5.1 KiB
2 years ago
|
<script lang="ts" setup>
|
||
|
import type { ViewTypes } from 'nocodb-sdk'
|
||
2 years ago
|
import { message } from 'ant-design-vue'
|
||
2 years ago
|
import { viewIcons } from '~/utils'
|
||
2 years ago
|
import { IsLockedInj, onKeyStroke, useDebounceFn, useNuxtApp, useUIPermission, useVModel } from '#imports'
|
||
2 years ago
|
|
||
|
interface Props {
|
||
|
view: Record<string, any>
|
||
2 years ago
|
onValidate: (view: Record<string, any>) => boolean | string
|
||
2 years ago
|
}
|
||
|
|
||
|
interface Emits {
|
||
|
(event: 'update:view', data: Record<string, any>): void
|
||
|
(event: 'changeView', view: Record<string, any>): void
|
||
|
(event: 'rename', view: Record<string, any>): void
|
||
|
(event: 'delete', view: Record<string, any>): void
|
||
2 years ago
|
(event: 'openModal', data: { type: ViewTypes; title?: string; copyViewId?: string }): void
|
||
2 years ago
|
}
|
||
|
|
||
|
const props = defineProps<Props>()
|
||
|
|
||
|
const emits = defineEmits<Emits>()
|
||
|
|
||
|
const vModel = useVModel(props, 'view', emits)
|
||
|
|
||
|
const { $e } = useNuxtApp()
|
||
|
|
||
2 years ago
|
const { isUIAllowed } = useUIPermission()
|
||
|
|
||
2 years ago
|
const isLocked = inject(IsLockedInj)
|
||
|
|
||
2 years ago
|
/** Is editing the view name enabled */
|
||
|
let isEditing = $ref<boolean>(false)
|
||
|
|
||
|
/** Helper to check if editing was disabled before the view navigation timeout triggers */
|
||
|
let isStopped = $ref(false)
|
||
|
|
||
|
/** Original view title when editing the view name */
|
||
|
let originalTitle = $ref<string | undefined>()
|
||
|
|
||
|
/** Debounce click handler, so we can potentially enable editing view name {@see onDblClick} */
|
||
|
const onClick = useDebounceFn(() => {
|
||
|
if (isEditing || isStopped) return
|
||
|
|
||
|
emits('changeView', vModel.value)
|
||
|
}, 250)
|
||
|
|
||
|
/** Enable editing view name on dbl click */
|
||
|
function onDblClick() {
|
||
|
if (!isEditing) {
|
||
|
isEditing = true
|
||
|
originalTitle = vModel.value.title
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** Handle keydown on input field */
|
||
|
function onKeyDown(event: KeyboardEvent) {
|
||
|
if (event.key === 'Escape') {
|
||
|
onKeyEsc(event)
|
||
|
} else if (event.key === 'Enter') {
|
||
|
onKeyEnter(event)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** Rename view when enter is pressed */
|
||
|
function onKeyEnter(event: KeyboardEvent) {
|
||
|
event.stopImmediatePropagation()
|
||
|
event.preventDefault()
|
||
|
|
||
|
onRename()
|
||
|
}
|
||
|
|
||
|
/** Disable renaming view when escape is pressed */
|
||
|
function onKeyEsc(event: KeyboardEvent) {
|
||
|
event.stopImmediatePropagation()
|
||
|
event.preventDefault()
|
||
|
|
||
|
onCancel()
|
||
|
}
|
||
|
|
||
|
onKeyStroke('Enter', (event) => {
|
||
|
if (isEditing) {
|
||
|
onKeyEnter(event)
|
||
|
}
|
||
|
})
|
||
|
|
||
|
function focusInput(el: HTMLInputElement) {
|
||
|
if (el) el.focus()
|
||
|
}
|
||
|
|
||
|
/** Duplicate a view */
|
||
|
// todo: This is not really a duplication, maybe we need to implement a true duplication?
|
||
|
function onDuplicate() {
|
||
2 years ago
|
emits('openModal', { type: vModel.value.type, title: vModel.value.title, copyViewId: vModel.value.id })
|
||
2 years ago
|
|
||
|
$e('c:view:copy', { view: vModel.value.type })
|
||
|
}
|
||
|
|
||
|
/** Delete a view */
|
||
|
async function onDelete() {
|
||
|
emits('delete', vModel.value)
|
||
|
}
|
||
|
|
||
|
/** Rename a view */
|
||
|
async function onRename() {
|
||
|
if (!isEditing) return
|
||
|
|
||
2 years ago
|
const isValid = props.onValidate(vModel.value)
|
||
|
|
||
|
if (isValid !== true) {
|
||
2 years ago
|
message.error(isValid)
|
||
2 years ago
|
|
||
|
onCancel()
|
||
|
return
|
||
|
}
|
||
|
|
||
2 years ago
|
if (vModel.value.title === '' || vModel.value.title === originalTitle) {
|
||
|
onCancel()
|
||
|
return
|
||
|
}
|
||
|
|
||
|
emits('rename', vModel.value)
|
||
|
|
||
|
onStopEdit()
|
||
|
}
|
||
|
|
||
|
/** Cancel renaming view */
|
||
|
function onCancel() {
|
||
|
if (!isEditing) return
|
||
|
|
||
|
vModel.value.title = originalTitle
|
||
|
onStopEdit()
|
||
|
}
|
||
|
|
||
|
/** Stop editing view name, timeout makes sure that view navigation (click trigger) does not pick up before stop is done */
|
||
|
function onStopEdit() {
|
||
|
isStopped = true
|
||
|
isEditing = false
|
||
|
originalTitle = ''
|
||
|
|
||
|
setTimeout(() => {
|
||
|
isStopped = false
|
||
|
}, 250)
|
||
|
}
|
||
|
</script>
|
||
|
|
||
|
<template>
|
||
2 years ago
|
<a-menu-item
|
||
2 years ago
|
class="select-none group !flex !items-center !my-0 hover:(bg-primary !bg-opacity-5)"
|
||
2 years ago
|
@dblclick.stop="isUIAllowed('virtualViewsCreateOrEdit') && onDblClick()"
|
||
2 years ago
|
@click.stop="onClick"
|
||
|
>
|
||
2 years ago
|
<div v-t="['a:view:open', { view: vModel.type }]" class="text-xs flex items-center w-full gap-2">
|
||
2 years ago
|
<div class="flex w-auto">
|
||
|
<MdiDrag
|
||
2 years ago
|
class="nc-drag-icon hidden group-hover:block transition-opacity opacity-0 group-hover:opacity-100 text-gray-500 !cursor-move"
|
||
2 years ago
|
@click.stop.prevent
|
||
2 years ago
|
/>
|
||
|
|
||
2 years ago
|
<component
|
||
|
:is="viewIcons[vModel.type].icon"
|
||
|
class="nc-view-icon group-hover:hidden"
|
||
2 years ago
|
:style="{ color: viewIcons[vModel?.type]?.color }"
|
||
2 years ago
|
/>
|
||
2 years ago
|
</div>
|
||
2 years ago
|
|
||
|
<a-input v-if="isEditing" :ref="focusInput" v-model:value="vModel.title" @blur="onCancel" @keydown="onKeyDown($event)" />
|
||
2 years ago
|
|
||
2 years ago
|
<div v-else>{{ vModel.alias || vModel.title }}</div>
|
||
|
|
||
|
<div class="flex-1" />
|
||
|
|
||
2 years ago
|
<template v-if="!isEditing && !isLocked && isUIAllowed('virtualViewsCreateOrEdit')">
|
||
2 years ago
|
<div class="flex items-center gap-1">
|
||
|
<a-tooltip placement="left">
|
||
|
<template #title>
|
||
|
{{ $t('activity.copyView') }}
|
||
|
</template>
|
||
|
|
||
|
<MdiContentCopy class="hidden group-hover:block text-gray-500" @click.stop="onDuplicate" />
|
||
|
</a-tooltip>
|
||
|
|
||
2 years ago
|
<template v-if="!vModel.is_default">
|
||
2 years ago
|
<a-tooltip placement="left">
|
||
|
<template #title>
|
||
|
{{ $t('activity.deleteView') }}
|
||
|
</template>
|
||
|
|
||
2 years ago
|
<MdiTrashCan class="hidden group-hover:block text-red-500 nc-view-delete-icon" @click.stop="onDelete" />
|
||
2 years ago
|
</a-tooltip>
|
||
2 years ago
|
</template>
|
||
2 years ago
|
</div>
|
||
|
</template>
|
||
|
</div>
|
||
|
</a-menu-item>
|
||
|
</template>
|