kelt-ui-kit-react 2.0.9 → 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/card/cardAction/CardAction.d.ts +2 -1
- package/dist/dataTable/dataTableColumn/DataTableColumn.d.ts +3 -1
- package/dist/index.js +3063 -3010
- package/dist/overlayPanel/OverlayPanel.d.ts +1 -1
- package/dist/select/Select.d.ts +3 -1
- package/dist/style.css +1 -1
- package/package.json +2 -2
- package/src/card/cardAction/CardAction.tsx +2 -8
- package/src/dataTable/DataTable.tsx +7 -18
- package/src/dataTable/dataTable.css +110 -1
- package/src/dataTable/dataTableColumn/DataTableColumn.tsx +70 -2
- package/src/dataTable/dataTableRow/DataTableRow.tsx +3 -1
- package/src/dataTable/useDataTableState.tsx +7 -1
- package/src/overlayPanel/OverlayPanel.tsx +6 -1
- package/src/select/Select.tsx +5 -3
- package/src/select/select.css +28 -22
package/package.json
CHANGED
|
@@ -4,17 +4,11 @@ import { Icon } from "../../icon/Icon";
|
|
|
4
4
|
import { IconSizeEnum } from "../../icon/iconSize.enum";
|
|
5
5
|
import { MenuInterface } from "../../menus/menu/menu.interface";
|
|
6
6
|
import { Menus } from "../../menus/Menus";
|
|
7
|
-
import { OverlayPanel } from "../../overlayPanel/OverlayPanel";
|
|
7
|
+
import { OverlayPanel, PositionOverlay } from "../../overlayPanel/OverlayPanel";
|
|
8
8
|
import { CardInterface } from "../card.interface";
|
|
9
9
|
import { CardActionInterface } from "../cardAction.interface";
|
|
10
10
|
import "./cardAction.css";
|
|
11
|
-
export type PositionOverlay
|
|
12
|
-
| "left"
|
|
13
|
-
| "right"
|
|
14
|
-
| "topLeft"
|
|
15
|
-
| "topRight"
|
|
16
|
-
| "bottomLeft"
|
|
17
|
-
| "bottomRight";
|
|
11
|
+
export type { PositionOverlay };
|
|
18
12
|
export type CardActionProps = {
|
|
19
13
|
actions: CardActionInterface[];
|
|
20
14
|
item?: CardInterface;
|
|
@@ -22,6 +22,7 @@ export type {
|
|
|
22
22
|
DataTableDateRangeFilterValue, DataTableFilterState, DataTableSortDirection,
|
|
23
23
|
DataTableSortState, FilterValue,
|
|
24
24
|
} from "./dataTable.utils";
|
|
25
|
+
|
|
25
26
|
export type DataTableProps<T> = {
|
|
26
27
|
id: string;
|
|
27
28
|
name?: string;
|
|
@@ -91,9 +92,7 @@ export function DataTable<T>(props: DataTableProps<T>) {
|
|
|
91
92
|
const tableState = useDataTableState(id, groupedColumns, sourceData, onFiltersChange, onSortChange);
|
|
92
93
|
const { flatColumns: columns, sortedData: data } = tableState;
|
|
93
94
|
const actionsColumnWidth = getActionsWidth(actions, explicitActionsWidth);
|
|
94
|
-
const filterRowRef = useRef<HTMLTableRowElement>(null);
|
|
95
95
|
const groupRowRef = useRef<HTMLTableRowElement>(null);
|
|
96
|
-
const [filterHeight, setFilterHeight] = useState(0);
|
|
97
96
|
const [groupHeight, setGroupHeight] = useState(0);
|
|
98
97
|
const hasGroups = groupedColumns.some((column) => column.children?.length);
|
|
99
98
|
const stickyOffsets = useMemo(() => {
|
|
@@ -110,7 +109,6 @@ export function DataTable<T>(props: DataTableProps<T>) {
|
|
|
110
109
|
|
|
111
110
|
useLayoutEffect(() => {
|
|
112
111
|
const measure = () => {
|
|
113
|
-
setFilterHeight(filterRowRef.current?.getBoundingClientRect().height ?? 0);
|
|
114
112
|
setGroupHeight(groupRowRef.current?.getBoundingClientRect().height ?? 0);
|
|
115
113
|
};
|
|
116
114
|
measure();
|
|
@@ -119,10 +117,9 @@ export function DataTable<T>(props: DataTableProps<T>) {
|
|
|
119
117
|
return () => window.removeEventListener("resize", measure);
|
|
120
118
|
}
|
|
121
119
|
const observer = new ResizeObserver(measure);
|
|
122
|
-
if (filterRowRef.current) observer.observe(filterRowRef.current);
|
|
123
120
|
if (groupRowRef.current) observer.observe(groupRowRef.current);
|
|
124
121
|
return () => observer.disconnect();
|
|
125
|
-
}, [
|
|
122
|
+
}, [hasGroups, groupedColumns]);
|
|
126
123
|
|
|
127
124
|
const isMobile = useIsMobile();
|
|
128
125
|
const showMobileCardView = mobileCardView && isMobile;
|
|
@@ -529,8 +526,8 @@ export function DataTable<T>(props: DataTableProps<T>) {
|
|
|
529
526
|
isFilterVisible={tableState.visibleFilters.has(column.name ?? column.id)}
|
|
530
527
|
onToggleFilter={tableState.toggleFilter} onToggleSort={tableState.toggleSort}
|
|
531
528
|
sortDirection={tableState.sort?.key === (column.name ?? column.id) ? tableState.sort.direction : null}
|
|
529
|
+
filterControl={tableState.renderFilter(column)}
|
|
532
530
|
/></tr></thead></table>
|
|
533
|
-
{tableState.renderFilter(column)}
|
|
534
531
|
</div>
|
|
535
532
|
))}
|
|
536
533
|
</div>
|
|
@@ -641,26 +638,17 @@ export function DataTable<T>(props: DataTableProps<T>) {
|
|
|
641
638
|
{actions.length > 0 && <col style={{ width: actionsColumnWidth }} />}
|
|
642
639
|
</colgroup>
|
|
643
640
|
<thead>
|
|
644
|
-
{tableState.visibleFilters.size > 0 && <tr ref={filterRowRef}>
|
|
645
|
-
{onSelectionChange && <th />}
|
|
646
|
-
{columns.map((column) => <th className="filter-row-cell" key={column.id}
|
|
647
|
-
style={{ position: "sticky", top: 0, zIndex: 14, background: "#f9fbfc", minWidth: column.minWidth,
|
|
648
|
-
...(column.sticky ? { left: stickyOffsets.get(column.id) } : {}) }}>
|
|
649
|
-
{tableState.renderFilter(column)}
|
|
650
|
-
</th>)}
|
|
651
|
-
{actions.length > 0 && <th />}
|
|
652
|
-
</tr>}
|
|
653
641
|
{hasGroups && <tr ref={groupRowRef} className="data-table-group-row">
|
|
654
642
|
{onSelectionChange && <th />}
|
|
655
643
|
{groupedColumns.map((column) => <th key={column.id} colSpan={flattenColumns([column]).length}
|
|
656
|
-
style={{ top:
|
|
644
|
+
style={{ top: 0 }}>{column.children?.length ? column.label : ""}</th>)}
|
|
657
645
|
{actions.length > 0 && <th />}
|
|
658
646
|
</tr>}
|
|
659
647
|
<tr>
|
|
660
648
|
{onSelectionChange && (
|
|
661
649
|
<th
|
|
662
650
|
className={`select-cell ${hideToggleAll ? "hidden" : ""}`}
|
|
663
|
-
style={{ width: "40px", textAlign: "center", top:
|
|
651
|
+
style={{ width: "40px", textAlign: "center", top: groupHeight,
|
|
664
652
|
...(stickyOffsets.size > 0 ? { left: 0, zIndex: 12 } : {}) }}
|
|
665
653
|
>
|
|
666
654
|
<Input
|
|
@@ -681,11 +669,12 @@ export function DataTable<T>(props: DataTableProps<T>) {
|
|
|
681
669
|
idx={idx}
|
|
682
670
|
onColumnClick={onColumnClick}
|
|
683
671
|
stickyLeft={stickyOffsets.get(column.id)}
|
|
684
|
-
stickyTop={
|
|
672
|
+
stickyTop={groupHeight}
|
|
685
673
|
isFilterVisible={tableState.visibleFilters.has(column.name ?? column.id)}
|
|
686
674
|
onToggleFilter={tableState.toggleFilter}
|
|
687
675
|
onToggleSort={tableState.toggleSort}
|
|
688
676
|
sortDirection={tableState.sort?.key === (column.name ?? column.id) ? tableState.sort.direction : null}
|
|
677
|
+
filterControl={tableState.renderFilter(column)}
|
|
689
678
|
/>
|
|
690
679
|
))}
|
|
691
680
|
{actions.length > 0 && (
|
|
@@ -135,6 +135,102 @@
|
|
|
135
135
|
color: var(--tertiary-color);
|
|
136
136
|
min-width: 100px;
|
|
137
137
|
padding: .5rem;
|
|
138
|
+
|
|
139
|
+
.data-table-column-filter,
|
|
140
|
+
.data-table-column-sort {
|
|
141
|
+
visibility: hidden;
|
|
142
|
+
|
|
143
|
+
&.active {
|
|
144
|
+
visibility: visible;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.data-table-column-filter-active,
|
|
149
|
+
.data-table-column-sort-active {
|
|
150
|
+
color: var(--tertiary-color);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
&:hover,
|
|
154
|
+
&:focus-within {
|
|
155
|
+
.data-table-column-filter,
|
|
156
|
+
.data-table-column-sort {
|
|
157
|
+
visibility: visible;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.data-table-filter-row {
|
|
163
|
+
th {
|
|
164
|
+
padding-top: 6px;
|
|
165
|
+
padding-bottom: 6px;
|
|
166
|
+
overflow: visible;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.data-table-filter-select {
|
|
170
|
+
width: 100%;
|
|
171
|
+
max-width: 100%;
|
|
172
|
+
|
|
173
|
+
button {
|
|
174
|
+
width: 100%;
|
|
175
|
+
max-width: 100%;
|
|
176
|
+
overflow: hidden;
|
|
177
|
+
padding: 4px 6px;
|
|
178
|
+
border: 1px solid #dedede;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.select-container-value {
|
|
182
|
+
display: block;
|
|
183
|
+
min-width: 0;
|
|
184
|
+
overflow: hidden;
|
|
185
|
+
text-overflow: ellipsis;
|
|
186
|
+
white-space: nowrap;
|
|
187
|
+
font-size: 0.7rem;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.data-table-filter-input-wrapper {
|
|
192
|
+
width: 100%;
|
|
193
|
+
max-width: 100%;
|
|
194
|
+
overflow: visible;
|
|
195
|
+
box-sizing: border-box;
|
|
196
|
+
|
|
197
|
+
.input-clear-icon,
|
|
198
|
+
.input-clear-btn,
|
|
199
|
+
.clear-btn {
|
|
200
|
+
right: 5px;
|
|
201
|
+
|
|
202
|
+
.bi {
|
|
203
|
+
font-size: 0.8rem;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.input-container {
|
|
208
|
+
min-width: 0;
|
|
209
|
+
width: 100%;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.select-container,
|
|
213
|
+
.select-container-button {
|
|
214
|
+
max-width: 100%;
|
|
215
|
+
min-width: 0;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.data-table-filter-input {
|
|
220
|
+
width: 100%;
|
|
221
|
+
max-width: 100%;
|
|
222
|
+
min-width: 0;
|
|
223
|
+
box-sizing: border-box;
|
|
224
|
+
overflow: hidden;
|
|
225
|
+
text-overflow: ellipsis;
|
|
226
|
+
white-space: nowrap;
|
|
227
|
+
border: 1px solid #c9cfdb;
|
|
228
|
+
border-radius: 6px;
|
|
229
|
+
padding: 4px 6px;
|
|
230
|
+
font-size: 12px;
|
|
231
|
+
background-color: #fff;
|
|
232
|
+
color: #333;
|
|
233
|
+
}
|
|
138
234
|
}
|
|
139
235
|
}
|
|
140
236
|
|
|
@@ -214,7 +310,20 @@
|
|
|
214
310
|
.data-table-column-heading {
|
|
215
311
|
display: flex;
|
|
216
312
|
align-items: center;
|
|
217
|
-
gap:
|
|
313
|
+
gap: 4px;
|
|
314
|
+
|
|
315
|
+
> .flex-1 {
|
|
316
|
+
flex: 1;
|
|
317
|
+
}
|
|
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);
|
|
218
327
|
}
|
|
219
328
|
.data-table-group-row th {
|
|
220
329
|
height: 40px;
|
|
@@ -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,
|
|
@@ -39,10 +99,18 @@ export const DataTableColumn = ({
|
|
|
39
99
|
onClick={() => onColumnClick?.(column)}
|
|
40
100
|
>
|
|
41
101
|
<div className="data-table-column-heading">
|
|
42
|
-
<span>{column.label}</span>
|
|
43
|
-
{column.sortable && <DataTableColumnSort column={column} sortDirection={sortDirection} onToggleSort={onToggleSort} />}
|
|
102
|
+
<span className="flex-1">{column.label}</span>
|
|
44
103
|
{column.filterable && <DataTableColumnFilter column={column} isFilterVisible={isFilterVisible} onToggleVisibility={onToggleFilter} />}
|
|
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}
|
|
@@ -60,6 +60,11 @@ export function useDataTableState<T>(
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
function renderFilter(column: dataTableColumnsInterface<T>) {
|
|
63
|
+
const control = renderFilterControl(column);
|
|
64
|
+
return control && <div className="data-table-filter-input-wrapper">{control}</div>;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function renderFilterControl(column: dataTableColumnsInterface<T>) {
|
|
63
68
|
const key = getFilterKey(column);
|
|
64
69
|
if (!column.filterable || !visibleFilters.has(key)) return null;
|
|
65
70
|
const value = filters[key];
|
|
@@ -72,7 +77,7 @@ export function useDataTableState<T>(
|
|
|
72
77
|
}
|
|
73
78
|
if (column.filterType === TypeInputEnum.CHECKBOX || column.filterType === TypeInputEnum.RADIO) {
|
|
74
79
|
return <Select id={inputId} options={column.filterDataEnum ?? []} multiple
|
|
75
|
-
usePortal showOptions={focusKey === key}
|
|
80
|
+
className="data-table-filter-select" classNameOverlay="data-table-filter-select-overlay" usePortal showOptions={focusKey === key}
|
|
76
81
|
value={String(value ?? "").split(",").filter(Boolean)}
|
|
77
82
|
onChangeMultiple={(values) => updateFilter(key, values.join(","))}
|
|
78
83
|
placeholder={`Filtrer ${column.label}`} />;
|
|
@@ -81,6 +86,7 @@ export function useDataTableState<T>(
|
|
|
81
86
|
type={column.filterType === TypeInputEnum.DATE ? TypeInputEnum.DATE : TypeInputEnum.TEXT}
|
|
82
87
|
value={String(value ?? "")} autoFocus={focusKey === key}
|
|
83
88
|
disabledDebounce showClearButton placeholder={`Filtrer ${column.label}`}
|
|
89
|
+
className="data-table-filter-input"
|
|
84
90
|
onClick={(event) => event.stopPropagation()}
|
|
85
91
|
onChange={(event) => updateFilter(key, event.target.value)} />;
|
|
86
92
|
}
|
|
@@ -20,6 +20,7 @@ export type PositionOverlay =
|
|
|
20
20
|
| "right"
|
|
21
21
|
| "topLeft"
|
|
22
22
|
| "topRight"
|
|
23
|
+
| "bottom"
|
|
23
24
|
| "bottomLeft"
|
|
24
25
|
| "bottomRight";
|
|
25
26
|
|
|
@@ -107,6 +108,10 @@ export const OverlayPanel = forwardRef(
|
|
|
107
108
|
top: rect.bottom,
|
|
108
109
|
left: rect.left,
|
|
109
110
|
}),
|
|
111
|
+
bottom: () => ({
|
|
112
|
+
top: rect.bottom,
|
|
113
|
+
left: rect.left + rect.width / 2 - boxRect.width / 2,
|
|
114
|
+
}),
|
|
110
115
|
left: () => ({
|
|
111
116
|
top: rect.top + rect.height / 2 - boxRect.height / 2,
|
|
112
117
|
left: rect.left - boxRect.width,
|
|
@@ -122,7 +127,7 @@ export const OverlayPanel = forwardRef(
|
|
|
122
127
|
({ top, left } = positions[position]());
|
|
123
128
|
// Manque d'espace en bas → afficher au-dessus de l'élément
|
|
124
129
|
if (
|
|
125
|
-
(position === "bottomLeft" || position === "bottomRight") &&
|
|
130
|
+
(position === "bottom" || position === "bottomLeft" || position === "bottomRight") &&
|
|
126
131
|
top + boxRect.height > innerHeight &&
|
|
127
132
|
rect.top - boxRect.height >= 0
|
|
128
133
|
) {
|
package/src/select/Select.tsx
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
2
2
|
import { Icon } from "../icon/Icon";
|
|
3
3
|
|
|
4
|
-
import { OverlayPanel } from "../overlayPanel/OverlayPanel";
|
|
4
|
+
import { OverlayPanel, PositionOverlay } from "../overlayPanel/OverlayPanel";
|
|
5
5
|
import "./select.css";
|
|
6
6
|
import { SelectOptionInterface } from "./selectOption.interface";
|
|
7
7
|
|
|
@@ -26,6 +26,7 @@ export interface SelectProps {
|
|
|
26
26
|
onChange?: (value: string) => void;
|
|
27
27
|
placeholder?: string;
|
|
28
28
|
classNameOverlay?: string;
|
|
29
|
+
positionOverlay?: PositionOverlay;
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
export const Select = ({
|
|
@@ -46,6 +47,7 @@ export const Select = ({
|
|
|
46
47
|
value,
|
|
47
48
|
placeholder,
|
|
48
49
|
classNameOverlay,
|
|
50
|
+
positionOverlay = "bottomLeft",
|
|
49
51
|
}: SelectProps): JSX.Element => {
|
|
50
52
|
const [showOverlay, setShowOverlay] = useState(false);
|
|
51
53
|
const [overlayRef, setOverlayRef] = useState<HTMLElement | null>(null);
|
|
@@ -143,7 +145,7 @@ export const Select = ({
|
|
|
143
145
|
}, [showOptions, disabled]);
|
|
144
146
|
|
|
145
147
|
return (
|
|
146
|
-
<div id={id} className={`select-container ${className ? className : ""}`}>
|
|
148
|
+
<div id={id} className={`select-container ${hasSelection && placeholder ? "has-selection" : ""} ${className ? className : ""}`}>
|
|
147
149
|
{label && (
|
|
148
150
|
<label htmlFor={`select-${id}`} className="select-container-label">
|
|
149
151
|
{label}
|
|
@@ -175,7 +177,7 @@ export const Select = ({
|
|
|
175
177
|
{showOverlay && overlayRef && (
|
|
176
178
|
<OverlayPanel
|
|
177
179
|
show={showOverlay}
|
|
178
|
-
position=
|
|
180
|
+
position={positionOverlay}
|
|
179
181
|
className={classNameOverlay ?? ""}
|
|
180
182
|
closeOverlay={() => setShowOverlay(false)}
|
|
181
183
|
referenceElement={overlayRef}
|
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;
|