lerna: split up bulk of packages

This commit is contained in:
41666 2019-04-02 23:10:45 -05:00
parent cb0b1d2410
commit 47a2e5694e
No known key found for this signature in database
GPG key ID: BC51D07640DC10AF
90 changed files with 0 additions and 0 deletions

View file

@ -0,0 +1,11 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<DiscordButton /> renders correctly 1`] = `
<discord-button__Button>
<discord-button__ButtonIcon
src="/static/discord-logo.svg"
/>
 
Hello!
</discord-button__Button>
`;

View file

@ -0,0 +1,91 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<DiscordGuildPic /> falls-back to a default when things go bad. 1`] = `
.c0 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: center;
-webkit-justify-content: center;
-ms-flex-pack: center;
justify-content: center;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
background-color: var(--fallback-color);
}
<DiscordGuildPic
icon="aaa"
id="0000"
name="Mock"
>
<discord-guild-pic__Fallback
serverName="Mock"
style={
Object {
"--fallback-color": "hsl(77,50%,50%)",
}
}
>
<StyledComponent
forwardedComponent={
Object {
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"componentId": "discord-guild-pic__Fallback-d55lwu-0",
"isStatic": true,
"lastClassName": "c0",
"rules": Array [
"display:flex;justify-content:center;align-items:center;background-color:var(--fallback-color);",
],
},
"displayName": "discord-guild-pic__Fallback",
"foldedComponentIds": Array [],
"render": [Function],
"styledComponentId": "discord-guild-pic__Fallback-d55lwu-0",
"target": "div",
"toString": [Function],
"warnTooManyClasses": [Function],
"withComponent": [Function],
}
}
forwardedRef={null}
serverName="Mock"
style={
Object {
"--fallback-color": "hsl(77,50%,50%)",
}
}
>
<div
className="c0"
style={
Object {
"--fallback-color": "hsl(77,50%,50%)",
}
}
>
M
</div>
</StyledComponent>
</discord-guild-pic__Fallback>
</DiscordGuildPic>
`;
exports[`<DiscordGuildPic /> renders a snapshot 1`] = `
<DiscordGuildPic
icon="aaa"
id="0000"
name="Mock"
>
<img
onError={[Function]}
onLoad={[Function]}
src="https://cdn.discordapp.com/icons/0000/aaa.png"
/>
</DiscordGuildPic>
`;

View file

@ -0,0 +1,14 @@
/* eslint-env jest */
import * as React from 'react'
// import renderer from 'react-test-renderer'
import { shallow } from 'enzyme'
import DiscordButton from '../discord-button'
import 'jest-styled-components'
describe('<DiscordButton />', () => {
it('renders correctly', () => {
const button = shallow(<DiscordButton>Hello!</DiscordButton>)
expect(button).toMatchSnapshot()
expect(button.text().trim()).toEqual('Hello!')
})
})

View file

@ -0,0 +1,33 @@
/* eslint-env jest */
import * as React from 'react'
// import renderer from 'react-test-renderer'
import { shallow, mount } from 'enzyme'
import DiscordGuildPic from '../discord-guild-pic'
import 'jest-styled-components'
describe('<DiscordGuildPic />', () => {
const mockServer = {
id: '0000',
icon: 'aaa',
name: 'Mock'
}
it('renders a snapshot', () => {
const pic = mount(<DiscordGuildPic {...mockServer} />)
expect(pic).toMatchSnapshot()
})
it('renders a well-formatted guild pic correctly', () => {
const pic = shallow(<DiscordGuildPic {...mockServer} />)
const expectedSrc = `https://cdn.discordapp.com/icons/${mockServer.id}/${mockServer.icon}.png`
expect(pic.find('img').prop('src')).toEqual(expectedSrc)
})
it('falls-back to a default when things go bad.', () => {
const pic = mount(<DiscordGuildPic {...mockServer} />)
pic.find('img').simulate('error')
expect(pic).toMatchSnapshot()
expect(pic.text()).toEqual(mockServer.name[0])
})
})

View file

@ -0,0 +1,15 @@
// @flow
import * as React from 'react'
import styled from 'styled-components'
import Role from '../role/demo'
import demoRoles from '../../config/demo'
const DemoWrapper = styled.div`
justify-content: center;
display: flex;
flex-wrap: wrap;
`
export default () => <DemoWrapper>
{ demoRoles.map((v, i) => <Role key={i} role={v} />) }
</DemoWrapper>

