@malaya_jeeva/rich-text-editor 1.0.0 → 1.0.2

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.
package/dist/index.mjs CHANGED
@@ -1,76 +1,117 @@
1
- // src/RichTextEditor.jsx
2
- import { useState, useRef, useCallback, useEffect } from "react";
1
+ // src/RichTextEditor.tsx
2
+ import {
3
+ useState,
4
+ useRef,
5
+ useCallback,
6
+ useEffect
7
+ } from "react";
8
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
9
  var CSS = `
4
10
  * { box-sizing: border-box; }
5
-
6
- /* \u2500\u2500 Toolbar \u2500\u2500 */
7
11
  .rte-toolbar {
8
12
  display: flex; flex-wrap: wrap; align-items: center; gap: 1px;
9
- padding: 4px 8px;
10
- background: #f8f8f8;
11
- border-bottom: 1px solid #e0e0e0;
12
- }
13
- @media (prefers-color-scheme: dark) {
14
- .rte-toolbar { background: #1e1e1e; border-color: #333; }
13
+ padding: 4px 8px; background: #f8f8f8; border-bottom: 1px solid #e0e0e0;
15
14
  }
15
+ @media (prefers-color-scheme: dark) { .rte-toolbar { background: #1e1e1e; border-color: #333; } }
16
16
 
17
17
  .rte-btn {
18
- background: transparent; border: none; border-radius: 3px;
19
- cursor: pointer; height: 30px; min-width: 30px; padding: 0 6px;
20
- color: #444; font-size: 13px; font-weight: 500;
21
- font-family: var(--font-sans); display: inline-flex; align-items: center;
22
- justify-content: center; user-select: none; white-space: nowrap;
23
- transition: background 0.1s;
24
- flex-shrink: 0;
18
+ background: transparent; border: none; border-radius: 3px; cursor: pointer;
19
+ height: 30px; min-width: 30px; padding: 0 6px; color: #444; font-size: 13px;
20
+ font-weight: 500; font-family: var(--font-sans); display: inline-flex;
21
+ align-items: center; justify-content: center; user-select: none;
22
+ white-space: nowrap; transition: background 0.1s; flex-shrink: 0;
25
23
  }
26
- .rte-btn:hover { background: #e8e8e8; }
27
- .rte-btn.active { background: #d0e4ff; color: #1a5fb4; }
24
+ .rte-btn:hover { background: #e8e8e8; }
25
+ .rte-btn.active { background: #d0e4ff; color: #1a5fb4; }
26
+ .rte-btn.danger { color: #c0392b; }
27
+ .rte-btn.danger:hover { background: #fdecea; }
28
28
  @media (prefers-color-scheme: dark) {
29
- .rte-btn { color: #ccc; }
30
- .rte-btn:hover { background: #2e2e2e; }
29
+ .rte-btn { color: #ccc; } .rte-btn:hover { background: #2e2e2e; }
31
30
  .rte-btn.active { background: #1a3a5c; color: #90c4ff; }
31
+ .rte-btn.danger { color: #ff6b6b; } .rte-btn.danger:hover { background: #3a1a1a; }
32
32
  }
33
+ .rte-sep { width: 1px; height: 20px; background: #d8d8d8; margin: 0 4px; flex-shrink: 0; }
34
+ @media (prefers-color-scheme: dark) { .rte-sep { background: #3a3a3a; } }
33
35
 
34
- /* thin vertical separator */
35
- .rte-sep {
36
- width: 1px; height: 20px; background: #d8d8d8;
37
- margin: 0 4px; flex-shrink: 0;
38
- }
39
- @media (prefers-color-scheme: dark) {
40
- .rte-sep { background: #3a3a3a; }
41
- }
42
-
43
- /* Block format select */
44
36
  .rte-select {
45
37
  height: 28px; border: 1px solid #d8d8d8; border-radius: 3px;
46
- background: #fff; color: #333;
47
- font-size: 12px; padding: 0 22px 0 7px; cursor: pointer;
48
- outline: none; font-family: var(--font-sans);
38
+ background: #fff; color: #333; font-size: 12px; padding: 0 22px 0 7px;
39
+ cursor: pointer; outline: none; font-family: var(--font-sans);
49
40
  appearance: none; -webkit-appearance: none;
50
41
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23999'/%3E%3C/svg%3E");
51
42
  background-repeat: no-repeat; background-position: right 6px center;
52
43
  }
44
+ @media (prefers-color-scheme: dark) { .rte-select { background-color: #2a2a2a; border-color: #444; color: #ccc; } }
45
+ .rte-swatch { width: 14px; height: 3px; border-radius: 1px; margin-top: 2px; }
46
+
47
+ /* Table picker */
48
+ .rte-tp-wrap { position: relative; display: inline-flex; }
49
+ .rte-tp {
50
+ position: absolute; top: 34px; left: 0; z-index: 100;
51
+ background: #fff; border: 1px solid #d8d8d8; border-radius: 6px;
52
+ box-shadow: 0 4px 16px rgba(0,0,0,0.12); padding: 10px;
53
+ }
54
+ @media (prefers-color-scheme: dark) { .rte-tp { background: #222; border-color: #444; } }
55
+ .rte-tp-lbl { font-size: 11px; color: #888; text-align: center; margin-bottom: 8px; font-family: var(--font-sans); }
56
+ .rte-tp-grid { display: grid; grid-template-columns: repeat(8, 18px); gap: 2px; }
57
+ .rte-tp-cell { width: 18px; height: 18px; border-radius: 2px; border: 1px solid #ddd; cursor: pointer; background: #fff; transition: background 0.08s; }
58
+ .rte-tp-cell.on { background: #d0e4ff; border-color: #1a5fb4; }
53
59
  @media (prefers-color-scheme: dark) {
54
- .rte-select { background-color: #2a2a2a; border-color: #444; color: #ccc; }
60
+ .rte-tp-cell { background: #2a2a2a; border-color: #444; }
61
+ .rte-tp-cell.on { background: #1a3a5c; border-color: #90c4ff; }
55
62
  }
56
63
 
57
- /* color swatch under A */
58
- .rte-swatch { width: 14px; height: 3px; border-radius: 1px; margin-top: 2px; }
59
-
60
- /* \u2500\u2500 Editor body \u2500\u2500 */
61
- .rte-wrap {
62
- background: #fff;
64
+ /* \u2500\u2500 Floating toolbar \u2500\u2500 */
65
+ .rte-float {
66
+ position: absolute; z-index: 50;
67
+ background: #fff; border: 1px solid #d0d0d0;
68
+ border-radius: 8px; box-shadow: 0 4px 20px rgba(0,0,0,0.13);
69
+ padding: 4px 6px;
70
+ display: flex; align-items: center; gap: 1px; flex-wrap: wrap;
63
71
  }
72
+ @media (prefers-color-scheme: dark) { .rte-float { background: #222; border-color: #444; } }
73
+ .rte-float-arrow {
74
+ position: absolute; top: -7px; width: 14px; height: 7px;
75
+ }
76
+ .rte-float-arrow::before,
77
+ .rte-float-arrow::after {
78
+ content: ''; position: absolute; left: 0;
79
+ border-left: 7px solid transparent; border-right: 7px solid transparent;
80
+ }
81
+ .rte-float-arrow::before { top: 0; border-bottom: 7px solid #d0d0d0; }
82
+ .rte-float-arrow::after { top: 1px; border-bottom: 6px solid #fff; }
64
83
  @media (prefers-color-scheme: dark) {
65
- .rte-wrap { background: #141414; }
84
+ .rte-float-arrow::before { border-bottom-color: #444; }
85
+ .rte-float-arrow::after { border-bottom-color: #222; }
86
+ }
87
+
88
+ /* Link bar inside float */
89
+ .rte-link-float { min-width: 340px; }
90
+ .rte-link-float input {
91
+ flex: 1; height: 26px; border: 1px solid #ccc; border-radius: 3px;
92
+ padding: 0 8px; font-size: 13px; outline: none; font-family: var(--font-sans);
93
+ background: #fff; color: #222;
94
+ min-width: 0;
66
95
  }
96
+ @media (prefers-color-scheme: dark) { .rte-link-float input { background: #1a1a1a; border-color: #555; color: #ddd; } }
97
+ .rte-link-apply {
98
+ height: 26px; padding: 0 10px; background: #1a6fc4; color: #fff;
99
+ border: none; border-radius: 3px; font-size: 12px; font-weight: 600; cursor: pointer; white-space: nowrap;
100
+ }
101
+ .rte-link-cancel {
102
+ height: 26px; padding: 0 8px; background: transparent; color: #666;
103
+ border: 1px solid #ccc; border-radius: 3px; font-size: 12px; cursor: pointer;
104
+ }
105
+ @media (prefers-color-scheme: dark) { .rte-link-cancel { color: #aaa; border-color: #555; } }
106
+
107
+ /* Editor */
108
+ .rte-area { position: relative; background: #fff; }
109
+ @media (prefers-color-scheme: dark) { .rte-area { background: #141414; } }
67
110
  .rte-body {
68
111
  min-height: 280px; outline: none; font-size: 15px; line-height: 1.75;
69
112
  color: #222; caret-color: #222; padding: 18px 20px;
70
113
  }
71
- @media (prefers-color-scheme: dark) {
72
- .rte-body { color: #ddd; caret-color: #ddd; }
73
- }
114
+ @media (prefers-color-scheme: dark) { .rte-body { color: #ddd; caret-color: #ddd; } }
74
115
  .rte-body h1 { font-size: 26px; font-weight: 600; margin: 0.6em 0 0.2em; }
75
116
  .rte-body h2 { font-size: 20px; font-weight: 600; margin: 0.6em 0 0.2em; }
76
117
  .rte-body h3 { font-size: 16px; font-weight: 600; margin: 0.6em 0 0.2em; }
@@ -78,7 +119,6 @@ var CSS = `
78
119
  .rte-body ol { list-style-type: decimal; padding-left: 1.6em; margin: 0.3em 0; }
