@lotics/ui 17.0.0 → 17.0.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.
@@ -275,10 +275,26 @@ aligns with nothing.
275
275
  date, a menu and children of its own. There is no separate subtask shape to outgrow, and no
276
276
  built-in chevron: collapsing is the app's call (hold a boolean, render the nested list or don't).
277
277
 
278
- **State flows from the LEAVES.** Where a parent's position is computable from its children, DERIVE
279
- it and render the parent's status read-only an editable copy lets a row disagree with its own
280
- children, and makes the operator enter one fact twice. Give the parent's ring the bulk gesture
281
- instead (tick it, every child ticks). A parent with nothing to derive from is set by hand.
278
+ **State flows from the LEAVES.** Where a row's position is computable from what it owns, DERIVE
279
+ it a stored status is a second copy of what the fields already say, and two copies drift. Give
280
+ the parent's ring the bulk gesture where its children ARE the fact (tick it, every child ticks);
281
+ where the ring answers a DIFFERENT field the children do not determine a date, say — coupling
282
+ them asserts something the operator never said.
283
+
284
+ **`TaskFields` holds CONTROLS.** Where exactly one field determines a row's status, that cell IS
285
+ the field's editor: a read-only copy above an editable control puts one value on the row twice,
286
+ under two labels. Everything else derived is prose in `TaskCaption`. A read-only pill parked in
287
+ the column sits `TASK_TEXT_INSET` left of its neighbours (a `variant="cell"` control insets its
288
+ text by that much; a bare `Badge` by nothing) and reads as pressable when it is not. A `Badge`
289
+ INSIDE a cell control is fine — it inherits that cell's inset, and the 12px its label sits in by
290
+ is the dot and its gap, not padding.
291
+
292
+ **A status reports what is OUTSTANDING, never what is settled.** A finished row already says so
293
+ twice — a full ring and its date — so naming the settled condition is a third copy of one fact.
294
+ The empty end is worse than redundant: "every document is in" and "nobody has looked yet" are the
295
+ same empty list, so a badge asserting the first states something no one established. Stay silent
296
+ at both ends, and separate "is it under way" (which drives the ring) from "has it anything to
297
+ say" (which drives the caption) — fusing them forces every pending state to invent a label.
282
298
 
283
299
  `CheckCircle` has THREE positions (`state="none" | "partial" | "done"`), and `partial` is not
284
300
  optional dressing: a step whose vocabulary reads "No portal account → Account created → Filed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lotics/ui",
3
- "version": "17.0.0",
3
+ "version": "17.0.1",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  "./vite": {
package/src/checkbox.tsx CHANGED
@@ -9,6 +9,12 @@ interface CheckboxProps {
9
9
 
10
10
  export function Checkbox(props: CheckboxProps) {
11
11
  const { checked, indeterminate } = props;
12
+ // `indeterminate` FILLS the box, exactly as `checked` does — the mixed state is a painted
13
+ // box with a dash on every platform that has one, and the dash is drawn in `white`. Keying
14
+ // the fill on `checked` alone meant a mixed-but-unchecked control drew a white dash on the
15
+ // white background: invisible. Every `selectAll` therefore reported "nothing selected" while
16
+ // rows were selected — under-reporting, which is the worse direction to be wrong in.
17
+ const filled = checked || indeterminate === true;
12
18
 
13
19
  return (
14
20
  <View
@@ -19,7 +25,7 @@ export function Checkbox(props: CheckboxProps) {
19
25
  alignItems: "center",
20
26
  borderWidth: 1,
21
27
  borderColor: colors.border,
22
- backgroundColor: checked ? colors.zinc["800"] : colors.background,
28
+ backgroundColor: filled ? colors.zinc["800"] : colors.background,
23
29
  borderRadius: 6,
24
30
  }}
25
31
  >
package/src/task.tsx CHANGED
@@ -71,13 +71,15 @@ const DENSE_ROW = 32;
71
71
  /** Horizontal rhythm between the row's slots. */
72
72
  const ROW_GAP = 12;
73
73
  /**
74
- * The inline controls rest inside 8px of horizontal padding, so a `variant="cell"` title's
75
- * TEXT starts 8px inside its box. Everything that hangs beneath a row — the caption, the
76
- * detail, a nested list — adds the same inset, so it lines up with the words rather than with
77
- * the editor's invisible box. A plain-`Text` title should carry it too (`TASK_TEXT_INSET`),
78
- * or its caption will sit 8px to its right.
74
+ * How far a `variant="cell"` control insets its own text: 1px of transparent border plus 8px
75
+ * of padding. Everything that hangs beneath a row — the caption, the detail, a nested list —
76
+ * adds the same, so it lines up with the WORDS rather than with the editor's invisible box. A
77
+ * plain-`Text` title carries it too, or it sits out of line with the editable titles around it.
78
+ *
79
+ * It was 8 — the padding alone, forgetting the border — which put every caption, detail block
80
+ * and nested list 1px left of the title it belonged to.
79
81
  */
80
- export const TASK_TEXT_INSET = 8;
82
+ export const TASK_TEXT_INSET = 9;
81
83
 
82
84
  export interface TaskListProps {
83
85
  children: ReactNode;