mirror of
https://github.com/nocodb/nocodb.git
synced 2026-05-01 17:17:06 +00:00
fix: base list modal oss changes
This commit is contained in:
277
packages/nc-gui/components/workspace/BaseListModal/BaseNode.vue
Normal file
277
packages/nc-gui/components/workspace/BaseListModal/BaseNode.vue
Normal file
@@ -0,0 +1,277 @@
|
||||
<script lang="ts" setup>
|
||||
import { useBaseActions } from './useBaseActions'
|
||||
|
||||
const props = defineProps<{
|
||||
base: NcProject
|
||||
isMarked?: boolean
|
||||
// Indicator icons - shown when base has attribute but displayed in another section
|
||||
showStarIndicator?: boolean
|
||||
showPrivateIndicator?: boolean
|
||||
}>()
|
||||
|
||||
// Get actions from provider
|
||||
const { onRename, onToggleStarred, onDuplicate, onOpenErd, onOpenSettings, onDelete, onUpdateColor, onSelect } =
|
||||
useBaseActions()
|
||||
|
||||
const { isUIAllowed } = useRoles()
|
||||
const { $e } = useNuxtApp()
|
||||
const { showRecordPlanLimitExceededModal } = useEeConfig()
|
||||
|
||||
// Local state
|
||||
const isMenuOpen = ref(false)
|
||||
const editMode = ref(false)
|
||||
const tempTitle = ref('')
|
||||
const inputRef = useTemplateRef('inputRef')
|
||||
|
||||
// Computed
|
||||
const iconColor = computed(() => parseProp(props.base.meta).iconColor)
|
||||
const baseRole = computed(() => props.base.project_role)
|
||||
|
||||
const isOptionVisible = computed(() => ({
|
||||
baseRename: isUIAllowed('baseRename'),
|
||||
baseDuplicate: isUIAllowed('baseDuplicate', { roles: baseRole.value }),
|
||||
baseMiscSettings: isUIAllowed('baseMiscSettings'),
|
||||
baseDelete: isUIAllowed('baseDelete', { roles: baseRole.value }),
|
||||
}))
|
||||
|
||||
// Handlers
|
||||
const handleSelect = () => {
|
||||
if (editMode.value) return
|
||||
onSelect(props.base)
|
||||
}
|
||||
|
||||
const enableEditMode = () => {
|
||||
if (!isOptionVisible.value.baseRename) return
|
||||
|
||||
editMode.value = true
|
||||
tempTitle.value = props.base.title || ''
|
||||
isMenuOpen.value = false
|
||||
|
||||
nextTick(() => {
|
||||
inputRef.value?.focus()
|
||||
inputRef.value?.select()
|
||||
})
|
||||
}
|
||||
|
||||
const updateTitle = () => {
|
||||
if (tempTitle.value?.trim()) {
|
||||
tempTitle.value = tempTitle.value.trim()
|
||||
}
|
||||
|
||||
if (!tempTitle.value || tempTitle.value === props.base.title) {
|
||||
editMode.value = false
|
||||
tempTitle.value = ''
|
||||
return
|
||||
}
|
||||
|
||||
onRename(props.base, tempTitle.value)
|
||||
editMode.value = false
|
||||
tempTitle.value = ''
|
||||
}
|
||||
|
||||
const handleToggleStarred = () => {
|
||||
onToggleStarred(props.base)
|
||||
isMenuOpen.value = false
|
||||
}
|
||||
|
||||
const handleDuplicate = () => {
|
||||
if (showRecordPlanLimitExceededModal()) return
|
||||
onDuplicate(props.base)
|
||||
isMenuOpen.value = false
|
||||
}
|
||||
|
||||
const handleOpenErd = () => {
|
||||
const source = props.base.sources?.[0]
|
||||
if (source) {
|
||||
onOpenErd(props.base, source)
|
||||
}
|
||||
isMenuOpen.value = false
|
||||
}
|
||||
|
||||
const handleOpenSettings = () => {
|
||||
onOpenSettings(props.base.id!)
|
||||
isMenuOpen.value = false
|
||||
}
|
||||
|
||||
const handleDelete = () => {
|
||||
onDelete(props.base)
|
||||
isMenuOpen.value = false
|
||||
}
|
||||
|
||||
const handleColorChange = (color: string) => {
|
||||
onUpdateColor(props.base, color)
|
||||
}
|
||||
|
||||
const onMenuClick = (e: Event) => {
|
||||
e.stopPropagation()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
:tabindex="0"
|
||||
class="nc-base-node group relative flex items-center gap-3 p-3 rounded-xl cursor-pointer border-1 transition-all border-nc-border-gray-medium hover:border-nc-border-gray-dark hover:shadow-sm"
|
||||
:class="{ 'is-marked': isMarked, 'is-editing': editMode }"
|
||||
@click="handleSelect"
|
||||
@keydown.enter.stop="handleSelect"
|
||||
>
|
||||
<!-- Project Icon with Color Picker -->
|
||||
<GeneralBaseIconColorPicker
|
||||
:managed-app="{
|
||||
managed_app_master: base.managed_app_master,
|
||||
managed_app_id: base.managed_app_id,
|
||||
}"
|
||||
:key="`${base.id}_${iconColor}`"
|
||||
:type="base?.type"
|
||||
:model-value="iconColor"
|
||||
size="small"
|
||||
:readonly="!isOptionVisible.baseRename"
|
||||
@update:model-value="handleColorChange"
|
||||
@click.stop
|
||||
/>
|
||||
|
||||
<div class="flex-1 min-w-0 min-h-[28px] flex items-center">
|
||||
<!-- Inline Edit Input -->
|
||||
<a-input
|
||||
v-if="editMode"
|
||||
ref="inputRef"
|
||||
v-model:value="tempTitle"
|
||||
class="!bg-transparent !text-sm !font-medium !rounded-md !px-1 !h-7 !-ml-1.2"
|
||||
@click.stop
|
||||
@keyup.enter="updateTitle"
|
||||
@keyup.esc="updateTitle"
|
||||
@blur="updateTitle"
|
||||
@keydown.stop
|
||||
/>
|
||||
<!-- Title Display -->
|
||||
<NcTooltip v-else show-on-truncate-only class="min-w-0 truncate text-sm font-medium">
|
||||
{{ base.title }}
|
||||
|
||||
<template #title>{{ base.title }}</template>
|
||||
</NcTooltip>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center space-x-2">
|
||||
<!-- Indicator icons when base has attribute but shown in another section -->
|
||||
<div v-if="showStarIndicator || showPrivateIndicator" class="flex items-center gap-1">
|
||||
<NcTooltip v-if="showStarIndicator" class="flex">
|
||||
<GeneralIcon icon="star" class="flex-none w-3.5 h-3.5 text-nc-content-gray-muted" />
|
||||
<template #title>{{ $t('general.starred') }}</template>
|
||||
</NcTooltip>
|
||||
<NcTooltip v-if="showPrivateIndicator" class="flex">
|
||||
<GeneralIcon icon="ncLock" class="flex-none w-3.5 h-3.5 text-nc-content-gray-muted" />
|
||||
<template #title>{{ $t('general.private') }}</template>
|
||||
</NcTooltip>
|
||||
</div>
|
||||
|
||||
<!-- More Options Button -->
|
||||
<div v-if="!editMode" class="nc-base-node-menu-wrapper" :class="{ 'is-open': isMenuOpen }">
|
||||
<NcDropdown
|
||||
v-model:visible="isMenuOpen"
|
||||
:trigger="['click']"
|
||||
placement="bottomRight"
|
||||
overlay-class-name="nc-base-node-menu"
|
||||
>
|
||||
<NcButton :tabindex="-1" type="text" size="xsmall" class="nc-base-node-menu-btn" @click.stop="onMenuClick">
|
||||
<GeneralIcon icon="threeDotVertical" class="text-nc-content-gray-muted" />
|
||||
</NcButton>
|
||||
|
||||
<template #overlay>
|
||||
<NcMenu class="!min-w-50" variant="small">
|
||||
<!-- Copy Base ID -->
|
||||
<NcMenuItemCopyId
|
||||
:id="base.id"
|
||||
:tooltip="$t('labels.clickToCopyBaseID')"
|
||||
:label="$t('labels.baseIdColon', { baseId: base.id })"
|
||||
/>
|
||||
<NcDivider />
|
||||
|
||||
<!-- Rename -->
|
||||
<NcMenuItem v-if="isOptionVisible.baseRename" data-testid="nc-base-node-rename" @click="enableEditMode">
|
||||
<GeneralIcon icon="rename" />
|
||||
{{ $t('general.rename') }} {{ $t('objects.project').toLowerCase() }}
|
||||
</NcMenuItem>
|
||||
|
||||
<!-- Toggle Starred -->
|
||||
<NcMenuItem data-testid="nc-base-node-starred" @click="handleToggleStarred">
|
||||
<GeneralIcon v-if="base.starred" icon="unStar" />
|
||||
<GeneralIcon v-else icon="star" />
|
||||
{{ base.starred ? $t('activity.removeFromStarred') : $t('activity.addToStarred') }}
|
||||
</NcMenuItem>
|
||||
|
||||
<!-- Duplicate -->
|
||||
<NcMenuItem v-if="isOptionVisible.baseDuplicate" data-testid="nc-base-node-duplicate" @click="handleDuplicate">
|
||||
<GeneralIcon icon="duplicate" />
|
||||
{{ $t('general.duplicate') }} {{ $t('objects.project').toLowerCase() }}
|
||||
</NcMenuItem>
|
||||
|
||||
<NcDivider />
|
||||
|
||||
<!-- ERD View -->
|
||||
<NcMenuItem v-if="base?.sources?.[0]?.enabled" data-testid="nc-base-node-erd" @click="handleOpenErd">
|
||||
<GeneralIcon icon="ncErd" />
|
||||
{{ $t('title.relations') }}
|
||||
</NcMenuItem>
|
||||
|
||||
<!-- Settings -->
|
||||
<NcMenuItem v-if="isOptionVisible.baseMiscSettings" data-testid="nc-base-node-settings" @click="handleOpenSettings">
|
||||
<GeneralIcon icon="settings" />
|
||||
{{ $t('activity.settings') }}
|
||||
</NcMenuItem>
|
||||
|
||||
<template v-if="isOptionVisible.baseDelete">
|
||||
<NcDivider />
|
||||
|
||||
<!-- Delete -->
|
||||
<NcMenuItem danger data-testid="nc-base-node-delete" @click="handleDelete">
|
||||
<GeneralIcon icon="delete" />
|
||||
{{ $t('general.delete') }} {{ $t('objects.project').toLowerCase() }}
|
||||
</NcMenuItem>
|
||||
</template>
|
||||
</NcMenu>
|
||||
</template>
|
||||
</NcDropdown>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.nc-base-node {
|
||||
@apply bg-white dark:bg-nc-bg-gray-light;
|
||||
|
||||
&:hover,
|
||||
&:focus-within {
|
||||
@apply bg-nc-bg-gray-light dark:bg-nc-bg-gray-medium;
|
||||
|
||||
.nc-base-node-menu-wrapper {
|
||||
@apply w-6 !flex;
|
||||
}
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
@apply outline-none shadow-focus;
|
||||
|
||||
.nc-base-node-menu-wrapper {
|
||||
@apply w-6 !flex;
|
||||
}
|
||||
}
|
||||
|
||||
&.is-marked {
|
||||
@apply bg-nc-bg-gray-medium border-nc-border-brand;
|
||||
}
|
||||
|
||||
&.is-editing {
|
||||
@apply cursor-default;
|
||||
}
|
||||
}
|
||||
|
||||
.nc-base-node-menu-wrapper {
|
||||
@apply w-0 hidden overflow-hidden items-center justify-center;
|
||||
@apply transition-all duration-200 ease-in-out;
|
||||
|
||||
&.is-open {
|
||||
@apply w-6 !flex;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user