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.
Files changed (169) hide show
  1. package/alert/styles/_index.scss +14 -3
  2. package/avatar/styles/_index.scss +44 -0
  3. package/bar-chart/styles/_index.scss +1 -0
  4. package/calendar-heatmap/styles/_index.scss +1 -0
  5. package/cascader/styles/_rules.scss +86 -18
  6. package/collapse/styles/_index.scss +0 -1
  7. package/date-picker/styles/_index.scss +30 -0
  8. package/density/styles/_index.scss +4 -2
  9. package/donut-chart/styles/_index.scss +10 -0
  10. package/dropdown/styles/_index.scss +67 -5
  11. package/event-calendar/styles/_index.scss +209 -35
  12. package/fesm2022/ngwr-avatar.mjs +19 -2
  13. package/fesm2022/ngwr-avatar.mjs.map +1 -1
  14. package/fesm2022/ngwr-bar-chart.mjs +29 -4
  15. package/fesm2022/ngwr-bar-chart.mjs.map +1 -1
  16. package/fesm2022/ngwr-calendar-heatmap.mjs +59 -4
  17. package/fesm2022/ngwr-calendar-heatmap.mjs.map +1 -1
  18. package/fesm2022/ngwr-cascader.mjs +2 -2
  19. package/fesm2022/ngwr-cascader.mjs.map +1 -1
  20. package/fesm2022/ngwr-checkbox.mjs +12 -2
  21. package/fesm2022/ngwr-checkbox.mjs.map +1 -1
  22. package/fesm2022/ngwr-date-picker.mjs +705 -46
  23. package/fesm2022/ngwr-date-picker.mjs.map +1 -1
  24. package/fesm2022/ngwr-donut-chart.mjs +46 -4
  25. package/fesm2022/ngwr-donut-chart.mjs.map +1 -1
  26. package/fesm2022/ngwr-dropdown.mjs +166 -17
  27. package/fesm2022/ngwr-dropdown.mjs.map +1 -1
  28. package/fesm2022/ngwr-event-calendar.mjs +9 -2
  29. package/fesm2022/ngwr-event-calendar.mjs.map +1 -1
  30. package/fesm2022/ngwr-form.mjs +52 -1
  31. package/fesm2022/ngwr-form.mjs.map +1 -1
  32. package/fesm2022/ngwr-gauge.mjs +33 -4
  33. package/fesm2022/ngwr-gauge.mjs.map +1 -1
  34. package/fesm2022/ngwr-i18n-ar.mjs +2 -0
  35. package/fesm2022/ngwr-i18n-ar.mjs.map +1 -1
  36. package/fesm2022/ngwr-i18n-cs.mjs +2 -0
  37. package/fesm2022/ngwr-i18n-cs.mjs.map +1 -1
  38. package/fesm2022/ngwr-i18n-de.mjs +2 -0
  39. package/fesm2022/ngwr-i18n-de.mjs.map +1 -1
  40. package/fesm2022/ngwr-i18n-en.mjs +5 -0
  41. package/fesm2022/ngwr-i18n-en.mjs.map +1 -1
  42. package/fesm2022/ngwr-i18n-es.mjs +2 -0
  43. package/fesm2022/ngwr-i18n-es.mjs.map +1 -1
  44. package/fesm2022/ngwr-i18n-fr.mjs +2 -0
  45. package/fesm2022/ngwr-i18n-fr.mjs.map +1 -1
  46. package/fesm2022/ngwr-i18n-he.mjs +2 -0
  47. package/fesm2022/ngwr-i18n-he.mjs.map +1 -1
  48. package/fesm2022/ngwr-i18n-hi.mjs +2 -0
  49. package/fesm2022/ngwr-i18n-hi.mjs.map +1 -1
  50. package/fesm2022/ngwr-i18n-id.mjs +2 -0
  51. package/fesm2022/ngwr-i18n-id.mjs.map +1 -1
  52. package/fesm2022/ngwr-i18n-it.mjs +2 -0
  53. package/fesm2022/ngwr-i18n-it.mjs.map +1 -1
  54. package/fesm2022/ngwr-i18n-ja.mjs +2 -0
  55. package/fesm2022/ngwr-i18n-ja.mjs.map +1 -1
  56. package/fesm2022/ngwr-i18n-ko.mjs +2 -0
  57. package/fesm2022/ngwr-i18n-ko.mjs.map +1 -1
  58. package/fesm2022/ngwr-i18n-nl.mjs +2 -0
  59. package/fesm2022/ngwr-i18n-nl.mjs.map +1 -1
  60. package/fesm2022/ngwr-i18n-pl.mjs +2 -0
  61. package/fesm2022/ngwr-i18n-pl.mjs.map +1 -1
  62. package/fesm2022/ngwr-i18n-pt.mjs +2 -0
  63. package/fesm2022/ngwr-i18n-pt.mjs.map +1 -1
  64. package/fesm2022/ngwr-i18n-ru.mjs +2 -0
  65. package/fesm2022/ngwr-i18n-ru.mjs.map +1 -1
  66. package/fesm2022/ngwr-i18n-sv.mjs +2 -0
  67. package/fesm2022/ngwr-i18n-sv.mjs.map +1 -1
  68. package/fesm2022/ngwr-i18n-tr.mjs +2 -0
  69. package/fesm2022/ngwr-i18n-tr.mjs.map +1 -1
  70. package/fesm2022/ngwr-i18n-uk.mjs +2 -0
  71. package/fesm2022/ngwr-i18n-uk.mjs.map +1 -1
  72. package/fesm2022/ngwr-i18n-vi.mjs +2 -0
  73. package/fesm2022/ngwr-i18n-vi.mjs.map +1 -1
  74. package/fesm2022/ngwr-i18n-zh-TW.mjs +2 -0
  75. package/fesm2022/ngwr-i18n-zh-TW.mjs.map +1 -1
  76. package/fesm2022/ngwr-i18n-zh.mjs +2 -0
  77. package/fesm2022/ngwr-i18n-zh.mjs.map +1 -1
  78. package/fesm2022/ngwr-image-cropper.mjs +149 -62
  79. package/fesm2022/ngwr-image-cropper.mjs.map +1 -1
  80. package/fesm2022/ngwr-input-number.mjs +12 -0
  81. package/fesm2022/ngwr-input-number.mjs.map +1 -1
  82. package/fesm2022/ngwr-lightbox.mjs +2 -2
  83. package/fesm2022/ngwr-lightbox.mjs.map +1 -1
  84. package/fesm2022/ngwr-line-chart.mjs +16 -5
  85. package/fesm2022/ngwr-line-chart.mjs.map +1 -1
  86. package/fesm2022/ngwr-meter-group-testing.mjs +7 -3
  87. package/fesm2022/ngwr-meter-group-testing.mjs.map +1 -1
  88. package/fesm2022/ngwr-meter-group.mjs +24 -4
  89. package/fesm2022/ngwr-meter-group.mjs.map +1 -1
  90. package/fesm2022/ngwr-page-header.mjs +10 -2
  91. package/fesm2022/ngwr-page-header.mjs.map +1 -1
  92. package/fesm2022/ngwr-popover.mjs +569 -7
  93. package/fesm2022/ngwr-popover.mjs.map +1 -1
  94. package/fesm2022/ngwr-select.mjs +73 -22
  95. package/fesm2022/ngwr-select.mjs.map +1 -1
  96. package/fesm2022/ngwr-sidebar.mjs +7 -0
  97. package/fesm2022/ngwr-sidebar.mjs.map +1 -1
  98. package/fesm2022/ngwr-sparkline.mjs +82 -8
  99. package/fesm2022/ngwr-sparkline.mjs.map +1 -1
  100. package/fesm2022/ngwr-table.mjs +1 -1
  101. package/fesm2022/ngwr-table.mjs.map +1 -1
  102. package/fesm2022/ngwr-toolbar.mjs +7 -0
  103. package/fesm2022/ngwr-toolbar.mjs.map +1 -1
  104. package/fesm2022/ngwr-version.mjs +1 -1
  105. package/fesm2022/ngwr-version.mjs.map +1 -1
  106. package/form/styles/_index.scss +11 -1
  107. package/gauge/styles/_index.scss +1 -0
  108. package/i18n/ar.json +3 -1
  109. package/i18n/cs.json +3 -1
  110. package/i18n/de.json +3 -1
  111. package/i18n/en.json +3 -1
  112. package/i18n/es.json +3 -1
  113. package/i18n/fr.json +3 -1
  114. package/i18n/he.json +3 -1
  115. package/i18n/hi.json +3 -1
  116. package/i18n/id.json +3 -1
  117. package/i18n/it.json +3 -1
  118. package/i18n/ja.json +3 -1
  119. package/i18n/ko.json +3 -1
  120. package/i18n/nl.json +3 -1
  121. package/i18n/pl.json +3 -1
  122. package/i18n/pt.json +3 -1
  123. package/i18n/ru.json +3 -1
  124. package/i18n/sv.json +3 -1
  125. package/i18n/tr.json +3 -1
  126. package/i18n/uk.json +3 -1
  127. package/i18n/vi.json +3 -1
  128. package/i18n/zh-TW.json +3 -1
  129. package/i18n/zh.json +3 -1
  130. package/input/styles/_index.scss +27 -2
  131. package/input-number/styles/_index.scss +69 -0
  132. package/lightbox/styles/_rules.scss +7 -1
  133. package/line-chart/styles/_index.scss +30 -2
  134. package/llms-full.txt +3 -3
  135. package/meter-group/styles/_index.scss +1 -0
  136. package/overlay/styles/_arrow.scss +27 -2
  137. package/package.json +1 -1
  138. package/page-header/styles/_index.scss +37 -1
  139. package/pagination/styles/_index.scss +9 -5
  140. package/popover/styles/_index.scss +73 -5
  141. package/select/styles/_index.scss +64 -13
  142. package/sidebar/styles/_index.scss +34 -2
  143. package/sparkline/styles/_index.scss +19 -0
  144. package/table/styles/_index.scss +55 -0
  145. package/toast/styles/_index.scss +9 -3
  146. package/toolbar/styles/_index.scss +35 -1
  147. package/tree/styles/_index.scss +45 -9
  148. package/types/ngwr-avatar.d.ts +15 -1
  149. package/types/ngwr-bar-chart.d.ts +19 -1
  150. package/types/ngwr-calendar-heatmap.d.ts +29 -1
  151. package/types/ngwr-checkbox.d.ts +2 -0
  152. package/types/ngwr-date-picker.d.ts +308 -43
  153. package/types/ngwr-donut-chart.d.ts +24 -1
  154. package/types/ngwr-dropdown.d.ts +22 -2
  155. package/types/ngwr-event-calendar.d.ts +8 -0
  156. package/types/ngwr-form.d.ts +35 -5
  157. package/types/ngwr-gauge.d.ts +20 -1
  158. package/types/ngwr-image-cropper.d.ts +48 -0
  159. package/types/ngwr-input-number.d.ts +7 -0
  160. package/types/ngwr-line-chart.d.ts +9 -1
  161. package/types/ngwr-meter-group-testing.d.ts +3 -2
  162. package/types/ngwr-meter-group.d.ts +14 -1
  163. package/types/ngwr-page-header.d.ts +10 -2
  164. package/types/ngwr-popover.d.ts +154 -3
  165. package/types/ngwr-select.d.ts +63 -6
  166. package/types/ngwr-sidebar.d.ts +7 -0
  167. package/types/ngwr-sparkline.d.ts +43 -1
  168. package/types/ngwr-toolbar.d.ts +7 -0
  169. package/types/ngwr-version.d.ts +1 -1
