@100mslive/roomkit-react 0.2.7 → 0.2.8-alpha.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (31) hide show
  1. package/dist/{HLSView-KPCDHIH2.js → HLSView-37B2YVTC.js} +2 -2
  2. package/dist/Prebuilt/components/MoreSettings/FullScreenItem.d.ts +2 -0
  3. package/dist/Prebuilt/components/MoreSettings/MuteAllContent.d.ts +15 -0
  4. package/dist/Prebuilt/components/MoreSettings/MuteAllModal.d.ts +5 -0
  5. package/dist/Prebuilt/components/MoreSettings/constants.d.ts +8 -0
  6. package/dist/Prebuilt/components/hooks/useFullscreen.d.ts +5 -0
  7. package/dist/{chunk-Q6VWGYM4.js → chunk-72B32WVR.js} +663 -636
  8. package/dist/chunk-72B32WVR.js.map +7 -0
  9. package/dist/index.cjs.js +969 -946
  10. package/dist/index.cjs.js.map +4 -4
  11. package/dist/index.js +1 -1
  12. package/dist/meta.cjs.json +105 -61
  13. package/dist/meta.esbuild.json +111 -67
  14. package/package.json +6 -6
  15. package/src/Prebuilt/components/Connection/TileConnection.tsx +30 -27
  16. package/src/Prebuilt/components/Footer/RoleOptions.tsx +81 -38
  17. package/src/Prebuilt/components/MoreSettings/{FullScreenItem.jsx → FullScreenItem.tsx} +1 -1
  18. package/src/Prebuilt/components/MoreSettings/MuteAllContent.tsx +81 -0
  19. package/src/Prebuilt/components/MoreSettings/{MuteAllModal.jsx → MuteAllModal.tsx} +21 -22
  20. package/src/Prebuilt/components/MoreSettings/SplitComponents/DesktopOptions.tsx +0 -2
  21. package/src/Prebuilt/components/MoreSettings/constants.ts +12 -0
  22. package/src/Prebuilt/components/Notifications/TrackBulkUnmuteModal.tsx +4 -2
  23. package/src/Prebuilt/components/Notifications/TrackUnmuteModal.tsx +2 -2
  24. package/src/Prebuilt/components/Preview/PreviewJoin.tsx +1 -1
  25. package/src/Prebuilt/components/VideoLayouts/ProminenceLayout.tsx +2 -1
  26. package/src/Prebuilt/components/VideoLayouts/RoleProminence.tsx +5 -1
  27. package/src/Prebuilt/components/hooks/{useFullscreen.js → useFullscreen.ts} +5 -3
  28. package/src/Prebuilt/primitives/DialogContent.jsx +3 -2
  29. package/dist/chunk-Q6VWGYM4.js.map +0 -7
  30. package/src/Prebuilt/components/MoreSettings/MuteAllContent.jsx +0 -61
  31. /package/dist/{HLSView-KPCDHIH2.js.map → HLSView-37B2YVTC.js.map} +0 -0
@@ -1,6 +1,13 @@
1
1
  import React, { useState } from 'react';
2
2
  import { DefaultConferencingScreen_Elements } from '@100mslive/types-prebuilt';
