webapp/src/components/Editor/MiniEditor/MiniEditor.tsx

86 lines
2.7 KiB
TypeScript
Raw Normal View History

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>
)
}