@malva-ui/editor 0.1.14 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -3
- package/fesm2022/malva-ui-editor.mjs +1800 -317
- package/fesm2022/malva-ui-editor.mjs.map +1 -1
- package/package.json +18 -16
- package/types/malva-ui-editor.d.ts +264 -17
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { InjectionToken, signal, Injectable, inject,
|
|
2
|
+
import { InjectionToken, signal, Injectable, computed, inject, DestroyRef, ElementRef, Directive, input, viewChild, effect, ChangeDetectionStrategy, ViewEncapsulation, Component, TemplateRef, ViewContainerRef, PLATFORM_ID, afterNextRender, untracked, afterRenderEffect, Injector, viewChildren, model, output, contentChildren, forwardRef } from '@angular/core';
|
|
3
3
|
import { LiveAnnouncer } from '@angular/cdk/a11y';
|
|
4
4
|
import { MLV_EDITOR_I18N, MlvI18nResolverService, MLV_FILE_UPLOAD_I18N } from '@malva-ui/i18n';
|
|
5
|
+
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
6
|
+
import { Subscription, fromEvent, merge, take, delay } from 'rxjs';
|
|
5
7
|
import { closeHistory } from '@tiptap/pm/history';
|
|
6
8
|
import { Node as Node$1, Slice, Fragment } from '@tiptap/pm/model';
|
|
7
9
|
import { PluginKey, Plugin, TextSelection, NodeSelection } from '@tiptap/pm/state';
|
|
@@ -16,12 +18,14 @@ import { MlvMenuTrigger, MlvMenu, MlvMenuItem } from '@malva-ui/core/menu';
|
|
|
16
18
|
import { MlvPopup, MlvPopupContainer, MlvPopupContent, MlvPopupTrigger } from '@malva-ui/core/popup';
|
|
17
19
|
import { MlvRadio, MlvRadioGroup } from '@malva-ui/core/radio';
|
|
18
20
|
import { MlvTooltip } from '@malva-ui/core/tooltip';
|
|
19
|
-
import { getExtensionField, flattenExtensions, findDuplicates, Extension, generateHTML, getMarkRange, Editor } from '@tiptap/core';
|
|
20
|
-
import { MlvRtlService, MlvResizeObserverService, mlvNextId, MlvFade, MlvSpacer } from '@malva-ui/cdk/utils';
|
|
21
|
+
import { getExtensionField, flattenExtensions, findDuplicates, posToDOMRect, Extension, generateHTML, getMarkRange, Editor } from '@tiptap/core';
|
|
22
|
+
import { MlvRtlService, MlvResizeObserverService, mlvMirrorInlineOffsets, mlvNextId, MlvFade, MlvSpacer } from '@malva-ui/cdk/utils';
|
|
21
23
|
import { MlvSelectionService, MlvSignalFormControlBase, MlvFormControlWrapper, MlvFormControlWrapperControl, MlvHint, MlvLabel, MlvMessage, MLV_FORM_CONTROL } from '@malva-ui/core/form-utils';
|
|
22
24
|
import { MlvToolbar } from '@malva-ui/core/toolbar';
|
|
23
25
|
import { MlvDivider } from '@malva-ui/core/divider';
|
|
24
26
|
import { Markdown } from '@tiptap/markdown';
|
|
27
|
+
import { Overlay } from '@angular/cdk/overlay';
|
|
28
|
+
import { TemplatePortal } from '@angular/cdk/portal';
|
|
25
29
|
import { FileHandlePlugin } from '@tiptap/extension-file-handler';
|
|
26
30
|
import Image from '@tiptap/extension-image';
|
|
27
31
|
import { TaskList, TaskItem } from '@tiptap/extension-list';
|
|
@@ -37,7 +41,6 @@ import { MlvDialogRef, MlvDialog, MlvDialogBody, MlvDialogFooter, MlvDialogHeade
|
|
|
37
41
|
import { MlvProgress } from '@malva-ui/core/progress';
|
|
38
42
|
import { MlvFileUpload } from '@malva-ui/core/file-upload';
|
|
39
43
|
import { DOWN_ARROW, UP_ARROW, RIGHT_ARROW, LEFT_ARROW } from '@angular/cdk/keycodes';
|
|
40
|
-
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
41
44
|
import { MlvDropdownPanel } from '@malva-ui/core/dropdown';
|
|
42
45
|
|
|
43
46
|
/** Optional dependency-injection token for a host-provided image uploader. */
|
|
@@ -173,10 +176,10 @@ class MlvEditorToolbarRevision {
|
|
|
173
176
|
/** @internal Increments after every Tiptap transaction or selection update. */
|
|
174
177
|
revision = signal(0, /* @ts-ignore */
|
|
175
178
|
...(ngDevMode ? [{ debugName: "revision" }] : /* istanbul ignore next */ []));
|
|
176
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
177
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.
|
|
179
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorToolbarRevision, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
180
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorToolbarRevision });
|
|
178
181
|
}
|
|
179
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
182
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorToolbarRevision, decorators: [{
|
|
180
183
|
type: Injectable
|
|
181
184
|
}] });
|
|
182
185
|
/** @internal Supplies the built-in toolbar's per-editor invalidation signal. */
|
|
@@ -212,15 +215,41 @@ class MlvEditorToolbarRovingRegistry {
|
|
|
212
215
|
this._disconnectObserver();
|
|
213
216
|
};
|
|
214
217
|
}
|
|
215
|
-
/**
|
|
218
|
+
/**
|
|
219
|
+
* @internal Resolves the single effective tab stop, even during DOM mutations.
|
|
220
|
+
*
|
|
221
|
+
* Called once per registered widget on every invalidation, so it does no DOM
|
|
222
|
+
* work of its own: the enabled set and its order come from
|
|
223
|
+
* `_enabledWidgets()`, which is derived once per generation and carries the
|
|
224
|
+
* `_domStateRevision` dependency that keeps this reactive.
|
|
225
|
+
*/
|
|
216
226
|
isActive(widget) {
|
|
217
|
-
this.
|
|
227
|
+
const enabled = this._enabledWidgets();
|
|
218
228
|
const active = this._active();
|
|
219
|
-
const effectiveActive = active &&
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
229
|
+
const effectiveActive = active && enabled.includes(active) ? active : (enabled[0] ?? null);
|
|
230
|
+
return (!this._disabled() &&
|
|
231
|
+
effectiveActive === widget &&
|
|
232
|
+
enabled.includes(widget));
|
|
223
233
|
}
|
|
234
|
+
/**
|
|
235
|
+
* @internal Promotes a widget to the tab stop, gated on a **live** DOM read
|
|
236
|
+
* rather than on `_enabledWidgets()`.
|
|
237
|
+
*
|
|
238
|
+
* Deliberate, and the only place the two readings differ. `activate()` runs
|
|
239
|
+
* from a `focus` / `pointerdown` handler, so the question it has to answer is
|
|
240
|
+
* "is this widget interactive at this instant" — the live read is that
|
|
241
|
+
* question, and it is never wrong. The memo can only lag the DOM inside the
|
|
242
|
+
* window between a DOM write and the observer's microtask, and no input event
|
|
243
|
+
* is dispatched in that window, so the two agree today; keeping the live read
|
|
244
|
+
* means they still agree if some future code writes an attribute and calls
|
|
245
|
+
* `activate()` in the same turn, where the memo would be a generation behind
|
|
246
|
+
* and would refuse a widget the user can see is enabled.
|
|
247
|
+
*
|
|
248
|
+
* `move()` and `focusBoundary()` take the memo instead because they need the
|
|
249
|
+
* *order* of the enabled set, and an order assembled from N independent live
|
|
250
|
+
* reads is exactly what the memo exists to prevent. Do not "harmonise" the
|
|
251
|
+
* two: they are asking different questions.
|
|
252
|
+
*/
|
|
224
253
|
activate(widget) {
|
|
225
254
|
if (this._widgets().includes(widget) && this._enabled(widget)) {
|
|
226
255
|
this._hasInteracted = true;
|
|
@@ -273,14 +302,42 @@ class MlvEditorToolbarRovingRegistry {
|
|
|
273
302
|
!widget.hasAttribute('disabled') &&
|
|
274
303
|
widget.getAttribute('aria-disabled') !== 'true');
|
|
275
304
|
}
|
|
276
|
-
|
|
305
|
+
/**
|
|
306
|
+
* @private Registered widgets that are currently interactive, in DOM order.
|
|
307
|
+
*
|
|
308
|
+
* Derived once per invalidation generation rather than once per widget.
|
|
309
|
+
* `isActive()` runs for every registered widget — 21 on the built-in
|
|
310
|
+
* toolbar — and Angular settles one `disabled` transition in about two
|
|
311
|
+
* change-detection passes, so the enabled order was assembled roughly
|
|
312
|
+
* `2N + 2` times, each assembly walking the DOM with `closest('[hidden]')`
|
|
313
|
+
* once per widget. The shape is therefore `(≈2N + c) derivations × N walks`,
|
|
314
|
+
* not the plain `N^2` an earlier draft of this comment claimed — `N^2` at
|
|
315
|
+
* N=21 is 441, and the measured cost is about twice that.
|
|
316
|
+
*
|
|
317
|
+
* Measured on the built-in toolbar at N=21, over `disabled` false → true:
|
|
318
|
+
* the code this replaced took 39 derivations / 841 walks; this file with only
|
|
319
|
+
* the `computed()` removed takes 44 / 924; as written it takes 1 / 21. The
|
|
320
|
+
* two pre-fix figures differ because the old `isActive()` short-circuited on
|
|
321
|
+
* a still-enabled `_active` and paid single `_enabled()` reads outside the
|
|
322
|
+
* derivations. 924 is the one the spec's guard is written against, since that
|
|
323
|
+
* ablation isolates this `computed()` and changes nothing else.
|
|
324
|
+
*
|
|
325
|
+
* `_domStateRevision` is what makes the memo correct. The enabled set is a
|
|
326
|
+
* DOM read, and the `MutationObserver` that bumps that signal is the only
|
|
327
|
+
* thing that tells the graph the DOM moved — the same dependency `isActive()`
|
|
328
|
+
* already relied on. Every consumer now shares one snapshot per generation
|
|
329
|
+
* instead of each re-reading the DOM at a different point in the same pass.
|
|
330
|
+
*/
|
|
331
|
+
_enabledWidgets = computed(() => {
|
|
332
|
+
this._domStateRevision();
|
|
277
333
|
return this._widgets()
|
|
278
334
|
.filter((widget) => this._enabled(widget))
|
|
279
335
|
.sort((left, right) => {
|
|
280
336
|
const position = left.compareDocumentPosition(right);
|
|
281
337
|
return position & 4 ? -1 : position & 2 ? 1 : 0;
|
|
282
338
|
});
|
|
283
|
-
}
|
|
339
|
+
}, /* @ts-ignore */
|
|
340
|
+
...(ngDevMode ? [{ debugName: "_enabledWidgets" }] : /* istanbul ignore next */ []));
|
|
284
341
|
_observeStateChanges() {
|
|
285
342
|
if (this._observer ||
|
|
286
343
|
typeof MutationObserver === 'undefined' ||
|
|
@@ -289,6 +346,21 @@ class MlvEditorToolbarRovingRegistry {
|
|
|
289
346
|
!(this._root instanceof Node)) {
|
|
290
347
|
return;
|
|
291
348
|
}
|
|
349
|
+
// No debounce, deliberately, and the durable reason is the memo below, not
|
|
350
|
+
// the batching: after it, a redundant invalidation costs exactly one
|
|
351
|
+
// derivation — 21 `closest('[hidden]')` walks on the built-in toolbar — so
|
|
352
|
+
// even a coalescer that did merge something would be buying ~nothing, and
|
|
353
|
+
// would pay for it by leaving the tab stop a turn stale.
|
|
354
|
+
//
|
|
355
|
+
// A `queueMicrotask` coalescer also merges nothing here, in either
|
|
356
|
+
// direction. Mutations written in one turn are already one callback:
|
|
357
|
+
// `MutationObserver` delivers once per microtask checkpoint carrying every
|
|
358
|
+
// record accumulated since the last delivery, so disabling the editor
|
|
359
|
+
// writes 22 attributes and lands here once ("coalesces a whole batch of
|
|
360
|
+
// observed mutations into one invalidation" pins that). Mutations spread
|
|
361
|
+
// across separate microtasks are not merged either — each already lands in
|
|
362
|
+
// its own turn, which is precisely where a per-turn debounce flushes.
|
|
363
|
+
// The cost that was worth removing was the fan-out below.
|
|
292
364
|
this._observer = new MutationObserver(() => {
|
|
293
365
|
this._domStateRevision.update((revision) => revision + 1);
|
|
294
366
|
this._synchronizeActiveWidget();
|
|
@@ -300,9 +372,23 @@ class MlvEditorToolbarRovingRegistry {
|
|
|
300
372
|
subtree: true,
|
|
301
373
|
});
|
|
302
374
|
}
|
|
375
|
+
/**
|
|
376
|
+
* @private Re-resolves `_active` when the widget holding the tab stop is no
|
|
377
|
+
* longer part of the enabled set.
|
|
378
|
+
*
|
|
379
|
+
* Reactive-graph note: this reads `_enabledWidgets()`, where it used to read
|
|
380
|
+
* a non-reactive `_enabled(active)`, so `MlvEditorToolbarRoot`'s effect —
|
|
381
|
+
* which reaches here through `setDisabled()` — now tracks `_widgets` and
|
|
382
|
+
* `_domStateRevision` on *both* branches, not just on the fall-through into
|
|
383
|
+
* `_focusFirstWidget()`. The effect therefore re-runs on every widget
|
|
384
|
+
* registration and every observed mutation. That is deliberate and cheap
|
|
385
|
+
* post-memo (a re-run costs one cached memo read), and it does not loop:
|
|
386
|
+
* `_active` converges in at most two runs, and the effect already both read
|
|
387
|
+
* and wrote `_active` before this change.
|
|
388
|
+
*/
|
|
303
389
|
_synchronizeActiveWidget() {
|
|
304
390
|
const active = this._active();
|
|
305
|
-
if (active && this.
|
|
391
|
+
if (active && this._enabledWidgets().includes(active))
|
|
306
392
|
return;
|
|
307
393
|
this._focusFirstWidget();
|
|
308
394
|
}
|
|
@@ -313,14 +399,28 @@ class MlvEditorToolbarRovingRegistry {
|
|
|
313
399
|
this._observer?.disconnect();
|
|
314
400
|
this._observer = undefined;
|
|
315
401
|
}
|
|
316
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
317
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.
|
|
402
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorToolbarRovingRegistry, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
403
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorToolbarRovingRegistry });
|
|
318
404
|
}
|
|
319
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
405
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorToolbarRovingRegistry, decorators: [{
|
|
320
406
|
type: Injectable
|
|
321
407
|
}] });
|
|
322
408
|
/** @internal Resolves the nearest editor's projected-toolbar roving registry. */
|
|
323
409
|
const MLV_EDITOR_TOOLBAR_ROVING = new InjectionToken('MLV_EDITOR_TOOLBAR_ROVING');
|
|
410
|
+
/**
|
|
411
|
+
* @internal Focuses the editor content with its selection. `view.focus()`
|
|
412
|
+
* alone moves no DOM focus while the view is not editable (`readonly`), so the
|
|
413
|
+
* contenteditable root is focused first; ProseMirror then writes the selection
|
|
414
|
+
* back to the DOM.
|
|
415
|
+
*/
|
|
416
|
+
function mlvEditorFocusContent(editor) {
|
|
417
|
+
if (editor.isDestroyed)
|
|
418
|
+
return;
|
|
419
|
+
if (!editor.view.editable) {
|
|
420
|
+
editor.view.dom.focus({ preventScroll: true });
|
|
421
|
+
}
|
|
422
|
+
editor.view.focus();
|
|
423
|
+
}
|
|
324
424
|
/**
|
|
325
425
|
* Internal per-editor registry for CDK overlay roots owned by toolbar controls.
|
|
326
426
|
* It is intentionally excluded from the public barrel: public controls use it
|
|
@@ -330,6 +430,14 @@ class MlvEditorOverlayRegistry {
|
|
|
330
430
|
_entries = new Map();
|
|
331
431
|
_onFocusIn;
|
|
332
432
|
_onFocusOut;
|
|
433
|
+
/**
|
|
434
|
+
* @private Net for the whole registry. Each entry is released the moment its
|
|
435
|
+
* overlay is removed — a destroy-scoped lifetime alone would hold every
|
|
436
|
+
* overlay ever opened — but this guarantees release even if a consumer never
|
|
437
|
+
* calls `destroy()` or `closeAll()`. The registry is provided by the editor
|
|
438
|
+
* component, so this ref is the editor's.
|
|
439
|
+
*/
|
|
440
|
+
_destroyRef = inject(DestroyRef);
|
|
333
441
|
/** @internal Wires the nearest editor's composite focus handlers. */
|
|
334
442
|
setFocusHandlers(onFocusIn, onFocusOut) {
|
|
335
443
|
this._onFocusIn = onFocusIn;
|
|
@@ -342,12 +450,17 @@ class MlvEditorOverlayRegistry {
|
|
|
342
450
|
existing.close = close;
|
|
343
451
|
return () => this._remove(root);
|
|
344
452
|
}
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
453
|
+
// Capture phase, matching the editor host's own boundary listeners: an
|
|
454
|
+
// overlay's content may stop focus events on its way back up, and the
|
|
455
|
+
// composite has to see the crossing regardless.
|
|
456
|
+
const listeners = new Subscription();
|
|
457
|
+
listeners.add(fromEvent(root, 'focusin', { capture: true })
|
|
458
|
+
.pipe(takeUntilDestroyed(this._destroyRef))
|
|
459
|
+
.subscribe((event) => this._onFocusIn?.(event)));
|
|
460
|
+
listeners.add(fromEvent(root, 'focusout', { capture: true })
|
|
461
|
+
.pipe(takeUntilDestroyed(this._destroyRef))
|
|
462
|
+
.subscribe((event) => this._onFocusOut?.(event)));
|
|
463
|
+
this._entries.set(root, { root, close, listeners });
|
|
351
464
|
return () => this._remove(root);
|
|
352
465
|
}
|
|
353
466
|
/** @internal Whether a node is rendered in an editor-owned overlay. */
|
|
@@ -370,6 +483,45 @@ class MlvEditorOverlayRegistry {
|
|
|
370
483
|
}
|
|
371
484
|
}
|
|
372
485
|
}
|
|
486
|
+
/**
|
|
487
|
+
* @internal Asks every owned overlay except `keep` to close and leaves each
|
|
488
|
+
* entry for its owner to release, unlike `closeAll()`: an entry with nothing
|
|
489
|
+
* to close (the AI review bar) keeps its focus listeners.
|
|
490
|
+
*/
|
|
491
|
+
closeOthers(keep) {
|
|
492
|
+
for (const entry of [...this._entries.values()]) {
|
|
493
|
+
if (entry.root === keep)
|
|
494
|
+
continue;
|
|
495
|
+
try {
|
|
496
|
+
entry.close();
|
|
497
|
+
}
|
|
498
|
+
catch {
|
|
499
|
+
// A third-party overlay must not keep the others open.
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* @internal A toolbar surface is going away because the editor turned
|
|
505
|
+
* `readonly` (#498): the docked band, which the next render removes, or the
|
|
506
|
+
* selection bubble's pane, which it hides. Asks every owned overlay except
|
|
507
|
+
* `surface` to close, like `closeOthers()`, and keeps focus in the editor:
|
|
508
|
+
* focus in `surface`, or in a popup (a registered root portaled into an
|
|
509
|
+
* overlay pane that `content` does not share; inside a dialog it does), moves
|
|
510
|
+
* to the content through `focusContent` first, while both still exist, so a
|
|
511
|
+
* closing popup finds focus outside its pane and hands nothing back to a
|
|
512
|
+
* trigger in `surface`. Focus anywhere else (the content, the status row,
|
|
513
|
+
* the AI review bar) stays where it is.
|
|
514
|
+
*/
|
|
515
|
+
closeForReadonly(surface, content, focusContent) {
|
|
516
|
+
const active = surface.ownerDocument.activeElement;
|
|
517
|
+
if (active &&
|
|
518
|
+
(surface.contains(active) ||
|
|
519
|
+
(this.contains(active) &&
|
|
520
|
+
!(active.closest('.cdk-overlay-pane')?.contains(content) ?? true)))) {
|
|
521
|
+
focusContent();
|
|
522
|
+
}
|
|
523
|
+
this.closeOthers(surface);
|
|
524
|
+
}
|
|
373
525
|
/** @internal Removes listener resources without invoking overlay close callbacks. */
|
|
374
526
|
destroy() {
|
|
375
527
|
for (const entry of [...this._entries.values()])
|
|
@@ -380,13 +532,12 @@ class MlvEditorOverlayRegistry {
|
|
|
380
532
|
if (!entry)
|
|
381
533
|
return;
|
|
382
534
|
this._entries.delete(root);
|
|
383
|
-
entry.
|
|
384
|
-
entry.root.removeEventListener('focusout', entry.onFocusOut, true);
|
|
535
|
+
entry.listeners.unsubscribe();
|
|
385
536
|
}
|
|
386
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
387
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.
|
|
537
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorOverlayRegistry, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
538
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorOverlayRegistry });
|
|
388
539
|
}
|
|
389
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
540
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorOverlayRegistry, decorators: [{
|
|
390
541
|
type: Injectable
|
|
391
542
|
}] });
|
|
392
543
|
/** @internal Injection token for editor-owned overlay registration. */
|
|
@@ -414,10 +565,10 @@ class MlvEditorUploadAbortRegistry {
|
|
|
414
565
|
}
|
|
415
566
|
}
|
|
416
567
|
}
|
|
417
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
418
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.
|
|
568
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorUploadAbortRegistry, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
569
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorUploadAbortRegistry });
|
|
419
570
|
}
|
|
420
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
571
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorUploadAbortRegistry, decorators: [{
|
|
421
572
|
type: Injectable
|
|
422
573
|
}] });
|
|
423
574
|
/** @internal Injection token for the editor-owned upload abort registry. */
|
|
@@ -2472,10 +2623,10 @@ class MlvEditorAiContext {
|
|
|
2472
2623
|
const text = editor.getText();
|
|
2473
2624
|
return text.length > 0 ? text : null;
|
|
2474
2625
|
}
|
|
2475
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
2476
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.
|
|
2626
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorAiContext, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
2627
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorAiContext });
|
|
2477
2628
|
}
|
|
2478
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
2629
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorAiContext, decorators: [{
|
|
2479
2630
|
type: Injectable
|
|
2480
2631
|
}] });
|
|
2481
2632
|
/** Injects the AI command state belonging to the nearest `mlv-editor`. */
|
|
@@ -2504,10 +2655,10 @@ class MlvEditorToolbarWidget {
|
|
|
2504
2655
|
_activateOnPointerDown() {
|
|
2505
2656
|
this._registry.activate(this._element);
|
|
2506
2657
|
}
|
|
2507
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
2508
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.
|
|
2658
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorToolbarWidget, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
2659
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.7", type: MlvEditorToolbarWidget, isStandalone: true, selector: "[mlvEditorToolbarWidget]", host: { listeners: { "focus": "_activate()", "pointerdown": "_activateOnPointerDown()" }, properties: { "attr.tabindex": "_tabIndex()" } }, ngImport: i0 });
|
|
2509
2660
|
}
|
|
2510
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
2661
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorToolbarWidget, decorators: [{
|
|
2511
2662
|
type: Directive,
|
|
2512
2663
|
args: [{
|
|
2513
2664
|
selector: '[mlvEditorToolbarWidget]',
|
|
@@ -2540,6 +2691,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
|
|
|
2540
2691
|
* resolves no provider (presence), and its actions disable — without hiding —
|
|
2541
2692
|
* while the editor is readonly or disabled or a transform is already running
|
|
2542
2693
|
* (executability), matching the toolbar's presence-vs-executability rule.
|
|
2694
|
+
* Projected into `mlv-editor`'s toolbar it is not rendered at all while the
|
|
2695
|
+
* editor is `readonly`, which renders no toolbar (#498).
|
|
2543
2696
|
*/
|
|
2544
2697
|
class MlvEditorAiMenu {
|
|
2545
2698
|
/**
|
|
@@ -2759,10 +2912,10 @@ class MlvEditorAiMenu {
|
|
|
2759
2912
|
this._unregisterMenu?.();
|
|
2760
2913
|
this._unregisterMenu = undefined;
|
|
2761
2914
|
}
|
|
2762
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
2763
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.
|
|
2915
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorAiMenu, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2916
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: MlvEditorAiMenu, isStandalone: true, selector: "mlv-editor-ai-menu", inputs: { actions: { classPropertyName: "actions", publicName: "actions", isSignal: true, isRequired: false, transformFunction: null }, showCustomPrompt: { classPropertyName: "showCustomPrompt", publicName: "showCustomPrompt", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "hidden": "!_hasProvider()" }, classAttribute: "mlv-editor-ai-menu" }, viewQueries: [{ propertyName: "_menu", first: true, predicate: ["_menu"], descendants: true, isSignal: true }, { propertyName: "_menuTrigger", first: true, predicate: MlvMenuTrigger, descendants: true, isSignal: true }, { propertyName: "_popup", first: true, predicate: ["_popup"], descendants: true, isSignal: true }, { propertyName: "_triggerElement", first: true, predicate: ["_triggerElement"], descendants: true, read: ElementRef, isSignal: true }, { propertyName: "_instructionInput", first: true, predicate: ["_instructionInput"], descendants: true, isSignal: true }, { propertyName: "_panel", first: true, predicate: ["_panel"], descendants: true, isSignal: true }], ngImport: i0, template: "<!-- The trigger is declared once and rendered by whichever branch applies:\n `mlv-popup-container` requires a popup content child, so it cannot wrap the\n trigger when the custom prompt is dropped. -->\n<ng-template #_trigger>\n <button\n #_triggerElement\n mlvButton\n mlvEditorToolbarWidget\n type=\"button\"\n shape=\"square\"\n mlvDensity=\"tight\"\n variant=\"transparent\"\n [disabled]=\"_disabled()\"\n [mlvMenuTrigger]=\"_menu\"\n [menuTriggerDisabled]=\"_disabled() || _running()\"\n [attr.aria-label]=\"_running() ? _copy().aiCancel : _copy().aiMenu\"\n [mlvTooltip]=\"_running() ? _copy().aiCancel : _copy().aiMenu\"\n (click)=\"_onTriggerClick()\"\n (menuOpened)=\"_registerMenuOverlay()\"\n (menuClosed)=\"_unregisterMenuOverlay()\"\n >\n @if (_running()) {\n <svg mlvButtonIcon lucideCircleStop aria-hidden=\"true\" />\n } @else {\n <svg mlvButtonIcon lucideSparkles aria-hidden=\"true\" />\n }\n </button>\n</ng-template>\n\n@if (showCustomPrompt()) {\n <mlv-popup-container class=\"mlv-editor-ai-menu__anchor\">\n <ng-container [ngTemplateOutlet]=\"_trigger\" />\n\n <mlv-popup\n #_popup\n panelRole=\"dialog\"\n [modal]=\"true\"\n [position]=\"['bottom-start', 'top-start', 'bottom-end', 'top-end']\"\n [ariaLabel]=\"_copy().aiCustom\"\n (afterOpened)=\"_onPromptOpened()\"\n (afterClosed)=\"_onPromptClosed()\"\n >\n <ng-template mlvPopupContent>\n <div #_panel class=\"mlv-editor-ai-menu__panel\">\n <mlv-input\n #_instructionInput\n [label]=\"_copy().aiCustom\"\n [placeholder]=\"_copy().aiPromptPlaceholder\"\n [disabled]=\"_disabled()\"\n [value]=\"_instruction()\"\n (valueChange)=\"_instruction.set($event)\"\n (keydown.enter)=\"_apply()\"\n />\n <mlv-radio-group\n class=\"mlv-editor-ai-menu__output\"\n [label]=\"_copy().aiOutputMode\"\n [value]=\"_output()\"\n (valueChange)=\"_onOutputChange($event)\"\n >\n <mlv-radio value=\"replace-selection\" [disabled]=\"_disabled()\">\n {{ _copy().aiReplaceSelection }}\n </mlv-radio>\n <mlv-radio value=\"insert-below\" [disabled]=\"_disabled()\">\n {{ _copy().aiInsertBelow }}\n </mlv-radio>\n <mlv-radio value=\"review\" [disabled]=\"_disabled()\">\n {{ _copy().aiReviewChanges }}\n </mlv-radio>\n </mlv-radio-group>\n <div class=\"mlv-editor-ai-menu__actions\">\n <button\n mlvButton\n type=\"button\"\n variant=\"primary\"\n [disabled]=\"_applyDisabled()\"\n (click)=\"_apply()\"\n >\n {{ _copy().aiApply }}\n </button>\n <button\n mlvButton\n type=\"button\"\n variant=\"secondary\"\n (click)=\"_closePrompt()\"\n >\n {{ _copy().aiCancel }}\n </button>\n </div>\n </div>\n </ng-template>\n </mlv-popup>\n </mlv-popup-container>\n} @else {\n <ng-container [ngTemplateOutlet]=\"_trigger\" />\n}\n\n<mlv-menu #_menu [label]=\"_copy().aiMenu\">\n <!-- Tracked by index: a host-authored list may repeat a kind, so the kind\n is not an identity. -->\n @for (action of _actions(); track $index) {\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_itemsDisabled()\"\n (itemClick)=\"_runAction(action)\"\n >\n {{ action.label }}\n </mlv-list-item>\n }\n @if (showCustomPrompt()) {\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_itemsDisabled()\"\n (itemClick)=\"_openPrompt()\"\n >\n {{ _copy().aiCustom }}\n </mlv-list-item>\n }\n</mlv-menu>\n", styles: ["@layer mlv.components{.mlv-editor-ai-menu{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor-ai-menu *{box-sizing:border-box}.mlv-editor-ai-menu,.mlv-editor-ai-menu__anchor{display:inline-flex}.mlv-editor-ai-menu__panel{display:grid;width:min(22rem,100vw - 3.5rem);gap:var(--mlv-spacing-1-5);padding:var(--mlv-spacing-2)}.mlv-editor-ai-menu__actions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:var(--mlv-spacing-3)}}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: MlvButton, selector: "button[mlvButton], a[mlvButton]", inputs: ["variant", "shape", "disabled", "loading", "selected"] }, { kind: "directive", type: MlvButtonIcon, selector: "[mlvButtonIcon]" }, { kind: "directive", type: MlvTooltip, selector: "[mlvTooltip]", inputs: ["mlvTooltip", "tooltipPlacement", "tooltipTone", "tooltipDelay", "tooltipDisabled", "tooltipArrow"] }, { kind: "component", type: LucideCircleStop, selector: "svg[lucideCircleStop], svg[lucideStopCircle]" }, { kind: "component", type: LucideSparkles, selector: "svg[lucideSparkles], svg[lucideStars]" }, { kind: "directive", type: MlvEditorToolbarWidget, selector: "[mlvEditorToolbarWidget]" }, { kind: "component", type: MlvInput, selector: "mlv-input", inputs: ["type", "placeholder", "autocomplete", "inputMode", "maxLength", "minLength", "pattern", "name", "autofocus", "min", "max", "step", "bare", "projectControl", "role", "ariaExpanded", "ariaHasPopup", "ariaAutocomplete", "ariaControls", "ariaActiveDescendant", "ariaDescribedBy", "value"], outputs: ["valueChange", "inputFocus", "inputBlur"] }, { kind: "component", type: MlvListItem, selector: "mlv-list-item", inputs: ["itemRole", "unread", "accent"] }, { kind: "component", type: MlvMenu, selector: "mlv-menu", inputs: ["label", "mlvDensity", "dataSource"], outputs: ["closed"] }, { kind: "directive", type: MlvMenuItem, selector: "mlv-list-item[mlvMenuItem]", inputs: ["disabled"], outputs: ["itemClick"] }, { kind: "directive", type: MlvMenuTrigger, selector: "[mlvMenuTrigger]", inputs: ["mlvMenuTrigger", "isSubmenuTrigger", "menuTriggerDisabled"], outputs: ["menuOpened", "menuClosed"] }, { kind: "component", type: MlvPopup, selector: "mlv-popup", inputs: ["position", "positions", "width", "height", "minWidth", "minHeight", "maxWidth", "maxHeight", "hasArrow", "hasShadow", "class", "mlvDensity", "flexibleDimensions", "scrollStrategy", "hasBackdrop", "restrictPosition", "panelRole", "modal", "ariaLabel", "dismissExcludeElements", "mobileMode", "mobileBreakpoint", "mobileTitle", "mobileCloseLabel", "opened"], outputs: ["openedChange", "afterOpened", "afterClosed"] }, { kind: "component", type: MlvPopupContainer, selector: "mlv-popup-container" }, { kind: "directive", type: MlvPopupContent, selector: "[mlvPopupContent]" }, { kind: "component", type: MlvRadio, selector: "mlv-radio", inputs: ["ariaLabel", "ariaLabelledBy", "value", "disabled"] }, { kind: "component", type: MlvRadioGroup, selector: "mlv-radio-group", inputs: ["value", "name"], outputs: ["valueChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
2764
2917
|
}
|
|
2765
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
2918
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorAiMenu, decorators: [{
|
|
2766
2919
|
type: Component,
|
|
2767
2920
|
args: [{ selector: 'mlv-editor-ai-menu', imports: [
|
|
2768
2921
|
NgTemplateOutlet,
|
|
@@ -2785,7 +2938,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
|
|
|
2785
2938
|
], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
2786
2939
|
class: 'mlv-editor-ai-menu',
|
|
2787
2940
|
'[hidden]': '!_hasProvider()',
|
|
2788
|
-
}, template: "<!-- The trigger is declared once and rendered by whichever branch applies:\n `mlv-popup-container` requires a popup content child, so it cannot wrap the\n trigger when the custom prompt is dropped. -->\n<ng-template #_trigger>\n <button\n #_triggerElement\n mlvButton\n mlvEditorToolbarWidget\n type=\"button\"\n shape=\"square\"\n mlvDensity=\"tight\"\n variant=\"transparent\"\n [disabled]=\"_disabled()\"\n [mlvMenuTrigger]=\"_menu\"\n [menuTriggerDisabled]=\"_disabled() || _running()\"\n [attr.aria-label]=\"_running() ? _copy().aiCancel : _copy().aiMenu\"\n [mlvTooltip]=\"_running() ? _copy().aiCancel : _copy().aiMenu\"\n (click)=\"_onTriggerClick()\"\n (menuOpened)=\"_registerMenuOverlay()\"\n (menuClosed)=\"_unregisterMenuOverlay()\"\n >\n @if (_running()) {\n
|
|
2941
|
+
}, template: "<!-- The trigger is declared once and rendered by whichever branch applies:\n `mlv-popup-container` requires a popup content child, so it cannot wrap the\n trigger when the custom prompt is dropped. -->\n<ng-template #_trigger>\n <button\n #_triggerElement\n mlvButton\n mlvEditorToolbarWidget\n type=\"button\"\n shape=\"square\"\n mlvDensity=\"tight\"\n variant=\"transparent\"\n [disabled]=\"_disabled()\"\n [mlvMenuTrigger]=\"_menu\"\n [menuTriggerDisabled]=\"_disabled() || _running()\"\n [attr.aria-label]=\"_running() ? _copy().aiCancel : _copy().aiMenu\"\n [mlvTooltip]=\"_running() ? _copy().aiCancel : _copy().aiMenu\"\n (click)=\"_onTriggerClick()\"\n (menuOpened)=\"_registerMenuOverlay()\"\n (menuClosed)=\"_unregisterMenuOverlay()\"\n >\n @if (_running()) {\n <svg mlvButtonIcon lucideCircleStop aria-hidden=\"true\" />\n } @else {\n <svg mlvButtonIcon lucideSparkles aria-hidden=\"true\" />\n }\n </button>\n</ng-template>\n\n@if (showCustomPrompt()) {\n <mlv-popup-container class=\"mlv-editor-ai-menu__anchor\">\n <ng-container [ngTemplateOutlet]=\"_trigger\" />\n\n <mlv-popup\n #_popup\n panelRole=\"dialog\"\n [modal]=\"true\"\n [position]=\"['bottom-start', 'top-start', 'bottom-end', 'top-end']\"\n [ariaLabel]=\"_copy().aiCustom\"\n (afterOpened)=\"_onPromptOpened()\"\n (afterClosed)=\"_onPromptClosed()\"\n >\n <ng-template mlvPopupContent>\n <div #_panel class=\"mlv-editor-ai-menu__panel\">\n <mlv-input\n #_instructionInput\n [label]=\"_copy().aiCustom\"\n [placeholder]=\"_copy().aiPromptPlaceholder\"\n [disabled]=\"_disabled()\"\n [value]=\"_instruction()\"\n (valueChange)=\"_instruction.set($event)\"\n (keydown.enter)=\"_apply()\"\n />\n <mlv-radio-group\n class=\"mlv-editor-ai-menu__output\"\n [label]=\"_copy().aiOutputMode\"\n [value]=\"_output()\"\n (valueChange)=\"_onOutputChange($event)\"\n >\n <mlv-radio value=\"replace-selection\" [disabled]=\"_disabled()\">\n {{ _copy().aiReplaceSelection }}\n </mlv-radio>\n <mlv-radio value=\"insert-below\" [disabled]=\"_disabled()\">\n {{ _copy().aiInsertBelow }}\n </mlv-radio>\n <mlv-radio value=\"review\" [disabled]=\"_disabled()\">\n {{ _copy().aiReviewChanges }}\n </mlv-radio>\n </mlv-radio-group>\n <div class=\"mlv-editor-ai-menu__actions\">\n <button\n mlvButton\n type=\"button\"\n variant=\"primary\"\n [disabled]=\"_applyDisabled()\"\n (click)=\"_apply()\"\n >\n {{ _copy().aiApply }}\n </button>\n <button\n mlvButton\n type=\"button\"\n variant=\"secondary\"\n (click)=\"_closePrompt()\"\n >\n {{ _copy().aiCancel }}\n </button>\n </div>\n </div>\n </ng-template>\n </mlv-popup>\n </mlv-popup-container>\n} @else {\n <ng-container [ngTemplateOutlet]=\"_trigger\" />\n}\n\n<mlv-menu #_menu [label]=\"_copy().aiMenu\">\n <!-- Tracked by index: a host-authored list may repeat a kind, so the kind\n is not an identity. -->\n @for (action of _actions(); track $index) {\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_itemsDisabled()\"\n (itemClick)=\"_runAction(action)\"\n >\n {{ action.label }}\n </mlv-list-item>\n }\n @if (showCustomPrompt()) {\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_itemsDisabled()\"\n (itemClick)=\"_openPrompt()\"\n >\n {{ _copy().aiCustom }}\n </mlv-list-item>\n }\n</mlv-menu>\n", styles: ["@layer mlv.components{.mlv-editor-ai-menu{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor-ai-menu *{box-sizing:border-box}.mlv-editor-ai-menu,.mlv-editor-ai-menu__anchor{display:inline-flex}.mlv-editor-ai-menu__panel{display:grid;width:min(22rem,100vw - 3.5rem);gap:var(--mlv-spacing-1-5);padding:var(--mlv-spacing-2)}.mlv-editor-ai-menu__actions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:var(--mlv-spacing-3)}}\n"] }]
|
|
2789
2942
|
}], ctorParameters: () => [], propDecorators: { actions: [{ type: i0.Input, args: [{ isSignal: true, alias: "actions", required: false }] }], showCustomPrompt: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCustomPrompt", required: false }] }], _menu: [{ type: i0.ViewChild, args: ['_menu', { isSignal: true }] }], _menuTrigger: [{ type: i0.ViewChild, args: [i0.forwardRef(() => MlvMenuTrigger), { isSignal: true }] }], _popup: [{ type: i0.ViewChild, args: ['_popup', { isSignal: true }] }], _triggerElement: [{ type: i0.ViewChild, args: ['_triggerElement', { ...{
|
|
2790
2943
|
read: ElementRef,
|
|
2791
2944
|
}, isSignal: true }] }], _instructionInput: [{ type: i0.ViewChild, args: ['_instructionInput', { isSignal: true }] }], _panel: [{ type: i0.ViewChild, args: ['_panel', { isSignal: true }] }] } });
|
|
@@ -2983,10 +3136,10 @@ class MlvEditorAiReviewBar {
|
|
|
2983
3136
|
_rejectAll() {
|
|
2984
3137
|
this._ai?.rejectAll();
|
|
2985
3138
|
}
|
|
2986
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
2987
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.
|
|
3139
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorAiReviewBar, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3140
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: MlvEditorAiReviewBar, isStandalone: true, selector: "mlv-editor-ai-review-bar", host: { attributes: { "role": "group" }, properties: { "attr.aria-label": "_copy().aiReviewBar", "hidden": "!_visible()" }, classAttribute: "mlv-editor-ai-review-bar" }, ngImport: i0, template: "@if (_running()) {\n <button\n mlvButton\n type=\"button\"\n variant=\"secondary\"\n mlvDensity=\"tight\"\n class=\"mlv-editor-ai-review-bar__stop\"\n [disabled]=\"_disabled()\"\n (click)=\"_stop()\"\n >\n <svg mlvButtonIcon lucideCircleStop aria-hidden=\"true\" />\n {{ _copy().aiStopGeneration }}\n </button>\n}\n@if (_reviewing()) {\n <span class=\"mlv-editor-ai-review-bar__count\">{{ _countText() }}</span>\n <!-- Screen-reader-only description of the current suggestion: the removed\n text exists only in the aria-hidden strikethrough widget, so the\n accept/reject buttons point here via aria-describedby. -->\n <span\n class=\"mlv-editor-ai-review-bar__current-description\"\n [id]=\"_descriptionId\"\n >{{ _currentDescription() }}</span\n >\n <div class=\"mlv-editor-ai-review-bar__group\">\n <button\n mlvButton\n type=\"button\"\n shape=\"square\"\n variant=\"transparent\"\n mlvDensity=\"tight\"\n [disabled]=\"_disabled()\"\n [attr.aria-label]=\"_copy().aiPreviousSuggestion\"\n [mlvTooltip]=\"_copy().aiPreviousSuggestion\"\n (click)=\"_previous()\"\n >\n <svg mlvButtonIcon lucideChevronLeft aria-hidden=\"true\" />\n </button>\n <button\n mlvButton\n type=\"button\"\n shape=\"square\"\n variant=\"transparent\"\n mlvDensity=\"tight\"\n [disabled]=\"_disabled()\"\n [attr.aria-label]=\"_copy().aiNextSuggestion\"\n [mlvTooltip]=\"_copy().aiNextSuggestion\"\n (click)=\"_next()\"\n >\n <svg mlvButtonIcon lucideChevronRight aria-hidden=\"true\" />\n </button>\n </div>\n <div class=\"mlv-editor-ai-review-bar__group\">\n <button\n mlvButton\n type=\"button\"\n shape=\"square\"\n variant=\"transparent\"\n mlvDensity=\"tight\"\n [disabled]=\"_disabled()\"\n [attr.aria-label]=\"_copy().aiAcceptSuggestion\"\n [attr.aria-describedby]=\"_descriptionId\"\n [mlvTooltip]=\"_copy().aiAcceptSuggestion\"\n (click)=\"_acceptCurrent()\"\n >\n <svg mlvButtonIcon lucideCheck aria-hidden=\"true\" />\n </button>\n <button\n mlvButton\n type=\"button\"\n shape=\"square\"\n variant=\"transparent\"\n mlvDensity=\"tight\"\n [disabled]=\"_disabled()\"\n [attr.aria-label]=\"_copy().aiRejectSuggestion\"\n [attr.aria-describedby]=\"_descriptionId\"\n [mlvTooltip]=\"_copy().aiRejectSuggestion\"\n (click)=\"_rejectCurrent()\"\n >\n <svg mlvButtonIcon lucideX aria-hidden=\"true\" />\n </button>\n </div>\n <div class=\"mlv-editor-ai-review-bar__group\">\n <button\n mlvButton\n type=\"button\"\n variant=\"primary\"\n mlvDensity=\"tight\"\n [disabled]=\"_disabled()\"\n (click)=\"_acceptAll()\"\n >\n {{ _copy().aiAcceptAll }}\n </button>\n <button\n mlvButton\n type=\"button\"\n variant=\"secondary\"\n mlvDensity=\"tight\"\n [disabled]=\"_disabled()\"\n (click)=\"_rejectAll()\"\n >\n {{ _copy().aiRejectAll }}\n </button>\n </div>\n}\n", styles: ["@layer mlv.components{.mlv-editor-ai-review-bar{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor-ai-review-bar *{box-sizing:border-box}.mlv-editor-ai-review-bar{display:inline-flex;flex-wrap:wrap;align-items:center;gap:var(--mlv-spacing-2);font-size:var(--mlv-typography-body-s-size)}.mlv-editor-ai-review-bar[hidden]{display:none}.mlv-editor-ai-review-bar__count{color:var(--mlv-text-secondary);white-space:nowrap}.mlv-editor-ai-review-bar__group{display:inline-flex;align-items:center;gap:var(--mlv-spacing-1)}.mlv-editor-ai-review-bar__stop{white-space:nowrap}.mlv-editor-ai-review-bar__current-description{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0;padding:0;margin:-1px}}\n"], dependencies: [{ kind: "component", type: LucideCheck, selector: "svg[lucideCheck]" }, { kind: "component", type: LucideChevronLeft, selector: "svg[lucideChevronLeft]" }, { kind: "component", type: LucideChevronRight, selector: "svg[lucideChevronRight]" }, { kind: "component", type: LucideCircleStop, selector: "svg[lucideCircleStop], svg[lucideStopCircle]" }, { kind: "component", type: LucideX, selector: "svg[lucideX]" }, { kind: "component", type: MlvButton, selector: "button[mlvButton], a[mlvButton]", inputs: ["variant", "shape", "disabled", "loading", "selected"] }, { kind: "directive", type: MlvButtonIcon, selector: "[mlvButtonIcon]" }, { kind: "directive", type: MlvTooltip, selector: "[mlvTooltip]", inputs: ["mlvTooltip", "tooltipPlacement", "tooltipTone", "tooltipDelay", "tooltipDisabled", "tooltipArrow"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
2988
3141
|
}
|
|
2989
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
3142
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorAiReviewBar, decorators: [{
|
|
2990
3143
|
type: Component,
|
|
2991
3144
|
args: [{ selector: 'mlv-editor-ai-review-bar', imports: [
|
|
2992
3145
|
LucideCheck,
|
|
@@ -3002,7 +3155,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
|
|
|
3002
3155
|
role: 'group',
|
|
3003
3156
|
'[attr.aria-label]': '_copy().aiReviewBar',
|
|
3004
3157
|
'[hidden]': '!_visible()',
|
|
3005
|
-
}, template: "@if (_running()) {\n<button\n
|
|
3158
|
+
}, template: "@if (_running()) {\n <button\n mlvButton\n type=\"button\"\n variant=\"secondary\"\n mlvDensity=\"tight\"\n class=\"mlv-editor-ai-review-bar__stop\"\n [disabled]=\"_disabled()\"\n (click)=\"_stop()\"\n >\n <svg mlvButtonIcon lucideCircleStop aria-hidden=\"true\" />\n {{ _copy().aiStopGeneration }}\n </button>\n}\n@if (_reviewing()) {\n <span class=\"mlv-editor-ai-review-bar__count\">{{ _countText() }}</span>\n <!-- Screen-reader-only description of the current suggestion: the removed\n text exists only in the aria-hidden strikethrough widget, so the\n accept/reject buttons point here via aria-describedby. -->\n <span\n class=\"mlv-editor-ai-review-bar__current-description\"\n [id]=\"_descriptionId\"\n >{{ _currentDescription() }}</span\n >\n <div class=\"mlv-editor-ai-review-bar__group\">\n <button\n mlvButton\n type=\"button\"\n shape=\"square\"\n variant=\"transparent\"\n mlvDensity=\"tight\"\n [disabled]=\"_disabled()\"\n [attr.aria-label]=\"_copy().aiPreviousSuggestion\"\n [mlvTooltip]=\"_copy().aiPreviousSuggestion\"\n (click)=\"_previous()\"\n >\n <svg mlvButtonIcon lucideChevronLeft aria-hidden=\"true\" />\n </button>\n <button\n mlvButton\n type=\"button\"\n shape=\"square\"\n variant=\"transparent\"\n mlvDensity=\"tight\"\n [disabled]=\"_disabled()\"\n [attr.aria-label]=\"_copy().aiNextSuggestion\"\n [mlvTooltip]=\"_copy().aiNextSuggestion\"\n (click)=\"_next()\"\n >\n <svg mlvButtonIcon lucideChevronRight aria-hidden=\"true\" />\n </button>\n </div>\n <div class=\"mlv-editor-ai-review-bar__group\">\n <button\n mlvButton\n type=\"button\"\n shape=\"square\"\n variant=\"transparent\"\n mlvDensity=\"tight\"\n [disabled]=\"_disabled()\"\n [attr.aria-label]=\"_copy().aiAcceptSuggestion\"\n [attr.aria-describedby]=\"_descriptionId\"\n [mlvTooltip]=\"_copy().aiAcceptSuggestion\"\n (click)=\"_acceptCurrent()\"\n >\n <svg mlvButtonIcon lucideCheck aria-hidden=\"true\" />\n </button>\n <button\n mlvButton\n type=\"button\"\n shape=\"square\"\n variant=\"transparent\"\n mlvDensity=\"tight\"\n [disabled]=\"_disabled()\"\n [attr.aria-label]=\"_copy().aiRejectSuggestion\"\n [attr.aria-describedby]=\"_descriptionId\"\n [mlvTooltip]=\"_copy().aiRejectSuggestion\"\n (click)=\"_rejectCurrent()\"\n >\n <svg mlvButtonIcon lucideX aria-hidden=\"true\" />\n </button>\n </div>\n <div class=\"mlv-editor-ai-review-bar__group\">\n <button\n mlvButton\n type=\"button\"\n variant=\"primary\"\n mlvDensity=\"tight\"\n [disabled]=\"_disabled()\"\n (click)=\"_acceptAll()\"\n >\n {{ _copy().aiAcceptAll }}\n </button>\n <button\n mlvButton\n type=\"button\"\n variant=\"secondary\"\n mlvDensity=\"tight\"\n [disabled]=\"_disabled()\"\n (click)=\"_rejectAll()\"\n >\n {{ _copy().aiRejectAll }}\n </button>\n </div>\n}\n", styles: ["@layer mlv.components{.mlv-editor-ai-review-bar{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor-ai-review-bar *{box-sizing:border-box}.mlv-editor-ai-review-bar{display:inline-flex;flex-wrap:wrap;align-items:center;gap:var(--mlv-spacing-2);font-size:var(--mlv-typography-body-s-size)}.mlv-editor-ai-review-bar[hidden]{display:none}.mlv-editor-ai-review-bar__count{color:var(--mlv-text-secondary);white-space:nowrap}.mlv-editor-ai-review-bar__group{display:inline-flex;align-items:center;gap:var(--mlv-spacing-1)}.mlv-editor-ai-review-bar__stop{white-space:nowrap}.mlv-editor-ai-review-bar__current-description{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0;padding:0;margin:-1px}}\n"] }]
|
|
3006
3159
|
}], ctorParameters: () => [] });
|
|
3007
3160
|
|
|
3008
3161
|
/**
|
|
@@ -3238,18 +3391,793 @@ function preflightMlvEditorExtensions(extensions, initialFormat) {
|
|
|
3238
3391
|
}
|
|
3239
3392
|
}
|
|
3240
3393
|
|
|
3394
|
+
/**
|
|
3395
|
+
* @internal Resolves an `MlvEditor` sizing input to the value its custom
|
|
3396
|
+
* property receives, or `null` to write nothing.
|
|
3397
|
+
*
|
|
3398
|
+
* A finite number is px, matching `mlv-popup`. A string passes through
|
|
3399
|
+
* verbatim once trimmed, so any CSS length, `calc()`, `min()` or viewport unit
|
|
3400
|
+
* works. `undefined`, `null`, a blank string and a non-finite number all
|
|
3401
|
+
* resolve to `null`, so the host binding removes the property and the
|
|
3402
|
+
* stylesheet's `var()` fallback applies.
|
|
3403
|
+
*/
|
|
3404
|
+
function mlvEditorCssLength(value) {
|
|
3405
|
+
if (typeof value === 'number') {
|
|
3406
|
+
return Number.isFinite(value) ? `${value}px` : null;
|
|
3407
|
+
}
|
|
3408
|
+
if (typeof value === 'string') {
|
|
3409
|
+
const trimmed = value.trim();
|
|
3410
|
+
return trimmed === '' ? null : trimmed;
|
|
3411
|
+
}
|
|
3412
|
+
return null;
|
|
3413
|
+
}
|
|
3414
|
+
/** @internal ProseMirror's own default `scrollMargin`, in px (prosemirror-view `scrollRectIntoView`). */
|
|
3415
|
+
const MLV_EDITOR_SCROLL_MARGIN = 5;
|
|
3416
|
+
/**
|
|
3417
|
+
* @internal ProseMirror `scrollMargin` / `scrollThreshold` for a toolbar that
|
|
3418
|
+
* obscures `obscured` px on its own side (WCAG 2.2 SC 2.4.11). The threshold is
|
|
3419
|
+
* the obscured extent: ProseMirror scrolls only once the caret enters it. The
|
|
3420
|
+
* margin leaves the default 5px beyond it. Every other side keeps
|
|
3421
|
+
* ProseMirror's defaults.
|
|
3422
|
+
*/
|
|
3423
|
+
function mlvEditorScrollSides(kind, position, obscured) {
|
|
3424
|
+
const base = kind === 'margin' ? MLV_EDITOR_SCROLL_MARGIN : 0;
|
|
3425
|
+
const edge = base + Math.max(0, Math.ceil(obscured));
|
|
3426
|
+
return {
|
|
3427
|
+
top: position === 'top' ? edge : base,
|
|
3428
|
+
right: base,
|
|
3429
|
+
bottom: position === 'bottom' ? edge : base,
|
|
3430
|
+
left: base,
|
|
3431
|
+
};
|
|
3432
|
+
}
|
|
3433
|
+
/**
|
|
3434
|
+
* @internal A scroll-prop object whose sides re-resolve on every read.
|
|
3435
|
+
* ProseMirror reads `value[side]` lazily each time it scrolls a rect into view
|
|
3436
|
+
* (`getSide()` in prosemirror-view), so one stable object follows density,
|
|
3437
|
+
* configuration and consumer CSS offsets without `setProps` churn. Only the
|
|
3438
|
+
* toolbar's side measures anything.
|
|
3439
|
+
*/
|
|
3440
|
+
function createMlvEditorLiveScrollSides(kind, position, obscured) {
|
|
3441
|
+
const base = kind === 'margin' ? MLV_EDITOR_SCROLL_MARGIN : 0;
|
|
3442
|
+
const side = (which) => position() === which
|
|
3443
|
+
? mlvEditorScrollSides(kind, which, obscured())[which]
|
|
3444
|
+
: base;
|
|
3445
|
+
return {
|
|
3446
|
+
get top() {
|
|
3447
|
+
return side('top');
|
|
3448
|
+
},
|
|
3449
|
+
get right() {
|
|
3450
|
+
return base;
|
|
3451
|
+
},
|
|
3452
|
+
get bottom() {
|
|
3453
|
+
return side('bottom');
|
|
3454
|
+
},
|
|
3455
|
+
get left() {
|
|
3456
|
+
return base;
|
|
3457
|
+
},
|
|
3458
|
+
};
|
|
3459
|
+
}
|
|
3460
|
+
/**
|
|
3461
|
+
* @internal The part of `rect` inside `boundary`, or `null` once none of it
|
|
3462
|
+
* is. Edges count as inside, so a zero-size caret lying on the boundary is
|
|
3463
|
+
* still visible.
|
|
3464
|
+
*
|
|
3465
|
+
* The selection bubble anchors on this: while a capped viewport scrolls, it
|
|
3466
|
+
* follows the visible part of the selection, and when the selection has left
|
|
3467
|
+
* the viewport (or the window) it hides instead of floating over content the
|
|
3468
|
+
* selection is no longer in.
|
|
3469
|
+
*/
|
|
3470
|
+
function mlvEditorVisibleRect(rect, boundary) {
|
|
3471
|
+
const top = Math.max(rect.top, boundary.top);
|
|
3472
|
+
const bottom = Math.min(rect.bottom, boundary.bottom);
|
|
3473
|
+
const left = Math.max(rect.left, boundary.left);
|
|
3474
|
+
const right = Math.min(rect.right, boundary.right);
|
|
3475
|
+
return top <= bottom && left <= right ? { top, bottom, left, right } : null;
|
|
3476
|
+
}
|
|
3477
|
+
/**
|
|
3478
|
+
* @internal Which side of `anchor` the selection bubble opens on: above,
|
|
3479
|
+
* unless a bubble `height` px tall, `gap` px from the anchor, would cross the
|
|
3480
|
+
* boundary's top edge (the window, or a capped scroller's edge).
|
|
3481
|
+
*
|
|
3482
|
+
* CDK's own fallback flips only on the window's edges; a capped viewport's top
|
|
3483
|
+
* edge is not one it knows about, so the preference is decided here and CDK
|
|
3484
|
+
* keeps the other side as its fallback.
|
|
3485
|
+
*/
|
|
3486
|
+
function mlvEditorBubblePlacement(anchor, boundary, height, gap) {
|
|
3487
|
+
return anchor.top - gap - height >= boundary.top ? 'above' : 'below';
|
|
3488
|
+
}
|
|
3489
|
+
|
|
3490
|
+
/** @internal Gap between the selection and the bubble, in px. */
|
|
3491
|
+
const MLV_EDITOR_BUBBLE_GAP = 8;
|
|
3492
|
+
/** @internal Closest the bubble comes to a window edge, in px. */
|
|
3493
|
+
const MLV_EDITOR_BUBBLE_VIEWPORT_MARGIN = 8;
|
|
3494
|
+
/** @internal Pane class; the bubble's BEM block. */
|
|
3495
|
+
const MLV_EDITOR_BUBBLE_CLASS = 'mlv-editor-bubble';
|
|
3496
|
+
/** @internal Pane modifier while the bubble is off screen. */
|
|
3497
|
+
const MLV_EDITOR_BUBBLE_HIDDEN_CLASS = 'mlv-editor-bubble--hidden';
|
|
3498
|
+
/** @internal Pane modifier while CDK resolved the below position. */
|
|
3499
|
+
const MLV_EDITOR_BUBBLE_BELOW_CLASS = 'mlv-editor-bubble--below';
|
|
3500
|
+
/** @internal Centred above the selection. */
|
|
3501
|
+
const MLV_EDITOR_BUBBLE_ABOVE = {
|
|
3502
|
+
originX: 'center',
|
|
3503
|
+
originY: 'top',
|
|
3504
|
+
overlayX: 'center',
|
|
3505
|
+
overlayY: 'bottom',
|
|
3506
|
+
offsetY: -MLV_EDITOR_BUBBLE_GAP,
|
|
3507
|
+
};
|
|
3508
|
+
/** @internal Centred below the selection. */
|
|
3509
|
+
const MLV_EDITOR_BUBBLE_BELOW = {
|
|
3510
|
+
originX: 'center',
|
|
3511
|
+
originY: 'bottom',
|
|
3512
|
+
overlayX: 'center',
|
|
3513
|
+
overlayY: 'top',
|
|
3514
|
+
offsetY: MLV_EDITOR_BUBBLE_GAP,
|
|
3515
|
+
};
|
|
3516
|
+
/** @internal Above first; CDK falls back below on a window edge. */
|
|
3517
|
+
const MLV_EDITOR_BUBBLE_ABOVE_FIRST = [
|
|
3518
|
+
MLV_EDITOR_BUBBLE_ABOVE,
|
|
3519
|
+
MLV_EDITOR_BUBBLE_BELOW,
|
|
3520
|
+
];
|
|
3521
|
+
/** @internal Below first, once above would cross the boundary. */
|
|
3522
|
+
const MLV_EDITOR_BUBBLE_BELOW_FIRST = [
|
|
3523
|
+
MLV_EDITOR_BUBBLE_BELOW,
|
|
3524
|
+
MLV_EDITOR_BUBBLE_ABOVE,
|
|
3525
|
+
];
|
|
3526
|
+
/** @internal Elements a press may focus; a press anywhere else keeps the caret. */
|
|
3527
|
+
const MLV_EDITOR_BUBBLE_FOCUSABLE = 'button, a[href], input, select, textarea, [tabindex], [contenteditable="true"]';
|
|
3528
|
+
/**
|
|
3529
|
+
* @internal Renders its template in a CDK overlay that floats over the
|
|
3530
|
+
* nearest `mlv-editor`'s selection: `toolbarAppearance="floating"`.
|
|
3531
|
+
*
|
|
3532
|
+
* The contents are whatever the template holds, so the same host can carry a
|
|
3533
|
+
* different set of controls later; `MlvEditor` stamps its toolbar template.
|
|
3534
|
+
* The overlay is created once and kept attached, hidden by a pane modifier,
|
|
3535
|
+
* so the controls keep their state and their narrow-mode measurement between
|
|
3536
|
+
* appearances instead of being re-created per selection. The one exception is
|
|
3537
|
+
* a `readonly` flip: `MlvEditor` stamps no toolbar into the template while
|
|
3538
|
+
* `readonly` (#498), so the pane is empty then and the controls are
|
|
3539
|
+
* re-created on the way back.
|
|
3540
|
+
*
|
|
3541
|
+
* Shown only while all of these hold: focus is in the editor composite, the
|
|
3542
|
+
* selection is non-empty (or Alt+F10 summoned it at the caret), no pointer is
|
|
3543
|
+
* still selecting, no drag or IME composition is in progress, the editor is
|
|
3544
|
+
* enabled and not `readonly`, and the selection is visible inside the window
|
|
3545
|
+
* and, for a capped editor, inside its scrolling viewport. It prefers above
|
|
3546
|
+
* the selection and flips below only when above would cross that boundary's
|
|
3547
|
+
* top edge. Turning `readonly` on while it is shown hides it and closes every
|
|
3548
|
+
* popup the editor owns; focus inside either returns to the content.
|
|
3549
|
+
*
|
|
3550
|
+
* Keyboard: Alt+F10 in the content shows it and moves focus to its first
|
|
3551
|
+
* control through the toolbar's roving registry; Escape dismisses it and
|
|
3552
|
+
* returns focus to the content with the selection intact; Tab leaves it back
|
|
3553
|
+
* into the content rather than for the end of the document, where the portal
|
|
3554
|
+
* sits. Appearing never moves focus.
|
|
3555
|
+
*
|
|
3556
|
+
* Escape and Alt+F10 in the content go through a ProseMirror plugin, not a DOM
|
|
3557
|
+
* listener: ProseMirror cancels every Escape it sees, so a listener behind it
|
|
3558
|
+
* would find each one already `defaultPrevented`, and one in front of it
|
|
3559
|
+
* would take Escape from a content popup. The plugin offers both keys to
|
|
3560
|
+
* every plugin registered after it and acts only when none claims them, so a
|
|
3561
|
+
* consumer's `@tiptap/suggestion` list or an AI stream still gets Escape
|
|
3562
|
+
* first. A dismissing Escape stops propagating, so a dialog or drawer around
|
|
3563
|
+
* the editor stays open; the next one reaches it. While `readonly` the bubble
|
|
3564
|
+
* claims neither key.
|
|
3565
|
+
*/
|
|
3566
|
+
class MlvEditorBubble {
|
|
3567
|
+
/** The editor's content viewport: the listener scope and the capped boundary. */
|
|
3568
|
+
viewport = input.required(/* @ts-ignore */
|
|
3569
|
+
...(ngDevMode ? [{ debugName: "viewport" }] : /* istanbul ignore next */ []));
|
|
3570
|
+
/** Whether the viewport is the editor's scroll container, bounding where the selection counts as visible. */
|
|
3571
|
+
capped = input(false, { ...(ngDevMode ? { debugName: "capped" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
|
|
3572
|
+
/** @private Template rendered into the overlay pane. */
|
|
3573
|
+
_templateRef = inject(TemplateRef);
|
|
3574
|
+
/** @private Container the template view is created in, so it keeps the editor's injector. */
|
|
3575
|
+
_viewContainerRef = inject(ViewContainerRef);
|
|
3576
|
+
/** @private CDK overlay factory. */
|
|
3577
|
+
_overlay = inject(Overlay);
|
|
3578
|
+
/** @private Injected document; never the ambient global. */
|
|
3579
|
+
_document = inject(DOCUMENT);
|
|
3580
|
+
/** @private Directive lifetime; owns every listener below. */
|
|
3581
|
+
_destroyRef = inject(DestroyRef);
|
|
3582
|
+
/** @private Editor state: instance, focus, disabled and zoom. */
|
|
3583
|
+
_context = inject(MLV_EDITOR_TOOLBAR_CONTEXT);
|
|
3584
|
+
/** @private Bumped after every Tiptap transaction or selection update. */
|
|
3585
|
+
_revision = inject(MLV_EDITOR_TOOLBAR_REVISION, {
|
|
3586
|
+
optional: true,
|
|
3587
|
+
});
|
|
3588
|
+
/** @private The toolbar's roving focus; Alt+F10 enters through it. */
|
|
3589
|
+
_roving = inject(MLV_EDITOR_TOOLBAR_ROVING);
|
|
3590
|
+
/** @private Makes the pane part of the editor's composite focus while shown. */
|
|
3591
|
+
_overlays = inject(MLV_EDITOR_OVERLAY_REGISTRY);
|
|
3592
|
+
/** @private Scoped direction for the pane. */
|
|
3593
|
+
_rtl = inject(MlvRtlService);
|
|
3594
|
+
/** @private Pane size changes reposition the bubble. */
|
|
3595
|
+
_resizeObserver = inject(MlvResizeObserverService);
|
|
3596
|
+
/** @private Invalidates the render effect from DOM events. */
|
|
3597
|
+
_tick = signal(0, /* @ts-ignore */
|
|
3598
|
+
...(ngDevMode ? [{ debugName: "_tick" }] : /* istanbul ignore next */ []));
|
|
3599
|
+
/** @private Identifies this instance's content-key plugin in the editor state. */
|
|
3600
|
+
_keyPluginKey = new PluginKey('mlvEditorBubbleKeys');
|
|
3601
|
+
/** @private The overlay, created after the first render in the browser. */
|
|
3602
|
+
_overlayRef = null;
|
|
3603
|
+
/** @private The overlay's position strategy, re-pointed at the selection. */
|
|
3604
|
+
_strategy = null;
|
|
3605
|
+
/** @private The position list last handed to the strategy. */
|
|
3606
|
+
_positions = null;
|
|
3607
|
+
/** @private Whether the bubble is on screen. */
|
|
3608
|
+
_shown = false;
|
|
3609
|
+
/** @private Releases the pane's composite-focus registration. */
|
|
3610
|
+
_unregister = null;
|
|
3611
|
+
/** @private Alt+F10 showed it at a bare caret; cleared when focus returns to the content. */
|
|
3612
|
+
_summoned = false;
|
|
3613
|
+
/** @private Escape dismissed it for this selection; any other selection clears it. */
|
|
3614
|
+
_dismissed = null;
|
|
3615
|
+
/** @private A primary pointer is down in the content, still selecting. */
|
|
3616
|
+
_pointerSelecting = false;
|
|
3617
|
+
/** @private A pointer is down on the bubble; covers browsers that blur on a button press. */
|
|
3618
|
+
_pressing = false;
|
|
3619
|
+
/** @private A native drag (block handle or text) is in progress. */
|
|
3620
|
+
_dragging = false;
|
|
3621
|
+
/** @private An IME composition is in progress. */
|
|
3622
|
+
_composing = false;
|
|
3623
|
+
/** @private Per-gesture listeners, replaced by the next gesture of the same kind. */
|
|
3624
|
+
_gestures = { select: null, press: null, drag: null };
|
|
3625
|
+
constructor() {
|
|
3626
|
+
if (!isPlatformBrowser(inject(PLATFORM_ID)))
|
|
3627
|
+
return;
|
|
3628
|
+
afterNextRender(() => this._create());
|
|
3629
|
+
// Registered once per editor instance and removed with this directive,
|
|
3630
|
+
// e.g. when `toolbarAppearance` returns to `'bar'`: left behind, it would
|
|
3631
|
+
// keep claiming Escape and Alt+F10 for a bubble that no longer exists.
|
|
3632
|
+
effect((onCleanup) => {
|
|
3633
|
+
const editor = this._context.editor();
|
|
3634
|
+
if (!editor || editor.isDestroyed)
|
|
3635
|
+
return;
|
|
3636
|
+
untracked(() => editor.registerPlugin(this._createKeyPlugin()));
|
|
3637
|
+
onCleanup(() => {
|
|
3638
|
+
if (!editor.isDestroyed)
|
|
3639
|
+
editor.unregisterPlugin(this._keyPluginKey);
|
|
3640
|
+
});
|
|
3641
|
+
});
|
|
3642
|
+
// Before the render effect below would hide the bubble, so focus in a
|
|
3643
|
+
// popup moves to the content while that popup still exists.
|
|
3644
|
+
effect(() => {
|
|
3645
|
+
if (!this._context.readonly())
|
|
3646
|
+
return;
|
|
3647
|
+
untracked(() => this._closeForReadonly());
|
|
3648
|
+
});
|
|
3649
|
+
afterRenderEffect(() => {
|
|
3650
|
+
this._tick();
|
|
3651
|
+
this._context.editor();
|
|
3652
|
+
this._context.disabled();
|
|
3653
|
+
this._context.readonly();
|
|
3654
|
+
this._context.focused();
|
|
3655
|
+
this._context.zoom();
|
|
3656
|
+
this._revision?.();
|
|
3657
|
+
this.capped();
|
|
3658
|
+
untracked(() => this._sync());
|
|
3659
|
+
});
|
|
3660
|
+
this._destroyRef.onDestroy(() => {
|
|
3661
|
+
for (const gesture of Object.values(this._gestures)) {
|
|
3662
|
+
gesture?.unsubscribe();
|
|
3663
|
+
}
|
|
3664
|
+
// Nothing is on screen once the overlay is gone.
|
|
3665
|
+
this._shown = false;
|
|
3666
|
+
this._unregister?.();
|
|
3667
|
+
this._unregister = null;
|
|
3668
|
+
this._overlayRef?.dispose();
|
|
3669
|
+
this._overlayRef = null;
|
|
3670
|
+
});
|
|
3671
|
+
}
|
|
3672
|
+
/** @private Creates the overlay and binds every listener, once. */
|
|
3673
|
+
_create() {
|
|
3674
|
+
const viewport = this.viewport();
|
|
3675
|
+
const direction = this._rtl.resolveDirection(viewport);
|
|
3676
|
+
const strategy = this._overlay
|
|
3677
|
+
.position()
|
|
3678
|
+
.flexibleConnectedTo({ x: 0, y: 0 })
|
|
3679
|
+
.withPositions(mlvMirrorInlineOffsets(MLV_EDITOR_BUBBLE_ABOVE_FIRST, direction))
|
|
3680
|
+
.withFlexibleDimensions(false)
|
|
3681
|
+
.withPush(true)
|
|
3682
|
+
.withViewportMargin(MLV_EDITOR_BUBBLE_VIEWPORT_MARGIN);
|
|
3683
|
+
const ref = this._overlay.create({
|
|
3684
|
+
positionStrategy: strategy,
|
|
3685
|
+
// Repositioned by `_sync()` from a document-level scroll listener, which
|
|
3686
|
+
// also sees the capped viewport's own scrolling; CDK's reposition
|
|
3687
|
+
// strategy only knows registered `cdkScrollable`s.
|
|
3688
|
+
scrollStrategy: this._overlay.scrollStrategies.noop(),
|
|
3689
|
+
direction,
|
|
3690
|
+
panelClass: [MLV_EDITOR_BUBBLE_CLASS, MLV_EDITOR_BUBBLE_HIDDEN_CLASS],
|
|
3691
|
+
hasBackdrop: false,
|
|
3692
|
+
});
|
|
3693
|
+
ref.attach(new TemplatePortal(this._templateRef, this._viewContainerRef));
|
|
3694
|
+
this._overlayRef = ref;
|
|
3695
|
+
this._strategy = strategy;
|
|
3696
|
+
this._positions = MLV_EDITOR_BUBBLE_ABOVE_FIRST;
|
|
3697
|
+
const pane = ref.overlayElement;
|
|
3698
|
+
strategy.positionChanges
|
|
3699
|
+
.pipe(takeUntilDestroyed(this._destroyRef))
|
|
3700
|
+
.subscribe((change) => pane.classList.toggle(MLV_EDITOR_BUBBLE_BELOW_CLASS, change.connectionPair.overlayY === 'top'));
|
|
3701
|
+
// Nothing re-parents an open pane, so a `[dir]` flip above the editor
|
|
3702
|
+
// re-mirrors it here.
|
|
3703
|
+
this._destroyRef.onDestroy(this._rtl.watchDirection(viewport, (next) => {
|
|
3704
|
+
ref.setDirection(next);
|
|
3705
|
+
this._positions = null;
|
|
3706
|
+
this._invalidate();
|
|
3707
|
+
}));
|
|
3708
|
+
this._resizeObserver
|
|
3709
|
+
.observe(pane)
|
|
3710
|
+
.pipe(takeUntilDestroyed(this._destroyRef))
|
|
3711
|
+
.subscribe(() => this._invalidate());
|
|
3712
|
+
this._bindViewportListeners(viewport);
|
|
3713
|
+
this._bindPaneListeners(pane);
|
|
3714
|
+
const window = this._document.defaultView;
|
|
3715
|
+
// Passive and capture: every scroll container's `scroll` reaches the
|
|
3716
|
+
// document in the capture phase, the capped viewport's included.
|
|
3717
|
+
fromEvent(this._document, 'scroll', { capture: true, passive: true })
|
|
3718
|
+
.pipe(takeUntilDestroyed(this._destroyRef))
|
|
3719
|
+
.subscribe(() => this._invalidateOnLayout());
|
|
3720
|
+
if (window) {
|
|
3721
|
+
fromEvent(window, 'resize', { passive: true })
|
|
3722
|
+
.pipe(takeUntilDestroyed(this._destroyRef))
|
|
3723
|
+
.subscribe(() => this._invalidateOnLayout());
|
|
3724
|
+
}
|
|
3725
|
+
this._sync();
|
|
3726
|
+
}
|
|
3727
|
+
/** @private Content-side gestures and keys. */
|
|
3728
|
+
_bindViewportListeners(viewport) {
|
|
3729
|
+
// Fallback behind the key plugin for ProseMirror's composition window: it
|
|
3730
|
+
// skips every key handler while it still counts as composing, and in
|
|
3731
|
+
// Safari for 500ms after `compositionend`, by when the bubble may already
|
|
3732
|
+
// be back. It cancels every key it does handle, so only a key it never
|
|
3733
|
+
// processed arrives here uncancelled. (A read-only view also skips them,
|
|
3734
|
+
// but the bubble claims no key there.)
|
|
3735
|
+
fromEvent(viewport, 'keydown')
|
|
3736
|
+
.pipe(takeUntilDestroyed(this._destroyRef))
|
|
3737
|
+
.subscribe((event) => {
|
|
3738
|
+
if (event.defaultPrevented)
|
|
3739
|
+
return;
|
|
3740
|
+
if (this._onContentKey(event))
|
|
3741
|
+
event.preventDefault();
|
|
3742
|
+
});
|
|
3743
|
+
fromEvent(viewport, 'focusin')
|
|
3744
|
+
.pipe(takeUntilDestroyed(this._destroyRef))
|
|
3745
|
+
.subscribe((event) => {
|
|
3746
|
+
if (!this._inContent(event.target))
|
|
3747
|
+
return;
|
|
3748
|
+
this._summoned = false;
|
|
3749
|
+
this._invalidate();
|
|
3750
|
+
});
|
|
3751
|
+
fromEvent(viewport, 'pointerdown')
|
|
3752
|
+
.pipe(takeUntilDestroyed(this._destroyRef))
|
|
3753
|
+
.subscribe((event) => {
|
|
3754
|
+
if (event.button !== 0 ||
|
|
3755
|
+
event.isPrimary === false ||
|
|
3756
|
+
!this._inContent(event.target)) {
|
|
3757
|
+
return;
|
|
3758
|
+
}
|
|
3759
|
+
this._pointerSelecting = true;
|
|
3760
|
+
this._summoned = false;
|
|
3761
|
+
this._invalidate();
|
|
3762
|
+
this._gesture('select', merge(fromEvent(this._document, 'pointerup', { capture: true }), fromEvent(this._document, 'pointercancel', { capture: true })).pipe(take(1)), () => {
|
|
3763
|
+
this._pointerSelecting = false;
|
|
3764
|
+
this._invalidate();
|
|
3765
|
+
});
|
|
3766
|
+
});
|
|
3767
|
+
fromEvent(viewport, 'compositionstart')
|
|
3768
|
+
.pipe(takeUntilDestroyed(this._destroyRef))
|
|
3769
|
+
.subscribe(() => {
|
|
3770
|
+
this._composing = true;
|
|
3771
|
+
this._invalidate();
|
|
3772
|
+
});
|
|
3773
|
+
fromEvent(viewport, 'compositionend')
|
|
3774
|
+
.pipe(takeUntilDestroyed(this._destroyRef))
|
|
3775
|
+
.subscribe(() => {
|
|
3776
|
+
this._composing = false;
|
|
3777
|
+
this._invalidate();
|
|
3778
|
+
});
|
|
3779
|
+
// Capture, so a drag source that stops `dragstart` still counts.
|
|
3780
|
+
fromEvent(viewport, 'dragstart', { capture: true })
|
|
3781
|
+
.pipe(takeUntilDestroyed(this._destroyRef))
|
|
3782
|
+
.subscribe(() => {
|
|
3783
|
+
this._dragging = true;
|
|
3784
|
+
this._invalidate();
|
|
3785
|
+
// `dragend` never reaches the document when the source left the DOM
|
|
3786
|
+
// mid-drag; the next press ends the wait instead.
|
|
3787
|
+
this._gesture('drag', merge(fromEvent(this._document, 'dragend', { capture: true }), fromEvent(this._document, 'drop', { capture: true }), fromEvent(this._document, 'pointerdown', { capture: true })).pipe(take(1)), () => {
|
|
3788
|
+
this._dragging = false;
|
|
3789
|
+
this._invalidate();
|
|
3790
|
+
});
|
|
3791
|
+
});
|
|
3792
|
+
}
|
|
3793
|
+
/** @private Bubble-side keys and presses. */
|
|
3794
|
+
_bindPaneListeners(pane) {
|
|
3795
|
+
fromEvent(pane, 'keydown')
|
|
3796
|
+
.pipe(takeUntilDestroyed(this._destroyRef))
|
|
3797
|
+
.subscribe((event) => {
|
|
3798
|
+
if (event.defaultPrevented)
|
|
3799
|
+
return;
|
|
3800
|
+
if (event.key === 'Escape') {
|
|
3801
|
+
event.preventDefault();
|
|
3802
|
+
// Kept from the page and any dialog around the editor: this Escape
|
|
3803
|
+
// belongs to the bubble.
|
|
3804
|
+
event.stopPropagation();
|
|
3805
|
+
this._dismiss();
|
|
3806
|
+
}
|
|
3807
|
+
else if (event.key === 'Tab') {
|
|
3808
|
+
// The pane is portaled to the end of the document; the next tab
|
|
3809
|
+
// stop after it is not the editor's.
|
|
3810
|
+
event.preventDefault();
|
|
3811
|
+
this._context.editor()?.view.focus();
|
|
3812
|
+
}
|
|
3813
|
+
});
|
|
3814
|
+
fromEvent(pane, 'pointerdown', { capture: true })
|
|
3815
|
+
.pipe(takeUntilDestroyed(this._destroyRef))
|
|
3816
|
+
.subscribe(() => {
|
|
3817
|
+
this._pressing = true;
|
|
3818
|
+
// Released a task after `pointerup`, so the `click` that follows
|
|
3819
|
+
// still finds the bubble on screen.
|
|
3820
|
+
this._gesture('press', merge(fromEvent(this._document, 'pointerup', { capture: true }), fromEvent(this._document, 'pointercancel', { capture: true })).pipe(take(1), delay(0)), () => {
|
|
3821
|
+
this._pressing = false;
|
|
3822
|
+
this._invalidate();
|
|
3823
|
+
});
|
|
3824
|
+
});
|
|
3825
|
+
// A press on the bubble's own padding keeps the caret and the selection
|
|
3826
|
+
// in the content; controls still take focus as usual.
|
|
3827
|
+
fromEvent(pane, 'mousedown')
|
|
3828
|
+
.pipe(takeUntilDestroyed(this._destroyRef))
|
|
3829
|
+
.subscribe((event) => {
|
|
3830
|
+
const target = event.target;
|
|
3831
|
+
if (target instanceof Element &&
|
|
3832
|
+
target.closest(MLV_EDITOR_BUBBLE_FOCUSABLE)) {
|
|
3833
|
+
return;
|
|
3834
|
+
}
|
|
3835
|
+
event.preventDefault();
|
|
3836
|
+
});
|
|
3837
|
+
}
|
|
3838
|
+
/**
|
|
3839
|
+
* @private The plugin carrying content-side Escape and Alt+F10. The loop is
|
|
3840
|
+
* what makes it the lowest-priority key handler, not its place in the list:
|
|
3841
|
+
* plugins before it run first anyway, and it offers each of those keys to
|
|
3842
|
+
* every plugin after it (an AI stream session registers its Escape claim
|
|
3843
|
+
* later) before acting. Once it has offered the key it acts and returns
|
|
3844
|
+
* `true` unconditionally, even if a declining later handler changed the
|
|
3845
|
+
* bubble's state meanwhile; returning `false` there would make ProseMirror
|
|
3846
|
+
* run those handlers a second time. Returning `true` makes ProseMirror
|
|
3847
|
+
* cancel the event.
|
|
3848
|
+
*/
|
|
3849
|
+
_createKeyPlugin() {
|
|
3850
|
+
const key = this._keyPluginKey;
|
|
3851
|
+
return new Plugin({
|
|
3852
|
+
key,
|
|
3853
|
+
props: {
|
|
3854
|
+
handleKeyDown: (view, event) => {
|
|
3855
|
+
if (!this._ownsContentKey(event))
|
|
3856
|
+
return false;
|
|
3857
|
+
const plugins = view.state.plugins;
|
|
3858
|
+
const own = key.get(view.state);
|
|
3859
|
+
for (let index = own ? plugins.indexOf(own) + 1 : plugins.length; index < plugins.length; index++) {
|
|
3860
|
+
const plugin = plugins[index];
|
|
3861
|
+
if (plugin.props.handleKeyDown?.call(plugin, view, event)) {
|
|
3862
|
+
return true;
|
|
3863
|
+
}
|
|
3864
|
+
}
|
|
3865
|
+
this._act(event);
|
|
3866
|
+
return true;
|
|
3867
|
+
},
|
|
3868
|
+
},
|
|
3869
|
+
});
|
|
3870
|
+
}
|
|
3871
|
+
/**
|
|
3872
|
+
* @private Whether `event` is a key the bubble acts on right now: never
|
|
3873
|
+
* while disabled or `readonly`, where it cannot show.
|
|
3874
|
+
*/
|
|
3875
|
+
_ownsContentKey(event) {
|
|
3876
|
+
if (this._context.disabled() || this._context.readonly())
|
|
3877
|
+
return false;
|
|
3878
|
+
return ((event.key === 'Escape' && this._shown) ||
|
|
3879
|
+
(event.key === 'F10' &&
|
|
3880
|
+
event.altKey &&
|
|
3881
|
+
!event.ctrlKey &&
|
|
3882
|
+
!event.metaKey &&
|
|
3883
|
+
!event.shiftKey));
|
|
3884
|
+
}
|
|
3885
|
+
/**
|
|
3886
|
+
* @private The DOM fallback's entry: acts only on a key the bubble owns.
|
|
3887
|
+
* Returns whether it acted; the caller cancels the event.
|
|
3888
|
+
*/
|
|
3889
|
+
_onContentKey(event) {
|
|
3890
|
+
if (!this._ownsContentKey(event))
|
|
3891
|
+
return false;
|
|
3892
|
+
this._act(event);
|
|
3893
|
+
return true;
|
|
3894
|
+
}
|
|
3895
|
+
/**
|
|
3896
|
+
* @private Alt+F10 summons; Escape dismisses and stops there, so a dialog or
|
|
3897
|
+
* drawer around the editor does not close with it. Unchecked: callers
|
|
3898
|
+
* establish that the bubble owns the key.
|
|
3899
|
+
*/
|
|
3900
|
+
_act(event) {
|
|
3901
|
+
if (event.key === 'Escape') {
|
|
3902
|
+
event.stopPropagation();
|
|
3903
|
+
this._dismiss();
|
|
3904
|
+
}
|
|
3905
|
+
else {
|
|
3906
|
+
this._summon();
|
|
3907
|
+
}
|
|
3908
|
+
}
|
|
3909
|
+
/** @private Shows the bubble at the caret or selection and focuses its first control. */
|
|
3910
|
+
_summon() {
|
|
3911
|
+
const ref = this._overlayRef;
|
|
3912
|
+
if (!ref || this._context.disabled() || !this._context.editor())
|
|
3913
|
+
return;
|
|
3914
|
+
this._summoned = true;
|
|
3915
|
+
this._dismissed = null;
|
|
3916
|
+
this._sync();
|
|
3917
|
+
if (!this._shown) {
|
|
3918
|
+
this._summoned = false;
|
|
3919
|
+
return;
|
|
3920
|
+
}
|
|
3921
|
+
this._roving.focusBoundary('start');
|
|
3922
|
+
if (!ref.overlayElement.contains(this._document.activeElement)) {
|
|
3923
|
+
this._summoned = false;
|
|
3924
|
+
this._sync();
|
|
3925
|
+
}
|
|
3926
|
+
}
|
|
3927
|
+
/** @private Hides the bubble for the current selection; focus returns to the content. */
|
|
3928
|
+
_dismiss() {
|
|
3929
|
+
this._dismissed = this._context.editor()?.state.selection ?? null;
|
|
3930
|
+
this._summoned = false;
|
|
3931
|
+
this._sync();
|
|
3932
|
+
}
|
|
3933
|
+
/** @private Shows, repositions or hides the bubble for the current state. */
|
|
3934
|
+
_sync() {
|
|
3935
|
+
const ref = this._overlayRef;
|
|
3936
|
+
const editor = this._context.editor();
|
|
3937
|
+
if (!ref || !editor || editor.isDestroyed || !this._wanted(editor)) {
|
|
3938
|
+
this._hide();
|
|
3939
|
+
return;
|
|
3940
|
+
}
|
|
3941
|
+
const windowRect = this._windowRect();
|
|
3942
|
+
const boundary = this.capped()
|
|
3943
|
+
? mlvEditorVisibleRect(this.viewport().getBoundingClientRect(), windowRect)
|
|
3944
|
+
: windowRect;
|
|
3945
|
+
const selection = this._selectionRect(editor);
|
|
3946
|
+
const visible = boundary ? mlvEditorVisibleRect(selection, boundary) : null;
|
|
3947
|
+
// Focus in the bubble (or a popup it opened) keeps it on screen when the
|
|
3948
|
+
// selection scrolls away, pushed to the window edge; hiding it would drop
|
|
3949
|
+
// that focus.
|
|
3950
|
+
const pinned = this._summoned || !editor.view.hasFocus();
|
|
3951
|
+
const anchor = visible ?? (pinned ? selection : null);
|
|
3952
|
+
if (!anchor) {
|
|
3953
|
+
this._hide();
|
|
3954
|
+
return;
|
|
3955
|
+
}
|
|
3956
|
+
this._show(ref, anchor, boundary ?? windowRect);
|
|
3957
|
+
}
|
|
3958
|
+
/** @private Whether every visibility condition except the anchor holds. */
|
|
3959
|
+
_wanted(editor) {
|
|
3960
|
+
if (this._context.disabled() || this._context.readonly())
|
|
3961
|
+
return false;
|
|
3962
|
+
if (!this._context.focused() && !this._pressing)
|
|
3963
|
+
return false;
|
|
3964
|
+
if (this._pointerSelecting || this._dragging || this._composing) {
|
|
3965
|
+
return false;
|
|
3966
|
+
}
|
|
3967
|
+
const selection = editor.state.selection;
|
|
3968
|
+
if (this._dismissed) {
|
|
3969
|
+
if (selection.eq(this._dismissed))
|
|
3970
|
+
return false;
|
|
3971
|
+
this._dismissed = null;
|
|
3972
|
+
}
|
|
3973
|
+
return this._summoned || !selection.empty;
|
|
3974
|
+
}
|
|
3975
|
+
/** @private Places and reveals the bubble at `anchor`. */
|
|
3976
|
+
_show(ref, anchor, boundary) {
|
|
3977
|
+
const strategy = this._strategy;
|
|
3978
|
+
if (!strategy)
|
|
3979
|
+
return;
|
|
3980
|
+
const pane = ref.overlayElement;
|
|
3981
|
+
const positions = mlvEditorBubblePlacement(anchor, boundary, pane.getBoundingClientRect().height, MLV_EDITOR_BUBBLE_GAP) === 'above'
|
|
3982
|
+
? MLV_EDITOR_BUBBLE_ABOVE_FIRST
|
|
3983
|
+
: MLV_EDITOR_BUBBLE_BELOW_FIRST;
|
|
3984
|
+
if (positions !== this._positions) {
|
|
3985
|
+
this._positions = positions;
|
|
3986
|
+
strategy.withPositions(mlvMirrorInlineOffsets(positions, ref.getDirection()));
|
|
3987
|
+
}
|
|
3988
|
+
strategy.setOrigin({
|
|
3989
|
+
x: anchor.left,
|
|
3990
|
+
y: anchor.top,
|
|
3991
|
+
width: anchor.right - anchor.left,
|
|
3992
|
+
height: anchor.bottom - anchor.top,
|
|
3993
|
+
});
|
|
3994
|
+
if (!this._shown) {
|
|
3995
|
+
this._shown = true;
|
|
3996
|
+
pane.classList.remove(MLV_EDITOR_BUBBLE_HIDDEN_CLASS);
|
|
3997
|
+
this._unregister = this._overlays.register(pane, () => {
|
|
3998
|
+
// The registry drops the entry itself after calling this.
|
|
3999
|
+
this._unregister = null;
|
|
4000
|
+
this._hide();
|
|
4001
|
+
});
|
|
4002
|
+
}
|
|
4003
|
+
ref.updatePosition();
|
|
4004
|
+
}
|
|
4005
|
+
/** @private Takes the bubble off screen, handing focus back to the content first. */
|
|
4006
|
+
_hide() {
|
|
4007
|
+
const ref = this._overlayRef;
|
|
4008
|
+
if (!ref || !this._shown)
|
|
4009
|
+
return;
|
|
4010
|
+
this._shown = false;
|
|
4011
|
+
const pane = ref.overlayElement;
|
|
4012
|
+
if (pane.contains(this._document.activeElement))
|
|
4013
|
+
this._focusContent();
|
|
4014
|
+
pane.classList.add(MLV_EDITOR_BUBBLE_HIDDEN_CLASS);
|
|
4015
|
+
this._unregister?.();
|
|
4016
|
+
this._unregister = null;
|
|
4017
|
+
}
|
|
4018
|
+
/**
|
|
4019
|
+
* @private `readonly` just turned on. While the bubble is shown, every popup
|
|
4020
|
+
* the editor owns closes (their controls no longer apply, and the ones the
|
|
4021
|
+
* bubble opened would float beside a hidden pane), and focus in the bubble
|
|
4022
|
+
* or in such a popup returns to the content before either goes away. The
|
|
4023
|
+
* docked bar goes through the same registry call (#498).
|
|
4024
|
+
*/
|
|
4025
|
+
_closeForReadonly() {
|
|
4026
|
+
const ref = this._overlayRef;
|
|
4027
|
+
const editor = this._context.editor();
|
|
4028
|
+
if (!ref || !this._shown || !editor || editor.isDestroyed)
|
|
4029
|
+
return;
|
|
4030
|
+
this._overlays.closeForReadonly(ref.overlayElement, editor.view.dom, () => this._focusContent());
|
|
4031
|
+
this._sync();
|
|
4032
|
+
}
|
|
4033
|
+
/** @private Focuses the content with its selection, unless disabled. */
|
|
4034
|
+
_focusContent() {
|
|
4035
|
+
const editor = this._context.editor();
|
|
4036
|
+
if (!editor || editor.isDestroyed || this._context.disabled())
|
|
4037
|
+
return;
|
|
4038
|
+
mlvEditorFocusContent(editor);
|
|
4039
|
+
}
|
|
4040
|
+
/** @private The selection's client rect; a caret is zero-width. */
|
|
4041
|
+
_selectionRect(editor) {
|
|
4042
|
+
const { from, to } = editor.state.selection;
|
|
4043
|
+
try {
|
|
4044
|
+
const { top, bottom, left, right } = posToDOMRect(editor.view, from, to);
|
|
4045
|
+
return { top, bottom, left, right };
|
|
4046
|
+
}
|
|
4047
|
+
catch {
|
|
4048
|
+
// `coordsAtPos` throws where nothing is laid out; fall back to the
|
|
4049
|
+
// content's top edge.
|
|
4050
|
+
const rect = editor.view.dom.getBoundingClientRect();
|
|
4051
|
+
return {
|
|
4052
|
+
top: rect.top,
|
|
4053
|
+
bottom: rect.top,
|
|
4054
|
+
left: rect.left,
|
|
4055
|
+
right: rect.right,
|
|
4056
|
+
};
|
|
4057
|
+
}
|
|
4058
|
+
}
|
|
4059
|
+
/** @private The window's client rect. */
|
|
4060
|
+
_windowRect() {
|
|
4061
|
+
const window = this._document.defaultView;
|
|
4062
|
+
return {
|
|
4063
|
+
top: 0,
|
|
4064
|
+
left: 0,
|
|
4065
|
+
bottom: window?.innerHeight ?? 0,
|
|
4066
|
+
right: window?.innerWidth ?? 0,
|
|
4067
|
+
};
|
|
4068
|
+
}
|
|
4069
|
+
/** @private Whether `target` is inside the ProseMirror content. */
|
|
4070
|
+
_inContent(target) {
|
|
4071
|
+
const dom = this._context.editor()?.view.dom;
|
|
4072
|
+
return !!dom && target instanceof Node && dom.contains(target);
|
|
4073
|
+
}
|
|
4074
|
+
/**
|
|
4075
|
+
* @private Runs `done` on the first emission of `end`, replacing any wait
|
|
4076
|
+
* of the same kind still open. Per gesture, so released here and at
|
|
4077
|
+
* destroy rather than by `takeUntilDestroyed`, which would keep every
|
|
4078
|
+
* earlier generation alive.
|
|
4079
|
+
*/
|
|
4080
|
+
_gesture(kind, end, done) {
|
|
4081
|
+
this._gestures[kind]?.unsubscribe();
|
|
4082
|
+
this._gestures[kind] = end.subscribe(() => {
|
|
4083
|
+
this._gestures[kind] = null;
|
|
4084
|
+
done();
|
|
4085
|
+
});
|
|
4086
|
+
}
|
|
4087
|
+
/** @private Schedules `_sync()` for the next render. */
|
|
4088
|
+
_invalidate() {
|
|
4089
|
+
this._tick.update((tick) => tick + 1);
|
|
4090
|
+
}
|
|
4091
|
+
/**
|
|
4092
|
+
* @private Scroll and resize only matter while the bubble is shown or could
|
|
4093
|
+
* show: summoned, or a non-empty selection that may scroll back into view.
|
|
4094
|
+
* A focused bare caret, and a disabled or `readonly` editor, skip the render
|
|
4095
|
+
* these events would schedule.
|
|
4096
|
+
*/
|
|
4097
|
+
_invalidateOnLayout() {
|
|
4098
|
+
if (this._shown) {
|
|
4099
|
+
this._invalidate();
|
|
4100
|
+
return;
|
|
4101
|
+
}
|
|
4102
|
+
if (!untracked(this._context.focused) ||
|
|
4103
|
+
untracked(this._context.disabled) ||
|
|
4104
|
+
untracked(this._context.readonly)) {
|
|
4105
|
+
return;
|
|
4106
|
+
}
|
|
4107
|
+
const editor = untracked(this._context.editor);
|
|
4108
|
+
if (this._summoned ||
|
|
4109
|
+
(editor && !editor.isDestroyed && !editor.state.selection.empty)) {
|
|
4110
|
+
this._invalidate();
|
|
4111
|
+
}
|
|
4112
|
+
}
|
|
4113
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorBubble, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
4114
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.7", type: MlvEditorBubble, isStandalone: true, selector: "ng-template[mlvEditorBubble]", inputs: { viewport: { classPropertyName: "viewport", publicName: "viewport", isSignal: true, isRequired: true, transformFunction: null }, capped: { classPropertyName: "capped", publicName: "capped", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
|
|
4115
|
+
}
|
|
4116
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorBubble, decorators: [{
|
|
4117
|
+
type: Directive,
|
|
4118
|
+
args: [{
|
|
4119
|
+
selector: 'ng-template[mlvEditorBubble]',
|
|
4120
|
+
}]
|
|
4121
|
+
}], ctorParameters: () => [], propDecorators: { viewport: [{ type: i0.Input, args: [{ isSignal: true, alias: "viewport", required: true }] }], capped: [{ type: i0.Input, args: [{ isSignal: true, alias: "capped", required: false }] }] } });
|
|
4122
|
+
|
|
3241
4123
|
/** @internal Class of the floating drop indicator. */
|
|
3242
4124
|
const MLV_EDITOR_DROP_INDICATOR_CLASS = 'mlv-editor__drop-indicator';
|
|
3243
4125
|
/** @internal Class of the off-screen wrapper handed to `setDragImage`. */
|
|
3244
4126
|
const MLV_EDITOR_DRAG_GHOST_CLASS = 'mlv-editor__drag-ghost';
|
|
3245
4127
|
/**
|
|
3246
|
-
* @internal Marks the
|
|
3247
|
-
*
|
|
3248
|
-
*
|
|
4128
|
+
* @internal Marks the block currently on the cursor, dimmed in place. Applied
|
|
4129
|
+
* as a node decoration, never written onto the element — see
|
|
4130
|
+
* `MLV_EDITOR_DRAG_SOURCE_KEY`.
|
|
3249
4131
|
*/
|
|
3250
|
-
const MLV_EDITOR_DRAGGING_CLASS = 'ProseMirror--block-dragging';
|
|
3251
|
-
/** @internal Marks the block currently on the cursor, dimmed in place. */
|
|
3252
4132
|
const MLV_EDITOR_DRAG_SOURCE_CLASS = 'mlv-editor__block--dragging';
|
|
4133
|
+
/**
|
|
4134
|
+
* @internal Plugin state holding the dimmed source's node decoration: empty
|
|
4135
|
+
* while no block drag is in flight, one `Decoration.node` while one is.
|
|
4136
|
+
*
|
|
4137
|
+
* The dim has to travel through ProseMirror's own render path because nothing
|
|
4138
|
+
* written directly onto a block's element survives a drag. ProseMirror's
|
|
4139
|
+
* `DOMObserver` treats an attribute mutation on any node it rendered — a class,
|
|
4140
|
+
* an inline style — as an external DOM change, marks that node dirty and
|
|
4141
|
+
* redraws it, **replacing** the element. Measured in Chromium, 22 of 25
|
|
4142
|
+
* top-level elements were replaced by the first `dragover`: the dim class and
|
|
4143
|
+
* the old partition's transforms all ended up on detached nodes, so none of it
|
|
4144
|
+
* was ever visible. A decoration is part of the view description instead, so a
|
|
4145
|
+
* redraw re-applies it rather than discarding it.
|
|
4146
|
+
*/
|
|
4147
|
+
const MLV_EDITOR_DRAG_SOURCE_KEY = new PluginKey('mlvEditorBlockDragSource');
|
|
4148
|
+
/**
|
|
4149
|
+
* @internal Clears a source dim a freshly built plugin view inherited rather
|
|
4150
|
+
* than published.
|
|
4151
|
+
*
|
|
4152
|
+
* Plugin state outlives the plugin view. `EditorState.reconfigure` — what
|
|
4153
|
+
* Tiptap's `registerPlugin` / `unregisterPlugin` call, e.g. when an AI stream
|
|
4154
|
+
* or suggestion session settles — keeps every state field but destroys and
|
|
4155
|
+
* rebuilds every plugin view. A reconfigure mid-drag therefore takes the view
|
|
4156
|
+
* that owned the drag, and its `dragend` listener, while the dim stays in
|
|
4157
|
+
* state; the replacing view never saw the drag, so without this the block
|
|
4158
|
+
* would stay dimmed until the next document change.
|
|
4159
|
+
*
|
|
4160
|
+
* Deferred to a microtask because this runs from the view factory, inside
|
|
4161
|
+
* ProseMirror's `updatePluginViews`, where a dispatch would re-enter
|
|
4162
|
+
* `updateState` while the views are still being built. The old view's
|
|
4163
|
+
* `destroy` cannot clear it either: it runs inside that same update, or inside
|
|
4164
|
+
* `EditorView.destroy()` before `isDestroyed` turns true. Every other teardown
|
|
4165
|
+
* discards the state (a destroyed editor) or the field (the plugin removed)
|
|
4166
|
+
* along with the dim. The clear only drops the exact set inherited, so it can
|
|
4167
|
+
* never undo a newer drag's dim.
|
|
4168
|
+
*/
|
|
4169
|
+
function clearInheritedDragSource(view) {
|
|
4170
|
+
const inherited = MLV_EDITOR_DRAG_SOURCE_KEY.getState(view.state);
|
|
4171
|
+
if (inherited === undefined || inherited === DecorationSet.empty)
|
|
4172
|
+
return;
|
|
4173
|
+
queueMicrotask(() => {
|
|
4174
|
+
if (view.isDestroyed)
|
|
4175
|
+
return;
|
|
4176
|
+
if (MLV_EDITOR_DRAG_SOURCE_KEY.getState(view.state) !== inherited)
|
|
4177
|
+
return;
|
|
4178
|
+
view.dispatch(view.state.tr.setMeta(MLV_EDITOR_DRAG_SOURCE_KEY, DecorationSet.empty));
|
|
4179
|
+
});
|
|
4180
|
+
}
|
|
3253
4181
|
/** @internal Duration of the post-drop settle, matching `--mlv-duration-normal`. */
|
|
3254
4182
|
const MLV_EDITOR_SETTLE_DURATION = 200;
|
|
3255
4183
|
/** @internal Sub-pixel movement not worth animating after a drop. */
|
|
@@ -3274,13 +4202,12 @@ function insertAnchor(doc, from, to) {
|
|
|
3274
4202
|
? childStart(doc, to) + doc.child(to).nodeSize
|
|
3275
4203
|
: childStart(doc, to);
|
|
3276
4204
|
}
|
|
3277
|
-
/** @internal
|
|
3278
|
-
function
|
|
4205
|
+
/** @internal Measures the mount layer's frame from a single rect read. */
|
|
4206
|
+
function mountFrame(mount) {
|
|
4207
|
+
const rect = mount.getBoundingClientRect();
|
|
3279
4208
|
const width = mount.offsetWidth;
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
const scaled = mount.getBoundingClientRect().width;
|
|
3283
|
-
return scaled > 0 ? scaled / width : 1;
|
|
4209
|
+
const scale = width && rect.width > 0 ? rect.width / width : 1;
|
|
4210
|
+
return { top: rect.top, scale };
|
|
3284
4211
|
}
|
|
3285
4212
|
/**
|
|
3286
4213
|
* @internal Top-level child index for one rendered child of the editor DOM, or
|
|
@@ -3315,6 +4242,51 @@ function topLevelIndexOfDom(view, element) {
|
|
|
3315
4242
|
return null;
|
|
3316
4243
|
return element.contains(view.nodeDOM(childStart(doc, index))) ? index : null;
|
|
3317
4244
|
}
|
|
4245
|
+
/**
|
|
4246
|
+
* @internal The hit one rendered child stands for, or null when that child is
|
|
4247
|
+
* not a top-level block.
|
|
4248
|
+
*
|
|
4249
|
+
* A widget decoration owns no top-level node, and its box is a sliver rather
|
|
4250
|
+
* than a block's. Letting one stand in for a block would pair a neighbouring
|
|
4251
|
+
* block's index with the widget's geometry, so `dropTargetIndex` would take
|
|
4252
|
+
* that block's midpoint from the wrong box and land the drop on the wrong side
|
|
4253
|
+
* of it — silently, in one undo step. `topLevelIndexOfDom` answering null is
|
|
4254
|
+
* what makes that a *category* rejection rather than a class-name test, so a
|
|
4255
|
+
* block-level decoration added later is covered without touching this file.
|
|
4256
|
+
*
|
|
4257
|
+
* Both edges come off one rect read, so nothing can pair a `top` from one
|
|
4258
|
+
* layout with a `bottom` from another.
|
|
4259
|
+
*/
|
|
4260
|
+
function blockHit(view, element) {
|
|
4261
|
+
const index = topLevelIndexOfDom(view, element);
|
|
4262
|
+
if (index === null)
|
|
4263
|
+
return null;
|
|
4264
|
+
const rect = element.getBoundingClientRect();
|
|
4265
|
+
return { index, top: rect.top, bottom: rect.bottom };
|
|
4266
|
+
}
|
|
4267
|
+
/**
|
|
4268
|
+
* @internal One rendered child at or before `from` — but never before `floor` —
|
|
4269
|
+
* that is a top-level block, paired with its own child index. Null when that
|
|
4270
|
+
* span holds nothing but widgets.
|
|
4271
|
+
*
|
|
4272
|
+
* This is the only way the search below reaches a rect, and that is the point:
|
|
4273
|
+
* **no geometry is ever read off a child that owns no top-level node.** A
|
|
4274
|
+
* widget's box is a sliver where a block's is a block, and one taken out of
|
|
4275
|
+
* flow reports a box that says nothing about where it sits — `display: none`
|
|
4276
|
+
* gives an all-zero rect, and `prosemirror-gapcursor`'s shipped stylesheet
|
|
4277
|
+
* makes exactly that: `position: absolute; display: none`, lifted to `block`
|
|
4278
|
+
* only under `.ProseMirror-focused`, so a gap-cursor selection surviving a blur
|
|
4279
|
+
* parks an all-zero-rect child at top level. Comparing a pointer against that
|
|
4280
|
+
* box would drag a search past real blocks.
|
|
4281
|
+
*/
|
|
4282
|
+
function blockAtOrBefore(view, children, from, floor) {
|
|
4283
|
+
for (let i = from; i >= floor; i -= 1) {
|
|
4284
|
+
const hit = blockHit(view, children[i]);
|
|
4285
|
+
if (hit)
|
|
4286
|
+
return { childIndex: i, hit };
|
|
4287
|
+
}
|
|
4288
|
+
return null;
|
|
4289
|
+
}
|
|
3318
4290
|
/**
|
|
3319
4291
|
* @internal Top-level block the given viewport y falls on, or the nearest one
|
|
3320
4292
|
* above it. Null for an empty document, or for a point the view cannot map.
|
|
@@ -3327,49 +4299,85 @@ function topLevelIndexOfDom(view, element) {
|
|
|
3327
4299
|
* test and returns null for every point outside it, in a real browser as much
|
|
3328
4300
|
* as under a test DOM.
|
|
3329
4301
|
*
|
|
3330
|
-
*
|
|
3331
|
-
*
|
|
3332
|
-
* one
|
|
4302
|
+
* **Assumes the top-level blocks are vertically ordered and non-overlapping** —
|
|
4303
|
+
* `top` non-decreasing down the blocks, and no block's box reaching into the
|
|
4304
|
+
* next one's. Normal flow guarantees it for blocks, which are the only children
|
|
4305
|
+
* this reads a box from. Violating it (a float, `position: absolute`, a
|
|
4306
|
+
* negative margin on a *block*) resolves the handle to a neighbouring block
|
|
4307
|
+
* rather than throwing. `slotAt` assumes the same, and so did the scan this
|
|
4308
|
+
* replaced, which stopped at the first child starting below the pointer.
|
|
4309
|
+
*
|
|
4310
|
+
* Under that ordering the answer is *the last block starting at or above the
|
|
4311
|
+
* pointer*, clamped to the first block when every block starts below it. The
|
|
4312
|
+
* search probes child indices, but every comparison is against a **block**: a
|
|
4313
|
+
* probe landing on a widget resolves back to the nearest block at or before it,
|
|
4314
|
+
* and that block's own child index — not the widget's — bounds the next step.
|
|
4315
|
+
* Widget geometry therefore cannot move the search, which is the same category
|
|
4316
|
+
* rejection `blockHit` performs, extended to the probe itself. Pairing a
|
|
4317
|
+
* block's index with a widget's box is what that prevents: `dropTargetIndex`
|
|
4318
|
+
* would take the deciding midpoint from the wrong box and land the drop on the
|
|
4319
|
+
* wrong side of its neighbour — silently, in one undo step.
|
|
3333
4320
|
*
|
|
3334
|
-
*
|
|
3335
|
-
*
|
|
3336
|
-
*
|
|
3337
|
-
*
|
|
3338
|
-
*
|
|
3339
|
-
*
|
|
3340
|
-
*
|
|
3341
|
-
*
|
|
3342
|
-
* *hovered* child, so it is deep hovering in a long document that degrades.
|
|
3343
|
-
* Restoring the linear scan without losing the category skip means deferring
|
|
3344
|
-
* the mapping until a winner is picked and walking back over the few
|
|
3345
|
-
* consecutive widgets — the clamp above the first block is what makes that
|
|
3346
|
-
* more than a one-liner, so it is deliberately left as follow-up.
|
|
4321
|
+
* Cost, against the mapping-per-child scan this replaced. `posAtDOM`, `nodeDOM`
|
|
4322
|
+
* and `childStart` each walk linearly to the child they address, so mapping the
|
|
4323
|
+
* child at index `k` is Θ(k) and scanning down to it was Θ(k²) — 500 mappings
|
|
4324
|
+
* and ~125k node walks for a hover at the bottom of a 500-block document, per
|
|
4325
|
+
* `mousemove`. Here it is Θ(log n) mappings, one per probe plus the widgets a
|
|
4326
|
+
* probe walks over, and ~4.5k node walks at the same size. Deferring to a
|
|
4327
|
+
* single mapping overall is only possible by trusting widget geometry, which
|
|
4328
|
+
* is the trade this deliberately refuses.
|
|
3347
4329
|
*/
|
|
3348
4330
|
function blockAtPoint(view, clientY) {
|
|
3349
4331
|
const children = view.dom.children;
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
const
|
|
3359
|
-
if (
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
candidate
|
|
3366
|
-
|
|
4332
|
+
// `high` is -1 for a view that renders no children at all, so both loops
|
|
4333
|
+
// below are skipped and the answer is null without a bounds check.
|
|
4334
|
+
let low = 0;
|
|
4335
|
+
let high = children.length - 1;
|
|
4336
|
+
let best = null;
|
|
4337
|
+
let bestChild = -1;
|
|
4338
|
+
while (low <= high) {
|
|
4339
|
+
const middle = (low + high) >> 1;
|
|
4340
|
+
const found = blockAtOrBefore(view, children, middle, low);
|
|
4341
|
+
if (found === null) {
|
|
4342
|
+
// Nothing but widgets in [low, middle]: every block still in range
|
|
4343
|
+
// starts after it. Discarding the span is what bounds the walk-backs.
|
|
4344
|
+
low = middle + 1;
|
|
4345
|
+
}
|
|
4346
|
+
else if (found.hit.top <= clientY) {
|
|
4347
|
+
// A candidate, and every block before it is one too; only a later block
|
|
4348
|
+
// can improve on it.
|
|
4349
|
+
best = found.hit;
|
|
4350
|
+
bestChild = found.childIndex;
|
|
4351
|
+
low = found.childIndex + 1;
|
|
3367
4352
|
}
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
4353
|
+
else {
|
|
4354
|
+
high = found.childIndex - 1;
|
|
4355
|
+
}
|
|
4356
|
+
}
|
|
4357
|
+
if (best === null) {
|
|
4358
|
+
// Every block starts below the pointer, which is the clamp: offer the
|
|
4359
|
+
// first block rather than blink the handle out. Null only for a view
|
|
4360
|
+
// rendering no top-level block at all.
|
|
4361
|
+
for (let i = 0; i < children.length; i += 1) {
|
|
4362
|
+
const hit = blockHit(view, children[i]);
|
|
4363
|
+
if (hit)
|
|
4364
|
+
return hit;
|
|
4365
|
+
}
|
|
4366
|
+
return null;
|
|
3371
4367
|
}
|
|
3372
|
-
|
|
4368
|
+
// A pointer exactly on a block's leading edge belongs to the block above it
|
|
4369
|
+
// when the two boxes touch, which is where the scan this replaced ended up:
|
|
4370
|
+
// it broke on the first box whose `bottom` reached the pointer. The shipped
|
|
4371
|
+
// stylesheet keeps `0.75rem` between paragraph border boxes so nothing can
|
|
4372
|
+
// land here, but the answer is a silent one to change. The predecessor is
|
|
4373
|
+
// only resolved for a pointer exactly on `best.top`, so a well-formed
|
|
4374
|
+
// document pays for it on one pixel per block and never elsewhere.
|
|
4375
|
+
if (clientY === best.top && bestChild > 0) {
|
|
4376
|
+
const previous = blockAtOrBefore(view, children, bestChild - 1, 0);
|
|
4377
|
+
if (previous !== null && previous.hit.bottom >= clientY)
|
|
4378
|
+
return previous.hit;
|
|
4379
|
+
}
|
|
4380
|
+
return best;
|
|
3373
4381
|
}
|
|
3374
4382
|
/**
|
|
3375
4383
|
* @internal Pre-move `to` index for dropping the block at `from` onto `hit`,
|
|
@@ -3390,30 +4398,32 @@ function dropTargetIndex(hit, clientY, from) {
|
|
|
3390
4398
|
/**
|
|
3391
4399
|
* @internal Measures every top-level block once, in the mount's unscaled space.
|
|
3392
4400
|
*
|
|
3393
|
-
* Taken at `dragstart` and read by everything downstream
|
|
3394
|
-
*
|
|
3395
|
-
*
|
|
3396
|
-
*
|
|
3397
|
-
*
|
|
3398
|
-
* the
|
|
3399
|
-
*
|
|
4401
|
+
* Taken at `dragstart` and read by everything downstream — target resolution,
|
|
4402
|
+
* the indicator's `top` and the settle's "before" — so no pointer event during
|
|
4403
|
+
* the drag reads a block's rect. Mount-relative rather than viewport-relative
|
|
4404
|
+
* so drag-autoscroll cannot invalidate it: only the mount's own rect is re-read
|
|
4405
|
+
* per event, one layout read instead of one per block. Unscaled for the same
|
|
4406
|
+
* reason the handle's `top` is: a CSS length on a child of the zoom layer
|
|
4407
|
+
* applies in that layer's space, while `getBoundingClientRect()` reports scaled
|
|
4408
|
+
* screen pixels.
|
|
3400
4409
|
*
|
|
3401
|
-
*
|
|
3402
|
-
*
|
|
3403
|
-
*
|
|
3404
|
-
*
|
|
3405
|
-
*
|
|
4410
|
+
* Boxes only, deliberately **no element**. ProseMirror replaces top-level
|
|
4411
|
+
* elements during a drag (see `MLV_EDITOR_DRAG_SOURCE_KEY`), so an element
|
|
4412
|
+
* captured here is detached by the first `dragover`, and anything written to it
|
|
4413
|
+
* afterwards is written to nothing. Everything after `dragstart` that needs a
|
|
4414
|
+
* live element resolves it from a document position instead.
|
|
3406
4415
|
*/
|
|
3407
4416
|
function snapshotBlocks(view, mount) {
|
|
3408
|
-
const scale =
|
|
3409
|
-
const mountTop = mount.getBoundingClientRect().top;
|
|
4417
|
+
const { top: mountTop, scale } = mountFrame(mount);
|
|
3410
4418
|
const slots = [];
|
|
3411
4419
|
const children = view.dom.children;
|
|
3412
4420
|
for (let i = 0; i < children.length; i += 1) {
|
|
3413
4421
|
const element = children[i];
|
|
3414
|
-
// Same category check the hover
|
|
3415
|
-
// top-level node and must not occupy a slot, or
|
|
3416
|
-
//
|
|
4422
|
+
// Same category check the hover path uses: a widget decoration owns no
|
|
4423
|
+
// top-level node and must not occupy a slot, or its sliver of a box would
|
|
4424
|
+
// decide a neighbour's drop side. Mapping every child once here is what the
|
|
4425
|
+
// drag pays instead of the hover path's deferred single mapping — the
|
|
4426
|
+
// settle needs a box per block regardless.
|
|
3417
4427
|
const index = topLevelIndexOfDom(view, element);
|
|
3418
4428
|
if (index === null)
|
|
3419
4429
|
continue;
|
|
@@ -3422,7 +4432,6 @@ function snapshotBlocks(view, mount) {
|
|
|
3422
4432
|
index,
|
|
3423
4433
|
top: (rect.top - mountTop) / scale,
|
|
3424
4434
|
bottom: (rect.bottom - mountTop) / scale,
|
|
3425
|
-
element: element,
|
|
3426
4435
|
});
|
|
3427
4436
|
}
|
|
3428
4437
|
return slots;
|
|
@@ -3432,9 +4441,10 @@ function snapshotBlocks(view, mount) {
|
|
|
3432
4441
|
*
|
|
3433
4442
|
* Reproduces `blockAtPoint`'s clamping exactly — above the first block resolves
|
|
3434
4443
|
* to the first, a point in the gap between two blocks resolves to the
|
|
3435
|
-
* preceding one, below the last resolves to the last
|
|
3436
|
-
*
|
|
3437
|
-
*
|
|
4444
|
+
* preceding one, below the last resolves to the last. Both are the same
|
|
4445
|
+
* "last block starting at or above the pointer" search on the same ordering
|
|
4446
|
+
* assumption; this one runs against an array the drag already mapped, so it
|
|
4447
|
+
* needs no widget walk-back and reads no rect at all.
|
|
3438
4448
|
*/
|
|
3439
4449
|
function slotAt(slots, y) {
|
|
3440
4450
|
if (slots.length === 0)
|
|
@@ -3444,6 +4454,9 @@ function slotAt(slots, y) {
|
|
|
3444
4454
|
let low = 0;
|
|
3445
4455
|
let high = slots.length - 1;
|
|
3446
4456
|
while (low < high) {
|
|
4457
|
+
// `ceil`, not `floor`: this converges by raising `low` *to* the midpoint,
|
|
4458
|
+
// so a `floor` midpoint of `low` for an adjacent pair would set `low` to
|
|
4459
|
+
// itself and spin forever.
|
|
3447
4460
|
const middle = Math.ceil((low + high) / 2);
|
|
3448
4461
|
if (slots[middle].top <= y)
|
|
3449
4462
|
low = middle;
|
|
@@ -3456,9 +4469,12 @@ function slotAt(slots, y) {
|
|
|
3456
4469
|
* @internal Mount-space y of the boundary the given gap sits on, where `gap`
|
|
3457
4470
|
* counts the `slots.length + 1` positions a block can be inserted at.
|
|
3458
4471
|
*
|
|
3459
|
-
*
|
|
3460
|
-
*
|
|
3461
|
-
*
|
|
4472
|
+
* An interior gap is the middle of the **natural** space between two blocks —
|
|
4473
|
+
* the midpoint of the previous block's bottom and the next block's top — since
|
|
4474
|
+
* nothing parts the blocks to preview a drop. The outer two sit on the first
|
|
4475
|
+
* block's top and the last block's bottom. The stylesheet centres the line on
|
|
4476
|
+
* this y (`translateY(-50%)`), so its own thickness never has to be resolved
|
|
4477
|
+
* here.
|
|
3462
4478
|
*/
|
|
3463
4479
|
function gapOffset(slots, gap) {
|
|
3464
4480
|
if (gap <= 0)
|
|
@@ -3476,9 +4492,9 @@ function gapOffset(slots, gap) {
|
|
|
3476
4492
|
* an ancestor class chain that would have to track whichever selectors happen
|
|
3477
4493
|
* to exist. Adapted from `@tiptap/extension-drag-handle`'s `cloneElement`.
|
|
3478
4494
|
*
|
|
3479
|
-
* Note what this cannot carry: computed styles are
|
|
3480
|
-
*
|
|
3481
|
-
*
|
|
4495
|
+
* Note what this cannot carry: computed styles are the element's own values
|
|
4496
|
+
* and never carry an ancestor's `zoom`. The editor's zoom is applied to the
|
|
4497
|
+
* wrapper instead — see `createGhostElement`.
|
|
3482
4498
|
*/
|
|
3483
4499
|
function cloneWithComputedStyles(source) {
|
|
3484
4500
|
const clone = source.cloneNode(true);
|
|
@@ -3513,7 +4529,8 @@ function cloneWithComputedStyles(source) {
|
|
|
3513
4529
|
* requires — without ever being seen. `zoom` rather than `transform` carries
|
|
3514
4530
|
* the editor's scale because it affects layout, so the wrapper's own box
|
|
3515
4531
|
* reflects it before the browser rasterizes; a `transform` is honoured
|
|
3516
|
-
* inconsistently across engines for drag images
|
|
4532
|
+
* inconsistently across engines for drag images, and the source's computed
|
|
4533
|
+
* styles carry no ancestor `zoom`.
|
|
3517
4534
|
*/
|
|
3518
4535
|
function createGhostElement(source, scale) {
|
|
3519
4536
|
const wrapper = document.createElement('div');
|
|
@@ -3659,7 +4676,26 @@ const MlvEditorBlockHandle = Extension.create({
|
|
|
3659
4676
|
const editor = this.editor;
|
|
3660
4677
|
return [
|
|
3661
4678
|
new Plugin({
|
|
4679
|
+
key: MLV_EDITOR_DRAG_SOURCE_KEY,
|
|
4680
|
+
state: {
|
|
4681
|
+
init: () => DecorationSet.empty,
|
|
4682
|
+
// Set and cleared only through a meta, from the plugin view (and
|
|
4683
|
+
// `clearInheritedDragSource`). A document change also ends the
|
|
4684
|
+
// drag (see `update` below), so the decoration is dropped in that
|
|
4685
|
+
// same transaction rather than mapped onto a block that may no
|
|
4686
|
+
// longer be the one on the pointer. That transaction therefore
|
|
4687
|
+
// never renders a set built against the previous document, and
|
|
4688
|
+
// `update`'s `endDrag` finds nothing left to dispatch.
|
|
4689
|
+
apply: (tr, decorations) => tr.getMeta(MLV_EDITOR_DRAG_SOURCE_KEY) ??
|
|
4690
|
+
(tr.docChanged ? DecorationSet.empty : decorations),
|
|
4691
|
+
},
|
|
4692
|
+
props: {
|
|
4693
|
+
decorations: (state) => MLV_EDITOR_DRAG_SOURCE_KEY.getState(state),
|
|
4694
|
+
},
|
|
3662
4695
|
view: (view) => {
|
|
4696
|
+
// First, before the mount check: a rebuilt view with no mount
|
|
4697
|
+
// still inherits whatever dim its predecessor left in state.
|
|
4698
|
+
clearInheritedDragSource(view);
|
|
3663
4699
|
const mount = options.mount();
|
|
3664
4700
|
if (!mount)
|
|
3665
4701
|
return { destroy: () => undefined };
|
|
@@ -3674,80 +4710,131 @@ const MlvEditorBlockHandle = Extension.create({
|
|
|
3674
4710
|
// mount is a sibling of `view.dom`, so no serializer can reach it.
|
|
3675
4711
|
const indicator = createIndicatorElement();
|
|
3676
4712
|
mount.appendChild(indicator);
|
|
3677
|
-
|
|
4713
|
+
/**
|
|
4714
|
+
* What the handle currently publishes, or null while it is
|
|
4715
|
+
* retracted.
|
|
4716
|
+
*
|
|
4717
|
+
* Comparing against this is what keeps a pointer move that resolves
|
|
4718
|
+
* an unchanged hit from touching the DOM at all, and the write it
|
|
4719
|
+
* saves is the smaller half of the point: an attribute write on the
|
|
4720
|
+
* handle dirties the layout tree, so the *next* move's rect reads
|
|
4721
|
+
* have to force a fresh layout before they can answer. Skipping the
|
|
4722
|
+
* writes leaves layout clean, and a hover that stays on one block
|
|
4723
|
+
* then forces no layout at all.
|
|
4724
|
+
*
|
|
4725
|
+
* Keyed on the values written rather than on `hit.index`, because
|
|
4726
|
+
* two of the three can change while the index does not: scrolling
|
|
4727
|
+
* moves the block under a stationary pointer, and `label()` is a
|
|
4728
|
+
* signal read that answers differently on a locale change.
|
|
4729
|
+
*/
|
|
4730
|
+
let published = null;
|
|
4731
|
+
const hide = () => {
|
|
4732
|
+
if (published === null)
|
|
4733
|
+
return;
|
|
4734
|
+
published = null;
|
|
4735
|
+
handle.setAttribute('data-visible', 'false');
|
|
4736
|
+
};
|
|
3678
4737
|
const onMouseMove = (event) => {
|
|
3679
4738
|
if (!options.enabled())
|
|
3680
4739
|
return hide();
|
|
3681
4740
|
const hit = blockAtPoint(view, event.clientY);
|
|
3682
4741
|
if (!hit)
|
|
3683
4742
|
return hide();
|
|
3684
|
-
// `getBoundingClientRect()` reports
|
|
3685
|
-
// CSS `top` on a child of the
|
|
3686
|
-
// layer's own
|
|
3687
|
-
//
|
|
3688
|
-
|
|
3689
|
-
const
|
|
3690
|
-
const
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
4743
|
+
// `getBoundingClientRect()` reports zoomed screen pixels, but a
|
|
4744
|
+
// CSS `top` on a child of the zoomed layer is applied in the
|
|
4745
|
+
// layer's own unzoomed space. Convert once, off one rect read of
|
|
4746
|
+
// the mount.
|
|
4747
|
+
const { top: mountTop, scale } = mountFrame(mount);
|
|
4748
|
+
const top = `${(hit.top - mountTop) / scale}px`;
|
|
4749
|
+
const index = String(hit.index);
|
|
4750
|
+
const label = options.label();
|
|
4751
|
+
if (published !== null &&
|
|
4752
|
+
published.top === top &&
|
|
4753
|
+
published.index === index &&
|
|
4754
|
+
published.label === label) {
|
|
4755
|
+
return;
|
|
4756
|
+
}
|
|
4757
|
+
published = { top, index, label };
|
|
4758
|
+
handle.style.top = top;
|
|
4759
|
+
handle.dataset['index'] = index;
|
|
4760
|
+
handle.title = label;
|
|
3694
4761
|
handle.setAttribute('data-visible', 'true');
|
|
3695
4762
|
};
|
|
3696
4763
|
/** Top-level index being dragged, or null when no drag is active. */
|
|
3697
4764
|
let source = null;
|
|
3698
|
-
/** The dimmed element the drag started from. */
|
|
3699
|
-
let sourceElement = null;
|
|
3700
4765
|
/** Geometry measured once at `dragstart`; empty while no drag runs. */
|
|
3701
4766
|
let slots = [];
|
|
3702
4767
|
/** The off-screen element handed to `setDragImage`. */
|
|
3703
4768
|
let ghost = null;
|
|
3704
|
-
/** Gap the
|
|
4769
|
+
/** Gap the indicator currently marks, or null while it is retracted. */
|
|
3705
4770
|
let gap = null;
|
|
3706
|
-
/**
|
|
3707
|
-
|
|
4771
|
+
/**
|
|
4772
|
+
* Converts a viewport y into the mount's own unscaled space, off
|
|
4773
|
+
* one rect read — this runs on every `dragover`.
|
|
4774
|
+
*/
|
|
4775
|
+
const toMountSpace = (clientY) => {
|
|
4776
|
+
const { top, scale } = mountFrame(mount);
|
|
4777
|
+
return (clientY - top) / scale;
|
|
4778
|
+
};
|
|
3708
4779
|
/**
|
|
3709
4780
|
* Gap a resolved target index sits on. `dropTargetIndex` folds the
|
|
3710
4781
|
* `slots.length + 1` gaps onto `moveBlock`'s pre-move convention,
|
|
3711
4782
|
* where `to` names the block the moved one ends up after when
|
|
3712
|
-
* moving down; this is that fold inverted, so the previewed
|
|
4783
|
+
* moving down; this is that fold inverted, so the previewed line
|
|
3713
4784
|
* and the committed move come from one resolved index.
|
|
3714
4785
|
*/
|
|
3715
4786
|
const gapForTarget = (to, from) => to <= from ? to : to + 1;
|
|
3716
|
-
/**
|
|
4787
|
+
/**
|
|
4788
|
+
* Moves the indicator to `next`, or retracts it when null. Only
|
|
4789
|
+
* the mount-owned line is written: nothing parts the blocks, so no
|
|
4790
|
+
* element ProseMirror rendered is touched during a drag.
|
|
4791
|
+
*/
|
|
3717
4792
|
const setGap = (next) => {
|
|
3718
4793
|
if (gap === next)
|
|
3719
4794
|
return;
|
|
3720
4795
|
gap = next;
|
|
3721
|
-
for (const slot of slots) {
|
|
3722
|
-
// The gap is a token rather than a number here, so the size
|
|
3723
|
-
// stays owned by the stylesheet.
|
|
3724
|
-
slot.element.style.transform =
|
|
3725
|
-
next !== null && slot.index >= next
|
|
3726
|
-
? 'translateY(var(--mlv-editor-drop-gap))'
|
|
3727
|
-
: '';
|
|
3728
|
-
}
|
|
3729
4796
|
if (next === null) {
|
|
3730
4797
|
indicator.setAttribute('data-visible', 'false');
|
|
3731
4798
|
return;
|
|
3732
4799
|
}
|
|
3733
|
-
// The *pre-partition* boundary: the stylesheet offsets the line
|
|
3734
|
-
// into the opened space by half the gap, so this never has to
|
|
3735
|
-
// resolve the token into pixels.
|
|
3736
4800
|
indicator.style.top = `${gapOffset(slots, next)}px`;
|
|
3737
4801
|
indicator.setAttribute('data-visible', 'true');
|
|
3738
4802
|
};
|
|
4803
|
+
/**
|
|
4804
|
+
* Publishes the source's dim, or clears it with `null`, through
|
|
4805
|
+
* plugin state. Skipped when nothing would change, so the several
|
|
4806
|
+
* paths that end one drag (`drop`, then `dragend`) dispatch once.
|
|
4807
|
+
*/
|
|
4808
|
+
const setSourceDecoration = (from) => {
|
|
4809
|
+
const current = MLV_EDITOR_DRAG_SOURCE_KEY.getState(view.state);
|
|
4810
|
+
if (from === null && current === DecorationSet.empty)
|
|
4811
|
+
return;
|
|
4812
|
+
const node = from === null ? null : view.state.doc.nodeAt(from);
|
|
4813
|
+
const next = from === null || node === null
|
|
4814
|
+
? DecorationSet.empty
|
|
4815
|
+
: DecorationSet.create(view.state.doc, [
|
|
4816
|
+
Decoration.node(from, from + node.nodeSize, {
|
|
4817
|
+
class: MLV_EDITOR_DRAG_SOURCE_CLASS,
|
|
4818
|
+
}),
|
|
4819
|
+
]);
|
|
4820
|
+
// A meta-only transaction: no step, so nothing for history to
|
|
4821
|
+
// record (prosemirror-history returns its state unchanged for a
|
|
4822
|
+
// step-less transaction, so no `addToHistory` flag is needed)
|
|
4823
|
+
// and no model emission. `update` below compares documents by
|
|
4824
|
+
// identity, so this does not read as a document change and end
|
|
4825
|
+
// the drag.
|
|
4826
|
+
view.dispatch(view.state.tr.setMeta(MLV_EDITOR_DRAG_SOURCE_KEY, next));
|
|
4827
|
+
};
|
|
3739
4828
|
const endDrag = () => {
|
|
3740
|
-
for (const slot of slots)
|
|
3741
|
-
slot.element.style.transform = '';
|
|
3742
4829
|
indicator.setAttribute('data-visible', 'false');
|
|
3743
|
-
view.dom.classList.remove(MLV_EDITOR_DRAGGING_CLASS);
|
|
3744
|
-
sourceElement?.classList.remove(MLV_EDITOR_DRAG_SOURCE_CLASS);
|
|
3745
4830
|
ghost?.remove();
|
|
3746
4831
|
slots = [];
|
|
3747
4832
|
gap = null;
|
|
3748
4833
|
source = null;
|
|
3749
|
-
sourceElement = null;
|
|
3750
4834
|
ghost = null;
|
|
4835
|
+
// Last, once the closure no longer records a drag: the dispatch
|
|
4836
|
+
// re-enters `update`, which must not see one still in flight.
|
|
4837
|
+
setSourceDecoration(null);
|
|
3751
4838
|
};
|
|
3752
4839
|
/** Target index for the pointer, or null when the drop is a no-op. */
|
|
3753
4840
|
const targetAt = (clientY) => {
|
|
@@ -3772,11 +4859,15 @@ const MlvEditorBlockHandle = Extension.create({
|
|
|
3772
4859
|
*
|
|
3773
4860
|
* Only the span between the two indices moved, so the walk is
|
|
3774
4861
|
* bounded by the distance dragged rather than by document length.
|
|
4862
|
+
* Each element is resolved live through `nodeDOM` after the move,
|
|
4863
|
+
* never taken from the snapshot: ProseMirror has replaced most of
|
|
4864
|
+
* the elements measured at `dragstart` by now.
|
|
3775
4865
|
*
|
|
3776
|
-
* Uses the Web Animations API rather than a transition
|
|
3777
|
-
* to force a reflow between setting and
|
|
3778
|
-
* animation cleans itself up, and it
|
|
3779
|
-
*
|
|
4866
|
+
* Uses the Web Animations API rather than a transition or an inline
|
|
4867
|
+
* `transform`: nothing has to force a reflow between setting and
|
|
4868
|
+
* clearing the offset, the animation cleans itself up, and it
|
|
4869
|
+
* writes no attribute — an inline style is a DOM mutation
|
|
4870
|
+
* ProseMirror would answer by redrawing the block mid-animation.
|
|
3780
4871
|
*/
|
|
3781
4872
|
const settle = (tops, from, to) => {
|
|
3782
4873
|
if (prefersReducedMotion())
|
|
@@ -3786,8 +4877,7 @@ const MlvEditorBlockHandle = Extension.create({
|
|
|
3786
4877
|
order.splice(from, 1);
|
|
3787
4878
|
order.splice(to, 0, from);
|
|
3788
4879
|
const doc = view.state.doc;
|
|
3789
|
-
const scale =
|
|
3790
|
-
const mountTop = mount.getBoundingClientRect().top;
|
|
4880
|
+
const { top: mountTop, scale } = mountFrame(mount);
|
|
3791
4881
|
const first = Math.min(from, to);
|
|
3792
4882
|
const last = Math.max(from, to);
|
|
3793
4883
|
for (let index = first; index <= last; index += 1) {
|
|
@@ -3824,11 +4914,18 @@ const MlvEditorBlockHandle = Extension.create({
|
|
|
3824
4914
|
return;
|
|
3825
4915
|
}
|
|
3826
4916
|
const measured = snapshotBlocks(view, mount);
|
|
3827
|
-
const
|
|
4917
|
+
const from = index < view.state.doc.childCount
|
|
4918
|
+
? childStart(view.state.doc, index)
|
|
4919
|
+
: null;
|
|
4920
|
+
// Resolved now, from the document position, and used only in
|
|
4921
|
+
// this handler — see `snapshotBlocks` for why no element is kept.
|
|
4922
|
+
const element = from === null ? null : view.nodeDOM(from);
|
|
3828
4923
|
// The hovered index outliving its block means the snapshot and
|
|
3829
4924
|
// `data-index` disagree; refusing is the same answer as a drag
|
|
3830
4925
|
// that never hovered.
|
|
3831
|
-
if (
|
|
4926
|
+
if (from === null ||
|
|
4927
|
+
!(element instanceof HTMLElement) ||
|
|
4928
|
+
!measured.some((candidate) => candidate.index === index)) {
|
|
3832
4929
|
event.preventDefault();
|
|
3833
4930
|
return;
|
|
3834
4931
|
}
|
|
@@ -3849,13 +4946,17 @@ const MlvEditorBlockHandle = Extension.create({
|
|
|
3849
4946
|
// Cloned *before* the source is dimmed: the clone carries
|
|
3850
4947
|
// resolved computed styles, so dimming first would bake the
|
|
3851
4948
|
// reduced opacity into the drag image.
|
|
3852
|
-
ghost = createGhostElement(
|
|
4949
|
+
ghost = createGhostElement(element, mountFrame(mount).scale);
|
|
4950
|
+
// The pointer rides the image's inline-start edge: its right
|
|
4951
|
+
// edge in RTL. The offset is in CSS pixels against the image as
|
|
4952
|
+
// painted, and the wrapper carries the editor zoom as CSS
|
|
4953
|
+
// `zoom`, which `offsetWidth` leaves out (measured 475 against
|
|
4954
|
+
// 594 at 125%): only the rendered width puts the pointer on
|
|
4955
|
+
// that edge.
|
|
3853
4956
|
event.dataTransfer?.setDragImage(ghost, getComputedStyle(view.dom).direction === 'rtl'
|
|
3854
|
-
? ghost.
|
|
4957
|
+
? Math.round(ghost.getBoundingClientRect().width)
|
|
3855
4958
|
: 0, 0);
|
|
3856
|
-
|
|
3857
|
-
sourceElement.classList.add(MLV_EDITOR_DRAG_SOURCE_CLASS);
|
|
3858
|
-
view.dom.classList.add(MLV_EDITOR_DRAGGING_CLASS);
|
|
4959
|
+
setSourceDecoration(from);
|
|
3859
4960
|
};
|
|
3860
4961
|
const onDragOver = (event) => {
|
|
3861
4962
|
if (source === null)
|
|
@@ -3893,8 +4994,8 @@ const MlvEditorBlockHandle = Extension.create({
|
|
|
3893
4994
|
event.clientY > rect.bottom);
|
|
3894
4995
|
};
|
|
3895
4996
|
const onDragLeave = (event) => {
|
|
3896
|
-
// Only the
|
|
3897
|
-
//
|
|
4997
|
+
// Only the line retracts; the source stays dimmed. The drag
|
|
4998
|
+
// itself is still in flight, and re-entering
|
|
3898
4999
|
// the editor must resolve a target again rather than leave the
|
|
3899
5000
|
// user dragging something that can no longer be dropped.
|
|
3900
5001
|
if (source !== null && leavesMount(event))
|
|
@@ -3909,10 +5010,9 @@ const MlvEditorBlockHandle = Extension.create({
|
|
|
3909
5010
|
// Resolved from the drop's own coordinates rather than the last
|
|
3910
5011
|
// `dragover`, so the release position always wins.
|
|
3911
5012
|
const to = targetAt(event.clientY);
|
|
3912
|
-
// Captured before `endDrag` discards the snapshot.
|
|
3913
|
-
//
|
|
3914
|
-
//
|
|
3915
|
-
// turn, so nothing the user saw shifted is measured as movement.
|
|
5013
|
+
// Captured before `endDrag` discards the snapshot. Nothing moves
|
|
5014
|
+
// a block during the drag, so the `dragstart` tops are exactly
|
|
5015
|
+
// the FLIP's "before".
|
|
3916
5016
|
const tops = slots.map((slot) => slot.top);
|
|
3917
5017
|
endDrag();
|
|
3918
5018
|
if (to === null)
|
|
@@ -3924,6 +5024,27 @@ const MlvEditorBlockHandle = Extension.create({
|
|
|
3924
5024
|
if (event.key === 'Escape' && source !== null)
|
|
3925
5025
|
endDrag();
|
|
3926
5026
|
};
|
|
5027
|
+
// These eight stay raw `addEventListener`s rather than becoming
|
|
5028
|
+
// `fromEvent` + `takeUntilDestroyed`, deliberately and for two
|
|
5029
|
+
// reasons.
|
|
5030
|
+
//
|
|
5031
|
+
// There is no `DestroyRef` to take. This file is a plain
|
|
5032
|
+
// ProseMirror plugin with no Angular import at all, constructed by
|
|
5033
|
+
// Tiptap outside any injection context, so `inject()` would throw.
|
|
5034
|
+
// The lifetime that matters here is not the editor component's
|
|
5035
|
+
// either: it is this plugin *view's*, which is torn down and
|
|
5036
|
+
// rebuilt whenever the editor is recreated (a format switch, an
|
|
5037
|
+
// extension-set change). `destroy()` below is that boundary, and it
|
|
5038
|
+
// unbinds all eight with the capture flags they were bound with.
|
|
5039
|
+
//
|
|
5040
|
+
// And the three capture-phase drag listeners are protocol-sensitive.
|
|
5041
|
+
// ProseMirror installs its own `dragover`/`drop` handlers on
|
|
5042
|
+
// `view.dom`, a descendant of `mount`; capturing on the ancestor is
|
|
5043
|
+
// what lets `onDrop` claim the drop and `stopPropagation()` before
|
|
5044
|
+
// ProseMirror inserts the dragged slice itself. Nothing in the unit
|
|
5045
|
+
// suite can drive a native HTML5 drag, so that ordering cannot be
|
|
5046
|
+
// pinned by a test here — and an unprovable reordering of block
|
|
5047
|
+
// drag-and-drop is not worth a stylistic conversion.
|
|
3927
5048
|
mount.addEventListener('mousemove', onMouseMove);
|
|
3928
5049
|
mount.addEventListener('mouseleave', hide);
|
|
3929
5050
|
handle.addEventListener('dragstart', onDragStart);
|
|
@@ -3960,6 +5081,8 @@ const MlvEditorBlockHandle = Extension.create({
|
|
|
3960
5081
|
// selection-only transaction carries the same reference — so
|
|
3961
5082
|
// this is exactly `docChanged` at O(1), where a structural
|
|
3962
5083
|
// compare would walk the whole document on every keystroke.
|
|
5084
|
+
// The same change has already emptied the source decoration in
|
|
5085
|
+
// plugin state (`apply`), so this `endDrag` dispatches nothing.
|
|
3963
5086
|
if (previous.doc !== updated.state.doc && source !== null) {
|
|
3964
5087
|
endDrag();
|
|
3965
5088
|
}
|
|
@@ -3970,7 +5093,9 @@ const MlvEditorBlockHandle = Extension.create({
|
|
|
3970
5093
|
// releasing the button after the revocation cannot reorder
|
|
3971
5094
|
// anything. Previously the line went through `decorations`,
|
|
3972
5095
|
// which read `enabled()` itself; a mount-owned element has to
|
|
3973
|
-
// be told.
|
|
5096
|
+
// be told. Clearing the dim dispatches from inside `update`;
|
|
5097
|
+
// that re-entry finds no drag recorded and returns, so it runs
|
|
5098
|
+
// once.
|
|
3974
5099
|
if (source !== null)
|
|
3975
5100
|
endDrag();
|
|
3976
5101
|
},
|
|
@@ -3988,6 +5113,8 @@ const MlvEditorBlockHandle = Extension.create({
|
|
|
3988
5113
|
// A drag interrupted by teardown leaves its drag image on
|
|
3989
5114
|
// `document.body`, outside everything else this removes.
|
|
3990
5115
|
ghost?.remove();
|
|
5116
|
+
// The dim stays in plugin state, where this cannot dispatch;
|
|
5117
|
+
// see `clearInheritedDragSource`.
|
|
3991
5118
|
},
|
|
3992
5119
|
};
|
|
3993
5120
|
},
|
|
@@ -4934,8 +6061,16 @@ class EditorImageUploadCoordinator extends MlvEditorImageUploadControl {
|
|
|
4934
6061
|
catch {
|
|
4935
6062
|
return undefined;
|
|
4936
6063
|
}
|
|
4937
|
-
|
|
4938
|
-
|
|
6064
|
+
// The author's metadata states intent and wins; the adapter's only fills
|
|
6065
|
+
// what the author left out. An author `alt: ''` marks the image
|
|
6066
|
+
// decorative: the alt stays empty (WCAG 1.1.1) and no title is kept,
|
|
6067
|
+
// the author's or the adapter's, because an `alt=""` image with a title
|
|
6068
|
+
// is exposed as an unnamed image described by it (WCAG H67). Otherwise
|
|
6069
|
+
// an empty author title means no title, so the adapter's may fill it.
|
|
6070
|
+
const adapterAlt = typeof value['alt'] === 'string' ? value['alt'] : undefined;
|
|
6071
|
+
const adapterTitle = typeof value['title'] === 'string' ? value['title'] : undefined;
|
|
6072
|
+
const alt = state.alt ?? adapterAlt;
|
|
6073
|
+
const title = state.alt === '' ? undefined : state.title || adapterTitle;
|
|
4939
6074
|
return {
|
|
4940
6075
|
src,
|
|
4941
6076
|
...(alt === undefined ? {} : { alt }),
|
|
@@ -5196,8 +6331,8 @@ class MlvEditorAlignment {
|
|
|
5196
6331
|
this._unregister?.();
|
|
5197
6332
|
this._unregister = undefined;
|
|
5198
6333
|
}
|
|
5199
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
5200
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.
|
|
6334
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorAlignment, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
6335
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: MlvEditorAlignment, isStandalone: true, selector: "mlv-editor-alignment", host: { properties: { "hidden": "!_supported()" }, classAttribute: "mlv-editor-alignment" }, viewQueries: [{ propertyName: "_menu", first: true, predicate: ["_menu"], descendants: true, isSignal: true }, { propertyName: "_trigger", first: true, predicate: MlvMenuTrigger, descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
5201
6336
|
<button
|
|
5202
6337
|
mlvButton
|
|
5203
6338
|
mlvEditorToolbarWidget
|
|
@@ -5229,7 +6364,7 @@ class MlvEditorAlignment {
|
|
|
5229
6364
|
</mlv-menu>
|
|
5230
6365
|
`, isInline: true, dependencies: [{ kind: "component", type: MlvButton, selector: "button[mlvButton], a[mlvButton]", inputs: ["variant", "shape", "disabled", "loading", "selected"] }, { kind: "directive", type: MlvButtonIcon, selector: "[mlvButtonIcon]" }, { kind: "directive", type: MlvTooltip, selector: "[mlvTooltip]", inputs: ["mlvTooltip", "tooltipPlacement", "tooltipTone", "tooltipDelay", "tooltipDisabled", "tooltipArrow"] }, { kind: "component", type: LucideAlignLeft, selector: "svg[lucideTextAlignStart], svg[lucideText], svg[lucideAlignLeft]" }, { kind: "component", type: MlvListItem, selector: "mlv-list-item", inputs: ["itemRole", "unread", "accent"] }, { kind: "component", type: MlvMenu, selector: "mlv-menu", inputs: ["label", "mlvDensity", "dataSource"], outputs: ["closed"] }, { kind: "directive", type: MlvMenuItem, selector: "mlv-list-item[mlvMenuItem]", inputs: ["disabled"], outputs: ["itemClick"] }, { kind: "directive", type: MlvMenuTrigger, selector: "[mlvMenuTrigger]", inputs: ["mlvMenuTrigger", "isSubmenuTrigger", "menuTriggerDisabled"], outputs: ["menuOpened", "menuClosed"] }, { kind: "directive", type: MlvEditorToolbarWidget, selector: "[mlvEditorToolbarWidget]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
5231
6366
|
}
|
|
5232
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
6367
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorAlignment, decorators: [{
|
|
5233
6368
|
type: Component,
|
|
5234
6369
|
args: [{
|
|
5235
6370
|
selector: 'mlv-editor-alignment',
|
|
@@ -5339,8 +6474,8 @@ class MlvEditorCommandButton {
|
|
|
5339
6474
|
return;
|
|
5340
6475
|
this._context.run(this.command());
|
|
5341
6476
|
}
|
|
5342
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
5343
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.
|
|
6477
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorCommandButton, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
6478
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.7", type: MlvEditorCommandButton, isStandalone: true, selector: "mlv-editor-command-button", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, supports: { classPropertyName: "supports", publicName: "supports", isSignal: true, isRequired: true, transformFunction: null }, command: { classPropertyName: "command", publicName: "command", isSignal: true, isRequired: true, transformFunction: null }, canCommand: { classPropertyName: "canCommand", publicName: "canCommand", isSignal: true, isRequired: false, transformFunction: null }, pressed: { classPropertyName: "pressed", publicName: "pressed", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "hidden": "!supported()" }, classAttribute: "mlv-editor-command-button" }, ngImport: i0, template: `
|
|
5344
6479
|
<button
|
|
5345
6480
|
mlvButton
|
|
5346
6481
|
mlvEditorToolbarWidget
|
|
@@ -5360,7 +6495,7 @@ class MlvEditorCommandButton {
|
|
|
5360
6495
|
</button>
|
|
5361
6496
|
`, isInline: true, dependencies: [{ kind: "component", type: MlvButton, selector: "button[mlvButton], a[mlvButton]", inputs: ["variant", "shape", "disabled", "loading", "selected"] }, { kind: "directive", type: MlvButtonIcon, selector: "[mlvButtonIcon]" }, { kind: "directive", type: MlvTooltip, selector: "[mlvTooltip]", inputs: ["mlvTooltip", "tooltipPlacement", "tooltipTone", "tooltipDelay", "tooltipDisabled", "tooltipArrow"] }, { kind: "directive", type: MlvEditorToolbarWidget, selector: "[mlvEditorToolbarWidget]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
5362
6497
|
}
|
|
5363
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
6498
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorCommandButton, decorators: [{
|
|
5364
6499
|
type: Component,
|
|
5365
6500
|
args: [{
|
|
5366
6501
|
selector: 'mlv-editor-command-button',
|
|
@@ -5418,8 +6553,8 @@ class MlvEditorBlockInsert {
|
|
|
5418
6553
|
this._revision();
|
|
5419
6554
|
return this._context.isActive(name);
|
|
5420
6555
|
};
|
|
5421
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
5422
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.
|
|
6556
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorBlockInsert, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
6557
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.7", type: MlvEditorBlockInsert, isStandalone: true, selector: "mlv-editor-block-insert", host: { attributes: { "role": "group" }, classAttribute: "mlv-editor-block-insert" }, ngImport: i0, template: `
|
|
5423
6558
|
<mlv-editor-command-button
|
|
5424
6559
|
[label]="_copy().blockquote"
|
|
5425
6560
|
[pressed]="_isActive('blockquote')"
|
|
@@ -5445,7 +6580,7 @@ class MlvEditorBlockInsert {
|
|
|
5445
6580
|
/></mlv-editor-command-button>
|
|
5446
6581
|
`, isInline: true, dependencies: [{ kind: "component", type: MlvEditorCommandButton, selector: "mlv-editor-command-button", inputs: ["label", "supports", "command", "canCommand", "pressed"] }, { kind: "component", type: LucideCode2, selector: "svg[lucideCodeXml], svg[lucideCode2]" }, { kind: "component", type: LucideMinus, selector: "svg[lucideMinus]" }, { kind: "component", type: LucideTextQuote, selector: "svg[lucideTextQuote]" }, { kind: "directive", type: MlvButtonIcon, selector: "[mlvButtonIcon]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
5447
6582
|
}
|
|
5448
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
6583
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorBlockInsert, decorators: [{
|
|
5449
6584
|
type: Component,
|
|
5450
6585
|
args: [{
|
|
5451
6586
|
selector: 'mlv-editor-block-insert',
|
|
@@ -5642,8 +6777,8 @@ class MlvEditorHeading {
|
|
|
5642
6777
|
this._unregister?.();
|
|
5643
6778
|
this._unregister = undefined;
|
|
5644
6779
|
}
|
|
5645
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
5646
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.
|
|
6780
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorHeading, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
6781
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: MlvEditorHeading, isStandalone: true, selector: "mlv-editor-heading", inputs: { levels: { classPropertyName: "levels", publicName: "levels", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "hidden": "!_supported()" }, classAttribute: "mlv-editor-heading" }, viewQueries: [{ propertyName: "_menu", first: true, predicate: ["_menu"], descendants: true, isSignal: true }, { propertyName: "_trigger", first: true, predicate: MlvMenuTrigger, descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
5647
6782
|
<button
|
|
5648
6783
|
mlvButton
|
|
5649
6784
|
mlvEditorToolbarWidget
|
|
@@ -5712,7 +6847,7 @@ class MlvEditorHeading {
|
|
|
5712
6847
|
</mlv-menu>
|
|
5713
6848
|
`, isInline: true, dependencies: [{ kind: "component", type: MlvButton, selector: "button[mlvButton], a[mlvButton]", inputs: ["variant", "shape", "disabled", "loading", "selected"] }, { kind: "directive", type: MlvButtonIcon, selector: "[mlvButtonIcon]" }, { kind: "directive", type: MlvTooltip, selector: "[mlvTooltip]", inputs: ["mlvTooltip", "tooltipPlacement", "tooltipTone", "tooltipDelay", "tooltipDisabled", "tooltipArrow"] }, { kind: "component", type: LucideHeading, selector: "svg[lucideHeading]" }, { kind: "component", type: LucideHeading1, selector: "svg[lucideHeading1]" }, { kind: "component", type: LucideHeading2, selector: "svg[lucideHeading2]" }, { kind: "component", type: LucideHeading3, selector: "svg[lucideHeading3]" }, { kind: "component", type: LucideHeading4, selector: "svg[lucideHeading4]" }, { kind: "component", type: LucideHeading5, selector: "svg[lucideHeading5]" }, { kind: "component", type: LucideHeading6, selector: "svg[lucideHeading6]" }, { kind: "component", type: MlvListItem, selector: "mlv-list-item", inputs: ["itemRole", "unread", "accent"] }, { kind: "component", type: MlvMenu, selector: "mlv-menu", inputs: ["label", "mlvDensity", "dataSource"], outputs: ["closed"] }, { kind: "directive", type: MlvMenuItem, selector: "mlv-list-item[mlvMenuItem]", inputs: ["disabled"], outputs: ["itemClick"] }, { kind: "directive", type: MlvMenuTrigger, selector: "[mlvMenuTrigger]", inputs: ["mlvMenuTrigger", "isSubmenuTrigger", "menuTriggerDisabled"], outputs: ["menuOpened", "menuClosed"] }, { kind: "directive", type: MlvEditorToolbarWidget, selector: "[mlvEditorToolbarWidget]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
5714
6849
|
}
|
|
5715
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
6850
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorHeading, decorators: [{
|
|
5716
6851
|
type: Component,
|
|
5717
6852
|
args: [{
|
|
5718
6853
|
selector: 'mlv-editor-heading',
|
|
@@ -5832,14 +6967,18 @@ class MlvEditorColorControl {
|
|
|
5832
6967
|
const trigger = this._picker().triggerElement();
|
|
5833
6968
|
if (!trigger)
|
|
5834
6969
|
return;
|
|
6970
|
+
// Released from `onCleanup`, not `takeUntilDestroyed`: this effect
|
|
6971
|
+
// re-runs whenever the picker publishes a different trigger element, and
|
|
6972
|
+
// a destroy-scoped teardown would leave every superseded generation
|
|
6973
|
+
// subscribed for the rest of the control's life.
|
|
6974
|
+
const listeners = new Subscription();
|
|
5835
6975
|
const activate = () => this._roving.activate(trigger);
|
|
5836
|
-
|
|
5837
|
-
|
|
6976
|
+
listeners.add(fromEvent(trigger, 'focus').subscribe(activate));
|
|
6977
|
+
listeners.add(fromEvent(trigger, 'pointerdown').subscribe(activate));
|
|
5838
6978
|
const unregister = untracked(() => this._roving.register(trigger));
|
|
5839
6979
|
onCleanup(() => {
|
|
5840
6980
|
unregister();
|
|
5841
|
-
|
|
5842
|
-
trigger.removeEventListener('pointerdown', activate);
|
|
6981
|
+
listeners.unsubscribe();
|
|
5843
6982
|
});
|
|
5844
6983
|
});
|
|
5845
6984
|
effect(() => {
|
|
@@ -5871,10 +7010,10 @@ class MlvEditorColorControl {
|
|
|
5871
7010
|
return;
|
|
5872
7011
|
this._context.run((editor) => this._run(editor, selection, color));
|
|
5873
7012
|
}
|
|
5874
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
5875
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "22.
|
|
7013
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorColorControl, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
7014
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "22.1.7", type: MlvEditorColorControl, isStandalone: true, viewQueries: [{ propertyName: "_picker", first: true, predicate: ["picker"], descendants: true, isSignal: true }], ngImport: i0 });
|
|
5876
7015
|
}
|
|
5877
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
7016
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorColorControl, decorators: [{
|
|
5878
7017
|
type: Directive
|
|
5879
7018
|
}], ctorParameters: () => [], propDecorators: { _picker: [{ type: i0.ViewChild, args: ['picker', { isSignal: true }] }] } });
|
|
5880
7019
|
|
|
@@ -5907,8 +7046,8 @@ class MlvEditorHighlight extends MlvEditorColorControl {
|
|
|
5907
7046
|
? chain.toggleHighlight({ color }).run()
|
|
5908
7047
|
: chain.unsetHighlight().run();
|
|
5909
7048
|
}
|
|
5910
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
5911
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.
|
|
7049
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorHighlight, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
7050
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.7", type: MlvEditorHighlight, isStandalone: true, selector: "mlv-editor-highlight", host: { properties: { "hidden": "!_supported()" }, classAttribute: "mlv-editor-highlight" }, usesInheritance: true, ngImport: i0, template: `
|
|
5912
7051
|
<mlv-color-picker-popup
|
|
5913
7052
|
#picker
|
|
5914
7053
|
presentation="icon"
|
|
@@ -5925,7 +7064,7 @@ class MlvEditorHighlight extends MlvEditorColorControl {
|
|
|
5925
7064
|
</mlv-color-picker-popup>
|
|
5926
7065
|
`, isInline: true, dependencies: [{ kind: "component", type: MlvColorPickerPopup, selector: "mlv-color-picker-popup", inputs: ["value", "presentation", "opened", "showOpacity", "defaultMode", "supportedFormats", "live"], outputs: ["valueChange", "openedChange", "afterOpened", "afterClosed", "colorChange"] }, { kind: "directive", type: MlvButtonIcon, selector: "[mlvButtonIcon]" }, { kind: "component", type: LucideHighlighter, selector: "svg[lucideHighlighter]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
5927
7066
|
}
|
|
5928
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
7067
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorHighlight, decorators: [{
|
|
5929
7068
|
type: Component,
|
|
5930
7069
|
args: [{
|
|
5931
7070
|
selector: 'mlv-editor-highlight',
|
|
@@ -5984,8 +7123,8 @@ class MlvEditorInlineMarks {
|
|
|
5984
7123
|
/** @protected Italic command. */ _italic = (editor) => editor.chain().focus().toggleItalic().run();
|
|
5985
7124
|
/** @protected Strike command. */ _strike = (editor) => editor.chain().focus().toggleStrike().run();
|
|
5986
7125
|
/** @protected Underline command. */ _underline = (editor) => editor.chain().focus().toggleUnderline().run();
|
|
5987
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
5988
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.
|
|
7126
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorInlineMarks, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
7127
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.7", type: MlvEditorInlineMarks, isStandalone: true, selector: "mlv-editor-inline-marks", host: { attributes: { "role": "group" }, classAttribute: "mlv-editor-inline-marks" }, ngImport: i0, template: `
|
|
5989
7128
|
<mlv-editor-command-button
|
|
5990
7129
|
[label]="_copy().bold"
|
|
5991
7130
|
[pressed]="_isActive('bold')"
|
|
@@ -6020,7 +7159,7 @@ class MlvEditorInlineMarks {
|
|
|
6020
7159
|
/></mlv-editor-command-button>
|
|
6021
7160
|
`, isInline: true, dependencies: [{ kind: "component", type: MlvEditorCommandButton, selector: "mlv-editor-command-button", inputs: ["label", "supports", "command", "canCommand", "pressed"] }, { kind: "component", type: LucideBold, selector: "svg[lucideBold]" }, { kind: "component", type: LucideItalic, selector: "svg[lucideItalic]" }, { kind: "component", type: LucideStrikethrough, selector: "svg[lucideStrikethrough]" }, { kind: "component", type: LucideUnderline, selector: "svg[lucideUnderline]" }, { kind: "directive", type: MlvButtonIcon, selector: "[mlvButtonIcon]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
6022
7161
|
}
|
|
6023
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
7162
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorInlineMarks, decorators: [{
|
|
6024
7163
|
type: Component,
|
|
6025
7164
|
args: [{
|
|
6026
7165
|
selector: 'mlv-editor-inline-marks',
|
|
@@ -6182,8 +7321,8 @@ class MlvEditorList {
|
|
|
6182
7321
|
this._unregister?.();
|
|
6183
7322
|
this._unregister = undefined;
|
|
6184
7323
|
}
|
|
6185
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
6186
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.
|
|
7324
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorList, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
7325
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: MlvEditorList, isStandalone: true, selector: "mlv-editor-list", host: { properties: { "hidden": "!_supported()" }, classAttribute: "mlv-editor-list" }, viewQueries: [{ propertyName: "_menu", first: true, predicate: ["_menu"], descendants: true, isSignal: true }, { propertyName: "_trigger", first: true, predicate: MlvMenuTrigger, descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
6187
7326
|
<button
|
|
6188
7327
|
mlvButton
|
|
6189
7328
|
mlvEditorToolbarWidget
|
|
@@ -6235,7 +7374,7 @@ class MlvEditorList {
|
|
|
6235
7374
|
</mlv-menu>
|
|
6236
7375
|
`, isInline: true, dependencies: [{ kind: "component", type: MlvButton, selector: "button[mlvButton], a[mlvButton]", inputs: ["variant", "shape", "disabled", "loading", "selected"] }, { kind: "directive", type: MlvButtonIcon, selector: "[mlvButtonIcon]" }, { kind: "directive", type: MlvTooltip, selector: "[mlvTooltip]", inputs: ["mlvTooltip", "tooltipPlacement", "tooltipTone", "tooltipDelay", "tooltipDisabled", "tooltipArrow"] }, { kind: "component", type: LucideList, selector: "svg[lucideList]" }, { kind: "component", type: MlvListItem, selector: "mlv-list-item", inputs: ["itemRole", "unread", "accent"] }, { kind: "component", type: MlvMenu, selector: "mlv-menu", inputs: ["label", "mlvDensity", "dataSource"], outputs: ["closed"] }, { kind: "directive", type: MlvMenuItem, selector: "mlv-list-item[mlvMenuItem]", inputs: ["disabled"], outputs: ["itemClick"] }, { kind: "directive", type: MlvMenuTrigger, selector: "[mlvMenuTrigger]", inputs: ["mlvMenuTrigger", "isSubmenuTrigger", "menuTriggerDisabled"], outputs: ["menuOpened", "menuClosed"] }, { kind: "directive", type: MlvEditorToolbarWidget, selector: "[mlvEditorToolbarWidget]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
6237
7376
|
}
|
|
6238
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
7377
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorList, decorators: [{
|
|
6239
7378
|
type: Component,
|
|
6240
7379
|
args: [{
|
|
6241
7380
|
selector: 'mlv-editor-list',
|
|
@@ -6538,15 +7677,14 @@ class MlvEditorLink {
|
|
|
6538
7677
|
.map((protocol) => protocol.trim().toLowerCase().replace(/:+$/, ''))
|
|
6539
7678
|
.filter(Boolean));
|
|
6540
7679
|
}
|
|
6541
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
6542
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.
|
|
7680
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorLink, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
7681
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: MlvEditorLink, isStandalone: true, selector: "mlv-editor-link", inputs: { allowedProtocols: { classPropertyName: "allowedProtocols", publicName: "allowedProtocols", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "hidden": "!_supported()" }, classAttribute: "mlv-editor-link" }, viewQueries: [{ propertyName: "_popup", first: true, predicate: ["_popup"], descendants: true, isSignal: true }, { propertyName: "_trigger", first: true, predicate: ["_trigger"], descendants: true, isSignal: true }, { propertyName: "_triggerElement", first: true, predicate: ["_triggerElement"], descendants: true, read: ElementRef, isSignal: true }, { propertyName: "_urlInput", first: true, predicate: ["_urlInput"], descendants: true, isSignal: true }, { propertyName: "_panel", first: true, predicate: ["_panel"], descendants: true, isSignal: true }], ngImport: i0, template: "<button\n #_trigger=\"mlvPopupTrigger\"\n #_triggerElement\n mlvButton\n mlvEditorToolbarWidget\n type=\"button\"\n shape=\"square\"\n mlvDensity=\"tight\"\n variant=\"transparent\"\n [disabled]=\"_disabled()\"\n [mlvPopupTrigger]=\"_popup\"\n [ariaHasPopup]=\"'dialog'\"\n [attr.aria-label]=\"_copy().link\"\n [attr.aria-pressed]=\"_active()\"\n [mlvTooltip]=\"_copy().link\"\n>\n <svg mlvButtonIcon lucideLink aria-hidden=\"true\" />\n</button>\n\n<mlv-popup\n #_popup\n panelRole=\"dialog\"\n [modal]=\"true\"\n [ariaLabel]=\"_copy().link\"\n (afterOpened)=\"_onOpened()\"\n (afterClosed)=\"_onClosed()\"\n>\n <ng-template mlvPopupContent>\n <div #_panel class=\"mlv-editor-link__panel\">\n <mlv-input\n #_urlInput\n type=\"url\"\n [label]=\"_copy().linkUrl\"\n [value]=\"_urlDraft()\"\n [disabled]=\"_disabled()\"\n [state]=\"_invalid() ? 'error' : 'default'\"\n [message]=\"_invalid() ? _copy().invalidLink : ''\"\n (valueChange)=\"_onUrlChange($event)\"\n (keydown.enter)=\"_apply()\"\n />\n <mlv-input\n [label]=\"_copy().linkText\"\n [value]=\"_textDraft()\"\n [disabled]=\"_disabled()\"\n (valueChange)=\"_textDraft.set($event)\"\n (keydown.enter)=\"_apply()\"\n />\n <mlv-switch [label]=\"_copy().openInNewTab\" [(checked)]=\"_newTab\">{{\n _copy().openInNewTab\n }}</mlv-switch>\n <div class=\"mlv-editor-link__actions\">\n <button\n mlvButton\n type=\"button\"\n variant=\"primary\"\n [disabled]=\"_disabled()\"\n (click)=\"_apply()\"\n >\n {{ _copy().applyLink }}\n </button>\n @if (_canRemove()) {\n <button\n mlvButton\n type=\"button\"\n variant=\"secondary\"\n [disabled]=\"_disabled()\"\n (click)=\"_remove()\"\n >\n {{ _copy().removeLink }}\n </button>\n }\n </div>\n </div>\n </ng-template>\n</mlv-popup>\n", styles: ["@layer mlv.components{.mlv-editor-link{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor-link *{box-sizing:border-box}.mlv-editor-link{display:inline-flex}.mlv-editor-link__panel{display:grid;width:min(22rem,100vw - 3.5rem);gap:var(--mlv-spacing-1-5);padding:var(--mlv-spacing-2)}.mlv-editor-link__actions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:var(--mlv-spacing-3)}}\n"], dependencies: [{ kind: "component", type: MlvButton, selector: "button[mlvButton], a[mlvButton]", inputs: ["variant", "shape", "disabled", "loading", "selected"] }, { kind: "directive", type: MlvButtonIcon, selector: "[mlvButtonIcon]" }, { kind: "directive", type: MlvTooltip, selector: "[mlvTooltip]", inputs: ["mlvTooltip", "tooltipPlacement", "tooltipTone", "tooltipDelay", "tooltipDisabled", "tooltipArrow"] }, { kind: "component", type: LucideLink, selector: "svg[lucideLink]" }, { kind: "directive", type: MlvEditorToolbarWidget, selector: "[mlvEditorToolbarWidget]" }, { kind: "component", type: MlvInput, selector: "mlv-input", inputs: ["type", "placeholder", "autocomplete", "inputMode", "maxLength", "minLength", "pattern", "name", "autofocus", "min", "max", "step", "bare", "projectControl", "role", "ariaExpanded", "ariaHasPopup", "ariaAutocomplete", "ariaControls", "ariaActiveDescendant", "ariaDescribedBy", "value"], outputs: ["valueChange", "inputFocus", "inputBlur"] }, { kind: "component", type: MlvPopup, selector: "mlv-popup", inputs: ["position", "positions", "width", "height", "minWidth", "minHeight", "maxWidth", "maxHeight", "hasArrow", "hasShadow", "class", "mlvDensity", "flexibleDimensions", "scrollStrategy", "hasBackdrop", "restrictPosition", "panelRole", "modal", "ariaLabel", "dismissExcludeElements", "mobileMode", "mobileBreakpoint", "mobileTitle", "mobileCloseLabel", "opened"], outputs: ["openedChange", "afterOpened", "afterClosed"] }, { kind: "directive", type: MlvPopupContent, selector: "[mlvPopupContent]" }, { kind: "directive", type: MlvPopupTrigger, selector: "[mlvPopupTrigger]", inputs: ["mlvPopupTrigger", "triggerOn", "ariaHasPopup"], exportAs: ["mlvPopupTrigger"] }, { kind: "component", type: MlvSwitch, selector: "mlv-switch", inputs: ["ariaLabelledBy", "checked"], outputs: ["checkedChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
6543
7682
|
}
|
|
6544
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
7683
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorLink, decorators: [{
|
|
6545
7684
|
type: Component,
|
|
6546
7685
|
args: [{ selector: 'mlv-editor-link', imports: [
|
|
6547
7686
|
MlvButton,
|
|
6548
7687
|
MlvButtonIcon,
|
|
6549
|
-
MlvButtonToggle,
|
|
6550
7688
|
MlvTooltip,
|
|
6551
7689
|
LucideLink,
|
|
6552
7690
|
MlvEditorToolbarWidget,
|
|
@@ -6558,7 +7696,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
|
|
|
6558
7696
|
], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
6559
7697
|
class: 'mlv-editor-link',
|
|
6560
7698
|
'[hidden]': '!_supported()',
|
|
6561
|
-
}, template: "<button\n #_trigger=\"mlvPopupTrigger\"\n #_triggerElement\n mlvButton\n mlvEditorToolbarWidget\n type=\"button\"\n shape=\"square\"\n mlvDensity=\"tight\"\n variant=\"transparent\"\n [disabled]=\"_disabled()\"\n [mlvPopupTrigger]=\"_popup\"\n [ariaHasPopup]=\"'dialog'\"\n [attr.aria-label]=\"_copy().link\"\n [attr.aria-pressed]=\"_active()\"\n [mlvTooltip]=\"_copy().link\"\n>\n <svg mlvButtonIcon lucideLink aria-hidden=\"true\" />\n</button>\n\n<mlv-popup\n #_popup\n panelRole=\"dialog\"\n [modal]=\"true\"\n [ariaLabel]=\"_copy().link\"\n (afterOpened)=\"_onOpened()\"\n (afterClosed)=\"_onClosed()\"\n>\n <ng-template mlvPopupContent>\n <div #_panel class=\"mlv-editor-link__panel\">\n <mlv-input\n #_urlInput\n type=\"url\"\n [label]=\"_copy().linkUrl\"\n [value]=\"_urlDraft()\"\n [disabled]=\"_disabled()\"\n [state]=\"_invalid() ? 'error' : 'default'\"\n [message]=\"_invalid() ? _copy().invalidLink : ''\"\n (valueChange)=\"_onUrlChange($event)\"\n (keydown.enter)=\"_apply()\"\n />\n <mlv-input\n [label]=\"_copy().linkText\"\n [value]=\"_textDraft()\"\n [disabled]=\"_disabled()\"\n (valueChange)=\"_textDraft.set($event)\"\n (keydown.enter)=\"_apply()\"\n />\n <mlv-switch [label]=\"_copy().openInNewTab\" [(checked)]=\"_newTab\"\n
|
|
7699
|
+
}, template: "<button\n #_trigger=\"mlvPopupTrigger\"\n #_triggerElement\n mlvButton\n mlvEditorToolbarWidget\n type=\"button\"\n shape=\"square\"\n mlvDensity=\"tight\"\n variant=\"transparent\"\n [disabled]=\"_disabled()\"\n [mlvPopupTrigger]=\"_popup\"\n [ariaHasPopup]=\"'dialog'\"\n [attr.aria-label]=\"_copy().link\"\n [attr.aria-pressed]=\"_active()\"\n [mlvTooltip]=\"_copy().link\"\n>\n <svg mlvButtonIcon lucideLink aria-hidden=\"true\" />\n</button>\n\n<mlv-popup\n #_popup\n panelRole=\"dialog\"\n [modal]=\"true\"\n [ariaLabel]=\"_copy().link\"\n (afterOpened)=\"_onOpened()\"\n (afterClosed)=\"_onClosed()\"\n>\n <ng-template mlvPopupContent>\n <div #_panel class=\"mlv-editor-link__panel\">\n <mlv-input\n #_urlInput\n type=\"url\"\n [label]=\"_copy().linkUrl\"\n [value]=\"_urlDraft()\"\n [disabled]=\"_disabled()\"\n [state]=\"_invalid() ? 'error' : 'default'\"\n [message]=\"_invalid() ? _copy().invalidLink : ''\"\n (valueChange)=\"_onUrlChange($event)\"\n (keydown.enter)=\"_apply()\"\n />\n <mlv-input\n [label]=\"_copy().linkText\"\n [value]=\"_textDraft()\"\n [disabled]=\"_disabled()\"\n (valueChange)=\"_textDraft.set($event)\"\n (keydown.enter)=\"_apply()\"\n />\n <mlv-switch [label]=\"_copy().openInNewTab\" [(checked)]=\"_newTab\">{{\n _copy().openInNewTab\n }}</mlv-switch>\n <div class=\"mlv-editor-link__actions\">\n <button\n mlvButton\n type=\"button\"\n variant=\"primary\"\n [disabled]=\"_disabled()\"\n (click)=\"_apply()\"\n >\n {{ _copy().applyLink }}\n </button>\n @if (_canRemove()) {\n <button\n mlvButton\n type=\"button\"\n variant=\"secondary\"\n [disabled]=\"_disabled()\"\n (click)=\"_remove()\"\n >\n {{ _copy().removeLink }}\n </button>\n }\n </div>\n </div>\n </ng-template>\n</mlv-popup>\n", styles: ["@layer mlv.components{.mlv-editor-link{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor-link *{box-sizing:border-box}.mlv-editor-link{display:inline-flex}.mlv-editor-link__panel{display:grid;width:min(22rem,100vw - 3.5rem);gap:var(--mlv-spacing-1-5);padding:var(--mlv-spacing-2)}.mlv-editor-link__actions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:var(--mlv-spacing-3)}}\n"] }]
|
|
6562
7700
|
}], ctorParameters: () => [], propDecorators: { allowedProtocols: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowedProtocols", required: false }] }], _popup: [{ type: i0.ViewChild, args: ['_popup', { isSignal: true }] }], _trigger: [{ type: i0.ViewChild, args: ['_trigger', { isSignal: true }] }], _triggerElement: [{ type: i0.ViewChild, args: ['_triggerElement', { ...{
|
|
6563
7701
|
read: ElementRef,
|
|
6564
7702
|
}, isSignal: true }] }], _urlInput: [{ type: i0.ViewChild, args: ['_urlInput', { isSignal: true }] }], _panel: [{ type: i0.ViewChild, args: ['_panel', { isSignal: true }] }] } });
|
|
@@ -6711,6 +7849,8 @@ class MlvEditorImageUploadDialog {
|
|
|
6711
7849
|
this._dialogRef.close();
|
|
6712
7850
|
return;
|
|
6713
7851
|
}
|
|
7852
|
+
// A removed failure: its "failed" text must not outlive it.
|
|
7853
|
+
this._announcement.set('');
|
|
6714
7854
|
this._reset();
|
|
6715
7855
|
});
|
|
6716
7856
|
this._dialogRef.beforeClose().subscribe(() => {
|
|
@@ -6784,6 +7924,16 @@ class MlvEditorImageUploadDialog {
|
|
|
6784
7924
|
this._progressBucket = -1;
|
|
6785
7925
|
this._coordinator.retry(id);
|
|
6786
7926
|
}
|
|
7927
|
+
/**
|
|
7928
|
+
* @protected Toggles Decorative. Turning it on clears the title draft: a
|
|
7929
|
+
* decorative image carries an empty alt and no title (WCAG H67), and the
|
|
7930
|
+
* title field stays disabled until it is turned off.
|
|
7931
|
+
*/
|
|
7932
|
+
_onDecorativeChange(decorative) {
|
|
7933
|
+
this._decorative.set(decorative);
|
|
7934
|
+
if (decorative)
|
|
7935
|
+
this._title.set('');
|
|
7936
|
+
}
|
|
6787
7937
|
/** @protected Removes the failed item and clears the draft. */
|
|
6788
7938
|
_remove() {
|
|
6789
7939
|
const id = this._activeId();
|
|
@@ -6822,10 +7972,10 @@ class MlvEditorImageUploadDialog {
|
|
|
6822
7972
|
if (url)
|
|
6823
7973
|
URL.revokeObjectURL(url);
|
|
6824
7974
|
}
|
|
6825
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
6826
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.
|
|
7975
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorImageUploadDialog, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
7976
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: MlvEditorImageUploadDialog, isStandalone: true, selector: "mlv-editor-image-upload-dialog", host: { classAttribute: "mlv-editor-image-upload-dialog" }, ngImport: i0, template: "<mlv-dialog>\n <mlv-dialog-header />\n <mlv-dialog-body>\n <div class=\"mlv-editor-image-upload-dialog__body\">\n <mlv-file-upload\n [accept]=\"_accept()\"\n [maxSize]=\"_maxSize()\"\n [multiple]=\"false\"\n [disabled]=\"_disabled() || !!_activeId()\"\n [value]=\"_files()\"\n (filesChange)=\"_onFilesChange($event)\"\n />\n\n @if (_file(); as selected) {\n @if (selected.previewUrl) {\n <img\n class=\"mlv-editor-image-upload-dialog__preview\"\n [src]=\"selected.previewUrl\"\n [alt]=\"_previewAlt()\"\n />\n }\n @if (_item(); as item) {\n @if (item.status === 'uploading') {\n <mlv-progress\n [value]=\"item.progress\"\n [showPercentage]=\"true\"\n [ariaLabel]=\"_progressLabel(item.progress)\"\n />\n } @else {\n <p\n class=\"mlv-editor-image-upload-dialog__error\"\n role=\"alert\"\n aria-live=\"assertive\"\n >\n {{ _copy().failed }}\n </p>\n }\n }\n }\n\n <mlv-input\n [label]=\"_copy().alt\"\n [ariaLabel]=\"_copy().alt\"\n [value]=\"_alt()\"\n [disabled]=\"_disabled() || !!_activeId() || _decorative()\"\n [state]=\"_altInvalid() ? 'error' : 'default'\"\n [message]=\"_altInvalid() ? _copy().altRequired : ''\"\n (valueChange)=\"_alt.set($event)\"\n />\n <mlv-input\n [label]=\"_copy().imageTitle\"\n [ariaLabel]=\"_copy().imageTitle\"\n [value]=\"_title()\"\n [disabled]=\"_disabled() || !!_activeId() || _decorative()\"\n (valueChange)=\"_title.set($event)\"\n />\n <mlv-switch\n [ariaLabel]=\"_copy().decorative\"\n [checked]=\"_decorative()\"\n [disabled]=\"_disabled() || !!_activeId()\"\n (checkedChange)=\"_onDecorativeChange($event)\"\n >\n {{ _copy().decorative }}\n </mlv-switch>\n\n <span\n class=\"mlv-editor-image-upload-dialog__live\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n >{{ _announcement() }}</span\n >\n </div>\n </mlv-dialog-body>\n\n <mlv-dialog-footer>\n @if (_item()?.status === 'uploading') {\n <button\n mlvButton\n type=\"button\"\n variant=\"secondary\"\n [attr.aria-label]=\"_copy().cancel\"\n (click)=\"_cancel()\"\n >\n {{ _copy().cancel }}\n </button>\n } @else if (_item()?.status === 'failed') {\n <button\n mlvButton\n type=\"button\"\n variant=\"primary\"\n [attr.aria-label]=\"_copy().retry\"\n [disabled]=\"_disabled()\"\n (click)=\"_retry()\"\n >\n {{ _copy().retry }}\n </button>\n <button\n mlvButton\n type=\"button\"\n variant=\"secondary\"\n [attr.aria-label]=\"_removeLabel()\"\n [disabled]=\"_disabled()\"\n (click)=\"_remove()\"\n >\n {{ _removeLabel() }}\n </button>\n } @else {\n <button\n mlvButton\n type=\"button\"\n variant=\"primary\"\n [attr.aria-label]=\"_copy().title\"\n [disabled]=\"_submitDisabled()\"\n (click)=\"_submit()\"\n >\n {{ _copy().title }}\n </button>\n }\n </mlv-dialog-footer>\n</mlv-dialog>\n", styles: ["@layer mlv.components{.mlv-editor-image-upload-dialog{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor-image-upload-dialog *{box-sizing:border-box}.mlv-editor-image-upload-dialog__body{display:grid;gap:var(--mlv-spacing-2)}.mlv-editor-image-upload-dialog__preview{display:block;max-block-size:12rem;max-inline-size:100%;margin-inline:auto;border-radius:var(--mlv-radius-card);object-fit:contain}.mlv-editor-image-upload-dialog__error{margin:0;color:var(--mlv-text-negative)}.mlv-editor-image-upload-dialog__live{position:absolute;width:.0625rem;height:.0625rem;overflow:hidden;clip-path:inset(50%);white-space:nowrap}.mlv-editor-image-upload-status{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor-image-upload-status *{box-sizing:border-box}.mlv-editor-image-upload-status{display:block;padding:0 var(--mlv-spacing-4) var(--mlv-spacing-2)}.mlv-editor-image-upload-status__list{display:grid;gap:var(--mlv-spacing-1-5);padding:0;margin:0;list-style:none}.mlv-editor-image-upload-status__item{display:grid;grid-template-columns:minmax(0,1fr) minmax(8rem,2fr) auto auto;align-items:center;gap:var(--mlv-spacing-3)}.mlv-editor-image-upload-status__name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mlv-editor-image-upload-status__live{position:absolute;width:.0625rem;height:.0625rem;overflow:hidden;clip-path:inset(50%);white-space:nowrap}}\n"], dependencies: [{ kind: "component", type: MlvButton, selector: "button[mlvButton], a[mlvButton]", inputs: ["variant", "shape", "disabled", "loading", "selected"] }, { kind: "component", type: MlvDialog, selector: "mlv-dialog" }, { kind: "component", type: MlvDialogBody, selector: "mlv-dialog-body, [mlvDialogBody]" }, { kind: "component", type: MlvDialogFooter, selector: "mlv-dialog-footer, [mlvDialogFooter]", inputs: ["align"] }, { kind: "component", type: MlvDialogHeader, selector: "mlv-dialog-header, [mlvDialogHeader]", inputs: ["title", "closable"] }, { kind: "component", type: MlvFileUpload, selector: "mlv-file-upload", inputs: ["value", "accept", "maxSize", "multiple", "title", "subtitle", "actionLabel", "compact", "previewMode"], outputs: ["valueChange", "filesChange"] }, { kind: "component", type: MlvInput, selector: "mlv-input", inputs: ["type", "placeholder", "autocomplete", "inputMode", "maxLength", "minLength", "pattern", "name", "autofocus", "min", "max", "step", "bare", "projectControl", "role", "ariaExpanded", "ariaHasPopup", "ariaAutocomplete", "ariaControls", "ariaActiveDescendant", "ariaDescribedBy", "value"], outputs: ["valueChange", "inputFocus", "inputBlur"] }, { kind: "component", type: MlvProgress, selector: "mlv-progress", inputs: ["shape", "value", "size", "tone", "showPercentage", "ariaLabel"] }, { kind: "component", type: MlvSwitch, selector: "mlv-switch", inputs: ["ariaLabelledBy", "checked"], outputs: ["checkedChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
6827
7977
|
}
|
|
6828
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
7978
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorImageUploadDialog, decorators: [{
|
|
6829
7979
|
type: Component,
|
|
6830
7980
|
args: [{ selector: 'mlv-editor-image-upload-dialog', imports: [
|
|
6831
7981
|
MlvButton,
|
|
@@ -6837,7 +7987,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
|
|
|
6837
7987
|
MlvInput,
|
|
6838
7988
|
MlvProgress,
|
|
6839
7989
|
MlvSwitch,
|
|
6840
|
-
], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, host: { class: 'mlv-editor-image-upload-dialog' }, template: "<mlv-dialog>\n <mlv-dialog-header />\n <mlv-dialog-body>\n <div class=\"mlv-editor-image-upload-dialog__body\">\n <mlv-file-upload\n [accept]=\"_accept()\"\n [maxSize]=\"_maxSize()\"\n [multiple]=\"false\"\n [disabled]=\"_disabled() || !!_activeId()\"\n [value]=\"_files()\"\n (filesChange)=\"_onFilesChange($event)\"\n />\n\n @if (_file(); as selected) {
|
|
7990
|
+
], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, host: { class: 'mlv-editor-image-upload-dialog' }, template: "<mlv-dialog>\n <mlv-dialog-header />\n <mlv-dialog-body>\n <div class=\"mlv-editor-image-upload-dialog__body\">\n <mlv-file-upload\n [accept]=\"_accept()\"\n [maxSize]=\"_maxSize()\"\n [multiple]=\"false\"\n [disabled]=\"_disabled() || !!_activeId()\"\n [value]=\"_files()\"\n (filesChange)=\"_onFilesChange($event)\"\n />\n\n @if (_file(); as selected) {\n @if (selected.previewUrl) {\n <img\n class=\"mlv-editor-image-upload-dialog__preview\"\n [src]=\"selected.previewUrl\"\n [alt]=\"_previewAlt()\"\n />\n }\n @if (_item(); as item) {\n @if (item.status === 'uploading') {\n <mlv-progress\n [value]=\"item.progress\"\n [showPercentage]=\"true\"\n [ariaLabel]=\"_progressLabel(item.progress)\"\n />\n } @else {\n <p\n class=\"mlv-editor-image-upload-dialog__error\"\n role=\"alert\"\n aria-live=\"assertive\"\n >\n {{ _copy().failed }}\n </p>\n }\n }\n }\n\n <mlv-input\n [label]=\"_copy().alt\"\n [ariaLabel]=\"_copy().alt\"\n [value]=\"_alt()\"\n [disabled]=\"_disabled() || !!_activeId() || _decorative()\"\n [state]=\"_altInvalid() ? 'error' : 'default'\"\n [message]=\"_altInvalid() ? _copy().altRequired : ''\"\n (valueChange)=\"_alt.set($event)\"\n />\n <mlv-input\n [label]=\"_copy().imageTitle\"\n [ariaLabel]=\"_copy().imageTitle\"\n [value]=\"_title()\"\n [disabled]=\"_disabled() || !!_activeId() || _decorative()\"\n (valueChange)=\"_title.set($event)\"\n />\n <mlv-switch\n [ariaLabel]=\"_copy().decorative\"\n [checked]=\"_decorative()\"\n [disabled]=\"_disabled() || !!_activeId()\"\n (checkedChange)=\"_onDecorativeChange($event)\"\n >\n {{ _copy().decorative }}\n </mlv-switch>\n\n <span\n class=\"mlv-editor-image-upload-dialog__live\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n >{{ _announcement() }}</span\n >\n </div>\n </mlv-dialog-body>\n\n <mlv-dialog-footer>\n @if (_item()?.status === 'uploading') {\n <button\n mlvButton\n type=\"button\"\n variant=\"secondary\"\n [attr.aria-label]=\"_copy().cancel\"\n (click)=\"_cancel()\"\n >\n {{ _copy().cancel }}\n </button>\n } @else if (_item()?.status === 'failed') {\n <button\n mlvButton\n type=\"button\"\n variant=\"primary\"\n [attr.aria-label]=\"_copy().retry\"\n [disabled]=\"_disabled()\"\n (click)=\"_retry()\"\n >\n {{ _copy().retry }}\n </button>\n <button\n mlvButton\n type=\"button\"\n variant=\"secondary\"\n [attr.aria-label]=\"_removeLabel()\"\n [disabled]=\"_disabled()\"\n (click)=\"_remove()\"\n >\n {{ _removeLabel() }}\n </button>\n } @else {\n <button\n mlvButton\n type=\"button\"\n variant=\"primary\"\n [attr.aria-label]=\"_copy().title\"\n [disabled]=\"_submitDisabled()\"\n (click)=\"_submit()\"\n >\n {{ _copy().title }}\n </button>\n }\n </mlv-dialog-footer>\n</mlv-dialog>\n", styles: ["@layer mlv.components{.mlv-editor-image-upload-dialog{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor-image-upload-dialog *{box-sizing:border-box}.mlv-editor-image-upload-dialog__body{display:grid;gap:var(--mlv-spacing-2)}.mlv-editor-image-upload-dialog__preview{display:block;max-block-size:12rem;max-inline-size:100%;margin-inline:auto;border-radius:var(--mlv-radius-card);object-fit:contain}.mlv-editor-image-upload-dialog__error{margin:0;color:var(--mlv-text-negative)}.mlv-editor-image-upload-dialog__live{position:absolute;width:.0625rem;height:.0625rem;overflow:hidden;clip-path:inset(50%);white-space:nowrap}.mlv-editor-image-upload-status{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor-image-upload-status *{box-sizing:border-box}.mlv-editor-image-upload-status{display:block;padding:0 var(--mlv-spacing-4) var(--mlv-spacing-2)}.mlv-editor-image-upload-status__list{display:grid;gap:var(--mlv-spacing-1-5);padding:0;margin:0;list-style:none}.mlv-editor-image-upload-status__item{display:grid;grid-template-columns:minmax(0,1fr) minmax(8rem,2fr) auto auto;align-items:center;gap:var(--mlv-spacing-3)}.mlv-editor-image-upload-status__name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mlv-editor-image-upload-status__live{position:absolute;width:.0625rem;height:.0625rem;overflow:hidden;clip-path:inset(50%);white-space:nowrap}}\n"] }]
|
|
6841
7991
|
}], ctorParameters: () => [] });
|
|
6842
7992
|
|
|
6843
7993
|
/** Public toolbar entry point for the editor image-upload dialog. */
|
|
@@ -6931,10 +8081,10 @@ class MlvEditorImageUpload {
|
|
|
6931
8081
|
}
|
|
6932
8082
|
});
|
|
6933
8083
|
}
|
|
6934
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
6935
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.
|
|
8084
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorImageUpload, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
8085
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.1.7", type: MlvEditorImageUpload, isStandalone: true, selector: "mlv-editor-image-upload", host: { properties: { "hidden": "!_visible()" }, classAttribute: "mlv-editor-image-upload" }, viewQueries: [{ propertyName: "_trigger", first: true, predicate: ["_trigger"], descendants: true, read: ElementRef, isSignal: true }], ngImport: i0, template: "<button\n #_trigger\n mlvButton\n mlvEditorToolbarWidget\n type=\"button\"\n shape=\"square\"\n mlvDensity=\"tight\"\n variant=\"transparent\"\n [disabled]=\"_disabled()\"\n [attr.aria-label]=\"_copy()\"\n [attr.aria-pressed]=\"false\"\n [mlvTooltip]=\"_copy()\"\n (click)=\"_open()\"\n>\n <svg mlvButtonIcon lucideImage aria-hidden=\"true\" />\n</button>\n", dependencies: [{ kind: "component", type: LucideImage, selector: "svg[lucideImage]" }, { kind: "component", type: MlvButton, selector: "button[mlvButton], a[mlvButton]", inputs: ["variant", "shape", "disabled", "loading", "selected"] }, { kind: "directive", type: MlvButtonIcon, selector: "[mlvButtonIcon]" }, { kind: "directive", type: MlvTooltip, selector: "[mlvTooltip]", inputs: ["mlvTooltip", "tooltipPlacement", "tooltipTone", "tooltipDelay", "tooltipDisabled", "tooltipArrow"] }, { kind: "directive", type: MlvEditorToolbarWidget, selector: "[mlvEditorToolbarWidget]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
6936
8086
|
}
|
|
6937
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
8087
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorImageUpload, decorators: [{
|
|
6938
8088
|
type: Component,
|
|
6939
8089
|
args: [{ selector: 'mlv-editor-image-upload', imports: [
|
|
6940
8090
|
LucideImage,
|
|
@@ -6966,6 +8116,11 @@ class MlvEditorImageUploadStatus {
|
|
|
6966
8116
|
...(ngDevMode ? [{ debugName: "_announcement" }] : /* istanbul ignore next */ []));
|
|
6967
8117
|
/** @private Last announced progress bucket for each active item. */
|
|
6968
8118
|
_buckets = new Map();
|
|
8119
|
+
/**
|
|
8120
|
+
* @private Upload whose progress or failure the live region currently
|
|
8121
|
+
* reads, or `null` when it reads a terminal announcement (or nothing).
|
|
8122
|
+
*/
|
|
8123
|
+
_announcedId = null;
|
|
6969
8124
|
/** @protected Whether status actions are inert. */
|
|
6970
8125
|
_disabled = computed(() => this._context.disabled() || this._context.readonly(), /* @ts-ignore */
|
|
6971
8126
|
...(ngDevMode ? [{ debugName: "_disabled" }] : /* istanbul ignore next */ []));
|
|
@@ -6985,7 +8140,7 @@ class MlvEditorImageUploadStatus {
|
|
|
6985
8140
|
const pending = this._pending();
|
|
6986
8141
|
for (const item of pending) {
|
|
6987
8142
|
if (item.status === 'failed') {
|
|
6988
|
-
this.
|
|
8143
|
+
this._announce(this._copy().failed, item.id);
|
|
6989
8144
|
this._buckets.delete(item.id);
|
|
6990
8145
|
continue;
|
|
6991
8146
|
}
|
|
@@ -6993,16 +8148,22 @@ class MlvEditorImageUploadStatus {
|
|
|
6993
8148
|
if (this._buckets.get(item.id) === bucket)
|
|
6994
8149
|
continue;
|
|
6995
8150
|
this._buckets.set(item.id, bucket);
|
|
6996
|
-
this.
|
|
8151
|
+
this._announce(this._progressLabel(bucket), item.id);
|
|
6997
8152
|
}
|
|
6998
8153
|
});
|
|
6999
8154
|
const stopTerminalEvents = this._coordinator.onTerminal(({ id, reason }) => {
|
|
7000
8155
|
this._buckets.delete(id);
|
|
7001
8156
|
if (reason === 'success' && !this._coordinator.hasDialogOwner(id)) {
|
|
7002
|
-
this.
|
|
8157
|
+
this._announce(this._fileI18n().uploadComplete, null);
|
|
7003
8158
|
}
|
|
7004
8159
|
else if (reason === 'cancelled') {
|
|
7005
|
-
this.
|
|
8160
|
+
this._announce(this._copy().cancel, null);
|
|
8161
|
+
}
|
|
8162
|
+
else if (this._announcedId === id) {
|
|
8163
|
+
// A dialog-owned success (the dialog announces it), a lifecycle
|
|
8164
|
+
// abort or a removed failure: the ended upload's progress or
|
|
8165
|
+
// failure text must not outlive it. Another upload's text stays.
|
|
8166
|
+
this._announce('', null);
|
|
7006
8167
|
}
|
|
7007
8168
|
});
|
|
7008
8169
|
inject(DestroyRef).onDestroy(stopTerminalEvents);
|
|
@@ -7011,6 +8172,11 @@ class MlvEditorImageUploadStatus {
|
|
|
7011
8172
|
_progressLabel(progress) {
|
|
7012
8173
|
return this._copy().progress.replace('{progress}', String(Math.floor(progress / 10) * 10));
|
|
7013
8174
|
}
|
|
8175
|
+
/** @private Writes the live region and records which upload it describes. */
|
|
8176
|
+
_announce(text, id) {
|
|
8177
|
+
this._announcement.set(text);
|
|
8178
|
+
this._announcedId = id;
|
|
8179
|
+
}
|
|
7014
8180
|
/** @protected Labels a remove action without exposing a bare icon. */
|
|
7015
8181
|
_removeLabel(name) {
|
|
7016
8182
|
return this._fileI18n().removeFile.replace('{name}', name);
|
|
@@ -7019,8 +8185,8 @@ class MlvEditorImageUploadStatus {
|
|
|
7019
8185
|
_cancel(id) {
|
|
7020
8186
|
this._coordinator.cancel(id);
|
|
7021
8187
|
}
|
|
7022
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
7023
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.
|
|
8188
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorImageUploadStatus, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
8189
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: MlvEditorImageUploadStatus, isStandalone: true, selector: "mlv-editor-image-upload-status", host: { classAttribute: "mlv-editor-image-upload-status" }, ngImport: i0, template: `
|
|
7024
8190
|
@if (_pending().length) {
|
|
7025
8191
|
<ul class="mlv-editor-image-upload-status__list">
|
|
7026
8192
|
@for (item of _pending(); track item.id) {
|
|
@@ -7080,7 +8246,7 @@ class MlvEditorImageUploadStatus {
|
|
|
7080
8246
|
>
|
|
7081
8247
|
`, isInline: true, styles: ["@layer mlv.components{.mlv-editor-image-upload-dialog{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor-image-upload-dialog *{box-sizing:border-box}.mlv-editor-image-upload-dialog__body{display:grid;gap:var(--mlv-spacing-2)}.mlv-editor-image-upload-dialog__preview{display:block;max-block-size:12rem;max-inline-size:100%;margin-inline:auto;border-radius:var(--mlv-radius-card);object-fit:contain}.mlv-editor-image-upload-dialog__error{margin:0;color:var(--mlv-text-negative)}.mlv-editor-image-upload-dialog__live{position:absolute;width:.0625rem;height:.0625rem;overflow:hidden;clip-path:inset(50%);white-space:nowrap}.mlv-editor-image-upload-status{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor-image-upload-status *{box-sizing:border-box}.mlv-editor-image-upload-status{display:block;padding:0 var(--mlv-spacing-4) var(--mlv-spacing-2)}.mlv-editor-image-upload-status__list{display:grid;gap:var(--mlv-spacing-1-5);padding:0;margin:0;list-style:none}.mlv-editor-image-upload-status__item{display:grid;grid-template-columns:minmax(0,1fr) minmax(8rem,2fr) auto auto;align-items:center;gap:var(--mlv-spacing-3)}.mlv-editor-image-upload-status__name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mlv-editor-image-upload-status__live{position:absolute;width:.0625rem;height:.0625rem;overflow:hidden;clip-path:inset(50%);white-space:nowrap}}\n"], dependencies: [{ kind: "component", type: MlvButton, selector: "button[mlvButton], a[mlvButton]", inputs: ["variant", "shape", "disabled", "loading", "selected"] }, { kind: "component", type: MlvProgress, selector: "mlv-progress", inputs: ["shape", "value", "size", "tone", "showPercentage", "ariaLabel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
7082
8248
|
}
|
|
7083
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
8249
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorImageUploadStatus, decorators: [{
|
|
7084
8250
|
type: Component,
|
|
7085
8251
|
args: [{ selector: 'mlv-editor-image-upload-status', imports: [MlvButton, MlvProgress], template: `
|
|
7086
8252
|
@if (_pending().length) {
|
|
@@ -7150,6 +8316,18 @@ class MlvEditorTable {
|
|
|
7150
8316
|
_context = inject(MLV_EDITOR_TOOLBAR_CONTEXT);
|
|
7151
8317
|
/** @private Normalizes horizontal grid movement for RTL. */
|
|
7152
8318
|
_rtlService = inject(MlvRtlService);
|
|
8319
|
+
/**
|
|
8320
|
+
* @private Host element; the scope horizontal arrow keys resolve their
|
|
8321
|
+
* direction against. The grid renders in a popup pane portaled out of this
|
|
8322
|
+
* subtree, so the handler speaks for the host, never for `event.target`.
|
|
8323
|
+
*/
|
|
8324
|
+
_elementRef = inject((ElementRef));
|
|
8325
|
+
/**
|
|
8326
|
+
* @private Direction applying to this control, resolved once and cached
|
|
8327
|
+
* behind the shared `dir` observer rather than re-walked on every arrow
|
|
8328
|
+
* keypress.
|
|
8329
|
+
*/
|
|
8330
|
+
_direction = this._rtlService.elementDirection(this._elementRef);
|
|
7153
8331
|
/** @private Transaction and selection invalidation bridge. */
|
|
7154
8332
|
_revision = inject(MLV_EDITOR_TOOLBAR_REVISION, {
|
|
7155
8333
|
optional: true,
|
|
@@ -7403,7 +8581,11 @@ class MlvEditorTable {
|
|
|
7403
8581
|
_onGridKeydown(event) {
|
|
7404
8582
|
const current = this._selectedSize();
|
|
7405
8583
|
let next = current;
|
|
7406
|
-
|
|
8584
|
+
// Columns run along the inline axis, resolved against this control's own
|
|
8585
|
+
// host rather than the document, so a scoped `dir` subtree — or a CDK
|
|
8586
|
+
// overlay pane, which carries its own `dir` — mirrors the pair correctly.
|
|
8587
|
+
switch (this._rtlService.normalizeArrowKey(event, this._direction()) ??
|
|
8588
|
+
event.key) {
|
|
7407
8589
|
case LEFT_ARROW:
|
|
7408
8590
|
next = { ...current, cols: Math.max(1, current.cols - 1) };
|
|
7409
8591
|
break;
|
|
@@ -7446,10 +8628,10 @@ class MlvEditorTable {
|
|
|
7446
8628
|
const index = (selected.rows - 1) * TABLE_GRID_BOUNDARY + (selected.cols - 1);
|
|
7447
8629
|
this._gridCells()[index]?.nativeElement.focus();
|
|
7448
8630
|
}
|
|
7449
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
7450
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: MlvEditorTable, isStandalone: true, selector: "mlv-editor-table", host: { properties: { "hidden": "!_supported()" }, classAttribute: "mlv-editor-table" }, viewQueries: [{ propertyName: "_popup", first: true, predicate: ["_popup"], descendants: true, isSignal: true }, { propertyName: "_trigger", first: true, predicate: ["_trigger"], descendants: true, isSignal: true }, { propertyName: "_menu", first: true, predicate: ["_menu"], descendants: true, isSignal: true }, { propertyName: "_menuTrigger", first: true, predicate: MlvMenuTrigger, descendants: true, isSignal: true }, { propertyName: "_triggerElement", first: true, predicate: ["_triggerElement"], descendants: true, read: ElementRef, isSignal: true }, { propertyName: "_panel", first: true, predicate: ["_panel"], descendants: true, isSignal: true }, { propertyName: "_firstAction", first: true, predicate: ["_firstAction"], descendants: true, read: ElementRef, isSignal: true }, { propertyName: "_gridCells", predicate: ["_gridCell"], descendants: true, read: ElementRef, isSignal: true }], ngImport: i0, template: "@if (_active()) {\n <button\n #_triggerElement\n mlvButton\n mlvEditorToolbarWidget\n type=\"button\"\n shape=\"square\"\n mlvDensity=\"tight\"\n variant=\"transparent\"\n selected\n [disabled]=\"_disabled()\"\n [mlvMenuTrigger]=\"_menu\"\n [menuTriggerDisabled]=\"_disabled()\"\n [attr.aria-label]=\"_copy().table\"\n [mlvTooltip]=\"_copy().table\"\n (menuOpened)=\"_registerMenuOverlay()\"\n (menuClosed)=\"_unregisterMenuOverlay()\"\n >\n <svg mlvButtonIcon lucideTable2 aria-hidden=\"true\" />\n </button>\n\n <mlv-menu #_menu [label]=\"_copy().table\">\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_commandDisabled(_canAddRowBefore)\"\n (itemClick)=\"_run(_addRowBefore)\"\n >\n {{ _copy().addRowBefore }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_commandDisabled(_canAddRowAfter)\"\n (itemClick)=\"_run(_addRowAfter)\"\n >\n {{ _copy().addRowAfter }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_commandDisabled(_canDeleteRow)\"\n (itemClick)=\"_run(_deleteRow)\"\n >\n {{ _copy().deleteRow }}\n </mlv-list-item>\n\n <mlv-divider muted />\n\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_commandDisabled(_canAddColumnBefore)\"\n (itemClick)=\"_run(_addColumnBefore)\"\n >\n {{ _copy().addColumnBefore }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_commandDisabled(_canAddColumnAfter)\"\n (itemClick)=\"_run(_addColumnAfter)\"\n >\n {{ _copy().addColumnAfter }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_commandDisabled(_canDeleteColumn)\"\n (itemClick)=\"_run(_deleteColumn)\"\n >\n {{ _copy().deleteColumn }}\n </mlv-list-item>\n\n <mlv-divider muted />\n\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_commandDisabled(_canMergeCells)\"\n (itemClick)=\"_run(_mergeCells)\"\n >\n {{ _copy().mergeCells }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_commandDisabled(_canSplitCell)\"\n (itemClick)=\"_run(_splitCell)\"\n >\n {{ _copy().splitCell }}\n </mlv-list-item>\n\n <mlv-divider muted />\n\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_commandDisabled(_canToggleHeaderRow)\"\n (itemClick)=\"_run(_toggleHeaderRow)\"\n >\n {{ _copy().toggleHeaderRow }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_commandDisabled(_canToggleHeaderColumn)\"\n (itemClick)=\"_run(_toggleHeaderColumn)\"\n >\n {{ _copy().toggleHeaderColumn }}\n </mlv-list-item>\n <!-- `aria-current` marks the state the caret's cell is already in; the row\n paints itself from the shared selected pair. -->\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_commandDisabled(_canToggleHeaderCell)\"\n [attr.aria-current]=\"_isHeaderCell() ? 'true' : null\"\n (itemClick)=\"_run(_toggleHeaderCell)\"\n >\n {{ _copy().toggleHeaderCell }}\n </mlv-list-item>\n\n <mlv-divider muted />\n\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_commandDisabled(_canDeleteTable)\"\n (itemClick)=\"_run(_deleteTable, true)\"\n >\n {{ _copy().deleteTable }}\n </mlv-list-item>\n </mlv-menu>\n} @else {\n <button\n #_trigger=\"mlvPopupTrigger\"\n #_triggerElement\n mlvButton\n mlvEditorToolbarWidget\n type=\"button\"\n shape=\"square\"\n mlvDensity=\"tight\"\n variant=\"transparent\"\n [disabled]=\"_disabled()\"\n [mlvPopupTrigger]=\"_popup\"\n [ariaHasPopup]=\"'dialog'\"\n [attr.aria-label]=\"_copy().table\"\n [mlvTooltip]=\"_copy().table\"\n >\n <svg mlvButtonIcon lucideTable2 aria-hidden=\"true\" />\n </button>\n\n <mlv-popup\n #_popup\n panelRole=\"dialog\"\n [modal]=\"true\"\n [ariaLabel]=\"_copy().table\"\n (afterOpened)=\"_onOpened()\"\n (afterClosed)=\"_onClosed()\"\n >\n <ng-template mlvPopupContent>\n <div #_panel class=\"mlv-editor-table__panel\">\n <div\n class=\"mlv-editor-table__grid\"\n role=\"grid\"\n tabindex=\"-1\"\n [attr.aria-label]=\"_copy().insertTable\"\n (keydown)=\"_onGridKeydown($event)\"\n >\n @for (row of _gridRows; track $index) {\n <div class=\"mlv-editor-table__grid-row\" role=\"row\">\n @for (size of row; track size.cols) {\n <button\n #_gridCell\n class=\"mlv-editor-table__grid-cell\"\n type=\"button\"\n role=\"gridcell\"\n [class.mlv-editor-table__grid-cell--selected]=\"\n _sizeSelected(size)\n \"\n [attr.aria-label]=\"_sizeLabel(size)\"\n [attr.aria-selected]=\"_sizeSelected(size)\"\n [attr.tabindex]=\"\n size.rows === _selectedSize().rows &&\n size.cols === _selectedSize().cols\n ? 0\n : -1\n \"\n [disabled]=\"_disabled()\"\n (focus)=\"_selectSize(size)\"\n (mouseenter)=\"_selectSize(size)\"\n (click)=\"_insertSize(size)\"\n ></button>\n }\n </div>\n }\n </div>\n\n <div class=\"mlv-editor-table__footer\">\n <span class=\"mlv-editor-table__size\" aria-hidden=\"true\">\n {{ _selectedSize().rows }} \u00D7 {{ _selectedSize().cols }}\n </span>\n <mlv-button-toggle\n [pressed]=\"_withHeaderRow()\"\n [disabled]=\"_disabled()\"\n (pressedChange)=\"_withHeaderRow.set($event)\"\n >\n {{ _copy().toggleHeaderRow }}\n </mlv-button-toggle>\n </div>\n </div>\n </ng-template>\n </mlv-popup>\n}\n", styles: ["@layer mlv.components{.mlv-editor-table{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor-table *{box-sizing:border-box}.mlv-editor-table{display:inline-flex}.mlv-editor-table__panel{display:grid;width:max-content;max-width:calc(100vw - 3.5rem);padding:var(--mlv-popover-inset);gap:var(--mlv-spacing-2)}.mlv-editor-table__actions{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));width:min(24rem,100vw - 4rem);gap:var(--mlv-spacing-2)}.mlv-editor-table__grid{display:grid;grid-template-columns:1fr;gap:.125rem}.mlv-editor-table__grid-row{display:grid;grid-template-columns:repeat(10,1.25rem);gap:.125rem}.mlv-editor-table__grid-cell{appearance:none;box-sizing:border-box;inline-size:1.25rem;block-size:1.25rem;margin:0;padding:0;border:var(--mlv-stroke-width) solid var(--mlv-border-normal);border-radius:var(--mlv-radius-xs);background:var(--mlv-background-base);cursor:pointer;transition:background-color var(--mlv-duration-fast) var(--mlv-ease-default),border-color var(--mlv-duration-fast) var(--mlv-ease-default)}.mlv-editor-table__grid-cell--selected{border-color:var(--mlv-border-focus);background:var(--mlv-background-selected)}.mlv-editor-table__grid-cell:disabled{cursor:default;opacity:var(--mlv-disabled-opacity)}.mlv-editor-table__grid-cell:focus-visible{outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:var(--mlv-focus-ring-offset)}.mlv-editor-table__footer{display:flex;align-items:center;justify-content:space-between;gap:var(--mlv-spacing-3)}.mlv-editor-table__size{color:var(--mlv-text-secondary);font-size:var(--mlv-typography-body-s-size);font-variant-numeric:tabular-nums;white-space:nowrap}@media(prefers-reduced-motion:reduce){.mlv-editor-table,.mlv-editor-table *,.mlv-editor-table:before,.mlv-editor-table:after,[class^=mlv-editor-table__],[class*=\" mlv-editor-table__\"],[class^=mlv-editor-table--],[class*=\" mlv-editor-table--\"]{scroll-behavior:auto!important;animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:var(--mlv-duration-instant)!important}}}\n"], dependencies: [{ kind: "component", type: MlvButton, selector: "button[mlvButton], a[mlvButton]", inputs: ["variant", "shape", "disabled", "loading", "selected"] }, { kind: "directive", type: MlvButtonIcon, selector: "[mlvButtonIcon]" }, { kind: "component", type: MlvButtonToggle, selector: "mlv-button-toggle", inputs: ["pressed", "disabled", "variant", "shape", "mlvDensity", "ariaLabel", "ariaLabelledBy"], outputs: ["pressedChange"] }, { kind: "directive", type: MlvTooltip, selector: "[mlvTooltip]", inputs: ["mlvTooltip", "tooltipPlacement", "tooltipTone", "tooltipDelay", "tooltipDisabled", "tooltipArrow"] }, { kind: "component", type: LucideTable2, selector: "svg[lucideTable2]" }, { kind: "directive", type: MlvEditorToolbarWidget, selector: "[mlvEditorToolbarWidget]" }, { kind: "component", type: MlvPopup, selector: "mlv-popup", inputs: ["position", "positions", "width", "height", "minWidth", "minHeight", "maxWidth", "maxHeight", "hasArrow", "hasShadow", "class", "mlvDensity", "flexibleDimensions", "scrollStrategy", "hasBackdrop", "restrictPosition", "panelRole", "modal", "ariaLabel", "dismissExcludeElements", "mobileMode", "mobileBreakpoint", "mobileTitle", "mobileCloseLabel", "opened"], outputs: ["openedChange", "afterOpened", "afterClosed"] }, { kind: "directive", type: MlvPopupContent, selector: "[mlvPopupContent]" }, { kind: "directive", type: MlvPopupTrigger, selector: "[mlvPopupTrigger]", inputs: ["mlvPopupTrigger", "triggerOn", "ariaHasPopup"], exportAs: ["mlvPopupTrigger"] }, { kind: "component", type: MlvDivider, selector: "mlv-divider", inputs: ["orientation", "dashed", "muted"] }, { kind: "component", type: MlvListItem, selector: "mlv-list-item", inputs: ["itemRole", "unread", "accent"] }, { kind: "component", type: MlvMenu, selector: "mlv-menu", inputs: ["label", "mlvDensity", "dataSource"], outputs: ["closed"] }, { kind: "directive", type: MlvMenuItem, selector: "mlv-list-item[mlvMenuItem]", inputs: ["disabled"], outputs: ["itemClick"] }, { kind: "directive", type: MlvMenuTrigger, selector: "[mlvMenuTrigger]", inputs: ["mlvMenuTrigger", "isSubmenuTrigger", "menuTriggerDisabled"], outputs: ["menuOpened", "menuClosed"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
8631
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorTable, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
8632
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: MlvEditorTable, isStandalone: true, selector: "mlv-editor-table", host: { properties: { "hidden": "!_supported()" }, classAttribute: "mlv-editor-table" }, viewQueries: [{ propertyName: "_popup", first: true, predicate: ["_popup"], descendants: true, isSignal: true }, { propertyName: "_trigger", first: true, predicate: ["_trigger"], descendants: true, isSignal: true }, { propertyName: "_menu", first: true, predicate: ["_menu"], descendants: true, isSignal: true }, { propertyName: "_menuTrigger", first: true, predicate: MlvMenuTrigger, descendants: true, isSignal: true }, { propertyName: "_triggerElement", first: true, predicate: ["_triggerElement"], descendants: true, read: ElementRef, isSignal: true }, { propertyName: "_panel", first: true, predicate: ["_panel"], descendants: true, isSignal: true }, { propertyName: "_firstAction", first: true, predicate: ["_firstAction"], descendants: true, read: ElementRef, isSignal: true }, { propertyName: "_gridCells", predicate: ["_gridCell"], descendants: true, read: ElementRef, isSignal: true }], ngImport: i0, template: "@if (_active()) {\n <button\n #_triggerElement\n mlvButton\n mlvEditorToolbarWidget\n type=\"button\"\n shape=\"square\"\n mlvDensity=\"tight\"\n variant=\"transparent\"\n selected\n [disabled]=\"_disabled()\"\n [mlvMenuTrigger]=\"_menu\"\n [menuTriggerDisabled]=\"_disabled()\"\n [attr.aria-label]=\"_copy().table\"\n [mlvTooltip]=\"_copy().table\"\n (menuOpened)=\"_registerMenuOverlay()\"\n (menuClosed)=\"_unregisterMenuOverlay()\"\n >\n <svg mlvButtonIcon lucideTable2 aria-hidden=\"true\" />\n </button>\n\n <mlv-menu #_menu [label]=\"_copy().table\">\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_commandDisabled(_canAddRowBefore)\"\n (itemClick)=\"_run(_addRowBefore)\"\n >\n {{ _copy().addRowBefore }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_commandDisabled(_canAddRowAfter)\"\n (itemClick)=\"_run(_addRowAfter)\"\n >\n {{ _copy().addRowAfter }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_commandDisabled(_canDeleteRow)\"\n (itemClick)=\"_run(_deleteRow)\"\n >\n {{ _copy().deleteRow }}\n </mlv-list-item>\n\n <mlv-divider muted />\n\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_commandDisabled(_canAddColumnBefore)\"\n (itemClick)=\"_run(_addColumnBefore)\"\n >\n {{ _copy().addColumnBefore }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_commandDisabled(_canAddColumnAfter)\"\n (itemClick)=\"_run(_addColumnAfter)\"\n >\n {{ _copy().addColumnAfter }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_commandDisabled(_canDeleteColumn)\"\n (itemClick)=\"_run(_deleteColumn)\"\n >\n {{ _copy().deleteColumn }}\n </mlv-list-item>\n\n <mlv-divider muted />\n\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_commandDisabled(_canMergeCells)\"\n (itemClick)=\"_run(_mergeCells)\"\n >\n {{ _copy().mergeCells }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_commandDisabled(_canSplitCell)\"\n (itemClick)=\"_run(_splitCell)\"\n >\n {{ _copy().splitCell }}\n </mlv-list-item>\n\n <mlv-divider muted />\n\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_commandDisabled(_canToggleHeaderRow)\"\n (itemClick)=\"_run(_toggleHeaderRow)\"\n >\n {{ _copy().toggleHeaderRow }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_commandDisabled(_canToggleHeaderColumn)\"\n (itemClick)=\"_run(_toggleHeaderColumn)\"\n >\n {{ _copy().toggleHeaderColumn }}\n </mlv-list-item>\n <!-- `aria-current` marks the state the caret's cell is already in; the row\n paints itself from the shared selected pair. -->\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_commandDisabled(_canToggleHeaderCell)\"\n [attr.aria-current]=\"_isHeaderCell() ? 'true' : null\"\n (itemClick)=\"_run(_toggleHeaderCell)\"\n >\n {{ _copy().toggleHeaderCell }}\n </mlv-list-item>\n\n <mlv-divider muted />\n\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_commandDisabled(_canDeleteTable)\"\n (itemClick)=\"_run(_deleteTable, true)\"\n >\n {{ _copy().deleteTable }}\n </mlv-list-item>\n </mlv-menu>\n} @else {\n <button\n #_trigger=\"mlvPopupTrigger\"\n #_triggerElement\n mlvButton\n mlvEditorToolbarWidget\n type=\"button\"\n shape=\"square\"\n mlvDensity=\"tight\"\n variant=\"transparent\"\n [disabled]=\"_disabled()\"\n [mlvPopupTrigger]=\"_popup\"\n [ariaHasPopup]=\"'dialog'\"\n [attr.aria-label]=\"_copy().table\"\n [mlvTooltip]=\"_copy().table\"\n >\n <svg mlvButtonIcon lucideTable2 aria-hidden=\"true\" />\n </button>\n\n <mlv-popup\n #_popup\n panelRole=\"dialog\"\n [modal]=\"true\"\n [ariaLabel]=\"_copy().table\"\n (afterOpened)=\"_onOpened()\"\n (afterClosed)=\"_onClosed()\"\n >\n <ng-template mlvPopupContent>\n <div #_panel class=\"mlv-editor-table__panel\">\n <div\n class=\"mlv-editor-table__grid\"\n role=\"grid\"\n tabindex=\"-1\"\n [attr.aria-label]=\"_copy().insertTable\"\n (keydown)=\"_onGridKeydown($event)\"\n >\n @for (row of _gridRows; track $index) {\n <div class=\"mlv-editor-table__grid-row\" role=\"row\">\n @for (size of row; track size.cols) {\n <button\n #_gridCell\n class=\"mlv-editor-table__grid-cell\"\n type=\"button\"\n role=\"gridcell\"\n [class.mlv-editor-table__grid-cell--selected]=\"\n _sizeSelected(size)\n \"\n [attr.aria-label]=\"_sizeLabel(size)\"\n [attr.aria-selected]=\"_sizeSelected(size)\"\n [attr.tabindex]=\"\n size.rows === _selectedSize().rows &&\n size.cols === _selectedSize().cols\n ? 0\n : -1\n \"\n [disabled]=\"_disabled()\"\n (focus)=\"_selectSize(size)\"\n (mouseenter)=\"_selectSize(size)\"\n (click)=\"_insertSize(size)\"\n ></button>\n }\n </div>\n }\n </div>\n\n <div class=\"mlv-editor-table__footer\">\n <span class=\"mlv-editor-table__size\" aria-hidden=\"true\">\n {{ _selectedSize().rows }} \u00D7 {{ _selectedSize().cols }}\n </span>\n <mlv-button-toggle\n [pressed]=\"_withHeaderRow()\"\n [disabled]=\"_disabled()\"\n (pressedChange)=\"_withHeaderRow.set($event)\"\n >\n {{ _copy().toggleHeaderRow }}\n </mlv-button-toggle>\n </div>\n </div>\n </ng-template>\n </mlv-popup>\n}\n", styles: ["@layer mlv.components{.mlv-editor-table{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor-table *{box-sizing:border-box}.mlv-editor-table{display:inline-flex}.mlv-editor-table__panel{display:grid;width:max-content;max-width:calc(100vw - 3.5rem);padding:var(--mlv-popover-inset);gap:var(--mlv-spacing-2)}.mlv-editor-table__actions{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));width:min(24rem,100vw - 4rem);gap:var(--mlv-spacing-2)}.mlv-editor-table__grid{display:grid;grid-template-columns:1fr;gap:.125rem}.mlv-editor-table__grid-row{display:grid;grid-template-columns:repeat(10,1.25rem);gap:.125rem}.mlv-editor-table__grid-cell{appearance:none;box-sizing:border-box;inline-size:1.25rem;block-size:1.25rem;margin:0;padding:0;border:var(--mlv-stroke-width) solid var(--mlv-border-normal);border-radius:var(--mlv-radius-xs);background:var(--mlv-background-base);cursor:pointer;transition:background-color var(--mlv-duration-fast) var(--mlv-ease-default),border-color var(--mlv-duration-fast) var(--mlv-ease-default)}.mlv-editor-table__grid-cell--selected{border-color:var(--mlv-border-focus);background:var(--mlv-background-selected)}.mlv-editor-table__grid-cell:disabled{cursor:default;opacity:var(--mlv-disabled-opacity)}.mlv-editor-table__grid-cell:focus-visible{outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:var(--mlv-focus-ring-offset)}.mlv-editor-table__footer{display:flex;align-items:center;justify-content:space-between;gap:var(--mlv-spacing-3)}.mlv-editor-table__size{color:var(--mlv-text-secondary);font-size:var(--mlv-typography-body-s-size);font-variant-numeric:tabular-nums;white-space:nowrap}@media(prefers-reduced-motion:reduce){.mlv-editor-table,.mlv-editor-table *,.mlv-editor-table:before,.mlv-editor-table:after,[class^=mlv-editor-table__],[class*=\" mlv-editor-table__\"],[class^=mlv-editor-table--],[class*=\" mlv-editor-table--\"]{scroll-behavior:auto!important;animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:var(--mlv-duration-instant)!important}}}\n"], dependencies: [{ kind: "component", type: MlvButton, selector: "button[mlvButton], a[mlvButton]", inputs: ["variant", "shape", "disabled", "loading", "selected"] }, { kind: "directive", type: MlvButtonIcon, selector: "[mlvButtonIcon]" }, { kind: "component", type: MlvButtonToggle, selector: "mlv-button-toggle", inputs: ["pressed", "disabled", "variant", "shape", "mlvDensity", "ariaLabel", "ariaLabelledBy"], outputs: ["pressedChange"] }, { kind: "directive", type: MlvTooltip, selector: "[mlvTooltip]", inputs: ["mlvTooltip", "tooltipPlacement", "tooltipTone", "tooltipDelay", "tooltipDisabled", "tooltipArrow"] }, { kind: "component", type: LucideTable2, selector: "svg[lucideTable2]" }, { kind: "directive", type: MlvEditorToolbarWidget, selector: "[mlvEditorToolbarWidget]" }, { kind: "component", type: MlvPopup, selector: "mlv-popup", inputs: ["position", "positions", "width", "height", "minWidth", "minHeight", "maxWidth", "maxHeight", "hasArrow", "hasShadow", "class", "mlvDensity", "flexibleDimensions", "scrollStrategy", "hasBackdrop", "restrictPosition", "panelRole", "modal", "ariaLabel", "dismissExcludeElements", "mobileMode", "mobileBreakpoint", "mobileTitle", "mobileCloseLabel", "opened"], outputs: ["openedChange", "afterOpened", "afterClosed"] }, { kind: "directive", type: MlvPopupContent, selector: "[mlvPopupContent]" }, { kind: "directive", type: MlvPopupTrigger, selector: "[mlvPopupTrigger]", inputs: ["mlvPopupTrigger", "triggerOn", "ariaHasPopup"], exportAs: ["mlvPopupTrigger"] }, { kind: "component", type: MlvDivider, selector: "mlv-divider", inputs: ["orientation", "dashed", "muted"] }, { kind: "component", type: MlvListItem, selector: "mlv-list-item", inputs: ["itemRole", "unread", "accent"] }, { kind: "component", type: MlvMenu, selector: "mlv-menu", inputs: ["label", "mlvDensity", "dataSource"], outputs: ["closed"] }, { kind: "directive", type: MlvMenuItem, selector: "mlv-list-item[mlvMenuItem]", inputs: ["disabled"], outputs: ["itemClick"] }, { kind: "directive", type: MlvMenuTrigger, selector: "[mlvMenuTrigger]", inputs: ["mlvMenuTrigger", "isSubmenuTrigger", "menuTriggerDisabled"], outputs: ["menuOpened", "menuClosed"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
7451
8633
|
}
|
|
7452
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
8634
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorTable, decorators: [{
|
|
7453
8635
|
type: Component,
|
|
7454
8636
|
args: [{ selector: 'mlv-editor-table', imports: [
|
|
7455
8637
|
MlvButton,
|
|
@@ -7632,21 +8814,24 @@ class MlvEditorTableControls {
|
|
|
7632
8814
|
...(ngDevMode ? [{ debugName: "_enabled" }] : /* istanbul ignore next */ []));
|
|
7633
8815
|
constructor() {
|
|
7634
8816
|
const destroyRef = inject(DestroyRef);
|
|
7635
|
-
|
|
7636
|
-
const onPointerLeave = () => this._release();
|
|
7637
|
-
// Registered on the document rather than on the editor DOM: the ProseMirror
|
|
8817
|
+
// Subscribed on the document rather than on the editor DOM: the ProseMirror
|
|
7638
8818
|
// element is replaced whenever the editor is recreated (a format switch, an
|
|
7639
8819
|
// extension-set change), and a listener bound to the old node would go
|
|
7640
|
-
// quiet without ever erroring. Capture is not needed — the events bubble
|
|
7641
|
-
|
|
7642
|
-
|
|
7643
|
-
|
|
7644
|
-
|
|
7645
|
-
|
|
7646
|
-
|
|
7647
|
-
|
|
7648
|
-
|
|
7649
|
-
|
|
8820
|
+
// quiet without ever erroring. Capture is not needed — the events bubble,
|
|
8821
|
+
// and `passive` is carried through because neither handler calls
|
|
8822
|
+
// `preventDefault()`.
|
|
8823
|
+
//
|
|
8824
|
+
// The *injected* document, never the ambient global: this component is
|
|
8825
|
+
// constructed during server rendering too, where the two are different
|
|
8826
|
+
// objects, so the bare global would bind a per-render component to a
|
|
8827
|
+
// process-wide document that no teardown reaches.
|
|
8828
|
+
fromEvent(this._document, 'pointermove', { passive: true })
|
|
8829
|
+
.pipe(takeUntilDestroyed(destroyRef))
|
|
8830
|
+
.subscribe((event) => this._track(event));
|
|
8831
|
+
fromEvent(this._document, 'pointerleave', { passive: true })
|
|
8832
|
+
.pipe(takeUntilDestroyed(destroyRef))
|
|
8833
|
+
.subscribe(() => this._release());
|
|
8834
|
+
destroyRef.onDestroy(() => this._unregisterOverlay());
|
|
7650
8835
|
}
|
|
7651
8836
|
/** @protected Inline placement for one grip box. */
|
|
7652
8837
|
_style(box) {
|
|
@@ -7819,10 +9004,10 @@ class MlvEditorTableControls {
|
|
|
7819
9004
|
gripSize: GRIP_SIZE,
|
|
7820
9005
|
}));
|
|
7821
9006
|
}
|
|
7822
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
7823
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.
|
|
9007
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorTableControls, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
9008
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: MlvEditorTableControls, isStandalone: true, selector: "mlv-editor-table-controls", inputs: { layer: { classPropertyName: "layer", publicName: "layer", isSignal: true, isRequired: true, transformFunction: null } }, host: { attributes: { "aria-hidden": "true" }, classAttribute: "mlv-editor-table-controls" }, ngImport: i0, template: "@if (_geometry(); as geometry) {\n <button\n class=\"mlv-editor-table-controls__grip mlv-editor-table-controls__grip--corner\"\n type=\"button\"\n tabindex=\"-1\"\n [style]=\"_style(geometry.corner)\"\n [mlvMenuTrigger]=\"_cornerMenu\"\n [attr.aria-label]=\"_copy().tableActions\"\n (menuOpened)=\"_openMenu(_cornerMenu)\"\n (menuClosed)=\"_closeMenu()\"\n >\n <svg lucideEllipsis [size]=\"10\" aria-hidden=\"true\" />\n </button>\n <mlv-menu #_cornerMenu [label]=\"_copy().tableActions\">\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_disabled(_canToggleHeaderRow)\"\n (itemClick)=\"_run(_toggleHeaderRow)\"\n >\n {{ _copy().toggleHeaderRow }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_disabled(_canToggleHeaderColumn)\"\n (itemClick)=\"_run(_toggleHeaderColumn)\"\n >\n {{ _copy().toggleHeaderColumn }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_disabled(_canToggleHeaderCell)\"\n (itemClick)=\"_run(_toggleHeaderCell)\"\n >\n {{ _copy().toggleHeaderCell }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_disabled(_canMergeCells)\"\n (itemClick)=\"_run(_mergeCells)\"\n >\n {{ _copy().mergeCells }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_disabled(_canSplitCell)\"\n (itemClick)=\"_run(_splitCell)\"\n >\n {{ _copy().splitCell }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_disabled(_canDeleteTable)\"\n (itemClick)=\"_run(_deleteTable)\"\n >\n {{ _copy().deleteTable }}\n </mlv-list-item>\n </mlv-menu>\n\n <button\n class=\"mlv-editor-table-controls__grip mlv-editor-table-controls__grip--column\"\n type=\"button\"\n tabindex=\"-1\"\n [style]=\"_style(geometry.column)\"\n [mlvMenuTrigger]=\"_columnMenu\"\n [attr.aria-label]=\"_copy().columnActions\"\n (menuOpened)=\"_openMenu(_columnMenu)\"\n (menuClosed)=\"_closeMenu()\"\n >\n <svg lucideGripHorizontal [size]=\"12\" aria-hidden=\"true\" />\n </button>\n <mlv-menu #_columnMenu [label]=\"_copy().columnActions\">\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_disabled(_canAddColumnBefore)\"\n (itemClick)=\"_run(_addColumnBefore)\"\n >\n {{ _copy().addColumnBefore }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_disabled(_canAddColumnAfter)\"\n (itemClick)=\"_run(_addColumnAfter)\"\n >\n {{ _copy().addColumnAfter }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_disabled(_canToggleHeaderColumn)\"\n (itemClick)=\"_run(_toggleHeaderColumn)\"\n >\n {{ _copy().toggleHeaderColumn }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_disabled(_canDeleteColumn)\"\n (itemClick)=\"_run(_deleteColumn)\"\n >\n {{ _copy().deleteColumn }}\n </mlv-list-item>\n </mlv-menu>\n\n <button\n class=\"mlv-editor-table-controls__grip mlv-editor-table-controls__grip--row\"\n type=\"button\"\n tabindex=\"-1\"\n [style]=\"_style(geometry.row)\"\n [mlvMenuTrigger]=\"_rowMenu\"\n [attr.aria-label]=\"_copy().rowActions\"\n (menuOpened)=\"_openMenu(_rowMenu)\"\n (menuClosed)=\"_closeMenu()\"\n >\n <svg lucideGripVertical [size]=\"12\" aria-hidden=\"true\" />\n </button>\n <mlv-menu #_rowMenu [label]=\"_copy().rowActions\">\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_disabled(_canAddRowBefore)\"\n (itemClick)=\"_run(_addRowBefore)\"\n >\n {{ _copy().addRowBefore }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_disabled(_canAddRowAfter)\"\n (itemClick)=\"_run(_addRowAfter)\"\n >\n {{ _copy().addRowAfter }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_disabled(_canToggleHeaderRow)\"\n (itemClick)=\"_run(_toggleHeaderRow)\"\n >\n {{ _copy().toggleHeaderRow }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_disabled(_canDeleteRow)\"\n (itemClick)=\"_run(_deleteRow)\"\n >\n {{ _copy().deleteRow }}\n </mlv-list-item>\n </mlv-menu>\n}\n", styles: ["@layer mlv.components{.mlv-editor-table-controls{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor-table-controls *{box-sizing:border-box}.mlv-editor-table-controls{display:contents;--mlv-editor-table-grip-size: 14px}.mlv-editor-table-controls__grip{position:absolute;display:flex;align-items:center;justify-content:center;padding:0;border:var(--mlv-stroke-width) solid var(--mlv-border-subtle);border-radius:var(--mlv-radius-xs);background:var(--mlv-background-neutral-1);color:var(--mlv-text-secondary);cursor:pointer;z-index:1;transition:background-color var(--mlv-duration-fast) var(--mlv-ease-default),color var(--mlv-duration-fast) var(--mlv-ease-default)}.mlv-editor-table-controls__grip:hover{background:var(--mlv-background-neutral-1-hover);color:var(--mlv-text-primary)}.mlv-editor-table-controls__grip[aria-expanded=true]{background:var(--mlv-background-selected);color:var(--mlv-text-on-selected)}.mlv-editor-table-controls__grip--corner svg{opacity:.75}@media(prefers-reduced-motion:reduce){.mlv-editor-table-controls,.mlv-editor-table-controls *,.mlv-editor-table-controls:before,.mlv-editor-table-controls:after,[class^=mlv-editor-table-controls__],[class*=\" mlv-editor-table-controls__\"],[class^=mlv-editor-table-controls--],[class*=\" mlv-editor-table-controls--\"]{scroll-behavior:auto!important;animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:var(--mlv-duration-instant)!important}}}\n"], dependencies: [{ kind: "component", type: LucideEllipsis, selector: "svg[lucideEllipsis], svg[lucideMoreHorizontal]" }, { kind: "component", type: LucideGripHorizontal, selector: "svg[lucideGripHorizontal]" }, { kind: "component", type: LucideGripVertical, selector: "svg[lucideGripVertical]" }, { kind: "component", type: MlvListItem, selector: "mlv-list-item", inputs: ["itemRole", "unread", "accent"] }, { kind: "component", type: MlvMenu, selector: "mlv-menu", inputs: ["label", "mlvDensity", "dataSource"], outputs: ["closed"] }, { kind: "directive", type: MlvMenuItem, selector: "mlv-list-item[mlvMenuItem]", inputs: ["disabled"], outputs: ["itemClick"] }, { kind: "directive", type: MlvMenuTrigger, selector: "[mlvMenuTrigger]", inputs: ["mlvMenuTrigger", "isSubmenuTrigger", "menuTriggerDisabled"], outputs: ["menuOpened", "menuClosed"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
7824
9009
|
}
|
|
7825
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
9010
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorTableControls, decorators: [{
|
|
7826
9011
|
type: Component,
|
|
7827
9012
|
args: [{ selector: 'mlv-editor-table-controls', imports: [
|
|
7828
9013
|
LucideEllipsis,
|
|
@@ -7848,10 +9033,10 @@ class MlvEditorToolbarDef {
|
|
|
7848
9033
|
static ngTemplateContextGuard(_dir, _context) {
|
|
7849
9034
|
return true;
|
|
7850
9035
|
}
|
|
7851
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
7852
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.
|
|
9036
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorToolbarDef, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
9037
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.7", type: MlvEditorToolbarDef, isStandalone: true, selector: "[mlvEditorToolbar]", ngImport: i0 });
|
|
7853
9038
|
}
|
|
7854
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
9039
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorToolbarDef, decorators: [{
|
|
7855
9040
|
type: Directive,
|
|
7856
9041
|
args: [{
|
|
7857
9042
|
selector: '[mlvEditorToolbar]',
|
|
@@ -7867,10 +9052,10 @@ class MlvEditorToolbarStartDef {
|
|
|
7867
9052
|
static ngTemplateContextGuard(_dir, _context) {
|
|
7868
9053
|
return true;
|
|
7869
9054
|
}
|
|
7870
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
7871
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.
|
|
9055
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorToolbarStartDef, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
9056
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.7", type: MlvEditorToolbarStartDef, isStandalone: true, selector: "[mlvEditorToolbarStart]", ngImport: i0 });
|
|
7872
9057
|
}
|
|
7873
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
9058
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorToolbarStartDef, decorators: [{
|
|
7874
9059
|
type: Directive,
|
|
7875
9060
|
args: [{
|
|
7876
9061
|
selector: '[mlvEditorToolbarStart]',
|
|
@@ -7886,10 +9071,10 @@ class MlvEditorToolbarEndDef {
|
|
|
7886
9071
|
static ngTemplateContextGuard(_dir, _context) {
|
|
7887
9072
|
return true;
|
|
7888
9073
|
}
|
|
7889
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
7890
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.
|
|
9074
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorToolbarEndDef, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
9075
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.7", type: MlvEditorToolbarEndDef, isStandalone: true, selector: "[mlvEditorToolbarEnd]", ngImport: i0 });
|
|
7891
9076
|
}
|
|
7892
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
9077
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorToolbarEndDef, decorators: [{
|
|
7893
9078
|
type: Directive,
|
|
7894
9079
|
args: [{
|
|
7895
9080
|
selector: '[mlvEditorToolbarEnd]',
|
|
@@ -7918,8 +9103,8 @@ class MlvEditorUndoRedo {
|
|
|
7918
9103
|
_undo = (editor) => editor.chain().focus().undo().run();
|
|
7919
9104
|
/** @protected Selection-restoring redo command. */
|
|
7920
9105
|
_redo = (editor) => editor.chain().focus().redo().run();
|
|
7921
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
7922
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.
|
|
9106
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorUndoRedo, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
9107
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.7", type: MlvEditorUndoRedo, isStandalone: true, selector: "mlv-editor-undo-redo", host: { attributes: { "role": "group" }, classAttribute: "mlv-editor-undo-redo" }, ngImport: i0, template: `
|
|
7923
9108
|
<mlv-editor-command-button
|
|
7924
9109
|
[label]="_copy().undo"
|
|
7925
9110
|
[supports]="_canUndo"
|
|
@@ -7936,7 +9121,7 @@ class MlvEditorUndoRedo {
|
|
|
7936
9121
|
/></mlv-editor-command-button>
|
|
7937
9122
|
`, isInline: true, dependencies: [{ kind: "component", type: MlvEditorCommandButton, selector: "mlv-editor-command-button", inputs: ["label", "supports", "command", "canCommand", "pressed"] }, { kind: "component", type: LucideRedo2, selector: "svg[lucideRedo2]" }, { kind: "component", type: LucideUndo2, selector: "svg[lucideUndo2]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
7938
9123
|
}
|
|
7939
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
9124
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorUndoRedo, decorators: [{
|
|
7940
9125
|
type: Component,
|
|
7941
9126
|
args: [{
|
|
7942
9127
|
selector: 'mlv-editor-undo-redo',
|
|
@@ -8307,10 +9492,10 @@ class MlvEditorZoom {
|
|
|
8307
9492
|
const finite = Number.isFinite(value) ? value : DEFAULT_ZOOM;
|
|
8308
9493
|
return Math.min(Math.max(finite, min), max);
|
|
8309
9494
|
}
|
|
8310
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
8311
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.
|
|
9495
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorZoom, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
9496
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.1.7", type: MlvEditorZoom, isStandalone: true, selector: "mlv-editor-zoom", inputs: { zoom: { classPropertyName: "zoom", publicName: "zoom", isSignal: true, isRequired: false, transformFunction: null }, zoomLevels: { classPropertyName: "zoomLevels", publicName: "zoomLevels", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, fitToContainer: { classPropertyName: "fitToContainer", publicName: "fitToContainer", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { zoom: "zoomChange" }, host: { classAttribute: "mlv-editor-zoom" }, providers: [MlvSelectionService], viewQueries: [{ propertyName: "_popup", first: true, predicate: ["_popup"], descendants: true, isSignal: true }, { propertyName: "_trigger", first: true, predicate: ["_trigger"], descendants: true, isSignal: true }, { propertyName: "_input", first: true, predicate: ["_input"], descendants: true, isSignal: true }], usesOnChanges: true, ngImport: i0, template: "<button\n mlvButton\n mlvEditorToolbarWidget\n type=\"button\"\n shape=\"square\"\n mlvDensity=\"tight\"\n variant=\"transparent\"\n [disabled]=\"_zoomOutDisabled()\"\n [attr.aria-label]=\"_copy().zoomOut\"\n [mlvTooltip]=\"_copy().zoomOut\"\n (click)=\"_zoomOut()\"\n>\n <svg mlvButtonIcon lucideZoomOut aria-hidden=\"true\" />\n</button>\n\n<button\n #_trigger=\"mlvPopupTrigger\"\n mlvButton\n mlvEditorToolbarWidget\n type=\"button\"\n mlvDensity=\"tight\"\n variant=\"transparent\"\n [disabled]=\"_disabled()\"\n [mlvPopupTrigger]=\"_popup\"\n [ariaHasPopup]=\"'dialog'\"\n [attr.aria-label]=\"_copy().zoom\"\n [mlvTooltip]=\"_copy().zoom\"\n>\n {{ zoom() }}%\n</button>\n\n<button\n mlvButton\n mlvEditorToolbarWidget\n type=\"button\"\n shape=\"square\"\n mlvDensity=\"tight\"\n variant=\"transparent\"\n [disabled]=\"_zoomInDisabled()\"\n [attr.aria-label]=\"_copy().zoomIn\"\n [mlvTooltip]=\"_copy().zoomIn\"\n (click)=\"_zoomIn()\"\n>\n <svg mlvButtonIcon lucideZoomIn aria-hidden=\"true\" />\n</button>\n\n<mlv-popup\n #_popup\n panelRole=\"dialog\"\n [modal]=\"true\"\n [ariaLabel]=\"_copy().zoom\"\n (afterOpened)=\"_onPopupOpened()\"\n (afterClosed)=\"_onPopupClosed()\"\n>\n <ng-template mlvPopupContent>\n <div [id]=\"_panelId\" class=\"mlv-editor-zoom__panel\">\n <div class=\"mlv-editor-zoom__field\">\n <mlv-input\n #_input\n type=\"number\"\n inputMode=\"numeric\"\n [ariaLabel]=\"_copy().zoom\"\n [min]=\"_bounds().min\"\n [max]=\"_bounds().max\"\n [disabled]=\"_disabled()\"\n [value]=\"_draft()\"\n (valueChange)=\"_draft.set($event)\"\n (keydown.enter)=\"_applyDraft($event)\"\n (inputBlur)=\"_applyDraft()\"\n />\n </div>\n <mlv-dropdown-panel\n class=\"mlv-editor-zoom__levels\"\n [options]=\"_levelOptions()\"\n [selectedValues]=\"_selectedLevels()\"\n [ariaLabel]=\"_copy().zoom\"\n [maxHeight]=\"_PANEL_MAX_HEIGHT\"\n (valueChange)=\"_selectLevel($event)\"\n />\n </div>\n </ng-template>\n</mlv-popup>\n", styles: ["@layer mlv.components{.mlv-editor-zoom{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-typography-body-m-size)}.mlv-editor-zoom *{box-sizing:border-box}.mlv-editor-zoom{display:inline-flex;align-items:center;gap:var(--mlv-spacing-1);white-space:nowrap}.mlv-editor-zoom__panel{display:grid;inline-size:11rem;padding:var(--mlv-popover-inset);gap:var(--mlv-spacing-1)}.mlv-editor-zoom__field,.mlv-editor-zoom__levels{display:block}.mlv-editor-zoom__levels.mlv-dropdown-panel .mlv-dropdown-panel__listbox{--mlv-list-padding-block: 0}}\n"], dependencies: [{ kind: "component", type: MlvButton, selector: "button[mlvButton], a[mlvButton]", inputs: ["variant", "shape", "disabled", "loading", "selected"] }, { kind: "directive", type: MlvButtonIcon, selector: "[mlvButtonIcon]" }, { kind: "directive", type: MlvTooltip, selector: "[mlvTooltip]", inputs: ["mlvTooltip", "tooltipPlacement", "tooltipTone", "tooltipDelay", "tooltipDisabled", "tooltipArrow"] }, { kind: "component", type: LucideZoomIn, selector: "svg[lucideZoomIn]" }, { kind: "component", type: LucideZoomOut, selector: "svg[lucideZoomOut]" }, { kind: "component", type: MlvDropdownPanel, selector: "mlv-dropdown-panel", inputs: ["scrollMode", "minHeight", "maxHeight", "options", "multiple", "selectedValues", "compareWith", "itemTemplate", "focusMode", "activeIndex", "listboxId", "ariaLabel", "highlightQuery", "loading", "loadingText", "loadingMore", "hasMore", "infiniteScrollThreshold"], outputs: ["loadMore", "valueChange"] }, { kind: "component", type: MlvInput, selector: "mlv-input", inputs: ["type", "placeholder", "autocomplete", "inputMode", "maxLength", "minLength", "pattern", "name", "autofocus", "min", "max", "step", "bare", "projectControl", "role", "ariaExpanded", "ariaHasPopup", "ariaAutocomplete", "ariaControls", "ariaActiveDescendant", "ariaDescribedBy", "value"], outputs: ["valueChange", "inputFocus", "inputBlur"] }, { kind: "component", type: MlvPopup, selector: "mlv-popup", inputs: ["position", "positions", "width", "height", "minWidth", "minHeight", "maxWidth", "maxHeight", "hasArrow", "hasShadow", "class", "mlvDensity", "flexibleDimensions", "scrollStrategy", "hasBackdrop", "restrictPosition", "panelRole", "modal", "ariaLabel", "dismissExcludeElements", "mobileMode", "mobileBreakpoint", "mobileTitle", "mobileCloseLabel", "opened"], outputs: ["openedChange", "afterOpened", "afterClosed"] }, { kind: "directive", type: MlvPopupContent, selector: "[mlvPopupContent]" }, { kind: "directive", type: MlvPopupTrigger, selector: "[mlvPopupTrigger]", inputs: ["mlvPopupTrigger", "triggerOn", "ariaHasPopup"], exportAs: ["mlvPopupTrigger"] }, { kind: "directive", type: MlvEditorToolbarWidget, selector: "[mlvEditorToolbarWidget]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
8312
9497
|
}
|
|
8313
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
9498
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorZoom, decorators: [{
|
|
8314
9499
|
type: Component,
|
|
8315
9500
|
args: [{ selector: 'mlv-editor-zoom', imports: [
|
|
8316
9501
|
MlvButton,
|
|
@@ -8356,8 +9541,8 @@ class MlvEditorTextColor extends MlvEditorColorControl {
|
|
|
8356
9541
|
const chain = editor.chain().setTextSelection(selection);
|
|
8357
9542
|
return color ? chain.setColor(color).run() : chain.unsetColor().run();
|
|
8358
9543
|
}
|
|
8359
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
8360
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.
|
|
9544
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorTextColor, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
9545
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.7", type: MlvEditorTextColor, isStandalone: true, selector: "mlv-editor-text-color", host: { properties: { "hidden": "!_supported()" }, classAttribute: "mlv-editor-text-color" }, usesInheritance: true, ngImport: i0, template: `
|
|
8361
9546
|
<mlv-color-picker-popup
|
|
8362
9547
|
#picker
|
|
8363
9548
|
presentation="icon"
|
|
@@ -8374,7 +9559,7 @@ class MlvEditorTextColor extends MlvEditorColorControl {
|
|
|
8374
9559
|
</mlv-color-picker-popup>
|
|
8375
9560
|
`, isInline: true, dependencies: [{ kind: "component", type: MlvColorPickerPopup, selector: "mlv-color-picker-popup", inputs: ["value", "presentation", "opened", "showOpacity", "defaultMode", "supportedFormats", "live"], outputs: ["valueChange", "openedChange", "afterOpened", "afterClosed", "colorChange"] }, { kind: "directive", type: MlvButtonIcon, selector: "[mlvButtonIcon]" }, { kind: "component", type: LucideType, selector: "svg[lucideType]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
8376
9561
|
}
|
|
8377
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
9562
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorTextColor, decorators: [{
|
|
8378
9563
|
type: Component,
|
|
8379
9564
|
args: [{
|
|
8380
9565
|
selector: 'mlv-editor-text-color',
|
|
@@ -8419,6 +9604,16 @@ class MlvEditorToolbarRoot {
|
|
|
8419
9604
|
...(ngDevMode ? [{ debugName: "orientation" }] : /* istanbul ignore next */ []));
|
|
8420
9605
|
/** Whether arrow navigation wraps at the boundaries. */
|
|
8421
9606
|
wrap = input(true, { ...(ngDevMode ? { debugName: "wrap" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
|
|
9607
|
+
/**
|
|
9608
|
+
* @internal Element whose width decides narrow mode, instead of this root's
|
|
9609
|
+
* own. `MlvEditor` passes its `.mlv-editor__surface`, for the docked bar
|
|
9610
|
+
* and the selection bubble alike. The bubble hugs its controls, so measuring
|
|
9611
|
+
* itself would be circular: hiding groups shrinks the bubble, which
|
|
9612
|
+
* un-narrows it. `undefined` observes this root, which the standalone
|
|
9613
|
+
* `MlvEditorToolbar` shell keeps doing.
|
|
9614
|
+
*/
|
|
9615
|
+
measureTarget = input(undefined, /* @ts-ignore */
|
|
9616
|
+
...(ngDevMode ? [{ debugName: "measureTarget" }] : /* istanbul ignore next */ []));
|
|
8422
9617
|
/** @internal Editor-scoped focus registry. */
|
|
8423
9618
|
_registry = inject(MLV_EDITOR_TOOLBAR_ROVING);
|
|
8424
9619
|
/** @internal Transaction state that re-evaluates disabled toolbar widgets. */
|
|
@@ -8427,7 +9622,10 @@ class MlvEditorToolbarRoot {
|
|
|
8427
9622
|
});
|
|
8428
9623
|
/** @internal Optional localized defaults. */
|
|
8429
9624
|
_i18n = inject(MLV_EDITOR_I18N, { optional: true });
|
|
8430
|
-
/**
|
|
9625
|
+
/**
|
|
9626
|
+
* @internal Actual composite-root element. Also the scope the horizontal
|
|
9627
|
+
* roving arrows resolve their direction against.
|
|
9628
|
+
*/
|
|
8431
9629
|
_element = inject(ElementRef);
|
|
8432
9630
|
/** @internal Root teardown lifecycle. */
|
|
8433
9631
|
_destroyRef = inject(DestroyRef);
|
|
@@ -8437,6 +9635,12 @@ class MlvEditorToolbarRoot {
|
|
|
8437
9635
|
_resizeObserver = inject(MlvResizeObserverService);
|
|
8438
9636
|
/** @internal Normalizes horizontal arrow meaning for RTL toolbars. */
|
|
8439
9637
|
_rtlService = inject(MlvRtlService);
|
|
9638
|
+
/**
|
|
9639
|
+
* @internal Direction applying to this toolbar, resolved once and cached
|
|
9640
|
+
* behind the shared `dir` observer rather than re-walked on every arrow
|
|
9641
|
+
* keypress.
|
|
9642
|
+
*/
|
|
9643
|
+
_direction = this._rtlService.elementDirection(this._element);
|
|
8440
9644
|
constructor() {
|
|
8441
9645
|
effect(() => {
|
|
8442
9646
|
this._revision?.();
|
|
@@ -8444,15 +9648,23 @@ class MlvEditorToolbarRoot {
|
|
|
8444
9648
|
});
|
|
8445
9649
|
if (this._isBrowser) {
|
|
8446
9650
|
this._destroyRef.onDestroy(this._registry.connect(this._element.nativeElement));
|
|
8447
|
-
|
|
8448
|
-
|
|
8449
|
-
|
|
9651
|
+
}
|
|
9652
|
+
effect((onCleanup) => {
|
|
9653
|
+
if (!this._isBrowser)
|
|
9654
|
+
return;
|
|
9655
|
+
const target = this.measureTarget() ?? this._element.nativeElement;
|
|
9656
|
+
// Re-created whenever the target changes, so it is released by this
|
|
9657
|
+
// effect's cleanup; `takeUntilDestroyed` would fire only at destroy and
|
|
9658
|
+
// leak every earlier generation (best-practices, "DOM Listeners").
|
|
9659
|
+
const subscription = this._resizeObserver
|
|
9660
|
+
.observe(target)
|
|
8450
9661
|
.subscribe((entries) => {
|
|
8451
9662
|
const width = entries[entries.length - 1]?.contentRect.width;
|
|
8452
9663
|
if (width !== undefined && width > 0)
|
|
8453
9664
|
this.narrow.set(width < 640);
|
|
8454
9665
|
});
|
|
8455
|
-
|
|
9666
|
+
onCleanup(() => subscription.unsubscribe());
|
|
9667
|
+
});
|
|
8456
9668
|
}
|
|
8457
9669
|
/** @internal Resolves the user or localized toolbar label. */
|
|
8458
9670
|
resolvedAriaLabel() {
|
|
@@ -8470,7 +9682,10 @@ class MlvEditorToolbarRoot {
|
|
|
8470
9682
|
this._registry.focusBoundary(event.key === 'Home' ? 'start' : 'end');
|
|
8471
9683
|
return;
|
|
8472
9684
|
}
|
|
8473
|
-
|
|
9685
|
+
// Resolved against the toolbar root, not the document: direction is scoped,
|
|
9686
|
+
// so a toolbar inside a `dir` subtree — or inside a CDK overlay pane, which
|
|
9687
|
+
// is stamped with its own `dir` — must mirror on its own reading.
|
|
9688
|
+
const key = this._rtlService.normalizeArrowKey(event, this._direction());
|
|
8474
9689
|
const direction = this.orientation() === 'vertical'
|
|
8475
9690
|
? key === UP_ARROW
|
|
8476
9691
|
? -1
|
|
@@ -8487,10 +9702,10 @@ class MlvEditorToolbarRoot {
|
|
|
8487
9702
|
event.preventDefault();
|
|
8488
9703
|
this._registry.move(target, direction, this.wrap());
|
|
8489
9704
|
}
|
|
8490
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
8491
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.
|
|
9705
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorToolbarRoot, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
9706
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.7", type: MlvEditorToolbarRoot, isStandalone: true, selector: "[mlvEditorToolbarRoot]", inputs: { ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null }, wrap: { classPropertyName: "wrap", publicName: "wrap", isSignal: true, isRequired: false, transformFunction: null }, measureTarget: { classPropertyName: "measureTarget", publicName: "measureTarget", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "toolbar" }, listeners: { "keydown": "handleKeydown($event)" }, properties: { "class.mlv-editor-toolbar--narrow": "narrow()", "attr.aria-label": "resolvedAriaLabel()", "attr.aria-orientation": "orientation()", "attr.aria-disabled": "disabled() || null" }, classAttribute: "mlv-editor-toolbar" }, exportAs: ["mlvEditorToolbarRoot"], ngImport: i0 });
|
|
8492
9707
|
}
|
|
8493
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
9708
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorToolbarRoot, decorators: [{
|
|
8494
9709
|
type: Directive,
|
|
8495
9710
|
args: [{
|
|
8496
9711
|
selector: '[mlvEditorToolbarRoot]',
|
|
@@ -8505,7 +9720,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
|
|
|
8505
9720
|
'(keydown)': 'handleKeydown($event)',
|
|
8506
9721
|
},
|
|
8507
9722
|
}]
|
|
8508
|
-
}], ctorParameters: () => [], propDecorators: { ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }], wrap: [{ type: i0.Input, args: [{ isSignal: true, alias: "wrap", required: false }] }] } });
|
|
9723
|
+
}], ctorParameters: () => [], propDecorators: { ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }], wrap: [{ type: i0.Input, args: [{ isSignal: true, alias: "wrap", required: false }] }], measureTarget: [{ type: i0.Input, args: [{ isSignal: true, alias: "measureTarget", required: false }] }] } });
|
|
8509
9724
|
/** @internal Responsive menu containing commands removed from the primary row. */
|
|
8510
9725
|
class MlvEditorToolbarOverflow {
|
|
8511
9726
|
/** @protected Responsive state owned by the enclosing toolbar root. */
|
|
@@ -8656,8 +9871,8 @@ class MlvEditorToolbarOverflow {
|
|
|
8656
9871
|
this._unregister?.();
|
|
8657
9872
|
this._unregister = undefined;
|
|
8658
9873
|
}
|
|
8659
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
8660
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.
|
|
9874
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorToolbarOverflow, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
9875
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: MlvEditorToolbarOverflow, isStandalone: true, selector: "mlv-editor-toolbar-overflow", host: { properties: { "hidden": "!_root.narrow()" }, classAttribute: "mlv-editor-toolbar__overflow" }, viewQueries: [{ propertyName: "_menu", first: true, predicate: ["_menu"], descendants: true, isSignal: true }, { propertyName: "_trigger", first: true, predicate: MlvMenuTrigger, descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
8661
9876
|
<button
|
|
8662
9877
|
mlvButton
|
|
8663
9878
|
mlvEditorToolbarWidget
|
|
@@ -8755,7 +9970,7 @@ class MlvEditorToolbarOverflow {
|
|
|
8755
9970
|
</mlv-menu>
|
|
8756
9971
|
`, isInline: true, dependencies: [{ kind: "component", type: MlvButton, selector: "button[mlvButton], a[mlvButton]", inputs: ["variant", "shape", "disabled", "loading", "selected"] }, { kind: "directive", type: MlvButtonIcon, selector: "[mlvButtonIcon]" }, { kind: "directive", type: MlvTooltip, selector: "[mlvTooltip]", inputs: ["mlvTooltip", "tooltipPlacement", "tooltipTone", "tooltipDelay", "tooltipDisabled", "tooltipArrow"] }, { kind: "component", type: LucideEllipsis, selector: "svg[lucideEllipsis], svg[lucideMoreHorizontal]" }, { kind: "component", type: MlvListItem, selector: "mlv-list-item", inputs: ["itemRole", "unread", "accent"] }, { kind: "component", type: MlvMenu, selector: "mlv-menu", inputs: ["label", "mlvDensity", "dataSource"], outputs: ["closed"] }, { kind: "directive", type: MlvMenuItem, selector: "mlv-list-item[mlvMenuItem]", inputs: ["disabled"], outputs: ["itemClick"] }, { kind: "directive", type: MlvMenuTrigger, selector: "[mlvMenuTrigger]", inputs: ["mlvMenuTrigger", "isSubmenuTrigger", "menuTriggerDisabled"], outputs: ["menuOpened", "menuClosed"] }, { kind: "directive", type: MlvEditorToolbarWidget, selector: "[mlvEditorToolbarWidget]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
8757
9972
|
}
|
|
8758
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
9973
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorToolbarOverflow, decorators: [{
|
|
8759
9974
|
type: Component,
|
|
8760
9975
|
args: [{
|
|
8761
9976
|
selector: 'mlv-editor-toolbar-overflow',
|
|
@@ -8926,10 +10141,10 @@ class MlvEditorToolbarContextProxy {
|
|
|
8926
10141
|
reportError(error) {
|
|
8927
10142
|
this._toolbar.context().reportError(error);
|
|
8928
10143
|
}
|
|
8929
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
8930
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.
|
|
10144
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorToolbarContextProxy, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
10145
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorToolbarContextProxy });
|
|
8931
10146
|
}
|
|
8932
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
10147
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorToolbarContextProxy, decorators: [{
|
|
8933
10148
|
type: Injectable
|
|
8934
10149
|
}] });
|
|
8935
10150
|
/** Public compatibility toolbar shell for standalone consumer use. */
|
|
@@ -8965,8 +10180,8 @@ class MlvEditorToolbar {
|
|
|
8965
10180
|
});
|
|
8966
10181
|
});
|
|
8967
10182
|
}
|
|
8968
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
8969
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.
|
|
10183
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorToolbar, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
10184
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: MlvEditorToolbar, isStandalone: true, selector: "mlv-editor-toolbar", inputs: { ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, startTemplate: { classPropertyName: "startTemplate", publicName: "startTemplate", isSignal: true, isRequired: false, transformFunction: null }, endTemplate: { classPropertyName: "endTemplate", publicName: "endTemplate", isSignal: true, isRequired: false, transformFunction: null }, context: { classPropertyName: "context", publicName: "context", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "class.mlv-editor-toolbar--disabled": "disabled() || context().disabled()" }, classAttribute: "mlv-editor-toolbar" }, providers: [
|
|
8970
10185
|
MlvEditorOverlayRegistry,
|
|
8971
10186
|
MlvEditorToolbarRevision,
|
|
8972
10187
|
MlvEditorToolbarRovingRegistry,
|
|
@@ -9071,9 +10286,9 @@ class MlvEditorToolbar {
|
|
|
9071
10286
|
/>
|
|
9072
10287
|
}
|
|
9073
10288
|
<ng-content select="[mlvEditorToolbarEnd]" /><ng-content />
|
|
9074
|
-
</div>`, isInline: true, styles: ["@layer mlv.components{.mlv-editor-toolbar{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-typography-body-m-size)}.mlv-editor-toolbar *{box-sizing:border-box}.mlv-editor-toolbar{display:flex;align-items:center;min-width:0;overflow:hidden}.mlv-editor-toolbar [hidden]{display:none!important}.mlv-editor-toolbar__scroll{flex:1 1 auto;min-width:0}.mlv-editor-toolbar__scroll>.mlv-toolbar{width:max-content;min-width:100%;flex-wrap:nowrap}.mlv-editor-toolbar__separator{flex:0 0 auto;align-self:stretch;min-height:var(--mlv-height-s);margin-inline:var(--mlv-spacing-1)}.mlv-editor-toolbar__overflow,.mlv-editor-toolbar .mlv-editor-undo-redo,.mlv-editor-toolbar .mlv-editor-inline-marks,.mlv-editor-toolbar .mlv-editor-block-insert{display:inline-flex;flex:0 0 auto;flex-direction:row;align-items:center;gap:var(--mlv-spacing-1)}.mlv-editor-toolbar mlv-editor-zoom,.mlv-editor-toolbar mlv-editor-heading,.mlv-editor-toolbar mlv-editor-list,.mlv-editor-toolbar mlv-editor-text-color,.mlv-editor-toolbar mlv-editor-highlight,.mlv-editor-toolbar mlv-editor-alignment,.mlv-editor-toolbar mlv-editor-link,.mlv-editor-toolbar mlv-editor-table,.mlv-editor-toolbar mlv-editor-image-upload{flex:0 0 auto}.mlv-editor-toolbar--disabled{opacity:var(--mlv-disabled-opacity)}}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: MlvFade, selector: "[mlvFade]", inputs: ["mlvFadeHeight", "mlvFadeSize", "mlvFadeOffset", "mlvFade"] }, { kind: "component", type: MlvToolbar, selector: "mlv-toolbar", inputs: ["gap", "equalSize"] }, { kind: "component", type: MlvDivider, selector: "mlv-divider", inputs: ["orientation", "dashed", "muted"] }, { kind: "directive", type: MlvEditorToolbarRoot, selector: "[mlvEditorToolbarRoot]", inputs: ["ariaLabel", "disabled", "orientation", "wrap"], exportAs: ["mlvEditorToolbarRoot"] }, { kind: "component", type: MlvEditorUndoRedo, selector: "mlv-editor-undo-redo" }, { kind: "component", type: MlvEditorZoom, selector: "mlv-editor-zoom", inputs: ["zoom", "zoomLevels", "min", "max", "fitToContainer"], outputs: ["zoomChange"] }, { kind: "component", type: MlvEditorHeading, selector: "mlv-editor-heading", inputs: ["levels"] }, { kind: "component", type: MlvEditorList, selector: "mlv-editor-list" }, { kind: "component", type: MlvEditorInlineMarks, selector: "mlv-editor-inline-marks" }, { kind: "component", type: MlvEditorTextColor, selector: "mlv-editor-text-color" }, { kind: "component", type: MlvEditorHighlight, selector: "mlv-editor-highlight" }, { kind: "component", type: MlvEditorLink, selector: "mlv-editor-link", inputs: ["allowedProtocols"] }, { kind: "component", type: MlvEditorImageUpload, selector: "mlv-editor-image-upload" }, { kind: "component", type: MlvEditorTable, selector: "mlv-editor-table" }, { kind: "component", type: MlvEditorAlignment, selector: "mlv-editor-alignment" }, { kind: "component", type: MlvEditorBlockInsert, selector: "mlv-editor-block-insert" }, { kind: "component", type: MlvEditorToolbarOverflow, selector: "mlv-editor-toolbar-overflow" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
10289
|
+
</div>`, isInline: true, styles: ["@layer mlv.components{.mlv-editor-toolbar{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-typography-body-m-size)}.mlv-editor-toolbar *{box-sizing:border-box}.mlv-editor-toolbar{display:flex;align-items:center;min-width:0;overflow:hidden}.mlv-editor-toolbar [hidden]{display:none!important}.mlv-editor-toolbar__scroll{flex:1 1 auto;min-width:0}.mlv-editor-toolbar__scroll>.mlv-toolbar{width:max-content;min-width:100%;flex-wrap:nowrap}.mlv-editor-toolbar__separator{flex:0 0 auto;align-self:stretch;min-height:var(--mlv-height-s);margin-inline:var(--mlv-spacing-1)}.mlv-editor-toolbar__overflow,.mlv-editor-toolbar .mlv-editor-undo-redo,.mlv-editor-toolbar .mlv-editor-inline-marks,.mlv-editor-toolbar .mlv-editor-block-insert{display:inline-flex;flex:0 0 auto;flex-direction:row;align-items:center;gap:var(--mlv-spacing-1)}.mlv-editor-toolbar mlv-editor-zoom,.mlv-editor-toolbar mlv-editor-heading,.mlv-editor-toolbar mlv-editor-list,.mlv-editor-toolbar mlv-editor-text-color,.mlv-editor-toolbar mlv-editor-highlight,.mlv-editor-toolbar mlv-editor-alignment,.mlv-editor-toolbar mlv-editor-link,.mlv-editor-toolbar mlv-editor-table,.mlv-editor-toolbar mlv-editor-image-upload{flex:0 0 auto}.mlv-editor-toolbar--disabled{opacity:var(--mlv-disabled-opacity)}}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: MlvFade, selector: "[mlvFade]", inputs: ["mlvFadeHeight", "mlvFadeSize", "mlvFadeOffset", "mlvFade"] }, { kind: "component", type: MlvToolbar, selector: "mlv-toolbar", inputs: ["gap", "equalSize"] }, { kind: "component", type: MlvDivider, selector: "mlv-divider", inputs: ["orientation", "dashed", "muted"] }, { kind: "directive", type: MlvEditorToolbarRoot, selector: "[mlvEditorToolbarRoot]", inputs: ["ariaLabel", "disabled", "orientation", "wrap", "measureTarget"], exportAs: ["mlvEditorToolbarRoot"] }, { kind: "component", type: MlvEditorUndoRedo, selector: "mlv-editor-undo-redo" }, { kind: "component", type: MlvEditorZoom, selector: "mlv-editor-zoom", inputs: ["zoom", "zoomLevels", "min", "max", "fitToContainer"], outputs: ["zoomChange"] }, { kind: "component", type: MlvEditorHeading, selector: "mlv-editor-heading", inputs: ["levels"] }, { kind: "component", type: MlvEditorList, selector: "mlv-editor-list" }, { kind: "component", type: MlvEditorInlineMarks, selector: "mlv-editor-inline-marks" }, { kind: "component", type: MlvEditorTextColor, selector: "mlv-editor-text-color" }, { kind: "component", type: MlvEditorHighlight, selector: "mlv-editor-highlight" }, { kind: "component", type: MlvEditorLink, selector: "mlv-editor-link", inputs: ["allowedProtocols"] }, { kind: "component", type: MlvEditorImageUpload, selector: "mlv-editor-image-upload" }, { kind: "component", type: MlvEditorTable, selector: "mlv-editor-table" }, { kind: "component", type: MlvEditorAlignment, selector: "mlv-editor-alignment" }, { kind: "component", type: MlvEditorBlockInsert, selector: "mlv-editor-block-insert" }, { kind: "component", type: MlvEditorToolbarOverflow, selector: "mlv-editor-toolbar-overflow" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
9075
10290
|
}
|
|
9076
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
10291
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorToolbar, decorators: [{
|
|
9077
10292
|
type: Component,
|
|
9078
10293
|
args: [{ selector: 'mlv-editor-toolbar', host: {
|
|
9079
10294
|
class: 'mlv-editor-toolbar',
|
|
@@ -9254,15 +10469,15 @@ class MlvEditorStatus {
|
|
|
9254
10469
|
return typeof storage?.characters === 'function' ? storage : null;
|
|
9255
10470
|
}, /* @ts-ignore */
|
|
9256
10471
|
...(ngDevMode ? [{ debugName: "_storage" }] : /* istanbul ignore next */ []));
|
|
9257
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
9258
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.
|
|
10472
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorStatus, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
10473
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: MlvEditorStatus, isStandalone: true, selector: "mlv-editor-status", inputs: { characterLimit: { classPropertyName: "characterLimit", publicName: "characterLimit", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "hidden": "!available()" }, classAttribute: "mlv-editor-status" }, ngImport: i0, template: "@if (available()) {\n <span\n class=\"mlv-editor-status__summary\"\n role=\"status\"\n aria-live=\"polite\"\n [attr.aria-label]=\"_ariaLabel()\"\n >\n @if (_limit(); as limit) {\n <span class=\"mlv-editor-status__characters\"\n >{{ characters() }}/{{ limit }} characters</span\n >\n } @else {\n <span class=\"mlv-editor-status__characters\"\n >{{ characters() }} characters</span\n >\n }\n <span aria-hidden=\"true\"> \u00B7 </span>\n <span class=\"mlv-editor-status__words\">{{ words() }} words</span>\n </span>\n}\n", styles: ["@layer mlv.components{.mlv-editor-status{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor-status *{box-sizing:border-box}.mlv-editor-status{display:inline-flex;color:var(--mlv-text-secondary);font-size:var(--mlv-typography-caption-size);line-height:var(--mlv-typography-caption-line-height)}.mlv-editor-status__summary{display:inline-flex;gap:var(--mlv-spacing-1)}.mlv-editor-status__summary *{color:var(--mlv-text-secondary);font-size:var(--mlv-typography-caption-size);line-height:var(--mlv-typography-caption-line-height)}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
9259
10474
|
}
|
|
9260
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
10475
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorStatus, decorators: [{
|
|
9261
10476
|
type: Component,
|
|
9262
10477
|
args: [{ selector: 'mlv-editor-status', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
9263
10478
|
class: 'mlv-editor-status',
|
|
9264
10479
|
'[hidden]': '!available()',
|
|
9265
|
-
}, template: "@if (available()) {\n<span\n
|
|
10480
|
+
}, template: "@if (available()) {\n <span\n class=\"mlv-editor-status__summary\"\n role=\"status\"\n aria-live=\"polite\"\n [attr.aria-label]=\"_ariaLabel()\"\n >\n @if (_limit(); as limit) {\n <span class=\"mlv-editor-status__characters\"\n >{{ characters() }}/{{ limit }} characters</span\n >\n } @else {\n <span class=\"mlv-editor-status__characters\"\n >{{ characters() }} characters</span\n >\n }\n <span aria-hidden=\"true\"> \u00B7 </span>\n <span class=\"mlv-editor-status__words\">{{ words() }} words</span>\n </span>\n}\n", styles: ["@layer mlv.components{.mlv-editor-status{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor-status *{box-sizing:border-box}.mlv-editor-status{display:inline-flex;color:var(--mlv-text-secondary);font-size:var(--mlv-typography-caption-size);line-height:var(--mlv-typography-caption-line-height)}.mlv-editor-status__summary{display:inline-flex;gap:var(--mlv-spacing-1)}.mlv-editor-status__summary *{color:var(--mlv-text-secondary);font-size:var(--mlv-typography-caption-size);line-height:var(--mlv-typography-caption-line-height)}}\n"] }]
|
|
9266
10481
|
}], propDecorators: { characterLimit: [{ type: i0.Input, args: [{ isSignal: true, alias: "characterLimit", required: false }] }] } });
|
|
9267
10482
|
|
|
9268
10483
|
/**
|
|
@@ -9285,6 +10500,87 @@ class MlvEditor extends MlvSignalFormControlBase {
|
|
|
9285
10500
|
/** Width of the centred content column; a gutter is reserved at every value. */
|
|
9286
10501
|
contentWidth = input('default', /* @ts-ignore */
|
|
9287
10502
|
...(ngDevMode ? [{ debugName: "contentWidth" }] : /* istanbul ignore next */ []));
|
|
10503
|
+
/**
|
|
10504
|
+
* Fixed block size of the editor surface (toolbar plus content viewport).
|
|
10505
|
+
* A number is px; a string is any CSS length, passed through verbatim.
|
|
10506
|
+
*
|
|
10507
|
+
* Setting it **caps** the editor: `.mlv-editor__viewport` becomes the only
|
|
10508
|
+
* scroll container. With neither `height` nor `maxHeight`, the editor grows
|
|
10509
|
+
* with its content, never shows a scrollbar, and never stops a scroll meant
|
|
10510
|
+
* for the page. Writing `--mlv-editor-height` in CSS alone does not cap.
|
|
10511
|
+
*/
|
|
10512
|
+
height = input(undefined, /* @ts-ignore */
|
|
10513
|
+
...(ngDevMode ? [{ debugName: "height" }] : /* istanbul ignore next */ []));
|
|
10514
|
+
/**
|
|
10515
|
+
* Minimum block size of the editable content area; the viewport adds its
|
|
10516
|
+
* 1rem padding around it. A number is px; a string is any CSS length. `undefined` keeps the `8rem` floor. Setting only this input
|
|
10517
|
+
* leaves the editor in auto mode. `--mlv-editor-min-height` may also be set
|
|
10518
|
+
* in CSS.
|
|
10519
|
+
*
|
|
10520
|
+
* Under a cap (`height` / `maxHeight`) the cap wins, even over a larger
|
|
10521
|
+
* `minHeight`: the viewport shrinks to fit the surface, and the floor moves
|
|
10522
|
+
* to the content inside the scroller, at every zoom level. A `minHeight`
|
|
10523
|
+
* above what the cap leaves the content (the cap minus the toolbar band and
|
|
10524
|
+
* the viewport padding) scrolls an empty document; for a fixed size set
|
|
10525
|
+
* `height` rather than `minHeight` equal to `maxHeight`.
|
|
10526
|
+
*/
|
|
10527
|
+
minHeight = input(undefined, /* @ts-ignore */
|
|
10528
|
+
...(ngDevMode ? [{ debugName: "minHeight" }] : /* istanbul ignore next */ []));
|
|
10529
|
+
/**
|
|
10530
|
+
* Largest block size of the editor surface. The surface grows with its
|
|
10531
|
+
* content up to this cap, and then the viewport scrolls. A number is px; a
|
|
10532
|
+
* string is any CSS length. Like `height`, this caps the editor; writing
|
|
10533
|
+
* `--mlv-editor-max-height` in CSS alone does not.
|
|
10534
|
+
*/
|
|
10535
|
+
maxHeight = input(undefined, /* @ts-ignore */
|
|
10536
|
+
...(ngDevMode ? [{ debugName: "maxHeight" }] : /* istanbul ignore next */ []));
|
|
10537
|
+
/**
|
|
10538
|
+
* Docked-bar placement. The DOM order follows it, so the Tab order matches
|
|
10539
|
+
* the visual order (WCAG 2.4.3). Changing it re-creates the toolbar view,
|
|
10540
|
+
* which closes any open toolbar popup. It does not move the floating
|
|
10541
|
+
* selection bubble, which always prefers above the selection. While
|
|
10542
|
+
* `readonly` no bar renders in either position (see `toolbarAppearance`).
|
|
10543
|
+
*/
|
|
10544
|
+
toolbarPosition = input('top', /* @ts-ignore */
|
|
10545
|
+
...(ngDevMode ? [{ debugName: "toolbarPosition" }] : /* istanbul ignore next */ []));
|
|
10546
|
+
/**
|
|
10547
|
+
* Toolbar drawing: `'bar'` is the docked row with a hairline; `'floating'` is
|
|
10548
|
+
* a selection bubble. The bubble is a CDK overlay (`.mlv-editor-bubble`)
|
|
10549
|
+
* shown only while focus is in the editor, the selection is non-empty and
|
|
10550
|
+
* the editor is neither disabled nor `readonly`; it prefers above the
|
|
10551
|
+
* selection and flips below at the window's or a capped viewport's edge.
|
|
10552
|
+
* Alt+F10 in the content summons it at the caret and focuses its first
|
|
10553
|
+
* control (the content carries `aria-keyshortcuts="Alt+F10"` while floating
|
|
10554
|
+
* and editable); Escape dismisses it and returns focus to the content with
|
|
10555
|
+
* the selection intact. A plugin that claims Escape itself (a consumer's
|
|
10556
|
+
* `@tiptap/suggestion` list, an AI stream) gets it first, and the Escape
|
|
10557
|
+
* that dismisses the bubble goes no further, so a dialog or drawer around
|
|
10558
|
+
* the editor closes only on the next one. Turning `readonly` on while the
|
|
10559
|
+
* bubble is shown hides it and closes its popups.
|
|
10560
|
+
*
|
|
10561
|
+
* A `readonly` editor renders **no toolbar in either appearance** (#498):
|
|
10562
|
+
* no `.mlv-editor__toolbar-band`, and no space kept for one. Turning
|
|
10563
|
+
* `readonly` on tears the bar down, closes every popup the editor owns and
|
|
10564
|
+
* returns focus from the bar or such a popup to the content, with the
|
|
10565
|
+
* selection intact; turning it off stamps the bar again in its position.
|
|
10566
|
+
* A consumer toolbar (`[mlvEditorToolbar]`, `[mlvEditorToolbarStart]`,
|
|
10567
|
+
* `[mlvEditorToolbarEnd]`) is stamped in the same band and goes with it.
|
|
10568
|
+
*/
|
|
10569
|
+
toolbarAppearance = input('bar', /* @ts-ignore */
|
|
10570
|
+
...(ngDevMode ? [{ debugName: "toolbarAppearance" }] : /* istanbul ignore next */ []));
|
|
10571
|
+
/**
|
|
10572
|
+
* Keeps the docked bar `position: sticky` against the page (or the nearest
|
|
10573
|
+
* consumer scroll container) while the surface is on screen, offset by
|
|
10574
|
+
* `--mlv-editor-toolbar-sticky-offset` (default `0`).
|
|
10575
|
+
*
|
|
10576
|
+
* It applies only to an **uncapped** **bar**. A capped editor
|
|
10577
|
+
* (`height` / `maxHeight`) scrolls inside its own viewport, and the bar
|
|
10578
|
+
* sits outside that scroller, so there is nothing for it to stick against;
|
|
10579
|
+
* the floating bubble follows the selection instead; and a `readonly`
|
|
10580
|
+
* editor renders no bar at all (#498). In each case the input is ignored
|
|
10581
|
+
* and `.mlv-editor--toolbar-sticky` is not stamped.
|
|
10582
|
+
*/
|
|
10583
|
+
toolbarSticky = input(false, { ...(ngDevMode ? { debugName: "toolbarSticky" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
|
|
9288
10584
|
/** Complete Tiptap extension replacement; omit it for Malva's fresh preset. */
|
|
9289
10585
|
extensions = input(undefined, /* @ts-ignore */
|
|
9290
10586
|
...(ngDevMode ? [{ debugName: "extensions" }] : /* istanbul ignore next */ []));
|
|
@@ -9322,6 +10618,57 @@ class MlvEditor extends MlvSignalFormControlBase {
|
|
|
9322
10618
|
/** View-only zoom percentage shared with the nearest toolbar. */
|
|
9323
10619
|
zoom = signal(100, /* @ts-ignore */
|
|
9324
10620
|
...(ngDevMode ? [{ debugName: "zoom" }] : /* istanbul ignore next */ []));
|
|
10621
|
+
/** @protected Resolved `height`, written into `--mlv-editor-height`; `null` writes nothing. */
|
|
10622
|
+
_heightStyle = computed(() => mlvEditorCssLength(this.height()), /* @ts-ignore */
|
|
10623
|
+
...(ngDevMode ? [{ debugName: "_heightStyle" }] : /* istanbul ignore next */ []));
|
|
10624
|
+
/** @protected Resolved `minHeight`, written into `--mlv-editor-min-height`; `null` writes nothing. */
|
|
10625
|
+
_minHeightStyle = computed(() => mlvEditorCssLength(this.minHeight()), /* @ts-ignore */
|
|
10626
|
+
...(ngDevMode ? [{ debugName: "_minHeightStyle" }] : /* istanbul ignore next */ []));
|
|
10627
|
+
/** @protected Resolved `maxHeight`, written into `--mlv-editor-max-height`; `null` writes nothing. */
|
|
10628
|
+
_maxHeightStyle = computed(() => mlvEditorCssLength(this.maxHeight()), /* @ts-ignore */
|
|
10629
|
+
...(ngDevMode ? [{ debugName: "_maxHeightStyle" }] : /* istanbul ignore next */ []));
|
|
10630
|
+
/**
|
|
10631
|
+
* @protected Whether the editor is capped. Only `height` or `maxHeight`
|
|
10632
|
+
* switches it on; `minHeight` alone stays in auto mode. It is the one input
|
|
10633
|
+
* that turns `.mlv-editor__viewport` into a scroll container.
|
|
10634
|
+
*/
|
|
10635
|
+
_capped = computed(() => this._heightStyle() !== null || this._maxHeightStyle() !== null, /* @ts-ignore */
|
|
10636
|
+
...(ngDevMode ? [{ debugName: "_capped" }] : /* istanbul ignore next */ []));
|
|
10637
|
+
/**
|
|
10638
|
+
* @protected Whether the editor stamps its toolbar at all, in either
|
|
10639
|
+
* appearance: never while `readonly` (#498 owner ruling, "a readonly editor
|
|
10640
|
+
* renders no toolbar"). The docked bar is torn down and the selection
|
|
10641
|
+
* bubble's pane stays empty; both come back when `readonly` turns off.
|
|
10642
|
+
*
|
|
10643
|
+
* This is the one switch, and it covers a consumer toolbar too: a complete
|
|
10644
|
+
* `[mlvEditorToolbar]` replacement and the `[mlvEditorToolbarStart]` /
|
|
10645
|
+
* `[mlvEditorToolbarEnd]` slots are stamped inside the same band. That is
|
|
10646
|
+
* an assumption the owner may reverse. Keeping a complete replacement while
|
|
10647
|
+
* `readonly` is `!this.readonly() || this._hasToolbarDef()` here for the
|
|
10648
|
+
* bar, and the default groups inside the band then need their own readonly
|
|
10649
|
+
* hiding; `_closeBarForReadonly()`'s `_barRendered()` early return must
|
|
10650
|
+
* then key on the removed region, not the band, or focus on a removed
|
|
10651
|
+
* default control drops to `<body>`. The bubble also gates itself on
|
|
10652
|
+
* `readonly` in `MlvEditorBubble._wanted()` / `_ownsContentKey()`, and
|
|
10653
|
+
* `_editorAttributes()` drops `aria-keyshortcuts`.
|
|
10654
|
+
*/
|
|
10655
|
+
_toolbarRendered = computed(() => !this.readonly(), /* @ts-ignore */
|
|
10656
|
+
...(ngDevMode ? [{ debugName: "_toolbarRendered" }] : /* istanbul ignore next */ []));
|
|
10657
|
+
/**
|
|
10658
|
+
* @protected Whether the docked bar is stamped before or after the
|
|
10659
|
+
* viewport: the `'bar'` appearance while `_toolbarRendered()`. With it
|
|
10660
|
+
* false no band exists, so nothing reserves space for one and no sticky
|
|
10661
|
+
* extent is measured.
|
|
10662
|
+
*/
|
|
10663
|
+
_barRendered = computed(() => this.toolbarAppearance() === 'bar' && this._toolbarRendered(), /* @ts-ignore */
|
|
10664
|
+
...(ngDevMode ? [{ debugName: "_barRendered" }] : /* istanbul ignore next */ []));
|
|
10665
|
+
/**
|
|
10666
|
+
* @protected Whether the docked bar is actually sticky: `toolbarSticky` on an
|
|
10667
|
+
* uncapped bar that renders (not while `readonly`). Drives
|
|
10668
|
+
* `.mlv-editor--toolbar-sticky` and the caret scroll margin.
|
|
10669
|
+
*/
|
|
10670
|
+
_stickyToolbar = computed(() => this.toolbarSticky() && this._barRendered() && !this._capped(), /* @ts-ignore */
|
|
10671
|
+
...(ngDevMode ? [{ debugName: "_stickyToolbar" }] : /* istanbul ignore next */ []));
|
|
9325
10672
|
/** Whether the mounted editor currently accepts document mutations. */
|
|
9326
10673
|
editable = computed(() => this._editor() !== null && !this.computedDisabled() && !this.readonly(), /* @ts-ignore */
|
|
9327
10674
|
...(ngDevMode ? [{ debugName: "editable" }] : /* istanbul ignore next */ []));
|
|
@@ -9354,7 +10701,7 @@ class MlvEditor extends MlvSignalFormControlBase {
|
|
|
9354
10701
|
_content = viewChild.required('content', /* @ts-ignore */
|
|
9355
10702
|
...(ngDevMode ? [{ debugName: "_content" }] : /* istanbul ignore next */ []));
|
|
9356
10703
|
/**
|
|
9357
|
-
* @private
|
|
10704
|
+
* @private Zoomed layer (CSS `zoom`) the floating block handle is mounted into.
|
|
9358
10705
|
*
|
|
9359
10706
|
* Deliberately optional rather than `viewChild.required`: the block-handle
|
|
9360
10707
|
* `mount` capability is typed `() => HTMLElement | null`, and the extension
|
|
@@ -9364,6 +10711,12 @@ class MlvEditor extends MlvSignalFormControlBase {
|
|
|
9364
10711
|
*/
|
|
9365
10712
|
_view = viewChild('view', /* @ts-ignore */
|
|
9366
10713
|
...(ngDevMode ? [{ debugName: "_view" }] : /* istanbul ignore next */ []));
|
|
10714
|
+
/**
|
|
10715
|
+
* @private Toolbar band currently stamped before or after the viewport.
|
|
10716
|
+
* Queried by declaration, so it follows the band across position changes.
|
|
10717
|
+
*/
|
|
10718
|
+
_toolbarBand = viewChild('toolbarBand', /* @ts-ignore */
|
|
10719
|
+
...(ngDevMode ? [{ debugName: "_toolbarBand" }] : /* istanbul ignore next */ []));
|
|
9367
10720
|
/** @protected Complete projected toolbar replacement, if the consumer provides one. */
|
|
9368
10721
|
_toolbarDefs = contentChildren(MlvEditorToolbarDef, /* @ts-ignore */
|
|
9369
10722
|
...(ngDevMode ? [{ debugName: "_toolbarDefs" }] : /* istanbul ignore next */ []));
|
|
@@ -9392,6 +10745,16 @@ class MlvEditor extends MlvSignalFormControlBase {
|
|
|
9392
10745
|
_uploadAbortRegistry = inject(MlvEditorUploadAbortRegistry);
|
|
9393
10746
|
/** @private Physical host used to determine the composite focus boundary. */
|
|
9394
10747
|
_host = inject(ElementRef);
|
|
10748
|
+
/**
|
|
10749
|
+
* @private Live ProseMirror `scrollMargin` (WCAG 2.2 SC 2.4.11). One stable
|
|
10750
|
+
* object passed on every `editorProps` write; its sides re-measure per read.
|
|
10751
|
+
* Read untracked: ProseMirror scrolls synchronously inside `updateState`,
|
|
10752
|
+
* which can run within `_synchronizeEditor`'s effect, and that effect must
|
|
10753
|
+
* not start depending on the toolbar inputs.
|
|
10754
|
+
*/
|
|
10755
|
+
_scrollMargin = createMlvEditorLiveScrollSides('margin', () => untracked(this.toolbarPosition), () => untracked(() => this._obscuredToolbarExtent()));
|
|
10756
|
+
/** @private Live ProseMirror `scrollThreshold`, paired with `_scrollMargin`. */
|
|
10757
|
+
_scrollThreshold = createMlvEditorLiveScrollSides('threshold', () => untracked(this.toolbarPosition), () => untracked(() => this._obscuredToolbarExtent()));
|
|
9395
10758
|
/** @private Optional translated accessible defaults. */
|
|
9396
10759
|
_i18n = inject(MLV_EDITOR_I18N, { optional: true });
|
|
9397
10760
|
/** @private Announces block moves politely; the editor content itself is not a live region. */
|
|
@@ -9418,13 +10781,7 @@ class MlvEditor extends MlvSignalFormControlBase {
|
|
|
9418
10781
|
_blurCheckQueued = false;
|
|
9419
10782
|
/** @private The last boundary event, retained until the microtask resolves focus ownership. */
|
|
9420
10783
|
_pendingBlurEvent;
|
|
9421
|
-
/** @private
|
|
9422
|
-
_onHostFocusIn = (event) => this._handleCompositeFocusIn(event);
|
|
9423
|
-
/** @private Stable capture listener for focus leaving the physical host. */
|
|
9424
|
-
_onHostFocusOut = (event) => this._handleCompositeFocusOut(event);
|
|
9425
|
-
/** @private Stable capture listener that blocks disabled projected controls. */
|
|
9426
|
-
_onDisabledPointerOrClick = (event) => this._blockDisabledInteraction(event);
|
|
9427
|
-
/** @private Stable capture listener that blocks disabled keyboard activation. */
|
|
10784
|
+
/** @private Blocks disabled keyboard activation; the other three subscribe to their handler directly. */
|
|
9428
10785
|
_onDisabledKeydown = (event) => {
|
|
9429
10786
|
if (event.key !== 'Enter' &&
|
|
9430
10787
|
event.key !== ' ' &&
|
|
@@ -9467,6 +10824,10 @@ class MlvEditor extends MlvSignalFormControlBase {
|
|
|
9467
10824
|
this._wasDisabled = disabled;
|
|
9468
10825
|
if (readonly && !this._wasReadonly) {
|
|
9469
10826
|
this._abortInFlightUploads();
|
|
10827
|
+
// Constructor effect: registered on the parent view, so it runs
|
|
10828
|
+
// before this component's template (and the wrapper-stamped band
|
|
10829
|
+
// `@if`) refreshes; focus in the band or its popups can still move.
|
|
10830
|
+
untracked(() => this._closeBarForReadonly());
|
|
9470
10831
|
}
|
|
9471
10832
|
this._wasReadonly = readonly;
|
|
9472
10833
|
if (!editor)
|
|
@@ -9560,7 +10921,11 @@ class MlvEditor extends MlvSignalFormControlBase {
|
|
|
9560
10921
|
extensions: selectedExtensions,
|
|
9561
10922
|
editable: !this.computedDisabled() && !this.readonly(),
|
|
9562
10923
|
enableContentCheck: true,
|
|
9563
|
-
editorProps: {
|
|
10924
|
+
editorProps: {
|
|
10925
|
+
attributes: this._editorAttributes(),
|
|
10926
|
+
scrollMargin: this._scrollMargin,
|
|
10927
|
+
scrollThreshold: this._scrollThreshold,
|
|
10928
|
+
},
|
|
9564
10929
|
onTransaction: ({ editor: changedEditor, transaction }) => {
|
|
9565
10930
|
if (this._destroyed)
|
|
9566
10931
|
return;
|
|
@@ -9604,6 +10969,23 @@ class MlvEditor extends MlvSignalFormControlBase {
|
|
|
9604
10969
|
});
|
|
9605
10970
|
}
|
|
9606
10971
|
}
|
|
10972
|
+
/**
|
|
10973
|
+
* @protected Focuses the editable content when the control's own
|
|
10974
|
+
* `<mlv-label>` is clicked.
|
|
10975
|
+
*
|
|
10976
|
+
* ProseMirror's contenteditable root is focusable but not HTML-labelable, so
|
|
10977
|
+
* `<label for>` can never name it and the native click-to-focus a field
|
|
10978
|
+
* label owes its control never ran — the visible label was inert (#216), the
|
|
10979
|
+
* same gap the three date/time pickers close. `readonly` still focuses (a
|
|
10980
|
+
* read-only `<textarea>` does), `disabled` returns early (the host is
|
|
10981
|
+
* `inert` anyway), and Tiptap's `focus` command restores the stored
|
|
10982
|
+
* selection rather than dropping the caret at the document start.
|
|
10983
|
+
*/
|
|
10984
|
+
_onLabelClick() {
|
|
10985
|
+
if (this.computedDisabled())
|
|
10986
|
+
return;
|
|
10987
|
+
this._editor()?.commands.focus();
|
|
10988
|
+
}
|
|
9607
10989
|
/** Clears the document through Tiptap when the shared form wrapper is clearable. */
|
|
9608
10990
|
clearValue() {
|
|
9609
10991
|
if (this.computedDisabled() || this.readonly())
|
|
@@ -9690,7 +11072,15 @@ class MlvEditor extends MlvSignalFormControlBase {
|
|
|
9690
11072
|
return;
|
|
9691
11073
|
const requestedFormat = this.format();
|
|
9692
11074
|
editor.setOptions({
|
|
9693
|
-
|
|
11075
|
+
// `setOptions` replaces Tiptap's stored `editorProps` wholesale. The
|
|
11076
|
+
// live view keeps its props (ProseMirror's `setProps` merges), but a
|
|
11077
|
+
// later `mount()` rebuilds the view from the stored copy, so the scroll
|
|
11078
|
+
// props travel with every call.
|
|
11079
|
+
editorProps: {
|
|
11080
|
+
attributes: this._editorAttributes(),
|
|
11081
|
+
scrollMargin: this._scrollMargin,
|
|
11082
|
+
scrollThreshold: this._scrollThreshold,
|
|
11083
|
+
},
|
|
9694
11084
|
});
|
|
9695
11085
|
this._synchronizeContentSurfaceState(editor);
|
|
9696
11086
|
const rawIncoming = this.value();
|
|
@@ -9899,6 +11289,39 @@ class MlvEditor extends MlvSignalFormControlBase {
|
|
|
9899
11289
|
recoverable: true,
|
|
9900
11290
|
});
|
|
9901
11291
|
}
|
|
11292
|
+
/**
|
|
11293
|
+
* @private Block extent the toolbar hides on its own side, in px.
|
|
11294
|
+
*
|
|
11295
|
+
* Only a sticky bar hides anything: its height plus its offset, at its
|
|
11296
|
+
* nearest scroll container. A docked bar sits outside the content, and the
|
|
11297
|
+
* floating bubble is an overlay that follows the selection, clear of it by
|
|
11298
|
+
* construction.
|
|
11299
|
+
* Sticky applies only to an uncapped bar (`_stickyToolbar`), so the margin
|
|
11300
|
+
* never lands on a capped viewport's own edge.
|
|
11301
|
+
*
|
|
11302
|
+
* Read by ProseMirror at scroll time, never for layout. ProseMirror applies
|
|
11303
|
+
* one margin at every scroll ancestor. For an uncapped editor the page is
|
|
11304
|
+
* usually the only one. With a consumer scroll container in between, the
|
|
11305
|
+
* band sticks to that container, where the margin is right; the page, one
|
|
11306
|
+
* scroll ancestor further out, also keeps the caret that far from its edge
|
|
11307
|
+
* although no band is there, which errs toward visibility.
|
|
11308
|
+
*/
|
|
11309
|
+
_obscuredToolbarExtent() {
|
|
11310
|
+
if (!this._stickyToolbar())
|
|
11311
|
+
return 0;
|
|
11312
|
+
const band = this._toolbarBand()?.nativeElement;
|
|
11313
|
+
if (!band)
|
|
11314
|
+
return 0;
|
|
11315
|
+
const side = this.toolbarPosition() === 'top'
|
|
11316
|
+
? 'inset-block-start'
|
|
11317
|
+
: 'inset-block-end';
|
|
11318
|
+
const view = band.ownerDocument.defaultView;
|
|
11319
|
+
const offset = view
|
|
11320
|
+
? Number.parseFloat(view.getComputedStyle(band).getPropertyValue(side)) ||
|
|
11321
|
+
0
|
|
11322
|
+
: 0;
|
|
11323
|
+
return band.getBoundingClientRect().height + offset;
|
|
11324
|
+
}
|
|
9902
11325
|
/** @private Builds the exact accessible attributes assigned to Tiptap's contenteditable root. */
|
|
9903
11326
|
_editorAttributes() {
|
|
9904
11327
|
const attributes = {
|
|
@@ -9936,6 +11359,14 @@ class MlvEditor extends MlvSignalFormControlBase {
|
|
|
9936
11359
|
if (this.readonly()) {
|
|
9937
11360
|
attributes['aria-readonly'] = 'true';
|
|
9938
11361
|
}
|
|
11362
|
+
// The selection bubble stays hidden until a selection exists, so the key
|
|
11363
|
+
// that reaches it is the only hint assistive technology gets. Disabled or
|
|
11364
|
+
// `readonly`, the bubble never shows and the key does nothing.
|
|
11365
|
+
if (this.toolbarAppearance() === 'floating' &&
|
|
11366
|
+
!this.computedDisabled() &&
|
|
11367
|
+
!this.readonly()) {
|
|
11368
|
+
attributes['aria-keyshortcuts'] = 'Alt+F10';
|
|
11369
|
+
}
|
|
9939
11370
|
return attributes;
|
|
9940
11371
|
}
|
|
9941
11372
|
/** @internal Capture handler for focus entering the host or an owned overlay root. */
|
|
@@ -9988,21 +11419,39 @@ class MlvEditor extends MlvSignalFormControlBase {
|
|
|
9988
11419
|
(this._host.nativeElement.contains(node) ||
|
|
9989
11420
|
this._overlayRegistry.contains(node)));
|
|
9990
11421
|
}
|
|
9991
|
-
/**
|
|
11422
|
+
/**
|
|
11423
|
+
* @private Subscribes the composite's host listeners, because overlay focus
|
|
11424
|
+
* happens outside Angular's view tree.
|
|
11425
|
+
*
|
|
11426
|
+
* Every one is registered in the **capture** phase, which is load-bearing
|
|
11427
|
+
* rather than incidental: ProseMirror binds its own handlers to `view.dom`,
|
|
11428
|
+
* a descendant of this host, so only a capture-phase listener on the host
|
|
11429
|
+
* runs ahead of them. The disabled handlers depend on that to call
|
|
11430
|
+
* `stopImmediatePropagation()` while the event is still travelling down —
|
|
11431
|
+
* from the bubble phase ProseMirror would already have acted on it. Capture
|
|
11432
|
+
* is carried through `fromEvent`'s third argument, and `editor-focus.spec.ts`
|
|
11433
|
+
* pins the ordering against descendant listeners.
|
|
11434
|
+
*
|
|
11435
|
+
* Called from `ngAfterViewInit`, which is not an injection context, so
|
|
11436
|
+
* `takeUntilDestroyed` is given the ref explicitly.
|
|
11437
|
+
*/
|
|
9992
11438
|
_bindCompositeFocusEvents() {
|
|
9993
11439
|
const host = this._host.nativeElement;
|
|
9994
|
-
host
|
|
9995
|
-
|
|
9996
|
-
|
|
9997
|
-
host
|
|
9998
|
-
|
|
9999
|
-
|
|
10000
|
-
|
|
10001
|
-
|
|
10002
|
-
|
|
10003
|
-
|
|
10004
|
-
|
|
10005
|
-
|
|
11440
|
+
fromEvent(host, 'focusin', { capture: true })
|
|
11441
|
+
.pipe(takeUntilDestroyed(this._destroyRef))
|
|
11442
|
+
.subscribe((event) => this._handleCompositeFocusIn(event));
|
|
11443
|
+
fromEvent(host, 'focusout', { capture: true })
|
|
11444
|
+
.pipe(takeUntilDestroyed(this._destroyRef))
|
|
11445
|
+
.subscribe((event) => this._handleCompositeFocusOut(event));
|
|
11446
|
+
fromEvent(host, 'pointerdown', { capture: true })
|
|
11447
|
+
.pipe(takeUntilDestroyed(this._destroyRef))
|
|
11448
|
+
.subscribe((event) => this._blockDisabledInteraction(event));
|
|
11449
|
+
fromEvent(host, 'click', { capture: true })
|
|
11450
|
+
.pipe(takeUntilDestroyed(this._destroyRef))
|
|
11451
|
+
.subscribe((event) => this._blockDisabledInteraction(event));
|
|
11452
|
+
fromEvent(host, 'keydown', { capture: true })
|
|
11453
|
+
.pipe(takeUntilDestroyed(this._destroyRef))
|
|
11454
|
+
.subscribe((event) => this._onDisabledKeydown(event));
|
|
10006
11455
|
}
|
|
10007
11456
|
/** @private Stops all pointer/click/activation-key command surfaces while disabled. */
|
|
10008
11457
|
_blockDisabledInteraction(event) {
|
|
@@ -10033,6 +11482,7 @@ class MlvEditor extends MlvSignalFormControlBase {
|
|
|
10033
11482
|
'aria-readonly',
|
|
10034
11483
|
'tabindex',
|
|
10035
11484
|
'aria-invalid',
|
|
11485
|
+
'aria-keyshortcuts',
|
|
10036
11486
|
];
|
|
10037
11487
|
for (const name of managedAttributes) {
|
|
10038
11488
|
const value = attributes[name];
|
|
@@ -10044,6 +11494,29 @@ class MlvEditor extends MlvSignalFormControlBase {
|
|
|
10044
11494
|
}
|
|
10045
11495
|
}
|
|
10046
11496
|
}
|
|
11497
|
+
/**
|
|
11498
|
+
* @private `readonly` just turned on over a docked bar, which this render
|
|
11499
|
+
* tears down (#498). Every popup the editor owns closes, and focus in the
|
|
11500
|
+
* band or in such a popup returns to the content first, through the same
|
|
11501
|
+
* registry call the selection bubble makes. Called from the constructor
|
|
11502
|
+
* effect, which is registered on the parent view, so it runs before this
|
|
11503
|
+
* component's template (and the wrapper-stamped band `@if`) refreshes: the
|
|
11504
|
+
* band and its focus are still there. A disabled editor is left alone, like
|
|
11505
|
+
* the bubble's own focus return: it takes no focus, even from a projected
|
|
11506
|
+
* consumer control, which the editor does not disable and which can hold
|
|
11507
|
+
* focus in the band of a disabled editor.
|
|
11508
|
+
*/
|
|
11509
|
+
_closeBarForReadonly() {
|
|
11510
|
+
if (this.toolbarAppearance() !== 'bar' || this._barRendered())
|
|
11511
|
+
return;
|
|
11512
|
+
if (this.computedDisabled())
|
|
11513
|
+
return;
|
|
11514
|
+
const band = this._toolbarBand()?.nativeElement;
|
|
11515
|
+
const editor = this._editor();
|
|
11516
|
+
if (!band || !editor || editor.isDestroyed)
|
|
11517
|
+
return;
|
|
11518
|
+
this._overlayRegistry.closeForReadonly(band, editor.view.dom, () => mlvEditorFocusContent(editor));
|
|
11519
|
+
}
|
|
10047
11520
|
/** @private Aborts registered upload work while preserving the document/editor instance. */
|
|
10048
11521
|
_abortInFlightUploads() {
|
|
10049
11522
|
this._uploadAbortRegistry.abortAll();
|
|
@@ -10056,8 +11529,8 @@ class MlvEditor extends MlvSignalFormControlBase {
|
|
|
10056
11529
|
this._editor.set(null);
|
|
10057
11530
|
editor?.destroy();
|
|
10058
11531
|
}
|
|
10059
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
10060
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.
|
|
11532
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditor, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
11533
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: MlvEditor, isStandalone: true, selector: "mlv-editor", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, format: { classPropertyName: "format", publicName: "format", isSignal: true, isRequired: false, transformFunction: null }, contentWidth: { classPropertyName: "contentWidth", publicName: "contentWidth", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, minHeight: { classPropertyName: "minHeight", publicName: "minHeight", isSignal: true, isRequired: false, transformFunction: null }, maxHeight: { classPropertyName: "maxHeight", publicName: "maxHeight", isSignal: true, isRequired: false, transformFunction: null }, toolbarPosition: { classPropertyName: "toolbarPosition", publicName: "toolbarPosition", isSignal: true, isRequired: false, transformFunction: null }, toolbarAppearance: { classPropertyName: "toolbarAppearance", publicName: "toolbarAppearance", isSignal: true, isRequired: false, transformFunction: null }, toolbarSticky: { classPropertyName: "toolbarSticky", publicName: "toolbarSticky", isSignal: true, isRequired: false, transformFunction: null }, extensions: { classPropertyName: "extensions", publicName: "extensions", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, characterLimit: { classPropertyName: "characterLimit", publicName: "characterLimit", isSignal: true, isRequired: false, transformFunction: null }, ariaLabelledBy: { classPropertyName: "ariaLabelledBy", publicName: "ariaLabelledBy", isSignal: true, isRequired: false, transformFunction: null }, ariaDescribedBy: { classPropertyName: "ariaDescribedBy", publicName: "ariaDescribedBy", isSignal: true, isRequired: false, transformFunction: null }, imageUploader: { classPropertyName: "imageUploader", publicName: "imageUploader", isSignal: true, isRequired: false, transformFunction: null }, imageUploadOptions: { classPropertyName: "imageUploadOptions", publicName: "imageUploadOptions", isSignal: true, isRequired: false, transformFunction: null }, aiProvider: { classPropertyName: "aiProvider", publicName: "aiProvider", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", editorReady: "editorReady", focus: "focus", blur: "blur", selectionChange: "selectionChange", transaction: "transaction", editorError: "editorError", imageUploadSuccess: "imageUploadSuccess", imageUploadFailure: "imageUploadFailure", imageUploadCancelled: "imageUploadCancelled" }, host: { properties: { "class": "\"mlv-editor--content-width-\" + contentWidth()", "class.mlv-editor--disabled": "computedDisabled()", "class.mlv-editor--readonly": "readonly()", "class.mlv-editor--focused": "focused()", "class.mlv-editor--capped": "_capped()", "class.mlv-editor--toolbar-top": "toolbarPosition() === \"top\"", "class.mlv-editor--toolbar-bottom": "toolbarPosition() === \"bottom\"", "class.mlv-editor--toolbar-bar": "toolbarAppearance() === \"bar\"", "class.mlv-editor--toolbar-floating": "toolbarAppearance() === \"floating\"", "class.mlv-editor--toolbar-sticky": "_stickyToolbar()", "attr.aria-disabled": "computedDisabled() || null", "attr.inert": "computedDisabled() ? \"\" : null", "style.--mlv-editor-zoom": "zoom() / 100", "style.--mlv-editor-height": "_heightStyle()", "style.--mlv-editor-min-height": "_minHeightStyle()", "style.--mlv-editor-max-height": "_maxHeightStyle()" }, classAttribute: "mlv-editor" }, providers: [
|
|
10061
11534
|
MlvEditorOverlayRegistry,
|
|
10062
11535
|
MlvEditorToolbarRevision,
|
|
10063
11536
|
MlvEditorToolbarRovingRegistry,
|
|
@@ -10116,9 +11589,9 @@ class MlvEditor extends MlvSignalFormControlBase {
|
|
|
10116
11589
|
}, inject(MLV_EDITOR_IMAGE_UPLOADER, { optional: true }) ?? undefined, inject(MLV_EDITOR_UPLOAD_ABORT_REGISTRY));
|
|
10117
11590
|
},
|
|
10118
11591
|
},
|
|
10119
|
-
], queries: [{ propertyName: "_toolbarDefs", predicate: MlvEditorToolbarDef, isSignal: true }, { propertyName: "_toolbarStartDefs", predicate: MlvEditorToolbarStartDef, isSignal: true }, { propertyName: "_toolbarEndDefs", predicate: MlvEditorToolbarEndDef, isSignal: true }], viewQueries: [{ propertyName: "_content", first: true, predicate: ["content"], descendants: true, isSignal: true }, { propertyName: "_view", first: true, predicate: ["view"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<mlv-form-control-wrapper (clear)=\"clearValue()\">\n @if (label() || hint()) {\n <mlv-label [for]=\"id()\" [id]=\"_labelId()\">\n {{ label() }} @if (hint()) {\n <mlv-hint>{{ hint() }}</mlv-hint>\n }\n </mlv-label>\n }\n\n <ng-template mlvFormControlWrapperControl>\n <div class=\"mlv-editor__surface\">\n <div\n mlvEditorToolbarRoot\n #toolbarRoot=\"mlvEditorToolbarRoot\"\n class=\"mlv-editor__toolbar\"\n [ariaLabel]=\"_toolbarAriaLabel()\"\n [disabled]=\"computedDisabled()\"\n >\n @if (_hasToolbarDef()) {\n <ng-content select=\"[mlvEditorToolbar]\" />\n @for (definition of _toolbarDefs(); track definition) { @if\n (definition.templateRef; as toolbarTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"toolbarTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: _toolbarContext }\"\n />\n } } } @else {\n <ng-content select=\"[mlvEditorToolbarStart]\" />\n @for (definition of _toolbarStartDefs(); track definition) { @if\n (definition.templateRef; as startTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"startTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: _toolbarContext }\"\n />\n } }\n <div mlvFade class=\"mlv-editor__toolbar-scroll\">\n <mlv-toolbar>\n <mlv-editor-undo-redo />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n />\n <mlv-editor-zoom />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n />\n <mlv-editor-heading />\n <mlv-editor-list />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n [hidden]=\"toolbarRoot.narrow()\"\n />\n <mlv-editor-inline-marks [hidden]=\"toolbarRoot.narrow()\" />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n [hidden]=\"toolbarRoot.narrow()\"\n />\n <mlv-editor-text-color />\n <mlv-editor-highlight />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n [hidden]=\"toolbarRoot.narrow()\"\n />\n <mlv-editor-alignment [hidden]=\"toolbarRoot.narrow()\" />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n [hidden]=\"toolbarRoot.narrow()\"\n />\n <mlv-editor-link />\n <mlv-editor-table />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n [hidden]=\"toolbarRoot.narrow()\"\n />\n <mlv-editor-block-insert [hidden]=\"toolbarRoot.narrow()\" />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n [hidden]=\"toolbarRoot.narrow()\"\n />\n <mlv-editor-image-upload />\n </mlv-toolbar>\n </div>\n <mlv-editor-toolbar-overflow />\n @for (definition of _toolbarEndDefs(); track definition) { @if\n (definition.templateRef; as endTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"endTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: _toolbarContext }\"\n />\n } }\n <ng-content select=\"[mlvEditorToolbarEnd]\" />\n }\n </div>\n\n <div class=\"mlv-editor__viewport\">\n <div #view class=\"mlv-editor__view\">\n <div #content class=\"mlv-editor__content\"></div>\n <mlv-editor-table-controls [layer]=\"view\" />\n </div>\n </div>\n <mlv-editor-image-upload-status />\n </div>\n </ng-template>\n\n @if (message()) {\n <mlv-message [state]=\"resolvedState()\" [id]=\"id() + '-message'\">\n {{ message() }}\n </mlv-message>\n }\n</mlv-form-control-wrapper>\n\n<div\n class=\"mlv-editor__status\"\n [attr.aria-disabled]=\"computedDisabled() || null\"\n>\n <ng-content select=\"[mlvEditorStatus]\" />\n <mlv-spacer />\n <mlv-editor-status [characterLimit]=\"characterLimit()\" />\n</div>\n", styles: ["@layer mlv.components{.mlv-editor{--mlv-editor-gutter: var(--mlv-spacing-6);--mlv-editor-measure: 45rem;--mlv-editor-drop-gap: 1.5rem;box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor *{box-sizing:border-box}.mlv-editor{display:block;color:var(--mlv-text-primary)}.mlv-editor .mlv-form-control-wrapper__control-container{height:auto;min-height:var(--form-ctrl-height);padding:0;overflow:hidden;background:var(--mlv-background-base);transition:border-color var(--mlv-duration-fast) var(--mlv-ease-default),box-shadow var(--mlv-duration-fast) var(--mlv-ease-default)}.mlv-editor .mlv-form-control-wrapper__control-row{align-items:stretch}.mlv-form-control-wrapper--state-error .mlv-editor__surface{box-shadow:inset 0 0 0 var(--mlv-stroke-width) var(--mlv-border-error)}.mlv-editor__surface{display:flex;flex:1 1 auto;flex-direction:column;min-width:0;width:100%;background:var(--mlv-elevation-bg-3)}.mlv-editor__toolbar{display:flex;flex-wrap:nowrap;align-items:center;gap:var(--mlv-spacing-1);min-width:0;padding:var(--mlv-padding-xs);overflow:hidden;border-bottom:var(--mlv-stroke-width) solid var(--mlv-border-normal);background:var(--mlv-elevation-bg-3)}.mlv-editor__toolbar [hidden]{display:none!important}.mlv-editor__toolbar-scroll{flex:1 1 auto;min-width:0}.mlv-editor__toolbar-scroll>.mlv-toolbar{width:max-content;min-width:100%;flex-wrap:nowrap}.mlv-editor .mlv-editor-toolbar__separator{flex:0 0 auto;min-height:var(--mlv-height-s);margin-inline:var(--mlv-spacing-1)}.mlv-editor .mlv-editor-toolbar__overflow,.mlv-editor .mlv-editor-undo-redo,.mlv-editor .mlv-editor-inline-marks,.mlv-editor .mlv-editor-block-insert{display:inline-flex;flex:0 0 auto;flex-direction:row;align-items:center;gap:var(--mlv-spacing-1)}.mlv-editor mlv-editor-zoom,.mlv-editor mlv-editor-heading,.mlv-editor mlv-editor-list,.mlv-editor mlv-editor-text-color,.mlv-editor mlv-editor-highlight,.mlv-editor mlv-editor-alignment,.mlv-editor mlv-editor-link,.mlv-editor mlv-editor-table,.mlv-editor mlv-editor-image-upload{flex:0 0 auto}.mlv-editor__viewport{min-block-size:8rem;overflow:auto;overscroll-behavior:contain}.mlv-editor__view{position:relative;min-inline-size:100%;transform:scale(var(--mlv-editor-zoom, 1));transform-origin:left top;transition:transform var(--mlv-duration-fast) var(--mlv-ease-default)}.mlv-editor__content{min-block-size:8rem;padding-block:var(--mlv-spacing-2);padding-inline:var(--mlv-editor-gutter);border:0;outline:none}.mlv-editor__block-handle{position:absolute;inset-inline-start:max(0rem,(100% - 2 * var(--mlv-editor-gutter) - var(--mlv-editor-measure)) / 2);display:inline-flex;align-items:center;justify-content:center;inline-size:var(--mlv-editor-gutter);block-size:var(--mlv-height-xs);border-radius:var(--mlv-radius-s);color:var(--mlv-text-tertiary);cursor:grab;opacity:0;pointer-events:none;transition:opacity var(--mlv-duration-fast) var(--mlv-ease-default)}.mlv-editor__block-handle svg{inline-size:var(--mlv-spacing-4);block-size:var(--mlv-spacing-4);fill:currentcolor}.mlv-editor__block-handle:hover{background-color:var(--mlv-background-neutral-1-hover);color:var(--mlv-text-secondary)}.mlv-editor__block-handle[data-visible=true]{opacity:1;pointer-events:auto}.mlv-editor__drop-indicator{position:absolute;inset-inline:calc(max(0rem,(100% - 2 * var(--mlv-editor-gutter) - var(--mlv-editor-measure)) / 2) + var(--mlv-editor-gutter));block-size:var(--mlv-stroke-width-medium);border-radius:var(--mlv-radius-full);background-color:var(--mlv-border-focus);opacity:0;pointer-events:none;transform:translateY(calc(var(--mlv-editor-drop-gap) / 2 - var(--mlv-stroke-width-medium) / 2));transition:top var(--mlv-duration-normal) var(--mlv-ease-out),opacity var(--mlv-duration-fast) var(--mlv-ease-default)}.mlv-editor__drop-indicator[data-visible=true]{opacity:1}.mlv-editor .mlv-editor__block--dragging{opacity:var(--mlv-disabled-opacity)}.mlv-editor .ProseMirror--block-dragging>*{transition:transform var(--mlv-duration-normal) var(--mlv-ease-out)}.mlv-editor__status{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:var(--mlv-spacing-2);min-block-size:var(--mlv-spacing-4);margin-block-start:var(--mlv-spacing-1)}.mlv-editor__upload-placeholder{position:relative;display:inline-block;inline-size:var(--mlv-height-l);block-size:var(--mlv-height-l);margin-inline:var(--mlv-spacing-1);border:var(--mlv-stroke-width) dashed var(--mlv-border-focus);border-radius:var(--mlv-radius-m);background:var(--mlv-background-accent-1-pale);vertical-align:middle}.mlv-editor__upload-placeholder:after{position:absolute;inset:var(--mlv-spacing-2);border:var(--mlv-stroke-width-medium) solid var(--mlv-border-normal);border-block-start-color:var(--mlv-border-focus);border-radius:var(--mlv-radius-full);animation:mlv-editor-upload-spin var(--mlv-duration-slower) var(--mlv-ease-linear) infinite;content:\"\"}.mlv-editor__ai-streaming{border-radius:var(--mlv-radius-xs);background-color:var(--mlv-background-accent-1-pale)}.mlv-editor__ai-streaming-chunk{animation:mlv-editor-ai-chunk-in var(--mlv-duration-normal) var(--mlv-ease-out)}.mlv-editor__ai-caret{position:relative;display:inline-block;inline-size:0;block-size:1em;pointer-events:none}.mlv-editor__ai-caret:after{position:absolute;inset-block:0;inset-inline-start:.0625rem;inline-size:.35em;border-radius:var(--mlv-radius-xs);background:linear-gradient(to bottom,var(--mlv-background-accent-1),var(--mlv-background-accent-2));content:\"\";animation:mlv-editor-ai-caret-pulse var(--mlv-duration-sluggish) var(--mlv-ease-in-out) infinite}.mlv-editor__ai-suggestion-insert{border-radius:var(--mlv-radius-xs);background-color:var(--mlv-background-success-pale)}.mlv-editor__ai-suggestion-delete{border-radius:var(--mlv-radius-xs);background-color:var(--mlv-background-danger-1-pale);color:var(--mlv-text-negative);text-decoration-line:line-through}.mlv-editor__ai-suggestion-current{outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:var(--mlv-focus-ring-offset)}.mlv-editor--content-width-default{--mlv-editor-measure: 45rem}.mlv-editor--content-width-wide{--mlv-editor-measure: 60rem}.mlv-editor--content-width-full{--mlv-editor-measure: 100%}.mlv-editor--focused .mlv-form-control-wrapper__control-container{border-color:var(--mlv-border-focus);box-shadow:var(--mlv-focus-ring)}.mlv-editor--readonly .ProseMirror{cursor:text}.mlv-editor--readonly .ProseMirror ::selection{background:var(--mlv-background-accent-1-pale)}.mlv-editor--disabled{opacity:var(--mlv-disabled-opacity)}.mlv-editor--disabled .mlv-editor__surface,.mlv-editor--disabled .mlv-editor__status{pointer-events:none}.mlv-editor--disabled .mlv-editor__content,.mlv-editor--disabled .ProseMirror{cursor:default;-webkit-user-select:none;user-select:none}.mlv-editor .ProseMirror{min-block-size:inherit;max-inline-size:var(--mlv-editor-measure);margin-inline:auto;color:var(--mlv-text-primary);font-family:var(--mlv-typography-family-text);font-size:var(--mlv-typography-body-m-size);font-weight:var(--mlv-typography-body-m-weight);line-height:var(--mlv-typography-body-m-line-height);overflow-wrap:anywhere;white-space:pre-wrap}.mlv-editor .ProseMirror:focus-visible{outline:none}.mlv-editor .ProseMirror>*:first-child{margin-block-start:0}.mlv-editor .ProseMirror>*:last-child{margin-block-end:0}.mlv-editor .ProseMirror p{margin-block:0 var(--mlv-spacing-3)}.mlv-editor .ProseMirror h1,.mlv-editor .ProseMirror h2,.mlv-editor .ProseMirror h3,.mlv-editor .ProseMirror h4,.mlv-editor .ProseMirror h5,.mlv-editor .ProseMirror h6{margin-block:var(--mlv-spacing-6) var(--mlv-spacing-3);color:var(--mlv-text-heading);font-family:var(--mlv-typography-family-heading)}.mlv-editor .ProseMirror h1{font-size:var(--mlv-typography-heading-h1-size);font-weight:var(--mlv-typography-heading-h1-weight);line-height:var(--mlv-typography-heading-h1-line-height)}.mlv-editor .ProseMirror h2{font-size:var(--mlv-typography-heading-h2-size);font-weight:var(--mlv-typography-heading-h2-weight);line-height:var(--mlv-typography-heading-h2-line-height)}.mlv-editor .ProseMirror h3{font-size:var(--mlv-typography-heading-h3-size);font-weight:var(--mlv-typography-heading-h3-weight);line-height:var(--mlv-typography-heading-h3-line-height)}.mlv-editor .ProseMirror h4{font-size:var(--mlv-typography-heading-h4-size);font-weight:var(--mlv-typography-heading-h4-weight);line-height:var(--mlv-typography-heading-h4-line-height)}.mlv-editor .ProseMirror h5{font-size:var(--mlv-typography-heading-h5-size);font-weight:var(--mlv-typography-heading-h5-weight);line-height:var(--mlv-typography-heading-h5-line-height)}.mlv-editor .ProseMirror h6{font-size:var(--mlv-typography-heading-h6-size);font-weight:var(--mlv-typography-heading-h6-weight);line-height:var(--mlv-typography-heading-h6-line-height)}.mlv-editor .ProseMirror h1{letter-spacing:var(--mlv-typography-heading-h1-letter-spacing)}.mlv-editor .ProseMirror h2{letter-spacing:var(--mlv-typography-heading-h2-letter-spacing)}.mlv-editor .ProseMirror h3,.mlv-editor .ProseMirror h4,.mlv-editor .ProseMirror h5,.mlv-editor .ProseMirror h6{letter-spacing:var(--mlv-letter-spacing-normal)}.mlv-editor .ProseMirror strong{font-weight:var(--mlv-font-weight-bold)}.mlv-editor .ProseMirror em{font-style:italic}.mlv-editor .ProseMirror s{text-decoration:line-through}.mlv-editor .ProseMirror u{text-underline-offset:var(--mlv-spacing-0-5)}.mlv-editor .ProseMirror a{color:var(--mlv-text-action);text-decoration:underline;text-underline-offset:var(--mlv-spacing-0-5);transition:color var(--mlv-duration-fast) var(--mlv-ease-default)}.mlv-editor .ProseMirror a:visited{color:var(--mlv-text-action)}.mlv-editor .ProseMirror a:hover{color:var(--mlv-text-action-hover)}.mlv-editor .ProseMirror a:focus-visible{border-radius:var(--mlv-radius-xs);outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:var(--mlv-focus-ring-offset)}.mlv-editor .ProseMirror ul,.mlv-editor .ProseMirror ol{padding-inline-start:var(--mlv-spacing-6);margin-block:0 var(--mlv-spacing-3)}.mlv-editor .ProseMirror ul ul,.mlv-editor .ProseMirror ul ol,.mlv-editor .ProseMirror ol ul,.mlv-editor .ProseMirror ol ol{margin-block:var(--mlv-spacing-1) 0}.mlv-editor .ProseMirror ul{list-style:disc}.mlv-editor .ProseMirror ul ul{list-style:circle}.mlv-editor .ProseMirror ol{list-style:decimal}.mlv-editor .ProseMirror li{margin-block:var(--mlv-spacing-1)}.mlv-editor .ProseMirror li>p{margin:0}.mlv-editor .ProseMirror ul[data-type=taskList]{padding:0;list-style:none}.mlv-editor .ProseMirror ul[data-type=taskList] li[data-type=taskItem]{display:flex;align-items:flex-start;gap:var(--mlv-spacing-2)}.mlv-editor .ProseMirror ul[data-type=taskList] li[data-type=taskItem]>label{display:inline-flex;align-items:center;min-block-size:var(--mlv-height-xs);cursor:pointer;-webkit-user-select:none;user-select:none}.mlv-editor .ProseMirror ul[data-type=taskList] li[data-type=taskItem]>label input{accent-color:var(--mlv-background-accent-1)}.mlv-editor .ProseMirror ul[data-type=taskList] li[data-type=taskItem]>label input:focus-visible{outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:var(--mlv-focus-ring-offset)}.mlv-editor .ProseMirror ul[data-type=taskList] li[data-type=taskItem]>div{flex:1 1 auto;min-width:0}.mlv-editor .ProseMirror blockquote{padding-inline-start:var(--mlv-spacing-4);margin:var(--mlv-spacing-4) 0;border-inline-start:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);color:var(--mlv-text-secondary)}.mlv-editor .ProseMirror code{padding:var(--mlv-spacing-0-5) var(--mlv-spacing-1);border-radius:var(--mlv-radius-s);background:var(--mlv-background-neutral-1);color:var(--mlv-text-primary);font-family:var(--mlv-typography-family-code);font-size:var(--mlv-typography-code-size)}.mlv-editor .ProseMirror pre{padding:var(--mlv-padding-m);margin-block:var(--mlv-spacing-4);overflow:auto;border:var(--mlv-stroke-width) solid var(--mlv-border-normal);border-radius:var(--mlv-radius-m);background:var(--mlv-background-sunken);color:var(--mlv-text-primary);font-family:var(--mlv-typography-family-code);font-size:var(--mlv-typography-code-size);line-height:var(--mlv-typography-body-m-line-height);tab-size:2}.mlv-editor .ProseMirror pre code{padding:0;background:transparent;color:inherit;font-size:inherit}.mlv-editor .ProseMirror hr{height:var(--mlv-stroke-width);margin-block:var(--mlv-spacing-6);border:0;background:var(--mlv-border-normal)}.mlv-editor .ProseMirror img{display:block;max-inline-size:100%;block-size:auto;margin-block:var(--mlv-spacing-4);border-radius:var(--mlv-radius-m)}.mlv-editor .ProseMirror img.ProseMirror-selectednode{outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:0}.mlv-editor .ProseMirror .ProseMirror-selectednode,.mlv-editor .ProseMirror [data-resize-state=true]{border-radius:var(--mlv-radius-s);outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:var(--mlv-spacing-1)}.mlv-editor .ProseMirror [data-resize-container]{margin-block:var(--mlv-spacing-4)}.mlv-editor .ProseMirror [data-resize-container] img{margin-block:0}.mlv-editor .ProseMirror [data-resize-container].ProseMirror-selectednode,.mlv-editor .ProseMirror [data-resize-container][data-resize-state=true]{outline:none}.mlv-editor .ProseMirror [data-resize-container].ProseMirror-selectednode>[data-resize-wrapper],.mlv-editor .ProseMirror [data-resize-container][data-resize-state=true]>[data-resize-wrapper]{border-radius:var(--mlv-radius-m);outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:0}.mlv-editor .ProseMirror [data-resize-handle],.mlv-editor .ProseMirror .resize-handle{inline-size:var(--mlv-spacing-3);block-size:var(--mlv-spacing-3);border:var(--mlv-stroke-width) solid var(--mlv-background-base);border-radius:var(--mlv-radius-full);background:var(--mlv-background-accent-1);box-shadow:var(--mlv-shadow-raised)}.mlv-editor .ProseMirror [data-resize-handle]:focus-visible,.mlv-editor .ProseMirror .resize-handle:focus-visible{outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:var(--mlv-focus-ring-offset)}.mlv-editor .ProseMirror .is-editor-empty:first-child:before{float:left;height:0;color:var(--mlv-text-placeholder);content:attr(data-placeholder);pointer-events:none}.mlv-editor .tableWrapper{margin-block:var(--mlv-spacing-3);overflow-x:auto}.mlv-editor table{width:100%;border-collapse:collapse;table-layout:fixed}.mlv-editor th,.mlv-editor td{position:relative;min-width:3rem;padding:var(--mlv-spacing-2) var(--mlv-spacing-3);border:var(--mlv-stroke-width) solid var(--mlv-border-normal);vertical-align:top}.mlv-editor th{background:var(--mlv-background-neutral-1);font-weight:var(--mlv-font-weight-semibold)}.mlv-editor .selectedCell:after{position:absolute;inset:0;z-index:1;background:var(--mlv-background-accent-1-pale);content:\"\";opacity:.55;pointer-events:none}.mlv-editor .ProseMirror-focused .selectedCell:after{box-shadow:inset 0 0 0 var(--mlv-stroke-width-medium) var(--mlv-border-focus)}.mlv-editor .column-resize-handle{position:absolute;inset-block:0;inset-inline-end:calc(var(--mlv-stroke-width) * -1);z-index:2;width:calc(var(--mlv-stroke-width) * 2);background:var(--mlv-border-focus);pointer-events:none}.mlv-editor .resize-cursor{cursor:col-resize}@keyframes mlv-editor-upload-spin{to{transform:rotate(1turn)}}@keyframes mlv-editor-ai-chunk-in{0%{opacity:0;filter:blur(.125rem)}to{opacity:1;filter:blur(0)}}@keyframes mlv-editor-ai-caret-pulse{0%,to{opacity:1}50%{opacity:.35}}@media(max-width:40rem){.mlv-editor{--mlv-editor-gutter: var(--mlv-spacing-3)}.mlv-editor__toolbar{flex-wrap:nowrap;overflow-x:auto}.mlv-editor__content{padding-block:var(--mlv-spacing-1-5)}.mlv-editor__status{justify-content:flex-start}.mlv-editor .ProseMirror h1{font-size:var(--mlv-typography-heading-h2-size)}.mlv-editor .ProseMirror h2{font-size:var(--mlv-typography-heading-h3-size)}}@media print{.mlv-editor__toolbar,.mlv-editor__status,.mlv-editor__block-handle,.mlv-editor__drop-indicator,.mlv-editor .mlv-editor-image-upload-status,.mlv-editor__upload-placeholder,.mlv-editor__ai-suggestion-delete,.mlv-editor__ai-caret{display:none}.mlv-editor__ai-suggestion-current{outline:none}.mlv-editor__ai-streaming-chunk{animation:none}.mlv-editor__viewport{overflow:visible}.mlv-editor__view{transform:none}.mlv-editor__content,.mlv-editor .ProseMirror{min-block-size:0;padding:0}.mlv-editor table{break-inside:avoid}}@media(hover:none){.mlv-editor__block-handle{display:none}}@media(prefers-reduced-motion:reduce){.mlv-editor .mlv-form-control-wrapper__control-container,.mlv-editor__view,.mlv-editor__block-handle,.mlv-editor__drop-indicator,.mlv-editor .ProseMirror--block-dragging>*,.mlv-editor .ProseMirror a{transition-duration:var(--mlv-duration-instant)}.mlv-editor__upload-placeholder:after,.mlv-editor__ai-streaming-chunk,.mlv-editor__ai-caret:after{animation:none}}.mlv-editor__drag-ghost{max-block-size:12rem;padding:var(--mlv-spacing-2);overflow:hidden;border-radius:var(--mlv-radius-m);background-color:var(--mlv-background-raised);box-shadow:var(--mlv-shadow-floating);opacity:.9;-webkit-mask-image:linear-gradient(to bottom,black 70%,transparent 100%);mask-image:linear-gradient(to bottom,black 70%,transparent 100%);scale:.85;transform-origin:top left}}\n"], dependencies: [{ kind: "component", type: MlvFormControlWrapper, selector: "mlv-form-control-wrapper", outputs: ["clear"] }, { kind: "directive", type: MlvFormControlWrapperControl, selector: "[mlvFormControlWrapperControl]" }, { kind: "component", type: MlvHint, selector: "mlv-hint" }, { kind: "component", type: MlvLabel, selector: "mlv-label", inputs: ["for", "required"] }, { kind: "component", type: MlvMessage, selector: "mlv-message", inputs: ["state"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: MlvFade, selector: "[mlvFade]", inputs: ["mlvFadeHeight", "mlvFadeSize", "mlvFadeOffset", "mlvFade"] }, { kind: "component", type: MlvToolbar, selector: "mlv-toolbar", inputs: ["gap", "equalSize"] }, { kind: "component", type: MlvDivider, selector: "mlv-divider", inputs: ["orientation", "dashed", "muted"] }, { kind: "directive", type: MlvEditorToolbarRoot, selector: "[mlvEditorToolbarRoot]", inputs: ["ariaLabel", "disabled", "orientation", "wrap"], exportAs: ["mlvEditorToolbarRoot"] }, { kind: "component", type: MlvEditorToolbarOverflow, selector: "mlv-editor-toolbar-overflow" }, { kind: "component", type: MlvEditorUndoRedo, selector: "mlv-editor-undo-redo" }, { kind: "component", type: MlvEditorZoom, selector: "mlv-editor-zoom", inputs: ["zoom", "zoomLevels", "min", "max", "fitToContainer"], outputs: ["zoomChange"] }, { kind: "component", type: MlvEditorHeading, selector: "mlv-editor-heading", inputs: ["levels"] }, { kind: "component", type: MlvEditorList, selector: "mlv-editor-list" }, { kind: "component", type: MlvEditorInlineMarks, selector: "mlv-editor-inline-marks" }, { kind: "component", type: MlvEditorTextColor, selector: "mlv-editor-text-color" }, { kind: "component", type: MlvEditorStatus, selector: "mlv-editor-status", inputs: ["characterLimit"] }, { kind: "component", type: MlvEditorHighlight, selector: "mlv-editor-highlight" }, { kind: "component", type: MlvEditorLink, selector: "mlv-editor-link", inputs: ["allowedProtocols"] }, { kind: "component", type: MlvEditorImageUpload, selector: "mlv-editor-image-upload" }, { kind: "component", type: MlvEditorImageUploadStatus, selector: "mlv-editor-image-upload-status" }, { kind: "component", type: MlvEditorTable, selector: "mlv-editor-table" }, { kind: "component", type: MlvEditorTableControls, selector: "mlv-editor-table-controls", inputs: ["layer"] }, { kind: "component", type: MlvEditorAlignment, selector: "mlv-editor-alignment" }, { kind: "component", type: MlvEditorBlockInsert, selector: "mlv-editor-block-insert" }, { kind: "component", type: MlvSpacer, selector: "mlv-spacer" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
11592
|
+
], queries: [{ propertyName: "_toolbarDefs", predicate: MlvEditorToolbarDef, isSignal: true }, { propertyName: "_toolbarStartDefs", predicate: MlvEditorToolbarStartDef, isSignal: true }, { propertyName: "_toolbarEndDefs", predicate: MlvEditorToolbarEndDef, isSignal: true }], viewQueries: [{ propertyName: "_content", first: true, predicate: ["content"], descendants: true, isSignal: true }, { propertyName: "_view", first: true, predicate: ["view"], descendants: true, isSignal: true }, { propertyName: "_toolbarBand", first: true, predicate: ["toolbarBand"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<mlv-form-control-wrapper (clear)=\"clearValue()\">\n @if (label() || hint()) {\n <mlv-label\n [for]=\"_ownLabelFor()\"\n [id]=\"_labelId()\"\n (click)=\"_onLabelClick()\"\n >\n {{ label() }}\n @if (hint()) {\n <mlv-hint>{{ hint() }}</mlv-hint>\n }\n </mlv-label>\n }\n\n <ng-template mlvFormControlWrapperControl>\n <div #surface class=\"mlv-editor__surface\">\n <!--\n Declared once and stamped by one outlet: before or after the viewport\n for the docked bar (#416), so the DOM order is always the visual\n order, with no CSS `order`; into the selection bubble's overlay for\n the floating appearance (#483). Re-stamping re-creates the view, which\n closes an open toolbar popup; projected slot content is re-attached.\n Neither stamps anything while `readonly` (#498): no band, no reserved\n space; `_toolbarRendered()` is the one switch.\n -->\n <ng-template #toolbarTemplate>\n <div #toolbarBand class=\"mlv-editor__toolbar-band\">\n <div\n mlvEditorToolbarRoot\n #toolbarRoot=\"mlvEditorToolbarRoot\"\n class=\"mlv-editor__toolbar\"\n [ariaLabel]=\"_toolbarAriaLabel()\"\n [disabled]=\"computedDisabled()\"\n [measureTarget]=\"surface\"\n >\n @if (_hasToolbarDef()) {\n <ng-content select=\"[mlvEditorToolbar]\" />\n @for (definition of _toolbarDefs(); track definition) {\n @if (definition.templateRef; as toolbarTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"toolbarTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: _toolbarContext }\"\n />\n }\n }\n } @else {\n <ng-content select=\"[mlvEditorToolbarStart]\" />\n @for (definition of _toolbarStartDefs(); track definition) {\n @if (definition.templateRef; as startTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"startTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: _toolbarContext }\"\n />\n }\n }\n <div mlvFade class=\"mlv-editor__toolbar-scroll\">\n <mlv-toolbar>\n <mlv-editor-undo-redo />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n />\n <mlv-editor-zoom />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n />\n <mlv-editor-heading />\n <mlv-editor-list />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n [hidden]=\"toolbarRoot.narrow()\"\n />\n <mlv-editor-inline-marks [hidden]=\"toolbarRoot.narrow()\" />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n [hidden]=\"toolbarRoot.narrow()\"\n />\n <mlv-editor-text-color />\n <mlv-editor-highlight />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n [hidden]=\"toolbarRoot.narrow()\"\n />\n <mlv-editor-alignment [hidden]=\"toolbarRoot.narrow()\" />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n [hidden]=\"toolbarRoot.narrow()\"\n />\n <mlv-editor-link />\n <mlv-editor-table />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n [hidden]=\"toolbarRoot.narrow()\"\n />\n <mlv-editor-block-insert [hidden]=\"toolbarRoot.narrow()\" />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n [hidden]=\"toolbarRoot.narrow()\"\n />\n <mlv-editor-image-upload />\n </mlv-toolbar>\n </div>\n <mlv-editor-toolbar-overflow />\n @for (definition of _toolbarEndDefs(); track definition) {\n @if (definition.templateRef; as endTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"endTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: _toolbarContext }\"\n />\n }\n }\n <ng-content select=\"[mlvEditorToolbarEnd]\" />\n }\n </div>\n </div>\n </ng-template>\n\n @if (_barRendered() && toolbarPosition() === 'top') {\n <ng-container [ngTemplateOutlet]=\"toolbarTemplate\" />\n }\n\n <div #viewport class=\"mlv-editor__viewport\">\n <div #view class=\"mlv-editor__view\">\n <div #content class=\"mlv-editor__content\"></div>\n <mlv-editor-table-controls [layer]=\"view\" />\n </div>\n </div>\n @if (_barRendered() && toolbarPosition() === 'bottom') {\n <ng-container [ngTemplateOutlet]=\"toolbarTemplate\" />\n }\n @if (toolbarAppearance() === 'floating') {\n <ng-template mlvEditorBubble [viewport]=\"viewport\" [capped]=\"_capped()\">\n @if (_toolbarRendered()) {\n <ng-container [ngTemplateOutlet]=\"toolbarTemplate\" />\n }\n </ng-template>\n }\n <mlv-editor-image-upload-status />\n </div>\n </ng-template>\n\n @if (message()) {\n <mlv-message [state]=\"resolvedState()\" [id]=\"id() + '-message'\">\n {{ message() }}\n </mlv-message>\n }\n</mlv-form-control-wrapper>\n\n<div\n class=\"mlv-editor__status\"\n [attr.aria-disabled]=\"computedDisabled() || null\"\n>\n <ng-content select=\"[mlvEditorStatus]\" />\n <mlv-spacer />\n <mlv-editor-status [characterLimit]=\"characterLimit()\" />\n</div>\n", styles: ["@layer mlv.components{.mlv-editor{--mlv-editor-gutter: var(--mlv-spacing-6);--mlv-editor-measure: 45rem;box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor *{box-sizing:border-box}.mlv-editor{display:block;color:var(--mlv-text-primary)}.mlv-editor .mlv-form-control-wrapper__control-container{height:auto;min-height:var(--form-ctrl-height);padding:0;overflow:clip;background:var(--mlv-background-base);transition:border-color var(--mlv-duration-fast) var(--mlv-ease-default),box-shadow var(--mlv-duration-fast) var(--mlv-ease-default)}.mlv-editor .mlv-form-control-wrapper__control-row{align-items:stretch}.mlv-form-control-wrapper--state-error .mlv-editor__surface{box-shadow:inset 0 0 0 var(--mlv-stroke-width) var(--mlv-border-error)}.mlv-editor__surface{display:flex;flex:1 1 auto;flex-direction:column;min-width:0;width:100%;background:var(--mlv-elevation-bg-3)}.mlv-editor__toolbar{display:flex;flex-wrap:nowrap;align-items:center;gap:var(--mlv-spacing-1);min-width:0;padding:var(--mlv-padding-xs);overflow:hidden;border-block-end:var(--mlv-stroke-width) solid var(--mlv-border-normal);background:var(--mlv-elevation-bg-3)}.mlv-editor__toolbar-band{display:flex;flex:0 0 auto;flex-direction:column;min-inline-size:0}.mlv-editor--toolbar-bottom .mlv-editor__toolbar{border-block-start:var(--mlv-stroke-width) solid var(--mlv-border-normal);border-block-end:0}.mlv-editor__toolbar [hidden]{display:none!important}.mlv-editor__toolbar-scroll{flex:1 1 auto;min-width:0}.mlv-editor__toolbar-scroll>.mlv-toolbar{width:max-content;min-width:100%;flex-wrap:nowrap}.mlv-editor .mlv-editor-toolbar__separator{flex:0 0 auto;min-height:var(--mlv-height-s);margin-inline:var(--mlv-spacing-1)}.mlv-editor .mlv-editor-toolbar__overflow,.mlv-editor .mlv-editor-undo-redo,.mlv-editor .mlv-editor-inline-marks,.mlv-editor .mlv-editor-block-insert{display:inline-flex;flex:0 0 auto;flex-direction:row;align-items:center;gap:var(--mlv-spacing-1)}.mlv-editor mlv-editor-zoom,.mlv-editor mlv-editor-heading,.mlv-editor mlv-editor-list,.mlv-editor mlv-editor-text-color,.mlv-editor mlv-editor-highlight,.mlv-editor mlv-editor-alignment,.mlv-editor mlv-editor-link,.mlv-editor mlv-editor-table,.mlv-editor mlv-editor-image-upload{flex:0 0 auto}.mlv-editor__viewport{flex:1 1 auto;min-block-size:var(--mlv-editor-min-height, 8rem)}.mlv-editor--capped .mlv-editor__surface{block-size:var(--mlv-editor-height, auto);max-block-size:var(--mlv-editor-max-height, none)}.mlv-editor--capped .mlv-editor__viewport{min-block-size:0;overflow:auto;overscroll-behavior:contain}.mlv-editor__view{position:relative;isolation:isolate;min-inline-size:100%;zoom:var(--mlv-editor-zoom, 1)}.mlv-editor--capped .mlv-editor__view{min-inline-size:0;inline-size:calc(100% * var(--mlv-editor-zoom, 1))}.mlv-editor__content{min-block-size:var(--mlv-editor-min-height, 8rem);padding-block:var(--mlv-spacing-2);padding-inline:var(--mlv-editor-gutter);border:0;outline:none}.mlv-editor--capped .mlv-editor__content{min-block-size:calc(var(--mlv-editor-min-height, 8rem) / var(--mlv-editor-zoom, 1))}.mlv-editor__block-handle{position:absolute;inset-inline-start:max(0rem,(100% - 2 * var(--mlv-editor-gutter) - var(--mlv-editor-measure)) / 2);display:inline-flex;align-items:center;justify-content:center;inline-size:var(--mlv-editor-gutter);block-size:var(--mlv-height-xs);border-radius:var(--mlv-radius-s);color:var(--mlv-text-tertiary);cursor:grab;opacity:0;pointer-events:none;transition:opacity var(--mlv-duration-fast) var(--mlv-ease-default)}.mlv-editor__block-handle svg{inline-size:var(--mlv-spacing-4);block-size:var(--mlv-spacing-4);fill:currentcolor}.mlv-editor__block-handle:hover{background-color:var(--mlv-background-neutral-1-hover);color:var(--mlv-text-secondary)}.mlv-editor__block-handle[data-visible=true]{opacity:1;pointer-events:auto}.mlv-editor__drop-indicator{position:absolute;inset-inline:calc(max(0rem,(100% - 2 * var(--mlv-editor-gutter) - var(--mlv-editor-measure)) / 2) + var(--mlv-editor-gutter));block-size:var(--mlv-stroke-width-medium);border-radius:var(--mlv-radius-full);background-color:var(--mlv-border-focus);opacity:0;pointer-events:none;transform:translateY(-50%);transition:top var(--mlv-duration-normal) var(--mlv-ease-out),opacity var(--mlv-duration-fast) var(--mlv-ease-default)}.mlv-editor__drop-indicator[data-visible=true]{opacity:1}.mlv-editor .mlv-editor__block--dragging{opacity:var(--mlv-disabled-opacity)}.mlv-editor__status{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:var(--mlv-spacing-2);min-block-size:var(--mlv-spacing-4);margin-block-start:var(--mlv-spacing-1)}.mlv-editor__upload-placeholder{position:relative;display:inline-block;inline-size:var(--mlv-height-l);block-size:var(--mlv-height-l);margin-inline:var(--mlv-spacing-1);border:var(--mlv-stroke-width) dashed var(--mlv-border-focus);border-radius:var(--mlv-radius-m);background:var(--mlv-background-accent-1-pale);vertical-align:middle}.mlv-editor__upload-placeholder:after{position:absolute;inset:var(--mlv-spacing-2);border:var(--mlv-stroke-width-medium) solid var(--mlv-border-normal);border-block-start-color:var(--mlv-border-focus);border-radius:var(--mlv-radius-full);animation:mlv-editor-upload-spin var(--mlv-duration-slower) var(--mlv-ease-linear) infinite;content:\"\"}.mlv-editor__ai-streaming{border-radius:var(--mlv-radius-xs);background-color:var(--mlv-background-accent-1-pale)}.mlv-editor__ai-streaming-chunk{animation:mlv-editor-ai-chunk-in var(--mlv-duration-normal) var(--mlv-ease-out)}.mlv-editor__ai-caret{position:relative;display:inline-block;inline-size:0;block-size:1em;pointer-events:none}.mlv-editor__ai-caret:after{position:absolute;inset-block:0;inset-inline-start:.0625rem;inline-size:.35em;border-radius:var(--mlv-radius-xs);background:linear-gradient(to bottom,var(--mlv-background-accent-1),var(--mlv-background-accent-2));content:\"\";animation:mlv-editor-ai-caret-pulse var(--mlv-duration-sluggish) var(--mlv-ease-in-out) infinite}.mlv-editor__ai-suggestion-insert{border-radius:var(--mlv-radius-xs);background-color:var(--mlv-background-success-pale)}.mlv-editor__ai-suggestion-delete{border-radius:var(--mlv-radius-xs);background-color:var(--mlv-background-danger-1-pale);color:var(--mlv-text-negative);text-decoration-line:line-through}.mlv-editor__ai-suggestion-current{outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:var(--mlv-focus-ring-offset)}.mlv-editor--content-width-default{--mlv-editor-measure: 45rem}.mlv-editor--content-width-wide{--mlv-editor-measure: 60rem}.mlv-editor--content-width-full{--mlv-editor-measure: 100%}.mlv-editor--focused .mlv-form-control-wrapper__control-container{border-color:var(--mlv-border-focus);box-shadow:var(--mlv-focus-ring)}.mlv-editor--readonly .ProseMirror{cursor:text}.mlv-editor--readonly .ProseMirror ::selection{background:var(--mlv-background-accent-1-pale)}.mlv-editor--disabled{opacity:var(--mlv-disabled-opacity)}.mlv-editor--disabled .mlv-editor__surface,.mlv-editor--disabled .mlv-editor__status{pointer-events:none}.mlv-editor--disabled .mlv-editor__content,.mlv-editor--disabled .ProseMirror{cursor:default;-webkit-user-select:none;user-select:none}.mlv-editor .ProseMirror{min-block-size:inherit;max-inline-size:var(--mlv-editor-measure);margin-inline:auto;color:var(--mlv-text-primary);font-family:var(--mlv-typography-family-text);font-size:var(--mlv-typography-body-m-size);font-weight:var(--mlv-typography-body-m-weight);line-height:var(--mlv-typography-body-m-line-height);overflow-wrap:anywhere;white-space:pre-wrap}.mlv-editor .ProseMirror:focus-visible{outline:none}.mlv-editor .ProseMirror>*:first-child{margin-block-start:0}.mlv-editor .ProseMirror>*:last-child{margin-block-end:0}.mlv-editor .ProseMirror p{margin-block:0 var(--mlv-spacing-3)}.mlv-editor .ProseMirror h1,.mlv-editor .ProseMirror h2,.mlv-editor .ProseMirror h3,.mlv-editor .ProseMirror h4,.mlv-editor .ProseMirror h5,.mlv-editor .ProseMirror h6{margin-block:var(--mlv-spacing-6) var(--mlv-spacing-3);color:var(--mlv-text-heading);font-family:var(--mlv-typography-family-heading)}.mlv-editor .ProseMirror h1{font-size:var(--mlv-typography-heading-h1-size);font-weight:var(--mlv-typography-heading-h1-weight);line-height:var(--mlv-typography-heading-h1-line-height)}.mlv-editor .ProseMirror h2{font-size:var(--mlv-typography-heading-h2-size);font-weight:var(--mlv-typography-heading-h2-weight);line-height:var(--mlv-typography-heading-h2-line-height)}.mlv-editor .ProseMirror h3{font-size:var(--mlv-typography-heading-h3-size);font-weight:var(--mlv-typography-heading-h3-weight);line-height:var(--mlv-typography-heading-h3-line-height)}.mlv-editor .ProseMirror h4{font-size:var(--mlv-typography-heading-h4-size);font-weight:var(--mlv-typography-heading-h4-weight);line-height:var(--mlv-typography-heading-h4-line-height)}.mlv-editor .ProseMirror h5{font-size:var(--mlv-typography-heading-h5-size);font-weight:var(--mlv-typography-heading-h5-weight);line-height:var(--mlv-typography-heading-h5-line-height)}.mlv-editor .ProseMirror h6{font-size:var(--mlv-typography-heading-h6-size);font-weight:var(--mlv-typography-heading-h6-weight);line-height:var(--mlv-typography-heading-h6-line-height)}.mlv-editor .ProseMirror h1{letter-spacing:var(--mlv-typography-heading-h1-letter-spacing)}.mlv-editor .ProseMirror h2{letter-spacing:var(--mlv-typography-heading-h2-letter-spacing)}.mlv-editor .ProseMirror h3,.mlv-editor .ProseMirror h4,.mlv-editor .ProseMirror h5,.mlv-editor .ProseMirror h6{letter-spacing:var(--mlv-letter-spacing-normal)}.mlv-editor .ProseMirror strong{font-weight:var(--mlv-font-weight-bold)}.mlv-editor .ProseMirror em{font-style:italic}.mlv-editor .ProseMirror s{text-decoration:line-through}.mlv-editor .ProseMirror u{text-underline-offset:var(--mlv-spacing-0-5)}.mlv-editor .ProseMirror a{color:var(--mlv-text-action);text-decoration:underline;text-underline-offset:var(--mlv-spacing-0-5);transition:color var(--mlv-duration-fast) var(--mlv-ease-default)}.mlv-editor .ProseMirror a:visited{color:var(--mlv-text-action)}.mlv-editor .ProseMirror a:hover{color:var(--mlv-text-action-hover)}.mlv-editor .ProseMirror a:focus-visible{border-radius:var(--mlv-radius-xs);outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:var(--mlv-focus-ring-offset)}.mlv-editor .ProseMirror ul,.mlv-editor .ProseMirror ol{padding-inline-start:var(--mlv-spacing-6);margin-block:0 var(--mlv-spacing-3)}.mlv-editor .ProseMirror ul ul,.mlv-editor .ProseMirror ul ol,.mlv-editor .ProseMirror ol ul,.mlv-editor .ProseMirror ol ol{margin-block:var(--mlv-spacing-1) 0}.mlv-editor .ProseMirror ul{list-style:disc}.mlv-editor .ProseMirror ul ul{list-style:circle}.mlv-editor .ProseMirror ol{list-style:decimal}.mlv-editor .ProseMirror li{margin-block:var(--mlv-spacing-1)}.mlv-editor .ProseMirror li>p{margin:0}.mlv-editor .ProseMirror ul[data-type=taskList]{padding:0;list-style:none}.mlv-editor .ProseMirror ul[data-type=taskList] li[data-type=taskItem]{display:flex;align-items:flex-start;gap:var(--mlv-spacing-2)}.mlv-editor .ProseMirror ul[data-type=taskList] li[data-type=taskItem]>label{display:inline-flex;align-items:center;min-block-size:var(--mlv-height-xs);cursor:pointer;-webkit-user-select:none;user-select:none}.mlv-editor .ProseMirror ul[data-type=taskList] li[data-type=taskItem]>label input{accent-color:var(--mlv-background-accent-1)}.mlv-editor .ProseMirror ul[data-type=taskList] li[data-type=taskItem]>label input:focus-visible{outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:var(--mlv-focus-ring-offset)}.mlv-editor .ProseMirror ul[data-type=taskList] li[data-type=taskItem]>div{flex:1 1 auto;min-width:0}.mlv-editor .ProseMirror blockquote{padding-inline-start:var(--mlv-spacing-4);margin:var(--mlv-spacing-4) 0;border-inline-start:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);color:var(--mlv-text-secondary)}.mlv-editor .ProseMirror code{padding:var(--mlv-spacing-0-5) var(--mlv-spacing-1);border-radius:var(--mlv-radius-s);background:var(--mlv-background-neutral-1);color:var(--mlv-text-primary);font-family:var(--mlv-typography-family-code);font-size:var(--mlv-typography-code-size)}.mlv-editor .ProseMirror pre{padding:var(--mlv-padding-m);margin-block:var(--mlv-spacing-4);overflow:auto;border:var(--mlv-stroke-width) solid var(--mlv-border-normal);border-radius:var(--mlv-radius-m);background:var(--mlv-background-sunken);color:var(--mlv-text-primary);font-family:var(--mlv-typography-family-code);font-size:var(--mlv-typography-code-size);line-height:var(--mlv-typography-body-m-line-height);tab-size:2}.mlv-editor .ProseMirror pre code{padding:0;background:transparent;color:inherit;font-size:inherit}.mlv-editor .ProseMirror hr{height:var(--mlv-stroke-width);margin-block:var(--mlv-spacing-6);border:0;background:var(--mlv-border-normal)}.mlv-editor .ProseMirror img{display:block;max-inline-size:100%;block-size:auto;margin-block:var(--mlv-spacing-4);border-radius:var(--mlv-radius-m)}.mlv-editor .ProseMirror img.ProseMirror-selectednode{outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:0}.mlv-editor .ProseMirror .ProseMirror-selectednode,.mlv-editor .ProseMirror [data-resize-state=true]{border-radius:var(--mlv-radius-s);outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:var(--mlv-spacing-1)}.mlv-editor .ProseMirror [data-resize-container]{margin-block:var(--mlv-spacing-4)}.mlv-editor .ProseMirror [data-resize-container] img{margin-block:0}.mlv-editor .ProseMirror [data-resize-container].ProseMirror-selectednode,.mlv-editor .ProseMirror [data-resize-container][data-resize-state=true]{outline:none}.mlv-editor .ProseMirror [data-resize-container].ProseMirror-selectednode>[data-resize-wrapper],.mlv-editor .ProseMirror [data-resize-container][data-resize-state=true]>[data-resize-wrapper]{border-radius:var(--mlv-radius-m);outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:0}.mlv-editor .ProseMirror [data-resize-handle],.mlv-editor .ProseMirror .resize-handle{inline-size:var(--mlv-spacing-3);block-size:var(--mlv-spacing-3);border:var(--mlv-stroke-width) solid var(--mlv-background-base);border-radius:var(--mlv-radius-full);background:var(--mlv-background-accent-1);box-shadow:var(--mlv-shadow-raised)}.mlv-editor .ProseMirror [data-resize-handle]:focus-visible,.mlv-editor .ProseMirror .resize-handle:focus-visible{outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:var(--mlv-focus-ring-offset)}.mlv-editor .ProseMirror .is-editor-empty:first-child:before{float:left;height:0;color:var(--mlv-text-placeholder);content:attr(data-placeholder);pointer-events:none}.mlv-editor .tableWrapper{margin-block:var(--mlv-spacing-3);overflow-x:auto}.mlv-editor table{width:100%;border-collapse:collapse;table-layout:fixed}.mlv-editor th,.mlv-editor td{position:relative;min-width:3rem;padding:var(--mlv-spacing-2) var(--mlv-spacing-3);border:var(--mlv-stroke-width) solid var(--mlv-border-normal);vertical-align:top}.mlv-editor th{background:var(--mlv-background-neutral-1);font-weight:var(--mlv-font-weight-semibold)}.mlv-editor .selectedCell:after{position:absolute;inset:0;z-index:1;background:var(--mlv-background-accent-1-pale);content:\"\";opacity:.55;pointer-events:none}.mlv-editor .ProseMirror-focused .selectedCell:after{box-shadow:inset 0 0 0 var(--mlv-stroke-width-medium) var(--mlv-border-focus)}.mlv-editor .column-resize-handle{position:absolute;inset-block:0;inset-inline-end:calc(var(--mlv-stroke-width) * -1);z-index:2;width:calc(var(--mlv-stroke-width) * 2);background:var(--mlv-border-focus);pointer-events:none}.mlv-editor .resize-cursor{cursor:col-resize}@keyframes mlv-editor-upload-spin{to{transform:rotate(1turn)}}@keyframes mlv-editor-ai-chunk-in{0%{opacity:0;filter:blur(.125rem)}to{opacity:1;filter:blur(0)}}@keyframes mlv-editor-ai-caret-pulse{0%,to{opacity:1}50%{opacity:.35}}.mlv-editor--toolbar-sticky.mlv-editor--toolbar-top .mlv-editor__toolbar-band{position:sticky;inset-block-start:var(--mlv-editor-toolbar-sticky-offset, 0);z-index:var(--mlv-z-raised)}.mlv-editor--toolbar-sticky.mlv-editor--toolbar-bottom .mlv-editor__toolbar-band{position:sticky;inset-block-end:var(--mlv-editor-toolbar-sticky-offset, 0);z-index:var(--mlv-z-raised)}.mlv-editor-bubble{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor-bubble *{box-sizing:border-box}.mlv-editor-bubble .mlv-editor-toolbar__separator{flex:0 0 auto;min-height:var(--mlv-height-s);margin-inline:var(--mlv-spacing-1)}.mlv-editor-bubble .mlv-editor-toolbar__overflow,.mlv-editor-bubble .mlv-editor-undo-redo,.mlv-editor-bubble .mlv-editor-inline-marks,.mlv-editor-bubble .mlv-editor-block-insert{display:inline-flex;flex:0 0 auto;flex-direction:row;align-items:center;gap:var(--mlv-spacing-1)}.mlv-editor-bubble mlv-editor-zoom,.mlv-editor-bubble mlv-editor-heading,.mlv-editor-bubble mlv-editor-list,.mlv-editor-bubble mlv-editor-text-color,.mlv-editor-bubble mlv-editor-highlight,.mlv-editor-bubble mlv-editor-alignment,.mlv-editor-bubble mlv-editor-link,.mlv-editor-bubble mlv-editor-table,.mlv-editor-bubble mlv-editor-image-upload{flex:0 0 auto}.mlv-editor-bubble{max-inline-size:calc(100vw - 2 * var(--mlv-spacing-2));border-radius:var(--mlv-radius-panel);background:var(--mlv-elevation-bg-4);box-shadow:var(--mlv-shadow-floating);color:var(--mlv-text-primary);animation:mlv-editor-bubble-enter var(--mlv-duration-fast) var(--mlv-ease-out)}.mlv-editor-bubble--hidden{visibility:hidden;animation:none}.mlv-editor-bubble .mlv-editor__toolbar-band{flex:1 1 auto;min-inline-size:0}.mlv-editor-bubble .mlv-editor__toolbar{border:0;border-radius:inherit;background:transparent}@keyframes mlv-editor-bubble-enter{0%{opacity:0}}@media(max-width:40rem){.mlv-editor{--mlv-editor-gutter: var(--mlv-spacing-3)}.mlv-editor__toolbar{flex-wrap:nowrap;overflow-x:auto}.mlv-editor__content{padding-block:var(--mlv-spacing-1-5)}.mlv-editor__status{justify-content:flex-start}.mlv-editor .ProseMirror h1{font-size:var(--mlv-typography-heading-h2-size)}.mlv-editor .ProseMirror h2{font-size:var(--mlv-typography-heading-h3-size)}}@media print{.mlv-editor__toolbar-band,.mlv-editor__toolbar,.mlv-editor__status,.mlv-editor__block-handle,.mlv-editor__drop-indicator,.mlv-editor .mlv-editor-image-upload-status,.mlv-editor__upload-placeholder,.mlv-editor__ai-suggestion-delete,.mlv-editor__ai-caret{display:none}.mlv-editor__ai-suggestion-current{outline:none}.mlv-editor__ai-streaming-chunk{animation:none}.mlv-editor__viewport,.mlv-editor--capped .mlv-editor__viewport{overflow:visible;padding-block:0}.mlv-editor--capped .mlv-editor__surface{block-size:auto;max-block-size:none}.mlv-editor__view,.mlv-editor--capped .mlv-editor__view{zoom:1;inline-size:auto;min-inline-size:0}.mlv-editor__content,.mlv-editor--capped .mlv-editor__content,.mlv-editor .ProseMirror{min-block-size:0;padding:0}.mlv-editor table{break-inside:avoid}}@media print{.mlv-editor-bubble{display:none}}@media(hover:none){.mlv-editor__block-handle{display:none}}@media(prefers-reduced-motion:reduce){.mlv-editor .mlv-form-control-wrapper__control-container,.mlv-editor__block-handle,.mlv-editor__drop-indicator,.mlv-editor .ProseMirror a{transition-duration:var(--mlv-duration-instant)}.mlv-editor__upload-placeholder:after,.mlv-editor__ai-streaming-chunk,.mlv-editor__ai-caret:after{animation:none}.mlv-editor-bubble{animation:none}}.mlv-editor__drag-ghost{max-block-size:12rem;padding:var(--mlv-spacing-2);overflow:hidden;border-radius:var(--mlv-radius-m);background-color:var(--mlv-background-accent-1-pale);opacity:.9;-webkit-mask-image:linear-gradient(to bottom,black 70%,transparent 100%);mask-image:linear-gradient(to bottom,black 70%,transparent 100%)}}\n"], dependencies: [{ kind: "component", type: MlvFormControlWrapper, selector: "mlv-form-control-wrapper", outputs: ["clear"] }, { kind: "directive", type: MlvFormControlWrapperControl, selector: "[mlvFormControlWrapperControl]" }, { kind: "component", type: MlvHint, selector: "mlv-hint" }, { kind: "component", type: MlvLabel, selector: "mlv-label", inputs: ["for", "required"] }, { kind: "component", type: MlvMessage, selector: "mlv-message", inputs: ["state"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: MlvFade, selector: "[mlvFade]", inputs: ["mlvFadeHeight", "mlvFadeSize", "mlvFadeOffset", "mlvFade"] }, { kind: "component", type: MlvToolbar, selector: "mlv-toolbar", inputs: ["gap", "equalSize"] }, { kind: "component", type: MlvDivider, selector: "mlv-divider", inputs: ["orientation", "dashed", "muted"] }, { kind: "directive", type: MlvEditorToolbarRoot, selector: "[mlvEditorToolbarRoot]", inputs: ["ariaLabel", "disabled", "orientation", "wrap", "measureTarget"], exportAs: ["mlvEditorToolbarRoot"] }, { kind: "component", type: MlvEditorToolbarOverflow, selector: "mlv-editor-toolbar-overflow" }, { kind: "component", type: MlvEditorUndoRedo, selector: "mlv-editor-undo-redo" }, { kind: "component", type: MlvEditorZoom, selector: "mlv-editor-zoom", inputs: ["zoom", "zoomLevels", "min", "max", "fitToContainer"], outputs: ["zoomChange"] }, { kind: "component", type: MlvEditorHeading, selector: "mlv-editor-heading", inputs: ["levels"] }, { kind: "component", type: MlvEditorList, selector: "mlv-editor-list" }, { kind: "component", type: MlvEditorInlineMarks, selector: "mlv-editor-inline-marks" }, { kind: "component", type: MlvEditorTextColor, selector: "mlv-editor-text-color" }, { kind: "component", type: MlvEditorStatus, selector: "mlv-editor-status", inputs: ["characterLimit"] }, { kind: "component", type: MlvEditorHighlight, selector: "mlv-editor-highlight" }, { kind: "component", type: MlvEditorLink, selector: "mlv-editor-link", inputs: ["allowedProtocols"] }, { kind: "component", type: MlvEditorImageUpload, selector: "mlv-editor-image-upload" }, { kind: "component", type: MlvEditorImageUploadStatus, selector: "mlv-editor-image-upload-status" }, { kind: "component", type: MlvEditorTable, selector: "mlv-editor-table" }, { kind: "component", type: MlvEditorTableControls, selector: "mlv-editor-table-controls", inputs: ["layer"] }, { kind: "component", type: MlvEditorAlignment, selector: "mlv-editor-alignment" }, { kind: "component", type: MlvEditorBlockInsert, selector: "mlv-editor-block-insert" }, { kind: "directive", type: MlvEditorBubble, selector: "ng-template[mlvEditorBubble]", inputs: ["viewport", "capped"] }, { kind: "component", type: MlvSpacer, selector: "mlv-spacer" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
10120
11593
|
}
|
|
10121
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
11594
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditor, decorators: [{
|
|
10122
11595
|
type: Component,
|
|
10123
11596
|
args: [{ selector: 'mlv-editor', imports: [
|
|
10124
11597
|
MlvFormControlWrapper,
|
|
@@ -10147,6 +11620,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
|
|
|
10147
11620
|
MlvEditorTableControls,
|
|
10148
11621
|
MlvEditorAlignment,
|
|
10149
11622
|
MlvEditorBlockInsert,
|
|
11623
|
+
MlvEditorBubble,
|
|
10150
11624
|
MlvSpacer,
|
|
10151
11625
|
], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, providers: [
|
|
10152
11626
|
MlvEditorOverlayRegistry,
|
|
@@ -10213,11 +11687,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
|
|
|
10213
11687
|
'[class.mlv-editor--disabled]': 'computedDisabled()',
|
|
10214
11688
|
'[class.mlv-editor--readonly]': 'readonly()',
|
|
10215
11689
|
'[class.mlv-editor--focused]': 'focused()',
|
|
11690
|
+
'[class.mlv-editor--capped]': '_capped()',
|
|
11691
|
+
'[class.mlv-editor--toolbar-top]': 'toolbarPosition() === "top"',
|
|
11692
|
+
'[class.mlv-editor--toolbar-bottom]': 'toolbarPosition() === "bottom"',
|
|
11693
|
+
'[class.mlv-editor--toolbar-bar]': 'toolbarAppearance() === "bar"',
|
|
11694
|
+
'[class.mlv-editor--toolbar-floating]': 'toolbarAppearance() === "floating"',
|
|
11695
|
+
'[class.mlv-editor--toolbar-sticky]': '_stickyToolbar()',
|
|
10216
11696
|
'[attr.aria-disabled]': 'computedDisabled() || null',
|
|
10217
11697
|
'[attr.inert]': 'computedDisabled() ? "" : null',
|
|
10218
11698
|
'[style.--mlv-editor-zoom]': 'zoom() / 100',
|
|
10219
|
-
}, template: "<mlv-form-control-wrapper (clear)=\"clearValue()\">\n @if (label() || hint()) {\n <mlv-label [for]=\"id()\" [id]=\"_labelId()\">\n {{ label() }} @if (hint()) {\n <mlv-hint>{{ hint() }}</mlv-hint>\n }\n </mlv-label>\n }\n\n <ng-template mlvFormControlWrapperControl>\n <div class=\"mlv-editor__surface\">\n <div\n mlvEditorToolbarRoot\n #toolbarRoot=\"mlvEditorToolbarRoot\"\n class=\"mlv-editor__toolbar\"\n [ariaLabel]=\"_toolbarAriaLabel()\"\n [disabled]=\"computedDisabled()\"\n >\n @if (_hasToolbarDef()) {\n <ng-content select=\"[mlvEditorToolbar]\" />\n @for (definition of _toolbarDefs(); track definition) { @if\n (definition.templateRef; as toolbarTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"toolbarTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: _toolbarContext }\"\n />\n } } } @else {\n <ng-content select=\"[mlvEditorToolbarStart]\" />\n @for (definition of _toolbarStartDefs(); track definition) { @if\n (definition.templateRef; as startTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"startTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: _toolbarContext }\"\n />\n } }\n <div mlvFade class=\"mlv-editor__toolbar-scroll\">\n <mlv-toolbar>\n <mlv-editor-undo-redo />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n />\n <mlv-editor-zoom />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n />\n <mlv-editor-heading />\n <mlv-editor-list />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n [hidden]=\"toolbarRoot.narrow()\"\n />\n <mlv-editor-inline-marks [hidden]=\"toolbarRoot.narrow()\" />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n [hidden]=\"toolbarRoot.narrow()\"\n />\n <mlv-editor-text-color />\n <mlv-editor-highlight />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n [hidden]=\"toolbarRoot.narrow()\"\n />\n <mlv-editor-alignment [hidden]=\"toolbarRoot.narrow()\" />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n [hidden]=\"toolbarRoot.narrow()\"\n />\n <mlv-editor-link />\n <mlv-editor-table />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n [hidden]=\"toolbarRoot.narrow()\"\n />\n <mlv-editor-block-insert [hidden]=\"toolbarRoot.narrow()\" />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n [hidden]=\"toolbarRoot.narrow()\"\n />\n <mlv-editor-image-upload />\n </mlv-toolbar>\n </div>\n <mlv-editor-toolbar-overflow />\n @for (definition of _toolbarEndDefs(); track definition) { @if\n (definition.templateRef; as endTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"endTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: _toolbarContext }\"\n />\n } }\n <ng-content select=\"[mlvEditorToolbarEnd]\" />\n }\n </div>\n\n <div class=\"mlv-editor__viewport\">\n <div #view class=\"mlv-editor__view\">\n <div #content class=\"mlv-editor__content\"></div>\n <mlv-editor-table-controls [layer]=\"view\" />\n </div>\n </div>\n <mlv-editor-image-upload-status />\n </div>\n </ng-template>\n\n @if (message()) {\n <mlv-message [state]=\"resolvedState()\" [id]=\"id() + '-message'\">\n {{ message() }}\n </mlv-message>\n }\n</mlv-form-control-wrapper>\n\n<div\n class=\"mlv-editor__status\"\n [attr.aria-disabled]=\"computedDisabled() || null\"\n>\n <ng-content select=\"[mlvEditorStatus]\" />\n <mlv-spacer />\n <mlv-editor-status [characterLimit]=\"characterLimit()\" />\n</div>\n", styles: ["@layer mlv.components{.mlv-editor{--mlv-editor-gutter: var(--mlv-spacing-6);--mlv-editor-measure: 45rem;--mlv-editor-drop-gap: 1.5rem;box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor *{box-sizing:border-box}.mlv-editor{display:block;color:var(--mlv-text-primary)}.mlv-editor .mlv-form-control-wrapper__control-container{height:auto;min-height:var(--form-ctrl-height);padding:0;overflow:hidden;background:var(--mlv-background-base);transition:border-color var(--mlv-duration-fast) var(--mlv-ease-default),box-shadow var(--mlv-duration-fast) var(--mlv-ease-default)}.mlv-editor .mlv-form-control-wrapper__control-row{align-items:stretch}.mlv-form-control-wrapper--state-error .mlv-editor__surface{box-shadow:inset 0 0 0 var(--mlv-stroke-width) var(--mlv-border-error)}.mlv-editor__surface{display:flex;flex:1 1 auto;flex-direction:column;min-width:0;width:100%;background:var(--mlv-elevation-bg-3)}.mlv-editor__toolbar{display:flex;flex-wrap:nowrap;align-items:center;gap:var(--mlv-spacing-1);min-width:0;padding:var(--mlv-padding-xs);overflow:hidden;border-bottom:var(--mlv-stroke-width) solid var(--mlv-border-normal);background:var(--mlv-elevation-bg-3)}.mlv-editor__toolbar [hidden]{display:none!important}.mlv-editor__toolbar-scroll{flex:1 1 auto;min-width:0}.mlv-editor__toolbar-scroll>.mlv-toolbar{width:max-content;min-width:100%;flex-wrap:nowrap}.mlv-editor .mlv-editor-toolbar__separator{flex:0 0 auto;min-height:var(--mlv-height-s);margin-inline:var(--mlv-spacing-1)}.mlv-editor .mlv-editor-toolbar__overflow,.mlv-editor .mlv-editor-undo-redo,.mlv-editor .mlv-editor-inline-marks,.mlv-editor .mlv-editor-block-insert{display:inline-flex;flex:0 0 auto;flex-direction:row;align-items:center;gap:var(--mlv-spacing-1)}.mlv-editor mlv-editor-zoom,.mlv-editor mlv-editor-heading,.mlv-editor mlv-editor-list,.mlv-editor mlv-editor-text-color,.mlv-editor mlv-editor-highlight,.mlv-editor mlv-editor-alignment,.mlv-editor mlv-editor-link,.mlv-editor mlv-editor-table,.mlv-editor mlv-editor-image-upload{flex:0 0 auto}.mlv-editor__viewport{min-block-size:8rem;overflow:auto;overscroll-behavior:contain}.mlv-editor__view{position:relative;min-inline-size:100%;transform:scale(var(--mlv-editor-zoom, 1));transform-origin:left top;transition:transform var(--mlv-duration-fast) var(--mlv-ease-default)}.mlv-editor__content{min-block-size:8rem;padding-block:var(--mlv-spacing-2);padding-inline:var(--mlv-editor-gutter);border:0;outline:none}.mlv-editor__block-handle{position:absolute;inset-inline-start:max(0rem,(100% - 2 * var(--mlv-editor-gutter) - var(--mlv-editor-measure)) / 2);display:inline-flex;align-items:center;justify-content:center;inline-size:var(--mlv-editor-gutter);block-size:var(--mlv-height-xs);border-radius:var(--mlv-radius-s);color:var(--mlv-text-tertiary);cursor:grab;opacity:0;pointer-events:none;transition:opacity var(--mlv-duration-fast) var(--mlv-ease-default)}.mlv-editor__block-handle svg{inline-size:var(--mlv-spacing-4);block-size:var(--mlv-spacing-4);fill:currentcolor}.mlv-editor__block-handle:hover{background-color:var(--mlv-background-neutral-1-hover);color:var(--mlv-text-secondary)}.mlv-editor__block-handle[data-visible=true]{opacity:1;pointer-events:auto}.mlv-editor__drop-indicator{position:absolute;inset-inline:calc(max(0rem,(100% - 2 * var(--mlv-editor-gutter) - var(--mlv-editor-measure)) / 2) + var(--mlv-editor-gutter));block-size:var(--mlv-stroke-width-medium);border-radius:var(--mlv-radius-full);background-color:var(--mlv-border-focus);opacity:0;pointer-events:none;transform:translateY(calc(var(--mlv-editor-drop-gap) / 2 - var(--mlv-stroke-width-medium) / 2));transition:top var(--mlv-duration-normal) var(--mlv-ease-out),opacity var(--mlv-duration-fast) var(--mlv-ease-default)}.mlv-editor__drop-indicator[data-visible=true]{opacity:1}.mlv-editor .mlv-editor__block--dragging{opacity:var(--mlv-disabled-opacity)}.mlv-editor .ProseMirror--block-dragging>*{transition:transform var(--mlv-duration-normal) var(--mlv-ease-out)}.mlv-editor__status{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:var(--mlv-spacing-2);min-block-size:var(--mlv-spacing-4);margin-block-start:var(--mlv-spacing-1)}.mlv-editor__upload-placeholder{position:relative;display:inline-block;inline-size:var(--mlv-height-l);block-size:var(--mlv-height-l);margin-inline:var(--mlv-spacing-1);border:var(--mlv-stroke-width) dashed var(--mlv-border-focus);border-radius:var(--mlv-radius-m);background:var(--mlv-background-accent-1-pale);vertical-align:middle}.mlv-editor__upload-placeholder:after{position:absolute;inset:var(--mlv-spacing-2);border:var(--mlv-stroke-width-medium) solid var(--mlv-border-normal);border-block-start-color:var(--mlv-border-focus);border-radius:var(--mlv-radius-full);animation:mlv-editor-upload-spin var(--mlv-duration-slower) var(--mlv-ease-linear) infinite;content:\"\"}.mlv-editor__ai-streaming{border-radius:var(--mlv-radius-xs);background-color:var(--mlv-background-accent-1-pale)}.mlv-editor__ai-streaming-chunk{animation:mlv-editor-ai-chunk-in var(--mlv-duration-normal) var(--mlv-ease-out)}.mlv-editor__ai-caret{position:relative;display:inline-block;inline-size:0;block-size:1em;pointer-events:none}.mlv-editor__ai-caret:after{position:absolute;inset-block:0;inset-inline-start:.0625rem;inline-size:.35em;border-radius:var(--mlv-radius-xs);background:linear-gradient(to bottom,var(--mlv-background-accent-1),var(--mlv-background-accent-2));content:\"\";animation:mlv-editor-ai-caret-pulse var(--mlv-duration-sluggish) var(--mlv-ease-in-out) infinite}.mlv-editor__ai-suggestion-insert{border-radius:var(--mlv-radius-xs);background-color:var(--mlv-background-success-pale)}.mlv-editor__ai-suggestion-delete{border-radius:var(--mlv-radius-xs);background-color:var(--mlv-background-danger-1-pale);color:var(--mlv-text-negative);text-decoration-line:line-through}.mlv-editor__ai-suggestion-current{outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:var(--mlv-focus-ring-offset)}.mlv-editor--content-width-default{--mlv-editor-measure: 45rem}.mlv-editor--content-width-wide{--mlv-editor-measure: 60rem}.mlv-editor--content-width-full{--mlv-editor-measure: 100%}.mlv-editor--focused .mlv-form-control-wrapper__control-container{border-color:var(--mlv-border-focus);box-shadow:var(--mlv-focus-ring)}.mlv-editor--readonly .ProseMirror{cursor:text}.mlv-editor--readonly .ProseMirror ::selection{background:var(--mlv-background-accent-1-pale)}.mlv-editor--disabled{opacity:var(--mlv-disabled-opacity)}.mlv-editor--disabled .mlv-editor__surface,.mlv-editor--disabled .mlv-editor__status{pointer-events:none}.mlv-editor--disabled .mlv-editor__content,.mlv-editor--disabled .ProseMirror{cursor:default;-webkit-user-select:none;user-select:none}.mlv-editor .ProseMirror{min-block-size:inherit;max-inline-size:var(--mlv-editor-measure);margin-inline:auto;color:var(--mlv-text-primary);font-family:var(--mlv-typography-family-text);font-size:var(--mlv-typography-body-m-size);font-weight:var(--mlv-typography-body-m-weight);line-height:var(--mlv-typography-body-m-line-height);overflow-wrap:anywhere;white-space:pre-wrap}.mlv-editor .ProseMirror:focus-visible{outline:none}.mlv-editor .ProseMirror>*:first-child{margin-block-start:0}.mlv-editor .ProseMirror>*:last-child{margin-block-end:0}.mlv-editor .ProseMirror p{margin-block:0 var(--mlv-spacing-3)}.mlv-editor .ProseMirror h1,.mlv-editor .ProseMirror h2,.mlv-editor .ProseMirror h3,.mlv-editor .ProseMirror h4,.mlv-editor .ProseMirror h5,.mlv-editor .ProseMirror h6{margin-block:var(--mlv-spacing-6) var(--mlv-spacing-3);color:var(--mlv-text-heading);font-family:var(--mlv-typography-family-heading)}.mlv-editor .ProseMirror h1{font-size:var(--mlv-typography-heading-h1-size);font-weight:var(--mlv-typography-heading-h1-weight);line-height:var(--mlv-typography-heading-h1-line-height)}.mlv-editor .ProseMirror h2{font-size:var(--mlv-typography-heading-h2-size);font-weight:var(--mlv-typography-heading-h2-weight);line-height:var(--mlv-typography-heading-h2-line-height)}.mlv-editor .ProseMirror h3{font-size:var(--mlv-typography-heading-h3-size);font-weight:var(--mlv-typography-heading-h3-weight);line-height:var(--mlv-typography-heading-h3-line-height)}.mlv-editor .ProseMirror h4{font-size:var(--mlv-typography-heading-h4-size);font-weight:var(--mlv-typography-heading-h4-weight);line-height:var(--mlv-typography-heading-h4-line-height)}.mlv-editor .ProseMirror h5{font-size:var(--mlv-typography-heading-h5-size);font-weight:var(--mlv-typography-heading-h5-weight);line-height:var(--mlv-typography-heading-h5-line-height)}.mlv-editor .ProseMirror h6{font-size:var(--mlv-typography-heading-h6-size);font-weight:var(--mlv-typography-heading-h6-weight);line-height:var(--mlv-typography-heading-h6-line-height)}.mlv-editor .ProseMirror h1{letter-spacing:var(--mlv-typography-heading-h1-letter-spacing)}.mlv-editor .ProseMirror h2{letter-spacing:var(--mlv-typography-heading-h2-letter-spacing)}.mlv-editor .ProseMirror h3,.mlv-editor .ProseMirror h4,.mlv-editor .ProseMirror h5,.mlv-editor .ProseMirror h6{letter-spacing:var(--mlv-letter-spacing-normal)}.mlv-editor .ProseMirror strong{font-weight:var(--mlv-font-weight-bold)}.mlv-editor .ProseMirror em{font-style:italic}.mlv-editor .ProseMirror s{text-decoration:line-through}.mlv-editor .ProseMirror u{text-underline-offset:var(--mlv-spacing-0-5)}.mlv-editor .ProseMirror a{color:var(--mlv-text-action);text-decoration:underline;text-underline-offset:var(--mlv-spacing-0-5);transition:color var(--mlv-duration-fast) var(--mlv-ease-default)}.mlv-editor .ProseMirror a:visited{color:var(--mlv-text-action)}.mlv-editor .ProseMirror a:hover{color:var(--mlv-text-action-hover)}.mlv-editor .ProseMirror a:focus-visible{border-radius:var(--mlv-radius-xs);outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:var(--mlv-focus-ring-offset)}.mlv-editor .ProseMirror ul,.mlv-editor .ProseMirror ol{padding-inline-start:var(--mlv-spacing-6);margin-block:0 var(--mlv-spacing-3)}.mlv-editor .ProseMirror ul ul,.mlv-editor .ProseMirror ul ol,.mlv-editor .ProseMirror ol ul,.mlv-editor .ProseMirror ol ol{margin-block:var(--mlv-spacing-1) 0}.mlv-editor .ProseMirror ul{list-style:disc}.mlv-editor .ProseMirror ul ul{list-style:circle}.mlv-editor .ProseMirror ol{list-style:decimal}.mlv-editor .ProseMirror li{margin-block:var(--mlv-spacing-1)}.mlv-editor .ProseMirror li>p{margin:0}.mlv-editor .ProseMirror ul[data-type=taskList]{padding:0;list-style:none}.mlv-editor .ProseMirror ul[data-type=taskList] li[data-type=taskItem]{display:flex;align-items:flex-start;gap:var(--mlv-spacing-2)}.mlv-editor .ProseMirror ul[data-type=taskList] li[data-type=taskItem]>label{display:inline-flex;align-items:center;min-block-size:var(--mlv-height-xs);cursor:pointer;-webkit-user-select:none;user-select:none}.mlv-editor .ProseMirror ul[data-type=taskList] li[data-type=taskItem]>label input{accent-color:var(--mlv-background-accent-1)}.mlv-editor .ProseMirror ul[data-type=taskList] li[data-type=taskItem]>label input:focus-visible{outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:var(--mlv-focus-ring-offset)}.mlv-editor .ProseMirror ul[data-type=taskList] li[data-type=taskItem]>div{flex:1 1 auto;min-width:0}.mlv-editor .ProseMirror blockquote{padding-inline-start:var(--mlv-spacing-4);margin:var(--mlv-spacing-4) 0;border-inline-start:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);color:var(--mlv-text-secondary)}.mlv-editor .ProseMirror code{padding:var(--mlv-spacing-0-5) var(--mlv-spacing-1);border-radius:var(--mlv-radius-s);background:var(--mlv-background-neutral-1);color:var(--mlv-text-primary);font-family:var(--mlv-typography-family-code);font-size:var(--mlv-typography-code-size)}.mlv-editor .ProseMirror pre{padding:var(--mlv-padding-m);margin-block:var(--mlv-spacing-4);overflow:auto;border:var(--mlv-stroke-width) solid var(--mlv-border-normal);border-radius:var(--mlv-radius-m);background:var(--mlv-background-sunken);color:var(--mlv-text-primary);font-family:var(--mlv-typography-family-code);font-size:var(--mlv-typography-code-size);line-height:var(--mlv-typography-body-m-line-height);tab-size:2}.mlv-editor .ProseMirror pre code{padding:0;background:transparent;color:inherit;font-size:inherit}.mlv-editor .ProseMirror hr{height:var(--mlv-stroke-width);margin-block:var(--mlv-spacing-6);border:0;background:var(--mlv-border-normal)}.mlv-editor .ProseMirror img{display:block;max-inline-size:100%;block-size:auto;margin-block:var(--mlv-spacing-4);border-radius:var(--mlv-radius-m)}.mlv-editor .ProseMirror img.ProseMirror-selectednode{outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:0}.mlv-editor .ProseMirror .ProseMirror-selectednode,.mlv-editor .ProseMirror [data-resize-state=true]{border-radius:var(--mlv-radius-s);outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:var(--mlv-spacing-1)}.mlv-editor .ProseMirror [data-resize-container]{margin-block:var(--mlv-spacing-4)}.mlv-editor .ProseMirror [data-resize-container] img{margin-block:0}.mlv-editor .ProseMirror [data-resize-container].ProseMirror-selectednode,.mlv-editor .ProseMirror [data-resize-container][data-resize-state=true]{outline:none}.mlv-editor .ProseMirror [data-resize-container].ProseMirror-selectednode>[data-resize-wrapper],.mlv-editor .ProseMirror [data-resize-container][data-resize-state=true]>[data-resize-wrapper]{border-radius:var(--mlv-radius-m);outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:0}.mlv-editor .ProseMirror [data-resize-handle],.mlv-editor .ProseMirror .resize-handle{inline-size:var(--mlv-spacing-3);block-size:var(--mlv-spacing-3);border:var(--mlv-stroke-width) solid var(--mlv-background-base);border-radius:var(--mlv-radius-full);background:var(--mlv-background-accent-1);box-shadow:var(--mlv-shadow-raised)}.mlv-editor .ProseMirror [data-resize-handle]:focus-visible,.mlv-editor .ProseMirror .resize-handle:focus-visible{outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:var(--mlv-focus-ring-offset)}.mlv-editor .ProseMirror .is-editor-empty:first-child:before{float:left;height:0;color:var(--mlv-text-placeholder);content:attr(data-placeholder);pointer-events:none}.mlv-editor .tableWrapper{margin-block:var(--mlv-spacing-3);overflow-x:auto}.mlv-editor table{width:100%;border-collapse:collapse;table-layout:fixed}.mlv-editor th,.mlv-editor td{position:relative;min-width:3rem;padding:var(--mlv-spacing-2) var(--mlv-spacing-3);border:var(--mlv-stroke-width) solid var(--mlv-border-normal);vertical-align:top}.mlv-editor th{background:var(--mlv-background-neutral-1);font-weight:var(--mlv-font-weight-semibold)}.mlv-editor .selectedCell:after{position:absolute;inset:0;z-index:1;background:var(--mlv-background-accent-1-pale);content:\"\";opacity:.55;pointer-events:none}.mlv-editor .ProseMirror-focused .selectedCell:after{box-shadow:inset 0 0 0 var(--mlv-stroke-width-medium) var(--mlv-border-focus)}.mlv-editor .column-resize-handle{position:absolute;inset-block:0;inset-inline-end:calc(var(--mlv-stroke-width) * -1);z-index:2;width:calc(var(--mlv-stroke-width) * 2);background:var(--mlv-border-focus);pointer-events:none}.mlv-editor .resize-cursor{cursor:col-resize}@keyframes mlv-editor-upload-spin{to{transform:rotate(1turn)}}@keyframes mlv-editor-ai-chunk-in{0%{opacity:0;filter:blur(.125rem)}to{opacity:1;filter:blur(0)}}@keyframes mlv-editor-ai-caret-pulse{0%,to{opacity:1}50%{opacity:.35}}@media(max-width:40rem){.mlv-editor{--mlv-editor-gutter: var(--mlv-spacing-3)}.mlv-editor__toolbar{flex-wrap:nowrap;overflow-x:auto}.mlv-editor__content{padding-block:var(--mlv-spacing-1-5)}.mlv-editor__status{justify-content:flex-start}.mlv-editor .ProseMirror h1{font-size:var(--mlv-typography-heading-h2-size)}.mlv-editor .ProseMirror h2{font-size:var(--mlv-typography-heading-h3-size)}}@media print{.mlv-editor__toolbar,.mlv-editor__status,.mlv-editor__block-handle,.mlv-editor__drop-indicator,.mlv-editor .mlv-editor-image-upload-status,.mlv-editor__upload-placeholder,.mlv-editor__ai-suggestion-delete,.mlv-editor__ai-caret{display:none}.mlv-editor__ai-suggestion-current{outline:none}.mlv-editor__ai-streaming-chunk{animation:none}.mlv-editor__viewport{overflow:visible}.mlv-editor__view{transform:none}.mlv-editor__content,.mlv-editor .ProseMirror{min-block-size:0;padding:0}.mlv-editor table{break-inside:avoid}}@media(hover:none){.mlv-editor__block-handle{display:none}}@media(prefers-reduced-motion:reduce){.mlv-editor .mlv-form-control-wrapper__control-container,.mlv-editor__view,.mlv-editor__block-handle,.mlv-editor__drop-indicator,.mlv-editor .ProseMirror--block-dragging>*,.mlv-editor .ProseMirror a{transition-duration:var(--mlv-duration-instant)}.mlv-editor__upload-placeholder:after,.mlv-editor__ai-streaming-chunk,.mlv-editor__ai-caret:after{animation:none}}.mlv-editor__drag-ghost{max-block-size:12rem;padding:var(--mlv-spacing-2);overflow:hidden;border-radius:var(--mlv-radius-m);background-color:var(--mlv-background-raised);box-shadow:var(--mlv-shadow-floating);opacity:.9;-webkit-mask-image:linear-gradient(to bottom,black 70%,transparent 100%);mask-image:linear-gradient(to bottom,black 70%,transparent 100%);scale:.85;transform-origin:top left}}\n"] }]
|
|
10220
|
-
|
|
11699
|
+
'[style.--mlv-editor-height]': '_heightStyle()',
|
|
11700
|
+
'[style.--mlv-editor-min-height]': '_minHeightStyle()',
|
|
11701
|
+
'[style.--mlv-editor-max-height]': '_maxHeightStyle()',
|
|
11702
|
+
}, template: "<mlv-form-control-wrapper (clear)=\"clearValue()\">\n @if (label() || hint()) {\n <mlv-label\n [for]=\"_ownLabelFor()\"\n [id]=\"_labelId()\"\n (click)=\"_onLabelClick()\"\n >\n {{ label() }}\n @if (hint()) {\n <mlv-hint>{{ hint() }}</mlv-hint>\n }\n </mlv-label>\n }\n\n <ng-template mlvFormControlWrapperControl>\n <div #surface class=\"mlv-editor__surface\">\n <!--\n Declared once and stamped by one outlet: before or after the viewport\n for the docked bar (#416), so the DOM order is always the visual\n order, with no CSS `order`; into the selection bubble's overlay for\n the floating appearance (#483). Re-stamping re-creates the view, which\n closes an open toolbar popup; projected slot content is re-attached.\n Neither stamps anything while `readonly` (#498): no band, no reserved\n space; `_toolbarRendered()` is the one switch.\n -->\n <ng-template #toolbarTemplate>\n <div #toolbarBand class=\"mlv-editor__toolbar-band\">\n <div\n mlvEditorToolbarRoot\n #toolbarRoot=\"mlvEditorToolbarRoot\"\n class=\"mlv-editor__toolbar\"\n [ariaLabel]=\"_toolbarAriaLabel()\"\n [disabled]=\"computedDisabled()\"\n [measureTarget]=\"surface\"\n >\n @if (_hasToolbarDef()) {\n <ng-content select=\"[mlvEditorToolbar]\" />\n @for (definition of _toolbarDefs(); track definition) {\n @if (definition.templateRef; as toolbarTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"toolbarTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: _toolbarContext }\"\n />\n }\n }\n } @else {\n <ng-content select=\"[mlvEditorToolbarStart]\" />\n @for (definition of _toolbarStartDefs(); track definition) {\n @if (definition.templateRef; as startTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"startTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: _toolbarContext }\"\n />\n }\n }\n <div mlvFade class=\"mlv-editor__toolbar-scroll\">\n <mlv-toolbar>\n <mlv-editor-undo-redo />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n />\n <mlv-editor-zoom />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n />\n <mlv-editor-heading />\n <mlv-editor-list />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n [hidden]=\"toolbarRoot.narrow()\"\n />\n <mlv-editor-inline-marks [hidden]=\"toolbarRoot.narrow()\" />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n [hidden]=\"toolbarRoot.narrow()\"\n />\n <mlv-editor-text-color />\n <mlv-editor-highlight />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n [hidden]=\"toolbarRoot.narrow()\"\n />\n <mlv-editor-alignment [hidden]=\"toolbarRoot.narrow()\" />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n [hidden]=\"toolbarRoot.narrow()\"\n />\n <mlv-editor-link />\n <mlv-editor-table />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n [hidden]=\"toolbarRoot.narrow()\"\n />\n <mlv-editor-block-insert [hidden]=\"toolbarRoot.narrow()\" />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n [hidden]=\"toolbarRoot.narrow()\"\n />\n <mlv-editor-image-upload />\n </mlv-toolbar>\n </div>\n <mlv-editor-toolbar-overflow />\n @for (definition of _toolbarEndDefs(); track definition) {\n @if (definition.templateRef; as endTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"endTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: _toolbarContext }\"\n />\n }\n }\n <ng-content select=\"[mlvEditorToolbarEnd]\" />\n }\n </div>\n </div>\n </ng-template>\n\n @if (_barRendered() && toolbarPosition() === 'top') {\n <ng-container [ngTemplateOutlet]=\"toolbarTemplate\" />\n }\n\n <div #viewport class=\"mlv-editor__viewport\">\n <div #view class=\"mlv-editor__view\">\n <div #content class=\"mlv-editor__content\"></div>\n <mlv-editor-table-controls [layer]=\"view\" />\n </div>\n </div>\n @if (_barRendered() && toolbarPosition() === 'bottom') {\n <ng-container [ngTemplateOutlet]=\"toolbarTemplate\" />\n }\n @if (toolbarAppearance() === 'floating') {\n <ng-template mlvEditorBubble [viewport]=\"viewport\" [capped]=\"_capped()\">\n @if (_toolbarRendered()) {\n <ng-container [ngTemplateOutlet]=\"toolbarTemplate\" />\n }\n </ng-template>\n }\n <mlv-editor-image-upload-status />\n </div>\n </ng-template>\n\n @if (message()) {\n <mlv-message [state]=\"resolvedState()\" [id]=\"id() + '-message'\">\n {{ message() }}\n </mlv-message>\n }\n</mlv-form-control-wrapper>\n\n<div\n class=\"mlv-editor__status\"\n [attr.aria-disabled]=\"computedDisabled() || null\"\n>\n <ng-content select=\"[mlvEditorStatus]\" />\n <mlv-spacer />\n <mlv-editor-status [characterLimit]=\"characterLimit()\" />\n</div>\n", styles: ["@layer mlv.components{.mlv-editor{--mlv-editor-gutter: var(--mlv-spacing-6);--mlv-editor-measure: 45rem;box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor *{box-sizing:border-box}.mlv-editor{display:block;color:var(--mlv-text-primary)}.mlv-editor .mlv-form-control-wrapper__control-container{height:auto;min-height:var(--form-ctrl-height);padding:0;overflow:clip;background:var(--mlv-background-base);transition:border-color var(--mlv-duration-fast) var(--mlv-ease-default),box-shadow var(--mlv-duration-fast) var(--mlv-ease-default)}.mlv-editor .mlv-form-control-wrapper__control-row{align-items:stretch}.mlv-form-control-wrapper--state-error .mlv-editor__surface{box-shadow:inset 0 0 0 var(--mlv-stroke-width) var(--mlv-border-error)}.mlv-editor__surface{display:flex;flex:1 1 auto;flex-direction:column;min-width:0;width:100%;background:var(--mlv-elevation-bg-3)}.mlv-editor__toolbar{display:flex;flex-wrap:nowrap;align-items:center;gap:var(--mlv-spacing-1);min-width:0;padding:var(--mlv-padding-xs);overflow:hidden;border-block-end:var(--mlv-stroke-width) solid var(--mlv-border-normal);background:var(--mlv-elevation-bg-3)}.mlv-editor__toolbar-band{display:flex;flex:0 0 auto;flex-direction:column;min-inline-size:0}.mlv-editor--toolbar-bottom .mlv-editor__toolbar{border-block-start:var(--mlv-stroke-width) solid var(--mlv-border-normal);border-block-end:0}.mlv-editor__toolbar [hidden]{display:none!important}.mlv-editor__toolbar-scroll{flex:1 1 auto;min-width:0}.mlv-editor__toolbar-scroll>.mlv-toolbar{width:max-content;min-width:100%;flex-wrap:nowrap}.mlv-editor .mlv-editor-toolbar__separator{flex:0 0 auto;min-height:var(--mlv-height-s);margin-inline:var(--mlv-spacing-1)}.mlv-editor .mlv-editor-toolbar__overflow,.mlv-editor .mlv-editor-undo-redo,.mlv-editor .mlv-editor-inline-marks,.mlv-editor .mlv-editor-block-insert{display:inline-flex;flex:0 0 auto;flex-direction:row;align-items:center;gap:var(--mlv-spacing-1)}.mlv-editor mlv-editor-zoom,.mlv-editor mlv-editor-heading,.mlv-editor mlv-editor-list,.mlv-editor mlv-editor-text-color,.mlv-editor mlv-editor-highlight,.mlv-editor mlv-editor-alignment,.mlv-editor mlv-editor-link,.mlv-editor mlv-editor-table,.mlv-editor mlv-editor-image-upload{flex:0 0 auto}.mlv-editor__viewport{flex:1 1 auto;min-block-size:var(--mlv-editor-min-height, 8rem)}.mlv-editor--capped .mlv-editor__surface{block-size:var(--mlv-editor-height, auto);max-block-size:var(--mlv-editor-max-height, none)}.mlv-editor--capped .mlv-editor__viewport{min-block-size:0;overflow:auto;overscroll-behavior:contain}.mlv-editor__view{position:relative;isolation:isolate;min-inline-size:100%;zoom:var(--mlv-editor-zoom, 1)}.mlv-editor--capped .mlv-editor__view{min-inline-size:0;inline-size:calc(100% * var(--mlv-editor-zoom, 1))}.mlv-editor__content{min-block-size:var(--mlv-editor-min-height, 8rem);padding-block:var(--mlv-spacing-2);padding-inline:var(--mlv-editor-gutter);border:0;outline:none}.mlv-editor--capped .mlv-editor__content{min-block-size:calc(var(--mlv-editor-min-height, 8rem) / var(--mlv-editor-zoom, 1))}.mlv-editor__block-handle{position:absolute;inset-inline-start:max(0rem,(100% - 2 * var(--mlv-editor-gutter) - var(--mlv-editor-measure)) / 2);display:inline-flex;align-items:center;justify-content:center;inline-size:var(--mlv-editor-gutter);block-size:var(--mlv-height-xs);border-radius:var(--mlv-radius-s);color:var(--mlv-text-tertiary);cursor:grab;opacity:0;pointer-events:none;transition:opacity var(--mlv-duration-fast) var(--mlv-ease-default)}.mlv-editor__block-handle svg{inline-size:var(--mlv-spacing-4);block-size:var(--mlv-spacing-4);fill:currentcolor}.mlv-editor__block-handle:hover{background-color:var(--mlv-background-neutral-1-hover);color:var(--mlv-text-secondary)}.mlv-editor__block-handle[data-visible=true]{opacity:1;pointer-events:auto}.mlv-editor__drop-indicator{position:absolute;inset-inline:calc(max(0rem,(100% - 2 * var(--mlv-editor-gutter) - var(--mlv-editor-measure)) / 2) + var(--mlv-editor-gutter));block-size:var(--mlv-stroke-width-medium);border-radius:var(--mlv-radius-full);background-color:var(--mlv-border-focus);opacity:0;pointer-events:none;transform:translateY(-50%);transition:top var(--mlv-duration-normal) var(--mlv-ease-out),opacity var(--mlv-duration-fast) var(--mlv-ease-default)}.mlv-editor__drop-indicator[data-visible=true]{opacity:1}.mlv-editor .mlv-editor__block--dragging{opacity:var(--mlv-disabled-opacity)}.mlv-editor__status{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:var(--mlv-spacing-2);min-block-size:var(--mlv-spacing-4);margin-block-start:var(--mlv-spacing-1)}.mlv-editor__upload-placeholder{position:relative;display:inline-block;inline-size:var(--mlv-height-l);block-size:var(--mlv-height-l);margin-inline:var(--mlv-spacing-1);border:var(--mlv-stroke-width) dashed var(--mlv-border-focus);border-radius:var(--mlv-radius-m);background:var(--mlv-background-accent-1-pale);vertical-align:middle}.mlv-editor__upload-placeholder:after{position:absolute;inset:var(--mlv-spacing-2);border:var(--mlv-stroke-width-medium) solid var(--mlv-border-normal);border-block-start-color:var(--mlv-border-focus);border-radius:var(--mlv-radius-full);animation:mlv-editor-upload-spin var(--mlv-duration-slower) var(--mlv-ease-linear) infinite;content:\"\"}.mlv-editor__ai-streaming{border-radius:var(--mlv-radius-xs);background-color:var(--mlv-background-accent-1-pale)}.mlv-editor__ai-streaming-chunk{animation:mlv-editor-ai-chunk-in var(--mlv-duration-normal) var(--mlv-ease-out)}.mlv-editor__ai-caret{position:relative;display:inline-block;inline-size:0;block-size:1em;pointer-events:none}.mlv-editor__ai-caret:after{position:absolute;inset-block:0;inset-inline-start:.0625rem;inline-size:.35em;border-radius:var(--mlv-radius-xs);background:linear-gradient(to bottom,var(--mlv-background-accent-1),var(--mlv-background-accent-2));content:\"\";animation:mlv-editor-ai-caret-pulse var(--mlv-duration-sluggish) var(--mlv-ease-in-out) infinite}.mlv-editor__ai-suggestion-insert{border-radius:var(--mlv-radius-xs);background-color:var(--mlv-background-success-pale)}.mlv-editor__ai-suggestion-delete{border-radius:var(--mlv-radius-xs);background-color:var(--mlv-background-danger-1-pale);color:var(--mlv-text-negative);text-decoration-line:line-through}.mlv-editor__ai-suggestion-current{outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:var(--mlv-focus-ring-offset)}.mlv-editor--content-width-default{--mlv-editor-measure: 45rem}.mlv-editor--content-width-wide{--mlv-editor-measure: 60rem}.mlv-editor--content-width-full{--mlv-editor-measure: 100%}.mlv-editor--focused .mlv-form-control-wrapper__control-container{border-color:var(--mlv-border-focus);box-shadow:var(--mlv-focus-ring)}.mlv-editor--readonly .ProseMirror{cursor:text}.mlv-editor--readonly .ProseMirror ::selection{background:var(--mlv-background-accent-1-pale)}.mlv-editor--disabled{opacity:var(--mlv-disabled-opacity)}.mlv-editor--disabled .mlv-editor__surface,.mlv-editor--disabled .mlv-editor__status{pointer-events:none}.mlv-editor--disabled .mlv-editor__content,.mlv-editor--disabled .ProseMirror{cursor:default;-webkit-user-select:none;user-select:none}.mlv-editor .ProseMirror{min-block-size:inherit;max-inline-size:var(--mlv-editor-measure);margin-inline:auto;color:var(--mlv-text-primary);font-family:var(--mlv-typography-family-text);font-size:var(--mlv-typography-body-m-size);font-weight:var(--mlv-typography-body-m-weight);line-height:var(--mlv-typography-body-m-line-height);overflow-wrap:anywhere;white-space:pre-wrap}.mlv-editor .ProseMirror:focus-visible{outline:none}.mlv-editor .ProseMirror>*:first-child{margin-block-start:0}.mlv-editor .ProseMirror>*:last-child{margin-block-end:0}.mlv-editor .ProseMirror p{margin-block:0 var(--mlv-spacing-3)}.mlv-editor .ProseMirror h1,.mlv-editor .ProseMirror h2,.mlv-editor .ProseMirror h3,.mlv-editor .ProseMirror h4,.mlv-editor .ProseMirror h5,.mlv-editor .ProseMirror h6{margin-block:var(--mlv-spacing-6) var(--mlv-spacing-3);color:var(--mlv-text-heading);font-family:var(--mlv-typography-family-heading)}.mlv-editor .ProseMirror h1{font-size:var(--mlv-typography-heading-h1-size);font-weight:var(--mlv-typography-heading-h1-weight);line-height:var(--mlv-typography-heading-h1-line-height)}.mlv-editor .ProseMirror h2{font-size:var(--mlv-typography-heading-h2-size);font-weight:var(--mlv-typography-heading-h2-weight);line-height:var(--mlv-typography-heading-h2-line-height)}.mlv-editor .ProseMirror h3{font-size:var(--mlv-typography-heading-h3-size);font-weight:var(--mlv-typography-heading-h3-weight);line-height:var(--mlv-typography-heading-h3-line-height)}.mlv-editor .ProseMirror h4{font-size:var(--mlv-typography-heading-h4-size);font-weight:var(--mlv-typography-heading-h4-weight);line-height:var(--mlv-typography-heading-h4-line-height)}.mlv-editor .ProseMirror h5{font-size:var(--mlv-typography-heading-h5-size);font-weight:var(--mlv-typography-heading-h5-weight);line-height:var(--mlv-typography-heading-h5-line-height)}.mlv-editor .ProseMirror h6{font-size:var(--mlv-typography-heading-h6-size);font-weight:var(--mlv-typography-heading-h6-weight);line-height:var(--mlv-typography-heading-h6-line-height)}.mlv-editor .ProseMirror h1{letter-spacing:var(--mlv-typography-heading-h1-letter-spacing)}.mlv-editor .ProseMirror h2{letter-spacing:var(--mlv-typography-heading-h2-letter-spacing)}.mlv-editor .ProseMirror h3,.mlv-editor .ProseMirror h4,.mlv-editor .ProseMirror h5,.mlv-editor .ProseMirror h6{letter-spacing:var(--mlv-letter-spacing-normal)}.mlv-editor .ProseMirror strong{font-weight:var(--mlv-font-weight-bold)}.mlv-editor .ProseMirror em{font-style:italic}.mlv-editor .ProseMirror s{text-decoration:line-through}.mlv-editor .ProseMirror u{text-underline-offset:var(--mlv-spacing-0-5)}.mlv-editor .ProseMirror a{color:var(--mlv-text-action);text-decoration:underline;text-underline-offset:var(--mlv-spacing-0-5);transition:color var(--mlv-duration-fast) var(--mlv-ease-default)}.mlv-editor .ProseMirror a:visited{color:var(--mlv-text-action)}.mlv-editor .ProseMirror a:hover{color:var(--mlv-text-action-hover)}.mlv-editor .ProseMirror a:focus-visible{border-radius:var(--mlv-radius-xs);outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:var(--mlv-focus-ring-offset)}.mlv-editor .ProseMirror ul,.mlv-editor .ProseMirror ol{padding-inline-start:var(--mlv-spacing-6);margin-block:0 var(--mlv-spacing-3)}.mlv-editor .ProseMirror ul ul,.mlv-editor .ProseMirror ul ol,.mlv-editor .ProseMirror ol ul,.mlv-editor .ProseMirror ol ol{margin-block:var(--mlv-spacing-1) 0}.mlv-editor .ProseMirror ul{list-style:disc}.mlv-editor .ProseMirror ul ul{list-style:circle}.mlv-editor .ProseMirror ol{list-style:decimal}.mlv-editor .ProseMirror li{margin-block:var(--mlv-spacing-1)}.mlv-editor .ProseMirror li>p{margin:0}.mlv-editor .ProseMirror ul[data-type=taskList]{padding:0;list-style:none}.mlv-editor .ProseMirror ul[data-type=taskList] li[data-type=taskItem]{display:flex;align-items:flex-start;gap:var(--mlv-spacing-2)}.mlv-editor .ProseMirror ul[data-type=taskList] li[data-type=taskItem]>label{display:inline-flex;align-items:center;min-block-size:var(--mlv-height-xs);cursor:pointer;-webkit-user-select:none;user-select:none}.mlv-editor .ProseMirror ul[data-type=taskList] li[data-type=taskItem]>label input{accent-color:var(--mlv-background-accent-1)}.mlv-editor .ProseMirror ul[data-type=taskList] li[data-type=taskItem]>label input:focus-visible{outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:var(--mlv-focus-ring-offset)}.mlv-editor .ProseMirror ul[data-type=taskList] li[data-type=taskItem]>div{flex:1 1 auto;min-width:0}.mlv-editor .ProseMirror blockquote{padding-inline-start:var(--mlv-spacing-4);margin:var(--mlv-spacing-4) 0;border-inline-start:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);color:var(--mlv-text-secondary)}.mlv-editor .ProseMirror code{padding:var(--mlv-spacing-0-5) var(--mlv-spacing-1);border-radius:var(--mlv-radius-s);background:var(--mlv-background-neutral-1);color:var(--mlv-text-primary);font-family:var(--mlv-typography-family-code);font-size:var(--mlv-typography-code-size)}.mlv-editor .ProseMirror pre{padding:var(--mlv-padding-m);margin-block:var(--mlv-spacing-4);overflow:auto;border:var(--mlv-stroke-width) solid var(--mlv-border-normal);border-radius:var(--mlv-radius-m);background:var(--mlv-background-sunken);color:var(--mlv-text-primary);font-family:var(--mlv-typography-family-code);font-size:var(--mlv-typography-code-size);line-height:var(--mlv-typography-body-m-line-height);tab-size:2}.mlv-editor .ProseMirror pre code{padding:0;background:transparent;color:inherit;font-size:inherit}.mlv-editor .ProseMirror hr{height:var(--mlv-stroke-width);margin-block:var(--mlv-spacing-6);border:0;background:var(--mlv-border-normal)}.mlv-editor .ProseMirror img{display:block;max-inline-size:100%;block-size:auto;margin-block:var(--mlv-spacing-4);border-radius:var(--mlv-radius-m)}.mlv-editor .ProseMirror img.ProseMirror-selectednode{outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:0}.mlv-editor .ProseMirror .ProseMirror-selectednode,.mlv-editor .ProseMirror [data-resize-state=true]{border-radius:var(--mlv-radius-s);outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:var(--mlv-spacing-1)}.mlv-editor .ProseMirror [data-resize-container]{margin-block:var(--mlv-spacing-4)}.mlv-editor .ProseMirror [data-resize-container] img{margin-block:0}.mlv-editor .ProseMirror [data-resize-container].ProseMirror-selectednode,.mlv-editor .ProseMirror [data-resize-container][data-resize-state=true]{outline:none}.mlv-editor .ProseMirror [data-resize-container].ProseMirror-selectednode>[data-resize-wrapper],.mlv-editor .ProseMirror [data-resize-container][data-resize-state=true]>[data-resize-wrapper]{border-radius:var(--mlv-radius-m);outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:0}.mlv-editor .ProseMirror [data-resize-handle],.mlv-editor .ProseMirror .resize-handle{inline-size:var(--mlv-spacing-3);block-size:var(--mlv-spacing-3);border:var(--mlv-stroke-width) solid var(--mlv-background-base);border-radius:var(--mlv-radius-full);background:var(--mlv-background-accent-1);box-shadow:var(--mlv-shadow-raised)}.mlv-editor .ProseMirror [data-resize-handle]:focus-visible,.mlv-editor .ProseMirror .resize-handle:focus-visible{outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:var(--mlv-focus-ring-offset)}.mlv-editor .ProseMirror .is-editor-empty:first-child:before{float:left;height:0;color:var(--mlv-text-placeholder);content:attr(data-placeholder);pointer-events:none}.mlv-editor .tableWrapper{margin-block:var(--mlv-spacing-3);overflow-x:auto}.mlv-editor table{width:100%;border-collapse:collapse;table-layout:fixed}.mlv-editor th,.mlv-editor td{position:relative;min-width:3rem;padding:var(--mlv-spacing-2) var(--mlv-spacing-3);border:var(--mlv-stroke-width) solid var(--mlv-border-normal);vertical-align:top}.mlv-editor th{background:var(--mlv-background-neutral-1);font-weight:var(--mlv-font-weight-semibold)}.mlv-editor .selectedCell:after{position:absolute;inset:0;z-index:1;background:var(--mlv-background-accent-1-pale);content:\"\";opacity:.55;pointer-events:none}.mlv-editor .ProseMirror-focused .selectedCell:after{box-shadow:inset 0 0 0 var(--mlv-stroke-width-medium) var(--mlv-border-focus)}.mlv-editor .column-resize-handle{position:absolute;inset-block:0;inset-inline-end:calc(var(--mlv-stroke-width) * -1);z-index:2;width:calc(var(--mlv-stroke-width) * 2);background:var(--mlv-border-focus);pointer-events:none}.mlv-editor .resize-cursor{cursor:col-resize}@keyframes mlv-editor-upload-spin{to{transform:rotate(1turn)}}@keyframes mlv-editor-ai-chunk-in{0%{opacity:0;filter:blur(.125rem)}to{opacity:1;filter:blur(0)}}@keyframes mlv-editor-ai-caret-pulse{0%,to{opacity:1}50%{opacity:.35}}.mlv-editor--toolbar-sticky.mlv-editor--toolbar-top .mlv-editor__toolbar-band{position:sticky;inset-block-start:var(--mlv-editor-toolbar-sticky-offset, 0);z-index:var(--mlv-z-raised)}.mlv-editor--toolbar-sticky.mlv-editor--toolbar-bottom .mlv-editor__toolbar-band{position:sticky;inset-block-end:var(--mlv-editor-toolbar-sticky-offset, 0);z-index:var(--mlv-z-raised)}.mlv-editor-bubble{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor-bubble *{box-sizing:border-box}.mlv-editor-bubble .mlv-editor-toolbar__separator{flex:0 0 auto;min-height:var(--mlv-height-s);margin-inline:var(--mlv-spacing-1)}.mlv-editor-bubble .mlv-editor-toolbar__overflow,.mlv-editor-bubble .mlv-editor-undo-redo,.mlv-editor-bubble .mlv-editor-inline-marks,.mlv-editor-bubble .mlv-editor-block-insert{display:inline-flex;flex:0 0 auto;flex-direction:row;align-items:center;gap:var(--mlv-spacing-1)}.mlv-editor-bubble mlv-editor-zoom,.mlv-editor-bubble mlv-editor-heading,.mlv-editor-bubble mlv-editor-list,.mlv-editor-bubble mlv-editor-text-color,.mlv-editor-bubble mlv-editor-highlight,.mlv-editor-bubble mlv-editor-alignment,.mlv-editor-bubble mlv-editor-link,.mlv-editor-bubble mlv-editor-table,.mlv-editor-bubble mlv-editor-image-upload{flex:0 0 auto}.mlv-editor-bubble{max-inline-size:calc(100vw - 2 * var(--mlv-spacing-2));border-radius:var(--mlv-radius-panel);background:var(--mlv-elevation-bg-4);box-shadow:var(--mlv-shadow-floating);color:var(--mlv-text-primary);animation:mlv-editor-bubble-enter var(--mlv-duration-fast) var(--mlv-ease-out)}.mlv-editor-bubble--hidden{visibility:hidden;animation:none}.mlv-editor-bubble .mlv-editor__toolbar-band{flex:1 1 auto;min-inline-size:0}.mlv-editor-bubble .mlv-editor__toolbar{border:0;border-radius:inherit;background:transparent}@keyframes mlv-editor-bubble-enter{0%{opacity:0}}@media(max-width:40rem){.mlv-editor{--mlv-editor-gutter: var(--mlv-spacing-3)}.mlv-editor__toolbar{flex-wrap:nowrap;overflow-x:auto}.mlv-editor__content{padding-block:var(--mlv-spacing-1-5)}.mlv-editor__status{justify-content:flex-start}.mlv-editor .ProseMirror h1{font-size:var(--mlv-typography-heading-h2-size)}.mlv-editor .ProseMirror h2{font-size:var(--mlv-typography-heading-h3-size)}}@media print{.mlv-editor__toolbar-band,.mlv-editor__toolbar,.mlv-editor__status,.mlv-editor__block-handle,.mlv-editor__drop-indicator,.mlv-editor .mlv-editor-image-upload-status,.mlv-editor__upload-placeholder,.mlv-editor__ai-suggestion-delete,.mlv-editor__ai-caret{display:none}.mlv-editor__ai-suggestion-current{outline:none}.mlv-editor__ai-streaming-chunk{animation:none}.mlv-editor__viewport,.mlv-editor--capped .mlv-editor__viewport{overflow:visible;padding-block:0}.mlv-editor--capped .mlv-editor__surface{block-size:auto;max-block-size:none}.mlv-editor__view,.mlv-editor--capped .mlv-editor__view{zoom:1;inline-size:auto;min-inline-size:0}.mlv-editor__content,.mlv-editor--capped .mlv-editor__content,.mlv-editor .ProseMirror{min-block-size:0;padding:0}.mlv-editor table{break-inside:avoid}}@media print{.mlv-editor-bubble{display:none}}@media(hover:none){.mlv-editor__block-handle{display:none}}@media(prefers-reduced-motion:reduce){.mlv-editor .mlv-form-control-wrapper__control-container,.mlv-editor__block-handle,.mlv-editor__drop-indicator,.mlv-editor .ProseMirror a{transition-duration:var(--mlv-duration-instant)}.mlv-editor__upload-placeholder:after,.mlv-editor__ai-streaming-chunk,.mlv-editor__ai-caret:after{animation:none}.mlv-editor-bubble{animation:none}}.mlv-editor__drag-ghost{max-block-size:12rem;padding:var(--mlv-spacing-2);overflow:hidden;border-radius:var(--mlv-radius-m);background-color:var(--mlv-background-accent-1-pale);opacity:.9;-webkit-mask-image:linear-gradient(to bottom,black 70%,transparent 100%);mask-image:linear-gradient(to bottom,black 70%,transparent 100%)}}\n"] }]
|
|
11703
|
+
}], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], format: [{ type: i0.Input, args: [{ isSignal: true, alias: "format", required: false }] }], contentWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "contentWidth", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], minHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "minHeight", required: false }] }], maxHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxHeight", required: false }] }], toolbarPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "toolbarPosition", required: false }] }], toolbarAppearance: [{ type: i0.Input, args: [{ isSignal: true, alias: "toolbarAppearance", required: false }] }], toolbarSticky: [{ type: i0.Input, args: [{ isSignal: true, alias: "toolbarSticky", required: false }] }], extensions: [{ type: i0.Input, args: [{ isSignal: true, alias: "extensions", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], characterLimit: [{ type: i0.Input, args: [{ isSignal: true, alias: "characterLimit", required: false }] }], ariaLabelledBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabelledBy", required: false }] }], ariaDescribedBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaDescribedBy", required: false }] }], imageUploader: [{ type: i0.Input, args: [{ isSignal: true, alias: "imageUploader", required: false }] }], imageUploadOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "imageUploadOptions", required: false }] }], aiProvider: [{ type: i0.Input, args: [{ isSignal: true, alias: "aiProvider", required: false }] }], editorReady: [{ type: i0.Output, args: ["editorReady"] }], focus: [{ type: i0.Output, args: ["focus"] }], blur: [{ type: i0.Output, args: ["blur"] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], transaction: [{ type: i0.Output, args: ["transaction"] }], editorError: [{ type: i0.Output, args: ["editorError"] }], imageUploadSuccess: [{ type: i0.Output, args: ["imageUploadSuccess"] }], imageUploadFailure: [{ type: i0.Output, args: ["imageUploadFailure"] }], imageUploadCancelled: [{ type: i0.Output, args: ["imageUploadCancelled"] }], _content: [{ type: i0.ViewChild, args: ['content', { isSignal: true }] }], _view: [{ type: i0.ViewChild, args: ['view', { isSignal: true }] }], _toolbarBand: [{ type: i0.ViewChild, args: ['toolbarBand', { isSignal: true }] }], _toolbarDefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => MlvEditorToolbarDef), { isSignal: true }] }], _toolbarStartDefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => MlvEditorToolbarStartDef), { isSignal: true }] }], _toolbarEndDefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => MlvEditorToolbarEndDef), { isSignal: true }] }] } });
|
|
10221
11704
|
|
|
10222
11705
|
/**
|
|
10223
11706
|
* Generated bundle index. Do not edit.
|