jfs-components 0.1.28 → 0.1.30

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 (26) hide show
  1. package/CHANGELOG.md +15 -2
  2. package/lib/commonjs/components/CompareTable/CompareTable.js +191 -25
  3. package/lib/commonjs/components/ContentSheet/ContentSheet.js +66 -6
  4. package/lib/commonjs/components/MoneyValue/MoneyValue.js +179 -54
  5. package/lib/commonjs/components/PdpCcCard/PdpCcCard.js +7 -1
  6. package/lib/commonjs/components/Table/Table.js +27 -7
  7. package/lib/commonjs/icons/registry.js +1 -1
  8. package/lib/module/components/CompareTable/CompareTable.js +191 -26
  9. package/lib/module/components/ContentSheet/ContentSheet.js +69 -9
  10. package/lib/module/components/MoneyValue/MoneyValue.js +182 -57
  11. package/lib/module/components/PdpCcCard/PdpCcCard.js +7 -1
  12. package/lib/module/components/Table/Table.js +27 -7
  13. package/lib/module/icons/registry.js +1 -1
  14. package/lib/typescript/src/components/CompareTable/CompareTable.d.ts +21 -1
  15. package/lib/typescript/src/components/ContentSheet/ContentSheet.d.ts +14 -1
  16. package/lib/typescript/src/components/MoneyValue/MoneyValue.d.ts +10 -1
  17. package/lib/typescript/src/components/PdpCcCard/PdpCcCard.d.ts +25 -1
  18. package/lib/typescript/src/components/Table/Table.d.ts +9 -1
  19. package/lib/typescript/src/icons/registry.d.ts +1 -1
  20. package/package.json +1 -1
  21. package/src/components/CompareTable/CompareTable.tsx +224 -30
  22. package/src/components/ContentSheet/ContentSheet.tsx +88 -8
  23. package/src/components/MoneyValue/MoneyValue.tsx +216 -65
  24. package/src/components/PdpCcCard/PdpCcCard.tsx +36 -1
  25. package/src/components/Table/Table.tsx +29 -4
  26. 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.1.30] - 2026-07-10
8
+
9
+ - `MoneyValue` — new `editable` prop (inline edit mode): tapping the value shows a numeric `TextInput` that accepts only digits + one decimal point; edit commits on blur/submit via new `onValueChange` callback; keyboard avoidance lifts the component above the keyboard using `Keyboard.addListener` + `measureInWindow` (same pattern as `ActionFooter`).
10
+ - `ContentSheet` — new `visible` prop controls a bottom pop-up / exit animation driven by Reanimated's `withSpring` (same spring config as `Drawer`: damping 32, stiffness 300). Sheet springs up from below the bottom edge on mount / `visible=true` and springs back down on `visible=false`. Animation composes with keyboard avoidance so both `translateY` offsets are summed in a single `useAnimatedStyle`.
11
+ - `CompareTable` — new `columnWidth` prop enables fixed-width scroll mode: each selection card and table cell gets this exact width; the cards row and every section body become independent horizontal `ScrollView`s whose scroll events are chained so they stay aligned in lockstep; the cards row pins to the top as a sticky header on the vertical axis; gray section-header rows render full-width outside the horizontal scroll so they don't slide with the cells.
12
+ - `PdpCcCard` — new `numberOfLines`, `disableTruncation`, and `singleLine` props passed through to the `Title` component for precise control over title truncation and wrapping.
13
+ - `Table` — new `columnWidth` prop applies a fixed width to every column (header + body) for uniform column sizing in scrollable layouts; new `scrollable` prop wraps the table in a horizontal `ScrollView`.
14
+ - Stories and MDX documentation updated for all new features.
15
+
16
+ ---
17
+
7
18
  ## [0.1.28] - 2026-07-07
8
19
 
9
20
  - Added `FormUpload` — token-driven attachment field with label, support text, `<Form>` validation integration, and a controlled `attachments` / `onAttachmentsChange` API. Renders a row of `Additem` cells (preview + empty upload trigger) with optional `maxCount` cap; accepts an injected `picker` callback so consumers wire their own image/document picker without adding native deps to the library.
