ngwr 14.7.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.
Files changed (152) hide show
  1. package/bar-chart/styles/_index.scss +1 -0
  2. package/calendar-heatmap/styles/_index.scss +1 -0
  3. package/cascader/styles/_rules.scss +86 -14
  4. package/date-picker/styles/_index.scss +30 -0
  5. package/density/styles/_index.scss +4 -2
  6. package/donut-chart/styles/_index.scss +10 -0
  7. package/dropdown/styles/_index.scss +67 -5
  8. package/event-calendar/styles/_index.scss +209 -35
  9. package/fesm2022/ngwr-bar-chart.mjs +29 -4
  10. package/fesm2022/ngwr-bar-chart.mjs.map +1 -1
  11. package/fesm2022/ngwr-calendar-heatmap.mjs +59 -4
  12. package/fesm2022/ngwr-calendar-heatmap.mjs.map +1 -1
  13. package/fesm2022/ngwr-cascader.mjs +2 -2
  14. package/fesm2022/ngwr-cascader.mjs.map +1 -1
  15. package/fesm2022/ngwr-checkbox.mjs +12 -2
  16. package/fesm2022/ngwr-checkbox.mjs.map +1 -1
  17. package/fesm2022/ngwr-date-picker.mjs +705 -46
  18. package/fesm2022/ngwr-date-picker.mjs.map +1 -1
  19. package/fesm2022/ngwr-donut-chart.mjs +46 -4
  20. package/fesm2022/ngwr-donut-chart.mjs.map +1 -1
  21. package/fesm2022/ngwr-dropdown.mjs +166 -17
  22. package/fesm2022/ngwr-dropdown.mjs.map +1 -1
  23. package/fesm2022/ngwr-event-calendar.mjs +9 -2
  24. package/fesm2022/ngwr-event-calendar.mjs.map +1 -1
  25. package/fesm2022/ngwr-form.mjs +52 -1
  26. package/fesm2022/ngwr-form.mjs.map +1 -1
  27. package/fesm2022/ngwr-gauge.mjs +33 -4
  28. package/fesm2022/ngwr-gauge.mjs.map +1 -1
  29. package/fesm2022/ngwr-i18n-ar.mjs +2 -0
  30. package/fesm2022/ngwr-i18n-ar.mjs.map +1 -1
  31. package/fesm2022/ngwr-i18n-cs.mjs +2 -0
  32. package/fesm2022/ngwr-i18n-cs.mjs.map +1 -1
  33. package/fesm2022/ngwr-i18n-de.mjs +2 -0
  34. package/fesm2022/ngwr-i18n-de.mjs.map +1 -1
  35. package/fesm2022/ngwr-i18n-en.mjs +5 -0
  36. package/fesm2022/ngwr-i18n-en.mjs.map +1 -1
  37. package/fesm2022/ngwr-i18n-es.mjs +2 -0
  38. package/fesm2022/ngwr-i18n-es.mjs.map +1 -1
  39. package/fesm2022/ngwr-i18n-fr.mjs +2 -0
  40. package/fesm2022/ngwr-i18n-fr.mjs.map +1 -1
  41. package/fesm2022/ngwr-i18n-he.mjs +2 -0
  42. package/fesm2022/ngwr-i18n-he.mjs.map +1 -1
  43. package/fesm2022/ngwr-i18n-hi.mjs +2 -0
  44. package/fesm2022/ngwr-i18n-hi.mjs.map +1 -1
  45. package/fesm2022/ngwr-i18n-id.mjs +2 -0
  46. package/fesm2022/ngwr-i18n-id.mjs.map +1 -1
  47. package/fesm2022/ngwr-i18n-it.mjs +2 -0
  48. package/fesm2022/ngwr-i18n-it.mjs.map +1 -1
  49. package/fesm2022/ngwr-i18n-ja.mjs +2 -0
  50. package/fesm2022/ngwr-i18n-ja.mjs.map +1 -1
  51. package/fesm2022/ngwr-i18n-ko.mjs +2 -0
  52. package/fesm2022/ngwr-i18n-ko.mjs.map +1 -1
  53. package/fesm2022/ngwr-i18n-nl.mjs +2 -0
  54. package/fesm2022/ngwr-i18n-nl.mjs.map +1 -1
  55. package/fesm2022/ngwr-i18n-pl.mjs +2 -0
  56. package/fesm2022/ngwr-i18n-pl.mjs.map +1 -1
  57. package/fesm2022/ngwr-i18n-pt.mjs +2 -0
  58. package/fesm2022/ngwr-i18n-pt.mjs.map +1 -1
  59. package/fesm2022/ngwr-i18n-ru.mjs +2 -0
  60. package/fesm2022/ngwr-i18n-ru.mjs.map +1 -1
  61. package/fesm2022/ngwr-i18n-sv.mjs +2 -0
  62. package/fesm2022/ngwr-i18n-sv.mjs.map +1 -1
  63. package/fesm2022/ngwr-i18n-tr.mjs +2 -0
  64. package/fesm2022/ngwr-i18n-tr.mjs.map +1 -1
  65. package/fesm2022/ngwr-i18n-uk.mjs +2 -0
  66. package/fesm2022/ngwr-i18n-uk.mjs.map +1 -1
  67. package/fesm2022/ngwr-i18n-vi.mjs +2 -0
  68. package/fesm2022/ngwr-i18n-vi.mjs.map +1 -1
  69. package/fesm2022/ngwr-i18n-zh-TW.mjs +2 -0
  70. package/fesm2022/ngwr-i18n-zh-TW.mjs.map +1 -1
  71. package/fesm2022/ngwr-i18n-zh.mjs +2 -0
  72. package/fesm2022/ngwr-i18n-zh.mjs.map +1 -1
  73. package/fesm2022/ngwr-input-number.mjs +12 -0
  74. package/fesm2022/ngwr-input-number.mjs.map +1 -1
  75. package/fesm2022/ngwr-line-chart.mjs +16 -5
  76. package/fesm2022/ngwr-line-chart.mjs.map +1 -1
  77. package/fesm2022/ngwr-meter-group-testing.mjs +7 -3
  78. package/fesm2022/ngwr-meter-group-testing.mjs.map +1 -1
  79. package/fesm2022/ngwr-meter-group.mjs +24 -4
  80. package/fesm2022/ngwr-meter-group.mjs.map +1 -1
  81. package/fesm2022/ngwr-page-header.mjs +10 -2
  82. package/fesm2022/ngwr-page-header.mjs.map +1 -1
  83. package/fesm2022/ngwr-popover.mjs +569 -7
  84. package/fesm2022/ngwr-popover.mjs.map +1 -1
  85. package/fesm2022/ngwr-sidebar.mjs +7 -0
  86. package/fesm2022/ngwr-sidebar.mjs.map +1 -1
  87. package/fesm2022/ngwr-sparkline.mjs +82 -8
  88. package/fesm2022/ngwr-sparkline.mjs.map +1 -1
  89. package/fesm2022/ngwr-table.mjs +1 -1
  90. package/fesm2022/ngwr-table.mjs.map +1 -1
  91. package/fesm2022/ngwr-toolbar.mjs +7 -0
  92. package/fesm2022/ngwr-toolbar.mjs.map +1 -1
  93. package/fesm2022/ngwr-version.mjs +1 -1
  94. package/fesm2022/ngwr-version.mjs.map +1 -1
  95. package/form/styles/_index.scss +11 -1
  96. package/gauge/styles/_index.scss +1 -0
  97. package/i18n/ar.json +3 -1
  98. package/i18n/cs.json +3 -1
  99. package/i18n/de.json +3 -1
  100. package/i18n/en.json +3 -1
  101. package/i18n/es.json +3 -1
  102. package/i18n/fr.json +3 -1
  103. package/i18n/he.json +3 -1
  104. package/i18n/hi.json +3 -1
  105. package/i18n/id.json +3 -1
  106. package/i18n/it.json +3 -1
  107. package/i18n/ja.json +3 -1
  108. package/i18n/ko.json +3 -1
  109. package/i18n/nl.json +3 -1
  110. package/i18n/pl.json +3 -1
  111. package/i18n/pt.json +3 -1
  112. package/i18n/ru.json +3 -1
  113. package/i18n/sv.json +3 -1
  114. package/i18n/tr.json +3 -1
  115. package/i18n/uk.json +3 -1
  116. package/i18n/vi.json +3 -1
  117. package/i18n/zh-TW.json +3 -1
  118. package/i18n/zh.json +3 -1
  119. package/input/styles/_index.scss +19 -0
  120. package/input-number/styles/_index.scss +69 -0
  121. package/line-chart/styles/_index.scss +30 -2
  122. package/llms-full.txt +3 -3
  123. package/meter-group/styles/_index.scss +1 -0
  124. package/overlay/styles/_arrow.scss +27 -2
  125. package/package.json +1 -1
  126. package/page-header/styles/_index.scss +37 -1
  127. package/pagination/styles/_index.scss +9 -5
  128. package/popover/styles/_index.scss +73 -5
  129. package/select/styles/_index.scss +48 -9
  130. package/sidebar/styles/_index.scss +34 -2
  131. package/sparkline/styles/_index.scss +19 -0
  132. package/toolbar/styles/_index.scss +35 -1
  133. package/tree/styles/_index.scss +45 -6
  134. package/types/ngwr-bar-chart.d.ts +19 -1
  135. package/types/ngwr-calendar-heatmap.d.ts +29 -1
  136. package/types/ngwr-checkbox.d.ts +2 -0
  137. package/types/ngwr-date-picker.d.ts +308 -43
  138. package/types/ngwr-donut-chart.d.ts +24 -1
  139. package/types/ngwr-dropdown.d.ts +22 -2
  140. package/types/ngwr-event-calendar.d.ts +8 -0
  141. package/types/ngwr-form.d.ts +35 -5
  142. package/types/ngwr-gauge.d.ts +20 -1
  143. package/types/ngwr-input-number.d.ts +7 -0
  144. package/types/ngwr-line-chart.d.ts +9 -1
  145. package/types/ngwr-meter-group-testing.d.ts +3 -2
  146. package/types/ngwr-meter-group.d.ts +14 -1
  147. package/types/ngwr-page-header.d.ts +10 -2
  148. package/types/ngwr-popover.d.ts +154 -3
  149. package/types/ngwr-sidebar.d.ts +7 -0
  150. package/types/ngwr-sparkline.d.ts +43 -1
  151. package/types/ngwr-toolbar.d.ts +7 -0
  152. package/types/ngwr-version.d.ts +1 -1
