@ankhorage/surface 2.2.0 → 3.0.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/CHANGELOG.md +12 -0
- package/README.md +1 -2
- package/dist/components/action-sheet/ActionSheet.d.ts.map +1 -1
- package/dist/components/action-sheet/ActionSheet.js +12 -7
- package/dist/components/action-sheet/ActionSheet.js.map +1 -1
- package/dist/components/button/Button.d.ts.map +1 -1
- package/dist/components/button/Button.js +5 -7
- package/dist/components/button/Button.js.map +1 -1
- package/dist/components/button/types.d.ts +2 -5
- package/dist/components/button/types.d.ts.map +1 -1
- package/dist/components/button/types.js.map +1 -1
- package/dist/components/drawer/Drawer.d.ts.map +1 -1
- package/dist/components/drawer/Drawer.js +12 -7
- package/dist/components/drawer/Drawer.js.map +1 -1
- package/dist/components/icon-button/IconButton.d.ts.map +1 -1
- package/dist/components/icon-button/IconButton.js +1 -1
- package/dist/components/icon-button/IconButton.js.map +1 -1
- package/dist/components/icon-button/types.d.ts +2 -5
- package/dist/components/icon-button/types.d.ts.map +1 -1
- package/dist/components/icon-button/types.js.map +1 -1
- package/dist/components/menu/Menu.d.ts.map +1 -1
- package/dist/components/menu/Menu.js +12 -7
- package/dist/components/menu/Menu.js.map +1 -1
- package/dist/components/modal/Modal.d.ts.map +1 -1
- package/dist/components/modal/Modal.js +12 -7
- package/dist/components/modal/Modal.js.map +1 -1
- package/dist/components/navigation/NavigationItem.d.ts.map +1 -1
- package/dist/components/navigation/NavigationItem.js +1 -1
- package/dist/components/navigation/NavigationItem.js.map +1 -1
- package/dist/components/navigation/TabBarItem.d.ts.map +1 -1
- package/dist/components/navigation/TabBarItem.js +1 -1
- package/dist/components/navigation/TabBarItem.js.map +1 -1
- package/dist/components/navigation/types.d.ts +2 -2
- package/dist/components/navigation/types.d.ts.map +1 -1
- package/dist/components/navigation/types.js.map +1 -1
- package/dist/components/toast/ToastProvider.d.ts.map +1 -1
- package/dist/components/toast/ToastProvider.js +3 -2
- package/dist/components/toast/ToastProvider.js.map +1 -1
- package/dist/components/tooltip/Tooltip.d.ts.map +1 -1
- package/dist/components/tooltip/Tooltip.js +10 -5
- package/dist/components/tooltip/Tooltip.js.map +1 -1
- package/dist/context/TranslationContext.d.ts.map +1 -1
- package/dist/context/TranslationContext.js +3 -3
- package/dist/context/TranslationContext.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/internal/overlay/OverlayProvider.d.ts.map +1 -1
- package/dist/internal/overlay/OverlayProvider.js +17 -16
- package/dist/internal/overlay/OverlayProvider.js.map +1 -1
- package/dist/internal/overlay/Portal.d.ts.map +1 -1
- package/dist/internal/overlay/Portal.js +2 -1
- package/dist/internal/overlay/Portal.js.map +1 -1
- package/dist/internal/resolvePointerEvents.d.ts +9 -0
- package/dist/internal/resolvePointerEvents.d.ts.map +1 -0
- package/dist/internal/resolvePointerEvents.js +17 -0
- package/dist/internal/resolvePointerEvents.js.map +1 -0
- package/dist/layout/AppBar.js +1 -1
- package/dist/layout/AppBar.js.map +1 -1
- package/dist/layout/Box.d.ts.map +1 -1
- package/dist/layout/Box.js +4 -22
- package/dist/layout/Box.js.map +1 -1
- package/dist/layout/Grid.js.map +1 -1
- package/dist/layout/Stack.d.ts +1 -1
- package/dist/layout/Stack.d.ts.map +1 -1
- package/dist/layout/Stack.js +2 -13
- package/dist/layout/Stack.js.map +1 -1
- package/dist/primitives/icon/Icon.d.ts +5 -12
- package/dist/primitives/icon/Icon.d.ts.map +1 -1
- package/dist/primitives/icon/Icon.js +5 -11
- package/dist/primitives/icon/Icon.js.map +1 -1
- package/dist/primitives/icon/PortableIcon.d.ts +51 -0
- package/dist/primitives/icon/PortableIcon.d.ts.map +1 -0
- package/dist/primitives/icon/PortableIcon.js +64 -0
- package/dist/primitives/icon/PortableIcon.js.map +1 -0
- package/dist/primitives/icon/index.d.ts +2 -1
- package/dist/primitives/icon/index.d.ts.map +1 -1
- package/dist/primitives/icon/index.js +1 -0
- package/dist/primitives/icon/index.js.map +1 -1
- package/dist/primitives/image/Image.d.ts.map +1 -1
- package/dist/primitives/image/Image.js +3 -5
- package/dist/primitives/image/Image.js.map +1 -1
- package/package.json +30 -27
- package/src/components/action-sheet/ActionSheet.tsx +14 -8
- package/src/components/button/Button.tsx +5 -17
- package/src/components/button/types.ts +2 -5
- package/src/components/drawer/Drawer.tsx +14 -8
- package/src/components/icon-button/IconButton.tsx +1 -6
- package/src/components/icon-button/types.ts +2 -5
- package/src/components/menu/Menu.tsx +14 -8
- package/src/components/modal/Modal.tsx +14 -8
- package/src/components/navigation/NavigationItem.tsx +1 -6
- package/src/components/navigation/TabBarItem.tsx +1 -6
- package/src/components/navigation/types.ts +2 -2
- package/src/components/toast/ToastProvider.tsx +3 -2
- package/src/components/tooltip/Tooltip.tsx +12 -6
- package/src/context/TranslationContext.tsx +12 -15
- package/src/index.test.ts +1 -0
- package/src/index.ts +2 -2
- package/src/internal/overlay/OverlayProvider.tsx +20 -20
- package/src/internal/overlay/Portal.tsx +2 -1
- package/src/internal/resolvePointerEvents.test.ts +19 -0
- package/src/internal/resolvePointerEvents.ts +29 -0
- package/src/layout/AppBar.tsx +1 -1
- package/src/layout/Box.tsx +5 -36
- package/src/layout/Grid.tsx +3 -3
- package/src/layout/Stack.test.ts +14 -0
- package/src/layout/Stack.tsx +3 -17
- package/src/primitives/icon/Icon.tsx +9 -22
- package/src/primitives/icon/PortableIcon.tsx +112 -0
- package/src/primitives/icon/PortableIcon.web.test.tsx +119 -0
- package/src/primitives/icon/index.ts +2 -1
- package/src/primitives/image/Image.tsx +3 -6
- package/src/theme/colorEngine.test.ts +2 -2
- package/src/webAcceptance.test.tsx +84 -0
- package/dist/primitives/icon/resolveExpoIconComponent.d.ts +0 -11
- package/dist/primitives/icon/resolveExpoIconComponent.d.ts.map +0 -1
- package/dist/primitives/icon/resolveExpoIconComponent.js +0 -20
- package/dist/primitives/icon/resolveExpoIconComponent.js.map +0 -1
- package/src/primitives/icon/resolveExpoIconComponent.test.ts +0 -45
- package/src/primitives/icon/resolveExpoIconComponent.ts +0 -33
|
@@ -31,11 +31,9 @@ export function Button({
|
|
|
31
31
|
{...props}
|
|
32
32
|
disabled={disabled || loading}
|
|
33
33
|
onPress={onPress}
|
|
34
|
-
style={
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
},
|
|
38
|
-
]}
|
|
34
|
+
style={{
|
|
35
|
+
alignSelf: fullWidth ? 'stretch' : 'flex-start',
|
|
36
|
+
}}
|
|
39
37
|
testID={testID}
|
|
40
38
|
>
|
|
41
39
|
{(state) => {
|
|
@@ -74,12 +72,7 @@ export function Button({
|
|
|
74
72
|
<>
|
|
75
73
|
{leadingIcon ? (
|
|
76
74
|
<View style={{ marginRight: theme.spacing.s }}>
|
|
77
|
-
<Icon
|
|
78
|
-
color={colors.contentColor}
|
|
79
|
-
name={leadingIcon.name}
|
|
80
|
-
provider={leadingIcon.provider}
|
|
81
|
-
size={iconSize}
|
|
82
|
-
/>
|
|
75
|
+
<Icon {...leadingIcon} color={colors.contentColor} size={iconSize} />
|
|
83
76
|
</View>
|
|
84
77
|
) : null}
|
|
85
78
|
<Text
|
|
@@ -92,12 +85,7 @@ export function Button({
|
|
|
92
85
|
</Text>
|
|
93
86
|
{trailingIcon ? (
|
|
94
87
|
<View style={{ marginLeft: theme.spacing.s }}>
|
|
95
|
-
<Icon
|
|
96
|
-
color={colors.contentColor}
|
|
97
|
-
name={trailingIcon.name}
|
|
98
|
-
provider={trailingIcon.provider}
|
|
99
|
-
size={iconSize}
|
|
100
|
-
/>
|
|
88
|
+
<Icon {...trailingIcon} color={colors.contentColor} size={iconSize} />
|
|
101
89
|
</View>
|
|
102
90
|
) : null}
|
|
103
91
|
</>
|
|
@@ -3,13 +3,10 @@ import type React from 'react';
|
|
|
3
3
|
import type { ControlSize } from '../../internal/resolvers/resolveControlSize';
|
|
4
4
|
import type { ButtonVariant } from '../../internal/resolvers/resolveInteractiveColors';
|
|
5
5
|
import type { ButtonBaseProps } from '../../primitives/button-base';
|
|
6
|
-
import type {
|
|
6
|
+
import type { IconSource } from '../../primitives/icon';
|
|
7
7
|
import type { SurfaceColor } from '../../surfaceColor';
|
|
8
8
|
|
|
9
|
-
export
|
|
10
|
-
name: IconProps['name'];
|
|
11
|
-
provider?: IconProps['provider'];
|
|
12
|
-
}
|
|
9
|
+
export type ButtonIconSpec = IconSource;
|
|
13
10
|
|
|
14
11
|
export interface ButtonProps extends Omit<
|
|
15
12
|
ButtonBaseProps,
|
|
@@ -3,11 +3,14 @@ import { Pressable, View } from 'react-native';
|
|
|
3
3
|
|
|
4
4
|
import { FocusScope } from '../../internal/focus/FocusScope';
|
|
5
5
|
import { Portal } from '../../internal/overlay/Portal';
|
|
6
|
+
import { resolvePointerEvents } from '../../internal/resolvePointerEvents';
|
|
6
7
|
import { resolveOverlayAnimation } from '../../internal/resolvers';
|
|
7
8
|
import { Box, Surface } from '../../layout';
|
|
8
9
|
import { useTheme } from '../../theme/ThemeContext';
|
|
9
10
|
import type { DrawerProps } from './types';
|
|
10
11
|
|
|
12
|
+
const boxNonePointerEvents = resolvePointerEvents('box-none');
|
|
13
|
+
|
|
11
14
|
export function Drawer({
|
|
12
15
|
visible,
|
|
13
16
|
onDismiss,
|
|
@@ -28,14 +31,17 @@ export function Drawer({
|
|
|
28
31
|
return (
|
|
29
32
|
<Portal layer="drawer" visible={visible}>
|
|
30
33
|
<View
|
|
31
|
-
|
|
32
|
-
style={
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
34
|
+
{...boxNonePointerEvents.props}
|
|
35
|
+
style={[
|
|
36
|
+
boxNonePointerEvents.style,
|
|
37
|
+
{
|
|
38
|
+
bottom: 0,
|
|
39
|
+
left: 0,
|
|
40
|
+
position: 'absolute',
|
|
41
|
+
right: 0,
|
|
42
|
+
top: 0,
|
|
43
|
+
},
|
|
44
|
+
]}
|
|
39
45
|
>
|
|
40
46
|
<Pressable
|
|
41
47
|
onPress={passive ? undefined : closeOnBackdrop ? onDismiss : undefined}
|
|
@@ -46,12 +46,7 @@ export function IconButton({
|
|
|
46
46
|
opacity: colors.opacity,
|
|
47
47
|
}}
|
|
48
48
|
>
|
|
49
|
-
<Icon
|
|
50
|
-
color={colors.contentColor}
|
|
51
|
-
name={icon.name}
|
|
52
|
-
provider={icon.provider}
|
|
53
|
-
size={resolveIconSize(theme, size)}
|
|
54
|
-
/>
|
|
49
|
+
<Icon {...icon} color={colors.contentColor} size={resolveIconSize(theme, size)} />
|
|
55
50
|
</Box>
|
|
56
51
|
);
|
|
57
52
|
}}
|
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
import type { ControlSize } from '../../internal/resolvers/resolveControlSize';
|
|
2
2
|
import type { ButtonVariant } from '../../internal/resolvers/resolveInteractiveColors';
|
|
3
3
|
import type { ButtonBaseProps } from '../../primitives/button-base';
|
|
4
|
-
import type {
|
|
4
|
+
import type { IconSource } from '../../primitives/icon';
|
|
5
5
|
import type { SurfaceColor } from '../../surfaceColor';
|
|
6
6
|
|
|
7
7
|
export interface IconButtonProps extends Omit<
|
|
8
8
|
ButtonBaseProps,
|
|
9
9
|
'children' | 'style' | 'accessibilityLabel'
|
|
10
10
|
> {
|
|
11
|
-
icon:
|
|
12
|
-
name: IconProps['name'];
|
|
13
|
-
provider?: IconProps['provider'];
|
|
14
|
-
};
|
|
11
|
+
icon: IconSource;
|
|
15
12
|
accessibilityLabel: string;
|
|
16
13
|
variant?: ButtonVariant;
|
|
17
14
|
color?: SurfaceColor;
|
|
@@ -4,6 +4,7 @@ import { type LayoutRectangle, Pressable, View } from 'react-native';
|
|
|
4
4
|
import { FocusScope } from '../../internal/focus/FocusScope';
|
|
5
5
|
import { useFocusManager } from '../../internal/focus/useFocusManager';
|
|
6
6
|
import { Portal } from '../../internal/overlay/Portal';
|
|
7
|
+
import { resolvePointerEvents } from '../../internal/resolvePointerEvents';
|
|
7
8
|
import { resolveOverlayAnimation } from '../../internal/resolvers';
|
|
8
9
|
import { Box, Inline, Stack, Surface } from '../../layout';
|
|
9
10
|
import { ButtonBase } from '../../primitives/button-base';
|
|
@@ -12,6 +13,8 @@ import { useTheme } from '../../theme/ThemeContext';
|
|
|
12
13
|
import { resolveNextMenuIndex } from './navigation';
|
|
13
14
|
import type { MenuAction, MenuProps } from './types';
|
|
14
15
|
|
|
16
|
+
const boxNonePointerEvents = resolvePointerEvents('box-none');
|
|
17
|
+
|
|
15
18
|
interface MeasurableNode {
|
|
16
19
|
measureInWindow?: (
|
|
17
20
|
callback: (x: number, y: number, width: number, height: number) => void,
|
|
@@ -142,14 +145,17 @@ export function Menu({
|
|
|
142
145
|
</ButtonBase>
|
|
143
146
|
<Portal layer="menu" visible={open && Boolean(layout)}>
|
|
144
147
|
<View
|
|
145
|
-
|
|
146
|
-
style={
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
148
|
+
{...boxNonePointerEvents.props}
|
|
149
|
+
style={[
|
|
150
|
+
boxNonePointerEvents.style,
|
|
151
|
+
{
|
|
152
|
+
bottom: 0,
|
|
153
|
+
left: 0,
|
|
154
|
+
position: 'absolute',
|
|
155
|
+
right: 0,
|
|
156
|
+
top: 0,
|
|
157
|
+
},
|
|
158
|
+
]}
|
|
153
159
|
>
|
|
154
160
|
<Pressable
|
|
155
161
|
onPress={passive ? undefined : closeMenu}
|
|
@@ -3,11 +3,14 @@ import { Pressable, View } from 'react-native';
|
|
|
3
3
|
|
|
4
4
|
import { FocusScope } from '../../internal/focus/FocusScope';
|
|
5
5
|
import { Portal } from '../../internal/overlay/Portal';
|
|
6
|
+
import { resolvePointerEvents } from '../../internal/resolvePointerEvents';
|
|
6
7
|
import { resolveOverlayAnimation } from '../../internal/resolvers';
|
|
7
8
|
import { Center, Surface } from '../../layout';
|
|
8
9
|
import { useTheme } from '../../theme/ThemeContext';
|
|
9
10
|
import type { ModalProps } from './types';
|
|
10
11
|
|
|
12
|
+
const boxNonePointerEvents = resolvePointerEvents('box-none');
|
|
13
|
+
|
|
11
14
|
export function Modal({
|
|
12
15
|
visible,
|
|
13
16
|
onDismiss,
|
|
@@ -27,14 +30,17 @@ export function Modal({
|
|
|
27
30
|
return (
|
|
28
31
|
<Portal layer="modal" visible={visible}>
|
|
29
32
|
<View
|
|
30
|
-
|
|
31
|
-
style={
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
33
|
+
{...boxNonePointerEvents.props}
|
|
34
|
+
style={[
|
|
35
|
+
boxNonePointerEvents.style,
|
|
36
|
+
{
|
|
37
|
+
bottom: 0,
|
|
38
|
+
left: 0,
|
|
39
|
+
position: 'absolute',
|
|
40
|
+
right: 0,
|
|
41
|
+
top: 0,
|
|
42
|
+
},
|
|
43
|
+
]}
|
|
38
44
|
>
|
|
39
45
|
<Pressable
|
|
40
46
|
onPress={passive ? undefined : closeOnBackdrop ? onDismiss : undefined}
|
|
@@ -56,12 +56,7 @@ export function NavigationItem({
|
|
|
56
56
|
>
|
|
57
57
|
{item.icon ? (
|
|
58
58
|
<View style={{ marginRight: theme.spacing.s }}>
|
|
59
|
-
<Icon
|
|
60
|
-
color={presentation.contentColor}
|
|
61
|
-
name={item.icon.name}
|
|
62
|
-
provider={item.icon.provider}
|
|
63
|
-
size={compact ? 's' : 'm'}
|
|
64
|
-
/>
|
|
59
|
+
<Icon {...item.icon} color={presentation.contentColor} size={compact ? 's' : 'm'} />
|
|
65
60
|
</View>
|
|
66
61
|
) : null}
|
|
67
62
|
<Box flex={1}>
|
|
@@ -57,12 +57,7 @@ export function TabBarItem({ item, compact = false, testID, interactionPolicy }:
|
|
|
57
57
|
>
|
|
58
58
|
{item.icon ? (
|
|
59
59
|
<View style={{ marginBottom: item.label ? theme.spacing.xs : 0 }}>
|
|
60
|
-
<Icon
|
|
61
|
-
color={contentColor}
|
|
62
|
-
name={item.icon.name}
|
|
63
|
-
provider={item.icon.provider}
|
|
64
|
-
size={compact ? 's' : 'm'}
|
|
65
|
-
/>
|
|
60
|
+
<Icon {...item.icon} color={contentColor} size={compact ? 's' : 'm'} />
|
|
66
61
|
</View>
|
|
67
62
|
) : null}
|
|
68
63
|
{item.label ? (
|
|
@@ -2,9 +2,9 @@ import type React from 'react';
|
|
|
2
2
|
import type { AccessibilityRole, AccessibilityState } from 'react-native';
|
|
3
3
|
|
|
4
4
|
import type { InteractionPolicy } from '../../interactionPolicy';
|
|
5
|
-
import type {
|
|
5
|
+
import type { IconSource } from '../../primitives/icon';
|
|
6
6
|
|
|
7
|
-
export type NavigationItemIcon =
|
|
7
|
+
export type NavigationItemIcon = IconSource;
|
|
8
8
|
|
|
9
9
|
export interface NavigationItemSpec {
|
|
10
10
|
id: string;
|
|
@@ -68,9 +68,10 @@ export function ToastProvider({
|
|
|
68
68
|
}, [defaultDuration, dismissToast, toasts]);
|
|
69
69
|
|
|
70
70
|
React.useEffect(() => {
|
|
71
|
+
const timers = timersRef.current;
|
|
71
72
|
return () => {
|
|
72
|
-
|
|
73
|
-
|
|
73
|
+
timers.forEach((timer) => clearTimeout(timer));
|
|
74
|
+
timers.clear();
|
|
74
75
|
};
|
|
75
76
|
}, []);
|
|
76
77
|
|
|
@@ -2,12 +2,15 @@ import React from 'react';
|
|
|
2
2
|
import { type LayoutRectangle, Platform, Pressable, View } from 'react-native';
|
|
3
3
|
|
|
4
4
|
import { Portal } from '../../internal/overlay/Portal';
|
|
5
|
+
import { resolvePointerEvents } from '../../internal/resolvePointerEvents';
|
|
5
6
|
import { resolveOverlayAnimation } from '../../internal/resolvers';
|
|
6
7
|
import { Surface } from '../../layout';
|
|
7
8
|
import { Text } from '../../primitives/text';
|
|
8
9
|
import { useTheme } from '../../theme/ThemeContext';
|
|
9
10
|
import type { TooltipProps } from './types';
|
|
10
11
|
|
|
12
|
+
const noPointerEvents = resolvePointerEvents('none');
|
|
13
|
+
|
|
11
14
|
interface MeasurableNode {
|
|
12
15
|
measureInWindow?: (
|
|
13
16
|
callback: (x: number, y: number, width: number, height: number) => void,
|
|
@@ -85,12 +88,15 @@ export function Tooltip({
|
|
|
85
88
|
</Pressable>
|
|
86
89
|
<Portal layer="tooltip" visible={visible && Boolean(layout)}>
|
|
87
90
|
<View
|
|
88
|
-
|
|
89
|
-
style={
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
91
|
+
{...noPointerEvents.props}
|
|
92
|
+
style={[
|
|
93
|
+
noPointerEvents.style,
|
|
94
|
+
{
|
|
95
|
+
left: tooltipLeft,
|
|
96
|
+
position: 'absolute',
|
|
97
|
+
top: tooltipTop,
|
|
98
|
+
},
|
|
99
|
+
]}
|
|
94
100
|
>
|
|
95
101
|
<Surface
|
|
96
102
|
p="s"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { createContext, useContext
|
|
1
|
+
import React, { createContext, useContext } from 'react';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Minimal translation runtime surface.
|
|
@@ -31,20 +31,17 @@ export function TranslationProvider(props: {
|
|
|
31
31
|
}) {
|
|
32
32
|
const { t, i18n, children } = props;
|
|
33
33
|
|
|
34
|
-
const value =
|
|
35
|
-
() =>
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}),
|
|
46
|
-
[t, i18n, i18n?.language],
|
|
47
|
-
);
|
|
34
|
+
const value: TranslationRuntime = {
|
|
35
|
+
t: (key: string, options?: Record<string, unknown>) => {
|
|
36
|
+
if (i18n?.t) {
|
|
37
|
+
const result = i18n.t(key, options);
|
|
38
|
+
// If translation returns the key, it's missing in the current dictionary
|
|
39
|
+
if (result !== key) return result;
|
|
40
|
+
}
|
|
41
|
+
return t(key, options);
|
|
42
|
+
},
|
|
43
|
+
i18n: i18n ?? null,
|
|
44
|
+
};
|
|
48
45
|
|
|
49
46
|
return <TranslationContext.Provider value={value}>{children}</TranslationContext.Provider>;
|
|
50
47
|
}
|
package/src/index.test.ts
CHANGED
|
@@ -19,6 +19,7 @@ const expectedRootExports = [
|
|
|
19
19
|
"export { Field } from './components/field';",
|
|
20
20
|
"export { HelperText } from './components/helper-text';",
|
|
21
21
|
"export { IconButton } from './components/icon-button';",
|
|
22
|
+
"export { Icon, SUPPORTED_ICON_PROVIDERS } from './primitives/icon';",
|
|
22
23
|
"export type { InteractionPolicy, InteractionPolicyProps } from './interactionPolicy';",
|
|
23
24
|
"export { Label } from './components/label';",
|
|
24
25
|
"export { ListItem } from './components/list-item';",
|
package/src/index.ts
CHANGED
|
@@ -67,8 +67,8 @@ export type { ButtonBaseProps } from './primitives/button-base';
|
|
|
67
67
|
export { ButtonBase } from './primitives/button-base';
|
|
68
68
|
export type { HeadingLevel, HeadingProps } from './primitives/heading';
|
|
69
69
|
export { Heading } from './primitives/heading';
|
|
70
|
-
export type { IconProps, IconProvider } from './primitives/icon';
|
|
71
|
-
export { Icon } from './primitives/icon';
|
|
70
|
+
export type { IconProps, IconProvider, IconSource, IconVariant } from './primitives/icon';
|
|
71
|
+
export { Icon, SUPPORTED_ICON_PROVIDERS } from './primitives/icon';
|
|
72
72
|
export type { ImageFit, ImageProps, SurfaceImageSource } from './primitives/image';
|
|
73
73
|
export { Image } from './primitives/image';
|
|
74
74
|
export type { TextProps } from './primitives/text';
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { View } from 'react-native';
|
|
2
|
+
import { StyleSheet, View, type ViewStyle } from 'react-native';
|
|
3
3
|
|
|
4
|
+
import { resolvePointerEvents } from '../resolvePointerEvents';
|
|
4
5
|
import {
|
|
5
6
|
createOverlayEntry,
|
|
6
7
|
type OverlayDescriptor,
|
|
@@ -10,6 +11,21 @@ import {
|
|
|
10
11
|
sortOverlayEntries,
|
|
11
12
|
} from './useOverlayStack';
|
|
12
13
|
|
|
14
|
+
const boxNonePointerEvents = resolvePointerEvents('box-none');
|
|
15
|
+
const styles = StyleSheet.create({
|
|
16
|
+
fill: {
|
|
17
|
+
bottom: 0,
|
|
18
|
+
left: 0,
|
|
19
|
+
position: 'absolute',
|
|
20
|
+
right: 0,
|
|
21
|
+
top: 0,
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
function overlayZIndex(zIndex: number): ViewStyle {
|
|
26
|
+
return { zIndex };
|
|
27
|
+
}
|
|
28
|
+
|
|
13
29
|
export function OverlayProvider({ children }: { children: React.ReactNode }) {
|
|
14
30
|
const orderRef = React.useRef(0);
|
|
15
31
|
const [overlays, setOverlays] = React.useState<OverlayEntry[]>([]);
|
|
@@ -52,28 +68,12 @@ export function OverlayProvider({ children }: { children: React.ReactNode }) {
|
|
|
52
68
|
<OverlayStackActionsContext.Provider value={actions}>
|
|
53
69
|
<OverlayStackContext.Provider value={value}>
|
|
54
70
|
{children}
|
|
55
|
-
<View
|
|
56
|
-
pointerEvents="box-none"
|
|
57
|
-
style={{
|
|
58
|
-
bottom: 0,
|
|
59
|
-
left: 0,
|
|
60
|
-
position: 'absolute',
|
|
61
|
-
right: 0,
|
|
62
|
-
top: 0,
|
|
63
|
-
}}
|
|
64
|
-
>
|
|
71
|
+
<View {...boxNonePointerEvents.props} style={[boxNonePointerEvents.style, styles.fill]}>
|
|
65
72
|
{overlays.map((overlay) => (
|
|
66
73
|
<View
|
|
74
|
+
{...boxNonePointerEvents.props}
|
|
67
75
|
key={overlay.id}
|
|
68
|
-
|
|
69
|
-
style={{
|
|
70
|
-
bottom: 0,
|
|
71
|
-
left: 0,
|
|
72
|
-
position: 'absolute',
|
|
73
|
-
right: 0,
|
|
74
|
-
top: 0,
|
|
75
|
-
zIndex: overlay.zIndex,
|
|
76
|
-
}}
|
|
76
|
+
style={[boxNonePointerEvents.style, styles.fill, overlayZIndex(overlay.zIndex)]}
|
|
77
77
|
>
|
|
78
78
|
{overlay.node}
|
|
79
79
|
</View>
|
|
@@ -19,9 +19,10 @@ export function Portal({ children, layer = 'modal', visible = true }: PortalProp
|
|
|
19
19
|
if (!overlayActions || !visible) {
|
|
20
20
|
return undefined;
|
|
21
21
|
}
|
|
22
|
+
const id = idRef.current;
|
|
22
23
|
|
|
23
24
|
return () => {
|
|
24
|
-
overlayActions.removeOverlay(
|
|
25
|
+
overlayActions.removeOverlay(id);
|
|
25
26
|
};
|
|
26
27
|
}, [overlayActions, visible]);
|
|
27
28
|
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test';
|
|
2
|
+
|
|
3
|
+
import { resolvePointerEventsForPlatform } from './resolvePointerEvents';
|
|
4
|
+
|
|
5
|
+
describe('resolvePointerEvents', () => {
|
|
6
|
+
test('moves React Native Web pointer events into style', () => {
|
|
7
|
+
expect(resolvePointerEventsForPlatform('box-none', 'web')).toEqual({
|
|
8
|
+
props: {},
|
|
9
|
+
style: { pointerEvents: 'box-none' },
|
|
10
|
+
});
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test('preserves the native pointer-events prop', () => {
|
|
14
|
+
expect(resolvePointerEventsForPlatform('none', 'ios')).toEqual({
|
|
15
|
+
props: { pointerEvents: 'none' },
|
|
16
|
+
style: null,
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Platform, type StyleProp, type ViewProps, type ViewStyle } from 'react-native';
|
|
2
|
+
|
|
3
|
+
interface ResolvedPointerEvents {
|
|
4
|
+
props: Pick<ViewProps, 'pointerEvents'>;
|
|
5
|
+
style: StyleProp<ViewStyle>;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function resolvePointerEvents(
|
|
9
|
+
pointerEvents: NonNullable<ViewProps['pointerEvents']>,
|
|
10
|
+
): ResolvedPointerEvents {
|
|
11
|
+
return resolvePointerEventsForPlatform(pointerEvents, Platform.OS);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function resolvePointerEventsForPlatform(
|
|
15
|
+
pointerEvents: NonNullable<ViewProps['pointerEvents']>,
|
|
16
|
+
platform: typeof Platform.OS,
|
|
17
|
+
): ResolvedPointerEvents {
|
|
18
|
+
if (platform === 'web') {
|
|
19
|
+
return {
|
|
20
|
+
props: {},
|
|
21
|
+
style: { pointerEvents },
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
props: { pointerEvents },
|
|
27
|
+
style: null,
|
|
28
|
+
};
|
|
29
|
+
}
|
package/src/layout/AppBar.tsx
CHANGED
|
@@ -33,7 +33,7 @@ export function AppBar({
|
|
|
33
33
|
topInset === 0 ? undefined : { paddingTop: topInset };
|
|
34
34
|
|
|
35
35
|
return (
|
|
36
|
-
<Box {...props} bg={bg ?? theme.semantics.surface.default} style={
|
|
36
|
+
<Box {...props} bg={bg ?? theme.semantics.surface.default} style={style}>
|
|
37
37
|
<View style={safeAreaStyle}>
|
|
38
38
|
<View
|
|
39
39
|
style={[
|
package/src/layout/Box.tsx
CHANGED
|
@@ -1,13 +1,8 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
3
|
-
Platform,
|
|
4
|
-
type StyleProp,
|
|
5
|
-
View,
|
|
6
|
-
type ViewProps as ReactNativeViewProps,
|
|
7
|
-
type ViewStyle,
|
|
8
|
-
} from 'react-native';
|
|
2
|
+
import { View, type ViewProps as ReactNativeViewProps } from 'react-native';
|
|
9
3
|
|
|
10
4
|
import { useResponsiveRuntime } from '../core/responsive';
|
|
5
|
+
import { resolvePointerEvents } from '../internal/resolvePointerEvents';
|
|
11
6
|
import { useTheme } from '../theme/ThemeContext';
|
|
12
7
|
import { type BoxStyleProps, resolveBoxStyles } from './helpers';
|
|
13
8
|
|
|
@@ -21,31 +16,6 @@ export interface BoxProps extends BoxStyleProps {
|
|
|
21
16
|
testID?: string;
|
|
22
17
|
}
|
|
23
18
|
|
|
24
|
-
function resolvePointerEventsStyle(
|
|
25
|
-
pointerEvents: ReactNativeViewProps['pointerEvents'] | undefined,
|
|
26
|
-
): StyleProp<ViewStyle> | null {
|
|
27
|
-
if (Platform.OS !== 'web' || pointerEvents === undefined) return null;
|
|
28
|
-
|
|
29
|
-
if (pointerEvents === 'auto' || pointerEvents === 'none') {
|
|
30
|
-
return { pointerEvents };
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return null;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function resolveViewPointerEvents(
|
|
37
|
-
pointerEvents: ReactNativeViewProps['pointerEvents'] | undefined,
|
|
38
|
-
): ReactNativeViewProps['pointerEvents'] | undefined {
|
|
39
|
-
if (pointerEvents === undefined) return undefined;
|
|
40
|
-
if (Platform.OS !== 'web') return pointerEvents;
|
|
41
|
-
|
|
42
|
-
if (pointerEvents === 'box-none' || pointerEvents === 'box-only') {
|
|
43
|
-
return pointerEvents;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return undefined;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
19
|
export function Box({
|
|
50
20
|
accessible,
|
|
51
21
|
accessibilityLabel,
|
|
@@ -60,18 +30,17 @@ export function Box({
|
|
|
60
30
|
const { theme } = useTheme();
|
|
61
31
|
const { breakpoint } = useResponsiveRuntime();
|
|
62
32
|
const resolved = resolveBoxStyles(theme, breakpoint, props);
|
|
63
|
-
const
|
|
64
|
-
const viewPointerEvents = resolveViewPointerEvents(pointerEvents);
|
|
33
|
+
const resolvedPointerEvents = pointerEvents ? resolvePointerEvents(pointerEvents) : null;
|
|
65
34
|
|
|
66
35
|
return (
|
|
67
36
|
<View
|
|
37
|
+
{...resolvedPointerEvents?.props}
|
|
68
38
|
accessible={accessible}
|
|
69
39
|
accessibilityLabel={accessibilityLabel}
|
|
70
40
|
accessibilityRole={accessibilityRole}
|
|
71
41
|
accessibilityState={accessibilityState}
|
|
72
|
-
pointerEvents={viewPointerEvents}
|
|
73
42
|
testID={testID}
|
|
74
|
-
style={[resolved,
|
|
43
|
+
style={[resolved, resolvedPointerEvents?.style, style]}
|
|
75
44
|
>
|
|
76
45
|
{children}
|
|
77
46
|
</View>
|
package/src/layout/Grid.tsx
CHANGED
|
@@ -35,7 +35,7 @@ export function Grid({
|
|
|
35
35
|
const colSpacing = Number(resolveSpacing(theme, activeColGap) ?? 0);
|
|
36
36
|
const activeMinItemWidth = resolveResponsive(minItemWidth, breakpoint);
|
|
37
37
|
|
|
38
|
-
const basisPercent = `${100 / activeCols}%`;
|
|
38
|
+
const basisPercent: `${number}%` = `${100 / activeCols}%`;
|
|
39
39
|
const nodes = React.Children.toArray(children);
|
|
40
40
|
|
|
41
41
|
return (
|
|
@@ -58,8 +58,8 @@ export function Grid({
|
|
|
58
58
|
flexGrow: 1,
|
|
59
59
|
}
|
|
60
60
|
: {
|
|
61
|
-
width: basisPercent
|
|
62
|
-
flexBasis: basisPercent
|
|
61
|
+
width: basisPercent,
|
|
62
|
+
flexBasis: basisPercent,
|
|
63
63
|
};
|
|
64
64
|
|
|
65
65
|
return (
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
|
|
3
|
+
import { describe, expect, it } from 'bun:test';
|
|
4
|
+
|
|
5
|
+
describe('Stack layout contract', () => {
|
|
6
|
+
it('keeps authored children as direct flex items and applies spacing through gap', async () => {
|
|
7
|
+
const source = await readFile(new URL('./Stack.tsx', import.meta.url), 'utf8');
|
|
8
|
+
|
|
9
|
+
expect(source).toContain('gap: activeGap');
|
|
10
|
+
expect(source).toContain('{children}');
|
|
11
|
+
expect(source).not.toContain("from 'react-native'");
|
|
12
|
+
expect(source).not.toContain('React.Children');
|
|
13
|
+
});
|
|
14
|
+
});
|
package/src/layout/Stack.tsx
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { View } from 'react-native';
|
|
1
|
+
import type React from 'react';
|
|
3
2
|
|
|
4
3
|
import { resolveResponsive, type Responsive, useResponsiveRuntime } from '../core/responsive';
|
|
5
4
|
import { useTheme } from '../theme/ThemeContext';
|
|
@@ -34,14 +33,13 @@ export function Stack({
|
|
|
34
33
|
const activeJustify = resolveResponsive(justify, breakpoint);
|
|
35
34
|
const activeWrap = resolveResponsive(wrap, breakpoint);
|
|
36
35
|
|
|
37
|
-
const nodes = React.Children.toArray(children);
|
|
38
|
-
|
|
39
36
|
return (
|
|
40
37
|
<Box
|
|
41
38
|
{...props}
|
|
42
39
|
style={[
|
|
43
40
|
{
|
|
44
41
|
flexDirection: activeDirection,
|
|
42
|
+
gap: activeGap,
|
|
45
43
|
alignItems: activeAlign,
|
|
46
44
|
justifyContent: activeJustify,
|
|
47
45
|
flexWrap: activeWrap,
|
|
@@ -49,19 +47,7 @@ export function Stack({
|
|
|
49
47
|
props.style,
|
|
50
48
|
]}
|
|
51
49
|
>
|
|
52
|
-
{
|
|
53
|
-
const spacing =
|
|
54
|
-
index === 0
|
|
55
|
-
? undefined
|
|
56
|
-
: activeDirection === 'row'
|
|
57
|
-
? { marginLeft: activeGap }
|
|
58
|
-
: { marginTop: activeGap };
|
|
59
|
-
return (
|
|
60
|
-
<View key={String(index)} style={spacing}>
|
|
61
|
-
{node}
|
|
62
|
-
</View>
|
|
63
|
-
);
|
|
64
|
-
})}
|
|
50
|
+
{children}
|
|
65
51
|
</Box>
|
|
66
52
|
);
|
|
67
53
|
}
|