jfs-components 0.0.65 → 0.0.67
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 +8 -0
- package/lib/commonjs/components/CardCTA/CardCTA.js +28 -2
- package/lib/commonjs/components/Carousel/Carousel.js +22 -4
- package/lib/commonjs/components/Image/Image.js +78 -0
- package/lib/commonjs/components/MediaCard/MediaCard.js +40 -27
- package/lib/commonjs/components/Section/Section.js +22 -7
- package/lib/commonjs/components/index.js +7 -0
- package/lib/commonjs/icons/registry.js +1 -1
- package/lib/module/components/CardCTA/CardCTA.js +28 -2
- package/lib/module/components/Carousel/Carousel.js +22 -4
- package/lib/module/components/Image/Image.js +73 -0
- package/lib/module/components/MediaCard/MediaCard.js +39 -29
- package/lib/module/components/Section/Section.js +23 -8
- package/lib/module/components/index.js +1 -0
- package/lib/module/icons/registry.js +1 -1
- package/lib/typescript/src/components/Image/Image.d.ts +60 -0
- package/lib/typescript/src/components/MediaCard/MediaCard.d.ts +22 -4
- package/lib/typescript/src/components/index.d.ts +2 -1
- package/lib/typescript/src/icons/registry.d.ts +1 -1
- package/package.json +1 -1
- package/src/components/CardCTA/CardCTA.tsx +25 -1
- package/src/components/Carousel/Carousel.tsx +21 -3
- package/src/components/Image/Image.tsx +125 -0
- package/src/components/MediaCard/MediaCard.tsx +110 -82
- package/src/components/Section/Section.tsx +29 -12
- package/src/components/index.ts +2 -1
- package/src/icons/registry.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,14 @@ 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.67] - 2026-04-22
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **`MediaCard` in `Carousel` (Yoga / Android):** `MediaCard.Header` no longer uses `flex: 1` shorthand (`flexBasis: 0`), which made the header size to zero on the first layout pass when the card sat in a height-unbounded parent (e.g. horizontal carousel content). Replaced with explicit `flexGrow: 1`, `flexShrink: 0`, `flexBasis: 'auto'` so the header has a content-based floor, matching the Yoga fix pattern used for `CardCTA` right-wrap.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
7
15
|
## [0.0.65] - 2026-04-21
|
|
8
16
|
|
|
9
17
|
### Added
|
|
@@ -61,23 +61,49 @@ function CardCTA({
|
|
|
61
61
|
flexDirection: 'row',
|
|
62
62
|
overflow: 'hidden'
|
|
63
63
|
};
|
|
64
|
+
|
|
65
|
+
// NOTE: `minWidth: 0` + explicit `flexShrink: 1` are required on native.
|
|
66
|
+
// Without them, Yoga's default `min-width: auto` clamps leftWrap to its
|
|
67
|
+
// single-line intrinsic text width, which steals all space from rightWrap
|
|
68
|
+
// and pushes the IconCapsule outside the card. See: text-not-wrapping
|
|
69
|
+
// inside flex rows on RN.
|
|
64
70
|
const leftWrapStyle = {
|
|
65
71
|
flex: 3,
|
|
72
|
+
flexShrink: 1,
|
|
73
|
+
flexBasis: 0,
|
|
74
|
+
minWidth: 0,
|
|
66
75
|
paddingHorizontal: leftPaddingH,
|
|
67
76
|
paddingVertical: leftPaddingV,
|
|
68
77
|
gap: leftGap,
|
|
69
78
|
alignItems: 'flex-start',
|
|
70
79
|
justifyContent: 'center'
|
|
71
80
|
};
|
|
81
|
+
|
|
82
|
+
// NOTE: rightWrap must NOT shrink on native. On Android (Yoga), the default
|
|
83
|
+
// `flex: 2` shorthand expands to `{ flexGrow: 2, flexShrink: 1, flexBasis: 0 }`,
|
|
84
|
+
// which — combined with `minWidth: 0` — lets Yoga shrink this wrapper below
|
|
85
|
+
// its own padding+IconCapsule width when leftWrap's text is long. Once that
|
|
86
|
+
// happens the horizontal padding collapses, `alignItems: 'flex-end'` pins
|
|
87
|
+
// the IconCapsule against the inner edge, and the icon ends up visually
|
|
88
|
+
// touching the body text (the wrapper "appears not to exist"). Web hides
|
|
89
|
+
// this because browsers honor `min-width: auto` on flex items. Use
|
|
90
|
+
// explicit `flexGrow`/`flexShrink: 0`/`flexBasis: 'auto'` so the wrapper
|
|
91
|
+
// is sized to its content as a floor and only grows for the design's
|
|
92
|
+
// 3:2 ratio when extra space is available. leftWrap already absorbs tight
|
|
93
|
+
// space via `flexShrink: 1` + `minWidth: 0`.
|
|
72
94
|
const rightWrapStyle = {
|
|
73
|
-
|
|
95
|
+
flexGrow: 2,
|
|
96
|
+
flexShrink: 0,
|
|
97
|
+
flexBasis: 'auto',
|
|
74
98
|
paddingHorizontal: rightPaddingH,
|
|
75
99
|
paddingVertical: rightPaddingV,
|
|
76
100
|
alignItems: 'flex-end',
|
|
77
101
|
justifyContent: 'flex-start'
|
|
78
102
|
};
|
|
79
103
|
const textWrapStyle = {
|
|
80
|
-
gap: textGap
|
|
104
|
+
gap: textGap,
|
|
105
|
+
alignSelf: 'stretch',
|
|
106
|
+
minWidth: 0
|
|
81
107
|
};
|
|
82
108
|
const titleStyle = {
|
|
83
109
|
color: titleColor,
|
|
@@ -189,8 +189,26 @@ function Carousel({
|
|
|
189
189
|
onScrollBeginDrag: handleScrollBeginDrag,
|
|
190
190
|
onScrollEndDrag: handleScrollEndDrag,
|
|
191
191
|
children: items.map((child, index) => {
|
|
192
|
-
|
|
193
|
-
|
|
192
|
+
// Strict slot box: width must be honored; never grow or shrink with
|
|
193
|
+
// content, and clip anything that misbehaves (e.g. a child whose
|
|
194
|
+
// inner flex layout would otherwise leak into the next slot on
|
|
195
|
+
// native).
|
|
196
|
+
const slotStyle = {
|
|
197
|
+
width: effectiveItemWidth > 0 ? effectiveItemWidth : undefined,
|
|
198
|
+
flexGrow: 0,
|
|
199
|
+
flexShrink: 0,
|
|
200
|
+
flexBasis: effectiveItemWidth > 0 ? effectiveItemWidth : 'auto',
|
|
201
|
+
overflow: 'hidden'
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
// The cloned style forces the child's outer node to also honor the
|
|
205
|
+
// slot width strictly. Without this, a child with a weird intrinsic
|
|
206
|
+
// size can render wider than the slot and visually overflow.
|
|
207
|
+
const childOverrideStyle = {
|
|
208
|
+
width: effectiveItemWidth > 0 ? effectiveItemWidth : undefined,
|
|
209
|
+
maxWidth: effectiveItemWidth > 0 ? effectiveItemWidth : undefined,
|
|
210
|
+
flexGrow: 0,
|
|
211
|
+
flexShrink: 0
|
|
194
212
|
};
|
|
195
213
|
|
|
196
214
|
// Pass modes down to children
|
|
@@ -199,10 +217,10 @@ function Carousel({
|
|
|
199
217
|
...(child.props?.modes || {}),
|
|
200
218
|
...modes
|
|
201
219
|
},
|
|
202
|
-
style: [
|
|
220
|
+
style: [childOverrideStyle, child.props?.style]
|
|
203
221
|
}) : child;
|
|
204
222
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
205
|
-
style:
|
|
223
|
+
style: slotStyle,
|
|
206
224
|
children: childWithModes
|
|
207
225
|
}, index);
|
|
208
226
|
})
|
|
@@ -0,0 +1,78 @@
|
|
|
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 _jsxRuntime = require("react/jsx-runtime");
|
|
10
|
+
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); }
|
|
11
|
+
function normalizeSource(imageSource) {
|
|
12
|
+
if (imageSource == null) return undefined;
|
|
13
|
+
if (typeof imageSource === 'string') return {
|
|
14
|
+
uri: imageSource
|
|
15
|
+
};
|
|
16
|
+
return imageSource;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* `Image` — the library's standard raster image primitive.
|
|
21
|
+
*
|
|
22
|
+
* Why this exists:
|
|
23
|
+
* - Gives consumers a single component for "show an image at a given aspect
|
|
24
|
+
* ratio inside a flex container" without having to remember the
|
|
25
|
+
* `width:'100%' / height:'100%' / resizeMode:'cover'` boilerplate.
|
|
26
|
+
* - Centralizes URL-vs-`{uri}` normalization that several components were
|
|
27
|
+
* re-implementing.
|
|
28
|
+
* - Uses the same `imageSource` prop name as the rest of the library
|
|
29
|
+
* (`Avatar`, `ProductLabel`, `CardProviderInfo`, ...) for a unified API.
|
|
30
|
+
*
|
|
31
|
+
* Layout rules:
|
|
32
|
+
* - If `ratio` is provided, the image lays out with `aspectRatio: ratio`
|
|
33
|
+
* and (unless `width` is given) fills the parent's width.
|
|
34
|
+
* - If neither `ratio` nor explicit dimensions are given, the image fills
|
|
35
|
+
* its parent (`width: '100%'`, `height: '100%'`) — same default as the
|
|
36
|
+
* most common usage in this library (background media, hero images).
|
|
37
|
+
*/
|
|
38
|
+
function Image({
|
|
39
|
+
imageSource,
|
|
40
|
+
ratio,
|
|
41
|
+
resizeMode = 'cover',
|
|
42
|
+
width,
|
|
43
|
+
height,
|
|
44
|
+
borderRadius,
|
|
45
|
+
style,
|
|
46
|
+
accessibilityLabel,
|
|
47
|
+
accessibilityElementsHidden,
|
|
48
|
+
importantForAccessibility
|
|
49
|
+
}) {
|
|
50
|
+
const source = (0, _react.useMemo)(() => normalizeSource(imageSource), [imageSource]);
|
|
51
|
+
const layoutStyle = (0, _react.useMemo)(() => {
|
|
52
|
+
const s = {};
|
|
53
|
+
if (ratio != null) {
|
|
54
|
+
s.aspectRatio = ratio;
|
|
55
|
+
s.width = width ?? '100%';
|
|
56
|
+
if (height != null) s.height = height;
|
|
57
|
+
} else {
|
|
58
|
+
s.width = width ?? '100%';
|
|
59
|
+
s.height = height ?? '100%';
|
|
60
|
+
}
|
|
61
|
+
if (borderRadius != null) s.borderRadius = borderRadius;
|
|
62
|
+
return s;
|
|
63
|
+
}, [ratio, width, height, borderRadius]);
|
|
64
|
+
if (!source) {
|
|
65
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
66
|
+
style: [layoutStyle, style]
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Image, {
|
|
70
|
+
source: source,
|
|
71
|
+
style: [layoutStyle, style],
|
|
72
|
+
resizeMode: resizeMode,
|
|
73
|
+
accessibilityLabel: accessibilityLabel,
|
|
74
|
+
accessibilityElementsHidden: accessibilityElementsHidden,
|
|
75
|
+
importantForAccessibility: importantForAccessibility
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
var _default = exports.default = /*#__PURE__*/_react.default.memo(Image);
|
|
@@ -13,56 +13,59 @@ exports.default = void 0;
|
|
|
13
13
|
var _react = _interopRequireWildcard(require("react"));
|
|
14
14
|
var _reactNative = require("react-native");
|
|
15
15
|
var _figmaVariablesResolver = require("../../design-tokens/figma-variables-resolver");
|
|
16
|
+
var _Image = _interopRequireDefault(require("../Image/Image"));
|
|
16
17
|
var _reactUtils = require("../../utils/react-utils");
|
|
17
18
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
19
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
18
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); }
|
|
19
|
-
|
|
20
|
-
* Context to share 'modes' with child components.
|
|
21
|
-
*/const MediaCardContext = /*#__PURE__*/(0, _react.createContext)({});
|
|
21
|
+
const MediaCardContext = /*#__PURE__*/(0, _react.createContext)({});
|
|
22
22
|
/**
|
|
23
23
|
* MediaCard component implementation from Figma node 1241:4140.
|
|
24
|
-
*
|
|
24
|
+
*
|
|
25
25
|
* Features a background media slot, a large title, and a glass-morphism footer.
|
|
26
|
+
*
|
|
27
|
+
* The background can be supplied either as `imageSource` (preferred — uses
|
|
28
|
+
* the shared `<Image>` primitive under the hood) or as a custom `media` node
|
|
29
|
+
* for non-image backgrounds.
|
|
26
30
|
*/
|
|
27
31
|
function MediaCard({
|
|
32
|
+
imageSource,
|
|
33
|
+
ratio,
|
|
28
34
|
media,
|
|
29
35
|
children,
|
|
30
36
|
modes = _reactUtils.EMPTY_MODES,
|
|
31
37
|
style
|
|
32
38
|
}) {
|
|
33
|
-
// Container Tokens
|
|
34
39
|
const radius = parseFloat((0, _figmaVariablesResolver.getVariableByName)('cardMedia/radius', modes) || '24');
|
|
35
40
|
const gap = parseFloat((0, _figmaVariablesResolver.getVariableByName)('cardMedia/gap', modes) || '0');
|
|
36
|
-
// Dimensions from Figma: w=369, h=308. We can make it flexible or default to these?
|
|
37
|
-
// Usually components should be flexible, but stories will constrain them.
|
|
38
|
-
// Figma context shows fixed/hug behavior. Let's start with flex container.
|
|
39
|
-
|
|
40
41
|
const containerStyle = {
|
|
41
42
|
borderRadius: radius,
|
|
42
43
|
gap,
|
|
43
44
|
overflow: 'hidden',
|
|
44
45
|
position: 'relative',
|
|
45
|
-
|
|
46
|
-
// However, to match "Maximize existing component usage", we follow patterns.
|
|
47
|
-
// We'll trust the parent layout or style prop for width/height.
|
|
48
|
-
minHeight: 308 // inferred from Figma height as a good default or minimum
|
|
46
|
+
minHeight: 308
|
|
49
47
|
};
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
48
|
+
|
|
49
|
+
// `media` wins for back-compat / custom nodes; otherwise we delegate to
|
|
50
|
+
// the shared <Image> for image-source backgrounds. All raster-rendering
|
|
51
|
+
// concerns (URL-vs-{uri}, resizeMode, aspect-ratio) live in <Image>.
|
|
52
|
+
const background = media ?? (imageSource != null ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_Image.default, {
|
|
53
|
+
imageSource: imageSource,
|
|
54
|
+
ratio: ratio,
|
|
55
|
+
resizeMode: "cover",
|
|
56
|
+
accessibilityElementsHidden: true,
|
|
57
|
+
importantForAccessibility: "no"
|
|
58
|
+
}) : null);
|
|
56
59
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(MediaCardContext.Provider, {
|
|
57
60
|
value: {
|
|
58
61
|
modes
|
|
59
62
|
},
|
|
60
63
|
children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
61
64
|
style: [containerStyle, style],
|
|
62
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
65
|
+
children: [background ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
63
66
|
style: _reactNative.StyleSheet.absoluteFill,
|
|
64
|
-
children:
|
|
65
|
-
}), children]
|
|
67
|
+
children: background
|
|
68
|
+
}) : null, children]
|
|
66
69
|
})
|
|
67
70
|
});
|
|
68
71
|
}
|
|
@@ -80,10 +83,24 @@ function Header({
|
|
|
80
83
|
children,
|
|
81
84
|
style
|
|
82
85
|
}) {
|
|
86
|
+
// NOTE: the previous `flex: 1` shorthand expanded on Yoga (Android) to
|
|
87
|
+
// `{ flexGrow: 1, flexShrink: 1, flexBasis: 0 }`. With `flexBasis: 0` the
|
|
88
|
+
// Header has *no intrinsic floor*, so when MediaCard is placed inside a
|
|
89
|
+
// height-unbounded parent — e.g. a Carousel slot whose contentContainer
|
|
90
|
+
// is `alignItems: 'flex-start'` — Yoga's first measurement pass sizes
|
|
91
|
+
// the Header at 0 and the card's overall height becomes non-deterministic.
|
|
92
|
+
// On native this manifests as the card "over-stretching" vertically (the
|
|
93
|
+
// same Yoga foot-gun we fixed in `CardCTA` rightWrap). Web hides it
|
|
94
|
+
// because browsers honor `min-height: auto` on flex items. Use explicit
|
|
95
|
+
// `flexGrow / flexShrink: 0 / flexBasis: 'auto'` so the Header is sized
|
|
96
|
+
// to its content as a floor and only grows to consume the extra space
|
|
97
|
+
// contributed by `MediaCard`'s `minHeight: 308`.
|
|
83
98
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
84
99
|
style: [{
|
|
85
100
|
padding: 16,
|
|
86
|
-
|
|
101
|
+
flexGrow: 1,
|
|
102
|
+
flexShrink: 0,
|
|
103
|
+
flexBasis: 'auto'
|
|
87
104
|
}, style],
|
|
88
105
|
children: children
|
|
89
106
|
});
|
|
@@ -129,8 +146,6 @@ function Footer({
|
|
|
129
146
|
}) {
|
|
130
147
|
const context = (0, _react.useContext)(MediaCardContext);
|
|
131
148
|
const modes = propModes || context.modes || {};
|
|
132
|
-
|
|
133
|
-
// Tokens
|
|
134
149
|
const gap = parseFloat((0, _figmaVariablesResolver.getVariableByName)('cardMedia/footer/gap', modes) || '24');
|
|
135
150
|
const paddingHorizontal = parseFloat((0, _figmaVariablesResolver.getVariableByName)('cardMedia/footer/padding/horizontal', modes) || '16');
|
|
136
151
|
const paddingVertical = parseFloat((0, _figmaVariablesResolver.getVariableByName)('cardMedia/footer/padding/vertical', modes) || '12');
|
|
@@ -215,8 +230,6 @@ function FooterSubtitle({
|
|
|
215
230
|
children: children
|
|
216
231
|
});
|
|
217
232
|
}
|
|
218
|
-
|
|
219
|
-
// Attach sub-components
|
|
220
233
|
MediaCard.Header = Header;
|
|
221
234
|
MediaCard.Title = Title;
|
|
222
235
|
MediaCard.Footer = Footer;
|
|
@@ -90,7 +90,14 @@ const SLOT_GRID_MAX_COLUMNS = 4;
|
|
|
90
90
|
const SLOT_GRID_STAGGER_CAP = 8;
|
|
91
91
|
const SLOT_GRID_ENTER_STAGGER_MS = 35;
|
|
92
92
|
const SLOT_GRID_EXIT_STAGGER_MS = 20;
|
|
93
|
+
const SLOT_GRID_ENTER_DURATION_MS = 220;
|
|
93
94
|
const SLOT_GRID_EXIT_DURATION_MS = 160;
|
|
95
|
+
const SLOT_GRID_HEIGHT_DURATION_MS = 280;
|
|
96
|
+
|
|
97
|
+
// Standard ease-out cubic curve. Calm, professional, no overshoot — matches
|
|
98
|
+
// system-style transitions. Defined once at module scope so it isn't
|
|
99
|
+
// re-allocated per render.
|
|
100
|
+
const SLOT_GRID_EASING = _reactNativeReanimated.Easing.out(_reactNativeReanimated.Easing.cubic);
|
|
94
101
|
const slotGridRowFlowStyle = {
|
|
95
102
|
flexDirection: 'row',
|
|
96
103
|
justifyContent: 'space-between'
|
|
@@ -136,6 +143,13 @@ const SlotGrid = /*#__PURE__*/_react.default.memo(function SlotGrid({
|
|
|
136
143
|
const containerStyle = (0, _react.useMemo)(() => ({
|
|
137
144
|
gap
|
|
138
145
|
}), [gap]);
|
|
146
|
+
// Strict `width` (not `minWidth`) so every cell in every row is exactly the
|
|
147
|
+
// same size — `space-between` then distributes identical leftover into
|
|
148
|
+
// identical inter-cell gaps on every row, which keeps column N of row 1
|
|
149
|
+
// aligned with column N of rows 2/3/etc. Cells whose label is wider than
|
|
150
|
+
// `cellWidth` simply wrap their text onto more lines (taking more vertical
|
|
151
|
+
// space; the row's height grows naturally to fit the tallest cell, and the
|
|
152
|
+
// animated-height clip springs to the new total).
|
|
139
153
|
const cellStyle = (0, _react.useMemo)(() => cellWidth !== null ? {
|
|
140
154
|
width: cellWidth
|
|
141
155
|
} : undefined, [cellWidth]);
|
|
@@ -169,8 +183,9 @@ const SlotGrid = /*#__PURE__*/_react.default.memo(function SlotGrid({
|
|
|
169
183
|
// and an explicit `height` driven by a shared value.
|
|
170
184
|
// 3. The inner view reports its natural height via `onLayout`. The first
|
|
171
185
|
// measurement snaps the shared value (no first-mount animation). Every
|
|
172
|
-
// subsequent change (e.g. expand/collapse adds or removes rows)
|
|
173
|
-
// the shared value to the new natural height
|
|
186
|
+
// subsequent change (e.g. expand/collapse adds or removes rows) eases
|
|
187
|
+
// the shared value to the new natural height with a calm ease-out
|
|
188
|
+
// timing curve — no spring, no bounce, no overshoot.
|
|
174
189
|
//
|
|
175
190
|
// Visually: the container reveals/conceals content like a curtain, and the
|
|
176
191
|
// cells never deform.
|
|
@@ -184,9 +199,9 @@ const SlotGrid = /*#__PURE__*/_react.default.memo(function SlotGrid({
|
|
|
184
199
|
animatedHeight.value = h;
|
|
185
200
|
return;
|
|
186
201
|
}
|
|
187
|
-
animatedHeight.value = (0, _reactNativeReanimated.
|
|
188
|
-
|
|
189
|
-
|
|
202
|
+
animatedHeight.value = (0, _reactNativeReanimated.withTiming)(h, {
|
|
203
|
+
duration: SLOT_GRID_HEIGHT_DURATION_MS,
|
|
204
|
+
easing: SLOT_GRID_EASING,
|
|
190
205
|
reduceMotion: _reactNativeReanimated.ReduceMotion.System
|
|
191
206
|
});
|
|
192
207
|
}, [animatedHeight]);
|
|
@@ -210,8 +225,8 @@ const SlotGrid = /*#__PURE__*/_react.default.memo(function SlotGrid({
|
|
|
210
225
|
const enterStaggerSteps = Math.min(extraOrdinal, SLOT_GRID_STAGGER_CAP);
|
|
211
226
|
const reverseOrdinal = Math.max(0, extrasCount - 1 - extraOrdinal);
|
|
212
227
|
const exitStaggerSteps = Math.min(reverseOrdinal, SLOT_GRID_STAGGER_CAP);
|
|
213
|
-
const entering = _reactNativeReanimated.FadeInUp.
|
|
214
|
-
const exiting = _reactNativeReanimated.FadeOutUp.duration(SLOT_GRID_EXIT_DURATION_MS).delay(exitStaggerSteps * SLOT_GRID_EXIT_STAGGER_MS).reduceMotion(_reactNativeReanimated.ReduceMotion.System);
|
|
228
|
+
const entering = _reactNativeReanimated.FadeInUp.duration(SLOT_GRID_ENTER_DURATION_MS).easing(SLOT_GRID_EASING).delay(enterStaggerSteps * SLOT_GRID_ENTER_STAGGER_MS).reduceMotion(_reactNativeReanimated.ReduceMotion.System);
|
|
229
|
+
const exiting = _reactNativeReanimated.FadeOutUp.duration(SLOT_GRID_EXIT_DURATION_MS).easing(SLOT_GRID_EASING).delay(exitStaggerSteps * SLOT_GRID_EXIT_STAGGER_MS).reduceMotion(_reactNativeReanimated.ReduceMotion.System);
|
|
215
230
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeReanimated.default.View, {
|
|
216
231
|
entering: entering,
|
|
217
232
|
exiting: exiting,
|
|
@@ -201,6 +201,12 @@ Object.defineProperty(exports, "IconCapsule", {
|
|
|
201
201
|
return _IconCapsule.default;
|
|
202
202
|
}
|
|
203
203
|
});
|
|
204
|
+
Object.defineProperty(exports, "Image", {
|
|
205
|
+
enumerable: true,
|
|
206
|
+
get: function () {
|
|
207
|
+
return _Image.default;
|
|
208
|
+
}
|
|
209
|
+
});
|
|
204
210
|
Object.defineProperty(exports, "InputSearch", {
|
|
205
211
|
enumerable: true,
|
|
206
212
|
get: function () {
|
|
@@ -531,6 +537,7 @@ var _HoldingsCard = _interopRequireDefault(require("./HoldingsCard/HoldingsCard"
|
|
|
531
537
|
var _HStack = _interopRequireDefault(require("./HStack/HStack"));
|
|
532
538
|
var _IconButton = _interopRequireDefault(require("./IconButton/IconButton"));
|
|
533
539
|
var _IconCapsule = _interopRequireDefault(require("./IconCapsule/IconCapsule"));
|
|
540
|
+
var _Image = _interopRequireDefault(require("./Image/Image"));
|
|
534
541
|
var _LazyList = _interopRequireDefault(require("./LazyList/LazyList"));
|
|
535
542
|
var _LinearMeter = _interopRequireDefault(require("./LinearMeter/LinearMeter"));
|
|
536
543
|
var _ListGroup = _interopRequireDefault(require("./ListGroup/ListGroup"));
|