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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "unicorn-demo-app",
3
- "version": "8.3.4",
3
+ "version": "8.4.0",
4
4
  "main": "src/index.js",
5
5
  "author": "Ethan Sharabi <ethan.shar@gmail.com>",
6
6
  "license": "MIT",
@@ -0,0 +1,6 @@
1
+ let SafeAreaContextPackage;
2
+ try {
3
+ SafeAreaContextPackage = require('react-native-safe-area-context');
4
+ } catch {}
5
+
6
+ export default SafeAreaContextPackage;
@@ -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 {Colors, Text, FloatingButton, FloatingButtonLayouts, Keyboard} from 'react-native-ui-lib';
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
- withTrackingView: boolean;
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
- withTrackingView: false
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, withTrackingView} = this.state;
36
- const Container = withTrackingView ? Keyboard.KeyboardTrackingView : React.Fragment;
36
+ const {showSecondary, showVertical, hoisted} = this.state;
37
37
  return (
38
- <View style={styles.container}>
39
- <Text text60 center $textDefault marginB-s4>
40
- Trigger Floating Button
41
- </Text>
42
- {renderBooleanOption.call(this, 'Show Floating Button', 'showButton')}
43
- {renderBooleanOption.call(this, 'Full Width Button', 'fullWidth')}
44
- {renderBooleanOption.call(this, 'Show Primary Button', 'showPrimary')}
45
- {renderBooleanOption.call(this, 'Show Secondary Button', 'showSecondary')}
46
- {renderBooleanOption.call(this, 'Button Layout Vertical', 'showVertical')}
47
- {renderBooleanOption.call(this, 'With tracking view', 'withTrackingView')}
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
- <ScrollView showsVerticalScrollIndicator={false}>
50
- <View paddingT-20>
51
- <Text text70 $textDefault style={{fontWeight: 'bold'}}>
52
- Scroll behind a FloatingButton
53
- </Text>
54
- <Text text80 $textDefault marginT-10 style={{lineHeight: 24}}>
55
- Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the
56
- industry standard dummy text ever since the 1500s, when an unknown printer took a galley of type and
57
- scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into
58
- electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release
59
- of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software
60
- like Aldus PageMaker including versions of Lorem Ipsum. Contrary to popular belief, Lorem Ipsum is not
61
- simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000
62
- years old. Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been
63
- the 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 into
65
- electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release
66
- of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software
67
- like Aldus PageMaker including versions of Lorem Ipsum. Contrary to popular belief, Lorem Ipsum is not
68
- simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000
69
- years old.
70
- </Text>
71
- </View>
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
- label: 'Approve',
82
- onPress: this.close
83
- }
80
+ label: 'Approve',
81
+ onPress: this.close
82
+ }
84
83
  : undefined
85
84
  }
86
85
  secondaryButton={
87
86
  showSecondary
88
87
  ? {
89
- label: 'Not now',
90
- onPress: this.notNow
91
- }
88
+ label: 'Not now',
89
+ onPress: this.notNow
90
+ }
92
91
  : undefined
93
92
  }
94
93
  buttonLayout={showVertical ? FloatingButtonLayouts.VERTICAL : FloatingButtonLayouts.HORIZONTAL}
95
- // bottomMargin={80}
96
- // hideBackgroundOverlay
97
- // withoutAnimation
94
+ hoisted={hoisted}
98
95
  />
99
- </Container>
100
- </View>
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);