@fountain-ui/lab 2.0.0-beta.82 → 2.0.0-beta.84

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,15 +1,23 @@
1
- import React from 'react';
2
- import { ScrollView } from 'react-native';
3
- import { Column, Modal, Paper, StyleSheet, css, useTheme } from '@fountain-ui/core';
1
+ import React, { useLayoutEffect, useMemo, useState } from 'react';
2
+ import { LayoutChangeEvent, ScrollView, useWindowDimensions, View } from 'react-native';
3
+ import { Column, css, Modal, Paper, StyleSheet, useElevationStyle, useTheme } from '@fountain-ui/core';
4
4
  import { NamedStylesStringUnion, UseStyles } from '@fountain-ui/styles';
5
5
  import AnimatedY from '../AnimatedY';
6
6
  import type BottomSheetProps from './BottomSheetProps';
7
7
  import useDynamicSnapPoints from './useDynamicSnapPoints';
8
8
 
9
- type BottomSheetStyles = NamedStylesStringUnion<'root' | 'animated' | 'paper' | 'topElementLocation'>;
9
+ const createHeightLayoutHandler = (setHeight: React.Dispatch<React.SetStateAction<number>>) => {
10
+ return (event: LayoutChangeEvent) => {
11
+ const { height } = event.nativeEvent.layout;
12
+ setHeight(height);
13
+ };
14
+ }
15
+
16
+ type BottomSheetStyles = NamedStylesStringUnion<'root' | 'animated' | 'paper' | 'topElementLocation' | 'headerContainer' | 'headerBorder' | 'stickyBottomElement' | 'stickyBottomElementShadow'>;
10
17
 
