@lotics/ui 44.9.0 → 44.9.1
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/MIGRATION.md +27 -0
- package/docs/catalog.md +8 -3
- package/package.json +1 -1
- package/src/ledger.tsx +8 -1
- package/src/matrix.tsx +17 -12
- package/src/table_fit.ts +15 -5
package/MIGRATION.md
CHANGED
|
@@ -4,6 +4,33 @@ Breaking changes, newest first — normally per major, plus the rare minor that
|
|
|
4
4
|
anyway (recorded under its exact version). The current contract lives in `AGENTS.md` + `docs/`;
|
|
5
5
|
this file exists only to move an app from one release to the next.
|
|
6
6
|
|
|
7
|
+
## 44.9.1 — a pivot's figures align, and a ledger row stops losing its own name
|
|
8
|
+
|
|
9
|
+
No API is removed and nothing fails to compile. Three renders change.
|
|
10
|
+
|
|
11
|
+
**`Matrix` right-aligns its figures instead of centring them.** Every value, column
|
|
12
|
+
header and total was `align="center"` while the cells also set `tabular` — and tabular
|
|
13
|
+
figures exist so digits line up in a column, which centring throws away. Measured on a
|
|
14
|
+
real pivot: centred, 24 figures painted across **14 distinct right edges**; right-aligned,
|
|
15
|
+
**12** — exactly one per column. No `align` prop came with this: a matrix is one measure
|
|
16
|
+
across two dimensions, so its cells cannot want different alignments, and a prop for a
|
|
17
|
+
centred case nobody has is surface without a consumer.
|
|
18
|
+
|
|
19
|
+
**`Matrix` row labels carry default ink, not `muted`.** A pivot whose rows are entities
|
|
20
|
+
rendered every entity name a step quieter than the numbers about it, inverting how the
|
|
21
|
+
grid is read — you find the row by its name, then scan across.
|
|
22
|
+
|
|
23
|
+
**`MATRIX_COL_GAP` is exported.** It was the literal `3` in four places, so a caller
|
|
24
|
+
sizing its own column budget had to hard-code it and could drift from what the grid draws.
|
|
25
|
+
|
|
26
|
+
**A `LedgerRow`'s `meta` yields before its label, and by 3×.** Only the label carried a
|
|
27
|
+
`flexShrink`, and RN-web resolves the unstyled caption to `1` as well — so under pressure
|
|
28
|
+
both shrank proportionally, and because a caption is usually the longer string, the
|
|
29
|
+
SUBORDINATE text ended up wider than the row's identity. Measured in a 180px row: before,
|
|
30
|
+
the label kept 31px of the 77 it needs while the caption kept 41 of 103; after, the label
|
|
31
|
+
keeps 55 and the caption 16. Any caller that dropped `meta` at small widths to dodge this
|
|
32
|
+
can stop.
|
|
33
|
+
|
|
7
34
|
## 44.8.0 — a value column gets its own floor, and eight defaults stop fighting their own docs
|
|
8
35
|
|
|
9
36
|
No API is removed. Three of these change what an existing screen renders, so read them even
|
package/docs/catalog.md
CHANGED
|
@@ -1407,8 +1407,9 @@ source (`src/<module>.tsx`/`.ts`) is the API reference.
|
|
|
1407
1407
|
- **`table`** — `Table` + `TableRow` + `TableCell`: the paginated high-volume register
|
|
1408
1408
|
(columns defined once, `sortLabels` localizable; rows are `PressableRow`-based).
|
|
1409
1409
|
CONTAINER-RESPONSIVE with no prop (measures itself, like `Breakdown`/`DetailTable`): when
|
|
1410
|
-
the width can't fit every column it hides columns by `TableColumn.priority` (
|
|
1411
|
-
|
|
1410
|
+
the width can't fit every column it hides columns by `TableColumn.priority` (**`1` = highest,
|
|
1411
|
+
as in P1/P2/P3** — the smallest number is the last to go; default = column order, rightmost
|
|
1412
|
+
first; column 0 — the identity — never drops), and
|
|
1412
1413
|
below the two-column floor every row STACKS: the identity cell (column 0) HEADS the pile
|
|
1413
1414
|
unlabelled — its column name would only restate the value — and each REMAINING cell renders
|
|
1414
1415
|
as a `DetailRow`-spread line (muted label left, value at the right edge — one vocabulary with
|
|
@@ -1705,7 +1706,11 @@ component rather than showing it at zero.
|
|
|
1705
1706
|
- **`matrix`** — `Matrix`: the PIVOT cross-tab — band-compound `Matrix` root +
|
|
1706
1707
|
`Matrix.Header` (corner + axis labels) + `Matrix.Grid` (`display` number|heat|both — the
|
|
1707
1708
|
cells, pressable, the value IN the cell) + `Matrix.Totals` (row + column + grand) +
|
|
1708
|
-
`Matrix.Legend`. Pick over `Heatmap` when the NUMBER and totals matter.
|
|
1709
|
+
`Matrix.Legend`. Pick over `Heatmap` when the NUMBER and totals matter. Figures are
|
|
1710
|
+
RIGHT-aligned — they are `tabular`, and centring them defeats the aligned column that is
|
|
1711
|
+
the reason to read a pivot at all — and row labels carry default ink, because a row's
|
|
1712
|
+
label is its identity, not chrome. `MATRIX_COL_GAP` is exported for a caller budgeting
|
|
1713
|
+
its own column widths.
|
|
1709
1714
|
- **`matrix_totals`** — the data layer for `Matrix`: `matrixTotals` (the cross-tab
|
|
1710
1715
|
aggregation) + `MatrixAxisItem` / `MatrixCellRef` / `MatrixTotalsResult`; React-free, so a
|
|
1711
1716
|
KPI can be driven off the same numbers the grid shows.
|
package/package.json
CHANGED
package/src/ledger.tsx
CHANGED
|
@@ -237,7 +237,7 @@ export function LedgerRow(props: LedgerRowProps) {
|
|
|
237
237
|
{label}
|
|
238
238
|
</Text>
|
|
239
239
|
{meta ? (
|
|
240
|
-
<Text size="xs" color="muted" numberOfLines={1}>
|
|
240
|
+
<Text size="xs" color="muted" numberOfLines={1} style={styles.yield}>
|
|
241
241
|
{meta}
|
|
242
242
|
</Text>
|
|
243
243
|
) : null}
|
|
@@ -357,6 +357,13 @@ const styles = StyleSheet.create({
|
|
|
357
357
|
rowHovered: { backgroundColor: colors.zinc[50] },
|
|
358
358
|
grow: { flexGrow: 1, flexShrink: 1 },
|
|
359
359
|
shrink: { flexShrink: 1 },
|
|
360
|
+
// The META yields BEFORE the label, and by more. Only the label carried a
|
|
361
|
+
// shrink, so under width pressure the row'''s IDENTITY was the one thing that
|
|
362
|
+
// gave way while its subordinate qualifier held full width — a narrow ledger
|
|
363
|
+
// read "Tổng thu (chư…" beside an intact "03/06, Cash". A caption qualifies a
|
|
364
|
+
// number; the label names it, and the name is what a reader needs when the
|
|
365
|
+
// row is too tight for both.
|
|
366
|
+
yield: { flexShrink: 3 },
|
|
360
367
|
total: { gap: 6 },
|
|
361
368
|
// The BASIS row, the only one whose two sides can differ in height: its label may
|
|
362
369
|
// carry a stacked `meta` while the figure stays one line. Centring then measures the
|
package/src/matrix.tsx
CHANGED
|
@@ -25,6 +25,11 @@ interface MatrixContextValue {
|
|
|
25
25
|
totalColWidth: number;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
/** Gap between a matrix's columns. Exported because a caller sizing its own
|
|
29
|
+
* column budget needs the same number the grid renders with — hard-coding it
|
|
30
|
+
* is how a width budget and the layout drift apart. */
|
|
31
|
+
export const MATRIX_COL_GAP = 3;
|
|
32
|
+
|
|
28
33
|
const MatrixContext = createContext<MatrixContextValue | null>(null);
|
|
29
34
|
|
|
30
35
|
function useMatrix(): MatrixContextValue {
|
|
@@ -138,12 +143,12 @@ function MatrixHeader({ corner, totalLabel }: MatrixHeaderProps) {
|
|
|
138
143
|
)}
|
|
139
144
|
</View>
|
|
140
145
|
{cols.map((c) => (
|
|
141
|
-
<Text key={c.key} size="xs" color="muted" align="
|
|
146
|
+
<Text key={c.key} size="xs" color="muted" align="right" numberOfLines={1} style={styles.colCell}>
|
|
142
147
|
{c.label}
|
|
143
148
|
</Text>
|
|
144
149
|
))}
|
|
145
150
|
{hasTotals ? (
|
|
146
|
-
<Text size="xs" color="muted" weight="medium" align="
|
|
151
|
+
<Text size="xs" color="muted" weight="medium" align="right" numberOfLines={1} style={[styles.totalCol, { width: totalColWidth }]}>
|
|
147
152
|
{resolvedTotalLabel}
|
|
148
153
|
</Text>
|
|
149
154
|
) : null}
|
|
@@ -163,14 +168,14 @@ function MatrixGrid({ display = "both" }: MatrixGridProps) {
|
|
|
163
168
|
<View style={styles.grid}>
|
|
164
169
|
{rows.map((r) => (
|
|
165
170
|
<View key={r.key} style={styles.row}>
|
|
166
|
-
<Text size="sm"
|
|
171
|
+
<Text size="sm" numberOfLines={1} style={[styles.rowLabel, { width: rowLabelWidth }]}>
|
|
167
172
|
{r.label}
|
|
168
173
|
</Text>
|
|
169
174
|
{cols.map((c) => (
|
|
170
175
|
<MatrixCell key={c.key} ctx={ctx} display={display} row={r} col={c} value={value(r.key, c.key)} />
|
|
171
176
|
))}
|
|
172
177
|
{hasTotals ? (
|
|
173
|
-
<Text size="sm" weight="semibold" tabular align="
|
|
178
|
+
<Text size="sm" weight="semibold" tabular align="right" numberOfLines={1} style={[styles.totalCol, { width: totalColWidth }]}>
|
|
174
179
|
{formatValue(rowTotals.get(r.key) ?? 0)}
|
|
175
180
|
</Text>
|
|
176
181
|
) : null}
|
|
@@ -206,7 +211,7 @@ function MatrixCell({ ctx, display, row, col, value }: MatrixCellProps) {
|
|
|
206
211
|
const label = `${row.label}, ${col.label}: ${formatValue(value)}`;
|
|
207
212
|
|
|
208
213
|
const content = showNumber ? (
|
|
209
|
-
<Text size="sm" weight="medium" tabular align="
|
|
214
|
+
<Text size="sm" weight="medium" tabular align="right" color={value === 0 ? "zinc-500" : "default"}>
|
|
210
215
|
{value === 0 ? "" : formatValue(value)}
|
|
211
216
|
</Text>
|
|
212
217
|
) : null;
|
|
@@ -254,11 +259,11 @@ function MatrixTotals({ label }: MatrixTotalsProps) {
|
|
|
254
259
|
{label ?? locale.matrix.total}
|
|
255
260
|
</Text>
|
|
256
261
|
{cols.map((c) => (
|
|
257
|
-
<Text key={c.key} size="sm" weight="semibold" tabular align="
|
|
262
|
+
<Text key={c.key} size="sm" weight="semibold" tabular align="right" numberOfLines={1} style={styles.colCell}>
|
|
258
263
|
{formatValue(colTotals.get(c.key) ?? 0)}
|
|
259
264
|
</Text>
|
|
260
265
|
))}
|
|
261
|
-
<Text size="sm" weight="semibold" tabular align="
|
|
266
|
+
<Text size="sm" weight="semibold" tabular align="right" numberOfLines={1} style={[styles.totalCol, { width: totalColWidth }]}>
|
|
262
267
|
{formatValue(grandTotal)}
|
|
263
268
|
</Text>
|
|
264
269
|
</View>
|
|
@@ -295,21 +300,21 @@ Matrix.Legend = MatrixLegend;
|
|
|
295
300
|
|
|
296
301
|
const styles = StyleSheet.create({
|
|
297
302
|
container: {
|
|
298
|
-
gap:
|
|
303
|
+
gap: MATRIX_COL_GAP,
|
|
299
304
|
},
|
|
300
305
|
headRow: {
|
|
301
306
|
flexDirection: "row",
|
|
302
307
|
alignItems: "center",
|
|
303
|
-
gap:
|
|
308
|
+
gap: MATRIX_COL_GAP,
|
|
304
309
|
paddingBottom: 2,
|
|
305
310
|
},
|
|
306
311
|
grid: {
|
|
307
|
-
gap:
|
|
312
|
+
gap: MATRIX_COL_GAP,
|
|
308
313
|
},
|
|
309
314
|
row: {
|
|
310
315
|
flexDirection: "row",
|
|
311
316
|
alignItems: "center",
|
|
312
|
-
gap:
|
|
317
|
+
gap: MATRIX_COL_GAP,
|
|
313
318
|
},
|
|
314
319
|
rowLabel: {
|
|
315
320
|
paddingRight: 8,
|
|
@@ -333,7 +338,7 @@ const styles = StyleSheet.create({
|
|
|
333
338
|
totalsRow: {
|
|
334
339
|
flexDirection: "row",
|
|
335
340
|
alignItems: "center",
|
|
336
|
-
gap:
|
|
341
|
+
gap: MATRIX_COL_GAP,
|
|
337
342
|
marginTop: 3,
|
|
338
343
|
paddingTop: 8,
|
|
339
344
|
borderTopWidth: 1,
|
package/src/table_fit.ts
CHANGED
|
@@ -20,12 +20,22 @@ export interface TableFitColumn {
|
|
|
20
20
|
key: string;
|
|
21
21
|
/** Fixed width in px; omit for a flexible column. */
|
|
22
22
|
width?: number;
|
|
23
|
-
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
23
|
+
/** This column's claim on space, **`1` = highest**, the way P1/P2/P3 rank a
|
|
24
|
+
* bug: the column you least want to lose gets the SMALLEST number. Said the
|
|
25
|
+
* other way round it is the same rule — the larger the number, the sooner the
|
|
26
|
+
* column drops, and ties drop right-to-left. The FIRST column is the row's
|
|
27
|
+
* identity and never drops.
|
|
26
28
|
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
+
* Stated rank-first deliberately. "Higher drops first" is accurate and was
|
|
30
|
+
* all this said, but a reader who takes `priority` to mean "bigger is more
|
|
31
|
+
* important" inverts it and gives their most important column the LARGEST
|
|
32
|
+
* number — which is then the first column the fit sheds. That failure is
|
|
33
|
+
* invisible at desk width and shows only on a phone, so it reviews as
|
|
34
|
+
* correct. It has been read backwards once already.
|
|
35
|
+
*
|
|
36
|
+
* An unannotated column defaults to `columns.length + index`, which sits
|
|
37
|
+
* above the small ranks a real annotation uses rather than interleaved with
|
|
38
|
+
* them, so an
|
|
29
39
|
* unannotated register still sheds right-to-left AND an explicit priority is
|
|
30
40
|
* strictly safer than none. Defaulting to the bare index made annotation
|
|
31
41
|
* actively harmful: marking your most important column `priority: 1` TIED it
|