Browse Source

Nc fix(nc-gui): Bug fixes (#7979)

* fix(nc-gui): hide form field label if field title & label is same

* fix(nc-gui): text area truncate issue in form prefilled readonly

* fix(nc-gui): reset form state if user hide the select type prefilled option from limit options

* fix(nc-gui): in form view on click sidebar form field make active edit field

* fix(nc-gui): rating icon update issue in edit column modal
pull/7969/head
Ramesh Mane 6 months ago committed by GitHub
parent
commit
e5a018de25
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 1
      packages/nc-gui/components/cell/Rating.vue
  2. 28
      packages/nc-gui/components/cell/TextArea.vue
  3. 171
      packages/nc-gui/components/smartsheet/Form.vue
  4. 27
      packages/nc-gui/components/smartsheet/form/LimitOptions.vue

1
packages/nc-gui/components/cell/Rating.vue

@ -78,6 +78,7 @@ watch(rateDomRef, () => {
:count="ratingMeta.max" :count="ratingMeta.max"
:class="readOnly ? 'pointer-events-none' : ''" :class="readOnly ? 'pointer-events-none' : ''"
:style="`color: ${ratingMeta.color}; padding: ${isExpandedFormOpen ? '0px 8px' : '0px 5px'};`" :style="`color: ${ratingMeta.color}; padding: ${isExpandedFormOpen ? '0px 8px' : '0px 5px'};`"
:key="ratingMeta.icon.full"
@keydown="onKeyPress" @keydown="onKeyPress"
> >
<template #character> <template #character>

28
packages/nc-gui/components/cell/TextArea.vue

@ -36,9 +36,13 @@ const rowHeight = inject(RowHeightInj, ref(1 as const))
const isForm = inject(IsFormInj, ref(false)) const isForm = inject(IsFormInj, ref(false))
const readOnly = inject(ReadonlyInj, ref(false))
const { showNull } = useGlobal() const { showNull } = useGlobal()
const vModel = useVModel(props, 'modelValue', emits) const vModel = useVModel(props, 'modelValue', emits, {
shouldEmit: () => !readOnly.value,
})
const isExpandedFormOpen = inject(IsExpandedFormOpenInj, ref(false))! const isExpandedFormOpen = inject(IsExpandedFormOpenInj, ref(false))!
@ -77,8 +81,6 @@ const inputWrapperRef = ref<HTMLElement | null>(null)
const inputRef = ref<HTMLTextAreaElement | null>(null) const inputRef = ref<HTMLTextAreaElement | null>(null)
const readOnly = inject(ReadonlyInj)
watch(isVisible, () => { watch(isVisible, () => {
if (isVisible.value) { if (isVisible.value) {
setTimeout(() => { setTimeout(() => {
@ -211,8 +213,22 @@ watch(inputWrapperRef, () => {
}" }"
> >
<div v-if="isForm && isRichMode" class="w-full"> <div v-if="isForm && isRichMode" class="w-full">
<div class="w-full relative pt-11 w-full px-0 pb-1"> <div
<LazyCellRichText v-model:value="vModel" class="border-t-1 border-gray-100 !max-h-50" :autofocus="false" show-menu /> class="w-full relative w-full px-0 pb-1"
:class="{
'pt-11': !readOnly,
}"
>
<LazyCellRichText
v-model:value="vModel"
class="!max-h-50"
:class="{
'border-t-1 border-gray-100': !readOnly,
}"
:autofocus="false"
show-menu
:read-only="readOnly"
/>
</div> </div>
</div> </div>
@ -233,7 +249,7 @@ watch(inputWrapperRef, () => {
<LazyCellRichText v-model:value="vModel" sync-value-change read-only /> <LazyCellRichText v-model:value="vModel" sync-value-change read-only />
</div> </div>
<textarea <textarea
v-else-if="editEnabled && !isVisible" v-else-if="(editEnabled && !isVisible) || isForm"
:ref="focus" :ref="focus"
v-model="vModel" v-model="vModel"
:rows="isForm ? 5 : 4" :rows="isForm ? 5 : 4"

171
packages/nc-gui/components/smartsheet/Form.vue

@ -186,6 +186,8 @@ const focusLabel = ref<HTMLTextAreaElement>()
const searchQuery = ref('') const searchQuery = ref('')
const autoScrollFormField = ref(false)
const { t } = useI18n() const { t } = useI18n()
const { betaFeatureToggleState } = useBetaFeatureToggle() const { betaFeatureToggleState } = useBetaFeatureToggle()
@ -444,7 +446,7 @@ function setFormData() {
localColumns.value = col localColumns.value = col
.filter((f) => !hiddenColTypes.includes(f.uidt) && !systemFieldsIds.value.includes(f.fk_column_id)) .filter((f) => !hiddenColTypes.includes(f.uidt) && !systemFieldsIds.value.includes(f.fk_column_id))
.sort((a, b) => a.order - b.order) .sort((a, b) => a.order - b.order)
.map((c) => ({ ...c, label: c.label || c.title, required: !!c.required })) .map((c) => ({ ...c, required: !!c.required }))
} }
function isRequired(_columnObj: Record<string, any>, required = false) { function isRequired(_columnObj: Record<string, any>, required = false) {
@ -519,9 +521,13 @@ const columnSupportsScanning = (elementType: UITypes) =>
betaFeatureToggleState.show && betaFeatureToggleState.show &&
[UITypes.SingleLineText, UITypes.Number, UITypes.Email, UITypes.URL, UITypes.LongText].includes(elementType) [UITypes.SingleLineText, UITypes.Number, UITypes.Email, UITypes.URL, UITypes.LongText].includes(elementType)
const onFormItemClick = (element: any) => { const onFormItemClick = (element: any, sidebarClick: boolean = false) => {
if (isLocked.value || !isEditable) return if (isLocked.value || !isEditable) return
if (sidebarClick) {
autoScrollFormField.value = true
}
activeRow.value = element.id activeRow.value = element.id
} }
@ -643,6 +649,16 @@ const onFocusActiveFieldLabel = (e: FocusEvent) => {
;(e.target as HTMLTextAreaElement).select() ;(e.target as HTMLTextAreaElement).select()
} }
const updateFieldTitle = (value: string) => {
if (!activeField.value) return
if (activeField.value.title === value) {
activeField.value.label = null
} else {
activeField.value.label = value
}
}
const updateSelectFieldLayout = (value: boolean) => { const updateSelectFieldLayout = (value: boolean) => {
if (!activeField.value) return if (!activeField.value) return
@ -702,17 +718,31 @@ watch(
}, },
) )
watch(activeField, (newValue) => { const handleAutoScrollFormField = (title: string, isSidebar: boolean) => {
if (newValue) { const field = document.querySelector(
const field = document.querySelector(`.nc-form-field-item-${CSS.escape(newValue?.title?.replaceAll(' ', ''))}`) `${isSidebar ? '.nc-form-field-item-' : '.nc-form-drag-'}${CSS.escape(title?.replaceAll(' ', ''))}`,
)
if (field) { if (field) {
setTimeout(() => { setTimeout(() => {
field?.scrollIntoView({ behavior: 'smooth', block: 'center' }) field?.scrollIntoView({ behavior: 'smooth', block: 'center' })
}, 50) }, 50)
} }
}
watch(activeField, (newValue, oldValue) => {
if (newValue && autoScrollFormField.value) {
nextTick(() => {
handleAutoScrollFormField(newValue.title, false)
})
} else if (oldValue) {
nextTick(() => {
handleAutoScrollFormField(oldValue.title, true)
})
} }
autoScrollFormField.value = false
dropdownStates.value = { dropdownStates.value = {
...dropdownStates.value, ...dropdownStates.value,
showColumnMenu: false, showColumnMenu: false,
@ -756,10 +786,11 @@ useEventListener(
document, document,
'mousedown', 'mousedown',
(e: MouseEvent) => { (e: MouseEvent) => {
console.log('e.target', e.target)
if ( if (
(draggableRef.value?.targetDomElement && draggableRef.value?.targetDomElement.contains(e.target)) || (draggableRef.value?.targetDomElement && draggableRef.value?.targetDomElement.contains(e.target)) ||
(e.target as HTMLElement)?.closest( (e.target as HTMLElement)?.closest(
'.nc-form-right-panel, [class*="dropdown"], .nc-form-rich-text-field, .ant-modal, .ant-modal-wrap, .nc-share-base-button', '.nc-form-right-panel, [class*="dropdown"], .nc-form-rich-text-field, .ant-modal, .ant-modal-wrap, .nc-share-base-button, .nc-form-right-sidebar-content-resizable-wrapper .splitpanes__splitter',
) )
) { ) {
return return
@ -1360,7 +1391,7 @@ useEventListener(
<a-textarea <a-textarea
ref="focusLabel" ref="focusLabel"
v-model:value="activeField.label" :value="activeField.label || activeField.title"
:rows="1" :rows="1"
auto-size auto-size
hide-details hide-details
@ -1369,6 +1400,7 @@ useEventListener(
:placeholder="$t('msg.info.formInput')" :placeholder="$t('msg.info.formInput')"
@focus="onFocusActiveFieldLabel" @focus="onFocusActiveFieldLabel"
@keydown.enter.prevent @keydown.enter.prevent
@update:value="updateFieldTitle"
@change="updateColMeta(activeField)" @change="updateColMeta(activeField)"
/> />
@ -1427,26 +1459,29 @@ useEventListener(
</div> </div>
<!-- Limit options --> <!-- Limit options -->
<div v-if="isSelectTypeCol(activeField.uidt)" class="flex items-start justify-between gap-3"> <div v-if="isSelectTypeCol(activeField.uidt)" class="w-full flex items-start justify-between gap-3">
<div class="flex items-center gap-3"> <div class="flex-1 max-w-[calc(100%_-_40px)]">
<div class="flex-1"> <div class="font-medium text-gray-800">{{ $t('labels.limitOptions') }}</div>
<div class="font-medium text-gray-800">{{ $t('labels.limitOptions') }}</div> <div class="text-gray-500 mt-2">{{ $t('labels.limitOptionsSubtext') }}.</div>
<div class="text-gray-500 mt-2">{{ $t('labels.limitOptionsSubtext') }}.</div> <div v-if="activeField.meta.isLimitOption" class="mt-3">
<div v-if="activeField.meta.isLimitOption" class="mt-3"> <LazySmartsheetFormLimitOptions
<LazySmartsheetFormLimitOptions v-model:model-value="activeField.meta.limitOptions"
v-model:model-value="activeField.meta.limitOptions" :form-field-state="formState[activeField.title] || ''"
:column="activeField" :column="activeField"
:is-required="isRequired(activeField, activeField.required)" :is-required="isRequired(activeField, activeField.required)"
@update:model-value="updateColMeta(activeField)" @update:model-value="updateColMeta(activeField)"
></LazySmartsheetFormLimitOptions> @update:form-field-state="(value)=>{
</div> formState[activeField!.title] = value
}"
></LazySmartsheetFormLimitOptions>
</div> </div>
</div> </div>
<a-switch <a-switch
v-model:checked="activeField.meta.isLimitOption" v-model:checked="activeField.meta.isLimitOption"
v-e="['a:form-view:field:limit-options']" v-e="['a:form-view:field:limit-options']"
size="small" size="small"
class="nc-form-switch-focus" class="flex-none nc-form-switch-focus"
@change="updateColMeta(activeField)" @change="updateColMeta(activeField)"
/> />
</div> </div>
@ -1581,57 +1616,69 @@ useEventListener(
<div <div
v-if="field.title.toLowerCase().includes(searchQuery.toLowerCase())" v-if="field.title.toLowerCase().includes(searchQuery.toLowerCase())"
:key="field.id" :key="field.id"
class="w-full px-2 py-1.5 flex flex-row items-center border-b-1 last:border-none border-gray-200" class="w-full px-2 flex flex-row items-center border-b-1 last:border-none border-gray-200"
:class="[ :class="[
`nc-form-field-item-${field.title.replaceAll(' ', '')}`, `nc-form-field-item-${field.title.replaceAll(' ', '')}`,
`${activeRow === field.id ? 'bg-brand-50 font-medium' : 'hover:bg-gray-50'}`, `${activeRow === field.id ? 'bg-brand-50 font-medium' : 'hover:bg-gray-50'}`,
]" ]"
:data-testid="`nc-form-field-item-${field.title}`" :data-testid="`nc-form-field-item-${field.title}`"
> >
<component :is="iconMap.drag" class="flex-none cursor-move !h-4 !w-4 text-gray-600 mr-1" /> <div class="py-1.5 flex items-center">
<component :is="iconMap.drag" class="flex-none cursor-move !h-4 !w-4 text-gray-600 mr-1" />
</div>
<div <div
class="flex-1 flex items-center justify-between cursor-pointer max-w-[calc(100%_-_20px)]" class="flex-1 flex items-center justify-between cursor-pointer max-w-[calc(100%_-_20px)] py-1.5"
@click="showOrHideColumn(field, !field.show, true)"
> >
<SmartsheetHeaderVirtualCellIcon v-if="field && isVirtualCol(field)" :column-meta="field" /> <div
<SmartsheetHeaderCellIcon v-else :column-meta="field" /> class="flex-1 flex items-center cursor-pointer max-w-[calc(100%_-_40px)]"
<div class="flex-1 flex items-center justify-start max-w-[calc(100%_-_68px)] mr-4"> @click.prevent="onFormItemClick(field, true)"
<div class="w-full flex items-center"> >
<div class="ml-1 inline-flex" :class="field.label?.trim() ? 'max-w-1/2' : 'max-w-[95%]'"> <SmartsheetHeaderVirtualCellIcon v-if="field && isVirtualCol(field)" :column-meta="field" />
<NcTooltip class="truncate text-sm" :disabled="drag" show-on-truncate-only> <SmartsheetHeaderCellIcon v-else :column-meta="field" />
<template #title> <div class="flex-1 flex items-center justify-start max-w-[calc(100%_-_28px)]">
<div class="text-center"> <div class="w-full flex items-center">
{{ field.title }} <div class="ml-1 inline-flex" :class="field.label?.trim() ? 'max-w-1/2' : 'max-w-[95%]'">
</div> <NcTooltip class="truncate text-sm" :disabled="drag" show-on-truncate-only>
</template> <template #title>
<span data-testid="nc-field-title"> {{ field.title }} </span> <div class="text-center">
</NcTooltip> {{ field.title }}
</div> </div>
<div </template>
v-if="field.label?.trim()" <span data-testid="nc-field-title"> {{ field.title }} </span>
class="truncate inline-flex text-xs font-normal text-gray-700" </NcTooltip>
> </div>
<span>&nbsp;(</span> <div
<NcTooltip class="truncate" :disabled="drag" show-on-truncate-only> v-if="field.label?.trim()"
<template #title> class="truncate inline-flex text-xs font-normal text-gray-700"
<div class="text-center"> >
{{ field.label }} <span>&nbsp;(</span>
</div> <NcTooltip class="truncate" :disabled="drag" show-on-truncate-only>
</template> <template #title>
<span data-testid="nc-field-title ">{{ field.label?.trim() }}</span> <div class="text-center">
</NcTooltip> {{ field.label }}
<span>)</span> </div>
</template>
<span data-testid="nc-field-title ">{{ field.label?.trim() }}</span>
</NcTooltip>
<span>)</span>
</div>
<span v-if="isRequired(field, field.required)" class="text-red-500 text-sm align-top"
>&nbsp;*</span
>
</div> </div>
<span v-if="isRequired(field, field.required)" class="text-red-500 text-sm align-top"
>&nbsp;*</span
>
</div> </div>
</div> </div>
<a-switch <a-switch
:checked="!!field.show" :checked="!!field.show"
:disabled="field.required || isLocked || !isEditable" :disabled="field.required || isLocked || !isEditable"
class="nc-switch" class="flex-none nc-switch"
size="small" size="small"
@change="
(value) => {
showOrHideColumn(field, value, true)
}
"
/> />
</div> </div>
</div> </div>

27
packages/nc-gui/components/smartsheet/form/LimitOptions.vue

@ -8,15 +8,16 @@ import { MetaInj, iconMap } from '#imports'
const props = defineProps<{ const props = defineProps<{
modelValue: FormFieldsLimitOptionsType[] modelValue: FormFieldsLimitOptionsType[]
formFieldState?: string | null
column: ColumnType column: ColumnType
isRequired?: boolean isRequired?: boolean
}>() }>()
const emit = defineEmits(['update:modelValue']) const emits = defineEmits(['update:modelValue', 'update:formFieldState'])
const meta = inject(MetaInj)! const meta = inject(MetaInj)!
const column = toRef(props, 'column') const { column, formFieldState } = toRefs(props)
const basesStore = useBases() const basesStore = useBases()
@ -55,7 +56,7 @@ const vModel = computed({
.sort((a, b) => a.order - b.order) .sort((a, b) => a.order - b.order)
if ((props.modelValue || []).length !== collaborators.length) { if ((props.modelValue || []).length !== collaborators.length) {
emit( emits(
'update:modelValue', 'update:modelValue',
collaborators.map((o) => ({ id: o.id, order: o.order, show: o.show })), collaborators.map((o) => ({ id: o.id, order: o.order, show: o.show })),
) )
@ -78,7 +79,7 @@ const vModel = computed({
}) })
if ((props.modelValue || []).length !== ((column.value.colOptions as SelectOptionsType)?.options || []).length) { if ((props.modelValue || []).length !== ((column.value.colOptions as SelectOptionsType)?.options || []).length) {
emit( emits(
'update:modelValue', 'update:modelValue',
updateModelValue.map((o) => ({ id: o.id, order: o.order, show: o.show })), updateModelValue.map((o) => ({ id: o.id, order: o.order, show: o.show })),
) )
@ -88,10 +89,24 @@ const vModel = computed({
return [] return []
}, },
set: (val) => { set: (val) => {
emit( const fieldState = (formFieldState.value || '').split(',')
const optionsToRemoveFromFieldState: string[] = []
emits(
'update:modelValue', 'update:modelValue',
val.map((o) => ({ id: o.id, order: o.order, show: o.show })), val.map((o) => {
if (!o.show) {
if (column.value.uidt === UITypes.User && fieldState.includes(o.id)) {
optionsToRemoveFromFieldState.push(o.id)
} else if (o?.title && fieldState.includes(o.title)) {
optionsToRemoveFromFieldState.push(o.title)
}
}
return { id: o.id, order: o.order, show: o.show }
}),
) )
emits('update:formFieldState', fieldState.filter((o) => !optionsToRemoveFromFieldState.includes(o)).join(','))
}, },
}) })

Loading…
Cancel
Save