@proteos/ui 0.50.1 → 0.51.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/dist/index.cjs +659 -304
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +230 -8
- package/dist/index.d.ts +230 -8
- package/dist/index.js +651 -307
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/src/components/badge/badge.stories.tsx +25 -0
- package/src/components/badge/badge.test.tsx +22 -0
- package/src/components/badge/badge.tsx +41 -10
- package/src/components/badge-multi-select/badge-multi-select.tsx +16 -11
- package/src/components/conversation-brief-card/conversation-brief-card.stories.tsx +81 -0
- package/src/components/conversation-brief-card/conversation-brief-card.tsx +102 -0
- package/src/components/conversation-brief-card/conversation-brief-preview.tsx +156 -0
- package/src/components/conversation-brief-card/conversation-brief-review.test.tsx +214 -0
- package/src/components/conversation-brief-card/conversation-brief-review.tsx +192 -0
- package/src/components/conversation-brief-card/create-conversation-brief-client.ts +18 -0
- package/src/components/conversation-brief-card/index.ts +24 -0
- package/src/components/conversation-brief-card/types.ts +50 -0
- package/src/components/data-table/data-table.test.tsx +62 -0
- package/src/components/data-table/data-table.tsx +75 -7
- package/src/components/record-filter/chip-value.tsx +2 -0
- package/src/index.ts +1 -0
- package/src/styles.css +1 -0
|
@@ -159,8 +159,25 @@ export type DataTableProps<TRow> = {
|
|
|
159
159
|
* Default false — read-only tables stay borderless in the body.
|
|
160
160
|
*/
|
|
161
161
|
columnBorders?: boolean
|
|
162
|
+
/**
|
|
163
|
+
* Group consecutive rows under a full-width heading. Returns the group a row
|
|
164
|
+
* belongs to, or null to leave it ungrouped; a heading is emitted whenever
|
|
165
|
+
* the group id CHANGES between neighbours, so the caller is responsible for
|
|
166
|
+
* ordering rows by their group (with server-side sorting, sort by the
|
|
167
|
+
* grouping column first). That contract is what lets grouping work with
|
|
168
|
+
* infinite scroll — a group may continue on the next page without the table
|
|
169
|
+
* having to know the full row set up front.
|
|
170
|
+
*/
|
|
171
|
+
groupBy?: (row: TRow) => { id: string; label: ReactNode } | null
|
|
172
|
+
/** Height of a group heading row. Defaults to `rowHeight`. */
|
|
173
|
+
groupHeaderHeight?: number
|
|
162
174
|
}
|
|
163
175
|
|
|
176
|
+
/** One entry of the virtualized list: either a data row or a group heading. */
|
|
177
|
+
type ListItem<TRow> =
|
|
178
|
+
| { kind: 'row'; row: Row<TRow>; dataIndex: number }
|
|
179
|
+
| { kind: 'group'; id: string; label: ReactNode }
|
|
180
|
+
|
|
164
181
|
const DEFAULT_ROW_HEIGHT = 32
|
|
165
182
|
|
|
166
183
|
/** Column id of the leading checkbox column added by `selectionMode`. */
|
|
@@ -194,6 +211,8 @@ export function DataTable<TRow>({
|
|
|
194
211
|
selectedIds,
|
|
195
212
|
onSelectionChange,
|
|
196
213
|
columnBorders = false,
|
|
214
|
+
groupBy,
|
|
215
|
+
groupHeaderHeight,
|
|
197
216
|
}: DataTableProps<TRow>) {
|
|
198
217
|
const rows = useMemo<TRow[]>(() => {
|
|
199
218
|
const pages = query.data?.pages
|
|
@@ -327,6 +346,27 @@ export function DataTable<TRow>({
|
|
|
327
346
|
const tableRows = table.getRowModel().rows
|
|
328
347
|
const tableRowCount = tableRows.length
|
|
329
348
|
|
|
349
|
+
// Flatten rows into the virtualized list, splicing a heading in wherever the
|
|
350
|
+
// group changes. Without `groupBy` this is the rows themselves.
|
|
351
|
+
const listItems = useMemo<ListItem<TRow>[]>(() => {
|
|
352
|
+
if (!groupBy) {
|
|
353
|
+
return tableRows.map((row, dataIndex) => ({ kind: 'row', row, dataIndex }) as const)
|
|
354
|
+
}
|
|
355
|
+
const items: ListItem<TRow>[] = []
|
|
356
|
+
let currentGroupId: string | null = null
|
|
357
|
+
tableRows.forEach((row, dataIndex) => {
|
|
358
|
+
const group = groupBy(row.original)
|
|
359
|
+
if (group && group.id !== currentGroupId) {
|
|
360
|
+
currentGroupId = group.id
|
|
361
|
+
items.push({ kind: 'group', id: group.id, label: group.label })
|
|
362
|
+
} else if (!group) {
|
|
363
|
+
currentGroupId = null
|
|
364
|
+
}
|
|
365
|
+
items.push({ kind: 'row', row, dataIndex })
|
|
366
|
+
})
|
|
367
|
+
return items
|
|
368
|
+
}, [tableRows, groupBy])
|
|
369
|
+
|
|
330
370
|
// Reset scroll to top when sorting changes — without this, the user is
|
|
331
371
|
// staring at a different slice of a freshly-sorted dataset.
|
|
332
372
|
const scrollerRef = useRef<HTMLDivElement | null>(null)
|
|
@@ -347,10 +387,12 @@ export function DataTable<TRow>({
|
|
|
347
387
|
setListOffset((prev) => (prev === top ? prev : top))
|
|
348
388
|
})
|
|
349
389
|
|
|
390
|
+
const headingHeight = groupHeaderHeight ?? rowHeight
|
|
391
|
+
|
|
350
392
|
const virtualizer = useVirtualizer({
|
|
351
|
-
count:
|
|
393
|
+
count: listItems.length,
|
|
352
394
|
getScrollElement: () => scrollerRef.current,
|
|
353
|
-
estimateSize: () => rowHeight,
|
|
395
|
+
estimateSize: (index) => (listItems[index]?.kind === 'group' ? headingHeight : rowHeight),
|
|
354
396
|
overscan,
|
|
355
397
|
scrollMargin: listOffset,
|
|
356
398
|
})
|
|
@@ -511,8 +553,8 @@ export function DataTable<TRow>({
|
|
|
511
553
|
style={{ height: totalSize, position: 'relative', width: '100%' }}
|
|
512
554
|
>
|
|
513
555
|
{virtualItems.map((vRow) => {
|
|
514
|
-
const
|
|
515
|
-
if (!
|
|
556
|
+
const item = listItems[vRow.index]
|
|
557
|
+
if (!item) return null
|
|
516
558
|
const rowStyle: CSSProperties = {
|
|
517
559
|
// Items' `start` includes the virtualizer's scrollMargin
|
|
518
560
|
// (the list's offset below the sticky header); subtract
|
|
@@ -520,11 +562,18 @@ export function DataTable<TRow>({
|
|
|
520
562
|
transform: `translateY(${vRow.start - listOffset}px)`,
|
|
521
563
|
height: vRow.size,
|
|
522
564
|
}
|
|
565
|
+
if (item.kind === 'group') {
|
|
566
|
+
return (
|
|
567
|
+
<GroupHeaderRow key={`group:${item.id}`} style={rowStyle}>
|
|
568
|
+
{item.label}
|
|
569
|
+
</GroupHeaderRow>
|
|
570
|
+
)
|
|
571
|
+
}
|
|
523
572
|
return (
|
|
524
573
|
<DataRow
|
|
525
|
-
key={row.id}
|
|
526
|
-
row={row}
|
|
527
|
-
index={
|
|
574
|
+
key={item.row.id}
|
|
575
|
+
row={item.row}
|
|
576
|
+
index={item.dataIndex}
|
|
528
577
|
zebra={zebra}
|
|
529
578
|
onRowClick={onRowClick}
|
|
530
579
|
style={rowStyle}
|
|
@@ -554,6 +603,25 @@ export function DataTable<TRow>({
|
|
|
554
603
|
)
|
|
555
604
|
}
|
|
556
605
|
|
|
606
|
+
/* ---------- Group heading ------------------------------------------------ */
|
|
607
|
+
|
|
608
|
+
function GroupHeaderRow({ style, children }: { style: CSSProperties; children: ReactNode }) {
|
|
609
|
+
return (
|
|
610
|
+
<TableRow
|
|
611
|
+
absolute
|
|
612
|
+
style={style}
|
|
613
|
+
className={cn(
|
|
614
|
+
'bg-surface-2 border-b border-rule',
|
|
615
|
+
'font-mono text-[11px] uppercase tracking-[0.06em] text-ink-3',
|
|
616
|
+
)}
|
|
617
|
+
>
|
|
618
|
+
{/* One cell spanning the row — the heading labels the rows beneath it
|
|
619
|
+
rather than any single column. */}
|
|
620
|
+
<TableCell className="gap-2 text-inherit">{children}</TableCell>
|
|
621
|
+
</TableRow>
|
|
622
|
+
)
|
|
623
|
+
}
|
|
624
|
+
|
|
557
625
|
/* ---------- Row ---------------------------------------------------------- */
|
|
558
626
|
|
|
559
627
|
type DataRowProps<TRow> = {
|
package/src/index.ts
CHANGED
|
@@ -12,6 +12,7 @@ export * from './components/checkbox'
|
|
|
12
12
|
export * from './components/color-input'
|
|
13
13
|
export * from './components/combobox'
|
|
14
14
|
export * from './components/command'
|
|
15
|
+
export * from './components/conversation-brief-card'
|
|
15
16
|
export * from './components/currency-input'
|
|
16
17
|
export * from './components/data-table'
|
|
17
18
|
export * from './components/date-picker'
|