@wingleeio/ori-react 0.0.3 → 0.1.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.
- package/dist/index.cjs +503 -420
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -41
- package/dist/index.d.ts +6 -41
- package/dist/index.js +505 -417
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/styles.css +124 -0
package/dist/index.cjs
CHANGED
|
@@ -2,9 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
var oriCore = require('@wingleeio/ori-core');
|
|
4
4
|
var react = require('react');
|
|
5
|
+
var client = require('react-dom/client');
|
|
5
6
|
var jsxRuntime = require('react/jsx-runtime');
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
var __defProp = Object.defineProperty;
|
|
9
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
10
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
8
11
|
function useEditor(options = {}) {
|
|
9
12
|
const ref = react.useRef(null);
|
|
10
13
|
if (ref.current === null) {
|
|
@@ -32,469 +35,554 @@ function useActiveMarks(editor) {
|
|
|
32
35
|
void snapshot.revision;
|
|
33
36
|
return editor.getActiveMarks();
|
|
34
37
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
function fragmentStyle(frag) {
|
|
40
|
-
const f = frag.font;
|
|
41
|
-
const decoration = [];
|
|
42
|
-
if (frag.marks.underline) decoration.push("underline");
|
|
43
|
-
if (frag.marks.strike) decoration.push("line-through");
|
|
44
|
-
return {
|
|
45
|
-
fontFamily: f.fontFamily,
|
|
46
|
-
fontSize: f.fontSize,
|
|
47
|
-
fontWeight: f.fontWeight,
|
|
48
|
-
fontStyle: f.italic ? "italic" : "normal",
|
|
49
|
-
letterSpacing: f.letterSpacing || void 0,
|
|
50
|
-
textDecorationLine: decoration.length ? decoration.join(" ") : void 0
|
|
51
|
-
};
|
|
38
|
+
|
|
39
|
+
// src/ce/dom.ts
|
|
40
|
+
function esc(s) {
|
|
41
|
+
return typeof CSS !== "undefined" && CSS.escape ? CSS.escape(s) : s.replace(/["\\]/g, "\\$&");
|
|
52
42
|
}
|
|
53
|
-
function
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
{
|
|
61
|
-
className: "ori-atom",
|
|
62
|
-
style: {
|
|
63
|
-
display: "inline-block",
|
|
64
|
-
width: atom.width,
|
|
65
|
-
verticalAlign: "middle",
|
|
66
|
-
whiteSpace: "normal"
|
|
67
|
-
},
|
|
68
|
-
children: render ? render({ editor, atom }) : null
|
|
69
|
-
}
|
|
70
|
-
);
|
|
43
|
+
function blockElOf(node, root) {
|
|
44
|
+
let n = node;
|
|
45
|
+
while (n && n !== root) {
|
|
46
|
+
if (n instanceof HTMLElement && n.dataset.blockId) return n;
|
|
47
|
+
n = n.parentNode;
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
71
50
|
}
|
|
72
|
-
function
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
{
|
|
80
|
-
className: "ori-line",
|
|
81
|
-
style: { height: line.height, lineHeight: `${line.height}px`, whiteSpace: "pre" },
|
|
82
|
-
children: line.fragments.map((frag) => {
|
|
83
|
-
if (frag.atom) {
|
|
84
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
85
|
-
AtomFragment,
|
|
86
|
-
{
|
|
87
|
-
editor,
|
|
88
|
-
atom: frag.atom,
|
|
89
|
-
render: atoms[frag.atom.type]
|
|
90
|
-
},
|
|
91
|
-
frag.start
|
|
92
|
-
);
|
|
93
|
-
}
|
|
94
|
-
const className = ["ori-frag"];
|
|
95
|
-
if (frag.marks.code) className.push("ori-frag-code");
|
|
96
|
-
if (frag.marks.link) className.push("ori-frag-link");
|
|
97
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
98
|
-
"span",
|
|
99
|
-
{
|
|
100
|
-
className: className.join(" "),
|
|
101
|
-
style: fragmentStyle(frag),
|
|
102
|
-
"data-start": frag.start,
|
|
103
|
-
children: frag.text
|
|
104
|
-
},
|
|
105
|
-
frag.start
|
|
106
|
-
);
|
|
107
|
-
})
|
|
108
|
-
}
|
|
109
|
-
);
|
|
51
|
+
function spanOf(node) {
|
|
52
|
+
let n = node;
|
|
53
|
+
while (n) {
|
|
54
|
+
if (n instanceof HTMLElement && n.dataset.off != null) return n;
|
|
55
|
+
n = n.parentNode;
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
110
58
|
}
|
|
111
|
-
function
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
{
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
);
|
|
59
|
+
function domToModel(root, node, offset) {
|
|
60
|
+
const blockEl = blockElOf(node, root);
|
|
61
|
+
if (!blockEl) return null;
|
|
62
|
+
const blockId = blockEl.dataset.blockId;
|
|
63
|
+
if (node && node.nodeType === Node.TEXT_NODE) {
|
|
64
|
+
const span = spanOf(node);
|
|
65
|
+
const base = span ? Number(span.dataset.off) : 0;
|
|
66
|
+
return { blockId, offset: base + offset };
|
|
67
|
+
}
|
|
68
|
+
const el = node;
|
|
69
|
+
if (el.dataset?.off != null) {
|
|
70
|
+
return { blockId, offset: Number(el.dataset.off) + (offset > 0 ? spanLen(el) : 0) };
|
|
71
|
+
}
|
|
72
|
+
const kids = Array.from(el.childNodes);
|
|
73
|
+
for (let i = offset; i < kids.length; i++) {
|
|
74
|
+
const k = kids[i];
|
|
75
|
+
if (k instanceof HTMLElement && k.dataset.off != null) return { blockId, offset: Number(k.dataset.off) };
|
|
76
|
+
}
|
|
77
|
+
let end = 0;
|
|
78
|
+
for (const k of kids) if (k instanceof HTMLElement && k.dataset.off != null) end = Math.max(end, Number(k.dataset.off) + spanLen(k));
|
|
79
|
+
return { blockId, offset: end };
|
|
131
80
|
}
|
|
132
|
-
function
|
|
133
|
-
|
|
134
|
-
snapshot
|
|
135
|
-
}) {
|
|
136
|
-
void snapshot.revision;
|
|
137
|
-
const rects = editor.selectionRectsForViewport();
|
|
138
|
-
if (rects.length === 0) return null;
|
|
139
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ori-selection-layer", "aria-hidden": true, children: rects.map((r, i) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
140
|
-
"div",
|
|
141
|
-
{
|
|
142
|
-
className: "ori-selection-rect",
|
|
143
|
-
style: { position: "absolute", left: r.x, top: r.y, width: r.width, height: r.height }
|
|
144
|
-
},
|
|
145
|
-
`${r.blockId}:${i}`
|
|
146
|
-
)) });
|
|
81
|
+
function spanLen(span) {
|
|
82
|
+
return span.dataset.len != null ? Number(span.dataset.len) : (span.textContent ?? "").length;
|
|
147
83
|
}
|
|
148
|
-
function
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
84
|
+
function modelToDom(root, blockId, offset) {
|
|
85
|
+
const blockEl = root.querySelector(`[data-block-id="${esc(blockId)}"]`);
|
|
86
|
+
if (!blockEl) return null;
|
|
87
|
+
const spans = Array.from(blockEl.querySelectorAll("[data-off]"));
|
|
88
|
+
if (spans.length === 0) {
|
|
89
|
+
return { node: blockEl, offset: 0 };
|
|
90
|
+
}
|
|
91
|
+
for (const span of spans) {
|
|
92
|
+
const start = Number(span.dataset.off);
|
|
93
|
+
const len = spanLen(span);
|
|
94
|
+
if (offset <= start + len) {
|
|
95
|
+
if (span.dataset.atom != null) {
|
|
96
|
+
const idx = Array.prototype.indexOf.call(blockEl.childNodes, span);
|
|
97
|
+
return { node: blockEl, offset: offset <= start ? idx : idx + 1 };
|
|
98
|
+
}
|
|
99
|
+
const textNode2 = span.firstChild ?? span;
|
|
100
|
+
return { node: textNode2, offset: Math.max(0, Math.min(offset - start, (textNode2.textContent ?? "").length)) };
|
|
163
101
|
}
|
|
164
|
-
|
|
102
|
+
}
|
|
103
|
+
const last = spans[spans.length - 1];
|
|
104
|
+
const textNode = last.firstChild ?? last;
|
|
105
|
+
return { node: textNode, offset: (textNode.textContent ?? "").length };
|
|
165
106
|
}
|
|
166
|
-
function
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
|
|
107
|
+
function markClass(marks) {
|
|
108
|
+
const m = marks ?? {};
|
|
109
|
+
const cls = ["ori-frag"];
|
|
110
|
+
if (m.bold) cls.push("ori-m-bold");
|
|
111
|
+
if (m.italic) cls.push("ori-m-italic");
|
|
112
|
+
if (m.underline) cls.push("ori-m-underline");
|
|
113
|
+
if (m.strike) cls.push("ori-m-strike");
|
|
114
|
+
if (m.code) cls.push("ori-frag-code");
|
|
115
|
+
if (m.link) cls.push("ori-frag-link");
|
|
116
|
+
return cls.join(" ");
|
|
117
|
+
}
|
|
118
|
+
function buildRun(item) {
|
|
119
|
+
const span = document.createElement("span");
|
|
120
|
+
span.className = markClass(item.marks);
|
|
121
|
+
span.dataset.off = String(item.start);
|
|
122
|
+
span.dataset.len = String(item.text.length);
|
|
123
|
+
span.textContent = item.text;
|
|
124
|
+
return span;
|
|
170
125
|
}
|
|
171
126
|
|
|
172
|
-
// src/
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
editor
|
|
176
|
-
|
|
177
|
-
editor
|
|
178
|
-
|
|
127
|
+
// src/ce/view.ts
|
|
128
|
+
var PLACEHOLDER = "\uFFFC";
|
|
129
|
+
var EditorView = class {
|
|
130
|
+
constructor(root, editor, opts) {
|
|
131
|
+
__publicField(this, "root", root);
|
|
132
|
+
__publicField(this, "editor", editor);
|
|
133
|
+
__publicField(this, "opts", opts);
|
|
134
|
+
__publicField(this, "roots", /* @__PURE__ */ new Map());
|
|
135
|
+
__publicField(this, "composing", false);
|
|
136
|
+
__publicField(this, "applyingModel", false);
|
|
137
|
+
__publicField(this, "detachers", []);
|
|
138
|
+
/** The model revision the DOM currently reflects (so external changes — remote
|
|
139
|
+
* edits, app commands — re-render, but our own edits don't clobber the caret). */
|
|
140
|
+
__publicField(this, "lastRevision", -1);
|
|
141
|
+
root.setAttribute("contenteditable", opts.readOnly ? "false" : "true");
|
|
142
|
+
root.setAttribute("spellcheck", opts.readOnly ? "false" : "true");
|
|
143
|
+
root.setAttribute("role", "textbox");
|
|
144
|
+
root.setAttribute("aria-multiline", "true");
|
|
145
|
+
this.renderBlocks();
|
|
146
|
+
this.lastRevision = this.rev();
|
|
147
|
+
const on = (t, h, o) => {
|
|
148
|
+
root.addEventListener(t, h, o);
|
|
149
|
+
this.detachers.push(() => root.removeEventListener(t, h, o));
|
|
150
|
+
};
|
|
151
|
+
on("beforeinput", (e) => this.onBeforeInput(e));
|
|
152
|
+
on("input", () => this.onInput());
|
|
153
|
+
on("keydown", (e) => this.onKeyDown(e));
|
|
154
|
+
on("compositionstart", () => this.composing = true);
|
|
155
|
+
on("compositionend", () => {
|
|
156
|
+
this.composing = false;
|
|
157
|
+
this.onInput();
|
|
158
|
+
});
|
|
159
|
+
const onSelChange = () => {
|
|
160
|
+
if (this.applyingModel || this.composing) return;
|
|
161
|
+
const sel = this.readSelection();
|
|
162
|
+
if (!sel) return;
|
|
163
|
+
this.editor.setSelection(sel);
|
|
164
|
+
this.lastRevision = this.rev();
|
|
165
|
+
};
|
|
166
|
+
document.addEventListener("selectionchange", onSelChange);
|
|
167
|
+
this.detachers.push(() => document.removeEventListener("selectionchange", onSelChange));
|
|
179
168
|
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
169
|
+
destroy() {
|
|
170
|
+
this.detachers.forEach((d) => d());
|
|
171
|
+
this.roots.forEach((r) => r.unmount());
|
|
172
|
+
this.roots.clear();
|
|
173
|
+
}
|
|
174
|
+
focus() {
|
|
175
|
+
this.root.focus();
|
|
176
|
+
}
|
|
177
|
+
// --- rendering ---------------------------------------------------------
|
|
178
|
+
rev() {
|
|
179
|
+
return this.editor.getSnapshot().revision;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Called by React on every model change. Only re-renders when the model moved
|
|
183
|
+
* ahead of what we last drew (an *external* change — app command, undo, remote);
|
|
184
|
+
* our own edits already updated the DOM and must not be clobbered.
|
|
185
|
+
*/
|
|
186
|
+
sync() {
|
|
187
|
+
const rev = this.rev();
|
|
188
|
+
if (rev === this.lastRevision) return;
|
|
189
|
+
const changed = this.renderBlocks();
|
|
190
|
+
if (changed) this.writeSelection();
|
|
191
|
+
this.lastRevision = rev;
|
|
192
|
+
}
|
|
193
|
+
/** After a controlled (preventDefault'd) edit: re-render + restore the caret. */
|
|
194
|
+
commit() {
|
|
195
|
+
this.renderBlocks();
|
|
196
|
+
this.writeSelection();
|
|
197
|
+
this.lastRevision = this.rev();
|
|
198
|
+
}
|
|
199
|
+
/** A content signature for a block, so unchanged blocks aren't re-rendered. */
|
|
200
|
+
sig(id) {
|
|
201
|
+
return this.editor.getBlockType(id) + "|" + JSON.stringify(this.editor.getInline(id));
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Reconcile the DOM to the *visible window* of blocks (virtualization): a top
|
|
205
|
+
* spacer, the windowed blocks, then a bottom spacer — heights from the
|
|
206
|
+
* controller's offscreen measurement. On-screen blocks are reused by id so a
|
|
207
|
+
* caret inside one survives a scroll. Returns true if the DOM was mutated.
|
|
208
|
+
*/
|
|
209
|
+
renderBlocks() {
|
|
210
|
+
let changed = false;
|
|
211
|
+
const snap = this.editor.getSnapshot();
|
|
212
|
+
const vis = snap.visible;
|
|
213
|
+
const topH = vis.length ? vis[0].top : 0;
|
|
214
|
+
const botH = vis.length ? Math.max(0, snap.totalHeight - (vis[vis.length - 1].top + vis[vis.length - 1].height)) : Math.max(0, snap.totalHeight);
|
|
215
|
+
const TOP = "\0top";
|
|
216
|
+
const BOTTOM = "\0bottom";
|
|
217
|
+
const want = [TOP, ...vis.map((v) => v.id), BOTTOM];
|
|
218
|
+
const keyOf = (el) => {
|
|
219
|
+
const e = el;
|
|
220
|
+
return e.dataset.spacer ? "\0" + e.dataset.spacer : e.dataset.blockId ?? "";
|
|
221
|
+
};
|
|
222
|
+
const have = /* @__PURE__ */ new Map();
|
|
223
|
+
for (const c of Array.from(this.root.children)) have.set(keyOf(c), c);
|
|
224
|
+
let prev = null;
|
|
225
|
+
for (const k of want) {
|
|
226
|
+
let el = have.get(k);
|
|
227
|
+
if (el) {
|
|
228
|
+
have.delete(k);
|
|
229
|
+
} else {
|
|
230
|
+
el = k === TOP || k === BOTTOM ? this.makeSpacer(k.slice(1)) : this.makeBlock(k);
|
|
231
|
+
changed = true;
|
|
232
|
+
}
|
|
233
|
+
const anchor = prev ? prev.nextSibling : this.root.firstChild;
|
|
234
|
+
if (anchor !== el) {
|
|
235
|
+
this.root.insertBefore(el, anchor);
|
|
236
|
+
changed = true;
|
|
237
|
+
}
|
|
238
|
+
prev = el;
|
|
232
239
|
}
|
|
240
|
+
for (const el of have.values()) {
|
|
241
|
+
if (el.dataset.blockId) this.unmountRootsIn(el);
|
|
242
|
+
el.remove();
|
|
243
|
+
changed = true;
|
|
244
|
+
}
|
|
245
|
+
const top = this.root.firstElementChild;
|
|
246
|
+
if (top && top.style.height !== `${topH}px`) top.style.height = `${topH}px`;
|
|
247
|
+
const bot = this.root.lastElementChild;
|
|
248
|
+
if (bot && bot.style.height !== `${botH}px`) bot.style.height = `${botH}px`;
|
|
249
|
+
for (const vb of vis) {
|
|
250
|
+
const el = this.root.querySelector(`[data-block-id="${esc(vb.id)}"]`);
|
|
251
|
+
if (!el) continue;
|
|
252
|
+
const sig = this.sig(vb.id);
|
|
253
|
+
if (el.dataset.sig !== sig) {
|
|
254
|
+
el.dataset.sig = sig;
|
|
255
|
+
this.renderBlockInner(el, vb.id);
|
|
256
|
+
changed = true;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
return changed;
|
|
233
260
|
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
261
|
+
makeBlock(id) {
|
|
262
|
+
const el = document.createElement("div");
|
|
263
|
+
el.dataset.blockId = id;
|
|
264
|
+
return el;
|
|
265
|
+
}
|
|
266
|
+
makeSpacer(which) {
|
|
267
|
+
const el = document.createElement("div");
|
|
268
|
+
el.dataset.spacer = which;
|
|
269
|
+
el.setAttribute("contenteditable", "false");
|
|
270
|
+
el.setAttribute("aria-hidden", "true");
|
|
271
|
+
el.style.userSelect = "none";
|
|
272
|
+
el.style.pointerEvents = "none";
|
|
273
|
+
return el;
|
|
274
|
+
}
|
|
275
|
+
renderBlockInner(el, id) {
|
|
276
|
+
this.unmountRootsIn(el);
|
|
277
|
+
const type = this.editor.getBlockType(id);
|
|
278
|
+
el.className = `ori-block ori-block-${type}`;
|
|
279
|
+
const blockRenderer = this.opts.renderBlock(type);
|
|
280
|
+
if (blockRenderer) {
|
|
281
|
+
el.contentEditable = "false";
|
|
282
|
+
el.textContent = "";
|
|
283
|
+
const root = client.createRoot(el);
|
|
284
|
+
root.render(blockRenderer({ editor: this.editor, block: { id, type, index: 0, top: 0, height: 0 }, layout: this.editor.getLayout(id) }));
|
|
285
|
+
this.roots.set(el, root);
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
el.contentEditable = "inherit";
|
|
289
|
+
el.textContent = "";
|
|
290
|
+
const items = this.editor.getInline(id);
|
|
291
|
+
if (items.length === 0) {
|
|
292
|
+
el.appendChild(document.createElement("br"));
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
for (const item of items) {
|
|
296
|
+
if (item.atom) {
|
|
297
|
+
const span = document.createElement("span");
|
|
298
|
+
span.className = "ori-atom";
|
|
299
|
+
span.contentEditable = "false";
|
|
300
|
+
span.dataset.atom = "true";
|
|
301
|
+
span.dataset.off = String(item.start);
|
|
302
|
+
span.dataset.len = "1";
|
|
303
|
+
el.appendChild(span);
|
|
304
|
+
const renderer = this.opts.renderAtom(item.atom.type);
|
|
305
|
+
if (renderer) {
|
|
306
|
+
const r = client.createRoot(span);
|
|
307
|
+
r.render(renderer({ editor: this.editor, atom: item.atom }));
|
|
308
|
+
this.roots.set(span, r);
|
|
309
|
+
}
|
|
310
|
+
} else {
|
|
311
|
+
el.appendChild(buildRun(item));
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
unmountRootsIn(el) {
|
|
316
|
+
for (const [node, root] of this.roots) {
|
|
317
|
+
if (el === node || el.contains(node)) {
|
|
318
|
+
root.unmount();
|
|
319
|
+
this.roots.delete(node);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
// --- selection ---------------------------------------------------------
|
|
324
|
+
readSelection() {
|
|
325
|
+
const s = window.getSelection();
|
|
326
|
+
if (!s || s.rangeCount === 0 || !this.root.contains(s.anchorNode)) return null;
|
|
327
|
+
const a = domToModel(this.root, s.anchorNode, s.anchorOffset);
|
|
328
|
+
const f = domToModel(this.root, s.focusNode, s.focusOffset);
|
|
329
|
+
if (!a || !f) return null;
|
|
330
|
+
return { anchor: { blockId: a.blockId, offset: a.offset }, focus: { blockId: f.blockId, offset: f.offset } };
|
|
331
|
+
}
|
|
332
|
+
/** Push the controller's selection back into the DOM (after a model op). */
|
|
333
|
+
writeSelection() {
|
|
334
|
+
const sel = this.editor.getSelection();
|
|
335
|
+
if (!sel) return;
|
|
336
|
+
const a = modelToDom(this.root, sel.anchor.blockId, sel.anchor.offset);
|
|
337
|
+
const f = modelToDom(this.root, sel.focus.blockId, sel.focus.offset);
|
|
338
|
+
if (!a || !f) return;
|
|
339
|
+
const r = document.createRange();
|
|
340
|
+
const s = window.getSelection();
|
|
341
|
+
if (!s) return;
|
|
342
|
+
this.applyingModel = true;
|
|
343
|
+
try {
|
|
344
|
+
r.setStart(a.node, a.offset);
|
|
345
|
+
s.removeAllRanges();
|
|
346
|
+
s.addRange(r);
|
|
347
|
+
s.extend(f.node, f.offset);
|
|
348
|
+
} catch {
|
|
349
|
+
} finally {
|
|
350
|
+
this.applyingModel = false;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
/** The block text as the model sees it (atoms collapse to one placeholder). */
|
|
354
|
+
domBlockText(el) {
|
|
355
|
+
let out = "";
|
|
356
|
+
for (const child of Array.from(el.childNodes)) {
|
|
357
|
+
if (child instanceof HTMLElement && child.dataset.atom != null) {
|
|
358
|
+
out += PLACEHOLDER;
|
|
359
|
+
} else {
|
|
360
|
+
out += child.textContent ?? "";
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
return out;
|
|
364
|
+
}
|
|
365
|
+
// --- input -------------------------------------------------------------
|
|
366
|
+
/** Formatting + history shortcuts (the browser fires these as keydown). */
|
|
367
|
+
onKeyDown(e) {
|
|
368
|
+
if (this.opts.readOnly) return;
|
|
369
|
+
const mod = e.metaKey || e.ctrlKey;
|
|
370
|
+
if (!mod || e.altKey) return;
|
|
371
|
+
const k = e.key.toLowerCase();
|
|
372
|
+
const mark = { b: "bold", i: "italic", u: "underline", e: "code" }[k];
|
|
373
|
+
if (mark) {
|
|
244
374
|
e.preventDefault();
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
375
|
+
const sel = this.readSelection();
|
|
376
|
+
if (sel) this.editor.setSelection(sel);
|
|
377
|
+
this.editor.toggleMark(mark);
|
|
378
|
+
this.commit();
|
|
379
|
+
} else if (k === "z") {
|
|
248
380
|
e.preventDefault();
|
|
249
|
-
if (
|
|
250
|
-
|
|
251
|
-
|
|
381
|
+
if (e.shiftKey) this.editor.redo();
|
|
382
|
+
else this.editor.undo();
|
|
383
|
+
this.commit();
|
|
384
|
+
} else if (k === "y") {
|
|
252
385
|
e.preventDefault();
|
|
253
|
-
editor.
|
|
254
|
-
|
|
255
|
-
|
|
386
|
+
this.editor.redo();
|
|
387
|
+
this.commit();
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
onBeforeInput(e) {
|
|
391
|
+
if (this.opts.readOnly) {
|
|
256
392
|
e.preventDefault();
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
const sel = this.readSelection();
|
|
396
|
+
if (!sel) return;
|
|
397
|
+
this.editor.setSelection(sel);
|
|
398
|
+
const collapsed = oriCore.isCollapsed(sel);
|
|
399
|
+
const startOffset = this.editor.orderedSelection()?.start.offset ?? sel.focus.offset;
|
|
400
|
+
const t = e.inputType;
|
|
401
|
+
if (collapsed && (t === "insertText" || t === "insertCompositionText" || t === "insertReplacementText")) return;
|
|
402
|
+
if (collapsed && t === "deleteContentForward") return;
|
|
403
|
+
if (collapsed && t === "deleteContentBackward" && startOffset > 0) return;
|
|
404
|
+
const ed = this.editor;
|
|
405
|
+
if (t === "insertParagraph") {
|
|
260
406
|
e.preventDefault();
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
case "ArrowDown":
|
|
407
|
+
ed.insertParagraphBreak();
|
|
408
|
+
} else if (t.startsWith("delete")) {
|
|
264
409
|
e.preventDefault();
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
410
|
+
if (t === "deleteContentForward") ed.deleteForward();
|
|
411
|
+
else ed.deleteBackward();
|
|
412
|
+
} else if (t === "insertText" || t === "insertReplacementText" || t === "insertFromPaste") {
|
|
268
413
|
e.preventDefault();
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
414
|
+
const text = e.data ?? e.dataTransfer?.getData("text/plain") ?? "";
|
|
415
|
+
if (text) ed.insertText(text);
|
|
416
|
+
} else if (t === "insertLineBreak") {
|
|
272
417
|
e.preventDefault();
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
418
|
+
ed.insertText("\n");
|
|
419
|
+
} else {
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
this.commit();
|
|
423
|
+
}
|
|
424
|
+
onInput() {
|
|
425
|
+
if (this.composing || this.opts.readOnly) return;
|
|
426
|
+
const blockEl = blockElOf(window.getSelection()?.anchorNode ?? null, this.root);
|
|
427
|
+
if (!blockEl) {
|
|
428
|
+
this.renderBlocks();
|
|
429
|
+
this.lastRevision = this.rev();
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
const id = blockEl.dataset.blockId;
|
|
433
|
+
const next = this.domBlockText(blockEl);
|
|
434
|
+
const cur = this.editor.getBlockText(id);
|
|
435
|
+
if (next === cur) return;
|
|
436
|
+
const max = Math.min(cur.length, next.length);
|
|
437
|
+
let p = 0;
|
|
438
|
+
while (p < max && cur[p] === next[p]) p++;
|
|
439
|
+
let s = 0;
|
|
440
|
+
while (s < max - p && cur[cur.length - 1 - s] === next[next.length - 1 - s]) s++;
|
|
441
|
+
const from = p;
|
|
442
|
+
const to = cur.length - s;
|
|
443
|
+
const insert = next.slice(p, next.length - s);
|
|
444
|
+
this.editor.setSelection({ anchor: { blockId: id, offset: from }, focus: { blockId: id, offset: to } });
|
|
445
|
+
if (to > from) this.editor.deleteBackward();
|
|
446
|
+
if (insert) this.editor.insertText(insert);
|
|
447
|
+
this.reindex(blockEl);
|
|
448
|
+
this.lastRevision = this.rev();
|
|
449
|
+
}
|
|
450
|
+
/** Re-derive data-off / data-len after a native edit (no node replacement). */
|
|
451
|
+
reindex(el) {
|
|
452
|
+
let off = 0;
|
|
453
|
+
for (const child of Array.from(el.children)) {
|
|
454
|
+
if (child.dataset.off == null) continue;
|
|
455
|
+
child.dataset.off = String(off);
|
|
456
|
+
const len = child.dataset.atom != null ? 1 : (child.textContent ?? "").length;
|
|
457
|
+
child.dataset.len = String(len);
|
|
458
|
+
off += len;
|
|
459
|
+
}
|
|
277
460
|
}
|
|
461
|
+
};
|
|
462
|
+
function caretClientRect() {
|
|
463
|
+
const s = window.getSelection();
|
|
464
|
+
if (!s || s.rangeCount === 0) return null;
|
|
465
|
+
const r = s.getRangeAt(0).cloneRange();
|
|
466
|
+
r.collapse(s.focusNode === r.endContainer && s.focusOffset === r.endOffset ? false : true);
|
|
467
|
+
const rects = r.getClientRects();
|
|
468
|
+
if (rects.length) return rects[rects.length - 1];
|
|
469
|
+
const b = r.getBoundingClientRect();
|
|
470
|
+
return b.height || b.width ? b : null;
|
|
278
471
|
}
|
|
279
|
-
var NoteEditor = react.forwardRef(function NoteEditor2({
|
|
280
|
-
editor,
|
|
281
|
-
className,
|
|
282
|
-
style,
|
|
283
|
-
maxWidth = 720,
|
|
284
|
-
placeholder = "Start writing\u2026",
|
|
285
|
-
autoFocus,
|
|
286
|
-
readOnly,
|
|
287
|
-
blockRenderers,
|
|
288
|
-
atomRenderers
|
|
289
|
-
}, ref) {
|
|
472
|
+
var NoteEditor = react.forwardRef(function NoteEditor2({ editor, className, style, maxWidth = 720, placeholder, autoFocus, readOnly, blockRenderers, atomRenderers }, ref) {
|
|
290
473
|
const snapshot = useEditorSnapshot(editor);
|
|
291
|
-
const renderers = react.useMemo(
|
|
292
|
-
() => ({ blocks: blockRenderers ?? {}, atoms: atomRenderers ?? {} }),
|
|
293
|
-
[blockRenderers, atomRenderers]
|
|
294
|
-
);
|
|
295
474
|
const scrollerRef = react.useRef(null);
|
|
296
475
|
const contentRef = react.useRef(null);
|
|
297
|
-
const
|
|
298
|
-
const draggingRef = react.useRef(false);
|
|
299
|
-
const composingRef = react.useRef(false);
|
|
476
|
+
const viewRef = react.useRef(null);
|
|
300
477
|
const [focused, setFocused] = react.useState(false);
|
|
478
|
+
const [caret, setCaret] = react.useState(null);
|
|
479
|
+
const renderersRef = react.useRef({ blockRenderers, atomRenderers });
|
|
480
|
+
renderersRef.current = { blockRenderers, atomRenderers };
|
|
301
481
|
react.useImperativeHandle(
|
|
302
482
|
ref,
|
|
303
483
|
() => ({
|
|
304
|
-
focus: () =>
|
|
484
|
+
focus: () => contentRef.current?.focus(),
|
|
305
485
|
getCaretRect: () => {
|
|
306
|
-
const
|
|
307
|
-
|
|
308
|
-
if (!c || !content) return null;
|
|
309
|
-
const r = content.getBoundingClientRect();
|
|
310
|
-
return { x: r.left + c.x, y: r.top + c.y, height: c.height };
|
|
486
|
+
const r = caretClientRect();
|
|
487
|
+
return r ? { x: r.left, y: r.top, height: r.height || 16 } : null;
|
|
311
488
|
},
|
|
312
489
|
getSelectionRect: () => {
|
|
313
|
-
const
|
|
314
|
-
if (!
|
|
315
|
-
const
|
|
316
|
-
if (
|
|
317
|
-
|
|
318
|
-
let top = Infinity;
|
|
319
|
-
let left = Infinity;
|
|
320
|
-
let bottom = -Infinity;
|
|
321
|
-
let right = -Infinity;
|
|
322
|
-
for (const rc of rects) {
|
|
323
|
-
top = Math.min(top, r.top + rc.y);
|
|
324
|
-
left = Math.min(left, r.left + rc.x);
|
|
325
|
-
bottom = Math.max(bottom, r.top + rc.y + rc.height);
|
|
326
|
-
right = Math.max(right, r.left + rc.x + rc.width);
|
|
327
|
-
}
|
|
328
|
-
return { top, left, right, bottom, width: right - left, height: bottom - top };
|
|
490
|
+
const s = window.getSelection();
|
|
491
|
+
if (!s || s.rangeCount === 0 || s.isCollapsed) return null;
|
|
492
|
+
const b = s.getRangeAt(0).getBoundingClientRect();
|
|
493
|
+
if (!b.width && !b.height) return null;
|
|
494
|
+
return { top: b.top, left: b.left, right: b.right, bottom: b.bottom, width: b.width, height: b.height };
|
|
329
495
|
},
|
|
330
496
|
getScrollElement: () => scrollerRef.current
|
|
331
497
|
}),
|
|
332
|
-
[
|
|
498
|
+
[]
|
|
333
499
|
);
|
|
500
|
+
react.useEffect(() => {
|
|
501
|
+
const el = contentRef.current;
|
|
502
|
+
if (!el) return;
|
|
503
|
+
const view = new EditorView(el, editor, {
|
|
504
|
+
readOnly,
|
|
505
|
+
renderAtom: (t) => renderersRef.current.atomRenderers?.[t],
|
|
506
|
+
renderBlock: (t) => renderersRef.current.blockRenderers?.[t]
|
|
507
|
+
});
|
|
508
|
+
viewRef.current = view;
|
|
509
|
+
return () => {
|
|
510
|
+
view.destroy();
|
|
511
|
+
viewRef.current = null;
|
|
512
|
+
};
|
|
513
|
+
}, [editor, readOnly]);
|
|
514
|
+
react.useEffect(() => {
|
|
515
|
+
viewRef.current?.sync();
|
|
516
|
+
}, [snapshot.revision]);
|
|
517
|
+
react.useEffect(() => {
|
|
518
|
+
if (autoFocus) contentRef.current?.focus();
|
|
519
|
+
}, [autoFocus]);
|
|
520
|
+
react.useEffect(() => {
|
|
521
|
+
const update = () => {
|
|
522
|
+
const content = contentRef.current;
|
|
523
|
+
const s = window.getSelection();
|
|
524
|
+
if (!content || !s || s.rangeCount === 0 || !s.isCollapsed || !content.contains(s.anchorNode)) {
|
|
525
|
+
setCaret(null);
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
528
|
+
const r = caretClientRect();
|
|
529
|
+
const box = content.getBoundingClientRect();
|
|
530
|
+
if (r) setCaret({ x: r.left - box.left, y: r.top - box.top, h: r.height || 18 });
|
|
531
|
+
};
|
|
532
|
+
document.addEventListener("selectionchange", update);
|
|
533
|
+
const ro = new ResizeObserver(update);
|
|
534
|
+
if (contentRef.current) ro.observe(contentRef.current);
|
|
535
|
+
update();
|
|
536
|
+
return () => {
|
|
537
|
+
document.removeEventListener("selectionchange", update);
|
|
538
|
+
ro.disconnect();
|
|
539
|
+
};
|
|
540
|
+
}, []);
|
|
334
541
|
react.useLayoutEffect(() => {
|
|
335
|
-
const
|
|
542
|
+
const sc = scrollerRef.current;
|
|
336
543
|
const content = contentRef.current;
|
|
337
|
-
if (!
|
|
544
|
+
if (!sc || !content) return;
|
|
338
545
|
const sync = () => {
|
|
339
546
|
editor.setWidth(content.clientWidth);
|
|
340
|
-
editor.setViewport(
|
|
547
|
+
editor.setViewport(sc.scrollTop, sc.clientHeight);
|
|
341
548
|
};
|
|
342
549
|
sync();
|
|
343
550
|
const ro = new ResizeObserver(sync);
|
|
344
|
-
ro.observe(
|
|
551
|
+
ro.observe(sc);
|
|
345
552
|
ro.observe(content);
|
|
346
553
|
return () => ro.disconnect();
|
|
347
554
|
}, [editor]);
|
|
348
|
-
react.useEffect(() => {
|
|
349
|
-
const fonts = document.fonts;
|
|
350
|
-
if (fonts?.ready) void fonts.ready.then(() => editor.invalidateMeasurements());
|
|
351
|
-
}, [editor]);
|
|
352
|
-
react.useEffect(() => {
|
|
353
|
-
if (autoFocus) inputRef.current?.focus();
|
|
354
|
-
}, [autoFocus]);
|
|
355
|
-
const pointToPosition = useCallbackRef((clientX, clientY) => {
|
|
356
|
-
const content = contentRef.current;
|
|
357
|
-
if (!content) return null;
|
|
358
|
-
const rect = content.getBoundingClientRect();
|
|
359
|
-
return editor.positionFromPoint(clientX - rect.left, clientY - rect.top);
|
|
360
|
-
});
|
|
361
|
-
react.useEffect(() => {
|
|
362
|
-
const onMove = (e) => {
|
|
363
|
-
if (!draggingRef.current) return;
|
|
364
|
-
const pos = pointToPosition(e.clientX, e.clientY);
|
|
365
|
-
const sel = editor.getSelection();
|
|
366
|
-
if (pos && sel) editor.setSelection({ anchor: sel.anchor, focus: pos });
|
|
367
|
-
};
|
|
368
|
-
const onUp = () => {
|
|
369
|
-
draggingRef.current = false;
|
|
370
|
-
};
|
|
371
|
-
window.addEventListener("mousemove", onMove);
|
|
372
|
-
window.addEventListener("mouseup", onUp);
|
|
373
|
-
return () => {
|
|
374
|
-
window.removeEventListener("mousemove", onMove);
|
|
375
|
-
window.removeEventListener("mouseup", onUp);
|
|
376
|
-
};
|
|
377
|
-
}, [editor, pointToPosition]);
|
|
378
555
|
const onScroll = () => {
|
|
379
|
-
const
|
|
380
|
-
if (
|
|
381
|
-
};
|
|
382
|
-
const onMouseDown = (e) => {
|
|
383
|
-
if (e.button !== 0) return;
|
|
384
|
-
const scroller = scrollerRef.current;
|
|
385
|
-
if (scroller && e.clientX - scroller.getBoundingClientRect().left >= scroller.clientWidth) {
|
|
386
|
-
return;
|
|
387
|
-
}
|
|
388
|
-
inputRef.current?.focus();
|
|
389
|
-
const pos = pointToPosition(e.clientX, e.clientY);
|
|
390
|
-
if (!pos) return;
|
|
391
|
-
e.preventDefault();
|
|
392
|
-
const sel = editor.getSelection();
|
|
393
|
-
if (e.shiftKey && sel) {
|
|
394
|
-
editor.setSelection({ anchor: sel.anchor, focus: pos });
|
|
395
|
-
} else {
|
|
396
|
-
editor.collapse(pos);
|
|
397
|
-
}
|
|
398
|
-
draggingRef.current = true;
|
|
399
|
-
};
|
|
400
|
-
const onKeyDown = (e) => {
|
|
401
|
-
handleKeyDown(editor, e, { readOnly });
|
|
556
|
+
const sc = scrollerRef.current;
|
|
557
|
+
if (sc) editor.setViewport(sc.scrollTop, sc.clientHeight);
|
|
402
558
|
};
|
|
403
|
-
const
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
}
|
|
426
|
-
};
|
|
427
|
-
const caret = editor.caretRect();
|
|
428
|
-
const inputStyle = {
|
|
429
|
-
position: "absolute",
|
|
430
|
-
left: caret ? caret.x : 0,
|
|
431
|
-
top: caret ? caret.y : 0,
|
|
432
|
-
width: 1,
|
|
433
|
-
height: caret ? caret.height : 16,
|
|
434
|
-
opacity: 0,
|
|
435
|
-
padding: 0,
|
|
436
|
-
border: 0,
|
|
437
|
-
outline: "none",
|
|
438
|
-
resize: "none",
|
|
439
|
-
background: "transparent",
|
|
440
|
-
caretColor: "transparent",
|
|
441
|
-
color: "transparent",
|
|
442
|
-
overflow: "hidden",
|
|
443
|
-
whiteSpace: "pre",
|
|
444
|
-
zIndex: 1
|
|
445
|
-
};
|
|
446
|
-
return /* @__PURE__ */ jsxRuntime.jsx(RenderersProvider, { value: renderers, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: `ori-root${className ? ` ${className}` : ""}`, style, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ori-scroller", ref: scrollerRef, onScroll, onMouseDown, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
447
|
-
"div",
|
|
448
|
-
{
|
|
449
|
-
className: "ori-content",
|
|
450
|
-
ref: contentRef,
|
|
451
|
-
style: { maxWidth, marginInline: "auto", position: "relative" },
|
|
452
|
-
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
453
|
-
"div",
|
|
454
|
-
{
|
|
455
|
-
className: "ori-canvas",
|
|
456
|
-
style: { position: "relative", width: "100%", height: snapshot.totalHeight },
|
|
457
|
-
children: [
|
|
458
|
-
/* @__PURE__ */ jsxRuntime.jsx(SelectionLayer, { editor, snapshot }),
|
|
459
|
-
snapshot.visible.map((block) => /* @__PURE__ */ jsxRuntime.jsx(BlockView, { editor, block }, block.id)),
|
|
460
|
-
/* @__PURE__ */ jsxRuntime.jsx(CaretLayer, { editor, snapshot, focused }),
|
|
461
|
-
snapshot.empty && placeholder ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ori-placeholder", "aria-hidden": true, children: placeholder }) : null,
|
|
462
|
-
!readOnly ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
463
|
-
"textarea",
|
|
464
|
-
{
|
|
465
|
-
ref: inputRef,
|
|
466
|
-
className: "ori-input",
|
|
467
|
-
style: inputStyle,
|
|
468
|
-
spellCheck: false,
|
|
469
|
-
autoCapitalize: "off",
|
|
470
|
-
autoCorrect: "off",
|
|
471
|
-
onKeyDown,
|
|
472
|
-
onInput,
|
|
473
|
-
onCompositionStart,
|
|
474
|
-
onCompositionEnd,
|
|
475
|
-
onFocus: () => setFocused(true),
|
|
476
|
-
onBlur: () => setFocused(false),
|
|
477
|
-
onCopy: (e) => {
|
|
478
|
-
e.preventDefault();
|
|
479
|
-
e.clipboardData.setData("text/plain", editor.getSelectedText());
|
|
480
|
-
},
|
|
481
|
-
onCut: (e) => {
|
|
482
|
-
e.preventDefault();
|
|
483
|
-
e.clipboardData.setData("text/plain", editor.getSelectedText());
|
|
484
|
-
editor.deleteBackward();
|
|
485
|
-
},
|
|
486
|
-
onPaste: (e) => {
|
|
487
|
-
e.preventDefault();
|
|
488
|
-
pasteText(editor, e.clipboardData.getData("text/plain"));
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
) : null
|
|
492
|
-
]
|
|
493
|
-
}
|
|
494
|
-
)
|
|
495
|
-
}
|
|
496
|
-
) }) }) });
|
|
559
|
+
const showCaret = focused && !!caret && !readOnly;
|
|
560
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `ori-root${className ? ` ${className}` : ""}`, style, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ori-scroller", ref: scrollerRef, onScroll, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ori-content", style: { maxWidth, marginInline: "auto", position: "relative" }, children: [
|
|
561
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
562
|
+
"div",
|
|
563
|
+
{
|
|
564
|
+
className: "ori-canvas ori-ce",
|
|
565
|
+
ref: contentRef,
|
|
566
|
+
onFocus: () => setFocused(true),
|
|
567
|
+
onBlur: () => setFocused(false),
|
|
568
|
+
suppressContentEditableWarning: true
|
|
569
|
+
}
|
|
570
|
+
),
|
|
571
|
+
showCaret && caret ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
572
|
+
"div",
|
|
573
|
+
{
|
|
574
|
+
className: "ori-caret",
|
|
575
|
+
style: { position: "absolute", left: caret.x, top: caret.y, height: caret.h, pointerEvents: "none" },
|
|
576
|
+
"aria-hidden": true
|
|
577
|
+
}
|
|
578
|
+
) : null,
|
|
579
|
+
snapshot.empty && placeholder ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ori-placeholder", "aria-hidden": true, children: placeholder }) : null
|
|
580
|
+
] }) }) });
|
|
497
581
|
});
|
|
582
|
+
var EMPTY = { blocks: {}, atoms: {} };
|
|
583
|
+
var RenderersContext = react.createContext(EMPTY);
|
|
584
|
+
RenderersContext.Provider;
|
|
585
|
+
var useRenderers = () => react.useContext(RenderersContext);
|
|
498
586
|
|
|
499
587
|
Object.defineProperty(exports, "DEFAULT_TYPOGRAPHY", {
|
|
500
588
|
enumerable: true,
|
|
@@ -540,12 +628,7 @@ Object.defineProperty(exports, "snapshotBlocks", {
|
|
|
540
628
|
enumerable: true,
|
|
541
629
|
get: function () { return oriCore.snapshotBlocks; }
|
|
542
630
|
});
|
|
543
|
-
exports.BlockView = BlockView;
|
|
544
|
-
exports.CaretLayer = CaretLayer;
|
|
545
631
|
exports.NoteEditor = NoteEditor;
|
|
546
|
-
exports.SelectionLayer = SelectionLayer;
|
|
547
|
-
exports.handleKeyDown = handleKeyDown;
|
|
548
|
-
exports.pasteText = pasteText;
|
|
549
632
|
exports.useActiveMarks = useActiveMarks;
|
|
550
633
|
exports.useEditor = useEditor;
|
|
551
634
|
exports.useEditorSnapshot = useEditorSnapshot;
|