MiniEditor-fix
This commit is contained in:
parent
962140e755
commit
7aa01d6152
|
@ -8,7 +8,6 @@ import { InlineForm } from '../InlineForm'
|
||||||
type Props = {
|
type Props = {
|
||||||
editor: Editor
|
editor: Editor
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
onFocus: (event: FocusEvent) => void
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const checkUrl = (url: string) => {
|
export const checkUrl = (url: string) => {
|
||||||
|
@ -62,7 +61,6 @@ export const InsertLinkForm = (props: Props) => {
|
||||||
validate={(value) => (validateUrl(value) ? '' : t('Invalid url format'))}
|
validate={(value) => (validateUrl(value) ? '' : t('Invalid url format'))}
|
||||||
onSubmit={handleLinkFormSubmit}
|
onSubmit={handleLinkFormSubmit}
|
||||||
onClose={props.onClose}
|
onClose={props.onClose}
|
||||||
onFocus={props.onFocus}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
@ -63,15 +63,6 @@ export const MicroEditor = (props: MicroEditorProps): JSX.Element => {
|
||||||
const [showLinkInput, setShowLinkInput] = createSignal(false)
|
const [showLinkInput, setShowLinkInput] = createSignal(false)
|
||||||
const [showSimpleMenu, setShowSimpleMenu] = createSignal(false)
|
const [showSimpleMenu, setShowSimpleMenu] = createSignal(false)
|
||||||
const [toolbarElement, setToolbarElement] = createSignal<HTMLElement>()
|
const [toolbarElement, setToolbarElement] = createSignal<HTMLElement>()
|
||||||
const [selectionRange, setSelectionRange] = createSignal<Range | null>(null)
|
|
||||||
|
|
||||||
const handleLinkInputFocus = (event: FocusEvent) => {
|
|
||||||
event.preventDefault()
|
|
||||||
const selection = window.getSelection()
|
|
||||||
if (selection?.rangeCount) {
|
|
||||||
setSelectionRange(selection.getRangeAt(0))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const editor = createTiptapEditor(() => ({
|
const editor = createTiptapEditor(() => ({
|
||||||
element: editorElement()!,
|
element: editorElement()!,
|
||||||
|
@ -87,12 +78,32 @@ export const MicroEditor = (props: MicroEditorProps): JSX.Element => {
|
||||||
content: props.content || ''
|
content: props.content || ''
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
const selection = createEditorTransaction(editor, (instance) => instance?.state.selection)
|
||||||
|
const [storedSelection, setStoredSelection] = createSignal<Editor['state']['selection']>()
|
||||||
|
const recoverSelection = () => {
|
||||||
|
if (!storedSelection()?.empty) {
|
||||||
|
// TODO set selection range from stored
|
||||||
|
createEditorTransaction(editor, (instance?: Editor) => {
|
||||||
|
const r = selection()
|
||||||
|
if (instance && r) {
|
||||||
|
instance.state.selection.from === r.from
|
||||||
|
instance.state.selection.to === r.to
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const storeSelection = (event: Event) => {
|
||||||
|
event.preventDefault()
|
||||||
|
const selection = editor()?.state.selection
|
||||||
|
if (!selection?.empty) {
|
||||||
|
setStoredSelection(selection)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const isEmpty = useEditorIsEmpty(editor)
|
const isEmpty = useEditorIsEmpty(editor)
|
||||||
const isFocused = useEditorIsFocused(editor)
|
const isFocused = useEditorIsFocused(editor)
|
||||||
const isTextSelection = createEditorTransaction(editor, (instance) => !instance?.state.selection.empty)
|
|
||||||
const html = useEditorHTML(editor)
|
const html = useEditorHTML(editor)
|
||||||
|
createEffect(on([selection, showLinkInput], ([s, l]) => !l && setShowSimpleMenu(!s?.empty)))
|
||||||
createEffect(on([isTextSelection, showLinkInput],([selected, linkEditing]) => !linkEditing && setShowSimpleMenu(selected)))
|
|
||||||
createEffect(on(html, (c?: string) => c && props.onChange?.(c)))
|
createEffect(on(html, (c?: string) => c && props.onChange?.(c)))
|
||||||
createEffect(on(showLinkInput, (x?: boolean) => x && editor()?.chain().focus().run()))
|
createEffect(on(showLinkInput, (x?: boolean) => x && editor()?.chain().focus().run()))
|
||||||
createReaction(on(toolbarElement, (t?: HTMLElement) => t?.addEventListener('mousedown', prevent)))
|
createReaction(on(toolbarElement, (t?: HTMLElement) => t?.addEventListener('mousedown', prevent)))
|
||||||
|
@ -117,19 +128,6 @@ export const MicroEditor = (props: MicroEditorProps): JSX.Element => {
|
||||||
ref={setToolbarElement}
|
ref={setToolbarElement}
|
||||||
>
|
>
|
||||||
<div class={styles.controls}>
|
<div class={styles.controls}>
|
||||||
<Show
|
|
||||||
when={!showLinkInput()}
|
|
||||||
fallback={<InsertLinkForm editor={instance}
|
|
||||||
onClose={() => {
|
|
||||||
setShowLinkInput(false)
|
|
||||||
if (selectionRange()) {
|
|
||||||
const selection = window.getSelection()
|
|
||||||
selection?.removeAllRanges()
|
|
||||||
selection?.addRange(selectionRange()!)
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onFocus={handleLinkInputFocus} />}
|
|
||||||
>
|
|
||||||
<div class={styles.actions}>
|
<div class={styles.actions}>
|
||||||
<Control
|
<Control
|
||||||
key="bold"
|
key="bold"
|
||||||
|
@ -157,6 +155,14 @@ export const MicroEditor = (props: MicroEditorProps): JSX.Element => {
|
||||||
<Icon name="editor-link" />
|
<Icon name="editor-link" />
|
||||||
</Control>
|
</Control>
|
||||||
</div>
|
</div>
|
||||||
|
<Show when={showLinkInput()}>
|
||||||
|
<InsertLinkForm
|
||||||
|
editor={instance}
|
||||||
|
onClose={() => {
|
||||||
|
setShowLinkInput(false)
|
||||||
|
recoverSelection()
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -164,7 +170,7 @@ export const MicroEditor = (props: MicroEditorProps): JSX.Element => {
|
||||||
)}
|
)}
|
||||||
</Show>
|
</Show>
|
||||||
|
|
||||||
<div id="micro-editor" ref={setEditorElement} style={styles.minimal} />
|
<div id="micro-editor" ref={setEditorElement} style={styles.minimal} onFocusOut={storeSelection} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,13 +2,10 @@ import type { Editor } from '@tiptap/core'
|
||||||
import CharacterCount from '@tiptap/extension-character-count'
|
import CharacterCount from '@tiptap/extension-character-count'
|
||||||
import Placeholder from '@tiptap/extension-placeholder'
|
import Placeholder from '@tiptap/extension-placeholder'
|
||||||
import clsx from 'clsx'
|
import clsx from 'clsx'
|
||||||
import { type JSX, Show, createEffect, createSignal, onCleanup } from 'solid-js'
|
import { type JSX, Show, createEffect, createSignal, on, onCleanup } from 'solid-js'
|
||||||
import {
|
import {
|
||||||
createEditorTransaction,
|
|
||||||
createTiptapEditor,
|
createTiptapEditor,
|
||||||
useEditorHTML,
|
useEditorHTML,
|
||||||
useEditorIsEmpty,
|
|
||||||
useEditorIsFocused
|
|
||||||
} from 'solid-tiptap'
|
} from 'solid-tiptap'
|
||||||
import { Toolbar } from 'terracotta'
|
import { Toolbar } from 'terracotta'
|
||||||
import { Icon } from '~/components/_shared/Icon/Icon'
|
import { Icon } from '~/components/_shared/Icon/Icon'
|
||||||
|
@ -63,7 +60,6 @@ export default function MiniEditor(props: MiniEditorProps): JSX.Element {
|
||||||
const [editorElement, setEditorElement] = createSignal<HTMLDivElement>()
|
const [editorElement, setEditorElement] = createSignal<HTMLDivElement>()
|
||||||
const [counter, setCounter] = createSignal(0)
|
const [counter, setCounter] = createSignal(0)
|
||||||
const [showLinkInput, setShowLinkInput] = createSignal(false)
|
const [showLinkInput, setShowLinkInput] = createSignal(false)
|
||||||
const [showSimpleMenu, setShowSimpleMenu] = createSignal(false)
|
|
||||||
const { t } = useLocalize()
|
const { t } = useLocalize()
|
||||||
const { showModal } = useUI()
|
const { showModal } = useUI()
|
||||||
|
|
||||||
|
@ -82,12 +78,9 @@ export default function MiniEditor(props: MiniEditorProps): JSX.Element {
|
||||||
content: props.content || ''
|
content: props.content || ''
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const isEmpty = useEditorIsEmpty(editor)
|
|
||||||
const isFocused = useEditorIsFocused(editor)
|
|
||||||
const isTextSelection = createEditorTransaction(editor, (instance) => !instance?.state.selection.empty)
|
|
||||||
const html = useEditorHTML(editor)
|
const html = useEditorHTML(editor)
|
||||||
|
createEffect(on(html, (c?: string) => c && props.onChange?.(c)))
|
||||||
createEffect(() => setShowSimpleMenu(isTextSelection()))
|
createEffect(on(showLinkInput, (x?: boolean) => x && editor()?.chain().focus().run()))
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
const textLength = editor()?.getText().length || 0
|
const textLength = editor()?.getText().length || 0
|
||||||
|
@ -112,13 +105,12 @@ export default function MiniEditor(props: MiniEditorProps): JSX.Element {
|
||||||
})
|
})
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
class={clsx(styles.SimplifiedEditor, styles.bordered, {
|
class={clsx(styles.SimplifiedEditor, styles.bordered, styles.isFocused)}
|
||||||
[styles.isFocused]: isEmpty() || isFocused()
|
|
||||||
})}
|
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<Show when={showSimpleMenu() || showLinkInput()}>
|
<div id="mini-editor" ref={setEditorElement} />
|
||||||
<Toolbar style={{ 'background-color': 'white' }} ref={setToolbarElement} horizontal>
|
|
||||||
|
<Toolbar style={{ 'background-color': 'white', display: 'inline-flex' }} ref={setToolbarElement} horizontal>
|
||||||
<Show when={editor()} keyed>
|
<Show when={editor()} keyed>
|
||||||
{(instance) => (
|
{(instance) => (
|
||||||
<div class={styles.controls}>
|
<div class={styles.controls}>
|
||||||
|
@ -174,15 +166,13 @@ export default function MiniEditor(props: MiniEditorProps): JSX.Element {
|
||||||
)}
|
)}
|
||||||
</Show>
|
</Show>
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
</Show>
|
|
||||||
|
|
||||||
<div id="mini-editor" ref={setEditorElement} />
|
|
||||||
|
|
||||||
<Show when={counter() > 0}>
|
<Show when={counter() > 0}>
|
||||||
<small class={styles.limit}>
|
<small class={styles.limit}>
|
||||||
{counter()} / {props.limit || '∞'}
|
{counter()} / {props.limit || '∞'}
|
||||||
</small>
|
</small>
|
||||||
</Show>
|
</Show>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user