@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 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` (higher drops
1411
- first; default = column order, rightmost first; column 0 the identity never drops), and
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lotics/ui",
3
- "version": "44.9.0",
3
+ "version": "44.9.1",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  "./vite": {
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="center" numberOfLines={1} style={styles.colCell}>
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="center" numberOfLines={1} style={[styles.totalCol, { width: totalColWidth }]}>
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" color="muted" numberOfLines={1} style={[styles.rowLabel, { width: rowLabelWidth }]}>
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="center" numberOfLines={1} style={[styles.totalCol, { width: totalColWidth }]}>
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="center" color={value === 0 ? "zinc-500" : "default"}>
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="center" numberOfLines={1} style={styles.colCell}>
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="center" numberOfLines={1} style={[styles.totalCol, { width: totalColWidth }]}>
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: 3,
303
+ gap: MATRIX_COL_GAP,
299
304
  },
300
305
  headRow: {
301
306
  flexDirection: "row",
302
307
  alignItems: "center",
303
- gap: 3,
308
+ gap: MATRIX_COL_GAP,
304
309
  paddingBottom: 2,
305
310
  },
306
311
  grid: {
307
- gap: 3,
312
+ gap: MATRIX_COL_GAP,
308
313
  },
309
314
  row: {
310
315
  flexDirection: "row",
311
316
  alignItems: "center",
312
- gap: 3,
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: 3,
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
- /** Drop precedence when the container can't fit every column: HIGHER numbers
24
- * drop first, ties drop right-to-left. The FIRST column is the row's identity
25
- * it never drops.
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
- * An unannotated column defaults to `columns.length + index`, which is above
28
- * every hand-written priority rather than interleaved with them, so an
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