@tactics/toddle-styleguide 5.4.43 → 5.4.46

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/babel.config.js CHANGED
@@ -15,6 +15,8 @@ module.exports = function (api) {
15
15
  },
16
16
  ],
17
17
  ],
18
- plugins: [],
18
+ plugins: [
19
+ 'react-native-reanimated/plugin',
20
+ ],
19
21
  };
20
22
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tactics/toddle-styleguide",
3
- "version": "5.4.43",
3
+ "version": "5.4.46",
4
4
  "main": "index.tsx",
5
5
  "types": "index.d.ts",
6
6
  "prepublish": "tsc",
@@ -64,7 +64,9 @@
64
64
  "react-native-web": "~0.21.0",
65
65
  "react-native-wheel-picker-expo": "^0.5.4",
66
66
  "react-native-wheely": "^0.6.0",
67
- "typescript": "~5.9.3"
67
+ "typescript": "~5.9.3",
68
+ "react-native-reanimated": "~4.1.6",
69
+ "react-native-worklets": "0.5.1"
68
70
  },
69
71
  "resolutions": {
70
72
  "@react-native-picker/picker": "2.11.1"
@@ -82,11 +84,12 @@
82
84
  },
83
85
  "scripts": {
84
86
  "preinstall": "npx only-allow pnpm",
85
- "start": "./node_modules/.bin/tsc && ./node_modules/.bin/expo start",
87
+ "start": "./node_modules/.bin/tsc && ./node_modules/.bin/expo start -c",
86
88
  "format": "./node_modules/.bin/prettier --write src/.",
87
89
  "types": "./node_modules/.bin/tsc index.tsx --declaration --allowJs --emitDeclarationOnly --esModuleInterop --jsx react-native --skipLibCheck --target es5",
88
- "android": "expo run:android",
89
- "ios": "expo run:ios",
90
+ "prebuild": "npx expo prebuild --clean",
91
+ "android": "npx expo run:android",
92
+ "ios": "npx expo run:ios",
90
93
  "release:patch": "pnpm version patch --git-tag-version=false",
91
94
  "release:minor": "pnpm version minor --git-tag-version=false",
92
95
  "release:major": "pnpm version major --git-tag-version=false"
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import {useContext, useMemo} from 'react';
2
+ import {useContext, useEffect, useRef} from 'react';
3
3
 
4
4
  import {ThemeCtx} from '../../../context/theme.context';
5
5
  import {Animated} from 'react-native';
@@ -12,34 +12,24 @@ type PopoverBackdropProps = {
12
12
 
13
13
  const Backdrop = ({isVisible, touchBackDrop}: PopoverBackdropProps) => {
14
14
  const context = useContext(ThemeCtx);
15
- const opacity = useMemo(() => new Animated.Value(0), []);
15
+ const opacity = useRef(new Animated.Value(0)).current;
16
16
 
17
17
  const styles = Stylesheet(context);
18
18
 
19
- const open = () => {
19
+ useEffect(() => {
20
20
  Animated.timing(opacity, {
21
- toValue: 0.75,
22
- duration: 80,
21
+ toValue: isVisible ? 0.75 : 0,
22
+ duration: isVisible ? 120 : 300,
23
23
  useNativeDriver: true,
24
24
  }).start();
25
- };
26
-
27
- const close = () => {
28
- Animated.timing(opacity, {
29
- toValue: 0,
30
- duration: 600,
31
- useNativeDriver: true,
32
- }).start();
33
- };
34
-
35
- if (isVisible) {
36
- open();
37
- } else {
38
- close();
39
- }
25
+ }, [isVisible, opacity]);
40
26
 
41
27
  return (
42
28
  <Animated.View
29
+ accessible={true}
30
+ accessibilityRole="button"
31
+ accessibilityLabel="Close modal"
32
+ onAccessibilityTap={() => (touchBackDrop ? touchBackDrop(false) : null)}
43
33
  onTouchStart={() => (touchBackDrop ? touchBackDrop(false) : null)}
44
34
  style={[styles.element, {opacity: opacity}]}
45
35
  />
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import {useContext, useRef} from 'react';
2
+ import {useContext, useEffect, useRef} from 'react';
3
3
 
4
4
  import {View, Animated, Pressable, Easing} from 'react-native';
5
5
  import {ThemeCtx} from '../../../context/theme.context';
@@ -21,12 +21,14 @@ export const Check = ({value, onToggle}: CheckProps) => {
21
21
  outputRange: [0, 27],
22
22
  });
23
23
 
24
- Animated.timing(positionButton, {
25
- toValue: value ? 1 : 0,
26
- duration: 200,
27
- easing: Easing.linear,
28
- useNativeDriver: true,
29
- }).start();
24
+ useEffect(() => {
25
+ Animated.timing(positionButton, {
26
+ toValue: value ? 1 : 0,
27
+ duration: 200,
28
+ easing: Easing.linear,
29
+ useNativeDriver: true,
30
+ }).start();
31
+ }, [value, positionButton]);
30
32
 
31
33
  const animatedStyle = {
32
34
  transform: [
@@ -38,7 +40,13 @@ export const Check = ({value, onToggle}: CheckProps) => {
38
40
 
39
41
  return (
40
42
  <View style={styles.container}>
41
- <Pressable onPress={onToggle}>
43
+ <Pressable
44
+ onPress={onToggle}
45
+ accessible={true}
46
+ accessibilityRole="switch"
47
+ accessibilityState={{ checked: value }}
48
+ accessibilityLabel="Toggle"
49
+ >
42
50
  <View style={styles.toggleContainer}>
43
51
  <Animated.View style={[styles.thumbStyle, animatedStyle]} />
44
52
  </View>
@@ -6,7 +6,7 @@ type PopoverModalProps = {
6
6
  isVisible: boolean;
7
7
  children: React.ReactNode;
8
8
  windowHeight: number;
9
- disableSrollContent?: boolean;
9
+ disableScrollContent?: boolean;
10
10
  };
11
- declare const Modal: ({ isVisible, title, subtitle, onClose, children, windowHeight, disableSrollContent, }: PopoverModalProps) => React.JSX.Element;
11
+ declare const Modal: ({ isVisible, title, subtitle, onClose, children, windowHeight, disableScrollContent, }: PopoverModalProps) => React.JSX.Element;
12
12
  export { Modal as Modal };
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import {useContext, useMemo, useState} from 'react';
2
+ import {useContext, useEffect, useRef, useState} from 'react';
3
3
  import {Animated, View} from 'react-native';
4
4
  import {Stylesheet} from './modal.styles';
5
5
  import {Close} from './close/close.component';
@@ -14,7 +14,7 @@ type PopoverModalProps = {
14
14
  isVisible: boolean;
15
15
  children: React.ReactNode;
16
16
  windowHeight: number;
17
- disableSrollContent?: boolean;
17
+ disableScrollContent?: boolean;
18
18
  };
19
19
 
20
20
  const Modal = ({
@@ -24,7 +24,7 @@ const Modal = ({
24
24
  onClose,
25
25
  children,
26
26
  windowHeight,
27
- disableSrollContent,
27
+ disableScrollContent,
28
28
  }: PopoverModalProps) => {
29
29
  // Keep element and window size in local state.
30
30
  const [elementSize, setElementSize] = useState({width: 0, height: 0});
@@ -40,32 +40,23 @@ const Modal = ({
40
40
 
41
41
  const saveHeight = elementHeight > maxHeight ? maxHeight : elementHeight;
42
42
 
43
- const translateY = useMemo(
44
- () => new Animated.Value(translateYStartingPoint),
45
- [translateYStartingPoint]
46
- );
47
-
48
- const open = () => {
49
- Animated.timing(translateY, {
50
- toValue: translateYStartingPoint - saveHeight,
51
- duration: 500,
52
- useNativeDriver: true,
53
- }).start();
54
- };
43
+ const translateY = useRef(new Animated.Value(translateYStartingPoint)).current;
55
44
 
56
- const close = () => {
45
+ // Animate when visibility changes
46
+ useEffect(() => {
57
47
  Animated.timing(translateY, {
58
- toValue: translateYStartingPoint,
48
+ toValue: isVisible ? translateYStartingPoint - saveHeight : translateYStartingPoint,
59
49
  duration: 500,
60
50
  useNativeDriver: true,
61
51
  }).start();
62
- };
52
+ }, [isVisible, saveHeight, translateYStartingPoint, translateY]);
63
53
 
64
- if (isVisible) {
65
- open();
66
- } else {
67
- close();
68
- }
54
+ // If height changes while visible, jump to new position without replaying from bottom
55
+ useEffect(() => {
56
+ if (!isVisible) return;
57
+ // @ts-ignore setValue exists on Animated.Value
58
+ translateY.setValue(translateYStartingPoint - saveHeight);
59
+ }, [saveHeight, translateYStartingPoint, isVisible, translateY]);
69
60
 
70
61
  const transform = [{translateY: translateY}];
71
62
 
@@ -76,11 +67,14 @@ const Modal = ({
76
67
  const {width, height} = event.nativeEvent.layout;
77
68
  setElementSize({width: width, height: height});
78
69
  }}
70
+ accessibilityViewIsModal={true}
71
+ importantForAccessibility="yes"
72
+ accessibilityLabel={title}
79
73
  >
80
74
  <Close onPress={onClose} />
81
75
  <Animated.View style={styles.inner}>
82
76
  {title ? <Heading title={title} subtitle={subtitle} /> : null}
83
- {disableSrollContent ? (
77
+ {disableScrollContent ? (
84
78
  <View>{children}</View>
85
79
  ) : (
86
80
  <ScrollContent>{children}</ScrollContent>
@@ -56,7 +56,7 @@ const Popover = ({
56
56
  onClose={onClose}
57
57
  title={title}
58
58
  subtitle={subtitle}
59
- disableSrollContent={disableScrollContent}
59
+ disableScrollContent={disableScrollContent}
60
60
  >
61
61
  {children}
62
62
  </Modal>
@@ -28,10 +28,10 @@ type PopOverActionProps = {
28
28
  };
29
29
 
30
30
  export const PopOverAction = ({
31
- isVisible,
32
- children,
33
- touchBackDrop,
34
- }: PopOverActionProps) => {
31
+ isVisible,
32
+ children,
33
+ touchBackDrop,
34
+ }: PopOverActionProps) => {
35
35
  const [windowHeight, setWindowHeight] = useState(
36
36
  Dimensions.get('window').height
37
37
  );
@@ -54,6 +54,12 @@ export const PopOverAction = ({
54
54
  updated[index] = layout;
55
55
  return updated;
56
56
  });
57
+
58
+ // Ensure element size is known for the currently visible child so the animation can open
59
+ // even if Swipe never emits onIndexChange (e.g., only one child or Swipe not implemented yet).
60
+ if (index === currentChildIndex) {
61
+ setElementSize({ width: layout.width, height: layout.height });
62
+ }
57
63
  };
58
64
 
59
65
  function renderChildrenWithLayout(
@@ -109,14 +115,11 @@ export const PopOverAction = ({
109
115
  const styles = Stylesheet(context);
110
116
 
111
117
  const elementHeight = Math.round(elementSize.height);
112
- const maxHeight = Math.round(windowHeight * 0.8);
118
+ const maxHeight = Math.round(windowHeight * 0.9);
113
119
  const translateYStartingPoint = windowHeight;
114
120
  const saveHeight = elementHeight > maxHeight ? maxHeight : elementHeight;
115
121
 
116
- const translateY = useMemo(
117
- () => new Animated.Value(translateYStartingPoint),
118
- [translateYStartingPoint]
119
- );
122
+ const translateY = React.useRef(new Animated.Value(translateYStartingPoint)).current;
120
123
 
121
124
  const open = () => {
122
125
  Animated.timing(translateY, {
@@ -137,7 +140,17 @@ export const PopOverAction = ({
137
140
  useEffect(() => {
138
141
  if (isVisible) open();
139
142
  else close();
140
- }, [isVisible, saveHeight]);
143
+ }, [isVisible]);
144
+
145
+ // When the content height changes while the popover is open, adjust the position
146
+ // instantly to match the new height instead of replaying the open animation.
147
+ useEffect(() => {
148
+ if (!isVisible) return;
149
+ // Jump to the new position without animating from the bottom again
150
+ translateY.stopAnimation?.(() => {});
151
+ // @ts-ignore - setValue exists on Animated.Value
152
+ translateY.setValue(translateYStartingPoint - saveHeight);
153
+ }, [saveHeight, isVisible, translateYStartingPoint]);
141
154
 
142
155
  const transform = [{ translateY }];
143
156
 
@@ -147,13 +160,12 @@ export const PopOverAction = ({
147
160
  pointerEvents={isVisible ? 'box-none' : 'none'}
148
161
  >
149
162
  <Backdrop isVisible={isVisible} touchBackDrop={touchBackDrop} />
150
-
151
163
  <Animated.View
152
- style={[
153
- styles.slidePanelContainer,
154
- { maxHeight },
155
- { transform },
156
- ]}
164
+ style={[styles.slidePanelContainer, {maxHeight: maxHeight}, {transform: transform}]}
165
+ onLayout={(event) => {
166
+ const {width, height} = event.nativeEvent.layout;
167
+ setElementSize({width: width, height: height});
168
+ }}
157
169
  >
158
170
  <Swipe onIndexChange={handleChildIndexChange}>
159
171
  {renderChildrenWithLayout(children, handleChildLayout)}