@seorii/tiptap 0.3.0-next.9 → 0.4.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.
Files changed (55) hide show
  1. package/dist/i18n/index.d.ts +106 -6
  2. package/dist/i18n/index.js +56 -11
  3. package/dist/index.d.ts +2 -1
  4. package/dist/index.js +2 -1
  5. package/dist/plugin/command/emoji.d.ts +3 -17
  6. package/dist/plugin/command/emoji.js +51 -24
  7. package/dist/plugin/command/stores.svelte.d.ts +52 -0
  8. package/dist/plugin/command/stores.svelte.js +69 -0
  9. package/dist/plugin/command/suggest.d.ts +6 -19
  10. package/dist/plugin/command/suggest.js +149 -49
  11. package/dist/plugin/embed.d.ts +2 -2
  12. package/dist/plugin/embed.js +6 -2
  13. package/dist/plugin/iframe.js +1 -1
  14. package/dist/plugin/image/dragdrop.d.ts +2 -0
  15. package/dist/plugin/image/dragdrop.js +126 -15
  16. package/dist/plugin/image/index.js +4 -3
  17. package/dist/plugin/indent.js +0 -1
  18. package/dist/plugin/orderedlist/index.d.ts +1 -1
  19. package/dist/plugin/orderedlist/index.js +1 -1
  20. package/dist/plugin/orderedlist/{korean.scss → korean.css} +2 -2
  21. package/dist/plugin/resize/index.d.ts +8 -0
  22. package/dist/plugin/resize/index.js +454 -0
  23. package/dist/plugin/table/index.d.ts +1 -1
  24. package/dist/plugin/table/index.js +19 -11
  25. package/dist/plugin/table/style/{cell.scss → cell.css} +6 -5
  26. package/dist/plugin/table/style/{grip.scss → grip.css} +14 -19
  27. package/dist/plugin/table/style/resize.css +28 -0
  28. package/dist/plugin/table/style/{table.scss → table.css} +15 -17
  29. package/dist/plugin/table/style.css +4 -0
  30. package/dist/plugin/table/tableCell/index.js +2 -4
  31. package/dist/plugin/table/tableHeader/index.js +1 -2
  32. package/dist/plugin/upload/skeleton/UploadSkeleton.svelte +97 -0
  33. package/dist/plugin/upload/skeleton/UploadSkeleton.svelte.d.ts +5 -0
  34. package/dist/plugin/upload/skeleton/index.d.ts +30 -0
  35. package/dist/plugin/upload/skeleton/index.js +147 -0
  36. package/dist/plugin/youtube.js +1 -1
  37. package/dist/tiptap/Bubble.svelte +231 -92
  38. package/dist/tiptap/Bubble.svelte.d.ts +9 -6
  39. package/dist/tiptap/Command.svelte +160 -158
  40. package/dist/tiptap/Command.svelte.d.ts +2 -3
  41. package/dist/tiptap/Floating.svelte +51 -24
  42. package/dist/tiptap/Floating.svelte.d.ts +1 -0
  43. package/dist/tiptap/TipTap.svelte +302 -140
  44. package/dist/tiptap/TipTap.svelte.d.ts +10 -3
  45. package/dist/tiptap/ToolbarButton.svelte +30 -10
  46. package/dist/tiptap/ToolbarButton.svelte.d.ts +10 -6
  47. package/dist/tiptap/setMath.d.ts +2 -1
  48. package/dist/tiptap/setMath.js +74 -12
  49. package/dist/tiptap/tiptap.d.ts +9 -1
  50. package/dist/tiptap/tiptap.js +172 -16
  51. package/package.json +63 -57
  52. package/dist/plugin/command/stores.d.ts +0 -13
  53. package/dist/plugin/command/stores.js +0 -7
  54. package/dist/plugin/table/style/resize.scss +0 -26
  55. package/dist/plugin/table/style.scss +0 -4