79
120
  .rte-body ol ol { list-style-type: lower-alpha; padding-left: 1.6em; }
80
121
  .rte-body ol ol ol { list-style-type: lower-roman; padding-left: 1.6em; }
81
- .rte-body ol ol ol ol { list-style-type: decimal; padding-left: 1.6em; }
82
122
  .rte-body ul { list-style-type: disc; padding-left: 1.6em; margin: 0.3em 0; }
83
123
  .rte-body ul ul { list-style-type: circle; padding-left: 1.6em; }
84
124
  .rte-body ul ul ul { list-style-type: square; padding-left: 1.6em; }
@@ -87,51 +127,232 @@ var CSS = `
87
127
  .rte-body pre { background: #f4f4f4; border: 1px solid #e0e0e0; border-radius: 4px; padding: 12px 14px; margin: 0.6em 0; overflow-x: auto; }
88
128
  .rte-body code { font-family: var(--font-mono); font-size: 13px; }
89
129
  .rte-body:empty:before { content: attr(data-ph); color: #aaa; pointer-events: none; }
90
-
91
- .rte-code {
92
- min-height: 280px; width: 100%; background: #1e1e2e;
93
- color: #cdd6f4; font-family: var(--font-mono); font-size: 13px;
94
- line-height: 1.6; padding: 18px 20px; border: none; outline: none;
95
- resize: vertical;
96
- }
97
- .rte-footer {
98
- font-size: 11px; color: #999; padding: 4px 20px 5px;
99
- border-top: 1px solid #e8e8e8; background: #fafafa;
100
- }
130
+ .rte-body table { border-collapse: collapse; width: 100%; margin: 0.8em 0; }
131
+ .rte-body td, .rte-body th { border: 1px solid #ccc; padding: 7px 10px; min-width: 60px; text-align: left; vertical-align: top; }
132
+ .rte-body th { background: #f5f5f5; font-weight: 600; }
101
133
  @media (prefers-color-scheme: dark) {
102
- .rte-footer { border-color: #2a2a2a; background: #1a1a1a; color: #555; }
134
+ .rte-body td, .rte-body th { border-color: #444; }
135
+ .rte-body th { background: #2a2a2a; }
103
136
  }
137
+ .rte-body td.rte-sel, .rte-body th.rte-sel { background: rgba(26,95,180,0.15) !important; outline: 2px solid #1a5fb4; outline-offset: -2px; }
138
+ .rte-code { min-height: 280px; width: 100%; background: #1e1e2e; color: #cdd6f4; font-family: var(--font-mono); font-size: 13px; line-height: 1.6; padding: 18px 20px; border: none; outline: none; resize: vertical; }
139
+ .rte-footer { font-size: 11px; color: #999; padding: 4px 20px 5px; border-top: 1px solid #e8e8e8; background: #fafafa; }
140
+ @media (prefers-color-scheme: dark) { .rte-footer { border-color: #2a2a2a; background: #1a1a1a; color: #555; } }
104
141
  `;
