react-native-terra-ui 0.4.1 → 0.5.1

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.
Files changed (53) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/lib/module/components/header/parts/HeaderGradientOverlay.js +1 -1
  3. package/lib/module/components/header/parts/HeaderGradientOverlay.js.map +1 -1
  4. package/lib/module/components/image/Image.js +5 -0
  5. package/lib/module/components/image/Image.js.map +1 -1
  6. package/lib/module/components/screen/Screen.js +1 -4
  7. package/lib/module/components/screen/Screen.js.map +1 -1
  8. package/lib/module/components/screen/ScreenFlatList.js +12 -0
  9. package/lib/module/components/screen/ScreenFlatList.js.map +1 -1
  10. package/lib/module/components/screen/ScreenList.js +2 -2
  11. package/lib/module/components/screen/ScreenList.js.map +1 -1
  12. package/lib/module/components/screen/index.js +0 -1
  13. package/lib/module/components/screen/index.js.map +1 -1
  14. package/lib/module/components/toast/Toast.js +2 -2
  15. package/lib/module/components/toolbar/Toolbar.js +3 -1
  16. package/lib/module/components/toolbar/Toolbar.js.map +1 -1
  17. package/lib/module/theme/tokens/primitives.js +8 -3
  18. package/lib/module/theme/tokens/primitives.js.map +1 -1
  19. package/lib/typescript/src/components/image/Image.d.ts +1 -1
  20. package/lib/typescript/src/components/image/Image.d.ts.map +1 -1
  21. package/lib/typescript/src/components/screen/Screen.d.ts +1 -2
  22. package/lib/typescript/src/components/screen/Screen.d.ts.map +1 -1
  23. package/lib/typescript/src/components/screen/ScreenContext.d.ts +2 -2
  24. package/lib/typescript/src/components/screen/ScreenFlatList.d.ts +11 -0
  25. package/lib/typescript/src/components/screen/ScreenFlatList.d.ts.map +1 -1
  26. package/lib/typescript/src/components/screen/ScreenList.d.ts +3 -2
  27. package/lib/typescript/src/components/screen/ScreenList.d.ts.map +1 -1
  28. package/lib/typescript/src/components/screen/index.d.ts +0 -2
  29. package/lib/typescript/src/components/screen/index.d.ts.map +1 -1
  30. package/lib/typescript/src/components/screen/types.d.ts +2 -3
  31. package/lib/typescript/src/components/screen/types.d.ts.map +1 -1
  32. package/lib/typescript/src/theme/tokens/primitives.d.ts +5 -0
  33. package/lib/typescript/src/theme/tokens/primitives.d.ts.map +1 -1
  34. package/lib/typescript/src/theme/types.d.ts +16 -1
  35. package/lib/typescript/src/theme/types.d.ts.map +1 -1
  36. package/package.json +2 -4
  37. package/src/components/header/parts/HeaderGradientOverlay.tsx +1 -1
  38. package/src/components/image/Image.tsx +10 -1
  39. package/src/components/screen/Screen.tsx +2 -5
  40. package/src/components/screen/ScreenContext.tsx +2 -2
  41. package/src/components/screen/ScreenFlatList.tsx +11 -0
  42. package/src/components/screen/ScreenList.tsx +5 -4
  43. package/src/components/screen/index.ts +0 -2
  44. package/src/components/screen/types.ts +2 -3
  45. package/src/components/toast/Toast.tsx +2 -2
  46. package/src/components/toolbar/Toolbar.tsx +2 -0
  47. package/src/theme/tokens/primitives.ts +9 -3
  48. package/src/theme/types.ts +16 -1
  49. package/lib/module/components/screen/ScreenFlashList.js +0 -166
  50. package/lib/module/components/screen/ScreenFlashList.js.map +0 -1
  51. package/lib/typescript/src/components/screen/ScreenFlashList.d.ts +0 -43
  52. package/lib/typescript/src/components/screen/ScreenFlashList.d.ts.map +0 -1
  53. package/src/components/screen/ScreenFlashList.tsx +0 -216
