feat: Update Control Bar buttons (#11)
* feat: Update control-bar size * feat: Refactor icons * feat: Update control bar buttons * feat: Add translations
This commit is contained in:
parent
580800457e
commit
84a92c540a
@ -1,19 +0,0 @@
|
||||
import * as React from 'react'
|
||||
import type { SVGProps } from 'react'
|
||||
const SvgChatIcon = (props: SVGProps<SVGSVGElement>) => (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width={16} height={18} fill="none" {...props}>
|
||||
<path
|
||||
fill="currentColor"
|
||||
fillRule="evenodd"
|
||||
d="M0 2.75A2.75 2.75 0 0 1 2.75 0h10.5A2.75 2.75 0 0 1 16 2.75v13.594a.75.75 0 0 1-1.234.572l-3.691-3.12a1.25 1.25 0 0 0-.807-.296H2.75A2.75 2.75 0 0 1 0 10.75v-8ZM2.75 1.5c-.69 0-1.25.56-1.25 1.25v8c0 .69.56 1.25 1.25 1.25h7.518c.65 0 1.279.23 1.775.65l2.457 2.077V2.75c0-.69-.56-1.25-1.25-1.25H2.75Z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
<path
|
||||
fill="currentColor"
|
||||
fillRule="evenodd"
|
||||
d="M3 4.5a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9a.5.5 0 0 1-.5-.5Zm0 2a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9a.5.5 0 0 1-.5-.5Zm0 2a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 0 1h-5a.5.5 0 0 1-.5-.5Z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
export default SvgChatIcon
|
||||
@ -1,15 +0,0 @@
|
||||
import * as React from 'react'
|
||||
import type { SVGProps } from 'react'
|
||||
|
||||
const PeopleIcon = (props: SVGProps<SVGSVGElement>) => (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="28" height="25" viewBox="0 0 28 25" fill="none" {...props}>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M10.0003 11.1667C12.9417 11.1667 15.3337 8.77467 15.3337 5.83333C15.3337 2.892 12.9417 0.5 10.0003 0.5C7.05899 0.5 4.66699 2.892 4.66699 5.83333C4.66699 8.77467 7.05899 11.1667 10.0003 11.1667ZM20.667 13.8333C22.8723 13.8333 24.667 12.0387 24.667 9.83333C24.667 7.628 22.8723 5.83333 20.667 5.83333C18.4617 5.83333 16.667 7.628 16.667 9.83333C16.667 12.0387 18.4617 13.8333 20.667 13.8333ZM27.3337 21.8333C27.3337 22.5693 26.7377 23.1667 26.0003 23.1667H19.3337C19.3337 23.9027 18.7377 24.5 18.0003 24.5H2.00033C1.26299 24.5 0.666992 23.9027 0.666992 23.1667C0.666992 18.02 4.85499 13.8333 10.0003 13.8333C12.5697 13.8333 14.8977 14.8773 16.587 16.5613C17.7457 15.6653 19.1723 15.1667 20.667 15.1667C24.343 15.1667 27.3337 18.1573 27.3337 21.8333Z"
|
||||
fill={props.fill ?? 'white'}
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
|
||||
export default PeopleIcon
|
||||
32
src/components/Icons/CameraIcon.tsx
Normal file
32
src/components/Icons/CameraIcon.tsx
Normal file
@ -0,0 +1,32 @@
|
||||
import React from 'react'
|
||||
import { Props } from './Icons.type'
|
||||
|
||||
const CameraIcon = ({ enabled = true, ...props }: Props) => {
|
||||
return enabled ? (
|
||||
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="camara">
|
||||
<path
|
||||
id="Vector"
|
||||
d="M28.28 24.2V30.536C28.28 31.0132 27.8932 31.4 27.416 31.4H11.864C11.3868 31.4 11 31.0132 11 30.536V17.864C11 17.3868 11.3868 17 11.864 17H27.416C27.8932 17 28.28 17.3868 28.28 17.864V24.2ZM28.28 24.2L35.5029 18.1809C36.0656 17.712 36.92 18.1121 36.92 18.8447V29.5554C36.92 30.2879 36.0656 30.6881 35.5029 30.2191L28.28 24.2Z"
|
||||
stroke="#FCFCFC"
|
||||
stroke-width="1.92"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
) : (
|
||||
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||
<path
|
||||
d="M28.28 24.2V30.536C28.28 31.0132 27.8932 31.4 27.416 31.4H11.864C11.3868 31.4 11 31.0132 11 30.536V17.864C11 17.3868 11.3868 17 11.864 17H27.416C27.8932 17 28.28 17.3868 28.28 17.864V24.2ZM28.28 24.2L35.5029 18.1809C36.0656 17.712 36.92 18.1121 36.92 18.8447V29.5554C36.92 30.2879 36.0656 30.6881 35.5029 30.2191L28.28 24.2Z"
|
||||
stroke="#FCFCFC"
|
||||
stroke-width="1.92"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path d="M35.5 10L8 37.5" stroke="#FCFCFC" stroke-width="2" stroke-linecap="round" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default CameraIcon
|
||||
14
src/components/Icons/ChatIcon.tsx
Normal file
14
src/components/Icons/ChatIcon.tsx
Normal file
@ -0,0 +1,14 @@
|
||||
import React from 'react'
|
||||
import { Props } from './Icons.type'
|
||||
|
||||
const ChatIcon = (props: Props) => (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width={24} height={23} viewBox="0 0 24 23" fill="none" {...props}>
|
||||
<path
|
||||
d="M1.33301 20.7111V3.72228C1.33301 2.49495 2.39429 1.5 3.70344 1.5H20.2965C21.6057 1.5 22.6669 2.49495 22.6669 3.72228V14.8337C22.6669 16.061 21.6057 17.056 20.2965 17.056H7.21316C6.49306 17.056 5.81201 17.3629 5.36216 17.89L2.59943 21.1275C2.1795 21.6196 1.33301 21.3413 1.33301 20.7111Z"
|
||||
fill={props?.fill}
|
||||
stroke={props?.stroke ?? 'white'}
|
||||
strokeWidth="3"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
export default ChatIcon
|
||||
5
src/components/Icons/Icons.type.ts
Normal file
5
src/components/Icons/Icons.type.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { SVGProps } from 'react'
|
||||
|
||||
export type Props = SVGProps<SVGSVGElement> & {
|
||||
enabled?: boolean
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
import * as React from 'react'
|
||||
import type { SVGProps } from 'react'
|
||||
const SvgLeaveIcon = (props: SVGProps<SVGSVGElement>) => (
|
||||
import React from 'react'
|
||||
import { Props } from './Icons.type'
|
||||
|
||||
const LeaveIcon = (props: Props) => (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width={16} height={16} fill="none" {...props}>
|
||||
<path
|
||||
fill="currentColor"
|
||||
@ -16,4 +17,5 @@ const SvgLeaveIcon = (props: SVGProps<SVGSVGElement>) => (
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
export default SvgLeaveIcon
|
||||
|
||||
export default LeaveIcon
|
||||
53
src/components/Icons/MicrophoneIcon.tsx
Normal file
53
src/components/Icons/MicrophoneIcon.tsx
Normal file
@ -0,0 +1,53 @@
|
||||
import React from 'react'
|
||||
import { Props } from './Icons.type'
|
||||
|
||||
const MicrophoneIcon = ({ enabled = true, ...props }: Props) => {
|
||||
return enabled ? (
|
||||
<svg width="21" height="29" viewBox="0 0 21 29" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||
<path
|
||||
d="M14.4468 5.03394C14.4468 2.80606 12.6407 1 10.4128 1C8.18496 1 6.37891 2.80606 6.37891 5.03394V13.1018C6.37891 15.3297 8.18496 17.1358 10.4128 17.1358C12.6407 17.1358 14.4468 15.3297 14.4468 13.1018V5.03394Z"
|
||||
stroke="white"
|
||||
stroke-width="1.92"
|
||||
/>
|
||||
<path
|
||||
d="M1 11.7573V13.1019C1 18.3003 5.21414 22.5144 10.4125 22.5144C15.6109 22.5144 19.8251 18.3003 19.8251 13.1019V11.7573"
|
||||
stroke="white"
|
||||
stroke-width="1.92"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M10.4128 22.5145V27.8931M10.4128 27.8931H6.37891M10.4128 27.8931H14.4468"
|
||||
stroke="white"
|
||||
stroke-width="1.92"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
) : (
|
||||
<svg width="30" height="31" viewBox="0 0 30 31" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||
<path
|
||||
d="M19.4468 7.03394C19.4468 4.80606 17.6407 3 15.4128 3C13.185 3 11.3789 4.80606 11.3789 7.03394V15.1018C11.3789 17.3297 13.185 19.1358 15.4128 19.1358C17.6407 19.1358 19.4468 17.3297 19.4468 15.1018V7.03394Z"
|
||||
stroke="white"
|
||||
stroke-width="1.92"
|
||||
/>
|
||||
<path
|
||||
d="M6 13.7573V15.1019C6 20.3003 10.2141 24.5144 15.4125 24.5144C20.6109 24.5144 24.8251 20.3003 24.8251 15.1019V13.7573"
|
||||
stroke="white"
|
||||
stroke-width="1.92"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M15.4128 24.5145V29.8931M15.4128 29.8931H11.3789M15.4128 29.8931H19.4468"
|
||||
stroke="white"
|
||||
stroke-width="1.92"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path d="M28.5 1L1 28.5" stroke="#FCFCFC" stroke-width="2" stroke-linecap="round" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default MicrophoneIcon
|
||||
26
src/components/Icons/PeopleIcon.tsx
Normal file
26
src/components/Icons/PeopleIcon.tsx
Normal file
@ -0,0 +1,26 @@
|
||||
import React from 'react'
|
||||
import { Props } from './Icons.type'
|
||||
|
||||
const PeopleIcon = (props: Props) => (
|
||||
<svg width="32" height="33" viewBox="0 0 32 33" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M24.0003 13.8333C24.0003 13.0987 23.403 12.5 22.667 12.5C21.931 12.5 21.3337 13.0987 21.3337 13.8333C21.3337 14.568 21.931 15.1667 22.667 15.1667C23.403 15.1667 24.0003 14.568 24.0003 13.8333ZM26.667 13.8333C26.667 16.0387 24.8723 17.8333 22.667 17.8333C20.4617 17.8333 18.667 16.0387 18.667 13.8333C18.667 11.628 20.4617 9.83333 22.667 9.83333C24.8723 9.83333 26.667 11.628 26.667 13.8333ZM14.667 9.83333C14.667 8.36267 13.471 7.16667 12.0003 7.16667C10.5297 7.16667 9.33366 8.36267 9.33366 9.83333C9.33366 11.304 10.5297 12.5 12.0003 12.5C13.471 12.5 14.667 11.304 14.667 9.83333ZM17.3337 9.83333C17.3337 12.7747 14.9417 15.1667 12.0003 15.1667C9.05899 15.1667 6.66699 12.7747 6.66699 9.83333C6.66699 6.892 9.05899 4.5 12.0003 4.5C14.9417 4.5 17.3337 6.892 17.3337 9.83333ZM18.587 20.5613C19.7457 19.6653 21.1723 19.1667 22.667 19.1667C26.343 19.1667 29.3337 22.1573 29.3337 25.8333C29.3337 26.5693 28.7377 27.1667 28.0003 27.1667C27.263 27.1667 26.667 26.5693 26.667 25.8333C26.667 23.628 24.8723 21.8333 22.667 21.8333C21.7563 21.8333 20.891 22.1453 20.1923 22.6987C20.9177 24.0267 21.3337 25.5493 21.3337 27.1667C21.3337 27.9027 20.7377 28.5 20.0003 28.5C19.263 28.5 18.667 27.9027 18.667 27.1667C18.667 23.4907 15.6763 20.5 12.0003 20.5C8.32433 20.5 5.33366 23.4907 5.33366 27.1667C5.33366 27.9027 4.73766 28.5 4.00033 28.5C3.26299 28.5 2.66699 27.9027 2.66699 27.1667C2.66699 22.02 6.85499 17.8333 12.0003 17.8333C14.5697 17.8333 16.8977 18.8773 18.587 20.5613Z"
|
||||
fill="#231F20"
|
||||
/>
|
||||
<mask id="mask0_283_1438" style={{ maskType: 'luminance' }} maskUnits="userSpaceOnUse" x="2" y="4" width="28" height="25">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M24.0003 13.8333C24.0003 13.0987 23.403 12.5 22.667 12.5C21.931 12.5 21.3337 13.0987 21.3337 13.8333C21.3337 14.568 21.931 15.1667 22.667 15.1667C23.403 15.1667 24.0003 14.568 24.0003 13.8333ZM26.667 13.8333C26.667 16.0387 24.8723 17.8333 22.667 17.8333C20.4617 17.8333 18.667 16.0387 18.667 13.8333C18.667 11.628 20.4617 9.83333 22.667 9.83333C24.8723 9.83333 26.667 11.628 26.667 13.8333ZM14.667 9.83333C14.667 8.36267 13.471 7.16667 12.0003 7.16667C10.5297 7.16667 9.33366 8.36267 9.33366 9.83333C9.33366 11.304 10.5297 12.5 12.0003 12.5C13.471 12.5 14.667 11.304 14.667 9.83333ZM17.3337 9.83333C17.3337 12.7747 14.9417 15.1667 12.0003 15.1667C9.05899 15.1667 6.66699 12.7747 6.66699 9.83333C6.66699 6.892 9.05899 4.5 12.0003 4.5C14.9417 4.5 17.3337 6.892 17.3337 9.83333ZM18.587 20.5613C19.7457 19.6653 21.1723 19.1667 22.667 19.1667C26.343 19.1667 29.3337 22.1573 29.3337 25.8333C29.3337 26.5693 28.7377 27.1667 28.0003 27.1667C27.263 27.1667 26.667 26.5693 26.667 25.8333C26.667 23.628 24.8723 21.8333 22.667 21.8333C21.7563 21.8333 20.891 22.1453 20.1923 22.6987C20.9177 24.0267 21.3337 25.5493 21.3337 27.1667C21.3337 27.9027 20.7377 28.5 20.0003 28.5C19.263 28.5 18.667 27.9027 18.667 27.1667C18.667 23.4907 15.6763 20.5 12.0003 20.5C8.32433 20.5 5.33366 23.4907 5.33366 27.1667C5.33366 27.9027 4.73766 28.5 4.00033 28.5C3.26299 28.5 2.66699 27.9027 2.66699 27.1667C2.66699 22.02 6.85499 17.8333 12.0003 17.8333C14.5697 17.8333 16.8977 18.8773 18.587 20.5613Z"
|
||||
fill={props?.fill ?? 'white'}
|
||||
/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_283_1438)">
|
||||
<rect y="0.5" width="32" height="32" fill={props?.fill ?? 'white'} />
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
|
||||
export default PeopleIcon
|
||||
30
src/components/Icons/PhoneIcon.tsx
Normal file
30
src/components/Icons/PhoneIcon.tsx
Normal file
@ -0,0 +1,30 @@
|
||||
import React from 'react'
|
||||
import { Props } from './Icons.type'
|
||||
|
||||
const PhoneIcon = (props: Props) => (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="14" viewBox="0 0 13 14" fill="none" {...props}>
|
||||
<path
|
||||
d="M1.59961 1.66504C1.59961 8.08588 5.60104 12.4595 12.208 12.4595"
|
||||
stroke={props?.stroke ?? 'white'}
|
||||
stroke-width="1.44"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M12.2082 11.9941V9.83621C12.2082 9.51588 11.9966 9.23408 11.689 9.14477L9.69381 8.56552C9.46247 8.49836 9.21287 8.55134 9.02876 8.70668L6.3457 10.9705"
|
||||
stroke={props?.stroke ?? 'white'}
|
||||
stroke-width="0.72"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M2.15576 1.66474L4.31332 1.62516C4.63359 1.61929 4.91924 1.82571 5.01418 2.13165L5.62992 4.11585C5.70131 4.34592 5.65292 4.59645 5.50098 4.78338L3.28675 7.50751"
|
||||
stroke={props?.stroke ?? 'white'}
|
||||
stroke-width="0.72"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
|
||||
export default PhoneIcon
|
||||
@ -1,7 +1,7 @@
|
||||
import * as React from 'react'
|
||||
import type { SVGProps } from 'react'
|
||||
import React from 'react'
|
||||
import { Props } from './Icons.type'
|
||||
|
||||
const SendIcon = (props: SVGProps<SVGSVGElement>) => (
|
||||
const SendIcon = (props: Props) => (
|
||||
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||
<path
|
||||
d="M25.7422 5.75146L14.0085 24.4085L12.2228 15.2179L4.19727 10.3964L25.7422 5.75146Z"
|
||||
30
src/components/Icons/ShareScreenIcon.tsx
Normal file
30
src/components/Icons/ShareScreenIcon.tsx
Normal file
@ -0,0 +1,30 @@
|
||||
import React from 'react'
|
||||
import { Props } from './Icons.type'
|
||||
|
||||
const ShareScreenIcon = ({ enabled = false, ...props }: Props) => {
|
||||
return enabled ? (
|
||||
<svg width="29" height="21" viewBox="0 0 29 21" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||
<rect x="0.96" y="0.96" width="27.08" height="19.08" rx="1.92" stroke="#1E1E1E" stroke-width="1.92" />
|
||||
<path
|
||||
d="M14.3471 15.2164V6.16M14.3471 6.16L18.6941 10.5071M14.3471 6.16L10 10.5071"
|
||||
stroke="#1E1E1E"
|
||||
stroke-width="1.92"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
) : (
|
||||
<svg width="29" height="21" viewBox="0 0 29 21" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||
<rect x="0.96" y="0.96" width="27.08" height="19.08" rx="1.92" stroke="white" stroke-width="1.92" />
|
||||
<path
|
||||
d="M14.3471 15.2163V6.15997M14.3471 6.15997L18.6941 10.507M14.3471 6.15997L10 10.507"
|
||||
stroke="white"
|
||||
stroke-width="1.92"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default ShareScreenIcon
|
||||
8
src/components/Icons/index.ts
Normal file
8
src/components/Icons/index.ts
Normal file
@ -0,0 +1,8 @@
|
||||
export { default as CameraIcon } from './CameraIcon'
|
||||
export { default as ChatIcon } from './ChatIcon'
|
||||
export { default as LeaveIcon } from './LeaveIcon'
|
||||
export { default as MicrophoneIcon } from './MicrophoneIcon'
|
||||
export { default as PeopleIcon } from './PeopleIcon'
|
||||
export { default as PhoneIcon } from './PhoneIcon'
|
||||
export { default as SendIcon } from './SendIcon'
|
||||
export { default as ShareScreenIcon } from './ShareScreenIcon'
|
||||
@ -1,7 +1,19 @@
|
||||
.ControlBarContainer {
|
||||
display: flex;
|
||||
position: relative;
|
||||
align-items: center;
|
||||
height: var(--lk-control-bar-height);
|
||||
border-top: none;
|
||||
margin-top: var(--lk-control-bar-margin-top);
|
||||
margin-bottom: var(--lk-control-bar-margin-bottom);
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.ControlBarCenterButtonGroup {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.ControlBarRightButtonGroup {
|
||||
@ -13,6 +25,110 @@
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.controlBarButtonGroup {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.controlBarButton:global(.lk-button) {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
background-color: #716b7c;
|
||||
border: 0;
|
||||
border-radius: 100%;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.controlBarButton:global(.lk-button):hover {
|
||||
background-color: #928ea4;
|
||||
}
|
||||
|
||||
.controlBarMenuButton:global(.lk-button-group-menu .lk-button.lk-button-menu) {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.controlBarMenuButton:global(.lk-button-group-menu .lk-button[aria-pressed='true'])::after {
|
||||
transform: rotate(135deg);
|
||||
}
|
||||
|
||||
.controlBarMenuButton:global(.lk-button-group-menu .lk-device-menu) {
|
||||
left: 10px !important;
|
||||
}
|
||||
|
||||
.disabled:global(.lk-button) {
|
||||
background-color: #ff2d55;
|
||||
}
|
||||
|
||||
.disabled:global(.lk-button):hover {
|
||||
background-color: #ff2d55;
|
||||
}
|
||||
|
||||
.screenShareEnabled:global(.lk-button) {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.screenShareEnabled:global(.lk-button):hover {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.stopSharingButton:global(.ui.button) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 10px;
|
||||
height: 30px;
|
||||
gap: 8px;
|
||||
width: 134px;
|
||||
background-color: #ffffff;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.stopSharingButton:global(.ui.button):hover {
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.stopSharingCloseButton {
|
||||
background-image: url('../../../assets/icons/Close.svg');
|
||||
background-size: contain;
|
||||
background-position: center;
|
||||
filter: invert(50%);
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border: 1px solid #a09ba8;
|
||||
}
|
||||
|
||||
.microphoneButtonGroup {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.chatToggleButton {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.chatToggleIcon {
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
}
|
||||
|
||||
.disconnectButton {
|
||||
margin-left: 61px;
|
||||
display: flex;
|
||||
height: 45px;
|
||||
padding: 14px 44px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
background-color: #ff2d55;
|
||||
border-radius: 6px;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
line-height: normal;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useMemo, useRef, useState } from 'react'
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { supportsScreenSharing } from '@livekit/components-core'
|
||||
import {
|
||||
ChatToggle,
|
||||
@ -7,15 +7,18 @@ import {
|
||||
StartAudio,
|
||||
TrackToggle,
|
||||
useLocalParticipantPermissions,
|
||||
useRoomContext
|
||||
useRoomContext,
|
||||
useTrackToggle
|
||||
} from '@livekit/components-react'
|
||||
import classNames from 'classnames'
|
||||
import { LocalAudioTrack, LocalVideoTrack, Track } from 'livekit-client'
|
||||
import ChatIcon from '../../../assets/icons/ChatIcon'
|
||||
import LeaveIcon from '../../../assets/icons/LeaveIcon'
|
||||
import { t } from 'decentraland-dapps/dist/modules/translation/utils'
|
||||
import { Button, Popup } from 'decentraland-ui'
|
||||
import { useLayoutContext } from '../../../hooks/useLayoutContext'
|
||||
import { useMediaQuery } from '../../../hooks/useMediaQuery'
|
||||
import { usePreviewTracks } from '../../../hooks/usePreviewTracks'
|
||||
import { mergeProps } from '../../../utils/mergeProps'
|
||||
import { CameraIcon, ChatIcon, MicrophoneIcon, PhoneIcon, ShareScreenIcon } from '../../Icons'
|
||||
import PeoplePanelToggleButton from './PeoplePanelToggleButton'
|
||||
import { ControlBarProps, DEFAULT_USER_CHOICES } from './ControlBar.types'
|
||||
import styles from './ControlBar.module.css'
|
||||
@ -38,6 +41,7 @@ import styles from './ControlBar.module.css'
|
||||
*/
|
||||
export function ControlBar({ variation, controls, ...props }: ControlBarProps) {
|
||||
const [isChatOpen, setIsChatOpen] = useState(false)
|
||||
const [confirmStopSharing, setConfirmStopSharing] = useState(false)
|
||||
const layoutContext = useLayoutContext()
|
||||
const {
|
||||
options: { videoCaptureDefaults, audioCaptureDefaults }
|
||||
@ -56,6 +60,7 @@ export function ControlBar({ variation, controls, ...props }: ControlBarProps) {
|
||||
visibleControls.chat = false
|
||||
visibleControls.microphone = false
|
||||
visibleControls.screenShare = false
|
||||
visibleControls.peoplePanel = false
|
||||
} else {
|
||||
visibleControls.camera ??= localPermissions.canPublish
|
||||
visibleControls.microphone ??= localPermissions.canPublish
|
||||
@ -68,13 +73,12 @@ export function ControlBar({ variation, controls, ...props }: ControlBarProps) {
|
||||
|
||||
const browserSupportsScreenSharing = supportsScreenSharing()
|
||||
|
||||
const [isScreenShareEnabled, setIsScreenShareEnabled] = useState(false)
|
||||
const handleStopScreenShare = useCallback(() => {
|
||||
toggleScreenShare()
|
||||
setConfirmStopSharing(false)
|
||||
}, [])
|
||||
|
||||
const onScreenShareChange = (enabled: boolean) => {
|
||||
setIsScreenShareEnabled(enabled)
|
||||
}
|
||||
|
||||
const htmlProps = mergeProps({ className: `lk-control-bar ${styles.ControlBarContainer}` }, props)
|
||||
const htmlProps = mergeProps({ className: styles.ControlBarContainer }, props)
|
||||
|
||||
const [videoEnabled, setVideoEnabled] = useState<boolean>(visibleControls.camera ?? DEFAULT_USER_CHOICES.videoEnabled)
|
||||
const initialVideoDeviceId = (videoCaptureDefaults?.deviceId as string) ?? DEFAULT_USER_CHOICES.videoDeviceId
|
||||
@ -83,6 +87,23 @@ export function ControlBar({ variation, controls, ...props }: ControlBarProps) {
|
||||
const [audioEnabled, setAudioEnabled] = useState<boolean>(visibleControls.microphone ?? DEFAULT_USER_CHOICES.audioEnabled)
|
||||
const [audioDeviceId, setAudioDeviceId] = useState<string>(initialAudioDeviceId)
|
||||
|
||||
const { enabled: isScreenShareEnabled, toggle: toggleScreenShare } = useTrackToggle({
|
||||
source: Track.Source.ScreenShare,
|
||||
captureOptions: { audio: true, selfBrowserSurface: 'include' }
|
||||
})
|
||||
|
||||
const handleOpen = () => {
|
||||
if (!isScreenShareEnabled) {
|
||||
toggleScreenShare()
|
||||
} else {
|
||||
setConfirmStopSharing(true)
|
||||
}
|
||||
}
|
||||
|
||||
const handleClose = () => {
|
||||
setConfirmStopSharing(false)
|
||||
}
|
||||
|
||||
const tracks = usePreviewTracks(
|
||||
{
|
||||
audio: audioEnabled ? { deviceId: initialAudioDeviceId } : false,
|
||||
@ -115,69 +136,89 @@ export function ControlBar({ variation, controls, ...props }: ControlBarProps) {
|
||||
|
||||
return (
|
||||
<div {...htmlProps}>
|
||||
{visibleControls.microphone && (
|
||||
<div className="lk-button-group">
|
||||
<TrackToggle
|
||||
source={Track.Source.Microphone}
|
||||
showIcon={showIcon}
|
||||
initialState={audioEnabled}
|
||||
onChange={enabled => setAudioEnabled(enabled)}
|
||||
>
|
||||
{showText && 'Microphone'}
|
||||
</TrackToggle>
|
||||
<div className="lk-button-group-menu">
|
||||
<MediaDeviceMenu
|
||||
initialSelection={audioDeviceId}
|
||||
kind="audioinput"
|
||||
disabled={!audioTrack}
|
||||
tracks={{ audioinput: audioTrack }}
|
||||
onActiveDeviceChange={(_, id) => setAudioDeviceId(id)}
|
||||
/>
|
||||
<div className={styles.ControlBarCenterButtonGroup}>
|
||||
{visibleControls.microphone && (
|
||||
<div className={styles.controlBarButtonGroup}>
|
||||
<TrackToggle
|
||||
className={classNames(styles.controlBarButton, { [styles.disabled]: !audioEnabled })}
|
||||
source={Track.Source.Microphone}
|
||||
showIcon={false}
|
||||
initialState={audioEnabled}
|
||||
onChange={enabled => setAudioEnabled(enabled)}
|
||||
>
|
||||
<MicrophoneIcon enabled={audioEnabled} />
|
||||
</TrackToggle>
|
||||
<div className={`lk-button-group-menu ${styles.controlBarMenuButton}`}>
|
||||
<MediaDeviceMenu
|
||||
initialSelection={audioDeviceId}
|
||||
kind="audioinput"
|
||||
disabled={!audioTrack}
|
||||
tracks={{ audioinput: audioTrack }}
|
||||
onActiveDeviceChange={(_, id) => setAudioDeviceId(id)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{visibleControls.camera && (
|
||||
<div className="lk-button-group">
|
||||
<TrackToggle
|
||||
source={Track.Source.Camera}
|
||||
showIcon={showIcon}
|
||||
initialState={videoEnabled}
|
||||
onChange={enabled => setVideoEnabled(enabled)}
|
||||
>
|
||||
{showText && 'Camera'}
|
||||
</TrackToggle>
|
||||
<div className="lk-button-group-menu">
|
||||
<MediaDeviceMenu
|
||||
kind="videoinput"
|
||||
initialSelection={videoDeviceId}
|
||||
disabled={!videoTrack}
|
||||
tracks={{ videoinput: videoTrack }}
|
||||
onActiveDeviceChange={(_, id) => setVideoDeviceId(id)}
|
||||
/>
|
||||
)}
|
||||
{visibleControls.camera && (
|
||||
<div className={styles.controlBarButtonGroup}>
|
||||
<TrackToggle
|
||||
className={classNames(styles.controlBarButton, { [styles.disabled]: !videoEnabled })}
|
||||
source={Track.Source.Camera}
|
||||
showIcon={false}
|
||||
initialState={videoEnabled}
|
||||
onChange={enabled => setVideoEnabled(enabled)}
|
||||
>
|
||||
<CameraIcon enabled={videoEnabled} />
|
||||
</TrackToggle>
|
||||
<div className={`lk-button-group-menu ${styles.controlBarMenuButton}`}>
|
||||
<MediaDeviceMenu
|
||||
kind="videoinput"
|
||||
initialSelection={videoDeviceId}
|
||||
disabled={!videoTrack}
|
||||
tracks={{ videoinput: videoTrack }}
|
||||
onActiveDeviceChange={(_, id) => setVideoDeviceId(id)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{visibleControls.screenShare && browserSupportsScreenSharing && (
|
||||
<TrackToggle
|
||||
source={Track.Source.ScreenShare}
|
||||
captureOptions={{ audio: true, selfBrowserSurface: 'include' }}
|
||||
showIcon={showIcon}
|
||||
onChange={onScreenShareChange}
|
||||
>
|
||||
{showText && (isScreenShareEnabled ? 'Stop screen share' : 'Share screen')}
|
||||
</TrackToggle>
|
||||
)}
|
||||
{visibleControls.leave && (
|
||||
<DisconnectButton>
|
||||
{showIcon && <LeaveIcon />}
|
||||
{showText && 'Leave'}
|
||||
</DisconnectButton>
|
||||
)}
|
||||
)}
|
||||
{visibleControls.screenShare && browserSupportsScreenSharing && (
|
||||
<Popup
|
||||
position="top left"
|
||||
open={confirmStopSharing}
|
||||
on="click"
|
||||
closeOnTriggerBlur
|
||||
onOpen={handleOpen}
|
||||
onClose={handleClose}
|
||||
content={
|
||||
<Button className={styles.stopSharingButton} onClick={handleStopScreenShare}>
|
||||
<div className={styles.stopSharingCloseButton} />
|
||||
{t('control_bar.stop_sharing')}
|
||||
</Button>
|
||||
}
|
||||
trigger={
|
||||
<button className={classNames('lk-button', styles.controlBarButton, { [styles.screenShareEnabled]: isScreenShareEnabled })}>
|
||||
<ShareScreenIcon enabled={isScreenShareEnabled} />
|
||||
</button>
|
||||
}
|
||||
basic
|
||||
style={{
|
||||
backgroundColor: 'transparent',
|
||||
padding: 0
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{visibleControls.leave && (
|
||||
<DisconnectButton className={styles.disconnectButton}>
|
||||
{showIcon && <PhoneIcon />}
|
||||
{t('control_bar.leave')}
|
||||
</DisconnectButton>
|
||||
)}
|
||||
</div>
|
||||
<StartAudio label="Start Audio" />
|
||||
<div className={styles.ControlBarRightButtonGroup}>
|
||||
{visibleControls.chat && (
|
||||
<ChatToggle className={styles.chatToggleButton}>
|
||||
{showIcon && <ChatIcon />}
|
||||
{showIcon && <ChatIcon className={styles.chatToggleIcon} />}
|
||||
{showText && 'Chat'}
|
||||
</ChatToggle>
|
||||
)}
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
.button {
|
||||
padding: 8px;
|
||||
height: 48px;
|
||||
width: 48px;
|
||||
background-color: transparent;
|
||||
padding: 0.625rem 1rem;
|
||||
}
|
||||
|
||||
.buttonActive {
|
||||
@ -8,8 +10,8 @@
|
||||
}
|
||||
|
||||
.peopleIcon {
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
}
|
||||
|
||||
.label {
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import React, { useCallback, useMemo } from 'react'
|
||||
import classNames from 'classnames'
|
||||
import { Label } from 'decentraland-ui'
|
||||
import PeopleIcon from '../../../../assets/icons/PeopleIcon'
|
||||
import { WidgetState, useLayoutContext } from '../../../../hooks/useLayoutContext'
|
||||
import { PeopleIcon } from '../../../Icons'
|
||||
import type { Props } from './PeoplePanelToggleButton.types'
|
||||
import styles from './PeoplePanelToggleButton.module.css'
|
||||
|
||||
|
||||
@ -4,10 +4,10 @@ import { useLocalParticipant } from '@livekit/components-react'
|
||||
import classNames from 'classnames'
|
||||
import { t } from 'decentraland-dapps/dist/modules/translation/utils'
|
||||
import { Button, Header, Input } from 'decentraland-ui'
|
||||
import SendIcon from '../../../../assets/icons/SendIcon'
|
||||
import { useChat } from '../../../../hooks/useChat'
|
||||
import { useLayoutContext } from '../../../../hooks/useLayoutContext'
|
||||
import { cloneSingleChild } from '../../../../utils/chat'
|
||||
import { SendIcon } from '../../../Icons'
|
||||
import ChatEntry from './ChatEntry'
|
||||
import { Props } from './Chat.types'
|
||||
import styles from './Chat.module.css'
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
.LayoutWrapper {
|
||||
flex-direction: row;
|
||||
padding: 8px 0px;
|
||||
height: calc(100% - (var(--lk-control-bar-height) + var(--lk-control-bar-margin-top) + var(--lk-control-bar-margin-bottom)));
|
||||
}
|
||||
|
||||
.GridLayout {
|
||||
|
||||
@ -139,5 +139,7 @@ code {
|
||||
}
|
||||
|
||||
[data-lk-theme='default'] {
|
||||
--lk-control-bar-height: 44px;
|
||||
--lk-control-bar-height: 48px;
|
||||
--lk-control-bar-margin-top: 18px;
|
||||
--lk-control-bar-margin-bottom: 26px;
|
||||
}
|
||||
|
||||
@ -20,5 +20,9 @@
|
||||
},
|
||||
"chat_entry": {
|
||||
"you": "You"
|
||||
},
|
||||
"control_bar": {
|
||||
"leave": "Leave",
|
||||
"stop_sharing": "Stop sharing"
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,5 +20,9 @@
|
||||
},
|
||||
"chat_entry": {
|
||||
"you": "Tu"
|
||||
},
|
||||
"control_bar": {
|
||||
"leave": "Salir",
|
||||
"stop_sharing": "Deja de compartir"
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,5 +20,9 @@
|
||||
},
|
||||
"chat_entry": {
|
||||
"you": "你"
|
||||
},
|
||||
"control_bar": {
|
||||
"leave": "离开",
|
||||
"stop_sharing": "停止共享"
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user