mirror of
https://github.com/roleypoly/roleypoly.git
synced 2025-04-24 19:39:11 +00:00
feat(UI): add initial server picker
This commit is contained in:
parent
d4e8e8330a
commit
e758c09fbf
19 changed files with 319 additions and 12 deletions
|
@ -43,6 +43,7 @@
|
|||
"react-is": "^17.0.1",
|
||||
"react-tooltip": "^4.2.11",
|
||||
"styled-components": "^5.2.1",
|
||||
"styled-normalize": "^8.0.7",
|
||||
"swr": "^0.3.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
@ -140,6 +140,13 @@ export const guild: Guild = {
|
|||
roles: [],
|
||||
};
|
||||
|
||||
export const roleypolyGuild: GuildSlug = {
|
||||
name: 'Roleypoly',
|
||||
id: '386659935687147521',
|
||||
permissionLevel: 0,
|
||||
icon: 'ffee638c73ff9c972554f64ca34d67ee',
|
||||
};
|
||||
|
||||
export const guildMap: { [x: string]: GuildSlug } = {
|
||||
'emoji megaporium': {
|
||||
name: guild.name,
|
||||
|
@ -147,12 +154,7 @@ export const guildMap: { [x: string]: GuildSlug } = {
|
|||
permissionLevel: 0,
|
||||
icon: guild.icon,
|
||||
},
|
||||
Roleypoly: {
|
||||
name: 'Roleypoly',
|
||||
id: '203493697696956418',
|
||||
permissionLevel: 0,
|
||||
icon: 'ff08d36f5aee1ff48f8377b65d031ab0',
|
||||
},
|
||||
Roleypoly: roleypolyGuild,
|
||||
'chamber of secrets': {
|
||||
name: 'chamber of secrets',
|
||||
id: 'aaa',
|
||||
|
@ -234,6 +236,6 @@ export const mastheadSlugs: GuildSlug[] = guildEnum.guilds.map<GuildSlug>(
|
|||
id: guild.guild.id,
|
||||
name: guild.guild.name,
|
||||
icon: guild.guild.icon,
|
||||
permissionLevel: idx % 3,
|
||||
permissionLevel: 1 << idx % 3,
|
||||
})
|
||||
);
|
||||
|
|
13
src/design-system/atoms/collapse/Collapse.stories.tsx
Normal file
13
src/design-system/atoms/collapse/Collapse.stories.tsx
Normal file
|
@ -0,0 +1,13 @@
|
|||
import { SmallTitle } from 'roleypoly/design-system/atoms/typography';
|
||||
import { Collapse } from './Collapse';
|
||||
|
||||
export default {
|
||||
title: 'Atoms/Collapse',
|
||||
component: Collapse,
|
||||
};
|
||||
|
||||
export const collapse = (args) => (
|
||||
<SmallTitle>
|
||||
Hello, <Collapse {...args}>small</Collapse> world!
|
||||
</SmallTitle>
|
||||
);
|
10
src/design-system/atoms/collapse/Collapse.tsx
Normal file
10
src/design-system/atoms/collapse/Collapse.tsx
Normal file
|
@ -0,0 +1,10 @@
|
|||
import styled, { css } from 'styled-components';
|
||||
import { onSmallScreen } from '../breakpoints';
|
||||
|
||||
export const Collapse = styled.span<{ preventCollapse?: boolean }>`
|
||||
${(props) =>
|
||||
!props.preventCollapse &&
|
||||
onSmallScreen(css`
|
||||
display: none;
|
||||
`)}
|
||||
`;
|
1
src/design-system/atoms/collapse/index.ts
Normal file
1
src/design-system/atoms/collapse/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export * from './Collapse';
|
|
@ -96,3 +96,13 @@ export const Link = styled.a`
|
|||
color: ${palette.taupe600};
|
||||
}
|
||||
`;
|
||||
|
||||
export const CompletelyStylelessLink = styled.a`
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
:visited,
|
||||
:active,
|
||||
:hover {
|
||||
color: inherit;
|
||||
}
|
||||
`;
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
import { roleypolyGuild } from 'roleypoly/common/types/storyData';
|
||||
import { ServerListingCard } from './ServerListingCard';
|
||||
|
||||
export default {
|
||||
title: 'Molecules/Server Listing Card',
|
||||
component: ServerListingCard,
|
||||
args: {
|
||||
guild: { ...roleypolyGuild, permissionLevel: 4 },
|
||||
},
|
||||
};
|
||||
|
||||
export const serverListingCard = (args) => <ServerListingCard {...args} />;
|
|
@ -0,0 +1,89 @@
|
|||
import { onSmallScreen, onTablet } from 'roleypoly/design-system/atoms/breakpoints';
|
||||
import { palette } from 'roleypoly/design-system/atoms/colors';
|
||||
import { transitions } from 'roleypoly/design-system/atoms/timings';
|
||||
import { text200, text500 } from 'roleypoly/design-system/atoms/typography';
|
||||
import styled, { css } from 'styled-components';
|
||||
|
||||
export const CardLine = styled.div<{ left?: boolean }>`
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
padding: 5px;
|
||||
box-sizing: border-box;
|
||||
|
||||
${(props) =>
|
||||
props.left &&
|
||||
css`
|
||||
flex: 1;
|
||||
justify-content: flex-end;
|
||||
align-items: flex-end;
|
||||
`}
|
||||
`;
|
||||
|
||||
export const MaxWidthTitle = styled.div`
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
`;
|
||||
|
||||
export const PermissionTagStyled = styled.div<{ hiddenOnSmall?: boolean }>`
|
||||
${text200}
|
||||
display: inline-block;
|
||||
background-color: ${palette.taupe200};
|
||||
padding: 4px 6px;
|
||||
border-radius: 2px;
|
||||
|
||||
svg {
|
||||
position: relative;
|
||||
top: 1px;
|
||||
${onTablet(
|
||||
css`
|
||||
margin-right: 2px;
|
||||
`
|
||||
)}
|
||||
}
|
||||
|
||||
${(props) =>
|
||||
props.hiddenOnSmall &&
|
||||
onSmallScreen(
|
||||
css`
|
||||
display: none;
|
||||
`
|
||||
)}
|
||||
`;
|
||||
|
||||
export const CardBase = styled.div`
|
||||
${text500}
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
background-color: ${palette.taupe300};
|
||||
overflow-x: hidden;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
transform: translate(0);
|
||||
transition: transform ease-in-out ${transitions.actionable}s,
|
||||
box-shadow ease-in-out ${transitions.actionable}s,
|
||||
border-color ease-in-out ${transitions.out2in}s;
|
||||
box-sizing: border-box;
|
||||
max-width: 98vw;
|
||||
:hover {
|
||||
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.25);
|
||||
transform: translate(0, -1px);
|
||||
}
|
||||
:active {
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
|
||||
transform: translate(0);
|
||||
}
|
||||
|
||||
${onTablet(css`
|
||||
flex-direction: column;
|
||||
justify-content: left;
|
||||
`)}
|
||||
`;
|
|
@ -0,0 +1,60 @@
|
|||
import * as React from 'react';
|
||||
import { GoPerson, GoStar, GoZap } from 'react-icons/go';
|
||||
import { GuildSlug, UserGuildPermissions } from 'roleypoly/common/types';
|
||||
import { Avatar, utils } from 'roleypoly/design-system/atoms/avatar';
|
||||
import { Collapse } from 'roleypoly/design-system/atoms/collapse';
|
||||
import {
|
||||
CardBase,
|
||||
CardLine,
|
||||
MaxWidthTitle,
|
||||
PermissionTagStyled,
|
||||
} from './ServerListingCard.styled';
|
||||
|
||||
type ServerListingProps = {
|
||||
guild: GuildSlug;
|
||||
};
|
||||
|
||||
export const ServerListingCard = (props: ServerListingProps) => (
|
||||
<CardBase>
|
||||
<CardLine>
|
||||
<Avatar
|
||||
hash={props.guild.icon}
|
||||
src={utils.avatarHash(props.guild.id, props.guild.icon, 'icons')}
|
||||
>
|
||||
{utils.initialsFromName(props.guild.name)}
|
||||
</Avatar>
|
||||
</CardLine>
|
||||
<MaxWidthTitle>{props.guild.name}</MaxWidthTitle>
|
||||
<CardLine left>
|
||||
<PermissionTag permissionLevel={props.guild.permissionLevel} />
|
||||
</CardLine>
|
||||
</CardBase>
|
||||
);
|
||||
|
||||
const PermissionTag = (props: { permissionLevel: UserGuildPermissions }) => {
|
||||
switch (props.permissionLevel) {
|
||||
case UserGuildPermissions.Admin:
|
||||
return (
|
||||
<PermissionTagStyled>
|
||||
<GoStar />
|
||||
<Collapse>Administrator</Collapse>
|
||||
</PermissionTagStyled>
|
||||
);
|
||||
case UserGuildPermissions.Manager:
|
||||
return (
|
||||
<PermissionTagStyled>
|
||||
<GoZap />
|
||||
<Collapse>Role Manager</Collapse>
|
||||
</PermissionTagStyled>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<PermissionTagStyled hiddenOnSmall>
|
||||
<GoPerson />
|
||||
<Collapse>Member</Collapse>
|
||||
</PermissionTagStyled>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
1
src/design-system/molecules/server-listing-card/index.ts
Normal file
1
src/design-system/molecules/server-listing-card/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export * from './ServerListingCard';
|
|
@ -0,0 +1,12 @@
|
|||
import { mastheadSlugs } from 'roleypoly/common/types/storyData';
|
||||
import { ServersListing } from './ServersListing';
|
||||
|
||||
export default {
|
||||
title: 'Organisms/Servers Listing',
|
||||
component: ServersListing,
|
||||
args: {
|
||||
guilds: mastheadSlugs,
|
||||
},
|
||||
};
|
||||
|
||||
export const serversListing = (args) => <ServersListing {...args} />;
|
|
@ -0,0 +1,23 @@
|
|||
import { onTablet } from 'roleypoly/design-system/atoms/breakpoints';
|
||||
import styled, { css } from 'styled-components';
|
||||
|
||||
export const ContentContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-width: calc(98vw - 15px);
|
||||
padding-bottom: 25px;
|
||||
${onTablet(css`
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
`)}
|
||||
`;
|
||||
|
||||
export const CardContainer = styled.div`
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 5px;
|
||||
${onTablet(css`
|
||||
margin: 5px;
|
||||
flex-basis: 30%;
|
||||
max-width: 30%;
|
||||
`)}
|
||||
`;
|
|
@ -0,0 +1,33 @@
|
|||
import Link from 'next/link';
|
||||
import * as React from 'react';
|
||||
import { GuildSlug } from 'roleypoly/common/types';
|
||||
import { sortBy } from 'roleypoly/common/utils/sortBy';
|
||||
import { CompletelyStylelessLink } from 'roleypoly/design-system/atoms/typography';
|
||||
import { ServerListingCard } from 'roleypoly/design-system/molecules/server-listing-card';
|
||||
import { CardContainer, ContentContainer } from './ServersListing.styled';
|
||||
|
||||
type ServersListingProps = {
|
||||
guilds: GuildSlug[];
|
||||
};
|
||||
|
||||
export const ServersListing = (props: ServersListingProps) => (
|
||||
<ContentContainer>
|
||||
{props.guilds &&
|
||||
sortBy(props.guilds, 'name', (a: string, b: string) =>
|
||||
a.toLowerCase() > b.toLowerCase() ? 1 : -1
|
||||
).map((guild, idx) => (
|
||||
<CardContainer key={idx}>
|
||||
<Link
|
||||
as={`/s/${guild.id}`}
|
||||
href={`/s/[id]`}
|
||||
prefetch={false}
|
||||
passHref
|
||||
>
|
||||
<CompletelyStylelessLink>
|
||||
<ServerListingCard guild={guild} />
|
||||
</CompletelyStylelessLink>
|
||||
</Link>
|
||||
</CardContainer>
|
||||
))}
|
||||
</ContentContainer>
|
||||
);
|
1
src/design-system/organisms/servers-listing/index.ts
Normal file
1
src/design-system/organisms/servers-listing/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export * from './ServersListing';
|
13
src/design-system/templates/servers/Servers.stories.tsx
Normal file
13
src/design-system/templates/servers/Servers.stories.tsx
Normal file
|
@ -0,0 +1,13 @@
|
|||
import * as React from 'react';
|
||||
import { mastheadSlugs, user } from 'roleypoly/common/types/storyData';
|
||||
import { ServersTemplate } from '.';
|
||||
|
||||
export default {
|
||||
title: 'Templates/Servers Page',
|
||||
args: {
|
||||
guilds: mastheadSlugs,
|
||||
user: user,
|
||||
},
|
||||
};
|
||||
|
||||
export const serversPage = (args) => <ServersTemplate {...args} />;
|
14
src/design-system/templates/servers/Servers.tsx
Normal file
14
src/design-system/templates/servers/Servers.tsx
Normal file
|
@ -0,0 +1,14 @@
|
|||
import * as React from 'react';
|
||||
import { GuildSlug } from 'roleypoly/common/types';
|
||||
import { AppShell, AppShellProps } from 'roleypoly/design-system/organisms/app-shell';
|
||||
import { ServersListing } from 'roleypoly/design-system/organisms/servers-listing/ServersListing';
|
||||
|
||||
type ServerTemplateProps = Omit<AppShellProps, 'children'> & {
|
||||
guilds: GuildSlug[];
|
||||
};
|
||||
|
||||
export const ServersTemplate = (props: ServerTemplateProps) => (
|
||||
<AppShell {...props} disableGuildPicker>
|
||||
<ServersListing guilds={props.guilds}></ServersListing>
|
||||
</AppShell>
|
||||
);
|
1
src/design-system/templates/servers/index.ts
Normal file
1
src/design-system/templates/servers/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export * from './Servers';
|
|
@ -1,11 +1,17 @@
|
|||
import { AppShell } from 'roleypoly/design-system/organisms/app-shell';
|
||||
import Head from 'next/head';
|
||||
import { ServersTemplate } from 'roleypoly/design-system/templates/servers';
|
||||
import { useAppShellProps } from 'roleypoly/providers/appShellData';
|
||||
|
||||
export default () => {
|
||||
const Servers = () => {
|
||||
const { appShellProps } = useAppShellProps();
|
||||
return (
|
||||
<AppShell {...appShellProps}>
|
||||
<div></div>
|
||||
</AppShell>
|
||||
<>
|
||||
<Head>
|
||||
<title>Viewing your servers - Roleypoly</title>
|
||||
</Head>
|
||||
<ServersTemplate {...appShellProps} guilds={appShellProps.guilds || []} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Servers;
|
||||
|
|
|
@ -12966,6 +12966,11 @@ styled-jsx@3.3.2:
|
|||
stylis "3.5.4"
|
||||
stylis-rule-sheet "0.0.10"
|
||||
|
||||
styled-normalize@^8.0.7:
|
||||
version "8.0.7"
|
||||
resolved "https://registry.yarnpkg.com/styled-normalize/-/styled-normalize-8.0.7.tgz#e883bff6a0c59a65a39365a4eb9c6cf48372c61f"
|
||||
integrity sha512-qQV4O7B9g7ZUnStCwGde7Dc/mcFF/pz0Ha/LL7+j/r6uopf6kJCmmR7jCPQMCBrDkYiQ4xvw1hUoceVJkdaMuQ==
|
||||
|
||||
stylelint-config-prettier@^8.0.2:
|
||||
version "8.0.2"
|
||||
resolved "https://registry.yarnpkg.com/stylelint-config-prettier/-/stylelint-config-prettier-8.0.2.tgz#da9de33da4c56893cbe7e26df239a7374045e14e"
|
||||
|
|
Loading…
Add table
Reference in a new issue