@pagopa/io-app-design-system 2.0.1 → 2.0.3
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/lib/commonjs/components/contentWrapper/ContentWrapper.js +2 -0
- package/lib/commonjs/components/contentWrapper/ContentWrapper.js.map +1 -1
- package/lib/commonjs/components/layout/BlockButtons.js +1 -1
- package/lib/commonjs/components/layout/FooterActions.js +162 -0
- package/lib/commonjs/components/layout/FooterActions.js.map +1 -0
- package/lib/commonjs/components/layout/FooterActionsInline.js +113 -0
- package/lib/commonjs/components/layout/FooterActionsInline.js.map +1 -0
- package/lib/commonjs/components/layout/FooterWithButtons.js +3 -1
- package/lib/commonjs/components/layout/FooterWithButtons.js.map +1 -1
- package/lib/commonjs/components/layout/HeaderSecondLevel.js +9 -4
- package/lib/commonjs/components/layout/HeaderSecondLevel.js.map +1 -1
- package/lib/commonjs/components/layout/hooks/index.js +28 -0
- package/lib/commonjs/components/layout/hooks/index.js.map +1 -0
- package/lib/commonjs/components/layout/hooks/useBottomMargins.js +31 -0
- package/lib/commonjs/components/layout/hooks/useBottomMargins.js.map +1 -0
- package/lib/commonjs/components/layout/hooks/useFooterActionsInlineMeasurements.js +30 -0
- package/lib/commonjs/components/layout/hooks/useFooterActionsInlineMeasurements.js.map +1 -0
- package/lib/commonjs/components/layout/hooks/useFooterActionsMeasurements.js +31 -0
- package/lib/commonjs/components/layout/hooks/useFooterActionsMeasurements.js.map +1 -0
- package/lib/commonjs/components/layout/index.js +33 -0
- package/lib/commonjs/components/layout/index.js.map +1 -1
- package/lib/commonjs/components/listitems/ListItemTransaction.js +4 -2
- package/lib/commonjs/components/listitems/ListItemTransaction.js.map +1 -1
- package/lib/commonjs/components/pictograms/Pictogram.js +10 -2
- package/lib/commonjs/components/pictograms/Pictogram.js.map +1 -1
- package/lib/commonjs/components/pictograms/svg/PictogramFingerprint.js +49 -0
- package/lib/commonjs/components/pictograms/svg/PictogramFingerprint.js.map +1 -0
- package/lib/commonjs/components/pictograms/svg/PictogramSmile.js +32 -0
- package/lib/commonjs/components/pictograms/svg/PictogramSmile.js.map +1 -0
- package/lib/commonjs/components/pictograms/svg/PictogramWalletDoc.js +46 -0
- package/lib/commonjs/components/pictograms/svg/PictogramWalletDoc.js.map +1 -0
- package/lib/commonjs/components/pictograms/svg/originals/PictogramFingerprint.svg +1 -0
- package/lib/commonjs/components/pictograms/svg/originals/PictogramSmile.svg +1 -0
- package/lib/commonjs/components/pictograms/svg/originals/PictogramWalletDoc.svg +1 -0
- package/lib/commonjs/core/IOColors.js +11 -4
- package/lib/commonjs/core/IOColors.js.map +1 -1
- package/lib/commonjs/core/IOSpacing.js +10 -1
- package/lib/commonjs/core/IOSpacing.js.map +1 -1
- package/lib/commonjs/core/index.js +0 -11
- package/lib/commonjs/core/index.js.map +1 -1
- package/lib/module/components/contentWrapper/ContentWrapper.js +2 -0
- package/lib/module/components/contentWrapper/ContentWrapper.js.map +1 -1
- package/lib/module/components/layout/BlockButtons.js +1 -1
- package/lib/module/components/layout/FooterActions.js +153 -0
- package/lib/module/components/layout/FooterActions.js.map +1 -0
- package/lib/module/components/layout/FooterActionsInline.js +104 -0
- package/lib/module/components/layout/FooterActionsInline.js.map +1 -0
- package/lib/module/components/layout/FooterWithButtons.js +3 -1
- package/lib/module/components/layout/FooterWithButtons.js.map +1 -1
- package/lib/module/components/layout/HeaderSecondLevel.js +10 -5
- package/lib/module/components/layout/HeaderSecondLevel.js.map +1 -1
- package/lib/module/components/layout/hooks/index.js +3 -0
- package/lib/module/components/layout/hooks/index.js.map +1 -0
- package/lib/module/components/layout/hooks/useBottomMargins.js +25 -0
- package/lib/module/components/layout/hooks/useBottomMargins.js.map +1 -0
- package/lib/module/components/layout/hooks/useFooterActionsInlineMeasurements.js +23 -0
- package/lib/module/components/layout/hooks/useFooterActionsInlineMeasurements.js.map +1 -0
- package/lib/module/components/layout/hooks/useFooterActionsMeasurements.js +24 -0
- package/lib/module/components/layout/hooks/useFooterActionsMeasurements.js.map +1 -0
- package/lib/module/components/layout/index.js +3 -0
- package/lib/module/components/layout/index.js.map +1 -1
- package/lib/module/components/listitems/ListItemTransaction.js +4 -2
- package/lib/module/components/listitems/ListItemTransaction.js.map +1 -1
- package/lib/module/components/pictograms/Pictogram.js +10 -2
- package/lib/module/components/pictograms/Pictogram.js.map +1 -1
- package/lib/module/components/pictograms/svg/PictogramFingerprint.js +41 -0
- package/lib/module/components/pictograms/svg/PictogramFingerprint.js.map +1 -0
- package/lib/module/components/pictograms/svg/PictogramSmile.js +24 -0
- package/lib/module/components/pictograms/svg/PictogramSmile.js.map +1 -0
- package/lib/module/components/pictograms/svg/PictogramWalletDoc.js +38 -0
- package/lib/module/components/pictograms/svg/PictogramWalletDoc.js.map +1 -0
- package/lib/module/components/pictograms/svg/originals/PictogramFingerprint.svg +1 -0
- package/lib/module/components/pictograms/svg/originals/PictogramSmile.svg +1 -0
- package/lib/module/components/pictograms/svg/originals/PictogramWalletDoc.svg +1 -0
- package/lib/module/core/IOColors.js +11 -4
- package/lib/module/core/IOColors.js.map +1 -1
- package/lib/module/core/IOSpacing.js +9 -0
- package/lib/module/core/IOSpacing.js.map +1 -1
- package/lib/module/core/index.js +0 -1
- package/lib/module/core/index.js.map +1 -1
- package/lib/typescript/components/contentWrapper/ContentWrapper.d.ts +4 -3
- package/lib/typescript/components/contentWrapper/ContentWrapper.d.ts.map +1 -1
- package/lib/typescript/components/layout/BlockButtons.d.ts +1 -1
- package/lib/typescript/components/layout/FooterActions.d.ts +45 -0
- package/lib/typescript/components/layout/FooterActions.d.ts.map +1 -0
- package/lib/typescript/components/layout/FooterActionsInline.d.ts +17 -0
- package/lib/typescript/components/layout/FooterActionsInline.d.ts.map +1 -0
- package/lib/typescript/components/layout/FooterWithButtons.d.ts +3 -1
- package/lib/typescript/components/layout/FooterWithButtons.d.ts.map +1 -1
- package/lib/typescript/components/layout/HeaderFirstLevel.d.ts +7 -7
- package/lib/typescript/components/layout/HeaderFirstLevel.d.ts.map +1 -1
- package/lib/typescript/components/layout/HeaderSecondLevel.d.ts +7 -7
- package/lib/typescript/components/layout/HeaderSecondLevel.d.ts.map +1 -1
- package/lib/typescript/components/layout/common.d.ts +1 -1
- package/lib/typescript/components/layout/common.d.ts.map +1 -1
- package/lib/typescript/components/layout/hooks/index.d.ts +3 -0
- package/lib/typescript/components/layout/hooks/index.d.ts.map +1 -0
- package/lib/typescript/components/layout/hooks/useBottomMargins.d.ts +5 -0
- package/lib/typescript/components/layout/hooks/useBottomMargins.d.ts.map +1 -0
- package/lib/typescript/components/layout/hooks/useFooterActionsInlineMeasurements.d.ts +17 -0
- package/lib/typescript/components/layout/hooks/useFooterActionsInlineMeasurements.d.ts.map +1 -0
- package/lib/typescript/components/layout/hooks/useFooterActionsMeasurements.d.ts +17 -0
- package/lib/typescript/components/layout/hooks/useFooterActionsMeasurements.d.ts.map +1 -0
- package/lib/typescript/components/layout/index.d.ts +3 -0
- package/lib/typescript/components/layout/index.d.ts.map +1 -1
- package/lib/typescript/components/listitems/ListItemTransaction.d.ts.map +1 -1
- package/lib/typescript/components/pictograms/Pictogram.d.ts +3 -0
- package/lib/typescript/components/pictograms/Pictogram.d.ts.map +1 -1
- package/lib/typescript/components/pictograms/svg/PictogramFingerprint.d.ts +5 -0
- package/lib/typescript/components/pictograms/svg/PictogramFingerprint.d.ts.map +1 -0
- package/lib/typescript/components/pictograms/svg/PictogramSmile.d.ts +5 -0
- package/lib/typescript/components/pictograms/svg/PictogramSmile.d.ts.map +1 -0
- package/lib/typescript/components/pictograms/svg/PictogramWalletDoc.d.ts +5 -0
- package/lib/typescript/components/pictograms/svg/PictogramWalletDoc.d.ts.map +1 -0
- package/lib/typescript/core/IOColors.d.ts +1 -1
- package/lib/typescript/core/IOColors.d.ts.map +1 -1
- package/lib/typescript/core/IOSpacing.d.ts +8 -0
- package/lib/typescript/core/IOSpacing.d.ts.map +1 -1
- package/lib/typescript/core/index.d.ts +0 -1
- package/lib/typescript/core/index.d.ts.map +1 -1
- package/lib/typescript/utils/fonts.d.ts +2 -2
- package/package.json +1 -1
- package/src/components/contentWrapper/ContentWrapper.tsx +5 -2
- package/src/components/layout/BlockButtons.tsx +1 -1
- package/src/components/layout/FooterActions.tsx +260 -0
- package/src/components/layout/FooterActionsInline.tsx +137 -0
- package/src/components/layout/FooterWithButtons.tsx +3 -1
- package/src/components/layout/HeaderFirstLevel.tsx +7 -7
- package/src/components/layout/HeaderSecondLevel.tsx +20 -12
- package/src/components/layout/common.ts +1 -1
- package/src/components/layout/hooks/index.ts +2 -0
- package/src/components/layout/hooks/useBottomMargins.ts +30 -0
- package/src/components/layout/hooks/useFooterActionsInlineMeasurements.ts +38 -0
- package/src/components/layout/hooks/useFooterActionsMeasurements.ts +35 -0
- package/src/components/layout/index.tsx +3 -0
- package/src/components/listitems/ListItemTransaction.tsx +5 -2
- package/src/components/pictograms/Pictogram.tsx +11 -2
- package/src/components/pictograms/svg/PictogramFingerprint.tsx +50 -0
- package/src/components/pictograms/svg/PictogramSmile.tsx +22 -0
- package/src/components/pictograms/svg/PictogramWalletDoc.tsx +44 -0
- package/src/components/pictograms/svg/originals/PictogramFingerprint.svg +1 -0
- package/src/components/pictograms/svg/originals/PictogramSmile.svg +1 -0
- package/src/components/pictograms/svg/originals/PictogramWalletDoc.svg +1 -0
- package/src/core/IOColors.ts +13 -2
- package/src/core/IOSpacing.ts +14 -0
- package/src/core/index.ts +0 -1
- package/lib/commonjs/core/IOStyleVariables.js +0 -14
- package/lib/commonjs/core/IOStyleVariables.js.map +0 -1
- package/lib/module/core/IOStyleVariables.js +0 -7
- package/lib/module/core/IOStyleVariables.js.map +0 -1
- package/lib/typescript/core/IOStyleVariables.d.ts +0 -7
- package/lib/typescript/core/IOStyleVariables.d.ts.map +0 -1
- package/src/core/IOStyleVariables.ts +0 -6
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { ComponentProps, Fragment, PropsWithChildren, useState } from "react";
|
|
3
|
+
import {
|
|
4
|
+
ColorValue,
|
|
5
|
+
LayoutChangeEvent,
|
|
6
|
+
LayoutRectangle,
|
|
7
|
+
StyleSheet,
|
|
8
|
+
Text,
|
|
9
|
+
View,
|
|
10
|
+
ViewStyle
|
|
11
|
+
} from "react-native";
|
|
12
|
+
import Animated from "react-native-reanimated";
|
|
13
|
+
import {
|
|
14
|
+
IOColors,
|
|
15
|
+
IOSpacer,
|
|
16
|
+
IOSpacing,
|
|
17
|
+
IOVisualCostants,
|
|
18
|
+
buttonSolidHeight,
|
|
19
|
+
hexToRgba,
|
|
20
|
+
useIOExperimentalDesign,
|
|
21
|
+
useIOTheme
|
|
22
|
+
} from "../../core";
|
|
23
|
+
import { WithTestID } from "../../utils/types";
|
|
24
|
+
import { ButtonLink, ButtonOutline, ButtonSolid } from "../buttons";
|
|
25
|
+
import { VSpacer } from "../spacer";
|
|
26
|
+
import { useBottomMargins } from "./hooks/useBottomMargins";
|
|
27
|
+
|
|
28
|
+
type FooterSingleButton = {
|
|
29
|
+
type: "SingleButton";
|
|
30
|
+
primary: Omit<ComponentProps<typeof ButtonSolid>, "fullWidth">;
|
|
31
|
+
secondary?: never;
|
|
32
|
+
tertiary?: never;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
type FooterTwoButtons = {
|
|
36
|
+
type: "TwoButtons";
|
|
37
|
+
primary: Omit<ComponentProps<typeof ButtonSolid>, "fullWidth">;
|
|
38
|
+
secondary: Omit<ComponentProps<typeof ButtonLink>, "color">;
|
|
39
|
+
tertiary?: never;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
type FooterThreeButtons = {
|
|
43
|
+
type: "ThreeButtons";
|
|
44
|
+
primary: Omit<ComponentProps<typeof ButtonSolid>, "fullWidth">;
|
|
45
|
+
secondary: Omit<ComponentProps<typeof ButtonOutline>, "fullWidth" | "color">;
|
|
46
|
+
tertiary: Omit<ComponentProps<typeof ButtonLink>, "color">;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export type FooterActionsMeasurements = {
|
|
50
|
+
// Height of the "Actions" block
|
|
51
|
+
actionBlockHeight: number;
|
|
52
|
+
/* Height of the safe bottom area. It includes:
|
|
53
|
+
- Margin between screen content
|
|
54
|
+
and actions (contentEndMargin)
|
|
55
|
+
- Actions block height
|
|
56
|
+
- Eventual safe area margin (bottomMargin)
|
|
57
|
+
This is the total bottom padding that needs
|
|
58
|
+
to be applied to the ScrollView.
|
|
59
|
+
*/
|
|
60
|
+
safeBottomAreaHeight: number;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
type FooterActions = FooterSingleButton | FooterTwoButtons | FooterThreeButtons;
|
|
64
|
+
|
|
65
|
+
type FooterAnimatedStyles = {
|
|
66
|
+
/* Apply object returned by `useAnimatedStyle` to the main block */
|
|
67
|
+
mainBlock?: Animated.AnimateStyle<ViewStyle>;
|
|
68
|
+
/* Apply object returned by `useAnimatedStyle` to the background */
|
|
69
|
+
background?: Animated.AnimateStyle<ViewStyle>;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
type FooterActionsProps = WithTestID<
|
|
73
|
+
PropsWithChildren<{
|
|
74
|
+
actions?: FooterActions;
|
|
75
|
+
onMeasure?: (measurements: FooterActionsMeasurements) => void;
|
|
76
|
+
animatedStyles?: FooterAnimatedStyles;
|
|
77
|
+
/* Make the background transparent */
|
|
78
|
+
transparent?: boolean;
|
|
79
|
+
/* Don't include safe area insets */
|
|
80
|
+
excludeSafeAreaMargins?: boolean;
|
|
81
|
+
/* Fixed at the bottom of the screen */
|
|
82
|
+
fixed?: boolean;
|
|
83
|
+
/* Show the following elements:
|
|
84
|
+
- Opaque red background to show the component boundaries
|
|
85
|
+
- Height of the component */
|
|
86
|
+
debugMode?: boolean;
|
|
87
|
+
}>
|
|
88
|
+
>;
|
|
89
|
+
|
|
90
|
+
/* Margin between ButtonSolid and ButtonOutline */
|
|
91
|
+
const spaceBetweenActions: IOSpacer = 16;
|
|
92
|
+
/* Margin between ButtonSolid and ButtonLink */
|
|
93
|
+
const spaceBetweenActionAndLink: IOSpacer = 16;
|
|
94
|
+
|
|
95
|
+
const styles = StyleSheet.create({
|
|
96
|
+
buttonContainer: {
|
|
97
|
+
paddingHorizontal: IOVisualCostants.appMarginDefault,
|
|
98
|
+
width: "100%",
|
|
99
|
+
flexShrink: 0
|
|
100
|
+
},
|
|
101
|
+
debugText: {
|
|
102
|
+
position: "absolute",
|
|
103
|
+
right: 8,
|
|
104
|
+
top: -16,
|
|
105
|
+
color: IOColors.black,
|
|
106
|
+
fontSize: 9,
|
|
107
|
+
opacity: 0.75
|
|
108
|
+
},
|
|
109
|
+
blockShadow: {
|
|
110
|
+
shadowColor: IOColors.black,
|
|
111
|
+
shadowOffset: {
|
|
112
|
+
width: 0,
|
|
113
|
+
height: -4
|
|
114
|
+
},
|
|
115
|
+
shadowOpacity: 0.1,
|
|
116
|
+
shadowRadius: 32
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
export const FooterActions = ({
|
|
121
|
+
actions,
|
|
122
|
+
excludeSafeAreaMargins = false,
|
|
123
|
+
animatedStyles,
|
|
124
|
+
fixed = true,
|
|
125
|
+
transparent = false,
|
|
126
|
+
onMeasure,
|
|
127
|
+
testID,
|
|
128
|
+
debugMode = false
|
|
129
|
+
}: FooterActionsProps) => {
|
|
130
|
+
const theme = useIOTheme();
|
|
131
|
+
const { isExperimental } = useIOExperimentalDesign();
|
|
132
|
+
|
|
133
|
+
const { bottomMargin, extraBottomMargin } = useBottomMargins(
|
|
134
|
+
!!actions?.secondary,
|
|
135
|
+
excludeSafeAreaMargins
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
/* Total height of actions */
|
|
139
|
+
const [actionBlockHeight, setActionBlockHeight] =
|
|
140
|
+
useState<LayoutRectangle["height"]>(0);
|
|
141
|
+
|
|
142
|
+
/* Background color should be app main background
|
|
143
|
+
(both light and dark themes) */
|
|
144
|
+
const HEADER_BG_COLOR: ColorValue = IOColors[theme["appBackground-primary"]];
|
|
145
|
+
const TRANSPARENT_BG_COLOR: ColorValue = "transparent";
|
|
146
|
+
const BUTTONSOLID_HEIGHT = isExperimental ? buttonSolidHeight : 40;
|
|
147
|
+
|
|
148
|
+
/* Safe background block. Cover everything until it reaches
|
|
149
|
+
the half of the primary action button. It avoids
|
|
150
|
+
glitchy behavior underneath. */
|
|
151
|
+
const safeBackgroundBlockHeight =
|
|
152
|
+
bottomMargin + actionBlockHeight - BUTTONSOLID_HEIGHT / 2;
|
|
153
|
+
|
|
154
|
+
const getActionBlockMeasurements = (event: LayoutChangeEvent) => {
|
|
155
|
+
const { height } = event.nativeEvent.layout;
|
|
156
|
+
setActionBlockHeight(height);
|
|
157
|
+
/* Height of the safe bottom area, applied to the ScrollView:
|
|
158
|
+
Actions + Content end margin */
|
|
159
|
+
const safeBottomAreaHeight =
|
|
160
|
+
bottomMargin + height + IOSpacing.screenEndMargin;
|
|
161
|
+
onMeasure?.({ actionBlockHeight: height, safeBottomAreaHeight });
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
return (
|
|
165
|
+
<Animated.View
|
|
166
|
+
style={[
|
|
167
|
+
{
|
|
168
|
+
width: "100%",
|
|
169
|
+
paddingBottom: bottomMargin
|
|
170
|
+
},
|
|
171
|
+
fixed
|
|
172
|
+
? { position: "absolute", bottom: 0 }
|
|
173
|
+
: { marginTop: IOSpacing.screenEndMargin },
|
|
174
|
+
debugMode && {
|
|
175
|
+
backgroundColor: hexToRgba(IOColors["error-500"], 0.15)
|
|
176
|
+
},
|
|
177
|
+
animatedStyles?.mainBlock
|
|
178
|
+
]}
|
|
179
|
+
testID={testID}
|
|
180
|
+
>
|
|
181
|
+
{/* Safe background block. It's added because when you swipe up
|
|
182
|
+
quickly, the content below is visible for about 100ms. Without this
|
|
183
|
+
block, the content scrolls underneath. */}
|
|
184
|
+
<Animated.View
|
|
185
|
+
style={[
|
|
186
|
+
{
|
|
187
|
+
...(fixed && {
|
|
188
|
+
width: "100%",
|
|
189
|
+
height: safeBackgroundBlockHeight,
|
|
190
|
+
position: "absolute",
|
|
191
|
+
bottom: 0,
|
|
192
|
+
backgroundColor: transparent
|
|
193
|
+
? TRANSPARENT_BG_COLOR
|
|
194
|
+
: HEADER_BG_COLOR
|
|
195
|
+
}),
|
|
196
|
+
...(fixed ? styles.blockShadow : null)
|
|
197
|
+
},
|
|
198
|
+
animatedStyles?.background
|
|
199
|
+
]}
|
|
200
|
+
pointerEvents="none"
|
|
201
|
+
/>
|
|
202
|
+
|
|
203
|
+
<View
|
|
204
|
+
style={styles.buttonContainer}
|
|
205
|
+
onLayout={getActionBlockMeasurements}
|
|
206
|
+
pointerEvents="box-none"
|
|
207
|
+
>
|
|
208
|
+
{debugMode && (
|
|
209
|
+
<Text style={styles.debugText}>{`Height: ${actionBlockHeight}`}</Text>
|
|
210
|
+
)}
|
|
211
|
+
|
|
212
|
+
{renderActions(actions, extraBottomMargin)}
|
|
213
|
+
</View>
|
|
214
|
+
</Animated.View>
|
|
215
|
+
);
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
const renderActions = (
|
|
219
|
+
actions: FooterActions | undefined,
|
|
220
|
+
extraBottomMargin: number
|
|
221
|
+
) => {
|
|
222
|
+
if (!actions) {
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
const {
|
|
226
|
+
type,
|
|
227
|
+
primary: primaryAction,
|
|
228
|
+
secondary: secondaryAction,
|
|
229
|
+
tertiary: tertiaryAction
|
|
230
|
+
} = actions;
|
|
231
|
+
return (
|
|
232
|
+
<Fragment>
|
|
233
|
+
{primaryAction && <ButtonSolid fullWidth {...primaryAction} />}
|
|
234
|
+
{type === "TwoButtons" && secondaryAction && (
|
|
235
|
+
<View style={{ alignSelf: "center", marginBottom: extraBottomMargin }}>
|
|
236
|
+
<VSpacer size={spaceBetweenActionAndLink} />
|
|
237
|
+
<ButtonLink color="primary" {...secondaryAction} />
|
|
238
|
+
</View>
|
|
239
|
+
)}
|
|
240
|
+
{type === "ThreeButtons" && (
|
|
241
|
+
<>
|
|
242
|
+
{secondaryAction && (
|
|
243
|
+
<>
|
|
244
|
+
<VSpacer size={spaceBetweenActions} />
|
|
245
|
+
<ButtonOutline fullWidth color="primary" {...secondaryAction} />
|
|
246
|
+
</>
|
|
247
|
+
)}
|
|
248
|
+
{tertiaryAction && (
|
|
249
|
+
<View
|
|
250
|
+
style={{ alignSelf: "center", marginBottom: extraBottomMargin }}
|
|
251
|
+
>
|
|
252
|
+
<VSpacer size={spaceBetweenActionAndLink} />
|
|
253
|
+
<ButtonLink color="primary" {...tertiaryAction} />
|
|
254
|
+
</View>
|
|
255
|
+
)}
|
|
256
|
+
</>
|
|
257
|
+
)}
|
|
258
|
+
</Fragment>
|
|
259
|
+
);
|
|
260
|
+
};
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { ComponentProps, PropsWithChildren } from "react";
|
|
3
|
+
import { ColorValue, LayoutChangeEvent, StyleSheet, View } from "react-native";
|
|
4
|
+
import {
|
|
5
|
+
IOColors,
|
|
6
|
+
IOSpacer,
|
|
7
|
+
IOSpacing,
|
|
8
|
+
IOSpacingScale,
|
|
9
|
+
IOVisualCostants,
|
|
10
|
+
useIOTheme,
|
|
11
|
+
useIOThemeContext
|
|
12
|
+
} from "../../core";
|
|
13
|
+
import { WithTestID } from "../../utils/types";
|
|
14
|
+
import { ButtonOutline, ButtonSolid } from "../buttons";
|
|
15
|
+
import { HSpacer } from "../spacer";
|
|
16
|
+
import { useBottomMargins } from "./hooks/useBottomMargins";
|
|
17
|
+
|
|
18
|
+
export type FooterActionsInlineMeasurements = {
|
|
19
|
+
/* Height of the safe bottom area. It includes:
|
|
20
|
+
- Margin between screen content
|
|
21
|
+
and actions (contentEndMargin)
|
|
22
|
+
- Actions block height
|
|
23
|
+
- Eventual safe area margin (bottomMargin)
|
|
24
|
+
This is the total bottom padding that needs
|
|
25
|
+
to be applied to the ScrollView.
|
|
26
|
+
*/
|
|
27
|
+
safeBottomAreaHeight: number;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
type FooterActionsInline = WithTestID<
|
|
31
|
+
PropsWithChildren<{
|
|
32
|
+
startAction: Omit<ComponentProps<typeof ButtonOutline>, "fullWidth">;
|
|
33
|
+
endAction: Omit<ComponentProps<typeof ButtonSolid>, "fullWidth">;
|
|
34
|
+
onMeasure?: (measurements: FooterActionsInlineMeasurements) => void;
|
|
35
|
+
/* Don't include safe area insets */
|
|
36
|
+
excludeSafeAreaMargins?: boolean;
|
|
37
|
+
/* Fixed at the bottom of the screen */
|
|
38
|
+
fixed?: boolean;
|
|
39
|
+
}>
|
|
40
|
+
>;
|
|
41
|
+
|
|
42
|
+
/* Margin between ButtonSolid and ButtonOutline */
|
|
43
|
+
const spaceBetweenActions: IOSpacer = 16;
|
|
44
|
+
|
|
45
|
+
const styles = StyleSheet.create({
|
|
46
|
+
buttonContainer: {
|
|
47
|
+
paddingHorizontal: IOVisualCostants.appMarginDefault,
|
|
48
|
+
width: "100%",
|
|
49
|
+
flexShrink: 0
|
|
50
|
+
},
|
|
51
|
+
buttonWrapper: {
|
|
52
|
+
flex: 1
|
|
53
|
+
},
|
|
54
|
+
blockShadow: {
|
|
55
|
+
shadowColor: IOColors.black,
|
|
56
|
+
shadowOffset: {
|
|
57
|
+
width: 0,
|
|
58
|
+
height: -4
|
|
59
|
+
},
|
|
60
|
+
shadowOpacity: 0.1,
|
|
61
|
+
shadowRadius: 32,
|
|
62
|
+
elevation: 10 // Prop supported on Android only
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
export const FooterActionsInline = ({
|
|
67
|
+
startAction,
|
|
68
|
+
endAction,
|
|
69
|
+
excludeSafeAreaMargins = false,
|
|
70
|
+
fixed = true,
|
|
71
|
+
onMeasure,
|
|
72
|
+
testID
|
|
73
|
+
}: FooterActionsInline) => {
|
|
74
|
+
const theme = useIOTheme();
|
|
75
|
+
const { themeType } = useIOThemeContext();
|
|
76
|
+
|
|
77
|
+
const { bottomMargin } = useBottomMargins(false, excludeSafeAreaMargins);
|
|
78
|
+
|
|
79
|
+
/* Top padding applied above the actions */
|
|
80
|
+
const topSpacingValue: IOSpacingScale = 16;
|
|
81
|
+
const topSpacing = fixed ? topSpacingValue : 0;
|
|
82
|
+
|
|
83
|
+
/* Background color should be app main background
|
|
84
|
+
(both light and dark themes) */
|
|
85
|
+
const HEADER_BG_COLOR: ColorValue = IOColors[theme["appBackground-primary"]];
|
|
86
|
+
|
|
87
|
+
const getActionBlockMeasurements = (event: LayoutChangeEvent) => {
|
|
88
|
+
const { height } = event.nativeEvent.layout;
|
|
89
|
+
/* Height of the safe bottom area, applied to the ScrollView:
|
|
90
|
+
Actions + Screen end margin */
|
|
91
|
+
const safeBottomAreaHeight =
|
|
92
|
+
bottomMargin + height + IOSpacing.screenEndMargin;
|
|
93
|
+
onMeasure?.({ safeBottomAreaHeight });
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<View
|
|
98
|
+
style={[
|
|
99
|
+
{
|
|
100
|
+
width: "100%",
|
|
101
|
+
paddingBottom: bottomMargin
|
|
102
|
+
},
|
|
103
|
+
fixed
|
|
104
|
+
? {
|
|
105
|
+
position: "absolute",
|
|
106
|
+
bottom: 0,
|
|
107
|
+
backgroundColor: HEADER_BG_COLOR
|
|
108
|
+
}
|
|
109
|
+
: { marginTop: IOSpacing.screenEndMargin },
|
|
110
|
+
/* Apply shadow only on light theme OR if fixed */
|
|
111
|
+
fixed || themeType === "light" ? styles.blockShadow : {},
|
|
112
|
+
/* Apply bottom border only on dark theme */
|
|
113
|
+
themeType === "dark" && {
|
|
114
|
+
borderTopColor: IOColors[theme["divider-bottomBar"]],
|
|
115
|
+
borderTopWidth: 1
|
|
116
|
+
}
|
|
117
|
+
]}
|
|
118
|
+
testID={testID}
|
|
119
|
+
>
|
|
120
|
+
<View
|
|
121
|
+
style={[styles.buttonContainer, { paddingTop: topSpacing }]}
|
|
122
|
+
onLayout={getActionBlockMeasurements}
|
|
123
|
+
pointerEvents="box-none"
|
|
124
|
+
>
|
|
125
|
+
<View style={{ flexDirection: "row" }}>
|
|
126
|
+
<View style={styles.buttonWrapper}>
|
|
127
|
+
<ButtonOutline fullWidth {...startAction} />
|
|
128
|
+
</View>
|
|
129
|
+
<HSpacer size={spaceBetweenActions} />
|
|
130
|
+
<View style={styles.buttonWrapper}>
|
|
131
|
+
<ButtonSolid fullWidth {...endAction} />
|
|
132
|
+
</View>
|
|
133
|
+
</View>
|
|
134
|
+
</View>
|
|
135
|
+
</View>
|
|
136
|
+
);
|
|
137
|
+
};
|
|
@@ -44,7 +44,9 @@ const verticalSpacing: IOSpacingScale = 16;
|
|
|
44
44
|
/**
|
|
45
45
|
* Implements a component that show buttons as sticky footer
|
|
46
46
|
* It can include 1, 2 or 3 buttons. If they are 2, they can have the inlineHalf or the inlineOneThird style
|
|
47
|
-
* @deprecated This component is deprecated. Use `FooterActions`
|
|
47
|
+
* @deprecated This component is deprecated. Use `FooterActions` or `FooterActionsInline` instead.
|
|
48
|
+
* `FooterActionsInline` is the official replacement for `FooterWithButtons`, but use it only
|
|
49
|
+
* if explicitly required.
|
|
48
50
|
*/
|
|
49
51
|
export const FooterWithButtons = ({
|
|
50
52
|
sticky = false,
|
|
@@ -23,7 +23,7 @@ import { WithTestID } from "../../utils/types";
|
|
|
23
23
|
import { IconButton } from "../buttons";
|
|
24
24
|
import { HSpacer } from "../spacer";
|
|
25
25
|
import { H3 } from "../typography";
|
|
26
|
-
import {
|
|
26
|
+
import { HeaderActionProps } from "./common";
|
|
27
27
|
|
|
28
28
|
type CommonProps = WithTestID<{
|
|
29
29
|
title: string;
|
|
@@ -41,23 +41,23 @@ interface Base extends CommonProps {
|
|
|
41
41
|
|
|
42
42
|
interface OneAction extends CommonProps {
|
|
43
43
|
type: "singleAction";
|
|
44
|
-
firstAction:
|
|
44
|
+
firstAction: HeaderActionProps;
|
|
45
45
|
secondAction?: never;
|
|
46
46
|
thirdAction?: never;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
interface TwoActions extends CommonProps {
|
|
50
50
|
type: "twoActions";
|
|
51
|
-
firstAction:
|
|
52
|
-
secondAction:
|
|
51
|
+
firstAction: HeaderActionProps;
|
|
52
|
+
secondAction: HeaderActionProps;
|
|
53
53
|
thirdAction?: never;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
interface ThreeActions extends CommonProps {
|
|
57
57
|
type: "threeActions";
|
|
58
|
-
firstAction:
|
|
59
|
-
secondAction:
|
|
60
|
-
thirdAction:
|
|
58
|
+
firstAction: HeaderActionProps;
|
|
59
|
+
secondAction: HeaderActionProps;
|
|
60
|
+
thirdAction: HeaderActionProps;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
export type HeaderFirstLevel = Base | OneAction | TwoActions | ThreeActions;
|
|
@@ -30,14 +30,15 @@ import {
|
|
|
30
30
|
hexToRgba,
|
|
31
31
|
iconBtnSizeSmall,
|
|
32
32
|
useIOExperimentalDesign,
|
|
33
|
-
useIOTheme
|
|
33
|
+
useIOTheme,
|
|
34
|
+
useIOThemeContext
|
|
34
35
|
} from "../../core";
|
|
35
36
|
import type { IOSpacer, IOSpacingScale } from "../../core/IOSpacing";
|
|
36
37
|
import { WithTestID } from "../../utils/types";
|
|
37
38
|
import IconButton from "../buttons/IconButton";
|
|
38
39
|
import { HSpacer } from "../spacer";
|
|
39
40
|
import { IOText } from "../typography";
|
|
40
|
-
import {
|
|
41
|
+
import { HeaderActionProps } from "./common";
|
|
41
42
|
|
|
42
43
|
type ScrollValues = {
|
|
43
44
|
contentOffsetY: SharedValue<number>;
|
|
@@ -85,23 +86,23 @@ interface Base extends CommonProps {
|
|
|
85
86
|
|
|
86
87
|
interface OneAction extends CommonProps {
|
|
87
88
|
type: "singleAction";
|
|
88
|
-
firstAction:
|
|
89
|
+
firstAction: HeaderActionProps;
|
|
89
90
|
secondAction?: never;
|
|
90
91
|
thirdAction?: never;
|
|
91
92
|
}
|
|
92
93
|
|
|
93
94
|
interface TwoActions extends CommonProps {
|
|
94
95
|
type: "twoActions";
|
|
95
|
-
firstAction:
|
|
96
|
-
secondAction:
|
|
96
|
+
firstAction: HeaderActionProps;
|
|
97
|
+
secondAction: HeaderActionProps;
|
|
97
98
|
thirdAction?: never;
|
|
98
99
|
}
|
|
99
100
|
|
|
100
101
|
interface ThreeActions extends CommonProps {
|
|
101
102
|
type: "threeActions";
|
|
102
|
-
firstAction:
|
|
103
|
-
secondAction:
|
|
104
|
-
thirdAction:
|
|
103
|
+
firstAction: HeaderActionProps;
|
|
104
|
+
secondAction: HeaderActionProps;
|
|
105
|
+
thirdAction: HeaderActionProps;
|
|
105
106
|
}
|
|
106
107
|
|
|
107
108
|
export type HeaderSecondLevel = BackProps &
|
|
@@ -156,21 +157,26 @@ export const HeaderSecondLevel = ({
|
|
|
156
157
|
|
|
157
158
|
const { isExperimental } = useIOExperimentalDesign();
|
|
158
159
|
const theme = useIOTheme();
|
|
160
|
+
const { themeType } = useIOThemeContext();
|
|
159
161
|
const insets = useSafeAreaInsets();
|
|
160
162
|
const isTitleAccessible = React.useMemo(() => !!title.trim(), [title]);
|
|
161
163
|
const paddingTop = useSharedValue(ignoreSafeAreaMargin ? 0 : insets.top);
|
|
162
164
|
|
|
163
165
|
const AnimatedIOText = Animated.createAnimatedComponent(IOText);
|
|
164
166
|
|
|
167
|
+
const iconButtonColorDefault: ComponentProps<typeof IconButton>["color"] =
|
|
168
|
+
themeType === "dark" ? "contrast" : "neutral";
|
|
169
|
+
|
|
165
170
|
const iconButtonColor: ComponentProps<typeof IconButton>["color"] =
|
|
166
|
-
variant === "
|
|
171
|
+
variant === "contrast" ? "contrast" : iconButtonColorDefault;
|
|
172
|
+
|
|
167
173
|
const titleColor: ColorValue =
|
|
168
174
|
variant === "neutral"
|
|
169
175
|
? IOColors[theme["textHeading-default"]]
|
|
170
176
|
: IOColors.white;
|
|
171
177
|
|
|
172
178
|
/* Visual attributes when there are transitions between states */
|
|
173
|
-
const HEADER_DEFAULT_BG_COLOR: IOColors = "
|
|
179
|
+
const HEADER_DEFAULT_BG_COLOR: IOColors = theme["appBackground-primary"];
|
|
174
180
|
|
|
175
181
|
const headerBgColorTransparentState = backgroundColor
|
|
176
182
|
? hexToRgba(backgroundColor, 0)
|
|
@@ -181,10 +187,12 @@ export const HeaderSecondLevel = ({
|
|
|
181
187
|
const headerBgColorSolidState =
|
|
182
188
|
backgroundColor ?? IOColors[HEADER_DEFAULT_BG_COLOR];
|
|
183
189
|
|
|
190
|
+
const borderColorDefault = IOColors[theme["divider-default"]];
|
|
191
|
+
|
|
184
192
|
const borderColorTransparentState = backgroundColor
|
|
185
193
|
? hexToRgba(backgroundColor, 0)
|
|
186
|
-
: hexToRgba(
|
|
187
|
-
const borderColorSolidState = backgroundColor ??
|
|
194
|
+
: hexToRgba(borderColorDefault, 0);
|
|
195
|
+
const borderColorSolidState = backgroundColor ?? borderColorDefault;
|
|
188
196
|
|
|
189
197
|
useLayoutEffect(() => {
|
|
190
198
|
if (isTitleAccessible) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { IconButton } from "../buttons";
|
|
3
3
|
|
|
4
|
-
export type
|
|
4
|
+
export type HeaderActionProps = Pick<
|
|
5
5
|
React.ComponentProps<typeof IconButton>,
|
|
6
6
|
"icon" | "onPress" | "accessibilityLabel" | "accessibilityHint" | "testID"
|
|
7
7
|
>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
2
|
+
import { IOSpacingScale, IOVisualCostants } from "../../../core";
|
|
3
|
+
|
|
4
|
+
/* Extra bottom margin for iPhone bottom handle because
|
|
5
|
+
ButtonLink doesn't have a fixed height */
|
|
6
|
+
const extraSafeAreaMargin: IOSpacingScale = 8;
|
|
7
|
+
|
|
8
|
+
export const useBottomMargins = (
|
|
9
|
+
withSecondaryAction: boolean = false,
|
|
10
|
+
excludeSafeAreaMargins: boolean = false
|
|
11
|
+
) => {
|
|
12
|
+
const insets = useSafeAreaInsets();
|
|
13
|
+
const needSafeAreaMargin = insets.bottom !== 0;
|
|
14
|
+
|
|
15
|
+
/* Check if the iPhone bottom handle is present.
|
|
16
|
+
If not, or if you don't need safe area insets,
|
|
17
|
+
add a default margin to prevent the button
|
|
18
|
+
from sticking to the bottom. */
|
|
19
|
+
const bottomMargin =
|
|
20
|
+
!needSafeAreaMargin || excludeSafeAreaMargins
|
|
21
|
+
? IOVisualCostants.appMarginDefault
|
|
22
|
+
: insets.bottom;
|
|
23
|
+
|
|
24
|
+
/* When the secondary action is visible, add extra margin
|
|
25
|
+
to avoid little space from iPhone bottom handle */
|
|
26
|
+
const extraBottomMargin =
|
|
27
|
+
withSecondaryAction && needSafeAreaMargin ? extraSafeAreaMargin : 0;
|
|
28
|
+
|
|
29
|
+
return { bottomMargin, extraBottomMargin };
|
|
30
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
import { FooterActionsInlineMeasurements } from "../FooterActionsInline";
|
|
3
|
+
|
|
4
|
+
type UseFooterActionsInlineMeasurementsProps = {
|
|
5
|
+
footerActionsInlineMeasurements: FooterActionsInlineMeasurements;
|
|
6
|
+
handleFooterActionsInlineMeasurements: (
|
|
7
|
+
values: FooterActionsInlineMeasurements
|
|
8
|
+
) => void;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Custom hook to handle the `FooterActions` measurements
|
|
12
|
+
* @returns
|
|
13
|
+
* - `footerActionsInlineMeasurements`
|
|
14
|
+
* Object containing the `FooterActionsInline` measurements
|
|
15
|
+
* - `handleFooterActionsInlineMeasurements`
|
|
16
|
+
* Function to update the footer actions measurements
|
|
17
|
+
* (to be applied to `onMeasure` prop of `FooterActionsInline`)
|
|
18
|
+
*/
|
|
19
|
+
export const useFooterActionsInlineMeasurements =
|
|
20
|
+
(): UseFooterActionsInlineMeasurementsProps => {
|
|
21
|
+
const [
|
|
22
|
+
footerActionsInlineMeasurements,
|
|
23
|
+
setFooterActionsInlineMeasurements
|
|
24
|
+
] = useState<FooterActionsInlineMeasurements>({
|
|
25
|
+
safeBottomAreaHeight: 0
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const handleFooterActionsInlineMeasurements = (
|
|
29
|
+
values: FooterActionsInlineMeasurements
|
|
30
|
+
) => {
|
|
31
|
+
setFooterActionsInlineMeasurements(values);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
footerActionsInlineMeasurements,
|
|
36
|
+
handleFooterActionsInlineMeasurements
|
|
37
|
+
};
|
|
38
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
import { FooterActionsMeasurements } from "../FooterActions";
|
|
3
|
+
|
|
4
|
+
type UseFooterActionsMeasurementsProps = {
|
|
5
|
+
footerActionsMeasurements: FooterActionsMeasurements;
|
|
6
|
+
handleFooterActionsMeasurements: (values: FooterActionsMeasurements) => void;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Custom hook to handle the `FooterActions` measurements
|
|
10
|
+
* @returns
|
|
11
|
+
* - `footerActionsMeasurements`
|
|
12
|
+
* Object containing the `FooterActions` measurements
|
|
13
|
+
* - `handleFooterActionsMeasurements`
|
|
14
|
+
* Function to update the footer actions measurements
|
|
15
|
+
* (to be applied to `onMeasure` prop of `FooterActions`)
|
|
16
|
+
*/
|
|
17
|
+
export const useFooterActionsMeasurements =
|
|
18
|
+
(): UseFooterActionsMeasurementsProps => {
|
|
19
|
+
const [footerActionsMeasurements, setFooterActionsMeasurements] =
|
|
20
|
+
useState<FooterActionsMeasurements>({
|
|
21
|
+
actionBlockHeight: 0,
|
|
22
|
+
safeBottomAreaHeight: 0
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const handleFooterActionsMeasurements = (
|
|
26
|
+
values: FooterActionsMeasurements
|
|
27
|
+
) => {
|
|
28
|
+
setFooterActionsMeasurements(values);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
footerActionsMeasurements,
|
|
33
|
+
handleFooterActionsMeasurements
|
|
34
|
+
};
|
|
35
|
+
};
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export * from "./BlockButtons";
|
|
2
|
+
export * from "./FooterActions";
|
|
3
|
+
export * from "./FooterActionsInline";
|
|
2
4
|
export * from "./FooterWithButtons";
|
|
3
5
|
export * from "./ForceScrollDownView";
|
|
4
6
|
export * from "./GradientBottomActions";
|
|
@@ -7,3 +9,4 @@ export * from "./HeaderFirstLevel";
|
|
|
7
9
|
export * from "./HeaderSecondLevel";
|
|
8
10
|
export * from "./ModalBSHeader";
|
|
9
11
|
export * from "./common";
|
|
12
|
+
export * from "./hooks";
|