@lotics/ui 27.17.1 → 28.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.
@@ -17,6 +17,7 @@ import { FloatingActionBar } from "@lotics/ui/floating_action_bar";
17
17
  import { FormField } from "@lotics/ui/form_field";
18
18
  import { Icon, type IconName } from "@lotics/ui/icon";
19
19
  import { IconButton } from "@lotics/ui/icon_button";
20
+ import { CommentsButton } from "@lotics/ui/comments_button";
20
21
  import { CopyButton } from "@lotics/ui/copy_button";
21
22
  import { ListItem } from "@lotics/ui/list_item";
22
23
  import { TplRecord } from "./tpl_record";
@@ -109,6 +110,9 @@ const TRANG_THAI: Record<TrangThai, { label: string; color: ColorName; doc: { ti
109
110
 
110
111
  interface HoSo {
111
112
  ma: string;
113
+ /** How many comments the record carries — surfaced on the row so a thread is
114
+ * discoverable from the book rather than only after opening. */
115
+ binhLuan: number;
112
116
  khach: string;
113
117
  dienThoai: string;
114
118
  trangThai: TrangThai;
@@ -120,29 +124,29 @@ interface HoSo {
120
124
  }
121
125
 
122
126
  const HO_SO: HoSo[] = [
123
- { ma: "RC-2026-0041", khach: "Michael Torres", dienThoai: "(555) 384-7560", trangThai: "dangxuly", phi: 2_500_000, daThu: true, ngayThu: "03/06", ngayNhan: "02/06", phuTrach: "Sarah Chen" },
124
- { ma: "RC-2026-0042", khach: "Anna Whitfield", dienThoai: "(555) 220-4158", trangThai: "dangxuly", phi: 1_800_000, daThu: false, ngayNhan: "03/06", phuTrach: "Sarah Chen" },
125
- { ma: "RC-2026-0043", khach: "Northwind Packaging", dienThoai: "(555) 558-2143", trangThai: "chobosung", phi: 4_200_000, daThu: true, ngayThu: "05/06", ngayNhan: "04/06", phuTrach: "David Park" },
126
- { ma: "RC-2026-0044", khach: "Robert Hayes", dienThoai: "(555) 471-8026", trangThai: "hoantat", phi: 2_500_000, daThu: true, ngayThu: "29/05", ngayNhan: "28/05", phuTrach: "Sarah Chen" },
127
- { ma: "RC-2026-0045", khach: "Karen Mitchell", dienThoai: "(555) 605-3317", trangThai: "dangxuly", phi: 3_100_000, daThu: true, ngayThu: "06/06", ngayNhan: "05/06", phuTrach: "David Park" },
128
- { ma: "RC-2026-0046", khach: "Daniel Brooks", dienThoai: "(555) 112-9084", trangThai: "chobosung", phi: 1_800_000, daThu: false, ngayNhan: "06/06", phuTrach: "Sarah Chen" },
129
- { ma: "RC-2026-0047", khach: "Crestline Foods", dienThoai: "(555) 446-2708", trangThai: "hoantat", phi: 5_600_000, daThu: true, ngayThu: "31/05", ngayNhan: "30/05", phuTrach: "David Park" },
130
- { ma: "RC-2026-0048", khach: "Laura Bennett", dienThoai: "(555) 833-5192", trangThai: "dangxuly", phi: 2_500_000, daThu: false, ngayNhan: "09/06", phuTrach: "James Walker" },
131
- { ma: "RC-2026-0049", khach: "Thomas Reed", dienThoai: "(555) 760-3844", trangThai: "hoantat", phi: 1_800_000, daThu: true, ngayThu: "02/06", ngayNhan: "27/05", phuTrach: "James Walker" },
132
- { ma: "RC-2026-0050", khach: "Kevin Doyle", dienThoai: "(555) 905-1273", trangThai: "dangxuly", phi: 4_200_000, daThu: true, ngayThu: "10/06", ngayNhan: "10/06", phuTrach: "James Walker" },
133
- { ma: "RC-2026-0051", khach: "Atlas Components", dienThoai: "(555) 271-6355", trangThai: "dangxuly", phi: 6_800_000, daThu: true, ngayThu: "06/06", ngayNhan: "05/06", phuTrach: "Maria Lopez" },
134
- { ma: "RC-2026-0052", khach: "Rachel Foster", dienThoai: "(555) 540-2866", trangThai: "chobosung", phi: 2_500_000, daThu: true, ngayThu: "04/06", ngayNhan: "03/06", phuTrach: "Sarah Chen" },
135
- { ma: "RC-2026-0053", khach: "Patrick Shaw", dienThoai: "(555) 119-4727", trangThai: "hoantat", phi: 1_200_000, daThu: true, ngayThu: "30/05", ngayNhan: "26/05", phuTrach: "David Park" },
136
- { ma: "RC-2026-0054", khach: "Summit Logistics", dienThoai: "(555) 662-8059", trangThai: "dangxuly", phi: 5_600_000, daThu: false, ngayNhan: "09/06", phuTrach: "Maria Lopez" },
137
- { ma: "RC-2026-0055", khach: "Brian Keller", dienThoai: "(555) 484-9131", trangThai: "chobosung", phi: 3_100_000, daThu: false, ngayNhan: "07/06", phuTrach: "James Walker" },
138
- { ma: "RC-2026-0056", khach: "Emily Carter", dienThoai: "(555) 358-0274", trangThai: "hoantat", phi: 2_500_000, daThu: true, ngayThu: "03/06", ngayNhan: "29/05", phuTrach: "Sarah Chen" },
139
- { ma: "RC-2026-0057", khach: "Jason Pratt", dienThoai: "(555) 705-4693", trangThai: "dangxuly", phi: 1_200_000, daThu: true, ngayThu: "08/06", ngayNhan: "08/06", phuTrach: "David Park" },
140
- { ma: "RC-2026-0058", khach: "Beacon Supplies", dienThoai: "(555) 813-2460", trangThai: "hoantat", phi: 4_200_000, daThu: true, ngayThu: "01/06", ngayNhan: "28/05", phuTrach: "Maria Lopez" },
141
- { ma: "RC-2026-0059", khach: "Nina Alvarez", dienThoai: "(555) 270-1548", trangThai: "dangxuly", phi: 1_800_000, daThu: false, ngayNhan: "10/06", phuTrach: "Sarah Chen" },
142
- { ma: "RC-2026-0060", khach: "Steven Drake", dienThoai: "(555) 593-7186", trangThai: "chobosung", phi: 2_500_000, daThu: true, ngayThu: "09/06", ngayNhan: "08/06", phuTrach: "James Walker" },
143
- { ma: "RC-2026-0061", khach: "Harbor Industries", dienThoai: "(555) 047-5829", trangThai: "dangxuly", phi: 6_800_000, daThu: true, ngayThu: "11/06", ngayNhan: "10/06", phuTrach: "Maria Lopez" },
144
- { ma: "RC-2026-0062", khach: "Grace Holloway", dienThoai: "(555) 826-3050", trangThai: "hoantat", phi: 3_100_000, daThu: true, ngayThu: "05/06", ngayNhan: "31/05", phuTrach: "David Park" },
145
- { ma: "RC-2026-0063", khach: "Olivia Manning", dienThoai: "(555) 631-2904", trangThai: "dangxuly", phi: 1_200_000, daThu: false, ngayNhan: "11/06", phuTrach: "Sarah Chen" },
127
+ { ma: "RC-2026-0041", binhLuan: 2, khach: "Michael Torres", dienThoai: "(555) 384-7560", trangThai: "dangxuly", phi: 2_500_000, daThu: true, ngayThu: "03/06", ngayNhan: "02/06", phuTrach: "Sarah Chen" },
128
+ { ma: "RC-2026-0042", binhLuan: 0, khach: "Anna Whitfield", dienThoai: "(555) 220-4158", trangThai: "dangxuly", phi: 1_800_000, daThu: false, ngayNhan: "03/06", phuTrach: "Sarah Chen" },
129
+ { ma: "RC-2026-0043", binhLuan: 5, khach: "Northwind Packaging", dienThoai: "(555) 558-2143", trangThai: "chobosung", phi: 4_200_000, daThu: true, ngayThu: "05/06", ngayNhan: "04/06", phuTrach: "David Park" },
130
+ { ma: "RC-2026-0044", binhLuan: 0, khach: "Robert Hayes", dienThoai: "(555) 471-8026", trangThai: "hoantat", phi: 2_500_000, daThu: true, ngayThu: "29/05", ngayNhan: "28/05", phuTrach: "Sarah Chen" },
131
+ { ma: "RC-2026-0045", binhLuan: 1, khach: "Karen Mitchell", dienThoai: "(555) 605-3317", trangThai: "dangxuly", phi: 3_100_000, daThu: true, ngayThu: "06/06", ngayNhan: "05/06", phuTrach: "David Park" },
132
+ { ma: "RC-2026-0046", binhLuan: 0, khach: "Daniel Brooks", dienThoai: "(555) 112-9084", trangThai: "chobosung", phi: 1_800_000, daThu: false, ngayNhan: "06/06", phuTrach: "Sarah Chen" },
133
+ { ma: "RC-2026-0047", binhLuan: 3, khach: "Crestline Foods", dienThoai: "(555) 446-2708", trangThai: "hoantat", phi: 5_600_000, daThu: true, ngayThu: "31/05", ngayNhan: "30/05", phuTrach: "David Park" },
134
+ { ma: "RC-2026-0048", binhLuan: 0, khach: "Laura Bennett", dienThoai: "(555) 833-5192", trangThai: "dangxuly", phi: 2_500_000, daThu: false, ngayNhan: "09/06", phuTrach: "James Walker" },
135
+ { ma: "RC-2026-0049", binhLuan: 2, khach: "Thomas Reed", dienThoai: "(555) 760-3844", trangThai: "hoantat", phi: 1_800_000, daThu: true, ngayThu: "02/06", ngayNhan: "27/05", phuTrach: "James Walker" },
136
+ { ma: "RC-2026-0050", binhLuan: 0, khach: "Kevin Doyle", dienThoai: "(555) 905-1273", trangThai: "dangxuly", phi: 4_200_000, daThu: true, ngayThu: "10/06", ngayNhan: "10/06", phuTrach: "James Walker" },
137
+ { ma: "RC-2026-0051", binhLuan: 5, khach: "Atlas Components", dienThoai: "(555) 271-6355", trangThai: "dangxuly", phi: 6_800_000, daThu: true, ngayThu: "06/06", ngayNhan: "05/06", phuTrach: "Maria Lopez" },
138
+ { ma: "RC-2026-0052", binhLuan: 0, khach: "Rachel Foster", dienThoai: "(555) 540-2866", trangThai: "chobosung", phi: 2_500_000, daThu: true, ngayThu: "04/06", ngayNhan: "03/06", phuTrach: "Sarah Chen" },
139
+ { ma: "RC-2026-0053", binhLuan: 1, khach: "Patrick Shaw", dienThoai: "(555) 119-4727", trangThai: "hoantat", phi: 1_200_000, daThu: true, ngayThu: "30/05", ngayNhan: "26/05", phuTrach: "David Park" },
140
+ { ma: "RC-2026-0054", binhLuan: 0, khach: "Summit Logistics", dienThoai: "(555) 662-8059", trangThai: "dangxuly", phi: 5_600_000, daThu: false, ngayNhan: "09/06", phuTrach: "Maria Lopez" },
141
+ { ma: "RC-2026-0055", binhLuan: 3, khach: "Brian Keller", dienThoai: "(555) 484-9131", trangThai: "chobosung", phi: 3_100_000, daThu: false, ngayNhan: "07/06", phuTrach: "James Walker" },
142
+ { ma: "RC-2026-0056", binhLuan: 0, khach: "Emily Carter", dienThoai: "(555) 358-0274", trangThai: "hoantat", phi: 2_500_000, daThu: true, ngayThu: "03/06", ngayNhan: "29/05", phuTrach: "Sarah Chen" },
143
+ { ma: "RC-2026-0057", binhLuan: 2, khach: "Jason Pratt", dienThoai: "(555) 705-4693", trangThai: "dangxuly", phi: 1_200_000, daThu: true, ngayThu: "08/06", ngayNhan: "08/06", phuTrach: "David Park" },
144
+ { ma: "RC-2026-0058", binhLuan: 0, khach: "Beacon Supplies", dienThoai: "(555) 813-2460", trangThai: "hoantat", phi: 4_200_000, daThu: true, ngayThu: "01/06", ngayNhan: "28/05", phuTrach: "Maria Lopez" },
145
+ { ma: "RC-2026-0059", binhLuan: 5, khach: "Nina Alvarez", dienThoai: "(555) 270-1548", trangThai: "dangxuly", phi: 1_800_000, daThu: false, ngayNhan: "10/06", phuTrach: "Sarah Chen" },
146
+ { ma: "RC-2026-0060", binhLuan: 0, khach: "Steven Drake", dienThoai: "(555) 593-7186", trangThai: "chobosung", phi: 2_500_000, daThu: true, ngayThu: "09/06", ngayNhan: "08/06", phuTrach: "James Walker" },
147
+ { ma: "RC-2026-0061", binhLuan: 1, khach: "Harbor Industries", dienThoai: "(555) 047-5829", trangThai: "dangxuly", phi: 6_800_000, daThu: true, ngayThu: "11/06", ngayNhan: "10/06", phuTrach: "Maria Lopez" },
148
+ { ma: "RC-2026-0062", binhLuan: 0, khach: "Grace Holloway", dienThoai: "(555) 826-3050", trangThai: "hoantat", phi: 3_100_000, daThu: true, ngayThu: "05/06", ngayNhan: "31/05", phuTrach: "David Park" },
149
+ { ma: "RC-2026-0063", binhLuan: 3, khach: "Olivia Manning", dienThoai: "(555) 631-2904", trangThai: "dangxuly", phi: 1_200_000, daThu: false, ngayNhan: "11/06", phuTrach: "Sarah Chen" },
146
150
  ];
147
151
 
148
152
  const TABS: { value: Tab; label: string }[] = [
@@ -165,13 +169,18 @@ const ASSIGNEES = [...new Set(HO_SO.map((r) => r.phuTrach))].map((n) => ({ label
165
169
  // (label-over-value) so nothing is lost — the drawer carries full detail
166
170
  // either way.
167
171
  const COLUMNS: TableColumn[] = [
168
- { key: "ma", label: "Record ID", width: 112, sortable: true },
172
+ // The SUBJECT leads. A reader scans this book by who the record is FOR, and
173
+ // says "the Harbor Freight one" — never "RC-2026-0041". The key is how the
174
+ // SYSTEM refers to the record, so it rides the identity cell's supporting line
175
+ // where every other supporting value lives. It kept a 112px column of its own
176
+ // while the thing everyone actually looks for sat second.
169
177
  { key: "khach", label: "Customer", flex: 1, sortable: true, priority: 1 },
170
178
  { key: "trangThai", label: "Status", width: 112, sortable: true, priority: 3 },
171
179
  { key: "phi", label: "Fee", width: 116, align: "right", sortable: true, priority: 2 },
172
180
  { key: "ngayNhan", label: "Received", width: 72, sortable: true, priority: 5 },
173
181
  ];
174
182
 
183
+
175
184
  // DOCUMENT — the paper the row needs NOW: while the fee is unpaid the
176
185
  // paper that matters is the payment slip; once paid, the stage's own document.
177
186
  function docFor(r: HoSo, daThu: boolean): { title: string; icon: IconName } {
@@ -189,10 +198,11 @@ function menuFor(r: HoSo, daThu: boolean): ActionMenuItem[] {
189
198
  ];
190
199
  }
191
200
 
192
- function HoSoRow({ hs, daThu, selected, marked, selectable, onToggle, onPress }: { hs: HoSo; daThu: boolean; selected: boolean; marked: boolean; selectable: boolean; onToggle: (on: boolean) => void; onPress: () => void }) {
201
+ function HoSoRow({ hs, ordinal, daThu, selected, marked, selectable, onToggle, onPress, onOpenComments }: { hs: HoSo; ordinal: number; daThu: boolean; selected: boolean; marked: boolean; selectable: boolean; onToggle: (on: boolean) => void; onPress: () => void; onOpenComments: () => void }) {
193
202
  return (
194
203
  <TableRow
195
204
  onPress={onPress}
205
+ ordinal={ordinal}
196
206
  selected={selected}
197
207
  marked={marked}
198
208
  minHeight={56}
@@ -202,11 +212,17 @@ function HoSoRow({ hs, daThu, selected, marked, selectable, onToggle, onPress }:
202
212
  trailing={<ActionMenu items={menuFor(hs, daThu)} accessibilityLabel={`Actions for ${hs.ma}`} />}
203
213
  >
204
214
  <TableCell>
205
- <Text size="sm" weight="semibold" tabular>{hs.ma}</Text>
206
- </TableCell>
207
- <TableCell>
208
- <View style={{ gap: 2, alignItems: "flex-start" }}>
209
- <Text size="sm" weight="medium" numberOfLines={1}>{hs.khach}</Text>
215
+ <View style={{ gap: 2, alignSelf: "stretch" }}>
216
+ {/* The name takes the row, the thread pins right. A comment count is
217
+ the one thing on this row that is about PEOPLE talking rather than
218
+ the record's own state, so it sits apart from the values below it
219
+ instead of joining them on the supporting line. */}
220
+ <View style={{ flexDirection: "row", alignItems: "center", gap: 8 }}>
221
+ <Text size="sm" weight="medium" numberOfLines={1} style={{ flexShrink: 1 }}>{hs.khach}</Text>
222
+ {/* Rendered only where a thread EXISTS — a zero on every quiet row is a
223
+ column of noise that trains the eye to skip where the signal appears. */}
224
+ {hs.binhLuan > 0 ? <CommentsButton count={hs.binhLuan} subject={hs.khach} onPress={onOpenComments} /> : null}
225
+ </View>
210
226
  {/* Every value on this row is selectable — that is what a register owes
211
227
  its reader, and it costs no chrome. The phone gets a CONTROL on top
212
228
  because it is the one value copied often enough to earn one (a desk
@@ -214,7 +230,19 @@ function HoSoRow({ hs, daThu, selected, marked, selectable, onToggle, onPress }:
214
230
  inside its own cell: the trailing gutter belongs to the ⋯, and with
215
231
  two values in this cell it would have nothing to name. */}
216
232
  <View style={{ flexDirection: "row", alignItems: "center", gap: 2 }}>
217
- <Text size="xs" color="muted" tabular numberOfLines={1}>{hs.dienThoai}</Text>
233
+ {/* The KEY, muted, on the supporting line — at most this, never a
234
+ column and never the row's name.
235
+
236
+ It does NOT shrink. A key is quoted whole or it is not a key: a
237
+ phone number clipped to "(555) 384-7…" is still recognisably a
238
+ number the reader can widen the column to finish, while
239
+ "RC-2026-0…" is unusable — it is the exact string a desk reads
240
+ out. So the fixed-length identifier holds its width and the phone
241
+ gives way, which is also the right order of loss: the phone has a
242
+ Copy control beside it that hands over the full value regardless. */}
243
+ <Text size="xs" color="muted" tabular numberOfLines={1} style={{ flexShrink: 0 }}>{hs.ma}</Text>
244
+ <Text size="xs" color="muted" style={{ flexShrink: 0 }}> · </Text>
245
+ <Text size="xs" color="muted" tabular numberOfLines={1} style={{ flexShrink: 1 }}>{hs.dienThoai}</Text>
218
246
  <CopyButton value={hs.dienThoai} label="Copy phone number" />
219
247
  </View>
220
248
  </View>
@@ -248,8 +276,11 @@ function HoSoRow({ hs, daThu, selected, marked, selectable, onToggle, onPress }:
248
276
  * to a BACK button + the pushed case id (the proper home for back — drawer
249
277
  * chrome, not scroll content), and the sequence hides (stepping the root
250
278
  * record from inside a linked one would be disorienting). */
251
- function RecordDrawer({ hs, daThu: _daThu, onPaid: _onPaid, onClose, onPrev, onNext, position }: {
279
+ function RecordDrawer({ hs, daThu: _daThu, onPaid: _onPaid, onClose, onPrev, onNext, position, openSection }: {
252
280
  hs: HoSo;
281
+ /** The section the record should land on — set when the row's comment bubble
282
+ * opened it, so the reader arrives in the conversation they pressed. */
283
+ openSection?: string;
253
284
  daThu: boolean;
254
285
  onPaid: () => void;
255
286
  onClose: () => void;
@@ -288,7 +319,7 @@ function RecordDrawer({ hs, daThu: _daThu, onPaid: _onPaid, onClose, onPrev, onN
288
319
  reading order as its last section. Rendering the component is what keeps
289
320
  the two identical; the hand-built workspace that used to live here is
290
321
  how they drifted — a checklist on this side, a pipeline on that one. */}
291
- <TplRecord chrome="drawer" code={hs.ma} />
322
+ <TplRecord chrome="drawer" code={hs.ma} openSection={openSection} />
292
323
  </Screen>
293
324
  <Screen route="/record/:ma">{(p) => <LinkedRecordScreen ma={p.ma} />}</Screen>
294
325
  </Drawer>
@@ -789,6 +820,9 @@ export function TplItemList() {
789
820
  const [search, setSearch] = useState("");
790
821
  const [page, setPage] = useState(0);
791
822
  const [openMa, setOpenMa] = useState<string | null>(null);
823
+ /** Which section the record should OPEN on — set by the row's comment bubble,
824
+ * cleared on close so the next plain row press lands at the top. */
825
+ const [openSection, setOpenSection] = useState<string | undefined>(undefined);
792
826
  // Paid state is LIFTED, not owned by the drawer: settling the ledger inside the
793
827
  // drawer flips the row's Unpaid/Print/menu live, because both read one source.
794
828
  const [paidMap, setPaidMap] = useState<Record<string, boolean>>({});
@@ -802,6 +836,7 @@ export function TplItemList() {
802
836
  const ma = `RC-2026-${String(nextMa.current++).padStart(4, "0")}`;
803
837
  const row: HoSo = {
804
838
  ma,
839
+ binhLuan: 0,
805
840
  khach,
806
841
  dienThoai,
807
842
  trangThai: "dangxuly",
@@ -1013,9 +1048,14 @@ export function TplItemList() {
1013
1048
 
1014
1049
  {/* the register — eyebrow band (Table header) → list rows → totals + paging */}
1015
1050
  <View>
1051
+ {/* THE COUNTED REGISTER. `count` is what MATCHED, not what this page
1052
+ shows — the reader's "how many" survives paging — and each row's
1053
+ `ordinal` continues across pages rather than restarting at 1, so the
1054
+ last row of page 2 reads 100 under a header that says 100. */}
1016
1055
  <Table
1017
1056
  columns={COLUMNS}
1018
1057
  leading={24}
1058
+ count={filtered.length}
1019
1059
  trailing={132}
1020
1060
  sort={sort}
1021
1061
  onSort={onSort}
@@ -1029,8 +1069,8 @@ export function TplItemList() {
1029
1069
  />
1030
1070
  }
1031
1071
  >
1032
- {pageRows.map((r) => (
1033
- <HoSoRow key={r.ma} hs={r} daThu={daThuOf(r)} selected={r.ma === openMa} marked={sel.has(r.ma)} selectable={isSelectable(r)} onToggle={(on) => sel.toggle(r.ma, on)} onPress={() => setOpenMa(r.ma)} />
1072
+ {pageRows.map((r, i) => (
1073
+ <HoSoRow key={r.ma} hs={r} ordinal={page * PAGE_SIZE + i + 1} daThu={daThuOf(r)} selected={r.ma === openMa} marked={sel.has(r.ma)} selectable={isSelectable(r)} onToggle={(on) => sel.toggle(r.ma, on)} onPress={() => setOpenMa(r.ma)} onOpenComments={() => { setOpenSection("comments"); setOpenMa(r.ma); }} />
1034
1074
  ))}
1035
1075
  </Table>
1036
1076
  {pageRows.length === 0 ? (
@@ -1078,9 +1118,10 @@ export function TplItemList() {
1078
1118
  <ScreenRouter key={openRow.ma}>
1079
1119
  <RecordDrawer
1080
1120
  hs={openRow}
1121
+ openSection={openSection}
1081
1122
  daThu={daThuOf(openRow)}
1082
1123
  onPaid={() => setPaidMap((prev) => ({ ...prev, [openRow.ma]: true }))}
1083
- onClose={() => setOpenMa(null)}
1124
+ onClose={() => { setOpenMa(null); setOpenSection(undefined); }}
1084
1125
  onPrev={seqIndex > 0 ? () => setOpenMa(pageRows[seqIndex - 1].ma) : undefined}
1085
1126
  onNext={seqIndex >= 0 && seqIndex < pageRows.length - 1 ? () => setOpenMa(pageRows[seqIndex + 1].ma) : undefined}
1086
1127
  position={seqIndex >= 0 ? `${seqIndex + 1}/${pageRows.length}` : undefined}