@lotics/ui 5.10.0 → 5.10.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/AGENTS.md CHANGED
@@ -690,7 +690,11 @@ recipe for a screen JOB; copy and adapt). Pick by the job:
690
690
  rows). For thousands+ you BROWSE, that's `tpl_dossier`'s paginated register, not this.** Both build
691
691
  on the `DataGrid` primitive (the grid shape, extracted once these two uses confirmed it); each owns
692
692
  only its data, toolbar, columns and per-group add. There is NO Task component — a task ROW varies
693
- too much to bake into one, so rows compose `CheckCircle` + `InlineTextInput` + inline-editor cells.
693
+ too much to bake into one, so rows compose `CheckCircle` + `InlineTextInput` + inline-editor cells
694
+ as **sibling** controls in a plain `View`. NEVER wrap the whole row in a `Pressable accessibilityRole="button"`
695
+ (for tap-to-expand) with those controls nested inside: RNW renders the row AND each control as a `<button>`,
696
+ so you get `<button>`s inside a `<button>` — invalid DOM + the inner controls drop out of the keyboard tab
697
+ order. Give "expand" its own affordance (a trailing `Pressable` over the meta/chevron), as `tpl_tasks` does.
694
698
  - **Data capture** — `tpl_intake` (multi-fieldset form + attachments) · `tpl_order` (the
695
699
  transactional form: find-or-create + line items + attachment CRUD + 2-col grid) · `tpl_billing`
696
700
  (charges→invoice→collect: the invoice DOCUMENT is the unit — editable charge lines + method,
@@ -285,25 +285,38 @@ export function TplTasks() {
285
285
  const d = dueInfo(t.due);
286
286
  return (
287
287
  <View key={t.id}>
288
- <Pressable onPress={() => toggleExpand(t.id, !open)} accessibilityRole="button" accessibilityLabel={t.title} style={({ hovered }: { hovered?: boolean }) => [styles.row, hovered ? styles.rowHover : null]}>
288
+ {/* A row is NOT a single button: the ring, the title, and the expand
289
+ affordance are SIBLING controls in a plain View. Wrapping the row in a
290
+ Pressable accessibilityRole="button" while nesting CheckCircle +
291
+ InlineTextInput (each renders a <button> on web) would put <button>s
292
+ inside a <button> — invalid DOM + the inner controls drop out of the
293
+ keyboard tab order. So the expand affordance is its own Pressable. */}
294
+ <View style={styles.row}>
289
295
  <CheckCircle checked={done} onChange={(on) => patch(t.id, { status: on ? "done" : "todo" })} accessibilityLabel={t.title} />
290
296
  <View style={{ flex: 1 }}>
291
297
  <InlineTextInput value={t.title} onSave={(v) => patch(t.id, { title: v })} struck={done} accessibilityLabel="Title" />
292
298
  </View>
293
- <View style={styles.meta}>
294
- {t.files.length > 0 ? (
295
- <View style={styles.clip}>
296
- <Icon name="paperclip" size={13} color={colors.zinc[400]} />
297
- <Text size="xs" color="muted" tabular>{t.files.length}</Text>
298
- </View>
299
- ) : null}
300
- {t.tags.length > 0 ? (
301
- <View style={styles.dots}>{t.tags.map((tag) => <View key={tag.value} style={[styles.dot, { backgroundColor: tagColor(tag) }]} />)}</View>
302
- ) : null}
303
- {d ? <Text size="xs" weight="medium" color={d.overdue && !done ? "danger" : "muted"}>{d.label}</Text> : null}
304
- </View>
305
- <Icon name={open ? "chevron-up" : "chevron-down"} size={16} color={colors.zinc[300]} />
306
- </Pressable>
299
+ <Pressable
300
+ onPress={() => toggleExpand(t.id, !open)}
301
+ accessibilityRole="button"
302
+ accessibilityLabel={open ? `Collapse ${t.title}` : `Expand ${t.title}`}
303
+ style={({ hovered }: { hovered?: boolean }) => [styles.expand, hovered ? styles.rowHover : null]}
304
+ >
305
+ <View style={styles.meta}>
306
+ {t.files.length > 0 ? (
307
+ <View style={styles.clip}>
308
+ <Icon name="paperclip" size={13} color={colors.zinc[400]} />
309
+ <Text size="xs" color="muted" tabular>{t.files.length}</Text>
310
+ </View>
311
+ ) : null}
312
+ {t.tags.length > 0 ? (
313
+ <View style={styles.dots}>{t.tags.map((tag) => <View key={tag.value} style={[styles.dot, { backgroundColor: tagColor(tag) }]} />)}</View>
314
+ ) : null}
315
+ {d ? <Text size="xs" weight="medium" color={d.overdue && !done ? "danger" : "muted"}>{d.label}</Text> : null}
316
+ </View>
317
+ <Icon name={open ? "chevron-up" : "chevron-down"} size={16} color={colors.zinc[300]} />
318
+ </Pressable>
319
+ </View>
307
320
  {open ? (
308
321
  <View style={styles.detail}>
309
322
  <Field label="Due">
@@ -414,6 +427,9 @@ const styles = StyleSheet.create({
414
427
  borderRadius: 10,
415
428
  },
416
429
  rowHover: { backgroundColor: colors.zinc[50] },
430
+ // The expand affordance — its own button (meta + chevron), sibling to the ring
431
+ // and title so no interactive control nests inside another.
432
+ expand: { flexDirection: "row", alignItems: "center", gap: 10, minHeight: 36, paddingHorizontal: 8, borderRadius: 8 },
417
433
  capture: {
418
434
  borderBottomWidth: 1,
419
435
  borderBottomColor: colors.zinc[100],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lotics/ui",
3
- "version": "5.10.0",
3
+ "version": "5.10.1",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  "./tokens": "./src/tokens.ts",