@@ -17,10 +28,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
17
28
 
18
29
  ## [0.1.27] - 2026-07-07
19
30
 
20
- - Added `ProfileCard` — dark rounded profile summary card with `Attached` monogram `Avatar`, optional badge, name + `UpiHandle` pill row, and a body slot; `Profile Card Appearance` mode (`Default` | `Premium`) recolors the whole card via `modes`.
31
+ - Added `ProfileCard` — dark rounded profile summary card (`profileCard/*` tokens) with an `Attached` monogram `Avatar` (optional `IconCapsule` badge), declarative header props (`name`, `monogram`, `avatarSource`, `upiHandles`) or a full `header` slot override, and a body slot below a `Divider`. Forces `context 10: jioPlus` so nested children (`ListItem`, `UpiHandle`, `Text`, `Button`, …) resolve on-dark profile-card tokens automatically; `Profile Card Appearance` mode (`Default` | `Premium`) recolors the whole card via `modes`.
32
+ - `UpiHandle` — avatar visibility is now **source-driven**: the leading image renders only when `source` / `avatarSource` is provided; omitting it hides the avatar and force-sets `UPI Handle Image: False` so left padding stays symmetric (imageless layout used by `ProfileCard` handle pills). Removed the bundled default `Image.png` placeholder. Consumer `UPI Handle Image` mode values are overridden by source presence so avatar visibility and token layout stay in lockstep. New `WithoutImage` story and MDX usage docs.
33
+ - `Section` — `Section.Bento` stories updated with `UpiHandle` pills that pass explicit avatar `source` values, matching the Figma Bento nav/upi layout (node 216-13845).
34
+ - Figma token sync — `context 10` mode renamed `Profile card` → `jioPlus`; `Profile Card Appearance` collection wired for `ProfileCard` / `UpiHandle`.
21
35
  - `Carousel` — new `type="Numbered"` variant: full-bleed slides with floating `AutoplayControl` + `NumberPagination` overlay at the bottom center; interactive play/pause state wired to autoplay.
22
36
  - `Carousel` — **Fix:** autoplay pagination no longer flickers back to the previous active page during scroll animations; `activeIndex` is derived from settled offsets instead of intermediate scroll positions.
23
- - `UpiHandle` — **Fix:** avatar image visibility now follows the presence of `source` / `avatarSource` (`showImage` no longer renders an empty avatar slot when no source is provided); `UPI Handle Image` mode is force-set from the resolved source so token layout stays in sync.
24
37
  - Regenerated component MDX token documentation and `.token-metadata.json` across the library.
25
38
 
26
39
  ---
@@ -4,7 +4,7 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.default = void 0;
7
- var _react = _interopRequireDefault(require("react"));
7
+ var _react = _interopRequireWildcard(require("react"));
8
8
  var _reactNative = require("react-native");
9
9
  var _figmaVariablesResolver = require("../../design-tokens/figma-variables-resolver");
10
10
  var _reactUtils = require("../../utils/react-utils");
@@ -15,6 +15,7 @@ var _IconButton = _interopRequireDefault(require("../IconButton/IconButton"));
15
15
  var _Icon = _interopRequireDefault(require("../../icons/Icon"));
16
16
  var _jsxRuntime = require("react/jsx-runtime");
17
17
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
18
+ 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); }
18
19
  /**
19
20
  * Brand link colour used by the selection-card CTA. The Figma node resolves
20
21
  * `button/foreground` to this value, but no reproducible token mode currently
@@ -111,7 +112,8 @@ function CompareTable({
111
112
  maxColumns = 4,
112
113
  modes = _reactUtils.EMPTY_MODES,
113
114
  style,
114
- disableTruncation
115
+ disableTruncation,
116
+ columnWidth
115
117
  }) {
116
118
  // --- selection card tokens ------------------------------------------------
117
119
  const cardBg = (0, _figmaVariablesResolver.getVariableByName)('selectionCard/background/color', modes) ?? '#ffffff';
@@ -168,6 +170,70 @@ function CompareTable({
168
170
  const showAddCard = onAddColumn != null && columnCount < maxColumns;
169
171
  /** Grid columns shared by the selection-card row and every table row. */
