buttons
This commit is contained in:
parent
abd7c2f073
commit
384da1e832
18 changed files with 150 additions and 116 deletions
95
src/components/inputs/FancyTextEditor.tsx
Normal file
95
src/components/inputs/FancyTextEditor.tsx
Normal file
|
@ -0,0 +1,95 @@
|
|||
import { useRef, useEffect, KeyboardEvent, useState } from 'react'
|
||||
|
||||
interface TextInputProps {
|
||||
value: string
|
||||
onInput: (value: string) => void
|
||||
onKeyDown: (e: TextInputKeyDownEvent) => void
|
||||
className?: string
|
||||
placeholder?: string
|
||||
}
|
||||
|
||||
export interface TextInputKeyDownEvent {
|
||||
key: string
|
||||
ctrlKey: boolean
|
||||
preventDefault: () => void
|
||||
}
|
||||
|
||||
export default function FancyTextEditor({
|
||||
value: _value,
|
||||
onInput,
|
||||
onKeyDown,
|
||||
className: extraClasses = '',
|
||||
placeholder = '',
|
||||
}: TextInputProps) {
|
||||
const divRef = useRef<HTMLDivElement>(null)
|
||||
const [hasFocus, setHasFocus] = useState(false)
|
||||
|
||||
// the contenteditable likes to slip in newlines at the bottom of our innerText
|
||||
// which makes it bad to check for empty string because it might be "\n"
|
||||
// so we just trim it upfront and then fogeddaboudit
|
||||
const value = _value.trim()
|
||||
|
||||
// The funky mechanics here are to stop the cursor from jumping back the start.
|
||||
// It probably will have the cursor jump to the start if anything changes programmatically,
|
||||
// which is probably unnecessary anyway
|
||||
useEffect(() => {
|
||||
const div = divRef.current
|
||||
if (div == null) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!value && !hasFocus) {
|
||||
div.innerText = placeholder
|
||||
} else if (div.innerText !== value) {
|
||||
div.innerText = value
|
||||
}
|
||||
}, [hasFocus, placeholder, value])
|
||||
|
||||
useEffect(() => {
|
||||
const div = divRef.current!
|
||||
if (div == null) {
|
||||
return
|
||||
}
|
||||
|
||||
const inputListener = () => {
|
||||
onInput(div.innerText)
|
||||
}
|
||||
|
||||
const blurListener = () => {
|
||||
setHasFocus(false)
|
||||
if (!value) {
|
||||
div.innerText = placeholder
|
||||
}
|
||||
}
|
||||
|
||||
const focusListener = () => {
|
||||
setHasFocus(true)
|
||||
div.innerText = value
|
||||
}
|
||||
|
||||
div.addEventListener('input', inputListener)
|
||||
div.addEventListener('blur', blurListener)
|
||||
div.addEventListener('focus', focusListener)
|
||||
|
||||
return () => {
|
||||
div.removeEventListener('focus', focusListener)
|
||||
div.removeEventListener('blur', blurListener)
|
||||
div.removeEventListener('input', inputListener)
|
||||
}
|
||||
}, [onInput, placeholder, value])
|
||||
|
||||
const handleKeyDown = (e: KeyboardEvent<HTMLDivElement>) => {
|
||||
onKeyDown(e)
|
||||
}
|
||||
|
||||
const textColour = value ? 'text-gray-900' : 'text-gray-500'
|
||||
return (
|
||||
<div
|
||||
ref={divRef}
|
||||
className={`text-input w-full min-h-30 ${textColour} ${extraClasses}`}
|
||||
contentEditable
|
||||
onKeyDown={handleKeyDown}
|
||||
suppressContentEditableWarning
|
||||
></div>
|
||||
)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue