jfs-components 0.1.2 → 0.1.12
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 +45 -0
- package/lib/commonjs/components/Accordion/Accordion.js +2 -1
- package/lib/commonjs/components/AccordionCheckbox/AccordionCheckbox.js +4 -3
- package/lib/commonjs/components/AccountCard/AccountCard.js +4 -3
- package/lib/commonjs/components/AllocationComparisonChart/AllocationComparisonChart.js +9 -6
- package/lib/commonjs/components/AmountInput/AmountInput.js +8 -5
- package/lib/commonjs/components/AreaLineChart/AreaLineChart.js +11 -7
- package/lib/commonjs/components/Badge/Badge.js +47 -8
- package/lib/commonjs/components/BenefitCard/BenefitCard.js +231 -0
- package/lib/commonjs/components/BrandChip/BrandChip.js +3 -3
- package/lib/commonjs/components/Button/Button.js +2 -1
- package/lib/commonjs/components/CardAdvisory/CardAdvisory.js +2 -1
- package/lib/commonjs/components/CardBankAccount/CardBankAccount.js +3 -2
- package/lib/commonjs/components/CardCTA/CardCTA.js +197 -27
- package/lib/commonjs/components/CardInsight/CardInsight.js +5 -1
- package/lib/commonjs/components/CcCard/CcCard.js +470 -0
- package/lib/commonjs/components/Checkbox/Checkbox.js +4 -3
- package/lib/commonjs/components/CheckboxItem/CheckboxItem.js +4 -3
- package/lib/commonjs/components/CircularProgressBar/CircularProgressBar.js +7 -3
- package/lib/commonjs/components/CircularProgressBarDoted/CircularProgressBarDoted.js +3 -2
- package/lib/commonjs/components/CircularRating/CircularRating.js +2 -1
- package/lib/commonjs/components/ClusterBubble/ClusterBubble.js +3 -2
- package/lib/commonjs/components/CompareTable/CompareTable.js +401 -0
- package/lib/commonjs/components/ComparisonBar/ComparisonBar.js +266 -0
- package/lib/commonjs/components/CoverageBarComparison/CoverageBarComparison.js +3 -2
- package/lib/commonjs/components/DonutChart/DonutChart.js +4 -3
- package/lib/commonjs/components/Dropdown/Dropdown.js +3 -2
- package/lib/commonjs/components/DropdownInput/DropdownInput.js +38 -5
- package/lib/commonjs/components/ExpandableCheckbox/ExpandableCheckbox.js +2 -1
- package/lib/commonjs/components/FormField/FormField.js +45 -4
- package/lib/commonjs/components/Gauge/Gauge.js +1 -1
- package/lib/commonjs/components/HoldingsCard/HoldingsCard.js +3 -2
- package/lib/commonjs/components/InputSearch/InputSearch.js +6 -4
- package/lib/commonjs/components/InstitutionBadge/InstitutionBadge.js +2 -1
- package/lib/commonjs/components/ListGroup/ListGroup.js +2 -1
- package/lib/commonjs/components/ListItem/ListItem.js +3 -2
- package/lib/commonjs/components/MediaCard/MediaCard.js +4 -2
- package/lib/commonjs/components/MetricLegendItem/MetricLegendItem.js +3 -2
- package/lib/commonjs/components/MonthlyStatusGrid/MonthlyStatusGrid.js +6 -3
- package/lib/commonjs/components/NoteInput/NoteInput.js +6 -5
- package/lib/commonjs/components/PdpCcCard/PdpCcCard.js +273 -0
- package/lib/commonjs/components/PoweredByLabel/PoweredByLabel.js +3 -2
- package/lib/commonjs/components/ProductMerchandisingCard/ProductMerchandisingCard.js +427 -0
- package/lib/commonjs/components/ProductOverview/ProductOverview.js +4 -3
- package/lib/commonjs/components/ProgressBadge/ProgressBadge.js +2 -2
- package/lib/commonjs/components/ProjectionMarker/ProjectionMarker.js +162 -0
- package/lib/commonjs/components/Radio/Radio.js +5 -5
- package/lib/commonjs/components/Section/Section.js +3 -2
- package/lib/commonjs/components/Slider/Slider.js +474 -0
- package/lib/commonjs/components/Stack/Stack.js +86 -0
- package/lib/commonjs/components/SuggestiveSearch/SuggestiveSearch.js +10 -1
- package/lib/commonjs/components/SummaryTile/SummaryTile.js +3 -2
- package/lib/commonjs/components/Table/Table.js +324 -0
- package/lib/commonjs/components/Text/Text.js +2 -1
- package/lib/commonjs/components/TextInput/TextInput.js +22 -9
- package/lib/commonjs/components/TextSegment/TextSegment.js +119 -0
- package/lib/commonjs/components/ThreadHero/ThreadHero.js +4 -5
- package/lib/commonjs/components/Title/Title.js +2 -1
- package/lib/commonjs/components/Toast/Toast.js +3 -2
- package/lib/commonjs/components/TransactionBubble/TransactionBubble.js +2 -1
- package/lib/commonjs/components/TransactionStatus/TransactionStatus.js +3 -2
- package/lib/commonjs/components/UpiHandle/UpiHandle.js +2 -2
- package/lib/commonjs/components/index.js +107 -0
- package/lib/commonjs/design-tokens/Coin Variables-variables-full.json +1 -1
- package/lib/commonjs/design-tokens/figma-modes.generated.js +39 -9
- package/lib/commonjs/icons/registry.js +1 -1
- package/lib/commonjs/utils/GlassFill/GlassFill.js +271 -0
- package/lib/commonjs/utils/GlassFill/GlassFill.web.js +120 -0
- package/lib/commonjs/utils/react-utils.js +78 -0
- package/lib/commonjs/utils/web-platform-utils.js +59 -0
- package/lib/module/components/Accordion/Accordion.js +3 -2
- package/lib/module/components/AccordionCheckbox/AccordionCheckbox.js +5 -4
- package/lib/module/components/AccountCard/AccountCard.js +5 -4
- package/lib/module/components/AllocationComparisonChart/AllocationComparisonChart.js +10 -7
- package/lib/module/components/AmountInput/AmountInput.js +6 -4
- package/lib/module/components/AreaLineChart/AreaLineChart.js +12 -8
- package/lib/module/components/Badge/Badge.js +49 -10
- package/lib/module/components/BenefitCard/BenefitCard.js +225 -0
- package/lib/module/components/BrandChip/BrandChip.js +4 -4
- package/lib/module/components/Button/Button.js +3 -2
- package/lib/module/components/CardAdvisory/CardAdvisory.js +3 -2
- package/lib/module/components/CardBankAccount/CardBankAccount.js +4 -3
- package/lib/module/components/CardCTA/CardCTA.js +198 -28
- package/lib/module/components/CardInsight/CardInsight.js +5 -1
- package/lib/module/components/CcCard/CcCard.js +464 -0
- package/lib/module/components/Checkbox/Checkbox.js +5 -4
- package/lib/module/components/CheckboxItem/CheckboxItem.js +5 -4
- package/lib/module/components/CircularProgressBar/CircularProgressBar.js +8 -4
- package/lib/module/components/CircularProgressBarDoted/CircularProgressBarDoted.js +4 -3
- package/lib/module/components/CircularRating/CircularRating.js +3 -2
- package/lib/module/components/ClusterBubble/ClusterBubble.js +4 -3
- package/lib/module/components/CompareTable/CompareTable.js +396 -0
- package/lib/module/components/ComparisonBar/ComparisonBar.js +260 -0
- package/lib/module/components/CoverageBarComparison/CoverageBarComparison.js +4 -3
- package/lib/module/components/DonutChart/DonutChart.js +5 -4
- package/lib/module/components/Dropdown/Dropdown.js +4 -3
- package/lib/module/components/DropdownInput/DropdownInput.js +40 -7
- package/lib/module/components/ExpandableCheckbox/ExpandableCheckbox.js +2 -1
- package/lib/module/components/FormField/FormField.js +48 -7
- package/lib/module/components/Gauge/Gauge.js +1 -1
- package/lib/module/components/HoldingsCard/HoldingsCard.js +4 -3
- package/lib/module/components/InputSearch/InputSearch.js +6 -4
- package/lib/module/components/InstitutionBadge/InstitutionBadge.js +3 -2
- package/lib/module/components/ListGroup/ListGroup.js +3 -2
- package/lib/module/components/ListItem/ListItem.js +4 -3
- package/lib/module/components/MediaCard/MediaCard.js +4 -2
- package/lib/module/components/MetricLegendItem/MetricLegendItem.js +4 -3
- package/lib/module/components/MonthlyStatusGrid/MonthlyStatusGrid.js +7 -4
- package/lib/module/components/NoteInput/NoteInput.js +7 -6
- package/lib/module/components/PdpCcCard/PdpCcCard.js +267 -0
- package/lib/module/components/PoweredByLabel/PoweredByLabel.js +4 -3
- package/lib/module/components/ProductMerchandisingCard/ProductMerchandisingCard.js +421 -0
- package/lib/module/components/ProductOverview/ProductOverview.js +5 -4
- package/lib/module/components/ProgressBadge/ProgressBadge.js +3 -3
- package/lib/module/components/ProjectionMarker/ProjectionMarker.js +157 -0
- package/lib/module/components/Radio/Radio.js +5 -4
- package/lib/module/components/Section/Section.js +4 -3
- package/lib/module/components/Slider/Slider.js +469 -0
- package/lib/module/components/Stack/Stack.js +81 -0
- package/lib/module/components/SuggestiveSearch/SuggestiveSearch.js +10 -1
- package/lib/module/components/SummaryTile/SummaryTile.js +4 -3
- package/lib/module/components/Table/Table.js +315 -0
- package/lib/module/components/Text/Text.js +3 -2
- package/lib/module/components/TextInput/TextInput.js +24 -11
- package/lib/module/components/TextSegment/TextSegment.js +114 -0
- package/lib/module/components/ThreadHero/ThreadHero.js +5 -6
- package/lib/module/components/Title/Title.js +3 -2
- package/lib/module/components/Toast/Toast.js +4 -3
- package/lib/module/components/TransactionBubble/TransactionBubble.js +3 -2
- package/lib/module/components/TransactionStatus/TransactionStatus.js +4 -3
- package/lib/module/components/UpiHandle/UpiHandle.js +3 -3
- package/lib/module/components/index.js +11 -0
- package/lib/module/design-tokens/Coin Variables-variables-full.json +1 -1
- package/lib/module/design-tokens/figma-modes.generated.js +39 -9
- package/lib/module/icons/registry.js +1 -1
- package/lib/module/utils/GlassFill/GlassFill.js +265 -0
- package/lib/module/utils/GlassFill/GlassFill.web.js +115 -0
- package/lib/module/utils/react-utils.js +75 -0
- package/lib/module/utils/web-platform-utils.js +58 -0
- package/lib/typescript/src/components/Accordion/Accordion.d.ts +7 -1
- package/lib/typescript/src/components/AccordionCheckbox/AccordionCheckbox.d.ts +7 -1
- package/lib/typescript/src/components/AccountCard/AccountCard.d.ts +7 -1
- package/lib/typescript/src/components/AllocationComparisonChart/AllocationComparisonChart.d.ts +7 -1
- package/lib/typescript/src/components/AmountInput/AmountInput.d.ts +3 -2
- package/lib/typescript/src/components/AreaLineChart/AreaLineChart.d.ts +8 -1
- package/lib/typescript/src/components/Badge/Badge.d.ts +32 -2
- package/lib/typescript/src/components/BenefitCard/BenefitCard.d.ts +93 -0
- package/lib/typescript/src/components/BrandChip/BrandChip.d.ts +7 -1
- package/lib/typescript/src/components/Button/Button.d.ts +7 -1
- package/lib/typescript/src/components/CardAdvisory/CardAdvisory.d.ts +7 -1
- package/lib/typescript/src/components/CardBankAccount/CardBankAccount.d.ts +7 -1
- package/lib/typescript/src/components/CardCTA/CardCTA.d.ts +27 -5
- package/lib/typescript/src/components/CcCard/CcCard.d.ts +137 -0
- package/lib/typescript/src/components/Checkbox/Checkbox.d.ts +3 -2
- package/lib/typescript/src/components/CheckboxItem/CheckboxItem.d.ts +2 -2
- package/lib/typescript/src/components/CircularProgressBar/CircularProgressBar.d.ts +7 -1
- package/lib/typescript/src/components/CircularProgressBarDoted/CircularProgressBarDoted.d.ts +7 -1
- package/lib/typescript/src/components/CircularRating/CircularRating.d.ts +7 -1
- package/lib/typescript/src/components/ClusterBubble/ClusterBubble.d.ts +7 -1
- package/lib/typescript/src/components/CompareTable/CompareTable.d.ts +94 -0
- package/lib/typescript/src/components/ComparisonBar/ComparisonBar.d.ts +118 -0
- package/lib/typescript/src/components/CoverageBarComparison/CoverageBarComparison.d.ts +7 -1
- package/lib/typescript/src/components/DonutChart/DonutChart.d.ts +7 -1
- package/lib/typescript/src/components/Dropdown/Dropdown.d.ts +7 -1
- package/lib/typescript/src/components/DropdownInput/DropdownInput.d.ts +26 -1
- package/lib/typescript/src/components/ExpandableCheckbox/ExpandableCheckbox.d.ts +7 -1
- package/lib/typescript/src/components/FormField/FormField.d.ts +2 -2
- package/lib/typescript/src/components/HoldingsCard/HoldingsCard.d.ts +7 -1
- package/lib/typescript/src/components/InputSearch/InputSearch.d.ts +23 -2
- package/lib/typescript/src/components/InstitutionBadge/InstitutionBadge.d.ts +7 -1
- package/lib/typescript/src/components/ListGroup/ListGroup.d.ts +7 -1
- package/lib/typescript/src/components/ListItem/ListItem.d.ts +7 -1
- package/lib/typescript/src/components/MetricLegendItem/MetricLegendItem.d.ts +7 -1
- package/lib/typescript/src/components/MonthlyStatusGrid/MonthlyStatusGrid.d.ts +14 -2
- package/lib/typescript/src/components/NoteInput/NoteInput.d.ts +19 -2
- package/lib/typescript/src/components/PdpCcCard/PdpCcCard.d.ts +84 -0
- package/lib/typescript/src/components/PoweredByLabel/PoweredByLabel.d.ts +7 -1
- package/lib/typescript/src/components/ProductMerchandisingCard/ProductMerchandisingCard.d.ts +87 -0
- package/lib/typescript/src/components/ProductOverview/ProductOverview.d.ts +7 -1
- package/lib/typescript/src/components/ProgressBadge/ProgressBadge.d.ts +7 -1
- package/lib/typescript/src/components/ProjectionMarker/ProjectionMarker.d.ts +88 -0
- package/lib/typescript/src/components/Radio/Radio.d.ts +3 -2
- package/lib/typescript/src/components/RadioButton/RadioButton.d.ts +2 -2
- package/lib/typescript/src/components/Section/Section.d.ts +7 -1
- package/lib/typescript/src/components/Slider/Slider.d.ts +105 -0
- package/lib/typescript/src/components/Stack/Stack.d.ts +81 -0
- package/lib/typescript/src/components/SummaryTile/SummaryTile.d.ts +7 -1
- package/lib/typescript/src/components/Table/Table.d.ts +139 -0
- package/lib/typescript/src/components/Text/Text.d.ts +7 -1
- package/lib/typescript/src/components/TextInput/TextInput.d.ts +9 -29
- package/lib/typescript/src/components/TextSegment/TextSegment.d.ts +106 -0
- package/lib/typescript/src/components/ThreadHero/ThreadHero.d.ts +7 -1
- package/lib/typescript/src/components/Title/Title.d.ts +7 -1
- package/lib/typescript/src/components/Toast/Toast.d.ts +7 -1
- package/lib/typescript/src/components/TransactionBubble/TransactionBubble.d.ts +7 -1
- package/lib/typescript/src/components/TransactionStatus/TransactionStatus.d.ts +7 -1
- package/lib/typescript/src/components/UpiHandle/UpiHandle.d.ts +7 -1
- package/lib/typescript/src/components/index.d.ts +14 -3
- package/lib/typescript/src/design-tokens/figma-modes.generated.d.ts +22 -2
- package/lib/typescript/src/icons/registry.d.ts +1 -1
- package/lib/typescript/src/utils/GlassFill/GlassFill.d.ts +84 -0
- package/lib/typescript/src/utils/GlassFill/GlassFill.web.d.ts +43 -0
- package/lib/typescript/src/utils/react-utils.d.ts +47 -0
- package/lib/typescript/src/utils/web-platform-utils.d.ts +36 -0
- package/package.json +2 -1
- package/src/components/Accordion/Accordion.tsx +9 -2
- package/src/components/AccordionCheckbox/AccordionCheckbox.tsx +10 -3
- package/src/components/AccountCard/AccountCard.tsx +10 -3
- package/src/components/AllocationComparisonChart/AllocationComparisonChart.tsx +14 -4
- package/src/components/AmountInput/AmountInput.tsx +7 -5
- package/src/components/AreaLineChart/AreaLineChart.tsx +15 -5
- package/src/components/Badge/Badge.tsx +78 -8
- package/src/components/BenefitCard/BenefitCard.tsx +309 -0
- package/src/components/BrandChip/BrandChip.tsx +9 -3
- package/src/components/Button/Button.tsx +9 -2
- package/src/components/CardAdvisory/CardAdvisory.tsx +9 -2
- package/src/components/CardBankAccount/CardBankAccount.tsx +9 -2
- package/src/components/CardCTA/CardCTA.tsx +214 -41
- package/src/components/CardInsight/CardInsight.tsx +7 -1
- package/src/components/CcCard/CcCard.tsx +598 -0
- package/src/components/Checkbox/Checkbox.tsx +5 -4
- package/src/components/CheckboxItem/CheckboxItem.tsx +5 -4
- package/src/components/CircularProgressBar/CircularProgressBar.tsx +14 -4
- package/src/components/CircularProgressBarDoted/CircularProgressBarDoted.tsx +10 -3
- package/src/components/CircularRating/CircularRating.tsx +9 -2
- package/src/components/ClusterBubble/ClusterBubble.tsx +9 -2
- package/src/components/CompareTable/CompareTable.tsx +525 -0
- package/src/components/ComparisonBar/ComparisonBar.tsx +356 -0
- package/src/components/CoverageBarComparison/CoverageBarComparison.tsx +9 -2
- package/src/components/DonutChart/DonutChart.tsx +10 -3
- package/src/components/Dropdown/Dropdown.tsx +9 -2
- package/src/components/DropdownInput/DropdownInput.tsx +64 -5
- package/src/components/ExpandableCheckbox/ExpandableCheckbox.tsx +8 -1
- package/src/components/FormField/FormField.tsx +52 -6
- package/src/components/Gauge/Gauge.tsx +1 -1
- package/src/components/HoldingsCard/HoldingsCard.tsx +9 -2
- package/src/components/InputSearch/InputSearch.tsx +8 -5
- package/src/components/InstitutionBadge/InstitutionBadge.tsx +9 -2
- package/src/components/ListGroup/ListGroup.tsx +9 -2
- package/src/components/ListItem/ListItem.tsx +10 -3
- package/src/components/MediaCard/MediaCard.tsx +2 -2
- package/src/components/MetricLegendItem/MetricLegendItem.tsx +9 -2
- package/src/components/MonthlyStatusGrid/MonthlyStatusGrid.tsx +17 -2
- package/src/components/NoteInput/NoteInput.tsx +8 -6
- package/src/components/PdpCcCard/PdpCcCard.tsx +356 -0
- package/src/components/PoweredByLabel/PoweredByLabel.tsx +9 -2
- package/src/components/ProductMerchandisingCard/ProductMerchandisingCard.tsx +490 -0
- package/src/components/ProductOverview/ProductOverview.tsx +10 -3
- package/src/components/ProgressBadge/ProgressBadge.tsx +9 -3
- package/src/components/ProjectionMarker/ProjectionMarker.tsx +284 -0
- package/src/components/Radio/Radio.tsx +5 -4
- package/src/components/Section/Section.tsx +10 -3
- package/src/components/Slider/Slider.tsx +635 -0
- package/src/components/Stack/Stack.tsx +131 -0
- package/src/components/SuggestiveSearch/SuggestiveSearch.tsx +10 -1
- package/src/components/SummaryTile/SummaryTile.tsx +9 -2
- package/src/components/Table/Table.tsx +449 -0
- package/src/components/Text/Text.tsx +9 -2
- package/src/components/TextInput/TextInput.tsx +22 -12
- package/src/components/TextSegment/TextSegment.tsx +173 -0
- package/src/components/ThreadHero/ThreadHero.tsx +10 -3
- package/src/components/Title/Title.tsx +9 -2
- package/src/components/Toast/Toast.tsx +9 -2
- package/src/components/TransactionBubble/TransactionBubble.tsx +9 -2
- package/src/components/TransactionStatus/TransactionStatus.tsx +9 -2
- package/src/components/UpiHandle/UpiHandle.tsx +9 -3
- package/src/components/index.ts +33 -3
- package/src/design-tokens/Coin Variables-variables-full.json +1 -1
- package/src/design-tokens/figma-modes.generated.ts +39 -9
- package/src/icons/registry.ts +1 -1
- package/src/utils/GlassFill/GlassFill.tsx +305 -0
- package/src/utils/GlassFill/GlassFill.web.tsx +148 -0
- package/src/utils/react-utils.ts +84 -0
- package/src/utils/web-platform-utils.ts +68 -0
- package/lib/commonjs/components/MediaCard/GlassFill.js +0 -62
- package/lib/commonjs/components/MediaCard/GlassFill.web.js +0 -48
- package/lib/module/components/MediaCard/GlassFill.js +0 -57
- package/lib/module/components/MediaCard/GlassFill.web.js +0 -43
- package/lib/typescript/scripts/extract-component-tokens.d.ts +0 -9
- package/lib/typescript/scripts/generate-component-docs.d.ts +0 -9
- package/lib/typescript/scripts/generate-icon-registry.d.ts +0 -3
- package/lib/typescript/scripts/generate-mode-types.d.ts +0 -2
- package/lib/typescript/scripts/retype-modes.d.cts +0 -2
- package/lib/typescript/src/components/MediaCard/GlassFill.d.ts +0 -47
- package/lib/typescript/src/components/MediaCard/GlassFill.web.d.ts +0 -20
- package/src/components/MediaCard/GlassFill.tsx +0 -89
- package/src/components/MediaCard/GlassFill.web.tsx +0 -53
|
@@ -0,0 +1,490 @@
|
|
|
1
|
+
import React, { useId, useMemo } from 'react'
|
|
2
|
+
import {
|
|
3
|
+
View,
|
|
4
|
+
Text,
|
|
5
|
+
Pressable,
|
|
6
|
+
StyleSheet,
|
|
7
|
+
Platform,
|
|
8
|
+
Image as RNImage,
|
|
9
|
+
type ViewStyle,
|
|
10
|
+
type TextStyle,
|
|
11
|
+
type StyleProp,
|
|
12
|
+
type ImageSourcePropType,
|
|
13
|
+
} from 'react-native'
|
|
14
|
+
import MaskedView from '@react-native-masked-view/masked-view'
|
|
15
|
+
import Svg, { Defs, LinearGradient, Stop, Rect } from 'react-native-svg'
|
|
16
|
+
import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
|
|
17
|
+
import { EMPTY_MODES, resolveTruncation } from '../../utils/react-utils'
|
|
18
|
+
import Avatar from '../Avatar/Avatar'
|
|
19
|
+
import Badge from '../Badge/Badge'
|
|
20
|
+
import Button from '../Button/Button'
|
|
21
|
+
import Image from '../Image/Image'
|
|
22
|
+
import GlassFill from '../../utils/GlassFill/GlassFill'
|
|
23
|
+
import type { Modes } from '../../design-tokens'
|
|
24
|
+
|
|
25
|
+
function normalizeImageSource(
|
|
26
|
+
src: ImageSourcePropType | string | undefined
|
|
27
|
+
): ImageSourcePropType | undefined {
|
|
28
|
+
if (src == null) return undefined
|
|
29
|
+
return typeof src === 'string' ? { uri: src } : src
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface ProductMerchandisingCardProps {
|
|
33
|
+
/**
|
|
34
|
+
* Background media for the card. Accepts a URL string or any RN
|
|
35
|
+
* `ImageSourcePropType` (same shape as `Avatar`, `MediaCard`, etc.). The
|
|
36
|
+
* image is rendered full-bleed behind the header and glass footer.
|
|
37
|
+
*/
|
|
38
|
+
imageSource?: ImageSourcePropType | string
|
|
39
|
+
/** Footer title (bold, white). */
|
|
40
|
+
title?: string
|
|
41
|
+
/** Footer subtitle (muted). */
|
|
42
|
+
subtitle?: string
|
|
43
|
+
/**
|
|
44
|
+
* Header badge label (top-right). The badge is hidden when this is
|
|
45
|
+
* `undefined` and no `badge` override is provided.
|
|
46
|
+
*/
|
|
47
|
+
badgeLabel?: string
|
|
48
|
+
/**
|
|
49
|
+
* Full override for the header badge. Takes precedence over `badgeLabel`.
|
|
50
|
+
*/
|
|
51
|
+
badge?: React.ReactNode
|
|
52
|
+
/** Show the header avatar (top-left). Defaults to `true`. */
|
|
53
|
+
showAvatar?: boolean
|
|
54
|
+
/** Avatar image source. */
|
|
55
|
+
avatarSource?: ImageSourcePropType | string
|
|
56
|
+
/** Avatar monogram fallback when no image is supplied. */
|
|
57
|
+
avatarMonogram?: string
|
|
58
|
+
/** Full override for the header avatar. Takes precedence over avatar props. */
|
|
59
|
+
avatar?: React.ReactNode
|
|
60
|
+
/**
|
|
61
|
+
* Footer "special badge" label (small frosted pill above the title). The
|
|
62
|
+
* badge is hidden when this is `undefined` and no `specialBadge` override
|
|
63
|
+
* is provided.
|
|
64
|
+
*/
|
|
65
|
+
specialBadgeLabel?: string
|
|
66
|
+
/** Optional leading node (icon/image) rendered inside the special badge. */
|
|
67
|
+
specialBadgeIcon?: React.ReactNode
|
|
68
|
+
/** Full override for the special badge. Takes precedence over the props above. */
|
|
69
|
+
specialBadge?: React.ReactNode
|
|
70
|
+
/** CTA button label. Defaults to `"CTA"`. */
|
|
71
|
+
ctaLabel?: string
|
|
72
|
+
/** CTA button press handler. */
|
|
73
|
+
onCtaPress?: () => void
|
|
74
|
+
/** Full override for the CTA. Takes precedence over `ctaLabel`/`onCtaPress`. */
|
|
75
|
+
cta?: React.ReactNode
|
|
76
|
+
/** Press handler for the whole card. When set, the card becomes pressable. */
|
|
77
|
+
onPress?: () => void
|
|
78
|
+
/** Card height in px. Defaults to `223` (Figma spec). */
|
|
79
|
+
height?: number
|
|
80
|
+
/** Modes object for design-token resolution. Cascaded to all children. */
|
|
81
|
+
modes?: Modes
|
|
82
|
+
/** Style overrides for the card container. */
|
|
83
|
+
style?: StyleProp<ViewStyle>
|
|
84
|
+
/** Accessibility label for the card. */
|
|
85
|
+
accessibilityLabel?: string
|
|
86
|
+
/**
|
|
87
|
+
* When `true`, disables text truncation: the trailing ellipsis (`…`) is never
|
|
88
|
+
* shown and any `numberOfLines` clamp is ignored, so the text wraps to as many
|
|
89
|
+
* lines as it needs even when horizontal space is tight.
|
|
90
|
+
*/
|
|
91
|
+
disableTruncation?: boolean
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* ProductMerchandisingCard — a full-bleed image card (Figma node 5277:317)
|
|
96
|
+
* with a top header (avatar + badge) and a bottom **glass footer** that
|
|
97
|
+
* combines a real native background blur with a transparent→black gradient
|
|
98
|
+
* scrim for legible text over any image.
|
|
99
|
+
*
|
|
100
|
+
* Glass footer implementation (works on iOS, Android and Web):
|
|
101
|
+
* - **iOS / Android:** `<GlassFill>` wraps `@react-native-community/blur`'s
|
|
102
|
+
* `BlurView` — iOS gets a real `UIVisualEffectView` (live OS blur),
|
|
103
|
+
* Android gets the community `RealtimeBlurView` with a tinted scrim
|
|
104
|
+
* fallback. The native-only module is isolated in `GlassFill.tsx`.
|
|
105
|
+
* - **Web:** the platform-extension `GlassFill.web.tsx` renders a translucent
|
|
106
|
+
* `View` with `backdrop-filter: blur()` — Metro picks the right file so the
|
|
107
|
+
* web bundle never imports the native blur module.
|
|
108
|
+
* - The gradient scrim is drawn with `react-native-svg` (one renderer for all
|
|
109
|
+
* platforms) so the fade is identical everywhere.
|
|
110
|
+
*
|
|
111
|
+
* The blur strength is driven by the Figma `blur/minimal` token, mapped to the
|
|
112
|
+
* `GlassFill` 0–100 intensity scale the same way `MediaCard` does.
|
|
113
|
+
*/
|
|
114
|
+
function ProductMerchandisingCard({
|
|
115
|
+
imageSource,
|
|
116
|
+
title = 'Title',
|
|
117
|
+
subtitle = 'Subtitle',
|
|
118
|
+
badgeLabel,
|
|
119
|
+
badge,
|
|
120
|
+
showAvatar = true,
|
|
121
|
+
avatarSource,
|
|
122
|
+
avatarMonogram,
|
|
123
|
+
avatar,
|
|
124
|
+
specialBadgeLabel,
|
|
125
|
+
specialBadgeIcon,
|
|
126
|
+
specialBadge,
|
|
127
|
+
ctaLabel = 'CTA',
|
|
128
|
+
onCtaPress,
|
|
129
|
+
cta,
|
|
130
|
+
onPress,
|
|
131
|
+
height = 223,
|
|
132
|
+
modes = EMPTY_MODES,
|
|
133
|
+
style,
|
|
134
|
+
accessibilityLabel,
|
|
135
|
+
disableTruncation,
|
|
136
|
+
}: ProductMerchandisingCardProps) {
|
|
137
|
+
// Unique gradient ids so multiple cards on one (web) document never collide.
|
|
138
|
+
const rawId = useId()
|
|
139
|
+
const safeId = rawId.replace(/[^a-zA-Z0-9_-]/g, '')
|
|
140
|
+
const scrimId = `pmc-footer-scrim-${safeId}`
|
|
141
|
+
const blurMaskId = `pmc-footer-blurmask-${safeId}`
|
|
142
|
+
|
|
143
|
+
const normalizedImageSource = useMemo(() => normalizeImageSource(imageSource), [imageSource])
|
|
144
|
+
|
|
145
|
+
const tokens = useMemo(() => resolveTokens(modes), [modes])
|
|
146
|
+
|
|
147
|
+
const containerStyle: ViewStyle = {
|
|
148
|
+
height,
|
|
149
|
+
borderRadius: tokens.radius,
|
|
150
|
+
overflow: 'hidden',
|
|
151
|
+
position: 'relative',
|
|
152
|
+
flexDirection: 'column',
|
|
153
|
+
justifyContent: 'space-between',
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Header avatar defaults to the design's 36px (Avatar Size = M). A
|
|
157
|
+
// consumer-supplied `modes['Avatar Size']` still wins via spread order.
|
|
158
|
+
const avatarModes = useMemo<Modes>(() => ({ 'Avatar Size': 'M', ...modes }), [modes])
|
|
159
|
+
|
|
160
|
+
// The Figma node renders the header badge with the brand "Secondary"
|
|
161
|
+
// appearance at "Medium" emphasis (lilac fill + dark label). These are set
|
|
162
|
+
// as defaults so the card matches the design out of the box; a
|
|
163
|
+
// consumer-supplied `modes` value still wins via spread order.
|
|
164
|
+
const badgeModes = useMemo<Modes>(
|
|
165
|
+
() => ({ AppearanceBrand: 'Secondary', Emphasis: 'Medium', ...modes }),
|
|
166
|
+
[modes]
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
const headerBadge =
|
|
170
|
+
badge ?? (badgeLabel != null ? <Badge label={badgeLabel} modes={badgeModes} /> : null)
|
|
171
|
+
|
|
172
|
+
const headerAvatar = showAvatar
|
|
173
|
+
? avatar ?? (
|
|
174
|
+
<Avatar
|
|
175
|
+
style={avatarSource ? 'Image' : avatarMonogram ? 'Monogram' : 'Image'}
|
|
176
|
+
imageSource={avatarSource}
|
|
177
|
+
monogram={avatarMonogram}
|
|
178
|
+
modes={avatarModes}
|
|
179
|
+
/>
|
|
180
|
+
)
|
|
181
|
+
: null
|
|
182
|
+
|
|
183
|
+
// The footer "special badge" is now the shared <Badge type="glass">. The
|
|
184
|
+
// hardcoded literals that defined its look (warm frosted overlay, gentle
|
|
185
|
+
// 5px blur, 6/2 padding, 4px radius, weight-500 white label) are preserved
|
|
186
|
+
// 1:1 via the glass overrides + `style`/`labelStyle`, and the optional
|
|
187
|
+
// `specialBadgeIcon` maps to Badge's `leading` slot — so every existing
|
|
188
|
+
// instantiation renders byte-for-byte the same as before.
|
|
189
|
+
const footerSpecialBadge =
|
|
190
|
+
specialBadge ??
|
|
191
|
+
(specialBadgeLabel != null ? (
|
|
192
|
+
<Badge
|
|
193
|
+
type="glass"
|
|
194
|
+
label={specialBadgeLabel}
|
|
195
|
+
leading={specialBadgeIcon}
|
|
196
|
+
glassOverlayColor={SPECIAL_BADGE_BG}
|
|
197
|
+
glassIntensity={SPECIAL_BADGE_INTENSITY}
|
|
198
|
+
modes={modes}
|
|
199
|
+
style={styles.specialBadge}
|
|
200
|
+
labelStyle={tokens.specialBadgeText}
|
|
201
|
+
{...(disableTruncation != null ? { disableTruncation } : null)}
|
|
202
|
+
/>
|
|
203
|
+
) : null)
|
|
204
|
+
|
|
205
|
+
// The Figma CTA uses the brand "Secondary" appearance (purple fill, white
|
|
206
|
+
// label) at the small size. Defaults match the design; consumer `modes`
|
|
207
|
+
// win via spread order.
|
|
208
|
+
const ctaModes = useMemo<Modes>(
|
|
209
|
+
() => ({ 'Button / Size': 'S', AppearanceBrand: 'Secondary', ...modes }),
|
|
210
|
+
[modes]
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
const footerCta =
|
|
214
|
+
cta ?? <Button label={ctaLabel} modes={ctaModes} onPress={onCtaPress} />
|
|
215
|
+
|
|
216
|
+
const content = (
|
|
217
|
+
<>
|
|
218
|
+
{imageSource != null ? (
|
|
219
|
+
<Image
|
|
220
|
+
imageSource={imageSource}
|
|
221
|
+
style={StyleSheet.absoluteFillObject as any}
|
|
222
|
+
width="100%"
|
|
223
|
+
height={height}
|
|
224
|
+
resizeMode="cover"
|
|
225
|
+
accessibilityElementsHidden
|
|
226
|
+
importantForAccessibility="no"
|
|
227
|
+
/>
|
|
228
|
+
) : null}
|
|
229
|
+
|
|
230
|
+
<View style={tokens.header} pointerEvents="box-none">
|
|
231
|
+
<View style={styles.avatarContainer}>{headerAvatar}</View>
|
|
232
|
+
{headerBadge}
|
|
233
|
+
</View>
|
|
234
|
+
|
|
235
|
+
<View style={tokens.footer} pointerEvents="box-none">
|
|
236
|
+
{/* Progressive blur — clear at the top of the footer, ramping to
|
|
237
|
+
a soft blur at the bottom. Rather than the unmaintained native
|
|
238
|
+
BlurView (which renders as a flat black tint on Android +
|
|
239
|
+
Fabric and can hide the image on iOS), we reveal a *blurred
|
|
240
|
+
copy of the card's own image* through a vertical gradient
|
|
241
|
+
mask. `Image.blurRadius` is a built-in RN prop with a real
|
|
242
|
+
Gaussian blur on BOTH iOS and Android and needs no native
|
|
243
|
+
module. The blurred copy is pinned to the card's bottom at the
|
|
244
|
+
full card height so it lines up pixel-for-pixel with the
|
|
245
|
+
full-bleed background, and the mask makes the blur swell in
|
|
246
|
+
smoothly toward the bottom (true progressive blur). */}
|
|
247
|
+
{normalizedImageSource != null ? (
|
|
248
|
+
<View style={StyleSheet.absoluteFill} pointerEvents="none">
|
|
249
|
+
<MaskedView
|
|
250
|
+
style={StyleSheet.absoluteFill}
|
|
251
|
+
maskElement={
|
|
252
|
+
<Svg width="100%" height="100%">
|
|
253
|
+
<Defs>
|
|
254
|
+
<LinearGradient id={blurMaskId} x1="0" y1="0" x2="0" y2="1">
|
|
255
|
+
<Stop offset="0" stopColor="#000000" stopOpacity={0} />
|
|
256
|
+
<Stop offset="0.35" stopColor="#000000" stopOpacity={0.5} />
|
|
257
|
+
<Stop offset="0.7" stopColor="#000000" stopOpacity={0.92} />
|
|
258
|
+
<Stop offset="1" stopColor="#000000" stopOpacity={1} />
|
|
259
|
+
</LinearGradient>
|
|
260
|
+
</Defs>
|
|
261
|
+
<Rect x="0" y="0" width="100%" height="100%" fill={`url(#${blurMaskId})`} />
|
|
262
|
+
</Svg>
|
|
263
|
+
}
|
|
264
|
+
>
|
|
265
|
+
<RNImage
|
|
266
|
+
source={normalizedImageSource}
|
|
267
|
+
blurRadius={tokens.imageBlurRadius}
|
|
268
|
+
resizeMode="cover"
|
|
269
|
+
style={{ position: 'absolute', left: 0, right: 0, bottom: 0, width: '100%', height }}
|
|
270
|
+
/>
|
|
271
|
+
</MaskedView>
|
|
272
|
+
</View>
|
|
273
|
+
) : (
|
|
274
|
+
<GlassFill tint="dark" intensity={tokens.blurIntensity} progressive />
|
|
275
|
+
)}
|
|
276
|
+
{/* Transparent -> black gradient scrim for text legibility,
|
|
277
|
+
identical on all platforms. Kept lighter than a solid fill so
|
|
278
|
+
the blurred image stays visible behind the footer text. */}
|
|
279
|
+
<View style={[StyleSheet.absoluteFill, styles.scrim]} pointerEvents="none">
|
|
280
|
+
<Svg width="100%" height="100%">
|
|
281
|
+
<Defs>
|
|
282
|
+
<LinearGradient id={scrimId} x1="0" y1="0" x2="0" y2="1">
|
|
283
|
+
<Stop offset="0.018" stopColor="#000000" stopOpacity={0} />
|
|
284
|
+
<Stop offset="0.974" stopColor="#000000" stopOpacity={1} />
|
|
285
|
+
</LinearGradient>
|
|
286
|
+
</Defs>
|
|
287
|
+
<Rect x="0" y="0" width="100%" height="100%" fill={`url(#${scrimId})`} />
|
|
288
|
+
</Svg>
|
|
289
|
+
</View>
|
|
290
|
+
|
|
291
|
+
{footerSpecialBadge}
|
|
292
|
+
|
|
293
|
+
<View style={styles.footerRow}>
|
|
294
|
+
<View style={tokens.textWrap}>
|
|
295
|
+
<Text style={tokens.title} {...resolveTruncation(disableTruncation, 1)}>
|
|
296
|
+
{title}
|
|
297
|
+
</Text>
|
|
298
|
+
{subtitle != null ? (
|
|
299
|
+
<Text style={tokens.subtitle} {...resolveTruncation(disableTruncation, 1)}>
|
|
300
|
+
{subtitle}
|
|
301
|
+
</Text>
|
|
302
|
+
) : null}
|
|
303
|
+
</View>
|
|
304
|
+
{footerCta}
|
|
305
|
+
</View>
|
|
306
|
+
</View>
|
|
307
|
+
</>
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
if (onPress) {
|
|
311
|
+
return (
|
|
312
|
+
<Pressable
|
|
313
|
+
style={({ pressed }) => [containerStyle, pressed ? styles.pressed : null, style]}
|
|
314
|
+
accessibilityRole="button"
|
|
315
|
+
accessibilityLabel={accessibilityLabel ?? title}
|
|
316
|
+
onPress={onPress}
|
|
317
|
+
>
|
|
318
|
+
{content}
|
|
319
|
+
</Pressable>
|
|
320
|
+
)
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return (
|
|
324
|
+
<View
|
|
325
|
+
style={[containerStyle, style]}
|
|
326
|
+
accessibilityLabel={accessibilityLabel}
|
|
327
|
+
>
|
|
328
|
+
{content}
|
|
329
|
+
</View>
|
|
330
|
+
)
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// ---------------------------------------------------------------------------
|
|
334
|
+
// Tokens / static styles
|
|
335
|
+
// ---------------------------------------------------------------------------
|
|
336
|
+
|
|
337
|
+
// Figma footer backdrop-blur token (`blur/minimal`). The footer uses a
|
|
338
|
+
// *progressive* blur, so this maps the token to the intensity at the BOTTOM of
|
|
339
|
+
// the ramp (the strongest point); the upper part fades to fully clear. Kept
|
|
340
|
+
// intentionally small to match the design's subtle "minimal" blur.
|
|
341
|
+
const BLUR_INTENSITY_FACTOR = 1.0
|
|
342
|
+
|
|
343
|
+
// Special badge ("frosted" pill) — values are literals in the Figma node
|
|
344
|
+
// (no dedicated tokens): bg rgba(255,245,229,0.15), 5px backdrop blur.
|
|
345
|
+
const SPECIAL_BADGE_BG = 'rgba(255,245,229,0.15)'
|
|
346
|
+
const SPECIAL_BADGE_INTENSITY = 17 // ~5px CSS blur / ~2 native blurAmount
|
|
347
|
+
|
|
348
|
+
interface ResolvedTokens {
|
|
349
|
+
radius: number
|
|
350
|
+
blurIntensity: number
|
|
351
|
+
imageBlurRadius: number
|
|
352
|
+
header: ViewStyle
|
|
353
|
+
footer: ViewStyle
|
|
354
|
+
textWrap: ViewStyle
|
|
355
|
+
title: TextStyle
|
|
356
|
+
subtitle: TextStyle
|
|
357
|
+
specialBadgeText: TextStyle
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function asNum(raw: unknown, fallback: number): number {
|
|
361
|
+
const n = typeof raw === 'number' ? raw : parseFloat(raw as string)
|
|
362
|
+
return Number.isFinite(n) ? n : fallback
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function asStr(raw: unknown, fallback: string): string {
|
|
366
|
+
return raw != null ? String(raw) : fallback
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
function resolveTokens(modes: Modes): ResolvedTokens {
|
|
370
|
+
// NOTE: token names are passed as string literals DIRECTLY to
|
|
371
|
+
// getVariableByName so the `extract-component-tokens` script can statically
|
|
372
|
+
// collect them for the generated docs. Do not refactor these into a helper
|
|
373
|
+
// that receives the name as a variable.
|
|
374
|
+
const radius = asNum(getVariableByName('productMerchandisingcard/radius', modes), 12)
|
|
375
|
+
const headerPadH = asNum(getVariableByName('productMerchandisingcard/header/padding/horizontal', modes), 12)
|
|
376
|
+
const headerPadV = asNum(getVariableByName('productMerchandisingcard/header/padding/vertical', modes), 12)
|
|
377
|
+
const footerGap = asNum(getVariableByName('productMerchandisingcard/footer/gap', modes), 8)
|
|
378
|
+
const footerPadH = asNum(getVariableByName('productMerchandisingcard/footer/padding/horizontal', modes), 16)
|
|
379
|
+
const footerPadV = asNum(getVariableByName('productMerchandisingcard/footer/padding/vertical', modes), 16)
|
|
380
|
+
const textGap = asNum(getVariableByName('productMerchandisingcard/footer/text/gap', modes), 2)
|
|
381
|
+
const blurRadius = asNum(getVariableByName('blur/minimal', modes), 29)
|
|
382
|
+
|
|
383
|
+
const titleColor = asStr(getVariableByName('productMerchandisingcard/footer/title/font/color', modes), '#ffffff')
|
|
384
|
+
const titleSize = asNum(getVariableByName('productMerchandisingcard/footer/title/fontsize', modes), 14)
|
|
385
|
+
const titleFamily = asStr(getVariableByName('productMerchandisingcard/footer/title/fontfamily', modes), 'JioType Var')
|
|
386
|
+
const titleWeight = asStr(getVariableByName('productMerchandisingcard/footer/title/fontweight', modes), '700')
|
|
387
|
+
|
|
388
|
+
const subtitleColor = asStr(getVariableByName('productMerchandisingcard/footer/subtitle/font/color', modes), '#c5bcb5')
|
|
389
|
+
const subtitleSize = asNum(getVariableByName('productMerchandisingcard/footer/subtitle/fontsize', modes), 12)
|
|
390
|
+
const subtitleFamily = asStr(getVariableByName('productMerchandisingcard/footer/subtitle/fontfamily', modes), 'JioType Var')
|
|
391
|
+
const subtitleWeight = asStr(getVariableByName('productMerchandisingcard/footer/subtitle/fontweight', modes), '400')
|
|
392
|
+
|
|
393
|
+
const sbSize = asNum(getVariableByName('productMerchandisingcard/specialbadge/text/fontsize', modes), 12)
|
|
394
|
+
const sbFamily = asStr(getVariableByName('productMerchandisingcard/specialbadge/text/fontfamily', modes), 'JioType Var')
|
|
395
|
+
const sbWeight = asStr(getVariableByName('productMerchandisingcard/specialbadge/text/fontweight', modes), '500')
|
|
396
|
+
|
|
397
|
+
// `Image.blurRadius` is interpreted differently per platform — iOS applies a
|
|
398
|
+
// strong Gaussian while Android's IterativeBoxBlur is gentler at the same
|
|
399
|
+
// numeric radius — so we tune each platform to land on the same perceived
|
|
400
|
+
// "minimal" frosted strength as the Figma design.
|
|
401
|
+
const imageBlurRadius = Platform.select({
|
|
402
|
+
ios: Math.max(6, Math.round(blurRadius * 0.5)),
|
|
403
|
+
android: Math.max(12, Math.round(blurRadius * 0.95)),
|
|
404
|
+
default: Math.max(8, Math.round(blurRadius * 0.6)),
|
|
405
|
+
}) as number
|
|
406
|
+
|
|
407
|
+
return {
|
|
408
|
+
radius,
|
|
409
|
+
blurIntensity: Math.max(0, Math.min(100, Math.round(blurRadius * BLUR_INTENSITY_FACTOR))),
|
|
410
|
+
imageBlurRadius,
|
|
411
|
+
header: {
|
|
412
|
+
flexDirection: 'row',
|
|
413
|
+
alignItems: 'flex-start',
|
|
414
|
+
justifyContent: 'space-between',
|
|
415
|
+
paddingHorizontal: headerPadH,
|
|
416
|
+
paddingVertical: headerPadV,
|
|
417
|
+
zIndex: 1,
|
|
418
|
+
},
|
|
419
|
+
footer: {
|
|
420
|
+
paddingHorizontal: footerPadH,
|
|
421
|
+
paddingVertical: footerPadV,
|
|
422
|
+
gap: footerGap,
|
|
423
|
+
overflow: 'hidden',
|
|
424
|
+
zIndex: 2,
|
|
425
|
+
},
|
|
426
|
+
textWrap: {
|
|
427
|
+
flex: 1,
|
|
428
|
+
gap: textGap,
|
|
429
|
+
justifyContent: 'center',
|
|
430
|
+
},
|
|
431
|
+
title: {
|
|
432
|
+
color: titleColor,
|
|
433
|
+
fontSize: titleSize,
|
|
434
|
+
fontFamily: titleFamily,
|
|
435
|
+
fontWeight: titleWeight as TextStyle['fontWeight'],
|
|
436
|
+
lineHeight: Math.round(titleSize * 1.2),
|
|
437
|
+
includeFontPadding: false as any,
|
|
438
|
+
},
|
|
439
|
+
subtitle: {
|
|
440
|
+
color: subtitleColor,
|
|
441
|
+
fontSize: subtitleSize,
|
|
442
|
+
fontFamily: subtitleFamily,
|
|
443
|
+
fontWeight: subtitleWeight as TextStyle['fontWeight'],
|
|
444
|
+
lineHeight: Math.round(subtitleSize * 1.2),
|
|
445
|
+
includeFontPadding: false as any,
|
|
446
|
+
},
|
|
447
|
+
specialBadgeText: {
|
|
448
|
+
color: '#ffffff',
|
|
449
|
+
fontSize: sbSize,
|
|
450
|
+
fontFamily: sbFamily,
|
|
451
|
+
fontWeight: sbWeight as TextStyle['fontWeight'],
|
|
452
|
+
lineHeight: Math.round(sbSize * 1.2),
|
|
453
|
+
includeFontPadding: false as any,
|
|
454
|
+
},
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
const styles = StyleSheet.create({
|
|
459
|
+
avatarContainer: {
|
|
460
|
+
flexShrink: 1,
|
|
461
|
+
justifyContent: 'center',
|
|
462
|
+
},
|
|
463
|
+
footerRow: {
|
|
464
|
+
flexDirection: 'row',
|
|
465
|
+
alignItems: 'center',
|
|
466
|
+
justifyContent: 'space-between',
|
|
467
|
+
gap: 8,
|
|
468
|
+
},
|
|
469
|
+
scrim: {
|
|
470
|
+
// The footer node sits at 90% opacity in Figma; applying it to the
|
|
471
|
+
// scrim (not the content) keeps the title/CTA crisp while preserving
|
|
472
|
+
// the intended fade strength.
|
|
473
|
+
opacity: 0.9,
|
|
474
|
+
},
|
|
475
|
+
// Overrides layered on top of <Badge type="glass">. Badge already supplies
|
|
476
|
+
// the row layout + 4px gap (when a leading icon is present), `overflow:
|
|
477
|
+
// 'hidden'`, `alignSelf: 'flex-start'` and `alignItems: 'center'`; these
|
|
478
|
+
// pin the special badge's literal Figma box (6/2 padding, 4px radius)
|
|
479
|
+
// regardless of the badge size tokens resolved by the active modes.
|
|
480
|
+
specialBadge: {
|
|
481
|
+
paddingHorizontal: 6,
|
|
482
|
+
paddingVertical: 2,
|
|
483
|
+
borderRadius: 4,
|
|
484
|
+
},
|
|
485
|
+
pressed: {
|
|
486
|
+
opacity: 0.92,
|
|
487
|
+
},
|
|
488
|
+
})
|
|
489
|
+
|
|
490
|
+
export default ProductMerchandisingCard
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
type ImageSourcePropType,
|
|
9
9
|
} from 'react-native'
|
|
10
10
|
import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
|
|
11
|
-
import { EMPTY_MODES, cloneChildrenWithModes } from '../../utils/react-utils'
|
|
11
|
+
import { EMPTY_MODES, cloneChildrenWithModes, resolveTruncation } from '../../utils/react-utils'
|
|
12
12
|
import Image from '../Image/Image'
|
|
13
13
|
import ProductLabel from '../ProductLabel/ProductLabel'
|
|
14
14
|
import type { Modes } from '../../design-tokens'
|
|
@@ -47,6 +47,12 @@ export type ProductOverviewProps = {
|
|
|
47
47
|
* for badges, callouts, etc. Receives the same `modes` as the parent.
|
|
48
48
|
*/
|
|
49
49
|
children?: React.ReactNode
|
|
50
|
+
/**
|
|
51
|
+
* When `true`, disables text truncation: the trailing ellipsis (`…`) is never
|
|
52
|
+
* shown and any `numberOfLines` clamp is ignored, so the text wraps to as many
|
|
53
|
+
* lines as it needs even when horizontal space is tight.
|
|
54
|
+
*/
|
|
55
|
+
disableTruncation?: boolean
|
|
50
56
|
}
|
|
51
57
|
|
|
52
58
|
const DEFAULT_STATS: ProductOverviewStat[] = [
|
|
@@ -65,6 +71,7 @@ const ProductOverview = ({
|
|
|
65
71
|
modes = EMPTY_MODES,
|
|
66
72
|
style,
|
|
67
73
|
children,
|
|
74
|
+
disableTruncation,
|
|
68
75
|
}: ProductOverviewProps) => {
|
|
69
76
|
const padding = (getVariableByName('productOverview/padding', modes) as number | null) ?? 24
|
|
70
77
|
const gap = (getVariableByName('productOverview/gap', modes) as number | null) ?? 12
|
|
@@ -220,10 +227,10 @@ const ProductOverview = ({
|
|
|
220
227
|
overflow: 'hidden',
|
|
221
228
|
}}
|
|
222
229
|
>
|
|
223
|
-
<Text style={statValueStyle}
|
|
230
|
+
<Text style={statValueStyle} {...resolveTruncation(disableTruncation, 1)}>
|
|
224
231
|
{stat.value}
|
|
225
232
|
</Text>
|
|
226
|
-
<Text style={statLabelStyle}
|
|
233
|
+
<Text style={statLabelStyle} {...resolveTruncation(disableTruncation, 1)}>
|
|
227
234
|
{stat.label}
|
|
228
235
|
</Text>
|
|
229
236
|
</View>
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
} from 'react-native'
|
|
9
9
|
import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
|
|
10
10
|
import Icon from '../../icons/Icon'
|
|
11
|
-
import { EMPTY_MODES, cloneChildrenWithModes } from '../../utils/react-utils'
|
|
11
|
+
import { EMPTY_MODES, cloneChildrenWithModes, resolveTruncation } from '../../utils/react-utils'
|
|
12
12
|
import type { Modes } from '../../design-tokens'
|
|
13
13
|
|
|
14
14
|
export type ProgressBadgeProps = {
|
|
@@ -26,6 +26,12 @@ export type ProgressBadgeProps = {
|
|
|
26
26
|
textStyle?: StyleProp<TextStyle>
|
|
27
27
|
/** Accessibility label for screen readers */
|
|
28
28
|
accessibilityLabel?: string
|
|
29
|
+
/**
|
|
30
|
+
* When `true`, disables text truncation: the trailing ellipsis (`…`) is never
|
|
31
|
+
* shown and any `numberOfLines` clamp is ignored, so the text wraps to as many
|
|
32
|
+
* lines as it needs even when horizontal space is tight.
|
|
33
|
+
*/
|
|
34
|
+
disableTruncation?: boolean
|
|
29
35
|
} & React.ComponentProps<typeof View>
|
|
30
36
|
|
|
31
37
|
/**
|
|
@@ -51,6 +57,7 @@ function ProgressBadge({
|
|
|
51
57
|
style,
|
|
52
58
|
textStyle: textStyleOverride,
|
|
53
59
|
accessibilityLabel,
|
|
60
|
+
disableTruncation,
|
|
54
61
|
...rest
|
|
55
62
|
}: ProgressBadgeProps) {
|
|
56
63
|
// Resolve layout tokens
|
|
@@ -158,8 +165,7 @@ function ProgressBadge({
|
|
|
158
165
|
) : null}
|
|
159
166
|
<Text
|
|
160
167
|
style={[textStyle, textStyleOverride]}
|
|
161
|
-
|
|
162
|
-
ellipsizeMode="tail"
|
|
168
|
+
{...resolveTruncation(disableTruncation, 1, 'tail')}
|
|
163
169
|
accessibilityElementsHidden={true}
|
|
164
170
|
importantForAccessibility="no"
|
|
165
171
|
>
|