w-design-system 0.0.28 → 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/.storybook/preview.ts +1 -0
- package/dist/components/DateRangePicker/styles.d.ts.map +1 -1
- package/dist/components/DateRangePicker/styles.js +0 -1
- package/dist/components/DateRangePicker/styles.js.map +1 -1
- package/dist/components/Input/styles.d.ts.map +1 -1
- package/dist/components/Input/styles.js +29 -13
- package/dist/components/Input/styles.js.map +1 -1
- package/dist/components/Select/styles.d.ts.map +1 -1
- package/dist/components/Select/styles.js +17 -9
- package/dist/components/Select/styles.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/src/globalStyles.d.ts.map +1 -1
- package/dist/src/globalStyles.js +6 -1
- package/dist/src/globalStyles.js.map +1 -1
- 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/DateRangePicker/styles.ts +0 -1
- package/src/components/Input/index.tsx +22 -5
- package/src/components/Input/styles.ts +29 -13
- package/src/components/Select/index.tsx +36 -19
- package/src/components/Select/stories.tsx +3 -2
- package/src/components/Select/styles.ts +33 -13
- 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
- package/src/index.tsx +2 -2
- package/src/reset.css +6 -0
- package/src/src/globalStyles.ts +9 -4
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { HTMLAttributes, useEffect, useMemo, useRef, useState } from 'react'
|
|
2
2
|
import { calendarSVG, chevronLeftSVG, chevronRightSVG } from '../../assets/icon'
|
|
3
3
|
import { IconWrapper } from '../IconWrapper'
|
|
4
|
-
import { Select, SelectRef } from '../Select'
|
|
4
|
+
import { Select, SelectProps, SelectRef } from '../Select'
|
|
5
5
|
import { theme } from '../Themes'
|
|
6
6
|
import {
|
|
7
7
|
CalendarDay,
|
|
@@ -12,15 +12,19 @@ import {
|
|
|
12
12
|
CalendarWeekDayLabel,
|
|
13
13
|
} from './styles'
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
type GetElementType<T> = T extends (infer U)[] ? U : never
|
|
16
|
+
|
|
17
|
+
export interface DateRangePickerProps<
|
|
18
|
+
T extends GetElementType<SelectProps['options']> = GetElementType<SelectProps['options']>,
|
|
19
|
+
> extends HTMLAttributes<HTMLDivElement> {
|
|
16
20
|
isRange?: boolean
|
|
17
21
|
placeholder?: string
|
|
18
22
|
label?: string
|
|
19
|
-
options?:
|
|
20
|
-
selectedOption?:
|
|
23
|
+
options?: T[]
|
|
24
|
+
selectedOption?: T | null
|
|
21
25
|
readonly?: boolean
|
|
22
26
|
disabled?: boolean
|
|
23
|
-
onSelectedOptionChange?: (option:
|
|
27
|
+
onSelectedOptionChange?: (option: T | null) => void
|
|
24
28
|
startCustomDate: Date | null
|
|
25
29
|
endCustomDate: Date | null
|
|
26
30
|
onSelectedCustomRange: (start: Date | null, end: Date | null) => void
|
|
@@ -59,7 +63,7 @@ const monthNames = [
|
|
|
59
63
|
const weekDays = ['DOM', 'SEG', 'TER', 'QUA', 'QUI', 'SEX', 'SAB']
|
|
60
64
|
|
|
61
65
|
type DayType = EmptyDay | Day
|
|
62
|
-
export const DateRangePicker
|
|
66
|
+
export const DateRangePicker = <T extends GetElementType<SelectProps['options']>>({
|
|
63
67
|
label,
|
|
64
68
|
options,
|
|
65
69
|
selectedOption,
|
|
@@ -70,7 +74,7 @@ export const DateRangePicker: React.FC<DateRangePickerProps> = ({
|
|
|
70
74
|
endCustomDate,
|
|
71
75
|
isRange,
|
|
72
76
|
...props
|
|
73
|
-
}) => {
|
|
77
|
+
}: DateRangePickerProps<T>) => {
|
|
74
78
|
const [currentYear, setCurrentYear] = useState(0)
|
|
75
79
|
const [currentMonth, setCurrentMonth] = useState(0)
|
|
76
80
|
const [currentMouseHoverDate, setCurrentMouseHoverDate] = useState<Date | null>(null)
|
|
@@ -90,7 +94,7 @@ export const DateRangePicker: React.FC<DateRangePickerProps> = ({
|
|
|
90
94
|
setCurrentMonth(newDate.getMonth())
|
|
91
95
|
}
|
|
92
96
|
|
|
93
|
-
const onSelectedOptionChangeHandler = (option:
|
|
97
|
+
const onSelectedOptionChangeHandler = (option: T | null) => {
|
|
94
98
|
if (onSelectedOptionChange) {
|
|
95
99
|
onSelectedOptionChange(option)
|
|
96
100
|
if (onSelectedCustomRange) onSelectedCustomRange(null, null)
|
|
@@ -171,17 +175,22 @@ export const DateRangePicker: React.FC<DateRangePickerProps> = ({
|
|
|
171
175
|
}, [selectedOption, firstDate, secondDate])
|
|
172
176
|
|
|
173
177
|
placeholder = placeholder ?? 'Selecione um período'
|
|
174
|
-
const {
|
|
178
|
+
const { customOption, optionComputed } = useMemo(() => {
|
|
175
179
|
if (options && options.length > 0) {
|
|
176
180
|
const customOptionId = Math.max(...options.map(option => option.id ?? 0)) + 1
|
|
177
|
-
const
|
|
178
|
-
|
|
181
|
+
const customOption = {
|
|
182
|
+
text: 'Período Personalizado',
|
|
183
|
+
id: customOptionId,
|
|
184
|
+
} as T
|
|
185
|
+
const optionComputed = [...options, customOption]
|
|
186
|
+
return { customOption, optionComputed }
|
|
179
187
|
}
|
|
180
|
-
return {
|
|
181
|
-
}, [
|
|
188
|
+
return { customOption: null, optionComputed: [] }
|
|
189
|
+
}, [options])
|
|
182
190
|
|
|
183
191
|
return (
|
|
184
|
-
<Select
|
|
192
|
+
<Select<T>
|
|
193
|
+
dropDownWidth="284px"
|
|
185
194
|
ref={selectRef}
|
|
186
195
|
placeholder={placeholder}
|
|
187
196
|
label={label}
|
|
@@ -190,7 +199,7 @@ export const DateRangePicker: React.FC<DateRangePickerProps> = ({
|
|
|
190
199
|
<IconWrapper className="icon" src={calendarSVG} width="20px" height="20px" color={theme.colors.shade30} />
|
|
191
200
|
}
|
|
192
201
|
options={optionComputed}
|
|
193
|
-
selectedOption={selectedOption ?? (firstDate ?
|
|
202
|
+
selectedOption={selectedOption ?? (firstDate ? customOption : null)}
|
|
194
203
|
onOptionChange={onSelectedOptionChangeHandler}
|
|
195
204
|
{...props}
|
|
196
205
|
>
|
|
@@ -23,7 +23,7 @@ export default meta
|
|
|
23
23
|
type Story = StoryObj<typeof DateRangePicker>
|
|
24
24
|
|
|
25
25
|
const ComponentWrapper = ({ options, ...args }: Partial<DateRangePickerProps>) => {
|
|
26
|
-
const [selectedOption, setSelectOption] = React.useState<
|
|
26
|
+
const [selectedOption, setSelectOption] = React.useState<DateRangePickerProps['selectedOption']>(null)
|
|
27
27
|
const [startDate, setStartDate] = React.useState<Date | null>(null)
|
|
28
28
|
const [endDate, setEndDate] = React.useState<Date | null>(null)
|
|
29
29
|
|
|
@@ -59,7 +59,6 @@ export const CalendarDay = styled.div<CalendarDayProps>`
|
|
|
59
59
|
cursor: pointer;
|
|
60
60
|
width: 36px;
|
|
61
61
|
height: 36px;
|
|
62
|
-
box-sizing: border-box;
|
|
63
62
|
color: ${({ isFirstSelected, isLastSelected }) =>
|
|
64
63
|
isFirstSelected || isLastSelected ? theme.colors.white : theme.colors.shade50};
|
|
65
64
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { HTMLAttributes, ReactNode, useEffect, useId, useRef } from 'react'
|
|
1
|
+
import React, { HTMLAttributes, ReactNode, useCallback, useEffect, useId, useRef } from 'react'
|
|
2
2
|
import { InputWrapper, StyledInput, StyledInputBorder, StyledInputContent, StyledLabel } from './styles'
|
|
3
3
|
|
|
4
4
|
export interface InputProps extends HTMLAttributes<HTMLDivElement> {
|
|
@@ -53,7 +53,7 @@ export const Input: React.FC<InputProps> = ({
|
|
|
53
53
|
const labelRef = useRef<HTMLLabelElement>(null)
|
|
54
54
|
const inputRef = useRef<HTMLInputElement>(null)
|
|
55
55
|
|
|
56
|
-
const updatePositions = () => {
|
|
56
|
+
const updatePositions = useCallback(() => {
|
|
57
57
|
if (contentRef.current) {
|
|
58
58
|
setContentPosition({
|
|
59
59
|
x: 0,
|
|
@@ -70,7 +70,7 @@ export const Input: React.FC<InputProps> = ({
|
|
|
70
70
|
height: labelRef.current.offsetHeight,
|
|
71
71
|
})
|
|
72
72
|
}
|
|
73
|
-
}
|
|
73
|
+
}, [])
|
|
74
74
|
|
|
75
75
|
const onClickHandler = () => {
|
|
76
76
|
inputRef.current?.focus()
|
|
@@ -79,9 +79,26 @@ export const Input: React.FC<InputProps> = ({
|
|
|
79
79
|
useEffect(() => {
|
|
80
80
|
const handler = () => updatePositions()
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
let resizeObserver: ResizeObserver | null = null
|
|
83
|
+
if (ResizeObserver) {
|
|
84
|
+
resizeObserver = new ResizeObserver(handler)
|
|
85
|
+
if (contentRef.current) {
|
|
86
|
+
resizeObserver.observe(contentRef.current)
|
|
87
|
+
}
|
|
88
|
+
if (labelRef.current) {
|
|
89
|
+
resizeObserver.observe(labelRef.current)
|
|
90
|
+
}
|
|
91
|
+
} else {
|
|
92
|
+
window.addEventListener('resize', handler)
|
|
93
|
+
}
|
|
83
94
|
|
|
84
|
-
return () =>
|
|
95
|
+
return () => {
|
|
96
|
+
if (resizeObserver) {
|
|
97
|
+
resizeObserver.disconnect()
|
|
98
|
+
} else {
|
|
99
|
+
window.removeEventListener('resize', handler)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
85
102
|
}, [])
|
|
86
103
|
|
|
87
104
|
useEffect(() => {
|
|
@@ -8,38 +8,46 @@ export const InputWrapper = styled.div`
|
|
|
8
8
|
|
|
9
9
|
export const StyledInput = styled.input`
|
|
10
10
|
width: 100%;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
font-size: ${theme.paragraph.medium};
|
|
11
|
+
|
|
12
|
+
background-color: transparent;
|
|
14
13
|
border: none;
|
|
14
|
+
outline: none;
|
|
15
|
+
|
|
15
16
|
color: ${theme.colors.shade50};
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
|
|
18
|
+
font-family: ${theme.fonts.join(', ')};
|
|
19
|
+
font-size: ${theme.paragraph.medium};
|
|
20
|
+
|
|
18
21
|
&:active,
|
|
19
22
|
&:focus {
|
|
20
23
|
outline: none;
|
|
21
24
|
border: none;
|
|
22
25
|
}
|
|
26
|
+
|
|
23
27
|
&::placeholder {
|
|
24
28
|
color: ${theme.colors.shade30};
|
|
25
29
|
font-size: ${theme.paragraph.medium};
|
|
26
30
|
opacity: 1;
|
|
27
31
|
}
|
|
32
|
+
|
|
28
33
|
&:disabled {
|
|
29
|
-
background-color: transparent
|
|
34
|
+
background-color: transparent;
|
|
30
35
|
cursor: not-allowed;
|
|
31
36
|
}
|
|
32
37
|
`
|
|
33
38
|
|
|
34
39
|
export const StyledLabel = styled.label`
|
|
35
40
|
position: absolute;
|
|
41
|
+
color: ${theme.colors.shade50};
|
|
42
|
+
|
|
43
|
+
box-sizing: content-box;
|
|
36
44
|
left: 15px;
|
|
37
45
|
top: -8px;
|
|
38
46
|
padding: 0 1px;
|
|
39
|
-
|
|
40
|
-
font-size: ${theme.paragraph.small};
|
|
41
|
-
color: ${theme.colors.shade50};
|
|
47
|
+
|
|
42
48
|
font-family: 'Nunito Sans', sans-serif;
|
|
49
|
+
font-size: ${theme.paragraph.small};
|
|
50
|
+
font-weight: 600;
|
|
43
51
|
text-wrap: nowrap;
|
|
44
52
|
`
|
|
45
53
|
|
|
@@ -49,16 +57,21 @@ export interface StyledInputContentProps {
|
|
|
49
57
|
|
|
50
58
|
export const StyledInputContent = styled.div<StyledInputContentProps>`
|
|
51
59
|
position: relative;
|
|
52
|
-
|
|
60
|
+
|
|
53
61
|
display: flex;
|
|
54
|
-
gap: 8px;
|
|
55
|
-
height: 22px;
|
|
56
62
|
align-items: center;
|
|
57
63
|
justify-content: space-between;
|
|
64
|
+
|
|
65
|
+
box-sizing: content-box;
|
|
66
|
+
padding: 18px 16px 14px 16px;
|
|
67
|
+
height: 22px;
|
|
68
|
+
gap: 8px;
|
|
58
69
|
border-radius: 4px;
|
|
59
|
-
|
|
70
|
+
|
|
60
71
|
background-color: ${({ disabled }: StyledInputContentProps) =>
|
|
61
72
|
disabled ? theme.colors.shade10 : theme.colors.white};
|
|
73
|
+
|
|
74
|
+
cursor: ${({ disabled }: StyledInputContentProps) => (disabled ? 'not-allowed' : 'pointer')};
|
|
62
75
|
`
|
|
63
76
|
|
|
64
77
|
export interface StyledInputBorderProps {
|
|
@@ -73,9 +86,12 @@ export interface StyledInputBorderProps {
|
|
|
73
86
|
*/
|
|
74
87
|
export const StyledInputBorder = styled.div<StyledInputBorderProps>`
|
|
75
88
|
position: absolute;
|
|
89
|
+
|
|
90
|
+
box-sizing: content-box;
|
|
76
91
|
inset: 0px;
|
|
77
92
|
border-radius: 4px;
|
|
78
93
|
border: 1px solid ${theme.colors.shade20};
|
|
94
|
+
|
|
79
95
|
clip-rule: evenodd;
|
|
80
96
|
clip-path: path(
|
|
81
97
|
'${({ label, content }) =>
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import React, {
|
|
2
|
-
FC,
|
|
3
2
|
HTMLAttributes,
|
|
4
3
|
PropsWithChildren,
|
|
5
4
|
ReactNode,
|
|
@@ -7,33 +6,49 @@ import React, {
|
|
|
7
6
|
useImperativeHandle,
|
|
8
7
|
useMemo,
|
|
9
8
|
useRef,
|
|
10
|
-
useState
|
|
9
|
+
useState
|
|
11
10
|
} from 'react'
|
|
12
11
|
import { chevronDownSVG } from '../../assets/icon'
|
|
13
12
|
import { IconWrapper } from '../IconWrapper'
|
|
14
13
|
import { Input } from '../Input'
|
|
15
14
|
import { theme } from '../Themes'
|
|
16
|
-
import {
|
|
15
|
+
import { Dropdown, DropdownWrapper, OptionButton, OptionText, SelectWrapper } from './styles'
|
|
17
16
|
|
|
18
|
-
export interface SelectProps
|
|
17
|
+
export interface SelectProps<
|
|
18
|
+
T extends { text: string; id: number; icon?: ReactNode } = { text: string; id: number; icon?: ReactNode },
|
|
19
|
+
> extends HTMLAttributes<HTMLDivElement> {
|
|
19
20
|
disabled?: boolean
|
|
20
21
|
label?: string
|
|
21
22
|
leftIcon?: ReactNode
|
|
22
|
-
onOptionChange?: (option:
|
|
23
|
-
options?:
|
|
23
|
+
onOptionChange?: (option: T | null) => void
|
|
24
|
+
options?: T[]
|
|
24
25
|
placeholder?: string
|
|
25
26
|
rightIcon?: ReactNode
|
|
26
|
-
selectedOption?:
|
|
27
|
+
selectedOption?: T | null
|
|
27
28
|
value?: string
|
|
29
|
+
dropDownTop?: boolean
|
|
30
|
+
dropDownWidth?: string
|
|
28
31
|
}
|
|
29
32
|
|
|
30
33
|
export interface SelectRef {
|
|
31
34
|
setOpen(open: boolean): void
|
|
32
35
|
}
|
|
33
36
|
|
|
34
|
-
const SelectFowardRef
|
|
35
|
-
{
|
|
36
|
-
|
|
37
|
+
const SelectFowardRef = <T extends { text: string; id: number; icon?: ReactNode }>(
|
|
38
|
+
{
|
|
39
|
+
children,
|
|
40
|
+
label,
|
|
41
|
+
leftIcon,
|
|
42
|
+
onOptionChange,
|
|
43
|
+
options,
|
|
44
|
+
placeholder,
|
|
45
|
+
rightIcon,
|
|
46
|
+
selectedOption,
|
|
47
|
+
value,
|
|
48
|
+
dropDownWidth,
|
|
49
|
+
...props
|
|
50
|
+
}: PropsWithChildren<SelectProps<T>>,
|
|
51
|
+
ref: React.ForwardedRef<SelectRef>,
|
|
37
52
|
) => {
|
|
38
53
|
const [open, setOpen] = useState(false)
|
|
39
54
|
const wrapperRef = useRef<HTMLDivElement>(null)
|
|
@@ -41,7 +56,7 @@ const SelectFowardRef: React.ForwardRefRenderFunction<SelectRef, SelectProps> =
|
|
|
41
56
|
useImperativeHandle(ref, () => ({
|
|
42
57
|
setOpen,
|
|
43
58
|
}))
|
|
44
|
-
|
|
59
|
+
|
|
45
60
|
useEffect(() => {
|
|
46
61
|
if (!open) return
|
|
47
62
|
const handler = (event: MouseEvent) => {
|
|
@@ -61,7 +76,7 @@ const SelectFowardRef: React.ForwardRefRenderFunction<SelectRef, SelectProps> =
|
|
|
61
76
|
}
|
|
62
77
|
}, [open])
|
|
63
78
|
|
|
64
|
-
const onOptionClick = (option:
|
|
79
|
+
const onOptionClick = (option: T) => {
|
|
65
80
|
onOptionChange && onOptionChange(option)
|
|
66
81
|
setOpen(false)
|
|
67
82
|
}
|
|
@@ -69,8 +84,7 @@ const SelectFowardRef: React.ForwardRefRenderFunction<SelectRef, SelectProps> =
|
|
|
69
84
|
const valueComputed = useMemo(() => {
|
|
70
85
|
if (value) return value
|
|
71
86
|
if (selectedOption) {
|
|
72
|
-
|
|
73
|
-
return option?.text
|
|
87
|
+
return selectedOption.text
|
|
74
88
|
}
|
|
75
89
|
return ''
|
|
76
90
|
}, [value, selectedOption, options])
|
|
@@ -98,16 +112,17 @@ const SelectFowardRef: React.ForwardRefRenderFunction<SelectRef, SelectProps> =
|
|
|
98
112
|
/>
|
|
99
113
|
{open && (
|
|
100
114
|
<DropdownWrapper>
|
|
101
|
-
<Dropdown>
|
|
115
|
+
<Dropdown dropDownTop={props.dropDownTop} dropDownWidth={dropDownWidth}>
|
|
102
116
|
{(options?.length ?? 0) > 0 && (
|
|
103
117
|
<div>
|
|
104
118
|
{options?.map(option => (
|
|
105
119
|
<OptionButton
|
|
106
|
-
selected={
|
|
120
|
+
selected={selectedOption?.id === option.id}
|
|
107
121
|
key={option.id}
|
|
108
|
-
onClick={() => onOptionClick(option
|
|
122
|
+
onClick={() => onOptionClick(option)}
|
|
109
123
|
>
|
|
110
|
-
{option.
|
|
124
|
+
{option.icon}
|
|
125
|
+
<OptionText>{option.text}</OptionText>
|
|
111
126
|
</OptionButton>
|
|
112
127
|
))}
|
|
113
128
|
</div>
|
|
@@ -120,4 +135,6 @@ const SelectFowardRef: React.ForwardRefRenderFunction<SelectRef, SelectProps> =
|
|
|
120
135
|
)
|
|
121
136
|
}
|
|
122
137
|
|
|
123
|
-
export const Select = React.forwardRef(SelectFowardRef)
|
|
138
|
+
export const Select = React.forwardRef(SelectFowardRef) as <T extends { text: string; id: number; icon?: ReactNode }>(
|
|
139
|
+
props: PropsWithChildren<SelectProps<T>> & { ref?: React.ForwardedRef<SelectRef> },
|
|
140
|
+
) => React.ReactElement
|
|
@@ -12,6 +12,7 @@ const meta: Meta<typeof Select> = {
|
|
|
12
12
|
disabled: { control: 'boolean' },
|
|
13
13
|
selectedOption: { control: 'number' },
|
|
14
14
|
onOptionChange: { action: 'selectedOptionChange' },
|
|
15
|
+
|
|
15
16
|
},
|
|
16
17
|
} satisfies Meta<typeof Select>
|
|
17
18
|
|
|
@@ -19,7 +20,7 @@ export default meta
|
|
|
19
20
|
type Story = StoryObj<typeof Select>
|
|
20
21
|
|
|
21
22
|
const ComponentWrapper = ({ options, ...args }: SelectProps) => {
|
|
22
|
-
const [selectedOption, setSelectOption] = React.useState<
|
|
23
|
+
const [selectedOption, setSelectOption] = React.useState<SelectProps['selectedOption']>(null)
|
|
23
24
|
|
|
24
25
|
return (
|
|
25
26
|
<Select
|
|
@@ -47,5 +48,5 @@ const ComponentWrapper = ({ options, ...args }: SelectProps) => {
|
|
|
47
48
|
}
|
|
48
49
|
|
|
49
50
|
export const Default: Story = {
|
|
50
|
-
render:
|
|
51
|
+
render: () => <ComponentWrapper label="Label" placeholder="Placeholder" />,
|
|
51
52
|
}
|
|
@@ -2,6 +2,8 @@ import styled from 'styled-components'
|
|
|
2
2
|
import { theme } from '../Themes'
|
|
3
3
|
|
|
4
4
|
export const SelectWrapper = styled.div`
|
|
5
|
+
display: flex;
|
|
6
|
+
flex-direction: column;
|
|
5
7
|
position: relative;
|
|
6
8
|
.icon {
|
|
7
9
|
transition: transform 0.3s;
|
|
@@ -10,40 +12,54 @@ export const SelectWrapper = styled.div`
|
|
|
10
12
|
transform: rotate(180deg) translateY(5px);
|
|
11
13
|
}
|
|
12
14
|
`
|
|
15
|
+
export interface DropdownProps {
|
|
16
|
+
dropDownTop?: boolean
|
|
17
|
+
dropDownWidth?: string
|
|
18
|
+
}
|
|
13
19
|
|
|
14
|
-
export const Dropdown = styled.div
|
|
20
|
+
export const Dropdown = styled.div<DropdownProps>`
|
|
15
21
|
position: absolute;
|
|
16
|
-
|
|
22
|
+
|
|
23
|
+
z-index: ${theme.zIndex.z9};
|
|
24
|
+
|
|
25
|
+
background-color: ${theme.colors.white};
|
|
26
|
+
width: ${({ dropDownWidth }) => (dropDownWidth ? dropDownWidth : '100%')};
|
|
27
|
+
${({ dropDownTop }) => (dropDownTop ? 'margin-botton: 4px;' : 'margin-top: 4px;')};
|
|
17
28
|
padding: 16px;
|
|
18
|
-
|
|
29
|
+
|
|
19
30
|
border-radius: 4px;
|
|
20
31
|
border: 1px solid ${theme.colors.shade20};
|
|
21
|
-
|
|
22
|
-
box-shadow: 0px 0px 15px rgba(0, 0, 0, 0.1);
|
|
32
|
+
box-shadow: 0px 0px 15px rgba(0, 0, 0, 0.05);
|
|
23
33
|
`
|
|
24
34
|
|
|
25
35
|
export const DropdownWrapper = styled.div`
|
|
26
36
|
position: relative;
|
|
37
|
+
z-index: ${theme.zIndex.z9};
|
|
27
38
|
`
|
|
28
39
|
|
|
29
|
-
|
|
30
40
|
export interface OptionButtonProps {
|
|
31
41
|
selected: boolean
|
|
32
42
|
}
|
|
33
43
|
|
|
34
44
|
export const OptionButton = styled.button<OptionButtonProps>`
|
|
45
|
+
display: flex;
|
|
46
|
+
|
|
47
|
+
align-items: center;
|
|
48
|
+
justify-content: space-between;
|
|
49
|
+
gap: 0.5rem;
|
|
50
|
+
color: ${({ selected }) => (selected ? theme.colors.cyan30 : theme.colors.shade40)};
|
|
35
51
|
background-color: transparent;
|
|
36
52
|
border: none;
|
|
37
|
-
|
|
38
|
-
font-family: 'Nunito Sans', sans-serif;
|
|
39
|
-
color: ${({ selected }) => (selected ? theme.colors.cyan30 : theme.colors.shade40)};
|
|
53
|
+
|
|
40
54
|
cursor: pointer;
|
|
41
|
-
|
|
42
|
-
|
|
55
|
+
|
|
56
|
+
font-family: 'Nunito Sans', sans-serif;
|
|
57
|
+
font-size: ${theme.paragraph.medium};
|
|
43
58
|
text-align: left;
|
|
59
|
+
|
|
44
60
|
height: 46px;
|
|
45
|
-
|
|
46
|
-
|
|
61
|
+
width: 100%;
|
|
62
|
+
padding: 0;
|
|
47
63
|
|
|
48
64
|
&:not(:last-child) {
|
|
49
65
|
border-bottom: 1px solid ${theme.colors.shade10};
|
|
@@ -53,3 +69,7 @@ export const OptionButton = styled.button<OptionButtonProps>`
|
|
|
53
69
|
color: ${theme.colors.cyan30};
|
|
54
70
|
}
|
|
55
71
|
`
|
|
72
|
+
|
|
73
|
+
export const OptionText = styled.div`
|
|
74
|
+
flex-grow: 1;
|
|
75
|
+
`
|
|
@@ -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
|
+
`
|