@bacon-pos/ui 0.2.1 → 0.3.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 (45) hide show
  1. package/lib/commonjs/components/Dialog/Dialog.js +215 -0
  2. package/lib/commonjs/components/Dialog/Dialog.js.map +1 -0
  3. package/lib/commonjs/components/Dialog/Dialog.stories.js +169 -0
  4. package/lib/commonjs/components/Dialog/Dialog.stories.js.map +1 -0
  5. package/lib/commonjs/components/Dialog/Dialog.test.js +71 -0
  6. package/lib/commonjs/components/Dialog/Dialog.test.js.map +1 -0
  7. package/lib/commonjs/components/Dialog/index.js +13 -0
  8. package/lib/commonjs/components/Dialog/index.js.map +1 -0
  9. package/lib/commonjs/components/_internal/ScrollArea.js +96 -0
  10. package/lib/commonjs/components/_internal/ScrollArea.js.map +1 -0
  11. package/lib/commonjs/components/index.js +22 -11
  12. package/lib/commonjs/components/index.js.map +1 -1
  13. package/lib/module/components/Dialog/Dialog.js +211 -0
  14. package/lib/module/components/Dialog/Dialog.js.map +1 -0
  15. package/lib/module/components/Dialog/Dialog.stories.js +165 -0
  16. package/lib/module/components/Dialog/Dialog.stories.js.map +1 -0
  17. package/lib/module/components/Dialog/Dialog.test.js +70 -0
  18. package/lib/module/components/Dialog/Dialog.test.js.map +1 -0
  19. package/lib/module/components/Dialog/index.js +4 -0
  20. package/lib/module/components/Dialog/index.js.map +1 -0
  21. package/lib/module/components/_internal/ScrollArea.js +92 -0
  22. package/lib/module/components/_internal/ScrollArea.js.map +1 -0
  23. package/lib/module/components/index.js +1 -0
  24. package/lib/module/components/index.js.map +1 -1
  25. package/lib/typescript/commonjs/components/Dialog/Dialog.d.ts +75 -0
  26. package/lib/typescript/commonjs/components/Dialog/Dialog.d.ts.map +1 -0
  27. package/lib/typescript/commonjs/components/Dialog/index.d.ts +2 -0
  28. package/lib/typescript/commonjs/components/Dialog/index.d.ts.map +1 -0
  29. package/lib/typescript/commonjs/components/_internal/ScrollArea.d.ts +20 -0
  30. package/lib/typescript/commonjs/components/_internal/ScrollArea.d.ts.map +1 -0
  31. package/lib/typescript/commonjs/components/index.d.ts +1 -0
  32. package/lib/typescript/commonjs/components/index.d.ts.map +1 -1
  33. package/lib/typescript/module/components/Dialog/Dialog.d.ts +75 -0
  34. package/lib/typescript/module/components/Dialog/Dialog.d.ts.map +1 -0
  35. package/lib/typescript/module/components/Dialog/index.d.ts +2 -0
  36. package/lib/typescript/module/components/Dialog/index.d.ts.map +1 -0
  37. package/lib/typescript/module/components/_internal/ScrollArea.d.ts +20 -0
  38. package/lib/typescript/module/components/_internal/ScrollArea.d.ts.map +1 -0
  39. package/lib/typescript/module/components/index.d.ts +1 -0
  40. package/lib/typescript/module/components/index.d.ts.map +1 -1
  41. package/package.json +1 -1
  42. package/src/components/Dialog/Dialog.tsx +269 -0
  43. package/src/components/Dialog/index.ts +6 -0
  44. package/src/components/_internal/ScrollArea.tsx +95 -0
  45. package/src/components/index.ts +1 -0
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+
3
+ import { useRef, useState } from 'react';
4
+ import { Animated } from 'react-native';
5
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
6
+ /**
7
+ * ScrollView with an elegant custom vertical indicator: thin, rounded, colored,
8
+ * and only visible while scrolling (fades out when idle). The native indicator
9
+ * is hidden so it looks the same on iOS, Android and web.
10
+ */
11
+ export function ScrollArea({
12
+ children,
13
+ indicatorColor,
14
+ indicatorWidth = 4,
15
+ keyboardShouldPersistTaps,
16
+ keyboardDismissMode,
17
+ style,
18
+ contentContainerStyle
19
+ }) {
20
+ const [viewportH, setViewportH] = useState(0);
21
+ const [contentH, setContentH] = useState(0);
22
+ const scrollY = useRef(new Animated.Value(0)).current;
23
+ const opacity = useRef(new Animated.Value(0)).current;
24
+ const hideTimer = useRef(null);
25
+ const scrollable = contentH > viewportH + 1;
26
+ const thumbH = scrollable ? Math.max(24, viewportH / contentH * viewportH) : 0;
27
+ const maxScroll = Math.max(1, contentH - viewportH);
28
+ const translateY = scrollY.interpolate({
29
+ inputRange: [0, maxScroll],
30
+ outputRange: [0, viewportH - thumbH],
31
+ extrapolate: 'clamp'
32
+ });
33
+ const reveal = () => {
34
+ Animated.timing(opacity, {
35
+ toValue: 1,
36
+ duration: 120,
37
+ useNativeDriver: true
38
+ }).start();
39
+ if (hideTimer.current) clearTimeout(hideTimer.current);
40
+ hideTimer.current = setTimeout(() => {
41
+ Animated.timing(opacity, {
42
+ toValue: 0,
43
+ duration: 400,
44
+ useNativeDriver: true
45
+ }).start();
46
+ }, 900);
47
+ };
48
+ return /*#__PURE__*/_jsxs(Animated.View, {
49
+ style: [{
50
+ position: 'relative'
51
+ }, style],
52
+ children: [/*#__PURE__*/_jsx(Animated.ScrollView, {
53
+ style: {
54
+ flexShrink: 1
55
+ },
56
+ contentContainerStyle: contentContainerStyle,
57
+ showsVerticalScrollIndicator: false,
58
+ scrollEventThrottle: 16,
59
+ keyboardShouldPersistTaps: keyboardShouldPersistTaps,
60
+ keyboardDismissMode: keyboardDismissMode,
61
+ onLayout: e => setViewportH(e.nativeEvent.layout.height),
62
+ onContentSizeChange: (_w, h) => setContentH(h),
63
+ onScroll: Animated.event([{
64
+ nativeEvent: {
65
+ contentOffset: {
66
+ y: scrollY
67
+ }
68
+ }
69
+ }], {
70
+ useNativeDriver: true,
71
+ listener: _e => reveal()
72
+ }),
73
+ children: children
74
+ }), scrollable ? /*#__PURE__*/_jsx(Animated.View, {
75
+ pointerEvents: "none",
76
+ style: {
77
+ position: 'absolute',
78
+ top: 0,
79
+ right: 2,
80
+ width: indicatorWidth,
81
+ height: thumbH,
82
+ borderRadius: indicatorWidth / 2,
83
+ backgroundColor: indicatorColor,
84
+ opacity,
85
+ transform: [{
86
+ translateY
87
+ }]
88
+ }
89
+ }) : null]
90
+ });
91
+ }
92
+ //# sourceMappingURL=ScrollArea.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["useRef","useState","Animated","jsx","_jsx","jsxs","_jsxs","ScrollArea","children","indicatorColor","indicatorWidth","keyboardShouldPersistTaps","keyboardDismissMode","style","contentContainerStyle","viewportH","setViewportH","contentH","setContentH","scrollY","Value","current","opacity","hideTimer","scrollable","thumbH","Math","max","maxScroll","translateY","interpolate","inputRange","outputRange","extrapolate","reveal","timing","toValue","duration","useNativeDriver","start","clearTimeout","setTimeout","View","position","ScrollView","flexShrink","showsVerticalScrollIndicator","scrollEventThrottle","onLayout","e","nativeEvent","layout","height","onContentSizeChange","_w","h","onScroll","event","contentOffset","y","listener","_e","pointerEvents","top","right","width","borderRadius","backgroundColor","transform"],"sourceRoot":"..\\..\\..\\..\\src","sources":["components/_internal/ScrollArea.tsx"],"mappings":"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","ignoreList":[]}
@@ -8,6 +8,7 @@ export * from "./Checkbox/index.js";
8
8
  export * from "./Chip/index.js";
