Skip to content

[Feature]: Support RTL Layout via inverted Prop in <Gallery /> #99

@mr3od

Description

@mr3od

Summary

I’d like to propose adding support for RTL (right-to-left) layouts in the component by introducing an inverted prop. This would follow the familiar FlatList API and allow proper layout for languages such as Arabic.

I regularly use this package in apps the supports both LTR & RTL layouts, and the current gallery direction doesn't feel natural for those users especially in horizontal gallery.

Expected API

<Gallery inverted={true} ... />
  • The inverted prop would follow the same behavior as in FlatList.
  • When true, images render in reverse order, and all gesture directions adapt accordingly.

How could we implement it

Gallery.tsx

  • Android verticla scalling is different because of known issue Android OS . invesionStyle is then applied to the root Animated.View
const invesionStyle: StyleProp<ViewStyle> = inverted
  ? vertical
    ? styles.verticallyInverted
    : styles.horizontallyInverted
  : null;

const styles = StyleSheet.create({
  verticallyInverted:
    Platform.OS === 'android'
      ? { transform: [{ scale: -1 }] }
      : { transform: [{ scaleY: -1 }] },
  horizontallyInverted: {
    transform: [{ scaleX: -1 }],
  },
});

GalleryGestureHandler.tsx

1. In onSwipe direction handling:

const delta = (() => {
  switch (direction) {
    case 'up':
      return vertical ? (!inverted ? 1 : -1) : 0;
    case 'down':
      return vertical ? (!inverted ? -1 : 1) : 0;
    case 'left':
      return !vertical ? (!inverted ? 1 : -1) : 0;
    case 'right':
      return !vertical ? (!inverted ? -1 : 1) : 0;
    default:
      return 0;
  }
})();

2. In pan gesture .onUpdate and .onEnd:

.onUpdate((e) => {
  const toX = offset.x.value + e.translationX * (inverted ? -1 : 1);
  const toY = offset.y.value + e.translationY * (inverted ? -1 : 1);
})

.onEnd((e) => {
  const configX = { velocity: e.velocityX * (inverted ? -1 : 1), clamp: [-bounds.x, bounds.x] };
  const configY = { velocity: e.velocityY * (inverted ? -1 : 1), clamp: [-bounds.y, bounds.y] };
})

3. In tap gesture .onEnd edge tap handling:

.onEnd((event) => {
  let toIndex = activeIndex.value;
  const canGoToItem = tapOnEdgeToItem && !vertical;
  if (event.x <= leftEdge && canGoToItem) {
    toIndex += inverted ? 1 : -1;
  }
  if (event.x >= rightEdge && canGoToItem) {
    toIndex += inverted ? -1 : 1;
  }
})

GalleryItem.tsx

//transitionStyle
const scale = vertical
  ? inverted
    ? Platform.OS === 'android'
      ? { scale: -1 }
      : { scaleY: -1 }
    : { scale: 1 }
  : { scaleX: inverted ? -1 : 1 };

return {
  transform: [translate, scale],
  opacity,
};

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions