ngwr 9.0.1 → 10.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -0
- package/badge/styles/_index.scss +16 -6
- package/badge/styles/_tag.scss +10 -1
- package/button/styles/_index.scss +48 -1
- package/date-picker/styles/_index.scss +62 -0
- package/dialog/styles/_index.scss +65 -0
- package/drawer/styles/_index.scss +83 -0
- package/fesm2022/ngwr-action-sheet.mjs +1 -1
- package/fesm2022/ngwr-action-sheet.mjs.map +1 -1
- package/fesm2022/ngwr-breadcrumbs.mjs +2 -2
- package/fesm2022/ngwr-breadcrumbs.mjs.map +1 -1
- package/fesm2022/ngwr-card.mjs +2 -2
- package/fesm2022/ngwr-card.mjs.map +1 -1
- package/fesm2022/ngwr-cascader.mjs +6 -5
- package/fesm2022/ngwr-cascader.mjs.map +1 -1
- package/fesm2022/ngwr-color-picker.mjs +6 -5
- package/fesm2022/ngwr-color-picker.mjs.map +1 -1
- package/fesm2022/ngwr-context-menu.mjs +7 -7
- package/fesm2022/ngwr-context-menu.mjs.map +1 -1
- package/fesm2022/ngwr-date-picker.mjs +521 -8
- package/fesm2022/ngwr-date-picker.mjs.map +1 -1
- package/fesm2022/ngwr-density.mjs +10 -2
- package/fesm2022/ngwr-density.mjs.map +1 -1
- package/fesm2022/ngwr-dialog.mjs +95 -13
- package/fesm2022/ngwr-dialog.mjs.map +1 -1
- package/fesm2022/ngwr-drag-drop.mjs +2 -2
- package/fesm2022/ngwr-drag-drop.mjs.map +1 -1
- package/fesm2022/ngwr-drawer.mjs +377 -22
- package/fesm2022/ngwr-drawer.mjs.map +1 -1
- package/fesm2022/ngwr-dropdown.mjs +4 -3
- package/fesm2022/ngwr-dropdown.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-en.mjs +21 -0
- package/fesm2022/ngwr-i18n-en.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-ru.mjs +22 -0
- package/fesm2022/ngwr-i18n-ru.mjs.map +1 -1
- package/fesm2022/ngwr-i18n.mjs +74 -1
- package/fesm2022/ngwr-i18n.mjs.map +1 -1
- package/fesm2022/ngwr-icon.mjs +74 -35
- package/fesm2022/ngwr-icon.mjs.map +1 -1
- package/fesm2022/ngwr-lightbox.mjs +2 -2
- package/fesm2022/ngwr-lightbox.mjs.map +1 -1
- package/fesm2022/ngwr-list.mjs +2 -2
- package/fesm2022/ngwr-list.mjs.map +1 -1
- package/fesm2022/ngwr-mention.mjs +166 -10
- package/fesm2022/ngwr-mention.mjs.map +1 -1
- package/fesm2022/ngwr-overlay.mjs +173 -3
- package/fesm2022/ngwr-overlay.mjs.map +1 -1
- package/fesm2022/ngwr-pagination.mjs +17 -4
- package/fesm2022/ngwr-pagination.mjs.map +1 -1
- package/fesm2022/ngwr-popconfirm.mjs +4 -3
- package/fesm2022/ngwr-popconfirm.mjs.map +1 -1
- package/fesm2022/ngwr-popover.mjs +10 -3
- package/fesm2022/ngwr-popover.mjs.map +1 -1
- package/fesm2022/ngwr-select.mjs +233 -43
- package/fesm2022/ngwr-select.mjs.map +1 -1
- package/fesm2022/ngwr-table.mjs +1 -1
- package/fesm2022/ngwr-table.mjs.map +1 -1
- package/fesm2022/ngwr-theme.mjs +13 -3
- package/fesm2022/ngwr-theme.mjs.map +1 -1
- package/fesm2022/ngwr-tree.mjs +4 -3
- package/fesm2022/ngwr-tree.mjs.map +1 -1
- package/fesm2022/ngwr-version.mjs +1 -1
- package/fesm2022/ngwr-version.mjs.map +1 -1
- package/fesm2022/ngwr-waves.mjs +25 -3
- package/fesm2022/ngwr-waves.mjs.map +1 -1
- package/i18n/en.json +165 -0
- package/i18n/ru.json +165 -0
- package/icon/styles/_index.scss +10 -1
- package/input/styles/_index.scss +16 -0
- package/input-number/styles/_index.scss +8 -1
- package/keyboard/styles/_index.scss +10 -3
- package/llms-full.txt +14 -14
- package/llms.txt +12 -6
- package/mention/styles/_index.scss +23 -0
- package/package.json +23 -5
- package/pagination/styles/_index.scss +12 -0
- package/popover/styles/_index.scss +129 -45
- package/schematics/ng-add/index.js +18 -4
- package/schematics/package.json +3 -0
- package/schematics/use/symbol-map.json +7 -0
- package/select/styles/_index.scss +13 -0
- package/splitter/styles/_index.scss +7 -1
- package/table/styles/_index.scss +28 -10
- package/theme/styles/_box-model.scss +29 -0
- package/theme/styles/_colors.scss +74 -15
- package/theme/styles/_dark.scss +52 -6
- package/theme/styles/_index.scss +1 -0
- package/types/ngwr-cascader.d.ts +1 -0
- package/types/ngwr-color-picker.d.ts +1 -0
- package/types/ngwr-context-menu.d.ts +1 -0
- package/types/ngwr-date-picker.d.ts +172 -4
- package/types/ngwr-density.d.ts +8 -1
- package/types/ngwr-dialog.d.ts +80 -8
- package/types/ngwr-drawer.d.ts +245 -10
- package/types/ngwr-dropdown.d.ts +1 -0
- package/types/ngwr-i18n.d.ts +65 -2
- package/types/ngwr-icon.d.ts +11 -19
- package/types/ngwr-mention.d.ts +45 -1
- package/types/ngwr-overlay.d.ts +80 -2
- package/types/ngwr-pagination.d.ts +14 -1
- package/types/ngwr-popconfirm.d.ts +1 -0
- package/types/ngwr-popover.d.ts +1 -0
- package/types/ngwr-select.d.ts +121 -9
- package/types/ngwr-theme.d.ts +8 -1
- package/types/ngwr-tree.d.ts +1 -0
- package/types/ngwr-version.d.ts +1 -1
- package/types/ngwr-waves.d.ts +10 -0
- package/waves/styles/_index.scss +21 -0
package/types/ngwr-mention.d.ts
CHANGED
|
@@ -62,12 +62,43 @@ declare class WrMention<T extends WrMentionItem = WrMentionItem> {
|
|
|
62
62
|
private readonly overlay;
|
|
63
63
|
private readonly scrollStrategies;
|
|
64
64
|
private readonly destroyRef;
|
|
65
|
+
private readonly isBrowser;
|
|
65
66
|
private overlayRef;
|
|
66
67
|
private panelRef;
|
|
67
68
|
private state;
|
|
68
69
|
private readonly activeIndex;
|
|
69
70
|
private readonly filteredItems;
|
|
71
|
+
private readonly isOpen;
|
|
72
|
+
/** @internal — read by the `aria-controls` host binding. */
|
|
73
|
+
protected readonly listboxId: string;
|
|
74
|
+
private readonly listLabel;
|
|
75
|
+
private readonly availableText;
|
|
76
|
+
private readonly insertedText;
|
|
77
|
+
/**
|
|
78
|
+
* `aria-activedescendant`, or `null`.
|
|
79
|
+
*
|
|
80
|
+
* Gated hard on `isOpen()`, which is only set once the panel's options are
|
|
81
|
+
* actually in the DOM. An id that resolves to nothing is an author error, and
|
|
82
|
+
* the failure mode is silent: the browser does not complain, it simply stops
|
|
83
|
+
* firing the event that makes a screen reader speak the option. Same
|
|
84
|
+
* discipline `tree.ts` uses for its virtualized rows.
|
|
85
|
+
*/
|
|
86
|
+
protected readonly activeOptionId: _angular_core.Signal<string | null>;
|
|
87
|
+
/**
|
|
88
|
+
* Polite live region, parked on `<body>`.
|
|
89
|
+
*
|
|
90
|
+
* Not inside the panel: `close()` disposes the overlay on every keystroke that
|
|
91
|
+
* leaves a mention, so a region living there would be recreated constantly —
|
|
92
|
+
* and a live region has to be in the DOM *before* its text changes to be
|
|
93
|
+
* announced at all. It also could not announce the two cases that matter most,
|
|
94
|
+
* since in both the panel is already gone: "nothing matched" and "inserted X".
|
|
95
|
+
*
|
|
96
|
+
* Not a sibling of the host either — that would land inside the consumer's
|
|
97
|
+
* markup and start matching their `+`, `~` and `:nth-child()` selectors.
|
|
98
|
+
*/
|
|
99
|
+
private liveRegion;
|
|
70
100
|
constructor();
|
|
101
|
+
private announce;
|
|
71
102
|
/** @internal */
|
|
72
103
|
protected onInput(): void;
|
|
73
104
|
/** @internal */
|
|
@@ -97,13 +128,26 @@ declare class WrMentionPanel {
|
|
|
97
128
|
readonly items: _angular_core.InputSignal<readonly WrMentionItem[]>;
|
|
98
129
|
readonly activeIndex: _angular_core.InputSignal<number>;
|
|
99
130
|
readonly displayWith: _angular_core.InputSignal<(item: WrMentionItem) => string>;
|
|
131
|
+
/**
|
|
132
|
+
* Id of the `<ul role="listbox">`, minted by the directive and pointed at by
|
|
133
|
+
* its `aria-controls`. Owned there rather than here because the panel is
|
|
134
|
+
* disposed and rebuilt whenever the caret leaves a mention, while the
|
|
135
|
+
* attribute on the host has to stay put.
|
|
136
|
+
*
|
|
137
|
+
* Plain `input('')`, never `input.required` — a ComponentPortal-created
|
|
138
|
+
* component throws on the first change detection if a required input is unset.
|
|
139
|
+
*/
|
|
140
|
+
readonly listboxId: _angular_core.InputSignal<string>;
|
|
141
|
+
/** Accessible name for the listbox. ARIA requires one; the directive supplies it from the catalog. */
|
|
142
|
+
readonly listLabel: _angular_core.InputSignal<string>;
|
|
143
|
+
protected optionId(index: number): string;
|
|
100
144
|
/** Emitted when the user picks an item (click). */
|
|
101
145
|
readonly picked: _angular_core.OutputEmitterRef<WrMentionItem>;
|
|
102
146
|
/** Emitted on mouseenter — used by the directive to track hover. */
|
|
103
147
|
readonly hovered: _angular_core.OutputEmitterRef<number>;
|
|
104
148
|
protected onPick(item: WrMentionItem, event: MouseEvent): void;
|
|
105
149
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<WrMentionPanel, never>;
|
|
106
|
-
static ɵcmp: _angular_core.ɵɵComponentDeclaration<WrMentionPanel, "wr-mention-panel", never, { "items": { "alias": "items"; "required": false; "isSignal": true; }; "activeIndex": { "alias": "activeIndex"; "required": false; "isSignal": true; }; "displayWith": { "alias": "displayWith"; "required": false; "isSignal": true; }; }, { "picked": "picked"; "hovered": "hovered"; }, never, never, true, never>;
|
|
150
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<WrMentionPanel, "wr-mention-panel", never, { "items": { "alias": "items"; "required": false; "isSignal": true; }; "activeIndex": { "alias": "activeIndex"; "required": false; "isSignal": true; }; "displayWith": { "alias": "displayWith"; "required": false; "isSignal": true; }; "listboxId": { "alias": "listboxId"; "required": false; "isSignal": true; }; "listLabel": { "alias": "listLabel"; "required": false; "isSignal": true; }; }, { "picked": "picked"; "hovered": "hovered"; }, never, never, true, never>;
|
|
107
151
|
}
|
|
108
152
|
|
|
109
153
|
export { WrMention, WrMentionPanel };
|
package/types/ngwr-overlay.d.ts
CHANGED
|
@@ -1,7 +1,85 @@
|
|
|
1
|
-
import { OverlayContainer, Overlay } from '@angular/cdk/overlay';
|
|
1
|
+
import { OverlayRef, OverlayContainer, Overlay } from '@angular/cdk/overlay';
|
|
2
|
+
import { Observable } from 'rxjs';
|
|
2
3
|
import * as i0 from '@angular/core';
|
|
3
4
|
import { EnvironmentProviders, InjectionToken } from '@angular/core';
|
|
4
5
|
|
|
6
|
+
/**
|
|
7
|
+
* @license
|
|
8
|
+
*
|
|
9
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
10
|
+
* found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Appends a dismiss (✕) button to a service-opened overlay panel.
|
|
14
|
+
*
|
|
15
|
+
* `WrDialog` and `WrDrawerManager` build their panel around a component the
|
|
16
|
+
* consumer supplies, so there is no template of their own to put a corner button
|
|
17
|
+
* in — they call this instead. Component-driven overlays (`<wr-drawer>`) render
|
|
18
|
+
* the same markup in their template.
|
|
19
|
+
*
|
|
20
|
+
* Uses `host.ownerDocument` rather than the global `document`, so it carries no
|
|
21
|
+
* hidden browser-only assumption; callers still gate on the platform because
|
|
22
|
+
* they have nothing to append to on the server.
|
|
23
|
+
*
|
|
24
|
+
* @param host Panel element the button is appended to (and positioned against).
|
|
25
|
+
* @param className BEM class for the button, e.g. `wr-dialog__close`.
|
|
26
|
+
* @param label Accessible name — already localized by the caller.
|
|
27
|
+
* @param onClose Invoked on click.
|
|
28
|
+
*/
|
|
29
|
+
declare function wrAppendOverlayClose(host: HTMLElement, className: string, label: string, onClose: () => void): HTMLButtonElement;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @license
|
|
33
|
+
*
|
|
34
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
35
|
+
* found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Outside-click source for overlays — a drop-in replacement for
|
|
40
|
+
* `OverlayRef.outsidePointerEvents()`.
|
|
41
|
+
*
|
|
42
|
+
* **Why this exists.** CDK's `OverlayOutsideClickDispatcher` binds its
|
|
43
|
+
* listeners to `document.body`. When page content is shorter than the viewport,
|
|
44
|
+
* a click in the empty space below it targets `<html>` — and `<html>` is
|
|
45
|
+
* `body`'s *parent*, so the event bubbles straight to the document and `body`
|
|
46
|
+
* never sees it. The overlay silently stays open. Listening on the document
|
|
47
|
+
* itself catches both cases, and costs nothing else.
|
|
48
|
+
*
|
|
49
|
+
* Semantics are otherwise identical to the CDK's, deliberately:
|
|
50
|
+
*
|
|
51
|
+
* - Emits on `click` / `auxclick` / `contextmenu`, in the capture phase, and
|
|
52
|
+
* tracks `pointerdown` so a `click` is judged by where the press *started*
|
|
53
|
+
* (a drag that ends outside the pane doesn't count as an outside click).
|
|
54
|
+
* - Walks watchers top-most first and stops at the first one that contains the
|
|
55
|
+
* event, so clicking inside a stacked overlay can't close the ones beneath
|
|
56
|
+
* it (a `wr-select` opened inside a `wr-popover`, say).
|
|
57
|
+
* - Fires for the very click that opened the overlay, which is why every caller
|
|
58
|
+
* also ignores events landing on its own trigger.
|
|
59
|
+
*
|
|
60
|
+
* Registration is per subscription: the document listeners go up with the first
|
|
61
|
+
* watcher and come down with the last, so an app with no open overlay has none.
|
|
62
|
+
*/
|
|
63
|
+
declare class WrOutsideClick {
|
|
64
|
+
private readonly doc;
|
|
65
|
+
private readonly isBrowser;
|
|
66
|
+
/** In attach order — last is top-most. */
|
|
67
|
+
private readonly watchers;
|
|
68
|
+
private cleanup;
|
|
69
|
+
private pressOrigin;
|
|
70
|
+
/**
|
|
71
|
+
* Emits every `click` / `auxclick` / `contextmenu` that lands outside the
|
|
72
|
+
* given overlay's pane, for as long as the returned observable is subscribed.
|
|
73
|
+
*/
|
|
74
|
+
outsidePointerEvents(overlayRef: OverlayRef): Observable<MouseEvent>;
|
|
75
|
+
private readonly onPointerDown;
|
|
76
|
+
private readonly onClick;
|
|
77
|
+
private listen;
|
|
78
|
+
private unlisten;
|
|
79
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<WrOutsideClick, never>;
|
|
80
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<WrOutsideClick>;
|
|
81
|
+
}
|
|
82
|
+
|
|
5
83
|
/**
|
|
6
84
|
* @license
|
|
7
85
|
*
|
|
@@ -175,4 +253,4 @@ declare const WR_OVERLAY: InjectionToken<Overlay>;
|
|
|
175
253
|
*/
|
|
176
254
|
declare const WR_OVERLAY_CONTAINER: InjectionToken<OverlayContainer>;
|
|
177
255
|
|
|
178
|
-
export { WR_OVERLAY, WR_OVERLAY_CONTAINER, WR_RESPONSIVE_OVERLAYS, WrOverlayContainer, provideWrOverlay, provideWrResponsiveOverlays, wrPresentAsSheet };
|
|
256
|
+
export { WR_OVERLAY, WR_OVERLAY_CONTAINER, WR_RESPONSIVE_OVERLAYS, WrOutsideClick, WrOverlayContainer, provideWrOverlay, provideWrResponsiveOverlays, wrAppendOverlayClose, wrPresentAsSheet };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import * as ngwr_i18n from 'ngwr/i18n';
|
|
1
2
|
import * as _angular_core from '@angular/core';
|
|
2
3
|
|
|
3
4
|
/**
|
|
@@ -91,11 +92,23 @@ declare class WrPagination {
|
|
|
91
92
|
readonly nextLabel: _angular_core.InputSignal<string | null>;
|
|
92
93
|
/** "Items per page" label. Falls back to `pagination.itemsPerPage`. */
|
|
93
94
|
readonly itemsPerPageLabel: _angular_core.InputSignal<string | null>;
|
|
95
|
+
/** Accessible name for the `role="navigation"` host. Falls back to `pagination.label`. */
|
|
96
|
+
readonly label: _angular_core.InputSignal<string | null>;
|
|
94
97
|
/** Resolved labels. */
|
|
95
98
|
protected readonly resolvedOf: _angular_core.Signal<string>;
|
|
96
99
|
protected readonly resolvedPrev: _angular_core.Signal<string>;
|
|
97
100
|
protected readonly resolvedNext: _angular_core.Signal<string>;
|
|
98
101
|
protected readonly resolvedItemsPerPage: _angular_core.Signal<string>;
|
|
102
|
+
/**
|
|
103
|
+
* Page-size option text — interpolates `{{size}}`. Was the template literal
|
|
104
|
+
* `{{ option }} / page`, which no catalog could reach, so a Russian UI read
|
|
105
|
+
* "25 / page".
|
|
106
|
+
*/
|
|
107
|
+
protected readonly perPage: (params?: ngwr_i18n.WrI18nParams) => string;
|
|
108
|
+
/** Per-page-button aria-label — interpolates `{{page}}`. */
|
|
109
|
+
protected readonly goToPage: (params?: ngwr_i18n.WrI18nParams) => string;
|
|
110
|
+
/** The nav landmark's own accessible name — was a hardcoded English literal. */
|
|
111
|
+
protected readonly navLabel: _angular_core.Signal<string>;
|
|
99
112
|
/** Internal: total page count. */
|
|
100
113
|
protected readonly totalPages: _angular_core.Signal<number>;
|
|
101
114
|
/** Internal: "X–Y of Z" string. */
|
|
@@ -112,7 +125,7 @@ declare class WrPagination {
|
|
|
112
125
|
protected goTo(page: number | typeof ELLIPSIS): void;
|
|
113
126
|
protected onSizeChange(size: number): void;
|
|
114
127
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<WrPagination, never>;
|
|
115
|
-
static ɵcmp: _angular_core.ɵɵComponentDeclaration<WrPagination, "wr-pagination", never, { "currentPage": { "alias": "currentPage"; "required": false; "isSignal": true; }; "pageSize": { "alias": "pageSize"; "required": false; "isSignal": true; }; "total": { "alias": "total"; "required": false; "isSignal": true; }; "pageSizeOptions": { "alias": "pageSizeOptions"; "required": false; "isSignal": true; }; "showSizeChanger": { "alias": "showSizeChanger"; "required": false; "isSignal": true; }; "showTotal": { "alias": "showTotal"; "required": false; "isSignal": true; }; "align": { "alias": "align"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "shape": { "alias": "shape"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "responsive": { "alias": "responsive"; "required": false; "isSignal": true; }; "ofLabel": { "alias": "ofLabel"; "required": false; "isSignal": true; }; "prevLabel": { "alias": "prevLabel"; "required": false; "isSignal": true; }; "nextLabel": { "alias": "nextLabel"; "required": false; "isSignal": true; }; "itemsPerPageLabel": { "alias": "itemsPerPageLabel"; "required": false; "isSignal": true; }; }, { "currentPage": "currentPageChange"; "pageSize": "pageSizeChange"; }, never, never, true, never>;
|
|
128
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<WrPagination, "wr-pagination", never, { "currentPage": { "alias": "currentPage"; "required": false; "isSignal": true; }; "pageSize": { "alias": "pageSize"; "required": false; "isSignal": true; }; "total": { "alias": "total"; "required": false; "isSignal": true; }; "pageSizeOptions": { "alias": "pageSizeOptions"; "required": false; "isSignal": true; }; "showSizeChanger": { "alias": "showSizeChanger"; "required": false; "isSignal": true; }; "showTotal": { "alias": "showTotal"; "required": false; "isSignal": true; }; "align": { "alias": "align"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "shape": { "alias": "shape"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "responsive": { "alias": "responsive"; "required": false; "isSignal": true; }; "ofLabel": { "alias": "ofLabel"; "required": false; "isSignal": true; }; "prevLabel": { "alias": "prevLabel"; "required": false; "isSignal": true; }; "nextLabel": { "alias": "nextLabel"; "required": false; "isSignal": true; }; "itemsPerPageLabel": { "alias": "itemsPerPageLabel"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; }, { "currentPage": "currentPageChange"; "pageSize": "pageSizeChange"; }, never, never, true, never>;
|
|
116
129
|
}
|
|
117
130
|
|
|
118
131
|
export { WrPagination };
|
|
@@ -43,6 +43,7 @@ declare class WrPopconfirm {
|
|
|
43
43
|
readonly cancelled: _angular_core.OutputEmitterRef<void>;
|
|
44
44
|
private readonly host;
|
|
45
45
|
private readonly overlay;
|
|
46
|
+
private readonly outsideClick;
|
|
46
47
|
private readonly vcr;
|
|
47
48
|
private readonly scrollStrategies;
|
|
48
49
|
private readonly destroyRef;
|
package/types/ngwr-popover.d.ts
CHANGED
|
@@ -51,6 +51,7 @@ declare class WrPopover {
|
|
|
51
51
|
readonly closed: _angular_core.OutputEmitterRef<void>;
|
|
52
52
|
private readonly host;
|
|
53
53
|
private readonly overlay;
|
|
54
|
+
private readonly outsideClick;
|
|
54
55
|
private readonly responsiveConfig;
|
|
55
56
|
private readonly vcr;
|
|
56
57
|
private readonly scrollStrategies;
|
package/types/ngwr-select.d.ts
CHANGED
|
@@ -81,8 +81,18 @@ interface WrSelectContext {
|
|
|
81
81
|
* no filter applied — options treat that as "show me".
|
|
82
82
|
*/
|
|
83
83
|
readonly searchQuery: Signal<string>;
|
|
84
|
-
/** True only when `mode="search"`.
|
|
84
|
+
/** True only when `mode="search"`. */
|
|
85
85
|
readonly isSearch: Signal<boolean>;
|
|
86
|
+
/**
|
|
87
|
+
* True when a filter is active — `mode="search"` or `[searchable]`. Options
|
|
88
|
+
* gate self-hiding on this.
|
|
89
|
+
*/
|
|
90
|
+
readonly isSearchable: Signal<boolean>;
|
|
91
|
+
/**
|
|
92
|
+
* False when the option list is already query-scoped upstream (`[loader]` or
|
|
93
|
+
* `[serverSearch]`), in which case options must NOT self-hide.
|
|
94
|
+
*/
|
|
95
|
+
readonly clientFilter: Signal<boolean>;
|
|
86
96
|
/** Is the given option value currently selected? Handles both single and multi. */
|
|
87
97
|
isSelected(value: unknown): boolean;
|
|
88
98
|
/**
|
|
@@ -120,6 +130,11 @@ interface SelectedChip {
|
|
|
120
130
|
* - `'tag'` — free-text + chips with `[separators]` / `[validate]` /
|
|
121
131
|
* `[allowDuplicates]`. Replaces `<wr-chips-input>`.
|
|
122
132
|
*
|
|
133
|
+
* `[searchable]` is orthogonal to the mode — it adds the type-ahead filter to
|
|
134
|
+
* `single` / `multi` without changing the value shape, which is how you get a
|
|
135
|
+
* searchable multi-select. Server-side search hangs off the `(searchChange)` output
|
|
136
|
+
* plus `[options]` / `[loading]`.
|
|
137
|
+
*
|
|
123
138
|
* A signal-forms native control: it implements `FormValueControl<unknown>`, so
|
|
124
139
|
* `[formField]` binds straight to its `value` model — no
|
|
125
140
|
* `ControlValueAccessor` in between. `[(value)]` works standalone, and classic
|
|
@@ -139,6 +154,16 @@ interface SelectedChip {
|
|
|
139
154
|
* <wr-option value="ts">TypeScript</wr-option>
|
|
140
155
|
* <wr-option value="ng">Angular</wr-option>
|
|
141
156
|
* </wr-select>
|
|
157
|
+
*
|
|
158
|
+
* <!-- Multi + type-ahead, options from a store -->
|
|
159
|
+
* <wr-select
|
|
160
|
+
* mode="multi"
|
|
161
|
+
* searchable
|
|
162
|
+
* [options]="results()"
|
|
163
|
+
* [loading]="pending()"
|
|
164
|
+
* [(value)]="categories"
|
|
165
|
+
* (searchChange)="store.search($event)"
|
|
166
|
+
* />
|
|
142
167
|
* ```
|
|
143
168
|
*
|
|
144
169
|
* The legacy `[multi]="true"` boolean is still accepted but is now an
|
|
@@ -192,15 +217,44 @@ declare class WrSelect implements FormValueControl<unknown>, WrSelectContext {
|
|
|
192
217
|
protected readonly isTag: _angular_core.Signal<boolean>;
|
|
193
218
|
/** Convenience — `effectiveMode() === 'search'`. Exposed via context for options. */
|
|
194
219
|
readonly isSearch: _angular_core.Signal<boolean>;
|
|
220
|
+
/**
|
|
221
|
+
* Add a type-ahead filter without changing the value shape — the missing
|
|
222
|
+
* multi-with-search combination, since `[mode]` picks exactly one shape:
|
|
223
|
+
*
|
|
224
|
+
* ```html
|
|
225
|
+
* <wr-select mode="multi" searchable [(value)]="categories">…</wr-select>
|
|
226
|
+
* ```
|
|
227
|
+
*
|
|
228
|
+
* On `multi` the trigger keeps its chips and grows an inline text field (the
|
|
229
|
+
* shape `tag` mode already uses); on `single` it is equivalent to
|
|
230
|
+
* `mode="search"`. Every search input (`[options]`, `[loader]`,
|
|
231
|
+
* `[debounceMs]`, `[minChars]`, `[virtualScroll]`, the `(searchChange)` output, …)
|
|
232
|
+
* applies either way. Ignored in `tag` mode, which owns its own input.
|
|
233
|
+
* @default false
|
|
234
|
+
*/
|
|
235
|
+
readonly searchable: _angular_core.InputSignalWithTransform<boolean, any>;
|
|
236
|
+
/**
|
|
237
|
+
* Filtering is active — either `mode="search"` or `[searchable]`. Exposed via
|
|
238
|
+
* context so each `<wr-option>` can self-hide non-matching rows.
|
|
239
|
+
*/
|
|
240
|
+
readonly isSearchable: _angular_core.Signal<boolean>;
|
|
241
|
+
/** The trigger *is* the search input: `mode="search"`, or a searchable single. */
|
|
242
|
+
protected readonly isSearchTrigger: _angular_core.Signal<boolean>;
|
|
243
|
+
/** Chips plus an inline search field — a searchable multi-select. */
|
|
244
|
+
protected readonly hasChipSearch: _angular_core.Signal<boolean>;
|
|
195
245
|
/** WrSelectContext — resolved multi-selection flag for child options. */
|
|
196
246
|
readonly multi: _angular_core.Signal<boolean>;
|
|
197
247
|
/** Both multi and tag render chips on the trigger. */
|
|
198
248
|
protected readonly hasChips: _angular_core.Signal<boolean>;
|
|
199
249
|
/**
|
|
200
|
-
*
|
|
250
|
+
* The live search query. Empty string = no filter. Exposed via context so
|
|
201
251
|
* each `<wr-option>` can self-hide non-matching rows.
|
|
252
|
+
*
|
|
253
|
+
* Two-way bindable, so the query can be owned or reset from outside:
|
|
254
|
+
* `[(searchQuery)]="query"`. For server-side search prefer the debounced
|
|
255
|
+
* {@link searchChange} output — `searchQueryChange` fires on every keystroke.
|
|
202
256
|
*/
|
|
203
|
-
readonly searchQuery: _angular_core.
|
|
257
|
+
readonly searchQuery: _angular_core.ModelSignal<string>;
|
|
204
258
|
/**
|
|
205
259
|
* Search mode: dynamic option array. Each item is rendered as a
|
|
206
260
|
* `<wr-option>` whose label comes from `[displayWith]`. Works
|
|
@@ -216,6 +270,21 @@ declare class WrSelect implements FormValueControl<unknown>, WrSelectContext {
|
|
|
216
270
|
* Observables, Promises, and plain arrays.
|
|
217
271
|
*/
|
|
218
272
|
readonly loader: _angular_core.InputSignal<WrSelectSearchLoader<unknown> | null>;
|
|
273
|
+
/**
|
|
274
|
+
* The option list is already scoped to the query upstream — skip the built-in
|
|
275
|
+
* client-side label filter. Set it when `[options]` is fed from a server via
|
|
276
|
+
* the {@link searchChange} output; the async `[loader]` path implies it.
|
|
277
|
+
*
|
|
278
|
+
* Without this, a server that ranks or fuzzy-matches (returning rows whose
|
|
279
|
+
* labels don't literally contain the query) would have those rows hidden
|
|
280
|
+
* again on the client. @default false
|
|
281
|
+
*/
|
|
282
|
+
readonly serverSearch: _angular_core.InputSignalWithTransform<boolean, any>;
|
|
283
|
+
/**
|
|
284
|
+
* Whether the built-in label filter runs. Exposed via context so each
|
|
285
|
+
* `<wr-option>` knows whether to self-hide. @internal
|
|
286
|
+
*/
|
|
287
|
+
readonly clientFilter: _angular_core.Signal<boolean>;
|
|
219
288
|
/** Search mode: debounce (ms) applied to the loader. @default 250 */
|
|
220
289
|
readonly debounceMs: _angular_core.InputSignalWithTransform<number, unknown>;
|
|
221
290
|
/** Search mode: minimum query length before the panel opens / loader fires. @default 0 */
|
|
@@ -253,10 +322,42 @@ declare class WrSelect implements FormValueControl<unknown>, WrSelectContext {
|
|
|
253
322
|
readonly loadingText: _angular_core.InputSignal<string | null>;
|
|
254
323
|
protected readonly resolvedNoResults: _angular_core.Signal<string>;
|
|
255
324
|
protected readonly resolvedLoading: _angular_core.Signal<string>;
|
|
325
|
+
/**
|
|
326
|
+
* Debounced search query, gated to searchable selects. Emits on every settled
|
|
327
|
+
* change — including the empty string when the field is cleared — so a
|
|
328
|
+
* store-backed option list can stay in sync and reset itself. `[debounceMs]`
|
|
329
|
+
* controls the delay; `[minChars]` does NOT gate it, for the same reason the
|
|
330
|
+
* `[loader]` path clears its results below that threshold.
|
|
331
|
+
*
|
|
332
|
+
* This is the hook for server-side search: dispatch on `(searchChange)`, feed
|
|
333
|
+
* the results back through `[options]` (plus `[serverSearch]`), and drive the
|
|
334
|
+
* panel's progress row with `[loading]`. Prefer it over the model's raw
|
|
335
|
+
* `(searchQueryChange)`, which fires on every keystroke.
|
|
336
|
+
*
|
|
337
|
+
* @example
|
|
338
|
+
* ```html
|
|
339
|
+
* <wr-select
|
|
340
|
+
* mode="search"
|
|
341
|
+
* serverSearch
|
|
342
|
+
* [options]="results()"
|
|
343
|
+
* [loading]="pending()"
|
|
344
|
+
* (searchChange)="store.search($event)"
|
|
345
|
+
* />
|
|
346
|
+
* ```
|
|
347
|
+
*/
|
|
348
|
+
readonly searchChange: _angular_core.OutputEmitterRef<string>;
|
|
349
|
+
/**
|
|
350
|
+
* Show the panel's progress row. Independent of `[loader]`, which raises and
|
|
351
|
+
* lowers its own flag — use this when the options come from a store fed by
|
|
352
|
+
* the {@link searchChange} output. @default false
|
|
353
|
+
*/
|
|
354
|
+
readonly loading: _angular_core.InputSignalWithTransform<boolean, any>;
|
|
256
355
|
/** Search mode: results returned by the async loader. @internal */
|
|
257
356
|
protected readonly loadedOptions: _angular_core.WritableSignal<readonly unknown[]>;
|
|
258
|
-
/**
|
|
259
|
-
|
|
357
|
+
/** True while the async `[loader]` is in flight. @internal */
|
|
358
|
+
private readonly loaderLoading;
|
|
359
|
+
/** Either flag shows the panel's progress row. @internal */
|
|
360
|
+
protected readonly isLoading: _angular_core.Signal<boolean>;
|
|
260
361
|
/**
|
|
261
362
|
* Tag mode: keys / characters that commit the current draft into a chip.
|
|
262
363
|
* `'Enter'` is the key name; everything else is a literal character watched
|
|
@@ -331,7 +432,7 @@ declare class WrSelect implements FormValueControl<unknown>, WrSelectContext {
|
|
|
331
432
|
* `dynamicOptions()` narrowed to the current search query. `dynamicOptions()`
|
|
332
433
|
* is intentionally UNFILTERED (projected `<wr-option>` self-hide via CSS), so
|
|
333
434
|
* the virtual path — which renders plain rows that can't self-hide — filters
|
|
334
|
-
* here. Loader results are already query-scoped upstream.
|
|
435
|
+
* here. Loader / `serverSearch` results are already query-scoped upstream.
|
|
335
436
|
*/
|
|
336
437
|
protected readonly filteredDynamicOptions: _angular_core.Signal<readonly unknown[]>;
|
|
337
438
|
/**
|
|
@@ -376,12 +477,17 @@ declare class WrSelect implements FormValueControl<unknown>, WrSelectContext {
|
|
|
376
477
|
/** Search mode: is this option hidden by the current query? @internal */
|
|
377
478
|
private isOptionHidden;
|
|
378
479
|
protected readonly panelTpl: _angular_core.Signal<TemplateRef<any>>;
|
|
379
|
-
/**
|
|
480
|
+
/** The inline input inside a chip trigger — tag mode or a searchable multi. @internal */
|
|
380
481
|
protected readonly tagInputEl: _angular_core.Signal<ElementRef<HTMLInputElement> | undefined>;
|
|
381
482
|
/** Search-mode inline input. Present only when `mode="search"`. @internal */
|
|
382
483
|
protected readonly searchInputEl: _angular_core.Signal<ElementRef<HTMLInputElement> | undefined>;
|
|
383
484
|
protected focusTagInput(): void;
|
|
384
485
|
protected focusSearchInput(): void;
|
|
486
|
+
protected onChipsInput(event: Event): void;
|
|
487
|
+
protected onChipsKeydown(event: KeyboardEvent): void;
|
|
488
|
+
protected onChipsFocus(): void;
|
|
489
|
+
protected onChipsPaste(event: ClipboardEvent): void;
|
|
490
|
+
protected onChipsBlur(): void;
|
|
385
491
|
protected onSearchInput(event: Event): void;
|
|
386
492
|
protected onSearchFocus(): void;
|
|
387
493
|
protected onSearchKey(event: KeyboardEvent): void;
|
|
@@ -393,6 +499,7 @@ declare class WrSelect implements FormValueControl<unknown>, WrSelectContext {
|
|
|
393
499
|
protected readonly searchDisplay: _angular_core.Signal<string>;
|
|
394
500
|
private readonly host;
|
|
395
501
|
private readonly overlay;
|
|
502
|
+
private readonly outsideClick;
|
|
396
503
|
private readonly responsiveConfig;
|
|
397
504
|
private readonly vcr;
|
|
398
505
|
private readonly scrollStrategies;
|
|
@@ -426,7 +533,7 @@ declare class WrSelect implements FormValueControl<unknown>, WrSelectContext {
|
|
|
426
533
|
private openOverlay;
|
|
427
534
|
private closeOverlay;
|
|
428
535
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<WrSelect, never>;
|
|
429
|
-
static ɵcmp: _angular_core.ɵɵComponentDeclaration<WrSelect, "wr-select", never, { "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "clearLabel": { "alias": "clearLabel"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "rounded": { "alias": "rounded"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "responsive": { "alias": "responsive"; "required": false; "isSignal": true; }; "mode": { "alias": "mode"; "required": false; "isSignal": true; }; "options": { "alias": "options"; "required": false; "isSignal": true; }; "displayWith": { "alias": "displayWith"; "required": false; "isSignal": true; }; "loader": { "alias": "loader"; "required": false; "isSignal": true; }; "debounceMs": { "alias": "debounceMs"; "required": false; "isSignal": true; }; "minChars": { "alias": "minChars"; "required": false; "isSignal": true; }; "freeText": { "alias": "freeText"; "required": false; "isSignal": true; }; "virtualScroll": { "alias": "virtualScroll"; "required": false; "isSignal": true; }; "rowHeight": { "alias": "rowHeight"; "required": false; "isSignal": true; }; "viewportHeight": { "alias": "viewportHeight"; "required": false; "isSignal": true; }; "overscan": { "alias": "overscan"; "required": false; "isSignal": true; }; "noResultsText": { "alias": "noResultsText"; "required": false; "isSignal": true; }; "loadingText": { "alias": "loadingText"; "required": false; "isSignal": true; }; "separators": { "alias": "separators"; "required": false; "isSignal": true; }; "allowDuplicates": { "alias": "allowDuplicates"; "required": false; "isSignal": true; }; "validate": { "alias": "validate"; "required": false; "isSignal": true; }; "clearable": { "alias": "clearable"; "required": false; "isSignal": true; }; "maxItems": { "alias": "maxItems"; "required": false; "isSignal": true; }; "maxTagCount": { "alias": "maxTagCount"; "required": false; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "touch": "touch"; }, ["projectedOptions"], ["*"], true, never>;
|
|
536
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<WrSelect, "wr-select", never, { "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "clearLabel": { "alias": "clearLabel"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "rounded": { "alias": "rounded"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "responsive": { "alias": "responsive"; "required": false; "isSignal": true; }; "mode": { "alias": "mode"; "required": false; "isSignal": true; }; "searchable": { "alias": "searchable"; "required": false; "isSignal": true; }; "searchQuery": { "alias": "searchQuery"; "required": false; "isSignal": true; }; "options": { "alias": "options"; "required": false; "isSignal": true; }; "displayWith": { "alias": "displayWith"; "required": false; "isSignal": true; }; "loader": { "alias": "loader"; "required": false; "isSignal": true; }; "serverSearch": { "alias": "serverSearch"; "required": false; "isSignal": true; }; "debounceMs": { "alias": "debounceMs"; "required": false; "isSignal": true; }; "minChars": { "alias": "minChars"; "required": false; "isSignal": true; }; "freeText": { "alias": "freeText"; "required": false; "isSignal": true; }; "virtualScroll": { "alias": "virtualScroll"; "required": false; "isSignal": true; }; "rowHeight": { "alias": "rowHeight"; "required": false; "isSignal": true; }; "viewportHeight": { "alias": "viewportHeight"; "required": false; "isSignal": true; }; "overscan": { "alias": "overscan"; "required": false; "isSignal": true; }; "noResultsText": { "alias": "noResultsText"; "required": false; "isSignal": true; }; "loadingText": { "alias": "loadingText"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; "separators": { "alias": "separators"; "required": false; "isSignal": true; }; "allowDuplicates": { "alias": "allowDuplicates"; "required": false; "isSignal": true; }; "validate": { "alias": "validate"; "required": false; "isSignal": true; }; "clearable": { "alias": "clearable"; "required": false; "isSignal": true; }; "maxItems": { "alias": "maxItems"; "required": false; "isSignal": true; }; "maxTagCount": { "alias": "maxTagCount"; "required": false; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; }, { "searchQuery": "searchQueryChange"; "searchChange": "searchChange"; "value": "valueChange"; "touch": "touch"; }, ["projectedOptions"], ["*"], true, never>;
|
|
430
537
|
}
|
|
431
538
|
|
|
432
539
|
/**
|
|
@@ -458,7 +565,7 @@ declare class WrOption {
|
|
|
458
565
|
/** @internal — true when this option is the keyboard cursor target. */
|
|
459
566
|
protected readonly active: _angular_core.Signal<boolean>;
|
|
460
567
|
/**
|
|
461
|
-
* @internal —
|
|
568
|
+
* @internal — searchable selects only. True when the parent has a query that
|
|
462
569
|
* the option's text content does not match (case-insensitive substring).
|
|
463
570
|
* Hidden options stay in the DOM so registration order survives but
|
|
464
571
|
* collapse via CSS.
|
|
@@ -485,6 +592,11 @@ declare class WrOption {
|
|
|
485
592
|
declare class WrOptionGroup {
|
|
486
593
|
/** Section heading shown above the options. */
|
|
487
594
|
readonly label: _angular_core.InputSignal<string>;
|
|
595
|
+
/**
|
|
596
|
+
* `role="group"` alone announces an unnamed group — the visible heading has to
|
|
597
|
+
* be wired to it for the name to reach a screen reader.
|
|
598
|
+
*/
|
|
599
|
+
protected readonly labelId: string;
|
|
488
600
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<WrOptionGroup, never>;
|
|
489
601
|
static ɵcmp: _angular_core.ɵɵComponentDeclaration<WrOptionGroup, "wr-option-group", never, { "label": { "alias": "label"; "required": true; "isSignal": true; }; }, {}, never, ["*"], true, never>;
|
|
490
602
|
}
|
package/types/ngwr-theme.d.ts
CHANGED
|
@@ -116,7 +116,14 @@ declare class WrTheme {
|
|
|
116
116
|
*/
|
|
117
117
|
|
|
118
118
|
/**
|
|
119
|
-
* Configure {@link WrTheme}.
|
|
119
|
+
* Configure and activate {@link WrTheme}. Config is partial — merged with
|
|
120
|
+
* defaults.
|
|
121
|
+
*
|
|
122
|
+
* Calling this is all it takes: the initializer instantiates {@link WrTheme},
|
|
123
|
+
* whose constructor effect mirrors the resolved theme onto `<html>`. Without
|
|
124
|
+
* that, the service (a tree-shakable `@Service()`) would only be created once
|
|
125
|
+
* something injected it, so `[data-theme]` was never written and an app that
|
|
126
|
+
* followed the docs silently ignored `defaultMode: 'auto'`.
|
|
120
127
|
*
|
|
121
128
|
* @example
|
|
122
129
|
* ```ts
|
package/types/ngwr-tree.d.ts
CHANGED
|
@@ -138,6 +138,7 @@ declare class WrTree<TId = string> implements FormValueControl<unknown> {
|
|
|
138
138
|
readonly touch: _angular_core.OutputEmitterRef<void>;
|
|
139
139
|
private readonly host;
|
|
140
140
|
private readonly overlay;
|
|
141
|
+
private readonly outsideClick;
|
|
141
142
|
private readonly vcr;
|
|
142
143
|
private readonly scrollStrategies;
|
|
143
144
|
private readonly destroyRef;
|
package/types/ngwr-version.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ import { InjectionToken } from '@angular/core';
|
|
|
16
16
|
* Distinct from `Version` exported by `@angular/core`, which reports the
|
|
17
17
|
* loaded Angular runtime version (e.g. `'21.2.13'`), not ngwr's.
|
|
18
18
|
*/
|
|
19
|
-
declare const NGWR_VERSION = "
|
|
19
|
+
declare const NGWR_VERSION = "10.0.0";
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
22
|
* @license
|
package/types/ngwr-waves.d.ts
CHANGED
|
@@ -53,6 +53,16 @@ declare class WrWaves {
|
|
|
53
53
|
private bounding;
|
|
54
54
|
private readonly mouse;
|
|
55
55
|
private rafId;
|
|
56
|
+
/**
|
|
57
|
+
* Flips once the canvas has drawn, which retires the CSS stand-in grid.
|
|
58
|
+
*
|
|
59
|
+
* A canvas cannot be prerendered, so on an SSR'd or prerendered page this
|
|
60
|
+
* element is simply blank until the bundle has loaded and hydration has run —
|
|
61
|
+
* measured at ~2.8s on ngwr.dev's own home page. The stylesheet paints a static
|
|
62
|
+
* grid of the same colour and pitch in the meantime, straight from the
|
|
63
|
+
* prerendered HTML, and this hands over to the real thing.
|
|
64
|
+
*/
|
|
65
|
+
protected readonly painted: _angular_core.WritableSignal<boolean>;
|
|
56
66
|
constructor();
|
|
57
67
|
private boot;
|
|
58
68
|
private setSize;
|
package/waves/styles/_index.scss
CHANGED
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
.wr-waves {
|
|
6
6
|
--wr-waves-line-color: rgba(var(--wr-color-on-surface-rgb), 0.12);
|
|
7
|
+
// Mirrors the `xGap` input; the component publishes it so the stand-in below
|
|
8
|
+
// lands on the same pitch as the real grid.
|
|
9
|
+
--wr-waves-x-gap: 10px;
|
|
7
10
|
|
|
8
11
|
position: absolute;
|
|
9
12
|
inset: 0;
|
|
@@ -12,6 +15,24 @@
|
|
|
12
15
|
padding: 0;
|
|
13
16
|
overflow: hidden;
|
|
14
17
|
|
|
18
|
+
// Stand-in grid, shown until the canvas has painted.
|
|
19
|
+
//
|
|
20
|
+
// A canvas cannot be prerendered, so on an SSR'd or prerendered page this
|
|
21
|
+
// element is blank until the JS bundle has loaded and hydrated — measured at
|
|
22
|
+
// ~2.8s on ngwr.dev's own home page, where it reads as the background hanging
|
|
23
|
+
// and then popping in. These are straight lines rather than the drifting
|
|
24
|
+
// curves, at the same colour and pitch, so the handover reads as the grid
|
|
25
|
+
// coming alive rather than as content appearing.
|
|
26
|
+
background-image: repeating-linear-gradient(
|
|
27
|
+
to right,
|
|
28
|
+
var(--wr-waves-line-color) 0 1px,
|
|
29
|
+
transparent 1px var(--wr-waves-x-gap)
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
&--painted {
|
|
33
|
+
background-image: none;
|
|
34
|
+
}
|
|
35
|
+
|
|
15
36
|
&__canvas {
|
|
16
37
|
display: block;
|
|
17
38
|
width: 100%;
|