GrValue

Package: @feugene/granularitycoreGroup: data

A quantity with its affixes: prefix, value, suffix — and nothing else.

Machine-translated from the Russian original, not yet reviewed. Read the original

When to take it

  • you are writing a component of your own with a quantity — a tile, a cell, a row of a report: the affixes will be styled the same way as in the rest of the package, with no copying of classes;
  • the currency stands on the right100 ₽ rather than $100: the side is set by which affix the symbol is put into;
  • the quantity is not a number — “2 h 15 min”, ”—”, “n/a” are printed as they are;
  • markup of your own is needed around the value — the sign, an arrow, a hidden label go into the slots between the parts of the record.

When to take something else

NeedTake
The sign and a tone by the sign inside a line of textGrDelta
A large metric as a tile, with a label and a trendGrStatistic
The value as a status or a labelGrBadge
A “property → value” pairGrDescriptionList
Formatting a number by localeformatStatisticValue from GrStatistic

The quantity arrives ready

The primitive formats nothing: the digit groups, the precision and the separators are the concern of whoever passes the value. That is not a saving but a boundary of responsibility: “2 h 15 min” and ”—” are quantities just as 1 284 500 is, and a component that tried to parse them would spoil both.

Formatting by locale exists in the package — formatStatisticValue; it is available separately and combines with the primitive in any way at all.

An affix is not necessarily a unit of measurement

The component does not decide what an affix is. That is its whole point: $, , %, ms, “per order” are different things, and one rule for all of them will lie one day.

The defaults are chosen by frequency rather than by truth:

The prefixThe suffix
the colouras in the quantitydimmed
the type sizeas in the quantitysmaller
the spacingnonethere is some

Behind them stands an observation: on the left people more often write the currency, and it is part of the sum; on the right more often a unit, and it does not belong to the quantity. $14.99 and 42 % come out by themselves.

A currency on the right — remove the dimming

The rouble is written after the number, but it is as much part of the sum as the $ before it. The default of the suffix therefore does not fit it, and it is changed with tokens:

<GrValue
  :value="formatted"
  suffix="₽"
  style="--gr-value-suffix-color: currentColor; --gr-value-suffix-size: 1em"
/>

An application where it is always so finds it more convenient to declare that in a class once:

.amount { --gr-value-suffix-color: currentColor; --gr-value-suffix-size: 1em; }

Precisely currentColor and 1em rather than inherit. For a custom property inherit is the keyword for inheriting the variable itself: it means “take its value from the parent”, and the parent does not have it, so the token stays undefined. The fallback then fires as usual, and the affix looks untouched — the edit silently gives nothing. currentColor and 1em are real values, and they mean exactly what is needed: the colour and the type size of the quantity.

The component does not choose the side of the symbol. The temptation to infer it from the locale is there, but Intl places the currency by locale rather than by currency: in ru-RU they all end up on the right, the dollar included (1 284 500,50 $), and in en-US all on the left, the rouble included (RUB 1,284,500.50). The familiar ”₽ on the right, $ and € on the left” is a mixed product rule, and there is nowhere to infer it from. It is decided by the consumer, by choosing the affix.

The tone reaches the affix by itself

The colour of the prefix is inherit by default rather than a copy of the tone role. The difference is visible on a negative sum: the whole record turns red, not the number without the currency sign. The tone is therefore set once on the parent — that is what GrStatistic does — and the primitive does not need to know about it.

The type size is inherited the same way: the primitive has no size scale of its own, and the quantity is set in the type size of the place it stands in.

Limits

The positions of the affixes are fixed: the prefix on the left, the suffix on the right. The order is changed neither by a prop nor by the locale — a quantity whose affixes swap places is two different records, and only the author of the screen can choose between them.

Playground 2

Loading…

Code
<GrValue />

Install

npm i @feugene/granularity

Import

import { GrValue } from '@feugene/granularity/components/GrValue'

API

Props

PropTypedefaultDescription
valuestring | number | null | undefinedA value ready for display. It is not formatted: "2 h 15 min" and "—" are quantities too.
prefixstring | undefinedAn addition before the value: a currency, a sign of approximation.
suffixstring | undefinedAn addition after the value: a unit of measurement, a currency on the right.

Slots

SlotTypeDescription
defaultanyThe value instead of the prop.
prefixanyAn addition before the value.
suffixanyAn addition after the value.
leadanyBefore the prefix: the sign of the quantity in `GrDelta`, an arrow of the direction. A slot rather than a prop — the markup there belongs to the consumer.
trailanyAfter the value: a hidden node for the screen reader in `GrStatistic`.

Examples 1

Currency left, currency right, unit

Валюта слева$1 284 500,5 Дефолт: приписка слева набирается как число
Валюта справа1 284 500,5 Два токена — и приписка стала частью суммы
Единица измерения42% Дефолт: приписка справа приглушена и отбита

Affixes
<script setup lang="ts">
import { GrCard, GrValue } from '@feugene/granularity'

const amount = new Intl.NumberFormat('ru-RU', { maximumFractionDigits: 2 }).format(1284500.5)
</script>

<template>
  <div class="grid gap-4 sm:grid-cols-3">
    <GrCard variant="outlined" padding="md" body-class="grid gap-1">
      <span class="text-[length:var(--gr-text-xs)] text-[var(--gr-muted-fg)]">Валюта слева</span>
      <span class="text-[length:var(--gr-text-2xl)] font-semibold">
        <GrValue :value="amount" prefix="$" />
      </span>
      <span class="text-[length:var(--gr-text-xs)] text-[var(--gr-muted-fg)]">
        Дефолт: приписка слева набирается как число
      </span>
    </GrCard>

    <!--
      Рубль пишут справа, но он такая же часть суммы. Дефолт суффикса ему не
      подходит, и его снимают токенами — компонент за потребителя не решает.
    -->
    <GrCard variant="outlined" padding="md" body-class="grid gap-1">
      <span class="text-[length:var(--gr-text-xs)] text-[var(--gr-muted-fg)]">Валюта справа</span>
      <span
        class="text-[length:var(--gr-text-2xl)] font-semibold"
        style="--gr-value-suffix-color: currentColor; --gr-value-suffix-size: 1em"
      >
        <GrValue :value="amount" suffix="" />
      </span>
      <span class="text-[length:var(--gr-text-xs)] text-[var(--gr-muted-fg)]">
        Два токена — и приписка стала частью суммы
      </span>
    </GrCard>

    <GrCard variant="outlined" padding="md" body-class="grid gap-1">
      <span class="text-[length:var(--gr-text-xs)] text-[var(--gr-muted-fg)]">Единица измерения</span>
      <span class="text-[length:var(--gr-text-2xl)] font-semibold">
        <GrValue :value="42" suffix="%" />
      </span>
      <span class="text-[length:var(--gr-text-xs)] text-[var(--gr-muted-fg)]">
        Дефолт: приписка справа приглушена и отбита
      </span>
    </GrCard>
  </div>
</template>

Component documentationAll components