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.
Files changed (50) hide show
  1. package/.storybook/preview.ts +1 -0
  2. package/dist/components/DateRangePicker/styles.d.ts.map +1 -1
  3. package/dist/components/DateRangePicker/styles.js +0 -1
  4. package/dist/components/DateRangePicker/styles.js.map +1 -1
  5. package/dist/components/Input/styles.d.ts.map +1 -1
  6. package/dist/components/Input/styles.js +29 -13
  7. package/dist/components/Input/styles.js.map +1 -1
  8. package/dist/components/Select/styles.d.ts.map +1 -1
  9. package/dist/components/Select/styles.js +17 -9
  10. package/dist/components/Select/styles.js.map +1 -1
  11. package/dist/index.d.ts +2 -2
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +2 -2
  14. package/dist/index.js.map +1 -1
  15. package/dist/src/globalStyles.d.ts.map +1 -1
  16. package/dist/src/globalStyles.js +6 -1
  17. package/dist/src/globalStyles.js.map +1 -1
  18. package/package.json +1 -1
  19. package/src/assets/icon/bookmark.svg +15 -0
  20. package/src/assets/icon/coins.svg +10 -0
  21. package/src/assets/icon/file.svg +15 -0
  22. package/src/assets/icon/gavel.svg +3 -0
  23. package/src/assets/icon/gear.svg +10 -0
  24. package/src/assets/icon/grid-2-plus.svg +3 -0
  25. package/src/assets/icon/index.ts +42 -26
  26. package/src/assets/icon/lock.svg +2 -0
  27. package/src/assets/icon/wave-pulse.svg +10 -0
  28. package/src/components/DatePicker/index.tsx +17 -7
  29. package/src/components/DatePicker/stories.tsx +5 -6
  30. package/src/components/DateRangePicker/index.tsx +24 -15
  31. package/src/components/DateRangePicker/stories.tsx +1 -1
  32. package/src/components/DateRangePicker/styles.ts +0 -1
  33. package/src/components/Input/index.tsx +22 -5
  34. package/src/components/Input/styles.ts +29 -13
  35. package/src/components/Select/index.tsx +36 -19
  36. package/src/components/Select/stories.tsx +3 -2
  37. package/src/components/Select/styles.ts +33 -13
  38. package/src/components/Sidebar/SidebarGroup/index.tsx +90 -0
  39. package/src/components/Sidebar/SidebarGroup/styles.ts +83 -0
  40. package/src/components/Sidebar/SidebarItem/index.tsx +69 -0
  41. package/src/components/Sidebar/SidebarItem/styles.ts +70 -0
  42. package/src/components/Sidebar/SidebarLogo/index.tsx +23 -0
  43. package/src/components/Sidebar/SidebarLogo/styles.ts +53 -0
  44. package/src/components/Sidebar/index.tsx +127 -0
  45. package/src/components/Sidebar/stories.tsx +51 -0
  46. package/src/components/Sidebar/styles.ts +80 -0
  47. package/src/components/Types/index.ts +23 -3
  48. package/src/index.tsx +2 -2
  49. package/src/reset.css +6 -0
  50. 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
- export interface DateRangePickerProps extends HTMLAttributes<HTMLDivElement> {
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?: { text: string; id: number }[]
20
- selectedOption?: number | null
23
+ options?: T[]
24
+ selectedOption?: T | null
21
25
  readonly?: boolean
22
26
  disabled?: boolean
23
- onSelectedOptionChange?: (option: number | null) => void
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: React.FC<DateRangePickerProps> = ({
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: number | null) => {
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 { customOptionId, optionComputed } = useMemo(() => {
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 optionComputed = [...options, { text: 'Período Personalizado', id: customOptionId }]
178
- return { customOptionId, optionComputed }
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 { customOptionId: 0, optionComputed: [] }
181
- }, [selectedOption, options])
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 ? customOptionId : null)}
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<number | null>(null)
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
- window.addEventListener('resize', handler)
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 () => window.removeEventListener('resize', handler)
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
- font-family: ${theme.fonts.join(', ')};
12
- outline: none;
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
- z-index: 1;
17
- background-color: transparent;
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
- font-weight: 600;
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
- padding: 18px 16px 14px 16px;
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
- cursor: ${({ disabled }: StyledInputContentProps) => (disabled ? 'not-allowed' : 'pointer')};
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 { OptionButton, Dropdown, DropdownWrapper, SelectWrapper } from './styles'
15
+ import { Dropdown, DropdownWrapper, OptionButton, OptionText, SelectWrapper } from './styles'
17
16
 
18
- export interface SelectProps extends HTMLAttributes<HTMLDivElement> {
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: number | null) => void
23
- options?: { text: string; id: number }[]
23
+ onOptionChange?: (option: T | null) => void
24
+ options?: T[]
24
25
  placeholder?: string
25
26
  rightIcon?: ReactNode
26
- selectedOption?: number | null
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: React.ForwardRefRenderFunction<SelectRef, SelectProps> = (
35
- { children, label, leftIcon, onOptionChange, options, placeholder, rightIcon, selectedOption, value, ...props },
36
- ref,
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: number) => {
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
- const option = options?.find(option => option.id === selectedOption)
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={option.id === selectedOption}
120
+ selected={selectedOption?.id === option.id}
107
121
  key={option.id}
108
- onClick={() => onOptionClick(option.id)}
122
+ onClick={() => onOptionClick(option)}
109
123
  >
110
- {option.text}
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<number | null>(null)
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: args => <ComponentWrapper label="Label" placeholder="Placeholder" />,
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
- width: 252px;
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
- z-index: 1;
29
+
19
30
  border-radius: 4px;
20
31
  border: 1px solid ${theme.colors.shade20};
21
- margin-top: 7px;
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
- font-size: ${theme.paragraph.medium};
38
- font-family: 'Nunito Sans', sans-serif;
39
- color: ${({ selected }) => (selected ? theme.colors.cyan30 : theme.colors.shade40)};
53
+
40
54
  cursor: pointer;
41
- padding: 0;
42
- width: 100%;
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
- display: flex;
46
- align-items: center;
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
+ `