@applicaster/zapp-react-native-ui-components 16.0.0-alpha.1453660071 → 16.0.0-alpha.1454930193

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.
Files changed (124) hide show
  1. package/Components/AudioPlayer/tv/__tests__/audioPlayer.test.js +19 -0
  2. package/Components/AudioPlayer/tv/__tests__/index.test.tsx +120 -0
  3. package/Components/AudioPlayer/tv/index.tsx +5 -1
  4. package/Components/Cell/Cell.tsx +3 -6
  5. package/Components/Cell/TvOSCellComponent.tsx +0 -1
  6. package/Components/GeneralContentScreen/GeneralContentScreenHookAdapter.tsx +15 -0
  7. package/Components/GeneralContentScreen/hookAdapter/__tests__/skipExpression.test.ts +294 -0
  8. package/Components/GeneralContentScreen/hookAdapter/__tests__/validationHelper.test.ts +64 -0
  9. package/Components/GeneralContentScreen/hookAdapter/skipExpression.ts +252 -0
  10. package/Components/GeneralContentScreen/hookAdapter/validationHelper.ts +18 -0
  11. package/Components/LinkHandler/LinkHandler.tsx +2 -7
  12. package/Components/MasterCell/DefaultComponents/ActionButton.tsx +5 -1
  13. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/placement.test.ts +47 -21
  14. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/ActionButtonController.tsx +19 -4
  15. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/__tests__/ActionButtonController.test.tsx +43 -1
  16. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/placement.ts +34 -6
  17. package/Components/MasterCell/DefaultComponents/Button.tsx +1 -0
  18. package/Components/MasterCell/DefaultComponents/Image/hoc/utils/__tests__/withDimensions.test.ts +420 -125
  19. package/Components/MasterCell/DefaultComponents/Image/hoc/utils/index.ts +114 -19
  20. package/Components/MasterCell/DefaultComponents/Image/hoc/withDimensions.tsx +19 -8
  21. package/Components/MasterCell/DefaultComponents/ImageContainer/index.tsx +5 -3
  22. package/Components/MasterCell/DefaultComponents/LiveImage/executePreloadHooks.ts +2 -4
  23. package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +2 -3
  24. package/Components/MasterCell/DefaultComponents/PressableView.tsx +29 -9
  25. package/Components/MasterCell/DefaultComponents/Text/hooks/useText.ts +4 -0
  26. package/Components/MasterCell/DefaultComponents/Toggle.tsx +36 -14
  27. package/Components/MasterCell/DefaultComponents/__tests__/Toggle.test.tsx +167 -0
  28. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/ActionButton.tsx +154 -100
  29. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/AssetComponent.tsx +15 -4
  30. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Button.ts +56 -22
  31. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Spacer.ts +6 -4
  32. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/TextLabelsContainer.ts +3 -1
  33. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/PressableView.test.tsx +32 -21
  34. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/index.test.ts +15 -4
  35. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/index.ts +23 -16
  36. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/__tests__/insertButtons.test.ts +8 -3
  37. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/index.ts +2 -2
  38. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/__tests__/insertButtonsBetweenLabels.test.ts +45 -13
  39. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/index.ts +2 -2
  40. package/Components/MasterCell/SharedUI/CollapsibleTextContainer/__tests__/factory.test.ts +32 -0
  41. package/Components/MasterCell/SharedUI/CollapsibleTextContainer/index.ts +1 -2
  42. package/Components/MasterCell/contexts/PressedStateContext.ts +3 -0
  43. package/Components/MasterCell/hooks/index.ts +2 -0
  44. package/Components/MasterCell/hooks/usePressedState.ts +4 -0
  45. package/Components/ModalComponent/ActionsBottomSheet/ActionButton.tsx +56 -0
  46. package/Components/ModalComponent/ActionsBottomSheet/__tests__/openActionsBottomSheet.test.ts +38 -0
  47. package/Components/ModalComponent/ActionsBottomSheet/boundActionButton.tsx +24 -0
  48. package/Components/ModalComponent/ActionsBottomSheet/index.ts +9 -0
  49. package/Components/ModalComponent/ActionsBottomSheet/openActionsBottomSheet.ts +61 -0
  50. package/Components/ModalComponent/AudioPlayer/Components/Button.tsx +392 -0
  51. package/Components/ModalComponent/AudioPlayer/Components/Header.tsx +772 -0
  52. package/Components/ModalComponent/AudioPlayer/Components/Input.tsx +728 -0
  53. package/Components/ModalComponent/AudioPlayer/Components/Item.tsx +1007 -0
  54. package/Components/ModalComponent/AudioPlayer/Components/NowPlayingHeaderSection.tsx +205 -0
  55. package/Components/ModalComponent/AudioPlayer/Components/__tests__/Item.test.tsx +113 -0
  56. package/Components/ModalComponent/AudioPlayer/Components/__tests__/NowPlayingHeaderSection.test.tsx +36 -0
  57. package/Components/ModalComponent/AudioPlayer/Components/index.ts +9 -0
  58. package/Components/ModalComponent/AudioPlayer/utils/__tests__/mergeStyles.test.ts +356 -0
  59. package/Components/ModalComponent/AudioPlayer/utils/mergeStyles.ts +521 -0
  60. package/Components/ModalComponent/BottomSheetDragArea.tsx +33 -0
  61. package/Components/ModalComponent/BottomSheetModalContent.tsx +505 -50
  62. package/Components/ModalComponent/Button/index.tsx +5 -3
  63. package/Components/ModalComponent/Header/CloseButton.tsx +12 -0
  64. package/Components/ModalComponent/Header/__tests__/CloseButton.test.tsx +72 -0
  65. package/Components/ModalComponent/Header/index.tsx +20 -7
  66. package/Components/ModalComponent/Header/utils.ts +1 -1
  67. package/Components/ModalComponent/ModalBlocksStyleContext.tsx +10 -0
  68. package/Components/ModalComponent/SortableList.tsx +156 -0
  69. package/Components/ModalComponent/SortableList.web.tsx +34 -0
  70. package/Components/ModalComponent/TextInputContent.tsx +135 -0
  71. package/Components/ModalComponent/__tests__/BottomSheetDragArea.test.tsx +74 -0
  72. package/Components/ModalComponent/__tests__/BottomSheetModalContent.test.tsx +343 -0
  73. package/Components/ModalComponent/__tests__/SortableList.web.test.tsx +16 -0
  74. package/Components/ModalComponent/__tests__/TextInputContent.test.tsx +169 -0
  75. package/Components/ModalComponent/__tests__/showTextInput.test.ts +95 -0
  76. package/Components/ModalComponent/index.tsx +18 -9
  77. package/Components/ModalComponent/presets/__tests__/getCell.test.ts +90 -0
  78. package/Components/ModalComponent/presets/dynamicCollectionCell.tsx +78 -0
  79. package/Components/ModalComponent/presets/index.ts +27 -0
  80. package/Components/ModalComponent/presets/selectableCell.tsx +28 -0
  81. package/Components/ModalComponent/presets/standardCell.tsx +41 -0
  82. package/Components/ModalComponent/presets/types.ts +35 -0
  83. package/Components/ModalComponent/showTextInput.ts +52 -0
  84. package/Components/ModalComponent/utils.ts +82 -21
  85. package/Components/OfflineHandler/__tests__/__snapshots__/index.test.tsx.snap +4 -85
  86. package/Components/OfflineHandler/__tests__/index.test.tsx +78 -18
  87. package/Components/OfflineHandler/hooks.ts +111 -0
  88. package/Components/OfflineHandler/index.tsx +51 -73
  89. package/Components/OfflineHandler/utils/index.ts +1 -13
  90. package/Components/PlayerContainer/PlayerContainer.tsx +11 -7
  91. package/Components/PlayerContainer/ProgramInfo/index.tsx +3 -4
  92. package/Components/PlayerContainer/__tests__/PlayerContainer.test.tsx +66 -13
  93. package/Components/PreloaderWrapper/index.tsx +1 -1
  94. package/Components/River/ComponentsMap/ComponentsMap.tsx +85 -59
  95. package/Components/River/River.tsx +4 -0
  96. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +140 -75
  97. package/Components/River/__tests__/componentsMap.test.js +8 -3
  98. package/Components/River/__tests__/resultCallback.test.tsx +47 -0
  99. package/Components/Screen/__tests__/Screen.test.tsx +24 -0
  100. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +2 -0
  101. package/Components/Screen/index.tsx +5 -0
  102. package/Components/Screen/navigationHandler.ts +8 -8
  103. package/Components/ScreenFeedLoader/ScreenFeedLoader.tsx +5 -3
  104. package/Components/ScreenResolverFeedProvider/ScreenResolverFeedProvider.tsx +1 -1
  105. package/Components/ScreenRevealManager/ScreenRevealManager.ts +18 -2
  106. package/Components/ScreenRevealManager/__tests__/ScreenRevealManager.test.ts +36 -0
  107. package/Components/ScreenRevealManager/__tests__/withScreenRevealManager.test.tsx +56 -0
  108. package/Components/ScreenRevealManager/withScreenRevealManager.tsx +21 -4
  109. package/Components/TopCutoffOverlay/index.tsx +2 -2
  110. package/Components/Transitioner/Transitioner.tsx +30 -20
  111. package/Components/Transitioner/__tests__/transitioner.test.js +83 -2
  112. package/Components/VideoLive/LiveImageManager.ts +9 -3
  113. package/Components/VideoLive/__tests__/LiveImageManager.test.ts +59 -0
  114. package/Contexts/ModalNavigationContext/__tests__/index.test.tsx +15 -0
  115. package/Contexts/ModalNavigationContext/index.tsx +2 -2
  116. package/Hooks/useEntryActionsExpander.ts +63 -0
  117. package/assets/empty-Info.png +0 -0
  118. package/package.json +6 -5
  119. package/Components/OfflineHandler/NotificationView/NotificationView.lg.tsx +0 -112
  120. package/Components/OfflineHandler/NotificationView/NotificationView.samsung.tsx +0 -107
  121. package/Components/OfflineHandler/NotificationView/NotificationView.tsx +0 -153
  122. package/Components/OfflineHandler/NotificationView/__tests__/index.test.tsx +0 -66
  123. package/Components/OfflineHandler/NotificationView/utils.ts +0 -34
  124. /package/Decorators/ZappPipesDataConnector/__tests__/{Hero.js → Hero.tsx} +0 -0
