jfs-components 0.1.35 → 0.1.48
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 +43 -0
- package/D2C.md +82 -87
- package/lib/commonjs/components/Accordion/Accordion.js +16 -3
- package/lib/commonjs/components/AppBar/AppBar.js +118 -93
- package/lib/commonjs/components/CompareTable/CompareTable.js +98 -33
- package/lib/commonjs/components/FormField/FormField.js +9 -1
- package/lib/commonjs/components/FullscreenModal/FullscreenModal.js +4 -14
- package/lib/commonjs/components/HelloJioInput/HelloJioInput.js +20 -4
- package/lib/commonjs/components/PageHero/PageHero.js +3 -0
- package/lib/commonjs/components/ProductMerchandisingCard/ProductMerchandisingCard.js +50 -30
- package/lib/commonjs/components/ValueBackMetric/ValueBackMetric.js +117 -89
- package/lib/commonjs/components/index.js +7 -1
- package/lib/commonjs/design-tokens/Coin Variables-variables-full.json +1 -42204
- package/lib/commonjs/icons/registry.js +1 -1
- package/lib/module/components/Accordion/Accordion.js +16 -3
- package/lib/module/components/AppBar/AppBar.js +116 -93
- package/lib/module/components/CompareTable/CompareTable.js +99 -34
- package/lib/module/components/FormField/FormField.js +9 -1
- package/lib/module/components/FullscreenModal/FullscreenModal.js +4 -14
- package/lib/module/components/HelloJioInput/HelloJioInput.js +20 -4
- package/lib/module/components/PageHero/PageHero.js +3 -0
- package/lib/module/components/ProductMerchandisingCard/ProductMerchandisingCard.js +50 -30
- package/lib/module/components/ValueBackMetric/ValueBackMetric.js +120 -93
- package/lib/module/components/index.js +1 -1
- package/lib/module/design-tokens/Coin Variables-variables-full.json +1 -42204
- package/lib/module/icons/registry.js +1 -1
- package/lib/typescript/src/components/Accordion/Accordion.d.ts +14 -1
- package/lib/typescript/src/components/AppBar/AppBar.d.ts +46 -14
- package/lib/typescript/src/components/CompareTable/CompareTable.d.ts +29 -12
- package/lib/typescript/src/components/HelloJioInput/HelloJioInput.d.ts +6 -4
- package/lib/typescript/src/components/PageHero/PageHero.d.ts +1 -0
- package/lib/typescript/src/components/ProductMerchandisingCard/ProductMerchandisingCard.d.ts +8 -1
- package/lib/typescript/src/components/ValueBackMetric/ValueBackMetric.d.ts +27 -16
- package/lib/typescript/src/components/index.d.ts +1 -1
- package/lib/typescript/src/icons/registry.d.ts +1 -1
- package/package.json +3 -1
- package/scripts/extract-figma-modes.js +150 -121
- package/src/components/Accordion/Accordion.tsx +61 -33
- package/src/components/AppBar/AppBar.tsx +154 -124
- package/src/components/CompareTable/CompareTable.tsx +138 -52
- package/src/components/FormField/FormField.tsx +7 -0
- package/src/components/FullscreenModal/FullscreenModal.tsx +5 -14
- package/src/components/HelloJioInput/HelloJioInput.tsx +31 -12
- package/src/components/PageHero/PageHero.tsx +5 -0
- package/src/components/ProductMerchandisingCard/ProductMerchandisingCard.tsx +63 -30
- package/src/components/ValueBackMetric/ValueBackMetric.tsx +178 -104
- package/src/components/index.ts +6 -1
- package/src/design-tokens/Coin Variables-variables-full.json +1 -42204
- package/src/icons/registry.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,49 @@ All notable changes to this project are documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
6
6
|
|
|
7
|
+
## [0.1.48] - 2026-07-22
|
|
8
|
+
|
|
9
|
+
- `CompareTable` (fixed-width scroll mode) — selection cards pin above the scroll surface (`stickyCards`, default `true`); accordion section titles stick under the cards and replace each other as you scroll (`stickyAccordionTitles`, default `true`); gray table-header sticky is now opt-in (`stickyHeaders` default `false`). Selection cards are square (corner radius forced to `0`).
|
|
10
|
+
- `Accordion` — new `showHeader` / `showContent` props so hosts can split sticky titles from bodies in scroll layouts (used by `CompareTable`).
|
|
11
|
+
- `HelloJioInput` — `leading={null}` hides the leading icon; IdleJioPlus is borderless frosted glass so blur shows through.
|
|
12
|
+
- `ProductMerchandisingCard` — new `badgeIcon`; footer special badge fully token-driven; footer text column keeps a min-width so CTAs don’t crush title/subtitle.
|
|
13
|
+
- `ValueBackMetric` — CTA is purple non-underlined `Text` (not `Link`), with optional `link` override slot; layout/typography resolve via `valueBackMetric/*`.
|
|
14
|
+
- Token sync — refreshed Coin Variables / `.token-metadata.json` / icon registry for the above components.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## [0.1.38] - 2026-07-22
|
|
19
|
+
|
|
20
|
+
- `FormField` — caret / cursor / selection color now follows the tokenized input text color (no platform blue/green accent).
|
|
21
|
+
- `FullscreenModal` — sticky footer no longer adds safe-area bottom padding on top of `ActionFooter` tokens (avoids double inset).
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## [0.1.36] - 2026-07-21
|
|
26
|
+
|
|
27
|
+
- `AppBar` — exported `JioDot` brand mark for MainPage `leadingSlot` (no default logo; opt in with `<JioDot />`). Also exports `AppBarProps` / `JioDotProps`.
|
|
28
|
+
- `PageHero` — new `body` / `showBody` (and `showEyebrow`) for a second text line; media sizing moves to `video/*` tokens + `Video / Output` mode (117px fallback).
|
|
29
|
+
- `LottiePlayer` — default size tokens renamed `media/*` → `video/*`; mode key is now `Video / Output` (**breaking** for callers still passing `Media / Output`).
|
|
30
|
+
- Library-wide mode/docs refresh (`figma-modes.generated.ts`, Coin Variables, component MDX).
|
|
31
|
+
|
|
32
|
+
> Note: `0.1.35` was published on npm as an intermediate build; the next documented release is `0.1.36`.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## [0.1.34] - 2026-07-21
|
|
37
|
+
|
|
38
|
+
- `jfs-extract-modes` now automatically loads `FIGMA_ACCESS_TOKEN` from the
|
|
39
|
+
consuming project's root `.env` without overriding a token exported in the
|
|
40
|
+
shell. `dotenv` is now an explicit runtime dependency.
|
|
41
|
+
- Simplified `D2C.md` for use as a Cursor slash command: clearer input handling,
|
|
42
|
+
ancestor-mode inheritance (`inheritedFromAncestors` + path merges), accurate
|
|
43
|
+
`unresolved` reasons, verify-before-invent component APIs, and practical
|
|
44
|
+
visual QA (fix implementation diffs; report environment-only diffs).
|
|
45
|
+
- Added focused tests for `.env` precedence and extracted mode diagnostics.
|
|
46
|
+
- Ignore local `.env` / `.env.*` secrets in git (keep `.env.example` allowed).
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
7
50
|
## [0.1.33] - 2026-07-20
|
|
8
51
|
|
|
9
52
|
- **Use [`D2C.md`](./D2C.md) for Design-to-Code.** This is the playbook we want agents and consumers to follow when implementing a Figma screen with `jfs-components`. It ships in the npm package (see `package.json` `files`) so it is available at `node_modules/jfs-components/D2C.md` after install. The workflow is mandatory end-to-end: Figma MCP (`get_design_context` + `get_screenshot`) → mode extraction via `npx jfs-extract-modes` (REST API; Dev seat / file-read token is enough) → build with exact modes → visual QA against the Figma screenshot. Point Cursor rules / agent instructions at this file rather than inventing a parallel D2C process.
|
package/D2C.md
CHANGED
|
@@ -1,118 +1,113 @@
|
|
|
1
|
-
# D2C — Design to Code
|
|
1
|
+
# D2C — Design to Code with jfs-components
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
order. Do not skip steps and do not improvise substitutes for them.
|
|
3
|
+
Implement the supplied Figma design using the installed `jfs-components`
|
|
4
|
+
package. Follow all four stages in order.
|
|
6
5
|
|
|
7
|
-
##
|
|
6
|
+
## Input
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
`<a>:<b>`).
|
|
8
|
+
Use the Figma URL supplied after `/D2C` or in the user's latest message. If no
|
|
9
|
+
Figma URL was supplied, ask for one before doing anything else.
|
|
12
10
|
|
|
13
|
-
1.
|
|
14
|
-
structure, layout, props, variants, text content, and Code Connect hints.
|
|
15
|
-
2. Call `get_screenshot` for the same node and keep the image. This is the
|
|
16
|
-
visual ground truth you will QA against in Step 4.
|
|
11
|
+
## 1. Read the design
|
|
17
12
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
13
|
+
1. Call the Figma MCP `get_design_context` for the linked node.
|
|
14
|
+
2. Call `get_screenshot` for the same node. Keep it as the visual reference for
|
|
15
|
+
final QA.
|
|
16
|
+
3. Follow Code Connect mappings, design annotations, and provided assets. If
|
|
17
|
+
Figma supplies an image or SVG URL, use it directly; do not create a
|
|
18
|
+
placeholder or install an icon package.
|
|
22
19
|
|
|
23
|
-
|
|
20
|
+
If the Figma MCP is unavailable or cannot read the node, report the blocker and
|
|
21
|
+
stop. Do not recreate the design from the URL or from assumptions.
|
|
24
22
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
## 2. Extract and resolve explicit modes
|
|
24
|
+
|
|
25
|
+
From the consuming project's root, run:
|
|
28
26
|
|
|
29
27
|
```bash
|
|
30
|
-
|
|
31
|
-
npx jfs-extract-modes "<the pasted Figma URL>" --out modes.json
|
|
28
|
+
npx jfs-extract-modes "<figma-url>" --out modes.json
|
|
32
29
|
```
|
|
33
30
|
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
The CLI automatically loads `FIGMA_ACCESS_TOKEN` from the project-root `.env`.
|
|
32
|
+
An already exported shell value takes precedence. The token only needs the
|
|
33
|
+
Figma `file_content:read` scope. Never print, log, or ask the user to paste the
|
|
34
|
+
token into chat.
|
|
36
35
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
If the token is missing, stop and direct the user to `[Open .env](.env)` and
|
|
37
|
+
add `FIGMA_ACCESS_TOKEN=` if needed. To create a token: in Figma's file browser,
|
|
38
|
+
open the account menu → **Settings** → **Security** → **Personal access
|
|
39
|
+
tokens** → **Generate new token**, choose an expiration, grant only
|
|
40
|
+
`file_content:read`, generate it, and copy it immediately into `.env`. Figma
|
|
41
|
+
shows the value only once. Use the
|
|
42
|
+
[official PAT documentation](https://developers.figma.com/docs/rest-api/personal-access-tokens/)
|
|
43
|
+
if the UI has changed. Never create or overwrite a token value yourself.
|
|
40
44
|
|
|
41
|
-
|
|
45
|
+
If the bin is unavailable in a nonstandard monorepo, run:
|
|
42
46
|
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
"modes": [
|
|
46
|
-
{ "layer": "Icon Button", "nodeId": "1:10638",
|
|
47
|
-
"path": "CC / Unsub > AppBar > Slot 2 > Icon Button",
|
|
48
|
-
"modes": { "Button / Size": "S", "Emphasis": "Low", "AppearanceBrand": "Neutral" } }
|
|
49
|
-
]
|
|
50
|
-
}
|
|
47
|
+
```bash
|
|
48
|
+
node node_modules/jfs-components/scripts/extract-figma-modes.js "<figma-url>" --out modes.json
|
|
51
49
|
```
|
|
52
50
|
|
|
53
|
-
|
|
51
|
+
Resolve the effective modes for each represented component as follows:
|
|
54
52
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
screen cannot be built faithfully. Do not guess missing modes.
|
|
53
|
+
1. Start with `inheritedFromAncestors`, when present.
|
|
54
|
+
2. Find every `modes` entry whose `path` is the component's path or an ancestor
|
|
55
|
+
of it.
|
|
56
|
+
3. Merge those entries from root to leaf; deeper entries override inherited
|
|
57
|
+
values.
|
|
58
|
+
4. Match by layer name and position, using `nodeId` to disambiguate duplicate
|
|
59
|
+
names.
|
|
63
60
|
|
|
64
|
-
|
|
61
|
+
If `unresolved` is present, report every item and its exact `reason`, then stop
|
|
62
|
+
before implementation because mode fidelity cannot be guaranteed:
|
|
65
63
|
|
|
66
|
-
|
|
67
|
-
library
|
|
64
|
+
- `local collection` means the design uses a file-local collection that the
|
|
65
|
+
packaged library data cannot resolve.
|
|
66
|
+
- `key not in variables JSON` or `mode not in collection` means the packaged
|
|
67
|
+
design-token JSON is out of date.
|
|
68
68
|
|
|
69
|
-
|
|
70
|
-
collection name → mode name, on exactly the layer (or subtree) the extractor
|
|
71
|
-
reported. There is zero tolerance for inconsistency here:
|
|
69
|
+
Do not guess or silently omit an unresolved mode.
|
|
72
70
|
|
|
73
|
-
|
|
74
|
-
`Emphasis: Low`).
|
|
75
|
-
- Do not drop modes that seem redundant — if the design sets
|
|
76
|
-
`AppearanceBrand: Neutral` on one Icon Button and `Appearance: Primary` on
|
|
77
|
-
another, those are different and intentional.
|
|
78
|
-
- Do not apply a child's modes to its parent or vice versa.
|
|
71
|
+
## 3. Implement
|
|
79
72
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
handling.
|
|
73
|
+
Before editing, inspect the installed package's exports/types and the consuming
|
|
74
|
+
app's existing screens. Reuse its file placement, navigation, styling, and
|
|
75
|
+
asset conventions. Never invent a `jfs-components` component or prop.
|
|
84
76
|
|
|
85
|
-
|
|
77
|
+
- Prefer mapped `jfs-components` over rebuilding them from primitives.
|
|
78
|
+
- Apply extracted collection/mode pairs exactly through each component's
|
|
79
|
+
`modes` prop.
|
|
80
|
+
- Put a mode on the closest code component that represents the reported Figma
|
|
81
|
+
layer or subtree. Preserve parent inheritance and child overrides; never
|
|
82
|
+
flatten a child override into its parent.
|
|
83
|
+
- Do not substitute similar-looking modes, remove apparently redundant modes,
|
|
84
|
+
or move modes between layers.
|
|
85
|
+
- Match Figma content, order, visibility, variants, spacing, and assets.
|
|
86
86
|
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
Run the consuming app's existing typecheck, tests, or build command relevant to
|
|
88
|
+
the changed screen and fix implementation errors.
|
|
89
89
|
|
|
90
|
-
|
|
91
|
-
screen, and take a screenshot (`mobile_take_screenshot`).
|
|
92
|
-
- **Web deployment:** use the browser tooling — navigate to the deployed/dev
|
|
93
|
-
URL and take a screenshot.
|
|
90
|
+
## 4. Render and visually verify
|
|
94
91
|
|
|
95
|
-
|
|
92
|
+
Render the implemented screen on the target platform and capture it with the
|
|
93
|
+
available mobile or browser tooling. Compare it with the Figma screenshot for:
|
|
96
94
|
|
|
97
|
-
1.
|
|
98
|
-
2.
|
|
99
|
-
3.
|
|
100
|
-
4.
|
|
101
|
-
5.
|
|
95
|
+
1. component size, variant, and emphasis;
|
|
96
|
+
2. color and appearance;
|
|
97
|
+
3. spacing, padding, alignment, and gaps;
|
|
98
|
+
4. text, wrapping, and truncation; and
|
|
99
|
+
5. icons, imagery, and layer visibility.
|
|
102
100
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
101
|
+
Fix every implementation-caused difference and capture the screen again.
|
|
102
|
+
Platform or environment differences that cannot be fixed in code, such as a
|
|
103
|
+
missing system font or renderer-specific antialiasing, must be identified
|
|
104
|
+
explicitly rather than hidden.
|
|
107
105
|
|
|
108
|
-
|
|
106
|
+
If no suitable render or screenshot tooling is available, do not claim visual
|
|
107
|
+
completion. Report what remains to be checked and give the exact manual run and
|
|
108
|
+
comparison steps.
|
|
109
109
|
|
|
110
|
-
|
|
111
|
-
|---|---|---|
|
|
112
|
-
| Design context + screenshot | Figma MCP | No |
|
|
113
|
-
| Mode extraction | `npx jfs-extract-modes` | No |
|
|
114
|
-
| Build | jfs-components | — |
|
|
115
|
-
| Visual QA loop | Mobile MCP or browser | No |
|
|
110
|
+
## Completion report
|
|
116
111
|
|
|
117
|
-
Report
|
|
118
|
-
|
|
112
|
+
Report the files changed, the effective modes applied to each component, the
|
|
113
|
+
validation commands and results, and the final visual comparison status.
|
|
@@ -68,6 +68,8 @@ function Accordion({
|
|
|
68
68
|
accessibilityState,
|
|
69
69
|
webAccessibilityProps,
|
|
70
70
|
disableTruncation,
|
|
71
|
+
showHeader = true,
|
|
72
|
+
showContent = true,
|
|
71
73
|
...rest
|
|
72
74
|
}) {
|
|
73
75
|
const [internalExpanded, setInternalExpanded] = (0, _react.useState)(defaultExpanded);
|
|
@@ -105,8 +107,11 @@ function Accordion({
|
|
|
105
107
|
const contentPaddingTop = (0, _figmaVariablesResolver.getVariableByName)('accordion/content/padding/top', resolvedModes) ?? 8;
|
|
106
108
|
const contentPaddingBottom = (0, _figmaVariablesResolver.getVariableByName)('accordion/content/padding/bottom', resolvedModes) ?? 8;
|
|
107
109
|
const borderColor = (0, _figmaVariablesResolver.getVariableByName)('accordion/border/color', resolvedModes) ?? '#e6e6e6';
|
|
110
|
+
|
|
111
|
+
// Header-only sticky pieces drop the divider while expanded so the paired
|
|
112
|
+
// content Accordion owns the section border.
|
|
108
113
|
const containerStyle = {
|
|
109
|
-
borderBottomWidth: 1,
|
|
114
|
+
borderBottomWidth: !showContent && isExpanded ? 0 : 1,
|
|
110
115
|
borderBottomColor: borderColor
|
|
111
116
|
};
|
|
112
117
|
const headerStyle = {
|
|
@@ -143,10 +148,18 @@ function Accordion({
|
|
|
143
148
|
webAccessibilityProps
|
|
144
149
|
});
|
|
145
150
|
const processedChildren = children ? (0, _reactUtils.cloneChildrenWithModes)(_react.default.Children.toArray(children), resolvedModes) : null;
|
|
151
|
+
|
|
152
|
+
// Content-only + collapsed: nothing to render (header lives in the sticky peer).
|
|
153
|
+
if (!showHeader && !isExpanded) {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
if (!showHeader && !showContent) {
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
146
159
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
147
160
|
style: [containerStyle, style],
|
|
148
161
|
...rest,
|
|
149
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Pressable, {
|
|
162
|
+
children: [showHeader && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Pressable, {
|
|
150
163
|
accessibilityRole: "button",
|
|
151
164
|
accessibilityLabel: undefined,
|
|
152
165
|
accessibilityHint: accessibilityHint || (isExpanded ? 'Collapse accordion' : 'Expand accordion'),
|
|
@@ -176,7 +189,7 @@ function Accordion({
|
|
|
176
189
|
accessibilityElementsHidden: true,
|
|
177
190
|
importantForAccessibility: "no"
|
|
178
191
|
})]
|
|
179
|
-
}), isExpanded && processedChildren && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
192
|
+
}), showContent && isExpanded && processedChildren && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
180
193
|
style: contentStyle,
|
|
181
194
|
children: processedChildren
|
|
182
195
|
})]
|
|
@@ -3,22 +3,98 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.
|
|
7
|
-
|
|
6
|
+
exports.JioDot = JioDot;
|
|
7
|
+
exports.default = void 0;
|
|
8
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
8
9
|
var _reactNative = require("react-native");
|
|
10
|
+
var _reactNativeSvg = _interopRequireWildcard(require("react-native-svg"));
|
|
9
11
|
var _figmaVariablesResolver = require("../../design-tokens/figma-variables-resolver");
|
|
10
12
|
var _JFSThemeProvider = require("../../design-tokens/JFSThemeProvider");
|
|
11
13
|
var _NavArrow = _interopRequireDefault(require("../NavArrow/NavArrow"));
|
|
12
14
|
var _reactUtils = require("../../utils/react-utils");
|
|
13
15
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
14
16
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
15
|
-
|
|
16
|
-
// (node 449:7876). The middle slot is
|
|
17
|
-
// size; its inner content (node 3991:4125)
|
|
18
|
-
//
|
|
17
|
+
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
18
|
+
// SubPage "slot wrap" geometry from Figma (node 449:7876). The middle slot is
|
|
19
|
+
// an absolutely-centered fixed-size box; its inner content (node 3991:4125)
|
|
20
|
+
// fills / shrinks responsively within that box.
|
|
19
21
|
const SUBPAGE_MIDDLE_DEFAULT_WIDTH = 192;
|
|
20
22
|
const SUBPAGE_MIDDLE_HEIGHT = 32;
|
|
21
23
|
const SUBPAGE_MIDDLE_PADDING_HORIZONTAL = 21;
|
|
24
|
+
const JIODOT_FALLBACK_SIZE = 32;
|
|
25
|
+
/**
|
|
26
|
+
* Figma "jiodot" mark (node 1117:1908). Opt-in via `leadingSlot` — MainPage
|
|
27
|
+
* does NOT render this by default so existing consumers stay empty-leading.
|
|
28
|
+
*/
|
|
29
|
+
function JioDot({
|
|
30
|
+
size,
|
|
31
|
+
modes = _reactUtils.EMPTY_MODES
|
|
32
|
+
}) {
|
|
33
|
+
const resolvedSize = size ?? (Number((0, _figmaVariablesResolver.getVariableByName)('appBar/mainPage/jiodot/size', modes)) || JIODOT_FALLBACK_SIZE);
|
|
34
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNativeSvg.default, {
|
|
35
|
+
width: resolvedSize,
|
|
36
|
+
height: resolvedSize,
|
|
37
|
+
viewBox: "0 0 32 32",
|
|
38
|
+
fill: "none",
|
|
39
|
+
accessibilityRole: "image",
|
|
40
|
+
accessibilityLabel: "Jio",
|
|
41
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNativeSvg.G, {
|
|
42
|
+
clipPath: "url(#jiodotClip)",
|
|
43
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeSvg.Path, {
|
|
44
|
+
d: "M0 16C0 7.16344 7.16344 0 16 0C24.8366 0 32 7.16344 32 16C32 24.8366 24.8366 32 16 32C7.16344 32 0 24.8366 0 16Z",
|
|
45
|
+
fill: "url(#jiodotGrad)"
|
|
46
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeSvg.Path, {
|
|
47
|
+
d: "M11.3039 9.64982H10.7705C9.75837 9.64982 9.20548 10.2205 9.20548 11.3621V16.8679C9.20548 18.2854 8.72667 18.7829 7.60341 18.7829C6.71985 18.7829 6.00192 18.3959 5.43096 17.6963C5.37555 17.6229 4.21615 18.1749 4.21615 19.5378C4.21615 21.0107 5.59629 21.9132 8.15689 21.9132C11.268 21.9132 12.9077 20.3479 12.9077 16.9233V11.3621C12.9059 10.222 12.3536 9.64982 11.3039 9.64982ZM23.7039 12.3381C20.6837 12.3381 18.6769 14.2531 18.6769 17.1085C18.6769 20.0365 20.6096 21.915 23.6482 21.915C26.6674 21.915 28.6556 20.0365 28.6556 17.1274C28.6573 14.2531 26.6867 12.3381 23.7039 12.3381ZM23.6668 19.206C22.4879 19.206 21.6781 18.3408 21.6781 17.107C21.6781 15.8922 22.5077 15.0264 23.6668 15.0264C24.8259 15.0264 25.6553 15.8922 25.6553 17.1257C25.6556 18.3221 24.8084 19.206 23.6668 19.206ZM16.1252 12.43H15.7563C14.8547 12.43 14.1735 12.8531 14.1735 14.1426V20.0347C14.1735 21.3423 14.8366 21.7476 15.7936 21.7476H16.1622C17.0644 21.7476 17.7086 21.3052 17.7086 20.0347V14.1426C17.7086 12.8169 17.0831 12.43 16.1252 12.43ZM15.9228 8.15885C14.7809 8.15885 14.063 8.80359 14.063 9.81634C14.063 10.8469 14.7999 11.4913 15.9779 11.4913C17.1196 11.4913 17.8378 10.8469 17.8378 9.81634C17.8378 8.78582 17.1012 8.15885 15.9228 8.15885Z",
|
|
48
|
+
fill: "white"
|
|
49
|
+
})]
|
|
50
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNativeSvg.Defs, {
|
|
51
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNativeSvg.LinearGradient, {
|
|
52
|
+
id: "jiodotGrad",
|
|
53
|
+
x1: "11.085",
|
|
54
|
+
y1: "9.25442",
|
|
55
|
+
x2: "22.3469",
|
|
56
|
+
y2: "15.7638",
|
|
57
|
+
gradientUnits: "userSpaceOnUse",
|
|
58
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeSvg.Stop, {
|
|
59
|
+
stopColor: "#FBDCAF"
|
|
60
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeSvg.Stop, {
|
|
61
|
+
offset: "1",
|
|
62
|
+
stopColor: "#AD8444"
|
|
63
|
+
})]
|
|
64
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeSvg.ClipPath, {
|
|
65
|
+
id: "jiodotClip",
|
|
66
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeSvg.Rect, {
|
|
67
|
+
width: "32",
|
|
68
|
+
height: "32",
|
|
69
|
+
fill: "white"
|
|
70
|
+
})
|
|
71
|
+
})]
|
|
72
|
+
})]
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* AppBar — top navigation bar for MainPage and SubPage layouts
|
|
77
|
+
* (Figma node 1070:18571).
|
|
78
|
+
*
|
|
79
|
+
* Visual values resolve from design tokens via `getVariableByName`. Slots
|
|
80
|
+
* cascade `modes` to children through `cloneChildrenWithModes`.
|
|
81
|
+
*
|
|
82
|
+
* @component
|
|
83
|
+
* @example
|
|
84
|
+
* ```tsx
|
|
85
|
+
* <AppBar
|
|
86
|
+
* type="MainPage"
|
|
87
|
+
* leadingSlot={<JioDot />}
|
|
88
|
+
* actionsSlot={
|
|
89
|
+
* <>
|
|
90
|
+
* <IconButton iconName="ic_add" />
|
|
91
|
+
* <IconButton iconName="ic_add" />
|
|
92
|
+
* <Avatar />
|
|
93
|
+
* </>
|
|
94
|
+
* }
|
|
95
|
+
* />
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
22
98
|
function AppBar({
|
|
23
99
|
type = 'MainPage',
|
|
24
100
|
leadingSlot,
|
|
@@ -35,118 +111,70 @@ function AppBar({
|
|
|
35
111
|
const {
|
|
36
112
|
modes: globalModes
|
|
37
113
|
} = (0, _JFSThemeProvider.useTokens)();
|
|
38
|
-
const modes = {
|
|
114
|
+
const modes = (0, _react.useMemo)(() => ({
|
|
39
115
|
Context2: 'AppBar',
|
|
40
116
|
...globalModes,
|
|
41
117
|
...propModes
|
|
42
|
-
};
|
|
118
|
+
}), [globalModes, propModes]);
|
|
43
119
|
const isMain = type === 'MainPage';
|
|
44
120
|
const isSub = type === 'SubPage';
|
|
45
|
-
|
|
46
|
-
// --- Tokens ---
|
|
47
|
-
// Construct token keys based on type. keys must be camelCase as per instruction.
|
|
48
|
-
// We use explicit string literals so that the extraction script can find them.
|
|
49
121
|
const paddingHorizontal = isMain ? (0, _figmaVariablesResolver.getVariableByName)('appBar/mainPage/padding/horizontal', modes) : (0, _figmaVariablesResolver.getVariableByName)('appBar/subPage/padding/horizontal', modes);
|
|
50
122
|
const paddingVertical = isMain ? (0, _figmaVariablesResolver.getVariableByName)('appBar/mainPage/padding/vertical', modes) : (0, _figmaVariablesResolver.getVariableByName)('appBar/subPage/padding/vertical', modes);
|
|
51
123
|
const backgroundColor = isMain ? (0, _figmaVariablesResolver.getVariableByName)('appBar/mainPage/background', modes) : (0, _figmaVariablesResolver.getVariableByName)('appBar/subPage/background', modes);
|
|
52
124
|
const actionsGap = isMain ? (0, _figmaVariablesResolver.getVariableByName)('appBar/mainPage/actions/gap', modes) : (0, _figmaVariablesResolver.getVariableByName)('appBar/subPage/actions/gap', modes);
|
|
53
|
-
|
|
54
|
-
// Layout styles
|
|
55
125
|
const containerStyle = {
|
|
56
126
|
flexDirection: 'row',
|
|
57
127
|
alignItems: 'center',
|
|
58
|
-
// No `justifyContent` here: with the inline middle slot using `flex: 1`
|
|
59
|
-
// the three sections lay out naturally (leading | middle | actions).
|
|
60
|
-
// When middleSlot is absent we fall back to `space-between` at the wrapper
|
|
61
|
-
// level so leading & actions still anchor to the edges.
|
|
62
128
|
paddingHorizontal: paddingHorizontal ?? 16,
|
|
63
129
|
paddingVertical: paddingVertical ?? (isMain ? 16 : 10),
|
|
64
130
|
backgroundColor: backgroundColor ?? '#FFFFFF'
|
|
65
|
-
// MainPage: h=68 (16 top/bot padding? 36 height content?)
|
|
66
|
-
// SubPage: h=52
|
|
67
131
|
};
|
|
68
132
|
|
|
69
|
-
//
|
|
70
|
-
// SubPage default
|
|
71
|
-
// MainPage default: Jio Logo (We'll use a placeholder or basic text if no asset,
|
|
72
|
-
// but looking at "AppBarHome" before, it had "ic_hellojio". Let's stick to generic defaults or empty if not provided,
|
|
73
|
-
// but usually a component library provides sensible defaults.)
|
|
74
|
-
|
|
75
|
-
// Actually, let's implement the specific defaults mentioned in the plan/analysis
|
|
76
|
-
// SubPage: NavArrow props (icon, etc).
|
|
77
|
-
|
|
133
|
+
// MainPage: no default leading (backward compatible — consumers opt in via
|
|
134
|
+
// leadingSlot, e.g. <JioDot />). SubPage: default back NavArrow.
|
|
78
135
|
let defaultLeading = null;
|
|
79
|
-
if (leadingSlot === undefined) {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
});
|
|
100
|
-
} else {
|
|
101
|
-
// MainPage default leading: often the Brand Logo.
|
|
102
|
-
// We'll leave it empty or render a placeholder if desired, but standard AppBar often lets consumer pass logo.
|
|
103
|
-
// Previous code had `leadingIconName='ic_hellojio'` for Home.
|
|
104
|
-
// We will render nothing by default for MainPage leading to keep it flexible, OR a specialized Logo component if we had one.
|
|
105
|
-
// For now, let's assume consumer passes it for MainPage, or we leave empty.
|
|
106
|
-
// Wait, Figma screenshot shows "Jio" logo.
|
|
107
|
-
// The user said: "mostly token namings are 'camelCase'. Maximize existing component usage."
|
|
108
|
-
// I'll leave defaultLeading as null for MainPage unless I have a "BrandLogo" component.
|
|
109
|
-
}
|
|
136
|
+
if (leadingSlot === undefined && isSub) {
|
|
137
|
+
defaultLeading = /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
|
|
138
|
+
...(onLeadingPress ? {
|
|
139
|
+
onPress: onLeadingPress
|
|
140
|
+
} : {}),
|
|
141
|
+
hitSlop: 8,
|
|
142
|
+
accessibilityRole: "button",
|
|
143
|
+
accessibilityLabel: undefined,
|
|
144
|
+
style: ({
|
|
145
|
+
pressed
|
|
146
|
+
}) => ({
|
|
147
|
+
opacity: pressed ? 0.7 : 1,
|
|
148
|
+
alignItems: 'center',
|
|
149
|
+
justifyContent: 'center'
|
|
150
|
+
}),
|
|
151
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_NavArrow.default, {
|
|
152
|
+
direction: "Back",
|
|
153
|
+
modes: modes
|
|
154
|
+
})
|
|
155
|
+
});
|
|
110
156
|
}
|
|
111
|
-
|
|
112
|
-
// --- Process Slots ---
|
|
113
157
|
const processedLeading = leadingSlot ? (0, _reactUtils.cloneChildrenWithModes)([leadingSlot], modes)[0] : defaultLeading;
|
|
114
158
|
const processedMiddle = middleSlot ? (0, _reactUtils.cloneChildrenWithModes)(_react.default.Children.toArray(middleSlot), modes) : null;
|
|
115
|
-
|
|
116
|
-
// Actions Gap wrapper
|
|
117
|
-
// The Figma has "Actions" slot with a gap.
|
|
118
159
|
const actionsStyle = {
|
|
119
160
|
flexDirection: 'row',
|
|
120
161
|
alignItems: 'center',
|
|
121
162
|
gap: actionsGap ?? 0
|
|
122
163
|
};
|
|
123
|
-
const processedActions = actionsSlot ?
|
|
124
|
-
style: actionsStyle,
|
|
125
|
-
children: (0, _reactUtils.cloneChildrenWithModes)(_react.default.Children.toArray(actionsSlot), modes)
|
|
126
|
-
}) : null;
|
|
164
|
+
const processedActions = actionsSlot ? (0, _reactUtils.cloneChildrenWithModes)(_react.default.Children.toArray(actionsSlot), modes) : null;
|
|
127
165
|
|
|
128
|
-
// SubPage
|
|
129
|
-
// "slot wrap"), so it never participates in the row flow. Only MainPage
|
|
130
|
-
// keeps the legacy in-flow middle slot.
|
|
166
|
+
// SubPage middle is absolute (Figma "slot wrap"); MainPage middle stays in-flow.
|
|
131
167
|
const hasInFlowMiddle = isMain && !!processedMiddle;
|
|
132
168
|
|
|
133
|
-
// With an in-flow middle
|
|
134
|
-
//
|
|
135
|
-
// other cases we pin leading & actions to the outer edges with
|
|
136
|
-
// `space-between`; the SubPage middle floats above, centered.
|
|
169
|
+
// With an in-flow middle the flex:1 middle absorbs leftover space. Otherwise
|
|
170
|
+
// pin leading & actions to the edges; SubPage middle floats centered above.
|
|
137
171
|
const wrapperStyle = {
|
|
138
172
|
...containerStyle,
|
|
139
173
|
justifyContent: hasInFlowMiddle ? 'flex-start' : 'space-between'
|
|
140
174
|
};
|
|
141
175
|
|
|
142
|
-
//
|
|
143
|
-
//
|
|
144
|
-
// Android resolves the percentage against an intermediate, content-driven
|
|
145
|
-
// parent height and lands a few px too high), the overlay STRETCHES to fill
|
|
146
|
-
// the whole bar (`top/bottom/left/right: 0`) and centers its content with
|
|
147
|
-
// flexbox. This uses the exact same full-height vertical reference as the
|
|
148
|
-
// leading/actions row (`alignItems: 'center'`), so the middle content always
|
|
149
|
-
// sits on the same line as the back arrow and end slot on every platform.
|
|
176
|
+
// Overlay stretches to the full bar and centers content with flexbox so the
|
|
177
|
+
// middle aligns with leading/actions on every platform.
|
|
150
178
|
const subPageMiddleOverlayStyle = {
|
|
151
179
|
position: 'absolute',
|
|
152
180
|
top: 0,
|
|
@@ -157,10 +185,6 @@ function AppBar({
|
|
|
157
185
|
alignItems: 'center',
|
|
158
186
|
justifyContent: 'center'
|
|
159
187
|
};
|
|
160
|
-
|
|
161
|
-
// Fixed-width clipping box (mirrors the Figma "slot wrap" geometry): keeps the
|
|
162
|
-
// middle content from bleeding under the leading/actions slots while staying
|
|
163
|
-
// centered within the overlay above.
|
|
164
188
|
const subPageMiddleBoxStyle = {
|
|
165
189
|
width: middleSlotWidth,
|
|
166
190
|
height: SUBPAGE_MIDDLE_HEIGHT,
|
|
@@ -184,7 +208,7 @@ function AppBar({
|
|
|
184
208
|
alignItems: 'center'
|
|
185
209
|
},
|
|
186
210
|
children: processedLeading
|
|
187
|
-
}), hasInFlowMiddle
|
|
211
|
+
}), hasInFlowMiddle ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
188
212
|
style: {
|
|
189
213
|
flex: 1,
|
|
190
214
|
minWidth: 0,
|
|
@@ -194,10 +218,10 @@ function AppBar({
|
|
|
194
218
|
},
|
|
195
219
|
pointerEvents: "box-none",
|
|
196
220
|
children: processedMiddle
|
|
197
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
221
|
+
}) : null, /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
198
222
|
style: actionsStyle,
|
|
199
223
|
children: processedActions
|
|
200
|
-
}), isSub && processedMiddle
|
|
224
|
+
}), isSub && processedMiddle ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
201
225
|
style: subPageMiddleOverlayStyle,
|
|
202
226
|
pointerEvents: "box-none",
|
|
203
227
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
@@ -216,6 +240,7 @@ function AppBar({
|
|
|
216
240
|
children: processedMiddle
|
|
217
241
|
})
|
|
218
242
|
})
|
|
219
|
-
})]
|
|
243
|
+
}) : null]
|
|
220
244
|
});
|
|
221
|
-
}
|
|
245
|
+
}
|
|
246
|
+
var _default = exports.default = AppBar;
|