mirror of
https://github.com/dzeiocom/components.git
synced 2025-06-07 00:29:55 +00:00
Updated
Signed-off-by: Avior <github@avior.me>
This commit is contained in:
parent
bb001148a5
commit
8d7a8c70f0
@ -3,7 +3,7 @@ const webpack = require('webpack')
|
||||
|
||||
module.exports = {
|
||||
"stories": [
|
||||
"../src/dzeio/**/*.stories.tsx",
|
||||
"../src/**/*.stories.tsx",
|
||||
],
|
||||
core: {
|
||||
builder: "webpack5"
|
||||
|
@ -1,3 +1,4 @@
|
||||
import React from 'react'
|
||||
// https://stackoverflow.com/a/64765638/7335674
|
||||
|
||||
import * as nextImage from 'next/image'
|
||||
|
@ -1,8 +1,9 @@
|
||||
import Router from 'next/router';
|
||||
import Router from 'next/router'
|
||||
|
||||
Router.router = {
|
||||
push: async () => {},
|
||||
replace: async () => {},
|
||||
prefetch: () => {},
|
||||
route: '/mock-route',
|
||||
pathname: 'mock-path',};
|
||||
pathname: 'mock-path',
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import '../src/dzeio/general.styl'
|
||||
import '../src/general.styl'
|
||||
import './mockNextRouter'
|
||||
import './mockNextImage'
|
||||
|
||||
export const parameters = {
|
||||
layout: 'centered'
|
||||
}
|
||||
}
|
||||
|
7724
package-lock.json
generated
7724
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
10
package.json
10
package.json
@ -8,11 +8,11 @@
|
||||
"@babel/core": "^7.12.16",
|
||||
"@babel/preset-env": "^7.12.16",
|
||||
"@babel/preset-react": "^7.12.13",
|
||||
"@storybook/addon-essentials": "^6.1.14",
|
||||
"@storybook/builder-webpack5": "^6.3.8",
|
||||
"@storybook/cli": "^6.1.14",
|
||||
"@storybook/manager-webpack5": "^6.3.8",
|
||||
"@storybook/react": "^6.1.14",
|
||||
"@storybook/addon-essentials": "^6.3.9",
|
||||
"@storybook/builder-webpack5": "^6.3.9",
|
||||
"@storybook/cli": "^6.3.9",
|
||||
"@storybook/manager-webpack5": "^6.3.9",
|
||||
"@storybook/react": "^6.3.9",
|
||||
"@types/node": "^16.10.2",
|
||||
"@types/react": "^17.0.2",
|
||||
"@types/react-dom": "^17.0.1",
|
||||
|
@ -1,43 +1,43 @@
|
||||
import React from 'react'
|
||||
import { Check } from 'lucide-react'
|
||||
import { buildClassName } from '../Util'
|
||||
|
||||
import css from './Checkbox.module.styl'
|
||||
import Text from '../Text'
|
||||
|
||||
interface Props extends React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement> {
|
||||
label: string
|
||||
type?: 'checkbox' | 'radio' | 'switch'
|
||||
}
|
||||
|
||||
export default class Checkbox extends React.Component<Props> {
|
||||
|
||||
public render() {
|
||||
const props: Partial<Props> = Object.assign({}, this.props)
|
||||
delete props.label
|
||||
delete props.type
|
||||
|
||||
const realType = this.props.type ?? 'checkbox'
|
||||
|
||||
return (
|
||||
<label htmlFor={this.props.id ?? this.props.label} className={buildClassName(
|
||||
[css.label],
|
||||
[css.radio, realType === 'radio'],
|
||||
[css.switch, realType === 'switch'],
|
||||
[css[this.props.color as string], this.props.color]
|
||||
)}>
|
||||
<input {...props}
|
||||
id={this.props.id ?? this.props.label}
|
||||
type={realType === 'switch' ? 'checkbox' : realType}
|
||||
/>
|
||||
<span>
|
||||
{realType === 'checkbox' && (
|
||||
<Check strokeWidth={4} size={16}/>
|
||||
)}
|
||||
</span>
|
||||
<Text>{this.props.label}</Text>
|
||||
</label>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
import React from 'react'
|
||||
import { Check } from 'lucide-react'
|
||||
import { buildClassName } from '../Util'
|
||||
|
||||
import css from './Checkbox.module.styl'
|
||||
import Text from '../Text'
|
||||
|
||||
interface Props extends React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement> {
|
||||
label: string
|
||||
type?: 'checkbox' | 'radio' | 'switch'
|
||||
}
|
||||
|
||||
export default class Checkbox extends React.Component<Props> {
|
||||
|
||||
public render() {
|
||||
const props: Partial<Props> = Object.assign({}, this.props)
|
||||
delete props.label
|
||||
delete props.type
|
||||
|
||||
const realType = this.props.type ?? 'checkbox'
|
||||
|
||||
return (
|
||||
<label htmlFor={this.props.id ?? this.props.label} className={buildClassName(
|
||||
[css.label],
|
||||
[css.radio, realType === 'radio'],
|
||||
[css.switch, realType === 'switch'],
|
||||
[css[this.props.color as string], this.props.color]
|
||||
)}>
|
||||
<input {...props}
|
||||
id={this.props.id ?? this.props.label}
|
||||
type={realType === 'switch' ? 'checkbox' : realType}
|
||||
/>
|
||||
<span>
|
||||
{realType === 'checkbox' && (
|
||||
<Check strokeWidth={4} size={16}/>
|
||||
)}
|
||||
</span>
|
||||
<Text>{this.props.label}</Text>
|
||||
</label>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
@ -27,6 +27,11 @@ interface Props extends React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLIn
|
||||
// Custom Types
|
||||
'textarea'
|
||||
choices?: Array<string | {display: string, value: string}>
|
||||
|
||||
/**
|
||||
* Always display every choices
|
||||
*/
|
||||
displayAllOptions?: boolean
|
||||
}
|
||||
|
||||
interface States {
|
||||
@ -179,7 +184,7 @@ export default class Input extends React.PureComponent<Props, States> {
|
||||
return this.props.choices
|
||||
.map((item, index) => typeof item === 'string' ? ({item: {display: item, value: item}, index}) : {item, index})
|
||||
.filter(
|
||||
(item) => !v || item.item.display.toLowerCase().includes(v) || item.item.display.toLowerCase().toLowerCase().includes(v)
|
||||
(item) => this.props.displayAllOptions || !v || item.item.display.toLowerCase().includes(v) || item.item.display.toLowerCase().toLowerCase().includes(v)
|
||||
)
|
||||
.map((item) => ({display: item.item.display, value: item.index}))
|
||||
}
|
@ -22,6 +22,7 @@
|
||||
margin-bottom 8px
|
||||
color $darkGrayLight
|
||||
cursor pointer
|
||||
transition all $transition
|
||||
font-weight bold
|
||||
border-radius 8px
|
||||
&:hover
|
177
src/Sidebar/Sidebar.module.styl
Normal file
177
src/Sidebar/Sidebar.module.styl
Normal file
@ -0,0 +1,177 @@
|
||||
@import '../config'
|
||||
|
||||
// $transition = 10s linear
|
||||
// $transitionTime = 10s
|
||||
// $transitionFunction = linear
|
||||
.sidebarBody
|
||||
margin-left 300px
|
||||
transition margin-left $transition
|
||||
&.short
|
||||
margin-left 56px
|
||||
|
||||
.sidebar
|
||||
background $foregroundLight
|
||||
@media (prefers-color-scheme dark)
|
||||
background $foregroundDark
|
||||
position fixed
|
||||
left 0
|
||||
top 0
|
||||
padding 24px
|
||||
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%
|
||||
.header p
|
||||
cursor pointer
|
||||
|
||||
.userSpace
|
||||
> div
|
||||
transition all $transition
|
||||
p
|
||||
overflow hidden
|
||||
white-space nowrap
|
||||
svg
|
||||
cursor pointer
|
||||
|
||||
&.short
|
||||
width 88px
|
||||
.userSpace > div:first-child
|
||||
.header > div:first-child
|
||||
width 0
|
||||
max-width 0
|
||||
.header
|
||||
margin-left -16px
|
||||
> div:first-child
|
||||
padding-left 0 !important
|
||||
|
||||
.header > div svg
|
||||
.userSpace svg
|
||||
margin 0 8px
|
||||
.header .imgContainer
|
||||
> ul
|
||||
span
|
||||
svg:last-child
|
||||
width 0
|
||||
padding-left 0
|
||||
padding-right 0
|
||||
margin 0 !important
|
||||
max-width 0
|
||||
|
||||
|
||||
|
||||
.header
|
||||
margin-left -8px
|
||||
//min-height 70px
|
||||
svg
|
||||
margin-right 8px
|
||||
|
||||
hr
|
||||
margin 0
|
||||
ul ul
|
||||
padding-left 40px
|
||||
max-height 0
|
||||
opacity 0
|
||||
overflow hidden
|
||||
transition all $transition
|
||||
position relative
|
||||
&::before
|
||||
content " "
|
||||
position absolute
|
||||
background $darkGrayLight
|
||||
@media (prefers-color-theme dark)
|
||||
background $lightGrayDark
|
||||
border-radius 2px
|
||||
width 2px
|
||||
height 100%
|
||||
margin-top 8px
|
||||
left 18px
|
||||
li.activeMenu
|
||||
svg:last-child
|
||||
transform rotateX(180deg)
|
||||
ul
|
||||
opacity 1
|
||||
// not the best but IDK what is better
|
||||
max-height 100vh
|
||||
ul li
|
||||
margin-top 8px
|
||||
div
|
||||
width 100%
|
||||
cursor pointer
|
||||
border-radius 8px
|
||||
transition all $transition
|
||||
color $darkGrayLight
|
||||
@media (prefers-color-scheme dark)
|
||||
color $lightGrayDark
|
||||
&:first-child
|
||||
margin-top 0
|
||||
&:hover
|
||||
|
||||
color black
|
||||
background $lightGrayLight
|
||||
@media (prefers-color-scheme dark)
|
||||
color white
|
||||
background $darkGrayDark
|
||||
&.active
|
||||
&:active
|
||||
color $textOnMain
|
||||
background $main
|
||||
padding 8px
|
||||
display flex
|
||||
align-items center
|
||||
z-index 111
|
||||
position relative
|
||||
|
||||
svg
|
||||
transition color $transition
|
||||
&:first-child + span
|
||||
margin-left 16px
|
||||
|
||||
span
|
||||
display inline-block
|
||||
white-space nowrap
|
||||
|
||||
a
|
||||
width 100%
|
||||
display flex
|
||||
//max-height 24px
|
||||
|
||||
.navbar
|
||||
.sidebar
|
||||
ul
|
||||
list-style none
|
||||
margin 0
|
||||
padding 0
|
||||
|
||||
.userMenu
|
||||
padding 8px 16px
|
||||
a
|
||||
display inline-block
|
||||
padding-bottom 16px
|
||||
|
||||
|
||||
.mobileMenu
|
||||
opacity 0
|
||||
transition opacity $transition
|
||||
pointer-events none
|
||||
&.shown
|
||||
opacity 1
|
||||
pointer-events initial
|
||||
|
||||
.mainGradient
|
||||
//WIP
|
||||
fill $mainGradient
|
43
src/Sidebar/Sidebar.stories.tsx
Normal file
43
src/Sidebar/Sidebar.stories.tsx
Normal file
@ -0,0 +1,43 @@
|
||||
import React from 'react'
|
||||
import { Meta, Story } from '@storybook/react/types-6-0'
|
||||
import { Zap, ZapOff } from 'lucide-react'
|
||||
import Component from '.'
|
||||
|
||||
export default {
|
||||
title: 'DZEIO/Sidebar',
|
||||
component: Component,
|
||||
parameters: {
|
||||
layout: 'fullscreen'
|
||||
}
|
||||
} as Meta
|
||||
|
||||
export const Sidebar: Story<any> = (args: any) => <Component {...args} />
|
||||
Sidebar.args = {
|
||||
logo: {src: '/90-38.svg', width: 90, height: 38},
|
||||
user: {
|
||||
name: 'Username',
|
||||
menu: [{
|
||||
path: '/logout',
|
||||
value: 'Logout'
|
||||
}, {
|
||||
path: '/logout',
|
||||
value: 'Logout'
|
||||
}]
|
||||
},
|
||||
menu: [{
|
||||
name: 'Dasboard',
|
||||
icon: Zap
|
||||
}, {
|
||||
name: 'With Childs',
|
||||
icon: Zap,
|
||||
subMenu: [{
|
||||
name: 'With Childs'
|
||||
}, {
|
||||
name: 'With Childs'
|
||||
}]
|
||||
}, {
|
||||
path: '/dashboard',
|
||||
name: 'Link',
|
||||
icon: ZapOff
|
||||
}],
|
||||
}
|
219
src/Sidebar/index.tsx
Normal file
219
src/Sidebar/index.tsx
Normal file
@ -0,0 +1,219 @@
|
||||
import React, { MouseEvent } from 'react'
|
||||
|
||||
import Router from 'next/router'
|
||||
import Image, { ImageProps } from 'next/image'
|
||||
import { ChevronDown, Minus, MoreHorizontal, Plus } from 'lucide-react'
|
||||
import Text from '../Text'
|
||||
import Col from '../Col'
|
||||
import Row from '../Row'
|
||||
import Link from '../Link'
|
||||
import { buildClassName } from '../Util'
|
||||
|
||||
import css from './Sidebar.module.styl'
|
||||
import { Icon } from '../interfaces'
|
||||
import { Menu } from '..'
|
||||
import Router from 'next/router'
|
||||
|
||||
interface MenuItem {
|
||||
path?: string
|
||||
icon?: Icon
|
||||
name: string
|
||||
subMenu?: Array<MenuItem>
|
||||
}
|
||||
|
||||
interface Props {
|
||||
|
||||
/**
|
||||
* Logo to display
|
||||
*/
|
||||
logo?: ImageProps & {height: number, width: number}
|
||||
/**
|
||||
* User Informations if loggedin
|
||||
*/
|
||||
user?: {
|
||||
/**
|
||||
* Username
|
||||
*/
|
||||
name: string
|
||||
/**
|
||||
* User Menu
|
||||
*/
|
||||
menu?: Menu['props']['items']
|
||||
}
|
||||
/**
|
||||
* Links to display
|
||||
*/
|
||||
menu: Array<MenuItem>
|
||||
/**
|
||||
* Internal Use don't use it !
|
||||
*/
|
||||
onClose?: () => void
|
||||
}
|
||||
|
||||
interface State {
|
||||
path?: string
|
||||
/**
|
||||
* Define if the menu is open or closed
|
||||
*
|
||||
* in mobile it will be hidden when closed
|
||||
*/
|
||||
open: boolean
|
||||
activeMenu?: string
|
||||
isMobile: boolean
|
||||
userMenu?: boolean
|
||||
subMenu?: {
|
||||
y: number
|
||||
menu: Menu['props']['items']
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sidebar Component
|
||||
* @version 1.0.0
|
||||
*/
|
||||
export default class Navbar extends React.Component<Props, State> {
|
||||
|
||||
public state: State = {
|
||||
open: true,
|
||||
isMobile: false
|
||||
}
|
||||
|
||||
public componentDidMount() {
|
||||
this.setState({
|
||||
path: Router.asPath
|
||||
})
|
||||
Router.events.on('routeChangeComplete', () => {
|
||||
this.setState({path: Router.asPath, open: false})
|
||||
})
|
||||
Router.events.on('routeChangeError', () => {
|
||||
this.setState({path: Router.asPath, open: false})
|
||||
})
|
||||
document.body.classList.add(css.sidebarBody)
|
||||
document.body.addEventListener('click', this.onBodyClick)
|
||||
}
|
||||
|
||||
public componentDidUpdate() {
|
||||
if (this.state.open) {
|
||||
document.body.classList.remove(css.short)
|
||||
} else {
|
||||
document.body.classList.add(css.short)
|
||||
}
|
||||
}
|
||||
|
||||
public componentWillUnmount() {
|
||||
document.body.classList.remove(css.short, css.sidebarBody)
|
||||
document.body.removeEventListener('click', this.onBodyClick)
|
||||
}
|
||||
|
||||
private onBodyClick = () => {
|
||||
this.setState({subMenu: undefined, userMenu: false})
|
||||
}
|
||||
|
||||
public onClick = (id: string, subMenu?: Array<MenuItem>) => (ev: MouseEvent) => {
|
||||
ev.stopPropagation()
|
||||
if (!this.state.open && subMenu) {
|
||||
console.log(ev)
|
||||
this.setState({
|
||||
subMenu: {
|
||||
y: (ev.currentTarget as HTMLElement).offsetTop,
|
||||
menu: subMenu.map((v) => ({
|
||||
display: v.name,
|
||||
value: v.path
|
||||
}))
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.setState({activeMenu: this.state.activeMenu === id ? undefined : id, subMenu: undefined})
|
||||
}
|
||||
}
|
||||
|
||||
public render = () => (
|
||||
<>
|
||||
<nav className={buildClassName(
|
||||
css.sidebar,
|
||||
[css.short, !this.state.open]
|
||||
)}>
|
||||
<Row nowrap justify="space-between" className={css.header} align="center">
|
||||
{this.props.logo && (
|
||||
<Col>
|
||||
<Link href="/">
|
||||
<Image {...this.props.logo} height={34} width={this.props.logo.width*34/this.props.logo.height} />
|
||||
</Link>
|
||||
</Col>
|
||||
)}
|
||||
<Col nogrow><Text tag="div">
|
||||
{this.state.open ? (
|
||||
<Minus size={24} onClick={() => this.setState({open: false, activeMenu: undefined})} />
|
||||
) : (
|
||||
<Plus size={24} onClick={() => this.setState({open: true})} />
|
||||
)}
|
||||
</Text></Col>
|
||||
</Row>
|
||||
<ul>
|
||||
{this.props.menu.map((item) => this.makeMenuItem(item))}
|
||||
</ul>
|
||||
<div style={{flex: 1}}></div>
|
||||
{/* Spacer */}
|
||||
{this.props.user && (
|
||||
<Row className={css.userSpace}>
|
||||
<Col><Text>{this.props.user.name}</Text></Col>
|
||||
<Col nogrow><Text><MoreHorizontal size={24} onClick={(ev) => {ev.stopPropagation(); this.setState({userMenu: !this.state.userMenu})}} /></Text></Col>
|
||||
</Row>
|
||||
)}
|
||||
</nav>
|
||||
{this.props.user?.menu && this.state.userMenu && (
|
||||
<div style={{position: 'absolute', bottom: 16, left: this.state.open ? 316 : 104}}>
|
||||
<Menu onClick={this.onMenuClick} outline items={this.props.user.menu} />
|
||||
</div>
|
||||
)}
|
||||
{this.state.subMenu && (
|
||||
<div style={{position: 'absolute', top: this.state.subMenu.y, left: this.state.open ? 316 : 104}}>
|
||||
<Menu onClick={this.onMenuClick} outline items={this.state.subMenu.menu} />
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
|
||||
private onMenuClick = (value?: string) => {
|
||||
this.setState({userMenu: false, subMenu: undefined})
|
||||
if (value) {
|
||||
Router.push(value)
|
||||
}
|
||||
}
|
||||
|
||||
private makeMenuItem(obj: MenuItem, isSub = false) {
|
||||
const id = obj.name + obj.path
|
||||
const content = (
|
||||
<>
|
||||
{obj.icon && (
|
||||
<obj.icon size={24} />
|
||||
)}
|
||||
<Text color="none" weight="bold" tag="span">
|
||||
{obj.name}
|
||||
</Text>
|
||||
{obj.subMenu && (
|
||||
<ChevronDown size={24} />
|
||||
)}
|
||||
</>
|
||||
)
|
||||
return <li key={id} className={buildClassName(
|
||||
[css.active, obj?.path && this.state.path?.startsWith(obj.path)],
|
||||
[css.activeMenu, id === this.state.activeMenu]
|
||||
)}>
|
||||
<div onClick={isSub ? undefined : this.onClick(id, obj.subMenu)}>
|
||||
{obj.path ? (
|
||||
<Link noStyle href={obj.path}>
|
||||
{content}
|
||||
</Link>
|
||||
) : content}
|
||||
</div>
|
||||
{obj.subMenu && (
|
||||
<ul>
|
||||
{obj.subMenu.map((it, key) => (
|
||||
<li key={it.name + key}>{this.makeMenuItem(it, true)}</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</li>
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@ import css from './Text.module.styl'
|
||||
type Types = 'hero' | 'h1' | 'h2' | 'h3' | 'h4' | 'text' | 'light' | 'bold'
|
||||
|
||||
interface Props {
|
||||
color?: 'black' | 'white'
|
||||
color?: 'black' | 'white' | 'none'
|
||||
type?: Types
|
||||
tag?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p' | 'em' | 'span' | 'div'
|
||||
weight?: 'normal' | 'bold' | 'light'
|
44
src/index.ts
44
src/index.ts
@ -2,29 +2,29 @@
|
||||
* @summary DZEIO Component Library
|
||||
*/
|
||||
|
||||
import './dzeio/general.styl'
|
||||
import './general.styl'
|
||||
|
||||
import Box from './dzeio/Box'
|
||||
import BoxHeader from './dzeio/Box/BoxHeader'
|
||||
import Button from './dzeio/Button'
|
||||
import Checkbox from './dzeio/Checkbox'
|
||||
import Code from './dzeio/Code'
|
||||
import Col from './dzeio/Col'
|
||||
import Container from './dzeio/Container'
|
||||
import Footer from './dzeio/Footer'
|
||||
import GradientBackground from './dzeio/GradientBackground'
|
||||
import Image from './dzeio/Image'
|
||||
import Input from './dzeio/Input'
|
||||
import Link from './dzeio/Link'
|
||||
import Loader from './dzeio/Loader'
|
||||
import Menu from './dzeio/Menu'
|
||||
import Navbar from './dzeio/Navbar'
|
||||
import NotificationManager from './dzeio/NotificationManager'
|
||||
import Popup from './dzeio/Popup'
|
||||
import Row from './dzeio/Row'
|
||||
import Table from './dzeio/Table'
|
||||
import Text from './dzeio/Text'
|
||||
import * as Util from './dzeio/Util'
|
||||
import Box from './Box'
|
||||
import BoxHeader from './Box/BoxHeader'
|
||||
import Button from './Button'
|
||||
import Checkbox from './Checkbox'
|
||||
import Code from './Code'
|
||||
import Col from './Col'
|
||||
import Container from './Container'
|
||||
import Footer from './Footer'
|
||||
import GradientBackground from './GradientBackground'
|
||||
import Image from './Image'
|
||||
import Input from './Input'
|
||||
import Link from './Link'
|
||||
import Loader from './Loader'
|
||||
import Menu from './Menu'
|
||||
import Navbar from './Navbar'
|
||||
import NotificationManager from './NotificationManager'
|
||||
import Popup from './Popup'
|
||||
import Row from './Row'
|
||||
import Table from './Table'
|
||||
import Text from './Text'
|
||||
import * as Util from './Util'
|
||||
|
||||
export {
|
||||
Box,
|
||||
|
Loading…
x
Reference in New Issue
Block a user