170
172
  const gridColumnCount = columnCount + (showAddCard ? 1 : 0);
173
+ const showCardsRow = columnCount > 0 || showAddCard;
174
+
175
+ // --- fixed-width scroll mode -------------------------------------------
176
+ // `columnWidth` opts the component into scroll mode (see prop doc). In that
177
+ // mode the cards row and each section's table body are independent
178
+ // horizontal ScrollViews; their scroll events are chained so they move in
179
+ // lockstep, and the cards row pins to the top on the vertical axis.
180
+ const useFixedWidth = columnWidth != null;
181
+ /** Total scrollable content width shared by every horizontal surface. */
182
+ const contentWidth = useFixedWidth ? gridColumnCount * columnWidth : 0;
183
+ const cardsScrollRef = (0, _react.useRef)(null);
184
+ const bodyScrollRefs = (0, _react.useRef)(new Map());
185
+ // Stable per-section ref-callback instances so React doesn't detach/reattach
186
+ // on every render (which would churn the map and re-fire scrollTo).
187
+ const bodyRefCallbacks = (0, _react.useRef)(new Map());
188
+ /** Last offset we synced everyone to. Echoed onScroll events reporting this
189
+ * same value are ignored — this is what prevents feedback loops. */
190
+ const lastSyncX = (0, _react.useRef)(0);
191
+ const applySync = (0, _react.useCallback)((x, source) => {
192
+ if (Math.abs(x - lastSyncX.current) < 0.5) return;
193
+ lastSyncX.current = x;
194
+ if (source !== 'cards' && cardsScrollRef.current) {
195
+ cardsScrollRef.current.scrollTo({
196
+ x,
197
+ animated: false
198
+ });
199
+ }
200
+ bodyScrollRefs.current.forEach((ref, idx) => {
201
+ if (source === 'cards' || idx !== source) {
202
+ ref.scrollTo({
203
+ x,
204
+ animated: false
205
+ });
206
+ }
207
+ });
208
+ }, []);
209
+ const onCardsScroll = (0, _react.useCallback)(e => {
210
+ applySync(e.nativeEvent.contentOffset.x, 'cards');
211
+ }, [applySync]);
212
+ const onBodyScroll = (0, _react.useCallback)(idx => e => {
213
+ applySync(e.nativeEvent.contentOffset.x, idx);
214
+ }, [applySync]);
215
+ const getBodyRefCallback = (0, _react.useCallback)(idx => {
216
+ let cb = bodyRefCallbacks.current.get(idx);
217
+ if (!cb) {
218
+ cb = ref => {
219
+ if (ref) {
220
+ bodyScrollRefs.current.set(idx, ref);
221
+ // Bring a freshly-expanded section's body into sync with
222
+ // the rest of the surfaces.
223
+ if (lastSyncX.current > 0) {
224
+ ref.scrollTo({
225
+ x: lastSyncX.current,
226
+ animated: false
227
+ });
228
+ }
229
+ } else {
230
+ bodyScrollRefs.current.delete(idx);
231
+ }
232
+ };
233
+ bodyRefCallbacks.current.set(idx, cb);
234
+ }
235
+ return cb;
236
+ }, []);
171
237
  const cardLabelStyle = {
172
238
  ...NO_WRAP_TEXT,
173
239
  color: cardLabelColor,
@@ -198,8 +264,12 @@ function CompareTable({
198
264
  fontWeight: toWeight(cellFontWeight)
199
265
  };
200
266
  const cardStyle = {
201
- flex: 1,
202
- minWidth: 0,
267
+ ...(useFixedWidth ? {
268
+ width: columnWidth
269
+ } : {
270
+ flex: 1,
271
+ minWidth: 0
272
+ }),
203
273
  minHeight: 147,
204
274
  backgroundColor: cardBg,
205
275
  borderRadius: cardRadius,
@@ -318,6 +388,7 @@ function CompareTable({
318
388
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_Table.default.Cell, {
319
389
  modes: modes,
320
390
  flex: 1,
391
+ width: useFixedWidth ? columnWidth : undefined,
321
392
  align: "left",
322
393
  isLastColumn: isLastCol,
323
394
  isLastRow: isLastRow,
@@ -371,31 +442,126 @@ function CompareTable({
371
442
  })
372
443
  })]
373
444
  });
374
- return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
375
- style: [{
376
- width: '100%',
377
- backgroundColor: cardBg,
378
- borderWidth: 1,
379
- borderColor: '#e8e8e8',
380
- overflow: 'hidden'
381
- }, style],
382
- children: [(columnCount > 0 || showAddCard) && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
445
+
446
+ // Render a single section in fixed-width scroll mode. Unlike `renderTable`
447
+ // (used in flex mode), the gray header row is rendered full-width OUTSIDE
448
+ // the body's horizontal ScrollView so it stays put horizontally (it does
449
+ // not slide with the cells) while still scrolling vertically with the table.
450
+ // The body rows live inside a horizontal ScrollView that is scroll-synced
451
+ // with the cards row and every other section's body.
452
+ const renderSection = (section, index) => /*#__PURE__*/(0, _jsxRuntime.jsxs)(_Accordion.default, {
453
+ title: section.title,
454
+ defaultExpanded: section.defaultExpanded ?? index === 0,
455
+ modes: modes,
456
+ children: [section.header != null && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
383
457
  style: {
384
- flexDirection: 'row',
385
- width: '100%'
458
+ width: '100%',
459
+ backgroundColor: headerBg,
460
+ paddingHorizontal: headerPaddingH,
461
+ paddingVertical: headerPaddingV,
462
+ borderBottomWidth: cellBorderSize,
463
+ borderBottomColor: cellBorderColor
386
464
  },
387
- children: [columns.map(renderCard), showAddCard && renderAddCard()]
388
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
465
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
466
+ style: headerTextStyle,
467
+ children: section.header
468
+ })
469
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ScrollView, {
470
+ ref: getBodyRefCallback(index),
471
+ horizontal: true,
472
+ showsHorizontalScrollIndicator: false,
473
+ scrollEventThrottle: 16,
474
+ onScroll: onBodyScroll(index),
475
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
476
+ style: {
477
+ width: contentWidth
478
+ },
479
+ children: section.rows.map((row, rowIndex) => {
480
+ const isLastRow = rowIndex === section.rows.length - 1;
481
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
482
+ style: {
483
+ flexDirection: 'row',
484
+ width: '100%'
485
+ },
486
+ children: [Array.from({
487
+ length: columnCount
488
+ }).map((_, colIndex) => renderTableCell(renderCellContent(row.values?.[colIndex], `${rowIndex}-${colIndex}`), colIndex, isLastRow, colIndex)), showAddCard && renderTableCell(null, gridColumnCount - 1, isLastRow, '__add_spacer__')]
489
+ }, row.key ?? rowIndex);
490
+ })
491
+ })
492
+ })]
493
+ }, section.key ?? section.title ?? index);
494
+ const outerStyle = {
495
+ width: '100%',
496
+ backgroundColor: cardBg,
497
+ borderWidth: 1,
498
+ borderColor: '#e8e8e8',
499
+ overflow: 'hidden'
500
+ };
501
+
502
+ // --- flex mode (columnWidth omitted): original behaviour, unchanged -------
503
+ if (!useFixedWidth) {
504
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
505
+ style: [outerStyle, style],
506
+ children: [showCardsRow && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
507
+ style: {
508
+ flexDirection: 'row',
509
+ width: '100%'
510
+ },
511
+ children: [columns.map(renderCard), showAddCard && renderAddCard()]
512
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
513
+ style: {
514
+ width: '100%'
515
+ },
516
+ children: sections.map((section, index) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_Accordion.default, {
517
+ title: section.title,
518
+ defaultExpanded: section.defaultExpanded ?? index === 0,
519
+ modes: modes,
520
+ children: renderTable(section)
521
+ }, section.key ?? section.title ?? index))
522
+ })]
523
+ });
524
+ }
525
+
526
+ // --- fixed-width scroll mode: chained horizontal sync + sticky cards -----
527
+ // The cards row (index 0) is a sticky header of the vertical ScrollView so
528
+ // it pins to the top while the sections scroll beneath. The cards row is
529
+ // itself a horizontal ScrollView, synced with every section body. For the
530
+ // vertical scroll (and thus the sticky behaviour) to engage, the host must
531
+ // give CompareTable a bounded height (e.g. a sized container or flex:1).
532
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
533
+ style: [outerStyle, style, {
534
+ flexDirection: 'column',
535
+ flex: 1
536
+ }],
537
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.ScrollView, {
389
538
  style: {
390
- width: '100%'
539
+ width: '100%',
540
+ flex: 1
391
541
  },
392
- children: sections.map((section, index) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_Accordion.default, {
393
- title: section.title,
394
- defaultExpanded: section.defaultExpanded ?? index === 0,
395
- modes: modes,
396
- children: renderTable(section)
397
- }, section.key ?? section.title ?? index))
398
- })]
542
+ stickyHeaderIndices: showCardsRow ? [0] : [],
543
+ showsVerticalScrollIndicator: false,
544
+ directionalLockEnabled: true,
545
+ children: [showCardsRow && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ScrollView, {
546
+ ref: cardsScrollRef,
547
+ horizontal: true,
548
+ showsHorizontalScrollIndicator: false,
549
+ scrollEventThrottle: 16,
550
+ onScroll: onCardsScroll,
551
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
552
+ style: {
553
+ flexDirection: 'row',
554
+ width: contentWidth
555
+ },
556
+ children: [columns.map(renderCard), showAddCard && renderAddCard()]
557
+ })
558
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
559
+ style: {
560
+ width: '100%'
561
+ },
562
+ children: sections.map(renderSection)
563
+ })]
564
+ })
399
565
  });
400
566
  }
401
567
  var _default = exports.default = CompareTable;
@@ -13,6 +13,18 @@ var _reactUtils = require("../../utils/react-utils");
13
13
  var _jsxRuntime = require("react/jsx-runtime");
14
14
  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); }
15
15
  const IS_WEB = _reactNative.Platform.OS === 'web';
16
+
17
+ // Spring config — mirrors the Drawer's bottom pop-up spring so the two
18
+ // bottom-sheet surfaces share the exact same entrance feel (slight bounce,
19
+ // snappy). Keep in sync with `Drawer.tsx` SPRING_CONFIG.
20
+ const SPRING_CONFIG = {
21
+ damping: 32,
22
+ stiffness: 300,
23
+ mass: 1,
24
+ overshootClamping: false,
25
+ restDisplacementThreshold: 0.1,
26
+ restSpeedThreshold: 0.1
27
+ };
16
28
  function resolveContentSheetStyle(modes) {
17
29
  const backgroundColor = (0, _figmaVariablesResolver.getVariableByName)('contentSheet/bg', modes);
18
30
  const gap = (0, _figmaVariablesResolver.getVariableByName)('contentSheet/gap', modes);
@@ -96,6 +108,7 @@ function ContentSheet({
96
108
  keyboardSpacing = 0,
97
109
  safeAreaBottom = true,
98
110
  pinToBottom = true,
111
+ visible = true,
99
112
  style,
100
113
  ...rest
101
114
  }) {
@@ -111,10 +124,25 @@ function ContentSheet({
111
124
  paddingBottom
112
125
  }, style], [pinToBottom, resolved.container, paddingBottom, style]);
113
126
 
127
+ // Entrance / exit "pop up from bottom" animation — mirrors the Drawer. The
128
+ // sheet is bottom-anchored, so a positive `translateY` equal to the window
129
+ // height parks it fully below the bottom edge. Springing it back to `0`
130
+ // produces the same bottom pop-up the Drawer uses. The shared value is
131
+ // initialised off-screen so the very first mount pops in (no flash).
132
+ const {
133
+ height: windowHeight
134
+ } = (0, _reactNative.useWindowDimensions)();
135
+ const entrance = (0, _reactNativeReanimated.useSharedValue)(windowHeight);
136
+ (0, _react.useEffect)(() => {
137
+ entrance.value = (0, _reactNativeReanimated.withSpring)(visible ? 0 : windowHeight, SPRING_CONFIG);
138
+ }, [visible, windowHeight, entrance]);
139
+
114
140
  // Switching between the keyboard-aware and plain implementation is keyed off
115
141
  // `avoidKeyboard` (and platform). Because they are distinct component types,
116
142
  // React remounts on toggle, so the conditional `useAnimatedKeyboard` hook
117
- // inside `KeyboardAwareSheet` always runs in a stable hook order.
143
+ // inside `KeyboardAwareSheet` always runs in a stable hook order. Both
144
+ // branches receive the `entrance` shared value so the bottom pop-up plays in
145
+ // every configuration.
118
146
  const header = title ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
119
147
  style: resolved.headerStyle,
120
148
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
@@ -126,12 +154,14 @@ function ContentSheet({
126
154
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(KeyboardAwareSheet, {
127
155
  style: containerStyle,
128
156
  spacing: keyboardSpacing,
157
+ entrance: entrance,
129
158
  ...rest,
130
159
  children: [header, processedChildren]
131
160
  });
132
161
  }
133
- return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
162
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(AnimatedSheet, {
134
163
  style: containerStyle,
164
+ entrance: entrance,
135
165
  ...rest,
136
166
  children: [header, processedChildren]
137
167
  });
@@ -139,25 +169,55 @@ function ContentSheet({
139
169
  /**
140
170
  * Native-only wrapper that lifts the sheet by the live keyboard height. The
141
171
  * translation is computed on the UI thread from `useAnimatedKeyboard`, so the
142
- * sheet tracks the keyboard frame-for-frame without any JS work.
172
+ * sheet tracks the keyboard frame-for-frame without any JS work. The keyboard
173
+ * offset is summed with the entrance `translateY` (the bottom pop-up) inside a
174
+ * single `useAnimatedStyle` so both motions stay on the UI thread and never
175
+ * fight each other.
143
176
  */
144
177
  function KeyboardAwareSheet({
145
178
  style,
146
179
  spacing,
180
+ entrance,
147
181
  children,
148
182
  ...rest
149
183
  }) {
150
184
  const keyboard = (0, _reactNativeReanimated.useAnimatedKeyboard)();
151
- const keyboardStyle = (0, _reactNativeReanimated.useAnimatedStyle)(() => {
185
+ const animatedStyle = (0, _reactNativeReanimated.useAnimatedStyle)(() => {
152
186
  const height = keyboard.height.value;
187
+ const keyboardOffset = height > 0 ? -(height + spacing) : 0;
188
+ return {
189
+ transform: [{
190
+ translateY: entrance.value + keyboardOffset
191
+ }]
192
+ };
193
+ });
194
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeReanimated.default.View, {
195
+ style: [style, animatedStyle],
196
+ ...rest,
197
+ children: children
198
+ });
199
+ }
200
+
201
+ /**
202
+ * Entrance-only animated sheet (web, or when keyboard avoidance is disabled).
203
+ * Applies the same bottom pop-up `translateY` as `KeyboardAwareSheet`, minus
204
+ * the keyboard offset.
205
+ */
206
+ function AnimatedSheet({
207
+ style,
208
+ entrance,
209
+ children,
210
+ ...rest
211
+ }) {
212
+ const animatedStyle = (0, _reactNativeReanimated.useAnimatedStyle)(() => {
153
213
  return {
154
214
  transform: [{
155
- translateY: height > 0 ? -(height + spacing) : 0
215
+ translateY: entrance.value
156
216
  }]
157
217
  };
158
218
  });
159
219
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeReanimated.default.View, {
160
- style: [style, keyboardStyle],
220
+ style: [style, animatedStyle],
161
221
  ...rest,
162
222
  children: children
163
223
  });