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.
@@ -7,10 +7,10 @@ import {
7
7
  Platform,
8
8
  } from 'react-native';
9
9
  import {useCustomization} from 'customization-implementation';
10
- import Navbar from '../../components/Navbar';
10
+ import Navbar, {NavbarProps} from '../../components/Navbar';
11
11
  import ParticipantsView from '../../components/ParticipantsView';
12
12
  import SettingsView from '../../components/SettingsView';
13
- import Controls from '../../components/Controls';
13
+ import Controls, {ControlsProps} from '../../components/Controls';
14
14
  import Chat, {ChatProps} from '../../components/Chat';
15
15
  import {SidePanelType} from '../../subComponents/SidePanelEnum';
16
16
  import {
@@ -25,9 +25,15 @@ import {videoView} from '../../../theme.json';
25
25
  import {ToolbarProvider, ToolbarPosition} from '../../utils/useToolbar';
26
26
  import SDKEvents from '../../utils/SdkEvents';
27
27
  import {useRoomInfo} from '../../components/room-info/useRoomInfo';
28
- import {controlMessageEnum, useUserName} from 'customization-api';
28
+ import {
29
+ ToolbarCustomItem,
30
+ controlMessageEnum,
31
+ useUserName,
32
+ } from 'customization-api';
29
33
  import events, {PersistanceLevel} from '../../rtm-events-api';
30
34
  import VideoCallMobileView from './VideoCallMobileView';
35
+ import Leftbar, {LeftbarProps} from '../../components/Leftbar';
36
+ import Rightbar, {RightbarProps} from '../../components/Rightbar';
31
37
 
32
38
  const VideoCallScreen = () => {
33
39
  const {sidePanel} = useSidePanel();
@@ -46,18 +52,26 @@ const VideoCallScreen = () => {
46
52
  VideocallAfterView,
47
53
  LeftbarComponent,
48
54
  RightbarComponent,
55
+ BottombarProps,
56
+ TopbarProps,
57
+ LeftbarProps,
58
+ RightbarProps,
49
59
  } = useCustomization((data) => {
50
60
  let components: {
51
61
  VideocallComponent?: React.ComponentType;
52
62
  ChatComponent: React.ComponentType<ChatProps>;
53
- BottombarComponent: React.ComponentType;
63
+ BottombarComponent: React.ComponentType<ControlsProps>;
54
64
  ParticipantsComponent: React.ComponentType;
55
65
  SettingsComponent: React.ComponentType;
56
- TopbarComponent: React.ComponentType;
66
+ TopbarComponent: React.ComponentType<NavbarProps>;
57
67
  VideocallBeforeView: React.ComponentType;
58
68
  VideocallAfterView: React.ComponentType;
59
- LeftbarComponent: React.ComponentType;
60
- RightbarComponent: React.ComponentType;
69
+ LeftbarComponent: React.ComponentType<LeftbarProps>;
70
+ RightbarComponent: React.ComponentType<RightbarProps>;
71
+ BottombarProps?: ToolbarCustomItem[];
72
+ TopbarProps?: ToolbarCustomItem[];
73
+ LeftbarProps?: ToolbarCustomItem[];
74
+ RightbarProps?: ToolbarCustomItem[];
61
75
  } = {
62
76
  BottombarComponent: Controls,
63
77
  TopbarComponent: Navbar,
@@ -66,8 +80,12 @@ const VideoCallScreen = () => {
66
80
  SettingsComponent: SettingsView,
67
81
  VideocallAfterView: React.Fragment,
68
82
  VideocallBeforeView: React.Fragment,
69
- LeftbarComponent: React.Fragment,
70
- RightbarComponent: React.Fragment,
83
+ LeftbarComponent: Leftbar,
84
+ RightbarComponent: Rightbar,
85
+ BottombarProps: [],
86
+ TopbarProps: [],
87
+ LeftbarProps: [],
88
+ RightbarProps: [],
71
89
  };
72
90
  if (
73
91
  data?.components?.videoCall &&
@@ -138,6 +156,35 @@ const VideoCallScreen = () => {
138
156
  components.RightbarComponent = data?.components?.videoCall.rightToolBar;
139
157
  }
140
158
 
159
+ if (
160
+ data?.components?.videoCall.bottomToolBar &&
161
+ typeof data?.components?.videoCall.bottomToolBar === 'object' &&
162
+ data?.components?.videoCall.bottomToolBar.length
163
+ ) {
164
+ components.BottombarProps = data?.components?.videoCall.bottomToolBar;
165
+ }
166
+ if (
167
+ data?.components?.videoCall.topToolBar &&
168
+ typeof data?.components?.videoCall.topToolBar === 'object' &&
169
+ data?.components?.videoCall.topToolBar.length
170
+ ) {
171
+ components.TopbarProps = data?.components?.videoCall.topToolBar;
172
+ }
173
+ if (
174
+ data?.components?.videoCall.rightToolBar &&
175
+ typeof data?.components?.videoCall.rightToolBar === 'object' &&
176
+ data?.components?.videoCall.rightToolBar.length
177
+ ) {
178
+ components.RightbarProps = data?.components?.videoCall.rightToolBar;
179
+ }
180
+ if (
181
+ data?.components?.videoCall.leftToolBar &&
182
+ typeof data?.components?.videoCall.leftToolBar === 'object' &&
183
+ data?.components?.videoCall.leftToolBar.length
184
+ ) {
185
+ components.LeftbarProps = data?.components?.videoCall.leftToolBar;
186
+ }
187
+
141
188
  if (
142
189
  data?.components?.videoCall.participantsPanel &&
143
190
  typeof data?.components?.videoCall.participantsPanel !== 'object' &&
@@ -175,11 +222,11 @@ const VideoCallScreen = () => {
175
222
  <VideocallBeforeView />
176
223
  <View style={style.fullRow}>
177
224
  <ToolbarProvider value={{position: ToolbarPosition.left}}>
178
- <LeftbarComponent />
225
+ <LeftbarComponent customItems={LeftbarProps} />
179
226
  </ToolbarProvider>
180
227
  <View style={style.full}>
181
228
  <ToolbarProvider value={{position: ToolbarPosition.top}}>
182
- <TopbarComponent />
229
+ <TopbarComponent customItems={TopbarProps} />
183
230
  </ToolbarProvider>
184
231
  <View
185
232
  style={[
@@ -211,12 +258,12 @@ const VideoCallScreen = () => {
211
258
  <></>
212
259
  ) : (
213
260
  <ToolbarProvider value={{position: ToolbarPosition.bottom}}>
214
- <BottombarComponent />
261
+ <BottombarComponent customItems={BottombarProps} />
215
262
  </ToolbarProvider>
216
263
  )}
217
264
  </View>
218
265
  <ToolbarProvider value={{position: ToolbarPosition.right}}>
219
- <RightbarComponent />
266
+ <RightbarComponent customItems={RightbarProps} />
220
267
  </ToolbarProvider>
221
268
  </View>
222
269
  <VideocallAfterView />
@@ -1,40 +1,44 @@
1
1
  import ParticipantsView from '../../components/ParticipantsView';
2
2
  import Chat from '../../components/Chat';
3
3
  import Navbar, {
4
- ParticipantsCountView,
5
- ParticipantsIconButton,
6
- ChatIconButton,
7
- SettingsIconButton,
4
+ MeetingTitleToolbarItem,
5
+ ParticipantCountToolbarItem,
6
+ RecordingStatusToolbarItem,
7
+ ChatToolbarItem,
8
+ ParticipantToolbarItem,
9
+ SettingsToobarItem,
8
10
  } from '../../components/Navbar';
9
- import CopyJoinInfo from '../../subComponents/CopyJoinInfo';
10
- import LayoutIconButton from '../../subComponents/LayoutIconButton';
11
- import SettingsView from '../../components/SettingsView';
12
- import Controls from '../../components/Controls';
13
- import LiveStreamControls from '../../components/livestream/views/LiveStreamControls';
14
- import LocalEndcall from '../../subComponents/LocalEndCall';
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';
20
-
11
+ import Controls, {
12
+ LayoutToolbarItem,
13
+ InviteToolbarItem,
14
+ RaiseHandToolbarItem,
15
+ LocalAudioToolbarItem,
16
+ LocalVideoToolbarItem,
17
+ SwitchCameraToolbarItem,
18
+ ScreenShareToolbarItem,
19
+ RecordingToolbarItem,
20
+ LocalEndcallToolbarItem,
21
+ } from '../../components/Controls';
21
22
  import ChatBubble from '../../subComponents/ChatBubble';
22
23
  import {ChatInput} from '../../subComponents/ChatInput';
24
+ import SettingsView from '../../components/SettingsView';
23
25
 
24
26
  const ToolbarComponents = {
25
- CopyJoinInfo,
26
- ParticipantsCountView,
27
- ParticipantsIconButton,
28
- ChatIconButton,
29
- LayoutIconButton,
30
- SettingsIconButton,
31
- LocalAudioMute,
32
- LocalVideoMute,
33
- LocalSwitchCamera,
34
- ScreenshareButton,
35
- Recording,
36
- LocalEndcall,
37
- LiveStreamControls,
27
+ MeetingTitleToolbarItem,
28
+ ParticipantCountToolbarItem,
29
+ RecordingStatusToolbarItem,
30
+ ChatToolbarItem,
31
+ ParticipantToolbarItem,
32
+ SettingsToobarItem,
33
+ LayoutToolbarItem,
34
+ InviteToolbarItem,
35
+ RaiseHandToolbarItem,
36
+ LocalAudioToolbarItem,
37
+ LocalVideoToolbarItem,
38
+ SwitchCameraToolbarItem,
39
+ ScreenShareToolbarItem,
40
+ RecordingToolbarItem,
41
+ LocalEndcallToolbarItem,
38
42
  };
39
43
  export {
40
44
  ParticipantsView,
@@ -15,6 +15,8 @@ import useGetMeetingPhrase from '../utils/useGetMeetingPhrase';
15
15
  import TertiaryButton from '../atoms/TertiaryButton';
16
16
  import IconButton, {IconButtonProps} from '../atoms/IconButton';
17
17
  import {useVideoCall} from '../components/useVideoCall';
18
+ import {useToolbarMenu} from '../utils/useMenu';
19
+ import ToolbarMenuItem from '../atoms/ToolbarMenuItem';
18
20
 
19
21
  export interface CopyJoinInfoProps {
20
22
  showLabel?: boolean;
@@ -24,6 +26,7 @@ export interface CopyJoinInfoProps {
24
26
  }
25
27
 
26
28
  const CopyJoinInfo = (props: CopyJoinInfoProps) => {
29
+ const {isToolbarMenuItem} = useToolbarMenu();
27
30
  const {
28
31
  showLabel = $config.ICON_TEXT || false,
29
32
  showTeritaryButton = false,
@@ -57,6 +60,8 @@ const CopyJoinInfo = (props: CopyJoinInfoProps) => {
57
60
  <>
58
61
  {showTeritaryButton ? (
59
62
  <TertiaryButton text="Invite" onPress={onPress} />
63
+ ) : isToolbarMenuItem ? (
64
+ <ToolbarMenuItem {...iconButtonProps} />
60
65
  ) : (
61
66
  <IconButton {...iconButtonProps} />
62
67
  )}
@@ -26,6 +26,9 @@ import IconButton, {IconButtonProps} from '../atoms/IconButton';
26
26
  import ThemeConfig from '../theme';
27
27
  import {ImageIconProps} from '../atoms/ImageIcon';
28
28
  import useIsHandRaised from '../utils/useIsHandRaised';
29
+ import {useToolbarMenu} from '../utils/useMenu';
30
+ import ToolbarMenuItem from '../atoms/ToolbarMenuItem';
31
+
29
32
  /**
30
33
  * A component to mute / unmute the local audio
31
34
  */
@@ -45,6 +48,7 @@ export interface LocalAudioMuteProps {
45
48
  }
46
49
 
47
50
  function LocalAudioMute(props: LocalAudioMuteProps) {
51
+ const {isToolbarMenuItem} = useToolbarMenu();
48
52
  const {rtcProps} = useContext(PropsContext);
49
53
  const {
50
54
  data: {isHost},
@@ -156,6 +160,8 @@ function LocalAudioMute(props: LocalAudioMuteProps) {
156
160
 
157
161
  return props?.render ? (
158
162
  props.render(onPress, isAudioEnabled)
163
+ ) : isToolbarMenuItem ? (
164
+ <ToolbarMenuItem {...iconButtonProps} />
159
165
  ) : (
160
166
  <IconButton {...iconButtonProps} />
161
167
  );
@@ -6,6 +6,9 @@ import IconButton, {IconButtonProps} from '../atoms/IconButton';
6
6
  import ReactNativeForegroundService from '@supersami/rn-foreground-service';
7
7
  import {Platform} from 'react-native';
8
8
  import {DispatchContext} from '../../agora-rn-uikit';
9
+ import {useToolbarMenu} from '../utils/useMenu';
10
+ import ToolbarMenuItem from '../atoms/ToolbarMenuItem';
11
+
9
12
  export interface LocalEndcallProps {
10
13
  showLabel?: boolean;
11
14
  isOnActionSheet?: boolean;
@@ -21,6 +24,7 @@ const stopForegroundService = () => {
21
24
  };
22
25
 
23
26
  const LocalEndcall = (props: LocalEndcallProps) => {
27
+ const {isToolbarMenuItem} = useToolbarMenu();
24
28
  const {dispatch} = useContext(DispatchContext);
25
29
  const {showLabel = $config.ICON_TEXT, isOnActionSheet = false} = props;
26
30
  //commented for v1 release
@@ -80,7 +84,11 @@ const LocalEndcall = (props: LocalEndcallProps) => {
80
84
  setModalVisible={setEndcallVisible}
81
85
  modalVisible={endcallVisible}
82
86
  />
83
- <IconButton {...iconButtonProps} />
87
+ {isToolbarMenuItem ? (
88
+ <ToolbarMenuItem {...iconButtonProps} />
89
+ ) : (
90
+ <IconButton {...iconButtonProps} />
91
+ )}
84
92
  </>
85
93
  );
86
94
  };
@@ -4,6 +4,8 @@ import {PropsContext, ToggleState} from '../../agora-rn-uikit';
4
4
  import Styles from '../components/styles';
5
5
  import {useLocalUserInfo, useRtc} from 'customization-api';
6
6
  import IconButton, {IconButtonProps} from '../atoms/IconButton';
7
+ import {useToolbarMenu} from '../utils/useMenu';
8
+ import ToolbarMenuItem from '../atoms/ToolbarMenuItem';
7
9
 
8
10
  export interface LocalSwitchCameraProps {
9
11
  showLabel?: boolean;
@@ -12,6 +14,7 @@ export interface LocalSwitchCameraProps {
12
14
  }
13
15
 
14
16
  function LocalSwitchCamera(props: LocalSwitchCameraProps) {
17
+ const {isToolbarMenuItem} = useToolbarMenu();
15
18
  const {callbacks} = useContext(PropsContext);
16
19
  const {RtcEngineUnsafe} = useRtc();
17
20
  const local = useLocalUserInfo();
@@ -39,6 +42,8 @@ function LocalSwitchCamera(props: LocalSwitchCameraProps) {
39
42
 
40
43
  return props?.render ? (
41
44
  props.render(onPress, isVideoEnabled)
45
+ ) : isToolbarMenuItem ? (
46
+ <ToolbarMenuItem {...iconButtonProps} />
42
47
  ) : (
43
48
  <IconButton {...iconButtonProps} />
44
49
  );
@@ -25,6 +25,9 @@ import IconButton, {IconButtonProps} from '../atoms/IconButton';
25
25
  import ThemeConfig from '../theme';
26
26
  import {ImageIconProps} from '../atoms/ImageIcon';
27
27
  import useIsHandRaised from '../utils/useIsHandRaised';
28
+ import {useToolbarMenu} from '../utils/useMenu';
29
+ import ToolbarMenuItem from '../atoms/ToolbarMenuItem';
30
+
28
31
  /**
29
32
  * A component to mute / unmute the local video
30
33
  */
@@ -45,6 +48,7 @@ export interface LocalVideoMuteProps {
45
48
 
46
49
  function LocalVideoMute(props: LocalVideoMuteProps) {
47
50
  const {rtcProps} = useContext(PropsContext);
51
+ const {isToolbarMenuItem} = useToolbarMenu();
48
52
  const {
49
53
  data: {isHost},
50
54
  } = useRoomInfo();
@@ -152,6 +156,8 @@ function LocalVideoMute(props: LocalVideoMuteProps) {
152
156
  }
153
157
  return props?.render ? (
154
158
  props.render(onPress, isVideoEnabled)
159
+ ) : isToolbarMenuItem ? (
160
+ <ToolbarMenuItem {...iconButtonProps} />
155
161
  ) : (
156
162
  <IconButton {...iconButtonProps} />
157
163
  );
@@ -13,6 +13,8 @@ import React from 'react';
13
13
  import {useRecording} from './recording/useRecording';
14
14
  import IconButton, {IconButtonProps} from '../atoms/IconButton';
15
15
  import {useVideoCall} from '../components/useVideoCall';
16
+ import {useToolbarMenu} from '../utils/useMenu';
17
+ import ToolbarMenuItem from '../atoms/ToolbarMenuItem';
16
18
 
17
19
  export interface RecordingButtonProps {
18
20
  showLabel?: boolean;
@@ -21,6 +23,7 @@ export interface RecordingButtonProps {
21
23
  }
22
24
 
23
25
  const Recording = (props: RecordingButtonProps) => {
26
+ const {isToolbarMenuItem} = useToolbarMenu();
24
27
  const {startRecording, inProgress, isRecordingActive} = useRecording();
25
28
  //commented for v1 release
26
29
  //const recordingButton = useString<boolean>('recordingButton');
@@ -55,10 +58,10 @@ const Recording = (props: RecordingButtonProps) => {
55
58
 
56
59
  return props?.render ? (
57
60
  props.render(onPress, isRecordingActive)
61
+ ) : isToolbarMenuItem ? (
62
+ <ToolbarMenuItem {...iconButtonProps} />
58
63
  ) : (
59
- <>
60
- <IconButton {...iconButtonProps} />
61
- </>
64
+ <IconButton {...iconButtonProps} />
62
65
  );
63
66
  };
64
67
 
@@ -21,11 +21,15 @@ import ThemeConfig from '../../../theme';
21
21
  import {ClientRole, PropsContext} from '../../../../agora-rn-uikit';
22
22
  import {useContent} from 'customization-api';
23
23
  import {isMobileUA} from '../../../utils/common';
24
+ import {IconButtonProps} from '../../../atoms/IconButton';
25
+ import {useToolbarMenu} from '../../../utils/useMenu';
26
+ import ToolbarMenuItem from '../../../atoms/ToolbarMenuItem';
24
27
 
25
28
  interface LocalRaiseHandProps {
26
29
  showLabel?: boolean;
27
30
  }
28
31
  const LocalRaiseHand = (props: LocalRaiseHandProps) => {
32
+ const {isToolbarMenuItem} = useToolbarMenu();
29
33
  const {audienceSendsRequest, audienceRecallsRequest, raiseHandList} =
30
34
  useContext(LiveStreamContext);
31
35
  const {rtcProps} = useContext(PropsContext);
@@ -37,30 +41,33 @@ const LocalRaiseHand = (props: LocalRaiseHandProps) => {
37
41
  const handStatusText = (toggle: boolean) =>
38
42
  toggle ? 'Lower hand' : 'Raise Hand';
39
43
  const isHandRasied = raiseHandList[localUid]?.raised === RaiseHandValue.TRUE;
40
- return (
41
- <IconButton
42
- iconProps={{
43
- name: isHandRasied ? 'lower-hand' : 'raise-hand',
44
- tintColor: isHandRasied
45
- ? $config.PRIMARY_ACTION_TEXT_COLOR
46
- : $config.SECONDARY_ACTION_COLOR,
47
- iconBackgroundColor: isHandRasied
48
- ? $config.PRIMARY_ACTION_BRAND_COLOR
49
- : '',
50
- base64: isMobileUA() ? true : false,
51
- }}
52
- btnTextProps={{
53
- text: showLabel ? handStatusText(isHandRasied) : '',
54
- textColor: $config.FONT_COLOR,
55
- }}
56
- onPress={() => {
57
- if (isHandRasied) {
58
- audienceRecallsRequest();
59
- } else {
60
- audienceSendsRequest();
61
- }
62
- }}
63
- />
44
+ const iconButtonProps: IconButtonProps = {
45
+ iconProps: {
46
+ name: isHandRasied ? 'lower-hand' : 'raise-hand',
47
+ tintColor: isHandRasied
48
+ ? $config.PRIMARY_ACTION_TEXT_COLOR
49
+ : $config.SECONDARY_ACTION_COLOR,
50
+ iconBackgroundColor: isHandRasied
51
+ ? $config.PRIMARY_ACTION_BRAND_COLOR
52
+ : '',
53
+ base64: isMobileUA() ? true : false,
54
+ },
55
+ btnTextProps: {
56
+ text: showLabel ? handStatusText(isHandRasied) : '',
57
+ textColor: $config.FONT_COLOR,
58
+ },
59
+ onPress: () => {
60
+ if (isHandRasied) {
61
+ audienceRecallsRequest();
62
+ } else {
63
+ audienceSendsRequest();
64
+ }
65
+ },
66
+ };
67
+ return isToolbarMenuItem ? (
68
+ <ToolbarMenuItem {...iconButtonProps} />
69
+ ) : (
70
+ <IconButton {...iconButtonProps} />
64
71
  );
65
72
  };
66
73
 
@@ -19,6 +19,8 @@ import hexadecimalTransparency from '../../utils/hexadecimalTransparency';
19
19
  import {PropsContext, ClientRole} from '../../../agora-rn-uikit';
20
20
  import {useLocalUserInfo, useRoomInfo} from 'customization-api';
21
21
  import useIsHandRaised from '../../utils/useIsHandRaised';
22
+ import {useToolbarMenu} from '../../utils/useMenu';
23
+ import ToolbarMenuItem from '../../atoms/ToolbarMenuItem';
22
24
  /**
23
25
  * A component to start and stop screen sharing on web clients.
24
26
  * Screen sharing is not yet implemented on mobile platforms.
@@ -30,6 +32,7 @@ export interface ScreenshareButtonProps {
30
32
  }
31
33
 
32
34
  const ScreenshareButton = (props: ScreenshareButtonProps) => {
35
+ const {isToolbarMenuItem} = useToolbarMenu();
33
36
  const {rtcProps} = useContext(PropsContext);
34
37
  const {
35
38
  data: {isHost},
@@ -84,6 +87,8 @@ const ScreenshareButton = (props: ScreenshareButtonProps) => {
84
87
 
85
88
  return props?.render ? (
86
89
  props.render(onPress, isScreenshareActive)
90
+ ) : isToolbarMenuItem ? (
91
+ <ToolbarMenuItem {...iconButtonProps} />
87
92
  ) : (
88
93
  <IconButton {...iconButtonProps} />
89
94
  );
@@ -0,0 +1,31 @@
1
+ /*
2
+ ********************************************
3
+ Copyright © 2021 Agora Lab, Inc., all rights reserved.
4
+ AppBuilder and all associated components, source code, APIs, services, and documentation
5
+ (the “Materials”) are owned by Agora Lab, Inc. and its licensors. The Materials may not be
6
+ accessed, used, modified, or distributed for any purpose without a license from Agora Lab, Inc.
7
+ Use without a license or in violation of any license terms and conditions (including use for
8
+ any purpose competitive to Agora Lab, Inc.’s business) is strictly prohibited. For more
9
+ information visit https://appbuilder.agora.io.
10
+ *********************************************
11
+ */
12
+
13
+ import React from 'react';
14
+ import {createHook} from 'customization-implementation';
15
+
16
+ const ToolbarMenuContext = React.createContext({isToolbarMenuItem: false});
17
+
18
+ export interface ToolbarMenuProviderProps {
19
+ children: React.ReactNode;
20
+ }
21
+ const ToolbarMenuProvider = (props: ToolbarMenuProviderProps) => {
22
+ return (
23
+ <ToolbarMenuContext.Provider value={{isToolbarMenuItem: true}}>
24
+ {props.children}
25
+ </ToolbarMenuContext.Provider>
26
+ );
27
+ };
28
+
29
+ const useToolbarMenu = createHook(ToolbarMenuContext);
30
+
31
+ export {ToolbarMenuProvider, useToolbarMenu};