@seorii/tiptap 0.4.3 → 0.4.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.
|
@@ -29,33 +29,37 @@ function matchItem(item, query, compactQuery) {
|
|
|
29
29
|
return [item.title, item.subtitle ?? '', ...(item.keywords ?? [])].some((value) => matchQuery(value, query, compactQuery));
|
|
30
30
|
}
|
|
31
31
|
function fixRange(editor, rawRange, split = '/') {
|
|
32
|
-
const
|
|
33
|
-
const
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
32
|
+
const doc = editor.state.doc;
|
|
33
|
+
const docSize = doc.content.size;
|
|
34
|
+
const range = {
|
|
35
|
+
from: Math.max(0, Math.min(rawRange.from, docSize)),
|
|
36
|
+
to: Math.max(0, Math.min(rawRange.to, docSize))
|
|
37
|
+
};
|
|
38
|
+
if (range.to < range.from)
|
|
39
|
+
range.to = range.from;
|
|
40
|
+
if (doc.textBetween(range.from, Math.min(range.from + 1, docSize)) !== split && range.from > 0) {
|
|
41
|
+
const minFrom = Math.max(0, range.from - 64);
|
|
42
|
+
for (let cursor = range.from; cursor > minFrom; cursor -= 1) {
|
|
43
|
+
const char = doc.textBetween(cursor - 1, cursor);
|
|
44
|
+
if (!char || /\s/.test(char))
|
|
43
45
|
break;
|
|
44
|
-
|
|
46
|
+
if (char !== split)
|
|
47
|
+
continue;
|
|
48
|
+
range.from = cursor - 1;
|
|
49
|
+
break;
|
|
45
50
|
}
|
|
46
|
-
range.from -= 1;
|
|
47
51
|
}
|
|
48
|
-
while (range.to <
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
catch {
|
|
53
|
-
range.to -= 1;
|
|
52
|
+
while (range.to < docSize) {
|
|
53
|
+
const next = doc.textBetween(range.to, range.to + 1);
|
|
54
|
+
if (!next || /\s/.test(next))
|
|
54
55
|
break;
|
|
55
|
-
|
|
56
|
+
range.to += 1;
|
|
56
57
|
}
|
|
57
58
|
return range;
|
|
58
59
|
}
|
|
60
|
+
function clampDocPos(editor, pos) {
|
|
61
|
+
return Math.max(0, Math.min(pos, editor.state.doc.content.size));
|
|
62
|
+
}
|
|
59
63
|
export function getDetail(editor, range, option) {
|
|
60
64
|
slashState.selection = () => {
|
|
61
65
|
editor.chain().focus().deleteRange(fixRange(editor, range)).run();
|
|
@@ -147,7 +151,18 @@ export const suggest = {
|
|
|
147
151
|
subtitle: i18n('imageInfo'),
|
|
148
152
|
keywords: createKeywords(['image', 'imageInfo']),
|
|
149
153
|
command: ({ editor, range }) => {
|
|
150
|
-
|
|
154
|
+
const fixedRange = fixRange(editor, range);
|
|
155
|
+
editor.chain().focus().deleteRange(fixedRange).run();
|
|
156
|
+
let insertAt = clampDocPos(editor, fixedRange.from);
|
|
157
|
+
let insertParagraph = true;
|
|
158
|
+
const { selection } = editor.state;
|
|
159
|
+
if (selection.empty &&
|
|
160
|
+
selection.$from.depth > 0 &&
|
|
161
|
+
selection.$from.parent.isTextblock &&
|
|
162
|
+
selection.$from.parent.content.size === 0) {
|
|
163
|
+
insertAt = clampDocPos(editor, selection.$from.before(selection.$from.depth));
|
|
164
|
+
insertParagraph = false;
|
|
165
|
+
}
|
|
151
166
|
const input = document.createElement('input');
|
|
152
167
|
input.type = 'file';
|
|
153
168
|
input.accept = 'image/*';
|
|
@@ -159,10 +174,13 @@ export const suggest = {
|
|
|
159
174
|
return;
|
|
160
175
|
const skeleton = insertUploadSkeleton(editor, {
|
|
161
176
|
kind: 'image',
|
|
162
|
-
height: 220
|
|
177
|
+
height: 220,
|
|
178
|
+
at: insertAt,
|
|
179
|
+
insertParagraph
|
|
163
180
|
});
|
|
164
181
|
try {
|
|
165
|
-
const
|
|
182
|
+
const imageUploader = window.__image_uploader;
|
|
183
|
+
const upload = imageUploader ?? fallbackUpload;
|
|
166
184
|
const src = await upload(file);
|
|
167
185
|
if (skeleton) {
|
|
168
186
|
skeleton.replaceWith({
|
|
@@ -171,9 +189,16 @@ export const suggest = {
|
|
|
171
189
|
});
|
|
172
190
|
}
|
|
173
191
|
else {
|
|
174
|
-
editor
|
|
192
|
+
editor
|
|
193
|
+
.chain()
|
|
194
|
+
.insertContentAt(clampDocPos(editor, insertAt), insertParagraph
|
|
195
|
+
? [{ type: 'image', attrs: { src } }, { type: 'paragraph' }]
|
|
196
|
+
: { type: 'image', attrs: { src } })
|
|
197
|
+
.run();
|
|
198
|
+
}
|
|
199
|
+
if (imageUploader) {
|
|
200
|
+
releaseObjectUrlOnImageSettled(editor.view, src);
|
|
175
201
|
}
|
|
176
|
-
releaseObjectUrlOnImageSettled(editor.view, src);
|
|
177
202
|
}
|
|
178
203
|
catch {
|
|
179
204
|
skeleton?.remove();
|
|
@@ -67,7 +67,8 @@ export const dropImagePlugin = () => {
|
|
|
67
67
|
props: {
|
|
68
68
|
handleDOMEvents: {
|
|
69
69
|
paste(view, event) {
|
|
70
|
-
const
|
|
70
|
+
const imageUploader = window.__image_uploader;
|
|
71
|
+
const upload = imageUploader || fallbackUpload;
|
|
71
72
|
const items = Array.from(event.clipboardData?.items || []);
|
|
72
73
|
const { schema } = view.state;
|
|
73
74
|
items.forEach((item) => {
|
|
@@ -95,7 +96,9 @@ export const dropImagePlugin = () => {
|
|
|
95
96
|
const transaction = view.state.tr.replaceSelectionWith(node);
|
|
96
97
|
view.dispatch(transaction);
|
|
97
98
|
}
|
|
98
|
-
|
|
99
|
+
if (imageUploader) {
|
|
100
|
+
releaseObjectUrlOnImageSettled(view, src);
|
|
101
|
+
}
|
|
99
102
|
})
|
|
100
103
|
.catch(() => {
|
|
101
104
|
skeleton?.remove();
|
|
@@ -119,7 +122,8 @@ export const dropImagePlugin = () => {
|
|
|
119
122
|
return false;
|
|
120
123
|
},
|
|
121
124
|
drop: (view, event) => {
|
|
122
|
-
const
|
|
125
|
+
const imageUploader = window.__image_uploader;
|
|
126
|
+
const upload = imageUploader || fallbackUpload;
|
|
123
127
|
const hasFiles = event.dataTransfer && event.dataTransfer.files && event.dataTransfer.files.length;
|
|
124
128
|
if (!hasFiles) {
|
|
125
129
|
return false;
|
|
@@ -160,7 +164,9 @@ export const dropImagePlugin = () => {
|
|
|
160
164
|
const transaction = view.state.tr.insert(coordinates.pos, node);
|
|
161
165
|
view.dispatch(transaction);
|
|
162
166
|
}
|
|
163
|
-
|
|
167
|
+
if (imageUploader) {
|
|
168
|
+
releaseObjectUrlOnImageSettled(view, src);
|
|
169
|
+
}
|
|
164
170
|
}
|
|
165
171
|
catch {
|
|
166
172
|
skeleton?.remove();
|
|
@@ -28,6 +28,17 @@ const aspectRatioOptions = [
|
|
|
28
28
|
{ label: '4:3', value: '4:3' },
|
|
29
29
|
{ label: '9:16', value: '9:16' }
|
|
30
30
|
];
|
|
31
|
+
const horizontalAlignValues = ['left', 'center', 'right'];
|
|
32
|
+
const horizontalAlignOptions = [
|
|
33
|
+
{ label: 'Auto', value: null },
|
|
34
|
+
{ label: 'Left', value: 'left' },
|
|
35
|
+
{ label: 'Center', value: 'center' },
|
|
36
|
+
{ label: 'Right', value: 'right' }
|
|
37
|
+
];
|
|
38
|
+
const widthPresetOptions = [
|
|
39
|
+
{ label: '100%', value: '100%' },
|
|
40
|
+
{ label: '50%', value: '50%' }
|
|
41
|
+
];
|
|
31
42
|
function isResizableType(value) {
|
|
32
43
|
return typeSet.has(value);
|
|
33
44
|
}
|
|
@@ -109,6 +120,28 @@ function sameAspectRatio(left, right) {
|
|
|
109
120
|
return false;
|
|
110
121
|
return Math.abs(leftRatio - rightRatio) <= 0.001;
|
|
111
122
|
}
|
|
123
|
+
function normalizeHorizontalAlignAttr(value) {
|
|
124
|
+
if (typeof value !== 'string')
|
|
125
|
+
return null;
|
|
126
|
+
const normalized = value.trim().toLowerCase();
|
|
127
|
+
if (!normalized)
|
|
128
|
+
return null;
|
|
129
|
+
return horizontalAlignValues.includes(normalized)
|
|
130
|
+
? normalized
|
|
131
|
+
: null;
|
|
132
|
+
}
|
|
133
|
+
function normalizeWidthPreset(value) {
|
|
134
|
+
if (typeof value !== 'string' && typeof value !== 'number')
|
|
135
|
+
return null;
|
|
136
|
+
const normalized = String(value).trim();
|
|
137
|
+
if (!normalized)
|
|
138
|
+
return null;
|
|
139
|
+
if (normalized === '100' || normalized === '100%')
|
|
140
|
+
return '100%';
|
|
141
|
+
if (normalized === '50' || normalized === '50%')
|
|
142
|
+
return '50%';
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
112
145
|
function normalizeStringAttr(value) {
|
|
113
146
|
if (typeof value !== 'string')
|
|
114
147
|
return null;
|
|
@@ -261,41 +294,209 @@ function buildResizeAttrs(kind, node, height, imageRatio, aspectRatio = normaliz
|
|
|
261
294
|
function canUseAspectRatioPreset(kind) {
|
|
262
295
|
return kind === 'iframe' || kind === 'embed';
|
|
263
296
|
}
|
|
297
|
+
function canUseLayoutOptions(kind) {
|
|
298
|
+
return kind === 'image' || canUseAspectRatioPreset(kind);
|
|
299
|
+
}
|
|
300
|
+
function createToolbarGroupIcon(group) {
|
|
301
|
+
const icon = document.createElement('span');
|
|
302
|
+
icon.className = 'tiptap-media-toolbar-group-icon';
|
|
303
|
+
icon.dataset.group = group;
|
|
304
|
+
icon.setAttribute('aria-hidden', 'true');
|
|
305
|
+
return icon;
|
|
306
|
+
}
|
|
264
307
|
function createResizeHandleDecoration(nodePos, widgetPos, resizeMeta, node) {
|
|
308
|
+
const widthKey = normalizeWidthAttr(node.attrs.width) || 'auto';
|
|
309
|
+
const aspectRatioKey = normalizeAspectRatioAttr(node.attrs.aspectRatio) || 'auto';
|
|
310
|
+
const horizontalAlignKey = normalizeHorizontalAlignAttr(node.attrs.horizontalAlign) || 'auto';
|
|
265
311
|
return Decoration.widget(widgetPos, () => {
|
|
266
312
|
const anchor = document.createElement('div');
|
|
267
313
|
anchor.className = 'tiptap-media-resize-anchor';
|
|
314
|
+
const isImageAnchor = resizeMeta.kind === 'image';
|
|
315
|
+
let controlsContainer = anchor;
|
|
316
|
+
if (isImageAnchor) {
|
|
317
|
+
anchor.classList.add('is-image-anchor');
|
|
318
|
+
const controls = document.createElement('div');
|
|
319
|
+
controls.className = 'tiptap-media-resize-controls';
|
|
320
|
+
controls.style.left = '0px';
|
|
321
|
+
controls.style.width = '100%';
|
|
322
|
+
anchor.append(controls);
|
|
323
|
+
controlsContainer = controls;
|
|
324
|
+
}
|
|
325
|
+
else {
|
|
326
|
+
const normalizedWidth = normalizeWidthAttr(node.attrs.width);
|
|
327
|
+
if (normalizedWidth) {
|
|
328
|
+
if (normalizedWidth.endsWith('%')) {
|
|
329
|
+
anchor.style.width = normalizedWidth;
|
|
330
|
+
}
|
|
331
|
+
else {
|
|
332
|
+
const numericWidth = parseNumericSize(normalizedWidth);
|
|
333
|
+
if (numericWidth !== null) {
|
|
334
|
+
anchor.style.width = `${Math.round(numericWidth)}px`;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
anchor.style.maxWidth = '100%';
|
|
338
|
+
}
|
|
339
|
+
const horizontalAlign = normalizeHorizontalAlignAttr(node.attrs.horizontalAlign);
|
|
340
|
+
if (horizontalAlign === 'center') {
|
|
341
|
+
anchor.style.marginLeft = 'auto';
|
|
342
|
+
anchor.style.marginRight = 'auto';
|
|
343
|
+
}
|
|
344
|
+
else if (horizontalAlign === 'right') {
|
|
345
|
+
anchor.style.marginLeft = 'auto';
|
|
346
|
+
anchor.style.marginRight = '0';
|
|
347
|
+
}
|
|
348
|
+
else {
|
|
349
|
+
anchor.style.marginLeft = '0';
|
|
350
|
+
anchor.style.marginRight = '0';
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
if (isImageAnchor) {
|
|
354
|
+
requestAnimationFrame(() => {
|
|
355
|
+
if (!anchor.isConnected)
|
|
356
|
+
return;
|
|
357
|
+
let candidate = anchor.previousElementSibling;
|
|
358
|
+
let imageElement = null;
|
|
359
|
+
while (candidate && !imageElement) {
|
|
360
|
+
if (candidate instanceof HTMLImageElement) {
|
|
361
|
+
imageElement = candidate;
|
|
362
|
+
}
|
|
363
|
+
else if (candidate instanceof HTMLElement) {
|
|
364
|
+
imageElement = candidate.querySelector('img');
|
|
365
|
+
}
|
|
366
|
+
candidate = candidate.previousElementSibling;
|
|
367
|
+
}
|
|
368
|
+
if (!imageElement && anchor.parentElement instanceof HTMLElement) {
|
|
369
|
+
imageElement =
|
|
370
|
+
anchor.parentElement.querySelector('figure.ProseMirror-selectednode img, img.ProseMirror-selectednode') || null;
|
|
371
|
+
}
|
|
372
|
+
if (!(imageElement instanceof HTMLElement))
|
|
373
|
+
return;
|
|
374
|
+
if (!(controlsContainer instanceof HTMLElement))
|
|
375
|
+
return;
|
|
376
|
+
const rect = imageElement.getBoundingClientRect();
|
|
377
|
+
if (rect.width <= 0 || rect.height <= 0)
|
|
378
|
+
return;
|
|
379
|
+
const anchorRect = anchor.getBoundingClientRect();
|
|
380
|
+
const leftOffset = rect.left - anchorRect.left;
|
|
381
|
+
const maxLeft = Math.max(0, anchorRect.width - rect.width);
|
|
382
|
+
const clampedLeft = clamp(leftOffset, 0, maxLeft);
|
|
383
|
+
const topOffset = rect.bottom - anchorRect.top + 6;
|
|
384
|
+
controlsContainer.style.top = `${Math.round(topOffset)}px`;
|
|
385
|
+
controlsContainer.style.left = `${Math.round(clampedLeft)}px`;
|
|
386
|
+
controlsContainer.style.width = `${Math.round(rect.width)}px`;
|
|
387
|
+
});
|
|
388
|
+
}
|
|
268
389
|
const button = document.createElement('button');
|
|
269
390
|
button.type = 'button';
|
|
270
391
|
button.className = 'tiptap-media-resize-handle';
|
|
271
392
|
button.dataset.resizePos = String(nodePos);
|
|
272
393
|
button.dataset.resizeKind = resizeMeta.kind;
|
|
273
|
-
button.setAttribute('aria-label',
|
|
274
|
-
|
|
275
|
-
|
|
394
|
+
button.setAttribute('aria-label', canUseLayoutOptions(resizeMeta.kind)
|
|
395
|
+
? 'Resize media height (click for layout options)'
|
|
396
|
+
: 'Resize media height');
|
|
397
|
+
controlsContainer.append(button);
|
|
398
|
+
if (canUseLayoutOptions(resizeMeta.kind)) {
|
|
276
399
|
const selectedAspectRatio = normalizeAspectRatioAttr(node.attrs.aspectRatio);
|
|
400
|
+
const selectedHorizontalAlign = normalizeHorizontalAlignAttr(node.attrs.horizontalAlign);
|
|
401
|
+
const selectedWidthPreset = normalizeWidthPreset(node.attrs.width);
|
|
402
|
+
const supportsAspectRatio = canUseAspectRatioPreset(resizeMeta.kind);
|
|
403
|
+
const supportsBottomWidthPreset = resizeMeta.kind === 'image';
|
|
404
|
+
if (supportsAspectRatio) {
|
|
405
|
+
const widthHandle = document.createElement('button');
|
|
406
|
+
widthHandle.type = 'button';
|
|
407
|
+
widthHandle.className = 'tiptap-media-width-resize-handle';
|
|
408
|
+
widthHandle.dataset.resizePos = String(nodePos);
|
|
409
|
+
widthHandle.dataset.resizeKind = resizeMeta.kind;
|
|
410
|
+
widthHandle.setAttribute('aria-label', 'Resize media width (click for width presets)');
|
|
411
|
+
controlsContainer.append(widthHandle);
|
|
412
|
+
}
|
|
277
413
|
const toolbar = document.createElement('div');
|
|
278
414
|
toolbar.className = 'tiptap-media-aspect-ratio-toolbar';
|
|
279
415
|
toolbar.setAttribute('role', 'toolbar');
|
|
280
|
-
toolbar.setAttribute('aria-label', '
|
|
416
|
+
toolbar.setAttribute('aria-label', 'Media resize options');
|
|
281
417
|
toolbar.dataset.resizePos = String(nodePos);
|
|
282
|
-
|
|
418
|
+
let hasToolbarItems = false;
|
|
419
|
+
if (supportsAspectRatio) {
|
|
420
|
+
toolbar.append(createToolbarGroupIcon('aspect'));
|
|
421
|
+
for (const option of aspectRatioOptions) {
|
|
422
|
+
const optionButton = document.createElement('button');
|
|
423
|
+
optionButton.type = 'button';
|
|
424
|
+
optionButton.className = 'tiptap-media-aspect-ratio-option';
|
|
425
|
+
optionButton.dataset.resizePos = String(nodePos);
|
|
426
|
+
optionButton.dataset.aspectRatio = option.value ?? 'auto';
|
|
427
|
+
optionButton.textContent = option.label;
|
|
428
|
+
const isActive = option.value
|
|
429
|
+
? sameAspectRatio(option.value, selectedAspectRatio)
|
|
430
|
+
: !selectedAspectRatio;
|
|
431
|
+
optionButton.setAttribute('aria-pressed', isActive ? 'true' : 'false');
|
|
432
|
+
toolbar.append(optionButton);
|
|
433
|
+
}
|
|
434
|
+
hasToolbarItems = true;
|
|
435
|
+
}
|
|
436
|
+
if (hasToolbarItems) {
|
|
437
|
+
const separator = document.createElement('span');
|
|
438
|
+
separator.className = 'tiptap-media-toolbar-separator';
|
|
439
|
+
separator.setAttribute('aria-hidden', 'true');
|
|
440
|
+
toolbar.append(separator);
|
|
441
|
+
}
|
|
442
|
+
toolbar.append(createToolbarGroupIcon('align'));
|
|
443
|
+
for (const option of horizontalAlignOptions) {
|
|
283
444
|
const optionButton = document.createElement('button');
|
|
284
445
|
optionButton.type = 'button';
|
|
285
|
-
optionButton.className = 'tiptap-media-
|
|
446
|
+
optionButton.className = 'tiptap-media-horizontal-align-option';
|
|
286
447
|
optionButton.dataset.resizePos = String(nodePos);
|
|
287
|
-
optionButton.dataset.
|
|
448
|
+
optionButton.dataset.horizontalAlign = option.value ?? 'auto';
|
|
288
449
|
optionButton.textContent = option.label;
|
|
289
450
|
const isActive = option.value
|
|
290
|
-
?
|
|
291
|
-
: !
|
|
451
|
+
? option.value === selectedHorizontalAlign
|
|
452
|
+
: !selectedHorizontalAlign;
|
|
292
453
|
optionButton.setAttribute('aria-pressed', isActive ? 'true' : 'false');
|
|
293
454
|
toolbar.append(optionButton);
|
|
294
455
|
}
|
|
295
|
-
|
|
456
|
+
hasToolbarItems = true;
|
|
457
|
+
if (supportsBottomWidthPreset) {
|
|
458
|
+
const separator = document.createElement('span');
|
|
459
|
+
separator.className = 'tiptap-media-toolbar-separator';
|
|
460
|
+
separator.setAttribute('aria-hidden', 'true');
|
|
461
|
+
toolbar.append(separator);
|
|
462
|
+
toolbar.append(createToolbarGroupIcon('width'));
|
|
463
|
+
for (const option of widthPresetOptions) {
|
|
464
|
+
const optionButton = document.createElement('button');
|
|
465
|
+
optionButton.type = 'button';
|
|
466
|
+
optionButton.className = 'tiptap-media-width-option';
|
|
467
|
+
optionButton.dataset.resizePos = String(nodePos);
|
|
468
|
+
optionButton.dataset.widthPreset = option.value;
|
|
469
|
+
optionButton.textContent = option.label;
|
|
470
|
+
optionButton.setAttribute('aria-pressed', selectedWidthPreset === option.value ? 'true' : 'false');
|
|
471
|
+
toolbar.append(optionButton);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
controlsContainer.append(toolbar);
|
|
475
|
+
if (supportsAspectRatio) {
|
|
476
|
+
const widthToolbar = document.createElement('div');
|
|
477
|
+
widthToolbar.className = 'tiptap-media-width-toolbar';
|
|
478
|
+
widthToolbar.setAttribute('role', 'toolbar');
|
|
479
|
+
widthToolbar.setAttribute('aria-label', 'Media width presets');
|
|
480
|
+
widthToolbar.dataset.resizePos = String(nodePos);
|
|
481
|
+
widthToolbar.append(createToolbarGroupIcon('width'));
|
|
482
|
+
for (const option of widthPresetOptions) {
|
|
483
|
+
const optionButton = document.createElement('button');
|
|
484
|
+
optionButton.type = 'button';
|
|
485
|
+
optionButton.className = 'tiptap-media-width-option';
|
|
486
|
+
optionButton.dataset.resizePos = String(nodePos);
|
|
487
|
+
optionButton.dataset.widthPreset = option.value;
|
|
488
|
+
optionButton.textContent = option.label;
|
|
489
|
+
optionButton.setAttribute('aria-pressed', selectedWidthPreset === option.value ? 'true' : 'false');
|
|
490
|
+
widthToolbar.append(optionButton);
|
|
491
|
+
}
|
|
492
|
+
controlsContainer.append(widthToolbar);
|
|
493
|
+
}
|
|
296
494
|
}
|
|
297
495
|
return anchor;
|
|
298
|
-
}, {
|
|
496
|
+
}, {
|
|
497
|
+
side: 1,
|
|
498
|
+
key: `media-resize-${nodePos}-${resizeMeta.typeName}-${resizeMeta.kind}-${widthKey}-${aspectRatioKey}-${horizontalAlignKey}`
|
|
499
|
+
});
|
|
299
500
|
}
|
|
300
501
|
function tryCreateNodeSelection(doc, pos) {
|
|
301
502
|
if (pos < 0 || pos > doc.content.size)
|
|
@@ -413,6 +614,14 @@ export default Extension.create({
|
|
|
413
614
|
const aspectRatio = normalizeAspectRatioAttr(attributes.aspectRatio);
|
|
414
615
|
return aspectRatio ? { 'data-resize-aspect-ratio': aspectRatio } : {};
|
|
415
616
|
}
|
|
617
|
+
},
|
|
618
|
+
horizontalAlign: {
|
|
619
|
+
default: null,
|
|
620
|
+
parseHTML: (element) => normalizeHorizontalAlignAttr(element.getAttribute('data-resize-horizontal-align')),
|
|
621
|
+
renderHTML: (attributes) => {
|
|
622
|
+
const horizontalAlign = normalizeHorizontalAlignAttr(attributes.horizontalAlign);
|
|
623
|
+
return horizontalAlign ? { 'data-resize-horizontal-align': horizontalAlign } : {};
|
|
624
|
+
}
|
|
416
625
|
}
|
|
417
626
|
}
|
|
418
627
|
}
|
|
@@ -422,11 +631,11 @@ export default Extension.create({
|
|
|
422
631
|
let removeDragListeners = null;
|
|
423
632
|
const closeOpenToolbars = (view, except = null) => {
|
|
424
633
|
view.dom
|
|
425
|
-
.querySelectorAll('.tiptap-media-resize-anchor.is-toolbar-open')
|
|
634
|
+
.querySelectorAll('.tiptap-media-resize-anchor.is-toolbar-open, .tiptap-media-resize-anchor.is-width-toolbar-open')
|
|
426
635
|
.forEach((anchor) => {
|
|
427
636
|
if (except && anchor === except)
|
|
428
637
|
return;
|
|
429
|
-
anchor.classList.remove('is-toolbar-open');
|
|
638
|
+
anchor.classList.remove('is-toolbar-open', 'is-width-toolbar-open');
|
|
430
639
|
});
|
|
431
640
|
};
|
|
432
641
|
return [
|
|
@@ -490,6 +699,60 @@ export default Extension.create({
|
|
|
490
699
|
return false;
|
|
491
700
|
if (!(event.target instanceof HTMLElement))
|
|
492
701
|
return false;
|
|
702
|
+
const widthOption = event.target.closest('.tiptap-media-width-option');
|
|
703
|
+
if (widthOption) {
|
|
704
|
+
event.preventDefault();
|
|
705
|
+
event.stopPropagation();
|
|
706
|
+
const pos = Number.parseInt(widthOption.dataset.resizePos || '', 10);
|
|
707
|
+
if (!Number.isFinite(pos))
|
|
708
|
+
return true;
|
|
709
|
+
const node = view.state.doc.nodeAt(pos);
|
|
710
|
+
if (!node)
|
|
711
|
+
return true;
|
|
712
|
+
const resizeMeta = resolveResizeMeta(node);
|
|
713
|
+
if (!resizeMeta || !canUseLayoutOptions(resizeMeta.kind))
|
|
714
|
+
return true;
|
|
715
|
+
const widthPreset = normalizeWidthPreset(widthOption.dataset.widthPreset);
|
|
716
|
+
if (!widthPreset)
|
|
717
|
+
return true;
|
|
718
|
+
if (node.attrs.width !== widthPreset) {
|
|
719
|
+
view.dispatch(view.state.tr.setNodeMarkup(pos, node.type, {
|
|
720
|
+
...node.attrs,
|
|
721
|
+
width: widthPreset
|
|
722
|
+
}));
|
|
723
|
+
}
|
|
724
|
+
closeOpenToolbars(view);
|
|
725
|
+
return true;
|
|
726
|
+
}
|
|
727
|
+
const horizontalAlignOption = event.target.closest('.tiptap-media-horizontal-align-option');
|
|
728
|
+
if (horizontalAlignOption) {
|
|
729
|
+
event.preventDefault();
|
|
730
|
+
event.stopPropagation();
|
|
731
|
+
const pos = Number.parseInt(horizontalAlignOption.dataset.resizePos || '', 10);
|
|
732
|
+
if (!Number.isFinite(pos))
|
|
733
|
+
return true;
|
|
734
|
+
const node = view.state.doc.nodeAt(pos);
|
|
735
|
+
if (!node)
|
|
736
|
+
return true;
|
|
737
|
+
const resizeMeta = resolveResizeMeta(node);
|
|
738
|
+
if (!resizeMeta || !canUseLayoutOptions(resizeMeta.kind))
|
|
739
|
+
return true;
|
|
740
|
+
const selectedHorizontalAlign = horizontalAlignOption.dataset.horizontalAlign || 'auto';
|
|
741
|
+
const normalizedHorizontalAlign = selectedHorizontalAlign === 'auto'
|
|
742
|
+
? null
|
|
743
|
+
: normalizeHorizontalAlignAttr(selectedHorizontalAlign);
|
|
744
|
+
if (selectedHorizontalAlign !== 'auto' && !normalizedHorizontalAlign)
|
|
745
|
+
return true;
|
|
746
|
+
const currentHorizontalAlign = normalizeHorizontalAlignAttr(node.attrs.horizontalAlign);
|
|
747
|
+
if (normalizedHorizontalAlign !== currentHorizontalAlign) {
|
|
748
|
+
view.dispatch(view.state.tr.setNodeMarkup(pos, node.type, {
|
|
749
|
+
...node.attrs,
|
|
750
|
+
horizontalAlign: normalizedHorizontalAlign
|
|
751
|
+
}));
|
|
752
|
+
}
|
|
753
|
+
closeOpenToolbars(view);
|
|
754
|
+
return true;
|
|
755
|
+
}
|
|
493
756
|
const ratioOption = event.target.closest('.tiptap-media-aspect-ratio-option');
|
|
494
757
|
if (ratioOption) {
|
|
495
758
|
event.preventDefault();
|
|
@@ -529,6 +792,151 @@ export default Extension.create({
|
|
|
529
792
|
closeOpenToolbars(view);
|
|
530
793
|
return true;
|
|
531
794
|
}
|
|
795
|
+
const widthHandle = event.target.closest('.tiptap-media-width-resize-handle');
|
|
796
|
+
if (widthHandle) {
|
|
797
|
+
const pos = Number.parseInt(widthHandle.dataset.resizePos || '', 10);
|
|
798
|
+
if (!Number.isFinite(pos))
|
|
799
|
+
return false;
|
|
800
|
+
const node = view.state.doc.nodeAt(pos);
|
|
801
|
+
if (!node)
|
|
802
|
+
return false;
|
|
803
|
+
const resizeMeta = resolveResizeMeta(node);
|
|
804
|
+
if (!resizeMeta || !canUseAspectRatioPreset(resizeMeta.kind))
|
|
805
|
+
return false;
|
|
806
|
+
const resizeKind = widthHandle.dataset.resizeKind;
|
|
807
|
+
if (resizeKind && resizeMeta.kind !== resizeKind)
|
|
808
|
+
return false;
|
|
809
|
+
const target = resolveTargetElement(view, pos, resizeMeta, node);
|
|
810
|
+
if (!target)
|
|
811
|
+
return false;
|
|
812
|
+
event.preventDefault();
|
|
813
|
+
event.stopPropagation();
|
|
814
|
+
const anchor = widthHandle.closest('.tiptap-media-resize-anchor');
|
|
815
|
+
const startX = event.clientX;
|
|
816
|
+
const startY = event.clientY;
|
|
817
|
+
const startHeight = resolveStartHeight(resizeMeta.kind, node, target);
|
|
818
|
+
const targetParent = target.parentElement;
|
|
819
|
+
const shouldShowProxy = resizeMeta.kind !== 'image' && Boolean(targetParent);
|
|
820
|
+
const currentHorizontalAlign = normalizeHorizontalAlignAttr(node.attrs.horizontalAlign);
|
|
821
|
+
const startWidth = Math.max(1, resolveElementWidth(node, target) ||
|
|
822
|
+
targetParent?.getBoundingClientRect().width ||
|
|
823
|
+
0);
|
|
824
|
+
let frame = 0;
|
|
825
|
+
let pendingWidth = startWidth;
|
|
826
|
+
let resizeProxy = null;
|
|
827
|
+
let restoreTarget = null;
|
|
828
|
+
let isDragging = false;
|
|
829
|
+
let appliedDragCursor = false;
|
|
830
|
+
const previousCursor = document.body.style.cursor;
|
|
831
|
+
const previousSelect = document.body.style.userSelect;
|
|
832
|
+
const dispatchWidth = (width) => {
|
|
833
|
+
const current = view.state.doc.nodeAt(pos);
|
|
834
|
+
if (!current || current.type.name !== resizeMeta.typeName)
|
|
835
|
+
return;
|
|
836
|
+
const currentMeta = resolveResizeMeta(current);
|
|
837
|
+
if (!currentMeta || !canUseAspectRatioPreset(currentMeta.kind))
|
|
838
|
+
return;
|
|
839
|
+
const containerWidth = target.parentElement?.getBoundingClientRect().width || 0;
|
|
840
|
+
const nextWidth = containerWidth > 0
|
|
841
|
+
? `${Math.round(clamp((width / containerWidth) * 100, 10, 100))}%`
|
|
842
|
+
: String(Math.max(1, Math.round(width)));
|
|
843
|
+
if (nextWidth === current.attrs.width)
|
|
844
|
+
return;
|
|
845
|
+
view.dispatch(view.state.tr.setNodeMarkup(pos, current.type, {
|
|
846
|
+
...current.attrs,
|
|
847
|
+
width: nextWidth
|
|
848
|
+
}));
|
|
849
|
+
};
|
|
850
|
+
const beginDrag = () => {
|
|
851
|
+
if (isDragging)
|
|
852
|
+
return;
|
|
853
|
+
isDragging = true;
|
|
854
|
+
closeOpenToolbars(view);
|
|
855
|
+
if (shouldShowProxy && targetParent) {
|
|
856
|
+
const targetElement = target;
|
|
857
|
+
const originalDisplay = targetElement.style.display;
|
|
858
|
+
resizeProxy = document.createElement('div');
|
|
859
|
+
resizeProxy.className = 'tiptap-media-resize-proxy';
|
|
860
|
+
resizeProxy.style.height = `${Math.round(startHeight)}px`;
|
|
861
|
+
resizeProxy.style.width = `${Math.round(startWidth)}px`;
|
|
862
|
+
if (currentHorizontalAlign === 'center') {
|
|
863
|
+
resizeProxy.style.marginLeft = 'auto';
|
|
864
|
+
resizeProxy.style.marginRight = 'auto';
|
|
865
|
+
}
|
|
866
|
+
else if (currentHorizontalAlign === 'right') {
|
|
867
|
+
resizeProxy.style.marginLeft = 'auto';
|
|
868
|
+
resizeProxy.style.marginRight = '0';
|
|
869
|
+
}
|
|
870
|
+
else {
|
|
871
|
+
resizeProxy.style.marginLeft = '0';
|
|
872
|
+
resizeProxy.style.marginRight = '0';
|
|
873
|
+
}
|
|
874
|
+
targetParent.insertBefore(resizeProxy, targetElement);
|
|
875
|
+
targetElement.style.display = 'none';
|
|
876
|
+
restoreTarget = () => {
|
|
877
|
+
targetElement.style.display = originalDisplay;
|
|
878
|
+
resizeProxy?.remove();
|
|
879
|
+
resizeProxy = null;
|
|
880
|
+
restoreTarget = null;
|
|
881
|
+
};
|
|
882
|
+
}
|
|
883
|
+
document.body.style.cursor = 'ew-resize';
|
|
884
|
+
document.body.style.userSelect = 'none';
|
|
885
|
+
appliedDragCursor = true;
|
|
886
|
+
};
|
|
887
|
+
const onMove = (moveEvent) => {
|
|
888
|
+
const deltaX = moveEvent.clientX - startX;
|
|
889
|
+
const deltaY = moveEvent.clientY - startY;
|
|
890
|
+
if (!isDragging && Math.max(Math.abs(deltaX), Math.abs(deltaY)) < 4)
|
|
891
|
+
return;
|
|
892
|
+
if (!isDragging)
|
|
893
|
+
beginDrag();
|
|
894
|
+
const containerWidth = target.parentElement?.getBoundingClientRect().width || 0;
|
|
895
|
+
const minWidth = containerWidth > 0 ? Math.max(120, containerWidth * 0.2) : 120;
|
|
896
|
+
const maxWidth = containerWidth > 0 ? containerWidth : maxHeight;
|
|
897
|
+
const nextWidth = clamp(startWidth + deltaX, minWidth, maxWidth);
|
|
898
|
+
pendingWidth = nextWidth;
|
|
899
|
+
if (resizeProxy)
|
|
900
|
+
resizeProxy.style.width = `${Math.round(nextWidth)}px`;
|
|
901
|
+
if (shouldShowProxy)
|
|
902
|
+
return;
|
|
903
|
+
if (frame)
|
|
904
|
+
cancelAnimationFrame(frame);
|
|
905
|
+
frame = requestAnimationFrame(() => dispatchWidth(nextWidth));
|
|
906
|
+
};
|
|
907
|
+
const cleanup = () => {
|
|
908
|
+
if (frame)
|
|
909
|
+
cancelAnimationFrame(frame);
|
|
910
|
+
window.removeEventListener('mousemove', onMove);
|
|
911
|
+
window.removeEventListener('mouseup', onUp);
|
|
912
|
+
if (appliedDragCursor) {
|
|
913
|
+
document.body.style.cursor = previousCursor;
|
|
914
|
+
document.body.style.userSelect = previousSelect;
|
|
915
|
+
}
|
|
916
|
+
restoreTarget?.();
|
|
917
|
+
removeDragListeners = null;
|
|
918
|
+
};
|
|
919
|
+
const onUp = () => {
|
|
920
|
+
if (!isDragging) {
|
|
921
|
+
const shouldOpen = Boolean(anchor) &&
|
|
922
|
+
!(anchor?.classList.contains('is-width-toolbar-open') ?? false);
|
|
923
|
+
closeOpenToolbars(view, shouldOpen && anchor ? anchor : null);
|
|
924
|
+
if (shouldOpen)
|
|
925
|
+
anchor?.classList.remove('is-toolbar-open');
|
|
926
|
+
anchor?.classList.toggle('is-width-toolbar-open', shouldOpen);
|
|
927
|
+
cleanup();
|
|
928
|
+
return;
|
|
929
|
+
}
|
|
930
|
+
if (shouldShowProxy)
|
|
931
|
+
dispatchWidth(pendingWidth);
|
|
932
|
+
cleanup();
|
|
933
|
+
};
|
|
934
|
+
removeDragListeners?.();
|
|
935
|
+
window.addEventListener('mousemove', onMove);
|
|
936
|
+
window.addEventListener('mouseup', onUp);
|
|
937
|
+
removeDragListeners = cleanup;
|
|
938
|
+
return true;
|
|
939
|
+
}
|
|
532
940
|
const handle = event.target.closest('.tiptap-media-resize-handle');
|
|
533
941
|
if (!handle) {
|
|
534
942
|
closeOpenToolbars(view);
|
|
@@ -556,7 +964,9 @@ export default Extension.create({
|
|
|
556
964
|
const startY = event.clientY;
|
|
557
965
|
const startHeight = resolveStartHeight(resizeMeta.kind, node, target);
|
|
558
966
|
const imageRatio = resizeMeta.kind === 'image' ? resolveImageRatio(node, target) : 1;
|
|
559
|
-
const shouldShowProxy =
|
|
967
|
+
const shouldShowProxy = true;
|
|
968
|
+
const currentHorizontalAlign = normalizeHorizontalAlignAttr(node.attrs.horizontalAlign);
|
|
969
|
+
const startWidth = resolveElementWidth(node, target);
|
|
560
970
|
let resizeProxy = null;
|
|
561
971
|
let restoreTarget = null;
|
|
562
972
|
let frame = 0;
|
|
@@ -596,6 +1006,21 @@ export default Extension.create({
|
|
|
596
1006
|
resizeProxy = document.createElement('div');
|
|
597
1007
|
resizeProxy.className = 'tiptap-media-resize-proxy';
|
|
598
1008
|
resizeProxy.style.height = `${Math.round(startHeight)}px`;
|
|
1009
|
+
if (startWidth > 0) {
|
|
1010
|
+
resizeProxy.style.width = `${Math.round(startWidth)}px`;
|
|
1011
|
+
}
|
|
1012
|
+
if (currentHorizontalAlign === 'center') {
|
|
1013
|
+
resizeProxy.style.marginLeft = 'auto';
|
|
1014
|
+
resizeProxy.style.marginRight = 'auto';
|
|
1015
|
+
}
|
|
1016
|
+
else if (currentHorizontalAlign === 'right') {
|
|
1017
|
+
resizeProxy.style.marginLeft = 'auto';
|
|
1018
|
+
resizeProxy.style.marginRight = '0';
|
|
1019
|
+
}
|
|
1020
|
+
else {
|
|
1021
|
+
resizeProxy.style.marginLeft = '0';
|
|
1022
|
+
resizeProxy.style.marginRight = '0';
|
|
1023
|
+
}
|
|
599
1024
|
target.parentElement.insertBefore(resizeProxy, targetElement);
|
|
600
1025
|
targetElement.style.display = 'none';
|
|
601
1026
|
restoreTarget = () => {
|
|
@@ -642,6 +1067,8 @@ export default Extension.create({
|
|
|
642
1067
|
if (!isDragging) {
|
|
643
1068
|
const shouldOpen = Boolean(anchor) && !(anchor?.classList.contains('is-toolbar-open') ?? false);
|
|
644
1069
|
closeOpenToolbars(view, shouldOpen && anchor ? anchor : null);
|
|
1070
|
+
if (shouldOpen)
|
|
1071
|
+
anchor?.classList.remove('is-width-toolbar-open');
|
|
645
1072
|
anchor?.classList.toggle('is-toolbar-open', shouldOpen);
|
|
646
1073
|
cleanup();
|
|
647
1074
|
return;
|
|
@@ -12,11 +12,9 @@
|
|
|
12
12
|
import { quartOut } from 'svelte/easing';
|
|
13
13
|
import defaultI18n, { I18N_CONTEXT, type I18nTranslate } from '../i18n';
|
|
14
14
|
|
|
15
|
-
const editor = getContext<{ v: any }>('editor');
|
|
16
15
|
const i18nFromContext = getContext<I18nTranslate | undefined>(I18N_CONTEXT);
|
|
17
16
|
const i18n: I18nTranslate = (...args) =>
|
|
18
17
|
i18nFromContext ? i18nFromContext(...args) : defaultI18n(...args);
|
|
19
|
-
const tiptap = $derived(editor.v);
|
|
20
18
|
|
|
21
19
|
let height = $state(0);
|
|
22
20
|
let input = $state(''),
|
|
@@ -32,7 +30,6 @@
|
|
|
32
30
|
if (!editor || !range) return;
|
|
33
31
|
|
|
34
32
|
item.command({ editor, range });
|
|
35
|
-
setTimeout(() => tiptap?.commands?.focus?.());
|
|
36
33
|
}
|
|
37
34
|
|
|
38
35
|
function runDetailCommand() {
|
|
@@ -18,7 +18,6 @@
|
|
|
18
18
|
type I18nTranslate
|
|
19
19
|
} from '../i18n';
|
|
20
20
|
import type { UploadFn } from '../plugin/image/dragdrop';
|
|
21
|
-
import { fallbackUpload } from '../plugin/image/dragdrop';
|
|
22
21
|
import MediaResize, { type ResizeOptions } from '../plugin/resize';
|
|
23
22
|
import { Render } from 'nunui';
|
|
24
23
|
|
|
@@ -51,7 +50,7 @@
|
|
|
51
50
|
ref = $bindable(null),
|
|
52
51
|
options = {},
|
|
53
52
|
loaded = $bindable(false),
|
|
54
|
-
imageUpload
|
|
53
|
+
imageUpload,
|
|
55
54
|
style = '',
|
|
56
55
|
blocks = [],
|
|
57
56
|
placeholder,
|
|
@@ -82,6 +81,7 @@
|
|
|
82
81
|
'data-resize-min-height',
|
|
83
82
|
'data-resize-max-height',
|
|
84
83
|
'data-resize-aspect-ratio',
|
|
84
|
+
'data-resize-horizontal-align',
|
|
85
85
|
'data-bubble-menu',
|
|
86
86
|
'data-hide-bubble-menu'
|
|
87
87
|
];
|
|
@@ -341,6 +341,10 @@
|
|
|
341
341
|
outline: 3px solid var(--primary);
|
|
342
342
|
}
|
|
343
343
|
|
|
344
|
+
.editable :global(.ProseMirror) {
|
|
345
|
+
position: relative;
|
|
346
|
+
}
|
|
347
|
+
|
|
344
348
|
.editable :global(.tiptap-media-resize-anchor) {
|
|
345
349
|
width: 100%;
|
|
346
350
|
display: flex;
|
|
@@ -353,6 +357,27 @@
|
|
|
353
357
|
overflow: visible;
|
|
354
358
|
}
|
|
355
359
|
|
|
360
|
+
.editable :global(.tiptap-media-resize-anchor.is-image-anchor) {
|
|
361
|
+
position: relative;
|
|
362
|
+
display: block;
|
|
363
|
+
width: 100%;
|
|
364
|
+
height: 0;
|
|
365
|
+
margin: 0;
|
|
366
|
+
z-index: 4;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
.editable :global(.tiptap-media-resize-anchor.is-image-anchor .tiptap-media-resize-controls) {
|
|
370
|
+
position: absolute;
|
|
371
|
+
top: 0;
|
|
372
|
+
left: 0;
|
|
373
|
+
display: flex;
|
|
374
|
+
flex-direction: column;
|
|
375
|
+
align-items: center;
|
|
376
|
+
line-height: 0;
|
|
377
|
+
pointer-events: none;
|
|
378
|
+
overflow: visible;
|
|
379
|
+
}
|
|
380
|
+
|
|
356
381
|
.editable :global(.tiptap-media-resize-handle) {
|
|
357
382
|
appearance: none;
|
|
358
383
|
-webkit-appearance: none;
|
|
@@ -383,6 +408,39 @@
|
|
|
383
408
|
transform: translateY(1px);
|
|
384
409
|
}
|
|
385
410
|
|
|
411
|
+
.editable :global(.tiptap-media-width-resize-handle) {
|
|
412
|
+
appearance: none;
|
|
413
|
+
-webkit-appearance: none;
|
|
414
|
+
position: absolute;
|
|
415
|
+
top: 0;
|
|
416
|
+
right: 0;
|
|
417
|
+
width: 12px;
|
|
418
|
+
height: 12px;
|
|
419
|
+
margin: 0;
|
|
420
|
+
padding: 0;
|
|
421
|
+
border: 1px solid var(--primary-light3, rgba(120, 120, 120, 0.45));
|
|
422
|
+
border-radius: 999px;
|
|
423
|
+
background: var(--primary-light6, rgba(120, 120, 120, 0.2));
|
|
424
|
+
cursor: ew-resize;
|
|
425
|
+
pointer-events: auto;
|
|
426
|
+
transform: translate(40%, -40%);
|
|
427
|
+
transition:
|
|
428
|
+
background-color 0.15s ease,
|
|
429
|
+
border-color 0.15s ease,
|
|
430
|
+
transform 0.15s ease;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
.editable :global(.tiptap-media-width-resize-handle:hover),
|
|
434
|
+
.editable :global(.tiptap-media-width-resize-handle:focus-visible) {
|
|
435
|
+
background: var(--primary-light4, rgba(120, 120, 120, 0.35));
|
|
436
|
+
border-color: var(--primary-light2, rgba(100, 100, 100, 0.55));
|
|
437
|
+
outline: none;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
.editable :global(.tiptap-media-width-resize-handle:active) {
|
|
441
|
+
transform: translate(40%, -40%) scale(0.95);
|
|
442
|
+
}
|
|
443
|
+
|
|
386
444
|
.editable :global(.tiptap-media-aspect-ratio-toolbar) {
|
|
387
445
|
display: none;
|
|
388
446
|
align-items: center;
|
|
@@ -402,12 +460,81 @@
|
|
|
402
460
|
white-space: nowrap;
|
|
403
461
|
}
|
|
404
462
|
|
|
463
|
+
.editable :global(.tiptap-media-width-toolbar) {
|
|
464
|
+
display: none;
|
|
465
|
+
align-items: center;
|
|
466
|
+
gap: 4px;
|
|
467
|
+
padding: 4px;
|
|
468
|
+
border: 1px solid var(--primary-light3, rgba(120, 120, 120, 0.4));
|
|
469
|
+
border-radius: 999px;
|
|
470
|
+
background: var(--surface, #fff);
|
|
471
|
+
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12);
|
|
472
|
+
pointer-events: auto;
|
|
473
|
+
line-height: 1;
|
|
474
|
+
position: absolute;
|
|
475
|
+
top: calc(100% + 6px);
|
|
476
|
+
right: 0;
|
|
477
|
+
z-index: 4;
|
|
478
|
+
white-space: nowrap;
|
|
479
|
+
}
|
|
480
|
+
|
|
405
481
|
.editable
|
|
406
482
|
:global(.tiptap-media-resize-anchor.is-toolbar-open .tiptap-media-aspect-ratio-toolbar) {
|
|
407
483
|
display: flex;
|
|
408
484
|
}
|
|
409
485
|
|
|
410
|
-
.editable :global(.tiptap-media-
|
|
486
|
+
.editable :global(.tiptap-media-resize-anchor.is-width-toolbar-open .tiptap-media-width-toolbar) {
|
|
487
|
+
display: flex;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
.editable :global(.tiptap-media-toolbar-separator) {
|
|
491
|
+
width: 1px;
|
|
492
|
+
height: 16px;
|
|
493
|
+
background: var(--primary-light2, rgba(120, 120, 120, 0.35));
|
|
494
|
+
opacity: 0.9;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
.editable :global(.tiptap-media-toolbar-group-icon) {
|
|
498
|
+
width: 14px;
|
|
499
|
+
height: 14px;
|
|
500
|
+
border: 1px solid var(--primary-light3, rgba(120, 120, 120, 0.45));
|
|
501
|
+
border-radius: 4px;
|
|
502
|
+
color: var(--on-surface, #333);
|
|
503
|
+
opacity: 0.75;
|
|
504
|
+
flex: 0 0 auto;
|
|
505
|
+
pointer-events: none;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
.editable :global(.tiptap-media-toolbar-group-icon[data-group='aspect']) {
|
|
509
|
+
background: linear-gradient(
|
|
510
|
+
135deg,
|
|
511
|
+
transparent 42%,
|
|
512
|
+
currentColor 43%,
|
|
513
|
+
currentColor 57%,
|
|
514
|
+
transparent 58%
|
|
515
|
+
)
|
|
516
|
+
center / 100% 100% no-repeat;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
.editable :global(.tiptap-media-toolbar-group-icon[data-group='align']) {
|
|
520
|
+
background:
|
|
521
|
+
linear-gradient(currentColor, currentColor) left 2px top 3px / 8px 1.5px no-repeat,
|
|
522
|
+
linear-gradient(currentColor, currentColor) center top 6px / 10px 1.5px no-repeat,
|
|
523
|
+
linear-gradient(currentColor, currentColor) right 2px top 9px / 8px 1.5px no-repeat;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
.editable :global(.tiptap-media-toolbar-group-icon[data-group='width']) {
|
|
527
|
+
background:
|
|
528
|
+
linear-gradient(currentColor, currentColor) center / 8px 1.5px no-repeat,
|
|
529
|
+
linear-gradient(45deg, transparent 38%, currentColor 39%, currentColor 61%, transparent 62%)
|
|
530
|
+
left 2px center / 4px 4px no-repeat,
|
|
531
|
+
linear-gradient(-45deg, transparent 38%, currentColor 39%, currentColor 61%, transparent 62%)
|
|
532
|
+
right 2px center / 4px 4px no-repeat;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
.editable :global(.tiptap-media-aspect-ratio-option),
|
|
536
|
+
.editable :global(.tiptap-media-horizontal-align-option),
|
|
537
|
+
.editable :global(.tiptap-media-width-option) {
|
|
411
538
|
appearance: none;
|
|
412
539
|
-webkit-appearance: none;
|
|
413
540
|
margin: 0;
|
|
@@ -423,12 +550,18 @@
|
|
|
423
550
|
}
|
|
424
551
|
|
|
425
552
|
.editable :global(.tiptap-media-aspect-ratio-option:hover),
|
|
426
|
-
.editable :global(.tiptap-media-aspect-ratio-option:focus-visible)
|
|
553
|
+
.editable :global(.tiptap-media-aspect-ratio-option:focus-visible),
|
|
554
|
+
.editable :global(.tiptap-media-horizontal-align-option:hover),
|
|
555
|
+
.editable :global(.tiptap-media-horizontal-align-option:focus-visible),
|
|
556
|
+
.editable :global(.tiptap-media-width-option:hover),
|
|
557
|
+
.editable :global(.tiptap-media-width-option:focus-visible) {
|
|
427
558
|
background: var(--primary-light1, rgba(120, 120, 120, 0.14));
|
|
428
559
|
outline: none;
|
|
429
560
|
}
|
|
430
561
|
|
|
431
|
-
.editable :global(.tiptap-media-aspect-ratio-option[aria-pressed='true'])
|
|
562
|
+
.editable :global(.tiptap-media-aspect-ratio-option[aria-pressed='true']),
|
|
563
|
+
.editable :global(.tiptap-media-horizontal-align-option[aria-pressed='true']),
|
|
564
|
+
.editable :global(.tiptap-media-width-option[aria-pressed='true']) {
|
|
432
565
|
background: var(--primary-light4, rgba(120, 120, 120, 0.3));
|
|
433
566
|
color: var(--on-primary, #000);
|
|
434
567
|
}
|
|
@@ -469,6 +602,10 @@
|
|
|
469
602
|
position: relative;
|
|
470
603
|
}
|
|
471
604
|
|
|
605
|
+
& :global(figure[data-bubble-menu='false']) {
|
|
606
|
+
margin: 0;
|
|
607
|
+
}
|
|
608
|
+
|
|
472
609
|
& :global(code.inline) {
|
|
473
610
|
background: var(--primary-light1);
|
|
474
611
|
padding: 2px 4px;
|
|
@@ -535,8 +672,38 @@
|
|
|
535
672
|
& :global(iframe),
|
|
536
673
|
& :global(embed) {
|
|
537
674
|
display: block;
|
|
538
|
-
width: 100%;
|
|
539
675
|
max-width: 100%;
|
|
676
|
+
margin-left: var(--tiptap-media-horizontal-margin-left, 0);
|
|
677
|
+
margin-right: var(--tiptap-media-horizontal-margin-right, 0);
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
& :global(img[data-resize-horizontal-align]) {
|
|
681
|
+
display: block;
|
|
682
|
+
margin-left: var(--tiptap-media-horizontal-margin-left, 0);
|
|
683
|
+
margin-right: var(--tiptap-media-horizontal-margin-right, 0);
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
& :global(img[width='100%']) {
|
|
687
|
+
width: 100%;
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
& :global(img[width='50%']) {
|
|
691
|
+
width: 50%;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
& :global([data-resize-horizontal-align='left']) {
|
|
695
|
+
--tiptap-media-horizontal-margin-left: 0;
|
|
696
|
+
--tiptap-media-horizontal-margin-right: auto;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
& :global([data-resize-horizontal-align='center']) {
|
|
700
|
+
--tiptap-media-horizontal-margin-left: auto;
|
|
701
|
+
--tiptap-media-horizontal-margin-right: auto;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
& :global([data-resize-horizontal-align='right']) {
|
|
705
|
+
--tiptap-media-horizontal-margin-left: auto;
|
|
706
|
+
--tiptap-media-horizontal-margin-right: 0;
|
|
540
707
|
}
|
|
541
708
|
|
|
542
709
|
& :global([data-resize-aspect-ratio='16:9']) {
|