@applicaster/zapp-react-native-ui-components 16.0.0-rc.10 → 16.0.0-rc.100

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 (154) 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/CellRendererResolver/index.ts +2 -2
  7. package/Components/ComponentResolver/__tests__/componentResolver.test.js +1 -1
  8. package/Components/FocusableGroup/FocusableTvOS.tsx +11 -7
  9. package/Components/GeneralContentScreen/GeneralContentScreenHookAdapter.tsx +54 -0
  10. package/Components/GeneralContentScreen/__tests__/GeneralContentScreenHookAdapter.test.tsx +64 -0
  11. package/Components/GeneralContentScreen/__tests__/HookContentFocusGroup.web.test.tsx +91 -0
  12. package/Components/GeneralContentScreen/hookAdapter/__tests__/networkService.test.ts +74 -0
  13. package/Components/GeneralContentScreen/hookAdapter/__tests__/runInBackground.test.ts +139 -0
  14. package/Components/GeneralContentScreen/hookAdapter/__tests__/skipExpression.test.ts +294 -0
  15. package/Components/GeneralContentScreen/hookAdapter/__tests__/validationHelper.test.ts +188 -0
  16. package/Components/GeneralContentScreen/hookAdapter/logger.ts +6 -0
  17. package/Components/GeneralContentScreen/hookAdapter/networkService.ts +53 -0
  18. package/Components/GeneralContentScreen/hookAdapter/runInBackground.ts +48 -0
  19. package/Components/GeneralContentScreen/hookAdapter/skipExpression.ts +252 -0
  20. package/Components/GeneralContentScreen/hookAdapter/validationHelper.ts +90 -0
  21. package/Components/GeneralContentScreen/hookFocus/index.tsx +13 -0
  22. package/Components/GeneralContentScreen/hookFocus/index.web.tsx +69 -0
  23. package/Components/GeneralContentScreen/index.ts +2 -0
  24. package/Components/Layout/FullWidthRow.tsx +38 -0
  25. package/Components/LinkHandler/LinkHandler.tsx +2 -7
  26. package/Components/MasterCell/DefaultComponents/ActionButton.tsx +5 -1
  27. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/placement.test.ts +47 -21
  28. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/ActionButtonController.tsx +19 -4
  29. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/__tests__/ActionButtonController.test.tsx +43 -1
  30. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/placement.ts +34 -6
  31. package/Components/MasterCell/DefaultComponents/Button.tsx +1 -0
  32. package/Components/MasterCell/DefaultComponents/Image/hoc/utils/__tests__/withDimensions.test.ts +420 -125
  33. package/Components/MasterCell/DefaultComponents/Image/hoc/utils/index.ts +114 -19
  34. package/Components/MasterCell/DefaultComponents/Image/hoc/withDimensions.tsx +19 -8
  35. package/Components/MasterCell/DefaultComponents/ImageContainer/index.tsx +5 -3
  36. package/Components/MasterCell/DefaultComponents/LiveImage/executePreloadHooks.ts +2 -4
  37. package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +2 -3
  38. package/Components/MasterCell/DefaultComponents/PressableView.tsx +29 -9
  39. package/Components/MasterCell/DefaultComponents/Text/hooks/useText.ts +4 -0
  40. package/Components/MasterCell/DefaultComponents/Toggle.tsx +36 -14
  41. package/Components/MasterCell/DefaultComponents/__tests__/Toggle.test.tsx +167 -0
  42. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/ActionButton.tsx +154 -100
  43. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/AssetComponent.tsx +15 -4
  44. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Button.ts +56 -22
  45. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Spacer.ts +6 -4
  46. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/TextLabelsContainer.ts +3 -1
  47. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/PressableView.test.tsx +32 -21
  48. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/index.test.ts +15 -4
  49. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/index.ts +23 -16
  50. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/__tests__/insertButtons.test.ts +8 -3
  51. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/index.ts +2 -2
  52. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/__tests__/insertButtonsBetweenLabels.test.ts +45 -13
  53. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/index.ts +2 -2
  54. package/Components/MasterCell/SharedUI/CollapsibleTextContainer/__tests__/factory.test.ts +32 -0
  55. package/Components/MasterCell/SharedUI/CollapsibleTextContainer/index.ts +1 -2
  56. package/Components/MasterCell/contexts/PressedStateContext.ts +3 -0
  57. package/Components/MasterCell/dataAdapter.ts +15 -5
  58. package/Components/MasterCell/hooks/index.ts +2 -0
  59. package/Components/MasterCell/hooks/usePressedState.ts +4 -0
  60. package/Components/MasterCell/index.tsx +30 -5
  61. package/Components/MasterCell/utils/__tests__/resolveColor.test.js +169 -132
  62. package/Components/MasterCell/utils/__tests__/resolveColorForProp.test.js +92 -0
  63. package/Components/MasterCell/utils/index.ts +85 -46
  64. package/Components/ModalComponent/ActionsBottomSheet/ActionButton.tsx +56 -0
  65. package/Components/ModalComponent/ActionsBottomSheet/__tests__/openActionsBottomSheet.test.ts +38 -0
  66. package/Components/ModalComponent/ActionsBottomSheet/boundActionButton.tsx +24 -0
  67. package/Components/ModalComponent/ActionsBottomSheet/index.ts +9 -0
  68. package/Components/ModalComponent/ActionsBottomSheet/openActionsBottomSheet.ts +61 -0
  69. package/Components/ModalComponent/AudioPlayer/Components/Button.tsx +392 -0
  70. package/Components/ModalComponent/AudioPlayer/Components/Header.tsx +772 -0
  71. package/Components/ModalComponent/AudioPlayer/Components/Input.tsx +728 -0
  72. package/Components/ModalComponent/AudioPlayer/Components/Item.tsx +1007 -0
  73. package/Components/ModalComponent/AudioPlayer/Components/NowPlayingHeaderSection.tsx +205 -0
  74. package/Components/ModalComponent/AudioPlayer/Components/__tests__/Item.test.tsx +113 -0
  75. package/Components/ModalComponent/AudioPlayer/Components/__tests__/NowPlayingHeaderSection.test.tsx +36 -0
  76. package/Components/ModalComponent/AudioPlayer/Components/index.ts +9 -0
  77. package/Components/ModalComponent/AudioPlayer/utils/__tests__/mergeStyles.test.ts +356 -0
  78. package/Components/ModalComponent/AudioPlayer/utils/mergeStyles.ts +521 -0
  79. package/Components/ModalComponent/BottomSheetDragArea.tsx +33 -0
  80. package/Components/ModalComponent/BottomSheetModalContent.tsx +505 -50
  81. package/Components/ModalComponent/Button/index.tsx +5 -3
  82. package/Components/ModalComponent/Header/CloseButton.tsx +12 -0
  83. package/Components/ModalComponent/Header/__tests__/CloseButton.test.tsx +72 -0
  84. package/Components/ModalComponent/Header/index.tsx +20 -7
  85. package/Components/ModalComponent/Header/utils.ts +1 -1
  86. package/Components/ModalComponent/ModalBlocksStyleContext.tsx +10 -0
  87. package/Components/ModalComponent/SortableList.tsx +156 -0
  88. package/Components/ModalComponent/SortableList.web.tsx +34 -0
  89. package/Components/ModalComponent/TextInputContent.tsx +135 -0
  90. package/Components/ModalComponent/__tests__/BottomSheetDragArea.test.tsx +74 -0
  91. package/Components/ModalComponent/__tests__/BottomSheetModalContent.test.tsx +343 -0
  92. package/Components/ModalComponent/__tests__/SortableList.web.test.tsx +16 -0
  93. package/Components/ModalComponent/__tests__/TextInputContent.test.tsx +169 -0
  94. package/Components/ModalComponent/__tests__/showTextInput.test.ts +95 -0
  95. package/Components/ModalComponent/index.tsx +18 -9
  96. package/Components/ModalComponent/presets/__tests__/getCell.test.ts +90 -0
  97. package/Components/ModalComponent/presets/dynamicCollectionCell.tsx +78 -0
  98. package/Components/ModalComponent/presets/index.ts +27 -0
  99. package/Components/ModalComponent/presets/selectableCell.tsx +28 -0
  100. package/Components/ModalComponent/presets/standardCell.tsx +41 -0
  101. package/Components/ModalComponent/presets/types.ts +35 -0
  102. package/Components/ModalComponent/showTextInput.ts +52 -0
  103. package/Components/ModalComponent/utils.ts +82 -21
  104. package/Components/OfflineHandler/__tests__/__snapshots__/index.test.tsx.snap +4 -85
  105. package/Components/OfflineHandler/__tests__/index.test.tsx +78 -18
  106. package/Components/OfflineHandler/hooks.ts +111 -0
  107. package/Components/OfflineHandler/index.tsx +51 -73
  108. package/Components/OfflineHandler/utils/index.ts +1 -13
  109. package/Components/PlayerContainer/PlayerContainer.tsx +25 -17
  110. package/Components/PlayerContainer/ProgramInfo/index.tsx +3 -4
  111. package/Components/PlayerContainer/__tests__/PlayerContainer.test.tsx +337 -0
  112. package/Components/PreloaderWrapper/index.tsx +1 -1
  113. package/Components/River/ComponentsMap/ComponentsMap.tsx +87 -61
  114. package/Components/River/River.tsx +4 -0
  115. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +140 -89
  116. package/Components/River/__tests__/componentsMap.test.js +8 -3
  117. package/Components/River/__tests__/resultCallback.test.tsx +47 -0
  118. package/Components/Screen/__tests__/Screen.test.tsx +24 -0
  119. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +2 -0
  120. package/Components/Screen/index.tsx +13 -4
  121. package/Components/Screen/navigationHandler.ts +8 -8
  122. package/Components/ScreenFeedLoader/ScreenFeedLoader.tsx +5 -3
  123. package/Components/ScreenResolverFeedProvider/ScreenResolverFeedProvider.tsx +1 -1
  124. package/Components/ScreenRevealManager/ScreenRevealManager.ts +18 -2
  125. package/Components/ScreenRevealManager/__tests__/ScreenRevealManager.test.ts +36 -0
  126. package/Components/ScreenRevealManager/__tests__/withScreenRevealManager.test.tsx +56 -0
  127. package/Components/ScreenRevealManager/withScreenRevealManager.tsx +21 -4
  128. package/Components/TopCutoffOverlay/index.tsx +2 -2
  129. package/Components/Transitioner/Transitioner.tsx +28 -17
  130. package/Components/VideoLive/LiveImageManager.ts +9 -3
  131. package/Components/VideoLive/__tests__/LiveImageManager.test.ts +59 -0
  132. package/Components/VideoModal/utils.ts +6 -1
  133. package/Components/ZappFrameworkComponents/BarView/BarView.tsx +12 -5
  134. package/Components/ZappFrameworkComponents/BarView/__tests__/BarView.test.tsx +2 -2
  135. package/Contexts/CachedDimensionsContext/__tests__/index.test.ts +154 -0
  136. package/Contexts/CachedDimensionsContext/index.ts +17 -2
  137. package/Contexts/ModalNavigationContext/__tests__/index.test.tsx +15 -0
  138. package/Contexts/ModalNavigationContext/index.tsx +2 -2
  139. package/Helpers/ComponentCellSelectionHelper/index.js +0 -6
  140. package/Helpers/index.js +0 -39
  141. package/Hooks/useEntryActionsExpander.ts +63 -0
  142. package/assets/empty-Info.png +0 -0
  143. package/package.json +6 -5
  144. package/Components/OfflineHandler/NotificationView/NotificationView.lg.tsx +0 -112
  145. package/Components/OfflineHandler/NotificationView/NotificationView.samsung.tsx +0 -107
  146. package/Components/OfflineHandler/NotificationView/NotificationView.tsx +0 -153
  147. package/Components/OfflineHandler/NotificationView/__tests__/index.test.tsx +0 -66
  148. package/Components/OfflineHandler/NotificationView/utils.ts +0 -34
  149. package/Components/Screen/utils.ts +0 -16
  150. package/Helpers/Analytics/index.js +0 -95
  151. /package/Components/BackgroundImage/{BackgroundImage.tv.android.tsx → BackgroundImage.android.tv.tsx} +0 -0
  152. /package/Components/BackgroundImage/{BackgroundImage.tv.ios.tsx → BackgroundImage.ios.tv.tsx} +0 -0
  153. /package/Components/MasterCell/DefaultComponents/ButtonContainerView/{index.tv.android.tsx → index.android.tv.tsx} +0 -0
  154. /package/Decorators/ZappPipesDataConnector/__tests__/{Hero.js → Hero.tsx} +0 -0
