UNPKG

@liveblocks/react-ui

Version:

A set of React pre-built components for the Liveblocks products. Liveblocks is the all-in-one toolkit to build collaborative products like Figma, Notion, and more.

203 lines (199 loc) 7.13 kB
'use strict'; var jsxRuntime = require('react/jsx-runtime'); var _private = require('@liveblocks/react/_private'); var reactSlot = require('@radix-ui/react-slot'); var react = require('react'); var slate = require('slate'); var slateHistory = require('slate-history'); var slateReact = require('slate-react'); var normalize = require('../slate/plugins/normalize.cjs'); var isEmpty = require('../slate/utils/is-empty.cjs'); const AI_CHAT_COMPOSER_SUBMIT_NAME = "AiChatComposerSubmit"; const AI_CHAT_COMPOSER_EDITOR_NAME = "AiChatComposerEditor"; const AI_CHAT_COMPOSER_FORM_NAME = "AiChatComposerForm"; const AiChatComposerContext = react.createContext(null); const AiChatComposerForm = react.forwardRef( ({ onComposerSubmit, onSubmit, disabled, asChild, ...props }, forwardedRef) => { const Component = asChild ? reactSlot.Slot : "form"; const formRef = react.useRef(null); const editorRef = react.useRef(null); if (editorRef.current === null) { editorRef.current = normalize.withNormalize(slateHistory.withHistory(slateReact.withReact(slate.createEditor()))); } const editor = editorRef.current; const [isEditorEmpty, setIsEditorEmpty] = react.useState(true); const handleSubmit = react.useCallback( (event) => { if (disabled || isEmpty.isEmpty(editor, editor.children)) return; onSubmit?.(event); if (onComposerSubmit === void 0 || event.isDefaultPrevented()) { event.preventDefault(); return; } const content = editor.children.map((block) => { if ("type" in block && block.type === "paragraph") { return block.children.map((child) => { if ("text" in child) { return child.text; } return ""; }).join(""); } return ""; }).join("\n"); onComposerSubmit({ text: content }, event); if (event.isDefaultPrevented()) { return; } event.preventDefault(); slate.Transforms.delete(editor, { at: { anchor: slate.Editor.start(editor, []), focus: slate.Editor.end(editor, []) } }); }, [disabled, editor, onSubmit, onComposerSubmit] ); _private.useLayoutEffect(() => { setIsEditorEmpty(isEmpty.isEmpty(editor, editor.children)); }, [editor]); const handleEditorValueChange = react.useCallback(() => { setIsEditorEmpty(isEmpty.isEmpty(editor, editor.children)); }, [editor]); const requestFormSubmit = react.useCallback(() => { if (isEmpty.isEmpty(editor, editor.children)) return; requestAnimationFrame(() => { if (formRef.current === null) return; if (typeof formRef.current.requestSubmit === "function") { return formRef.current.requestSubmit(); } const submitter = document.createElement("input"); submitter.type = "submit"; submitter.hidden = true; formRef.current.appendChild(submitter); submitter.click(); formRef.current.removeChild(submitter); }); }, [editor]); react.useImperativeHandle( forwardedRef, () => formRef.current, [] ); return /* @__PURE__ */ jsxRuntime.jsx(AiChatComposerContext.Provider, { value: { editor, onEditorValueChange: handleEditorValueChange, isEditorEmpty, requestFormSubmit, disabled: disabled || false }, children: /* @__PURE__ */ jsxRuntime.jsx(Component, { onSubmit: handleSubmit, ...props, ref: formRef }) }); } ); const AiChatComposerEditor = react.forwardRef( ({ defaultValue = "", onKeyDown, disabled, autoFocus, ...props }, forwardedRef) => { const context = react.useContext(AiChatComposerContext); if (context === null) { throw new Error("AiChatComposer.Form is missing from the React tree."); } const { editor, onEditorValueChange, requestFormSubmit, disabled: isFormDisabled } = context; const handleKeyDown = react.useCallback( (event) => { onKeyDown?.(event); if (event.isDefaultPrevented()) return; if (event.key === "Enter" && !event.shiftKey) { event.preventDefault(); requestFormSubmit(); } else if (event.key === "Enter" && event.shiftKey) { event.preventDefault(); editor.insertBreak(); } }, [editor, onKeyDown, requestFormSubmit] ); react.useImperativeHandle( forwardedRef, () => slateReact.ReactEditor.toDOMNode(editor, editor), [editor] ); react.useEffect(() => { if (!autoFocus) return; try { if (!slateReact.ReactEditor.isFocused(editor)) { slate.Transforms.select(editor, slate.Editor.end(editor, [])); slateReact.ReactEditor.focus(editor); } } catch { } }, [editor, autoFocus]); const initialValue = react.useMemo(() => { return defaultValue.split("\n").map((text) => ({ type: "paragraph", children: [{ text }] })); }, [defaultValue]); return /* @__PURE__ */ jsxRuntime.jsx(slateReact.Slate, { editor, initialValue, onValueChange: onEditorValueChange, children: /* @__PURE__ */ jsxRuntime.jsx(slateReact.Editable, { enterKeyHint: "send", autoCapitalize: "sentences", onKeyDown: handleKeyDown, "data-disabled": disabled || isFormDisabled || void 0, ...props, readOnly: disabled || isFormDisabled, disabled: disabled || isFormDisabled, renderPlaceholder: function({ attributes, children }) { const { opacity: _opacity, ...style } = attributes.style; return /* @__PURE__ */ jsxRuntime.jsx("span", { ...attributes, style, "data-placeholder": "", children }); } }) }); } ); const AiChatComposerSubmit = react.forwardRef(({ disabled, asChild, ...props }, forwardedRef) => { const Component = asChild ? reactSlot.Slot : "button"; const context = react.useContext(AiChatComposerContext); if (context === null) { throw new Error("AiChatComposer.Form is missing from the React tree."); } const { disabled: isFormDisabled, isEditorEmpty } = context; return /* @__PURE__ */ jsxRuntime.jsx(Component, { type: "submit", ...props, ref: forwardedRef, disabled: disabled || isFormDisabled || isEditorEmpty }); }); if (process.env.NODE_ENV !== "production") { AiChatComposerEditor.displayName = AI_CHAT_COMPOSER_EDITOR_NAME; AiChatComposerForm.displayName = AI_CHAT_COMPOSER_FORM_NAME; AiChatComposerSubmit.displayName = AI_CHAT_COMPOSER_SUBMIT_NAME; } exports.AiChatComposerEditor = AiChatComposerEditor; exports.AiChatComposerForm = AiChatComposerForm; exports.AiChatComposerSubmit = AiChatComposerSubmit; exports.Editor = AiChatComposerEditor; exports.Form = AiChatComposerForm; exports.Submit = AiChatComposerSubmit; //# sourceMappingURL=index.cjs.map