@alaarab/ogrid-core 2.0.9 → 2.0.12
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/utils/columnAutosize.js +38 -0
- package/dist/esm/utils/dataGridViewModel.js +2 -1
- package/dist/esm/utils/dom.js +53 -0
- package/dist/esm/utils/gridContextMenuHelpers.js +28 -0
- package/dist/esm/utils/index.js +4 -1
- package/dist/esm/utils/sortHelpers.js +28 -0
- package/dist/types/utils/columnAutosize.d.ts +21 -0
- package/dist/types/utils/dataGridViewModel.d.ts +1 -1
- package/dist/types/utils/dom.d.ts +38 -0
- package/dist/types/utils/gridContextMenuHelpers.d.ts +28 -0
- package/dist/types/utils/index.d.ts +7 -2
- package/dist/types/utils/sortHelpers.d.ts +16 -0
- package/package.json +1 -1
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Column autosize DOM measurement utilities shared across all frameworks.
|
|
3
|
+
*/
|
|
4
|
+
import { DEFAULT_MIN_COLUMN_WIDTH } from '../constants/layout';
|
|
5
|
+
/** Extra pixels added to header label width to account for sort/filter icons + padding. */
|
|
6
|
+
export const AUTOSIZE_EXTRA_PX = 44;
|
|
7
|
+
/** Maximum column width from autosize. */
|
|
8
|
+
export const AUTOSIZE_MAX_PX = 520;
|
|
9
|
+
/**
|
|
10
|
+
* Measure the ideal width for a column by scanning all DOM cells with
|
|
11
|
+
* `[data-column-id="<columnId>"]` and computing the maximum scrollWidth.
|
|
12
|
+
*
|
|
13
|
+
* Header cells with a `[data-header-label]` child get extra padding for icons.
|
|
14
|
+
*
|
|
15
|
+
* @param columnId - Column to measure
|
|
16
|
+
* @param minWidth - Minimum width (defaults to DEFAULT_MIN_COLUMN_WIDTH)
|
|
17
|
+
* @param container - Optional container element to scope the query (defaults to `document`)
|
|
18
|
+
* @returns The ideal column width in pixels, clamped between minWidth and AUTOSIZE_MAX_PX
|
|
19
|
+
*/
|
|
20
|
+
export function measureColumnContentWidth(columnId, minWidth, container) {
|
|
21
|
+
const minW = minWidth ?? DEFAULT_MIN_COLUMN_WIDTH;
|
|
22
|
+
const root = container ?? document;
|
|
23
|
+
const cells = root.querySelectorAll(`[data-column-id="${columnId}"]`);
|
|
24
|
+
if (cells.length === 0)
|
|
25
|
+
return minW;
|
|
26
|
+
let maxWidth = minW;
|
|
27
|
+
cells.forEach((cell) => {
|
|
28
|
+
const el = cell;
|
|
29
|
+
const label = el.querySelector?.('[data-header-label]');
|
|
30
|
+
if (label) {
|
|
31
|
+
maxWidth = Math.max(maxWidth, label.scrollWidth + AUTOSIZE_EXTRA_PX);
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
maxWidth = Math.max(maxWidth, el.scrollWidth);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
return Math.min(AUTOSIZE_MAX_PX, Math.max(minW, Math.ceil(maxWidth)));
|
|
38
|
+
}
|
|
@@ -76,7 +76,8 @@ export function getCellRenderDescriptor(item, col, rowIndex, colIdx, input) {
|
|
|
76
76
|
const canEditAny = canEditInline || canEditPopup;
|
|
77
77
|
const isEditing = input.editingCell?.rowId === rowId &&
|
|
78
78
|
input.editingCell?.columnId === col.columnId;
|
|
79
|
-
const isActive = input.
|
|
79
|
+
const isActive = !input.isDragging &&
|
|
80
|
+
input.activeCell?.rowIndex === rowIndex &&
|
|
80
81
|
input.activeCell?.columnIndex === globalColIndex;
|
|
81
82
|
const isInRange = input.selectionRange != null &&
|
|
82
83
|
isInSelectionRange(input.selectionRange, rowIndex, colIdx);
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DOM utility functions for OGrid components.
|
|
3
|
+
* These utilities are framework-agnostic and can be used across React, Angular, Vue, and vanilla JS implementations.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Measure the bounding rect of a cell range within a container.
|
|
7
|
+
*
|
|
8
|
+
* @param container - The grid container element with position: relative
|
|
9
|
+
* @param range - The selection range to measure
|
|
10
|
+
* @param colOffset - Column offset (1 when checkbox column is present, else 0)
|
|
11
|
+
* @returns Rectangle describing the range position and size, or null if cells not found
|
|
12
|
+
*/
|
|
13
|
+
export function measureRange(container, range, colOffset) {
|
|
14
|
+
const startGlobalCol = range.startCol + colOffset;
|
|
15
|
+
const endGlobalCol = range.endCol + colOffset;
|
|
16
|
+
const topLeft = container.querySelector(`[data-row-index="${range.startRow}"][data-col-index="${startGlobalCol}"]`);
|
|
17
|
+
const bottomRight = container.querySelector(`[data-row-index="${range.endRow}"][data-col-index="${endGlobalCol}"]`);
|
|
18
|
+
if (!topLeft || !bottomRight)
|
|
19
|
+
return null;
|
|
20
|
+
const cRect = container.getBoundingClientRect();
|
|
21
|
+
const tlRect = topLeft.getBoundingClientRect();
|
|
22
|
+
const brRect = bottomRight.getBoundingClientRect();
|
|
23
|
+
return {
|
|
24
|
+
top: tlRect.top - cRect.top,
|
|
25
|
+
left: tlRect.left - cRect.left,
|
|
26
|
+
width: brRect.right - tlRect.left,
|
|
27
|
+
height: brRect.bottom - tlRect.top,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Inject a global CSS rule into the document head (once per page, deduplicated by ID).
|
|
32
|
+
*
|
|
33
|
+
* @param id - Unique ID for the style element (prevents duplicates)
|
|
34
|
+
* @param css - CSS content to inject
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```ts
|
|
38
|
+
* injectGlobalStyles(
|
|
39
|
+
* 'ogrid-marching-ants-keyframes',
|
|
40
|
+
* '@keyframes ogrid-marching-ants{to{stroke-dashoffset:-8}}'
|
|
41
|
+
* );
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export function injectGlobalStyles(id, css) {
|
|
45
|
+
if (typeof document === 'undefined')
|
|
46
|
+
return;
|
|
47
|
+
if (document.getElementById(id))
|
|
48
|
+
return;
|
|
49
|
+
const style = document.createElement('style');
|
|
50
|
+
style.id = id;
|
|
51
|
+
style.textContent = css;
|
|
52
|
+
document.head.appendChild(style);
|
|
53
|
+
}
|
|
@@ -50,3 +50,31 @@ export const COLUMN_HEADER_MENU_ITEMS = [
|
|
|
50
50
|
{ id: 'pinRight', label: 'Pin right' },
|
|
51
51
|
{ id: 'unpin', label: 'Unpin' },
|
|
52
52
|
];
|
|
53
|
+
/**
|
|
54
|
+
* Builds the complete column header menu items based on current state.
|
|
55
|
+
* Returns pinning, sorting, and sizing options.
|
|
56
|
+
*/
|
|
57
|
+
export function getColumnHeaderMenuItems(input) {
|
|
58
|
+
const { canPinLeft, canPinRight, canUnpin, currentSort, isSortable = true, isResizable = true } = input;
|
|
59
|
+
const items = [];
|
|
60
|
+
// Pinning section
|
|
61
|
+
items.push({ id: 'pinLeft', label: 'Pin left', disabled: !canPinLeft }, { id: 'pinRight', label: 'Pin right', disabled: !canPinRight }, { id: 'unpin', label: 'Unpin', disabled: !canUnpin, divider: isSortable || isResizable });
|
|
62
|
+
// Sorting section
|
|
63
|
+
if (isSortable) {
|
|
64
|
+
if (!currentSort) {
|
|
65
|
+
// No sort applied - show both options
|
|
66
|
+
items.push({ id: 'sortAsc', label: 'Sort ascending' }, { id: 'sortDesc', label: 'Sort descending', divider: isResizable });
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
// Sort applied - show opposite + clear
|
|
70
|
+
const oppositeSort = currentSort === 'asc' ? 'desc' : 'asc';
|
|
71
|
+
const oppositeLabel = currentSort === 'asc' ? 'Sort descending' : 'Sort ascending';
|
|
72
|
+
items.push({ id: `sort${oppositeSort === 'asc' ? 'Asc' : 'Desc'}`, label: oppositeLabel }, { id: 'clearSort', label: 'Clear sort', divider: isResizable });
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// Autosize section
|
|
76
|
+
if (isResizable) {
|
|
77
|
+
items.push({ id: 'autosizeThis', label: 'Autosize this column' }, { id: 'autosizeAll', label: 'Autosize all columns' });
|
|
78
|
+
}
|
|
79
|
+
return items;
|
|
80
|
+
}
|
package/dist/esm/utils/index.js
CHANGED
|
@@ -5,7 +5,7 @@ export { getFilterField, mergeFilter, deriveFilterOptionsFromData, getMultiSelec
|
|
|
5
5
|
export { getStatusBarParts } from './statusBarHelpers';
|
|
6
6
|
export { getDataGridStatusBarConfig } from './dataGridStatusBar';
|
|
7
7
|
export { getPaginationViewModel, PAGE_SIZE_OPTIONS, MAX_PAGE_BUTTONS, } from './paginationHelpers';
|
|
8
|
-
export { GRID_CONTEXT_MENU_ITEMS, COLUMN_HEADER_MENU_ITEMS, getContextMenuHandlers, formatShortcut } from './gridContextMenuHelpers';
|
|
8
|
+
export { GRID_CONTEXT_MENU_ITEMS, COLUMN_HEADER_MENU_ITEMS, getContextMenuHandlers, getColumnHeaderMenuItems, formatShortcut } from './gridContextMenuHelpers';
|
|
9
9
|
export { parseValue, numberParser, currencyParser, dateParser, emailParser, booleanParser, } from './valueParsers';
|
|
10
10
|
export { computeAggregations } from './aggregationUtils';
|
|
11
11
|
export { processClientSideData } from './clientSideData';
|
|
@@ -14,3 +14,6 @@ export { getPinStateForColumn, reorderColumnArray, calculateDropTarget, } from '
|
|
|
14
14
|
export { computeVisibleRange, computeTotalHeight, getScrollTopForRow, } from './virtualScroll';
|
|
15
15
|
export { getHeaderFilterConfig, getCellRenderDescriptor, resolveCellDisplayContent, resolveCellStyle, buildInlineEditorProps, buildPopoverEditorProps, } from './dataGridViewModel';
|
|
16
16
|
export { debounce } from './debounce';
|
|
17
|
+
export { measureRange, injectGlobalStyles } from './dom';
|
|
18
|
+
export { computeNextSortState } from './sortHelpers';
|
|
19
|
+
export { measureColumnContentWidth, AUTOSIZE_EXTRA_PX, AUTOSIZE_MAX_PX } from './columnAutosize';
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sort state computation helpers shared across all frameworks.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Compute the next sort state given the current state and a sort request.
|
|
6
|
+
*
|
|
7
|
+
* @param current - Current sort state
|
|
8
|
+
* @param columnKey - Column being sorted
|
|
9
|
+
* @param direction - Explicit direction, `null` to clear, or `undefined` to toggle
|
|
10
|
+
* @returns New sort state
|
|
11
|
+
*/
|
|
12
|
+
export function computeNextSortState(current, columnKey, direction) {
|
|
13
|
+
if (direction === null) {
|
|
14
|
+
// Clear sort
|
|
15
|
+
return { field: '', direction: 'asc' };
|
|
16
|
+
}
|
|
17
|
+
else if (direction) {
|
|
18
|
+
// Explicit direction (from column menu)
|
|
19
|
+
return { field: columnKey, direction };
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
// Toggle (existing behavior for header click)
|
|
23
|
+
return {
|
|
24
|
+
field: columnKey,
|
|
25
|
+
direction: current.field === columnKey && current.direction === 'asc' ? 'desc' : 'asc',
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Column autosize DOM measurement utilities shared across all frameworks.
|
|
3
|
+
*/
|
|
4
|
+
/** Extra pixels added to header label width to account for sort/filter icons + padding. */
|
|
5
|
+
export declare const AUTOSIZE_EXTRA_PX = 44;
|
|
6
|
+
/** Maximum column width from autosize. */
|
|
7
|
+
export declare const AUTOSIZE_MAX_PX = 520;
|
|
8
|
+
/**
|
|
9
|
+
* Measure the ideal width for a column by scanning all DOM cells with
|
|
10
|
+
* `[data-column-id="<columnId>"]` and computing the maximum scrollWidth.
|
|
11
|
+
*
|
|
12
|
+
* Header cells with a `[data-header-label]` child get extra padding for icons.
|
|
13
|
+
*
|
|
14
|
+
* @param columnId - Column to measure
|
|
15
|
+
* @param minWidth - Minimum width (defaults to DEFAULT_MIN_COLUMN_WIDTH)
|
|
16
|
+
* @param container - Optional container element to scope the query (defaults to `document`)
|
|
17
|
+
* @returns The ideal column width in pixels, clamped between minWidth and AUTOSIZE_MAX_PX
|
|
18
|
+
*/
|
|
19
|
+
export declare function measureColumnContentWidth(columnId: string, minWidth?: number, container?: {
|
|
20
|
+
querySelectorAll: (selector: string) => NodeListOf<Element>;
|
|
21
|
+
}): number;
|
|
@@ -9,7 +9,7 @@ import type { RowId, UserLike, IFilters, FilterValue } from '../types/dataGridTy
|
|
|
9
9
|
export interface HeaderFilterConfigInput {
|
|
10
10
|
sortBy?: string;
|
|
11
11
|
sortDirection: 'asc' | 'desc';
|
|
12
|
-
onColumnSort: (columnKey: string) => void;
|
|
12
|
+
onColumnSort: (columnKey: string, direction?: 'asc' | 'desc' | null) => void;
|
|
13
13
|
filters: IFilters;
|
|
14
14
|
onFilterChange: (key: string, value: FilterValue | undefined) => void;
|
|
15
15
|
filterOptions: Record<string, string[]>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DOM utility functions for OGrid components.
|
|
3
|
+
* These utilities are framework-agnostic and can be used across React, Angular, Vue, and vanilla JS implementations.
|
|
4
|
+
*/
|
|
5
|
+
import type { ISelectionRange } from '../types';
|
|
6
|
+
/**
|
|
7
|
+
* Rectangle describing position and dimensions of a DOM element.
|
|
8
|
+
*/
|
|
9
|
+
export interface OverlayRect {
|
|
10
|
+
top: number;
|
|
11
|
+
left: number;
|
|
12
|
+
width: number;
|
|
13
|
+
height: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Measure the bounding rect of a cell range within a container.
|
|
17
|
+
*
|
|
18
|
+
* @param container - The grid container element with position: relative
|
|
19
|
+
* @param range - The selection range to measure
|
|
20
|
+
* @param colOffset - Column offset (1 when checkbox column is present, else 0)
|
|
21
|
+
* @returns Rectangle describing the range position and size, or null if cells not found
|
|
22
|
+
*/
|
|
23
|
+
export declare function measureRange(container: HTMLElement, range: ISelectionRange, colOffset: number): OverlayRect | null;
|
|
24
|
+
/**
|
|
25
|
+
* Inject a global CSS rule into the document head (once per page, deduplicated by ID).
|
|
26
|
+
*
|
|
27
|
+
* @param id - Unique ID for the style element (prevents duplicates)
|
|
28
|
+
* @param css - CSS content to inject
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```ts
|
|
32
|
+
* injectGlobalStyles(
|
|
33
|
+
* 'ogrid-marching-ants-keyframes',
|
|
34
|
+
* '@keyframes ogrid-marching-ants{to{stroke-dashoffset:-8}}'
|
|
35
|
+
* );
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export declare function injectGlobalStyles(id: string, css: string): void;
|
|
@@ -34,6 +34,34 @@ export interface IColumnHeaderMenuItem {
|
|
|
34
34
|
id: string;
|
|
35
35
|
label: string;
|
|
36
36
|
icon?: string;
|
|
37
|
+
disabled?: boolean;
|
|
38
|
+
divider?: boolean;
|
|
37
39
|
}
|
|
38
40
|
/** Column header menu items for pin/unpin actions. */
|
|
39
41
|
export declare const COLUMN_HEADER_MENU_ITEMS: IColumnHeaderMenuItem[];
|
|
42
|
+
/** Input for building column header menu items. */
|
|
43
|
+
export interface ColumnHeaderMenuInput {
|
|
44
|
+
canPinLeft: boolean;
|
|
45
|
+
canPinRight: boolean;
|
|
46
|
+
canUnpin: boolean;
|
|
47
|
+
currentSort?: 'asc' | 'desc' | null;
|
|
48
|
+
isSortable?: boolean;
|
|
49
|
+
isResizable?: boolean;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Builds the complete column header menu items based on current state.
|
|
53
|
+
* Returns pinning, sorting, and sizing options.
|
|
54
|
+
*/
|
|
55
|
+
export declare function getColumnHeaderMenuItems(input: ColumnHeaderMenuInput): IColumnHeaderMenuItem[];
|
|
56
|
+
/** Handlers for column header menu actions. */
|
|
57
|
+
export interface ColumnHeaderMenuHandlers {
|
|
58
|
+
onPinLeft: () => void;
|
|
59
|
+
onPinRight: () => void;
|
|
60
|
+
onUnpin: () => void;
|
|
61
|
+
onSortAsc: () => void;
|
|
62
|
+
onSortDesc: () => void;
|
|
63
|
+
onClearSort: () => void;
|
|
64
|
+
onAutosizeThis: () => void;
|
|
65
|
+
onAutosizeAll: () => void;
|
|
66
|
+
onClose: () => void;
|
|
67
|
+
}
|
|
@@ -6,10 +6,10 @@ export { getStatusBarParts } from './statusBarHelpers';
|
|
|
6
6
|
export { getDataGridStatusBarConfig } from './dataGridStatusBar';
|
|
7
7
|
export { getPaginationViewModel, PAGE_SIZE_OPTIONS, MAX_PAGE_BUTTONS, } from './paginationHelpers';
|
|
8
8
|
export type { PaginationViewModel } from './paginationHelpers';
|
|
9
|
-
export { GRID_CONTEXT_MENU_ITEMS, COLUMN_HEADER_MENU_ITEMS, getContextMenuHandlers, formatShortcut } from './gridContextMenuHelpers';
|
|
9
|
+
export { GRID_CONTEXT_MENU_ITEMS, COLUMN_HEADER_MENU_ITEMS, getContextMenuHandlers, getColumnHeaderMenuItems, formatShortcut } from './gridContextMenuHelpers';
|
|
10
10
|
export type { CsvColumn } from './exportToCsv';
|
|
11
11
|
export type { StatusBarPart, StatusBarPartsInput } from './statusBarHelpers';
|
|
12
|
-
export type { GridContextMenuItem, IColumnHeaderMenuItem, GridContextMenuHandlerProps } from './gridContextMenuHelpers';
|
|
12
|
+
export type { GridContextMenuItem, IColumnHeaderMenuItem, GridContextMenuHandlerProps, ColumnHeaderMenuInput, ColumnHeaderMenuHandlers } from './gridContextMenuHelpers';
|
|
13
13
|
export { parseValue, numberParser, currencyParser, dateParser, emailParser, booleanParser, } from './valueParsers';
|
|
14
14
|
export type { ParseValueResult } from './valueParsers';
|
|
15
15
|
export { computeAggregations } from './aggregationUtils';
|
|
@@ -24,3 +24,8 @@ export type { IVisibleRange } from './virtualScroll';
|
|
|
24
24
|
export { getHeaderFilterConfig, getCellRenderDescriptor, resolveCellDisplayContent, resolveCellStyle, buildInlineEditorProps, buildPopoverEditorProps, } from './dataGridViewModel';
|
|
25
25
|
export type { HeaderFilterConfigInput, HeaderFilterConfig, CellRenderDescriptorInput, CellRenderDescriptor, CellRenderMode, } from './dataGridViewModel';
|
|
26
26
|
export { debounce } from './debounce';
|
|
27
|
+
export { measureRange, injectGlobalStyles } from './dom';
|
|
28
|
+
export type { OverlayRect } from './dom';
|
|
29
|
+
export { computeNextSortState } from './sortHelpers';
|
|
30
|
+
export type { ISortState } from './sortHelpers';
|
|
31
|
+
export { measureColumnContentWidth, AUTOSIZE_EXTRA_PX, AUTOSIZE_MAX_PX } from './columnAutosize';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sort state computation helpers shared across all frameworks.
|
|
3
|
+
*/
|
|
4
|
+
export interface ISortState {
|
|
5
|
+
field: string;
|
|
6
|
+
direction: 'asc' | 'desc';
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Compute the next sort state given the current state and a sort request.
|
|
10
|
+
*
|
|
11
|
+
* @param current - Current sort state
|
|
12
|
+
* @param columnKey - Column being sorted
|
|
13
|
+
* @param direction - Explicit direction, `null` to clear, or `undefined` to toggle
|
|
14
|
+
* @returns New sort state
|
|
15
|
+
*/
|
|
16
|
+
export declare function computeNextSortState(current: ISortState, columnKey: string, direction?: 'asc' | 'desc' | null): ISortState;
|
package/package.json
CHANGED