Compare commits

..

19 Commits

Author SHA1 Message Date
6c2b3466ba v0.5.2 2021-03-29 14:55:50 +02:00
31c9e30d3d Fixed Link colors
Signed-off-by: Avior <florian.bouillon@delta-wings.net>
2021-03-29 14:55:29 +02:00
0b60fa84ba v0.5.1 2021-03-29 14:07:38 +02:00
b586a32d00 Added back stylus utils
Signed-off-by: Avior <florian.bouillon@delta-wings.net>
2021-03-29 14:07:07 +02:00
ba3dcb22a1 v0.5.0 2021-03-29 14:01:46 +02:00
60f0ec23fc Updated Navbar Component
Signed-off-by: Florian BOUILLON <florian.bouillon@delta-wings.net>
2021-03-29 14:01:25 +02:00
803915999b v0.4.2 - Changed to be used only inconjonction with stylus
Signed-off-by: Avior <florian.bouillon@delta-wings.net>
2021-03-29 13:48:50 +02:00
af99c13922 v0.4.2 2021-03-29 13:43:03 +02:00
3d2fb93d72 v0.4.1 2021-03-29 13:32:26 +02:00
552f5b3dc6 v0.4.0 2021-03-29 12:48:16 +02:00
07dd430ffa Removed Tag element + Global Update
Signed-off-by: Florian BOUILLON <florian.bouillon@delta-wings.net>
2021-03-18 14:57:35 +01:00
6ff39ff3a7 Cleaned ignore files
Signed-off-by: Florian BOUILLON <florian.bouillon@delta-wings.net>
2021-03-15 17:28:58 +01:00
594e77a509 v0.3.1 2021-03-15 17:26:41 +01:00
8b12c56fe7 Moved types to the module folder
Signed-off-by: Florian BOUILLON <florian.bouillon@delta-wings.net>
2021-03-15 17:23:04 +01:00
3a908ccce0 Fixed Loader not found
Signed-off-by: Florian BOUILLON <florian.bouillon@delta-wings.net>
2021-03-15 17:22:40 +01:00
b34d25e5fd v0.3.0 2021-03-15 14:55:14 +01:00
67a4825cda Added a Loader module
Signed-off-by: Florian BOUILLON <florian.bouillon@delta-wings.net>
2021-03-15 14:54:27 +01:00
d898dc2f5b Fixed problems with Input and Fieldset
Signed-off-by: Florian BOUILLON <florian.bouillon@delta-wings.net>
2021-03-05 10:07:02 +01:00
8d99f1f3e6 v0.2.1 2021-03-05 10:06:11 +01:00
40 changed files with 1628 additions and 2666 deletions

11
.gitignore vendored
View File

