@cyber-harbour/ui 1.0.20 → 1.0.21

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cyber-harbour/ui",
3
- "version": "1.0.20",
3
+ "version": "1.0.21",
4
4
  "main": "dist/index.js",
5
5
  "module": "dist/index.mjs",
6
6
  "types": "dist/index.d.ts",
@@ -43,6 +43,15 @@ export const ContextMenu = ({
43
43
  align={align}
44
44
  onClickOutside={onClickOutside}
45
45
  content={children}
46
+ containerStyle={{
47
+ backgroundColor: theme.colors.background,
48
+ border: `1px solid ${theme.colors.stroke.light}`,
49
+ boxShadow: '0px 0px 10px 0px rgba(0, 0, 0, 0.25)',
50
+ borderRadius: '5px',
51
+ overflow: 'auto',
52
+ maxHeight: '500px',
53
+ zIndex: `${9999}`,
54
+ }}
46
55
  >
47
56
  <StyledButton
48
57
  ref={buttonRef}
@@ -51,6 +60,8 @@ export const ContextMenu = ({
51
60
  $fullWidth={fullWidth}
52
61
  $size={size}
53
62
  className={className}
63
+ type="button"
64
+ disabled={disabled}
54
65
  >
55
66
  <div>{anchor}</div>
56
67
  {isOpen ? (
@@ -88,7 +99,7 @@ const StyledButton = styled.button<{
88
99
  font-weight: 500;
89
100
  display: inline-flex;
90
101
  align-items: center;
91
- justify-content: center;
102
+ justify-content: ${$fullWidth ? 'space-between' : 'center'};
92
103
  text-decoration: none;
93
104
  transition: all 0.2s ease;
94
105
  outline: none;
@@ -118,5 +129,3 @@ const StyledButton = styled.button<{
118
129
  `;
119
130
  }}
120
131
  `;
121
-
122
- const StyledContainer = styled.div``;
@@ -0,0 +1,20 @@
1
+ import { SVGProps } from 'react';
2
+
3
+ interface PrintIconProps extends SVGProps<SVGSVGElement> {
4
+ fill?: string;
5
+ }
6
+
7
+ export const PlusIcon = ({ fill = 'currentColor', ...props }: PrintIconProps) => {
8
+ return (
9
+ <svg viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
10
+ <path
11
+ d="M9.21053 5.78952H0.789474C0.357895 5.78952 0 5.43162 0 5.00005C0 4.56847 0.357895 4.21057 0.789474 4.21057H9.21053C9.64211 4.21057 10 4.56847 10 5.00005C10 5.43162 9.64211 5.78952 9.21053 5.78952Z"
12
+ fill={fill}
13
+ />
14
+ <path
15
+ d="M5.00041 10C4.56883 10 4.21094 9.64211 4.21094 9.21053V0.789474C4.21094 0.357895 4.56883 0 5.00041 0C5.43199 0 5.78988 0.357895 5.78988 0.789474V9.21053C5.78988 9.64211 5.43199 10 5.00041 10Z"
16
+ fill={fill}
17
+ />
18
+ </svg>
19
+ );
20
+ };
@@ -36,3 +36,4 @@ export { BallsMenu } from './BallsMenu';
36
36
  export { CheckIcon } from './Check';
37
37
  export { ChevronDownIcon } from './ChevronDown';
38
38
  export { ChevronUpIcon } from './ChevronUp';
39
+ export { PlusIcon } from './Plus';
@@ -0,0 +1,116 @@
1
+ import { useCallback, useState } from 'react';
2
+ import { PopoverAlign, PopoverPosition } from 'react-tiny-popover';
3
+ import { ContextMenu } from '../ContextMenu';
4
+ import { ButtonSize, getButtonSizeStyles } from '@/Theme';
5
+ import { styled } from 'styled-components';
6
+
7
+ interface SelectProps<T extends string | number> {
8
+ selected?: T;
9
+ options: { value: T; inputDisplay?: string }[];
10
+ handleSelect: (id: T) => void;
11
+ placeholder: string;
12
+ disabled?: boolean;
13
+ positions?: PopoverPosition[] | PopoverPosition;
14
+ align?: PopoverAlign;
15
+ size?: ButtonSize;
16
+ }
17
+
18
+ export const Select = <T extends string | number>({
19
+ options,
20
+ selected,
21
+ handleSelect,
22
+ placeholder,
23
+ disabled = false,
24
+ positions = ['bottom'],
25
+ align = 'start',
26
+ size = 'small',
27
+ }: SelectProps<T>) => {
28
+ const [isOpen, setIsOpen] = useState<boolean>(false);
29
+ const handleToggle = useCallback(() => {
30
+ if (!disabled) setIsOpen((prev) => !prev);
31
+ }, []);
32
+
33
+ return (
34
+ <ContextMenu
35
+ isOpen={isOpen}
36
+ onClickOutside={() => setIsOpen(false)}
37
+ onClick={handleToggle}
38
+ disabled={disabled}
39
+ anchor={!selected ? placeholder : options.find((option) => option.value === selected)?.inputDisplay || selected}
40
+ fullWidth
41
+ positions={positions}
42
+ align={align}
43
+ size={size}
44
+ >
45
+ <StyledWrapper>
46
+ {options.map((item) => (
47
+ <StyledItem
48
+ onClick={() => {
49
+ handleSelect(item.value);
50
+ setIsOpen(false);
51
+ }}
52
+ type="button"
53
+ $selected={item.value === selected}
54
+ key={item.value}
55
+ disabled={disabled}
56
+ $size={size}
57
+ >
58
+ {item.inputDisplay || item.value}
59
+ </StyledItem>
60
+ ))}
61
+ </StyledWrapper>
62
+ </ContextMenu>
63
+ );
64
+ };
65
+
66
+ const StyledWrapper = styled.div`
67
+ padding-block: 7px;
68
+ padding-inline: 5px;
69
+ button:not(:last-of-type) {
70
+ margin-bottom: 4px;
71
+ }
72
+ `;
73
+
74
+ const StyledItem = styled.button<{ $size: ButtonSize; $selected: boolean }>`
75
+ ${({ theme, $size, $selected }) => {
76
+ const sizes = getButtonSizeStyles(theme, $size);
77
+ return `
78
+ background: ${theme.select.item.default.background};
79
+ color: ${theme.select.item.default.text};
80
+ font-size: ${sizes.fontSize};
81
+ gap: ${sizes.gap};
82
+ padding-block: ${sizes.paddingBlock};
83
+ padding-inline: ${sizes.paddingInline};
84
+ border-radius: ${sizes.borderRadius};
85
+ border-width: ${sizes.borderWidth};
86
+ border: none;
87
+ width: 100%;
88
+ cursor: pointer;
89
+ font-weight: 400;
90
+ display: inline-flex;
91
+ align-items: center;
92
+ justify-content: flex-start;
93
+ text-decoration: none;
94
+ transition: all 0.2s ease;
95
+ outline: none;
96
+ flex-direction: row;
97
+
98
+ &:hover {
99
+ background-color: ${theme.select.item.hover.background};
100
+ color: ${theme.select.item.hover.text};
101
+ }
102
+
103
+ ${
104
+ $selected &&
105
+ `background: ${theme.select.item.active.background};
106
+ color: ${theme.select.item.active.text};`
107
+ }
108
+
109
+ &:disabled {
110
+ background: ${theme.select.item.disabled.background};
111
+ color: ${theme.select.item.disabled.text};
112
+ cursor: not-allowed;
113
+ }
114
+ `;
115
+ }}
116
+ `;
@@ -0,0 +1 @@
1
+ export * from './Select';
package/src/Core/index.ts CHANGED
@@ -7,3 +7,4 @@ export * from './Header';
7
7
  export * from './Table';
8
8
  export * from './Pagination';
9
9
  export * from './ContextMenu';
10
+ export * from './Select';
@@ -12,7 +12,7 @@ export const PageLayout = ({ children, header, sidebar }: PageLayoutProps) => {
12
12
  <StyledContainer $withHeader={!!header} $withSidebar={!!sidebar}>
13
13
  {header}
14
14
  {sidebar}
15
- <main>{children}</main>
15
+ <StyledMain>{children}</StyledMain>
16
16
  </StyledContainer>
17
17
  );
18
18
  };
@@ -53,3 +53,7 @@ export const StyledContainer = styled.div<StyledContainerProps>(
53
53
  }
54
54
  `
55
55
  );
56
+
57
+ const StyledMain = styled.main`
58
+ min-width: 0;
59
+ `;
@@ -497,7 +497,7 @@ export const lightThemePx: Theme = {
497
497
  boxShadow: 'none',
498
498
  },
499
499
  disabled: {
500
- background: '#EBEBEB',
500
+ background: '#FAFAFA',
501
501
  text: '#99989C',
502
502
  border: ' #EBEBEB',
503
503
  boxShadow: 'none',
@@ -516,6 +516,35 @@ export const lightThemePx: Theme = {
516
516
  size: 7,
517
517
  },
518
518
  },
519
+ // Компонент Select
520
+ select: {
521
+ item: {
522
+ default: {
523
+ background: 'transparent',
524
+ text: '#101010',
525
+ border: ' none',
526
+ boxShadow: 'none',
527
+ },
528
+ hover: {
529
+ background: '#E5ECFD',
530
+ text: '#101010',
531
+ border: ' none',
532
+ boxShadow: 'none',
533
+ },
534
+ active: {
535
+ background: '#E8EAEE',
536
+ text: '#101010',
537
+ border: ' none',
538
+ boxShadow: 'none',
539
+ },
540
+ disabled: {
541
+ background: '#FAFAFA',
542
+ text: '#99989C',
543
+ border: ' none',
544
+ boxShadow: 'none',
545
+ },
546
+ },
547
+ },
519
548
  };
520
549
 
521
550
  // Конвертуємо всі розміри з px в rem
@@ -147,6 +147,10 @@ export type Theme = {
147
147
  size: number | string;
148
148
  };
149
149
  };
150
+ //Select
151
+ select: {
152
+ item: Record<ButtonState, ButtonElementStyle>;
153
+ };
150
154
  };
151
155
 
152
156
  //TODO check and refactoring