@applicaster/zapp-react-native-ui-components 16.0.0-rc.33 → 16.0.0-rc.34

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 (22) hide show
  1. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/placement.test.ts +47 -21
  2. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/placement.ts +34 -6
  3. package/Components/MasterCell/DefaultComponents/ImageContainer/index.tsx +5 -3
  4. package/Components/MasterCell/DefaultComponents/PressableView.tsx +3 -3
  5. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/ActionButton.tsx +40 -2
  6. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/AssetComponent.tsx +15 -4
  7. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Button.ts +56 -22
  8. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Spacer.ts +6 -4
  9. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/TextLabelsContainer.ts +3 -0
  10. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/PressableView.test.tsx +1 -1
  11. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/index.test.ts +15 -4
  12. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/index.ts +23 -16
  13. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/__tests__/insertButtons.test.ts +8 -3
  14. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/index.ts +2 -2
  15. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/__tests__/insertButtonsBetweenLabels.test.ts +45 -13
  16. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/index.ts +2 -2
  17. package/Components/ModalComponent/AudioPlayer/Components/Action.tsx +368 -0
  18. package/Components/ModalComponent/AudioPlayer/Components/Button.tsx +424 -0
  19. package/Components/ModalComponent/AudioPlayer/Components/Header.tsx +728 -0
  20. package/Components/ModalComponent/AudioPlayer/Components/Item.tsx +709 -0
  21. package/Components/ModalComponent/AudioPlayer/Components/index.ts +7 -0
  22. package/package.json +5 -5