@@ -100,9 +100,20 @@ $alert-colors: (
100
100
  color var(--wr-transition-base),
101
101
  background var(--wr-transition-base);
102
102
 
103
- --wr-icon-size: 1rem;
104
-
105
- @include theme.touch-target; // ~20px glyph -> 44px tap area on touch
103
+ // The ✕ is a bare `.wr-icon__svg`, which is `1em`, and a `<button>` does not
104
+ // inherit its font-size: it takes the browser's control font (about 13.33px)
105
+ // unless the app resets it. So the same glyph measured 13.33px on a page with
106
+ // no reset and the surrounding text's size under `ngwr/reset`, which sets
107
+ // `font: inherit`. Inheriting the size makes `1em` the alert's own text in
108
+ // every app. `--wr-icon-size` cannot do this job — only a `<wr-icon>` host
109
+ // reads it, and the rule here used to set it for nothing. The SIZE only,
110
+ // never the `font` shorthand, which would bring the line-height with it; and
111
+ // no `line-height` of its own either, the way `wr-dialog` needs one — the
112
+ // alert stretches its row, so this box is the alert's height whatever the
113
+ // strut says.
114
+ font-size: inherit;
115
+
116
+ @include theme.touch-target; // glyph -> 44px tap area on touch
106
117
 
107
118
  &:hover {
108
119
  color: var(--wr-alert-title);
@@ -7,6 +7,13 @@
7
7
  --wr-avatar-radius: 10%;
8
8
  --wr-avatar-img-opacity: 0;
9
9
  --wr-avatar-ring: var(--wr-color-border);
10
+ // The most of the box projected initials may take, as a share of the size the
11
+ // host publishes as `--wr-avatar-size`. Measured rather than picked: at 0.375
12
+ // the widest capital pairs (`ЖШ`, `WW`) in Inter, Geist and the system face, at
13
+ // weights 400 to 700, keep every inked pixel at least 14% of the radius inside
14
+ // a circle from 16px to 40px; at 0.4 the semibold `WW` in a 16px circle was
15
+ // down to 0.6px.
16
+ --wr-avatar-initials-scale: 0.375;
10
17
 
11
18
  display: inline-flex;
12
19
  align-items: center;
@@ -41,6 +48,43 @@
41
48
  // setting a consumer put on the host) exactly as before this wrapper existed.
42
49
  &__content {
43
50
  display: contents;
51
+ // Inert: a click on the letters belongs to whatever holds the avatar (a chip,
52
+ // a link, a row), and a selection dragged across a list of people should not
53
+ // pick up their initials. Both reach the text through inheritance, which is
54
+ // what a `contents` box still does.
55
+ user-select: none;
56
+ pointer-events: none;
57
+ // No `line-height` or `white-space` here: both inherit already, and setting
58
+ // them changed a 6rem avatar — a longer fallback stopped wrapping and was
59
+ // clipped on both sides, and a projected `rem`-sized span moved by a pixel.
60
+ }
61
+
62
+ // Projected initials take the text around the avatar, and never more than
63
+ // `--wr-avatar-initials-scale` of the box. `min()` against `1em` (the inherited
64
+ // size) is what keeps this to the avatars that need it: a 6rem one beside 16px
65
+ // text keeps its 16px exactly, and a 16px one in a 14px chip draws 6px letters
66
+ // instead of 14px ones clipped by its own edge. The cap bites wherever the
67
+ // inherited size is larger than that share, and that includes a 6rem avatar
68
+ // inside text bigger than 36px (its share at the default root size). Anything
69
+ // projected that is sized in `em` follows the cap, a `<wr-icon>` included.
70
+ //
71
+ // On the HOST rather than on `__content`, because a `contents` wrapper has no
72
+ // line box of its own: its text is laid out on the host's, whose strut keeps
73
+ // the host's font. Shrinking only the wrapper's text sat the letters on the
74
+ // baseline of the bigger strut, about 4px below centre in a 16px circle.
75
+ //
76
+ // Only while something is projected, because the spinner drawn when nothing
77
+ // is sizes itself in `em` too, and it has no business shrinking. A size the
78
+ // host did not publish leaves the `var()` unresolved, and the declaration
79
+ // falls back to inheriting.
80
+ //
81
+ // `:where()` around the WHOLE selector leaves it no specificity, so any
82
+ // unlayered `font-size` rule a consumer writes for the avatar — a type, class
83
+ // or attribute selector, loaded before this stylesheet or after it — still
84
+ // wins. A rule inside a cascade layer does not (Tailwind v4 ships its
85
+ // utilities in one): unlayered CSS beats layered CSS whatever the selector.
86
+ :where(&:has(> .wr-avatar__content:not(:empty))) {
87
+ font-size: min(1em, var(--wr-avatar-size) * var(--wr-avatar-initials-scale));
44
88
  }
45
89
 
46
90
  // Once loaded, how the fallback leaves the paint follows whether the image is
@@ -1,4 +1,5 @@
1
1
  @use '../../theme/styles' as theme;
2
+ @use '../../popover/styles'; // the `.wr-tooltip` chip the hover tooltip renders into
2
3
 
3
4
  .wr-bar-chart {
4
5
  display: block;
@@ -1,4 +1,5 @@
1
1
  @use '../../theme/styles' as theme;
2
+ @use '../../popover/styles'; // the `.wr-tooltip` chip the hover tooltip renders into
2
3
 
3
4
  .wr-calendar-heatmap {
4
5
  display: inline-block;
@@ -2,6 +2,8 @@
2
2
  // is a horizontal row of columns where each column lists the children
3
3
  // of the previously-active node. Picking a leaf commits the path.
4
4
 
5
+ @use '../../theme/styles/mixins' as theme;
6
+
5
7
  // `focus` is the one theme partial these rules still load that emits CSS of its
6
8
  // own — the shared ring mixin lives in the same file as the baseline
7
9
  // `.wr-*:focus-visible` rules, and there is no mixin-only module to take it from.
@@ -21,15 +23,49 @@
21
23
  --wr-cascader-line-height: var(--wr-control-line-height-md);
22
24
  --wr-cascader-col-width: 12rem;
23
25
  --wr-cascader-max-height: 16rem;
24
- // Trigger width floor — hooked for the same reason as `--wr-select-min-width`:
25
- // in a narrow grid column the literal wins over `minmax(0, 1fr)` and the
26
- // trigger overhangs its cell, and the only escape was overriding
27
- // `.wr-cascader__trigger` from outside. Set it to 0 to defer to the layout.
26
+ // Trigger width floor, the same contract as `--wr-select-min-width`. Despite
27
+ // the name it is a DEFAULT width rather than a minimum: an explicit `width`,
28
+ // `width: 100%` in a narrow parent, a `minmax(0, 1fr)` cell and a shrinking
29
+ // flex item all win over it (see `::after` below). Set it to 0 to size a bare
30
+ // cascader to its path alone. This hook is the only way to lower it: a
31
+ // `min-width` on `.wr-cascader__trigger`, the override that did it before the
32
+ // hook existed, no longer reaches the floor.
28
33
  --wr-cascader-min-width: 12rem;
29
34
 
30
- display: inline-flex;
35
+ // One column that shrinks to whatever the layout gives it and, left to size
36
+ // itself, grows to the widest thing in it — the trigger's path or the floor.
37
+ // The floor used to be a `min-width` on the trigger, which no width on the
38
+ // host could beat: `style="width: 6rem"` drew a 192px trigger in a 96px host.
39
+ // `minmax(0, …)` also drops the path's text from the host's min-content —
40
+ // together the two held a cascader showing a path at 210px in a 200px flex
41
+ // row beside a 120px sibling, 138px past the row's edge — so it now shrinks
42
+ // as a flex item the way `[wrInput]` does and the path truncates instead.
43
+ display: inline-grid;
44
+ grid-template-columns: minmax(0, 1fr);
45
+ // The spacer below lands in a row of its own, and it must be a FIXED 0 rather
46
+ // than an implicit `auto` one: a grid shares height beyond its content among
47
+ // its auto rows, so a host stretched by a taller flex or grid row, or given a
48
+ // `height`, drew a 45px trigger in a 60px host with an empty band beneath it.
49
+ grid-template-rows: auto 0;
31
50
  vertical-align: middle;
32
51
 
52
+ // The floor as a zero-height spacer in the same column. A grid track's
53
+ // max-content is its widest item, so a host sized to its content is at least
54
+ // `--wr-cascader-min-width` wide; `max-width: 100%` is ignored while that
55
+ // size is being worked out and applies once the column exists, so a narrower
56
+ // host caps the spacer instead of the spacer stretching the host. `flex: 0 0
57
+ // 0` keeps it from taking room beside the trigger if a consumer turns the
58
+ // host into a flex container (the floor is then inert, never in the way). The
59
+ // template renders exactly one element into the host — the trigger — so this
60
+ // is the column's only other item, and the zero row is the spacer's.
61
+ &::after {
62
+ content: '';
63
+ flex: 0 0 0;
64
+ width: var(--wr-cascader-min-width);
65
+ max-width: 100%;
66
+ height: 0;
67
+ }
68
+
33
69
  &--disabled {
34
70
  --wr-cascader-color: var(--wr-color-on-surface-muted);
35
71
  --wr-cascader-bg: var(--wr-color-fill);
@@ -57,7 +93,10 @@
57
93
  align-items: center;
58
94
  gap: 0.5rem;
59
95
  width: 100%;
60
- min-width: var(--wr-cascader-min-width);
96
+ // Not the floor — that lives on the host, where a width can beat it. Zero
97
+ // lets the trigger follow the host down and the path's ellipsis take the
98
+ // difference.
99
+ min-width: 0;
61
100
  appearance: none;
62
101
  background: var(--wr-cascader-bg);
63
102
  color: var(--wr-cascader-color);
@@ -86,6 +125,20 @@
86
125
  &:disabled {
87
126
  cursor: not-allowed;
88
127
  }
128
+
129
+ // The ×'s touch halo (on `__clear`) CLEARS the path, and nothing undoes that,
130
+ // so it must never reach past the control it belongs to. A 32px halo on a 22px
131
+ // `sm` trigger overhung its border by 5px above and below, and a tap 3px above
132
+ // the trigger, on whatever sits over it, cleared this one. Clipping the block
133
+ // axis bounds the halo to the trigger's own padding box: 20 / 28 / 32px tall
134
+ // for sm / md / lg, and still inside under any padding, line-height or density.
135
+ // A cap re-derived from the hooks overhung again on md and lg as soon as the
136
+ // trigger's `padding-block` was set directly. `clip` rather than `hidden`, so
137
+ // there is no scroll container and no baseline change, and the inline axis stays
138
+ // visible.
139
+ @media (pointer: coarse) {
140
+ overflow-y: clip;
141
+ }
89
142
  }
90
143
 
91
144
  // A read-only trigger still takes focus and still shows its path, so it keeps
@@ -105,6 +158,13 @@
105
158
  &__value,
106
159
  &__placeholder {
107
160
  flex: 1 1 auto;
161
+ // The text line is what gives the trigger its size's height, so it has to
162
+ // exist when there is no text. Nothing chosen and no placeholder left an
163
+ // empty span with no line box, and the trigger fell to the chevron's 1em:
164
+ // measured 18 / 24 / 28px for sm / md / lg, where the contract is 22 / 30 / 36.
165
+ // `1lh` is the span's own line, not the hook, so a `line-height` set on the
166
+ // trigger itself shortens an empty trigger the same way it shortens a filled one.
167
+ min-height: 1lh;
108
168
  overflow: hidden;
109
169
  text-overflow: ellipsis;
110
170
  white-space: nowrap;
@@ -115,7 +175,6 @@
115
175
  }
116
176
 
117
177
  &__chevron {
118
- --wr-icon-size: 1rem;
119
178
  flex: 0 0 auto;
120
179
  color: var(--wr-color-on-surface-muted);
121
180
  transition: transform var(--wr-transition-base);
@@ -126,12 +185,19 @@
126
185
  }
127
186
 
128
187
  &__clear {
129
- --wr-icon-size: 0.875rem;
130
188
  display: inline-flex;
131
189
  align-items: center;
132
190
  justify-content: center;
133
191
  width: 1.125rem;
134
192
  height: 1.125rem;
193
+ // Never adds to the text line, so choosing a value cannot resize the trigger.
194
+ // The 18px circle is taller than `sm`'s 16px line and made an `sm` cascader
195
+ // with a value 24px tall. A symmetric negative margin of at least half the box
196
+ // takes the × out of the line's cross size, for any box up to 2rem, and
197
+ // `align-items: center` keeps the circle centred as drawn. A margin worked out
198
+ // from the hook and the 18px literal let a restyled `.wr-cascader__clear` grow
199
+ // the trigger again: a 24px box made sm / md / lg 28 / 36 / 42px with a value.
200
+ margin-block: -1rem;
135
201
  border-radius: 999px;
136
202
  color: var(--wr-color-on-surface-muted);
137
203
  cursor: pointer;
@@ -143,6 +209,15 @@
143
209
  background: rgba(var(--wr-color-on-surface-rgb), 0.06);
144
210
  color: var(--wr-color-on-surface);
145
211
  }
212
+
213
+ // The same 32px tap area the × has always had on touch, as a halo rather
214
+ // than a box. Growing the box itself put 32px inside the text line: with a
215
+ // value, sm / md / lg measured 38 / 42 / 44px under a coarse pointer against
216
+ // 22 / 30 / 36 without one, so `lg` read as `md` and a pick moved the row.
217
+ // 2rem rather than the mixin's 44px default, which would reach over the
218
+ // chevron 8px away and turn a tap meant to open the panel into a clear. The
219
+ // trigger clips the halo's height to its own (see `__trigger`).
220
+ @include theme.touch-target($size: 2rem);
146
221
  }
147
222
  }
148
223
 
@@ -234,7 +309,6 @@
234
309
  }
235
310
 
236
311
  .wr-cascader__opt-arrow {
237
- --wr-icon-size: 0.875rem;
238
312
  flex: 0 0 auto;
239
313
  color: var(--wr-color-on-surface-muted);
240
314
  }
@@ -247,16 +321,10 @@
247
321
  }
