import isPrintableKeyEvent from "is-printable-key-event"; import * as React from "react"; import styled from "styled-components"; type Props = Omit, "ref" | "onChange"> & { disabled?: boolean; readOnly?: boolean; onChange?: (text: string) => void; onBlur?: React.FocusEventHandler | undefined; onInput?: React.FormEventHandler | undefined; onKeyDown?: React.KeyboardEventHandler | undefined; placeholder?: string; maxLength?: number; autoFocus?: boolean; children?: React.ReactNode; value: string; }; /** * Defines a content editable component with the same interface as a native * HTMLInputElement (or, as close as we can get). */ function ContentEditable({ disabled, onChange, onInput, onBlur, onKeyDown, value, children, className, maxLength, autoFocus, placeholder, readOnly, ...rest }: Props) { const ref = React.useRef(null); const [innerHTML, setInnerHTML] = React.useState(value); const lastValue = React.useRef(""); const wrappedEvent = ( callback: | React.FocusEventHandler | React.FormEventHandler | React.KeyboardEventHandler | undefined ) => (event: any) => { const text = ref.current?.innerText || ""; if (maxLength && isPrintableKeyEvent(event) && text.length >= maxLength) { event?.preventDefault(); return; } if (text !== lastValue.current) { lastValue.current = text; onChange && onChange(text); } callback?.(event); }; React.useLayoutEffect(() => { if (autoFocus) { ref.current?.focus(); } }); React.useEffect(() => { if (value !== ref.current?.innerText) { setInnerHTML(value); } }, [value]); return (
{children}
); } const Content = styled.span` &:empty { display: inline-block; } &:empty::before { display: inline-block; color: ${(props) => props.theme.placeholder}; -webkit-text-fill-color: ${(props) => props.theme.placeholder}; content: attr(data-placeholder); pointer-events: none; height: 0; } `; export default React.memo(ContentEditable);