@@ -0,0 +1,1007 @@
1
+ /**
2
+ * Modal Building Block — Item (Queue / Playlist)
3
+ *
4
+ * Single-file Expo Snack demo. Three item forms driven by configuration + data.
5
+ *
6
+ * Green (configurable): background, image, nowPlayingAsset, nowPlayingLabel,
7
+ * textLabel1, textLabel2
8
+ * White (hardcoded): container padding/gutters, nowPlaying container layout,
9
+ * button sizes/padding/assets
10
+ *
11
+ * Variants (VARIANT_*) are preset configuration bundles — not passed as a field.
12
+ */
13
+
14
+ import React from "react";
15
+ import { QBImage as Image } from "@applicaster/zapp-react-native-ui-components/Components/Image";
16
+ import { Platform, Pressable, StyleSheet, Text, View } from "react-native";
17
+ import {
18
+ MenuItem,
19
+ SelectionBehavior,
20
+ } from "@applicaster/zapp-react-native-utils/modalState/types";
21
+ import { useModalBlocksStyle } from "../../ModalBlocksStyleContext";
22
+ import { mergeItemConfiguration } from "../utils/mergeStyles";
23
+
24
+ const styles = StyleSheet.create({
25
+ row: {
26
+ flexDirection: "row",
27
+ alignItems: "center",
28
+ },
29
+ contentColumn: {
30
+ flex: 1,
31
+ justifyContent: "center",
32
+ },
33
+ buttonsRow: {
34
+ flexDirection: "row",
35
+ alignItems: "center",
36
+ },
37
+ });
38
+
39
+ // ---------------------------------------------------------------------------
40
+ // Configuration (green fields)
41
+ // ---------------------------------------------------------------------------
42
+
43
+ export type TextTransform =
44
+ | "default"
45
+ | "lowercase"
46
+ | "uppercase"
47
+ | "capitalize";
48
+
49
+ export type BackgroundConfiguration = {
50
+ defaultColor: string;
51
+ focusedColor: string;
52
+ cornerRadius: number;
53
+ paddingTop?: number;
54
+ paddingRight?: number;
55
+ paddingBottom?: number;
56
+ paddingLeft?: number;
57
+ highlightInsetLeft?: number;
58
+ highlightInsetRight?: number;
59
+ };
60
+
61
+ export type ImageConfiguration = {
62
+ width: number;
63
+ height: number;
64
+ borderWidth: number;
65
+ borderColor: string;
66
+ cornerRadius: number;
67
+ };
68
+
69
+ export type LabelConfiguration = {
70
+ fontColor: string;
71
+ focusedFontColor?: string;
72
+ iosFontFamily: string;
73
+ androidFontFamily: string;
74
+ fontSize: number;
75
+ lineHeight: number;
76
+ iosLetterSpacing: number;
77
+ androidLetterSpacing: number;
78
+ textTransform: TextTransform;
79
+ };
80
+
81
+ export type NowPlayingAssetConfiguration = {
82
+ asset?: string;
83
+ };
84
+
85
+ export type CheckboxAssetsConfiguration = {
86
+ /** Unchecked multiSelect control (item.checkbox_asset). */
87
+ asset?: string;
88
+ /** Checked multiSelect control (item.checked_checkbox_asset). */
89
+ checkedAsset?: string;
90
+ };
91
+
92
+ export type ActionButtonAssetsConfiguration = {
93
+ /** Leading remove control on Edit Order rows. */
94
+ removeAsset?: string;
95
+ /** Trailing drag handle on Edit Order / queue rows. */
96
+ dragAsset?: string;
97
+ };
98
+
99
+ export type ItemConfiguration = {
100
+ background: BackgroundConfiguration;
101
+ image: ImageConfiguration;
102
+ nowPlayingAsset: NowPlayingAssetConfiguration;
103
+ nowPlayingLabel: LabelConfiguration;
104
+ textLabel1: LabelConfiguration;
105
+ textLabel2: LabelConfiguration;
106
+ checkboxAssets?: CheckboxAssetsConfiguration;
107
+ actionButtonAssets?: ActionButtonAssetsConfiguration;
108
+ };
109
+
110
+ export const DEFAULT_TEXT_LABEL_1_CONFIGURATION: LabelConfiguration = {
111
+ fontColor: "#FFFFFF",
112
+ focusedFontColor: "#FFFFFF",
113
+ iosFontFamily: "SFProText-Semibold",
114
+ androidFontFamily: "Roboto-Medium",
115
+ fontSize: 15,
116
+ lineHeight: 18,
117
+ iosLetterSpacing: -0.2,
118
+ androidLetterSpacing: 0,
119
+ textTransform: "default",
120
+ };
121
+
122
+ export const DEFAULT_TEXT_LABEL_2_CONFIGURATION: LabelConfiguration = {
123
+ fontColor: "#999999",
124
+ iosFontFamily: "SFProText-Medium",
125
+ androidFontFamily: "Roboto-Medium",
126
+ fontSize: 13,
127
+ lineHeight: 16,
128
+ iosLetterSpacing: -0.1,
129
+ androidLetterSpacing: 0,
130
+ textTransform: "default",
131
+ };
132
+
133
+ export const DEFAULT_NOW_PLAYING_LABEL_CONFIGURATION: LabelConfiguration = {
134
+ fontColor: "#FFFFFF",
135
+ iosFontFamily: "SFProText-Semibold",
136
+ androidFontFamily: "Roboto-Medium",
137
+ fontSize: 13,
138
+ lineHeight: 16,
139
+ iosLetterSpacing: 0,
140
+ androidLetterSpacing: 0,
141
+ textTransform: "default",
142
+ };
143
+
144
+ // ---------------------------------------------------------------------------
145
+ // Data
146
+ // ---------------------------------------------------------------------------
147
+
148
+ export type ItemButtonAction = "playPause" | "drag" | "multiSelect" | "remove";
149
+
150
+ export type ItemData = {
151
+ imageUri?: string;
152
+ textLabel1: string;
153
+ textLabel2: string;
154
+ /** When set, shows the Now Playing row (equalizer + label). */
155
+ nowPlayingLabel?: string;
156
+ /** Button 2 (left most) — hardcoded action decides icon. */
157
+ leadingButton?: ItemButtonAction;
158
+ /** Button 1 (right most) — hardcoded action decides icon. */
159
+ trailingButton?: ItemButtonAction;
160
+ isSelected?: boolean;
161
+ };
162
+
163
+ export type AudioPlayerTrackItemProps = {
164
+ configuration: ItemConfiguration;
165
+ data: ItemData;
166
+ focused?: boolean;
167
+ focusedLeadingButton?: boolean;
168
+ focusedTrailingButton?: boolean;
169
+ onPress?: () => void;
170
+ onLeadingButtonPress?: () => void;
171
+ onTrailingButtonPress?: () => void;
172
+ renderHandle?: (element: React.ReactElement) => React.ReactElement;
173
+ };
174
+
175
+ // ---------------------------------------------------------------------------
176
+ // Hardcoded (white fields)
177
+ // ---------------------------------------------------------------------------
178
+
179
+ type ContainerSpec = {
180
+ paddingTop: number;
181
+ paddingRight: number;
182
+ paddingBottom: number;
183
+ paddingLeft: number;
184
+ horizontalGutter: number;
185
+ verticalGutter: number;
186
+ };
187
+
188
+ type ButtonLayoutSpec = {
189
+ assetWidth: number;
190
+ assetHeight: number;
191
+ paddingTop: number;
192
+ paddingRight: number;
193
+ paddingBottom: number;
194
+ paddingLeft: number;
195
+ };
196
+
197
+ const CONTAINER_SPEC_LARGE_IMAGE: ContainerSpec = {
198
+ paddingTop: 8,
199
+ paddingRight: 20,
200
+ paddingBottom: 8,
201
+ paddingLeft: 20,
202
+ horizontalGutter: 12,
203
+ verticalGutter: 2,
204
+ };
205
+
206
+ const CONTAINER_SPEC_DEFAULT: ContainerSpec = {
207
+ paddingTop: 8,
208
+ paddingRight: 10,
209
+ paddingBottom: 8,
210
+ paddingLeft: 10,
211
+ horizontalGutter: 12,
212
+ verticalGutter: 2,
213
+ };
214
+
215
+ const BUTTONS_CONTAINER_HORIZONTAL_GUTTER = 4;
216
+
217
+ const NOW_PLAYING_CONTAINER_SPEC = {
218
+ horizontalGutter: 4,
219
+ marginTop: 0,
220
+ marginRight: 0,
221
+ marginBottom: 6,
222
+ marginLeft: 0,
223
+ };
224
+
225
+ const NOW_PLAYING_ASSET_SPEC = {
226
+ width: 16,
227
+ height: 16,
228
+ };
229
+
230
+ const BUTTON_LAYOUT_SPEC: Record<ItemButtonAction, ButtonLayoutSpec> = {
231
+ playPause: {
232
+ assetWidth: 44,
233
+ assetHeight: 44,
234
+ paddingTop: 0,
235
+ paddingRight: 0,
236
+ paddingBottom: 0,
237
+ paddingLeft: 0,
238
+ },
239
+ drag: {
240
+ assetWidth: 24,
241
+ assetHeight: 24,
242
+ paddingTop: 10,
243
+ paddingRight: 10,
244
+ paddingBottom: 10,
245
+ paddingLeft: 10,
246
+ },
247
+ multiSelect: {
248
+ assetWidth: 24,
249
+ assetHeight: 24,
250
+ paddingTop: 10,
251
+ paddingRight: 10,
252
+ paddingBottom: 10,
253
+ paddingLeft: 10,
254
+ },
255
+ remove: {
256
+ assetWidth: 24,
257
+ assetHeight: 24,
258
+ paddingTop: 10,
259
+ paddingRight: 10,
260
+ paddingBottom: 10,
261
+ paddingLeft: 10,
262
+ },
263
+ };
264
+
265
+ const DEFAULT_THUMB_ASSET =
266
+ "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAQklEQVR42u3PMQ0AAAgDsEmcfzNggZekRwU0beezCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAhcLQVXaABdK6k6AAAAAElFTkSuQmCC";
267
+
268
+ const DEFAULT_EQUALIZER_ASSET =
269
+ "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAI0lEQVR42mNgGNTgPxTQzwB0DVQ3gKCBowbQwYAhkBIHBAAA9TvPMeOFaLMAAAAASUVORK5CYII=";
270
+
271
+ function getTextTransformStyle(
272
+ textTransform: TextTransform
273
+ ): "none" | "lowercase" | "uppercase" | "capitalize" {
274
+ return textTransform === "default" ? "none" : textTransform;
275
+ }
276
+
277
+ function getFontFamily(ios: string, android: string) {
278
+ return Platform.select({ ios, android, default: android });
279
+ }
280
+
281
+ /** Hardcoded: large image (72) uses wider horizontal container padding. */
282
+ function resolveContainerSpec(image: ImageConfiguration): ContainerSpec {
283
+ return image.width >= 72
284
+ ? CONTAINER_SPEC_LARGE_IMAGE
285
+ : CONTAINER_SPEC_DEFAULT;
286
+ }
287
+
288
+ export function mapMenuItemToNowPlayingData(
289
+ item: MenuItem,
290
+ isPaused?: boolean,
291
+ nowPlayingLabel = "Now Playing"
292
+ ): ItemData {
293
+ return {
294
+ imageUri: item.icon,
295
+ textLabel1: item.title,
296
+ textLabel2: item.summary ?? "",
297
+ nowPlayingLabel,
298
+ trailingButton: "playPause",
299
+ isSelected: isPaused !== undefined ? !isPaused : item.isSelected,
300
+ };
301
+ }
302
+
303
+ export function mapMenuItemToQueuedData(item: MenuItem): ItemData {
304
+ return {
305
+ imageUri: item.icon,
306
+ textLabel1: item.title,
307
+ textLabel2: item.summary ?? "",
308
+ leadingButton: "remove",
309
+ trailingButton: "drag",
310
+ isSelected: item.isSelected,
311
+ };
312
+ }
313
+
314
+ export function mapMenuItemToPlaylistData(
315
+ item: MenuItem,
316
+ behavior?: SelectionBehavior
317
+ ): ItemData {
318
+ const showSelectionWidget =
319
+ behavior?.selectMode !== "none" && item.trailingButton !== "none";
320
+
321
+ const summary =
322
+ typeof item.summary === "string" ? item.summary.trim() : item.summary;
323
+
324
+ return {
325
+ imageUri: item.icon,
326
+ textLabel1: item.title,
327
+ textLabel2: summary || "",
328
+ trailingButton: showSelectionWidget
329
+ ? item.trailingButton || ("multiSelect" as ItemButtonAction)
330
+ : undefined,
331
+ isSelected: item.isSelected,
332
+ };
333
+ }
334
+
335
+ // ---------------------------------------------------------------------------
336
+ // Inner components
337
+ // ---------------------------------------------------------------------------
338
+
339
+ type ItemContainerProps = {
340
+ image: ImageConfiguration;
341
+ background: BackgroundConfiguration;
342
+ focused: boolean;
343
+ children: React.ReactNode;
344
+ onPress?: () => void;
345
+ };
346
+
347
+ function ItemContainer({
348
+ image,
349
+ background,
350
+ focused,
351
+ children,
352
+ onPress,
353
+ }: ItemContainerProps) {
354
+ const container = resolveContainerSpec(image);
355
+ const insetLeft = background.highlightInsetLeft ?? 0;
356
+ const insetRight = background.highlightInsetRight ?? 0;
357
+
358
+ return (
359
+ <Pressable onPress={onPress}>
360
+ {({ pressed }) => (
361
+ <View
362
+ style={{
363
+ marginLeft: insetLeft,
364
+ marginRight: insetRight,
365
+ backgroundColor:
366
+ pressed || focused
367
+ ? background.focusedColor
368
+ : background.defaultColor,
369
+ borderRadius: background.cornerRadius,
370
+ paddingTop: background.paddingTop ?? container.paddingTop,
371
+ paddingRight: background.paddingRight ?? container.paddingRight,
372
+ paddingBottom: background.paddingBottom ?? container.paddingBottom,
373
+ paddingLeft: background.paddingLeft ?? container.paddingLeft,
374
+ }}
375
+ >
376
+ {children}
377
+ </View>
378
+ )}
379
+ </Pressable>
380
+ );
381
+ }
382
+
383
+ type ItemThumbnailProps = {
384
+ configuration: ImageConfiguration;
385
+ imageUri?: string;
386
+ };
387
+
388
+ function ItemThumbnail({ configuration, imageUri }: ItemThumbnailProps) {
389
+ return (
390
+ <Image
391
+ source={{ uri: imageUri ?? DEFAULT_THUMB_ASSET }}
392
+ style={{
393
+ width: configuration.width,
394
+ height: configuration.height,
395
+ borderWidth: configuration.borderWidth,
396
+ borderColor: configuration.borderColor,
397
+ borderRadius: configuration.cornerRadius,
398
+ }}
399
+ resizeMode="cover"
400
+ />
401
+ );
402
+ }
403
+
404
+ type ItemLabelProps = {
405
+ text: string;
406
+ configuration: LabelConfiguration;
407
+ focused?: boolean;
408
+ };
409
+
410
+ function ItemLabel({ text, configuration, focused = false }: ItemLabelProps) {
411
+ if (!text) {
412
+ return null;
413
+ }
414
+
415
+ const color =
416
+ focused && configuration.focusedFontColor
417
+ ? configuration.focusedFontColor
418
+ : configuration.fontColor;
419
+
420
+ return (
421
+ <Text
422
+ style={{
423
+ color,
424
+ fontSize: configuration.fontSize,
425
+ lineHeight: configuration.lineHeight,
426
+ letterSpacing:
427
+ Platform.OS === "ios"
428
+ ? configuration.iosLetterSpacing
429
+ : configuration.androidLetterSpacing,
430
+ fontFamily: getFontFamily(
431
+ configuration.iosFontFamily,
432
+ configuration.androidFontFamily
433
+ ),
434
+ textTransform: getTextTransformStyle(configuration.textTransform),
435
+ }}
436
+ numberOfLines={1}
437
+ >
438
+ {text}
439
+ </Text>
440
+ );
441
+ }
442
+
443
+ type NowPlayingRowProps = {
444
+ assetConfiguration: NowPlayingAssetConfiguration;
445
+ labelConfiguration: LabelConfiguration;
446
+ label: string;
447
+ };
448
+
449
+ function NowPlayingRow({
450
+ assetConfiguration,
451
+ labelConfiguration,
452
+ label,
453
+ }: NowPlayingRowProps) {
454
+ const asset = assetConfiguration.asset ?? DEFAULT_EQUALIZER_ASSET;
455
+
456
+ return (
457
+ <View
458
+ style={{
459
+ flexDirection: "row",
460
+ alignItems: "center",
461
+ marginTop: NOW_PLAYING_CONTAINER_SPEC.marginTop,
462
+ marginRight: NOW_PLAYING_CONTAINER_SPEC.marginRight,
463
+ marginBottom: NOW_PLAYING_CONTAINER_SPEC.marginBottom,
464
+ marginLeft: NOW_PLAYING_CONTAINER_SPEC.marginLeft,
465
+ }}
466
+ >
467
+ <Image
468
+ source={{ uri: asset }}
469
+ style={{
470
+ width: NOW_PLAYING_ASSET_SPEC.width,
471
+ height: NOW_PLAYING_ASSET_SPEC.height,
472
+ }}
473
+ resizeMode="contain"
474
+ />
475
+ <View style={{ width: NOW_PLAYING_CONTAINER_SPEC.horizontalGutter }} />
476
+ <ItemLabel text={label} configuration={labelConfiguration} />
477
+ </View>
478
+ );
479
+ }
480
+
481
+ type ItemButtonProps = {
482
+ action: ItemButtonAction;
483
+ focused?: boolean;
484
+ onPress?: () => void;
485
+ isSelected?: boolean;
486
+ checkboxAssets?: CheckboxAssetsConfiguration;
487
+ actionButtonAssets?: ActionButtonAssetsConfiguration;
488
+ };
489
+
490
+ function ItemButton({
491
+ action,
492
+ focused = false,
493
+ onPress,
494
+ isSelected = false,
495
+ checkboxAssets,
496
+ actionButtonAssets,
497
+ }: ItemButtonProps) {
498
+ const layout = BUTTON_LAYOUT_SPEC[action];
499
+
500
+ if (!layout) {
501
+ return null;
502
+ }
503
+
504
+ const renderIcon = () => {
505
+ switch (action) {
506
+ case "playPause": {
507
+ const iconSize = layout.assetWidth;
508
+ const iconRadius = iconSize / 2;
509
+
510
+ return (
511
+ <View
512
+ style={{
513
+ width: iconSize,
514
+ height: iconSize,
515
+ borderRadius: iconRadius,
516
+ backgroundColor: "#FFFFFF",
517
+ justifyContent: "center",
518
+ alignItems: "center",
519
+ }}
520
+ >
521
+ {isSelected ? (
522
+ <View
523
+ style={{
524
+ flexDirection: "row",
525
+ justifyContent: "space-between",
526
+ width: 10,
527
+ height: 16,
528
+ }}
529
+ >
530
+ <View
531
+ style={{
532
+ width: 3.5,
533
+ height: 16,
534
+ backgroundColor: "#1e1e1e",
535
+ borderRadius: 1,
536
+ }}
537
+ />
538
+ <View
539
+ style={{
540
+ width: 3.5,
541
+ height: 16,
542
+ backgroundColor: "#1e1e1e",
543
+ borderRadius: 1,
544
+ }}
545
+ />
546
+ </View>
547
+ ) : (
548
+ <View
549
+ style={{
550
+ width: 0,
551
+ height: 0,
552
+ backgroundColor: "transparent",
553
+ borderStyle: "solid",
554
+ borderLeftWidth: 14,
555
+ borderRightWidth: 0,
556
+ borderTopWidth: 8,
557
+ borderBottomWidth: 8,
558
+ borderLeftColor: "#1e1e1e",
559
+ borderRightColor: "transparent",
560
+ borderTopColor: "transparent",
561
+ borderBottomColor: "transparent",
562
+ marginLeft: 3,
563
+ }}
564
+ />
565
+ )}
566
+ </View>
567
+ );
568
+ }
569
+
570
+ case "drag": {
571
+ if (actionButtonAssets?.dragAsset) {
572
+ return (
573
+ <Image
574
+ source={{ uri: actionButtonAssets.dragAsset }}
575
+ style={{
576
+ width: layout.assetWidth,
577
+ height: layout.assetHeight,
578
+ }}
579
+ resizeMode="contain"
580
+ />
581
+ );
582
+ }
583
+
584
+ return (
585
+ <View
586
+ style={{
587
+ width: 20,
588
+ height: 12,
589
+ justifyContent: "space-between",
590
+ }}
591
+ >
592
+ <View
593
+ style={{
594
+ height: 2,
595
+ backgroundColor: "rgba(255, 255, 255, 0.7)",
596
+ borderRadius: 1,
597
+ }}
598
+ />
599
+ <View
600
+ style={{
601
+ height: 2,
602
+ backgroundColor: "rgba(255, 255, 255, 0.7)",
603
+ borderRadius: 1,
604
+ }}
605
+ />
606
+ <View
607
+ style={{
608
+ height: 2,
609
+ backgroundColor: "rgba(255, 255, 255, 0.7)",
610
+ borderRadius: 1,
611
+ }}
612
+ />
613
+ </View>
614
+ );
615
+ }
616
+
617
+ case "remove": {
618
+ if (actionButtonAssets?.removeAsset) {
619
+ return (
620
+ <Image
621
+ source={{ uri: actionButtonAssets.removeAsset }}
622
+ style={{
623
+ width: layout.assetWidth,
624
+ height: layout.assetHeight,
625
+ }}
626
+ resizeMode="contain"
627
+ />
628
+ );
629
+ }
630
+
631
+ return (
632
+ <View
633
+ style={{
634
+ width: 24,
635
+ height: 24,
636
+ borderRadius: 12,
637
+ borderWidth: 1.5,
638
+ borderColor: "rgba(255, 255, 255, 0.7)",
639
+ justifyContent: "center",
640
+ alignItems: "center",
641
+ }}
642
+ >
643
+ <View
644
+ style={{
645
+ width: 10,
646
+ height: 1.5,
647
+ backgroundColor: "rgba(255, 255, 255, 0.7)",
648
+ borderRadius: 1,
649
+ }}
650
+ />
651
+ </View>
652
+ );
653
+ }
654
+
655
+ case "multiSelect": {
656
+ const studioAsset = isSelected
657
+ ? checkboxAssets?.checkedAsset
658
+ : checkboxAssets?.asset;
659
+
660
+ if (studioAsset) {
661
+ return (
662
+ <Image
663
+ source={{ uri: studioAsset }}
664
+ style={{
665
+ width: layout.assetWidth,
666
+ height: layout.assetHeight,
667
+ }}
668
+ resizeMode="contain"
669
+ />
670
+ );
671
+ }
672
+
673
+ if (!isSelected) {
674
+ return (
675
+ <View
676
+ style={{
677
+ width: layout.assetWidth,
678
+ height: layout.assetHeight,
679
+ borderRadius: layout.assetWidth / 2,
680
+ borderWidth: 1.5,
681
+ borderColor: "rgba(255, 255, 255, 0.7)",
682
+ backgroundColor: "transparent",
683
+ }}
684
+ />
685
+ );
686
+ }
687
+
688
+ return (
689
+ <View
690
+ style={{
691
+ width: 24,
692
+ height: 24,
693
+ borderRadius: 12,
694
+ backgroundColor: "#FFFFFF",
695
+ justifyContent: "center",
696
+ alignItems: "center",
697
+ }}
698
+ >
699
+ <View
700
+ style={{
701
+ width: 6,
702
+ height: 10,
703
+ borderBottomWidth: 2,
704
+ borderRightWidth: 2,
705
+ borderColor: "#1e1e1e",
706
+ transform: [{ rotate: "45deg" }],
707
+ marginTop: -2,
708
+ }}
709
+ />
710
+ </View>
711
+ );
712
+ }
713
+
714
+ default:
715
+ return null;
716
+ }
717
+ };
718
+
719
+ const buttonPadding = {
720
+ paddingTop: layout.paddingTop,
721
+ paddingRight: layout.paddingRight,
722
+ paddingBottom: layout.paddingBottom,
723
+ paddingLeft: layout.paddingLeft,
724
+ };
725
+
726
+ // A nested Pressable with no handler swallows the parent row press.
727
+ if (!onPress) {
728
+ return (
729
+ <View pointerEvents="none" style={buttonPadding}>
730
+ {renderIcon()}
731
+ </View>
732
+ );
733
+ }
734
+
735
+ return (
736
+ <Pressable
737
+ onPress={(e: any) => {
738
+ e?.stopPropagation?.();
739
+ onPress();
740
+ }}
741
+ style={({ pressed }) => [
742
+ buttonPadding,
743
+ {
744
+ opacity: pressed || focused ? 0.7 : 1,
745
+ },
746
+ ]}
747
+ >
748
+ {renderIcon()}
749
+ </Pressable>
750
+ );
751
+ }
752
+
753
+ type ItemButtonsProps = {
754
+ leadingButton?: ItemButtonAction;
755
+ trailingButton?: ItemButtonAction;
756
+ focusedLeadingButton?: boolean;
757
+ focusedTrailingButton?: boolean;
758
+ onLeadingButtonPress?: () => void;
759
+ onTrailingButtonPress?: () => void;
760
+ isSelected?: boolean;
761
+ renderHandle?: (element: React.ReactElement) => React.ReactElement;
762
+ checkboxAssets?: CheckboxAssetsConfiguration;
763
+ actionButtonAssets?: ActionButtonAssetsConfiguration;
764
+ };
765
+
766
+ function ItemButtons({
767
+ leadingButton,
768
+ trailingButton,
769
+ focusedLeadingButton = false,
770
+ focusedTrailingButton = false,
771
+ onLeadingButtonPress,
772
+ onTrailingButtonPress,
773
+ isSelected = false,
774
+ renderHandle,
775
+ checkboxAssets,
776
+ actionButtonAssets,
777
+ }: ItemButtonsProps) {
778
+ if (!leadingButton && !trailingButton) {
779
+ return null;
780
+ }
781
+
782
+ return (
783
+ <View style={styles.buttonsRow}>
784
+ {leadingButton ? (
785
+ <>
786
+ <ItemButton
787
+ action={leadingButton}
788
+ focused={focusedLeadingButton}
789
+ onPress={onLeadingButtonPress}
790
+ isSelected={isSelected}
791
+ checkboxAssets={checkboxAssets}
792
+ actionButtonAssets={actionButtonAssets}
793
+ />
794
+ <View style={{ width: BUTTONS_CONTAINER_HORIZONTAL_GUTTER }} />
795
+ </>
796
+ ) : null}
797
+ {trailingButton ? (
798
+ trailingButton === "drag" ? (
799
+ renderHandle ? (
800
+ renderHandle(
801
+ <ItemButton
802
+ action={trailingButton}
803
+ focused={focusedTrailingButton}
804
+ onPress={onTrailingButtonPress}
805
+ isSelected={isSelected}
806
+ checkboxAssets={checkboxAssets}
807
+ actionButtonAssets={actionButtonAssets}
808
+ />
809
+ )
810
+ ) : (
811
+ <ItemButton
812
+ action={trailingButton}
813
+ focused={focusedTrailingButton}
814
+ onPress={onTrailingButtonPress}
815
+ isSelected={isSelected}
816
+ checkboxAssets={checkboxAssets}
817
+ actionButtonAssets={actionButtonAssets}
818
+ />
819
+ )
820
+ ) : (
821
+ <ItemButton
822
+ action={trailingButton}
823
+ focused={focusedTrailingButton}
824
+ onPress={onTrailingButtonPress}
825
+ isSelected={isSelected}
826
+ checkboxAssets={checkboxAssets}
827
+ actionButtonAssets={actionButtonAssets}
828
+ />
829
+ )
830
+ ) : null}
831
+ </View>
832
+ );
833
+ }
834
+
835
+ // ---------------------------------------------------------------------------
836
+ // AudioPlayerTrackItem
837
+ // ---------------------------------------------------------------------------
838
+
839
+ export function AudioPlayerTrackItem({
840
+ configuration,
841
+ data,
842
+ focused = false,
843
+ focusedLeadingButton = false,
844
+ focusedTrailingButton = false,
845
+ onPress,
846
+ onLeadingButtonPress,
847
+ onTrailingButtonPress,
848
+ renderHandle,
849
+ }: AudioPlayerTrackItemProps) {
850
+ const modalBlocksStyle = useModalBlocksStyle();
851
+ const showNowPlaying = !!data.nowPlayingLabel;
852
+
853
+ const itemBlock =
854
+ showNowPlaying && modalBlocksStyle?.nowPlayingItem
855
+ ? modalBlocksStyle.nowPlayingItem
856
+ : modalBlocksStyle?.item;
857
+
858
+ const effectiveConfig = React.useMemo(() => {
859
+ return mergeItemConfiguration(configuration, itemBlock);
860
+ }, [configuration, itemBlock]);
861
+
862
+ const {
863
+ background,
864
+ image,
865
+ textLabel1,
866
+ textLabel2,
867
+ nowPlayingLabel,
868
+ nowPlayingAsset,
869
+ checkboxAssets,
870
+ actionButtonAssets,
871
+ } = effectiveConfig;
872
+
873
+ const container = resolveContainerSpec(image);
874
+
875
+ const handleTrailingButtonPress =
876
+ onTrailingButtonPress ??
877
+ (data.trailingButton === "multiSelect" ? onPress : undefined);
878
+
879
+ return (
880
+ <ItemContainer
881
+ image={image}
882
+ background={background}
883
+ focused={focused}
884
+ onPress={onPress}
885
+ >
886
+ <View style={styles.row}>
887
+ <ItemThumbnail configuration={image} imageUri={data.imageUri} />
888
+ <View style={{ width: container.horizontalGutter }} />
889
+ <View style={styles.contentColumn}>
890
+ {showNowPlaying ? (
891
+ <NowPlayingRow
892
+ assetConfiguration={nowPlayingAsset}
893
+ labelConfiguration={nowPlayingLabel}
894
+ label={data.nowPlayingLabel}
895
+ />
896
+ ) : null}
897
+ {showNowPlaying && (data.textLabel1 || data.textLabel2) ? (
898
+ <View style={{ height: container.verticalGutter }} />
899
+ ) : null}
900
+ {data.textLabel1 ? (
901
+ <ItemLabel
902
+ text={data.textLabel1}
903
+ configuration={textLabel1}
904
+ focused={focused}
905
+ />
906
+ ) : null}
907
+ {data.textLabel1 && data.textLabel2 ? (
908
+ <View style={{ height: container.verticalGutter }} />
909
+ ) : null}
910
+ {data.textLabel2 ? (
911
+ <ItemLabel text={data.textLabel2} configuration={textLabel2} />
912
+ ) : null}
913
+ </View>
914
+ <ItemButtons
915
+ leadingButton={data.leadingButton}
916
+ trailingButton={data.trailingButton}
917
+ focusedLeadingButton={focusedLeadingButton}
918
+ focusedTrailingButton={focusedTrailingButton}
919
+ onLeadingButtonPress={onLeadingButtonPress}
920
+ onTrailingButtonPress={handleTrailingButtonPress}
921
+ isSelected={data.isSelected}
922
+ renderHandle={renderHandle}
923
+ checkboxAssets={checkboxAssets}
924
+ actionButtonAssets={actionButtonAssets}
925
+ />
926
+ </View>
927
+ </ItemContainer>
928
+ );
929
+ }
930
+
931
+ // ---------------------------------------------------------------------------
932
+ // Variant presets — collections of configuration keys (not a variant field)
933
+ // ---------------------------------------------------------------------------
934
+
935
+ /** Variant 1 — Now Playing */
936
+ export const VARIANT_NOW_PLAYING: ItemConfiguration = {
937
+ background: {
938
+ defaultColor: "transparent",
939
+ focusedColor: "transparent",
940
+ cornerRadius: 0,
941
+ paddingTop: 8,
942
+ paddingRight: 20,
943
+ paddingBottom: 8,
944
+ paddingLeft: 20,
945
+ },
946
+ image: {
947
+ width: 72,
948
+ height: 72,
949
+ borderWidth: 1,
950
+ borderColor: "rgba(255, 255, 255, 0.1)",
951
+ cornerRadius: 8,
952
+ },
953
+ nowPlayingAsset: {},
954
+ nowPlayingLabel: DEFAULT_NOW_PLAYING_LABEL_CONFIGURATION,
955
+ textLabel1: DEFAULT_TEXT_LABEL_1_CONFIGURATION,
956
+ textLabel2: DEFAULT_TEXT_LABEL_2_CONFIGURATION,
957
+ };
958
+
959
+ /** Default / Variant 2 — Queued Item */
960
+ export const VARIANT_QUEUED_ITEM: ItemConfiguration = {
961
+ background: {
962
+ defaultColor: "transparent",
963
+ focusedColor: "rgba(62, 62, 62, 1)",
964
+ cornerRadius: 12,
965
+ paddingTop: 8,
966
+ paddingRight: 10,
967
+ paddingBottom: 8,
968
+ paddingLeft: 10,
969
+ highlightInsetLeft: 10,
970
+ highlightInsetRight: 10,
971
+ },
972
+ image: {
973
+ width: 48,
974
+ height: 48,
975
+ borderWidth: 1,
976
+ borderColor: "rgba(255, 255, 255, 0.1)",
977
+ cornerRadius: 6,
978
+ },
979
+ nowPlayingAsset: {},
980
+ nowPlayingLabel: DEFAULT_NOW_PLAYING_LABEL_CONFIGURATION,
981
+ textLabel1: DEFAULT_TEXT_LABEL_1_CONFIGURATION,
982
+ textLabel2: DEFAULT_TEXT_LABEL_2_CONFIGURATION,
983
+ };
984
+
985
+ /** Variant 3 — Playlist Item */
986
+ export const VARIANT_PLAYLIST_ITEM: ItemConfiguration = {
987
+ background: {
988
+ defaultColor: "transparent",
989
+ focusedColor: "rgba(62, 62, 62, 1)",
990
+ cornerRadius: 12,
991
+ paddingTop: 8,
992
+ paddingRight: 10,
993
+ paddingBottom: 8,
994
+ paddingLeft: 10,
995
+ },
996
+ image: {
997
+ width: 48,
998
+ height: 48,
999
+ borderWidth: 1,
1000
+ borderColor: "rgba(255, 255, 255, 0.1)",
1001
+ cornerRadius: 6,
1002
+ },
1003
+ nowPlayingAsset: {},
1004
+ nowPlayingLabel: DEFAULT_NOW_PLAYING_LABEL_CONFIGURATION,
1005
+ textLabel1: DEFAULT_TEXT_LABEL_1_CONFIGURATION,
1006
+ textLabel2: DEFAULT_TEXT_LABEL_2_CONFIGURATION,
1007
+ };