Browse Source

Nc fix: Form view bug fixes (#8322)

* fix(nc-gui): form view rich text link option issue

* chore(nc-gui): lint

* fix(nc-gui): pw test fail issue

* fix(nc-gui): add checkbox required form validation rule
pull/8330/head
Ramesh Mane 2 months ago committed by GitHub
parent
commit
6075611c0f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 44
      packages/nc-gui/components/cell/RichText.vue
  2. 13
      packages/nc-gui/components/cell/RichText/LinkOptions.vue
  3. 1
      packages/nc-gui/components/smartsheet/Form.vue
  4. 8
      packages/nc-gui/composables/useSharedFormViewStore.ts

44
packages/nc-gui/components/cell/RichText.vue

@ -59,6 +59,10 @@ const isFocused = ref(false)
const keys = useMagicKeys()
const shouldShowLinkOption = computed(() => {
return isFormField.value ? isFocused.value : true
})
const turndownService = new TurndownService({})
turndownService.addRule('lineBreak', {
@ -133,6 +137,8 @@ marked.use({ extensions: [checkListItem] })
const editorDom = ref<HTMLElement | null>(null)
const richTextLinkOptionRef = ref<HTMLElement | null>(null)
const vModel = useVModel(props, 'value', emits, { defaultValue: '' })
const tiptapExtensions = [
@ -167,7 +173,7 @@ const editor = useEditor({
emits('focus')
},
onBlur: (e) => {
if (!(e?.event?.relatedTarget as HTMLElement)?.closest('.bubble-menu, .nc-textarea-rich-editor')) {
if (!(e?.event?.relatedTarget as HTMLElement)?.closest('.bubble-menu, .nc-textarea-rich-editor, .nc-rich-text')) {
isFocused.value = false
emits('blur')
}
@ -239,13 +245,37 @@ useEventListener(
'focusout',
(e: FocusEvent) => {
const targetEl = e?.relatedTarget as HTMLElement
if (targetEl?.classList?.contains('tiptap') || !targetEl?.closest('.bubble-menu, .nc-textarea-rich-editor')) {
if (targetEl?.classList?.contains('tiptap') || !targetEl?.closest('.bubble-menu, .tippy-content, .nc-textarea-rich-editor')) {
isFocused.value = false
emits('blur')
}
},
true,
)
useEventListener(
richTextLinkOptionRef,
'focusout',
(e: FocusEvent) => {
const targetEl = e?.relatedTarget as HTMLElement
if (!targetEl && (e.target as HTMLElement)?.closest('.bubble-menu, .tippy-content, .nc-textarea-rich-editor')) return
if (!targetEl?.closest('.bubble-menu, .tippy-content, .nc-textarea-rich-editor')) {
isFocused.value = false
emits('blur')
}
},
true,
)
onClickOutside(editorDom, (e) => {
if (!isFocused.value) return
const targetEl = e?.target as HTMLElement
if (!targetEl?.closest('.bubble-menu,.tippy-content, .nc-textarea-rich-editor')) {
isFocused.value = false
emits('blur')
}
})
</script>
<template>
@ -281,7 +311,15 @@ useEventListener(
</div>
<CellRichTextSelectedBubbleMenuPopup v-if="editor && !isFormField && !isForm" :editor="editor" />
<CellRichTextLinkOptions v-if="editor" :editor="editor" />
<template v-if="shouldShowLinkOption">
<CellRichTextLinkOptions
v-if="editor"
ref="richTextLinkOptionRef"
:editor="editor"
:is-form-field="isFormField"
@blur="isFocused = false"
/>
</template>
<EditorContent
ref="editorDom"

13
packages/nc-gui/components/cell/RichText/LinkOptions.vue

@ -6,11 +6,14 @@ import type { Mark } from 'prosemirror-model'
const props = defineProps<Props>()
const emits = defineEmits(['blur'])
interface Props {
editor: Editor
isFormField?: boolean
}
const editor = computed(() => props.editor)
const { editor, isFormField } = toRefs(props)
const inputRef = ref<HTMLInputElement>()
const linkNodeMark = ref<Mark | undefined>()
@ -164,6 +167,10 @@ const onMountLinkOptions = (e) => {
e.popper.style.width = '95%'
}
}
const tabIndex = computed(() => {
return isFormField.value ? -1 : 0
})
</script>
<template>
@ -188,17 +195,20 @@ const onMountLinkOptions = (e) => {
<a-input
ref="inputRef"
v-model:value="href"
:tabindex="tabIndex"
class="nc-text-area-rich-link-option-input flex-1 !mx-0.5 !px-1.5 !py-0.5 !rounded-md z-10"
:bordered="false"
placeholder="Enter a link"
@change="onChange"
@press-enter="onInputBoxEnter"
@keydown="handleInputBoxKeyDown"
@blur="emits('blur')"
/>
</div>
<NcTooltip overlay-class-name="nc-text-area-rich-link-options">
<template #title> Open link </template>
<NcButton
:tabindex="tabIndex"
:class="{
'!text-gray-300 cursor-not-allowed': href.length === 0,
}"
@ -213,6 +223,7 @@ const onMountLinkOptions = (e) => {
<NcTooltip overlay-class-name="nc-text-area-rich-link-options">
<template #title> Delete link </template>
<NcButton
:tabindex="tabIndex"
class="!duration-0 !hover:(text-red-400 bg-red-50)"
data-testid="nc-text-area-rich-link-options-open-delete"
size="small"

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

@ -622,6 +622,7 @@ const formElementValidationRules = (element) => {
{
required: isRequired(element, element.required),
message: t('msg.error.fieldRequired', { value: 'This field' }),
...(element.uidt === UITypes.Checkbox && isRequired(element, element.required) ? { type: 'enum', enum: [1, true] } : {}),
},
]

8
packages/nc-gui/composables/useSharedFormViewStore.ts

@ -1,5 +1,5 @@
import useVuelidate from '@vuelidate/core'
import { helpers, minLength, required } from '@vuelidate/validators'
import { helpers, minLength, required, sameAs } from '@vuelidate/validators'
import dayjs from 'dayjs'
import type { Ref } from 'vue'
import type {
@ -100,8 +100,8 @@ const [useProvideSharedFormStore, useSharedFormStore] = useInjectionState((share
}),
)
const fieldRequired = (fieldName = 'This field') =>
helpers.withMessage(t('msg.error.fieldRequired', { value: fieldName }), required)
const fieldRequired = (fieldName = 'This field', isBoolean = false) =>
helpers.withMessage(t('msg.error.fieldRequired', { value: fieldName }), isBoolean ? sameAs(true) : required)
const formColumns = computed(() =>
columns.value
@ -217,7 +217,7 @@ const [useProvideSharedFormStore, useSharedFormStore] = useInjectionState((share
((column.rqd && !column.cdf) || (column.pk && !(column.ai || column.cdf)) || column.required)
) {
obj.localState[column.title!] = {
required: fieldRequired(),
required: fieldRequired(undefined, column.uidt === UITypes.Checkbox && column.required ? true : false),
}
} else if (
isLinksOrLTAR(column) &&

Loading…
Cancel
Save