measureLayout instead of measureInWindow to get size relative to gallery bleed ancestor
This commit is contained in:
@@ -44,13 +44,16 @@ interface GalleryProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const Context = createContext<{
|
const Context = createContext<{
|
||||||
ref: React.RefObject<View | null>
|
bleedRef: React.RefObject<View | null>
|
||||||
|
bleedWidth: number
|
||||||
}>({
|
}>({
|
||||||
ref: {current: null},
|
bleedRef: {current: null},
|
||||||
|
bleedWidth: 0,
|
||||||
})
|
})
|
||||||
|
|
||||||
export function GalleryBleed({children}: {children: React.ReactNode}) {
|
export function GalleryBleed({children}: {children: React.ReactNode}) {
|
||||||
const ref = useRef<View>(null)
|
const ref = useRef<View>(null)
|
||||||
|
const [bleedWidth, setBleedWidth] = useState(0)
|
||||||
|
|
||||||
if (!isValidElement(children)) {
|
if (!isValidElement(children)) {
|
||||||
throw new Error('GalleryBleed children must be a single React element')
|
throw new Error('GalleryBleed children must be a single React element')
|
||||||
@@ -59,18 +62,19 @@ export function GalleryBleed({children}: {children: React.ReactNode}) {
|
|||||||
const node = children as React.ReactElement<any>
|
const node = children as React.ReactElement<any>
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Context.Provider value={{ref}}>
|
<Context.Provider value={{bleedRef: ref, bleedWidth}}>
|
||||||
{cloneElement(node, {
|
{cloneElement(node, {
|
||||||
ref: mergeRefs([ref, node?.props?.ref]),
|
ref: mergeRefs([ref, node?.props?.ref]),
|
||||||
|
onLayout: (e: {nativeEvent: {layout: {width: number}}}) => {
|
||||||
|
setBleedWidth(e.nativeEvent.layout.width)
|
||||||
|
},
|
||||||
})}
|
})}
|
||||||
</Context.Provider>
|
</Context.Provider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useGalleryBleedRef() {
|
export function useGalleryBleed() {
|
||||||
const {ref} = useContext(Context)
|
return useContext(Context)
|
||||||
// TODO throw?
|
|
||||||
return ref
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Gallery({
|
export function Gallery({
|
||||||
@@ -98,38 +102,31 @@ export function Gallery({
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Container overflow styles
|
* Container overflow styles
|
||||||
|
*
|
||||||
|
* Uses measureLayout to get the Gallery's offset relative to the GalleryBleed
|
||||||
|
* ancestor. This is a layout-relative measurement that doesn't depend on
|
||||||
|
* scroll position, so it works correctly for off-screen FlatList items.
|
||||||
*/
|
*/
|
||||||
const bleedRef = useGalleryBleedRef()
|
const {bleedRef, bleedWidth} = useGalleryBleed()
|
||||||
const [bleedDims, setBleedDims] = useState<{
|
|
||||||
left: number
|
|
||||||
right: number
|
|
||||||
width: number
|
|
||||||
}>()
|
|
||||||
const measureBleed = () => {
|
|
||||||
bleedRef?.current?.measureInWindow((x, _y, width) => {
|
|
||||||
setBleedDims({left: x, right: x + width, width})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
const contentRef = useRef<View>(null)
|
const contentRef = useRef<View>(null)
|
||||||
const [contentDims, setContentDims] = useState<{
|
const [insets, setInsets] = useState<{left: number; right: number}>()
|
||||||
left: number
|
const measure = () => {
|
||||||
right: number
|
if (contentRef.current && bleedRef.current && bleedWidth > 0) {
|
||||||
width: number
|
contentRef.current.measureLayout(
|
||||||
}>()
|
bleedRef.current,
|
||||||
const measureContent = () => {
|
(x, _y, w) => {
|
||||||
contentRef?.current?.measureInWindow((x, _y, width) => {
|
setInsets({
|
||||||
setContentDims({left: x, right: x + width, width})
|
left: x,
|
||||||
|
right: Math.max(0, bleedWidth - x - w),
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
() => {},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
const insetLeft =
|
}
|
||||||
bleedDims && contentDims
|
const insetLeft = insets?.left ?? 0
|
||||||
? Math.max(0, contentDims.left - bleedDims.left)
|
const insetRight = insets?.right ?? 0
|
||||||
: 999
|
const width = bleedWidth || Math.min(600, window.width)
|
||||||
const insetRight =
|
|
||||||
bleedDims && contentDims
|
|
||||||
? Math.max(0, bleedDims.right - contentDims.right)
|
|
||||||
: 999
|
|
||||||
const width = bleedDims ? bleedDims.width : Math.min(600, window.width)
|
|
||||||
/* End container overflow styles */
|
/* End container overflow styles */
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -142,10 +139,7 @@ export function Gallery({
|
|||||||
overflow: 'visible',
|
overflow: 'visible',
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
onLayout={() => {
|
onLayout={measure}>
|
||||||
measureBleed()
|
|
||||||
measureContent()
|
|
||||||
}}>
|
|
||||||
<BlockDrawerGesture>
|
<BlockDrawerGesture>
|
||||||
<FlatList
|
<FlatList
|
||||||
horizontal
|
horizontal
|
||||||
@@ -165,7 +159,7 @@ export function Gallery({
|
|||||||
height: contentHeight,
|
height: contentHeight,
|
||||||
marginLeft: -insetLeft,
|
marginLeft: -insetLeft,
|
||||||
width,
|
width,
|
||||||
}, a.debug]}
|
}]}
|
||||||
contentContainerStyle={{
|
contentContainerStyle={{
|
||||||
gap: ITEM_GAP,
|
gap: ITEM_GAP,
|
||||||
paddingLeft: insetLeft,
|
paddingLeft: insetLeft,
|
||||||
|
|||||||
Reference in New Issue
Block a user