fix web bleed and 3/2 ratio
This commit is contained in:
@@ -19,7 +19,7 @@ import {PostEmbedViewContext} from '#/components/Post/Embed/types'
|
|||||||
import {Text} from '#/components/Typography'
|
import {Text} from '#/components/Typography'
|
||||||
import {useAnalytics} from '#/analytics'
|
import {useAnalytics} from '#/analytics'
|
||||||
|
|
||||||
const CONTAINER_ASPECT_RATIO = 4 / 3
|
const CONTAINER_ASPECT_RATIO = 3 / 2
|
||||||
const ITEM_GAP = 8 // tokens.space.sm
|
const ITEM_GAP = 8 // tokens.space.sm
|
||||||
|
|
||||||
interface GalleryProps {
|
interface GalleryProps {
|
||||||
@@ -83,11 +83,11 @@ export function Gallery({
|
|||||||
if (ar && ar.width > 0 && ar.height > 0) {
|
if (ar && ar.width > 0 && ar.height > 0) {
|
||||||
const ratio = ar.width / ar.height
|
const ratio = ar.width / ar.height
|
||||||
// Width derived from image's own aspect ratio at the fixed container height
|
// Width derived from image's own aspect ratio at the fixed container height
|
||||||
const w = containerHeight * ratio
|
// Clamp aspect ratio between 2:3 (portrait) and 3:2 (landscape)
|
||||||
// Clamp: at least 40% of content width, at most the full content width
|
const clamped = Math.max(2 / 3, Math.min(ratio, 3 / 2))
|
||||||
return Math.max(containerWidth * 0.4, Math.min(w, containerWidth))
|
return containerHeight * clamped
|
||||||
}
|
}
|
||||||
return containerWidth
|
return containerHeight // default to square-ish
|
||||||
}
|
}
|
||||||
|
|
||||||
if (screenReaderEnabled) {
|
if (screenReaderEnabled) {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import {PostEmbedViewContext} from '#/components/Post/Embed/types'
|
|||||||
import {Text} from '#/components/Typography'
|
import {Text} from '#/components/Typography'
|
||||||
import {useAnalytics} from '#/analytics'
|
import {useAnalytics} from '#/analytics'
|
||||||
|
|
||||||
const CONTAINER_ASPECT_RATIO = 4 / 3
|
const CONTAINER_ASPECT_RATIO = 3 / 2
|
||||||
const ITEM_GAP = 8 // tokens.space.sm
|
const ITEM_GAP = 8 // tokens.space.sm
|
||||||
|
|
||||||
interface GalleryProps {
|
interface GalleryProps {
|
||||||
@@ -41,7 +41,10 @@ export function Gallery({
|
|||||||
const largeAltBadge = useLargeAltBadgeEnabled()
|
const largeAltBadge = useLargeAltBadgeEnabled()
|
||||||
const currentPageRef = useRef(0)
|
const currentPageRef = useRef(0)
|
||||||
const scrollRef = useRef<ScrollView>(null)
|
const scrollRef = useRef<ScrollView>(null)
|
||||||
|
const containerRef = useRef<View>(null)
|
||||||
const [containerWidth, setContainerWidth] = useState(0)
|
const [containerWidth, setContainerWidth] = useState(0)
|
||||||
|
const [insetLeft, setInsetLeft] = useState(0)
|
||||||
|
const [insetRight, setInsetRight] = useState(0)
|
||||||
|
|
||||||
const containerRefs = useRef<AnimatedRef<any>[]>([]).current
|
const containerRefs = useRef<AnimatedRef<any>[]>([]).current
|
||||||
const thumbDimsRef = useRef<(Dimensions | null)[]>([])
|
const thumbDimsRef = useRef<(Dimensions | null)[]>([])
|
||||||
@@ -59,17 +62,24 @@ export function Gallery({
|
|||||||
viewContext === PostEmbedViewContext.FeedEmbedRecordWithMedia
|
viewContext === PostEmbedViewContext.FeedEmbedRecordWithMedia
|
||||||
const hideBadges = isWithinQuote
|
const hideBadges = isWithinQuote
|
||||||
|
|
||||||
|
const QUOTE_PADDING = 12
|
||||||
const containerHeight =
|
const containerHeight =
|
||||||
containerWidth > 0 ? containerWidth / CONTAINER_ASPECT_RATIO : 0
|
containerWidth > 0 ? containerWidth / CONTAINER_ASPECT_RATIO : 0
|
||||||
|
const scrollWidth = isWithinQuote
|
||||||
|
? containerWidth + QUOTE_PADDING * 2
|
||||||
|
: insetLeft + insetRight > 0
|
||||||
|
? containerWidth + insetLeft + insetRight
|
||||||
|
: containerWidth
|
||||||
|
|
||||||
const getItemWidth = (image: AppBskyEmbedImages.ViewImage) => {
|
const getItemWidth = (image: AppBskyEmbedImages.ViewImage) => {
|
||||||
const ar = image.aspectRatio
|
const ar = image.aspectRatio
|
||||||
if (ar && ar.width > 0 && ar.height > 0) {
|
if (ar && ar.width > 0 && ar.height > 0) {
|
||||||
const ratio = ar.width / ar.height
|
const ratio = ar.width / ar.height
|
||||||
const w = containerHeight * ratio
|
// Clamp aspect ratio between 2:3 (portrait) and 3:2 (landscape)
|
||||||
return Math.max(containerWidth * 0.4, Math.min(w, containerWidth))
|
const clamped = Math.max(2 / 3, Math.min(ratio, 3 / 2))
|
||||||
|
return containerHeight * clamped
|
||||||
}
|
}
|
||||||
return containerWidth
|
return containerHeight // default to square-ish
|
||||||
}
|
}
|
||||||
|
|
||||||
// Click-and-drag scrolling via DOM listeners
|
// Click-and-drag scrolling via DOM listeners
|
||||||
@@ -155,14 +165,37 @@ export function Gallery({
|
|||||||
<View
|
<View
|
||||||
style={
|
style={
|
||||||
containerWidth > 0
|
containerWidth > 0
|
||||||
? {height: containerHeight}
|
? {height: containerHeight, overflow: 'visible'}
|
||||||
: {aspectRatio: CONTAINER_ASPECT_RATIO}
|
: {aspectRatio: CONTAINER_ASPECT_RATIO}
|
||||||
}
|
}
|
||||||
|
ref={containerRef}
|
||||||
onLayout={e => {
|
onLayout={e => {
|
||||||
const w = e.nativeEvent.layout.width
|
const w = e.nativeEvent.layout.width
|
||||||
if (w > 0) {
|
if (w > 0) {
|
||||||
setContainerWidth(w)
|
setContainerWidth(w)
|
||||||
}
|
}
|
||||||
|
// Measure distance to post edges for bleed
|
||||||
|
if (!isWithinQuote) {
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
const el = containerRef.current as unknown as HTMLElement
|
||||||
|
if (!el) return
|
||||||
|
const galleryRect = el.getBoundingClientRect()
|
||||||
|
// Walk up to find the post outer container (has paddingLeft/paddingRight)
|
||||||
|
let parent: HTMLElement | null = el.parentElement
|
||||||
|
while (parent) {
|
||||||
|
const ps = window.getComputedStyle(parent)
|
||||||
|
const pl = parseFloat(ps.paddingLeft)
|
||||||
|
const pr = parseFloat(ps.paddingRight)
|
||||||
|
if (pl >= 8 && pr >= 8 && ps.cursor === 'pointer') {
|
||||||
|
const parentRect = parent.getBoundingClientRect()
|
||||||
|
setInsetLeft(galleryRect.left - parentRect.left)
|
||||||
|
setInsetRight(parentRect.right - galleryRect.right)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
parent = parent.parentElement
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
role="group"
|
role="group"
|
||||||
aria-roledescription="carousel"
|
aria-roledescription="carousel"
|
||||||
@@ -176,6 +209,12 @@ export function Gallery({
|
|||||||
style={[
|
style={[
|
||||||
{
|
{
|
||||||
height: containerHeight,
|
height: containerHeight,
|
||||||
|
width: scrollWidth,
|
||||||
|
marginLeft: isWithinQuote
|
||||||
|
? -QUOTE_PADDING
|
||||||
|
: insetLeft > 0
|
||||||
|
? -insetLeft
|
||||||
|
: 0,
|
||||||
},
|
},
|
||||||
web({
|
web({
|
||||||
WebkitOverflowScrolling: 'touch',
|
WebkitOverflowScrolling: 'touch',
|
||||||
@@ -183,6 +222,16 @@ export function Gallery({
|
|||||||
]}
|
]}
|
||||||
contentContainerStyle={{
|
contentContainerStyle={{
|
||||||
gap: ITEM_GAP,
|
gap: ITEM_GAP,
|
||||||
|
paddingLeft: isWithinQuote
|
||||||
|
? QUOTE_PADDING
|
||||||
|
: insetLeft > 0
|
||||||
|
? insetLeft
|
||||||
|
: 0,
|
||||||
|
paddingRight: isWithinQuote
|
||||||
|
? QUOTE_PADDING
|
||||||
|
: insetRight > 0
|
||||||
|
? insetRight
|
||||||
|
: 0,
|
||||||
}}
|
}}
|
||||||
onScroll={e => {
|
onScroll={e => {
|
||||||
const offsetX = e.nativeEvent.contentOffset.x
|
const offsetX = e.nativeEvent.contentOffset.x
|
||||||
|
|||||||
Reference in New Issue
Block a user