@tactics/toddle-styleguide 5.4.47 → 5.4.49
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/package.json +1 -1
- package/src/components/atoms/paragraph-components/paragraph-text.styles.d.ts +2 -0
- package/src/components/atoms/paragraph-components/paragraph-text.styles.js +2 -0
- package/src/components/templates/popover/components/modal/modal.component.tsx +7 -1
- package/src/components/templates/popover/popover.component.tsx +5 -6
- package/src/components/templates/popover-action/popover-action.component.tsx +48 -22
- package/src/components/templates/popover-action/popover-action.preview.tsx +148 -114
- package/src/components/templates/popover-action/popover-action.styles.d.ts +6 -0
- package/src/components/templates/popover-action/popover-action.styles.js +6 -0
package/package.json
CHANGED
|
@@ -14,6 +14,7 @@ type PopoverModalProps = {
|
|
|
14
14
|
isVisible: boolean;
|
|
15
15
|
children: React.ReactNode;
|
|
16
16
|
windowHeight: number;
|
|
17
|
+
bottomInset?: number;
|
|
17
18
|
disableScrollContent?: boolean;
|
|
18
19
|
};
|
|
19
20
|
|
|
@@ -24,6 +25,7 @@ const Modal = ({
|
|
|
24
25
|
onClose,
|
|
25
26
|
children,
|
|
26
27
|
windowHeight,
|
|
28
|
+
bottomInset = 0,
|
|
27
29
|
disableScrollContent,
|
|
28
30
|
}: PopoverModalProps) => {
|
|
29
31
|
// Keep element and window size in local state.
|
|
@@ -36,7 +38,11 @@ const Modal = ({
|
|
|
36
38
|
const elementHeight = Math.round(elementSize.height);
|
|
37
39
|
|
|
38
40
|
const maxHeight = Math.round(windowHeight / 100) * 90;
|
|
39
|
-
|
|
41
|
+
// windowHeight already reflects the full edge-to-edge screen height on
|
|
42
|
+
// Android, but it excludes the space behind the navigation bar, so the
|
|
43
|
+
// sheet's rest/closed position needs bottomInset added or its top edge
|
|
44
|
+
// peeks out from behind the nav bar when closed (see PopoverAction).
|
|
45
|
+
const translateYStartingPoint = windowHeight + bottomInset;
|
|
40
46
|
|
|
41
47
|
const saveHeight = elementHeight > maxHeight ? maxHeight : elementHeight;
|
|
42
48
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import {useEffect, useState} from 'react';
|
|
3
|
-
import {Dimensions, Platform,
|
|
3
|
+
import {Dimensions, Platform, View} from 'react-native';
|
|
4
|
+
import {useSafeAreaInsets} from 'react-native-safe-area-context';
|
|
4
5
|
import {Stylesheet} from './popover.styles';
|
|
5
6
|
import {Backdrop} from '../../atoms/backdrop/backdrop.component';
|
|
6
7
|
import {Modal, Foreground} from './components';
|
|
@@ -23,17 +24,14 @@ const Popover = ({
|
|
|
23
24
|
disableScrollContent,
|
|
24
25
|
}: PopoverProps) => {
|
|
25
26
|
const styles = Stylesheet();
|
|
27
|
+
const insets = useSafeAreaInsets();
|
|
26
28
|
const [windowHeight, setWindowHeight] = useState(
|
|
27
29
|
Dimensions.get('window').height
|
|
28
30
|
);
|
|
29
31
|
|
|
30
32
|
useEffect(() => {
|
|
31
33
|
const updateWindowHeight = () => {
|
|
32
|
-
|
|
33
|
-
if (Platform.OS === 'android') {
|
|
34
|
-
availableHeight += StatusBar.currentHeight ?? 0;
|
|
35
|
-
}
|
|
36
|
-
setWindowHeight(availableHeight);
|
|
34
|
+
setWindowHeight(Dimensions.get('window').height);
|
|
37
35
|
};
|
|
38
36
|
|
|
39
37
|
updateWindowHeight();
|
|
@@ -52,6 +50,7 @@ const Popover = ({
|
|
|
52
50
|
<Foreground>
|
|
53
51
|
<Modal
|
|
54
52
|
windowHeight={windowHeight}
|
|
53
|
+
bottomInset={Platform.OS === 'android' ? insets.bottom : 0}
|
|
55
54
|
isVisible={isVisible}
|
|
56
55
|
onClose={onClose}
|
|
57
56
|
title={title}
|
|
@@ -12,7 +12,6 @@ import {
|
|
|
12
12
|
Dimensions,
|
|
13
13
|
LayoutChangeEvent,
|
|
14
14
|
Platform,
|
|
15
|
-
StatusBar,
|
|
16
15
|
View,
|
|
17
16
|
ViewProps,
|
|
18
17
|
} from 'react-native';
|
|
@@ -20,6 +19,7 @@ import { Backdrop } from '../../atoms/backdrop/backdrop.component';
|
|
|
20
19
|
import { Stylesheet } from './popover-action.styles';
|
|
21
20
|
import { Swipe } from '../../molecules/swipe/swipe.component';
|
|
22
21
|
import { ThemeCtx } from '../../../context/theme.context';
|
|
22
|
+
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
23
23
|
|
|
24
24
|
type PopOverActionProps = {
|
|
25
25
|
isVisible: boolean;
|
|
@@ -51,18 +51,29 @@ export const PopOverAction = ({
|
|
|
51
51
|
) => {
|
|
52
52
|
// Ignore zero-height layouts which can occur before content renders,
|
|
53
53
|
// otherwise the sheet may collapse and appear to lose content.
|
|
54
|
-
if (layout?.height
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
54
|
+
if (!layout?.height || layout.height <= 0) return;
|
|
55
|
+
|
|
56
|
+
setChildDimensions((prev) => {
|
|
57
|
+
const existing = prev[index];
|
|
58
|
+
// Some children mount their real content a tick after an initial empty
|
|
59
|
+
// render (e.g. TimePicker), so a later layout call can briefly report a
|
|
60
|
+
// smaller height than what was already measured. Keep the tallest
|
|
61
|
+
// measurement seen instead of last-write-wins, otherwise the sheet can
|
|
62
|
+
// settle on a stale, too-small height and clip content.
|
|
63
|
+
if (existing && existing.height >= layout.height) return prev;
|
|
64
|
+
const updated = [...prev];
|
|
65
|
+
updated[index] = layout;
|
|
66
|
+
return updated;
|
|
67
|
+
});
|
|
60
68
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
69
|
+
// Ensure element size is known for the currently visible child so the animation can open
|
|
70
|
+
// even if Swipe never emits onIndexChange (e.g., only one child or Swipe not implemented yet).
|
|
71
|
+
if (index === currentChildIndex) {
|
|
72
|
+
setElementSize((prev) =>
|
|
73
|
+
prev.height >= layout.height
|
|
74
|
+
? prev
|
|
75
|
+
: { width: layout.width, height: layout.height }
|
|
76
|
+
);
|
|
66
77
|
}
|
|
67
78
|
};
|
|
68
79
|
|
|
@@ -102,11 +113,7 @@ export const PopOverAction = ({
|
|
|
102
113
|
|
|
103
114
|
useEffect(() => {
|
|
104
115
|
const updateWindowHeight = () => {
|
|
105
|
-
|
|
106
|
-
if (Platform.OS === 'android') {
|
|
107
|
-
availableHeight += StatusBar.currentHeight ?? 0;
|
|
108
|
-
}
|
|
109
|
-
setWindowHeight(availableHeight);
|
|
116
|
+
setWindowHeight(Dimensions.get('window').height);
|
|
110
117
|
};
|
|
111
118
|
|
|
112
119
|
updateWindowHeight();
|
|
@@ -117,10 +124,16 @@ export const PopOverAction = ({
|
|
|
117
124
|
|
|
118
125
|
const context = useContext(ThemeCtx);
|
|
119
126
|
const styles = Stylesheet(context);
|
|
127
|
+
const insets = useSafeAreaInsets();
|
|
120
128
|
|
|
121
129
|
const elementHeight = Math.round(elementSize.height);
|
|
122
130
|
const maxHeight = Math.round(windowHeight * 0.9);
|
|
123
|
-
|
|
131
|
+
// On Android, Dimensions.get('window') excludes the space behind the
|
|
132
|
+
// navigation bar, but the sheet's container renders behind it (edge-to-edge).
|
|
133
|
+
// Without this, the sheet doesn't travel far enough down when closed and its
|
|
134
|
+
// top edge peeks out from behind the navigation bar.
|
|
135
|
+
const translateYStartingPoint =
|
|
136
|
+
windowHeight + (Platform.OS === 'android' ? insets.bottom : 0);
|
|
124
137
|
const saveHeight = elementHeight > maxHeight ? maxHeight : elementHeight;
|
|
125
138
|
|
|
126
139
|
const translateY = React.useRef(new Animated.Value(translateYStartingPoint)).current;
|
|
@@ -166,15 +179,28 @@ export const PopOverAction = ({
|
|
|
166
179
|
<Backdrop isVisible={isVisible} touchBackDrop={touchBackDrop} />
|
|
167
180
|
<Animated.View
|
|
168
181
|
style={[styles.slidePanelContainer, {maxHeight: maxHeight}, {transform: transform}]}
|
|
169
|
-
onLayout={(event) => {
|
|
170
|
-
const {width, height} = event.nativeEvent.layout;
|
|
171
|
-
setElementSize({width: width, height: height});
|
|
172
|
-
}}
|
|
173
182
|
>
|
|
174
183
|
<Swipe onIndexChange={handleChildIndexChange}>
|
|
175
184
|
{renderChildrenWithLayout(children, handleChildLayout)}
|
|
176
185
|
</Swipe>
|
|
177
186
|
</Animated.View>
|
|
187
|
+
{/* Hidden pass so every child's height is measured up front, regardless of
|
|
188
|
+
which pages the Swipe pager actually keeps mounted (it doesn't guarantee
|
|
189
|
+
all of them are laid out ahead of time). Without this, swiping to a page
|
|
190
|
+
that hasn't been measured yet leaves the sheet at the previous page's height.
|
|
191
|
+
Height is fixed to maxHeight (rather than left to shrink-wrap) so that a child
|
|
192
|
+
using a percentage height (e.g. height: '100%') resolves against a real value
|
|
193
|
+
instead of collapsing to 0, which otherwise leaves the sheet stuck fully closed. */}
|
|
194
|
+
<View
|
|
195
|
+
style={[
|
|
196
|
+
styles.slidePanelContainer,
|
|
197
|
+
styles.measurementContainer,
|
|
198
|
+
{height: maxHeight},
|
|
199
|
+
]}
|
|
200
|
+
pointerEvents="none"
|
|
201
|
+
>
|
|
202
|
+
{renderChildrenWithLayout(children, handleChildLayout)}
|
|
203
|
+
</View>
|
|
178
204
|
</View>
|
|
179
205
|
);
|
|
180
206
|
};
|
|
@@ -5,12 +5,135 @@ import {PopOverAction} from './popover-action.component';
|
|
|
5
5
|
import {Heading2} from '../../atoms/heading-components';
|
|
6
6
|
import {Icon} from '../../../icons/index';
|
|
7
7
|
import {Paragraph} from '../../atoms/paragraph-components';
|
|
8
|
-
import {
|
|
8
|
+
import {MoreInfoButton} from '../../molecules/more-info-button/more-info-button.component';
|
|
9
|
+
import {BareTimePicker} from '../../molecules/bare-time-picker/bare-time-picker.component';
|
|
10
|
+
import {Scale} from '../../../theme/scale/index';
|
|
11
|
+
|
|
12
|
+
// Mirrors Toddle-For-Daycares' src/modals/actions-single-child/actions-single-child.modal.tsx,
|
|
13
|
+
// which renders this exact shape as the two PopOverAction pages shown when a caregiver taps a
|
|
14
|
+
// child in the home list. Each page root uses height:'100%'/width:'100%' (actions-single-child's
|
|
15
|
+
// and action-content's `rootContainer`), and the time picker is `BareTimePicker`, not `TimePicker`
|
|
16
|
+
// - both details matter for reproducing the sizing bug reported in production.
|
|
17
|
+
// Called as a plain function (not JSX) so the View it returns is the direct child of
|
|
18
|
+
// PopOverAction - PopOverAction measures each direct child via cloneElement(child, {onLayout}),
|
|
19
|
+
// which only reaches a real host View, not a custom component placed between them.
|
|
20
|
+
const renderActionContent = ({
|
|
21
|
+
pageKey,
|
|
22
|
+
actionLabel,
|
|
23
|
+
childDisplayName,
|
|
24
|
+
buttonLabel,
|
|
25
|
+
secondaryButtonLabel,
|
|
26
|
+
hours,
|
|
27
|
+
minutes,
|
|
28
|
+
setHours,
|
|
29
|
+
setMinutes,
|
|
30
|
+
}: {
|
|
31
|
+
pageKey: string;
|
|
32
|
+
actionLabel: string;
|
|
33
|
+
childDisplayName: string;
|
|
34
|
+
buttonLabel: string;
|
|
35
|
+
secondaryButtonLabel: string;
|
|
36
|
+
hours: string;
|
|
37
|
+
minutes: string;
|
|
38
|
+
setHours: (value: string) => void;
|
|
39
|
+
setMinutes: (value: string) => void;
|
|
40
|
+
}) => (
|
|
41
|
+
<View key={pageKey} style={{height: '100%', width: '100%'}}>
|
|
42
|
+
<View style={{alignItems: 'center', marginBottom: Scale.s}}>
|
|
43
|
+
<Heading2 bold={true} textAlign={'center'}>
|
|
44
|
+
{actionLabel}
|
|
45
|
+
</Heading2>
|
|
46
|
+
|
|
47
|
+
<View
|
|
48
|
+
style={{
|
|
49
|
+
flexDirection: 'row',
|
|
50
|
+
justifyContent: 'center',
|
|
51
|
+
alignItems: 'center',
|
|
52
|
+
paddingTop: Scale.xs,
|
|
53
|
+
paddingBottom: Scale.xs,
|
|
54
|
+
}}
|
|
55
|
+
>
|
|
56
|
+
<Icon style={'regular'} name={'calendar'} color={'#000000'} />
|
|
57
|
+
<View style={{marginLeft: Scale.xs}}>
|
|
58
|
+
<Paragraph>17 Jan 2021</Paragraph>
|
|
59
|
+
</View>
|
|
60
|
+
</View>
|
|
61
|
+
</View>
|
|
62
|
+
|
|
63
|
+
<View style={{justifyContent: 'center'}}>
|
|
64
|
+
<View
|
|
65
|
+
style={{
|
|
66
|
+
marginLeft: 'auto',
|
|
67
|
+
marginRight: 'auto',
|
|
68
|
+
alignItems: 'center',
|
|
69
|
+
justifyContent: 'center',
|
|
70
|
+
marginBottom: Scale.l,
|
|
71
|
+
}}
|
|
72
|
+
>
|
|
73
|
+
<View
|
|
74
|
+
style={{
|
|
75
|
+
borderRadius: 50,
|
|
76
|
+
alignItems: 'center',
|
|
77
|
+
justifyContent: 'center',
|
|
78
|
+
height: 40,
|
|
79
|
+
maxHeight: 40,
|
|
80
|
+
paddingLeft: Scale.s,
|
|
81
|
+
paddingTop: Scale.xs,
|
|
82
|
+
paddingRight: Scale.s,
|
|
83
|
+
paddingBottom: Scale.xs,
|
|
84
|
+
backgroundColor: '#E5E5E5',
|
|
85
|
+
}}
|
|
86
|
+
>
|
|
87
|
+
<Paragraph
|
|
88
|
+
bold={true}
|
|
89
|
+
textAlign={'center'}
|
|
90
|
+
ellipsizeMode={'tail'}
|
|
91
|
+
numberOfLines={1}
|
|
92
|
+
>
|
|
93
|
+
{childDisplayName}
|
|
94
|
+
</Paragraph>
|
|
95
|
+
</View>
|
|
96
|
+
</View>
|
|
97
|
+
|
|
98
|
+
<View
|
|
99
|
+
style={{
|
|
100
|
+
alignItems: 'center',
|
|
101
|
+
justifyContent: 'center',
|
|
102
|
+
marginBottom: Scale.m,
|
|
103
|
+
}}
|
|
104
|
+
>
|
|
105
|
+
<BareTimePicker
|
|
106
|
+
initialHours={hours}
|
|
107
|
+
initialMinutes={minutes}
|
|
108
|
+
onChangeHours={setHours}
|
|
109
|
+
onChangeMinutes={setMinutes}
|
|
110
|
+
/>
|
|
111
|
+
</View>
|
|
112
|
+
|
|
113
|
+
<View
|
|
114
|
+
style={{
|
|
115
|
+
alignItems: 'center',
|
|
116
|
+
marginBottom: Scale.m * 3.75,
|
|
117
|
+
gap: Scale.m * 1.25,
|
|
118
|
+
}}
|
|
119
|
+
>
|
|
120
|
+
<Button label={buttonLabel} onPress={() => console.log('pressed')} />
|
|
121
|
+
<MoreInfoButton
|
|
122
|
+
label={secondaryButtonLabel}
|
|
123
|
+
textColor={'#000000'}
|
|
124
|
+
onPress={() => console.log('secondary pressed')}
|
|
125
|
+
/>
|
|
126
|
+
</View>
|
|
127
|
+
</View>
|
|
128
|
+
</View>
|
|
129
|
+
);
|
|
9
130
|
|
|
10
131
|
export const PopOverActionPreview = () => {
|
|
11
132
|
const [isVisible, setIsVisible] = useState<boolean>(false);
|
|
12
133
|
const [checkInHours, setCheckInHours] = useState('09');
|
|
13
134
|
const [checkInMinutes, setCheckInMinutes] = useState('06');
|
|
135
|
+
const [messageHours, setMessageHours] = useState('09');
|
|
136
|
+
const [messageMinutes, setMessageMinutes] = useState('06');
|
|
14
137
|
|
|
15
138
|
return (
|
|
16
139
|
<View style={{width: '100%', height: '100%', zIndex: 10000}}>
|
|
@@ -22,120 +145,31 @@ export const PopOverActionPreview = () => {
|
|
|
22
145
|
</View>
|
|
23
146
|
|
|
24
147
|
<PopOverAction isVisible={isVisible} touchBackDrop={setIsVisible}>
|
|
25
|
-
{/*
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
</Heading2>
|
|
38
|
-
|
|
39
|
-
<View
|
|
40
|
-
style={{
|
|
41
|
-
flexDirection: 'row',
|
|
42
|
-
gap: 16,
|
|
43
|
-
justifyContent: 'center',
|
|
44
|
-
alignItems: 'center',
|
|
45
|
-
}}
|
|
46
|
-
>
|
|
47
|
-
<Icon style={'regular'} name={'calendar'} color={'#000000'} />
|
|
48
|
-
<View>
|
|
49
|
-
<Paragraph>17 Jan 2021</Paragraph>
|
|
50
|
-
</View>
|
|
51
|
-
</View>
|
|
52
|
-
</View>
|
|
53
|
-
<TimePicker
|
|
54
|
-
initialHours={checkInHours}
|
|
55
|
-
initialMinutes={checkInMinutes}
|
|
56
|
-
onChangeHours={setCheckInHours}
|
|
57
|
-
onChangeMinutes={setCheckInMinutes}
|
|
58
|
-
/>
|
|
59
|
-
</View>
|
|
60
|
-
|
|
61
|
-
{/*SECOND CHILD*/}
|
|
62
|
-
|
|
63
|
-
<View
|
|
64
|
-
style={{
|
|
65
|
-
alignItems: 'center',
|
|
66
|
-
gap: 60,
|
|
67
|
-
height: 400,
|
|
68
|
-
}}
|
|
69
|
-
key={2}
|
|
70
|
-
>
|
|
71
|
-
<View style={{gap: 12}}>
|
|
72
|
-
<Heading2 bold={true} textAlign={'center'}>
|
|
73
|
-
Uurregistratie
|
|
74
|
-
</Heading2>
|
|
75
|
-
|
|
76
|
-
<View
|
|
77
|
-
style={{
|
|
78
|
-
flexDirection: 'row',
|
|
79
|
-
gap: 16,
|
|
80
|
-
justifyContent: 'center',
|
|
81
|
-
alignItems: 'center',
|
|
82
|
-
}}
|
|
83
|
-
>
|
|
84
|
-
<Icon style={'regular'} name={'calendar'} color={'#000000'} />
|
|
85
|
-
<View>
|
|
86
|
-
<Paragraph>17 Jan 2021</Paragraph>
|
|
87
|
-
</View>
|
|
88
|
-
</View>
|
|
89
|
-
</View>
|
|
90
|
-
|
|
91
|
-
<TimePicker
|
|
92
|
-
initialHours={checkInHours}
|
|
93
|
-
initialMinutes={checkInMinutes}
|
|
94
|
-
onChangeHours={setCheckInHours}
|
|
95
|
-
onChangeMinutes={setCheckInMinutes}
|
|
96
|
-
/>
|
|
148
|
+
{/* CHECK IN / OUT - matches the modal's incomplete-presence branch */}
|
|
149
|
+
{renderActionContent({
|
|
150
|
+
pageKey: 'checkin',
|
|
151
|
+
actionLabel: 'Uurregistratie',
|
|
152
|
+
childDisplayName: 'Nora',
|
|
153
|
+
buttonLabel: 'Check in',
|
|
154
|
+
secondaryButtonLabel: 'View registrations',
|
|
155
|
+
hours: checkInHours,
|
|
156
|
+
minutes: checkInMinutes,
|
|
157
|
+
setHours: setCheckInHours,
|
|
158
|
+
setMinutes: setCheckInMinutes,
|
|
159
|
+
})}
|
|
97
160
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
height: 600,
|
|
111
|
-
}}
|
|
112
|
-
key={3}
|
|
113
|
-
>
|
|
114
|
-
<View style={{gap: 12}}>
|
|
115
|
-
<Heading2 bold={true} textAlign={'center'}>
|
|
116
|
-
Uurregistratie
|
|
117
|
-
</Heading2>
|
|
118
|
-
|
|
119
|
-
<View
|
|
120
|
-
style={{
|
|
121
|
-
flexDirection: 'row',
|
|
122
|
-
gap: 16,
|
|
123
|
-
justifyContent: 'center',
|
|
124
|
-
alignItems: 'center',
|
|
125
|
-
}}
|
|
126
|
-
>
|
|
127
|
-
<Icon style={'regular'} name={'calendar'} color={'#000000'} />
|
|
128
|
-
<View>
|
|
129
|
-
<Paragraph>17 Jan 2021</Paragraph>
|
|
130
|
-
</View>
|
|
131
|
-
</View>
|
|
132
|
-
</View>
|
|
133
|
-
|
|
134
|
-
<Button
|
|
135
|
-
label={'Test button'}
|
|
136
|
-
onPress={() => console.log('pressed')}
|
|
137
|
-
/>
|
|
138
|
-
</View>
|
|
161
|
+
{/* ADD A MESSAGE - matches the modal's second PopOverAction page */}
|
|
162
|
+
{renderActionContent({
|
|
163
|
+
pageKey: 'message',
|
|
164
|
+
actionLabel: 'Add a message',
|
|
165
|
+
childDisplayName: 'Nora',
|
|
166
|
+
buttonLabel: 'Custom message',
|
|
167
|
+
secondaryButtonLabel: 'Standard messages',
|
|
168
|
+
hours: messageHours,
|
|
169
|
+
minutes: messageMinutes,
|
|
170
|
+
setHours: setMessageHours,
|
|
171
|
+
setMinutes: setMessageMinutes,
|
|
172
|
+
})}
|
|
139
173
|
</PopOverAction>
|
|
140
174
|
</View>
|
|
141
175
|
);
|