Files
nocodb/packages/nc-gui/components/nc/List/SourceSelector.vue
2026-02-24 14:03:22 +00:00

132 lines
3.6 KiB
Vue

<script lang="ts" setup>
interface Props {
baseId: string
sourceId?: string
showSourceSelector?: boolean
forceLayout?: 'vertical' | 'horizontal'
}
const props = withDefaults(defineProps<Props>(), {
showSourceSelector: true,
})
const emits = defineEmits(['update:sourceId'])
const { baseId } = toRefs(props)
const { t } = useI18n()
const { bases } = storeToRefs(useBases())
const base = computed(() => bases.value.get(baseId.value))
const sourceId = useVModel(props, 'sourceId', emits, {
defaultValue: undefined,
passive: true,
})
const isOpenSourceSelectDropdown = ref(false)
const sourceList = computed(() => {
return (base.value?.sources || [])?.map((source, idx) => {
const isHidden = source.enabled === false
const ncItemTooltip = isHidden
? t('tooltip.sourceVisibilityIsHidden')
: source.is_schema_readonly
? t('tooltip.schemaChangeDisabled')
: ''
let sourceLabel = t('general.default')
if (idx !== 0 && (source.is_meta || source.is_local)) {
sourceLabel = t('general.base')
} else if (idx !== 0) {
sourceLabel = source.alias || source.id!
}
return {
label: sourceLabel,
value: source.id,
ncItemDisabled: isHidden || source.is_schema_readonly,
ncItemTooltip,
...source,
}
})
})
const selectedSource = computed(() => {
if (!sourceList.value.length) return undefined
return sourceList.value.find((source) => sourceId.value && source.value === sourceId.value) || sourceList.value[0]
})
onMounted(() => {
const newSourceId = sourceId.value || sourceList.value[0]?.value
const sourceObj = sourceList.value.find((source) => source.value === newSourceId)
// Change source id only if it is default source selected initially and its not enabled
if (sourceObj && sourceObj.ncItemDisabled && sourceObj.value === sourceList.value[0]?.value) {
sourceId.value = sourceList.value.find((source) => !source.ncItemDisabled)?.value || sourceList.value[0]?.value
} else {
sourceId.value = newSourceId
}
})
const onUpdateValue = (value: string) => {
sourceId.value = value
}
defineExpose({
sourceId,
selectedSource,
isOpenSourceSelectDropdown,
})
</script>
<template>
<a-form-item
v-if="selectedSource"
name="sourceId"
class="!mb-0 nc-source-selector"
:class="`nc-force-layout-${forceLayout}`"
:validate-status="selectedSource?.ncItemDisabled ? 'error' : ''"
:help="selectedSource?.ncItemDisabled ? [selectedSource.ncItemTooltip] : []"
@click.stop
@dblclick.stop
>
<template #label>
<div>{{ t('general.datasource') }}</div>
</template>
<NcListDropdown
v-model:is-open="isOpenSourceSelectDropdown"
:disabled="!showSourceSelector"
:has-error="!!selectedSource?.ncItemDisabled"
>
<div class="flex-1 flex items-center gap-2">
<span class="text-sm flex-1">{{ selectedSource?.label || t('general.default') }}</span>
<GeneralIcon
icon="ncChevronDown"
class="flex-none h-4 w-4 transition-transform opacity-70"
:class="{ 'transform rotate-180': isOpenSourceSelectDropdown }"
/>
</div>
<template #overlay="{ onEsc }">
<NcList
v-model:open="isOpenSourceSelectDropdown"
:value="sourceId || selectedSource?.value || ''"
:list="sourceList"
variant="medium"
class="!w-auto"
wrapper-class-name="!h-auto"
@update:value="onUpdateValue($event as string)"
@escape="onEsc"
>
</NcList>
</template>
</NcListDropdown>
</a-form-item>
</template>