Browse Source

fix: Added format menu on top right of rich modal

pull/7046/head
Muhammed Mustafa 1 year ago
parent
commit
863472826e
  1. 6
      packages/nc-gui/components/cell/RichText.vue
  2. 90
      packages/nc-gui/components/cell/RichText/SelectedBubbleMenu.vue
  3. 66
      packages/nc-gui/components/cell/RichText/SelectedBubbleMenuPopup.vue
  4. 3
      packages/nc-gui/components/cell/TextArea.vue

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

@ -13,6 +13,7 @@ const props = defineProps<{
value?: string | null value?: string | null
readonly?: boolean readonly?: boolean
syncValueChange?: boolean syncValueChange?: boolean
showMenu?: boolean
}>() }>()
const emits = defineEmits(['update:value']) const emits = defineEmits(['update:value'])
@ -70,7 +71,10 @@ onMounted(() => {
<template> <template>
<div class="h-full"> <div class="h-full">
<CellRichTextSelectedBubbleMenu v-if="editor" :editor="editor" /> <div v-if="props.showMenu" class="absolute top-1 z-1000 right-3">
<CellRichTextSelectedBubbleMenu v-if="editor" :editor="editor" embed-mode />
</div>
<CellRichTextSelectedBubbleMenuPopup v-if="editor" :editor="editor" />
<CellRichTextLinkOptions v-if="editor" :editor="editor" /> <CellRichTextLinkOptions v-if="editor" :editor="editor" />
<EditorContent :editor="editor" class="nc-textarea-rich w-full h-full nc-text-rich-scroll nc-scrollbar-md" /> <EditorContent :editor="editor" class="nc-textarea-rich w-full h-full nc-text-rich-scroll nc-scrollbar-md" />
</div> </div>

90
packages/nc-gui/components/cell/RichText/SelectedBubbleMenu.vue

@ -1,71 +1,35 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { Editor } from '@tiptap/vue-3' import type { Editor } from '@tiptap/vue-3'
import { BubbleMenu } from '@tiptap/vue-3'
import MdiFormatBulletList from '~icons/mdi/format-list-bulleted' import MdiFormatBulletList from '~icons/mdi/format-list-bulleted'
import MdiFormatStrikeThrough from '~icons/mdi/format-strikethrough' import MdiFormatStrikeThrough from '~icons/mdi/format-strikethrough'
import MdiFormatListNumber from '~icons/mdi/format-list-numbered' import MdiFormatListNumber from '~icons/mdi/format-list-numbered'
import MdiFormatListCheckbox from '~icons/mdi/format-list-checkbox' import MdiFormatListCheckbox from '~icons/mdi/format-list-checkbox'
const { editor } = defineProps<Props>()
interface Props { interface Props {
editor: Editor editor: Editor
embedMode?: boolean
} }
// Debounce show menu to prevent flickering const props = defineProps<Props>()
const showMenu = computed(() => {
if (!editor) return false
return !editor.state.selection.empty
})
const showMenuDebounced = ref(false)
watchDebounced(
() => showMenu.value,
(value) => {
showMenuDebounced.value = value
},
{
debounce: 200,
maxWait: 800,
immediate: true,
},
)
const handleEditorMouseDown = (e: MouseEvent) => {
const domsInEvent = document.elementsFromPoint(e.clientX, e.clientY) as HTMLElement[]
const isBubble = domsInEvent.some((dom) => dom?.classList?.contains('bubble-menu'))
if (isBubble) return
const pageContent = document.querySelector('.nc-docs-page-wrapper')
pageContent?.classList.add('bubble-menu-hidden')
}
const handleEditorMouseUp = (e: MouseEvent) => { const editor = computed(() => props.editor)
const domsInEvent = document.elementsFromPoint(e.clientX, e.clientY) as HTMLElement[]
const isBubble = domsInEvent.some((dom) => dom?.classList?.contains('bubble-menu'))
if (isBubble) return
setTimeout(() => { const embedMode = computed(() => props.embedMode)
const pageContent = document.querySelector('.nc-docs-page-wrapper')
pageContent?.classList.remove('bubble-menu-hidden')
}, 100)
}
const onToggleLink = () => { const onToggleLink = () => {
const activeNode = editor?.state?.selection?.$from?.nodeBefore || editor?.state?.selection?.$from?.nodeAfter const activeNode = editor.value?.state?.selection?.$from?.nodeBefore || editor.value?.state?.selection?.$from?.nodeAfter
const isLinkMarkedStoredInEditor = editor?.state?.storedMarks?.some((mark: Mark) => mark.type.name === 'link') const isLinkMarkedStoredInEditor = editor.value?.state?.storedMarks?.some((mark: any) => mark.type.name === 'link')
const isActiveNodeMarkActive = activeNode?.marks?.some((mark: Mark) => mark.type.name === 'link') || isLinkMarkedStoredInEditor const isActiveNodeMarkActive = activeNode?.marks?.some((mark: any) => mark.type.name === 'link') || isLinkMarkedStoredInEditor
if (isActiveNodeMarkActive) { if (isActiveNodeMarkActive) {
editor!.chain().focus().unsetLink().run() editor.value!.chain().focus().unsetLink().run()
} else { } else {
editor! editor
.chain() .value!.chain()
.focus() .focus()
.toggleLink({ .setLink({
href: '', href: '',
}) })
.selectTextblockEnd() .selectTextblockEnd()
@ -79,21 +43,16 @@ const onToggleLink = () => {
}, 100) }, 100)
} }
} }
onMounted(() => {
document.addEventListener('mouseup', handleEditorMouseUp)
document.addEventListener('mousedown', handleEditorMouseDown)
})
onUnmounted(() => {
document.removeEventListener('mouseup', handleEditorMouseUp)
document.removeEventListener('mousedown', handleEditorMouseDown)
})
</script> </script>
<template> <template>
<BubbleMenu :editor="editor" :update-delay="300" :tippy-options="{ duration: 100, maxWidth: 600 }"> <div
<div v-if="showMenuDebounced" class="bubble-menu flex flex-row gap-x-1 bg-gray-100 py-1 rounded-lg px-1"> class="bubble-menu flex flex-row gap-x-1 bg-gray-100 py-1 rounded-lg px-1"
:class="{
'embed-mode': embedMode,
'full-mode': !embedMode,
}"
>
<a-button <a-button
type="text" type="text"
:class="{ 'is-active': editor.isActive('bold') }" :class="{ 'is-active': editor.isActive('bold') }"
@ -182,7 +141,6 @@ onUnmounted(() => {
</div> </div>
</a-button> </a-button>
</div> </div>
</BubbleMenu>
</template> </template>
<style lang="scss"> <style lang="scss">
@ -205,11 +163,19 @@ onUnmounted(() => {
@apply rounded-md py-1 my-0 pl-2.5 pr-3 cursor-pointer items-center gap-x-2.5 hover:bg-gray-100; @apply rounded-md py-1 my-0 pl-2.5 pr-3 cursor-pointer items-center gap-x-2.5 hover:bg-gray-100;
} }
.bubble-menu.full-mode {
@apply border-gray-100
box-shadow: 0px 0px 1.2rem 0 rgb(230, 230, 230) !important;
}
.bubble-menu.embed-mode {
@apply border-transparent !shadow-none;
}
.bubble-menu { .bubble-menu {
// shadow // shadow
@apply border-gray-100 bg-white; @apply bg-white;
border-width: 1px; border-width: 1px;
box-shadow: 0px 0px 1.2rem 0 rgb(230, 230, 230) !important;
.is-active { .is-active {
@apply border-1 !hover:bg-gray-200 border-1 border-gray-200 bg-gray-100; @apply border-1 !hover:bg-gray-200 border-1 border-gray-200 bg-gray-100;

66
packages/nc-gui/components/cell/RichText/SelectedBubbleMenuPopup.vue

@ -0,0 +1,66 @@
<script lang="ts" setup>
import type { Editor } from '@tiptap/vue-3'
import { BubbleMenu } from '@tiptap/vue-3'
const { editor } = defineProps<Props>()
interface Props {
editor: Editor
}
// Debounce show menu to prevent flickering
const showMenu = computed(() => {
if (!editor) return false
return !editor.state.selection.empty
})
const showMenuDebounced = ref(false)
watchDebounced(
() => showMenu.value,
(value) => {
showMenuDebounced.value = value
},
{
debounce: 200,
maxWait: 800,
immediate: true,
},
)
const handleEditorMouseDown = (e: MouseEvent) => {
const domsInEvent = document.elementsFromPoint(e.clientX, e.clientY) as HTMLElement[]
const isBubble = domsInEvent.some((dom) => dom?.classList?.contains('bubble-menu'))
if (isBubble) return
const pageContent = document.querySelector('.nc-docs-page-wrapper')
pageContent?.classList.add('bubble-menu-hidden')
}
const handleEditorMouseUp = (e: MouseEvent) => {
const domsInEvent = document.elementsFromPoint(e.clientX, e.clientY) as HTMLElement[]
const isBubble = domsInEvent.some((dom) => dom?.classList?.contains('bubble-menu'))
if (isBubble) return
setTimeout(() => {
const pageContent = document.querySelector('.nc-docs-page-wrapper')
pageContent?.classList.remove('bubble-menu-hidden')
}, 100)
}
onMounted(() => {
document.addEventListener('mouseup', handleEditorMouseUp)
document.addEventListener('mousedown', handleEditorMouseDown)
})
onUnmounted(() => {
document.removeEventListener('mouseup', handleEditorMouseUp)
document.removeEventListener('mousedown', handleEditorMouseDown)
})
</script>
<template>
<BubbleMenu :editor="editor" :update-delay="300" :tippy-options="{ duration: 100, maxWidth: 600 }">
<CellRichTextSelectedBubbleMenu v-if="showMenuDebounced" :editor="editor" />
</BubbleMenu>
</template>

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

@ -171,7 +171,7 @@ watch(editEnabled, () => {
</div> </div>
</div> </div>
<template #overlay> <template #overlay>
<div ref="inputWrapperRef" class="flex flex-col min-w-120 min-h-70 py-3 pl-3 pr-1 expanded-cell-input"> <div ref="inputWrapperRef" class="flex flex-col min-w-120 min-h-70 py-3 pl-3 pr-1 expanded-cell-input relative">
<div <div
v-if="column" v-if="column"
class="flex flex-row gap-x-1 items-center font-medium pb-2.5 mb-1 py-1 mr-3 ml-1 border-b-1 border-gray-100" class="flex flex-row gap-x-1 items-center font-medium pb-2.5 mb-1 py-1 mr-3 ml-1 border-b-1 border-gray-100"
@ -201,6 +201,7 @@ watch(editEnabled, () => {
:style="{ :style="{
'max-height': 'calc(min(60vh, 100rem))', 'max-height': 'calc(min(60vh, 100rem))',
}" }"
show-menu
/> />
</div> </div>
</template> </template>

Loading…
Cancel
Save