@@ -62,7 +62,9 @@
62
62
  &__actions {
63
63
  display: flex;
64
64
  align-items: center;
65
- gap: 0.5rem;
65
+ // Density-aware: `touch` widens it so adjacent actions do not crowd a
66
+ // fingertip, `sm` tightens it. `md` resolves to the 8px it always was.
67
+ gap: calc(0.5rem * var(--wr-density-gap, 1));
66
68
  flex-wrap: wrap;
67
69
  flex: 0 0 auto;
68
70
 
@@ -84,6 +86,40 @@
84
86
  }
85
87
  }
86
88
 
89
+ // The documented actions shape is ONE wrapper around the buttons —
90
+ // `<div wrPageHeaderActions><wr-btn>…</wr-btn><wr-btn>…</wr-btn></div>` — and the
91
+ // `gap` above spaces the region's CHILDREN, which is that wrapper alone, so the
92
+ // buttons inside it met edge to edge (0px, measured in Chromium). A BARE wrapper
93
+ // is laid out the way the region is, taking the region's own gap through
94
+ // `inherit`, so both shapes — one wrapper, or the attribute on each button —
95
+ // space the same and a density change moves them together. What BARE excludes,
96
+ // and why:
97
+ //
98
+ // - A `class` or a `style`. Zero specificity alone does not keep a consumer's
99
+ // layout: ngwr ships unlayered CSS, and unlayered CSS beats every cascade
100
+ // layer whatever its specificity, so a Tailwind v4 `hidden` or `space-x-2` on
101
+ // the wrapper lost to this rule and a hidden wrapper came back. A wrapper with
102
+ // a class or a style is the consumer's, and nothing here touches it — not
103
+ // `display`, not `align-items`, not `flex-wrap`. An EMPTY one still counts as
104
+ // bare, because `class=""` / `style=""` is what a `[class.x]` / `[style.x]`
105
+ // binding leaves behind when it switches off, and the same state must not lay
106
+ // out two ways.
107
+ // - Anything but a `div`. A button projected directly is already a child of the
108
+ // region and spaced by its gap, and must not become a flex container itself.
109
+ // - `[hidden]`. An author rule outranks the user agent's
110
+ // `[hidden] { display: none }` at any specificity.
111
+ //
112
+ // The extra row is deliberately NOT given the same rule: it is where metadata
113
+ // prose lives, and a flex row would turn the words and links of a bare `<div>`
114
+ // into separate items. Tags go in as its children instead, through an
115
+ // `<ng-container wrPageHeaderExtra>`, which the class JSDoc shows.
116
+ :where(.wr-page-header__actions > div:not([hidden], [class]:not([class='']), [style]:not([style='']))) {
117
+ display: flex;
118
+ align-items: center;
119
+ flex-wrap: wrap;
120
+ gap: inherit;
121
+ }
122
+
87
123
  // Responsive headers stack the title above the actions when their own box is
