Skip to content

Commit

Permalink
Merge pull request #8024 from nocodb/develop
Browse files Browse the repository at this point in the history
  • Loading branch information
github-actions[bot] committed Apr 2, 2024
2 parents e948894 + 7194216 commit 99294e4
Show file tree
Hide file tree
Showing 656 changed files with 3,588 additions and 5,182 deletions.
8 changes: 8 additions & 0 deletions packages/nc-gui/components/account/UsersModal.vue
Expand Up @@ -16,6 +16,7 @@ import {
useI18n,
useNuxtApp,
} from '#imports'
import { extractEmail } from '~/helpers/parsers/parserHelpers'
interface Props {
show: boolean
Expand Down Expand Up @@ -99,6 +100,12 @@ const clickInviteMore = () => {
}
const emailInput: VNodeRef = (el) => (el as HTMLInputElement)?.focus()
const onPaste = (e: ClipboardEvent) => {
const pastedText = e.clipboardData?.getData('text') ?? ''
usersData.value.emails = extractEmail(pastedText) || pastedText
}
</script>

<template>
Expand Down Expand Up @@ -189,6 +196,7 @@ const emailInput: VNodeRef = (el) => (el as HTMLInputElement)?.focus()
size="middle"
validate-trigger="onBlur"
:placeholder="$t('labels.email')"
@paste.prevent="onPaste"
/>
</a-form-item>
</div>
Expand Down
3 changes: 2 additions & 1 deletion packages/nc-gui/components/cell/Currency.vue
Expand Up @@ -112,13 +112,14 @@ onMounted(() => {
</span>
</div>
<input
v-if="!readOnly && editEnabled"
v-if="(!readOnly && editEnabled) || (isForm && !isEditColumn)"
:ref="focus"
v-model="vModel"
type="number"
class="nc-cell-field h-full text-sm border-none rounded-md py-1 outline-none focus:outline-none focus:ring-0"
:class="isForm && !isEditColumn ? 'flex flex-1' : 'w-full'"
:placeholder="isEditColumn ? $t('labels.optional') : ''"
:disabled="readOnly"
@blur="onBlur"
@keydown.enter="onKeydownEnter"
@keydown.down.stop
Expand Down
12 changes: 12 additions & 0 deletions packages/nc-gui/components/cell/Email.vue
Expand Up @@ -11,6 +11,7 @@ import {
useI18n,
validateEmail,
} from '#imports'
import { extractEmail } from '~/helpers/parsers/parserHelpers'
interface Props {
modelValue: string | null | undefined
Expand Down Expand Up @@ -56,6 +57,16 @@ const validEmail = computed(() => vModel.value && validateEmail(vModel.value))
const focus: VNodeRef = (el) =>
!isExpandedFormOpen.value && !isEditColumn.value && !isForm.value && (el as HTMLInputElement)?.focus()
const onPaste = (e: ClipboardEvent) => {
const pastedText = e.clipboardData?.getData('text') ?? ''
if (parseProp(column.value.meta).validate) {
vModel.value = extractEmail(pastedText) || pastedText
} else {
vModel.value = pastedText
}
}
watch(
() => editEnabled.value,
() => {
Expand Down Expand Up @@ -90,6 +101,7 @@ watch(
@keydown.delete.stop
@selectstart.capture.stop
@mousedown.stop
@paste.prevent="onPaste"
/>

<span v-else-if="vModel === null && showNull" class="nc-cell-field nc-null uppercase">{{ $t('general.null') }}</span>
Expand Down
5 changes: 5 additions & 0 deletions packages/nc-gui/components/cell/MultiSelect.vue
Expand Up @@ -10,6 +10,7 @@ import {
EditColumnInj,
EditModeInj,
IsKanbanInj,
IsSurveyFormInj,
ReadonlyInj,
RowHeightInj,
computed,
Expand Down Expand Up @@ -64,6 +65,8 @@ const isEditColumn = inject(EditColumnInj, ref(false))
const rowHeight = inject(RowHeightInj, ref(undefined))
const isSurveyForm = inject(IsSurveyFormInj, ref(false))
const selectedIds = ref<string[]>([])
const aselect = ref<typeof AntSelect>()
Expand Down Expand Up @@ -386,6 +389,8 @@ const onFocus = () => {
isFocusing.value = false
}, 250)
if (isSurveyForm.value && vModel.value?.length) return
isOpen.value = true
}
</script>
Expand Down
9 changes: 8 additions & 1 deletion packages/nc-gui/components/cell/Rating.vue
Expand Up @@ -73,11 +73,12 @@ watch(rateDomRef, () => {
<template>
<a-rate
ref="rateDomRef"
:key="ratingMeta.icon.full"
v-model:value="vModel"
:disabled="readOnly"
:count="ratingMeta.max"
:class="readOnly ? 'pointer-events-none' : ''"
:style="`color: ${ratingMeta.color}; padding: ${isExpandedFormOpen ? '0px 8px' : '0px 5px'};`"
:style="`color: ${ratingMeta.color}; padding: ${isExpandedFormOpen ? '0px 8px' : '0px 2px'};`"
@keydown="onKeyPress"
>
<template #character>
Expand All @@ -89,3 +90,9 @@ watch(rateDomRef, () => {
</template>
</a-rate>
</template>

<style scoped lang="scss">
:deep(li:not(:last-child)) {
@apply mr-[1.5px];
}
</style>
107 changes: 76 additions & 31 deletions packages/nc-gui/components/cell/RichText.vue
Expand Up @@ -9,21 +9,31 @@ import Underline from '@tiptap/extension-underline'
import Placeholder from '@tiptap/extension-placeholder'
import { TaskItem } from '@/helpers/dbTiptapExtensions/task-item'
import { Link } from '@/helpers/dbTiptapExtensions/links'
import { IsExpandedFormOpenInj, IsFormInj, IsGridInj, ReadonlyInj, RowHeightInj } from '#imports'
const props = defineProps<{
value?: string | null
readOnly?: boolean
syncValueChange?: boolean
showMenu?: boolean
fullMode?: boolean
isFormField?: boolean
autofocus?: boolean
placeholder?: string
renderAsText?: boolean
}>()
const emits = defineEmits(['update:value'])
import type { RichTextBubbleMenuOptions } from '#imports'
import { IsExpandedFormOpenInj, IsFormInj, IsGridInj, IsSurveyFormInj, ReadonlyInj, RowHeightInj } from '#imports'
const props = withDefaults(
defineProps<{
value?: string | null
readOnly?: boolean
syncValueChange?: boolean
showMenu?: boolean
fullMode?: boolean
isFormField?: boolean
autofocus?: boolean
placeholder?: string
renderAsText?: boolean
hiddenBubbleMenuOptions?: RichTextBubbleMenuOptions[]
}>(),
{
isFormField: false,
hiddenBubbleMenuOptions: () => [],
},
)
const emits = defineEmits(['update:value', 'focus', 'blur'])
const { isFormField, hiddenBubbleMenuOptions } = toRefs(props)
const isExpandedFormOpen = inject(IsExpandedFormOpenInj, ref(false))!
Expand All @@ -35,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', {
Expand Down Expand Up @@ -115,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({
Expand All @@ -136,16 +150,18 @@ const editor = useEditor({
.turndown(editor.getHTML().replaceAll(/<p><\/p>/g, '<br />'))
.replaceAll(/\n\n<br \/>\n\n/g, '<br>\n\n')
vModel.value = props.isFormField && markdown === '<br />' ? '' : markdown
vModel.value = isFormField.value && markdown === '<br />' ? '' : 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')
}
},
})
Expand Down Expand Up @@ -176,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)
Expand All @@ -197,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()
Expand All @@ -208,8 +230,10 @@ useEventListener(
editorDom,
'focusout',
(e: FocusEvent) => {
if (!(e?.relatedTarget as HTMLElement)?.closest('.bubble-menu, .nc-textarea-rich-editor')) {
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,
Expand All @@ -218,54 +242,67 @@ useEventListener(

<template>
<div
class="h-full focus:outline-none"
class="nc-rich-text h-full focus:outline-none"
:class="{
'flex flex-col flex-grow nc-rich-text-full': fullMode,
'nc-rich-text-embed flex flex-col pl-1 w-full': !fullMode,
'readonly': readOnly,
'nc-form-rich-text-field !p-0': isFormField,
'nc-form-rich-text-field !p-0 relative': isFormField,
'nc-rich-text-grid': isGrid,
}"
:tabindex="readOnlyCell || isFormField ? -1 : 0"
@focus="onFocusWrapper"
>
<div v-if="renderAsText" class="truncate">
<span v-if="editor"> {{ editor?.getText() ?? '' }}</span>
</div>
<template v-else>
<div
v-if="showMenu && !readOnly && !isFormField"
class="absolute top-0 right-0.5 xs:hidden"
class="absolute top-0 right-0.5"
:class="{
'max-w-[calc(100%_-_198px)] flex justify-end rounded-tr-2xl overflow-hidden': fullMode,
'flex rounded-tr-2xl overflow-hidden w-full': fullMode || isForm,
'max-w-[calc(100%_-_198px)]': fullMode,
'justify-start left-0.5': isForm,
'justify-end xs:hidden': !isForm,
}"
>
<div class="nc-longtext-scrollbar">
<div class="scrollbar-thin scrollbar-thumb-gray-200 hover:scrollbar-thumb-gray-300 scrollbar-track-transparent">
<CellRichTextSelectedBubbleMenu v-if="editor" :editor="editor" embed-mode :is-form-field="isFormField" />
</div>
</div>
<CellRichTextSelectedBubbleMenuPopup v-if="editor && !isFormField" :editor="editor" />
<CellRichTextSelectedBubbleMenuPopup v-if="editor && !isFormField && !isForm" :editor="editor" />

<CellRichTextLinkOptions v-if="editor" :editor="editor" />

<EditorContent
ref="editorDom"
:editor="editor"
class="flex flex-col nc-textarea-rich-editor w-full"
:class="{
'mt-2.5 flex-grow': fullMode,
'nc-scrollbar-md': !fullMode || (!fullMode && isExpandedFormOpen),
'scrollbar-thin scrollbar-thumb-gray-200 scrollbar-track-transparent': !fullMode || (!fullMode && isExpandedFormOpen),
'flex-grow': isExpandedFormOpen,
[`!overflow-hidden children:line-clamp-${rowHeight}`]:
!fullMode && readOnly && rowHeight && !isExpandedFormOpen && !isForm,
}"
@keydown.alt.enter.stop
@keydown.shift.enter.stop
/>
<div v-if="isFormField && !readOnly">
<div v-if="isFormField && !readOnly" class="nc-form-field-bubble-menu-wrapper overflow-hidden">
<div
class="overflow-hidden"
:class="isFocused ? 'max-h-[50px]' : 'max-h-0'"
:style="{
transition: 'max-height 0.2s ease-in-out',
}"
>
<CellRichTextSelectedBubbleMenu v-if="editor" :editor="editor" embed-mode is-form-field />
<CellRichTextSelectedBubbleMenu
v-if="editor"
:editor="editor"
embed-mode
is-form-field
:hidden-options="hiddenBubbleMenuOptions"
/>
</div>
</div>
</template>
Expand Down Expand Up @@ -343,7 +380,7 @@ useEventListener(

.nc-textarea-rich-editor {
.tiptap p.is-editor-empty:first-child::before {
color: #6a7184;
color: #9aa2af;
content: attr(data-placeholder);
float: left;
height: 0;
Expand Down Expand Up @@ -429,18 +466,21 @@ useEventListener(
font-weight: 700;
font-size: 1.85rem;
margin-bottom: 0.1rem;
line-height: 36px;
}

h2 {
font-weight: 600;
font-size: 1.55rem;
margin-bottom: 0.1em;
line-height: 30px;
}

h3 {
font-weight: 600;
font-size: 1.15rem;
margin-bottom: 0.1em;
line-height: 24px;
}

blockquote {
Expand All @@ -462,4 +502,9 @@ useEventListener(
height: fit-content;
}
}
.nc-form-field-bubble-menu-wrapper {
@apply absolute -bottom-9 left-1/2 z-50 rounded-lg;
transform: translateX(-50%);
box-shadow: 0px 8px 8px -4px rgba(0, 0, 0, 0.04), 0px 20px 24px -4px rgba(0, 0, 0, 0.1);
}
</style>

0 comments on commit 99294e4

Please sign in to comment.