9
9
  export * from "./CodeField/index.js";
10
10
  export * from "./CodeInput/index.js";
11
+ export * from "./Dialog/index.js";
11
12
  export * from "./Icon/index.js";
12
13
  export * from "./IconButton/index.js";
13
14
  export * from "./Input/index.js";
@@ -1 +1 @@
1
- {"version":3,"names":[],"sourceRoot":"..\\..\\..\\src","sources":["components/index.ts"],"mappings":";;AAAA,cAAc,mBAAU;AACxB,cAAc,kBAAS;AACvB,cAAc,mBAAU;AACxB,cAAc,iBAAQ;AACtB,cAAc,qBAAY;AAC1B,cAAc,iBAAQ;AACtB,cAAc,sBAAa;AAC3B,cAAc,sBAAa;AAC3B,cAAc,iBAAQ;AACtB,cAAc,uBAAc;AAC5B,cAAc,kBAAS;AACvB,cAAc,uBAAc;AAC5B,cAAc,wBAAe;AAC7B,cAAc,4BAAmB;AACjC,cAAc,kBAAS;AACvB,cAAc,oBAAW;AACzB,cAAc,oBAAW;AACzB,cAAc,mBAAU;AACxB,cAAc,iBAAQ","ignoreList":[]}
1
+ {"version":3,"names":[],"sourceRoot":"..\\..\\..\\src","sources":["components/index.ts"],"mappings":";;AAAA,cAAc,mBAAU;AACxB,cAAc,kBAAS;AACvB,cAAc,mBAAU;AACxB,cAAc,iBAAQ;AACtB,cAAc,qBAAY;AAC1B,cAAc,iBAAQ;AACtB,cAAc,sBAAa;AAC3B,cAAc,sBAAa;AAC3B,cAAc,mBAAU;AACxB,cAAc,iBAAQ;AACtB,cAAc,uBAAc;AAC5B,cAAc,kBAAS;AACvB,cAAc,uBAAc;AAC5B,cAAc,wBAAe;AAC7B,cAAc,4BAAmB;AACjC,cAAc,kBAAS;AACvB,cAAc,oBAAW;AACzB,cAAc,oBAAW;AACzB,cAAc,mBAAU;AACxB,cAAc,iBAAQ","ignoreList":[]}
@@ -0,0 +1,75 @@
1
+ import { type ReactNode } from 'react';
2
+ import { type StyleProp, type ViewStyle } from 'react-native';
3
+ import { type ButtonVariant } from '../Button/Button';
4
+ export interface DialogAction {
5
+ label: string;
6
+ onPress?: () => void;
7
+ /** Button variant. Defaults to `default` (lime). Pass another for other colors. */
8
+ variant?: ButtonVariant;
9
+ loading?: boolean;
10
+ disabled?: boolean;
11
+ }
12
+ export interface DialogCancelConfig {
13
+ /** Cancel label. Defaults to "Cancel". */
14
+ label?: string;
15
+ /** Handler. Defaults to `onClose`. */
16
+ onPress?: () => void;
17
+ /** Use the destructive style (subtle red) instead of the neutral secondary. */
18
+ destructive?: boolean;
19
+ }
20
+ export interface DialogProps {
21
+ /** Whether the dialog is shown. */
22
+ visible: boolean;
23
+ /** Called on backdrop press, close button, or Android back. */
24
+ onClose: () => void;
25
+ /** Required title. */
26
+ title: string;
27
+ /** Optional subtitle. */
28
+ subtitle?: string;
29
+ /** Any content — from text to a full form. */
30
+ children?: ReactNode;
31
+ /** Primary/secondary action buttons (right side). Keep total buttons ≤ 3. */
32
+ actions?: DialogAction[];
33
+ /** Optional cancel button (left side). */
34
+ cancel?: DialogCancelConfig;
35
+ /** Show the top-right close (×). Defaults to true. */
36
+ showClose?: boolean;
37
+ /** Render the close (×) in the destructive color. */
38
+ closeDestructive?: boolean;
39
+ /** Dismiss when the backdrop (overlay) is pressed. Defaults to false. */
40
+ dismissOnBackdrop?: boolean;
41
+ /**
42
+ * `inner` (default): the card caps at the screen height and its content
43
+ * scrolls inside. `page`: the card grows to its content and the whole modal
44
+ * scrolls (nothing behind moves) — better for short screens.
45
+ */
46
+ scrollMode?: 'inner' | 'page';
47
+ /** Keep the header pinned while content scrolls (inner mode). Defaults to true. */
48
+ stickyHeader?: boolean;
49
+ /** Keep the footer pinned while content scrolls (inner mode). Defaults to true. */
50
+ stickyFooter?: boolean;
51
+ /** Inner padding. Defaults to 24. */
52
+ padding?: number;
53
+ /** Vertical space between header↔content and content↔footer. Defaults to 24. */
54
+ contentSpacing?: number;
55
+ /** Corner radius. Defaults to 24. */
56
+ radius?: number;
57
+ /** Max width of the dialog card. Defaults to 560. */
58
+ maxWidth?: number;
59
+ /** Fraction of screen height the card may occupy (inner mode). Defaults to 0.9. */
60
+ maxHeightRatio?: number;
61
+ /** Scroll indicator color (a design-system color). Defaults to a subtle neutral. */
62
+ scrollbarColor?: string;
63
+ /** Override the card style. */
64
+ style?: StyleProp<ViewStyle>;
65
+ }
66
+ /**
67
+ * Dialog — an accessible, keyboard-aware modal that can hold anything from a
68
+ * short message to a full form (selects, date pickers, etc. keep working thanks
69
+ * to `keyboardShouldPersistTaps`). Content scrolls with an elegant thin,
70
+ * auto-hiding indicator. `scrollMode="page"` scrolls the whole modal on short
71
+ * screens instead of scrolling inside the card. Footer composes the
72
+ * design-system `Button`.
73
+ */
74
+ export declare function Dialog({ visible, onClose, title, subtitle, children, actions, cancel, showClose, closeDestructive, dismissOnBackdrop, scrollMode, stickyHeader, stickyFooter, padding, contentSpacing, radius, maxWidth, maxHeightRatio, scrollbarColor, style, }: DialogProps): import("react/jsx-runtime").JSX.Element;
75
+ //# sourceMappingURL=Dialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Dialog.d.ts","sourceRoot":"","sources":["../../../../../src/components/Dialog/Dialog.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,EASL,KAAK,SAAS,EACd,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAU,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAK9D,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,mFAAmF;IACnF,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,0CAA0C;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,+EAA+E;IAC/E,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,WAAW;IAC1B,mCAAmC;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,+DAA+D;IAC/D,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,sBAAsB;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,yBAAyB;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,6EAA6E;IAC7E,OAAO,CAAC,EAAE,YAAY,EAAE,CAAC;IACzB,0CAA0C;IAC1C,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAC5B,sDAAsD;IACtD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,qDAAqD;IACrD,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,yEAAyE;IACzE,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAC9B,mFAAmF;IACnF,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,mFAAmF;IACnF,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,qCAAqC;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gFAAgF;IAChF,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,qCAAqC;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qDAAqD;IACrD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mFAAmF;IACnF,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,oFAAoF;IACpF,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,+BAA+B;IAC/B,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B;AAED;;;;;;;GAOG;AACH,wBAAgB,MAAM,CAAC,EACrB,OAAO,EACP,OAAO,EACP,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,OAAY,EACZ,MAAM,EACN,SAAgB,EAChB,gBAAwB,EACxB,iBAAyB,EACzB,UAAoB,EACpB,YAAmB,EACnB,YAAmB,EACnB,OAAY,EACZ,cAAmB,EACnB,MAAW,EACX,QAAc,EACd,cAAoB,EACpB,cAAc,EACd,KAAK,GACN,EAAE,WAAW,2CA2Jb"}
@@ -0,0 +1,2 @@
1
+ export { Dialog, type DialogProps, type DialogAction, type DialogCancelConfig, } from './Dialog';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/components/Dialog/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,kBAAkB,GACxB,MAAM,UAAU,CAAC"}
@@ -0,0 +1,20 @@
1
+ import { type ReactNode } from 'react';
2
+ import { type StyleProp, type ViewStyle } from 'react-native';
3
+ export interface ScrollAreaProps {
4
+ children: ReactNode;
5
+ /** Thin indicator color (a design-system color). */
6
+ indicatorColor: string;
7
+ /** Indicator thickness. Defaults to 4. */
8
+ indicatorWidth?: number;
9
+ keyboardShouldPersistTaps?: 'always' | 'never' | 'handled';
10
+ keyboardDismissMode?: 'none' | 'on-drag' | 'interactive';
11
+ style?: StyleProp<ViewStyle>;
12
+ contentContainerStyle?: StyleProp<ViewStyle>;
13
+ }
14
+ /**
15
+ * ScrollView with an elegant custom vertical indicator: thin, rounded, colored,
16
+ * and only visible while scrolling (fades out when idle). The native indicator
17
+ * is hidden so it looks the same on iOS, Android and web.
18
+ */
19
+ export declare function ScrollArea({ children, indicatorColor, indicatorWidth, keyboardShouldPersistTaps, keyboardDismissMode, style, contentContainerStyle, }: ScrollAreaProps): import("react/jsx-runtime").JSX.Element;
20
+ //# sourceMappingURL=ScrollArea.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ScrollArea.d.ts","sourceRoot":"","sources":["../../../../../src/components/_internal/ScrollArea.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAoB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACzD,OAAO,EAIL,KAAK,SAAS,EACd,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAEtB,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,SAAS,CAAC;IACpB,oDAAoD;IACpD,cAAc,EAAE,MAAM,CAAC;IACvB,0CAA0C;IAC1C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,yBAAyB,CAAC,EAAE,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAC;IAC3D,mBAAmB,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,aAAa,CAAC;IACzD,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,qBAAqB,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9C;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,EACzB,QAAQ,EACR,cAAc,EACd,cAAkB,EAClB,yBAAyB,EACzB,mBAAmB,EACnB,KAAK,EACL,qBAAqB,GACtB,EAAE,eAAe,2CA4DjB"}
@@ -6,6 +6,7 @@ export * from './Checkbox';
6
6
  export * from './Chip';
