@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
JavaScript
;
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