11
18
  const useStyles: UseStyles<BottomSheetStyles> = function (): BottomSheetStyles {
12
19
  const theme = useTheme();
20
+ const stickyBottomElementShadow = useElevationStyle(8);
13
21
 
14
22
  return {
15
23
  root: {
@@ -34,26 +42,54 @@ const useStyles: UseStyles<BottomSheetStyles> = function (): BottomSheetStyles {
34
42
  bottom: 0,
35
43
  width: '100%',
36
44
  },
45
+ headerContainer: {
46
+ backgroundColor: theme.palette.paper.default,
47
+ },
48
+ headerBorder: {
49
+ borderBottomWidth: 0.5,
50
+ borderBottomColor: theme.palette.divider,
51
+ },
52
+ stickyBottomElement: {
53
+ position: 'absolute',
54
+ bottom: 0,
55
+ left: 0,
56
+ right: 0,
57
+ backgroundColor: theme.palette.paper.default,
58
+ },
59
+ stickyBottomElementShadow,
37
60
  };
38
61
  };
39
62
 
40
63
  export default function BottomSheet(props: BottomSheetProps) {
41
64
  const {
42
65
  backdropOpacity,
66
+ borderRadius,
43
67
  children,
44
68
  enableDynamicSizing = true,
45
69
  header,
70
+ stickyBottomElement,
46
71
  topElement,
47
72
  index,
48
73
  maxHeightNormalizedRatio = 0.9,
49
74
  onChange,
50
75
  snapPoints = [],
51
- disableDefaultBackgroundColor = false,
52
- disableDefaultShadow = false,
76
+ enableScrollableHeaderBorder = false,
53
77
  } = props;
54
78
 
55
79
  const styles = useStyles();
56
80
 
81
+ const { height: windowHeight } = useWindowDimensions();
82
+
83
+ const [topElementHeight, setTopElementHeight] = useState(0);
84
+ const [stickyBottomElementHeight, setStickyBottomElementHeight] = useState(0);
85
+ const [contentHeight, setContentHeight] = useState(0);
86
+ const [isScrollable, setIsScrollable] = useState(false);
87
+
88
+ const maxDynamicContentSize = Math.round(windowHeight * maxHeightNormalizedRatio) - topElementHeight;
89
+
90
+ const handleTopElementLayout = createHeightLayoutHandler(setTopElementHeight);
91
+ const handleStickyBottomElementLayout = createHeightLayoutHandler(setStickyBottomElementHeight);
92
+
57
93
  const handleClose = () => {
58
94
  if (onChange) {
59
95
  onChange(-1);
@@ -72,11 +108,37 @@ export default function BottomSheet(props: BottomSheetProps) {
72
108
 
73
109
  const translateY = highestSnapPoint - (convertedSnapPoints[index] ?? 0);
74
110
 
75
- const paperStyles = [
111
+ const adjustedContentHeight = useMemo(() => {
112
+ const adjustedHighestSnapPoint = highestSnapPoint + stickyBottomElementHeight;
113
+
114
+ return Math.min(maxDynamicContentSize, adjustedHighestSnapPoint);
115
+ }, [highestSnapPoint, stickyBottomElementHeight]);
116
+
117
+ const contentStyles = [
76
118
  styles.paper,
77
- { height: highestSnapPoint },
119
+ borderRadius ? { borderTopLeftRadius: borderRadius, borderTopRightRadius: borderRadius } : {},
120
+ { height: adjustedContentHeight, maxHeight: maxDynamicContentSize },
121
+ ];
122
+
123
+ const headerStyles = [
124
+ styles.headerContainer,
125
+ isScrollable && enableScrollableHeaderBorder ? styles.headerBorder : {},
126
+ ];
127
+
128
+ const stickyBottomElementStyles = [
129
+ styles.stickyBottomElement,
130
+ isScrollable ? styles.stickyBottomElementShadow : {},
78
131
  ];
79
132
 
133
+ useLayoutEffect(() => {
134
+ if (contentHeight === 0 || highestSnapPoint === 0) {
135
+ return;
136
+ }
137
+
138
+ const adjustedHighestSnapPoint = highestSnapPoint + stickyBottomElementHeight;
139
+ setIsScrollable(adjustedHighestSnapPoint > maxDynamicContentSize);
140
+ }, [contentHeight, highestSnapPoint, stickyBottomElementHeight, maxDynamicContentSize]);
141
+
80
142
  return (
81
143
  <Modal
82
144
  backdropOpacity={backdropOpacity}
@@ -90,25 +152,44 @@ export default function BottomSheet(props: BottomSheetProps) {
90
152
  >
91
153
  {topElement ? (
92
154
  <Column>
93
- <Column style={styles.topElementLocation}>
155
+ <Column
156
+ onLayout={handleTopElementLayout}
157
+ style={styles.topElementLocation}
158
+ >
94
159
  {topElement}
95
160
  </Column>
96
161
  </Column>
97
162
  ) : null}
98
163
 
99
164
  <Paper
100
- elevation={disableDefaultShadow ? 0 : 12}
101
- style={paperStyles}
102
- colorValue={disableDefaultBackgroundColor ? '#ffffff00' : undefined}
165
+ elevation={12}
166
+ style={contentStyles}
103
167
  >
104
168
  <ScrollView
105
- onContentSizeChange={handleContentSizeChange}
169
+ onContentSizeChange={(contentWidth: number, contentHeight: number) => {
170
+ setContentHeight(contentHeight);
171
+ handleContentSizeChange(contentWidth, contentHeight);
172
+ }}
106
173
  stickyHeaderIndices={header ? [0] : undefined}
174
+ style={{ paddingBottom: stickyBottomElementHeight }}
107
175
  >
108
- {header}
176
+ {header ? (
177
+ <View style={headerStyles}>
178
+ {header}
179
+ </View>
180
+ ): null}
109
181
 
110
182
  {children}
111
183
  </ScrollView>
184
+
185
+ {stickyBottomElement ? (
186
+ <Column
187
+ style={stickyBottomElementStyles}
188
+ onLayout={handleStickyBottomElementLayout}
189
+ >
190
+ {stickyBottomElement}
191
+ </Column>
192
+ ): null}
112
193
  </Paper>
113
194
  </AnimatedY>
114
195
  </Modal>