@lotics/ui 44.7.1 → 44.9.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/AGENTS.md +15 -3
- package/MIGRATION.md +110 -5
- package/docs/ai_patterns.md +20 -0
- package/docs/catalog.md +100 -8
- package/docs/composition.md +100 -6
- package/docs/data_entry.md +60 -6
- package/docs/reviewing.md +44 -3
- package/examples/tpl_item_list.tsx +8 -4
- package/package.json +4 -1
- package/src/accordion.tsx +9 -1
- package/src/agent_run_pane.tsx +7 -5
- package/src/card.tsx +7 -1
- package/src/card_select_item.tsx +15 -2
- package/src/checklist.tsx +23 -3
- package/src/control_surface.ts +18 -4
- package/src/data_grid.tsx +9 -1
- package/src/date_range_filter_field.tsx +9 -2
- package/src/date_stamp.tsx +8 -2
- package/src/detail_row.tsx +17 -12
- package/src/empty_state.tsx +9 -2
- package/src/error_state.tsx +9 -2
- package/src/field_annotations.tsx +25 -2
- package/src/finding.tsx +19 -10
- package/src/format_money.ts +16 -2
- package/src/inline_edit.tsx +29 -9
- package/src/inline_select.tsx +8 -1
- package/src/inline_text_input.tsx +6 -7
- package/src/ledger.tsx +5 -2
- package/src/locale.tsx +35 -1
- package/src/number_input.tsx +8 -1
- package/src/reference_field.tsx +9 -2
- package/src/related_record_row.tsx +80 -0
- package/src/running_ledger.tsx +196 -0
- package/src/section_heading.tsx +17 -5
- package/src/select.tsx +16 -16
- package/src/sequence.tsx +41 -25
- package/src/step_progress.tsx +6 -4
- package/src/stepper.tsx +26 -2
- package/src/table_fit.ts +18 -8
- package/src/tabs.tsx +19 -0
- package/src/text_input_field.tsx +35 -1
package/src/tabs.tsx
CHANGED
|
@@ -12,6 +12,19 @@ export interface TabOption<T extends string> {
|
|
|
12
12
|
/** A small status dot before the label — set only when the tab's area needs
|
|
13
13
|
* attention (a blocker / missing item). Omit for the resting state. */
|
|
14
14
|
status?: ColorName;
|
|
15
|
+
/** How many rows the tab's band holds, shown after the label.
|
|
16
|
+
*
|
|
17
|
+
* A PROP, never a count formatted into `label`. A tab band partitions a
|
|
18
|
+
* register exactly as `TableGroup` does, and that component states its size
|
|
19
|
+
* the same way for the same reason: a hand-built `"Trên tàu (4)"` is a second
|
|
20
|
+
* copy of a number the screen already computes, and it is the copy that goes
|
|
21
|
+
* stale. Pressing the tab is the filter, so the count is also the reason to
|
|
22
|
+
* press it.
|
|
23
|
+
*
|
|
24
|
+
* Yours to pass because it is rarely the child count — a paged or filtered
|
|
25
|
+
* band counts what matched, not what rendered. Omit when every band is
|
|
26
|
+
* always whole. */
|
|
27
|
+
count?: number;
|
|
15
28
|
}
|
|
16
29
|
|
|
17
30
|
interface TabsProps<T extends string> {
|
|
@@ -113,6 +126,12 @@ function TabButton<T extends string>(props: TabButtonProps<T>) {
|
|
|
113
126
|
>
|
|
114
127
|
{option.label}
|
|
115
128
|
</Text>
|
|
129
|
+
{option.count != null ? (
|
|
130
|
+
<Text color="zinc-500" tabular userSelect="none">
|
|
131
|
+
{option.count}
|
|
132
|
+
</Text>
|
|
133
|
+
) : null}
|
|
134
|
+
|
|
116
135
|
</View>
|
|
117
136
|
);
|
|
118
137
|
|
package/src/text_input_field.tsx
CHANGED
|
@@ -45,6 +45,24 @@ interface TextInputFieldProps extends RNTextInputProps {
|
|
|
45
45
|
* because the host already spent `CONTROL_TEXT_INSET` on it and two insets
|
|
46
46
|
* stacked is a visible jump to the right on focus. */
|
|
47
47
|
seamless?: boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Frameless at rest — no ground, no visible edge — for a field that must read
|
|
50
|
+
* as its own value until touched: a table cell, a title in place. The edge
|
|
51
|
+
* arrives on hover and the ring on focus, exactly as in the framed variant.
|
|
52
|
+
*
|
|
53
|
+
* It is a PROP rather than a `style` the caller passes because of the order
|
|
54
|
+
* below. Hover is applied before `style`, so a caller-supplied
|
|
55
|
+
* `borderColor: "transparent"` landed on top of it and erased the hover edge
|
|
56
|
+
* on every bare field in the kit — leaving a control whose entire affordance
|
|
57
|
+
* was that edge with no affordance at all, and no way to discover it was a
|
|
58
|
+
* control short of clicking. Nothing measured wrong: the field had a real
|
|
59
|
+
* focus ring, a real role, correct type and spacing, and the hover rule was
|
|
60
|
+
* right there in the array being silently overwritten one line later.
|
|
61
|
+
*
|
|
62
|
+
* Distinct from `seamless`, which surrenders the surface to a HOST that draws
|
|
63
|
+
* it. Bare has no host — it draws its own frame, just not at rest.
|
|
64
|
+
*/
|
|
65
|
+
variant?: "framed" | "bare";
|
|
48
66
|
// DOM-only ARIA attrs not declared on React Native's TextInputProps. They
|
|
49
67
|
// are forwarded verbatim to the underlying web input.
|
|
50
68
|
"aria-controls"?: string;
|
|
@@ -63,6 +81,7 @@ export function TextInputField(props: TextInputFieldProps) {
|
|
|
63
81
|
const {
|
|
64
82
|
style,
|
|
65
83
|
seamless,
|
|
84
|
+
variant = "framed",
|
|
66
85
|
icon,
|
|
67
86
|
clearable,
|
|
68
87
|
onClear,
|
|
@@ -202,8 +221,20 @@ export function TextInputField(props: TextInputFieldProps) {
|
|
|
202
221
|
icon && styles.withIcon,
|
|
203
222
|
showClear && styles.withClear,
|
|
204
223
|
showShortcut && shortcutWidth > 0 && { paddingRight: SHORTCUT_INSET + shortcutWidth },
|
|
205
|
-
|
|
224
|
+
// RESTING FRAME first, so a caller's `style` can still override it.
|
|
225
|
+
variant === "bare" && styles.bare,
|
|
206
226
|
style,
|
|
227
|
+
// ...and HOVER after `style`, which is the grammar's rule for fields
|
|
228
|
+
// (composition.md § the surface table) and was inverted here. A caller
|
|
229
|
+
// passing a resting `borderColor` — which is exactly how `bare` used
|
|
230
|
+
// to be expressed — silently erased the hover edge one line after it
|
|
231
|
+
// was computed. Order is the whole bug: every property was correct.
|
|
232
|
+
//
|
|
233
|
+
// Still BEFORE the last two. The ring is a shadow, so it does not
|
|
234
|
+
// compete; `seamless` must outrank hover, because there the border
|
|
235
|
+
// belongs to the host shell and the input drawing its own would put
|
|
236
|
+
// two edges on one field.
|
|
237
|
+
hovered && editable && { borderColor: HOVER_BORDER },
|
|
207
238
|
focusVisible && !seamless && { boxShadow: FOCUS_RING },
|
|
208
239
|
seamless && styles.seamless,
|
|
209
240
|
]}
|
|
@@ -277,6 +308,9 @@ const styles = StyleSheet.create({
|
|
|
277
308
|
// Safe for height because the box is `border-box`: dropping the border changes
|
|
278
309
|
// the content box, never the field's 40px outer size.
|
|
279
310
|
seamless: { borderColor: "transparent", borderWidth: 0, backgroundColor: "transparent", paddingHorizontal: 0 },
|
|
311
|
+
// Nothing at rest — but the 1px border STAYS, transparent, so the hover edge
|
|
312
|
+
// costs no reflow when it arrives.
|
|
313
|
+
bare: { borderColor: "transparent", backgroundColor: "transparent" },
|
|
280
314
|
disabled: {
|
|
281
315
|
color: colors.zinc["400"],
|
|
282
316
|
outlineStyle: "none" as unknown as "solid",
|