expo-tvos-search 1.2.3 → 1.3.0

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/src/index.tsx CHANGED
@@ -1,5 +1,6 @@
1
1
  import React from "react";
2
- import { ViewStyle, Platform } from "react-native";
2
+ import type { ViewStyle } from "react-native";
3
+ import { Platform } from "react-native";
3
4
 
4
5
  /**
5
6
  * Event payload for search text changes.
@@ -23,6 +24,45 @@ export interface SelectItemEvent {
23
24
  };
24
25
  }
25
26
 
27
+ /**
28
+ * Categories of errors that can occur in the search view.
29
+ */
30
+ export type SearchViewErrorCategory =
31
+ | "module_unavailable"
32
+ | "validation_failed"
33
+ | "image_load_failed"
34
+ | "unknown";
35
+
36
+ /**
37
+ * Event payload for error callbacks.
38
+ * Provides details about errors that occur during search view operations.
39
+ */
40
+ export interface SearchViewErrorEvent {
41
+ nativeEvent: {
42
+ /** Category of the error for programmatic handling */
43
+ category: SearchViewErrorCategory;
44
+ /** Human-readable error message */
45
+ message: string;
46
+ /** Optional additional context (e.g., result ID, URL) */
47
+ context?: string;
48
+ };
49
+ }
50
+
51
+ /**
52
+ * Event payload for validation warnings.
53
+ * Non-fatal issues like truncated fields or clamped values.
54
+ */
55
+ export interface ValidationWarningEvent {
56
+ nativeEvent: {
57
+ /** Type of validation warning */
58
+ type: "field_truncated" | "value_clamped" | "url_invalid" | "validation_failed";
59
+ /** Human-readable warning message */
60
+ message: string;
61
+ /** Optional additional context */
62
+ context?: string;
63
+ };
64
+ }
65
+
26
66
  /**
27
67
  * Represents a single search result displayed in the grid.
28
68
  */
