@alaarab/ogrid-react 2.1.2 → 2.1.4
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/esm/index.js +7233 -26
- package/package.json +7 -4
- package/dist/esm/components/BaseColumnHeaderMenu.js +0 -78
- package/dist/esm/components/BaseDropIndicator.js +0 -4
- package/dist/esm/components/BaseEmptyState.js +0 -4
- package/dist/esm/components/BaseInlineCellEditor.js +0 -167
- package/dist/esm/components/BaseLoadingOverlay.js +0 -4
- package/dist/esm/components/CellErrorBoundary.js +0 -43
- package/dist/esm/components/ColumnChooserProps.js +0 -6
- package/dist/esm/components/ColumnHeaderFilterContent.js +0 -33
- package/dist/esm/components/ColumnHeaderFilterRenderers.js +0 -67
- package/dist/esm/components/EmptyState.js +0 -19
- package/dist/esm/components/GridContextMenu.js +0 -35
- package/dist/esm/components/MarchingAntsOverlay.js +0 -90
- package/dist/esm/components/OGridLayout.js +0 -136
- package/dist/esm/components/PaginationControlsProps.js +0 -6
- package/dist/esm/components/SideBar.js +0 -123
- package/dist/esm/components/StatusBar.js +0 -6
- package/dist/esm/components/createOGrid.js +0 -19
- package/dist/esm/constants/domHelpers.js +0 -16
- package/dist/esm/hooks/index.js +0 -43
- package/dist/esm/hooks/useActiveCell.js +0 -75
- package/dist/esm/hooks/useCellEditing.js +0 -15
- package/dist/esm/hooks/useCellSelection.js +0 -389
- package/dist/esm/hooks/useClipboard.js +0 -106
- package/dist/esm/hooks/useColumnChooserState.js +0 -74
- package/dist/esm/hooks/useColumnHeaderFilterState.js +0 -191
- package/dist/esm/hooks/useColumnHeaderMenuState.js +0 -106
- package/dist/esm/hooks/useColumnMeta.js +0 -61
- package/dist/esm/hooks/useColumnPinning.js +0 -67
- package/dist/esm/hooks/useColumnReorder.js +0 -143
- package/dist/esm/hooks/useColumnResize.js +0 -127
- package/dist/esm/hooks/useContextMenu.js +0 -21
- package/dist/esm/hooks/useDataGridContextMenu.js +0 -24
- package/dist/esm/hooks/useDataGridEditing.js +0 -56
- package/dist/esm/hooks/useDataGridInteraction.js +0 -109
- package/dist/esm/hooks/useDataGridLayout.js +0 -172
- package/dist/esm/hooks/useDataGridState.js +0 -169
- package/dist/esm/hooks/useDataGridTableOrchestration.js +0 -199
- package/dist/esm/hooks/useDateFilterState.js +0 -34
- package/dist/esm/hooks/useDebounce.js +0 -35
- package/dist/esm/hooks/useFillHandle.js +0 -200
- package/dist/esm/hooks/useFilterOptions.js +0 -55
- package/dist/esm/hooks/useInlineCellEditorState.js +0 -38
- package/dist/esm/hooks/useKeyboardNavigation.js +0 -261
- package/dist/esm/hooks/useLatestRef.js +0 -11
- package/dist/esm/hooks/useListVirtualizer.js +0 -29
- package/dist/esm/hooks/useMultiSelectFilterState.js +0 -59
- package/dist/esm/hooks/useOGrid.js +0 -355
- package/dist/esm/hooks/useOGridDataFetching.js +0 -74
- package/dist/esm/hooks/useOGridFilters.js +0 -59
- package/dist/esm/hooks/useOGridPagination.js +0 -24
- package/dist/esm/hooks/useOGridSorting.js +0 -24
- package/dist/esm/hooks/usePaginationControls.js +0 -16
- package/dist/esm/hooks/usePeopleFilterState.js +0 -73
- package/dist/esm/hooks/useRichSelectState.js +0 -60
- package/dist/esm/hooks/useRowSelection.js +0 -69
- package/dist/esm/hooks/useSelectState.js +0 -62
- package/dist/esm/hooks/useShallowEqualMemo.js +0 -14
- package/dist/esm/hooks/useSideBarState.js +0 -39
- package/dist/esm/hooks/useTableLayout.js +0 -69
- package/dist/esm/hooks/useTextFilterState.js +0 -25
- package/dist/esm/hooks/useUndoRedo.js +0 -84
- package/dist/esm/hooks/useVirtualScroll.js +0 -69
- package/dist/esm/storybook/index.js +0 -1
- package/dist/esm/storybook/mockData.js +0 -73
- package/dist/esm/types/columnTypes.js +0 -1
- package/dist/esm/types/dataGridTypes.js +0 -1
- package/dist/esm/types/index.js +0 -1
- package/dist/esm/utils/dataGridViewModel.js +0 -54
- package/dist/esm/utils/gridRowComparator.js +0 -2
- package/dist/esm/utils/index.js +0 -5
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useState, useCallback, useEffect } from 'react';
|
|
3
|
-
import { SideBar } from './SideBar';
|
|
4
|
-
import { GRID_BORDER_RADIUS } from '@alaarab/ogrid-core';
|
|
5
|
-
// Stable style objects (avoid re-creating on every render)
|
|
6
|
-
const borderedContainerStyle = {
|
|
7
|
-
border: '1px solid var(--ogrid-border, #e0e0e0)',
|
|
8
|
-
borderRadius: GRID_BORDER_RADIUS,
|
|
9
|
-
overflow: 'hidden',
|
|
10
|
-
display: 'flex',
|
|
11
|
-
flexDirection: 'column',
|
|
12
|
-
flex: 1,
|
|
13
|
-
minHeight: 0,
|
|
14
|
-
background: 'var(--ogrid-bg, #fff)',
|
|
15
|
-
};
|
|
16
|
-
const fullscreenContainerStyle = {
|
|
17
|
-
...borderedContainerStyle,
|
|
18
|
-
borderRadius: 0,
|
|
19
|
-
border: 'none',
|
|
20
|
-
};
|
|
21
|
-
const toolbarStripBase = {
|
|
22
|
-
display: 'flex',
|
|
23
|
-
justifyContent: 'space-between',
|
|
24
|
-
alignItems: 'center',
|
|
25
|
-
padding: '6px 12px',
|
|
26
|
-
background: 'var(--ogrid-header-bg, #f5f5f5)',
|
|
27
|
-
gap: 8,
|
|
28
|
-
flexWrap: 'wrap',
|
|
29
|
-
minHeight: 0,
|
|
30
|
-
};
|
|
31
|
-
/** Toolbar strip with border-bottom (when it's the only toolbar row). */
|
|
32
|
-
const toolbarStripStyle = {
|
|
33
|
-
...toolbarStripBase,
|
|
34
|
-
borderBottom: '1px solid var(--ogrid-border, #e0e0e0)',
|
|
35
|
-
};
|
|
36
|
-
/** Toolbar strip without border-bottom (when toolbarBelow follows — it owns the border). */
|
|
37
|
-
const toolbarStripNoBorderStyle = toolbarStripBase;
|
|
38
|
-
const toolbarSectionStyle = {
|
|
39
|
-
display: 'flex',
|
|
40
|
-
alignItems: 'center',
|
|
41
|
-
gap: 8,
|
|
42
|
-
};
|
|
43
|
-
/** Secondary toolbar row (e.g. active filter chips). Matches toolbar strip styling. */
|
|
44
|
-
const toolbarBelowStyle = {
|
|
45
|
-
borderBottom: '1px solid var(--ogrid-border, #e0e0e0)',
|
|
46
|
-
padding: '6px 12px',
|
|
47
|
-
background: 'var(--ogrid-header-bg, #f5f5f5)',
|
|
48
|
-
};
|
|
49
|
-
const footerStripStyle = {
|
|
50
|
-
borderTop: '1px solid var(--ogrid-border, #e0e0e0)',
|
|
51
|
-
background: 'var(--ogrid-header-bg, #f5f5f5)',
|
|
52
|
-
padding: '6px 12px',
|
|
53
|
-
};
|
|
54
|
-
const gridAreaFlexStyle = {
|
|
55
|
-
width: '100%',
|
|
56
|
-
minWidth: 0,
|
|
57
|
-
minHeight: 0,
|
|
58
|
-
flex: 1,
|
|
59
|
-
display: 'flex',
|
|
60
|
-
};
|
|
61
|
-
const gridAreaSoloStyle = {
|
|
62
|
-
width: '100%',
|
|
63
|
-
minWidth: 0,
|
|
64
|
-
minHeight: 0,
|
|
65
|
-
flex: 1,
|
|
66
|
-
display: 'flex',
|
|
67
|
-
flexDirection: 'column',
|
|
68
|
-
};
|
|
69
|
-
const gridChildStyle = {
|
|
70
|
-
flex: 1,
|
|
71
|
-
minWidth: 0,
|
|
72
|
-
minHeight: 0,
|
|
73
|
-
display: 'flex',
|
|
74
|
-
flexDirection: 'column',
|
|
75
|
-
};
|
|
76
|
-
const rootStyle = {
|
|
77
|
-
display: 'flex',
|
|
78
|
-
flexDirection: 'column',
|
|
79
|
-
height: '100%',
|
|
80
|
-
};
|
|
81
|
-
const fullscreenRootStyle = {
|
|
82
|
-
position: 'fixed',
|
|
83
|
-
inset: 0,
|
|
84
|
-
zIndex: 9999,
|
|
85
|
-
display: 'flex',
|
|
86
|
-
flexDirection: 'column',
|
|
87
|
-
background: 'var(--ogrid-bg, #fff)',
|
|
88
|
-
};
|
|
89
|
-
const fullscreenBtnStyle = {
|
|
90
|
-
background: 'none',
|
|
91
|
-
border: '1px solid var(--ogrid-border, #e0e0e0)',
|
|
92
|
-
borderRadius: 4,
|
|
93
|
-
padding: '4px 6px',
|
|
94
|
-
cursor: 'pointer',
|
|
95
|
-
display: 'flex',
|
|
96
|
-
alignItems: 'center',
|
|
97
|
-
justifyContent: 'center',
|
|
98
|
-
color: 'var(--ogrid-fg, #242424)',
|
|
99
|
-
};
|
|
100
|
-
// SVG expand icon (enter fullscreen)
|
|
101
|
-
const ExpandIcon = () => (_jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("polyline", { points: "10 2 14 2 14 6" }), _jsx("polyline", { points: "6 14 2 14 2 10" }), _jsx("line", { x1: "14", y1: "2", x2: "10", y2: "6" }), _jsx("line", { x1: "2", y1: "14", x2: "6", y2: "10" })] }));
|
|
102
|
-
// SVG collapse icon (exit fullscreen)
|
|
103
|
-
const CollapseIcon = () => (_jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("polyline", { points: "4 10 0 10 0 14" }), _jsx("polyline", { points: "12 6 16 6 16 2" }), _jsx("line", { x1: "0", y1: "10", x2: "4", y2: "6" }), _jsx("line", { x1: "16", y1: "6", x2: "12", y2: "10" })] }));
|
|
104
|
-
/**
|
|
105
|
-
* Renders OGrid layout as a unified bordered container:
|
|
106
|
-
* ┌────────────────────────────────────┐
|
|
107
|
-
* │ [toolbar strip] │
|
|
108
|
-
* ├────────────────────────────────────┤
|
|
109
|
-
* │ [sidebar]? [grid] │
|
|
110
|
-
* ├────────────────────────────────────┤
|
|
111
|
-
* │ [footer strip / pagination] │
|
|
112
|
-
* └────────────────────────────────────┘
|
|
113
|
-
*/
|
|
114
|
-
export function OGridLayout(props) {
|
|
115
|
-
const { containerComponent: Container = 'div', containerProps = {}, className, toolbar, toolbarEnd, toolbarBelow, children, pagination, sideBar, fullScreen, } = props;
|
|
116
|
-
const [isFullScreen, setIsFullScreen] = useState(false);
|
|
117
|
-
const toggleFullScreen = useCallback(() => {
|
|
118
|
-
setIsFullScreen((prev) => !prev);
|
|
119
|
-
}, []);
|
|
120
|
-
// ESC key to exit fullscreen
|
|
121
|
-
useEffect(() => {
|
|
122
|
-
if (!isFullScreen)
|
|
123
|
-
return;
|
|
124
|
-
const handleKeyDown = (e) => {
|
|
125
|
-
if (e.key === 'Escape')
|
|
126
|
-
setIsFullScreen(false);
|
|
127
|
-
};
|
|
128
|
-
document.addEventListener('keydown', handleKeyDown);
|
|
129
|
-
return () => document.removeEventListener('keydown', handleKeyDown);
|
|
130
|
-
}, [isFullScreen]);
|
|
131
|
-
const hasSideBar = sideBar != null;
|
|
132
|
-
const sideBarPosition = sideBar?.position ?? 'right';
|
|
133
|
-
const hasToolbar = toolbar != null || toolbarEnd != null || fullScreen;
|
|
134
|
-
const fullscreenButton = fullScreen ? (_jsx("button", { type: "button", style: fullscreenBtnStyle, onClick: toggleFullScreen, title: isFullScreen ? 'Exit fullscreen' : 'Fullscreen', "aria-label": isFullScreen ? 'Exit fullscreen' : 'Fullscreen', children: isFullScreen ? _jsx(CollapseIcon, {}) : _jsx(ExpandIcon, {}) })) : null;
|
|
135
|
-
return (_jsx(Container, { className: className, style: isFullScreen ? fullscreenRootStyle : rootStyle, ...containerProps, children: _jsxs("div", { style: isFullScreen ? fullscreenContainerStyle : borderedContainerStyle, children: [hasToolbar && (_jsxs("div", { style: toolbarBelow ? toolbarStripNoBorderStyle : toolbarStripStyle, children: [_jsx("div", { style: toolbarSectionStyle, children: toolbar }), _jsxs("div", { style: toolbarSectionStyle, children: [toolbarEnd, fullscreenButton] })] })), toolbarBelow && (_jsx("div", { style: toolbarBelowStyle, children: toolbarBelow })), hasSideBar ? (_jsxs("div", { style: gridAreaFlexStyle, children: [sideBarPosition === 'left' && _jsx(SideBar, { ...sideBar }), _jsx("div", { style: gridChildStyle, children: children }), sideBarPosition !== 'left' && _jsx(SideBar, { ...sideBar })] })) : (_jsx("div", { style: gridAreaSoloStyle, children: children })), pagination && (_jsx("div", { style: footerStripStyle, children: pagination }))] }) }));
|
|
136
|
-
}
|
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
const PANEL_WIDTH = 240;
|
|
3
|
-
const TAB_WIDTH = 36;
|
|
4
|
-
const PANEL_LABELS = {
|
|
5
|
-
columns: 'Columns',
|
|
6
|
-
filters: 'Filters',
|
|
7
|
-
};
|
|
8
|
-
// --- Stable style objects (avoid re-creating on every render) ---
|
|
9
|
-
const tabStripBaseStyle = {
|
|
10
|
-
display: 'flex',
|
|
11
|
-
flexDirection: 'column',
|
|
12
|
-
width: TAB_WIDTH,
|
|
13
|
-
background: 'var(--ogrid-header-bg, #f5f5f5)',
|
|
14
|
-
};
|
|
15
|
-
const tabStripBorderLeft = { ...tabStripBaseStyle, borderLeft: '1px solid var(--ogrid-border, #e0e0e0)' };
|
|
16
|
-
const tabStripBorderRight = { ...tabStripBaseStyle, borderRight: '1px solid var(--ogrid-border, #e0e0e0)' };
|
|
17
|
-
const tabButtonBase = {
|
|
18
|
-
width: TAB_WIDTH,
|
|
19
|
-
height: TAB_WIDTH,
|
|
20
|
-
border: 'none',
|
|
21
|
-
cursor: 'pointer',
|
|
22
|
-
color: 'var(--ogrid-fg, #242424)',
|
|
23
|
-
fontSize: 14,
|
|
24
|
-
display: 'flex',
|
|
25
|
-
alignItems: 'center',
|
|
26
|
-
justifyContent: 'center',
|
|
27
|
-
};
|
|
28
|
-
const tabButtonActive = { ...tabButtonBase, background: 'var(--ogrid-bg, #fff)', fontWeight: 'bold' };
|
|
29
|
-
const tabButtonInactive = { ...tabButtonBase, background: 'transparent', fontWeight: 'normal' };
|
|
30
|
-
const panelContainerBase = {
|
|
31
|
-
width: PANEL_WIDTH,
|
|
32
|
-
display: 'flex',
|
|
33
|
-
flexDirection: 'column',
|
|
34
|
-
overflow: 'hidden',
|
|
35
|
-
background: 'var(--ogrid-bg, #fff)',
|
|
36
|
-
color: 'var(--ogrid-fg, #242424)',
|
|
37
|
-
};
|
|
38
|
-
const panelContainerBorderLeft = { ...panelContainerBase, borderLeft: '1px solid var(--ogrid-border, #e0e0e0)' };
|
|
39
|
-
const panelContainerBorderRight = { ...panelContainerBase, borderRight: '1px solid var(--ogrid-border, #e0e0e0)' };
|
|
40
|
-
const panelHeaderStyle = {
|
|
41
|
-
display: 'flex',
|
|
42
|
-
justifyContent: 'space-between',
|
|
43
|
-
alignItems: 'center',
|
|
44
|
-
padding: '8px 12px',
|
|
45
|
-
borderBottom: '1px solid var(--ogrid-border, #e0e0e0)',
|
|
46
|
-
fontWeight: 600,
|
|
47
|
-
};
|
|
48
|
-
const closeButtonStyle = { border: 'none', background: 'transparent', cursor: 'pointer', fontSize: 16, color: 'var(--ogrid-fg, #242424)' };
|
|
49
|
-
const panelBodyStyle = { flex: 1, overflowY: 'auto', padding: '8px 12px' };
|
|
50
|
-
const sideBarRootStyle = { display: 'flex', flexDirection: 'row', flexShrink: 0 };
|
|
51
|
-
const buttonRowStyle = { display: 'flex', gap: 8, marginBottom: 8 };
|
|
52
|
-
const actionButtonStyle = { flex: 1, cursor: 'pointer', background: 'var(--ogrid-bg-subtle, #f3f2f1)', color: 'var(--ogrid-fg, #242424)', border: '1px solid var(--ogrid-border, #e0e0e0)', borderRadius: 4, padding: '4px 8px' };
|
|
53
|
-
const checkboxLabelStyle = { display: 'flex', alignItems: 'center', gap: 6, padding: '2px 0', cursor: 'pointer' };
|
|
54
|
-
const noFilterStyle = { color: 'var(--ogrid-muted, #999)', fontStyle: 'italic' };
|
|
55
|
-
const filterGroupStyle = { marginBottom: 12 };
|
|
56
|
-
const filterLabelStyle = { fontWeight: 500, marginBottom: 4, fontSize: 13 };
|
|
57
|
-
const textInputStyle = { width: '100%', boxSizing: 'border-box', padding: '4px 6px', background: 'var(--ogrid-bg, #fff)', color: 'var(--ogrid-fg, #242424)', border: '1px solid var(--ogrid-border, #e0e0e0)', borderRadius: 4 };
|
|
58
|
-
const dateContainerStyle = { display: 'flex', flexDirection: 'column', gap: 4 };
|
|
59
|
-
const dateLabelStyle = { display: 'flex', alignItems: 'center', gap: 4, fontSize: 12 };
|
|
60
|
-
const dateInputStyle = { flex: 1, padding: '2px 4px', background: 'var(--ogrid-bg, #fff)', color: 'var(--ogrid-fg, #242424)', border: '1px solid var(--ogrid-border, #e0e0e0)', borderRadius: 4 };
|
|
61
|
-
const multiSelectContainerStyle = { maxHeight: 120, overflowY: 'auto' };
|
|
62
|
-
const multiSelectLabelStyle = { display: 'flex', alignItems: 'center', gap: 4, padding: '1px 0', cursor: 'pointer', fontSize: 13 };
|
|
63
|
-
export function SideBar(props) {
|
|
64
|
-
const { activePanel, onPanelChange, panels, position, columns, visibleColumns, onVisibilityChange, onSetVisibleColumns, filterableColumns, filters, onFilterChange, filterOptions, } = props;
|
|
65
|
-
const isOpen = activePanel !== null;
|
|
66
|
-
const handleTabClick = (panel) => {
|
|
67
|
-
onPanelChange(activePanel === panel ? null : panel);
|
|
68
|
-
};
|
|
69
|
-
const tabStripStyle = position === 'right' ? tabStripBorderLeft : tabStripBorderRight;
|
|
70
|
-
const panelContainerStyle = position === 'right' ? panelContainerBorderLeft : panelContainerBorderRight;
|
|
71
|
-
const tabStrip = (_jsx("div", { style: tabStripStyle, role: "tablist", "aria-label": "Side bar tabs", children: panels.map((panel) => (_jsx("button", { role: "tab", "aria-selected": activePanel === panel, "aria-label": PANEL_LABELS[panel], onClick: () => handleTabClick(panel), title: PANEL_LABELS[panel], style: activePanel === panel ? tabButtonActive : tabButtonInactive, children: panel === 'columns' ? '\u2261' : '\u2A65' }, panel))) }));
|
|
72
|
-
const panelContent = isOpen && activePanel ? (_jsxs("div", { role: "tabpanel", "aria-label": PANEL_LABELS[activePanel], style: panelContainerStyle, children: [_jsxs("div", { style: panelHeaderStyle, children: [_jsx("span", { children: PANEL_LABELS[activePanel] }), _jsx("button", { onClick: () => onPanelChange(null), style: closeButtonStyle, "aria-label": "Close panel", children: "\u00D7" })] }), _jsxs("div", { style: panelBodyStyle, children: [activePanel === 'columns' && (_jsx(ColumnsPanel, { columns: columns, visibleColumns: visibleColumns, onVisibilityChange: onVisibilityChange, onSetVisibleColumns: onSetVisibleColumns })), activePanel === 'filters' && (_jsx(FiltersPanel, { filterableColumns: filterableColumns, filters: filters, onFilterChange: onFilterChange, filterOptions: filterOptions }))] })] })) : null;
|
|
73
|
-
return (_jsxs("div", { style: sideBarRootStyle, role: "complementary", "aria-label": "Side bar", children: [position === 'left' && tabStrip, position === 'left' && panelContent, position === 'right' && panelContent, position === 'right' && tabStrip] }));
|
|
74
|
-
}
|
|
75
|
-
// --- Internal sub-components ---
|
|
76
|
-
function ColumnsPanel(props) {
|
|
77
|
-
const { columns, visibleColumns, onVisibilityChange, onSetVisibleColumns } = props;
|
|
78
|
-
const allVisible = columns.every((c) => visibleColumns.has(c.columnId));
|
|
79
|
-
const handleSelectAll = () => {
|
|
80
|
-
const next = new Set(visibleColumns);
|
|
81
|
-
columns.forEach((c) => next.add(c.columnId));
|
|
82
|
-
onSetVisibleColumns(next);
|
|
83
|
-
};
|
|
84
|
-
const handleClearAll = () => {
|
|
85
|
-
const next = new Set();
|
|
86
|
-
columns.forEach((c) => {
|
|
87
|
-
if (c.required && visibleColumns.has(c.columnId))
|
|
88
|
-
next.add(c.columnId);
|
|
89
|
-
});
|
|
90
|
-
onSetVisibleColumns(next);
|
|
91
|
-
};
|
|
92
|
-
return (_jsxs(_Fragment, { children: [_jsxs("div", { style: buttonRowStyle, children: [_jsx("button", { onClick: handleSelectAll, disabled: allVisible, style: actionButtonStyle, children: "Select All" }), _jsx("button", { onClick: handleClearAll, style: actionButtonStyle, children: "Clear All" })] }), columns.map((col) => (_jsxs("label", { style: checkboxLabelStyle, children: [_jsx("input", { type: "checkbox", checked: visibleColumns.has(col.columnId), onChange: (e) => onVisibilityChange(col.columnId, e.target.checked), disabled: col.required }), _jsx("span", { children: col.name })] }, col.columnId)))] }));
|
|
93
|
-
}
|
|
94
|
-
function FiltersPanel(props) {
|
|
95
|
-
const { filterableColumns, filters, onFilterChange, filterOptions } = props;
|
|
96
|
-
if (filterableColumns.length === 0) {
|
|
97
|
-
return _jsx("div", { style: noFilterStyle, children: "No filterable columns" });
|
|
98
|
-
}
|
|
99
|
-
return (_jsx(_Fragment, { children: filterableColumns.map((col) => {
|
|
100
|
-
const filterKey = col.filterField;
|
|
101
|
-
const fv = filters[filterKey];
|
|
102
|
-
return (_jsxs("div", { style: filterGroupStyle, children: [_jsx("div", { style: filterLabelStyle, children: col.name }), col.filterType === 'text' && (_jsx("input", { type: "text", value: fv?.type === 'text' ? fv.value : '', onChange: (e) => onFilterChange(filterKey, e.target.value ? { type: 'text', value: e.target.value } : undefined), placeholder: `Filter ${col.name}...`, "aria-label": `Filter ${col.name}`, style: textInputStyle })), col.filterType === 'date' && (_jsxs("div", { style: dateContainerStyle, children: [_jsxs("label", { style: dateLabelStyle, children: ["From:", _jsx("input", { type: "date", value: fv?.type === 'date' ? (fv.value.from ?? '') : '', onChange: (e) => {
|
|
103
|
-
const from = e.target.value || undefined;
|
|
104
|
-
const existingValue = fv?.type === 'date' ? fv.value : {};
|
|
105
|
-
const to = existingValue.to;
|
|
106
|
-
onFilterChange(filterKey, from || to ? { type: 'date', value: { from, to } } : undefined);
|
|
107
|
-
}, "aria-label": `${col.name} from date`, style: dateInputStyle })] }), _jsxs("label", { style: dateLabelStyle, children: ["To:", _jsx("input", { type: "date", value: fv?.type === 'date' ? (fv.value.to ?? '') : '', onChange: (e) => {
|
|
108
|
-
const to = e.target.value || undefined;
|
|
109
|
-
const existingValue = fv?.type === 'date' ? fv.value : {};
|
|
110
|
-
const from = existingValue.from;
|
|
111
|
-
onFilterChange(filterKey, from || to ? { type: 'date', value: { from, to } } : undefined);
|
|
112
|
-
}, "aria-label": `${col.name} to date`, style: dateInputStyle })] })] })), col.filterType === 'multiSelect' && (_jsx("div", { style: multiSelectContainerStyle, role: "group", "aria-label": `${col.name} options`, children: (filterOptions[filterKey] ?? []).map((opt) => {
|
|
113
|
-
const selected = fv?.type === 'multiSelect' ? fv.value.includes(opt) : false;
|
|
114
|
-
return (_jsxs("label", { style: multiSelectLabelStyle, children: [_jsx("input", { type: "checkbox", checked: selected, onChange: (e) => {
|
|
115
|
-
const current = fv?.type === 'multiSelect' ? fv.value : [];
|
|
116
|
-
const next = e.target.checked
|
|
117
|
-
? [...current, opt]
|
|
118
|
-
: current.filter((v) => v !== opt);
|
|
119
|
-
onFilterChange(filterKey, next.length > 0 ? { type: 'multiSelect', value: next } : undefined);
|
|
120
|
-
} }), _jsx("span", { children: opt })] }, opt));
|
|
121
|
-
}) }))] }, col.columnId));
|
|
122
|
-
}) }));
|
|
123
|
-
}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { getStatusBarParts } from '../utils';
|
|
3
|
-
export function StatusBar({ classNames, ...rest }) {
|
|
4
|
-
const parts = getStatusBarParts(rest);
|
|
5
|
-
return (_jsx("div", { className: classNames?.statusBar, role: "status", "aria-live": "polite", children: parts.map((p) => (_jsxs("span", { className: classNames?.statusBarItem, children: [_jsx("span", { className: classNames?.statusBarLabel, children: p.label }), _jsx("span", { className: classNames?.statusBarValue, children: p.value.toLocaleString() })] }, p.key))) }));
|
|
6
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import * as React from 'react';
|
|
3
|
-
import { forwardRef } from 'react';
|
|
4
|
-
import { useOGrid } from '../hooks';
|
|
5
|
-
import { OGridLayout } from './OGridLayout';
|
|
6
|
-
/**
|
|
7
|
-
* Factory that creates a memoized, forwardRef OGrid component.
|
|
8
|
-
* Used by Radix and Fluent to avoid duplicating the same wiring code.
|
|
9
|
-
* Material uses its own OGrid because it adds MUI theme bridging (containerSx).
|
|
10
|
-
*/
|
|
11
|
-
export function createOGrid(components) {
|
|
12
|
-
const { DataGridTable, ColumnChooser, PaginationControls, containerComponent, containerProps, } = components;
|
|
13
|
-
const OGridInner = forwardRef(function OGridInner(props, ref) {
|
|
14
|
-
const { dataGridProps, pagination, columnChooser, layout } = useOGrid(props, ref);
|
|
15
|
-
return (_jsx(OGridLayout, { containerComponent: containerComponent, containerProps: containerProps, className: layout.className, sideBar: layout.sideBarProps, toolbar: layout.toolbar, toolbarBelow: layout.toolbarBelow, fullScreen: layout.fullScreen, toolbarEnd: columnChooser.placement === 'toolbar' ? (_jsx(ColumnChooser, { columns: columnChooser.columns, visibleColumns: columnChooser.visibleColumns, onVisibilityChange: columnChooser.onVisibilityChange, onSetVisibleColumns: columnChooser.onSetVisibleColumns })) : undefined, pagination: _jsx(PaginationControls, { currentPage: pagination.page, pageSize: pagination.pageSize, totalCount: pagination.displayTotalCount, onPageChange: pagination.setPage, onPageSizeChange: pagination.setPageSize, pageSizeOptions: pagination.pageSizeOptions, entityLabelPlural: pagination.entityLabelPlural }), children: _jsx(DataGridTable, { ...dataGridProps }) }));
|
|
16
|
-
});
|
|
17
|
-
OGridInner.displayName = 'OGrid';
|
|
18
|
-
return React.memo(OGridInner);
|
|
19
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Module-scope stable constants shared across all React UI DataGridTable implementations.
|
|
3
|
-
* Avoid per-render allocations by keeping these at module scope.
|
|
4
|
-
*/
|
|
5
|
-
/** Root container style for the DataGridTable (flex column layout). */
|
|
6
|
-
export const GRID_ROOT_STYLE = { position: 'relative', flex: 1, minHeight: 0, display: 'flex', flexDirection: 'column' };
|
|
7
|
-
/** Applied to cells that support editing — shows the cell cursor. */
|
|
8
|
-
export const CURSOR_CELL_STYLE = { cursor: 'cell' };
|
|
9
|
-
/** Minimum size for popover anchor elements. */
|
|
10
|
-
export const POPOVER_ANCHOR_STYLE = { minHeight: '100%', minWidth: 40 };
|
|
11
|
-
/** Prevents the default browser action for mouse events. */
|
|
12
|
-
export const PREVENT_DEFAULT = (e) => { e.preventDefault(); };
|
|
13
|
-
/** No-operation function. */
|
|
14
|
-
export const NOOP = () => { };
|
|
15
|
-
/** Stops event propagation (e.g. click on checkbox inside a row). */
|
|
16
|
-
export const STOP_PROPAGATION = (e) => { e.stopPropagation(); };
|
package/dist/esm/hooks/index.js
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
export { useFilterOptions } from './useFilterOptions';
|
|
2
|
-
export { useOGrid } from './useOGrid';
|
|
3
|
-
export { useOGridPagination } from './useOGridPagination';
|
|
4
|
-
export { useOGridSorting } from './useOGridSorting';
|
|
5
|
-
export { useOGridFilters as useOGridFiltersState } from './useOGridFilters';
|
|
6
|
-
export { useOGridDataFetching } from './useOGridDataFetching';
|
|
7
|
-
export { useActiveCell } from './useActiveCell';
|
|
8
|
-
export { useCellEditing } from './useCellEditing';
|
|
9
|
-
export { useContextMenu } from './useContextMenu';
|
|
10
|
-
export { useCellSelection } from './useCellSelection';
|
|
11
|
-
export { useClipboard } from './useClipboard';
|
|
12
|
-
export { useRowSelection } from './useRowSelection';
|
|
13
|
-
export { useKeyboardNavigation } from './useKeyboardNavigation';
|
|
14
|
-
export { useUndoRedo } from './useUndoRedo';
|
|
15
|
-
export { useDebounce } from './useDebounce';
|
|
16
|
-
export { useFillHandle } from './useFillHandle';
|
|
17
|
-
export { useDataGridState } from './useDataGridState';
|
|
18
|
-
export { useDataGridLayout } from './useDataGridLayout';
|
|
19
|
-
export { useDataGridEditing } from './useDataGridEditing';
|
|
20
|
-
export { useDataGridInteraction } from './useDataGridInteraction';
|
|
21
|
-
export { useDataGridContextMenu } from './useDataGridContextMenu';
|
|
22
|
-
export { useColumnHeaderFilterState } from './useColumnHeaderFilterState';
|
|
23
|
-
export { useTextFilterState } from './useTextFilterState';
|
|
24
|
-
export { useMultiSelectFilterState } from './useMultiSelectFilterState';
|
|
25
|
-
export { usePeopleFilterState } from './usePeopleFilterState';
|
|
26
|
-
export { useDateFilterState } from './useDateFilterState';
|
|
27
|
-
export { useColumnChooserState } from './useColumnChooserState';
|
|
28
|
-
export { useInlineCellEditorState } from './useInlineCellEditorState';
|
|
29
|
-
export { useColumnResize } from './useColumnResize';
|
|
30
|
-
export { useColumnPinning } from './useColumnPinning';
|
|
31
|
-
export { useColumnHeaderMenuState } from './useColumnHeaderMenuState';
|
|
32
|
-
export { useRichSelectState } from './useRichSelectState';
|
|
33
|
-
export { useSelectState } from './useSelectState';
|
|
34
|
-
export { useSideBarState } from './useSideBarState';
|
|
35
|
-
export { useTableLayout } from './useTableLayout';
|
|
36
|
-
export { useColumnReorder } from './useColumnReorder';
|
|
37
|
-
export { useVirtualScroll } from './useVirtualScroll';
|
|
38
|
-
export { useListVirtualizer } from './useListVirtualizer';
|
|
39
|
-
export { useLatestRef } from './useLatestRef';
|
|
40
|
-
export { useShallowEqualMemo } from './useShallowEqualMemo';
|
|
41
|
-
export { usePaginationControls } from './usePaginationControls';
|
|
42
|
-
export { useDataGridTableOrchestration } from './useDataGridTableOrchestration';
|
|
43
|
-
export { useColumnMeta } from './useColumnMeta';
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import { useState, useLayoutEffect, useEffect, useCallback, useRef } from 'react';
|
|
2
|
-
/**
|
|
3
|
-
* Tracks the active cell for keyboard navigation.
|
|
4
|
-
* When wrapperRef and editingCell are provided, scrolls the active cell into view when it changes (and not editing).
|
|
5
|
-
*/
|
|
6
|
-
export function useActiveCell(wrapperRef, editingCell) {
|
|
7
|
-
const [activeCell, _setActiveCell] = useState(null);
|
|
8
|
-
const activeCellRef = useRef(activeCell);
|
|
9
|
-
activeCellRef.current = activeCell;
|
|
10
|
-
// Deduplicating setter — skips state update (and all downstream effects) when
|
|
11
|
-
// the cell coordinates haven't actually changed. This prevents re-renders when
|
|
12
|
-
// rapidly clicking the same cell.
|
|
13
|
-
const setActiveCell = useCallback((cell) => {
|
|
14
|
-
const prev = activeCellRef.current;
|
|
15
|
-
if (prev === cell)
|
|
16
|
-
return;
|
|
17
|
-
if (prev && cell && prev.rowIndex === cell.rowIndex && prev.columnIndex === cell.columnIndex)
|
|
18
|
-
return;
|
|
19
|
-
_setActiveCell(cell);
|
|
20
|
-
}, []);
|
|
21
|
-
// RAF ref for batching scroll-into-view during rapid keyboard navigation
|
|
22
|
-
const scrollRafRef = useRef(0);
|
|
23
|
-
// Synchronously focus the cell to prevent the browser from resetting
|
|
24
|
-
// focus to body between arrow presses.
|
|
25
|
-
useLayoutEffect(() => {
|
|
26
|
-
if (activeCell == null || wrapperRef?.current == null || editingCell != null)
|
|
27
|
-
return;
|
|
28
|
-
const { rowIndex, columnIndex } = activeCell;
|
|
29
|
-
const selector = `[data-row-index="${rowIndex}"][data-col-index="${columnIndex}"]`;
|
|
30
|
-
const cell = wrapperRef.current.querySelector(selector);
|
|
31
|
-
if (cell && document.activeElement !== cell && typeof cell.focus === 'function') {
|
|
32
|
-
cell.focus({ preventScroll: true });
|
|
33
|
-
}
|
|
34
|
-
}, [activeCell, editingCell, wrapperRef]);
|
|
35
|
-
// Batch scroll-into-view via RAF so rapid keyboard navigation only scrolls once
|
|
36
|
-
useEffect(() => {
|
|
37
|
-
if (activeCell == null || wrapperRef?.current == null || editingCell != null)
|
|
38
|
-
return;
|
|
39
|
-
cancelAnimationFrame(scrollRafRef.current);
|
|
40
|
-
scrollRafRef.current = requestAnimationFrame(() => {
|
|
41
|
-
const wrapper = wrapperRef?.current;
|
|
42
|
-
if (!wrapper)
|
|
43
|
-
return;
|
|
44
|
-
const { rowIndex, columnIndex } = activeCell;
|
|
45
|
-
const selector = `[data-row-index="${rowIndex}"][data-col-index="${columnIndex}"]`;
|
|
46
|
-
const cell = wrapper.querySelector(selector);
|
|
47
|
-
if (cell) {
|
|
48
|
-
const thead = wrapper.querySelector('thead');
|
|
49
|
-
const headerHeight = thead ? thead.getBoundingClientRect().height : 0;
|
|
50
|
-
const wrapperRect = wrapper.getBoundingClientRect();
|
|
51
|
-
const cellRect = cell.getBoundingClientRect();
|
|
52
|
-
// Vertical scroll (account for sticky thead)
|
|
53
|
-
const visibleTop = wrapperRect.top + headerHeight;
|
|
54
|
-
if (cellRect.top < visibleTop) {
|
|
55
|
-
wrapper.scrollTop -= visibleTop - cellRect.top;
|
|
56
|
-
}
|
|
57
|
-
else if (cellRect.bottom > wrapperRect.bottom) {
|
|
58
|
-
wrapper.scrollTop += cellRect.bottom - wrapperRect.bottom;
|
|
59
|
-
}
|
|
60
|
-
// Horizontal scroll
|
|
61
|
-
if (cellRect.left < wrapperRect.left) {
|
|
62
|
-
wrapper.scrollLeft -= wrapperRect.left - cellRect.left;
|
|
63
|
-
}
|
|
64
|
-
else if (cellRect.right > wrapperRect.right) {
|
|
65
|
-
wrapper.scrollLeft += cellRect.right - wrapperRect.right;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
|
-
}, [activeCell, editingCell, wrapperRef]);
|
|
70
|
-
// Clean up pending RAF on unmount
|
|
71
|
-
useEffect(() => {
|
|
72
|
-
return () => cancelAnimationFrame(scrollRafRef.current);
|
|
73
|
-
}, []);
|
|
74
|
-
return { activeCell, setActiveCell };
|
|
75
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { useState } from 'react';
|
|
2
|
-
/**
|
|
3
|
-
* Manages cell editing state: which cell is being edited and its pending value.
|
|
4
|
-
* @returns Current editing cell, setter, pending editor value, and setter.
|
|
5
|
-
*/
|
|
6
|
-
export function useCellEditing() {
|
|
7
|
-
const [editingCell, setEditingCell] = useState(null);
|
|
8
|
-
const [pendingEditorValue, setPendingEditorValue] = useState(undefined);
|
|
9
|
-
return {
|
|
10
|
-
editingCell,
|
|
11
|
-
setEditingCell,
|
|
12
|
-
pendingEditorValue,
|
|
13
|
-
setPendingEditorValue,
|
|
14
|
-
};
|
|
15
|
-
}
|