@@ -0,0 +1,728 @@
1
+ /**
2
+ * Modal Building Block — Input
3
+ *
4
+ * Green (configurable): background, label, text, icons
5
+ * White (hardcoded): default search / clear icon assets, clear button circle layout
6
+ *
7
+ * Variants (VARIANT_*) are preset configuration bundles — not a config field.
8
+ */
9
+
10
+ import React, { useRef } from "react";
11
+ import {
12
+ Image,
13
+ Platform,
14
+ Pressable,
15
+ StyleSheet,
16
+ Text,
17
+ TextInput,
18
+ View,
19
+ } from "react-native";
20
+
21
+ import { resolveDefaultAssetUri } from "@applicaster/zapp-react-native-utils/actionUtils";
22
+
23
+ const styles = StyleSheet.create({
24
+ column: {
25
+ alignItems: "center",
26
+ width: "100%",
27
+ },
28
+ validationMessage: {
29
+ flexDirection: "row",
30
+ alignItems: "center",
31
+ alignSelf: "center",
32
+ overflow: "hidden",
33
+ backgroundColor: "rgba(229,30,37,1.00)",
34
+ borderRadius: 100,
35
+ marginTop: 16,
36
+ paddingLeft: 8,
37
+ paddingRight: 12,
38
+ paddingTop: 8,
39
+ paddingBottom: 8,
40
+ gap: 8,
41
+ },
42
+ validationMessageIcon: {
43
+ width: 20,
44
+ height: 20,
45
+ alignSelf: "flex-start",
46
+ },
47
+ validationMessageText: {
48
+ color: "rgba(255,255,255,1.00)",
49
+ fontFamily: Platform.select({
50
+ ios: "GreycliffCF-Bold",
51
+ android: "Greycliff-Bold",
52
+ default: "Greycliff-Bold",
53
+ }),
54
+ fontSize: 13,
55
+ lineHeight: 20,
56
+ },
57
+ textInput: {
58
+ padding: 0,
59
+ margin: 0,
60
+ },
61
+ textInputRow: {
62
+ flex: 1,
63
+ },
64
+ clearButton: {
65
+ alignItems: "center",
66
+ justifyContent: "center",
67
+ },
68
+ underline: {
69
+ alignSelf: "stretch",
70
+ },
71
+ });
72
+
73
+ // ---------------------------------------------------------------------------
74
+ // Configuration (green / editable fields)
75
+ // ---------------------------------------------------------------------------
76
+
77
+ export type TextTransform =
78
+ | "default"
79
+ | "lowercase"
80
+ | "uppercase"
81
+ | "capitalize";
82
+
83
+ export type TextAlignment = "left" | "center";
84
+
85
+ export type UnderlineConfiguration = {
86
+ focusedBorderColor: string;
87
+ borderColor: string;
88
+ /** Bottom underline height; 0 = none (pill search uses borderWidth instead) */
89
+ underlineHeight: number;
90
+ underlineMarginTop: number;
91
+ /** Radius of the underline bar itself (bottom border style). */
92
+ underlineCornerRadius: number;
93
+ };
94
+
95
+ export type BackgroundConfiguration = {
96
+ defaultColor: string;
97
+ focusedColor: string;
98
+ borderColor: string;
99
+ focusedBorderColor: string;
100
+ borderWidth: number;
101
+ cornerRadius: number;
102
+ paddingTop: number;
103
+ paddingRight: number;
104
+ paddingBottom: number;
105
+ paddingLeft: number;
106
+ horizontalGutter: number;
107
+ verticalGutter: number;
108
+ /** Fixed height for pill layout; 0 = hug content (name / underline layout) */
109
+ height: number;
110
+ };
111
+
112
+ export type LabelConfiguration = {
113
+ enabled: boolean;
114
+ fontColor: string;
115
+ iosFontFamily: string;
116
+ androidFontFamily: string;
117
+ fontSize: number;
118
+ lineHeight: number;
119
+ iosLetterSpacing: number;
120
+ androidLetterSpacing: number;
121
+ textTransform: TextTransform;
122
+ };
123
+
124
+ export type TextConfiguration = {
125
+ fontColor: string;
126
+ focusedFontColor: string;
127
+ placeholderColor: string;
128
+ cursorColor: string;
129
+ iosFontFamily: string;
130
+ androidFontFamily: string;
131
+ fontSize: number;
132
+ lineHeight: number;
133
+ iosLetterSpacing: number;
134
+ androidLetterSpacing: number;
135
+ textTransform: TextTransform;
136
+ alignment: TextAlignment;
137
+ /** Vertical spacing around the input text; drives the gap above the underline. */
138
+ marginTop: number;
139
+ marginBottom: number;
140
+ };
141
+
142
+ export type LeadingIconConfiguration = {
143
+ enabled: boolean;
144
+ asset?: string;
145
+ };
146
+
147
+ export type TrailingIconConfiguration = {
148
+ enabled: boolean;
149
+ asset?: string;
150
+ backgroundColor: string;
151
+ focusedBackgroundColor: string;
152
+ };
153
+
154
+ export type IconsConfiguration = {
155
+ width: number;
156
+ height: number;
157
+ leadingIcon: LeadingIconConfiguration;
158
+ trailingIcon: TrailingIconConfiguration;
159
+ };
160
+
161
+ export type InputConfiguration = {
162
+ underline: UnderlineConfiguration;
163
+ background: BackgroundConfiguration;
164
+ label: LabelConfiguration;
165
+ text: TextConfiguration;
166
+ icons: IconsConfiguration;
167
+ };
168
+
169
+ export const DEFAULT_BACKGROUND_CONFIGURATION: BackgroundConfiguration = {
170
+ defaultColor: "#000000",
171
+ focusedColor: "#000000",
172
+ borderColor: "rgba(255, 255, 255, 0.55)",
173
+ focusedBorderColor: "rgba(255, 255, 255, 0.85)",
174
+ borderWidth: 1,
175
+ cornerRadius: 24,
176
+ paddingTop: 10,
177
+ paddingRight: 10,
178
+ paddingBottom: 10,
179
+ paddingLeft: 14,
180
+ horizontalGutter: 8,
181
+ verticalGutter: 0,
182
+ height: 44,
183
+ };
184
+
185
+ export const DEFAULT_LABEL_CONFIGURATION: LabelConfiguration = {
186
+ enabled: false,
187
+ fontColor: "rgba(142, 142, 147, 1)",
188
+ iosFontFamily: "SFProText-Regular",
189
+ androidFontFamily: "Roboto",
190
+ fontSize: 15,
191
+ lineHeight: 20,
192
+ iosLetterSpacing: -0.2,
193
+ androidLetterSpacing: 0,
194
+ textTransform: "default",
195
+ };
196
+
197
+ export const DEFAULT_TEXT_CONFIGURATION: TextConfiguration = {
198
+ fontColor: "#FFFFFF",
199
+ focusedFontColor: "#FFFFFF",
200
+ placeholderColor: "rgba(142, 142, 147, 1)",
201
+ cursorColor: "#FFFFFF",
202
+ iosFontFamily: "SFProText-Regular",
203
+ androidFontFamily: "Roboto",
204
+ fontSize: 15,
205
+ lineHeight: 20,
206
+ iosLetterSpacing: -0.2,
207
+ androidLetterSpacing: 0,
208
+ textTransform: "default",
209
+ alignment: "left",
210
+ marginTop: 0,
211
+ marginBottom: 0,
212
+ };
213
+
214
+ export const DEFAULT_ICONS_CONFIGURATION: IconsConfiguration = {
215
+ width: 20,
216
+ height: 20,
217
+ leadingIcon: {
218
+ enabled: true,
219
+ },
220
+ trailingIcon: {
221
+ enabled: true,
222
+ backgroundColor: "rgba(58, 58, 60, 1)",
223
+ focusedBackgroundColor: "rgba(72, 72, 74, 1)",
224
+ },
225
+ };
226
+
227
+ export const DEFAULT_UNDERLINE_CONFIGURATION: UnderlineConfiguration = {
228
+ borderColor: "rgba(255, 255, 255, 0.55)",
229
+ focusedBorderColor: "rgba(255, 255, 255, 0.85)",
230
+ underlineHeight: 0,
231
+ underlineMarginTop: 0,
232
+ underlineCornerRadius: 0,
233
+ };
234
+
235
+ export const DEFAULT_INPUT_CONFIGURATION: InputConfiguration = {
236
+ background: DEFAULT_BACKGROUND_CONFIGURATION,
237
+ underline: DEFAULT_UNDERLINE_CONFIGURATION,
238
+ label: DEFAULT_LABEL_CONFIGURATION,
239
+ text: DEFAULT_TEXT_CONFIGURATION,
240
+ icons: DEFAULT_ICONS_CONFIGURATION,
241
+ };
242
+
243
+ // ---------------------------------------------------------------------------
244
+ // Data (runtime / localization)
245
+ // ---------------------------------------------------------------------------
246
+
247
+ export type InputData = {
248
+ placeholder: string;
249
+ /** Top instructional label (e.g. "Name your playlist") */
250
+ label?: string;
251
+ };
252
+
253
+ export type ItemProps = {
254
+ configuration: InputConfiguration;
255
+ data: InputData;
256
+ value?: string;
257
+ focused?: boolean;
258
+ focusedClearButton?: boolean;
259
+ validationMessage?: string;
260
+ onChangeText?: (text: string) => void;
261
+ onClearPress?: () => void;
262
+ onFocus?: () => void;
263
+ onBlur?: () => void;
264
+ };
265
+
266
+ // ---------------------------------------------------------------------------
267
+ // Hardcoded (white fields)
268
+ // ---------------------------------------------------------------------------
269
+
270
+ const CLEAR_BUTTON_SPEC = {
271
+ width: 20,
272
+ height: 20,
273
+ };
274
+
275
+ const DEFAULT_SEARCH_ICON_ASSET =
276
+ "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAr0lEQVR42mNgGAXkgnXrNrEAcT4Q7wTiB1C8EyrGQqnhXlAD/0PxayiG8UFyXpQYDjOoG4hVkORUoGIweS9yggXm8kg86iKRfMJCigX5MJcToRbmk3xSLNgJ1aRChFoVqNqdpFgA8vJrEtSDIv7BoLKA5pHE80imbTIlI6M9AmJ9WhUVjyi1BG9hBzIUyZLHZFlChCP0oYbDLNEbspbooVnCQktLaGMBUsKgjeEDBgBwWWj4ea9s3wAAAABJRU5ErkJggg==";
277
+
278
+ const DEFAULT_CLEAR_ICON_ASSET =
279
+ "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAPklEQVR42mNgGAWjYGiD/0BATXVkaabIcEKGUMVwXIZR1XB0Q2liOE1dThcf0DQOaJqKaJoP6JKTR8EoIAgADw5TrVXxxAIAAAAASUVORK5CYII=";
280
+
281
+ function getTextTransformStyle(
282
+ textTransform: TextTransform
283
+ ): "none" | "lowercase" | "uppercase" | "capitalize" {
284
+ return textTransform === "default" ? "none" : textTransform;
285
+ }
286
+
287
+ function shouldShowLeadingIcon(leadingIcon: LeadingIconConfiguration) {
288
+ return leadingIcon.enabled;
289
+ }
290
+
291
+ function shouldShowTrailingIcon(trailingIcon: TrailingIconConfiguration) {
292
+ return trailingIcon.enabled;
293
+ }
294
+
295
+ function shouldShowLabel(label: LabelConfiguration, labelText?: string) {
296
+ return label.enabled && !!labelText;
297
+ }
298
+
299
+ function isUnderlineLayout(configuration: InputConfiguration) {
300
+ return configuration.underline.underlineHeight > 0;
301
+ }
302
+
303
+ // ---------------------------------------------------------------------------
304
+ // Inner components
305
+ // ---------------------------------------------------------------------------
306
+
307
+ type InputLabelProps = {
308
+ text: string;
309
+ configuration: LabelConfiguration;
310
+ };
311
+
312
+ function InputLabel({ text, configuration }: InputLabelProps) {
313
+ const letterSpacing =
314
+ Platform.OS === "ios"
315
+ ? configuration.iosLetterSpacing
316
+ : configuration.androidLetterSpacing;
317
+
318
+ return (
319
+ <Text
320
+ style={{
321
+ color: configuration.fontColor,
322
+ fontSize: configuration.fontSize,
323
+ lineHeight: configuration.lineHeight,
324
+ letterSpacing,
325
+ fontFamily: Platform.select({
326
+ ios: configuration.iosFontFamily,
327
+ android: configuration.androidFontFamily,
328
+ default: configuration.androidFontFamily,
329
+ }),
330
+ textTransform: getTextTransformStyle(configuration.textTransform),
331
+ textAlign: "center",
332
+ }}
333
+ numberOfLines={1}
334
+ >
335
+ {text}
336
+ </Text>
337
+ );
338
+ }
339
+
340
+ type InputContainerProps = {
341
+ configuration: BackgroundConfiguration;
342
+ focused: boolean;
343
+ underlineLayout: boolean;
344
+ children: React.ReactNode;
345
+ };
346
+
347
+ function InputContainer({
348
+ configuration,
349
+ focused,
350
+ underlineLayout,
351
+ children,
352
+ }: InputContainerProps) {
353
+ return (
354
+ <View
355
+ style={{
356
+ flexDirection: underlineLayout ? "column" : "row",
357
+ alignItems: "center",
358
+ alignSelf: "stretch",
359
+ height: configuration.height > 0 ? configuration.height : undefined,
360
+ backgroundColor: focused
361
+ ? configuration.focusedColor
362
+ : configuration.defaultColor,
363
+ borderColor: focused
364
+ ? configuration.focusedBorderColor
365
+ : configuration.borderColor,
366
+ borderWidth: configuration.borderWidth,
367
+ borderRadius: configuration.cornerRadius,
368
+ paddingTop: configuration.paddingTop,
369
+ paddingRight: configuration.paddingRight,
370
+ paddingBottom: configuration.paddingBottom,
371
+ paddingLeft: configuration.paddingLeft,
372
+ }}
373
+ >
374
+ {children}
375
+ </View>
376
+ );
377
+ }
378
+
379
+ type InputUnderlineProps = {
380
+ configuration: UnderlineConfiguration;
381
+ focused: boolean;
382
+ };
383
+
384
+ function InputUnderline({ configuration, focused }: InputUnderlineProps) {
385
+ if (configuration.underlineHeight <= 0) {
386
+ return null;
387
+ }
388
+
389
+ return (
390
+ <View
391
+ style={[
392
+ styles.underline,
393
+ {
394
+ marginTop: configuration.underlineMarginTop,
395
+ height: configuration.underlineHeight,
396
+ borderRadius: configuration.underlineCornerRadius,
397
+ backgroundColor: focused
398
+ ? configuration.focusedBorderColor
399
+ : configuration.borderColor,
400
+ },
401
+ ]}
402
+ />
403
+ );
404
+ }
405
+
406
+ type InputLeadingIconProps = {
407
+ icons: IconsConfiguration;
408
+ tintColor: string;
409
+ };
410
+
411
+ function InputLeadingIcon({ icons, tintColor }: InputLeadingIconProps) {
412
+ const asset = icons.leadingIcon.asset ?? DEFAULT_SEARCH_ICON_ASSET;
413
+
414
+ return (
415
+ <Image
416
+ source={{ uri: asset }}
417
+ style={{
418
+ width: icons.width,
419
+ height: icons.height,
420
+ tintColor,
421
+ }}
422
+ resizeMode="contain"
423
+ />
424
+ );
425
+ }
426
+
427
+ type TextInputRef = React.ElementRef<typeof TextInput>;
428
+
429
+ type InputFieldProps = {
430
+ inputRef?: React.RefObject<TextInputRef>;
431
+ value: string;
432
+ placeholder: string;
433
+ configuration: TextConfiguration;
434
+ focused: boolean;
435
+ stretch: boolean;
436
+ onChangeText?: (text: string) => void;
437
+ onFocus?: () => void;
438
+ onBlur?: () => void;
439
+ };
440
+
441
+ function InputField({
442
+ inputRef,
443
+ value,
444
+ placeholder,
445
+ configuration,
446
+ focused,
447
+ stretch,
448
+ onChangeText,
449
+ onFocus,
450
+ onBlur,
451
+ }: InputFieldProps) {
452
+ const letterSpacing =
453
+ Platform.OS === "ios"
454
+ ? configuration.iosLetterSpacing
455
+ : configuration.androidLetterSpacing;
456
+
457
+ return (
458
+ <TextInput
459
+ ref={inputRef}
460
+ value={value}
461
+ placeholder={placeholder}
462
+ placeholderTextColor={configuration.placeholderColor}
463
+ onChangeText={onChangeText}
464
+ onFocus={onFocus}
465
+ onBlur={onBlur}
466
+ showSoftInputOnFocus
467
+ selectionColor={configuration.cursorColor}
468
+ cursorColor={configuration.cursorColor}
469
+ style={[
470
+ styles.textInput,
471
+ stretch ? styles.textInputRow : { alignSelf: "stretch", width: "100%" },
472
+ {
473
+ color: focused
474
+ ? configuration.focusedFontColor
475
+ : configuration.fontColor,
476
+ fontSize: configuration.fontSize,
477
+ lineHeight: configuration.lineHeight,
478
+ letterSpacing,
479
+ fontFamily: Platform.select({
480
+ ios: configuration.iosFontFamily,
481
+ android: configuration.androidFontFamily,
482
+ default: configuration.androidFontFamily,
483
+ }),
484
+ textTransform: getTextTransformStyle(configuration.textTransform),
485
+ textAlign: configuration.alignment,
486
+ marginTop: configuration.marginTop,
487
+ marginBottom: configuration.marginBottom,
488
+ },
489
+ ]}
490
+ />
491
+ );
492
+ }
493
+
494
+ type InputClearButtonProps = {
495
+ icons: IconsConfiguration;
496
+ focused?: boolean;
497
+ };
498
+
499
+ function InputClearButton({ icons, focused = false }: InputClearButtonProps) {
500
+ const { trailingIcon } = icons;
501
+ const asset = trailingIcon.asset ?? DEFAULT_CLEAR_ICON_ASSET;
502
+
503
+ return (
504
+ <View
505
+ style={[
506
+ styles.clearButton,
507
+ {
508
+ width: CLEAR_BUTTON_SPEC.width,
509
+ height: CLEAR_BUTTON_SPEC.height,
510
+ borderRadius: CLEAR_BUTTON_SPEC.width / 2,
511
+ backgroundColor: focused
512
+ ? trailingIcon.focusedBackgroundColor
513
+ : trailingIcon.backgroundColor,
514
+ },
515
+ ]}
516
+ >
517
+ <Image
518
+ source={{ uri: asset }}
519
+ style={{
520
+ width: CLEAR_BUTTON_SPEC.width * 0.55,
521
+ height: CLEAR_BUTTON_SPEC.height * 0.55,
522
+ tintColor: "#000000",
523
+ }}
524
+ resizeMode="contain"
525
+ />
526
+ </View>
527
+ );
528
+ }
529
+
530
+ // ---------------------------------------------------------------------------
531
+ // Item
532
+ // ---------------------------------------------------------------------------
533
+
534
+ export function AudioPlayerInput({
535
+ configuration,
536
+ data,
537
+ value = "",
538
+ focused = false,
539
+ focusedClearButton = false,
540
+ validationMessage,
541
+ onChangeText,
542
+ onClearPress,
543
+ onFocus,
544
+ onBlur,
545
+ }: ItemProps) {
546
+ const { background, label, text, icons, underline } = configuration;
547
+ const inputRef = useRef<TextInputRef>(null);
548
+ const underlineLayout = isUnderlineLayout(configuration);
549
+ const showLabel = shouldShowLabel(label, data.label);
550
+ const showLeading = shouldShowLeadingIcon(icons.leadingIcon);
551
+ const showTrailing = shouldShowTrailingIcon(icons.trailingIcon);
552
+
553
+ const focusInput = () => {
554
+ inputRef.current?.focus();
555
+ };
556
+
557
+ const clearButton = showTrailing ? (
558
+ <InputClearButton icons={icons} focused={focusedClearButton} />
559
+ ) : null;
560
+
561
+ const field = (
562
+ <InputField
563
+ inputRef={inputRef}
564
+ value={value}
565
+ placeholder={data.placeholder}
566
+ configuration={text}
567
+ focused={focused}
568
+ stretch={!underlineLayout}
569
+ onChangeText={onChangeText}
570
+ onFocus={onFocus}
571
+ onBlur={onBlur}
572
+ />
573
+ );
574
+
575
+ const inputRow = (
576
+ <Pressable onPress={focusInput} style={{ alignSelf: "stretch" }}>
577
+ <InputContainer
578
+ configuration={background}
579
+ focused={focused}
580
+ underlineLayout={underlineLayout}
581
+ >
582
+ {showLeading ? (
583
+ <>
584
+ <InputLeadingIcon icons={icons} tintColor={text.placeholderColor} />
585
+ <View style={{ width: background.horizontalGutter }} />
586
+ </>
587
+ ) : null}
588
+ {field}
589
+ {clearButton ? (
590
+ <>
591
+ <View style={{ width: background.horizontalGutter }} />
592
+ {onClearPress ? (
593
+ <Pressable
594
+ onPress={(event) => {
595
+ event.stopPropagation?.();
596
+ onClearPress();
597
+ }}
598
+ >
599
+ {clearButton}
600
+ </Pressable>
601
+ ) : (
602
+ clearButton
603
+ )}
604
+ </>
605
+ ) : null}
606
+ {underlineLayout ? (
607
+ <InputUnderline configuration={underline} focused={focused} />
608
+ ) : null}
609
+ </InputContainer>
610
+ </Pressable>
611
+ );
612
+
613
+ return (
614
+ <View style={styles.column}>
615
+ {showLabel && data.label ? (
616
+ <>
617
+ <InputLabel text={data.label} configuration={label} />
618
+ <View style={{ height: background.verticalGutter }} />
619
+ </>
620
+ ) : null}
621
+ {inputRow}
622
+ {validationMessage ? (
623
+ <View style={styles.validationMessage} accessibilityRole="alert">
624
+ <Image
625
+ source={{
626
+ uri: resolveDefaultAssetUri(
627
+ require("../../../../assets/empty-Info.png")
628
+ ),
629
+ }}
630
+ style={styles.validationMessageIcon}
631
+ />
632
+ <Text
633
+ style={styles.validationMessageText}
634
+ numberOfLines={1}
635
+ ellipsizeMode="tail"
636
+ >
637
+ {validationMessage}
638
+ </Text>
639
+ </View>
640
+ ) : null}
641
+ </View>
642
+ );
643
+ }
644
+
645
+ // ---------------------------------------------------------------------------
646
+ // Variant configurations
647
+ // ---------------------------------------------------------------------------
648
+
649
+ /** Search pill — leading icon + clear button */
650
+ export const VARIANT_SEARCH_INPUT: InputConfiguration =
651
+ DEFAULT_INPUT_CONFIGURATION;
652
+
653
+ /**
654
+ * Name playlist — centered title + 32pt text over a rounded bottom underline.
655
+ *
656
+ * Values mirror the manifest configuration the
657
+ * `quick-brick-component-text-field` plugin renders from; the comment on each
658
+ * line names the reference key it comes from.
659
+ */
660
+ export const VARIANT_NAME_PLAYLIST_INPUT: InputConfiguration = {
661
+ background: {
662
+ // text_input_background_*_background_color
663
+ defaultColor: "rgba(30,30,30,0.00)",
664
+ focusedColor: "rgba(46,46,46,0.00)",
665
+ // text_input_style_border_*_border_color
666
+ borderColor: "rgba(255,255,255,0.25)",
667
+ focusedBorderColor: "rgba(255,255,255,1.00)",
668
+ // border_border_style is "bottom", so the box border is off and the
669
+ // underline below draws the border instead.
670
+ borderWidth: 0,
671
+ cornerRadius: 0,
672
+ // text_input_style_input_padding_*
673
+ paddingTop: 0,
674
+ paddingRight: 0,
675
+ paddingBottom: 0,
676
+ paddingLeft: 0,
677
+ horizontalGutter: 0, // text_input_style_input_horizontal_gutter
678
+ verticalGutter: 0, // component_style_vertical_gutter
679
+ height: 0, // hug content
680
+ },
681
+ underline: {
682
+ borderColor: "rgba(255,255,255,0.25)",
683
+ focusedBorderColor: "rgba(255,255,255,1.00)",
684
+ underlineHeight: 2, // text_input_style_border_border_size
685
+ // The gap above the bar comes from the text's marginBottom, matching how
686
+ // the plugin applies `text_label_margin_bottom` to the input itself.
687
+ underlineMarginTop: 0,
688
+ underlineCornerRadius: 4, // ..._border_bottom_border_corner_radius
689
+ },
690
+ label: {
691
+ enabled: true, // title_enable
692
+ fontColor: "rgba(255,255,255,0.50)", // title_style_color
693
+ iosFontFamily: "GreycliffCF-Bold", // title_style_ios_font_family
694
+ androidFontFamily: "Greycliff-Bold", // title_style_android_font_family
695
+ fontSize: 14, // title_style_font_size
696
+ lineHeight: 32, // title_style_line_height
697
+ iosLetterSpacing: 0,
698
+ androidLetterSpacing: 0,
699
+ textTransform: "default", // title_style_text_transform: "none"
700
+ },
701
+ text: {
702
+ // text_input_style_text_label_*
703
+ fontColor: "rgba(255,255,255,1.00)", // active_label_color
704
+ focusedFontColor: "rgba(255,255,255,1.00)",
705
+ placeholderColor: "rgba(255,255,255,0.50)", // placeholder_label_color
706
+ cursorColor: "rgba(249,183,72,1.00)", // text_input_style_cursor_cursor_color
707
+ iosFontFamily: "GreycliffCF-Bold",
708
+ androidFontFamily: "Greycliff-Bold",
709
+ fontSize: 32,
710
+ lineHeight: 40,
711
+ iosLetterSpacing: 0,
712
+ androidLetterSpacing: 0,
713
+ textTransform: "default",
714
+ alignment: "center",
715
+ marginTop: 20, // text_label_margin_top
716
+ marginBottom: 20, // text_label_margin_bottom
717
+ },
718
+ icons: {
719
+ width: 20,
720
+ height: 20,
721
+ leadingIcon: { enabled: false },
722
+ trailingIcon: {
723
+ enabled: false, // clear_button_enable
724
+ backgroundColor: "rgba(58, 58, 60, 1)",
725
+ focusedBackgroundColor: "rgba(72, 72, 74, 1)",
726
+ },
727
+ },
728
+ };