@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
|
@@ -18,10 +18,9 @@ function shouldShowNavigation(route, screenData) {
|
|
|
18
18
|
return false;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
) {
|
|
21
|
+
const currentScreenData = screenData?.targetScreen || screenData;
|
|
22
|
+
|
|
23
|
+
if (currentScreenData.type === "qb_search_screen") {
|
|
25
24
|
return false;
|
|
26
25
|
}
|
|
27
26
|
|
|
@@ -31,10 +30,11 @@ function shouldShowNavigation(route, screenData) {
|
|
|
31
30
|
return hookPlugin?.showNavBar || hookPlugin?.presentFullScreen !== true;
|
|
32
31
|
}
|
|
33
32
|
|
|
34
|
-
if (
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
33
|
+
if (currentScreenData?.general?.hide_menu) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (currentScreenData?.general?.allow_screen_plugin_presentation) {
|
|
38
38
|
return false;
|
|
39
39
|
}
|
|
40
40
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { render } from "@testing-library/react-native";
|
|
3
|
+
import { withModalNavigationContextProvider } from "../";
|
|
4
|
+
|
|
5
|
+
describe("withModalNavigationContextProvider", () => {
|
|
6
|
+
it("does not re-render the wrapped component when props are unchanged", () => {
|
|
7
|
+
const Component = jest.fn(() => null);
|
|
8
|
+
const WrappedComponent = withModalNavigationContextProvider(Component);
|
|
9
|
+
const { rerender } = render(<WrappedComponent title="Modal" />);
|
|
10
|
+
|
|
11
|
+
rerender(<WrappedComponent title="Modal" />);
|
|
12
|
+
|
|
13
|
+
expect(Component).toHaveBeenCalledTimes(1);
|
|
14
|
+
});
|
|
15
|
+
});
|
|
@@ -6,11 +6,11 @@ export function useModalNavigationContext() {
|
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
export function withModalNavigationContextProvider(Component) {
|
|
9
|
-
return function WrappedWithModalNavigationContextProvider(props) {
|
|
9
|
+
return React.memo(function WrappedWithModalNavigationContextProvider(props) {
|
|
10
10
|
return (
|
|
11
11
|
<ModalNavigationContext.Provider value={true}>
|
|
12
12
|
<Component {...props} />
|
|
13
13
|
</ModalNavigationContext.Provider>
|
|
14
14
|
);
|
|
15
|
-
};
|
|
15
|
+
});
|
|
16
16
|
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
import { ActionExecutorContext } from "@applicaster/zapp-react-native-utils/actionsExecutor/ActionExecutorContext";
|
|
4
|
+
import {
|
|
5
|
+
buildEntryActions,
|
|
6
|
+
EntryActionDeps,
|
|
7
|
+
} from "@applicaster/zapp-react-native-utils/entryActions";
|
|
8
|
+
import { useRoute } from "@applicaster/zapp-react-native-utils/reactHooks/navigation";
|
|
9
|
+
import { useScreenStateStore } from "@applicaster/zapp-react-native-utils/reactHooks/navigation/useScreenStateStore";
|
|
10
|
+
import {
|
|
11
|
+
useCurrentScreenData,
|
|
12
|
+
useScreenContext,
|
|
13
|
+
} from "@applicaster/zapp-react-native-utils/reactHooks/screen";
|
|
14
|
+
import { RegisteredAction } from "@applicaster/zapp-react-native-utils/uiActionsRegistrator";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Expands `entry.extensions.entry_action` into invokable actions, against the
|
|
18
|
+
* screen this hook is called from.
|
|
19
|
+
*
|
|
20
|
+
* Entry actions are data on the entry, not an installed capability, so there is
|
|
21
|
+
* nothing to look up by identifier: the surface showing the entry expands them.
|
|
22
|
+
* That also decides which screen they execute against — the one whose tree this
|
|
23
|
+
* hook runs in, rather than whichever screen happened to be current when a
|
|
24
|
+
* global provider was registered.
|
|
25
|
+
*
|
|
26
|
+
* Pass the result to `resolveActionIdentifiers` / `resolvePlayerActions` as
|
|
27
|
+
* `expandEntryActions`.
|
|
28
|
+
*/
|
|
29
|
+
export function useEntryActionsExpander(): (
|
|
30
|
+
entry: ZappEntry | ZappFeed
|
|
31
|
+
) => RegisteredAction[] {
|
|
32
|
+
const { pathname } = useRoute();
|
|
33
|
+
|
|
34
|
+
const screenContext = useScreenContext();
|
|
35
|
+
const screenData = useCurrentScreenData();
|
|
36
|
+
const screenState = screenContext?.options;
|
|
37
|
+
const screenStateStore = useScreenStateStore();
|
|
38
|
+
const actionExecutor = React.useContext(ActionExecutorContext);
|
|
39
|
+
|
|
40
|
+
// Read through a ref so the returned expander keeps a stable identity: it
|
|
41
|
+
// feeds `useMemo` dependency lists on surfaces that re-resolve their actions
|
|
42
|
+
// on every registry change.
|
|
43
|
+
const depsRef = React.useRef<EntryActionDeps>();
|
|
44
|
+
|
|
45
|
+
depsRef.current = {
|
|
46
|
+
actionExecutor,
|
|
47
|
+
actionContext: {
|
|
48
|
+
screenData,
|
|
49
|
+
screenState,
|
|
50
|
+
screenRoute: pathname,
|
|
51
|
+
screenStateStore,
|
|
52
|
+
// From the screen context, the way `useCellClick` builds the same bag.
|
|
53
|
+
// The ZappPipes entry context is a route-keyed map, so reading `.data`
|
|
54
|
+
// off it was always undefined.
|
|
55
|
+
screenEntry: screenContext?.entry,
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
return React.useCallback(
|
|
60
|
+
(entry: ZappEntry | ZappFeed) => buildEntryActions(entry, depsRef.current),
|
|
61
|
+
[]
|
|
62
|
+
);
|
|
63
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applicaster/zapp-react-native-ui-components",
|
|
3
|
-
"version": "15.1.
|
|
3
|
+
"version": "15.1.15-rc.0",
|
|
4
4
|
"description": "Applicaster Zapp React Native ui components for the Quick Brick App",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -28,12 +28,13 @@
|
|
|
28
28
|
},
|
|
29
29
|
"homepage": "https://github.com/applicaster/quickbrick#readme",
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@applicaster/applicaster-types": "15.1.
|
|
32
|
-
"@applicaster/zapp-react-native-bridge": "15.1.
|
|
33
|
-
"@applicaster/zapp-react-native-redux": "15.1.
|
|
34
|
-
"@applicaster/zapp-react-native-utils": "15.1.
|
|
31
|
+
"@applicaster/applicaster-types": "15.1.15-rc.0",
|
|
32
|
+
"@applicaster/zapp-react-native-bridge": "15.1.15-rc.0",
|
|
33
|
+
"@applicaster/zapp-react-native-redux": "15.1.15-rc.0",
|
|
34
|
+
"@applicaster/zapp-react-native-utils": "15.1.15-rc.0",
|
|
35
35
|
"fast-json-stable-stringify": "^2.1.0",
|
|
36
36
|
"promise": "^8.3.0",
|
|
37
|
+
"react-native-sortables": "1.7.1",
|
|
37
38
|
"url": "^0.11.0",
|
|
38
39
|
"uuid": "^3.3.2"
|
|
39
40
|
},
|
|
@@ -1,368 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Modal Building Block — Action
|
|
3
|
-
*
|
|
4
|
-
* Single-file Expo Snack demo for the Action list item building block spec.
|
|
5
|
-
* Three forms (text only / leading icon + text / leading + text + trailing)
|
|
6
|
-
* are driven by data — no separate variants.
|
|
7
|
-
*
|
|
8
|
-
* Green (configurable): background, title, trailingIcon.chevronAsset
|
|
9
|
-
* White (hardcoded): container padding/gutter, icon size, leading icon assets,
|
|
10
|
-
* leading/trailing visibility logic from data
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import React from "react";
|
|
14
|
-
import { Image, Platform, StyleSheet, Text, View } from "react-native";
|
|
15
|
-
|
|
16
|
-
const styles = StyleSheet.create({
|
|
17
|
-
row: {
|
|
18
|
-
flexDirection: "row",
|
|
19
|
-
alignItems: "center",
|
|
20
|
-
},
|
|
21
|
-
demoScreen: {
|
|
22
|
-
flex: 1,
|
|
23
|
-
backgroundColor: "#1C1C1E",
|
|
24
|
-
paddingTop: 64,
|
|
25
|
-
paddingHorizontal: 16,
|
|
26
|
-
gap: 12,
|
|
27
|
-
},
|
|
28
|
-
demoHeading: {
|
|
29
|
-
color: "#FFFFFF",
|
|
30
|
-
fontSize: 20,
|
|
31
|
-
fontWeight: "600",
|
|
32
|
-
marginBottom: 8,
|
|
33
|
-
},
|
|
34
|
-
demoLabel: {
|
|
35
|
-
color: "#999999",
|
|
36
|
-
fontSize: 13,
|
|
37
|
-
marginTop: 8,
|
|
38
|
-
},
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
// ---------------------------------------------------------------------------
|
|
42
|
-
// Configuration (green / editable fields from spec)
|
|
43
|
-
// ---------------------------------------------------------------------------
|
|
44
|
-
|
|
45
|
-
export type TextTransform =
|
|
46
|
-
| "default"
|
|
47
|
-
| "lowercase"
|
|
48
|
-
| "uppercase"
|
|
49
|
-
| "capitalize";
|
|
50
|
-
|
|
51
|
-
export type BackgroundConfiguration = {
|
|
52
|
-
defaultColor: string;
|
|
53
|
-
focusedColor: string;
|
|
54
|
-
cornerRadius: number;
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
export type TitleConfiguration = {
|
|
58
|
-
fontColor: string;
|
|
59
|
-
focusedFontColor: string;
|
|
60
|
-
iosFontFamily: string;
|
|
61
|
-
androidFontFamily: string;
|
|
62
|
-
fontSize: number;
|
|
63
|
-
lineHeight: number;
|
|
64
|
-
iosLetterSpacing: number;
|
|
65
|
-
androidLetterSpacing: number;
|
|
66
|
-
textTransform: TextTransform;
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
export type TrailingIconConfiguration = {
|
|
70
|
-
chevronAsset?: string;
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
export type ActionConfiguration = {
|
|
74
|
-
background: BackgroundConfiguration;
|
|
75
|
-
title: TitleConfiguration;
|
|
76
|
-
trailingIcon: TrailingIconConfiguration;
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
export const DEFAULT_BACKGROUND_CONFIGURATION: BackgroundConfiguration = {
|
|
80
|
-
defaultColor: "transparent",
|
|
81
|
-
focusedColor: "rgba(62, 62, 62, 1)",
|
|
82
|
-
cornerRadius: 12,
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
export const DEFAULT_TITLE_CONFIGURATION: TitleConfiguration = {
|
|
86
|
-
fontColor: "#FFFFFF",
|
|
87
|
-
focusedFontColor: "#FFFFFF",
|
|
88
|
-
iosFontFamily: "SFProText-Semibold",
|
|
89
|
-
androidFontFamily: "Roboto-Medium",
|
|
90
|
-
fontSize: 15,
|
|
91
|
-
lineHeight: 24,
|
|
92
|
-
iosLetterSpacing: -0.2,
|
|
93
|
-
androidLetterSpacing: 0,
|
|
94
|
-
textTransform: "default",
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
export const DEFAULT_TRAILING_ICON_CONFIGURATION: TrailingIconConfiguration =
|
|
98
|
-
{};
|
|
99
|
-
|
|
100
|
-
export const DEFAULT_ACTION_CONFIGURATION: ActionConfiguration = {
|
|
101
|
-
background: DEFAULT_BACKGROUND_CONFIGURATION,
|
|
102
|
-
title: DEFAULT_TITLE_CONFIGURATION,
|
|
103
|
-
trailingIcon: DEFAULT_TRAILING_ICON_CONFIGURATION,
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
// ---------------------------------------------------------------------------
|
|
107
|
-
// Data (runtime — controls which elements are shown)
|
|
108
|
-
// ---------------------------------------------------------------------------
|
|
109
|
-
|
|
110
|
-
export type LeadingActionType =
|
|
111
|
-
| "addToQueue"
|
|
112
|
-
| "addToPlaylist"
|
|
113
|
-
| "favorite"
|
|
114
|
-
| "share"
|
|
115
|
-
| "sleepTimer"
|
|
116
|
-
| "playbackSpeed";
|
|
117
|
-
|
|
118
|
-
export type ActionData = {
|
|
119
|
-
title: string;
|
|
120
|
-
/** When set, leading icon is shown (Enable: On). */
|
|
121
|
-
leadingActionType?: LeadingActionType;
|
|
122
|
-
/** When true, trailing chevron is shown (Enable: On). */
|
|
123
|
-
showTrailingChevron?: boolean;
|
|
124
|
-
};
|
|
125
|
-
|
|
126
|
-
export type ItemProps = {
|
|
127
|
-
configuration: ActionConfiguration;
|
|
128
|
-
data: ActionData;
|
|
129
|
-
focused?: boolean;
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
// ---------------------------------------------------------------------------
|
|
133
|
-
// Hardcoded spec values (white fields)
|
|
134
|
-
// ---------------------------------------------------------------------------
|
|
135
|
-
|
|
136
|
-
const CONTAINER_SPEC = {
|
|
137
|
-
paddingTop: 13,
|
|
138
|
-
paddingRight: 10,
|
|
139
|
-
paddingBottom: 13,
|
|
140
|
-
paddingLeft: 10,
|
|
141
|
-
horizontalGutter: 12,
|
|
142
|
-
};
|
|
143
|
-
|
|
144
|
-
const ICON_SPEC = {
|
|
145
|
-
width: 24,
|
|
146
|
-
height: 24,
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
const DEFAULT_CHEVRON_ASSET =
|
|
150
|
-
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAALElEQVR42mNgGAXUBlu2bPmPjIeWBeiGDy0Lhrbrh1/qGXqZaxSMglEw0gEA4c+IpUrpYX4AAAAASUVORK5CYII=";
|
|
151
|
-
|
|
152
|
-
/** Hardcoded: action type → leading icon asset (base64 PNG). */
|
|
153
|
-
const LEADING_ICON_ASSETS: Record<LeadingActionType, string> = {
|
|
154
|
-
addToQueue:
|
|
155
|
-
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAALElEQVR42mNgGAUjE/xHAzS3gFgweC0Y+nEwGslkBRFFwTj8LBjNyaNgmAMAJTf+EEEZcS0AAAAASUVORK5CYII=",
|
|
156
|
-
addToPlaylist:
|
|
157
|
-
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAKUlEQVR42mNgGAVDBvxHA6MWjFpAvEGkglELKLdgNJmOWkA7C0YBzQAAivu+UKRR1ZIAAAAASUVORK5CYII=",
|
|
158
|
-
favorite:
|
|
159
|
-
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAKklEQVR42mNgGAWjYBTgBP+xAGLkRi0YQhbgM4wqBo9aMHgsGAWjgCAAALyAZqh4yyZZAAAAAElFTkSuQmCC",
|
|
160
|
-
share:
|
|
161
|
-
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAK0lEQVR42mNgGAVDDvxHAqMWjFowUiz4TwQYgRYQYxBFlo1aMPAWjAK6AgD2g7pUUT7VeAAAAABJRU5ErkJggg==",
|
|
162
|
-
sleepTimer:
|
|
163
|
-
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAYUlEQVR42tWUSQ4AIAgD+f+n8eJRliIQ6MmEZTSxJZokvipdXgZpAbzOO17AglIXI7XULxmCoEMwRGuWam6AdRsN4IJYTRH4PIDkhxTA92zUPNBcFDDHaOVR0RJ2LXG9QgemFVK8tpo4PwAAAABJRU5ErkJggg==",
|
|
164
|
-
playbackSpeed:
|
|
165
|
-
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAK0lEQVR42mNgGAVDDvxHAqMWjFowUiz4TwQYgRYQYxBFlo1aMPAWjAK6AgD2g7pUUT7VeAAAAABJRU5ErkJggg==",
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
function getTextTransformStyle(
|
|
169
|
-
textTransform: TextTransform
|
|
170
|
-
): "none" | "lowercase" | "uppercase" | "capitalize" {
|
|
171
|
-
return textTransform === "default" ? "none" : textTransform;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
function shouldShowLeadingIcon(leadingActionType?: LeadingActionType) {
|
|
175
|
-
return !!leadingActionType;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
function shouldShowTrailingIcon(showTrailingChevron?: boolean) {
|
|
179
|
-
return !!showTrailingChevron;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// ---------------------------------------------------------------------------
|
|
183
|
-
// Inner components
|
|
184
|
-
// ---------------------------------------------------------------------------
|
|
185
|
-
|
|
186
|
-
type ActionContainerProps = {
|
|
187
|
-
configuration: BackgroundConfiguration;
|
|
188
|
-
focused: boolean;
|
|
189
|
-
children: React.ReactNode;
|
|
190
|
-
};
|
|
191
|
-
|
|
192
|
-
function ActionContainer({
|
|
193
|
-
configuration,
|
|
194
|
-
focused,
|
|
195
|
-
children,
|
|
196
|
-
}: ActionContainerProps) {
|
|
197
|
-
return (
|
|
198
|
-
<View
|
|
199
|
-
style={{
|
|
200
|
-
backgroundColor: focused
|
|
201
|
-
? configuration.focusedColor
|
|
202
|
-
: configuration.defaultColor,
|
|
203
|
-
borderRadius: configuration.cornerRadius,
|
|
204
|
-
paddingTop: CONTAINER_SPEC.paddingTop,
|
|
205
|
-
paddingRight: CONTAINER_SPEC.paddingRight,
|
|
206
|
-
paddingBottom: CONTAINER_SPEC.paddingBottom,
|
|
207
|
-
paddingLeft: CONTAINER_SPEC.paddingLeft,
|
|
208
|
-
}}
|
|
209
|
-
>
|
|
210
|
-
{children}
|
|
211
|
-
</View>
|
|
212
|
-
);
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
type ActionTitleProps = {
|
|
216
|
-
title: string;
|
|
217
|
-
configuration: TitleConfiguration;
|
|
218
|
-
focused: boolean;
|
|
219
|
-
};
|
|
220
|
-
|
|
221
|
-
function ActionTitle({ title, configuration, focused }: ActionTitleProps) {
|
|
222
|
-
const letterSpacing =
|
|
223
|
-
Platform.OS === "ios"
|
|
224
|
-
? configuration.iosLetterSpacing
|
|
225
|
-
: configuration.androidLetterSpacing;
|
|
226
|
-
|
|
227
|
-
return (
|
|
228
|
-
<Text
|
|
229
|
-
style={{
|
|
230
|
-
flex: 1,
|
|
231
|
-
color: focused
|
|
232
|
-
? configuration.focusedFontColor
|
|
233
|
-
: configuration.fontColor,
|
|
234
|
-
fontSize: configuration.fontSize,
|
|
235
|
-
lineHeight: configuration.lineHeight,
|
|
236
|
-
letterSpacing,
|
|
237
|
-
fontFamily: Platform.select({
|
|
238
|
-
ios: configuration.iosFontFamily,
|
|
239
|
-
android: configuration.androidFontFamily,
|
|
240
|
-
default: configuration.androidFontFamily,
|
|
241
|
-
}),
|
|
242
|
-
textTransform: getTextTransformStyle(configuration.textTransform),
|
|
243
|
-
}}
|
|
244
|
-
numberOfLines={1}
|
|
245
|
-
>
|
|
246
|
-
{title}
|
|
247
|
-
</Text>
|
|
248
|
-
);
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
type ActionLeadingIconProps = {
|
|
252
|
-
actionType: LeadingActionType;
|
|
253
|
-
};
|
|
254
|
-
|
|
255
|
-
function ActionLeadingIcon({ actionType }: ActionLeadingIconProps) {
|
|
256
|
-
return (
|
|
257
|
-
<Image
|
|
258
|
-
source={{ uri: LEADING_ICON_ASSETS[actionType] }}
|
|
259
|
-
style={{ width: ICON_SPEC.width, height: ICON_SPEC.height }}
|
|
260
|
-
resizeMode="contain"
|
|
261
|
-
/>
|
|
262
|
-
);
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
type ActionTrailingIconProps = {
|
|
266
|
-
configuration: TrailingIconConfiguration;
|
|
267
|
-
};
|
|
268
|
-
|
|
269
|
-
function ActionTrailingIcon({ configuration }: ActionTrailingIconProps) {
|
|
270
|
-
const asset = configuration.chevronAsset ?? DEFAULT_CHEVRON_ASSET;
|
|
271
|
-
|
|
272
|
-
return (
|
|
273
|
-
<Image
|
|
274
|
-
source={{ uri: asset }}
|
|
275
|
-
style={{ width: ICON_SPEC.width, height: ICON_SPEC.height }}
|
|
276
|
-
resizeMode="contain"
|
|
277
|
-
/>
|
|
278
|
-
);
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
// ---------------------------------------------------------------------------
|
|
282
|
-
// Item
|
|
283
|
-
// ---------------------------------------------------------------------------
|
|
284
|
-
|
|
285
|
-
export function Item({ configuration, data, focused = false }: ItemProps) {
|
|
286
|
-
const { background, title, trailingIcon } = configuration;
|
|
287
|
-
const { title: label, leadingActionType, showTrailingChevron } = data;
|
|
288
|
-
|
|
289
|
-
const showLeading = shouldShowLeadingIcon(leadingActionType);
|
|
290
|
-
const showTrailing = shouldShowTrailingIcon(showTrailingChevron);
|
|
291
|
-
|
|
292
|
-
return (
|
|
293
|
-
<ActionContainer configuration={background} focused={focused}>
|
|
294
|
-
<View style={styles.row}>
|
|
295
|
-
{showLeading ? (
|
|
296
|
-
<>
|
|
297
|
-
<ActionLeadingIcon actionType={leadingActionType} />
|
|
298
|
-
<View style={{ width: CONTAINER_SPEC.horizontalGutter }} />
|
|
299
|
-
</>
|
|
300
|
-
) : null}
|
|
301
|
-
<ActionTitle title={label} configuration={title} focused={focused} />
|
|
302
|
-
{showTrailing ? (
|
|
303
|
-
<>
|
|
304
|
-
<View style={{ width: CONTAINER_SPEC.horizontalGutter }} />
|
|
305
|
-
<ActionTrailingIcon configuration={trailingIcon} />
|
|
306
|
-
</>
|
|
307
|
-
) : null}
|
|
308
|
-
</View>
|
|
309
|
-
</ActionContainer>
|
|
310
|
-
);
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
// ---------------------------------------------------------------------------
|
|
314
|
-
// Demo
|
|
315
|
-
// ---------------------------------------------------------------------------
|
|
316
|
-
|
|
317
|
-
export default function App() {
|
|
318
|
-
return (
|
|
319
|
-
<View style={styles.demoScreen}>
|
|
320
|
-
<Text style={styles.demoHeading}>Modal Action Building Block</Text>
|
|
321
|
-
|
|
322
|
-
<Text style={styles.demoLabel}>
|
|
323
|
-
Leading icon + text — Add To Queue (focused)
|
|
324
|
-
</Text>
|
|
325
|
-
<Item
|
|
326
|
-
configuration={DEFAULT_ACTION_CONFIGURATION}
|
|
327
|
-
data={{ title: "Add To Queue", leadingActionType: "addToQueue" }}
|
|
328
|
-
focused
|
|
329
|
-
/>
|
|
330
|
-
|
|
331
|
-
<Text style={styles.demoLabel}>
|
|
332
|
-
Leading icon + text + trailing chevron — Add To Playlist
|
|
333
|
-
</Text>
|
|
334
|
-
<Item
|
|
335
|
-
configuration={DEFAULT_ACTION_CONFIGURATION}
|
|
336
|
-
data={{
|
|
337
|
-
title: "Add To Playlist",
|
|
338
|
-
leadingActionType: "addToPlaylist",
|
|
339
|
-
showTrailingChevron: true,
|
|
340
|
-
}}
|
|
341
|
-
/>
|
|
342
|
-
|
|
343
|
-
<Text style={styles.demoLabel}>Text only — Go To Playlist</Text>
|
|
344
|
-
<Item
|
|
345
|
-
configuration={DEFAULT_ACTION_CONFIGURATION}
|
|
346
|
-
data={{ title: "Go To Playlist" }}
|
|
347
|
-
/>
|
|
348
|
-
|
|
349
|
-
<Text style={styles.demoLabel}>Leading icon + text — Favorite</Text>
|
|
350
|
-
<Item
|
|
351
|
-
configuration={DEFAULT_ACTION_CONFIGURATION}
|
|
352
|
-
data={{ title: "Favorite", leadingActionType: "favorite" }}
|
|
353
|
-
/>
|
|
354
|
-
|
|
355
|
-
<Text style={styles.demoLabel}>Leading icon + text — Share</Text>
|
|
356
|
-
<Item
|
|
357
|
-
configuration={DEFAULT_ACTION_CONFIGURATION}
|
|
358
|
-
data={{ title: "Share", leadingActionType: "share" }}
|
|
359
|
-
/>
|
|
360
|
-
|
|
361
|
-
<Text style={styles.demoLabel}>Text only — Go To Artist</Text>
|
|
362
|
-
<Item
|
|
363
|
-
configuration={DEFAULT_ACTION_CONFIGURATION}
|
|
364
|
-
data={{ title: "Go To Artist" }}
|
|
365
|
-
/>
|
|
366
|
-
</View>
|
|
367
|
-
);
|
|
368
|
-
}
|
|
File without changes
|