2022-05-04 17:09:48 +01:00
|
|
|
/*
|
|
|
|
Copyright 2022 Matrix.org Foundation C.I.C.
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
*/
|
2022-08-08 20:05:44 +02:00
|
|
|
import React, { forwardRef, useCallback } from "react";
|
2022-06-11 23:21:20 +02:00
|
|
|
import { PressEvent } from "@react-types/shared";
|
2021-12-07 11:59:57 -08:00
|
|
|
import classNames from "classnames";
|
2022-06-11 23:21:20 +02:00
|
|
|
import { useButton } from "@react-aria/button";
|
|
|
|
import { mergeProps, useObjectRef } from "@react-aria/utils";
|
2022-10-10 09:19:10 -04:00
|
|
|
import { useTranslation } from "react-i18next";
|
2022-06-11 23:21:20 +02:00
|
|
|
|
2021-12-07 11:59:57 -08:00
|
|
|
import styles from "./Button.module.css";
|
|
|
|
import { ReactComponent as MicIcon } from "../icons/Mic.svg";
|
|
|
|
import { ReactComponent as MuteMicIcon } from "../icons/MuteMic.svg";
|
|
|
|
import { ReactComponent as VideoIcon } from "../icons/Video.svg";
|
|
|
|
import { ReactComponent as DisableVideoIcon } from "../icons/DisableVideo.svg";
|
|
|
|
import { ReactComponent as HangupIcon } from "../icons/Hangup.svg";
|
|
|
|
import { ReactComponent as ScreenshareIcon } from "../icons/Screenshare.svg";
|
2022-04-27 16:47:23 -07:00
|
|
|
import { ReactComponent as SettingsIcon } from "../icons/Settings.svg";
|
|
|
|
import { ReactComponent as AddUserIcon } from "../icons/AddUser.svg";
|
2022-05-26 13:52:06 -04:00
|
|
|
import { ReactComponent as ArrowDownIcon } from "../icons/ArrowDown.svg";
|
2022-08-07 19:04:59 +02:00
|
|
|
import { ReactComponent as Fullscreen } from "../icons/Fullscreen.svg";
|
|
|
|
import { ReactComponent as FullscreenExit } from "../icons/FullscreenExit.svg";
|
2022-02-04 12:31:59 -08:00
|
|
|
import { TooltipTrigger } from "../Tooltip";
|
2022-08-02 14:30:12 +02:00
|
|
|
import { VolumeIcon } from "./VolumeIcon";
|
2021-12-07 11:59:57 -08:00
|
|
|
|
2022-07-02 21:20:53 +02:00
|
|
|
export type ButtonVariant =
|
|
|
|
| "default"
|
|
|
|
| "toolbar"
|
|
|
|
| "toolbarSecondary"
|
|
|
|
| "icon"
|
|
|
|
| "secondary"
|
|
|
|
| "copy"
|
|
|
|
| "secondaryCopy"
|
|
|
|
| "iconCopy"
|
|
|
|
| "secondaryHangup"
|
|
|
|
| "dropdown"
|
|
|
|
| "link";
|
|
|
|
|
2021-12-14 16:12:58 -08:00
|
|
|
export const variantToClassName = {
|
2021-12-07 11:59:57 -08:00
|
|
|
default: [styles.button],
|
|
|
|
toolbar: [styles.toolbarButton],
|
2022-02-15 12:58:55 -08:00
|
|
|
toolbarSecondary: [styles.toolbarButtonSecondary],
|
2021-12-07 11:59:57 -08:00
|
|
|
icon: [styles.iconButton],
|
2021-12-13 14:54:44 -08:00
|
|
|
secondary: [styles.secondary],
|
2021-12-07 11:59:57 -08:00
|
|
|
copy: [styles.copyButton],
|
2022-06-02 16:30:35 -04:00
|
|
|
secondaryCopy: [styles.secondaryCopy, styles.copyButton],
|
2021-12-13 12:39:29 -08:00
|
|
|
iconCopy: [styles.iconCopyButton],
|
2022-04-22 18:05:48 -07:00
|
|
|
secondaryHangup: [styles.secondaryHangup],
|
2022-05-26 13:52:06 -04:00
|
|
|
dropdown: [styles.dropdownButton],
|
2022-06-09 21:56:58 +01:00
|
|
|
link: [styles.linkButton],
|
2021-12-07 11:59:57 -08:00
|
|
|
};
|
|
|
|
|
2022-07-02 21:20:53 +02:00
|
|
|
export type ButtonSize = "lg";
|
|
|
|
|
|
|
|
export const sizeToClassName: { lg: string[] } = {
|
2021-12-14 16:12:58 -08:00
|
|
|
lg: [styles.lg],
|
|
|
|
};
|
2022-06-11 23:21:20 +02:00
|
|
|
interface Props {
|
2022-07-02 21:20:53 +02:00
|
|
|
variant: ButtonVariant;
|
|
|
|
size: ButtonSize;
|
2022-06-11 23:21:20 +02:00
|
|
|
on: () => void;
|
|
|
|
off: () => void;
|
|
|
|
iconStyle: string;
|
|
|
|
className: string;
|
|
|
|
children: Element[];
|
|
|
|
onPress: (e: PressEvent) => void;
|
|
|
|
onPressStart: (e: PressEvent) => void;
|
2022-08-02 00:46:16 +02:00
|
|
|
// TODO: add all props for <Button>
|
2022-06-11 23:21:20 +02:00
|
|
|
[index: string]: unknown;
|
|
|
|
}
|
2022-07-05 17:44:09 +02:00
|
|
|
export const Button = forwardRef<HTMLButtonElement, Props>(
|
2021-12-07 17:59:55 -08:00
|
|
|
(
|
2021-12-14 16:12:58 -08:00
|
|
|
{
|
|
|
|
variant = "default",
|
|
|
|
size,
|
|
|
|
on,
|
|
|
|
off,
|
|
|
|
iconStyle,
|
|
|
|
className,
|
|
|
|
children,
|
2022-01-18 14:25:02 -08:00
|
|
|
onPress,
|
|
|
|
onPressStart,
|
2021-12-14 16:12:58 -08:00
|
|
|
...rest
|
|
|
|
},
|
2021-12-07 17:59:55 -08:00
|
|
|
ref
|
|
|
|
) => {
|
2022-07-05 17:44:09 +02:00
|
|
|
const buttonRef = useObjectRef<HTMLButtonElement>(ref);
|
2022-01-18 14:25:02 -08:00
|
|
|
const { buttonProps } = useButton(
|
|
|
|
{ onPress, onPressStart, ...rest },
|
|
|
|
buttonRef
|
|
|
|
);
|
2021-12-07 11:59:57 -08:00
|
|
|
|
|
|
|
// TODO: react-aria's useButton hook prevents form submission via keyboard
|
|
|
|
// Remove the hack below after this is merged https://github.com/adobe/react-spectrum/pull/904
|
|
|
|
let filteredButtonProps = buttonProps;
|
|
|
|
|
|
|
|
if (rest.type === "submit" && !rest.onPress) {
|
2022-06-11 23:21:20 +02:00
|
|
|
const { ...filtered } = buttonProps;
|
2021-12-07 11:59:57 -08:00
|
|
|
filteredButtonProps = filtered;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
|
|
|
<button
|
2021-12-07 17:59:55 -08:00
|
|
|
className={classNames(
|
|
|
|
variantToClassName[variant],
|
2021-12-14 16:12:58 -08:00
|
|
|
sizeToClassName[size],
|
2021-12-07 17:59:55 -08:00
|
|
|
styles[iconStyle],
|
|
|
|
className,
|
|
|
|
{
|
|
|
|
[styles.on]: on,
|
|
|
|
[styles.off]: off,
|
|
|
|
}
|
|
|
|
)}
|
2021-12-23 14:40:23 -08:00
|
|
|
{...mergeProps(rest, filteredButtonProps)}
|
2021-12-07 11:59:57 -08:00
|
|
|
ref={buttonRef}
|
|
|
|
>
|
2022-07-08 14:56:00 +01:00
|
|
|
<>
|
|
|
|
{children}
|
|
|
|
{variant === "dropdown" && <ArrowDownIcon />}
|
|
|
|
</>
|
2021-12-07 11:59:57 -08:00
|
|
|
</button>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
2022-07-02 21:20:53 +02:00
|
|
|
export function MicButton({
|
|
|
|
muted,
|
|
|
|
...rest
|
|
|
|
}: {
|
|
|
|
muted: boolean;
|
2022-08-02 00:46:16 +02:00
|
|
|
// TODO: add all props for <Button>
|
2022-07-02 21:20:53 +02:00
|
|
|
[index: string]: unknown;
|
|
|
|
}) {
|
2022-10-10 09:19:10 -04:00
|
|
|
const { t } = useTranslation();
|
|
|
|
|
2021-12-07 11:59:57 -08:00
|
|
|
return (
|
2022-07-30 10:06:28 +02:00
|
|
|
<TooltipTrigger
|
2022-10-10 09:19:10 -04:00
|
|
|
tooltip={() => (muted ? t("Unmute microphone") : t("Mute microphone"))}
|
2022-07-30 10:06:28 +02:00
|
|
|
>
|
2021-12-14 22:00:00 -08:00
|
|
|
<Button variant="toolbar" {...rest} off={muted}>
|
|
|
|
{muted ? <MuteMicIcon /> : <MicIcon />}
|
|
|
|
</Button>
|
|
|
|
</TooltipTrigger>
|
2021-12-07 11:59:57 -08:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-07-02 21:20:53 +02:00
|
|
|
export function VideoButton({
|
|
|
|
muted,
|
|
|
|
...rest
|
|
|
|
}: {
|
|
|
|
muted: boolean;
|
2022-08-02 00:46:16 +02:00
|
|
|
// TODO: add all props for <Button>
|
2022-07-02 21:20:53 +02:00
|
|
|
[index: string]: unknown;
|
|
|
|
}) {
|
2022-10-10 09:19:10 -04:00
|
|
|
const { t } = useTranslation();
|
|
|
|
|
2021-12-07 11:59:57 -08:00
|
|
|
return (
|
2022-07-30 10:06:28 +02:00
|
|
|
<TooltipTrigger
|
2022-10-10 09:19:10 -04:00
|
|
|
tooltip={() => (muted ? t("Turn on camera") : t("Turn off camera"))}
|
2022-07-30 10:06:28 +02:00
|
|
|
>
|
2021-12-14 22:00:00 -08:00
|
|
|
<Button variant="toolbar" {...rest} off={muted}>
|
|
|
|
{muted ? <DisableVideoIcon /> : <VideoIcon />}
|
|
|
|
</Button>
|
|
|
|
</TooltipTrigger>
|
2021-12-07 11:59:57 -08:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-07-02 21:20:53 +02:00
|
|
|
export function ScreenshareButton({
|
|
|
|
enabled,
|
|
|
|
className,
|
|
|
|
...rest
|
|
|
|
}: {
|
|
|
|
enabled: boolean;
|
2022-07-02 21:45:31 +02:00
|
|
|
className?: string;
|
2022-08-02 00:46:16 +02:00
|
|
|
// TODO: add all props for <Button>
|
2022-07-02 21:20:53 +02:00
|
|
|
[index: string]: unknown;
|
|
|
|
}) {
|
2022-10-10 09:19:10 -04:00
|
|
|
const { t } = useTranslation();
|
|
|
|
|
2021-12-07 11:59:57 -08:00
|
|
|
return (
|
2022-07-30 10:06:28 +02:00
|
|
|
<TooltipTrigger
|
2022-10-10 09:19:10 -04:00
|
|
|
tooltip={() => (enabled ? t("Stop sharing screen") : t("Share screen"))}
|
2022-07-30 10:06:28 +02:00
|
|
|
>
|
2022-02-15 12:58:55 -08:00
|
|
|
<Button variant="toolbarSecondary" {...rest} on={enabled}>
|
2021-12-14 22:00:00 -08:00
|
|
|
<ScreenshareIcon />
|
|
|
|
</Button>
|
|
|
|
</TooltipTrigger>
|
2021-12-07 11:59:57 -08:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-07-02 21:20:53 +02:00
|
|
|
export function HangupButton({
|
|
|
|
className,
|
|
|
|
...rest
|
|
|
|
}: {
|
2022-07-02 21:45:31 +02:00
|
|
|
className?: string;
|
2022-08-02 00:46:16 +02:00
|
|
|
// TODO: add all props for <Button>
|
2022-07-02 21:20:53 +02:00
|
|
|
[index: string]: unknown;
|
|
|
|
}) {
|
2022-10-10 09:19:10 -04:00
|
|
|
const { t } = useTranslation();
|
|
|
|
const tooltip = useCallback(() => t("Leave"), [t]);
|
|
|
|
|
2021-12-07 11:59:57 -08:00
|
|
|
return (
|
2022-10-10 09:19:10 -04:00
|
|
|
<TooltipTrigger tooltip={tooltip}>
|
2021-12-14 22:00:00 -08:00
|
|
|
<Button
|
|
|
|
variant="toolbar"
|
|
|
|
className={classNames(styles.hangupButton, className)}
|
|
|
|
{...rest}
|
|
|
|
>
|
|
|
|
<HangupIcon />
|
|
|
|
</Button>
|
|
|
|
</TooltipTrigger>
|
2021-12-07 11:59:57 -08:00
|
|
|
);
|
|
|
|
}
|
2022-04-27 16:47:23 -07:00
|
|
|
|
2022-07-02 21:20:53 +02:00
|
|
|
export function SettingsButton({
|
|
|
|
className,
|
|
|
|
...rest
|
|
|
|
}: {
|
2022-07-02 21:45:31 +02:00
|
|
|
className?: string;
|
2022-08-02 00:46:16 +02:00
|
|
|
// TODO: add all props for <Button>
|
2022-07-02 21:20:53 +02:00
|
|
|
[index: string]: unknown;
|
|
|
|
}) {
|
2022-10-10 09:19:10 -04:00
|
|
|
const { t } = useTranslation();
|
|
|
|
const tooltip = useCallback(() => t("Settings"), [t]);
|
|
|
|
|
2022-04-27 16:47:23 -07:00
|
|
|
return (
|
2022-10-10 09:19:10 -04:00
|
|
|
<TooltipTrigger tooltip={tooltip}>
|
2022-04-27 16:47:23 -07:00
|
|
|
<Button variant="toolbar" {...rest}>
|
|
|
|
<SettingsIcon />
|
|
|
|
</Button>
|
|
|
|
</TooltipTrigger>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-07-02 21:20:53 +02:00
|
|
|
export function InviteButton({
|
|
|
|
className,
|
|
|
|
...rest
|
|
|
|
}: {
|
2022-07-02 21:45:31 +02:00
|
|
|
className?: string;
|
2022-08-02 00:46:16 +02:00
|
|
|
// TODO: add all props for <Button>
|
2022-07-02 21:20:53 +02:00
|
|
|
[index: string]: unknown;
|
|
|
|
}) {
|
2022-10-10 09:19:10 -04:00
|
|
|
const { t } = useTranslation();
|
|
|
|
const tooltip = useCallback(() => t("Invite"), [t]);
|
|
|
|
|
2022-04-27 16:47:23 -07:00
|
|
|
return (
|
2022-10-10 09:19:10 -04:00
|
|
|
<TooltipTrigger tooltip={tooltip}>
|
2022-04-27 16:47:23 -07:00
|
|
|
<Button variant="toolbar" {...rest}>
|
|
|
|
<AddUserIcon />
|
|
|
|
</Button>
|
|
|
|
</TooltipTrigger>
|
|
|
|
);
|
|
|
|
}
|
2022-07-15 11:18:56 +02:00
|
|
|
|
2022-08-02 14:30:12 +02:00
|
|
|
interface AudioButtonProps extends Omit<Props, "variant"> {
|
|
|
|
/**
|
|
|
|
* A number between 0 and 1
|
|
|
|
*/
|
|
|
|
volume: number;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function AudioButton({ volume, ...rest }: AudioButtonProps) {
|
2022-10-10 09:19:10 -04:00
|
|
|
const { t } = useTranslation();
|
|
|
|
const tooltip = useCallback(() => t("Local volume"), [t]);
|
|
|
|
|
2022-07-15 11:18:56 +02:00
|
|
|
return (
|
2022-10-10 09:19:10 -04:00
|
|
|
<TooltipTrigger tooltip={tooltip}>
|
2022-08-02 14:30:12 +02:00
|
|
|
<Button variant="icon" {...rest}>
|
|
|
|
<VolumeIcon volume={volume} />
|
2022-07-15 11:18:56 +02:00
|
|
|
</Button>
|
|
|
|
</TooltipTrigger>
|
|
|
|
);
|
|
|
|
}
|
2022-08-07 19:04:59 +02:00
|
|
|
|
|
|
|
interface FullscreenButtonProps extends Omit<Props, "variant"> {
|
|
|
|
fullscreen?: boolean;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function FullscreenButton({
|
|
|
|
fullscreen,
|
|
|
|
...rest
|
|
|
|
}: FullscreenButtonProps) {
|
2022-10-10 09:19:10 -04:00
|
|
|
const { t } = useTranslation();
|
|
|
|
const tooltip = useCallback(() => {
|
|
|
|
return fullscreen ? t("Exit full screen") : t("Full screen");
|
|
|
|
}, [fullscreen, t]);
|
2022-08-08 20:05:44 +02:00
|
|
|
|
2022-08-07 19:04:59 +02:00
|
|
|
return (
|
2022-10-10 09:19:10 -04:00
|
|
|
<TooltipTrigger tooltip={tooltip}>
|
2022-08-07 19:04:59 +02:00
|
|
|
<Button variant="icon" {...rest}>
|
|
|
|
{fullscreen ? <FullscreenExit /> : <Fullscreen />}
|
|
|
|
</Button>
|
|
|
|
</TooltipTrigger>
|
|
|
|
);
|
|
|
|
}
|