View file

@ -0,0 +1,85 @@
import * as React from 'react'
import moment from 'moment'
import Typist from 'react-typist'
import styled from 'styled-components'
import MediaQuery from '../../kit/media'
import demoRoles from '../../config/demo'
const Outer = styled.div`
background-color: var(--dark-but-not-black);
padding: 10px;
text-align: left;
color: var(--c-white);
`
const Chat = styled.div`
padding: 10px 0;
font-size: 0.8em;
${() => MediaQuery({ sm: 'font-size: 1em;' })}
& span {
display: inline-block;
margin-left: 5px;
}
`
const TextArea = styled.div`
background-color: hsla(218,5%,47%,.3);
border-radius: 5px;
padding: 10px;
font-size: 0.8em;
${() => MediaQuery({ sm: 'font-size: 1em;' })}
& .Typist .Cursor {
display: inline-block;
color: transparent;
border-left: 1px solid var(--c-white);
user-select: none;
&--blinking {
opacity: 1;
animation: blink 2s ease-in-out infinite;
@keyframes blink {
0% {
opacity: 1;
}
50% {
opacity: 0;
}
100% {
opacity: 1;
}
}
}
}
`
const Timestamp = styled.span`
font-size: 0.7em;
color: hsla(0,0%,100%,.2);
`
const Username = styled.span`
font-weight: bold;
`
const Typing = () => <Outer>
<Chat>
<Timestamp className='timestamp'>{moment().format('LT')}</Timestamp>
<Username className='username'>okano岡野</Username>
<span className='text'>Hey, I want some roles!</span>
</Chat>
<TextArea>
<Typist cursor={{ blink: true }}>
{ demoRoles.map(({ name }) => [
<span>.iam {name}</span>,
<Typist.Backspace count={30} delay={1500} />
]) }
<span>i have too many roles.</span>
</Typist>
</TextArea>
</Outer>
export default Typing

View file

@ -0,0 +1,62 @@
// @flow
import * as React from 'react'
import styled from 'styled-components'
export type ButtonProps = {
children: React.Node
}
const Button = styled.a`
background-color: var(--c-discord);
color: var(--c-white);
padding: 0.4em 1em;
border-radius: 3px;
border: 1px solid rgba(0,0,0,0.25);
text-decoration: none;
display: flex;
align-items: center;
justify-content: center;
transform: translateY(0px);
transition: all 0.3s ease-in-out;
position: relative;
&::after {
content: '';
position: absolute;
top: 0;
right: 0;
left: 0;
bottom: 0;
transition: all 0.35s ease-in-out;
background-color: hsla(0,0%,100%,0.1);
pointer-events: none;
opacity: 0;
z-index: 2;
}
&:hover {
box-shadow: 0 1px 2px rgba(0,0,0,0.75);
transform: translateY(-1px);
&::after {
opacity: 1;
}
}
&:active {
transform: translateY(0px);
box-shadow: none;
}
`
const ButtonIcon = styled.img`
height: 1.5em;
`
const DiscordButton = ({ children, ...props }: ButtonProps) => (
<Button {...props}>
<ButtonIcon src='/static/discord-logo.svg' />&nbsp;
{children}
</Button>
)
export default DiscordButton

View file

@ -0,0 +1,61 @@
// @flow
// export default ({ id, icon, ...rest }) => <img src={`https://cdn.discordapp.com/icons/${id}/${icon}.png`} {...rest} />
import * as React from 'react'
import styled from 'styled-components'
export type GuildPicProps = {
id: string,
icon: string,
name: string
}
export type GuildPicState = {
src: ?string,
ok: boolean
}
const Fallback = styled.div`
display: flex;
justify-content: center;
align-items: center;
background-color: var(--fallback-color);
`
export default class DiscordGuildPic extends React.Component<GuildPicProps, GuildPicState> {
state = {
src: this.src,
ok: false
}
get src () {
return `https://cdn.discordapp.com/icons/${this.props.id}/${this.props.icon}.png`
}
renderFallback () {
const { name, id, icon, ...rest } = this.props
return <Fallback serverName={name} style={{ '--fallback-color': `hsl(${(name.codePointAt(0) % 360)},50%,50%)` }} {...rest}>{name[0]}</Fallback>
}
onError = () => {
// console.log('onError')
this.setState({
src: null
})
}
onLoad = () => {
this.setState({
ok: true
})
}
renderImg () {
const { name, id, icon, ...rest } = this.props
return <img src={this.state.src} onError={this.onError} onLoad={this.onLoad} {...rest} />
}
render () {
return (this.state.src === null) ? this.renderFallback() : this.renderImg()
}
}

View file

@ -0,0 +1,91 @@
// @flow
// import * as React from 'react'
import { createGlobalStyle } from 'styled-components'
export const colors = {
white: '#efefef',
c9: '#EBD6D4',
c7: '#ab9b9a',
c5: '#756867',
c3: '#5d5352',
c1: '#453e3d',
dark: '#332d2d',
green: '#46b646',
red: '#e95353',
discord: '#7289da'
}
const getColors = () => {
return Object.keys(colors).map(key => {
const nk = key.replace(/c([0-9])/, '$1')
return `--c-${nk}: ${colors[key]};`
}).join(' \n')
}
export default createGlobalStyle`
body {
margin: 0;
padding: 0;
font-family: "source-han-sans-japanese", "Source Sans Pro", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" !important;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
/* prevent FOUC */
transition: opacity 0.2s ease-in-out;
}
* {
box-sizing: border-box;
}
.font-sans-serif {
font-family: sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
}
:root {
${() => getColors()}
--not-quite-black: #23272A;
--dark-but-not-black: #2C2F33;
--greyple: #99AAB5;
--blurple: var(--c-discord);
}
::selection {
background: var(--c-9);
color: var(--c-1);
}
::-moz-selection {
background: var(--c-9);
color: var(--c-1);
}
html {
overflow: hidden;
height: 100%;
}
body {
margin: 0;
padding: 0;
height: 100%;
overflow: auto;
color: var(--c-white);
background-color: var(--c-1);
/* overflow-y: hidden; */
}
h1,h2,h3,h4,h5,h6 {
color: var(--c-9);
}
.fade-element {
opacity: 1;
transition: opacity 0.3s ease-in-out;
}
.fade {
opacity: 0;
}
`

View file

@ -0,0 +1,100 @@
// @flow
import * as React from 'react'
import HeaderBarCommon, { Logomark } from './common'
import { type User } from '../../stores/user'
import DiscordIcon from '../discord-guild-pic'
import styled from 'styled-components'
import { Hide } from '../../kit/media'
import Link from 'next/link'
import { connect } from 'react-redux'
import { getCurrentServerState } from '../../stores/currentServer'
const temporaryServer = {
id: '423497622876061707',
name: 'Placeholder',
icon: '8d03476c186ec8b2f6a1a4f5e55b13fe'
}
const LogoBox = styled.a`
flex: 0;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
`
const StyledServerPic = styled(DiscordIcon)`
border-radius: 100%;
box-shadow: 0 0 1px rgba(0,0,0,0.1);
border: 1px solid rgba(0,0,0,0.25);
height: 35px;
width: 35px;
margin-right: 10px;
display: flex;
align-items: center;
justify-content: center;
`
const StyledAvatar = styled.img`
border-radius: 100%;
border: 1px solid var(--c-green);
height: 35px;
width: 35px;
margin-left: 10px;
display: flex;
align-items: center;
justify-content: center;
`
const ServerSelector = (props) => <div {...props}>
<div>
<StyledServerPic {...props} />
</div>
<div>
{ props.name }
</div>
</div>
const StyledServerSelector = styled(ServerSelector)`
flex: 1;
display: flex;
align-items: center;
justify-content: left;
`
const UserSection = ({ user, ...props }) => <div {...props}>
<Hide.SM>{ user.username }</Hide.SM>
<StyledAvatar src={user.avatar} />
</div>
const StyledUserSection = styled(UserSection)`
display: flex;
align-items: center;
justify-content: flex-end;
text-align: right;
`
const Spacer = styled.div`
flex: 1;
`
const HeaderBarAuth: React.StatelessFunctionalComponent<{ user: User, isOnServer: boolean, currentServer: * }> = ({ user, isOnServer, currentServer = temporaryServer }) => (
<HeaderBarCommon noBackground={false}>
<>
<Link href='/'>
<LogoBox>
<Logomark />
</LogoBox>
</Link>
{ isOnServer ? <StyledServerSelector name={currentServer.name} id={currentServer.id} icon={currentServer.icon} /> : <Spacer /> }
<StyledUserSection user={user} />
</>
</HeaderBarCommon>
)
const mapStateToProps = (state, { router }) => ({
isOnServer: router.pathname === '/_internal/_server',
currentServer: router.pathname === '/_internal/_server' ? getCurrentServerState(state, router.query.id).server : {}
})
export default connect(mapStateToProps)(HeaderBarAuth)