7
7
  export * from './CodeField';
8
8
  export * from './CodeInput';
9
+ export * from './Dialog';
9
10
  export * from './Icon';
10
11
  export * from './IconButton';
11
12
  export * from './Input';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,YAAY,CAAC;AAC3B,cAAc,QAAQ,CAAC;AACvB,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,QAAQ,CAAC;AACvB,cAAc,cAAc,CAAC;AAC7B,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,mBAAmB,CAAC;AAClC,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AAEvB,YAAY,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,YAAY,CAAC;AAC3B,cAAc,QAAQ,CAAC;AACvB,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,cAAc,CAAC;AAC7B,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,mBAAmB,CAAC;AAClC,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AAEvB,YAAY,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC"}
@@ -0,0 +1,75 @@
1
+ import { type ReactNode } from 'react';
2
+ import { type StyleProp, type ViewStyle } from 'react-native';
3
+ import { type ButtonVariant } from '../Button/Button';
4
+ export interface DialogAction {
5
+ label: string;
6
+ onPress?: () => void;
7
+ /** Button variant. Defaults to `default` (lime). Pass another for other colors. */
8
+ variant?: ButtonVariant;
9
+ loading?: boolean;
10
+ disabled?: boolean;
11
+ }
12
+ export interface DialogCancelConfig {
13
+ /** Cancel label. Defaults to "Cancel". */
14
+ label?: string;
15
+ /** Handler. Defaults to `onClose`. */
16
+ onPress?: () => void;
17
+ /** Use the destructive style (subtle red) instead of the neutral secondary. */
18
+ destructive?: boolean;
19
+ }
20
+ export interface DialogProps {
21
+ /** Whether the dialog is shown. */
22
+ visible: boolean;
23
+ /** Called on backdrop press, close button, or Android back. */
24
+ onClose: () => void;
25
+ /** Required title. */
26
+ title: string;
27
+ /** Optional subtitle. */
28
+ subtitle?: string;
29
+ /** Any content — from text to a full form. */
30
+ children?: ReactNode;
31
+ /** Primary/secondary action buttons (right side). Keep total buttons ≤ 3. */
32
+ actions?: DialogAction[];
33
+ /** Optional cancel button (left side). */
34
+ cancel?: DialogCancelConfig;
35
+ /** Show the top-right close (×). Defaults to true. */
36
+ showClose?: boolean;
37
+ /** Render the close (×) in the destructive color. */
38
+ closeDestructive?: boolean;
39
+ /** Dismiss when the backdrop (overlay) is pressed. Defaults to false. */
40
+ dismissOnBackdrop?: boolean;
41
+ /**
42
+ * `inner` (default): the card caps at the screen height and its content
43
+ * scrolls inside. `page`: the card grows to its content and the whole modal
44
+ * scrolls (nothing behind moves) — better for short screens.
45
+ */
46
+ scrollMode?: 'inner' | 'page';
47
+ /** Keep the header pinned while content scrolls (inner mode). Defaults to true. */
48
+ stickyHeader?: boolean;
49
+ /** Keep the footer pinned while content scrolls (inner mode). Defaults to true. */
50
+ stickyFooter?: boolean;
51
+ /** Inner padding. Defaults to 24. */
52
+ padding?: number;
53
+ /** Vertical space between header↔content and content↔footer. Defaults to 24. */
54
+ contentSpacing?: number;
55
+ /** Corner radius. Defaults to 24. */
56
+ radius?: number;
57
+ /** Max width of the dialog card. Defaults to 560. */
58
+ maxWidth?: number;
59
+ /** Fraction of screen height the card may occupy (inner mode). Defaults to 0.9. */
60
+ maxHeightRatio?: number;
61
+ /** Scroll indicator color (a design-system color). Defaults to a subtle neutral. */
62
+ scrollbarColor?: string;
63
+ /** Override the card style. */
64
+ style?: StyleProp<ViewStyle>;
65
+ }
66
+ /**
67
+ * Dialog — an accessible, keyboard-aware modal that can hold anything from a
68
+ * short message to a full form (selects, date pickers, etc. keep working thanks
69
+ * to `keyboardShouldPersistTaps`). Content scrolls with an elegant thin,
70
+ * auto-hiding indicator. `scrollMode="page"` scrolls the whole modal on short
71
+ * screens instead of scrolling inside the card. Footer composes the
72
+ * design-system `Button`.
73
+ */
74
+ export declare function Dialog({ visible, onClose, title, subtitle, children, actions, cancel, showClose, closeDestructive, dismissOnBackdrop, scrollMode, stickyHeader, stickyFooter, padding, contentSpacing, radius, maxWidth, maxHeightRatio, scrollbarColor, style, }: DialogProps): import("react/jsx-runtime").JSX.Element;
75
+ //# sourceMappingURL=Dialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Dialog.d.ts","sourceRoot":"","sources":["../../../../../src/components/Dialog/Dialog.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,EASL,KAAK,SAAS,EACd,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAU,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAK9D,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,mFAAmF;IACnF,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,0CAA0C;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,+EAA+E;IAC/E,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,WAAW;IAC1B,mCAAmC;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,+DAA+D;IAC/D,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,sBAAsB;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,yBAAyB;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,6EAA6E;IAC7E,OAAO,CAAC,EAAE,YAAY,EAAE,CAAC;IACzB,0CAA0C;IAC1C,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAC5B,sDAAsD;IACtD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,qDAAqD;IACrD,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,yEAAyE;IACzE,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAC9B,mFAAmF;IACnF,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,mFAAmF;IACnF,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,qCAAqC;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gFAAgF;IAChF,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,qCAAqC;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qDAAqD;IACrD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mFAAmF;IACnF,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,oFAAoF;IACpF,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,+BAA+B;IAC/B,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B;AAED;;;;;;;GAOG;AACH,wBAAgB,MAAM,CAAC,EACrB,OAAO,EACP,OAAO,EACP,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,OAAY,EACZ,MAAM,EACN,SAAgB,EAChB,gBAAwB,EACxB,iBAAyB,EACzB,UAAoB,EACpB,YAAmB,EACnB,YAAmB,EACnB,OAAY,EACZ,cAAmB,EACnB,MAAW,EACX,QAAc,EACd,cAAoB,EACpB,cAAc,EACd,KAAK,GACN,EAAE,WAAW,2CA2Jb"}
@@ -0,0 +1,2 @@
1
+ export { Dialog, type DialogProps, type DialogAction, type DialogCancelConfig, } from './Dialog';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/components/Dialog/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,kBAAkB,GACxB,MAAM,UAAU,CAAC"}
@@ -0,0 +1,20 @@
1
+ import { type ReactNode } from 'react';
2
+ import { type StyleProp, type ViewStyle } from 'react-native';
3
+ export interface ScrollAreaProps {
4
+ children: ReactNode;
5
+ /** Thin indicator color (a design-system color). */
6
+ indicatorColor: string;
7
+ /** Indicator thickness. Defaults to 4. */
8
+ indicatorWidth?: number;
9
+ keyboardShouldPersistTaps?: 'always' | 'never' | 'handled';
10
+ keyboardDismissMode?: 'none' | 'on-drag' | 'interactive';
11
+ style?: StyleProp<ViewStyle>;
12
+ contentContainerStyle?: StyleProp<ViewStyle>;
13
+ }
14
+ /**
15
+ * ScrollView with an elegant custom vertical indicator: thin, rounded, colored,
16
+ * and only visible while scrolling (fades out when idle). The native indicator
17
+ * is hidden so it looks the same on iOS, Android and web.
18
+ */
19
+ export declare function ScrollArea({ children, indicatorColor, indicatorWidth, keyboardShouldPersistTaps, keyboardDismissMode, style, contentContainerStyle, }: ScrollAreaProps): import("react/jsx-runtime").JSX.Element;
20
+ //# sourceMappingURL=ScrollArea.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ScrollArea.d.ts","sourceRoot":"","sources":["../../../../../src/components/_internal/ScrollArea.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAoB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACzD,OAAO,EAIL,KAAK,SAAS,EACd,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAEtB,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,SAAS,CAAC;IACpB,oDAAoD;IACpD,cAAc,EAAE,MAAM,CAAC;IACvB,0CAA0C;IAC1C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,yBAAyB,CAAC,EAAE,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAC;IAC3D,mBAAmB,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,aAAa,CAAC;IACzD,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,qBAAqB,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9C;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,EACzB,QAAQ,EACR,cAAc,EACd,cAAkB,EAClB,yBAAyB,EACzB,mBAAmB,EACnB,KAAK,EACL,qBAAqB,GACtB,EAAE,eAAe,2CA4DjB"}
@@ -6,6 +6,7 @@ export * from './Checkbox';
6
6
  export * from './Chip';
