@oneplatformdev/react-native-uikit 1.2.1 → 1.2.3
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.
- package/README.md +11 -2
- package/package.json +3 -1
- package/registry/manifest.json +4 -3
- package/src/components/Loader/index.ts +2 -2
- package/src/components/PhotoPreview/presenters/usePhotoPreview.ts +49 -0
- package/src/components/PhotoPreview/types/types.ts +15 -4
- package/src/components/PhotoPreview/ui/PhotoPreview.tsx +136 -15
- package/src/components/PhotoPreview/ui/styles.ts +51 -0
package/README.md
CHANGED
|
@@ -319,7 +319,16 @@ Owned source is available through `npx @oneplatformdev/react-native-uikit add sw
|
|
|
319
319
|
|
|
320
320
|
## PhotoPreview and EmptyListView
|
|
321
321
|
|
|
322
|
-
`PhotoPreview`
|
|
322
|
+
`PhotoPreview` uses `@d11/react-native-fast-image` for both the thumbnail and optional fullscreen viewer. Install that native peer in the app. It accepts a FastImage source (`{ uri }` or a local `require(...)`) and passes it unchanged to both images, preserving headers, priority, and any explicit cache policy. Without `source.cache`, FastImage uses its own default `immutable` policy (URI-keyed caching); choose `source={{ uri, cache: FastImage.cacheControl.web }}` when HTTP-header-aware caching is needed. The default thumbnail is a 120-unit square with the theme's medium radius. `width`/`height` are design values; `aspectRatio` derives the missing dimension (width wins if both dimensions and a ratio are supplied). `style` applies after container geometry; `imageStyle` applies after the fill-image style. Source, resize-mode, and image-style types follow FastImage rather than React Native `Image`.
|
|
323
|
+
|
|
324
|
+
```tsx
|
|
325
|
+
<PhotoPreview source={{ uri }} />
|
|
326
|
+
<PhotoPreview source={{ uri }} fullscreen closeAccessibilityLabel={closePhotoLabel} />
|
|
327
|
+
<PhotoPreview source={{ uri }} fullscreen closeIcon={<MyCloseIcon />} loadingIndicator={<MyLoader />} />
|
|
328
|
+
<PhotoPreview source={{ uri }} onRemovePress={onRemove} removeIcon={<MyRemoveIcon />} />
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
`fullscreen` is opt-in. Pressing opens one built-in Modal with a cached FastImage (`contain` by default), a centered package Loader until load or error, and a close button positioned at the unscaled top safe-area inset plus scaled design spacing. Apps using fullscreen should configure their usual `SafeAreaProvider`. Override the fullscreen background, resize mode, image style, close icon/labels, or loading indicator as needed. With `fullscreen`, `onPress` observes the same thumbnail press after opening; `onFullscreenOpen`/`onFullscreenClose` observe viewer transitions. Without `fullscreen`, `onPress` keeps its prior behavior. The independent remove action never opens fullscreen; `overlay` remains thumbnail-only. A built-in × is used if `removeIcon` or `closeIcon` is omitted. Supply localized accessibility labels for actions.
|
|
323
332
|
|
|
324
333
|
`EmptyListView` centers optional `visual`, `title`, and `description` in available list space. Strings and numbers use themed Typography (`title` and `body` by default); custom React nodes render as supplied. `titleStyle` and `descriptionStyle` apply to built-in text, while custom nodes own their styling. A supplied `action` replaces the built-in Button; otherwise the Button appears only when both `actionLabel` and `onActionPress` are given. The optional `accessibilityLabel` labels the visual without grouping the readable text or action.
|
|
325
334
|
|
|
@@ -337,7 +346,7 @@ Owned source is available with `npx @oneplatformdev/react-native-uikit add photo
|
|
|
337
346
|
|
|
338
347
|
`BottomModal` provides the reference-style slide/drag dismissal and keyboard-height-aware positioning. Supply `children`, `onClose`, and optionally `title`, `customHeader`, and a labeled `closeAction` icon. `Keyboard.dismiss()` and the close animation start together; `closeRef` can trigger the same close path externally. The kit does not render app-specific toasts.
|
|
339
348
|
|
|
340
|
-
Both modes require consumers to install the declared native peers used by these components: `react-native-keyboard-controller`, `react-native-linear-gradient`, `react-native-reanimated`, and `react-native-safe-area-context`. Applications using keyboard-aware or safe-area behavior should configure the providers and native setup required by those libraries. The kit does not bundle those peers or install them through `add`.
|
|
349
|
+
Both modes require consumers to install the declared native peers used by these components: `@d11/react-native-fast-image`, `react-native-keyboard-controller`, `react-native-linear-gradient`, `react-native-reanimated`, and `react-native-safe-area-context`. Applications using keyboard-aware or safe-area behavior should configure the providers and native setup required by those libraries. The kit does not bundle those peers or install them through `add`.
|
|
341
350
|
|
|
342
351
|
## ConnectionContainer
|
|
343
352
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oneplatformdev/react-native-uikit",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.3",
|
|
4
4
|
"main": "src/index.ts",
|
|
5
5
|
"types": "src/index.ts",
|
|
6
6
|
"bin": {
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"validate:registry": "node scripts/validate-registry.cjs"
|
|
19
19
|
},
|
|
20
20
|
"peerDependencies": {
|
|
21
|
+
"@d11/react-native-fast-image": "^8.13.0",
|
|
21
22
|
"react": "*",
|
|
22
23
|
"react-native": "*",
|
|
23
24
|
"react-native-keyboard-controller": "^1.22.5",
|
|
@@ -26,6 +27,7 @@
|
|
|
26
27
|
"react-native-safe-area-context": "^5.5.2"
|
|
27
28
|
},
|
|
28
29
|
"devDependencies": {
|
|
30
|
+
"@d11/react-native-fast-image": "^8.13.0",
|
|
29
31
|
"@types/react": "^19.2.0",
|
|
30
32
|
"react-native-keyboard-controller": "^1.22.5",
|
|
31
33
|
"react-native-linear-gradient": "^2.8.3",
|
package/registry/manifest.json
CHANGED
|
@@ -58,10 +58,10 @@
|
|
|
58
58
|
},
|
|
59
59
|
"photo-preview": {
|
|
60
60
|
"folder": "PhotoPreview",
|
|
61
|
-
"files": ["ui/PhotoPreview.tsx", "ui/styles.ts", "types/types.ts", "index.ts"],
|
|
62
|
-
"componentDependencies": [],
|
|
61
|
+
"files": ["ui/PhotoPreview.tsx", "presenters/usePhotoPreview.ts", "ui/styles.ts", "types/types.ts", "index.ts"],
|
|
62
|
+
"componentDependencies": ["loader"],
|
|
63
63
|
"foundations": ["useUITheme", "useScaling", "UIColors", "UIRadius", "UISpacing", "ScalingFunctions"],
|
|
64
|
-
"dependencies": ["react", "react-native", "@oneplatformdev/react-native-uikit"],
|
|
64
|
+
"dependencies": ["react", "react-native", "@d11/react-native-fast-image", "react-native-safe-area-context", "@oneplatformdev/react-native-uikit"],
|
|
65
65
|
"rewrites": {
|
|
66
66
|
"../../../theme": "@oneplatformdev/react-native-uikit",
|
|
67
67
|
"../../../utils": "@oneplatformdev/react-native-uikit"
|
|
@@ -148,6 +148,7 @@
|
|
|
148
148
|
"folder": "Loader",
|
|
149
149
|
"files": ["ui/Loader.tsx", "ui/styles.ts", "types/types.ts", "index.ts"],
|
|
150
150
|
"componentDependencies": [],
|
|
151
|
+
"publicExports": { "Loader": "ui/Loader.tsx", "LoaderProps": "types/types.ts" },
|
|
151
152
|
"foundations": ["useUITheme", "UIColors"],
|
|
152
153
|
"dependencies": ["react", "react-native", "@oneplatformdev/react-native-uikit"],
|
|
153
154
|
"rewrites": {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
1
|
+
export * from './ui/Loader';
|
|
2
|
+
export * from './types/types';
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
2
|
+
import type { PhotoPreviewProps } from '../types/types';
|
|
3
|
+
|
|
4
|
+
export const usePhotoPreview = ({
|
|
5
|
+
fullscreen,
|
|
6
|
+
disabled,
|
|
7
|
+
onPress,
|
|
8
|
+
onFullscreenOpen,
|
|
9
|
+
onFullscreenClose,
|
|
10
|
+
}: Pick<PhotoPreviewProps, 'fullscreen' | 'disabled' | 'onPress' | 'onFullscreenOpen' | 'onFullscreenClose'>) => {
|
|
11
|
+
const [visible, setVisible] = useState(false);
|
|
12
|
+
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
if (!fullscreen) setVisible(false);
|
|
15
|
+
}, [fullscreen]);
|
|
16
|
+
|
|
17
|
+
const onThumbnailPress = useCallback(() => {
|
|
18
|
+
if (disabled) return;
|
|
19
|
+
if (fullscreen) {
|
|
20
|
+
setVisible(true);
|
|
21
|
+
onFullscreenOpen?.();
|
|
22
|
+
}
|
|
23
|
+
onPress?.();
|
|
24
|
+
}, [disabled, fullscreen, onFullscreenOpen, onPress]);
|
|
25
|
+
|
|
26
|
+
const onClose = useCallback(() => {
|
|
27
|
+
setVisible(false);
|
|
28
|
+
onFullscreenClose?.();
|
|
29
|
+
}, [onFullscreenClose]);
|
|
30
|
+
|
|
31
|
+
return { visible: fullscreen === true && visible, onThumbnailPress, onClose };
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
type LoadState = 'loading' | 'loaded' | 'error';
|
|
35
|
+
|
|
36
|
+
export const useFullscreenImage = () => {
|
|
37
|
+
const [loadState, setLoadState] = useState<LoadState>('loading');
|
|
38
|
+
const onLoadStart = useCallback(() => setLoadState('loading'), []);
|
|
39
|
+
const onLoad = useCallback(() => setLoadState('loaded'), []);
|
|
40
|
+
const onError = useCallback(() => setLoadState('error'), []);
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
isLoading: loadState === 'loading',
|
|
44
|
+
isImageVisible: loadState === 'loaded',
|
|
45
|
+
onLoadStart,
|
|
46
|
+
onLoad,
|
|
47
|
+
onError,
|
|
48
|
+
};
|
|
49
|
+
};
|
|
@@ -1,13 +1,24 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
|
-
import type {
|
|
2
|
+
import type { StyleProp, ViewStyle } from 'react-native';
|
|
3
|
+
import type { FastImageProps } from '@d11/react-native-fast-image';
|
|
3
4
|
|
|
4
5
|
export interface PhotoPreviewProps {
|
|
5
|
-
source:
|
|
6
|
+
source: NonNullable<FastImageProps['source']>;
|
|
6
7
|
width?: number;
|
|
7
8
|
height?: number;
|
|
8
9
|
aspectRatio?: number;
|
|
9
10
|
borderRadius?: number;
|
|
10
|
-
resizeMode?:
|
|
11
|
+
resizeMode?: FastImageProps['resizeMode'];
|
|
12
|
+
fullscreen?: boolean;
|
|
13
|
+
fullscreenResizeMode?: FastImageProps['resizeMode'];
|
|
14
|
+
fullscreenBackgroundColor?: string;
|
|
15
|
+
fullscreenImageStyle?: FastImageProps['style'];
|
|
16
|
+
closeIcon?: ReactNode;
|
|
17
|
+
closeAccessibilityLabel?: string;
|
|
18
|
+
closeAccessibilityHint?: string;
|
|
19
|
+
loadingIndicator?: ReactNode;
|
|
20
|
+
onFullscreenOpen?: () => void;
|
|
21
|
+
onFullscreenClose?: () => void;
|
|
11
22
|
overlay?: ReactNode;
|
|
12
23
|
removeIcon?: ReactNode;
|
|
13
24
|
onRemovePress?: () => void;
|
|
@@ -18,5 +29,5 @@ export interface PhotoPreviewProps {
|
|
|
18
29
|
accessibilityHint?: string;
|
|
19
30
|
disabled?: boolean;
|
|
20
31
|
style?: StyleProp<ViewStyle>;
|
|
21
|
-
imageStyle?:
|
|
32
|
+
imageStyle?: FastImageProps['style'];
|
|
22
33
|
}
|
|
@@ -1,17 +1,118 @@
|
|
|
1
1
|
import { useMemo } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { Modal, Pressable, StyleSheet, Text, View } from 'react-native';
|
|
3
|
+
import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
4
|
+
import FastImage from '@d11/react-native-fast-image';
|
|
5
|
+
import { Loader } from '../../Loader';
|
|
3
6
|
import { useUITheme } from '../../../theme';
|
|
4
7
|
import { useScaling } from '../../../utils';
|
|
5
|
-
import {
|
|
8
|
+
import { useFullscreenImage, usePhotoPreview } from '../presenters/usePhotoPreview';
|
|
9
|
+
import { fastImageStyles, getFullscreenStyles, getStyles } from './styles';
|
|
6
10
|
import type { PhotoPreviewProps } from '../types/types';
|
|
7
11
|
|
|
12
|
+
type FullscreenImageProps = Pick<PhotoPreviewProps, 'source' | 'fullscreenResizeMode' | 'fullscreenImageStyle' | 'loadingIndicator'>
|
|
13
|
+
& { styles: ReturnType<typeof getFullscreenStyles>['styles'] };
|
|
14
|
+
|
|
15
|
+
const FullscreenImage = ({ source, fullscreenResizeMode, fullscreenImageStyle, loadingIndicator, styles }: FullscreenImageProps) => {
|
|
16
|
+
const { isLoading, isImageVisible, onLoadStart, onLoad, onError } = useFullscreenImage();
|
|
17
|
+
const hasSource = typeof source === 'number' || Boolean(source.uri?.trim());
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<View style={styles.imageArea}>
|
|
21
|
+
{hasSource ? (
|
|
22
|
+
<FastImage
|
|
23
|
+
source={source}
|
|
24
|
+
resizeMode={fullscreenResizeMode ?? FastImage.resizeMode.contain}
|
|
25
|
+
accessible={false}
|
|
26
|
+
style={[fastImageStyles.fill, fullscreenImageStyle, !isImageVisible && fastImageStyles.hidden]}
|
|
27
|
+
onLoadStart={onLoadStart}
|
|
28
|
+
onLoad={onLoad}
|
|
29
|
+
onError={onError}
|
|
30
|
+
/>
|
|
31
|
+
) : null}
|
|
32
|
+
{hasSource && isLoading ? (
|
|
33
|
+
<View pointerEvents="none" style={styles.loadingOverlay}>
|
|
34
|
+
{loadingIndicator === undefined ? <Loader inline transparent accessibilityLabel="Loading photo" /> : loadingIndicator}
|
|
35
|
+
</View>
|
|
36
|
+
) : null}
|
|
37
|
+
</View>
|
|
38
|
+
);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
type FullscreenViewerProps = Pick<PhotoPreviewProps,
|
|
42
|
+
| 'source' | 'fullscreenResizeMode' | 'fullscreenBackgroundColor' | 'fullscreenImageStyle'
|
|
43
|
+
| 'closeIcon' | 'closeAccessibilityLabel' | 'closeAccessibilityHint' | 'loadingIndicator'>
|
|
44
|
+
& { visible: boolean; onClose: () => void };
|
|
45
|
+
|
|
46
|
+
const FullscreenViewer = ({
|
|
47
|
+
source,
|
|
48
|
+
visible,
|
|
49
|
+
onClose,
|
|
50
|
+
fullscreenResizeMode,
|
|
51
|
+
fullscreenBackgroundColor,
|
|
52
|
+
fullscreenImageStyle,
|
|
53
|
+
closeIcon,
|
|
54
|
+
closeAccessibilityLabel,
|
|
55
|
+
closeAccessibilityHint,
|
|
56
|
+
loadingIndicator,
|
|
57
|
+
}: FullscreenViewerProps) => {
|
|
58
|
+
const { colors, spacing } = useUITheme();
|
|
59
|
+
const scaling = useScaling();
|
|
60
|
+
const { top } = useSafeAreaInsets();
|
|
61
|
+
const { styles, closeHitSlop } = useMemo(
|
|
62
|
+
() => getFullscreenStyles(colors, spacing, scaling, top, fullscreenBackgroundColor),
|
|
63
|
+
[colors, spacing, scaling, top, fullscreenBackgroundColor],
|
|
64
|
+
);
|
|
65
|
+
const sourceKey = typeof source === 'number' ? `asset:${source}` : JSON.stringify(source);
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
<Modal visible={visible} animationType="fade" statusBarTranslucent onRequestClose={onClose}>
|
|
69
|
+
<SafeAreaView edges={['left', 'right', 'bottom']} style={styles.fullscreen} accessibilityViewIsModal>
|
|
70
|
+
<View style={styles.toolbar}>
|
|
71
|
+
<Pressable
|
|
72
|
+
accessibilityRole="button"
|
|
73
|
+
accessibilityLabel={closeAccessibilityLabel ?? 'Close photo'}
|
|
74
|
+
accessibilityHint={closeAccessibilityHint}
|
|
75
|
+
hitSlop={closeHitSlop}
|
|
76
|
+
onPress={onClose}
|
|
77
|
+
style={styles.close}
|
|
78
|
+
>
|
|
79
|
+
{closeIcon === undefined ? <Text style={styles.closeText}>×</Text>
|
|
80
|
+
: typeof closeIcon === 'string' || typeof closeIcon === 'number'
|
|
81
|
+
? <Text style={styles.closeText}>{closeIcon}</Text> : closeIcon}
|
|
82
|
+
</Pressable>
|
|
83
|
+
</View>
|
|
84
|
+
{visible ? (
|
|
85
|
+
<FullscreenImage
|
|
86
|
+
key={sourceKey}
|
|
87
|
+
source={source}
|
|
88
|
+
fullscreenResizeMode={fullscreenResizeMode}
|
|
89
|
+
fullscreenImageStyle={fullscreenImageStyle}
|
|
90
|
+
loadingIndicator={loadingIndicator}
|
|
91
|
+
styles={styles}
|
|
92
|
+
/>
|
|
93
|
+
) : null}
|
|
94
|
+
</SafeAreaView>
|
|
95
|
+
</Modal>
|
|
96
|
+
);
|
|
97
|
+
};
|
|
98
|
+
|
|
8
99
|
export const PhotoPreview = ({
|
|
9
100
|
source,
|
|
10
101
|
width,
|
|
11
102
|
height,
|
|
12
103
|
aspectRatio,
|
|
13
104
|
borderRadius,
|
|
14
|
-
resizeMode =
|
|
105
|
+
resizeMode = FastImage.resizeMode.cover,
|
|
106
|
+
fullscreen = false,
|
|
107
|
+
fullscreenResizeMode,
|
|
108
|
+
fullscreenBackgroundColor,
|
|
109
|
+
fullscreenImageStyle,
|
|
110
|
+
closeIcon,
|
|
111
|
+
closeAccessibilityLabel,
|
|
112
|
+
closeAccessibilityHint,
|
|
113
|
+
loadingIndicator,
|
|
114
|
+
onFullscreenOpen,
|
|
115
|
+
onFullscreenClose,
|
|
15
116
|
overlay,
|
|
16
117
|
removeIcon,
|
|
17
118
|
onRemovePress,
|
|
@@ -30,28 +131,34 @@ export const PhotoPreview = ({
|
|
|
30
131
|
() => getStyles(colors, radius, spacing, scaling, width, height, aspectRatio, borderRadius),
|
|
31
132
|
[colors, radius, spacing, scaling, width, height, aspectRatio, borderRadius],
|
|
32
133
|
);
|
|
134
|
+
const { visible, onThumbnailPress, onClose } = usePhotoPreview({
|
|
135
|
+
fullscreen, disabled, onPress, onFullscreenOpen, onFullscreenClose,
|
|
136
|
+
});
|
|
137
|
+
const isPressable = fullscreen || onPress !== undefined;
|
|
33
138
|
const image = (
|
|
34
|
-
<
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
139
|
+
<View style={styles.image}>
|
|
140
|
+
<FastImage
|
|
141
|
+
source={source}
|
|
142
|
+
resizeMode={resizeMode}
|
|
143
|
+
accessible={!isPressable}
|
|
144
|
+
accessibilityRole={isPressable ? undefined : 'image'}
|
|
145
|
+
accessibilityLabel={isPressable ? undefined : accessibilityLabel}
|
|
146
|
+
accessibilityHint={isPressable ? undefined : accessibilityHint}
|
|
147
|
+
style={[fastImageStyles.fill, imageStyle]}
|
|
148
|
+
/>
|
|
149
|
+
</View>
|
|
43
150
|
);
|
|
44
151
|
|
|
45
152
|
return (
|
|
46
153
|
<View style={[styles.container, style]}>
|
|
47
|
-
{
|
|
154
|
+
{isPressable ? (
|
|
48
155
|
<Pressable
|
|
49
156
|
accessibilityRole="button"
|
|
50
|
-
accessibilityLabel={accessibilityLabel}
|
|
157
|
+
accessibilityLabel={accessibilityLabel ?? 'Open photo'}
|
|
51
158
|
accessibilityHint={accessibilityHint}
|
|
52
159
|
accessibilityState={{ disabled }}
|
|
53
160
|
disabled={disabled}
|
|
54
|
-
onPress={
|
|
161
|
+
onPress={onThumbnailPress}
|
|
55
162
|
style={StyleSheet.absoluteFill}
|
|
56
163
|
>
|
|
57
164
|
{image}
|
|
@@ -78,6 +185,20 @@ export const PhotoPreview = ({
|
|
|
78
185
|
? <Text style={styles.removeText}>{removeIcon}</Text> : removeIcon}
|
|
79
186
|
</Pressable>
|
|
80
187
|
) : null}
|
|
188
|
+
{fullscreen ? (
|
|
189
|
+
<FullscreenViewer
|
|
190
|
+
source={source}
|
|
191
|
+
visible={visible}
|
|
192
|
+
onClose={onClose}
|
|
193
|
+
fullscreenResizeMode={fullscreenResizeMode}
|
|
194
|
+
fullscreenBackgroundColor={fullscreenBackgroundColor}
|
|
195
|
+
fullscreenImageStyle={fullscreenImageStyle}
|
|
196
|
+
closeIcon={closeIcon}
|
|
197
|
+
closeAccessibilityLabel={closeAccessibilityLabel}
|
|
198
|
+
closeAccessibilityHint={closeAccessibilityHint}
|
|
199
|
+
loadingIndicator={loadingIndicator}
|
|
200
|
+
/>
|
|
201
|
+
) : null}
|
|
81
202
|
</View>
|
|
82
203
|
);
|
|
83
204
|
};
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { StyleSheet } from 'react-native';
|
|
2
|
+
import type { ViewStyle } from 'react-native';
|
|
3
|
+
import type { ImageStyle as FastImageImageStyle } from '@d11/react-native-fast-image';
|
|
2
4
|
import type { UIColors, UIRadius, UISpacing } from '../../../theme';
|
|
3
5
|
import type { ScalingFunctions } from '../../../utils';
|
|
4
6
|
import type { PhotoPreviewProps } from '../types/types';
|
|
@@ -6,6 +8,13 @@ import type { PhotoPreviewProps } from '../types/types';
|
|
|
6
8
|
const positiveOrUndefined = (value: number | undefined) =>
|
|
7
9
|
value !== undefined && Number.isFinite(value) && value > 0 ? value : undefined;
|
|
8
10
|
|
|
11
|
+
type CompatibleFastImageStyle = FastImageImageStyle & Pick<ViewStyle, 'width' | 'height' | 'opacity'>;
|
|
12
|
+
|
|
13
|
+
export const fastImageStyles: Record<'fill' | 'hidden', CompatibleFastImageStyle> = {
|
|
14
|
+
fill: { width: '100%', height: '100%' },
|
|
15
|
+
hidden: { opacity: 0 },
|
|
16
|
+
};
|
|
17
|
+
|
|
9
18
|
export const getStyles = (
|
|
10
19
|
colors: UIColors,
|
|
11
20
|
radius: UIRadius,
|
|
@@ -66,3 +75,45 @@ export const getStyles = (
|
|
|
66
75
|
|
|
67
76
|
return { styles, removeHitSlop };
|
|
68
77
|
};
|
|
78
|
+
|
|
79
|
+
export const getFullscreenStyles = (
|
|
80
|
+
colors: UIColors,
|
|
81
|
+
spacing: UISpacing,
|
|
82
|
+
scaling: ScalingFunctions,
|
|
83
|
+
topInset: number,
|
|
84
|
+
backgroundColor?: string,
|
|
85
|
+
) => {
|
|
86
|
+
const styles = StyleSheet.create({
|
|
87
|
+
fullscreen: {
|
|
88
|
+
flex: 1,
|
|
89
|
+
backgroundColor: backgroundColor ?? colors.text,
|
|
90
|
+
},
|
|
91
|
+
toolbar: {
|
|
92
|
+
alignItems: 'flex-end',
|
|
93
|
+
paddingHorizontal: scaling.scaleHorizontal(spacing.lg),
|
|
94
|
+
paddingTop: topInset + scaling.scaleVertical(spacing.sm),
|
|
95
|
+
},
|
|
96
|
+
close: {
|
|
97
|
+
width: scaling.scaleHorizontal(40),
|
|
98
|
+
height: scaling.scaleHorizontal(40),
|
|
99
|
+
alignItems: 'center',
|
|
100
|
+
justifyContent: 'center',
|
|
101
|
+
},
|
|
102
|
+
closeText: {
|
|
103
|
+
color: colors.onPrimary,
|
|
104
|
+
fontSize: scaling.scaleFontSize(24),
|
|
105
|
+
lineHeight: scaling.scaleLineHeight(28),
|
|
106
|
+
},
|
|
107
|
+
imageArea: {
|
|
108
|
+
flex: 1,
|
|
109
|
+
width: '100%',
|
|
110
|
+
},
|
|
111
|
+
loadingOverlay: {
|
|
112
|
+
...StyleSheet.absoluteFill,
|
|
113
|
+
alignItems: 'center',
|
|
114
|
+
justifyContent: 'center',
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
return { styles, closeHitSlop: scaling.scaleHorizontal(spacing.xs) };
|
|
119
|
+
};
|