View file

@ -0,0 +1,60 @@
// @flow
import * as React from 'react'
import dynamic from 'next/dynamic'
import styled from 'styled-components'
import * as logo from '../logo'
export type CommonProps = {
children: React.Element<any>,
noBackground: boolean
}
const Header = styled.div`
background-color: ${({ noBackground }: any) => noBackground === false ? 'var(--c-dark);' : 'var(--c-1);'}
position: relative;
transition: background-color 0.3s ease-in-out;
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 5;
`
const HeaderInner = styled.div`
display: flex;
justify-content: center;
align-items: center;
max-width: 960px;
width: 100vw;
margin: 0 auto;
height: 50px;
padding: 3px 5px;
position: relative;
top: -1px;
& > div {
margin: 0 0.5em;
}
`
export const Logotype = styled(logo.Logotype)`
height: 30px;
`
export const Logomark = styled(logo.Logomark)`
width: 40px;
height: 40px;
`
//
const DebugBreakpoints = dynamic(() => import('../../kit/debug-breakpoints'))
const HeaderBarCommon = ({ children, noBackground = false }: CommonProps) => (
<Header noBackground={noBackground}>
{ (process.env.NODE_ENV === 'development') && <DebugBreakpoints />}
<HeaderInner>
{ children }
</HeaderInner>
</Header>
)
export default HeaderBarCommon

View file

@ -0,0 +1,53 @@
// @flow
import * as React from 'react'
import HeaderBarCommon, { Logotype, type CommonProps } from './common'
import styled from 'styled-components'
import Link from 'next/link'
const LogoBox = styled.a`
flex: 1;
display: flex;
align-items: center;
justify-content: flex-start;
cursor: pointer;
`
const LoginButton = styled.a`
cursor: pointer;
background-color: transparent;
display: block;
padding: 0.3em 1em;
border-radius: 2px;
border: 1px solid transparent;
transition: all 0.3s ease-in-out;
&:hover {
transform: translateY(-1px);
box-shadow: 0 1px 2px rgba(0,0,0,0.75);
background-color: var(--c-green);
border-color: rgba(0,0,0,0.25);
text-shadow: 1px 1px 0px rgba(0,0,0,0.25);
}
&:active {
transform: translateY(0px);
box-shadow: none;
}
`
const HeaderBarUnauth: React.StatelessFunctionalComponent<CommonProps> = (props) => (
<HeaderBarCommon {...props}>
<>
<Link href='/' prefetch>
<LogoBox>
<Logotype />
</LogoBox>
</Link>
<Link href='/auth/login' prefetch>
<LoginButton>Sign in </LoginButton>
</Link>
</>
</HeaderBarCommon>
)
export default HeaderBarUnauth

View file

