@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,96 @@
1
+ import styled from 'styled-components';
2
+
3
+ export const DEFAULT_FILTER_ROW_HEIGHT = 24;
4
+
5
+ export const InputContainer = styled.div.attrs({
6
+ className: 'InputContainer',
7
+ })`
8
+ position: relative;
9
+ border-radius: var(--rounded-md);
10
+ padding: 0 var(--space-1);
11
+
12
+ svg {
13
+ position: absolute;
14
+ top: 50%;
15
+ transform: translateY(-50%);
16
+ left: var(--space-4);
17
+ display: flex;
18
+ align-items: center;
19
+ fill: var(--color-neutral-400);
20
+ width: var(--space-4);
21
+ height: var(--space-4);
22
+ }
23
+
24
+ input {
25
+ padding-left: var(--space-8);
26
+ }
27
+ `;
28
+
29
+ export const CheckboxesContainer = styled.div.attrs({
30
+ className: 'CheckboxesContainer',
31
+ })`
32
+ font-weight: normal;
33
+ user-select: none;
34
+ padding: var(--space-2);
35
+ margin: var(--space-1);
36
+ margin-bottom: 0;
37
+ border: 1px solid var(--color-neutral-300);
38
+ border-radius: var(--rounded-md);
39
+ box-shadow: var(--shadow-inner);
40
+ background-color: var(--color-neutral-0);
41
+ height: 20em;
42
+ overflow-y: hidden;
43
+ white-space: nowrap;
44
+ `;
45
+
46
+ export const Separator = styled.div.attrs({
47
+ className: 'Separator',
48
+ })`
49
+ border-top: 1px solid var(--color-neutral-200);
50
+ margin: var(--space-1) 0;
51
+ `;
52
+
53
+ export const FilterValueContainer = styled.div.attrs({
54
+ className: 'FilterValueContainer',
55
+ })`
56
+ position: absolute;
57
+ left: 0;
58
+ right: 0;
59
+ display: flex;
60
+ flex-direction: row;
61
+ align-items: center;
62
+ cursor: pointer;
63
+ &:hover {
64
+ background-color: var(--color-neutral-50);
65
+ }
66
+ `;
67
+
68
+ export const FilterValuesScrollerContainer = styled.div.attrs({
69
+ className: 'FilterValuesScrollerContainer',
70
+ })<{
71
+ $rowHeight?: number;
72
+ }>`
73
+ display: block;
74
+ font-size: var(--text-base);
75
+ background-color: var(--color-neutral-0);
76
+ overflow-y: scroll;
77
+ overflow-x: hidden;
78
+ height: 100%;
79
+
80
+ & > div {
81
+ position: relative;
82
+ }
83
+
84
+ & ${FilterValueContainer} {
85
+ position: absolute;
86
+ display: flex;
87
+ flex-direction: row;
88
+ align-items: center;
89
+ height: ${({ $rowHeight = DEFAULT_FILTER_ROW_HEIGHT }) =>
90
+ `${$rowHeight}px`};
91
+
92
+ input[type='checkbox'] {
93
+ margin-right: var(--space-1);
94
+ }
95
+ }
96
+ `;
@@ -0,0 +1,42 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ /* eslint-disable @typescript-eslint/no-unnecessary-type-constraint */
3
+
4
+ import * as styles from './styles';
5
+
6
+ import { DataGridContext } from './types';
7
+ import { useDataGridContext } from './hooks';
8
+
9
+ export const DataGridFooter = <R,>({
10
+ context,
11
+ }: {
12
+ context: DataGridContext<R>;
13
+ }) => {
14
+ const {
15
+ visibleColumns,
16
+ rows,
17
+ selectedRows,
18
+ sortedRows,
19
+ selectable,
20
+ footers = {},
21
+ gridTemplateColumns,
22
+ footerFunctions,
23
+ } = useDataGridContext(context);
24
+
25
+ if (!Object.keys(footers).length) {
26
+ return null;
27
+ }
28
+
29
+ return (
30
+ <styles.DataGridFooterRow $gridTemplateColumns={gridTemplateColumns}>
31
+ {!!selectable && <styles.HeaderSelectionCell />}
32
+ {visibleColumns.map(([key, col]) => (
33
+ <styles.DataGridHeaderCellContainer
34
+ key={key}
35
+ style={{ width: (col.width ?? 150) + 'px' }}
36
+ >
37
+ {footerFunctions?.[key]?.(rows, sortedRows, selectedRows)}
38
+ </styles.DataGridHeaderCellContainer>
39
+ ))}
40
+ </styles.DataGridFooterRow>
41
+ );
42
+ };
@@ -0,0 +1,126 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ /* eslint-disable @typescript-eslint/no-unnecessary-type-constraint */
3
+
4
+ import * as styles from './styles';
5
+
6
+ import {
7
+ ArrowsRotateIcon,
8
+ CopyIcon,
9
+ FilterSlashIcon,
10
+ TableColumnsIcon,
11
+ } from '../../../Icons';
12
+ import { useCallback, useState } from 'react';
13
+
14
+ import { Button } from '../../forms';
15
+ import { DataGridContext } from './types';
16
+ import { DataGridHeaderCell } from './DataGridHeaderCell';
17
+ import { IndeterminateCheckbox } from '../../forms/IndeterminateCheckbox';
18
+ import { Loading } from '../../layout';
19
+ import { useDataGridColumnsModal } from './DataGridColumnsModal/hooks';
20
+ import { useDataGridContext } from './hooks';
21
+
22
+ export const DataGridHeader = <R,>({
23
+ context,
24
+ }: {
25
+ context: DataGridContext<R>;
26
+ }) => {
27
+ const {
28
+ name,
29
+ visibleColumns,
30
+ selectable,
31
+ rows,
32
+ selectedKeys,
33
+ setSelectedKeys,
34
+ copyTable,
35
+ setFilters,
36
+ refresh,
37
+ headerColor,
38
+ rowKeyGetter,
39
+ gridTemplateColumns,
40
+ getAllIds,
41
+ } = useDataGridContext(context);
42
+ const [visibleFilter, setVisibleFilter] = useState<string | undefined>();
43
+
44
+ const { openModal, modal } = useDataGridColumnsModal<R>(context);
45
+
46
+ const checkboxStatus =
47
+ selectedKeys.length === 0
48
+ ? false
49
+ : selectedKeys.length === rows.length
50
+ ? true
51
+ : undefined;
52
+ const toggleAll = useCallback(
53
+ async (newStatus: boolean) => {
54
+ const allIds = getAllIds ? await getAllIds() : rows.map(rowKeyGetter);
55
+ setSelectedKeys(newStatus ? allIds : []);
56
+ },
57
+ [getAllIds, rowKeyGetter, rows, setSelectedKeys]
58
+ );
59
+
60
+ const onFilterButtonClicked = useCallback((columnKey: string) => {
61
+ setVisibleFilter((prev) => (prev === columnKey ? undefined : columnKey));
62
+ }, []);
63
+
64
+ const [isLoadingVisible, setIsLoadingVisible] = useState(false);
65
+ const runCopyTable = useCallback(() => {
66
+ setIsLoadingVisible(true);
67
+ copyTable().then(() => setIsLoadingVisible(false));
68
+ }, [copyTable]);
69
+
70
+ const toolsRow = (
71
+ <styles.DataGridToolsRow>
72
+ <Loading visible={isLoadingVisible} />
73
+ {refresh && (
74
+ <Button size="small" onClick={refresh}>
75
+ <ArrowsRotateIcon />
76
+ Rafraîchir
77
+ </Button>
78
+ )}
79
+ <Button color="emerald" size="small" onClick={runCopyTable}>
80
+ <CopyIcon />
81
+ Copier la table
82
+ </Button>
83
+ <Button size="small" color="danger" onClick={() => setFilters({})}>
84
+ <FilterSlashIcon />
85
+ Supprimer les filtres
86
+ </Button>
87
+ {name && (
88
+ <Button color="info" size="small" onClick={openModal}>
89
+ <TableColumnsIcon />
90
+ Paramètres des colonnes
91
+ </Button>
92
+ )}
93
+ </styles.DataGridToolsRow>
94
+ );
95
+
96
+ return (
97
+ <>
98
+ {modal}
99
+ {toolsRow}
100
+ <styles.DataGridHeaderRow
101
+ $gridTemplateColumns={gridTemplateColumns}
102
+ $headerColor={headerColor}
103
+ >
104
+ {!!selectable && (
105
+ <styles.HeaderSelectionCell
106
+ $headerColor={headerColor}
107
+ onClick={() => toggleAll(!(checkboxStatus ?? true))}
108
+ >
109
+ <IndeterminateCheckbox checked={checkboxStatus} readOnly />
110
+ </styles.HeaderSelectionCell>
111
+ )}
112
+ {visibleColumns.map(([key, col], index) => (
113
+ <DataGridHeaderCell
114
+ key={key}
115
+ columnKey={key}
116
+ column={col}
117
+ context={context}
118
+ columnIndex={index}
119
+ isFilterOpen={visibleFilter === key}
120
+ onFilterButtonClicked={onFilterButtonClicked}
121
+ />
122
+ ))}
123
+ </styles.DataGridHeaderRow>
124
+ </>
125
+ );
126
+ };
@@ -0,0 +1,132 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ /* eslint-disable @typescript-eslint/no-unnecessary-type-constraint */
3
+
4
+ import * as styles from './styles';
5
+
6
+ import { MouseEvent, useCallback, useMemo, useRef, useState } from 'react';
7
+
8
+ import { ChevronDownIcon } from '../../../Icons';
9
+ import { DataGridFilterMenu } from './DataGridFilterMenu';
10
+ import { DataGridHeaderCellProps } from './types';
11
+ import { Dropdown } from '../../layout';
12
+ import { IconButton } from '../../forms/IconButton';
13
+ import { useDataGridContext } from './hooks';
14
+ import { useUi } from '../../../providers';
15
+
16
+ export const DataGridHeaderCell = <R,>({
17
+ columnKey,
18
+ columnIndex,
19
+ column,
20
+ context,
21
+ }: DataGridHeaderCellProps<R>) => {
22
+ const { getElementScreenRect } = useUi();
23
+ const contextValue = useDataGridContext(context);
24
+ const {
25
+ filters = {},
26
+ setColumnWidth,
27
+ saveSettings,
28
+ headerColor,
29
+ } = contextValue;
30
+ const filterButtonRef = useRef<HTMLButtonElement | null>(null);
31
+
32
+ /** RESIZING */
33
+
34
+ const [isResizing, setIsResizing] = useState(false);
35
+ const resizingOffset = useRef(0);
36
+
37
+ const onResizeStart = useCallback(
38
+ (e: MouseEvent) => {
39
+ resizingOffset.current = e.screenX - (column.width || 150);
40
+ setIsResizing(true);
41
+ },
42
+ [column.width]
43
+ );
44
+
45
+ const onResizeMove = useCallback(
46
+ (e: MouseEvent) => {
47
+ if (isResizing) {
48
+ const newWidth = Math.max(86, e.screenX - resizingOffset.current);
49
+ setColumnWidth(columnKey, newWidth);
50
+ }
51
+ },
52
+ [columnKey, isResizing, setColumnWidth]
53
+ );
54
+
55
+ const onResizeEnd = useCallback(() => {
56
+ setIsResizing(false);
57
+ saveSettings();
58
+ }, [saveSettings]);
59
+
60
+ const [isFilterDropdownVisible, setIsFilterDropdownVisible] = useState(false);
61
+ const filterDropdown = useMemo(() => {
62
+ if (!isFilterDropdownVisible || !filterButtonRef.current || !columnKey) {
63
+ return null;
64
+ }
65
+ const filterButtonRect = getElementScreenRect(filterButtonRef.current);
66
+ filterButtonRect.x += window.scrollX;
67
+ filterButtonRect.y += window.scrollY;
68
+ return (
69
+ <Dropdown
70
+ $sourceRect={filterButtonRect}
71
+ onClose={() => setIsFilterDropdownVisible(false)}
72
+ $width={320}
73
+ $height={[250, 400]}
74
+ $autoPositionX
75
+ >
76
+ <DataGridFilterMenu
77
+ columnKey={columnKey}
78
+ columnIndex={columnIndex}
79
+ context={context}
80
+ onClose={() => setIsFilterDropdownVisible(false)}
81
+ />
82
+ </Dropdown>
83
+ );
84
+ }, [
85
+ columnIndex,
86
+ columnKey,
87
+ context,
88
+ getElementScreenRect,
89
+ isFilterDropdownVisible,
90
+ ]);
91
+
92
+ const onFilterButtonClicked = useCallback(() => {
93
+ setIsFilterDropdownVisible(true);
94
+ }, []);
95
+
96
+ const hasFilters = filters[columnKey]?.values.length > 0;
97
+
98
+ return (
99
+ <styles.DataGridHeaderCellContainer
100
+ $headerColor={headerColor}
101
+ $isResizing={isResizing}
102
+ >
103
+ {filterDropdown}
104
+ <span>{column.name}</span>
105
+ {!!column.filter && (
106
+ <IconButton
107
+ size="small"
108
+ className={hasFilters ? 'danger' : ''}
109
+ ref={filterButtonRef}
110
+ icon={ChevronDownIcon}
111
+ color={hasFilters ? 'danger' : headerColor}
112
+ onClick={onFilterButtonClicked}
113
+ />
114
+ )}
115
+ {column.resizable !== false && (
116
+ <>
117
+ <styles.DataGridResizeGrip
118
+ className={isResizing ? 'active' : ''}
119
+ $headerColor={headerColor}
120
+ onMouseDown={onResizeStart}
121
+ />
122
+ {isResizing && (
123
+ <styles.ResizeBackdrop
124
+ onMouseMove={onResizeMove}
125
+ onMouseUp={onResizeEnd}
126
+ />
127
+ )}
128
+ </>
129
+ )}
130
+ </styles.DataGridHeaderCellContainer>
131
+ );
132
+ };
@@ -0,0 +1,136 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ /* eslint-disable @typescript-eslint/no-unnecessary-type-constraint */
3
+
4
+ import * as styles from './styles';
5
+
6
+ import {
7
+ DataGridFilter,
8
+ DataGridFilterOperator,
9
+ DataGridFilterType,
10
+ } from '../types';
11
+ import { Input, Select } from '../../../forms';
12
+ import { useEffect, useRef } from 'react';
13
+
14
+ // import { Select } from '../../Select';
15
+ // import clx from 'classnames';
16
+
17
+ const filterOperators: {
18
+ [K in DataGridFilterType]: Partial<{
19
+ [K2 in DataGridFilterOperator<K>]: string;
20
+ }>;
21
+ } = {
22
+ date: {
23
+ after: 'Après',
24
+ before: 'Avant',
25
+ equals: 'Égal à',
26
+ notEquals: 'Différent de',
27
+ inRange: "Dans l'intervalle",
28
+ },
29
+ number: {
30
+ equals: 'Égal à',
31
+ notEquals: 'Différent de',
32
+ greaterThan: 'Supérieur à',
33
+ greaterThanOrEqual: 'Supérieur ou égal à',
34
+ lessThan: 'Inférieur à',
35
+ lessThanOrEqual: 'Inférieur ou égal à',
36
+ inRange: "Dans l'intervalle",
37
+ },
38
+ text: {
39
+ equals: 'Égal à',
40
+ notEquals: 'Différent de',
41
+ contains: 'Contient',
42
+ notContains: 'Ne contient pas',
43
+ startsWith: 'Commence par',
44
+ endsWith: 'Finit par',
45
+ },
46
+ };
47
+
48
+ type FilterModalContentProps = {
49
+ filter: DataGridFilter;
50
+ onFilterChanged: (filter: DataGridFilter) => void;
51
+ };
52
+
53
+ export const FilterModalContent = ({
54
+ filter,
55
+ onFilterChanged,
56
+ }: FilterModalContentProps) => {
57
+ const value1Ref = useRef<HTMLInputElement>(null);
58
+ useEffect(() => {
59
+ setTimeout(() => value1Ref.current?.focus(), 100);
60
+ }, []);
61
+
62
+ if (!filter) return null;
63
+
64
+ const { operator, type, values } = filter;
65
+ return (
66
+ <styles.FilterModalContentContainer>
67
+ <label htmlFor="filterOperator">
68
+ <span>Opérateur :</span>
69
+ <Select
70
+ name="filterOperator"
71
+ items={Object.entries(filterOperators[filter.type])}
72
+ itemKey={0}
73
+ itemLabel={1}
74
+ value={String(operator)}
75
+ className="text-sm"
76
+ onChange={(e) =>
77
+ onFilterChanged({ ...filter, operator: e.target.value as any })
78
+ }
79
+ />
80
+ </label>
81
+ <label htmlFor="filterValue1">
82
+ <span>{operator === 'inRange' ? 'Entre' : 'Valeur'} :</span>
83
+ <Input
84
+ ref={value1Ref}
85
+ name="filterValue1"
86
+ type={filter.type}
87
+ value={values[0] ?? ''}
88
+ onChange={(e) => {
89
+ if (type === 'number') {
90
+ const newValues = [...values];
91
+ newValues[0] = Number(e.target.value);
92
+ onFilterChanged({
93
+ ...filter,
94
+ values: newValues,
95
+ });
96
+ } else {
97
+ const newValues = [...values];
98
+ newValues[0] = String(e.target.value) as any;
99
+ onFilterChanged({
100
+ ...filter,
101
+ values: newValues,
102
+ });
103
+ }
104
+ }}
105
+ />
106
+ </label>
107
+ {operator === 'inRange' && (
108
+ <label htmlFor="filterValue2">
109
+ <span>et</span>
110
+ <Input
111
+ name="filterValue2"
112
+ type={filter.type}
113
+ value={values[1] ?? ''}
114
+ onChange={(e) => {
115
+ if (type === 'number') {
116
+ const newValues = [...values];
117
+ newValues[1] = Number(e.target.value);
118
+ onFilterChanged({
119
+ ...filter,
120
+ values: newValues,
121
+ });
122
+ } else {
123
+ const newValues = [...values];
124
+ newValues[1] = String(e.target.value) as any;
125
+ onFilterChanged({
126
+ ...filter,
127
+ values: newValues,
128
+ });
129
+ }
130
+ }}
131
+ />
132
+ </label>
133
+ )}
134
+ </styles.FilterModalContentContainer>
135
+ );
136
+ };
@@ -0,0 +1,22 @@
1
+ import styled from 'styled-components';
2
+
3
+ export const FilterModalContentContainer = styled.div.attrs({
4
+ className: 'FilterModalContentContainer',
5
+ })`
6
+ display: flex;
7
+ flex-direction: column;
8
+ font-size: var(--text-base);
9
+ gap: var(--space-1);
10
+
11
+ & > label {
12
+ display: flex;
13
+ flex-direction: row;
14
+ gap: var(--space-1);
15
+ align-items: center;
16
+
17
+ & > span {
18
+ font-weight: bold;
19
+ width: var(--space-32);
20
+ }
21
+ }
22
+ `;
@@ -0,0 +1,46 @@
1
+ import * as styles from './styles';
2
+
3
+ import { ReactNode, useContext } from 'react';
4
+
5
+ import { DataGridContext } from './types';
6
+
7
+ type VirtualScrollerProps<R> = {
8
+ showAllRows?: boolean;
9
+ rowTemplate: (row: R, index: number) => ReactNode;
10
+ hasFooter?: boolean;
11
+ context: DataGridContext<R>;
12
+ onRangeChange?: (startIndex: number, length: number) => void;
13
+ };
14
+
15
+ export const VirtualScroller = <R,>(props: VirtualScrollerProps<R>) => {
16
+ const {
17
+ rowHeight = styles.DEFAULT_ROW_HEIGHT,
18
+ // headerRowHeight = styles.DEFAULT_HEADER_ROW_HEIGHT,
19
+ sortedRows,
20
+ index,
21
+ visibleRows,
22
+ gridTemplateColumns,
23
+ } = useContext(props.context);
24
+ const {
25
+ rowTemplate,
26
+ // hasFooter, onRangeChange
27
+ } = props;
28
+
29
+ const totalHeight = sortedRows.length * rowHeight;
30
+ const topPadding =
31
+ Math.max(0, index - styles.VIRTUAL_SCROLL_TOLERANCE) * rowHeight;
32
+ // const headerAndFooterHeight =
33
+ // 2 * headerRowHeight + (hasFooter ? rowHeight : 0) + 2;
34
+
35
+ return (
36
+ <styles.VirtualScrollerContainer $height={totalHeight}>
37
+ <styles.VirtualScrollerRowsContainer
38
+ $gridTemplateColumns={gridTemplateColumns}
39
+ $topPadding={topPadding}
40
+ $rowHeight={rowHeight}
41
+ >
42
+ {visibleRows.map(rowTemplate)}
43
+ </styles.VirtualScrollerRowsContainer>
44
+ </styles.VirtualScrollerContainer>
45
+ );
46
+ };