@alaarab/ogrid-core 1.6.0 → 1.7.2

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/README.md CHANGED
@@ -32,14 +32,13 @@ npm install @alaarab/ogrid-core
32
32
 
33
33
  ### Components
34
34
 
35
- - `OGridLayout` -- Layout structure for OGrid: toolbar row (title, toolbar, columnChooser), grid area, pagination. Accepts `containerComponent` and `gap` so Fluent/Material/Radix supply their Container (div or Box).
35
+ - `OGridLayout` -- Unified bordered layout: toolbar strip (custom content + column chooser), optional secondary toolbar row, sidebar, grid area, and footer strip (pagination).
36
36
 
37
37
  ### Utilities
38
38
 
39
39
  - `getPaginationViewModel(...)` -- Page numbers, ellipsis, start/end item for PaginationControls
40
40
  - `getHeaderFilterConfig(col, input)` -- ColumnHeaderFilter props from column + filter/sort state
41
41
  - `getCellRenderDescriptor(item, col, rowIndex, colIdx, input)` -- Cell mode (editing-inline / editing-popover / display) and flags for DataGridTable
42
- - `toDataGridFilterProps(filters)` -- Splits `IFilters` into `multiSelectFilters`, `textFilters`, `peopleFilters`
43
42
  - `toUserLike(user)` -- Converts a user-like object to `UserLike`
44
43
  - `exportToCsv(items, columns, getValue, filename)` -- Full CSV export
45
44
  - `buildCsvHeader`, `buildCsvRows`, `triggerCsvDownload`, `escapeCsvValue` -- Low-level CSV helpers
@@ -11,22 +11,33 @@ const borderedContainerStyle = {
11
11
  minHeight: 0,
12
12
  background: 'var(--ogrid-bg, #fff)',
13
13
  };
14
- const toolbarStripStyle = {
14
+ const toolbarStripBase = {
15
15
  display: 'flex',
16
16
  justifyContent: 'space-between',
17
17
  alignItems: 'center',
18
18
  padding: '6px 12px',
19
- borderBottom: '1px solid var(--ogrid-border, #e0e0e0)',
20
19
  background: 'var(--ogrid-header-bg, #f5f5f5)',
21
20
  gap: 8,
22
21
  flexWrap: 'wrap',
23
22
  minHeight: 0,
24
23
  };
24
+ /** Toolbar strip with border-bottom (when it's the only toolbar row). */
25
+ const toolbarStripStyle = {
26
+ ...toolbarStripBase,
27
+ borderBottom: '1px solid var(--ogrid-border, #e0e0e0)',
28
+ };
29
+ /** Toolbar strip without border-bottom (when toolbarBelow follows — it owns the border). */
30
+ const toolbarStripNoBorderStyle = toolbarStripBase;
25
31
  const toolbarSectionStyle = {
26
32
  display: 'flex',
27
33
  alignItems: 'center',
28
34
  gap: 8,
29
35
  };
36
+ const toolbarBelowStyle = {
37
+ padding: '6px 12px',
38
+ borderBottom: '1px solid var(--ogrid-border, #e0e0e0)',
39
+ background: 'var(--ogrid-header-bg, #f5f5f5)',
40
+ };
30
41
  const footerStripStyle = {
31
42
  borderTop: '1px solid var(--ogrid-border, #e0e0e0)',
32
43
  background: 'var(--ogrid-header-bg, #f5f5f5)',
@@ -65,7 +76,7 @@ const gridChildStyle = {
65
76
  * └────────────────────────────────────┘
66
77
  */
67
78
  export function OGridLayout(props) {
68
- const { containerComponent: Container = 'div', containerProps = {}, className, toolbar, toolbarEnd, children, pagination, sideBar, } = props;
79
+ const { containerComponent: Container = 'div', containerProps = {}, className, toolbar, toolbarEnd, toolbarBelow, children, pagination, sideBar, } = props;
69
80
  const hasSideBar = sideBar != null;
70
81
  const sideBarPosition = sideBar?.position ?? 'right';
71
82
  const hasToolbar = toolbar != null || toolbarEnd != null;
@@ -74,5 +85,5 @@ export function OGridLayout(props) {
74
85
  flexDirection: 'column',
75
86
  height: '100%',
76
87
  };
77
- return (_jsx(Container, { className: className, style: rootStyle, ...containerProps, children: _jsxs("div", { style: borderedContainerStyle, children: [hasToolbar && (_jsxs("div", { style: toolbarStripStyle, children: [_jsx("div", { style: toolbarSectionStyle, children: toolbar }), _jsx("div", { style: toolbarSectionStyle, children: toolbarEnd })] })), 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 }))] }) }));
88
+ return (_jsx(Container, { className: className, style: rootStyle, ...containerProps, children: _jsxs("div", { style: borderedContainerStyle, children: [hasToolbar && (_jsxs("div", { style: toolbarBelow ? toolbarStripNoBorderStyle : toolbarStripStyle, children: [_jsx("div", { style: toolbarSectionStyle, children: toolbar }), _jsx("div", { style: toolbarSectionStyle, children: toolbarEnd })] })), 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 }))] }) }));
78
89
  }
@@ -99,6 +99,8 @@ export function useDataGridState(params) {
99
99
  endBatch: undoRedo.endBatch,
100
100
  });
