Refactor header
This commit is contained in:
parent
87e5cafb77
commit
eb620e9220
16 changed files with 307 additions and 130 deletions
|
@ -1,11 +0,0 @@
|
||||||
import "../src/index.css";
|
|
||||||
|
|
||||||
export const parameters = {
|
|
||||||
actions: { argTypesRegex: "^on[A-Z].*" },
|
|
||||||
controls: {
|
|
||||||
matchers: {
|
|
||||||
color: /(background|color)$/i,
|
|
||||||
date: /Date$/,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
25
.storybook/preview.jsx
Normal file
25
.storybook/preview.jsx
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import React from "react";
|
||||||
|
import { addDecorator } from "@storybook/react";
|
||||||
|
import { MemoryRouter } from "react-router-dom";
|
||||||
|
import { usePageFocusStyle } from "../src/usePageFocusStyle";
|
||||||
|
import { OverlayProvider } from "@react-aria/overlays";
|
||||||
|
import "../src/index.css";
|
||||||
|
|
||||||
|
export const parameters = {
|
||||||
|
actions: { argTypesRegex: "^on[A-Z].*" },
|
||||||
|
controls: {
|
||||||
|
matchers: {
|
||||||
|
color: /(background|color)$/i,
|
||||||
|
date: /Date$/,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
addDecorator((story) => {
|
||||||
|
usePageFocusStyle();
|
||||||
|
return (
|
||||||
|
<MemoryRouter initialEntries={["/"]}>
|
||||||
|
<OverlayProvider>{story()}</OverlayProvider>
|
||||||
|
</MemoryRouter>
|
||||||
|
);
|
||||||
|
});
|
21
src/App.jsx
21
src/App.jsx
|
@ -32,29 +32,12 @@ import {
|
||||||
ClientProvider,
|
ClientProvider,
|
||||||
defaultHomeserverHost,
|
defaultHomeserverHost,
|
||||||
} from "./ConferenceCallManagerHooks";
|
} from "./ConferenceCallManagerHooks";
|
||||||
import { useFocusVisible } from "@react-aria/interactions";
|
|
||||||
import styles from "./App.module.css";
|
|
||||||
import { LoadingView } from "./FullScreenView";
|
import { LoadingView } from "./FullScreenView";
|
||||||
|
import { usePageFocusStyle } from "./usePageFocusStyle";
|
||||||
const SentryRoute = Sentry.withSentryRouting(Route);
|
const SentryRoute = Sentry.withSentryRouting(Route);
|
||||||
|
|
||||||
export default function App({ history }) {
|
export default function App({ history }) {
|
||||||
const { isFocusVisible } = useFocusVisible();
|
usePageFocusStyle();
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const classList = document.body.classList;
|
|
||||||
const hasClass = classList.contains(styles.hideFocus);
|
|
||||||
|
|
||||||
if (isFocusVisible && hasClass) {
|
|
||||||
classList.remove(styles.hideFocus);
|
|
||||||
} else if (!isFocusVisible && !hasClass) {
|
|
||||||
classList.add(styles.hideFocus);
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
classList.remove(styles.hideFocus);
|
|
||||||
};
|
|
||||||
}, [isFocusVisible]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Router history={history}>
|
<Router history={history}>
|
||||||
|
|
|
@ -15,10 +15,15 @@ export function Header({ children, className, ...rest }) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function LeftNav({ children, className, ...rest }) {
|
export function LeftNav({ children, className, hideMobile, ...rest }) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={classNames(styles.nav, styles.leftNav, className)}
|
className={classNames(
|
||||||
|
styles.nav,
|
||||||
|
styles.leftNav,
|
||||||
|
{ [styles.hideMobile]: hideMobile },
|
||||||
|
className
|
||||||
|
)}
|
||||||
{...rest}
|
{...rest}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
@ -39,7 +44,7 @@ export function RightNav({ children, className, ...rest }) {
|
||||||
|
|
||||||
export function HeaderLogo() {
|
export function HeaderLogo() {
|
||||||
return (
|
return (
|
||||||
<Link className={styles.logo} to="/">
|
<Link className={styles.headerLogo} to="/">
|
||||||
<Logo />
|
<Logo />
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
|
|
|
@ -16,16 +16,24 @@
|
||||||
height: 64px;
|
height: 64px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.logo {
|
.headerLogo {
|
||||||
display: flex;
|
display: none;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.leftNav.hideMobile {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.leftNav > * {
|
.leftNav > * {
|
||||||
margin-right: 12px;
|
margin-right: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.leftNav h3 {
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
.rightNav {
|
.rightNav {
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
}
|
}
|
||||||
|
@ -40,7 +48,7 @@
|
||||||
|
|
||||||
.roomAvatar {
|
.roomAvatar {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: none;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
width: 36px;
|
width: 36px;
|
||||||
|
@ -93,7 +101,17 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 800px) {
|
@media (min-width: 800px) {
|
||||||
|
.headerLogo,
|
||||||
|
.roomAvatar,
|
||||||
|
.leftNav.hideMobile {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leftNav h3 {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
.nav {
|
.nav {
|
||||||
height: 98px;
|
height: 76px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,106 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Header, LeftNav, RightNav } from "./Header";
|
import { GridLayoutMenu } from "./GridLayoutMenu";
|
||||||
|
import {
|
||||||
|
Header,
|
||||||
|
HeaderLogo,
|
||||||
|
LeftNav,
|
||||||
|
RightNav,
|
||||||
|
RoomHeaderInfo,
|
||||||
|
} from "./Header";
|
||||||
|
import { UserMenu } from "./UserMenu";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: "Header",
|
title: "Header",
|
||||||
component: Header,
|
component: Header,
|
||||||
|
parameters: {
|
||||||
|
layout: "fullscreen",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Home = () => (
|
export const HomeAnonymous = () => (
|
||||||
<Header>
|
<Header>
|
||||||
<LeftNav></LeftNav>
|
<LeftNav>
|
||||||
|
<HeaderLogo />
|
||||||
|
</LeftNav>
|
||||||
|
<RightNav>
|
||||||
|
<UserMenu />
|
||||||
|
</RightNav>
|
||||||
|
</Header>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const HomeNamedGuest = () => (
|
||||||
|
<Header>
|
||||||
|
<LeftNav>
|
||||||
|
<HeaderLogo />
|
||||||
|
</LeftNav>
|
||||||
|
<RightNav>
|
||||||
|
<UserMenu isAuthenticated isPasswordlessUser displayName="Yara" />
|
||||||
|
</RightNav>
|
||||||
|
</Header>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const HomeLoggedIn = () => (
|
||||||
|
<Header>
|
||||||
|
<LeftNav>
|
||||||
|
<HeaderLogo />
|
||||||
|
</LeftNav>
|
||||||
|
<RightNav>
|
||||||
|
<UserMenu isAuthenticated displayName="Yara" />
|
||||||
|
</RightNav>
|
||||||
|
</Header>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const LobbyNamedGuest = () => (
|
||||||
|
<Header>
|
||||||
|
<LeftNav>
|
||||||
|
<RoomHeaderInfo roomName="Q4Roadmap" />
|
||||||
|
</LeftNav>
|
||||||
|
<RightNav>
|
||||||
|
<UserMenu isAuthenticated isPasswordlessUser displayName="Yara" />
|
||||||
|
</RightNav>
|
||||||
|
</Header>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const LobbyLoggedIn = () => (
|
||||||
|
<Header>
|
||||||
|
<LeftNav>
|
||||||
|
<RoomHeaderInfo roomName="Q4Roadmap" />
|
||||||
|
</LeftNav>
|
||||||
|
<RightNav>
|
||||||
|
<UserMenu isAuthenticated displayName="Yara" />
|
||||||
|
</RightNav>
|
||||||
|
</Header>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const InRoomNamedGuest = () => (
|
||||||
|
<Header>
|
||||||
|
<LeftNav>
|
||||||
|
<RoomHeaderInfo roomName="Q4Roadmap" />
|
||||||
|
</LeftNav>
|
||||||
|
<RightNav>
|
||||||
|
<GridLayoutMenu layout="freedom" />
|
||||||
|
<UserMenu isAuthenticated isPasswordlessUser displayName="Yara" />
|
||||||
|
</RightNav>
|
||||||
|
</Header>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const InRoomLoggedIn = () => (
|
||||||
|
<Header>
|
||||||
|
<LeftNav>
|
||||||
|
<RoomHeaderInfo roomName="Q4Roadmap" />
|
||||||
|
</LeftNav>
|
||||||
|
<RightNav>
|
||||||
|
<GridLayoutMenu layout="freedom" />
|
||||||
|
<UserMenu isAuthenticated displayName="Yara" />
|
||||||
|
</RightNav>
|
||||||
|
</Header>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const CreateAccount = () => (
|
||||||
|
<Header>
|
||||||
|
<LeftNav>
|
||||||
|
<HeaderLogo />
|
||||||
|
</LeftNav>
|
||||||
<RightNav></RightNav>
|
<RightNav></RightNav>
|
||||||
</Header>
|
</Header>
|
||||||
);
|
);
|
||||||
|
|
|
@ -36,6 +36,7 @@ import { useModalTriggerState } from "./Modal";
|
||||||
import { randomString } from "matrix-js-sdk/src/randomstring";
|
import { randomString } from "matrix-js-sdk/src/randomstring";
|
||||||
import { JoinExistingCallModal } from "./JoinExistingCallModal";
|
import { JoinExistingCallModal } from "./JoinExistingCallModal";
|
||||||
import { RecaptchaInput } from "./RecaptchaInput";
|
import { RecaptchaInput } from "./RecaptchaInput";
|
||||||
|
import { UserMenuContainer } from "./UserMenuContainer";
|
||||||
|
|
||||||
export function Home() {
|
export function Home() {
|
||||||
const {
|
const {
|
||||||
|
@ -188,7 +189,7 @@ function UnregisteredView({
|
||||||
<HeaderLogo />
|
<HeaderLogo />
|
||||||
</LeftNav>
|
</LeftNav>
|
||||||
<RightNav>
|
<RightNav>
|
||||||
<UserMenu />
|
<UserMenuContainer />
|
||||||
</RightNav>
|
</RightNav>
|
||||||
</Header>
|
</Header>
|
||||||
<div className={styles.splitContainer}>
|
<div className={styles.splitContainer}>
|
||||||
|
@ -319,7 +320,7 @@ function RegisteredView({
|
||||||
<HeaderLogo />
|
<HeaderLogo />
|
||||||
</LeftNav>
|
</LeftNav>
|
||||||
<RightNav>
|
<RightNav>
|
||||||
<UserMenu />
|
<UserMenuContainer />
|
||||||
</RightNav>
|
</RightNav>
|
||||||
</Header>
|
</Header>
|
||||||
<div className={styles.splitContainer}>
|
<div className={styles.splitContainer}>
|
||||||
|
|
|
@ -3,11 +3,11 @@ import { DismissButton, useOverlay } from "@react-aria/overlays";
|
||||||
import { FocusScope } from "@react-aria/focus";
|
import { FocusScope } from "@react-aria/focus";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import styles from "./Popover.module.css";
|
import styles from "./Popover.module.css";
|
||||||
|
import { useObjectRef } from "@react-aria/utils";
|
||||||
|
|
||||||
export const Popover = forwardRef(
|
export const Popover = forwardRef(
|
||||||
({ isOpen = true, onClose, className, children, ...rest }, ref) => {
|
({ isOpen = true, onClose, className, children, ...rest }, ref) => {
|
||||||
const fallbackRef = useRef();
|
const popoverRef = useObjectRef(ref);
|
||||||
const popoverRef = ref || fallbackRef;
|
|
||||||
|
|
||||||
const { overlayProps } = useOverlay(
|
const { overlayProps } = useOverlay(
|
||||||
{
|
{
|
||||||
|
|
|
@ -51,6 +51,7 @@ import { GridLayoutMenu } from "./GridLayoutMenu";
|
||||||
import { UserMenu } from "./UserMenu";
|
import { UserMenu } from "./UserMenu";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { Avatar } from "./Avatar";
|
import { Avatar } from "./Avatar";
|
||||||
|
import { UserMenuContainer } from "./UserMenuContainer";
|
||||||
|
|
||||||
const canScreenshare = "getDisplayMedia" in navigator.mediaDevices;
|
const canScreenshare = "getDisplayMedia" in navigator.mediaDevices;
|
||||||
// There is currently a bug in Safari our our code with cloning and sending MediaStreams
|
// There is currently a bug in Safari our our code with cloning and sending MediaStreams
|
||||||
|
@ -309,7 +310,7 @@ function RoomSetupView({
|
||||||
<RoomHeaderInfo roomName={roomName} />
|
<RoomHeaderInfo roomName={roomName} />
|
||||||
</LeftNav>
|
</LeftNav>
|
||||||
<RightNav>
|
<RightNav>
|
||||||
<UserMenu />
|
<UserMenuContainer />
|
||||||
</RightNav>
|
</RightNav>
|
||||||
</Header>
|
</Header>
|
||||||
<div className={styles.joinRoom}>
|
<div className={styles.joinRoom}>
|
||||||
|
@ -470,7 +471,7 @@ function InRoomView({
|
||||||
</LeftNav>
|
</LeftNav>
|
||||||
<RightNav>
|
<RightNav>
|
||||||
<GridLayoutMenu layout={layout} setLayout={setLayout} />
|
<GridLayoutMenu layout={layout} setLayout={setLayout} />
|
||||||
{!isGuest && <UserMenu disableLogout />}
|
{!isGuest && <UserMenuContainer disableLogout />}
|
||||||
</RightNav>
|
</RightNav>
|
||||||
</Header>
|
</Header>
|
||||||
{items.length === 0 ? (
|
{items.length === 0 ? (
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React, { forwardRef, useRef } from "react";
|
import React, { forwardRef } from "react";
|
||||||
import { useTooltipTriggerState } from "@react-stately/tooltip";
|
import { useTooltipTriggerState } from "@react-stately/tooltip";
|
||||||
import { useTooltipTrigger, useTooltip } from "@react-aria/tooltip";
|
import { useTooltipTrigger, useTooltip } from "@react-aria/tooltip";
|
||||||
import { mergeProps } from "@react-aria/utils";
|
import { mergeProps, useObjectRef } from "@react-aria/utils";
|
||||||
import styles from "./Tooltip.module.css";
|
import styles from "./Tooltip.module.css";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
|
|
||||||
|
@ -20,8 +20,7 @@ export function Tooltip({ position, state, ...props }) {
|
||||||
|
|
||||||
export const TooltipTrigger = forwardRef(({ children, ...rest }, ref) => {
|
export const TooltipTrigger = forwardRef(({ children, ...rest }, ref) => {
|
||||||
const tooltipState = useTooltipTriggerState(rest);
|
const tooltipState = useTooltipTriggerState(rest);
|
||||||
const fallbackRef = useRef();
|
const triggerRef = useObjectRef(ref);
|
||||||
const triggerRef = ref || fallbackRef;
|
|
||||||
const { triggerProps, tooltipProps } = useTooltipTrigger(
|
const { triggerProps, tooltipProps } = useTooltipTrigger(
|
||||||
rest,
|
rest,
|
||||||
tooltipState,
|
tooltipState,
|
||||||
|
|
|
@ -1,58 +1,34 @@
|
||||||
import React, { useCallback, useMemo } from "react";
|
import React, { useMemo } from "react";
|
||||||
|
import { Item } from "@react-stately/collections";
|
||||||
import { Button, LinkButton } from "./button";
|
import { Button, LinkButton } from "./button";
|
||||||
import { PopoverMenuTrigger } from "./PopoverMenu";
|
import { PopoverMenuTrigger } from "./PopoverMenu";
|
||||||
|
import { Menu } from "./Menu";
|
||||||
|
import { Tooltip, TooltipTrigger } from "./Tooltip";
|
||||||
|
import { Avatar } from "./Avatar";
|
||||||
import { ReactComponent as UserIcon } from "./icons/User.svg";
|
import { ReactComponent as UserIcon } from "./icons/User.svg";
|
||||||
import { ReactComponent as LoginIcon } from "./icons/Login.svg";
|
import { ReactComponent as LoginIcon } from "./icons/Login.svg";
|
||||||
import { ReactComponent as LogoutIcon } from "./icons/Logout.svg";
|
import { ReactComponent as LogoutIcon } from "./icons/Logout.svg";
|
||||||
import styles from "./UserMenu.module.css";
|
import styles from "./UserMenu.module.css";
|
||||||
import { Item } from "@react-stately/collections";
|
import { useLocation } from "react-router-dom";
|
||||||
import { Menu } from "./Menu";
|
|
||||||
import { useHistory, useLocation } from "react-router-dom";
|
|
||||||
import { useClient, useProfile } from "./ConferenceCallManagerHooks";
|
|
||||||
import { useModalTriggerState } from "./Modal";
|
|
||||||
import { ProfileModal } from "./ProfileModal";
|
|
||||||
import { Tooltip, TooltipTrigger } from "./Tooltip";
|
|
||||||
import { Avatar } from "./Avatar";
|
|
||||||
|
|
||||||
export function UserMenu({ disableLogout }) {
|
export function UserMenu({
|
||||||
const location = useLocation();
|
disableLogout,
|
||||||
const history = useHistory();
|
|
||||||
const {
|
|
||||||
isAuthenticated,
|
isAuthenticated,
|
||||||
isGuest,
|
|
||||||
isPasswordlessUser,
|
isPasswordlessUser,
|
||||||
logout,
|
displayName,
|
||||||
userName,
|
avatarUrl,
|
||||||
client,
|
onAction,
|
||||||
} = useClient();
|
}) {
|
||||||
const { displayName, avatarUrl } = useProfile(client);
|
const location = useLocation();
|
||||||
const { modalState, modalProps } = useModalTriggerState();
|
|
||||||
|
|
||||||
const onAction = useCallback(
|
|
||||||
(value) => {
|
|
||||||
switch (value) {
|
|
||||||
case "user":
|
|
||||||
modalState.open();
|
|
||||||
break;
|
|
||||||
case "logout":
|
|
||||||
logout();
|
|
||||||
break;
|
|
||||||
case "login":
|
|
||||||
history.push("/login", { state: { from: location } });
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[history, location, logout, modalState]
|
|
||||||
);
|
|
||||||
|
|
||||||
const items = useMemo(() => {
|
const items = useMemo(() => {
|
||||||
const arr = [];
|
const arr = [];
|
||||||
|
|
||||||
if (isAuthenticated && !isGuest) {
|
if (isAuthenticated) {
|
||||||
arr.push({
|
arr.push({
|
||||||
key: "user",
|
key: "user",
|
||||||
icon: UserIcon,
|
icon: UserIcon,
|
||||||
label: displayName || userName,
|
label: displayName,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isPasswordlessUser) {
|
if (isPasswordlessUser) {
|
||||||
|
@ -73,9 +49,9 @@ export function UserMenu({ disableLogout }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return arr;
|
return arr;
|
||||||
}, [isAuthenticated, isGuest, userName, displayName]);
|
}, [isAuthenticated, isPasswordlessUser, displayName, disableLogout]);
|
||||||
|
|
||||||
if (isGuest || !isAuthenticated) {
|
if (!isAuthenticated) {
|
||||||
return (
|
return (
|
||||||
<LinkButton to={{ pathname: "/login", state: { from: location } }}>
|
<LinkButton to={{ pathname: "/login", state: { from: location } }}>
|
||||||
Log in
|
Log in
|
||||||
|
@ -84,15 +60,15 @@ export function UserMenu({ disableLogout }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
|
||||||
<PopoverMenuTrigger placement="bottom right">
|
<PopoverMenuTrigger placement="bottom right">
|
||||||
<TooltipTrigger>
|
<TooltipTrigger>
|
||||||
<Button variant="icon" className={styles.userButton}>
|
<Button variant="icon" className={styles.userButton}>
|
||||||
{isAuthenticated && !isGuest && !isPasswordlessUser ? (
|
{isAuthenticated && !isPasswordlessUser ? (
|
||||||
<Avatar
|
<Avatar
|
||||||
size="sm"
|
size="sm"
|
||||||
|
className={styles.avatar}
|
||||||
src={avatarUrl}
|
src={avatarUrl}
|
||||||
fallback={(displayName || userName).slice(0, 1).toUpperCase()}
|
fallback={displayName.slice(0, 1).toUpperCase()}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<UserIcon />
|
<UserIcon />
|
||||||
|
@ -115,15 +91,5 @@ export function UserMenu({ disableLogout }) {
|
||||||
</Menu>
|
</Menu>
|
||||||
)}
|
)}
|
||||||
</PopoverMenuTrigger>
|
</PopoverMenuTrigger>
|
||||||
{modalState.isOpen && (
|
|
||||||
<ProfileModal
|
|
||||||
client={client}
|
|
||||||
isAuthenticated={isAuthenticated}
|
|
||||||
isGuest={isGuest}
|
|
||||||
isPasswordlessUser={isPasswordlessUser}
|
|
||||||
{...modalProps}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,17 @@
|
||||||
.userButton svg * {
|
.userButton svg * {
|
||||||
fill: var(--textColor1);
|
fill: var(--textColor1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 800px) {
|
||||||
|
.avatar {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
62
src/UserMenuContainer.jsx
Normal file
62
src/UserMenuContainer.jsx
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
import React, { useCallback } from "react";
|
||||||
|
import { useHistory, useLocation } from "react-router-dom";
|
||||||
|
import { useClient, useProfile } from "./ConferenceCallManagerHooks";
|
||||||
|
import { useModalTriggerState } from "./Modal";
|
||||||
|
import { ProfileModal } from "./ProfileModal";
|
||||||
|
import { UserMenu } from "./UserMenu";
|
||||||
|
|
||||||
|
export function UserMenuContainer({ disableLogout }) {
|
||||||
|
const location = useLocation();
|
||||||
|
const history = useHistory();
|
||||||
|
const {
|
||||||
|
isAuthenticated,
|
||||||
|
isGuest,
|
||||||
|
isPasswordlessUser,
|
||||||
|
logout,
|
||||||
|
userName,
|
||||||
|
client,
|
||||||
|
} = useClient();
|
||||||
|
const { displayName, avatarUrl } = useProfile(client);
|
||||||
|
const { modalState, modalProps } = useModalTriggerState();
|
||||||
|
|
||||||
|
const onAction = useCallback(
|
||||||
|
(value) => {
|
||||||
|
switch (value) {
|
||||||
|
case "user":
|
||||||
|
modalState.open();
|
||||||
|
break;
|
||||||
|
case "logout":
|
||||||
|
logout();
|
||||||
|
break;
|
||||||
|
case "login":
|
||||||
|
history.push("/login", { state: { from: location } });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[history, location, logout, modalState]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<UserMenu
|
||||||
|
disableLogout={disableLogout}
|
||||||
|
isAuthenticated={isAuthenticated}
|
||||||
|
isPasswordlessUser={isPasswordlessUser}
|
||||||
|
avatarUrl={avatarUrl}
|
||||||
|
onAction={onAction}
|
||||||
|
displayName={
|
||||||
|
displayName || (userName ? userName.replace("@", "") : undefined)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
{modalState.isOpen && (
|
||||||
|
<ProfileModal
|
||||||
|
client={client}
|
||||||
|
isAuthenticated={isAuthenticated}
|
||||||
|
isGuest={isGuest}
|
||||||
|
isPasswordlessUser={isPasswordlessUser}
|
||||||
|
{...modalProps}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -62,7 +62,7 @@ export const Button = forwardRef(
|
||||||
[styles.off]: off,
|
[styles.off]: off,
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
{...filteredButtonProps}
|
{...mergeProps(rest, filteredButtonProps)}
|
||||||
ref={buttonRef}
|
ref={buttonRef}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|
22
src/usePageFocusStyle.js
Normal file
22
src/usePageFocusStyle.js
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import { useEffect } from "react";
|
||||||
|
import { useFocusVisible } from "@react-aria/interactions";
|
||||||
|
import styles from "./usePageFocusStyle.module.css";
|
||||||
|
|
||||||
|
export function usePageFocusStyle() {
|
||||||
|
const { isFocusVisible } = useFocusVisible();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const classList = document.body.classList;
|
||||||
|
const hasClass = classList.contains(styles.hideFocus);
|
||||||
|
|
||||||
|
if (isFocusVisible && hasClass) {
|
||||||
|
classList.remove(styles.hideFocus);
|
||||||
|
} else if (!isFocusVisible && !hasClass) {
|
||||||
|
classList.add(styles.hideFocus);
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
classList.remove(styles.hideFocus);
|
||||||
|
};
|
||||||
|
}, [isFocusVisible]);
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue