w-design-system 0.0.29 → 0.0.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/assets/icon/bookmark.svg +15 -0
- package/src/assets/icon/coins.svg +10 -0
- package/src/assets/icon/file.svg +15 -0
- package/src/assets/icon/gavel.svg +3 -0
- package/src/assets/icon/gear.svg +10 -0
- package/src/assets/icon/grid-2-plus.svg +3 -0
- package/src/assets/icon/index.ts +42 -26
- package/src/assets/icon/lock.svg +2 -0
- package/src/assets/icon/wave-pulse.svg +10 -0
- package/src/components/DatePicker/index.tsx +17 -7
- package/src/components/DatePicker/stories.tsx +5 -6
- package/src/components/DateRangePicker/index.tsx +24 -15
- package/src/components/DateRangePicker/stories.tsx +1 -1
- package/src/components/Input/index.tsx +22 -5
- package/src/components/Select/index.tsx +36 -19
- package/src/components/Select/stories.tsx +3 -2
- package/src/components/Select/styles.ts +19 -6
- package/src/components/Sidebar/SidebarGroup/index.tsx +90 -0
- package/src/components/Sidebar/SidebarGroup/styles.ts +83 -0
- package/src/components/Sidebar/SidebarItem/index.tsx +69 -0
- package/src/components/Sidebar/SidebarItem/styles.ts +70 -0
- package/src/components/Sidebar/SidebarLogo/index.tsx +23 -0
- package/src/components/Sidebar/SidebarLogo/styles.ts +53 -0
- package/src/components/Sidebar/index.tsx +127 -0
- package/src/components/Sidebar/stories.tsx +51 -0
- package/src/components/Sidebar/styles.ts +80 -0
- package/src/components/Types/index.ts +23 -3
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { MouseEvent, PropsWithChildren, useCallback, useEffect, useState } from 'react'
|
|
2
|
+
import { SidebarGroupContainer, SidebarGroupHeaderContainer } from './styles'
|
|
3
|
+
import React from 'react'
|
|
4
|
+
import { IconWrapper } from '../../IconWrapper'
|
|
5
|
+
import { Paragraph } from '../../Paragraph'
|
|
6
|
+
import { theme } from '../../Themes'
|
|
7
|
+
import { chevronDownSVG, lockSVG } from '../../../assets/icon'
|
|
8
|
+
|
|
9
|
+
export interface SidebarGroupProps {
|
|
10
|
+
title: string
|
|
11
|
+
icon: string
|
|
12
|
+
sidebarOpen: boolean
|
|
13
|
+
disabled?: boolean
|
|
14
|
+
isCurrentGroup?: boolean
|
|
15
|
+
onClick: () => void
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const SidebarGroup = ({
|
|
19
|
+
title,
|
|
20
|
+
icon,
|
|
21
|
+
children,
|
|
22
|
+
sidebarOpen,
|
|
23
|
+
disabled,
|
|
24
|
+
onClick,
|
|
25
|
+
isCurrentGroup,
|
|
26
|
+
}: PropsWithChildren<SidebarGroupProps>) => {
|
|
27
|
+
const [groupOpen, setGroupOpen] = useState(false)
|
|
28
|
+
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
if (groupOpen && disabled) setGroupOpen(false)
|
|
31
|
+
}, [groupOpen, disabled])
|
|
32
|
+
|
|
33
|
+
const onClickHandler = useCallback(
|
|
34
|
+
(event: MouseEvent<HTMLDivElement>) => {
|
|
35
|
+
if (disabled) return
|
|
36
|
+
|
|
37
|
+
if (!sidebarOpen) {
|
|
38
|
+
onClick()
|
|
39
|
+
} else {
|
|
40
|
+
event.stopPropagation()
|
|
41
|
+
setGroupOpen(!groupOpen)
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
[disabled, sidebarOpen, groupOpen, onClick],
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
let color = '#fff'
|
|
48
|
+
|
|
49
|
+
if (disabled) {
|
|
50
|
+
color = 'rgba(255,255,255,0.3)'
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (isCurrentGroup) {
|
|
54
|
+
color = '#217efd'
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<SidebarGroupContainer open={sidebarOpen} groupOpen={groupOpen}>
|
|
59
|
+
<SidebarGroupHeaderContainer
|
|
60
|
+
disabled={disabled}
|
|
61
|
+
sidebarOpen={sidebarOpen}
|
|
62
|
+
groupOpen={groupOpen}
|
|
63
|
+
onClick={onClickHandler}
|
|
64
|
+
>
|
|
65
|
+
<IconWrapper src={icon} color={color} />
|
|
66
|
+
{sidebarOpen && (
|
|
67
|
+
<span className="title">
|
|
68
|
+
<Paragraph size="medium" heavyBod={isCurrentGroup} color={disabled ? theme.colors.shade40 : 'white'}>
|
|
69
|
+
{title}
|
|
70
|
+
</Paragraph>
|
|
71
|
+
</span>
|
|
72
|
+
)}
|
|
73
|
+
{sidebarOpen &&
|
|
74
|
+
(!disabled ? (
|
|
75
|
+
<IconWrapper
|
|
76
|
+
className={`icon ${groupOpen ? 'icon-rotate' : ''}`}
|
|
77
|
+
src={chevronDownSVG}
|
|
78
|
+
width="16px"
|
|
79
|
+
color={theme.colors.shade30}
|
|
80
|
+
/>
|
|
81
|
+
) : (
|
|
82
|
+
<IconWrapper src={lockSVG} width="14px" color="white" />
|
|
83
|
+
))}
|
|
84
|
+
</SidebarGroupHeaderContainer>
|
|
85
|
+
{children}
|
|
86
|
+
</SidebarGroupContainer>
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export default SidebarGroup
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import styled from 'styled-components'
|
|
2
|
+
import { theme } from '../../Themes'
|
|
3
|
+
|
|
4
|
+
interface SidebarGroupContainerProps {
|
|
5
|
+
open: boolean
|
|
6
|
+
groupOpen: boolean
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const SidebarGroupContainer = styled.div<SidebarGroupContainerProps>`
|
|
10
|
+
display: flex;
|
|
11
|
+
flex-direction: column;
|
|
12
|
+
cursor: pointer;
|
|
13
|
+
|
|
14
|
+
height: ${({ groupOpen, open }) => (groupOpen && open ? 'auto' : '40px')};
|
|
15
|
+
background-color: ${({ groupOpen, open }) => (groupOpen && open ? 'rgba(255, 255, 255, 0.1)' : 'inherit')};
|
|
16
|
+
overflow: hidden;
|
|
17
|
+
border-radius: 4px;
|
|
18
|
+
|
|
19
|
+
&:hover {
|
|
20
|
+
background-color: #192d53;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.wrapperTitle {
|
|
24
|
+
cursor: pointer;
|
|
25
|
+
min-height: ${({ groupOpen, open }) => (groupOpen && open ? '40px' : '54px')};
|
|
26
|
+
padding: 0 14px;
|
|
27
|
+
display: flex;
|
|
28
|
+
align-items: center;
|
|
29
|
+
height: 100%;
|
|
30
|
+
gap: 30px;
|
|
31
|
+
justify-content: left;
|
|
32
|
+
svg {
|
|
33
|
+
width: 20px;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
&.locked {
|
|
38
|
+
pointer-events: none;
|
|
39
|
+
}
|
|
40
|
+
`
|
|
41
|
+
|
|
42
|
+
interface SidebarGroupContainerHeaderProps {
|
|
43
|
+
sidebarOpen: boolean
|
|
44
|
+
groupOpen: boolean
|
|
45
|
+
disabled?: boolean
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export const SidebarGroupHeaderContainer = styled.div<SidebarGroupContainerHeaderProps>`
|
|
49
|
+
display: flex;
|
|
50
|
+
align-items: center;
|
|
51
|
+
min-height: ${({ groupOpen, sidebarOpen }) => (groupOpen && sidebarOpen ? '40px' : '40px')};
|
|
52
|
+
${({ groupOpen, sidebarOpen }) => groupOpen && sidebarOpen && 'border-bottom: 1px solid rgba(255, 255, 255, 0.1)'};
|
|
53
|
+
justify-content: ${sidebarOpen => (sidebarOpen ? 'space-between' : 'center')};
|
|
54
|
+
text-wrap: nowrap;
|
|
55
|
+
|
|
56
|
+
padding: 8px 12px;
|
|
57
|
+
cursor: pointer;
|
|
58
|
+
height: 100%;
|
|
59
|
+
gap: 24px;
|
|
60
|
+
|
|
61
|
+
color: ${({ disabled }) => (disabled ? 'rgba(255, 255, 255, 0.3)' : '#fff')};
|
|
62
|
+
|
|
63
|
+
.title {
|
|
64
|
+
width: 100%;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
svg {
|
|
68
|
+
width: 20px;
|
|
69
|
+
}
|
|
70
|
+
`
|
|
71
|
+
|
|
72
|
+
export const LockedModal = styled.div`
|
|
73
|
+
width: 400px;
|
|
74
|
+
height: 150px;
|
|
75
|
+
display: flex;
|
|
76
|
+
align-items: center;
|
|
77
|
+
justify-content: center;
|
|
78
|
+
text-align: center;
|
|
79
|
+
|
|
80
|
+
.item-locked {
|
|
81
|
+
color: ${theme.colors.honey40};
|
|
82
|
+
}
|
|
83
|
+
`
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { IconWrapper } from '../../IconWrapper'
|
|
3
|
+
import { Paragraph } from '../../Paragraph'
|
|
4
|
+
import { Bullet, Container } from './styles'
|
|
5
|
+
import { theme } from '../../Themes'
|
|
6
|
+
|
|
7
|
+
export interface SidebarItemProps {
|
|
8
|
+
disabled?: boolean
|
|
9
|
+
title: string
|
|
10
|
+
icon?: string
|
|
11
|
+
onClick: () => void
|
|
12
|
+
isCurrentItem: boolean
|
|
13
|
+
sidebarOpen: boolean
|
|
14
|
+
isInsideGroup?: boolean
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const SidebarItem = ({
|
|
18
|
+
title,
|
|
19
|
+
disabled,
|
|
20
|
+
icon,
|
|
21
|
+
isInsideGroup,
|
|
22
|
+
isCurrentItem,
|
|
23
|
+
sidebarOpen,
|
|
24
|
+
onClick,
|
|
25
|
+
}: SidebarItemProps) => {
|
|
26
|
+
let color = '#fff'
|
|
27
|
+
|
|
28
|
+
if (disabled) {
|
|
29
|
+
color = 'rgba(255,255,255,0.3)'
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (isCurrentItem) {
|
|
33
|
+
color = theme.colors.cyan30
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<Container
|
|
38
|
+
isCurrentItem={isCurrentItem}
|
|
39
|
+
disabled={disabled ?? false}
|
|
40
|
+
sidebarOpen={sidebarOpen}
|
|
41
|
+
isInsideGroup={isInsideGroup}
|
|
42
|
+
onClick={() => !disabled && onClick()}
|
|
43
|
+
>
|
|
44
|
+
{isInsideGroup ? (
|
|
45
|
+
<Bullet />
|
|
46
|
+
) : (
|
|
47
|
+
icon && (
|
|
48
|
+
<div className="icon">
|
|
49
|
+
<IconWrapper src={icon} color={color} />
|
|
50
|
+
</div>
|
|
51
|
+
)
|
|
52
|
+
)}
|
|
53
|
+
{sidebarOpen && (
|
|
54
|
+
<span className="title">
|
|
55
|
+
<Paragraph
|
|
56
|
+
size={!isInsideGroup ? 'medium' : 'small'}
|
|
57
|
+
/* eslint-disable-next-line no-nested-ternary */
|
|
58
|
+
color={disabled ? theme.colors.shade40 : !isInsideGroup ? 'white' : theme.colors.shade10}
|
|
59
|
+
heavyBod={isCurrentItem}
|
|
60
|
+
>
|
|
61
|
+
{title}
|
|
62
|
+
</Paragraph>
|
|
63
|
+
</span>
|
|
64
|
+
)}
|
|
65
|
+
</Container>
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export default SidebarItem
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import styled from 'styled-components'
|
|
2
|
+
|
|
3
|
+
interface SidebarItemContainerProps {
|
|
4
|
+
sidebarOpen: boolean
|
|
5
|
+
disabled: boolean
|
|
6
|
+
isCurrentItem: boolean
|
|
7
|
+
isInsideGroup?: boolean
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const Container = styled.div<SidebarItemContainerProps>`
|
|
11
|
+
display: flex;
|
|
12
|
+
justify-content: ${({ sidebarOpen }) => (sidebarOpen ? 'space-between' : 'center')};
|
|
13
|
+
align-items: center;
|
|
14
|
+
min-height: 38px;
|
|
15
|
+
padding: 8px;
|
|
16
|
+
gap: 24px;
|
|
17
|
+
|
|
18
|
+
color: ${({ disabled }) => (disabled ? 'rgba(255,255,255,0.4)' : '#fff')};
|
|
19
|
+
cursor: ${({ disabled }) => (disabled ? 'default' : 'pointer')};
|
|
20
|
+
border-radius: ${({ isInsideGroup }) => !isInsideGroup && '4px'};
|
|
21
|
+
&:hover {
|
|
22
|
+
background-color: #192d53;
|
|
23
|
+
}
|
|
24
|
+
&:not(:last-child) {
|
|
25
|
+
border-bottom: ${({ isInsideGroup }) => isInsideGroup && '1px solid rgba(255, 255, 255, 0.1)'};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.wrapper {
|
|
29
|
+
padding: 4px;
|
|
30
|
+
cursor: ${({ disabled }) => (disabled ? 'default' : 'pointer')};
|
|
31
|
+
display: flex;
|
|
32
|
+
align-items: center;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.title {
|
|
36
|
+
text-decoration: none;
|
|
37
|
+
width: 100%;
|
|
38
|
+
text-wrap: nowrap;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.lock {
|
|
42
|
+
position: absolute;
|
|
43
|
+
right: 20px;
|
|
44
|
+
svg {
|
|
45
|
+
opacity: 0.5;
|
|
46
|
+
position: absolute;
|
|
47
|
+
margin-bottom: 16px;
|
|
48
|
+
font-size: 10px;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
`
|
|
52
|
+
|
|
53
|
+
export const Bullet = styled.div`
|
|
54
|
+
position: relative;
|
|
55
|
+
width: 24px;
|
|
56
|
+
height: 100%;
|
|
57
|
+
|
|
58
|
+
&::after {
|
|
59
|
+
position: absolute;
|
|
60
|
+
background: #fff;
|
|
61
|
+
|
|
62
|
+
right: 0px;
|
|
63
|
+
top: -2.5px;
|
|
64
|
+
content: '';
|
|
65
|
+
width: 5px;
|
|
66
|
+
height: 5px;
|
|
67
|
+
|
|
68
|
+
border-radius: 50%;
|
|
69
|
+
}
|
|
70
|
+
`
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { chevronLeftSVG, chevronRightSVG } from '../../../assets/icon'
|
|
3
|
+
import { IconWrapper } from '../../IconWrapper'
|
|
4
|
+
import { SidebarLogoContainer } from './styles'
|
|
5
|
+
|
|
6
|
+
export interface SidebarLogoProps {
|
|
7
|
+
sidebarOpen: boolean
|
|
8
|
+
logoOpacity?: number
|
|
9
|
+
setSidebarOpen: (value: boolean) => void
|
|
10
|
+
logoUrl: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const SidebarLogo = ({ sidebarOpen, setSidebarOpen, logoOpacity, logoUrl }: SidebarLogoProps) => {
|
|
14
|
+
return (
|
|
15
|
+
<SidebarLogoContainer logoUrl={logoUrl} logoOpacity={logoOpacity} sidebarOpen={sidebarOpen}>
|
|
16
|
+
<button type="button" onClick={() => setSidebarOpen(!sidebarOpen)} className="close" aria-label="Fechar menu">
|
|
17
|
+
<IconWrapper src={sidebarOpen ? chevronRightSVG : chevronLeftSVG} color="#fff" width="12px" height="12px" />
|
|
18
|
+
</button>
|
|
19
|
+
<div className="logo" />
|
|
20
|
+
</SidebarLogoContainer>
|
|
21
|
+
)
|
|
22
|
+
}
|
|
23
|
+
export default SidebarLogo
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import styled from 'styled-components'
|
|
2
|
+
|
|
3
|
+
interface SidebarLogoContainerProps {
|
|
4
|
+
logoOpacity?: number
|
|
5
|
+
sidebarOpen: boolean
|
|
6
|
+
logoUrl: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const SidebarLogoContainer = styled.div<SidebarLogoContainerProps>`
|
|
10
|
+
position: relative;
|
|
11
|
+
|
|
12
|
+
--padding: 8px;
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
margin-bottom: 32px;
|
|
16
|
+
|
|
17
|
+
.logo {
|
|
18
|
+
height: 40px;
|
|
19
|
+
|
|
20
|
+
background: url(${({ logoUrl }) => logoUrl});
|
|
21
|
+
background-size: ${({ sidebarOpen: open }) => (open ? `202px 40px` : '141px 28px')};
|
|
22
|
+
background-repeat: no-repeat;
|
|
23
|
+
background-position: -8px 0px;
|
|
24
|
+
|
|
25
|
+
opacity: ${({ logoOpacity }) => logoOpacity ?? 1};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.close {
|
|
29
|
+
--width: 28px;
|
|
30
|
+
--height: 32px;
|
|
31
|
+
display: flex;
|
|
32
|
+
position: absolute;
|
|
33
|
+
|
|
34
|
+
align-items: center;
|
|
35
|
+
justify-content: center;
|
|
36
|
+
|
|
37
|
+
height: var(--height);
|
|
38
|
+
width: var(--width);
|
|
39
|
+
|
|
40
|
+
background: #001640;
|
|
41
|
+
color: #fff;
|
|
42
|
+
|
|
43
|
+
border-radius: 0 var(--width) var(--width) 0;
|
|
44
|
+
|
|
45
|
+
top: 42px;
|
|
46
|
+
right: calc((var(--padding) + var(--width)) * -1);
|
|
47
|
+
|
|
48
|
+
border: none;
|
|
49
|
+
cursor: pointer;
|
|
50
|
+
|
|
51
|
+
z-index: 2;
|
|
52
|
+
}
|
|
53
|
+
`
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { useState } from 'react'
|
|
2
|
+
import SidebarGroup from './SidebarGroup'
|
|
3
|
+
import SidebarItem from './SidebarItem'
|
|
4
|
+
import SidebarLogo from './SidebarLogo'
|
|
5
|
+
|
|
6
|
+
import React from 'react'
|
|
7
|
+
import { SidebarContainer, SidebarScrollContainer, SidebarScrollWrapperContainer, SidebarSpacer } from './styles'
|
|
8
|
+
|
|
9
|
+
export interface SidebarLayoutItemTypeItem {
|
|
10
|
+
id: string
|
|
11
|
+
type: 'item'
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface SidebarLayoutItemTypeGroup {
|
|
15
|
+
id: string
|
|
16
|
+
type: 'group'
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface SidebarLayoutItemTypeSpacer {
|
|
20
|
+
type: 'spacer'
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface SidebarItemData {
|
|
24
|
+
showInMenu?: boolean
|
|
25
|
+
menuTitle: string
|
|
26
|
+
icon: string
|
|
27
|
+
disabled: boolean
|
|
28
|
+
link?: string
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface SidebarGroupData {
|
|
32
|
+
name: string
|
|
33
|
+
link?: string
|
|
34
|
+
icon: string
|
|
35
|
+
itemIds: string[]
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface SidebarProps {
|
|
39
|
+
layout: (SidebarLayoutItemTypeItem | SidebarLayoutItemTypeGroup | SidebarLayoutItemTypeSpacer)[]
|
|
40
|
+
currentItemId: string
|
|
41
|
+
currentGroupId: string
|
|
42
|
+
setLink: (link: string) => void
|
|
43
|
+
items: Record<string, SidebarItemData>
|
|
44
|
+
groups: Record<string, SidebarGroupData>
|
|
45
|
+
logoUrl: string
|
|
46
|
+
logoOpacity?: number
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export const Sidebar = ({
|
|
50
|
+
logoUrl,
|
|
51
|
+
layout,
|
|
52
|
+
currentItemId,
|
|
53
|
+
currentGroupId,
|
|
54
|
+
setLink,
|
|
55
|
+
items,
|
|
56
|
+
logoOpacity,
|
|
57
|
+
groups,
|
|
58
|
+
}: SidebarProps) => {
|
|
59
|
+
const [sidebarOpen, setSidebarOpen] = useState(true)
|
|
60
|
+
|
|
61
|
+
const sidebarItemClickHandler = (itemId: string) => {
|
|
62
|
+
const item = items[itemId]
|
|
63
|
+
const link = item?.link
|
|
64
|
+
if (link) setLink(link)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
<SidebarContainer sidebarOpen={sidebarOpen}>
|
|
69
|
+
<SidebarLogo
|
|
70
|
+
logoUrl={logoUrl}
|
|
71
|
+
sidebarOpen={sidebarOpen}
|
|
72
|
+
setSidebarOpen={setSidebarOpen}
|
|
73
|
+
logoOpacity={logoOpacity}
|
|
74
|
+
/>
|
|
75
|
+
<SidebarScrollWrapperContainer>
|
|
76
|
+
<SidebarScrollContainer>
|
|
77
|
+
{layout
|
|
78
|
+
.map((item, index) => ({ id: index, disabled: undefined, ...item }))
|
|
79
|
+
.map(item => {
|
|
80
|
+
if (item.type === 'spacer') return <SidebarSpacer key={item.id} />
|
|
81
|
+
if (item.type === 'item')
|
|
82
|
+
return (
|
|
83
|
+
<SidebarItem
|
|
84
|
+
key={item.id}
|
|
85
|
+
title={items[item.id]?.menuTitle}
|
|
86
|
+
icon={items[item.id]?.icon}
|
|
87
|
+
disabled={items[item.id]?.disabled}
|
|
88
|
+
onClick={() => sidebarItemClickHandler(item.id)}
|
|
89
|
+
sidebarOpen={sidebarOpen}
|
|
90
|
+
isCurrentItem={currentItemId === item.id}
|
|
91
|
+
/>
|
|
92
|
+
)
|
|
93
|
+
if (item.type === 'group') {
|
|
94
|
+
const group = groups[item.id]
|
|
95
|
+
return (
|
|
96
|
+
<SidebarGroup
|
|
97
|
+
disabled={group.itemIds.every(id => items[id]?.disabled)}
|
|
98
|
+
key={item.id}
|
|
99
|
+
title={group.name}
|
|
100
|
+
icon={group.icon}
|
|
101
|
+
sidebarOpen={sidebarOpen}
|
|
102
|
+
onClick={() => group.link && setLink(group.link)}
|
|
103
|
+
isCurrentGroup={currentGroupId === item.id}
|
|
104
|
+
>
|
|
105
|
+
{group.itemIds
|
|
106
|
+
.filter(id => items[id]?.showInMenu !== false)
|
|
107
|
+
.map(id => (
|
|
108
|
+
<SidebarItem
|
|
109
|
+
key={id}
|
|
110
|
+
title={items[id]?.menuTitle}
|
|
111
|
+
disabled={items[id]?.disabled}
|
|
112
|
+
onClick={() => sidebarItemClickHandler(id)}
|
|
113
|
+
isCurrentItem={currentItemId === id}
|
|
114
|
+
sidebarOpen={sidebarOpen}
|
|
115
|
+
isInsideGroup
|
|
116
|
+
/>
|
|
117
|
+
))}
|
|
118
|
+
</SidebarGroup>
|
|
119
|
+
)
|
|
120
|
+
}
|
|
121
|
+
return null
|
|
122
|
+
})}
|
|
123
|
+
</SidebarScrollContainer>
|
|
124
|
+
</SidebarScrollWrapperContainer>
|
|
125
|
+
</SidebarContainer>
|
|
126
|
+
)
|
|
127
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { Meta, StoryObj } from '@storybook/react'
|
|
3
|
+
import { Sidebar } from './index'
|
|
4
|
+
import { action } from '@storybook/addon-actions'
|
|
5
|
+
import { dashboardSVG } from '../../assets/icon'
|
|
6
|
+
|
|
7
|
+
const meta: Meta<typeof Sidebar> = {
|
|
8
|
+
title: 'Components/Sidebar',
|
|
9
|
+
tags: ['autodocs'],
|
|
10
|
+
|
|
11
|
+
component: Sidebar,
|
|
12
|
+
} satisfies Meta<typeof Sidebar>
|
|
13
|
+
|
|
14
|
+
export default meta
|
|
15
|
+
|
|
16
|
+
type Story = StoryObj<typeof Sidebar>
|
|
17
|
+
|
|
18
|
+
export const Playground: Story = {
|
|
19
|
+
render: () => {
|
|
20
|
+
return (
|
|
21
|
+
<Sidebar
|
|
22
|
+
layout={[
|
|
23
|
+
{
|
|
24
|
+
type: 'item',
|
|
25
|
+
id: 'DASHBOARD',
|
|
26
|
+
},
|
|
27
|
+
{ type: 'group', id: 'group1' },
|
|
28
|
+
]}
|
|
29
|
+
items={{
|
|
30
|
+
DASHBOARD: {
|
|
31
|
+
menuTitle: 'Dashboard',
|
|
32
|
+
icon: dashboardSVG,
|
|
33
|
+
link: '/dashboard',
|
|
34
|
+
disabled: false,
|
|
35
|
+
},
|
|
36
|
+
}}
|
|
37
|
+
currentItemId={''}
|
|
38
|
+
currentGroupId={''}
|
|
39
|
+
setLink={action('setLink')}
|
|
40
|
+
groups={{
|
|
41
|
+
group1: {
|
|
42
|
+
name: 'Group 1',
|
|
43
|
+
icon: dashboardSVG,
|
|
44
|
+
itemIds: ['DASHBOARD'],
|
|
45
|
+
},
|
|
46
|
+
}}
|
|
47
|
+
logoUrl={''}
|
|
48
|
+
/>
|
|
49
|
+
)
|
|
50
|
+
},
|
|
51
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import styled from 'styled-components'
|
|
2
|
+
import { theme } from '../Themes'
|
|
3
|
+
|
|
4
|
+
interface SidebarContainerProps {
|
|
5
|
+
sidebarOpen: boolean
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const SidebarContainer = styled.div<SidebarContainerProps>`
|
|
9
|
+
display: flex;
|
|
10
|
+
flex-direction: column;
|
|
11
|
+
grid-area: sidebar;
|
|
12
|
+
position: relative;
|
|
13
|
+
box-sizing: border-box;
|
|
14
|
+
color: white;
|
|
15
|
+
background: ${theme.colors.cyan50};
|
|
16
|
+
height: 100vh;
|
|
17
|
+
height: 100dvh;
|
|
18
|
+
padding: 16px 16px 0 16px;
|
|
19
|
+
width: min-content;
|
|
20
|
+
border-radius: ${({ sidebarOpen }) => sidebarOpen && '0 60px 0px 0;'};
|
|
21
|
+
|
|
22
|
+
/* Mantenha o estilo do container ao passar o mouse */
|
|
23
|
+
&:hover {
|
|
24
|
+
&::-webkit-scrollbar {
|
|
25
|
+
width: 0;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
&::-webkit-scrollbar-track {
|
|
29
|
+
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
&::-webkit-scrollbar-thumb {
|
|
33
|
+
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.5);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
`
|
|
37
|
+
|
|
38
|
+
export const SidebarScrollContainer = styled.div`
|
|
39
|
+
display: flex;
|
|
40
|
+
flex-direction: column;
|
|
41
|
+
gap: 16px;
|
|
42
|
+
min-height: calc(100% - 36px);
|
|
43
|
+
margin-bottom: 36px;
|
|
44
|
+
`
|
|
45
|
+
|
|
46
|
+
export const SidebarScrollWrapperContainer = styled.div`
|
|
47
|
+
height: 100%;
|
|
48
|
+
overflow-y: auto;
|
|
49
|
+
overflow-x: hidden;
|
|
50
|
+
|
|
51
|
+
&::-webkit-scrollbar {
|
|
52
|
+
width: 0;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
&::-webkit-scrollbar-track {
|
|
56
|
+
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
&::-webkit-scrollbar-thumb {
|
|
60
|
+
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.5);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
&:hover {
|
|
64
|
+
&::-webkit-scrollbar {
|
|
65
|
+
width: 0;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
&::-webkit-scrollbar-track {
|
|
69
|
+
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
&::-webkit-scrollbar-thumb {
|
|
73
|
+
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.5);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
`
|
|
77
|
+
|
|
78
|
+
export const SidebarSpacer = styled.div`
|
|
79
|
+
flex-grow: 1;
|
|
80
|
+
`
|
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
import { ButtonHTMLAttributes, HTMLAttributes, ReactNode } from 'react'
|
|
2
2
|
import type { DatePickerProps } from '../DatePicker'
|
|
3
3
|
import type { DateRangePickerProps } from '../DateRangePicker'
|
|
4
|
-
import type { SelectProps } from '../Select'
|
|
5
|
-
import type { SelectRef } from '../Select'
|
|
6
4
|
import type { PaginateProps } from '../Paginate'
|
|
5
|
+
import type { SelectProps, SelectRef } from '../Select'
|
|
6
|
+
import type {
|
|
7
|
+
SidebarGroupData,
|
|
8
|
+
SidebarItemData,
|
|
9
|
+
SidebarLayoutItemTypeGroup,
|
|
10
|
+
SidebarLayoutItemTypeItem,
|
|
11
|
+
SidebarLayoutItemTypeSpacer,
|
|
12
|
+
SidebarProps,
|
|
13
|
+
} from '../Sidebar'
|
|
14
|
+
|
|
7
15
|
type ColorsVariant = 'primary' | 'secondary' | 'success' | 'danger' | 'gray' | 'warning' | 'yellow'
|
|
8
16
|
type ColorsVariantBanner = 'notice' | 'success' | 'danger' | 'warning'
|
|
9
17
|
|
|
@@ -53,4 +61,16 @@ export interface TabsTypes extends HTMLAttributes<HTMLDivElement> {
|
|
|
53
61
|
onTabChange?: (activeTab: string) => void
|
|
54
62
|
}
|
|
55
63
|
|
|
56
|
-
export {
|
|
64
|
+
export {
|
|
65
|
+
DatePickerProps,
|
|
66
|
+
DateRangePickerProps,
|
|
67
|
+
PaginateProps,
|
|
68
|
+
SelectProps,
|
|
69
|
+
SelectRef,
|
|
70
|
+
SidebarGroupData,
|
|
71
|
+
SidebarItemData,
|
|
72
|
+
SidebarLayoutItemTypeGroup,
|
|
73
|
+
SidebarLayoutItemTypeItem,
|
|
74
|
+
SidebarLayoutItemTypeSpacer,
|
|
75
|
+
SidebarProps,
|
|
76
|
+
}
|