2024-09-15 18:47:21 +00:00
|
|
|
import CharacterCount from '@tiptap/extension-character-count'
|
|
|
|
import Placeholder from '@tiptap/extension-placeholder'
|
|
|
|
import clsx from 'clsx'
|
2024-09-27 16:31:54 +00:00
|
|
|
import { type JSX, Show, createEffect, createSignal, on } from 'solid-js'
|
|
|
|
import { createEditorTransaction, createTiptapEditor, useEditorHTML } from 'solid-tiptap'
|
2024-09-27 18:09:50 +00:00
|
|
|
import { Button } from '~/components/_shared/Button'
|
|
|
|
import { useLocalize } from '~/context/localize'
|
2024-09-24 06:48:39 +00:00
|
|
|
import { base } from '~/lib/editorExtensions'
|
2024-09-27 17:57:25 +00:00
|
|
|
import { EditorToolbar } from '../EditorToolbar/EditorToolbar'
|
2024-09-15 18:47:21 +00:00
|
|
|
|
2024-09-27 18:09:50 +00:00
|
|
|
import styles from './MiniEditor.module.scss'
|
2024-09-15 18:47:21 +00:00
|
|
|
|
|
|
|
interface MiniEditorProps {
|
|
|
|
content?: string
|
|
|
|
onChange?: (content: string) => void
|
2024-09-27 16:31:54 +00:00
|
|
|
onSubmit?: (content: string) => void
|
|
|
|
onCancel?: () => void
|
2024-09-15 18:47:21 +00:00
|
|
|
limit?: number
|
|
|
|
placeholder?: string
|
|
|
|
}
|
|
|
|
|
|
|
|
export default function MiniEditor(props: MiniEditorProps): JSX.Element {
|
2024-09-27 17:57:25 +00:00
|
|
|
const { t } = useLocalize()
|
2024-09-15 18:47:21 +00:00
|
|
|
const [editorElement, setEditorElement] = createSignal<HTMLDivElement>()
|
|
|
|
const [counter, setCounter] = createSignal(0)
|
|
|
|
|
|
|
|
const editor = createTiptapEditor(() => ({
|
|
|
|
element: editorElement()!,
|
|
|
|
extensions: [
|
|
|
|
...base,
|
|
|
|
Placeholder.configure({ emptyNodeClass: styles.emptyNode, placeholder: props.placeholder }),
|
|
|
|
CharacterCount.configure({ limit: props.limit })
|
|
|
|
],
|
|
|
|
editorProps: {
|
|
|
|
attributes: {
|
|
|
|
class: styles.simplifiedEditorField
|
|
|
|
}
|
|
|
|
},
|
|
|
|
content: props.content || ''
|
|
|
|
}))
|
|
|
|
|
2024-09-27 17:57:25 +00:00
|
|
|
const isFocused = createEditorTransaction(editor, (instance) => instance?.isFocused)
|
|
|
|
const isEmpty = createEditorTransaction(editor, (instance) => instance?.isEmpty)
|
2024-09-15 18:47:21 +00:00
|
|
|
const html = useEditorHTML(editor)
|
2024-09-27 17:57:25 +00:00
|
|
|
|
2024-09-27 14:26:40 +00:00
|
|
|
createEffect(on(html, (c?: string) => c && props.onChange?.(c)))
|
2024-09-15 18:47:21 +00:00
|
|
|
|
|
|
|
createEffect(() => {
|
|
|
|
const textLength = editor()?.getText().length || 0
|
|
|
|
setCounter(textLength)
|
|
|
|
const content = html()
|
|
|
|
content && props.onChange?.(content)
|
|
|
|
})
|
|
|
|
|
2024-09-27 17:57:25 +00:00
|
|
|
const handleSubmit = () => {
|
|
|
|
html() && props.onSubmit?.(html() || '')
|
|
|
|
editor()?.commands.clearContent(true)
|
|
|
|
}
|
2024-09-15 18:47:21 +00:00
|
|
|
|
|
|
|
return (
|
2024-09-27 18:09:50 +00:00
|
|
|
<div class={clsx(styles.MiniEditor, styles.bordered, { [styles.isFocused]: isFocused() })}>
|
2024-09-15 18:47:21 +00:00
|
|
|
<div>
|
|
|
|
<div id="mini-editor" ref={setEditorElement} />
|
|
|
|
|
2024-09-27 17:57:25 +00:00
|
|
|
<EditorToolbar editor={editor} mode={'mini'} />
|
|
|
|
|
|
|
|
<div class={styles.buttons}>
|
|
|
|
<Button
|
|
|
|
value={t('Cancel')}
|
|
|
|
disabled={isEmpty()}
|
|
|
|
variant="secondary"
|
|
|
|
onClick={() => editor()?.commands.clearContent()}
|
|
|
|
/>
|
|
|
|
<Button value={t('Send')} variant="primary" disabled={isEmpty()} onClick={handleSubmit} />
|
|
|
|
</div>
|
2024-09-27 14:26:40 +00:00
|
|
|
|
2024-09-15 18:47:21 +00:00
|
|
|
<Show when={counter() > 0}>
|
|
|
|
<small class={styles.limit}>
|
|
|
|
{counter()} / {props.limit || '∞'}
|
|
|
|
</small>
|
|
|
|
</Show>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
)
|
|
|
|
}
|