@ -0,0 +1,37 @@
// @flow
import * as React from 'react'
import GlobalColors from './global-colors'
import SocialCards from './social-cards'
import HeaderBar from '../containers/header-bar'
import { type User } from '../stores/user'
import styled from 'styled-components'
const LayoutWrapper = styled.div`
transition: opacity: 0.1s ease-out;
opacity: 0;
.wf-active &, .force-active & {
opacity: 1;
}
`
const ContentBox = styled.div`
margin: 0 auto;
width: 960px;
max-width: 100vw;
padding: 5px;
padding-top: 50px;
/* max-height: calc(100vh - 50px); */
`
const Layout = ({ children, user, noBackground, router }: {children: React.Element<any>, user: User, noBackground: boolean, router: * }) => <>
<GlobalColors />
<SocialCards />
<LayoutWrapper>
<HeaderBar user={user} noBackground={noBackground} router={router} />
<ContentBox>
{children}
</ContentBox>
</LayoutWrapper>
</>
export default Layout

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,78 @@
/* eslint-env jest */
import * as React from 'react'
// import renderer from 'react-test-renderer'
import { shallow } from 'enzyme'
import Role from '../index'
import 'jest-styled-components'
describe('<Role />', () => {
it('renders correctly', () => {
const role = shallow(<Role role={{ name: 'Test Role', color: '#ffffff' }} />)
expect(role).toMatchSnapshot()
})
it('triggers onToggle with new state', () => {
let changed = false
const role = shallow(
<Role
role={{ name: 'Test Role', color: '#ffffff' }}
onToggle={(next) => { changed = next }}
active={false}
/>
)
role.simulate('click')
expect(changed).toBe(true)
const role2 = shallow(
<Role
role={{ name: 'Test Role', color: '#ffffff' }}
onToggle={(next) => { changed = next }}
active
/>
)
role2.simulate('click')
expect(changed).toBe(false)
})
it('fixes colors when they are not set', () => {
const role = shallow(<Role role={{ name: 'Test Role', color: 0 }} />)
expect(role.props().style['--role-color-base']).toEqual('hsl(0, 0%, 93.7%)')
})
it('has a single space for a name when empty', () => {
const role = shallow(<Role role={{ name: '', color: '#ffffff' }} />)
expect(role.text()).toEqual(' ')
})
describe('when disabled,', () => {
it('handles touch hover events', () => {
const role = shallow(<Role role={{ name: 'unsafe role', color: '#ffffff' }} disabled />)
role.simulate('touchstart')
expect(role.state().hovering).toEqual(true)
expect(role).toMatchSnapshot() // expecting tooltip
expect(role.exists('tooltip')).toEqual(true)
role.simulate('touchend')
expect(role.state().hovering).toEqual(false)
})
it('does not trigger onToggle on click', () => {
let changed = false
const role = shallow(
<Role
role={{ name: 'Test Role', color: '#ffffff' }}
onToggle={() => { changed = true }}
active={changed}
disabled
/>
)
expect(role).toMatchSnapshot()
role.simulate('click')
expect(role.html()).toBe(role.html())
expect(changed).toBe(false)
})
})
})

View file

@ -0,0 +1,64 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<Role /> renders correctly 1`] = `
<rolestyled
onClick={[Function]}
onTouchEnd={null}
onTouchStart={null}
style={
Object {
"--role-color-active": "hsl(0, 0%, 100%)",
"--role-color-base": "hsl(0, 0%, 100%)",
"--role-color-outline": "hsla(0, 0%, 100%, 0.7)",
"--role-color-outline-alt": "hsla(0, 0%, 100%, 0.4)",
}
}
title={null}
>
Test Role
</rolestyled>
`;
exports[`<Role /> when disabled, does not trigger onToggle on click 1`] = `
<rolestyled
active={false}
disabled={true}
onClick={null}
onTouchEnd={[Function]}
onTouchStart={[Function]}
style={
Object {
"--role-color-active": "hsl(0, 0%, 100%)",
"--role-color-base": "hsl(0, 0%, 100%)",
"--role-color-outline": "hsla(0, 0%, 100%, 0.7)",
"--role-color-outline-alt": "hsla(0, 0%, 100%, 0.4)",
}
}
title="This role has unsafe permissions."
>
Test Role
</rolestyled>
`;
exports[`<Role /> when disabled, handles touch hover events 1`] = `
<rolestyled
disabled={true}
onClick={null}
onTouchEnd={[Function]}
onTouchStart={[Function]}
style={
Object {
"--role-color-active": "hsl(0, 0%, 100%)",
"--role-color-base": "hsl(0, 0%, 100%)",
"--role-color-outline": "hsla(0, 0%, 100%, 0.7)",
"--role-color-outline-alt": "hsla(0, 0%, 100%, 0.4)",
}
}
title="This role has unsafe permissions."
>
unsafe role
<tooltip>
This role has unsafe permissions.
</tooltip>
</rolestyled>
`;

View file

@ -0,0 +1,14 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<RoleDemo /> renders 1`] = `
<Role
active={false}
onToggle={[Function]}
role={
Object {
"color": "#ffffff",
"name": "test demo role",
}
}
/>
`;