101
101
  const handleCellMouseDown = useCallback((e, rowIndex, globalColIndex) => {
102
+ if (e.button !== 0)
103
+ return;
102
104
  wrapperRef.current?.focus();
103
105
  clearClipboardRanges();
104
106
  handleCellMouseDownBase(e, rowIndex, globalColIndex);
@@ -5,7 +5,7 @@ import { useFilterOptions } from './useFilterOptions';
5
5
  import { useSideBarState } from './useSideBarState';
6
6
  const DEFAULT_PAGE_SIZE = 25;
7
7
  export function useOGrid(props, ref) {
8
- const { columns: columnsProp, getRowId, data, dataSource, page: controlledPage, pageSize: controlledPageSize, sort: controlledSort, filters: controlledFilters, visibleColumns: controlledVisibleColumns, isLoading: controlledLoading, onPageChange, onPageSizeChange, onSortChange, onFiltersChange, onVisibleColumnsChange, columnOrder, onColumnOrderChange, onColumnResized, onColumnPinned, freezeRows, freezeCols, defaultPageSize = DEFAULT_PAGE_SIZE, defaultSortBy, defaultSortDirection = 'asc', toolbar, emptyState, entityLabelPlural = 'items', className, layoutMode = 'fill', suppressHorizontalScroll, editable, cellSelection, onCellValueChanged, onUndo, onRedo, canUndo, canRedo, rowSelection = 'none', selectedRows, onSelectionChange, statusBar, pageSizeOptions, sideBar, onFirstDataRendered, onError, columnChooser: columnChooserProp, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, } = props;
8
+ const { columns: columnsProp, getRowId, data, dataSource, page: controlledPage, pageSize: controlledPageSize, sort: controlledSort, filters: controlledFilters, visibleColumns: controlledVisibleColumns, isLoading: controlledLoading, onPageChange, onPageSizeChange, onSortChange, onFiltersChange, onVisibleColumnsChange, columnOrder, onColumnOrderChange, onColumnResized, onColumnPinned, freezeRows, freezeCols, defaultPageSize = DEFAULT_PAGE_SIZE, defaultSortBy, defaultSortDirection = 'asc', toolbar, toolbarBelow, emptyState, entityLabelPlural = 'items', className, layoutMode = 'fill', suppressHorizontalScroll, editable, cellSelection, onCellValueChanged, onUndo, onRedo, canUndo, canRedo, rowSelection = 'none', selectedRows, onSelectionChange, statusBar, pageSizeOptions, sideBar, onFirstDataRendered, onError, columnChooser: columnChooserProp, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, } = props;
9
9
  // Resolve column chooser placement
10
10
  const columnChooserPlacement = columnChooserProp === false ? 'none'
11
11
  : columnChooserProp === 'sidebar' ? 'sidebar'
@@ -402,6 +402,7 @@ export function useOGrid(props, ref) {
402
402
  handleVisibilityChange,
403
403
  columnChooserPlacement,
404
404
  toolbar,
405
+ toolbarBelow,
405
406
  className,
406
407
  entityLabelPlural,
407
408
  emptyState,
@@ -55,8 +55,8 @@ export function useRowSelection(params) {
55
55
  updateSelection(new Set());
56
56
  }
57
57
  }, [items, getRowId, updateSelection]);
58
- const allSelected = items.length > 0 && items.every((item) => selectedRowIds.has(getRowId(item)));
59
- const someSelected = !allSelected && items.some((item) => selectedRowIds.has(getRowId(item)));
58
+ const allSelected = useMemo(() => items.length > 0 && items.every((item) => selectedRowIds.has(getRowId(item))), [items, selectedRowIds, getRowId]);
59
+ const someSelected = useMemo(() => !allSelected && items.some((item) => selectedRowIds.has(getRowId(item))), [allSelected, items, selectedRowIds, getRowId]);
60
60
  return {
61
61
  selectedRowIds,
62
62
  updateSelection,
@@ -15,6 +15,8 @@ export interface OGridLayoutProps {
15
15
  toolbar?: React.ReactNode;
16
16
  /** Built-in toolbar items rendered on the right side (column chooser, etc.). */
17
17
  toolbarEnd?: React.ReactNode;
18
+ /** Secondary toolbar row below the primary toolbar (e.g. active filter chips). Full width. */
19
+ toolbarBelow?: React.ReactNode;
18
20
  /** Grid content (DataGridTable). */
19
21
  children: React.ReactNode;
20
22
  /** Pagination controls (rendered in footer strip inside the bordered container). */
@@ -16,6 +16,7 @@ export interface UseOGridResult<T> {
16
16
  /** Resolved placement of the column chooser. */
17
17
  columnChooserPlacement: ColumnChooserPlacement;
18
18
  toolbar: React.ReactNode;
19
+ toolbarBelow: React.ReactNode;
19
20
  className?: string;
20
21
  entityLabelPlural: string;
21
22
  emptyState?: {
@@ -197,6 +197,8 @@ export interface IOGridProps<T> {
197
197
  defaultSortBy?: string;
198
198
  defaultSortDirection?: 'asc' | 'desc';
199
199
  toolbar?: ReactNode;
200
+ /** Secondary toolbar row rendered below the primary toolbar (e.g. active filter chips). */
201
+ toolbarBelow?: ReactNode;
200
202
  emptyState?: {
201
203
  message?: ReactNode;
202
204
  render?: () => ReactNode;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alaarab/ogrid-core",
3
- "version": "1.6.0",
3
+ "version": "1.7.2",
4
4
  "description": "OGrid core – framework-agnostic types, hooks, and utilities for OGrid data tables.",
5
5
  "main": "dist/esm/index.js",
6
6
  "module": "dist/esm/index.js",