jfs-components 0.0.1 → 0.0.3
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/lib/commonjs/components/ActionFooter/ActionFooter.js +124 -0
- package/lib/commonjs/components/ActionFooter/ActionFooter.js.map +1 -0
- package/lib/commonjs/components/ActionFooter/ActionFooter.mdx +101 -0
- package/lib/commonjs/components/Button/Button.js +1 -1
- package/lib/commonjs/components/Button/Button.js.map +1 -1
- package/lib/commonjs/components/Button/Button.mdx +2 -2
- package/lib/commonjs/components/CardFeedback/CardFeedback.js +47 -11
- package/lib/commonjs/components/CardFeedback/CardFeedback.js.map +1 -1
- package/lib/commonjs/components/CardFeedback/CardFeedback.mdx +1 -0
- package/lib/commonjs/components/Divider/Divider.js +63 -0
- package/lib/commonjs/components/Divider/Divider.js.map +1 -0
- package/lib/commonjs/components/Divider/Divider.mdx +91 -0
- package/lib/commonjs/components/ListItem/ListItem.js +24 -13
- package/lib/commonjs/components/ListItem/ListItem.js.map +1 -1
- package/lib/commonjs/components/ListItem/ListItem.mdx +46 -5
- package/lib/commonjs/components/MerchantProfile/MerchantProfile.js +133 -0
- package/lib/commonjs/components/MerchantProfile/MerchantProfile.js.map +1 -0
- package/lib/commonjs/components/MerchantProfile/MerchantProfile.mdx +139 -0
- package/lib/commonjs/components/MoneyValue/MoneyValue.js +36 -4
- package/lib/commonjs/components/MoneyValue/MoneyValue.js.map +1 -1
- package/lib/commonjs/components/MoneyValue/MoneyValue.mdx +4 -0
- package/lib/commonjs/components/NavArrow/NavArrow.js +90 -0
- package/lib/commonjs/components/NavArrow/NavArrow.js.map +1 -0
- package/lib/commonjs/components/NavArrow/NavArrow.mdx +123 -0
- package/lib/commonjs/components/Section/Section.mdx +4 -4
- package/lib/commonjs/components/Stepper/Step.mdx +1 -1
- package/lib/commonjs/components/index.js +28 -0
- package/lib/commonjs/components/index.js.map +1 -1
- package/lib/commonjs/design-tokens/JFS Variables-variables-full.json +18633 -1
- package/lib/commonjs/design-tokens/figma-variables-resolver.js.map +1 -2
- package/lib/commonjs/icons/registry.js +1 -1
- package/lib/module/components/ActionFooter/ActionFooter.js +119 -0
- package/lib/module/components/ActionFooter/ActionFooter.js.map +1 -0
- package/lib/module/components/ActionFooter/ActionFooter.mdx +101 -0
- package/lib/module/components/Button/Button.js +1 -1
- package/lib/module/components/Button/Button.js.map +1 -1
- package/lib/module/components/Button/Button.mdx +2 -2
- package/lib/module/components/CardFeedback/CardFeedback.js +46 -11
- package/lib/module/components/CardFeedback/CardFeedback.js.map +1 -1
- package/lib/module/components/CardFeedback/CardFeedback.mdx +1 -0
- package/lib/module/components/Divider/Divider.js +58 -0
- package/lib/module/components/Divider/Divider.js.map +1 -0
- package/lib/module/components/Divider/Divider.mdx +91 -0
- package/lib/module/components/ListItem/ListItem.js +24 -13
- package/lib/module/components/ListItem/ListItem.js.map +1 -1
- package/lib/module/components/ListItem/ListItem.mdx +46 -5
- package/lib/module/components/MerchantProfile/MerchantProfile.js +128 -0
- package/lib/module/components/MerchantProfile/MerchantProfile.js.map +1 -0
- package/lib/module/components/MerchantProfile/MerchantProfile.mdx +139 -0
- package/lib/module/components/MoneyValue/MoneyValue.js +36 -4
- package/lib/module/components/MoneyValue/MoneyValue.js.map +1 -1
- package/lib/module/components/MoneyValue/MoneyValue.mdx +4 -0
- package/lib/module/components/NavArrow/NavArrow.js +84 -0
- package/lib/module/components/NavArrow/NavArrow.js.map +1 -0
- package/lib/module/components/NavArrow/NavArrow.mdx +123 -0
- package/lib/module/components/Section/Section.mdx +4 -4
- package/lib/module/components/Stepper/Step.mdx +1 -1
- package/lib/module/components/index.js +4 -0
- package/lib/module/components/index.js.map +1 -1
- package/lib/module/design-tokens/JFS Variables-variables-full.json +18633 -1
- package/lib/module/icons/registry.js +1 -1
- package/lib/typescript/components/ActionFooter/ActionFooter.d.ts +58 -0
- package/lib/typescript/components/ActionFooter/ActionFooter.d.ts.map +1 -0
- package/lib/typescript/components/CardFeedback/CardFeedback.d.ts +4 -3
- package/lib/typescript/components/CardFeedback/CardFeedback.d.ts.map +1 -1
- package/lib/typescript/components/Divider/Divider.d.ts +50 -0
- package/lib/typescript/components/Divider/Divider.d.ts.map +1 -0
- package/lib/typescript/components/ListItem/ListItem.d.ts +4 -3
- package/lib/typescript/components/ListItem/ListItem.d.ts.map +1 -1
- package/lib/typescript/components/MerchantProfile/MerchantProfile.d.ts +68 -0
- package/lib/typescript/components/MerchantProfile/MerchantProfile.d.ts.map +1 -0
- package/lib/typescript/components/MoneyValue/MoneyValue.d.ts +9 -2
- package/lib/typescript/components/MoneyValue/MoneyValue.d.ts.map +1 -1
- package/lib/typescript/components/NavArrow/NavArrow.d.ts +35 -0
- package/lib/typescript/components/NavArrow/NavArrow.d.ts.map +1 -0
- package/lib/typescript/components/index.d.ts +4 -0
- package/lib/typescript/components/index.d.ts.map +1 -1
- package/lib/typescript/icons/registry.d.ts +1 -1
- package/package.json +2 -2
- package/src/components/.token-metadata.json +99 -11
- package/src/components/ActionFooter/ActionFooter.mdx +101 -0
- package/src/components/ActionFooter/ActionFooter.tsx +142 -0
- package/src/components/Button/Button.mdx +2 -2
- package/src/components/Button/Button.tsx +1 -1
- package/src/components/CardFeedback/CardFeedback.mdx +1 -0
- package/src/components/CardFeedback/CardFeedback.tsx +37 -12
- package/src/components/Divider/Divider.mdx +91 -0
- package/src/components/Divider/Divider.tsx +91 -0
- package/src/components/ListItem/ListItem.mdx +46 -5
- package/src/components/ListItem/ListItem.tsx +22 -11
- package/src/components/MerchantProfile/MerchantProfile.mdx +139 -0
- package/src/components/MerchantProfile/MerchantProfile.tsx +174 -0
- package/src/components/MoneyValue/MoneyValue.mdx +4 -0
- package/src/components/MoneyValue/MoneyValue.tsx +39 -3
- package/src/components/NavArrow/NavArrow.mdx +123 -0
- package/src/components/NavArrow/NavArrow.tsx +108 -0
- package/src/components/Section/Section.mdx +4 -4
- package/src/components/Stepper/Step.mdx +1 -1
- package/src/components/index.ts +4 -0
- package/src/design-tokens/JFS Variables-variables-full.json +18633 -1
- package/src/icons/registry.ts +1 -1
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { Meta, Story, Canvas, PureArgsTable as ArgsTable } from '@storybook/addon-docs/blocks';
|
|
2
|
+
import * as ActionFooterStories from './ActionFooter.stories';
|
|
3
|
+
import ActionFooter from './ActionFooter';
|
|
4
|
+
|
|
5
|
+
<Meta of={ActionFooterStories} />
|
|
6
|
+
|
|
7
|
+
# ActionFooter
|
|
8
|
+
|
|
9
|
+
A fixed footer container for action buttons. Typically used at the bottom of screens to hold primary actions like `IconButton` and `Button` components.
|
|
10
|
+
|
|
11
|
+
The ActionFooter provides consistent spacing, padding, and shadow styling for action areas at the bottom of your UI.
|
|
12
|
+
|
|
13
|
+
## Props
|
|
14
|
+
|
|
15
|
+
<ArgsTable of={ActionFooter} />
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
## Available Collections and Modes
|
|
19
|
+
|
|
20
|
+
This component does not use any design token collections with multiple modes.
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
The default configuration shows the typical use case with an IconButton and two Button components:
|
|
24
|
+
|
|
25
|
+
<Canvas>
|
|
26
|
+
<Story of={ActionFooterStories.Default} />
|
|
27
|
+
</Canvas>
|
|
28
|
+
|
|
29
|
+
## Examples
|
|
30
|
+
|
|
31
|
+
### Single Button
|
|
32
|
+
|
|
33
|
+
For simple confirmation flows, use a single full-width button:
|
|
34
|
+
|
|
35
|
+
<Canvas>
|
|
36
|
+
<Story of={ActionFooterStories.SingleButton} />
|
|
37
|
+
</Canvas>
|
|
38
|
+
|
|
39
|
+
### Two Buttons
|
|
40
|
+
|
|
41
|
+
Common pattern for cancel/confirm actions:
|
|
42
|
+
|
|
43
|
+
<Canvas>
|
|
44
|
+
<Story of={ActionFooterStories.TwoButtons} />
|
|
45
|
+
</Canvas>
|
|
46
|
+
|
|
47
|
+
### With Multiple Icon Buttons
|
|
48
|
+
|
|
49
|
+
For screens requiring multiple secondary actions alongside a primary button:
|
|
50
|
+
|
|
51
|
+
<Canvas>
|
|
52
|
+
<Story of={ActionFooterStories.WithMultipleIconButtons} />
|
|
53
|
+
</Canvas>
|
|
54
|
+
|
|
55
|
+
## Component Usage
|
|
56
|
+
|
|
57
|
+
```tsx
|
|
58
|
+
import { ActionFooter, IconButton, Button } from 'jsf-components';
|
|
59
|
+
|
|
60
|
+
function PaymentScreen() {
|
|
61
|
+
const modes = {};
|
|
62
|
+
const secondaryModes = { Appearance: 'secondary' };
|
|
63
|
+
const primaryModes = { Appearance: 'primary' };
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<View style={{ flex: 1 }}>
|
|
67
|
+
{/* Screen content */}
|
|
68
|
+
|
|
69
|
+
<ActionFooter modes={modes}>
|
|
70
|
+
<IconButton iconName="ic_split" modes={modes} />
|
|
71
|
+
<Button
|
|
72
|
+
label="Request"
|
|
73
|
+
modes={secondaryModes}
|
|
74
|
+
style={{ flex: 1 }}
|
|
75
|
+
/>
|
|
76
|
+
<Button
|
|
77
|
+
label="Pay"
|
|
78
|
+
modes={primaryModes}
|
|
79
|
+
style={{ flex: 1 }}
|
|
80
|
+
/>
|
|
81
|
+
</ActionFooter>
|
|
82
|
+
</View>
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Design Tokens
|
|
88
|
+
|
|
89
|
+
This component uses the following design tokens, resolved through `getVariableByName`:
|
|
90
|
+
|
|
91
|
+
- **`actionFooter/background`**
|
|
92
|
+
- **`actionFooter/gap`**
|
|
93
|
+
- **`actionFooter/padding/bottom`**
|
|
94
|
+
- **`actionFooter/padding/horizontal`**
|
|
95
|
+
- **`actionFooter/padding/top`**
|
|
96
|
+
|
|
97
|
+
All tokens support mode-based theming through the `modes` prop.
|
|
98
|
+
## Accessibility
|
|
99
|
+
|
|
100
|
+
The ActionFooter uses `accessibilityRole="toolbar"` to indicate it contains a group of action controls. Provide a meaningful `accessibilityLabel` to describe the footer's purpose to screen reader users.
|
|
101
|
+
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import {
|
|
3
|
+
View,
|
|
4
|
+
type ViewStyle,
|
|
5
|
+
type StyleProp,
|
|
6
|
+
Platform,
|
|
7
|
+
} from 'react-native'
|
|
8
|
+
import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
|
|
9
|
+
|
|
10
|
+
export type ActionFooterProps = {
|
|
11
|
+
/**
|
|
12
|
+
* Content to render inside the action footer slot.
|
|
13
|
+
* Typically includes IconButton and Button components.
|
|
14
|
+
*/
|
|
15
|
+
children?: React.ReactNode
|
|
16
|
+
/**
|
|
17
|
+
* Mode configuration passed to the token resolver.
|
|
18
|
+
* Pass the same modes to children components for consistent theming.
|
|
19
|
+
*/
|
|
20
|
+
modes?: Record<string, any>
|
|
21
|
+
/**
|
|
22
|
+
* Optional style overrides for the container
|
|
23
|
+
*/
|
|
24
|
+
style?: StyleProp<ViewStyle>
|
|
25
|
+
/**
|
|
26
|
+
* Accessibility label for the footer region
|
|
27
|
+
*/
|
|
28
|
+
accessibilityLabel?: string
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* ActionFooter component that provides a fixed footer container for action buttons.
|
|
33
|
+
*
|
|
34
|
+
* This component is designed to hold action items like IconButton and Button components
|
|
35
|
+
* at the bottom of a screen. It includes a shadow for visual separation from content above.
|
|
36
|
+
*
|
|
37
|
+
* The `modes` prop is automatically passed to all slot children. If a child has its own
|
|
38
|
+
* `modes` prop, it will be merged with the parent's modes (child modes take precedence).
|
|
39
|
+
*
|
|
40
|
+
* @component
|
|
41
|
+
* @param {Object} props - Component props
|
|
42
|
+
* @param {React.ReactNode} [props.children] - Action elements to display (e.g., IconButton, Button)
|
|
43
|
+
* @param {Object} [props.modes={}] - Mode configuration for design tokens (automatically passed to children)
|
|
44
|
+
* @param {Object} [props.style] - Optional style overrides
|
|
45
|
+
* @param {string} [props.accessibilityLabel] - Accessibility label for the footer region
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```tsx
|
|
49
|
+
* // Basic usage - modes are automatically passed to all children
|
|
50
|
+
* <ActionFooter modes={modes}>
|
|
51
|
+
* <IconButton iconName="ic_split" />
|
|
52
|
+
* <Button label="Request" style={{ flex: 1 }} />
|
|
53
|
+
* <Button label="Pay" style={{ flex: 1 }} />
|
|
54
|
+
* </ActionFooter>
|
|
55
|
+
*
|
|
56
|
+
* // Children can override with their own modes (merged with parent)
|
|
57
|
+
* <ActionFooter modes={modes}>
|
|
58
|
+
* <IconButton iconName="ic_split" />
|
|
59
|
+
* <Button label="Request" modes={{ Appearance: 'secondary' }} style={{ flex: 1 }} />
|
|
60
|
+
* <Button label="Pay" modes={{ Appearance: 'primary' }} style={{ flex: 1 }} />
|
|
61
|
+
* </ActionFooter>
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
function ActionFooter({
|
|
65
|
+
children,
|
|
66
|
+
modes = {},
|
|
67
|
+
style,
|
|
68
|
+
accessibilityLabel = 'Action footer',
|
|
69
|
+
}: ActionFooterProps) {
|
|
70
|
+
// Resolve design tokens
|
|
71
|
+
const backgroundColor = getVariableByName('actionFooter/background', modes) ?? '#ffffff'
|
|
72
|
+
const gap = getVariableByName('actionFooter/gap', modes) ?? 8
|
|
73
|
+
const paddingHorizontal = getVariableByName('actionFooter/padding/horizontal', modes) ?? 16
|
|
74
|
+
const paddingTop = getVariableByName('actionFooter/padding/top', modes) ?? 10
|
|
75
|
+
const paddingBottom = getVariableByName('actionFooter/padding/bottom', modes) ?? 41
|
|
76
|
+
|
|
77
|
+
// Shadow styles - cross-platform
|
|
78
|
+
const shadowStyle: ViewStyle = Platform.select({
|
|
79
|
+
ios: {
|
|
80
|
+
shadowColor: 'rgba(12, 13, 16, 1)',
|
|
81
|
+
shadowOffset: { width: 0, height: -12 },
|
|
82
|
+
shadowOpacity: 0.16,
|
|
83
|
+
shadowRadius: 24,
|
|
84
|
+
},
|
|
85
|
+
android: {
|
|
86
|
+
elevation: 16,
|
|
87
|
+
},
|
|
88
|
+
default: {
|
|
89
|
+
// Web shadow using boxShadow (RNW supports this)
|
|
90
|
+
},
|
|
91
|
+
}) as ViewStyle
|
|
92
|
+
|
|
93
|
+
const containerStyle: ViewStyle = {
|
|
94
|
+
backgroundColor,
|
|
95
|
+
paddingLeft: paddingHorizontal,
|
|
96
|
+
paddingRight: paddingHorizontal,
|
|
97
|
+
paddingTop,
|
|
98
|
+
paddingBottom,
|
|
99
|
+
...shadowStyle,
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Slot container style for horizontal layout of action items
|
|
103
|
+
const slotStyle: ViewStyle = {
|
|
104
|
+
flexDirection: 'row',
|
|
105
|
+
alignItems: 'flex-start',
|
|
106
|
+
gap,
|
|
107
|
+
flex: 1,
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Web-specific box-shadow
|
|
111
|
+
const webShadow = Platform.OS === 'web'
|
|
112
|
+
? { boxShadow: '0px -12px 24px 0px rgba(12, 13, 16, 0.12), 0px -16px 48px 0px rgba(12, 13, 16, 0.16)' } as any
|
|
113
|
+
: {}
|
|
114
|
+
|
|
115
|
+
// Pass modes prop to all slot children
|
|
116
|
+
const childrenWithModes = React.Children.map(children, (child) => {
|
|
117
|
+
if (React.isValidElement(child)) {
|
|
118
|
+
// Merge modes: child's modes take precedence over parent's modes
|
|
119
|
+
const childModes = (child.props as any).modes
|
|
120
|
+
const mergedModes = childModes ? { ...modes, ...childModes } : modes
|
|
121
|
+
return React.cloneElement(child as React.ReactElement<any>, {
|
|
122
|
+
modes: mergedModes,
|
|
123
|
+
})
|
|
124
|
+
}
|
|
125
|
+
return child
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
return (
|
|
129
|
+
<View
|
|
130
|
+
style={[containerStyle, webShadow, style]}
|
|
131
|
+
accessibilityRole="toolbar"
|
|
132
|
+
accessibilityLabel={accessibilityLabel}
|
|
133
|
+
>
|
|
134
|
+
<View style={slotStyle}>
|
|
135
|
+
{childrenWithModes}
|
|
136
|
+
</View>
|
|
137
|
+
</View>
|
|
138
|
+
)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export default ActionFooter
|
|
142
|
+
|
|
@@ -34,7 +34,7 @@ This component uses the following design token collections. Each collection supp
|
|
|
34
34
|
- **Default:** positive
|
|
35
35
|
|
|
36
36
|
### Context
|
|
37
|
-
- **Modes:** Default | Nudge&Alert
|
|
37
|
+
- **Modes:** Default | Nudge&Alert | CTACard | ListItem
|
|
38
38
|
- **Default:** Default
|
|
39
39
|
|
|
40
40
|
### Button / Size
|
|
@@ -90,7 +90,7 @@ This component uses the following design tokens, resolved through `getVariableBy
|
|
|
90
90
|
- **`button/fontFamily`**
|
|
91
91
|
- **`button/fontSize`**
|
|
92
92
|
- **`button/fontWeight`**
|
|
93
|
-
- **`button/
|
|
93
|
+
- **`button/foreground`**
|
|
94
94
|
- **`button/gap`**
|
|
95
95
|
- **`button/lineHeight`**
|
|
96
96
|
- **`button/padding/horizontal`**
|
|
@@ -89,7 +89,7 @@ function Button({
|
|
|
89
89
|
const fontWeight = typeof fontWeightValue === 'number' ? fontWeightValue.toString() : fontWeightValue
|
|
90
90
|
const lineHeight = getVariableByName('button/lineHeight', modes) || 19
|
|
91
91
|
const fontSize = getVariableByName('button/fontSize', modes) || 16
|
|
92
|
-
const textColor = getVariableByName('button/
|
|
92
|
+
const textColor = getVariableByName('button/foreground', modes) || '#0f0d0a'
|
|
93
93
|
|
|
94
94
|
const baseLabelTextStyle: TextStyle = {
|
|
95
95
|
color: textColor,
|
|
@@ -62,6 +62,7 @@ This component uses the following design token collections. Each collection supp
|
|
|
62
62
|
|
|
63
63
|
This component uses the following design tokens, resolved through `getVariableByName`:
|
|
64
64
|
|
|
65
|
+
- **`appearanceFeedback/cardFeedback/icon/color`**
|
|
65
66
|
- **`cardFeedback/actionSlot/gap`**
|
|
66
67
|
- **`cardFeedback/background`**
|
|
67
68
|
- **`cardFeedback/body/color`**
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React, { createContext, useContext, isValidElement, cloneElement } from 'react';
|
|
2
2
|
import { View, Text, StyleSheet, type ViewStyle, type TextStyle, type StyleProp, Image } from 'react-native';
|
|
3
3
|
import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
|
|
4
|
+
import IconComponent from '../../icons/Icon';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Context to share 'modes' with child components.
|
|
@@ -29,9 +30,15 @@ export interface CardFeedbackProps {
|
|
|
29
30
|
*/
|
|
30
31
|
export function CardFeedback({
|
|
31
32
|
children,
|
|
32
|
-
modes
|
|
33
|
+
modes: propModes,
|
|
33
34
|
style,
|
|
34
35
|
}: CardFeedbackProps) {
|
|
36
|
+
const modes = {
|
|
37
|
+
'Appearance.System': 'positive',
|
|
38
|
+
'Context': 'Nudge&Alert',
|
|
39
|
+
'Semantic Intent': 'System',
|
|
40
|
+
...propModes,
|
|
41
|
+
};
|
|
35
42
|
// Container Tokens
|
|
36
43
|
const backgroundColor = getVariableByName('cardFeedback/background', modes) || '#dbf0d9';
|
|
37
44
|
const radius = parseFloat(getVariableByName('cardFeedback/radius', modes) || '12');
|
|
@@ -66,16 +73,16 @@ export function CardFeedback({
|
|
|
66
73
|
|
|
67
74
|
/**
|
|
68
75
|
* Icon Wrapper
|
|
69
|
-
* Tokens: cardFeedback/icon/size, cardFeedback/icon/color
|
|
76
|
+
* Tokens: cardFeedback/icon/size, appearanceFeedback/cardFeedback/icon/color
|
|
70
77
|
*/
|
|
71
|
-
export function Icon({ children, style, modes: propModes }: { children?: React.ReactNode; style?: StyleProp<ViewStyle>; modes?: Record<string, any
|
|
78
|
+
export function Icon({ children, style, modes: propModes, icon }: { children?: React.ReactNode; style?: StyleProp<ViewStyle>; modes?: Record<string, any>; icon?: string }) {
|
|
72
79
|
const context = useContext(CardFeedbackContext);
|
|
73
80
|
const modes = propModes || context.modes || {};
|
|
74
81
|
|
|
75
82
|
const size = parseFloat(getVariableByName('cardFeedback/icon/size', modes) || '20');
|
|
76
|
-
// Color
|
|
77
|
-
|
|
78
|
-
|
|
83
|
+
// Color for the icon itself
|
|
84
|
+
const color = getVariableByName('appearanceFeedback/cardFeedback/icon/color', modes);
|
|
85
|
+
console.log(color);
|
|
79
86
|
const containerStyle: ViewStyle = {
|
|
80
87
|
width: size,
|
|
81
88
|
height: size,
|
|
@@ -84,11 +91,21 @@ export function Icon({ children, style, modes: propModes }: { children?: React.R
|
|
|
84
91
|
overflow: 'hidden',
|
|
85
92
|
};
|
|
86
93
|
|
|
87
|
-
//
|
|
88
|
-
|
|
94
|
+
// Pass modes to children (e.g., icon components that accept modes)
|
|
95
|
+
const childrenWithModes = React.Children.map(children, child => {
|
|
96
|
+
if (isValidElement(child)) {
|
|
97
|
+
return cloneElement(child as React.ReactElement<{ modes?: Record<string, any> }>, { modes: { ...(child.props as any).modes, ...modes } });
|
|
98
|
+
}
|
|
99
|
+
return child;
|
|
100
|
+
});
|
|
101
|
+
|
|
89
102
|
return (
|
|
90
103
|
<View style={[containerStyle, style]}>
|
|
91
|
-
{
|
|
104
|
+
{icon ? (
|
|
105
|
+
<IconComponent name={icon} size={size} color={color} />
|
|
106
|
+
) : (
|
|
107
|
+
childrenWithModes
|
|
108
|
+
)}
|
|
92
109
|
</View>
|
|
93
110
|
);
|
|
94
111
|
}
|
|
@@ -104,9 +121,17 @@ export function Content({ children, style, modes: propModes }: { children?: Reac
|
|
|
104
121
|
|
|
105
122
|
const gap = parseFloat(getVariableByName('cardFeedback/text/gap', modes) || '8');
|
|
106
123
|
|
|
124
|
+
// Pass modes to children (Title, Body, etc.)
|
|
125
|
+
const childrenWithModes = React.Children.map(children, child => {
|
|
126
|
+
if (isValidElement(child)) {
|
|
127
|
+
return cloneElement(child as React.ReactElement<{ modes?: Record<string, any> }>, { modes: { ...(child.props as any).modes, ...modes } });
|
|
128
|
+
}
|
|
129
|
+
return child;
|
|
130
|
+
});
|
|
131
|
+
|
|
107
132
|
return (
|
|
108
133
|
<View style={[{ gap, width: '100%' }, style]}>
|
|
109
|
-
{
|
|
134
|
+
{childrenWithModes}
|
|
110
135
|
</View>
|
|
111
136
|
);
|
|
112
137
|
}
|
|
@@ -169,12 +194,12 @@ export function Action({ children, style, modes: propModes }: { children?: React
|
|
|
169
194
|
const context = useContext(CardFeedbackContext);
|
|
170
195
|
const modes = propModes || context.modes || {};
|
|
171
196
|
|
|
172
|
-
const gap = parseFloat(getVariableByName('cardFeedback/actionSlot/gap', modes) || '
|
|
197
|
+
const gap = parseFloat(getVariableByName('cardFeedback/actionSlot/gap', modes) || '0');
|
|
173
198
|
|
|
174
199
|
// "Maximise existing component usage" -> Pass modes to children (Buttons)
|
|
175
200
|
const childrenWithModes = React.Children.map(children, child => {
|
|
176
201
|
if (isValidElement(child)) {
|
|
177
|
-
return cloneElement(child as
|
|
202
|
+
return cloneElement(child as React.ReactElement<{ modes?: Record<string, any> }>, { modes: { ...(child.props as any).modes, ...modes } });
|
|
178
203
|
}
|
|
179
204
|
return child;
|
|
180
205
|
});
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { Meta, Story, Canvas, PureArgsTable as ArgsTable } from '@storybook/addon-docs/blocks';
|
|
2
|
+
import * as DividerStories from './Divider.stories';
|
|
3
|
+
import Divider from './Divider';
|
|
4
|
+
|
|
5
|
+
<Meta of={DividerStories} />
|
|
6
|
+
|
|
7
|
+
# Divider
|
|
8
|
+
|
|
9
|
+
A simple divider component that renders a horizontal or vertical separator line. Uses design tokens for consistent sizing and color.
|
|
10
|
+
|
|
11
|
+
## Props
|
|
12
|
+
|
|
13
|
+
<ArgsTable of={Divider} />
|
|
14
|
+
|
|
15
|
+
## Available Collections and Modes
|
|
16
|
+
|
|
17
|
+
This component uses the following design token collections. Each collection supports multiple modes that can be configured via the `modes` prop.
|
|
18
|
+
|
|
19
|
+
### Color Mode
|
|
20
|
+
- **Modes:** Light | Dark
|
|
21
|
+
- **Default:** Light
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
### Horizontal Divider
|
|
25
|
+
|
|
26
|
+
The default horizontal divider stretches to fill its container width:
|
|
27
|
+
|
|
28
|
+
<Canvas>
|
|
29
|
+
<Story of={DividerStories.Horizontal} />
|
|
30
|
+
</Canvas>
|
|
31
|
+
|
|
32
|
+
### Vertical Divider
|
|
33
|
+
|
|
34
|
+
Vertical dividers stretch to fill their container height:
|
|
35
|
+
|
|
36
|
+
<Canvas>
|
|
37
|
+
<Story of={DividerStories.Vertical} />
|
|
38
|
+
</Canvas>
|
|
39
|
+
|
|
40
|
+
### In Content Layout
|
|
41
|
+
|
|
42
|
+
Use horizontal dividers to separate content sections:
|
|
43
|
+
|
|
44
|
+
<Canvas>
|
|
45
|
+
<Story of={DividerStories.InContentLayout} />
|
|
46
|
+
</Canvas>
|
|
47
|
+
|
|
48
|
+
### In Row Layout
|
|
49
|
+
|
|
50
|
+
Use vertical dividers to separate items in a horizontal row:
|
|
51
|
+
|
|
52
|
+
<Canvas>
|
|
53
|
+
<Story of={DividerStories.InRowLayout} />
|
|
54
|
+
</Canvas>
|
|
55
|
+
|
|
56
|
+
## Component Usage
|
|
57
|
+
|
|
58
|
+
```tsx
|
|
59
|
+
import { Divider } from 'jsf-components';
|
|
60
|
+
|
|
61
|
+
// Horizontal divider (default)
|
|
62
|
+
<Divider />
|
|
63
|
+
|
|
64
|
+
// Vertical divider
|
|
65
|
+
<Divider direction="vertical" />
|
|
66
|
+
|
|
67
|
+
// In a vertical layout
|
|
68
|
+
<View>
|
|
69
|
+
<Text>Section 1</Text>
|
|
70
|
+
<Divider />
|
|
71
|
+
<Text>Section 2</Text>
|
|
72
|
+
</View>
|
|
73
|
+
|
|
74
|
+
// In a horizontal layout
|
|
75
|
+
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
|
|
76
|
+
<Text>Item 1</Text>
|
|
77
|
+
<View style={{ marginHorizontal: 8 }}>
|
|
78
|
+
<Divider direction="vertical" />
|
|
79
|
+
</View>
|
|
80
|
+
<Text>Item 2</Text>
|
|
81
|
+
</View>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Design Tokens
|
|
85
|
+
|
|
86
|
+
This component uses the following design tokens, resolved through `getVariableByName`:
|
|
87
|
+
|
|
88
|
+
- **`divider/color`**
|
|
89
|
+
- **`divider/size`**
|
|
90
|
+
|
|
91
|
+
All tokens support mode-based theming through the `modes` prop.
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import {
|
|
3
|
+
View,
|
|
4
|
+
type ViewStyle,
|
|
5
|
+
type StyleProp,
|
|
6
|
+
} from 'react-native'
|
|
7
|
+
import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
|
|
8
|
+
|
|
9
|
+
export type DividerDirection = 'horizontal' | 'vertical'
|
|
10
|
+
|
|
11
|
+
export type DividerProps = {
|
|
12
|
+
/**
|
|
13
|
+
* Direction of the divider line.
|
|
14
|
+
* - 'horizontal': renders a horizontal line (default)
|
|
15
|
+
* - 'vertical': renders a vertical line
|
|
16
|
+
*/
|
|
17
|
+
direction?: DividerDirection
|
|
18
|
+
/**
|
|
19
|
+
* Mode configuration passed to the token resolver.
|
|
20
|
+
*/
|
|
21
|
+
modes?: Record<string, any>
|
|
22
|
+
/**
|
|
23
|
+
* Optional style overrides for the divider
|
|
24
|
+
*/
|
|
25
|
+
style?: StyleProp<ViewStyle>
|
|
26
|
+
/**
|
|
27
|
+
* Accessibility label for the divider
|
|
28
|
+
*/
|
|
29
|
+
accessibilityLabel?: string
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Divider component that renders a horizontal or vertical separator line.
|
|
34
|
+
*
|
|
35
|
+
* This component is used to visually separate content sections. It supports
|
|
36
|
+
* both horizontal and vertical orientations based on the `direction` prop.
|
|
37
|
+
*
|
|
38
|
+
* @component
|
|
39
|
+
* @param {Object} props - Component props
|
|
40
|
+
* @param {'horizontal' | 'vertical'} [props.direction='horizontal'] - Direction of the divider
|
|
41
|
+
* @param {Object} [props.modes={}] - Mode configuration for design tokens
|
|
42
|
+
* @param {Object} [props.style] - Optional style overrides
|
|
43
|
+
* @param {string} [props.accessibilityLabel] - Accessibility label for the divider
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```tsx
|
|
47
|
+
* // Horizontal divider (default)
|
|
48
|
+
* <Divider />
|
|
49
|
+
*
|
|
50
|
+
* // Vertical divider
|
|
51
|
+
* <Divider direction="vertical" />
|
|
52
|
+
*
|
|
53
|
+
* // With custom modes
|
|
54
|
+
* <Divider modes={{ Appearance: 'dark' }} />
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
function Divider({
|
|
58
|
+
direction = 'horizontal',
|
|
59
|
+
modes = {},
|
|
60
|
+
style,
|
|
61
|
+
accessibilityLabel = 'Divider',
|
|
62
|
+
}: DividerProps) {
|
|
63
|
+
// Resolve design tokens
|
|
64
|
+
const size = getVariableByName('divider/size', modes) ?? 1
|
|
65
|
+
const color = getVariableByName('divider/color', modes) ?? '#1a1c1f'
|
|
66
|
+
|
|
67
|
+
const isVertical = direction === 'vertical'
|
|
68
|
+
|
|
69
|
+
const dividerStyle: ViewStyle = isVertical
|
|
70
|
+
? {
|
|
71
|
+
width: size,
|
|
72
|
+
backgroundColor: color,
|
|
73
|
+
alignSelf: 'stretch',
|
|
74
|
+
}
|
|
75
|
+
: {
|
|
76
|
+
height: size,
|
|
77
|
+
backgroundColor: color,
|
|
78
|
+
alignSelf: 'stretch',
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
<View
|
|
83
|
+
style={[dividerStyle, style]}
|
|
84
|
+
accessibilityRole="none"
|
|
85
|
+
accessibilityLabel={accessibilityLabel}
|
|
86
|
+
/>
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export default Divider
|
|
91
|
+
|
|
@@ -7,7 +7,9 @@ import { AccessibilitySection, AnatomySection, UsageConstraintsSection } from '.
|
|
|
7
7
|
|
|
8
8
|
# ListItem
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
A token-driven list item component supporting vertical and horizontal layouts, with optional navigation behaviour and slots for leading, support text and trailing content.
|
|
11
|
+
|
|
12
|
+
The horizontal layout includes a **NavArrow** on the far right by default, which can be hidden via the `navArrow` prop.
|
|
11
13
|
|
|
12
14
|
## Available Collections and Modes
|
|
13
15
|
|
|
@@ -26,9 +28,47 @@ This component uses the following design token collections. Each collection supp
|
|
|
26
28
|
- **Default:** Light
|
|
27
29
|
## Usage
|
|
28
30
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
### Horizontal Layout with NavArrow
|
|
32
|
+
|
|
33
|
+
<Canvas of={ListItemStories.HorizontalNavigation} />
|
|
34
|
+
|
|
35
|
+
### Horizontal Layout (NavArrow only, no endSlot)
|
|
36
|
+
|
|
37
|
+
<Canvas of={ListItemStories.HorizontalWithNavArrowOnly} />
|
|
38
|
+
|
|
39
|
+
### Vertical Layout
|
|
40
|
+
|
|
41
|
+
<Canvas of={ListItemStories.VerticalStatic} />
|
|
42
|
+
|
|
43
|
+
### Usage Example
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
import ListItem from './ListItem';
|
|
47
|
+
import Button from '../Button/Button';
|
|
48
|
+
|
|
49
|
+
// Horizontal with NavArrow (default)
|
|
50
|
+
<ListItem
|
|
51
|
+
layout="Horizontal"
|
|
52
|
+
title="Navigate to Details"
|
|
53
|
+
supportText="Tap to view more"
|
|
54
|
+
navArrow={true}
|
|
55
|
+
/>
|
|
56
|
+
|
|
57
|
+
// Horizontal without NavArrow
|
|
58
|
+
<ListItem
|
|
59
|
+
layout="Horizontal"
|
|
60
|
+
title="Balance"
|
|
61
|
+
supportText="₹500"
|
|
62
|
+
navArrow={false}
|
|
63
|
+
endSlot={<Button label="Add Money" />}
|
|
64
|
+
/>
|
|
65
|
+
|
|
66
|
+
// Vertical layout (NavArrow not applicable)
|
|
67
|
+
<ListItem
|
|
68
|
+
layout="Vertical"
|
|
69
|
+
supportText="Payments"
|
|
70
|
+
/>
|
|
71
|
+
```
|
|
32
72
|
|
|
33
73
|
|
|
34
74
|
<AccessibilitySection
|
|
@@ -43,7 +83,8 @@ This component uses the following design token collections. Each collection supp
|
|
|
43
83
|
<ul>
|
|
44
84
|
<li><strong>Leading slot</strong> — optional icon/avatar.</li>
|
|
45
85
|
<li><strong>Title & support</strong> — primary and secondary text.</li>
|
|
46
|
-
<li><strong>Trailing slot</strong> — optional meta info or action affordance.</li>
|
|
86
|
+
<li><strong>Trailing slot (endSlot)</strong> — optional meta info or action affordance. <strong>Note:</strong> This slot strictly enforces <code>Context: 'ListItem'</code> for its children, overriding any conflicting context passed via the <code>modes</code> prop.</li>
|
|
87
|
+
<li><strong>NavArrow</strong> — navigation chevron on the far right (Horizontal layout only). Shown by default, can be hidden via <code>navArrow={false}</code>.</li>
|
|
47
88
|
</ul>
|
|
48
89
|
</AnatomySection>
|
|
49
90
|
|