@butternutbox/pawprint-native 1.6.3 → 2.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.
Files changed (44) hide show
  1. package/.turbo/turbo-build.log +9 -9
  2. package/CHANGELOG.md +52 -0
  3. package/COMPONENT_GUIDELINES.md +57 -1
  4. package/dist/index.cjs +1459 -1380
  5. package/dist/index.cjs.map +1 -1
  6. package/dist/index.d.cts +81 -117
  7. package/dist/index.d.ts +81 -117
  8. package/dist/index.js +1206 -1125
  9. package/dist/index.js.map +1 -1
  10. package/package.json +1 -1
  11. package/src/__mocks__/react-native.tsx +33 -5
  12. package/src/__mocks__/rn-primitives/portal.tsx +71 -0
  13. package/src/components/atoms/Input/InputField.tsx +12 -2
  14. package/src/components/molecules/Drawer/Drawer.test.tsx +54 -2
  15. package/src/components/molecules/Drawer/DrawerBody.tsx +2 -17
  16. package/src/components/molecules/Drawer/DrawerContent.tsx +49 -2
  17. package/src/components/molecules/Drawer/DrawerOverlay.tsx +28 -16
  18. package/src/components/molecules/SelectField/AsyncSearch.test.tsx +373 -0
  19. package/src/components/molecules/SelectField/SelectDrawerField.stories.tsx +192 -0
  20. package/src/components/molecules/SelectField/SelectDrawerField.test.tsx +210 -0
  21. package/src/components/molecules/SelectField/SelectDrawerField.tsx +245 -0
  22. package/src/components/molecules/SelectField/SelectField.stories.tsx +221 -421
  23. package/src/components/molecules/SelectField/SelectField.test.tsx +214 -662
  24. package/src/components/molecules/SelectField/SelectField.tsx +269 -539
  25. package/src/components/molecules/SelectField/SelectFieldDropdown.tsx +90 -0
  26. package/src/components/molecules/SelectField/SelectFieldList.tsx +88 -0
  27. package/src/components/molecules/SelectField/SelectFieldOption.tsx +90 -0
  28. package/src/components/molecules/SelectField/core/index.ts +8 -0
  29. package/src/components/molecules/SelectField/core/listHeight.test.ts +22 -0
  30. package/src/components/molecules/SelectField/core/listHeight.ts +8 -0
  31. package/src/components/molecules/SelectField/core/options.ts +35 -0
  32. package/src/components/molecules/SelectField/core/useSelectOptions.ts +116 -0
  33. package/src/components/molecules/SelectField/index.ts +12 -9
  34. package/src/index.ts +0 -1
  35. package/src/test-utils.tsx +5 -1
  36. package/vitest.config.ts +7 -0
  37. package/src/components/molecules/SelectField/SelectFieldContent.tsx +0 -81
  38. package/src/components/molecules/SelectField/SelectFieldItem.tsx +0 -133
  39. package/src/components/molecules/SelectField/SelectFieldTrigger.tsx +0 -188
  40. package/src/components/molecules/SelectField/SelectFieldValue.tsx +0 -33
  41. package/src/components/molecules/SelectField/hooks/index.ts +0 -2
  42. package/src/components/molecules/SelectField/hooks/useSelectField.ts +0 -84
  43. package/src/hooks/index.ts +0 -1
  44. package/src/hooks/useDrawerKeyboardAvoidance.ts +0 -29
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@butternutbox/pawprint-native",
3
- "version": "1.6.3",
3
+ "version": "2.0.0",
4
4
  "type": "module",
5
5
  "description": "ButternutBox Pawprint Design System - React Native Components",
6
6
  "main": "./dist/index.cjs",
@@ -28,6 +28,15 @@ const attachMeasure =
28
28
  MEASURED_TRIGGER.pageX,
29
29
  MEASURED_TRIGGER.pageY
30
30
  )
31
+ ;(node as unknown as { measureInWindow: unknown }).measureInWindow = (
32
+ callback: (x: number, y: number, width: number, height: number) => void
33
+ ) =>
34
+ callback(
35
+ MEASURED_TRIGGER.pageX,
36
+ MEASURED_TRIGGER.pageY,
37
+ MEASURED_TRIGGER.width,
38
+ MEASURED_TRIGGER.height
39
+ )
31
40
  }
32
41
  if (typeof ref === "function") ref(node)
