expo-tvos-search 1.2.3 → 1.3.1

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/build/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import React from "react";
2
- import { ViewStyle } from "react-native";
1
+ /// <reference types="react" />
2
+ import type { ViewStyle } from "react-native";
3
3
  /**
4
4
  * Event payload for search text changes.
5
5
  * Fired when the user types in the native search field.
@@ -20,6 +20,38 @@ export interface SelectItemEvent {
20
20
  id: string;
21
21
  };
22
22
  }
23
+ /**
24
+ * Categories of errors that can occur in the search view.
25
+ */
26
+ export type SearchViewErrorCategory = "module_unavailable" | "validation_failed" | "image_load_failed" | "unknown";
27
+ /**
28
+ * Event payload for error callbacks.
29
+ * Provides details about errors that occur during search view operations.
30
+ */
31
+ export interface SearchViewErrorEvent {
32
+ nativeEvent: {
33
+ /** Category of the error for programmatic handling */
34
+ category: SearchViewErrorCategory;
35
+ /** Human-readable error message */
36
+ message: string;
37
+ /** Optional additional context (e.g., result ID, URL) */
38
+ context?: string;
39
+ };
40
+ }
41
+ /**
42
+ * Event payload for validation warnings.
43
+ * Non-fatal issues like truncated fields or clamped values.
44
+ */
45
+ export interface ValidationWarningEvent {
46
+ nativeEvent: {
47
+ /** Type of validation warning */
48
+ type: "field_truncated" | "value_clamped" | "url_invalid" | "validation_failed";
49
+ /** Human-readable warning message */
50
+ message: string;
51
+ /** Optional additional context */
52
+ context?: string;
53
+ };
54
+ }
23
55
  /**
24
56
  * Represents a single search result displayed in the grid.
25
57
  */