@@ -0,0 +1,424 @@
1
+ /**
2
+ * Modal Building Block — Button
3
+ *
4
+ * Single-file Expo Snack demo for the Button building block spec.
5
+ * Two variants (with / without leading icon) are driven only by configuration + data.
6
+ *
7
+ * Green (configurable): background, title, icons (size + leading icon enable/action type)
8
+ * Black (hardcoded): action type → icon asset mapping
9
+ */
10
+
11
+ import React from "react";
12
+ import { Image, Platform, StyleSheet, Text, View } from "react-native";
13
+
14
+ const styles = StyleSheet.create({
15
+ container: {
16
+ alignSelf: "stretch",
17
+ alignItems: "center",
18
+ justifyContent: "center",
19
+ },
20
+ contentRow: {
21
+ flexDirection: "row",
22
+ alignItems: "center",
23
+ justifyContent: "center",
24
+ },
25
+ title: {
26
+ textAlign: "center",
27
+ },
28
+ demoScreen: {
29
+ flex: 1,
30
+ backgroundColor: "#1C1C1E",
31
+ paddingTop: 64,
32
+ paddingHorizontal: 24,
33
+ gap: 12,
34
+ },
35
+ demoHeading: {
36
+ color: "#FFFFFF",
37
+ fontSize: 20,
38
+ fontWeight: "600",
39
+ marginBottom: 8,
40
+ },
41
+ demoLabel: {
42
+ color: "#999999",
43
+ fontSize: 13,
44
+ marginTop: 8,
45
+ },
46
+ });
47
+
48
+ // ---------------------------------------------------------------------------
49
+ // Configuration (green / editable fields from spec)
50
+ // ---------------------------------------------------------------------------
51
+
52
+ export type ButtonActionType =
53
+ | "addToQueue"
54
+ | "addToPlaylist"
55
+ | "goToPlaylist"
56
+ | "confirm"
57
+ | "cancel";
58
+
59
+ export type TextTransform =
60
+ | "default"
61
+ | "lowercase"
62
+ | "uppercase"
63
+ | "capitalize";
64
+
65
+ export type BackgroundConfiguration = {
66
+ defaultColor: string;
67
+ focusedColor: string;
68
+ inactiveColor: string;
69
+ cornerRadius: number;
70
+ paddingTop: number;
71
+ paddingRight: number;
72
+ paddingBottom: number;
73
+ paddingLeft: number;
74
+ horizontalGutter: number;
75
+ };
76
+
77
+ export type TitleConfiguration = {
78
+ fontColor: string;
79
+ focusedFontColor: string;
80
+ inactiveFontColor: string;
81
+ iosFontFamily: string;
82
+ androidFontFamily: string;
83
+ fontSize: number;
84
+ lineHeight: number;
85
+ iosLetterSpacing: number;
86
+ androidLetterSpacing: number;
87
+ textTransform: TextTransform;
88
+ };
89
+
90
+ export type LeadingIconConfiguration = {
91
+ /** Enable: On / Off */
92
+ enabled: boolean;
93
+ /** Decides which icon will be displayed when enabled */
94
+ actionType?: ButtonActionType;
95
+ };
96
+
97
+ export type IconsConfiguration = {
98
+ width: number;
99
+ height: number;
100
+ leadingIcon: LeadingIconConfiguration;
101
+ };
102
+
103
+ export type ButtonConfiguration = {
104
+ background: BackgroundConfiguration;
105
+ title: TitleConfiguration;
106
+ icons: IconsConfiguration;
107
+ };
108
+
109
+ export const DEFAULT_BACKGROUND_CONFIGURATION: BackgroundConfiguration = {
110
+ defaultColor: "#FFFFFF",
111
+ focusedColor: "#E0E0E0",
112
+ inactiveColor: "#999999",
113
+ cornerRadius: 12,
114
+ paddingTop: 12,
115
+ paddingRight: 12,
116
+ paddingBottom: 12,
117
+ paddingLeft: 12,
118
+ horizontalGutter: 6,
119
+ };
120
+
121
+ export const DEFAULT_TITLE_CONFIGURATION: TitleConfiguration = {
122
+ fontColor: "#000000",
123
+ focusedFontColor: "#000000",
124
+ inactiveFontColor: "#000000",
125
+ iosFontFamily: "SFProText-Bold",
126
+ androidFontFamily: "Roboto-Bold",
127
+ fontSize: 15,
128
+ lineHeight: 24,
129
+ iosLetterSpacing: -0.2,
130
+ androidLetterSpacing: 0,
131
+ textTransform: "default",
132
+ };
133
+
134
+ export const DEFAULT_ICONS_CONFIGURATION: IconsConfiguration = {
135
+ width: 24,
136
+ height: 24,
137
+ leadingIcon: {
138
+ enabled: false,
139
+ },
140
+ };
141
+
142
+ // ---------------------------------------------------------------------------
143
+ // Data (runtime / localization — not styling config)
144
+ // ---------------------------------------------------------------------------
145
+
146
+ export type ButtonData = {
147
+ title: string;
148
+ disabled?: boolean;
149
+ };
150
+
151
+ export type ItemProps = {
152
+ configuration: ButtonConfiguration;
153
+ data: ButtonData;
154
+ disabled?: boolean;
155
+ focused?: boolean;
156
+ };
157
+
158
+ // ---------------------------------------------------------------------------
159
+ // Hardcoded spec values (black fields)
160
+ // ---------------------------------------------------------------------------
161
+
162
+ // Hardcoded: action type → icon asset mapping (base64 PNG)
163
+ const ACTION_ICON_ASSETS: Record<ButtonActionType, string> = {
164
+ addToQueue:
165
+ "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAKUlEQVR42mNgGAVDCfxHw6MWjFpAvEGk4lELKLdgNJmOWkA7C0YBbQAAwiZvkR9eFgQAAAAASUVORK5CYII=",
166
+ addToPlaylist:
167
+ "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAJklEQVR42mNgGAUjFvzHgmluATF4cFswGsmjkTwayaORPAqGLwAA6Jpxj9Ms9QMAAAAASUVORK5CYII=",
168
+ goToPlaylist:
169
+ "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAALklEQVR42mNgGAWjYNiD/7Q2/D8tLflPiSX/ycQ0t+D/yLGAEkeMWjAKRgG9AQB4zlSshwSzWwAAAABJRU5ErkJggg==",
170
+ confirm:
171
+ "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAJElEQVR42mNgGAWjYBSQCv4TwKMWDHELho/hNE/3o2AUDDcAAPv0QMAyy1CtAAAAAElFTkSuQmCC",
172
+ cancel:
173
+ "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAASElEQVR42mNgGAUjGvynkVoUTf+pqA6nxv8UqiHbEooNx2cQ1QzHZiDVDcdlCcOQsoCmQUTTSKZpMqV5RqNLUUHTwm4UDBMAALBLQ73LTvoyAAAAAElFTkSuQmCC",
174
+ };
175
+
176
+ function getTextTransformStyle(
177
+ textTransform: TextTransform
178
+ ): "none" | "lowercase" | "uppercase" | "capitalize" {
179
+ return textTransform === "default" ? "none" : textTransform;
180
+ }
181
+
182
+ function shouldShowLeadingIcon(leadingIcon: LeadingIconConfiguration) {
183
+ return leadingIcon.enabled && !!leadingIcon.actionType;
184
+ }
185
+
186
+ // ---------------------------------------------------------------------------
187
+ // Inner components (hardcoded styling lives here)
188
+ // ---------------------------------------------------------------------------
189
+
190
+ type ButtonContainerProps = {
191
+ focused: boolean;
192
+ disabled: boolean;
193
+ configuration: BackgroundConfiguration;
194
+ children: React.ReactNode;
195
+ };
196
+
197
+ function ButtonContainer({
198
+ focused,
199
+ disabled,
200
+ configuration,
201
+ children,
202
+ }: ButtonContainerProps) {
203
+ const backgroundColor = disabled
204
+ ? configuration.inactiveColor
205
+ : focused
206
+ ? configuration.focusedColor
207
+ : configuration.defaultColor;
208
+
209
+ return (
210
+ <View
211
+ style={[
212
+ styles.container,
213
+ {
214
+ backgroundColor,
215
+ borderRadius: configuration.cornerRadius,
216
+ paddingTop: configuration.paddingTop,
217
+ paddingRight: configuration.paddingRight,
218
+ paddingBottom: configuration.paddingBottom,
219
+ paddingLeft: configuration.paddingLeft,
220
+ },
221
+ ]}
222
+ >
223
+ {children}
224
+ </View>
225
+ );
226
+ }
227
+
228
+ type ButtonTitleProps = {
229
+ title: string;
230
+ focused: boolean;
231
+ disabled: boolean;
232
+ configuration: TitleConfiguration;
233
+ };
234
+
235
+ function ButtonTitle({
236
+ title,
237
+ focused,
238
+ disabled,
239
+ configuration,
240
+ }: ButtonTitleProps) {
241
+ const color = disabled
242
+ ? configuration.inactiveFontColor
243
+ : focused
244
+ ? configuration.focusedFontColor
245
+ : configuration.fontColor;
246
+
247
+ const letterSpacing =
248
+ Platform.OS === "ios"
249
+ ? configuration.iosLetterSpacing
250
+ : configuration.androidLetterSpacing;
251
+
252
+ return (
253
+ <Text
254
+ style={[
255
+ styles.title,
256
+ {
257
+ color,
258
+ fontSize: configuration.fontSize,
259
+ lineHeight: configuration.lineHeight,
260
+ letterSpacing,
261
+ fontFamily: Platform.select({
262
+ ios: configuration.iosFontFamily,
263
+ android: configuration.androidFontFamily,
264
+ default: configuration.androidFontFamily,
265
+ }),
266
+ textTransform: getTextTransformStyle(configuration.textTransform),
267
+ },
268
+ ]}
269
+ numberOfLines={1}
270
+ >
271
+ {title}
272
+ </Text>
273
+ );
274
+ }
275
+
276
+ type ButtonLeadingIconProps = {
277
+ actionType: ButtonActionType;
278
+ configuration: IconsConfiguration;
279
+ };
280
+
281
+ function ButtonLeadingIcon({
282
+ actionType,
283
+ configuration,
284
+ }: ButtonLeadingIconProps) {
285
+ return (
286
+ <Image
287
+ source={{ uri: ACTION_ICON_ASSETS[actionType] }}
288
+ style={{
289
+ width: configuration.width,
290
+ height: configuration.height,
291
+ }}
292
+ resizeMode="contain"
293
+ />
294
+ );
295
+ }
296
+
297
+ // ---------------------------------------------------------------------------
298
+ // Item — single building block driven by configuration + data
299
+ // ---------------------------------------------------------------------------
300
+
301
+ export function Item({
302
+ configuration,
303
+ data,
304
+ disabled,
305
+ focused = false,
306
+ }: ItemProps) {
307
+ const {
308
+ background: backgroundConfiguration,
309
+ title: titleConfiguration,
310
+ icons: iconsConfiguration,
311
+ } = configuration;
312
+
313
+ const { title } = data;
314
+ const isDisabled = disabled ?? data.disabled ?? false;
315
+
316
+ const { leadingIcon } = iconsConfiguration;
317
+ const showIcon = shouldShowLeadingIcon(leadingIcon);
318
+
319
+ return (
320
+ <ButtonContainer
321
+ focused={focused}
322
+ disabled={isDisabled}
323
+ configuration={backgroundConfiguration}
324
+ >
325
+ <View style={styles.contentRow}>
326
+ {showIcon ? (
327
+ <>
328
+ <ButtonLeadingIcon
329
+ actionType={leadingIcon.actionType}
330
+ configuration={iconsConfiguration}
331
+ />
332
+ <View style={{ width: backgroundConfiguration.horizontalGutter }} />
333
+ </>
334
+ ) : null}
335
+ <ButtonTitle
336
+ title={title}
337
+ focused={focused}
338
+ disabled={isDisabled}
339
+ configuration={titleConfiguration}
340
+ />
341
+ </View>
342
+ </ButtonContainer>
343
+ );
344
+ }
345
+
346
+ // ---------------------------------------------------------------------------
347
+ // Snack demo — two Button variants from configuration only
348
+ // ---------------------------------------------------------------------------
349
+
350
+ export const VARIANT_TEXT_ONLY: ButtonConfiguration = {
351
+ background: DEFAULT_BACKGROUND_CONFIGURATION,
352
+ title: DEFAULT_TITLE_CONFIGURATION,
353
+ icons: DEFAULT_ICONS_CONFIGURATION,
354
+ };
355
+
356
+ export const VARIANT_WITH_ICON: ButtonConfiguration = {
357
+ background: DEFAULT_BACKGROUND_CONFIGURATION,
358
+ title: DEFAULT_TITLE_CONFIGURATION,
359
+ icons: {
360
+ ...DEFAULT_ICONS_CONFIGURATION,
361
+ leadingIcon: {
362
+ enabled: true,
363
+ actionType: "addToPlaylist",
364
+ },
365
+ },
366
+ };
367
+
368
+ export const VARIANT_UPPERCASE_TITLE: ButtonConfiguration = {
369
+ background: DEFAULT_BACKGROUND_CONFIGURATION,
370
+ title: {
371
+ ...DEFAULT_TITLE_CONFIGURATION,
372
+ textTransform: "uppercase",
373
+ },
374
+ icons: DEFAULT_ICONS_CONFIGURATION,
375
+ };
376
+
377
+ export const VARIANT_LARGE_ICON: ButtonConfiguration = {
378
+ background: DEFAULT_BACKGROUND_CONFIGURATION,
379
+ title: DEFAULT_TITLE_CONFIGURATION,
380
+ icons: {
381
+ width: 32,
382
+ height: 32,
383
+ leadingIcon: {
384
+ enabled: true,
385
+ actionType: "addToQueue",
386
+ },
387
+ },
388
+ };
389
+
390
+ export default function App() {
391
+ return (
392
+ <View style={styles.demoScreen}>
393
+ <Text style={styles.demoHeading}>Modal Button Building Block</Text>
394
+
395
+ <Text style={styles.demoLabel}>Variant 1 — Text only (confirmation)</Text>
396
+ <Item configuration={VARIANT_TEXT_ONLY} data={{ title: "Confirm" }} />
397
+
398
+ <Text style={styles.demoLabel}>Variant 2 — Leading icon + text</Text>
399
+ <Item
400
+ configuration={VARIANT_WITH_ICON}
401
+ data={{ title: "Add To Playlist" }}
402
+ />
403
+
404
+ <Text style={styles.demoLabel}>Inactive state</Text>
405
+ <Item
406
+ configuration={VARIANT_TEXT_ONLY}
407
+ data={{ title: "Unavailable" }}
408
+ disabled
409
+ />
410
+
411
+ <Text style={styles.demoLabel}>Custom title config — uppercase</Text>
412
+ <Item
413
+ configuration={VARIANT_UPPERCASE_TITLE}
414
+ data={{ title: "Save Changes" }}
415
+ />
416
+
417
+ <Text style={styles.demoLabel}>Custom icons config — 32×32</Text>
418
+ <Item
419
+ configuration={VARIANT_LARGE_ICON}
420
+ data={{ title: "Add To Queue" }}
421
+ />
422
+ </View>
423
+ );
424
+ }