88
124
  // too narrow to sit them side by side (regardless of viewport).
89
125
  @container wr-page-header (max-width: 32rem) {
@@ -108,11 +108,15 @@
108
108
  &__size {
109
109
  min-width: 0;
110
110
 
111
- // The 10rem floor on `.wr-select__trigger` is sized for a standalone field.
112
- // Here the select only ever holds "20 / page", so the pager was reserving
113
- // 160px for a ~75px label — the single widest thing in the control.
114
- .wr-select__trigger {
115
- min-width: 0;
111
+ // The select's 10rem width floor is sized for a standalone field. Here it
112
+ // only ever holds "20 / page", so the pager was reserving 160px for a ~75px
113
+ // label — the single widest thing in the control. Through the hook, since
114
+ // the floor lives on the select's host and a `min-width` on its trigger no
115
+ // longer reaches it — and compounded with `.wr-select`, which declares the
116
+ // default on this same element, so the override does not hang on which
117
+ // stylesheet loads last.
118
+ &.wr-select {
119
+ --wr-select-min-width: 0;
116
120
  }
117
121
  }
118
122
 
@@ -72,6 +72,46 @@
72
72
  // API, and the arrow is the pane's business rather than the chip's.
73
73
  }
74
74
 
75
+ // The chip a chart's hover tooltip renders into (`useChartTooltip()`, used by
76
+ // every chart but `wr-line-chart`, which draws its own). It carries `.wr-tooltip`
77
+ // as well, so everything above is its look and the arrow below is its arrow; this
78
+ // only lays out the inside.
79
+ //
80
+ // The label is told from the value by WEIGHT, not by a muted colour. The chip's
81
+ // colours are the two `--wr-tooltip-*` hooks, and the documented way to invert
82
+ // them makes the chip dark — a muted grey picked against the light surface would
83
+ // be the one thing that did not follow.
84
+ .wr-chart-tooltip {
85
+ display: inline-flex;
86
+ // Its own text's width, up to the tooltip's `max-width`, whatever room the pane has
87
+ // left. The CDK pushes a chip against the viewport edge by its ROUNDED width, which
88
+ // can leave the absolutely positioned pane a fraction of a pixel short, and the chip
89
+ // for the last week of a year then wrapped `Sep 19, 2026: 0` onto a second line at
90
+ // 375px, over two more rows of days.
91
+ flex-shrink: 0;
92
+ width: max-content;
93
+ align-items: center;
94
+ gap: 0.375rem;
95
+
96
+ &__swatch {
97
+ flex: 0 0 auto;
98
+ width: 0.5rem;
99
+ height: 0.5rem;
100
+ border-radius: 50%;
101
+ }
102
+
103
+ &__value {
104
+ font-weight: var(--wr-font-weight-semibold);
105
+ font-variant-numeric: tabular-nums;
106
+
107
+ // Alone, with no label or swatch beside it, the value is a whole sentence (a
108
+ // heatmap day's), and a sentence in semibold reads as shouting.
109
+ &:only-child {
110
+ font-weight: inherit;
111
+ }
112
+ }
113
+ }
114
+
75
115
  // Arrow for both modes.
76
116
  //
77
117
  // Popover: the pane wraps whatever the author projected, so the panel selector
@@ -84,18 +124,46 @@
84
124
  $border: var(--wr-color-outline)
85
125
  );
