@addev-be/ui 0.2.16 → 0.2.18

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 (88) hide show
  1. package/eslint.config.js +28 -0
  2. package/package.json +13 -20
  3. package/src/Icons.tsx +108 -0
  4. package/src/components/data/AdvancedRequestDataGrid/helpers/advancedRequests.ts +93 -0
  5. package/src/components/data/AdvancedRequestDataGrid/helpers/columns.tsx +262 -0
  6. package/src/components/data/AdvancedRequestDataGrid/helpers/index.ts +2 -0
  7. package/src/components/data/AdvancedRequestDataGrid/index.tsx +267 -0
  8. package/src/components/data/AdvancedRequestDataGrid/types.ts +47 -0
  9. package/src/components/data/DataGrid/DataGridCell.tsx +73 -0
  10. package/src/components/data/DataGrid/DataGridColumnsModal/helpers.ts +14 -0
  11. package/src/components/data/DataGrid/DataGridColumnsModal/hooks.tsx +59 -0
  12. package/src/components/data/DataGrid/DataGridColumnsModal/index.tsx +181 -0
  13. package/src/components/data/DataGrid/DataGridColumnsModal/styles.ts +104 -0
  14. package/src/components/data/DataGrid/DataGridEditableCell.tsx +43 -0
  15. package/src/components/data/DataGrid/DataGridFilterMenu/FilterValuesScroller.tsx +120 -0
  16. package/src/components/data/DataGrid/DataGridFilterMenu/hooks.tsx +75 -0
  17. package/src/components/data/DataGrid/DataGridFilterMenu/index.tsx +360 -0
  18. package/src/components/data/DataGrid/DataGridFilterMenu/styles.ts +96 -0
  19. package/src/components/data/DataGrid/DataGridFooter.tsx +42 -0
  20. package/src/components/data/DataGrid/DataGridHeader.tsx +126 -0
  21. package/src/components/data/DataGrid/DataGridHeaderCell.tsx +132 -0
  22. package/src/components/data/DataGrid/FilterModalContent/index.tsx +136 -0
  23. package/src/components/data/DataGrid/FilterModalContent/styles.ts +22 -0
  24. package/src/components/data/DataGrid/VirtualScroller.tsx +46 -0
  25. package/src/components/data/DataGrid/helpers/columns.tsx +295 -0
  26. package/src/components/data/DataGrid/helpers/filters.ts +287 -0
  27. package/src/components/data/DataGrid/helpers/index.ts +2 -0
  28. package/src/components/data/DataGrid/hooks/index.ts +30 -0
  29. package/src/components/data/DataGrid/hooks/useDataGrid.tsx +306 -0
  30. package/src/components/data/DataGrid/hooks/useDataGridCopy.ts +175 -0
  31. package/src/components/data/DataGrid/hooks/useDataGridSettings.ts +48 -0
  32. package/src/components/data/DataGrid/index.tsx +140 -0
  33. package/src/components/data/DataGrid/styles.ts +323 -0
  34. package/src/components/data/DataGrid/types.ts +267 -0
  35. package/src/components/data/SqlRequestDataGrid/helpers/columns.tsx +277 -0
  36. package/src/components/data/SqlRequestDataGrid/helpers/index.ts +2 -0
  37. package/src/components/data/SqlRequestDataGrid/helpers/sqlRequests.ts +16 -0
  38. package/src/components/data/SqlRequestDataGrid/index.tsx +347 -0
  39. package/src/components/data/SqlRequestDataGrid/types.ts +47 -0
  40. package/src/components/data/index.ts +8 -0
  41. package/src/components/forms/Button.tsx +99 -0
  42. package/src/components/forms/IconButton.tsx +56 -0
  43. package/src/components/forms/IndeterminateCheckbox.tsx +46 -0
  44. package/src/components/forms/Select.tsx +40 -0
  45. package/src/components/forms/index.ts +5 -0
  46. package/src/components/forms/styles.ts +20 -0
  47. package/src/components/index.ts +3 -0
  48. package/src/components/layout/Dropdown/index.tsx +79 -0
  49. package/src/components/layout/Dropdown/styles.ts +44 -0
  50. package/src/components/layout/Loading/index.tsx +29 -0
  51. package/src/components/layout/Loading/styles.ts +29 -0
  52. package/src/components/layout/Modal/index.tsx +51 -0
  53. package/src/components/layout/Modal/styles.ts +110 -0
  54. package/src/components/layout/index.ts +3 -0
  55. package/src/components/ui/ContextMenu/index.tsx +79 -0
  56. package/src/components/ui/ContextMenu/styles.ts +119 -0
  57. package/src/config/index.ts +14 -0
  58. package/src/helpers/dates.ts +9 -0
  59. package/src/helpers/getScrollbarSize.ts +14 -0
  60. package/src/helpers/numbers.ts +26 -0
  61. package/src/hooks/index.ts +2 -0
  62. package/src/hooks/useElementSize.ts +24 -0
  63. package/src/hooks/useWindowSize.ts +20 -0
  64. package/src/index.ts +7 -0
  65. package/src/providers/PortalsProvider/index.tsx +54 -0
  66. package/src/providers/PortalsProvider/styles.ts +27 -0
  67. package/src/providers/SettingsProvider/index.tsx +70 -0
  68. package/src/providers/ThemeProvider/ThemeProvider.ts +55 -0
  69. package/src/providers/ThemeProvider/defaultTheme.ts +444 -0
  70. package/src/providers/ThemeProvider/index.ts +3 -0
  71. package/src/providers/ThemeProvider/types.ts +123 -0
  72. package/src/providers/UiProviders/index.tsx +65 -0
  73. package/src/providers/UiProviders/styles.ts +10 -0
  74. package/src/providers/hooks.ts +8 -0
  75. package/src/providers/index.ts +5 -0
  76. package/src/services/HttpService.ts +80 -0
  77. package/src/services/WebSocketService.ts +147 -0
  78. package/src/services/advancedRequests.ts +101 -0
  79. package/src/services/base.ts +31 -0
  80. package/src/services/globalSearch.ts +27 -0
  81. package/src/services/hooks.ts +23 -0
  82. package/src/services/index.ts +2 -0
  83. package/src/services/sqlRequests.ts +110 -0
  84. package/src/styles/animations.scss +30 -0
  85. package/src/styles/index.scss +42 -0
  86. package/src/typings.d.ts +6 -0
  87. package/tsconfig.json +18 -0
  88. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,79 @@