@@ -69,7 +101,7 @@ export interface TvosSearchViewProps {
69
101
  columns?: number;
70
102
  /**
71
103
  * Placeholder text shown in the search field when empty.
72
- * @default "Search..."
104
+ * @default "Search movies and videos..."
73
105
  */
74
106
  placeholder?: string;
75
107
  /**
@@ -141,6 +173,61 @@ export interface TvosSearchViewProps {
141
173
  * @default "Try a different search term"
142
174
  */
143
175
  noResultsHintText?: string;
176
+ /**
177
+ * Color for text and UI elements in the search interface.
178
+ * Hex color string (e.g., "#FFFFFF", "#E5E5E5").
179
+ * @default Uses system default based on userInterfaceStyle
180
+ * @example "#E5E5E5" for light gray text on dark background
181
+ */
182
+ textColor?: string;
183
+ /**
184
+ * Accent color for focused elements and highlights.
185
+ * Hex color string (e.g., "#FFC312").
186
+ * @default "#FFC312" (gold)
187
+ * @example "#E50914" for Netflix red
188
+ */
189
+ accentColor?: string;
190
+ /**
191
+ * Width of each result card in points.
192
+ * Allows customization for portrait, landscape, or square layouts.
193
+ * @default 280
194
+ * @example 420 for landscape cards
195
+ */
196
+ cardWidth?: number;
197
+ /**
198
+ * Height of each result card in points.
199
+ * Allows customization for portrait, landscape, or square layouts.
200
+ * @default 420
201
+ * @example 240 for landscape cards (16:9 ratio with width=420)
202
+ */
203
+ cardHeight?: number;
204
+ /**
205
+ * How the image fills the card area.
206
+ * - 'fill': Image fills entire card, may crop (default)
207
+ * - 'fit': Image fits within card, may show letterboxing
208
+ * - 'contain': Same as fit (alias for consistency)
209
+ * @default "fill"
210
+ */
211
+ imageContentMode?: 'fill' | 'fit' | 'contain';
212
+ /**
213
+ * Spacing between cards in the grid layout (both horizontal and vertical).
214
+ * @default 40
215
+ * @example 60 for spacious layouts, 20 for compact grids
216
+ */
217
+ cardMargin?: number;
218
+ /**
219
+ * Padding inside the card for overlay content (title, subtitle).
220
+ * @default 16
221
+ * @example 20 for more breathing room, 12 for compact cards
222
+ */
223
+ cardPadding?: number;
224
+ /**
225
+ * Font size for title in the blur overlay (when showTitleOverlay is true).
226
+ * Allows customization of overlay text size for different card layouts.
227
+ * @default 20
228
+ * @example 18 for smaller cards, 24 for larger cards
229
+ */
230
+ overlayTitleSize?: number;
144
231
  /**
145
232
  * Callback fired when the search text changes.
146
233
  * Debounce this handler to avoid excessive API calls.
@@ -151,6 +238,30 @@ export interface TvosSearchViewProps {
151
238
  * Use the `id` from the event to identify which result was selected.
152
239
  */
153
240
  onSelectItem: (event: SelectItemEvent) => void;
241
+ /**
242
+ * Optional callback fired when errors occur.
243
+ * Use this to monitor and log issues in production.
244
+ * @example
245
+ * ```tsx
246
+ * onError={(e) => {
247
+ * const { category, message, context } = e.nativeEvent;
248
+ * logger.error(`Search error [${category}]: ${message}`, { context });
249
+ * }}
250
+ * ```
251
+ */
252
+ onError?: (event: SearchViewErrorEvent) => void;
253
+ /**
254
+ * Optional callback fired for non-fatal validation warnings.
255
+ * Examples: truncated fields, clamped values, invalid URLs.
256
+ * @example
257
+ * ```tsx
258
+ * onValidationWarning={(e) => {
259
+ * const { type, message } = e.nativeEvent;
260
+ * console.warn(`Validation warning [${type}]: ${message}`);
261
+ * }}
262
+ * ```
263
+ */
264
+ onValidationWarning?: (event: ValidationWarningEvent) => void;
154
265
  /**
155
266
  * Optional style for the view container.
156
267
  */
@@ -189,7 +300,7 @@ export interface TvosSearchViewProps {
189
300
  * @param props - Component props
190
301
  * @returns The native search view on tvOS, or `null` if unavailable
191
302
  */
192
- export declare function TvosSearchView(props: TvosSearchViewProps): React.JSX.Element | null;
303
+ export declare function TvosSearchView(props: TvosSearchViewProps): JSX.Element | null;
193
304
  /**
194
305
  * Checks if the native tvOS search component is available.
195
306
  *
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,SAAS,EAAY,MAAM,cAAc,CAAC;AAEnD;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,WAAW,EAAE;QACX,0DAA0D;QAC1D,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;CACH;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE;QACX,0DAA0D;QAC1D,EAAE,EAAE,MAAM,CAAC;KACZ,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,uEAAuE;IACvE,EAAE,EAAE,MAAM,CAAC;IACX,0CAA0C;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,wDAAwD;IACxD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yDAAyD;IACzD,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;;;;OAMG;IACH,OAAO,EAAE,YAAY,EAAE,CAAC;IAExB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAE3B;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B;;;OAGG;IACH,QAAQ,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;IAEvC;;;OAGG;IACH,YAAY,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAC;IAE/C;;OAEG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC;CACnB;AAmBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,4BAKxD;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,uBAAuB,IAAI,OAAO,CAEjD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"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"}
package/build/index.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import React from "react";
2
2
  import { Platform } from "react-native";
3
- // Safely try to load the native view - it may not be available if:
4
- // 1. Running on a non-tvOS platform
5
- // 2. Native module hasn't been built yet (needs expo prebuild)
6
- // 3. expo-modules-core isn't properly installed
3
+ /**
4
+ * Native view component loaded at module initialization.
5
+ * Returns null on non-tvOS platforms or when the native module is unavailable.
6
+ */
7
7
  let NativeView = null;
8
8
  if (Platform.OS === "ios" && Platform.isTV) {
9
9
  try {
@@ -11,9 +11,38 @@ if (Platform.OS === "ios" && Platform.isTV) {
11
11
  if (typeof requireNativeViewManager === "function") {
12
12
  NativeView = requireNativeViewManager("ExpoTvosSearch");
13
13
  }
14
+ else {
15
+ console.warn("[expo-tvos-search] requireNativeViewManager is not a function. " +
16
+ "This usually indicates an incompatible expo-modules-core version. " +
17
+ "Try reinstalling expo-modules-core or updating to a compatible version.");
18
+ }
14
19
  }
15
- catch {
16
- // Native module not available - will fall back to React Native implementation
20
+ catch (error) {
21
+ // Categorize the error to help with debugging
22
+ const errorMessage = error instanceof Error ? error.message : String(error);
23
+ if (errorMessage.includes("expo-modules-core")) {
24
+ console.warn("[expo-tvos-search] Failed to load expo-modules-core. " +
25
+ "Make sure expo-modules-core is installed: npm install expo-modules-core\n" +
26
+ `Error: ${errorMessage}`);
27
+ }
28
+ else if (errorMessage.includes("ExpoTvosSearch")) {
29
+ console.warn("[expo-tvos-search] Native module ExpoTvosSearch not found. " +
30
+ "This usually means:\n" +
31
+ "1. You haven't run 'expo prebuild' yet, or\n" +
32
+ "2. The native project needs to be rebuilt (try 'expo prebuild --clean')\n" +
33
+ "3. You're not running on a tvOS simulator/device\n" +
34
+ `Error: ${errorMessage}`);
35
+ }
36
+ else {
37
+ // Unexpected error - log full details for debugging
38
+ console.warn("[expo-tvos-search] Unexpected error loading native module.\n" +
39
+ `Error: ${errorMessage}\n` +
40
+ "Please report this issue at: https://github.com/keiver/expo-tvos-search/issues");
41
+ // In development, log the full error for debugging
42
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
43
+ console.error("[expo-tvos-search] Full error details:", error);
44
+ }
45
+ }
17
46
  }
18
47
  }
19
48
  /**
@@ -51,6 +80,25 @@ if (Platform.OS === "ios" && Platform.isTV) {
51
80
  */
52
81
  export function TvosSearchView(props) {
53
82
  if (!NativeView) {
83
+ // Warn in development when native module is unavailable
84
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
85
+ const isRunningOnTvOS = Platform.OS === "ios" && Platform.isTV;
86
+ if (isRunningOnTvOS) {
87
+ // On tvOS but module failed to load - this is unexpected
88
+ console.warn("[expo-tvos-search] TvosSearchView is rendering null on tvOS. " +
89
+ "This usually means:\n" +
90
+ "1. The native module wasn't built properly (try 'expo prebuild --clean')\n" +
91
+ "2. expo-modules-core is missing or incompatible\n" +
92
+ "3. The app needs to be restarted after installing the module\n\n" +
93
+ "Check the earlier console logs for specific error details.");
94
+ }
95
+ else {
96
+ // Not on tvOS - expected behavior, but developer might want to know
97
+ console.info("[expo-tvos-search] TvosSearchView is not available on " +
98
+ `${Platform.OS}${Platform.isTV ? " (TV)" : ""}. ` +
99
+ "Use isNativeSearchAvailable() to check before rendering this component.");
100
+ }
101
+ }
54
102
  return null;
55
103
  }
56
104
  return React.createElement(NativeView, { ...props });
@@ -12,17 +12,32 @@ public class ExpoTvosSearchModule: Module {
12
12
  Name("ExpoTvosSearch")
13
13
 
14
14
  View(ExpoTvosSearchView.self) {
15
- Events("onSearch", "onSelectItem")
15
+ Events("onSearch", "onSelectItem", "onError", "onValidationWarning")
16
16
 
17
17
  Prop("results") { (view: ExpoTvosSearchView, results: [[String: Any]]) in
18
18
  // Limit results array size to prevent memory issues
19
19
  let limitedResults = Array(results.prefix(Self.maxResults))
20
+ if results.count > Self.maxResults {
21
+ view.onValidationWarning([
22
+ "type": "value_clamped",
23
+ "message": "Results array truncated from \(results.count) to \(Self.maxResults) items",
24
+ "context": "maxResults=\(Self.maxResults)"
25
+ ])
26
+ }
20
27
  view.updateResults(limitedResults)
21
28
  }
22
29
 
23
30
  Prop("columns") { (view: ExpoTvosSearchView, columns: Int) in
24
31
  // Clamp columns between min and max for safe grid layout
25
- view.columns = min(max(Self.minColumns, columns), Self.maxColumns)
32
+ let clampedValue = min(max(Self.minColumns, columns), Self.maxColumns)
33
+ if clampedValue != columns {
34
+ view.onValidationWarning([
35
+ "type": "value_clamped",
36
+ "message": "columns value \(columns) was clamped to range [\(Self.minColumns), \(Self.maxColumns)]",
37
+ "context": "columns=\(clampedValue)"
38
+ ])
39
+ }
40
+ view.columns = clampedValue
26
41
  }
27
42
 
28
43
  Prop("placeholder") { (view: ExpoTvosSearchView, placeholder: String) in
@@ -48,7 +63,15 @@ public class ExpoTvosSearchModule: Module {
48
63
 
49
64
  Prop("topInset") { (view: ExpoTvosSearchView, topInset: Double) in
50
65
  // Clamp to non-negative values (max 500 points reasonable for any screen)
51
- view.topInset = CGFloat(min(max(0, topInset), 500))
66
+ let clampedValue = min(max(0, topInset), 500)
67
+ if clampedValue != topInset {
68
+ view.onValidationWarning([
69
+ "type": "value_clamped",
70
+ "message": "topInset value \(topInset) was clamped to range [0, 500]",
71
+ "context": "topInset=\(clampedValue)"
72
+ ])
73
+ }
74
+ view.topInset = CGFloat(clampedValue)
52
75
  }
53
76
 
54
77
  Prop("showTitleOverlay") { (view: ExpoTvosSearchView, show: Bool) in
@@ -61,7 +84,15 @@ public class ExpoTvosSearchModule: Module {
61
84
 
62
85
  Prop("marqueeDelay") { (view: ExpoTvosSearchView, delay: Double) in
63
86
  // Clamp between 0 and maxMarqueeDelay seconds
64
- view.marqueeDelay = min(max(0, delay), Self.maxMarqueeDelay)
87
+ let clampedValue = min(max(0, delay), Self.maxMarqueeDelay)
88
+ if clampedValue != delay {
89
+ view.onValidationWarning([
90
+ "type": "value_clamped",
91
+ "message": "marqueeDelay value \(delay) was clamped to range [0, \(Self.maxMarqueeDelay)]",
92
+ "context": "marqueeDelay=\(clampedValue)"
93
+ ])
94
+ }
95
+ view.marqueeDelay = clampedValue
65
96
  }
66
97
 
67
98
  Prop("emptyStateText") { (view: ExpoTvosSearchView, text: String) in
@@ -79,6 +110,38 @@ public class ExpoTvosSearchModule: Module {
79
110
  Prop("noResultsHintText") { (view: ExpoTvosSearchView, text: String) in
80
111
  view.noResultsHintText = String(text.prefix(Self.maxStringLength))
81
112
  }
113
+
114
+ Prop("textColor") { (view: ExpoTvosSearchView, colorHex: String?) in
115
+ view.textColor = colorHex
116
+ }
117
+
118
+ Prop("accentColor") { (view: ExpoTvosSearchView, colorHex: String) in
119
+ view.accentColor = colorHex
120
+ }
121
+
122
+ Prop("cardWidth") { (view: ExpoTvosSearchView, width: Double) in
123
+ view.cardWidth = CGFloat(max(50, min(1000, width))) // Clamp to reasonable range
124
+ }
125
+
126
+ Prop("cardHeight") { (view: ExpoTvosSearchView, height: Double) in
127
+ view.cardHeight = CGFloat(max(50, min(1000, height))) // Clamp to reasonable range
128
+ }
129
+
130
+ Prop("imageContentMode") { (view: ExpoTvosSearchView, mode: String) in
131
+ view.imageContentMode = mode
132
+ }
133
+
134
+ Prop("cardMargin") { (view: ExpoTvosSearchView, margin: Double) in
135
+ view.cardMargin = CGFloat(max(0, min(200, margin))) // Clamp to reasonable range
136
+ }
137
+
138
+ Prop("cardPadding") { (view: ExpoTvosSearchView, padding: Double) in
139
+ view.cardPadding = CGFloat(max(0, min(100, padding))) // Clamp to reasonable range
140
+ }
141
+
142
+ Prop("overlayTitleSize") { (view: ExpoTvosSearchView, size: Double) in
143
+ view.overlayTitleSize = CGFloat(max(8, min(72, size))) // Clamp to reasonable font size range
144
+ }
82
145
  }
83
146
  }
84
147
  }