105
- var Btn = ({ onClick, title, active, children, style }) => /* @__PURE__ */ React.createElement(
106
- "button",
107
- {
108
- className: `rte-btn${active ? " active" : ""}`,
109
- style,
110
- onMouseDown: (e) => {
111
- e.preventDefault();
112
- onClick(e);
113
- },
114
- title
115
- },
116
- children
117
- );
118
- var Sep = () => /* @__PURE__ */ React.createElement("div", { className: "rte-sep" });
119
- var IcoUL = () => /* @__PURE__ */ React.createElement("svg", { width: "15", height: "15", viewBox: "0 0 15 15", fill: "none" }, /* @__PURE__ */ React.createElement("circle", { cx: "2", cy: "4", r: "1.3", fill: "currentColor" }), /* @__PURE__ */ React.createElement("circle", { cx: "2", cy: "8", r: "1.3", fill: "currentColor" }), /* @__PURE__ */ React.createElement("circle", { cx: "2", cy: "12", r: "1.3", fill: "currentColor" }), /* @__PURE__ */ React.createElement("rect", { x: "5.5", y: "3.2", width: "8.5", height: "1.6", rx: ".8", fill: "currentColor" }), /* @__PURE__ */ React.createElement("rect", { x: "5.5", y: "7.2", width: "8.5", height: "1.6", rx: ".8", fill: "currentColor" }), /* @__PURE__ */ React.createElement("rect", { x: "5.5", y: "11.2", width: "8.5", height: "1.6", rx: ".8", fill: "currentColor" }));
120
- var IcoOL = () => /* @__PURE__ */ React.createElement("svg", { width: "15", height: "15", viewBox: "0 0 15 15", fill: "none" }, /* @__PURE__ */ React.createElement("text", { x: "0", y: "5", fontSize: "5.5", fill: "currentColor", fontFamily: "monospace" }, "1."), /* @__PURE__ */ React.createElement("text", { x: "0", y: "9.5", fontSize: "5.5", fill: "currentColor", fontFamily: "monospace" }, "2."), /* @__PURE__ */ React.createElement("text", { x: "0", y: "14", fontSize: "5.5", fill: "currentColor", fontFamily: "monospace" }, "3."), /* @__PURE__ */ React.createElement("rect", { x: "5.5", y: "3.2", width: "8.5", height: "1.6", rx: ".8", fill: "currentColor" }), /* @__PURE__ */ React.createElement("rect", { x: "5.5", y: "7.2", width: "8.5", height: "1.6", rx: ".8", fill: "currentColor" }), /* @__PURE__ */ React.createElement("rect", { x: "5.5", y: "11.2", width: "8.5", height: "1.6", rx: ".8", fill: "currentColor" }));
121
- var IcoIndent = () => /* @__PURE__ */ React.createElement("svg", { width: "15", height: "15", viewBox: "0 0 15 15", fill: "none" }, /* @__PURE__ */ React.createElement("rect", { x: "1", y: "2", width: "13", height: "1.6", rx: ".8", fill: "currentColor" }), /* @__PURE__ */ React.createElement("rect", { x: "4.5", y: "6", width: "9.5", height: "1.6", rx: ".8", fill: "currentColor" }), /* @__PURE__ */ React.createElement("rect", { x: "4.5", y: "10", width: "9.5", height: "1.6", rx: ".8", fill: "currentColor" }), /* @__PURE__ */ React.createElement("path", { d: "M1 6.5L3.5 8.25L1 10V6.5Z", fill: "currentColor" }));
122
- var IcoOutdent = () => /* @__PURE__ */ React.createElement("svg", { width: "15", height: "15", viewBox: "0 0 15 15", fill: "none" }, /* @__PURE__ */ React.createElement("rect", { x: "1", y: "2", width: "13", height: "1.6", rx: ".8", fill: "currentColor" }), /* @__PURE__ */ React.createElement("rect", { x: "4.5", y: "6", width: "9.5", height: "1.6", rx: ".8", fill: "currentColor" }), /* @__PURE__ */ React.createElement("rect", { x: "4.5", y: "10", width: "9.5", height: "1.6", rx: ".8", fill: "currentColor" }), /* @__PURE__ */ React.createElement("path", { d: "M3.5 6.5L1 8.25L3.5 10V6.5Z", fill: "currentColor" }));
123
- var IcoLink = () => /* @__PURE__ */ React.createElement("svg", { width: "15", height: "15", viewBox: "0 0 15 15", fill: "none" }, /* @__PURE__ */ React.createElement("path", { d: "M6 9C6.4 9.6 7.1 10 7.8 10H9.8C10.6 10 11.4 9.6 11.9 9C12.4 8.4 12.6 7.6 12.6 6.8C12.6 6 12.4 5.3 11.9 4.7C11.4 4.1 10.6 3.8 9.8 3.8H8.6", stroke: "currentColor", strokeWidth: "1.3", strokeLinecap: "round" }), /* @__PURE__ */ React.createElement("path", { d: "M9 6C8.6 5.4 7.9 5 7.2 5H5.2C4.4 5 3.6 5.4 3.1 6C2.6 6.6 2.4 7.4 2.4 8.2C2.4 9 2.6 9.7 3.1 10.3C3.6 10.9 4.4 11.2 5.2 11.2H6.4", stroke: "currentColor", strokeWidth: "1.3", strokeLinecap: "round" }));
124
- var IcoCode = () => /* @__PURE__ */ React.createElement("svg", { width: "15", height: "15", viewBox: "0 0 15 15", fill: "none" }, /* @__PURE__ */ React.createElement("path", { d: "M5 4.5L1.5 7.5L5 10.5", stroke: "currentColor", strokeWidth: "1.4", strokeLinecap: "round", strokeLinejoin: "round" }), /* @__PURE__ */ React.createElement("path", { d: "M10 4.5L13.5 7.5L10 10.5", stroke: "currentColor", strokeWidth: "1.4", strokeLinecap: "round", strokeLinejoin: "round" }), /* @__PURE__ */ React.createElement("path", { d: "M8.5 2L6.5 13", stroke: "currentColor", strokeWidth: "1.4", strokeLinecap: "round" }));
125
- var IcoCopy = () => /* @__PURE__ */ React.createElement("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none" }, /* @__PURE__ */ React.createElement("rect", { x: "1", y: "3", width: "8.5", height: "10", rx: "1.5", stroke: "currentColor", strokeWidth: "1.2" }), /* @__PURE__ */ React.createElement("path", { d: "M4 1H12.5C13.1 1 13.5 1.4 13.5 2V10", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round" }));
126
- var AlignIco = ({ t }) => {
127
- const w = { left: [13, 9, 11], center: [9, 7, 11], right: [13, 9, 11], justify: [13, 13, 13] }[t];
128
- const x = { left: [1, 1, 1], center: [3, 4, 2], right: [1, 1, 1], justify: [1, 1, 1] }[t];
129
- const xr = { left: [0, 0, 0], center: [0, 0, 0], right: [1, 1, 1], justify: [0, 0, 0] }[t];
130
- return /* @__PURE__ */ React.createElement("svg", { width: "15", height: "15", viewBox: "0 0 15 15", fill: "none" }, [0, 1, 2].map((i) => /* @__PURE__ */ React.createElement("rect", { key: i, x: t === "right" ? 15 - w[i] - 1 : x[i], y: [2, 6.5, 11][i], width: w[i], height: "1.6", rx: ".8", fill: "currentColor" })));
131
- };
132
- function RichTextEditor() {
142
+ function Btn({ onClick, title, active, danger, children, style }) {
143
+ return /* @__PURE__ */ jsx(
144
+ "button",
145
+ {
146
+ className: `rte-btn${active ? " active" : ""}${danger ? " danger" : ""}`,
147
+ style,
148
+ onMouseDown: (e) => {
149
+ e.preventDefault();
150
+ onClick(e);
151
+ },
152
+ title,
153
+ children
154
+ }
155
+ );
156
+ }
157
+ function Sep() {
158
+ return /* @__PURE__ */ jsx("div", { className: "rte-sep" });
159
+ }
160
+ function TablePicker({ onInsert, onClose }) {
161
+ const [hover, setHover] = useState([0, 0]);
162
+ return /* @__PURE__ */ jsxs("div", { className: "rte-tp", children: [
163
+ /* @__PURE__ */ jsx("div", { className: "rte-tp-lbl", children: hover[0] > 0 ? `${hover[0]} \xD7 ${hover[1]}` : "Select table size" }),
164
+ /* @__PURE__ */ jsx("div", { className: "rte-tp-grid", children: Array.from({ length: 64 }, (_, i) => {
165
+ const r = Math.floor(i / 8) + 1, c = i % 8 + 1;
166
+ return /* @__PURE__ */ jsx(
167
+ "div",
168
+ {
169
+ className: `rte-tp-cell${r <= hover[0] && c <= hover[1] ? " on" : ""}`,
170
+ onMouseEnter: () => setHover([r, c]),
171
+ onMouseDown: (e) => {
172
+ e.preventDefault();
173
+ if (hover[0] > 0) {
174
+ onInsert(hover[0], hover[1]);
175
+ onClose();
176
+ }
177
+ }
178
+ },
179
+ i
180
+ );
181
+ }) })
182
+ ] });
183
+ }
184
+ function IcoUL() {
185
+ return /* @__PURE__ */ jsxs("svg", { width: "15", height: "15", viewBox: "0 0 15 15", fill: "none", children: [
186
+ /* @__PURE__ */ jsx("circle", { cx: "2", cy: "4", r: "1.3", fill: "currentColor" }),
187
+ /* @__PURE__ */ jsx("circle", { cx: "2", cy: "8", r: "1.3", fill: "currentColor" }),
188
+ /* @__PURE__ */ jsx("circle", { cx: "2", cy: "12", r: "1.3", fill: "currentColor" }),
189
+ /* @__PURE__ */ jsx("rect", { x: "5.5", y: "3.2", width: "8.5", height: "1.6", rx: ".8", fill: "currentColor" }),
190
+ /* @__PURE__ */ jsx("rect", { x: "5.5", y: "7.2", width: "8.5", height: "1.6", rx: ".8", fill: "currentColor" }),
191
+ /* @__PURE__ */ jsx("rect", { x: "5.5", y: "11.2", width: "8.5", height: "1.6", rx: ".8", fill: "currentColor" })
192
+ ] });
193
+ }
194
+ function IcoOL() {
195
+ return /* @__PURE__ */ jsxs("svg", { width: "15", height: "15", viewBox: "0 0 15 15", fill: "none", children: [
196
+ /* @__PURE__ */ jsx("text", { x: "0", y: "5", fontSize: "5.5", fill: "currentColor", fontFamily: "monospace", children: "1." }),
197
+ /* @__PURE__ */ jsx("text", { x: "0", y: "9.5", fontSize: "5.5", fill: "currentColor", fontFamily: "monospace", children: "2." }),
198
+ /* @__PURE__ */ jsx("text", { x: "0", y: "14", fontSize: "5.5", fill: "currentColor", fontFamily: "monospace", children: "3." }),
199
+ /* @__PURE__ */ jsx("rect", { x: "5.5", y: "3.2", width: "8.5", height: "1.6", rx: ".8", fill: "currentColor" }),
200
+ /* @__PURE__ */ jsx("rect", { x: "5.5", y: "7.2", width: "8.5", height: "1.6", rx: ".8", fill: "currentColor" }),
201
+ /* @__PURE__ */ jsx("rect", { x: "5.5", y: "11.2", width: "8.5", height: "1.6", rx: ".8", fill: "currentColor" })
202
+ ] });
203
+ }
204
+ function IcoIndent() {
205
+ return /* @__PURE__ */ jsxs("svg", { width: "15", height: "15", viewBox: "0 0 15 15", fill: "none", children: [
206
+ /* @__PURE__ */ jsx("rect", { x: "1", y: "2", width: "13", height: "1.6", rx: ".8", fill: "currentColor" }),
207
+ /* @__PURE__ */ jsx("rect", { x: "4.5", y: "6", width: "9.5", height: "1.6", rx: ".8", fill: "currentColor" }),
208
+ /* @__PURE__ */ jsx("rect", { x: "4.5", y: "10", width: "9.5", height: "1.6", rx: ".8", fill: "currentColor" }),
209
+ /* @__PURE__ */ jsx("path", { d: "M1 6.5L3.5 8.25L1 10V6.5Z", fill: "currentColor" })
210
+ ] });
211
+ }
212
+ function IcoOutdent() {
213
+ return /* @__PURE__ */ jsxs("svg", { width: "15", height: "15", viewBox: "0 0 15 15", fill: "none", children: [
214
+ /* @__PURE__ */ jsx("rect", { x: "1", y: "2", width: "13", height: "1.6", rx: ".8", fill: "currentColor" }),
215
+ /* @__PURE__ */ jsx("rect", { x: "4.5", y: "6", width: "9.5", height: "1.6", rx: ".8", fill: "currentColor" }),
216
+ /* @__PURE__ */ jsx("rect", { x: "4.5", y: "10", width: "9.5", height: "1.6", rx: ".8", fill: "currentColor" }),
217
+ /* @__PURE__ */ jsx("path", { d: "M3.5 6.5L1 8.25L3.5 10V6.5Z", fill: "currentColor" })
218
+ ] });
219
+ }
220
+ function IcoLink() {
221
+ return /* @__PURE__ */ jsxs("svg", { width: "15", height: "15", viewBox: "0 0 15 15", fill: "none", children: [
222
+ /* @__PURE__ */ jsx("path", { d: "M6 9C6.4 9.6 7.1 10 7.8 10H9.8C10.6 10 11.4 9.6 11.9 9C12.4 8.4 12.6 7.6 12.6 6.8C12.6 6 12.4 5.3 11.9 4.7C11.4 4.1 10.6 3.8 9.8 3.8H8.6", stroke: "currentColor", strokeWidth: "1.3", strokeLinecap: "round" }),
223
+ /* @__PURE__ */ jsx("path", { d: "M9 6C8.6 5.4 7.9 5 7.2 5H5.2C4.4 5 3.6 5.4 3.1 6C2.6 6.6 2.4 7.4 2.4 8.2C2.4 9 2.6 9.7 3.1 10.3C3.6 10.9 4.4 11.2 5.2 11.2H6.4", stroke: "currentColor", strokeWidth: "1.3", strokeLinecap: "round" })
224
+ ] });
225
+ }
226
+ function IcoCode() {
227
+ return /* @__PURE__ */ jsxs("svg", { width: "15", height: "15", viewBox: "0 0 15 15", fill: "none", children: [
228
+ /* @__PURE__ */ jsx("path", { d: "M5 4.5L1.5 7.5L5 10.5", stroke: "currentColor", strokeWidth: "1.4", strokeLinecap: "round", strokeLinejoin: "round" }),
229
+ /* @__PURE__ */ jsx("path", { d: "M10 4.5L13.5 7.5L10 10.5", stroke: "currentColor", strokeWidth: "1.4", strokeLinecap: "round", strokeLinejoin: "round" }),
230
+ /* @__PURE__ */ jsx("path", { d: "M8.5 2L6.5 13", stroke: "currentColor", strokeWidth: "1.4", strokeLinecap: "round" })
231
+ ] });
232
+ }
233
+ function IcoCopy() {
234
+ return /* @__PURE__ */ jsxs("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", children: [
235
+ /* @__PURE__ */ jsx("rect", { x: "1", y: "3", width: "8.5", height: "10", rx: "1.5", stroke: "currentColor", strokeWidth: "1.2" }),
236
+ /* @__PURE__ */ jsx("path", { d: "M4 1H12.5C13.1 1 13.5 1.4 13.5 2V10", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round" })
237
+ ] });
238
+ }
239
+ function IcoTable() {
240
+ return /* @__PURE__ */ jsxs("svg", { width: "15", height: "15", viewBox: "0 0 15 15", fill: "none", children: [
241
+ /* @__PURE__ */ jsx("rect", { x: "1", y: "1", width: "13", height: "13", rx: "1.5", stroke: "currentColor", strokeWidth: "1.2" }),
242
+ /* @__PURE__ */ jsx("line", { x1: "1", y1: "5.5", x2: "14", y2: "5.5", stroke: "currentColor", strokeWidth: "1.1" }),
243
+ /* @__PURE__ */ jsx("line", { x1: "1", y1: "10", x2: "14", y2: "10", stroke: "currentColor", strokeWidth: "1.1" }),
244
+ /* @__PURE__ */ jsx("line", { x1: "5.5", y1: "5.5", x2: "5.5", y2: "14", stroke: "currentColor", strokeWidth: "1.1" }),
245
+ /* @__PURE__ */ jsx("line", { x1: "10", y1: "5.5", x2: "10", y2: "14", stroke: "currentColor", strokeWidth: "1.1" })
246
+ ] });
247
+ }
248
+ function IcoRowAbove() {
249
+ return /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: [
250
+ /* @__PURE__ */ jsx("rect", { x: "1", y: "7", width: "14", height: "8", rx: "1", stroke: "currentColor", strokeWidth: "1.1" }),
251
+ /* @__PURE__ */ jsx("line", { x1: "8", y1: "7", x2: "8", y2: "15", stroke: "currentColor", strokeWidth: "1" }),
252
+ /* @__PURE__ */ jsx("path", { d: "M8 5V1M6 3L8 1L10 3", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round", strokeLinejoin: "round" })
253
+ ] });
254
+ }
255
+ function IcoRowBelow() {
256
+ return /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: [
257
+ /* @__PURE__ */ jsx("rect", { x: "1", y: "1", width: "14", height: "8", rx: "1", stroke: "currentColor", strokeWidth: "1.1" }),
258
+ /* @__PURE__ */ jsx("line", { x1: "8", y1: "1", x2: "8", y2: "9", stroke: "currentColor", strokeWidth: "1" }),
259
+ /* @__PURE__ */ jsx("path", { d: "M8 11V15M6 13L8 15L10 13", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round", strokeLinejoin: "round" })
260
+ ] });
261
+ }
262
+ function IcoDelRow() {
263
+ return /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: [
264
+ /* @__PURE__ */ jsx("rect", { x: "1", y: "4", width: "14", height: "8", rx: "1", stroke: "currentColor", strokeWidth: "1.1" }),
265
+ /* @__PURE__ */ jsx("line", { x1: "1", y1: "8", x2: "15", y2: "8", stroke: "currentColor", strokeWidth: "1" }),
266
+ /* @__PURE__ */ jsx("path", { d: "M5.5 6L10.5 10M10.5 6L5.5 10", stroke: "currentColor", strokeWidth: "1.3", strokeLinecap: "round" })
267
+ ] });
268
+ }
269
+ function IcoColLeft() {
270
+ return /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: [
271
+ /* @__PURE__ */ jsx("rect", { x: "7", y: "1", width: "8", height: "14", rx: "1", stroke: "currentColor", strokeWidth: "1.1" }),
272
+ /* @__PURE__ */ jsx("line", { x1: "7", y1: "8", x2: "15", y2: "8", stroke: "currentColor", strokeWidth: "1" }),
273
+ /* @__PURE__ */ jsx("path", { d: "M5 8H1M3 6L1 8L3 10", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round", strokeLinejoin: "round" })
274
+ ] });
275
+ }
276
+ function IcoColRight() {
277
+ return /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: [
278
+ /* @__PURE__ */ jsx("rect", { x: "1", y: "1", width: "8", height: "14", rx: "1", stroke: "currentColor", strokeWidth: "1.1" }),
279
+ /* @__PURE__ */ jsx("line", { x1: "1", y1: "8", x2: "9", y2: "8", stroke: "currentColor", strokeWidth: "1" }),
280
+ /* @__PURE__ */ jsx("path", { d: "M11 8H15M13 6L15 8L13 10", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round", strokeLinejoin: "round" })
281
+ ] });
282
+ }
283
+ function IcoDelCol() {
284
+ return /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: [
285
+ /* @__PURE__ */ jsx("rect", { x: "4", y: "1", width: "8", height: "14", rx: "1", stroke: "currentColor", strokeWidth: "1.1" }),
286
+ /* @__PURE__ */ jsx("line", { x1: "8", y1: "1", x2: "8", y2: "15", stroke: "currentColor", strokeWidth: "1" }),
287
+ /* @__PURE__ */ jsx("path", { d: "M6 5.5L10 10.5M10 5.5L6 10.5", stroke: "currentColor", strokeWidth: "1.3", strokeLinecap: "round" })
288
+ ] });
289
+ }
290
+ function IcoMerge() {
291
+ return /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: [
292
+ /* @__PURE__ */ jsx("rect", { x: "1", y: "1", width: "6", height: "14", rx: "1", stroke: "currentColor", strokeWidth: "1.1" }),
293
+ /* @__PURE__ */ jsx("rect", { x: "9", y: "1", width: "6", height: "14", rx: "1", stroke: "currentColor", strokeWidth: "1.1" }),
294
+ /* @__PURE__ */ jsx("path", { d: "M7 8H9M7.5 6.5L9 8L7.5 9.5M8.5 6.5L7 8L8.5 9.5", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round", strokeLinejoin: "round" })
295
+ ] });
296
+ }
297
+ function IcoSplit() {
298
+ return /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: [
299
+ /* @__PURE__ */ jsx("rect", { x: "1", y: "1", width: "14", height: "14", rx: "1", stroke: "currentColor", strokeWidth: "1.1" }),
300
+ /* @__PURE__ */ jsx("line", { x1: "8", y1: "1", x2: "8", y2: "15", stroke: "currentColor", strokeWidth: "1.2" }),
301
+ /* @__PURE__ */ jsx("path", { d: "M5.5 8H3M12.5 8H10", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round" }),
302
+ /* @__PURE__ */ jsx("path", { d: "M4.5 6.5L3 8L4.5 9.5M11.5 6.5L13 8L11.5 9.5", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round", strokeLinejoin: "round" })
303
+ ] });
304
+ }
305
+ function IcoDelTable() {
306
+ return /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: [
307
+ /* @__PURE__ */ jsx("rect", { x: "1", y: "1", width: "14", height: "14", rx: "1.5", stroke: "currentColor", strokeWidth: "1.1" }),
308
+ /* @__PURE__ */ jsx("path", { d: "M5 5L11 11M11 5L5 11", stroke: "currentColor", strokeWidth: "1.4", strokeLinecap: "round" })
309
+ ] });
310
+ }
311
+ function AlignIco({ t }) {
312
+ const w = { left: [13, 9, 11], center: [9, 7, 11], right: [13, 9, 11], justify: [13, 13, 13] };
313
+ return /* @__PURE__ */ jsx("svg", { width: "15", height: "15", viewBox: "0 0 15 15", fill: "none", children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx("rect", { x: t === "right" ? 15 - w[t][i] - 1 : 1, y: [2, 6.5, 11][i], width: w[t][i], height: "1.6", rx: ".8", fill: "currentColor" }, i)) });
314
+ }
315
+ function getCurrentCell() {
316
+ var _a;
317
+ const node = (_a = window.getSelection()) == null ? void 0 : _a.anchorNode;
318
+ const el = (node == null ? void 0 : node.nodeType) === 1 ? node : node == null ? void 0 : node.parentElement;
319
+ return el == null ? void 0 : el.closest("td, th");
320
+ }
321
+ function getCurrentTable() {
322
+ var _a;
323
+ return (_a = getCurrentCell()) == null ? void 0 : _a.closest("table");
324
+ }
325
+ function getCellCoords(table, cell) {
326
+ const rows = Array.from(table.rows);
327
+ for (let r = 0; r < rows.length; r++) {
328
+ const cells = Array.from(rows[r].cells);
329
+ for (let c = 0; c < cells.length; c++) {
330
+ if (cells[c] === cell) return [r, c];
331
+ }
332
+ }
333
+ return null;
334
+ }
335
+ function getCellsInRange(table, a, b) {
336
+ const ca = getCellCoords(table, a), cb = getCellCoords(table, b);
337
+ if (!ca || !cb) return [a];
338
+ const [r1, c1] = [Math.min(ca[0], cb[0]), Math.min(ca[1], cb[1])];
339
+ const [r2, c2] = [Math.max(ca[0], cb[0]), Math.max(ca[1], cb[1])];
340
+ const result = [];
341
+ Array.from(table.rows).forEach((row, ri) => {
342
+ if (ri >= r1 && ri <= r2) Array.from(row.cells).forEach((cell, ci) => {
343
+ if (ci >= c1 && ci <= c2) result.push(cell);
344
+ });
345
+ });
346
+ return result;
347
+ }
348
+ function RichTextEditor({ value, onChange }) {
349
+ var _a;
133
350
  const editorRef = useRef(null);
351
+ const editorAreaRef = useRef(null);
134
352
  const colorRef = useRef(null);
353
+ const savedRangeRef = useRef(null);
354
+ const dragStartCell = useRef(null);
355
+ const isDragging = useRef(false);
135
356
  const [isCode, setIsCode] = useState(false);
136
357
  const [codeVal, setCodeVal] = useState("");
137
358
  const [fmt, setFmt] = useState({ block: "p" });
@@ -139,22 +360,50 @@ function RichTextEditor() {
139
360
  const [words, setWords] = useState(0);
140
361
  const [linkBar, setLinkBar] = useState(false);
141
362
  const [linkUrl, setLinkUrl] = useState("https://");
142
- const savedRangeRef = useRef(null);
363
+ const [showTable, setShowTable] = useState(false);
364
+ const [selCells, setSelCells] = useState([]);
365
+ const [tableFP, setTableFP] = useState(null);
366
+ const [linkFP, setLinkFP] = useState(null);
367
+ const calcFloat = useCallback((el, toolbarW) => {
368
+ const area = editorAreaRef.current;
369
+ const er = el.getBoundingClientRect();
370
+ const ar = area.getBoundingClientRect();
371
+ const arrowCenter = er.left + er.width / 2 - ar.left;
372
+ const rawLeft = arrowCenter - toolbarW / 2;
373
+ const left = Math.max(4, Math.min(rawLeft, ar.width - toolbarW - 4));
374
+ const arrowLeft = Math.max(8, Math.min(arrowCenter - left - 7, toolbarW - 22));
375
+ return { top: er.bottom - ar.top + 10, left, arrowLeft };
376
+ }, []);
377
+ const applySelection = useCallback((cells) => {
378
+ var _a2;
379
+ (_a2 = editorRef.current) == null ? void 0 : _a2.querySelectorAll(".rte-sel").forEach((c) => c.classList.remove("rte-sel"));
380
+ cells.forEach((c) => c.classList.add("rte-sel"));
381
+ setSelCells(cells);
382
+ }, []);
383
+ const clearSelection = useCallback(() => {
384
+ var _a2;
385
+ (_a2 = editorRef.current) == null ? void 0 : _a2.querySelectorAll(".rte-sel").forEach((c) => c.classList.remove("rte-sel"));
386
+ setSelCells([]);
387
+ dragStartCell.current = null;
388
+ }, []);
143
389
  const exec = useCallback((cmd, val = null) => {
144
- var _a;
145
- (_a = editorRef.current) == null ? void 0 : _a.focus();
146
- document.execCommand(cmd, false, val);
390
+ var _a2;
391
+ (_a2 = editorRef.current) == null ? void 0 : _a2.focus();
392
+ document.execCommand(cmd, false, val != null ? val : void 0);
147
393
  refresh();
148
394
  }, []);
149
395
  const refresh = useCallback(() => {
150
- var _a;
396
+ var _a2, _b, _c, _d;
151
397
  const raw = document.queryCommandValue("formatBlock").toLowerCase().replace(/[<>]/g, "");
152
398
  const block = ["h1", "h2", "h3"].includes(raw) ? raw : "p";
153
399
  const c = document.queryCommandValue("foreColor");
154
400
  if (c && c !== "false") {
155
401
  const m = c.match(/\d+/g);
156
- if (m) setColor("#" + m.slice(0, 3).map((n) => (+n).toString(16).padStart(2, "0")).join(""));
402
+ if (m) setColor("#" + m.slice(0, 3).map((n) => parseInt(n).toString(16).padStart(2, "0")).join(""));
157
403
  }
404
+ const cell = getCurrentCell();
405
+ const inTable = !!cell;
406
+ const cellMerged = cell ? cell.colSpan > 1 || cell.rowSpan > 1 : false;
158
407
  setFmt({
159
408
  bold: document.queryCommandState("bold"),
160
409
  italic: document.queryCommandState("italic"),
@@ -165,16 +414,65 @@ function RichTextEditor() {
165
414
  aC: document.queryCommandState("justifyCenter"),
166
415
  aR: document.queryCommandState("justifyRight"),
167
416
  aJ: document.queryCommandState("justifyFull"),
168
- block
417
+ block,
418
+ inTable,
419
+ cellMerged
169
420
  });
170
- const txt = ((_a = editorRef.current) == null ? void 0 : _a.innerText) || "";
421
+ if (cell && editorAreaRef.current) setTableFP(calcFloat(cell, 370));
422
+ else setTableFP(null);
423
+ const txt = (_b = (_a2 = editorRef.current) == null ? void 0 : _a2.innerText) != null ? _b : "";
171
424
  setWords(txt.trim() ? txt.trim().split(/\s+/).length : 0);
425
+ onChange == null ? void 0 : onChange((_d = (_c = editorRef.current) == null ? void 0 : _c.innerHTML) != null ? _d : "");
426
+ }, [onChange, calcFloat]);
427
+ const handleEditorMouseDown = useCallback((e) => {
428
+ const target = e.target.closest("td, th");
429
+ if (!target) {
430
+ clearSelection();
431
+ return;
432
+ }
433
+ if (e.shiftKey && dragStartCell.current) {
434
+ const table = target.closest("table");
435
+ if (dragStartCell.current.closest("table") === table) {
436
+ e.preventDefault();
437
+ applySelection(getCellsInRange(table, dragStartCell.current, target));
438
+ return;
439
+ }
440
+ }
441
+ dragStartCell.current = target;
442
+ isDragging.current = true;
443
+ applySelection([target]);
444
+ }, [applySelection, clearSelection]);
445
+ const handleEditorMouseMove = useCallback((e) => {
446
+ if (!isDragging.current || !dragStartCell.current) return;
447
+ const target = e.target.closest("td, th");
448
+ if (!target || target === dragStartCell.current) return;
449
+ const table = dragStartCell.current.closest("table");
450
+ if (target.closest("table") !== table) return;
451
+ const cells = getCellsInRange(table, dragStartCell.current, target);
452
+ if (cells.length > 1) {
453
+ e.preventDefault();
454
+ applySelection(cells);
455
+ }
456
+ }, [applySelection]);
457
+ const handleEditorMouseUp = useCallback(() => {
458
+ isDragging.current = false;
172
459
  }, []);
173
460
  const handleKeyDown = useCallback((e) => {
174
- var _a, _b;
461
+ var _a2, _b;
462
+ clearSelection();
175
463
  if (e.key === "Tab") {
176
464
  e.preventDefault();
177
- const node = (_a = window.getSelection()) == null ? void 0 : _a.anchorNode;
465
+ const cell = getCurrentCell();
466
+ if (cell) {
467
+ const cells = Array.from(cell.closest("table").querySelectorAll("td, th"));
468
+ const next = e.shiftKey ? cells[cells.indexOf(cell) - 1] : cells[cells.indexOf(cell) + 1];
469
+ if (next) {
470
+ next.focus();
471
+ next.click();
472
+ }
473
+ return;
474
+ }
475
+ const node = (_a2 = window.getSelection()) == null ? void 0 : _a2.anchorNode;
178
476
  const li = (node == null ? void 0 : node.nodeType) === 1 ? node.closest("li") : (_b = node == null ? void 0 : node.parentElement) == null ? void 0 : _b.closest("li");
179
477
  if (li) exec(e.shiftKey ? "outdent" : "indent");
180
478
  else document.execCommand("insertText", false, "\xA0\xA0\xA0\xA0");
@@ -193,42 +491,194 @@ function RichTextEditor() {
193
491
  exec("underline");
194
492
  }
195
493
  }
196
- }, [exec]);
197
- const toCode = () => {
198
- var _a;
199
- setCodeVal(((_a = editorRef.current) == null ? void 0 : _a.innerHTML) || "");
200
- setIsCode(true);
201
- };
202
- const toVisual = () => {
203
- if (editorRef.current) editorRef.current.innerHTML = codeVal;
204
- setIsCode(false);
494
+ }, [exec, clearSelection]);
495
+ const insertTable = (rows, cols) => {
496
+ var _a2;
497
+ (_a2 = editorRef.current) == null ? void 0 : _a2.focus();
498
+ let html = `<table>`;
499
+ for (let r = 0; r < rows; r++) {
500
+ html += "<tr>";
501
+ for (let c = 0; c < cols; c++) html += r === 0 ? `<th><br></th>` : `<td><br></td>`;
502
+ html += "</tr>";
503
+ }
504
+ html += "</table><p><br></p>";
505
+ document.execCommand("insertHTML", false, html);
205
506
  refresh();
206
507
  };
508
+ const tableOp = useCallback((op) => {
509
+ const cell = getCurrentCell(), table = getCurrentTable();
510
+ if (!cell || !table) return;
511
+ const row = cell.closest("tr");
512
+ const rows = Array.from(table.rows);
513
+ const ri = rows.indexOf(row), ci = Array.from(row.cells).indexOf(cell);
514
+ if (op === "addRowAbove" || op === "addRowBelow") {
515
+ const nr = document.createElement("tr");
516
+ for (let i = 0; i < row.cells.length; i++) {
517
+ const td = document.createElement("td");
518
+ td.innerHTML = "<br>";
519
+ nr.appendChild(td);
520
+ }
521
+ op === "addRowBelow" ? row.insertAdjacentElement("afterend", nr) : row.insertAdjacentElement("beforebegin", nr);
522
+ }
523
+ if (op === "deleteRow") {
524
+ rows.length > 1 ? row.remove() : table.remove();
525
+ }
526
+ if (op === "addColLeft" || op === "addColRight") {
527
+ rows.forEach((r, rIdx) => {
528
+ const td = document.createElement(rIdx === 0 ? "th" : "td");
529
+ td.innerHTML = "<br>";
530
+ const ref = r.cells[op === "addColRight" ? ci + 1 : ci];
531
+ ref ? r.insertBefore(td, ref) : r.appendChild(td);
532
+ });
533
+ }
534
+ if (op === "deleteCol") {
535
+ row.cells.length > 1 ? rows.forEach((r) => {
536
+ var _a2;
537
+ return (_a2 = r.cells[ci]) == null ? void 0 : _a2.remove();
538
+ }) : table.remove();
539
+ }
540
+ if (op === "deleteTable") {
541
+ table.remove();
542
+ clearSelection();
543
+ }
544
+ refresh();
545
+ }, [clearSelection, refresh]);
546
+ const doMerge = useCallback((cells) => {
547
+ var _a2;
548
+ if (cells.length < 2) return;
549
+ const table = cells[0].closest("table");
550
+ const coords = cells.map((c) => getCellCoords(table, c)).filter((x) => x !== null);
551
+ const minR = Math.min(...coords.map((c) => c[0])), maxR = Math.max(...coords.map((c) => c[0]));
552
+ const minC = Math.min(...coords.map((c) => c[1])), maxC = Math.max(...coords.map((c) => c[1]));
553
+ const anchor = (_a2 = cells.find((c) => {
554
+ const co = getCellCoords(table, c);
555
+ return co && co[0] === minR && co[1] === minC;
556
+ })) != null ? _a2 : cells[0];
557
+ anchor.innerHTML = cells.map((c) => c.innerHTML.replace(/<br\s*\/?>/gi, "").trim()).filter(Boolean).join(" ") || "<br>";
558
+ anchor.colSpan = maxC - minC + 1;
559
+ anchor.rowSpan = maxR - minR + 1;
560
+ cells.filter((c) => c !== anchor).forEach((c) => c.remove());
561
+ clearSelection();
562
+ refresh();
563
+ }, [clearSelection, refresh]);
564
+ const doSplit = useCallback(() => {
565
+ const cell = getCurrentCell();
566
+ if (!cell || cell.colSpan === 1 && cell.rowSpan === 1) return;
567
+ const cs = cell.colSpan, rs = cell.rowSpan;
568
+ const table = cell.closest("table");
569
+ const rows = Array.from(table.rows), row = cell.closest("tr");
570
+ const ri = rows.indexOf(row), ci = Array.from(row.cells).indexOf(cell);
571
+ cell.colSpan = 1;
572
+ cell.rowSpan = 1;
573
+ for (let c = 1; c < cs; c++) {
574
+ const td = document.createElement(ri === 0 ? "th" : "td");
575
+ td.innerHTML = "<br>";
576
+ const ref = row.cells[ci + c];
577
+ ref ? row.insertBefore(td, ref) : row.appendChild(td);
578
+ }
579
+ for (let r = 1; r < rs; r++) {
580
+ const tr = rows[ri + r];
581
+ if (!tr) continue;
582
+ for (let c = 0; c < cs; c++) {
583
+ const td = document.createElement("td");
584
+ td.innerHTML = "<br>";
585
+ const ref = tr.cells[ci + c];
586
+ ref ? tr.insertBefore(td, ref) : tr.appendChild(td);
587
+ }
588
+ }
589
+ refresh();
590
+ }, [refresh]);
207
591
  const openLinkBar = () => {
208
592
  const sel = window.getSelection();
209
593
  savedRangeRef.current = (sel == null ? void 0 : sel.rangeCount) ? sel.getRangeAt(0).cloneRange() : null;
210
- setLinkUrl((sel == null ? void 0 : sel.toString()) ? "https://" : "https://");
594
+ if ((sel == null ? void 0 : sel.rangeCount) && editorAreaRef.current) {
595
+ const rect = sel.getRangeAt(0).getBoundingClientRect();
596
+ if (rect.width > 0 || rect.height > 0) setLinkFP(calcFloat({ getBoundingClientRect: () => rect }, 360));
597
+ else {
598
+ const area = editorAreaRef.current.getBoundingClientRect();
599
+ setLinkFP({ top: 60, left: 20, arrowLeft: 20 });
600
+ }
601
+ }
602
+ setLinkUrl("https://");
211
603
  setLinkBar(true);
212
604
  };
213
605
  const applyLink = () => {
214
- var _a, _b, _c;
606
+ var _a2, _b;
215
607
  if (!linkUrl) return;
216
- (_a = editorRef.current) == null ? void 0 : _a.focus();
608
+ (_a2 = editorRef.current) == null ? void 0 : _a2.focus();
217
609
  const sel = window.getSelection();
218
- if (savedRangeRef.current) {
610
+ if (savedRangeRef.current && sel) {
219
611
  sel.removeAllRanges();
220
612
  sel.addRange(savedRangeRef.current);
221
613
  }
222
- if ((_b = savedRangeRef.current) == null ? void 0 : _b.toString()) {
223
- document.execCommand("createLink", false, linkUrl);
224
- } else {
614
+ if ((_b = savedRangeRef.current) == null ? void 0 : _b.toString()) document.execCommand("createLink", false, linkUrl);
615
+ else if (savedRangeRef.current) {
225
616
  const a = document.createElement("a");
226
617
  a.href = linkUrl;
227
618
  a.textContent = linkUrl;
228
- (_c = savedRangeRef.current) == null ? void 0 : _c.insertNode(a);
619
+ savedRangeRef.current.insertNode(a);
229
620
  }
230
621
  setLinkBar(false);
231
622
  setLinkUrl("https://");
623
+ setLinkFP(null);
624
+ refresh();
625
+ };
626
+ const prettifyHtml = (html) => {
627
+ const INLINE = /* @__PURE__ */ new Set(["a", "b", "i", "u", "em", "strong", "span", "code", "br", "small", "sub", "sup"]);
628
+ let indent = 0;
629
+ const pad = () => " ".repeat(indent);
630
+ const tokens = html.replace(/>\s+</g, "><").replace(/(<\/?[^>]+>)/g, "\0$1\0").split("\0").filter(Boolean);
631
+ const lines = [];
632
+ let inline = "";
633
+ const flush = () => {
634
+ if (inline.trim()) {
635
+ lines.push(pad() + inline.trim());
636
+ inline = "";
637
+ }
638
+ };
639
+ for (const tok of tokens) {
640
+ const ot = tok.match(/^<([a-zA-Z][a-zA-Z0-9]*)[^>]*>$/), ct = tok.match(/^<\/([a-zA-Z][a-zA-Z0-9]*)>$/);
641
+ if (tok.match(/^<[^>]+\/>$/)) {
642
+ flush();
643
+ lines.push(pad() + tok);
644
+ continue;
645
+ }
646
+ if (ot) {
647
+ const tag = ot[1].toLowerCase();
648
+ if (INLINE.has(tag)) {
649
+ inline += tok;
650
+ continue;
651
+ }
652
+ flush();
653
+ lines.push(pad() + tok);
654
+ indent++;
655
+ continue;
656
+ }
657
+ if (ct) {
658
+ const tag = ct[1].toLowerCase();
659
+ if (INLINE.has(tag)) {
660
+ inline += tok;
661
+ continue;
662
+ }
663
+ flush();
664
+ indent = Math.max(0, indent - 1);
665
+ const prev = lines[lines.length - 1];
666
+ prev && prev.trim().startsWith("<") && !prev.includes("</") ? lines[lines.length - 1] = prev + tok : lines.push(pad() + tok);
667
+ continue;
668
+ }
669
+ inline += tok;
670
+ }
671
+ flush();
672
+ return lines.join("\n");
673
+ };
674
+ const toCode = () => {
675
+ var _a2, _b;
676
+ setCodeVal(prettifyHtml((_b = (_a2 = editorRef.current) == null ? void 0 : _a2.innerHTML) != null ? _b : ""));
677
+ setIsCode(true);
678
+ };
679
+ const toVisual = () => {
680
+ if (editorRef.current) editorRef.current.innerHTML = codeVal;
681
+ setIsCode(false);
232
682
  refresh();
233
683
  };
234
684
  const insertCodeBlock = () => {
@@ -250,112 +700,197 @@ function RichTextEditor() {
250
700
  refresh();
251
701
  };
252
702
  const copyHtml = () => {
253
- var _a;
254
- return navigator.clipboard.writeText(isCode ? codeVal : ((_a = editorRef.current) == null ? void 0 : _a.innerHTML) || "").catch(() => {
703
+ var _a2, _b;
704
+ navigator.clipboard.writeText(isCode ? codeVal : (_b = (_a2 = editorRef.current) == null ? void 0 : _a2.innerHTML) != null ? _b : "").catch(() => {
255
705
  });
256
706
  };
257
707
  useEffect(() => {
258
708
  if (editorRef.current) {
259
- editorRef.current.innerHTML = "<p>Start writing here...</p>";
709
+ editorRef.current.innerHTML = value != null ? value : "<p>Start writing here...</p>";
260
710
  refresh();
261
711
  }
262
712
  }, []);
263
- return /* @__PURE__ */ React.createElement("div", { style: { padding: "1rem 0" } }, /* @__PURE__ */ React.createElement("style", null, CSS), /* @__PURE__ */ React.createElement("div", { style: { border: "1px solid #d8d8d8", borderRadius: 4, overflow: "hidden", boxShadow: "0 1px 3px rgba(0,0,0,0.06)" } }, /* @__PURE__ */ React.createElement("div", { className: "rte-toolbar" }, !isCode && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Btn, { onClick: () => exec("bold"), title: "Bold (Ctrl+B)", active: fmt.bold, style: { fontWeight: 800, fontFamily: "Georgia,serif" } }, "B"), /* @__PURE__ */ React.createElement(Btn, { onClick: () => exec("italic"), title: "Italic (Ctrl+I)", active: fmt.italic, style: { fontStyle: "italic", fontFamily: "Georgia,serif" } }, "I"), /* @__PURE__ */ React.createElement(Btn, { onClick: () => exec("underline"), title: "Underline (Ctrl+U)", active: fmt.underline, style: { textDecoration: "underline" } }, "U"), /* @__PURE__ */ React.createElement(Btn, { onClick: () => {
264
- var _a;
265
- return (_a = colorRef.current) == null ? void 0 : _a.click();
266
- }, title: "Text color", style: { flexDirection: "column", gap: 1, padding: "3px 6px" } }, /* @__PURE__ */ React.createElement("span", { style: { fontSize: 13, fontWeight: 800, fontFamily: "Georgia,serif", lineHeight: 1, color: "#222" } }, "A"), /* @__PURE__ */ React.createElement("span", { className: "rte-swatch", style: { background: color } })), /* @__PURE__ */ React.createElement(
267
- "input",
268
- {
269
- ref: colorRef,
270
- type: "color",
271
- value: color,
272
- onChange: (e) => {
273
- setColor(e.target.value);
274
- exec("foreColor", e.target.value);
275
- },
276
- style: { position: "absolute", opacity: 0, width: 0, height: 0, pointerEvents: "none" }
277
- }
278
- ), /* @__PURE__ */ React.createElement(Sep, null), /* @__PURE__ */ React.createElement(
279
- "select",
280
- {
281
- className: "rte-select",
282
- value: fmt.block || "p",
283
- onMouseDown: () => {
284
- var _a;
285
- return (_a = editorRef.current) == null ? void 0 : _a.focus();
286
- },
287
- onChange: (e) => {
288
- var _a;
289
- (_a = editorRef.current) == null ? void 0 : _a.focus();
290
- document.execCommand("formatBlock", false, e.target.value);
291
- refresh();
292
- }
293
- },
294
- /* @__PURE__ */ React.createElement("option", { value: "p" }, "Paragraph"),
295
- /* @__PURE__ */ React.createElement("option", { value: "h1" }, "Heading 1"),
296
- /* @__PURE__ */ React.createElement("option", { value: "h2" }, "Heading 2"),
297
- /* @__PURE__ */ React.createElement("option", { value: "h3" }, "Heading 3")
298
- ), /* @__PURE__ */ React.createElement(Sep, null), /* @__PURE__ */ React.createElement(Btn, { onClick: () => exec("justifyLeft"), title: "Align left", active: fmt.aL }, /* @__PURE__ */ React.createElement(AlignIco, { t: "left" })), /* @__PURE__ */ React.createElement(Btn, { onClick: () => exec("justifyCenter"), title: "Align center", active: fmt.aC }, /* @__PURE__ */ React.createElement(AlignIco, { t: "center" })), /* @__PURE__ */ React.createElement(Btn, { onClick: () => exec("justifyRight"), title: "Align right", active: fmt.aR }, /* @__PURE__ */ React.createElement(AlignIco, { t: "right" })), /* @__PURE__ */ React.createElement(Btn, { onClick: () => exec("justifyFull"), title: "Justify", active: fmt.aJ }, /* @__PURE__ */ React.createElement(AlignIco, { t: "justify" })), /* @__PURE__ */ React.createElement(Sep, null), /* @__PURE__ */ React.createElement(Btn, { onClick: () => exec("insertUnorderedList"), title: "Bullet list", active: fmt.ul }, /* @__PURE__ */ React.createElement(IcoUL, null)), /* @__PURE__ */ React.createElement(Btn, { onClick: () => exec("insertOrderedList"), title: "Numbered list", active: fmt.ol }, /* @__PURE__ */ React.createElement(IcoOL, null)), /* @__PURE__ */ React.createElement(Btn, { onClick: () => exec("indent"), title: "Indent (Tab)" }, /* @__PURE__ */ React.createElement(IcoIndent, null)), /* @__PURE__ */ React.createElement(Btn, { onClick: () => exec("outdent"), title: "Outdent (Shift+Tab)" }, /* @__PURE__ */ React.createElement(IcoOutdent, null)), /* @__PURE__ */ React.createElement(Sep, null), /* @__PURE__ */ React.createElement(Btn, { onClick: openLinkBar, title: "Insert link", active: linkBar }, /* @__PURE__ */ React.createElement(IcoLink, null)), /* @__PURE__ */ React.createElement(Btn, { onClick: insertCodeBlock, title: "Code block" }, /* @__PURE__ */ React.createElement(IcoCode, null)), /* @__PURE__ */ React.createElement(Sep, null)), /* @__PURE__ */ React.createElement(
299
- Btn,
300
- {
301
- onClick: isCode ? toVisual : toCode,
302
- title: "Toggle HTML source",
303
- active: isCode,
304
- style: { fontSize: 12, fontWeight: 600, letterSpacing: "0.03em", padding: "0 8px" }
305
- },
306
- isCode ? "Visual" : "HTML"
307
- ), /* @__PURE__ */ React.createElement(Btn, { onClick: copyHtml, title: "Copy HTML" }, /* @__PURE__ */ React.createElement(IcoCopy, null)), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }), /* @__PURE__ */ React.createElement("span", { style: { fontSize: 12, color: "#aaa" } }, words, " ", words === 1 ? "word" : "words")), linkBar && /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 6, padding: "6px 10px", background: "#f0f4ff", borderBottom: "1px solid #c8d8f8" } }, /* @__PURE__ */ React.createElement(IcoLink, null), /* @__PURE__ */ React.createElement(
308
- "input",
309
- {
310
- autoFocus: true,
311
- type: "text",
312
- value: linkUrl,
313
- onChange: (e) => setLinkUrl(e.target.value),
314
- onKeyDown: (e) => {
315
- if (e.key === "Enter") applyLink();
316
- if (e.key === "Escape") setLinkBar(false);
317
- },
318
- placeholder: "https://example.com",
319
- style: { flex: 1, height: 26, border: "1px solid #b0c4f0", borderRadius: 3, padding: "0 8px", fontSize: 13, outline: "none", fontFamily: "var(--font-sans)" }
320
- }
321
- ), /* @__PURE__ */ React.createElement(
322
- "button",
323
- {
324
- onClick: applyLink,
325
- style: { height: 26, padding: "0 12px", background: "#1a6fc4", color: "#fff", border: "none", borderRadius: 3, fontSize: 12, fontWeight: 600, cursor: "pointer" }
326
- },
327
- "Apply"
328
- ), /* @__PURE__ */ React.createElement(
329
- "button",
330
- {
331
- onClick: () => setLinkBar(false),
332
- style: { height: 26, padding: "0 10px", background: "transparent", color: "#666", border: "1px solid #ccc", borderRadius: 3, fontSize: 12, cursor: "pointer" }
333
- },
334
- "Cancel"
335
- )), /* @__PURE__ */ React.createElement("div", { className: "rte-wrap" }, /* @__PURE__ */ React.createElement(
336
- "textarea",
337
- {
338
- className: "rte-code",
339
- value: codeVal,
340
- onChange: (e) => setCodeVal(e.target.value),
341
- spellCheck: false,
342
- style: { display: isCode ? "block" : "none" }
343
- }
344
- ), /* @__PURE__ */ React.createElement(
345
- "div",
346
- {
347
- ref: editorRef,
348
- className: "rte-body",
349
- contentEditable: true,
350
- suppressContentEditableWarning: true,
351
- "data-ph": "Start typing...",
352
- onKeyDown: handleKeyDown,
353
- onKeyUp: refresh,
354
- onMouseUp: refresh,
355
- onSelect: refresh,
356
- style: { display: isCode ? "none" : "block" }
357
- }
358
- )), !isCode && /* @__PURE__ */ React.createElement("div", { className: "rte-footer" }, "Inside a list: ", /* @__PURE__ */ React.createElement("strong", null, "Tab"), " to go deeper \xA0\xB7\xA0 ", /* @__PURE__ */ React.createElement("strong", null, "Shift+Tab"), " to go back up")));
713
+ useEffect(() => {
714
+ if (!showTable) return;
715
+ const h = () => setShowTable(false);
716
+ document.addEventListener("mousedown", h);
717
+ return () => document.removeEventListener("mousedown", h);
718
+ }, [showTable]);
719
+ const canMerge = selCells.length >= 2;
720
+ const canSplit = fmt.cellMerged;
721
+ return /* @__PURE__ */ jsxs("div", { style: { padding: "1rem 0" }, children: [
722
+ /* @__PURE__ */ jsx("style", { children: CSS }),
723
+ /* @__PURE__ */ jsxs("div", { style: { border: "1px solid #d8d8d8", borderRadius: 4, boxShadow: "0 1px 3px rgba(0,0,0,0.06)" }, children: [
724
+ /* @__PURE__ */ jsxs("div", { className: "rte-toolbar", children: [
725
+ !isCode && /* @__PURE__ */ jsxs(Fragment, { children: [
726
+ /* @__PURE__ */ jsx(Btn, { onClick: () => exec("bold"), title: "Bold (Ctrl+B)", active: fmt.bold, style: { fontWeight: 800, fontFamily: "Georgia,serif" }, children: "B" }),
727
+ /* @__PURE__ */ jsx(Btn, { onClick: () => exec("italic"), title: "Italic (Ctrl+I)", active: fmt.italic, style: { fontStyle: "italic", fontFamily: "Georgia,serif" }, children: "I" }),
728
+ /* @__PURE__ */ jsx(Btn, { onClick: () => exec("underline"), title: "Underline (Ctrl+U)", active: fmt.underline, style: { textDecoration: "underline" }, children: "U" }),
729
+ /* @__PURE__ */ jsxs(Btn, { onClick: () => {
730
+ var _a2;
731
+ return (_a2 = colorRef.current) == null ? void 0 : _a2.click();
732
+ }, title: "Text color", style: { flexDirection: "column", gap: 1, padding: "3px 6px" }, children: [
733
+ /* @__PURE__ */ jsx("span", { style: { fontSize: 13, fontWeight: 800, fontFamily: "Georgia,serif", lineHeight: 1, color: "#222" }, children: "A" }),
734
+ /* @__PURE__ */ jsx("span", { className: "rte-swatch", style: { background: color } })
735
+ ] }),
736
+ /* @__PURE__ */ jsx(
737
+ "input",
738
+ {
739
+ ref: colorRef,
740
+ type: "color",
741
+ value: color,
742
+ onChange: (e) => {
743
+ setColor(e.target.value);
744
+ exec("foreColor", e.target.value);
745
+ },
746
+ style: { position: "absolute", opacity: 0, width: 0, height: 0, pointerEvents: "none" }
747
+ }
748
+ ),
749
+ /* @__PURE__ */ jsx(Sep, {}),
750
+ /* @__PURE__ */ jsxs(
751
+ "select",
752
+ {
753
+ className: "rte-select",
754
+ value: (_a = fmt.block) != null ? _a : "p",
755
+ onMouseDown: () => {
756
+ var _a2;
757
+ return (_a2 = editorRef.current) == null ? void 0 : _a2.focus();
758
+ },
759
+ onChange: (e) => {
760
+ var _a2;
761
+ (_a2 = editorRef.current) == null ? void 0 : _a2.focus();
762
+ document.execCommand("formatBlock", false, e.target.value);
763
+ refresh();
764
+ },
765
+ children: [
766
+ /* @__PURE__ */ jsx("option", { value: "p", children: "Paragraph" }),
767
+ /* @__PURE__ */ jsx("option", { value: "h1", children: "Heading 1" }),
768
+ /* @__PURE__ */ jsx("option", { value: "h2", children: "Heading 2" }),
769
+ /* @__PURE__ */ jsx("option", { value: "h3", children: "Heading 3" })
770
+ ]
771
+ }
772
+ ),
773
+ /* @__PURE__ */ jsx(Sep, {}),
774
+ /* @__PURE__ */ jsx(Btn, { onClick: () => exec("justifyLeft"), title: "Align left", active: fmt.aL, children: /* @__PURE__ */ jsx(AlignIco, { t: "left" }) }),
775
+ /* @__PURE__ */ jsx(Btn, { onClick: () => exec("justifyCenter"), title: "Align center", active: fmt.aC, children: /* @__PURE__ */ jsx(AlignIco, { t: "center" }) }),
776
+ /* @__PURE__ */ jsx(Btn, { onClick: () => exec("justifyRight"), title: "Align right", active: fmt.aR, children: /* @__PURE__ */ jsx(AlignIco, { t: "right" }) }),
777
+ /* @__PURE__ */ jsx(Btn, { onClick: () => exec("justifyFull"), title: "Justify", active: fmt.aJ, children: /* @__PURE__ */ jsx(AlignIco, { t: "justify" }) }),
778
+ /* @__PURE__ */ jsx(Sep, {}),
779
+ /* @__PURE__ */ jsx(Btn, { onClick: () => exec("insertUnorderedList"), title: "Bullet list", active: fmt.ul, children: /* @__PURE__ */ jsx(IcoUL, {}) }),
780
+ /* @__PURE__ */ jsx(Btn, { onClick: () => exec("insertOrderedList"), title: "Numbered list", active: fmt.ol, children: /* @__PURE__ */ jsx(IcoOL, {}) }),
781
+ /* @__PURE__ */ jsx(Btn, { onClick: () => exec("indent"), title: "Indent (Tab)", children: /* @__PURE__ */ jsx(IcoIndent, {}) }),
782
+ /* @__PURE__ */ jsx(Btn, { onClick: () => exec("outdent"), title: "Outdent (Shift+Tab)", children: /* @__PURE__ */ jsx(IcoOutdent, {}) }),
783
+ /* @__PURE__ */ jsx(Sep, {}),
784
+ /* @__PURE__ */ jsx(Btn, { onClick: openLinkBar, title: "Insert link", active: linkBar, children: /* @__PURE__ */ jsx(IcoLink, {}) }),
785
+ /* @__PURE__ */ jsx(Btn, { onClick: insertCodeBlock, title: "Code block", children: /* @__PURE__ */ jsx(IcoCode, {}) }),
786
+ /* @__PURE__ */ jsxs("div", { className: "rte-tp-wrap", onMouseDown: (e) => e.stopPropagation(), children: [
787
+ /* @__PURE__ */ jsx(Btn, { onClick: () => setShowTable((v) => !v), title: "Insert table", active: showTable || !!fmt.inTable, children: /* @__PURE__ */ jsx(IcoTable, {}) }),
788
+ showTable && /* @__PURE__ */ jsx(TablePicker, { onInsert: (r, c) => {
789
+ insertTable(r, c);
790
+ setShowTable(false);
791
+ }, onClose: () => setShowTable(false) })
792
+ ] }),
793
+ /* @__PURE__ */ jsx(Sep, {})
794
+ ] }),
795
+ /* @__PURE__ */ jsx(
796
+ Btn,
797
+ {
798
+ onClick: isCode ? toVisual : toCode,
799
+ title: "Toggle HTML",
800
+ active: isCode,
801
+ style: { fontSize: 12, fontWeight: 600, letterSpacing: "0.03em", padding: "0 8px" },
802
+ children: isCode ? "Visual" : "HTML"
803
+ }
804
+ ),
805
+ /* @__PURE__ */ jsx(Btn, { onClick: copyHtml, title: "Copy HTML", children: /* @__PURE__ */ jsx(IcoCopy, {}) }),
806
+ /* @__PURE__ */ jsx("div", { style: { flex: 1 } }),
807
+ /* @__PURE__ */ jsxs("span", { style: { fontSize: 12, color: "#aaa" }, children: [
808
+ words,
809
+ " ",
810
+ words === 1 ? "word" : "words"
811
+ ] })
812
+ ] }),
813
+ /* @__PURE__ */ jsxs("div", { className: "rte-area", ref: editorAreaRef, children: [
814
+ /* @__PURE__ */ jsx(
815
+ "textarea",
816
+ {
817
+ className: "rte-code",
818
+ value: codeVal,
819
+ onChange: (e) => setCodeVal(e.target.value),
820
+ spellCheck: false,
821
+ style: { display: isCode ? "block" : "none" }
822
+ }
823
+ ),
824
+ /* @__PURE__ */ jsx(
825
+ "div",
826
+ {
827
+ ref: editorRef,
828
+ className: "rte-body",
829
+ contentEditable: true,
830
+ suppressContentEditableWarning: true,
831
+ "data-ph": "Start typing...",
832
+ onMouseDown: handleEditorMouseDown,
833
+ onMouseMove: handleEditorMouseMove,
834
+ onMouseUp: handleEditorMouseUp,
835
+ onKeyDown: handleKeyDown,
836
+ onKeyUp: refresh,
837
+ onSelect: refresh,
838
+ style: { display: isCode ? "none" : "block" }
839
+ }
840
+ ),
841
+ !isCode && fmt.inTable && tableFP && /* @__PURE__ */ jsxs("div", { className: "rte-float", style: { top: tableFP.top, left: tableFP.left }, children: [
842
+ /* @__PURE__ */ jsx("div", { className: "rte-float-arrow", style: { left: tableFP.arrowLeft } }),
843
+ /* @__PURE__ */ jsx(Btn, { onClick: () => tableOp("addRowAbove"), title: "Add row above", children: /* @__PURE__ */ jsx(IcoRowAbove, {}) }),
844
+ /* @__PURE__ */ jsx(Btn, { onClick: () => tableOp("addRowBelow"), title: "Add row below", children: /* @__PURE__ */ jsx(IcoRowBelow, {}) }),
845
+ /* @__PURE__ */ jsx(Btn, { onClick: () => tableOp("deleteRow"), title: "Delete row", danger: true, children: /* @__PURE__ */ jsx(IcoDelRow, {}) }),
846
+ /* @__PURE__ */ jsx(Sep, {}),
847
+ /* @__PURE__ */ jsx(Btn, { onClick: () => tableOp("addColLeft"), title: "Add column left", children: /* @__PURE__ */ jsx(IcoColLeft, {}) }),
848
+ /* @__PURE__ */ jsx(Btn, { onClick: () => tableOp("addColRight"), title: "Add column right", children: /* @__PURE__ */ jsx(IcoColRight, {}) }),
849
+ /* @__PURE__ */ jsx(Btn, { onClick: () => tableOp("deleteCol"), title: "Delete column", danger: true, children: /* @__PURE__ */ jsx(IcoDelCol, {}) }),
850
+ /* @__PURE__ */ jsx(Sep, {}),
851
+ canMerge && /* @__PURE__ */ jsx(Btn, { onClick: () => doMerge(selCells), title: `Merge ${selCells.length} cells`, children: /* @__PURE__ */ jsx(IcoMerge, {}) }),
852
+ canSplit && /* @__PURE__ */ jsx(Btn, { onClick: doSplit, title: "Split merged cell", children: /* @__PURE__ */ jsx(IcoSplit, {}) }),
853
+ (canMerge || canSplit) && /* @__PURE__ */ jsx(Sep, {}),
854
+ /* @__PURE__ */ jsx(Btn, { onClick: () => tableOp("deleteTable"), title: "Delete table", danger: true, children: /* @__PURE__ */ jsx(IcoDelTable, {}) })
855
+ ] }),
856
+ !isCode && linkBar && linkFP && /* @__PURE__ */ jsxs("div", { className: "rte-float rte-link-float", style: { top: linkFP.top, left: linkFP.left }, children: [
857
+ /* @__PURE__ */ jsx("div", { className: "rte-float-arrow", style: { left: linkFP.arrowLeft } }),
858
+ /* @__PURE__ */ jsx(IcoLink, {}),
859
+ /* @__PURE__ */ jsx(
860
+ "input",
861
+ {
862
+ autoFocus: true,
863
+ type: "text",
864
+ value: linkUrl,
865
+ onChange: (e) => setLinkUrl(e.target.value),
866
+ onKeyDown: (e) => {
867
+ if (e.key === "Enter") applyLink();
868
+ if (e.key === "Escape") {
869
+ setLinkBar(false);
870
+ setLinkFP(null);
871
+ }
872
+ },
873
+ placeholder: "https://example.com"
874
+ }
875
+ ),
876
+ /* @__PURE__ */ jsx("button", { className: "rte-link-apply", onClick: applyLink, children: "Apply" }),
877
+ /* @__PURE__ */ jsx("button", { className: "rte-link-cancel", onClick: () => {
878
+ setLinkBar(false);
879
+ setLinkFP(null);
880
+ }, children: "\u2715" })
881
+ ] })
882
+ ] }),
883
+ !isCode && /* @__PURE__ */ jsxs("div", { className: "rte-footer", children: [
884
+ "Table: ",
885
+ /* @__PURE__ */ jsx("strong", { children: "drag" }),
886
+ " or ",
887
+ /* @__PURE__ */ jsx("strong", { children: "Shift+click" }),
888
+ " to select cells \xA0\xB7\xA0 ",
889
+ /* @__PURE__ */ jsx("strong", { children: "Tab" }),
890
+ " moves between cells"
891
+ ] })
892
+ ] })
893
+ ] });
359
894
  }
360
895
  export {
361
896
  RichTextEditor