tldraw
Version:
A tiny little drawing editor.
149 lines (148 loc) • 5.8 kB
JavaScript
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