@carto/ps-react-ui 4.18.0 → 4.20.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.
@@ -0,0 +1,46 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { getSpreadBand } from './band'
3
+
4
+ describe('getSpreadBand', () => {
5
+ it('reads `spread` as a percentage of the centre value by default', () => {
6
+ expect(getSpreadBand({ value: 1000, spread: 5 })).toEqual({
7
+ min: 950,
8
+ max: 1050,
9
+ })
10
+ })
11
+
12
+ it('reads `spread` as a percentage when asked explicitly', () => {
13
+ expect(
14
+ getSpreadBand({ value: 204_113, spread: 5, variant: 'percentage' }),
15
+ ).toEqual({ min: 193_907.35, max: 214_318.65 })
16
+ })
17
+
18
+ it('reads `spread` as raw units when the caller opts into `absolute`', () => {
19
+ expect(
20
+ getSpreadBand({ value: 204_113, spread: 5, variant: 'absolute' }),
21
+ ).toEqual({ min: 204_108, max: 204_118 })
22
+ })
23
+
24
+ it('collapses to the centre value when `spread` is 0', () => {
25
+ expect(getSpreadBand({ value: 42, spread: 0 })).toEqual({
26
+ min: 42,
27
+ max: 42,
28
+ })
29
+ })
30
+
31
+ // A percentage of a negative centre value is a negative offset, which would
32
+ // otherwise invert the band into `min: -180, max: -220`.
33
+ it('keeps `min <= max` for a negative centre value', () => {
34
+ expect(getSpreadBand({ value: -200, spread: 10 })).toEqual({
35
+ min: -220,
36
+ max: -180,
37
+ })
38
+ })
39
+
40
+ it('keeps `min <= max` for a negative `spread`', () => {
41
+ expect(getSpreadBand({ value: 1000, spread: -5 })).toEqual({
42
+ min: 950,
43
+ max: 1050,
44
+ })
45
+ })
46
+ })
@@ -0,0 +1,47 @@
1
+ /**
2
+ * How a `spread` amount is applied to a centre value.
3
+ *
4
+ * - `percentage` — `spread` is a whole-number percentage of the centre
5
+ * value, so `5` means +/-5%.
6
+ * - `absolute` — `spread` is a raw offset in the value's own units.
7
+ */
8
+ export type SpreadVariant = 'percentage' | 'absolute'
9
+
10
+ export interface GetSpreadBandOptions {
11
+ /** The centre value the band is drawn around. */
12
+ value: number
13
+ /** Half-width of the band, read according to `variant`. */
14
+ spread: number
15
+ /** Defaults to `percentage`. */
16
+ variant?: SpreadVariant
17
+ }
18
+
19
+ /**
20
+ * Derives the `min` / `max` band around a centre value, for feeding a
21
+ * {@link SpreadDataItem} from a single aggregated number (a prediction, a
22
+ * target) plus a tolerance — rather than from a real column range.
23
+ *
24
+ * `spread` is read as a percentage by default, which is what a tolerance
25
+ * almost always is: an absolute offset is meaningless without knowing the
26
+ * value's magnitude, and gets visually lost on large values (+/-5 on a
27
+ * prediction of 204,113 renders as a range indistinguishable from a single
28
+ * number).
29
+ *
30
+ * @example
31
+ * getSpreadBand({ value: 1000, spread: 5 })
32
+ * // -> { min: 950, max: 1050 }
33
+ * getSpreadBand({ value: 1000, spread: 5, variant: 'absolute' })
34
+ * // -> { min: 995, max: 1005 }
35
+ */
36
+ export function getSpreadBand({
37
+ value,
38
+ spread,
39
+ variant = 'percentage',
40
+ }: GetSpreadBandOptions): { min: number; max: number } {
41
+ const offset = variant === 'absolute' ? spread : (spread / 100) * value
42
+ // Keep `min <= max`: a percentage of a negative centre value (or a negative
43
+ // `spread`) yields a negative offset, which would invert the band.
44
+ const width = Math.abs(offset)
45
+
46
+ return { min: value - width, max: value + width }
47
+ }
@@ -3,4 +3,5 @@ export { SpreadUI, type SpreadUIProps } from './spread-ui'
3
3
  export { Separator } from './separator'
4
4
  export { SpreadSkeleton, type SpreadSkeletonProps } from './skeleton'
5
5
  export { createSpreadDownloadConfig } from './download'
6
+ export { getSpreadBand, type SpreadVariant, type GetSpreadBandOptions } from './band'
6
7
  export type { SpreadDataItem, SpreadWidgetData } from './types'