@@ -75,7 +115,7 @@ export interface TvosSearchViewProps {
75
115
 
76
116
  /**
77
117
  * Placeholder text shown in the search field when empty.
78
- * @default "Search..."
118
+ * @default "Search movies and videos..."
79
119
  */
80
120
  placeholder?: string;
81
121
 
@@ -160,6 +200,69 @@ export interface TvosSearchViewProps {
160
200
  */
161
201
  noResultsHintText?: string;
162
202
 
203
+ /**
204
+ * Color for text and UI elements in the search interface.
205
+ * Hex color string (e.g., "#FFFFFF", "#E5E5E5").
206
+ * @default Uses system default based on userInterfaceStyle
207
+ * @example "#E5E5E5" for light gray text on dark background
208
+ */
209
+ textColor?: string;
210
+
211
+ /**
212
+ * Accent color for focused elements and highlights.
213
+ * Hex color string (e.g., "#FFC312").
214
+ * @default "#FFC312" (gold)
215
+ * @example "#E50914" for Netflix red
216
+ */
217
+ accentColor?: string;
218
+
219
+ /**
220
+ * Width of each result card in points.
221
+ * Allows customization for portrait, landscape, or square layouts.
222
+ * @default 280
223
+ * @example 420 for landscape cards
224
+ */
225
+ cardWidth?: number;
226
+
227
+ /**
228
+ * Height of each result card in points.
229
+ * Allows customization for portrait, landscape, or square layouts.
230
+ * @default 420
231
+ * @example 240 for landscape cards (16:9 ratio with width=420)
232
+ */
233
+ cardHeight?: number;
234
+
235
+ /**
236
+ * How the image fills the card area.
237
+ * - 'fill': Image fills entire card, may crop (default)
238
+ * - 'fit': Image fits within card, may show letterboxing
239
+ * - 'contain': Same as fit (alias for consistency)
240
+ * @default "fill"
241
+ */
242
+ imageContentMode?: 'fill' | 'fit' | 'contain';
243
+
244
+ /**
245
+ * Spacing between cards in the grid layout (both horizontal and vertical).
246
+ * @default 40
247
+ * @example 60 for spacious layouts, 20 for compact grids
248
+ */
249
+ cardMargin?: number;
250
+
251
+ /**
252
+ * Padding inside the card for overlay content (title, subtitle).
253
+ * @default 16
254
+ * @example 20 for more breathing room, 12 for compact cards
255
+ */
256
+ cardPadding?: number;
257
+
258
+ /**
259
+ * Font size for title in the blur overlay (when showTitleOverlay is true).
260
+ * Allows customization of overlay text size for different card layouts.
261
+ * @default 20
262
+ * @example 18 for smaller cards, 24 for larger cards
263
+ */
264
+ overlayTitleSize?: number;
265
+
163
266
  /**
164
267
  * Callback fired when the search text changes.
165
268
  * Debounce this handler to avoid excessive API calls.
@@ -172,16 +275,42 @@ export interface TvosSearchViewProps {
172
275
  */
173
276
  onSelectItem: (event: SelectItemEvent) => void;
174
277
 
278
+ /**
279
+ * Optional callback fired when errors occur.
280
+ * Use this to monitor and log issues in production.
281
+ * @example
282
+ * ```tsx
283
+ * onError={(e) => {
284
+ * const { category, message, context } = e.nativeEvent;
285
+ * logger.error(`Search error [${category}]: ${message}`, { context });
286
+ * }}
287
+ * ```
288
+ */
289
+ onError?: (event: SearchViewErrorEvent) => void;
290
+
291
+ /**
292
+ * Optional callback fired for non-fatal validation warnings.
293
+ * Examples: truncated fields, clamped values, invalid URLs.
294
+ * @example
295
+ * ```tsx
296
+ * onValidationWarning={(e) => {
297
+ * const { type, message } = e.nativeEvent;
298
+ * console.warn(`Validation warning [${type}]: ${message}`);
299
+ * }}
300
+ * ```
301
+ */
302
+ onValidationWarning?: (event: ValidationWarningEvent) => void;
303
+
175
304
  /**
176
305
  * Optional style for the view container.
177
306
  */
178
307
  style?: ViewStyle;
179
308
  }
180
309
 
181
- // Safely try to load the native view - it may not be available if:
182
- // 1. Running on a non-tvOS platform
183
- // 2. Native module hasn't been built yet (needs expo prebuild)
184
- // 3. expo-modules-core isn't properly installed
310
+ /**
311
+ * Native view component loaded at module initialization.
312
+ * Returns null on non-tvOS platforms or when the native module is unavailable.
313
+ */
185
314
  let NativeView: React.ComponentType<TvosSearchViewProps> | null = null;
186
315
 
187
316
  if (Platform.OS === "ios" && Platform.isTV) {
@@ -189,9 +318,45 @@ if (Platform.OS === "ios" && Platform.isTV) {
189
318
  const { requireNativeViewManager } = require("expo-modules-core");
190
319
  if (typeof requireNativeViewManager === "function") {
191
320
  NativeView = requireNativeViewManager("ExpoTvosSearch");
321
+ } else {
322
+ console.warn(
323
+ "[expo-tvos-search] requireNativeViewManager is not a function. " +
324
+ "This usually indicates an incompatible expo-modules-core version. " +
325
+ "Try reinstalling expo-modules-core or updating to a compatible version."
326
+ );
327
+ }
328
+ } catch (error) {
329
+ // Categorize the error to help with debugging
330
+ const errorMessage = error instanceof Error ? error.message : String(error);
331
+
332
+ if (errorMessage.includes("expo-modules-core")) {
333
+ console.warn(
334
+ "[expo-tvos-search] Failed to load expo-modules-core. " +
335
+ "Make sure expo-modules-core is installed: npm install expo-modules-core\n" +
336
+ `Error: ${errorMessage}`
337
+ );
338
+ } else if (errorMessage.includes("ExpoTvosSearch")) {
339
+ console.warn(
340
+ "[expo-tvos-search] Native module ExpoTvosSearch not found. " +
341
+ "This usually means:\n" +
342
+ "1. You haven't run 'expo prebuild' yet, or\n" +
343
+ "2. The native project needs to be rebuilt (try 'expo prebuild --clean')\n" +
344
+ "3. You're not running on a tvOS simulator/device\n" +
345
+ `Error: ${errorMessage}`
346
+ );
347
+ } else {
348
+ // Unexpected error - log full details for debugging
349
+ console.warn(
350
+ "[expo-tvos-search] Unexpected error loading native module.\n" +
351
+ `Error: ${errorMessage}\n` +
352
+ "Please report this issue at: https://github.com/keiver/expo-tvos-search/issues"
353
+ );
354
+
355
+ // In development, log the full error for debugging
356
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
357
+ console.error("[expo-tvos-search] Full error details:", error);
358
+ }
192
359
  }
193
- } catch {
194
- // Native module not available - will fall back to React Native implementation
195
360
  }
196
361
  }
197
362
 
@@ -228,8 +393,31 @@ if (Platform.OS === "ios" && Platform.isTV) {
228
393
  * @param props - Component props
229
394
  * @returns The native search view on tvOS, or `null` if unavailable
230
395
  */
231
- export function TvosSearchView(props: TvosSearchViewProps) {
396
+ export function TvosSearchView(props: TvosSearchViewProps): JSX.Element | null {
232
397
  if (!NativeView) {
398
+ // Warn in development when native module is unavailable
399
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
400
+ const isRunningOnTvOS = Platform.OS === "ios" && Platform.isTV;
401
+
402
+ if (isRunningOnTvOS) {
403
+ // On tvOS but module failed to load - this is unexpected
404
+ console.warn(
405
+ "[expo-tvos-search] TvosSearchView is rendering null on tvOS. " +
406
+ "This usually means:\n" +
407
+ "1. The native module wasn't built properly (try 'expo prebuild --clean')\n" +
408
+ "2. expo-modules-core is missing or incompatible\n" +
409
+ "3. The app needs to be restarted after installing the module\n\n" +
410
+ "Check the earlier console logs for specific error details."
411
+ );
412
+ } else {
413
+ // Not on tvOS - expected behavior, but developer might want to know
414
+ console.info(
415
+ "[expo-tvos-search] TvosSearchView is not available on " +
416
+ `${Platform.OS}${Platform.isTV ? " (TV)" : ""}. ` +
417
+ "Use isNativeSearchAvailable() to check before rendering this component."
418
+ );
419
+ }
420
+ }
233
421
  return null;
234
422
  }
235
423
  return <NativeView {...props} />;