ngwr 14.9.0 → 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.
- package/editor/styles/_index.scss +16 -1
- package/fesm2022/ngwr-editor-testing.mjs +132 -53
- package/fesm2022/ngwr-editor-testing.mjs.map +1 -1
- package/fesm2022/ngwr-editor.mjs +119 -20
- package/fesm2022/ngwr-editor.mjs.map +1 -1
- package/fesm2022/ngwr-sidebar.mjs +7 -2
- package/fesm2022/ngwr-sidebar.mjs.map +1 -1
- package/fesm2022/ngwr-version.mjs +1 -1
- package/fesm2022/ngwr-version.mjs.map +1 -1
- package/package.json +1 -1
- package/types/ngwr-editor-testing.d.ts +79 -33
- package/types/ngwr-editor.d.ts +63 -9
- package/types/ngwr-sidebar.d.ts +25 -0
- package/types/ngwr-version.d.ts +1 -1
package/fesm2022/ngwr-editor.mjs
CHANGED
|
@@ -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
|
|
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
|
-
*
|
|
1867
|
-
*
|
|
1868
|
-
*
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
2105
|
-
*
|
|
2106
|
-
*
|
|
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
|
|
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
|
-
|
|
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
|
-
/**
|
|
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
|
-
|
|
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:
|
|
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.
|