ngwr 14.6.0 → 14.8.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 +14 -3
- package/avatar/styles/_index.scss +44 -0
- package/bar-chart/styles/_index.scss +1 -0
- package/calendar-heatmap/styles/_index.scss +1 -0
- package/cascader/styles/_rules.scss +86 -18
- package/collapse/styles/_index.scss +0 -1
- package/date-picker/styles/_index.scss +30 -0
- package/density/styles/_index.scss +4 -2
- package/donut-chart/styles/_index.scss +10 -0
- package/dropdown/styles/_index.scss +67 -5
- package/event-calendar/styles/_index.scss +209 -35
- package/fesm2022/ngwr-avatar.mjs +19 -2
- package/fesm2022/ngwr-avatar.mjs.map +1 -1
- package/fesm2022/ngwr-bar-chart.mjs +29 -4
- package/fesm2022/ngwr-bar-chart.mjs.map +1 -1
- package/fesm2022/ngwr-calendar-heatmap.mjs +59 -4
- package/fesm2022/ngwr-calendar-heatmap.mjs.map +1 -1
- package/fesm2022/ngwr-cascader.mjs +2 -2
- package/fesm2022/ngwr-cascader.mjs.map +1 -1
- package/fesm2022/ngwr-checkbox.mjs +12 -2
- package/fesm2022/ngwr-checkbox.mjs.map +1 -1
- package/fesm2022/ngwr-date-picker.mjs +705 -46
- package/fesm2022/ngwr-date-picker.mjs.map +1 -1
- package/fesm2022/ngwr-donut-chart.mjs +46 -4
- package/fesm2022/ngwr-donut-chart.mjs.map +1 -1
- package/fesm2022/ngwr-dropdown.mjs +166 -17
- package/fesm2022/ngwr-dropdown.mjs.map +1 -1
- package/fesm2022/ngwr-event-calendar.mjs +9 -2
- package/fesm2022/ngwr-event-calendar.mjs.map +1 -1
- package/fesm2022/ngwr-form.mjs +52 -1
- package/fesm2022/ngwr-form.mjs.map +1 -1
- package/fesm2022/ngwr-gauge.mjs +33 -4
- package/fesm2022/ngwr-gauge.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 +5 -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-image-cropper.mjs +149 -62
- package/fesm2022/ngwr-image-cropper.mjs.map +1 -1
- package/fesm2022/ngwr-input-number.mjs +12 -0
- package/fesm2022/ngwr-input-number.mjs.map +1 -1
- package/fesm2022/ngwr-lightbox.mjs +2 -2
- package/fesm2022/ngwr-lightbox.mjs.map +1 -1
- package/fesm2022/ngwr-line-chart.mjs +16 -5
- package/fesm2022/ngwr-line-chart.mjs.map +1 -1
- package/fesm2022/ngwr-meter-group-testing.mjs +7 -3
- package/fesm2022/ngwr-meter-group-testing.mjs.map +1 -1
- package/fesm2022/ngwr-meter-group.mjs +24 -4
- package/fesm2022/ngwr-meter-group.mjs.map +1 -1
- package/fesm2022/ngwr-page-header.mjs +10 -2
- package/fesm2022/ngwr-page-header.mjs.map +1 -1
- package/fesm2022/ngwr-popover.mjs +569 -7
- package/fesm2022/ngwr-popover.mjs.map +1 -1
- package/fesm2022/ngwr-select.mjs +73 -22
- package/fesm2022/ngwr-select.mjs.map +1 -1
- package/fesm2022/ngwr-sidebar.mjs +7 -0
- package/fesm2022/ngwr-sidebar.mjs.map +1 -1
- package/fesm2022/ngwr-sparkline.mjs +82 -8
- package/fesm2022/ngwr-sparkline.mjs.map +1 -1
- package/fesm2022/ngwr-table.mjs +1 -1
- package/fesm2022/ngwr-table.mjs.map +1 -1
- package/fesm2022/ngwr-toolbar.mjs +7 -0
- package/fesm2022/ngwr-toolbar.mjs.map +1 -1
- package/fesm2022/ngwr-version.mjs +1 -1
- package/fesm2022/ngwr-version.mjs.map +1 -1
- package/form/styles/_index.scss +11 -1
- package/gauge/styles/_index.scss +1 -0
- package/i18n/ar.json +3 -1
- package/i18n/cs.json +3 -1
- package/i18n/de.json +3 -1
- package/i18n/en.json +3 -1
- package/i18n/es.json +3 -1
- package/i18n/fr.json +3 -1
- package/i18n/he.json +3 -1
- package/i18n/hi.json +3 -1
- package/i18n/id.json +3 -1
- package/i18n/it.json +3 -1
- package/i18n/ja.json +3 -1
- package/i18n/ko.json +3 -1
- package/i18n/nl.json +3 -1
- package/i18n/pl.json +3 -1
- package/i18n/pt.json +3 -1
- package/i18n/ru.json +3 -1
- package/i18n/sv.json +3 -1
- package/i18n/tr.json +3 -1
- package/i18n/uk.json +3 -1
- package/i18n/vi.json +3 -1
- package/i18n/zh-TW.json +3 -1
- package/i18n/zh.json +3 -1
- package/input/styles/_index.scss +27 -2
- package/input-number/styles/_index.scss +69 -0
- package/lightbox/styles/_rules.scss +7 -1
- package/line-chart/styles/_index.scss +30 -2
- package/llms-full.txt +3 -3
- package/meter-group/styles/_index.scss +1 -0
- package/overlay/styles/_arrow.scss +27 -2
- package/package.json +1 -1
- package/page-header/styles/_index.scss +37 -1
- package/pagination/styles/_index.scss +9 -5
- package/popover/styles/_index.scss +73 -5
- package/select/styles/_index.scss +64 -13
- package/sidebar/styles/_index.scss +34 -2
- package/sparkline/styles/_index.scss +19 -0
- package/table/styles/_index.scss +55 -0
- package/toast/styles/_index.scss +9 -3
- package/toolbar/styles/_index.scss +35 -1
- package/tree/styles/_index.scss +45 -9
- package/types/ngwr-avatar.d.ts +15 -1
- package/types/ngwr-bar-chart.d.ts +19 -1
- package/types/ngwr-calendar-heatmap.d.ts +29 -1
- package/types/ngwr-checkbox.d.ts +2 -0
- package/types/ngwr-date-picker.d.ts +308 -43
- package/types/ngwr-donut-chart.d.ts +24 -1
- package/types/ngwr-dropdown.d.ts +22 -2
- package/types/ngwr-event-calendar.d.ts +8 -0
- package/types/ngwr-form.d.ts +35 -5
- package/types/ngwr-gauge.d.ts +20 -1
- package/types/ngwr-image-cropper.d.ts +48 -0
- package/types/ngwr-input-number.d.ts +7 -0
- package/types/ngwr-line-chart.d.ts +9 -1
- package/types/ngwr-meter-group-testing.d.ts +3 -2
- package/types/ngwr-meter-group.d.ts +14 -1
- package/types/ngwr-page-header.d.ts +10 -2
- package/types/ngwr-popover.d.ts +154 -3
- package/types/ngwr-select.d.ts +63 -6
- package/types/ngwr-sidebar.d.ts +7 -0
- package/types/ngwr-sparkline.d.ts +43 -1
- package/types/ngwr-toolbar.d.ts +7 -0
- package/types/ngwr-version.d.ts +1 -1
|
@@ -8,15 +8,19 @@
|
|
|
8
8
|
/// one of the 120-odd style entries that referenced tokens without pulling the
|
|
9
9
|
/// layer that declares them: `@use 'ngwr/event-calendar'` on its own emitted
|
|
10
10
|
/// 5.5 kB of rules carrying 57 unresolved `var(--wr-*)` references, so the grid
|
|
11
|
-
/// lines (`box-shadow`, hence no fallback) vanished and the text fell
|
|
12
|
-
/// black. The ten other entries that skip the load — `affix`, `density`
|
|
13
|
-
/// eight canvas / text-animation ones — reference no token at all, which
|
|
14
|
-
/// a sweep for "who is missing it" is not enough on its own.
|
|
11
|
+
/// lines (then a `box-shadow`, hence no fallback) vanished and the text fell
|
|
12
|
+
/// back to black. The ten other entries that skip the load — `affix`, `density`
|
|
13
|
+
/// and the eight canvas / text-animation ones — reference no token at all, which
|
|
14
|
+
/// is why a sweep for "who is missing it" is not enough on its own.
|
|
15
15
|
///
|
|
16
|
-
/// Grid lines are
|
|
17
|
-
///
|
|
18
|
-
///
|
|
19
|
-
///
|
|
16
|
+
/// Grid lines are borders on an absolutely positioned `::after`, never on the
|
|
17
|
+
/// cell itself. A border on the cell would take up layout space, and the whole
|
|
18
|
+
/// chip model depends on cell boxes being exactly contiguous: a band's
|
|
19
|
+
/// `calc(var(--span) * 100%)` is only the right width if one cell ends precisely
|
|
20
|
+
/// where the next begins. They used to be `box-shadow: inset`, which costs no
|
|
21
|
+
/// space either but has no logical form — and each cell drew its line on the
|
|
22
|
+
/// physical right and bottom whether or not that edge was the frame, so the last
|
|
23
|
+
/// column and the last row painted a second hairline against the grid's own.
|
|
20
24
|
@use '../../theme/styles';
|
|
21
25
|
|
|
22
26
|
.wr-event-calendar {
|
|
@@ -29,6 +33,17 @@
|
|
|
29
33
|
// dark enough that `--wr-color-on-surface-muted` on top of it lands at 3.6:1.
|
|
30
34
|
--wr-event-calendar-fill: var(--wr-color-fill);
|
|
31
35
|
--wr-event-calendar-cols: 7;
|
|
36
|
+
// A chip is a 20px strip carrying a 3px accent on its leading edge, and the
|
|
37
|
+
// library's small radius (6px) bent that accent into a bracket. Half of it
|
|
38
|
+
// keeps the corner soft and the accent straight, and still follows a theme
|
|
39
|
+
// that re-tunes `--wr-border-radius-sm`.
|
|
40
|
+
--wr-event-calendar-chip-radius: calc(var(--wr-border-radius-sm) / 2);
|
|
41
|
+
// Space above the first chip (below the day number, or below the header rule
|
|
42
|
+
// in the all-day band) and between stacked chips.
|
|
43
|
+
--wr-event-calendar-chip-gap: 0.25rem;
|
|
44
|
+
// How far a chip stands in from the lines of the cell it sits in — on both
|
|
45
|
+
// inline sides, and below a timed chip so back-to-back events do not merge.
|
|
46
|
+
--wr-event-calendar-chip-inset: 0.125rem;
|
|
32
47
|
|
|
33
48
|
display: flex;
|
|
34
49
|
flex-direction: column;
|
|
@@ -75,6 +90,9 @@
|
|
|
75
90
|
display: grid;
|
|
76
91
|
overflow: hidden;
|
|
77
92
|
border-radius: var(--wr-border-radius-base);
|
|
93
|
+
// The frame, spread OUTSIDE the box. It owns the four outer edges, which is
|
|
94
|
+
// why no cell draws a line on a side that meets it (see the grid lines
|
|
95
|
+
// below).
|
|
78
96
|
box-shadow: 0 0 0 1px var(--wr-event-calendar-line);
|
|
79
97
|
|
|
80
98
|
&--month {
|
|
@@ -82,7 +100,15 @@
|
|
|
82
100
|
}
|
|
83
101
|
|
|
84
102
|
&--time {
|
|
85
|
-
|
|
103
|
+
// The hook is the gutter's FLOOR, not its width. A fixed 3.75rem could not
|
|
104
|
+
// hold "09:00 AM" at the label's own padding, so the label wrapped onto a
|
|
105
|
+
// second line, the rows carrying one grew to 36px while the slot cells
|
|
106
|
+
// stayed 24px, and every column line broke for 12px at every hour — and a
|
|
107
|
+
// timed chip, sized in percentages of one 24px slot, ended short of its
|
|
108
|
+
// own end time by 12px per hour it crossed.
|
|
109
|
+
grid-template-columns:
|
|
110
|
+
minmax(var(--wr-event-calendar-gutter), max-content)
|
|
111
|
+
repeat(var(--wr-event-calendar-cols), minmax(0, 1fr));
|
|
86
112
|
}
|
|
87
113
|
}
|
|
88
114
|
|
|
@@ -94,13 +120,13 @@
|
|
|
94
120
|
|
|
95
121
|
&__weekday,
|
|
96
122
|
&__colhead {
|
|
123
|
+
position: relative;
|
|
97
124
|
padding: 0.5rem 0.375rem;
|
|
98
125
|
font-size: var(--wr-text-xs);
|
|
99
126
|
font-weight: var(--wr-font-weight-medium);
|
|
100
127
|
color: var(--wr-color-on-surface-muted);
|
|
101
128
|
text-align: center;
|
|
102
129
|
background: var(--wr-event-calendar-fill);
|
|
103
|
-
box-shadow: inset 0 -1px 0 var(--wr-event-calendar-line);
|
|
104
130
|
}
|
|
105
131
|
|
|
106
132
|
&__colhead {
|
|
@@ -122,11 +148,36 @@
|
|
|
122
148
|
&__gutter {
|
|
123
149
|
padding: 0 0.5rem;
|
|
124
150
|
font-size: var(--wr-text-xs);
|
|
151
|
+
// One line box exactly one em tall, so the nudge below centres the label on
|
|
152
|
+
// its line and the label can never make a slot row taller than the slot.
|
|
153
|
+
line-height: 1;
|
|
125
154
|
color: var(--wr-color-on-surface-muted);
|
|
126
155
|
text-align: end;
|
|
156
|
+
white-space: nowrap;
|
|
127
157
|
// Nudged up so the label reads as the line it labels, not the band below it.
|
|
158
|
+
// The transform moves the box, so the gutter draws no line of its own — the
|
|
159
|
+
// divider is the first day column's (see the grid lines below).
|
|
128
160
|
transform: translateY(-0.5em);
|
|
129
|
-
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// The all-day label names a band, not a line: its top level with the first
|
|
164
|
+
// chip's. Not centred on the chip — the first hour's label below is centred on
|
|
165
|
+
// the band's bottom rule, and a centred "All day" ran into it.
|
|
166
|
+
//
|
|
167
|
+
// And it does not size the column. The hour labels have to stay on one line,
|
|
168
|
+
// because a wrapped one grows its row; this label only grows the all-day row,
|
|
169
|
+
// which is a floor anyway. Left to `nowrap`, "Toute la journée" widened the
|
|
170
|
+
// French gutter from 72px to 107px and took a fifth off every day column at
|
|
171
|
+
// phone width, and the English label overrode a smaller gutter hook. A wrapped
|
|
172
|
+
// label grows the row through its bottom padding instead: the first hour's
|
|
173
|
+
// label is lifted 0.5em into this row, and the other 0.5em is the space a
|
|
174
|
+
// one-line "All day" already leaves above it. Without it the second line of
|
|
175
|
+
// "Toute la journée" overlapped "08:00 AM".
|
|
176
|
+
&__row--allday > &__gutter {
|
|
177
|
+
contain: inline-size;
|
|
178
|
+
padding-block: var(--wr-event-calendar-chip-gap) 1em;
|
|
179
|
+
white-space: normal;
|
|
180
|
+
transform: none;
|
|
130
181
|
}
|
|
131
182
|
|
|
132
183
|
&__cell {
|
|
@@ -137,7 +188,6 @@
|
|
|
137
188
|
position: relative;
|
|
138
189
|
min-width: 0;
|
|
139
190
|
cursor: pointer;
|
|
140
|
-
box-shadow: inset -1px -1px 0 var(--wr-event-calendar-line);
|
|
141
191
|
|
|
142
192
|
&:focus-visible {
|
|
143
193
|
z-index: 2;
|
|
@@ -146,7 +196,18 @@
|
|
|
146
196
|
}
|
|
147
197
|
|
|
148
198
|
&--day {
|
|
149
|
-
|
|
199
|
+
// A floor that knows the lanes. Every chip is absolutely positioned, so
|
|
200
|
+
// nothing in flow made the cell tall enough for its last lane: a third
|
|
201
|
+
// chip hung into the next week's row. `--wr-event-calendar-lanes` is the
|
|
202
|
+
// number of lanes the week row fills, set on the row and inherited through
|
|
203
|
+
// its `display: contents`.
|
|
204
|
+
min-height: max(
|
|
205
|
+
5.5rem,
|
|
206
|
+
calc(
|
|
207
|
+
1.75rem + var(--wr-event-calendar-chip-gap) + var(--wr-event-calendar-lanes, 0) *
|
|
208
|
+
(var(--wr-event-calendar-band-height) + var(--wr-event-calendar-chip-gap))
|
|
209
|
+
)
|
|
210
|
+
);
|
|
150
211
|
padding: 0.25rem;
|
|
151
212
|
}
|
|
152
213
|
|
|
@@ -164,22 +225,92 @@
|
|
|
164
225
|
}
|
|
165
226
|
|
|
166
227
|
&--allday {
|
|
167
|
-
|
|
168
|
-
|
|
228
|
+
// Same rule as a day cell: the band has no ceiling on lanes, and a second
|
|
229
|
+
// all-day event used to spill over the first hour of the grid.
|
|
230
|
+
min-height: calc(
|
|
231
|
+
var(--wr-event-calendar-chip-gap) + var(--wr-event-calendar-lanes, 1) *
|
|
232
|
+
(var(--wr-event-calendar-band-height) + var(--wr-event-calendar-chip-gap))
|
|
233
|
+
);
|
|
169
234
|
}
|
|
170
235
|
|
|
171
236
|
&--slot {
|
|
172
|
-
height
|
|
173
|
-
|
|
237
|
+
// A floor rather than a height, so a row that grows anyway stretches its
|
|
238
|
+
// cells with it instead of leaving their lines short.
|
|
239
|
+
min-height: var(--wr-event-calendar-slot-height);
|
|
174
240
|
}
|
|
241
|
+
}
|
|
175
242
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
243
|
+
// ------------------------------------------------------------- grid lines
|
|
244
|
+
//
|
|
245
|
+
// One hairline per edge, and one owner per line. A cell draws the line on its
|
|
246
|
+
// inline END and its block END, and draws nothing where that edge is the
|
|
247
|
+
// frame; the time grid draws hour lines on the block START of a major row,
|
|
248
|
+
// except where a header or the all-day band already put a rule there. Logical
|
|
249
|
+
// sides throughout, so the lines follow the columns under `dir="rtl"`.
|
|
250
|
+
&__weekday,
|
|
251
|
+
&__colhead,
|
|
252
|
+
&__cell {
|
|
253
|
+
&::after {
|
|
254
|
+
position: absolute;
|
|
255
|
+
inset: 0;
|
|
256
|
+
pointer-events: none;
|
|
257
|
+
content: '';
|
|
258
|
+
border: 0 solid var(--wr-event-calendar-line);
|
|
180
259
|
}
|
|
181
260
|
}
|
|
182
261
|
|
|
262
|
+
&__weekday::after,
|
|
263
|
+
&__colhead::after {
|
|
264
|
+
border-block-end-width: 1px;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
&__cell--day::after,
|
|
268
|
+
&__cell--allday::after {
|
|
269
|
+
border-inline-end-width: 1px;
|
|
270
|
+
border-block-end-width: 1px;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
&__cell--slot::after {
|
|
274
|
+
border-inline-end-width: 1px;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
&__cell--major::after {
|
|
278
|
+
border-block-start-width: 1px;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// The divider between the gutter and the days. The first day column draws it,
|
|
282
|
+
// in every row, because the gutter's labels are moved with a transform that
|
|
283
|
+
// would carry any line of the gutter's own up with them — it used to stop
|
|
284
|
+
// 0.5em short of the grid's bottom edge.
|
|
285
|
+
&__gutter + &__colhead::after,
|
|
286
|
+
&__gutter + &__cell::after {
|
|
287
|
+
border-inline-start-width: 1px;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// The first slot row sits directly under the header rule or the all-day rule.
|
|
291
|
+
&__row:first-child + &__row > &__cell--major::after,
|
|
292
|
+
&__row--allday + &__row > &__cell--major::after {
|
|
293
|
+
border-block-start-width: 0;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Edges that meet the frame.
|
|
297
|
+
&__row > :last-child::after {
|
|
298
|
+
border-inline-end-width: 0;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
&__row:last-child > &__cell::after {
|
|
302
|
+
border-block-end-width: 0;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Under the focus ring, never over it. A focused cell takes `z-index: 2`, so
|
|
306
|
+
// it is a stacking context, and Chromium paints a positioned `::after` inside
|
|
307
|
+
// one AFTER the cell's own outline: the inset ring lost its outer pixel on
|
|
308
|
+
// every side the cell draws a line on. A negative z-index still paints above
|
|
309
|
+
// the cell's background, so a muted day keeps its lines.
|
|
310
|
+
&__cell:focus-visible::after {
|
|
311
|
+
z-index: -1;
|
|
312
|
+
}
|
|
313
|
+
|
|
183
314
|
&__daynum {
|
|
184
315
|
display: inline-flex;
|
|
185
316
|
align-items: center;
|
|
@@ -207,7 +338,7 @@
|
|
|
207
338
|
background: color-mix(in srgb, var(--wr-event-calendar-color) 18%, transparent);
|
|
208
339
|
border: 0;
|
|
209
340
|
border-inline-start: 3px solid var(--wr-event-calendar-color);
|
|
210
|
-
border-radius: var(--wr-
|
|
341
|
+
border-radius: var(--wr-event-calendar-chip-radius);
|
|
211
342
|
transition: filter var(--wr-transition-short);
|
|
212
343
|
|
|
213
344
|
&:hover {
|
|
@@ -221,19 +352,31 @@
|
|
|
221
352
|
}
|
|
222
353
|
|
|
223
354
|
&--band {
|
|
224
|
-
inset-inline-start:
|
|
355
|
+
inset-inline-start: var(--wr-event-calendar-chip-inset);
|
|
356
|
+
// `offset` is where the chips start — below the day number in a month
|
|
357
|
+
// cell, at the top of the all-day band — and the first lane keeps one gap
|
|
358
|
+
// clear of it, the way every later lane keeps one clear of the chip above.
|
|
225
359
|
top: calc(
|
|
226
|
-
var(--wr-event-calendar-offset) + var(--wr-event-calendar-lane) *
|
|
227
|
-
(var(--wr-event-calendar-band-height) +
|
|
360
|
+
var(--wr-event-calendar-offset) + var(--wr-event-calendar-chip-gap) + var(--wr-event-calendar-lane) *
|
|
361
|
+
(var(--wr-event-calendar-band-height) + var(--wr-event-calendar-chip-gap))
|
|
228
362
|
);
|
|
229
363
|
// Reaching across `--span` cells: each is exactly 100% of this one,
|
|
230
364
|
// because the grid has no gaps and the lines cost no layout space.
|
|
231
|
-
width: calc(var(--wr-event-calendar-span) * 100% -
|
|
365
|
+
width: calc(var(--wr-event-calendar-span) * 100% - 2 * var(--wr-event-calendar-chip-inset));
|
|
232
366
|
height: var(--wr-event-calendar-band-height);
|
|
233
367
|
padding: 0 0.375rem;
|
|
234
368
|
}
|
|
235
369
|
|
|
236
370
|
&--time {
|
|
371
|
+
// The template writes the lane geometry as custom properties rather than
|
|
372
|
+
// as `inset-inline-start` / `width` / `height` directly, so the inset can
|
|
373
|
+
// be taken off here: an inline style cannot be adjusted by a stylesheet,
|
|
374
|
+
// and a chip filling its lane to the pixel covered the column line and
|
|
375
|
+
// touched its neighbour in the next lane and the event after it. `top`
|
|
376
|
+
// needs no adjustment and stays an inline `top`.
|
|
377
|
+
inset-inline-start: calc(var(--wr-event-calendar-inline-start) + var(--wr-event-calendar-chip-inset));
|
|
378
|
+
width: calc(var(--wr-event-calendar-inline-size) - 2 * var(--wr-event-calendar-chip-inset));
|
|
379
|
+
height: calc(var(--wr-event-calendar-block-size) - var(--wr-event-calendar-chip-inset));
|
|
237
380
|
padding: 0.125rem 0.25rem;
|
|
238
381
|
// The clock and the title share a line while there is room for both and
|
|
239
382
|
// the title drops below it when there is not. `flex-direction: column`
|
|
@@ -250,6 +393,10 @@
|
|
|
250
393
|
row-gap: 0;
|
|
251
394
|
min-height: 1.25rem;
|
|
252
395
|
white-space: normal;
|
|
396
|
+
// The chip's height is set from outside and never from its content, so
|
|
397
|
+
// size containment costs nothing, and it lets the title ask how many lines
|
|
398
|
+
// the chip holds (see `&-title`).
|
|
399
|
+
container: wr-event-calendar-chip / size;
|
|
253
400
|
}
|
|
254
401
|
|
|
255
402
|
// A segment clipped by the edge of the view loses the rounding on that side,
|
|
@@ -280,8 +427,8 @@
|
|
|
280
427
|
// one-slot event has — so the label was clipped through its own digits.
|
|
281
428
|
white-space: nowrap;
|
|
282
429
|
// The muted role is calibrated against the plain surface, and the chip
|
|
283
|
-
// puts an 18% wash of the event colour under it (
|
|
284
|
-
// the whole margin: 4.18–4.28:1 across the shipped demo colours, under AA
|
|
430
|
+
// puts an 18% wash of the event colour under it (its `background`) — which
|
|
431
|
+
// spent the whole margin: 4.18–4.28:1 across the shipped demo colours, under AA
|
|
285
432
|
// in LIGHT for every one of them. Mixing toward the page's own ink is the
|
|
286
433
|
// `-ink` recipe: `--wr-color-on-surface` flips per theme, so one
|
|
287
434
|
// declaration darkens on a light tint and lightens on a dark one. 85% is
|
|
@@ -307,18 +454,28 @@
|
|
|
307
454
|
// The one thing in a day cell that is NOT absolutely positioned, and it has
|
|
308
455
|
// to stay that way. Its lane is `maxLanes` — one past the last chip — so
|
|
309
456
|
// absolute put it at 1.75rem + 3 x 1.375rem = 5.875rem inside a cell whose
|
|
310
|
-
// floor
|
|
457
|
+
// floor was 5.5rem: the entire button landed in the following week's row,
|
|
311
458
|
// which comes later in the DOM and (correctly, per the `__cell` note above)
|
|
312
459
|
// has no z-index to lose to it, so it swallowed every click. A block box in
|
|
313
|
-
// flow lands
|
|
314
|
-
// precisely the 1.75rem the chips offset by, so only the
|
|
315
|
-
// skipped — and makes the cell GROW to hold it instead.
|
|
316
|
-
// cell is a floor, not a size; the row stretches its
|
|
460
|
+
// flow lands exactly where that lane would start — the day-number line box is
|
|
461
|
+
// precisely the 1.75rem the chips offset by, so only the first gap and the
|
|
462
|
+
// lanes have to be skipped — and makes the cell GROW to hold it instead.
|
|
463
|
+
// `min-height` on the cell is a floor, not a size; the row stretches its
|
|
464
|
+
// siblings to match.
|
|
465
|
+
//
|
|
466
|
+
// It is as tall as a chip. At its own 18px line its centre sat 23px below the
|
|
467
|
+
// centre of the chip above it, one pixel short of the 24px WCAG 2.5.8's
|
|
468
|
+
// spacing exception asks for. axe flagged both targets wherever the cell was
|
|
469
|
+
// narrow enough to put the two centres nearly in line.
|
|
317
470
|
&__more {
|
|
318
471
|
display: block;
|
|
319
472
|
width: fit-content;
|
|
320
|
-
|
|
321
|
-
margin-
|
|
473
|
+
min-height: var(--wr-event-calendar-band-height);
|
|
474
|
+
margin-top: calc(
|
|
475
|
+
var(--wr-event-calendar-chip-gap) + var(--wr-event-calendar-lane) *
|
|
476
|
+
(var(--wr-event-calendar-band-height) + var(--wr-event-calendar-chip-gap))
|
|
477
|
+
);
|
|
478
|
+
margin-inline-start: var(--wr-event-calendar-chip-inset);
|
|
322
479
|
padding: 0 0.375rem;
|
|
323
480
|
font: inherit;
|
|
324
481
|
font-size: var(--wr-text-xs);
|
|
@@ -354,3 +511,20 @@
|
|
|
354
511
|
user-select: none;
|
|
355
512
|
}
|
|
356
513
|
}
|
|
514
|
+
|
|
515
|
+
// A timed chip with room for ONE line: two of the chip's 1.2 line boxes are
|
|
516
|
+
// 2.4em, and the query measures the content box. There the title stays on the
|
|
517
|
+
// clock's line and ends in an ellipsis. Left to wrap, it dropped to a second
|
|
518
|
+
// line that started 14.4px down a 16px content box, so a one-slot chip showed
|
|
519
|
+
// the top of the title's letters and nothing else. On the docs page that
|
|
520
|
+
// happened at 1280px wide, where the week columns are 118px. A chip narrower
|
|
521
|
+
// than its clock plus the gap still wraps the title, because a container query
|
|
522
|
+
// cannot turn off `flex-wrap` on the chip itself; at that width the clock is
|
|
523
|
+
// already cut off.
|
|
524
|
+
@container wr-event-calendar-chip (height < 2.4em) {
|
|
525
|
+
.wr-event-calendar__chip-title {
|
|
526
|
+
flex: 1 1 0;
|
|
527
|
+
min-width: 0;
|
|
528
|
+
white-space: nowrap;
|
|
529
|
+
}
|
|
530
|
+
}
|
package/fesm2022/ngwr-avatar.mjs
CHANGED
|
@@ -21,6 +21,19 @@ const DEFAULT_SIZE = '6rem';
|
|
|
21
21
|
* spinner is drawn over it; with nothing projected, the spinner marks the
|
|
22
22
|
* loading state instead. See `alt` for what a screen reader hears.
|
|
23
23
|
*
|
|
24
|
+
* Projected initials take the text size around the avatar, capped at
|
|
25
|
+
* `--wr-avatar-initials-scale` (`0.375`) of the box, so they shrink only where
|
|
26
|
+
* they would not fit: a 16px avatar in a 14px chip draws 6px letters, while a
|
|
27
|
+
* `6rem` one keeps the surrounding size up to 36px and is capped only past it.
|
|
28
|
+
* Anything projected that is sized in `em` follows the cap too — a `<wr-icon>`
|
|
29
|
+
* at its default `1.2em` included — so pin a `px` or `rem` size on the projected
|
|
30
|
+
* element, or override the hook, to keep it. A `font-size` rule on the avatar
|
|
31
|
+
* itself still wins, unless it sits in a cascade layer (Tailwind v4's utilities
|
|
32
|
+
* do), which ngwr's unlayered stylesheet beats whatever the selector.
|
|
33
|
+
*
|
|
34
|
+
* The initials are inert: `pointer-events: none` hands a click on them to whatever
|
|
35
|
+
* holds the avatar, and `user-select: none` keeps them out of a text selection.
|
|
36
|
+
*
|
|
24
37
|
* @example
|
|
25
38
|
* ```html
|
|
26
39
|
* <wr-avatar url="/me.png" alt="Roman" size="3rem" shape="circle" />
|
|
@@ -61,7 +74,8 @@ class WrAvatar {
|
|
|
61
74
|
shape = input('rounded', /* @ts-ignore */
|
|
62
75
|
...(ngDevMode ? [{ debugName: "shape" }] : /* istanbul ignore next */ []));
|
|
63
76
|
/**
|
|
64
|
-
* Box size. See {@link WrAvatarSize} for accepted values.
|
|
77
|
+
* Box size. See {@link WrAvatarSize} for accepted values. The resolved value is
|
|
78
|
+
* published on the host as `--wr-avatar-size`.
|
|
65
79
|
*
|
|
66
80
|
* @default '6rem'
|
|
67
81
|
*/
|
|
@@ -156,7 +170,7 @@ class WrAvatar {
|
|
|
156
170
|
this.failed.set(true);
|
|
157
171
|
}
|
|
158
172
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: WrAvatar, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
159
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: WrAvatar, isStandalone: true, selector: "wr-avatar", inputs: { url: { classPropertyName: "url", publicName: "url", isSignal: true, isRequired: false, transformFunction: null }, alt: { classPropertyName: "alt", publicName: "alt", isSignal: true, isRequired: false, transformFunction: null }, shape: { classPropertyName: "shape", publicName: "shape", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()", "style.width": "cssSize()", "style.height": "cssSize()" } }, viewQueries: [{ propertyName: "img", first: true, predicate: ["img"], descendants: true, isSignal: true }], ngImport: i0, template: "<span class=\"wr-avatar__content\" [attr.aria-hidden]=\"fallbackHidden() ? 'true' : null\"><ng-content /></span>\n\n@if (url(); as src) {\n @if (!failed()) {\n <img\n #img\n class=\"wr-avatar__img\"\n [src]=\"src\"\n [alt]=\"resolvedAlt()\"\n [attr.width]=\"pxSize()\"\n [attr.height]=\"pxSize()\"\n (load)=\"onImageLoad()\"\n (error)=\"onImageError()\"\n decoding=\"async\"\n />\n\n @if (!loaded()) {\n <wr-spinner class=\"wr-avatar__spin\" />\n }\n }\n}\n", dependencies: [{ kind: "component", type: WrSpinner, selector: "wr-spinner", inputs: ["size", "ariaLabel"] }], encapsulation: i0.ViewEncapsulation.None });
|
|
173
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: WrAvatar, isStandalone: true, selector: "wr-avatar", inputs: { url: { classPropertyName: "url", publicName: "url", isSignal: true, isRequired: false, transformFunction: null }, alt: { classPropertyName: "alt", publicName: "alt", isSignal: true, isRequired: false, transformFunction: null }, shape: { classPropertyName: "shape", publicName: "shape", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()", "style.width": "cssSize()", "style.height": "cssSize()", "style.--wr-avatar-size": "cssSize()" } }, viewQueries: [{ propertyName: "img", first: true, predicate: ["img"], descendants: true, isSignal: true }], ngImport: i0, template: "<span class=\"wr-avatar__content\" [attr.aria-hidden]=\"fallbackHidden() ? 'true' : null\"><ng-content /></span>\n\n@if (url(); as src) {\n @if (!failed()) {\n <img\n #img\n class=\"wr-avatar__img\"\n [src]=\"src\"\n [alt]=\"resolvedAlt()\"\n [attr.width]=\"pxSize()\"\n [attr.height]=\"pxSize()\"\n (load)=\"onImageLoad()\"\n (error)=\"onImageError()\"\n decoding=\"async\"\n />\n\n @if (!loaded()) {\n <wr-spinner class=\"wr-avatar__spin\" />\n }\n }\n}\n", dependencies: [{ kind: "component", type: WrSpinner, selector: "wr-spinner", inputs: ["size", "ariaLabel"] }], encapsulation: i0.ViewEncapsulation.None });
|
|
160
174
|
}
|
|
161
175
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: WrAvatar, decorators: [{
|
|
162
176
|
type: Component,
|
|
@@ -164,6 +178,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
|
|
|
164
178
|
'[class]': 'classes()',
|
|
165
179
|
'[style.width]': 'cssSize()',
|
|
166
180
|
'[style.height]': 'cssSize()',
|
|
181
|
+
// What the stylesheet sizes projected initials against. Always px or rem: a
|
|
182
|
+
// value `resolveCssSize` has no pixels for falls back to the default box.
|
|
183
|
+
'[style.--wr-avatar-size]': 'cssSize()',
|
|
167
184
|
}, imports: [WrSpinner], template: "<span class=\"wr-avatar__content\" [attr.aria-hidden]=\"fallbackHidden() ? 'true' : null\"><ng-content /></span>\n\n@if (url(); as src) {\n @if (!failed()) {\n <img\n #img\n class=\"wr-avatar__img\"\n [src]=\"src\"\n [alt]=\"resolvedAlt()\"\n [attr.width]=\"pxSize()\"\n [attr.height]=\"pxSize()\"\n (load)=\"onImageLoad()\"\n (error)=\"onImageError()\"\n decoding=\"async\"\n />\n\n @if (!loaded()) {\n <wr-spinner class=\"wr-avatar__spin\" />\n }\n }\n}\n" }]
|
|
168
185
|
}], ctorParameters: () => [], propDecorators: { url: [{ type: i0.Input, args: [{ isSignal: true, alias: "url", required: false }] }], alt: [{ type: i0.Input, args: [{ isSignal: true, alias: "alt", required: false }] }], shape: [{ type: i0.Input, args: [{ isSignal: true, alias: "shape", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], img: [{ type: i0.ViewChild, args: ['img', { isSignal: true }] }] } });
|
|
169
186
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ngwr-avatar.mjs","sources":["../../../projects/lib/avatar/avatar.ts","../../../projects/lib/avatar/avatar.html","../../../projects/lib/avatar/ngwr-avatar.ts"],"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 {\n Component,\n type ElementRef,\n ViewEncapsulation,\n afterNextRender,\n computed,\n effect,\n input,\n signal,\n untracked,\n viewChild,\n} from '@angular/core';\n\nimport { useI18nText } from 'ngwr/i18n';\nimport { WrSpinner } from 'ngwr/spinner';\nimport { resolveCssSize, type ResolvedCssSize } from 'ngwr/utils';\n\nimport type { WrAvatarShape, WrAvatarSize } from './interfaces';\n\nconst DEFAULT_SIZE: WrAvatarSize = '6rem';\n\n/**\n * Image avatar with configurable corner treatment.\n *\n * Projected content (typically initials) is the FALLBACK: it shows with no\n * `url`, while the image loads and after it fails, and leaves the paint once the\n * image has loaded. It is projected into a `.wr-avatar__content` wrapper, so a\n * `wr-avatar > …` child selector no longer reaches it. While it is there, no\n * spinner is drawn over it; with nothing projected, the spinner marks the\n * loading state instead. See `alt` for what a screen reader hears.\n *\n * @example\n * ```html\n * <wr-avatar url=\"/me.png\" alt=\"Roman\" size=\"3rem\" shape=\"circle\" />\n * <wr-avatar [url]=\"photo\" alt=\"Roman Khegay\" [size]=\"48\">RK</wr-avatar>\n * ```\n *\n * @see https://ngwr.dev/reference/components/avatar\n */\n@Component({\n selector: 'wr-avatar',\n templateUrl: './avatar.html',\n encapsulation: ViewEncapsulation.None,\n host: {\n '[class]': 'classes()',\n '[style.width]': 'cssSize()',\n '[style.height]': 'cssSize()',\n },\n imports: [WrSpinner],\n})\nexport class WrAvatar {\n /**\n * Image URL. Projected content (e.g. initials) shows while it is unset,\n * loading or broken, and gives way once the image has loaded.\n *\n * @default null\n */\n readonly url = input<string | null>(null);\n\n /**\n * Alt text for the image. Falls back to `avatar.alt`, then `'Avatar'` — pass\n * the person's name where you have it, since that is what a reader wants.\n *\n * With an `alt`, projected initials are hidden from assistive technology while\n * an image is in play, so the name is announced once. Without one, the image\n * carries only the generic name, so the initials stay announced beside it —\n * visually hidden once it has loaded, never dropped.\n *\n * @default null\n */\n readonly alt = input<string | null>(null);\n\n protected readonly resolvedAlt = useI18nText(this.alt, 'avatar.alt', 'Avatar');\n\n /**\n * Corner treatment. `rounded` (default) is a soft rounded square,\n * `circle` is the classic profile avatar, `squircle` is the iOS look.\n *\n * @default 'rounded'\n */\n readonly shape = input<WrAvatarShape>('rounded');\n\n /**\n * Box size. See {@link WrAvatarSize} for accepted values.\n *\n * @default '6rem'\n */\n readonly size = input<WrAvatarSize>(DEFAULT_SIZE);\n\n protected readonly loaded = signal(false);\n\n /**\n * Whether the image failed. Without this the spinner spun forever on a broken\n * URL — the one state where an avatar is guaranteed to have projected initials\n * worth showing instead.\n */\n protected readonly failed = signal(false);\n\n private readonly img = viewChild<ElementRef<HTMLImageElement>>('img');\n\n constructor() {\n // A new URL is a new attempt: after a failure the fallback would otherwise\n // stay put for an image that loads perfectly well.\n effect(() => {\n this.url();\n untracked(() => {\n this.loaded.set(false);\n this.failed.set(false);\n });\n });\n\n afterNextRender(() => this.adoptSettledImage());\n }\n\n /**\n * A prerendered `<img>` starts loading when the browser parses the HTML, and\n * `(load)` / `(error)` are only attached at hydration — so on a slow bundle the\n * image settles first, both events go nowhere, and the avatar sits on its\n * fallback over a photo it already has (or names a broken image it never\n * learns about). Only the FIRST render can race: every later `<img>` is\n * created on the client with its listeners in place before `src` is set.\n */\n private adoptSettledImage(): void {\n const img = this.img()?.nativeElement;\n if (!img?.complete || this.loaded() || this.failed()) return;\n\n if (img.naturalWidth > 0) {\n this.loaded.set(true);\n return;\n }\n\n // Zero wide is a broken image, or an SVG with no intrinsic size in an engine\n // that reports it as 0 — only the element can tell. Setting `src` to the\n // value it already has makes it fire `load` or `error` again, to listeners\n // that exist now.\n const src = img.getAttribute('src');\n if (src !== null) img.setAttribute('src', src);\n }\n\n protected readonly resolved = computed<ResolvedCssSize>(() => {\n const result = resolveCssSize(this.size(), { defaultValue: DEFAULT_SIZE });\n if (result.pxValue == null || result.pxValue <= 0) {\n return resolveCssSize(DEFAULT_SIZE, { defaultValue: DEFAULT_SIZE });\n }\n return result;\n });\n\n /**\n * Whether the projected fallback is `aria-hidden`: an `<img>` is in the DOM\n * (loading or loaded) AND the consumer named it. Then the image's `alt` owns\n * the name, announced once and unchanged at the moment the photo arrives.\n * Without a real `alt` the image is only \"Avatar\", and hiding the initials\n * would drop the one thing that says who this is.\n *\n * The stylesheet keys on the same attribute once `--loaded`: a hidden fallback\n * leaves the paint through `visibility`, a named-by-initials one is clipped and\n * stays in the accessibility tree. While loading it is painted either way.\n */\n protected readonly fallbackHidden = computed(() => !!this.url() && !this.failed() && !!this.alt()?.trim());\n\n protected readonly cssSize = computed(() => this.resolved().cssValue);\n protected readonly pxSize = computed(() => this.resolved().pxValue);\n\n protected readonly classes = computed(() => {\n const parts = ['wr-avatar'];\n const shape = this.shape();\n if (shape !== 'rounded') parts.push(`wr-avatar--${shape}`);\n if (this.loaded()) parts.push('wr-avatar--loaded');\n return parts.join(' ');\n });\n\n protected onImageLoad(): void {\n this.loaded.set(true);\n }\n\n protected onImageError(): void {\n this.failed.set(true);\n }\n}\n","<span class=\"wr-avatar__content\" [attr.aria-hidden]=\"fallbackHidden() ? 'true' : null\"><ng-content /></span>\n\n@if (url(); as src) {\n @if (!failed()) {\n <img\n #img\n class=\"wr-avatar__img\"\n [src]=\"src\"\n [alt]=\"resolvedAlt()\"\n [attr.width]=\"pxSize()\"\n [attr.height]=\"pxSize()\"\n (load)=\"onImageLoad()\"\n (error)=\"onImageError()\"\n decoding=\"async\"\n />\n\n @if (!loaded()) {\n <wr-spinner class=\"wr-avatar__spin\" />\n }\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ngwr-avatar.mjs","sources":["../../../projects/lib/avatar/avatar.ts","../../../projects/lib/avatar/avatar.html","../../../projects/lib/avatar/ngwr-avatar.ts"],"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 {\n Component,\n type ElementRef,\n ViewEncapsulation,\n afterNextRender,\n computed,\n effect,\n input,\n signal,\n untracked,\n viewChild,\n} from '@angular/core';\n\nimport { useI18nText } from 'ngwr/i18n';\nimport { WrSpinner } from 'ngwr/spinner';\nimport { resolveCssSize, type ResolvedCssSize } from 'ngwr/utils';\n\nimport type { WrAvatarShape, WrAvatarSize } from './interfaces';\n\nconst DEFAULT_SIZE: WrAvatarSize = '6rem';\n\n/**\n * Image avatar with configurable corner treatment.\n *\n * Projected content (typically initials) is the FALLBACK: it shows with no\n * `url`, while the image loads and after it fails, and leaves the paint once the\n * image has loaded. It is projected into a `.wr-avatar__content` wrapper, so a\n * `wr-avatar > …` child selector no longer reaches it. While it is there, no\n * spinner is drawn over it; with nothing projected, the spinner marks the\n * loading state instead. See `alt` for what a screen reader hears.\n *\n * Projected initials take the text size around the avatar, capped at\n * `--wr-avatar-initials-scale` (`0.375`) of the box, so they shrink only where\n * they would not fit: a 16px avatar in a 14px chip draws 6px letters, while a\n * `6rem` one keeps the surrounding size up to 36px and is capped only past it.\n * Anything projected that is sized in `em` follows the cap too — a `<wr-icon>`\n * at its default `1.2em` included — so pin a `px` or `rem` size on the projected\n * element, or override the hook, to keep it. A `font-size` rule on the avatar\n * itself still wins, unless it sits in a cascade layer (Tailwind v4's utilities\n * do), which ngwr's unlayered stylesheet beats whatever the selector.\n *\n * The initials are inert: `pointer-events: none` hands a click on them to whatever\n * holds the avatar, and `user-select: none` keeps them out of a text selection.\n *\n * @example\n * ```html\n * <wr-avatar url=\"/me.png\" alt=\"Roman\" size=\"3rem\" shape=\"circle\" />\n * <wr-avatar [url]=\"photo\" alt=\"Roman Khegay\" [size]=\"48\">RK</wr-avatar>\n * ```\n *\n * @see https://ngwr.dev/reference/components/avatar\n */\n@Component({\n selector: 'wr-avatar',\n templateUrl: './avatar.html',\n encapsulation: ViewEncapsulation.None,\n host: {\n '[class]': 'classes()',\n '[style.width]': 'cssSize()',\n '[style.height]': 'cssSize()',\n // What the stylesheet sizes projected initials against. Always px or rem: a\n // value `resolveCssSize` has no pixels for falls back to the default box.\n '[style.--wr-avatar-size]': 'cssSize()',\n },\n imports: [WrSpinner],\n})\nexport class WrAvatar {\n /**\n * Image URL. Projected content (e.g. initials) shows while it is unset,\n * loading or broken, and gives way once the image has loaded.\n *\n * @default null\n */\n readonly url = input<string | null>(null);\n\n /**\n * Alt text for the image. Falls back to `avatar.alt`, then `'Avatar'` — pass\n * the person's name where you have it, since that is what a reader wants.\n *\n * With an `alt`, projected initials are hidden from assistive technology while\n * an image is in play, so the name is announced once. Without one, the image\n * carries only the generic name, so the initials stay announced beside it —\n * visually hidden once it has loaded, never dropped.\n *\n * @default null\n */\n readonly alt = input<string | null>(null);\n\n protected readonly resolvedAlt = useI18nText(this.alt, 'avatar.alt', 'Avatar');\n\n /**\n * Corner treatment. `rounded` (default) is a soft rounded square,\n * `circle` is the classic profile avatar, `squircle` is the iOS look.\n *\n * @default 'rounded'\n */\n readonly shape = input<WrAvatarShape>('rounded');\n\n /**\n * Box size. See {@link WrAvatarSize} for accepted values. The resolved value is\n * published on the host as `--wr-avatar-size`.\n *\n * @default '6rem'\n */\n readonly size = input<WrAvatarSize>(DEFAULT_SIZE);\n\n protected readonly loaded = signal(false);\n\n /**\n * Whether the image failed. Without this the spinner spun forever on a broken\n * URL — the one state where an avatar is guaranteed to have projected initials\n * worth showing instead.\n */\n protected readonly failed = signal(false);\n\n private readonly img = viewChild<ElementRef<HTMLImageElement>>('img');\n\n constructor() {\n // A new URL is a new attempt: after a failure the fallback would otherwise\n // stay put for an image that loads perfectly well.\n effect(() => {\n this.url();\n untracked(() => {\n this.loaded.set(false);\n this.failed.set(false);\n });\n });\n\n afterNextRender(() => this.adoptSettledImage());\n }\n\n /**\n * A prerendered `<img>` starts loading when the browser parses the HTML, and\n * `(load)` / `(error)` are only attached at hydration — so on a slow bundle the\n * image settles first, both events go nowhere, and the avatar sits on its\n * fallback over a photo it already has (or names a broken image it never\n * learns about). Only the FIRST render can race: every later `<img>` is\n * created on the client with its listeners in place before `src` is set.\n */\n private adoptSettledImage(): void {\n const img = this.img()?.nativeElement;\n if (!img?.complete || this.loaded() || this.failed()) return;\n\n if (img.naturalWidth > 0) {\n this.loaded.set(true);\n return;\n }\n\n // Zero wide is a broken image, or an SVG with no intrinsic size in an engine\n // that reports it as 0 — only the element can tell. Setting `src` to the\n // value it already has makes it fire `load` or `error` again, to listeners\n // that exist now.\n const src = img.getAttribute('src');\n if (src !== null) img.setAttribute('src', src);\n }\n\n protected readonly resolved = computed<ResolvedCssSize>(() => {\n const result = resolveCssSize(this.size(), { defaultValue: DEFAULT_SIZE });\n if (result.pxValue == null || result.pxValue <= 0) {\n return resolveCssSize(DEFAULT_SIZE, { defaultValue: DEFAULT_SIZE });\n }\n return result;\n });\n\n /**\n * Whether the projected fallback is `aria-hidden`: an `<img>` is in the DOM\n * (loading or loaded) AND the consumer named it. Then the image's `alt` owns\n * the name, announced once and unchanged at the moment the photo arrives.\n * Without a real `alt` the image is only \"Avatar\", and hiding the initials\n * would drop the one thing that says who this is.\n *\n * The stylesheet keys on the same attribute once `--loaded`: a hidden fallback\n * leaves the paint through `visibility`, a named-by-initials one is clipped and\n * stays in the accessibility tree. While loading it is painted either way.\n */\n protected readonly fallbackHidden = computed(() => !!this.url() && !this.failed() && !!this.alt()?.trim());\n\n protected readonly cssSize = computed(() => this.resolved().cssValue);\n protected readonly pxSize = computed(() => this.resolved().pxValue);\n\n protected readonly classes = computed(() => {\n const parts = ['wr-avatar'];\n const shape = this.shape();\n if (shape !== 'rounded') parts.push(`wr-avatar--${shape}`);\n if (this.loaded()) parts.push('wr-avatar--loaded');\n return parts.join(' ');\n });\n\n protected onImageLoad(): void {\n this.loaded.set(true);\n }\n\n protected onImageError(): void {\n this.failed.set(true);\n }\n}\n","<span class=\"wr-avatar__content\" [attr.aria-hidden]=\"fallbackHidden() ? 'true' : null\"><ng-content /></span>\n\n@if (url(); as src) {\n @if (!failed()) {\n <img\n #img\n class=\"wr-avatar__img\"\n [src]=\"src\"\n [alt]=\"resolvedAlt()\"\n [attr.width]=\"pxSize()\"\n [attr.height]=\"pxSize()\"\n (load)=\"onImageLoad()\"\n (error)=\"onImageError()\"\n decoding=\"async\"\n />\n\n @if (!loaded()) {\n <wr-spinner class=\"wr-avatar__spin\" />\n }\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { coerceBooleanProperty, coerceNumberProperty } from '@angular/cdk/coercion';
|
|
2
2
|
import * as i0 from '@angular/core';
|
|
3
|
-
import { input, computed, ViewEncapsulation, Component } from '@angular/core';
|
|
3
|
+
import { input, viewChildren, computed, ViewEncapsulation, Component } from '@angular/core';
|
|
4
|
+
import { useChartTooltip } from 'ngwr/popover';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* @license
|
|
@@ -35,6 +36,13 @@ class WrBarChart {
|
|
|
35
36
|
height = input(200, { ...(ngDevMode ? { debugName: "height" } : /* istanbul ignore next */ {}), transform: (v) => Math.max(40, coerceNumberProperty(v, 200)) });
|
|
36
37
|
/** Explicit max value. `0` = auto. @default 0 */
|
|
37
38
|
max = input(0, { ...(ngDevMode ? { debugName: "max" } : /* istanbul ignore next */ {}), transform: (v) => Math.max(0, coerceNumberProperty(v, 0)) });
|
|
39
|
+
/**
|
|
40
|
+
* Show a tooltip with the bar's label and value on hover. Pointer only — the
|
|
41
|
+
* column's accessible name already carries both. @default true
|
|
42
|
+
*/
|
|
43
|
+
tooltip = input(true, { ...(ngDevMode ? { debugName: "tooltip" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
|
|
44
|
+
barEls = viewChildren('barEl', /* @ts-ignore */
|
|
45
|
+
...(ngDevMode ? [{ debugName: "barEls" }] : /* istanbul ignore next */ []));
|
|
38
46
|
/**
|
|
39
47
|
* A datum's value, with anything non-finite read as `0`.
|
|
40
48
|
*
|
|
@@ -74,13 +82,30 @@ class WrBarChart {
|
|
|
74
82
|
};
|
|
75
83
|
}), /* @ts-ignore */
|
|
76
84
|
...(ngDevMode ? [{ debugName: "bars" }] : /* istanbul ignore next */ []));
|
|
85
|
+
/**
|
|
86
|
+
* One tooltip for the whole chart, with the value exactly as it is printed above
|
|
87
|
+
* the bar. It points at the top edge of the BAR — above it, below it when there is
|
|
88
|
+
* no room — rather than at the column, which is the full height of the plot and
|
|
89
|
+
* would put the chip above every bar alike. A bar with nothing to draw (zero, a
|
|
90
|
+
* negative value) keeps its 1px floor on the baseline, so its chip points there.
|
|
91
|
+
*
|
|
92
|
+
* The bar is the anchor element itself, so an open chip follows it through the
|
|
93
|
+
* height transition a data change starts.
|
|
94
|
+
*/
|
|
95
|
+
tip = useChartTooltip(this.tooltip, index => {
|
|
96
|
+
const bar = this.bars()[index];
|
|
97
|
+
const anchor = this.barEls()[index]?.nativeElement;
|
|
98
|
+
if (!bar || !anchor)
|
|
99
|
+
return null;
|
|
100
|
+
return { datum: { label: bar.label, value: String(bar.value), color: bar.color }, anchor };
|
|
101
|
+
});
|
|
77
102
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: WrBarChart, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
78
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: WrBarChart, isStandalone: true, selector: "wr-bar-chart", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, showValues: { classPropertyName: "showValues", publicName: "showValues", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "wr-bar-chart" }, ngImport: i0, template: "<div class=\"wr-bar-chart__plot\" [style.height.px]=\"height()\">\n @for (bar of bars(); track $index) {\n <div
|
|
103
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: WrBarChart, isStandalone: true, selector: "wr-bar-chart", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, showValues: { classPropertyName: "showValues", publicName: "showValues", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, tooltip: { classPropertyName: "tooltip", publicName: "tooltip", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "wr-bar-chart" }, viewQueries: [{ propertyName: "barEls", predicate: ["barEl"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"wr-bar-chart__plot\" [style.height.px]=\"height()\">\n @for (bar of bars(); track $index) {\n <div\n class=\"wr-bar-chart__column\"\n role=\"img\"\n [attr.aria-label]=\"bar.name\"\n (mouseenter)=\"tip.enter($index)\"\n (mouseleave)=\"tip.leave($event)\"\n >\n @if (showValues()) {\n <span class=\"wr-bar-chart__value\">{{ bar.value }}</span>\n }\n <div #barEl class=\"wr-bar-chart__bar\" [style.height.%]=\"bar.heightPercent\" [style.background]=\"bar.color\"></div>\n </div>\n }\n</div>\n<!-- Named on the column above, so the visible label row is decoration here. -->\n<div class=\"wr-bar-chart__labels\" aria-hidden=\"true\">\n @for (bar of bars(); track $index) {\n <span class=\"wr-bar-chart__label\">{{ bar.label }}</span>\n }\n</div>\n", encapsulation: i0.ViewEncapsulation.None });
|
|
79
104
|
}
|
|
80
105
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: WrBarChart, decorators: [{
|
|
81
106
|
type: Component,
|
|
82
|
-
args: [{ selector: 'wr-bar-chart', encapsulation: ViewEncapsulation.None, host: { class: 'wr-bar-chart' }, template: "<div class=\"wr-bar-chart__plot\" [style.height.px]=\"height()\">\n @for (bar of bars(); track $index) {\n <div
|
|
83
|
-
}], propDecorators: { data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], showValues: [{ type: i0.Input, args: [{ isSignal: true, alias: "showValues", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }] } });
|
|
107
|
+
args: [{ selector: 'wr-bar-chart', encapsulation: ViewEncapsulation.None, host: { class: 'wr-bar-chart' }, template: "<div class=\"wr-bar-chart__plot\" [style.height.px]=\"height()\">\n @for (bar of bars(); track $index) {\n <div\n class=\"wr-bar-chart__column\"\n role=\"img\"\n [attr.aria-label]=\"bar.name\"\n (mouseenter)=\"tip.enter($index)\"\n (mouseleave)=\"tip.leave($event)\"\n >\n @if (showValues()) {\n <span class=\"wr-bar-chart__value\">{{ bar.value }}</span>\n }\n <div #barEl class=\"wr-bar-chart__bar\" [style.height.%]=\"bar.heightPercent\" [style.background]=\"bar.color\"></div>\n </div>\n }\n</div>\n<!-- Named on the column above, so the visible label row is decoration here. -->\n<div class=\"wr-bar-chart__labels\" aria-hidden=\"true\">\n @for (bar of bars(); track $index) {\n <span class=\"wr-bar-chart__label\">{{ bar.label }}</span>\n }\n</div>\n" }]
|
|
108
|
+
}], propDecorators: { data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], showValues: [{ type: i0.Input, args: [{ isSignal: true, alias: "showValues", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], tooltip: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltip", required: false }] }], barEls: [{ type: i0.ViewChildren, args: ['barEl', { isSignal: true }] }] } });
|
|
84
109
|
|
|
85
110
|
/**
|
|
86
111
|
* Generated bundle index. Do not edit.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ngwr-bar-chart.mjs","sources":["../../../projects/lib/bar-chart/bar-chart.ts","../../../projects/lib/bar-chart/bar-chart.html","../../../projects/lib/bar-chart/ngwr-bar-chart.ts"],"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, coerceNumberProperty } from '@angular/cdk/coercion';\nimport { Component, ViewEncapsulation, computed, input } from '@angular/core';\n\nimport type { WrBarChartDatum } from './interfaces';\n\n/**\n * Minimal vertical bar chart. Pass `[data]` — each datum becomes a bar.\n * Axis-free; for the simple \"show me a comparison\" case.\n *\n * @example\n * ```html\n * <wr-bar-chart [data]=\"[\n * { label: 'Mon', value: 12 },\n * { label: 'Tue', value: 18, color: 'var(--wr-color-success)' },\n * { label: 'Wed', value: 9 }\n * ]\" />\n * ```\n *\n * @see https://ngwr.dev/reference/components/bar-chart\n */\n@Component({\n selector: 'wr-bar-chart',\n templateUrl: './bar-chart.html',\n encapsulation: ViewEncapsulation.None,\n host: { class: 'wr-bar-chart' },\n})\nexport class WrBarChart {\n readonly data = input<readonly WrBarChartDatum[]>([]);\n\n /** Default bar colour when a datum has none. @default primary */\n readonly color = input<string>('var(--wr-color-primary)');\n\n /** Show value labels above each bar. @default true */\n readonly showValues = input(true, { transform: coerceBooleanProperty });\n\n /** Pixel height of the chart area. @default 200 */\n readonly height = input(200, { transform: (v: unknown): number => Math.max(40, coerceNumberProperty(v, 200)) });\n\n /** Explicit max value. `0` = auto. @default 0 */\n readonly max = input(0, { transform: (v: unknown): number => Math.max(0, coerceNumberProperty(v, 0)) });\n\n /**\n * A datum's value, with anything non-finite read as `0`.\n *\n * One `NaN` used to take out the whole chart rather than its own bar:\n * `Math.max(...values)` is `NaN` as soon as one value is, so the scale fell\n * back to `1` and every OTHER bar was asked for a height of `value * 100`%.\n * The bad bar also printed the literal text `NaN` above itself.\n */\n private static value(raw: number): number {\n return Number.isFinite(raw) ? raw : 0;\n }\n\n protected readonly resolvedMax = computed(() => {\n const explicit = this.max();\n if (explicit > 0) return explicit;\n const data = this.data();\n if (data.length === 0) return 1;\n const top = Math.max(...data.map(d => WrBarChart.value(d.value)), 0);\n return top > 0 ? top : 1;\n });\n\n protected readonly bars = computed(() =>\n this.data().map(d => {\n const value = WrBarChart.value(d.value);\n return {\n label: d.label,\n value,\n color: d.color ?? this.color(),\n heightPercent: (Math.max(0, value) / this.resolvedMax()) * 100,\n /**\n * The bar's accessible name. The label and the value sit in separate\n * rows, so a screen reader read every number and then every label with\n * nothing tying the two together — and with `showValues` off the\n * numbers were not in the accessible tree at all.\n */\n name: `${d.label}: ${value}`,\n };\n })\n );\n}\n\nexport type { WrBarChartDatum } from './interfaces';\n","<div class=\"wr-bar-chart__plot\" [style.height.px]=\"height()\">\n @for (bar of bars(); track $index) {\n <div
|
|
1
|
+
{"version":3,"file":"ngwr-bar-chart.mjs","sources":["../../../projects/lib/bar-chart/bar-chart.ts","../../../projects/lib/bar-chart/bar-chart.html","../../../projects/lib/bar-chart/ngwr-bar-chart.ts"],"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, coerceNumberProperty } from '@angular/cdk/coercion';\nimport { Component, type ElementRef, ViewEncapsulation, computed, input, viewChildren } from '@angular/core';\n\nimport { useChartTooltip } from 'ngwr/popover';\n\nimport type { WrBarChartDatum } from './interfaces';\n\n/**\n * Minimal vertical bar chart. Pass `[data]` — each datum becomes a bar.\n * Axis-free; for the simple \"show me a comparison\" case.\n *\n * @example\n * ```html\n * <wr-bar-chart [data]=\"[\n * { label: 'Mon', value: 12 },\n * { label: 'Tue', value: 18, color: 'var(--wr-color-success)' },\n * { label: 'Wed', value: 9 }\n * ]\" />\n * ```\n *\n * @see https://ngwr.dev/reference/components/bar-chart\n */\n@Component({\n selector: 'wr-bar-chart',\n templateUrl: './bar-chart.html',\n encapsulation: ViewEncapsulation.None,\n host: { class: 'wr-bar-chart' },\n})\nexport class WrBarChart {\n readonly data = input<readonly WrBarChartDatum[]>([]);\n\n /** Default bar colour when a datum has none. @default primary */\n readonly color = input<string>('var(--wr-color-primary)');\n\n /** Show value labels above each bar. @default true */\n readonly showValues = input(true, { transform: coerceBooleanProperty });\n\n /** Pixel height of the chart area. @default 200 */\n readonly height = input(200, { transform: (v: unknown): number => Math.max(40, coerceNumberProperty(v, 200)) });\n\n /** Explicit max value. `0` = auto. @default 0 */\n readonly max = input(0, { transform: (v: unknown): number => Math.max(0, coerceNumberProperty(v, 0)) });\n\n /**\n * Show a tooltip with the bar's label and value on hover. Pointer only — the\n * column's accessible name already carries both. @default true\n */\n readonly tooltip = input(true, { transform: coerceBooleanProperty });\n\n private readonly barEls = viewChildren<ElementRef<HTMLElement>>('barEl');\n\n /**\n * A datum's value, with anything non-finite read as `0`.\n *\n * One `NaN` used to take out the whole chart rather than its own bar:\n * `Math.max(...values)` is `NaN` as soon as one value is, so the scale fell\n * back to `1` and every OTHER bar was asked for a height of `value * 100`%.\n * The bad bar also printed the literal text `NaN` above itself.\n */\n private static value(raw: number): number {\n return Number.isFinite(raw) ? raw : 0;\n }\n\n protected readonly resolvedMax = computed(() => {\n const explicit = this.max();\n if (explicit > 0) return explicit;\n const data = this.data();\n if (data.length === 0) return 1;\n const top = Math.max(...data.map(d => WrBarChart.value(d.value)), 0);\n return top > 0 ? top : 1;\n });\n\n protected readonly bars = computed(() =>\n this.data().map(d => {\n const value = WrBarChart.value(d.value);\n return {\n label: d.label,\n value,\n color: d.color ?? this.color(),\n heightPercent: (Math.max(0, value) / this.resolvedMax()) * 100,\n /**\n * The bar's accessible name. The label and the value sit in separate\n * rows, so a screen reader read every number and then every label with\n * nothing tying the two together — and with `showValues` off the\n * numbers were not in the accessible tree at all.\n */\n name: `${d.label}: ${value}`,\n };\n })\n );\n\n /**\n * One tooltip for the whole chart, with the value exactly as it is printed above\n * the bar. It points at the top edge of the BAR — above it, below it when there is\n * no room — rather than at the column, which is the full height of the plot and\n * would put the chip above every bar alike. A bar with nothing to draw (zero, a\n * negative value) keeps its 1px floor on the baseline, so its chip points there.\n *\n * The bar is the anchor element itself, so an open chip follows it through the\n * height transition a data change starts.\n */\n protected readonly tip = useChartTooltip(this.tooltip, index => {\n const bar = this.bars()[index];\n const anchor = this.barEls()[index]?.nativeElement;\n if (!bar || !anchor) return null;\n return { datum: { label: bar.label, value: String(bar.value), color: bar.color }, anchor };\n });\n}\n\nexport type { WrBarChartDatum } from './interfaces';\n","<div class=\"wr-bar-chart__plot\" [style.height.px]=\"height()\">\n @for (bar of bars(); track $index) {\n <div\n class=\"wr-bar-chart__column\"\n role=\"img\"\n [attr.aria-label]=\"bar.name\"\n (mouseenter)=\"tip.enter($index)\"\n (mouseleave)=\"tip.leave($event)\"\n >\n @if (showValues()) {\n <span class=\"wr-bar-chart__value\">{{ bar.value }}</span>\n }\n <div #barEl class=\"wr-bar-chart__bar\" [style.height.%]=\"bar.heightPercent\" [style.background]=\"bar.color\"></div>\n </div>\n }\n</div>\n<!-- Named on the column above, so the visible label row is decoration here. -->\n<div class=\"wr-bar-chart__labels\" aria-hidden=\"true\">\n @for (bar of bars(); track $index) {\n <span class=\"wr-bar-chart__label\">{{ bar.label }}</span>\n }\n</div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|