248
322
  }
249
323
 
250
- // Touch pointers: grow the clear button and make the column option rows ~44px
251
- // tall so each is a comfortable tap target. Fine pointers keep the compact
252
- // desktop sizing above.
324
+ // Touch pointers: make the column option rows ~44px tall so each is a
325
+ // comfortable tap target (the clear button's own tap area is on `__clear`).
326
+ // Fine pointers keep the compact desktop sizing above.
253
327
  @media (pointer: coarse) {
254
- .wr-cascader__clear {
255
- --wr-icon-size: 1.125rem;
256
- width: 2rem;
257
- height: 2rem;
258
- }
259
-
260
328
  .wr-cascader-panel .wr-cascader__opt {
261
329
  padding-top: 0.75rem;
262
330
  padding-bottom: 0.75rem;
@@ -48,7 +48,6 @@
48
48
  }
49
49
 
50
50
  &__chevron {
51
- --wr-icon-size: 1rem;
52
51
  flex: 0 0 auto;
53
52
  color: var(--wr-color-on-surface-muted);
54
53
  transition: transform var(--wr-transition-base);
@@ -1,5 +1,20 @@
1
1
  @use '../../theme/styles' as theme;
2
2
 
3
+ // The refusal's live region — a screen reader's copy of what the border says. One
4
+ // element per block, since a BEM class is public API: the range picker's is its own.
5
+ .wr-date-picker__sr-only,
6
+ .wr-date-range-picker__sr-only {
7
+ position: absolute;
8
+ width: 1px;
9
+ height: 1px;
10
+ padding: 0;
11
+ margin: -1px;
12
+ overflow: hidden;
13
+ clip: rect(0, 0, 0, 0);
14
+ white-space: nowrap;
15
+ border: 0;
16
+ }
17
+
3
18
  .wr-date-picker {
4
19
  display: inline-block;
5
20
 
@@ -51,6 +66,21 @@
51
66
  }
52
67
  }
53
68
 
69
+ // A typed entry the picker refused, once it is on show (Enter, or leaving the
70
+ // field). The group owns the border — it strips the inner input's — so the hook
71
+ // is the group's, set exactly as `.wr-form-field--invalid` sets it, and at the
72
+ // same (0,3,0) so a focused group's primary border does not win it back while
73
+ // the user is correcting the text.
74
+ // color-ok: the colour is the SIGHTED half of a state that is not carried by
75
+ // colour alone — the input takes `aria-invalid` and the reason is announced
76
+ // through the picker's live region, or through the surrounding field's message.
77
+ // `danger` against the default `--wr-color-outline` also separates in lightness
78
+ // (3.11:1 light), so the cue survives greyscale.
79
+ .wr-date-picker.wr-date-picker--invalid .wr-input-group,
80
+ .wr-date-range-picker.wr-date-range-picker--invalid .wr-input-group {
81
+ --wr-input-group-border: var(--wr-color-danger);
82
+ }
83
+
54
84
  // Overlay pane — z-index isolation only. <wr-calendar> + the time panel
55
85
  // bring their own chrome.
56
86
  .wr-date-picker-overlay {
@@ -11,8 +11,10 @@
11
11
  :root {
12
12
  --wr-density-y: 1;
13
13
  --wr-density-x: 1;
14
- // `-text` and `-gap` are declared for consumers to read; no shipped
15
- // component multiplies by either yet. `-y` / `-x` are the live ones.
14
+ // `-text` is declared for consumers to read; no shipped component multiplies
15
+ // by it yet. `-gap` scales the space a layout component puts between the
16
+ // controls projected into it, and is there for consumers to read as well.
17
+ // `-y` / `-x` are the padding multipliers.
16
18
  --wr-density-text: 1;
17
19
  --wr-density-gap: 1;
18
20
  }
@@ -1,4 +1,5 @@
1
1
  @use '../../theme/styles' as theme;
2
+ @use '../../popover/styles'; // the `.wr-tooltip` chip the hover tooltip renders into
2
3
 
3
4
  .wr-donut-chart {
4
5
  display: inline-flex;
@@ -27,6 +28,15 @@
27
28
  align-items: center;
28
29
  justify-content: center;
29
30
  text-align: center;
31
+ // The box covers the whole drawing, so it took every pointer event meant for
32
+ // the arcs under it and no slice could be hovered. Only the text itself is
33
+ // hittable, which keeps it selectable.
34
+ pointer-events: none;
35
+ }
36
+
37
+ &__center-value,
38
+ &__center-label {
39
+ pointer-events: auto;
30
40
  }
31
41
 
32
42
  &__center-value {
@@ -1,5 +1,17 @@
1
1
  @use '../../theme/styles' as theme;
2
2
  @use '../../overlay/styles'; // shared .wr-overlay-sheet / .wr-overlay-backdrop for responsive mode
3
+ @use '../../overlay/styles/arrow';
4
+
5
+ /// Gap between the trigger and the menu, as pane padding on the edge facing the
6
+ /// trigger. Padding rather than a CDK offset so a pointer crossing it is still
7
+ /// over the overlay — a `trigger="hover"` menu closes the moment the pointer is
8
+ /// anywhere else.
9
+ $gap: 0.25rem;
10
+
11
+ /// The gap with the arrow on. The arrow reaches `arrow.$half + 1px` (4px) out of
12
+ /// the menu's border, so the plain gap would put its tip ON the trigger; this is
13
+ /// the 8px `wr-popover` keeps, leaving the same 4px between tip and control.
14
+ $arrow-gap: 0.5rem;
3
15
 
4
16
  // The directive marks its host `.wr-dropdown-trigger` — `.wr-dropdown` below is
5
17
  // reachable only if a consumer applies it by hand, which is why it stays: the
@@ -41,17 +53,19 @@
41
53
  }
42
54
 
43
55
  .wr-dropdown-overlay {
44
- // Positional offsets between trigger and menu by side.
56
+ // Positional offsets between trigger and menu by side. The directive swaps the
57
+ // placement class when the menu flips, so the gap moves to the edge that faces
58
+ // the trigger from where it actually opened.
45
59
  &--top,
46
60
  &--top-start,
47
61
  &--top-end {
48
- padding-bottom: 0.25rem;
62
+ padding-bottom: $gap;
49
63
  }
50
64
 
51
65
  &--bottom,
52
66
  &--bottom-start,
53
67
  &--bottom-end {
54
- padding-top: 0.25rem;
68
+ padding-top: $gap;
55
69
  }
56
70
 
57
71
  // The gap sits on the pane edge that faces the trigger. `left` / `right` map
@@ -59,12 +73,60 @@
59
73
  // `FlexibleConnectedPositionStrategy` already resolves against the ambient
60
74
  // `Directionality` — so the padding has to follow the same axis, logically.
61
75
  &--left {
62
- padding-inline-end: 0.25rem;
76
+ padding-inline-end: $gap;
63
77
  }
64
78
 
65
79
  &--right {
66
- padding-inline-start: 0.25rem;
80
+ padding-inline-start: $gap;
67
81
  }
82
+
83
+ // Same edges, wider, when the arrow is drawn. The placement half sits inside
84
+ // `:where()`, so each rule weighs ONE class, exactly like its plain twin above,
85
+ // and wins on source order alone. Two classes of weight would also beat a
86
+ // consumer's own `.wr-dropdown-overlay--bottom-start { padding-top: … }` —
87
+ // which has been the only way to retune this gap, since it has no hook — and
88
+ // with the arrow on by default every such override would be silently ignored.
89
+ &--arrow {
90
+ &:where(.wr-dropdown-overlay--top, .wr-dropdown-overlay--top-start, .wr-dropdown-overlay--top-end) {
91
+ padding-bottom: $arrow-gap;
92
+ }
93
+
94
+ &:where(.wr-dropdown-overlay--bottom, .wr-dropdown-overlay--bottom-start, .wr-dropdown-overlay--bottom-end) {
95
+ padding-top: $arrow-gap;
96
+ }
97
+
98
+ &:where(.wr-dropdown-overlay--left) {
99
+ padding-inline-end: $arrow-gap;
100
+ }
101
+
102
+ &:where(.wr-dropdown-overlay--right) {
103
+ padding-inline-start: $arrow-gap;
104
+ }
105
+ }
106
+ }
107
+
108
+ // The arrow — the rotated square `wr-popover` and `wr-popconfirm` draw, from the
109
+ // same mixin, so the three anchored panels cannot drift apart. Filled and
110
+ // outlined from the menu's OWN hooks rather than the tokens behind them: a
111
+ // consumer who retints one menu through `--wr-dropdown-bg` / `-border` would
112
+ // otherwise get an arrow in the old colours. The `::after` inherits the hooks
113
+ // from the menu it belongs to, so the per-instance value reaches it.
114
+ //
115
+ // Eight placements, not twelve: `WrDropdownPosition` has no `left-start` and
116
+ // friends, and those rules could never match.
117
+ @include arrow.anchored-arrow(
118
+ $pane: '.wr-dropdown-overlay',
119
+ $panel: '> .wr-dropdown-menu',
120
+ $background: var(--wr-dropdown-bg),
121
+ $border: var(--wr-dropdown-border),
122
+ $placements-used: (top, top-start, top-end, bottom, bottom-start, bottom-end, left, right)
123
+ );
124
+
125
+ // `[arrow]="false"` — the directive leaves `--arrow` off the pane and the square
126
+ // is never generated. The mixin's own sheet rule covers `responsive`, which gets
127
+ // no `--arrow` either.
128
+ .wr-dropdown-overlay:not(.wr-dropdown-overlay--arrow) > .wr-dropdown-menu::after {
129
+ content: none;
68
130
  }
69
131
 
70
132
  .wr-dropdown-item {