33
42
  else if (ref) (ref as React.RefObject<HTMLDivElement | null>).current = node
@@ -162,11 +171,13 @@ export const TextInput = React.forwardRef<HTMLInputElement, any>(
162
171
  onChangeText,
163
172
  onChange,
164
173
  onBlur,
174
+ onFocus,
165
175
  value,
166
176
  defaultValue,
167
177
  placeholder,
168
178
  editable,
169
179
  keyboardType: _keyboardType,
180
+ accessibilityRole,
170
181
  accessibilityState,
171
182
  ..._rest
172
183
  },
@@ -180,7 +191,10 @@ export const TextInput = React.forwardRef<HTMLInputElement, any>(
180
191
  defaultValue={defaultValue}
181
192
  placeholder={placeholder}
182
193
  disabled={editable === false}
194
+ role={accessibilityRole}
183
195
  aria-disabled={accessibilityState?.disabled}
196
+ aria-expanded={accessibilityState?.expanded}
197
+ onFocus={onFocus}
184
198
  onBlur={onBlur}
185
199
  onChange={(e) => {
186
200
  onChangeText?.(e.target.value)
@@ -355,9 +369,16 @@ export const PanResponder = {
355
369
  }
356
370
 
357
371
  // ─── Easing ──────────────────────────────────────────────────────────────
372
+ type EasingFn = (t: number) => number
373
+
358
374
  export const Easing = {
359
375
  linear: (t: number) => t,
360
376
  ease: (t: number) => t,
377
+ quad: (t: number) => t * t,
378
+ cubic: (t: number) => t * t * t,
379
+ in: (fn: EasingFn = (t) => t) => fn,
380
+ out: (fn: EasingFn = (t) => t) => fn,
381
+ inOut: (fn: EasingFn = (t) => t) => fn,
361
382
  bezier: (_x1: number, _y1: number, _x2: number, _y2: number) => (t: number) =>
362
383
  t
363
384
  }
@@ -368,18 +389,25 @@ export const Platform = {
368
389
  select: (obj: any) => obj.ios ?? obj.default
369
390
  }
370
391
 
371
- const keyboardHandlers = new Map<string, Set<() => void>>()
392
+ const keyboardHandlers = new Map<string, Set<(event?: unknown) => void>>()
393
+
394
+ const DEFAULT_KEYBOARD_EVENT = {
395
+ endCoordinates: { height: 336, screenX: 0, screenY: 476, width: 375 }
396
+ }
372
397
 
373
398
  export const Keyboard = {
374
- addListener: (event: string, handler: () => void) => {
399
+ addListener: (event: string, handler: (event?: unknown) => void) => {
375
400
  const handlers = keyboardHandlers.get(event) ?? new Set()
376
401
  handlers.add(handler)
377
402
  keyboardHandlers.set(event, handlers)
378
403
  return { remove: () => handlers.delete(handler) }
379
404
  },
380
- dismiss: () => {},
381
- emit: (event: string) => {
382
- keyboardHandlers.get(event)?.forEach((handler) => handler())
405
+ dismiss: () => {
406
+ const active = document.activeElement
407
+ if (active instanceof HTMLElement) active.blur()
408
+ },
409
+ emit: (event: string, payload: unknown = DEFAULT_KEYBOARD_EVENT) => {
410
+ keyboardHandlers.get(event)?.forEach((handler) => handler(payload))
383
411
  }
384
412
  }
385
413
 
@@ -0,0 +1,71 @@
1
+ import React from "react"
2
+
3
+ type PortalMap = Map<string, Map<string, React.ReactNode>>
4
+
5
+ const DEFAULT_HOST = "INTERNAL_PRIMITIVE_DEFAULT_HOST_NAME"
6
+
7
+ const portals: PortalMap = new Map([[DEFAULT_HOST, new Map()]])
8
+ const listeners = new Set<() => void>()
9
+
10
+ const notify = () => listeners.forEach((listener) => listener())
11
+
12
+ const subscribe = (listener: () => void) => {
13
+ listeners.add(listener)
14
+ return () => {
15
+ listeners.delete(listener)
16
+ }
17
+ }
18
+
19
+ let version = 0
20
+ const getSnapshot = () => version
21
+
22
+ export const PortalHost = ({ name = DEFAULT_HOST }: { name?: string }) => {
23
+ React.useSyncExternalStore(subscribe, getSnapshot, getSnapshot)
24
+ const host = portals.get(name)
25
+ if (!host || host.size === 0) return null
26
+ return (
27
+ <>
28
+ {Array.from(host.entries()).map(([key, node]) => (
29
+ <React.Fragment key={key}>{node}</React.Fragment>
30
+ ))}
31
+ </>
32
+ )
33
+ }
34
+ PortalHost.displayName = "PortalHost"
35
+
36
+ export const Portal = ({
37
+ name,
38
+ hostName = DEFAULT_HOST,
39
+ children
40
+ }: {
41
+ name: string
42
+ hostName?: string
43
+ children: React.ReactNode
44
+ }) => {
45
+ React.useEffect(() => {
46
+ const host = portals.get(hostName) ?? new Map()
47
+ host.set(name, children)
48
+ portals.set(hostName, host)
49
+ version += 1
50
+ notify()
51
+ }, [hostName, name, children])
52
+
53
+ React.useEffect(
54
+ () => () => {
55
+ portals.get(hostName)?.delete(name)
56
+ version += 1
57
+ notify()
58
+ },
59
+ [hostName, name]
60
+ )
61
+
62
+ return null
63
+ }
64
+ Portal.displayName = "Portal"
65
+
66
+ export const useModalPortalRoot = () => ({
67
+ ref: { current: null },
68
+ sideOffset: 0,
69
+ onLayout: () => {},
70
+ style: { flex: 1 }
71
+ })
@@ -27,6 +27,7 @@ type InputFieldOwnProps = {
27
27
  containerWidth?: number
28
28
  containerHeight?: number
29
29
  hasInlineHelp?: boolean
30
+ onPress?: () => void
30
31
  }
31
32
 
32
33
  export type InputFieldProps = InputFieldOwnProps &
@@ -131,6 +132,7 @@ export const InputField = React.forwardRef<TextInput, InputFieldProps>(
131
132
  editable,
132
133
  onFocus,
133
134
  onBlur,
135
+ onPress,
134
136
  ...rest
135
137
  },
136
138
  ref
@@ -154,7 +156,11 @@ export const InputField = React.forwardRef<TextInput, InputFieldProps>(
154
156
  },
155
157
  [ref]
156
158
  )
157
- const isDisabled = editable === false
159
+ // `editable={false}` on its own means a disabled field. Paired with
160
+ // `onPress` it means a field that displays a value and opens something else
161
+ // (e.g. SelectDrawerField), which is interactive and must not render in the
162
+ // disabled palette.
163
+ const isDisabled = editable === false && onPress === undefined
158
164
  const hasState = state === "error" || state === "success"
159
165
 
160
166
  const baseBorderWidth = parseTokenValue(borderWidth.field.default)
@@ -242,7 +248,7 @@ export const InputField = React.forwardRef<TextInput, InputFieldProps>(
242
248
  }
243
249
 
244
250
  return (
245
- <Pressable onPress={handleWrapperPress}>
251
+ <Pressable onPress={onPress ?? handleWrapperPress}>
246
252
  <StyledInputWrapper
247
253
  isDisabled={isDisabled}
248
254
  containerWidth={containerWidth}
@@ -275,6 +281,10 @@ export const InputField = React.forwardRef<TextInput, InputFieldProps>(
275
281
  style={style}
276
282
  placeholderTextColor={colour.field.text.placeholder}
277
283
  editable={editable}
284
+ // A non-editable TextInput still captures touches in its own area
285
+ // and does nothing with them, so without this only taps on the
286
+ // surrounding padding would reach the wrapper's `onPress`.
287
+ pointerEvents={onPress ? "none" : undefined}
278
288
  onFocus={handleFocus}
279
289
  onBlur={handleBlur}
280
290
  hasInlineHelp={hasInlineHelp}
@@ -1,7 +1,9 @@
1
1
  import React from "react"
2
- import { screen, fireEvent } from "@testing-library/react"
3
- import { describe, it, expect, vi } from "vitest"
2
+ import { screen, fireEvent, act } from "@testing-library/react"
3
+ import { describe, it, expect, vi, afterEach } from "vitest"
4
+ import { Keyboard } from "react-native"
4
5
  import { renderWithTheme } from "../../../test-utils"
6
+ import { Typography } from "../../atoms/Typography"
5
7
  import { Drawer } from "./Drawer"
6
8
 
7
9
  const BasicDrawer = ({
@@ -40,6 +42,56 @@ const BasicDrawer = ({
40
42
  </Drawer.Root>
41
43
  )
42
44
 
45
+ describe("Drawer keyboard lift", () => {
46
+ afterEach(() => {
47
+ Keyboard.emit("keyboardDidHide", {
48
+ endCoordinates: { height: 0 },
49
+ duration: 0
50
+ })
51
+ })
52
+
53
+ it("renders its content while the keyboard is closed", () => {
54
+ renderWithTheme(
55
+ <Drawer.Root defaultOpen>
56
+ <Drawer.Portal>
57
+ <Drawer.Overlay />
58
+ <Drawer.Content>
59
+ <Drawer.Body>
60
+ <Typography>Row one</Typography>
61
+ </Drawer.Body>
62
+ </Drawer.Content>
63
+ </Drawer.Portal>
64
+ </Drawer.Root>
65
+ )
66
+
67
+ expect(screen.getByText("Row one")).toBeInTheDocument()
68
+ })
69
+
70
+ it("keeps its content when the keyboard opens", () => {
71
+ renderWithTheme(
72
+ <Drawer.Root defaultOpen>
73
+ <Drawer.Portal>
74
+ <Drawer.Overlay />
75
+ <Drawer.Content>
76
+ <Drawer.Body>
77
+ <Typography>Row one</Typography>
78
+ </Drawer.Body>
79
+ </Drawer.Content>
80
+ </Drawer.Portal>
81
+ </Drawer.Root>
82
+ )
83
+
84
+ act(() => {
85
+ Keyboard.emit("keyboardWillShow", {
86
+ endCoordinates: { height: 336 },
87
+ duration: 250
88
+ })
89
+ })
90
+
91
+ expect(screen.getByText("Row one")).toBeInTheDocument()
92
+ })
93
+ })
94
+
43
95
  describe("Drawer", () => {
44
96
  describe("rendering", () => {
45
97
  it("renders the trigger", () => {
@@ -6,11 +6,8 @@ import { useSafeAreaInsets } from "react-native-safe-area-context"
6
6
  import { useDrawerHeaderContext } from "./DrawerHeaderContext"
7
7
  import { useDrawerFooterContext } from "./DrawerFooterContext"
8
8
  import { useDrawerBodyMax } from "./DrawerBodyMaxContext"
9
- import { useDrawerKeyboardAvoidance } from "../../../hooks/useDrawerKeyboardAvoidance"
10
9
 
11
- export type DrawerBodyProps = ScrollViewProps & {
12
- ignoreKeyboardAvoidance?: boolean
13
- }
10
+ export type DrawerBodyProps = ScrollViewProps
14
11
 
15
12
  const parseTokenValue = (value: string): number => parseFloat(value)
16
13
 
@@ -43,15 +40,7 @@ const StyledScrollView = styled(ScrollView)<{
43
40
  * chrome-aware `maxHeight`, scrolling beyond that.
44
41
  */
45
42
  export const DrawerBody = React.forwardRef<ScrollView, DrawerBodyProps>(
46
- (
47
- {
48
- children,
49
- contentContainerStyle,
50
- ignoreKeyboardAvoidance = false,
51
- ...props
52
- },
53
- ref
54
- ) => {
43
+ ({ children, contentContainerStyle, ...props }, ref) => {
55
44
  const theme = useTheme()
56
45
  const { spacing } = theme.tokens.components.drawer
57
46
  const { buttons } = theme.tokens.components
@@ -61,9 +50,6 @@ export const DrawerBody = React.forwardRef<ScrollView, DrawerBodyProps>(
61
50
  const providedMaxHeight = useDrawerBodyMax()
62
51
  const { height: windowHeight } = useWindowDimensions()
63
52
  const insets = useSafeAreaInsets()
64
- const { contentContainerStyle: keyboardStyle } =
65
- useDrawerKeyboardAvoidance()
66
- const effectiveKeyboardStyle = ignoreKeyboardAvoidance ? {} : keyboardStyle
67
53
 
68
54
  const gap = parseTokenValue(content.slot.gap)
69
55
  const horizontalPadding = parseTokenValue(content.slot.horizontalPadding)
@@ -107,7 +93,6 @@ export const DrawerBody = React.forwardRef<ScrollView, DrawerBodyProps>(
107
93
  paddingTop: topPadding,
108
94
  paddingBottom: 0
109
95
  },
110
- effectiveKeyboardStyle,
111
96
  contentContainerStyle
112
97
  ]}
113
98
  {...props}
@@ -5,6 +5,8 @@ import {
5
5
  PanResponder,
6
6
  StyleSheet,
7
7
  View,
8
+ Keyboard,
9
+ Platform,
8
10
  useWindowDimensions,
9
11
  type ViewProps
10
12
  } from "react-native"
@@ -22,6 +24,7 @@ import {
22
24
 
23
25
  export type DrawerContentProps = Omit<ViewProps, "style">
24
26
 
27
+ const KEYBOARD_DURATION = 250
25
28
  const OPEN_DURATION = 320
26
29
  const CLOSE_DURATION = 220
27
30
  const DISMISS_THRESHOLD = 80
@@ -108,7 +111,9 @@ export const DrawerContent = React.forwardRef<View, DrawerContentProps>(
108
111
  const { isOpen, closeDrawer, onExitComplete } = useDrawerContext()
109
112
 
110
113
  const { height: windowHeight } = useWindowDimensions()
111
- const maxHeight = windowHeight * 0.9
114
+ const [keyboardHeight, setKeyboardHeight] = useState(0)
115
+ const keyboardSpacer = useRef(new Animated.Value(0)).current
116
+ const maxHeight = (windowHeight - keyboardHeight) * 0.9
112
117
 
113
118
  // Constant parts of the chrome that aren't a measured section: the panel's
114
119
  // top border, and (when there's no footer) the body's own bottom padding.
@@ -134,6 +139,7 @@ export const DrawerContent = React.forwardRef<View, DrawerContentProps>(
134
139
  // ScrollView's `maxHeight`, so tall content scrolls within the cap and short
135
140
  // content lets the panel wrap tight. Null until the chrome is measured, in
136
141
  // which case DrawerBody falls back to its own window-based heuristic.
142
+ const applyBodyMaxRef = useRef<(() => void) | null>(null)
137
143
  const [bodyMax, setBodyMax] = useState<number | null>(null)
138
144
  const bodyMaxRef = useRef<number | null>(null)
139
145
 
@@ -153,6 +159,39 @@ export const DrawerContent = React.forwardRef<View, DrawerContentProps>(
153
159
  return () => translateY.removeListener(id)
154
160
  }, [translateY])
155
161
 
162
+ useEffect(() => {
163
+ const showEvent =
164
+ Platform.OS === "ios" ? "keyboardWillShow" : "keyboardDidShow"
165
+ const hideEvent =
166
+ Platform.OS === "ios" ? "keyboardWillHide" : "keyboardDidHide"
167
+
168
+ // Height, not transform: translating the panel would lift it off the
169
+ // bottom of the screen and show the backdrop beneath it. Height can't be
170
+ // native-driven, but it is one view for the keyboard's own duration.
171
+ const lift = (toValue: number, duration: number | undefined) => {
172
+ Animated.timing(keyboardSpacer, {
173
+ toValue,
174
+ duration: duration && duration > 0 ? duration : KEYBOARD_DURATION,
175
+ easing: Easing.out(Easing.ease),
176
+ useNativeDriver: false
177
+ }).start()
178
+ }
179
+
180
+ const shown = Keyboard.addListener(showEvent, (event) => {
181
+ setKeyboardHeight(event.endCoordinates.height)
182
+ lift(event.endCoordinates.height, event.duration)
183
+ })
184
+ const hidden = Keyboard.addListener(hideEvent, (event) => {
185
+ setKeyboardHeight(0)
186
+ lift(0, event.duration)
187
+ })
188
+
189
+ return () => {
190
+ shown.remove()
191
+ hidden.remove()
192
+ }
193
+ }, [keyboardSpacer])
194
+
156
195
  // Clamp the cached panel height when the window shrinks (e.g. orientation
157
196
  // change) so the entry animation never starts from below the screen. This
158
197
  // is animation-only bookkeeping — the panel itself sizes from its content.
@@ -162,6 +201,10 @@ export const DrawerContent = React.forwardRef<View, DrawerContentProps>(
162
201
  }
163
202
  }, [maxHeight])
164
203
 
204
+ useEffect(() => {
205
+ applyBodyMaxRef.current?.()
206
+ }, [maxHeight])
207
+
165
208
  const runEntry = useCallback(() => {
166
209
  if (panelHeightRef.current === 0) return
167
210
  translateY.setValue(panelHeightRef.current)
@@ -236,6 +279,8 @@ export const DrawerContent = React.forwardRef<View, DrawerContentProps>(
236
279
  }
237
280
  }, [measureChrome, maxHeight])
238
281
 
282
+ applyBodyMaxRef.current = applyBodyMax
283
+
239
284
  const setHeaderHeight = useCallback(
240
285
  (height: number) => {
241
286
  if (height <= 0 || Math.abs(height - headerHeightRef.current) < 1)
@@ -370,6 +415,7 @@ export const DrawerContent = React.forwardRef<View, DrawerContentProps>(
370
415
  <DrawerMeasureContext.Provider value={measureContextValue}>
371
416
  <DrawerDragContext.Provider value={dragContextValue}>
372
417
  <Animated.View
418
+ pointerEvents="box-none"
373
419
  style={[
374
420
  styles.animatedWrapper,
375
421
  { transform: [{ translateY }] }
@@ -381,7 +427,7 @@ export const DrawerContent = React.forwardRef<View, DrawerContentProps>(
381
427
  panelBorderRadius={parseTokenValue(drawer.borderRadius.top)}
382
428
  panelBgColor={colour.background.container.default}
383
429
  panelMaxWidth={parseTokenValue(drawer.size.maxWidth)}
384
- panelMaxHeight={maxHeight}
430
+ panelMaxHeight={maxHeight + keyboardHeight}
385
431
  panelBorderWidth={parseTokenValue(dimensions.borderWidth.sm)}
386
432
  panelBorderColor={colour.border.default}
387
433
  panelShadowColor={modal.shadow.color}
@@ -395,6 +441,7 @@ export const DrawerContent = React.forwardRef<View, DrawerContentProps>(
395
441
  <DrawerBodyMaxContext.Provider value={bodyMax}>
396
442
  {children}
397
443
  </DrawerBodyMaxContext.Provider>
444
+ <Animated.View style={{ height: keyboardSpacer }} />
398
445
  </StyledPanel>
399
446
  </Animated.View>
400
447
  </DrawerDragContext.Provider>
@@ -1,5 +1,10 @@
1
1
  import React, { useEffect, useRef } from "react"
2
- import { Animated, Pressable, StyleSheet } from "react-native"
2
+ import {
3
+ Animated,
4
+ Pressable,
5
+ StyleSheet,
6
+ useWindowDimensions
7
+ } from "react-native"
3
8
  import { useTheme } from "@emotion/react"
4
9
  import { useDrawerContext } from "./DrawerContext"
5
10
 
@@ -19,6 +24,7 @@ export const DrawerOverlay = React.forwardRef<
19
24
  DrawerOverlayProps
20
25
  >(({ accessible = false }, _ref) => {
21
26
  const theme = useTheme()
27
+ const { width: screenWidth, height: screenHeight } = useWindowDimensions()
22
28
  const { isOpen, closeDrawer, backdrop } = useDrawerContext()
23
29
  const opacity = useRef(new Animated.Value(0)).current
24
30
 
@@ -34,22 +40,28 @@ export const DrawerOverlay = React.forwardRef<
34
40
  if (!backdrop) return null
35
41
 
36
42
  return (
37
- <Pressable
38
- style={StyleSheet.absoluteFillObject}
39
- onPress={closeDrawer}
40
- accessible={accessible}
41
- accessibilityLabel="Close drawer"
43
+ <Animated.View
44
+ pointerEvents="box-none"
45
+ style={[
46
+ StyleSheet.absoluteFillObject,
47
+ { width: screenWidth, height: screenHeight, opacity }
48
+ ]}
42
49
  >
43
- <Animated.View
44
- style={[
45
- StyleSheet.absoluteFillObject,
46
- {
47
- backgroundColor: theme.tokens.components.modal.overlay.colour,
48
- opacity
49
- }
50
- ]}
51
- />
52
- </Pressable>
50
+ <Pressable
51
+ style={{ width: screenWidth, height: screenHeight }}
52
+ onPress={closeDrawer}
53
+ accessible={accessible}
54
+ accessibilityLabel="Close drawer"
55
+ >
56
+ <Animated.View
57
+ style={{
58
+ width: screenWidth,
59
+ height: screenHeight,
60
+ backgroundColor: theme.tokens.components.modal.overlay.colour
61
+ }}
62
+ />
63
+ </Pressable>
64
+ </Animated.View>
53
65
  )
54
66
  })
55
67