|
|
|
@ -2,7 +2,17 @@
|
|
|
|
|
import type { TableType, ViewType } from 'nocodb-sdk' |
|
|
|
|
import { RelationTypes, UITypes, isSystemColumn, isVirtualCol } from 'nocodb-sdk' |
|
|
|
|
import Draggable from 'vuedraggable' |
|
|
|
|
import { CellClickHookInj, IsFormInj, MetaInj, PaginationDataInj, provide, ref, toRef, useVModel } from '#imports' |
|
|
|
|
import { |
|
|
|
|
CellClickHookInj, |
|
|
|
|
IsExpandedFormOpenInj, |
|
|
|
|
IsFormInj, |
|
|
|
|
MetaInj, |
|
|
|
|
PaginationDataInj, |
|
|
|
|
provide, |
|
|
|
|
ref, |
|
|
|
|
toRef, |
|
|
|
|
useVModel, |
|
|
|
|
} from '#imports' |
|
|
|
|
import type { Row } from '~/lib' |
|
|
|
|
|
|
|
|
|
interface Props { |
|
|
|
@ -26,6 +36,10 @@ enum BulkUpdateMode {
|
|
|
|
|
|
|
|
|
|
const meta = toRef(props, 'meta') |
|
|
|
|
|
|
|
|
|
const isExpanded = useVModel(props, 'modelValue', emits, { |
|
|
|
|
defaultValue: false, |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
// override cell click hook to avoid unexpected behavior at form fields |
|
|
|
|
provide(CellClickHookInj, null) |
|
|
|
|
|
|
|
|
@ -33,6 +47,8 @@ provide(MetaInj, meta)
|
|
|
|
|
|
|
|
|
|
provide(IsFormInj, ref(true)) |
|
|
|
|
|
|
|
|
|
provide(IsExpandedFormOpenInj, isExpanded) |
|
|
|
|
|
|
|
|
|
const formState: Record<string, any> = reactive({}) |
|
|
|
|
|
|
|
|
|
const updateMode = ref(BulkUpdateMode.ALL) |
|
|
|
@ -72,10 +88,6 @@ const editCount = computed(() => {
|
|
|
|
|
} |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
const isExpanded = useVModel(props, 'modelValue', emits, { |
|
|
|
|
defaultValue: false, |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
function isRequired(_columnObj: Record<string, any>, required = false) { |
|
|
|
|
let columnObj = _columnObj |
|
|
|
|
if ( |
|
|
|
@ -160,6 +172,24 @@ const save = () => {
|
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const addAllColumns = () => { |
|
|
|
|
for (const col of fields.value) { |
|
|
|
|
if (editColumns.value.find((c) => c.id === col.id)) { |
|
|
|
|
continue |
|
|
|
|
} |
|
|
|
|
if (!col || !col.title) continue |
|
|
|
|
editColumns.value.push(col) |
|
|
|
|
formState[col.title] = null |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const removeAllColumns = () => { |
|
|
|
|
for (const col of editColumns.value) { |
|
|
|
|
delete formState[col.title] |
|
|
|
|
} |
|
|
|
|
editColumns.value = [] |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
onMounted(() => { |
|
|
|
|
if (!props.selectedAllRecords && !props.rows) { |
|
|
|
|
isExpanded.value = false |
|
|
|
@ -185,7 +215,7 @@ onMounted(() => {
|
|
|
|
|
class="nc-drawer-bulk-update" |
|
|
|
|
:class="{ active: isExpanded }" |
|
|
|
|
> |
|
|
|
|
<div class="flex p-2 items-center gap-2 p-4 nc-bulk-update-form-header"> |
|
|
|
|
<div class="flex p-2 items-center gap-2 p-4 nc-bulk-update-header"> |
|
|
|
|
<h5 class="text-lg font-weight-medium flex items-center gap-1 mb-0 min-w-0 overflow-x-hidden truncate"> |
|
|
|
|
<GeneralTableIcon :style="{ color: iconColor }" :meta="meta" class="mx-2" /> |
|
|
|
|
|
|
|
|
@ -223,6 +253,22 @@ onMounted(() => {
|
|
|
|
|
Bulk Update Selected |
|
|
|
|
</div> |
|
|
|
|
</a-button> |
|
|
|
|
<a-dropdown> |
|
|
|
|
<component :is="iconMap.threeDotVertical" class="nc-icon-transition" /> |
|
|
|
|
<template #overlay> |
|
|
|
|
<a-menu> |
|
|
|
|
<a-menu-item @click="isExpanded = false"> |
|
|
|
|
<div v-e="['c:row-expand:delete']" class="py-2 flex gap-2 items-center"> |
|
|
|
|
<component |
|
|
|
|
:is="iconMap.closeCircle" |
|
|
|
|
class="nc-icon-transition cursor-pointer select-none nc-delete-row text-gray-500 mx-1 min-w-4" |
|
|
|
|
/> |
|
|
|
|
{{ $t('general.close') }} |
|
|
|
|
</div> |
|
|
|
|
</a-menu-item> |
|
|
|
|
</a-menu> |
|
|
|
|
</template> |
|
|
|
|
</a-dropdown> |
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
<div class="flex w-full !bg-gray-100 flex-1"> |
|
|
|
@ -239,17 +285,17 @@ onMounted(() => {
|
|
|
|
|
@start="drag = true" |
|
|
|
|
@end="drag = false" |
|
|
|
|
> |
|
|
|
|
<template #item="{ element, index }"> |
|
|
|
|
<template #item="{ element }"> |
|
|
|
|
<div |
|
|
|
|
class="color-transition nc-editable item cursor-pointer hover:(bg-primary bg-opacity-10 ring-1 ring-accent ring-opacity-100) px-4 lg:px-12 py-4 relative" |
|
|
|
|
:class="[`nc-form-drag-${element.title.replaceAll(' ', '')}`]" |
|
|
|
|
data-testid="nc-form-fields" |
|
|
|
|
:class="[`nc-bulk-update-drag-${element.title.replaceAll(' ', '')}`]" |
|
|
|
|
data-testid="nc-bulk-update-fields" |
|
|
|
|
> |
|
|
|
|
<div class="text-gray group absolute top-4 right-12"> |
|
|
|
|
<component |
|
|
|
|
:is="iconMap.eyeSlash" |
|
|
|
|
class="opacity-0 nc-field-remove-icon group-hover:text-red-500 cursor-pointer" |
|
|
|
|
data-testid="nc-form-fields-remove-icon" |
|
|
|
|
data-testid="nc-bulk-update-fields-remove-icon" |
|
|
|
|
@click="handleRemove(element)" |
|
|
|
|
/> |
|
|
|
|
</div> |
|
|
|
@ -260,7 +306,7 @@ onMounted(() => {
|
|
|
|
|
:column="{ ...element, title: element.label || element.title }" |
|
|
|
|
:required="isRequired(element, element.required)" |
|
|
|
|
:hide-menu="true" |
|
|
|
|
data-testid="nc-form-input-label" |
|
|
|
|
data-testid="nc-bulk-update-input-label" |
|
|
|
|
/> |
|
|
|
|
|
|
|
|
|
<LazySmartsheetHeaderCell |
|
|
|
@ -268,7 +314,7 @@ onMounted(() => {
|
|
|
|
|
:column="{ ...element, title: element.label || element.title }" |
|
|
|
|
:required="isRequired(element, element.required)" |
|
|
|
|
:hide-menu="true" |
|
|
|
|
data-testid="nc-form-input-label" |
|
|
|
|
data-testid="nc-bulk-update-input-label" |
|
|
|
|
/> |
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
@ -286,8 +332,8 @@ onMounted(() => {
|
|
|
|
|
<LazySmartsheetVirtualCell |
|
|
|
|
v-model="formState[element.title]" |
|
|
|
|
class="nc-input" |
|
|
|
|
:class="`nc-form-input-${element.title.replaceAll(' ', '')}`" |
|
|
|
|
:data-testid="`nc-form-input-${element.title.replaceAll(' ', '')}`" |
|
|
|
|
:class="`nc-bulk-update-input-${element.title.replaceAll(' ', '')}`" |
|
|
|
|
:data-testid="`nc-bulk-update-input-${element.title.replaceAll(' ', '')}`" |
|
|
|
|
:column="element" |
|
|
|
|
/> |
|
|
|
|
</a-form-item> |
|
|
|
@ -303,12 +349,10 @@ onMounted(() => {
|
|
|
|
|
}, |
|
|
|
|
]" |
|
|
|
|
> |
|
|
|
|
<LazySmartsheetDivDataCell class="relative"> |
|
|
|
|
<LazySmartsheetDivDataCell class="!bg-white rounded px-1 min-h-[35px] flex items-center mt-2 relative"> |
|
|
|
|
<LazySmartsheetCell |
|
|
|
|
v-model="formState[element.title]" |
|
|
|
|
class="nc-input" |
|
|
|
|
:class="`nc-form-input-${element.title.replaceAll(' ', '')}`" |
|
|
|
|
:data-testid="`nc-form-input-${element.title.replaceAll(' ', '')}`" |
|
|
|
|
:data-testid="`nc-bulk-update-input-${element.title.replaceAll(' ', '')}`" |
|
|
|
|
:column="element" |
|
|
|
|
:edit-enabled="true" |
|
|
|
|
:active="true" |
|
|
|
@ -316,7 +360,7 @@ onMounted(() => {
|
|
|
|
|
</LazySmartsheetDivDataCell> |
|
|
|
|
</a-form-item> |
|
|
|
|
|
|
|
|
|
<div class="nc-form-help-text text-gray-500 text-xs" data-testid="nc-form-input-help-text-label"> |
|
|
|
|
<div class="nc-bulk-update-help-text text-gray-500 text-xs" data-testid="nc-bulk-update-input-help-text-label"> |
|
|
|
|
{{ element.description }} |
|
|
|
|
</div> |
|
|
|
|
</div> |
|
|
|
@ -335,6 +379,31 @@ onMounted(() => {
|
|
|
|
|
<!-- TODO i18n --> |
|
|
|
|
Select columns to Edit |
|
|
|
|
</div> |
|
|
|
|
<div class="flex flex-wrap gap-2 mb-4"> |
|
|
|
|
<button |
|
|
|
|
v-if="fields.length > editColumns.length" |
|
|
|
|
type="button" |
|
|
|
|
class="nc-bulk-update-add-all color-transition bg-white transform hover:(text-primary ring ring-accent ring-opacity-100) active:translate-y-[1px] px-2 py-1 shadow-md rounded" |
|
|
|
|
data-testid="nc-bulk-update-add-all" |
|
|
|
|
tabindex="-1" |
|
|
|
|
@click="addAllColumns" |
|
|
|
|
> |
|
|
|
|
<!-- Add all --> |
|
|
|
|
{{ $t('general.addAll') }} |
|
|
|
|
</button> |
|
|
|
|
|
|
|
|
|
<button |
|
|
|
|
v-if="editColumns.length" |
|
|
|
|
type="button" |
|
|
|
|
class="nc-bulk-update-remove-all color-transition bg-white transform hover:(text-primary ring ring-accent ring-opacity-100) active:translate-y-[1px] px-2 py-1 shadow-md rounded" |
|
|
|
|
data-testid="nc-bulk-update-remove-all" |
|
|
|
|
tabindex="-1" |
|
|
|
|
@click="removeAllColumns" |
|
|
|
|
> |
|
|
|
|
<!-- Remove all --> |
|
|
|
|
{{ $t('general.removeAll') }} |
|
|
|
|
</button> |
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
<Draggable |
|
|
|
|
:list="fields" |
|
|
|
@ -349,7 +418,7 @@ onMounted(() => {
|
|
|
|
|
<a-card |
|
|
|
|
size="small" |
|
|
|
|
class="!border-0 color-transition cursor-pointer item hover:(bg-primary ring-1 ring-accent ring-opacity-100) bg-opacity-10 !rounded !shadow-lg" |
|
|
|
|
:data-testid="`nc-form-hidden-column-${element.label || element.title}`" |
|
|
|
|
:data-testid="`nc-bulk-update-hidden-column-${element.label || element.title}`" |
|
|
|
|
@mousedown="moved = false" |
|
|
|
|
@mousemove="moved = false" |
|
|
|
|
@mouseup="handleMouseUp(element)" |
|
|
|
@ -384,7 +453,7 @@ onMounted(() => {
|
|
|
|
|
@apply !bg-white; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
.nc-form-wrapper { |
|
|
|
|
.nc-bulk-update-wrapper { |
|
|
|
|
max-height: max(calc(100vh - 65px), 600px); |
|
|
|
|
height: max-content !important; |
|
|
|
|
} |
|
|
|
@ -407,14 +476,14 @@ onMounted(() => {
|
|
|
|
|
@apply text-[#3d3d3d] italic; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
.nc-form-input-label, |
|
|
|
|
.nc-form-input-help-text { |
|
|
|
|
.nc-bulk-update-input-label, |
|
|
|
|
.nc-bulk-update-input-help-text { |
|
|
|
|
&::placeholder { |
|
|
|
|
@apply !text-gray-500 !text-xs; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
.nc-form-help-text, |
|
|
|
|
.nc-bulk-update-help-text, |
|
|
|
|
.nc-input-required-error { |
|
|
|
|
max-width: 100%; |
|
|
|
|
word-break: break-all; |
|
|
|
|