3
- import { HMSPeer, selectPermissions, useHMSActions, useHMSStore, useHMSVanillaStore } from '@100mslive/react-sdk';
3
+ import {
4
+ HMSPeer,
5
+ selectPermissions,
6
+ selectRoleByRoleName,
7
+ useHMSActions,
8
+ useHMSStore,
9
+ useHMSVanillaStore,
10
+ } from '@100mslive/react-sdk';
4
11
  import {
5
12
  MicOffIcon,
6
13
  MicOnIcon,
@@ -20,33 +27,96 @@ import { getMetadata } from '../../common/utils';
20
27
  const dropdownItemCSS = { backgroundColor: '$surface_default', gap: '$4', p: '$8' };
21
28
  const optionTextCSS = { fontWeight: '$semiBold', color: '$on_surface_high', textTransform: 'none' };
22
29
 
23
- export const RoleOptions = ({ roleName, peerList }: { roleName: string; peerList: HMSPeer[] }) => {
24
- const [openOptions, setOpenOptions] = useState(false);
25
- const permissions = useHMSStore(selectPermissions);
26
- const hmsActions = useHMSActions();
27
- const { elements } = useRoomLayoutConferencingScreen();
28
- const { on_stage_role, off_stage_roles = [] } = (elements as DefaultConferencingScreen_Elements)?.on_stage_exp || {};
29
-
30
+ const MuteUnmuteOption = ({ roleName, peerList }: { peerList: HMSPeer[]; roleName: string }) => {
30
31
  const vanillaStore = useHMSVanillaStore();
31
32
  const store = vanillaStore.getState();
33
+ const hmsActions = useHMSActions();
34
+ const permissions = useHMSStore(selectPermissions);
35
+ const role = useHMSStore(selectRoleByRoleName(roleName));
32
36
 
33
37
  let allPeersHaveVideoOn = true;
34
38
  let allPeersHaveAudioOn = true;
35
39
 
36
40
  peerList.forEach(peer => {
41
+ if (peer.isLocal) {
42
+ return;
43
+ }
37
44
  const isAudioOn = !!peer.audioTrack && store.tracks[peer.audioTrack]?.enabled;
38
45
  const isVideoOn = !!peer.videoTrack && store.tracks[peer.videoTrack]?.enabled;
39
46
  allPeersHaveAudioOn = allPeersHaveAudioOn && isAudioOn;
40
47
  allPeersHaveVideoOn = allPeersHaveVideoOn && isVideoOn;
41
48
  });
42
49
 
43
- const canMuteRole = permissions?.mute && roleName === on_stage_role;
50
+ const setTrackEnabled = async (type: 'audio' | 'video', enabled = false) => {
51
+ try {
52
+ await hmsActions.setRemoteTracksEnabled({ roles: [roleName], source: 'regular', type, enabled });
53
+ } catch (e) {
54
+ console.error(e);
55
+ }
56
+ };
57
+
58
+ return (
59
+ <>
60
+ {role.publishParams.allowed?.includes('audio') && (
61
+ <>
62
+ {allPeersHaveAudioOn && permissions?.mute ? (
63
+ <Dropdown.Item css={dropdownItemCSS} onClick={() => setTrackEnabled('audio', false)}>
64
+ <MicOffIcon />
65
+ <Text variant="sm" css={optionTextCSS}>
66
+ Mute Audio
67
+ </Text>
68
+ </Dropdown.Item>
69
+ ) : null}
70
+
71
+ {!allPeersHaveAudioOn && permissions?.unmute ? (
72
+ <Dropdown.Item css={dropdownItemCSS} onClick={() => setTrackEnabled('audio', true)}>
73
+ <MicOnIcon />
74
+ <Text variant="sm" css={optionTextCSS}>
75
+ Unmute Audio
76
+ </Text>
77
+ </Dropdown.Item>
78
+ ) : null}
79
+ </>
80
+ )}
81
+
82
+ {role.publishParams.allowed?.includes('audio') && (
83
+ <>
84
+ {allPeersHaveVideoOn && permissions?.mute ? (
85
+ <Dropdown.Item css={dropdownItemCSS} onClick={() => setTrackEnabled('video', false)}>
86
+ <VideoOffIcon />
87
+ <Text variant="sm" css={optionTextCSS}>
88
+ Mute Video
89
+ </Text>
90
+ </Dropdown.Item>
91
+ ) : null}
92
+
93
+ {!allPeersHaveVideoOn && permissions?.unmute ? (
94
+ <Dropdown.Item css={dropdownItemCSS} onClick={() => setTrackEnabled('video', true)}>
95
+ <VideoOnIcon />
96
+ <Text variant="sm" css={optionTextCSS}>
97
+ Unmute Video
98
+ </Text>
99
+ </Dropdown.Item>
100
+ ) : null}
101
+ </>
102
+ )}
103
+ </>
104
+ );
105
+ };
106
+
107
+ export const RoleOptions = ({ roleName, peerList }: { roleName: string; peerList: HMSPeer[] }) => {
108
+ const [openOptions, setOpenOptions] = useState(false);
109
+ const permissions = useHMSStore(selectPermissions);
110
+ const hmsActions = useHMSActions();
111
+ const { elements } = useRoomLayoutConferencingScreen();
112
+ const { on_stage_role, off_stage_roles = [] } = (elements as DefaultConferencingScreen_Elements)?.on_stage_exp || {};
113
+ const canMuteOrUnmute = permissions?.mute || permissions?.unmute;
44
114
  const canRemoveRoleFromStage = permissions?.changeRole && roleName === on_stage_role;
45
115
  // on stage and off stage roles
46
116
  const canRemoveRoleFromRoom =
47
117
  permissions?.removeOthers && (on_stage_role === roleName || off_stage_roles?.includes(roleName));
48
118
 
49
- if (!(canMuteRole || canRemoveRoleFromStage || canRemoveRoleFromRoom) || peerList.length === 0) {
119
+ if (!(canMuteOrUnmute || canRemoveRoleFromStage || canRemoveRoleFromRoom) || peerList.length === 0) {
50
120
  return null;
51
121
  }
52
122
 
@@ -59,14 +129,6 @@ export const RoleOptions = ({ roleName, peerList }: { roleName: string; peerList
59
129
  });
60
130
  };
61
131
 
62
- const setTrackEnabled = async (type: 'audio' | 'video', enabled = false) => {
63
- try {
64
- await hmsActions.setRemoteTracksEnabled({ roles: [roleName], source: 'regular', type, enabled });
65
- } catch (e) {
66
- console.error(e);
67
- }
68
- };
69
-
70
132
  const removePeersFromRoom = async () => {
71
133
  try {
72
134
  peerList.forEach(async peer => {
@@ -118,26 +180,7 @@ export const RoleOptions = ({ roleName, peerList }: { roleName: string; peerList
118
180
  </Dropdown.Item>
119
181
  )}
120
182
 
121
- {canMuteRole && (
122
- <>
123
- <Dropdown.Item css={dropdownItemCSS} onClick={() => setTrackEnabled('audio', !allPeersHaveAudioOn)}>
124
- {allPeersHaveAudioOn ? <MicOffIcon /> : <MicOnIcon />}
125
- <Text variant="sm" css={optionTextCSS}>
126
- {allPeersHaveAudioOn ? 'Mute' : 'Unmute'} Audio
127
- </Text>
128
- </Dropdown.Item>
129
-
130
- <Dropdown.Item
131
- css={{ ...dropdownItemCSS, borderTop: '1px solid $border_bright' }}
132
- onClick={() => setTrackEnabled('video', !allPeersHaveVideoOn)}
133
- >
134
- {allPeersHaveVideoOn ? <VideoOffIcon /> : <VideoOnIcon />}
135
- <Text variant="sm" css={optionTextCSS}>
136
- {allPeersHaveVideoOn ? 'Mute' : 'Unmute'} Video
137
- </Text>
138
- </Dropdown.Item>
139
- </>
140
- )}
183
+ {canMuteOrUnmute && <MuteUnmuteOption peerList={peerList} roleName={roleName} />}
141
184
 
142
185
  {canRemoveRoleFromRoom && (
143
186
  <Dropdown.Item
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import { ExpandIcon } from '@100mslive/react-icons';
3
- import { Dropdown, Text } from '../../../';
3
+ import { Dropdown, Text } from '../../..';
4
4
  import { useFullscreen } from '../hooks/useFullscreen';
5
5
 
6
6
  export const FullScreenItem = () => {
@@ -0,0 +1,81 @@
1
+ import React from 'react';
2
+ import { HMSRoleName, HMSTrackSource, HMSTrackType, selectPermissions, useHMSStore } from '@100mslive/react-sdk';
3
+ import { Button } from '../../../Button';
4
+ import { Label } from '../../../Label';
5
+ import { Flex } from '../../../Layout';
6
+ import { RadioGroup } from '../../../RadioGroup';
7
+ import { Text } from '../../../Text';
8
+ // @ts-ignore: No implicit any
9
+ import { DialogRow, DialogSelect } from '../../primitives/DialogContent';
10
+ import { trackSourceOptions, trackTypeOptions } from './constants';
11
+
12
+ export const MuteAllContent = (props: {
13
+ muteAll: () => Promise<void>;
14
+ roles?: HMSRoleName[];
15
+ enabled: boolean;
16
+ setEnabled: (value: boolean) => void;
17
+ trackType?: HMSTrackType;
18
+ setTrackType: (value: HMSTrackType) => void;
19
+ selectedRole?: HMSRoleName;
20
+ setRole: (value: HMSRoleName) => void;
21
+ selectedSource?: HMSTrackSource;
22
+ setSource: (value: HMSTrackSource) => void;
23
+ isMobile: boolean;
24
+ }) => {
25
+ const roles = props.roles || [];
26
+ const permissions = useHMSStore(selectPermissions);
27
+ return (
28
+ <>
29
+ <DialogSelect
30
+ title="Role"
31
+ options={[{ label: 'All Roles', value: '' }, ...roles.map(role => ({ label: role, value: role }))]}
32
+ selected={props.selectedRole}
33
+ keyField="value"
34
+ labelField="label"
35
+ onChange={props.setRole}
36
+ />
37
+ <DialogSelect
38
+ title="Track type"
39
+ options={trackTypeOptions}
40
+ selected={props.trackType}
41
+ onChange={props.setTrackType}
42
+ keyField="value"
43
+ labelField="label"
44
+ />
45
+ <DialogSelect
46
+ title="Track source"
47
+ options={trackSourceOptions}
48
+ selected={props.selectedSource}
49
+ onChange={props.setSource}
50
+ keyField="value"
51
+ labelField="label"
52
+ />
53
+ <DialogRow>
54
+ <Text variant="md">Track status</Text>
55
+ <RadioGroup.Root value={String(props.enabled)} onValueChange={value => props.setEnabled(value === 'true')}>
56
+ {permissions?.mute && (
57
+ <Flex align="center" css={{ mr: '$8' }}>
58
+ <RadioGroup.Item value="false" id="trackDisableRadio" css={{ mr: '$4' }}>
59
+ <RadioGroup.Indicator />
60
+ </RadioGroup.Item>
61
+ <Label htmlFor="trackDisableRadio">Mute</Label>
62
+ </Flex>
63
+ )}
64
+ {permissions?.unmute && (
65
+ <Flex align="center" css={{ cursor: 'pointer' }}>
66
+ <RadioGroup.Item value="true" id="trackEnableRadio" css={{ mr: '$4' }}>
67
+ <RadioGroup.Indicator />
68
+ </RadioGroup.Item>
69
+ <Label htmlFor="trackEnableRadio">Request Unmute</Label>
70
+ </Flex>
71
+ )}
72
+ </RadioGroup.Root>
73
+ </DialogRow>
74
+ <DialogRow justify="end">
75
+ <Button variant="primary" onClick={props.muteAll} css={{ w: props?.isMobile ? '100%' : '' }}>
76
+ Apply
77
+ </Button>
78
+ </DialogRow>
79
+ </>
80
+ );
81
+ };
@@ -1,31 +1,32 @@
1
1
  import React, { useCallback, useState } from 'react';
2
- import { useHMSActions } from '@100mslive/react-sdk';
2
+ import {
3
+ HMSRoleName,
4
+ HMSTrackSource,
5
+ HMSTrackType,
6
+ selectAvailableRoleNames,
7
+ useHMSActions,
8
+ useHMSStore,
9
+ } from '@100mslive/react-sdk';
3
10
  import { MicOffIcon } from '@100mslive/react-icons';
4
- import { Dialog } from '../../../';
11
+ import { Dialog } from '../../..';
5
12
  import { Sheet } from '../../../Sheet';
13
+ // @ts-ignore: No implicit any
6
14
  import { DialogContent } from '../../primitives/DialogContent';
7
15
  import { MuteAllContent } from './MuteAllContent';
8
- import { useFilteredRoles } from '../../common/hooks';
9
16
 
10
- const trackSourceOptions = [
11
- { label: 'All Track Sources', value: '' },
12
- { label: 'regular', value: 'regular' },
13
- { label: 'screen', value: 'screen' },
14
- { label: 'audioplaylist', value: 'audioplaylist' },
15
- { label: 'videoplaylist', value: 'videoplaylist' },
16
- ];
17
- const trackTypeOptions = [
18
- { label: 'All Track Types', value: '' },
19
- { label: 'audio', value: 'audio' },
20
- { label: 'video', value: 'video' },
21
- ];
22
- export const MuteAllModal = ({ onOpenChange, isMobile = false }) => {
23
- const roles = useFilteredRoles();
17
+ export const MuteAllModal = ({
18
+ onOpenChange,
19
+ isMobile = false,
20
+ }: {
21
+ onOpenChange: (value: boolean) => void;
22
+ isMobile?: boolean;
23
+ }) => {
24
+ const roles = useHMSStore(selectAvailableRoleNames);
24
25
  const hmsActions = useHMSActions();
25
26
  const [enabled, setEnabled] = useState(false);
26
- const [trackType, setTrackType] = useState();
27
- const [selectedRole, setRole] = useState();
28
- const [selectedSource, setSource] = useState();
27
+ const [trackType, setTrackType] = useState<HMSTrackType>();
28
+ const [selectedRole, setRole] = useState<HMSRoleName>();
29
+ const [selectedSource, setSource] = useState<HMSTrackSource>();
29
30
 
30
31
  const muteAll = useCallback(async () => {
31
32
  await hmsActions.setRemoteTracksEnabled({
@@ -48,8 +49,6 @@ export const MuteAllModal = ({ onOpenChange, isMobile = false }) => {
48
49
  setRole,
49
50
  selectedSource,
50
51
  setSource,
51
- trackSourceOptions,
52
- trackTypeOptions,
53
52
  isMobile,
54
53
  };
55
54
 
@@ -8,7 +8,6 @@ import {
8
8
  import { selectAppData, selectLocalPeerID, useHMSActions, useHMSStore } from '@100mslive/react-sdk';
9
9
  import { BrbIcon, CheckIcon, HamburgerMenuIcon, InfoIcon, PipIcon, SettingsIcon } from '@100mslive/react-icons';
10
10
  import { Checkbox, Dropdown, Flex, Text, Tooltip } from '../../../..';
11
- // @ts-ignore: No implicit any
12
11
  import IconButton from '../../../IconButton';
13
12
  // @ts-ignore: No implicit any
14
13
  import { PIP } from '../../PIP';
@@ -26,7 +25,6 @@ import { StatsForNerds } from '../../StatsForNerds';
26
25
  import { BulkRoleChangeModal } from '../BulkRoleChangeModal';
27
26
  // @ts-ignore: No implicit any
28
27
  import { FullScreenItem } from '../FullScreenItem';
29
- // @ts-ignore: No implicit any
30
28
  import { MuteAllModal } from '../MuteAllModal';
31
29
  // @ts-ignore: No implicit any
32
30
  import { useDropdownList } from '../../hooks/useDropdownList';
@@ -0,0 +1,12 @@
1
+ export const trackSourceOptions = [
2
+ { label: 'All Track Sources', value: '' },
3
+ { label: 'regular', value: 'regular' },
4
+ { label: 'screen', value: 'screen' },
5
+ { label: 'audioplaylist', value: 'audioplaylist' },
6
+ { label: 'videoplaylist', value: 'videoplaylist' },
7
+ ];
8
+ export const trackTypeOptions = [
9
+ { label: 'All Track Types', value: '' },
10
+ { label: 'audio', value: 'audio' },
11
+ { label: 'video', value: 'video' },
12
+ ];
@@ -40,10 +40,12 @@ export const TrackBulkUnmuteModal = () => {
40
40
 
41
41
  const { requestedBy: peer, tracks, enabled } = muteNotification;
42
42
 
43
+ const types = new Set(tracks.map(track => track.type));
44
+
43
45
  return (
44
46
  <RequestDialog
45
- title="Track Unmute Request"
46
- body={`${peer?.name} has requested you to unmute your tracks.`}
47
+ title="Unmute request"
48
+ body={`${peer?.name} is requesting you to unmute your ${Array.from(types).join(',')}`}
47
49
  onOpenChange={(value: boolean) => !value && setMuteNotification(null)}
48
50
  onAction={() => {
49
51
  tracks.forEach(track => {
@@ -42,9 +42,9 @@ export const TrackUnmuteModal = () => {
42
42
 
43
43
  return (
44
44
  <RequestDialog
45
- title="Track Unmute Request"
45
+ title={`Unmute your ${track.type}?`}
46
46
  onOpenChange={(value: boolean) => !value && setMuteNotification(null)}
47
- body={`${peer?.name} has requested you to unmute your ${track?.source} ${track?.type}.`}
47
+ body={`${peer?.name}is requesting you to unmute your ${track?.type}.`}
48
48
  onAction={() => {
49
49
  hmsActions.setEnabledTrack(track.id, enabled);
50
50
  setMuteNotification(null);
@@ -225,7 +225,7 @@ export const PreviewTile = ({ name, error }: { name: string; error?: boolean })
225
225
  >
226
226
  {localPeer ? (
227
227
  <>
228
- <TileConnection name={name} peerId={localPeer.id} hideLabel={true} />
228
+ <TileConnection name="" peerId={localPeer.id} hideLabel={false} />
229
229
  <Video
230
230
  mirror={track?.facingMode !== 'environment' && mirrorLocalVideo}
231
231
  trackId={localPeer.videoTrack}
@@ -42,7 +42,8 @@ const SecondarySection = ({
42
42
  const gridStyles = hasSidebar
43
43
  ? {
44
44
  gridTemplateColumns: '1fr',
45
- gridTemplateRows: `repeat(${tiles.length}, minmax(0, 135px))`,
45
+ gridTemplateRows: `repeat(${tiles.length}, minmax(0, 1fr))`,
46
+ maxHeight: '100%',
46
47
  }
47
48
  : {
48
49
  gridTemplateRows: React.Children.count(children) > 0 ? '136px auto' : '154px',
@@ -1,5 +1,7 @@
1
1
  import React, { useEffect, useState } from 'react';
2
+ import { useMedia } from 'react-use';
2
3
  import { selectLocalPeer, useHMSStore } from '@100mslive/react-sdk';
4
+ import { config as cssConfig } from '../../../Theme';
3
5
  import { InsetTile } from '../InsetTile';
4
6
  import { Pagination } from '../Pagination';
5
7
  import { SecondaryTiles } from '../SecondaryTiles';
@@ -24,7 +26,9 @@ export function RoleProminence({
24
26
  const { prominentPeers, secondaryPeers } = useRoleProminencePeers(prominentRoles, peers, isInsetEnabled);
25
27
  const localPeer = useHMSStore(selectLocalPeer);
26
28
  const layoutMode = useUISettings(UI_SETTINGS.layoutMode);
27
- const maxTileCount = 4;
29
+ const isMobile = useMedia(cssConfig.media.md);
30
+ let maxTileCount = useUISettings(UI_SETTINGS.maxTileCount);
31
+ maxTileCount = isMobile ? 4 : maxTileCount;
28
32
  const pageList = usePagesWithTiles({
29
33
  peers: prominentPeers,
30
34
  maxTileCount,
@@ -1,5 +1,6 @@
1
1
  import { useCallback, useEffect, useState } from 'react';
2
2
  import screenfull from 'screenfull';
3
+ // @ts-ignore: No implicit any
3
4
  import { ToastManager } from '../Toast/ToastManager';
4
5
  import { DEFAULT_PORTAL_CONTAINER } from '../../common/constants';
5
6
 
@@ -12,13 +13,14 @@ export const useFullscreen = () => {
12
13
  return;
13
14
  }
14
15
  try {
16
+ const container = document.querySelector(DEFAULT_PORTAL_CONTAINER);
15
17
  if (isFullScreenEnabled) {
16
18
  await screenfull.exit();
17
- } else {
18
- await screenfull.request(document.querySelector(DEFAULT_PORTAL_CONTAINER));
19
+ } else if (container) {
20
+ await screenfull.request(container);
19
21
  }
20
22
  } catch (err) {
21
- ToastManager.addToast({ title: err.message });
23
+ ToastManager.addToast({ title: (err as Error).message });
22
24
  }
23
25
  }, [isFullScreenEnabled]);
24
26
 
@@ -11,6 +11,7 @@ import { Dialog } from '../../Modal';
11
11
  import { Select } from '../../Select';
12
12
  import { Switch } from '../../Switch';
13
13
  import { Text } from '../../Text';
14
+ import { flexCenter } from '../../utils';
14
15
 
15
16
  export const DialogContent = ({ Icon, title, closeable = true, children, css, iconCSS = {}, ...props }) => {
16
17
  return (
@@ -21,9 +22,9 @@ export const DialogContent = ({ Icon, title, closeable = true, children, css, ic
21
22
  <Flex justify="between">
22
23
  <Flex align="center" css={{ mb: '$1' }}>
23
24
  {Icon ? (
24
- <Box css={{ mr: '$2', color: '$on_primary_high', ...iconCSS }}>
25
+ <Flex css={{ mr: '$2', color: '$on_primary_high', ...flexCenter, ...iconCSS }}>
25
26
  <Icon />
26
- </Box>
27
+ </Flex>
27
28
  ) : null}
28
29
  <Text variant="h6" inline>
29
30
  {title}