@namiml/expo-sdk 3.4.4-dev.202607022250 → 3.4.4-dev.202607071644

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@namiml/expo-sdk",
3
- "version": "3.4.4-dev.202607022250",
3
+ "version": "3.4.4-dev.202607071644",
4
4
  "type": "module",
5
5
  "description": "Nami Expo SDK — paywall and subscription management for Expo apps",
6
6
  "main": "./dist/index.cjs",
@@ -29,8 +29,8 @@
29
29
  "prepublishOnly": "yarn clean && yarn build:prod"
30
30
  },
31
31
  "dependencies": {
32
- "@namiml/expo-nami-iap": "3.4.4-dev.202607022250",
33
- "@namiml/sdk-core": "3.4.4-dev.202607022250",
32
+ "@namiml/expo-nami-iap": "3.4.4-dev.202607071644",
33
+ "@namiml/sdk-core": "3.4.4-dev.202607071644",
34
34
  "react-native-qrcode-svg": "^6.3.21",
35
35
  "react-native-safe-area-context": "^5.6.0",
36
36
  "react-native-svg": "^15.15.4"
@@ -1,7 +1,8 @@
1
- import React, { useMemo } from 'react';
1
+ import React, { useMemo, useState } from 'react';
2
2
  import { ScrollView, StyleSheet, View } from 'react-native';
3
- import { applyStyles, parseSize } from '../../utils/styles';
3
+ import { applyStyles, focusedStyleOverrides, parseSize } from '../../utils/styles';
4
4
  import { NamiSegmentPickerItem } from './NamiSegmentPickerItem';
5
+ import { FocusScope, useFocusEnabled } from '../../context/FocusContext';
5
6
 
6
7
  interface Props {
7
8
  component: any;
@@ -16,17 +17,28 @@ export const NamiSegmentPicker: React.FC<Props> = ({
16
17
  onClose,
17
18
  parentDirection,
18
19
  }) => {
19
- const containerStyle = useMemo(
20
- () => applyStyles(component, scaleFactor, false, parentDirection),
21
- [component, scaleFactor, parentDirection]
22
- );
20
+ const focusEnabled = useFocusEnabled();
21
+
22
+ // NAM-1213 CTV: track whether any segment item currently holds remote focus so the
23
+ // whole container can apply its focused visual treatment (focusedFillColor etc.).
24
+ const [isContainerFocused, setIsContainerFocused] = useState(false);
25
+
26
+ const containerStyle = useMemo(() => {
27
+ const base = applyStyles(component, scaleFactor, false, parentDirection);
28
+ // Container focused treatment: apply focusedFillColor/focusedBorder* with fallback
29
+ // to non-focused equivalents (per design doc §2 property-fallback contract).
30
+ if (focusEnabled && isContainerFocused) {
31
+ Object.assign(base, focusedStyleOverrides(component, scaleFactor));
32
+ }
33
+ return base;
34
+ }, [component, scaleFactor, parentDirection, focusEnabled, isContainerFocused]);
23
35
 
24
36
  const direction = component.direction ?? 'horizontal';
25
37
  const parentFormId = component.formId ?? component.id ?? '';
26
38
  const segments = component.components ?? [];
27
39
  const spacing = parseSize(component.spacing, scaleFactor) ?? 0;
28
40
 
29
- return (
41
+ const inner = (
30
42
  <View style={containerStyle}>
31
43
  <ScrollView
32
44
  horizontal={direction !== 'vertical'}
@@ -55,6 +67,18 @@ export const NamiSegmentPicker: React.FC<Props> = ({
55
67
  </ScrollView>
56
68
  </View>
57
69
  );
70
+
71
+ // On television form factors, wrap in a FocusScope so we can detect when any child
72
+ // segment item gains remote focus and apply the container's focused visual state.
73
+ if (focusEnabled) {
74
+ return (
75
+ <FocusScope onFocusWithinChange={setIsContainerFocused}>
76
+ {inner}
77
+ </FocusScope>
78
+ );
79
+ }
80
+
81
+ return inner;
58
82
  };
59
83
 
60
84
  const styles = StyleSheet.create({
@@ -1,5 +1,5 @@
1
1
  import React, { useEffect, useMemo, useRef } from 'react';
2
- import { Pressable, StyleSheet, Text, View } from 'react-native';
2
+ import { Platform, Pressable, StyleSheet, Text, View } from 'react-native';
3
3
  import { useFirstFocusReadyContext, usePaywallContext } from '../../context/PaywallContext';
4
4
  import {
5
5
  applyStyles,
@@ -95,6 +95,15 @@ export const NamiSegmentPickerItem: React.FC<Props> = ({
95
95
  return base;
96
96
  }, [merged, scaleFactor, parentDirection, isFocused]);
97
97
 
98
+ // NAM-1213 CTV: focused fill overlay. When the remote is on this item, draw an
99
+ // absolute-fill View with focusedFillColor over the active/inactive background —
100
+ // same full-cell geometry as the active fill (design doc §6b fill-rule).
101
+ // Item border when focused defaults to NONE if focusedBorder* are not authored (§2).
102
+ const focusedOverlayColor = useMemo(() => {
103
+ if (!focusEnabled || !isFocused) return null;
104
+ return parseColor(component.focusedFillColor) ?? null;
105
+ }, [focusEnabled, isFocused, component.focusedFillColor]);
106
+
98
107
  const onPress = () => {
99
108
  if (component.formId && parentFormId) {
100
109
  ctx.setFormState(parentFormId, component.formId);
@@ -143,8 +152,19 @@ export const NamiSegmentPickerItem: React.FC<Props> = ({
143
152
  onFocus={handleFocus}
144
153
  onBlur={onBlur}
145
154
  hasTVPreferredFocus={focusEnabled && Boolean(component.focused || component.checked)}
155
+ // Suppress platform default focus ripple/highlight so only our focused overlay shows.
156
+ android_ripple={Platform.isTV ? null : undefined}
146
157
  style={({ pressed }) => [styles.item, itemStyle, pressed ? styles.pressed : null]}
147
158
  >
159
+ {/* NAM-1213 CTV: focused fill overlay — drawn over the active/inactive background,
160
+ under the text/children, spanning the full cell (same geometry as active fill).
161
+ Only shown when the remote is on this item and focusedFillColor is authored. */}
162
+ {focusedOverlayColor != null && (
163
+ <View
164
+ style={[StyleSheet.absoluteFillObject, { backgroundColor: focusedOverlayColor }]}
165
+ pointerEvents="none"
166
+ />
167
+ )}
148
168
  <FocusedStyleProvider focused={isFocused || isActive}>
149
169
  {component.components?.length ? (
150
170
  component.components.map((child: TComponent, i: number) => (
@@ -163,10 +183,21 @@ export const NamiSegmentPickerItem: React.FC<Props> = ({
163
183
  ) : (
164
184
  <Text
165
185
  style={[
166
- textStyles(merged, scaleFactor, isFocused || isActive),
167
- isActive && {
186
+ // Base text style uses active/inactive merged props; isFocused=false here
187
+ // so we can apply focused font color separately with correct precedence.
188
+ textStyles(merged, scaleFactor, false),
189
+ // Active font color — overrides base when isActive and not focused.
190
+ !isFocused && isActive && {
168
191
  color: parseColor(activeStyles.fontColor ?? activeStyles.textColor ?? component.selectedFontColor) ?? '#fff',
169
192
  },
193
+ // NAM-1213 CTV: focused font color wins over active/inactive (design doc §2).
194
+ focusEnabled && isFocused && {
195
+ color: parseColor(component.focusedFontColor)
196
+ ?? parseColor(isActive
197
+ ? (activeStyles.fontColor ?? activeStyles.textColor ?? component.selectedFontColor)
198
+ : (inactiveStyles.fontColor ?? inactiveStyles.textColor))
199
+ ?? undefined,
200
+ },
170
201
  ]}
171
202
  allowFontScaling={false}
172
203
  >