Skip to content
This repository was archived by the owner on Dec 16, 2022. It is now read-only.

Commit 5978ba8

Browse files
authored
[WINDOW]: support for pinned right columns. (#160)
1 parent 823c2c3 commit 5978ba8

File tree

9 files changed

+187
-45
lines changed

9 files changed

+187
-45
lines changed

.changeset/gentle-shirts-push.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@resembli/react-virtualized-window": patch
3+
---
4+
5+
Added support for pinned right columns and RTL warnings for IOS devices

packages/react-virtualized-window/src/PinnedColumn.tsx

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type { GridProps } from "./types"
66
interface PinnedColumnsProps<T> {
77
totalWidth: number
88
topOffset: number
9+
position?: "sticky" | "absolute"
910
left?: number
1011
right?: number
1112
rtl?: boolean
@@ -15,6 +16,7 @@ interface PinnedColumnsProps<T> {
1516
runningHeight: number
1617
vertStart: number
1718
vertEnd: number
19+
pinnedRight?: boolean
1820
verticalGap: number
1921
horizontalGap: number
2022
Component: GridProps<T>["children"]
@@ -33,26 +35,28 @@ export function PinnedColumn<T>({
3335
Component,
3436
vertStart,
3537
vertEnd,
38+
pinnedRight,
3639
verticalGap,
3740
horizontalGap,
3841
}: PinnedColumnsProps<T>) {
3942
return (
4043
<div
4144
style={{
4245
width: totalWidth,
43-
position: "sticky",
46+
position: "absolute",
4447
left,
4548
right,
4649
transform: `translate3d(0px, ${topOffset}px, 0px)`,
47-
height: innerHeight,
48-
display: "flex",
50+
display: "inline-flex",
4951
}}
5052
>
5153
{columns.map((pinnedColumn, colIndex) => {
5254
const columnWidth = widths[colIndex]
5355

54-
const marginLeft = !rtl && colIndex !== 0 ? horizontalGap : 0
55-
const marginRight = rtl && colIndex !== 0 ? horizontalGap : 0
56+
const marginLeft =
57+
pinnedRight && !rtl ? horizontalGap : !rtl && colIndex !== 0 ? horizontalGap : 0
58+
const marginRight =
59+
pinnedRight && rtl ? horizontalGap : rtl && colIndex !== 0 ? horizontalGap : 0
5660

5761
return (
5862
<div key={colIndex}>
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import * as React from "react"
2+
import type { PropsWithChildren } from "react"
3+
4+
interface ScrollDivProps {
5+
rtl?: boolean
6+
disableSticky?: boolean
7+
leftOffset: number
8+
topOffset: number
9+
pinnedLeftWidth: number
10+
pinnedRightWidth: number
11+
}
12+
13+
export function ScrollDiv({
14+
rtl,
15+
disableSticky,
16+
leftOffset,
17+
topOffset,
18+
pinnedLeftWidth,
19+
children,
20+
}: PropsWithChildren<ScrollDivProps>) {
21+
if (rtl) {
22+
return (
23+
<div
24+
style={{
25+
position: "absolute",
26+
transform: `translate3d(${disableSticky ? 0 : leftOffset}px, ${
27+
disableSticky ? 0 : -topOffset
28+
}px, 0px)`,
29+
top: 0,
30+
right: pinnedLeftWidth,
31+
willChange: "transform",
32+
}}
33+
>
34+
{children}
35+
</div>
36+
)
37+
}
38+
39+
return (
40+
<div
41+
style={{
42+
position: "absolute",
43+
transform: `translate3d(${disableSticky ? 0 : -leftOffset}px, ${
44+
disableSticky ? 0 : -topOffset
45+
}px, 0px)`,
46+
top: 0,
47+
left: pinnedLeftWidth,
48+
willChange: "transform",
49+
}}
50+
>
51+
{children}
52+
</div>
53+
)
54+
}

packages/react-virtualized-window/src/components/Grid.tsx

Lines changed: 76 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,22 @@
11
import * as React from "react"
22

33
import { PinnedColumn } from "../PinnedColumn"
4+
import { ScrollDiv } from "../ScrollDiv"
45
import { SizingDiv } from "../SizingDiv"
56
import { StickyDiv } from "../StickyDiv"
67
import { getHorizontalGap, getVerticalGap } from "../itemGapUtilities"
78
import type { GridProps } from "../types"
89
import { useDataDimension } from "../useDataDimension"
910
import { useIndicesForDimensions } from "../useDimensionIndices"
11+
import { useRTLWarnings } from "../useRtlWarnings"
1012
import { useScrollAdjustWindowDims } from "../useScrollAdjustedDim"
1113
import { useScrollItems } from "../useScrollItems"
1214
import { useSmartSticky } from "../useSmartSticky"
1315
import { useWindowApi } from "../useWindowApi"
1416
import { useWindowDimensions } from "../useWindowDimensions"
1517
import { useWindowScroll } from "../useWindowScroll"
1618

17-
export function Grid<T>({
19+
export function Grid<T, L = unknown, R = unknown>({
1820
data,
1921
children,
2022
defaultRowHeight,
@@ -41,10 +43,11 @@ export function Grid<T>({
4143
onScroll: userOnScroll,
4244

4345
pinnedLeft,
46+
pinnedRight,
4447
leftWidths,
45-
}: GridProps<T>) {
48+
rightWidths,
49+
}: GridProps<T, L, R>) {
4650
const windowRef = React.useRef<HTMLDivElement>(null)
47-
const transRef = React.useRef<HTMLDivElement>(null)
4851
useWindowApi(windowRef, apiRef)
4952

5053
const verticalGap = getVerticalGap(gap)
@@ -53,10 +56,10 @@ export function Grid<T>({
5356
const [width, height, browserWidth] = useWindowDimensions(windowRef)
5457
const [overscan, disableSticky] = useSmartSticky(browserWidth, userOverscan, userDisableSticky)
5558

59+
useRTLWarnings({ rtl, disableSticky, pinnedLeft, pinnedRight })
60+
5661
const [topOffset, leftOffset, onScroll] = useWindowScroll({
5762
userOnScroll,
58-
transRef,
59-
disableSticky: disableSticky ?? false,
6063
rtl: rtl ?? false,
6164
})
6265

@@ -94,15 +97,15 @@ export function Grid<T>({
9497
offset: topOffset,
9598
gapBetweenItems: verticalGap,
9699
windowDimension: height,
97-
overscan: overscan ?? 0,
100+
overscan: overscan ?? 1,
98101
})
99102

100103
const [horiStart, horiEnd, runningWidth] = useIndicesForDimensions({
101104
windowDimension: width,
102105
offset: leftOffset,
103106
gapBetweenItems: horizontalGap,
104107
itemDimensions: dataWidths,
105-
overscan: overscan ?? 0,
108+
overscan: overscan ?? 1,
106109
})
107110

108111
const [lWidths, leftTotalWidth] = useDataDimension({
@@ -113,6 +116,14 @@ export function Grid<T>({
113116
dimensions: leftWidths,
114117
})
115118

119+
const [rWidths, rightTotalWidth] = useDataDimension({
120+
count: pinnedRight?.length ?? 0,
121+
defaultDimension: defaultColumnWidth,
122+
windowDim: adjustedWidth,
123+
gap: horizontalGap,
124+
dimensions: rightWidths,
125+
})
126+
116127
const scrollableItems = useScrollItems({
117128
children,
118129
data,
@@ -151,46 +162,75 @@ export function Grid<T>({
151162
direction: rtl ? "rtl" : "ltr",
152163
}}
153164
>
154-
<div style={{ width: innerWidth + leftTotalWidth - horizontalGap, height: innerHeight }}>
165+
<div
166+
style={{
167+
width: innerWidth + leftTotalWidth - horizontalGap + rightTotalWidth,
168+
height: innerHeight,
169+
}}
170+
>
155171
<StickyDiv
156172
disabled={disableSticky ?? false}
157173
rtl={rtl ?? false}
158174
height={adjustedHeight}
159175
width={adjustedWidth}
160176
>
177+
<ScrollDiv
178+
rtl={rtl}
179+
disableSticky={disableSticky}
180+
topOffset={topOffset}
181+
leftOffset={leftOffset}
182+
pinnedLeftWidth={leftTotalWidth}
183+
pinnedRightWidth={200}
184+
>
185+
{scrollableItems}
186+
</ScrollDiv>
161187
<div
162-
ref={transRef}
163188
style={{
164-
position: "absolute",
165-
transform: `translate3d(${disableSticky ? 0 : rtl ? leftOffset : -leftOffset}px, ${
166-
disableSticky ? 0 : -topOffset
167-
}px, 0px)`,
168-
top: 0,
169-
left: rtl ? undefined : leftTotalWidth,
170-
right: rtl ? leftTotalWidth : undefined,
171-
willChange: "transform",
189+
display: "flex",
190+
width: adjustedWidth,
191+
position: "sticky",
192+
left: rtl ? undefined : 0,
193+
right: rtl ? 0 : undefined,
172194
}}
173195
>
174-
{scrollableItems}
196+
{pinnedLeft && (
197+
<PinnedColumn
198+
Component={children}
199+
totalWidth={leftTotalWidth}
200+
left={rtl ? undefined : 0}
201+
right={rtl ? 0 : undefined}
202+
topOffset={disableSticky ? 0 : -topOffset}
203+
columns={pinnedLeft}
204+
widths={lWidths}
205+
heights={dataHeights}
206+
vertStart={vertStart}
207+
vertEnd={vertEnd}
208+
verticalGap={verticalGap}
209+
horizontalGap={horizontalGap}
210+
runningHeight={runningHeight}
211+
rtl={rtl}
212+
/>
213+
)}
214+
{pinnedRight && (
215+
<PinnedColumn
216+
Component={children}
217+
totalWidth={rightTotalWidth}
218+
left={rtl ? 0 : undefined}
219+
right={rtl ? undefined : 0}
220+
topOffset={disableSticky ? 0 : -topOffset}
221+
columns={pinnedRight}
222+
widths={rWidths}
223+
heights={dataHeights}
224+
vertStart={vertStart}
225+
vertEnd={vertEnd}
226+
verticalGap={verticalGap}
227+
horizontalGap={horizontalGap}
228+
pinnedRight
229+
runningHeight={runningHeight}
230+
rtl={rtl}
231+
/>
232+
)}
175233
</div>
176-
{pinnedLeft && (
177-
<PinnedColumn
178-
Component={children}
179-
totalWidth={leftTotalWidth}
180-
left={rtl ? undefined : 0}
181-
right={rtl ? 0 : undefined}
182-
topOffset={disableSticky ? 0 : -topOffset}
183-
columns={pinnedLeft}
184-
widths={lWidths}
185-
heights={dataHeights}
186-
vertStart={vertStart}
187-
vertEnd={vertEnd}
188-
verticalGap={verticalGap}
189-
horizontalGap={horizontalGap}
190-
runningHeight={runningHeight}
191-
rtl={rtl}
192-
/>
193-
)}
194234
</StickyDiv>
195235
</div>
196236
</div>

packages/react-virtualized-window/src/types.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export interface CellMeta {
3131
pinned?: "left" | "right"
3232
}
3333

34-
export interface GridProps<T, L = unknown> extends VirtualWindowBaseProps<T> {
34+
export interface GridProps<T, L = unknown, R = unknown> extends VirtualWindowBaseProps<T> {
3535
data: T[][]
3636
children: <B extends T>(props: {
3737
data: B
@@ -45,6 +45,9 @@ export interface GridProps<T, L = unknown> extends VirtualWindowBaseProps<T> {
4545

4646
pinnedLeft?: L[][]
4747
leftWidths?: NumberOrPercent[]
48+
49+
pinnedRight?: R[][]
50+
rightWidths?: NumberOrPercent[]
4851
}
4952

5053
export type RenderItem<T> = GridProps<T>["children"]
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import * as React from "react"
2+
3+
interface UseRTLWarningsArgs {
4+
rtl?: boolean
5+
disableSticky?: boolean
6+
pinnedLeft?: unknown
7+
pinnedRight?: unknown
8+
}
9+
10+
export function useRTLWarnings({
11+
rtl,
12+
disableSticky,
13+
pinnedLeft,
14+
pinnedRight,
15+
}: UseRTLWarningsArgs) {
16+
React.useEffect(() => {
17+
if (!rtl) return
18+
19+
if (!disableSticky)
20+
console.warn(
21+
"[react-virtualized-window]: RTL with stickEnabled does not work on IOS touch devices. Consider setting disableSticky to false",
22+
)
23+
24+
if (pinnedLeft || pinnedRight)
25+
console.warn(
26+
"[react-virtualized-window]: RLT with pinned columns does not work on IOS touch devices. Consider removing the pinned columns",
27+
)
28+
}, [disableSticky, pinnedLeft, pinnedRight, rtl])
29+
}

packages/react-virtualized-window/src/useWindowScroll.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ import * as React from "react"
22

33
interface UseWindowScrollArgs {
44
rtl: boolean
5-
transRef: React.RefObject<HTMLDivElement>
6-
disableSticky?: boolean
75
userOnScroll?: React.UIEventHandler<HTMLElement>
86
}
97

playgrounds/src/App.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,12 @@ const pinnedLeft = [
4141
Array.from({ length: 1000 }, (_, row) => [row, -1]),
4242
Array.from({ length: 1000 }, (_, row) => [row, -2]),
4343
Array.from({ length: 1000 }, (_, row) => [row, -3]),
44-
Array.from({ length: 1000 }, (_, row) => [row, -3]),
44+
]
45+
46+
const pinnedRight = [
47+
Array.from({ length: 1000 }, (_, row) => [row, "R1"]),
48+
Array.from({ length: 1000 }, (_, row) => [row, "R2"]),
49+
Array.from({ length: 1000 }, (_, row) => [row, "R2"]),
4550
]
4651

4752
function App() {
@@ -72,6 +77,7 @@ function App() {
7277
height="70%"
7378
rtl={rtl}
7479
disableSticky={disableSticky}
80+
pinnedRight={pinnedRight}
7581
pinnedLeft={pinnedLeft}
7682
>
7783
{({ data, style, cellMeta }) => (

playgrounds/vite.config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,7 @@ import { defineConfig } from "vite"
44
// https://vitejs.dev/config/
55
export default defineConfig({
66
plugins: [react()],
7+
server: {
8+
host: true,
9+
},
710
})

0 commit comments

Comments
 (0)