@addev-be/ui 0.3.5 → 0.3.6

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": "@addev-be/ui",
3
- "version": "0.3.5",
3
+ "version": "0.3.6",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "watch": "tsc -b --watch",
@@ -0,0 +1,69 @@
1
+ import * as styles from './styles';
2
+
3
+ import { DataGridCell } from './DataGridCell';
4
+ import { DataGridRowTemplateProps } from './types';
5
+ import { useContext } from 'react';
6
+
7
+ export const DataGridRowTemplate = <R,>({
8
+ row,
9
+ rowIndex,
10
+ context,
11
+ }: DataGridRowTemplateProps<R>) => {
12
+ const { visibleColumns, rowKeyGetter, toggleSelection, ...props } =
13
+ useContext(context);
14
+
15
+ if (!row) {
16
+ return (
17
+ <styles.DataGridRow key={`loading-row-${rowIndex}`}>
18
+ {!!props.selectable && (
19
+ <styles.LoadingCell className="animate-pulse">
20
+ <div />
21
+ </styles.LoadingCell>
22
+ )}
23
+ {visibleColumns.map((_, index) => (
24
+ <styles.LoadingCell
25
+ className="animate-pulse"
26
+ key={`loading-${rowIndex}-${index}`}
27
+ >
28
+ <div />
29
+ </styles.LoadingCell>
30
+ ))}
31
+ </styles.DataGridRow>
32
+ );
33
+ }
34
+ const key = rowKeyGetter(row);
35
+ const selected = props.selectedKeys.includes(key);
36
+ const { className, style } = props.rowClassNameGetter?.(row) ?? {
37
+ className: '',
38
+ style: undefined,
39
+ };
40
+ return (
41
+ <styles.DataGridRow key={key}>
42
+ {!!props.selectable && (
43
+ <styles.SelectionCell
44
+ key="__select_checkbox__"
45
+ onClick={() => toggleSelection(key)}
46
+ >
47
+ <input
48
+ type="checkbox"
49
+ value={key as string}
50
+ checked={selected}
51
+ readOnly
52
+ />
53
+ </styles.SelectionCell>
54
+ )}
55
+ {visibleColumns.map(([key, col], index) => (
56
+ <DataGridCell
57
+ key={`loading-${rowIndex}-${index}`}
58
+ {...(index === 0 ? { className, style } : {})}
59
+ row={row}
60
+ rowIndex={rowIndex}
61
+ column={col}
62
+ columnIndex={index}
63
+ context={context}
64
+ columnKey={key}
65
+ />
66
+ ))}
67
+ </styles.DataGridRow>
68
+ );
69
+ };
@@ -1,12 +1,11 @@
1
1
  import * as styles from './styles';
2
2
 
3
- import { ReactNode, useContext } from 'react';
4
-
5
- import { DataGridContext } from './types';
3
+ import { DataGridContext, DataGridRowTemplateProps } from './types';
4
+ import { FC, useContext } from 'react';
6
5
 
7
6
  type VirtualScrollerProps<R> = {
8
7
  showAllRows?: boolean;
9
- rowTemplate: (row: R, index: number) => ReactNode;
8
+ rowTemplate: FC<DataGridRowTemplateProps<R>>;
10
9
  hasFooter?: boolean;
11
10
  context: DataGridContext<R>;
12
11
  onRangeChange?: (startIndex: number, length: number) => void;
@@ -22,7 +21,7 @@ export const VirtualScroller = <R,>(props: VirtualScrollerProps<R>) => {
22
21
  gridTemplateColumns,
23
22
  } = useContext(props.context);
24
23
  const {
25
- rowTemplate,
24
+ rowTemplate: RowTemplate,
26
25
  // hasFooter, onRangeChange
27
26
  } = props;
28
27
 
@@ -39,7 +38,9 @@ export const VirtualScroller = <R,>(props: VirtualScrollerProps<R>) => {
39
38
  $topPadding={topPadding}
40
39
  $rowHeight={rowHeight}
41
40
  >
42
- {visibleRows.map(rowTemplate)}
41
+ {visibleRows.map((row, index) => (
42
+ <RowTemplate row={row} rowIndex={index} context={props.context} />
43
+ ))}
43
44
  </styles.VirtualScrollerRowsContainer>
44
45
  </styles.VirtualScrollerContainer>
45
46
  );
@@ -92,6 +92,17 @@ export const useDataGrid = <R,>(
92
92
  onSelectionChange?.(selectedKeys);
93
93
  }, [onSelectionChange, selectedKeys]);
94
94
 
95
+ const toggleSelection = useCallback(
96
+ (key: string) => {
97
+ if (selectedKeys.includes(key)) {
98
+ setSelectedKeys(selectedKeys.filter((p) => p !== key));
99
+ } else {
100
+ setSelectedKeys([...selectedKeys, key]);
101
+ }
102
+ },
103
+ [selectedKeys, setSelectedKeys]
104
+ );
105
+
95
106
  /** ROWS FILTERING **/
96
107
 
97
108
  const [filters, setFilters] = useState<DataGridFilters>({});
@@ -240,6 +251,7 @@ export const useDataGrid = <R,>(
240
251
  footers,
241
252
  setFooters,
242
253
  footerFunctions,
254
+ toggleSelection,
243
255
  ...override,
244
256
  }),
245
257
  [
@@ -265,6 +277,7 @@ export const useDataGrid = <R,>(
265
277
  gridTemplateColumns,
266
278
  footers,
267
279
  footerFunctions,
280
+ toggleSelection,
268
281
  override,
269
282
  ]
270
283
  );
@@ -298,6 +311,7 @@ export const useDataGrid = <R,>(
298
311
  gridTemplateColumns: '',
299
312
  footers: {},
300
313
  setFooters: () => {},
314
+ toggleSelection: () => {},
301
315
  }),
302
316
  []
303
317
  );
@@ -2,11 +2,10 @@ import * as styles from './styles';
2
2
 
3
3
  import { DataGridContextProps, DataGridProps } from './types';
4
4
 
5
- import { DataGridCell } from './DataGridCell';
6
5
  import { DataGridFooter } from './DataGridFooter';
7
6
  import { DataGridHeader } from './DataGridHeader';
7
+ import { DataGridRowTemplate } from './DataGridRowTemplate';
8
8
  import { VirtualScroller } from './VirtualScroller';
9
- import { useCallback } from 'react';
10
9
  import { useDataGrid } from './hooks';
11
10
 
12
11
  /* eslint-disable @typescript-eslint/no-explicit-any */
@@ -25,95 +24,16 @@ export const DataGrid = <R,>({
25
24
  } = props;
26
25
  const [contextProps, DataGridContext] = useDataGrid(props, contextOverride);
27
26
  const {
28
- selectedKeys,
29
- setSelectedKeys,
30
27
  columns,
31
- visibleColumns,
32
28
  rowHeight = 32,
33
29
  headerRowHeight = 40,
34
30
  scrollableRef,
35
31
  onScroll,
36
- rowKeyGetter,
37
32
  } = contextProps;
38
33
 
39
34
  const hasFooter = Object.values(columns).some((col) => col.footer);
40
35
 
41
- const toggleSelection = useCallback(
42
- (key: string) => {
43
- if (selectedKeys.includes(key)) {
44
- setSelectedKeys(selectedKeys.filter((p) => p !== key));
45
- } else {
46
- setSelectedKeys([...selectedKeys, key]);
47
- }
48
- },
49
- [selectedKeys, setSelectedKeys]
50
- );
51
-
52
- const rowTemplate = useCallback(
53
- (row: R, rowIndex: number) => {
54
- if (!row) {
55
- return (
56
- <styles.DataGridRow key={`loading-row-${rowIndex}`}>
57
- {!!props.selectable && (
58
- <styles.LoadingCell className="animate-pulse">
59
- <div />
60
- </styles.LoadingCell>
61
- )}
62
- {visibleColumns.map((_, index) => (
63
- <styles.LoadingCell
64
- className="animate-pulse"
65
- key={`loading-${rowIndex}-${index}`}
66
- >
67
- <div />
68
- </styles.LoadingCell>
69
- ))}
70
- </styles.DataGridRow>
71
- );
72
- }
73
- const key = rowKeyGetter(row);
74
- const { className, style } = props.rowClassNameGetter?.(row) ?? {
75
- className: '',
76
- style: undefined,
77
- };
78
- return (
79
- <styles.DataGridRow key={key}>
80
- {!!props.selectable && (
81
- <styles.SelectionCell
82
- key="__select_checkbox__"
83
- onClick={() => toggleSelection(key)}
84
- >
85
- <input
86
- type="checkbox"
87
- value={key as string}
88
- checked={selectedKeys.includes(key)}
89
- readOnly
90
- />
91
- </styles.SelectionCell>
92
- )}
93
- {visibleColumns.map(([key, col], index) => (
94
- <DataGridCell
95
- key={`loading-${rowIndex}-${index}`}
96
- {...(index === 0 ? { className, style } : {})}
97
- row={row}
98
- rowIndex={rowIndex}
99
- column={col}
100
- columnIndex={index}
101
- context={DataGridContext}
102
- columnKey={key}
103
- />
104
- ))}
105
- </styles.DataGridRow>
106
- );
107
- },
108
- [
109
- DataGridContext,
110
- props,
111
- rowKeyGetter,
112
- selectedKeys,
113
- toggleSelection,
114
- visibleColumns,
115
- ]
116
- );
36
+ const rowTemplate = DataGridRowTemplate;
117
37
 
118
38
  return (
119
39
  <DataGridContext.Provider value={contextProps}>
@@ -131,6 +131,7 @@ export type DataGridContextProps<R> = DataGridProps<R> & {
131
131
  length: number;
132
132
  rowKeyGetter: (row: R) => string;
133
133
  gridTemplateColumns: string;
134
+ toggleSelection: (key: string) => void;
134
135
  };
135
136
 
136
137
  export type DataGridContext<R> = Context<DataGridContextProps<R>>;
@@ -265,3 +266,11 @@ export type DataGridFilterCheckbox = {
265
266
  values: DataGridFilterValue[];
266
267
  level: number;
267
268
  };
269
+
270
+ export type DataGridRowTemplateProps<R> = {
271
+ row: R | null;
272
+ rowIndex: number;
273
+ selected?: boolean;
274
+ toggleSelection?: () => void;
275
+ context: DataGridContext<R>;
276
+ };
@@ -15,6 +15,8 @@ export const inputStyle = css`
15
15
  }
16
16
  `;
17
17
 
18
- export const Input = styled.input`
18
+ export const Input = styled.input.attrs({
19
+ className: 'Input',
20
+ })`
19
21
  ${inputStyle}
20
22
  `;
@@ -1,8 +1,10 @@
1
+ import * as styles from './styles';
2
+
1
3
  import { SearchResults, SearchTypeDefinitions } from './types';
2
4
  import { useCallback, useEffect, useRef, useState } from 'react';
3
5
 
4
6
  import { Dropdown } from '../layout';
5
- import { QuickSearchBarInput } from './styles';
7
+ import { Input } from '../forms';
6
8
  import { QuickSearchResults } from './QuickSearchResults';
7
9
  import { useDebounce } from '@uidotdev/usehooks';
8
10
  import { useGlobalSearchRequestHandler } from '../../services';
@@ -18,8 +20,10 @@ export const QuickSearchBar = <R,>({ definitions }: QuickSearchBarProps<R>) => {
18
20
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
19
21
  const [results, setResults] = useState<SearchResults<R> | null>(null);
20
22
 
23
+ const fakeInputRef = useRef<HTMLInputElement | null>(null);
21
24
  const inputRef = useRef<HTMLInputElement | null>(null);
22
- const rect = inputRef.current?.getBoundingClientRect() ?? new DOMRect();
25
+ const rect = fakeInputRef.current?.getBoundingClientRect() ?? new DOMRect();
26
+ const destRect = new DOMRect(rect.x, rect.y, rect.width, 0);
23
27
  const globalSearch = useGlobalSearchRequestHandler();
24
28
 
25
29
  useEffect(() => {
@@ -36,41 +40,39 @@ export const QuickSearchBar = <R,>({ definitions }: QuickSearchBarProps<R>) => {
36
40
  }, [globalSearch, debouncedTerm]);
37
41
 
38
42
  const onFocus = useCallback(() => {
39
- if (term) {
40
- setDropdownVisible(true);
41
- }
42
- }, [term]);
43
-
44
- useEffect(() => {
45
- const input = inputRef.current;
46
- input?.addEventListener('focus', onFocus);
47
- return () => {
48
- input?.removeEventListener('focus', onFocus);
49
- };
50
- }, [onFocus]);
43
+ setDropdownVisible(true);
44
+ requestAnimationFrame(() => {
45
+ inputRef.current?.focus();
46
+ });
47
+ }, []);
51
48
 
52
49
  return (
53
50
  <>
54
- <QuickSearchBarInput
55
- type="text"
56
- value={term}
57
- onChange={(e) => setTerm(e.target.value)}
58
- ref={inputRef}
59
- $opened={dropdownVisible}
60
- />
61
- {results && dropdownVisible && rect && (
51
+ <Input type="text" ref={fakeInputRef} value={term} onFocus={onFocus} />
52
+ {dropdownVisible && rect && (
62
53
  <Dropdown
63
- $sourceRect={rect}
54
+ $sourceRect={destRect}
64
55
  onClose={() => setDropdownVisible(false)}
65
56
  $width={rect.width}
66
- $height={[250, 400]}
57
+ $height={[results ? 250 : rect.height, 400]}
67
58
  $autoPositionY={false}
68
59
  >
69
- <QuickSearchResults
70
- results={results}
71
- definitions={definitions}
72
- term={debouncedTerm}
73
- />
60
+ <styles.QuickSearchDropdownContainer>
61
+ <Input
62
+ type="text"
63
+ ref={inputRef}
64
+ value={term}
65
+ onChange={(e) => setTerm(e.target.value)}
66
+ onClick={(e) => e.stopPropagation()}
67
+ />
68
+ {results && (
69
+ <QuickSearchResults
70
+ results={results}
71
+ definitions={definitions}
72
+ term={debouncedTerm}
73
+ />
74
+ )}
75
+ </styles.QuickSearchDropdownContainer>
74
76
  </Dropdown>
75
77
  )}
76
78
  </>
@@ -1,41 +1,64 @@
1
- import { Input } from '../forms';
2
1
  import styled from 'styled-components';
3
2
 
4
- export const QuickSearchResultsContainer = styled.div`
3
+ export const QuickSearchDropdownContainer = styled.div.attrs({
4
+ className: 'QuickSearchDropdownContainer',
5
+ })`
6
+ display: flex;
7
+ flex-direction: column;
8
+ height: 100%;
9
+ `;
10
+
11
+ export const QuickSearchResultsContainer = styled.div.attrs({
12
+ className: 'QuickSearchResultsContainer',
13
+ })`
5
14
  display: flex;
6
15
  flex-direction: row;
7
16
  height: 100%;
17
+ flex: 1;
18
+ padding: var(--space-2) 0;
19
+ overflow: hidden;
8
20
  `;
9
21
 
10
- export const QuickSearchResultsListContainer = styled.div`
22
+ export const QuickSearchResultsListContainer = styled.div.attrs({
23
+ className: 'QuickSearchResultsListContainer',
24
+ })`
11
25
  display: flex;
12
26
  flex-direction: column;
13
27
  padding: var(--space-2);
14
- border-right: 1px solid var(--color-gray-200);
15
- flex: 3;
28
+ border-right: 1px solid var(--color-neutral-200);
29
+ flex: 1;
16
30
  overflow: auto;
17
31
  `;
18
32
 
19
- export const QuickSearchResultsTitle = styled.div`
33
+ export const QuickSearchResultsTitle = styled.div.attrs({
34
+ className: 'QuickSearchResultsTitle',
35
+ })`
20
36
  margin: 0;
21
37
  margin-bottom: var(--space-1);
22
38
  &:not(:first-child) {
23
39
  margin-top: var(--space-2);
24
40
  }
25
41
  font-weight: bold;
26
- font-size: var(--text-lg);
42
+ font-size: var(--text-sm);
43
+ text-transform: uppercase;
44
+ letter-spacing: 120%;
45
+ color: var(--color-neutral-500);
27
46
  `;
28
47
 
29
- export const QuickSearchResultsItem = styled.div`
48
+ export const QuickSearchResultsItem = styled.div.attrs({
49
+ className: 'QuickSearchResultsItem',
50
+ })`
30
51
  padding: var(--space-2) var(--space-3);
31
52
  cursor: pointer;
32
53
  border-radius: 4px;
33
54
  &:hover {
34
- background-color: var(--color-gray-100);
55
+ background-color: var(--color-neutral-100);
35
56
  }
36
57
  `;
37
58
 
38
- export const QuickSearchResultsDetailsContainer = styled.div`
59
+ export const QuickSearchResultsDetailsContainer = styled.div.attrs({
60
+ className: 'QuickSearchResultsDetailsContainer',
61
+ })`
39
62
  display: flex;
40
63
  flex-direction: column;
41
64
  padding: var(--space-2);
@@ -46,15 +69,11 @@ export const QuickSearchResultsDetailsDivider = styled.hr`
46
69
  margin: var(--space-2) 0;
47
70
  height: 1px;
48
71
  border: none;
49
- background-color: var(--color-gray-200);
72
+ background-color: var(--color-neutral-200);
50
73
  `;
51
74
 
52
- export const QuickSearchResultDetailsTitle = styled.div`
75
+ export const QuickSearchResultDetailsTitle = styled.div.attrs({
76
+ className: 'QuickSearchResultDetailsTitle',
77
+ })`
53
78
  margin: 0;
54
79
  `;
55
-
56
- export const QuickSearchBarInput = styled(Input)<{ $opened?: boolean }>`
57
- position: relative;
58
- width: 100%;
59
- z-index: ${({ $opened }) => ($opened ? 10000 : 0)};
60
- `;
@@ -45,6 +45,7 @@ export class WebSocketService {
45
45
  if (this.socket && this.socket.readyState === 1) {
46
46
  clearInterval(interval);
47
47
  this.setStatus(true);
48
+ this.sendQueue();
48
49
  this.onOpen?.();
49
50
  }
50
51
  }, 100);