View file

@ -0,0 +1,19 @@
/* eslint-env jest */
import * as React from 'react'
import { shallow } from 'enzyme'
import RoleDemo from '../demo'
import 'jest-styled-components'
describe('<RoleDemo />', () => {
it('renders', () => {
const demo = shallow(<RoleDemo role={{ name: 'test demo role', color: '#ffffff' }} />)
expect(demo).toMatchSnapshot()
})
it('changes state when clicked', () => {
const demo = shallow(<RoleDemo role={{ name: 'test demo role', color: '#ffffff' }} />)
expect(demo.state().active).toEqual(false)
demo.dive().simulate('click')
expect(demo.state().active).toEqual(true)
})
})

View file

@ -0,0 +1,25 @@
// @flow
import * as React from 'react'
import Role, { type RoleData } from './index'
export type DemoRoleProps = {
role: RoleData
}
type DemoRoleState = {
active: boolean
}
export default class RoleDemo extends React.Component<DemoRoleProps, DemoRoleState> {
state = {
active: false
}
onToggle = (n: boolean) => {
this.setState({ active: n })
}
render () {
return <Role role={this.props.role} onToggle={this.onToggle} active={this.state.active} />
}
}

View file

@ -0,0 +1,88 @@
// @flow
import * as React from 'react'
import { colors } from '../global-colors'
import Color from 'color'
import RoleStyled from './role.styled'
import Tooltip from '../tooltip'
// import logger from '../../../logger'
// const log = logger(__filename)
const fromColors = (colors) => Object.entries(colors).reduce(
(acc, [v, val]) => ({ ...acc, [`--role-color-${v}`]: val })
, {})
export type RoleData = {
color: string,
name: string,
}
export type RoleProps = {
active?: boolean, // is lit up as if it's in use
disabled?: boolean, // is interaction-disabled
type?: 'drag' | 'button',
role: RoleData,
isDragging?: boolean,
onToggle?: (nextState: boolean, lastState: boolean) => void,
connectDragSource?: (component: React.Node) => void
}
// const tooltip = ({ show = true, text, ...rest }) => <div {...rest}>{text}</div>
type RoleState = {
hovering: boolean
}
export default class Role extends React.Component<RoleProps, RoleState> {
state = {
hovering: false
}
onToggle = () => {
if (!this.props.disabled && this.props.onToggle) {
const { active = false } = this.props
this.props.onToggle(!active, active)
}
}
onMouseOver = () => {
// log.debug('caught hovering')
if (this.props.disabled && this.state.hovering === false) {
// log.debug('set hovering')
this.setState({ hovering: true })
}
}
onMouseOut = () => {
// log.debug('out hovering')
this.setState({ hovering: false })
}
render () {
let color = Color(this.props.role.color)
if (this.props.role.color === 0) {
color = colors.white
}
const roleColors = {
'outline': Color(color).alpha(0.7).hsl().string(),
'outline-alt': Color(color).alpha(0.4).hsl().string(),
'active': Color(color).lighten(0.1).hsl().string(),
'base': Color(color).hsl().string()
}
const name = (this.props.role.name !== '') ? this.props.role.name : ' '
return <RoleStyled
active={this.props.active}
disabled={this.props.disabled}
onClick={(this.props.disabled) ? null : this.onToggle}
onTouchStart={(this.props.disabled) ? this.onMouseOver : null}
onTouchEnd={(this.props.disabled) ? this.onMouseOut : null}
style={fromColors(roleColors)}
title={(this.props.disabled) ? 'This role has unsafe permissions.' : null}
>
{name}
{ (this.props.disabled && this.state.hovering) && <Tooltip>This role has unsafe permissions.</Tooltip> }
</RoleStyled>
}
}

View file

