@cyber-harbour/ui 1.0.72 → 1.0.74

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.72",
3
+ "version": "1.0.74",
4
4
  "main": "dist/index.js",
5
5
  "module": "dist/index.mjs",
6
6
  "types": "dist/index.d.ts",
@@ -19,6 +19,7 @@ interface ContextMenuProps {
19
19
  hasBorder?: boolean;
20
20
  maxHeight?: number;
21
21
  matchAnchorWidth?: boolean;
22
+ anchorIcon?: any;
22
23
  }
23
24
 
24
25
  export const ContextMenu = ({
@@ -36,6 +37,7 @@ export const ContextMenu = ({
36
37
  hasBorder = true,
37
38
  maxHeight = 500,
38
39
  matchAnchorWidth = false,
40
+ anchorIcon,
39
41
  }: ContextMenuProps) => {
40
42
  const [anchorWidth, setAnchorWidth] = useState<number>(0);
41
43
  const buttonRef = useRef<HTMLButtonElement | null>(null);
@@ -66,6 +68,7 @@ export const ContextMenu = ({
66
68
  zIndex: `${9999}`,
67
69
  width: anchorWidth && matchAnchorWidth ? `${anchorWidth}px` : undefined,
68
70
  }}
71
+ containerClassName="cb-ui-context-menu"
69
72
  >
70
73
  <StyledButton
71
74
  onClick={onClick}
@@ -78,11 +81,12 @@ export const ContextMenu = ({
78
81
  $hasBorder={hasBorder}
79
82
  >
80
83
  <StyledAnchor>{anchor}</StyledAnchor>
81
- {isOpen ? (
82
- <ChevronUpIcon width={theme.contextMenu.icon.size} height={theme.contextMenu.icon.size} />
83
- ) : (
84
- <ChevronDownIcon width={theme.contextMenu.icon.size} height={theme.contextMenu.icon.size} />
85
- )}
84
+ {anchorIcon ||
85
+ (isOpen ? (
86
+ <ChevronUpIcon width={theme.contextMenu.icon.size} height={theme.contextMenu.icon.size} />
87
+ ) : (
88
+ <ChevronDownIcon width={theme.contextMenu.icon.size} height={theme.contextMenu.icon.size} />
89
+ ))}
86
90
  </StyledButton>
87
91
  </Popover>
88
92
  );
@@ -36,7 +36,8 @@ const DrawerWithOutclick = ({ onClose, children, width, header }: DrawerProps) =
36
36
  if (
37
37
  drawerRef.current &&
38
38
  !drawerRef.current.contains(target) &&
39
- !target.closest('.react-tiny-popover-container')
39
+ !target.closest('.cb-ui-context-menu') &&
40
+ !target.closest('.cb-ui-backdrop')
40
41
  ) {
41
42
  onClose();
42
43
  }
@@ -0,0 +1,116 @@
1
+ import styled from 'styled-components';
2
+ import { Overlay } from '../Overlay';
3
+ import { createStyledComponent, FabricComponent, generatePropertySpaceStyle, propToRem, pxToRem } from '../../Theme';
4
+
5
+ type ModalProps = {
6
+ onClose: () => void;
7
+ width?: number;
8
+ children: any;
9
+ isNested?: boolean;
10
+ };
11
+
12
+ interface ModalChildrenProps extends FabricComponent<Omit<React.HTMLAttributes<HTMLDivElement>, 'children'>> {
13
+ children: any;
14
+ }
15
+
16
+ export const Modal = ({ children, onClose, isNested = false, width }: ModalProps) => {
17
+ return (
18
+ <Overlay onOutsideClick={onClose} isLocked={!isNested}>
19
+ <StyledContainer $width={width}>{children}</StyledContainer>
20
+ </Overlay>
21
+ );
22
+ };
23
+
24
+ export const ModalHeader = createStyledComponent<ModalChildrenProps>(
25
+ styled.div(({ theme, p = theme.modal.padding, px, pl, pr }) => {
26
+ const left = pl || px || p;
27
+ const right = pr || px || p;
28
+ return `
29
+ grid-area: modal-header;
30
+ position: relative;
31
+ &::after {
32
+ content: '';
33
+ position: absolute;
34
+ bottom: 0;
35
+ left: ${typeof left === 'string' ? left : pxToRem(left)};
36
+ right: ${typeof right === 'string' ? right : pxToRem(right)};
37
+ height: ${pxToRem(1)};
38
+ background-color: ${theme.modal.borderColor};
39
+ }
40
+
41
+ ${generatePropertySpaceStyle(theme, 'padding', p)};
42
+ `;
43
+ }),
44
+ {
45
+ ignoreStyles: ['padding'],
46
+ }
47
+ );
48
+
49
+ export const ModalBody = createStyledComponent<ModalChildrenProps>(
50
+ styled.div(
51
+ ({ theme, p = theme.modal.padding }) => `
52
+ grid-area: modal-body;
53
+ max-height: 100%;
54
+ overflow-y: auto;
55
+ ${generatePropertySpaceStyle(theme, 'padding', p)};
56
+ `
57
+ ),
58
+ {
59
+ ignoreStyles: ['padding'],
60
+ }
61
+ );
62
+
63
+ export const ModalFooter = createStyledComponent<ModalChildrenProps>(
64
+ styled.div(({ theme, p = theme.modal.padding, px, pl, pr }) => {
65
+ const left = pl || px || p;
66
+ const right = pr || px || p;
67
+ return `
68
+ grid-area: modal-footer;
69
+ position: relative;
70
+ &::after {
71
+ content: '';
72
+ position: absolute;
73
+ top: ${pxToRem(1)};
74
+ left: ${typeof left === 'string' ? left : pxToRem(left)};
75
+ right: ${typeof right === 'string' ? right : pxToRem(right)};
76
+ height: ${pxToRem(1)};
77
+ background-color: ${theme.modal.borderColor};
78
+ }
79
+ ${generatePropertySpaceStyle(theme, 'padding', p)};
80
+ `;
81
+ }),
82
+ {
83
+ ignoreStyles: ['padding'],
84
+ }
85
+ );
86
+
87
+ const StyledContainer = styled.div<{ $width?: number }>(
88
+ ({ theme, $width = theme.modal.width }) => `
89
+ display: grid;
90
+ grid-template-areas:
91
+ "modal-header"
92
+ "modal-body"
93
+ "modal-footer";
94
+ grid-template-rows: auto 1fr auto;
95
+ position: absolute;
96
+ z-index:${theme.zIndex.modal};
97
+ top: 0;
98
+ left: 0;
99
+ width: 100%;
100
+ height: 100%;
101
+ overflow: hidden;
102
+ background-color: ${theme.modal.background};
103
+ @media (min-width: ${theme.breakpoints.m}px) {
104
+ top: 50%;
105
+ left: 50%;
106
+ transform: translate(-50%, -50%);
107
+ height: auto;
108
+ max-height: 90dvh;
109
+ width: ${propToRem($width, theme.baseSize)};
110
+ max-width: 90vw;
111
+ border: 1px solid ${theme.modal.borderColor};
112
+ box-shadow: ${theme.modal.shadow};
113
+ border-radius: ${theme.modal.borderRadius};
114
+ }
115
+ `
116
+ );
@@ -0,0 +1 @@
1
+ export * from './Modal';
@@ -12,14 +12,16 @@ export type OverlayProps = {
12
12
  appendPosition?: Position;
13
13
  prepend?: any;
14
14
  prependPosition?: Position;
15
+ isLocked?: boolean;
15
16
  };
16
17
 
17
18
  export const Overlay = createComponent<OverlayProps>(
18
- ({ children, onOutsideClick, prepend, append, prependPosition, appendPosition, ...props }) => {
19
- useBodyScrollLock(true);
19
+ ({ children, onOutsideClick, prepend, append, prependPosition, appendPosition, isLocked = true, ...props }) => {
20
+ useBodyScrollLock(isLocked);
20
21
  return createPortal(
21
22
  <Container
22
23
  {...props}
24
+ className="cb-ui-backdrop"
23
25
  onClick={(e) => {
24
26
  if (e.target === e.currentTarget && onOutsideClick) {
25
27
  onOutsideClick();
package/src/Core/index.ts CHANGED
@@ -24,3 +24,4 @@ export * from './Drawer';
24
24
  export * from './Tooltip';
25
25
  export * from './ContentLoader';
26
26
  export * from './Overlay';
27
+ export * from './Modal';
@@ -992,6 +992,14 @@ export const darkThemePx: Theme = {
992
992
  gap: 6,
993
993
  background: 'rgba(0, 0, 0, 0.5)',
994
994
  },
995
+ modal: {
996
+ padding: 20,
997
+ width: 545,
998
+ shadow: '0px 0px 10px 0px rgba(0, 0, 0, 0.25)',
999
+ borderColor: '#1E2226',
1000
+ background: '#0F1317',
1001
+ borderRadius: 8,
1002
+ },
995
1003
  };
996
1004
 
997
1005
  export const darkTheme = convertPaletteToRem(darkThemePx, darkThemePx.baseSize) as DefaultTheme;
@@ -991,6 +991,14 @@ export const lightThemePx: Theme = {
991
991
  gap: 6,
992
992
  background: 'rgba(16, 16, 16, 0.1)',
993
993
  },
994
+ modal: {
995
+ padding: 20,
996
+ width: 545,
997
+ shadow: '0px 0px 10px 0px rgba(0, 0, 0, 0.25)',
998
+ borderColor: '#EBEBEB',
999
+ background: '#FFFFFF',
1000
+ borderRadius: 8,
1001
+ },
994
1002
  };
995
1003
 
996
1004
  export const lightTheme = convertPaletteToRem(lightThemePx, lightThemePx.baseSize) as DefaultTheme;
@@ -335,6 +335,14 @@ export type Theme = {
335
335
  background: string;
336
336
  gap: string | number;
337
337
  };
338
+ modal: {
339
+ padding: string | number;
340
+ width: number;
341
+ shadow: string;
342
+ borderColor: string;
343
+ background: string;
344
+ borderRadius: string | number;
345
+ };
338
346
  };
339
347
 
340
348
  //TODO check and refactoring
@@ -88,8 +88,8 @@ const IGNORE_CONVERT_KEYS: Record<string, string[] | boolean> = {
88
88
  */
89
89
  export const propToRem = (value: number | string, baseSize: number = 16): string => {
90
90
  // Check if value ends with units that should not be converted to rem
91
- if (typeof value === 'string' && /(%|d?vh|d?vw)$/.test(value.trim())) {
92
- return value; // Return percentage and viewport values as-is
91
+ if (typeof value === 'string' && /(%|d?vh|d?vw|d?rem)$/.test(value.trim())) {
92
+ return value; // Return percentage, viewport and rem values as-is
93
93
  }
94
94
 
95
95
  const numericValue = typeof value === 'string' ? parseFloat(value) : value;