Browse Source

feat(nc-gui): customizable extension header

pull/9351/head
Ramesh Mane 3 months ago
parent
commit
d09e2785eb
  1. 50
      packages/nc-gui/components/extensions/Extension.vue
  2. 113
      packages/nc-gui/components/extensions/Extension/Header.vue
  3. 12
      packages/nc-gui/components/extensions/Extension/HeaderMenu.vue
  4. 30
      packages/nc-gui/components/extensions/Extension/Wrapper.vue
  5. 320
      packages/nc-gui/extensions/data-exporter/index.vue
  6. 12
      packages/nc-gui/extensions/json-exporter/index.vue

50
packages/nc-gui/components/extensions/Extension.vue

@ -222,56 +222,10 @@ eventBus.on((event, payload) => {
: {} : {}
" "
> >
<div
v-if="fullscreen"
class="flex items-center gap-3 cursor-default px-4 pt-4 pb-[15px] border-b-1 border-nc-gray-medium"
>
<img
v-if="extensionManifest"
:src="getExtensionAssetsUrl(extensionManifest.iconUrl)"
alt="icon"
class="flex-none w-8 h-8"
/>
<div v-if="titleEditMode" class="flex-1 flex">
<a-input
ref="titleInput"
v-model:value="tempTitle"
type="text"
class="flex-1 flex-grow !h-8 !px-1 !py-1 !-ml-1 !rounded-lg !text-lg font-semibold extension-title max-w-[420px]"
@click.stop
@keyup.enter.stop="updateExtensionTitle"
@keyup.esc.stop="updateExtensionTitle"
@blur="updateExtensionTitle"
>
</a-input>
</div>
<NcTooltip v-else show-on-truncate-only class="extension-title truncate text-lg flex-1">
<template #title>
{{ extension.title }}
</template>
<span class="cursor-pointer text-gray-800 font-semibold" @dblclick="enableEditMode">
{{ extension.title }}
</span>
</NcTooltip>
<ExtensionsExtensionHeaderMenu
:active-error="activeError"
:fullscreen="fullscreen"
@rename="enableEditMode"
@duplicate="handleDuplicateExtension(extension.id, true)"
@show-details="showExtensionDetails(extension.extensionId, 'extension')"
@clear-data="extension.clear()"
@delete="extension.delete()"
/>
<NcButton size="xs" type="text" class="flex-none !px-1" @click="fullscreen = false">
<GeneralIcon icon="close" />
</NcButton>
</div>
<div <div
v-show="fullscreen || !collapsed" v-show="fullscreen || !collapsed"
class="extension-content" class="extension-content h-full"
:class="{ 'fullscreen h-[calc(100%-40px)] p-6': fullscreen, 'h-full': !fullscreen }" :class="{ 'fullscreen': fullscreen, 'h-full': !fullscreen }"
> >
<component :is="component" :key="extension.uiKey" class="h-full" /> <component :is="component" :key="extension.uiKey" class="h-full" />
</div> </div>

113
packages/nc-gui/components/extensions/Extension/Header.vue

@ -1,4 +1,11 @@
<script lang="ts" setup> <script lang="ts" setup>
/**
* ExtensionHeader component.
*
* @slot prefix - Slot for custom content to be displayed at the start of the header when in fullscreen mode.
* @slot extra - Slot for additional custom content to be displayed before the options and close button in fullscreen mode.
*/
interface Props { interface Props {
isFullscreen?: boolean isFullscreen?: boolean
} }
@ -36,6 +43,12 @@ const expandExtension = () => {
collapsed.value = false collapsed.value = false
} }
/**
* Handles the duplication of an extension.
*
* @param id - The ID of the extension to duplicate.
* @param open - Optional. If true, the duplicated extension will be opened.
*/
const handleDuplicateExtension = async (id: string, open: boolean = false) => { const handleDuplicateExtension = async (id: string, open: boolean = false) => {
const duplicatedExt = await duplicateExtension(id) const duplicatedExt = await duplicateExtension(id)
@ -49,72 +62,88 @@ const handleDuplicateExtension = async (id: string, open: boolean = false) => {
<template> <template>
<div <div
class="extension-header px-3 py-2" v-if="(isFullscreen && fullscreen) || !isFullscreen"
class="extension-header flex items-center"
:class="{ :class="{
'border-b-1 border-gray-200 h-[49px]': !collapsed, 'border-b-1 border-nc-gray-medium h-[49px]': !collapsed && !isFullscreen,
'collapsed border-transparent h-[48px]': collapsed, 'collapsed border-transparent h-[48px]': collapsed && !isFullscreen,
'px-3 py-2 gap-1': !isFullscreen,
'gap-3 px-4 pt-4 pb-[15px] border-b-1 border-nc-gray-medium': isFullscreen,
}" }"
@click="expandExtension" @click="expandExtension"
> >
<div class="extension-header-left max-w-[calc(100%_-_100px)]"> <slot v-if="isFullscreen" name="prefix"></slot>
<!-- Todo: enable later when we support extension reordering --> <NcButton v-if="!isFullscreen" size="xs" type="text" class="nc-extension-drag-handler !px-1" @click.stop>
<!-- eslint-disable vue/no-constant-condition --> <GeneralIcon icon="ncDrag" class="flex-none text-gray-500" />
<NcButton size="xs" type="text" class="nc-extension-drag-handler !px-1" @click.stop> </NcButton>
<GeneralIcon icon="ncDrag" class="flex-none text-gray-500" />
</NcButton> <img
v-if="extensionManifest"
<img :src="getExtensionAssetsUrl(extensionManifest.iconUrl)"
v-if="extensionManifest" alt="icon"
:src="getExtensionAssetsUrl(extensionManifest.iconUrl)" class="h-8 w-8 object-contain flex-none"
alt="icon" />
class="h-8 w-8 object-contain" <div v-if="titleEditMode" class="flex-1">
/>
<a-input <a-input
v-if="titleEditMode && !fullscreen"
ref="titleInput" ref="titleInput"
v-model:value="tempTitle" v-model:value="tempTitle"
type="text" type="text"
class="flex-grow !h-8 !px-1 !py-1 !-ml-1 !rounded-lg w-4/5 extension-title" class="flex-1 !h-8 !px-1 !py-1 !-ml-1 !rounded-lg extension-title"
:class="{
'w-4/5': !isFullscreen,
'!text-lg !font-semibold max-w-[420px]': isFullscreen,
}"
@click.stop @click.stop
@keyup.enter="updateExtensionTitle" @keyup.enter="updateExtensionTitle"
@keyup.esc="updateExtensionTitle" @keyup.esc="updateExtensionTitle"
@blur="updateExtensionTitle" @blur="updateExtensionTitle"
> >
</a-input> </a-input>
<NcTooltip v-else show-on-truncate-only class="truncate">
<template #title>
{{ extension.title }}
</template>
<span class="extension-title cursor-pointer" @dblclick.stop="enableEditMode" @click.stop>
{{ extension.title }}
</span>
</NcTooltip>
</div> </div>
<div class="extension-header-right" @click.stop>
<ExtensionsExtensionHeaderMenu <NcTooltip v-else show-on-truncate-only class="truncate flex-1">
:active-error="activeError" <template #title>
class="nc-extension-menu" {{ extension.title }}
@rename="enableEditMode" </template>
@duplicate="handleDuplicateExtension(extension.id, true)" <span
@show-details="showExtensionDetails(extension.extensionId, 'extension')" class="extension-title cursor-pointer"
@clear-data="extension.clear()" :class="{
@delete="extension.delete()" 'text-lg font-semibold ': isFullscreen,
/> }"
<NcButton v-if="!activeError" type="text" size="xs" class="nc-extension-expand-btn !px-1" @click="fullscreen = true"> @dblclick.stop="enableEditMode"
@click.stop
>
{{ extension.title }}
</span>
</NcTooltip>
<slot v-if="isFullscreen" name="extra"></slot>
<ExtensionsExtensionHeaderMenu
:is-fullscreen="isFullscreen"
class="nc-extension-menu"
@rename="enableEditMode"
@duplicate="handleDuplicateExtension(extension.id, true)"
@show-details="showExtensionDetails(extension.extensionId, 'extension')"
@clear-data="extension.clear()"
@delete="extension.delete()"
/>
<template v-if="!isFullscreen">
<NcButton v-if="!activeError" size="xs" type="text" class="nc-extension-expand-btn !px-1" @click.stop="fullscreen = true">
<GeneralIcon icon="ncMaximize2" class="h-3.5 w-3.5" /> <GeneralIcon icon="ncMaximize2" class="h-3.5 w-3.5" />
</NcButton> </NcButton>
<NcButton size="xs" type="text" class="!px-1" @click="collapsed = !collapsed"> <NcButton size="xs" type="text" class="!px-1" @click.stop="collapsed = !collapsed">
<GeneralIcon :icon="collapsed ? 'arrowDown' : 'arrowUp'" class="flex-none" /> <GeneralIcon :icon="collapsed ? 'arrowDown' : 'arrowUp'" class="flex-none" />
</NcButton> </NcButton>
</div> </template>
<template v-else>
<NcButton :size="isFullscreen ? 'small' : 'xs'" type="text" class="flex-none !px-1" @click="fullscreen = false">
<GeneralIcon icon="close" />
</NcButton>
</template>
</div> </div>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
.extension-header { .extension-header {
@apply flex justify-between;
&.collapsed:not(:hover) { &.collapsed:not(:hover) {
.nc-extension-expand-btn, .nc-extension-expand-btn,
.nc-extension-menu { .nc-extension-menu {

12
packages/nc-gui/components/extensions/Extension/HeaderMenu.vue

@ -1,17 +1,19 @@
<script setup lang="ts"> <script setup lang="ts">
interface Props { interface Props {
fullscreen?: boolean isFullscreen?: boolean
activeError?: boolean
} }
const { fullscreen, activeError } = defineProps<Props>() defineProps<Props>()
const { activeError } = useExtensionHelperOrThrow()
const emits = defineEmits(['rename', 'duplicate', 'showDetails', 'clearData', 'delete']) const emits = defineEmits(['rename', 'duplicate', 'showDetails', 'clearData', 'delete'])
</script> </script>
<template> <template>
<div class="flex items-center"> <div class="flex items-center" @click.stop>
<NcDropdown :trigger="['click']" placement="bottomRight"> <NcDropdown :trigger="['click']" placement="bottomRight">
<NcButton type="text" size="xs" class="!px-1"> <NcButton type="text" :size="isFullscreen ? 'small' : 'xs'" class="!px-1">
<GeneralIcon icon="threeDotVertical" /> <GeneralIcon icon="threeDotVertical" />
</NcButton> </NcButton>

30
packages/nc-gui/components/extensions/Extension/Wrapper.vue

@ -0,0 +1,30 @@
<script lang="ts" setup>
const { fullscreen } = useExtensionHelperOrThrow()
const headerRef = ref<HTMLDivElement>()
const { height } = useElementSize(headerRef)
</script>
<template>
<div>
<div ref="headerRef">
<ExtensionsExtensionHeader>
<template #extra>
<slot name="headerExtra"></slot>
</template>
</ExtensionsExtensionHeader>
</div>
<div
:class="{
'p-6 max': fullscreen,
'h-full': !fullscreen,
}"
:style="fullscreen ? { height: height ? `calc(100% - ${height}px)` : 'calc(100% - 64px)' } : {}"
>
<slot />
</div>
</div>
</template>
<style lang="scss" scoped></style>

320
packages/nc-gui/extensions/data-exporter/index.vue

@ -203,191 +203,193 @@ onMounted(() => {
</script> </script>
<template> <template>
<div ref="dataExporterRef" class="data-exporter"> <ExtensionsExtensionWrapper>
<div class="pb-3 flex items-center justify-between gap-2.5 flex-wrap"> <div ref="dataExporterRef" class="data-exporter">
<div <div class="pb-3 flex items-center justify-between gap-2.5 flex-wrap">
class="flex-1 flex items-center" <div
:class="{ class="flex-1 flex items-center"
'max-w-[min(350px,calc(100%-124px))]': isExporting && !fullscreen && width > 325,
'max-w-[min(350px,calc(100%_-_84px))]': !isExporting && !fullscreen && width > 325,
'max-w-full': width <= 325,
'max-w-[900px]': fullscreen,
}"
>
<NcSelect
v-model:value="exportPayload.tableId"
placeholder="-select table-"
:disabled="isExporting"
class="nc-data-exporter-table-select"
:class="{ :class="{
'flex-1 max-w-[240px]': fullscreen, 'max-w-[min(350px,calc(100%-124px))]': isExporting && !fullscreen && width > 325,
'min-w-1/2 max-w-[175px]': !fullscreen, 'max-w-[min(350px,calc(100%_-_84px))]': !isExporting && !fullscreen && width > 325,
'max-w-full': width <= 325,
'max-w-[900px]': fullscreen,
}" }"
:filter-option="filterOption"
dropdown-class-name="w-[250px]"
show-search
@change="onTableSelect"
> >
<a-select-option v-for="table of tableList" :key="table.label" :value="table.value"> <NcSelect
<div class="w-full flex items-center gap-2"> v-model:value="exportPayload.tableId"
<div class="min-w-5 flex items-center justify-center"> placeholder="-select table-"
<GeneralTableIcon :meta="{ meta: table.meta }" class="text-gray-500" /> :disabled="isExporting"
</div> class="nc-data-exporter-table-select"
<NcTooltip class="flex-1 truncate" show-on-truncate-only> :class="{
<template #title>{{ table.label }}</template> 'flex-1 max-w-[240px]': fullscreen,
<span>{{ table.label }}</span> 'min-w-1/2 max-w-[175px]': !fullscreen,
</NcTooltip> }"
<component :filter-option="filterOption"
:is="iconMap.check" dropdown-class-name="w-[250px]"
v-if="exportPayload.tableId === table.value" show-search
id="nc-selected-item-icon" @change="onTableSelect"
class="flex-none text-primary w-4 h-4" >
/> <a-select-option v-for="table of tableList" :key="table.label" :value="table.value">
</div> <div class="w-full flex items-center gap-2">
</a-select-option> <div class="min-w-5 flex items-center justify-center">
</NcSelect> <GeneralTableIcon :meta="{ meta: table.meta }" class="text-gray-500" />
</div>
<NcSelect <NcTooltip class="flex-1 truncate" show-on-truncate-only>
v-model:value="exportPayload.viewId" <template #title>{{ table.label }}</template>
placeholder="-select view-" <span>{{ table.label }}</span>
:disabled="isExporting" </NcTooltip>
class="nc-data-exporter-view-select" <component
:class="{ :is="iconMap.check"
'flex-1 max-w-[240px]': fullscreen, v-if="exportPayload.tableId === table.value"
'min-w-1/2 max-w-[175px]': !fullscreen, id="nc-selected-item-icon"
}" class="flex-none text-primary w-4 h-4"
dropdown-class-name="w-[250px]" />
:filter-option="filterOption"
show-search
@change="onViewSelect"
>
<a-select-option v-for="view of viewList" :key="view.label" :value="view.value">
<div class="w-full flex items-center gap-2">
<div class="min-w-5 flex items-center justify-center">
<GeneralViewIcon :meta="{ meta: view.meta, type: view.type }" class="flex-none text-gray-500" />
</div> </div>
<NcTooltip class="flex-1 truncate" show-on-truncate-only> </a-select-option>
<template #title>{{ view.label }}</template> </NcSelect>
<span>{{ view.label }}</span>
</NcTooltip> <NcSelect
<component v-model:value="exportPayload.viewId"
:is="iconMap.check" placeholder="-select view-"
v-if="exportPayload.viewId === view.value" :disabled="isExporting"
id="nc-selected-item-icon" class="nc-data-exporter-view-select"
class="flex-none text-primary w-4 h-4"
/>
</div> </a-select-option
></NcSelect>
</div>
<div class="flex-none flex justify-end">
<NcTooltip class="flex" placement="topRight" :disabled="!isExporting">
<template #title> The CSV file is being prepared in the background. You'll be notified once it's ready. </template>
<NcButton :disabled="!exportPayload?.viewId" :loading="isExporting" size="xs" @click="exportDataAsync">{{
isExporting ? 'Generating' : 'Export'
}}</NcButton>
</NcTooltip>
</div>
</div>
<div class="data-exporter-body flex-1 flex flex-col">
<div class="data-exporter-header">Recent Exports</div>
<div v-if="exportedFiles.length" class="flex-1 flex flex-col nc-scrollbar-thin max-h-[calc(100%_-_25px)]">
<template v-for="exp of exportedFiles">
<div
v-if="exp.status === JobStatus.COMPLETED ? exp.result : true"
:key="exp.id"
class="px-3 py-2 flex gap-2 justify-between border-b-1 hover:bg-gray-50"
:class="{ :class="{
'px-4 py-3': fullscreen, 'flex-1 max-w-[240px]': fullscreen,
'px-3 py-2': !fullscreen, 'min-w-1/2 max-w-[175px]': !fullscreen,
}" }"
dropdown-class-name="w-[250px]"
:filter-option="filterOption"
show-search
@change="onViewSelect"
> >
<a-select-option v-for="view of viewList" :key="view.label" :value="view.value">
<div class="w-full flex items-center gap-2">
<div class="min-w-5 flex items-center justify-center">
<GeneralViewIcon :meta="{ meta: view.meta, type: view.type }" class="flex-none text-gray-500" />
</div>
<NcTooltip class="flex-1 truncate" show-on-truncate-only>
<template #title>{{ view.label }}</template>
<span>{{ view.label }}</span>
</NcTooltip>
<component
:is="iconMap.check"
v-if="exportPayload.viewId === view.value"
id="nc-selected-item-icon"
class="flex-none text-primary w-4 h-4"
/>
</div> </a-select-option
></NcSelect>
</div>
<div class="flex-none flex justify-end">
<NcTooltip class="flex" placement="topRight" :disabled="!isExporting">
<template #title> The CSV file is being prepared in the background. You'll be notified once it's ready. </template>
<NcButton :disabled="!exportPayload?.viewId" :loading="isExporting" size="xs" @click="exportDataAsync">{{
isExporting ? 'Generating' : 'Export'
}}</NcButton>
</NcTooltip>
</div>
</div>
<div class="data-exporter-body flex-1 flex flex-col">
<div class="data-exporter-header">Recent Exports</div>
<div v-if="exportedFiles.length" class="flex-1 flex flex-col nc-scrollbar-thin max-h-[calc(100%_-_25px)]">
<template v-for="exp of exportedFiles">
<div <div
class="flex-1 flex items-center gap-3" v-if="exp.status === JobStatus.COMPLETED ? exp.result : true"
:key="exp.id"
class="px-3 py-2 flex gap-2 justify-between border-b-1 hover:bg-gray-50"
:class="{ :class="{
'max-w-[calc(100%_-_74px)]': exp.status === JobStatus.COMPLETED, 'px-4 py-3': fullscreen,
'max-w-[calc(100%_-_38px)]': exp.status !== JobStatus.COMPLETED, 'px-3 py-2': !fullscreen,
}" }"
> >
<NcTooltip v-if="[JobStatus.COMPLETED, JobStatus.FAILED].includes(exp.status)" class="flex"> <div
<template #title> class="flex-1 flex items-center gap-3"
{{ jobStatusTooltip[exp.status] }} :class="{
</template> 'max-w-[calc(100%_-_74px)]': exp.status === JobStatus.COMPLETED,
<GeneralIcon 'max-w-[calc(100%_-_38px)]': exp.status !== JobStatus.COMPLETED,
:icon="exp.status === JobStatus.COMPLETED ? 'circleCheckSolid' : 'alertTriangleSolid'" }"
class="flex-none h-4 w-4" >
:class="{ <NcTooltip v-if="[JobStatus.COMPLETED, JobStatus.FAILED].includes(exp.status)" class="flex">
'!text-green-700': exp.status === JobStatus.COMPLETED, <template #title>
'!text-red-700': exp.status === JobStatus.FAILED, {{ jobStatusTooltip[exp.status] }}
}" </template>
/> <GeneralIcon
</NcTooltip> :icon="exp.status === JobStatus.COMPLETED ? 'circleCheckSolid' : 'alertTriangleSolid'"
<div v-else class="h-5 flex items-center"> class="flex-none h-4 w-4"
<GeneralLoader size="regular" class="flex-none" /> :class="{
</div> '!text-green-700': exp.status === JobStatus.COMPLETED,
'!text-red-700': exp.status === JobStatus.FAILED,
}"
/>
</NcTooltip>
<div v-else class="h-5 flex items-center">
<GeneralLoader size="regular" class="flex-none" />
</div>
<div class="flex-1 max-w-[calc(100%_-_28px)] flex flex-col gap-1"> <div class="flex-1 max-w-[calc(100%_-_28px)] flex flex-col gap-1">
<div class="inline-flex gap-1 text-sm text-gray-800 -ml-[1px]"> <div class="inline-flex gap-1 text-sm text-gray-800 -ml-[1px]">
<span class="inline-flex items-center h-5"> <span class="inline-flex items-center h-5">
<GeneralIcon icon="file" class="flex-none text-gray-600/80 h-3.5 w-3.5" /> <GeneralIcon icon="file" class="flex-none text-gray-600/80 h-3.5 w-3.5" />
</span> </span>
<NcTooltip class="truncate max-w-[calc(100%_-_20px)]" show-on-truncate-only> <NcTooltip class="truncate max-w-[calc(100%_-_20px)]" show-on-truncate-only>
<template #title> <template #title>
{{ exp.result.title || titleHelper() }}
</template>
{{ exp.result.title || titleHelper() }} {{ exp.result.title || titleHelper() }}
</template> </NcTooltip>
{{ exp.result.title || titleHelper() }} </div>
</NcTooltip> <div v-if="exp.result.timestamp" class="text-[10px] leading-4 text-gray-600">
</div> <NcTooltip class="truncate" show-on-truncate-only>
<div v-if="exp.result.timestamp" class="text-[10px] leading-4 text-gray-600"> <template #title>
<NcTooltip class="truncate" show-on-truncate-only> {{ dayjs(exp.result.timestamp).format('MM/DD/YYYY [at] hh:mm A') }}
<template #title> </template>
{{ dayjs(exp.result.timestamp).format('MM/DD/YYYY [at] hh:mm A') }} {{ dayjs(exp.result.timestamp).format('MM/DD/YYYY [at] hh:mm A') }}
</template> </NcTooltip>
{{ dayjs(exp.result.timestamp).format('MM/DD/YYYY [at] hh:mm A') }} </div>
</NcTooltip>
</div> </div>
</div> </div>
</div>
<div <div
v-if="exp.status === JobStatus.COMPLETED" v-if="exp.status === JobStatus.COMPLETED"
class="flex items-center" class="flex items-center"
@click="handleDownload(urlHelper(exp.result.url))" @click="handleDownload(urlHelper(exp.result.url))"
> >
<NcTooltip class="flex items-center"> <NcTooltip class="flex items-center">
<template #title> <template #title>
{{ $t('general.download') }} {{ $t('general.download') }}
</template> </template>
<NcButton type="secondary" size="xs" class="!px-[5px]"> <NcButton type="secondary" size="xs" class="!px-[5px]">
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<GeneralIcon icon="download" /> <GeneralIcon icon="download" />
</div> </div>
</NcButton> </NcButton>
</NcTooltip> </NcTooltip>
</div> </div>
<div class="flex items-center"> <div class="flex items-center">
<NcTooltip class="flex"> <NcTooltip class="flex">
<template #title> <template #title>
{{ $t('general.remove') }} {{ $t('general.remove') }}
</template> </template>
<NcButton type="text" size="xs" class="!px-[5px]" @click="onRemoveExportedFile(exp.id)"> <NcButton type="text" size="xs" class="!px-[5px]" @click="onRemoveExportedFile(exp.id)">
<GeneralIcon icon="close" /> <GeneralIcon icon="close" />
</NcButton> </NcButton>
</NcTooltip> </NcTooltip>
</div>
</div> </div>
</div> </template>
</template> </div>
<div v-else class="px-3 py-2 flex-1 flex items-center justify-center text-gray-600">No exports</div>
</div> </div>
<div v-else class="px-3 py-2 flex-1 flex items-center justify-center text-gray-600">No exports</div>
</div> </div>
</div> </ExtensionsExtensionWrapper>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
.data-exporter { .data-exporter {
@apply flex flex-col overflow-hidden h-full; @apply flex flex-col overflow-hidden h-full;
.data-exporter-header { .data-exporter-header {
@apply px-3 py-1 bg-gray-100 text-[11px] leading-4 text-gray-600 border-b-1; @apply px-3 py-1 bg-gray-100 text-[11px] leading-4 text-gray-600 border-b-1;
} }

12
packages/nc-gui/extensions/json-exporter/index.vue

@ -92,11 +92,13 @@ onMounted(() => {
</script> </script>
<template> <template>
<div class="flex flex-col gap-2 p-2 border-1 rounded-lg"> <ExtensionsExtensionWrapper>
<NcSelect v-model:value="exportPayload.tableId" :options="tableList" placeholder="-select table-" @change="onTableSelect" /> <div class="flex flex-col gap-2 p-2 border-1 rounded-lg">
<NcSelect v-model:value="exportPayload.viewId" :options="viewList" placeholder="-select view-" @change="onViewSelect" /> <NcSelect v-model:value="exportPayload.tableId" :options="tableList" placeholder="-select table-" @change="onTableSelect" />
<NcButton @click="exportJson">Export</NcButton> <NcSelect v-model:value="exportPayload.viewId" :options="viewList" placeholder="-select view-" @change="onViewSelect" />
</div> <NcButton @click="exportJson">Export</NcButton>
</div>
</ExtensionsExtensionWrapper>
</template> </template>
<style lang="scss"></style> <style lang="scss"></style>

Loading…
Cancel
Save