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.
Files changed (49) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/D2C.md +82 -87
  3. package/lib/commonjs/components/Accordion/Accordion.js +16 -3
  4. package/lib/commonjs/components/AppBar/AppBar.js +118 -93
  5. package/lib/commonjs/components/CompareTable/CompareTable.js +98 -33
  6. package/lib/commonjs/components/FormField/FormField.js +9 -1
  7. package/lib/commonjs/components/FullscreenModal/FullscreenModal.js +4 -14
  8. package/lib/commonjs/components/HelloJioInput/HelloJioInput.js +20 -4
  9. package/lib/commonjs/components/PageHero/PageHero.js +3 -0
  10. package/lib/commonjs/components/ProductMerchandisingCard/ProductMerchandisingCard.js +50 -30
  11. package/lib/commonjs/components/ValueBackMetric/ValueBackMetric.js +117 -89
  12. package/lib/commonjs/components/index.js +7 -1
  13. package/lib/commonjs/design-tokens/Coin Variables-variables-full.json +1 -42204
  14. package/lib/commonjs/icons/registry.js +1 -1
  15. package/lib/module/components/Accordion/Accordion.js +16 -3
  16. package/lib/module/components/AppBar/AppBar.js +116 -93
  17. package/lib/module/components/CompareTable/CompareTable.js +99 -34
  18. package/lib/module/components/FormField/FormField.js +9 -1
  19. package/lib/module/components/FullscreenModal/FullscreenModal.js +4 -14
  20. package/lib/module/components/HelloJioInput/HelloJioInput.js +20 -4
  21. package/lib/module/components/PageHero/PageHero.js +3 -0
  22. package/lib/module/components/ProductMerchandisingCard/ProductMerchandisingCard.js +50 -30
  23. package/lib/module/components/ValueBackMetric/ValueBackMetric.js +120 -93
  24. package/lib/module/components/index.js +1 -1
  25. package/lib/module/design-tokens/Coin Variables-variables-full.json +1 -42204
  26. package/lib/module/icons/registry.js +1 -1
  27. package/lib/typescript/src/components/Accordion/Accordion.d.ts +14 -1
  28. package/lib/typescript/src/components/AppBar/AppBar.d.ts +46 -14
  29. package/lib/typescript/src/components/CompareTable/CompareTable.d.ts +29 -12
  30. package/lib/typescript/src/components/HelloJioInput/HelloJioInput.d.ts +6 -4
  31. package/lib/typescript/src/components/PageHero/PageHero.d.ts +1 -0
  32. package/lib/typescript/src/components/ProductMerchandisingCard/ProductMerchandisingCard.d.ts +8 -1
  33. package/lib/typescript/src/components/ValueBackMetric/ValueBackMetric.d.ts +27 -16
  34. package/lib/typescript/src/components/index.d.ts +1 -1
  35. package/lib/typescript/src/icons/registry.d.ts +1 -1
  36. package/package.json +3 -1
  37. package/scripts/extract-figma-modes.js +150 -121
  38. package/src/components/Accordion/Accordion.tsx +61 -33
  39. package/src/components/AppBar/AppBar.tsx +154 -124
  40. package/src/components/CompareTable/CompareTable.tsx +138 -52
  41. package/src/components/FormField/FormField.tsx +7 -0
  42. package/src/components/FullscreenModal/FullscreenModal.tsx +5 -14
  43. package/src/components/HelloJioInput/HelloJioInput.tsx +31 -12
  44. package/src/components/PageHero/PageHero.tsx +5 -0
  45. package/src/components/ProductMerchandisingCard/ProductMerchandisingCard.tsx +63 -30
  46. package/src/components/ValueBackMetric/ValueBackMetric.tsx +178 -104
  47. package/src/components/index.ts +6 -1
  48. package/src/design-tokens/Coin Variables-variables-full.json +1 -42204
  49. 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 (jfs-components)
1
+ # D2C — Design to Code with jfs-components
2
2
 
3
- You are implementing a screen from a Figma design using the `jfs-components`
4
- library. The user has pasted a Figma link. Follow this workflow exactly, in
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
- ## Step 1 — Pull the design through the Figma MCP
6
+ ## Input
8
7
 
