@uni-design-system/uni-angular 9.0.1 → 10.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,294 @@
1
1
  # @uni-design-system/uni-angular
2
2
 
3
+ ## 10.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - [`21b655d`](https://github.com/uni-design-system/uni/commit/21b655df0f93e2e2de6a22ccf38050b474d4e5ab) Thanks [@gaenglish](https://github.com/gaenglish)! - `uni-slider` is rebuilt on custom thumbs instead of `<input type="range">`,
8
+ gaining range mode, marks, a value readout and an exact decimal step model. This
9
+ is a breaking change to both the component's value shape and its theme options.
10
+
11
+ **Why it could not stay native.** One `<input type="range">` cannot carry two
12
+ thumbs, so range mode, thumb crossing and per-thumb ARIA bounds were all
13
+ unreachable; marks and a value tooltip were unstyleable through it. The
14
+ alternative — a second component for the range case — would have meant two
15
+ keyboard implementations to keep in step, which is the drift the shared step
16
+ model exists to prevent. What the platform was giving us (the slider ARIA
17
+ pattern and the keyboard map) is reimplemented explicitly and covered by 29
18
+ specs.
19
+
20
+ **Breaking: value shape.**
21
+
22
+ ```ts
23
+ // before
24
+ value = model<number>(0);
25
+ // after — the shape follows `mode`, and `null` is empty
26
+ value = model<number | UniNumberRange | null>(null);
27
+ ```
28
+
29
+ A `single` slider still reads and writes a plain number, so
30
+ `[(value)]="volume"` is unchanged. Code that relied on `value()` being
31
+ non-nullable, or that read it without narrowing, now needs to handle `null` and
32
+ the `{ start, end }` range.
33
+
34
+ **Breaking: theme options.** `slider.options.color` is **removed**. Fill and
35
+ thumb colour now come from the `variant` role pair, the rule every other
36
+ component follows, so `variant="warn"` recolours a slider with no theme edit. A
37
+ theme that set `color` should delete it and pass `variant` at the call site.
38
+ `trackColor` now defaults to `primary-container` rather than `surface-variant`.
39
+
40
+ New options: `thumbBorderRadius`, `minTouchTarget`, `markSize`, `markColor`,
41
+ `labelTypeface`, `labelColor`, `tooltipColor`, `tooltipTextColor`,
42
+ `tooltipShadow`, `tooltipBorderRadius`, `transitionMs`. `trackHeight`,
43
+ `thumbSize` and `borderRadius` are unchanged.
44
+
45
+ **New capability.**
46
+ - `mode="range"` — two thumbs, a `{ start, end }` value, `minGap` to fence the
47
+ ends apart. Thumbs may cross and swap, and the dragged one keeps focus.
48
+ - `marks` and `snapToMarks` — labelled stops, spoken in place of the number.
49
+ - `valueDisplay` — `none`, `inline`, `tooltip`, or `input`, which seats a
50
+ compact `uni-number-input` at the trailing edge, two-way bound to the same
51
+ value: drag for the ballpark, type for the exact figure. Single mode only.
52
+ - `origin` — anchor the fill somewhere other than `min`, for sliders spanning ±.
53
+ - `sliding` and `changed` outputs, replacing an implicit per-frame model write.
54
+ **Bind `changed` in forms**; `sliding` fires every frame of a drag.
55
+ - `largeStep`, defaulting to a tenth of the range, for `PageUp`/`PageDown` and
56
+ `Shift+Arrow`.
57
+ - Right-to-left support: the horizontal arrows and the track mirror; the value
58
+ does not.
59
+
60
+ **Also new: the `cdk/number` primitives** this is built on, shared with the
61
+ numeric input family still to come — exact scaled-`BigInt` decimal arithmetic
62
+ (`stepDecimal`, `roundDecimal`, `clampDecimal`), locale-aware
63
+ `parseNumber`/`formatNumber` over `Intl`, and `createPressRepeat` for
64
+ hold-to-repeat stepper buttons. Stepping `0.1` twenty times from `0` now lands
65
+ on exactly `2`, and `1.15` rounds to `1.2` where `(1.15).toFixed(1)` gives
66
+ `'1.1'`.
67
+
68
+ ### Minor Changes
69
+
70
+ - [`2a79bb8`](https://github.com/uni-design-system/uni/commit/2a79bb8ebd8fd0b5cd792697972b6f48b444c80f) Thanks [@gaenglish](https://github.com/gaenglish)! - New `uni-number-input`: the field for a quantity, a price, a percentage or a
71
+ measurement, with locale-aware parsing, `Intl` formatting on commit,
72
+ prefix/suffix adornments, min/max/step fences and steppers that hold to repeat.
73
+
74
+ ```html
75
+ <uni-number-input label="Quantity" [(value)]="qty" [min]="1" />
76
+ <uni-number-input label="Unit price" currency="USD" [(value)]="price" />
77
+ ```
78
+
79
+ **Why not `<input type="number">`.** Per the HTML value sanitization algorithm,
80
+ a number input whose text is not a valid floating-point number reports
81
+ `value === ''`. Type `12,50` as most of Europe does, or paste `1,234.56` from a
82
+ spreadsheet, and the app reads an empty field with no way to tell that from a
83
+ blank one — a data-loss bug, and the reason this is `type="text"` with
84
+ `role="spinbutton"`. The platform control also cannot group thousands, cannot
85
+ place an affix outside the editable text, has ~10px unstyleable spinners below
86
+ the WCAG 2.2 target minimum, changes value on the scroll wheel while focused,
87
+ and steps in floats.
88
+
89
+ **What it does instead.**
90
+ - **Presets** — `decimal`, `integer`, `currency`, `percent` — supply decimals,
91
+ grouping, affix and `inputmode` together, so a money field is `label`,
92
+ `currency="USD"`, `[(value)]`. `numberFormat` is the escape hatch, merged over
93
+ the preset.
94
+ - **Parses what people actually type**: canonical ASCII always, locale grouping
95
+ (`1.234,56` in German), pasted affixes and currency symbols, accounting
96
+ negatives `(1,234.56)`, localized digit systems, compact `1.5k`, and — behind
97
+ `allowExpressions` — spreadsheet arithmetic like `12*3`, via a shunting-yard
98
+ parser that never calls `eval`.
99
+ - **Unreadable text stays in the field**, flagged with a dashed underline and a
100
+ `rejected` event, rather than being silently swallowed.
101
+ - **Exact arithmetic.** Stepping `0.1` twenty times from `0` lands on exactly
102
+ `2`; `1.15` rounds to `1.2` where `(1.15).toFixed(1)` gives `'1.1'`. A second
103
+ `valueAsString` model carries digits a `number` cannot, and a dev-mode warning
104
+ fires when a bound `value` cannot round-trip.
105
+ - **Percent never divides behind your back**: `preset="percent"` shows `15%` for
106
+ `15`. Models that really are fractions set `valueIsFraction`.
107
+ - Clamping on commit rather than per keystroke, visible fences that disable the
108
+ matching stepper and announce, `wrap` for cyclic fields, `emptyStepValue`,
109
+ four stepper layouts, and hold-to-repeat that announces once on release.
110
+
111
+ `uni-slider` gains `valueDisplay="input"`, which seats one of these as its
112
+ readout — drag for the ballpark, type for the exact value.
113
+
114
+ Adds `numberInput` to `ComponentName` with a theme entry. Field chrome is not
115
+ duplicated there: colour, border, radius and focus come from the shared `input`
116
+ options via `uni-input-box`, so a number field restyles with every other field.
117
+
118
+ `uni-input-box` gains a `managedInset` input. The themed leading inset normally
119
+ rides the inner `<input>`, which is right while the text is the field's leading
120
+ edge and wrong the moment an adornment sits in front — a currency prefix would
121
+ hug the border while the number it belongs to sat indented past it. A field with
122
+ adornments sets `managedInset` and places the inset on whichever element is
123
+ actually first. Existing fields are unaffected: the default is `false`.
124
+
125
+ - [`e7875ee`](https://github.com/uni-design-system/uni/commit/e7875ee81030a703f4ca1904bb94cb8ddc7f57b9) Thanks [@gaenglish](https://github.com/gaenglish)! - New `uni-number-range-input`: two linked numeric fields in one chrome with a
126
+ single `{ start, end }` value — price filters, thresholds, tolerances. This
127
+ completes the numeric family alongside `uni-number-input`,
128
+ `uni-quantity-stepper` and the rebuilt `uni-slider`.
129
+
130
+ ```html
131
+ <uni-number-range-input label="Price range" currency="USD" [(value)]="price" [minGap]="50" />
132
+ ```
133
+
134
+ `start`/`end` deliberately match `UniDateRange`, so the library has one range
135
+ vocabulary, and they never collide with the `min`/`max` **inputs**, which mean
136
+ the fence rather than the value.
137
+
138
+ **The rules that make it one field rather than two glued together:**
139
+ - **Either end alone is a valid value.** `{ start: 50 }` means "50 and up",
140
+ which is a real filter. This is where it diverges from `uni-date-time-input`,
141
+ whose two parts are two halves of one answer.
142
+ - **Stepping is fenced; typing swaps.** A stepper can never walk one end through
143
+ the other — its wall is the other end, held off by `minGap`, and each end's
144
+ `aria-valuemin`/`aria-valuemax` report that wall rather than the outer bounds.
145
+ A _typed_ backwards commit is swapped and announced instead, the rule
146
+ `uni-calendar` applies to a backwards date range: clamping against the other
147
+ end would destroy the number just entered.
148
+ - **`minGap` pushes the end you edited**, not the other one, which is what makes
149
+ stepping behave as a fence rather than dragging the range along.
150
+ - A refused draft flags only the end it was typed into; the other stays valid.
151
+ - `preset`, `currency`, `prefix`, `suffix`, `decimals`, `grouping`, `locale` and
152
+ `roundingMode` are forwarded to both ends so the halves always read alike.
153
+
154
+ It owns its commit path rather than nesting two `uni-number-input`s, because the
155
+ two behaviours above need _different_ bounds — a stepper must be fenced at the
156
+ other end while a typed commit must arrive un-clamped — and a child field
157
+ applies one bound pair to both. The arithmetic, parsing and formatting are still
158
+ the shared `cdk/number` primitives.
159
+
160
+ Adds `numberRangeInput` to `ComponentName` with a theme entry (`partGap`,
161
+ `dividerText`, `dividerColor`). Field chrome is not duplicated there: colour,
162
+ border, radius and focus come from the shared `input` options via
163
+ `uni-input-box`. `dividerText` is literal punctuation rather than an icon token —
164
+ an en dash between two numbers is not a glyph a theme swaps artwork for.
165
+
166
+ - [`589cecb`](https://github.com/uni-design-system/uni/commit/589cecb178d9119eea3fbc3f3cd9149eefdaa036) Thanks [@gaenglish](https://github.com/gaenglish)! - New `uni-quantity-stepper`: `− 3 +` for cart lines, table cells and seat counts
167
+ — the numeric core with no field chrome, no label and no room for either.
168
+
169
+ ```html
170
+ <uni-quantity-stepper label="Quantity, Blue T-shirt (M)" [(value)]="qty" [min]="1" />
171
+ ```
172
+
173
+ A separate component rather than a `chrome="bare"` flag on `uni-number-input`,
174
+ because this control is defined by what it does _not_ have — presets, affixes,
175
+ expressions, four stepper layouts — and eight inputs are easier to write
176
+ correctly than forty plus a list of which ones to leave alone. The arithmetic,
177
+ parsing and hold-to-repeat come from the same `cdk/number` primitives, so
178
+ `1,200` and the keyboard map behave identically in both.
179
+ - **`deleteAtMin`** is the cart pattern in one attribute: at the floor the
180
+ decrement becomes a remove affordance, renamed `Remove {label}`, and emits
181
+ `removed` rather than stepping to zero. Without it every shop reimplements the
182
+ same `value === 1 ? remove() : step(-1)` branch outside the component. (The
183
+ spec called this output `emptied`; that is a native `HTMLMediaElement` event
184
+ name, which `@angular-eslint/no-output-native` rightly rejects, and `removed`
185
+ is already what `uni-tag` calls the same request.)
186
+ - The middle is a real input by default — typing `12` beats tapping `+` eleven
187
+ times, and it takes the same grouped and locale-aware entry the field does.
188
+ `editable=false` renders the number as text for read-mostly tables, and the
189
+ buttons become the tab stops since there is nothing else to focus.
190
+ - `size` is `sm` / `md` / `lg` at 24 / 32 / 40px _outer_ height, so an `md`
191
+ stepper lines up with a 32px field beside it, with the buttons square at that
192
+ height. `md` and `lg` clear the 24×24 pointer target of WCAG 2.2 SC 2.5.8;
193
+ `sm` leaves 22px inside its border and is the dense desktop option.
194
+
195
+ Adds `quantityStepper` to `ComponentName` with a theme entry. Unlike the other
196
+ numeric controls it does **not** inherit the shared `input` chrome — it is not a
197
+ field — so it carries its own container tokens, defaulted to the same values
198
+ `input` uses so a cart stepper and a form field look related out of the box.
199
+ Height comes from the entry's `sizes` block rather than an option.
200
+
201
+ ### Patch Changes
202
+
203
+ - [`ce94c8a`](https://github.com/uni-design-system/uni/commit/ce94c8a6acb5c70351fecfaca3469c46727c2aa4) Thanks [@gaenglish](https://github.com/gaenglish)! - `uni-angular`'s build now runs `tsc --noEmit` first, closing the last gap left
204
+ by the earlier "typecheck the builds" change — core and react already did this,
205
+ angular did not.
206
+
207
+ `ng-packagr` alone does not surface every type error in the package. A real one
208
+ reached a Storybook build unnoticed: a form control declaring
209
+ `min = input(0)` where `FormValueControl` types the property as
210
+ `InputSignal<number | undefined>` (Signal Forms syncs it from `min()`
211
+ validators), which is a variance error `pnpm build` reported as success. Every
212
+ form control must declare `min`/`max` as `input<number | undefined>(…)` and read
213
+ a `resolvedMin()` computed internally.
214
+
215
+ Also here:
216
+ - A `type-check` script, matching core and react, so `turbo type-check` covers
217
+ the whole workspace.
218
+ - `prototypes/**` is excluded from the package tsconfig. Those are standalone
219
+ design explorations that reference modules and dependencies which do not
220
+ exist in this package — excluding them is what makes a real typecheck
221
+ possible over the code that ships.
222
+ - Three latent type errors fixed: `vitest.config.ts` took `defineConfig` from
223
+ `vite`, whose overload does not accept the `test` block (it comes from
224
+ `vitest/config`); `spacing.spec.ts` lost callback inference through an
225
+ untyped `vi.spyOn` return; and `radio.motion.spec.ts` typed a `motion`
226
+ argument as `Record<string, unknown>` rather than `Motions`.
227
+
228
+ - [`5cf120b`](https://github.com/uni-design-system/uni/commit/5cf120b9cb268e74a2ed062d8df2cf5cf9749750) Thanks [@gaenglish](https://github.com/gaenglish)! - Lint MDX prose for stray `{`, which MDX compiles to a JSX expression.
229
+
230
+ Writing `named "Increase {label}"` in a docs bullet makes `label` a reference to
231
+ an undefined variable, and the page dies at runtime with
232
+ `ReferenceError: label is not defined` under Storybook's "The component failed to
233
+ render properly" banner. Nothing caught it: it is a React render error rather
234
+ than a compile error, so **`build-storybook` passes**, and `check-doc-links.mjs`
235
+ only validates link ids. Only opening the page found it — twice.
236
+
237
+ `scripts/check-mdx-braces.mjs` now runs as part of the package's `lint` script,
238
+ so `turbo run lint` (and therefore CI) fails on it. It skips the four places a
239
+ brace is legitimate — fenced code blocks, inline code spans including ones that
240
+ soft-wrap across a line, ESM `import`/`export` statements, and JSX tags such as
241
+ `of={Stories.X}` or `rows={[…]}` — plus MDX comment containers. Hits are
242
+ reported as `file:line:column` with the offending line and the fix: backtick the
243
+ text, or escape the brace as `\{`.
244
+
245
+ Also available on its own as `pnpm lint:mdx`.
246
+
247
+ - [`05f991f`](https://github.com/uni-design-system/uni/commit/05f991f0cc6b0895777763ede7605b5e274dc0a1) Thanks [@gaenglish](https://github.com/gaenglish)! - Three cosmetic fixes in the numeric family.
248
+
249
+ **`uni-quantity-stepper`'s dividers were heavier than its frame.** The rules
250
+ either side of the value took `dividerColor: 'outline'` — a solid grey — against
251
+ an outer border of the 8%-alpha `light` token, so the control read as three
252
+ pieces stuck together rather than one frame. They now take the **same `border`
253
+ token as the container** and move with it on focus, so a focused stepper is not
254
+ accented on the outside and grey down the middle. `dividerColor` remains as an
255
+ opt-in override for a deliberately distinct rule, and is unset in the base theme.
256
+
257
+ **`uni-quantity-stepper` had no focus state.** Every other field gets its focus
258
+ chrome from `uni-input-box`, which the stepper deliberately does not use — and
259
+ its inner input clears its own outline via `removeInputPlatformStyling`, so
260
+ focusing the middle showed nothing at all. The container now carries the same
261
+ `:has(input:focus)` rule and the same `input` theme tokens the box applies
262
+ (`focusOutline`, `focusOutlineOffset`, and the optional `focusBorder` /
263
+ `focusShadow` / `focusColor`), so a stepper highlights exactly like the field
264
+ beside it — including in themes such as Wellsourced that express focus as a
265
+ border and ring rather than an outline. Error state still wins, keeping a
266
+ flagged control visibly flagged while it is corrected.
267
+
268
+ **A trailing suffix sat against the right border.** The leading inset was
269
+ already handled, so the two sides did not match. `uni-number-range-input` — which
270
+ has no steppers — now insets both edges of its row, and `uni-number-input` insets
271
+ the trailing edge whenever no stepper occupies it (`stepperLayout="none"`, or a
272
+ read-only field). Where a stepper _is_ present the trailing edge is still left
273
+ to it, because a button is meant to reach the border.
274
+
275
+ Both insets ride the row rather than the `<input>`: `uni-input-box` styles
276
+ `& input` at a higher specificity than a component class can reach, so padding
277
+ set on the input itself is silently dropped.
278
+
279
+ - [`1fe8941`](https://github.com/uni-design-system/uni/commit/1fe89415c65d38c97d610fb5725e8a050432192f) Thanks [@gaenglish](https://github.com/gaenglish)! - `uni-tag-input`'s first chip no longer rides the left border.
280
+
281
+ The themed leading inset was applied by `uni-input-box` to the inner `<input>`,
282
+ which is the field's leading edge only while it is empty. Once a chip existed,
283
+ the chip sat flush against the border while the text after it stayed indented.
284
+ The chip row now owns the inset — it is the leading content — via the
285
+ `managedInset` input added alongside `uni-number-input`, so the first chip and
286
+ an empty field's placeholder both start at the same 8px as every other field's
287
+ text. Wrapped chip rows are unaffected; vertical padding already handled those.
288
+
289
+ - Updated dependencies [[`2a79bb8`](https://github.com/uni-design-system/uni/commit/2a79bb8ebd8fd0b5cd792697972b6f48b444c80f), [`e7875ee`](https://github.com/uni-design-system/uni/commit/e7875ee81030a703f4ca1904bb94cb8ddc7f57b9), [`589cecb`](https://github.com/uni-design-system/uni/commit/589cecb178d9119eea3fbc3f3cd9149eefdaa036), [`21b655d`](https://github.com/uni-design-system/uni/commit/21b655df0f93e2e2de6a22ccf38050b474d4e5ab)]:
290
+ - @uni-design-system/uni-core@10.0.0
291
+
3
292
  ## 9.0.1
4
293
 
5
294
  ## 9.0.0