@ -1,7 +1,10 @@
module/ module/
storybook-static/ node_modules/
*.mjs
*.js *.js
!src/dzeio/stylusUtils.js
*.d.ts
!src/stylus.d.ts
!.storybook/*.js
style.css style.css
yarn-error.log yarn-error.log
node_modules
types

View File

@ -1,11 +1,10 @@
.storybook/ .storybook/
src/ node_modules/
storybook-static/ *.stories.js
.gitattributes
.gitignore .gitignore
.npmignore .npmignore
rollup.config.js rollup.config.js
tsconfig.json tsconfig.json
yarn.lock yarn.lock
yarn-error.log
tsconfig.json
yarn-error.log yarn-error.log

View File

@ -2,10 +2,9 @@ const path = require("path");
module.exports = { module.exports = {
"stories": [ "stories": [
"../src/dzeio/**/*.stories.@(js|jsx|ts|tsx)", "../src/dzeio/**/*.stories.@(ts|tsx)",
], ],
"addons": [ "addons": [
"@storybook/addon-links",
"@storybook/addon-essentials" "@storybook/addon-essentials"
], ],
typescript: { typescript: {
@ -13,8 +12,8 @@ module.exports = {
checkOptions: {}, checkOptions: {},
reactDocgen: 'react-docgen-typescript', reactDocgen: 'react-docgen-typescript',
reactDocgenTypescriptOptions: { reactDocgenTypescriptOptions: {
shouldExtractLiteralValuesFromEnum: true, shouldExtractLiteralValuesFromEnum: true,
propFilter: (prop) => (prop.parent ? !/node_modules/.test(prop.parent.fileName) : true), propFilter: (prop) => (prop.parent ? !/node_modules/.test(prop.parent.fileName) : true),
}, },
}, },
presets: [path.resolve(__dirname, "./next.js")] presets: [path.resolve(__dirname, "./next.js")]

View File

@ -18,12 +18,13 @@ module.exports = {
}); });
newConfig.resolve.extensions.push('.ts', '.tsx'); newConfig.resolve.extensions.push('.ts', '.tsx');
// SCSS // Stylus
newConfig.module.rules.push({ newConfig.module.rules.push({
test: /\.styl$/, test: /\.styl$/,
use: ['style-loader', { use: ['style-loader', {
loader: 'css-loader', loader: 'css-loader',
options: { options: {
url: false,
importLoaders: 1, importLoaders: 1,
modules: true modules: true
}, },

View File

@ -1,4 +1,5 @@
import '../src/dzeio/general.styl'
export const parameters = { export const parameters = {
actions: { argTypesRegex: "^on[A-Z].*" }, layout: 'centered'
} }

View File

@ -1,20 +1,14 @@
{ {
"name": "@dzeio/components", "name": "@dzeio/components",
"version": "0.2.0", "version": "0.5.2",
"license": "MIT", "license": "MIT",
"main": "./index.js", "main": "./src/index.mjs",
"module": "./module/index.js", "types": "./src/index.d.ts",
"types": "./types/index.d.ts",
"dependencies": {},
"devDependencies": { "devDependencies": {
"@babel/core": "^7.12.16", "@babel/core": "^7.12.16",
"@babel/preset-env": "^7.12.16", "@babel/preset-env": "^7.12.16",
"@babel/preset-react": "^7.12.13", "@babel/preset-react": "^7.12.13",
"@rollup/plugin-typescript": "^8.2.0",
"@storybook/addon-actions": "^6.1.14",
"@storybook/addon-essentials": "^6.1.14", "@storybook/addon-essentials": "^6.1.14",
"@storybook/addon-knobs": "^6.1.14",
"@storybook/addon-links": "^6.1.14",
"@storybook/cli": "^6.1.14", "@storybook/cli": "^6.1.14",
"@storybook/react": "^6.1.14", "@storybook/react": "^6.1.14",
"@types/node": "^14.14.28", "@types/node": "^14.14.28",
@ -26,8 +20,6 @@
"react": "^17.0.1", "react": "^17.0.1",
"react-dom": "^17.0.1", "react-dom": "^17.0.1",
"react-feather": "^2.0.9", "react-feather": "^2.0.9",
"rollup": "^2.39.0",
"rollup-plugin-styles": "^3.14.1",
"style-loader": "^2.0.0", "style-loader": "^2.0.0",
"stylus": "^0.54.8", "stylus": "^0.54.8",
"stylus-loader": "^4.3.3", "stylus-loader": "^4.3.3",
@ -42,9 +34,8 @@
"react-feather": "^2.0.9" "react-feather": "^2.0.9"
}, },
"scripts": { "scripts": {
"storybook": "start-storybook -p 6006", "storybook": "rm -rf src/dzeio/**/*.js && start-storybook -p 6006",
"build-storybook": "build-storybook", "build": "tsc && mv src/index.js src/index.mjs",
"build": "tsc && rollup --config",
"prepublishOnly": "yarn build" "prepublishOnly": "yarn build"
} }
} }

View File

@ -1,32 +0,0 @@
import typescript from '@rollup/plugin-typescript';
import styles from 'rollup-plugin-styles'
import pkg from './package.json';
export default [
{
input: 'src/index.ts',
external: ['ms'],
plugins: [
styles({
modules: true,
url: false,
autoModules: true,
mode: 'extract',
}),
typescript(), // so Rollup can convert TypeScript to JavaScript
],
output: [
{
dir: './',
format: 'cjs',
assetFileNames: 'style.css'
},
{
file: pkg.module,
format: 'es',
assetFileNames: 'style.css'
}
]
}
];

View File

@ -3,19 +3,11 @@
.header .header
padding 16px padding 16px
.delimiter
border-bottom 2px solid grey
padding-bottom 2px
.img
border-top-left-radius 4px
border-top-right-radius 4px
.title .title
font-weight bold font-weight bold
font-size rem(20) font-size rem(18)
margin 0 0 8px margin 0 0 8px
.subtitle .subtitle
font-size rem(14) font-size rem(16)
margin 0 margin 0

View File

@ -11,21 +11,15 @@ export interface Props {
title?: string title?: string
titleColSize?: number titleColSize?: number
subtitle?: string subtitle?: string
delimiter?: boolean
titleClassName?: string titleClassName?: string
// image?: ImageProps
} }
export default class BoxHeader extends React.Component<Props> { export default class BoxHeader extends React.Component<Props> {
public render = () => ( public render = () => (
<> <>
{/* {this.props.image && (
<Image {...this.props.image} />
)} */}
<div className={buildClassName( <div className={buildClassName(
[css.header], css.header
[css.delimiter, this.props.delimiter]
)}> )}>
<Row> <Row>
<Col size={this.props.titleColSize as 1 || 8}> <Col size={this.props.titleColSize as 1 || 8}>

View File

@ -1,21 +1,12 @@
@import "../../config" @import "../../config"
.box .box
background white background $foregroundLight
@media (prefers-color-scheme dark)
background $foregroundDark
border-radius 8px border-radius 8px
box-shadow 0px 2px 4px 0px rgba(black, .33)
transition all $transition
.outline .outline
border 2px solid #E0E0E0 border 2px solid $grayDark
box-shadow none @media (prefers-color-scheme dark)
transition border-color $transition border-color $grayLight
&:hover
border-color darken(@border[2], 20%)
@media (prefers-color-scheme dark)
.box
background #202020
.outline
border-color #1F1F1F

View File

@ -32,7 +32,6 @@ export default class Box extends React.Component<Props> {
title={this.props.title} title={this.props.title}
titleColSize={this.props.titleColSize} titleColSize={this.props.titleColSize}
subtitle={this.props.subtitle} subtitle={this.props.subtitle}
delimiter={this.props.delimiter}
titleClassName={this.props.titleClassName} titleClassName={this.props.titleClassName}
> >
{this.props.headerButtons} {this.props.headerButtons}

View File

@ -3,3 +3,11 @@
background #E8EAF6 background #E8EAF6
padding 4px 8px padding 4px 8px
border-radius 8px border-radius 8px
.pre
border-radius 8px
padding 4px 8px
background #E8EAF6
display block
.code
padding 0

View File

@ -19,7 +19,7 @@ export default class Code extends React.Component<Props> {
} }
return ( return (
<pre> <pre className={css.pre}>
{code} {code}
</pre> </pre>
) )

View File

@ -3,7 +3,7 @@ import Row from '../Row'
import css from './DebugCols.module.styl' import css from './DebugCols.module.styl'
import Col from '.' import Col from '../Col'
enum Breakpoint { enum Breakpoint {
MOBILE, MOBILE,

View File

@ -2,7 +2,7 @@
.fieldset .fieldset
border-radius 4px border-radius 4px
border 2px solid grey border 2px solid $grayDark
transition all $transition transition all $transition
margin 0 margin 0

View File

@ -0,0 +1,16 @@
import { Meta } from '@storybook/react/types-6-0'
import React from 'react'
import Component from '.'
import Input from '../Input'
export default {
title: 'DZEIO/Fieldset',
component: Component,
argTypes: {
title: { control: 'text'}
}
} as Meta
export const Basic = (args: any) => (
<Component {...args}><Input label="Test" /></Component>
)

View File

@ -2,22 +2,27 @@
.footer .footer
padding 24px 16px padding 24px 16px
background $foregroundLight
@media (prefers-color-scheme dark)
background $foregroundDark
ul
list-type none
li
display inline-block
.animation .animation
animation grow 1s linear infinite animation grow 1s linear infinite
display inline-block display inline-block
vertical-align middle
margin 0 2px
@keyframes grow @keyframes grow
0% 0%
transform scale(1)
40% 40%
transform scale(1)
50%
transform scale(1.2)
60% 60%
transform scale(1)
70%
transform scale(1.2)
95% 95%
transform scale(1) transform scale(1)
50%
70%
transform scale(1.2)

View File

@ -0,0 +1,15 @@
import { Meta, Story } from '@storybook/react/types-6-0'
import React from 'react'
import Component from '.'
export default {
title: 'DZEIO/Footer',
component: Component,
} as Meta
export const Basic: Story<any> = (args: any) => <Component {...args} />
let tmp = Basic.bind({})
tmp.args = {links: [{name: 'test1', path: '/'}, {name: 'test2', path: '/'}, {name: 'test3', path: '/'}]}
export const Normal = tmp

View File

@ -1,13 +1,32 @@
import React from 'react' import React from 'react'
import { Heart } from 'react-feather'
import Link from '../Link'
import Text from '../Text' import Text from '../Text'
import css from './Footer.module.styl' import css from './Footer.module.styl'
export default class Footer extends React.Component { interface Props {
text?: string
company?: string
links?: Array<{
path: string
name: string
}>
}
export default class Footer extends React.Component<Props> {
public render = () => ( public render = () => (
<footer className={css.footer}> <footer className={css.footer}>
<Text align="center">Made with <span className={css.animation}>💗</span> by Dzeio</Text> {this.props.text ? (
<Text align="center">Copyright © 2020 Dzeio. All rights reserved.</Text> <Text align="center">{this.props.text}</Text>
) : (
<Text align="center">Made with <span className={css.animation}><Heart color={'#E6808A'} fill={'#E6808A'} size={16} fillOpacity={0.5} /></span> by {this.props.company || 'Dzeio'}</Text>
)}
{this.props.links && (
<ul>{this.props.links.map((l, index) => (
<li key={l.path}><Text>{index !== 0 && (<>&nbsp;- </>)}<Link href={l.path}>{l.name}</Link></Text></li>
))}</ul>
)}
</footer> </footer>
) )
} }

View File

@ -34,7 +34,7 @@
left 16px // input padding-left left 16px // input padding-left
~ label ~ label
left 16px + 24px + 16px left 16px + 24px + 10px
&.right &.right
right 16px right 16px
@ -68,6 +68,7 @@
display flex display flex
opacity 0 opacity 0
transition all $transition transition all $transition
overflow-x hidden
pointer-events none pointer-events none
// display flex // display flex
flex-direction column flex-direction column
@ -109,10 +110,12 @@
@media (prefers-color-scheme dark) @media (prefers-color-scheme dark)
background lighten(lighten($foregroundDark, 5%), 20%) background lighten(lighten($foregroundDark, 5%), 20%)
div + .autocomplete
top calc(100% - 4px - .9em)
input:focus + .autocomplete input:focus ~ .autocomplete
select:focus + .autocomplete select:focus ~ .autocomplete
textarea:focus + .autocomplete textarea:focus ~ .autocomplete
.autocomplete:hover .autocomplete:hover
opacity 1 opacity 1
pointer-events inherit pointer-events inherit
@ -122,7 +125,7 @@
textarea textarea
padding 14px 16px padding 14px 16px
height 56px height 56px
border 2px solid rgba(black, .3) border 2px solid $grayDark
border-radius 4px border-radius 4px
max-width 100% max-width 100%
box-sizing border-box box-sizing border-box
@ -132,20 +135,9 @@
transition all $transition transition all $transition
color black color black
@media (prefers-color-scheme dark) @media (prefers-color-scheme dark)
border-color rgba(white, .3) border-color $grayLight
color white color white
&:not(:disabled):hover
border-color black
@media (prefers-color-scheme dark)
border-color white
+ svg
color black
@media (prefers-color-scheme dark)
color white
&:not(:placeholder-shown) &:not(:placeholder-shown)
&:focus &:focus
&:not([placeholder=" "]) &:not([placeholder=" "])
@ -168,27 +160,47 @@
~ label ~ label
color #999 color #999
&:invalid &:not(:disabled)
border-color $danger &:hover
border-color black
@media (prefers-color-scheme dark)
border-color white
~ label + svg
color @border-color color black
~ svg @media (prefers-color-scheme dark)
color @border-color color white
&:focus &:focus
border-color $default border-color $main
~ label ~ label
color @border-color color @border-color
~ svg
color @border-color
&:invalid
border-color $errorLight
~ label
color @border-color
~ svg
color @border-color
@media (prefers-color-scheme dark)
border-color $errorDark
~ label
color @border-color
~ svg
color @border-color
~ svg
color @border-color
&.iconLeft &.iconLeft
padding-left 16px + 24px + 16px padding-left 16px + 24px + 10px
&.iconRight &.iconRight
padding-right 16 + 24 + 16px padding-right 16 + 24 + 10px
&.filled &.filled
border none border none
background rgba(gray, .1) background rgba(gray, .1)
@ -235,12 +247,12 @@
padding 0 padding 0
font-size .75rem font-size .75rem
~ svg.left ~ label ~ svg.left ~ label
left 16px + 24px + 16px // .input/padding-left label/padding-left left 16px + 24px + 10px // .input/padding-left label/padding-left
~ svg.rotate ~ svg.rotate
transform rotateX(0) transform rotateX(0)
transition $transition transition $transition
&:hover:focus ~ svg.rotate, ~.autocomplete:hover ~ svg.rotate &:focus ~ svg.rotate, ~.autocomplete:hover ~ svg.rotate
transform rotateX(180deg) transform rotateX(180deg)
div div
display flex display flex

View File

@ -1,10 +1,29 @@
import { Meta } from '@storybook/react/types-6-0' import { Meta } from '@storybook/react/types-6-0'
import { Story } from "@storybook/react"
import React from 'react' import React from 'react'
import Component from '.' import Component from '.'
import { X } from 'react-feather'
export default { export default {
title: 'DZEIO/Input', title: 'DZEIO/Input',
component: Component component: Component
} as Meta } as Meta
export const Basic = (args: any) => <Component {...args} /> export const Basic: Story<any> = (args: any) => <Component {...args} />
let tmp = Basic.bind({})
tmp.args = {label: 'Label', helper: 'Helper', maxLength: 6, characterCount: true, icon: X}
export const Normal = tmp
tmp = Basic.bind({})
tmp.args = {label: 'Label', filled:true, helper: 'Helper', autocomplete: ['a', 'b', 'cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc'], characterCount: true, icon: X}
export const AutoComplete = tmp
export const Select: Story<any> = (args: any) => <Component type="select" {...args}>
<option>a</option>
<option>b</option>
<option>c</option>
<option>d</option>
</Component>

View File

@ -5,6 +5,7 @@ import Text from '../Text'
import { IconProps } from '../interfaces' import { IconProps } from '../interfaces'
import { buildClassName } from '../Util' import { buildClassName } from '../Util'
import css from './Input.module.styl' import css from './Input.module.styl'
import Row from '../Row'
interface Props extends React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement> { interface Props extends React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement> {
id?: string id?: string
@ -37,6 +38,8 @@ interface States {
export default class Input extends React.Component<Props, States> { export default class Input extends React.Component<Props, States> {
public state: States = {}
// any because f*ck types // any because f*ck types
private inputRef: React.RefObject<any> = React.createRef() private inputRef: React.RefObject<any> = React.createRef()
private parentRef: React.RefObject<HTMLDivElement> = React.createRef() private parentRef: React.RefObject<HTMLDivElement> = React.createRef()
@ -50,11 +53,14 @@ export default class Input extends React.Component<Props, States> {
} }
if (this.props.autocomplete) { if (this.props.autocomplete) {
window.addEventListener('scroll', this.parentScroll) window.addEventListener('scroll', this.parentScroll)
this.parentScroll()
} }
} }
public componentWillUnmount() { public componentWillUnmount() {
window.removeEventListener('scroll', this.parentScroll) if (this.props.autocomplete) {
window.removeEventListener('scroll', this.parentScroll)
}
} }
public render() { public render() {
@ -65,7 +71,9 @@ export default class Input extends React.Component<Props, States> {
delete props.opaque delete props.opaque
delete props.helper delete props.helper
delete props.infinityText delete props.infinityText
delete props.autocomplete
delete props.filled delete props.filled
delete props.iconRight
delete props.inputRef delete props.inputRef
delete props.selectRef delete props.selectRef
delete props.block delete props.block
@ -77,7 +85,7 @@ export default class Input extends React.Component<Props, States> {
ref: this.props.inputRef || this.inputRef, ref: this.props.inputRef || this.inputRef,
className: buildClassName( className: buildClassName(
[css.iconLeft, this.props.icon], [css.iconLeft, this.props.icon],
[css.iconRight, this.props.iconRight], [css.iconRight, this.props.iconRight || this.props.autocomplete],
[css.filled, this.props.filled], [css.filled, this.props.filled],
[css.opaque, this.props.opaque] [css.opaque, this.props.opaque]
), ),
@ -94,7 +102,7 @@ export default class Input extends React.Component<Props, States> {
input = ( input = (
<select <select
ref={this.props.selectRef || this.inputRef} ref={this.props.selectRef || this.inputRef}
{...this.props as React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLSelectElement>, HTMLSelectElement>} {...props as React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLSelectElement>, HTMLSelectElement>}
className={buildClassName( className={buildClassName(
[css.iconLeft, this.props.icon], [css.iconLeft, this.props.icon],
[css.iconRight, !this.props.disabled || this.props.iconRight], [css.iconRight, !this.props.disabled || this.props.iconRight],
@ -138,12 +146,6 @@ export default class Input extends React.Component<Props, States> {
> >
{input} {input}
{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>
)}
{/* Process Icon */} {/* Process Icon */}
{this.props.icon && ( {this.props.icon && (
<this.props.icon className={css.left} /> <this.props.icon className={css.left} />
@ -167,6 +169,12 @@ export default class Input extends React.Component<Props, States> {
)} )}
</div> </div>
)} )}
{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>
)}
</div> </div>
) )
} }
@ -175,6 +183,7 @@ export default class Input extends React.Component<Props, States> {
const div = this.parentRef.current const div = this.parentRef.current
if (!div) {return} if (!div) {return}
const result = !(div.offsetTop - window.scrollY >= window.innerHeight / 2) const result = !(div.offsetTop - window.scrollY >= window.innerHeight / 2)
console.log(result, div, this.state.isInFirstPartOfScreen)
if (this.state.isInFirstPartOfScreen !== result) { if (this.state.isInFirstPartOfScreen !== result) {
this.setState({isInFirstPartOfScreen: result}) this.setState({isInFirstPartOfScreen: result})
} }

View File

@ -1,3 +1,12 @@
@import '../config'
.link
color $infoDark
@media (prefers-color-scheme dark)
color $infoLight
&:hover
text-decoration underline
.icon .icon
vertical-align sub vertical-align sub
margin 2px margin 2px

View File

@ -3,22 +3,28 @@ import NextLink from 'next/link'
import { ExternalLink } from 'react-feather' import { ExternalLink } from 'react-feather'
import css from './Link.module.styl' import css from './Link.module.styl'
import { buildClassName } from '../Util'
interface Props { interface Props {
href: string href: string
children?: React.ReactNode children?: React.ReactNode
className?: string className?: string
forceNewTab?: boolean /**
* Override external detection system
*/
noStyle?: boolean
external?: boolean
} }
export default class Link extends React.Component<Props> { export default class Link extends React.Component<Props> {
public render() { public render() {
if (!this.props.href.startsWith('/')) { const external = this.props.external ?? !this.props.href.startsWith('/')
if (external) {
// external link // external link
return ( return (
<a <a
className={this.props.className} className={buildClassName(this.props.className, [css.link, !this.props.noStyle])}
href={this.props.href} href={this.props.href}
rel="noreferrer nofollow" rel="noreferrer nofollow"
target="_blank" target="_blank"
@ -30,9 +36,7 @@ export default class Link extends React.Component<Props> {
return ( return (
<NextLink href={this.props.href}> <NextLink href={this.props.href}>
<a <a
className={this.props.className} className={buildClassName(this.props.className, [css.link, !this.props.noStyle])}
target={this.props.forceNewTab ? '_blank' : undefined}
rel={this.props.forceNewTab ? 'noreferrer nofollow' : undefined}
>{this.props.children}</a> >{this.props.children}</a>
</NextLink> </NextLink>
) )

View File

@ -0,0 +1,18 @@
@import '../config'
.div
position fixed
left 0
width 100%
height 7px
pointer-events none
top 0
&.hide
transition top .5s ease-in-out
top -7px
div
&:not([style="width: 0px;"])
transition width 50ms ease-in-out
background rgba($main, .7)
height 100%

View File

@ -0,0 +1,79 @@
import { Router } from 'next/router'
import React from 'react'
import { buildClassName } from '../Util'
import css from './Loader.module.styl'
interface Props {
percent?: number
auto?: {
interval: [number, number]
increment: [number, number]
}
}
interface State {
percent?: number
}
export default class Loader extends React.Component<Props, State> {
public state: State = {}
private interval?: NodeJS.Timeout
public componentDidMount() {
if (this.props.auto) {
Router.events.on('routeChangeComplete', this.routeChangeComplete)
Router.events.on('routeChangeStart', this.routeChangeStart)
}
}
public componentWillUnmount() {
if (this.props.auto) {
Router.events.off('routechangeComplete', this.routeChangeComplete)
Router.events.off('routechangeStart', this.routeChangeStart)
}
}
public render = () => (
<div className={buildClassName(css.div, [css.hide, (this.props.percent || this.state.percent) === 100])}>
<div style={{width: (this.props.percent || this.state.percent) ? `${(this.props.percent || this.state.percent)}%` : 0}}></div>
</div>
)
private routeChangeComplete = () => {
if (this.interval) {
clearTimeout(this.interval)
this.interval = undefined
}
this.setState({percent: 100})
}
private routeChangeStart = () => {
if (this.interval) {
clearTimeout(this.interval)
}
this.setState({percent: 0}, () => {
if (!this.props.auto) {return}
this.interval = setTimeout(this.timeoutFn, this.randomInt(this.props.auto.interval[0], this.props.auto.interval[1]))
})
}
private timeoutFn = () => {
if (!this.props.auto) {return}
const p = this.state.percent || 0
this.setState({
percent: Math.min(
99,
p >= 80 ? p + Math.random() : p + this.randomInt(this.props.auto.increment[0], this.props.auto.increment[1])
)
})
this.interval = setTimeout(this.timeoutFn, this.randomInt(this.props.auto.interval[0], this.props.auto.interval[1]))
}
private randomInt(min = 0, max = 10) {
min = Math.ceil(min)
max = Math.floor(max)
return Math.floor(Math.random() * (max - min + 1)) + min
}
}

View File

@ -1,49 +1,124 @@
@import "../config" @import '../config'
.navbar .body
width 100% margin-left 300px
height 70px transition margin-left $transition
padding 16px
position absolute &.short
top 0 margin-left 56px
.sidebar
background $foregroundLight
@media (prefers-color-scheme dark)
background $foregroundDark
position fixed
left 0 left 0
border-bottom 1px solid white top 0
height 100vh
width 300px
z-index 100
display flex
flex-direction column
&
transition width $transition
.header
.userSpace
.header .imgContainer
> ul span
// transition all $transition
transition-property width, padding, margin, max-width
transition-duration $transitionTime
transition-timing-function $transitionFunction
overflow hidden
> ul span
width calc(100% - 40px)
max-width 100%
&.small .userSpaceParent
padding-left 216px background $backgroundLight
@media (prefers-color-scheme dark)
background $backgroundDark
.alignRight .userSpace
text-align right cursor pointer
user-select none
padding 16px
width 100%
max-width 100%
min-height 56px
p
overflow hidden
p:last-child:not(:first-child)
margin-top 8px
font-style italic
font-size rem(14)
.favicon img p:first-child
height 38px font-weight 500
border-radius 8px svg
border 2px solid white vertical-align top
padding 2px transition transform $transition
background white &.menuActive
width 38px transform rotateX(180deg)
.userIcon img .userMenu
padding 0 max-height 0px
cursor pointer transition all $transition
.text ul
display inline-block padding-bottom 16px
margin 0 a
height 38px padding 16px 0
line-height 1
font-weight bold
font-size rem(20)
padding 7px 0 11px 16px
text-decoration none
color white
cursor pointer
.spacer &.menuActive
height 70px // TODO find better way to animate this shit
max-height 100%
.icon &.short
padding 7px 16px width 56px
color white .header > div
box-sizing content-box padding 0
cursor pointer .header .imgContainer
.userSpace
> ul span
width 0
padding 0
margin 0
max-width 0
.header
min-height 70px
> div
padding 0
&:first-child
padding 16px
p > div
padding 16px
hr
margin 0
ul
list-style none
margin 0
padding 0
> ul li
width 100%
p
padding 16px 0
display flex
align-items center
// TODO Currently Gradient aren't supported by Transition
// transition-property background-image, color
// transition-duration $transitionTime
// transition-timing-function $transitionFunction
&:hover
&.active
background-image $mainGradient
color white
svg
margin-left 16px
span
padding-left 16px
height inherit

View File

@ -1,11 +0,0 @@
import React from 'react'
import css from './Navbar.module.styl'
export default class NavbarSpace extends React.Component {
public render = () => (
<div className={css.spacer}></div>
)
}

View File

@ -1,48 +1,128 @@
import React from 'react' import Router from 'next/router'
import Link from 'next/link'
import Row from '../Row' import Image, { ImageProps } from 'next/image'
import React, { FC } from 'react'
import { ChevronDown, ChevronsRight, X } from 'react-feather'
import Text from '../Text'
import Col from '../Col' import Col from '../Col'
import Image from '../Image' import Row from '../Row'
import Link from '../Link'
import { buildClassName } from '../Util'
import css from './Navbar.module.styl' import css from './Navbar.module.styl'
interface Props { interface Props {
logo?: { type: 'navbar' | 'sidebar'
link?: string logo: ImageProps
label?: string user?: {
src: string name: string
alt?: string description?: string
settings?: string
menu?: {
links: Array<{
path: string
name: string
}>
informations?: JSX.Element
}
} }
items: Array<{
path: string
icon: FC
name: string
}>
} }
export default class Navbar extends React.Component<Props> { interface State {
path?: string
short: boolean
menuActive: boolean
}
export default class Navbar extends React.Component<Props, State> {
public state: State = {
short: false,
menuActive: false
}
public componentDidMount() {
this.setState({path: Router.asPath})
Router.events.on('routeChangeComplete', () => {
this.setState({path: Router.asPath})
})
document.body.classList.add(css.body)
if (this.state.short) {
document.body.classList.add(css.short)
}
}
public componentDidUpdate() {
if (this.state.short) {
document.body.classList.add(css.short)
} else {
document.body.classList.remove(css.short)
}
}
public componentWillUnmount() {
document.body.classList.remove(css.body, css.short)
}
public render = () => ( public render = () => (
<nav className={css.navbar}> <nav className={buildClassName(css.sidebar, [css.short, this.state.short])}>
<Row nomargin> <Row nomargin className={css.header} align="center">
{this.props.logo && ( <Col className={css.imgContainer}><Link href="/"><Image {...this.props.logo} /></Link></Col>
<Col> <Col nogrow><Text><div onClick={() => this.setState({short: !this.state.short, menuActive: false})}>
<Row align="center"> {this.state.short ? (
<Link href={this.props.logo.link || '/'}> <ChevronsRight size={30} />
<a aria-label={this.props.logo.label || 'Homepage'}> ) : (
<Image <X size={30} />
alt={this.props.logo.alt} )}
src={this.props.logo.src} </div></Text></Col>
height={38}
width={120}
/>
</a>
</Link>
</Row>
</Col>
)}
<Col>
<Row justify="flex-end" align="center">
{this.props.children}
</Row>
</Col>
</Row> </Row>
<hr/>
<ul>
{this.props.items.map((item) => (
<li key={item.path}><Link href={item.path}><a>
<Text className={buildClassName([css.active, this.state.path?.startsWith(item.path)])} >
<item.icon />
<span>{item.name}</span>
</Text>
</a></Link></li>
))}
</ul>
<div style={{flex: 1}}></div>
{/* Spacer */}
{this.props.user && (
<>
<div className={css.userSpaceParent}>
<hr/>
<div onClick={() => this.setState({menuActive: !this.state.menuActive})} className={css.userSpace}>
<Text>
{this.props.user.name}
<ChevronDown className={buildClassName([css.menuActive, this.state.menuActive])} />
</Text>
{this.props.user.description && (
<Text>{this.props.user.description}</Text>
)}
</div>
</div>
<div className={buildClassName(css.userMenu, [css.menuActive, this.state.menuActive])}>
<Row nomargin>
<Col></Col>
<Col>
<ul>
{this.props.user.menu?.links.map((l) => (
<li key={l.path}><Text><Link href={l.path}>{l.name}</Link></Text></li>
))}
</ul>
</Col>
</Row>
</div>
</>
)}
</nav> </nav>
) )
} }

View File

@ -6,22 +6,38 @@
width 100% width 100%
top 0 top 0
left 0 left 0
background rgba(black, .3) background rgba($backgroundLight, .7)
@media (prefers-color-scheme dark)
background rgba($backgroundDark, .7)
cursor pointer cursor pointer
z-index 1000 z-index 200
animation fadeIn .3s ease-in-out 1 forwards
@keyframes fadeIn
from
opacity 0
to
opacity 1
.popupChild .popupChild
cursor initial cursor initial
z-index 1001 z-index 201
min-width 50% min-width 50%
animation popin .3s ease-in-out 1
@media (max-width $tablet) @media (max-width $tablet)
min-width 70% min-width 70%
@media (max-width $mobile) @media (max-width $mobile)
min-width 90% min-width 90%
// min-height 50vh
@keyframes popin
from
margin-top 50px
to
margin-top 0
.exit .exit
cursor pointer cursor pointer

View File

@ -1,139 +0,0 @@
@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)

View File

@ -1,17 +0,0 @@
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>
)
}

View File

@ -1,42 +0,0 @@
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>
)
}
}

View File

@ -24,13 +24,3 @@ export function buildClassName(...classes: Array<Array<any> | string | undefined
} }
return classesFinal.join(' ') return classesFinal.join(' ')
} }
export const colors = {
default: '#3949AB', // This color should never appear
primary: '#3949AB',
secondary: '#FCFCFC',
info: '#03A9F4',
success: '#2DCE89',
danger: '#F5365C',
warning: '#FB6340'
}

View File

@ -52,3 +52,9 @@ $success = $successLight
$danger = $errorLight $danger = $errorLight
$warning = $warningLight $warning = $warningLight
$darkBackground = $backgroundDark $darkBackground = $backgroundDark
// See https://github.com/stylus/stylus/issues/1872#issuecomment-86553717
use('stylusUtils.js')
@import '../../../../../../theme' if file-exists('../../../../../../theme.styl')

View File

@ -27,13 +27,16 @@ a
} }
/* Track */ /* Track */
::-webkit-scrollbar-corner
::-webkit-scrollbar-track ::-webkit-scrollbar-track
background $foregroundLight background $foregroundLight
transition $transition
@media (prefers-color-scheme dark) @media (prefers-color-scheme dark)
background $foregroundDark background $foregroundDark
::-webkit-scrollbar-thumb ::-webkit-scrollbar-thumb
background: darken($foregroundLight, 16%) background: darken($foregroundLight, 16%)
transition $transition
@media (prefers-color-scheme dark) @media (prefers-color-scheme dark)
background lighten($foregroundDark, 16%) background lighten($foregroundDark, 16%)
&:hover &:hover

11
src/dzeio/stylusUtils.js Normal file
View File

@ -0,0 +1,11 @@
// See https://github.com/stylus/stylus/issues/1872#issuecomment-86553717
var stylus = require('stylus');
module.exports = function() {
return function(style) {
style.define('file-exists', function(path) {
return !!stylus.utils.lookup(path.string, this.paths);
});
};
};

View File

@ -1,4 +1,4 @@
import Box, { BoxBody, BoxHeader, BoxWrapper } from './dzeio/Box' import Box from './dzeio/Box'
import Button from './dzeio/Button' import Button from './dzeio/Button'
import Checkbox from './dzeio/Checkbox' import Checkbox from './dzeio/Checkbox'
import Code from './dzeio/Code' import Code from './dzeio/Code'
@ -7,28 +7,22 @@ import Container from './dzeio/Container'
import Fieldset from './dzeio/Fieldset' import Fieldset from './dzeio/Fieldset'
import Footer from './dzeio/Footer' import Footer from './dzeio/Footer'
import GradientBackground from './dzeio/GradientBackground' import GradientBackground from './dzeio/GradientBackground'
import Input from './dzeio/Input'
import Image from './dzeio/Image' import Image from './dzeio/Image'
import Input from './dzeio/Input'
import Link from './dzeio/Link' import Link from './dzeio/Link'
import Loader from './dzeio/Loader'
import Menu from './dzeio/Menu' import Menu from './dzeio/Menu'
import Navbar from './dzeio/Navbar' import Navbar from './dzeio/Navbar'
import NavbarSpace from './dzeio/Navbar/NavbarSpace'
import Overflow from './dzeio/Overflow' import Overflow from './dzeio/Overflow'
import Popup from './dzeio/Popup' 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'
import './dzeio/general.styl'
export { export {
Box, Box,
BoxBody,
BoxHeader,
BoxWrapper,
Button, Button,
Checkbox, Checkbox,
Code, Code,
@ -40,15 +34,14 @@ export {
Image, Image,
Input, Input,
Link, Link,
Loader,
Menu, Menu,
Navbar, Navbar,
NavbarSpace,
Overflow, Overflow,
Popup, Popup,
Row, Row,
SidebarContainer, SidebarContainer,
Table, Table,
Tag,
Text, Text,
Util Util
} }

View File

@ -11,7 +11,7 @@
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
// "sourceMap": true, /* Generates corresponding '.map' file. */ // "sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./", /* Concatenate and emit output to single file. */ // "outFile": "./", /* Concatenate and emit output to single file. */
"declarationDir": "types", "declarationDir": "src",
// "outDir": "./dist", /* Redirect output structure to the directory. */ // "outDir": "./dist", /* Redirect output structure to the directory. */
// "rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ // "rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
// "composite": true, /* Enable project compilation */ // "composite": true, /* Enable project compilation */
@ -64,6 +64,7 @@
], ],
"exclude": [ "exclude": [
"node_modules", "node_modules",
"build" "build",
"src/**/*.stories.tsx"
] ]
} }

3241
yarn.lock

File diff suppressed because it is too large Load Diff