agora-appbuilder-core 4.0.0-api.1 → 4.0.0-api.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agora-appbuilder-core",
3
- "version": "4.0.0-api.1",
3
+ "version": "4.0.0-api.3",
4
4
  "description": "React Native template for RTE app builder",
5
5
  "main": "index.js",
6
6
  "files": [
@@ -18,8 +18,6 @@ export {Icons} from '../agora-rn-uikit';
18
18
  //video call components
19
19
  export {
20
20
  ParticipantsView,
21
- Controls,
22
- Navbar,
23
21
  ChatBubble,
24
22
  ChatInput,
25
23
  Chat,
@@ -52,3 +50,11 @@ export {
52
50
  ClientRole as UikitClientRole,
53
51
  ChannelProfile as UikitChannelProfile,
54
52
  } from '../agora-rn-uikit';
53
+ export {default as Toolbar} from '../src/atoms/Toolbar';
54
+ export {default as ToolbarItem} from '../src/atoms/ToolbarItem';
55
+ export {default as ToolbarPreset} from '../src/atoms/ToolbarPreset';
56
+ export {default as ToolbarMenu} from '../src/atoms/ToolbarMenu';
57
+ export type {
58
+ ToolbarCustomItem,
59
+ ToolbarPresetProps,
60
+ } from '../src/atoms/ToolbarPreset';
@@ -22,6 +22,7 @@ import {IconsInterface} from '../src/atoms/CustomIcon';
22
22
  export type {ContentInterface, ContentStateInterface, UidType};
23
23
  export type {ChatTextInputProps} from '../src/subComponents/ChatInput';
24
24
  import {ChatTextInputProps} from '../src/subComponents/ChatInput';
25
+ import {ToolbarCustomItem} from './sub-components';
25
26
 
26
27
  export const CUSTOM_ROUTES_PREFIX = '/r/';
27
28
 
@@ -59,12 +60,13 @@ export interface layoutItem {
59
60
  component: layoutComponent;
60
61
  }
61
62
 
63
+ export type ToolbarType = React.ComponentType | Array<ToolbarCustomItem>;
62
64
  export interface VideoCallInterface extends BeforeAndAfterInterface {
63
65
  // commented for v1 release
64
- topToolBar?: React.ComponentType;
65
- bottomToolBar?: React.ComponentType;
66
- leftToolBar?: React.ComponentType;
67
- rightToolBar?: React.ComponentType;
66
+ topToolBar?: ToolbarType;
67
+ bottomToolBar?: ToolbarType;
68
+ leftToolBar?: ToolbarType;
69
+ rightToolBar?: ToolbarType;
68
70
  //settingsPanel?: React.ComponentType;
69
71
  participantsPanel?: React.ComponentType;
70
72
  chat?: ChatCmpInterface;
@@ -21,6 +21,9 @@ export {default as useIsPSTN} from '../src/utils/useIsPSTN';
21
21
  export {default as useIsAudioEnabled} from '../src/utils/useIsAudioEnabled';
22
22
  export {default as useIsVideoEnabled} from '../src/utils/useIsVideoEnabled';
23
23
 
24
+ //hook to get active speaker uid
25
+ export {default as useActiveSpeaker} from '../src/utils/useActiveSpeaker';
26
+
24
27
  //hooks used for navigation
25
28
  export {useHistory, useParams} from '../src/components/Router';
26
29
 
@@ -0,0 +1,40 @@
1
+ import React from 'react';
2
+ import {View, StyleSheet, ViewStyle} from 'react-native';
3
+ import {ToolbarMenuProvider} from '../utils/useMenu';
4
+
5
+ export interface ToolbarMenuProps {
6
+ children: React.ReactNode;
7
+ containerStyle?: ViewStyle;
8
+ }
9
+ const ToolbarMenu = (props: ToolbarMenuProps) => {
10
+ const {children, containerStyle} = props;
11
+ if (!children) {
12
+ return null;
13
+ }
14
+ return (
15
+ <ToolbarMenuProvider>
16
+ <View style={[ToolbarMenuStyle.containerStyle, containerStyle]}>
17
+ {children}
18
+ </View>
19
+ </ToolbarMenuProvider>
20
+ );
21
+ };
22
+ export default ToolbarMenu;
23
+
24
+ const ToolbarMenuStyle = StyleSheet.create({
25
+ containerStyle: {
26
+ flex: 1,
27
+ justifyContent: 'flex-start',
28
+ backgroundColor: $config.CARD_LAYER_4_COLOR,
29
+ borderRadius: 4,
30
+ shadowColor: '#000',
31
+ shadowOffset: {
32
+ width: 0,
33
+ height: 4,
34
+ },
35
+ shadowOpacity: 0.1,
36
+ shadowRadius: 8,
37
+ zIndex: 1,
38
+ elevation: 1,
39
+ },
40
+ });
@@ -0,0 +1,104 @@
1
+ import React, {useState} from 'react';
2
+ import {
3
+ View,
4
+ StyleSheet,
5
+ TouchableOpacityProps,
6
+ TouchableOpacity,
7
+ Text,
8
+ } from 'react-native';
9
+ import ThemeConfig from '../theme';
10
+ import ImageIcon from './ImageIcon';
11
+ import {IconButtonProps} from './IconButton';
12
+ import {isWebInternal} from '../utils/common';
13
+ import hexadecimalTransparency from '../utils/hexadecimalTransparency';
14
+
15
+ export interface ToolbarMenuItemProps extends IconButtonProps {
16
+ // disabled?: boolean;
17
+ // onPress: TouchableOpacityProps['onPress'];
18
+ // isHovered?: boolean;
19
+ // menuIcon?: string;
20
+ // menuText?: string;
21
+ // menuIconColor?: string;
22
+ }
23
+ const ToolbarMenuItem = (props: ToolbarMenuItemProps) => {
24
+ return (
25
+ <PlatformWrapper>
26
+ {(isHovered: boolean) => (
27
+ <>
28
+ <TouchableOpacity
29
+ disabled={props.disabled}
30
+ style={[
31
+ props.disabled ? {opacity: 0.4} : {},
32
+ isHovered
33
+ ? {
34
+ backgroundColor:
35
+ $config.CARD_LAYER_5_COLOR +
36
+ hexadecimalTransparency['15%'],
37
+ }
38
+ : {},
39
+ ToolbarMenuItemStyles.container,
40
+ ]}
41
+ onPress={props.onPress}>
42
+ <View style={ToolbarMenuItemStyles.iconContainer}>
43
+ <ImageIcon
44
+ base64={false}
45
+ iconType="plain"
46
+ iconSize={20}
47
+ name={props?.iconProps?.name}
48
+ tintColor={props?.iconProps?.tintColor}
49
+ />
50
+ </View>
51
+ <Text
52
+ style={[
53
+ ToolbarMenuItemStyles.textStyle,
54
+ props?.btnTextProps?.textStyle,
55
+ props?.btnTextProps?.textColor
56
+ ? {color: props?.btnTextProps?.textColor}
57
+ : {},
58
+ ]}>
59
+ {props?.btnTextProps?.text}
60
+ </Text>
61
+ </TouchableOpacity>
62
+ </>
63
+ )}
64
+ </PlatformWrapper>
65
+ );
66
+ };
67
+ export default ToolbarMenuItem;
68
+ const PlatformWrapper = ({children}) => {
69
+ const [isHovered, setIsHovered] = useState(false);
70
+ return isWebInternal() ? (
71
+ <div
72
+ onMouseEnter={() => {
73
+ setIsHovered(true);
74
+ }}
75
+ onMouseLeave={() => {
76
+ setIsHovered(false);
77
+ }}>
78
+ {children(isHovered)}
79
+ </div>
80
+ ) : (
81
+ <>{children(false)}</>
82
+ );
83
+ };
84
+ const ToolbarMenuItemStyles = StyleSheet.create({
85
+ container: {
86
+ flexDirection: 'row',
87
+ borderBottomWidth: 1,
88
+ borderBottomColor: $config.CARD_LAYER_3_COLOR,
89
+ },
90
+ iconContainer: {
91
+ justifyContent: 'center',
92
+ alignItems: 'center',
93
+ marginRight: 8,
94
+ marginVertical: 12,
95
+ marginLeft: 12,
96
+ },
97
+ textStyle: {
98
+ paddingVertical: 14,
99
+ color: $config.SECONDARY_ACTION_COLOR,
100
+ fontSize: ThemeConfig.FontSize.normal,
101
+ fontWeight: '400',
102
+ fontFamily: ThemeConfig.FontFamily.sansPro,
103
+ },
104
+ });
@@ -0,0 +1,39 @@
1
+ import React from 'react';
2
+ import Navbar from '../components/Navbar';
3
+ import Controls from '../components/Controls';
4
+ import Leftbar from '../components/Leftbar';
5
+ import Rightbar from '../components/Rightbar';
6
+
7
+ export interface ToolbarCustomItem {
8
+ component: () => JSX.Element;
9
+ align: 'start' | 'center' | 'end';
10
+ hide: 'yes' | 'no' | 'never';
11
+ order?: number;
12
+ }
13
+
14
+ export interface ToolbarPresetProps {
15
+ align: 'top' | 'left' | 'bottom' | 'right';
16
+ customItems?: Array<ToolbarCustomItem>;
17
+ }
18
+
19
+ const ToolbarPreset = (props: ToolbarPresetProps) => {
20
+ const {align} = props;
21
+ if (!align) {
22
+ console.log(
23
+ 'ToolbarPreset align prop is empty. please provide align prop to get the corresponding toolbar preset',
24
+ );
25
+ return null;
26
+ }
27
+ if (align === 'left') {
28
+ return <Leftbar customItems={props?.customItems} />;
29
+ } else if (align === 'right') {
30
+ return <Rightbar customItems={props?.customItems} />;
31
+ } else if (align === 'top') {
32
+ return <Navbar customItems={props?.customItems} />;
33
+ } else if (align === 'bottom') {
34
+ return <Controls customItems={props?.customItems} />;
35
+ } else {
36
+ return null;
37
+ }
38
+ };
39
+ export default ToolbarPreset;
@@ -10,38 +10,21 @@
10
10
  *********************************************
11
11
  */
12
12
  import React, {useState, useContext, useEffect, useRef} from 'react';
13
- import {View, StyleSheet, Text, useWindowDimensions} from 'react-native';
13
+ import {View, StyleSheet, useWindowDimensions} from 'react-native';
14
14
  import {PropsContext} from '../../agora-rn-uikit';
15
- import LocalAudioMute, {
16
- LocalAudioMuteProps,
17
- } from '../subComponents/LocalAudioMute';
18
- import LocalVideoMute, {
19
- LocalVideoMuteProps,
20
- } from '../subComponents/LocalVideoMute';
21
- import Recording, {RecordingButtonProps} from '../subComponents/Recording';
22
- import LocalSwitchCamera, {
23
- LocalSwitchCameraProps,
24
- } from '../subComponents/LocalSwitchCamera';
25
- import ScreenshareButton, {
26
- ScreenshareButtonProps,
27
- } from '../subComponents/screenshare/ScreenshareButton';
15
+ import LocalAudioMute from '../subComponents/LocalAudioMute';
16
+ import LocalVideoMute from '../subComponents/LocalVideoMute';
17
+ import Recording from '../subComponents/Recording';
18
+ import LocalSwitchCamera from '../subComponents/LocalSwitchCamera';
19
+ import ScreenshareButton from '../subComponents/screenshare/ScreenshareButton';
28
20
  import isMobileOrTablet from '../utils/isMobileOrTablet';
29
21
  import {ClientRole} from '../../agora-rn-uikit';
30
- import LiveStreamControls, {
31
- LiveStreamControlsProps,
32
- } from './livestream/views/LiveStreamControls';
33
- import {
34
- BREAKPOINTS,
35
- calculatePosition,
36
- isWebInternal,
37
- useIsDesktop,
38
- } from '../utils/common';
22
+ import LiveStreamControls from './livestream/views/LiveStreamControls';
23
+ import {BREAKPOINTS, useIsDesktop} from '../utils/common';
39
24
  import {useRoomInfo} from './room-info/useRoomInfo';
40
- import LocalEndcall, {LocalEndcallProps} from '../subComponents/LocalEndCall';
41
- import Spacer from '../atoms/Spacer';
25
+ import LocalEndcall from '../subComponents/LocalEndCall';
42
26
  import LayoutIconButton from '../subComponents/LayoutIconButton';
43
27
  import CopyJoinInfo from '../subComponents/CopyJoinInfo';
44
- import hexadecimalTransparency from '../utils/hexadecimalTransparency';
45
28
  import IconButton from '../atoms/IconButton';
46
29
  import ActionMenu, {ActionMenuItem} from '../atoms/ActionMenu';
47
30
  import useLayoutsData from '../pages/video-call/useLayoutsData';
@@ -57,8 +40,8 @@ import {useVideoCall} from './useVideoCall';
57
40
  import {useScreenshare} from '../subComponents/screenshare/useScreenshare';
58
41
  import LayoutIconDropdown from '../subComponents/LayoutIconDropdown';
59
42
  import Toolbar from '../atoms/Toolbar';
60
- import {ToolbarPosition, useToolbar} from '../utils/useToolbar';
61
43
  import ToolbarItem from '../atoms/ToolbarItem';
44
+ import {ToolbarCustomItem} from '../atoms/ToolbarPreset';
62
45
 
63
46
  const MoreButton = () => {
64
47
  const {rtcProps} = useContext(PropsContext);
@@ -278,117 +261,272 @@ const MoreButton = () => {
278
261
  </>
279
262
  );
280
263
  };
281
- const Controls = () => {
264
+ export const LayoutToolbarItem = () => (
265
+ <ToolbarItem testID="layout-btn" collapsable={false}>
266
+ {/**
267
+ * .measure returns undefined on Android unless collapsable=false or onLayout are specified
268
+ * so added collapsable property
269
+ * https://github.com/facebook/react-native/issues/29712
270
+ * */}
271
+ <LayoutIconButton />
272
+ </ToolbarItem>
273
+ );
274
+ export const InviteToolbarItem = () => {
275
+ return (
276
+ <ToolbarItem testID="invite-btn">
277
+ <CopyJoinInfo />
278
+ </ToolbarItem>
279
+ );
280
+ };
281
+ const defaultStartItems: Array<ToolbarCustomItem> = [
282
+ {
283
+ align: 'start',
284
+ component: LayoutToolbarItem,
285
+ order: 0,
286
+ hide: 'no',
287
+ },
288
+ {
289
+ align: 'start',
290
+ component: InviteToolbarItem,
291
+ order: 1,
292
+ hide: 'no',
293
+ },
294
+ ];
295
+
296
+ export const RaiseHandToolbarItem = () => {
282
297
  const {rtcProps} = useContext(PropsContext);
283
298
  const isDesktop = useIsDesktop();
284
299
  const {
285
300
  data: {isHost},
286
301
  } = useRoomInfo();
287
- const {isHorizontal} = useToolbar();
302
+ return $config.EVENT_MODE ? (
303
+ rtcProps.role == ClientRole.Audience ? (
304
+ <LiveStreamControls
305
+ showControls={true}
306
+ isDesktop={isDesktop('toolbar')}
307
+ />
308
+ ) : rtcProps?.role == ClientRole.Broadcaster ? (
309
+ /**
310
+ * In event mode when raise hand feature is active
311
+ * and audience is promoted to host, the audience can also
312
+ * demote himself
313
+ */
314
+ <LiveStreamControls
315
+ isDesktop={isDesktop('toolbar')}
316
+ showControls={!isHost}
317
+ />
318
+ ) : (
319
+ <></>
320
+ )
321
+ ) : (
322
+ <></>
323
+ );
324
+ };
325
+
326
+ export const LocalAudioToolbarItem = () => {
327
+ return (
328
+ <ToolbarItem testID="localAudio-btn">
329
+ <LocalAudioMute showToolTip={true} />
330
+ </ToolbarItem>
331
+ );
332
+ };
333
+
334
+ export const LocalVideoToolbarItem = () => {
335
+ return (
336
+ !$config.AUDIO_ROOM && (
337
+ <ToolbarItem testID="localVideo-btn">
338
+ <LocalVideoMute showToolTip={true} />
339
+ </ToolbarItem>
340
+ )
341
+ );
342
+ };
343
+
344
+ export const SwitchCameraToolbarItem = () => {
345
+ return (
346
+ !$config.AUDIO_ROOM &&
347
+ isMobileOrTablet() && (
348
+ <ToolbarItem testID="switchCamera-btn">
349
+ <LocalSwitchCamera />
350
+ </ToolbarItem>
351
+ )
352
+ );
353
+ };
354
+
355
+ export const ScreenShareToolbarItem = () => {
356
+ const {width} = useWindowDimensions();
357
+ return (
358
+ width > BREAKPOINTS.sm &&
359
+ $config.SCREEN_SHARING &&
360
+ !isMobileOrTablet() && (
361
+ <ToolbarItem testID="screenShare-btn">
362
+ <ScreenshareButton />
363
+ </ToolbarItem>
364
+ )
365
+ );
366
+ };
367
+ export const RecordingToolbarItem = () => {
368
+ const {width} = useWindowDimensions();
369
+ const {
370
+ data: {isHost},
371
+ } = useRoomInfo();
372
+ return (
373
+ width > BREAKPOINTS.sm &&
374
+ isHost &&
375
+ $config.CLOUD_RECORDING && (
376
+ <ToolbarItem testID="recording-btn">
377
+ <Recording />
378
+ </ToolbarItem>
379
+ )
380
+ );
381
+ };
382
+
383
+ export const MoreButtonToolbarItem = () => {
288
384
  const {width} = useWindowDimensions();
385
+ return (
386
+ width < BREAKPOINTS.md && (
387
+ <ToolbarItem testID="more-btn">
388
+ <MoreButton />
389
+ </ToolbarItem>
390
+ )
391
+ );
392
+ };
393
+ export const LocalEndcallToolbarItem = () => {
394
+ return (
395
+ <ToolbarItem testID="endCall-btn">
396
+ <LocalEndcall />
397
+ </ToolbarItem>
398
+ );
399
+ };
400
+
401
+ const defaultCenterItems: ToolbarCustomItem[] = [
402
+ {
403
+ align: 'start',
404
+ component: RaiseHandToolbarItem,
405
+ order: 0,
406
+ hide: 'no',
407
+ },
408
+ {
409
+ align: 'start',
410
+ component: LocalAudioToolbarItem,
411
+ order: 1,
412
+ hide: 'no',
413
+ },
414
+ {
415
+ align: 'start',
416
+ component: LocalVideoToolbarItem,
417
+ order: 2,
418
+ hide: 'no',
419
+ },
420
+ {
421
+ align: 'start',
422
+ component: SwitchCameraToolbarItem,
423
+ order: 3,
424
+ hide: 'no',
425
+ },
426
+ {
427
+ align: 'start',
428
+ component: ScreenShareToolbarItem,
429
+ order: 4,
430
+ hide: 'no',
431
+ },
432
+ {
433
+ align: 'start',
434
+ component: RecordingToolbarItem,
435
+ order: 5,
436
+ hide: 'no',
437
+ },
438
+ {
439
+ align: 'start',
440
+ component: MoreButtonToolbarItem,
441
+ order: 6,
442
+ hide: 'no',
443
+ },
444
+ {
445
+ align: 'start',
446
+ component: LocalEndcallToolbarItem,
447
+ order: 7,
448
+ hide: 'no',
449
+ },
450
+ ];
451
+
452
+ const defaultEndItems: ToolbarCustomItem[] = [];
453
+
454
+ export interface ControlsProps {
455
+ customItems?: ToolbarCustomItem[];
456
+ }
457
+ const Controls = (props: ControlsProps) => {
458
+ const {customItems = []} = props;
459
+ const {width} = useWindowDimensions();
460
+
461
+ const isHidden = (i) => {
462
+ return i?.hide === 'yes';
463
+ };
464
+ const customStartItems = customItems
465
+ ?.filter((i) => i?.align === 'start' && !isHidden(i))
466
+ ?.concat(defaultStartItems)
467
+ ?.sort((a, b) => a?.order - b?.order);
468
+
469
+ const customCenterItems = customItems
470
+ ?.filter((i) => i?.align === 'center' && !isHidden(i))
471
+ ?.concat(defaultCenterItems)
472
+ ?.sort((a, b) => a?.order - b?.order);
473
+
474
+ const customEndItems = customItems
475
+ ?.filter((i) => i?.align === 'end' && !isHidden(i))
476
+ ?.concat(defaultEndItems)
477
+ ?.sort((a, b) => a?.order - b?.order);
478
+
479
+ const renderContent = (
480
+ items: ToolbarCustomItem[],
481
+ type: 'start' | 'center' | 'end',
482
+ ) => {
483
+ return items?.map((item, index) => {
484
+ const ToolbarItem = item?.component;
485
+ if (ToolbarItem) {
486
+ return <ToolbarItem key={`bottom-toolbar-${type}` + index} />;
487
+ } else {
488
+ return null;
489
+ }
490
+ });
491
+ };
289
492
  return (
290
493
  <Toolbar>
291
494
  {width >= BREAKPOINTS.md && (
292
- <View
293
- style={[
294
- style.leftContent,
295
- isHorizontal ? {flexDirection: 'row'} : {flexDirection: 'column'},
296
- ]}>
297
- <ToolbarItem testID="layout-btn" collapsable={false}>
298
- {/**
299
- * .measure returns undefined on Android unless collapsable=false or onLayout are specified
300
- * so added collapsable property
301
- * https://github.com/facebook/react-native/issues/29712
302
- * */}
303
- <LayoutIconButton />
304
- </ToolbarItem>
305
- <ToolbarItem testID="invite-btn">
306
- <CopyJoinInfo />
307
- </ToolbarItem>
495
+ <View style={[style.startContent]}>
496
+ {renderContent(customStartItems, 'start')}
308
497
  </View>
309
498
  )}
310
- <View
311
- style={[
312
- style.centerContent,
313
- isHorizontal ? {flexDirection: 'row'} : {flexDirection: 'column'},
314
- ]}>
315
- {$config.EVENT_MODE && rtcProps.role == ClientRole.Audience ? (
316
- <LiveStreamControls
317
- showControls={true}
318
- isDesktop={isDesktop('toolbar')}
319
- />
320
- ) : (
321
- <></>
322
- )}
323
- <>
324
- {/**
325
- * In event mode when raise hand feature is active
326
- * and audience is promoted to host, the audience can also
327
- * demote himself
328
- */}
329
- {$config.EVENT_MODE ? (
330
- <LiveStreamControls
331
- isDesktop={isDesktop('toolbar')}
332
- showControls={rtcProps?.role == ClientRole.Broadcaster && !isHost}
333
- />
334
- ) : (
335
- <></>
336
- )}
337
- <ToolbarItem testID="localAudio-btn">
338
- <LocalAudioMute showToolTip={true} />
339
- </ToolbarItem>
340
- {!$config.AUDIO_ROOM && (
341
- <ToolbarItem testID="localVideo-btn">
342
- <LocalVideoMute showToolTip={true} />
343
- </ToolbarItem>
344
- )}
345
- {!$config.AUDIO_ROOM && isMobileOrTablet() && (
346
- <ToolbarItem testID="switchCamera-btn">
347
- <LocalSwitchCamera />
348
- </ToolbarItem>
349
- )}
350
- {width > BREAKPOINTS.sm &&
351
- $config.SCREEN_SHARING &&
352
- !isMobileOrTablet() && (
353
- <ToolbarItem testID="screenShare-btn">
354
- <ScreenshareButton />
355
- </ToolbarItem>
356
- )}
357
- {width > BREAKPOINTS.sm && isHost && $config.CLOUD_RECORDING && (
358
- <ToolbarItem testID="recording-btn">
359
- <Recording />
360
- </ToolbarItem>
361
- )}
362
- </>
363
- {width < BREAKPOINTS.md && (
364
- <ToolbarItem testID="more-btn">
365
- <MoreButton />
366
- </ToolbarItem>
367
- )}
368
- <ToolbarItem testID="endCall-btn">
369
- <LocalEndcall />
370
- </ToolbarItem>
499
+ <View style={[style.centerContent]}>
500
+ {renderContent(customCenterItems, 'center')}
371
501
  </View>
372
- {width >= BREAKPOINTS.md && <View style={style.rightContent}></View>}
502
+ {width >= BREAKPOINTS.md && (
503
+ <View style={style.endContent}>
504
+ {renderContent(customEndItems, 'end')}
505
+ </View>
506
+ )}
373
507
  </Toolbar>
374
508
  );
375
509
  };
376
510
 
377
511
  const style = StyleSheet.create({
378
- leftContent: {
512
+ startContent: {
379
513
  flex: 1,
380
-
514
+ flexDirection: 'row',
381
515
  justifyContent: 'flex-start',
382
516
  alignItems: 'center',
383
517
  },
384
518
  centerContent: {
385
519
  zIndex: 2,
386
520
  flex: 1,
521
+ flexDirection: 'row',
387
522
  justifyContent: 'center',
388
523
  alignItems: 'center',
389
524
  },
390
- rightContent: {
525
+ endContent: {
391
526
  flex: 1,
527
+ flexDirection: 'row',
528
+ justifyContent: 'flex-end',
529
+ alignItems: 'center',
392
530
  },
393
531
  });
394
532