Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 5 commits into from
Mar 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/nc-gui/components/cell/Rating.vue
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ watch(rateDomRef, () => {
:count="ratingMeta.max"
:class="readOnly ? 'pointer-events-none' : ''"
:style="`color: ${ratingMeta.color}; padding: ${isExpandedFormOpen ? '0px 8px' : '0px 5px'};`"
:key="ratingMeta.icon.full"
@keydown="onKeyPress"
>
<template #character>
Expand Down
28 changes: 22 additions & 6 deletions packages/nc-gui/components/cell/TextArea.vue
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,13 @@ const rowHeight = inject(RowHeightInj, ref(1 as const))

const isForm = inject(IsFormInj, ref(false))

const readOnly = inject(ReadonlyInj, ref(false))

const { showNull } = useGlobal()

const vModel = useVModel(props, 'modelValue', emits)
const vModel = useVModel(props, 'modelValue', emits, {
shouldEmit: () => !readOnly.value,
})

const isExpandedFormOpen = inject(IsExpandedFormOpenInj, ref(false))!

Expand Down Expand Up @@ -77,8 +81,6 @@ const inputWrapperRef = ref<HTMLElement | null>(null)

const inputRef = ref<HTMLTextAreaElement | null>(null)

const readOnly = inject(ReadonlyInj)

watch(isVisible, () => {
if (isVisible.value) {
setTimeout(() => {
Expand Down Expand Up @@ -211,8 +213,22 @@ watch(inputWrapperRef, () => {
}"
>
<div v-if="isForm && isRichMode" class="w-full">
<div class="w-full relative pt-11 w-full px-0 pb-1">
<LazyCellRichText v-model:value="vModel" class="border-t-1 border-gray-100 !max-h-50" :autofocus="false" show-menu />
<div
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>

Expand All @@ -233,7 +249,7 @@ watch(inputWrapperRef, () => {
<LazyCellRichText v-model:value="vModel" sync-value-change read-only />
</div>
<textarea
v-else-if="editEnabled && !isVisible"
v-else-if="(editEnabled && !isVisible) || isForm"
:ref="focus"
v-model="vModel"
:rows="isForm ? 5 : 4"
Expand Down
171 changes: 109 additions & 62 deletions packages/nc-gui/components/smartsheet/Form.vue
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,8 @@ const focusLabel = ref<HTMLTextAreaElement>()

const searchQuery = ref('')

const autoScrollFormField = ref(false)

const { t } = useI18n()

const { betaFeatureToggleState } = useBetaFeatureToggle()
Expand Down Expand Up @@ -444,7 +446,7 @@ function setFormData() {
localColumns.value = col
.filter((f) => !hiddenColTypes.includes(f.uidt) && !systemFieldsIds.value.includes(f.fk_column_id))
.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) {
Expand Down Expand Up @@ -519,9 +521,13 @@ const columnSupportsScanning = (elementType: UITypes) =>
betaFeatureToggleState.show &&
[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 (sidebarClick) {
autoScrollFormField.value = true
}

activeRow.value = element.id
}

Expand Down Expand Up @@ -643,6 +649,16 @@ const onFocusActiveFieldLabel = (e: FocusEvent) => {
;(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) => {
if (!activeField.value) return

Expand Down Expand Up @@ -702,17 +718,31 @@ watch(
},
)

watch(activeField, (newValue) => {
if (newValue) {
const field = document.querySelector(`.nc-form-field-item-${CSS.escape(newValue?.title?.replaceAll(' ', ''))}`)
const handleAutoScrollFormField = (title: string, isSidebar: boolean) => {
const field = document.querySelector(
`${isSidebar ? '.nc-form-field-item-' : '.nc-form-drag-'}${CSS.escape(title?.replaceAll(' ', ''))}`,
)

if (field) {
setTimeout(() => {
field?.scrollIntoView({ behavior: 'smooth', block: 'center' })
}, 50)
}
if (field) {
setTimeout(() => {
field?.scrollIntoView({ behavior: 'smooth', block: 'center' })
}, 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,
showColumnMenu: false,
Expand Down Expand Up @@ -756,10 +786,11 @@ useEventListener(
document,
'mousedown',
(e: MouseEvent) => {
console.log('e.target', e.target)
if (
(draggableRef.value?.targetDomElement && draggableRef.value?.targetDomElement.contains(e.target)) ||
(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
Expand Down Expand Up @@ -1360,7 +1391,7 @@ useEventListener(

<a-textarea
ref="focusLabel"
v-model:value="activeField.label"
:value="activeField.label || activeField.title"
:rows="1"
auto-size
hide-details
Expand All @@ -1369,6 +1400,7 @@ useEventListener(
:placeholder="$t('msg.info.formInput')"
@focus="onFocusActiveFieldLabel"
@keydown.enter.prevent
@update:value="updateFieldTitle"
@change="updateColMeta(activeField)"
/>

Expand Down Expand Up @@ -1427,26 +1459,29 @@ useEventListener(
</div>

<!-- Limit options -->
<div v-if="isSelectTypeCol(activeField.uidt)" class="flex items-start justify-between gap-3">
<div class="flex items-center gap-3">
<div class="flex-1">
<div class="font-medium text-gray-800">{{ $t('labels.limitOptions') }}</div>
<div class="text-gray-500 mt-2">{{ $t('labels.limitOptionsSubtext') }}.</div>
<div v-if="activeField.meta.isLimitOption" class="mt-3">
<LazySmartsheetFormLimitOptions
v-model:model-value="activeField.meta.limitOptions"
:column="activeField"
:is-required="isRequired(activeField, activeField.required)"
@update:model-value="updateColMeta(activeField)"
></LazySmartsheetFormLimitOptions>
</div>
<div v-if="isSelectTypeCol(activeField.uidt)" class="w-full flex items-start justify-between gap-3">
<div class="flex-1 max-w-[calc(100%_-_40px)]">
<div class="font-medium text-gray-800">{{ $t('labels.limitOptions') }}</div>
<div class="text-gray-500 mt-2">{{ $t('labels.limitOptionsSubtext') }}.</div>
<div v-if="activeField.meta.isLimitOption" class="mt-3">
<LazySmartsheetFormLimitOptions
v-model:model-value="activeField.meta.limitOptions"
:form-field-state="formState[activeField.title] || ''"
:column="activeField"
:is-required="isRequired(activeField, activeField.required)"
@update:model-value="updateColMeta(activeField)"
@update:form-field-state="(value)=>{
formState[activeField!.title] = value
}"
></LazySmartsheetFormLimitOptions>
</div>
</div>

<a-switch
v-model:checked="activeField.meta.isLimitOption"
v-e="['a:form-view:field:limit-options']"
size="small"
class="nc-form-switch-focus"
class="flex-none nc-form-switch-focus"
@change="updateColMeta(activeField)"
/>
</div>
Expand Down Expand Up @@ -1581,57 +1616,69 @@ useEventListener(
<div
v-if="field.title.toLowerCase().includes(searchQuery.toLowerCase())"
: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="[
`nc-form-field-item-${field.title.replaceAll(' ', '')}`,
`${activeRow === field.id ? 'bg-brand-50 font-medium' : 'hover:bg-gray-50'}`,
]"
: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
class="flex-1 flex items-center justify-between cursor-pointer max-w-[calc(100%_-_20px)]"
@click="showOrHideColumn(field, !field.show, true)"
class="flex-1 flex items-center justify-between cursor-pointer max-w-[calc(100%_-_20px)] py-1.5"
>
<SmartsheetHeaderVirtualCellIcon v-if="field && isVirtualCol(field)" :column-meta="field" />
<SmartsheetHeaderCellIcon v-else :column-meta="field" />
<div class="flex-1 flex items-center justify-start max-w-[calc(100%_-_68px)] mr-4">
<div class="w-full flex items-center">
<div class="ml-1 inline-flex" :class="field.label?.trim() ? 'max-w-1/2' : 'max-w-[95%]'">
<NcTooltip class="truncate text-sm" :disabled="drag" show-on-truncate-only>
<template #title>
<div class="text-center">
{{ field.title }}
</div>
</template>
<span data-testid="nc-field-title"> {{ field.title }} </span>
</NcTooltip>
</div>
<div
v-if="field.label?.trim()"
class="truncate inline-flex text-xs font-normal text-gray-700"
>
<span>&nbsp;(</span>
<NcTooltip class="truncate" :disabled="drag" show-on-truncate-only>
<template #title>
<div class="text-center">
{{ field.label }}
</div>
</template>
<span data-testid="nc-field-title ">{{ field.label?.trim() }}</span>
</NcTooltip>
<span>)</span>
<div
class="flex-1 flex items-center cursor-pointer max-w-[calc(100%_-_40px)]"
@click.prevent="onFormItemClick(field, true)"
>
<SmartsheetHeaderVirtualCellIcon v-if="field && isVirtualCol(field)" :column-meta="field" />
<SmartsheetHeaderCellIcon v-else :column-meta="field" />
<div class="flex-1 flex items-center justify-start max-w-[calc(100%_-_28px)]">
<div class="w-full flex items-center">
<div class="ml-1 inline-flex" :class="field.label?.trim() ? 'max-w-1/2' : 'max-w-[95%]'">
<NcTooltip class="truncate text-sm" :disabled="drag" show-on-truncate-only>
<template #title>
<div class="text-center">
{{ field.title }}
</div>
</template>
<span data-testid="nc-field-title"> {{ field.title }} </span>
</NcTooltip>
</div>
<div
v-if="field.label?.trim()"
class="truncate inline-flex text-xs font-normal text-gray-700"
>
<span>&nbsp;(</span>
<NcTooltip class="truncate" :disabled="drag" show-on-truncate-only>
<template #title>
<div class="text-center">
{{ field.label }}
</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>
<span v-if="isRequired(field, field.required)" class="text-red-500 text-sm align-top"
>&nbsp;*</span
>
</div>
</div>

<a-switch
:checked="!!field.show"
:disabled="field.required || isLocked || !isEditable"
class="nc-switch"
class="flex-none nc-switch"
size="small"
@change="
(value) => {
showOrHideColumn(field, value, true)
}
"
/>
</div>
</div>
Expand Down
27 changes: 21 additions & 6 deletions packages/nc-gui/components/smartsheet/form/LimitOptions.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,16 @@ import { MetaInj, iconMap } from '#imports'
const props = defineProps<{
modelValue: FormFieldsLimitOptionsType[]
formFieldState?: string | null
column: ColumnType
isRequired?: boolean
}>()
const emit = defineEmits(['update:modelValue'])
const emits = defineEmits(['update:modelValue', 'update:formFieldState'])
const meta = inject(MetaInj)!
const column = toRef(props, 'column')
const { column, formFieldState } = toRefs(props)
const basesStore = useBases()
Expand Down Expand Up @@ -55,7 +56,7 @@ const vModel = computed({
.sort((a, b) => a.order - b.order)
if ((props.modelValue || []).length !== collaborators.length) {
emit(
emits(
'update:modelValue',
collaborators.map((o) => ({ id: o.id, order: o.order, show: o.show })),
)
Expand All @@ -78,7 +79,7 @@ const vModel = computed({
})
if ((props.modelValue || []).length !== ((column.value.colOptions as SelectOptionsType)?.options || []).length) {
emit(
emits(
'update:modelValue',
updateModelValue.map((o) => ({ id: o.id, order: o.order, show: o.show })),
)
Expand All @@ -88,10 +89,24 @@ const vModel = computed({
return []
},
set: (val) => {
emit(
const fieldState = (formFieldState.value || '').split(',')
const optionsToRemoveFromFieldState: string[] = []
emits(
'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(','))
},
})
Expand Down