ngwr 15.2.0 → 15.4.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/alert/styles/_index.scss +13 -5
- package/bits/border-glow/styles/_rules.scss +60 -49
- package/bits/click-spark/click-spark.scss +5 -1
- package/bits/click-spark/styles/_index.scss +10 -15
- package/bits/click-spark/styles/_rules.scss +20 -0
- package/fesm2022/ngwr-alert.mjs +2 -2
- package/fesm2022/ngwr-alert.mjs.map +1 -1
- package/fesm2022/ngwr-bits-aurora.mjs +108 -52
- package/fesm2022/ngwr-bits-aurora.mjs.map +1 -1
- package/fesm2022/ngwr-bits-border-glow-testing.mjs +8 -8
- package/fesm2022/ngwr-bits-border-glow-testing.mjs.map +1 -1
- package/fesm2022/ngwr-bits-border-glow.mjs +29 -26
- package/fesm2022/ngwr-bits-border-glow.mjs.map +1 -1
- package/fesm2022/ngwr-bits-dot-grid.mjs +11 -14
- package/fesm2022/ngwr-bits-dot-grid.mjs.map +1 -1
- package/fesm2022/ngwr-bits-fuzzy-text.mjs +18 -4
- package/fesm2022/ngwr-bits-fuzzy-text.mjs.map +1 -1
- package/fesm2022/ngwr-bits-ripple-grid.mjs +14 -13
- package/fesm2022/ngwr-bits-ripple-grid.mjs.map +1 -1
- package/fesm2022/ngwr-bits-splash-cursor.mjs +21 -10
- package/fesm2022/ngwr-bits-splash-cursor.mjs.map +1 -1
- package/fesm2022/ngwr-bits-waves.mjs +10 -15
- package/fesm2022/ngwr-bits-waves.mjs.map +1 -1
- package/fesm2022/ngwr-breadcrumbs.mjs +18 -2
- package/fesm2022/ngwr-breadcrumbs.mjs.map +1 -1
- package/fesm2022/ngwr-hotkey.mjs +23 -1
- package/fesm2022/ngwr-hotkey.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-ar.mjs +2 -0
- package/fesm2022/ngwr-i18n-ar.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-cs.mjs +2 -0
- package/fesm2022/ngwr-i18n-cs.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-de.mjs +2 -0
- package/fesm2022/ngwr-i18n-de.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-en.mjs +2 -0
- package/fesm2022/ngwr-i18n-en.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-es.mjs +2 -0
- package/fesm2022/ngwr-i18n-es.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-fr.mjs +2 -0
- package/fesm2022/ngwr-i18n-fr.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-he.mjs +2 -0
- package/fesm2022/ngwr-i18n-he.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-hi.mjs +2 -0
- package/fesm2022/ngwr-i18n-hi.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-id.mjs +2 -0
- package/fesm2022/ngwr-i18n-id.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-it.mjs +2 -0
- package/fesm2022/ngwr-i18n-it.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-ja.mjs +2 -0
- package/fesm2022/ngwr-i18n-ja.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-ko.mjs +2 -0
- package/fesm2022/ngwr-i18n-ko.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-nl.mjs +2 -0
- package/fesm2022/ngwr-i18n-nl.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-pl.mjs +2 -0
- package/fesm2022/ngwr-i18n-pl.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-pt.mjs +2 -0
- package/fesm2022/ngwr-i18n-pt.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-ru.mjs +2 -0
- package/fesm2022/ngwr-i18n-ru.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-sv.mjs +2 -0
- package/fesm2022/ngwr-i18n-sv.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-tr.mjs +2 -0
- package/fesm2022/ngwr-i18n-tr.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-uk.mjs +2 -0
- package/fesm2022/ngwr-i18n-uk.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-vi.mjs +2 -0
- package/fesm2022/ngwr-i18n-vi.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-zh-tw.mjs +2 -0
- package/fesm2022/ngwr-i18n-zh-tw.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-zh.mjs +2 -0
- package/fesm2022/ngwr-i18n-zh.mjs.map +1 -1
- package/fesm2022/ngwr-pagination.mjs +3 -0
- package/fesm2022/ngwr-pagination.mjs.map +1 -1
- package/fesm2022/ngwr-pipes.mjs +163 -49
- package/fesm2022/ngwr-pipes.mjs.map +1 -1
- package/fesm2022/ngwr-schema-form.mjs +3 -0
- package/fesm2022/ngwr-schema-form.mjs.map +1 -1
- package/fesm2022/ngwr-select-testing.mjs +16 -0
- package/fesm2022/ngwr-select-testing.mjs.map +1 -1
- package/fesm2022/ngwr-select.mjs +218 -38
- package/fesm2022/ngwr-select.mjs.map +1 -1
- package/fesm2022/ngwr-statistic.mjs +20 -21
- package/fesm2022/ngwr-statistic.mjs.map +1 -1
- package/fesm2022/ngwr-version.mjs +1 -1
- package/fesm2022/ngwr-version.mjs.map +1 -1
- package/i18n/ar.json +2 -0
- package/i18n/cs.json +2 -0
- package/i18n/de.json +2 -0
- package/i18n/en.json +2 -0
- package/i18n/es.json +2 -0
- package/i18n/fr.json +2 -0
- package/i18n/he.json +2 -0
- package/i18n/hi.json +2 -0
- package/i18n/id.json +2 -0
- package/i18n/it.json +2 -0
- package/i18n/ja.json +2 -0
- package/i18n/ko.json +2 -0
- package/i18n/nl.json +2 -0
- package/i18n/pl.json +2 -0
- package/i18n/pt.json +2 -0
- package/i18n/ru.json +2 -0
- package/i18n/sv.json +2 -0
- package/i18n/tr.json +2 -0
- package/i18n/uk.json +2 -0
- package/i18n/vi.json +2 -0
- package/i18n/zh-TW.json +2 -0
- package/i18n/zh.json +2 -0
- package/llms-full.txt +5 -5
- package/package.json +1 -1
- package/schematics/use/symbol-map.json +2 -0
- package/select/styles/_index.scss +23 -1
- package/skills/ngwr/SKILL.md +3 -1
- package/skills/ngwr/references/catalog.md +1 -1
- package/types/ngwr-alert.d.ts +3 -1
- package/types/ngwr-bits-aurora.d.ts +24 -2
- package/types/ngwr-bits-border-glow-testing.d.ts +9 -5
- package/types/ngwr-bits-border-glow.d.ts +10 -4
- package/types/ngwr-bits-fuzzy-text.d.ts +2 -1
- package/types/ngwr-bits-ripple-grid.d.ts +7 -2
- package/types/ngwr-bits-splash-cursor.d.ts +2 -1
- package/types/ngwr-breadcrumbs.d.ts +15 -0
- package/types/ngwr-hotkey.d.ts +14 -2
- package/types/ngwr-pipes.d.ts +142 -32
- package/types/ngwr-select-testing.d.ts +9 -0
- package/types/ngwr-select.d.ts +133 -2
- package/types/ngwr-sidebar.d.ts +1 -1
- package/types/ngwr-version.d.ts +1 -1
package/alert/styles/_index.scss
CHANGED
|
@@ -108,7 +108,10 @@ $alert-colors: (
|
|
|
108
108
|
line-height: var(--wr-alert-title-line-height);
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
|
|
111
|
+
// Projected content reads as the message does. Hidden while empty, so an alert
|
|
112
|
+
// with nothing inside keeps the body's gap to what it had.
|
|
113
|
+
&__message,
|
|
114
|
+
&__content {
|
|
112
115
|
margin: 0;
|
|
113
116
|
color: var(--wr-alert-message);
|
|
114
117
|
font-family: var(--wr-alert-font-family);
|
|
@@ -116,6 +119,10 @@ $alert-colors: (
|
|
|
116
119
|
line-height: var(--wr-alert-message-line-height);
|
|
117
120
|
}
|
|
118
121
|
|
|
122
|
+
&__content:empty {
|
|
123
|
+
display: none;
|
|
124
|
+
}
|
|
125
|
+
|
|
119
126
|
&__close {
|
|
120
127
|
flex: 0 0 auto;
|
|
121
128
|
appearance: none;
|
|
@@ -136,11 +143,12 @@ $alert-colors: (
|
|
|
136
143
|
// `font: inherit`. Inheriting the size makes `1em` the alert's own text in
|
|
137
144
|
// every app. `--wr-icon-size` cannot do this job — only a `<wr-icon>` host
|
|
138
145
|
// reads it, and the rule here used to set it for nothing. The SIZE only,
|
|
139
|
-
// never the `font` shorthand, which would bring the line-height with it
|
|
140
|
-
// no `line-height` of its own either, the way `wr-dialog` needs one — the
|
|
141
|
-
// alert stretches its row, so this box is the alert's height whatever the
|
|
142
|
-
// strut says.
|
|
146
|
+
// never the `font` shorthand, which would bring the line-height with it.
|
|
143
147
|
font-size: inherit;
|
|
148
|
+
// Top of the row, by the title, rather than stretched to the alert's height:
|
|
149
|
+
// with projected content under the message, a stretched button put the ✕
|
|
150
|
+
// halfway down the alert and painted its hover tint as a full-height strip.
|
|
151
|
+
align-self: flex-start;
|
|
144
152
|
|
|
145
153
|
@include theme.touch-target; // glyph -> 44px tap area on touch
|
|
146
154
|
|
|
@@ -2,12 +2,37 @@
|
|
|
2
2
|
// All visual state is driven by two custom properties the component writes:
|
|
3
3
|
// --wr-border-glow-edge-proximity 0..100, scales with pointer-to-edge distance
|
|
4
4
|
// --wr-border-glow-angle 0deg..360deg, pointer-from-centre angle
|
|
5
|
-
// Plus a colour/geometry bag set once from inputs (see border-glow.ts).
|
|
5
|
+
// Plus a colour/geometry bag set once from inputs (see border-glow.ts). The three
|
|
6
|
+
// colour inputs write an `--wr-border-glow-input-*` twin of the hook they stand
|
|
7
|
+
// in for, never the hook itself, and every read below prefers the twin.
|
|
6
8
|
|
|
7
9
|
.wr-border-glow {
|
|
8
10
|
--wr-border-glow-edge-proximity: 0;
|
|
9
11
|
--wr-border-glow-angle: 45deg;
|
|
10
|
-
//
|
|
12
|
+
// The card fill: the theme surface, unless an app sets this or `[backgroundColor]`.
|
|
13
|
+
--wr-border-glow-bg: var(--wr-color-surface);
|
|
14
|
+
// What `[backgroundColor]`, `[glowColor]` and `[colors]` write on the host, which
|
|
15
|
+
// the inline value beats. Unset here so a card projected inside another one does
|
|
16
|
+
// not inherit the outer card's inputs — and twins rather than the hooks' own
|
|
17
|
+
// names, so a `--wr-border-glow-bg` an author set on the host stays there and is
|
|
18
|
+
// read again the moment the input is unset.
|
|
19
|
+
--wr-border-glow-input-bg: initial;
|
|
20
|
+
--wr-border-glow-input-color: initial;
|
|
21
|
+
--wr-border-glow-input-color-60: initial;
|
|
22
|
+
--wr-border-glow-input-color-50: initial;
|
|
23
|
+
--wr-border-glow-input-color-40: initial;
|
|
24
|
+
--wr-border-glow-input-color-30: initial;
|
|
25
|
+
--wr-border-glow-input-color-20: initial;
|
|
26
|
+
--wr-border-glow-input-color-10: initial;
|
|
27
|
+
--wr-border-glow-input-gradient-one: initial;
|
|
28
|
+
--wr-border-glow-input-gradient-two: initial;
|
|
29
|
+
--wr-border-glow-input-gradient-three: initial;
|
|
30
|
+
--wr-border-glow-input-gradient-four: initial;
|
|
31
|
+
--wr-border-glow-input-gradient-five: initial;
|
|
32
|
+
--wr-border-glow-input-gradient-six: initial;
|
|
33
|
+
--wr-border-glow-input-gradient-seven: initial;
|
|
34
|
+
--wr-border-glow-input-gradient-base: initial;
|
|
35
|
+
// Theme-default halo (`[glowColor]` writes the `-input-` twins beside it): a deep
|
|
11
36
|
// amber that reads on white, the pale pastel on dark.
|
|
12
37
|
--wr-border-glow-color: light-dark(hsl(40deg 90% 45% / 100%), hsl(40deg 80% 80% / 100%));
|
|
13
38
|
--wr-border-glow-color-60: light-dark(hsl(40deg 90% 45% / 60%), hsl(40deg 80% 80% / 60%));
|
|
@@ -16,8 +41,8 @@
|
|
|
16
41
|
--wr-border-glow-color-30: light-dark(hsl(40deg 90% 45% / 30%), hsl(40deg 80% 80% / 30%));
|
|
17
42
|
--wr-border-glow-color-20: light-dark(hsl(40deg 90% 45% / 20%), hsl(40deg 80% 80% / 20%));
|
|
18
43
|
--wr-border-glow-color-10: light-dark(hsl(40deg 90% 45% / 10%), hsl(40deg 80% 80% / 10%));
|
|
19
|
-
// Theme-default mesh palette (
|
|
20
|
-
// tones that read on white, the reactbits pastels on dark. The geometry is
|
|
44
|
+
// Theme-default mesh palette (`[colors]` writes the `-input-` twins beside it):
|
|
45
|
+
// deep tones that read on white, the reactbits pastels on dark. The geometry is
|
|
21
46
|
// the same in both schemes, so only the one colour stop of each layer moves.
|
|
22
47
|
--wr-border-glow-gradient-one: radial-gradient(
|
|
23
48
|
at 80% 55%,
|
|
@@ -63,19 +88,19 @@
|
|
|
63
88
|
// so an inline `[glowColor]` reaches every layer of it.
|
|
64
89
|
/* stylelint-disable-next-line */
|
|
65
90
|
--wr-border-glow-edge-shadow:
|
|
66
|
-
inset 0 0 0 1px var(--wr-border-glow-color),
|
|
67
|
-
inset 0 0 1px 0 light-dark(transparent, var(--wr-border-glow-color-60)),
|
|
68
|
-
inset 0 0 3px 0 light-dark(transparent, var(--wr-border-glow-color-50)),
|
|
69
|
-
inset 0 0 6px 0 light-dark(transparent, var(--wr-border-glow-color-40)),
|
|
70
|
-
inset 0 0 15px 0 light-dark(transparent, var(--wr-border-glow-color-30)),
|
|
71
|
-
inset 0 0 25px 2px light-dark(transparent, var(--wr-border-glow-color-20)),
|
|
72
|
-
inset 0 0 50px 2px light-dark(transparent, var(--wr-border-glow-color-10)),
|
|
73
|
-
0 0 1px 0 var(--wr-border-glow-color-60),
|
|
74
|
-
0 0 3px 0 var(--wr-border-glow-color-50),
|
|
75
|
-
0 0 6px 0 var(--wr-border-glow-color-40),
|
|
76
|
-
0 0 15px 0 var(--wr-border-glow-color-30),
|
|
77
|
-
0 0 25px 2px var(--wr-border-glow-color-20),
|
|
78
|
-
0 0 50px 2px var(--wr-border-glow-color-10);
|
|
91
|
+
inset 0 0 0 1px var(--wr-border-glow-input-color, var(--wr-border-glow-color)),
|
|
92
|
+
inset 0 0 1px 0 light-dark(transparent, var(--wr-border-glow-input-color-60, var(--wr-border-glow-color-60))),
|
|
93
|
+
inset 0 0 3px 0 light-dark(transparent, var(--wr-border-glow-input-color-50, var(--wr-border-glow-color-50))),
|
|
94
|
+
inset 0 0 6px 0 light-dark(transparent, var(--wr-border-glow-input-color-40, var(--wr-border-glow-color-40))),
|
|
95
|
+
inset 0 0 15px 0 light-dark(transparent, var(--wr-border-glow-input-color-30, var(--wr-border-glow-color-30))),
|
|
96
|
+
inset 0 0 25px 2px light-dark(transparent, var(--wr-border-glow-input-color-20, var(--wr-border-glow-color-20))),
|
|
97
|
+
inset 0 0 50px 2px light-dark(transparent, var(--wr-border-glow-input-color-10, var(--wr-border-glow-color-10))),
|
|
98
|
+
0 0 1px 0 var(--wr-border-glow-input-color-60, var(--wr-border-glow-color-60)),
|
|
99
|
+
0 0 3px 0 var(--wr-border-glow-input-color-50, var(--wr-border-glow-color-50)),
|
|
100
|
+
0 0 6px 0 var(--wr-border-glow-input-color-40, var(--wr-border-glow-color-40)),
|
|
101
|
+
0 0 15px 0 var(--wr-border-glow-input-color-30, var(--wr-border-glow-color-30)),
|
|
102
|
+
0 0 25px 2px var(--wr-border-glow-input-color-20, var(--wr-border-glow-color-20)),
|
|
103
|
+
0 0 50px 2px var(--wr-border-glow-input-color-10, var(--wr-border-glow-color-10));
|
|
79
104
|
// Blend modes and a strength are not colours, so `light-dark()` cannot switch
|
|
80
105
|
// them, and each value below is the one that holds up in both schemes. The
|
|
81
106
|
// additive look dark used — `plus-lighter` on the halo, `soft-light` on the
|
|
@@ -100,7 +125,7 @@
|
|
|
100
125
|
position: relative;
|
|
101
126
|
display: grid;
|
|
102
127
|
border-radius: var(--wr-border-glow-radius);
|
|
103
|
-
background: var(--wr-border-glow-bg, var(--wr-
|
|
128
|
+
background: var(--wr-border-glow-input-bg, var(--wr-border-glow-bg));
|
|
104
129
|
overflow: visible;
|
|
105
130
|
isolation: isolate;
|
|
106
131
|
transform: translate3d(0, 0, 0.01px); // create a new layer to keep blends tidy
|
|
@@ -137,23 +162,16 @@
|
|
|
137
162
|
&::before {
|
|
138
163
|
border: 1px solid transparent;
|
|
139
164
|
background:
|
|
140
|
-
linear-gradient(var(--wr-border-glow-bg, var(--wr-
|
|
165
|
+
linear-gradient(var(--wr-border-glow-input-bg, var(--wr-border-glow-bg)) 0 100%) padding-box,
|
|
141
166
|
linear-gradient(rgb(255 255 255 / 0%) 0 100%) border-box,
|
|
142
|
-
var(--wr-border-glow-gradient-one,
|
|
143
|
-
|
|
144
|
-
var(--wr-border-glow-gradient-
|
|
145
|
-
|
|
146
|
-
var(--wr-border-glow-gradient-
|
|
147
|
-
|
|
148
|
-
var(--wr-border-glow-gradient-
|
|
149
|
-
|
|
150
|
-
var(--wr-border-glow-gradient-five, radial-gradient(at 86% 85%, hsl(186deg 100% 74%) 0px, transparent 50%))
|
|
151
|
-
border-box,
|
|
152
|
-
var(--wr-border-glow-gradient-six, radial-gradient(at 82% 18%, hsl(52deg 100% 65%) 0px, transparent 50%))
|
|
153
|
-
border-box,
|
|
154
|
-
var(--wr-border-glow-gradient-seven, radial-gradient(at 51% 4%, hsl(12deg 100% 72%) 0px, transparent 50%))
|
|
155
|
-
border-box,
|
|
156
|
-
var(--wr-border-glow-gradient-base, linear-gradient(#c299ff 0 100%)) border-box;
|
|
167
|
+
var(--wr-border-glow-input-gradient-one, var(--wr-border-glow-gradient-one)) border-box,
|
|
168
|
+
var(--wr-border-glow-input-gradient-two, var(--wr-border-glow-gradient-two)) border-box,
|
|
169
|
+
var(--wr-border-glow-input-gradient-three, var(--wr-border-glow-gradient-three)) border-box,
|
|
170
|
+
var(--wr-border-glow-input-gradient-four, var(--wr-border-glow-gradient-four)) border-box,
|
|
171
|
+
var(--wr-border-glow-input-gradient-five, var(--wr-border-glow-gradient-five)) border-box,
|
|
172
|
+
var(--wr-border-glow-input-gradient-six, var(--wr-border-glow-gradient-six)) border-box,
|
|
173
|
+
var(--wr-border-glow-input-gradient-seven, var(--wr-border-glow-gradient-seven)) border-box,
|
|
174
|
+
var(--wr-border-glow-input-gradient-base, var(--wr-border-glow-gradient-base)) border-box;
|
|
157
175
|
|
|
158
176
|
opacity: calc(
|
|
159
177
|
(var(--wr-border-glow-edge-proximity) - var(--wr-border-glow-color-sensitivity)) /
|
|
@@ -173,21 +191,14 @@
|
|
|
173
191
|
&::after {
|
|
174
192
|
border: 1px solid transparent;
|
|
175
193
|
background:
|
|
176
|
-
var(--wr-border-glow-gradient-one,
|
|
177
|
-
|
|
178
|
-
var(--wr-border-glow-gradient-
|
|
179
|
-
|
|
180
|
-
var(--wr-border-glow-gradient-
|
|
181
|
-
|
|
182
|
-
var(--wr-border-glow-gradient-
|
|
183
|
-
|
|
184
|
-
var(--wr-border-glow-gradient-five, radial-gradient(at 86% 85%, hsl(186deg 100% 74%) 0px, transparent 50%))
|
|
185
|
-
padding-box,
|
|
186
|
-
var(--wr-border-glow-gradient-six, radial-gradient(at 82% 18%, hsl(52deg 100% 65%) 0px, transparent 50%))
|
|
187
|
-
padding-box,
|
|
188
|
-
var(--wr-border-glow-gradient-seven, radial-gradient(at 51% 4%, hsl(12deg 100% 72%) 0px, transparent 50%))
|
|
189
|
-
padding-box,
|
|
190
|
-
var(--wr-border-glow-gradient-base, linear-gradient(#c299ff 0 100%)) padding-box;
|
|
194
|
+
var(--wr-border-glow-input-gradient-one, var(--wr-border-glow-gradient-one)) padding-box,
|
|
195
|
+
var(--wr-border-glow-input-gradient-two, var(--wr-border-glow-gradient-two)) padding-box,
|
|
196
|
+
var(--wr-border-glow-input-gradient-three, var(--wr-border-glow-gradient-three)) padding-box,
|
|
197
|
+
var(--wr-border-glow-input-gradient-four, var(--wr-border-glow-gradient-four)) padding-box,
|
|
198
|
+
var(--wr-border-glow-input-gradient-five, var(--wr-border-glow-gradient-five)) padding-box,
|
|
199
|
+
var(--wr-border-glow-input-gradient-six, var(--wr-border-glow-gradient-six)) padding-box,
|
|
200
|
+
var(--wr-border-glow-input-gradient-seven, var(--wr-border-glow-gradient-seven)) padding-box,
|
|
201
|
+
var(--wr-border-glow-input-gradient-base, var(--wr-border-glow-gradient-base)) padding-box;
|
|
191
202
|
|
|
192
203
|
mask-image:
|
|
193
204
|
linear-gradient(to bottom, black, black), radial-gradient(ellipse at 50% 50%, black 40%, transparent 65%),
|
|
@@ -1 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
// Component-scoped, but the lib uses `ViewEncapsulation.None` everywhere so
|
|
2
|
+
// the selector starts with `.wr-click-spark` — the same as the public Sass
|
|
3
|
+
// entry. Forwards the rules alone: `styles/_index.scss` is the entry that
|
|
4
|
+
// loads the theme, and a `styleUrl` is its own compilation.
|
|
5
|
+
@forward './styles/rules';
|
|
@@ -1,16 +1,11 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
// Public Sass entry for `@use 'ngwr/bits/click-spark'`.
|
|
2
|
+
//
|
|
3
|
+
// Loads the theme although `_rules` reads no token: the component strokes its
|
|
4
|
+
// sparks in `sparkColor`, whose default is `var(--wr-color-on-surface)` resolved
|
|
5
|
+
// against the canvas, so an app that writes this one `@use` still has the token
|
|
6
|
+
// the default paints with. `click-spark.scss` — the component's `styleUrl` —
|
|
7
|
+
// forwards `_rules` and nothing else, so the compiled component styles carry this
|
|
8
|
+
// component's own CSS and no second copy of the theme layer.
|
|
9
|
+
@use '../../../theme/styles';
|
|
6
10
|
|
|
7
|
-
|
|
8
|
-
position: absolute;
|
|
9
|
-
inset: 0;
|
|
10
|
-
width: 100%;
|
|
11
|
-
height: 100%;
|
|
12
|
-
pointer-events: none;
|
|
13
|
-
user-select: none;
|
|
14
|
-
z-index: 1;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
11
|
+
@forward 'rules';
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Click spark — canvas overlay that ignores pointer events so projected
|
|
2
|
+
// children stay interactive. The sparks themselves are stroked in
|
|
3
|
+
// `click-spark.ts`, in `sparkColor`, which defaults to `var(--wr-color-on-surface)`
|
|
4
|
+
// and is resolved against the canvas: the token is read from TypeScript rather than
|
|
5
|
+
// from a rule here, which is why `_index.scss` loads the theme for a sheet that
|
|
6
|
+
// names no token itself.
|
|
7
|
+
.wr-click-spark {
|
|
8
|
+
position: relative;
|
|
9
|
+
display: block;
|
|
10
|
+
|
|
11
|
+
&__canvas {
|
|
12
|
+
position: absolute;
|
|
13
|
+
inset: 0;
|
|
14
|
+
width: 100%;
|
|
15
|
+
height: 100%;
|
|
16
|
+
pointer-events: none;
|
|
17
|
+
user-select: none;
|
|
18
|
+
z-index: 1;
|
|
19
|
+
}
|
|
20
|
+
}
|
package/fesm2022/ngwr-alert.mjs
CHANGED
|
@@ -125,7 +125,7 @@ var WrAlert = class WrAlert {
|
|
|
125
125
|
"attr.aria-live": "liveLevel()"
|
|
126
126
|
} },
|
|
127
127
|
ngImport: i0,
|
|
128
|
-
template: "@if (!dismissed()) {\n @if (iconName(); as n) {\n <wr-icon class=\"wr-alert__icon\" [name]=\"n\" />\n } @else if (icon()) {\n <svg\n class=\"wr-alert__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 @switch (type()) {\n @case ('success') {\n <path d=\"M21.801 10A10 10 0 1 1 17 3.335\" />\n <path d=\"m9 11 3 3L22 4\" />\n }\n @case ('warning') {\n <path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\" />\n <path d=\"M12 9v4\" />\n <path d=\"M12 17h.01\" />\n }\n <!-- An OCTAGON, and the shape is the point. `danger` used to be a\n circle holding a bar over a dot, which is the vertical mirror of\n the `i` the @default branch draws — at the rendered 18px the two\n differ only by where a 1.5px gap sits. The stop-sign silhouette\n reads at that size and survives greyscale, which a mirrored gap\n does not. -->\n @case ('danger') {\n <path\n d=\"M2.586 16.726A2 2 0 0 1 2 15.312V8.688a2 2 0 0 1 .586-1.414l4.688-4.688A2 2 0 0 1 8.688 2h6.624a2 2 0 0 1 1.414.586l4.688 4.688A2 2 0 0 1 22 8.688v6.624a2 2 0 0 1-.586 1.414l-4.688 4.688a2 2 0 0 1-1.414.586H8.688a2 2 0 0 1-1.414-.586z\"\n />\n <path d=\"M12 8v4\" />\n <path d=\"M12 16h.01\" />\n }\n <!-- A bar, not the exclamation mark `danger` used to carry: `neutral`\n emitted that glyph byte for byte. The icon is the whole difference\n between these two, because what the component actually paints —\n `danger-ink` #a32a33 (L 0.0968) against `on-surface-muted` #5f6c7d\n (L 0.1464) on tints 1.036:1 apart — is a hue difference and little\n else. -->\n @case ('neutral') {\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"M8 12h8\" />\n }\n @case ('offline') {\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"m4.9 4.9 14.2 14.2\" />\n }\n @default {\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"M12 16v-4\" />\n <path d=\"M12 8h.01\" />\n }\n }\n </svg>\n }\n\n <div class=\"wr-alert__body\">\n @if (title(); as t) {\n <p class=\"wr-alert__title\">{{ t }}</p>\n @if (message(); as m) {\n <p class=\"wr-alert__message\">{{ m }}</p>\n }\n } @else if (message(); as m) {\n <p class=\"wr-alert__title\">{{ m }}</p>\n }\n </div>\n\n @if (closable()) {\n <button type=\"button\" class=\"wr-alert__close\" [attr.aria-label]=\"resolvedCloseLabel()\" (click)=\"onClose()\">\n <svg\n class=\"wr-icon__svg\"\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 <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </button>\n }\n}\n",
|
|
128
|
+
template: "@if (!dismissed()) {\n @if (iconName(); as n) {\n <wr-icon class=\"wr-alert__icon\" [name]=\"n\" />\n } @else if (icon()) {\n <svg\n class=\"wr-alert__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 @switch (type()) {\n @case ('success') {\n <path d=\"M21.801 10A10 10 0 1 1 17 3.335\" />\n <path d=\"m9 11 3 3L22 4\" />\n }\n @case ('warning') {\n <path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\" />\n <path d=\"M12 9v4\" />\n <path d=\"M12 17h.01\" />\n }\n <!-- An OCTAGON, and the shape is the point. `danger` used to be a\n circle holding a bar over a dot, which is the vertical mirror of\n the `i` the @default branch draws — at the rendered 18px the two\n differ only by where a 1.5px gap sits. The stop-sign silhouette\n reads at that size and survives greyscale, which a mirrored gap\n does not. -->\n @case ('danger') {\n <path\n d=\"M2.586 16.726A2 2 0 0 1 2 15.312V8.688a2 2 0 0 1 .586-1.414l4.688-4.688A2 2 0 0 1 8.688 2h6.624a2 2 0 0 1 1.414.586l4.688 4.688A2 2 0 0 1 22 8.688v6.624a2 2 0 0 1-.586 1.414l-4.688 4.688a2 2 0 0 1-1.414.586H8.688a2 2 0 0 1-1.414-.586z\"\n />\n <path d=\"M12 8v4\" />\n <path d=\"M12 16h.01\" />\n }\n <!-- A bar, not the exclamation mark `danger` used to carry: `neutral`\n emitted that glyph byte for byte. The icon is the whole difference\n between these two, because what the component actually paints —\n `danger-ink` #a32a33 (L 0.0968) against `on-surface-muted` #5f6c7d\n (L 0.1464) on tints 1.036:1 apart — is a hue difference and little\n else. -->\n @case ('neutral') {\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"M8 12h8\" />\n }\n @case ('offline') {\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"m4.9 4.9 14.2 14.2\" />\n }\n @default {\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"M12 16v-4\" />\n <path d=\"M12 8h.01\" />\n }\n }\n </svg>\n }\n\n <div class=\"wr-alert__body\">\n @if (title(); as t) {\n <p class=\"wr-alert__title\">{{ t }}</p>\n @if (message(); as m) {\n <p class=\"wr-alert__message\">{{ m }}</p>\n }\n } @else if (message(); as m) {\n <p class=\"wr-alert__title\">{{ m }}</p>\n }\n <div class=\"wr-alert__content\"><ng-content /></div>\n </div>\n\n @if (closable()) {\n <button type=\"button\" class=\"wr-alert__close\" [attr.aria-label]=\"resolvedCloseLabel()\" (click)=\"onClose()\">\n <svg\n class=\"wr-icon__svg\"\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 <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </button>\n }\n}\n",
|
|
129
129
|
dependencies: [{
|
|
130
130
|
kind: "component",
|
|
131
131
|
type: WrIcon,
|
|
@@ -152,7 +152,7 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
152
152
|
"[attr.role]": "liveRole()",
|
|
153
153
|
"[attr.aria-live]": "liveLevel()"
|
|
154
154
|
},
|
|
155
|
-
template: "@if (!dismissed()) {\n @if (iconName(); as n) {\n <wr-icon class=\"wr-alert__icon\" [name]=\"n\" />\n } @else if (icon()) {\n <svg\n class=\"wr-alert__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 @switch (type()) {\n @case ('success') {\n <path d=\"M21.801 10A10 10 0 1 1 17 3.335\" />\n <path d=\"m9 11 3 3L22 4\" />\n }\n @case ('warning') {\n <path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\" />\n <path d=\"M12 9v4\" />\n <path d=\"M12 17h.01\" />\n }\n <!-- An OCTAGON, and the shape is the point. `danger` used to be a\n circle holding a bar over a dot, which is the vertical mirror of\n the `i` the @default branch draws — at the rendered 18px the two\n differ only by where a 1.5px gap sits. The stop-sign silhouette\n reads at that size and survives greyscale, which a mirrored gap\n does not. -->\n @case ('danger') {\n <path\n d=\"M2.586 16.726A2 2 0 0 1 2 15.312V8.688a2 2 0 0 1 .586-1.414l4.688-4.688A2 2 0 0 1 8.688 2h6.624a2 2 0 0 1 1.414.586l4.688 4.688A2 2 0 0 1 22 8.688v6.624a2 2 0 0 1-.586 1.414l-4.688 4.688a2 2 0 0 1-1.414.586H8.688a2 2 0 0 1-1.414-.586z\"\n />\n <path d=\"M12 8v4\" />\n <path d=\"M12 16h.01\" />\n }\n <!-- A bar, not the exclamation mark `danger` used to carry: `neutral`\n emitted that glyph byte for byte. The icon is the whole difference\n between these two, because what the component actually paints —\n `danger-ink` #a32a33 (L 0.0968) against `on-surface-muted` #5f6c7d\n (L 0.1464) on tints 1.036:1 apart — is a hue difference and little\n else. -->\n @case ('neutral') {\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"M8 12h8\" />\n }\n @case ('offline') {\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"m4.9 4.9 14.2 14.2\" />\n }\n @default {\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"M12 16v-4\" />\n <path d=\"M12 8h.01\" />\n }\n }\n </svg>\n }\n\n <div class=\"wr-alert__body\">\n @if (title(); as t) {\n <p class=\"wr-alert__title\">{{ t }}</p>\n @if (message(); as m) {\n <p class=\"wr-alert__message\">{{ m }}</p>\n }\n } @else if (message(); as m) {\n <p class=\"wr-alert__title\">{{ m }}</p>\n }\n </div>\n\n @if (closable()) {\n <button type=\"button\" class=\"wr-alert__close\" [attr.aria-label]=\"resolvedCloseLabel()\" (click)=\"onClose()\">\n <svg\n class=\"wr-icon__svg\"\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 <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </button>\n }\n}\n"
|
|
155
|
+
template: "@if (!dismissed()) {\n @if (iconName(); as n) {\n <wr-icon class=\"wr-alert__icon\" [name]=\"n\" />\n } @else if (icon()) {\n <svg\n class=\"wr-alert__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 @switch (type()) {\n @case ('success') {\n <path d=\"M21.801 10A10 10 0 1 1 17 3.335\" />\n <path d=\"m9 11 3 3L22 4\" />\n }\n @case ('warning') {\n <path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\" />\n <path d=\"M12 9v4\" />\n <path d=\"M12 17h.01\" />\n }\n <!-- An OCTAGON, and the shape is the point. `danger` used to be a\n circle holding a bar over a dot, which is the vertical mirror of\n the `i` the @default branch draws — at the rendered 18px the two\n differ only by where a 1.5px gap sits. The stop-sign silhouette\n reads at that size and survives greyscale, which a mirrored gap\n does not. -->\n @case ('danger') {\n <path\n d=\"M2.586 16.726A2 2 0 0 1 2 15.312V8.688a2 2 0 0 1 .586-1.414l4.688-4.688A2 2 0 0 1 8.688 2h6.624a2 2 0 0 1 1.414.586l4.688 4.688A2 2 0 0 1 22 8.688v6.624a2 2 0 0 1-.586 1.414l-4.688 4.688a2 2 0 0 1-1.414.586H8.688a2 2 0 0 1-1.414-.586z\"\n />\n <path d=\"M12 8v4\" />\n <path d=\"M12 16h.01\" />\n }\n <!-- A bar, not the exclamation mark `danger` used to carry: `neutral`\n emitted that glyph byte for byte. The icon is the whole difference\n between these two, because what the component actually paints —\n `danger-ink` #a32a33 (L 0.0968) against `on-surface-muted` #5f6c7d\n (L 0.1464) on tints 1.036:1 apart — is a hue difference and little\n else. -->\n @case ('neutral') {\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"M8 12h8\" />\n }\n @case ('offline') {\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"m4.9 4.9 14.2 14.2\" />\n }\n @default {\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"M12 16v-4\" />\n <path d=\"M12 8h.01\" />\n }\n }\n </svg>\n }\n\n <div class=\"wr-alert__body\">\n @if (title(); as t) {\n <p class=\"wr-alert__title\">{{ t }}</p>\n @if (message(); as m) {\n <p class=\"wr-alert__message\">{{ m }}</p>\n }\n } @else if (message(); as m) {\n <p class=\"wr-alert__title\">{{ m }}</p>\n }\n <div class=\"wr-alert__content\"><ng-content /></div>\n </div>\n\n @if (closable()) {\n <button type=\"button\" class=\"wr-alert__close\" [attr.aria-label]=\"resolvedCloseLabel()\" (click)=\"onClose()\">\n <svg\n class=\"wr-icon__svg\"\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 <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </button>\n }\n}\n"
|
|
156
156
|
}]
|
|
157
157
|
}],
|
|
158
158
|
propDecorators: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ngwr-alert.mjs","names":[],"sources":["../../../projects/lib/alert/alert.ts","../../../projects/lib/alert/alert.html"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport {\n Component,\n ElementRef,\n Injector,\n ViewEncapsulation,\n afterNextRender,\n computed,\n inject,\n input,\n output,\n signal,\n} from '@angular/core';\n\nimport { useI18nText } from 'ngwr/i18n';\nimport { WrIcon, type WrIconName } from 'ngwr/icon';\nimport { getFocusableElements } from 'ngwr/utils';\n\nimport type { WrAlertType } from './types';\n\n/**\n * Inline status banner. Use for feedback messages — saved/failed/notice etc.\n *\n * @example\n * ```html\n * <wr-alert title=\"Saved\" message=\"Your changes are live.\" type=\"success\" />\n * <wr-alert title=\"Failed\" type=\"danger\" closable (closed)=\"onClose()\" />\n * ```\n *\n * @see https://ngwr.dev/reference/components/alert\n */\n@Component({\n selector: 'wr-alert',\n templateUrl: './alert.html',\n imports: [WrIcon],\n encapsulation: ViewEncapsulation.None,\n host: {\n // `title` is an input, and a static attribute that feeds an input is ALSO\n // written onto the host — where the browser shows it as a native tooltip\n // over the whole component, and where it feeds the accessible name of any\n // element that has a role. `<wr-alert title=\"Heads up\">` is the documented\n // spelling, so every page writing it shipped both. A bound `[title]` never\n // did this, which is why the two forms behaved differently for no reason a\n // reader could see.\n '[attr.title]': 'null',\n '[class]': 'classes()',\n '[attr.role]': 'liveRole()',\n '[attr.aria-live]': 'liveLevel()',\n },\n})\nexport class WrAlert {\n /** Accessible name. Falls back to `alert.close`, then `'Close alert'`. */\n readonly closeLabel = input<string | null>(null);\n\n protected readonly resolvedCloseLabel = useI18nText(this.closeLabel, 'alert.close', 'Close alert');\n\n /**\n * Optional headline shown at the top of the alert.\n *\n * @default null\n */\n readonly title = input<string | null>(null);\n\n /**\n * Visual variant.\n *\n * @default 'info'\n */\n readonly type = input<WrAlertType>('info');\n\n /**\n * Override the default per-type icon with any ngwr icon name.\n *\n * @default null\n */\n readonly iconName = input<WrIconName | null>(null);\n\n /**\n * Optional secondary message rendered below the title.\n *\n * @default null\n */\n readonly message = input<string | null>(null);\n\n /**\n * When `true`, renders a leading status icon matching the `type`.\n * Pass `false` to hide. Ignored when `iconName` is set.\n *\n * @default true\n */\n readonly icon = input(true, { transform: coerceBooleanProperty });\n\n /**\n * When `true`, renders a close button.\n *\n * @default false\n */\n readonly closable = input(false, { transform: coerceBooleanProperty });\n\n /**\n * Emitted when the user dismisses the alert via the close button. If the close\n * button had focus, focus moves on to the next focusable element after the alert\n * (or the one before it, when nothing follows) — unless a `(closed)` handler has\n * placed it somewhere itself.\n */\n readonly closed = output<void>();\n\n protected readonly dismissed = signal(false);\n\n protected readonly classes = computed(() => {\n const parts = ['wr-alert', `wr-alert--${this.type()}`];\n if (this.dismissed()) parts.push('wr-alert--dismissed');\n return parts.join(' ');\n });\n\n /** Escalate role/aria-live for danger/warning alerts. */\n protected readonly liveRole = computed(() =>\n this.dismissed() ? null : this.type() === 'danger' ? 'alert' : 'status'\n );\n protected readonly liveLevel = computed(() =>\n this.dismissed() ? null : this.type() === 'danger' || this.type() === 'warning' ? 'assertive' : 'polite'\n );\n\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\n private readonly injector = inject(Injector);\n\n protected onClose(): void {\n const host = this.host.nativeElement;\n const hadFocus = host.contains(host.ownerDocument.activeElement);\n this.dismissed.set(true);\n this.closed.emit();\n if (hadFocus) afterNextRender(() => this.handOnFocus(), { injector: this.injector });\n }\n\n /**\n * The close button is removed with the rest of the content, and a focused element\n * that leaves the DOM drops the caret on `<body>`: a keyboard user's next Tab, and\n * a screen reader's reading position, started over from the top of the page. Run\n * after the render that removed it, so a `(closed)` handler that moved focus on\n * purpose is seen and left alone.\n */\n private handOnFocus(): void {\n const host = this.host.nativeElement;\n const doc = host.ownerDocument;\n const active = doc.activeElement;\n if (active && active !== doc.body) return;\n // Tab stops only: a roving composite parks its other items at -1. The host has\n // nothing focusable left inside it, so every stop is either before or after it.\n const stops = getFocusableElements(doc.body).filter(el => el.tabIndex >= 0);\n // eslint-disable-next-line no-bitwise -- compareDocumentPosition answers with a bitmask\n const after = stops.findIndex(el => (host.compareDocumentPosition(el) & Node.DOCUMENT_POSITION_FOLLOWING) !== 0);\n (after >= 0 ? stops[after] : stops.at(-1))?.focus();\n }\n}\n","@if (!dismissed()) {\n @if (iconName(); as n) {\n <wr-icon class=\"wr-alert__icon\" [name]=\"n\" />\n } @else if (icon()) {\n <svg\n class=\"wr-alert__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 @switch (type()) {\n @case ('success') {\n <path d=\"M21.801 10A10 10 0 1 1 17 3.335\" />\n <path d=\"m9 11 3 3L22 4\" />\n }\n @case ('warning') {\n <path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\" />\n <path d=\"M12 9v4\" />\n <path d=\"M12 17h.01\" />\n }\n <!-- An OCTAGON, and the shape is the point. `danger` used to be a\n circle holding a bar over a dot, which is the vertical mirror of\n the `i` the @default branch draws — at the rendered 18px the two\n differ only by where a 1.5px gap sits. The stop-sign silhouette\n reads at that size and survives greyscale, which a mirrored gap\n does not. -->\n @case ('danger') {\n <path\n d=\"M2.586 16.726A2 2 0 0 1 2 15.312V8.688a2 2 0 0 1 .586-1.414l4.688-4.688A2 2 0 0 1 8.688 2h6.624a2 2 0 0 1 1.414.586l4.688 4.688A2 2 0 0 1 22 8.688v6.624a2 2 0 0 1-.586 1.414l-4.688 4.688a2 2 0 0 1-1.414.586H8.688a2 2 0 0 1-1.414-.586z\"\n />\n <path d=\"M12 8v4\" />\n <path d=\"M12 16h.01\" />\n }\n <!-- A bar, not the exclamation mark `danger` used to carry: `neutral`\n emitted that glyph byte for byte. The icon is the whole difference\n between these two, because what the component actually paints —\n `danger-ink` #a32a33 (L 0.0968) against `on-surface-muted` #5f6c7d\n (L 0.1464) on tints 1.036:1 apart — is a hue difference and little\n else. -->\n @case ('neutral') {\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"M8 12h8\" />\n }\n @case ('offline') {\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"m4.9 4.9 14.2 14.2\" />\n }\n @default {\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"M12 16v-4\" />\n <path d=\"M12 8h.01\" />\n }\n }\n </svg>\n }\n\n <div class=\"wr-alert__body\">\n @if (title(); as t) {\n <p class=\"wr-alert__title\">{{ t }}</p>\n @if (message(); as m) {\n <p class=\"wr-alert__message\">{{ m }}</p>\n }\n } @else if (message(); as m) {\n <p class=\"wr-alert__title\">{{ m }}</p>\n }\n </div>\n\n @if (closable()) {\n <button type=\"button\" class=\"wr-alert__close\" [attr.aria-label]=\"resolvedCloseLabel()\" (click)=\"onClose()\">\n <svg\n class=\"wr-icon__svg\"\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 <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </button>\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ngwr-alert.mjs","names":[],"sources":["../../../projects/lib/alert/alert.ts","../../../projects/lib/alert/alert.html"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport {\n Component,\n ElementRef,\n Injector,\n ViewEncapsulation,\n afterNextRender,\n computed,\n inject,\n input,\n output,\n signal,\n} from '@angular/core';\n\nimport { useI18nText } from 'ngwr/i18n';\nimport { WrIcon, type WrIconName } from 'ngwr/icon';\nimport { getFocusableElements } from 'ngwr/utils';\n\nimport type { WrAlertType } from './types';\n\n/**\n * Inline status banner. Use for feedback messages — saved/failed/notice etc.\n * Content placed inside renders below the message, in `.wr-alert__content`.\n *\n * @example\n * ```html\n * <wr-alert title=\"Saved\" message=\"Your changes are live.\" type=\"success\" />\n * <wr-alert title=\"Failed\" type=\"danger\" closable (closed)=\"onClose()\" />\n * <wr-alert title=\"Sync failed\" type=\"danger\">Request <code>8f2c41</code> timed out.</wr-alert>\n * ```\n *\n * @see https://ngwr.dev/reference/components/alert\n */\n@Component({\n selector: 'wr-alert',\n templateUrl: './alert.html',\n imports: [WrIcon],\n encapsulation: ViewEncapsulation.None,\n host: {\n // `title` is an input, and a static attribute that feeds an input is ALSO\n // written onto the host — where the browser shows it as a native tooltip\n // over the whole component, and where it feeds the accessible name of any\n // element that has a role. `<wr-alert title=\"Heads up\">` is the documented\n // spelling, so every page writing it shipped both. A bound `[title]` never\n // did this, which is why the two forms behaved differently for no reason a\n // reader could see.\n '[attr.title]': 'null',\n '[class]': 'classes()',\n '[attr.role]': 'liveRole()',\n '[attr.aria-live]': 'liveLevel()',\n },\n})\nexport class WrAlert {\n /** Accessible name. Falls back to `alert.close`, then `'Close alert'`. */\n readonly closeLabel = input<string | null>(null);\n\n protected readonly resolvedCloseLabel = useI18nText(this.closeLabel, 'alert.close', 'Close alert');\n\n /**\n * Optional headline shown at the top of the alert.\n *\n * @default null\n */\n readonly title = input<string | null>(null);\n\n /**\n * Visual variant.\n *\n * @default 'info'\n */\n readonly type = input<WrAlertType>('info');\n\n /**\n * Override the default per-type icon with any ngwr icon name.\n *\n * @default null\n */\n readonly iconName = input<WrIconName | null>(null);\n\n /**\n * Optional secondary message rendered below the title.\n *\n * @default null\n */\n readonly message = input<string | null>(null);\n\n /**\n * When `true`, renders a leading status icon matching the `type`.\n * Pass `false` to hide. Ignored when `iconName` is set.\n *\n * @default true\n */\n readonly icon = input(true, { transform: coerceBooleanProperty });\n\n /**\n * When `true`, renders a close button.\n *\n * @default false\n */\n readonly closable = input(false, { transform: coerceBooleanProperty });\n\n /**\n * Emitted when the user dismisses the alert via the close button. If the close\n * button had focus, focus moves on to the next focusable element after the alert\n * (or the one before it, when nothing follows) — unless a `(closed)` handler has\n * placed it somewhere itself.\n */\n readonly closed = output<void>();\n\n protected readonly dismissed = signal(false);\n\n protected readonly classes = computed(() => {\n const parts = ['wr-alert', `wr-alert--${this.type()}`];\n if (this.dismissed()) parts.push('wr-alert--dismissed');\n return parts.join(' ');\n });\n\n /** Escalate role/aria-live for danger/warning alerts. */\n protected readonly liveRole = computed(() =>\n this.dismissed() ? null : this.type() === 'danger' ? 'alert' : 'status'\n );\n protected readonly liveLevel = computed(() =>\n this.dismissed() ? null : this.type() === 'danger' || this.type() === 'warning' ? 'assertive' : 'polite'\n );\n\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\n private readonly injector = inject(Injector);\n\n protected onClose(): void {\n const host = this.host.nativeElement;\n const hadFocus = host.contains(host.ownerDocument.activeElement);\n this.dismissed.set(true);\n this.closed.emit();\n if (hadFocus) afterNextRender(() => this.handOnFocus(), { injector: this.injector });\n }\n\n /**\n * The close button is removed with the rest of the content, and a focused element\n * that leaves the DOM drops the caret on `<body>`: a keyboard user's next Tab, and\n * a screen reader's reading position, started over from the top of the page. Run\n * after the render that removed it, so a `(closed)` handler that moved focus on\n * purpose is seen and left alone.\n */\n private handOnFocus(): void {\n const host = this.host.nativeElement;\n const doc = host.ownerDocument;\n const active = doc.activeElement;\n if (active && active !== doc.body) return;\n // Tab stops only: a roving composite parks its other items at -1. The host has\n // nothing focusable left inside it, so every stop is either before or after it.\n const stops = getFocusableElements(doc.body).filter(el => el.tabIndex >= 0);\n // eslint-disable-next-line no-bitwise -- compareDocumentPosition answers with a bitmask\n const after = stops.findIndex(el => (host.compareDocumentPosition(el) & Node.DOCUMENT_POSITION_FOLLOWING) !== 0);\n (after >= 0 ? stops[after] : stops.at(-1))?.focus();\n }\n}\n","@if (!dismissed()) {\n @if (iconName(); as n) {\n <wr-icon class=\"wr-alert__icon\" [name]=\"n\" />\n } @else if (icon()) {\n <svg\n class=\"wr-alert__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 @switch (type()) {\n @case ('success') {\n <path d=\"M21.801 10A10 10 0 1 1 17 3.335\" />\n <path d=\"m9 11 3 3L22 4\" />\n }\n @case ('warning') {\n <path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\" />\n <path d=\"M12 9v4\" />\n <path d=\"M12 17h.01\" />\n }\n <!-- An OCTAGON, and the shape is the point. `danger` used to be a\n circle holding a bar over a dot, which is the vertical mirror of\n the `i` the @default branch draws — at the rendered 18px the two\n differ only by where a 1.5px gap sits. The stop-sign silhouette\n reads at that size and survives greyscale, which a mirrored gap\n does not. -->\n @case ('danger') {\n <path\n d=\"M2.586 16.726A2 2 0 0 1 2 15.312V8.688a2 2 0 0 1 .586-1.414l4.688-4.688A2 2 0 0 1 8.688 2h6.624a2 2 0 0 1 1.414.586l4.688 4.688A2 2 0 0 1 22 8.688v6.624a2 2 0 0 1-.586 1.414l-4.688 4.688a2 2 0 0 1-1.414.586H8.688a2 2 0 0 1-1.414-.586z\"\n />\n <path d=\"M12 8v4\" />\n <path d=\"M12 16h.01\" />\n }\n <!-- A bar, not the exclamation mark `danger` used to carry: `neutral`\n emitted that glyph byte for byte. The icon is the whole difference\n between these two, because what the component actually paints —\n `danger-ink` #a32a33 (L 0.0968) against `on-surface-muted` #5f6c7d\n (L 0.1464) on tints 1.036:1 apart — is a hue difference and little\n else. -->\n @case ('neutral') {\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"M8 12h8\" />\n }\n @case ('offline') {\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"m4.9 4.9 14.2 14.2\" />\n }\n @default {\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"M12 16v-4\" />\n <path d=\"M12 8h.01\" />\n }\n }\n </svg>\n }\n\n <div class=\"wr-alert__body\">\n @if (title(); as t) {\n <p class=\"wr-alert__title\">{{ t }}</p>\n @if (message(); as m) {\n <p class=\"wr-alert__message\">{{ m }}</p>\n }\n } @else if (message(); as m) {\n <p class=\"wr-alert__title\">{{ m }}</p>\n }\n <div class=\"wr-alert__content\"><ng-content /></div>\n </div>\n\n @if (closable()) {\n <button type=\"button\" class=\"wr-alert__close\" [attr.aria-label]=\"resolvedCloseLabel()\" (click)=\"onClose()\">\n <svg\n class=\"wr-icon__svg\"\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 <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </button>\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from "@angular/core";
|
|
2
|
-
import { Component, DestroyRef, ElementRef, NgZone, PLATFORM_ID, ViewEncapsulation, afterNextRender, effect, inject, input, signal, viewChild } from "@angular/core";
|
|
2
|
+
import { Component, DestroyRef, ElementRef, NgZone, PLATFORM_ID, ViewEncapsulation, afterNextRender, afterRenderEffect, effect, inject, input, signal, untracked, viewChild } from "@angular/core";
|
|
3
3
|
import { isPlatformBrowser } from "@angular/common";
|
|
4
4
|
import { numAttr, resolveCssColorChannels, watchColorScheme } from "ngwr/utils";
|
|
5
5
|
import { WrPlatform } from "ngwr/platform";
|
|
@@ -154,7 +154,7 @@ var WrAurora = class WrAurora {
|
|
|
154
154
|
platform = inject(WrPlatform);
|
|
155
155
|
isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
|
|
156
156
|
gl = null;
|
|
157
|
-
|
|
157
|
+
gpu = null;
|
|
158
158
|
rafId = 0;
|
|
159
159
|
time = 0;
|
|
160
160
|
booted = signal(false, ...ngDevMode ? [{ debugName: "booted" }] : /* istanbul ignore next */ []);
|
|
@@ -163,16 +163,19 @@ var WrAurora = class WrAurora {
|
|
|
163
163
|
constructor() {
|
|
164
164
|
if (!this.isBrowser) return;
|
|
165
165
|
afterNextRender(() => this.zone.runOutsideAngular(() => this.boot()));
|
|
166
|
-
|
|
166
|
+
afterRenderEffect(() => {
|
|
167
|
+
if (!this.booted()) return;
|
|
168
|
+
const still = this.platform.prefersReducedMotion();
|
|
167
169
|
this.colorStops();
|
|
168
170
|
this.amplitude();
|
|
169
171
|
this.blend();
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
172
|
+
untracked(() => {
|
|
173
|
+
this.pushUniforms();
|
|
174
|
+
if (still) this.draw(0);
|
|
175
|
+
});
|
|
173
176
|
});
|
|
174
177
|
watchColorScheme(() => {
|
|
175
|
-
if (!this.
|
|
178
|
+
if (!this.gpu || !this.platform.prefersReducedMotion()) return;
|
|
176
179
|
this.pushUniforms();
|
|
177
180
|
this.draw(0);
|
|
178
181
|
});
|
|
@@ -196,41 +199,11 @@ var WrAurora = class WrAurora {
|
|
|
196
199
|
});
|
|
197
200
|
if (!gl) return;
|
|
198
201
|
this.gl = gl;
|
|
199
|
-
|
|
200
|
-
gl.
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
gl.shaderSource(shader, source);
|
|
205
|
-
gl.compileShader(shader);
|
|
206
|
-
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) console.error("wr-aurora shader error:", gl.getShaderInfoLog(shader));
|
|
207
|
-
return shader;
|
|
208
|
-
};
|
|
209
|
-
const program = gl.createProgram();
|
|
210
|
-
gl.attachShader(program, compile(gl.VERTEX_SHADER, VERT));
|
|
211
|
-
gl.attachShader(program, compile(gl.FRAGMENT_SHADER, FRAG));
|
|
212
|
-
gl.linkProgram(program);
|
|
213
|
-
gl.useProgram(program);
|
|
214
|
-
for (const name of [
|
|
215
|
-
"uTime",
|
|
216
|
-
"uAmplitude",
|
|
217
|
-
"uColorStops",
|
|
218
|
-
"uResolution",
|
|
219
|
-
"uBlend"
|
|
220
|
-
]) this.uniforms[name] = gl.getUniformLocation(program, name);
|
|
221
|
-
const buffer = gl.createBuffer();
|
|
222
|
-
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
|
223
|
-
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
|
|
224
|
-
-1,
|
|
225
|
-
-1,
|
|
226
|
-
3,
|
|
227
|
-
-1,
|
|
228
|
-
-1,
|
|
229
|
-
3
|
|
230
|
-
]), gl.STATIC_DRAW);
|
|
231
|
-
const position = gl.getAttribLocation(program, "position");
|
|
232
|
-
gl.enableVertexAttribArray(position);
|
|
233
|
-
gl.vertexAttribPointer(position, 2, gl.FLOAT, false, 0, 0);
|
|
202
|
+
this.gpu = this.build(gl);
|
|
203
|
+
if (!this.gpu && !gl.isContextLost()) {
|
|
204
|
+
this.release(gl);
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
234
207
|
const hostEl = this.host.nativeElement;
|
|
235
208
|
const fit = () => {
|
|
236
209
|
const width = hostEl.clientWidth;
|
|
@@ -239,8 +212,21 @@ var WrAurora = class WrAurora {
|
|
|
239
212
|
canvas.width = Math.max(1, width);
|
|
240
213
|
canvas.height = Math.max(1, height);
|
|
241
214
|
gl.viewport(0, 0, canvas.width, canvas.height);
|
|
242
|
-
gl.uniform2f(this.uniforms["uResolution"], canvas.width, canvas.height);
|
|
215
|
+
if (this.gpu) gl.uniform2f(this.gpu.uniforms["uResolution"], canvas.width, canvas.height);
|
|
216
|
+
};
|
|
217
|
+
const onLost = (event) => {
|
|
218
|
+
event.preventDefault();
|
|
219
|
+
this.gpu = null;
|
|
220
|
+
this.booted.set(false);
|
|
221
|
+
};
|
|
222
|
+
const onRestored = () => {
|
|
223
|
+
this.gpu = this.build(gl);
|
|
224
|
+
if (!this.gpu) return;
|
|
225
|
+
fit();
|
|
226
|
+
this.booted.set(true);
|
|
243
227
|
};
|
|
228
|
+
canvas.addEventListener("webglcontextlost", onLost);
|
|
229
|
+
canvas.addEventListener("webglcontextrestored", onRestored);
|
|
244
230
|
const resize = () => {
|
|
245
231
|
fit();
|
|
246
232
|
this.draw(this.platform.prefersReducedMotion() ? 0 : this.time);
|
|
@@ -260,17 +246,87 @@ var WrAurora = class WrAurora {
|
|
|
260
246
|
window.removeEventListener("resize", resize);
|
|
261
247
|
ro.disconnect();
|
|
262
248
|
io?.disconnect();
|
|
249
|
+
canvas.removeEventListener("webglcontextlost", onLost);
|
|
250
|
+
canvas.removeEventListener("webglcontextrestored", onRestored);
|
|
251
|
+
this.release(gl);
|
|
263
252
|
});
|
|
264
|
-
this.booted.set(true);
|
|
253
|
+
if (this.gpu) this.booted.set(true);
|
|
254
|
+
}
|
|
255
|
+
build(gl) {
|
|
256
|
+
const vert = this.compile(gl, gl.VERTEX_SHADER, VERT);
|
|
257
|
+
const frag = this.compile(gl, gl.FRAGMENT_SHADER, FRAG);
|
|
258
|
+
const program = vert && frag ? gl.createProgram() : null;
|
|
259
|
+
if (vert && frag && program) {
|
|
260
|
+
gl.attachShader(program, vert);
|
|
261
|
+
gl.attachShader(program, frag);
|
|
262
|
+
gl.linkProgram(program);
|
|
263
|
+
}
|
|
264
|
+
gl.deleteShader(vert);
|
|
265
|
+
gl.deleteShader(frag);
|
|
266
|
+
if (!program) return null;
|
|
267
|
+
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
|
268
|
+
if (!gl.isContextLost()) console.error("wr-aurora program error:", gl.getProgramInfoLog(program));
|
|
269
|
+
gl.deleteProgram(program);
|
|
270
|
+
return null;
|
|
271
|
+
}
|
|
272
|
+
gl.useProgram(program);
|
|
273
|
+
gl.clearColor(0, 0, 0, 0);
|
|
274
|
+
gl.enable(gl.BLEND);
|
|
275
|
+
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
|
|
276
|
+
const uniforms = {};
|
|
277
|
+
for (const name of [
|
|
278
|
+
"uTime",
|
|
279
|
+
"uAmplitude",
|
|
280
|
+
"uColorStops",
|
|
281
|
+
"uResolution",
|
|
282
|
+
"uBlend"
|
|
283
|
+
]) uniforms[name] = gl.getUniformLocation(program, name);
|
|
284
|
+
const buffer = gl.createBuffer();
|
|
285
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
|
286
|
+
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
|
|
287
|
+
-1,
|
|
288
|
+
-1,
|
|
289
|
+
3,
|
|
290
|
+
-1,
|
|
291
|
+
-1,
|
|
292
|
+
3
|
|
293
|
+
]), gl.STATIC_DRAW);
|
|
294
|
+
const position = gl.getAttribLocation(program, "position");
|
|
295
|
+
gl.enableVertexAttribArray(position);
|
|
296
|
+
gl.vertexAttribPointer(position, 2, gl.FLOAT, false, 0, 0);
|
|
297
|
+
return {
|
|
298
|
+
program,
|
|
299
|
+
buffer,
|
|
300
|
+
uniforms
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
compile(gl, type, source) {
|
|
304
|
+
const shader = gl.createShader(type);
|
|
305
|
+
if (!shader) return null;
|
|
306
|
+
gl.shaderSource(shader, source);
|
|
307
|
+
gl.compileShader(shader);
|
|
308
|
+
if (gl.getShaderParameter(shader, gl.COMPILE_STATUS)) return shader;
|
|
309
|
+
if (!gl.isContextLost()) console.error("wr-aurora shader error:", gl.getShaderInfoLog(shader));
|
|
310
|
+
gl.deleteShader(shader);
|
|
311
|
+
return null;
|
|
312
|
+
}
|
|
313
|
+
release(gl) {
|
|
314
|
+
if (this.gpu) {
|
|
315
|
+
gl.deleteProgram(this.gpu.program);
|
|
316
|
+
gl.deleteBuffer(this.gpu.buffer);
|
|
317
|
+
}
|
|
318
|
+
gl.getExtension("WEBGL_lose_context")?.loseContext();
|
|
319
|
+
this.gpu = null;
|
|
320
|
+
this.gl = null;
|
|
265
321
|
}
|
|
266
322
|
pushUniforms() {
|
|
267
|
-
const gl = this
|
|
268
|
-
if (!gl) return;
|
|
323
|
+
const { gl, gpu } = this;
|
|
324
|
+
if (!gl || !gpu) return;
|
|
269
325
|
const flat = /* @__PURE__ */ new Float32Array(9);
|
|
270
326
|
for (let i = 0; i < 3; i++) flat.set(this.resolveStop(i), i * 3);
|
|
271
|
-
gl.uniform3fv(
|
|
272
|
-
gl.uniform1f(
|
|
273
|
-
gl.uniform1f(
|
|
327
|
+
gl.uniform3fv(gpu.uniforms["uColorStops"], flat);
|
|
328
|
+
gl.uniform1f(gpu.uniforms["uAmplitude"], this.amplitude());
|
|
329
|
+
gl.uniform1f(gpu.uniforms["uBlend"], this.blend());
|
|
274
330
|
}
|
|
275
331
|
resolveStop(index) {
|
|
276
332
|
const explicit = this.colorStops();
|
|
@@ -279,10 +335,10 @@ var WrAurora = class WrAurora {
|
|
|
279
335
|
return resolveCssColorChannels(this.canvasRef().nativeElement, expression, fallback);
|
|
280
336
|
}
|
|
281
337
|
draw(time) {
|
|
282
|
-
const gl = this
|
|
283
|
-
if (!gl || !this.hasBox()) return;
|
|
338
|
+
const { gl, gpu } = this;
|
|
339
|
+
if (!gl || !gpu || !this.hasBox()) return;
|
|
284
340
|
this.time = time;
|
|
285
|
-
gl.uniform1f(
|
|
341
|
+
gl.uniform1f(gpu.uniforms["uTime"], time);
|
|
286
342
|
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
287
343
|
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
|
288
344
|
}
|