7
7
  export * from './CodeField';
8
8
  export * from './CodeInput';
9
+ export * from './Dialog';
9
10
  export * from './Icon';
10
11
  export * from './IconButton';
11
12
  export * from './Input';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,YAAY,CAAC;AAC3B,cAAc,QAAQ,CAAC;AACvB,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,QAAQ,CAAC;AACvB,cAAc,cAAc,CAAC;AAC7B,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,mBAAmB,CAAC;AAClC,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AAEvB,YAAY,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,YAAY,CAAC;AAC3B,cAAc,QAAQ,CAAC;AACvB,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,cAAc,CAAC;AAC7B,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,mBAAmB,CAAC;AAClC,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AAEvB,YAAY,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bacon-pos/ui",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "Bacon POS design system — React Native component library",
5
5
  "main": "./lib/commonjs/index.js",
6
6
  "module": "./lib/module/index.js",
@@ -0,0 +1,269 @@
1
+ import { type ReactNode } from 'react';
2
+ import {
3
+ KeyboardAvoidingView,
4
+ Modal,
5
+ Platform,
6
+ Pressable,
7
+ ScrollView,
8
+ StyleSheet,
9
+ useWindowDimensions,
10
+ View,
11
+ type StyleProp,
12
+ type ViewStyle,
13
+ } from 'react-native';
14
+ import { useTheme } from '../../theme';
15
+ import { Button, type ButtonVariant } from '../Button/Button';
16
+ import { Icon } from '../Icon/Icon';
17
+ import { Text } from '../Text/Text';
18
+ import { ScrollArea } from '../_internal/ScrollArea';
19
+
20
+ export interface DialogAction {
21
+ label: string;
22
+ onPress?: () => void;
23
+ /** Button variant. Defaults to `default` (lime). Pass another for other colors. */
24
+ variant?: ButtonVariant;
25
+ loading?: boolean;
26
+ disabled?: boolean;
27
+ }
28
+
29
+ export interface DialogCancelConfig {
30
+ /** Cancel label. Defaults to "Cancel". */
31
+ label?: string;
32
+ /** Handler. Defaults to `onClose`. */
33
+ onPress?: () => void;
34
+ /** Use the destructive style (subtle red) instead of the neutral secondary. */
35
+ destructive?: boolean;
36
+ }
37
+
38
+ export interface DialogProps {
39
+ /** Whether the dialog is shown. */
40
+ visible: boolean;
41
+ /** Called on backdrop press, close button, or Android back. */
42
+ onClose: () => void;
43
+ /** Required title. */
44
+ title: string;
45
+ /** Optional subtitle. */
46
+ subtitle?: string;
47
+ /** Any content — from text to a full form. */
48
+ children?: ReactNode;
49
+ /** Primary/secondary action buttons (right side). Keep total buttons ≤ 3. */
50
+ actions?: DialogAction[];
51
+ /** Optional cancel button (left side). */
52
+ cancel?: DialogCancelConfig;
53
+ /** Show the top-right close (×). Defaults to true. */
54
+ showClose?: boolean;
55
+ /** Render the close (×) in the destructive color. */
56
+ closeDestructive?: boolean;
57
+ /** Dismiss when the backdrop (overlay) is pressed. Defaults to false. */
58
+ dismissOnBackdrop?: boolean;
59
+ /**
60
+ * `inner` (default): the card caps at the screen height and its content
61
+ * scrolls inside. `page`: the card grows to its content and the whole modal
62
+ * scrolls (nothing behind moves) — better for short screens.
63
+ */
64
+ scrollMode?: 'inner' | 'page';
65
+ /** Keep the header pinned while content scrolls (inner mode). Defaults to true. */
66
+ stickyHeader?: boolean;
67
+ /** Keep the footer pinned while content scrolls (inner mode). Defaults to true. */
68
+ stickyFooter?: boolean;
69
+ /** Inner padding. Defaults to 24. */
70
+ padding?: number;
71
+ /** Vertical space between header↔content and content↔footer. Defaults to 24. */
72
+ contentSpacing?: number;
73
+ /** Corner radius. Defaults to 24. */
74
+ radius?: number;
75
+ /** Max width of the dialog card. Defaults to 560. */
76
+ maxWidth?: number;
77
+ /** Fraction of screen height the card may occupy (inner mode). Defaults to 0.9. */
78
+ maxHeightRatio?: number;
79
+ /** Scroll indicator color (a design-system color). Defaults to a subtle neutral. */
80
+ scrollbarColor?: string;
81
+ /** Override the card style. */
82
+ style?: StyleProp<ViewStyle>;
83
+ }
84
+
85
+ /**
86
+ * Dialog — an accessible, keyboard-aware modal that can hold anything from a
87
+ * short message to a full form (selects, date pickers, etc. keep working thanks
88
+ * to `keyboardShouldPersistTaps`). Content scrolls with an elegant thin,
89
+ * auto-hiding indicator. `scrollMode="page"` scrolls the whole modal on short
90
+ * screens instead of scrolling inside the card. Footer composes the
91
+ * design-system `Button`.
92
+ */
93
+ export function Dialog({
94
+ visible,
95
+ onClose,
96
+ title,
97
+ subtitle,
98
+ children,
99
+ actions = [],
100
+ cancel,
101
+ showClose = true,
102
+ closeDestructive = false,
103
+ dismissOnBackdrop = false,
104
+ scrollMode = 'inner',
105
+ stickyHeader = true,
106
+ stickyFooter = true,
107
+ padding = 24,
108
+ contentSpacing = 24,
109
+ radius = 24,
110
+ maxWidth = 560,
111
+ maxHeightRatio = 0.9,
112
+ scrollbarColor,
113
+ style,
114
+ }: DialogProps) {
115
+ const theme = useTheme();
116
+ const { height } = useWindowDimensions();
117
+ const indicatorColor = scrollbarColor ?? theme.colors.text.tertiary;
118
+
119
+ const closeBtn = showClose ? (
120
+ <Pressable
121
+ onPress={onClose}
122
+ hitSlop={8}
123
+ accessibilityRole="button"
124
+ accessibilityLabel="Close"
125
+ style={{ position: 'absolute', top: padding, right: padding, padding: 4, zIndex: 1 }}
126
+ >
127
+ <Icon
128
+ name="close"
129
+ size={24}
130
+ color={closeDestructive ? theme.colors.feedback.error : theme.colors.text.secondary}
131
+ />
132
+ </Pressable>
133
+ ) : null;
134
+
135
+ const header = (
136
+ <View style={{ paddingHorizontal: padding, paddingTop: padding, paddingBottom: contentSpacing }}>
137
+ <View style={{ paddingRight: showClose ? 40 : 0, gap: 4 }}>
138
+ <Text variant="h3" weight="semibold" color="primary">
139
+ {title}
140
+ </Text>
141
+ {subtitle ? (
142
+ <Text variant="bodyLarge" color="secondary">
143
+ {subtitle}
144
+ </Text>
145
+ ) : null}
146
+ </View>
147
+ </View>
148
+ );
149
+
150
+ const contentBody = <View style={{ paddingHorizontal: padding }}>{children}</View>;
151
+
152
+ const hasFooter = cancel != null || actions.length > 0;
153
+ const footer = hasFooter ? (
154
+ <View
155
+ style={{
156
+ flexDirection: 'row',
157
+ gap: 12,
158
+ paddingHorizontal: padding,
159
+ paddingBottom: padding,
160
+ paddingTop: contentSpacing,
161
+ }}
162
+ >
163
+ {cancel ? (
164
+ <View style={{ flex: 1 }}>
165
+ <Button
166
+ fullWidth
167
+ variant={cancel.destructive ? 'destructive' : 'secondary'}
168
+ label={cancel.label ?? 'Cancel'}
169
+ onPress={cancel.onPress ?? onClose}
170
+ />
171
+ </View>
172
+ ) : null}
173
+ {actions.map((action, i) => (
174
+ <View key={i} style={{ flex: 1 }}>
175
+ <Button
176
+ fullWidth
177
+ variant={action.variant ?? 'default'}
178
+ label={action.label}
179
+ onPress={action.onPress}
180
+ loading={action.loading}
181
+ disabled={action.disabled}
182
+ />
183
+ </View>
184
+ ))}
185
+ </View>
186
+ ) : null;
187
+
188
+ const cardBase: ViewStyle = {
189
+ width: '100%',
190
+ maxWidth,
191
+ backgroundColor: theme.colors.background.default,
192
+ borderRadius: radius,
193
+ overflow: 'hidden',
194
+ };
195
+
196
+ const card =
197
+ scrollMode === 'page' ? (
198
+ <View accessibilityViewIsModal style={[cardBase, style]}>
199
+ {closeBtn}
200
+ {header}
201
+ {contentBody}
202
+ {footer}
203
+ </View>
204
+ ) : (
205
+ <View
206
+ accessibilityViewIsModal
207
+ style={[cardBase, { maxHeight: height * maxHeightRatio }, style]}
208
+ >
209
+ {closeBtn}
210
+ {stickyHeader ? header : null}
211
+ <ScrollArea
212
+ indicatorColor={indicatorColor}
213
+ style={{ flexShrink: 1 }}
214
+ keyboardShouldPersistTaps="handled"
215
+ keyboardDismissMode="interactive"
216
+ >
217
+ {!stickyHeader ? header : null}
218
+ {contentBody}
219
+ {!stickyFooter ? footer : null}
220
+ </ScrollArea>
221
+ {stickyFooter ? footer : null}
222
+ </View>
223
+ );
224
+
225
+ return (
226
+ <Modal
227
+ visible={visible}
228
+ transparent
229
+ animationType="fade"
230
+ statusBarTranslucent
231
+ onRequestClose={onClose}
232
+ >
233
+ <KeyboardAvoidingView
234
+ behavior={Platform.OS === 'ios' ? 'padding' : undefined}
235
+ style={{ flex: 1 }}
236
+ >
237
+ <View style={{ flex: 1, backgroundColor: 'rgba(16, 18, 20, 0.55)' }}>
238
+ {scrollMode === 'page' ? (
239
+ <ScrollView
240
+ style={{ flex: 1 }}
241
+ contentContainerStyle={{ flexGrow: 1, padding: 16 }}
242
+ keyboardShouldPersistTaps="handled"
243
+ showsVerticalScrollIndicator={false}
244
+ >
245
+ <Pressable
246
+ style={{ flexGrow: 1, justifyContent: 'center', alignItems: 'center' }}
247
+ onPress={dismissOnBackdrop ? onClose : undefined}
248
+ >
249
+ <Pressable style={{ width: '100%', alignItems: 'center' }} onPress={() => {}}>
250
+ {card}
251
+ </Pressable>
252
+ </Pressable>
253
+ </ScrollView>
254
+ ) : (
255
+ <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center', padding: 16 }}>
256
+ <Pressable
257
+ style={StyleSheet.absoluteFill}
258
+ onPress={dismissOnBackdrop ? onClose : undefined}
259
+ accessibilityElementsHidden
260
+ importantForAccessibility="no-hide-descendants"
261
+ />
262
+ {card}
263
+ </View>
264
+ )}
265
+ </View>
266
+ </KeyboardAvoidingView>
267
+ </Modal>
268
+ );
269
+ }