@ -0,0 +1,124 @@
import styled from 'styled-components'
import MediaQuery from '../../kit/media'
export default styled.div`
border: solid 1px var(--role-color-outline);
border-radius: 1.2em;
box-sizing: border-box;
cursor: pointer;
position: relative;
display: flex;
overflow: hidden;
align-items: center;
justify-content: flex-start;
flex-wrap: wrap;
flex-direction: column;
font-size: 1.2em;
line-height: 20px;
margin: 0.3em;
padding: 4px 0.5em;
min-height: 32px;
max-width: 90vw;
transition: box-shadow 0.3s ease-in-out;
text-shadow: 1px 1px 1px rgba(0,0,0,0.45);
text-overflow: ellipsis;
user-select: none;
white-space: nowrap;
transform: rotateZ(0);
${(props: any) => (props.active) ? `
box-shadow: inset 0 0 0 3em var(--role-color-outline-alt);
` : `
`}
.wf-active & {
/* padding-top: 4px; */
}
&[disabled]:hover {
overflow: visible;
}
&:hover::after {
transform: translateY(-1px) rotateZ(0);
box-shadow: 0 0 1px rgba(0,0,0,0.75);
border-color: var(--role-color-active);
clip-path: border-box circle(50.2% at 49.6% 50%); /* firefox fix */
}
&:active::after {
transform: none;
}
&::after {
content: '';
display: none;
box-sizing: border-box;
position: absolute;
left: 4px;
bottom: 2px;
top: 4px;
width: 22px;
height: 22px;
border: 1px solid var(--role-color-base);
border-radius: 100%;
clip-path: border-box circle(50.2% at 50% 50%); /* this is just for you, firefox. */
transform: rotateZ(0);
${(props: any) => (props.active) ? `
transition: border 0.3s ease-in-out, transform 0.1s ease-in-out, background-color 1ms ease-in-out 0.29s;
border-left-width: 21px;
` : `
transition: border 0.3s ease-in-out, transform 0.1s ease-in-out, background-color 0.3s ease-in-out;
`}
}
${(props: any) => MediaQuery({
md: `
font-size: 1em;
text-shadow: none;
padding-left: 32px;
${(props.active) ? `box-shadow: none;` : ''}
&::after {
${(props.active) ? `background-color: var(--role-color-base);` : ''}
display: block;
}
&:hover::after {
${(props.active) ? `background-color: var(--role-color-active);` : ''}
}
`
})}
&[disabled] {
border-color: hsl(0,0%,40%);
color: hsla(0,0%,40%,0.7);
cursor: default;
box-shadow: none;
${(props: any) => (props.active) ? `
box-shadow: inset 0 0 0 3em hsla(0,0%,40%,0.1);
background-color: hsl(0,0%,40%);`
: ``};
&::after {
border-color: hsl(0,0%,40%);
}
&:hover::after {
border-color: hsl(0,0%,40%);
transform: none;
box-shadow: none;
}
}
`

View file

@ -0,0 +1,36 @@
// @flow
import * as React from 'react'
import NextHead from 'next/head'
export type SocialCardProps = {
title?: string,
description?: string,
image?: string,
imageSize?: number
}
const defaultProps: SocialCardProps = {
title: 'Roleypoly',
description: 'Tame your Discord roles.',
image: 'https://rp.kat.cafe/static/social.png',
imageSize: 200
}
const SocialCards = (props: SocialCardProps) => {
props = {
...defaultProps,
...props
}
return <NextHead>
<meta key='og:title' property='og:title' content={props.title} />
<meta key='og:description' property='og:description' content={props.description} />
<meta key='twitter:card' name='twitter:card' content='summary_large_image' />
<meta key='twitter:image' name='twitter:image' content={props.image} />
<meta key='og:image' property='og:image' content={props.image} />
<meta key='og:image:width' property='og:image:width' content={props.imageSize} />
<meta key='og:image:height' property='og:image:height' content={props.imageSize} />
</NextHead>
}
export default SocialCards

View file

@ -0,0 +1,22 @@
import styled from 'styled-components'
import MediaQuery from '../kit/media'
export default styled.div`
position: absolute;
bottom: 35px;
font-size: 0.9em;
background-color: rgba(0,0,0,0.50);
padding: 5px;
color: var(--c-red);
border-radius: 3px;
border: 1px black solid;
z-index: 10;
opacity: 0.99;
overflow: auto;
pointer-events: none;
/* max-width: 50vw; */
white-space: normal;
${() => MediaQuery({ md: `
white-space: nowrap; `
})}
`