@@ -0,0 +1,454 @@
1
+ import { Extension } from '@tiptap/core';
2
+ import { NodeSelection, Plugin, PluginKey } from '@tiptap/pm/state';
3
+ import { Decoration, DecorationSet } from '@tiptap/pm/view';
4
+ const resizableTypes = ['image', 'iframe', 'embed', 'tiptap-midibus'];
5
+ const typeSet = new Set(resizableTypes);
6
+ const pluginKey = new PluginKey('tiptap-media-height-resize');
7
+ const defaultHeight = {
8
+ image: 360,
9
+ iframe: 600,
10
+ embed: 500,
11
+ 'tiptap-midibus': 600,
12
+ attr: 420
13
+ };
14
+ const minHeight = {
15
+ image: 120,
16
+ iframe: 180,
17
+ embed: 200,
18
+ 'tiptap-midibus': 220,
19
+ attr: 160
20
+ };
21
+ const maxHeight = 1600;
22
+ function isResizableType(value) {
23
+ return typeSet.has(value);
24
+ }
25
+ function clamp(value, min, max) {
26
+ return Math.min(max, Math.max(min, value));
27
+ }
28
+ function parseNumericSize(value) {
29
+ if (typeof value === 'number')
30
+ return Number.isFinite(value) ? value : null;
31
+ if (typeof value !== 'string')
32
+ return null;
33
+ const trimmed = value.trim();
34
+ if (!trimmed)
35
+ return null;
36
+ const normalized = trimmed.toLowerCase().endsWith('px') ? trimmed.slice(0, -2) : trimmed;
37
+ const parsed = Number.parseFloat(normalized);
38
+ return Number.isFinite(parsed) ? parsed : null;
39
+ }
40
+ function normalizeStringAttr(value) {
41
+ if (typeof value !== 'string')
42
+ return null;
43
+ const trimmed = value.trim();
44
+ return trimmed || null;
45
+ }
46
+ function hasResizeHandler(value) {
47
+ if (typeof value === 'string') {
48
+ const normalized = value.trim().toLowerCase();
49
+ if (!normalized)
50
+ return true;
51
+ return !['false', '0', 'off', 'no'].includes(normalized);
52
+ }
53
+ return Boolean(value);
54
+ }
55
+ function normalizeNumericAttr(value) {
56
+ const parsed = parseNumericSize(value);
57
+ if (parsed === null)
58
+ return null;
59
+ return String(Math.round(parsed));
60
+ }
61
+ function normalizeWidthAttr(value) {
62
+ if (typeof value === 'number')
63
+ return String(Math.round(value));
64
+ if (typeof value !== 'string')
65
+ return null;
66
+ const trimmed = value.trim();
67
+ if (!trimmed)
68
+ return null;
69
+ if (trimmed.endsWith('%'))
70
+ return trimmed;
71
+ return normalizeNumericAttr(trimmed);
72
+ }
73
+ function resolveResizeMeta(node) {
74
+ const typeName = node.type.name;
75
+ const kind = isResizableType(typeName)
76
+ ? typeName
77
+ : hasResizeHandler(node.attrs.resizeHandler)
78
+ ? 'attr'
79
+ : null;
80
+ if (!kind)
81
+ return null;
82
+ const minFromAttr = parseNumericSize(node.attrs.minHeight);
83
+ const maxFromAttr = parseNumericSize(node.attrs.maxHeight);
84
+ const resolvedMin = minFromAttr !== null ? Math.max(1, minFromAttr) : minHeight[kind];
85
+ const resolvedMax = maxFromAttr !== null ? Math.max(resolvedMin, maxFromAttr) : maxHeight;
86
+ return {
87
+ kind,
88
+ typeName,
89
+ minHeight: resolvedMin,
90
+ maxHeight: resolvedMax
91
+ };
92
+ }
93
+ function resolveImageRatio(node, element) {
94
+ if (element instanceof HTMLImageElement && element.naturalWidth && element.naturalHeight) {
95
+ return element.naturalWidth / element.naturalHeight;
96
+ }
97
+ const rect = element.getBoundingClientRect();
98
+ if (rect.width > 0 && rect.height > 0) {
99
+ return rect.width / rect.height;
100
+ }
101
+ const width = parseNumericSize(node.attrs.width);
102
+ const height = parseNumericSize(node.attrs.height);
103
+ if (width && height)
104
+ return width / height;
105
+ return 1;
106
+ }
107
+ function resolveStartHeight(kind, node, element) {
108
+ const rect = element.getBoundingClientRect();
109
+ if (rect.height > 0)
110
+ return rect.height;
111
+ const fromAttr = parseNumericSize(node.attrs.height);
112
+ if (fromAttr !== null)
113
+ return fromAttr;
114
+ return defaultHeight[kind];
115
+ }
116
+ function resolveTargetElement(view, pos, resizeMeta, node) {
117
+ const nodeDom = view.nodeDOM(pos);
118
+ if (!(nodeDom instanceof HTMLElement))
119
+ return null;
120
+ if (resizeMeta.kind === 'image') {
121
+ if (nodeDom instanceof HTMLImageElement)
122
+ return nodeDom;
123
+ return nodeDom.querySelector('img');
124
+ }
125
+ if (resizeMeta.kind === 'iframe') {
126
+ if (nodeDom instanceof HTMLIFrameElement)
127
+ return nodeDom;
128
+ return nodeDom.querySelector('iframe');
129
+ }
130
+ if (resizeMeta.kind === 'embed') {
131
+ return (nodeDom.querySelector('[data-tiptap-pdf-container]') ||
132
+ nodeDom.querySelector('embed') ||
133
+ nodeDom);
134
+ }
135
+ if (resizeMeta.kind === 'attr') {
136
+ const preferredSelector = normalizeStringAttr(node.attrs.resizeTarget);
137
+ if (preferredSelector) {
138
+ try {
139
+ const preferredTarget = nodeDom.querySelector(preferredSelector);
140
+ if (preferredTarget)
141
+ return preferredTarget;
142
+ }
143
+ catch {
144
+ // ignore invalid selector values
145
+ }
146
+ }
147
+ return (nodeDom.querySelector('[data-tiptap-resize-target]') ||
148
+ nodeDom.querySelector('[data-resize-target]') ||
149
+ nodeDom.querySelector('iframe, embed, img') ||
150
+ nodeDom);
151
+ }
152
+ return (nodeDom.querySelector('[data-tiptap-midibus-frame]') ||
153
+ nodeDom.querySelector('iframe') ||
154
+ nodeDom);
155
+ }
156
+ function buildResizeAttrs(kind, node, height, imageRatio) {
157
+ const attrs = { ...node.attrs };
158
+ const roundedHeight = String(Math.round(height));
159
+ if (kind === 'image') {
160
+ const roundedWidth = String(Math.max(1, Math.round(height * imageRatio)));
161
+ return { ...attrs, width: roundedWidth, height: roundedHeight };
162
+ }
163
+ if (kind === 'iframe' || kind === 'embed') {
164
+ return { ...attrs, width: attrs.width || '100%', height: roundedHeight };
165
+ }
166
+ return { ...attrs, height: roundedHeight };
167
+ }
168
+ function createResizeHandleDecoration(nodePos, widgetPos, resizeMeta) {
169
+ return Decoration.widget(widgetPos, () => {
170
+ const anchor = document.createElement('div');
171
+ anchor.className = 'tiptap-media-resize-anchor';
172
+ const button = document.createElement('button');
173
+ button.type = 'button';
174
+ button.className = 'tiptap-media-resize-handle';
175
+ button.dataset.resizePos = String(nodePos);
176
+ button.dataset.resizeKind = resizeMeta.kind;
177
+ button.setAttribute('aria-label', 'Resize media height');
178
+ anchor.append(button);
179
+ return anchor;
180
+ }, { side: 1, key: `media-resize-${nodePos}-${resizeMeta.typeName}-${resizeMeta.kind}` });
181
+ }
182
+ function tryCreateNodeSelection(doc, pos) {
183
+ if (pos < 0 || pos > doc.content.size)
184
+ return null;
185
+ const node = doc.nodeAt(pos);
186
+ if (!node || node.type.spec.selectable === false)
187
+ return null;
188
+ try {
189
+ return NodeSelection.create(doc, pos);
190
+ }
191
+ catch {
192
+ return null;
193
+ }
194
+ }
195
+ export default Extension.create({
196
+ name: 'tiptap-media-height-resize',
197
+ addOptions() {
198
+ return {
199
+ attributeTypes: [],
200
+ showHandleAlways: true,
201
+ showHandleOnActive: true
202
+ };
203
+ },
204
+ addGlobalAttributes() {
205
+ const attributeTypes = Array.from(new Set([...(this.options.attributeTypes || []), ...resizableTypes]));
206
+ return [
207
+ {
208
+ types: ['image'],
209
+ attributes: {
210
+ width: {
211
+ default: null,
212
+ parseHTML: (element) => normalizeWidthAttr(element.getAttribute('width') || element.style.width),
213
+ renderHTML: (attributes) => {
214
+ const width = normalizeWidthAttr(attributes.width);
215
+ return width ? { width } : {};
216
+ }
217
+ },
218
+ height: {
219
+ default: null,
220
+ parseHTML: (element) => normalizeNumericAttr(element.getAttribute('height') || element.style.height),
221
+ renderHTML: (attributes) => {
222
+ const height = normalizeNumericAttr(attributes.height);
223
+ return height ? { height } : {};
224
+ }
225
+ }
226
+ }
227
+ },
228
+ {
229
+ types: ['iframe'],
230
+ attributes: {
231
+ width: {
232
+ default: '100%',
233
+ parseHTML: (element) => normalizeWidthAttr(element.getAttribute('width') || element.style.width) || '100%',
234
+ renderHTML: (attributes) => {
235
+ const width = normalizeWidthAttr(attributes.width) || '100%';
236
+ return { width };
237
+ }
238
+ },
239
+ height: {
240
+ default: '600',
241
+ parseHTML: (element) => normalizeNumericAttr(element.getAttribute('height') || element.style.height) || '600',
242
+ renderHTML: (attributes) => {
243
+ const height = normalizeNumericAttr(attributes.height) || '600';
244
+ return { height };
245
+ }
246
+ }
247
+ }
248
+ },
249
+ {
250
+ types: attributeTypes,
251
+ attributes: {
252
+ resizeHandler: {
253
+ default: false,
254
+ parseHTML: (element) => hasResizeHandler(element.getAttribute('data-resize-handler') ||
255
+ element.getAttribute('resize-handler')),
256
+ renderHTML: (attributes) => hasResizeHandler(attributes.resizeHandler)
257
+ ? { 'data-resize-handler': 'true' }
258
+ : {}
259
+ },
260
+ resizeTarget: {
261
+ default: null,
262
+ parseHTML: (element) => normalizeStringAttr(element.getAttribute('data-resize-target')),
263
+ renderHTML: (attributes) => {
264
+ const resizeTarget = normalizeStringAttr(attributes.resizeTarget);
265
+ return resizeTarget ? { 'data-resize-target': resizeTarget } : {};
266
+ }
267
+ },
268
+ minHeight: {
269
+ default: null,
270
+ parseHTML: (element) => normalizeNumericAttr(element.getAttribute('data-resize-min-height')),
271
+ renderHTML: (attributes) => {
272
+ const minHeight = normalizeNumericAttr(attributes.minHeight);
273
+ return minHeight ? { 'data-resize-min-height': minHeight } : {};
274
+ }
275
+ },
276
+ maxHeight: {
277
+ default: null,
278
+ parseHTML: (element) => normalizeNumericAttr(element.getAttribute('data-resize-max-height')),
279
+ renderHTML: (attributes) => {
280
+ const maxHeight = normalizeNumericAttr(attributes.maxHeight);
281
+ return maxHeight ? { 'data-resize-max-height': maxHeight } : {};
282
+ }
283
+ }
284
+ }
285
+ }
286
+ ];
287
+ },
288
+ addProseMirrorPlugins() {
289
+ let removeDragListeners = null;
290
+ return [
291
+ new Plugin({
292
+ key: pluginKey,
293
+ appendTransaction: (transactions, _oldState, newState) => {
294
+ if (!transactions.some((tr) => tr.docChanged))
295
+ return null;
296
+ if (newState.selection instanceof NodeSelection)
297
+ return null;
298
+ const nodeBefore = newState.selection.$from.nodeBefore;
299
+ if (!nodeBefore)
300
+ return null;
301
+ const resizeMeta = resolveResizeMeta(nodeBefore);
302
+ if (!resizeMeta)
303
+ return null;
304
+ const typeName = nodeBefore.type.name;
305
+ const nodePos = newState.selection.from - nodeBefore.nodeSize;
306
+ if (nodePos < 0)
307
+ return null;
308
+ if (newState.doc.nodeAt(nodePos)?.type.name !== typeName)
309
+ return null;
310
+ const nodeSelection = tryCreateNodeSelection(newState.doc, nodePos);
311
+ if (!nodeSelection)
312
+ return null;
313
+ return newState.tr.setSelection(nodeSelection);
314
+ },
315
+ props: {
316
+ decorations: (state) => {
317
+ if (!this.editor.isEditable)
318
+ return DecorationSet.empty;
319
+ const showHandleAlways = this.options.showHandleAlways !== false;
320
+ const showHandleOnActive = this.options.showHandleOnActive !== false;
321
+ if (!showHandleAlways && !showHandleOnActive)
322
+ return DecorationSet.empty;
323
+ const decorations = [];
324
+ const handled = new Set();
325
+ if (showHandleAlways) {
326
+ state.doc.descendants((node, pos) => {
327
+ const resizeMeta = resolveResizeMeta(node);
328
+ if (!resizeMeta || resizeMeta.kind === 'image' || node.isInline)
329
+ return;
330
+ decorations.push(createResizeHandleDecoration(pos, pos + node.nodeSize, resizeMeta));
331
+ handled.add(pos);
332
+ });
333
+ }
334
+ if (showHandleOnActive && state.selection instanceof NodeSelection) {
335
+ const pos = state.selection.from;
336
+ const resizeMeta = resolveResizeMeta(state.selection.node);
337
+ if (resizeMeta && !handled.has(pos)) {
338
+ decorations.push(createResizeHandleDecoration(pos, pos + state.selection.node.nodeSize, resizeMeta));
339
+ }
340
+ }
341
+ if (!decorations.length)
342
+ return DecorationSet.empty;
343
+ return DecorationSet.create(state.doc, decorations);
344
+ },
345
+ handleDOMEvents: {
346
+ mousedown: (view, event) => {
347
+ if (!this.editor.isEditable)
348
+ return false;
349
+ if (!(event.target instanceof HTMLElement))
350
+ return false;
351
+ const handle = event.target.closest('.tiptap-media-resize-handle');
352
+ if (!handle)
353
+ return false;
354
+ const pos = Number.parseInt(handle.dataset.resizePos || '', 10);
355
+ if (!Number.isFinite(pos))
356
+ return false;
357
+ const node = view.state.doc.nodeAt(pos);
358
+ if (!node)
359
+ return false;
360
+ const resizeMeta = resolveResizeMeta(node);
361
+ if (!resizeMeta)
362
+ return false;
363
+ const resizeKind = handle.dataset.resizeKind;
364
+ if (resizeKind && resizeMeta.kind !== resizeKind)
365
+ return false;
366
+ const target = resolveTargetElement(view, pos, resizeMeta, node);
367
+ if (!target)
368
+ return false;
369
+ event.preventDefault();
370
+ event.stopPropagation();
371
+ const startY = event.clientY;
372
+ const startHeight = resolveStartHeight(resizeMeta.kind, node, target);
373
+ const imageRatio = resizeMeta.kind === 'image' ? resolveImageRatio(node, target) : 1;
374
+ const shouldShowProxy = resizeMeta.kind !== 'image';
375
+ let resizeProxy = null;
376
+ let restoreTarget = null;
377
+ let frame = 0;
378
+ let pendingHeight = startHeight;
379
+ if (shouldShowProxy && target.parentElement) {
380
+ const targetElement = target;
381
+ const originalDisplay = targetElement.style.display;
382
+ resizeProxy = document.createElement('div');
383
+ resizeProxy.className = 'tiptap-media-resize-proxy';
384
+ resizeProxy.style.height = `${Math.round(startHeight)}px`;
385
+ target.parentElement.insertBefore(resizeProxy, targetElement);
386
+ targetElement.style.display = 'none';
387
+ restoreTarget = () => {
388
+ targetElement.style.display = originalDisplay;
389
+ resizeProxy?.remove();
390
+ resizeProxy = null;
391
+ restoreTarget = null;
392
+ };
393
+ }
394
+ const dispatchHeight = (height) => {
395
+ const current = view.state.doc.nodeAt(pos);
396
+ if (!current || current.type.name !== resizeMeta.typeName)
397
+ return;
398
+ const currentMeta = resolveResizeMeta(current);
399
+ if (!currentMeta)
400
+ return;
401
+ const nextAttrs = buildResizeAttrs(currentMeta.kind, current, height, imageRatio);
402
+ const nextWidth = 'width' in nextAttrs ? nextAttrs.width : current.attrs.width;
403
+ if (nextAttrs.height === current.attrs.height && nextWidth === current.attrs.width)
404
+ return;
405
+ const tr = view.state.tr.setNodeMarkup(pos, current.type, nextAttrs);
406
+ view.dispatch(tr);
407
+ };
408
+ const previousCursor = document.body.style.cursor;
409
+ const previousSelect = document.body.style.userSelect;
410
+ document.body.style.cursor = 'ns-resize';
411
+ document.body.style.userSelect = 'none';
412
+ const onMove = (moveEvent) => {
413
+ const nextHeight = clamp(startHeight + moveEvent.clientY - startY, resizeMeta.minHeight, resizeMeta.maxHeight);
414
+ pendingHeight = nextHeight;
415
+ if (resizeProxy)
416
+ resizeProxy.style.height = `${Math.round(nextHeight)}px`;
417
+ if (shouldShowProxy)
418
+ return;
419
+ if (frame)
420
+ cancelAnimationFrame(frame);
421
+ frame = requestAnimationFrame(() => dispatchHeight(nextHeight));
422
+ };
423
+ const cleanup = () => {
424
+ if (frame)
425
+ cancelAnimationFrame(frame);
426
+ window.removeEventListener('mousemove', onMove);
427
+ window.removeEventListener('mouseup', onUp);
428
+ document.body.style.cursor = previousCursor;
429
+ document.body.style.userSelect = previousSelect;
430
+ restoreTarget?.();
431
+ removeDragListeners = null;
432
+ };
433
+ const onUp = () => {
434
+ if (shouldShowProxy)
435
+ dispatchHeight(pendingHeight);
436
+ cleanup();
437
+ };
438
+ removeDragListeners?.();
439
+ window.addEventListener('mousemove', onMove);
440
+ window.addEventListener('mouseup', onUp);
441
+ removeDragListeners = cleanup;
442
+ return true;
443
+ }
444
+ }
445
+ },
446
+ view: () => ({
447
+ destroy: () => {
448
+ removeDragListeners?.();
449
+ }
450
+ })
451
+ })
452
+ ];
453
+ }
454
+ });
@@ -1,3 +1,3 @@
1
- import './style.scss';
1
+ import './style.css';
2
2
  declare const _default: import("@tiptap/core").Node<import("@tiptap/extension-table").TableOptions, any>;
