about summary refs log tree commit diff
path: root/src/view/com/util/Views.web.tsx
blob: 58a367f20b682e1e12c17e84d1c57989a1a599b9 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
/**
 * In the Web build, we center all content so that it mirrors the
 * mobile experience (a single narrow column). We then place a UI
 * shell around the content if you're in desktop.
 *
 * Because scrolling is handled by components deep in the hierarchy,
 * we can't just wrap the top-level element with a max width. The
 * centering has to be done at the ScrollView.
 *
 * These components wrap the RN ScrollView-based components to provide
 * consistent layout. It also provides <CenteredView> for views that
 * need to match layout but which aren't scrolled.
 */

import React from 'react'
import {
  FlatList as RNFlatList,
  FlatListProps,
  ScrollView as RNScrollView,
  ScrollViewProps,
  StyleSheet,
  View,
  ViewProps,
} from 'react-native'
import {addStyle} from 'lib/styles'
import {usePalette} from 'lib/hooks/usePalette'
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'

interface AddedProps {
  desktopFixedHeight?: boolean
}

export function CenteredView({
  style,
  ...props
}: React.PropsWithChildren<ViewProps>) {
  style = addStyle(style, styles.container)
  return <View style={style} {...props} />
}

export const FlatList = React.forwardRef(function <ItemT>(
  {
    contentContainerStyle,
    style,
    contentOffset,
    desktopFixedHeight,
    ...props
  }: React.PropsWithChildren<FlatListProps<ItemT> & AddedProps>,
  ref: React.Ref<RNFlatList>,
) {
  const pal = usePalette('default')
  const {isMobile} = useWebMediaQueries()
  contentContainerStyle = addStyle(
    contentContainerStyle,
    styles.containerScroll,
  )
  if (contentOffset && contentOffset?.y !== 0) {
    // NOTE
    // we use paddingTop & contentOffset to space around the floating header
    // but reactnative web puts the paddingTop on the wrong element (style instead of the contentContainer)
    // so we manually correct it here
    // -prf
    style = addStyle(style, {
      paddingTop: 0,
    })
    contentContainerStyle = addStyle(contentContainerStyle, {
      paddingTop: Math.abs(contentOffset.y),
    })
  }
  if (desktopFixedHeight) {
    style = addStyle(style, styles.fixedHeight)
    if (!isMobile) {
      contentContainerStyle = addStyle(
        contentContainerStyle,
        styles.stableGutters,
      )
    }
  }
  return (
    <RNFlatList
      ref={ref}
      contentContainerStyle={[
        contentContainerStyle,
        pal.border,
        styles.contentContainer,
      ]}
      style={style}
      contentOffset={contentOffset}
      {...props}
    />
  )
})

export const ScrollView = React.forwardRef(function (
  {contentContainerStyle, ...props}: React.PropsWithChildren<ScrollViewProps>,
  ref: React.Ref<RNScrollView>,
) {
  const pal = usePalette('default')

  contentContainerStyle = addStyle(
    contentContainerStyle,
    styles.containerScroll,
  )
  return (
    <RNScrollView
      contentContainerStyle={[
        contentContainerStyle,
        pal.border,
        styles.contentContainer,
      ]}
      ref={ref}
      {...props}
    />
  )
})

const styles = StyleSheet.create({
  contentContainer: {
    borderLeftWidth: 1,
    borderRightWidth: 1,
    minHeight: '100vh',
  },
  container: {
    width: '100%',
    maxWidth: 600,
    marginLeft: 'auto',
    marginRight: 'auto',
  },
  containerScroll: {
    width: '100%',
    maxWidth: 600,
    marginLeft: 'auto',
    marginRight: 'auto',
  },
  fixedHeight: {
    height: '100vh',
  },
  stableGutters: {
    // @ts-ignore web only -prf
    scrollbarGutter: 'stable both-edges',
  },
})