jfs-components 0.0.78 → 0.0.84
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/CHANGELOG.md +11 -0
- package/lib/commonjs/components/AppBar/AppBar.js +56 -6
- package/lib/commonjs/components/Attached/Attached.js +183 -0
- package/lib/commonjs/components/Card/Card.js +25 -2
- package/lib/commonjs/components/Checkbox/Checkbox.js +18 -2
- package/lib/commonjs/components/Drawer/Drawer.js +6 -1
- package/lib/commonjs/components/DropdownInput/DropdownInput.js +30 -6
- package/lib/commonjs/components/ExpandableCheckbox/ExpandableCheckbox.js +17 -11
- package/lib/commonjs/components/FormField/FormField.js +1 -14
- package/lib/commonjs/components/FullscreenModal/FullscreenModal.js +9 -7
- package/lib/commonjs/components/ListItem/ListItem.js +26 -24
- package/lib/commonjs/components/MessageField/MessageField.js +1 -13
- package/lib/commonjs/components/PaymentFeedback/PaymentFeedback.js +12 -9
- package/lib/commonjs/components/PlanComparisonCard/PlanComparisonCard.js +237 -0
- package/lib/commonjs/components/Slot/Slot.js +73 -0
- package/lib/commonjs/components/Spinner/Spinner.js +217 -0
- package/lib/commonjs/components/TextInput/TextInput.js +33 -18
- package/lib/commonjs/components/index.js +28 -0
- package/lib/commonjs/icons/components/IconArrowdown.js +19 -0
- package/lib/commonjs/icons/components/IconArrowup.js +19 -0
- package/lib/commonjs/icons/components/IconChevrondowncircle.js +19 -0
- package/lib/commonjs/icons/components/IconChevronleftcircle.js +19 -0
- package/lib/commonjs/icons/components/IconChevronrightcircle.js +19 -0
- package/lib/commonjs/icons/components/IconChevronupcircle.js +19 -0
- package/lib/commonjs/icons/components/IconOsnavback.js +19 -0
- package/lib/commonjs/icons/components/IconOsnavcenter.js +19 -0
- package/lib/commonjs/icons/components/IconOsnavhome.js +19 -0
- package/lib/commonjs/icons/components/IconOsnavtask.js +19 -0
- package/lib/commonjs/icons/components/IconSignin.js +19 -0
- package/lib/commonjs/icons/components/IconSignout.js +19 -0
- package/lib/commonjs/icons/components/index.js +132 -0
- package/lib/commonjs/icons/registry.js +2 -2
- package/lib/module/components/AppBar/AppBar.js +56 -6
- package/lib/module/components/Attached/Attached.js +178 -0
- package/lib/module/components/Card/Card.js +25 -2
- package/lib/module/components/Checkbox/Checkbox.js +18 -2
- package/lib/module/components/Drawer/Drawer.js +6 -1
- package/lib/module/components/DropdownInput/DropdownInput.js +30 -6
- package/lib/module/components/ExpandableCheckbox/ExpandableCheckbox.js +17 -11
- package/lib/module/components/FormField/FormField.js +3 -16
- package/lib/module/components/FullscreenModal/FullscreenModal.js +9 -7
- package/lib/module/components/ListItem/ListItem.js +26 -24
- package/lib/module/components/MessageField/MessageField.js +3 -15
- package/lib/module/components/PaymentFeedback/PaymentFeedback.js +13 -9
- package/lib/module/components/PlanComparisonCard/PlanComparisonCard.js +234 -0
- package/lib/module/components/Slot/Slot.js +68 -0
- package/lib/module/components/Spinner/Spinner.js +212 -0
- package/lib/module/components/TextInput/TextInput.js +34 -19
- package/lib/module/components/index.js +4 -0
- package/lib/module/icons/components/IconArrowdown.js +12 -0
- package/lib/module/icons/components/IconArrowup.js +12 -0
- package/lib/module/icons/components/IconChevrondowncircle.js +12 -0
- package/lib/module/icons/components/IconChevronleftcircle.js +12 -0
- package/lib/module/icons/components/IconChevronrightcircle.js +12 -0
- package/lib/module/icons/components/IconChevronupcircle.js +12 -0
- package/lib/module/icons/components/IconOsnavback.js +12 -0
- package/lib/module/icons/components/IconOsnavcenter.js +12 -0
- package/lib/module/icons/components/IconOsnavhome.js +12 -0
- package/lib/module/icons/components/IconOsnavtask.js +12 -0
- package/lib/module/icons/components/IconSignin.js +12 -0
- package/lib/module/icons/components/IconSignout.js +12 -0
- package/lib/module/icons/components/index.js +12 -0
- package/lib/module/icons/registry.js +2 -2
- package/lib/typescript/src/components/AppBar/AppBar.d.ts +12 -1
- package/lib/typescript/src/components/Attached/Attached.d.ts +64 -0
- package/lib/typescript/src/components/Card/Card.d.ts +9 -2
- package/lib/typescript/src/components/DropdownInput/DropdownInput.d.ts +3 -2
- package/lib/typescript/src/components/ListItem/ListItem.d.ts +16 -6
- package/lib/typescript/src/components/PaymentFeedback/PaymentFeedback.d.ts +5 -1
- package/lib/typescript/src/components/PlanComparisonCard/PlanComparisonCard.d.ts +66 -0
- package/lib/typescript/src/components/Slot/Slot.d.ts +52 -0
- package/lib/typescript/src/components/Spinner/Spinner.d.ts +45 -0
- package/lib/typescript/src/components/index.d.ts +4 -0
- package/lib/typescript/src/icons/components/IconArrowdown.d.ts +3 -0
- package/lib/typescript/src/icons/components/IconArrowup.d.ts +3 -0
- package/lib/typescript/src/icons/components/IconChevrondowncircle.d.ts +3 -0
- package/lib/typescript/src/icons/components/IconChevronleftcircle.d.ts +3 -0
- package/lib/typescript/src/icons/components/IconChevronrightcircle.d.ts +3 -0
- package/lib/typescript/src/icons/components/IconChevronupcircle.d.ts +3 -0
- package/lib/typescript/src/icons/components/IconOsnavback.d.ts +3 -0
- package/lib/typescript/src/icons/components/IconOsnavcenter.d.ts +3 -0
- package/lib/typescript/src/icons/components/IconOsnavhome.d.ts +3 -0
- package/lib/typescript/src/icons/components/IconOsnavtask.d.ts +3 -0
- package/lib/typescript/src/icons/components/IconSignin.d.ts +3 -0
- package/lib/typescript/src/icons/components/IconSignout.d.ts +3 -0
- package/lib/typescript/src/icons/components/index.d.ts +12 -0
- package/lib/typescript/src/icons/registry.d.ts +1 -1
- package/package.json +3 -2
- package/src/components/AppBar/AppBar.tsx +79 -12
- package/src/components/Attached/Attached.tsx +237 -0
- package/src/components/Card/Card.tsx +28 -1
- package/src/components/Checkbox/Checkbox.tsx +14 -2
- package/src/components/Drawer/Drawer.tsx +4 -0
- package/src/components/DropdownInput/DropdownInput.tsx +54 -20
- package/src/components/ExpandableCheckbox/ExpandableCheckbox.tsx +13 -9
- package/src/components/FormField/FormField.tsx +3 -19
- package/src/components/FullscreenModal/FullscreenModal.tsx +6 -3
- package/src/components/ListItem/ListItem.tsx +42 -25
- package/src/components/MessageField/MessageField.tsx +3 -18
- package/src/components/PaymentFeedback/PaymentFeedback.tsx +15 -8
- package/src/components/PlanComparisonCard/PlanComparisonCard.tsx +316 -0
- package/src/components/Slot/Slot.tsx +91 -0
- package/src/components/Spinner/Spinner.tsx +273 -0
- package/src/components/TextInput/TextInput.tsx +37 -19
- package/src/components/index.ts +4 -0
- package/src/icons/components/IconArrowdown.tsx +11 -0
- package/src/icons/components/IconArrowup.tsx +11 -0
- package/src/icons/components/IconChevrondowncircle.tsx +11 -0
- package/src/icons/components/IconChevronleftcircle.tsx +11 -0
- package/src/icons/components/IconChevronrightcircle.tsx +11 -0
- package/src/icons/components/IconChevronupcircle.tsx +11 -0
- package/src/icons/components/IconOsnavback.tsx +11 -0
- package/src/icons/components/IconOsnavcenter.tsx +11 -0
- package/src/icons/components/IconOsnavhome.tsx +11 -0
- package/src/icons/components/IconOsnavtask.tsx +11 -0
- package/src/icons/components/IconSignin.tsx +11 -0
- package/src/icons/components/IconSignout.tsx +11 -0
- package/src/icons/components/index.ts +12 -0
- package/src/icons/registry.ts +49 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,17 @@ All notable changes to this project are documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
6
6
|
|
|
7
|
+
## [0.0.79] - 2026-05-30
|
|
8
|
+
|
|
9
|
+
- Added `Attached` — positions a badge over main content at nine anchor points (corners, edges, center).
|
|
10
|
+
- Added `PlanComparisonCard` — plan comparison table with column headers, feature rows, brand highlight, and cell values (text / cross / custom node).
|
|
11
|
+
- Added `Slot` — token-driven vertical/horizontal layout container with `modes` cascade to children.
|
|
12
|
+
- `Card` — new `header` slot above media (e.g. brand logo).
|
|
13
|
+
- `ListItem` — new `trailing` slot; `endSlot` deprecated (still honored).
|
|
14
|
+
- `FullscreenModal` — footer action stack now uses `Slot` instead of a raw `View`.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
7
18
|
## [0.0.78] - 2026-05-29
|
|
8
19
|
|
|
9
20
|
- Added `ExpandableCheckbox` — checkbox row with collapsible long label and Read more / Read less toggle.
|
|
@@ -12,10 +12,18 @@ var _NavArrow = _interopRequireDefault(require("../NavArrow/NavArrow"));
|
|
|
12
12
|
var _reactUtils = require("../../utils/react-utils");
|
|
13
13
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
14
14
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
15
|
+
// SubPage "slot wrap" geometry, taken directly from the Figma design
|
|
16
|
+
// (node 449:7876). The middle slot is an absolutely-centered box of a fixed
|
|
17
|
+
// size; its inner content (node 3991:4125) is a `flex: 1 0 0; min-width: 1px`
|
|
18
|
+
// item so it fills / shrinks responsively within that box.
|
|
19
|
+
const SUBPAGE_MIDDLE_DEFAULT_WIDTH = 192;
|
|
20
|
+
const SUBPAGE_MIDDLE_HEIGHT = 32;
|
|
21
|
+
const SUBPAGE_MIDDLE_PADDING_HORIZONTAL = 21;
|
|
15
22
|
function AppBar({
|
|
16
23
|
type = 'MainPage',
|
|
17
24
|
leadingSlot,
|
|
18
25
|
middleSlot,
|
|
26
|
+
middleSlotWidth = SUBPAGE_MIDDLE_DEFAULT_WIDTH,
|
|
19
27
|
actionsSlot,
|
|
20
28
|
modes: propModes = _reactUtils.EMPTY_MODES,
|
|
21
29
|
onLeadingPress,
|
|
@@ -117,13 +125,40 @@ function AppBar({
|
|
|
117
125
|
children: (0, _reactUtils.cloneChildrenWithModes)(_react.default.Children.toArray(actionsSlot), modes)
|
|
118
126
|
}) : null;
|
|
119
127
|
|
|
120
|
-
//
|
|
121
|
-
//
|
|
122
|
-
//
|
|
123
|
-
|
|
128
|
+
// SubPage centers its middle slot via absolute positioning (see Figma
|
|
129
|
+
// "slot wrap"), so it never participates in the row flow. Only MainPage
|
|
130
|
+
// keeps the legacy in-flow middle slot.
|
|
131
|
+
const hasInFlowMiddle = isMain && !!processedMiddle;
|
|
132
|
+
|
|
133
|
+
// With an in-flow middle (MainPage) the middle (flex: 1) absorbs the
|
|
134
|
+
// remaining space, so leading & actions sit at the edges naturally. In all
|
|
135
|
+
// other cases we pin leading & actions to the outer edges with
|
|
136
|
+
// `space-between`; the SubPage middle floats above, centered.
|
|
124
137
|
const wrapperStyle = {
|
|
125
138
|
...containerStyle,
|
|
126
|
-
justifyContent:
|
|
139
|
+
justifyContent: hasInFlowMiddle ? 'flex-start' : 'space-between'
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// Absolutely-centered middle box for SubPage, mirroring the Figma geometry.
|
|
143
|
+
// `left/top: 50%` + a negative translate keeps it centered regardless of the
|
|
144
|
+
// bar width, while the fixed width clips overly-wide content (overflow:
|
|
145
|
+
// hidden) instead of letting it bleed under the leading/actions slots.
|
|
146
|
+
const subPageMiddleStyle = {
|
|
147
|
+
position: 'absolute',
|
|
148
|
+
top: '50%',
|
|
149
|
+
left: '50%',
|
|
150
|
+
width: middleSlotWidth,
|
|
151
|
+
height: SUBPAGE_MIDDLE_HEIGHT,
|
|
152
|
+
transform: [{
|
|
153
|
+
translateX: -middleSlotWidth / 2
|
|
154
|
+
}, {
|
|
155
|
+
translateY: -SUBPAGE_MIDDLE_HEIGHT / 2
|
|
156
|
+
}],
|
|
157
|
+
flexDirection: 'row',
|
|
158
|
+
alignItems: 'center',
|
|
159
|
+
justifyContent: 'center',
|
|
160
|
+
paddingHorizontal: SUBPAGE_MIDDLE_PADDING_HORIZONTAL,
|
|
161
|
+
overflow: 'hidden'
|
|
127
162
|
};
|
|
128
163
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
129
164
|
style: [wrapperStyle, style],
|
|
@@ -139,7 +174,7 @@ function AppBar({
|
|
|
139
174
|
alignItems: 'center'
|
|
140
175
|
},
|
|
141
176
|
children: processedLeading
|
|
142
|
-
}),
|
|
177
|
+
}), hasInFlowMiddle && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
143
178
|
style: {
|
|
144
179
|
flex: 1,
|
|
145
180
|
minWidth: 0,
|
|
@@ -152,6 +187,21 @@ function AppBar({
|
|
|
152
187
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
153
188
|
style: actionsStyle,
|
|
154
189
|
children: processedActions
|
|
190
|
+
}), isSub && processedMiddle && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
191
|
+
style: subPageMiddleStyle,
|
|
192
|
+
pointerEvents: "box-none",
|
|
193
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
194
|
+
style: {
|
|
195
|
+
flex: 1,
|
|
196
|
+
minWidth: 1,
|
|
197
|
+
height: '100%',
|
|
198
|
+
flexDirection: 'row',
|
|
199
|
+
alignItems: 'center',
|
|
200
|
+
justifyContent: 'center'
|
|
201
|
+
},
|
|
202
|
+
pointerEvents: "box-none",
|
|
203
|
+
children: processedMiddle
|
|
204
|
+
})
|
|
155
205
|
})]
|
|
156
206
|
});
|
|
157
207
|
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
8
|
+
var _reactNative = require("react-native");
|
|
9
|
+
var _JFSThemeProvider = require("../../design-tokens/JFSThemeProvider");
|
|
10
|
+
var _reactUtils = require("../../utils/react-utils");
|
|
11
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
12
|
+
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
13
|
+
/**
|
|
14
|
+
* Anchor point on the main content where the attached `badge` is centered.
|
|
15
|
+
* Mirrors the nine Figma `position` variants (corners, edge midpoints, center).
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const ZERO_SIZE = {
|
|
19
|
+
width: 0,
|
|
20
|
+
height: 0
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Fraction (0 | 0.5 | 1) of the main content's width/height at which the badge
|
|
25
|
+
* center should sit, derived from the `position` anchor.
|
|
26
|
+
*/
|
|
27
|
+
function resolveAnchorFractions(position) {
|
|
28
|
+
const fx = position.includes('left') ? 0 : position.includes('right') ? 1 : 0.5;
|
|
29
|
+
const fy = position.startsWith('top') ? 0 : position.startsWith('bottom') ? 1 : 0.5;
|
|
30
|
+
return {
|
|
31
|
+
fx,
|
|
32
|
+
fy
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Attached — overlays a small `badge` on top of arbitrary main content,
|
|
38
|
+
* centered on one of nine anchor points (corners, edge midpoints, or center).
|
|
39
|
+
*
|
|
40
|
+
* The badge straddles the chosen anchor regardless of either element's size:
|
|
41
|
+
* both the main content and the badge are measured via `onLayout`, then the
|
|
42
|
+
* badge is absolutely positioned so its center lands exactly on the anchor.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```tsx
|
|
46
|
+
* <Attached position="bottom-right" badge={<InstitutionBadge modes={modes} />} modes={modes}>
|
|
47
|
+
* <IconCapsule iconName="ic_card" modes={modes} />
|
|
48
|
+
* </Attached>
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
/**
|
|
52
|
+
* Stretches the immediate badge child/children to fill the enforced badge box.
|
|
53
|
+
* Merges `{ width: '100%', height: '100%' }` into each top-level element's
|
|
54
|
+
* `style` so an arbitrary node (e.g. an `Image` with its own width/aspectRatio)
|
|
55
|
+
* fills the fixed `badgeSize` box instead of laying out at its intrinsic size.
|
|
56
|
+
* The wrapping box's `overflow: 'hidden'` clips anything that still overflows.
|
|
57
|
+
*/
|
|
58
|
+
function forceBadgeFill(children) {
|
|
59
|
+
return _react.default.Children.map(children, child => {
|
|
60
|
+
if (! /*#__PURE__*/_react.default.isValidElement(child)) return child;
|
|
61
|
+
const childStyle = child.props?.style;
|
|
62
|
+
return /*#__PURE__*/_react.default.cloneElement(child, {
|
|
63
|
+
style: [FILL_STYLE, childStyle]
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
function Attached({
|
|
68
|
+
children,
|
|
69
|
+
badge,
|
|
70
|
+
badgeSize,
|
|
71
|
+
badgeRadius,
|
|
72
|
+
position = 'bottom-right',
|
|
73
|
+
circular = true,
|
|
74
|
+
modes: propModes = _reactUtils.EMPTY_MODES,
|
|
75
|
+
style,
|
|
76
|
+
...rest
|
|
77
|
+
}) {
|
|
78
|
+
const {
|
|
79
|
+
modes: globalModes
|
|
80
|
+
} = (0, _JFSThemeProvider.useTokens)();
|
|
81
|
+
const modes = (0, _react.useMemo)(() => globalModes === _reactUtils.EMPTY_MODES && propModes === _reactUtils.EMPTY_MODES ? _reactUtils.EMPTY_MODES : {
|
|
82
|
+
...globalModes,
|
|
83
|
+
...propModes
|
|
84
|
+
}, [globalModes, propModes]);
|
|
85
|
+
const [mainSize, setMainSize] = (0, _react.useState)(ZERO_SIZE);
|
|
86
|
+
const [measuredBadgeSize, setMeasuredBadgeSize] = (0, _react.useState)(ZERO_SIZE);
|
|
87
|
+
const onMainLayout = (0, _react.useCallback)(e => {
|
|
88
|
+
const {
|
|
89
|
+
width,
|
|
90
|
+
height
|
|
91
|
+
} = e.nativeEvent.layout;
|
|
92
|
+
setMainSize(prev => prev.width === width && prev.height === height ? prev : {
|
|
93
|
+
width,
|
|
94
|
+
height
|
|
95
|
+
});
|
|
96
|
+
}, []);
|
|
97
|
+
const onBadgeLayout = (0, _react.useCallback)(e => {
|
|
98
|
+
const {
|
|
99
|
+
width,
|
|
100
|
+
height
|
|
101
|
+
} = e.nativeEvent.layout;
|
|
102
|
+
setMeasuredBadgeSize(prev => prev.width === width && prev.height === height ? prev : {
|
|
103
|
+
width,
|
|
104
|
+
height
|
|
105
|
+
});
|
|
106
|
+
}, []);
|
|
107
|
+
const mainChildren = (0, _react.useMemo)(() => children != null ? (0, _reactUtils.cloneChildrenWithModes)(children, modes) : null, [children, modes]);
|
|
108
|
+
const badgeChildren = (0, _react.useMemo)(() => badge != null ? (0, _reactUtils.cloneChildrenWithModes)(badge, modes) : null, [badge, modes]);
|
|
109
|
+
|
|
110
|
+
// When a fixed size is requested, the badge is wrapped in a clipped box and
|
|
111
|
+
// its content is force-stretched to fill it (see `forceBadgeFill`).
|
|
112
|
+
const badgeBoxStyle = (0, _react.useMemo)(() => {
|
|
113
|
+
if (badgeSize == null) return null;
|
|
114
|
+
return {
|
|
115
|
+
width: badgeSize,
|
|
116
|
+
height: badgeSize,
|
|
117
|
+
borderRadius: badgeRadius ?? badgeSize / 2,
|
|
118
|
+
overflow: 'hidden'
|
|
119
|
+
};
|
|
120
|
+
}, [badgeSize, badgeRadius]);
|
|
121
|
+
const badgePlacement = (0, _react.useMemo)(() => {
|
|
122
|
+
const {
|
|
123
|
+
fx,
|
|
124
|
+
fy
|
|
125
|
+
} = resolveAnchorFractions(position);
|
|
126
|
+
const measured = mainSize.width > 0 && measuredBadgeSize.width > 0;
|
|
127
|
+
let anchorX;
|
|
128
|
+
let anchorY;
|
|
129
|
+
if (circular) {
|
|
130
|
+
// Project the anchor onto the circle inscribed in the bounding box, so
|
|
131
|
+
// corner badges land on the circumference (45°) instead of the box corner.
|
|
132
|
+
const cx = mainSize.width / 2;
|
|
133
|
+
const cy = mainSize.height / 2;
|
|
134
|
+
const radius = Math.min(mainSize.width, mainSize.height) / 2;
|
|
135
|
+
const dx = (fx - 0.5) * 2; // -1 | 0 | 1
|
|
136
|
+
const dy = (fy - 0.5) * 2; // -1 | 0 | 1
|
|
137
|
+
const len = Math.hypot(dx, dy) || 1; // 'center' → 0, guard against /0
|
|
138
|
+
anchorX = cx + dx / len * radius;
|
|
139
|
+
anchorY = cy + dy / len * radius;
|
|
140
|
+
} else {
|
|
141
|
+
anchorX = mainSize.width * fx;
|
|
142
|
+
anchorY = mainSize.height * fy;
|
|
143
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
position: 'absolute',
|
|
146
|
+
left: anchorX - measuredBadgeSize.width / 2,
|
|
147
|
+
top: anchorY - measuredBadgeSize.height / 2,
|
|
148
|
+
// Hide until both elements are measured to avoid a one-frame flash at (0,0).
|
|
149
|
+
opacity: measured ? 1 : 0
|
|
150
|
+
};
|
|
151
|
+
}, [position, circular, mainSize, measuredBadgeSize]);
|
|
152
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
153
|
+
style: [styles.container, style],
|
|
154
|
+
...rest,
|
|
155
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
156
|
+
onLayout: onMainLayout,
|
|
157
|
+
children: mainChildren
|
|
158
|
+
}), badgeChildren != null && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
159
|
+
style: badgePlacement,
|
|
160
|
+
onLayout: onBadgeLayout,
|
|
161
|
+
pointerEvents: "box-none",
|
|
162
|
+
children: badgeBoxStyle != null ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
163
|
+
style: badgeBoxStyle,
|
|
164
|
+
children: forceBadgeFill(badgeChildren)
|
|
165
|
+
}) : badgeChildren
|
|
166
|
+
})]
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
const styles = {
|
|
170
|
+
// alignSelf flex-start so the wrapper hugs the main content; anchors are then
|
|
171
|
+
// computed relative to the content size rather than a stretched parent.
|
|
172
|
+
container: {
|
|
173
|
+
position: 'relative',
|
|
174
|
+
alignSelf: 'flex-start'
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
/** Fill style merged into badge content when `badgeSize` enforces a fixed box. */
|
|
179
|
+
const FILL_STYLE = {
|
|
180
|
+
width: '100%',
|
|
181
|
+
height: '100%'
|
|
182
|
+
};
|
|
183
|
+
var _default = exports.default = /*#__PURE__*/_react.default.memo(Attached);
|
|
@@ -20,9 +20,11 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
|
|
|
20
20
|
* Card component implementation from Figma node 765:6186.
|
|
21
21
|
*
|
|
22
22
|
* Supports a `media` slot (with aspect ratio) and a content area.
|
|
23
|
+
* Supports an optional `header` slot (e.g. a brand logo), a `media` slot
|
|
24
|
+
* (with aspect ratio) and a content area.
|
|
23
25
|
* Usage:
|
|
24
26
|
* ```tsx
|
|
25
|
-
* <Card media={<Image source={...} />} modes={modes}>
|
|
27
|
+
* <Card header={<GoldLogo />} media={<Image source={...} />} modes={modes}>
|
|
26
28
|
* <Card.SupportText>Support text</Card.SupportText>
|
|
27
29
|
* <Card.Title>Title</Card.Title>
|
|
28
30
|
* <Card.SupportText>Support text</Card.SupportText>
|
|
@@ -30,6 +32,7 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
|
|
|
30
32
|
* ```
|
|
31
33
|
*/
|
|
32
34
|
function Card({
|
|
35
|
+
header,
|
|
33
36
|
media,
|
|
34
37
|
children,
|
|
35
38
|
modes = _reactUtils.EMPTY_MODES,
|
|
@@ -57,6 +60,14 @@ function Card({
|
|
|
57
60
|
...modes
|
|
58
61
|
}
|
|
59
62
|
}) : media;
|
|
63
|
+
|
|
64
|
+
// Clone header to pass modes if it's a valid element
|
|
65
|
+
const headerWithModes = /*#__PURE__*/(0, _react.isValidElement)(header) ? /*#__PURE__*/(0, _react.cloneElement)(header, {
|
|
66
|
+
modes: {
|
|
67
|
+
...header.props.modes,
|
|
68
|
+
...modes
|
|
69
|
+
}
|
|
70
|
+
}) : header;
|
|
60
71
|
const containerStyle = {
|
|
61
72
|
backgroundColor,
|
|
62
73
|
borderColor,
|
|
@@ -67,6 +78,15 @@ function Card({
|
|
|
67
78
|
paddingVertical,
|
|
68
79
|
overflow: 'hidden' // Ensure border radius clips content
|
|
69
80
|
};
|
|
81
|
+
|
|
82
|
+
// Header wrap uses fixed padding from Figma (no dedicated tokens defined).
|
|
83
|
+
const headerWrapperStyle = {
|
|
84
|
+
width: '100%',
|
|
85
|
+
flexDirection: 'row',
|
|
86
|
+
alignItems: 'flex-start',
|
|
87
|
+
paddingHorizontal: 12,
|
|
88
|
+
paddingVertical: 16
|
|
89
|
+
};
|
|
70
90
|
const mediaWrapperStyle = {
|
|
71
91
|
width: '100%',
|
|
72
92
|
aspectRatio: mediaAspectRatio,
|
|
@@ -87,7 +107,10 @@ function Card({
|
|
|
87
107
|
},
|
|
88
108
|
children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
89
109
|
style: [containerStyle, style],
|
|
90
|
-
children: [
|
|
110
|
+
children: [header && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
111
|
+
style: headerWrapperStyle,
|
|
112
|
+
children: headerWithModes
|
|
113
|
+
}), media && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
91
114
|
style: mediaWrapperStyle,
|
|
92
115
|
children: mediaWithModes
|
|
93
116
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
@@ -53,11 +53,25 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
|
|
|
53
53
|
/** Minimum touch target per iOS HIG / Material accessibility guidance. */
|
|
54
54
|
const MIN_TOUCH_TARGET = 44;
|
|
55
55
|
const touchTargetStyle = {
|
|
56
|
-
minWidth: MIN_TOUCH_TARGET,
|
|
57
|
-
minHeight: MIN_TOUCH_TARGET,
|
|
58
56
|
alignItems: 'center',
|
|
59
57
|
justifyContent: 'center'
|
|
60
58
|
};
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Expands the tappable region to the 44pt minimum without changing layout.
|
|
62
|
+
* `hitSlop` extends the press-responder bounds beyond the visual box on both
|
|
63
|
+
* native and web (react-native-web ≥ 0.19), so the Pressable keeps its natural
|
|
64
|
+
* checkbox-sized footprint and sibling alignment stays intact.
|
|
65
|
+
*/
|
|
66
|
+
function invisibleTouchHitSlop(checkboxSize) {
|
|
67
|
+
const slop = Math.max(0, Math.ceil((MIN_TOUCH_TARGET - checkboxSize) / 2));
|
|
68
|
+
return {
|
|
69
|
+
top: slop,
|
|
70
|
+
bottom: slop,
|
|
71
|
+
left: slop,
|
|
72
|
+
right: slop
|
|
73
|
+
};
|
|
74
|
+
}
|
|
61
75
|
/**
|
|
62
76
|
* Checkbox component that maps directly to the Figma design using design tokens.
|
|
63
77
|
*
|
|
@@ -182,8 +196,10 @@ function Checkbox({
|
|
|
182
196
|
};
|
|
183
197
|
};
|
|
184
198
|
const markColor = disabled && isChecked ? disabledActiveMark : selectedMarkColor;
|
|
199
|
+
const hitSlop = invisibleTouchHitSlop(size);
|
|
185
200
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
|
|
186
201
|
style: [touchTargetStyle, style],
|
|
202
|
+
hitSlop: hitSlop,
|
|
187
203
|
onPress: handlePress,
|
|
188
204
|
disabled: disabled,
|
|
189
205
|
onHoverIn: () => setIsHovered(true),
|
|
@@ -368,7 +368,12 @@ function Drawer({
|
|
|
368
368
|
flexDirection: 'column',
|
|
369
369
|
alignItems: 'stretch'
|
|
370
370
|
}, contentContainerStyle],
|
|
371
|
-
showsVerticalScrollIndicator: showsVerticalScrollIndicator
|
|
371
|
+
showsVerticalScrollIndicator: showsVerticalScrollIndicator
|
|
372
|
+
// Let a tap on an input inside the sheet focus it on the FIRST tap
|
|
373
|
+
// even while the keyboard is already open (default 'never' would
|
|
374
|
+
// eat that tap just to dismiss the keyboard).
|
|
375
|
+
,
|
|
376
|
+
keyboardShouldPersistTaps: "handled",
|
|
372
377
|
animatedProps: animatedScrollProps,
|
|
373
378
|
alwaysBounceVertical: false,
|
|
374
379
|
overScrollMode: "always",
|
|
@@ -145,7 +145,7 @@ function DropdownInput({
|
|
|
145
145
|
supportText,
|
|
146
146
|
errorMessage,
|
|
147
147
|
menuMaxHeight = 240,
|
|
148
|
-
menuOffset
|
|
148
|
+
menuOffset,
|
|
149
149
|
matchTriggerWidth = true,
|
|
150
150
|
closeOnBackdropPress = true,
|
|
151
151
|
modes: propModes = _reactUtils.EMPTY_MODES,
|
|
@@ -208,10 +208,29 @@ function DropdownInput({
|
|
|
208
208
|
const tokens = useFormFieldTokens(modes);
|
|
209
209
|
const chevron = useChevronTokens(modes);
|
|
210
210
|
|
|
211
|
+
// Gap between the input and the popup. Falls back to the `formField/gap`
|
|
212
|
+
// token so the menu's offset matches the field's own internal spacing.
|
|
213
|
+
const effectiveMenuOffset = menuOffset ?? tokens.gap;
|
|
214
|
+
|
|
211
215
|
// ---------------- Layout / measurement ----------------
|
|
212
216
|
const triggerRef = (0, _react.useRef)(null);
|
|
213
217
|
const [triggerRect, setTriggerRect] = (0, _react.useState)(null);
|
|
214
218
|
const insets = (0, _reactNativeSafeAreaContext.useSafeAreaInsets)();
|
|
219
|
+
|
|
220
|
+
// Android coordinate-space bridge.
|
|
221
|
+
//
|
|
222
|
+
// The popup lives inside a `statusBarTranslucent` Modal, whose window is
|
|
223
|
+
// laid out from the PHYSICAL top of the screen (behind the status bar).
|
|
224
|
+
// The trigger, however, is rendered inside the app's content area (Expo
|
|
225
|
+
// Router / react-native-screens under edge-to-edge), so its
|
|
226
|
+
// `measureInWindow` Y is relative to the content area — it does NOT include
|
|
227
|
+
// the status bar height. Feeding that Y straight into the Modal would place
|
|
228
|
+
// the popup one status-bar-height too high, landing it on top of the input.
|
|
229
|
+
//
|
|
230
|
+
// Adding `insets.top` converts the trigger's content-relative Y into the
|
|
231
|
+
// Modal's full-screen coordinate space. iOS/web share a single coordinate
|
|
232
|
+
// space for the Modal and the trigger, so no shift is needed there.
|
|
233
|
+
const windowTopOffset = _reactNative.Platform.OS === 'android' ? insets.top : 0;
|
|
215
234
|
const measure = (0, _react.useCallback)(() => {
|
|
216
235
|
if (!triggerRef.current) return;
|
|
217
236
|
triggerRef.current.measureInWindow((x, y, width, height) => {
|
|
@@ -277,7 +296,7 @@ function DropdownInput({
|
|
|
277
296
|
const spaceBelow = windowHeight - (triggerRect.y + triggerRect.height) - insets.bottom;
|
|
278
297
|
const spaceAbove = triggerRect.y - insets.top;
|
|
279
298
|
const desiredHeight = Math.min(menuSize?.height ?? menuMaxHeight, menuMaxHeight);
|
|
280
|
-
const needed = desiredHeight +
|
|
299
|
+
const needed = desiredHeight + effectiveMenuOffset + 8;
|
|
281
300
|
if (placement === 'top') {
|
|
282
301
|
return spaceAbove >= needed || spaceAbove >= spaceBelow ? 'top' : 'bottom';
|
|
283
302
|
}
|
|
@@ -285,7 +304,7 @@ function DropdownInput({
|
|
|
285
304
|
return spaceBelow >= needed || spaceBelow >= spaceAbove ? 'bottom' : 'top';
|
|
286
305
|
}
|
|
287
306
|
return spaceBelow >= needed || spaceBelow >= spaceAbove ? 'bottom' : 'top';
|
|
288
|
-
}, [triggerRect, placement, windowHeight, menuSize?.height, menuMaxHeight,
|
|
307
|
+
}, [triggerRect, placement, windowHeight, menuSize?.height, menuMaxHeight, effectiveMenuOffset, insets.top, insets.bottom]);
|
|
289
308
|
const popupStyle = (0, _react.useMemo)(() => {
|
|
290
309
|
if (!triggerRect) {
|
|
291
310
|
return {
|
|
@@ -304,15 +323,18 @@ function DropdownInput({
|
|
|
304
323
|
const minLeft = insets.left + screenPadding;
|
|
305
324
|
if (leftPos > maxLeft) leftPos = maxLeft;
|
|
306
325
|
if (leftPos < minLeft) leftPos = minLeft;
|
|
326
|
+
|
|
327
|
+
// Trigger top expressed in the Modal's (full-screen) coordinate space.
|
|
328
|
+
const triggerTop = triggerRect.y + windowTopOffset;
|
|
307
329
|
let topPos;
|
|
308
330
|
if (computedPlacement === 'top') {
|
|
309
331
|
const desiredHeight = menuSize?.height ?? menuMaxHeight;
|
|
310
|
-
topPos =
|
|
332
|
+
topPos = triggerTop - desiredHeight - effectiveMenuOffset;
|
|
311
333
|
if (topPos < insets.top + screenPadding) {
|
|
312
334
|
topPos = insets.top + screenPadding;
|
|
313
335
|
}
|
|
314
336
|
} else {
|
|
315
|
-
topPos =
|
|
337
|
+
topPos = triggerTop + triggerRect.height + effectiveMenuOffset;
|
|
316
338
|
}
|
|
317
339
|
const style = {
|
|
318
340
|
position: 'absolute',
|
|
@@ -324,7 +346,7 @@ function DropdownInput({
|
|
|
324
346
|
// the wrong place. menuSize becomes truthy after the first layout.
|
|
325
347
|
if (menuSize == null) style.opacity = 0;
|
|
326
348
|
return style;
|
|
327
|
-
}, [triggerRect, computedPlacement, menuSize,
|
|
349
|
+
}, [triggerRect, computedPlacement, menuSize, effectiveMenuOffset, windowTopOffset, menuMaxHeight, matchTriggerWidth, windowWidth, insets.top, insets.left, insets.right]);
|
|
328
350
|
|
|
329
351
|
// Reset menu size when closing so the next open re-measures (handles items
|
|
330
352
|
// changing while the menu was closed).
|
|
@@ -513,6 +535,8 @@ function DropdownInput({
|
|
|
513
535
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Modal, {
|
|
514
536
|
visible: isOpen,
|
|
515
537
|
transparent: true,
|
|
538
|
+
statusBarTranslucent: true,
|
|
539
|
+
navigationBarTranslucent: true,
|
|
516
540
|
animationType: "fade",
|
|
517
541
|
onRequestClose: closeMenu,
|
|
518
542
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
|
|
@@ -88,8 +88,6 @@ function ExpandableCheckbox({
|
|
|
88
88
|
}, [disabled, isExpanded, isExpandedControlled, onExpandedChange]);
|
|
89
89
|
const gap = (0, _figmaVariablesResolver.getVariableByName)('expandableCheckbox/gap', modes) ?? 8;
|
|
90
90
|
const rowGap = (0, _figmaVariablesResolver.getVariableByName)('checkboxItem/gap', modes) ?? 8;
|
|
91
|
-
const rowPaddingHorizontal = (0, _figmaVariablesResolver.getVariableByName)('checkboxItem/padding/horizontal', modes) ?? 0;
|
|
92
|
-
const rowPaddingVertical = (0, _figmaVariablesResolver.getVariableByName)('checkboxItem/padding/vertical', modes) ?? 0;
|
|
93
91
|
const labelColor = (0, _figmaVariablesResolver.getVariableByName)('checkboxItem/foreground', modes) ?? '#1a1c1f';
|
|
94
92
|
const labelFontFamily = (0, _figmaVariablesResolver.getVariableByName)('checkboxItem/label/fontFamily', modes) ?? 'JioType Var';
|
|
95
93
|
const labelFontSize = (0, _figmaVariablesResolver.getVariableByName)('checkboxItem/label/fontSize', modes) ?? 14;
|
|
@@ -110,11 +108,9 @@ function ExpandableCheckbox({
|
|
|
110
108
|
alignSelf: isExpanded ? 'stretch' : 'auto',
|
|
111
109
|
minWidth: 0,
|
|
112
110
|
flexDirection: 'row',
|
|
113
|
-
alignItems: 'flex-start',
|
|
114
|
-
gap: rowGap
|
|
115
|
-
|
|
116
|
-
paddingVertical: rowPaddingVertical
|
|
117
|
-
}), [isExpanded, rowGap, rowPaddingHorizontal, rowPaddingVertical]);
|
|
111
|
+
alignItems: isExpanded ? 'flex-start' : 'center',
|
|
112
|
+
gap: rowGap
|
|
113
|
+
}), [isExpanded, rowGap]);
|
|
118
114
|
const resolvedLabelStyle = (0, _react.useMemo)(() => ({
|
|
119
115
|
flex: 1,
|
|
120
116
|
minWidth: 0,
|
|
@@ -122,11 +118,21 @@ function ExpandableCheckbox({
|
|
|
122
118
|
fontFamily: labelFontFamily,
|
|
123
119
|
fontSize: labelFontSize,
|
|
124
120
|
lineHeight: labelLineHeight,
|
|
125
|
-
fontWeight: labelFontWeight
|
|
126
|
-
|
|
121
|
+
fontWeight: labelFontWeight,
|
|
122
|
+
// Android adds asymmetric font padding and top-aligns the glyph inside
|
|
123
|
+
// an inflated line box when `lineHeight` is set. That makes the centered
|
|
124
|
+
// checkbox look like it drops below the text. Disabling the extra
|
|
125
|
+
// padding + centering the glyph keeps the single-line label optically
|
|
126
|
+
// aligned with the checkbox. No-op on iOS / web.
|
|
127
|
+
includeFontPadding: false,
|
|
128
|
+
textAlignVertical: isExpanded ? 'top' : 'center'
|
|
129
|
+
}), [labelColor, labelFontFamily, labelFontSize, labelLineHeight, labelFontWeight, isExpanded]);
|
|
130
|
+
|
|
131
|
+
// Layer component modes first (e.g. Color Mode), then button defaults so
|
|
132
|
+
// Secondary / XS / Low always win unless a dedicated override prop is added.
|
|
127
133
|
const buttonModes = (0, _react.useMemo)(() => ({
|
|
128
|
-
...
|
|
129
|
-
...
|
|
134
|
+
...modes,
|
|
135
|
+
...BUTTON_DEFAULT_MODES
|
|
130
136
|
}), [modes]);
|
|
131
137
|
const a11yLabel = accessibilityLabel ?? (typeof label === 'string' ? label : undefined);
|
|
132
138
|
const buttonLabel = isExpanded ? readLessLabel : readMoreLabel;
|
|
@@ -208,16 +208,6 @@ function FormField({
|
|
|
208
208
|
const [isFocused, setIsFocused] = (0, _react.useState)(false);
|
|
209
209
|
const interactive = !isDisabled && !isReadOnly;
|
|
210
210
|
|
|
211
|
-
// Ref to the native input so tapping anywhere in the input row (padding,
|
|
212
|
-
// leading/trailing gutters) focuses it on the FIRST tap — fixing the Android
|
|
213
|
-
// "two taps to open the keyboard" issue caused by the row intercepting the
|
|
214
|
-
// initial touch.
|
|
215
|
-
const inputRef = (0, _react.useRef)(null);
|
|
216
|
-
const focusInput = (0, _react.useCallback)(() => {
|
|
217
|
-
if (!interactive) return;
|
|
218
|
-
inputRef.current?.focus();
|
|
219
|
-
}, [interactive]);
|
|
220
|
-
|
|
221
211
|
// FormField States cascade — error > read only/disabled > active (focused) > idle.
|
|
222
212
|
// Disabled maps to "Read Only" since there is no dedicated disabled mode and
|
|
223
213
|
// the visual treatment is closest. This is only the DEFAULT — an explicit
|
|
@@ -350,16 +340,13 @@ function FormField({
|
|
|
350
340
|
style: requiredIndicatorStyle,
|
|
351
341
|
children: " *"
|
|
352
342
|
})]
|
|
353
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.
|
|
343
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
354
344
|
style: [inputRowStyle, inputStyle],
|
|
355
|
-
onPress: focusInput,
|
|
356
|
-
accessible: false,
|
|
357
345
|
children: [processedLeading != null && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
358
346
|
accessibilityElementsHidden: true,
|
|
359
347
|
importantForAccessibility: "no",
|
|
360
348
|
children: processedLeading
|
|
361
349
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TextInput, {
|
|
362
|
-
ref: inputRef,
|
|
363
350
|
style: [inputTextStyles, inputTextStyle],
|
|
364
351
|
value: value ?? '',
|
|
365
352
|
onChangeText: handleChangeText,
|
|
@@ -14,6 +14,7 @@ var _Button = _interopRequireDefault(require("../Button/Button"));
|
|
|
14
14
|
var _Disclaimer = _interopRequireDefault(require("../Disclaimer/Disclaimer"));
|
|
15
15
|
var _IconButton = _interopRequireDefault(require("../IconButton/IconButton"));
|
|
16
16
|
var _ActionFooter = _interopRequireDefault(require("../ActionFooter/ActionFooter"));
|
|
17
|
+
var _Slot = _interopRequireDefault(require("../Slot/Slot"));
|
|
17
18
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
18
19
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
19
20
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
@@ -274,8 +275,9 @@ function FullscreenModal({
|
|
|
274
275
|
if (footer) {
|
|
275
276
|
footerContent = footer;
|
|
276
277
|
} else if (primaryActionLabel) {
|
|
277
|
-
footerContent = /*#__PURE__*/(0, _jsxRuntime.jsxs)(
|
|
278
|
-
|
|
278
|
+
footerContent = /*#__PURE__*/(0, _jsxRuntime.jsxs)(_Slot.default, {
|
|
279
|
+
layoutDirection: "vertical",
|
|
280
|
+
modes: modes,
|
|
279
281
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_Button.default, {
|
|
280
282
|
label: primaryActionLabel,
|
|
281
283
|
modes: modes,
|
|
@@ -299,7 +301,11 @@ function FullscreenModal({
|
|
|
299
301
|
contentContainerStyle: scrollContentStyle,
|
|
300
302
|
showsVerticalScrollIndicator: false,
|
|
301
303
|
onScroll: onScroll,
|
|
302
|
-
scrollEventThrottle: 16
|
|
304
|
+
scrollEventThrottle: 16
|
|
305
|
+
// Tap an input in the body and it focuses on the FIRST tap, even when
|
|
306
|
+
// the keyboard is already open (default 'never' eats that tap).
|
|
307
|
+
,
|
|
308
|
+
keyboardShouldPersistTaps: "handled",
|
|
303
309
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
304
310
|
style: heroTextRegionStyle,
|
|
305
311
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(HeroText, {
|
|
@@ -340,10 +346,6 @@ const scrollViewStyle = {
|
|
|
340
346
|
const scrollContentStyle = {
|
|
341
347
|
flexGrow: 1
|
|
342
348
|
};
|
|
343
|
-
const footerColumnStyle = {
|
|
344
|
-
width: '100%',
|
|
345
|
-
gap: 8
|
|
346
|
-
};
|
|
347
349
|
const fullWidthStyle = {
|
|
348
350
|
width: '100%'
|
|
349
351
|
};
|