3
3
  export default _default;
@@ -1,9 +1,20 @@
1
- import BuiltInTable from '@tiptap/extension-table';
1
+ import { Table as BuiltInTable } from '@tiptap/extension-table';
2
2
  import { Plugin } from 'prosemirror-state';
3
3
  import { tableEditing, columnResizing } from 'prosemirror-tables';
4
4
  import { Decoration, DecorationSet } from 'prosemirror-view';
5
5
  import deleteTable from './deleteTable';
6
- import './style.scss';
6
+ import './style.css';
7
+ const resolveTableElement = (view, pos) => {
8
+ if (!view)
9
+ return null;
10
+ const dom = view.nodeDOM(pos);
11
+ if (dom instanceof HTMLTableElement)
12
+ return dom;
13
+ if (!(dom instanceof HTMLElement))
14
+ return null;
15
+ const table = dom.querySelector('table');
16
+ return table instanceof HTMLTableElement ? table : null;
17
+ };
7
18
  export default BuiltInTable.extend({
8
19
  renderHTML() {
9
20
  return [
@@ -17,7 +28,6 @@ export default BuiltInTable.extend({
17
28
  ];
18
29
  },
19
30
  addProseMirrorPlugins() {
20
- const { isEditable } = this.editor;
21
31
  return [
22
32
  tableEditing(),
23
33
  columnResizing({}),
@@ -26,25 +36,23 @@ export default BuiltInTable.extend({
26
36
  decorations: (state) => {
27
37
  const { doc } = state;
28
38
  const decorations = [];
29
- let index = 0;
39
+ const isEditable = this.editor.isEditable;
40
+ const view = this.editor.view;
30
41
  doc.descendants((node, pos) => {
31
42
  if (node.type.name !== this.name)
32
43
  return;
33
- const elements = document.getElementsByClassName('as-table');
34
- const table = elements[index];
44
+ const table = resolveTableElement(view, pos);
35
45
  if (!table)
36
46
  return;
37
- if (!isEditable)
38
- table.classList.add('is-readonly');
39
- const element = table.parentElement;
40
- const shadowRight = !!(element && element.scrollWidth > element.clientWidth);
47
+ table.classList.toggle('is-readonly', !isEditable);
48
+ const scrollable = table.parentElement;
49
+ const shadowRight = !!(scrollable instanceof HTMLElement && scrollable.scrollWidth > scrollable.clientWidth);
41
50
  if (shadowRight)
42
51
  decorations.push(Decoration.widget(pos + 1, () => {
43
52
  const shadow = document.createElement('div');
44
53
  shadow.className = `scrollable-shadow right ${isEditable ? 'is-editable' : ''}`;
45
54
  return shadow;
46
55
  }));
47
- index++;
48
56
  });
49
57
  return DecorationSet.create(doc, decorations);
50
58
  }
@@ -1,6 +1,6 @@
1
1
  .ProseMirror table {
2
- td,
3
- th {
2
+ & td,
3
+ & th {
4
4
  position: relative;
5
5
  min-width: 8px;
6
6
  padding: 4px 8px;
@@ -9,9 +9,10 @@
9
9
  border: 1px solid var(--primary-light3) !important;
10
10
  transition: all 0.1s ease-in-out;
11
11
  font-weight: normal;
12
+ }
12
13
 
13
- > * {
14
- margin-bottom: 0;
15
- }
14
+ & td > *,
15
+ & th > * {
16
+ margin-bottom: 0;
16
17
  }
17
18
  }
@@ -1,19 +1,16 @@
1
- $grip-margin: 3px;
2
-
3
1
  .ProseMirror table {
4
- .grip-column {
2
+ & .grip-column {
5
3
  position: absolute;
6
4
  top: -0.7em;
7
5
  left: 0;
8
6
  z-index: 10;
9
7
  display: block;
10
- width: calc(100% - 2 * #{$grip-margin});
8
+ width: calc(100% - 6px);
11
9
  height: 0.4em;
12
- margin: 0 $grip-margin 3px $grip-margin;
10
+ margin: 0 3px 3px 3px;
13
11
  background: var(--primary-light3);
14
12
  opacity: 0;
15
13
  border-radius: 10px;
16
-
17
14
  transition: all 0.1s ease-in-out;
18
15
 
19
16
  &:hover,
@@ -22,19 +19,18 @@ $grip-margin: 3px;
22
19
  }
23
20
  }
24
21
 
25
- .grip-row {
22
+ & .grip-row {
26
23
  position: absolute;
27
24
  top: 0;
28
25
  left: -0.7em;
29
26
  z-index: 10;
30
27
  display: block;
31
28
  width: 0.4em;
32
- height: calc(100% - 2 * #{$grip-margin});
33
- margin: $grip-margin 3px $grip-margin 0;
29
+ height: calc(100% - 6px);
30
+ margin: 3px 3px 3px 0;
34
31
  background: var(--primary-light3);
35
32
  opacity: 0;
36
33
  border-radius: 10px;
37
-
38
34
  transition: all 0.1s ease-in-out;
39
35
 
40
36
  &:hover,
@@ -43,7 +39,7 @@ $grip-margin: 3px;
43
39
  }
44
40
  }
45
41
 
46
- .grip-table {
42
+ & .grip-table {
47
43
  position: absolute;
48
44
  top: -0.8em;
49
45
  left: -0.8em;
@@ -54,7 +50,6 @@ $grip-margin: 3px;
54
50
  background: var(--primary-light3);
55
51
  border-radius: 50%;
56
52
  opacity: 0;
57
-
58
53
  transition: all 0.1s ease-in-out;
59
54
 
60
55
  &:hover,
@@ -62,13 +57,13 @@ $grip-margin: 3px;
62
57
  background: var(--primary-light6);
63
58
  }
64
59
  }
60
+ }
65
61
 
66
- .editable & {
67
- .grip-column,
68
- .grip-row,
69
- .grip-table {
70
- opacity: 1;
71
- cursor: pointer;
72
- }
62
+ .editable .ProseMirror table {
63
+ & .grip-column,
64
+ & .grip-row,
65
+ & .grip-table {
66
+ opacity: 1;
67
+ cursor: pointer;
73
68
  }
74
69
  }
@@ -0,0 +1,28 @@
1
+ .ProseMirror {
2
+ & table .column-resize-handle {
3
+ position: absolute;
4
+ top: 0;
5
+ right: -2px;
6
+ bottom: -2px;
7
+ width: 4px;
8
+ pointer-events: none;
9
+ background-color: var(--primary-light6);
10
+ opacity: 0;
11
+ }
12
+
13
+ &.resize-cursor {
14
+ pointer-events: none;
15
+ }
16
+ }
17
+
18
+ .editable .ProseMirror {
19
+ & table .column-resize-handle {
20
+ opacity: 1;
21
+ }
22
+
23
+ &.resize-cursor {
24
+ pointer-events: initial;
25
+ cursor: ew-resize;
26
+ cursor: col-resize; /* stylelint-disable declaration-block-no-duplicate-properties */
27
+ }
28
+ }