unicorn-demo-app 8.3.4 → 8.4.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/package.json +1 -1
- package/src/optionalDependencies/SafeAreaContextPackage.tsx +6 -0
- package/src/optionalDependencies/index.ts +1 -0
- package/src/screens/MenuStructure.js +1 -0
- package/src/screens/componentScreens/FloatingButtonScreen.tsx +48 -51
- package/src/screens/componentScreens/ScreenFooterScreen.tsx +512 -0
- package/src/screens/componentScreens/index.js +1 -0
package/package.json
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {default as SafeAreaContextPackage} from './SafeAreaContextPackage';
|
|
@@ -83,6 +83,7 @@ export const navigationData = {
|
|
|
83
83
|
{title: 'Dialog', tags: 'dialog modal popup alert', screen: 'unicorn.components.DialogScreen'},
|
|
84
84
|
{title: 'Feature Highlight', tags: 'native feature overlay', screen: 'unicorn.components.FeatureHighlightScreen'},
|
|
85
85
|
{title: 'Floating Button', tags: 'floating button', screen: 'unicorn.components.FloatingButtonScreen'},
|
|
86
|
+
{title: 'Screen Footer', tags: 'screen footer sticky bottom', screen: 'unicorn.components.ScreenFooterScreen'},
|
|
86
87
|
{title: 'Hint', tags: 'hints tooltip', screen: 'unicorn.components.HintsScreen'},
|
|
87
88
|
{title: 'Toast', tags: 'toast top bottom snackbar', screen: 'unicorn.components.ToastsScreen'}
|
|
88
89
|
]
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React, {Component} from 'react';
|
|
2
2
|
import {View, StyleSheet, Alert, ScrollView} from 'react-native';
|
|
3
|
-
import {
|
|
3
|
+
import {SafeAreaProvider} from 'react-native-safe-area-context';
|
|
4
|
+
import {Colors, Text, TextField, FloatingButton, FloatingButtonLayouts} from 'react-native-ui-lib';
|
|
4
5
|
import {renderBooleanOption} from '../ExampleScreenPresenter';
|
|
5
6
|
|
|
6
7
|
interface State {
|
|
@@ -8,7 +9,7 @@ interface State {
|
|
|
8
9
|
showPrimary: boolean;
|
|
9
10
|
showSecondary: boolean;
|
|
10
11
|
showVertical: boolean;
|
|
11
|
-
|
|
12
|
+
hoisted: boolean;
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
export default class FloatingButtonScreen extends Component<{}, State> {
|
|
@@ -18,7 +19,7 @@ export default class FloatingButtonScreen extends Component<{}, State> {
|
|
|
18
19
|
showSecondary: true,
|
|
19
20
|
showVertical: true,
|
|
20
21
|
fullWidth: false,
|
|
21
|
-
|
|
22
|
+
hoisted: true
|
|
22
23
|
};
|
|
23
24
|
|
|
24
25
|
notNow = () => {
|
|
@@ -32,72 +33,68 @@ export default class FloatingButtonScreen extends Component<{}, State> {
|
|
|
32
33
|
};
|
|
33
34
|
|
|
34
35
|
render() {
|
|
35
|
-
const {showSecondary, showVertical,
|
|
36
|
-
const Container = withTrackingView ? Keyboard.KeyboardTrackingView : React.Fragment;
|
|
36
|
+
const {showSecondary, showVertical, hoisted} = this.state;
|
|
37
37
|
return (
|
|
38
|
-
<
|
|
39
|
-
<
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
38
|
+
<SafeAreaProvider>
|
|
39
|
+
<View style={styles.container}>
|
|
40
|
+
<Text text60 center $textDefault marginB-s4>
|
|
41
|
+
Trigger Floating Button
|
|
42
|
+
</Text>
|
|
43
|
+
{renderBooleanOption.call(this, 'Show Floating Button', 'showButton')}
|
|
44
|
+
{renderBooleanOption.call(this, 'Full Width Button', 'fullWidth')}
|
|
45
|
+
{renderBooleanOption.call(this, 'Show Primary Button', 'showPrimary')}
|
|
46
|
+
{renderBooleanOption.call(this, 'Show Secondary Button', 'showSecondary')}
|
|
47
|
+
{renderBooleanOption.call(this, 'Button Layout Vertical', 'showVertical')}
|
|
48
|
+
{renderBooleanOption.call(this, 'Hoisted (keyboard-aware)', 'hoisted')}
|
|
48
49
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
</
|
|
71
|
-
</
|
|
72
|
-
</ScrollView>
|
|
50
|
+
<TextField migrate placeholder="Tap to test keyboard hoisting" marginB-s4 />
|
|
51
|
+
|
|
52
|
+
<ScrollView
|
|
53
|
+
showsVerticalScrollIndicator={false}
|
|
54
|
+
keyboardDismissMode="on-drag"
|
|
55
|
+
keyboardShouldPersistTaps="handled"
|
|
56
|
+
>
|
|
57
|
+
<View paddingT-20>
|
|
58
|
+
<Text text70 $textDefault style={{fontWeight: 'bold'}}>
|
|
59
|
+
Scroll behind a FloatingButton
|
|
60
|
+
</Text>
|
|
61
|
+
<Text text80 $textDefault marginT-10 style={{lineHeight: 24}}>
|
|
62
|
+
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the
|
|
63
|
+
industry standard dummy text ever since the 1500s, when an unknown printer took a galley of type and
|
|
64
|
+
scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap
|
|
65
|
+
into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the
|
|
66
|
+
release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing
|
|
67
|
+
software like Aldus PageMaker including versions of Lorem Ipsum. Contrary to popular belief, Lorem Ipsum
|
|
68
|
+
is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it
|
|
69
|
+
over 2000 years old.
|
|
70
|
+
</Text>
|
|
71
|
+
</View>
|
|
72
|
+
</ScrollView>
|
|
73
73
|
|
|
74
|
-
<Container>
|
|
75
74
|
<FloatingButton
|
|
76
75
|
visible={this.state.showButton}
|
|
77
76
|
fullWidth={this.state.fullWidth}
|
|
78
77
|
button={
|
|
79
78
|
this.state.showPrimary
|
|
80
79
|
? {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
80
|
+
label: 'Approve',
|
|
81
|
+
onPress: this.close
|
|
82
|
+
}
|
|
84
83
|
: undefined
|
|
85
84
|
}
|
|
86
85
|
secondaryButton={
|
|
87
86
|
showSecondary
|
|
88
87
|
? {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
88
|
+
label: 'Not now',
|
|
89
|
+
onPress: this.notNow
|
|
90
|
+
}
|
|
92
91
|
: undefined
|
|
93
92
|
}
|
|
94
93
|
buttonLayout={showVertical ? FloatingButtonLayouts.VERTICAL : FloatingButtonLayouts.HORIZONTAL}
|
|
95
|
-
|
|
96
|
-
// hideBackgroundOverlay
|
|
97
|
-
// withoutAnimation
|
|
94
|
+
hoisted={hoisted}
|
|
98
95
|
/>
|
|
99
|
-
</
|
|
100
|
-
</
|
|
96
|
+
</View>
|
|
97
|
+
</SafeAreaProvider>
|
|
101
98
|
);
|
|
102
99
|
}
|
|
103
100
|
}
|
|
@@ -0,0 +1,512 @@
|
|
|
1
|
+
import React, {useState} from 'react';
|
|
2
|
+
import {StyleSheet, ScrollView} from 'react-native';
|
|
3
|
+
import {
|
|
4
|
+
View,
|
|
5
|
+
Text,
|
|
6
|
+
Button,
|
|
7
|
+
Colors,
|
|
8
|
+
SegmentedControl,
|
|
9
|
+
ScreenFooter,
|
|
10
|
+
ScreenFooterLayouts,
|
|
11
|
+
ScreenFooterBackgrounds,
|
|
12
|
+
KeyboardBehavior,
|
|
13
|
+
FooterAlignment,
|
|
14
|
+
HorizontalItemsDistribution,
|
|
15
|
+
ItemsFit,
|
|
16
|
+
Switch,
|
|
17
|
+
TextField,
|
|
18
|
+
Hooks,
|
|
19
|
+
Incubator,
|
|
20
|
+
Icon
|
|
21
|
+
} from 'react-native-ui-lib';
|
|
22
|
+
import {SafeAreaContextPackage} from '../../optionalDependencies';
|
|
23
|
+
const SafeAreaProvider = SafeAreaContextPackage?.SafeAreaProvider;
|
|
24
|
+
|
|
25
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
26
|
+
const basketIcon = require('../../assets/icons/collections.png');
|
|
27
|
+
|
|
28
|
+
enum ButtonType {
|
|
29
|
+
PRIMARY = 'Primary',
|
|
30
|
+
SECONDARY = 'Secondary',
|
|
31
|
+
LINK = 'Link'
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
enum ItemSize {
|
|
35
|
+
SMALL = 'small',
|
|
36
|
+
MEDIUM = 'medium',
|
|
37
|
+
LARGE = 'large'
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const ITEMS_COUNT_OPTIONS = [
|
|
41
|
+
{label: '1', value: 1},
|
|
42
|
+
{label: '2', value: 2},
|
|
43
|
+
{label: '3', value: 3}
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
const LAYOUT_OPTIONS = [
|
|
47
|
+
{label: 'Horizontal', value: ScreenFooterLayouts.HORIZONTAL},
|
|
48
|
+
{label: 'Vertical', value: ScreenFooterLayouts.VERTICAL}
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
const BACKGROUND_OPTIONS = [
|
|
52
|
+
{label: 'Solid', value: ScreenFooterBackgrounds.SOLID},
|
|
53
|
+
{label: 'Fading', value: ScreenFooterBackgrounds.FADING},
|
|
54
|
+
{label: 'Transparent', value: ScreenFooterBackgrounds.TRANSPARENT}
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
const ALIGNMENT_OPTIONS = [
|
|
58
|
+
{label: 'Start', value: FooterAlignment.START},
|
|
59
|
+
{label: 'Center', value: FooterAlignment.CENTER},
|
|
60
|
+
{label: 'End', value: FooterAlignment.END}
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
const DISTRIBUTION_OPTIONS = [
|
|
64
|
+
{label: 'Stack', value: HorizontalItemsDistribution.STACK},
|
|
65
|
+
{label: 'Spread', value: HorizontalItemsDistribution.SPREAD}
|
|
66
|
+
];
|
|
67
|
+
|
|
68
|
+
const ITEMS_FIT_OPTIONS = [
|
|
69
|
+
{label: 'Fit', value: ItemsFit.FIT},
|
|
70
|
+
{label: 'Stretch', value: ItemsFit.STRETCH},
|
|
71
|
+
{label: 'Fixed', value: ItemsFit.FIXED}
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
const BUTTON_TYPE_OPTIONS = [
|
|
75
|
+
{label: 'Primary', value: ButtonType.PRIMARY},
|
|
76
|
+
{label: 'Secondary', value: ButtonType.SECONDARY},
|
|
77
|
+
{label: 'Link', value: ButtonType.LINK}
|
|
78
|
+
];
|
|
79
|
+
|
|
80
|
+
const SIZE_OPTIONS = [
|
|
81
|
+
{label: 'Small', value: ItemSize.SMALL},
|
|
82
|
+
{label: 'Medium', value: ItemSize.MEDIUM},
|
|
83
|
+
{label: 'Large', value: ItemSize.LARGE}
|
|
84
|
+
];
|
|
85
|
+
|
|
86
|
+
const KEYBOARD_BEHAVIOR_OPTIONS = [
|
|
87
|
+
{label: 'Sticky', value: KeyboardBehavior.STICKY},
|
|
88
|
+
{label: 'Hoisted', value: KeyboardBehavior.HOISTED}
|
|
89
|
+
];
|
|
90
|
+
|
|
91
|
+
const KEYBOARD_BEHAVIOR_OPTIONS_SPACED = [
|
|
92
|
+
{label: 'Sticky', value: KeyboardBehavior.STICKY},
|
|
93
|
+
{label: 'Hoisted', value: KeyboardBehavior.HOISTED},
|
|
94
|
+
{label: '', value: 'dummy'}
|
|
95
|
+
];
|
|
96
|
+
|
|
97
|
+
const MissingDependencyScreen = () => {
|
|
98
|
+
return (
|
|
99
|
+
<View flex center padding-s5>
|
|
100
|
+
<Text text60 $textDangerLight center>
|
|
101
|
+
Missing Dependency
|
|
102
|
+
</Text>
|
|
103
|
+
<Text text70 center marginT-s3>
|
|
104
|
+
This screen requires react-native-safe-area-context to be installed.
|
|
105
|
+
</Text>
|
|
106
|
+
<Text text80 $textNeutral center marginT-s2>
|
|
107
|
+
Run: npm/yarn install react-native-safe-area-context
|
|
108
|
+
</Text>
|
|
109
|
+
</View>
|
|
110
|
+
);
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const ScreenFooterContent = () => {
|
|
114
|
+
const [itemsCount, setItemsCount] = useState(2);
|
|
115
|
+
const [layout, setLayout] = useState<ScreenFooterLayouts>(ScreenFooterLayouts.HORIZONTAL);
|
|
116
|
+
const [background, setBackground] = useState<ScreenFooterBackgrounds>(ScreenFooterBackgrounds.SOLID);
|
|
117
|
+
const [keyboardBehavior, setKeyboardBehavior] = useState<KeyboardBehavior>(KeyboardBehavior.STICKY);
|
|
118
|
+
const [alignment, setAlignment] = useState<FooterAlignment>(FooterAlignment.CENTER);
|
|
119
|
+
const [horizontalAlignment, setHorizontalAlignment] = useState<FooterAlignment>(FooterAlignment.CENTER);
|
|
120
|
+
const [distribution, setDistribution] = useState<HorizontalItemsDistribution>(HorizontalItemsDistribution.STACK);
|
|
121
|
+
const [itemsFit, setItemsFit] = useState<ItemsFit>(ItemsFit.FIT);
|
|
122
|
+
|
|
123
|
+
const [button1Type, setButton1Type] = useState<ButtonType>(ButtonType.PRIMARY);
|
|
124
|
+
const [button2Type, setButton2Type] = useState<ButtonType>(ButtonType.SECONDARY);
|
|
125
|
+
const [button3Type, setButton3Type] = useState<ButtonType>(ButtonType.LINK);
|
|
126
|
+
const [buttonSize, setButtonSize] = useState<ItemSize>(ItemSize.MEDIUM);
|
|
127
|
+
const [showExtraText, setShowExtraText] = useState(false);
|
|
128
|
+
const [showImage, setShowImage] = useState(false);
|
|
129
|
+
const [extraContentSize, setExtraContentSize] = useState<ItemSize>(ItemSize.MEDIUM);
|
|
130
|
+
const [useLongButtonText, setUseLongButtonText] = useState(false);
|
|
131
|
+
const [itemWidth, setItemWidth] = useState(150);
|
|
132
|
+
const [shouldHideOnScroll, setShouldHideOnScroll] = useState(false);
|
|
133
|
+
|
|
134
|
+
const {onScroll, visible} = Hooks.useScrollToHide();
|
|
135
|
+
|
|
136
|
+
const isHorizontal = layout === ScreenFooterLayouts.HORIZONTAL;
|
|
137
|
+
|
|
138
|
+
const getButtonSize = (size: ItemSize) => {
|
|
139
|
+
switch (size) {
|
|
140
|
+
case ItemSize.SMALL:
|
|
141
|
+
return Button.sizes.small;
|
|
142
|
+
case ItemSize.MEDIUM:
|
|
143
|
+
return Button.sizes.medium;
|
|
144
|
+
case ItemSize.LARGE:
|
|
145
|
+
return Button.sizes.large;
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
const getTextPreset = (size: ItemSize): {main: string; subtext: string} => {
|
|
150
|
+
switch (size) {
|
|
151
|
+
case ItemSize.SMALL:
|
|
152
|
+
return {main: 'text80', subtext: 'text100'};
|
|
153
|
+
case ItemSize.MEDIUM:
|
|
154
|
+
return {main: 'text70', subtext: 'text90'};
|
|
155
|
+
case ItemSize.LARGE:
|
|
156
|
+
return {main: 'text60', subtext: 'text80'};
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
const getButtonProps = (type: ButtonType) => {
|
|
161
|
+
switch (type) {
|
|
162
|
+
case ButtonType.PRIMARY:
|
|
163
|
+
return {
|
|
164
|
+
backgroundColor: Colors.$backgroundPrimaryHeavy,
|
|
165
|
+
label: useLongButtonText ? 'Primary-longtxtlongtxtlongtxtlongtxtlongtxtlongtxt' : 'Checkout'
|
|
166
|
+
};
|
|
167
|
+
case ButtonType.SECONDARY:
|
|
168
|
+
return {
|
|
169
|
+
backgroundColor: Colors.$backgroundDefault,
|
|
170
|
+
outline: true,
|
|
171
|
+
outlineColor: Colors.$backgroundPrimaryHeavy,
|
|
172
|
+
label: 'Secondary'
|
|
173
|
+
};
|
|
174
|
+
case ButtonType.LINK:
|
|
175
|
+
return {
|
|
176
|
+
link: true,
|
|
177
|
+
backgroundColor: undefined,
|
|
178
|
+
label: 'Link',
|
|
179
|
+
color: Colors.$backgroundPrimaryHeavy
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
const getImageSize = (size: ItemSize): number => {
|
|
185
|
+
switch (size) {
|
|
186
|
+
case ItemSize.SMALL:
|
|
187
|
+
return 24;
|
|
188
|
+
case ItemSize.MEDIUM:
|
|
189
|
+
return 32;
|
|
190
|
+
case ItemSize.LARGE:
|
|
191
|
+
return 40;
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
const renderFooterItems = () => {
|
|
196
|
+
const items = [];
|
|
197
|
+
const textPreset = getTextPreset(extraContentSize);
|
|
198
|
+
const imageSize = getImageSize(extraContentSize);
|
|
199
|
+
|
|
200
|
+
// Extra Text (Total price)
|
|
201
|
+
if (showExtraText) {
|
|
202
|
+
items.push(
|
|
203
|
+
<View key="extra-text" centerV flexS marginB-s4={!isHorizontal}>
|
|
204
|
+
<Text {...{[textPreset.main]: true}} numberOfLines={1}>
|
|
205
|
+
Total:{' '}
|
|
206
|
+
<Text {...{[textPreset.main]: true}} style={{fontWeight: 'bold'}}>
|
|
207
|
+
257$
|
|
208
|
+
</Text>
|
|
209
|
+
</Text>
|
|
210
|
+
<Text {...{[textPreset.subtext]: true}} $textNeutralLight numberOfLines={1}>
|
|
211
|
+
Including VAT.
|
|
212
|
+
</Text>
|
|
213
|
+
</View>
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Image (Basket icon)
|
|
218
|
+
if (showExtraText && showImage) {
|
|
219
|
+
items.push(
|
|
220
|
+
<View key="extra-image" centerV marginB-s4={!isHorizontal}>
|
|
221
|
+
<Icon source={basketIcon} size={imageSize} tintColor={Colors.$iconDefault} />
|
|
222
|
+
</View>
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (itemsCount >= 1) {
|
|
227
|
+
items.push(<Button key="btn1" size={getButtonSize(buttonSize)} {...getButtonProps(button1Type)} />);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (itemsCount >= 2) {
|
|
231
|
+
items.push(<Button key="btn2" size={getButtonSize(buttonSize)} {...getButtonProps(button2Type)} />);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (itemsCount >= 3) {
|
|
235
|
+
items.push(<Button key="btn3" size={getButtonSize(buttonSize)} {...getButtonProps(button3Type)} />);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return items;
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
return (
|
|
242
|
+
<SafeAreaProvider>
|
|
243
|
+
<ScrollView contentContainerStyle={styles.scrollContent} onScroll={onScroll} scrollEventThrottle={16}>
|
|
244
|
+
<Text text60 marginB-s4>
|
|
245
|
+
ScreenFooter Configuration
|
|
246
|
+
</Text>
|
|
247
|
+
|
|
248
|
+
<TextField placeholder="Focus me to test keyboard behavior" label="Test Input" floatingPlaceholder />
|
|
249
|
+
|
|
250
|
+
{/* Hide On Scroll Toggle */}
|
|
251
|
+
<View row spread centerV marginB-s4>
|
|
252
|
+
<Text text70M>Hide on Scroll</Text>
|
|
253
|
+
<Switch value={shouldHideOnScroll} onValueChange={setShouldHideOnScroll} />
|
|
254
|
+
</View>
|
|
255
|
+
|
|
256
|
+
{/* Layout Selection */}
|
|
257
|
+
<View marginB-s4>
|
|
258
|
+
<Text text70M marginB-s2>
|
|
259
|
+
Layout
|
|
260
|
+
</Text>
|
|
261
|
+
<SegmentedControl
|
|
262
|
+
segments={LAYOUT_OPTIONS}
|
|
263
|
+
initialIndex={LAYOUT_OPTIONS.findIndex(opt => opt.value === layout)}
|
|
264
|
+
onChangeIndex={index => setLayout(LAYOUT_OPTIONS[index].value)}
|
|
265
|
+
/>
|
|
266
|
+
</View>
|
|
267
|
+
|
|
268
|
+
{/* Items Count */}
|
|
269
|
+
<View marginB-s4>
|
|
270
|
+
<Text text70M marginB-s2>
|
|
271
|
+
Number of Items
|
|
272
|
+
</Text>
|
|
273
|
+
<SegmentedControl
|
|
274
|
+
segments={ITEMS_COUNT_OPTIONS}
|
|
275
|
+
initialIndex={ITEMS_COUNT_OPTIONS.findIndex(opt => opt.value === itemsCount)}
|
|
276
|
+
onChangeIndex={index => setItemsCount(ITEMS_COUNT_OPTIONS[index].value)}
|
|
277
|
+
/>
|
|
278
|
+
</View>
|
|
279
|
+
|
|
280
|
+
{/* Extra Text Toggle */}
|
|
281
|
+
<View row spread centerV marginB-s4>
|
|
282
|
+
<Text text70M>Show Extra Text</Text>
|
|
283
|
+
<Switch value={showExtraText} onValueChange={setShowExtraText} />
|
|
284
|
+
</View>
|
|
285
|
+
|
|
286
|
+
{/* Show Image Toggle (only when Extra Text is shown) */}
|
|
287
|
+
{showExtraText && (
|
|
288
|
+
<View row spread centerV marginB-s4 marginL-s4>
|
|
289
|
+
<Text text70M>Show Image</Text>
|
|
290
|
+
<Switch value={showImage} onValueChange={setShowImage} />
|
|
291
|
+
</View>
|
|
292
|
+
)}
|
|
293
|
+
|
|
294
|
+
{/* Extra Content Size */}
|
|
295
|
+
{showExtraText && (
|
|
296
|
+
<View marginB-s4>
|
|
297
|
+
<Text text70M marginB-s2>
|
|
298
|
+
Extra Content Size
|
|
299
|
+
</Text>
|
|
300
|
+
<SegmentedControl
|
|
301
|
+
segments={SIZE_OPTIONS}
|
|
302
|
+
initialIndex={SIZE_OPTIONS.findIndex(opt => opt.value === extraContentSize)}
|
|
303
|
+
onChangeIndex={index => setExtraContentSize(SIZE_OPTIONS[index].value)}
|
|
304
|
+
/>
|
|
305
|
+
</View>
|
|
306
|
+
)}
|
|
307
|
+
|
|
308
|
+
{/* Button Size (applies to all buttons) */}
|
|
309
|
+
<Text text70M marginB-s2>
|
|
310
|
+
Button Size
|
|
311
|
+
</Text>
|
|
312
|
+
<SegmentedControl
|
|
313
|
+
segments={SIZE_OPTIONS}
|
|
314
|
+
initialIndex={SIZE_OPTIONS.findIndex(opt => opt.value === buttonSize)}
|
|
315
|
+
onChangeIndex={index => setButtonSize(SIZE_OPTIONS[index].value)}
|
|
316
|
+
/>
|
|
317
|
+
|
|
318
|
+
{/* Long Button Text Toggle */}
|
|
319
|
+
<View row spread centerV marginB-s4 marginT-s4>
|
|
320
|
+
<Text text70M>Use Long Button Text</Text>
|
|
321
|
+
<Switch value={useLongButtonText} onValueChange={setUseLongButtonText} />
|
|
322
|
+
</View>
|
|
323
|
+
|
|
324
|
+
{/* Button 1 Type */}
|
|
325
|
+
{itemsCount >= 1 && (
|
|
326
|
+
<View marginB-s4>
|
|
327
|
+
<Text text70M marginB-s2>
|
|
328
|
+
Button 1 Type
|
|
329
|
+
</Text>
|
|
330
|
+
<SegmentedControl
|
|
331
|
+
segments={BUTTON_TYPE_OPTIONS}
|
|
332
|
+
initialIndex={BUTTON_TYPE_OPTIONS.findIndex(opt => opt.value === button1Type)}
|
|
333
|
+
onChangeIndex={index => setButton1Type(BUTTON_TYPE_OPTIONS[index].value)}
|
|
334
|
+
/>
|
|
335
|
+
</View>
|
|
336
|
+
)}
|
|
337
|
+
|
|
338
|
+
{/* Button 2 Type */}
|
|
339
|
+
{itemsCount >= 2 && (
|
|
340
|
+
<View marginB-s4>
|
|
341
|
+
<Text text70M marginB-s2>
|
|
342
|
+
Button 2 Type
|
|
343
|
+
</Text>
|
|
344
|
+
<SegmentedControl
|
|
345
|
+
segments={BUTTON_TYPE_OPTIONS}
|
|
346
|
+
initialIndex={BUTTON_TYPE_OPTIONS.findIndex(opt => opt.value === button2Type)}
|
|
347
|
+
onChangeIndex={index => setButton2Type(BUTTON_TYPE_OPTIONS[index].value)}
|
|
348
|
+
/>
|
|
349
|
+
</View>
|
|
350
|
+
)}
|
|
351
|
+
|
|
352
|
+
{/* Button 3 Type */}
|
|
353
|
+
{itemsCount >= 3 && (
|
|
354
|
+
<View marginB-s4>
|
|
355
|
+
<Text text70M marginB-s2>
|
|
356
|
+
Button 3 Type
|
|
357
|
+
</Text>
|
|
358
|
+
<SegmentedControl
|
|
359
|
+
segments={BUTTON_TYPE_OPTIONS}
|
|
360
|
+
initialIndex={BUTTON_TYPE_OPTIONS.findIndex(opt => opt.value === button3Type)}
|
|
361
|
+
onChangeIndex={index => setButton3Type(BUTTON_TYPE_OPTIONS[index].value)}
|
|
362
|
+
/>
|
|
363
|
+
</View>
|
|
364
|
+
)}
|
|
365
|
+
|
|
366
|
+
{/* Background */}
|
|
367
|
+
<View marginB-s4>
|
|
368
|
+
<Text text70M marginB-s2>
|
|
369
|
+
Background
|
|
370
|
+
</Text>
|
|
371
|
+
<SegmentedControl
|
|
372
|
+
segments={BACKGROUND_OPTIONS}
|
|
373
|
+
initialIndex={BACKGROUND_OPTIONS.findIndex(opt => opt.value === background)}
|
|
374
|
+
onChangeIndex={index => setBackground(BACKGROUND_OPTIONS[index].value)}
|
|
375
|
+
/>
|
|
376
|
+
</View>
|
|
377
|
+
|
|
378
|
+
{/* Position */}
|
|
379
|
+
<View marginB-s4>
|
|
380
|
+
<Text text70M marginB-s2>
|
|
381
|
+
Keyboard Behavior
|
|
382
|
+
</Text>
|
|
383
|
+
<View row>
|
|
384
|
+
<SegmentedControl
|
|
385
|
+
segments={KEYBOARD_BEHAVIOR_OPTIONS}
|
|
386
|
+
initialIndex={KEYBOARD_BEHAVIOR_OPTIONS.findIndex(opt => opt.value === keyboardBehavior)}
|
|
387
|
+
onChangeIndex={index => setKeyboardBehavior(KEYBOARD_BEHAVIOR_OPTIONS[index].value)}
|
|
388
|
+
/>
|
|
389
|
+
<View flex />
|
|
390
|
+
</View>
|
|
391
|
+
</View>
|
|
392
|
+
|
|
393
|
+
{/* Alignment (Cross Axis) */}
|
|
394
|
+
<View marginB-s4>
|
|
395
|
+
<Text text70M marginB-s2>
|
|
396
|
+
{isHorizontal ? 'Vertical Alignment' : 'Alignment'}
|
|
397
|
+
</Text>
|
|
398
|
+
<SegmentedControl
|
|
399
|
+
segments={ALIGNMENT_OPTIONS}
|
|
400
|
+
initialIndex={ALIGNMENT_OPTIONS.findIndex(opt => opt.value === alignment)}
|
|
401
|
+
onChangeIndex={index => setAlignment(ALIGNMENT_OPTIONS[index].value)}
|
|
402
|
+
/>
|
|
403
|
+
</View>
|
|
404
|
+
|
|
405
|
+
{/* Distribution (for Horizontal layout) */}
|
|
406
|
+
{isHorizontal && (
|
|
407
|
+
<View marginB-s4>
|
|
408
|
+
<Text text70M marginB-s2>
|
|
409
|
+
Distribution
|
|
410
|
+
</Text>
|
|
411
|
+
<SegmentedControl
|
|
412
|
+
containerStyle={styles.rowContainer}
|
|
413
|
+
segments={DISTRIBUTION_OPTIONS}
|
|
414
|
+
initialIndex={DISTRIBUTION_OPTIONS.findIndex(opt => opt.value === distribution)}
|
|
415
|
+
onChangeIndex={index => setDistribution(DISTRIBUTION_OPTIONS[index].value)}
|
|
416
|
+
/>
|
|
417
|
+
</View>
|
|
418
|
+
)}
|
|
419
|
+
|
|
420
|
+
{/* Horizontal Alignment (for Horizontal layout + Stack distribution) */}
|
|
421
|
+
{isHorizontal && distribution === HorizontalItemsDistribution.STACK && (
|
|
422
|
+
<View marginB-s4>
|
|
423
|
+
<Text text70M marginB-s2>
|
|
424
|
+
Horizontal Alignment
|
|
425
|
+
</Text>
|
|
426
|
+
<SegmentedControl
|
|
427
|
+
segments={ALIGNMENT_OPTIONS}
|
|
428
|
+
initialIndex={ALIGNMENT_OPTIONS.findIndex(opt => opt.value === horizontalAlignment)}
|
|
429
|
+
onChangeIndex={index => setHorizontalAlignment(ALIGNMENT_OPTIONS[index].value)}
|
|
430
|
+
/>
|
|
431
|
+
</View>
|
|
432
|
+
)}
|
|
433
|
+
|
|
434
|
+
{/* Items Fit */}
|
|
435
|
+
<View marginB-s4>
|
|
436
|
+
<Text text70M marginB-s2>
|
|
437
|
+
Items Fit
|
|
438
|
+
</Text>
|
|
439
|
+
<SegmentedControl
|
|
440
|
+
segments={ITEMS_FIT_OPTIONS}
|
|
441
|
+
initialIndex={ITEMS_FIT_OPTIONS.findIndex(opt => opt.value === itemsFit)}
|
|
442
|
+
onChangeIndex={index => setItemsFit(ITEMS_FIT_OPTIONS[index].value)}
|
|
443
|
+
/>
|
|
444
|
+
</View>
|
|
445
|
+
|
|
446
|
+
{/* Item Width Slider (only when Fixed is selected) */}
|
|
447
|
+
{itemsFit === ItemsFit.FIXED && (
|
|
448
|
+
<View marginB-s4>
|
|
449
|
+
<Text text70M marginB-s2>
|
|
450
|
+
Item Width: {itemWidth}px
|
|
451
|
+
</Text>
|
|
452
|
+
<Incubator.Slider
|
|
453
|
+
value={itemWidth}
|
|
454
|
+
minimumValue={50}
|
|
455
|
+
maximumValue={250}
|
|
456
|
+
step={10}
|
|
457
|
+
onValueChange={setItemWidth}
|
|
458
|
+
/>
|
|
459
|
+
</View>
|
|
460
|
+
)}
|
|
461
|
+
|
|
462
|
+
{/* Spacer for content */}
|
|
463
|
+
<View marginT-s10>
|
|
464
|
+
<Text text70>
|
|
465
|
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et
|
|
466
|
+
dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex
|
|
467
|
+
ea commodo consequat.
|
|
468
|
+
</Text>
|
|
469
|
+
<Text text70 marginT-s4>
|
|
470
|
+
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
|
|
471
|
+
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
|
|
472
|
+
laborum.
|
|
473
|
+
</Text>
|
|
474
|
+
</View>
|
|
475
|
+
</ScrollView>
|
|
476
|
+
|
|
477
|
+
<ScreenFooter
|
|
478
|
+
layout={layout}
|
|
479
|
+
backgroundType={background}
|
|
480
|
+
keyboardBehavior={keyboardBehavior}
|
|
481
|
+
alignment={alignment}
|
|
482
|
+
horizontalAlignment={horizontalAlignment}
|
|
483
|
+
horizontalItemsDistribution={distribution}
|
|
484
|
+
itemsFit={itemsFit}
|
|
485
|
+
itemWidth={itemWidth}
|
|
486
|
+
visible={shouldHideOnScroll ? visible : true}
|
|
487
|
+
>
|
|
488
|
+
{renderFooterItems()}
|
|
489
|
+
</ScreenFooter>
|
|
490
|
+
</SafeAreaProvider>
|
|
491
|
+
);
|
|
492
|
+
};
|
|
493
|
+
|
|
494
|
+
const ScreenFooterScreen = () => {
|
|
495
|
+
if (!SafeAreaProvider) {
|
|
496
|
+
return <MissingDependencyScreen />;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
return <ScreenFooterContent />;
|
|
500
|
+
};
|
|
501
|
+
|
|
502
|
+
const styles = StyleSheet.create({
|
|
503
|
+
scrollContent: {
|
|
504
|
+
padding: 20,
|
|
505
|
+
paddingBottom: 150
|
|
506
|
+
},
|
|
507
|
+
rowContainer: {
|
|
508
|
+
flexDirection: 'row'
|
|
509
|
+
}
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
export default ScreenFooterScreen;
|
|
@@ -21,6 +21,7 @@ export function registerScreens(registrar) {
|
|
|
21
21
|
registrar('unicorn.components.DrawerScreen', () => require('./DrawerScreen').default);
|
|
22
22
|
registrar('unicorn.components.ExpandableSectionScreen', () => require('./ExpandableSectionScreen').default);
|
|
23
23
|
registrar('unicorn.components.FloatingButtonScreen', () => require('./FloatingButtonScreen').default);
|
|
24
|
+
registrar('unicorn.components.ScreenFooterScreen', () => require('./ScreenFooterScreen').default);
|
|
24
25
|
registrar('unicorn.components.HapticScreen', () => require('./HapticScreen').default);
|
|
25
26
|
registrar('unicorn.components.HintsScreen', () => require('./HintsScreen').default);
|
|
26
27
|
registrar('unicorn.components.IconScreen', () => require('./IconScreen').default);
|