@lotics/ui 18.2.0 → 19.0.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/MIGRATION.md +65 -0
- package/docs/catalog.md +78 -26
- package/docs/data_entry.md +55 -21
- package/docs/templates.md +17 -17
- package/examples/tpl_item_list.tsx +26 -26
- package/examples/tpl_record.tsx +38 -38
- package/package.json +1 -1
- package/src/detail_row.tsx +7 -1
- package/src/linked_record_box.tsx +30 -13
- package/src/task.tsx +347 -135
- package/src/task_metrics.test.ts +48 -0
- package/src/task_metrics.ts +55 -0
package/package.json
CHANGED
package/src/detail_row.tsx
CHANGED
|
@@ -19,6 +19,12 @@ const DetailTableContext = createContext<DetailTableColumns | null>(null);
|
|
|
19
19
|
* side-by-side columns and stacks — below it, editors get crushed. */
|
|
20
20
|
const MIN_VALUE_WIDTH = 160;
|
|
21
21
|
|
|
22
|
+
/** THE kit's label column — ~18 characters, where real field names live. It is
|
|
23
|
+
* this table's default AND the floor a `TaskSubRow`'s label sizes from, so a
|
|
24
|
+
* record's fields and a task's read as one language on a surface carrying
|
|
25
|
+
* both; that only holds while the two are ONE number. */
|
|
26
|
+
export const DETAIL_LABEL_WIDTH = 130;
|
|
27
|
+
|
|
22
28
|
export interface DetailTableProps {
|
|
23
29
|
/** Label column width (px). Default 130. */
|
|
24
30
|
labelWidth?: number;
|
|
@@ -64,7 +70,7 @@ export interface DetailTableProps {
|
|
|
64
70
|
* the switch is automatic.
|
|
65
71
|
*/
|
|
66
72
|
export function DetailTable(props: DetailTableProps) {
|
|
67
|
-
const { labelWidth =
|
|
73
|
+
const { labelWidth = DETAIL_LABEL_WIDTH, trailingWidth, minHeight = INLINE_CONTROL_HEIGHT, minValueWidth = MIN_VALUE_WIDTH, children, style } = props;
|
|
68
74
|
const [width, setWidth] = useState<number | null>(null);
|
|
69
75
|
const stacked =
|
|
70
76
|
width != null && width < labelWidth + (trailingWidth ?? 0) + minValueWidth + 24;
|
|
@@ -5,6 +5,7 @@ import { PressDoor } from "./press_door";
|
|
|
5
5
|
import { Text } from "./text";
|
|
6
6
|
import { Icon, type IconName } from "./icon";
|
|
7
7
|
import { colors } from "./colors";
|
|
8
|
+
import { INLINE_CONTROL_HEIGHT } from "./inline_edit";
|
|
8
9
|
|
|
9
10
|
/** One reference fact. `V` is the value the variant admits — text under a door, any node without one. */
|
|
10
11
|
interface LinkedRecordFact<V = ReactNode> {
|
|
@@ -94,18 +95,30 @@ export function LinkedRecordBox(props: LinkedRecordBoxProps) {
|
|
|
94
95
|
</View>
|
|
95
96
|
{facts.length > 0 ? (
|
|
96
97
|
<View style={{ gap: 6 }}>
|
|
97
|
-
{facts.map((f) =>
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
98
|
+
{facts.map((f) => {
|
|
99
|
+
// THE ALIGNMENT LAW, the same one `DetailRow` runs on: the fact TOP-aligns and each
|
|
100
|
+
// cell centres within the first CONTROL LINE. A node value brings the kit's control
|
|
101
|
+
// band (an inline editor is 40 tall, a chip less), so both cells claim it — without
|
|
102
|
+
// it the label `Text`, stretched to the row, printed its words at the band's TOP,
|
|
103
|
+
// a half-band above the value it names. A STRING value has no band: label and value
|
|
104
|
+
// are the same 20px line, so they sit at the row's top and a long value that wraps
|
|
105
|
+
// still tops out level with its label.
|
|
106
|
+
const banded = typeof f.value !== "string";
|
|
107
|
+
return (
|
|
108
|
+
<View key={f.label} style={styles.fact}>
|
|
109
|
+
<View style={[{ width: factLabelWidth }, banded && styles.bandedCell]}>
|
|
110
|
+
<Text size="sm" color="muted">{f.label}</Text>
|
|
111
|
+
</View>
|
|
112
|
+
{banded ? (
|
|
113
|
+
// A node prints as authored — the "—" placeholder is a TEXT affordance, and a
|
|
114
|
+
// control renders its own empty state.
|
|
115
|
+
<View style={[{ flex: 1, minWidth: 0 }, styles.bandedCell]}>{f.value}</View>
|
|
116
|
+
) : (
|
|
117
|
+
<Text size="sm" style={{ flex: 1 }}>{f.value || "—"}</Text>
|
|
118
|
+
)}
|
|
119
|
+
</View>
|
|
120
|
+
);
|
|
121
|
+
})}
|
|
109
122
|
</View>
|
|
110
123
|
) : null}
|
|
111
124
|
{actions ? (
|
|
@@ -134,7 +147,11 @@ const styles = StyleSheet.create({
|
|
|
134
147
|
box: { flexDirection: "column", alignItems: "stretch", borderWidth: 1, borderColor: colors.zinc[200], borderRadius: 10, paddingHorizontal: 16, paddingTop: 14, paddingBottom: 10, gap: 12 },
|
|
135
148
|
identity: { flexDirection: "row", alignItems: "center", gap: 10 },
|
|
136
149
|
glyph: { width: 34, height: 34, borderRadius: 8, backgroundColor: colors.zinc[100], alignItems: "center", justifyContent: "center" },
|
|
137
|
-
fact: { flexDirection: "row", gap: 10 },
|
|
150
|
+
fact: { flexDirection: "row", gap: 10, alignItems: "flex-start" },
|
|
151
|
+
// A cell on the control band: exactly `minHeight` tall for a one-line label / a short chip,
|
|
152
|
+
// and taller content (a wrapped label, a stack of controls) tops out level instead of being
|
|
153
|
+
// dragged toward the block's centre.
|
|
154
|
+
bandedCell: { minHeight: INLINE_CONTROL_HEIGHT, justifyContent: "center" },
|
|
138
155
|
divider: { height: 1, backgroundColor: colors.zinc[100], marginHorizontal: -16 },
|
|
139
156
|
actions: { flexDirection: "row", alignItems: "center", flexWrap: "wrap", columnGap: 8, rowGap: 8, zIndex: 1 },
|
|
140
157
|
});
|