1
0
mirror of https://github.com/spacebarchat/client.git synced 2024-11-22 10:22:30 +01:00

feat(GuildSidebar): sidebar pills

This commit is contained in:
Puyodead1 2023-05-08 20:59:44 -04:00
parent e53abb0849
commit 35a870b599
No known key found for this signature in database
GPG Key ID: A4FA4FEC0DD353FC
6 changed files with 154 additions and 42 deletions

View File

@ -70,7 +70,7 @@ function App() {
element={<AuthenticationGuard component={AppPage} />}
/>
<Route
path="/channels/:channelId"
path="/channels/:guildId/:channelId?"
element={<AuthenticationGuard component={ChannelPage} />}
/>
<Route

View File

@ -1,26 +1,23 @@
import { Tooltip } from "@mui/material";
import { CDNRoutes, ImageFormat } from "@spacebarchat/spacebar-api-types/v9";
import React from "react";
import { useNavigate } from "react-router-dom";
import styled from "styled-components";
import { useAppStore } from "../stores/AppStore";
import REST from "../utils/REST";
import Container from "./Container";
import Tooltip from "./Tooltip";
import SidebarListItem from "./SidebarListItem";
import SidebarPill, { PillType } from "./SidebarPill";
const ListItem = styled.li`
padding: 0;
margin: 0;
`;
const Wrapper = styled(Container)`
margin-top: 9px;
padding: 0;
width: 48px;
height: 48px;
border-radius: 50%;
background-color: var(--background-secondary);
const Wrapper = styled(Container)<{ active?: boolean }>`
display: flex;
align-items: center;
justify-content: center;
width: 48px;
height: 48px;
border-radius: ${(props) => (props.active ? "30%" : "50%")};
background-color: ${(props) =>
props.active ? "var(--primary)" : "var(--background-secondary)"};
transition: border-radius 0.2s ease, background-color 0.2s ease;
&:hover {
@ -31,15 +28,18 @@ const Wrapper = styled(Container)`
interface Props {
guildId: string;
active?: boolean;
}
/**
* List item for use in the guild sidebar
*/
function Guild(props: Props) {
function GuildItem(props: Props) {
const app = useAppStore();
const navigate = useNavigate();
const guild = app.guilds.get(props.guildId);
const [pillType, setPillType] = React.useState<PillType>("none");
const [isHovered, setHovered] = React.useState(false);
if (!guild) return null;
@ -47,10 +47,23 @@ function Guild(props: Props) {
navigate(`/channels/${props.guildId}`);
};
React.useEffect(() => {
if (props.active) return setPillType("active");
else if (isHovered) return setPillType("hover");
// TODO: unread
else return setPillType("none");
}, [props.active, isHovered]);
return (
<ListItem>
<SidebarListItem>
<SidebarPill type={pillType} />
<Tooltip title={guild.name} placement="right">
<Wrapper onClick={doNavigate}>
<Wrapper
onClick={doNavigate}
active={props.active}
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
>
{guild.icon ? (
<img
src={REST.makeCDNUrl(
@ -70,8 +83,8 @@ function Guild(props: Props) {
)}
</Wrapper>
</Tooltip>
</ListItem>
</SidebarListItem>
);
}
export default Guild;
export default GuildItem;

View File

@ -1,13 +1,14 @@
import { observer } from "mobx-react-lite";
import { useNavigate } from "react-router-dom";
import { useNavigate, useParams } from "react-router-dom";
import styled from "styled-components";
import { useAppStore } from "../stores/AppStore";
import Guild from "./Guild";
import GuildItem from "./GuildItem";
import SidebarAction from "./SidebarAction";
import SidebarListItem from "./SidebarListItem";
const List = styled.ul`
list-style: none;
padding: 12px;
padding: 12px 12px 12px 0;
margin: 0;
display: flex;
flex-direction: column;
@ -19,14 +20,17 @@ const List = styled.ul`
}
`;
const Hr = styled.hr`
width: 100%;
margin-top: 12px;
const Divider = styled.div`
height: 2px;
width: 32px;
border-radius: 1px;
background-color: white;
`;
function GuildSidebar() {
const app = useAppStore();
const navigate = useNavigate();
const { guildId } = useParams();
return (
<List>
@ -38,11 +42,20 @@ function GuildSidebar() {
}}
action={() => navigate("/channels/@me")}
margin={false}
active={guildId === "@me"}
/>
<Hr key="hr" />
<SidebarListItem>
<Divider key="divider" />
</SidebarListItem>
<div aria-label="Servers">
{app.guilds.getAll().map((guild) => (
<Guild key={guild.id} guildId={guild.id} />
<GuildItem
key={guild.id}
guildId={guild.id}
active={guild.id === guildId}
/>
))}
</div>
</List>
);
}

View File

@ -1,20 +1,19 @@
import React from "react";
import styled from "styled-components";
import Container from "./Container";
import Icon, { IconProps } from "./Icon";
import SidebarListItem from "./SidebarListItem";
import SidebarPill, { PillType } from "./SidebarPill";
import Tooltip from "./Tooltip";
const ListItem = styled.li`
padding: 0;
margin: 0;
`;
const Wrapper = styled(Container)<{ margin?: boolean }>`
const Wrapper = styled(Container)<{ margin?: boolean; active?: boolean }>`
${(props) => (props.margin !== false ? "margin-top: 9px;" : "")}};
padding: 0;
width: 48px;
height: 48px;
border-radius: 50%;
background-color: var(--background-secondary);
border-radius: ${(props) => (props.active ? "30%" : "50%")};
background-color: ${(props) =>
props.active ? "var(--primary)" : "var(--background-secondary)"};
display: flex;
align-items: center;
justify-content: center;
@ -36,6 +35,7 @@ interface Props {
icon?: IconProps;
label?: string;
margin?: boolean;
active?: boolean;
}
function SidebarAction(props: Props) {
@ -44,16 +44,33 @@ function SidebarAction(props: Props) {
"SidebarAction can only have one of image, icon, or label",
);
const [pillType, setPillType] = React.useState<PillType>("none");
const [isHovered, setHovered] = React.useState(false);
React.useEffect(() => {
if (props.active) return setPillType("active");
else if (isHovered) return setPillType("hover");
// TODO: unread
else return setPillType("none");
}, [props.active, isHovered]);
return (
<ListItem>
<SidebarListItem>
<SidebarPill type={pillType} />
<Tooltip title={props.tooltip} placement="right">
<Wrapper onClick={props.action} margin={props.margin}>
{props.image && <img {...props.image} />}
<Wrapper
onClick={props.action}
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
margin={props.margin}
active={props.active}
>
{/* {props.image && <img {...props.image} />} */}
{props.icon && <Icon {...props.icon} />}
{props.label && <span>{props.label}</span>}
{/* {props.label && <span>{props.label}</span>} */}
</Wrapper>
</Tooltip>
</ListItem>
</SidebarListItem>
);
}

View File

@ -0,0 +1,11 @@
import styled from "styled-components";
const SidebarListItem = styled.li`
position: relative;
margin: 0 0 8px;
display: flex;
justify-content: center;
width: 72px;
`;
export default SidebarListItem;

View File

@ -0,0 +1,58 @@
import styled from "styled-components";
import Container from "./Container";
export type PillType = "none" | "unread" | "hover" | "active";
const Wrapper = styled(Container)`
position: absolute;
top: 0;
left: 0;
width: 8px;
height: 48px;
display: flex;
justify-content: flex-start;
align-items: center;
`;
const Pill = styled.span<{ type: PillType }>`
width: 8px;
border-radius: 0 4px 4px 0;
background-color: white;
margin-left: -4px;
transition: height 0.3s ease;
${(props) => {
switch (props.type) {
case "unread":
return `
height: 8px;
`;
case "hover":
return `
height: 20px;
`;
case "active":
return `
height: 40px;
`;
default:
return `
height: 0;
`;
}
}}
`;
interface Props {
type: PillType;
}
function SidebarPill({ type }: Props) {
return (
<Wrapper>
<Pill type={type} />
</Wrapper>
);
}
export default SidebarPill;