9
- Extract `fileKey` and `nodeId` from the pasted URL
10
- (`https://www.figma.com/design/<fileKey>/...?node-id=<a>-<b>` nodeId is
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. Call `get_design_context` for the node. This gives you the component
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
- The Figma MCP gives you almost everything — except one thing: the **variable
19
- modes explicitly set on each layer**. Those are invisible to the MCP and they
20
- change how components render (size, emphasis, appearance, color mode, etc.).
21
- That is what Step 2 is for.
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
- ## Step 2 Extract the modes (mandatory, never skip)
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
- Run the mode extractor that ships with this package. It needs a Figma personal
26
- access token with plain file-read access (a view-only / Dev seat token works;
27
- no Enterprise scopes, no edit access):
23
+ ## 2. Extract and resolve explicit modes
24
+
25
+ From the consuming project's root, run:
28
26
 
29
27
  ```bash
30
- export FIGMA_ACCESS_TOKEN=figd_...
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
- If `npx` cannot find the bin (e.g. unusual monorepo setup), call the script by
35
- its path inside the installed package:
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
- ```bash
38
- node node_modules/jfs-components/scripts/extract-figma-modes.js "<figma-url>" --out modes.json
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
- The output is a flat list one entry per layer that has explicit modes:
45
+ If the bin is unavailable in a nonstandard monorepo, run:
42
46
 
43
- ```json
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
- Rules for using it:
51
+ Resolve the effective modes for each represented component as follows:
54
52
 
55
- - Children inherit ancestor modes unless they set their own. To get the
56
- effective modes of any layer, merge every entry whose `path` is a prefix of
57
- that layer's path, top-down.
58
- - Correlate entries with the Figma MCP structure by layer name and position;
59
- use `nodeId` when two layers share a name.
60
- - If the output contains an `unresolved` array, STOP and report it to the user
61
- before building. It means the packaged variables JSON is out of date and the
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
- ## Step 3 Build the screen
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
- Implement with `jfs-components` components, mapping Figma component names to
67
- library exports.
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
- **Modes are non-negotiable.** Apply every extracted mode exactly as given —
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
- - Do not substitute a "close enough" mode (`Emphasis: Medium` is not
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
- For everything else (spacing, order, text, icons, variants, props), follow the
81
- Figma MCP output one-to-one. Reuse existing screens in the consuming app as
82
- convention references for file placement, navigation registration, and asset
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
- ## Step 4 QA against the design (mandatory, never skip)
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
- After the screen compiles and renders, you MUST verify it visually against the
88
- Step 1 screenshot:
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
- - **Native app:** use the mobile MCP — launch the app, navigate to the new
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
- Compare your screenshot with the Figma screenshot side by side. Check:
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. Component sizes and emphasis levels (these are where wrong modes show up)
98
- 2. Colors / appearance (brand vs neutral vs secondary treatments)
99
- 3. Spacing, padding, and gaps
100
- 4. Text content and truncation
101
- 5. Icon choices and visibility (some layers in the design are hidden)
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
- If there is ANY visible difference, fix it and re-screenshot. Repeat until the
104
- implementation matches the design. Typical root causes, in order of
105
- likelihood: a mode applied to the wrong layer, a missing mode (falling back to
106
- the collection default), a wrong variant, a wrong icon.
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
- ## Summary contract
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
- | Step | Tool | Skippable? |
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 back to the user with: the screen file(s) created, the modes applied per
118
- layer, and the final QA screenshot comparison.
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.default = AppBar;
7
- var _react = _interopRequireDefault(require("react"));
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
- // SubPage "slot wrap" geometry, taken directly from the Figma design
16
- // (node 449:7876). The middle slot is an absolutely-centered box of a fixed
17
- // size; its inner content (node 3991:4125) is a `flex: 1 0 0; min-width: 1px`
18
- // item so it fills / shrinks responsively within that box.
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
- // --- Leading Slot Default ---
70
- // SubPage default: Back Arrow
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
- if (isSub) {
81
- defaultLeading = /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
82
- ...(onLeadingPress ? {
83
- onPress: onLeadingPress
84
- } : {}),
85
- hitSlop: 8,
86
- accessibilityRole: "button",
87
- accessibilityLabel: undefined,
88
- style: ({
89
- pressed
90
- }) => ({
91
- opacity: pressed ? 0.7 : 1,
92
- alignItems: 'center',
93
- justifyContent: 'center'
94
- }),
95
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_NavArrow.default, {
96
- direction: "Back",
97
- modes: modes
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 ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
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 centers its middle slot via absolute positioning (see Figma
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 (MainPage) the middle (flex: 1) absorbs the
134
- // remaining space, so leading & actions sit at the edges naturally. In all
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
- // Absolutely-positioned overlay for the SubPage middle slot. Instead of
143
- // centering a fixed-height box with `top/left: 50%` + transform (which on
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 && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
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 && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
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;