@umriss-ui/calculation 0.2.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 +71 -0
- package/LICENSE +20 -0
- package/README.md +113 -0
- package/dist/calculation.css +1 -0
- package/dist/calculation.js +409 -0
- package/dist/index.d.ts +132 -0
- package/package.json +77 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Changes to `@umriss-ui/calculation`
|
|
2
|
+
|
|
3
|
+
This document describes what changes for **callers** of the package: exports,
|
|
4
|
+
props, behaviour. What was worked on in the repository stands in the
|
|
5
|
+
repository's journal (`docs/journal.md`) and in the specs under `.scratch/`.
|
|
6
|
+
|
|
7
|
+
**The numbers.** The middle digit rises when something is added; the last one
|
|
8
|
+
when something is repaired. As long as the first digit is `0`, no number
|
|
9
|
+
promises compatibility — which is why whatever changes existing behaviour stands
|
|
10
|
+
under a heading "Changed" of its own, no matter which digit rose.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 0.2.0 – A chain stands open (Sep. 2026)
|
|
15
|
+
|
|
16
|
+
0.1.0 was committed in the repository but never published; this is the first
|
|
17
|
+
version on npm.
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- **A chain in view no longer folds.** Every line and every interim stands
|
|
22
|
+
open, as on paper - gross salary, each deduction with its minus, the net
|
|
23
|
+
salary beneath a rule once they all stand. What should show only on request
|
|
24
|
+
is written as a tree in one line: a `<Sum>` held by a `<Minus>` folds like
|
|
25
|
+
any tree and opens beneath its line. A chain that is an operand still folds,
|
|
26
|
+
as a whole, and opens whole.
|
|
27
|
+
- **No disclosure column where nothing can fold**: a calculation of givens
|
|
28
|
+
and chains alone starts its labels at the surface's edge.
|
|
29
|
+
- **The hover band on operands** is half the accent's surface instead of a
|
|
30
|
+
grey, so coloured text in a marked row keeps its contrast.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 0.1.0 – First release (Sep. 2026)
|
|
35
|
+
|
|
36
|
+
A calculation a reader can follow and redo: the package evaluates every
|
|
37
|
+
operation it shows, so what stands on the screen cannot disagree with the
|
|
38
|
+
number. Takes `@umriss-ui/core` `^0.5.0` as a peer, for its wording.
|
|
39
|
+
|
|
40
|
+
### Added
|
|
41
|
+
|
|
42
|
+
- **`Calculation`** and the **tree** (ADR-0027): `Given`, `Sum`, `Difference`,
|
|
43
|
+
`Product`, `Quotient` and `Ref`, written as nested elements the way the
|
|
44
|
+
derivation reads. Full precision throughout, rounding only on the screen.
|
|
45
|
+
- **The chain** (ADR-0028): `Chain`, `Plus`, `Minus`, `Times`, `DividedBy` and
|
|
46
|
+
`Interim` - a calculation read top to bottom, each operand worked into the
|
|
47
|
+
value before it, ended by an interim. No precedence: a `Times` or
|
|
48
|
+
`DividedBy` stands alone between two named values. Tree and chain mix both
|
|
49
|
+
ways.
|
|
50
|
+
- **Absence and approximation.** A missing given or a division by zero makes
|
|
51
|
+
every dependent quantity absent, with the reason - never zero. Where the
|
|
52
|
+
rounded operands do not give the rounded result, the result is marked "≈".
|
|
53
|
+
- **Assessment and freshness** through core: `target` and `limits` on any
|
|
54
|
+
quantity, `source`, `asOf` and `ages` on a given; the worst verdict inside a
|
|
55
|
+
folded row stands beside it.
|
|
56
|
+
- **The statement**: a surface in the material of the library's tables and
|
|
57
|
+
cards, rows of one height, fixed columns for label, names, operator, number,
|
|
58
|
+
unit and assessment. The outermost statement stands as on paper and closes
|
|
59
|
+
on the Result, underlined twice. Every other derivation is folded and opens
|
|
60
|
+
beneath its row, joined to it by a falling line and closing with
|
|
61
|
+
"= label"; the row never moves. A folded row shows the formula it hides in
|
|
62
|
+
names, or how many operands there are above four. The verdict is core's
|
|
63
|
+
`Badge`; "≈" explains itself in a `Tooltip`. Narrower than 34 rem the
|
|
64
|
+
layout gives the numbers room.
|
|
65
|
+
- **Accessibility**: a nested list, one sentence per row - "Availability
|
|
66
|
+
equals Run time divided by Planned production time, equals 412 min divided
|
|
67
|
+
by 450 min, equals 91.6 percent" - and a disclosure button per derivation.
|
|
68
|
+
- **Development errors** on the first render, each saying which and where: a
|
|
69
|
+
`Ref` to nothing (listing the ids that exist), a circle through references,
|
|
70
|
+
a wrong operand count, a duplicate id, a component of the caller's own
|
|
71
|
+
wrapping an element, and every rule of the chain.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Roman Händler
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# @umriss-ui/calculation
|
|
2
|
+
|
|
3
|
+
A calculation a reader can follow and redo. A plant screen shows a figure — an
|
|
4
|
+
OEE of 82 %, a cost per piece — and this package shows how it came about, as a
|
|
5
|
+
statement of account: the operator before each number, the final result above a
|
|
6
|
+
double rule, and every figure opening beneath itself into the calculation it
|
|
7
|
+
came from, down to the numbers at its root. The package **performs every operation it
|
|
8
|
+
shows**, so what stands on the screen cannot disagree with the number.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pnpm add @umriss-ui/calculation @umriss-ui/core
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
`@umriss-ui/core` is a **peer dependency**: the calculation takes its styling
|
|
17
|
+
regime, formats, wording, assessment and freshness from there. React 18 or 19
|
|
18
|
+
as a peer as well.
|
|
19
|
+
|
|
20
|
+
## Two forms, written as they are shown
|
|
21
|
+
|
|
22
|
+
A **tree** suits a figure put together from factors. Operators are elements,
|
|
23
|
+
their children are their operands in order, givens are the leaves (ADR-0027). A
|
|
24
|
+
quantity used twice is defined once and stands elsewhere as a `<Ref>`:
|
|
25
|
+
|
|
26
|
+
```tsx
|
|
27
|
+
import { Calculation, Difference, Given, Product, Quotient, Ref } from "@umriss-ui/calculation";
|
|
28
|
+
|
|
29
|
+
<Calculation aria-label="OEE, early shift">
|
|
30
|
+
<Product label="OEE" format="percent" target={0.85}>
|
|
31
|
+
<Quotient label="Availability" format="percent">
|
|
32
|
+
<Difference id="runtime" label="Run time" unit="min">
|
|
33
|
+
<Given id="planned" label="Planned production time" value={450} unit="min" />
|
|
34
|
+
<Given label="Downtime" value={38} unit="min" />
|
|
35
|
+
</Difference>
|
|
36
|
+
<Ref to="planned" />
|
|
37
|
+
</Quotient>
|
|
38
|
+
<Quotient label="Performance" format="percent">
|
|
39
|
+
<Product label="Ideal run time" unit="min">
|
|
40
|
+
<Given label="Ideal cycle time" value={0.8} unit="min/pc" />
|
|
41
|
+
<Given id="total" label="Total count" value={480} unit="pcs" />
|
|
42
|
+
</Product>
|
|
43
|
+
<Ref to="runtime" />
|
|
44
|
+
</Quotient>
|
|
45
|
+
<Quotient label="Quality" format="percent">
|
|
46
|
+
<Given label="Good count" value={461} unit="pcs" />
|
|
47
|
+
<Ref to="total" />
|
|
48
|
+
</Quotient>
|
|
49
|
+
</Product>
|
|
50
|
+
</Calculation>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
A **chain** suits a sheet read top to bottom (ADR-0028). A first quantity,
|
|
54
|
+
then `Plus`, `Minus`, `Times` or `DividedBy`, each working its operand into the
|
|
55
|
+
value before it, strictly in order, and `Interim`s naming the value where they
|
|
56
|
+
stand. A chain stands open, as on paper - each line in view, a rule, the
|
|
57
|
+
interim. What should show only on request goes into one line as a tree, and
|
|
58
|
+
folds:
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
<Calculation aria-label="Payslip, March">
|
|
62
|
+
<Chain>
|
|
63
|
+
<Given id="gross" label="Gross salary" value={4200} unit="€" />
|
|
64
|
+
<Minus label="Income tax" value={612.5} unit="€" />
|
|
65
|
+
<Minus label="Church tax" value={49} unit="€" />
|
|
66
|
+
<Minus>
|
|
67
|
+
<Sum label="Social security contributions" unit="€">
|
|
68
|
+
<Given label="Pension insurance" value={390.6} unit="€" />
|
|
69
|
+
<Given label="Unemployment insurance" value={54.6} unit="€" />
|
|
70
|
+
<Given label="Health insurance" value={344.4} unit="€" />
|
|
71
|
+
<Given label="Long-term care insurance" value={71.4} unit="€" />
|
|
72
|
+
</Sum>
|
|
73
|
+
</Minus>
|
|
74
|
+
<Interim label="Net salary" unit="€" decimals={2} />
|
|
75
|
+
<Minus label="Capital-forming benefits" value={40} unit="€" />
|
|
76
|
+
<Plus label="Travel allowance" value={60} unit="€" />
|
|
77
|
+
<Interim label="Amount paid out" unit="€" decimals={2} />
|
|
78
|
+
</Chain>
|
|
79
|
+
</Calculation>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
The two mix: a line of a chain can hold a tree, and a chain can be an operand
|
|
83
|
+
in a tree - there it folds, and opens whole.
|
|
84
|
+
|
|
85
|
+
- **Four operators, and nothing else:** `Sum`, `Product`, `Difference`
|
|
86
|
+
(a − b − c) take two or more operands, `Quotient` exactly two.
|
|
87
|
+
- **A chain has no precedence**, so a `Times` or `DividedBy` stands alone
|
|
88
|
+
between two named values; anything else fails on the first render.
|
|
89
|
+
- **Full precision throughout**; rounding happens only on the screen. Where the
|
|
90
|
+
rounded operands do not give the rounded result, the result carries "≈".
|
|
91
|
+
- **An absent given is never zero.** Every quantity that depends on it is
|
|
92
|
+
absent too, with the reason; a quotient by zero is absent with its own.
|
|
93
|
+
- **Target and limits** are assessed through core's `assess()`. A folded
|
|
94
|
+
derivation that holds a worse verdict than its line says so, quietly.
|
|
95
|
+
- **A statement on a surface**, in the material of the library's tables and
|
|
96
|
+
cards. A derivation opens beneath its row as one group with it and closes
|
|
97
|
+
with "= label"; folded, the row shows the formula it hides in names — or how
|
|
98
|
+
many operands there are, above four. The Result is the last row, its number
|
|
99
|
+
underlined twice.
|
|
100
|
+
- **Every line is read as one sentence** — "Availability equals Run time
|
|
101
|
+
divided by Planned production time, equals 412 min divided by 450 min,
|
|
102
|
+
equals 91.6 percent" — and every derivation is a disclosure.
|
|
103
|
+
- **A declaration the calculation cannot evaluate fails on the first render**
|
|
104
|
+
with a message saying which and where: a `Ref` to nothing (listing the ids
|
|
105
|
+
that exist), a circle through references, a wrong operand count, a
|
|
106
|
+
duplicate id, a component of your own wrapping `Given`. `.map` inside an
|
|
107
|
+
operator works.
|
|
108
|
+
|
|
109
|
+
## Documentation
|
|
110
|
+
|
|
111
|
+
The demo is the documentation: `pnpm dev:calculation` in the repository, port
|
|
112
|
+
4177. The vocabulary — calculation, quantity, result, given, operator, operand,
|
|
113
|
+
derivation, reference — stands in `CONTEXT.md` under "Calculations".
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@layer umriss.tokens,umriss.base,umriss.components;@layer umriss.base{._text_1hv7a_23{font-family:var(--u-font-sans);font-size:var(--u-text-md);line-height:var(--u-leading-normal);color:var(--u-color-text);-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}._field_1hv7a_34{caret-color:var(--u-color-accent)}._scroll_1hv7a_40{scrollbar-width:thin;scrollbar-color:color-mix(in srgb,var(--u-color-text) 25%,transparent) transparent}._scroll_1hv7a_40::-webkit-scrollbar{width:10px;height:10px}._scroll_1hv7a_40::-webkit-scrollbar-thumb{background:color-mix(in srgb,var(--u-color-text) 22%,transparent);border-radius:var(--u-radius-full);border:3px solid transparent;background-clip:content-box}._scroll_1hv7a_40::-webkit-scrollbar-track{background:transparent}}@layer umriss.components{._ring_1hv7a_65:focus-visible{outline:none;box-shadow:var(--u-focus-ring)}@media(pointer:coarse){._field_1hv7a_34._field_1hv7a_34{font-size:16px}}._field_1hv7a_34:-webkit-autofill,._field_1hv7a_34:-webkit-autofill:hover,._field_1hv7a_34:-webkit-autofill:focus{-webkit-box-shadow:inset 0 0 0 1000px var(--u-color-surface),0 0 0 1px var(--u-edge-color-strong);-webkit-text-fill-color:var(--u-color-text);caret-color:var(--u-color-accent)}}@layer umriss.tokens,umriss.base,umriss.components;@layer umriss.components{._frame_1hhx1_21,._frame_1hhx1_21:before,._frame_1hhx1_21:after{box-sizing:border-box}._calculation_1hhx1_39,._calculation_1hhx1_39:before,._calculation_1hhx1_39:after{box-sizing:border-box}._disclosure_1hhx1_53,._disclosure_1hhx1_53:before,._disclosure_1hhx1_53:after{box-sizing:border-box}._list_1hhx1_57,._list_1hhx1_57:before,._list_1hhx1_57:after{box-sizing:border-box}._item_1hhx1_58,._item_1hhx1_58:before,._item_1hhx1_58:after{box-sizing:border-box}._derivation_1hhx1_59,._derivation_1hhx1_59:before,._derivation_1hhx1_59:after{box-sizing:border-box}._line_1hhx1_109,._line_1hhx1_109:before,._line_1hhx1_109:after{box-sizing:border-box}._labelCell_1hhx1_151,._labelCell_1hhx1_151:before,._labelCell_1hhx1_151:after{box-sizing:border-box}._names_1hhx1_152,._names_1hhx1_152:before,._names_1hhx1_152:after{box-sizing:border-box}._operator_1hhx1_153,._operator_1hhx1_153:before,._operator_1hhx1_153:after{box-sizing:border-box}._amount_1hhx1_154,._amount_1hhx1_154:before,._amount_1hhx1_154:after{box-sizing:border-box}._unit_1hhx1_155,._unit_1hhx1_155:before,._unit_1hhx1_155:after{box-sizing:border-box}._assessment_1hhx1_156,._assessment_1hhx1_156:before,._assessment_1hhx1_156:after{box-sizing:border-box}._label_1hhx1_151,._label_1hhx1_151:before,._label_1hhx1_151:after{box-sizing:border-box}._approximate_1hhx1_269,._approximate_1hhx1_269:before,._approximate_1hhx1_269:after{box-sizing:border-box}._target_1hhx1_297,._target_1hhx1_297:before,._target_1hhx1_297:after{box-sizing:border-box}._worst_1hhx1_298,._worst_1hhx1_298:before,._worst_1hhx1_298:after{box-sizing:border-box}._notes_1hhx1_390,._notes_1hhx1_390:before,._notes_1hhx1_390:after{box-sizing:border-box}._freshness_1hhx1_404,._freshness_1hhx1_404:before,._freshness_1hhx1_404:after{box-sizing:border-box}._frame_1hhx1_21{font-size:var(--u-text-sm);border-radius:var(--u-radius-md);corner-shape:squircle;background:var(--u-color-surface);box-shadow:var(--u-edge);overflow:hidden;--disclosure-size: 20px;--disclosure-gap: var(--u-space-2);container-type:inline-size;width:100%}._calculation_1hhx1_39{display:grid;grid-template-columns:fit-content(22rem) minmax(0,1fr) auto auto auto auto;margin:0;padding:0;list-style:none}._frame_1hhx1_21[data-still]{--disclosure-size: 0px;--disclosure-gap: 0px}._frame_1hhx1_21[data-still] ._disclosure_1hhx1_53{display:none}._list_1hhx1_57,._item_1hhx1_58,._derivation_1hhx1_59{display:grid;grid-template-columns:subgrid;grid-column:1 / -1;margin:0;padding:0;list-style:none}._derivation_1hhx1_59[hidden]{display:none}._derivation_1hhx1_59:not([hidden]){animation:_derivationIn_1hhx1_1 var(--u-duration-fast) var(--u-ease-out)}@keyframes _derivationIn_1hhx1_1{0%{opacity:0}}._item_1hhx1_58{--fall: calc(var(--u-space-3) + var(--depth, 0) * var(--u-space-5) + var(--disclosure-size) / 2 - 1px)}._derivation_1hhx1_59{position:relative}._derivation_1hhx1_59:before{content:"";position:absolute;top:0;bottom:0;left:var(--fall);z-index:1;width:2px;background:var(--u-color-accent);pointer-events:none}:not(._derivation_1hhx1_59)>._item_1hhx1_58[data-open]>._line_1hhx1_109:not([data-mark]),:not(._derivation_1hhx1_59)>._item_1hhx1_58>._derivation_1hhx1_59{background-color:var(--u-color-surface-sunken)}._derivation_1hhx1_59 ._line_1hhx1_109{border-top-color:transparent}._line_1hhx1_109{position:relative;display:grid;grid-template-columns:subgrid;grid-column:1 / -1;align-items:baseline;border-top:1px solid var(--u-hairline)}._calculation_1hhx1_39>._item_1hhx1_58:first-child>._line_1hhx1_109:first-child,._calculation_1hhx1_39>._item_1hhx1_58:first-child>._list_1hhx1_57>._item_1hhx1_58:first-child>._line_1hhx1_109{border-top-color:transparent}._line_1hhx1_109[data-foldable]{cursor:pointer}._line_1hhx1_109[data-mark]{background:color-mix(in srgb,var(--u-color-accent-subtle) 50%,transparent)}._line_1hhx1_109[data-mark=use]{background:var(--u-color-accent-subtle)}._labelCell_1hhx1_151,._names_1hhx1_152,._operator_1hhx1_153,._amount_1hhx1_154,._unit_1hhx1_155,._assessment_1hhx1_156{grid-row:1;padding-top:var(--u-space-2);padding-bottom:var(--u-space-2)}._labelCell_1hhx1_151{grid-column:1;display:flex;align-items:baseline;gap:var(--disclosure-gap);min-width:0;padding-left:calc(var(--u-space-3) + var(--depth, 0) * var(--u-space-5));padding-right:var(--u-space-5)}._label_1hhx1_151{min-width:0;color:var(--u-color-text)}._line_1hhx1_109[data-reference] ._label_1hhx1_151{font-style:italic;color:var(--u-color-text-secondary)}._disclosure_1hhx1_53{flex:none;align-self:center;display:inline-flex;align-items:center;justify-content:center;width:var(--disclosure-size);height:var(--disclosure-size);padding:0;border:none;border-radius:var(--u-radius-xs);corner-shape:squircle;background:none;color:var(--u-color-text-muted)}button._disclosure_1hhx1_53{box-sizing:border-box}button._disclosure_1hhx1_53{cursor:pointer}button._disclosure_1hhx1_53:hover,._line_1hhx1_109[data-foldable]:hover button._disclosure_1hhx1_53{box-sizing:border-box}button._disclosure_1hhx1_53:hover,._line_1hhx1_109[data-foldable]:hover button._disclosure_1hhx1_53{background:var(--u-color-surface-sunken);color:var(--u-color-text)}._item_1hhx1_58[data-open]>._line_1hhx1_109 button._disclosure_1hhx1_53{box-sizing:border-box}._item_1hhx1_58[data-open]>._line_1hhx1_109 button._disclosure_1hhx1_53{color:var(--u-color-accent)}button._disclosure_1hhx1_53:focus-visible{box-sizing:border-box}button._disclosure_1hhx1_53:focus-visible{outline:none;box-shadow:var(--u-focus-ring)}._disclosure_1hhx1_53 svg{box-sizing:border-box}._disclosure_1hhx1_53 svg{transition:transform var(--u-duration-fast) var(--u-ease-out)}._disclosure_1hhx1_53 svg[data-open]{box-sizing:border-box}._disclosure_1hhx1_53 svg[data-open]{transform:rotate(90deg)}._names_1hhx1_152{grid-column:2;min-width:0;overflow:hidden;padding-right:var(--u-space-4);white-space:nowrap;text-overflow:ellipsis;color:var(--u-color-text-secondary)}._operator_1hhx1_153,._amount_1hhx1_154,._unit_1hhx1_155{font-family:var(--u-font-mono);font-variant-numeric:tabular-nums;font-feature-settings:"tnum" 1;white-space:nowrap}._operator_1hhx1_153{grid-column:3;padding-right:var(--u-space-3);text-align:right;color:var(--u-color-text-secondary)}._amount_1hhx1_154{grid-column:4;text-align:right;color:var(--u-color-text)}._amount_1hhx1_154[data-verdict=unknown]{color:var(--u-color-text-secondary)}._approximate_1hhx1_269{display:inline-block;margin-right:var(--u-space-1);color:var(--u-color-text-secondary);cursor:help}._unit_1hhx1_155{grid-column:5;padding-left:var(--u-space-1);padding-right:var(--u-space-3);color:var(--u-color-text-secondary)}._assessment_1hhx1_156{grid-column:6;display:flex;align-items:baseline;justify-content:flex-end;gap:var(--u-space-2);padding-right:var(--u-space-3);white-space:nowrap}._assessment_1hhx1_156:empty{padding-right:0}._target_1hhx1_297,._worst_1hhx1_298{font-size:var(--u-text-xs);color:var(--u-color-text-secondary)}._worst_1hhx1_298:before{content:"● "}._worst_1hhx1_298[data-verdict=unknown]:before{color:var(--u-color-text-muted)}._worst_1hhx1_298[data-verdict=warning]:before{color:var(--u-color-warning)}._worst_1hhx1_298[data-verdict=alarm]:before{color:var(--u-color-danger)}._line_1hhx1_109[data-inner]>._amount_1hhx1_154{color:var(--u-color-text-secondary)}._line_1hhx1_109[data-kind=interim] ._label_1hhx1_151,._line_1hhx1_109[data-kind=interim]>._amount_1hhx1_154{font-weight:var(--u-weight-medium)}._line_1hhx1_109[data-kind=interim]:before{content:"";grid-row:1;grid-column:3 / 6;align-self:start;border-top:1px solid var(--u-color-text-secondary);pointer-events:none}._line_1hhx1_109[data-closing] ._label_1hhx1_151{font-weight:var(--u-weight-medium);color:var(--u-color-text-secondary)}._line_1hhx1_109[data-closing]:before{content:"";grid-row:1;grid-column:3 / 6;align-self:start;border-top:1px solid var(--u-color-text-muted);pointer-events:none}._line_1hhx1_109[data-kind=result]{border-top:1px solid var(--u-edge-color-strong)}._line_1hhx1_109[data-kind=result]:not([data-mark]){background:var(--u-color-surface-sunken)}._line_1hhx1_109[data-kind=result] ._label_1hhx1_151{font-weight:var(--u-weight-semibold)}._line_1hhx1_109[data-kind=result]>._amount_1hhx1_154{font-size:var(--u-text-md);font-weight:var(--u-weight-semibold)}._line_1hhx1_109[data-kind=result]>._amount_1hhx1_154:not([data-verdict=unknown]){text-decoration-line:underline;text-decoration-style:double;text-underline-offset:5px}._notes_1hhx1_390{grid-row:2;grid-column:1 / 3;display:flex;flex-wrap:wrap;align-items:center;column-gap:var(--u-space-3);margin-top:calc(-1 * var(--u-space-1));padding:0 var(--u-space-4) var(--u-space-2) calc(var(--u-space-3) + var(--depth, 0) * var(--u-space-5) + var(--disclosure-size) + var(--disclosure-gap));font-size:var(--u-text-xs);color:var(--u-color-text-secondary)}._freshness_1hhx1_404[data-freshness=stale]{color:var(--u-color-warning)}._freshness_1hhx1_404[data-freshness=lost]{color:var(--u-color-danger);font-weight:var(--u-weight-medium)}@container (max-width: 34rem){._calculation_1hhx1_39{grid-template-columns:minmax(0,1fr) 0 auto auto auto 0}._names_1hhx1_152{display:none}._labelCell_1hhx1_151{padding-right:var(--u-space-3)}._assessment_1hhx1_156:not(:empty){grid-area:2 / 1 / 3 / -1;justify-content:flex-start;flex-wrap:wrap;padding:0 var(--u-space-3) var(--u-space-2) calc(var(--u-space-3) + var(--depth, 0) * var(--u-space-5) + var(--disclosure-size) + var(--disclosure-gap));white-space:normal}._notes_1hhx1_390{grid-row:3;grid-column:1 / -1}}@media(prefers-reduced-motion:reduce){._disclosure_1hhx1_53 svg{transition:none}._derivation_1hhx1_59:not([hidden]){animation:none}}}
|
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
import "./calculation.css";
|
|
2
|
+
import { jsx as f, jsxs as k } from "react/jsx-runtime";
|
|
3
|
+
import { Children as ve, isValidElement as j, Fragment as ge, useId as _e, useState as oe } from "react";
|
|
4
|
+
import { assess as be, verdictWeight as K, useFormats as me, useWording as fe, VisuallyHidden as $e, AngleGlyph as xe, Tooltip as re, Badge as ie, useFreshness as Ne } from "@umriss-ui/core";
|
|
5
|
+
function we(t) {
|
|
6
|
+
return t.decimals !== void 0 ? t.decimals : t.format === "percent" ? 1 : t.given ? void 0 : 2;
|
|
7
|
+
}
|
|
8
|
+
function ye(t, n) {
|
|
9
|
+
return n === void 0 ? t : Math.sign(t) * Number(Math.abs(t).toFixed(n));
|
|
10
|
+
}
|
|
11
|
+
function G(t, n) {
|
|
12
|
+
return ye(t.format === "percent" ? n * 100 : n, we(t));
|
|
13
|
+
}
|
|
14
|
+
const Oe = (t, n) => t.format === "percent" ? n / 100 : n;
|
|
15
|
+
function le(t, n, v) {
|
|
16
|
+
const [m, ...i] = n;
|
|
17
|
+
switch (t) {
|
|
18
|
+
case "sum":
|
|
19
|
+
return i.reduce((e, h, o) => v[o + 1] ? e - h : e + h, m);
|
|
20
|
+
case "difference":
|
|
21
|
+
return i.reduce((e, h) => e - h, m);
|
|
22
|
+
case "product":
|
|
23
|
+
return i.reduce((e, h) => e * h, m);
|
|
24
|
+
case "quotient":
|
|
25
|
+
return m / i[0];
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
const ce = (t, n) => n === void 0 || t !== void 0 && K(t) >= K(n) ? t : n;
|
|
29
|
+
function Se(t) {
|
|
30
|
+
const n = /* @__PURE__ */ new Map(), v = (m) => {
|
|
31
|
+
var $, y, T;
|
|
32
|
+
const i = n.get(m);
|
|
33
|
+
if (i) return i;
|
|
34
|
+
const e = t.quantities.get(m);
|
|
35
|
+
let h = null, o, w = !1, C;
|
|
36
|
+
if (e.given) {
|
|
37
|
+
const x = e.given.value;
|
|
38
|
+
typeof x == "number" && Number.isFinite(x) ? h = x : o = { kind: "missing", label: e.label };
|
|
39
|
+
} else {
|
|
40
|
+
const x = e.operands.map((a) => ({ operand: a, ...v(a.key) }));
|
|
41
|
+
for (const { operand: a, verdict: c, worst: r } of x) {
|
|
42
|
+
const _ = a.reference ? c : ce(c, r);
|
|
43
|
+
C = ce(C, _);
|
|
44
|
+
}
|
|
45
|
+
o = ($ = x.find((a) => a.absence)) == null ? void 0 : $.absence;
|
|
46
|
+
const O = x[1];
|
|
47
|
+
if (!o && e.operator === "quotient" && O.value === 0 && (o = { kind: "zero", label: t.quantities.get(O.operand.key).label }), !o) {
|
|
48
|
+
const a = e.operands.map((r) => r.negated === !0);
|
|
49
|
+
h = le(e.operator, x.map((r) => r.value), a);
|
|
50
|
+
const c = le(
|
|
51
|
+
e.operator,
|
|
52
|
+
x.map((r) => Oe(t.quantities.get(r.operand.key), r.shown)),
|
|
53
|
+
a
|
|
54
|
+
);
|
|
55
|
+
w = G(e, c) !== G(e, h);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const d = { target: e.target, limits: e.limits }, g = e.target !== void 0 || (((y = e.limits) == null ? void 0 : y.length) ?? 0) > 0 ? be(h, d) : void 0, b = (((T = e.limits) == null ? void 0 : T.length) ?? 0) > 0 ? g.verdict : h === null ? "unknown" : void 0, l = {
|
|
59
|
+
value: h,
|
|
60
|
+
absence: o,
|
|
61
|
+
shown: h === null ? null : G(e, h),
|
|
62
|
+
approximate: w,
|
|
63
|
+
assessment: g,
|
|
64
|
+
verdict: b,
|
|
65
|
+
worst: C
|
|
66
|
+
};
|
|
67
|
+
return n.set(m, l), l;
|
|
68
|
+
};
|
|
69
|
+
for (const m of t.quantities.keys()) v(m);
|
|
70
|
+
return n;
|
|
71
|
+
}
|
|
72
|
+
const qe = () => null, Te = () => null, ke = () => null, Ce = () => null, Ae = () => null, ue = () => null, Ie = () => null, Me = () => null, Pe = () => null, De = () => null, Ee = () => null, Y = () => null, Be = /* @__PURE__ */ new Map([
|
|
73
|
+
[Te, "sum"],
|
|
74
|
+
[ke, "difference"],
|
|
75
|
+
[Ce, "product"],
|
|
76
|
+
[Ae, "quotient"]
|
|
77
|
+
]), We = {
|
|
78
|
+
sum: "Sum",
|
|
79
|
+
difference: "Difference",
|
|
80
|
+
product: "Product",
|
|
81
|
+
quotient: "Quotient"
|
|
82
|
+
}, Z = /* @__PURE__ */ new Map([
|
|
83
|
+
[Me, { name: "Plus", operator: "sum" }],
|
|
84
|
+
[Pe, { name: "Minus", operator: "sum", negated: !0 }],
|
|
85
|
+
[De, { name: "Times", operator: "product" }],
|
|
86
|
+
[Ee, { name: "DividedBy", operator: "quotient" }]
|
|
87
|
+
]), X = "<Given>, <Sum>, <Difference>, <Product>, <Quotient> or <Chain>";
|
|
88
|
+
function q(t) {
|
|
89
|
+
throw new Error(`@umriss-ui/calculation: ${t}`);
|
|
90
|
+
}
|
|
91
|
+
function Q(t) {
|
|
92
|
+
return ve.toArray(t).flatMap(
|
|
93
|
+
(n) => j(n) && n.type === ge ? Q(n.props.children) : [n]
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
function V(t) {
|
|
97
|
+
if (!j(t)) return JSON.stringify(t);
|
|
98
|
+
const n = t.type;
|
|
99
|
+
return typeof n == "string" ? `<${n}>` : `<${n.displayName ?? n.name ?? "anonymous"}>`;
|
|
100
|
+
}
|
|
101
|
+
const ee = (t, n, v) => `${t}/${j(n) && n.key || v}`;
|
|
102
|
+
function je(t) {
|
|
103
|
+
const n = Q(t);
|
|
104
|
+
n.length !== 1 && q(`<Calculation> takes exactly one child, the result; it was given ${n.length}.`);
|
|
105
|
+
const v = /* @__PURE__ */ new Map(), m = /* @__PURE__ */ new Map(), i = [], e = (d, g, b) => {
|
|
106
|
+
const { id: l, ...$ } = g;
|
|
107
|
+
l !== void 0 && (m.has(l) && q(`${b}: the id "${l}" is used twice.`), m.set(l, d));
|
|
108
|
+
const y = { ...$, key: d, id: l, operands: [] };
|
|
109
|
+
return v.set(d, y), y;
|
|
110
|
+
}, h = (d, g, b) => {
|
|
111
|
+
if (j(d) && d.type === ue) {
|
|
112
|
+
const l = { key: "", reference: !0 };
|
|
113
|
+
return i.push({ operand: l, to: d.props.to, where: b }), l;
|
|
114
|
+
}
|
|
115
|
+
return { key: o(d, g, b), reference: !1 };
|
|
116
|
+
}, o = (d, g, b) => {
|
|
117
|
+
if ((!j(d) || d.type === ue || Z.has(d.type) || d.type === Y) && q(`${b}: ${V(d)} cannot stand here; a quantity must be ${X}.`), d.type === Ie) return w(d.props.children, g, b);
|
|
118
|
+
const l = Be.get(d.type);
|
|
119
|
+
d.type !== qe && l === void 0 && q(
|
|
120
|
+
`${b}: ${V(d)} is not an element of the calculation. A calculation reads its children's props and cannot look inside a component of your own; write ${X} or <Ref> directly, or return them from .map.`
|
|
121
|
+
);
|
|
122
|
+
const { children: $, value: y, source: T, asOf: x, ages: O, ...a } = d.props, c = `${b} › ${a.label}`, r = e(g, a, c);
|
|
123
|
+
if (l === void 0)
|
|
124
|
+
return r.given = { value: y, source: T, asOf: x, ages: O }, g;
|
|
125
|
+
r.operator = l;
|
|
126
|
+
const _ = Q($), s = _.length;
|
|
127
|
+
return (l === "quotient" ? s !== 2 : s < 2) && q(
|
|
128
|
+
`${c}: <${We[l]}> takes ${l === "quotient" ? "exactly two operands" : "two or more operands"}; it was given ${s}.`
|
|
129
|
+
), r.operands = _.map((u, N) => h(u, ee(g, u, N), c)), g;
|
|
130
|
+
}, w = (d, g, b) => {
|
|
131
|
+
const l = `${b} › <Chain>`, [$, ...y] = Q(d);
|
|
132
|
+
($ === void 0 || j($) && (Z.has($.type) || $.type === Y)) && q(`${l}: a chain starts with a quantity that has no operator - ${X} or <Ref>.`);
|
|
133
|
+
let T = h($, ee(g, $, 0), l), x = V($), O = [], a;
|
|
134
|
+
return y.forEach((c, r) => {
|
|
135
|
+
const _ = ee(g, c, r + 1);
|
|
136
|
+
if (j(c) && c.type === Y) {
|
|
137
|
+
const M = c.props, D = `${l} › ${M.label}`;
|
|
138
|
+
O.length === 0 && q(`${D}: an <Interim> needs an operand since the named value before it, ${x}.`);
|
|
139
|
+
const S = e(_, M, D);
|
|
140
|
+
S.interim = !0, S.operator = O[0].line.operator, S.operands = [T, ...O.map((J) => J.operand)], T = { key: _, reference: !1, previous: !0 }, x = `<Interim label="${M.label}">`, O = [], a = _;
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
const s = j(c) ? Z.get(c.type) : void 0;
|
|
144
|
+
s === void 0 && q(`${l}: ${V(c)} needs an operator in a chain - <Plus>, <Minus>, <Times> or <DividedBy> - or is an <Interim>.`);
|
|
145
|
+
const u = s.operator !== "sum", N = O.find((M) => M.line.operator !== "sum");
|
|
146
|
+
(u && O.length > 0 || N) && q(
|
|
147
|
+
`${l}: <${(N ?? { line: s }).line.name}> stands alone between two named values - directly after the first quantity or an <Interim>, and directly before an <Interim>.`
|
|
148
|
+
);
|
|
149
|
+
const { children: B, ...A } = c.props, W = Q(B), I = () => q(`${l}: <${s.name}> takes either a label and a value, or exactly one quantity as its child.`);
|
|
150
|
+
if (W.length > 0) {
|
|
151
|
+
(W.length > 1 || Object.values(A).some((D) => D !== void 0)) && I();
|
|
152
|
+
const M = h(W[0], _, l);
|
|
153
|
+
M.negated = s.negated, O.push({ operand: M, line: s });
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
A.label === void 0 && I();
|
|
157
|
+
const { value: E, source: U, asOf: F, ages: R, ...L } = A, z = e(_, { ...L, label: A.label }, `${l} › ${A.label}`);
|
|
158
|
+
z.given = { value: E, source: U, asOf: F, ages: R }, O.push({ operand: { key: _, reference: !1, negated: s.negated }, line: s });
|
|
159
|
+
}), (O.length > 0 || a === void 0) && q(`${l}: a chain ends with an <Interim>, which names its value.`), a;
|
|
160
|
+
}, C = o(n[0], "0", "<Calculation>");
|
|
161
|
+
for (const { operand: d, to: g, where: b } of i) {
|
|
162
|
+
const l = m.get(g);
|
|
163
|
+
if (l === void 0) {
|
|
164
|
+
const $ = [...m.keys()].map((y) => `"${y}"`).join(", ");
|
|
165
|
+
q(`${b}: <Ref to="${g}"> names no quantity. ${$ ? `The ids that exist: ${$}.` : "No quantity has an id."}`);
|
|
166
|
+
}
|
|
167
|
+
d.key = l;
|
|
168
|
+
}
|
|
169
|
+
return Re(v), { result: C, quantities: v };
|
|
170
|
+
}
|
|
171
|
+
function Re(t) {
|
|
172
|
+
const n = /* @__PURE__ */ new Set(), v = [], m = (i) => {
|
|
173
|
+
if (n.has(i)) return;
|
|
174
|
+
const e = v.indexOf(i);
|
|
175
|
+
if (e !== -1) {
|
|
176
|
+
const h = [...v.slice(e), i].map((o) => t.get(o).label);
|
|
177
|
+
q(`the calculation goes round in a circle through references: ${h.join(" → ")}.`);
|
|
178
|
+
}
|
|
179
|
+
v.push(i);
|
|
180
|
+
for (const h of t.get(i).operands) m(h.key);
|
|
181
|
+
v.pop(), n.add(i);
|
|
182
|
+
};
|
|
183
|
+
for (const i of t.keys()) m(i);
|
|
184
|
+
}
|
|
185
|
+
const de = 4, Fe = {
|
|
186
|
+
sum: "calculationSumSymbol",
|
|
187
|
+
difference: "calculationDifferenceSymbol",
|
|
188
|
+
product: "calculationProductSymbol",
|
|
189
|
+
quotient: "calculationQuotientSymbol"
|
|
190
|
+
}, Qe = {
|
|
191
|
+
sum: "calculationSumWord",
|
|
192
|
+
difference: "calculationDifferenceWord",
|
|
193
|
+
product: "calculationProductWord",
|
|
194
|
+
quotient: "calculationQuotientWord"
|
|
195
|
+
};
|
|
196
|
+
function H(t, n) {
|
|
197
|
+
switch (t) {
|
|
198
|
+
case "alarm":
|
|
199
|
+
return n.verdictAlarm;
|
|
200
|
+
case "warning":
|
|
201
|
+
return n.verdictWarning;
|
|
202
|
+
case "unknown":
|
|
203
|
+
return n.verdictUnknown;
|
|
204
|
+
default:
|
|
205
|
+
return n.verdictOk;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
function te(t, n, v) {
|
|
209
|
+
const m = t.negated ? "difference" : t.operator;
|
|
210
|
+
return v[(n ? Qe : Fe)[m]];
|
|
211
|
+
}
|
|
212
|
+
function Ue(t, n) {
|
|
213
|
+
return t.kind === "missing" ? n.calculationMissing(t.label) : n.calculationDivisionByZero(t.label);
|
|
214
|
+
}
|
|
215
|
+
function pe(t, n, v, m) {
|
|
216
|
+
const i = t.format === "percent" ? m : t.unit, e = v.number(n, t.decimals);
|
|
217
|
+
return i ? `${e} ${i}` : e;
|
|
218
|
+
}
|
|
219
|
+
function Le(t, n, v, m, i, e, h) {
|
|
220
|
+
const o = t.quantities.get(v), w = n.get(v), C = (_, s, u) => s.shown === null ? u ? e.verdictUnknown : e.statAbsentValue : pe(_, s.shown, i, u ? e.calculationPercent : "%"), d = (_) => {
|
|
221
|
+
if (m || !o.operator) return {};
|
|
222
|
+
if (!_ && o.operands.length > de)
|
|
223
|
+
return { names: e.calculationOperandCount(o.operands.length) };
|
|
224
|
+
const s = (u) => o.operands.map((N, B) => {
|
|
225
|
+
const A = t.quantities.get(N.key);
|
|
226
|
+
return B === 0 ? u(A) : `${te({ operator: o.operator, negated: N.negated === !0 }, _, e)} ${u(A)}`;
|
|
227
|
+
}).join(" ");
|
|
228
|
+
return {
|
|
229
|
+
names: s((u) => u.label),
|
|
230
|
+
numbers: s((u) => C(u, n.get(u.key), _))
|
|
231
|
+
};
|
|
232
|
+
}, g = (_) => {
|
|
233
|
+
var N;
|
|
234
|
+
const s = (N = w.assessment) == null ? void 0 : N.deviation;
|
|
235
|
+
if (s === void 0 || o.target === void 0) return;
|
|
236
|
+
const u = pe(o, G(o, o.target), i, _ ? e.calculationPercent : "%");
|
|
237
|
+
return s > 0 ? e.calculationAboveTarget(u) : s < 0 ? e.calculationBelowTarget(u) : e.calculationOnTarget(u);
|
|
238
|
+
}, b = d(!1).names, l = b === void 0 || o.operands.length > de ? b : `= ${b}`, $ = d(!0), y = w.absence ? Ue(w.absence, e) : void 0, T = ` ${e.calculationEquals} `, x = w.approximate ? `${e.calculationApproximately} ${C(o, w, !0)}` : C(o, w, !0), a = [
|
|
239
|
+
(h ? `${te(h, !0, e)} ` : "") + o.label + T + [$.names, $.numbers, x].filter(Boolean).join(`,${T}`),
|
|
240
|
+
w.verdict !== void 0 && w.verdict !== "unknown" ? H(w.verdict, e) : void 0,
|
|
241
|
+
g(!0),
|
|
242
|
+
y
|
|
243
|
+
].filter(Boolean).join(", "), c = w.shown === null ? void 0 : o.format === "percent" ? "%" : o.unit;
|
|
244
|
+
return { amount: w.shown === null ? e.statAbsentValue : i.number(w.shown, o.decimals), unit: c, names: l, target: g(!1), reason: y, sentence: a };
|
|
245
|
+
}
|
|
246
|
+
const Ve = "_frame_1hhx1_21 _text_1hv7a_23", Ge = "_calculation_1hhx1_39", He = "_disclosure_1hhx1_53", Ke = "_list_1hhx1_57", ze = "_item_1hhx1_58", Je = "_derivation_1hhx1_59", Ye = "_line_1hhx1_109", Ze = "_labelCell_1hhx1_151", Xe = "_names_1hhx1_152", et = "_operator_1hhx1_153", tt = "_amount_1hhx1_154", nt = "_unit_1hhx1_155", at = "_assessment_1hhx1_156", st = "_label_1hhx1_151", ot = "_approximate_1hhx1_269", rt = "_target_1hhx1_297", it = "_worst_1hhx1_298", lt = "_notes_1hhx1_390", ct = "_freshness_1hhx1_404", p = {
|
|
247
|
+
frame: Ve,
|
|
248
|
+
calculation: Ge,
|
|
249
|
+
disclosure: He,
|
|
250
|
+
list: Ke,
|
|
251
|
+
item: ze,
|
|
252
|
+
derivation: Je,
|
|
253
|
+
line: Ye,
|
|
254
|
+
labelCell: Ze,
|
|
255
|
+
names: Xe,
|
|
256
|
+
operator: et,
|
|
257
|
+
amount: tt,
|
|
258
|
+
unit: nt,
|
|
259
|
+
assessment: at,
|
|
260
|
+
label: st,
|
|
261
|
+
approximate: ot,
|
|
262
|
+
target: rt,
|
|
263
|
+
worst: it,
|
|
264
|
+
notes: lt,
|
|
265
|
+
freshness: ct
|
|
266
|
+
}, ut = { ok: "success", unknown: "neutral", warning: "warning", alarm: "danger" }, dt = (...t) => t.filter(Boolean).join(" ");
|
|
267
|
+
function pt({ asOf: t, ages: n }) {
|
|
268
|
+
const v = me(), m = fe(), i = Ne(t, n), e = i.freshness === "lost" ? m.freshnessDisconnected : i.freshness === "stale" ? m.freshnessStale : m.freshnessFresh;
|
|
269
|
+
return /* @__PURE__ */ k("span", { className: p.freshness, "data-freshness": i.freshness, children: [
|
|
270
|
+
e,
|
|
271
|
+
i.age !== null && ` · ${v.relative(i.age)}`
|
|
272
|
+
] });
|
|
273
|
+
}
|
|
274
|
+
function vt({ children: t, className: n, style: v, ...m }) {
|
|
275
|
+
const i = me(), e = fe(), h = _e(), o = je(t), w = Se(o), [C, d] = oe(() => /* @__PURE__ */ new Set()), [g, b] = oe(null), l = (a) => d((c) => {
|
|
276
|
+
const r = new Set(c);
|
|
277
|
+
return r.delete(a) || r.add(a), r;
|
|
278
|
+
}), $ = (a, c, r) => r === 0 || c.previous ? void 0 : { operator: a.operator, negated: c.negated === !0 };
|
|
279
|
+
let y = !1;
|
|
280
|
+
const T = (a, c) => g === null ? void 0 : a === g ? "use" : c === g ? "operand" : void 0, x = (a, c, r, _) => {
|
|
281
|
+
const s = o.quantities.get(a), u = w.get(a), N = Le(o, w, a, c, i, e, r.position), B = s.operator !== void 0 && !c, A = a === o.result && !c, W = B && (s.interim ? r.flat : A), I = B && !W, E = I && C.has(a);
|
|
282
|
+
y || (y = I);
|
|
283
|
+
const U = `${h}-${a}`, F = { depth: W ? r.depth : r.depth + 1, flat: !1, parent: a }, R = B ? s.operands.find((P) => P.previous) : void 0, L = R && r.flat ? x(R.key, !1, { ...r, parent: a, position: void 0 }, `${_}-c`) : [], z = R && !r.flat ? x(R.key, !1, { ...F, flat: !0 }, `${_}-c`) : [], M = B ? s.operands.map(
|
|
284
|
+
(P, se) => P.previous ? null : x(P.key, P.reference, { ...F, position: $(s, P, se) }, `${_}-${se}`)
|
|
285
|
+
) : [], D = I && !E && u.worst !== void 0 && K(u.worst) > K(u.verdict ?? "ok") ? u.worst : void 0, S = c ? void 0 : s.given, J = r.position ? te(r.position, !1, e) : void 0, ne = [
|
|
286
|
+
!c && s.explanation !== void 0 && /* @__PURE__ */ f("span", { children: s.explanation }, "explanation"),
|
|
287
|
+
(S == null ? void 0 : S.source) !== void 0 && /* @__PURE__ */ f("span", { children: e.calculationSource(S.source) }, "source"),
|
|
288
|
+
(S == null ? void 0 : S.asOf) !== void 0 && (S.ages !== void 0 ? /* @__PURE__ */ f(pt, { asOf: S.asOf, ages: S.ages }, "asOf") : /* @__PURE__ */ f("span", { children: e.asOfAgo(i.dateTime(new Date(S.asOf), !1)) }, "asOf")),
|
|
289
|
+
!c && s.aside !== void 0 && /* @__PURE__ */ f("span", { children: s.aside }, "aside")
|
|
290
|
+
].filter(Boolean), he = A ? "result" : s.interim && !c && r.flat ? "interim" : void 0, ae = /* @__PURE__ */ k(
|
|
291
|
+
"div",
|
|
292
|
+
{
|
|
293
|
+
className: p.line,
|
|
294
|
+
style: { "--depth": r.depth },
|
|
295
|
+
"data-inner": r.depth > 0 ? "" : void 0,
|
|
296
|
+
"data-kind": he,
|
|
297
|
+
"data-foldable": I ? "" : void 0,
|
|
298
|
+
"data-reference": c ? "" : void 0,
|
|
299
|
+
"data-mark": T(a, r.parent),
|
|
300
|
+
onPointerEnter: () => b(a),
|
|
301
|
+
onPointerLeave: () => b(null),
|
|
302
|
+
onFocus: () => b(a),
|
|
303
|
+
onBlur: () => b(null),
|
|
304
|
+
onClick: I ? () => {
|
|
305
|
+
var P;
|
|
306
|
+
(P = window.getSelection()) != null && P.toString() || l(a);
|
|
307
|
+
} : void 0,
|
|
308
|
+
children: [
|
|
309
|
+
/* @__PURE__ */ f($e, { children: [
|
|
310
|
+
N.sentence,
|
|
311
|
+
u.approximate && e.calculationApproximateNote,
|
|
312
|
+
D !== void 0 && e.calculationWorstInside(H(D, e))
|
|
313
|
+
].filter(Boolean).join(". ") }),
|
|
314
|
+
/* @__PURE__ */ k("span", { className: p.labelCell, children: [
|
|
315
|
+
I ? /* @__PURE__ */ f(
|
|
316
|
+
"button",
|
|
317
|
+
{
|
|
318
|
+
type: "button",
|
|
319
|
+
className: p.disclosure,
|
|
320
|
+
"aria-expanded": E,
|
|
321
|
+
"aria-controls": U,
|
|
322
|
+
"aria-label": (E ? e.calculationHideDerivation : e.calculationShowDerivation)(s.label),
|
|
323
|
+
children: /* @__PURE__ */ f(xe, { "data-open": E ? "" : void 0 })
|
|
324
|
+
}
|
|
325
|
+
) : /* @__PURE__ */ f("span", { className: p.disclosure, "aria-hidden": "true" }),
|
|
326
|
+
/* @__PURE__ */ f("span", { className: p.label, "aria-hidden": "true", children: s.label })
|
|
327
|
+
] }),
|
|
328
|
+
/* @__PURE__ */ f("span", { className: p.names, "aria-hidden": "true", children: I && !E ? N.names : void 0 }),
|
|
329
|
+
/* @__PURE__ */ f("span", { className: p.operator, "aria-hidden": "true", children: J }),
|
|
330
|
+
/* @__PURE__ */ k("span", { className: p.amount, "aria-hidden": "true", "data-verdict": u.verdict, children: [
|
|
331
|
+
u.approximate && /* @__PURE__ */ f(re, { content: e.calculationApproximateNote, children: /* @__PURE__ */ f("span", { className: p.approximate, children: "≈" }) }),
|
|
332
|
+
N.amount
|
|
333
|
+
] }),
|
|
334
|
+
/* @__PURE__ */ f("span", { className: p.unit, "aria-hidden": "true", children: N.unit }),
|
|
335
|
+
/* @__PURE__ */ k("span", { className: p.assessment, "aria-hidden": "true", children: [
|
|
336
|
+
N.reason !== void 0 ? /* @__PURE__ */ f(ie, { children: N.reason }) : u.verdict !== void 0 && /* @__PURE__ */ f(ie, { tone: ut[u.verdict], children: H(u.verdict, e) }),
|
|
337
|
+
N.target !== void 0 && /* @__PURE__ */ f("span", { className: p.target, children: N.target }),
|
|
338
|
+
D !== void 0 && /* @__PURE__ */ f("span", { className: p.worst, "data-verdict": D, children: e.calculationWorstInside(H(D, e)) })
|
|
339
|
+
] }),
|
|
340
|
+
ne.length > 0 && /* A click here - on a link in the aside, on a sparkline - is the
|
|
341
|
+
caller's, not a fold. */
|
|
342
|
+
/* @__PURE__ */ f("span", { className: p.notes, onClick: (P) => P.stopPropagation(), children: ne })
|
|
343
|
+
]
|
|
344
|
+
}
|
|
345
|
+
);
|
|
346
|
+
return W ? [
|
|
347
|
+
...L,
|
|
348
|
+
/* @__PURE__ */ k("li", { className: p.item, children: [
|
|
349
|
+
/* @__PURE__ */ f("ul", { className: p.list, children: M }),
|
|
350
|
+
ae
|
|
351
|
+
] }, _)
|
|
352
|
+
] : [
|
|
353
|
+
...L,
|
|
354
|
+
/* @__PURE__ */ k("li", { className: p.item, "data-open": E ? "" : void 0, style: { "--depth": r.depth }, children: [
|
|
355
|
+
ae,
|
|
356
|
+
I && /* @__PURE__ */ k("ul", { id: U, className: p.derivation, hidden: !E, children: [
|
|
357
|
+
z,
|
|
358
|
+
M,
|
|
359
|
+
/* @__PURE__ */ f("li", { className: p.item, "aria-hidden": "true", children: /* @__PURE__ */ k(
|
|
360
|
+
"div",
|
|
361
|
+
{
|
|
362
|
+
className: p.line,
|
|
363
|
+
style: { "--depth": F.depth },
|
|
364
|
+
"data-inner": "",
|
|
365
|
+
"data-closing": "",
|
|
366
|
+
"data-mark": g === a ? "use" : void 0,
|
|
367
|
+
children: [
|
|
368
|
+
/* @__PURE__ */ k("span", { className: p.labelCell, children: [
|
|
369
|
+
/* @__PURE__ */ f("span", { className: p.disclosure }),
|
|
370
|
+
/* @__PURE__ */ k("span", { className: p.label, children: [
|
|
371
|
+
"= ",
|
|
372
|
+
s.label
|
|
373
|
+
] })
|
|
374
|
+
] }),
|
|
375
|
+
/* @__PURE__ */ f("span", { className: p.names }),
|
|
376
|
+
/* @__PURE__ */ f("span", { className: p.operator }),
|
|
377
|
+
/* @__PURE__ */ k("span", { className: p.amount, "data-verdict": u.verdict, children: [
|
|
378
|
+
u.approximate && /* @__PURE__ */ f(re, { content: e.calculationApproximateNote, children: /* @__PURE__ */ f("span", { className: p.approximate, children: "≈" }) }),
|
|
379
|
+
N.amount
|
|
380
|
+
] }),
|
|
381
|
+
/* @__PURE__ */ f("span", { className: p.unit, children: N.unit })
|
|
382
|
+
]
|
|
383
|
+
}
|
|
384
|
+
) })
|
|
385
|
+
] })
|
|
386
|
+
] }, _)
|
|
387
|
+
];
|
|
388
|
+
}, O = x(o.result, !1, { depth: 0, flat: !0, parent: null }, "r");
|
|
389
|
+
return (
|
|
390
|
+
/* The frame is the surface and the container the layout measures itself
|
|
391
|
+
against; the list inside it is the statement. */
|
|
392
|
+
/* @__PURE__ */ f("div", { className: dt(p.frame, n), style: v, "data-still": y ? void 0 : "", children: /* @__PURE__ */ f("ul", { className: p.calculation, ...m, children: O }) })
|
|
393
|
+
);
|
|
394
|
+
}
|
|
395
|
+
export {
|
|
396
|
+
vt as Calculation,
|
|
397
|
+
Ie as Chain,
|
|
398
|
+
ke as Difference,
|
|
399
|
+
Ee as DividedBy,
|
|
400
|
+
qe as Given,
|
|
401
|
+
Y as Interim,
|
|
402
|
+
Pe as Minus,
|
|
403
|
+
Me as Plus,
|
|
404
|
+
Ce as Product,
|
|
405
|
+
Ae as Quotient,
|
|
406
|
+
ue as Ref,
|
|
407
|
+
Te as Sum,
|
|
408
|
+
De as Times
|
|
409
|
+
};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { FreshnessAges } from '@umriss-ui/core';
|
|
2
|
+
import { HTMLAttributes } from 'react';
|
|
3
|
+
import { JSX } from 'react';
|
|
4
|
+
import { Limit } from '@umriss-ui/core';
|
|
5
|
+
import { ReactNode } from 'react';
|
|
6
|
+
|
|
7
|
+
export declare function Calculation({ children, className, style, ...rest }: CalculationProps): JSX.Element;
|
|
8
|
+
|
|
9
|
+
/** `className` and `style` size and place the surface; every other attribute -
|
|
10
|
+
`aria-label` first of all - lands on the list the surface holds. */
|
|
11
|
+
export declare interface CalculationProps extends Omit<HTMLAttributes<HTMLUListElement>, "children"> {
|
|
12
|
+
/** The result: exactly one quantity - a tree (`<Sum>`, `<Difference>`,
|
|
13
|
+
`<Product>`, `<Quotient>` with their operands, `<Given>` as leaves) or a
|
|
14
|
+
`<Chain>`, the two mixing freely; `<Ref>` for a quantity defined
|
|
15
|
+
elsewhere. */
|
|
16
|
+
children: ReactNode;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** A calculation read top to bottom, as on paper. */
|
|
20
|
+
export declare const Chain: (props: ChainProps) => null;
|
|
21
|
+
|
|
22
|
+
export declare interface ChainOperandProps extends Omit<GivenProps, "label" | "value"> {
|
|
23
|
+
/** The operand's name, where the line is a given written in place. Left
|
|
24
|
+
out where the line holds a quantity as its child. */
|
|
25
|
+
label?: string;
|
|
26
|
+
/** The operand's number, where the line is a given written in place.
|
|
27
|
+
Absent as for a `Given`: the line and every interim after it are
|
|
28
|
+
absent, with the reason. */
|
|
29
|
+
value?: number | null;
|
|
30
|
+
/** Instead of `label` and `value`: exactly one quantity - a tree, a
|
|
31
|
+
`<Ref>` or a `<Chain>`. */
|
|
32
|
+
children?: ReactNode;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export declare interface ChainProps {
|
|
36
|
+
/** The operands, top to bottom: a first quantity with no operator, then
|
|
37
|
+
`Plus`, `Minus`, `Times` and `DividedBy` with their operands and
|
|
38
|
+
`Interim`s naming the value where they stand. The last line is an
|
|
39
|
+
`Interim`; a `Times` or `DividedBy` stands alone between two named
|
|
40
|
+
values. */
|
|
41
|
+
children?: ReactNode;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** The first operand minus every other: a − b − c. */
|
|
45
|
+
export declare const Difference: (props: OperatorProps) => null;
|
|
46
|
+
|
|
47
|
+
/** Divides the named value before it by its operand. */
|
|
48
|
+
export declare const DividedBy: (props: ChainOperandProps) => null;
|
|
49
|
+
|
|
50
|
+
/** A number the calculation does not derive. */
|
|
51
|
+
export declare const Given: (props: GivenProps) => null;
|
|
52
|
+
|
|
53
|
+
export declare interface GivenProps extends QuantityProps {
|
|
54
|
+
/** The number the calculation starts from. Absent (`null`, `undefined`,
|
|
55
|
+
not finite) makes every quantity that depends on it absent, with the
|
|
56
|
+
reason - never zero. */
|
|
57
|
+
value: number | null | undefined;
|
|
58
|
+
/** Where the number came from: a system, a report, a person. */
|
|
59
|
+
source?: string;
|
|
60
|
+
/** When the number was true - not when it was fetched. */
|
|
61
|
+
asOf?: Date | number;
|
|
62
|
+
/** The ages at which `asOf` turns stale and lost. With both, the given
|
|
63
|
+
carries a freshness through core; with `asOf` alone, only the time. */
|
|
64
|
+
ages?: FreshnessAges;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Names the value of a chain where it stands. */
|
|
68
|
+
export declare const Interim: (props: QuantityProps) => null;
|
|
69
|
+
|
|
70
|
+
/** Takes its operand from the value before it. */
|
|
71
|
+
export declare const Minus: (props: ChainOperandProps) => null;
|
|
72
|
+
|
|
73
|
+
export declare interface OperatorProps extends QuantityProps {
|
|
74
|
+
/** The operands, in order: quantities, givens and references. */
|
|
75
|
+
children?: ReactNode;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Adds its operand to the value before it. */
|
|
79
|
+
export declare const Plus: (props: ChainOperandProps) => null;
|
|
80
|
+
|
|
81
|
+
/** The product of two or more operands. */
|
|
82
|
+
export declare const Product: (props: OperatorProps) => null;
|
|
83
|
+
|
|
84
|
+
/** What every quantity - given or derived - can say about itself. */
|
|
85
|
+
export declare interface QuantityProps {
|
|
86
|
+
/** What the quantity is called. It stands on its line, wherever it is an
|
|
87
|
+
operand, and in the accessible sentence. */
|
|
88
|
+
label: string;
|
|
89
|
+
/** Makes the quantity referable: `<Ref to>` stands for it elsewhere. Needed
|
|
90
|
+
only for a quantity used more than once. */
|
|
91
|
+
id?: string;
|
|
92
|
+
/** A label written after the number - "min", "pcs", "€". Never converted
|
|
93
|
+
and never checked; the calculation does no unit algebra. */
|
|
94
|
+
unit?: string;
|
|
95
|
+
/** `percent` presents a plain ratio as per cent: 0.916 is shown as 91.6 %.
|
|
96
|
+
The quantity stays a ratio in every operation. */
|
|
97
|
+
format?: "percent";
|
|
98
|
+
/** Fixed number of fraction digits as shown. Rounding happens only in
|
|
99
|
+
presentation; every operation uses the full value. Without it: one digit
|
|
100
|
+
for a percentage, at most two for a derived number, and a given as it
|
|
101
|
+
was given. */
|
|
102
|
+
decimals?: number;
|
|
103
|
+
/** The value this quantity is meant to reach - missed, never violated. */
|
|
104
|
+
target?: number;
|
|
105
|
+
/** Bounds the quantity is assessed against, through core's `assess()`. */
|
|
106
|
+
limits?: readonly Limit[];
|
|
107
|
+
/** A sentence of plain text beside the line: why the quantity is here, or
|
|
108
|
+
what it means. */
|
|
109
|
+
explanation?: string;
|
|
110
|
+
/** Content of the caller's own beside the line - a sparkline, a link. It
|
|
111
|
+
never takes part in the calculation. */
|
|
112
|
+
aside?: ReactNode;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Exactly two operands: the first divided by the second. */
|
|
116
|
+
export declare const Quotient: (props: OperatorProps) => null;
|
|
117
|
+
|
|
118
|
+
/** A quantity defined elsewhere in the calculation, standing as an operand. */
|
|
119
|
+
export declare const Ref: (props: RefProps) => null;
|
|
120
|
+
|
|
121
|
+
export declare interface RefProps {
|
|
122
|
+
/** The `id` of the quantity this reference stands for. */
|
|
123
|
+
to: string;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** The sum of two or more operands. */
|
|
127
|
+
export declare const Sum: (props: OperatorProps) => null;
|
|
128
|
+
|
|
129
|
+
/** Multiplies the named value before it by its operand. */
|
|
130
|
+
export declare const Times: (props: ChainOperandProps) => null;
|
|
131
|
+
|
|
132
|
+
export { }
|
package/package.json
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@umriss-ui/calculation",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "The umriss calculation - a derivation written as it is shown, evaluated by the library, folded and read line by line.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Roman Händler",
|
|
7
|
+
"homepage": "https://romanhaendler.github.io/umriss-ui/calculation/",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/romanhaendler/umriss-ui.git",
|
|
11
|
+
"directory": "packages/calculation"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/romanhaendler/umriss-ui/issues"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"react",
|
|
18
|
+
"components",
|
|
19
|
+
"ui",
|
|
20
|
+
"umriss",
|
|
21
|
+
"calculation",
|
|
22
|
+
"derivation"
|
|
23
|
+
],
|
|
24
|
+
"type": "module",
|
|
25
|
+
"files": [
|
|
26
|
+
"dist",
|
|
27
|
+
"CHANGELOG.md"
|
|
28
|
+
],
|
|
29
|
+
"publishConfig": {
|
|
30
|
+
"access": "public"
|
|
31
|
+
},
|
|
32
|
+
"sideEffects": [
|
|
33
|
+
"**/*.css"
|
|
34
|
+
],
|
|
35
|
+
"main": "./dist/calculation.js",
|
|
36
|
+
"types": "./dist/index.d.ts",
|
|
37
|
+
"exports": {
|
|
38
|
+
".": {
|
|
39
|
+
"types": "./dist/index.d.ts",
|
|
40
|
+
"import": "./dist/calculation.js"
|
|
41
|
+
},
|
|
42
|
+
"./styles.css": "./dist/calculation.css"
|
|
43
|
+
},
|
|
44
|
+
"peerDependencies": {
|
|
45
|
+
"react": ">=18",
|
|
46
|
+
"react-dom": ">=18",
|
|
47
|
+
"@umriss-ui/core": "^0.5.0"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@fontsource/geist-mono": "^5.3.0",
|
|
51
|
+
"@fontsource/geist-sans": "^5.3.0",
|
|
52
|
+
"@types/node": "^22.20.2",
|
|
53
|
+
"@types/react": "^19.0.0",
|
|
54
|
+
"@types/react-dom": "^19.0.0",
|
|
55
|
+
"@vitejs/plugin-react": "^4.3.4",
|
|
56
|
+
"react": "^19.0.0",
|
|
57
|
+
"react-dom": "^19.0.0",
|
|
58
|
+
"typescript": "^5.6.3",
|
|
59
|
+
"vite": "^6.0.0",
|
|
60
|
+
"vite-plugin-dts": "^4.3.0",
|
|
61
|
+
"@umriss-ui/core": "0.5.0",
|
|
62
|
+
"@umriss-ui/demo": "0.0.0"
|
|
63
|
+
},
|
|
64
|
+
"scripts": {
|
|
65
|
+
"props": "node --disable-warning=ExperimentalWarning --experimental-strip-types demo/props.ts",
|
|
66
|
+
"predev": "pnpm run props",
|
|
67
|
+
"dev": "vite --config vite.demo.config.ts",
|
|
68
|
+
"build": "vite build",
|
|
69
|
+
"prebuild:demo": "pnpm run props",
|
|
70
|
+
"build:demo": "vite build --config vite.demo.config.ts",
|
|
71
|
+
"pretypecheck": "pnpm run props",
|
|
72
|
+
"typecheck": "tsc --noEmit",
|
|
73
|
+
"preview:demo": "vite preview --config vite.demo.config.ts --port 4177 --strictPort",
|
|
74
|
+
"test:unit": "vitest run",
|
|
75
|
+
"check:dist": "node --disable-warning=ExperimentalWarning --experimental-strip-types ../../scripts/check-dist.ts calculation.js calculation.css"
|
|
76
|
+
}
|
|
77
|
+
}
|