mirror of
https://github.com/dzeiocom/components.git
synced 2025-04-22 10:52:16 +00:00
0.2.0
Signed-off-by: Florian BOUILLON <florian.bouillon@delta-wings.net>
This commit is contained in:
parent
2013f0bf41
commit
d80fda2589
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
* text=lf
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,6 +1,7 @@
|
|||||||
module/
|
module/
|
||||||
storybook-static/
|
storybook-static/
|
||||||
index.js
|
*.js
|
||||||
style.css
|
style.css
|
||||||
yarn-error.log
|
yarn-error.log
|
||||||
node_modules
|
node_modules
|
||||||
|
types
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@dzeio/components",
|
"name": "@dzeio/components",
|
||||||
"version": "0.1.0",
|
"version": "0.2.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"main": "./index.js",
|
"main": "./index.js",
|
||||||
"module": "./module/index.js",
|
"module": "./module/index.js",
|
||||||
@ -44,7 +44,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"storybook": "start-storybook -p 6006",
|
"storybook": "start-storybook -p 6006",
|
||||||
"build-storybook": "build-storybook",
|
"build-storybook": "build-storybook",
|
||||||
"build": "tsc || rollup --config",
|
"build": "tsc && rollup --config",
|
||||||
"prepublishOnly": "yarn build"
|
"prepublishOnly": "yarn build"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ interface Props {
|
|||||||
// Wrapper
|
// Wrapper
|
||||||
outline?: boolean
|
outline?: boolean
|
||||||
className?: string
|
className?: string
|
||||||
|
onClick?: (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => void
|
||||||
|
|
||||||
// Header
|
// Header
|
||||||
title?: string
|
title?: string
|
||||||
@ -25,7 +26,7 @@ interface Props {
|
|||||||
|
|
||||||
export default class Box extends React.Component<Props> {
|
export default class Box extends React.Component<Props> {
|
||||||
public render = () => (
|
public render = () => (
|
||||||
<BoxWrapper outline={this.props.outline} className={this.props.className}>
|
<BoxWrapper onClick={this.props.onClick} outline={this.props.outline} className={this.props.className}>
|
||||||
{(this.props.headerButtons || this.props.title || this.props.titleColSize || this.props.subtitle || this.props.delimiter || this.props.titleClassName) && (
|
{(this.props.headerButtons || this.props.title || this.props.titleColSize || this.props.subtitle || this.props.delimiter || this.props.titleClassName) && (
|
||||||
<BoxHeader
|
<BoxHeader
|
||||||
title={this.props.title}
|
title={this.props.title}
|
||||||
@ -37,7 +38,6 @@ export default class Box extends React.Component<Props> {
|
|||||||
{this.props.headerButtons}
|
{this.props.headerButtons}
|
||||||
</BoxHeader>
|
</BoxHeader>
|
||||||
)}
|
)}
|
||||||
<BoxHeader></BoxHeader>
|
|
||||||
<BoxBody noPadding={this.props.noPadding}>
|
<BoxBody noPadding={this.props.noPadding}>
|
||||||
{this.props.children}
|
{this.props.children}
|
||||||
</BoxBody>
|
</BoxBody>
|
||||||
|
@ -15,8 +15,8 @@
|
|||||||
border-radius 4px
|
border-radius 4px
|
||||||
border none
|
border none
|
||||||
justify-content center
|
justify-content center
|
||||||
color white
|
color $textOnMain
|
||||||
background-color $default
|
background-color $main
|
||||||
|
|
||||||
// Chrome Specific
|
// Chrome Specific
|
||||||
outline none
|
outline none
|
||||||
@ -68,22 +68,31 @@
|
|||||||
margin-top 8px
|
margin-top 8px
|
||||||
|
|
||||||
&:disabled
|
&:disabled
|
||||||
background #E0E0E0 !important
|
background $grayLight
|
||||||
color #B0B0B0 !important
|
color $grayDark
|
||||||
transform none !important
|
transform none
|
||||||
box-shadow none !important
|
box-shadow none
|
||||||
cursor initial
|
cursor initial
|
||||||
|
|
||||||
|
&.outline
|
||||||
|
border 2px solid @grayDark
|
||||||
|
background transparent
|
||||||
|
color @grayDark
|
||||||
|
|
||||||
|
@media (prefers-color-scheme dark)
|
||||||
|
border 2px solid @grayLight
|
||||||
|
color @grayLight
|
||||||
|
|
||||||
&.loading
|
&.loading
|
||||||
color transparent !important
|
color transparent
|
||||||
position relative
|
position relative
|
||||||
pointer-events none
|
pointer-events none
|
||||||
|
|
||||||
&::after
|
&::after
|
||||||
content ""
|
content ""
|
||||||
display block
|
display block
|
||||||
border white 2px solid
|
border $textOnMain 2px solid
|
||||||
border-color transparent transparent white white
|
border-color transparent transparent $textOnMain $textOnMain
|
||||||
width 1em
|
width 1em
|
||||||
position absolute
|
position absolute
|
||||||
top calc(50% - (1em / 2))
|
top calc(50% - (1em / 2))
|
||||||
@ -93,105 +102,69 @@
|
|||||||
box-sizing inherit
|
box-sizing inherit
|
||||||
animation ButtonLoading 1s infinite linear
|
animation ButtonLoading 1s infinite linear
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.textInner
|
.textInner
|
||||||
margin-left 8px
|
margin-left 8px
|
||||||
|
|
||||||
.primary
|
/**
|
||||||
background-color $primary
|
* $color: the color to use
|
||||||
|
* $theme: the theme used ('lighten' | 'darken')
|
||||||
|
*/
|
||||||
|
btn($color, $theme)
|
||||||
|
background-color $color
|
||||||
|
|
||||||
|
// Get Text Color
|
||||||
|
$textColor = white
|
||||||
|
if $theme is 'darken'
|
||||||
|
$textColor = black
|
||||||
|
else
|
||||||
|
$textColor = white
|
||||||
|
|
||||||
|
color $textColor
|
||||||
|
|
||||||
&.outline
|
&.outline
|
||||||
color @background-color
|
color @background-color
|
||||||
border-color @background-color
|
border-color @background-color
|
||||||
|
|
||||||
&:hover
|
&:hover
|
||||||
background-color @background-color
|
|
||||||
|
|
||||||
&:active
|
&:active
|
||||||
&:focus
|
&:focus
|
||||||
background-color darken(@background-color, 30%)
|
|
||||||
|
|
||||||
&.loading::after
|
|
||||||
border-color transparent transparent @background-color @background-color
|
|
||||||
|
|
||||||
.secondary
|
|
||||||
background-color $secondary
|
|
||||||
color black
|
|
||||||
&.outline
|
|
||||||
color @color
|
color @color
|
||||||
border-color @background-color
|
|
||||||
|
|
||||||
&:hover
|
&:hover
|
||||||
background-color @background-color
|
background-color @background-color
|
||||||
|
box-shadow 0 4px 4px rgba(@background-color,.2)
|
||||||
|
|
||||||
&:active
|
&:active
|
||||||
&:focus
|
&:focus
|
||||||
|
if $theme is 'darken'
|
||||||
background-color darken(@background-color, 30%)
|
background-color darken(@background-color, 30%)
|
||||||
|
else
|
||||||
|
background-color lighten(@background-color, 30%)
|
||||||
|
|
||||||
&.loading::after
|
&.loading
|
||||||
border-color transparent transparent @color @color
|
color transparent
|
||||||
.info
|
&::after
|
||||||
background-color $info
|
border-color transparent transparent $textColor $textColor
|
||||||
&.outline
|
|
||||||
color @background-color
|
|
||||||
border-color @background-color
|
|
||||||
|
|
||||||
&:hover
|
.info:not(:disabled)
|
||||||
background-color @background-color
|
btn($infoLight, 'darken')
|
||||||
|
@media (prefers-color-scheme dark)
|
||||||
|
btn($infoDark, 'lighten')
|
||||||
|
|
||||||
&:active
|
.success:not(:disabled)
|
||||||
&:focus
|
btn($successLight, 'darken')
|
||||||
background-color darken(@background-color, 30%)
|
@media (prefers-color-scheme dark)
|
||||||
|
btn($successDark, 'lighten')
|
||||||
|
|
||||||
&.loading::after
|
.error:not(:disabled)
|
||||||
border-color transparent transparent @background-color @background-color
|
btn($errorLight, 'darken')
|
||||||
.success
|
@media (prefers-color-scheme dark)
|
||||||
background-color $success
|
btn($errorDark, 'lighten')
|
||||||
&.outline
|
|
||||||
color @background-color
|
|
||||||
border-color @background-color
|
|
||||||
|
|
||||||
&:hover
|
.warning:not(:disabled)
|
||||||
background-color @background-color
|
btn($warningLight, 'darken')
|
||||||
|
@media (prefers-color-scheme dark)
|
||||||
&:active
|
btn($warningDark, 'lighten')
|
||||||
&:focus
|
|
||||||
background-color darken(@background-color, 30%)
|
|
||||||
|
|
||||||
&.loading::after
|
|
||||||
border-color transparent transparent @background-color @background-color
|
|
||||||
.danger
|
|
||||||
background-color $danger
|
|
||||||
|
|
||||||
&.outline
|
|
||||||
color @background-color
|
|
||||||
border-color @background-color
|
|
||||||
|
|
||||||
&:hover
|
|
||||||
background-color @background-color
|
|
||||||
|
|
||||||
&:active
|
|
||||||
&:focus
|
|
||||||
background-color darken(@background-color, 30%)
|
|
||||||
|
|
||||||
&.loading::after
|
|
||||||
border-color transparent transparent @background-color @background-color
|
|
||||||
.warning
|
|
||||||
background-color $warning
|
|
||||||
|
|
||||||
&.outline
|
|
||||||
color @background-color
|
|
||||||
border-color @background-color
|
|
||||||
|
|
||||||
&:hover
|
|
||||||
background-color @background-color
|
|
||||||
|
|
||||||
&:active
|
|
||||||
&:focus
|
|
||||||
background-color darken(@background-color, 30%)
|
|
||||||
|
|
||||||
&.loading::after
|
|
||||||
border-color transparent transparent @background-color @background-color
|
|
||||||
|
|
||||||
@keyframes ButtonLoading
|
@keyframes ButtonLoading
|
||||||
0%
|
0%
|
||||||
|
@ -6,6 +6,8 @@ import Image from '../Image'
|
|||||||
|
|
||||||
import css from './Button.module.styl'
|
import css from './Button.module.styl'
|
||||||
|
|
||||||
|
// MAKE OUTLINE use Fieldset instead of the current one xd
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
outline?: boolean
|
outline?: boolean
|
||||||
nomargintop?: boolean
|
nomargintop?: boolean
|
||||||
@ -31,7 +33,7 @@ export default class Button extends React.Component<Props> {
|
|||||||
inner = (
|
inner = (
|
||||||
<>
|
<>
|
||||||
{typeof Icon === 'string' ? (
|
{typeof Icon === 'string' ? (
|
||||||
<Image src={Icon} max={{ height: 16, width: 16 }} />
|
<Image src={Icon} width={16} height={16} />
|
||||||
) : (
|
) : (
|
||||||
<Icon size={16} />
|
<Icon size={16} />
|
||||||
)}
|
)}
|
||||||
|
@ -17,7 +17,6 @@ interface Props extends React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLIn
|
|||||||
|
|
||||||
export default class Checkbox extends React.Component<Props> {
|
export default class Checkbox extends React.Component<Props> {
|
||||||
|
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const props: Props = Object.assign({}, this.props)
|
const props: Props = Object.assign({}, this.props)
|
||||||
delete props.label
|
delete props.label
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
@import '../config.styl'
|
@import '../config.styl'
|
||||||
|
|
||||||
.fieldset
|
.fieldset
|
||||||
border-radius 8px
|
border-radius 4px
|
||||||
border 2px solid grey
|
border 2px solid grey
|
||||||
transition all $transition
|
transition all $transition
|
||||||
margin 0
|
margin 0
|
||||||
@ -10,9 +10,10 @@
|
|||||||
font-weight 600
|
font-weight 600
|
||||||
transition all $transition
|
transition all $transition
|
||||||
padding 0 4px
|
padding 0 4px
|
||||||
|
@media (prefers-color-scheme dark)
|
||||||
|
color white
|
||||||
&:hover
|
&:hover
|
||||||
border-color black
|
border-color black
|
||||||
|
|
||||||
> legend
|
@media (prefers-color-scheme dark)
|
||||||
color $default
|
border-color white
|
||||||
|
@ -5,61 +5,21 @@ import NextImage from 'next/image'
|
|||||||
import css from './Image.module.styl'
|
import css from './Image.module.styl'
|
||||||
|
|
||||||
export interface ImageProps {
|
export interface ImageProps {
|
||||||
defaultHeight?: number
|
src: string
|
||||||
src?: string
|
|
||||||
sources?: Array<string>
|
|
||||||
deleteOnError?: boolean
|
deleteOnError?: boolean
|
||||||
downgradeOnError?: string
|
|
||||||
canFullscreen?: boolean
|
canFullscreen?: boolean
|
||||||
max?: {
|
width: number
|
||||||
height?: number|string
|
height: number
|
||||||
width?: number|string
|
|
||||||
}
|
|
||||||
width?: number|string
|
|
||||||
default?: {
|
|
||||||
height?: number|string
|
|
||||||
width?: number|string
|
|
||||||
}
|
|
||||||
alt?: string
|
alt?: string
|
||||||
classes?: string
|
|
||||||
|
// ClassNames
|
||||||
|
parentClassName?: string
|
||||||
className?: string
|
className?: string
|
||||||
|
|
||||||
|
// Events
|
||||||
onClick?: () => void
|
onClick?: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
enum images {
|
|
||||||
JPEG = 'image/jpeg',
|
|
||||||
XICON = 'image/x-icon',
|
|
||||||
TIFF = 'image/tiff'
|
|
||||||
}
|
|
||||||
|
|
||||||
const mimeTypes = {
|
|
||||||
apng: 'image/apng',
|
|
||||||
bmp: 'image/bmp',
|
|
||||||
gif: 'image/gif',
|
|
||||||
|
|
||||||
ico: images.XICON,
|
|
||||||
cur: images.XICON,
|
|
||||||
|
|
||||||
jpg: images.JPEG,
|
|
||||||
jpeg: images.JPEG,
|
|
||||||
jfif: images.JPEG,
|
|
||||||
pjpeg: images.JPEG,
|
|
||||||
pjp: images.JPEG,
|
|
||||||
|
|
||||||
png: 'image/png',
|
|
||||||
svg: 'image/svg+xml',
|
|
||||||
|
|
||||||
tif: images.TIFF,
|
|
||||||
tiff: images.TIFF,
|
|
||||||
|
|
||||||
webp: 'image/webp'
|
|
||||||
}
|
|
||||||
|
|
||||||
const getMimeType = (img: string) => {
|
|
||||||
const arr = img.split('.')
|
|
||||||
return mimeTypes[arr[arr.length-1] as 'apng'] || mimeTypes.png
|
|
||||||
}
|
|
||||||
|
|
||||||
type evType<T = HTMLImageElement> = React.SyntheticEvent<T, Event>
|
type evType<T = HTMLImageElement> = React.SyntheticEvent<T, Event>
|
||||||
|
|
||||||
export default class Image extends React.Component<ImageProps> {
|
export default class Image extends React.Component<ImageProps> {
|
||||||
@ -69,7 +29,6 @@ export default class Image extends React.Component<ImageProps> {
|
|||||||
private parent: React.RefObject<HTMLDivElement> = React.createRef()
|
private parent: React.RefObject<HTMLDivElement> = React.createRef()
|
||||||
private pic: React.RefObject<HTMLDivElement> = React.createRef()
|
private pic: React.RefObject<HTMLDivElement> = React.createRef()
|
||||||
|
|
||||||
private wasDowngraded = false
|
|
||||||
private cardPos: Array<number> = []
|
private cardPos: Array<number> = []
|
||||||
private cardSize: Array<number> = []
|
private cardSize: Array<number> = []
|
||||||
|
|
||||||
@ -104,30 +63,15 @@ export default class Image extends React.Component<ImageProps> {
|
|||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const pic = (
|
const pic = (
|
||||||
// <picture ref={this.pic} className={this.props.classes}>
|
<div ref={this.pic} className={buildClassName(this.props.parentClassName, css.parent)}>
|
||||||
// {this.props.sources && this.props.sources.map((el, index) => (
|
|
||||||
// <source key={index} srcSet={el} type={getMimeType(el)}/>
|
|
||||||
// ))}
|
|
||||||
// <img
|
|
||||||
// />
|
|
||||||
// </picture>
|
|
||||||
<div ref={this.pic} className={buildClassName(this.props.classes, css.parent)}>
|
|
||||||
<NextImage
|
<NextImage
|
||||||
className={buildClassName([css.image], [this.props.className])}
|
className={buildClassName([css.image], [this.props.className])}
|
||||||
// ref={this.ref}
|
src={this.props.src}
|
||||||
src={this.props.src || ''}
|
onClick={this.props.canFullscreen ? this.onClick : this.props.onClick}
|
||||||
onClick={this.props.canFullscreen && this.onClick || this.props.onClick}
|
|
||||||
onLoad={this.props.default && this.onLoad || undefined}
|
|
||||||
onError={this.props.deleteOnError && this.onError || undefined}
|
onError={this.props.deleteOnError && this.onError || undefined}
|
||||||
// layout="fill"
|
// layout="fill"
|
||||||
width={200}
|
width={this.props.width}
|
||||||
height={44}
|
height={this.props.height}
|
||||||
// style={{
|
|
||||||
// width: this.props.default?.width,
|
|
||||||
// height: this.props.default?.height,
|
|
||||||
// maxHeight: this.props.max?.height,
|
|
||||||
// maxWidth: this.props.max?.width
|
|
||||||
// }}
|
|
||||||
alt={this.props.alt}
|
alt={this.props.alt}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -191,8 +135,8 @@ export default class Image extends React.Component<ImageProps> {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
const w = this.valToPixel(this.props.width)
|
const w = this.valToPixel(this.props.width)
|
||||||
const mh = this.valToPixel(this.props.max?.height)
|
const mh = this.valToPixel(this.props?.height)
|
||||||
const mw = this.valToPixel(this.props.max?.width)
|
const mw = this.valToPixel(this.props?.width)
|
||||||
c.classList.add(css.none)
|
c.classList.add(css.none)
|
||||||
i.style.height = ''
|
i.style.height = ''
|
||||||
i.style.width = w
|
i.style.width = w
|
||||||
|
@ -2,7 +2,12 @@
|
|||||||
|
|
||||||
.parent
|
.parent
|
||||||
position relative
|
position relative
|
||||||
margin 16px
|
margin 16px 0
|
||||||
|
max-width 100%
|
||||||
|
display inline-block
|
||||||
|
|
||||||
|
+ .parent
|
||||||
|
margin-left 16px
|
||||||
|
|
||||||
label
|
label
|
||||||
font-size 1rem
|
font-size 1rem
|
||||||
@ -22,23 +27,104 @@
|
|||||||
svg
|
svg
|
||||||
position absolute
|
position absolute
|
||||||
color #AAA
|
color #AAA
|
||||||
top 14px
|
|
||||||
left 16px // input padding-left
|
|
||||||
transition color $transition
|
transition color $transition
|
||||||
pointer-events none
|
pointer-events none
|
||||||
|
top 14px
|
||||||
|
&.left
|
||||||
|
left 16px // input padding-left
|
||||||
|
|
||||||
~ label
|
~ label
|
||||||
left 16px + 24px + 16px
|
left 16px + 24px + 16px
|
||||||
|
|
||||||
|
&.right
|
||||||
|
right 16px
|
||||||
|
|
||||||
select
|
select
|
||||||
appearance none
|
appearance none
|
||||||
|
|
||||||
|
option
|
||||||
|
background $foregroundLight
|
||||||
|
color black
|
||||||
|
@media (prefers-color-scheme dark)
|
||||||
|
background lighten($foregroundDark, 5%)
|
||||||
|
color white
|
||||||
|
|
||||||
|
textarea
|
||||||
|
resize none
|
||||||
|
overflow-y hidden
|
||||||
|
|
||||||
|
|
||||||
|
/* Remove the arrows from the Number Input */
|
||||||
|
input[type="number"]
|
||||||
|
-moz-appearance textfield
|
||||||
|
|
||||||
|
input::-webkit-outer-spin-button
|
||||||
|
input::-webkit-inner-spin-button
|
||||||
|
-webkit-appearance none
|
||||||
|
margin 0
|
||||||
|
/* End */
|
||||||
|
|
||||||
|
.autocomplete
|
||||||
|
display flex
|
||||||
|
opacity 0
|
||||||
|
transition all $transition
|
||||||
|
pointer-events none
|
||||||
|
// display flex
|
||||||
|
flex-direction column
|
||||||
|
list-style none
|
||||||
|
position absolute
|
||||||
|
top calc(100% - 4px)
|
||||||
|
left 0
|
||||||
|
width 100%
|
||||||
|
z-index 100
|
||||||
|
box-shadow 0 4px 8px rgba(black, .3)
|
||||||
|
margin 0
|
||||||
|
padding 0
|
||||||
|
background darken($foregroundLight, 5%)
|
||||||
|
@media (prefers-color-scheme dark)
|
||||||
|
background lighten($foregroundDark, 5%)
|
||||||
|
|
||||||
|
border-bottom-left-radius 4px
|
||||||
|
border-bottom-right-radius 4px
|
||||||
|
max-height 25vh
|
||||||
|
overflow-y auto
|
||||||
|
@media (max-width $mobile)
|
||||||
|
max-height 50vh
|
||||||
|
&.reverse
|
||||||
|
flex-direction column-reverse
|
||||||
|
top initial
|
||||||
|
bottom 100%
|
||||||
|
box-shadow 0 -4px 8px rgba(black, .3)
|
||||||
|
border-radius 0
|
||||||
|
border-top-left-radius 4px
|
||||||
|
border-top-right-radius 4px
|
||||||
|
li
|
||||||
|
transition all $transition
|
||||||
|
padding 8px
|
||||||
|
@media (max-width $mobile)
|
||||||
|
padding 24px
|
||||||
|
cursor pointer
|
||||||
|
&:hover
|
||||||
|
background darken(@background, 20%)
|
||||||
|
@media (prefers-color-scheme dark)
|
||||||
|
background lighten(lighten($foregroundDark, 5%), 20%)
|
||||||
|
|
||||||
|
|
||||||
|
input:focus + .autocomplete
|
||||||
|
select:focus + .autocomplete
|
||||||
|
textarea:focus + .autocomplete
|
||||||
|
.autocomplete:hover
|
||||||
|
opacity 1
|
||||||
|
pointer-events inherit
|
||||||
|
|
||||||
input
|
input
|
||||||
select
|
select
|
||||||
|
textarea
|
||||||
padding 14px 16px
|
padding 14px 16px
|
||||||
height 56px
|
height 56px
|
||||||
border 2px solid rgba(black, .3)
|
border 2px solid rgba(black, .3)
|
||||||
border-radius 4px
|
border-radius 4px
|
||||||
|
max-width 100%
|
||||||
box-sizing border-box
|
box-sizing border-box
|
||||||
font-size .875rem
|
font-size .875rem
|
||||||
outline none
|
outline none
|
||||||
@ -49,7 +135,7 @@
|
|||||||
border-color rgba(white, .3)
|
border-color rgba(white, .3)
|
||||||
color white
|
color white
|
||||||
|
|
||||||
&:hover
|
&:not(:disabled):hover
|
||||||
border-color black
|
border-color black
|
||||||
@media (prefers-color-scheme dark)
|
@media (prefers-color-scheme dark)
|
||||||
border-color white
|
border-color white
|
||||||
@ -72,6 +158,16 @@
|
|||||||
@media (prefers-color-scheme dark)
|
@media (prefers-color-scheme dark)
|
||||||
background #202020
|
background #202020
|
||||||
|
|
||||||
|
&:disabled
|
||||||
|
border-color #999
|
||||||
|
|
||||||
|
@media (prefers-color-scheme dark)
|
||||||
|
border-color #444
|
||||||
|
~label
|
||||||
|
color #444
|
||||||
|
~ label
|
||||||
|
color #999
|
||||||
|
|
||||||
&:invalid
|
&:invalid
|
||||||
border-color $danger
|
border-color $danger
|
||||||
|
|
||||||
@ -89,69 +185,20 @@
|
|||||||
~ svg
|
~ svg
|
||||||
color @border-color
|
color @border-color
|
||||||
|
|
||||||
&.primary
|
&.iconLeft
|
||||||
border-color $primary
|
|
||||||
|
|
||||||
~ label
|
|
||||||
color @border-color
|
|
||||||
|
|
||||||
~ svg
|
|
||||||
color @border-color
|
|
||||||
|
|
||||||
&.secondary
|
|
||||||
border-color $secondary
|
|
||||||
|
|
||||||
~ label
|
|
||||||
color @border-color
|
|
||||||
|
|
||||||
~ svg
|
|
||||||
color @border-color
|
|
||||||
|
|
||||||
&.info
|
|
||||||
border-color $info
|
|
||||||
|
|
||||||
~ label
|
|
||||||
color @border-color
|
|
||||||
|
|
||||||
~ svg
|
|
||||||
color @border-color
|
|
||||||
|
|
||||||
&.success
|
|
||||||
border-color $success
|
|
||||||
|
|
||||||
~ label
|
|
||||||
color @border-color
|
|
||||||
|
|
||||||
~ svg
|
|
||||||
color @border-color
|
|
||||||
|
|
||||||
&.danger
|
|
||||||
border-color $danger
|
|
||||||
|
|
||||||
~ label
|
|
||||||
color @border-color
|
|
||||||
|
|
||||||
~ svg
|
|
||||||
color @border-color
|
|
||||||
|
|
||||||
&.warning
|
|
||||||
border-color $warning
|
|
||||||
|
|
||||||
~ label
|
|
||||||
color @border-color
|
|
||||||
|
|
||||||
~ svg
|
|
||||||
color @border-color
|
|
||||||
|
|
||||||
&.hasIcon
|
|
||||||
padding-left 16px + 24px + 16px
|
padding-left 16px + 24px + 16px
|
||||||
|
&.iconRight
|
||||||
|
padding-right 16 + 24 + 16px
|
||||||
&.filled
|
&.filled
|
||||||
border none
|
border none
|
||||||
background rgba(gray, .1)
|
background rgba(gray, .1)
|
||||||
border-radius @border-radius @border-radius 0 0
|
border-radius @border-radius @border-radius 0 0
|
||||||
border-bottom 2px solid rgba(black,.4)
|
border-bottom 2px solid rgba(black,.4)
|
||||||
|
|
||||||
|
@media (prefers-color-scheme dark)
|
||||||
|
background rgba(white, .1)
|
||||||
|
border-bottom 2px solid rgba(white,.4)
|
||||||
|
|
||||||
&.opaque
|
&.opaque
|
||||||
background white
|
background white
|
||||||
|
|
||||||
@ -161,7 +208,8 @@
|
|||||||
&:hover
|
&:hover
|
||||||
background rgba(gray, .2)
|
background rgba(gray, .2)
|
||||||
|
|
||||||
|
@media (prefers-color-scheme dark)
|
||||||
|
background rgba(white, .2)
|
||||||
|
|
||||||
&.opaque
|
&.opaque
|
||||||
background darken(white, 5%)
|
background darken(white, 5%)
|
||||||
@ -169,6 +217,7 @@
|
|||||||
background #1c1c1c
|
background #1c1c1c
|
||||||
&:focus
|
&:focus
|
||||||
background rgba(gray, .3)
|
background rgba(gray, .3)
|
||||||
|
border-bottom 2px solid $default
|
||||||
|
|
||||||
&.opaque
|
&.opaque
|
||||||
background white
|
background white
|
||||||
@ -185,14 +234,20 @@
|
|||||||
background transparent
|
background transparent
|
||||||
padding 0
|
padding 0
|
||||||
font-size .75rem
|
font-size .75rem
|
||||||
~ svg ~ label
|
~ svg.left ~ label
|
||||||
left 16px + 24px + 16px // .input/padding-left label/padding-left
|
left 16px + 24px + 16px // .input/padding-left label/padding-left
|
||||||
|
~ svg.rotate
|
||||||
|
transform rotateX(0)
|
||||||
|
transition $transition
|
||||||
|
|
||||||
|
&:hover:focus ~ svg.rotate, ~.autocomplete:hover ~ svg.rotate
|
||||||
|
transform rotateX(180deg)
|
||||||
div
|
div
|
||||||
display flex
|
display flex
|
||||||
justify-content space-between
|
justify-content space-between
|
||||||
padding 0 16px
|
padding 0 16px
|
||||||
font-size .9em
|
font-size .9em
|
||||||
|
|
||||||
&.block input
|
&.block, &.block input, &.block select, &.block textarea
|
||||||
width 100%
|
width 100%
|
||||||
|
display block
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import React, { FC } from 'react'
|
import React, { FC } from 'react'
|
||||||
|
|
||||||
import { ChevronDown } from 'react-feather'
|
import { ChevronDown } from 'react-feather'
|
||||||
import { IconProps, ColorType } from '../interfaces'
|
import Text from '../Text'
|
||||||
|
import { IconProps } from '../interfaces'
|
||||||
import { buildClassName } from '../Util'
|
import { buildClassName } from '../Util'
|
||||||
import css from './Input.module.styl'
|
import css from './Input.module.styl'
|
||||||
|
|
||||||
@ -9,34 +10,57 @@ interface Props extends React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLIn
|
|||||||
id?: string
|
id?: string
|
||||||
label?: string
|
label?: string
|
||||||
icon?: FC<IconProps>
|
icon?: FC<IconProps>
|
||||||
|
iconRight?: FC<IconProps>
|
||||||
helper?: string
|
helper?: string
|
||||||
characterCount?: boolean
|
characterCount?: boolean
|
||||||
inputRef?: React.RefObject<HTMLInputElement>
|
inputRef?: React.RefObject<HTMLInputElement>
|
||||||
selectRef?: React.RefObject<HTMLSelectElement>
|
selectRef?: React.RefObject<HTMLSelectElement>
|
||||||
type?: 'color' | 'text' | 'date' | 'datetime-local' |
|
type?: 'color' | 'text' | 'date' | 'datetime-local' |
|
||||||
'email' | 'file' | 'month' | 'number' | 'password' |
|
'email' | 'file' | 'month' | 'number' | 'password' |
|
||||||
'range' | 'search' | 'tel' | 'time' | 'url' | 'week' | 'select'
|
'range' | 'search' | 'tel' | 'time' | 'url' | 'week' |
|
||||||
maxLength?: number | undefined
|
// Custom Types
|
||||||
|
'select' | 'textarea'
|
||||||
|
autocomplete?: Array<string>
|
||||||
infinityText?: string
|
infinityText?: string
|
||||||
filled?: boolean
|
filled?: boolean
|
||||||
opaque?: boolean
|
opaque?: boolean
|
||||||
block?: boolean
|
block?: boolean
|
||||||
color?: ColorType
|
|
||||||
children?: React.ReactNode
|
children?: React.ReactNode
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class Input extends React.Component<Props> {
|
interface States {
|
||||||
|
charCount?: string
|
||||||
|
textAreaHeight?: number
|
||||||
|
value?: string
|
||||||
|
isInFirstPartOfScreen?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
private charCountRef: React.RefObject<HTMLSpanElement> = React.createRef()
|
export default class Input extends React.Component<Props, States> {
|
||||||
|
|
||||||
|
// any because f*ck types
|
||||||
|
private inputRef: React.RefObject<any> = React.createRef()
|
||||||
|
private parentRef: React.RefObject<HTMLDivElement> = React.createRef()
|
||||||
|
|
||||||
public componentDidMount() {
|
public componentDidMount() {
|
||||||
this.updatecharCount()
|
if (this.props.characterCount) {
|
||||||
|
this.onChange()
|
||||||
|
}
|
||||||
|
if (this.props.type === 'textarea') {
|
||||||
|
this.textareaHandler()
|
||||||
|
}
|
||||||
|
if (this.props.autocomplete) {
|
||||||
|
window.addEventListener('scroll', this.parentScroll)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentWillUnmount() {
|
||||||
|
window.removeEventListener('scroll', this.parentScroll)
|
||||||
}
|
}
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const props: Props = Object.assign({}, this.props)
|
const props: Props = Object.assign({}, this.props)
|
||||||
const Icon = this.props.icon
|
|
||||||
delete props.label
|
delete props.label
|
||||||
|
delete props.children
|
||||||
delete props.icon
|
delete props.icon
|
||||||
delete props.opaque
|
delete props.opaque
|
||||||
delete props.helper
|
delete props.helper
|
||||||
@ -50,30 +74,30 @@ export default class Input extends React.Component<Props> {
|
|||||||
|
|
||||||
const baseProps: React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement> = {
|
const baseProps: React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement> = {
|
||||||
placeholder: this.props.placeholder || ' ',
|
placeholder: this.props.placeholder || ' ',
|
||||||
ref: this.props.inputRef,
|
ref: this.props.inputRef || this.inputRef,
|
||||||
className: buildClassName(
|
className: buildClassName(
|
||||||
[css.hasIcon, Icon],
|
[css.iconLeft, this.props.icon],
|
||||||
|
[css.iconRight, this.props.iconRight],
|
||||||
[css.filled, this.props.filled],
|
[css.filled, this.props.filled],
|
||||||
[css.opaque, this.props.opaque],
|
[css.opaque, this.props.opaque]
|
||||||
[css[this.props.color as string], this.props.color]
|
|
||||||
),
|
),
|
||||||
onInvalid: (ev: React.FormEvent<HTMLInputElement>) => ev.preventDefault()
|
onInvalid: (ev: React.FormEvent<HTMLInputElement>) => ev.preventDefault(),
|
||||||
}
|
}
|
||||||
|
|
||||||
let input: React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>
|
let input: React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>
|
||||||
|
|
||||||
if (this.props.type === 'number') {
|
if (this.props.type === 'number') {
|
||||||
baseProps.type = 'text'
|
baseProps.onWheel = (ev: React.WheelEvent<HTMLInputElement>) => ev.currentTarget.blur()
|
||||||
baseProps.inputMode = 'numeric'
|
|
||||||
baseProps.pattern = '[0-9]*'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.props.type === 'select') {
|
if (this.props.type === 'select') {
|
||||||
input = (
|
input = (
|
||||||
<select
|
<select
|
||||||
ref={this.props.selectRef}
|
ref={this.props.selectRef || this.inputRef}
|
||||||
|
{...this.props as React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLSelectElement>, HTMLSelectElement>}
|
||||||
className={buildClassName(
|
className={buildClassName(
|
||||||
css.hasIcon,
|
[css.iconLeft, this.props.icon],
|
||||||
|
[css.iconRight, !this.props.disabled || this.props.iconRight],
|
||||||
[css.filled, this.props.filled],
|
[css.filled, this.props.filled],
|
||||||
[css[this.props.color as string], this.props.color]
|
[css[this.props.color as string], this.props.color]
|
||||||
)}
|
)}
|
||||||
@ -81,6 +105,19 @@ export default class Input extends React.Component<Props> {
|
|||||||
{this.props.children}
|
{this.props.children}
|
||||||
</select>
|
</select>
|
||||||
)
|
)
|
||||||
|
} else if (this.props.type === 'textarea') {
|
||||||
|
delete baseProps.ref
|
||||||
|
input = (
|
||||||
|
<textarea
|
||||||
|
{...props as React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLTextAreaElement>, HTMLTextAreaElement>}
|
||||||
|
{...baseProps as any}
|
||||||
|
ref={this.inputRef}
|
||||||
|
style={{minHeight: this.state?.textAreaHeight}}
|
||||||
|
onKeyDown={this.textareaHandler}
|
||||||
|
onKeyUp={this.textareaHandler}
|
||||||
|
onFocus={this.textareaHandler}
|
||||||
|
/>
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
input = (
|
input = (
|
||||||
<input
|
<input
|
||||||
@ -96,42 +133,94 @@ export default class Input extends React.Component<Props> {
|
|||||||
[css.parent],
|
[css.parent],
|
||||||
[css.block, this.props.block]
|
[css.block, this.props.block]
|
||||||
)}
|
)}
|
||||||
onChangeCapture={this.props.characterCount ? this.updatecharCount : undefined}
|
onChangeCapture={this.onChange}
|
||||||
|
ref={this.parentRef}
|
||||||
>
|
>
|
||||||
{input}
|
{input}
|
||||||
{this.props.type === 'select' && (
|
|
||||||
<ChevronDown />
|
{this.props.autocomplete && this.props.autocomplete.indexOf(this.state?.value || '') === -1 && (
|
||||||
|
<ul className={buildClassName(css.autocomplete, [css.reverse, !this.state?.isInFirstPartOfScreen])}>
|
||||||
|
{this.props.autocomplete.filter((item) => item.includes(this.state?.value || '')).map((item) => (<li key={item} onClick={this.onAutoCompleteClick(item)}><Text>{item}</Text></li>))}
|
||||||
|
</ul>
|
||||||
)}
|
)}
|
||||||
{Icon && (
|
|
||||||
<Icon />
|
{/* Process Icon */}
|
||||||
|
{this.props.icon && (
|
||||||
|
<this.props.icon className={css.left} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{this.props.iconRight ? (
|
||||||
|
<this.props.iconRight className={css.right} />
|
||||||
|
) : ((this.props.type === 'select' || this.props.autocomplete) && !this.props.disabled) && (
|
||||||
|
<ChevronDown className={buildClassName(css.right, css.rotate)} />
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Input Label */}
|
||||||
{this.props.label && (
|
{this.props.label && (
|
||||||
<label className={css.label} htmlFor={this.props.id}>{this.props.label}</label>
|
<label className={css.label} htmlFor={this.props.id}>{this.props.label}</label>
|
||||||
)}
|
)}
|
||||||
{(this.props.helper || this.props.characterCount) && (
|
{(this.props.helper || this.props.characterCount) && (
|
||||||
<div>
|
<div>
|
||||||
<span>{this.props.helper}</span>
|
<Text type="span">{this.props.helper}</Text>
|
||||||
<span ref={this.charCountRef}></span>
|
{this.props.characterCount && (
|
||||||
|
<Text type="span">{this.state?.charCount}</Text>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private updatecharCount = async (event?: React.FormEvent<HTMLDivElement>) => {
|
private parentScroll = async () => {
|
||||||
if (this.props.characterCount && this.charCountRef.current) {
|
const div = this.parentRef.current
|
||||||
|
if (!div) {return}
|
||||||
|
const result = !(div.offsetTop - window.scrollY >= window.innerHeight / 2)
|
||||||
|
if (this.state.isInFirstPartOfScreen !== result) {
|
||||||
|
this.setState({isInFirstPartOfScreen: result})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private getElement(): undefined | HTMLInputElement {
|
||||||
|
const item = this.props.inputRef || this.props.selectRef || this.inputRef
|
||||||
|
if (!item || !item.current) {return}
|
||||||
|
return item.current
|
||||||
|
}
|
||||||
|
|
||||||
|
private textareaHandler = async () =>
|
||||||
|
this.setState({textAreaHeight: undefined}, () => {
|
||||||
|
if (!this.inputRef.current) {return}
|
||||||
|
this.setState({textAreaHeight: this.inputRef.current.scrollHeight})
|
||||||
|
})
|
||||||
|
|
||||||
|
private onAutoCompleteClick = (value: string) => () => {
|
||||||
|
console.log('test')
|
||||||
|
const item = this.getElement()
|
||||||
|
if (!item) {return}
|
||||||
|
const valueSetter = Object.getOwnPropertyDescriptor(item, 'value')?.set
|
||||||
|
const prototype = Object.getPrototypeOf(item)
|
||||||
|
const prototypeValueSetter = Object.getOwnPropertyDescriptor(prototype, 'value')?.set
|
||||||
|
if (valueSetter && valueSetter !== prototypeValueSetter) {
|
||||||
|
// @ts-expect-error IDK why
|
||||||
|
prototypeValueSetter.call(item, value)
|
||||||
|
} else {
|
||||||
|
// @ts-expect-error IDK why
|
||||||
|
valueSetter.call(item, value)
|
||||||
|
}
|
||||||
|
item.dispatchEvent(new Event('input', {bubbles: true}))
|
||||||
|
}
|
||||||
|
|
||||||
|
private onChange = async (event?: React.FormEvent<HTMLDivElement>) => {
|
||||||
|
if (this.props.characterCount) {
|
||||||
const max = this.props.maxLength || this.props.infinityText || 'Infinity'
|
const max = this.props.maxLength || this.props.infinityText || 'Infinity'
|
||||||
let currentCount = 0
|
const baseItem = this.props.value || this.props.defaultValue || ''
|
||||||
|
let currentCount = baseItem.toString().length
|
||||||
if (event) {
|
if (event) {
|
||||||
currentCount = (event.target as HTMLInputElement).value.length
|
currentCount = (event.target as HTMLInputElement).value.length
|
||||||
} else {
|
|
||||||
if (this.props.defaultValue) {
|
|
||||||
currentCount = this.props.defaultValue.toString().length
|
|
||||||
} else if (this.props.value) {
|
|
||||||
currentCount = this.props.value.toString().length
|
|
||||||
}
|
}
|
||||||
|
this.setState({charCount: `${currentCount}/${max}`})
|
||||||
}
|
}
|
||||||
this.charCountRef.current.innerText = currentCount + ' / ' + max
|
if (event) {
|
||||||
|
this.setState({value: (event.target as HTMLInputElement).value })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,131 +1,48 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { Bell, Grid } from 'react-feather'
|
|
||||||
import Router from 'next/router'
|
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import Row from '../Row'
|
import Row from '../Row'
|
||||||
import Col from '../Col'
|
import Col from '../Col'
|
||||||
import Button from '../Button'
|
|
||||||
import Image from '../Image'
|
import Image from '../Image'
|
||||||
|
|
||||||
import { buildClassName } from '../Util'
|
|
||||||
import Menu from '../Menu'
|
|
||||||
import css from './Navbar.module.styl'
|
import css from './Navbar.module.styl'
|
||||||
|
|
||||||
interface States {
|
|
||||||
domain?: string
|
|
||||||
showUserMenu?: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
username?: string
|
|
||||||
userPic?: string
|
|
||||||
userMenu?: Array<{href: string, name: string}>
|
|
||||||
loginUrl?: string
|
|
||||||
registerUrl?: string
|
|
||||||
logoUrl?: string
|
|
||||||
logoLabel?: string
|
|
||||||
logo?: {
|
logo?: {
|
||||||
|
link?: string
|
||||||
|
label?: string
|
||||||
src: string
|
src: string
|
||||||
alt: string
|
alt?: string
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
export default class Navbar extends React.Component<Props> {
|
||||||
|
|
||||||
export default class Navbar extends React.Component<Props, States> {
|
|
||||||
|
|
||||||
public state: States = {}
|
|
||||||
|
|
||||||
public constructor(props: Props) {
|
|
||||||
super(props)
|
|
||||||
}
|
|
||||||
|
|
||||||
public componentDidMount() {
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
domain: Router.query.domain as string
|
|
||||||
})
|
|
||||||
Router.events.on('routeChangeComplete', () => {
|
|
||||||
this.setState({
|
|
||||||
domain: Router.query.domain as string | undefined
|
|
||||||
})
|
|
||||||
})
|
|
||||||
document.body.addEventListener('click', this.onClickAnywhere)
|
|
||||||
}
|
|
||||||
|
|
||||||
public componentWillUnmount() {
|
|
||||||
document.body.removeEventListener('click', this.onClickAnywhere)
|
|
||||||
}
|
|
||||||
|
|
||||||
public render = () => (
|
public render = () => (
|
||||||
<nav className={buildClassName([css.navbar], [css.small, this.state.domain])}>
|
<nav className={css.navbar}>
|
||||||
<Row >
|
<Row nomargin>
|
||||||
{this.props.logo && (
|
{this.props.logo && (
|
||||||
<Col>
|
<Col>
|
||||||
<Row align="center">
|
<Row align="center">
|
||||||
<Link href={this.props.logoUrl || '/'}>
|
<Link href={this.props.logo.link || '/'}>
|
||||||
<a aria-label={this.props.logoLabel || 'Homepage'}>
|
<a aria-label={this.props.logo.label || 'Homepage'}>
|
||||||
<Image
|
<Image
|
||||||
alt={this.props.logo.alt}
|
alt={this.props.logo.alt}
|
||||||
src={this.props.logo.src}
|
src={this.props.logo.src}
|
||||||
max={{ height: 70-32 }}
|
height={38}
|
||||||
|
width={120}
|
||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
</Row>
|
</Row>
|
||||||
</Col>
|
</Col>
|
||||||
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Col>
|
<Col>
|
||||||
<Row justify="flex-end" align="center">
|
<Row justify="flex-end" align="center">
|
||||||
{this.props.username ? (
|
{this.props.children}
|
||||||
<>
|
|
||||||
{/* <Bell className={css.icon} />
|
|
||||||
<Grid className={css.icon} /> */}
|
|
||||||
{this.props.userPic && (
|
|
||||||
<Image
|
|
||||||
onClick={this.onMenuClick}
|
|
||||||
alt="User Profile Picture"
|
|
||||||
classes={buildClassName([css.favicon], [css.userIcon])}
|
|
||||||
src={this.props.userPic}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<p
|
|
||||||
onClick={this.props.userMenu ? this.onMenuClick : undefined}
|
|
||||||
className={css.text}
|
|
||||||
>
|
|
||||||
{this.props.username}
|
|
||||||
</p>
|
|
||||||
{this.props.userMenu && (
|
|
||||||
<Menu
|
|
||||||
show={this.state.showUserMenu}
|
|
||||||
pos={{ right: 16, top: 86 }}
|
|
||||||
content={this.props.userMenu}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<Button nomargintop href={this.props.loginUrl}>Login</Button>
|
|
||||||
<Button nomargintop color="secondary" href={this.props.registerUrl}>Register</Button>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Row>
|
</Row>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</nav>
|
</nav>
|
||||||
)
|
)
|
||||||
|
|
||||||
private onClickAnywhere = () => {
|
|
||||||
this.setState({
|
|
||||||
showUserMenu: false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private onMenuClick = () => {
|
|
||||||
const newMenuState = !this.state.showUserMenu
|
|
||||||
this.setState({
|
|
||||||
showUserMenu: newMenuState
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
@import "../config.styl"
|
||||||
|
|
||||||
.popup
|
.popup
|
||||||
position fixed
|
position fixed
|
||||||
height 100%
|
height 100%
|
||||||
@ -6,9 +8,21 @@
|
|||||||
left 0
|
left 0
|
||||||
background rgba(black, .3)
|
background rgba(black, .3)
|
||||||
cursor pointer
|
cursor pointer
|
||||||
|
z-index 1000
|
||||||
|
|
||||||
.popupChild
|
.popupChild
|
||||||
cursor initial
|
cursor initial
|
||||||
|
z-index 1001
|
||||||
|
|
||||||
|
min-width 50%
|
||||||
|
|
||||||
|
@media (max-width $tablet)
|
||||||
|
min-width 70%
|
||||||
|
|
||||||
|
@media (max-width $mobile)
|
||||||
|
min-width 90%
|
||||||
|
// min-height 50vh
|
||||||
|
|
||||||
.exit
|
.exit
|
||||||
cursor pointer
|
cursor pointer
|
||||||
|
height 100%
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { X } from 'react-feather'
|
import { X } from 'react-feather'
|
||||||
import { BoxWrapper, BoxHeader, BoxBody } from '../Box'
|
import Text from '../Text'
|
||||||
import { Props as HeaderProps } from '../Box/BoxHeader'
|
import Box from '../Box'
|
||||||
import Row from '../Row'
|
import Row from '../Row'
|
||||||
|
import { Props as HeaderProps } from '../Box/BoxHeader'
|
||||||
|
|
||||||
import css from './Popup.module.styl'
|
import css from './Popup.module.styl'
|
||||||
|
|
||||||
@ -15,20 +16,16 @@ interface Props {
|
|||||||
export default class Popup extends React.Component<Props> {
|
export default class Popup extends React.Component<Props> {
|
||||||
|
|
||||||
public render = () => (
|
public render = () => (
|
||||||
<Row onClick={this.parentClose} justify="center" align="center" className={css.popup}>
|
<Row nomargin onClick={this.parentClose} justify="center" align="center" className={css.popup}>
|
||||||
<BoxWrapper className={css.popupChild}>
|
<Box {...this.props.header} className={css.popupChild} onClick={(ev) => ev.stopPropagation()} headerButtons={(<Text><X onClick={this.props.onClose} className={css.exit} /></Text>)}>
|
||||||
<BoxHeader {...this.props.header}>
|
|
||||||
<X onClick={this.props.onClose} className={css.exit} />
|
|
||||||
</BoxHeader>
|
|
||||||
<BoxBody>
|
|
||||||
{this.props.children}
|
{this.props.children}
|
||||||
</BoxBody>
|
</Box>
|
||||||
</BoxWrapper>
|
|
||||||
</Row>
|
</Row>
|
||||||
)
|
)
|
||||||
|
|
||||||
private parentClose = (ev: React.MouseEvent<HTMLDivElement>) => {
|
private parentClose = (ev: React.MouseEvent<HTMLDivElement>) => {
|
||||||
if ((ev.target as HTMLElement).classList.contains(css.popup) && this.props.onClose) {
|
const target = ev.currentTarget
|
||||||
|
if (target.classList.contains(css.popup) && this.props.onClose) {
|
||||||
this.props.onClose()
|
this.props.onClose()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ export default class SidebarContainer extends React.Component<Props> {
|
|||||||
<nav className={css.sidebar}>
|
<nav className={css.sidebar}>
|
||||||
<Link href="/dashboard">
|
<Link href="/dashboard">
|
||||||
<a>
|
<a>
|
||||||
<Image src="/assets/logo.svg" max={{ width: '100%' }} />
|
<Image src="/assets/logo.svg" width={175} height={100} />
|
||||||
</a>
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
{this.menu.map((item, index) => (
|
{this.menu.map((item, index) => (
|
||||||
|
139
src/dzeio/Tag/Tag.module.styl
Normal file
139
src/dzeio/Tag/Tag.module.styl
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
@import '../config'
|
||||||
|
|
||||||
|
.tag
|
||||||
|
padding 8px 12px
|
||||||
|
border-radius 8px
|
||||||
|
margin-left 8px
|
||||||
|
height 32px
|
||||||
|
line-height 1
|
||||||
|
display inline-block
|
||||||
|
color white
|
||||||
|
|
||||||
|
background $default
|
||||||
|
outline none
|
||||||
|
&:hover
|
||||||
|
background darken($default, 10%)
|
||||||
|
&:focus
|
||||||
|
background darken($default, 20%)
|
||||||
|
|
||||||
|
&.outline
|
||||||
|
border 2px solid $default
|
||||||
|
padding 6px 10px
|
||||||
|
background transparent
|
||||||
|
&:hover
|
||||||
|
background rgba($default, .5)
|
||||||
|
&:focus
|
||||||
|
background rgba($default, .7)
|
||||||
|
|
||||||
|
.primary
|
||||||
|
$color = $primary
|
||||||
|
color white
|
||||||
|
background $color
|
||||||
|
&:hover
|
||||||
|
background lighten($color, 30%)
|
||||||
|
&:focus
|
||||||
|
background lighten($color, 15%)
|
||||||
|
|
||||||
|
&.outline
|
||||||
|
color black
|
||||||
|
border 2px solid $color
|
||||||
|
background transparent
|
||||||
|
&:hover
|
||||||
|
color white
|
||||||
|
background rgba($color, .5)
|
||||||
|
&:focus
|
||||||
|
color white
|
||||||
|
background rgba($color, .7)
|
||||||
|
.secondary
|
||||||
|
$color = $secondary
|
||||||
|
background $color
|
||||||
|
&:hover
|
||||||
|
background lighten($color, 30%)
|
||||||
|
&:focus
|
||||||
|
background lighten($color, 15%)
|
||||||
|
&.outline
|
||||||
|
color white
|
||||||
|
border 2px solid $color
|
||||||
|
background transparent
|
||||||
|
&:hover
|
||||||
|
color black
|
||||||
|
background rgba($color, .5)
|
||||||
|
&:focus
|
||||||
|
color black
|
||||||
|
background rgba($color, .7)
|
||||||
|
|
||||||
|
.info
|
||||||
|
$color = $info
|
||||||
|
color white
|
||||||
|
background $color
|
||||||
|
&:hover
|
||||||
|
background lighten($color, 30%)
|
||||||
|
&:focus
|
||||||
|
background lighten($color, 15%)
|
||||||
|
&.outline
|
||||||
|
color black
|
||||||
|
border 2px solid $color
|
||||||
|
background transparent
|
||||||
|
&:hover
|
||||||
|
color white
|
||||||
|
background rgba($color, .5)
|
||||||
|
&:focus
|
||||||
|
color white
|
||||||
|
background rgba($color, .7)
|
||||||
|
|
||||||
|
.success
|
||||||
|
$color = $success
|
||||||
|
color white
|
||||||
|
background $color
|
||||||
|
&:hover
|
||||||
|
background lighten($color, 30%)
|
||||||
|
&:focus
|
||||||
|
background lighten($color, 15%)
|
||||||
|
&.outline
|
||||||
|
color black
|
||||||
|
border 2px solid $color
|
||||||
|
background transparent
|
||||||
|
&:hover
|
||||||
|
color white
|
||||||
|
background rgba($color, .5)
|
||||||
|
&:focus
|
||||||
|
color white
|
||||||
|
background rgba($color, .7)
|
||||||
|
|
||||||
|
.danger
|
||||||
|
$color = $danger
|
||||||
|
color white
|
||||||
|
background $color
|
||||||
|
&:hover
|
||||||
|
background lighten($color, 30%)
|
||||||
|
&:focus
|
||||||
|
background lighten($color, 15%)
|
||||||
|
&.outline
|
||||||
|
color black
|
||||||
|
border 2px solid $color
|
||||||
|
background transparent
|
||||||
|
&:hover
|
||||||
|
color white
|
||||||
|
background rgba($color, .5)
|
||||||
|
&:focus
|
||||||
|
color white
|
||||||
|
background rgba($color, .7)
|
||||||
|
|
||||||
|
.warning
|
||||||
|
$color = $warning
|
||||||
|
color white
|
||||||
|
background $color
|
||||||
|
&:hover
|
||||||
|
background lighten($color, 30%)
|
||||||
|
&:focus
|
||||||
|
background lighten($color, 15%)
|
||||||
|
&.outline
|
||||||
|
color black
|
||||||
|
border 2px solid $color
|
||||||
|
background transparent
|
||||||
|
&:hover
|
||||||
|
color white
|
||||||
|
background rgba($color, .5)
|
||||||
|
&:focus
|
||||||
|
color white
|
||||||
|
background rgba($color, .7)
|
17
src/dzeio/Tag/Tag.stories.tsx
Normal file
17
src/dzeio/Tag/Tag.stories.tsx
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { Meta } from '@storybook/react/types-6-0'
|
||||||
|
import React from 'react'
|
||||||
|
import Component from '.'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'DZEIO/Tag',
|
||||||
|
component: Component
|
||||||
|
} as Meta
|
||||||
|
|
||||||
|
export const Basic = (args: any) => {
|
||||||
|
const content = args.content
|
||||||
|
delete args.content
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Component {...args}>{content}</Component>
|
||||||
|
)
|
||||||
|
}
|
42
src/dzeio/Tag/index.tsx
Normal file
42
src/dzeio/Tag/index.tsx
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import { ColorType } from '../interfaces'
|
||||||
|
import { buildClassName } from '../Util'
|
||||||
|
import Link from '../Link'
|
||||||
|
|
||||||
|
import css from './Tag.module.styl'
|
||||||
|
import Text from '../Text'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
text: string
|
||||||
|
color?: ColorType
|
||||||
|
href?: string
|
||||||
|
outline?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class Tag extends React.Component<Props> {
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
const classes = buildClassName(
|
||||||
|
css.tag,
|
||||||
|
[css[this.props.color as string], this.props.color],
|
||||||
|
[css.outline, this.props.outline]
|
||||||
|
)
|
||||||
|
if (!this.props.href) {
|
||||||
|
return (
|
||||||
|
<Text
|
||||||
|
className={classes}
|
||||||
|
>{this.props.text}</Text>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
href={this.props.href}
|
||||||
|
className={classes}
|
||||||
|
>
|
||||||
|
{this.props.text}
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -13,6 +13,8 @@
|
|||||||
.align-right
|
.align-right
|
||||||
text-align right
|
text-align right
|
||||||
|
|
||||||
|
.align-left
|
||||||
|
text-align left
|
||||||
|
|
||||||
@media (prefers-color-scheme dark)
|
@media (prefers-color-scheme dark)
|
||||||
.white:not(.noDarkTheme)
|
.white:not(.noDarkTheme)
|
||||||
|
@ -3,10 +3,10 @@ import { buildClassName } from '../Util'
|
|||||||
import css from './Text.module.styl'
|
import css from './Text.module.styl'
|
||||||
interface Props {
|
interface Props {
|
||||||
color?: 'black' | 'white'
|
color?: 'black' | 'white'
|
||||||
type?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p' | 'em'
|
type?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p' | 'em' | 'span'
|
||||||
className?: string
|
className?: string
|
||||||
noDarkTheme?: boolean
|
noDarkTheme?: boolean
|
||||||
align?: 'right' | 'center'
|
align?: 'left' | 'right' | 'center'
|
||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,6 +31,7 @@ export default class Text extends React.Component<Props> {
|
|||||||
case 'h5': return (<h5 className={classes}>{this.props.children}</h5>)
|
case 'h5': return (<h5 className={classes}>{this.props.children}</h5>)
|
||||||
case 'h6': return (<h6 className={classes}>{this.props.children}</h6>)
|
case 'h6': return (<h6 className={classes}>{this.props.children}</h6>)
|
||||||
case 'em': return (<p className={classes}><em>{this.props.children}</em></p>)
|
case 'em': return (<p className={classes}><em>{this.props.children}</em></p>)
|
||||||
|
case 'span': return (<span className={classes}>{this.props.children}</span>)
|
||||||
default: return (<p className={classes}>{this.props.children}</p>)
|
default: return (<p className={classes}>{this.props.children}</p>)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,32 @@
|
|||||||
$default = #4285f4 // This color should never appear
|
|
||||||
$primary = #4285f4
|
$main = #4285F4
|
||||||
$secondary = #FCFCFC
|
$textOnMain = white
|
||||||
$info = #03A9F4
|
|
||||||
$success = #2DCE89
|
$infoDark = #304FFE
|
||||||
$danger = #F5365C
|
$infoLight = #29B6F6
|
||||||
$warning = #FB6340
|
|
||||||
|
$successDark = #388E3C
|
||||||
|
$successLight = #4CAF50
|
||||||
|
|
||||||
|
$errorDark = #D32F2F
|
||||||
|
$errorLight = #F44336
|
||||||
|
|
||||||
|
$warningDark = #F57C00
|
||||||
|
$warningLight = #FF9800
|
||||||
|
|
||||||
|
$backgroundDark = #161616
|
||||||
|
$backgroundLight = #EEEEEE
|
||||||
|
$foregroundDark = #202020
|
||||||
|
$foregroundLight = #FFFFFF
|
||||||
|
|
||||||
|
$grayDark = #B0B0B0
|
||||||
|
$grayLight = #E0E0E0
|
||||||
|
|
||||||
|
|
||||||
$transitionTime = .15s
|
$transitionTime = .15s
|
||||||
$transitionFunction = ease-in-out
|
$transitionFunction = ease-in-out
|
||||||
$transition = $transitionTime $transitionFunction
|
$transition = $transitionTime $transitionFunction
|
||||||
|
|
||||||
$darkBackground = #161616
|
|
||||||
|
|
||||||
// Breakpoints
|
// Breakpoints
|
||||||
$mobile = 768px
|
$mobile = 768px
|
||||||
@ -22,7 +38,17 @@ $colCount = 12
|
|||||||
$colCountTablet = 8
|
$colCountTablet = 8
|
||||||
$colCountMobile = 4
|
$colCountMobile = 4
|
||||||
|
|
||||||
$gapSize = 16px //$totalGapSize / ($colCount+1)
|
$gapSize = 16px
|
||||||
|
|
||||||
rem($a)
|
rem($a)
|
||||||
($a / 16)rem
|
($a / 16)rem
|
||||||
|
|
||||||
|
// @deprecated colors
|
||||||
|
$primary = $main
|
||||||
|
$default = $main
|
||||||
|
$secondary = $main
|
||||||
|
$info = $infoLight
|
||||||
|
$success = $successLight
|
||||||
|
$danger = $errorLight
|
||||||
|
$warning = $warningLight
|
||||||
|
$darkBackground = $backgroundDark
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
@import "_aileron"
|
@import "_aileron"
|
||||||
|
@import "config"
|
||||||
|
|
||||||
*
|
*
|
||||||
*::before
|
*::before
|
||||||
*::after
|
*::after
|
||||||
box-sizing border-box
|
box-sizing border-box
|
||||||
|
|
||||||
html
|
|
||||||
font-family Aileron, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif
|
font-family Aileron, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif
|
||||||
|
|
||||||
html
|
html
|
||||||
@ -13,11 +12,36 @@ body
|
|||||||
body > div[id="__next"]
|
body > div[id="__next"]
|
||||||
margin 0
|
margin 0
|
||||||
min-height 100vh
|
min-height 100vh
|
||||||
background #f5f5f5
|
background $backgroundLight
|
||||||
|
|
||||||
@media (prefers-color-scheme dark)
|
@media (prefers-color-scheme dark)
|
||||||
background #202020
|
background $backgroundDark
|
||||||
|
|
||||||
a
|
a
|
||||||
color inherit
|
color inherit
|
||||||
text-decoration none
|
text-decoration none
|
||||||
|
|
||||||
|
/* width */
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Track */
|
||||||
|
::-webkit-scrollbar-track
|
||||||
|
background $foregroundLight
|
||||||
|
@media (prefers-color-scheme dark)
|
||||||
|
background $foregroundDark
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb
|
||||||
|
background: darken($foregroundLight, 16%)
|
||||||
|
@media (prefers-color-scheme dark)
|
||||||
|
background lighten($foregroundDark, 16%)
|
||||||
|
&:hover
|
||||||
|
background: darken($foregroundLight, 24%)
|
||||||
|
@media (prefers-color-scheme dark)
|
||||||
|
background: lighten($foregroundDark, 24%)
|
||||||
|
|
||||||
|
&:active
|
||||||
|
background: darken($foregroundLight, 32%)
|
||||||
|
@media (prefers-color-scheme dark)
|
||||||
|
background: lighten($foregroundDark, 32%)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { SVGAttributes } from 'react'
|
import { SVGAttributes } from 'react'
|
||||||
|
|
||||||
export type ColorType = 'primary' | 'secondary' | 'info' | 'success' | 'danger' | 'warning'
|
export type ColorType = 'primary' | 'info' | 'success' | 'error' | 'warning'
|
||||||
|
|
||||||
export interface IconProps extends SVGAttributes<SVGElement> {
|
export interface IconProps extends SVGAttributes<SVGElement> {
|
||||||
color?: string
|
color?: string
|
||||||
|
@ -1,11 +1,15 @@
|
|||||||
.fullscrean
|
@import './config'
|
||||||
|
|
||||||
|
.fullscreen
|
||||||
min-height 100vh
|
min-height 100vh
|
||||||
|
|
||||||
.fullHeight
|
.w100p
|
||||||
height 50vh
|
width 100%
|
||||||
|
|
||||||
|
.hideMobile
|
||||||
|
@media (max-width $mobile)
|
||||||
|
display none
|
||||||
|
|
||||||
|
.hideTablet
|
||||||
// Utils for Width
|
@media (max-width $tablet)
|
||||||
.w50p
|
display none
|
||||||
width 50%
|
|
||||||
|
@ -18,6 +18,7 @@ import Popup from './dzeio/Popup'
|
|||||||
import Row from './dzeio/Row'
|
import Row from './dzeio/Row'
|
||||||
import SidebarContainer from './dzeio/SidebarContainer'
|
import SidebarContainer from './dzeio/SidebarContainer'
|
||||||
import Table from './dzeio/Table'
|
import Table from './dzeio/Table'
|
||||||
|
import Tag from './dzeio/Tag'
|
||||||
import Text from './dzeio/Text'
|
import Text from './dzeio/Text'
|
||||||
import * as Util from './dzeio/Util'
|
import * as Util from './dzeio/Util'
|
||||||
|
|
||||||
@ -47,6 +48,7 @@ export {
|
|||||||
Row,
|
Row,
|
||||||
SidebarContainer,
|
SidebarContainer,
|
||||||
Table,
|
Table,
|
||||||
|
Tag,
|
||||||
Text,
|
Text,
|
||||||
Util
|
Util
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user