jfs-components 0.0.78 → 0.0.79
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/Attached/Attached.js +144 -0
- package/lib/commonjs/components/Card/Card.js +25 -2
- package/lib/commonjs/components/FullscreenModal/FullscreenModal.js +4 -6
- package/lib/commonjs/components/ListItem/ListItem.js +22 -15
- package/lib/commonjs/components/PlanComparisonCard/PlanComparisonCard.js +328 -0
- package/lib/commonjs/components/Slot/Slot.js +73 -0
- package/lib/commonjs/components/index.js +21 -0
- package/lib/commonjs/icons/registry.js +1 -1
- package/lib/module/components/Attached/Attached.js +139 -0
- package/lib/module/components/Card/Card.js +25 -2
- package/lib/module/components/FullscreenModal/FullscreenModal.js +4 -6
- package/lib/module/components/ListItem/ListItem.js +22 -15
- package/lib/module/components/PlanComparisonCard/PlanComparisonCard.js +322 -0
- package/lib/module/components/Slot/Slot.js +68 -0
- package/lib/module/components/index.js +3 -0
- package/lib/module/icons/registry.js +1 -1
- package/lib/typescript/src/components/Attached/Attached.d.ts +61 -0
- package/lib/typescript/src/components/Card/Card.d.ts +9 -2
- package/lib/typescript/src/components/ListItem/ListItem.d.ts +15 -5
- package/lib/typescript/src/components/PlanComparisonCard/PlanComparisonCard.d.ts +64 -0
- package/lib/typescript/src/components/Slot/Slot.d.ts +52 -0
- package/lib/typescript/src/components/index.d.ts +3 -0
- package/lib/typescript/src/icons/registry.d.ts +1 -1
- package/package.json +1 -1
- package/src/components/Attached/Attached.tsx +181 -0
- package/src/components/Card/Card.tsx +28 -1
- package/src/components/FullscreenModal/FullscreenModal.tsx +3 -3
- package/src/components/ListItem/ListItem.tsx +35 -16
- package/src/components/PlanComparisonCard/PlanComparisonCard.tsx +426 -0
- package/src/components/Slot/Slot.tsx +91 -0
- package/src/components/index.ts +3 -0
- package/src/icons/registry.ts +1 -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.
|
|
@@ -0,0 +1,144 @@
|
|
|
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
|
+
function Attached({
|
|
52
|
+
children,
|
|
53
|
+
badge,
|
|
54
|
+
position = 'bottom-right',
|
|
55
|
+
circular = true,
|
|
56
|
+
modes: propModes = _reactUtils.EMPTY_MODES,
|
|
57
|
+
style,
|
|
58
|
+
...rest
|
|
59
|
+
}) {
|
|
60
|
+
const {
|
|
61
|
+
modes: globalModes
|
|
62
|
+
} = (0, _JFSThemeProvider.useTokens)();
|
|
63
|
+
const modes = (0, _react.useMemo)(() => globalModes === _reactUtils.EMPTY_MODES && propModes === _reactUtils.EMPTY_MODES ? _reactUtils.EMPTY_MODES : {
|
|
64
|
+
...globalModes,
|
|
65
|
+
...propModes
|
|
66
|
+
}, [globalModes, propModes]);
|
|
67
|
+
const [mainSize, setMainSize] = (0, _react.useState)(ZERO_SIZE);
|
|
68
|
+
const [badgeSize, setBadgeSize] = (0, _react.useState)(ZERO_SIZE);
|
|
69
|
+
const onMainLayout = (0, _react.useCallback)(e => {
|
|
70
|
+
const {
|
|
71
|
+
width,
|
|
72
|
+
height
|
|
73
|
+
} = e.nativeEvent.layout;
|
|
74
|
+
setMainSize(prev => prev.width === width && prev.height === height ? prev : {
|
|
75
|
+
width,
|
|
76
|
+
height
|
|
77
|
+
});
|
|
78
|
+
}, []);
|
|
79
|
+
const onBadgeLayout = (0, _react.useCallback)(e => {
|
|
80
|
+
const {
|
|
81
|
+
width,
|
|
82
|
+
height
|
|
83
|
+
} = e.nativeEvent.layout;
|
|
84
|
+
setBadgeSize(prev => prev.width === width && prev.height === height ? prev : {
|
|
85
|
+
width,
|
|
86
|
+
height
|
|
87
|
+
});
|
|
88
|
+
}, []);
|
|
89
|
+
const mainChildren = (0, _react.useMemo)(() => children != null ? (0, _reactUtils.cloneChildrenWithModes)(children, modes) : null, [children, modes]);
|
|
90
|
+
const badgeChildren = (0, _react.useMemo)(() => badge != null ? (0, _reactUtils.cloneChildrenWithModes)(badge, modes) : null, [badge, modes]);
|
|
91
|
+
const badgePlacement = (0, _react.useMemo)(() => {
|
|
92
|
+
const {
|
|
93
|
+
fx,
|
|
94
|
+
fy
|
|
95
|
+
} = resolveAnchorFractions(position);
|
|
96
|
+
const measured = mainSize.width > 0 && badgeSize.width > 0;
|
|
97
|
+
let anchorX;
|
|
98
|
+
let anchorY;
|
|
99
|
+
if (circular) {
|
|
100
|
+
// Project the anchor onto the circle inscribed in the bounding box, so
|
|
101
|
+
// corner badges land on the circumference (45°) instead of the box corner.
|
|
102
|
+
const cx = mainSize.width / 2;
|
|
103
|
+
const cy = mainSize.height / 2;
|
|
104
|
+
const radius = Math.min(mainSize.width, mainSize.height) / 2;
|
|
105
|
+
const dx = (fx - 0.5) * 2; // -1 | 0 | 1
|
|
106
|
+
const dy = (fy - 0.5) * 2; // -1 | 0 | 1
|
|
107
|
+
const len = Math.hypot(dx, dy) || 1; // 'center' → 0, guard against /0
|
|
108
|
+
anchorX = cx + dx / len * radius;
|
|
109
|
+
anchorY = cy + dy / len * radius;
|
|
110
|
+
} else {
|
|
111
|
+
anchorX = mainSize.width * fx;
|
|
112
|
+
anchorY = mainSize.height * fy;
|
|
113
|
+
}
|
|
114
|
+
return {
|
|
115
|
+
position: 'absolute',
|
|
116
|
+
left: anchorX - badgeSize.width / 2,
|
|
117
|
+
top: anchorY - badgeSize.height / 2,
|
|
118
|
+
// Hide until both elements are measured to avoid a one-frame flash at (0,0).
|
|
119
|
+
opacity: measured ? 1 : 0
|
|
120
|
+
};
|
|
121
|
+
}, [position, circular, mainSize, badgeSize]);
|
|
122
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
123
|
+
style: [styles.container, style],
|
|
124
|
+
...rest,
|
|
125
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
126
|
+
onLayout: onMainLayout,
|
|
127
|
+
children: mainChildren
|
|
128
|
+
}), badgeChildren != null && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
129
|
+
style: badgePlacement,
|
|
130
|
+
onLayout: onBadgeLayout,
|
|
131
|
+
pointerEvents: "box-none",
|
|
132
|
+
children: badgeChildren
|
|
133
|
+
})]
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
const styles = {
|
|
137
|
+
// alignSelf flex-start so the wrapper hugs the main content; anchors are then
|
|
138
|
+
// computed relative to the content size rather than a stretched parent.
|
|
139
|
+
container: {
|
|
140
|
+
position: 'relative',
|
|
141
|
+
alignSelf: 'flex-start'
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
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, {
|
|
@@ -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,
|
|
@@ -340,10 +342,6 @@ const scrollViewStyle = {
|
|
|
340
342
|
const scrollContentStyle = {
|
|
341
343
|
flexGrow: 1
|
|
342
344
|
};
|
|
343
|
-
const footerColumnStyle = {
|
|
344
|
-
width: '100%',
|
|
345
|
-
gap: 8
|
|
346
|
-
};
|
|
347
345
|
const fullWidthStyle = {
|
|
348
346
|
width: '100%'
|
|
349
347
|
};
|
|
@@ -21,9 +21,10 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
|
|
|
21
21
|
const IS_IOS = _reactNative.Platform.OS === 'ios';
|
|
22
22
|
const PRESS_DELAY = IS_IOS ? 130 : 0;
|
|
23
23
|
|
|
24
|
-
// Forced modes for the
|
|
25
|
-
// overridden by external modes. Frozen so identity is stable across
|
|
26
|
-
|
|
24
|
+
// Forced modes for the leading/trailing slots — `Context: 'ListItem'` can
|
|
25
|
+
// never be overridden by external modes. Frozen so identity is stable across
|
|
26
|
+
// renders. Applied to both slots so they cascade modes identically.
|
|
27
|
+
const SLOT_FORCED_MODES = Object.freeze({
|
|
27
28
|
Context: 'ListItem'
|
|
28
29
|
});
|
|
29
30
|
|
|
@@ -38,7 +39,7 @@ const pressedOverlayStyle = {
|
|
|
38
39
|
// ---------------------------------------------------------------------------
|
|
39
40
|
|
|
40
41
|
function resolveListItemTokens(modes) {
|
|
41
|
-
// Modes used to cascade into slot children (leading / supportSlot /
|
|
42
|
+
// Modes used to cascade into slot children (leading / supportSlot / trailing).
|
|
42
43
|
// We do NOT inject an `AppearanceBrand` default here: slot content such as
|
|
43
44
|
// Buttons or Badges carry their own intended appearance, so forcing one onto
|
|
44
45
|
// them would be surprising.
|
|
@@ -137,9 +138,11 @@ const verticalSupportTextOverride = {
|
|
|
137
138
|
* - **design-token driven styling** via `getVariableByName` and `modes`
|
|
138
139
|
*
|
|
139
140
|
* Wherever the Figma layer name contains "Slot", this component exposes a
|
|
140
|
-
* dedicated React "slot" prop
|
|
141
|
+
* dedicated React "slot" prop. The leading and trailing edges share a
|
|
142
|
+
* symmetric `leading` / `trailing` slot API:
|
|
143
|
+
* - Slot "leading" → `leading`
|
|
141
144
|
* - Slot "support text" → `supportSlot`
|
|
142
|
-
* - Slot "
|
|
145
|
+
* - Slot "trailing" → `trailing`
|
|
143
146
|
*
|
|
144
147
|
* @component
|
|
145
148
|
* @param {Object} props
|
|
@@ -147,9 +150,9 @@ const verticalSupportTextOverride = {
|
|
|
147
150
|
* @param {string} [props.title='Title'] - Primary title used in the horizontal layout.
|
|
148
151
|
* @param {string} [props.supportText='Support Text'] - Support text used in both layouts when `supportSlot` is not provided.
|
|
149
152
|
* @param {boolean} [props.showSupportText=true] - Toggles rendering of the support text in Horizontal layout.
|
|
150
|
-
* @param {React.ReactNode} [props.leading] - Optional leading
|
|
153
|
+
* @param {React.ReactNode} [props.leading] - Optional leading slot. Defaults to `IconCapsule`.
|
|
151
154
|
* @param {React.ReactNode} [props.supportSlot] - Optional custom slot used instead of the default support text block.
|
|
152
|
-
* @param {React.ReactNode} [props.
|
|
155
|
+
* @param {React.ReactNode} [props.trailing] - Optional trailing slot (Figma Slot "trailing"). Horizontal layout only.
|
|
153
156
|
* @param {boolean} [props.navArrow=true] - Whether to show NavArrow on the far right (Horizontal layout only).
|
|
154
157
|
* @param {Object} [props.modes={}] - Modes object passed to `getVariableByName` for all design tokens.
|
|
155
158
|
* @param {Function} [props.onPress] - When provided, the entire item becomes pressable (navigation variant).
|
|
@@ -178,6 +181,7 @@ function ListItemImpl({
|
|
|
178
181
|
showSupportText = true,
|
|
179
182
|
leading,
|
|
180
183
|
supportSlot,
|
|
184
|
+
trailing,
|
|
181
185
|
endSlot,
|
|
182
186
|
navArrow = true,
|
|
183
187
|
modes = _reactUtils.EMPTY_MODES,
|
|
@@ -215,7 +219,7 @@ function ListItemImpl({
|
|
|
215
219
|
// Process leading slot to pass modes to children. Memoized on
|
|
216
220
|
// (leading, resolvedModes) so a parent re-render doesn't re-walk the tree.
|
|
217
221
|
const leadingElement = (0, _react.useMemo)(() => {
|
|
218
|
-
const processed = leading ? (0, _reactUtils.cloneChildrenWithModes)(_react.default.Children.toArray(leading), tokens.resolvedModes) : [];
|
|
222
|
+
const processed = leading ? (0, _reactUtils.cloneChildrenWithModes)(_react.default.Children.toArray(leading), tokens.resolvedModes, SLOT_FORCED_MODES) : [];
|
|
219
223
|
if (processed.length === 0) {
|
|
220
224
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_IconCapsule.default, {
|
|
221
225
|
modes: tokens.resolvedModes,
|
|
@@ -229,11 +233,14 @@ function ListItemImpl({
|
|
|
229
233
|
const processed = (0, _reactUtils.cloneChildrenWithModes)(_react.default.Children.toArray(supportSlot), tokens.resolvedModes);
|
|
230
234
|
return processed.length === 1 ? processed[0] : processed;
|
|
231
235
|
}, [supportSlot, tokens.resolvedModes]);
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
236
|
+
|
|
237
|
+
// `trailing` wins; `endSlot` is the deprecated alias kept for back-compat.
|
|
238
|
+
const trailingContent = trailing ?? endSlot;
|
|
239
|
+
const processedTrailing = (0, _react.useMemo)(() => {
|
|
240
|
+
if (!trailingContent) return null;
|
|
241
|
+
const processed = (0, _reactUtils.cloneChildrenWithModes)(_react.default.Children.toArray(trailingContent), tokens.resolvedModes, SLOT_FORCED_MODES);
|
|
235
242
|
return processed.length === 1 ? processed[0] : processed;
|
|
236
|
-
}, [
|
|
243
|
+
}, [trailingContent, tokens.resolvedModes]);
|
|
237
244
|
const renderSupportContent = () => {
|
|
238
245
|
if (processedSupportSlot) return processedSupportSlot;
|
|
239
246
|
|
|
@@ -279,9 +286,9 @@ function ListItemImpl({
|
|
|
279
286
|
numberOfLines: 1,
|
|
280
287
|
children: title
|
|
281
288
|
}), showSupportText && renderSupportContent()]
|
|
282
|
-
}),
|
|
289
|
+
}), processedTrailing ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
283
290
|
style: tokens.trailingWrapperStyle,
|
|
284
|
-
children:
|
|
291
|
+
children: processedTrailing
|
|
285
292
|
}) : null, navArrow && /*#__PURE__*/(0, _jsxRuntime.jsx)(_NavArrow.default, {
|
|
286
293
|
direction: "Forward",
|
|
287
294
|
modes: tokens.resolvedModes
|