@licium/editor-plugin-details 3.2.13 → 3.2.14
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/toastui-editor-plugin-details.css +1 -1
- package/dist/toastui-editor-plugin-details.js +6287 -25
- package/package.json +2 -2
- package/src/index.ts +123 -29
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@licium/editor-plugin-details",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.14",
|
|
4
4
|
"description": "Details/Summary plugin for Toast UI Editor",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"nhn",
|
|
@@ -48,5 +48,5 @@
|
|
|
48
48
|
"publishConfig": {
|
|
49
49
|
"access": "public"
|
|
50
50
|
},
|
|
51
|
-
"gitHead": "
|
|
51
|
+
"gitHead": "474aa76d6fcc3523669c320a45286ac0cfe51a91"
|
|
52
52
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/* eslint-disable */
|
|
2
|
+
import { Plugin } from 'prosemirror-state';
|
|
2
3
|
import type { PluginContext, PluginInfo, I18n, MdNode } from '@licium/editor';
|
|
3
4
|
import { PluginOptions } from '@t/index';
|
|
4
5
|
import { addLangs } from './i18n/langs';
|
|
@@ -6,6 +7,63 @@ import './css/plugin.css';
|
|
|
6
7
|
|
|
7
8
|
const PREFIX = 'toastui-editor-';
|
|
8
9
|
|
|
10
|
+
// Factory function for the ProseMirror plugin (TUI Editor expects functions, not instances)
|
|
11
|
+
function createDetailsClickPlugin() {
|
|
12
|
+
return new Plugin({
|
|
13
|
+
props: {
|
|
14
|
+
handleClick(view, pos, event) {
|
|
15
|
+
const target = event.target as HTMLElement;
|
|
16
|
+
const summary = target.closest('summary');
|
|
17
|
+
|
|
18
|
+
if (summary) {
|
|
19
|
+
event.preventDefault();
|
|
20
|
+
|
|
21
|
+
const $pos = view.state.doc.resolve(pos);
|
|
22
|
+
let detailsNode = null;
|
|
23
|
+
let detailsPos = -1;
|
|
24
|
+
|
|
25
|
+
// Walk up depth to find the parent Details node
|
|
26
|
+
for (let d = $pos.depth; d >= 0; d -= 1) {
|
|
27
|
+
if ($pos.node(d).type.name === 'details') {
|
|
28
|
+
detailsNode = $pos.node(d);
|
|
29
|
+
detailsPos = $pos.before(d);
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (detailsNode && detailsPos !== -1) {
|
|
35
|
+
const node = detailsNode;
|
|
36
|
+
const htmlAttrs = (node.attrs && node.attrs.htmlAttrs) || {};
|
|
37
|
+
const isOpenAttr = node.attrs.open;
|
|
38
|
+
|
|
39
|
+
const isOpen = isOpenAttr === true || (htmlAttrs && htmlAttrs.open !== null && typeof htmlAttrs.open !== 'undefined');
|
|
40
|
+
|
|
41
|
+
console.log('[Details Plugin] Click intercepted. Toggling from:', isOpen);
|
|
42
|
+
|
|
43
|
+
const newHtmlAttrs = { ...htmlAttrs };
|
|
44
|
+
|
|
45
|
+
if (isOpen) {
|
|
46
|
+
delete newHtmlAttrs.open;
|
|
47
|
+
} else {
|
|
48
|
+
newHtmlAttrs.open = '';
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const newAttrs = {
|
|
52
|
+
...node.attrs,
|
|
53
|
+
open: !isOpen,
|
|
54
|
+
htmlAttrs: newHtmlAttrs,
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
view.dispatch(view.state.tr.setNodeMarkup(detailsPos, null, newAttrs));
|
|
58
|
+
return true; // Stop propagation, we handled it
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
9
67
|
function createToolbarItemOption(i18n: I18n) {
|
|
10
68
|
return {
|
|
11
69
|
name: 'details',
|
|
@@ -170,6 +228,7 @@ export default function detailsPlugin(
|
|
|
170
228
|
handleDetailsExit(context, event);
|
|
171
229
|
});
|
|
172
230
|
|
|
231
|
+
|
|
173
232
|
return {
|
|
174
233
|
toHTMLRenderers: {
|
|
175
234
|
htmlBlock: {
|
|
@@ -202,12 +261,74 @@ export default function detailsPlugin(
|
|
|
202
261
|
},
|
|
203
262
|
},
|
|
204
263
|
|
|
264
|
+
// Inject the ProseMirror plugin
|
|
265
|
+
wysiwygPlugins: [(() => {
|
|
266
|
+
console.log('[Details Plugin] Initializing ProseMirror plugin');
|
|
267
|
+
// @ts-ignore
|
|
268
|
+
if (typeof Plugin === 'undefined') {
|
|
269
|
+
console.error('[Details Plugin] CRITICAL: ProseMirror Plugin class is undefined');
|
|
270
|
+
return {};
|
|
271
|
+
}
|
|
272
|
+
return new Plugin({
|
|
273
|
+
props: {
|
|
274
|
+
handleClick(view, pos, event) {
|
|
275
|
+
const target = event.target as HTMLElement;
|
|
276
|
+
const summary = target.closest('summary');
|
|
277
|
+
|
|
278
|
+
if (summary) {
|
|
279
|
+
event.preventDefault();
|
|
280
|
+
|
|
281
|
+
const $pos = view.state.doc.resolve(pos);
|
|
282
|
+
let detailsNode = null;
|
|
283
|
+
let detailsPos = -1;
|
|
284
|
+
|
|
285
|
+
// Walk up depth to find the parent Details node
|
|
286
|
+
for (let d = $pos.depth; d >= 0; d -= 1) {
|
|
287
|
+
if ($pos.node(d).type.name === 'details') {
|
|
288
|
+
detailsNode = $pos.node(d);
|
|
289
|
+
detailsPos = $pos.before(d);
|
|
290
|
+
break;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if (detailsNode && detailsPos !== -1) {
|
|
295
|
+
const node = detailsNode;
|
|
296
|
+
const htmlAttrs = (node.attrs && node.attrs.htmlAttrs) || {};
|
|
297
|
+
const isOpenAttr = node.attrs.open;
|
|
298
|
+
|
|
299
|
+
const isOpen = isOpenAttr === true || (htmlAttrs && htmlAttrs.open !== null && typeof htmlAttrs.open !== 'undefined');
|
|
300
|
+
|
|
301
|
+
console.log('[Details Plugin] Click intercepted. Toggling from:', isOpen);
|
|
302
|
+
|
|
303
|
+
const newHtmlAttrs = { ...htmlAttrs };
|
|
304
|
+
|
|
305
|
+
if (isOpen) {
|
|
306
|
+
delete newHtmlAttrs.open;
|
|
307
|
+
} else {
|
|
308
|
+
newHtmlAttrs.open = '';
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
const newAttrs = {
|
|
312
|
+
...node.attrs,
|
|
313
|
+
open: !isOpen,
|
|
314
|
+
htmlAttrs: newHtmlAttrs,
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
view.dispatch(view.state.tr.setNodeMarkup(detailsPos, null, newAttrs));
|
|
318
|
+
return true; // Stop propagation
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
return false;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
}) as any],
|
|
205
326
|
|
|
206
327
|
wysiwygNodeViews: {
|
|
207
328
|
details: (node, view, getPos) => {
|
|
208
329
|
const dom = document.createElement('details');
|
|
209
330
|
const htmlAttrs = (node.attrs && node.attrs.htmlAttrs) || {};
|
|
210
|
-
const isOpenAttr = node.attrs.open;
|
|
331
|
+
const isOpenAttr = node.attrs.open;
|
|
211
332
|
|
|
212
333
|
// Check both direct attribute (priority) and htmlAttrs (legacy/plugin)
|
|
213
334
|
const isOpen = isOpenAttr === true || (htmlAttrs && htmlAttrs.open !== null && typeof htmlAttrs.open !== 'undefined');
|
|
@@ -216,34 +337,7 @@ export default function detailsPlugin(
|
|
|
216
337
|
dom.open = true;
|
|
217
338
|
}
|
|
218
339
|
|
|
219
|
-
|
|
220
|
-
const target = e.target as HTMLElement;
|
|
221
|
-
|
|
222
|
-
if (target.tagName === 'SUMMARY' || target.closest('summary')) {
|
|
223
|
-
e.preventDefault();
|
|
224
|
-
|
|
225
|
-
const pos = getPos();
|
|
226
|
-
|
|
227
|
-
if (typeof pos === 'number') {
|
|
228
|
-
const isOpen = htmlAttrs.open !== null && typeof htmlAttrs.open !== 'undefined';
|
|
229
|
-
const newHtmlAttrs = { ...htmlAttrs };
|
|
230
|
-
|
|
231
|
-
if (isOpen) {
|
|
232
|
-
delete newHtmlAttrs.open;
|
|
233
|
-
} else {
|
|
234
|
-
newHtmlAttrs.open = '';
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
const newAttrs = {
|
|
238
|
-
...node.attrs,
|
|
239
|
-
htmlAttrs: newHtmlAttrs,
|
|
240
|
-
};
|
|
241
|
-
|
|
242
|
-
view.dispatch(view.state.tr.setNodeMarkup(pos, null, newAttrs));
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
});
|
|
246
|
-
|
|
340
|
+
// Clean NodeView, no event listeners here
|
|
247
341
|
return { dom, contentDOM: dom };
|
|
248
342
|
},
|
|
249
343
|
},
|