From 779db0104be864dc0a3f18b8faf4dba485a68354 Mon Sep 17 00:00:00 2001 From: Ramesh Mane <101566080+rameshmane7218@users.noreply.github.com> Date: Tue, 26 Mar 2024 23:15:01 +0530 Subject: [PATCH] Nc Feat: Allow inline edit rich text field in form view (#7974) * feat(nc-gui): allow inline edit rich text field in form view setup * fix(nc-gui): rich text link options width issue * fix(nc-gui): form view title, description on focus bg color issue * fix(nc-gui): form view rich text field shift tab focus out issue * fix(nc-gui): set max height of rich text field in form view to 240px * fix(nc-gui): rich text full mode options visibility issue * chore(nc-gui): lint --- packages/nc-gui/components/cell/RichText.vue | 47 ++++++++++++++----- .../components/cell/RichText/LinkOptions.vue | 22 +++++++-- packages/nc-gui/components/cell/TextArea.vue | 10 +++- .../nc-gui/components/smartsheet/Form.vue | 18 ++++--- .../index/[typeOrId]/form/[viewId]/index.vue | 18 +++++-- 5 files changed, 83 insertions(+), 32 deletions(-) diff --git a/packages/nc-gui/components/cell/RichText.vue b/packages/nc-gui/components/cell/RichText.vue index 4bc2c87660..f3a4aabaec 100644 --- a/packages/nc-gui/components/cell/RichText.vue +++ b/packages/nc-gui/components/cell/RichText.vue @@ -10,7 +10,7 @@ import Placeholder from '@tiptap/extension-placeholder' import { TaskItem } from '@/helpers/dbTiptapExtensions/task-item' import { Link } from '@/helpers/dbTiptapExtensions/links' import type { RichTextBubbleMenuOptions } from '#imports' -import { IsExpandedFormOpenInj, IsFormInj, IsGridInj, ReadonlyInj, RowHeightInj } from '#imports' +import { IsExpandedFormOpenInj, IsFormInj, IsGridInj, IsSurveyFormInj, ReadonlyInj, RowHeightInj } from '#imports' const props = withDefaults( defineProps<{ @@ -26,13 +26,14 @@ const props = withDefaults( hiddenBubbleMenuOptions?: RichTextBubbleMenuOptions[] }>(), { + isFormField: false, hiddenBubbleMenuOptions: () => [], }, ) -const emits = defineEmits(['update:value']) +const emits = defineEmits(['update:value', 'focus', 'blur']) -const { hiddenBubbleMenuOptions } = toRefs(props) +const { isFormField, hiddenBubbleMenuOptions } = toRefs(props) const isExpandedFormOpen = inject(IsExpandedFormOpenInj, ref(false))! @@ -44,8 +45,12 @@ const isForm = inject(IsFormInj, ref(false)) const isGrid = inject(IsGridInj, ref(false)) +const isSurveyForm = inject(IsSurveyFormInj, ref(false)) + const isFocused = ref(false) +const keys = useMagicKeys() + const turndownService = new TurndownService({}) turndownService.addRule('lineBreak', { @@ -124,7 +129,7 @@ const vModel = useVModel(props, 'value', emits, { defaultValue: '' }) const tiptapExtensions = [ StarterKit.configure({ - heading: props.isFormField ? false : undefined, + heading: isFormField.value ? false : undefined, }), TaskList, TaskItem.configure({ @@ -145,16 +150,18 @@ const editor = useEditor({ .turndown(editor.getHTML().replaceAll(/

<\/p>/g, '
')) .replaceAll(/\n\n
\n\n/g, '
\n\n') - vModel.value = props.isFormField && markdown === '
' ? '' : markdown + vModel.value = isFormField.value && markdown === '
' ? '' : markdown }, editable: !props.readOnly, autofocus: props.autofocus, onFocus: () => { isFocused.value = true + emits('focus') }, onBlur: (e) => { if (!(e?.event?.relatedTarget as HTMLElement)?.closest('.bubble-menu, .nc-textarea-rich-editor')) { isFocused.value = false + emits('blur') } }, }) @@ -185,13 +192,19 @@ const setEditorContent = (contentMd: any, focusEndOfDoc?: boolean) => { }, 100) } +const onFocusWrapper = () => { + if (isForm.value && !isFormField.value && !props.readOnly && !keys.shift.value) { + editor.value?.chain().focus().run() + } +} + if (props.syncValueChange) { watch([vModel, editor], () => { setEditorContent(vModel.value) }) } -if (props.isFormField) { +if (isFormField.value) { watch([props, editor], () => { if (props.readOnly) { editor.value?.setEditable(false) @@ -206,7 +219,7 @@ watch(editorDom, () => { setEditorContent(vModel.value, true) - if (props.isFormField) return + if ((isForm.value && !isSurveyForm.value) || isFormField.value) return // Focus editor after editor is mounted setTimeout(() => { editor.value?.chain().focus().run() @@ -220,6 +233,7 @@ useEventListener( const targetEl = e?.relatedTarget as HTMLElement if (targetEl?.classList?.contains('tiptap') || !targetEl?.closest('.bubble-menu, .nc-textarea-rich-editor')) { isFocused.value = false + emits('blur') } }, true, @@ -228,7 +242,7 @@ useEventListener(