@tiptap/extension-ruby-text 3.30.2 → 3.30.4

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 CHANGED
@@ -1,317 +1,279 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
-
20
- // src/index.ts
21
- var index_exports = {};
22
- __export(index_exports, {
23
- RubyText: () => RubyText,
24
- default: () => index_default
1
+ Object.defineProperties(exports, {
2
+ __esModule: { value: true },
3
+ [Symbol.toStringTag]: { value: "Module" }
25
4
  });
26
- module.exports = __toCommonJS(index_exports);
27
-
28
- // src/ruby-text.ts
29
- var import_core = require("@tiptap/core");
30
-
31
- // src/ruby-text-decoration-plugin.ts
32
- var import_state = require("@tiptap/pm/state");
33
- var import_view = require("@tiptap/pm/view");
34
- var rubyTextDecorationPluginKey = new import_state.PluginKey("rubyTextDecoration");
35
- var editSessions = /* @__PURE__ */ new WeakMap();
5
+ let _tiptap_core = require("@tiptap/core");
6
+ let _tiptap_pm_state = require("@tiptap/pm/state");
7
+ let _tiptap_pm_view = require("@tiptap/pm/view");
8
+ //#region src/ruby-text-decoration-plugin.ts
9
+ const rubyTextDecorationPluginKey = new _tiptap_pm_state.PluginKey("rubyTextDecoration");
10
+ const editSessions = /* @__PURE__ */ new WeakMap();
36
11
  function isImeEvent(event) {
37
- const legacyKeyCode = event.keyCode;
38
- return event.isComposing || legacyKeyCode === 229;
12
+ const legacyKeyCode = event.keyCode;
13
+ return event.isComposing || legacyKeyCode === 229;
39
14
  }
40
- function defaultRenderAnnotationEditor({
41
- annotation,
42
- submit,
43
- dismiss
44
- }) {
45
- const input = document.createElement("input");
46
- input.type = "text";
47
- input.value = annotation;
48
- input.size = Math.max(annotation.length, 1);
49
- input.setAttribute("aria-label", "Ruby text annotation");
50
- input.addEventListener("focus", () => input.select(), { once: true });
51
- input.addEventListener("blur", () => dismiss());
52
- input.addEventListener("keydown", (event) => {
53
- if (isImeEvent(event)) {
54
- return;
55
- }
56
- if (event.key === "Escape") {
57
- event.preventDefault();
58
- dismiss();
59
- }
60
- if (event.key === "Enter") {
61
- event.preventDefault();
62
- submit(input.value);
63
- }
64
- });
65
- return input;
15
+ /** Default annotation editor: a plain text input. Enter submits, Escape or blur dismisses. */
16
+ function defaultRenderAnnotationEditor({ annotation, submit, dismiss }) {
17
+ const input = document.createElement("input");
18
+ input.type = "text";
19
+ input.value = annotation;
20
+ input.size = Math.max(annotation.length, 1);
21
+ input.setAttribute("aria-label", "Ruby text annotation");
22
+ input.addEventListener("focus", () => input.select(), { once: true });
23
+ input.addEventListener("blur", () => dismiss());
24
+ input.addEventListener("keydown", (event) => {
25
+ if (isImeEvent(event)) return;
26
+ if (event.key === "Escape") {
27
+ event.preventDefault();
28
+ dismiss();
29
+ }
30
+ if (event.key === "Enter") {
31
+ event.preventDefault();
32
+ submit(input.value);
33
+ }
34
+ });
35
+ return input;
66
36
  }
67
37
  function createRtElement(annotation, range, rubyTextType, view, options) {
68
- const rt = document.createElement("rt");
69
- rt.contentEditable = "false";
70
- rt.textContent = annotation;
71
- let editing = false;
72
- const restoreEditorFocus = () => {
73
- view.dispatch(view.state.tr.setSelection(import_state.TextSelection.create(view.state.doc, range.to)));
74
- view.focus();
75
- };
76
- if (!options.allowClickToEdit || !view.editable) {
77
- return rt;
78
- }
79
- editSessions.set(rt, () => {
80
- editing = false;
81
- });
82
- const dismiss = () => {
83
- if (!editing) {
84
- return;
85
- }
86
- editing = false;
87
- rt.textContent = annotation;
88
- restoreEditorFocus();
89
- };
90
- const submit = (value) => {
91
- if (!editing) {
92
- return;
93
- }
94
- if (!view.editable || value === annotation) {
95
- dismiss();
96
- return;
97
- }
98
- editing = false;
99
- const transaction = view.state.tr.addMark(
100
- range.from,
101
- range.to,
102
- rubyTextType.create({ ...range.mark.attrs, rt: value })
103
- );
104
- view.dispatch(transaction.setSelection(import_state.TextSelection.create(transaction.doc, range.to)));
105
- view.focus();
106
- };
107
- const openEditor = () => {
108
- var _a, _b;
109
- const renderEditor = (_a = options.renderAnnotationEditor) != null ? _a : defaultRenderAnnotationEditor;
110
- const element = renderEditor({ annotation, submit, dismiss, editor: options.editor });
111
- rt.replaceChildren(element);
112
- const focusTarget = (_b = element.querySelector("[autofocus]")) != null ? _b : element;
113
- focusTarget.focus();
114
- };
115
- rt.addEventListener("click", () => {
116
- if (editing || !view.editable) {
117
- return;
118
- }
119
- editing = true;
120
- openEditor();
121
- });
122
- return rt;
38
+ const rt = document.createElement("rt");
39
+ rt.contentEditable = "false";
40
+ rt.textContent = annotation;
41
+ let editing = false;
42
+ const restoreEditorFocus = () => {
43
+ view.dispatch(view.state.tr.setSelection(_tiptap_pm_state.TextSelection.create(view.state.doc, range.to)));
44
+ view.focus();
45
+ };
46
+ if (!options.allowClickToEdit || !view.editable) return rt;
47
+ editSessions.set(rt, () => {
48
+ editing = false;
49
+ });
50
+ const dismiss = () => {
51
+ if (!editing) return;
52
+ editing = false;
53
+ rt.textContent = annotation;
54
+ restoreEditorFocus();
55
+ };
56
+ const submit = (value) => {
57
+ if (!editing) return;
58
+ if (!view.editable || value === annotation) {
59
+ dismiss();
60
+ return;
61
+ }
62
+ editing = false;
63
+ const transaction = view.state.tr.addMark(range.from, range.to, rubyTextType.create({
64
+ ...range.mark.attrs,
65
+ rt: value
66
+ }));
67
+ view.dispatch(transaction.setSelection(_tiptap_pm_state.TextSelection.create(transaction.doc, range.to)));
68
+ view.focus();
69
+ };
70
+ const openEditor = () => {
71
+ var _options$renderAnnota, _element$querySelecto;
72
+ const element = ((_options$renderAnnota = options.renderAnnotationEditor) !== null && _options$renderAnnota !== void 0 ? _options$renderAnnota : defaultRenderAnnotationEditor)({
73
+ annotation,
74
+ submit,
75
+ dismiss,
76
+ editor: options.editor
77
+ });
78
+ rt.replaceChildren(element);
79
+ ((_element$querySelecto = element.querySelector("[autofocus]")) !== null && _element$querySelecto !== void 0 ? _element$querySelecto : element).focus();
80
+ };
81
+ rt.addEventListener("click", () => {
82
+ if (editing || !view.editable) return;
83
+ editing = true;
84
+ openEditor();
85
+ });
86
+ return rt;
123
87
  }
88
+ /** Gets the mark of ruby text from the given node, if it exists. */
124
89
  function getRubyTextMark(node, rubyTextType) {
125
- return node.isText ? node.marks.find((candidate) => candidate.type === rubyTextType) : void 0;
90
+ return node.isText ? node.marks.find((candidate) => candidate.type === rubyTextType) : void 0;
126
91
  }
92
+ /** Adds a ruby text range to an existing array of ranges. */
127
93
  function addRange(ranges, range) {
128
- if (range) {
129
- ranges.push(range);
130
- }
94
+ if (range) ranges.push(range);
131
95
  }
96
+ /** Can the current range be extended with the given mark at the given position? */
132
97
  function canExtendRange(range, mark, pos) {
133
- return (range == null ? void 0 : range.to) === pos && range.mark.eq(mark);
98
+ return (range === null || range === void 0 ? void 0 : range.to) === pos && range.mark.eq(mark);
134
99
  }
100
+ /**
101
+ * Gets the next ruby text range based on the current node and position.
102
+ * @param ranges The array of ruby text ranges to add to.
103
+ * @param range The current ruby text range being built.
104
+ * @param node The current ProseMirror node being processed.
105
+ * @param pos The position of the current node in the document.
106
+ * @param rubyTextType The MarkType for ruby text.
107
+ * @returns The updated ruby text range, or null if the current node does not have a ruby text mark.
108
+ */
135
109
  function getNextRange(ranges, range, node, pos, rubyTextType) {
136
- const mark = getRubyTextMark(node, rubyTextType);
137
- if (!mark) {
138
- addRange(ranges, range);
139
- return null;
140
- }
141
- if (range && canExtendRange(range, mark, pos)) {
142
- range.to += node.nodeSize;
143
- return range;
144
- }
145
- addRange(ranges, range);
146
- return { from: pos, to: pos + node.nodeSize, mark };
110
+ const mark = getRubyTextMark(node, rubyTextType);
111
+ if (!mark) {
112
+ addRange(ranges, range);
113
+ return null;
114
+ }
115
+ if (range && canExtendRange(range, mark, pos)) {
116
+ range.to += node.nodeSize;
117
+ return range;
118
+ }
119
+ addRange(ranges, range);
120
+ return {
121
+ from: pos,
122
+ to: pos + node.nodeSize,
123
+ mark
124
+ };
147
125
  }
148
126
  function getRubyTextRanges(doc, rubyTextType) {
149
- const ranges = [];
150
- let range = null;
151
- doc.descendants((node, pos) => {
152
- range = getNextRange(ranges, range, node, pos, rubyTextType);
153
- });
154
- addRange(ranges, range);
155
- return ranges;
127
+ const ranges = [];
128
+ let range = null;
129
+ doc.descendants((node, pos) => {
130
+ range = getNextRange(ranges, range, node, pos, rubyTextType);
131
+ });
132
+ addRange(ranges, range);
133
+ return ranges;
156
134
  }
157
- var STOPPED_EVENT_TYPES = /* @__PURE__ */ new Set([
158
- "click",
159
- "dblclick",
160
- "contextmenu",
161
- "mousedown",
162
- "mouseup",
163
- "pointerdown",
164
- "pointerup",
165
- "touchstart",
166
- "touchend",
167
- "keydown",
168
- "keypress",
169
- "keyup",
170
- "input",
171
- "beforeinput",
172
- "compositionstart",
173
- "compositionupdate",
174
- "compositionend",
175
- "paste",
176
- "cut",
177
- "copy",
178
- "focus",
179
- "blur",
180
- "focusin",
181
- "focusout"
135
+ const STOPPED_EVENT_TYPES = /* @__PURE__ */ new Set([
136
+ "click",
137
+ "dblclick",
138
+ "contextmenu",
139
+ "mousedown",
140
+ "mouseup",
141
+ "pointerdown",
142
+ "pointerup",
143
+ "touchstart",
144
+ "touchend",
145
+ "keydown",
146
+ "keypress",
147
+ "keyup",
148
+ "input",
149
+ "beforeinput",
150
+ "compositionstart",
151
+ "compositionupdate",
152
+ "compositionend",
153
+ "paste",
154
+ "cut",
155
+ "copy",
156
+ "focus",
157
+ "blur",
158
+ "focusin",
159
+ "focusout"
182
160
  ]);
183
161
  function createDecorations(doc, rubyTextType, options) {
184
- const decorations = getRubyTextRanges(doc, rubyTextType).map((range) => {
185
- var _a;
186
- const annotation = (_a = range.mark.attrs.rt) != null ? _a : "";
187
- return import_view.Decoration.widget(
188
- range.to,
189
- (view) => createRtElement(annotation, range, rubyTextType, view, options),
190
- {
191
- key: `ruby-text-${range.from}-${range.to}-${annotation}`,
192
- marks: [range.mark],
193
- side: 1,
194
- stopEvent: (event) => options.allowClickToEdit && STOPPED_EVENT_TYPES.has(event.type),
195
- destroy: (node) => {
196
- var _a2;
197
- return (_a2 = editSessions.get(node)) == null ? void 0 : _a2();
198
- }
199
- }
200
- );
201
- });
202
- return import_view.DecorationSet.create(doc, decorations);
162
+ const decorations = getRubyTextRanges(doc, rubyTextType).map((range) => {
163
+ var _range$mark$attrs$rt;
164
+ const annotation = (_range$mark$attrs$rt = range.mark.attrs.rt) !== null && _range$mark$attrs$rt !== void 0 ? _range$mark$attrs$rt : "";
165
+ return _tiptap_pm_view.Decoration.widget(range.to, (view) => createRtElement(annotation, range, rubyTextType, view, options), {
166
+ key: `ruby-text-${range.from}-${range.to}-${annotation}`,
167
+ marks: [range.mark],
168
+ side: 1,
169
+ stopEvent: (event) => options.allowClickToEdit && STOPPED_EVENT_TYPES.has(event.type),
170
+ destroy: (node) => {
171
+ var _editSessions$get;
172
+ return (_editSessions$get = editSessions.get(node)) === null || _editSessions$get === void 0 ? void 0 : _editSessions$get();
173
+ }
174
+ });
175
+ });
176
+ return _tiptap_pm_view.DecorationSet.create(doc, decorations);
203
177
  }
204
178
  function RubyTextDecorationPlugin(rubyTextType, options) {
205
- return new import_state.Plugin({
206
- key: rubyTextDecorationPluginKey,
207
- state: {
208
- init: (_, state) => createDecorations(state.doc, rubyTextType, options),
209
- apply: (transaction, decorations) => transaction.docChanged ? createDecorations(transaction.doc, rubyTextType, options) : decorations.map(transaction.mapping, transaction.doc)
210
- },
211
- props: {
212
- decorations: (state) => rubyTextDecorationPluginKey.getState(state)
213
- }
214
- });
179
+ return new _tiptap_pm_state.Plugin({
180
+ key: rubyTextDecorationPluginKey,
181
+ state: {
182
+ init: (_, state) => createDecorations(state.doc, rubyTextType, options),
183
+ apply: (transaction, decorations) => transaction.docChanged ? createDecorations(transaction.doc, rubyTextType, options) : decorations.map(transaction.mapping, transaction.doc)
184
+ },
185
+ props: { decorations: (state) => rubyTextDecorationPluginKey.getState(state) }
186
+ });
215
187
  }
216
-
217
- // src/ruby-text.ts
218
- var RubyText = import_core.Mark.create({
219
- name: "rubyText",
220
- inclusive: false,
221
- addOptions() {
222
- return {
223
- HTMLAttributes: {},
224
- allowClickToEdit: true,
225
- renderAnnotationEditor: void 0
226
- };
227
- },
228
- addAttributes() {
229
- return {
230
- rt: {
231
- default: null
232
- }
233
- };
234
- },
235
- parseHTML() {
236
- return [
237
- {
238
- tag: "ruby",
239
- contentElement: (node) => {
240
- const rb = node.querySelector("rb");
241
- if (rb) {
242
- return rb;
243
- }
244
- const surrogate = document.createElement("span");
245
- Array.from(node.childNodes).forEach((child) => {
246
- if (child.nodeName !== "RT" && child.nodeName !== "RP") {
247
- surrogate.appendChild(child.cloneNode(true));
248
- }
249
- });
250
- return surrogate;
251
- },
252
- getAttrs: (node) => {
253
- var _a;
254
- const rt = node.querySelector("rt");
255
- if (!rt) {
256
- return false;
257
- }
258
- return { rt: (_a = rt.textContent) != null ? _a : "" };
259
- }
260
- }
261
- ];
262
- },
263
- renderHTML({ HTMLAttributes, mark }) {
264
- return [
265
- "ruby",
266
- (0, import_core.mergeAttributes)(this.options.HTMLAttributes, HTMLAttributes),
267
- ["rb", 0],
268
- ...mark.attrs.rt == null ? [] : [["rt", { contenteditable: "false" }, mark.attrs.rt]]
269
- ];
270
- },
271
- addCommands() {
272
- return {
273
- setRubyText: (attributes) => ({ commands }) => {
274
- return commands.setMark(this.name, attributes);
275
- },
276
- toggleRubyText: (attributes) => ({ commands }) => {
277
- return commands.toggleMark(this.name, attributes, { extendEmptyMarkRange: true });
278
- },
279
- unsetRubyText: () => ({ commands }) => {
280
- return commands.unsetMark(this.name, { extendEmptyMarkRange: true });
281
- }
282
- };
283
- },
284
- addProseMirrorPlugins() {
285
- return [
286
- RubyTextDecorationPlugin(this.type, {
287
- editor: this.editor,
288
- allowClickToEdit: this.options.allowClickToEdit,
289
- renderAnnotationEditor: this.options.renderAnnotationEditor
290
- })
291
- ];
292
- },
293
- addMarkView() {
294
- return ({ HTMLAttributes }) => {
295
- const ruby = document.createElement("ruby");
296
- Object.entries((0, import_core.mergeAttributes)(this.options.HTMLAttributes, HTMLAttributes)).forEach(
297
- ([key, value]) => {
298
- if (value != null) {
299
- ruby.setAttribute(key, String(value));
300
- }
301
- }
302
- );
303
- return {
304
- dom: ruby,
305
- contentDOM: ruby
306
- };
307
- };
308
- }
188
+ //#endregion
189
+ //#region src/ruby-text.ts
190
+ /**
191
+ * This extension adds support for HTML ruby text annotations.
192
+ * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ruby
193
+ */
194
+ const RubyText = _tiptap_core.Mark.create({
195
+ name: "rubyText",
196
+ inclusive: false,
197
+ addOptions() {
198
+ return {
199
+ HTMLAttributes: {},
200
+ allowClickToEdit: true,
201
+ renderAnnotationEditor: void 0
202
+ };
203
+ },
204
+ addAttributes() {
205
+ return { rt: { default: null } };
206
+ },
207
+ parseHTML() {
208
+ return [{
209
+ tag: "ruby",
210
+ contentElement: (node) => {
211
+ const rb = node.querySelector("rb");
212
+ if (rb) return rb;
213
+ const surrogate = document.createElement("span");
214
+ Array.from(node.childNodes).forEach((child) => {
215
+ if (child.nodeName !== "RT" && child.nodeName !== "RP") surrogate.appendChild(child.cloneNode(true));
216
+ });
217
+ return surrogate;
218
+ },
219
+ getAttrs: (node) => {
220
+ var _rt$textContent;
221
+ const rt = node.querySelector("rt");
222
+ if (!rt) return false;
223
+ return { rt: (_rt$textContent = rt.textContent) !== null && _rt$textContent !== void 0 ? _rt$textContent : "" };
224
+ }
225
+ }];
226
+ },
227
+ renderHTML({ HTMLAttributes, mark }) {
228
+ return [
229
+ "ruby",
230
+ (0, _tiptap_core.mergeAttributes)(this.options.HTMLAttributes, HTMLAttributes),
231
+ ["rb", 0],
232
+ ...mark.attrs.rt == null ? [] : [[
233
+ "rt",
234
+ { contenteditable: "false" },
235
+ mark.attrs.rt
236
+ ]]
237
+ ];
238
+ },
239
+ addCommands() {
240
+ return {
241
+ setRubyText: (attributes) => ({ commands }) => {
242
+ return commands.setMark(this.name, attributes);
243
+ },
244
+ toggleRubyText: (attributes) => ({ commands }) => {
245
+ return commands.toggleMark(this.name, attributes, { extendEmptyMarkRange: true });
246
+ },
247
+ unsetRubyText: () => ({ commands }) => {
248
+ return commands.unsetMark(this.name, { extendEmptyMarkRange: true });
249
+ }
250
+ };
251
+ },
252
+ addProseMirrorPlugins() {
253
+ return [RubyTextDecorationPlugin(this.type, {
254
+ editor: this.editor,
255
+ allowClickToEdit: this.options.allowClickToEdit,
256
+ renderAnnotationEditor: this.options.renderAnnotationEditor
257
+ })];
258
+ },
259
+ addMarkView() {
260
+ return ({ HTMLAttributes }) => {
261
+ const ruby = document.createElement("ruby");
262
+ Object.entries((0, _tiptap_core.mergeAttributes)(this.options.HTMLAttributes, HTMLAttributes)).forEach(([key, value]) => {
263
+ if (value != null) ruby.setAttribute(key, String(value));
264
+ });
265
+ return {
266
+ dom: ruby,
267
+ contentDOM: ruby
268
+ };
269
+ };
270
+ }
309
271
  });
272
+ //#endregion
273
+ //#region src/index.ts
274
+ var src_default = RubyText;
275
+ //#endregion
276
+ exports.RubyText = RubyText;
277
+ exports.default = src_default;
310
278
 
311
- // src/index.ts
312
- var index_default = RubyText;
313
- // Annotate the CommonJS export names for ESM import in node:
314
- 0 && (module.exports = {
315
- RubyText
316
- });
317
279
  //# sourceMappingURL=index.cjs.map