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.
100 lines
2.9 KiB
100 lines
2.9 KiB
2 weeks ago
|
<script lang="ts" setup>
|
||
|
interface Props {
|
||
|
activeTab: string
|
||
|
tabs: {
|
||
|
title: string
|
||
|
key: string
|
||
|
hidden?: boolean
|
||
|
}[]
|
||
|
as?: 'default' | 'tabs'
|
||
|
contentClassName?: string
|
||
|
}
|
||
|
|
||
|
const props = withDefaults(defineProps<Props>(), {
|
||
|
as: 'default',
|
||
|
contentClassName: '',
|
||
|
})
|
||
|
|
||
|
const emits = defineEmits(['update:activeTab', 'navigateToIntegrations'])
|
||
|
|
||
|
const activeTab = useVModel(props, 'activeTab', emits)
|
||
|
|
||
|
const { tabs, as, contentClassName } = toRefs(props)
|
||
|
|
||
|
const { aiIntegrationAvailable, aiLoading } = useNocoAi()
|
||
|
|
||
|
const handleChangeTab = (tab: string) => {
|
||
|
if (aiLoading.value) return
|
||
|
activeTab.value = tab
|
||
|
}
|
||
|
</script>
|
||
|
|
||
|
<template>
|
||
|
<div class="nc-ai-wizard-card">
|
||
|
<div class="nc-ai-wizard-card-tab-header">
|
||
|
<div class="flex nc-ai-wizard-card-tab-wrapper">
|
||
|
<template v-for="tab of tabs" :key="tab.key">
|
||
|
<div
|
||
|
v-if="!tab.hidden"
|
||
|
class="nc-ai-wizard-card-tab"
|
||
|
:class="{
|
||
|
'active-tab': activeTab === tab.key,
|
||
|
'!cursor-wait': aiLoading,
|
||
|
}"
|
||
|
@click="handleChangeTab(tab.key)"
|
||
|
>
|
||
|
{{ tab.title }}
|
||
|
</div>
|
||
|
</template>
|
||
|
</div>
|
||
|
<div class="nc-ai-wizard-card-tab-extra-right">
|
||
|
<slot name="tabExtraRight"></slot>
|
||
|
</div>
|
||
|
</div>
|
||
|
<div class="nc-ai-wizard-card-tab-content" :class="contentClassName">
|
||
|
<div v-if="as === 'default' && !aiIntegrationAvailable" class="py-2.5 pl-3 pr-2 flex items-center gap-3">
|
||
|
<GeneralIcon icon="alertTriangleSolid" class="!text-nc-content-orange-medium w-4 h-4" />
|
||
|
<div class="text-sm text-nc-content-gray-subtle flex-1">{{ $t('title.noAiIntegrationAvailable') }}</div>
|
||
|
|
||
|
<NcButton size="small" type="text" class="!text-nc-content-brand" @click.stop="emits('navigateToIntegrations')">
|
||
|
{{ $t('labels.createAiIntegration') }}
|
||
|
</NcButton>
|
||
|
</div>
|
||
|
<slot v-else name="tabContent"> </slot>
|
||
|
</div>
|
||
|
</div>
|
||
|
</template>
|
||
|
|
||
|
<style lang="scss" scoped>
|
||
|
.nc-ai-wizard-card {
|
||
|
@apply border-1 border-purple-200 rounded-lg overflow-y-auto nc-scrollbar-thin transition-colors relative;
|
||
|
|
||
|
.nc-ai-wizard-card-tab-header {
|
||
|
@apply bg-nc-bg-purple-light flex justify-between h-10 -mt-[1px] -ml-[1px] border-b-1 border-purple-200 sticky -top-[1px] z-1;
|
||
|
|
||
|
.nc-ai-wizard-card-tab {
|
||
|
@apply relative px-4 py-2 text-sm cursor-pointer rounded-t-lg border-t-1 border-x-1;
|
||
|
|
||
|
&.active-tab {
|
||
|
@apply text-nc-content-purple-dark bg-white border-purple-200 font-semibold;
|
||
|
|
||
|
&::after {
|
||
|
@apply absolute content-[''] -bottom-[1px] left-0 right-0 border-b-1 border-white;
|
||
|
}
|
||
|
}
|
||
|
&:not(.active-tab) {
|
||
|
@apply text-nc-content-purple-light border-transparent font-weight-500;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
.nc-ai-wizard-card-tab-extra-right {
|
||
|
@apply flex items-center gap-2 pr-2 text-nc-content-purple-dark;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
.nc-ai-wizard-card-tab-content {
|
||
|
@apply bg-white;
|
||
|
}
|
||
|
}
|
||
|
</style>
|