多维表格
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.

374 lines
7.5 KiB

feat: button field (#9144) * feat: static button type * fix: swagger * fix: style corrections * feat: webhook trigger * fix: disables * feat: Button icons fix: row Delete failing * fix: expanded-form ux fix: disable buttons in forms fix: update PlainCell button handling fix: webhook delete, active change handling * fix: disable case * fix: disable case * fix: update Button styles * fix: refactor min/max with limit for columns fix: disable filter, groupby for Button Field fix: disable aggregation for Buttons * fix: hide button field in Filters * fix: fields menu corrections fix: update menu corrections * fix: rebase * feat: support webhook creation from ButtonOptions * fix: sort related tests * fix: keep webhook modal open * fix: ui fixes * fix: icon duplicate * fix: syntax highlighing for handlebar fix: disable mascot fix: truncate selected webhook * fix: sort disable tooltip * test: button playwright test * fix: button column duplication * fix: add error fix: column options * fix: EditOrAddProvider.vue * fix: add invalid configuration * fix: ux corrections * fix: ux corrections * fix: error handling fix: clear single query cache on hook delete * fix: include button type in api * fix: update overlay styles fix: webhook update * fix: formula placeholder * fix: playwright tests * fix: playwright tests * feat: refactor formula input * fix: added more spacing * fix: no icon text * fix: lint * fix: handle invalid url * fix: handle sort by for button causes issue when button used as lookup * fix: button field position * docs: button field * fix: tooltip correction * fix: link * fix: add btn href * fix: handle some edge cases * fix: handle some edge cases * fix: update font color * fix: add manual trigger docs * fix: sqlite BaseModel fix * docs: button share view info * fix: rebase * fix: reduce height and added resize support fix: added tooltip if label overflow * fix: manual hook disable state * docs: manual trigger details in webhook page * fix: chevron grey shade to 500 * docs: sample payload for manual trigger * fix: style update * fix: pr review comments * fix: pr review changes * fix: reactivity issue * fix: reactivity issue * fix: filter enabled on button * fix: reload meta on webhook change * fix: error handling in formula filter * fix: handle url error --------- Co-authored-by: Raju Udava <86527202+dstala@users.noreply.github.com>
3 months ago
<script setup lang="ts">
import type { ButtonType, ColumnType } from 'nocodb-sdk'
import type { Ref } from 'vue'
const column = inject(ColumnInj) as Ref<
ColumnType & {
colOptions: ButtonType
}
>
const cellValue = inject(CellValueInj, ref())
const { currentRow } = useSmartsheetRowStoreOrThrow()
const meta = inject(MetaInj, ref())
const isGrid = inject(IsGridInj, ref(false))
const isExpandedForm = inject(IsExpandedFormOpenInj, ref(false))
const { isUIAllowed } = useRoles()
const isPublic = inject(IsPublicInj, ref(false))
const { $api } = useNuxtApp()
const rowId = computed(() => {
return extractPkFromRow(currentRow.value?.row, meta.value!.columns!)
})
const isLoading = ref(false)
const triggerAction = async () => {
const colOptions = column.value.colOptions
if (colOptions.type === 'webhook') {
try {
isLoading.value = true
await $api.dbTableWebhook.trigger(cellValue.value?.fk_webhook_id, rowId!.value)
} catch (e) {
console.log(e)
message.error(await extractSdkResponseErrorMsg(e))
} finally {
isLoading.value = false
}
}
}
const componentProps = computed(() => {
if (column.value.colOptions.type === 'url') {
let url = `${cellValue.value?.url}`
url = encodeURI(/^(https?|ftp|mailto|file):\/\//.test(url) ? url : url.trim() ? `http://${url}` : '')
return {
href: url,
target: '_blank',
...(column.value?.colOptions.error ? { disabled: true } : {}),
}
} else {
return {
disabled:
isPublic.value ||
!isUIAllowed('hookTrigger') ||
isLoading.value ||
!column.value.colOptions.fk_webhook_id ||
!cellValue.value?.fk_webhook_id,
}
}
})
</script>
<template>
<div
:class="{
'justify-center': isGrid && !isExpandedForm,
}"
class="w-full flex items-center"
>
<component
:is="column.colOptions.type === 'url' ? 'a' : 'button'"
v-bind="componentProps"
data-testid="nc-button-cell"
:class="[
`${column.colOptions.color ?? 'brand'} ${column.colOptions.theme ?? 'solid'}`,
{ '!w-6': !column.colOptions.label },
]"
class="nc-cell-button nc-button-cell-link btn-cell-colors truncate flex items-center h-6"
@click="triggerAction"
>
<GeneralLoader
v-if="isLoading"
:class="{
solid: column.colOptions.theme === 'solid',
text: column.colOptions.theme === 'text',
light: column.colOptions.theme === 'light',
}"
class="flex btn-cell-colors !bg-transparent w-4 h-4"
size="medium"
/>
<GeneralIcon v-else-if="column.colOptions.icon" :icon="column.colOptions.icon" class="!w-4 min-w-4 min-h-4 !h-4" />
<NcTooltip v-if="column.colOptions.label" class="!truncate" show-on-truncate-only>
<span class="text-[13px] truncate font-medium">
{{ column.colOptions.label }}
</span>
<template #title>
{{ column.colOptions.label }}
</template>
</NcTooltip>
</component>
</div>
</template>
<style lang="scss">
.nc-data-cell {
&:has(.nc-virtual-cell-button) {
@apply !border-none;
box-shadow: none !important;
}
.nc-cell-attachment {
@apply !border-none;
}
}
.nc-button-cell-link {
@apply !no-underline;
}
</style>
<style scoped lang="scss">
.nc-cell-button {
@apply rounded-lg px-2 flex items-center gap-2 transition-all justify-center;
&:not([class*='text']) {
box-shadow: 0px 3px 1px -2px rgba(0, 0, 0, 0.06), 0px 5px 3px -2px rgba(0, 0, 0, 0.02);
}
&:focus-within {
box-shadow: 0px 0px 0px 2px #fff, 0px 0px 0px 4px #3069fe;
}
&[disabled] {
@apply !bg-gray-100 text-gray-400;
}
}
.btn-cell-colors {
&.solid {
@apply text-white;
&.brand {
@apply bg-brand-500 hover:bg-brand-600;
.nc-loader {
@apply !text-brand-500;
}
}
&.red {
@apply bg-red-600 hover:bg-red-700;
.nc-loader {
@apply !text-red-600;
}
}
&.green {
@apply bg-green-600 hover:bg-green-700;
.nc-loader {
@apply !text-green-600;
}
}
&.maroon {
@apply bg-maroon-600 hover:bg-maroon-700;
.nc-loader {
@apply !text-maroon-600;
}
}
&.blue {
@apply bg-blue-600 hover:bg-blue-700;
.nc-loader {
@apply !text-blue-600;
}
}
&.orange {
@apply bg-orange-600 hover:bg-orange-700;
.nc-loader {
@apply !text-orange-600;
}
}
&.pink {
@apply bg-pink-600 hover:bg-pink-700;
.nc-loader {
@apply !text-pink-600;
}
}
&.purple {
@apply bg-purple-500 hover:bg-purple-700;
.nc-loader {
@apply !text-purple-600;
}
}
&.yellow {
@apply bg-yellow-600 hover:bg-yellow-700;
.nc-loader {
@apply !text-yellow-600;
}
}
&.gray {
@apply bg-gray-600 hover:bg-gray-700;
.nc-loader {
@apply !text-gray-600;
}
}
}
&.light {
@apply text-gray-700;
box-shadow: 0px 3px 1px -2px rgba(0, 0, 0, 0.06), 0px 5px 3px -2px rgba(0, 0, 0, 0.02);
&.brand {
@apply bg-brand-100 hover:bg-brand-200;
.nc-loader {
@apply !text-brand-500;
}
}
&.red {
@apply bg-red-100 hover:bg-red-200;
.nc-loader {
@apply !text-red-600;
}
}
&.green {
@apply bg-green-100 hover:bg-green-200;
.nc-loader {
@apply !text-green-600;
}
}
&.maroon {
@apply bg-maroon-100 hover:bg-maroon-200;
.nc-loader {
@apply !text-maroon-600;
}
}
&.blue {
@apply bg-blue-100 hover:bg-blue-200;
.nc-loader {
@apply !text-blue-600;
}
}
&.orange {
@apply bg-orange-100 hover:bg-orange-200;
.nc-loader {
@apply !text-orange-600;
}
}
&.pink {
@apply bg-pink-100 hover:bg-pink-200;
.nc-loader {
@apply !text-pink-600;
}
}
&.purple {
@apply bg-purple-100 hover:bg-purple-200;
.nc-loader {
@apply !text-purple-600;
}
}
&.yellow {
@apply bg-yellow-100 hover:bg-yellow-200;
.nc-loader {
@apply !text-yellow-600;
}
}
&.gray {
@apply bg-gray-100 hover:bg-gray-200;
.nc-loader {
@apply !text-gray-600;
}
}
}
&.text {
&:hover {
@apply bg-gray-200;
}
&:focus {
box-shadow: 0px 0px 0px 2px #fff, 0px 0px 0px 4px #3069fe;
}
&.brand {
@apply text-brand-500;
.nc-loader {
@apply !text-brand-500;
}
}
&.red {
@apply text-red-600;
.nc-loader {
@apply !text-red-600;
}
}
&.green {
@apply text-green-600;
.nc-loader {
@apply !text-green-600;
}
}
&.maroon {
@apply text-maroon-600;
.nc-loader {
@apply !text-maroon-600;
}
}
&.blue {
@apply text-blue-600;
.nc-loader {
@apply !text-blue-600;
}
}
&.orange {
@apply text-orange-600;
.nc-loader {
@apply !text-orange-600;
}
}
&.pink {
@apply text-pink-600;
.nc-loader {
@apply !text-pink-600;
}
}
&.purple {
@apply text-purple-500;
.nc-loader {
@apply !text-purple-600;
}
}
&.yellow {
@apply text-yellow-600;
.nc-loader {
@apply !text-yellow-600;
}
}
&.gray {
@apply text-gray-600;
.nc-loader {
@apply !text-gray-600;
}
}
}
}
</style>