microeditor: hide on blur
This commit is contained in:
parent
7e6ef23bd2
commit
d485314b11
|
@ -1,8 +1,8 @@
|
||||||
import BubbleMenu from '@tiptap/extension-bubble-menu'
|
import BubbleMenu from '@tiptap/extension-bubble-menu'
|
||||||
import Placeholder from '@tiptap/extension-placeholder'
|
import Placeholder from '@tiptap/extension-placeholder'
|
||||||
import clsx from 'clsx'
|
import clsx from 'clsx'
|
||||||
import { type JSX, createEffect, createSignal, on } from 'solid-js'
|
import { type JSX, createEffect, createSignal, on, onCleanup, onMount } from 'solid-js'
|
||||||
import { createTiptapEditor, useEditorHTML } from 'solid-tiptap'
|
import { createEditorTransaction, createTiptapEditor, useEditorHTML } from 'solid-tiptap'
|
||||||
import { minimal } from '~/lib/editorExtensions'
|
import { minimal } from '~/lib/editorExtensions'
|
||||||
import { MicroBubbleMenu } from './Toolbar/MicroBubbleMenu'
|
import { MicroBubbleMenu } from './Toolbar/MicroBubbleMenu'
|
||||||
|
|
||||||
|
@ -12,13 +12,18 @@ interface MicroEditorProps {
|
||||||
content?: string
|
content?: string
|
||||||
onChange?: (content: string) => void
|
onChange?: (content: string) => void
|
||||||
onSubmit?: (content: string) => void
|
onSubmit?: (content: string) => void
|
||||||
|
onBlur?: () => void
|
||||||
placeholder?: string
|
placeholder?: string
|
||||||
bordered?: boolean
|
bordered?: boolean
|
||||||
|
shownAsLead?: boolean
|
||||||
|
focusOnMount?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MicroEditor = (props: MicroEditorProps): JSX.Element => {
|
export const MicroEditor = (props: MicroEditorProps): JSX.Element => {
|
||||||
const [editorElement, setEditorElement] = createSignal<HTMLDivElement>()
|
const [editorElement, setEditorElement] = createSignal<HTMLDivElement>()
|
||||||
const [bubbleMenuElement, setBubbleMenuElement] = createSignal<HTMLDivElement>()
|
const [bubbleMenuElement, setBubbleMenuElement] = createSignal<HTMLDivElement>()
|
||||||
|
const [isBlurred, setIsBlurred] = createSignal(false)
|
||||||
|
let blurTimer: number | undefined
|
||||||
|
|
||||||
const editor = createTiptapEditor(() => ({
|
const editor = createTiptapEditor(() => ({
|
||||||
element: editorElement()!,
|
element: editorElement()!,
|
||||||
|
@ -40,9 +45,54 @@ export const MicroEditor = (props: MicroEditorProps): JSX.Element => {
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const html = useEditorHTML(editor)
|
const html = useEditorHTML(editor)
|
||||||
|
|
||||||
createEffect(on(html, (c?: string) => c && props.onChange?.(c)))
|
createEffect(on(html, (c?: string) => c && props.onChange?.(c)))
|
||||||
|
|
||||||
|
const lostFocusEmpty = createEditorTransaction(
|
||||||
|
editor,
|
||||||
|
(e) => e && !e.isFocused && e?.view.state.doc.textContent.trim() === ''
|
||||||
|
)
|
||||||
|
createEffect(
|
||||||
|
on(
|
||||||
|
isBlurred,
|
||||||
|
(lost?: boolean) => {
|
||||||
|
if (lost && props.shownAsLead && props.onBlur) {
|
||||||
|
setTimeout(props.onBlur, 1000)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ defer: true }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
const handleBlur = () => {
|
||||||
|
blurTimer = window.setTimeout(() => {
|
||||||
|
const isEmpty = editor()?.view.state.doc.textContent.trim() === ''
|
||||||
|
if (isEmpty && props.shownAsLead && props.onBlur) {
|
||||||
|
props.onBlur()
|
||||||
|
}
|
||||||
|
setIsBlurred(true)
|
||||||
|
}, 100) // небольшая задержка для обработки кликов внутри редактора
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleFocus = () => {
|
||||||
|
clearTimeout(blurTimer)
|
||||||
|
setIsBlurred(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
|
const editorInstance = editor()
|
||||||
|
if (editorInstance) {
|
||||||
|
editorInstance.on('blur', handleBlur)
|
||||||
|
editorInstance.on('focus', handleFocus)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
onCleanup(() => {
|
||||||
|
clearTimeout(blurTimer)
|
||||||
|
editor()?.off('blur', handleBlur)
|
||||||
|
editor()?.off('focus', handleFocus)
|
||||||
|
})
|
||||||
|
|
||||||
|
onMount(() => props.focusOnMount && editor()?.commands.focus())
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
class={clsx(styles.MiniEditor, {
|
class={clsx(styles.MiniEditor, {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { clsx } from 'clsx'
|
import { clsx } from 'clsx'
|
||||||
import deepEqual from 'fast-deep-equal'
|
import deepEqual from 'fast-deep-equal'
|
||||||
import { Show, createEffect, createSignal, lazy, on, onCleanup, onMount } from 'solid-js'
|
import { Show, createEffect, createSignal, on, onCleanup, onMount } from 'solid-js'
|
||||||
import { createStore } from 'solid-js/store'
|
import { createStore } from 'solid-js/store'
|
||||||
import { debounce } from 'throttle-debounce'
|
import { debounce } from 'throttle-debounce'
|
||||||
import { EditorComponent } from '~/components/Editor/Editor'
|
import { EditorComponent } from '~/components/Editor/Editor'
|
||||||
|
@ -22,16 +22,15 @@ import { LayoutType } from '~/types/common'
|
||||||
import { MediaItem } from '~/types/mediaitem'
|
import { MediaItem } from '~/types/mediaitem'
|
||||||
import { clone } from '~/utils/clone'
|
import { clone } from '~/utils/clone'
|
||||||
import { AutoSaveNotice } from '../Editor/AutoSaveNotice'
|
import { AutoSaveNotice } from '../Editor/AutoSaveNotice'
|
||||||
|
import { Panel } from '../Editor/Panel/Panel'
|
||||||
import { AudioUploader } from '../Upload/AudioUploader'
|
import { AudioUploader } from '../Upload/AudioUploader'
|
||||||
import { VideoUploader } from '../Upload/VideoUploader'
|
import { VideoUploader } from '../Upload/VideoUploader'
|
||||||
import { Modal } from '../_shared/Modal'
|
import { Modal } from '../_shared/Modal'
|
||||||
import { TableOfContents } from '../_shared/TableOfContents'
|
import { TableOfContents } from '../_shared/TableOfContents'
|
||||||
|
|
||||||
import styles from '~/styles/views/EditView.module.scss'
|
import styles from '~/styles/views/EditView.module.scss'
|
||||||
import { Panel } from '../Editor/Panel/Panel'
|
import MicroEditor from '../Editor/MicroEditor'
|
||||||
|
import GrowingTextarea from '../_shared/GrowingTextarea/GrowingTextarea'
|
||||||
const MicroEditor = lazy(() => import('../Editor/MicroEditor'))
|
|
||||||
const GrowingTextarea = lazy(() => import('~/components/_shared/GrowingTextarea/GrowingTextarea'))
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
shout: Shout
|
shout: Shout
|
||||||
|
@ -63,8 +62,7 @@ export const EditView = (props: Props) => {
|
||||||
setFormErrors,
|
setFormErrors,
|
||||||
saveDraft,
|
saveDraft,
|
||||||
saveDraftToLocalStorage,
|
saveDraftToLocalStorage,
|
||||||
getDraftFromLocalStorage,
|
getDraftFromLocalStorage
|
||||||
isCollabMode
|
|
||||||
} = useEditorContext()
|
} = useEditorContext()
|
||||||
|
|
||||||
const [subtitleInput, setSubtitleInput] = createSignal<HTMLTextAreaElement | undefined>()
|
const [subtitleInput, setSubtitleInput] = createSignal<HTMLTextAreaElement | undefined>()
|
||||||
|
@ -266,6 +264,10 @@ export const EditView = (props: Props) => {
|
||||||
setIsLeadVisible(true)
|
setIsLeadVisible(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hideLeadInput = () => {
|
||||||
|
setIsLeadVisible(false)
|
||||||
|
}
|
||||||
|
|
||||||
const HeadingActions = () => {
|
const HeadingActions = () => {
|
||||||
return (
|
return (
|
||||||
<div class="col-md-19 col-lg-18 col-xl-16 offset-md-5">
|
<div class="col-md-19 col-lg-18 col-xl-16 offset-md-5">
|
||||||
|
@ -340,8 +342,10 @@ export const EditView = (props: Props) => {
|
||||||
</Show>
|
</Show>
|
||||||
<Show when={isLeadVisible()}>
|
<Show when={isLeadVisible()}>
|
||||||
<MicroEditor
|
<MicroEditor
|
||||||
|
shownAsLead={isLeadVisible()}
|
||||||
placeholder={t('A short introduction to keep the reader interested')}
|
placeholder={t('A short introduction to keep the reader interested')}
|
||||||
content={form.lead}
|
content={form.lead}
|
||||||
|
onBlur={hideLeadInput}
|
||||||
onChange={(value: string) => handleInputChange('lead', value)}
|
onChange={(value: string) => handleInputChange('lead', value)}
|
||||||
/>
|
/>
|
||||||
</Show>
|
</Show>
|
||||||
|
@ -455,7 +459,6 @@ export const EditView = (props: Props) => {
|
||||||
shoutId={form.shoutId}
|
shoutId={form.shoutId}
|
||||||
initialContent={form.body}
|
initialContent={form.body}
|
||||||
onChange={(body: string) => handleInputChange('body', body)}
|
onChange={(body: string) => handleInputChange('body', body)}
|
||||||
disableCollaboration={!isCollabMode()}
|
|
||||||
/>
|
/>
|
||||||
<Show when={draft()?.id}>
|
<Show when={draft()?.id}>
|
||||||
<Panel shoutId={draft()?.id} />
|
<Panel shoutId={draft()?.id} />
|
||||||
|
|
Loading…
Reference in New Issue
Block a user