ngwr 14.9.1 → 14.10.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.
@@ -1821,6 +1821,16 @@ function withoutMarks(node, known) {
1821
1821
  * editor is read-only and carries `wr-editor--refused`: the model still holds the
1822
1822
  * refused value, and an edit would write the document on screen over it.
1823
1823
  *
1824
+ * **`readonly` never mounts ProseMirror.** The document is drawn as ordinary
1825
+ * Angular markup instead — the same rendering the server prerenders — so a page
1826
+ * showing thirty saved comments boots no editor at all, and the headings, lists
1827
+ * and tables of what it shows stay in the accessibility tree rather than being
1828
+ * swallowed by a textbox, every descendant of which is presentational. It is
1829
+ * lazy, not permanent: the first time `readonly` is lifted the editor mounts,
1830
+ * and from there behaves exactly as one that never was read-only. Setting it
1831
+ * again keeps the mounted view — throwing a live session's undo history away to
1832
+ * save one view would cost more than the view.
1833
+ *
1824
1834
  * Headings stop at three levels: an h4–h6 read from HTML or markdown becomes an
1825
1835
  * h3 rather than a paragraph. Underline exists in `html` and `json` only —
1826
1836
  * markdown cannot spell it, so in that format the tool, the shortcut and the mark
@@ -1859,13 +1869,22 @@ class WrEditor {
1859
1869
  */
1860
1870
  disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
1861
1871
  /**
1862
- * Refuse edits while the text stays focusable, selectable and announced.
1872
+ * Refuse edits while the text stays reachable, selectable and announced.
1863
1873
  * Bound automatically from the field's readonly state when used with
1864
1874
  * `[formField]`.
1865
1875
  *
1866
- * The surface keeps its tab stop and reports `aria-readonly`; typing, paste,
1867
- * drop, the shortcuts and every toolbar command are refused. The toolbar goes
1868
- * inert, since none of its tools can apply.
1876
+ * Set before the editor mounts, it means ProseMirror is never started: the
1877
+ * document is drawn as ordinary markup in a focusable `role="group"` that
1878
+ * keeps the tab stop and carries the editor's name, so a viewer costs no
1879
+ * editor and the structure of what it shows is still announced. `aria-readonly`
1880
+ * is deliberately absent there — ARIA does not allow it on `group`, and
1881
+ * `aria-disabled`, the only attribute that would apply, means something else —
1882
+ * so the cue is the `wr-editor--readonly` class. The toolbar goes inert, since
1883
+ * none of its tools can apply; a pure viewer binds `[toolbar]="false"`.
1884
+ *
1885
+ * A view that is already mounted stays mounted, refuses typing, paste, drop,
1886
+ * the shortcuts and every toolbar command, and reports `aria-readonly` on its
1887
+ * surface as before.
1869
1888
  *
1870
1889
  * @default false
1871
1890
  */
@@ -1878,7 +1897,8 @@ class WrEditor {
1878
1897
  placeholder = input('', /* @ts-ignore */
1879
1898
  ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
1880
1899
  /**
1881
- * Accessible name of the text surface. Falls back to the surrounding
1900
+ * Accessible name of the text — of the surface, or of the document a
1901
+ * read-only editor draws in its place. Falls back to the surrounding
1882
1902
  * `<wr-form-field>`'s label, then to `editor.label`.
1883
1903
  */
1884
1904
  ariaLabel = input(null, /* @ts-ignore */
@@ -1934,6 +1954,13 @@ class WrEditor {
1934
1954
  /** ProseMirror owns the surface from here on; until then the preview stands in. */
1935
1955
  mounted = signal(false, /* @ts-ignore */
1936
1956
  ...(ngDevMode ? [{ debugName: "mounted" }] : /* istanbul ignore next */ []));
1957
+ /**
1958
+ * Read-only and nothing mounted: the preview is not standing in for a surface,
1959
+ * it IS the editor, and the surface is not rendered at all. One id, one name
1960
+ * and one tab stop move onto it with the job. @internal
1961
+ */
1962
+ staticMode = computed(() => !this.mounted() && this.readonly(), /* @ts-ignore */
1963
+ ...(ngDevMode ? [{ debugName: "staticMode" }] : /* istanbul ignore next */ []));
1937
1964
  focused = signal(false, /* @ts-ignore */
1938
1965
  ...(ngDevMode ? [{ debugName: "focused" }] : /* istanbul ignore next */ []));
1939
1966
  editorState = signal(null, /* @ts-ignore */
@@ -1953,8 +1980,12 @@ class WrEditor {
1953
1980
  /** Apple keyboards name the modifier ⌘. Read in the browser only — see `mount()`. */
1954
1981
  apple = signal(false, /* @ts-ignore */
1955
1982
  ...(ngDevMode ? [{ debugName: "apple" }] : /* istanbul ignore next */ []));
1956
- surface = viewChild.required('surface', /* @ts-ignore */
1983
+ // Optional, both of them: exactly one is in the DOM at a time, and which one
1984
+ // is the difference between a mounted editor and the static document.
1985
+ surface = viewChild('surface', /* @ts-ignore */
1957
1986
  ...(ngDevMode ? [{ debugName: "surface" }] : /* istanbul ignore next */ []));
1987
+ staticDoc = viewChild('staticDoc', /* @ts-ignore */
1988
+ ...(ngDevMode ? [{ debugName: "staticDoc" }] : /* istanbul ignore next */ []));
1958
1989
  // `read`, because `#tool` sits on a `wr-btn` and would otherwise resolve to the component.
1959
1990
  toolButtons = viewChildren('tool', { ...(ngDevMode ? { debugName: "toolButtons" } : /* istanbul ignore next */ {}), read: ElementRef });
1960
1991
  linkPopover = viewChild('linkPopover', /* @ts-ignore */
@@ -1970,6 +2001,12 @@ class WrEditor {
1970
2001
  */
1971
2002
  lastSerialized = undefined;
1972
2003
  linkToSurface = false;
2004
+ /**
2005
+ * Whether the static path has already named the refusal the model is holding.
2006
+ * A read-only editor reports one itself, and a mount that follows would
2007
+ * otherwise warn a second time about the same value.
2008
+ */
2009
+ refusalWarned = false;
1973
2010
  // Labels
1974
2011
  toolbarLabel = readI18nText('editor.toolbar', 'Formatting');
1975
2012
  linkUrlLabel = readI18nText('editor.linkUrl', 'Link address');
@@ -2092,7 +2129,12 @@ class WrEditor {
2092
2129
  list_item: listItemView({ done: this.taskDoneLabel(), todo: this.taskTodoLabel() }),
2093
2130
  }), /* @ts-ignore */
2094
2131
  ...(ngDevMode ? [{ debugName: "nodeViews" }] : /* istanbul ignore next */ []));
2095
- ariaPlaceholder = computed(() => (this.placeholder() && this.empty() ? this.placeholder() : null), /* @ts-ignore */
2132
+ /**
2133
+ * Nothing in the static mode: a hint for input, where no input is taken, and
2134
+ * `aria-placeholder` is not among the attributes `role="group"` allows — so
2135
+ * one drawn there would be text no screen reader ever reaches.
2136
+ */
2137
+ ariaPlaceholder = computed(() => this.placeholder() && this.empty() && !this.staticMode() ? this.placeholder() : null, /* @ts-ignore */
2096
2138
  ...(ngDevMode ? [{ debugName: "ariaPlaceholder" }] : /* istanbul ignore next */ []));
2097
2139
  // Link panel
2098
2140
  linkDraft = signal('', /* @ts-ignore */
@@ -2101,9 +2143,10 @@ class WrEditor {
2101
2143
  linkActive = computed(() => this.toolStates().get('link')?.active ?? false, /* @ts-ignore */
2102
2144
  ...(ngDevMode ? [{ debugName: "linkActive" }] : /* istanbul ignore next */ []));
2103
2145
  /**
2104
- * The value rendered by Angular until ProseMirror takes over: on the server,
2105
- * and for the first browser frame. Read from the doc JSON, so it shows exactly
2106
- * what the schema kept — the same sanitised document the editor will mount.
2146
+ * The value rendered by Angular rather than by ProseMirror: on the server, for
2147
+ * the first browser frame, and — for as long as it lasts — in the static mode,
2148
+ * where no mount is coming. Read from the doc JSON, so it shows exactly what
2149
+ * the schema kept: the same sanitised document the editor would mount.
2107
2150
  */
2108
2151
  preview = computed(() => {
2109
2152
  if (this.mounted())
@@ -2113,7 +2156,8 @@ class WrEditor {
2113
2156
  return readValue(this.value(), format, editorSchema(format), this.document).toJSON();
2114
2157
  }
2115
2158
  catch (error) {
2116
- // The browser warns once, when it mounts; the server has no mount to wait for.
2159
+ // The browser warns from `sync()` — at the mount, or at the write itself
2160
+ // while read-only; the server has neither to wait for.
2117
2161
  if (!this.isBrowser && isDevMode())
2118
2162
  this.warnRefused(format, error);
2119
2163
  return null;
@@ -2170,9 +2214,22 @@ class WrEditor {
2170
2214
  // flag, not on `PLATFORM_ID`, so a platform test would still run it.
2171
2215
  if (!this.isBrowser)
2172
2216
  return;
2173
- afterNextRender(() => this.mount());
2217
+ // Mount on the first render that is not read-only — usually the very first
2218
+ // one, and otherwise the one that lifts `readonly`. A WRITE phase, so the
2219
+ // surface the template puts back is already in the DOM by the time
2220
+ // `mount()` looks for it; `mount()` itself happens once and never undoes.
2221
+ afterRenderEffect({
2222
+ write: () => {
2223
+ if (!this.readonly())
2224
+ this.mount();
2225
+ },
2226
+ });
2174
2227
  }
2175
- /** Focus the text surface — what Signal Forms calls to focus this control. */
2228
+ /**
2229
+ * Focus the editor — what Signal Forms calls to focus this control. Whatever
2230
+ * is on screen takes it: the mounted surface, or the static document, which
2231
+ * keeps a tab stop for exactly this reason.
2232
+ */
2176
2233
  focus(options) {
2177
2234
  const view = this.view;
2178
2235
  // ProseMirror's own `focus()` is a no-op while the view is not editable,
@@ -2180,7 +2237,7 @@ class WrEditor {
2180
2237
  if (view?.editable)
2181
2238
  view.focus();
2182
2239
  else
2183
- this.surface().nativeElement.focus(options);
2240
+ (this.surface() ?? this.staticDoc())?.nativeElement.focus(options);
2184
2241
  }
2185
2242
  // Template handlers
2186
2243
  label(tool) {
@@ -2387,6 +2444,15 @@ class WrEditor {
2387
2444
  return !this.readonly() && !this.disabled() && !this.refused();
2388
2445
  }
2389
2446
  mount() {
2447
+ // Once and for all: a second call would drop a live document, and going
2448
+ // back to read-only is not a reason to throw a session's history away.
2449
+ if (this.view)
2450
+ return;
2451
+ // The template draws the surface in every state but the static one, which
2452
+ // this is never called out of — after the render that puts it back.
2453
+ const surface = this.surface();
2454
+ if (!surface)
2455
+ return;
2390
2456
  this.apple.set((this.platform.userAgent ?? '').toLowerCase().includes('mac'));
2391
2457
  const format = untracked(this.resolvedFormat);
2392
2458
  const value = untracked(this.value);
@@ -2396,14 +2462,15 @@ class WrEditor {
2396
2462
  doc = readValue(value, format, schema, this.document);
2397
2463
  }
2398
2464
  catch (error) {
2399
- if (isDevMode())
2465
+ // Never twice for a refusal the static path has already named.
2466
+ if (isDevMode() && !this.refusalWarned)
2400
2467
  this.warnRefused(format, error);
2401
2468
  doc = schema.topNodeType.createAndFill();
2402
2469
  // An empty document over a stored value: see `refused`.
2403
2470
  this.refused.set(true);
2404
2471
  }
2405
2472
  const state = this.createState(doc);
2406
- this.view = new EditorView({ mount: this.surface().nativeElement }, {
2473
+ this.view = new EditorView({ mount: surface.nativeElement }, {
2407
2474
  state,
2408
2475
  editable: () => untracked(() => this.canEdit()),
2409
2476
  dispatchTransaction: tr => this.dispatch(tr),
@@ -2464,8 +2531,16 @@ class WrEditor {
2464
2531
  */
2465
2532
  sync(value, format) {
2466
2533
  const view = this.view;
2467
- if (!view)
2534
+ if (!view) {
2535
+ // Nothing to bring it into. `preview()` redraws it either way; what it
2536
+ // cannot do is REPORT a value it could not read, and in the static mode
2537
+ // no mount is coming to do that — so the check happens here instead.
2538
+ // Before a mount that IS coming it is `mount()` that reports, and on the
2539
+ // server `preview()` does, each exactly once.
2540
+ if (this.isBrowser && untracked(this.readonly))
2541
+ this.readStatically(value, format);
2468
2542
  return;
2543
+ }
2469
2544
  if (format === this.viewFormat && value === this.lastSerialized)
2470
2545
  return;
2471
2546
  const schema = editorSchema(format);
@@ -2492,6 +2567,30 @@ class WrEditor {
2492
2567
  this.viewFormat = format;
2493
2568
  this.replaceState(doc);
2494
2569
  }
2570
+ /**
2571
+ * The check `mount()` makes, made without a mount: the same warning and the
2572
+ * same `refused` flag, so an unreadable value is refused whether or not
2573
+ * ProseMirror ever starts. Without it a read-only editor met one with silence
2574
+ * — `preview()` renders nothing for a document it cannot read, and the model
2575
+ * goes on holding a value nothing has said a word about.
2576
+ *
2577
+ * The parsed document is dropped; `preview()` reads its own. The cost is one
2578
+ * extra parse per write, and only while read-only, where a write is rare.
2579
+ */
2580
+ readStatically(value, format) {
2581
+ try {
2582
+ readValue(value, format, editorSchema(format), this.document);
2583
+ this.refused.set(false);
2584
+ this.refusalWarned = false;
2585
+ }
2586
+ catch (error) {
2587
+ if (isDevMode()) {
2588
+ this.warnRefused(format, error);
2589
+ this.refusalWarned = true;
2590
+ }
2591
+ this.refused.set(true);
2592
+ }
2593
+ }
2495
2594
  /**
2496
2595
  * A new format whose value could not be read: keep the document, moved into
2497
2596
  * the new format's schema — markdown's lacks underline, so that mark is
@@ -2580,7 +2679,7 @@ class WrEditor {
2580
2679
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: WrEditor, isStandalone: true, selector: "wr-editor", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, format: { classPropertyName: "format", publicName: "format", isSignal: true, isRequired: false, transformFunction: null }, toolbar: { classPropertyName: "toolbar", publicName: "toolbar", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", touch: "touch" }, host: { listeners: { "focusout": "onFocusOut($event)" }, properties: { "class": "classes()" } }, providers: [
2581
2680
  { provide: WrEditorLinkField, useFactory: () => new WrEditorLinkField() },
2582
2681
  { provide: WR_FORM_FIELD, useExisting: WrEditorLinkField },
2583
- ], viewQueries: [{ propertyName: "surface", first: true, predicate: ["surface"], descendants: true, isSignal: true }, { propertyName: "toolButtons", predicate: ["tool"], descendants: true, read: ElementRef, isSignal: true }, { propertyName: "linkPopover", first: true, predicate: ["linkPopover"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (tools().length) {\n <div class=\"wr-editor__toolbar\" role=\"toolbar\" [attr.aria-label]=\"toolbarLabel()\" [attr.aria-controls]=\"surfaceId\">\n @for (item of tools(); track $index; let n = $index) {\n @if (item === '|') {\n <span class=\"wr-editor__separator\" aria-hidden=\"true\"></span>\n } @else if (item === 'link') {\n <!-- The link button hosts the link panel, so it carries the popover's own\n `aria-haspopup` / `aria-expanded` / `aria-controls` \u2014 and no tooltip,\n since one element cannot host `[wrPopover]` twice; its name and\n `aria-keyshortcuts` still say what the others' tooltips say. No\n `aria-pressed` either: a button that opens a popup is not a toggle,\n and the panel's remove button is where \"in a link\" is announced. -->\n <button\n #tool\n #linkPopover=\"wrPopover\"\n wr-btn\n type=\"button\"\n class=\"wr-editor__tool\"\n [class.wr-editor__tool--active]=\"state(item)?.active\"\n [disabled]=\"!state(item)?.available\"\n [attr.aria-label]=\"label(item)\"\n [attr.aria-keyshortcuts]=\"keyShortcuts(item)\"\n [wrPopover]=\"linkPanel\"\n [ariaLabel]=\"label(item)\"\n (opened)=\"onLinkOpened()\"\n (closed)=\"onLinkClosed()\"\n (focus)=\"onToolFocus(buttonIndex()[n])\"\n (keydown)=\"onToolbarKeydown($event)\"\n (mousedown)=\"$event.preventDefault()\"\n >\n <svg\n class=\"wr-editor__icon\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n @for (d of icon(item); track $index) {\n <path [attr.d]=\"d\" />\n }\n </svg>\n </button>\n } @else {\n <button\n #tool\n wr-btn\n type=\"button\"\n class=\"wr-editor__tool\"\n mode=\"tooltip\"\n [class.wr-editor__tool--active]=\"state(item)?.active\"\n [disabled]=\"!state(item)?.available\"\n [attr.aria-pressed]=\"isToggle(item) ? (state(item)?.active ? 'true' : 'false') : null\"\n [attr.aria-label]=\"label(item)\"\n [attr.aria-keyshortcuts]=\"keyShortcuts(item)\"\n [wrPopover]=\"tooltip(item)\"\n (click)=\"onToolClick(item, $event)\"\n (focus)=\"onToolFocus(buttonIndex()[n])\"\n (keydown)=\"onToolbarKeydown($event)\"\n (mousedown)=\"$event.preventDefault()\"\n >\n <svg\n class=\"wr-editor__icon\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n @for (d of icon(item); track $index) {\n <path [attr.d]=\"d\" />\n }\n </svg>\n </button>\n }\n }\n </div>\n}\n\n<!--\n The static preview: what Angular renders until ProseMirror mounts \u2014 on the\n server, and for the first browser frame. It is a SIBLING of the mount surface,\n never inside it: `{ mount }` replaces the element's children, and nodes Angular\n owns would be deleted out from under the next change detection.\n-->\n@if (preview(); as doc) {\n <div class=\"wr-editor__preview wr-editor__content\">\n <ng-container [ngTemplateOutlet]=\"previewBlocks\" [ngTemplateOutletContext]=\"{ $implicit: doc.content }\" />\n </div>\n}\n\n<!--\n The mount surface. Angular binds every attribute it needs, so the prerendered\n page and the synchronous first render already carry the role, the name and the\n field's error state; ProseMirror adds only its own class, `contenteditable` and\n `translate`, and never touches an attribute it did not set. No Angular child\n ever lives in here.\n-->\n<div\n #surface\n class=\"wr-editor__surface wr-editor__content\"\n role=\"textbox\"\n aria-multiline=\"true\"\n [id]=\"surfaceId\"\n [hidden]=\"!mounted()\"\n [attr.aria-label]=\"accessibleName()\"\n [attr.aria-labelledby]=\"labelledBy()\"\n [attr.aria-describedby]=\"fieldAria.describedBy()\"\n [attr.aria-invalid]=\"fieldAria.ariaInvalid()\"\n [attr.aria-readonly]=\"locked() && !disabled() ? 'true' : null\"\n [attr.aria-disabled]=\"disabled() ? 'true' : null\"\n [attr.aria-placeholder]=\"ariaPlaceholder()\"\n [attr.tabindex]=\"locked() && !disabled() ? 0 : null\"\n (focus)=\"focused.set(true)\"\n (blur)=\"focused.set(false)\"\n></div>\n\n<!--\n The popover styles its panel as `.wr-popover-overlay > *`, which is this form,\n so the form stays a plain block and the layout lives one level in.\n-->\n<ng-template #linkPanel>\n <form\n class=\"wr-editor-link\"\n novalidate\n [id]=\"linkField.panelId\"\n (submit)=\"applyLink($event)\"\n (focusout)=\"onFocusOut($event)\"\n >\n <div class=\"wr-editor-link__body\">\n <label class=\"wr-editor-link__label\" [attr.for]=\"linkField.controlId()\">{{ linkUrlLabel() }}</label>\n <input\n wrInput\n class=\"wr-editor-link__input\"\n type=\"text\"\n inputmode=\"url\"\n autocomplete=\"url\"\n spellcheck=\"false\"\n [value]=\"linkDraft()\"\n (input)=\"onLinkInput($event)\"\n />\n @if (linkField.invalid()) {\n <p class=\"wr-editor-link__error\" role=\"alert\" [id]=\"linkField.errorId\">{{ linkInvalidLabel() }}</p>\n }\n <div class=\"wr-editor-link__actions\">\n @if (linkActive()) {\n <button wr-btn type=\"button\" class=\"wr-editor-link__remove\" (click)=\"removeLink()\">\n {{ linkRemoveLabel() }}\n </button>\n }\n <button wr-btn type=\"submit\" color=\"primary\" class=\"wr-editor-link__apply\">{{ linkApplyLabel() }}</button>\n </div>\n </div>\n </form>\n</ng-template>\n\n<!--\n Recursion runs through `ngTemplateOutlet`, as in `<wr-markdown>`, so the preview\n is exactly the document with no wrapper between a `<ul>` and its `<li>`.\n-->\n<ng-template #previewBlocks let-raw>\n @for (node of nodesOf(raw); track $index) {\n @switch (node.type) {\n @case ('paragraph') {\n <p [class.wr-editor__placeholder]=\"ariaPlaceholder() !== null\" [attr.data-placeholder]=\"ariaPlaceholder()\">\n <ng-container [ngTemplateOutlet]=\"previewInline\" [ngTemplateOutletContext]=\"{ $implicit: node.content }\" />\n </p>\n }\n @case ('heading') {\n @switch (attr(node, 'level')) {\n @case (2) {\n <h2>\n <ng-container\n [ngTemplateOutlet]=\"previewInline\"\n [ngTemplateOutletContext]=\"{ $implicit: node.content }\"\n />\n </h2>\n }\n @case (3) {\n <h3>\n <ng-container\n [ngTemplateOutlet]=\"previewInline\"\n [ngTemplateOutletContext]=\"{ $implicit: node.content }\"\n />\n </h3>\n }\n @default {\n <h1>\n <ng-container\n [ngTemplateOutlet]=\"previewInline\"\n [ngTemplateOutletContext]=\"{ $implicit: node.content }\"\n />\n </h1>\n }\n }\n }\n @case ('blockquote') {\n <blockquote>\n <ng-container [ngTemplateOutlet]=\"previewBlocks\" [ngTemplateOutletContext]=\"{ $implicit: node.content }\" />\n </blockquote>\n }\n @case ('code_block') {\n <pre><code>{{ textOf(node) }}</code></pre>\n }\n @case ('horizontal_rule') {\n <hr />\n }\n @case ('bullet_list') {\n <ul>\n <ng-container [ngTemplateOutlet]=\"previewBlocks\" [ngTemplateOutletContext]=\"{ $implicit: node.content }\" />\n </ul>\n }\n @case ('ordered_list') {\n <ol [attr.start]=\"attr(node, 'order') === 1 ? null : attr(node, 'order')\">\n <ng-container [ngTemplateOutlet]=\"previewBlocks\" [ngTemplateOutletContext]=\"{ $implicit: node.content }\" />\n </ol>\n }\n @case ('list_item') {\n @let checked = attr(node, 'checked');\n @if (checked === null) {\n <li>\n <ng-container [ngTemplateOutlet]=\"previewBlocks\" [ngTemplateOutletContext]=\"{ $implicit: node.content }\" />\n </li>\n } @else {\n <li class=\"wr-editor__item wr-editor__item--task\" [attr.data-checked]=\"checked\">\n <span class=\"wr-editor__task\" [class.wr-editor__task--checked]=\"checked === true\" aria-hidden=\"true\"></span>\n <span class=\"wr-editor__sr-only\">{{ checked === true ? taskDoneLabel() : taskTodoLabel() }}</span>\n <div class=\"wr-editor__task-body\">\n <ng-container\n [ngTemplateOutlet]=\"previewBlocks\"\n [ngTemplateOutletContext]=\"{ $implicit: node.content }\"\n />\n </div>\n </li>\n }\n }\n @case ('table') {\n <table>\n <tbody>\n <ng-container [ngTemplateOutlet]=\"previewBlocks\" [ngTemplateOutletContext]=\"{ $implicit: node.content }\" />\n </tbody>\n </table>\n }\n @case ('table_row') {\n <tr>\n <ng-container [ngTemplateOutlet]=\"previewBlocks\" [ngTemplateOutletContext]=\"{ $implicit: node.content }\" />\n </tr>\n }\n @case ('table_header') {\n <th [style.text-align]=\"text(node, 'align')\">\n <ng-container [ngTemplateOutlet]=\"previewBlocks\" [ngTemplateOutletContext]=\"{ $implicit: node.content }\" />\n </th>\n }\n @case ('table_cell') {\n <td [style.text-align]=\"text(node, 'align')\">\n <ng-container [ngTemplateOutlet]=\"previewBlocks\" [ngTemplateOutletContext]=\"{ $implicit: node.content }\" />\n </td>\n }\n }\n }\n</ng-template>\n\n<ng-template #previewInline let-raw>\n @for (node of nodesOf(raw); track $index) {\n <ng-container [ngTemplateOutlet]=\"previewMarked\" [ngTemplateOutletContext]=\"{ $implicit: node, depth: 0 }\" />\n }\n</ng-template>\n\n<!-- One mark per level, outermost first \u2014 the order the schema ranks them in. -->\n<ng-template #previewMarked let-raw let-depth=\"depth\">\n @let node = nodeOf(raw);\n @let mark = markAt(node, depth);\n @if (mark) {\n @switch (mark.type) {\n @case ('link') {\n <a [href]=\"text(mark, 'href')\" [attr.title]=\"text(mark, 'title')\" rel=\"noopener noreferrer nofollow\"\n ><ng-container\n [ngTemplateOutlet]=\"previewMarked\"\n [ngTemplateOutletContext]=\"{ $implicit: node, depth: depth + 1 }\"\n /></a>\n }\n @case ('strong') {\n <strong\n ><ng-container\n [ngTemplateOutlet]=\"previewMarked\"\n [ngTemplateOutletContext]=\"{ $implicit: node, depth: depth + 1 }\"\n /></strong>\n }\n @case ('em') {\n <em\n ><ng-container\n [ngTemplateOutlet]=\"previewMarked\"\n [ngTemplateOutletContext]=\"{ $implicit: node, depth: depth + 1 }\"\n /></em>\n }\n @case ('underline') {\n <u\n ><ng-container\n [ngTemplateOutlet]=\"previewMarked\"\n [ngTemplateOutletContext]=\"{ $implicit: node, depth: depth + 1 }\"\n /></u>\n }\n @case ('strike') {\n <s\n ><ng-container\n [ngTemplateOutlet]=\"previewMarked\"\n [ngTemplateOutletContext]=\"{ $implicit: node, depth: depth + 1 }\"\n /></s>\n }\n @case ('code') {\n <code\n ><ng-container\n [ngTemplateOutlet]=\"previewMarked\"\n [ngTemplateOutletContext]=\"{ $implicit: node, depth: depth + 1 }\"\n /></code>\n }\n @default {\n <ng-container\n [ngTemplateOutlet]=\"previewMarked\"\n [ngTemplateOutletContext]=\"{ $implicit: node, depth: depth + 1 }\"\n />\n }\n }\n } @else {\n @switch (node.type) {\n @case ('text') {\n <ng-container>{{ node.text }}</ng-container>\n }\n @case ('hard_break') {\n <br />\n }\n @case ('image') {\n <img [src]=\"text(node, 'src')\" [alt]=\"text(node, 'alt') ?? ''\" [attr.title]=\"text(node, 'title')\" />\n }\n }\n }\n</ng-template>\n", dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: WrButton, selector: "wr-btn, button[wr-btn], a[wr-btn]", inputs: ["color", "size", "shape", "icon", "iconPosition", "disabled", "outlined", "block", "loading", "disabledWhenLoading"] }, { kind: "directive", type: WrInput, selector: "input[wrInput], textarea[wrInput]", inputs: ["size", "rounded"] }, { kind: "directive", type: WrPopover, selector: "[wrPopover]", inputs: ["wrPopover", "mode", "trigger", "position", "showDelay", "hideDelay", "responsive", "ariaLabel"], outputs: ["opened", "closed"], exportAs: ["wrPopover"] }], encapsulation: i0.ViewEncapsulation.None });
2682
+ ], viewQueries: [{ propertyName: "surface", first: true, predicate: ["surface"], descendants: true, isSignal: true }, { propertyName: "staticDoc", first: true, predicate: ["staticDoc"], descendants: true, isSignal: true }, { propertyName: "toolButtons", predicate: ["tool"], descendants: true, read: ElementRef, isSignal: true }, { propertyName: "linkPopover", first: true, predicate: ["linkPopover"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (tools().length) {\n <div class=\"wr-editor__toolbar\" role=\"toolbar\" [attr.aria-label]=\"toolbarLabel()\" [attr.aria-controls]=\"surfaceId\">\n @for (item of tools(); track $index; let n = $index) {\n @if (item === '|') {\n <span class=\"wr-editor__separator\" aria-hidden=\"true\"></span>\n } @else if (item === 'link') {\n <!-- The link button hosts the link panel, so it carries the popover's own\n `aria-haspopup` / `aria-expanded` / `aria-controls` \u2014 and no tooltip,\n since one element cannot host `[wrPopover]` twice; its name and\n `aria-keyshortcuts` still say what the others' tooltips say. No\n `aria-pressed` either: a button that opens a popup is not a toggle,\n and the panel's remove button is where \"in a link\" is announced. -->\n <button\n #tool\n #linkPopover=\"wrPopover\"\n wr-btn\n type=\"button\"\n class=\"wr-editor__tool\"\n [class.wr-editor__tool--active]=\"state(item)?.active\"\n [disabled]=\"!state(item)?.available\"\n [attr.aria-label]=\"label(item)\"\n [attr.aria-keyshortcuts]=\"keyShortcuts(item)\"\n [wrPopover]=\"linkPanel\"\n [ariaLabel]=\"label(item)\"\n (opened)=\"onLinkOpened()\"\n (closed)=\"onLinkClosed()\"\n (focus)=\"onToolFocus(buttonIndex()[n])\"\n (keydown)=\"onToolbarKeydown($event)\"\n (mousedown)=\"$event.preventDefault()\"\n >\n <svg\n class=\"wr-editor__icon\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n @for (d of icon(item); track $index) {\n <path [attr.d]=\"d\" />\n }\n </svg>\n </button>\n } @else {\n <button\n #tool\n wr-btn\n type=\"button\"\n class=\"wr-editor__tool\"\n mode=\"tooltip\"\n [class.wr-editor__tool--active]=\"state(item)?.active\"\n [disabled]=\"!state(item)?.available\"\n [attr.aria-pressed]=\"isToggle(item) ? (state(item)?.active ? 'true' : 'false') : null\"\n [attr.aria-label]=\"label(item)\"\n [attr.aria-keyshortcuts]=\"keyShortcuts(item)\"\n [wrPopover]=\"tooltip(item)\"\n (click)=\"onToolClick(item, $event)\"\n (focus)=\"onToolFocus(buttonIndex()[n])\"\n (keydown)=\"onToolbarKeydown($event)\"\n (mousedown)=\"$event.preventDefault()\"\n >\n <svg\n class=\"wr-editor__icon\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n @for (d of icon(item); track $index) {\n <path [attr.d]=\"d\" />\n }\n </svg>\n </button>\n }\n }\n </div>\n}\n\n<!--\n The document drawn by Angular, in either of the two ways it stands on screen.\n\n In the STATIC mode \u2014 read-only with nothing mounted \u2014 it is the editor, for as\n long as `readonly` holds, so it takes the id, the name, the field's state and\n the tab stop the surface below would have carried, and that surface is not\n rendered at all: two elements with one id, or a nameless empty textbox beside\n the text, is what rendering both would mean. A focusable `role=\"group\"` and not\n a textbox, because every descendant of a textbox is presentational \u2014 the\n headings, lists and tables being shown would stop being announced, which is the\n whole point of showing a document. No `aria-readonly`: ARIA does not allow it\n on `group`, and `.wr-editor--readonly` on the host is the cue instead.\n\n Otherwise it is the preview that stands in until ProseMirror mounts \u2014 on the\n server, and for the first browser frame \u2014 and carries nothing of the surface's.\n\n Either way it is a SIBLING of the mount surface, never inside it: `{ mount }`\n replaces the element's children, and nodes Angular owns would be deleted out\n from under the next change detection.\n-->\n@if (staticMode()) {\n <div\n #staticDoc\n class=\"wr-editor__preview wr-editor__preview--static wr-editor__content\"\n role=\"group\"\n [id]=\"surfaceId\"\n [attr.aria-label]=\"accessibleName()\"\n [attr.aria-labelledby]=\"labelledBy()\"\n [attr.aria-describedby]=\"fieldAria.describedBy()\"\n [attr.aria-invalid]=\"fieldAria.ariaInvalid()\"\n [attr.aria-disabled]=\"disabled() ? 'true' : null\"\n [attr.tabindex]=\"disabled() ? null : 0\"\n (focus)=\"focused.set(true)\"\n (blur)=\"focused.set(false)\"\n >\n <ng-container [ngTemplateOutlet]=\"previewContent\" />\n </div>\n} @else {\n @if (preview()) {\n <div class=\"wr-editor__preview wr-editor__content\">\n <ng-container [ngTemplateOutlet]=\"previewContent\" />\n </div>\n }\n\n <!--\n The mount surface. Angular binds every attribute it needs, so the prerendered\n page and the synchronous first render already carry the role, the name and the\n field's error state; ProseMirror adds only its own class, `contenteditable` and\n `translate`, and never touches an attribute it did not set. No Angular child\n ever lives in here.\n -->\n <div\n #surface\n class=\"wr-editor__surface wr-editor__content\"\n role=\"textbox\"\n aria-multiline=\"true\"\n [id]=\"surfaceId\"\n [hidden]=\"!mounted()\"\n [attr.aria-label]=\"accessibleName()\"\n [attr.aria-labelledby]=\"labelledBy()\"\n [attr.aria-describedby]=\"fieldAria.describedBy()\"\n [attr.aria-invalid]=\"fieldAria.ariaInvalid()\"\n [attr.aria-readonly]=\"locked() && !disabled() ? 'true' : null\"\n [attr.aria-disabled]=\"disabled() ? 'true' : null\"\n [attr.aria-placeholder]=\"ariaPlaceholder()\"\n [attr.tabindex]=\"locked() && !disabled() ? 0 : null\"\n (focus)=\"focused.set(true)\"\n (blur)=\"focused.set(false)\"\n ></div>\n}\n\n<!-- The document itself, written once for both of the containers above. -->\n<ng-template #previewContent>\n <ng-container [ngTemplateOutlet]=\"previewBlocks\" [ngTemplateOutletContext]=\"{ $implicit: preview()?.content }\" />\n</ng-template>\n\n<!--\n The popover styles its panel as `.wr-popover-overlay > *`, which is this form,\n so the form stays a plain block and the layout lives one level in.\n-->\n<ng-template #linkPanel>\n <form\n class=\"wr-editor-link\"\n novalidate\n [id]=\"linkField.panelId\"\n (submit)=\"applyLink($event)\"\n (focusout)=\"onFocusOut($event)\"\n >\n <div class=\"wr-editor-link__body\">\n <label class=\"wr-editor-link__label\" [attr.for]=\"linkField.controlId()\">{{ linkUrlLabel() }}</label>\n <input\n wrInput\n class=\"wr-editor-link__input\"\n type=\"text\"\n inputmode=\"url\"\n autocomplete=\"url\"\n spellcheck=\"false\"\n [value]=\"linkDraft()\"\n (input)=\"onLinkInput($event)\"\n />\n @if (linkField.invalid()) {\n <p class=\"wr-editor-link__error\" role=\"alert\" [id]=\"linkField.errorId\">{{ linkInvalidLabel() }}</p>\n }\n <div class=\"wr-editor-link__actions\">\n @if (linkActive()) {\n <button wr-btn type=\"button\" class=\"wr-editor-link__remove\" (click)=\"removeLink()\">\n {{ linkRemoveLabel() }}\n </button>\n }\n <button wr-btn type=\"submit\" color=\"primary\" class=\"wr-editor-link__apply\">{{ linkApplyLabel() }}</button>\n </div>\n </div>\n </form>\n</ng-template>\n\n<!--\n Recursion runs through `ngTemplateOutlet`, as in `<wr-markdown>`, so the preview\n is exactly the document with no wrapper between a `<ul>` and its `<li>`.\n-->\n<ng-template #previewBlocks let-raw>\n @for (node of nodesOf(raw); track $index) {\n @switch (node.type) {\n @case ('paragraph') {\n <p [class.wr-editor__placeholder]=\"ariaPlaceholder() !== null\" [attr.data-placeholder]=\"ariaPlaceholder()\">\n <ng-container [ngTemplateOutlet]=\"previewInline\" [ngTemplateOutletContext]=\"{ $implicit: node.content }\" />\n </p>\n }\n @case ('heading') {\n @switch (attr(node, 'level')) {\n @case (2) {\n <h2>\n <ng-container\n [ngTemplateOutlet]=\"previewInline\"\n [ngTemplateOutletContext]=\"{ $implicit: node.content }\"\n />\n </h2>\n }\n @case (3) {\n <h3>\n <ng-container\n [ngTemplateOutlet]=\"previewInline\"\n [ngTemplateOutletContext]=\"{ $implicit: node.content }\"\n />\n </h3>\n }\n @default {\n <h1>\n <ng-container\n [ngTemplateOutlet]=\"previewInline\"\n [ngTemplateOutletContext]=\"{ $implicit: node.content }\"\n />\n </h1>\n }\n }\n }\n @case ('blockquote') {\n <blockquote>\n <ng-container [ngTemplateOutlet]=\"previewBlocks\" [ngTemplateOutletContext]=\"{ $implicit: node.content }\" />\n </blockquote>\n }\n @case ('code_block') {\n <pre><code>{{ textOf(node) }}</code></pre>\n }\n @case ('horizontal_rule') {\n <hr />\n }\n @case ('bullet_list') {\n <ul>\n <ng-container [ngTemplateOutlet]=\"previewBlocks\" [ngTemplateOutletContext]=\"{ $implicit: node.content }\" />\n </ul>\n }\n @case ('ordered_list') {\n <ol [attr.start]=\"attr(node, 'order') === 1 ? null : attr(node, 'order')\">\n <ng-container [ngTemplateOutlet]=\"previewBlocks\" [ngTemplateOutletContext]=\"{ $implicit: node.content }\" />\n </ol>\n }\n @case ('list_item') {\n @let checked = attr(node, 'checked');\n @if (checked === null) {\n <li>\n <ng-container [ngTemplateOutlet]=\"previewBlocks\" [ngTemplateOutletContext]=\"{ $implicit: node.content }\" />\n </li>\n } @else {\n <li class=\"wr-editor__item wr-editor__item--task\" [attr.data-checked]=\"checked\">\n <span class=\"wr-editor__task\" [class.wr-editor__task--checked]=\"checked === true\" aria-hidden=\"true\"></span>\n <span class=\"wr-editor__sr-only\">{{ checked === true ? taskDoneLabel() : taskTodoLabel() }}</span>\n <div class=\"wr-editor__task-body\">\n <ng-container\n [ngTemplateOutlet]=\"previewBlocks\"\n [ngTemplateOutletContext]=\"{ $implicit: node.content }\"\n />\n </div>\n </li>\n }\n }\n @case ('table') {\n <table>\n <tbody>\n <ng-container [ngTemplateOutlet]=\"previewBlocks\" [ngTemplateOutletContext]=\"{ $implicit: node.content }\" />\n </tbody>\n </table>\n }\n @case ('table_row') {\n <tr>\n <ng-container [ngTemplateOutlet]=\"previewBlocks\" [ngTemplateOutletContext]=\"{ $implicit: node.content }\" />\n </tr>\n }\n @case ('table_header') {\n <th [style.text-align]=\"text(node, 'align')\">\n <ng-container [ngTemplateOutlet]=\"previewBlocks\" [ngTemplateOutletContext]=\"{ $implicit: node.content }\" />\n </th>\n }\n @case ('table_cell') {\n <td [style.text-align]=\"text(node, 'align')\">\n <ng-container [ngTemplateOutlet]=\"previewBlocks\" [ngTemplateOutletContext]=\"{ $implicit: node.content }\" />\n </td>\n }\n }\n }\n</ng-template>\n\n<ng-template #previewInline let-raw>\n @for (node of nodesOf(raw); track $index) {\n <ng-container [ngTemplateOutlet]=\"previewMarked\" [ngTemplateOutletContext]=\"{ $implicit: node, depth: 0 }\" />\n }\n</ng-template>\n\n<!-- One mark per level, outermost first \u2014 the order the schema ranks them in. -->\n<ng-template #previewMarked let-raw let-depth=\"depth\">\n @let node = nodeOf(raw);\n @let mark = markAt(node, depth);\n @if (mark) {\n @switch (mark.type) {\n @case ('link') {\n <a [href]=\"text(mark, 'href')\" [attr.title]=\"text(mark, 'title')\" rel=\"noopener noreferrer nofollow\"\n ><ng-container\n [ngTemplateOutlet]=\"previewMarked\"\n [ngTemplateOutletContext]=\"{ $implicit: node, depth: depth + 1 }\"\n /></a>\n }\n @case ('strong') {\n <strong\n ><ng-container\n [ngTemplateOutlet]=\"previewMarked\"\n [ngTemplateOutletContext]=\"{ $implicit: node, depth: depth + 1 }\"\n /></strong>\n }\n @case ('em') {\n <em\n ><ng-container\n [ngTemplateOutlet]=\"previewMarked\"\n [ngTemplateOutletContext]=\"{ $implicit: node, depth: depth + 1 }\"\n /></em>\n }\n @case ('underline') {\n <u\n ><ng-container\n [ngTemplateOutlet]=\"previewMarked\"\n [ngTemplateOutletContext]=\"{ $implicit: node, depth: depth + 1 }\"\n /></u>\n }\n @case ('strike') {\n <s\n ><ng-container\n [ngTemplateOutlet]=\"previewMarked\"\n [ngTemplateOutletContext]=\"{ $implicit: node, depth: depth + 1 }\"\n /></s>\n }\n @case ('code') {\n <code\n ><ng-container\n [ngTemplateOutlet]=\"previewMarked\"\n [ngTemplateOutletContext]=\"{ $implicit: node, depth: depth + 1 }\"\n /></code>\n }\n @default {\n <ng-container\n [ngTemplateOutlet]=\"previewMarked\"\n [ngTemplateOutletContext]=\"{ $implicit: node, depth: depth + 1 }\"\n />\n }\n }\n } @else {\n @switch (node.type) {\n @case ('text') {\n <ng-container>{{ node.text }}</ng-container>\n }\n @case ('hard_break') {\n <br />\n }\n @case ('image') {\n <img [src]=\"text(node, 'src')\" [alt]=\"text(node, 'alt') ?? ''\" [attr.title]=\"text(node, 'title')\" />\n }\n }\n }\n</ng-template>\n", dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: WrButton, selector: "wr-btn, button[wr-btn], a[wr-btn]", inputs: ["color", "size", "shape", "icon", "iconPosition", "disabled", "outlined", "block", "loading", "disabledWhenLoading"] }, { kind: "directive", type: WrInput, selector: "input[wrInput], textarea[wrInput]", inputs: ["size", "rounded"] }, { kind: "directive", type: WrPopover, selector: "[wrPopover]", inputs: ["wrPopover", "mode", "trigger", "position", "showDelay", "hideDelay", "responsive", "ariaLabel"], outputs: ["opened", "closed"], exportAs: ["wrPopover"] }], encapsulation: i0.ViewEncapsulation.None });
2584
2683
  }
2585
2684
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrEditor, decorators: [{
2586
2685
  type: Component,
@@ -2590,8 +2689,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImpor
2590
2689
  }, providers: [
2591
2690
  { provide: WrEditorLinkField, useFactory: () => new WrEditorLinkField() },
2592
2691
  { provide: WR_FORM_FIELD, useExisting: WrEditorLinkField },
2593
- ], imports: [NgTemplateOutlet, WrButton, WrInput, WrPopover], template: "@if (tools().length) {\n <div class=\"wr-editor__toolbar\" role=\"toolbar\" [attr.aria-label]=\"toolbarLabel()\" [attr.aria-controls]=\"surfaceId\">\n @for (item of tools(); track $index; let n = $index) {\n @if (item === '|') {\n <span class=\"wr-editor__separator\" aria-hidden=\"true\"></span>\n } @else if (item === 'link') {\n <!-- The link button hosts the link panel, so it carries the popover's own\n `aria-haspopup` / `aria-expanded` / `aria-controls` \u2014 and no tooltip,\n since one element cannot host `[wrPopover]` twice; its name and\n `aria-keyshortcuts` still say what the others' tooltips say. No\n `aria-pressed` either: a button that opens a popup is not a toggle,\n and the panel's remove button is where \"in a link\" is announced. -->\n <button\n #tool\n #linkPopover=\"wrPopover\"\n wr-btn\n type=\"button\"\n class=\"wr-editor__tool\"\n [class.wr-editor__tool--active]=\"state(item)?.active\"\n [disabled]=\"!state(item)?.available\"\n [attr.aria-label]=\"label(item)\"\n [attr.aria-keyshortcuts]=\"keyShortcuts(item)\"\n [wrPopover]=\"linkPanel\"\n [ariaLabel]=\"label(item)\"\n (opened)=\"onLinkOpened()\"\n (closed)=\"onLinkClosed()\"\n (focus)=\"onToolFocus(buttonIndex()[n])\"\n (keydown)=\"onToolbarKeydown($event)\"\n (mousedown)=\"$event.preventDefault()\"\n >\n <svg\n class=\"wr-editor__icon\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n @for (d of icon(item); track $index) {\n <path [attr.d]=\"d\" />\n }\n </svg>\n </button>\n } @else {\n <button\n #tool\n wr-btn\n type=\"button\"\n class=\"wr-editor__tool\"\n mode=\"tooltip\"\n [class.wr-editor__tool--active]=\"state(item)?.active\"\n [disabled]=\"!state(item)?.available\"\n [attr.aria-pressed]=\"isToggle(item) ? (state(item)?.active ? 'true' : 'false') : null\"\n [attr.aria-label]=\"label(item)\"\n [attr.aria-keyshortcuts]=\"keyShortcuts(item)\"\n [wrPopover]=\"tooltip(item)\"\n (click)=\"onToolClick(item, $event)\"\n (focus)=\"onToolFocus(buttonIndex()[n])\"\n (keydown)=\"onToolbarKeydown($event)\"\n (mousedown)=\"$event.preventDefault()\"\n >\n <svg\n class=\"wr-editor__icon\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n @for (d of icon(item); track $index) {\n <path [attr.d]=\"d\" />\n }\n </svg>\n </button>\n }\n }\n </div>\n}\n\n<!--\n The static preview: what Angular renders until ProseMirror mounts \u2014 on the\n server, and for the first browser frame. It is a SIBLING of the mount surface,\n never inside it: `{ mount }` replaces the element's children, and nodes Angular\n owns would be deleted out from under the next change detection.\n-->\n@if (preview(); as doc) {\n <div class=\"wr-editor__preview wr-editor__content\">\n <ng-container [ngTemplateOutlet]=\"previewBlocks\" [ngTemplateOutletContext]=\"{ $implicit: doc.content }\" />\n </div>\n}\n\n<!--\n The mount surface. Angular binds every attribute it needs, so the prerendered\n page and the synchronous first render already carry the role, the name and the\n field's error state; ProseMirror adds only its own class, `contenteditable` and\n `translate`, and never touches an attribute it did not set. No Angular child\n ever lives in here.\n-->\n<div\n #surface\n class=\"wr-editor__surface wr-editor__content\"\n role=\"textbox\"\n aria-multiline=\"true\"\n [id]=\"surfaceId\"\n [hidden]=\"!mounted()\"\n [attr.aria-label]=\"accessibleName()\"\n [attr.aria-labelledby]=\"labelledBy()\"\n [attr.aria-describedby]=\"fieldAria.describedBy()\"\n [attr.aria-invalid]=\"fieldAria.ariaInvalid()\"\n [attr.aria-readonly]=\"locked() && !disabled() ? 'true' : null\"\n [attr.aria-disabled]=\"disabled() ? 'true' : null\"\n [attr.aria-placeholder]=\"ariaPlaceholder()\"\n [attr.tabindex]=\"locked() && !disabled() ? 0 : null\"\n (focus)=\"focused.set(true)\"\n (blur)=\"focused.set(false)\"\n></div>\n\n<!--\n The popover styles its panel as `.wr-popover-overlay > *`, which is this form,\n so the form stays a plain block and the layout lives one level in.\n-->\n<ng-template #linkPanel>\n <form\n class=\"wr-editor-link\"\n novalidate\n [id]=\"linkField.panelId\"\n (submit)=\"applyLink($event)\"\n (focusout)=\"onFocusOut($event)\"\n >\n <div class=\"wr-editor-link__body\">\n <label class=\"wr-editor-link__label\" [attr.for]=\"linkField.controlId()\">{{ linkUrlLabel() }}</label>\n <input\n wrInput\n class=\"wr-editor-link__input\"\n type=\"text\"\n inputmode=\"url\"\n autocomplete=\"url\"\n spellcheck=\"false\"\n [value]=\"linkDraft()\"\n (input)=\"onLinkInput($event)\"\n />\n @if (linkField.invalid()) {\n <p class=\"wr-editor-link__error\" role=\"alert\" [id]=\"linkField.errorId\">{{ linkInvalidLabel() }}</p>\n }\n <div class=\"wr-editor-link__actions\">\n @if (linkActive()) {\n <button wr-btn type=\"button\" class=\"wr-editor-link__remove\" (click)=\"removeLink()\">\n {{ linkRemoveLabel() }}\n </button>\n }\n <button wr-btn type=\"submit\" color=\"primary\" class=\"wr-editor-link__apply\">{{ linkApplyLabel() }}</button>\n </div>\n </div>\n </form>\n</ng-template>\n\n<!--\n Recursion runs through `ngTemplateOutlet`, as in `<wr-markdown>`, so the preview\n is exactly the document with no wrapper between a `<ul>` and its `<li>`.\n-->\n<ng-template #previewBlocks let-raw>\n @for (node of nodesOf(raw); track $index) {\n @switch (node.type) {\n @case ('paragraph') {\n <p [class.wr-editor__placeholder]=\"ariaPlaceholder() !== null\" [attr.data-placeholder]=\"ariaPlaceholder()\">\n <ng-container [ngTemplateOutlet]=\"previewInline\" [ngTemplateOutletContext]=\"{ $implicit: node.content }\" />\n </p>\n }\n @case ('heading') {\n @switch (attr(node, 'level')) {\n @case (2) {\n <h2>\n <ng-container\n [ngTemplateOutlet]=\"previewInline\"\n [ngTemplateOutletContext]=\"{ $implicit: node.content }\"\n />\n </h2>\n }\n @case (3) {\n <h3>\n <ng-container\n [ngTemplateOutlet]=\"previewInline\"\n [ngTemplateOutletContext]=\"{ $implicit: node.content }\"\n />\n </h3>\n }\n @default {\n <h1>\n <ng-container\n [ngTemplateOutlet]=\"previewInline\"\n [ngTemplateOutletContext]=\"{ $implicit: node.content }\"\n />\n </h1>\n }\n }\n }\n @case ('blockquote') {\n <blockquote>\n <ng-container [ngTemplateOutlet]=\"previewBlocks\" [ngTemplateOutletContext]=\"{ $implicit: node.content }\" />\n </blockquote>\n }\n @case ('code_block') {\n <pre><code>{{ textOf(node) }}</code></pre>\n }\n @case ('horizontal_rule') {\n <hr />\n }\n @case ('bullet_list') {\n <ul>\n <ng-container [ngTemplateOutlet]=\"previewBlocks\" [ngTemplateOutletContext]=\"{ $implicit: node.content }\" />\n </ul>\n }\n @case ('ordered_list') {\n <ol [attr.start]=\"attr(node, 'order') === 1 ? null : attr(node, 'order')\">\n <ng-container [ngTemplateOutlet]=\"previewBlocks\" [ngTemplateOutletContext]=\"{ $implicit: node.content }\" />\n </ol>\n }\n @case ('list_item') {\n @let checked = attr(node, 'checked');\n @if (checked === null) {\n <li>\n <ng-container [ngTemplateOutlet]=\"previewBlocks\" [ngTemplateOutletContext]=\"{ $implicit: node.content }\" />\n </li>\n } @else {\n <li class=\"wr-editor__item wr-editor__item--task\" [attr.data-checked]=\"checked\">\n <span class=\"wr-editor__task\" [class.wr-editor__task--checked]=\"checked === true\" aria-hidden=\"true\"></span>\n <span class=\"wr-editor__sr-only\">{{ checked === true ? taskDoneLabel() : taskTodoLabel() }}</span>\n <div class=\"wr-editor__task-body\">\n <ng-container\n [ngTemplateOutlet]=\"previewBlocks\"\n [ngTemplateOutletContext]=\"{ $implicit: node.content }\"\n />\n </div>\n </li>\n }\n }\n @case ('table') {\n <table>\n <tbody>\n <ng-container [ngTemplateOutlet]=\"previewBlocks\" [ngTemplateOutletContext]=\"{ $implicit: node.content }\" />\n </tbody>\n </table>\n }\n @case ('table_row') {\n <tr>\n <ng-container [ngTemplateOutlet]=\"previewBlocks\" [ngTemplateOutletContext]=\"{ $implicit: node.content }\" />\n </tr>\n }\n @case ('table_header') {\n <th [style.text-align]=\"text(node, 'align')\">\n <ng-container [ngTemplateOutlet]=\"previewBlocks\" [ngTemplateOutletContext]=\"{ $implicit: node.content }\" />\n </th>\n }\n @case ('table_cell') {\n <td [style.text-align]=\"text(node, 'align')\">\n <ng-container [ngTemplateOutlet]=\"previewBlocks\" [ngTemplateOutletContext]=\"{ $implicit: node.content }\" />\n </td>\n }\n }\n }\n</ng-template>\n\n<ng-template #previewInline let-raw>\n @for (node of nodesOf(raw); track $index) {\n <ng-container [ngTemplateOutlet]=\"previewMarked\" [ngTemplateOutletContext]=\"{ $implicit: node, depth: 0 }\" />\n }\n</ng-template>\n\n<!-- One mark per level, outermost first \u2014 the order the schema ranks them in. -->\n<ng-template #previewMarked let-raw let-depth=\"depth\">\n @let node = nodeOf(raw);\n @let mark = markAt(node, depth);\n @if (mark) {\n @switch (mark.type) {\n @case ('link') {\n <a [href]=\"text(mark, 'href')\" [attr.title]=\"text(mark, 'title')\" rel=\"noopener noreferrer nofollow\"\n ><ng-container\n [ngTemplateOutlet]=\"previewMarked\"\n [ngTemplateOutletContext]=\"{ $implicit: node, depth: depth + 1 }\"\n /></a>\n }\n @case ('strong') {\n <strong\n ><ng-container\n [ngTemplateOutlet]=\"previewMarked\"\n [ngTemplateOutletContext]=\"{ $implicit: node, depth: depth + 1 }\"\n /></strong>\n }\n @case ('em') {\n <em\n ><ng-container\n [ngTemplateOutlet]=\"previewMarked\"\n [ngTemplateOutletContext]=\"{ $implicit: node, depth: depth + 1 }\"\n /></em>\n }\n @case ('underline') {\n <u\n ><ng-container\n [ngTemplateOutlet]=\"previewMarked\"\n [ngTemplateOutletContext]=\"{ $implicit: node, depth: depth + 1 }\"\n /></u>\n }\n @case ('strike') {\n <s\n ><ng-container\n [ngTemplateOutlet]=\"previewMarked\"\n [ngTemplateOutletContext]=\"{ $implicit: node, depth: depth + 1 }\"\n /></s>\n }\n @case ('code') {\n <code\n ><ng-container\n [ngTemplateOutlet]=\"previewMarked\"\n [ngTemplateOutletContext]=\"{ $implicit: node, depth: depth + 1 }\"\n /></code>\n }\n @default {\n <ng-container\n [ngTemplateOutlet]=\"previewMarked\"\n [ngTemplateOutletContext]=\"{ $implicit: node, depth: depth + 1 }\"\n />\n }\n }\n } @else {\n @switch (node.type) {\n @case ('text') {\n <ng-container>{{ node.text }}</ng-container>\n }\n @case ('hard_break') {\n <br />\n }\n @case ('image') {\n <img [src]=\"text(node, 'src')\" [alt]=\"text(node, 'alt') ?? ''\" [attr.title]=\"text(node, 'title')\" />\n }\n }\n }\n</ng-template>\n" }]
2594
- }], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], touch: [{ type: i0.Output, args: ["touch"] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], format: [{ type: i0.Input, args: [{ isSignal: true, alias: "format", required: false }] }], toolbar: [{ type: i0.Input, args: [{ isSignal: true, alias: "toolbar", required: false }] }], surface: [{ type: i0.ViewChild, args: ['surface', { isSignal: true }] }], toolButtons: [{ type: i0.ViewChildren, args: ['tool', { ...{ read: ElementRef }, isSignal: true }] }], linkPopover: [{ type: i0.ViewChild, args: ['linkPopover', { isSignal: true }] }] } });
2692
+ ], imports: [NgTemplateOutlet, WrButton, WrInput, WrPopover], template: "@if (tools().length) {\n <div class=\"wr-editor__toolbar\" role=\"toolbar\" [attr.aria-label]=\"toolbarLabel()\" [attr.aria-controls]=\"surfaceId\">\n @for (item of tools(); track $index; let n = $index) {\n @if (item === '|') {\n <span class=\"wr-editor__separator\" aria-hidden=\"true\"></span>\n } @else if (item === 'link') {\n <!-- The link button hosts the link panel, so it carries the popover's own\n `aria-haspopup` / `aria-expanded` / `aria-controls` \u2014 and no tooltip,\n since one element cannot host `[wrPopover]` twice; its name and\n `aria-keyshortcuts` still say what the others' tooltips say. No\n `aria-pressed` either: a button that opens a popup is not a toggle,\n and the panel's remove button is where \"in a link\" is announced. -->\n <button\n #tool\n #linkPopover=\"wrPopover\"\n wr-btn\n type=\"button\"\n class=\"wr-editor__tool\"\n [class.wr-editor__tool--active]=\"state(item)?.active\"\n [disabled]=\"!state(item)?.available\"\n [attr.aria-label]=\"label(item)\"\n [attr.aria-keyshortcuts]=\"keyShortcuts(item)\"\n [wrPopover]=\"linkPanel\"\n [ariaLabel]=\"label(item)\"\n (opened)=\"onLinkOpened()\"\n (closed)=\"onLinkClosed()\"\n (focus)=\"onToolFocus(buttonIndex()[n])\"\n (keydown)=\"onToolbarKeydown($event)\"\n (mousedown)=\"$event.preventDefault()\"\n >\n <svg\n class=\"wr-editor__icon\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n @for (d of icon(item); track $index) {\n <path [attr.d]=\"d\" />\n }\n </svg>\n </button>\n } @else {\n <button\n #tool\n wr-btn\n type=\"button\"\n class=\"wr-editor__tool\"\n mode=\"tooltip\"\n [class.wr-editor__tool--active]=\"state(item)?.active\"\n [disabled]=\"!state(item)?.available\"\n [attr.aria-pressed]=\"isToggle(item) ? (state(item)?.active ? 'true' : 'false') : null\"\n [attr.aria-label]=\"label(item)\"\n [attr.aria-keyshortcuts]=\"keyShortcuts(item)\"\n [wrPopover]=\"tooltip(item)\"\n (click)=\"onToolClick(item, $event)\"\n (focus)=\"onToolFocus(buttonIndex()[n])\"\n (keydown)=\"onToolbarKeydown($event)\"\n (mousedown)=\"$event.preventDefault()\"\n >\n <svg\n class=\"wr-editor__icon\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n @for (d of icon(item); track $index) {\n <path [attr.d]=\"d\" />\n }\n </svg>\n </button>\n }\n }\n </div>\n}\n\n<!--\n The document drawn by Angular, in either of the two ways it stands on screen.\n\n In the STATIC mode \u2014 read-only with nothing mounted \u2014 it is the editor, for as\n long as `readonly` holds, so it takes the id, the name, the field's state and\n the tab stop the surface below would have carried, and that surface is not\n rendered at all: two elements with one id, or a nameless empty textbox beside\n the text, is what rendering both would mean. A focusable `role=\"group\"` and not\n a textbox, because every descendant of a textbox is presentational \u2014 the\n headings, lists and tables being shown would stop being announced, which is the\n whole point of showing a document. No `aria-readonly`: ARIA does not allow it\n on `group`, and `.wr-editor--readonly` on the host is the cue instead.\n\n Otherwise it is the preview that stands in until ProseMirror mounts \u2014 on the\n server, and for the first browser frame \u2014 and carries nothing of the surface's.\n\n Either way it is a SIBLING of the mount surface, never inside it: `{ mount }`\n replaces the element's children, and nodes Angular owns would be deleted out\n from under the next change detection.\n-->\n@if (staticMode()) {\n <div\n #staticDoc\n class=\"wr-editor__preview wr-editor__preview--static wr-editor__content\"\n role=\"group\"\n [id]=\"surfaceId\"\n [attr.aria-label]=\"accessibleName()\"\n [attr.aria-labelledby]=\"labelledBy()\"\n [attr.aria-describedby]=\"fieldAria.describedBy()\"\n [attr.aria-invalid]=\"fieldAria.ariaInvalid()\"\n [attr.aria-disabled]=\"disabled() ? 'true' : null\"\n [attr.tabindex]=\"disabled() ? null : 0\"\n (focus)=\"focused.set(true)\"\n (blur)=\"focused.set(false)\"\n >\n <ng-container [ngTemplateOutlet]=\"previewContent\" />\n </div>\n} @else {\n @if (preview()) {\n <div class=\"wr-editor__preview wr-editor__content\">\n <ng-container [ngTemplateOutlet]=\"previewContent\" />\n </div>\n }\n\n <!--\n The mount surface. Angular binds every attribute it needs, so the prerendered\n page and the synchronous first render already carry the role, the name and the\n field's error state; ProseMirror adds only its own class, `contenteditable` and\n `translate`, and never touches an attribute it did not set. No Angular child\n ever lives in here.\n -->\n <div\n #surface\n class=\"wr-editor__surface wr-editor__content\"\n role=\"textbox\"\n aria-multiline=\"true\"\n [id]=\"surfaceId\"\n [hidden]=\"!mounted()\"\n [attr.aria-label]=\"accessibleName()\"\n [attr.aria-labelledby]=\"labelledBy()\"\n [attr.aria-describedby]=\"fieldAria.describedBy()\"\n [attr.aria-invalid]=\"fieldAria.ariaInvalid()\"\n [attr.aria-readonly]=\"locked() && !disabled() ? 'true' : null\"\n [attr.aria-disabled]=\"disabled() ? 'true' : null\"\n [attr.aria-placeholder]=\"ariaPlaceholder()\"\n [attr.tabindex]=\"locked() && !disabled() ? 0 : null\"\n (focus)=\"focused.set(true)\"\n (blur)=\"focused.set(false)\"\n ></div>\n}\n\n<!-- The document itself, written once for both of the containers above. -->\n<ng-template #previewContent>\n <ng-container [ngTemplateOutlet]=\"previewBlocks\" [ngTemplateOutletContext]=\"{ $implicit: preview()?.content }\" />\n</ng-template>\n\n<!--\n The popover styles its panel as `.wr-popover-overlay > *`, which is this form,\n so the form stays a plain block and the layout lives one level in.\n-->\n<ng-template #linkPanel>\n <form\n class=\"wr-editor-link\"\n novalidate\n [id]=\"linkField.panelId\"\n (submit)=\"applyLink($event)\"\n (focusout)=\"onFocusOut($event)\"\n >\n <div class=\"wr-editor-link__body\">\n <label class=\"wr-editor-link__label\" [attr.for]=\"linkField.controlId()\">{{ linkUrlLabel() }}</label>\n <input\n wrInput\n class=\"wr-editor-link__input\"\n type=\"text\"\n inputmode=\"url\"\n autocomplete=\"url\"\n spellcheck=\"false\"\n [value]=\"linkDraft()\"\n (input)=\"onLinkInput($event)\"\n />\n @if (linkField.invalid()) {\n <p class=\"wr-editor-link__error\" role=\"alert\" [id]=\"linkField.errorId\">{{ linkInvalidLabel() }}</p>\n }\n <div class=\"wr-editor-link__actions\">\n @if (linkActive()) {\n <button wr-btn type=\"button\" class=\"wr-editor-link__remove\" (click)=\"removeLink()\">\n {{ linkRemoveLabel() }}\n </button>\n }\n <button wr-btn type=\"submit\" color=\"primary\" class=\"wr-editor-link__apply\">{{ linkApplyLabel() }}</button>\n </div>\n </div>\n </form>\n</ng-template>\n\n<!--\n Recursion runs through `ngTemplateOutlet`, as in `<wr-markdown>`, so the preview\n is exactly the document with no wrapper between a `<ul>` and its `<li>`.\n-->\n<ng-template #previewBlocks let-raw>\n @for (node of nodesOf(raw); track $index) {\n @switch (node.type) {\n @case ('paragraph') {\n <p [class.wr-editor__placeholder]=\"ariaPlaceholder() !== null\" [attr.data-placeholder]=\"ariaPlaceholder()\">\n <ng-container [ngTemplateOutlet]=\"previewInline\" [ngTemplateOutletContext]=\"{ $implicit: node.content }\" />\n </p>\n }\n @case ('heading') {\n @switch (attr(node, 'level')) {\n @case (2) {\n <h2>\n <ng-container\n [ngTemplateOutlet]=\"previewInline\"\n [ngTemplateOutletContext]=\"{ $implicit: node.content }\"\n />\n </h2>\n }\n @case (3) {\n <h3>\n <ng-container\n [ngTemplateOutlet]=\"previewInline\"\n [ngTemplateOutletContext]=\"{ $implicit: node.content }\"\n />\n </h3>\n }\n @default {\n <h1>\n <ng-container\n [ngTemplateOutlet]=\"previewInline\"\n [ngTemplateOutletContext]=\"{ $implicit: node.content }\"\n />\n </h1>\n }\n }\n }\n @case ('blockquote') {\n <blockquote>\n <ng-container [ngTemplateOutlet]=\"previewBlocks\" [ngTemplateOutletContext]=\"{ $implicit: node.content }\" />\n </blockquote>\n }\n @case ('code_block') {\n <pre><code>{{ textOf(node) }}</code></pre>\n }\n @case ('horizontal_rule') {\n <hr />\n }\n @case ('bullet_list') {\n <ul>\n <ng-container [ngTemplateOutlet]=\"previewBlocks\" [ngTemplateOutletContext]=\"{ $implicit: node.content }\" />\n </ul>\n }\n @case ('ordered_list') {\n <ol [attr.start]=\"attr(node, 'order') === 1 ? null : attr(node, 'order')\">\n <ng-container [ngTemplateOutlet]=\"previewBlocks\" [ngTemplateOutletContext]=\"{ $implicit: node.content }\" />\n </ol>\n }\n @case ('list_item') {\n @let checked = attr(node, 'checked');\n @if (checked === null) {\n <li>\n <ng-container [ngTemplateOutlet]=\"previewBlocks\" [ngTemplateOutletContext]=\"{ $implicit: node.content }\" />\n </li>\n } @else {\n <li class=\"wr-editor__item wr-editor__item--task\" [attr.data-checked]=\"checked\">\n <span class=\"wr-editor__task\" [class.wr-editor__task--checked]=\"checked === true\" aria-hidden=\"true\"></span>\n <span class=\"wr-editor__sr-only\">{{ checked === true ? taskDoneLabel() : taskTodoLabel() }}</span>\n <div class=\"wr-editor__task-body\">\n <ng-container\n [ngTemplateOutlet]=\"previewBlocks\"\n [ngTemplateOutletContext]=\"{ $implicit: node.content }\"\n />\n </div>\n </li>\n }\n }\n @case ('table') {\n <table>\n <tbody>\n <ng-container [ngTemplateOutlet]=\"previewBlocks\" [ngTemplateOutletContext]=\"{ $implicit: node.content }\" />\n </tbody>\n </table>\n }\n @case ('table_row') {\n <tr>\n <ng-container [ngTemplateOutlet]=\"previewBlocks\" [ngTemplateOutletContext]=\"{ $implicit: node.content }\" />\n </tr>\n }\n @case ('table_header') {\n <th [style.text-align]=\"text(node, 'align')\">\n <ng-container [ngTemplateOutlet]=\"previewBlocks\" [ngTemplateOutletContext]=\"{ $implicit: node.content }\" />\n </th>\n }\n @case ('table_cell') {\n <td [style.text-align]=\"text(node, 'align')\">\n <ng-container [ngTemplateOutlet]=\"previewBlocks\" [ngTemplateOutletContext]=\"{ $implicit: node.content }\" />\n </td>\n }\n }\n }\n</ng-template>\n\n<ng-template #previewInline let-raw>\n @for (node of nodesOf(raw); track $index) {\n <ng-container [ngTemplateOutlet]=\"previewMarked\" [ngTemplateOutletContext]=\"{ $implicit: node, depth: 0 }\" />\n }\n</ng-template>\n\n<!-- One mark per level, outermost first \u2014 the order the schema ranks them in. -->\n<ng-template #previewMarked let-raw let-depth=\"depth\">\n @let node = nodeOf(raw);\n @let mark = markAt(node, depth);\n @if (mark) {\n @switch (mark.type) {\n @case ('link') {\n <a [href]=\"text(mark, 'href')\" [attr.title]=\"text(mark, 'title')\" rel=\"noopener noreferrer nofollow\"\n ><ng-container\n [ngTemplateOutlet]=\"previewMarked\"\n [ngTemplateOutletContext]=\"{ $implicit: node, depth: depth + 1 }\"\n /></a>\n }\n @case ('strong') {\n <strong\n ><ng-container\n [ngTemplateOutlet]=\"previewMarked\"\n [ngTemplateOutletContext]=\"{ $implicit: node, depth: depth + 1 }\"\n /></strong>\n }\n @case ('em') {\n <em\n ><ng-container\n [ngTemplateOutlet]=\"previewMarked\"\n [ngTemplateOutletContext]=\"{ $implicit: node, depth: depth + 1 }\"\n /></em>\n }\n @case ('underline') {\n <u\n ><ng-container\n [ngTemplateOutlet]=\"previewMarked\"\n [ngTemplateOutletContext]=\"{ $implicit: node, depth: depth + 1 }\"\n /></u>\n }\n @case ('strike') {\n <s\n ><ng-container\n [ngTemplateOutlet]=\"previewMarked\"\n [ngTemplateOutletContext]=\"{ $implicit: node, depth: depth + 1 }\"\n /></s>\n }\n @case ('code') {\n <code\n ><ng-container\n [ngTemplateOutlet]=\"previewMarked\"\n [ngTemplateOutletContext]=\"{ $implicit: node, depth: depth + 1 }\"\n /></code>\n }\n @default {\n <ng-container\n [ngTemplateOutlet]=\"previewMarked\"\n [ngTemplateOutletContext]=\"{ $implicit: node, depth: depth + 1 }\"\n />\n }\n }\n } @else {\n @switch (node.type) {\n @case ('text') {\n <ng-container>{{ node.text }}</ng-container>\n }\n @case ('hard_break') {\n <br />\n }\n @case ('image') {\n <img [src]=\"text(node, 'src')\" [alt]=\"text(node, 'alt') ?? ''\" [attr.title]=\"text(node, 'title')\" />\n }\n }\n }\n</ng-template>\n" }]
2693
+ }], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], touch: [{ type: i0.Output, args: ["touch"] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], format: [{ type: i0.Input, args: [{ isSignal: true, alias: "format", required: false }] }], toolbar: [{ type: i0.Input, args: [{ isSignal: true, alias: "toolbar", required: false }] }], surface: [{ type: i0.ViewChild, args: ['surface', { isSignal: true }] }], staticDoc: [{ type: i0.ViewChild, args: ['staticDoc', { isSignal: true }] }], toolButtons: [{ type: i0.ViewChildren, args: ['tool', { ...{ read: ElementRef }, isSignal: true }] }], linkPopover: [{ type: i0.ViewChild, args: ['linkPopover', { isSignal: true }] }] } });
2595
2694
 
2596
2695
  /**
2597
2696
  * Generated bundle index. Do not edit.