86
126
 
87
- // Tooltip: a known child, and no border faces — the chip's own border colour is
88
- // overridable per instance through `--wr-tooltip-border`, so painting the faces
89
- // from the token here would desync the moment anyone retints one. The arrow
90
- // points AT the trigger and overlaps it, so it is the one inert piece: a
91
- // hittable arrow would eat clicks meant for the control the tooltip describes.
127
+ // Tooltip: a known child, with the chip's border carried onto the arrow's outward
128
+ // faces. Painted from the `--wr-tooltip-border` HOOK rather than the outline token:
129
+ // the square is the chip's own pseudo-element, so a chip retinted per instance
130
+ // retints its arrow with it. Without the faces the chip's outline stopped at the
131
+ // arrow's base and the tip read as a notch cut out of the border. The arrow points
132
+ // AT the trigger and overlaps it, so it is the one inert piece: a hittable arrow
133
+ // would eat clicks meant for the control the tooltip describes.
92
134
  @include arrow.anchored-arrow(
93
135
  $pane: '.wr-tooltip-overlay',
94
136
  $panel: '.wr-tooltip',
95
137
  $background: var(--wr-tooltip-bg),
138
+ $border: var(--wr-tooltip-border),
96
139
  $inert: true
97
140
  );
98
141
 
142
+ // A chart's chip keeps the edge the placement gives its arrow and moves the arrow
143
+ // ALONG that edge, onto the section it describes. The chip is not always centred on
144
+ // its anchor — `withPush` slides the one for a heatmap's first week along the
145
+ // viewport edge — so `useChartTooltip()` measures where the anchor falls on the chip
146
+ // and writes it to `--wr-chart-tooltip-arrow`, in px from the padding edge. Unset,
147
+ // before anything is measured, it is the middle, where the rule above puts it.
148
+ // After that rule on purpose: same specificity, so source order decides.
149
+ .wr-tooltip-overlay--top,
150
+ .wr-tooltip-overlay--bottom {
151
+ .wr-chart-tooltip::after {
152
+ inset-inline: auto;
153
+ margin-inline: 0;
154
+ // rtl-ok: a length measured from the chip's physical left edge in viewport pixels
155
+ left: calc(var(--wr-chart-tooltip-arrow, 50%) - #{arrow.$half});
156
+ }
157
+ }
158
+
159
+ .wr-tooltip-overlay--left,
160
+ .wr-tooltip-overlay--right {
161
+ .wr-chart-tooltip::after {
162
+ top: calc(var(--wr-chart-tooltip-arrow, 50%) - #{arrow.$half});
163
+ margin-top: 0;
164
+ }
165
+ }
166
+
99
167
  @media (prefers-reduced-motion: reduce) {
100
168
  .wr-tooltip {
101
169
  animation: none;
@@ -11,17 +11,49 @@
11
11
  --wr-select-font-size: var(--wr-control-font-size-md);
12
12
  --wr-select-line-height: var(--wr-control-line-height-md);
13
13
  // The floor that keeps a bare `<wr-select>` from collapsing to its
14
- // placeholder. A hook rather than a literal because the floor is wrong as
15
- // often as it is right: dropped into a `minmax(0, 1fr)` grid column the
16
- // trigger holds 160px and overhangs its cell, and overriding
17
- // `.wr-select__trigger` is reaching past the public contract for something
18
- // the rest of the component already exposes. Set it to 0 to hand the width
19
- // back to the layout.
14
+ // placeholder. Despite the name it is a DEFAULT width rather than a minimum:
15
+ // an explicit `width`, `width: 100%` in a narrow parent, a `minmax(0, 1fr)`
16
+ // cell and a shrinking flex item all win over it (see `::after` below). Set
17
+ // it to 0 to size a bare select to its content alone. This hook is the only
18
+ // way to lower it: a `min-width` on `.wr-select__trigger`, the override that
19
+ // did it before the hook existed, no longer reaches the floor.
20
20
  --wr-select-min-width: 10rem;
21
21
 
22
- display: inline-flex;
22
+ // One column that shrinks to whatever the layout gives it and, left to size
23
+ // itself, grows to the widest thing in it — the trigger's content or the
24
+ // floor. The floor used to be a `min-width` on the trigger, which no width on
25
+ // the host could beat: `<wr-select size="sm" style="width: 9rem">` drew a
26
+ // 160px trigger in a 144px host and ran 16px into the next control, and in a
27
+ // 200px flex row beside a 120px sibling the select overflowed the row by
28
+ // 88px. `minmax(0, …)` is also what drops the trigger's text from the host's
29
+ // min-content, so the host shrinks as a flex item the way `[wrInput]` and
30
+ // `<wr-date-picker>` already do, and the label truncates instead.
31
+ display: inline-grid;
32
+ grid-template-columns: minmax(0, 1fr);
33
+ // The spacer below lands in a row of its own, and it must be a FIXED 0 rather
34
+ // than an implicit `auto` one: a grid shares height beyond its content among
35
+ // its auto rows, so a host stretched by a taller flex or grid row, or given a
36
+ // `height`, drew a 45px trigger in a 60px host with an empty band beneath it.
37
+ grid-template-rows: auto 0;
23
38
  vertical-align: middle;
24
39
 
40
+ // The floor as a zero-height spacer in the same column. A grid track's
41
+ // max-content is its widest item, so a host sized to its content is at least
42
+ // `--wr-select-min-width` wide; `max-width: 100%` is ignored while that size
43
+ // is being worked out and applies once the column exists, so a narrower host
44
+ // caps the spacer instead of the spacer stretching the host. `flex: 0 0 0`
45
+ // keeps it from taking room beside the trigger if a consumer turns the host
46
+ // into a flex container (the floor is then inert, never in the way).
47
+ // The template renders exactly one element into the host — the trigger — so
48
+ // this is the column's only other item, and the zero row is the spacer's.
49
+ &::after {
50
+ content: '';
51
+ flex: 0 0 0;
52
+ width: var(--wr-select-min-width);
53
+ max-width: 100%;
54
+ height: 0;
55
+ }
56
+
25
57
  // Sizes — share the `--wr-control-*` contract (22 / 30 / 36 px). Multi/tag
26
58
  // min-height and the search/tag inputs all read these vars, so every mode
27
59
  // tracks the size. Declared before `--rounded` so the pill modifier wins.
@@ -73,7 +105,10 @@
73
105
  align-items: center;
74
106
  gap: 0.5rem;
75
107
  width: 100%;
76
- min-width: var(--wr-select-min-width);
108
+ // Not the floor — that lives on the host, where a width can beat it. Zero
109
+ // lets the trigger follow the host down and the value / placeholder
110
+ // ellipsis take the difference.
111
+ min-width: 0;
77
112
  appearance: none;
78
113
  background: var(--wr-select-bg);
79
114
  color: var(--wr-select-color);
@@ -228,7 +263,11 @@
228
263
 
229
264
  &__tag-input {
230
265
  flex: 1 0 6rem;
231
- min-width: 4rem;
266
+ // Both capped by the chip row, which can now be as narrow as the host: with
267
+ // no 10rem trigger floor underneath, a bare 4rem minimum and an unshrinkable
268
+ // 6rem basis ran the field 9px past the border of a 100px tag select.
269
+ min-width: min(4rem, 100%);
270
+ max-width: 100%;
232
271
  border: 0;
233
272
  outline: 0;
234
273
  padding: 0.125rem 0.25rem;
@@ -1,7 +1,18 @@
1
1
  @use '../../theme/styles' as theme;
2
2
 
3
3
  .wr-sidebar {
4
+ // The outer inset, what `--wr-dropdown-padding` is to a menu. Every row paints
5
+ // its hover and active tint across the host's full width, and at 0 that tint
6
+ // met whatever frames the nav: the bordered card of a demo, the border of a
7
+ // `wr-layout-sider`, which pads nothing either. 8px on every side keeps each
8
+ // tint, nested rows included, off the frame in both directions, and leaves an
9
+ // outset focus ring room inside a sider that clips (`overflow: hidden`). Where
10
+ // the container already provides the inset, set it to 0 on `.wr-sidebar`, not
11
+ // on the container: this declaration shadows anything inherited.
12
+ --wr-sidebar-padding: 0.5rem;
13
+
4
14
  display: block;
15
+ padding: var(--wr-sidebar-padding);
5
16
  font-family: var(--wr-font-family-base);
6
17
  color: var(--wr-color-on-surface);
7
18
 
@@ -83,6 +94,13 @@
83
94
  border: 0;
84
95
  border-radius: var(--wr-border-radius-sm);
85
96
  color: var(--wr-color-on-surface);
97
+ // A `<button>` inherits no type. The UA's 13.33px and `line-height: normal`
98
+ // made this row 33px under the 36px link beside it, and shrank the icon and
99
+ // the chevron, which size from `em` — so the same type as `__entry`.
100
+ font: inherit;
101
+ font-size: var(--wr-text-sm);
102
+ font-weight: var(--wr-font-weight-semibold);
103
+ line-height: 1.25rem;
86
104
  cursor: pointer;
87
105
  text-align: start;
88
106
  transition: background var(--wr-transition-base);
@@ -152,12 +170,22 @@
152
170
  border-inline-start: 1px solid var(--wr-color-outline);
153
171
  }
154
172
 
173
+ // The list's 6px bottom margin spaces an open group from the row after it. On
174
+ // the LAST group there is no such row, and the margin stacked onto the host's
175
+ // inset: 14px under the final nested row against 8px above the first row.
176
+ &__group:last-child &__list {
177
+ margin-block-end: 0;
178
+ }
179
+
155
180
  &__item {
156
181
  position: relative;
157
182
  display: flex;
158
183
  align-items: center;
159
184
  gap: 0.5rem;
160
- padding: 0.3125rem 0.75rem;
185
+ // 6px around the 20px line: a 32px row, the geometry of a dropdown item. At
186
+ // 5px the hover and active tint sat closer to the text than anywhere else in
187
+ // the nav, and a badge row came out 2px taller than its neighbours.
188
+ padding: 0.375rem 0.75rem;
161
189
  border-radius: var(--wr-border-radius-sm);
162
190
  color: var(--wr-color-on-surface);
163
191
  font-size: var(--wr-text-sm);
@@ -223,7 +251,10 @@
223
251
 
224
252
  &__badge {
225
253
  flex: 0 0 auto;
226
- padding: 0.0625rem 0.375rem;
254
+ // 16px line plus 2px either side is the row's own 20px line box, so a badge
255
+ // never makes its row taller: it inherited the row's 20px line and came out
256
+ // 22px, which grew `__entry` from 36px to 38px and `__item` by the same 2px.
257
+ padding: 0.125rem 0.375rem;
227
258
  // The row's corner, not a pill: the badge sits inside `__entry` / `__item`,
228
259
  // which both read `--wr-border-radius-sm`, and a stadium nested in a 6px row
229
260
  // reads as a foreign shape rather than a part of it.
@@ -232,6 +263,7 @@
232
263
  color: var(--wr-color-on-surface-muted);
233
264
  font-size: var(--wr-text-xs);
234
265
  font-weight: var(--wr-font-weight-semibold);
266
+ line-height: 1rem;
235
267
  text-transform: uppercase;
236
268
  letter-spacing: var(--wr-tracking-wide);
237
269
  }
@@ -1,9 +1,28 @@
1
+ @use '../../popover/styles'; // the `.wr-tooltip` chip the hover tooltip renders into
2
+
1
3
  .wr-sparkline {
2
4
  display: inline-block;
5
+ // The hover marker is placed in percentages of this box, which is the drawing's.
6
+ position: relative;
3
7
  line-height: 0;
4
8
 
5
9
  svg {
6
10
  display: block;
7
11
  overflow: visible;
8
12
  }
13
+
14
+ // The point the hover tooltip is reading. Placed by the component in SVG
15
+ // coordinates, which do not flip with `direction`, and never hittable: it sits
16
+ // under the pointer, and taking the `mousemove` would lose the line.
17
+ &__marker {
18
+ position: absolute;
19
+ width: 0.5rem;
20
+ height: 0.5rem;
21
+ border: 2px solid var(--wr-color-surface);
22
+ border-radius: 50%;
23
+ box-sizing: content-box;
24
+ // rtl-ok: SVG coordinates, see above
25
+ transform: translate(-50%, -50%);
26
+ pointer-events: none;
27
+ }
9
28
  }
@@ -33,7 +33,9 @@
33
33
  &__zone {
34
34
  display: flex;
35
35
  align-items: center;
36
- gap: 0.5rem;
36
+ // Density-aware: `touch` widens it so adjacent controls do not crowd a
37
+ // fingertip, `sm` tightens it. `md` resolves to the 8px it always was.
38
+ gap: calc(0.5rem * var(--wr-density-gap, 1));
37
39
  min-width: 0;
38
40
 
39
41
  &:empty {
@@ -74,6 +76,38 @@
74
76
  }
75
77
  }
76
78
 
79
+ // The documented zone shape is ONE wrapper around the controls —
80
+ // `<div wrToolbarEnd><wr-btn>…</wr-btn><wr-btn>…</wr-btn></div>` — and the zone's
81
+ // `gap` spaces the zone's CHILDREN, which is that wrapper alone, so the buttons
82
+ // inside it met edge to edge (0px, measured in Chromium). A BARE wrapper is laid
83
+ // out the way the zone is, taking the zone's own gap through `inherit`, so both
84
+ // shapes — one wrapper, or the attribute on each control — space the same and a
85
+ // density change moves them together. It wraps, as the inline flow it replaces
86
+ // did, so a stacked `responsive` zone still folds its controls onto a second
87
+ // row, now with the gap between the rows as well. What BARE excludes, and why:
88
+ //
89
+ // - A `class` or a `style`. Zero specificity alone does not keep a consumer's
90
+ // layout: ngwr ships unlayered CSS, and unlayered CSS beats every cascade
91
+ // layer whatever its specificity, so a Tailwind v4 `hidden`, `grid` or `gap-4`
92
+ // on the wrapper lost to this rule and a hidden wrapper came back. A wrapper
93
+ // with a class or a style is the consumer's, and nothing here touches it — not
94
+ // `display`, not `align-items`, not `flex-wrap`. An EMPTY one still counts as
95
+ // bare, because `class=""` / `style=""` is what a `[class.x]` / `[style.x]`
96
+ // binding leaves behind when it switches off, and the same state must not lay
97
+ // out two ways.
98
+ // - Anything but a `div`. A control projected directly is already a child of the
99
+ // zone and spaced by its gap, and must not become a flex container itself.
100
+ // Running text belongs in a `<span>` or a `<p>`, which stay in inline flow: in
101
+ // a bare `<div>` its words and links would become separate flex items.
102
+ // - `[hidden]`. An author rule outranks the user agent's
103
+ // `[hidden] { display: none }` at any specificity.
104
+ :where(.wr-toolbar__zone > div:not([hidden], [class]:not([class='']), [style]:not([style='']))) {
105
+ display: flex;
106
+ align-items: center;
107
+ flex-wrap: wrap;
108
+ gap: inherit;
109
+ }
110
+
77
111
  // Responsive toolbars stack their zones when the bar is too narrow for a row.
78
112
  @container wr-toolbar (max-width: 32rem) {
79
113
  .wr-toolbar--responsive .wr-toolbar__zone {
@@ -177,15 +177,51 @@
177
177
  --wr-tree-trigger-font-size: var(--wr-control-font-size-md);
178
178
  --wr-tree-trigger-line-height: var(--wr-control-line-height-md);
179
179
  --wr-tree-panel-max-height: 18rem;
180
- // Trigger width floor — hooked for the same reason as `--wr-select-min-width`:
181
- // in a narrow grid column the literal wins over `minmax(0, 1fr)` and the
182
- // trigger overhangs its cell, and the only escape was overriding
183
- // `.wr-tree__trigger` from outside. Set it to 0 to defer to the layout.
180
+ // Trigger width floor, the same contract as `--wr-select-min-width`.
181
+ // Despite the name it is a DEFAULT width rather than a minimum: an explicit
182
+ // `width`, `width: 100%` in a narrow parent, a `minmax(0, 1fr)` cell and a
183
+ // shrinking flex item all win over it (see `::after` below). Set it to 0 to
184
+ // size a bare trigger to its content alone. This hook is the only way to
185
+ // lower it: a `min-width` on `.wr-tree__trigger`, the override that did it
186
+ // before the hook existed, no longer reaches the floor.
184
187
  --wr-tree-trigger-min-width: 12rem;
185
188
 
186
- display: inline-flex;
189
+ // One column that shrinks to whatever the layout gives it and, left to size
190
+ // itself, grows to the widest thing in it — the trigger's content or the
191
+ // floor. The floor used to be a `min-width` on the trigger, which no width
192
+ // on the host could beat: `style="width: 6rem"` drew a 192px trigger in a
193
+ // 96px host, and in a 200px flex row beside a 120px sibling the tree
194
+ // overflowed the row by 120px. `minmax(0, …)` also drops the label's text
195
+ // from the host's min-content, so it shrinks as a flex item the way
196
+ // `[wrInput]` does and the label truncates instead.
197
+ display: inline-grid;
198
+ grid-template-columns: minmax(0, 1fr);
199
+ // The spacer below lands in a row of its own, and it must be a FIXED 0
200
+ // rather than an implicit `auto` one: a grid shares height beyond its
201
+ // content among its auto rows, so a host stretched by a taller flex or grid
202
+ // row, or given a `height`, drew a 45px trigger in a 60px host with an empty
203
+ // band beneath it.
204
+ grid-template-rows: auto 0;
187
205
  vertical-align: middle;
188
206
 
207
+ // The floor as a zero-height spacer in the same column. A grid track's
208
+ // max-content is its widest item, so a host sized to its content is at
209
+ // least `--wr-tree-trigger-min-width` wide; `max-width: 100%` is ignored
210
+ // while that size is being worked out and applies once the column exists,
211
+ // so a narrower host caps the spacer instead of the spacer stretching the
212
+ // host. `flex: 0 0 0` keeps it from taking room beside the trigger if a
213
+ // consumer turns the host into a flex container (the floor is then inert,
214
+ // never in the way). In overlay mode the template renders exactly one
215
+ // element into the host — the trigger — so this is the column's only other
216
+ // item, and the zero row is the spacer's.
217
+ &::after {
218
+ content: '';
219
+ flex: 0 0 0;
220
+ width: var(--wr-tree-trigger-min-width);
221
+ max-width: 100%;
222
+ height: 0;
223
+ }
224
+
189
225
  &.wr-tree--disabled {
190
226
  --wr-tree-trigger-color: var(--wr-color-on-surface-muted);
191
227
  --wr-tree-trigger-bg: var(--wr-color-fill);
@@ -197,7 +233,10 @@
197
233
  align-items: center;
198
234
  gap: 0.5rem;
199
235
  width: 100%;
200
- min-width: var(--wr-tree-trigger-min-width);
236
+ // Not the floor — that lives on the host, where a width can beat it. Zero
237
+ // lets the trigger follow the host down and the label's ellipsis take the
238
+ // difference.
239
+ min-width: 0;
201
240
  appearance: none;
202
241
  background: var(--wr-tree-trigger-bg);
203
242
  color: var(--wr-tree-trigger-color);
@@ -1,3 +1,4 @@
1
+ import * as ngwr_popover from 'ngwr/popover';
1
2
  import * as _angular_core from '@angular/core';
2
3
 
3
4
  /** One bar's worth of data. */
@@ -32,6 +33,12 @@ declare class WrBarChart {
32
33
  readonly height: _angular_core.InputSignalWithTransform<number, unknown>;
33
34
  /** Explicit max value. `0` = auto. @default 0 */
34
35
  readonly max: _angular_core.InputSignalWithTransform<number, unknown>;
36
+ /**
37
+ * Show a tooltip with the bar's label and value on hover. Pointer only — the
38
+ * column's accessible name already carries both. @default true
39
+ */
40
+ readonly tooltip: _angular_core.InputSignalWithTransform<boolean, any>;
41
+ private readonly barEls;
35
42
  /**
36
43
  * A datum's value, with anything non-finite read as `0`.
37
44
  *
@@ -55,8 +62,19 @@ declare class WrBarChart {
55
62
  */
56
63
  name: string;
57
64
  }[]>;
65
+ /**
66
+ * One tooltip for the whole chart, with the value exactly as it is printed above
67
+ * the bar. It points at the top edge of the BAR — above it, below it when there is
68
+ * no room — rather than at the column, which is the full height of the plot and
69
+ * would put the chip above every bar alike. A bar with nothing to draw (zero, a
70
+ * negative value) keeps its 1px floor on the baseline, so its chip points there.
71
+ *
72
+ * The bar is the anchor element itself, so an open chip follows it through the
73
+ * height transition a data change starts.
74
+ */
75
+ protected readonly tip: ngwr_popover.WrChartTooltip;
58
76
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<WrBarChart, never>;
59
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<WrBarChart, "wr-bar-chart", never, { "data": { "alias": "data"; "required": false; "isSignal": true; }; "color": { "alias": "color"; "required": false; "isSignal": true; }; "showValues": { "alias": "showValues"; "required": false; "isSignal": true; }; "height": { "alias": "height"; "required": false; "isSignal": true; }; "max": { "alias": "max"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
77
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<WrBarChart, "wr-bar-chart", never, { "data": { "alias": "data"; "required": false; "isSignal": true; }; "color": { "alias": "color"; "required": false; "isSignal": true; }; "showValues": { "alias": "showValues"; "required": false; "isSignal": true; }; "height": { "alias": "height"; "required": false; "isSignal": true; }; "max": { "alias": "max"; "required": false; "isSignal": true; }; "tooltip": { "alias": "tooltip"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
60
78
  }
61
79
 
62
80
  export { WrBarChart };
@@ -1,3 +1,4 @@
1
+ import * as ngwr_popover from 'ngwr/popover';
1
2
  import * as _angular_core from '@angular/core';
2
3
 
3
4
  interface WrHeatmapDatum {
@@ -70,6 +71,13 @@ declare class WrCalendarHeatmap {
70
71
  readonly emptyColor: _angular_core.InputSignal<string>;
71
72
  /** Show the weekday + month labels around the grid. @default true */
72
73
  readonly showLabels: _angular_core.InputSignalWithTransform<boolean, any>;
74
+ /**
75
+ * Show a tooltip with the day and its count on hover. It is the sentence the
76
+ * square's `title` carried, so the `title` is written only while this is off —
77
+ * or the browser would open a second copy a moment later. @default true
78
+ */
79
+ readonly tooltip: _angular_core.InputSignalWithTransform<boolean, any>;
80
+ private readonly daysEl;
73
81
  private readonly locale;
74
82
  /**
75
83
  * Weekday names from the locale rather than a hard-coded English list — the row order is
@@ -84,10 +92,30 @@ declare class WrCalendarHeatmap {
84
92
  week: number;
85
93
  }[]>;
86
94
  protected readonly columnCount: _angular_core.Signal<number>;
95
+ /** Each day's position in `cells()`, for the square the pointer lands on. */
96
+ private readonly indexByIso;
97
+ /**
98
+ * One tooltip for the whole year, pointing at the hovered day: above its square,
99
+ * below it when there is no room above, and slid along the viewport edge for the
100
+ * first and last weeks with the arrow still on the square. A listener on the grid
101
+ * rather than one per square: a year is 371 of them.
102
+ *
103
+ * The box is the square's LAYOUT box rather than its client rect. The hovered square
104
+ * grows by `:hover` over a transition, and a rect read part way through it would
105
+ * move the chip by up to a fifth of a square depending on when it was measured;
106
+ * the scale is centred, so the layout box is the rect's centre and the square's
107
+ * own size.
108
+ */
109
+ protected readonly tip: ngwr_popover.WrChartTooltip;
110
+ /**
111
+ * Which square the pointer is on, from a `mousemove` on the grid. Over the gap
112
+ * between two squares it is on none, and the tooltip starts to leave.
113
+ */
114
+ protected onCellsPointer(event: MouseEvent): void;
87
115
  protected colorFor(intensity: number): string;
88
116
  protected opacityFor(intensity: number): number;
89
117
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<WrCalendarHeatmap, never>;
90
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<WrCalendarHeatmap, "wr-calendar-heatmap", never, { "data": { "alias": "data"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; "endDate": { "alias": "endDate"; "required": false; "isSignal": true; }; "weeks": { "alias": "weeks"; "required": false; "isSignal": true; }; "cellSize": { "alias": "cellSize"; "required": false; "isSignal": true; }; "cellGap": { "alias": "cellGap"; "required": false; "isSignal": true; }; "color": { "alias": "color"; "required": false; "isSignal": true; }; "emptyColor": { "alias": "emptyColor"; "required": false; "isSignal": true; }; "showLabels": { "alias": "showLabels"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
118
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<WrCalendarHeatmap, "wr-calendar-heatmap", never, { "data": { "alias": "data"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; "endDate": { "alias": "endDate"; "required": false; "isSignal": true; }; "weeks": { "alias": "weeks"; "required": false; "isSignal": true; }; "cellSize": { "alias": "cellSize"; "required": false; "isSignal": true; }; "cellGap": { "alias": "cellGap"; "required": false; "isSignal": true; }; "color": { "alias": "color"; "required": false; "isSignal": true; }; "emptyColor": { "alias": "emptyColor"; "required": false; "isSignal": true; }; "showLabels": { "alias": "showLabels"; "required": false; "isSignal": true; }; "tooltip": { "alias": "tooltip"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
91
119
  }
92
120
 
93
121
  export { WrCalendarHeatmap };
@@ -111,6 +111,8 @@ declare class WrCheckbox implements FormCheckboxControl {
111
111
  * only and controlled: set it yourself for a parent "select all" whose
112
112
  * children are partly checked, and clear it on the next toggle. Takes visual
113
113
  * precedence over `checked`; the native input reports `aria-checked="mixed"`.
114
+ * A click the host does not answer still toggles `checked` underneath, and the
115
+ * dash and the mixed state both stay until the binding turns `false`.
114
116
  *
115
117
  * @default false
116
118
  */