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
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { type ImageSourcePropType, type ImageStyle, type StyleProp, type ViewStyle, type ImageResizeMode } from 'react-native';
|
|
3
|
+
export type ImageProps = {
|
|
4
|
+
/**
|
|
5
|
+
* Image source. Accepts the same shapes as React Native's `<Image>` plus a
|
|
6
|
+
* raw URL string. Naming is intentionally aligned with `Avatar`,
|
|
7
|
+
* `ProductLabel`, `CardProviderInfo`, etc. so the library has a single,
|
|
8
|
+
* predictable image-source prop name.
|
|
9
|
+
*/
|
|
10
|
+
imageSource?: ImageSourcePropType | string | undefined;
|
|
11
|
+
/**
|
|
12
|
+
* Width-to-height aspect ratio as a number, e.g. `16 / 9`, `1`, `4 / 3`.
|
|
13
|
+
* When set, the image lays out using `aspectRatio` so it scales naturally
|
|
14
|
+
* with whatever width it receives — no need to specify height.
|
|
15
|
+
*
|
|
16
|
+
* Equivalent CSS: `aspect-ratio: <ratio>`.
|
|
17
|
+
*/
|
|
18
|
+
ratio?: number | undefined;
|
|
19
|
+
/** How the image is fit inside its box. Defaults to `'cover'`. */
|
|
20
|
+
resizeMode?: ImageResizeMode | undefined;
|
|
21
|
+
/**
|
|
22
|
+
* Optional explicit width. If omitted and a `ratio` is set, the image
|
|
23
|
+
* defaults to `width: '100%'` so it fills its parent.
|
|
24
|
+
*/
|
|
25
|
+
width?: ViewStyle['width'] | undefined;
|
|
26
|
+
/** Optional explicit height. Usually you only need `ratio` instead. */
|
|
27
|
+
height?: ViewStyle['height'] | undefined;
|
|
28
|
+
/** Border radius applied to the image (clips the underlying raster). */
|
|
29
|
+
borderRadius?: number | undefined;
|
|
30
|
+
/** Style merged onto the underlying RN `<Image>`. */
|
|
31
|
+
style?: StyleProp<ImageStyle> | undefined;
|
|
32
|
+
/** Accessibility label forwarded to RN `<Image>`. */
|
|
33
|
+
accessibilityLabel?: string | undefined;
|
|
34
|
+
/** Hide from the a11y tree (e.g. when image is purely decorative). */
|
|
35
|
+
accessibilityElementsHidden?: boolean | undefined;
|
|
36
|
+
importantForAccessibility?: 'auto' | 'yes' | 'no' | 'no-hide-descendants' | undefined;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* `Image` — the library's standard raster image primitive.
|
|
40
|
+
*
|
|
41
|
+
* Why this exists:
|
|
42
|
+
* - Gives consumers a single component for "show an image at a given aspect
|
|
43
|
+
* ratio inside a flex container" without having to remember the
|
|
44
|
+
* `width:'100%' / height:'100%' / resizeMode:'cover'` boilerplate.
|
|
45
|
+
* - Centralizes URL-vs-`{uri}` normalization that several components were
|
|
46
|
+
* re-implementing.
|
|
47
|
+
* - Uses the same `imageSource` prop name as the rest of the library
|
|
48
|
+
* (`Avatar`, `ProductLabel`, `CardProviderInfo`, ...) for a unified API.
|
|
49
|
+
*
|
|
50
|
+
* Layout rules:
|
|
51
|
+
* - If `ratio` is provided, the image lays out with `aspectRatio: ratio`
|
|
52
|
+
* and (unless `width` is given) fills the parent's width.
|
|
53
|
+
* - If neither `ratio` nor explicit dimensions are given, the image fills
|
|
54
|
+
* its parent (`width: '100%'`, `height: '100%'`) — same default as the
|
|
55
|
+
* most common usage in this library (background media, hero images).
|
|
56
|
+
*/
|
|
57
|
+
declare function Image({ imageSource, ratio, resizeMode, width, height, borderRadius, style, accessibilityLabel, accessibilityElementsHidden, importantForAccessibility, }: ImageProps): import("react/jsx-runtime").JSX.Element;
|
|
58
|
+
declare const _default: React.MemoExoticComponent<typeof Image>;
|
|
59
|
+
export default _default;
|
|
60
|
+
//# sourceMappingURL=Image.d.ts.map
|
|
@@ -1,9 +1,23 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { type ViewStyle, type TextStyle, type StyleProp } from 'react-native';
|
|
2
|
+
import { type ViewStyle, type TextStyle, type StyleProp, type ImageSourcePropType } from 'react-native';
|
|
3
3
|
export interface MediaCardProps {
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* Image source for the background media. Same shape as the rest of the
|
|
6
|
+
* library (`Avatar`, `ProductLabel`, etc.) — accepts a URL string or any
|
|
7
|
+
* RN `ImageSourcePropType`. The card renders this through the shared
|
|
8
|
+
* `<Image>` component, so all image-rendering details (normalization,
|
|
9
|
+
* resize behaviour, `aspectRatio`) live there, not here.
|
|
10
|
+
*/
|
|
11
|
+
imageSource?: ImageSourcePropType | string | undefined;
|
|
12
|
+
/**
|
|
13
|
+
* Width-to-height aspect ratio for the background image when using
|
|
14
|
+
* `imageSource`, e.g. `16 / 9`. Forwarded to `<Image ratio>`.
|
|
15
|
+
*/
|
|
16
|
+
ratio?: number | undefined;
|
|
17
|
+
/**
|
|
18
|
+
* Escape hatch: a fully custom background node (e.g. a gradient view, a
|
|
19
|
+
* video). Takes precedence over `imageSource`. Prefer `imageSource` for
|
|
20
|
+
* the common case of a single background image.
|
|
7
21
|
*/
|
|
8
22
|
media?: React.ReactNode;
|
|
9
23
|
/**
|
|
@@ -23,8 +37,12 @@ export interface MediaCardProps {
|
|
|
23
37
|
* MediaCard component implementation from Figma node 1241:4140.
|
|
24
38
|
*
|
|
25
39
|
* Features a background media slot, a large title, and a glass-morphism footer.
|
|
40
|
+
*
|
|
41
|
+
* The background can be supplied either as `imageSource` (preferred — uses
|
|
42
|
+
* the shared `<Image>` primitive under the hood) or as a custom `media` node
|
|
43
|
+
* for non-image backgrounds.
|
|
26
44
|
*/
|
|
27
|
-
export declare function MediaCard({ media, children, modes, style, }: MediaCardProps): import("react/jsx-runtime").JSX.Element;
|
|
45
|
+
export declare function MediaCard({ imageSource, ratio, media, children, modes, style, }: MediaCardProps): import("react/jsx-runtime").JSX.Element;
|
|
28
46
|
export declare namespace MediaCard {
|
|
29
47
|
var Header: typeof import("./MediaCard").Header;
|
|
30
48
|
var Title: typeof import("./MediaCard").Title;
|
|
@@ -24,11 +24,12 @@ export { default as HoldingsCard, type HoldingsCardProps } from './HoldingsCard/
|
|
|
24
24
|
export { default as HStack, type HStackProps } from './HStack/HStack';
|
|
25
25
|
export { default as IconButton } from './IconButton/IconButton';
|
|
26
26
|
export { default as IconCapsule } from './IconCapsule/IconCapsule';
|
|
27
|
+
export { default as Image, type ImageProps } from './Image/Image';
|
|
27
28
|
export { default as LazyList } from './LazyList/LazyList';
|
|
28
29
|
export { default as LinearMeter, type LinearMeterProps } from './LinearMeter/LinearMeter';
|
|
29
30
|
export { default as ListGroup } from './ListGroup/ListGroup';
|
|
30
31
|
export { default as ListItem } from './ListItem/ListItem';
|
|
31
|
-
export { default as MediaCard } from './MediaCard/MediaCard';
|
|
32
|
+
export { default as MediaCard, type MediaCardProps } from './MediaCard/MediaCard';
|
|
32
33
|
export { default as MerchantProfile, type MerchantProfileProps } from './MerchantProfile/MerchantProfile';
|
|
33
34
|
export { default as MoneyValue } from './MoneyValue/MoneyValue';
|
|
34
35
|
export { default as NoteInput, type NoteInputProps } from './NoteInput/NoteInput';
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Auto-generated from SVG files in src/icons/
|
|
5
5
|
* DO NOT EDIT MANUALLY - Run "npm run icons:generate" to regenerate
|
|
6
6
|
*
|
|
7
|
-
* Generated: 2026-04-
|
|
7
|
+
* Generated: 2026-04-22T11:47:15.563Z
|
|
8
8
|
*/
|
|
9
9
|
export declare const iconRegistry: Record<string, {
|
|
10
10
|
path: string;
|
package/package.json
CHANGED
|
@@ -78,8 +78,16 @@ function CardCTA({
|
|
|
78
78
|
overflow: 'hidden',
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
+
// NOTE: `minWidth: 0` + explicit `flexShrink: 1` are required on native.
|
|
82
|
+
// Without them, Yoga's default `min-width: auto` clamps leftWrap to its
|
|
83
|
+
// single-line intrinsic text width, which steals all space from rightWrap
|
|
84
|
+
// and pushes the IconCapsule outside the card. See: text-not-wrapping
|
|
85
|
+
// inside flex rows on RN.
|
|
81
86
|
const leftWrapStyle: ViewStyle = {
|
|
82
87
|
flex: 3,
|
|
88
|
+
flexShrink: 1,
|
|
89
|
+
flexBasis: 0,
|
|
90
|
+
minWidth: 0,
|
|
83
91
|
paddingHorizontal: leftPaddingH,
|
|
84
92
|
paddingVertical: leftPaddingV,
|
|
85
93
|
gap: leftGap,
|
|
@@ -87,8 +95,22 @@ function CardCTA({
|
|
|
87
95
|
justifyContent: 'center',
|
|
88
96
|
}
|
|
89
97
|
|
|
98
|
+
// NOTE: rightWrap must NOT shrink on native. On Android (Yoga), the default
|
|
99
|
+
// `flex: 2` shorthand expands to `{ flexGrow: 2, flexShrink: 1, flexBasis: 0 }`,
|
|
100
|
+
// which — combined with `minWidth: 0` — lets Yoga shrink this wrapper below
|
|
101
|
+
// its own padding+IconCapsule width when leftWrap's text is long. Once that
|
|
102
|
+
// happens the horizontal padding collapses, `alignItems: 'flex-end'` pins
|
|
103
|
+
// the IconCapsule against the inner edge, and the icon ends up visually
|
|
104
|
+
// touching the body text (the wrapper "appears not to exist"). Web hides
|
|
105
|
+
// this because browsers honor `min-width: auto` on flex items. Use
|
|
106
|
+
// explicit `flexGrow`/`flexShrink: 0`/`flexBasis: 'auto'` so the wrapper
|
|
107
|
+
// is sized to its content as a floor and only grows for the design's
|
|
108
|
+
// 3:2 ratio when extra space is available. leftWrap already absorbs tight
|
|
109
|
+
// space via `flexShrink: 1` + `minWidth: 0`.
|
|
90
110
|
const rightWrapStyle: ViewStyle = {
|
|
91
|
-
|
|
111
|
+
flexGrow: 2,
|
|
112
|
+
flexShrink: 0,
|
|
113
|
+
flexBasis: 'auto',
|
|
92
114
|
paddingHorizontal: rightPaddingH,
|
|
93
115
|
paddingVertical: rightPaddingV,
|
|
94
116
|
alignItems: 'flex-end',
|
|
@@ -97,6 +119,8 @@ function CardCTA({
|
|
|
97
119
|
|
|
98
120
|
const textWrapStyle: ViewStyle = {
|
|
99
121
|
gap: textGap,
|
|
122
|
+
alignSelf: 'stretch',
|
|
123
|
+
minWidth: 0,
|
|
100
124
|
}
|
|
101
125
|
|
|
102
126
|
const titleStyle: TextStyle = {
|
|
@@ -279,8 +279,26 @@ export function Carousel({
|
|
|
279
279
|
|
|
280
280
|
>
|
|
281
281
|
{items.map((child, index) => {
|
|
282
|
-
|
|
282
|
+
// Strict slot box: width must be honored; never grow or shrink with
|
|
283
|
+
// content, and clip anything that misbehaves (e.g. a child whose
|
|
284
|
+
// inner flex layout would otherwise leak into the next slot on
|
|
285
|
+
// native).
|
|
286
|
+
const slotStyle: ViewStyle = {
|
|
283
287
|
width: effectiveItemWidth > 0 ? effectiveItemWidth : undefined,
|
|
288
|
+
flexGrow: 0,
|
|
289
|
+
flexShrink: 0,
|
|
290
|
+
flexBasis: effectiveItemWidth > 0 ? effectiveItemWidth : 'auto',
|
|
291
|
+
overflow: 'hidden',
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// The cloned style forces the child's outer node to also honor the
|
|
295
|
+
// slot width strictly. Without this, a child with a weird intrinsic
|
|
296
|
+
// size can render wider than the slot and visually overflow.
|
|
297
|
+
const childOverrideStyle: ViewStyle = {
|
|
298
|
+
width: effectiveItemWidth > 0 ? effectiveItemWidth : undefined,
|
|
299
|
+
maxWidth: effectiveItemWidth > 0 ? effectiveItemWidth : undefined,
|
|
300
|
+
flexGrow: 0,
|
|
301
|
+
flexShrink: 0,
|
|
284
302
|
}
|
|
285
303
|
|
|
286
304
|
// Pass modes down to children
|
|
@@ -290,12 +308,12 @@ export function Carousel({
|
|
|
290
308
|
...((child.props as any)?.modes || {}),
|
|
291
309
|
...modes,
|
|
292
310
|
},
|
|
293
|
-
style: [
|
|
311
|
+
style: [childOverrideStyle, (child.props as any)?.style],
|
|
294
312
|
})
|
|
295
313
|
: child
|
|
296
314
|
|
|
297
315
|
return (
|
|
298
|
-
<View key={index} style={
|
|
316
|
+
<View key={index} style={slotStyle}>
|
|
299
317
|
{childWithModes}
|
|
300
318
|
</View>
|
|
301
319
|
)
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import React, { useMemo } from 'react'
|
|
2
|
+
import {
|
|
3
|
+
Image as RNImage,
|
|
4
|
+
View,
|
|
5
|
+
StyleSheet,
|
|
6
|
+
type ImageSourcePropType,
|
|
7
|
+
type ImageStyle,
|
|
8
|
+
type StyleProp,
|
|
9
|
+
type ViewStyle,
|
|
10
|
+
type ImageResizeMode,
|
|
11
|
+
} from 'react-native'
|
|
12
|
+
|
|
13
|
+
export type ImageProps = {
|
|
14
|
+
/**
|
|
15
|
+
* Image source. Accepts the same shapes as React Native's `<Image>` plus a
|
|
16
|
+
* raw URL string. Naming is intentionally aligned with `Avatar`,
|
|
17
|
+
* `ProductLabel`, `CardProviderInfo`, etc. so the library has a single,
|
|
18
|
+
* predictable image-source prop name.
|
|
19
|
+
*/
|
|
20
|
+
imageSource?: ImageSourcePropType | string | undefined
|
|
21
|
+
/**
|
|
22
|
+
* Width-to-height aspect ratio as a number, e.g. `16 / 9`, `1`, `4 / 3`.
|
|
23
|
+
* When set, the image lays out using `aspectRatio` so it scales naturally
|
|
24
|
+
* with whatever width it receives — no need to specify height.
|
|
25
|
+
*
|
|
26
|
+
* Equivalent CSS: `aspect-ratio: <ratio>`.
|
|
27
|
+
*/
|
|
28
|
+
ratio?: number | undefined
|
|
29
|
+
/** How the image is fit inside its box. Defaults to `'cover'`. */
|
|
30
|
+
resizeMode?: ImageResizeMode | undefined
|
|
31
|
+
/**
|
|
32
|
+
* Optional explicit width. If omitted and a `ratio` is set, the image
|
|
33
|
+
* defaults to `width: '100%'` so it fills its parent.
|
|
34
|
+
*/
|
|
35
|
+
width?: ViewStyle['width'] | undefined
|
|
36
|
+
/** Optional explicit height. Usually you only need `ratio` instead. */
|
|
37
|
+
height?: ViewStyle['height'] | undefined
|
|
38
|
+
/** Border radius applied to the image (clips the underlying raster). */
|
|
39
|
+
borderRadius?: number | undefined
|
|
40
|
+
/** Style merged onto the underlying RN `<Image>`. */
|
|
41
|
+
style?: StyleProp<ImageStyle> | undefined
|
|
42
|
+
/** Accessibility label forwarded to RN `<Image>`. */
|
|
43
|
+
accessibilityLabel?: string | undefined
|
|
44
|
+
/** Hide from the a11y tree (e.g. when image is purely decorative). */
|
|
45
|
+
accessibilityElementsHidden?: boolean | undefined
|
|
46
|
+
importantForAccessibility?:
|
|
47
|
+
| 'auto'
|
|
48
|
+
| 'yes'
|
|
49
|
+
| 'no'
|
|
50
|
+
| 'no-hide-descendants'
|
|
51
|
+
| undefined
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function normalizeSource(
|
|
55
|
+
imageSource: ImageProps['imageSource']
|
|
56
|
+
): ImageSourcePropType | undefined {
|
|
57
|
+
if (imageSource == null) return undefined
|
|
58
|
+
if (typeof imageSource === 'string') return { uri: imageSource }
|
|
59
|
+
return imageSource
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* `Image` — the library's standard raster image primitive.
|
|
64
|
+
*
|
|
65
|
+
* Why this exists:
|
|
66
|
+
* - Gives consumers a single component for "show an image at a given aspect
|
|
67
|
+
* ratio inside a flex container" without having to remember the
|
|
68
|
+
* `width:'100%' / height:'100%' / resizeMode:'cover'` boilerplate.
|
|
69
|
+
* - Centralizes URL-vs-`{uri}` normalization that several components were
|
|
70
|
+
* re-implementing.
|
|
71
|
+
* - Uses the same `imageSource` prop name as the rest of the library
|
|
72
|
+
* (`Avatar`, `ProductLabel`, `CardProviderInfo`, ...) for a unified API.
|
|
73
|
+
*
|
|
74
|
+
* Layout rules:
|
|
75
|
+
* - If `ratio` is provided, the image lays out with `aspectRatio: ratio`
|
|
76
|
+
* and (unless `width` is given) fills the parent's width.
|
|
77
|
+
* - If neither `ratio` nor explicit dimensions are given, the image fills
|
|
78
|
+
* its parent (`width: '100%'`, `height: '100%'`) — same default as the
|
|
79
|
+
* most common usage in this library (background media, hero images).
|
|
80
|
+
*/
|
|
81
|
+
function Image({
|
|
82
|
+
imageSource,
|
|
83
|
+
ratio,
|
|
84
|
+
resizeMode = 'cover',
|
|
85
|
+
width,
|
|
86
|
+
height,
|
|
87
|
+
borderRadius,
|
|
88
|
+
style,
|
|
89
|
+
accessibilityLabel,
|
|
90
|
+
accessibilityElementsHidden,
|
|
91
|
+
importantForAccessibility,
|
|
92
|
+
}: ImageProps) {
|
|
93
|
+
const source = useMemo(() => normalizeSource(imageSource), [imageSource])
|
|
94
|
+
|
|
95
|
+
const layoutStyle: ImageStyle = useMemo(() => {
|
|
96
|
+
const s: ImageStyle = {}
|
|
97
|
+
if (ratio != null) {
|
|
98
|
+
s.aspectRatio = ratio
|
|
99
|
+
s.width = width ?? '100%'
|
|
100
|
+
if (height != null) s.height = height
|
|
101
|
+
} else {
|
|
102
|
+
s.width = width ?? '100%'
|
|
103
|
+
s.height = height ?? '100%'
|
|
104
|
+
}
|
|
105
|
+
if (borderRadius != null) s.borderRadius = borderRadius
|
|
106
|
+
return s
|
|
107
|
+
}, [ratio, width, height, borderRadius])
|
|
108
|
+
|
|
109
|
+
if (!source) {
|
|
110
|
+
return <View style={[layoutStyle, style as StyleProp<ViewStyle>]} />
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return (
|
|
114
|
+
<RNImage
|
|
115
|
+
source={source}
|
|
116
|
+
style={[layoutStyle, style]}
|
|
117
|
+
resizeMode={resizeMode}
|
|
118
|
+
accessibilityLabel={accessibilityLabel}
|
|
119
|
+
accessibilityElementsHidden={accessibilityElementsHidden}
|
|
120
|
+
importantForAccessibility={importantForAccessibility}
|
|
121
|
+
/>
|
|
122
|
+
)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export default React.memo(Image)
|