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/build/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
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):
|
|
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
|
*
|
package/build/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"
|
|
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
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
}
|