1
+ import { Divider, MenuContainer, MenuItemContainer, SubMenu } from './styles';
2
+ import {
3
+ FC,
4
+ HTMLAttributes,
5
+ PropsWithChildren,
6
+ useCallback,
7
+ useRef,
8
+ useState,
9
+ } from 'react';
10
+
11
+ type ContextMenuFC = FC<PropsWithChildren<HTMLAttributes<HTMLDivElement>>> & {
12
+ Item: typeof MenuItemContainer;
13
+ ParentItem: typeof ParentMenuItem;
14
+ Divider: typeof Divider;
15
+ SubMenu: typeof SubMenu;
16
+ };
17
+
18
+ export const ContextMenu: ContextMenuFC = ({ children }) => {
19
+ return (
20
+ <MenuContainer onClick={(e) => e.stopPropagation()}>
21
+ {children}
22
+ </MenuContainer>
23
+ );
24
+ };
25
+
26
+ export const ParentMenuItem: FC<HTMLAttributes<HTMLDivElement>> = ({
27
+ children,
28
+ ...props
29
+ }) => {
30
+ const [isOpened, setIsOpened] = useState(false);
31
+ const [isSubMenuLeft, setIsSubMenuLeft] = useState(false);
32
+ const [currentTimeout, setCurrentTimeout] = useState<number | null>(null);
33
+ const containerRef = useRef<HTMLDivElement | null>(null);
34
+
35
+ const stopTimeout = useCallback(() => {
36
+ if (currentTimeout) {
37
+ clearTimeout(currentTimeout);
38
+ }
39
+ setCurrentTimeout(null);
40
+ }, [currentTimeout]);
41
+
42
+ const startTimeout = useCallback(
43
+ (open: boolean) => {
44
+ stopTimeout();
45
+ setCurrentTimeout(
46
+ window.setTimeout(
47
+ () => {
48
+ const rect = containerRef.current?.getBoundingClientRect();
49
+ setIsSubMenuLeft(!!rect && rect.right + 280 > window.innerWidth);
50
+ setIsOpened(open);
51
+ },
52
+ open ? 100 : 300
53
+ )
54
+ );
55
+ },
56
+ [stopTimeout]
57
+ );
58
+
59
+ const open = useCallback(() => startTimeout(true), [startTimeout]);
60
+ const close = useCallback(() => startTimeout(false), [startTimeout]);
61
+
62
+ return (
63
+ <MenuItemContainer
64
+ ref={containerRef}
65
+ {...props}
66
+ className={`${isOpened ? 'opened' : ''} ${isSubMenuLeft ? 'left' : ''}`}
67
+ onMouseEnter={open}
68
+ onMouseLeave={close}
69
+ $withArrow
70
+ >
71
+ {children}
72
+ </MenuItemContainer>
73
+ );
74
+ };
75
+
76
+ ContextMenu.Item = MenuItemContainer;
77
+ ContextMenu.ParentItem = ParentMenuItem;
78
+ ContextMenu.Divider = Divider;
79
+ ContextMenu.SubMenu = SubMenu;
@@ -0,0 +1,119 @@
1
+ import styled, { css } from 'styled-components';
2
+
3
+ import { ThemeColor } from '../../../providers/ThemeProvider/types';
4
+
5
+ const menuContainerCss = css`
6
+ position: absolute;
7
+ color: var(--color-neutral-900);
8
+ border-radius: var(--rounded-md);
9
+ padding: var(--space-1) 0;
10
+ box-shadow: var(--shadow-lg);
11
+ background-color: var(--color-neutral-100);
12
+ min-width: 20em;
13
+ outline: 1px solid var(--color-neutral-200);
14
+ display: flex;
15
+ flex-direction: column;
16
+ `;
17
+
18
+ export const MenuContainer = styled.div.attrs({
19
+ className: 'MenuContainer',
20
+ })`
21
+ ${menuContainerCss}
22
+ inset: 0;
23
+ `;
24
+ MenuContainer.displayName = 'MenuContainer';
25
+
26
+ export const SubMenu = styled.div.attrs({
27
+ className: 'SubMenu',
28
+ })`
29
+ ${menuContainerCss}
30
+ top: 0;
31
+ left: 100%;
32
+ margin-top: -var(--space-1);
33
+ `;
34
+ SubMenu.displayName = 'SubMenu';
35
+
36
+ export const MenuItemContainer = styled.div.attrs({
37
+ className: 'MenuItemContainer',
38
+ })<{
39
+ $color?: ThemeColor;
40
+ disabled?: boolean;
41
+ $withArrow?: boolean;
42
+ $opened?: boolean;
43
+ }>`
44
+ position: relative;
45
+ display: flex;
46
+ align-items: center;
47
+ font-family: var(--font-sans);
48
+ font-weight: normal;
49
+ text-align: left;
50
+ padding: var(--space-1) var(--space-2);
51
+ font-size: var(--text-base);
52
+ line-height: var(--leading-6);
53
+ border: none;
54
+ cursor: pointer;
55
+
56
+ ${({ $withArrow }) =>
57
+ $withArrow &&
58
+ css`
59
+ &::after {
60
+ content: '';
61
+ position: absolute;
62
+ right: var(--space-2);
63
+ top: 50%;
64
+ transform: translateY(-50%);
65
+ border: 4px solid transparent;
66
+ border-left-color: var(--color-neutral-600);
67
+ }
68
+ `}
69
+
70
+ ${({ $color, disabled }) =>
71
+ disabled
72
+ ? css`
73
+ color: var(--color-neutral-300);
74
+ background-color: var(--color-neutral-100);
75
+ cursor: default;
76
+ `
77
+ : $color
78
+ ? css`
79
+ color: var(--color-${$color}-600);
80
+ background-color: var(--color-neutral-100);
81
+ &:hover {
82
+ background-color: var(--color-${$color}-200);
83
+ }
84
+ `
85
+ : css`
86
+ color: var(--color-neutral-900);
87
+ background-color: var(--color-neutral-100);
88
+ &:hover {
89
+ background-color: var(--color-neutral-200);
90
+ }
91
+ `}
92
+
93
+ svg {
94
+ fill: currentColor;
95
+ width: var(--space-4);
96
+ height: var(--space-4);
97
+ margin-right: var(--space-2);
98
+ }
99
+
100
+ & > ${SubMenu} {
101
+ display: none;
102
+ margin-top: calc(0px - var(--space-1));
103
+ }
104
+ &.opened > ${SubMenu} {
105
+ display: block;
106
+ }
107
+ &.opened.left > ${SubMenu} {
108
+ left: auto;
109
+ right: 100%;
110
+ }
111
+ `;
112
+ MenuItemContainer.displayName = 'MenuItemContainer';
113
+
114
+ export const Divider = styled.div.attrs({
115
+ className: 'Divider',
116
+ })`
117
+ border-top: 1px solid var(--color-neutral-200);
118
+ margin: var(--space-1) 0;
119
+ `;
@@ -0,0 +1,14 @@
1
+ export type EnvType = 'dev' | 'test' | 'staging' | 'prod';
2
+
3
+ export type Config = {
4
+ webSocketUrl: string;
5
+ httpServerUrl: `${string}/`;
6
+ };
7
+
8
+ type DeepPartial<T> = T extends object
9
+ ? {
10
+ [P in keyof T]?: DeepPartial<T[P]>;
11
+ }
12
+ : T;
13
+
14
+ export type PartialConfig = DeepPartial<Config>;
@@ -0,0 +1,9 @@
1
+ import moment from 'moment';
2
+
3
+ export const formatDate = (
4
+ date: moment.MomentInput,
5
+ format: string = 'DD/MM/YYYY'
6
+ ): string => {
7
+ const m = moment.utc(date);
8
+ return m.isValid() ? m.format(format) : '';
9
+ };
@@ -0,0 +1,14 @@
1
+ let scrollBarSize: number[] | undefined = undefined;
2
+
3
+ export const getScrollBarSize = () => {
4
+ if (!scrollBarSize) {
5
+ const el = document.createElement('div');
6
+ el.style.cssText = 'overflow:scroll; visibility:hidden; position:absolute;';
7
+ document.body.appendChild(el);
8
+ const width = el.offsetWidth - el.clientWidth;
9
+ const height = el.offsetHeight - el.clientHeight;
10
+ el.remove();
11
+ scrollBarSize = [width, height];
12
+ }
13
+ return scrollBarSize;
14
+ };
@@ -0,0 +1,26 @@
1
+ export const formatMoney = (number: number, decimals = 2, currency = 'EUR') =>
2
+ new Intl.NumberFormat(navigator.language, {
3
+ style: 'currency',
4
+ currency,
5
+ minimumFractionDigits: decimals,
6
+ maximumFractionDigits: decimals,
7
+ }).format(number);
8
+
9
+ export const formatPercentage = (number: number, decimals = 2) =>
10
+ new Intl.NumberFormat(navigator.language, {
11
+ style: 'percent',
12
+ minimumFractionDigits: decimals,
13
+ maximumFractionDigits: decimals,
14
+ }).format(number);
15
+
16
+ export const formatNumber = (number: number, decimals = 2) =>
17
+ new Intl.NumberFormat(navigator.language, {
18
+ minimumFractionDigits: decimals,
19
+ maximumFractionDigits: decimals,
20
+ }).format(number);
21
+
22
+ export const formatNumberInvariant = (number: number, decimals = 2) =>
23
+ new Intl.NumberFormat('es-US', {
24
+ minimumFractionDigits: decimals,
25
+ maximumFractionDigits: decimals,
26
+ }).format(number);
@@ -0,0 +1,2 @@
1
+ export * from './useElementSize';
2
+ export * from './useWindowSize';
@@ -0,0 +1,24 @@
1
+ import { useEffect, useState } from 'react';
2
+
3
+ export const useElementSize = (element: HTMLElement | null) => {
4
+ const [size, setSize] = useState({ width: 0, height: 0 });
5
+
6
+ useEffect(() => {
7
+ if (!element) {
8
+ return;
9
+ }
10
+
11
+ const observer = new ResizeObserver((entries) => {
12
+ const entry = entries[0];
13
+ const { width, height } = entry.contentRect;
14
+ setSize({ width, height });
15
+ });
16
+
17
+ observer.observe(element);
18
+ return () => {
19
+ observer.disconnect();
20
+ };
21
+ }, [element]);
22
+
23
+ return size;
24
+ };
@@ -0,0 +1,20 @@
1
+ import { useEffect, useState } from 'react';
2
+
3
+ export const useWindowSize = () => {
4
+ const [size, setSize] = useState({ width: 0, height: 0 });
5
+
6
+ useEffect(() => {
7
+ const handleResize = () => {
8
+ setSize({ width: window.outerWidth, height: window.outerHeight });
9
+ };
10
+
11
+ window.addEventListener('resize', handleResize);
12
+ handleResize();
13
+
14
+ return () => {
15
+ window.removeEventListener('resize', handleResize);
16
+ };
17
+ }, []);
18
+
19
+ return size;
20
+ };
package/src/index.ts ADDED
@@ -0,0 +1,7 @@
1
+ export * from './components';
2
+ export * from './providers';
3
+
4
+ export * from './Icons';
5
+
6
+ export * from './config';
7
+ export * from './services';
@@ -0,0 +1,54 @@
1
+ import * as styles from './styles';
2
+
3
+ import {
4
+ FC,
5
+ PropsWithChildren,
6
+ ReactNode,
7
+ ReactPortal,
8
+ createContext,
9
+ useCallback,
10
+ useMemo,
11
+ useRef,
12
+ } from 'react';
13
+
14
+ import { createPortal } from 'react-dom';
15
+
16
+ export type PortalProps = {
17
+ key: string;
18
+ children: ReactNode;
19
+ };
20
+
21
+ export type PortalsContextType = {
22
+ createPortal: (children: ReactNode) => ReactPortal | null;
23
+ };
24
+
25
+ export const PortalsContext = createContext<PortalsContextType>({
26
+ createPortal: () => null,
27
+ });
28
+
29
+ export const PortalsProvider: FC<PropsWithChildren> = ({ children }) => {
30
+ const containerRef = useRef<HTMLDivElement | null>(null);
31
+
32
+ const createLocalPortal = useCallback(
33
+ (children: ReactNode) =>
34
+ createPortal(
35
+ <styles.PortalContainer>{children}</styles.PortalContainer>,
36
+ containerRef.current!
37
+ ) ?? null,
38
+ []
39
+ );
40
+
41
+ const value = useMemo(
42
+ () => ({
43
+ createPortal: createLocalPortal,
44
+ }),
45
+ [createLocalPortal]
46
+ );
47
+
48
+ return (
49
+ <PortalsContext.Provider value={value}>
50
+ {children}
51
+ <styles.PortalsWrapper ref={containerRef} />
52
+ </PortalsContext.Provider>
53
+ );
54
+ };
@@ -0,0 +1,27 @@
1
+ import styled from 'styled-components';
2
+
3
+ export const BackdropContainer = styled.div.attrs({
4
+ className: 'BackdropContainer',
5
+ })`
6
+ position: fixed;
7
+ top: 0;
8
+ left: 0;
9
+ right: 0;
10
+ bottom: 0;
11
+ z-index: 1000;
12
+ background-color: rgba(0, 0, 0, 0.5);
13
+ display: flex;
14
+ justify-content: center;
15
+ align-items: center;
16
+ `;
17
+
18
+ export const PortalsWrapper = styled.div.attrs({
19
+ className: 'PortalsWrapper',
20
+ })`
21
+ width: 0;
22
+ height: 0;
23
+ `;
24
+
25
+ export const PortalContainer = styled.div.attrs({
26
+ className: 'PortalContainer',
27
+ })``;
@@ -0,0 +1,70 @@
1
+ import {
2
+ FC,
3
+ PropsWithChildren,
4
+ createContext,
5
+ useCallback,
6
+ useEffect,
7
+ useMemo,
8
+ useState,
9
+ } from 'react';
10
+
11
+ export type Settings = Record<string, string | undefined>;
12
+
13
+ export type SettingsContextProps = {
14
+ settings: Settings;
15
+ updateSettings: (partialSettings: Settings) => void;
16
+ };
17
+
18
+ export const SettingsContext = createContext<SettingsContextProps>({
19
+ settings: {},
20
+ updateSettings: () => {},
21
+ });
22
+
23
+ export const SettingsProvider: FC<PropsWithChildren> = ({ children }) => {
24
+ const [settings, setSettings] = useState({});
25
+ const getSettings = useCallback(() => {
26
+ try {
27
+ const settingsJson = localStorage.getItem('settings');
28
+ return settingsJson ? JSON.parse(settingsJson) : {};
29
+ } catch (error) {
30
+ console.warn('Settings load error', error);
31
+ return {};
32
+ }
33
+ }, []);
34
+
35
+ const saveSettings = useCallback((settingsToSave: Settings) => {
36
+ try {
37
+ localStorage.setItem('settings', JSON.stringify(settingsToSave));
38
+ } catch (error) {
39
+ console.error('Settings save error', error);
40
+ }
41
+ }, []);
42
+
43
+ const updateSettings = useCallback(
44
+ (partialSettings: Settings) => {
45
+ const newSettings = {
46
+ ...settings,
47
+ ...partialSettings,
48
+ };
49
+ setSettings(newSettings);
50
+ saveSettings(newSettings);
51
+ },
52
+ [saveSettings, settings]
53
+ );
54
+
55
+ useEffect(() => {
56
+ const readSettings = getSettings();
57
+ setSettings(readSettings);
58
+ }, [getSettings]);
59
+
60
+ const contextValue = useMemo(
61
+ () => ({ settings, updateSettings }),
62
+ [settings, updateSettings]
63
+ );
64
+
65
+ return (
66
+ <SettingsContext.Provider value={contextValue}>
67
+ {children}
68
+ </SettingsContext.Provider>
69
+ );
70
+ };
@@ -0,0 +1,55 @@
1
+ import { Theme } from './types';
2
+ import { defaultTheme } from './defaultTheme';
3
+ import styled from 'styled-components';
4
+
5
+ type ThemeProviderProps = {
6
+ $theme?: Theme;
7
+ $darkMode?: boolean;
8
+ };
9
+
10
+ const getThemeValuesCss = (
11
+ prefix: string,
12
+ obj: Record<string, string> | undefined
13
+ ) => {
14
+ return Object.entries(obj ?? {})
15
+ .map(([key, value]) => {
16
+ return `--${prefix}-${key}: ${value};`;
17
+ })
18
+ .join('');
19
+ };
20
+
21
+ const getThemeColorsCss = (colors: Theme['colors'], darkMode = false) => {
22
+ return Object.entries(colors ?? {})
23
+ .map(([color, intensities]) =>
24
+ Object.entries(
25
+ (typeof intensities === 'string'
26
+ ? (colors ?? {})[intensities] ??
27
+ (defaultTheme.colors ?? {})[intensities]
28
+ : intensities) ?? {}
29
+ )
30
+ .map(
31
+ ([intensity, value]) =>
32
+ `--color-${color}-${
33
+ darkMode ? 1000 - Number(intensity) : intensity
34
+ }: ${value};`
35
+ )
36
+ .join('')
37
+ )
38
+ .join('');
39
+ };
40
+
41
+ export const ThemeProvider = styled.div<ThemeProviderProps>`
42
+ display: contents;
43
+ ${({ $theme = {}, $darkMode }) => {
44
+ return [
45
+ getThemeColorsCss($theme.colors, $darkMode),
46
+ getThemeValuesCss('font', $theme.fonts),
47
+ getThemeValuesCss('text', $theme.texts),
48
+ getThemeValuesCss('weight', $theme.weights),
49
+ getThemeValuesCss('space', $theme.spaces),
50
+ getThemeValuesCss('size', $theme.sizes),
51
+ getThemeValuesCss('rounded', $theme.rounded),
52
+ getThemeValuesCss('shadow', $theme.shadows),
53
+ ].join('');
54
+ }}
55
+ `;