@unif/react-native-design 0.37.0 → 0.39.0
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/lib/module/components/ui/ActionMenuContent/ActionMenuContent.js +62 -18
- package/lib/module/components/ui/ActionMenuContent/ActionMenuContent.js.map +1 -1
- package/lib/module/components/ui/ActionMenuContent/ActionMenuItem.js +57 -0
- package/lib/module/components/ui/ActionMenuContent/ActionMenuItem.js.map +1 -0
- package/lib/module/components/ui/ActionMenuContent/ActionMenuSheetCard.js +25 -0
- package/lib/module/components/ui/ActionMenuContent/ActionMenuSheetCard.js.map +1 -0
- package/lib/module/components/ui/ActionMenuContent/styles.js +50 -2
- package/lib/module/components/ui/ActionMenuContent/styles.js.map +1 -1
- package/lib/module/components/ui/ActionMenuContent/useMenuMousePress.js +26 -0
- package/lib/module/components/ui/ActionMenuContent/useMenuMousePress.js.map +1 -0
- package/lib/module/components/ui/ImagePreview/ImagePreview.js +81 -72
- package/lib/module/components/ui/ImagePreview/ImagePreview.js.map +1 -1
- package/lib/module/components/ui/ImagePreview/constants.js +2 -0
- package/lib/module/components/ui/ImagePreview/constants.js.map +1 -1
- package/lib/module/components/ui/ImagePreview/styles.js +5 -1
- package/lib/module/components/ui/ImagePreview/styles.js.map +1 -1
- package/lib/module/components/ui/Sheet/Sheet.js +2 -1
- package/lib/module/components/ui/Sheet/Sheet.js.map +1 -1
- package/lib/typescript/src/components/ui/ActionMenuContent/ActionMenuContent.d.ts +1 -1
- package/lib/typescript/src/components/ui/ActionMenuContent/ActionMenuContent.d.ts.map +1 -1
- package/lib/typescript/src/components/ui/ActionMenuContent/ActionMenuItem.d.ts +3 -0
- package/lib/typescript/src/components/ui/ActionMenuContent/ActionMenuItem.d.ts.map +1 -0
- package/lib/typescript/src/components/ui/ActionMenuContent/ActionMenuSheetCard.d.ts +3 -0
- package/lib/typescript/src/components/ui/ActionMenuContent/ActionMenuSheetCard.d.ts.map +1 -0
- package/lib/typescript/src/components/ui/ActionMenuContent/styles.d.ts +57 -2
- package/lib/typescript/src/components/ui/ActionMenuContent/styles.d.ts.map +1 -1
- package/lib/typescript/src/components/ui/ActionMenuContent/types.d.ts +15 -1
- package/lib/typescript/src/components/ui/ActionMenuContent/types.d.ts.map +1 -1
- package/lib/typescript/src/components/ui/ActionMenuContent/useMenuMousePress.d.ts +10 -0
- package/lib/typescript/src/components/ui/ActionMenuContent/useMenuMousePress.d.ts.map +1 -0
- package/lib/typescript/src/components/ui/ImagePreview/ImagePreview.d.ts.map +1 -1
- package/lib/typescript/src/components/ui/ImagePreview/constants.d.ts +1 -0
- package/lib/typescript/src/components/ui/ImagePreview/constants.d.ts.map +1 -1
- package/lib/typescript/src/components/ui/ImagePreview/styles.d.ts +4 -1
- package/lib/typescript/src/components/ui/ImagePreview/styles.d.ts.map +1 -1
- package/lib/typescript/src/components/ui/Sheet/Sheet.d.ts +1 -1
- package/lib/typescript/src/components/ui/Sheet/Sheet.d.ts.map +1 -1
- package/lib/typescript/src/components/ui/Sheet/types.d.ts +1 -0
- package/lib/typescript/src/components/ui/Sheet/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/ui/ActionMenuContent/ActionMenuContent.tsx +108 -50
- package/src/components/ui/ActionMenuContent/ActionMenuItem.tsx +56 -0
- package/src/components/ui/ActionMenuContent/ActionMenuSheetCard.tsx +28 -0
- package/src/components/ui/ActionMenuContent/styles.ts +50 -3
- package/src/components/ui/ActionMenuContent/types.ts +17 -1
- package/src/components/ui/ActionMenuContent/useMenuMousePress.ts +30 -0
- package/src/components/ui/ImagePreview/ImagePreview.tsx +92 -76
- package/src/components/ui/ImagePreview/constants.ts +3 -0
- package/src/components/ui/ImagePreview/styles.ts +5 -1
- package/src/components/ui/Sheet/Sheet.tsx +4 -1
- package/src/components/ui/Sheet/types.ts +1 -0
|
@@ -1,8 +1,55 @@
|
|
|
1
1
|
import { StyleSheet } from 'react-native';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import {
|
|
3
|
+
fixed,
|
|
4
|
+
fw,
|
|
5
|
+
r,
|
|
6
|
+
radius,
|
|
7
|
+
space,
|
|
8
|
+
type as typography,
|
|
9
|
+
} from '../../../theme';
|
|
10
|
+
import type { ColorTokens, ShadowTokens } from '../../../theme';
|
|
11
|
+
export const makeStyles = (c: ColorTokens, shadows: ShadowTokens) =>
|
|
5
12
|
StyleSheet.create({
|
|
13
|
+
popoverRoot: {
|
|
14
|
+
width: r(200),
|
|
15
|
+
maxWidth: '100%',
|
|
16
|
+
borderRadius: r(20),
|
|
17
|
+
borderWidth: 0.5,
|
|
18
|
+
borderColor: c.outline,
|
|
19
|
+
backgroundColor: c.surface,
|
|
20
|
+
...shadows.card,
|
|
21
|
+
},
|
|
22
|
+
popoverCard: {
|
|
23
|
+
borderRadius: r(20),
|
|
24
|
+
overflow: 'hidden',
|
|
25
|
+
paddingVertical: space[2],
|
|
26
|
+
},
|
|
27
|
+
popoverRow: {
|
|
28
|
+
minHeight: fixed.hitTarget,
|
|
29
|
+
flexDirection: 'row',
|
|
30
|
+
alignItems: 'center',
|
|
31
|
+
gap: space[5],
|
|
32
|
+
paddingVertical: space[3],
|
|
33
|
+
paddingHorizontal: space[6],
|
|
34
|
+
},
|
|
35
|
+
popoverRowPressed: { backgroundColor: c.surfaceContainer },
|
|
36
|
+
popoverRowDisabled: { opacity: 0.4 },
|
|
37
|
+
popoverIcon: {
|
|
38
|
+
width: 40,
|
|
39
|
+
height: 40,
|
|
40
|
+
borderRadius: 20,
|
|
41
|
+
alignItems: 'center',
|
|
42
|
+
justifyContent: 'center',
|
|
43
|
+
backgroundColor: c.surfaceContainer,
|
|
44
|
+
},
|
|
45
|
+
popoverLabel: {
|
|
46
|
+
flex: 1,
|
|
47
|
+
minWidth: 0,
|
|
48
|
+
fontSize: typography.body,
|
|
49
|
+
color: c.foreground,
|
|
50
|
+
},
|
|
51
|
+
popoverDanger: { color: c.error },
|
|
52
|
+
popoverConfirmationActions: { paddingTop: space[2] },
|
|
6
53
|
dialogRoot: {
|
|
7
54
|
flex: 1,
|
|
8
55
|
justifyContent: 'center',
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
1
2
|
import type { StyleProp, ViewStyle } from 'react-native';
|
|
3
|
+
import type { IconName } from '../Icon';
|
|
2
4
|
|
|
3
5
|
export interface ActionMenuConfirmation {
|
|
4
6
|
message: string;
|
|
@@ -8,6 +10,8 @@ export interface ActionMenuConfirmation {
|
|
|
8
10
|
export interface ActionMenuAction {
|
|
9
11
|
id: string;
|
|
10
12
|
label: string;
|
|
13
|
+
icon?: IconName;
|
|
14
|
+
accessibilityHint?: string;
|
|
11
15
|
onPress(): void;
|
|
12
16
|
disabled?: boolean;
|
|
13
17
|
loading?: boolean;
|
|
@@ -18,10 +22,22 @@ export interface ActionMenuAction {
|
|
|
18
22
|
/** 操作、确认有效性和宿主关闭由调用方负责。 */
|
|
19
23
|
export interface ActionMenuContentProps {
|
|
20
24
|
actions: readonly ActionMenuAction[];
|
|
21
|
-
presentation?: 'dialog' | 'sheet';
|
|
25
|
+
presentation?: 'dialog' | 'sheet' | 'popover';
|
|
22
26
|
title?: string;
|
|
23
27
|
cancelLabel?: string;
|
|
24
28
|
onClose(): void;
|
|
25
29
|
style?: StyleProp<ViewStyle>;
|
|
30
|
+
contentStyle?: StyleProp<ViewStyle>;
|
|
26
31
|
testID?: string;
|
|
27
32
|
}
|
|
33
|
+
|
|
34
|
+
export interface ActionMenuItemProps {
|
|
35
|
+
action: ActionMenuAction;
|
|
36
|
+
onPress(): void;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface ActionMenuSheetCardProps {
|
|
40
|
+
children: ReactNode;
|
|
41
|
+
contentStyle?: StyleProp<ViewStyle>;
|
|
42
|
+
testID: string;
|
|
43
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
import { Platform } from 'react-native';
|
|
3
|
+
import type { PointerEvent } from 'react-native';
|
|
4
|
+
|
|
5
|
+
/** Web 菜单按下时保留原输入焦点;实际选择只由 Pressable.onPress 交付。 */
|
|
6
|
+
export function useMenuMousePress(disabled: boolean) {
|
|
7
|
+
const [pressed, setPressed] = useState(false);
|
|
8
|
+
const enabled = Platform.OS === 'web' && !disabled;
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
if (disabled) setPressed(false);
|
|
11
|
+
}, [disabled]);
|
|
12
|
+
const begin = (event: PointerEvent) => {
|
|
13
|
+
if (
|
|
14
|
+
!enabled ||
|
|
15
|
+
event.nativeEvent.pointerType !== 'mouse' ||
|
|
16
|
+
event.nativeEvent.button !== 0
|
|
17
|
+
)
|
|
18
|
+
return;
|
|
19
|
+
event.preventDefault();
|
|
20
|
+
setPressed(true);
|
|
21
|
+
};
|
|
22
|
+
const end = () => setPressed(false);
|
|
23
|
+
return {
|
|
24
|
+
mousePressed: enabled && pressed,
|
|
25
|
+
onPointerDown: begin,
|
|
26
|
+
onPointerUp: end,
|
|
27
|
+
onPointerCancel: end,
|
|
28
|
+
onPointerLeave: end,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
@@ -12,7 +12,11 @@ import { radius, useThemedStyles } from '../../../theme';
|
|
|
12
12
|
import { IconButton } from '../IconButton';
|
|
13
13
|
import { Thumbnail } from '../Thumbnail';
|
|
14
14
|
import { Carousel, type CarouselRef } from 'react-native-reanimated-carousel';
|
|
15
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
DELETE_LABEL,
|
|
17
|
+
IMAGE_COUNTER_HEIGHT,
|
|
18
|
+
IMAGE_UNAVAILABLE,
|
|
19
|
+
} from './constants';
|
|
16
20
|
import { makeStyles } from './styles';
|
|
17
21
|
import type {
|
|
18
22
|
ImagePreviewHandle,
|
|
@@ -149,82 +153,94 @@ export const ImagePreview = forwardRef<ImagePreviewHandle, ImagePreviewProps>(
|
|
|
149
153
|
|
|
150
154
|
return (
|
|
151
155
|
<View style={[styles.container, style]} testID={testID}>
|
|
152
|
-
<
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
156
|
+
<View style={{ width, height: height + IMAGE_COUNTER_HEIGHT / 2 }}>
|
|
157
|
+
<Carousel
|
|
158
|
+
key={selection.carouselRevision}
|
|
159
|
+
ref={carouselRef}
|
|
160
|
+
data={data}
|
|
161
|
+
defaultIndex={selection.index}
|
|
162
|
+
loop={false}
|
|
163
|
+
itemSize={width}
|
|
164
|
+
style={{ width, height }}
|
|
165
|
+
keyExtractor={(id) => id}
|
|
166
|
+
testID={testID ? `${testID}-carousel` : undefined}
|
|
167
|
+
onScrollStart={() => {
|
|
168
|
+
if (
|
|
169
|
+
mounted.current &&
|
|
170
|
+
selection.carouselRevision ===
|
|
171
|
+
committed.current.carouselRevision
|
|
172
|
+
) {
|
|
173
|
+
moving.current = true;
|
|
174
|
+
}
|
|
175
|
+
}}
|
|
176
|
+
onSnapToItem={(index) => {
|
|
177
|
+
const latest = committed.current;
|
|
178
|
+
if (
|
|
179
|
+
!mounted.current ||
|
|
180
|
+
selection.carouselRevision !== latest.carouselRevision ||
|
|
181
|
+
items.length !== latest.items.length ||
|
|
182
|
+
items.some(
|
|
183
|
+
(item, itemIndex) => item.id !== latest.items[itemIndex]?.id
|
|
184
|
+
)
|
|
178
185
|
)
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
{
|
|
198
|
-
|
|
199
|
-
<
|
|
200
|
-
{
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
186
|
+
return;
|
|
187
|
+
moving.current = false;
|
|
188
|
+
const id = items[index]?.id;
|
|
189
|
+
const nextIndex = latest.items.findIndex(
|
|
190
|
+
(item) => item.id === id
|
|
191
|
+
);
|
|
192
|
+
if (nextIndex >= 0 && nextIndex !== latest.index) {
|
|
193
|
+
const next = { ...latest, index: nextIndex };
|
|
194
|
+
// SDK 已完成落位;句柄同步可读,不等待 React 下一次提交。
|
|
195
|
+
committed.current = next;
|
|
196
|
+
setSelection(next);
|
|
197
|
+
notifiedId.current = id;
|
|
198
|
+
currentChange.current?.(next.items[nextIndex]!);
|
|
199
|
+
}
|
|
200
|
+
}}
|
|
201
|
+
renderItem={({ item: id, index }) => {
|
|
202
|
+
const item = items.find((candidate) => candidate.id === id)!;
|
|
203
|
+
return (
|
|
204
|
+
<View style={[styles.media, { width, height }]}>
|
|
205
|
+
{item.source === undefined ? (
|
|
206
|
+
<View style={styles.placeholder}>
|
|
207
|
+
<Text style={styles.placeholderText}>
|
|
208
|
+
{IMAGE_UNAVAILABLE}
|
|
209
|
+
</Text>
|
|
210
|
+
</View>
|
|
211
|
+
) : (
|
|
212
|
+
<Thumbnail
|
|
213
|
+
source={item.source}
|
|
214
|
+
size={{ width, height, borderRadius: radius.xl }}
|
|
215
|
+
resizeMode="contain"
|
|
216
|
+
imageStyle={styles.image}
|
|
217
|
+
accessibilityLabel={
|
|
218
|
+
item.label ?? `图片第 ${index + 1} 张`
|
|
219
|
+
}
|
|
220
|
+
fallback={
|
|
221
|
+
<View style={styles.placeholder}>
|
|
222
|
+
<Text style={styles.placeholderText}>
|
|
223
|
+
{IMAGE_UNAVAILABLE}
|
|
224
|
+
</Text>
|
|
225
|
+
</View>
|
|
226
|
+
}
|
|
227
|
+
/>
|
|
228
|
+
)}
|
|
229
|
+
<View pointerEvents="none" style={styles.border} />
|
|
230
|
+
</View>
|
|
231
|
+
);
|
|
232
|
+
}}
|
|
233
|
+
/>
|
|
234
|
+
<View
|
|
235
|
+
style={styles.counter}
|
|
236
|
+
accessible
|
|
237
|
+
accessibilityLiveRegion="polite"
|
|
238
|
+
accessibilityLabel={`第${selection.index + 1}张,共${items.length}张`}
|
|
239
|
+
>
|
|
240
|
+
<Text style={styles.counterText}>
|
|
241
|
+
第{selection.index + 1}/{items.length}张
|
|
242
|
+
</Text>
|
|
243
|
+
</View>
|
|
228
244
|
</View>
|
|
229
245
|
{current.canDelete && onRequestDelete ? (
|
|
230
246
|
<IconButton
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { StyleSheet } from 'react-native';
|
|
2
2
|
import { fontMono, radius, space, type as typography } from '../../../theme';
|
|
3
3
|
import type { ColorTokens } from '../../../theme';
|
|
4
|
+
import { IMAGE_COUNTER_HEIGHT } from './constants';
|
|
4
5
|
|
|
5
6
|
export const makeStyles = (colors: ColorTokens) =>
|
|
6
7
|
StyleSheet.create({
|
|
@@ -31,8 +32,11 @@ export const makeStyles = (colors: ColorTokens) =>
|
|
|
31
32
|
textAlign: 'center',
|
|
32
33
|
},
|
|
33
34
|
counter: {
|
|
35
|
+
position: 'absolute',
|
|
36
|
+
bottom: 0,
|
|
34
37
|
alignSelf: 'center',
|
|
35
|
-
|
|
38
|
+
height: IMAGE_COUNTER_HEIGHT,
|
|
39
|
+
justifyContent: 'center',
|
|
36
40
|
paddingHorizontal: space[3],
|
|
37
41
|
borderRadius: radius.pill,
|
|
38
42
|
borderWidth: StyleSheet.hairlineWidth,
|
|
@@ -10,6 +10,7 @@ export function Sheet({
|
|
|
10
10
|
footer,
|
|
11
11
|
contentMode = 'fixed',
|
|
12
12
|
contentContainerStyle,
|
|
13
|
+
footerStyle,
|
|
13
14
|
style,
|
|
14
15
|
testID,
|
|
15
16
|
}: SheetProps): React.JSX.Element {
|
|
@@ -37,7 +38,9 @@ export function Sheet({
|
|
|
37
38
|
{children}
|
|
38
39
|
</View>
|
|
39
40
|
)}
|
|
40
|
-
{footer ?
|
|
41
|
+
{footer ? (
|
|
42
|
+
<View style={[styles.footer, footerStyle]}>{footer}</View>
|
|
43
|
+
) : null}
|
|
41
44
|
</View>
|
|
42
45
|
);
|
|
43
46
|
}
|