smartrte-react 0.2.8 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,20 @@
1
+ export interface LinkEditorApplyValue {
2
+ href: string;
3
+ text?: string;
4
+ openInNewTab: boolean;
5
+ }
6
+ export interface LinkEditorPopoverProps {
7
+ x: number;
8
+ y: number;
9
+ initialHref?: string;
10
+ initialText?: string;
11
+ initialOpenInNewTab?: boolean;
12
+ showTextInput?: boolean;
13
+ showOpen?: boolean;
14
+ showRemove?: boolean;
15
+ onApply: (value: LinkEditorApplyValue) => void;
16
+ onOpen?: () => void;
17
+ onRemove?: () => void;
18
+ onCancel: () => void;
19
+ }
20
+ export declare function LinkEditorPopover({ x, y, initialHref, initialText, initialOpenInNewTab, showTextInput, showOpen, showRemove, onApply, onOpen, onRemove, onCancel, }: LinkEditorPopoverProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,87 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useId, useRef, useState } from "react";
3
+ import { normalizeLinkInput } from "smartrte-core";
4
+ const inputStyle = {
5
+ width: "100%",
6
+ height: 36,
7
+ boxSizing: "border-box",
8
+ marginTop: 5,
9
+ padding: "0 10px",
10
+ border: "1px solid var(--srte-input-border)",
11
+ borderRadius: 6,
12
+ outline: "none",
13
+ background: "var(--srte-input-bg)",
14
+ color: "var(--srte-input-text)",
15
+ font: "inherit",
16
+ };
17
+ const buttonStyle = {
18
+ minHeight: 34,
19
+ padding: "0 11px",
20
+ border: "1px solid var(--srte-input-border)",
21
+ borderRadius: 6,
22
+ background: "var(--srte-input-bg)",
23
+ color: "var(--srte-menu-text)",
24
+ cursor: "pointer",
25
+ fontWeight: 500,
26
+ };
27
+ export function LinkEditorPopover({ x, y, initialHref = "", initialText = "", initialOpenInNewTab = false, showTextInput = false, showOpen = false, showRemove = false, onApply, onOpen, onRemove, onCancel, }) {
28
+ const [href, setHref] = useState(initialHref);
29
+ const [text, setText] = useState(initialText);
30
+ const [openInNewTab, setOpenInNewTab] = useState(initialOpenInNewTab);
31
+ const [error, setError] = useState("");
32
+ const hrefRef = useRef(null);
33
+ const titleId = useId();
34
+ const errorId = useId();
35
+ useEffect(() => {
36
+ hrefRef.current?.focus();
37
+ hrefRef.current?.select();
38
+ }, []);
39
+ const apply = () => {
40
+ const normalized = normalizeLinkInput(href);
41
+ if (!normalized.href) {
42
+ setError("Enter a safe, valid web URL, email address, phone number, or page anchor.");
43
+ return;
44
+ }
45
+ if (showTextInput && !text.trim()) {
46
+ setError("Enter display text.");
47
+ return;
48
+ }
49
+ onApply({
50
+ href: normalized.href,
51
+ text: showTextInput ? text.trim() : undefined,
52
+ openInNewTab,
53
+ });
54
+ };
55
+ return (_jsxs("div", { "data-srte-link-popover": "true", role: "dialog", "aria-labelledby": titleId, style: {
56
+ position: "fixed",
57
+ left: Math.max(8, Math.min(x, typeof window === "undefined" ? x : window.innerWidth - 376)),
58
+ top: Math.max(8, Math.min(y, typeof window === "undefined" ? y : window.innerHeight - 344)),
59
+ zIndex: 70,
60
+ width: 360,
61
+ maxWidth: "calc(100vw - 16px)",
62
+ boxSizing: "border-box",
63
+ maxHeight: "calc(100vh - 16px)",
64
+ overflowY: "auto",
65
+ background: "var(--srte-menu-bg)",
66
+ color: "var(--srte-menu-text)",
67
+ border: "1px solid var(--srte-border)",
68
+ borderRadius: 10,
69
+ boxShadow: "var(--srte-menu-shadow)",
70
+ padding: 14,
71
+ }, onKeyDown: (event) => {
72
+ if (event.key === "Escape") {
73
+ event.preventDefault();
74
+ onCancel();
75
+ }
76
+ if (event.key === "Enter" && !event.shiftKey) {
77
+ event.preventDefault();
78
+ apply();
79
+ }
80
+ }, children: [_jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 12 }, children: [_jsxs("div", { id: titleId, style: { display: "flex", alignItems: "center", gap: 8, fontWeight: 650 }, children: [_jsx("span", { "aria-hidden": "true", style: { color: "var(--srte-primary)", fontSize: 18 }, children: "\u2197" }), showRemove ? "Edit link" : "Insert link"] }), _jsx("button", { type: "button", "aria-label": "Close link editor", title: "Close", onClick: onCancel, style: { ...buttonStyle, minWidth: 28, minHeight: 28, padding: 0, border: 0, background: "transparent", fontSize: 18 }, children: "\u00D7" })] }), showTextInput && (_jsxs("label", { style: { display: "block", marginBottom: 10, fontSize: 12, fontWeight: 600 }, children: ["Display text", _jsx("input", { "data-srte-link-text-input": "true", value: text, autoComplete: "off", onChange: (event) => {
81
+ setText(event.target.value);
82
+ setError("");
83
+ }, style: inputStyle })] })), _jsxs("label", { style: { display: "block", marginBottom: 10, fontSize: 12, fontWeight: 600 }, children: ["Link destination", _jsx("input", { ref: hrefRef, "data-srte-link-href-input": "true", value: href, inputMode: "url", autoCapitalize: "none", autoCorrect: "off", spellCheck: false, placeholder: "Paste a URL, email, phone, or #anchor", "aria-invalid": Boolean(error), "aria-describedby": error ? errorId : undefined, onChange: (event) => {
84
+ setHref(event.target.value);
85
+ setError("");
86
+ }, style: { ...inputStyle, borderColor: error ? "var(--srte-danger)" : "var(--srte-input-border)" } })] }), _jsxs("label", { style: { display: "flex", alignItems: "center", gap: 8, marginBottom: error ? 8 : 14, fontSize: 13, cursor: "pointer" }, children: [_jsx("input", { "data-srte-link-new-tab-input": "true", type: "checkbox", checked: openInNewTab, onChange: (event) => setOpenInNewTab(event.target.checked) }), "Open in a new tab"] }), error && (_jsx("div", { id: errorId, "data-srte-link-error": "true", role: "alert", style: { color: "var(--srte-danger)", fontSize: 12, marginBottom: 10 }, children: error })), _jsxs("div", { style: { display: "flex", alignItems: "center", gap: 7 }, children: [showRemove && (_jsx("button", { type: "button", onClick: onRemove, style: { ...buttonStyle, color: "var(--srte-danger)", marginRight: "auto" }, children: "Remove link" })), !showRemove && _jsx("span", { style: { flex: 1 } }), showOpen && _jsx("button", { type: "button", onClick: onOpen, style: buttonStyle, children: "Open" }), _jsx("button", { type: "button", onClick: onCancel, style: buttonStyle, children: "Cancel" }), _jsx("button", { type: "button", onClick: apply, style: { ...buttonStyle, borderColor: "var(--srte-primary)", background: "var(--srte-primary)", color: "var(--srte-on-primary)" }, children: showRemove ? "Update" : "Insert" })] })] }));
87
+ }