@tactics/toddle-styleguide 5.4.46 → 5.4.48

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tactics/toddle-styleguide",
3
- "version": "5.4.46",
3
+ "version": "5.4.48",
4
4
  "main": "index.tsx",
5
5
  "types": "index.d.ts",
6
6
  "prepublish": "tsc",
@@ -10,6 +10,8 @@ export function Stylesheet(
10
10
  width: string;
11
11
  textAlign: any;
12
12
  color: any;
13
+ includeFontPadding: boolean;
14
+ textAlignVertical: string;
13
15
  };
14
16
  paragraph: {
15
17
  fontFamily: string;
@@ -10,6 +10,8 @@ export const Stylesheet = (Context, bold, textColor, textAlign) =>
10
10
  color: !textColor
11
11
  ? Context.colors.ui.black
12
12
  : HexColor.of(textColor).toString(),
13
+ includeFontPadding: false,
14
+ textAlignVertical: 'center',
13
15
  },
14
16
  paragraph: [
15
17
  !bold
@@ -69,6 +69,7 @@ const Swipe = ({children, onIndexChange}: SwipeProps) => {
69
69
  initialPage={0}
70
70
  collapsable={false}
71
71
  onPageSelected={handlePageSelected}
72
+ orientation={"horizontal"}
72
73
  >
73
74
  {children}
74
75
  </PagerView>
@@ -24,5 +24,6 @@ export function Stylesheet(): {
24
24
  };
25
25
  swipeContainer: {
26
26
  flex: number;
27
+
27
28
  };
28
29
  };
@@ -28,5 +28,6 @@ export const Stylesheet = () =>
28
28
  },
29
29
  swipeContainer: {
30
30
  flex: 1,
31
+ zIndex: 1,
31
32
  },
32
33
  });
@@ -20,6 +20,7 @@ import { Backdrop } from '../../atoms/backdrop/backdrop.component';
20
20
  import { Stylesheet } from './popover-action.styles';
21
21
  import { Swipe } from '../../molecules/swipe/swipe.component';
22
22
  import { ThemeCtx } from '../../../context/theme.context';
23
+ import { useSafeAreaInsets } from 'react-native-safe-area-context';
23
24
 
