kelt-ui-kit-react 2.1.0 → 2.1.1
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/dist/dataTable/dataTableColumn/DataTableColumn.d.ts +3 -1
- package/dist/index.js +2411 -2373
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/dataTable/DataTable.tsx +6 -22
- package/src/dataTable/dataTable.css +9 -0
- package/src/dataTable/dataTableColumn/DataTableColumn.tsx +68 -0
- package/src/dataTable/dataTableRow/DataTableRow.tsx +3 -1
- package/src/select/Select.tsx +1 -1
- package/src/select/select.css +28 -22
package/package.json
CHANGED
|
@@ -22,7 +22,6 @@ export type {
|
|
|
22
22
|
DataTableDateRangeFilterValue, DataTableFilterState, DataTableSortDirection,
|
|
23
23
|
DataTableSortState, FilterValue,
|
|
24
24
|
} from "./dataTable.utils";
|
|
25
|
-
const DEFAULT_FILTER_COLUMN_WIDTH = 100;
|
|
26
25
|
|
|
27
26
|
export type DataTableProps<T> = {
|
|
28
27
|
id: string;
|
|
@@ -93,9 +92,7 @@ export function DataTable<T>(props: DataTableProps<T>) {
|
|
|
93
92
|
const tableState = useDataTableState(id, groupedColumns, sourceData, onFiltersChange, onSortChange);
|
|
94
93
|
const { flatColumns: columns, sortedData: data } = tableState;
|
|
95
94
|
const actionsColumnWidth = getActionsWidth(actions, explicitActionsWidth);
|
|
96
|
-
const filterRowRef = useRef<HTMLTableRowElement>(null);
|
|
97
95
|
const groupRowRef = useRef<HTMLTableRowElement>(null);
|
|
98
|
-
const [filterHeight, setFilterHeight] = useState(0);
|
|
99
96
|
const [groupHeight, setGroupHeight] = useState(0);
|
|
100
97
|
const hasGroups = groupedColumns.some((column) => column.children?.length);
|
|
101
98
|
const stickyOffsets = useMemo(() => {
|
|
@@ -112,7 +109,6 @@ export function DataTable<T>(props: DataTableProps<T>) {
|
|
|
112
109
|
|
|
113
110
|
useLayoutEffect(() => {
|
|
114
111
|
const measure = () => {
|
|
115
|
-
setFilterHeight(filterRowRef.current?.getBoundingClientRect().height ?? 0);
|
|
116
112
|
setGroupHeight(groupRowRef.current?.getBoundingClientRect().height ?? 0);
|
|
117
113
|
};
|
|
118
114
|
measure();
|
|
@@ -121,10 +117,9 @@ export function DataTable<T>(props: DataTableProps<T>) {
|
|
|
121
117
|
return () => window.removeEventListener("resize", measure);
|
|
122
118
|
}
|
|
123
119
|
const observer = new ResizeObserver(measure);
|
|
124
|
-
if (filterRowRef.current) observer.observe(filterRowRef.current);
|
|
125
120
|
if (groupRowRef.current) observer.observe(groupRowRef.current);
|
|
126
121
|
return () => observer.disconnect();
|
|
127
|
-
}, [
|
|
122
|
+
}, [hasGroups, groupedColumns]);
|
|
128
123
|
|
|
129
124
|
const isMobile = useIsMobile();
|
|
130
125
|
const showMobileCardView = mobileCardView && isMobile;
|
|
@@ -531,8 +526,8 @@ export function DataTable<T>(props: DataTableProps<T>) {
|
|
|
531
526
|
isFilterVisible={tableState.visibleFilters.has(column.name ?? column.id)}
|
|
532
527
|
onToggleFilter={tableState.toggleFilter} onToggleSort={tableState.toggleSort}
|
|
533
528
|
sortDirection={tableState.sort?.key === (column.name ?? column.id) ? tableState.sort.direction : null}
|
|
529
|
+
filterControl={tableState.renderFilter(column)}
|
|
534
530
|
/></tr></thead></table>
|
|
535
|
-
{tableState.renderFilter(column)}
|
|
536
531
|
</div>
|
|
537
532
|
))}
|
|
538
533
|
</div>
|
|
@@ -643,29 +638,17 @@ export function DataTable<T>(props: DataTableProps<T>) {
|
|
|
643
638
|
{actions.length > 0 && <col style={{ width: actionsColumnWidth }} />}
|
|
644
639
|
</colgroup>
|
|
645
640
|
<thead>
|
|
646
|
-
{tableState.visibleFilters.size > 0 && <tr className="data-table-filter-row" ref={filterRowRef}>
|
|
647
|
-
{onSelectionChange && <th />}
|
|
648
|
-
{columns.map((column) => <th className="filter-row-cell" key={column.id}
|
|
649
|
-
style={{ position: "sticky", top: 0, zIndex: 14, background: "#f9fbfc",
|
|
650
|
-
width: column.width ?? DEFAULT_FILTER_COLUMN_WIDTH,
|
|
651
|
-
minWidth: column.width ?? DEFAULT_FILTER_COLUMN_WIDTH,
|
|
652
|
-
maxWidth: column.width ?? DEFAULT_FILTER_COLUMN_WIDTH,
|
|
653
|
-
...(column.sticky ? { left: stickyOffsets.get(column.id) } : {}) }}>
|
|
654
|
-
{tableState.renderFilter(column)}
|
|
655
|
-
</th>)}
|
|
656
|
-
{actions.length > 0 && <th />}
|
|
657
|
-
</tr>}
|
|
658
641
|
{hasGroups && <tr ref={groupRowRef} className="data-table-group-row">
|
|
659
642
|
{onSelectionChange && <th />}
|
|
660
643
|
{groupedColumns.map((column) => <th key={column.id} colSpan={flattenColumns([column]).length}
|
|
661
|
-
style={{ top:
|
|
644
|
+
style={{ top: 0 }}>{column.children?.length ? column.label : ""}</th>)}
|
|
662
645
|
{actions.length > 0 && <th />}
|
|
663
646
|
</tr>}
|
|
664
647
|
<tr>
|
|
665
648
|
{onSelectionChange && (
|
|
666
649
|
<th
|
|
667
650
|
className={`select-cell ${hideToggleAll ? "hidden" : ""}`}
|
|
668
|
-
style={{ width: "40px", textAlign: "center", top:
|
|
651
|
+
style={{ width: "40px", textAlign: "center", top: groupHeight,
|
|
669
652
|
...(stickyOffsets.size > 0 ? { left: 0, zIndex: 12 } : {}) }}
|
|
670
653
|
>
|
|
671
654
|
<Input
|
|
@@ -686,11 +669,12 @@ export function DataTable<T>(props: DataTableProps<T>) {
|
|
|
686
669
|
idx={idx}
|
|
687
670
|
onColumnClick={onColumnClick}
|
|
688
671
|
stickyLeft={stickyOffsets.get(column.id)}
|
|
689
|
-
stickyTop={
|
|
672
|
+
stickyTop={groupHeight}
|
|
690
673
|
isFilterVisible={tableState.visibleFilters.has(column.name ?? column.id)}
|
|
691
674
|
onToggleFilter={tableState.toggleFilter}
|
|
692
675
|
onToggleSort={tableState.toggleSort}
|
|
693
676
|
sortDirection={tableState.sort?.key === (column.name ?? column.id) ? tableState.sort.direction : null}
|
|
677
|
+
filterControl={tableState.renderFilter(column)}
|
|
694
678
|
/>
|
|
695
679
|
))}
|
|
696
680
|
{actions.length > 0 && (
|
|
@@ -316,6 +316,15 @@
|
|
|
316
316
|
flex: 1;
|
|
317
317
|
}
|
|
318
318
|
}
|
|
319
|
+
.data-table-filter-popover {
|
|
320
|
+
z-index: 1200;
|
|
321
|
+
padding: 8px;
|
|
322
|
+
box-sizing: border-box;
|
|
323
|
+
border: 1px solid #dce3e7;
|
|
324
|
+
border-radius: 7px;
|
|
325
|
+
background: #fff;
|
|
326
|
+
box-shadow: 0 6px 18px rgba(20, 40, 50, 0.16);
|
|
327
|
+
}
|
|
319
328
|
.data-table-group-row th {
|
|
320
329
|
height: 40px;
|
|
321
330
|
box-sizing: border-box;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { ReactNode, useLayoutEffect, useRef, useState } from "react";
|
|
2
|
+
import { createPortal } from "react-dom";
|
|
1
3
|
import { dataTableColumnsInterface } from "../dataTable.interface";
|
|
2
4
|
import { DataTableColumnFilter } from "./dataTableColumnFilter/DataTableColumnFilter";
|
|
3
5
|
import { DataTableColumnSort } from "./dataTableColumnSort/DataTableColumnSort";
|
|
@@ -13,7 +15,62 @@ type DataTableColumnProps = {
|
|
|
13
15
|
sortDirection?: DataTableSortDirection | null;
|
|
14
16
|
stickyLeft?: number;
|
|
15
17
|
stickyTop?: number;
|
|
18
|
+
filterControl?: ReactNode;
|
|
16
19
|
};
|
|
20
|
+
|
|
21
|
+
function FilterPopover({
|
|
22
|
+
anchor,
|
|
23
|
+
label,
|
|
24
|
+
children,
|
|
25
|
+
}: {
|
|
26
|
+
anchor: HTMLElement;
|
|
27
|
+
label: string;
|
|
28
|
+
children: ReactNode;
|
|
29
|
+
}) {
|
|
30
|
+
const [style, setStyle] = useState<React.CSSProperties>();
|
|
31
|
+
|
|
32
|
+
useLayoutEffect(() => {
|
|
33
|
+
const updatePosition = () => {
|
|
34
|
+
const rect = anchor.getBoundingClientRect();
|
|
35
|
+
if (!rect.width || !rect.height) {
|
|
36
|
+
setStyle(undefined);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const width = Math.min(Math.max(rect.width, 220), window.innerWidth - 24);
|
|
40
|
+
setStyle({
|
|
41
|
+
position: "fixed",
|
|
42
|
+
top: Math.min(rect.bottom + 4, window.innerHeight - 80),
|
|
43
|
+
left: Math.max(12, Math.min(rect.left, window.innerWidth - width - 12)),
|
|
44
|
+
width,
|
|
45
|
+
});
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
updatePosition();
|
|
49
|
+
window.addEventListener("resize", updatePosition);
|
|
50
|
+
window.addEventListener("scroll", updatePosition, true);
|
|
51
|
+
return () => {
|
|
52
|
+
window.removeEventListener("resize", updatePosition);
|
|
53
|
+
window.removeEventListener("scroll", updatePosition, true);
|
|
54
|
+
};
|
|
55
|
+
}, [anchor]);
|
|
56
|
+
|
|
57
|
+
return style && typeof document !== "undefined"
|
|
58
|
+
? createPortal(
|
|
59
|
+
<div
|
|
60
|
+
className="data-table-filter-popover"
|
|
61
|
+
style={style}
|
|
62
|
+
role="group"
|
|
63
|
+
aria-label={`Filtrer ${label}`}
|
|
64
|
+
onMouseDown={(event) => event.stopPropagation()}
|
|
65
|
+
onClick={(event) => event.stopPropagation()}
|
|
66
|
+
>
|
|
67
|
+
{children}
|
|
68
|
+
</div>,
|
|
69
|
+
document.body,
|
|
70
|
+
)
|
|
71
|
+
: null;
|
|
72
|
+
}
|
|
73
|
+
|
|
17
74
|
export const DataTableColumn = ({
|
|
18
75
|
column,
|
|
19
76
|
idx,
|
|
@@ -24,9 +81,12 @@ export const DataTableColumn = ({
|
|
|
24
81
|
sortDirection,
|
|
25
82
|
stickyLeft,
|
|
26
83
|
stickyTop = 0,
|
|
84
|
+
filterControl,
|
|
27
85
|
}: DataTableColumnProps) => {
|
|
86
|
+
const headerRef = useRef<HTMLTableCellElement>(null);
|
|
28
87
|
return (
|
|
29
88
|
<th
|
|
89
|
+
ref={headerRef}
|
|
30
90
|
key={`colonne-${column.id}-${idx}`}
|
|
31
91
|
style={{
|
|
32
92
|
minWidth: column.minWidth,
|
|
@@ -43,6 +103,14 @@ export const DataTableColumn = ({
|
|
|
43
103
|
{column.filterable && <DataTableColumnFilter column={column} isFilterVisible={isFilterVisible} onToggleVisibility={onToggleFilter} />}
|
|
44
104
|
{column.sortable && <DataTableColumnSort column={column} sortDirection={sortDirection} onToggleSort={onToggleSort} />}
|
|
45
105
|
</div>
|
|
106
|
+
{isFilterVisible && filterControl && headerRef.current && (
|
|
107
|
+
<FilterPopover
|
|
108
|
+
anchor={headerRef.current}
|
|
109
|
+
label={column.label}
|
|
110
|
+
>
|
|
111
|
+
{filterControl}
|
|
112
|
+
</FilterPopover>
|
|
113
|
+
)}
|
|
46
114
|
</th>
|
|
47
115
|
);
|
|
48
116
|
};
|
|
@@ -67,7 +67,9 @@ export const DataTableRow = <T,>({
|
|
|
67
67
|
column.onChange?.(next, row);
|
|
68
68
|
commit(next);
|
|
69
69
|
}}
|
|
70
|
-
style={{ minWidth: column.minWidth, maxWidth: column.maxWidth, width: column.width
|
|
70
|
+
style={{ minWidth: column.minWidth, maxWidth: column.maxWidth, width: column.width,
|
|
71
|
+
// le bouton du Select est un bloc : textAlign ne l'aligne pas
|
|
72
|
+
...(align === "right" ? { marginLeft: "auto" } : align === "center" ? { marginInline: "auto" } : {}) }} />
|
|
71
73
|
) : (
|
|
72
74
|
<input ref={inputRef} type={column.type === "image" ? "text" : column.type}
|
|
73
75
|
value={enterCommit ? draft : stringValue}
|
package/src/select/Select.tsx
CHANGED
|
@@ -145,7 +145,7 @@ export const Select = ({
|
|
|
145
145
|
}, [showOptions, disabled]);
|
|
146
146
|
|
|
147
147
|
return (
|
|
148
|
-
<div id={id} className={`select-container ${className ? className : ""}`}>
|
|
148
|
+
<div id={id} className={`select-container ${hasSelection && placeholder ? "has-selection" : ""} ${className ? className : ""}`}>
|
|
149
149
|
{label && (
|
|
150
150
|
<label htmlFor={`select-${id}`} className="select-container-label">
|
|
151
151
|
{label}
|
package/src/select/select.css
CHANGED
|
@@ -1,15 +1,6 @@
|
|
|
1
1
|
.select-container {
|
|
2
2
|
position: relative;
|
|
3
3
|
|
|
4
|
-
&::after,
|
|
5
|
-
&::before {
|
|
6
|
-
--size: 0.3rem;
|
|
7
|
-
content: "";
|
|
8
|
-
position: absolute;
|
|
9
|
-
right: 1rem;
|
|
10
|
-
pointer-events: none;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
4
|
.clear-btn {
|
|
14
5
|
position: absolute;
|
|
15
6
|
top: 50%;
|
|
@@ -18,22 +9,13 @@
|
|
|
18
9
|
align-items: center;
|
|
19
10
|
z-index: 2;
|
|
20
11
|
justify-content: center;
|
|
21
|
-
right:
|
|
12
|
+
/* à gauche de la flèche (dessinée à right: 1rem, largeur 0.6rem) */
|
|
13
|
+
right: 2rem;
|
|
22
14
|
cursor: pointer;
|
|
23
15
|
}
|
|
24
16
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
border-right: var(--size) solid transparent;
|
|
28
|
-
border-bottom: var(--size) solid black;
|
|
29
|
-
top: 40%;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
&::after {
|
|
33
|
-
border-left: var(--size) solid transparent;
|
|
34
|
-
border-right: var(--size) solid transparent;
|
|
35
|
-
border-top: var(--size) solid black;
|
|
36
|
-
top: 55%;
|
|
17
|
+
&.has-selection .select-container-button {
|
|
18
|
+
padding-right: 3.5rem;
|
|
37
19
|
}
|
|
38
20
|
|
|
39
21
|
.select-container-label {
|
|
@@ -45,6 +27,7 @@
|
|
|
45
27
|
|
|
46
28
|
.select-container-button {
|
|
47
29
|
appearance: none;
|
|
30
|
+
position: relative;
|
|
48
31
|
width: 100%;
|
|
49
32
|
font-size: 0.8rem;
|
|
50
33
|
padding: 0.5rem 1rem;
|
|
@@ -60,6 +43,29 @@
|
|
|
60
43
|
justify-content: space-between;
|
|
61
44
|
padding-right: 2rem;
|
|
62
45
|
|
|
46
|
+
&::before,
|
|
47
|
+
&::after {
|
|
48
|
+
--size: 0.3rem;
|
|
49
|
+
content: "";
|
|
50
|
+
position: absolute;
|
|
51
|
+
right: 1rem;
|
|
52
|
+
pointer-events: none;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
&::before {
|
|
56
|
+
border-left: var(--size) solid transparent;
|
|
57
|
+
border-right: var(--size) solid transparent;
|
|
58
|
+
border-bottom: var(--size) solid black;
|
|
59
|
+
top: calc(50% - var(--size));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
&::after {
|
|
63
|
+
border-left: var(--size) solid transparent;
|
|
64
|
+
border-right: var(--size) solid transparent;
|
|
65
|
+
border-top: var(--size) solid black;
|
|
66
|
+
top: calc(50% + 1px);
|
|
67
|
+
}
|
|
68
|
+
|
|
63
69
|
.select-container-value {
|
|
64
70
|
flex: 1;
|
|
65
71
|
min-width: 0;
|