refactor styling system

This commit is contained in:
Pig Fang 2020-05-16 21:56:08 +08:00
parent 16b4ee7b6d
commit ec24dff36a
43 changed files with 682 additions and 499 deletions

View File

@ -16,6 +16,8 @@
"test": "jest"
},
"dependencies": {
"@emotion/core": "^10.0.28",
"@emotion/styled": "^10.0.27",
"@fortawesome/fontawesome-free": "^5.12.0",
"@hot-loader/react-dom": "^16.11.0",
"@tweenjs/tween.js": "^18.4.2",

View File

@ -1,3 +0,0 @@
.captcha {
cursor: pointer;
}

View File

@ -1,8 +1,10 @@
import React from 'react'
/** @jsx jsx */
import * as React from 'react'
import { jsx } from '@emotion/core'
import Reaptcha from 'reaptcha'
import { emit, on } from '@/scripts/event'
import { t } from '@/scripts/i18n'
import styles from './Captcha.module.scss'
import * as cssUtils from '@/styles/utils'
const eventId = Symbol()
@ -31,7 +33,7 @@ class Captcha extends React.Component<{}, State> {
execute = async () => {
const recaptcha = this.ref.current
if (recaptcha && this.state.invisible) {
return new Promise<string>(resolve => {
return new Promise<string>((resolve) => {
const off = on(eventId, (value: string) => {
resolve(value)
off()
@ -89,7 +91,7 @@ class Captcha extends React.Component<{}, State> {
<img
src={`${blessing.base_url}/auth/captcha?v=${this.state.time}`}
alt={t('auth.captcha')}
className={styles.captcha}
css={cssUtils.pointerCursor}
height={34}
title={t('auth.change-captcha')}
onClick={this.handleRefresh}

View File

@ -1,3 +0,0 @@
.label::after {
display: none;
}

View File

@ -1,6 +1,13 @@
import React, { useRef } from 'react'
/** @jsx jsx */
import { useRef } from 'react'
import { jsx, css } from '@emotion/core'
import { t } from '@/scripts/i18n'
import styles from './FileInput.module.scss'
const hideRawBrowseButton = css`
::after {
display: none;
}
`
interface Props {
file: File | null
@ -8,7 +15,7 @@ interface Props {
onChange(event: React.ChangeEvent<HTMLInputElement>): void
}
const FileInput: React.FC<Props> = props => {
const FileInput: React.FC<Props> = (props) => {
const ref = useRef<HTMLInputElement>(null)
const handleClick = () => {
@ -29,7 +36,7 @@ const FileInput: React.FC<Props> = props => {
ref={ref}
onChange={props.onChange}
/>
<label className={`custom-file-label ${styles.label}`}>
<label className="custom-file-label" css={hideRawBrowseButton}>
{props.file?.name}
</label>
</div>

View File

@ -1,12 +0,0 @@
.toast {
position: fixed;
right: calc((100% - 350px) / 2);
width: 350px;
z-index: 1050;
transition-property: top;
transition-duration: 0.3s;
}
.shadow {
box-shadow: 0 0.25rem 0.75rem rgba(0, 0, 0, 0.1);
}

View File

@ -1,5 +1,6 @@
/** @jsx jsx */
import React, { useState, useEffect } from 'react'
import styles from './Toast.module.scss'
import { jsx, css } from '@emotion/core'
export type ToastType = 'success' | 'info' | 'warning' | 'error'
@ -16,7 +17,19 @@ const icons = new Map<ToastType, string>([
['error', 'times-circle'],
])
const Toast: React.FC<Props> = props => {
const wrapper = css`
position: fixed;
right: calc((100% - 350px) / 2);
width: 350px;
z-index: 1050;
transition-property: top;
transition-duration: 0.3s;
`
const shadow = css`
box-shadow: 0 0.25rem 0.75rem rgba(0, 0, 0, 0.1);
`
const Toast: React.FC<Props> = (props) => {
const [show, setShow] = useState(false)
useEffect(() => {
@ -37,7 +50,6 @@ const Toast: React.FC<Props> = props => {
`alert alert-${type}`,
'd-flex justify-content-between',
'fade',
styles.shadow,
]
if (show) {
classes.push('show')
@ -46,8 +58,8 @@ const Toast: React.FC<Props> = props => {
const role = type === 'success' || type === 'info' ? 'status' : 'alert'
return (
<div className={styles.toast} style={{ top: `${props.distance}px` }}>
<div className={classes.join(' ')} role={role}>
<div css={wrapper} style={{ top: `${props.distance}px` }}>
<div className={classes.join(' ')} css={shadow} role={role}>
<span className="mr-1 d-flex align-items-center">
<i className={`icon fas fa-${icons.get(props.type)}`}></i>
</span>

View File

@ -1,32 +0,0 @@
@use '../styles/breakpoints';
.body {
background-repeat: no-repeat;
background-position: center;
background-size: cover;
}
.viewer {
@include breakpoints.greater-than('lg') {
min-height: 500px;
}
canvas {
cursor: move;
}
}
.actions i {
display: inline;
padding: 0.5em 0.5em;
&:hover {
color: #555;
cursor: pointer;
}
}
.btn {
display: flex;
justify-content: center;
align-items: center;
}

View File

@ -1,7 +1,11 @@
/** @jsx jsx */
import React, { useState, useEffect, useRef } from 'react'
import { jsx, css } from '@emotion/core'
import styled from '@emotion/styled'
import * as skinview3d from 'skinview3d'
import { t } from '@/scripts/i18n'
import styles from './Viewer.module.scss'
import * as cssUtils from '@/styles/utils'
import * as breakpoints from '@/styles/breakpoints'
import SkinSteve from '../../../misc/textures/steve.png'
import bg1 from '../../../misc/backgrounds/1.png'
import bg2 from '../../../misc/backgrounds/2.png'
@ -42,6 +46,30 @@ const emptyStuff: ViewerStuff = {
firstRun: true,
}
const ActionButton = styled.i`
display: inline;
padding: 0.5em 0.5em;
&:hover {
color: #555;
cursor: pointer;
}
`
const cssCardBody = css`
background-repeat: no-repeat;
background-position: center;
background-size: cover;
`
const cssViewer = css`
${breakpoints.greaterThan(breakpoints.Breakpoint.lg)} {
min-height: 500px;
}
canvas {
cursor: move;
}
`
const Viewer: React.FC<Props> = (props) => {
const { initPositionZ = 70 } = props
@ -187,40 +215,40 @@ const Viewer: React.FC<Props> = (props) => {
<span className="badge bg-olive ml-1">{indicator}</span>
)}
</h3>
<div className={styles.actions}>
<i
<div>
<ActionButton
className={`fas fa-${running ? 'walking' : 'running'}`}
data-toggle="tooltip"
data-placement="bottom"
title={`${t('general.walk')} / ${t('general.run')}`}
onClick={toggleRun}
></i>
<i
></ActionButton>
<ActionButton
className="fas fa-redo-alt"
data-toggle="tooltip"
data-placement="bottom"
title={t('general.rotation')}
onClick={toggleRotate}
></i>
<i
></ActionButton>
<ActionButton
className={`fas fa-${paused ? 'play' : 'pause'}`}
data-toggle="tooltip"
data-placement="bottom"
title={t('general.pause')}
onClick={togglePause}
></i>
<i
></ActionButton>
<ActionButton
className="fas fa-stop"
data-toggle="tooltip"
data-placement="bottom"
title={t('general.reset')}
onClick={handleReset}
></i>
></ActionButton>
</div>
</div>
</div>
<div className={`card-body ${styles.body}`} style={{ background }}>
<div ref={containerRef} className={styles.viewer}></div>
<div className="card-body" css={cssCardBody} style={{ background }}>
<div ref={containerRef} css={cssViewer}></div>
</div>
<div className="card-footer">
<div className="mt-2 mb-3 d-flex">
@ -240,14 +268,16 @@ const Viewer: React.FC<Props> = (props) => {
onClick={setGray}
/>
<div
className={`btn-color bg-green rounded-pill mr-2 elevation-2 ${styles.btn}`}
className="btn-color bg-green rounded-pill mr-2 elevation-2"
css={cssUtils.center}
title={t('colors.prev')}
onClick={setPrevPicture}
>
<i className="fas fa-arrow-left"></i>
</div>
<div
className={`btn-color bg-green rounded-pill mr-2 elevation-2 ${styles.btn}`}
className="btn-color bg-green rounded-pill mr-2 elevation-2"
css={cssUtils.center}
title={t('colors.next')}
onClick={setNextPicture}
>

View File

@ -1,5 +1,6 @@
import React, { useEffect, useRef } from 'react'
import ReactDOM from 'react-dom'
import styled from '@emotion/styled'
import { Terminal } from 'xterm'
import { FitAddon } from 'xterm-addon-fit'
import { Shell } from 'blessing-skin-shell'
@ -11,10 +12,39 @@ import ClosetCommand from './cli/ClosetCommand'
import DnfCommand from './cli/DnfCommand'
import PacmanCommand from './cli/PacmanCommand'
import RmCommand from './cli/RmCommand'
import styles from '@/styles/terminal.module.scss'
import * as breakpoints from '@/styles/breakpoints'
let launched = false
const TerminalContainer = styled.div`
z-index: 1040;
position: fixed;
bottom: 7vh;
user-select: none;
.card-body {
background-color: #000;
}
${breakpoints.greaterThan(breakpoints.Breakpoint.xl)} {
left: 25vw;
width: 50vw;
height: 50vh;
}
${breakpoints.between(breakpoints.Breakpoint.md, breakpoints.Breakpoint.xl)} {
left: 5vw;
width: 90vw;
height: 40vh;
}
${breakpoints.lessThan(breakpoints.Breakpoint.md)} {
left: 1vw;
width: 98vw;
height: 35vh;
}
`
const TerminalWindow: React.FC<{ onClose(): void }> = (props) => {
const mount = useRef<HTMLDivElement>(null)
@ -59,7 +89,7 @@ const TerminalWindow: React.FC<{ onClose(): void }> = (props) => {
return (
<Draggable handle=".card-header">
<div className={`card ${styles.terminal}`}>
<TerminalContainer className="card">
<div className="card-header">
<div className="d-flex justify-content-between">
<h4 className="card-title mt-1">Blessing Skin Shell</h4>
@ -68,8 +98,8 @@ const TerminalWindow: React.FC<{ onClose(): void }> = (props) => {
</button>
</div>
</div>
<div className={`card-body p-2 ${styles.body}`} ref={mount}></div>
</div>
<div className="card-body p-2" ref={mount}></div>
</TerminalContainer>
</Draggable>
)
}

View File

@ -1,27 +0,0 @@
@use 'sass:map';
$breakpoints: (
xs: 0,
sm: 576px,
md: 768px,
lg: 992px,
xl: 1200px,
);
@mixin less-than($breakpoint) {
@media (max-width: map-get($breakpoints, $breakpoint)) {
@content;
}
}
@mixin between($down, $up) {
@media (min-width: map-get($breakpoints, $down)) and (max-width: map-get($breakpoints, $up)) {
@content;
}
}
@mixin greater-than($breakpoint) {
@media (min-width: map-get($breakpoints, $breakpoint)) {
@content;
}
}

View File

@ -1,5 +0,0 @@
@mixin truncate-text {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}

View File

@ -3,6 +3,3 @@
transition all 0.2s ease-in-out
&:hover
color #42a5f5
.captcha
cursor pointer

View File

@ -0,0 +1,19 @@
export const enum Breakpoint {
xs = 0,
sm = 576,
md = 768,
lg = 992,
xl = 1200,
}
export function lessThan(breakpoint: Breakpoint): string {
return `@media (max-width: ${breakpoint}px)`
}
export function between(down: Breakpoint, up: Breakpoint): string {
return `@media (min-width: ${down}px) and (max-width: ${up}px)`
}
export function greaterThan(breakpoint: Breakpoint): string {
return `@media (min-width: ${breakpoint}px)`
}

View File

@ -1,19 +0,0 @@
@use './breakpoints';
.header {
display: flex;
& > div {
margin-left: 4px;
& label {
cursor: pointer;
}
}
@include breakpoints.less-than('sm') {
flex-wrap: wrap;
& > div {
margin: 7px 0 0 0;
}
}
}

View File

@ -1,30 +0,0 @@
@use '../styles/breakpoints';
.terminal {
z-index: 1040;
position: fixed;
bottom: 7vh;
user-select: none;
@include breakpoints.greater-than('xl') {
left: 25vw;
width: 50vw;
height: 50vh;
}
@include breakpoints.between('md', 'xl') {
left: 5vw;
width: 90vw;
height: 40vh;
}
@include breakpoints.less-than('md') {
left: 1vw;
width: 98vw;
height: 35vh;
}
}
.body {
background-color: #000;
}

View File

@ -0,0 +1,17 @@
import { css } from '@emotion/core'
export const truncateText = css`
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
`
export const pointerCursor = css`
cursor: pointer;
`
export const center = css`
display: flex;
justify-content: center;
align-items: center;
`

View File

@ -1,10 +0,0 @@
@use '../../../styles/breakpoints';
.box {
width: 48%;
margin: 7px;
@include breakpoints.less-than('lg') {
width: 98%;
}
}

View File

@ -1,8 +1,18 @@
import React from 'react'
/** @jsx jsx */
import { jsx, css } from '@emotion/core'
import { t } from '@/scripts/i18n'
import { showModal } from '@/scripts/notify'
import { Player } from '@/scripts/types'
import styles from './Card.module.scss'
import * as breakpoints from '@/styles/breakpoints'
const style = css`
width: 48%;
margin: 7px;
${breakpoints.lessThan(breakpoints.Breakpoint.lg)} {
width: 98%;
}
`
interface Props {
player: Player
@ -55,7 +65,7 @@ const Card: React.FC<Props> = (props) => {
}
return (
<div className={`info-box ${styles.box}`}>
<div className="info-box" css={style}>
<div className="info-box-icon">
<img
className="bs-avatar"

View File

@ -6,9 +6,9 @@ import { t } from '@/scripts/i18n'
import * as fetch from '@/scripts/net'
import { Player, Paginator } from '@/scripts/types'
import { toast, showModal } from '@/scripts/notify'
import modeSwitchStyles from '@/styles/table-mode-switch.module.scss'
import Loading from '@/components/Loading'
import Pagination from '@/components/Pagination'
import Header from '../UsersManagement/Header'
import Card from './Card'
import Row from './Row'
import ModalUpdateTexture from './ModalUpdateTexture'
@ -162,7 +162,7 @@ const PlayersManagement: React.FC = () => {
return (
<div className="card">
<div className={`card-header ${modeSwitchStyles.header}`}>
<Header className="card-header">
<form className="input-group" onSubmit={handleSubmitQuery}>
<input
type="text"
@ -203,7 +203,7 @@ const PlayersManagement: React.FC = () => {
<i className="fas fa-grip-vertical"></i>
</label>
</div>
</div>
</Header>
{isLoading ? (
<div className="card-body">
<Loading />

View File

@ -1,39 +0,0 @@
@use '../../../styles/utils';
.box {
cursor: default;
transition-property: box-shadow;
transition-duration: 0.3s;
&:hover {
box-shadow: 0 0.5rem 1rem rgba(#000, 0.15);
}
}
.content {
max-width: calc(100% - 70px);
}
.header {
max-width: calc(100% - 40px);
}
.title {
@include utils.truncate-text;
}
.actions a {
transition-property: color;
transition-duration: 0.3s;
color: #000;
&:hover {
color: #999;
}
&:not(:last-child) {
margin-right: 9px;
}
}
.description {
font-size: 14px;
@include utils.truncate-text;
}

View File

@ -1,7 +1,41 @@
import React from 'react'
/** @jsx jsx */
import { jsx } from '@emotion/core'
import styled from '@emotion/styled'
import { t } from '@/scripts/i18n'
import * as cssUtils from '@/styles/utils'
import { Plugin } from './types'
import styles from './InfoBox.module.scss'
const Box = styled.div`
cursor: default;
transition-property: box-shadow;
transition-duration: 0.3s;
&:hover {
box-shadow: 0 0.5rem 1rem rgba(#000, 0.15);
}
.info-box-content {
max-width: calc(100% - 70px);
}
`
const ActionButton = styled.a`
transition-property: color;
transition-duration: 0.3s;
color: #000;
&:hover {
color: #999;
}
&:not(:last-child) {
margin-right: 9px;
}
`
const Header = styled.div`
max-width: calc(100% - 40px);
display: flex;
`
const Description = styled.div`
font-size: 14px;
${cssUtils.truncateText};
`
interface Props {
plugin: Plugin
@ -27,13 +61,13 @@ const InfoBox: React.FC<Props> = (props) => {
const handleDelete = () => props.onDelete(plugin)
return (
<div className={`info-box mr-3 ${styles.box}`}>
<Box className="info-box mr-3">
<span className={`info-box-icon bg-${plugin.icon.bg}`}>
<i className={`${plugin.icon.faType} fa-${plugin.icon.fa}`} />
</span>
<div className={`info-box-content ${styles.content}`}>
<div className="info-box-content">
<div className="d-flex justify-content-between">
<div className={`d-flex ${styles.header}`}>
<Header>
<input
className="mr-2 d-inline-block"
type="checkbox"
@ -45,43 +79,44 @@ const InfoBox: React.FC<Props> = (props) => {
}
onChange={handleChange}
/>
<strong className={`d-inline-block mr-2 ${styles.title}`}>
<strong className="d-inline-block mr-2" css={cssUtils.truncateText}>
{plugin.title}
</strong>
<span className="d-none d-sm-inline-block text-gray">
v{plugin.version}
</span>
</div>
<div className={styles.actions}>
</Header>
<div>
{plugin.readme && (
<a
<ActionButton
href={`${props.baseUrl}/admin/plugins/readme/${plugin.name}`}
title={t('admin.pluginReadme')}
>
<i className="fas fa-question" />
</a>
</ActionButton>
)}
{plugin.enabled && plugin.config && (
<a
<ActionButton
href={`${props.baseUrl}/admin/plugins/config/${plugin.name}`}
title={t('admin.configurePlugin')}
>
<i className="fas fa-cog" />
</a>
</ActionButton>
)}
<a href="#" title={t('admin.deletePlugin')} onClick={handleDelete}>
<ActionButton
href="#"
title={t('admin.deletePlugin')}
onClick={handleDelete}
>
<i className="fas fa-trash" />
</a>
</ActionButton>
</div>
</div>
<div
className={`mt-2 ${styles.description}`}
title={plugin.description}
>
<Description className="mt-2" title={plugin.description}>
{plugin.description}
</div>
</Description>
</div>
</div>
</Box>
)
}

View File

@ -1,31 +0,0 @@
@use '../../../styles/utils';
.box {
width: 240px;
transition-property: box-shadow;
transition-duration: 0.3s;
}
.body {
flex: unset;
display: flex;
justify-content: center;
img {
cursor: pointer;
width: 170px;
height: 170px;
}
}
.footer {
flex: 1 1 auto;
* {
margin: 2.5px 0;
}
}
.reason {
@include utils.truncate-text;
}

View File

@ -1,8 +1,35 @@
import React from 'react'
/** @jsx jsx */
import { jsx } from '@emotion/core'
import styled from '@emotion/styled'
import { t } from '@/scripts/i18n'
import { Texture } from '@/scripts/types'
import * as cssUtils from '@/styles/utils'
import { Report, Status } from './types'
import styles from './ImageBox.module.scss'
const Card = styled.div`
width: 240px;
transition-property: box-shadow;
transition-duration: 0.3s;
.card-body {
flex: unset;
display: flex;
justify-content: center;
}
img {
cursor: pointer;
width: 170px;
height: 170px;
}
.card-footer {
flex: 1 1 auto;
* {
margin: 2.5px 0;
}
}
`
interface Props {
report: Report
@ -18,7 +45,7 @@ const ImageBox: React.FC<Props> = (props) => {
const handleImageClick = () => props.onClick(report.texture)
return (
<div className={`card mr-3 mb-3 ${styles.box}`}>
<Card className="card mr-3 mb-3">
<div className="card-header">
<b>
{t('skinlib.show.uploader')}
@ -27,7 +54,7 @@ const ImageBox: React.FC<Props> = (props) => {
<span className="mr-1">{report.uploaderName}</span>
(UID: {report.uploader})
</div>
<div className={`card-body ${styles.body}`}>
<div className="card-body">
<img
src={`${blessing.base_url}/preview/${report.tid}?height=150`}
alt={report.tid.toString()}
@ -35,7 +62,7 @@ const ImageBox: React.FC<Props> = (props) => {
onClick={handleImageClick}
/>
</div>
<div className={`card-footer ${styles.footer}`}>
<div className="card-footer">
<div className="d-flex justify-content-between">
<div>
{report.status === Status.Pending ? (
@ -92,7 +119,7 @@ const ImageBox: React.FC<Props> = (props) => {
(UID: {report.reporter})
</div>
<details>
<summary className={styles.reason}>
<summary css={cssUtils.truncateText}>
<b>
{t('report.reason')}
{': '}
@ -109,7 +136,7 @@ const ImageBox: React.FC<Props> = (props) => {
</div>
</details>
</div>
</div>
</Card>
)
}

View File

@ -1,11 +0,0 @@
.group {
width: 15%;
}
.key {
width: 20%;
}
.operations {
width: 25%;
}

View File

@ -1,7 +1,17 @@
import styled from '@emotion/styled'
import React from 'react'
import { t } from '@/scripts/i18n'
import { Line } from './types'
import styles from './Row.module.scss'
const Group = styled.td`
width: 15%;
`
const Key = styled.td`
width: 20%;
`
const Operations = styled.td`
width: 25%;
`
interface Props {
line: Line
@ -19,17 +29,17 @@ const Row: React.FC<Props> = (props) => {
return (
<tr>
<td className={styles.group}>{line.group}</td>
<td className={styles.key}>{line.key}</td>
<Group>{line.group}</Group>
<Key>{line.key}</Key>
<td>{text || t('admin.i18n.empty')}</td>
<td className={styles.operations}>
<Operations>
<button className="btn btn-default mr-2" onClick={handleEditClick}>
{t('admin.i18n.modify')}
</button>
<button className="btn btn-danger" onClick={handleRemoveClick}>
{t('admin.i18n.delete')}
</button>
</td>
</Operations>
</tr>
)
}

View File

@ -1,27 +0,0 @@
@use '../../../styles/breakpoints';
.box {
width: 48%;
margin: 7px;
@include breakpoints.less-than('lg') {
width: 98%;
}
}
.icon {
width: 70px;
display: flex;
justify-content: center;
padding-top: 22px;
}
$border: 1px solid rgba(0, 0, 0, 0.125);
.border {
@include breakpoints.less-than('sm') {
border-bottom: $border;
}
@include breakpoints.greater-than('sm') {
border-right: $border;
}
}

View File

@ -1,13 +1,39 @@
import React from 'react'
import styled from '@emotion/styled'
import { t } from '@/scripts/i18n'
import { User } from '@/scripts/types'
import * as breakpoints from '@/styles/breakpoints'
import {
humanizePermission,
verificationStatusText,
canModifyUser,
canModifyPermission,
} from './utils'
import styles from './Card.module.scss'
const Box = styled.div`
width: 48%;
margin: 7px;
${breakpoints.lessThan(breakpoints.Breakpoint.lg)} {
width: 98%;
}
`
const Icon = styled.div`
width: 70px;
display: flex;
justify-content: center;
padding-top: 22px;
`
const InfoTable = styled.div`
> div:not(:last-child) {
${breakpoints.lessThan(breakpoints.Breakpoint.sm)} {
border-bottom: 1px solid rgba(0, 0, 0, 0.125);
}
${breakpoints.greaterThan(breakpoints.Breakpoint.sm)} {
border-right: 1px solid rgba(0, 0, 0, 0.125);
}
}
`
interface Props {
user: User
@ -27,13 +53,13 @@ const Card: React.FC<Props> = (props) => {
const canModify = canModifyUser(user, currentUser)
return (
<div className={`info-box ${styles.box}`}>
<div className={styles.icon}>
<Box className="info-box">
<Icon>
<img
className="bs-avatar"
src={`${blessing.base_url}/avatar/user/${user.uid}`}
/>
</div>
</Icon>
<div className="info-box-content">
<div className="row">
<div className="col-10">
@ -123,12 +149,12 @@ const Card: React.FC<Props> = (props) => {
{': '}
<span>{user.email}</span>
</div>
<div className="row m-2 border-top border-bottom">
<div className={`col-sm-4 py-1 text-center ${styles.border}`}>
<InfoTable className="row m-2 border-top border-bottom">
<div className="col-sm-4 py-1 text-center">
<b className="d-block">{t('general.user.score')}</b>
<span className="d-block py-1">{user.score}</span>
</div>
<div className={`col-sm-4 py-1 text-center ${styles.border}`}>
<div className="col-sm-4 py-1 text-center">
<b className="d-block">{t('admin.permission')}</b>
<span className="d-block py-1">
{humanizePermission(user.permission)}
@ -140,7 +166,7 @@ const Card: React.FC<Props> = (props) => {
{verificationStatusText(user.verified)}
</span>
</div>
</div>
</InfoTable>
<div>
<small className="text-gray">
{t('general.user.register-at')}
@ -150,7 +176,7 @@ const Card: React.FC<Props> = (props) => {
</div>
</div>
</div>
</div>
</Box>
)
}

View File

@ -0,0 +1,22 @@
import styled from '@emotion/styled'
import { lessThan, Breakpoint } from '@/styles/breakpoints'
const Header = styled.div`
display: flex;
& > div {
margin-left: 4px;
& label {
cursor: pointer;
}
}
${lessThan(Breakpoint.sm)} {
flex-wrap: wrap;
& > div {
margin: 7px 0 0 0;
}
}
`
export default Header

View File

@ -7,10 +7,10 @@ import { t } from '@/scripts/i18n'
import * as fetch from '@/scripts/net'
import { User, UserPermission, Paginator } from '@/scripts/types'
import { toast, showModal } from '@/scripts/notify'
import modeSwitchStyles from '@/styles/table-mode-switch.module.scss'
import type { Props as ModalInputProps } from '@/components/ModalInput'
import Loading from '@/components/Loading'
import Pagination from '@/components/Pagination'
import Header from './Header'
import Card from './Card'
import Row from './Row'
@ -253,7 +253,7 @@ const UsersManagement: React.FC = () => {
return (
<div className="card">
<div className={`card-header ${modeSwitchStyles.header}`}>
<Header className="card-header">
<form className="input-group" onSubmit={handleSubmitQuery}>
<input
type="text"
@ -294,7 +294,7 @@ const UsersManagement: React.FC = () => {
<i className="fas fa-grip-vertical"></i>
</label>
</div>
</div>
</Header>
{isLoading ? (
<div className="card-body">
<Loading />

View File

@ -1,3 +1,5 @@
/** @jsx jsx */
import { jsx } from '@emotion/core'
import React, { useState, useEffect } from 'react'
import { createPortal } from 'react-dom'
import { hot } from 'react-hot-loader/root'
@ -9,11 +11,11 @@ import { showModal, toast } from '@/scripts/notify'
import { Texture, TextureType } from '@/scripts/types'
import ButtonEdit from '@/components/ButtonEdit'
import ViewerSkeleton from '@/components/ViewerSkeleton'
import * as cssUtils from '@/styles/utils'
import ModalApply from '@/views/user/Closet/ModalApply'
import removeClosetItem from '@/views/user/Closet/removeClosetItem'
import setAsAvatar from '@/views/user/Closet/setAsAvatar'
import addClosetItem from './addClosetItem'
import styles from './styles.module.scss'
export type Badge = {
color: string
@ -245,7 +247,7 @@ const Show: React.FC = () => {
const textureUrl = `${blessing.base_url}/textures/${texture.hash}`
return (
<>
<React.Fragment>
{createPortal(
<React.Suspense fallback={<ViewerSkeleton />}>
<Previewer
@ -335,7 +337,11 @@ const Show: React.FC = () => {
<div className="container">
<div className="row mt-2 mb-4">
<div className="col-4">{t('skinlib.show.name')}</div>
<div className={`col-7 ${styles.truncate}`} title={texture.name}>
<div
className="col-7"
css={cssUtils.truncateText}
title={texture.name}
>
{texture.name}
</div>
{canEdit && (
@ -365,7 +371,11 @@ const Show: React.FC = () => {
</div>
<div className="row my-4">
<div className="col-4">Hash</div>
<div className={`col-8 ${styles.truncate}`} title={texture.hash}>
<div
className="col-8"
css={cssUtils.truncateText}
title={texture.hash}
>
{texture.hash}
</div>
</div>
@ -375,9 +385,9 @@ const Show: React.FC = () => {
</div>
<div className="row my-4">
<div className="col-4">{t('skinlib.show.uploader')}</div>
<div className={`col-8 ${styles.truncate}`}>
<div className="col-8" css={cssUtils.truncateText}>
{isUploaderExists ? (
<>
<React.Fragment>
<div>
<a href={linkToUploader} target="_blank">
{nickname}
@ -393,7 +403,7 @@ const Show: React.FC = () => {
</span>
))}
</div>
</>
</React.Fragment>
) : (
nickname
)}
@ -441,7 +451,7 @@ const Show: React.FC = () => {
}}
onClose={handleCloseModalApply}
/>
</>
</React.Fragment>
)
}

View File

@ -1,5 +0,0 @@
@use '../../../styles/utils';
.truncate {
@include utils.truncate-text;
}

View File

@ -1,33 +0,0 @@
@use '../../../styles/utils';
.card {
width: 245px;
transition-property: box-shadow;
transition-duration: 0.3s;
&:hover {
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
}
}
.image {
background-color: #eff1f0;
img {
height: 210px;
}
}
.truncate {
@include utils.truncate-text;
}
.like {
color: #6c757d;
&:hover {
color: #343a40;
}
&[data-liked="true"] {
color: #dc3545;
}
}

View File

@ -1,8 +1,37 @@
import React from 'react'
/** @jsx jsx */
import { jsx } from '@emotion/core'
import styled from '@emotion/styled'
import { t } from '@/scripts/i18n'
import * as cssUtils from '@/styles/utils'
import { LibraryItem } from './types'
import { humanizeType } from './utils'
import styles from './Item.module.scss'
const Card = styled.div`
width: 245px;
transition-property: box-shadow;
transition-duration: 0.3s;
&:hover {
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
}
.card-body {
background-color: #eff1f0;
}
img {
height: 210px;
}
`
interface ButtonLikeProps {
liked: boolean
}
const ButtonLike = styled.a<ButtonLikeProps>`
color: ${(props) => (props.liked ? '#dc3545' : '#6c757d')};
&:hover {
color: ${(props) => (props.liked ? '#dc3545' : '#343a40')};
}
`
interface Props {
item: LibraryItem
@ -29,8 +58,8 @@ const Item: React.FC<Props> = (props) => {
return (
<div className="ml-3 mr-2 mb-2">
<div className={`card ${styles.card}`}>
<div className={`card-body ${styles.image}`}>
<Card className="card">
<div className="card-body">
{item.public || (
<div className="ribbon-wrapper">
<div className="ribbon bg-pink">{t('skinlib.private')}</div>
@ -42,7 +71,8 @@ const Item: React.FC<Props> = (props) => {
</div>
<div className="card-footer">
<a
className={`d-block mb-1 ${styles.truncate}`}
className="d-block mb-1"
css={cssUtils.truncateText}
title={item.name}
href={link}
target="_blank"
@ -62,18 +92,18 @@ const Item: React.FC<Props> = (props) => {
{item.nickname}
</a>
</div>
<a
className={`cursor-pointer ${styles.like}`}
onClick={handleHeartClick}
<ButtonLike
liked={props.liked}
css={cssUtils.pointerCursor}
tabIndex={-1}
data-liked={props.liked}
onClick={handleHeartClick}
>
<i className="fas fa-heart mr-1"></i>
{item.likes}
</a>
</ButtonLike>
</div>
</div>
</div>
</Card>
</div>
)
}

View File

@ -1,23 +0,0 @@
@use '../../../styles/utils';
.item {
width: 235px;
transition-property: box-shadow;
transition-duration: 0.3s;
&:hover {
cursor: pointer;
}
}
.icon:hover {
color: #000;
}
.bg {
background-color: #eff1f0;
}
.truncate {
@include utils.truncate-text;
}

View File

@ -1,8 +1,30 @@
/** @jsx jsx */
import React from 'react'
import { jsx, css } from '@emotion/core'
import styled from '@emotion/styled'
import { t } from '@/scripts/i18n'
import { ClosetItem } from '@/scripts/types'
import * as cssUtils from '@/styles/utils'
import setAsAvatar from './setAsAvatar'
import styles from './ClosetItem.module.scss'
const Card = styled.div`
width: 235px;
transition-property: box-shadow;
transition-duration: 0.3s;
&:hover {
cursor: pointer;
}
.card-body {
background-color: #eff1f0;
}
`
const blackOnHover = css`
:hover {
color: #000;
}
`
interface Props {
item: ClosetItem
@ -12,7 +34,7 @@ interface Props {
onRemove(): void
}
const ClosetItem: React.FC<Props> = props => {
const ClosetItem: React.FC<Props> = (props) => {
const { item } = props
const handleItemClick = () => {
@ -22,12 +44,8 @@ const ClosetItem: React.FC<Props> = props => {
const handleSetAsAvatar = () => setAsAvatar(item.tid)
return (
<div
className={`card mr-3 mb-3 ${styles.item} ${
props.selected ? 'shadow' : ''
}`}
>
<div className={`card-body ${styles.bg}`} onClick={handleItemClick}>
<Card className={`card mr-3 mb-3 ${props.selected ? 'shadow' : ''}`}>
<div className="card-body" onClick={handleItemClick}>
<img
src={`${blessing.base_url}/preview/${item.tid}?height=150`}
alt={item.pivot.item_name}
@ -36,7 +54,7 @@ const ClosetItem: React.FC<Props> = props => {
</div>
<div className="card-footer pb-2 pt-2 pl-1 pr-1">
<div className="container d-flex justify-content-between">
<span className={styles.truncate} title={item.pivot.item_name}>
<span css={cssUtils.truncateText} title={item.pivot.item_name}>
{item.pivot.item_name}
</span>
<span className="d-inline-block drop-down">
@ -45,7 +63,7 @@ const ClosetItem: React.FC<Props> = props => {
aria-haspopup="true"
aria-expanded="false"
>
<i className={`fas fa-cog text-gray ${styles.icon}`} />
<i className="fas fa-cog text-gray" css={blackOnHover} />
</span>
<div className="dropdown-menu">
<a href="#" className="dropdown-item" onClick={props.onRename}>
@ -68,7 +86,7 @@ const ClosetItem: React.FC<Props> = props => {
</span>
</div>
</div>
</div>
</Card>
)
}

View File

@ -1,12 +1,13 @@
import React, { useState, useEffect, useCallback } from 'react'
import styled from '@emotion/styled'
import { hot } from 'react-hot-loader/root'
import { t } from '@/scripts/i18n'
import * as fetch from '@/scripts/net'
import { toast } from '@/scripts/notify'
import useTween from '@/scripts/hooks/useTween'
import * as breakpoints from '@/styles/breakpoints'
import InfoBox from './InfoBox'
import SignButton from './SignButton'
import scoreStyle from './score.module.scss'
type ScoreInfo = {
signAfterZero: boolean
@ -25,6 +26,24 @@ type SignReturn = {
storage: Stat
}
const ScoreTitle = styled.p`
font-weight: bold;
margin-top: 5px;
${breakpoints.lessThan(breakpoints.Breakpoint.md)} {
margin-top: 12px;
}
`
const Score = styled.p`
font-family: 'Minecraft';
font-size: 50px;
margin-top: 20px;
cursor: help;
`
const ScoreNotice = styled.p`
font-size: smaller;
margin-top: 20px;
`
const Dashboard: React.FC = () => {
const [loading, setLoading] = useState(false)
const [players, setPlayers] = useState<Stat>({ used: 0, total: 1 })
@ -106,15 +125,11 @@ const Dashboard: React.FC = () => {
)}
</div>
<div className="col-md-4 text-center">
<p className={scoreStyle.title}>{t('user.cur-score')}</p>
<p
className={scoreStyle.number}
data-toggle="modal"
data-target="#modal-score-instruction"
>
<ScoreTitle>{t('user.cur-score')}</ScoreTitle>
<Score data-toggle="modal" data-target="#modal-score-instruction">
{~~score}
</p>
<p className={scoreStyle.notice}>{t('user.score-notice')}</p>
</Score>
<ScoreNotice>{t('user.score-notice')}</ScoreNotice>
</div>
</div>
</div>

View File

@ -1,23 +0,0 @@
@use 'sass:map';
@use '../../../styles/breakpoints';
.title {
font-weight: bold;
margin-top: 5px;
@include breakpoints.less-than('md') {
margin-top: 12px;
}
}
.number {
font-family: 'Minecraft';
font-size: 50px;
margin-top: 20px;
cursor: help;
}
.notice {
font-size: smaller;
margin-top: 20px;
}

View File

@ -1,7 +0,0 @@
.row {
cursor: pointer;
}
.selected {
background-color: #efefef;
}

View File

@ -1,8 +1,9 @@
import React from 'react'
/** @jsx jsx */
import { jsx, css } from '@emotion/core'
import { t } from '@/scripts/i18n'
import ButtonEdit from '@/components/ButtonEdit'
import { Player } from '@/scripts/types'
import styles from './Row.module.scss'
import ButtonEdit from '@/components/ButtonEdit'
import * as cssUtils from '@/styles/utils'
interface Props {
player: Player
@ -13,7 +14,7 @@ interface Props {
onDelete(player: Player): Promise<void>
}
const Row: React.FC<Props> = props => {
const Row: React.FC<Props> = (props) => {
const { player } = props
const handleEdit = () => {
@ -24,13 +25,14 @@ const Row: React.FC<Props> = props => {
props.onDelete(player)
}
const classes = [styles.row]
if (props.selected) {
classes.push(styles.selected)
}
const selected =
props.selected &&
css`
background-color: #efefef;
`
return (
<tr className={classes.join(' ')} onClick={props.onClick}>
<tr css={[cssUtils.pointerCursor, selected]} onClick={props.onClick}>
<td>{player.pid}</td>
<td>
<span>{player.name}</span>

View File

@ -1,8 +0,0 @@
.texture {
max-height: 64px;
width: 64px;
}
.line {
width: 80%;
}

View File

@ -1,43 +1,46 @@
import React from 'react'
import styled from '@emotion/styled'
import { t } from '@/scripts/i18n'
import styles from './Viewer2d.module.scss'
const TexturePreview = styled.div`
display: flex;
justify-content: space-between;
width: 80%;
img {
max-height: 64px;
width: 64px;
}
`
interface Props {
skin: string
cape: string
}
const Viewer2d: React.FC<Props> = props => {
const Viewer2d: React.FC<Props> = (props) => {
return (
<div className="card">
<div className="card-header">
<h3 className="card-title">{t('general.texturePreview')}</h3>
</div>
<div className="card-body">
<div className={`d-flex justify-content-between mb-5 ${styles.line}`}>
<TexturePreview className="mb-5">
<span>{t('general.skin')}</span>
{props.skin ? (
<img
src={props.skin}
className={styles.texture}
alt={t('general.skin')}
/>
<img src={props.skin} alt={t('general.skin')} />
) : (
<span>{t('user.player.texture-empty')}</span>
)}
</div>
<div className={`d-flex justify-content-between mt-5 ${styles.line}`}>
</TexturePreview>
<TexturePreview className="mt-5">
<span>{t('general.cape')}</span>
{props.cape ? (
<img
src={props.cape}
className={styles.texture}
alt={t('general.cape')}
/>
<img src={props.cape} alt={t('general.cape')} />
) : (
<span>{t('user.player.texture-empty')}</span>
)}
</div>
</TexturePreview>
</div>
<div className="card-footer">{props.children}</div>
</div>

213
yarn.lock
View File

@ -128,7 +128,7 @@
dependencies:
"@babel/types" "^7.8.3"
"@babel/helper-module-imports@^7.8.3":
"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz#7fe39589b39c016331b6b8c3f441e8f0b1419498"
integrity sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg==
@ -275,6 +275,13 @@
core-js-pure "^3.0.0"
regenerator-runtime "^0.13.4"
"@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2":
version "7.9.6"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.9.6.tgz#a9102eb5cadedf3f31d08a9ecf294af7827ea29f"
integrity sha512-64AF1xY3OAkFHqOb9s4jpgk1Mm5vDZ4L3acHvAml+53nO1XbXLuDodsVpO4OIUsmemlUHMxNdYMNJmsvOwLrvQ==
dependencies:
regenerator-runtime "^0.13.4"
"@babel/runtime@^7.7.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2":
version "7.9.2"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.9.2.tgz#d90df0583a3a252f09aaa619665367bae518db06"
@ -394,6 +401,108 @@
exec-sh "^0.3.2"
minimist "^1.2.0"
"@emotion/cache@^10.0.27":
version "10.0.29"
resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-10.0.29.tgz#87e7e64f412c060102d589fe7c6dc042e6f9d1e0"
integrity sha512-fU2VtSVlHiF27empSbxi1O2JFdNWZO+2NFHfwO0pxgTep6Xa3uGb+3pVKfLww2l/IBGLNEZl5Xf/++A4wAYDYQ==
dependencies:
"@emotion/sheet" "0.9.4"
"@emotion/stylis" "0.8.5"
"@emotion/utils" "0.11.3"
"@emotion/weak-memoize" "0.2.5"
"@emotion/core@^10.0.28":
version "10.0.28"
resolved "https://registry.yarnpkg.com/@emotion/core/-/core-10.0.28.tgz#bb65af7262a234593a9e952c041d0f1c9b9bef3d"
integrity sha512-pH8UueKYO5jgg0Iq+AmCLxBsvuGtvlmiDCOuv8fGNYn3cowFpLN98L8zO56U0H1PjDIyAlXymgL3Wu7u7v6hbA==
dependencies:
"@babel/runtime" "^7.5.5"
"@emotion/cache" "^10.0.27"
"@emotion/css" "^10.0.27"
"@emotion/serialize" "^0.11.15"
"@emotion/sheet" "0.9.4"
"@emotion/utils" "0.11.3"
"@emotion/css@^10.0.27":
version "10.0.27"
resolved "https://registry.yarnpkg.com/@emotion/css/-/css-10.0.27.tgz#3a7458198fbbebb53b01b2b87f64e5e21241e14c"
integrity sha512-6wZjsvYeBhyZQYNrGoR5yPMYbMBNEnanDrqmsqS1mzDm1cOTu12shvl2j4QHNS36UaTE0USIJawCH9C8oW34Zw==
dependencies:
"@emotion/serialize" "^0.11.15"
"@emotion/utils" "0.11.3"
babel-plugin-emotion "^10.0.27"
"@emotion/hash@0.8.0":
version "0.8.0"
resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413"
integrity sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==
"@emotion/is-prop-valid@0.8.8":
version "0.8.8"
resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz#db28b1c4368a259b60a97311d6a952d4fd01ac1a"
integrity sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==
dependencies:
"@emotion/memoize" "0.7.4"
"@emotion/memoize@0.7.4":
version "0.7.4"
resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.4.tgz#19bf0f5af19149111c40d98bb0cf82119f5d9eeb"
integrity sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==
"@emotion/serialize@^0.11.15", "@emotion/serialize@^0.11.16":
version "0.11.16"
resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-0.11.16.tgz#dee05f9e96ad2fb25a5206b6d759b2d1ed3379ad"
integrity sha512-G3J4o8by0VRrO+PFeSc3js2myYNOXVJ3Ya+RGVxnshRYgsvErfAOglKAiy1Eo1vhzxqtUvjCyS5gtewzkmvSSg==
dependencies:
"@emotion/hash" "0.8.0"
"@emotion/memoize" "0.7.4"
"@emotion/unitless" "0.7.5"
"@emotion/utils" "0.11.3"
csstype "^2.5.7"
"@emotion/sheet@0.9.4":
version "0.9.4"
resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-0.9.4.tgz#894374bea39ec30f489bbfc3438192b9774d32e5"
integrity sha512-zM9PFmgVSqBw4zL101Q0HrBVTGmpAxFZH/pYx/cjJT5advXguvcgjHFTCaIO3enL/xr89vK2bh0Mfyj9aa0ANA==
"@emotion/styled-base@^10.0.27":
version "10.0.31"
resolved "https://registry.yarnpkg.com/@emotion/styled-base/-/styled-base-10.0.31.tgz#940957ee0aa15c6974adc7d494ff19765a2f742a"
integrity sha512-wTOE1NcXmqMWlyrtwdkqg87Mu6Rj1MaukEoEmEkHirO5IoHDJ8LgCQL4MjJODgxWxXibGR3opGp1p7YvkNEdXQ==
dependencies:
"@babel/runtime" "^7.5.5"
"@emotion/is-prop-valid" "0.8.8"
"@emotion/serialize" "^0.11.15"
"@emotion/utils" "0.11.3"
"@emotion/styled@^10.0.27":
version "10.0.27"
resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-10.0.27.tgz#12cb67e91f7ad7431e1875b1d83a94b814133eaf"
integrity sha512-iK/8Sh7+NLJzyp9a5+vIQIXTYxfT4yB/OJbjzQanB2RZpvmzBQOHZWhpAMZWYEKRNNbsD6WfBw5sVWkb6WzS/Q==
dependencies:
"@emotion/styled-base" "^10.0.27"
babel-plugin-emotion "^10.0.27"
"@emotion/stylis@0.8.5":
version "0.8.5"
resolved "https://registry.yarnpkg.com/@emotion/stylis/-/stylis-0.8.5.tgz#deacb389bd6ee77d1e7fcaccce9e16c5c7e78e04"
integrity sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==
"@emotion/unitless@0.7.5":
version "0.7.5"
resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed"
integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==
"@emotion/utils@0.11.3":
version "0.11.3"
resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-0.11.3.tgz#a759863867befa7e583400d322652a3f44820924"
integrity sha512-0o4l6pZC+hI88+bzuaX/6BgOvQVhbt2PfmxauVaYOGgbsAw14wdKyvMCZXnsnsHys94iadcF+RG/wZyx6+ZZBw==
"@emotion/weak-memoize@0.2.5":
version "0.2.5"
resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz#8eed982e2ee6f7f4e44c253e12962980791efd46"
integrity sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==
"@fortawesome/fontawesome-free@^5.11.2":
version "5.11.2"
resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-free/-/fontawesome-free-5.11.2.tgz#8644bc25b19475779a7b7c1fc104bc0a794f4465"
@ -881,6 +990,11 @@
resolved "https://registry.npmjs.org/@types/node/-/node-12.7.5.tgz#e19436e7f8e9b4601005d73673b6dc4784ffcc2f"
integrity sha512-9fq4jZVhPNW8r+UYKnxF1e2HkDWOWKM5bC2/7c9wPV835I0aOrVbS/Hw/pWPk2uKrNXQqg9Z959Kz+IYDd5p3w==
"@types/parse-json@^4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
"@types/prettier@^1.19.0":
version "1.19.1"
resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-1.19.1.tgz#33509849f8e679e4add158959fdb086440e9553f"
@ -1658,6 +1772,22 @@ babel-jest@^25.2.3:
chalk "^3.0.0"
slash "^3.0.0"
babel-plugin-emotion@^10.0.27:
version "10.0.33"
resolved "https://registry.yarnpkg.com/babel-plugin-emotion/-/babel-plugin-emotion-10.0.33.tgz#ce1155dcd1783bbb9286051efee53f4e2be63e03"
integrity sha512-bxZbTTGz0AJQDHm8k6Rf3RQJ8tX2scsfsRyKVgAbiUPUNIRtlK+7JxP+TAd1kRLABFxe0CFm2VdK4ePkoA9FxQ==
dependencies:
"@babel/helper-module-imports" "^7.0.0"
"@emotion/hash" "0.8.0"
"@emotion/memoize" "0.7.4"
"@emotion/serialize" "^0.11.16"
babel-plugin-macros "^2.0.0"
babel-plugin-syntax-jsx "^6.18.0"
convert-source-map "^1.5.0"
escape-string-regexp "^1.0.5"
find-root "^1.1.0"
source-map "^0.5.7"
babel-plugin-istanbul@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz#e159ccdc9af95e0b570c75b4573b7c34d671d765"
@ -1676,6 +1806,20 @@ babel-plugin-jest-hoist@^25.2.1:
dependencies:
"@types/babel__traverse" "^7.0.6"
babel-plugin-macros@^2.0.0:
version "2.8.0"
resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz#0f958a7cc6556b1e65344465d99111a1e5e10138"
integrity sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==
dependencies:
"@babel/runtime" "^7.7.2"
cosmiconfig "^6.0.0"
resolve "^1.12.0"
babel-plugin-syntax-jsx@^6.18.0:
version "6.18.0"
resolved "https://registry.yarnpkg.com/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946"
integrity sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=
babel-preset-jest@^25.2.1:
version "25.2.1"
resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-25.2.1.tgz#4ccd0e577f69aa11b71806edfe8b25a5c3ac93a2"
@ -2504,7 +2648,7 @@ convert-source-map@^1.1.0, convert-source-map@^1.4.0:
dependencies:
safe-buffer "~5.1.1"
convert-source-map@^1.5.1, convert-source-map@^1.6.0, convert-source-map@^1.7.0:
convert-source-map@^1.5.0, convert-source-map@^1.5.1, convert-source-map@^1.6.0, convert-source-map@^1.7.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442"
integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==
@ -2576,6 +2720,17 @@ cosmiconfig@^5.0.0:
js-yaml "^3.13.1"
parse-json "^4.0.0"
cosmiconfig@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-6.0.0.tgz#da4fee853c52f6b1e6935f41c1a2fc50bd4a9982"
integrity sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==
dependencies:
"@types/parse-json" "^4.0.0"
import-fresh "^3.1.0"
parse-json "^5.0.0"
path-type "^4.0.0"
yaml "^1.7.2"
create-ecdh@^4.0.0:
version "4.0.3"
resolved "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz#c9111b6f33045c4697f144787f9254cdc77c45ff"
@ -2850,6 +3005,11 @@ csstype@^2.2.0:
resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.8.tgz#0fb6fc2417ffd2816a418c9336da74d7f07db431"
integrity sha512-msVS9qTuMT5zwAGCVm4mxfrZ18BNc6Csd0oJAtiFMZ1FAx1CCvy2+5MDmYoix63LM/6NDbNtodCiGYGmFgO0dA==
csstype@^2.5.7:
version "2.6.10"
resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.10.tgz#e63af50e66d7c266edb6b32909cfd0aabe03928b"
integrity sha512-D34BqZU4cIlMCY93rZHbrq9pjTAQJ3U8S8rfBqjwHxkGPThWFjzZDQpgMJY0QViLxth6ZKYiwFBo14RdN44U/w==
cyclist@^1.0.1:
version "1.0.1"
resolved "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9"
@ -4156,6 +4316,11 @@ find-cache-dir@^2.1.0:
make-dir "^2.0.0"
pkg-dir "^3.0.0"
find-root@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4"
integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==
find-up@^3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73"
@ -4865,6 +5030,14 @@ import-fresh@^3.0.0:
parent-module "^1.0.0"
resolve-from "^4.0.0"
import-fresh@^3.1.0:
version "3.2.1"
resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.2.1.tgz#633ff618506e793af5ac91bf48b72677e15cbe66"
integrity sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==
dependencies:
parent-module "^1.0.0"
resolve-from "^4.0.0"
import-from@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/import-from/-/import-from-2.1.0.tgz#335db7f2a7affd53aaa471d4b8021dee36b7f3b1"
@ -6045,6 +6218,11 @@ linebreak@^1.0.2:
brfs "^2.0.2"
unicode-trie "^1.0.0"
lines-and-columns@^1.1.6:
version "1.1.6"
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00"
integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=
loader-runner@^2.4.0:
version "2.4.0"
resolved "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357"
@ -7056,6 +7234,16 @@ parse-json@^4.0.0:
error-ex "^1.3.1"
json-parse-better-errors "^1.0.1"
parse-json@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.0.0.tgz#73e5114c986d143efa3712d4ea24db9a4266f60f"
integrity sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==
dependencies:
"@babel/code-frame" "^7.0.0"
error-ex "^1.3.1"
json-parse-better-errors "^1.0.1"
lines-and-columns "^1.1.6"
parse-passwd@^1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6"
@ -7126,6 +7314,11 @@ path-to-regexp@0.1.7:
resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=
path-type@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
pbkdf2@^3.0.3:
version "3.0.17"
resolved "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6"
@ -8142,6 +8335,13 @@ resolve@^1.1.5:
dependencies:
path-parse "^1.0.6"
resolve@^1.12.0:
version "1.17.0"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444"
integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==
dependencies:
path-parse "^1.0.6"
resolve@^1.15.1:
version "1.15.1"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.15.1.tgz#27bdcdeffeaf2d6244b95bb0f9f4b4653451f3e8"
@ -8709,7 +8909,7 @@ source-map-url@^0.4.0:
resolved "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3"
integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=
source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6:
source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7:
version "0.5.7"
resolved "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
@ -10076,6 +10276,13 @@ yallist@^3.0.0, yallist@^3.0.2, yallist@^3.0.3:
resolved "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz#b4b049e314be545e3ce802236d6cd22cd91c3de9"
integrity sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==
yaml@^1.7.2:
version "1.9.2"
resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.9.2.tgz#f0cfa865f003ab707663e4f04b3956957ea564ed"
integrity sha512-HPT7cGGI0DuRcsO51qC1j9O16Dh1mZ2bnXwsi0jrSpsLz0WxOLSLXfkABVl6bZO629py3CU+OMJtpNHDLB97kg==
dependencies:
"@babel/runtime" "^7.9.2"
yargs-parser@^11.1.1:
version "11.1.1"
resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz#879a0865973bca9f6bab5cbdf3b1c67ec7d3bcf4"