feat: upgrade dependencies (#480)

* feat: upgrade dependencies

* chore: update linter and CI workflows

* chore: upgrade browserslist
This commit is contained in:
Rui Saraiva 2021-12-04 18:32:53 +00:00 committed by GitHub
parent 6c6d4f6e63
commit 785563a71c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
67 changed files with 7648 additions and 7433 deletions

View File

@ -1,14 +0,0 @@
{
"presets": ["next/babel"],
"plugins": [
[
"prismjs",
{
"languages": ["markup"],
"plugins": [],
"theme": "default",
"css": false
}
]
]
}

View File

@ -1,10 +1,9 @@
{
"parser": "@typescript-eslint/parser",
"extends": [
"react-app",
"next",
"plugin:prettier/recommended",
"plugin:react/recommended",
"plugin:react-hooks/recommended",
"plugin:storybook/recommended",
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/recommended-requiring-type-checking"
],

View File

@ -9,13 +9,16 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Use Node.js 14
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: "14"
node-version-file: '.nvmrc'
cache: 'yarn'
- name: Install dependencies
uses: bahmutov/npm-install@v1
run: yarn install --frozen-lockfile
- name: Lint Scripts
run: yarn lint:scripts
- name: Lint CSS
run: yarn lint:styles
- name: Lint JSON
run: yarn lint:json

View File

@ -12,12 +12,13 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Use Node.js 14
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: '14'
node-version-file: '.nvmrc'
cache: 'yarn'
- name: Install dependencies
uses: bahmutov/npm-install@v1
run: yarn install --frozen-lockfile
- name: Cache Next.js build cache
uses: actions/cache@v2
with:

View File

@ -5,17 +5,18 @@ on: pull_request
jobs:
test_e2e:
name: Run Cypress
runs-on: ubuntu-16.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Use Node.js 14
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: "14"
node-version-file: '.nvmrc'
cache: 'yarn'
- name: Install dependencies
uses: bahmutov/npm-install@v1
run: yarn install --frozen-lockfile
- name: Cache Next.js build cache
uses: actions/cache@v2

4
.husky/commit-msg Executable file
View File

@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npx --no -- commitlint --edit "$1"

4
.husky/pre-commit Executable file
View File

@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npx lint-staged

4
.husky/pre-push Executable file
View File

@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npm run lint

View File

@ -1,7 +0,0 @@
{
"hooks": {
"pre-commit": "lint-staged",
"pre-push": "npm run lint",
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
}

2
.nvmrc
View File

@ -1 +1 @@
14.16.0
14.18

3
.prettierignore Normal file
View File

@ -0,0 +1,3 @@
node_modules
.next
storybook-static

View File

@ -1,6 +0,0 @@
import '@storybook/addon-actions/register'
import '@storybook/addon-links/register'
import '@storybook/addon-knobs/register'
import '@storybook/addon-viewport/register'
import '@storybook/addon-storysource/register'
import '@storybook/addon-backgrounds/register'

View File

@ -1,72 +0,0 @@
import { addDecorator, addParameters, configure } from '@storybook/react'
import { withConsole } from '@storybook/addon-console'
import { INITIAL_VIEWPORTS } from '@storybook/addon-viewport'
import { RouterContext } from 'next/dist/next-server/lib/router-context'
import { myRouter } from './mock-router'
import 'styles/main.scss'
import './styles.css'
addDecorator((storyFn, context) => withConsole()(storyFn)(context))
addParameters({
options: {
storySort: (a, b) => {
if (a[1].kind === 'Welcome') {
return -1
}
return a[1].kind === b[1].kind ? 0 : a[1].id.localeCompare(b[1].id, { numeric: true })
},
},
})
const customViewports = {
responsive: {
name: 'Responsive',
styles: {
height: '100%',
width: '100%',
},
type: 'desktop',
},
desktop1280: {
name: 'Desktop 1280px',
styles: {
height: '800px',
width: '1280px',
},
type: 'desktop',
},
desktop1440: {
name: 'Desktop 1440px',
styles: {
height: '900px',
width: '1440px',
},
type: 'desktop',
},
}
addParameters({
viewport: {
viewports: {
...INITIAL_VIEWPORTS,
...customViewports,
},
defaultViewport: 'responsive',
},
options: {
showPanel: true,
},
})
const Theme = (StoryFn) => (
<RouterContext.Provider value={myRouter}>
<StoryFn />
</RouterContext.Provider>
)
addDecorator(Theme)
// automatically import all files ending in *.stories.tsx
configure(require.context('../components', true, /\.stories\.tsx$/), module)

View File

@ -1,8 +1,33 @@
const path = require('path')
// Export a function. Accept the base config as the only param.
/**
* @type {import('@storybook/react/types').StorybookConfig}
*/
module.exports = {
webpackFinal: async (config) => {
core: {
builder: 'webpack5',
},
reactOptions: {
fastRefresh: true,
},
staticDirs: ['../public'],
stories: ['../components/**/*.stories.mdx', '../components/**/*.stories.@(js|jsx|ts|tsx)'],
addons: [
'@storybook/addon-links',
'@storybook/addon-essentials',
'storybook-addon-next-router',
{
name: '@storybook/addon-postcss',
options: {
postcssLoaderOptions: {
implementation: require('postcss'),
},
},
},
'@storybook/addon-a11y',
'storybook-dark-mode',
],
webpackFinal: async (config, { configType }) => {
config.module.rules.push({
test: /\.scss$/,
use: [
@ -19,7 +44,12 @@ module.exports = {
include: path.resolve(__dirname, '../'),
})
// merge whatever from nextConfig into the webpack config storybook will use
config.resolve.alias['components'] = path.join(__dirname, '../components')
config.resolve.alias['pages'] = path.join(__dirname, '../pages')
config.resolve.alias['styles'] = path.join(__dirname, '../styles')
config.resolve.alias['utils'] = path.join(__dirname, '../utils')
config.resolve.alias['hooks'] = path.join(__dirname, '../hooks')
return config
},
}

View File

@ -1,22 +0,0 @@
import Router from 'next/router'
const commonRouterData = {
push: () => new Promise((resolve) => resolve(true)),
replace: () => new Promise((resolve) => resolve(true)),
prefetch: () => new Promise((resolve) => resolve()),
reload: () => {},
back: () => {},
}
Router.router = {
...commonRouterData,
}
export const myRouter = {
...commonRouterData,
pathname: '/',
route: '/',
query: {},
asPath: '/',
isFallback: false,
}

114
.storybook/preview.js Normal file
View File

@ -0,0 +1,114 @@
import { useEffect, useState } from 'react'
import { RouterContext } from 'next/dist/shared/lib/router-context'
import * as NextImage from 'next/image'
import addons from '@storybook/addons'
import { withConsole } from '@storybook/addon-console'
import { INITIAL_VIEWPORTS } from '@storybook/addon-viewport'
import { addDecorator } from '@storybook/react'
import { DARK_MODE_EVENT_NAME } from 'storybook-dark-mode'
import { useDarkMode } from '../hooks/use-dark-mode'
import * as themes from './theme'
import 'styles/main.scss'
import './styles.css'
const OriginalNextImage = NextImage.default
Object.defineProperty(NextImage, 'default', {
configurable: true,
value: (props) => <OriginalNextImage {...props} unoptimized />,
})
addDecorator((storyFn, context) => withConsole()(storyFn)(context))
const customViewports = {
responsive: {
name: 'Responsive',
styles: {
height: '100%',
width: '100%',
},
type: 'desktop',
},
desktop1280: {
name: 'Desktop 1280px',
styles: {
height: '800px',
width: '1280px',
},
type: 'desktop',
},
desktop1440: {
name: 'Desktop 1440px',
styles: {
height: '900px',
width: '1440px',
},
type: 'desktop',
},
}
export const parameters = {
actions: { argTypesRegex: '^on[A-Z].*' },
viewport: {
viewports: {
...INITIAL_VIEWPORTS,
...customViewports,
},
defaultViewport: 'responsive',
},
darkMode: {
dark: themes.dark,
light: themes.light,
darkClass: 'dark',
lightClass: 'light',
classTarget: 'html',
stylePreview: true,
},
nextRouter: {
Provider: RouterContext.Provider,
},
options: {
storySort: (a, b) => {
if (a[1].kind === 'Welcome') {
return -1
}
return a[1].kind === b[1].kind ? 0 : a[1].id.localeCompare(b[1].id, { numeric: true })
},
},
}
const channel = addons.getChannel()
function ThemeWrapper(props) {
const [isDark, setDark] = useState(false)
const darkMode = useDarkMode(false, {
classNameDark: 'dark',
classNameLight: 'light',
element: document.documentElement,
})
useEffect(() => {
channel.on(DARK_MODE_EVENT_NAME, setDark)
return () => channel.off(DARK_MODE_EVENT_NAME, setDark)
}, [setDark])
useEffect(() => {
if (isDark) {
darkMode.enable()
} else {
darkMode.disable()
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isDark])
return props.children
}
export const decorators = [
(Story) => (
<ThemeWrapper>
<Story />
</ThemeWrapper>
),
]

View File

@ -4,10 +4,10 @@
transition: background 0.2s ease;
}
.sb-show-main.light-mode {
.sb-show-main.light {
background: #fafafa;
}
.sb-show-main.dark-mode {
.sb-show-main.dark {
background: #101114;
}

37
.storybook/theme.js Normal file
View File

@ -0,0 +1,37 @@
import { create } from '@storybook/theming'
const baseStyles = {
// Typography
fontBase: '"Manrope", sans-serif',
fontCode: 'monospace',
brandTitle: 'Favycon',
brandUrl: 'https://favycon.vercel.app',
brandImage: '/logo-horizontal.svg',
}
export const light = create({
base: 'light',
...baseStyles,
// Text colors
textColor: '#0e141a',
textInverseColor: '#e4ebf2',
// Background colors
appContentBg: '#fafafa',
})
export const dark = create({
base: 'dark',
...baseStyles,
// Text colors
textColor: '#e4ebf2',
textInverseColor: '#0e141a',
// Background colors
appBg: '#1e262f',
appContentBg: '#0e141a',
barBg: '#1e262f',
})

View File

@ -1,44 +0,0 @@
const path = require('path')
const WebpackNotifierPlugin = require('webpack-notifier')
module.exports = ({ config, mode }) => {
config.module.rules.push({
test: /\.stories\.tsx?$/,
loaders: [
{
loader: require.resolve('@storybook/source-loader'),
options: { parser: 'typescript' },
},
],
enforce: 'pre',
})
config.module.rules.push({
test: /\.(ts|tsx)$/,
loader: require.resolve('babel-loader'),
options: {
presets: [['react-app', { flow: false, typescript: true }]],
},
})
config.resolve.extensions.push('.ts', '.tsx')
config.resolve.alias['components'] = path.join(__dirname, '../components')
config.resolve.alias['pages'] = path.join(__dirname, '../pages')
config.resolve.alias['styles'] = path.join(__dirname, '../styles')
config.resolve.alias['utils'] = path.join(__dirname, '../utils')
config.resolve.alias['hooks'] = path.join(__dirname, '../hooks')
if (mode === 'DEVELOPMENT') {
config.plugins.push(
new WebpackNotifierPlugin({
title: 'Favycon Storybook',
excludeWarnings: true,
alwaysNotify: true,
contentImage: path.join(__dirname, '../public/favicon.png'),
})
)
}
return config
}

View File

@ -1,4 +1,5 @@
{
"customSyntax": "postcss-scss",
"extends": [
"stylelint-config-recess-order",
"stylelint-config-standard",
@ -13,6 +14,9 @@
{
"ignoreAtRules": ["mixin", "include"]
}
]
],
"font-family-name-quotes": null,
"selector-class-pattern": null,
"color-function-notation": "legacy"
}
}

View File

@ -2,7 +2,7 @@
.base {
padding: 5px 12px;
font-family: Manrope, Arial, Helvetica Neue, Helvetica, sans-serif;
font-family: Manrope, Arial, '"Helvetica Neue"', Helvetica, sans-serif;
font-size: $font-size-title;
line-height: $line-height-title;
color: $black;
@ -43,7 +43,7 @@
}
}
:global(.dark-mode) .primary {
:global(.dark) .primary {
color: $white;
background-color: $dark-black;
border: 1px solid $dark-black;
@ -64,7 +64,7 @@
}
}
:global(.dark-mode) .transparent {
:global(.dark) .transparent {
@include breakpoint-md {
background: $dark-grey;
}
@ -113,7 +113,7 @@
color: $grey;
}
:global(.dark-mode) .gray {
:global(.dark) .gray {
color: $grey-dark;
@include breakpoint-md {
@ -133,7 +133,7 @@
}
}
:global(.dark-mode) .link {
:global(.dark) .link {
color: $white;
&:hover {
@ -151,7 +151,7 @@
}
}
:global(.dark-mode) .bgLink {
:global(.dark) .bgLink {
background: $link-dark;
border-color: $link-dark;
@ -171,7 +171,7 @@
}
}
:global(.dark-mode) .bgGreen {
:global(.dark) .bgGreen {
background: $green;
border-color: $green;

View File

@ -1,30 +1,14 @@
import { withKnobs, text, select, boolean } from '@storybook/addon-knobs'
import { Button } from '.'
import useDarkMode from 'use-dark-mode'
import { ComponentMeta, Story } from '@storybook/react'
import { Button, ButtonProps } from '.'
export default {
title: 'Button',
decorators: [withKnobs],
}
component: Button,
} as ComponentMeta<typeof Button>
export const ButtonDesktop = () => {
const { toggle } = useDarkMode()
const variant = select('variant', ['primary', 'transparent'], 'primary')
const weight = select('weight', ['regular', 'medium', 'semiBold', 'bold'], 'bold')
const body = text('body', 'Mac app coming soon')
const disabled = boolean('disabled', false)
return (
<div style={{ padding: 20 }}>
<Button variant={variant} weight={weight} disabled={disabled}>
{body}
</Button>
<br />
<br />
<Button onClick={toggle}>Toggle Dark Mode</Button>
</div>
)
}
const Template: Story<ButtonProps> = (args) => <Button {...args} />
ButtonDesktop.story = {
name: 'Desktop',
export const Desktop = Template.bind({})
Desktop.args = {
children: 'Mac app coming soon',
}

View File

@ -3,22 +3,15 @@ import classNames from 'classnames'
import styles from './index.module.scss'
type ButtonProps = {
export type ButtonProps = {
children: PropTypes.ReactNodeLike
variant: 'primary' | 'transparent' | 'regularTransparent' | 'modalClose'
weight: 'regular' | 'medium' | 'semiBold' | 'bold'
color: 'black' | 'gray' | 'white' | 'link'
background: 'bgLink' | 'bgGreen' | 'bgDarkGray'
}
} & React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>
const Button = ({
children,
variant,
weight,
color,
background,
...props
}: ButtonProps & React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>) => {
const Button = ({ children, variant, weight, color, background, ...props }: ButtonProps) => {
const className = classNames(styles[variant], styles[weight], styles[color], styles[background], props.className)
return (

View File

@ -11,8 +11,7 @@
cursor: pointer;
visibility: hidden;
outline: none;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
-webkit-tap-highlight-color: transparent;
}

View File

@ -1,30 +1,22 @@
import { Checkbox } from '.'
import useDarkMode from 'use-dark-mode'
import { ComponentMeta, Story } from '@storybook/react'
import { Checkbox, CheckboxProps } from '.'
import { Typography } from 'components/typography'
import { boolean, withKnobs } from '@storybook/addon-knobs'
export default {
title: 'Checkbox',
decorators: [withKnobs],
}
component: Checkbox,
} as ComponentMeta<typeof Checkbox>
export const ToggleDesktop = () => {
const darkMode = useDarkMode(false)
const disabled = boolean('disabled', false)
const Template: Story<CheckboxProps> = (args) => (
<Checkbox {...args}>
<Typography variant="regularBody" weight="medium" muted={args.disabled}>
Test Checkbox
</Typography>
</Checkbox>
)
return (
<div style={{ padding: 20 }}>
<Checkbox name="test" id="test" disabled={disabled}>
<Typography variant="regularBody" weight="medium" muted={disabled}>
Test Checkbox
</Typography>
</Checkbox>
<br />
<Typography>dark mode: {darkMode.value ? 'enabled' : 'disabled'}</Typography>
</div>
)
}
ToggleDesktop.story = {
export const Desktop = Template.bind({})
Desktop.args = {
name: 'Desktop',
id: 'desktop',
}

View File

@ -4,17 +4,12 @@ import { SvgCheckbox } from 'components/svgs/svg-checkbox'
import styles from './index.module.scss'
type CheckboxProps = {
export type CheckboxProps = {
name: string
id: string
}
} & React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>
const Checkbox = ({
name,
id,
children,
...props
}: React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement> & CheckboxProps) => {
const Checkbox = ({ name, id, children, ...props }: CheckboxProps) => {
const [value, setValue] = useState(false)
const onCheckChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setValue(!value)

View File

@ -26,8 +26,7 @@
cursor: pointer;
visibility: hidden;
outline: none;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
-webkit-tap-highlight-color: transparent;
}
@ -45,14 +44,16 @@
}
}
:global(.light-mode) {
:global(.light) {
.sun {
visibility: hidden;
transform: translateX(-1px) scale(0);
}
.moon {
transform: translate(0, 0) rotate(0) scale(1);
}
.sun,
.moon {
path {
@ -61,14 +62,16 @@
}
}
:global(.dark-mode) {
:global(.dark) {
.sun {
transform: translateX(0) scale(1);
}
.moon {
visibility: hidden;
transform: translate(0, -2px) rotate(115deg) scale(0.4);
}
.sun,
.moon {
path {
@ -77,7 +80,7 @@
}
}
:global(.light-mode) .label {
:global(.light) .label {
@media (pointer: fine) {
&:hover path {
fill: $link;
@ -85,7 +88,7 @@
}
}
:global(.dark-mode) .label {
:global(.dark) .label {
@media (pointer: fine) {
&:hover path {
fill: $link-dark;

View File

@ -1,23 +1,27 @@
import { ComponentMeta, Story } from '@storybook/react'
import { DarkModeToggle } from '.'
import useDarkMode from 'use-dark-mode'
import { useDarkMode } from 'hooks/use-dark-mode'
import { Typography } from 'components/typography'
export default {
title: 'Dark Mode Toggle',
}
} as ComponentMeta<typeof DarkModeToggle>
export const ToggleDesktop = () => {
const darkMode = useDarkMode(false)
const Template: Story = (args) => {
const darkMode = useDarkMode(false, {
classNameDark: 'dark',
classNameLight: 'light',
element: document.documentElement,
})
return (
<div style={{ padding: 20 }}>
<DarkModeToggle />
<DarkModeToggle {...args} />
<br />
<Typography>dark mode: {darkMode.value ? 'enabled' : 'disabled'}</Typography>
</div>
)
}
ToggleDesktop.story = {
name: 'Desktop',
}
export const Default = Template.bind({})
Default.args = {}

View File

@ -1,11 +1,15 @@
import useDarkMode from 'use-dark-mode'
import { useDarkMode } from 'hooks/use-dark-mode'
import { SvgSun } from 'components/svgs/svg-sun'
import { SvgMoon } from 'components/svgs/svg-moon'
import styles from './index.module.scss'
const DarkModeToggle = () => {
const darkMode = useDarkMode(false)
const darkMode = useDarkMode(false, {
classNameDark: 'dark',
classNameLight: 'light',
element: typeof document !== 'undefined' ? document.documentElement : undefined,
})
return (
<div className={styles.root}>

View File

@ -23,7 +23,7 @@
}
}
:global(.dark-mode) .root {
:global(.dark) .root {
background: $dark-black;
&.transparent {
@ -81,7 +81,7 @@
}
}
:global(.dark-mode) .dashed {
:global(.dark) .dashed {
rect {
stroke: #495c70;
}
@ -116,7 +116,7 @@
}
}
:global(.dark-mode) .dropZoneWrapper {
:global(.dark) .dropZoneWrapper {
background: $dark-grey;
@include breakpoint-md {
@ -134,6 +134,7 @@
0% {
stroke-dashoffset: 0;
}
100% {
stroke-dashoffset: -20;
}
@ -159,7 +160,7 @@
}
}
:global(.dark-mode) .dropZoneFooter {
:global(.dark) .dropZoneFooter {
@include breakpoint-md {
background: rgba($dark-black, 0.88);
}
@ -221,7 +222,7 @@
}
}
:global(.dark-mode) .imageUploadText {
:global(.dark) .imageUploadText {
color: $white-dark;
@include breakpoint-md {
@ -249,7 +250,7 @@
}
}
:global(.dark-mode) .info {
:global(.dark) .info {
color: $off-white-dark;
}
@ -315,7 +316,7 @@
transition: all 0.2s ease;
}
:global(.dark-mode) .imageInfo {
:global(.dark) .imageInfo {
border-top: 1px solid $dark-grey;
}
@ -329,7 +330,7 @@
}
}
:global(.dark-mode) .imageOptionsWrapper {
:global(.dark) .imageOptionsWrapper {
border-top: 1px solid $dark-grey;
}
@ -459,7 +460,7 @@
}
}
:global(.dark-mode) .preview {
:global(.dark) .preview {
background: $off-white-dark;
}
@ -489,7 +490,7 @@
}
}
:global(.dark-mode) .showCode:hover {
:global(.dark) .showCode:hover {
color: $off-white-dark;
}
@ -501,7 +502,7 @@
align-items: center;
justify-content: flex-end;
width: 100%;
padding: 23px 24px 24px 24px;
padding: 23px 24px 24px;
border-top: 1px solid $light-grey;
transition: all 0.2s ease;
@ -511,7 +512,7 @@
@include breakpoint-md {
flex-direction: column-reverse;
padding: 11px 24px 12px 24px;
padding: 11px 24px 12px;
border-top: 1px solid transparent;
> * {
@ -524,7 +525,7 @@
}
}
:global(.dark-mode) .imageFooter {
:global(.dark) .imageFooter {
border-top: 1px solid $dark-grey;
@include breakpoint-md {
@ -539,7 +540,7 @@
}
}
:global(.dark-mode) .makeNewOne {
:global(.dark) .makeNewOne {
&:hover {
color: $off-white-dark;
}
@ -552,7 +553,7 @@
}
}
:global(.dark-mode) .reUpload {
:global(.dark) .reUpload {
&:hover {
color: $off-white-dark;
}
@ -609,11 +610,11 @@
bottom: 0;
left: 0;
z-index: 10;
background-color: rgba(255, 255, 255, 0.64);
background-color: rgba($white, 0.64);
}
:global(.dark-mode) .overlay {
background-color: rgba(0, 0, 0, 0.8);
:global(.dark) .overlay {
background-color: rgba($black, 0.8);
}
.content {
@ -663,7 +664,7 @@
}
}
:global(.dark-mode) .copyWrapper::before {
:global(.dark) .copyWrapper::before {
color: $off-white-dark;
}

View File

@ -1,33 +1,13 @@
import useDarkMode from 'use-dark-mode'
import { DragAndDrop } from '.'
import { Button } from 'components/button'
import { ComponentMeta, Story } from '@storybook/react'
import { DragAndDrop, DragAndDropProps } from '.'
export default {
title: 'Drag & Drop',
}
component: DragAndDrop,
argTypes: {},
} as ComponentMeta<typeof DragAndDrop>
export const DragAndDropDesktop = () => {
const { toggle } = useDarkMode(false)
const onGenerate = async (image: File) => {
console.log('onGenerate', image)
return Promise.resolve(new ArrayBuffer(8))
}
const onError = (message: string) => {
console.log(message)
}
const onFile = (hasFile: boolean) => {
console.log(hasFile)
}
const Template: Story<DragAndDropProps> = (args) => <DragAndDrop {...args} />
return (
<div style={{ padding: 20 }}>
<DragAndDrop onGenerate={onGenerate} onError={onError} onFile={onFile} />
<br />
<Button onClick={toggle}>Toggle Dark Mode</Button>
</div>
)
}
DragAndDropDesktop.story = {
name: 'Desktop',
}
export const Desktop = Template.bind({})
Desktop.args = {}

View File

@ -1,3 +1,4 @@
/* eslint-disable @next/next/no-img-element */
import dynamic from 'next/dynamic'
import React, { useState, useEffect, useCallback, useRef } from 'react'
import classnames from 'classnames'
@ -33,7 +34,7 @@ import styles from './index.module.scss'
const Modal = dynamic(() => import('react-modal'))
const Clipboard = dynamic(() => import('react-clipboard.js'))
type DragAndDropProps = {
export type DragAndDropProps = {
onFile: (hasFile: boolean) => void
onGenerate: (image: File, pwa: boolean, darkMode: boolean) => Promise<ArrayBuffer>
onError: (message: string) => void
@ -135,8 +136,8 @@ const DragAndDrop = ({ onFile, onGenerate, onError }: DragAndDropProps) => {
setIsLoading(true)
const zip = await onGenerate(file, pwa, darkMode)
setZipData(zip)
} catch (error) {
onError(error.message || 'Something went wrong. Please try again.')
} catch (error: any) {
onError((error.message as string) || 'Something went wrong. Please try again.')
} finally {
setIsLoading(false)
}

View File

@ -45,6 +45,7 @@
0% {
stroke-dashoffset: 0;
}
100% {
stroke-dashoffset: 44;
}

View File

@ -41,7 +41,7 @@
border: 1px solid $light-grey !important;
}
:global(.dark-mode) .button {
:global(.dark) .button {
color: $off-white-dark !important;
background: $dark-grey !important;
border: 1px solid $dark-grey !important;
@ -62,8 +62,8 @@
}
}
:global(.dark-mode) .firstParagraph,
:global(.dark-mode) .secondParagraph {
:global(.dark) .firstParagraph,
:global(.dark) .secondParagraph {
color: $white-dark !important;
}
@ -99,7 +99,7 @@
}
}
:global(.dark-mode) .footer {
:global(.dark) .footer {
button,
a {
color: $white-dark;
@ -120,7 +120,7 @@
background-color: rgba($light-grey, 0.64);
}
:global(.dark-mode) .overlay {
:global(.dark) .overlay {
background-color: rgba($black, 0.8);
}
@ -151,7 +151,7 @@
}
}
:global(.dark-mode) .content {
:global(.dark) .content {
background: $dark-black;
@include breakpoint-md {
@ -177,7 +177,7 @@
}
}
:global(.dark-mode) .modalContainer {
:global(.dark) .modalContainer {
background: $dark-black;
}
@ -228,7 +228,7 @@
margin-bottom: 24px;
}
:global(.dark-mode) .subTitle {
:global(.dark) .subTitle {
@include breakpoint-md {
color: $white-dark !important;
}
@ -261,7 +261,7 @@
margin-right: 16px;
}
:global(.dark-mode) .personRole {
:global(.dark) .personRole {
@include breakpoint-md {
color: $white-dark !important;
}
@ -307,7 +307,7 @@
}
/* stylelint-disable-next-line */
:global(.dark-mode) .personTwitter p {
:global(.dark) .personTwitter p {
color: #55acee;
&:hover {
@ -315,7 +315,7 @@
}
}
:global(.dark-mode) .personTwitter.personGithub p {
:global(.dark) .personTwitter.personGithub p {
color: $white;
&:hover {
@ -323,7 +323,7 @@
}
}
:global(.dark-mode) .personTwitter.personGithub p svg path {
:global(.dark) .personTwitter.personGithub p svg path {
fill: $white;
}

View File

@ -102,7 +102,7 @@
transition: all 0.2s ease;
}
:global(.light-mode) {
:global(.light) {
.imageLight {
opacity: 1;
}
@ -113,7 +113,7 @@
}
}
:global(.dark-mode) {
:global(.dark) {
.imageLight {
visibility: hidden;
opacity: 0;

View File

@ -6,12 +6,12 @@
.container {
height: 68px;
padding: 20px 20px 0 20px;
padding: 20px 20px 0;
margin: 0 auto;
@include breakpoint-md {
height: 74px;
padding: 26px 26px 0 26px;
padding: 26px 26px 0;
}
}
@ -56,7 +56,7 @@
}
}
:global(.dark-mode) .item {
:global(.dark) .item {
background: $darker-black;
box-shadow: 0 0 10px rgba($darker-black, 0.5);

View File

@ -0,0 +1,9 @@
import { Meta } from '@storybook/addon-docs'
<Meta title="Welcome" />
# Welcome to Favycon Storybook.
This is a development environment for the application UI components.
A story is a unique state one or more components. You can have as many stories as you want. Basically a story is virtual test case.

View File

@ -1,27 +0,0 @@
export default {
title: 'Welcome',
}
export const toStorybook = () => (
<article className="welcome">
<h1>Welcome to Favycon Storybook.</h1>
<p>This is a development environment for the application UI components.</p>
<p>
A story is a unique state one or more components. You can have as many stories as you want. Basically a story is
virtual test case.
</p>
<style jsx>{`
.welcome {
padding: 15px;
line-height: 1.4;
font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif;
background-color: rgb(255, 255, 255);
color: rgb(0, 0, 0);
}
`}</style>
</article>
)
toStorybook.story = {
name: 'to Storybook',
}

View File

@ -1,3 +1,4 @@
/* eslint-disable @next/next/no-img-element */
import React, { useRef, useEffect } from 'react'
import { LazyImageFull, ImageState } from 'react-lazy-images'
import objectFitImages from 'object-fit-images'

View File

@ -1,6 +1,6 @@
import React, { createContext, useContext, useMemo } from 'react'
import { useMedia } from 'react-use'
import PropTypes from 'prop-types'
import useMedia from 'use-media'
type MediaQueryProviderProps = {
children: PropTypes.ReactNodeLike

View File

@ -12,7 +12,7 @@
}
}
:global(.dark-mode) .unchecked {
:global(.dark) .unchecked {
fill: $dark-grey;
stroke: $darker-black;
stroke-width: 1.5px;

View File

@ -9,6 +9,6 @@
}
}
:global(.dark-mode) .rect {
:global(.dark) .rect {
stroke: $off-white-dark;
}

View File

@ -5,7 +5,7 @@
transition: fill 0.2s ease;
}
:global(.dark-mode) .background {
:global(.dark) .background {
fill: #495c70;
}
@ -14,7 +14,7 @@
transition: fill 0.2s ease;
}
:global(.dark-mode) .foreground {
:global(.dark) .foreground {
fill: #1e262f;
}
@ -23,7 +23,7 @@
transition: fill 0.2s ease;
}
:global(.dark-mode) .top {
:global(.dark) .top {
fill: #f6a044;
}
@ -32,7 +32,7 @@
transition: fill 0.2s ease;
}
:global(.dark-mode) .corner {
:global(.dark) .corner {
fill: #f9cdff;
}
@ -41,7 +41,7 @@
transition: fill 0.2s ease;
}
:global(.dark-mode) .left {
:global(.dark) .left {
fill: #728dff;
}
@ -50,6 +50,6 @@
transition: fill 0.2s ease;
}
:global(.dark-mode) .star {
:global(.dark) .star {
fill: #728dff;
}

View File

@ -51,7 +51,7 @@
}
}
:global(.dark-mode) .rect {
:global(.dark) .rect {
fill: #495c70;
&.active {

View File

@ -9,7 +9,7 @@
}
}
:global(.dark-mode) .background {
:global(.dark) .background {
fill: $dark-grey;
&.active {
@ -26,7 +26,7 @@
}
}
:global(.dark-mode) .text {
:global(.dark) .text {
fill: $off-white-dark;
&.active {

View File

@ -16,9 +16,9 @@
}
}
:global(.dark-mode) .root {
:global(.dark) .root {
h1 {
color: #e4ebf2;
color: $off-white-dark;
}
span {

View File

@ -1,24 +1,12 @@
import { withKnobs } from '@storybook/addon-knobs'
import useDarkMode from 'use-dark-mode'
import { Button } from 'components/button'
import { ToolTitle } from '.'
import { ComponentMeta, Story } from '@storybook/react'
import { ToolTitle, ToolTitleProps } from '.'
export default {
title: 'Tool Title',
decorators: [withKnobs],
}
component: ToolTitle,
} as ComponentMeta<typeof ToolTitle>
export const ToolTitleDesktop = () => {
const { toggle } = useDarkMode(false)
return (
<div style={{ padding: 20 }}>
<ToolTitle />
<br />
<Button onClick={toggle}>Toggle Dark Mode</Button>
</div>
)
}
const Template: Story<ToolTitleProps> = (args) => <ToolTitle {...args} />
ToolTitleDesktop.story = {
name: 'desktop',
}
export const Desktop = Template.bind({})
Desktop.args = {}

View File

@ -3,7 +3,7 @@ import { SvgFavycon } from 'components/svgs/svg-favycon'
import styles from './index.module.scss'
type ToolTitleProps = {
export type ToolTitleProps = {
hideLogo?: boolean
small?: boolean
}

View File

@ -106,7 +106,7 @@
transition: color 0.2s ease;
}
:global(.dark-mode) .black {
:global(.dark) .black {
color: $off-white;
}
@ -114,7 +114,7 @@
color: $grey;
}
:global(.dark-mode) .gray {
:global(.dark) .gray {
color: $grey-dark;
}
@ -123,7 +123,7 @@
transition: color 0.2s ease;
}
:global(.dark-mode) .white:not(.colorImmutable) {
:global(.dark) .white:not(.colorImmutable) {
color: $black;
}
@ -136,6 +136,6 @@
transition: color 0.2s ease;
}
:global(.dark-mode) .muted {
:global(.dark) .muted {
color: $grey-dark;
}

View File

@ -1,34 +1,37 @@
import { withKnobs, text, select } from '@storybook/addon-knobs'
import useDarkMode from 'use-dark-mode'
import { Button } from 'components/button'
import { Typography } from '.'
import { ComponentMeta, Story } from '@storybook/react'
import { Typography, TypographyProps } from '.'
export default {
title: 'Typography',
decorators: [withKnobs],
}
component: Typography,
argTypes: {
variant: {
control: {
type: 'select',
},
options: ['h1', 'smallBody', 'regularBody', 'mediumBody', 'largeBody', 'footer', 'superscript'],
},
weight: {
control: {
type: 'select',
},
options: ['regular', 'medium', 'semiBold', 'bold'],
},
color: {
control: {
type: 'select',
},
options: ['black', 'gray', 'white'],
},
},
} as ComponentMeta<typeof Typography>
export const TypographyDesktop = () => {
const { toggle } = useDarkMode(false)
const variant = select(
'variant',
['h1', 'smallBody', 'regularBody', 'mediumBody', 'largeBody', 'footer', 'superscript'],
'h1'
)
const weight = select('weight', ['regular', 'medium', 'semiBold', 'bold'], 'regular')
const color = select('color', ['black', 'gray', 'white'], 'black')
const body = text('body', 'Some text')
return (
<div style={{ padding: 20 }}>
<Typography variant={variant} weight={weight} color={color}>
{body}
</Typography>
<br />
<Button onClick={toggle}>Toggle Dark Mode</Button>
</div>
)
}
const Template: Story<TypographyProps> = (args) => <Typography {...args} />
TypographyDesktop.story = {
name: 'Desktop',
export const Desktop = Template.bind({})
Desktop.args = {
variant: 'h1',
weight: 'regular',
color: 'black',
children: 'Some text',
}

View File

@ -4,7 +4,7 @@ import classNames from 'classnames'
import styles from './index.module.scss'
type TypographyProps = {
export type TypographyProps = {
children: PropTypes.ReactNodeLike
variant:
| 'h1'

View File

@ -8,6 +8,9 @@ describe('Drag & Drop', () => {
cy.wait(250)
})
/**
* @param {string} fixture
*/
function dropFile(fixture) {
cy.get('[data-cy="drag-and-drop"]').attachFile(fixture, {
subjectType: 'drag-n-drop',

86
hooks/use-dark-mode.ts Normal file
View File

@ -0,0 +1,86 @@
import { useEffect, useCallback, useMemo } from 'react'
import { useEvent, useLocalStorage } from 'react-use'
type DarkModeConfig = {
element?: HTMLElement
classNameDark?: string
classNameLight?: string
onChange?: (val: boolean) => void
storageKey?: string
global?: typeof globalThis
}
type MediaQueryEvents = {
addEventListener: (_: string, handler: (ev: MediaQueryListEvent) => void) => void
removeEventListener: (_: string, handler: (ev: MediaQueryListEvent) => void) => void
}
// eslint-disable-next-line @typescript-eslint/no-empty-function
const noop = () => {}
const mockElement = {
classList: {
add: noop,
remove: noop,
},
}
const preferDarkQuery = '(prefers-color-scheme: dark)'
export const useDarkMode = (
initialValue = false,
{
element,
classNameDark,
classNameLight,
onChange,
storageKey = 'darkMode',
global: glbl = global,
}: DarkModeConfig = {}
) => {
const mql = glbl.matchMedia ? glbl.matchMedia(preferDarkQuery) : ({} as MediaQueryList)
const mediaQueryEventTarget: MediaQueryEvents = {
addEventListener: (_, handler) => mql.addListener && mql.addListener(handler),
removeEventListener: (_, handler) => mql.removeListener && mql.removeListener(handler),
}
const isColorSchemeQuerySupported = mql.media === preferDarkQuery
const getInitialValue = (usersInitialState: boolean) =>
isColorSchemeQuerySupported ? mql.matches : usersInitialState
// Mock element if SSR else real body element.
const defaultElement = (glbl.document && glbl.document.body) || mockElement
const getDefaultOnChange = useCallback(
(element = defaultElement, classNameDark = 'dark-mode', classNameLight = 'light-mode') =>
(val: boolean) => {
element.classList.add(val ? classNameDark : classNameLight)
element.classList.remove(val ? classNameLight : classNameDark)
},
[defaultElement]
)
const [state, setState] = useLocalStorage(storageKey, getInitialValue(initialValue))
const stateChangeCallback = useMemo(
() => onChange || getDefaultOnChange(element, classNameDark, classNameLight),
[onChange, element, classNameDark, classNameLight, getDefaultOnChange]
)
// Call the onChange handler
useEffect(() => {
stateChangeCallback(state || false)
}, [stateChangeCallback, state])
// Listen for media changes and set state.
useEvent('change', ({ matches }) => setState(matches), mediaQueryEventTarget)
return {
value: state,
enable: useCallback(() => setState(true), [setState]),
disable: useCallback(() => setState(false), [setState]),
toggle: useCallback(() => setState(!state), [state, setState]),
}
}

5
next-env.d.ts vendored
View File

@ -1,2 +1,5 @@
/// <reference types="next" />
/// <reference types="next/types/global" />
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.

View File

@ -2,6 +2,7 @@
"name": "favycon",
"version": "1.0.0",
"description": "A favicon generator tool",
"private": true,
"repository": {
"type": "git",
"url": "git+https://github.com/ruisaraiva19/favycon.git"
@ -30,107 +31,109 @@
"build": "next build",
"start": "next start",
"lint": "npm-run-all --parallel lint:*",
"lint:scripts": "eslint --ext js,jsx,ts,tsx .",
"lint:styles": "stylelint ./**/*.{css,scss}",
"storybook": "start-storybook -s ./public -p 6006",
"storybook:build": "build-storybook -s ./public",
"lint:scripts": "eslint .",
"lint:styles": "npx stylelint \"**/*.{css,scss}\"",
"lint:json": "prettier --check \"**/*.json\"",
"postinstall": "husky install",
"storybook": "start-storybook -p 6006",
"storybook:build": "build-storybook",
"commit": "git-cz",
"e2e": "start-test dev 3000 cy:open",
"cy:open": "cypress open"
},
"dependencies": {
"@fiahfy/ico-convert": "0.0.7",
"adm-zip": "0.4.16",
"blurhash": "1.1.3",
"@fiahfy/ico-convert": "0.0.12",
"adm-zip": "0.5.9",
"blurhash": "1.1.4",
"classnames": "2.3.1",
"multer": "1.4.2",
"next": "10.2.3",
"multer": "1.4.3",
"next": "12.0.6",
"nprogress": "0.2.0",
"object-fit-images": "3.2.4",
"prismjs": "1.24.1",
"prismjs": "1.25.0",
"prop-types": "15.7.2",
"react": "16.14.0",
"react": "17.0.2",
"react-blurhash": "0.1.3",
"react-clipboard.js": "2.0.16",
"react-dom": "16.14.0",
"react-dropzone": "11.3.4",
"react-dom": "17.0.2",
"react-dropzone": "11.4.2",
"react-lazy-images": "1.1.0",
"react-modal": "3.14.3",
"react-modal": "3.14.4",
"react-transition-group": "4.4.2",
"react-use-gesture": "7.0.16",
"sharp": "0.25.0",
"use-dark-mode": "2.3.1",
"use-media": "1.4.0"
"react-use": "17.3.1",
"react-use-gesture": "7.0.16"
},
"devDependencies": {
"@babel/core": "7.14.6",
"@commitlint/cli": "12.1.4",
"@commitlint/config-conventional": "12.1.4",
"@storybook/addon-actions": "5.3.21",
"@storybook/addon-backgrounds": "5.3.21",
"@storybook/addon-console": "1.2.2",
"@storybook/addon-knobs": "5.3.21",
"@storybook/addon-links": "5.3.21",
"@storybook/addon-storysource": "5.3.21",
"@storybook/addon-viewport": "5.3.21",
"@storybook/addons": "5.3.21",
"@storybook/react": "5.3.21",
"@storybook/source-loader": "5.3.21",
"@types/adm-zip": "0.4.33",
"@babel/core": "7.16.0",
"@commitlint/cli": "15.0.0",
"@commitlint/config-conventional": "15.0.0",
"@storybook/addon-a11y": "6.4.7",
"@storybook/addon-actions": "6.4.7",
"@storybook/addon-console": "1.2.3",
"@storybook/addon-docs": "6.4.7",
"@storybook/addon-essentials": "6.4.7",
"@storybook/addon-links": "6.4.7",
"@storybook/addon-postcss": "2.0.0",
"@storybook/addons": "6.4.7",
"@storybook/api": "6.4.7",
"@storybook/builder-webpack5": "6.4.7",
"@storybook/client-api": "6.4.7",
"@storybook/components": "6.4.7",
"@storybook/core-events": "6.4.7",
"@storybook/manager-webpack5": "6.4.7",
"@storybook/node-logger": "6.4.7",
"@storybook/react": "6.4.7",
"@storybook/theming": "6.4.7",
"@types/adm-zip": "0.4.34",
"@types/multer": "1.4.7",
"@types/node": "14.14.34",
"@types/node": "16.11.11",
"@types/nprogress": "0.2.0",
"@types/object-fit-images": "3.2.2",
"@types/prismjs": "1.16.6",
"@types/react": "17.0.14",
"@types/react-modal": "3.12.1",
"@types/react-transition-group": "4.4.2",
"@types/sharp": "0.25.1",
"@typescript-eslint/eslint-plugin": "4.28.3",
"@typescript-eslint/parser": "4.28.3",
"@types/react": "17.0.37",
"@types/react-modal": "3.13.1",
"@types/react-transition-group": "4.4.4",
"@types/sharp": "0.29.4",
"@typescript-eslint/eslint-plugin": "5.5.0",
"@typescript-eslint/parser": "5.5.0",
"all-contributors-cli": "6.20.0",
"babel-eslint": "10.1.0",
"babel-loader": "8.2.2",
"babel-plugin-prismjs": "2.1.0",
"babel-preset-react-app": "10.0.0",
"babel-loader": "8.2.3",
"commitizen": "4.2.4",
"cypress": "5.6.0",
"cypress-file-upload": "4.1.1",
"cypress": "9.1.1",
"cypress-file-upload": "5.0.8",
"cz-conventional-changelog": "3.3.0",
"dotenv": "10.0.0",
"eslint": "7.30.0",
"eslint": "8.4.0",
"eslint-config-next": "12.0.6",
"eslint-config-prettier": "8.3.0",
"eslint-config-react": "1.1.7",
"eslint-config-react-app": "6.0.0",
"eslint-plugin-cypress": "2.11.3",
"eslint-plugin-flowtype": "5.8.0",
"eslint-plugin-import": "2.23.4",
"eslint-plugin-jsx-a11y": "6.4.1",
"eslint-plugin-prettier": "3.4.0",
"eslint-plugin-react": "7.24.0",
"eslint-plugin-react-hooks": "4.2.0",
"husky": "4.3.8",
"lint-staged": "11.0.0",
"eslint-plugin-cypress": "2.12.1",
"eslint-plugin-prettier": "4.0.0",
"eslint-plugin-storybook": "0.5.3",
"husky": "7.0.4",
"lint-staged": "12.1.2",
"npm-run-all": "4.1.5",
"postcss": "8.4.4",
"postcss-100vh-fix": "1.0.2",
"postcss-flexbugs-fixes": "5.0.2",
"postcss-preset-env": "6.7.0",
"postcss-preset-env": "7.0.1",
"postcss-scss": "4.0.2",
"prettier": "2.3.2",
"react-refresh": "0.10.0",
"regenerator-runtime": "0.13.7",
"sass": "1.35.2",
"sass-loader": "12.1.0",
"semantic-release": "17.4.4",
"start-server-and-test": "1.12.6",
"stylelint": "13.13.1",
"stylelint-config-css-modules": "2.2.0",
"stylelint-config-prettier": "8.0.2",
"stylelint-config-recess-order": "2.4.0",
"stylelint-config-standard": "22.0.0",
"stylelint-prettier": "1.2.0",
"stylelint-scss": "3.19.0",
"typescript": "4.3.5",
"webpack-notifier": "1.13.0"
"react-refresh": "0.11.0",
"sass": "1.44.0",
"sass-loader": "12.3.0",
"semantic-release": "18.0.1",
"start-server-and-test": "1.14.0",
"storybook-addon-next-router": "3.1.0",
"storybook-dark-mode": "1.0.8",
"stylelint": "14.1.0",
"stylelint-config-css-modules": "2.3.0",
"stylelint-config-prettier": "9.0.3",
"stylelint-config-recess-order": "3.0.0",
"stylelint-config-standard": "24.0.0",
"stylelint-prettier": "2.0.0",
"stylelint-scss": "4.0.0",
"typescript": "4.4.4",
"webpack": "5.64.4"
},
"config": {
"commitizen": {

View File

@ -1,3 +1,4 @@
/* eslint-disable @next/next/no-sync-scripts */
import Document, { Html, Head, Main, NextScript } from 'next/document'
const isProd = process.env.NODE_ENV === 'production'
@ -7,7 +8,9 @@ class MyDocument extends Document {
return (
<Html lang="en">
<Head>
{isProd && <script async src="https://www.googletagmanager.com/gtag/js?id=UA-171039315-1" />}
{isProd && (
<script async src={`https://www.googletagmanager.com/gtag/js?id=${process.env.NEXT_PUBLIC_GTM_ID || ''}`} />
)}
{isProd && (
<script
dangerouslySetInnerHTML={{
@ -15,7 +18,7 @@ class MyDocument extends Document {
window.dataLayer = window.dataLayer || [];
function gtag() { dataLayer.push(arguments); }
gtag('js', new Date());
gtag('config', 'UA-171039315-1', { 'anonymize_ip': true });
gtag('config', '${process.env.NEXT_PUBLIC_GTM_ID || ''}', { 'anonymize_ip': true });
`,
}}
/>

View File

@ -5,12 +5,12 @@
;(function () {
// Change these if you use something different in your hook.
var storageKey = 'darkMode'
var classNameDark = 'dark-mode'
var classNameLight = 'light-mode'
var classNameDark = 'dark'
var classNameLight = 'light'
function setClassOnDocumentBody(darkMode) {
document.body.classList.add(darkMode ? classNameDark : classNameLight)
document.body.classList.remove(darkMode ? classNameLight : classNameDark)
document.documentElement.classList.add(darkMode ? classNameDark : classNameLight)
document.documentElement.classList.remove(darkMode ? classNameLight : classNameDark)
}
var preferDarkQuery = '(prefers-color-scheme: dark)'
@ -34,8 +34,8 @@
setClassOnDocumentBody(mql.matches)
localStorage.setItem(storageKey, mql.matches)
} else {
// source of truth from document.body
var isDarkMode = document.body.classList.contains(classNameDark)
// source of truth from document.documentElement
var isDarkMode = document.documentElement.classList.contains(classNameDark)
localStorage.setItem(storageKey, JSON.stringify(isDarkMode))
}
})()

View File

@ -0,0 +1,9 @@
<svg width="272" height="71" viewBox="0 0 272 71" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.5354 12.8599L37.4579 9.2169C41.6306 8.63437 43.204 8.85173 44.847 9.45165C46.49 10.0516 47.8548 11.0862 48.8805 12.4947C49.915 13.9032 50.5583 15.3551 51.1407 19.5285L54.7831 45.4552C55.3655 49.6285 55.1482 51.2022 54.5484 52.8454C53.9486 54.4886 52.9141 55.8537 51.5058 56.8796C50.0976 57.9142 48.6458 58.5576 44.4732 59.1401L18.5506 62.7831C14.378 63.3656 12.8046 63.1483 11.1616 62.5484C9.51861 61.9484 8.1538 60.9138 7.12803 59.5053C6.09356 58.0968 5.45028 56.6449 4.86785 52.4715L1.21678 26.5448C0.634352 22.3715 0.851677 20.7978 1.45149 19.1546C2.05131 17.5114 3.08578 16.1463 4.49405 15.1204C5.91101 14.0945 7.36274 13.4424 11.5354 12.8599Z" fill="#2E3A47" />
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.0918 14.3727L37.3363 10.8254C40.8135 10.3385 42.1261 10.5211 43.4909 11.0253C44.8557 11.5296 45.9945 12.3817 46.8551 13.5641C47.7157 14.7379 48.2547 15.9464 48.7415 19.4328L51.8971 41.8992C52.4795 46.0725 52.2622 47.6462 51.6624 49.2894C51.0625 50.9327 50.0281 52.2977 48.6198 53.3236C47.2115 54.3582 45.7598 55.0016 41.5872 55.5842L20.3415 58.5663C16.1688 59.1489 14.5954 58.9315 12.9524 58.3316C11.3094 57.7317 9.94461 56.697 8.91884 55.2885C7.88437 53.8801 7.24109 52.4281 6.65866 48.2548L3.50309 25.7797C3.01629 22.302 3.19884 20.9891 3.70303 19.6241C4.20723 18.2591 5.05914 17.1201 6.24139 16.2594C7.40626 15.3986 8.61458 14.8596 12.0918 14.3727Z" fill="white" />
<path fill-rule="evenodd" clip-rule="evenodd" d="M35.276 25.4407L18.116 27.849L17.238 21.6064L40.6396 18.3199C41.1264 21.7629 38.7272 24.9538 35.276 25.4407Z" fill="#F6A044" />
<path fill-rule="evenodd" clip-rule="evenodd" d="M18.1161 27.849L11.8745 28.7271C11.3877 25.2841 13.787 22.0933 17.2381 21.6064L18.1161 27.849Z" fill="#493B39" />
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.0562 51.3587L11.8745 28.7271L18.1161 27.849L20.4197 44.2379C20.8979 47.6809 18.4986 50.8718 15.0562 51.3587Z" fill="#4C5ED5" />
<path fill-rule="evenodd" clip-rule="evenodd" d="M28.2087 39.2909L25.9572 40.9167C25.7138 41.0906 25.366 41.0384 25.1835 40.795C25.1139 40.6994 25.0792 40.5776 25.0792 40.4559L25.1661 37.6824C25.1748 37.4998 25.0879 37.3259 24.9401 37.2216L22.6973 35.5783C22.4539 35.3958 22.4017 35.0567 22.5756 34.8045C22.6451 34.7089 22.7494 34.6393 22.8625 34.6046L25.5312 33.8221C25.7051 33.7699 25.8442 33.6395 25.8963 33.4656L26.7656 30.8312C26.8612 30.5443 27.1742 30.3878 27.4611 30.4834C27.5741 30.5182 27.6784 30.5965 27.7479 30.7008L29.3127 32.9961C29.417 33.1439 29.5821 33.2395 29.7647 33.2395L32.5465 33.2482C32.8507 33.2482 33.0941 33.5004 33.0941 33.8047C33.0941 33.9264 33.0507 34.0394 32.9811 34.1438L31.2773 36.3435C31.1643 36.4913 31.1295 36.6738 31.1904 36.8477L32.0336 39.4908C32.1292 39.7777 31.964 40.0907 31.6772 40.1864C31.5642 40.2212 31.4338 40.2212 31.3207 40.1777L28.7042 39.2387C28.5477 39.1604 28.3564 39.1865 28.2087 39.2909Z" fill="#4C5ED5" />
<path d="M65.64 55H72.712V39.816H85.4V32.744H72.712V24.632H88.52V17.56H65.64V55ZM99.9986 55.78C102.165 55.78 103.985 55.4507 105.459 54.792C106.949 54.116 108.319 53.0413 109.567 51.568V55H115.755V37.84C115.755 36.5573 115.703 35.396 115.599 34.356C115.495 33.316 115.165 32.276 114.611 31.236C113.623 29.364 112.167 28.0467 110.243 27.284C108.319 26.5213 106.117 26.14 103.639 26.14C100.259 26.14 97.5893 26.868 95.6306 28.324C93.6719 29.78 92.3373 31.6693 91.6266 33.992L98.0226 36.02C98.4733 34.7373 99.2619 33.8447 100.389 33.342C101.515 32.8393 102.599 32.588 103.639 32.588C105.303 32.588 106.542 32.9 107.357 33.524C108.171 34.1307 108.648 35.0927 108.787 36.41C106.152 36.7913 103.829 37.164 101.819 37.528C99.8079 37.8747 98.1266 38.2907 96.7746 38.776C94.5213 39.608 92.9353 40.7433 92.0166 42.182C91.0979 43.6033 90.6386 45.2933 90.6386 47.252C90.6386 48.812 90.9939 50.242 91.7046 51.542C92.4326 52.8247 93.4899 53.856 94.8766 54.636C96.2806 55.3987 97.9879 55.78 99.9986 55.78ZM101.507 50.216C100.293 50.216 99.3313 49.9473 98.6206 49.41C97.9099 48.8553 97.5546 48.1013 97.5546 47.148C97.5546 46.4373 97.7799 45.8307 98.2306 45.328C98.6813 44.808 99.4266 44.34 100.467 43.924C101.264 43.6293 102.243 43.3607 103.405 43.118C104.583 42.858 106.36 42.5287 108.735 42.13C108.717 42.806 108.665 43.5513 108.579 44.366C108.492 45.1633 108.301 45.8653 108.007 46.472C107.816 46.8707 107.452 47.3647 106.915 47.954C106.395 48.5433 105.684 49.072 104.783 49.54C103.899 49.9907 102.807 50.216 101.507 50.216ZM129.091 55H136.163L146.355 26.92H139.283L132.627 46.368L125.971 26.92H118.899L129.091 55ZM154.388 67.48H160.94L176.8 26.92H169.728L162.396 46.03L155.012 26.92H147.68L159.016 54.766L154.388 67.48ZM191.817 55.78C195.075 55.78 197.814 54.974 200.033 53.362C202.251 51.75 203.759 49.4533 204.557 46.472L197.329 44.964C196.913 46.316 196.289 47.356 195.457 48.084C194.625 48.812 193.411 49.176 191.817 49.176C189.685 49.176 188.073 48.422 186.981 46.914C185.906 45.3887 185.369 43.404 185.369 40.96C185.369 38.62 185.88 36.67 186.903 35.11C187.943 33.5327 189.581 32.744 191.817 32.744C193.117 32.744 194.278 33.1513 195.301 33.966C196.341 34.7807 197.069 35.916 197.485 37.372L204.557 35.5C203.933 32.6053 202.494 30.326 200.241 28.662C198.005 26.9807 195.231 26.14 191.921 26.14C189.061 26.14 186.582 26.7813 184.485 28.064C182.387 29.3467 180.758 31.106 179.597 33.342C178.453 35.578 177.881 38.1173 177.881 40.96C177.881 43.768 178.435 46.29 179.545 48.526C180.654 50.762 182.249 52.53 184.329 53.83C186.409 55.13 188.905 55.78 191.817 55.78ZM221.631 55.78C224.439 55.78 226.909 55.156 229.041 53.908C231.191 52.6427 232.863 50.9007 234.059 48.682C235.273 46.446 235.879 43.872 235.879 40.96C235.879 38.0653 235.281 35.5087 234.085 33.29C232.889 31.054 231.217 29.3033 229.067 28.038C226.935 26.7727 224.457 26.14 221.631 26.14C218.858 26.14 216.405 26.764 214.273 28.012C212.141 29.26 210.469 31.002 209.255 33.238C208.042 35.4567 207.435 38.0307 207.435 40.96C207.435 43.8547 208.025 46.42 209.203 48.656C210.399 50.8747 212.063 52.6167 214.195 53.882C216.327 55.1473 218.806 55.78 221.631 55.78ZM221.631 49.176C219.43 49.176 217.757 48.4393 216.613 46.966C215.487 45.4753 214.923 43.4733 214.923 40.96C214.923 38.516 215.461 36.54 216.535 35.032C217.627 33.5067 219.326 32.744 221.631 32.744C223.867 32.744 225.549 33.4893 226.675 34.98C227.819 36.4707 228.391 38.464 228.391 40.96C228.391 43.3693 227.828 45.3453 226.701 46.888C225.592 48.4133 223.902 49.176 221.631 49.176ZM241.545 55H248.721V40.752C248.721 39.0013 248.912 37.5973 249.293 36.54C249.692 35.4827 250.194 34.6853 250.801 34.148C251.408 33.5933 252.058 33.2207 252.751 33.03C253.444 32.8393 254.094 32.744 254.701 32.744C256.209 32.744 257.37 33.082 258.185 33.758C259.017 34.434 259.615 35.2747 259.979 36.28C260.343 37.2853 260.56 38.2907 260.629 39.296C260.698 40.284 260.733 41.0987 260.733 41.74V55H267.909V39.244C267.909 38.776 267.874 38.0307 267.805 37.008C267.736 35.9853 267.536 34.8587 267.207 33.628C266.878 32.38 266.323 31.184 265.543 30.04C264.78 28.896 263.706 27.9513 262.319 27.206C260.932 26.4607 259.138 26.088 256.937 26.088C254.909 26.088 253.141 26.426 251.633 27.102C250.125 27.778 248.86 28.6793 247.837 29.806V26.92H241.545V55Z" fill="black" />
</svg>

After

Width:  |  Height:  |  Size: 7.1 KiB

View File

@ -1,49 +1,49 @@
@font-face {
font-family: 'Manrope';
font-family: Manrope;
font-style: normal;
font-weight: 400;
src: url('https://fonts.gstatic.com/s/manrope/v4/xn7gYHE41ni1AdIRggexSvfedN4.woff2') format('woff2');
font-display: swap;
src: url(https://fonts.gstatic.com/s/manrope/v4/xn7gYHE41ni1AdIRggexSvfedN4.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC,
U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: 'Manrope';
font-family: Manrope;
font-style: normal;
font-weight: 500;
src: url('https://fonts.gstatic.com/s/manrope/v4/xn7gYHE41ni1AdIRggexSvfedN4.woff2') format('woff2');
font-display: swap;
src: url(https://fonts.gstatic.com/s/manrope/v4/xn7gYHE41ni1AdIRggexSvfedN4.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC,
U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: 'Manrope';
font-family: Manrope;
font-style: normal;
font-weight: 600;
src: url('https://fonts.gstatic.com/s/manrope/v4/xn7gYHE41ni1AdIRggexSvfedN4.woff2') format('woff2');
font-display: swap;
src: url(https://fonts.gstatic.com/s/manrope/v4/xn7gYHE41ni1AdIRggexSvfedN4.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC,
U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: 'Manrope';
font-family: Manrope;
font-style: normal;
font-weight: 700;
src: url('https://fonts.gstatic.com/s/manrope/v4/xn7gYHE41ni1AdIRggexSvfedN4.woff2') format('woff2');
font-display: swap;
src: url(https://fonts.gstatic.com/s/manrope/v4/xn7gYHE41ni1AdIRggexSvfedN4.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC,
U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: 'Manrope';
font-family: Manrope;
font-style: normal;
font-weight: 800;
src: url('https://fonts.gstatic.com/s/manrope/v4/xn7gYHE41ni1AdIRggexSvfedN4.woff2') format('woff2');
font-display: swap;
src: url(https://fonts.gstatic.com/s/manrope/v4/xn7gYHE41ni1AdIRggexSvfedN4.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC,
U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

View File

@ -11,7 +11,7 @@ body {
/* Disables pull-to-refresh but allows overscroll glow effects. */
overscroll-behavior-y: contain;
font-family: Manrope, Arial, Helvetica Neue, Helvetica, sans-serif;
font-family: Manrope, Arial, 'Helvetica Neue', Helvetica, sans-serif;
/* https://gist.github.com/hsleonis/55712b0eafc9b25f1944 */
font-size: 100%;
@ -22,18 +22,18 @@ body {
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
&.light-mode {
&.light {
background: $off-white;
}
&.dark-mode {
&.dark {
background: $darker-black;
}
}
* {
box-sizing: border-box;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
-webkit-tap-highlight-color: rgba($black, 0);
}
hr {
@ -44,23 +44,26 @@ hr {
transition: background 0.2s ease;
}
.dark-mode {
.dark {
hr {
background: $darker-grey;
}
}
/* stylelint-disable-next-line */
.ReactModal__Overlay {
opacity: 0;
transition: all 0.2s ease;
transform: translate3d(0, 24px, 0);
}
/* stylelint-disable-next-line */
.ReactModal__Overlay--after-open {
opacity: 1;
transform: translate3d(0, 0, 0);
}
/* stylelint-disable-next-line */
.ReactModal__Overlay--before-close {
opacity: 0;
transform: translate3d(0, 24px, 0);
@ -68,25 +71,25 @@ hr {
.preload,
.preload * {
-webkit-transition: none !important;
-moz-transition: none !important;
-ms-transition: none !important;
-o-transition: none !important;
transition: none !important;
}
.collapse-enter {
max-width: 0;
opacity: 0;
}
.collapse-enter-active {
max-width: 50px;
opacity: 1;
transition: all 0.2s linear;
}
.collapse-exit {
max-width: 50px;
opacity: 1;
}
.collapse-exit-active {
max-width: 0;
opacity: 0;
@ -96,13 +99,16 @@ hr {
.fade-enter {
opacity: 0;
}
.fade-enter-active {
opacity: 1;
transition: opacity 0.2s linear;
}
.fade-exit {
opacity: 1;
}
.fade-exit-active {
opacity: 0;
transition: opacity 0.2s linear;
@ -112,14 +118,17 @@ hr {
opacity: 0;
transform: translate3d(0, -8px, 0);
}
.error-enter-active {
opacity: 1;
transition: opacity 0.3s ease-in-out, transform 0.3s ease-in-out;
transform: translate3d(0, 0, 0);
}
.error-exit {
opacity: 1;
}
.error-exit-active {
opacity: 0;
transition: opacity 0.3s ease-in-out, transform 0.3s ease-in-out;

View File

@ -58,6 +58,7 @@
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}

View File

@ -106,17 +106,21 @@ nav,
section {
display: block;
}
body {
line-height: 1;
}
ol,
ul {
list-style: none;
}
blockquote,
q {
quotes: none;
}
blockquote::before,
blockquote::after,
q::before,
@ -124,10 +128,12 @@ q::after {
content: ' ';
content: none;
}
table {
border-spacing: 0;
border-collapse: collapse;
}
button {
padding: 0;
border: 0;

View File

@ -1,27 +1,27 @@
@import 'breakpoints';
$white: #fff;
$white-dark: #a8b7c4;
$off-white: #fafafa;
$off-white-dark: #e4ebf2;
$white: rgb(255, 255, 255);
$white-dark: rgb(168, 183, 196);
$off-white: rgb(250, 250, 250);
$off-white-dark: rgb(228, 235, 242);
$grey: #9698a3;
$grey-dark: #657584;
$light-grey: #ebebeb;
$dark-grey: #2e3a47;
$darker-grey: #33353d;
$grey: rgb(150, 152, 163);
$grey-dark: rgb(101, 117, 132);
$light-grey: rgb(235, 235, 235);
$dark-grey: rgb(46, 58, 71);
$darker-grey: rgb(51, 53, 61);
$black: #000;
$dark-black: #1e262f;
$darker-black: #0e141a;
$black: rgb(0, 0, 0);
$dark-black: rgb(30, 38, 47);
$darker-black: rgb(14, 20, 26);
$link: #4c5ed5;
$link-dark: #7083ff;
$link-hover: #7683e2;
$link: rgb(76, 94, 213);
$link-dark: rgb(112, 131, 255);
$link-hover: rgb(118, 131, 226);
$red: #e15151;
$green: #3ea161;
$green-hover: #69b985;
$red: rgb(225, 81, 81);
$green: rgb(62, 161, 97);
$green-hover: rgb(105, 185, 133);
$font-size-h1: 52px;
$line-height-h1: 64px;

View File

@ -17,7 +17,8 @@
"noFallthroughCasesInSwitch": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"baseUrl": "."
"baseUrl": ".",
"incremental": true
},
"exclude": ["node_modules"],
"include": ["**/*.ts", "**/*.tsx", "./*.js", "./public/**/*.js", "./cypress/**/*.js"]

View File

@ -7,6 +7,7 @@ export const isTouchCapable = () => {
// @ts-ignore
(window.DocumentTouch && document instanceof window.DocumentTouch) ||
navigator.maxTouchPoints > 0 ||
// @ts-ignore
window.navigator.msMaxTouchPoints > 0
)
}

View File

@ -27,6 +27,7 @@ export const getImageFileSizes = (file: File) =>
export const downloadFile = (data: ArrayBuffer, filename: string) => {
if ('msSaveOrOpenBlob' in window.navigator) {
// Blob for IE11
// @ts-ignore
window.navigator.msSaveOrOpenBlob(new Blob([data]), filename)
} else {
// Blob navigator

13875
yarn.lock

File diff suppressed because it is too large Load Diff