@patternfly/react-data-view 6.4.0-prerelease.2 → 6.4.0-prerelease.3
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/cjs/DataViewTable/DataViewTable.d.ts +4 -0
- package/dist/cjs/DataViewTable/DataViewTable.js +21 -1
- package/dist/cjs/DataViewTableBasic/DataViewTableBasic.d.ts +2 -0
- package/dist/cjs/DataViewTableBasic/DataViewTableBasic.js +2 -2
- package/dist/cjs/DataViewTableHead/DataViewTableHead.d.ts +2 -0
- package/dist/cjs/DataViewTableHead/DataViewTableHead.js +5 -4
- package/dist/cjs/DataViewTh/DataViewTh.d.ts +32 -0
- package/dist/cjs/DataViewTh/DataViewTh.js +222 -0
- package/dist/esm/DataViewTable/DataViewTable.d.ts +4 -0
- package/dist/esm/DataViewTable/DataViewTable.js +21 -1
- package/dist/esm/DataViewTableBasic/DataViewTableBasic.d.ts +2 -0
- package/dist/esm/DataViewTableBasic/DataViewTableBasic.js +2 -2
- package/dist/esm/DataViewTableHead/DataViewTableHead.d.ts +2 -0
- package/dist/esm/DataViewTableHead/DataViewTableHead.js +5 -4
- package/dist/esm/DataViewTh/DataViewTh.d.ts +32 -0
- package/dist/esm/DataViewTh/DataViewTh.js +215 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +6 -6
- package/patternfly-docs/content/extensions/data-view/examples/Table/DataViewTableResizableColumnsExample.tsx +155 -0
- package/patternfly-docs/content/extensions/data-view/examples/Table/Table.md +50 -14
- package/src/DataViewCheckboxFilter/__snapshots__/DataViewCheckboxFilter.test.tsx.snap +0 -2
- package/src/DataViewFilters/__snapshots__/DataViewFilters.test.tsx.snap +0 -2
- package/src/DataViewTable/DataViewTable.tsx +48 -27
- package/src/DataViewTable/__snapshots__/DataViewTable.test.tsx.snap +10 -18
- package/src/DataViewTableBasic/DataViewTableBasic.tsx +4 -1
- package/src/DataViewTableBasic/__snapshots__/DataViewTableBasic.test.tsx.snap +20 -20
- package/src/DataViewTableHead/DataViewTableHead.tsx +24 -23
- package/src/DataViewTableHead/__snapshots__/DataViewTableHead.test.tsx.snap +15 -15
- package/src/DataViewTableTree/__snapshots__/DataViewTableTree.test.tsx.snap +25 -41
- package/src/DataViewTextFilter/__snapshots__/DataViewTextFilter.test.tsx.snap +0 -3
- package/src/DataViewTh/DataViewTh.tsx +342 -0
- package/src/DataViewToolbar/__snapshots__/DataViewToolbar.test.tsx.snap +0 -10
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
12
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
13
|
+
import { useState, useCallback, useRef, useEffect, Fragment } from 'react';
|
|
14
|
+
import { Th } from '@patternfly/react-table';
|
|
15
|
+
import { Button, getLanguageDirection } from '@patternfly/react-core';
|
|
16
|
+
import { createUseStyles } from 'react-jss';
|
|
17
|
+
import tableCellPaddingBlockEnd from '@patternfly/react-tokens/dist/esm/c_table_cell_PaddingBlockEnd';
|
|
18
|
+
import tableCellPaddingInlineEnd from '@patternfly/react-tokens/dist/esm/c_table_cell_PaddingInlineEnd';
|
|
19
|
+
import globalFontSizeBodyDefault from '@patternfly/react-tokens/dist/esm/t_global_font_size_body_default';
|
|
20
|
+
const ResizeIcon = () => (_jsx("svg", { className: "pf-v6-svg", viewBox: "0 0 1024 1024", fill: "currentColor", "aria-hidden": "true", role: "img", width: "1em", height: "1em", children: _jsx("path", { fillRule: "evenodd", d: "M52.7,529.8l190.5,161.9c18.6,15.9,50.5,4.7,50.5-17.8v-324c0-22.4-31.8-33.6-50.5-17.8L52.7,494.2c-11.5,9.8-11.5,25.8,0,35.6ZM480.8,12.9h62.4v998.3h-62.4V12.9ZM971.3,529.8l-190.5,161.9c-18.6,15.9-50.5,4.7-50.5-17.8v-324c0-22.4,31.8-33.6,50.5-17.8l190.5,161.9c11.5,9.8,11.5,25.8,0,35.6Z" }) }));
|
|
21
|
+
const useStyles = createUseStyles({
|
|
22
|
+
dataViewResizeableTh: {
|
|
23
|
+
[tableCellPaddingInlineEnd.name]: `calc(${globalFontSizeBodyDefault.var} * 2)`
|
|
24
|
+
},
|
|
25
|
+
dataViewResizableButton: {
|
|
26
|
+
position: 'absolute',
|
|
27
|
+
insetInlineEnd: `calc(${globalFontSizeBodyDefault.var} / 2)`,
|
|
28
|
+
insetBlockEnd: tableCellPaddingBlockEnd.var,
|
|
29
|
+
cursor: 'grab',
|
|
30
|
+
'&:active': {
|
|
31
|
+
cursor: 'grabbing'
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
export const DataViewTh = (_a) => {
|
|
36
|
+
var { content, resizableProps = {}, hasResizableColumns = false, thProps } = _a, props = __rest(_a, ["content", "resizableProps", "hasResizableColumns", "thProps"]);
|
|
37
|
+
const thRef = useRef(null);
|
|
38
|
+
const [width, setWidth] = useState((resizableProps === null || resizableProps === void 0 ? void 0 : resizableProps.width) ? resizableProps.width : 0);
|
|
39
|
+
// Tracks the current column width for the onResize callback, because the width state is not updated until after the resize is complete
|
|
40
|
+
const trackedWidth = useRef(0);
|
|
41
|
+
const classes = useStyles();
|
|
42
|
+
const isResizable = (resizableProps === null || resizableProps === void 0 ? void 0 : resizableProps.isResizable) || false;
|
|
43
|
+
const increment = (resizableProps === null || resizableProps === void 0 ? void 0 : resizableProps.increment) || 5;
|
|
44
|
+
const shiftIncrement = (resizableProps === null || resizableProps === void 0 ? void 0 : resizableProps.shiftIncrement) || 25;
|
|
45
|
+
const resizeButtonAriaLabel = resizableProps === null || resizableProps === void 0 ? void 0 : resizableProps.resizeButtonAriaLabel;
|
|
46
|
+
const onResize = (resizableProps === null || resizableProps === void 0 ? void 0 : resizableProps.onResize) || undefined;
|
|
47
|
+
const screenReaderText = (resizableProps === null || resizableProps === void 0 ? void 0 : resizableProps.screenReaderText) || `Column ${width.toFixed(0)} pixels`;
|
|
48
|
+
const dataViewThClassName = isResizable ? classes.dataViewResizeableTh : undefined;
|
|
49
|
+
const resizeButtonRef = useRef(null);
|
|
50
|
+
const setInitialVals = useRef(true);
|
|
51
|
+
const dragOffset = useRef(0);
|
|
52
|
+
const isResizing = useRef(false);
|
|
53
|
+
const isInView = useRef(true);
|
|
54
|
+
if (isResizable && !resizeButtonAriaLabel) {
|
|
55
|
+
// eslint-disable-next-line no-console
|
|
56
|
+
console.warn('DataViewTh: Missing resizeButtonAriaLabel. An aria label must be passed to each resizable column to provide a context specific label for its resize button.');
|
|
57
|
+
}
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
if (!isResizable) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const observed = resizeButtonRef.current;
|
|
63
|
+
const observer = new IntersectionObserver(([entry]) => {
|
|
64
|
+
isInView.current = entry.isIntersecting;
|
|
65
|
+
}, { threshold: 0.3 });
|
|
66
|
+
if (observed) {
|
|
67
|
+
observer.observe(observed);
|
|
68
|
+
}
|
|
69
|
+
return () => {
|
|
70
|
+
if (observed) {
|
|
71
|
+
observer.unobserve(observed);
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
}, []);
|
|
75
|
+
useEffect(() => {
|
|
76
|
+
var _a;
|
|
77
|
+
if ((isResizable || hasResizableColumns) && setInitialVals.current && width === 0) {
|
|
78
|
+
setWidth(((_a = thRef.current) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect().width) || 0);
|
|
79
|
+
setInitialVals.current = false;
|
|
80
|
+
}
|
|
81
|
+
}, [isResizable, hasResizableColumns, setInitialVals]);
|
|
82
|
+
const setDragOffset = (e) => {
|
|
83
|
+
var _a, _b;
|
|
84
|
+
const isRTL = getLanguageDirection(thRef.current) === 'rtl';
|
|
85
|
+
const startingMousePos = 'clientX' in e ? e.clientX : e.touches[0].clientX;
|
|
86
|
+
const startingColumnPos = (isRTL ? (_a = thRef.current) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect().left : (_b = thRef.current) === null || _b === void 0 ? void 0 : _b.getBoundingClientRect().right) || 0;
|
|
87
|
+
dragOffset.current = startingColumnPos - startingMousePos;
|
|
88
|
+
};
|
|
89
|
+
const handleMousedown = (e) => {
|
|
90
|
+
e.stopPropagation();
|
|
91
|
+
e.preventDefault();
|
|
92
|
+
document.addEventListener('mousemove', callbackMouseMove);
|
|
93
|
+
document.addEventListener('mouseup', callbackMouseUp);
|
|
94
|
+
// When a user begins resizing a column, set the drag offset so the drag motion aligns with the column edge
|
|
95
|
+
if (dragOffset.current === 0) {
|
|
96
|
+
setDragOffset(e);
|
|
97
|
+
}
|
|
98
|
+
isResizing.current = true;
|
|
99
|
+
};
|
|
100
|
+
const handleMouseMove = (e) => {
|
|
101
|
+
const mousePos = e.clientX;
|
|
102
|
+
handleControlMove(e, dragOffset.current + mousePos);
|
|
103
|
+
};
|
|
104
|
+
const handleTouchStart = (e) => {
|
|
105
|
+
e.stopPropagation();
|
|
106
|
+
document.addEventListener('touchmove', callbackTouchMove, { passive: false });
|
|
107
|
+
document.addEventListener('touchend', callbackTouchEnd);
|
|
108
|
+
// When a user begins resizing a column, set the drag offset so the drag motion aligns with the column edge
|
|
109
|
+
if (dragOffset.current === 0) {
|
|
110
|
+
setDragOffset(e);
|
|
111
|
+
}
|
|
112
|
+
isResizing.current = true;
|
|
113
|
+
};
|
|
114
|
+
const handleTouchMove = (e) => {
|
|
115
|
+
e.preventDefault();
|
|
116
|
+
e.stopImmediatePropagation();
|
|
117
|
+
const touchPos = e.touches[0].clientX;
|
|
118
|
+
handleControlMove(e, touchPos);
|
|
119
|
+
};
|
|
120
|
+
const handleControlMove = (e, controlPosition) => {
|
|
121
|
+
var _a, _b;
|
|
122
|
+
e.stopPropagation();
|
|
123
|
+
if (!isResizing.current) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const columnRect = (_a = thRef.current) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
|
|
127
|
+
if (columnRect === undefined) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
const mousePos = controlPosition;
|
|
131
|
+
const isRTL = getLanguageDirection(thRef.current) === 'rtl';
|
|
132
|
+
let newSize = isRTL ? (columnRect === null || columnRect === void 0 ? void 0 : columnRect.right) - mousePos : mousePos - (columnRect === null || columnRect === void 0 ? void 0 : columnRect.left);
|
|
133
|
+
// Prevent the column from shrinking below a specified minimum width
|
|
134
|
+
if (resizableProps.minWidth && newSize < resizableProps.minWidth) {
|
|
135
|
+
newSize = resizableProps.minWidth;
|
|
136
|
+
}
|
|
137
|
+
(_b = thRef.current) === null || _b === void 0 ? void 0 : _b.style.setProperty('min-width', newSize + 'px');
|
|
138
|
+
trackedWidth.current = newSize;
|
|
139
|
+
setWidth(newSize);
|
|
140
|
+
};
|
|
141
|
+
const handleMouseup = (e) => {
|
|
142
|
+
var _a;
|
|
143
|
+
if (!isResizing.current) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
// Reset the isResizing and dragOffset values to their initial states
|
|
147
|
+
isResizing.current = false;
|
|
148
|
+
dragOffset.current = 0;
|
|
149
|
+
// Call the onResize callback with the new width
|
|
150
|
+
onResize && onResize(e, thProps === null || thProps === void 0 ? void 0 : thProps.id, trackedWidth.current);
|
|
151
|
+
// Handle scroll into view when column drag button is moved off screen
|
|
152
|
+
if (resizeButtonRef.current && !isInView.current) {
|
|
153
|
+
(_a = resizeButtonRef.current) === null || _a === void 0 ? void 0 : _a.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
154
|
+
}
|
|
155
|
+
document.removeEventListener('mousemove', callbackMouseMove);
|
|
156
|
+
document.removeEventListener('mouseup', callbackMouseUp);
|
|
157
|
+
};
|
|
158
|
+
const handleTouchEnd = (e) => {
|
|
159
|
+
e.stopPropagation();
|
|
160
|
+
if (!isResizing) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
// Reset the isResizing and dragOffset values to their initial states
|
|
164
|
+
isResizing.current = false;
|
|
165
|
+
dragOffset.current = 0;
|
|
166
|
+
// Call the onResize callback with the new width
|
|
167
|
+
onResize && onResize(e, thProps === null || thProps === void 0 ? void 0 : thProps.id, trackedWidth.current);
|
|
168
|
+
document.removeEventListener('touchmove', callbackTouchMove);
|
|
169
|
+
document.removeEventListener('touchend', callbackTouchEnd);
|
|
170
|
+
};
|
|
171
|
+
const callbackMouseMove = useCallback(handleMouseMove, []);
|
|
172
|
+
const callbackTouchEnd = useCallback(handleTouchEnd, []);
|
|
173
|
+
const callbackTouchMove = useCallback(handleTouchMove, []);
|
|
174
|
+
const callbackMouseUp = useCallback(handleMouseup, []);
|
|
175
|
+
const handleKeys = (e) => {
|
|
176
|
+
var _a, _b;
|
|
177
|
+
const key = e.key;
|
|
178
|
+
if (key === 'Tab') {
|
|
179
|
+
isResizing.current = false;
|
|
180
|
+
}
|
|
181
|
+
if (key !== 'ArrowLeft' && key !== 'ArrowRight') {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
e.preventDefault();
|
|
185
|
+
isResizing.current = true;
|
|
186
|
+
const isRTL = getLanguageDirection(thRef.current) === 'rtl';
|
|
187
|
+
const columnRect = (_a = thRef.current) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
|
|
188
|
+
let newSize = (columnRect === null || columnRect === void 0 ? void 0 : columnRect.width) || 0;
|
|
189
|
+
let delta = 0;
|
|
190
|
+
const _increment = e.shiftKey ? shiftIncrement : increment;
|
|
191
|
+
if (key === 'ArrowRight') {
|
|
192
|
+
if (isRTL) {
|
|
193
|
+
delta = -_increment;
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
delta = _increment;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
else if (key === 'ArrowLeft') {
|
|
200
|
+
if (isRTL) {
|
|
201
|
+
delta = _increment;
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
delta = -_increment;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
newSize = newSize + delta;
|
|
208
|
+
(_b = thRef.current) === null || _b === void 0 ? void 0 : _b.style.setProperty('min-width', newSize + 'px');
|
|
209
|
+
setWidth(newSize);
|
|
210
|
+
onResize && onResize(e, thProps === null || thProps === void 0 ? void 0 : thProps.id, newSize);
|
|
211
|
+
};
|
|
212
|
+
const resizableContent = (_jsxs(Fragment, { children: [_jsx("div", { "aria-live": "polite", className: "pf-v6-screen-reader", children: screenReaderText }), _jsx(Button, { ref: resizeButtonRef, variant: "plain", hasNoPadding: true, icon: _jsx(ResizeIcon, {}), onMouseDown: handleMousedown, onKeyDown: handleKeys, onTouchStart: handleTouchStart, "aria-label": resizeButtonAriaLabel, className: classes.dataViewResizableButton })] }));
|
|
213
|
+
return (_jsx(Th, Object.assign({}, thProps, props, { style: width > 0 ? { minWidth: width } : undefined, ref: thRef, modifier: "truncate", className: dataViewThClassName }, (isResizable && { additionalContent: resizableContent }), { children: content })));
|
|
214
|
+
};
|
|
215
|
+
export default DataViewTh;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["../src/index.ts","../src/DataView/DataView.test.tsx","../src/DataView/DataView.tsx","../src/DataView/index.ts","../src/DataViewCheckboxFilter/DataViewCheckboxFilter.test.tsx","../src/DataViewCheckboxFilter/DataViewCheckboxFilter.tsx","../src/DataViewCheckboxFilter/index.ts","../src/DataViewEventsContext/DataViewEventsContext.test.tsx","../src/DataViewEventsContext/DataViewEventsContext.tsx","../src/DataViewEventsContext/index.ts","../src/DataViewFilters/DataViewFilters.test.tsx","../src/DataViewFilters/DataViewFilters.tsx","../src/DataViewFilters/index.tsx","../src/DataViewTable/DataViewTable.test.tsx","../src/DataViewTable/DataViewTable.tsx","../src/DataViewTable/index.ts","../src/DataViewTableBasic/DataViewTableBasic.test.tsx","../src/DataViewTableBasic/DataViewTableBasic.tsx","../src/DataViewTableBasic/index.ts","../src/DataViewTableHead/DataViewTableHead.test.tsx","../src/DataViewTableHead/DataViewTableHead.tsx","../src/DataViewTableHead/index.ts","../src/DataViewTableTree/DataViewTableTree.test.tsx","../src/DataViewTableTree/DataViewTableTree.tsx","../src/DataViewTableTree/index.ts","../src/DataViewTextFilter/DataViewTextFilter.test.tsx","../src/DataViewTextFilter/DataViewTextFilter.tsx","../src/DataViewTextFilter/index.ts","../src/DataViewToolbar/DataViewToolbar.test.tsx","../src/DataViewToolbar/DataViewToolbar.tsx","../src/DataViewToolbar/index.ts","../src/Hooks/filters.test.tsx","../src/Hooks/filters.ts","../src/Hooks/index.ts","../src/Hooks/pagination.test.tsx","../src/Hooks/pagination.ts","../src/Hooks/selection.test.tsx","../src/Hooks/selection.ts","../src/Hooks/sort.test.tsx","../src/Hooks/sort.ts","../src/InternalContext/InternalContext.test.tsx","../src/InternalContext/InternalContext.tsx","../src/InternalContext/index.ts"],"version":"5.9.2"}
|
|
1
|
+
{"root":["../src/index.ts","../src/DataView/DataView.test.tsx","../src/DataView/DataView.tsx","../src/DataView/index.ts","../src/DataViewCheckboxFilter/DataViewCheckboxFilter.test.tsx","../src/DataViewCheckboxFilter/DataViewCheckboxFilter.tsx","../src/DataViewCheckboxFilter/index.ts","../src/DataViewEventsContext/DataViewEventsContext.test.tsx","../src/DataViewEventsContext/DataViewEventsContext.tsx","../src/DataViewEventsContext/index.ts","../src/DataViewFilters/DataViewFilters.test.tsx","../src/DataViewFilters/DataViewFilters.tsx","../src/DataViewFilters/index.tsx","../src/DataViewTable/DataViewTable.test.tsx","../src/DataViewTable/DataViewTable.tsx","../src/DataViewTable/index.ts","../src/DataViewTableBasic/DataViewTableBasic.test.tsx","../src/DataViewTableBasic/DataViewTableBasic.tsx","../src/DataViewTableBasic/index.ts","../src/DataViewTableHead/DataViewTableHead.test.tsx","../src/DataViewTableHead/DataViewTableHead.tsx","../src/DataViewTableHead/index.ts","../src/DataViewTableTree/DataViewTableTree.test.tsx","../src/DataViewTableTree/DataViewTableTree.tsx","../src/DataViewTableTree/index.ts","../src/DataViewTextFilter/DataViewTextFilter.test.tsx","../src/DataViewTextFilter/DataViewTextFilter.tsx","../src/DataViewTextFilter/index.ts","../src/DataViewTh/DataViewTh.tsx","../src/DataViewToolbar/DataViewToolbar.test.tsx","../src/DataViewToolbar/DataViewToolbar.tsx","../src/DataViewToolbar/index.ts","../src/Hooks/filters.test.tsx","../src/Hooks/filters.ts","../src/Hooks/index.ts","../src/Hooks/pagination.test.tsx","../src/Hooks/pagination.ts","../src/Hooks/selection.test.tsx","../src/Hooks/selection.ts","../src/Hooks/sort.test.tsx","../src/Hooks/sort.ts","../src/InternalContext/InternalContext.test.tsx","../src/InternalContext/InternalContext.tsx","../src/InternalContext/index.ts"],"version":"5.9.2"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@patternfly/react-data-view",
|
|
3
|
-
"version": "6.4.0-prerelease.
|
|
3
|
+
"version": "6.4.0-prerelease.3",
|
|
4
4
|
"description": "Data view used for Red Hat projects.",
|
|
5
5
|
"main": "dist/cjs/index.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
@@ -32,9 +32,9 @@
|
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@patternfly/react-component-groups": "^6.1.0",
|
|
35
|
-
"@patternfly/react-core": "
|
|
36
|
-
"@patternfly/react-icons": "
|
|
37
|
-
"@patternfly/react-table": "
|
|
35
|
+
"@patternfly/react-core": "6.4.0-prerelease.1",
|
|
36
|
+
"@patternfly/react-icons": "6.4.0-prerelease.1",
|
|
37
|
+
"@patternfly/react-table": "6.4.0-prerelease.2",
|
|
38
38
|
"clsx": "^2.1.1",
|
|
39
39
|
"react-jss": "^10.10.0"
|
|
40
40
|
},
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@patternfly/documentation-framework": "^6.5.20",
|
|
47
|
-
"@patternfly/patternfly": "
|
|
48
|
-
"@patternfly/react-code-editor": "
|
|
47
|
+
"@patternfly/patternfly": "6.4.0-prerelease.1",
|
|
48
|
+
"@patternfly/react-code-editor": "6.4.0-prerelease.1",
|
|
49
49
|
"@patternfly/patternfly-a11y": "^5.1.0",
|
|
50
50
|
"@types/react": "^18.3.23",
|
|
51
51
|
"@types/react-dom": "^18.3.7",
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { FunctionComponent } from 'react';
|
|
2
|
+
import { DataViewTable, DataViewTr, DataViewTh } from '@patternfly/react-data-view/dist/dynamic/DataViewTable';
|
|
3
|
+
import { Button } from '@patternfly/react-core';
|
|
4
|
+
import { ActionsColumn } from '@patternfly/react-table';
|
|
5
|
+
|
|
6
|
+
interface Repository {
|
|
7
|
+
id: number;
|
|
8
|
+
name: string;
|
|
9
|
+
branches: string | null;
|
|
10
|
+
prs: string | null;
|
|
11
|
+
workspaces: string;
|
|
12
|
+
lastCommit: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const repositories: Repository[] = [
|
|
16
|
+
{
|
|
17
|
+
id: 1,
|
|
18
|
+
name: 'Repository one',
|
|
19
|
+
branches: 'Branch one',
|
|
20
|
+
prs: 'Pull request one',
|
|
21
|
+
workspaces: 'Workspace one',
|
|
22
|
+
lastCommit: 'Timestamp one'
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: 2,
|
|
26
|
+
name: 'Repository two',
|
|
27
|
+
branches: 'Branch two',
|
|
28
|
+
prs: 'Pull request two',
|
|
29
|
+
workspaces: 'Workspace two',
|
|
30
|
+
lastCommit: 'Timestamp two'
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
id: 3,
|
|
34
|
+
name: 'Repository three',
|
|
35
|
+
branches: 'Branch three',
|
|
36
|
+
prs: 'Pull request three',
|
|
37
|
+
workspaces: 'Workspace three',
|
|
38
|
+
lastCommit: 'Timestamp three'
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
id: 4,
|
|
42
|
+
name: 'Repository four',
|
|
43
|
+
branches: 'Branch four',
|
|
44
|
+
prs: 'Pull request four',
|
|
45
|
+
workspaces: 'Workspace four',
|
|
46
|
+
lastCommit: 'Timestamp four'
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
id: 5,
|
|
50
|
+
name: 'Repository five',
|
|
51
|
+
branches: 'Branch five',
|
|
52
|
+
prs: 'Pull request five',
|
|
53
|
+
workspaces: 'Workspace five',
|
|
54
|
+
lastCommit: 'Timestamp five'
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
id: 6,
|
|
58
|
+
name: 'Repository six',
|
|
59
|
+
branches: 'Branch six',
|
|
60
|
+
prs: 'Pull request six',
|
|
61
|
+
workspaces: 'Workspace six',
|
|
62
|
+
lastCommit: 'Timestamp six'
|
|
63
|
+
}
|
|
64
|
+
];
|
|
65
|
+
|
|
66
|
+
const rowActions = [
|
|
67
|
+
{
|
|
68
|
+
title: 'Some action',
|
|
69
|
+
onClick: () => console.log('clicked on Some action') // eslint-disable-line no-console
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
title: <div>Another action</div>,
|
|
73
|
+
onClick: () => console.log('clicked on Another action') // eslint-disable-line no-console
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
isSeparator: true
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
title: 'Third action',
|
|
80
|
+
onClick: () => console.log('clicked on Third action') // eslint-disable-line no-console
|
|
81
|
+
}
|
|
82
|
+
];
|
|
83
|
+
|
|
84
|
+
// you can also pass props to Tr by returning { row: DataViewTd[], props: TrProps } }
|
|
85
|
+
const rows: DataViewTr[] = repositories.map(({ id, name, branches, prs, workspaces, lastCommit }) => [
|
|
86
|
+
{ id, cell: workspaces, props: { favorites: { isFavorited: true } } },
|
|
87
|
+
{
|
|
88
|
+
cell: (
|
|
89
|
+
<Button href="#" variant="link" isInline>
|
|
90
|
+
{name}
|
|
91
|
+
</Button>
|
|
92
|
+
)
|
|
93
|
+
},
|
|
94
|
+
branches,
|
|
95
|
+
prs,
|
|
96
|
+
workspaces,
|
|
97
|
+
lastCommit,
|
|
98
|
+
{ cell: <ActionsColumn items={rowActions} />, props: { isActionCell: true } }
|
|
99
|
+
]);
|
|
100
|
+
|
|
101
|
+
const ouiaId = 'TableExample';
|
|
102
|
+
|
|
103
|
+
export const ResizableColumnsExample: FunctionComponent = () => {
|
|
104
|
+
const onResize = (
|
|
105
|
+
_e: React.MouseEvent | MouseEvent | React.KeyboardEvent | KeyboardEvent | TouchEvent,
|
|
106
|
+
id: string | number | undefined,
|
|
107
|
+
width: number
|
|
108
|
+
) => {
|
|
109
|
+
// eslint-disable-next-line no-console
|
|
110
|
+
console.log(`resized column id: ${id} width to: ${width.toFixed(0)}px`);
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const columns: DataViewTh[] = [
|
|
114
|
+
null,
|
|
115
|
+
'Repositories',
|
|
116
|
+
{
|
|
117
|
+
cell: 'Branches',
|
|
118
|
+
resizableProps: {
|
|
119
|
+
isResizable: true,
|
|
120
|
+
onResize,
|
|
121
|
+
resizeButtonAriaLabel: 'Resize repositories column'
|
|
122
|
+
},
|
|
123
|
+
props: { id: 'repositories' }
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
cell: 'Pull requests',
|
|
127
|
+
resizableProps: {
|
|
128
|
+
isResizable: true,
|
|
129
|
+
onResize,
|
|
130
|
+
resizeButtonAriaLabel: 'Resize pull requests column'
|
|
131
|
+
},
|
|
132
|
+
props: { info: { tooltip: 'More information' }, id: 'pull-requests' }
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
cell: 'This is a really long title',
|
|
136
|
+
resizableProps: {
|
|
137
|
+
isResizable: true,
|
|
138
|
+
onResize,
|
|
139
|
+
resizeButtonAriaLabel: 'Resize this is a really long title column'
|
|
140
|
+
},
|
|
141
|
+
props: { info: { tooltip: 'More information' }, id: 'this-is-a-really-long-title' }
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
cell: 'Last commit',
|
|
145
|
+
resizableProps: {
|
|
146
|
+
isResizable: true,
|
|
147
|
+
onResize,
|
|
148
|
+
resizeButtonAriaLabel: 'Resize last commit column'
|
|
149
|
+
},
|
|
150
|
+
props: { sort: { sortBy: {}, columnIndex: 4 }, id: 'last-commit' }
|
|
151
|
+
}
|
|
152
|
+
];
|
|
153
|
+
|
|
154
|
+
return <DataViewTable isResizable aria-label="Repositories table" ouiaId={ouiaId} columns={columns} rows={rows} />;
|
|
155
|
+
};
|
|
@@ -12,10 +12,19 @@ source: react
|
|
|
12
12
|
# If you use typescript, the name of the interface to display props for
|
|
13
13
|
# These are found through the sourceProps function provided in patternfly-docs.source.js
|
|
14
14
|
sortValue: 3
|
|
15
|
-
propComponents:
|
|
15
|
+
propComponents:
|
|
16
|
+
[
|
|
17
|
+
'DataViewTableBasic',
|
|
18
|
+
'DataViewTableTree',
|
|
19
|
+
'DataViewTrTree',
|
|
20
|
+
'DataViewTrObject',
|
|
21
|
+
'DataViewTh',
|
|
22
|
+
'DataViewThResizableProps'
|
|
23
|
+
]
|
|
16
24
|
sourceLink: https://github.com/patternfly/react-data-view/blob/main/packages/module/patternfly-docs/content/extensions/data-view/examples/Table/Table.md
|
|
17
25
|
---
|
|
18
|
-
|
|
26
|
+
|
|
27
|
+
import { FunctionComponent, useMemo, useState } from 'react';
|
|
19
28
|
import { BrowserRouter, useSearchParams } from 'react-router-dom';
|
|
20
29
|
import { Button, EmptyState, EmptyStateActions, EmptyStateBody, EmptyStateFooter } from '@patternfly/react-core';
|
|
21
30
|
import { CubesIcon, FolderIcon, FolderOpenIcon, LeafIcon, ExclamationCircleIcon } from '@patternfly/react-icons';
|
|
@@ -28,7 +37,9 @@ import { DataView, DataViewState } from '@patternfly/react-data-view/dist/dynami
|
|
|
28
37
|
The **data view table** component renders your data into columns and rows within a [PatternFly table](/components/table) component. You can easily customize and configure the table with these additional [data view components and props](/extensions/data-view/table#props).
|
|
29
38
|
|
|
30
39
|
## Configuring rows and columns
|
|
40
|
+
|
|
31
41
|
To define rows and columns for your table, use these props:
|
|
42
|
+
|
|
32
43
|
- `columns`: Defines the column heads of the table. Each item in the array can be a `ReactNode` for simple heads, or an object with the following properties:
|
|
33
44
|
- `cell`: Content to display in the column head.
|
|
34
45
|
- `props` (optional): (`ThProps`) to pass to the `<Th>` component, such as `width`, `sort`, and other table head cell properties.
|
|
@@ -42,20 +53,38 @@ It is also possible to disable row selection using the `isSelectDisabled` functi
|
|
|
42
53
|
If you want to have all expandable nodes open on initial load pass the `expandAll` prop to the DataViewTable component
|
|
43
54
|
|
|
44
55
|
### Table example
|
|
56
|
+
|
|
45
57
|
```js file="./DataViewTableExample.tsx"
|
|
46
58
|
|
|
47
59
|
```
|
|
48
60
|
|
|
61
|
+
### Resizable columns
|
|
62
|
+
|
|
63
|
+
To allow a column to resize, add `isResizable` to the `DataViewTable` element, and pass `resizableProps` to each applicable header cell. The `resizableProps` object consists of the following fields:
|
|
64
|
+
|
|
65
|
+
- `isResizable` - indicates that the column is resizable
|
|
66
|
+
- `resizeButtonAriaLabel` - an accessible name for the resizable column's resize button. This must be passed in if the column is resizable.
|
|
67
|
+
- `onResize` - a callback that will return the source event and the new width of the column
|
|
68
|
+
- `width` - a default width value for a column
|
|
69
|
+
- `minWidth` - the minimum width a column may shrink to
|
|
70
|
+
- `increment` - how many pixels the column will move left or right for keyboard navigation
|
|
71
|
+
- `shiftIncrement` - how many pixels the column will move left or right while shift is held for keyboard navigation
|
|
72
|
+
- `screenReaderText` - text that will be announced when a column is resized
|
|
73
|
+
|
|
74
|
+
```js file="./DataViewTableResizableColumnsExample.tsx"
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
|
|
49
78
|
## Tree table
|
|
50
79
|
|
|
51
|
-
A tree table includes expandable rows and custom icons for leaf and parent nodes.
|
|
80
|
+
A tree table includes expandable rows and custom icons for leaf and parent nodes.
|
|
52
81
|
To enable a tree table, pass the `isTreeTable` flag to the `<DataViewTable>` component.
|
|
53
82
|
|
|
54
|
-
|
|
55
83
|
Tree table rows have to be defined with following keys:
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
84
|
+
|
|
85
|
+
- `row`: Defines the content for each cell in the row.
|
|
86
|
+
- `id`: Unique identifier for the row that's used for matching selected items.
|
|
87
|
+
- `children` (optional): Defines the children rows.
|
|
59
88
|
|
|
60
89
|
To update a row's icon to reflect its expansion state, pass `collapsedIcon`, `expandedIcon`, and `leafIcon` to `<DataViewTable>`.
|
|
61
90
|
|
|
@@ -68,17 +97,21 @@ To disable row selection, pass the `isSelectDisabled` function to `selection` pr
|
|
|
68
97
|
```
|
|
69
98
|
|
|
70
99
|
## Sorting
|
|
100
|
+
|
|
71
101
|
The following example demonstrates how to enable sorting functionality within a data view. This implementation supports dynamic sorting by column and persists the sort state in the page's URL via [React Router](https://reactrouter.com/).
|
|
72
102
|
|
|
73
103
|
### Sorting example
|
|
104
|
+
|
|
74
105
|
```js file="./SortingExample.tsx"
|
|
75
106
|
|
|
76
107
|
```
|
|
108
|
+
|
|
77
109
|
### Sorting state
|
|
78
110
|
|
|
79
111
|
The `useDataViewSort` hook manages the sorting state of a data view and provides an easy way to handle sorting logic, such as synchronization with URL parameters and the definition of default sorting behavior.
|
|
80
112
|
|
|
81
113
|
**Initial values:**
|
|
114
|
+
|
|
82
115
|
- `initialSort` object to set default `sortBy` and `direction` values:
|
|
83
116
|
- `sortBy`: Key of the initial column to sort.
|
|
84
117
|
- `direction`: Default sorting direction (`asc` or `desc`).
|
|
@@ -88,44 +121,47 @@ The `useDataViewSort` hook manages the sorting state of a data view and provides
|
|
|
88
121
|
- Customizable parameter names for the URL:
|
|
89
122
|
- `sortByParam`: Name of the URL parameter for the column key.
|
|
90
123
|
- `directionParam`: Name of the URL parameter for the sorting direction.
|
|
91
|
-
The `useDataViewSort` hook integrates seamlessly with [React Router](https://reactrouter.com/) to manage the sort state via URL parameters. Alternatively, you can use `URLSearchParams` and `window.history.pushState` APIs, or other routing libraries. If URL synchronization is not configured, the sort state is managed internally within the component.
|
|
124
|
+
The `useDataViewSort` hook integrates seamlessly with [React Router](https://reactrouter.com/) to manage the sort state via URL parameters. Alternatively, you can use `URLSearchParams` and `window.history.pushState` APIs, or other routing libraries. If URL synchronization is not configured, the sort state is managed internally within the component.
|
|
92
125
|
|
|
93
126
|
**Return values:**
|
|
127
|
+
|
|
94
128
|
- `sortBy`: Key of the column currently being sorted.
|
|
95
129
|
- `direction`: Current sorting direction (`asc` or `desc`).
|
|
96
130
|
- `onSort`: Function to handle sorting changes programmatically or via user interaction.
|
|
97
131
|
|
|
98
132
|
## States
|
|
99
133
|
|
|
100
|
-
The data view table allows you to react to the `activeState` of the data view (such as `empty`, `error`, `loading`). You can use the `headStates` and `bodyStates` props to define the table head and body for a given state.
|
|
134
|
+
The data view table allows you to react to the `activeState` of the data view (such as `empty`, `error`, `loading`). You can use the `headStates` and `bodyStates` props to define the table head and body for a given state.
|
|
101
135
|
|
|
102
136
|
### Empty
|
|
103
|
-
When there is no data to render in the data view, you can instead display an empty state.
|
|
104
137
|
|
|
105
|
-
|
|
138
|
+
When there is no data to render in the data view, you can instead display an empty state.
|
|
139
|
+
|
|
140
|
+
You can create your empty state by passing a [PatternFly empty state](/components/empty-state) to the `empty` key of `headStates` or `bodyStates`.
|
|
106
141
|
|
|
107
142
|
```js file="./DataViewTableEmptyExample.tsx"
|
|
108
143
|
|
|
109
144
|
```
|
|
110
145
|
|
|
111
146
|
### Error
|
|
147
|
+
|
|
112
148
|
When there is a data connection or retrieval error, you can display an error state.
|
|
113
149
|
|
|
114
150
|
The error state will be displayed when the data view `activeState` value is `error`.
|
|
115
151
|
|
|
116
|
-
You can create your error state by passing either the [component groups extension's error state](/component-groups/error-state) or a [PatternFly empty state](/components/empty-state) to the `error` key of `headStates` or `bodyStates`.
|
|
152
|
+
You can create your error state by passing either the [component groups extension's error state](/component-groups/error-state) or a [PatternFly empty state](/components/empty-state) to the `error` key of `headStates` or `bodyStates`.
|
|
117
153
|
|
|
118
154
|
```js file="./DataViewTableErrorExample.tsx"
|
|
119
155
|
|
|
120
156
|
```
|
|
121
157
|
|
|
122
158
|
### Loading
|
|
159
|
+
|
|
123
160
|
To indicate that data is loading, you can display a loading state.
|
|
124
161
|
|
|
125
162
|
The loading state will be displayed when the data view `activeState` value is `loading`.
|
|
126
163
|
|
|
127
|
-
You can create your loading state by passing either the [component groups extension's skeleton table](/component-groups/skeleton-table) or a customized [PatternFly empty state](/components/empty-state) to the `loading` key of `headStates` or `bodyStates`.
|
|
128
|
-
|
|
164
|
+
You can create your loading state by passing either the [component groups extension's skeleton table](/component-groups/skeleton-table) or a customized [PatternFly empty state](/components/empty-state) to the `loading` key of `headStates` or `bodyStates`.
|
|
129
165
|
|
|
130
166
|
```js file="./DataViewTableLoadingExample.tsx"
|
|
131
167
|
|
|
@@ -135,7 +135,6 @@ exports[`DataViewCheckboxFilter component should render correctly 1`] = `
|
|
|
135
135
|
class="pf-v6-c-label__actions"
|
|
136
136
|
>
|
|
137
137
|
<button
|
|
138
|
-
aria-disabled="false"
|
|
139
138
|
aria-label="Close Workspace one"
|
|
140
139
|
class="pf-v6-c-button pf-m-plain pf-m-no-padding"
|
|
141
140
|
data-ouia-component-id="OUIA-Generated-Button-plain-1"
|
|
@@ -176,7 +175,6 @@ exports[`DataViewCheckboxFilter component should render correctly 1`] = `
|
|
|
176
175
|
class="pf-v6-c-toolbar__item"
|
|
177
176
|
>
|
|
178
177
|
<button
|
|
179
|
-
aria-disabled="false"
|
|
180
178
|
class="pf-v6-c-button pf-m-link pf-m-inline"
|
|
181
179
|
data-ouia-component-id="DataViewToolbar-clear-all-filters"
|
|
182
180
|
data-ouia-component-type="PF6/Button"
|
|
@@ -26,7 +26,6 @@ exports[`DataViewFilters component should render correctly 1`] = `
|
|
|
26
26
|
class="pf-v6-c-toolbar__toggle"
|
|
27
27
|
>
|
|
28
28
|
<button
|
|
29
|
-
aria-disabled="false"
|
|
30
29
|
aria-haspopup="false"
|
|
31
30
|
aria-label="Show Filters"
|
|
32
31
|
class="pf-v6-c-button pf-m-plain"
|
|
@@ -173,7 +172,6 @@ exports[`DataViewFilters component should render correctly 1`] = `
|
|
|
173
172
|
class="pf-v6-c-toolbar__item"
|
|
174
173
|
>
|
|
175
174
|
<button
|
|
176
|
-
aria-disabled="false"
|
|
177
175
|
class="pf-v6-c-button pf-m-link pf-m-inline"
|
|
178
176
|
data-ouia-component-id="DataViewToolbar-clear-all-filters"
|
|
179
177
|
data-ouia-component-type="PF6/Button"
|