@@ -1,216 +0,0 @@
1
- import { FlashList, type FlashListProps } from '@shopify/flash-list';
2
- import type { ComponentType } from 'react';
3
- import {
4
- type NativeScrollEvent,
5
- type NativeSyntheticEvent,
6
- View,
7
- } from 'react-native';
8
- import Animated, {
9
- useAnimatedScrollHandler,
10
- useComposedEventHandler,
11
- } from 'react-native-reanimated';
12
-
13
- import { StyleSheet } from 'react-native-unistyles';
14
-
15
- import { resolveScreenMargin } from '#theme/screen-margin';
16
- import { PortalHost } from '../portal';
17
- import { BottomSafeArea } from './parts/BottomSafeArea';
18
- import { renderScreenPlaceholder } from './parts/ScreenPlaceholder';
19
- import { SCREEN_HEADER_PORTAL_HOST, useScreen } from './ScreenContext';
20
- import type { ScreenMargins, ScreenScrollableProps } from './types';
21
- import { hasXMargin, hasYMargin, renderListHeader } from './utils';
22
-
23
- const AnimatedFlashList = Animated.createAnimatedComponent(
24
- FlashList as unknown as ComponentType<FlashListProps<unknown>>
25
- );
26
-
27
- export interface ScreenFlashListProps<T>
28
- extends Omit<FlashListProps<T>, 'horizontal'>,
29
- ScreenScrollableProps {
30
- /**
31
- * Extra bottom padding on the list content — clearance for a tab bar or home
32
- * indicator. Defaults to the `layout.screen.margin.y` token when margins are
33
- * enabled, otherwise `0`.
34
- */
35
- bottomInset?: number;
36
- }
37
-
38
- /**
39
- * `FlashList` variant of `Screen.ScrollView`: mirrors scroll offset into the
40
- * screen's shared `scrollY`, hosts the portal target above the list, and
41
- * applies token-driven content padding.
42
- *
43
- * Requires `@shopify/flash-list` — it is a peer dependency of
44
- * `react-native-terra-ui` and must be installed in the consuming app.
45
- *
46
- * Note: unlike `Screen.FlatList`, this does not bind the screen's shared
47
- * imperative `scrollRef` — `FlashList`'s ref exposes a different (non
48
- * Reanimated-compatible) imperative API.
49
- *
50
- * Shares `loading` / `LoadingComponent` / `EmptyComponent` / `margins` /
51
- * `horizontal` with `Screen.ScrollView`, so a screen can swap one container for
52
- * another without relearning its API.
53
- *
54
- * @example
55
- * ```tsx
56
- * <Screen.FlashList data={items} renderItem={({ item }) => <Row item={item} />} />
57
- * ```
58
- *
59
- * @example Loading and empty placeholders
60
- * ```tsx
61
- * <Screen.FlashList
62
- * data={items}
63
- * loading={isLoading}
64
- * EmptyComponent={<EmptyState icon="status.info" title="Nothing yet" fill />}
65
- * renderItem={({ item }) => <Row item={item} />}
66
- * />
67
- * ```
68
- */
69
- export function ScreenFlashList<T>({
70
- data,
71
- style,
72
- contentContainerStyle,
73
- ListHeaderComponent,
74
- ListFooterComponent,
75
- ListEmptyComponent,
76
- horizontal = false,
77
- loading = false,
78
- LoadingComponent,
79
- EmptyComponent,
80
- margins,
81
- bottomInset,
82
- onScroll,
83
- ...rest
84
- }: ScreenFlashListProps<T>) {
85
- if (FlashList == null) {
86
- throw new Error(
87
- '[react-native-terra-ui] Screen.FlashList requires "@shopify/flash-list" to be installed in your app. Run `yarn add @shopify/flash-list` (or the npm/pnpm equivalent) and rebuild.'
88
- );
89
- }
90
-
91
- const {
92
- background,
93
- scrollHandler,
94
- headerSnapOffsets,
95
- margins: screenMargins,
96
- hasHeader,
97
- } = useScreen();
98
-
99
- const resolvedMargins = margins ?? screenMargins;
100
- // The placeholder rides in on `ListEmptyComponent` rather than replacing the
101
- // list, so the header portal and snap offsets stay put. While loading the
102
- // list is fed an empty `data` so that slot is the one that renders.
103
- const placeholder = renderScreenPlaceholder({
104
- loading,
105
- LoadingComponent,
106
- EmptyComponent,
107
- isEmpty: !data || data.length === 0,
108
- });
109
-
110
- const jsScrollHandler = useAnimatedScrollHandler(
111
- (event) => {
112
- if (onScroll)
113
- onScroll(event as unknown as NativeSyntheticEvent<NativeScrollEvent>);
114
- },
115
- [onScroll]
116
- );
117
-
118
- const composedHandler = useComposedEventHandler([
119
- scrollHandler,
120
- jsScrollHandler,
121
- ]);
122
-
123
- // `AnimatedFlashList` is typed for `unknown` items, so the remaining props
124
- // are cast back to its prop type — minus the ones set explicitly below,
125
- // which the spread would otherwise re-supply.
126
- const listProps = rest as Omit<FlashListProps<unknown>, 'data' | 'onScroll'>;
127
-
128
- // A caller naming its own `bottomInset` has taken the bottom over entirely,
129
- // and a horizontal list has no bottom edge to clear.
130
- const needsBottomSafeArea = bottomInset === undefined && !horizontal;
131
-
132
- const footerComponent = (
133
- <>
134
- {renderListHeader(ListFooterComponent)}
135
- {needsBottomSafeArea ? <BottomSafeArea /> : null}
136
- </>
137
- );
138
-
139
- const headerComponent = (
140
- <>
141
- {/* `headerWrapper`'s negative margin cancels the list's inherited
142
- content padding for the portaled header (`LargeTitlePortalContent`
143
- etc. manage their own horizontal padding) — a caller's own
144
- `ListHeaderComponent` has no such self-padding, so it stays outside
145
- this wrapper and gets the list's normal item padding instead. */}
146
- <View style={styles.headerWrapper(resolvedMargins)}>
147
- <PortalHost name={SCREEN_HEADER_PORTAL_HOST} />
148
- </View>
149
- {renderListHeader(ListHeaderComponent)}
150
- </>
151
- );
152
-
153
- return (
154
- <AnimatedFlashList
155
- data={loading ? [] : (data as readonly unknown[] | null | undefined)}
156
- style={[{ backgroundColor: background }, style]}
157
- horizontal={horizontal}
158
- contentInsetAdjustmentBehavior={hasHeader ? 'never' : undefined}
159
- contentContainerStyle={[
160
- styles.content(resolvedMargins, hasHeader, bottomInset),
161
- contentContainerStyle,
162
- ]}
163
- ListHeaderComponent={headerComponent}
164
- ListFooterComponent={footerComponent}
165
- ListEmptyComponent={placeholder ?? ListEmptyComponent}
166
- showsVerticalScrollIndicator={false}
167
- scrollEventThrottle={16}
168
- snapToOffsets={headerSnapOffsets}
169
- {...listProps}
170
- onScroll={composedHandler}
171
- />
172
- );
173
- }
174
-
175
- // `resolveScreenMargin` is called *inside* each dynamic function below, not
176
- // hoisted to the factory's top level — the Babel plugin only attributes a
177
- // runtime dependency (here, breakpoint) to a style key when the `rt`/
178
- // `runtime` read it derives from sits inside that key's own value. Hoisting
179
- // it would silently drop the dependency, so the margin would freeze at
180
- // whatever breakpoint was active on mount and never update on rotation.
181
- const styles = StyleSheet.create((theme, runtime) => ({
182
- content: (
183
- margins: ScreenMargins,
184
- hasHeader: boolean,
185
- bottomInset: number | undefined
186
- ) => {
187
- const { x: marginX, y: marginY } = resolveScreenMargin(
188
- theme,
189
- runtime.breakpoint
190
- );
191
- return {
192
- flexGrow: 1,
193
- // The header is absolutely positioned, so the list reserves its full
194
- // height — bar plus top safe area. This assumes the scroll view does not
195
- // also get UIKit's automatic inset adjustment: on a native tab screen
196
- // that means `disableAutomaticContentInsets` on the tab, or the two
197
- // insets are counted twice.
198
- paddingTop:
199
- (hasHeader ? theme.layout.header.height + runtime.insets.top : 0) +
200
- (hasYMargin(margins) ? marginY : 0),
201
- paddingHorizontal: hasXMargin(margins) ? marginX : 0,
202
- // The safe area is `BottomSafeArea`'s job; counting it here as well
203
- // would leave a gap the height of the gesture bar.
204
- paddingBottom: bottomInset ?? (hasYMargin(margins) ? marginY : 0),
205
- };
206
- },
207
- safeArea: {
208
- height: runtime.insets.top,
209
- },
210
- headerWrapper: (margins: ScreenMargins) => {
211
- const { x: marginX } = resolveScreenMargin(theme, runtime.breakpoint);
212
- return {
213
- marginHorizontal: hasXMargin(margins) ? -marginX : 0,
214
- };
215
- },
216
- }));