mirror of
https://github.com/nocodb/nocodb.git
synced 2026-05-02 16:26:57 +00:00
* fix(nc-gui): remove field modal title & type selector label * fix(nc-gui): hide default value input initially * fix(nc-gui): remove clear icon from default value input * fix(nc-gui): update email, phone, url column validation settings ui * fix(nc-gui): add missing validate field condition * fix(nc-gui): update long text field modal ui * fix(nc-gui): update default field modal width & enable rich text option * fix(nc-gui): small changes * fix(nc-gui): hide default value input only if user clicks on delete icon * fix(nc-gui): decimal field option ui * fix(nc-gui): update percent option ui * fix(nc-gui): small changes * fix(nc-gui): update field modal switch option alignment * fix(nc-gui): update date & dateTime field modal options * feat(nc-gui): add 12, 24 hrs time format option in field modal * feat(nc-gui): add 12hr time cell format support * fix(nc-gui): update time cell placeholder according to time format * fix(nc-gui): field modal default value option visibility issue * fix(nc-gui): update barcode, qr code field modal option * fix(nc-gui): field modal expanded json input modal overlay click issue * fix(nc-gui): currency code option from field modal * fix(nc-gui): udpate duration option * fix(nc-gui): date time cell clear icon visibility issue in link record dropdown * fix(nc-gui): update field modal lookup options * fix(nc-gui): update user option from field modal * fix(nc-gui): update rollup option from field modal * fix(nc-gui): update select field type ui for create column * fix(nc-gui): update field modal cancel & save btn alignment * fix(nc-gui): update formula option margin * fic(nc-gui): update select type option * fix(nc-gui): small changes * fix(nc-gui): update links field options * fix(nc-gui): small changes * fix(nc-gui): select option border issue * fix(nc-gui): add new color picker * fix(nc-gui): update rating field options * fix(nc-gui): update geodata field options * fix(nc-gui): geodata option small changes * fix(nc-gui): add new color picker for select type options * fix(nc-gui): show only title & field type list if uidt is null * feat(nc-gui): add 12hrs time support in dateTime cell * fix(nc-gui): formula suggestion list visibility issue * fix(nc-gui): reduce formaula suggestion fields icon size * fix(nc-gui): rich text default value visibility issue in field modal * fix(nc-gui): update rich text default value bubble menu option * fix(nc-gui): some pr review changes * fix(nc-gui): remove example from duration format * feat(nc-gui): add keyboard navigation support for field list * fix(nc-gui): update email, url, phone validate text * fix(nc-gui): update qr & barcode value select input * fix(nc-gui): pr review changes * test: update create column test cases * fix(nc-gui): remove required symbol from field modal inputs * fix(nc-gui): remove delete default value icon and add cross icon in default input itself * test: update duration field type test * fix(nc-gui): update column name & type input shadow * fix(nc-gui): add hover effect on selected type * fix(nc-gui): enabel rich text case update * fix(nc-gui): update select options * fix(nc-gui): show full time format in edit modal default value * fix(nc-gui): remove optional placeholder of default value * fix(nc-gui): instead of removing field type option disable it if it is onlyNameUpdateOnEditColumns * fix(nc-gui): update links field icons from field modal * fix(nc-gui): add links options in edit modal * fix(nc-gui): show links config and disable if it is not editable in edit column mode * fix(nc-gui): add support to configure date time format for create & last modified type field * fix(nc-gui): virtual field icon visibility issue if it is edit mode * fix(nc-gui): disabled edit option from field context menu if column is pk or system column * fix(nc-gui): update field modal submit btn text * fix(nc-gui): add shdow on input field in field modal * fix(nc-gui): disable submit btn if field modal has some warnings * test: update field add/edit save test case * test: update links column add/edit test cases * test: uncomment code * test: update user field default value update test cases * test: update select type option default value * test: update multi field editor test cases * test: update kanban view add option test cases * test: update multifield editor test cases * test: update create column keyboard shortcut test case * chore(nc-gui): lint * fix(nc-gui): field modal redio option shadow issue * fix(nc-gui): update field modal select option color picker btn border radius * fix(nc-gui): checkbox & rating icon alignment issue * fix(nc-gui): update field modal formula field * fix(nc-gui): field modal padding and gap issue * fix(nc-gui): update set default value font case & font color * fix(nc-gui): update field modal formula suggestion list ui * fix(nc-gui): removecolumn create field search list from multifield editor * fix(nc-gui): add placeholder for lookup & rollup options * fix: label * fix(nc-gui): remove placeholder from select type * fix(nc-gui): remove link type from link field select option * fix(nc-gui): qr, barcode value field icon issue * fix(nc-gui): set color picker tab according to active color * fix(nc-gui): json editor save btn ui changes in edit modal * fix(nc-gui): disable editing primary key col * chore(nc-gui): lint --------- Co-authored-by: Raju Udava <86527202+dstala@users.noreply.github.com>
455 lines
13 KiB
Vue
455 lines
13 KiB
Vue
<script lang="ts" setup>
|
|
import type { Editor } from '@tiptap/vue-3'
|
|
import MdiFormatBulletList from '~icons/mdi/format-list-bulleted'
|
|
import MdiFormatStrikeThrough from '~icons/mdi/format-strikethrough'
|
|
import MdiFormatListNumber from '~icons/mdi/format-list-numbered'
|
|
import MdiFormatListCheckbox from '~icons/mdi/format-list-checkbox'
|
|
import MsFormatH1 from '~icons/material-symbols/format-h1'
|
|
import MsFormatH2 from '~icons/material-symbols/format-h2'
|
|
import MsFormatH3 from '~icons/material-symbols/format-h3'
|
|
import TablerBlockQuote from '~icons/tabler/blockquote'
|
|
import MsCode from '~icons/material-symbols/code'
|
|
import MsFormatQuote from '~icons/material-symbols/format-quote'
|
|
import { RichTextBubbleMenuOptions } from '#imports'
|
|
|
|
interface Props {
|
|
editor: Editor
|
|
embedMode?: boolean
|
|
isFormField?: boolean
|
|
hiddenOptions?: RichTextBubbleMenuOptions[]
|
|
}
|
|
|
|
const props = withDefaults(defineProps<Props>(), {
|
|
embedMode: false,
|
|
isFormField: false,
|
|
hiddenOptions: () => [],
|
|
})
|
|
|
|
const { editor, embedMode, isFormField, hiddenOptions } = toRefs(props)
|
|
|
|
const isEditColumn = inject(EditColumnInj, ref(false))
|
|
|
|
const cmdOrCtrlKey = computed(() => {
|
|
return isMac() ? '⌘' : 'CTRL'
|
|
})
|
|
|
|
const shiftKey = computed(() => {
|
|
return isMac() ? '⇧' : 'Shift'
|
|
})
|
|
|
|
const altKey = computed(() => {
|
|
return isMac() ? '⌥' : 'Alt'
|
|
})
|
|
|
|
const tooltipPlacement = computed(() => {
|
|
if (isFormField.value) return 'bottom'
|
|
})
|
|
|
|
const tabIndex = computed(() => {
|
|
return isFormField.value ? -1 : 0
|
|
})
|
|
|
|
const onToggleLink = () => {
|
|
const activeNode = editor.value?.state?.selection?.$from?.nodeBefore || editor.value?.state?.selection?.$from?.nodeAfter
|
|
|
|
const isLinkMarkedStoredInEditor = editor.value?.state?.storedMarks?.some((mark: any) => mark.type.name === 'link')
|
|
|
|
const isActiveNodeMarkActive = activeNode?.marks?.some((mark: any) => mark.type.name === 'link') || isLinkMarkedStoredInEditor
|
|
|
|
if (isActiveNodeMarkActive) {
|
|
editor.value!.chain().focus().unsetLink().run()
|
|
} else {
|
|
if (editor.value.state.selection.empty) {
|
|
editor
|
|
.value!.chain()
|
|
.focus()
|
|
.insertContent(' ')
|
|
.setTextSelection({ from: editor.value!.state.selection.$from.pos, to: editor.value!.state.selection.$from.pos + 1 })
|
|
.toggleLink({
|
|
href: '',
|
|
})
|
|
.setTextSelection({ from: editor.value!.state.selection.$from.pos, to: editor.value!.state.selection.$from.pos + 1 })
|
|
.deleteSelection()
|
|
.run()
|
|
} else {
|
|
editor
|
|
.value!.chain()
|
|
.focus()
|
|
.setLink({
|
|
href: '',
|
|
})
|
|
.selectTextblockEnd()
|
|
.run()
|
|
}
|
|
|
|
setTimeout(() => {
|
|
const linkInput = document.querySelector('.nc-text-area-rich-link-option-input')
|
|
if (linkInput) {
|
|
;(linkInput as any).focus()
|
|
}
|
|
}, 100)
|
|
}
|
|
}
|
|
|
|
const isOptionVisible = (option: RichTextBubbleMenuOptions) => {
|
|
if (isFormField.value) return !hiddenOptions.value.includes(option)
|
|
|
|
return true
|
|
}
|
|
|
|
const showDivider = (options: RichTextBubbleMenuOptions[]) => {
|
|
return !isFormField.value || options.some((o) => !hiddenOptions.value.includes(o))
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div
|
|
class="bubble-menu flex-row gap-x-1 rounded-lg"
|
|
:class="{
|
|
'nc-form-field-bubble-menu inline-flex py-0': isFormField,
|
|
'flex bg-gray-100 px-1 py-1': !isFormField,
|
|
'embed-mode': embedMode,
|
|
'full-mode': !embedMode,
|
|
'edit-column-mode': isEditColumn,
|
|
}"
|
|
>
|
|
<NcTooltip :placement="tooltipPlacement" :disabled="editor.isActive('codeBlock')">
|
|
<template #title>
|
|
<div class="flex flex-col items-center">
|
|
<div>
|
|
{{ $t('labels.bold') }}
|
|
</div>
|
|
<div class="text-xs">{{ cmdOrCtrlKey }} B</div>
|
|
</div>
|
|
</template>
|
|
<NcButton
|
|
size="small"
|
|
type="text"
|
|
:class="{ 'is-active': editor.isActive('bold') }"
|
|
:disabled="editor.isActive('codeBlock')"
|
|
:tabindex="tabIndex"
|
|
@click="editor!.chain().focus().toggleBold().run()"
|
|
>
|
|
<MdiFormatBold />
|
|
</NcButton>
|
|
</NcTooltip>
|
|
|
|
<NcTooltip :placement="tooltipPlacement" :disabled="editor.isActive('codeBlock')">
|
|
<template #title>
|
|
<div class="flex flex-col items-center">
|
|
<div>
|
|
{{ $t('labels.italic') }}
|
|
</div>
|
|
<div>{{ cmdOrCtrlKey }} I</div>
|
|
</div>
|
|
</template>
|
|
<NcButton
|
|
size="small"
|
|
type="text"
|
|
:disabled="editor.isActive('codeBlock')"
|
|
:class="{ 'is-active': editor.isActive('italic') }"
|
|
:tabindex="tabIndex"
|
|
@click=";(editor!.chain().focus() as any).toggleItalic().run()"
|
|
>
|
|
<MdiFormatItalic />
|
|
</NcButton>
|
|
</NcTooltip>
|
|
<NcTooltip :placement="tooltipPlacement" :disabled="editor.isActive('codeBlock')">
|
|
<template #title>
|
|
<div class="flex flex-col items-center">
|
|
<div>
|
|
{{ $t('labels.underline') }}
|
|
</div>
|
|
<div>{{ cmdOrCtrlKey }} U</div>
|
|
</div>
|
|
</template>
|
|
|
|
<NcButton
|
|
size="small"
|
|
type="text"
|
|
:class="{ 'is-active': editor.isActive('underline') }"
|
|
:disabled="editor.isActive('codeBlock')"
|
|
:tabindex="tabIndex"
|
|
@click="editor!.chain().focus().toggleUnderline().run()"
|
|
>
|
|
<MdiFormatUnderline />
|
|
</NcButton>
|
|
</NcTooltip>
|
|
<NcTooltip v-if="embedMode && !isEditColumn" :placement="tooltipPlacement" :disabled="editor.isActive('codeBlock')">
|
|
<template #title>
|
|
<div class="flex flex-col items-center">
|
|
<div>
|
|
{{ $t('labels.strike') }}
|
|
</div>
|
|
<div>{{ shiftKey }} {{ cmdOrCtrlKey }} S</div>
|
|
</div>
|
|
</template>
|
|
<NcButton
|
|
size="small"
|
|
type="text"
|
|
:class="{ 'is-active': editor.isActive('strike') }"
|
|
:disabled="editor.isActive('codeBlock')"
|
|
:tabindex="tabIndex"
|
|
@click="editor!.chain().focus().toggleStrike().run()"
|
|
>
|
|
<MdiFormatStrikeThrough />
|
|
</NcButton>
|
|
</NcTooltip>
|
|
|
|
<NcTooltip v-if="embedMode && isOptionVisible(RichTextBubbleMenuOptions.code)" :placement="tooltipPlacement">
|
|
<template #title> {{ $t('general.code') }}</template>
|
|
<NcButton
|
|
size="small"
|
|
type="text"
|
|
:tabindex="tabIndex"
|
|
:class="{ 'is-active': editor.isActive('codeBlock') }"
|
|
@click="editor!.chain().focus().toggleCodeBlock().run()"
|
|
>
|
|
<MsCode />
|
|
</NcButton>
|
|
</NcTooltip>
|
|
<NcTooltip
|
|
v-if="isFormField ? isOptionVisible(RichTextBubbleMenuOptions.quote) : !embedMode"
|
|
:placement="tooltipPlacement"
|
|
:disabled="editor.isActive('codeBlock')"
|
|
>
|
|
<template #title> {{ $t('general.quote') }}</template>
|
|
<NcButton
|
|
size="small"
|
|
type="text"
|
|
:tabindex="tabIndex"
|
|
:class="{ 'is-active': editor.isActive('code') }"
|
|
:disabled="editor.isActive('codeBlock')"
|
|
@click="editor!.chain().focus().toggleCode().run()"
|
|
>
|
|
<MsFormatQuote />
|
|
</NcButton>
|
|
</NcTooltip>
|
|
<div class="divider"></div>
|
|
|
|
<template v-if="embedMode && !isFormField">
|
|
<NcTooltip>
|
|
<template #title>
|
|
<div class="flex flex-col items-center">
|
|
<div>
|
|
{{ $t('labels.heading1') }}
|
|
</div>
|
|
<div>{{ cmdOrCtrlKey }} {{ altKey }} 1</div>
|
|
</div>
|
|
</template>
|
|
<NcButton
|
|
size="small"
|
|
type="text"
|
|
:class="{ 'is-active': editor.isActive('heading', { level: 1 }) }"
|
|
@click="editor!.chain().focus().toggleHeading({ level: 1 }).run()"
|
|
>
|
|
<MsFormatH1 />
|
|
</NcButton>
|
|
</NcTooltip>
|
|
<NcTooltip>
|
|
<template #title>
|
|
<div class="flex flex-col items-center">
|
|
<div>
|
|
{{ $t('labels.heading2') }}
|
|
</div>
|
|
<div>{{ cmdOrCtrlKey }} {{ altKey }} 2</div>
|
|
</div>
|
|
</template>
|
|
<NcButton
|
|
size="small"
|
|
type="text"
|
|
:class="{ 'is-active': editor.isActive('heading', { level: 2 }) }"
|
|
@click="editor!.chain().focus().toggleHeading({ level: 2 }).run()"
|
|
>
|
|
<MsFormatH2 />
|
|
</NcButton>
|
|
</NcTooltip>
|
|
<NcTooltip>
|
|
<template #title>
|
|
<div class="flex flex-col items-center">
|
|
<div>
|
|
{{ $t('labels.heading3') }}
|
|
</div>
|
|
<div>{{ cmdOrCtrlKey }} {{ altKey }} 3</div>
|
|
</div>
|
|
</template>
|
|
<NcButton
|
|
size="small"
|
|
type="text"
|
|
:class="{ 'is-active': editor.isActive('heading', { level: 3 }) }"
|
|
@click="editor!.chain().focus().toggleHeading({ level: 3 }).run()"
|
|
>
|
|
<MsFormatH3 />
|
|
</NcButton>
|
|
</NcTooltip>
|
|
|
|
<div class="divider"></div>
|
|
</template>
|
|
|
|
<NcTooltip
|
|
v-if="embedMode && !isEditColumn && isOptionVisible(RichTextBubbleMenuOptions.blockQuote)"
|
|
:placement="tooltipPlacement"
|
|
>
|
|
<template #title> {{ $t('labels.blockQuote') }}</template>
|
|
<NcButton
|
|
size="small"
|
|
type="text"
|
|
:tabindex="tabIndex"
|
|
:class="{ 'is-active': editor.isActive('blockquote') }"
|
|
@click="editor!.chain().focus().toggleBlockquote().run()"
|
|
>
|
|
<TablerBlockQuote class="-mt-0.25" />
|
|
</NcButton>
|
|
</NcTooltip>
|
|
|
|
<NcTooltip v-if="isOptionVisible(RichTextBubbleMenuOptions.bulletList)" :placement="tooltipPlacement">
|
|
<template #title> {{ $t('labels.bulletList') }}</template>
|
|
<NcButton
|
|
size="small"
|
|
type="text"
|
|
:tabindex="tabIndex"
|
|
:class="{ 'is-active': editor.isActive('bulletList') }"
|
|
@click="editor!.chain().focus().toggleBulletList().run()"
|
|
>
|
|
<MdiFormatBulletList />
|
|
</NcButton>
|
|
</NcTooltip>
|
|
|
|
<NcTooltip v-if="isOptionVisible(RichTextBubbleMenuOptions.numberedList)" :placement="tooltipPlacement">
|
|
<template #title> {{ $t('labels.numberedList') }}</template>
|
|
<NcButton
|
|
size="small"
|
|
type="text"
|
|
:tabindex="tabIndex"
|
|
:class="{ 'is-active': editor.isActive('orderedList') }"
|
|
@click="editor!.chain().focus().toggleOrderedList().run()"
|
|
>
|
|
<MdiFormatListNumber />
|
|
</NcButton>
|
|
</NcTooltip>
|
|
|
|
<NcTooltip v-if="isOptionVisible(RichTextBubbleMenuOptions.taskList)" :placement="tooltipPlacement">
|
|
<template #title> {{ $t('labels.taskList') }}</template>
|
|
<NcButton
|
|
size="small"
|
|
type="text"
|
|
:tabindex="tabIndex"
|
|
:class="{ 'is-active': editor.isActive('taskList') }"
|
|
@click="editor!.chain().focus().toggleTaskList().run()"
|
|
>
|
|
<MdiFormatListCheckbox />
|
|
</NcButton>
|
|
</NcTooltip>
|
|
|
|
<div
|
|
v-if="
|
|
showDivider([
|
|
RichTextBubbleMenuOptions.blockQuote,
|
|
RichTextBubbleMenuOptions.bulletList,
|
|
RichTextBubbleMenuOptions.numberedList,
|
|
RichTextBubbleMenuOptions.taskList,
|
|
])
|
|
"
|
|
class="divider"
|
|
></div>
|
|
|
|
<NcTooltip
|
|
v-if="isOptionVisible(RichTextBubbleMenuOptions.link)"
|
|
:placement="tooltipPlacement"
|
|
:disabled="editor.isActive('codeBlock')"
|
|
>
|
|
<template #title> {{ $t('general.link') }}</template>
|
|
<NcButton
|
|
size="small"
|
|
type="text"
|
|
:class="{ 'is-active': editor.isActive('link') }"
|
|
:disabled="editor.isActive('codeBlock')"
|
|
:tabindex="tabIndex"
|
|
@click="onToggleLink"
|
|
>
|
|
<GeneralIcon v-if="isFormField" icon="link2"></GeneralIcon>
|
|
<div v-else class="flex flex-row items-center px-0.5">
|
|
<MdiLink />
|
|
<div class="!text-xs !ml-1">{{ $t('general.link') }}</div>
|
|
</div>
|
|
</NcButton>
|
|
</NcTooltip>
|
|
</div>
|
|
</template>
|
|
|
|
<style lang="scss">
|
|
.bubble-menu-hidden {
|
|
[data-tippy-root] {
|
|
opacity: 0;
|
|
height: 0;
|
|
overflow: hidden;
|
|
z-index: -1;
|
|
user-select: none;
|
|
}
|
|
}
|
|
|
|
.bubble-text-format-button-icon {
|
|
@apply px-1.5 py-0 border-1 border-gray-300 rounded-sm items-center justify-center;
|
|
font-size: 0.8rem;
|
|
font-weight: 600;
|
|
}
|
|
.bubble-text-format-button {
|
|
@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:not(.nc-form-field-bubble-menu) {
|
|
@apply border-transparent !shadow-none;
|
|
}
|
|
.bubble-menu.form-field-mode {
|
|
@apply bg-transparent px-0;
|
|
}
|
|
|
|
.embed-mode.bubble-menu:not(.nc-form-field-bubble-menu) {
|
|
@apply !py-0 !my-0 !border-0;
|
|
|
|
.divider {
|
|
@apply my-0 !h-11 border-gray-100;
|
|
}
|
|
|
|
.nc-button {
|
|
@apply !mt-1.65;
|
|
}
|
|
}
|
|
|
|
.bubble-menu {
|
|
// shadow
|
|
@apply bg-white;
|
|
border-width: 1px;
|
|
|
|
&.nc-form-field-bubble-menu {
|
|
.divider {
|
|
@apply border-r-1 border-gray-200 my-0;
|
|
}
|
|
}
|
|
|
|
.nc-button.is-active {
|
|
@apply !hover:outline-gray-200 bg-gray-100 text-brand-500;
|
|
outline: 1px;
|
|
}
|
|
&:not(.nc-form-field-bubble-menu) {
|
|
.divider {
|
|
@apply border-r-1 border-gray-200 !h-6 !mx-0.5 my-1;
|
|
}
|
|
}
|
|
.ant-select-selector {
|
|
@apply !rounded-md;
|
|
}
|
|
.ant-select-selector .ant-select-selection-item {
|
|
@apply !text-xs;
|
|
}
|
|
.ant-btn-loading-icon {
|
|
@apply pb-0.5;
|
|
}
|
|
}
|
|
</style>
|