@open-mercato/ui 0.10.1-develop.7702.1.f991ecf392 → 0.10.1-develop.7707.1.61845a3ef7
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/ui",
|
|
3
|
-
"version": "0.10.1-develop.
|
|
3
|
+
"version": "0.10.1-develop.7707.1.61845a3ef7",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -155,14 +155,14 @@
|
|
|
155
155
|
"remark-gfm": "^4.0.1"
|
|
156
156
|
},
|
|
157
157
|
"peerDependencies": {
|
|
158
|
-
"@open-mercato/shared": "0.10.1-develop.
|
|
158
|
+
"@open-mercato/shared": "0.10.1-develop.7707.1.61845a3ef7",
|
|
159
159
|
"react": ">=18.0.0",
|
|
160
160
|
"react-dom": ">=18.0.0",
|
|
161
161
|
"react-is": ">=18.0.0"
|
|
162
162
|
},
|
|
163
163
|
"devDependencies": {
|
|
164
164
|
"@figma/code-connect": "^1.3.4",
|
|
165
|
-
"@open-mercato/shared": "0.10.1-develop.
|
|
165
|
+
"@open-mercato/shared": "0.10.1-develop.7707.1.61845a3ef7",
|
|
166
166
|
"@testing-library/dom": "^10.4.1",
|
|
167
167
|
"@testing-library/jest-dom": "^7.0.1",
|
|
168
168
|
"@testing-library/react": "^16.3.3",
|
|
@@ -553,6 +553,14 @@ const STICKY_RIGHT_SHADOW_CLASS =
|
|
|
553
553
|
'md:before:absolute md:before:inset-y-0 md:before:-left-2 md:before:w-2 md:before:bg-gradient-to-l md:before:from-foreground/8 md:before:to-transparent md:before:pointer-events-none'
|
|
554
554
|
const STICKY_LEFT_SHADOW_CLASS =
|
|
555
555
|
'md:after:absolute md:after:inset-y-0 md:after:-right-2 md:after:w-2 md:after:bg-gradient-to-r md:after:from-foreground/8 md:after:to-transparent md:after:pointer-events-none'
|
|
556
|
+
// Pinned cells need an opaque background so scrolled columns don't show through,
|
|
557
|
+
// which also hides the translucent tints painted behind them (the `bg-muted/40`
|
|
558
|
+
// header strip on `<thead>`, the `bg-muted/30` row hover on `<tr>`). A flat
|
|
559
|
+
// gradient (equal stops) layers the same tint over the opaque surface, so the
|
|
560
|
+
// pinned cell composites to exactly what the transparent cells show (#7414).
|
|
561
|
+
const STICKY_HEADER_TINT_CLASS = 'md:bg-linear-to-r md:from-muted/40 md:to-muted/40'
|
|
562
|
+
const STICKY_ROW_HOVER_TINT_CLASS =
|
|
563
|
+
'md:group-hover/row:bg-linear-to-r md:group-hover/row:from-muted/30 md:group-hover/row:to-muted/30'
|
|
556
564
|
|
|
557
565
|
export type BulkActionExecuteResult = {
|
|
558
566
|
ok: boolean
|
|
@@ -3591,6 +3599,11 @@ function DataTableImpl<T extends RowData>({
|
|
|
3591
3599
|
// `isolate` keeps the z-index of pinned cells local to the table so they
|
|
3592
3600
|
// scroll beneath the sticky app top bar instead of over it.
|
|
3593
3601
|
const tableScrollWrapperClassName = embedded ? 'isolate' : 'isolate overflow-auto'
|
|
3602
|
+
// Non-embedded tables sit on the `bg-card` container above; embedded ones
|
|
3603
|
+
// render on whatever surface hosts them, so they keep the page background.
|
|
3604
|
+
const stickySurfaceClass = embedded ? 'md:bg-background' : 'md:bg-card'
|
|
3605
|
+
const stickyHeaderSurfaceClass = `${stickySurfaceClass} ${STICKY_HEADER_TINT_CLASS}`
|
|
3606
|
+
const stickyBodySurfaceClass = `${stickySurfaceClass} ${STICKY_ROW_HOVER_TINT_CLASS}`
|
|
3594
3607
|
|
|
3595
3608
|
const virtualScrollRef = React.useRef<HTMLDivElement>(null)
|
|
3596
3609
|
// Measure the horizontal scroll viewport so the empty and error states can center within
|
|
@@ -3764,7 +3777,7 @@ function DataTableImpl<T extends RowData>({
|
|
|
3764
3777
|
const columnMeta = (header.column.columnDef as any)?.meta
|
|
3765
3778
|
const priority = resolvePriority(header.column)
|
|
3766
3779
|
const isFirstDataColumn = headerIndex === 0
|
|
3767
|
-
const stickyClass = stickyFirstColumn && isFirstDataColumn ? ` md:sticky md:left-0 md:z-10
|
|
3780
|
+
const stickyClass = stickyFirstColumn && isFirstDataColumn ? ` md:sticky md:left-0 md:z-10 ${stickyHeaderSurfaceClass} ${STICKY_LEFT_SHADOW_CLASS}` : ''
|
|
3768
3781
|
const isColumnSortable = sortable && !!header.column.getCanSort?.()
|
|
3769
3782
|
const headerContent = header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())
|
|
3770
3783
|
// Columns that can't be sorted (e.g. a manual "select" checkbox column) may render
|
|
@@ -3820,7 +3833,7 @@ function DataTableImpl<T extends RowData>({
|
|
|
3820
3833
|
<TableHead
|
|
3821
3834
|
className={cn(
|
|
3822
3835
|
actionsColumnAlign === 'center' ? 'w-0 text-center' : 'w-0 text-right',
|
|
3823
|
-
stickyActionsColumn && `md:sticky md:right-0 md:z-20
|
|
3836
|
+
stickyActionsColumn && `md:sticky md:right-0 md:z-20 ${stickyHeaderSurfaceClass} ${STICKY_RIGHT_SHADOW_CLASS}`,
|
|
3824
3837
|
)}
|
|
3825
3838
|
>
|
|
3826
3839
|
{t('ui.dataTable.actionsColumn', 'Actions')}
|
|
@@ -3871,7 +3884,7 @@ function DataTableImpl<T extends RowData>({
|
|
|
3871
3884
|
<TableRow
|
|
3872
3885
|
key={row.id}
|
|
3873
3886
|
data-state={row.getIsSelected() && 'selected'}
|
|
3874
|
-
className={isClickable
|
|
3887
|
+
className={cn('group/row', isClickable && 'cursor-pointer hover:bg-muted/50 transition-colors')}
|
|
3875
3888
|
onClick={isClickable ? (e) => {
|
|
3876
3889
|
// Don't trigger row click if clicking on actions cell
|
|
3877
3890
|
if ((e.target as HTMLElement).closest('[data-actions-cell]')) {
|
|
@@ -3986,7 +3999,7 @@ function DataTableImpl<T extends RowData>({
|
|
|
3986
3999
|
return (
|
|
3987
4000
|
<TableCell
|
|
3988
4001
|
key={cell.id}
|
|
3989
|
-
className={responsiveClass(priority, columnMeta?.hidden) + (isStickyCell ? ` md:sticky md:left-0 md:z-10
|
|
4002
|
+
className={responsiveClass(priority, columnMeta?.hidden) + (isStickyCell ? ` md:sticky md:left-0 md:z-10 ${stickyBodySurfaceClass} ${STICKY_LEFT_SHADOW_CLASS}` : '')}
|
|
3990
4003
|
style={typeof sizedWidth === 'number' ? { width: sizedWidth, minWidth: sizedWidth, maxWidth: sizedWidth } : undefined}
|
|
3991
4004
|
>
|
|
3992
4005
|
{expandAffordance ? (
|
|
@@ -4002,7 +4015,7 @@ function DataTableImpl<T extends RowData>({
|
|
|
4002
4015
|
<TableCell
|
|
4003
4016
|
className={cn(
|
|
4004
4017
|
actionsColumnAlign === 'center' ? 'text-center whitespace-nowrap' : 'text-right whitespace-nowrap',
|
|
4005
|
-
stickyActionsColumn && `md:sticky md:right-0 md:z-10
|
|
4018
|
+
stickyActionsColumn && `md:sticky md:right-0 md:z-10 ${stickyBodySurfaceClass} ${STICKY_RIGHT_SHADOW_CLASS}`,
|
|
4006
4019
|
)}
|
|
4007
4020
|
data-actions-cell
|
|
4008
4021
|
>
|
|
@@ -28,7 +28,7 @@ const tokensOf = (el: Element | null): string[] =>
|
|
|
28
28
|
// `md` breakpoint up, so narrow viewports fall back to the documented plain
|
|
29
29
|
// horizontal-scroll behavior where every column can be swiped into view.
|
|
30
30
|
describe('DataTable sticky columns are viewport-gated', () => {
|
|
31
|
-
function renderStickyTable() {
|
|
31
|
+
function renderStickyTable({ embedded = false }: { embedded?: boolean } = {}) {
|
|
32
32
|
const columns: ColumnDef<Row>[] = [
|
|
33
33
|
{ accessorKey: 'title', header: 'Title' },
|
|
34
34
|
{ accessorKey: 'status', header: 'Status' },
|
|
@@ -40,6 +40,7 @@ describe('DataTable sticky columns are viewport-gated', () => {
|
|
|
40
40
|
<DataTable
|
|
41
41
|
columns={columns}
|
|
42
42
|
data={[{ id: '1', title: 'Solar rollout', status: 'open' }]}
|
|
43
|
+
embedded={embedded}
|
|
43
44
|
stickyFirstColumn
|
|
44
45
|
stickyActionsColumn
|
|
45
46
|
rowActions={() => (
|
|
@@ -62,7 +63,7 @@ describe('DataTable sticky columns are viewport-gated', () => {
|
|
|
62
63
|
for (const cell of [firstHeader, firstBodyCell]) {
|
|
63
64
|
const tokens = tokensOf(cell)
|
|
64
65
|
expect(tokens).toEqual(
|
|
65
|
-
expect.arrayContaining(['md:sticky', 'md:left-0', 'md:bg-
|
|
66
|
+
expect.arrayContaining(['md:sticky', 'md:left-0', 'md:bg-card', 'md:after:absolute']),
|
|
66
67
|
)
|
|
67
68
|
expect(tokens).not.toContain('sticky')
|
|
68
69
|
expect(tokens).not.toContain('left-0')
|
|
@@ -104,7 +105,7 @@ describe('DataTable sticky columns are viewport-gated', () => {
|
|
|
104
105
|
for (const cell of [actionsHeader, actionsBodyCell]) {
|
|
105
106
|
const tokens = tokensOf(cell)
|
|
106
107
|
expect(tokens).toEqual(
|
|
107
|
-
expect.arrayContaining(['md:sticky', 'md:right-0', 'md:bg-
|
|
108
|
+
expect.arrayContaining(['md:sticky', 'md:right-0', 'md:bg-card', 'md:before:absolute']),
|
|
108
109
|
)
|
|
109
110
|
expect(tokens).not.toContain('sticky')
|
|
110
111
|
expect(tokens).not.toContain('right-0')
|
|
@@ -115,4 +116,62 @@ describe('DataTable sticky columns are viewport-gated', () => {
|
|
|
115
116
|
queryClient.clear()
|
|
116
117
|
}
|
|
117
118
|
})
|
|
119
|
+
|
|
120
|
+
// A non-embedded table sits on its `bg-card` container. Pinned cells used to
|
|
121
|
+
// paint `bg-background`, a darker band in the dark theme, and their opaque
|
|
122
|
+
// fill hid the header strip and the row-hover tint behind them (#7414).
|
|
123
|
+
it('paints pinned cells with the card surface and carries the header strip and row hover through', () => {
|
|
124
|
+
const { container, queryClient } = renderStickyTable()
|
|
125
|
+
try {
|
|
126
|
+
const headerCells = Array.from(container.querySelectorAll('thead th'))
|
|
127
|
+
const pinnedHeaders = [headerCells[0], headerCells[headerCells.length - 1]]
|
|
128
|
+
const bodyRow = container.querySelector('tbody tr')
|
|
129
|
+
const pinnedBodyCells = [
|
|
130
|
+
container.querySelector('tbody tr td'),
|
|
131
|
+
container.querySelector('tbody tr td[data-actions-cell]'),
|
|
132
|
+
]
|
|
133
|
+
|
|
134
|
+
for (const cell of pinnedHeaders) {
|
|
135
|
+
const tokens = tokensOf(cell)
|
|
136
|
+
expect(tokens).toEqual(
|
|
137
|
+
expect.arrayContaining(['md:bg-card', 'md:bg-linear-to-r', 'md:from-muted/40', 'md:to-muted/40']),
|
|
138
|
+
)
|
|
139
|
+
expect(tokens).not.toContain('md:bg-background')
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
expect(tokensOf(bodyRow)).toContain('group/row')
|
|
143
|
+
for (const cell of pinnedBodyCells) {
|
|
144
|
+
const tokens = tokensOf(cell)
|
|
145
|
+
expect(tokens).toEqual(
|
|
146
|
+
expect.arrayContaining([
|
|
147
|
+
'md:bg-card',
|
|
148
|
+
'md:group-hover/row:bg-linear-to-r',
|
|
149
|
+
'md:group-hover/row:from-muted/30',
|
|
150
|
+
'md:group-hover/row:to-muted/30',
|
|
151
|
+
]),
|
|
152
|
+
)
|
|
153
|
+
expect(tokens).not.toContain('md:bg-background')
|
|
154
|
+
}
|
|
155
|
+
} finally {
|
|
156
|
+
queryClient.clear()
|
|
157
|
+
}
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
it('keeps the page surface on pinned cells of an embedded table but still carries the tints', () => {
|
|
161
|
+
const { container, queryClient } = renderStickyTable({ embedded: true })
|
|
162
|
+
try {
|
|
163
|
+
const actionsHeader = Array.from(container.querySelectorAll('thead th')).pop() ?? null
|
|
164
|
+
const actionsBodyCell = container.querySelector('tbody tr td[data-actions-cell]')
|
|
165
|
+
|
|
166
|
+
expect(tokensOf(actionsHeader)).toEqual(
|
|
167
|
+
expect.arrayContaining(['md:bg-background', 'md:from-muted/40']),
|
|
168
|
+
)
|
|
169
|
+
expect(tokensOf(actionsBodyCell)).toEqual(
|
|
170
|
+
expect.arrayContaining(['md:bg-background', 'md:group-hover/row:from-muted/30']),
|
|
171
|
+
)
|
|
172
|
+
expect(tokensOf(actionsBodyCell)).not.toContain('md:bg-card')
|
|
173
|
+
} finally {
|
|
174
|
+
queryClient.clear()
|
|
175
|
+
}
|
|
176
|
+
})
|
|
118
177
|
})
|