mirror of
https://github.com/roleypoly/roleypoly.git
synced 2025-06-15 17:19:10 +00:00
chore: update prettier tab width for consistency (#175)
This commit is contained in:
parent
a931f8c69c
commit
f24d2fcc99
247 changed files with 7224 additions and 7375 deletions
|
@ -2,7 +2,7 @@ import * as React from 'react';
|
|||
import { DemoDiscord } from './DemoDiscord';
|
||||
|
||||
export default {
|
||||
title: 'Molecules/Role Demos',
|
||||
title: 'Molecules/Role Demos',
|
||||
};
|
||||
|
||||
export const Discord = () => <DemoDiscord />;
|
||||
|
|
|
@ -2,35 +2,35 @@ import { palette } from '@roleypoly/design-system/atoms/colors';
|
|||
import styled, { keyframes } from 'styled-components';
|
||||
|
||||
export const Base = styled.div`
|
||||
background-color: ${palette.discord100};
|
||||
border: solid 1px rgba(0, 0, 0, 0.15);
|
||||
border-radius: 3px;
|
||||
padding: 10px;
|
||||
user-select: none;
|
||||
background-color: ${palette.discord100};
|
||||
border: solid 1px rgba(0, 0, 0, 0.15);
|
||||
border-radius: 3px;
|
||||
padding: 10px;
|
||||
user-select: none;
|
||||
`;
|
||||
|
||||
export const Timestamp = styled.span`
|
||||
padding: 0 5px;
|
||||
font-size: 0.7em;
|
||||
opacity: 0.3;
|
||||
padding: 0 5px;
|
||||
font-size: 0.7em;
|
||||
opacity: 0.3;
|
||||
`;
|
||||
|
||||
export const TextParts = styled.span`
|
||||
padding: 0 5px;
|
||||
padding: 0 5px;
|
||||
`;
|
||||
|
||||
export const Username = styled(TextParts)`
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
`;
|
||||
|
||||
export const InputBox = styled.div`
|
||||
margin-top: 10px;
|
||||
background-color: ${palette.discord200};
|
||||
padding: 7px 10px;
|
||||
border-radius: 3px;
|
||||
margin-top: 10px;
|
||||
background-color: ${palette.discord200};
|
||||
padding: 7px 10px;
|
||||
border-radius: 3px;
|
||||
`;
|
||||
|
||||
const lineBlink = keyframes`
|
||||
|
@ -52,16 +52,16 @@ const lineBlink = keyframes`
|
|||
`;
|
||||
|
||||
export const Line = styled.div`
|
||||
background-color: ${palette.grey600};
|
||||
width: 1px;
|
||||
height: 1.5em;
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
right: -5px;
|
||||
animation: ${lineBlink} 0.5s ease-in-out infinite alternate-reverse;
|
||||
background-color: ${palette.grey600};
|
||||
width: 1px;
|
||||
height: 1.5em;
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
right: -5px;
|
||||
animation: ${lineBlink} 0.5s ease-in-out infinite alternate-reverse;
|
||||
`;
|
||||
|
||||
export const InputTextAlignment = styled.div`
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
`;
|
||||
|
|
|
@ -2,52 +2,52 @@ import { Typist } from '@roleypoly/design-system/atoms/typist';
|
|||
import { demoData } from '@roleypoly/types/demoData';
|
||||
import * as React from 'react';
|
||||
import {
|
||||
Base,
|
||||
InputBox,
|
||||
InputTextAlignment,
|
||||
Line,
|
||||
TextParts,
|
||||
Timestamp,
|
||||
Username,
|
||||
Base,
|
||||
InputBox,
|
||||
InputTextAlignment,
|
||||
Line,
|
||||
TextParts,
|
||||
Timestamp,
|
||||
Username,
|
||||
} from './DemoDiscord.styled';
|
||||
|
||||
export const DemoDiscord = () => {
|
||||
const time = new Date();
|
||||
const timeString = time.toTimeString();
|
||||
const time = new Date();
|
||||
const timeString = time.toTimeString();
|
||||
|
||||
const [easterEggCount, setEasterEggCount] = React.useState(0);
|
||||
const [easterEggCount, setEasterEggCount] = React.useState(0);
|
||||
|
||||
return (
|
||||
<Base>
|
||||
<Timestamp>
|
||||
{time.getHours() % 12}:{timeString.slice(3, 5)}
|
||||
{time.getHours() <= 12 ? 'AM' : 'PM'}
|
||||
</Timestamp>
|
||||
<Username onClick={() => setEasterEggCount(easterEggCount + 1)}>
|
||||
okano cat
|
||||
</Username>
|
||||
<TextParts>
|
||||
{easterEggCount >= 15
|
||||
? `NYAAAAAAA${'A'.repeat(easterEggCount - 15)}`
|
||||
: easterEggCount >= 11
|
||||
? `I'm.. I'm gonna...`
|
||||
: easterEggCount >= 10
|
||||
? `S-senpai... Be careful...`
|
||||
: easterEggCount >= 5
|
||||
? `H-hey... Stop that..`
|
||||
: `Hey, I'd like some roles!`}
|
||||
</TextParts>
|
||||
<InputBox>
|
||||
<InputTextAlignment>
|
||||
|
||||
<Typist
|
||||
resetTimeout={2000}
|
||||
charTimeout={75}
|
||||
lines={demoData.map((role) => `.iam ${role.name}`)}
|
||||
/>
|
||||
<Line />
|
||||
</InputTextAlignment>
|
||||
</InputBox>
|
||||
</Base>
|
||||
);
|
||||
return (
|
||||
<Base>
|
||||
<Timestamp>
|
||||
{time.getHours() % 12}:{timeString.slice(3, 5)}
|
||||
{time.getHours() <= 12 ? 'AM' : 'PM'}
|
||||
</Timestamp>
|
||||
<Username onClick={() => setEasterEggCount(easterEggCount + 1)}>
|
||||
okano cat
|
||||
</Username>
|
||||
<TextParts>
|
||||
{easterEggCount >= 15
|
||||
? `NYAAAAAAA${'A'.repeat(easterEggCount - 15)}`
|
||||
: easterEggCount >= 11
|
||||
? `I'm.. I'm gonna...`
|
||||
: easterEggCount >= 10
|
||||
? `S-senpai... Be careful...`
|
||||
: easterEggCount >= 5
|
||||
? `H-hey... Stop that..`
|
||||
: `Hey, I'd like some roles!`}
|
||||
</TextParts>
|
||||
<InputBox>
|
||||
<InputTextAlignment>
|
||||
|
||||
<Typist
|
||||
resetTimeout={2000}
|
||||
charTimeout={75}
|
||||
lines={demoData.map((role) => `.iam ${role.name}`)}
|
||||
/>
|
||||
<Line />
|
||||
</InputTextAlignment>
|
||||
</InputBox>
|
||||
</Base>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -2,7 +2,7 @@ import * as React from 'react';
|
|||
import { DemoPicker } from './DemoPicker';
|
||||
|
||||
export default {
|
||||
title: 'Molecules/Role Demos',
|
||||
title: 'Molecules/Role Demos',
|
||||
};
|
||||
|
||||
export const Picker = () => <DemoPicker />;
|
||||
|
|
|
@ -5,42 +5,42 @@ import * as React from 'react';
|
|||
import styled from 'styled-components';
|
||||
|
||||
const Container = styled.div`
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
height: 95px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
height: 95px;
|
||||
`;
|
||||
|
||||
const RoleWrap = styled.div`
|
||||
padding: 2.5px;
|
||||
display: inline-block;
|
||||
padding: 2.5px;
|
||||
display: inline-block;
|
||||
`;
|
||||
|
||||
export const DemoPicker = () => {
|
||||
const [selectedStates, setSelectedStates] = React.useState<
|
||||
{
|
||||
[key in RPCRole['id']]: boolean;
|
||||
}
|
||||
>(demoData.reduce((acc, role) => ({ ...acc, [role.id]: false }), {}));
|
||||
const [selectedStates, setSelectedStates] = React.useState<
|
||||
{
|
||||
[key in RPCRole['id']]: boolean;
|
||||
}
|
||||
>(demoData.reduce((acc, role) => ({ ...acc, [role.id]: false }), {}));
|
||||
|
||||
return (
|
||||
<Container>
|
||||
{demoData.map((role) => (
|
||||
<RoleWrap key={`role${role.id}`}>
|
||||
<Role
|
||||
role={role}
|
||||
selected={selectedStates[role.id]}
|
||||
onClick={() => {
|
||||
setSelectedStates({
|
||||
...selectedStates,
|
||||
[role.id]: !selectedStates[role.id],
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</RoleWrap>
|
||||
))}
|
||||
</Container>
|
||||
);
|
||||
return (
|
||||
<Container>
|
||||
{demoData.map((role) => (
|
||||
<RoleWrap key={`role${role.id}`}>
|
||||
<Role
|
||||
role={role}
|
||||
selected={selectedStates[role.id]}
|
||||
onClick={() => {
|
||||
setSelectedStates({
|
||||
...selectedStates,
|
||||
[role.id]: !selectedStates[role.id],
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</RoleWrap>
|
||||
))}
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -3,17 +3,17 @@ import { mockCategory, roleCategory, roleCategory2 } from '../../fixtures/storyD
|
|||
import { EditorCategory } from './EditorCategory';
|
||||
|
||||
export default {
|
||||
title: 'Molecules/Editor/Category',
|
||||
title: 'Molecules/Editor/Category',
|
||||
};
|
||||
|
||||
export const CategoryEditor = () => {
|
||||
const [categoryData, setCategoryData] = React.useState(mockCategory);
|
||||
return (
|
||||
<EditorCategory
|
||||
category={categoryData}
|
||||
onChange={(category) => setCategoryData(category)}
|
||||
uncategorizedRoles={roleCategory}
|
||||
guildRoles={[...roleCategory, ...roleCategory2]}
|
||||
/>
|
||||
);
|
||||
const [categoryData, setCategoryData] = React.useState(mockCategory);
|
||||
return (
|
||||
<EditorCategory
|
||||
category={categoryData}
|
||||
onChange={(category) => setCategoryData(category)}
|
||||
uncategorizedRoles={roleCategory}
|
||||
guildRoles={[...roleCategory, ...roleCategory2]}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import styled from 'styled-components';
|
||||
|
||||
export const RoleContainer = styled.div`
|
||||
display: flex;
|
||||
margin: 10px;
|
||||
display: flex;
|
||||
margin: 10px;
|
||||
`;
|
||||
|
|
|
@ -12,134 +12,133 @@ import { GoSearch } from 'react-icons/go';
|
|||
import { RoleContainer } from './EditorCategory.styled';
|
||||
|
||||
type Props = {
|
||||
category: Category;
|
||||
uncategorizedRoles: RoleType[];
|
||||
guildRoles: RoleType[];
|
||||
onChange: (category: Category) => void;
|
||||
category: Category;
|
||||
uncategorizedRoles: RoleType[];
|
||||
guildRoles: RoleType[];
|
||||
onChange: (category: Category) => void;
|
||||
};
|
||||
|
||||
const typeEnumToSwitch = (typeData: CategoryType) => {
|
||||
if (typeData === CategoryType.Single) {
|
||||
return 'Single';
|
||||
} else {
|
||||
return 'Multiple';
|
||||
}
|
||||
if (typeData === CategoryType.Single) {
|
||||
return 'Single';
|
||||
} else {
|
||||
return 'Multiple';
|
||||
}
|
||||
};
|
||||
|
||||
const switchToTypeEnum = (typeData: 'Single' | 'Multiple') => {
|
||||
if (typeData === 'Single') {
|
||||
return CategoryType.Single;
|
||||
} else {
|
||||
return CategoryType.Multi;
|
||||
}
|
||||
if (typeData === 'Single') {
|
||||
return CategoryType.Single;
|
||||
} else {
|
||||
return CategoryType.Multi;
|
||||
}
|
||||
};
|
||||
|
||||
export const EditorCategory = (props: Props) => {
|
||||
const [roleSearchPopoverActive, setRoleSearchPopoverActive] = React.useState(false);
|
||||
const [roleSearchTerm, updateSearchTerm] = React.useState('');
|
||||
const [roleSearchPopoverActive, setRoleSearchPopoverActive] = React.useState(false);
|
||||
const [roleSearchTerm, updateSearchTerm] = React.useState('');
|
||||
|
||||
const onUpdate = (
|
||||
key: keyof typeof props.category,
|
||||
pred?: (newValue: any) => any
|
||||
) => (newValue: any) => {
|
||||
props.onChange({
|
||||
...props.category,
|
||||
[key]: pred ? pred(newValue) : newValue,
|
||||
});
|
||||
};
|
||||
const onUpdate = (key: keyof typeof props.category, pred?: (newValue: any) => any) => (
|
||||
newValue: any
|
||||
) => {
|
||||
props.onChange({
|
||||
...props.category,
|
||||
[key]: pred ? pred(newValue) : newValue,
|
||||
});
|
||||
};
|
||||
|
||||
const handleRoleSelect = (role: RoleType) => {
|
||||
setRoleSearchPopoverActive(false);
|
||||
updateSearchTerm('');
|
||||
props.onChange({
|
||||
...props.category,
|
||||
roles: [...props.category.roles, role.id],
|
||||
});
|
||||
};
|
||||
const handleRoleSelect = (role: RoleType) => {
|
||||
setRoleSearchPopoverActive(false);
|
||||
updateSearchTerm('');
|
||||
props.onChange({
|
||||
...props.category,
|
||||
roles: [...props.category.roles, role.id],
|
||||
});
|
||||
};
|
||||
|
||||
const handleRoleDeselect = (role: RoleType) => () => {
|
||||
props.onChange({
|
||||
...props.category,
|
||||
roles: props.category.roles.filter((x) => x !== role.id),
|
||||
});
|
||||
};
|
||||
const handleRoleDeselect = (role: RoleType) => () => {
|
||||
props.onChange({
|
||||
...props.category,
|
||||
roles: props.category.roles.filter((x) => x !== role.id),
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Text>Category Name</Text>
|
||||
<TextInput
|
||||
placeholder="Pronouns, Political, Colors..."
|
||||
value={props.category.name}
|
||||
onChange={onUpdate('name', (x) => x.target.value)}
|
||||
/>
|
||||
return (
|
||||
<div>
|
||||
<Text>Category Name</Text>
|
||||
<TextInput
|
||||
placeholder="Pronouns, Political, Colors..."
|
||||
value={props.category.name}
|
||||
onChange={onUpdate('name', (x) => x.target.value)}
|
||||
/>
|
||||
|
||||
<Space />
|
||||
<Space />
|
||||
|
||||
<Text>Selection Type</Text>
|
||||
<div>
|
||||
<HorizontalSwitch
|
||||
items={['Multiple', 'Single']}
|
||||
value={typeEnumToSwitch(props.category.type)}
|
||||
onChange={onUpdate('type', switchToTypeEnum)}
|
||||
/>
|
||||
</div>
|
||||
<Text>Selection Type</Text>
|
||||
<div>
|
||||
<HorizontalSwitch
|
||||
items={['Multiple', 'Single']}
|
||||
value={typeEnumToSwitch(props.category.type)}
|
||||
onChange={onUpdate('type', switchToTypeEnum)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Space />
|
||||
<Space />
|
||||
|
||||
<Text>Visiblity</Text>
|
||||
<div>
|
||||
<HorizontalSwitch
|
||||
items={['Visible', 'Hidden']}
|
||||
value={props.category.hidden ? 'Hidden' : 'Visible'}
|
||||
onChange={onUpdate('hidden', (a) => a === 'Hidden')}
|
||||
/>
|
||||
</div>
|
||||
<Text>Visiblity</Text>
|
||||
<div>
|
||||
<HorizontalSwitch
|
||||
items={['Visible', 'Hidden']}
|
||||
value={props.category.hidden ? 'Hidden' : 'Visible'}
|
||||
onChange={onUpdate('hidden', (a) => a === 'Hidden')}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Space />
|
||||
<Text>Roles</Text>
|
||||
<Popover
|
||||
position={'top left'}
|
||||
headContent={null}
|
||||
active={roleSearchPopoverActive}
|
||||
onExit={() => setRoleSearchPopoverActive(false)}
|
||||
>
|
||||
{() => (
|
||||
<RoleSearch
|
||||
placeholder={'Type or drag a role...'}
|
||||
roles={props.uncategorizedRoles}
|
||||
onSelect={handleRoleSelect}
|
||||
searchTerm={roleSearchTerm}
|
||||
onSearchUpdate={(newTerm) => updateSearchTerm(newTerm)}
|
||||
/>
|
||||
)}
|
||||
</Popover>
|
||||
<FaderOpacity isVisible={!roleSearchPopoverActive}>
|
||||
<TextInputWithIcon
|
||||
icon={<GoSearch />}
|
||||
placeholder={'Type or drag a role...'}
|
||||
onFocus={() => setRoleSearchPopoverActive(true)}
|
||||
value={roleSearchTerm}
|
||||
onChange={(x) => updateSearchTerm(x.target.value)}
|
||||
/>
|
||||
<RoleContainer>
|
||||
{props.category.roles.map((id) => {
|
||||
const role = props.guildRoles.find((x) => x.id === id);
|
||||
if (!role) {
|
||||
return <></>;
|
||||
}
|
||||
<Space />
|
||||
<Text>Roles</Text>
|
||||
<Popover
|
||||
position={'top left'}
|
||||
headContent={null}
|
||||
active={roleSearchPopoverActive}
|
||||
onExit={() => setRoleSearchPopoverActive(false)}
|
||||
>
|
||||
{() => (
|
||||
<RoleSearch
|
||||
placeholder={'Type or drag a role...'}
|
||||
roles={props.uncategorizedRoles}
|
||||
onSelect={handleRoleSelect}
|
||||
searchTerm={roleSearchTerm}
|
||||
onSearchUpdate={(newTerm) => updateSearchTerm(newTerm)}
|
||||
/>
|
||||
)}
|
||||
</Popover>
|
||||
<FaderOpacity isVisible={!roleSearchPopoverActive}>
|
||||
<TextInputWithIcon
|
||||
icon={<GoSearch />}
|
||||
placeholder={'Type or drag a role...'}
|
||||
onFocus={() => setRoleSearchPopoverActive(true)}
|
||||
value={roleSearchTerm}
|
||||
onChange={(x) => updateSearchTerm(x.target.value)}
|
||||
/>
|
||||
<RoleContainer>
|
||||
{props.category.roles.map((id) => {
|
||||
const role = props.guildRoles.find((x) => x.id === id);
|
||||
if (!role) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Role
|
||||
role={role}
|
||||
selected={false}
|
||||
key={id}
|
||||
type="delete"
|
||||
onClick={handleRoleDeselect(role)}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</RoleContainer>
|
||||
</FaderOpacity>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<Role
|
||||
role={role}
|
||||
selected={false}
|
||||
key={id}
|
||||
type="delete"
|
||||
onClick={handleRoleDeselect(role)}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</RoleContainer>
|
||||
</FaderOpacity>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -2,25 +2,25 @@ import * as React from 'react';
|
|||
import { ErrorBanner } from './ErrorBanner';
|
||||
|
||||
export default {
|
||||
title: 'Molecules/Error Banner',
|
||||
argTypes: {
|
||||
english: { control: 'text' },
|
||||
japanese: { control: 'text' },
|
||||
friendlyCode: { control: 'text' },
|
||||
},
|
||||
args: {
|
||||
english: 'Oh no! I lost it!',
|
||||
japanese: 'ちょっとにんげんだよ',
|
||||
friendlyCode: '404',
|
||||
},
|
||||
title: 'Molecules/Error Banner',
|
||||
argTypes: {
|
||||
english: { control: 'text' },
|
||||
japanese: { control: 'text' },
|
||||
friendlyCode: { control: 'text' },
|
||||
},
|
||||
args: {
|
||||
english: 'Oh no! I lost it!',
|
||||
japanese: 'ちょっとにんげんだよ',
|
||||
friendlyCode: '404',
|
||||
},
|
||||
};
|
||||
|
||||
export const ErrorBanner_ = ({ english, japanese, friendlyCode }) => (
|
||||
<ErrorBanner
|
||||
message={{
|
||||
english,
|
||||
japanese,
|
||||
friendlyCode,
|
||||
}}
|
||||
/>
|
||||
<ErrorBanner
|
||||
message={{
|
||||
english,
|
||||
japanese,
|
||||
friendlyCode,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -4,38 +4,38 @@ import { text300, text500, text700 } from '@roleypoly/design-system/atoms/typogr
|
|||
import styled, { css } from 'styled-components';
|
||||
|
||||
export const ErrorWrapper = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
${onSmallScreen(css`
|
||||
display: block;
|
||||
text-align: center;
|
||||
`)}
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
${onSmallScreen(css`
|
||||
display: block;
|
||||
text-align: center;
|
||||
`)}
|
||||
`;
|
||||
|
||||
export const ErrorDivider = styled.div`
|
||||
width: 1px;
|
||||
height: 3em;
|
||||
background: ${palette.grey600};
|
||||
margin: 0 1em;
|
||||
${onSmallScreen(css`
|
||||
display: none;
|
||||
`)}
|
||||
width: 1px;
|
||||
height: 3em;
|
||||
background: ${palette.grey600};
|
||||
margin: 0 1em;
|
||||
${onSmallScreen(css`
|
||||
display: none;
|
||||
`)}
|
||||
`;
|
||||
|
||||
export const ErrorSideCode = styled.div`
|
||||
${text700}
|
||||
${onSmallScreen(css`
|
||||
margin-bottom: 0.4em;
|
||||
`)}
|
||||
${text700}
|
||||
${onSmallScreen(css`
|
||||
margin-bottom: 0.4em;
|
||||
`)}
|
||||
`;
|
||||
|
||||
export const ErrorText = styled.div`
|
||||
${text500}
|
||||
${text500}
|
||||
`;
|
||||
|
||||
export const ErrorTextLower = styled.div`
|
||||
${text300}
|
||||
${text300}
|
||||
|
||||
color: ${palette.taupe500};
|
||||
color: ${palette.taupe500};
|
||||
`;
|
||||
|
|
|
@ -1,29 +1,29 @@
|
|||
import * as React from 'react';
|
||||
import {
|
||||
ErrorDivider,
|
||||
ErrorSideCode,
|
||||
ErrorText,
|
||||
ErrorTextLower,
|
||||
ErrorWrapper,
|
||||
ErrorDivider,
|
||||
ErrorSideCode,
|
||||
ErrorText,
|
||||
ErrorTextLower,
|
||||
ErrorWrapper,
|
||||
} from './ErrorBanner.styled';
|
||||
|
||||
export type ErrorMessage = {
|
||||
english: string;
|
||||
japanese?: string;
|
||||
friendlyCode?: string;
|
||||
english: string;
|
||||
japanese?: string;
|
||||
friendlyCode?: string;
|
||||
};
|
||||
|
||||
type ErrorBannerProps = {
|
||||
message: Required<ErrorMessage>;
|
||||
message: Required<ErrorMessage>;
|
||||
};
|
||||
|
||||
export const ErrorBanner = (props: ErrorBannerProps) => (
|
||||
<ErrorWrapper>
|
||||
<ErrorSideCode>{props.message.friendlyCode}</ErrorSideCode>
|
||||
<ErrorDivider />
|
||||
<div>
|
||||
<ErrorText>{props.message.english}</ErrorText>
|
||||
<ErrorTextLower>{props.message.japanese}</ErrorTextLower>
|
||||
</div>
|
||||
</ErrorWrapper>
|
||||
<ErrorWrapper>
|
||||
<ErrorSideCode>{props.message.friendlyCode}</ErrorSideCode>
|
||||
<ErrorDivider />
|
||||
<div>
|
||||
<ErrorText>{props.message.english}</ErrorText>
|
||||
<ErrorTextLower>{props.message.japanese}</ErrorTextLower>
|
||||
</div>
|
||||
</ErrorWrapper>
|
||||
);
|
||||
|
|
|
@ -1,94 +1,70 @@
|
|||
import * as React from 'react';
|
||||
|
||||
type FlagsProps = {
|
||||
width?: number | string;
|
||||
height?: number | string;
|
||||
width?: number | string;
|
||||
height?: number | string;
|
||||
};
|
||||
|
||||
export const Flags = (props: FlagsProps) => (
|
||||
<svg width={props.width} height={props.height} viewBox="0 0 3372 900" version="1.1">
|
||||
<defs>
|
||||
<rect id="path-3" x="1772" y="0" width="1600" height="900" rx="100"></rect>
|
||||
</defs>
|
||||
<g id="Page-1" stroke="none" strokeWidth="1" fill="none" fillRule="evenodd">
|
||||
<g id="Rectangle-6"></g>
|
||||
<g id="Trans">
|
||||
<rect
|
||||
id="Rectangle"
|
||||
fill="#55CDFC"
|
||||
x="0"
|
||||
y="0"
|
||||
width="1600"
|
||||
height="900"
|
||||
rx="100"
|
||||
></rect>
|
||||
<rect
|
||||
id="Rectangle-2"
|
||||
fill="#F7A8B8"
|
||||
x="0"
|
||||
y="170"
|
||||
width="1600"
|
||||
height="560"
|
||||
></rect>
|
||||
<rect
|
||||
id="Rectangle-3"
|
||||
fill="#FFFFFF"
|
||||
x="0"
|
||||
y="350"
|
||||
width="1600"
|
||||
height="200"
|
||||
></rect>
|
||||
</g>
|
||||
<mask id="mask-4" fill="white">
|
||||
<use href="#path-3"></use>
|
||||
</mask>
|
||||
<g id="Rectangle-5"></g>
|
||||
<g id="Geyy" mask="url(#mask-4)">
|
||||
<g transform="translate(1772.000000, 0.000000)" id="Rectangle-4">
|
||||
<rect
|
||||
fill="#F9238B"
|
||||
x="0"
|
||||
y="0"
|
||||
width="1600"
|
||||
height="151.006711"
|
||||
></rect>
|
||||
<rect
|
||||
fill="#FB7B04"
|
||||
x="0"
|
||||
y="150"
|
||||
width="1600"
|
||||
height="151.006711"
|
||||
></rect>
|
||||
<rect
|
||||
fill="#FFCA66"
|
||||
x="0"
|
||||
y="300"
|
||||
width="1600"
|
||||
height="151.006711"
|
||||
></rect>
|
||||
<rect
|
||||
fill="#00B289"
|
||||
x="0"
|
||||
y="450"
|
||||
width="1600"
|
||||
height="151.006711"
|
||||
></rect>
|
||||
<rect
|
||||
fill="#5A38B5"
|
||||
x="0"
|
||||
y="598.993289"
|
||||
width="1600"
|
||||
height="151.006711"
|
||||
></rect>
|
||||
<rect
|
||||
fill="#B413F5"
|
||||
x="0"
|
||||
y="748.993289"
|
||||
width="1600"
|
||||
height="151.006711"
|
||||
></rect>
|
||||
</g>
|
||||
</g>
|
||||
<svg width={props.width} height={props.height} viewBox="0 0 3372 900" version="1.1">
|
||||
<defs>
|
||||
<rect id="path-3" x="1772" y="0" width="1600" height="900" rx="100"></rect>
|
||||
</defs>
|
||||
<g id="Page-1" stroke="none" strokeWidth="1" fill="none" fillRule="evenodd">
|
||||
<g id="Rectangle-6"></g>
|
||||
<g id="Trans">
|
||||
<rect
|
||||
id="Rectangle"
|
||||
fill="#55CDFC"
|
||||
x="0"
|
||||
y="0"
|
||||
width="1600"
|
||||
height="900"
|
||||
rx="100"
|
||||
></rect>
|
||||
<rect
|
||||
id="Rectangle-2"
|
||||
fill="#F7A8B8"
|
||||
x="0"
|
||||
y="170"
|
||||
width="1600"
|
||||
height="560"
|
||||
></rect>
|
||||
<rect
|
||||
id="Rectangle-3"
|
||||
fill="#FFFFFF"
|
||||
x="0"
|
||||
y="350"
|
||||
width="1600"
|
||||
height="200"
|
||||
></rect>
|
||||
</g>
|
||||
<mask id="mask-4" fill="white">
|
||||
<use href="#path-3"></use>
|
||||
</mask>
|
||||
<g id="Rectangle-5"></g>
|
||||
<g id="Geyy" mask="url(#mask-4)">
|
||||
<g transform="translate(1772.000000, 0.000000)" id="Rectangle-4">
|
||||
<rect fill="#F9238B" x="0" y="0" width="1600" height="151.006711"></rect>
|
||||
<rect fill="#FB7B04" x="0" y="150" width="1600" height="151.006711"></rect>
|
||||
<rect fill="#FFCA66" x="0" y="300" width="1600" height="151.006711"></rect>
|
||||
<rect fill="#00B289" x="0" y="450" width="1600" height="151.006711"></rect>
|
||||
<rect
|
||||
fill="#5A38B5"
|
||||
x="0"
|
||||
y="598.993289"
|
||||
width="1600"
|
||||
height="151.006711"
|
||||
></rect>
|
||||
<rect
|
||||
fill="#B413F5"
|
||||
x="0"
|
||||
y="748.993289"
|
||||
width="1600"
|
||||
height="151.006711"
|
||||
></rect>
|
||||
</g>
|
||||
</svg>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
|
|
|
@ -2,8 +2,8 @@ import * as React from 'react';
|
|||
import { Footer as FooterComponent } from './Footer';
|
||||
|
||||
export default {
|
||||
title: 'Molecules',
|
||||
component: FooterComponent,
|
||||
title: 'Molecules',
|
||||
component: FooterComponent,
|
||||
};
|
||||
|
||||
export const Footer = (args) => <FooterComponent {...args} />;
|
||||
|
|
|
@ -3,28 +3,28 @@ import { transitions } from '@roleypoly/design-system/atoms/timings';
|
|||
import styled from 'styled-components';
|
||||
|
||||
export const FooterWrapper = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
|
||||
a {
|
||||
color: ${palette.taupe500};
|
||||
text-decoration: none;
|
||||
transition: color ${transitions.actionable}s ease-in-out;
|
||||
&:hover {
|
||||
color: ${palette.taupe600};
|
||||
}
|
||||
a {
|
||||
color: ${palette.taupe500};
|
||||
text-decoration: none;
|
||||
transition: color ${transitions.actionable}s ease-in-out;
|
||||
&:hover {
|
||||
color: ${palette.taupe600};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const HoverColor = styled.div`
|
||||
opacity: 0.3;
|
||||
filter: saturate(0);
|
||||
transition: all ${transitions.in2in}s ease-in-out;
|
||||
opacity: 0.3;
|
||||
filter: saturate(0);
|
||||
transition: all ${transitions.in2in}s ease-in-out;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
filter: none;
|
||||
}
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
filter: none;
|
||||
}
|
||||
`;
|
||||
|
|
|
@ -7,21 +7,21 @@ import { FooterWrapper, HoverColor } from './Footer.styled';
|
|||
const year = new Date().getFullYear();
|
||||
|
||||
export const Footer = () => (
|
||||
<FooterWrapper>
|
||||
<AmbientLarge>
|
||||
<div>
|
||||
© {year} Roleypoly – Made with{' '}
|
||||
<FaHeart size={'0.8em'} color={'#fe4365'} />
|
||||
in Raleigh, NC
|
||||
</div>
|
||||
<div>
|
||||
<a href="https://discord.gg/Xj6rK3E">Discord/Support</a> –
|
||||
<a href="https://patreon.com/kata">Patreon</a> –
|
||||
<a href="https://github.com/roleypoly">GitHub</a>
|
||||
</div>
|
||||
<HoverColor>
|
||||
<Flags height={'1em'} />
|
||||
</HoverColor>
|
||||
</AmbientLarge>
|
||||
</FooterWrapper>
|
||||
<FooterWrapper>
|
||||
<AmbientLarge>
|
||||
<div>
|
||||
© {year} Roleypoly – Made with{' '}
|
||||
<FaHeart size={'0.8em'} color={'#fe4365'} />
|
||||
in Raleigh, NC
|
||||
</div>
|
||||
<div>
|
||||
<a href="https://discord.gg/Xj6rK3E">Discord/Support</a> –
|
||||
<a href="https://patreon.com/kata">Patreon</a> –
|
||||
<a href="https://github.com/roleypoly">GitHub</a>
|
||||
</div>
|
||||
<HoverColor>
|
||||
<Flags height={'1em'} />
|
||||
</HoverColor>
|
||||
</AmbientLarge>
|
||||
</FooterWrapper>
|
||||
);
|
||||
|
|
|
@ -4,18 +4,18 @@ import { mastheadSlugs } from '../../fixtures/storyData';
|
|||
import { GuildNav } from './GuildNav';
|
||||
|
||||
export default {
|
||||
title: 'Molecules/Guild Nav',
|
||||
component: GuildNav,
|
||||
title: 'Molecules/Guild Nav',
|
||||
component: GuildNav,
|
||||
};
|
||||
|
||||
export const HasGuilds = () => (
|
||||
<PopoverBase active>
|
||||
<GuildNav guilds={mastheadSlugs} />
|
||||
</PopoverBase>
|
||||
<PopoverBase active>
|
||||
<GuildNav guilds={mastheadSlugs} />
|
||||
</PopoverBase>
|
||||
);
|
||||
|
||||
export const NoGuilds = () => (
|
||||
<PopoverBase active>
|
||||
<GuildNav guilds={[]} />
|
||||
</PopoverBase>
|
||||
<PopoverBase active>
|
||||
<GuildNav guilds={[]} />
|
||||
</PopoverBase>
|
||||
);
|
||||
|
|
|
@ -3,19 +3,19 @@ import { transitions } from '@roleypoly/design-system/atoms/timings';
|
|||
import styled from 'styled-components';
|
||||
|
||||
export const GuildNavItem = styled.a`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
transition: border ${transitions.in2in}s ease-in-out;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 3px;
|
||||
box-sizing: border-box;
|
||||
margin: 5px;
|
||||
user-select: none;
|
||||
color: unset;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
transition: border ${transitions.in2in}s ease-in-out;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 3px;
|
||||
box-sizing: border-box;
|
||||
margin: 5px;
|
||||
user-select: none;
|
||||
color: unset;
|
||||
text-decoration: none;
|
||||
|
||||
&:hover {
|
||||
border-color: ${palette.taupe300};
|
||||
cursor: pointer;
|
||||
}
|
||||
&:hover {
|
||||
border-color: ${palette.taupe300};
|
||||
cursor: pointer;
|
||||
}
|
||||
`;
|
||||
|
|
|
@ -8,61 +8,61 @@ import ReactTooltip from 'react-tooltip';
|
|||
import { GuildNavItem } from './GuildNav.styled';
|
||||
|
||||
type Props = {
|
||||
guilds: GuildSlug[];
|
||||
recentGuilds: string[];
|
||||
guilds: GuildSlug[];
|
||||
recentGuilds: string[];
|
||||
};
|
||||
|
||||
const tooltipId = 'guildnav';
|
||||
|
||||
const Badges = (props: { guild: GuildSlug }) => {
|
||||
return React.useMemo(() => {
|
||||
if (props.guild.permissionLevel === UserGuildPermissions.Admin) {
|
||||
return <GoStar data-tip="Administrator" data-for={tooltipId} />;
|
||||
}
|
||||
return React.useMemo(() => {
|
||||
if (props.guild.permissionLevel === UserGuildPermissions.Admin) {
|
||||
return <GoStar data-tip="Administrator" data-for={tooltipId} />;
|
||||
}
|
||||
|
||||
if (props.guild.permissionLevel === UserGuildPermissions.Manager) {
|
||||
return <GoZap data-tip="Role Editor" data-for={tooltipId} />;
|
||||
}
|
||||
if (props.guild.permissionLevel === UserGuildPermissions.Manager) {
|
||||
return <GoZap data-tip="Role Editor" data-for={tooltipId} />;
|
||||
}
|
||||
|
||||
return null;
|
||||
}, [props.guild.permissionLevel]);
|
||||
return null;
|
||||
}, [props.guild.permissionLevel]);
|
||||
};
|
||||
|
||||
const NavList = (props: { guilds: Props['guilds'] }) => (
|
||||
<>
|
||||
{props.guilds.map((guild) => (
|
||||
<GuildNavItem href={`/s/${guild.id}`} key={guild.id}>
|
||||
<NavSlug guild={guild || null} key={guild.id} />
|
||||
<Badges guild={guild} />
|
||||
</GuildNavItem>
|
||||
))}
|
||||
</>
|
||||
<>
|
||||
{props.guilds.map((guild) => (
|
||||
<GuildNavItem href={`/s/${guild.id}`} key={guild.id}>
|
||||
<NavSlug guild={guild || null} key={guild.id} />
|
||||
<Badges guild={guild} />
|
||||
</GuildNavItem>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
|
||||
export const GuildNav = (props: Props) => {
|
||||
const { sortedGuildSlugs, recentGuildSlugs } = getRecentAndSortedGuilds(
|
||||
props.guilds,
|
||||
props.recentGuilds
|
||||
);
|
||||
const { sortedGuildSlugs, recentGuildSlugs } = getRecentAndSortedGuilds(
|
||||
props.guilds,
|
||||
props.recentGuilds
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Scrollbars
|
||||
universal
|
||||
autoHide
|
||||
// autoHeight
|
||||
style={{ height: 'calc(100vh - 45px - 1.4em)', overflowX: 'hidden' }}
|
||||
>
|
||||
{recentGuildSlugs && (
|
||||
<>
|
||||
<div>Recents</div>
|
||||
<NavList guilds={recentGuildSlugs} />
|
||||
<div>All Guilds</div>
|
||||
</>
|
||||
)}
|
||||
<NavList guilds={sortedGuildSlugs} />
|
||||
<ReactTooltip id={tooltipId} />
|
||||
</Scrollbars>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div>
|
||||
<Scrollbars
|
||||
universal
|
||||
autoHide
|
||||
// autoHeight
|
||||
style={{ height: 'calc(100vh - 45px - 1.4em)', overflowX: 'hidden' }}
|
||||
>
|
||||
{recentGuildSlugs && (
|
||||
<>
|
||||
<div>Recents</div>
|
||||
<NavList guilds={recentGuildSlugs} />
|
||||
<div>All Guilds</div>
|
||||
</>
|
||||
)}
|
||||
<NavList guilds={sortedGuildSlugs} />
|
||||
<ReactTooltip id={tooltipId} />
|
||||
</Scrollbars>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -2,13 +2,13 @@ import * as React from 'react';
|
|||
import { HelpStoryWrapper } from './storyDecorator';
|
||||
|
||||
export default {
|
||||
title: 'Molecules/Help Page',
|
||||
decorators: [HelpStoryWrapper],
|
||||
title: 'Molecules/Help Page',
|
||||
decorators: [HelpStoryWrapper],
|
||||
};
|
||||
|
||||
export const Base = () => (
|
||||
<>
|
||||
<h1>What is the world but vibrations?</h1>
|
||||
<p>Vibrations that synchronize and tie it together, running free forever.</p>
|
||||
</>
|
||||
<>
|
||||
<h1>What is the world but vibrations?</h1>
|
||||
<p>Vibrations that synchronize and tie it together, running free forever.</p>
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -3,19 +3,19 @@ import * as React from 'react';
|
|||
import styled from 'styled-components';
|
||||
|
||||
export type HelpPageProps = {
|
||||
children: React.ReactNode;
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
const Container = styled.div`
|
||||
background: ${palette.taupe300};
|
||||
padding: 2em 3em;
|
||||
width: 1024px;
|
||||
max-width: 98vw;
|
||||
margin: 0 auto;
|
||||
margin-top: 75px;
|
||||
box-sizing: border-box;
|
||||
background: ${palette.taupe300};
|
||||
padding: 2em 3em;
|
||||
width: 1024px;
|
||||
max-width: 98vw;
|
||||
margin: 0 auto;
|
||||
margin-top: 75px;
|
||||
box-sizing: border-box;
|
||||
`;
|
||||
|
||||
export const HelpPageBase = (props: HelpPageProps) => (
|
||||
<Container>{props.children}</Container>
|
||||
<Container>{props.children}</Container>
|
||||
);
|
||||
|
|
|
@ -3,7 +3,7 @@ import * as React from 'react';
|
|||
import { HelpPageBase } from './HelpPageBase';
|
||||
|
||||
export const HelpStoryWrapper = (storyFn: any): React.ReactNode => (
|
||||
<Content>
|
||||
<HelpPageBase>{storyFn()}</HelpPageBase>
|
||||
</Content>
|
||||
<Content>
|
||||
<HelpPageBase>{storyFn()}</HelpPageBase>
|
||||
</Content>
|
||||
);
|
||||
|
|
|
@ -3,8 +3,8 @@ import { guild } from '../../fixtures/storyData';
|
|||
import { NavSlug } from './NavSlug';
|
||||
|
||||
export default {
|
||||
title: 'Molecules/Server Slug',
|
||||
component: NavSlug,
|
||||
title: 'Molecules/Server Slug',
|
||||
component: NavSlug,
|
||||
};
|
||||
|
||||
export const Empty = () => <NavSlug guild={null} />;
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
import styled from 'styled-components';
|
||||
|
||||
export const SlugContainer = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
padding: 5px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
padding: 5px;
|
||||
`;
|
||||
|
||||
export const SlugName = styled.div`
|
||||
padding: 0 10px;
|
||||
position: relative;
|
||||
top: -1px;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
padding: 0 10px;
|
||||
position: relative;
|
||||
top: -1px;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
`;
|
||||
|
|
|
@ -5,23 +5,19 @@ import { GoOrganization } from 'react-icons/go';
|
|||
import { SlugContainer, SlugName } from './NavSlug.styled';
|
||||
|
||||
type Props = {
|
||||
guild: GuildSlug | null;
|
||||
guild: GuildSlug | null;
|
||||
};
|
||||
|
||||
export const NavSlug = (props: Props) => (
|
||||
<SlugContainer>
|
||||
<Avatar
|
||||
hash={props.guild ? props.guild.icon : undefined}
|
||||
src={
|
||||
props.guild
|
||||
? utils.avatarHash(props.guild.id, props.guild.icon)
|
||||
: undefined
|
||||
}
|
||||
deliberatelyEmpty={!props.guild}
|
||||
size={35}
|
||||
>
|
||||
{props.guild ? utils.initialsFromName(props.guild.name) : <GoOrganization />}
|
||||
</Avatar>
|
||||
<SlugName>{props.guild?.name || <>Your Guilds</>}</SlugName>
|
||||
</SlugContainer>
|
||||
<SlugContainer>
|
||||
<Avatar
|
||||
hash={props.guild ? props.guild.icon : undefined}
|
||||
src={props.guild ? utils.avatarHash(props.guild.id, props.guild.icon) : undefined}
|
||||
deliberatelyEmpty={!props.guild}
|
||||
size={35}
|
||||
>
|
||||
{props.guild ? utils.initialsFromName(props.guild.name) : <GoOrganization />}
|
||||
</Avatar>
|
||||
<SlugName>{props.guild?.name || <>Your Guilds</>}</SlugName>
|
||||
</SlugContainer>
|
||||
);
|
||||
|
|
|
@ -3,32 +3,32 @@ import { mockCategory, roleCategory, roleWikiData } from '../../fixtures/storyDa
|
|||
import { PickerCategory } from './PickerCategory';
|
||||
|
||||
export default {
|
||||
title: 'Molecules/Picker Category',
|
||||
component: PickerCategory,
|
||||
args: {
|
||||
title: 'Pronouns',
|
||||
roles: roleCategory,
|
||||
category: mockCategory,
|
||||
selectedRoles: [],
|
||||
},
|
||||
title: 'Molecules/Picker Category',
|
||||
component: PickerCategory,
|
||||
args: {
|
||||
title: 'Pronouns',
|
||||
roles: roleCategory,
|
||||
category: mockCategory,
|
||||
selectedRoles: [],
|
||||
},
|
||||
};
|
||||
|
||||
export const Default = (args) => {
|
||||
return <PickerCategory {...args} />;
|
||||
return <PickerCategory {...args} />;
|
||||
};
|
||||
export const Single = (args) => {
|
||||
return <PickerCategory {...args} type="single" />;
|
||||
return <PickerCategory {...args} type="single" />;
|
||||
};
|
||||
Single.args = {
|
||||
type: 'single',
|
||||
type: 'single',
|
||||
};
|
||||
export const Multi = (args) => {
|
||||
return <PickerCategory {...args} type="single" />;
|
||||
return <PickerCategory {...args} type="single" />;
|
||||
};
|
||||
Multi.args = {
|
||||
type: 'multi',
|
||||
type: 'multi',
|
||||
};
|
||||
|
||||
export const Wiki = (args) => {
|
||||
return <PickerCategory {...args} wikiMode roleWikiData={roleWikiData} />;
|
||||
return <PickerCategory {...args} wikiMode roleWikiData={roleWikiData} />;
|
||||
};
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
import styled from 'styled-components';
|
||||
|
||||
export const Head = styled.div`
|
||||
margin: 7px 5px;
|
||||
line-height: 200%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin: 7px 5px;
|
||||
line-height: 200%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
`;
|
||||
|
||||
export const HeadTitle = styled.div`
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
`;
|
||||
|
||||
export const HeadSub = styled.div`
|
||||
flex-shrink: 0;
|
||||
margin-top: -4px;
|
||||
flex-shrink: 0;
|
||||
margin-top: -4px;
|
||||
`;
|
||||
|
|
|
@ -8,57 +8,57 @@ import styled from 'styled-components';
|
|||
import { Head, HeadSub, HeadTitle } from './PickerCategory.styled';
|
||||
|
||||
export type CategoryProps = {
|
||||
title: string;
|
||||
roles: RPCRole[];
|
||||
category: RPCCategory;
|
||||
selectedRoles: string[];
|
||||
onChange: (role: RPCRole) => (newState: boolean) => void;
|
||||
type: 'single' | 'multi';
|
||||
title: string;
|
||||
roles: RPCRole[];
|
||||
category: RPCCategory;
|
||||
selectedRoles: string[];
|
||||
onChange: (role: RPCRole) => (newState: boolean) => void;
|
||||
type: 'single' | 'multi';
|
||||
} & (
|
||||
| {
|
||||
wikiMode: true;
|
||||
roleWikiData: { [roleId: string]: string };
|
||||
}
|
||||
| {
|
||||
wikiMode: false;
|
||||
}
|
||||
| {
|
||||
wikiMode: true;
|
||||
roleWikiData: { [roleId: string]: string };
|
||||
}
|
||||
| {
|
||||
wikiMode: false;
|
||||
}
|
||||
);
|
||||
|
||||
const Category = styled.div`
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
`;
|
||||
|
||||
const Container = styled.div`
|
||||
overflow: hidden;
|
||||
padding: 5px;
|
||||
overflow: hidden;
|
||||
padding: 5px;
|
||||
`;
|
||||
|
||||
export const PickerCategory = (props: CategoryProps) => (
|
||||
<div>
|
||||
<Head>
|
||||
<HeadTitle>
|
||||
<LargeText>{props.title}</LargeText>
|
||||
</HeadTitle>
|
||||
{props.type === 'single' && (
|
||||
<HeadSub>
|
||||
<AmbientLarge>Pick one</AmbientLarge>
|
||||
</HeadSub>
|
||||
)}
|
||||
</Head>
|
||||
<Category>
|
||||
{sortBy(props.roles, 'position').map((role, idx) => (
|
||||
<Container key={idx}>
|
||||
<Role
|
||||
role={role}
|
||||
selected={props.selectedRoles.includes(role.id)}
|
||||
onClick={props.onChange(role)}
|
||||
disabled={role.safety !== RoleSafety.Safe}
|
||||
tooltipId={props.category.id}
|
||||
/>
|
||||
</Container>
|
||||
))}
|
||||
</Category>
|
||||
<ReactTooltip id={props.category.id} />
|
||||
</div>
|
||||
<div>
|
||||
<Head>
|
||||
<HeadTitle>
|
||||
<LargeText>{props.title}</LargeText>
|
||||
</HeadTitle>
|
||||
{props.type === 'single' && (
|
||||
<HeadSub>
|
||||
<AmbientLarge>Pick one</AmbientLarge>
|
||||
</HeadSub>
|
||||
)}
|
||||
</Head>
|
||||
<Category>
|
||||
{sortBy(props.roles, 'position').map((role, idx) => (
|
||||
<Container key={idx}>
|
||||
<Role
|
||||
role={role}
|
||||
selected={props.selectedRoles.includes(role.id)}
|
||||
onClick={props.onChange(role)}
|
||||
disabled={role.safety !== RoleSafety.Safe}
|
||||
tooltipId={props.category.id}
|
||||
/>
|
||||
</Container>
|
||||
))}
|
||||
</Category>
|
||||
<ReactTooltip id={props.category.id} />
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -3,11 +3,11 @@ import { mastheadSlugs } from '../../fixtures/storyData';
|
|||
import { PreauthGreeting } from './PreauthGreeting';
|
||||
|
||||
export default {
|
||||
title: 'Molecules/Preauth/Greeting',
|
||||
component: PreauthGreeting,
|
||||
args: {
|
||||
guildSlug: mastheadSlugs[0],
|
||||
},
|
||||
title: 'Molecules/Preauth/Greeting',
|
||||
component: PreauthGreeting,
|
||||
args: {
|
||||
guildSlug: mastheadSlugs[0],
|
||||
},
|
||||
};
|
||||
|
||||
export const Greeting = (args) => <PreauthGreeting {...args} />;
|
||||
|
|
|
@ -6,36 +6,30 @@ import * as React from 'react';
|
|||
import styled from 'styled-components';
|
||||
|
||||
type GreetingProps = {
|
||||
guildSlug: GuildSlug;
|
||||
guildSlug: GuildSlug;
|
||||
};
|
||||
|
||||
const Center = styled.div`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
`;
|
||||
|
||||
export const PreauthGreeting = (props: GreetingProps) => (
|
||||
<Center>
|
||||
<Avatar
|
||||
size={64}
|
||||
src={avatarUtils.avatarHash(
|
||||
props.guildSlug.id,
|
||||
props.guildSlug.icon,
|
||||
'icons',
|
||||
512
|
||||
)}
|
||||
hash={props.guildSlug.icon}
|
||||
>
|
||||
{avatarUtils.initialsFromName(props.guildSlug.name)}
|
||||
</Avatar>
|
||||
<AccentTitle>
|
||||
Hi there. <b>{props.guildSlug.name}</b> uses Roleypoly to help assign you
|
||||
roles.
|
||||
</AccentTitle>
|
||||
<Space />
|
||||
<Space />
|
||||
</Center>
|
||||
<Center>
|
||||
<Avatar
|
||||
size={64}
|
||||
src={avatarUtils.avatarHash(props.guildSlug.id, props.guildSlug.icon, 'icons', 512)}
|
||||
hash={props.guildSlug.icon}
|
||||
>
|
||||
{avatarUtils.initialsFromName(props.guildSlug.name)}
|
||||
</Avatar>
|
||||
<AccentTitle>
|
||||
Hi there. <b>{props.guildSlug.name}</b> uses Roleypoly to help assign you roles.
|
||||
</AccentTitle>
|
||||
<Space />
|
||||
<Space />
|
||||
</Center>
|
||||
);
|
||||
|
|
|
@ -7,17 +7,17 @@ const onReset = jest.fn();
|
|||
const onSubmit = jest.fn();
|
||||
|
||||
it('calls onReset when reset is clicked', () => {
|
||||
const view = shallow(<ResetSubmit onSubmit={onSubmit} onReset={onReset} />);
|
||||
const view = shallow(<ResetSubmit onSubmit={onSubmit} onReset={onReset} />);
|
||||
|
||||
view.find(Button).at(0).simulate('click');
|
||||
view.find(Button).at(0).simulate('click');
|
||||
|
||||
expect(onReset).toBeCalled();
|
||||
expect(onReset).toBeCalled();
|
||||
});
|
||||
|
||||
it('calls onSubmit when submit is clicked', () => {
|
||||
const view = shallow(<ResetSubmit onSubmit={onSubmit} onReset={onReset} />);
|
||||
const view = shallow(<ResetSubmit onSubmit={onSubmit} onReset={onReset} />);
|
||||
|
||||
view.find(Button).at(1).simulate('click');
|
||||
view.find(Button).at(1).simulate('click');
|
||||
|
||||
expect(onSubmit).toBeCalled();
|
||||
expect(onSubmit).toBeCalled();
|
||||
});
|
||||
|
|
|
@ -2,8 +2,8 @@ import * as React from 'react';
|
|||
import { ResetSubmit } from './ResetSubmit';
|
||||
|
||||
export default {
|
||||
title: 'Molecules',
|
||||
component: ResetSubmit,
|
||||
title: 'Molecules',
|
||||
component: ResetSubmit,
|
||||
};
|
||||
|
||||
export const ResetAndSubmit = (args) => <ResetSubmit {...args} />;
|
||||
|
|
|
@ -2,18 +2,18 @@ import { onSmallScreen } from '@roleypoly/design-system/atoms/breakpoints';
|
|||
import styled from 'styled-components';
|
||||
|
||||
export const Buttons = styled.div`
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
`;
|
||||
|
||||
export const Left = styled.div`
|
||||
flex: 0;
|
||||
${onSmallScreen`
|
||||
flex: 0;
|
||||
${onSmallScreen`
|
||||
flex: 1 1 100%;
|
||||
order: 2;
|
||||
`}
|
||||
`;
|
||||
|
||||
export const Right = styled.div`
|
||||
flex: 1;
|
||||
flex: 1;
|
||||
`;
|
||||
|
|
|
@ -5,38 +5,38 @@ import { MdRestore } from 'react-icons/md';
|
|||
import styled from 'styled-components';
|
||||
|
||||
type Props = {
|
||||
onSubmit: () => void;
|
||||
onReset: () => void;
|
||||
onSubmit: () => void;
|
||||
onReset: () => void;
|
||||
};
|
||||
|
||||
const Buttons = styled.div`
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
`;
|
||||
|
||||
const Left = styled.div`
|
||||
flex: 0;
|
||||
${onSmallScreen`
|
||||
flex: 0;
|
||||
${onSmallScreen`
|
||||
flex: 1 1 100%;
|
||||
order: 2;
|
||||
`}
|
||||
`;
|
||||
|
||||
const Right = styled.div`
|
||||
flex: 1;
|
||||
flex: 1;
|
||||
`;
|
||||
|
||||
export const ResetSubmit = (props: Props) => {
|
||||
return (
|
||||
<Buttons>
|
||||
<Left>
|
||||
<Button color="muted" icon={<MdRestore />} onClick={props.onReset}>
|
||||
Reset
|
||||
</Button>
|
||||
</Left>
|
||||
<Right>
|
||||
<Button onClick={props.onSubmit}>Submit</Button>
|
||||
</Right>
|
||||
</Buttons>
|
||||
);
|
||||
return (
|
||||
<Buttons>
|
||||
<Left>
|
||||
<Button color="muted" icon={<MdRestore />} onClick={props.onReset}>
|
||||
Reset
|
||||
</Button>
|
||||
</Left>
|
||||
<Right>
|
||||
<Button onClick={props.onSubmit}>Submit</Button>
|
||||
</Right>
|
||||
</Buttons>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -3,12 +3,12 @@ import { roleCategory } from '../../fixtures/storyData';
|
|||
import { RoleSearch } from './RoleSearch';
|
||||
|
||||
export default {
|
||||
title: 'Molecules/Role Search',
|
||||
component: RoleSearch,
|
||||
args: {
|
||||
roles: roleCategory,
|
||||
searchTerm: '',
|
||||
},
|
||||
title: 'Molecules/Role Search',
|
||||
component: RoleSearch,
|
||||
args: {
|
||||
roles: roleCategory,
|
||||
searchTerm: '',
|
||||
},
|
||||
};
|
||||
|
||||
export const Search = (args) => <RoleSearch {...args} />;
|
||||
|
|
|
@ -8,50 +8,50 @@ import { GoSearch } from 'react-icons/go';
|
|||
import styled from 'styled-components';
|
||||
|
||||
type Props = {
|
||||
roles: RoleType[];
|
||||
placeholder?: string;
|
||||
onSelect: (role: RoleType) => void;
|
||||
onSearchUpdate: (newTerm: string) => void;
|
||||
searchTerm: string;
|
||||
roles: RoleType[];
|
||||
placeholder?: string;
|
||||
onSelect: (role: RoleType) => void;
|
||||
onSearchUpdate: (newTerm: string) => void;
|
||||
searchTerm: string;
|
||||
};
|
||||
|
||||
export const RoleSearch = (props: Props) => {
|
||||
const fuse = new Fuse(props.roles, { includeScore: true, keys: ['name'] });
|
||||
const results =
|
||||
props.searchTerm !== ''
|
||||
? fuse.search(props.searchTerm)
|
||||
: props.roles.map((role) => ({
|
||||
item: role,
|
||||
}));
|
||||
const fuse = new Fuse(props.roles, { includeScore: true, keys: ['name'] });
|
||||
const results =
|
||||
props.searchTerm !== ''
|
||||
? fuse.search(props.searchTerm)
|
||||
: props.roles.map((role) => ({
|
||||
item: role,
|
||||
}));
|
||||
|
||||
const handleClick = (role: RoleType) => () => {
|
||||
props.onSelect(role);
|
||||
};
|
||||
const handleClick = (role: RoleType) => () => {
|
||||
props.onSelect(role);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<TextInputWithIcon
|
||||
icon={<GoSearch />}
|
||||
placeholder={props.placeholder || 'Search or drag a role...'}
|
||||
value={props.searchTerm}
|
||||
onChange={(x) => props.onSearchUpdate(x.target.value)}
|
||||
/>
|
||||
<Space />
|
||||
{results.map((resultRole, idx) => (
|
||||
<RoleInliner key={idx}>
|
||||
<Role
|
||||
selected={false}
|
||||
role={resultRole.item}
|
||||
onClick={handleClick(resultRole.item)}
|
||||
key={`${idx}role`}
|
||||
/>
|
||||
</RoleInliner>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div>
|
||||
<TextInputWithIcon
|
||||
icon={<GoSearch />}
|
||||
placeholder={props.placeholder || 'Search or drag a role...'}
|
||||
value={props.searchTerm}
|
||||
onChange={(x) => props.onSearchUpdate(x.target.value)}
|
||||
/>
|
||||
<Space />
|
||||
{results.map((resultRole, idx) => (
|
||||
<RoleInliner key={idx}>
|
||||
<Role
|
||||
selected={false}
|
||||
role={resultRole.item}
|
||||
onClick={handleClick(resultRole.item)}
|
||||
key={`${idx}role`}
|
||||
/>
|
||||
</RoleInliner>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const RoleInliner = styled.div`
|
||||
display: flex;
|
||||
margin: 5px 0;
|
||||
display: flex;
|
||||
margin: 5px 0;
|
||||
`;
|
||||
|
|
|
@ -2,11 +2,11 @@ import { roleypolyGuild } from '../../fixtures/storyData';
|
|||
import { ServerListingCard } from './ServerListingCard';
|
||||
|
||||
export default {
|
||||
title: 'Molecules/Server Listing Card',
|
||||
component: ServerListingCard,
|
||||
args: {
|
||||
guild: { ...roleypolyGuild, permissionLevel: 4 },
|
||||
},
|
||||
title: 'Molecules/Server Listing Card',
|
||||
component: ServerListingCard,
|
||||
args: {
|
||||
guild: { ...roleypolyGuild, permissionLevel: 4 },
|
||||
},
|
||||
};
|
||||
|
||||
export const serverListingCard = (args) => <ServerListingCard {...args} />;
|
||||
|
|
|
@ -5,86 +5,86 @@ 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;
|
||||
`}
|
||||
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;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
`;
|
||||
|
||||
export const PermissionTagStyled = styled.div<{ hiddenOnSmall?: boolean }>`
|
||||
${text200}
|
||||
${text200}
|
||||
|
||||
display: inline-block;
|
||||
background-color: ${palette.taupe200};
|
||||
padding: 4px 6px;
|
||||
border-radius: 2px;
|
||||
display: inline-block;
|
||||
background-color: ${palette.taupe200};
|
||||
padding: 4px 6px;
|
||||
border-radius: 2px;
|
||||
|
||||
svg {
|
||||
position: relative;
|
||||
top: 1px;
|
||||
${onTablet(
|
||||
css`
|
||||
margin-right: 2px;
|
||||
`
|
||||
)}
|
||||
}
|
||||
svg {
|
||||
position: relative;
|
||||
top: 1px;
|
||||
${onTablet(
|
||||
css`
|
||||
margin-right: 2px;
|
||||
`
|
||||
)}
|
||||
}
|
||||
|
||||
${(props) =>
|
||||
props.hiddenOnSmall &&
|
||||
onSmallScreen(
|
||||
css`
|
||||
display: none;
|
||||
`
|
||||
)}
|
||||
${(props) =>
|
||||
props.hiddenOnSmall &&
|
||||
onSmallScreen(
|
||||
css`
|
||||
display: none;
|
||||
`
|
||||
)}
|
||||
`;
|
||||
|
||||
export const CardBase = styled.div`
|
||||
${text500}
|
||||
${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;
|
||||
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);
|
||||
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;
|
||||
`)}
|
||||
${onTablet(css`
|
||||
flex-direction: column;
|
||||
justify-content: left;
|
||||
`)}
|
||||
`;
|
||||
|
|
|
@ -4,55 +4,55 @@ import { GuildSlug, UserGuildPermissions } from '@roleypoly/types';
|
|||
import * as React from 'react';
|
||||
import { GoPerson, GoStar, GoZap } from 'react-icons/go';
|
||||
import {
|
||||
CardBase,
|
||||
CardLine,
|
||||
MaxWidthTitle,
|
||||
PermissionTagStyled,
|
||||
CardBase,
|
||||
CardLine,
|
||||
MaxWidthTitle,
|
||||
PermissionTagStyled,
|
||||
} from './ServerListingCard.styled';
|
||||
|
||||
type ServerListingProps = {
|
||||
guild: GuildSlug;
|
||||
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>
|
||||
<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>
|
||||
);
|
||||
}
|
||||
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>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -7,13 +7,13 @@ import { ServerMasthead } from './ServerMasthead';
|
|||
import { Editable } from './ServerMasthead.styled';
|
||||
|
||||
it('shows Edit Server when editable is true', () => {
|
||||
const view = shallow(<ServerMasthead editable={true} guild={mastheadSlugs[0]} />);
|
||||
const view = shallow(<ServerMasthead editable={true} guild={mastheadSlugs[0]} />);
|
||||
|
||||
expect(view.find(Editable).length).not.toBe(0);
|
||||
expect(view.find(Editable).length).not.toBe(0);
|
||||
});
|
||||
|
||||
it('hides Edit Server when editable is true', () => {
|
||||
const view = shallow(<ServerMasthead editable={false} guild={mastheadSlugs[0]} />);
|
||||
const view = shallow(<ServerMasthead editable={false} guild={mastheadSlugs[0]} />);
|
||||
|
||||
expect(view.find(Editable).length).toBe(0);
|
||||
expect(view.find(Editable).length).toBe(0);
|
||||
});
|
||||
|
|
|
@ -3,15 +3,15 @@ import { guild } from '../../fixtures/storyData';
|
|||
import { ServerMasthead } from './ServerMasthead';
|
||||
|
||||
export default {
|
||||
title: 'Molecules/Server Masthead',
|
||||
args: {
|
||||
editable: false,
|
||||
guild,
|
||||
},
|
||||
title: 'Molecules/Server Masthead',
|
||||
args: {
|
||||
editable: false,
|
||||
guild,
|
||||
},
|
||||
};
|
||||
|
||||
export const Default = (args) => <ServerMasthead {...args} />;
|
||||
export const Editable = (args) => <ServerMasthead {...args} />;
|
||||
Editable.args = {
|
||||
editable: true,
|
||||
editable: true,
|
||||
};
|
||||
|
|
|
@ -3,34 +3,34 @@ import { transitions } from '@roleypoly/design-system/atoms/timings';
|
|||
import styled from 'styled-components';
|
||||
|
||||
export const Wrapper = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
`;
|
||||
|
||||
export const Name = styled.div`
|
||||
margin: 0 10px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
margin: 0 10px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
`;
|
||||
|
||||
export const Icon = styled.div`
|
||||
flex-shrink: 0;
|
||||
flex-shrink: 0;
|
||||
`;
|
||||
|
||||
export const Editable = styled.div`
|
||||
color: ${palette.taupe500};
|
||||
display: flex;
|
||||
align-items: center;
|
||||
user-select: none;
|
||||
transition: color ${transitions.actionable}s ease-in-out;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
color: ${palette.taupe600};
|
||||
}
|
||||
color: ${palette.taupe500};
|
||||
display: flex;
|
||||
align-items: center;
|
||||
user-select: none;
|
||||
transition: color ${transitions.actionable}s ease-in-out;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
color: ${palette.taupe600};
|
||||
}
|
||||
`;
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { Avatar, utils } from '@roleypoly/design-system/atoms/avatar';
|
||||
import {
|
||||
AccentTitle,
|
||||
AmbientLarge,
|
||||
CompletelyStylelessLink,
|
||||
AccentTitle,
|
||||
AmbientLarge,
|
||||
CompletelyStylelessLink,
|
||||
} from '@roleypoly/design-system/atoms/typography';
|
||||
import { GuildSlug } from '@roleypoly/types';
|
||||
import * as React from 'react';
|
||||
|
@ -10,33 +10,33 @@ import { GoPencil } from 'react-icons/go';
|
|||
import { Editable, Icon, Name, Wrapper } from './ServerMasthead.styled';
|
||||
|
||||
export type ServerMastheadProps = {
|
||||
guild: GuildSlug;
|
||||
editable: boolean;
|
||||
guild: GuildSlug;
|
||||
editable: boolean;
|
||||
};
|
||||
|
||||
export const ServerMasthead = (props: ServerMastheadProps) => {
|
||||
return (
|
||||
<Wrapper>
|
||||
<Icon>
|
||||
<Avatar
|
||||
hash={props.guild.icon}
|
||||
size={props.editable ? 60 : 48}
|
||||
src={utils.avatarHash(props.guild.id, props.guild.icon, 'icons', 512)}
|
||||
>
|
||||
{utils.initialsFromName(props.guild.name)}
|
||||
</Avatar>
|
||||
</Icon>
|
||||
<Name>
|
||||
<AccentTitle>{props.guild.name}</AccentTitle>
|
||||
{props.editable && (
|
||||
<CompletelyStylelessLink to={`/s/${props.guild.id}/edit`}>
|
||||
<Editable role="button">
|
||||
<GoPencil />
|
||||
<AmbientLarge>Edit Server</AmbientLarge>
|
||||
</Editable>
|
||||
</CompletelyStylelessLink>
|
||||
)}
|
||||
</Name>
|
||||
</Wrapper>
|
||||
);
|
||||
return (
|
||||
<Wrapper>
|
||||
<Icon>
|
||||
<Avatar
|
||||
hash={props.guild.icon}
|
||||
size={props.editable ? 60 : 48}
|
||||
src={utils.avatarHash(props.guild.id, props.guild.icon, 'icons', 512)}
|
||||
>
|
||||
{utils.initialsFromName(props.guild.name)}
|
||||
</Avatar>
|
||||
</Icon>
|
||||
<Name>
|
||||
<AccentTitle>{props.guild.name}</AccentTitle>
|
||||
{props.editable && (
|
||||
<CompletelyStylelessLink to={`/s/${props.guild.id}/edit`}>
|
||||
<Editable role="button">
|
||||
<GoPencil />
|
||||
<AmbientLarge>Edit Server</AmbientLarge>
|
||||
</Editable>
|
||||
</CompletelyStylelessLink>
|
||||
)}
|
||||
</Name>
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -4,16 +4,16 @@ import { user } from '../../fixtures/storyData';
|
|||
import { UserAvatarGroup } from './UserAvatarGroup';
|
||||
|
||||
export default {
|
||||
title: 'Molecules/User Avatar Group',
|
||||
component: UserAvatarGroup,
|
||||
args: {
|
||||
user,
|
||||
preventCollapse: true,
|
||||
},
|
||||
title: 'Molecules/User Avatar Group',
|
||||
component: UserAvatarGroup,
|
||||
args: {
|
||||
user,
|
||||
preventCollapse: true,
|
||||
},
|
||||
};
|
||||
|
||||
export const Default = (args) => (
|
||||
<Hero>
|
||||
<UserAvatarGroup {...args} />
|
||||
</Hero>
|
||||
<Hero>
|
||||
<UserAvatarGroup {...args} />
|
||||
</Hero>
|
||||
);
|
||||
|
|
|
@ -3,27 +3,27 @@ import { palette } from '@roleypoly/design-system/atoms/colors';
|
|||
import styled, { css } from 'styled-components';
|
||||
|
||||
export const Collapse = styled.div<{ preventCollapse: boolean }>`
|
||||
${(props) =>
|
||||
!props.preventCollapse &&
|
||||
onSmallScreen(css`
|
||||
display: none;
|
||||
`)}
|
||||
${(props) =>
|
||||
!props.preventCollapse &&
|
||||
onSmallScreen(css`
|
||||
display: none;
|
||||
`)}
|
||||
`;
|
||||
|
||||
export const Group = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
white-space: nowrap;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
white-space: nowrap;
|
||||
`;
|
||||
|
||||
export const Discriminator = styled.span`
|
||||
color: ${palette.taupe500};
|
||||
font-size: 75%;
|
||||
padding: 0 5px;
|
||||
color: ${palette.taupe500};
|
||||
font-size: 75%;
|
||||
padding: 0 5px;
|
||||
`;
|
||||
|
||||
export const GroupText = styled.span`
|
||||
position: relative;
|
||||
top: -2px;
|
||||
position: relative;
|
||||
top: -2px;
|
||||
`;
|
||||
|
|
|
@ -4,25 +4,25 @@ import * as React from 'react';
|
|||
import { Collapse, Discriminator, Group, GroupText } from './UserAvatarGroup.styled';
|
||||
|
||||
type Props = {
|
||||
user: DiscordUser;
|
||||
preventCollapse?: boolean;
|
||||
user: DiscordUser;
|
||||
preventCollapse?: boolean;
|
||||
};
|
||||
|
||||
export const UserAvatarGroup = (props: Props) => (
|
||||
<Group>
|
||||
<Collapse preventCollapse={props.preventCollapse || false}>
|
||||
<GroupText>
|
||||
{props.user.username}
|
||||
<Discriminator>#{props.user.discriminator}</Discriminator>
|
||||
</GroupText>
|
||||
|
||||
</Collapse>
|
||||
<Avatar
|
||||
size={34}
|
||||
hash={props.user.avatar}
|
||||
src={utils.avatarHash(props.user.id, props.user.avatar, 'avatars')}
|
||||
>
|
||||
{utils.initialsFromName(props.user.username)}
|
||||
</Avatar>
|
||||
</Group>
|
||||
<Group>
|
||||
<Collapse preventCollapse={props.preventCollapse || false}>
|
||||
<GroupText>
|
||||
{props.user.username}
|
||||
<Discriminator>#{props.user.discriminator}</Discriminator>
|
||||
</GroupText>
|
||||
|
||||
</Collapse>
|
||||
<Avatar
|
||||
size={34}
|
||||
hash={props.user.avatar}
|
||||
src={utils.avatarHash(props.user.id, props.user.avatar, 'avatars')}
|
||||
>
|
||||
{utils.initialsFromName(props.user.username)}
|
||||
</Avatar>
|
||||
</Group>
|
||||
);
|
||||
|
|
|
@ -4,15 +4,15 @@ import { user } from '../../fixtures/storyData';
|
|||
import { UserPopover as UserPopoverComponent } from './UserPopover';
|
||||
|
||||
export default {
|
||||
title: 'Molecules/User Popover',
|
||||
component: UserPopoverComponent,
|
||||
args: {
|
||||
user,
|
||||
},
|
||||
title: 'Molecules/User Popover',
|
||||
component: UserPopoverComponent,
|
||||
args: {
|
||||
user,
|
||||
},
|
||||
};
|
||||
|
||||
export const UserPopover = (args) => (
|
||||
<PopoverBase active>
|
||||
<UserPopoverComponent {...args} />
|
||||
</PopoverBase>
|
||||
<PopoverBase active>
|
||||
<UserPopoverComponent {...args} />
|
||||
</PopoverBase>
|
||||
);
|
||||
|
|
|
@ -3,31 +3,31 @@ import { transitions } from '@roleypoly/design-system/atoms/timings';
|
|||
import styled from 'styled-components';
|
||||
|
||||
export const Base = styled.div`
|
||||
text-align: right;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
user-select: none;
|
||||
text-align: right;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
user-select: none;
|
||||
`;
|
||||
|
||||
export const NavAction = styled.div`
|
||||
height: 2.25em;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
transition: color ${transitions.actionable}s ease-in-out;
|
||||
color: ${palette.taupe500};
|
||||
box-sizing: border-box;
|
||||
height: 2.25em;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
transition: color ${transitions.actionable}s ease-in-out;
|
||||
color: ${palette.taupe500};
|
||||
box-sizing: border-box;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
color: ${palette.taupe600};
|
||||
}
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
color: ${palette.taupe600};
|
||||
}
|
||||
|
||||
svg {
|
||||
font-size: 120%;
|
||||
box-sizing: content-box;
|
||||
padding: 5px 8px;
|
||||
position: relative;
|
||||
top: 0.1em;
|
||||
}
|
||||
svg {
|
||||
font-size: 120%;
|
||||
box-sizing: content-box;
|
||||
padding: 5px 8px;
|
||||
position: relative;
|
||||
top: 0.1em;
|
||||
}
|
||||
`;
|
||||
|
|
|
@ -6,21 +6,21 @@ import { GoGear, GoSignOut } from 'react-icons/go';
|
|||
import { Base, NavAction } from './UserPopover.styled';
|
||||
|
||||
type UserPopoverProps = {
|
||||
user: DiscordUser;
|
||||
user: DiscordUser;
|
||||
};
|
||||
|
||||
export const UserPopover = (props: UserPopoverProps) => (
|
||||
<Base>
|
||||
<UserAvatarGroup user={props.user} preventCollapse={true} />
|
||||
<NavAction>
|
||||
<CompletelyStylelessLink to="/user/settings">
|
||||
Settings <GoGear />
|
||||
</CompletelyStylelessLink>
|
||||
</NavAction>
|
||||
<NavAction>
|
||||
<CompletelyStylelessLink to="/machinery/logout">
|
||||
Log Out <GoSignOut />
|
||||
</CompletelyStylelessLink>
|
||||
</NavAction>
|
||||
</Base>
|
||||
<Base>
|
||||
<UserAvatarGroup user={props.user} preventCollapse={true} />
|
||||
<NavAction>
|
||||
<CompletelyStylelessLink to="/user/settings">
|
||||
Settings <GoGear />
|
||||
</CompletelyStylelessLink>
|
||||
</NavAction>
|
||||
<NavAction>
|
||||
<CompletelyStylelessLink to="/machinery/logout">
|
||||
Log Out <GoSignOut />
|
||||
</CompletelyStylelessLink>
|
||||
</NavAction>
|
||||
</Base>
|
||||
);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue