@digigov/ui 0.33.0 → 0.34.0

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.
@@ -7,76 +7,45 @@ import {
7
7
  TableHead,
8
8
  TableRow,
9
9
  TableBody,
10
- TableHeadCellSort,
10
+ TableHeadCell,
11
11
  TableDataCell,
12
12
  TableNoDataRow,
13
- useSortableData,
13
+ TableSortLabel,
14
14
  } from '@digigov/ui/core/Table';
15
+ import { useSort } from '@digigov/ui/hooks/useSort';
15
16
 
16
- const headerItems = [
17
+ const headerConfig = [
17
18
  {
18
19
  name: 'product',
19
20
  label: 'Προϊόν',
20
- sortLabels: [
21
- {
22
- label: 'Αλφαβητική σειρά (Α -> Ω)',
23
- direction: 'asc',
24
- value: 1,
25
- },
26
- {
27
- label: 'Αλφαβητική σειρά (Ω -> Α)',
28
- direction: 'desc',
29
- value: -1,
30
- },
31
- ],
21
+ sortLabels: {
22
+ asc: 'Αλφαβητική σειρά (Α -> Ω)',
23
+ desc: 'Αλφαβητική σειρά (Ω -> Α)',
24
+ },
32
25
  },
33
26
  {
34
27
  name: 'price',
35
28
  label: 'Τιμή',
36
- sortLabels: [
37
- {
38
- label: 'Αύξουσα σειρά',
39
- direction: 'ascending',
40
- value: 1,
41
- },
42
- {
43
- label: 'Φθίνουσα σειρά',
44
- direction: 'descending',
45
- value: -1,
46
- },
47
- ],
29
+ sortLabels: {
30
+ asc: 'Αύξουσα σειρά',
31
+ desc: 'Φθίνουσα σειρά',
32
+ },
48
33
  },
49
34
  {
50
35
  name: 'stock',
51
36
  label: 'Στοκ',
52
- sortLabels: [
53
- {
54
- label: 'Αύξουσα σειρά',
55
- direction: 'ascending',
56
- value: 1,
57
- },
58
- {
59
- label: 'Φθίνουσα σειρά',
60
- direction: 'descending',
61
- value: -1,
62
- },
63
- ],
37
+ sortLabels: {
38
+ asc: 'Αύξουσα σειρά',
39
+ desc: 'Φθίνουσα σειρά',
40
+ },
64
41
  },
65
42
  {
66
43
  name: 'date',
67
44
  label: 'Ημερομηνία',
68
- sortLabels: [
69
- {
70
- label: 'Αύξουσα σειρά',
71
- direction: 'ascending',
72
- value: 1,
73
- },
74
- {
75
- label: 'Φθίνουσα σειρά',
76
- direction: 'descending',
77
- value: -1,
78
- },
79
- ],
45
+ sortLabels: {
46
+ asc: 'Αύξουσα σειρά',
47
+ desc: 'Φθίνουσα σειρά',
48
+ },
80
49
  },
81
50
  ];
82
51
 
@@ -133,38 +102,32 @@ const data = [
133
102
  ];
134
103
 
135
104
  export const WithSortFilters = () => {
136
- const { sortedData, sortedProps } = useSortableData(data);
105
+ const { sortedData, setSortedField, field, direction } = useSort(data);
137
106
 
138
107
  return (
139
108
  <div className="example">
140
- <TableContainer
141
- /* Possible style solution in order to display dropdown properly when we have only one table row
142
- * style={{
143
- * overflow: 'inherit',
144
- * }}
145
- */
146
- >
109
+ <TableContainer>
147
110
  <Table>
148
111
  <TableCaption>Table of products</TableCaption>
149
112
  <TableHead>
150
113
  <TableRow>
151
- {headerItems &&
152
- headerItems.length > 0 &&
153
- headerItems.map((header, index) => {
154
- return (
155
- <TableHeadCellSort
156
- key={index}
157
- {...sortedProps}
158
- headerItems={header}
114
+ {headerConfig.map((header, index) => {
115
+ return (
116
+ <TableHeadCell key={index}>
117
+ <TableSortLabel
118
+ labels={header.sortLabels}
119
+ onSort={(dir) => setSortedField(header.name, dir)}
120
+ direction={header.name === field ? direction : 0}
159
121
  >
160
122
  {header.label}
161
- </TableHeadCellSort>
162
- );
163
- })}
123
+ </TableSortLabel>
124
+ </TableHeadCell>
125
+ );
126
+ })}
164
127
  </TableRow>
165
128
  </TableHead>
166
129
  <TableBody>
167
- {sortedData && sortedData.length > 0 ? (
130
+ {sortedData.length > 0 ? (
168
131
  sortedData.map((d, index) => {
169
132
  return (
170
133
  <TableRow key={index}>
@@ -1,194 +1,112 @@
1
- import React, { useCallback, useState, useRef } from 'react';
1
+ import React, { useRef } from 'react';
2
2
  import Table from '@digigov/react-core/Table';
3
- import { useMemo } from 'react';
4
3
  import {
5
4
  Dropdown,
6
5
  DropdownButton,
7
6
  DropdownContent,
7
+ DropdownProps,
8
8
  } from '@digigov/ui/admin/Dropdown';
9
9
  import { NavVertical } from '@digigov/ui/core/NavList/NavVertical';
10
10
  import { NavVerticalItem } from '@digigov/ui/core/NavList/NavVerticalItem';
11
11
  import { CaretIcon } from '@digigov/ui/core/SvgIcon';
12
- import TableHeadCellBase, {
13
- TableHeadCellProps as TableHeadCellSortProps,
14
- } from '@digigov/react-core/TableHeadCell';
15
12
  import CaretContainer from '@digigov/ui/core/CaretContainer';
16
13
 
17
- export interface sortedProps extends TableHeadCellSortProps {
18
- sortedAscDesc?: (x: any, y: number) => void;
19
- sortedField?: string;
20
- sortedDirectionValue?: number;
21
- headerItems?: any;
22
- sortedData?: any[];
23
- }
24
-
25
- const useSortableData = (items) => {
26
- const [field, setField] = useState('');
27
- const [directionValue, setDirectionValue] = useState(0);
28
- const sortedItems = useMemo(() => {
29
- const sortableItems = [...items];
30
- if (sortableItems.length === 0) {
31
- return;
32
- }
33
- if (field) {
34
- sortableItems.sort((a, b) => {
35
- if (a[field] === null) return 1;
36
- if (b[field] === null) return -1;
37
- if (a[field] === null && b[field] === null) return 0;
38
- return (
39
- a[field].toString().localeCompare(b[field].toString(), undefined, {
40
- numeric: true,
41
- }) * (directionValue > 0 ? 1 : -1)
42
- );
43
- });
44
- }
45
- return sortableItems;
46
- }, [items, directionValue, field]);
14
+ export type SortDirection = 1 | -1 | 0;
47
15
 
48
- const sortAscDesc = (item, value) => {
49
- setDirectionValue(value);
50
- setField(item);
16
+ export interface TableSortLabelProps extends DropdownProps {
17
+ labels: {
18
+ asc: string;
19
+ desc: string;
51
20
  };
21
+ disabled?: boolean;
22
+ direction?: SortDirection;
23
+ onSort: (direction: SortDirection) => void;
24
+ }
52
25
 
53
- return {
54
- sortedData: sortedItems,
55
- sortedProps: {
56
- sortedData: sortedItems,
57
- sortedDirectionValue: directionValue,
58
- sortedField: field,
59
- sortedAscDesc: sortAscDesc,
60
- },
61
- };
62
- };
63
- const TableHeadCellSort = React.forwardRef<HTMLTableCellElement, sortedProps>(
64
- function TableHeadCellSort({ ...props }, ref) {
65
- return <TableHeadCellSortBase ref={ref} {...props} />;
66
- }
67
- );
68
-
69
- export const TableHeadCellSortBase = React.forwardRef<
70
- HTMLTableCellElement,
71
- sortedProps
72
- >(function TableHeadCellSortBase(
73
- {
74
- sortedAscDesc,
75
- sortedField,
76
- sortedDirectionValue,
77
- headerItems,
78
- sortedData,
79
- children,
80
- ...props
81
- },
26
+ export const TableSortLabel = React.forwardRef<
27
+ HTMLDetailsElement,
28
+ TableSortLabelProps
29
+ >(function TableSortLabel(
30
+ { labels, disabled, children, direction = 0, onSort, ...props },
82
31
  ref
83
32
  ) {
84
- const innerRef = useRef() as React.MutableRefObject<HTMLDetailsElement>;
85
- const { name, sortLabels } = headerItems || {};
86
- const [sortDirection, setSortDirection] = useState(0);
87
- const setSortAscDesc = useCallback(
88
- (name, value) => {
89
- if (sortedAscDesc) {
90
- if (sortedField !== name) {
91
- sortedAscDesc('', 0);
92
-
93
- setSortDirection(value);
94
- sortedAscDesc(name, value);
95
- } else {
96
- if (value !== sortDirection) {
97
- setSortDirection(value);
98
- sortedAscDesc(name, value);
99
- } else {
100
- setSortDirection(0);
101
- sortedAscDesc('', 0);
102
- }
103
- }
104
- }
105
- },
106
- [sortedField, sortedDirectionValue, sortDirection]
107
- );
108
- const onKeyDown = useCallback(
109
- (e, name, value) => {
110
- if (e.key === 'Enter' && innerRef.current.contains(e.target)) {
111
- if (innerRef.current.open) {
112
- setSortAscDesc(name, value);
113
- innerRef.current.open = false;
114
- }
115
- }
116
- },
117
- [name, sortDirection]
118
- );
119
- const setDisabled = useCallback(
120
- (sortedData) => {
121
- return sortedData === undefined || sortedData.length === 0 ? true : false;
122
- },
123
- [sortedData]
124
- );
125
- const setCaretColor = useCallback(
126
- (value: number) => {
127
- if (sortedField === name) {
128
- return sortedDirectionValue === value ? 'dark' : 'gray';
129
- } else {
130
- return 'gray';
131
- }
132
- },
133
- [sortedDirectionValue, sortedField]
134
- );
33
+ // TODO: this is a workaround for the dropdown component
34
+ // https://itnext.io/reusing-the-ref-from-forwardref-with-react-hooks-4ce9df693dd
35
+ const dropdownRef = useRef<HTMLDetailsElement | null>(null);
36
+ const active = [-1, 1].includes(direction);
135
37
  return (
136
- <TableHeadCellBase {...props} ref={ref}>
137
- <Dropdown tabIndex={0} disabled={setDisabled(sortedData)} ref={innerRef}>
138
- <DropdownButton
139
- variant="link"
140
- underline={sortedField === name ? true : false}
141
- >
142
- {children}
143
- <CaretContainer marginLeft={1}>
144
- {sortLabels &&
145
- sortLabels.length > 0 &&
146
- sortLabels.map((obj, index) => {
147
- return (
148
- <CaretIcon
149
- key={index}
150
- direction={obj.value == 1 ? 'up' : 'down'}
151
- size="m"
152
- variant={setCaretColor(obj.value)}
153
- />
154
- );
155
- })}
156
- </CaretContainer>
157
- </DropdownButton>
158
- <DropdownContent>
159
- {sortLabels &&
160
- sortLabels.length > 0 &&
161
- sortLabels.map((obj, index) => {
162
- return (
163
- <NavVertical key={index}>
164
- <NavVerticalItem
165
- {...(sortedField === name &&
166
- sortedDirectionValue === obj.value && { active: true })}
167
- tabIndex={0}
168
- onClick={() => {
169
- if (setSortAscDesc) {
170
- setSortAscDesc(name, obj.value);
171
- innerRef.current.open = false;
172
- }
173
- }}
174
- onKeyDown={(e) => {
175
- onKeyDown(e, name, obj.value);
176
- }}
177
- >
178
- <CaretIcon
179
- direction={obj.value == 1 ? 'up' : 'down'}
180
- marginRight={1}
181
- marginTop={1}
182
- size="m"
183
- />
184
- {obj.label}
185
- </NavVerticalItem>
186
- </NavVertical>
187
- );
188
- })}
189
- </DropdownContent>
190
- </Dropdown>
191
- </TableHeadCellBase>
38
+ <Dropdown
39
+ ref={(el) => {
40
+ dropdownRef.current = el;
41
+ typeof ref === 'function' && ref(el);
42
+ }}
43
+ disabled={disabled}
44
+ {...props}
45
+ >
46
+ <DropdownButton variant="link" tabIndex={0} underline={active}>
47
+ {children}
48
+ <CaretContainer marginLeft={1}>
49
+ <CaretIcon
50
+ direction={'up'}
51
+ size="m"
52
+ variant={direction === 1 ? 'dark' : 'gray'}
53
+ />
54
+ <CaretIcon
55
+ direction={'down'}
56
+ size="m"
57
+ variant={direction === -1 ? 'dark' : 'gray'}
58
+ />
59
+ </CaretContainer>
60
+ </DropdownButton>
61
+ <DropdownContent>
62
+ <NavVertical>
63
+ <NavVerticalItem
64
+ active={direction === 1}
65
+ tabIndex={0}
66
+ onClick={() => {
67
+ onSort(1);
68
+ dropdownRef.current && (dropdownRef.current.open = false);
69
+ }}
70
+ onKeyDown={(e) => {
71
+ if (e.key === 'Enter') {
72
+ onSort(1);
73
+ dropdownRef.current && (dropdownRef.current.open = false);
74
+ }
75
+ }}
76
+ >
77
+ <CaretIcon
78
+ direction={'up'}
79
+ marginRight={1}
80
+ marginTop={1}
81
+ size="m"
82
+ />
83
+ {labels.asc}
84
+ </NavVerticalItem>
85
+ <NavVerticalItem
86
+ active={direction === -1}
87
+ tabIndex={0}
88
+ onClick={() => {
89
+ onSort(-1);
90
+ dropdownRef.current && (dropdownRef.current.open = false);
91
+ }}
92
+ onKeyDown={(e) => {
93
+ if (e.key === 'Enter') {
94
+ onSort(-1);
95
+ dropdownRef.current && (dropdownRef.current.open = false);
96
+ }
97
+ }}
98
+ >
99
+ <CaretIcon
100
+ direction={'down'}
101
+ marginRight={1}
102
+ marginTop={1}
103
+ size="m"
104
+ />
105
+ {labels.desc}
106
+ </NavVerticalItem>
107
+ </NavVertical>
108
+ </DropdownContent>
109
+ </Dropdown>
192
110
  );
193
111
  });
194
112
 
@@ -202,5 +120,4 @@ export * from '@digigov/react-core/TableHeadCell';
202
120
  export * from '@digigov/react-core/TableRow';
203
121
  export * from '@digigov/react-core/TableNoDataRow';
204
122
  export * from '@digigov/ui/core/Table/TableFloatingScroll';
205
- export { TableHeadCellSort, useSortableData };
206
123
  export default Table;
@@ -0,0 +1,53 @@
1
+ import { useState } from 'react';
2
+ import { useMemo } from 'react';
3
+ import { SortDirection } from '@digigov/ui/core/Table';
4
+
5
+ export interface UseSortResult {
6
+ sortedData: any[];
7
+ setSortedField: (item: string, value: SortDirection) => void;
8
+ field: string;
9
+ direction: SortDirection;
10
+ }
11
+ export type SortData = Record<string, any>[];
12
+ export const useSort = (data: SortData): UseSortResult => {
13
+ const [sortDataBy, setSortDataBy] = useState('');
14
+ const [direction, setDirection] = useState<-1 | 1 | 0>(0);
15
+ const sortedItems = useMemo(() => {
16
+ const sortableItems = [...data];
17
+ if (sortableItems.length === 0) {
18
+ return [];
19
+ }
20
+ if (sortDataBy) {
21
+ sortableItems.sort((a, b) => {
22
+ if (a[sortDataBy] === null) return 1;
23
+ if (b[sortDataBy] === null) return -1;
24
+ if (a[sortDataBy] === null && b[sortDataBy] === null) return 0;
25
+ return (
26
+ a[sortDataBy]
27
+ .toString()
28
+ .localeCompare(b[sortDataBy].toString(), undefined, {
29
+ numeric: true,
30
+ }) * (direction > 0 ? 1 : -1)
31
+ );
32
+ });
33
+ }
34
+ return sortableItems;
35
+ }, [data, direction, sortDataBy]);
36
+
37
+ const setSortedField = (item, value) => {
38
+ if (item === sortDataBy && value === direction) {
39
+ setDirection(0);
40
+ setSortDataBy('');
41
+ return;
42
+ }
43
+ setDirection(value);
44
+ setSortDataBy(item);
45
+ };
46
+
47
+ return {
48
+ sortedData: sortedItems,
49
+ setSortedField,
50
+ field: sortDataBy,
51
+ direction,
52
+ };
53
+ };