24
25
  type PopOverActionProps = {
25
26
  isVisible: boolean;
@@ -49,7 +50,18 @@ export const PopOverAction = ({
49
50
  index: number,
50
51
  layout: LayoutChangeEvent['nativeEvent']['layout']
51
52
  ) => {
53
+ // Ignore zero-height layouts which can occur before content renders,
54
+ // otherwise the sheet may collapse and appear to lose content.
55
+ if (!layout?.height || layout.height <= 0) return;
56
+
52
57
  setChildDimensions((prev) => {
58
+ const existing = prev[index];
59
+ // Some children mount their real content a tick after an initial empty
60
+ // render (e.g. TimePicker), so a later layout call can briefly report a
61
+ // smaller height than what was already measured. Keep the tallest
62
+ // measurement seen instead of last-write-wins, otherwise the sheet can
63
+ // settle on a stale, too-small height and clip content.
64
+ if (existing && existing.height >= layout.height) return prev;
53
65
  const updated = [...prev];
54
66
  updated[index] = layout;
55
67
  return updated;
@@ -58,7 +70,11 @@ export const PopOverAction = ({
58
70
  // Ensure element size is known for the currently visible child so the animation can open
59
71
  // even if Swipe never emits onIndexChange (e.g., only one child or Swipe not implemented yet).
60
72
  if (index === currentChildIndex) {
61
- setElementSize({ width: layout.width, height: layout.height });
73
+ setElementSize((prev) =>
74
+ prev.height >= layout.height
75
+ ? prev
76
+ : { width: layout.width, height: layout.height }
77
+ );
62
78
  }
63
79
  };
64
80
 
@@ -113,10 +129,16 @@ export const PopOverAction = ({
113
129
 
114
130
  const context = useContext(ThemeCtx);
115
131
  const styles = Stylesheet(context);
132
+ const insets = useSafeAreaInsets();
116
133
 
117
134
  const elementHeight = Math.round(elementSize.height);
118
135
  const maxHeight = Math.round(windowHeight * 0.9);
119
- const translateYStartingPoint = windowHeight;
136
+ // On Android, Dimensions.get('window') excludes the space behind the
137
+ // navigation bar, but the sheet's container renders behind it (edge-to-edge).
138
+ // Without this, the sheet doesn't travel far enough down when closed and its
139
+ // top edge peeks out from behind the navigation bar.
140
+ const translateYStartingPoint =
141
+ windowHeight + (Platform.OS === 'android' ? insets.bottom : 0);
120
142
  const saveHeight = elementHeight > maxHeight ? maxHeight : elementHeight;
121
143
 
122
144
  const translateY = React.useRef(new Animated.Value(translateYStartingPoint)).current;
@@ -162,15 +184,28 @@ export const PopOverAction = ({
162
184
  <Backdrop isVisible={isVisible} touchBackDrop={touchBackDrop} />
163
185
  <Animated.View
164
186
  style={[styles.slidePanelContainer, {maxHeight: maxHeight}, {transform: transform}]}
165
- onLayout={(event) => {
166
- const {width, height} = event.nativeEvent.layout;
167
- setElementSize({width: width, height: height});
168
- }}
169
187
  >
170
188
  <Swipe onIndexChange={handleChildIndexChange}>
171
189
  {renderChildrenWithLayout(children, handleChildLayout)}
172
190
  </Swipe>
173
191
  </Animated.View>
192
+ {/* Hidden pass so every child's height is measured up front, regardless of
193
+ which pages the Swipe pager actually keeps mounted (it doesn't guarantee
194
+ all of them are laid out ahead of time). Without this, swiping to a page
195
+ that hasn't been measured yet leaves the sheet at the previous page's height.
196
+ Height is fixed to maxHeight (rather than left to shrink-wrap) so that a child
197
+ using a percentage height (e.g. height: '100%') resolves against a real value
198
+ instead of collapsing to 0, which otherwise leaves the sheet stuck fully closed. */}
199
+ <View
200
+ style={[
201
+ styles.slidePanelContainer,
202
+ styles.measurementContainer,
203
+ {height: maxHeight},
204
+ ]}
205
+ pointerEvents="none"
206
+ >
207
+ {renderChildrenWithLayout(children, handleChildLayout)}
208
+ </View>
174
209
  </View>
175
210
  );
176
211
  };
@@ -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 {TimePicker} from '../../molecules/time-picker/time-picker.component';
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
- {/*FIRST CHILD*/}
26
- <View
27
- style={{
28
- alignItems: 'center',
29
- gap: 60,
30
- height: 460,
31
- }}
32
- key={1}
33
- >
34
- <View style={{gap: 10}}>
35
- <Heading2 bold={true} textAlign={'center'}>
36
- Uurregistratie
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
- <Button
99
- label={'Test button'}
100
- onPress={() => console.log('pressed')}
101
- />
102
- </View>
103
-
104
- {/*Third CHILD*/}
105
-
106
- <View
107
- style={{
108
- alignItems: 'center',
109
- gap: 60,
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
  );
@@ -25,4 +25,10 @@ export function Stylesheet(context: any): {
25
25
  zIndex: number;
26
26
  backgroundColor: any;
27
27
  };
28
+ measurementContainer: {
29
+ position: 'absolute';
30
+ top: number;
31
+ opacity: number;
32
+ flex: number;
33
+ };
28
34
  };
@@ -30,4 +30,10 @@ export const Stylesheet = (context) =>
30
30
  zIndex: 1004,
31
31
  backgroundColor: context.colors.ui.white,
32
32
  },
33
+ measurementContainer: {
34
+ position: 'absolute',
35
+ top: 0,
36
+ opacity: 0,
37
+ flex: 0,
38
+ },
33
39
  });