@applicaster/zapp-react-native-ui-components 15.1.14 → 15.1.15-rc.0
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/Components/GeneralContentScreen/GeneralContentScreenHookAdapter.tsx +15 -0
- package/Components/MasterCell/DefaultComponents/ActionButton.tsx +4 -1
- package/Components/ModalComponent/ActionsBottomSheet/ActionButton.tsx +56 -0
- package/Components/ModalComponent/ActionsBottomSheet/__tests__/openActionsBottomSheet.test.ts +38 -0
- package/Components/ModalComponent/ActionsBottomSheet/boundActionButton.tsx +24 -0
- package/Components/ModalComponent/ActionsBottomSheet/index.ts +9 -0
- package/Components/ModalComponent/ActionsBottomSheet/openActionsBottomSheet.ts +61 -0
- package/Components/ModalComponent/AudioPlayer/Components/Button.tsx +58 -97
- package/Components/ModalComponent/AudioPlayer/Components/Header.tsx +142 -142
- package/Components/ModalComponent/AudioPlayer/Components/Input.tsx +628 -0
- package/Components/ModalComponent/AudioPlayer/Components/Item.tsx +448 -175
- package/Components/ModalComponent/AudioPlayer/Components/NowPlayingHeaderSection.tsx +94 -0
- package/Components/ModalComponent/AudioPlayer/Components/__tests__/Item.test.tsx +113 -0
- package/Components/ModalComponent/AudioPlayer/Components/index.ts +6 -4
- package/Components/ModalComponent/AudioPlayer/utils/__tests__/mergeStyles.test.ts +248 -0
- package/Components/ModalComponent/AudioPlayer/utils/mergeStyles.ts +386 -0
- package/Components/ModalComponent/BottomSheetModalContent.tsx +425 -66
- package/Components/ModalComponent/Button/index.tsx +5 -3
- package/Components/ModalComponent/Header/index.tsx +20 -7
- package/Components/ModalComponent/Header/utils.ts +1 -1
- package/Components/ModalComponent/ModalBlocksStyleContext.tsx +10 -0
- package/Components/ModalComponent/SortableList.tsx +157 -0
- package/Components/ModalComponent/SortableList.web.tsx +102 -0
- package/Components/ModalComponent/TextInputContent.tsx +112 -0
- package/Components/ModalComponent/__tests__/BottomSheetModalContent.test.tsx +255 -0
- package/Components/ModalComponent/__tests__/TextInputContent.test.tsx +156 -0
- package/Components/ModalComponent/__tests__/showTextInput.test.ts +95 -0
- package/Components/ModalComponent/index.tsx +18 -9
- package/Components/ModalComponent/presets/__tests__/getCell.test.ts +90 -0
- package/Components/ModalComponent/presets/dynamicCollectionCell.tsx +78 -0
- package/Components/ModalComponent/presets/index.ts +27 -0
- package/Components/ModalComponent/presets/selectableCell.tsx +28 -0
- package/Components/ModalComponent/presets/standardCell.tsx +41 -0
- package/Components/ModalComponent/presets/types.ts +35 -0
- package/Components/ModalComponent/showTextInput.ts +43 -0
- package/Components/ModalComponent/utils.ts +82 -21
- package/Components/River/ComponentsMap/ComponentsMap.tsx +87 -60
- package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +139 -75
- package/Components/River/__tests__/componentsMap.test.js +8 -3
- package/Components/Screen/navigationHandler.ts +8 -8
- package/Contexts/ModalNavigationContext/__tests__/index.test.tsx +15 -0
- package/Contexts/ModalNavigationContext/index.tsx +2 -2
- package/Hooks/useEntryActionsExpander.ts +63 -0
- package/package.json +6 -5
- package/Components/ModalComponent/AudioPlayer/Components/Action.tsx +0 -368
- /package/Decorators/ZappPipesDataConnector/__tests__/{Hero.js → Hero.tsx} +0 -0
|
@@ -12,7 +12,14 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import React from "react";
|
|
15
|
-
import {
|
|
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";
|
|
16
23
|
|
|
17
24
|
const styles = StyleSheet.create({
|
|
18
25
|
row: {
|
|
@@ -27,25 +34,6 @@ const styles = StyleSheet.create({
|
|
|
27
34
|
flexDirection: "row",
|
|
28
35
|
alignItems: "center",
|
|
29
36
|
},
|
|
30
|
-
demoScreen: {
|
|
31
|
-
flex: 1,
|
|
32
|
-
backgroundColor: "#1C1C1E",
|
|
33
|
-
paddingTop: 64,
|
|
34
|
-
paddingHorizontal: 0,
|
|
35
|
-
gap: 16,
|
|
36
|
-
},
|
|
37
|
-
demoHeading: {
|
|
38
|
-
color: "#FFFFFF",
|
|
39
|
-
fontSize: 20,
|
|
40
|
-
fontWeight: "600",
|
|
41
|
-
marginBottom: 8,
|
|
42
|
-
paddingHorizontal: 16,
|
|
43
|
-
},
|
|
44
|
-
demoLabel: {
|
|
45
|
-
color: "#999999",
|
|
46
|
-
fontSize: 13,
|
|
47
|
-
paddingHorizontal: 16,
|
|
48
|
-
},
|
|
49
37
|
});
|
|
50
38
|
|
|
51
39
|
// ---------------------------------------------------------------------------
|
|
@@ -62,6 +50,10 @@ export type BackgroundConfiguration = {
|
|
|
62
50
|
defaultColor: string;
|
|
63
51
|
focusedColor: string;
|
|
64
52
|
cornerRadius: number;
|
|
53
|
+
paddingTop?: number;
|
|
54
|
+
paddingRight?: number;
|
|
55
|
+
paddingBottom?: number;
|
|
56
|
+
paddingLeft?: number;
|
|
65
57
|
};
|
|
66
58
|
|
|
67
59
|
export type ImageConfiguration = {
|
|
@@ -88,6 +80,20 @@ export type NowPlayingAssetConfiguration = {
|
|
|
88
80
|
asset?: string;
|
|
89
81
|
};
|
|
90
82
|
|
|
83
|
+
export type CheckboxAssetsConfiguration = {
|
|
84
|
+
/** Unchecked multiSelect control (item.checkbox_asset). */
|
|
85
|
+
asset?: string;
|
|
86
|
+
/** Checked multiSelect control (item.checked_checkbox_asset). */
|
|
87
|
+
checkedAsset?: string;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
export type ActionButtonAssetsConfiguration = {
|
|
91
|
+
/** Leading remove control on Edit Order rows. */
|
|
92
|
+
removeAsset?: string;
|
|
93
|
+
/** Trailing drag handle on Edit Order / queue rows. */
|
|
94
|
+
dragAsset?: string;
|
|
95
|
+
};
|
|
96
|
+
|
|
91
97
|
export type ItemConfiguration = {
|
|
92
98
|
background: BackgroundConfiguration;
|
|
93
99
|
image: ImageConfiguration;
|
|
@@ -95,52 +101,10 @@ export type ItemConfiguration = {
|
|
|
95
101
|
nowPlayingLabel: LabelConfiguration;
|
|
96
102
|
textLabel1: LabelConfiguration;
|
|
97
103
|
textLabel2: LabelConfiguration;
|
|
104
|
+
checkboxAssets?: CheckboxAssetsConfiguration;
|
|
105
|
+
actionButtonAssets?: ActionButtonAssetsConfiguration;
|
|
98
106
|
};
|
|
99
107
|
|
|
100
|
-
export interface Action {
|
|
101
|
-
type: string;
|
|
102
|
-
payload?: any;
|
|
103
|
-
options?: any;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
export interface Header {
|
|
107
|
-
title: string;
|
|
108
|
-
subtitle?: string;
|
|
109
|
-
icon?: string;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
export interface Content {
|
|
113
|
-
title: string;
|
|
114
|
-
stickyTitle?: boolean;
|
|
115
|
-
items: MenuItem[];
|
|
116
|
-
itemsUrl?: string;
|
|
117
|
-
action?: Action;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
export interface Menu {
|
|
121
|
-
header?: Header;
|
|
122
|
-
content: Content;
|
|
123
|
-
closeButton?: boolean;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
export interface PresentSubMenuAction extends Action {
|
|
127
|
-
menu: Menu;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
export interface SecondaryAction {
|
|
131
|
-
action: Action | PresentSubMenuAction;
|
|
132
|
-
icon?: string;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
export interface MenuItem {
|
|
136
|
-
title: string;
|
|
137
|
-
summary?: string;
|
|
138
|
-
icon?: string;
|
|
139
|
-
isSelected?: boolean;
|
|
140
|
-
action?: Action | PresentSubMenuAction;
|
|
141
|
-
secondaryAction?: SecondaryAction;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
108
|
export const DEFAULT_TEXT_LABEL_1_CONFIGURATION: LabelConfiguration = {
|
|
145
109
|
fontColor: "#FFFFFF",
|
|
146
110
|
focusedFontColor: "#FFFFFF",
|
|
@@ -191,14 +155,19 @@ export type ItemData = {
|
|
|
191
155
|
leadingButton?: ItemButtonAction;
|
|
192
156
|
/** Button 1 (right most) — hardcoded action decides icon. */
|
|
193
157
|
trailingButton?: ItemButtonAction;
|
|
158
|
+
isSelected?: boolean;
|
|
194
159
|
};
|
|
195
160
|
|
|
196
|
-
export type
|
|
161
|
+
export type AudioPlayerTrackItemProps = {
|
|
197
162
|
configuration: ItemConfiguration;
|
|
198
163
|
data: ItemData;
|
|
199
164
|
focused?: boolean;
|
|
200
165
|
focusedLeadingButton?: boolean;
|
|
201
166
|
focusedTrailingButton?: boolean;
|
|
167
|
+
onPress?: () => void;
|
|
168
|
+
onLeadingButtonPress?: () => void;
|
|
169
|
+
onTrailingButtonPress?: () => void;
|
|
170
|
+
renderHandle?: (element: React.ReactElement) => React.ReactElement;
|
|
202
171
|
};
|
|
203
172
|
|
|
204
173
|
// ---------------------------------------------------------------------------
|
|
@@ -234,9 +203,9 @@ const CONTAINER_SPEC_LARGE_IMAGE: ContainerSpec = {
|
|
|
234
203
|
|
|
235
204
|
const CONTAINER_SPEC_DEFAULT: ContainerSpec = {
|
|
236
205
|
paddingTop: 8,
|
|
237
|
-
paddingRight:
|
|
206
|
+
paddingRight: 20,
|
|
238
207
|
paddingBottom: 8,
|
|
239
|
-
paddingLeft:
|
|
208
|
+
paddingLeft: 20,
|
|
240
209
|
horizontalGutter: 12,
|
|
241
210
|
verticalGutter: 2,
|
|
242
211
|
};
|
|
@@ -297,16 +266,6 @@ const DEFAULT_THUMB_ASSET =
|
|
|
297
266
|
const DEFAULT_EQUALIZER_ASSET =
|
|
298
267
|
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAI0lEQVR42mNgGNTgPxTQzwB0DVQ3gKCBowbQwYAhkBIHBAAA9TvPMeOFaLMAAAAASUVORK5CYII=";
|
|
299
268
|
|
|
300
|
-
const BUTTON_ASSETS: Record<ItemButtonAction, string> = {
|
|
301
|
-
playPause:
|
|
302
|
-
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAYAAAAehFoBAAAAdklEQVR42u3ZwRUAERAEUfkn3TaF5TFTrSuCf3CgjZGg6ast7G8YaAlcB8JAj8F1MRR2G63CUNglNAqsRvmB1TAfsBoXcMABV4MFKEci4IADNrkAed2H80R66SHqvUvglh/ktoZcL5H7MHKBx/5xYH+RsP90t5t2UpF6+22a+AAAAABJRU5ErkJggg==",
|
|
303
|
-
drag: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAIklEQVR42mNgGAWjYGiC/2SCwWPBaByMxsFoHIyCUTDYAAAqhk7AVXdZJwAAAABJRU5ErkJggg==",
|
|
304
|
-
multiSelect:
|
|
305
|
-
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAWklEQVR42t2VUQ4AIAhCvf+lrQsULJJZbH7GM1cS0Uk5dd0Qqcz4GJSCSs0pSCkAHNoWBfkTgIxpgGK+ujyEKABqRGyn9FO1A+yfrf+qsGxTSx5YEs2WyU9oAKBJKg8Kfq4dAAAAAElFTkSuQmCC",
|
|
306
|
-
remove:
|
|
307
|
-
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAUklEQVR42mNgGPbgPx5AVYOJlSPJcGqrJU8DKXooCVui9FIacXj1UyNl4DWDLhYgc8nA+INpeFgwtCOZ5vmALjmZ5mURXUpTutQHdKnRRgEMAACwjWWprU1B+AAAAABJRU5ErkJggg==",
|
|
308
|
-
};
|
|
309
|
-
|
|
310
269
|
function getTextTransformStyle(
|
|
311
270
|
textTransform: TextTransform
|
|
312
271
|
): "none" | "lowercase" | "uppercase" | "capitalize" {
|
|
@@ -324,32 +283,50 @@ function resolveContainerSpec(image: ImageConfiguration): ContainerSpec {
|
|
|
324
283
|
: CONTAINER_SPEC_DEFAULT;
|
|
325
284
|
}
|
|
326
285
|
|
|
327
|
-
function mapMenuItemToNowPlayingData(
|
|
286
|
+
export function mapMenuItemToNowPlayingData(
|
|
287
|
+
item: MenuItem,
|
|
288
|
+
isPaused?: boolean,
|
|
289
|
+
nowPlayingLabel = "Now Playing"
|
|
290
|
+
): ItemData {
|
|
328
291
|
return {
|
|
329
292
|
imageUri: item.icon,
|
|
330
293
|
textLabel1: item.title,
|
|
331
294
|
textLabel2: item.summary ?? "",
|
|
332
|
-
nowPlayingLabel
|
|
295
|
+
nowPlayingLabel,
|
|
333
296
|
trailingButton: "playPause",
|
|
297
|
+
isSelected: isPaused !== undefined ? !isPaused : item.isSelected,
|
|
334
298
|
};
|
|
335
299
|
}
|
|
336
300
|
|
|
337
|
-
function mapMenuItemToQueuedData(item: MenuItem): ItemData {
|
|
301
|
+
export function mapMenuItemToQueuedData(item: MenuItem): ItemData {
|
|
338
302
|
return {
|
|
339
303
|
imageUri: item.icon,
|
|
340
304
|
textLabel1: item.title,
|
|
341
305
|
textLabel2: item.summary ?? "",
|
|
342
306
|
leadingButton: "remove",
|
|
343
307
|
trailingButton: "drag",
|
|
308
|
+
isSelected: item.isSelected,
|
|
344
309
|
};
|
|
345
310
|
}
|
|
346
311
|
|
|
347
|
-
function mapMenuItemToPlaylistData(
|
|
312
|
+
export function mapMenuItemToPlaylistData(
|
|
313
|
+
item: MenuItem,
|
|
314
|
+
behavior?: SelectionBehavior
|
|
315
|
+
): ItemData {
|
|
316
|
+
const showSelectionWidget =
|
|
317
|
+
behavior?.selectMode !== "none" && item.trailingButton !== "none";
|
|
318
|
+
|
|
319
|
+
const summary =
|
|
320
|
+
typeof item.summary === "string" ? item.summary.trim() : item.summary;
|
|
321
|
+
|
|
348
322
|
return {
|
|
349
323
|
imageUri: item.icon,
|
|
350
324
|
textLabel1: item.title,
|
|
351
|
-
textLabel2:
|
|
352
|
-
trailingButton:
|
|
325
|
+
textLabel2: summary || "",
|
|
326
|
+
trailingButton: showSelectionWidget
|
|
327
|
+
? item.trailingButton || ("multiSelect" as ItemButtonAction)
|
|
328
|
+
: undefined,
|
|
329
|
+
isSelected: item.isSelected,
|
|
353
330
|
};
|
|
354
331
|
}
|
|
355
332
|
|
|
@@ -362,6 +339,7 @@ type ItemContainerProps = {
|
|
|
362
339
|
background: BackgroundConfiguration;
|
|
363
340
|
focused: boolean;
|
|
364
341
|
children: React.ReactNode;
|
|
342
|
+
onPress?: () => void;
|
|
365
343
|
};
|
|
366
344
|
|
|
367
345
|
function ItemContainer({
|
|
@@ -369,23 +347,25 @@ function ItemContainer({
|
|
|
369
347
|
background,
|
|
370
348
|
focused,
|
|
371
349
|
children,
|
|
350
|
+
onPress,
|
|
372
351
|
}: ItemContainerProps) {
|
|
373
352
|
const container = resolveContainerSpec(image);
|
|
374
353
|
const bg = focused ? background.focusedColor : background.defaultColor;
|
|
375
354
|
|
|
376
355
|
return (
|
|
377
|
-
<
|
|
378
|
-
|
|
379
|
-
|
|
356
|
+
<Pressable
|
|
357
|
+
onPress={onPress}
|
|
358
|
+
style={({ pressed }) => ({
|
|
359
|
+
backgroundColor: pressed ? background.focusedColor : bg,
|
|
380
360
|
borderRadius: background.cornerRadius,
|
|
381
|
-
paddingTop: container.paddingTop,
|
|
382
|
-
paddingRight: container.paddingRight,
|
|
383
|
-
paddingBottom: container.paddingBottom,
|
|
384
|
-
paddingLeft: container.paddingLeft,
|
|
385
|
-
}}
|
|
361
|
+
paddingTop: background.paddingTop ?? container.paddingTop,
|
|
362
|
+
paddingRight: background.paddingRight ?? container.paddingRight,
|
|
363
|
+
paddingBottom: background.paddingBottom ?? container.paddingBottom,
|
|
364
|
+
paddingLeft: background.paddingLeft ?? container.paddingLeft,
|
|
365
|
+
})}
|
|
386
366
|
>
|
|
387
367
|
{children}
|
|
388
|
-
</
|
|
368
|
+
</Pressable>
|
|
389
369
|
);
|
|
390
370
|
}
|
|
391
371
|
|
|
@@ -417,6 +397,10 @@ type ItemLabelProps = {
|
|
|
417
397
|
};
|
|
418
398
|
|
|
419
399
|
function ItemLabel({ text, configuration, focused = false }: ItemLabelProps) {
|
|
400
|
+
if (!text) {
|
|
401
|
+
return null;
|
|
402
|
+
}
|
|
403
|
+
|
|
420
404
|
const color =
|
|
421
405
|
focused && configuration.focusedFontColor
|
|
422
406
|
? configuration.focusedFontColor
|
|
@@ -486,27 +470,272 @@ function NowPlayingRow({
|
|
|
486
470
|
type ItemButtonProps = {
|
|
487
471
|
action: ItemButtonAction;
|
|
488
472
|
focused?: boolean;
|
|
473
|
+
onPress?: () => void;
|
|
474
|
+
isSelected?: boolean;
|
|
475
|
+
checkboxAssets?: CheckboxAssetsConfiguration;
|
|
476
|
+
actionButtonAssets?: ActionButtonAssetsConfiguration;
|
|
489
477
|
};
|
|
490
478
|
|
|
491
|
-
function ItemButton({
|
|
479
|
+
function ItemButton({
|
|
480
|
+
action,
|
|
481
|
+
focused = false,
|
|
482
|
+
onPress,
|
|
483
|
+
isSelected = false,
|
|
484
|
+
checkboxAssets,
|
|
485
|
+
actionButtonAssets,
|
|
486
|
+
}: ItemButtonProps) {
|
|
492
487
|
const layout = BUTTON_LAYOUT_SPEC[action];
|
|
493
488
|
|
|
489
|
+
if (!layout) {
|
|
490
|
+
return null;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
const renderIcon = () => {
|
|
494
|
+
switch (action) {
|
|
495
|
+
case "playPause": {
|
|
496
|
+
const iconSize = layout.assetWidth;
|
|
497
|
+
const iconRadius = iconSize / 2;
|
|
498
|
+
|
|
499
|
+
return (
|
|
500
|
+
<View
|
|
501
|
+
style={{
|
|
502
|
+
width: iconSize,
|
|
503
|
+
height: iconSize,
|
|
504
|
+
borderRadius: iconRadius,
|
|
505
|
+
backgroundColor: "#FFFFFF",
|
|
506
|
+
justifyContent: "center",
|
|
507
|
+
alignItems: "center",
|
|
508
|
+
}}
|
|
509
|
+
>
|
|
510
|
+
{isSelected ? (
|
|
511
|
+
<View
|
|
512
|
+
style={{
|
|
513
|
+
flexDirection: "row",
|
|
514
|
+
justifyContent: "space-between",
|
|
515
|
+
width: 10,
|
|
516
|
+
height: 16,
|
|
517
|
+
}}
|
|
518
|
+
>
|
|
519
|
+
<View
|
|
520
|
+
style={{
|
|
521
|
+
width: 3.5,
|
|
522
|
+
height: 16,
|
|
523
|
+
backgroundColor: "#1e1e1e",
|
|
524
|
+
borderRadius: 1,
|
|
525
|
+
}}
|
|
526
|
+
/>
|
|
527
|
+
<View
|
|
528
|
+
style={{
|
|
529
|
+
width: 3.5,
|
|
530
|
+
height: 16,
|
|
531
|
+
backgroundColor: "#1e1e1e",
|
|
532
|
+
borderRadius: 1,
|
|
533
|
+
}}
|
|
534
|
+
/>
|
|
535
|
+
</View>
|
|
536
|
+
) : (
|
|
537
|
+
<View
|
|
538
|
+
style={{
|
|
539
|
+
width: 0,
|
|
540
|
+
height: 0,
|
|
541
|
+
backgroundColor: "transparent",
|
|
542
|
+
borderStyle: "solid",
|
|
543
|
+
borderLeftWidth: 14,
|
|
544
|
+
borderRightWidth: 0,
|
|
545
|
+
borderTopWidth: 8,
|
|
546
|
+
borderBottomWidth: 8,
|
|
547
|
+
borderLeftColor: "#1e1e1e",
|
|
548
|
+
borderRightColor: "transparent",
|
|
549
|
+
borderTopColor: "transparent",
|
|
550
|
+
borderBottomColor: "transparent",
|
|
551
|
+
marginLeft: 3,
|
|
552
|
+
}}
|
|
553
|
+
/>
|
|
554
|
+
)}
|
|
555
|
+
</View>
|
|
556
|
+
);
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
case "drag": {
|
|
560
|
+
if (actionButtonAssets?.dragAsset) {
|
|
561
|
+
return (
|
|
562
|
+
<Image
|
|
563
|
+
source={{ uri: actionButtonAssets.dragAsset }}
|
|
564
|
+
style={{
|
|
565
|
+
width: layout.assetWidth,
|
|
566
|
+
height: layout.assetHeight,
|
|
567
|
+
}}
|
|
568
|
+
resizeMode="contain"
|
|
569
|
+
/>
|
|
570
|
+
);
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
return (
|
|
574
|
+
<View
|
|
575
|
+
style={{
|
|
576
|
+
width: 20,
|
|
577
|
+
height: 12,
|
|
578
|
+
justifyContent: "space-between",
|
|
579
|
+
}}
|
|
580
|
+
>
|
|
581
|
+
<View
|
|
582
|
+
style={{
|
|
583
|
+
height: 2,
|
|
584
|
+
backgroundColor: "rgba(255, 255, 255, 0.7)",
|
|
585
|
+
borderRadius: 1,
|
|
586
|
+
}}
|
|
587
|
+
/>
|
|
588
|
+
<View
|
|
589
|
+
style={{
|
|
590
|
+
height: 2,
|
|
591
|
+
backgroundColor: "rgba(255, 255, 255, 0.7)",
|
|
592
|
+
borderRadius: 1,
|
|
593
|
+
}}
|
|
594
|
+
/>
|
|
595
|
+
<View
|
|
596
|
+
style={{
|
|
597
|
+
height: 2,
|
|
598
|
+
backgroundColor: "rgba(255, 255, 255, 0.7)",
|
|
599
|
+
borderRadius: 1,
|
|
600
|
+
}}
|
|
601
|
+
/>
|
|
602
|
+
</View>
|
|
603
|
+
);
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
case "remove": {
|
|
607
|
+
if (actionButtonAssets?.removeAsset) {
|
|
608
|
+
return (
|
|
609
|
+
<Image
|
|
610
|
+
source={{ uri: actionButtonAssets.removeAsset }}
|
|
611
|
+
style={{
|
|
612
|
+
width: layout.assetWidth,
|
|
613
|
+
height: layout.assetHeight,
|
|
614
|
+
}}
|
|
615
|
+
resizeMode="contain"
|
|
616
|
+
/>
|
|
617
|
+
);
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
return (
|
|
621
|
+
<View
|
|
622
|
+
style={{
|
|
623
|
+
width: 24,
|
|
624
|
+
height: 24,
|
|
625
|
+
borderRadius: 12,
|
|
626
|
+
borderWidth: 1.5,
|
|
627
|
+
borderColor: "rgba(255, 255, 255, 0.7)",
|
|
628
|
+
justifyContent: "center",
|
|
629
|
+
alignItems: "center",
|
|
630
|
+
}}
|
|
631
|
+
>
|
|
632
|
+
<View
|
|
633
|
+
style={{
|
|
634
|
+
width: 10,
|
|
635
|
+
height: 1.5,
|
|
636
|
+
backgroundColor: "rgba(255, 255, 255, 0.7)",
|
|
637
|
+
borderRadius: 1,
|
|
638
|
+
}}
|
|
639
|
+
/>
|
|
640
|
+
</View>
|
|
641
|
+
);
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
case "multiSelect": {
|
|
645
|
+
const studioAsset = isSelected
|
|
646
|
+
? checkboxAssets?.checkedAsset
|
|
647
|
+
: checkboxAssets?.asset;
|
|
648
|
+
|
|
649
|
+
if (studioAsset) {
|
|
650
|
+
return (
|
|
651
|
+
<Image
|
|
652
|
+
source={{ uri: studioAsset }}
|
|
653
|
+
style={{
|
|
654
|
+
width: layout.assetWidth,
|
|
655
|
+
height: layout.assetHeight,
|
|
656
|
+
}}
|
|
657
|
+
resizeMode="contain"
|
|
658
|
+
/>
|
|
659
|
+
);
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
if (!isSelected) {
|
|
663
|
+
return (
|
|
664
|
+
<View
|
|
665
|
+
style={{
|
|
666
|
+
width: layout.assetWidth,
|
|
667
|
+
height: layout.assetHeight,
|
|
668
|
+
borderRadius: layout.assetWidth / 2,
|
|
669
|
+
borderWidth: 1.5,
|
|
670
|
+
borderColor: "rgba(255, 255, 255, 0.7)",
|
|
671
|
+
backgroundColor: "transparent",
|
|
672
|
+
}}
|
|
673
|
+
/>
|
|
674
|
+
);
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
return (
|
|
678
|
+
<View
|
|
679
|
+
style={{
|
|
680
|
+
width: 24,
|
|
681
|
+
height: 24,
|
|
682
|
+
borderRadius: 12,
|
|
683
|
+
backgroundColor: "#FFFFFF",
|
|
684
|
+
justifyContent: "center",
|
|
685
|
+
alignItems: "center",
|
|
686
|
+
}}
|
|
687
|
+
>
|
|
688
|
+
<View
|
|
689
|
+
style={{
|
|
690
|
+
width: 6,
|
|
691
|
+
height: 10,
|
|
692
|
+
borderBottomWidth: 2,
|
|
693
|
+
borderRightWidth: 2,
|
|
694
|
+
borderColor: "#1e1e1e",
|
|
695
|
+
transform: [{ rotate: "45deg" }],
|
|
696
|
+
marginTop: -2,
|
|
697
|
+
}}
|
|
698
|
+
/>
|
|
699
|
+
</View>
|
|
700
|
+
);
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
default:
|
|
704
|
+
return null;
|
|
705
|
+
}
|
|
706
|
+
};
|
|
707
|
+
|
|
708
|
+
const buttonPadding = {
|
|
709
|
+
paddingTop: layout.paddingTop,
|
|
710
|
+
paddingRight: layout.paddingRight,
|
|
711
|
+
paddingBottom: layout.paddingBottom,
|
|
712
|
+
paddingLeft: layout.paddingLeft,
|
|
713
|
+
};
|
|
714
|
+
|
|
715
|
+
// A nested Pressable with no handler swallows the parent row press.
|
|
716
|
+
if (!onPress) {
|
|
717
|
+
return (
|
|
718
|
+
<View pointerEvents="none" style={buttonPadding}>
|
|
719
|
+
{renderIcon()}
|
|
720
|
+
</View>
|
|
721
|
+
);
|
|
722
|
+
}
|
|
723
|
+
|
|
494
724
|
return (
|
|
495
|
-
<
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
paddingBottom: layout.paddingBottom,
|
|
500
|
-
paddingLeft: layout.paddingLeft,
|
|
501
|
-
opacity: focused ? 0.7 : 1,
|
|
725
|
+
<Pressable
|
|
726
|
+
onPress={(e: any) => {
|
|
727
|
+
e?.stopPropagation?.();
|
|
728
|
+
onPress();
|
|
502
729
|
}}
|
|
730
|
+
style={({ pressed }) => [
|
|
731
|
+
buttonPadding,
|
|
732
|
+
{
|
|
733
|
+
opacity: pressed || focused ? 0.7 : 1,
|
|
734
|
+
},
|
|
735
|
+
]}
|
|
503
736
|
>
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
style={{ width: layout.assetWidth, height: layout.assetHeight }}
|
|
507
|
-
resizeMode="contain"
|
|
508
|
-
/>
|
|
509
|
-
</View>
|
|
737
|
+
{renderIcon()}
|
|
738
|
+
</Pressable>
|
|
510
739
|
);
|
|
511
740
|
}
|
|
512
741
|
|
|
@@ -515,6 +744,12 @@ type ItemButtonsProps = {
|
|
|
515
744
|
trailingButton?: ItemButtonAction;
|
|
516
745
|
focusedLeadingButton?: boolean;
|
|
517
746
|
focusedTrailingButton?: boolean;
|
|
747
|
+
onLeadingButtonPress?: () => void;
|
|
748
|
+
onTrailingButtonPress?: () => void;
|
|
749
|
+
isSelected?: boolean;
|
|
750
|
+
renderHandle?: (element: React.ReactElement) => React.ReactElement;
|
|
751
|
+
checkboxAssets?: CheckboxAssetsConfiguration;
|
|
752
|
+
actionButtonAssets?: ActionButtonAssetsConfiguration;
|
|
518
753
|
};
|
|
519
754
|
|
|
520
755
|
function ItemButtons({
|
|
@@ -522,6 +757,12 @@ function ItemButtons({
|
|
|
522
757
|
trailingButton,
|
|
523
758
|
focusedLeadingButton = false,
|
|
524
759
|
focusedTrailingButton = false,
|
|
760
|
+
onLeadingButtonPress,
|
|
761
|
+
onTrailingButtonPress,
|
|
762
|
+
isSelected = false,
|
|
763
|
+
renderHandle,
|
|
764
|
+
checkboxAssets,
|
|
765
|
+
actionButtonAssets,
|
|
525
766
|
}: ItemButtonsProps) {
|
|
526
767
|
if (!leadingButton && !trailingButton) {
|
|
527
768
|
return null;
|
|
@@ -531,69 +772,145 @@ function ItemButtons({
|
|
|
531
772
|
<View style={styles.buttonsRow}>
|
|
532
773
|
{leadingButton ? (
|
|
533
774
|
<>
|
|
534
|
-
<ItemButton
|
|
775
|
+
<ItemButton
|
|
776
|
+
action={leadingButton}
|
|
777
|
+
focused={focusedLeadingButton}
|
|
778
|
+
onPress={onLeadingButtonPress}
|
|
779
|
+
isSelected={isSelected}
|
|
780
|
+
checkboxAssets={checkboxAssets}
|
|
781
|
+
actionButtonAssets={actionButtonAssets}
|
|
782
|
+
/>
|
|
535
783
|
<View style={{ width: BUTTONS_CONTAINER_HORIZONTAL_GUTTER }} />
|
|
536
784
|
</>
|
|
537
785
|
) : null}
|
|
538
786
|
{trailingButton ? (
|
|
539
|
-
|
|
787
|
+
trailingButton === "drag" ? (
|
|
788
|
+
renderHandle ? (
|
|
789
|
+
renderHandle(
|
|
790
|
+
<ItemButton
|
|
791
|
+
action={trailingButton}
|
|
792
|
+
focused={focusedTrailingButton}
|
|
793
|
+
onPress={onTrailingButtonPress}
|
|
794
|
+
isSelected={isSelected}
|
|
795
|
+
checkboxAssets={checkboxAssets}
|
|
796
|
+
actionButtonAssets={actionButtonAssets}
|
|
797
|
+
/>
|
|
798
|
+
)
|
|
799
|
+
) : (
|
|
800
|
+
<ItemButton
|
|
801
|
+
action={trailingButton}
|
|
802
|
+
focused={focusedTrailingButton}
|
|
803
|
+
onPress={onTrailingButtonPress}
|
|
804
|
+
isSelected={isSelected}
|
|
805
|
+
checkboxAssets={checkboxAssets}
|
|
806
|
+
actionButtonAssets={actionButtonAssets}
|
|
807
|
+
/>
|
|
808
|
+
)
|
|
809
|
+
) : (
|
|
810
|
+
<ItemButton
|
|
811
|
+
action={trailingButton}
|
|
812
|
+
focused={focusedTrailingButton}
|
|
813
|
+
onPress={onTrailingButtonPress}
|
|
814
|
+
isSelected={isSelected}
|
|
815
|
+
checkboxAssets={checkboxAssets}
|
|
816
|
+
actionButtonAssets={actionButtonAssets}
|
|
817
|
+
/>
|
|
818
|
+
)
|
|
540
819
|
) : null}
|
|
541
820
|
</View>
|
|
542
821
|
);
|
|
543
822
|
}
|
|
544
823
|
|
|
545
824
|
// ---------------------------------------------------------------------------
|
|
546
|
-
//
|
|
825
|
+
// AudioPlayerTrackItem
|
|
547
826
|
// ---------------------------------------------------------------------------
|
|
548
827
|
|
|
549
|
-
export function
|
|
828
|
+
export function AudioPlayerTrackItem({
|
|
550
829
|
configuration,
|
|
551
830
|
data,
|
|
552
831
|
focused = false,
|
|
553
832
|
focusedLeadingButton = false,
|
|
554
833
|
focusedTrailingButton = false,
|
|
555
|
-
|
|
834
|
+
onPress,
|
|
835
|
+
onLeadingButtonPress,
|
|
836
|
+
onTrailingButtonPress,
|
|
837
|
+
renderHandle,
|
|
838
|
+
}: AudioPlayerTrackItemProps) {
|
|
839
|
+
const modalBlocksStyle = useModalBlocksStyle();
|
|
840
|
+
const showNowPlaying = !!data.nowPlayingLabel;
|
|
841
|
+
|
|
842
|
+
const itemBlock =
|
|
843
|
+
showNowPlaying && modalBlocksStyle?.nowPlayingItem
|
|
844
|
+
? modalBlocksStyle.nowPlayingItem
|
|
845
|
+
: modalBlocksStyle?.item;
|
|
846
|
+
|
|
847
|
+
const effectiveConfig = React.useMemo(() => {
|
|
848
|
+
return mergeItemConfiguration(configuration, itemBlock);
|
|
849
|
+
}, [configuration, itemBlock]);
|
|
850
|
+
|
|
556
851
|
const {
|
|
557
852
|
background,
|
|
558
853
|
image,
|
|
559
|
-
nowPlayingAsset,
|
|
560
|
-
nowPlayingLabel,
|
|
561
854
|
textLabel1,
|
|
562
855
|
textLabel2,
|
|
563
|
-
|
|
856
|
+
nowPlayingLabel,
|
|
857
|
+
nowPlayingAsset,
|
|
858
|
+
checkboxAssets,
|
|
859
|
+
actionButtonAssets,
|
|
860
|
+
} = effectiveConfig;
|
|
564
861
|
|
|
565
862
|
const container = resolveContainerSpec(image);
|
|
566
|
-
|
|
863
|
+
|
|
864
|
+
const handleTrailingButtonPress =
|
|
865
|
+
onTrailingButtonPress ??
|
|
866
|
+
(data.trailingButton === "multiSelect" ? onPress : undefined);
|
|
567
867
|
|
|
568
868
|
return (
|
|
569
|
-
<ItemContainer
|
|
869
|
+
<ItemContainer
|
|
870
|
+
image={image}
|
|
871
|
+
background={background}
|
|
872
|
+
focused={focused}
|
|
873
|
+
onPress={onPress}
|
|
874
|
+
>
|
|
570
875
|
<View style={styles.row}>
|
|
571
876
|
<ItemThumbnail configuration={image} imageUri={data.imageUri} />
|
|
572
877
|
<View style={{ width: container.horizontalGutter }} />
|
|
573
878
|
<View style={styles.contentColumn}>
|
|
574
879
|
{showNowPlaying ? (
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
880
|
+
<NowPlayingRow
|
|
881
|
+
assetConfiguration={nowPlayingAsset}
|
|
882
|
+
labelConfiguration={nowPlayingLabel}
|
|
883
|
+
label={data.nowPlayingLabel}
|
|
884
|
+
/>
|
|
885
|
+
) : null}
|
|
886
|
+
{showNowPlaying && (data.textLabel1 || data.textLabel2) ? (
|
|
887
|
+
<View style={{ height: container.verticalGutter }} />
|
|
888
|
+
) : null}
|
|
889
|
+
{data.textLabel1 ? (
|
|
890
|
+
<ItemLabel
|
|
891
|
+
text={data.textLabel1}
|
|
892
|
+
configuration={textLabel1}
|
|
893
|
+
focused={focused}
|
|
894
|
+
/>
|
|
895
|
+
) : null}
|
|
896
|
+
{data.textLabel1 && data.textLabel2 ? (
|
|
897
|
+
<View style={{ height: container.verticalGutter }} />
|
|
898
|
+
) : null}
|
|
899
|
+
{data.textLabel2 ? (
|
|
900
|
+
<ItemLabel text={data.textLabel2} configuration={textLabel2} />
|
|
583
901
|
) : null}
|
|
584
|
-
<ItemLabel
|
|
585
|
-
text={data.textLabel1}
|
|
586
|
-
configuration={textLabel1}
|
|
587
|
-
focused={focused}
|
|
588
|
-
/>
|
|
589
|
-
<View style={{ height: container.verticalGutter }} />
|
|
590
|
-
<ItemLabel text={data.textLabel2} configuration={textLabel2} />
|
|
591
902
|
</View>
|
|
592
903
|
<ItemButtons
|
|
593
904
|
leadingButton={data.leadingButton}
|
|
594
905
|
trailingButton={data.trailingButton}
|
|
595
906
|
focusedLeadingButton={focusedLeadingButton}
|
|
596
907
|
focusedTrailingButton={focusedTrailingButton}
|
|
908
|
+
onLeadingButtonPress={onLeadingButtonPress}
|
|
909
|
+
onTrailingButtonPress={handleTrailingButtonPress}
|
|
910
|
+
isSelected={data.isSelected}
|
|
911
|
+
renderHandle={renderHandle}
|
|
912
|
+
checkboxAssets={checkboxAssets}
|
|
913
|
+
actionButtonAssets={actionButtonAssets}
|
|
597
914
|
/>
|
|
598
915
|
</View>
|
|
599
916
|
</ItemContainer>
|
|
@@ -663,47 +980,3 @@ export const VARIANT_PLAYLIST_ITEM: ItemConfiguration = {
|
|
|
663
980
|
textLabel1: DEFAULT_TEXT_LABEL_1_CONFIGURATION,
|
|
664
981
|
textLabel2: DEFAULT_TEXT_LABEL_2_CONFIGURATION,
|
|
665
982
|
};
|
|
666
|
-
|
|
667
|
-
export default function App() {
|
|
668
|
-
// Hardcoded core mock data objects matching user types framework
|
|
669
|
-
const sharedMockItem: MenuItem = {
|
|
670
|
-
title: "Content Title",
|
|
671
|
-
summary: "Artist",
|
|
672
|
-
icon: "https://picsum.photos/200",
|
|
673
|
-
action: { type: "PLAY_AUDIO" },
|
|
674
|
-
secondaryAction: { action: { type: "OPEN_CONTEXT_MENU" } },
|
|
675
|
-
};
|
|
676
|
-
|
|
677
|
-
const playlistMockItem: MenuItem = {
|
|
678
|
-
title: "Playlist Name",
|
|
679
|
-
summary: "12 Songs",
|
|
680
|
-
icon: "https://picsum.photos/201",
|
|
681
|
-
isSelected: true,
|
|
682
|
-
action: { type: "NAVIGATE_PLAYLIST" },
|
|
683
|
-
};
|
|
684
|
-
|
|
685
|
-
return (
|
|
686
|
-
<View style={styles.demoScreen}>
|
|
687
|
-
<Text style={styles.demoHeading}>Modal Item Building Block</Text>
|
|
688
|
-
|
|
689
|
-
<Text style={styles.demoLabel}>Now Playing</Text>
|
|
690
|
-
<Item
|
|
691
|
-
configuration={VARIANT_NOW_PLAYING}
|
|
692
|
-
data={mapMenuItemToNowPlayingData(sharedMockItem)}
|
|
693
|
-
/>
|
|
694
|
-
|
|
695
|
-
<Text style={styles.demoLabel}>Queued Item</Text>
|
|
696
|
-
<Item
|
|
697
|
-
configuration={VARIANT_QUEUED_ITEM}
|
|
698
|
-
data={mapMenuItemToQueuedData(sharedMockItem)}
|
|
699
|
-
focused
|
|
700
|
-
/>
|
|
701
|
-
|
|
702
|
-
<Text style={styles.demoLabel}>Playlist Item</Text>
|
|
703
|
-
<Item
|
|
704
|
-
configuration={VARIANT_PLAYLIST_ITEM}
|
|
705
|
-
data={mapMenuItemToPlaylistData(playlistMockItem)}
|
|
706
|
-
/>
|
|
707
|
-
</View>
|
|
708
|
-
);
|
|
709
|
-
}
|