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/README.md +493 -50
- package/build/index.d.ts +115 -4
- package/build/index.d.ts.map +1 -1
- package/build/index.js +54 -6
- package/ios/ExpoTvosSearchModule.swift +67 -4
- package/ios/ExpoTvosSearchView.swift +325 -61
- package/ios/MarqueeText.swift +2 -2
- package/package.json +9 -1
- package/src/__tests__/index.test.tsx +456 -1
- package/src/index.tsx +197 -9
package/src/index.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import { ViewStyle
|
|
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
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
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} />;
|