A sheet read top to bottom (a running calculation): each line worked into the value before it, and interims naming the value where they stand. Reach for it for invoices, budgets and cost sheets.
import { Chain, Plus, Minus, Times, DividedBy, Interim } from "@umriss-ui/calculation";
A chain has no precedence: every line works into the value before it, strictly in order. So Times and DividedBy stand alone between two named values; anything else fails on the first render.
A chain in view stands open – it is the working itself. What should show only on request goes into one line as a tree, which folds. Only an Interim shows the running value (ADR-0028).
A chain starts with a quantity without operator; Minus and Plus take a label and a value directly, and an Interim ends it.
import { Calculation, Chain, Given, Interim, Minus } from "@umriss-ui/calculation";
export default function NetWeight() {
return (
<Calculation aria-label="Net weight, shipment FP-1004223">
<Chain>
<Given label="Gross weight" value={1250} unit="kg" decimals={1} />
<Minus label="Pallet" value={25} unit="kg" decimals={1} />
<Minus label="Crates" value={144} unit="kg" decimals={1} />
<Minus label="Packaging" value={11.5} unit="kg" decimals={1} />
<Interim label="Net weight" unit="kg" decimals={1} />
</Chain>
</Calculation>
);
}
Where a figure is a list of items, write each as a line of its own; a credit is a Minus where it stands.
import { Calculation, Chain, Given, Interim, Minus, Plus } from "@umriss-ui/calculation";
export default function TourCost() {
return (
<Calculation aria-label="Cost of tour T-01, Tuesday">
<Chain>
<Given label="Driver, 6.5 h" value={224.25} unit="€" decimals={2} />
<Plus label="Diesel" value={13.89} unit="€" decimals={2} />
<Plus label="Van, one day" value={58} unit="€" decimals={2} />
<Plus label="Tolls" value={9.4} unit="€" decimals={2} />
<Plus label="Parking" value={6} unit="€" decimals={2} />
<Minus label="Fuel card rebate" value={0.7} unit="€" decimals={2} />
<Interim label="Cost of the tour" unit="€" decimals={2} />
</Chain>
</Calculation>
);
}
A chain has no precedence, so Times and DividedBy stand alone between two interims; after a Plus they fail on the first render.
import { Calculation, Chain, Given, Interim, Plus, Times } from "@umriss-ui/calculation";
export default function MarkUpAndVat() {
return (
<Calculation aria-label="Price of a workshop day">
<Chain>
<Given label="Two facilitators, one day" value={960} unit="€" decimals={2} />
<Plus label="Room and materials" value={180} unit="€" decimals={2} />
<Interim label="Cost of the day" unit="€" decimals={2} />
<Times label="Mark-up" value={1.25} />
<Interim label="Net price" unit="€" decimals={2} />
<Times label="VAT factor" value={1.19} />
<Interim label="Gross price" unit="€" decimals={2} />
</Chain>
</Calculation>
);
}
A chain in view never folds; to show items only on request, put them into one line as a tree: a Sum held by a Minus – a Minus, because contributions are positive by nature. Rows of either sign from data go into a Plus instead; the page Rows from data shows every case.
import { Calculation, Chain, Given, Interim, Minus, Plus, Product, Ref, Sum } from "@umriss-ui/calculation";
const CONTRIBUTIONS = [
{ name: "Pension insurance", rate: 0.093 },
{ name: "Unemployment insurance", rate: 0.013 },
{ name: "Health insurance", rate: 0.082 },
{ name: "Long-term care insurance", rate: 0.017 },
];
export default function Payslip() {
return (
<Calculation aria-label="Payslip, March">
<Chain>
<Given id="gross" label="Gross salary" value={4200} unit="€" decimals={2} />
<Minus label="Income tax" value={612.5} unit="€" decimals={2} />
<Minus label="Church tax" value={49} unit="€" decimals={2} />
<Minus>
<Sum label="Social security contributions" unit="€" decimals={2}>
{CONTRIBUTIONS.map((contribution) => (
<Product key={contribution.name} label={contribution.name} unit="€" decimals={2}>
<Given label={`Rate, ${contribution.name.toLowerCase()}`} value={contribution.rate} format="percent" />
<Ref to="gross" />
</Product>
))}
</Sum>
</Minus>
<Interim label="Net salary" unit="€" decimals={2} />
<Minus label="Capital-forming benefits" value={40} unit="€" decimals={2} />
<Plus label="Travel allowance" value={60} unit="€" decimals={2} />
<Interim label="Amount paid out" unit="€" decimals={2} />
</Chain>
</Calculation>
);
}
.map inside a chain makes each item a Plus, and all stand in view above their interim.
import { Calculation, Chain, Given, Interim, Plus } from "@umriss-ui/calculation";
const ITEMS = [
["Compute, booking API", 4128.4],
["Compute, search", 2864.1],
["Compute, workers", 1741.9],
["Database, primary", 2310],
["Database, replicas", 1540],
["Object storage", 386.2],
["Block storage", 512.8],
["Data transfer out", 948.6],
["Load balancers", 264],
["Queues", 118.3],
["Cache cluster", 690],
["Log storage", 431.7],
["Metrics and traces", 822.5],
["Backups", 207.4],
["Support plan", 1200],
] as const;
export default function FifteenItems() {
const [[firstName, firstAmount], ...rest] = ITEMS;
return (
<Calculation aria-label="Cloud bill, March">
<Chain>
<Given label={firstName} value={firstAmount} unit="€" decimals={2} />
{rest.map(([name, amount]) => (
<Plus key={name} label={name} value={amount} unit="€" decimals={2} />
))}
<Interim label="Cloud bill, March" unit="€" decimals={2} />
</Chain>
</Calculation>
);
}
As an operand a chain folds and stands for its last interim; opened, it stands whole beneath its line.
import { Calculation, Chain, Given, Interim, Minus, Plus, Quotient, Ref } from "@umriss-ui/calculation";
export default function ChainInATree() {
return (
<Calculation aria-label="Delivery rate, North depot, Tuesday">
<Quotient label="Delivery rate" format="percent" target={0.97}>
<Chain>
<Given id="out" label="Parcels out" value={1000} unit="parcels" />
<Minus label="Failed attempts" value={31} unit="parcels" />
<Minus label="Refused at the door" value={6} unit="parcels" />
<Plus label="Delivered on a second run" value={14} unit="parcels" />
<Interim label="Parcels delivered" unit="parcels" />
</Chain>
<Ref to="out" />
</Quotient>
</Calculation>
);
}
An Interim takes target and limits like any quantity; here the error budget left is assessed against two lower limits.
import { Calculation, Chain, Given, Interim, Minus, Product } from "@umriss-ui/calculation";
export default function ErrorBudget() {
return (
<Calculation aria-label="Error budget of Checkout, March">
<Chain>
<Product label="Error budget, March" unit="min" decimals={1}>
<Given label="Minutes in March" value={44640} unit="min" />
<Given label="Allowed downtime" value={0.0005} format="percent" decimals={2} />
</Product>
<Minus label="INC-1039, payment provider timeouts" value={9} unit="min" decimals={1} />
<Minus label="INC-1036, failed deploy" value={4} unit="min" decimals={1} />
<Minus label="INC-1048, card payments time out" value={21} unit="min" decimals={1} />
<Interim
label="Budget left"
unit="min"
decimals={1}
limits={[
{ value: 5, side: "lower", severity: "warning" },
{ value: 0, side: "lower", severity: "alarm" },
]}
/>
</Chain>
</Calculation>
);
}
The keys of Calculation apply here.
ChainProps| Name | Type | Default | Description |
|---|---|---|---|
children | ReactNode | — | The operands, top to bottom: a first quantity with no operator, then
Plus, Minus, Times and DividedBy with their operands and
Interims naming the value where they stand. The last line is an
Interim; a Times or DividedBy stands alone between two named
values.Shown in: Take amounts away from a first quantity, Mix additions and deductions, Multiply between two interims and 22 more |
ChainOperandProps| Name | Type | Default | Description |
|---|---|---|---|
label | string | — | The operand's name, where the line is a given written in place. Left
out where the line holds a quantity as its child. Shown in: Take amounts away from a first quantity, Mix additions and deductions, Multiply between two interims and 17 more |
value | | number | — | The operand's number, where the line is a given written in place.
Absent as for a Given: the line and every interim after it are
absent, with the reason. With metrics, an object by metric id, as on
a Given.Shown in: Take amounts away from a first quantity, Mix additions and deductions, Multiply between two interims and 17 more |
children | ReactNode | — | Instead of label and value: exactly one quantity - a tree, a
<Ref> or a <Chain>.Shown in: Fold a group of lines into one, Write a chain (Calculation), Emphasis inside a group (Calculation) and 12 more |
source | string | — | Where the number came from: a system, a report, a person. from GivenPropsShown in: Say where a number came from (Given), State when a number was true (Given), Staff movement as a chain (Metrics) and 8 more |
asOf | Date | number | — | When the number was true - not when it was fetched. from GivenPropsShown in: Say where a number came from (Given), State when a number was true (Given), The cost per stop of a day's tours (Worked examples) |
ages | FreshnessAges | — | The ages at which asOf turns stale and lost. With both, the given
carries a freshness through core; with asOf alone, only the time. from GivenPropsShown in: Say where a number came from (Given), The cost per stop of a day's tours (Worked examples) |
id | string | — | Makes the quantity referable: <Ref to> stands for it elsewhere. Needed
only for a quantity used more than once. from QuantityPropsShown in: Fold a group of lines into one, Put a chain inside a tree, The availability of a service (Installation) and 10 more |
unit | string | — | A label written after the number - "min", "pcs", "€". Never converted
and never checked; the calculation does no unit algebra. from QuantityPropsShown in: Take amounts away from a first quantity, Mix additions and deductions, Multiply between two interims and 42 more |
format | "percent" | — | percent presents a plain ratio as per cent: 0.916 is shown as 91.6 %.
The quantity stays a ratio in every operation. from QuantityPropsShown in: Fold a group of lines into one, Put a chain inside a tree, Hold an interim to its limits and 17 more |
decimals | number | one digit for a percentage, at most two for a derived number, a given as it was given | Fixed number of fraction digits as shown. Rounding happens only in
presentation; every operation uses the full value. from QuantityPropsShown in: Take amounts away from a first quantity, Mix additions and deductions, Multiply between two interims and 29 more |
target | number | — | The value this quantity is meant to reach - missed, never violated. from QuantityPropsShown in: Put a chain inside a tree, The availability of a service (Installation), Write a tree (Calculation) and 6 more |
limits | readonly Limit[] | — | Bounds the quantity is assessed against, through core's assess(). from QuantityPropsShown in: Hold an interim to its limits, Density (Calculation), Hold a figure to its limits (Tree) and 5 more |
explanation | string | — | A sentence of plain text beside the line: why the quantity is here, or
what it means. from QuantityPropsShown in: Say where a number came from (Given), A number one metric does not have (Metrics), Staff movement as a chain (Metrics) and 2 more |
aside | ReactNode | — | Content of the caller's own beside the line - a sparkline, a link. It
never takes part in the calculation. from QuantityPropsShown in: Say where a number came from (Given) |
emphasis | "strong" | "muted" | — | How much the line stands out among the others (ADR-0049): strong to
be read first, muted to recede - a muted line still counts in full.
Not on the Result, which is already the heaviest line. from QuantityPropsShown in: A stronger line (Calculation), A quieter line (Calculation), Emphasis inside a group (Calculation) |
rule | "above" | — | above rules the line off from the ones before it, where a section
starts that is not an interim. Not on the Result. from QuantityPropsShown in: A rule above (Calculation), Emphasis inside a group (Calculation) |
QuantityProps| Name | Type | Default | Description |
|---|---|---|---|
labelrequired | string | — | What the quantity is called. It stands on its line, wherever it is an
operand, and in the accessible sentence. Shown in: Take amounts away from a first quantity, Mix additions and deductions, Multiply between two interims and 50 more |
id | string | — | Makes the quantity referable: <Ref to> stands for it elsewhere. Needed
only for a quantity used more than once.Shown in: Fold a group of lines into one, Put a chain inside a tree, The availability of a service (Installation) and 10 more |
unit | string | — | A label written after the number - "min", "pcs", "€". Never converted
and never checked; the calculation does no unit algebra. Shown in: Take amounts away from a first quantity, Mix additions and deductions, Multiply between two interims and 42 more |
format | "percent" | — | percent presents a plain ratio as per cent: 0.916 is shown as 91.6 %.
The quantity stays a ratio in every operation.Shown in: Fold a group of lines into one, Put a chain inside a tree, Hold an interim to its limits and 17 more |
decimals | number | one digit for a percentage, at most two for a derived number, a given as it was given | Fixed number of fraction digits as shown. Rounding happens only in
presentation; every operation uses the full value. Shown in: Take amounts away from a first quantity, Mix additions and deductions, Multiply between two interims and 29 more |
target | number | — | The value this quantity is meant to reach - missed, never violated. Shown in: Put a chain inside a tree, The availability of a service (Installation), Write a tree (Calculation) and 6 more |
limits | readonly Limit[] | — | Bounds the quantity is assessed against, through core's assess().Shown in: Hold an interim to its limits, Density (Calculation), Hold a figure to its limits (Tree) and 5 more |
explanation | string | — | A sentence of plain text beside the line: why the quantity is here, or
what it means. Shown in: Say where a number came from (Given), A number one metric does not have (Metrics), Staff movement as a chain (Metrics) and 2 more |
aside | ReactNode | — | Content of the caller's own beside the line - a sparkline, a link. It
never takes part in the calculation. Shown in: Say where a number came from (Given) |
emphasis | "strong" | "muted" | — | How much the line stands out among the others (ADR-0049): strong to
be read first, muted to recede - a muted line still counts in full.
Not on the Result, which is already the heaviest line.Shown in: A stronger line (Calculation), A quieter line (Calculation), Emphasis inside a group (Calculation) |
rule | "above" | — | above rules the line off from the ones before it, where a section
starts that is not an interim. Not on the Result.Shown in: A rule above (Calculation), Emphasis inside a group (Calculation) |
MetricValuesA given's numbers where the calculation has metrics: one per metric id,
null where the number is absent.
type MetricValues = Readonly<Record<string, number | null>>;FreshnessAgesFrom @umriss-ui/core.
The two ages at which a value changes its state, in milliseconds. Both
bounds belong to the older state: exactly five minutes old is already
stale.
alt <= lost is the sensible order. Stated the other way round, the
harder statement still wins - the middle state simply falls away.
LimitFrom @umriss-ui/core.
A bound: a value, a side, a severity.
No tolerance band as input. A band is derived - two limits of equal
severity on opposite sides enclose one - and as an input type it would
force a null on one edge in the one-sided case.
| Name | Type | Default | Description |
|---|---|---|---|
valuerequired | number | — | Shown in: Hold an interim to its limits, Density (Calculation), Hold a figure to its limits (Tree) and 3 more |
siderequired | Side"upper" | "lower" | — | Shown in: Hold an interim to its limits, Density (Calculation), Hold a figure to its limits (Tree) and 3 more |
severityrequired | Severity"warning" | "alarm" | — | Shown in: Hold an interim to its limits, Density (Calculation), Hold a figure to its limits (Tree) and 3 more |
SideFrom @umriss-ui/core.
Whether a limit is an upper or a lower bound.
type Side = "upper" | "lower";SeverityFrom @umriss-ui/core.
How seriously a violation weighs: one severity that advises and one that calls for action. Exactly two; for a third distinction one sets a second limit.
type Severity = "warning" | "alarm";What umriss deliberately does not build, and why: ADR-0032.