webapp/src/components/Editor/EditorBubbleMenu.tsx
2023-03-20 12:19:14 +03:00

124 lines
3.8 KiB
TypeScript

import type { Editor } from '@tiptap/core'
import styles from './EditorBubbleMenu.module.scss'
import { Icon } from '../_shared/Icon'
import { clsx } from 'clsx'
import { createEditorTransaction } from 'solid-tiptap'
import { createSignal } from 'solid-js'
import { useLocalize } from '../../context/localize'
import validateUrl from '../../utils/validateUrl'
type BubbleMenuProps = {
editor: Editor
ref: (el: HTMLDivElement) => void
}
export const EditorBubbleMenu = (props: BubbleMenuProps) => {
const { t } = useLocalize()
const [linkEditorOpen, setLinkEditorOpen] = createSignal<boolean>(false)
const [url, setUrl] = createSignal<string>('')
const [prevUrl, setPrevUrl] = createSignal<string | null>(null)
const [linkError, setLinkError] = createSignal<string | null>(null)
const isBold = createEditorTransaction(
() => props.editor,
(editor) => editor && editor.isActive('bold')
)
const isLink = createEditorTransaction(
() => props.editor,
(editor) => {
editor && editor.isActive('link')
setPrevUrl(editor && editor.getAttributes('link').href)
}
)
const clearLinkForm = () => {
setUrl('')
setLinkEditorOpen(false)
}
const handleSubmitLink = (e) => {
e.preventDefault()
if (url().length === 0) {
props.editor.chain().focus().unsetLink().run()
clearLinkForm()
return
}
if (url().length > 1 && validateUrl(url())) {
props.editor.commands.toggleLink({ href: url() })
clearLinkForm()
} else {
setLinkError(t('Invalid url format'))
}
}
return (
<>
<div ref={props.ref} class={styles.bubbleMenu}>
{linkEditorOpen() ? (
<>
<form onSubmit={(e) => handleSubmitLink(e)} class={styles.linkForm}>
<input
type="text"
placeholder={t('Enter URL address')}
autofocus
value={prevUrl() ? prevUrl() : null}
onChange={(e) => setUrl(e.currentTarget.value)}
/>
<button type="submit">
<Icon name="status-done" />
</button>
<button role="button" onClick={() => clearLinkForm()}>
<Icon name="status-cancel" />
</button>
</form>
{linkError() && <div class={styles.linkError}>{linkError()}</div>}
</>
) : (
<>
<button
onClick={(e) => {
e.preventDefault()
setLinkEditorOpen(true)
}}
class={clsx(styles.bubbleMenuButton, {
[styles.bubbleMenuButtonActive]: isLink()
})}
>
<Icon name="editor-link" />
</button>
<button class={clsx(styles.bubbleMenuButton)}>
<Icon name="editor-text-size" />
</button>
<button
class={clsx(styles.bubbleMenuButton, {
[styles.bubbleMenuButtonActive]: isBold()
})}
onClick={(e) => {
e.preventDefault()
props.editor.commands.toggleBold()
}}
>
<Icon name="editor-bold" />
</button>
<button class={styles.bubbleMenuButton}>
<Icon name="editor-italic" />
</button>
<div class={styles.delimiter}>D</div>
<button class={styles.bubbleMenuButton}>
<Icon name="editor-link" />
</button>
<button class={styles.bubbleMenuButton}>
<Icon name="editor-footnote" />
</button>
<div class={styles.delimiter} />
<button class={styles.bubbleMenuButton}>
<Icon name="editor-ul" />
</button>
</>
)}
</div>
</>
)
}