mirror of
https://github.com/revoltchat/revite.git
synced 2024-12-25 23:22:06 -05:00
feat(header): add chevron / unified sidebar collapse
This commit is contained in:
parent
e263b627aa
commit
d8d002cc4a
7 changed files with 91 additions and 79 deletions
|
@ -1,3 +1,4 @@
|
||||||
|
import { observer } from "mobx-react-lite";
|
||||||
import { Route, Switch } from "react-router";
|
import { Route, Switch } from "react-router";
|
||||||
|
|
||||||
import { useApplicationState } from "../../mobx/State";
|
import { useApplicationState } from "../../mobx/State";
|
||||||
|
@ -8,7 +9,7 @@ import HomeSidebar from "./left/HomeSidebar";
|
||||||
import ServerListSidebar from "./left/ServerListSidebar";
|
import ServerListSidebar from "./left/ServerListSidebar";
|
||||||
import ServerSidebar from "./left/ServerSidebar";
|
import ServerSidebar from "./left/ServerSidebar";
|
||||||
|
|
||||||
export default function LeftSidebar() {
|
export default observer(() => {
|
||||||
const layout = useApplicationState().layout;
|
const layout = useApplicationState().layout;
|
||||||
const isOpen = layout.getSectionState(SIDEBAR_CHANNELS, true);
|
const isOpen = layout.getSectionState(SIDEBAR_CHANNELS, true);
|
||||||
|
|
||||||
|
@ -35,4 +36,4 @@ export default function LeftSidebar() {
|
||||||
</Switch>
|
</Switch>
|
||||||
</SidebarBase>
|
</SidebarBase>
|
||||||
);
|
);
|
||||||
}
|
});
|
||||||
|
|
|
@ -1,15 +1,25 @@
|
||||||
import { Menu } from "@styled-icons/boxicons-regular";
|
import {
|
||||||
|
ChevronLeft,
|
||||||
|
ChevronRight,
|
||||||
|
Menu,
|
||||||
|
} from "@styled-icons/boxicons-regular";
|
||||||
|
import { observer } from "mobx-react-lite";
|
||||||
import styled, { css } from "styled-components";
|
import styled, { css } from "styled-components";
|
||||||
|
|
||||||
import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice";
|
import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice";
|
||||||
|
|
||||||
|
import { useApplicationState } from "../../mobx/State";
|
||||||
|
import { SIDEBAR_CHANNELS } from "../../mobx/stores/Layout";
|
||||||
|
|
||||||
|
import { Children } from "../../types/Preact";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
borders?: boolean;
|
borders?: boolean;
|
||||||
background?: boolean;
|
background?: boolean;
|
||||||
placement: "primary" | "secondary";
|
placement: "primary" | "secondary";
|
||||||
}
|
}
|
||||||
|
|
||||||
export default styled.div<Props>`
|
const Header = styled.div<Props>`
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
height: 48px;
|
height: 48px;
|
||||||
flex: 0 auto;
|
flex: 0 auto;
|
||||||
|
@ -33,10 +43,6 @@ export default styled.div<Props>`
|
||||||
color: var(--secondary-foreground);
|
color: var(--secondary-foreground);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*@media only screen and (max-width: 768px) {
|
|
||||||
padding: 0 12px;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
${() =>
|
${() =>
|
||||||
isTouchscreenDevice &&
|
isTouchscreenDevice &&
|
||||||
css`
|
css`
|
||||||
|
@ -66,6 +72,60 @@ export default styled.div<Props>`
|
||||||
`}
|
`}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export default Header;
|
||||||
|
|
||||||
|
const IconContainer = styled.div`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--secondary-foreground);
|
||||||
|
margin-right: 5px;
|
||||||
|
|
||||||
|
> svg {
|
||||||
|
margin-right: -5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
${!isTouchscreenDevice &&
|
||||||
|
css`
|
||||||
|
&:hover {
|
||||||
|
color: var(--foreground);
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
`;
|
||||||
|
|
||||||
|
interface PageHeaderProps {
|
||||||
|
noBurger?: boolean;
|
||||||
|
children: Children;
|
||||||
|
icon: Children;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PageHeader = observer(
|
||||||
|
({ children, icon, noBurger }: PageHeaderProps) => {
|
||||||
|
const layout = useApplicationState().layout;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Header placement="primary">
|
||||||
|
{!noBurger && <HamburgerAction />}
|
||||||
|
<IconContainer
|
||||||
|
onClick={() =>
|
||||||
|
layout.toggleSectionState(SIDEBAR_CHANNELS, true)
|
||||||
|
}>
|
||||||
|
{!isTouchscreenDevice &&
|
||||||
|
layout.getSectionState(SIDEBAR_CHANNELS, true) && (
|
||||||
|
<ChevronLeft size={18} />
|
||||||
|
)}
|
||||||
|
{icon}
|
||||||
|
{!isTouchscreenDevice &&
|
||||||
|
!layout.getSectionState(SIDEBAR_CHANNELS, true) && (
|
||||||
|
<ChevronRight size={18} />
|
||||||
|
)}
|
||||||
|
</IconContainer>
|
||||||
|
{children}
|
||||||
|
</Header>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
export function HamburgerAction() {
|
export function HamburgerAction() {
|
||||||
if (!isTouchscreenDevice) return null;
|
if (!isTouchscreenDevice) return null;
|
||||||
|
|
||||||
|
|
|
@ -177,6 +177,6 @@ export default class Layout implements Store, Persistent<Data> {
|
||||||
* @param def Default state value
|
* @param def Default state value
|
||||||
*/
|
*/
|
||||||
@action toggleSectionState(id: string, def?: boolean) {
|
@action toggleSectionState(id: string, def?: boolean) {
|
||||||
this.setSectionState(id, !this.getSectionState(id, def));
|
this.setSectionState(id, !this.getSectionState(id, def), def);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
import { At, Hash } from "@styled-icons/boxicons-regular";
|
import {
|
||||||
|
At,
|
||||||
|
ChevronLeft,
|
||||||
|
ChevronRight,
|
||||||
|
Hash,
|
||||||
|
} from "@styled-icons/boxicons-regular";
|
||||||
import { Notepad, Group } from "@styled-icons/boxicons-solid";
|
import { Notepad, Group } from "@styled-icons/boxicons-solid";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { Channel } from "revolt.js/dist/maps/Channels";
|
import { Channel } from "revolt.js/dist/maps/Channels";
|
||||||
|
@ -8,14 +13,17 @@ import styled, { css } from "styled-components";
|
||||||
import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice";
|
import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice";
|
||||||
|
|
||||||
import { useApplicationState } from "../../mobx/State";
|
import { useApplicationState } from "../../mobx/State";
|
||||||
import { SIDEBAR_MEMBERS } from "../../mobx/stores/Layout";
|
import { SIDEBAR_CHANNELS, SIDEBAR_MEMBERS } from "../../mobx/stores/Layout";
|
||||||
|
|
||||||
import { useIntermediate } from "../../context/intermediate/Intermediate";
|
import { useIntermediate } from "../../context/intermediate/Intermediate";
|
||||||
import { getChannelName } from "../../context/revoltjs/util";
|
import { getChannelName } from "../../context/revoltjs/util";
|
||||||
|
|
||||||
import { useStatusColour } from "../../components/common/user/UserIcon";
|
import { useStatusColour } from "../../components/common/user/UserIcon";
|
||||||
import UserStatus from "../../components/common/user/UserStatus";
|
import UserStatus from "../../components/common/user/UserStatus";
|
||||||
import Header, { HamburgerAction } from "../../components/ui/Header";
|
import Header, {
|
||||||
|
HamburgerAction,
|
||||||
|
PageHeader,
|
||||||
|
} from "../../components/ui/Header";
|
||||||
|
|
||||||
import Markdown from "../../components/markdown/Markdown";
|
import Markdown from "../../components/markdown/Markdown";
|
||||||
import HeaderActions from "./actions/HeaderActions";
|
import HeaderActions from "./actions/HeaderActions";
|
||||||
|
@ -68,25 +76,6 @@ const Info = styled.div`
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const IconConainer = styled.div`
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
cursor: pointer;
|
|
||||||
color: var(--secondary-foreground);
|
|
||||||
margin-right: 5px;
|
|
||||||
|
|
||||||
> svg {
|
|
||||||
margin-right: -5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
${!isTouchscreenDevice &&
|
|
||||||
css`
|
|
||||||
&:hover {
|
|
||||||
color: var(--foreground);
|
|
||||||
}
|
|
||||||
`}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export default observer(({ channel }: ChannelHeaderProps) => {
|
export default observer(({ channel }: ChannelHeaderProps) => {
|
||||||
const { openScreen } = useIntermediate();
|
const { openScreen } = useIntermediate();
|
||||||
const layout = useApplicationState().layout;
|
const layout = useApplicationState().layout;
|
||||||
|
@ -110,15 +99,7 @@ export default observer(({ channel }: ChannelHeaderProps) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Header placement="primary">
|
<PageHeader icon={icon}>
|
||||||
<HamburgerAction />
|
|
||||||
<IconConainer
|
|
||||||
onClick={() =>
|
|
||||||
layout.toggleSectionState(SIDEBAR_MEMBERS, true)
|
|
||||||
}>
|
|
||||||
{/*isTouchscreenDevice && <ChevronLeft size={18} /> FIXME: requires mobx merge */}
|
|
||||||
{icon}
|
|
||||||
</IconConainer>
|
|
||||||
<Info>
|
<Info>
|
||||||
<span className="name">{name}</span>
|
<span className="name">{name}</span>
|
||||||
{isTouchscreenDevice &&
|
{isTouchscreenDevice &&
|
||||||
|
@ -162,6 +143,6 @@ export default observer(({ channel }: ChannelHeaderProps) => {
|
||||||
)}
|
)}
|
||||||
</Info>
|
</Info>
|
||||||
<HeaderActions channel={channel} />
|
<HeaderActions channel={channel} />
|
||||||
</Header>
|
</PageHeader>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { TextReact } from "../../lib/i18n";
|
||||||
|
|
||||||
import { AppContext } from "../../context/revoltjs/RevoltClient";
|
import { AppContext } from "../../context/revoltjs/RevoltClient";
|
||||||
|
|
||||||
import Header from "../../components/ui/Header";
|
import Header, { PageHeader } from "../../components/ui/Header";
|
||||||
|
|
||||||
export default function Developer() {
|
export default function Developer() {
|
||||||
// const voice = useContext(VoiceContext);
|
// const voice = useContext(VoiceContext);
|
||||||
|
@ -27,10 +27,7 @@ export default function Developer() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Header placement="primary">
|
<PageHeader icon={<Wrench size="24" />}>Developer Tab</PageHeader>
|
||||||
<Wrench size="24" />
|
|
||||||
Developer Tab
|
|
||||||
</Header>
|
|
||||||
<div style={{ padding: "16px" }}>
|
<div style={{ padding: "16px" }}>
|
||||||
<PaintCounter always />
|
<PaintCounter always />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -16,7 +16,7 @@ import { useClient } from "../../context/revoltjs/RevoltClient";
|
||||||
import CollapsibleSection from "../../components/common/CollapsibleSection";
|
import CollapsibleSection from "../../components/common/CollapsibleSection";
|
||||||
import Tooltip from "../../components/common/Tooltip";
|
import Tooltip from "../../components/common/Tooltip";
|
||||||
import UserIcon from "../../components/common/user/UserIcon";
|
import UserIcon from "../../components/common/user/UserIcon";
|
||||||
import Header from "../../components/ui/Header";
|
import Header, { PageHeader } from "../../components/ui/Header";
|
||||||
import IconButton from "../../components/ui/IconButton";
|
import IconButton from "../../components/ui/IconButton";
|
||||||
|
|
||||||
import { Children } from "../../types/Preact";
|
import { Children } from "../../types/Preact";
|
||||||
|
@ -72,8 +72,7 @@ export default observer(() => {
|
||||||
const isEmpty = lists.reduce((p: number, n) => p + n.length, 0) === 0;
|
const isEmpty = lists.reduce((p: number, n) => p + n.length, 0) === 0;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Header placement="primary">
|
<PageHeader icon={<UserDetail size={24} />} noBurger>
|
||||||
{!isTouchscreenDevice && <UserDetail size={24} />}
|
|
||||||
<div className={styles.title}>
|
<div className={styles.title}>
|
||||||
<Text id="app.navigation.tabs.friends" />
|
<Text id="app.navigation.tabs.friends" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -115,7 +114,7 @@ export default observer(() => {
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
*/}
|
*/}
|
||||||
</div>
|
</div>
|
||||||
</Header>
|
</PageHeader>
|
||||||
<div
|
<div
|
||||||
className={styles.list}
|
className={styles.list}
|
||||||
data-empty={isEmpty}
|
data-empty={isEmpty}
|
||||||
|
|
|
@ -17,30 +17,15 @@ import "./snow.scss";
|
||||||
import { Text } from "preact-i18n";
|
import { Text } from "preact-i18n";
|
||||||
import { useContext, useMemo } from "preact/hooks";
|
import { useContext, useMemo } from "preact/hooks";
|
||||||
|
|
||||||
import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice";
|
|
||||||
|
|
||||||
import { useApplicationState } from "../../mobx/State";
|
import { useApplicationState } from "../../mobx/State";
|
||||||
import { SIDEBAR_CHANNELS } from "../../mobx/stores/Layout";
|
|
||||||
|
|
||||||
import { AppContext } from "../../context/revoltjs/RevoltClient";
|
import { AppContext } from "../../context/revoltjs/RevoltClient";
|
||||||
|
|
||||||
import wideSVG from "../../../public/assets/wide.svg";
|
import wideSVG from "../../../public/assets/wide.svg";
|
||||||
import Tooltip from "../../components/common/Tooltip";
|
import Tooltip from "../../components/common/Tooltip";
|
||||||
import Header from "../../components/ui/Header";
|
import { PageHeader } from "../../components/ui/Header";
|
||||||
import CategoryButton from "../../components/ui/fluent/CategoryButton";
|
import CategoryButton from "../../components/ui/fluent/CategoryButton";
|
||||||
|
|
||||||
const IconConainer = styled.div`
|
|
||||||
cursor: pointer;
|
|
||||||
color: var(--secondary-foreground);
|
|
||||||
|
|
||||||
${!isTouchscreenDevice &&
|
|
||||||
css`
|
|
||||||
&:hover {
|
|
||||||
color: var(--foreground);
|
|
||||||
}
|
|
||||||
`}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const Overlay = styled.div`
|
const Overlay = styled.div`
|
||||||
display: grid;
|
display: grid;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
@ -58,14 +43,6 @@ export default observer(() => {
|
||||||
const client = useContext(AppContext);
|
const client = useContext(AppContext);
|
||||||
const state = useApplicationState();
|
const state = useApplicationState();
|
||||||
|
|
||||||
const toggleChannelSidebar = () => {
|
|
||||||
if (isTouchscreenDevice) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
state.layout.toggleSectionState(SIDEBAR_CHANNELS, true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const toggleSeasonalTheme = () =>
|
const toggleSeasonalTheme = () =>
|
||||||
state.settings.set(
|
state.settings.set(
|
||||||
"appearance:seasonal",
|
"appearance:seasonal",
|
||||||
|
@ -107,12 +84,9 @@ export default observer(() => {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="content">
|
<div className="content">
|
||||||
<Header placement="primary">
|
<PageHeader icon={<HomeIcon size={24} />}>
|
||||||
<IconConainer onClick={toggleChannelSidebar}>
|
|
||||||
<HomeIcon size={24} />
|
|
||||||
</IconConainer>
|
|
||||||
<Text id="app.navigation.tabs.home" />
|
<Text id="app.navigation.tabs.home" />
|
||||||
</Header>
|
</PageHeader>
|
||||||
<div className={styles.homeScreen}>
|
<div className={styles.homeScreen}>
|
||||||
<h3>
|
<h3>
|
||||||
<Text id="app.special.modals.onboarding.welcome" />
|
<Text id="app.special.modals.onboarding.welcome" />
|
||||||
|
|
Loading…
Reference in a new issue