UNPKG

tldraw

Version:

A tiny little drawing editor.

149 lines (148 loc) 5.8 kB
import { Fragment, jsx, jsxs } from "react/jsx-runtime"; import { T, track, useEditor } from "@tldraw/editor"; import { useCallback, useEffect, useRef, useState } from "react"; import { useTranslation } from "../hooks/useTranslation/useTranslation.mjs"; import { TldrawUiButton } from "./primitives/Button/TldrawUiButton.mjs"; import { TldrawUiButtonLabel } from "./primitives/Button/TldrawUiButtonLabel.mjs"; import { TldrawUiDialogBody, TldrawUiDialogCloseButton, TldrawUiDialogFooter, TldrawUiDialogHeader, TldrawUiDialogTitle } from "./primitives/TldrawUiDialog.mjs"; import { TldrawUiInput } from "./primitives/TldrawUiInput.mjs"; function validateUrl(url) { if (T.linkUrl.isValid(url)) { return { isValid: true, hasProtocol: true }; } if (T.linkUrl.isValid("https://" + url)) { return { isValid: true, hasProtocol: false }; } return { isValid: false, hasProtocol: false }; } function isShapeWithUrl(shape) { return !!(shape && "url" in shape.props && typeof shape.props.url === "string"); } function assertShapeWithUrl(shape) { if (!isShapeWithUrl(shape)) { throw new Error("Shape is not a valid ShapeWithUrl"); } } const EditLinkDialog = track(function EditLinkDialog2({ onClose }) { const editor = useEditor(); const selectedShape = editor.getOnlySelectedShape(); if (!isShapeWithUrl(selectedShape)) { return null; } return /* @__PURE__ */ jsx(EditLinkDialogInner, { onClose, selectedShape }); }); const EditLinkDialogInner = track(function EditLinkDialogInner2({ onClose, selectedShape }) { const editor = useEditor(); const msg = useTranslation(); const rInput = useRef(null); useEffect(() => { editor.timers.requestAnimationFrame(() => rInput.current?.focus()); }, [editor]); const rInitialValue = useRef(selectedShape.props.url); const [urlInputState, setUrlInputState] = useState(() => { const urlValidResult = validateUrl(selectedShape.props.url); const initialValue = urlValidResult.isValid === true ? urlValidResult.hasProtocol ? selectedShape.props.url : "https://" + selectedShape.props.url : "https://"; return { actual: initialValue, safe: initialValue, valid: true }; }); const handleChange = useCallback((rawValue) => { const fixedRawValue = rawValue.replace(/https?:\/\/(https?:\/\/)/, (_match, arg1) => { return arg1; }); const urlValidResult = validateUrl(fixedRawValue); const safeValue = urlValidResult.isValid === true ? urlValidResult.hasProtocol ? fixedRawValue : "https://" + fixedRawValue : "https://"; setUrlInputState({ actual: fixedRawValue, safe: safeValue, valid: urlValidResult.isValid }); }, []); const handleClear = useCallback(() => { const onlySelectedShape = editor.getOnlySelectedShape(); if (!onlySelectedShape) return; assertShapeWithUrl(onlySelectedShape); editor.updateShapes([ { id: onlySelectedShape.id, type: onlySelectedShape.type, props: { url: "" } } ]); onClose(); }, [editor, onClose]); const handleComplete = useCallback(() => { const onlySelectedShape = editor.getOnlySelectedShape(); if (!onlySelectedShape) return; assertShapeWithUrl(onlySelectedShape); if (onlySelectedShape && "url" in onlySelectedShape.props) { if (onlySelectedShape.props.url !== urlInputState.safe) { editor.updateShapes([ { id: onlySelectedShape.id, type: onlySelectedShape.type, props: { url: urlInputState.safe } } ]); } } onClose(); }, [editor, onClose, urlInputState]); const handleCancel = useCallback(() => { onClose(); }, [onClose]); if (!selectedShape) { onClose(); return null; } const isRemoving = rInitialValue.current && !urlInputState.valid; return /* @__PURE__ */ jsxs(Fragment, { children: [ /* @__PURE__ */ jsxs(TldrawUiDialogHeader, { children: [ /* @__PURE__ */ jsx(TldrawUiDialogTitle, { children: msg("edit-link-dialog.title") }), /* @__PURE__ */ jsx(TldrawUiDialogCloseButton, {}) ] }), /* @__PURE__ */ jsx(TldrawUiDialogBody, { children: /* @__PURE__ */ jsxs("div", { className: "tlui-edit-link-dialog", children: [ /* @__PURE__ */ jsx( TldrawUiInput, { ref: rInput, className: "tlui-edit-link-dialog__input", label: "edit-link-dialog.url", autoFocus: true, autoSelect: true, placeholder: "https://example.com", value: urlInputState.actual, onValueChange: handleChange, onComplete: handleComplete, onCancel: handleCancel } ), /* @__PURE__ */ jsx("div", { children: urlInputState.valid ? msg("edit-link-dialog.detail") : msg("edit-link-dialog.invalid-url") }) ] }) }), /* @__PURE__ */ jsxs(TldrawUiDialogFooter, { className: "tlui-dialog__footer__actions", children: [ /* @__PURE__ */ jsx(TldrawUiButton, { type: "normal", onClick: handleCancel, onTouchEnd: handleCancel, children: /* @__PURE__ */ jsx(TldrawUiButtonLabel, { children: msg("edit-link-dialog.cancel") }) }), isRemoving ? /* @__PURE__ */ jsx(TldrawUiButton, { type: "danger", onTouchEnd: handleClear, onClick: handleClear, children: /* @__PURE__ */ jsx(TldrawUiButtonLabel, { children: msg("edit-link-dialog.clear") }) }) : /* @__PURE__ */ jsx( TldrawUiButton, { type: "primary", disabled: !urlInputState.valid, onTouchEnd: handleComplete, onClick: handleComplete, children: /* @__PURE__ */ jsx(TldrawUiButtonLabel, { children: msg("edit-link-dialog.save") }) } ) ] }) ] }); }); export { EditLinkDialog, EditLinkDialogInner }; //# sourceMappingURL=EditLinkDialog.mjs.map