photon-grid-core 2.0.1 → 2.0.5
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 +3 -3
- package/cell-selection/cell-selection-engine.d.ts +14 -0
- package/cell-selection/cell-selection-engine.d.ts.map +1 -1
- package/cell-selection/cell-selection-engine.js +63 -9
- package/cell-selection/cell-selection-engine.js.map +1 -1
- package/chart/range-chart-controller.d.ts +10 -0
- package/chart/range-chart-controller.d.ts.map +1 -1
- package/chart/range-chart-controller.js +22 -2
- package/chart/range-chart-controller.js.map +1 -1
- package/column-groups/column-group-drag-handler.d.ts.map +1 -1
- package/column-groups/column-group-drag-handler.js +64 -25
- package/column-groups/column-group-drag-handler.js.map +1 -1
- package/column-groups/column-group-header-builder.d.ts.map +1 -1
- package/column-groups/column-group-header-builder.js +7 -5
- package/column-groups/column-group-header-builder.js.map +1 -1
- package/column-groups/display-group-drag-handler.d.ts +7 -3
- package/column-groups/display-group-drag-handler.d.ts.map +1 -1
- package/column-groups/display-group-drag-handler.js +46 -7
- package/column-groups/display-group-drag-handler.js.map +1 -1
- package/column-groups/display-group-header-builder.d.ts +1 -1
- package/column-groups/display-group-header-builder.d.ts.map +1 -1
- package/column-groups/display-group-header-builder.js +9 -7
- package/column-groups/display-group-header-builder.js.map +1 -1
- package/core/column-model.d.ts +88 -5
- package/core/column-model.d.ts.map +1 -1
- package/core/column-model.js +215 -3
- package/core/column-model.js.map +1 -1
- package/core/grid-api.d.ts +34 -2
- package/core/grid-api.d.ts.map +1 -1
- package/core/grid-api.js +40 -0
- package/core/grid-api.js.map +1 -1
- package/core/grid-core.d.ts +7 -0
- package/core/grid-core.d.ts.map +1 -1
- package/core/grid-core.js +42 -12
- package/core/grid-core.js.map +1 -1
- package/core/grid-store.d.ts +5 -5
- package/core/grid-store.d.ts.map +1 -1
- package/core/pointer-utils.d.ts +59 -0
- package/core/pointer-utils.d.ts.map +1 -0
- package/core/pointer-utils.js +59 -0
- package/core/pointer-utils.js.map +1 -0
- package/drag-drop/drag-drop-engine.d.ts.map +1 -1
- package/drag-drop/drag-drop-engine.js +8 -8
- package/drag-drop/drag-drop-engine.js.map +1 -1
- package/engines/editing/cell-editor-engine.js +1 -1
- package/engines/editing/cell-editor-engine.js.map +1 -1
- package/engines/master-detail/master-detail-engine.d.ts +4 -3
- package/engines/master-detail/master-detail-engine.d.ts.map +1 -1
- package/engines/master-detail/master-detail-engine.js +14 -5
- package/engines/master-detail/master-detail-engine.js.map +1 -1
- package/engines/selection/row-selection-engine.d.ts +41 -0
- package/engines/selection/row-selection-engine.d.ts.map +1 -1
- package/engines/selection/row-selection-engine.js +103 -0
- package/engines/selection/row-selection-engine.js.map +1 -1
- package/index.d.ts +8 -4
- package/index.d.ts.map +1 -1
- package/index.js +4 -1
- package/index.js.map +1 -1
- package/package.json +1 -1
- package/photon-ai/index.d.ts +2 -0
- package/photon-ai/index.d.ts.map +1 -1
- package/photon-ai/index.js +2 -0
- package/photon-ai/index.js.map +1 -1
- package/photon-ai/photon-ai-panel.d.ts +40 -0
- package/photon-ai/photon-ai-panel.d.ts.map +1 -1
- package/photon-ai/photon-ai-panel.js +163 -8
- package/photon-ai/photon-ai-panel.js.map +1 -1
- package/photon-ai/photon-ai-service.d.ts +38 -1
- package/photon-ai/photon-ai-service.d.ts.map +1 -1
- package/photon-ai/photon-ai-service.js +72 -1
- package/photon-ai/photon-ai-service.js.map +1 -1
- package/photon-ai/provider/ai-provider.types.d.ts +174 -0
- package/photon-ai/provider/ai-provider.types.d.ts.map +1 -0
- package/photon-ai/provider/ai-provider.types.js +42 -0
- package/photon-ai/provider/ai-provider.types.js.map +1 -0
- package/photon-ai/provider/command-normalizer.d.ts +36 -0
- package/photon-ai/provider/command-normalizer.d.ts.map +1 -0
- package/photon-ai/provider/command-normalizer.js +120 -0
- package/photon-ai/provider/command-normalizer.js.map +1 -0
- package/photon-ai/provider/grid-context-builder.d.ts +37 -0
- package/photon-ai/provider/grid-context-builder.d.ts.map +1 -0
- package/photon-ai/provider/grid-context-builder.js +96 -0
- package/photon-ai/provider/grid-context-builder.js.map +1 -0
- package/photon-ai/provider/http-ai-provider.d.ts +42 -0
- package/photon-ai/provider/http-ai-provider.d.ts.map +1 -0
- package/photon-ai/provider/http-ai-provider.js +125 -0
- package/photon-ai/provider/http-ai-provider.js.map +1 -0
- package/photon-ai/provider/index.d.ts +30 -0
- package/photon-ai/provider/index.d.ts.map +1 -0
- package/photon-ai/provider/index.js +42 -0
- package/photon-ai/provider/index.js.map +1 -0
- package/photon-ai/provider/presets.d.ts +29 -0
- package/photon-ai/provider/presets.d.ts.map +1 -0
- package/photon-ai/provider/presets.js +110 -0
- package/photon-ai/provider/presets.js.map +1 -0
- package/photon-ai/provider/response-parsing.d.ts +22 -0
- package/photon-ai/provider/response-parsing.d.ts.map +1 -0
- package/photon-ai/provider/response-parsing.js +79 -0
- package/photon-ai/provider/response-parsing.js.map +1 -0
- package/photon-ai/provider/system-prompt.d.ts +26 -0
- package/photon-ai/provider/system-prompt.d.ts.map +1 -0
- package/photon-ai/provider/system-prompt.js +82 -0
- package/photon-ai/provider/system-prompt.js.map +1 -0
- package/photon-grid.min.js +697 -488
- package/renderer/body-renderer.d.ts.map +1 -1
- package/renderer/body-renderer.js +34 -9
- package/renderer/body-renderer.js.map +1 -1
- package/renderer/column-chooser.d.ts +82 -0
- package/renderer/column-chooser.d.ts.map +1 -0
- package/renderer/column-chooser.js +335 -0
- package/renderer/column-chooser.js.map +1 -0
- package/renderer/column-menu.d.ts.map +1 -1
- package/renderer/column-menu.js +32 -20
- package/renderer/column-menu.js.map +1 -1
- package/renderer/column-style-manager.d.ts +17 -0
- package/renderer/column-style-manager.d.ts.map +1 -1
- package/renderer/column-style-manager.js +28 -0
- package/renderer/column-style-manager.js.map +1 -1
- package/renderer/detail-row-renderer.d.ts.map +1 -1
- package/renderer/detail-row-renderer.js +9 -7
- package/renderer/detail-row-renderer.js.map +1 -1
- package/renderer/grid-renderer.d.ts +23 -1
- package/renderer/grid-renderer.d.ts.map +1 -1
- package/renderer/grid-renderer.js +66 -8
- package/renderer/grid-renderer.js.map +1 -1
- package/renderer/group-drop-zone.js +14 -14
- package/renderer/group-drop-zone.js.map +1 -1
- package/renderer/header-renderer.d.ts +70 -0
- package/renderer/header-renderer.d.ts.map +1 -1
- package/renderer/header-renderer.js +361 -52
- package/renderer/header-renderer.js.map +1 -1
- package/renderer/row-animator.d.ts +18 -1
- package/renderer/row-animator.d.ts.map +1 -1
- package/renderer/row-animator.js +96 -20
- package/renderer/row-animator.js.map +1 -1
- package/renderer/row-drag-renderer.js +6 -6
- package/renderer/row-drag-renderer.js.map +1 -1
- package/renderer/scroll-controller.d.ts +40 -0
- package/renderer/scroll-controller.d.ts.map +1 -1
- package/renderer/scroll-controller.js +215 -13
- package/renderer/scroll-controller.js.map +1 -1
- package/styles/base/cells.css.d.ts +1 -1
- package/styles/base/cells.css.d.ts.map +1 -1
- package/styles/base/cells.css.js +13 -13
- package/styles/base/column-chooser.css.d.ts +13 -0
- package/styles/base/column-chooser.css.d.ts.map +1 -0
- package/styles/base/column-chooser.css.js +241 -0
- package/styles/base/column-chooser.css.js.map +1 -0
- package/styles/base/column-context-menu.css.d.ts +1 -1
- package/styles/base/column-context-menu.css.d.ts.map +1 -1
- package/styles/base/column-context-menu.css.js +29 -5
- package/styles/base/column-context-menu.css.js.map +1 -1
- package/styles/base/column-group-header.css.d.ts +1 -1
- package/styles/base/column-group-header.css.d.ts.map +1 -1
- package/styles/base/column-group-header.css.js +1 -1
- package/styles/base/context-menu.css.d.ts +1 -1
- package/styles/base/context-menu.css.d.ts.map +1 -1
- package/styles/base/context-menu.css.js +8 -0
- package/styles/base/context-menu.css.js.map +1 -1
- package/styles/base/filter.css.d.ts +1 -1
- package/styles/base/filter.css.d.ts.map +1 -1
- package/styles/base/filter.css.js +7 -7
- package/styles/base/group-drop-zone.css.d.ts +1 -1
- package/styles/base/group-drop-zone.css.d.ts.map +1 -1
- package/styles/base/group-drop-zone.css.js +3 -3
- package/styles/base/header.css.d.ts +1 -1
- package/styles/base/header.css.d.ts.map +1 -1
- package/styles/base/header.css.js +23 -0
- package/styles/base/header.css.js.map +1 -1
- package/styles/base/photon-ai.css.d.ts +1 -1
- package/styles/base/photon-ai.css.d.ts.map +1 -1
- package/styles/base/photon-ai.css.js +65 -0
- package/styles/base/photon-ai.css.js.map +1 -1
- package/styles/base/row-drag.css.d.ts +1 -1
- package/styles/base/row-drag.css.d.ts.map +1 -1
- package/styles/base/row-drag.css.js +1 -1
- package/styles/base/rows.css.d.ts +1 -1
- package/styles/base/rows.css.d.ts.map +1 -1
- package/styles/base/rows.css.js +4 -16
- package/styles/base/rows.css.js.map +1 -1
- package/styles/base/scrollbars.css.d.ts +1 -1
- package/styles/base/scrollbars.css.d.ts.map +1 -1
- package/styles/base/scrollbars.css.js +56 -7
- package/styles/base/scrollbars.css.js.map +1 -1
- package/styles/base/sparkline.css.d.ts +1 -1
- package/styles/base/sparkline.css.d.ts.map +1 -1
- package/styles/base/sparkline.css.js +6 -0
- package/styles/base/sparkline.css.js.map +1 -1
- package/styles/base/tooltip.css.d.ts +1 -1
- package/styles/base/tooltip.css.d.ts.map +1 -1
- package/styles/base/tooltip.css.js +1 -1
- package/styles/base/touch.css.d.ts +23 -0
- package/styles/base/touch.css.d.ts.map +1 -0
- package/styles/base/touch.css.js +85 -0
- package/styles/base/touch.css.js.map +1 -0
- package/styles/base-styles.d.ts.map +1 -1
- package/styles/base-styles.js +9 -2
- package/styles/base-styles.js.map +1 -1
- package/styles/themes/theme-alpine.d.ts +9 -1
- package/styles/themes/theme-alpine.d.ts.map +1 -1
- package/styles/themes/theme-alpine.js +34 -43
- package/styles/themes/theme-alpine.js.map +1 -1
- package/styles/themes/theme-balham.d.ts +9 -1
- package/styles/themes/theme-balham.d.ts.map +1 -1
- package/styles/themes/theme-balham.js +33 -42
- package/styles/themes/theme-balham.js.map +1 -1
- package/styles/themes/theme-material.d.ts +9 -1
- package/styles/themes/theme-material.d.ts.map +1 -1
- package/styles/themes/theme-material.js +35 -43
- package/styles/themes/theme-material.js.map +1 -1
- package/styles/themes/theme-quartz.d.ts +14 -1
- package/styles/themes/theme-quartz.d.ts.map +1 -1
- package/styles/themes/theme-quartz.js +63 -43
- package/styles/themes/theme-quartz.js.map +1 -1
- package/theme/theme-manager.d.ts +55 -7
- package/theme/theme-manager.d.ts.map +1 -1
- package/theme/theme-manager.js +139 -35
- package/theme/theme-manager.js.map +1 -1
- package/types/column.types.d.ts +78 -0
- package/types/column.types.d.ts.map +1 -1
- package/types/column.types.js +26 -1
- package/types/column.types.js.map +1 -1
- package/types/grid.types.d.ts +92 -3
- package/types/grid.types.d.ts.map +1 -1
- package/types/photon-ai.types.d.ts +125 -5
- package/types/photon-ai.types.d.ts.map +1 -1
- package/types/photon-ai.types.js +18 -1
- package/types/photon-ai.types.js.map +1 -1
- package/types/theme.types.d.ts +19 -0
- package/types/theme.types.d.ts.map +1 -1
- package/types/theme.types.js +7 -1
- package/types/theme.types.js.map +1 -1
- package/styles/themes/theme-dark.d.ts +0 -2
- package/styles/themes/theme-dark.d.ts.map +0 -1
- package/styles/themes/theme-dark.js +0 -216
- package/styles/themes/theme-dark.js.map +0 -1
|
@@ -1,11 +1,19 @@
|
|
|
1
|
+
import { HeaderIconDisplay } from '../types/column.types';
|
|
1
2
|
import { GridEventType } from '../types/event.types';
|
|
2
3
|
import { ColumnMenu } from './column-menu';
|
|
3
4
|
import { GroupContextMenu } from './group-context-menu';
|
|
4
5
|
import { createDiv, toggleClass } from './dom-utils';
|
|
5
6
|
import { resolveColumnRenderer } from './renderer-resolver';
|
|
7
|
+
import { isTouchPointer, DRAG_THRESHOLD_MOUSE, DRAG_THRESHOLD_TOUCH, LONG_PRESS_MS, } from '../core/pointer-utils';
|
|
6
8
|
export class HeaderRenderer {
|
|
7
9
|
/** Read by grid-renderer's columns-store watcher to skip header destroy during drag */
|
|
8
10
|
get isDraggingCol() { return this.isDragging; }
|
|
11
|
+
/**
|
|
12
|
+
* `true` while the header owns the pointer for a column reorder or resize.
|
|
13
|
+
* The ScrollController's touch-pan yields to this (see `setGestureGuard`) so a
|
|
14
|
+
* long-press reorder or an edge resize is never fought by kinetic scrolling.
|
|
15
|
+
*/
|
|
16
|
+
get isBusy() { return this.isDragging || this._isResizingColumn; }
|
|
9
17
|
/**
|
|
10
18
|
* `true` between the resize-handle `mousedown` and the corresponding `mouseup`.
|
|
11
19
|
* Grid-renderer uses this to suppress body `renderRows` during resize — column
|
|
@@ -91,8 +99,15 @@ export class HeaderRenderer {
|
|
|
91
99
|
this.lastDragClientX = 0;
|
|
92
100
|
/** Callback wired by GridRenderer to open a filter panel for a column. */
|
|
93
101
|
this.openFilterPanelFn = null;
|
|
102
|
+
/** Callback wired by GridRenderer to open the Column Chooser dialog. */
|
|
103
|
+
this.openColumnChooserFn = null;
|
|
104
|
+
/** Callback wired by GridRenderer to re-run the data pipeline (e.g. after an aggregate change). */
|
|
105
|
+
this.columnDataRefreshFn = null;
|
|
94
106
|
/** True while the user holds the resize handle and is actively moving the mouse. */
|
|
95
107
|
this._isResizingColumn = false;
|
|
108
|
+
/** Column id and timestamp of the last resize-handle press, for double-click (auto-size) detection. */
|
|
109
|
+
this.lastResizeDownColId = '';
|
|
110
|
+
this.lastResizeDownAt = 0;
|
|
96
111
|
// ─── Auto-scroll ──────────────────────────────────────────────────────────
|
|
97
112
|
this.tickAutoScroll = () => {
|
|
98
113
|
if (!this.isDragging || this.autoScrollSpeed === 0)
|
|
@@ -114,11 +129,11 @@ export class HeaderRenderer {
|
|
|
114
129
|
};
|
|
115
130
|
this.columnMenu = new ColumnMenu(columnModel, sortEngine, eventBus, iconRenderer, (action, colId) => this.onAction(action, colId));
|
|
116
131
|
this.columnMenu.setMenuCallbacks({
|
|
117
|
-
onAutoSize: (colId) => this.onAction('autosize', colId),
|
|
118
|
-
onAutoSizeAll: () => this.onAction('autosize-all', ''),
|
|
119
|
-
onFitToGrid: () => this.onAction('fit-to-grid', ''),
|
|
120
|
-
onResetWidth: (colId) => this.onAction('reset-width', colId),
|
|
121
|
-
onOpenColumnChooser: () => this.onAction('column-chooser', ''),
|
|
132
|
+
onAutoSize: (colId) => { this.autoSizeColumns([colId]); this.onAction('autosize', colId); },
|
|
133
|
+
onAutoSizeAll: () => { this.autoSizeColumns(this.columnModel.getVisibleColumns().map((c) => c.colId)); this.onAction('autosize-all', ''); },
|
|
134
|
+
onFitToGrid: () => { this.fitColumnsToGrid(); this.onAction('fit-to-grid', ''); },
|
|
135
|
+
onResetWidth: (colId) => { this.resetColumnWidth(colId); this.onAction('reset-width', colId); },
|
|
136
|
+
onOpenColumnChooser: () => { this.openColumnChooserFn?.(); this.onAction('column-chooser', ''); },
|
|
122
137
|
onOpenAdvancedFilter: (colDef, anchorEl) => {
|
|
123
138
|
this.openFilterPanelFn?.(colDef, anchorEl);
|
|
124
139
|
this.onAction('advanced-filter', colDef.colId);
|
|
@@ -130,12 +145,26 @@ export class HeaderRenderer {
|
|
|
130
145
|
},
|
|
131
146
|
onCopyColumn: (colDef) => this.onAction('copy-column', colDef.colId),
|
|
132
147
|
onCopyValues: (colDef) => this.onAction('copy-values', colDef.colId),
|
|
133
|
-
onRename: (colDef) => this.onAction('rename', colDef.colId),
|
|
134
|
-
onDuplicate: (colDef) => this.onAction('duplicate', colDef.colId),
|
|
135
|
-
onFreezePosition: (colDef) => this.onAction('freeze', colDef.colId),
|
|
136
|
-
onLockColumn: (colDef) => this.onAction('lock', colDef.colId),
|
|
137
|
-
onResetColumn: (colDef) => this.onAction('reset-column', colDef.colId),
|
|
138
|
-
onAggregate: (colDef, func) =>
|
|
148
|
+
onRename: (colDef) => { this.startHeaderRename(colDef.colId); this.onAction('rename', colDef.colId); },
|
|
149
|
+
onDuplicate: (colDef) => { this.columnModel.duplicateColumn(colDef.colId); this.onAction('duplicate', colDef.colId); },
|
|
150
|
+
onFreezePosition: (colDef) => { this.columnModel.toggleColumnFrozen(colDef.colId); this.onAction('freeze', colDef.colId); },
|
|
151
|
+
onLockColumn: (colDef) => { this.columnModel.toggleColumnLocked(colDef.colId); this.onAction('lock', colDef.colId); },
|
|
152
|
+
onResetColumn: (colDef) => { this.colStyles.clearUserWidth(colDef.colId); this.columnModel.resetColumn(colDef.colId); this.onAction('reset-column', colDef.colId); },
|
|
153
|
+
onAggregate: (colDef, func) => {
|
|
154
|
+
// Toggle: re-selecting the current function clears it.
|
|
155
|
+
const f = func;
|
|
156
|
+
const current = this.columnModel.getColumn(colDef.colId)?.aggFunc;
|
|
157
|
+
this.columnModel.setColumnAggFunc(colDef.colId, current === f ? null : f);
|
|
158
|
+
// aggFunc only affects the *grouped* aggregation display, so a data
|
|
159
|
+
// pipeline refresh is only needed when grouping is active. When it isn't,
|
|
160
|
+
// the change has no visible effect yet (it applies the next time rows are
|
|
161
|
+
// grouped) — so we skip refresh() to avoid rebuilding unrelated live
|
|
162
|
+
// views such as linked range charts, which watch `visibleRows`.
|
|
163
|
+
if (this.store.get('groupedColumnIds').length > 0) {
|
|
164
|
+
this.columnDataRefreshFn?.();
|
|
165
|
+
}
|
|
166
|
+
this.onAction(`aggregate-${func}`, colDef.colId);
|
|
167
|
+
},
|
|
139
168
|
onMoveLeft: (colId) => {
|
|
140
169
|
const cols = columnModel.getVisibleColumns();
|
|
141
170
|
const idx = cols.findIndex((c) => c.colId === colId);
|
|
@@ -171,6 +200,14 @@ export class HeaderRenderer {
|
|
|
171
200
|
});
|
|
172
201
|
this.boundMouseMove = this.onGlobalMouseMove.bind(this);
|
|
173
202
|
this.boundMouseUp = this.onGlobalMouseUp.bind(this);
|
|
203
|
+
// Keep the sort arrows in sync in place. Sorting no longer rebuilds the
|
|
204
|
+
// header (that teardown fought the row animation), so the indicator is
|
|
205
|
+
// updated directly here — covering both header clicks and programmatic
|
|
206
|
+
// sorts. `colId === ''` (clear-all) clears every arrow.
|
|
207
|
+
this.eventBus.on(GridEventType.COLUMN_SORTED, (payload) => {
|
|
208
|
+
const p = payload;
|
|
209
|
+
this.updateSortIndicator(p.colId, p.order);
|
|
210
|
+
});
|
|
174
211
|
}
|
|
175
212
|
setScrollCallback(fn, canScrollX) {
|
|
176
213
|
this.scrollByX = fn;
|
|
@@ -188,6 +225,25 @@ export class HeaderRenderer {
|
|
|
188
225
|
setOpenFilterPanelCallback(fn) {
|
|
189
226
|
this.openFilterPanelFn = fn;
|
|
190
227
|
}
|
|
228
|
+
/**
|
|
229
|
+
* Registers the callback that opens the Column Chooser dialog, invoked from
|
|
230
|
+
* the column menu's and group menu's "Column Chooser…" items.
|
|
231
|
+
*
|
|
232
|
+
* @param fn - Opens the chooser (owned by `GridRenderer`).
|
|
233
|
+
*/
|
|
234
|
+
setColumnChooserCallback(fn) {
|
|
235
|
+
this.openColumnChooserFn = fn;
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Registers a callback that re-runs the grid's data pipeline (filter → sort →
|
|
239
|
+
* group → aggregate). Invoked after a column aggregate function changes so
|
|
240
|
+
* group aggregations recompute immediately when grouping is active.
|
|
241
|
+
*
|
|
242
|
+
* @param fn - Runs the pipeline + render (typically `GridApi.refresh`).
|
|
243
|
+
*/
|
|
244
|
+
setColumnDataRefreshCallback(fn) {
|
|
245
|
+
this.columnDataRefreshFn = fn;
|
|
246
|
+
}
|
|
191
247
|
/**
|
|
192
248
|
* Wire the column-group model and DOM builder into the header renderer.
|
|
193
249
|
*
|
|
@@ -214,7 +270,7 @@ export class HeaderRenderer {
|
|
|
214
270
|
this.displayGroupEngine = engine;
|
|
215
271
|
this.groupContextMenu = new GroupContextMenu(engine, this.iconRenderer);
|
|
216
272
|
this.groupContextMenu.setCallbacks({
|
|
217
|
-
onOpenColumnChooser: () => this.onAction('column-chooser', ''),
|
|
273
|
+
onOpenColumnChooser: () => { this.openColumnChooserFn?.(); this.onAction('column-chooser', ''); },
|
|
218
274
|
onAction: (action, groupId) => this.onAction(action, groupId),
|
|
219
275
|
});
|
|
220
276
|
}
|
|
@@ -397,13 +453,19 @@ export class HeaderRenderer {
|
|
|
397
453
|
for (const row of rows) {
|
|
398
454
|
for (const cell of Array.from(row.querySelectorAll('.pg-th[data-col-id]'))) {
|
|
399
455
|
const isTarget = cell.getAttribute('data-col-id') === colId;
|
|
456
|
+
const wasSorted = cell.classList.contains('pg-th--sorted');
|
|
400
457
|
toggleClass(cell, 'pg-th--sort-asc', isTarget && order === 'asc');
|
|
401
458
|
toggleClass(cell, 'pg-th--sort-desc', isTarget && order === 'desc');
|
|
402
459
|
toggleClass(cell, 'pg-th--sorted', isTarget && order !== null);
|
|
460
|
+
const iconEl = cell.querySelector('.pg-th__sort-icon');
|
|
461
|
+
if (!iconEl)
|
|
462
|
+
continue;
|
|
403
463
|
if (isTarget) {
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
464
|
+
this.iconRenderer.updateIcon(iconEl, order === 'asc' ? 'sortAsc' : order === 'desc' ? 'sortDesc' : 'sortNone');
|
|
465
|
+
}
|
|
466
|
+
else if (wasSorted) {
|
|
467
|
+
// A different column just became the sort target — clear this one's arrow.
|
|
468
|
+
this.iconRenderer.updateIcon(iconEl, 'sortNone');
|
|
407
469
|
}
|
|
408
470
|
}
|
|
409
471
|
}
|
|
@@ -423,8 +485,8 @@ export class HeaderRenderer {
|
|
|
423
485
|
});
|
|
424
486
|
}
|
|
425
487
|
destroy() {
|
|
426
|
-
document.removeEventListener('
|
|
427
|
-
document.removeEventListener('
|
|
488
|
+
document.removeEventListener('pointermove', this.boundMouseMove);
|
|
489
|
+
document.removeEventListener('pointerup', this.boundMouseUp);
|
|
428
490
|
this.columnMenu.destroy();
|
|
429
491
|
this.groupContextMenu?.destroy();
|
|
430
492
|
this.leftHeaderRowEl = null;
|
|
@@ -625,12 +687,17 @@ export class HeaderRenderer {
|
|
|
625
687
|
}
|
|
626
688
|
th.appendChild(content);
|
|
627
689
|
}
|
|
628
|
-
// Filter icon —
|
|
629
|
-
|
|
690
|
+
// Filter icon — reveal-on-hover by default; always visible when a filter is
|
|
691
|
+
// active, or when the column/grid opts into `HeaderIconDisplay.ALWAYS`.
|
|
692
|
+
// Skipped entirely when the mode is `HeaderIconDisplay.HIDDEN`.
|
|
693
|
+
const filterMode = col.filterIconDisplay ?? options.filterIconDisplay ?? HeaderIconDisplay.HOVER;
|
|
694
|
+
if (col.filterable !== false && filterMode !== HeaderIconDisplay.HIDDEN) {
|
|
630
695
|
const filterBtn = createDiv('pg-th__filter-btn');
|
|
631
696
|
const filterActive = col.filterActive === true;
|
|
632
697
|
if (filterActive)
|
|
633
698
|
filterBtn.classList.add('pg-th__filter-btn--active');
|
|
699
|
+
if (filterMode === HeaderIconDisplay.ALWAYS)
|
|
700
|
+
filterBtn.classList.add('pg-th__filter-btn--always');
|
|
634
701
|
filterBtn.innerHTML = this.iconRenderer.renderToString(filterActive ? 'filterActive' : 'filter', 14);
|
|
635
702
|
filterBtn.title = 'Filter column';
|
|
636
703
|
filterBtn.setAttribute('tabindex', '0');
|
|
@@ -641,8 +708,13 @@ export class HeaderRenderer {
|
|
|
641
708
|
});
|
|
642
709
|
th.appendChild(filterBtn);
|
|
643
710
|
}
|
|
644
|
-
|
|
711
|
+
// Column-menu "⋯" icon — hidden entirely when the mode is
|
|
712
|
+
// `HeaderIconDisplay.HIDDEN` (the header right-click menu still works).
|
|
713
|
+
const menuMode = col.menuIconDisplay ?? options.menuIconDisplay ?? HeaderIconDisplay.HOVER;
|
|
714
|
+
if (options.showColumnMenu !== false && menuMode !== HeaderIconDisplay.HIDDEN) {
|
|
645
715
|
const menuBtn = createDiv('pg-th__menu-btn');
|
|
716
|
+
if (menuMode === HeaderIconDisplay.ALWAYS)
|
|
717
|
+
menuBtn.classList.add('pg-th__menu-btn--always');
|
|
646
718
|
menuBtn.innerHTML = this.iconRenderer.renderToString('menuHorizontal', 14);
|
|
647
719
|
menuBtn.title = 'Column options';
|
|
648
720
|
menuBtn.setAttribute('tabindex', '0');
|
|
@@ -658,7 +730,27 @@ export class HeaderRenderer {
|
|
|
658
730
|
if (col.resizable !== false) {
|
|
659
731
|
const resizeHandle = createDiv('pg-th__resize-handle');
|
|
660
732
|
// resizeHandle.innerHTML = '|';
|
|
661
|
-
resizeHandle.addEventListener('
|
|
733
|
+
resizeHandle.addEventListener('pointerdown', (e) => {
|
|
734
|
+
if (e.button !== 0)
|
|
735
|
+
return;
|
|
736
|
+
e.preventDefault();
|
|
737
|
+
e.stopPropagation();
|
|
738
|
+
// Double-press on the same handle → auto-size the column (AG Grid
|
|
739
|
+
// convention). Detected by timing rather than the native `dblclick`
|
|
740
|
+
// event: the first press's mouseup can rebuild the header
|
|
741
|
+
// (COLUMNS_STATE_CHANGED) and replace this element, so a native dblclick
|
|
742
|
+
// would never fire on the original node. Keying on colId survives that.
|
|
743
|
+
if (this.lastResizeDownColId === col.colId && e.timeStamp - this.lastResizeDownAt < 350) {
|
|
744
|
+
this.lastResizeDownColId = '';
|
|
745
|
+
this.lastResizeDownAt = 0;
|
|
746
|
+
this.autoSizeColumns([col.colId]);
|
|
747
|
+
this.onAction('autosize', col.colId);
|
|
748
|
+
return;
|
|
749
|
+
}
|
|
750
|
+
this.lastResizeDownColId = col.colId;
|
|
751
|
+
this.lastResizeDownAt = e.timeStamp;
|
|
752
|
+
this.startResize(e, col, th);
|
|
753
|
+
});
|
|
662
754
|
th.appendChild(resizeHandle);
|
|
663
755
|
}
|
|
664
756
|
if (col.sortable !== false) {
|
|
@@ -710,30 +802,71 @@ export class HeaderRenderer {
|
|
|
710
802
|
return row;
|
|
711
803
|
}
|
|
712
804
|
attachColumnDragListeners(th, col, _panelColumns, panelRowEl) {
|
|
713
|
-
th.addEventListener('
|
|
805
|
+
th.addEventListener('pointerdown', (e) => {
|
|
714
806
|
if (e.target.closest('.pg-th__resize-handle, .pg-th__menu-btn, .pg-th__filter-btn, .pg-checkbox'))
|
|
715
807
|
return;
|
|
716
808
|
if (col.draggable === false || e.button !== 0)
|
|
717
809
|
return;
|
|
718
810
|
const startX = e.clientX;
|
|
719
|
-
|
|
720
|
-
const
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
811
|
+
const startY = e.clientY;
|
|
812
|
+
const touch = isTouchPointer(e);
|
|
813
|
+
let started = false; // actual column drag engaged
|
|
814
|
+
let longPressTimer = 0;
|
|
815
|
+
// Tear down the pre-drag probe listeners + long-press timer + affordance.
|
|
816
|
+
const cleanupProbe = () => {
|
|
817
|
+
if (longPressTimer) {
|
|
818
|
+
clearTimeout(longPressTimer);
|
|
819
|
+
longPressTimer = 0;
|
|
820
|
+
}
|
|
821
|
+
th.classList.remove('pg-th--drag-armed');
|
|
822
|
+
document.removeEventListener('pointermove', onMoveCheck);
|
|
823
|
+
document.removeEventListener('pointerup', onUpEarly);
|
|
824
|
+
};
|
|
825
|
+
// Promote the press to a live column drag and hand movement over to the
|
|
826
|
+
// global drag handlers. Called by the mouse threshold or the touch
|
|
827
|
+
// long-press timer.
|
|
828
|
+
const beginDrag = () => {
|
|
829
|
+
if (started)
|
|
830
|
+
return;
|
|
831
|
+
started = true;
|
|
832
|
+
if (longPressTimer) {
|
|
833
|
+
clearTimeout(longPressTimer);
|
|
834
|
+
longPressTimer = 0;
|
|
727
835
|
}
|
|
728
|
-
|
|
729
|
-
|
|
836
|
+
th.classList.remove('pg-th--drag-armed');
|
|
837
|
+
this.startColumnDrag(col, panelRowEl);
|
|
838
|
+
document.removeEventListener('pointermove', onMoveCheck);
|
|
839
|
+
document.addEventListener('pointermove', this.boundMouseMove);
|
|
840
|
+
document.addEventListener('pointerup', this.boundMouseUp);
|
|
841
|
+
this.onDragMove(startX);
|
|
730
842
|
};
|
|
731
|
-
const
|
|
732
|
-
|
|
733
|
-
|
|
843
|
+
const onMoveCheck = (ev) => {
|
|
844
|
+
if (started)
|
|
845
|
+
return; // global handlers own movement now
|
|
846
|
+
const dx = Math.abs(ev.clientX - startX);
|
|
847
|
+
const dy = Math.abs(ev.clientY - startY);
|
|
848
|
+
if (touch) {
|
|
849
|
+
// Movement before the long-press fires means the user is swiping to
|
|
850
|
+
// scroll, not reordering — abandon so the ScrollController pans.
|
|
851
|
+
if (dx > DRAG_THRESHOLD_TOUCH || dy > DRAG_THRESHOLD_TOUCH)
|
|
852
|
+
cleanupProbe();
|
|
853
|
+
}
|
|
854
|
+
else if (dx > DRAG_THRESHOLD_MOUSE) {
|
|
855
|
+
beginDrag();
|
|
856
|
+
}
|
|
734
857
|
};
|
|
735
|
-
|
|
736
|
-
|
|
858
|
+
const onUpEarly = () => { if (!started)
|
|
859
|
+
cleanupProbe(); };
|
|
860
|
+
document.addEventListener('pointermove', onMoveCheck);
|
|
861
|
+
document.addEventListener('pointerup', onUpEarly);
|
|
862
|
+
if (touch) {
|
|
863
|
+
// Arm a long-press: hold ~400ms without swiping to pick the column up.
|
|
864
|
+
th.classList.add('pg-th--drag-armed');
|
|
865
|
+
longPressTimer = window.setTimeout(() => {
|
|
866
|
+
longPressTimer = 0;
|
|
867
|
+
beginDrag();
|
|
868
|
+
}, LONG_PRESS_MS);
|
|
869
|
+
}
|
|
737
870
|
});
|
|
738
871
|
}
|
|
739
872
|
startColumnDrag(col, panelRowEl) {
|
|
@@ -1279,17 +1412,12 @@ export class HeaderRenderer {
|
|
|
1279
1412
|
this.draggingPanelRowEl = null;
|
|
1280
1413
|
document.body.style.cursor = '';
|
|
1281
1414
|
document.body.style.userSelect = '';
|
|
1282
|
-
document.removeEventListener('
|
|
1283
|
-
document.removeEventListener('
|
|
1415
|
+
document.removeEventListener('pointermove', this.boundMouseMove);
|
|
1416
|
+
document.removeEventListener('pointerup', this.boundMouseUp);
|
|
1284
1417
|
}
|
|
1285
1418
|
startResize(e, col, thEl) {
|
|
1286
1419
|
const startX = e.clientX;
|
|
1287
1420
|
const startWidth = this.colStyles.getWidth(col.colId);
|
|
1288
|
-
// Lock every flex column to its current pixel width up front, so dragging
|
|
1289
|
-
// this one resizes only it — the others keep their widths (and the total
|
|
1290
|
-
// may overflow into a horizontal scrollbar) instead of flex redistributing
|
|
1291
|
-
// the container and collapsing them to minWidth.
|
|
1292
|
-
this.colStyles.freezeFlexWidths();
|
|
1293
1421
|
// Right-pinned columns anchor their right edge to the grid border, so their
|
|
1294
1422
|
// resize handle sits on the *left* (inner) edge — the only edge free to move.
|
|
1295
1423
|
// Dragging that edge left must therefore widen the column, so the pointer
|
|
@@ -1297,11 +1425,27 @@ export class HeaderRenderer {
|
|
|
1297
1425
|
// their right-edge handle: dragging right widens.
|
|
1298
1426
|
const dir = col.pinned === 'right' ? -1 : 1;
|
|
1299
1427
|
const minWidth = col.minWidth ?? 60;
|
|
1300
|
-
thEl.classList.add('pg-th--resizing');
|
|
1301
1428
|
document.body.style.cursor = 'col-resize';
|
|
1302
1429
|
document.body.style.userSelect = 'none';
|
|
1303
1430
|
this._isResizingColumn = true;
|
|
1431
|
+
// Only treat this as a resize once the pointer actually moves past a small
|
|
1432
|
+
// threshold. A bare click (e.g. the first press of a double-click) must not
|
|
1433
|
+
// resize, rebuild the header, or freeze flex columns — otherwise the handle
|
|
1434
|
+
// element is replaced mid-gesture and the double-click auto-size is lost.
|
|
1435
|
+
let moved = false;
|
|
1304
1436
|
const onMove = (ev) => {
|
|
1437
|
+
if (!moved) {
|
|
1438
|
+
if (Math.abs(ev.clientX - startX) <= 2)
|
|
1439
|
+
return;
|
|
1440
|
+
moved = true;
|
|
1441
|
+
thEl.classList.add('pg-th--resizing');
|
|
1442
|
+
// Lock every flex column to its current pixel width so dragging this one
|
|
1443
|
+
// resizes only it — the others keep their widths (the total may overflow
|
|
1444
|
+
// into a horizontal scrollbar) instead of flex redistributing and
|
|
1445
|
+
// collapsing them to minWidth. Done here (not on mousedown) so a plain
|
|
1446
|
+
// click never silently converts flex columns to fixed.
|
|
1447
|
+
this.colStyles.freezeFlexWidths();
|
|
1448
|
+
}
|
|
1305
1449
|
this.colStyles.setWidth(col.colId, Math.max(minWidth, startWidth + (ev.clientX - startX) * dir));
|
|
1306
1450
|
this.onResizeCb?.();
|
|
1307
1451
|
};
|
|
@@ -1309,17 +1453,19 @@ export class HeaderRenderer {
|
|
|
1309
1453
|
// Clear the flag BEFORE setColumnWidth fires an event so the final
|
|
1310
1454
|
// post-resize render calls renderRows normally and the body is correct.
|
|
1311
1455
|
this._isResizingColumn = false;
|
|
1312
|
-
const newWidth = Math.max(minWidth, startWidth + (ev.clientX - startX) * dir);
|
|
1313
|
-
this.colStyles.setWidth(col.colId, newWidth);
|
|
1314
|
-
this.columnModel.setColumnWidth(col.colId, newWidth, true);
|
|
1315
1456
|
thEl.classList.remove('pg-th--resizing');
|
|
1316
1457
|
document.body.style.cursor = '';
|
|
1317
1458
|
document.body.style.userSelect = '';
|
|
1318
|
-
document.removeEventListener('
|
|
1319
|
-
document.removeEventListener('
|
|
1459
|
+
document.removeEventListener('pointermove', onMove);
|
|
1460
|
+
document.removeEventListener('pointerup', onUp);
|
|
1461
|
+
if (!moved)
|
|
1462
|
+
return; // no drag occurred — leave widths untouched
|
|
1463
|
+
const newWidth = Math.max(minWidth, startWidth + (ev.clientX - startX) * dir);
|
|
1464
|
+
this.colStyles.setWidth(col.colId, newWidth);
|
|
1465
|
+
this.columnModel.setColumnWidth(col.colId, newWidth, true);
|
|
1320
1466
|
};
|
|
1321
|
-
document.addEventListener('
|
|
1322
|
-
document.addEventListener('
|
|
1467
|
+
document.addEventListener('pointermove', onMove);
|
|
1468
|
+
document.addEventListener('pointerup', onUp);
|
|
1323
1469
|
}
|
|
1324
1470
|
/**
|
|
1325
1471
|
* Returns `true` when `colId` (at its new position `newIdx` in the visible
|
|
@@ -1343,6 +1489,169 @@ export class HeaderRenderer {
|
|
|
1343
1489
|
const right = cols[newIdx + 1];
|
|
1344
1490
|
return !(left && siblings.has(left.colId)) && !(right && siblings.has(right.colId));
|
|
1345
1491
|
}
|
|
1492
|
+
// ──────────────────── Column menu: resize operations ────────────────────
|
|
1493
|
+
/** Resolves the owning `.pg-grid` element from any live header row, or `null`. */
|
|
1494
|
+
getGridEl() {
|
|
1495
|
+
return (this.centerHeaderRowEl ?? this.leftHeaderRowEl ?? this.rightHeaderRowEl)
|
|
1496
|
+
?.closest('.pg-grid') ?? null;
|
|
1497
|
+
}
|
|
1498
|
+
/**
|
|
1499
|
+
* Starts an inline rename of a column: replaces the header label with a text
|
|
1500
|
+
* input, committing on Enter/blur and cancelling on Escape. Committing calls
|
|
1501
|
+
* {@link ColumnModel.setColumnHeader}, which rebuilds the header with the new
|
|
1502
|
+
* text. A no-op when the header cell can't be found.
|
|
1503
|
+
*/
|
|
1504
|
+
startHeaderRename(colId) {
|
|
1505
|
+
const gridEl = this.getGridEl();
|
|
1506
|
+
const th = gridEl?.querySelector(`.pg-th[data-col-id="${colId}"]`);
|
|
1507
|
+
const label = th?.querySelector('.pg-th__label');
|
|
1508
|
+
const col = this.columnModel.getColumn(colId);
|
|
1509
|
+
if (!th || !label || !col)
|
|
1510
|
+
return;
|
|
1511
|
+
const input = document.createElement('input');
|
|
1512
|
+
input.className = 'pg-th__rename-input';
|
|
1513
|
+
input.type = 'text';
|
|
1514
|
+
input.value = col.header;
|
|
1515
|
+
input.setAttribute('aria-label', 'Rename column');
|
|
1516
|
+
let done = false;
|
|
1517
|
+
const commit = () => {
|
|
1518
|
+
if (done)
|
|
1519
|
+
return;
|
|
1520
|
+
done = true;
|
|
1521
|
+
const value = input.value.trim();
|
|
1522
|
+
if (value && value !== col.header) {
|
|
1523
|
+
this.columnModel.setColumnHeader(colId, value); // triggers header rebuild
|
|
1524
|
+
}
|
|
1525
|
+
else {
|
|
1526
|
+
input.replaceWith(label); // unchanged → restore original label
|
|
1527
|
+
}
|
|
1528
|
+
};
|
|
1529
|
+
const cancel = () => {
|
|
1530
|
+
if (done)
|
|
1531
|
+
return;
|
|
1532
|
+
done = true;
|
|
1533
|
+
input.replaceWith(label);
|
|
1534
|
+
};
|
|
1535
|
+
input.addEventListener('keydown', (e) => {
|
|
1536
|
+
e.stopPropagation();
|
|
1537
|
+
if (e.key === 'Enter') {
|
|
1538
|
+
e.preventDefault();
|
|
1539
|
+
commit();
|
|
1540
|
+
}
|
|
1541
|
+
else if (e.key === 'Escape') {
|
|
1542
|
+
e.preventDefault();
|
|
1543
|
+
cancel();
|
|
1544
|
+
}
|
|
1545
|
+
});
|
|
1546
|
+
input.addEventListener('blur', commit);
|
|
1547
|
+
// Don't let the input's own clicks reach the header's sort/drag handlers.
|
|
1548
|
+
input.addEventListener('mousedown', (e) => e.stopPropagation());
|
|
1549
|
+
input.addEventListener('click', (e) => e.stopPropagation());
|
|
1550
|
+
label.replaceWith(input);
|
|
1551
|
+
input.focus();
|
|
1552
|
+
input.select();
|
|
1553
|
+
}
|
|
1554
|
+
/**
|
|
1555
|
+
* Measures the pixel width needed to show a column's header plus every
|
|
1556
|
+
* currently-rendered cell without truncation, clamped to the column's
|
|
1557
|
+
* min/max. Only virtualized (on-screen) rows are measured, so the cost scales
|
|
1558
|
+
* with the viewport, not the dataset. Uses one reused off-screen `<span>` so
|
|
1559
|
+
* cell fonts/metrics match the grid.
|
|
1560
|
+
*/
|
|
1561
|
+
measureColumnContentWidth(gridEl, col) {
|
|
1562
|
+
const measurer = gridEl.ownerDocument.createElement('span');
|
|
1563
|
+
measurer.style.cssText =
|
|
1564
|
+
'position:absolute;top:-9999px;left:-9999px;visibility:hidden;white-space:nowrap;pointer-events:none;padding:0 12px;';
|
|
1565
|
+
gridEl.appendChild(measurer);
|
|
1566
|
+
measurer.textContent = col.header ?? '';
|
|
1567
|
+
let maxWidth = measurer.offsetWidth + 24; // header text + sort/menu affordance headroom
|
|
1568
|
+
const cells = gridEl.querySelectorAll(`.pg-cell[data-col-id="${col.colId}"]`);
|
|
1569
|
+
for (let i = 0; i < cells.length; i++) {
|
|
1570
|
+
measurer.textContent = cells[i].textContent ?? '';
|
|
1571
|
+
const w = measurer.offsetWidth + 24;
|
|
1572
|
+
if (w > maxWidth)
|
|
1573
|
+
maxWidth = w;
|
|
1574
|
+
}
|
|
1575
|
+
measurer.remove();
|
|
1576
|
+
const min = col.minWidth ?? 40;
|
|
1577
|
+
const max = col.maxWidth ?? Infinity;
|
|
1578
|
+
return Math.round(Math.min(max, Math.max(min, maxWidth)));
|
|
1579
|
+
}
|
|
1580
|
+
/**
|
|
1581
|
+
* Auto-sizes the given columns to their content width. Measures each, then
|
|
1582
|
+
* commits all widths in a single style flush + single render (via the batch
|
|
1583
|
+
* setters), so "Auto Size All" stays cheap. A no-op if the grid DOM or the
|
|
1584
|
+
* columns cannot be resolved.
|
|
1585
|
+
*/
|
|
1586
|
+
autoSizeColumns(colIds) {
|
|
1587
|
+
const gridEl = this.getGridEl();
|
|
1588
|
+
if (!gridEl)
|
|
1589
|
+
return;
|
|
1590
|
+
const entries = [];
|
|
1591
|
+
for (const colId of colIds) {
|
|
1592
|
+
const col = this.columnModel.getColumn(colId);
|
|
1593
|
+
if (!col)
|
|
1594
|
+
continue;
|
|
1595
|
+
entries.push([colId, this.measureColumnContentWidth(gridEl, col)]);
|
|
1596
|
+
}
|
|
1597
|
+
if (entries.length === 0)
|
|
1598
|
+
return;
|
|
1599
|
+
// Style manager first (immediate CSS width + marks fixed so flex can't
|
|
1600
|
+
// override), then the model (persisted state + one COLUMNS_STATE_CHANGED
|
|
1601
|
+
// that drives the re-render and scrollbar/content-width recompute).
|
|
1602
|
+
this.colStyles.setWidths(entries);
|
|
1603
|
+
this.columnModel.setColumnWidths(entries);
|
|
1604
|
+
}
|
|
1605
|
+
/**
|
|
1606
|
+
* Distributes the center viewport width across the visible unpinned columns
|
|
1607
|
+
* so they exactly fill the grid with no horizontal scroll (proportional to
|
|
1608
|
+
* current widths, clamped to each column's min/max). Pinned columns keep
|
|
1609
|
+
* their widths. A no-op when the center viewport can't be measured.
|
|
1610
|
+
*/
|
|
1611
|
+
fitColumnsToGrid() {
|
|
1612
|
+
const gridEl = this.getGridEl();
|
|
1613
|
+
if (!gridEl)
|
|
1614
|
+
return;
|
|
1615
|
+
const centerBody = gridEl.querySelector('.pg-panel--center .pg-panel__body');
|
|
1616
|
+
const available = centerBody?.clientWidth ?? 0;
|
|
1617
|
+
if (available <= 0)
|
|
1618
|
+
return;
|
|
1619
|
+
const centerCols = this.columnModel
|
|
1620
|
+
.getVisibleColumns()
|
|
1621
|
+
.filter((c) => c.pinned !== 'left' && c.pinned !== 'right');
|
|
1622
|
+
if (centerCols.length === 0)
|
|
1623
|
+
return;
|
|
1624
|
+
const totalCurrent = centerCols.reduce((sum, c) => sum + this.colStyles.getWidth(c.colId), 0) || 1;
|
|
1625
|
+
const entries = [];
|
|
1626
|
+
let allocated = 0;
|
|
1627
|
+
for (const c of centerCols) {
|
|
1628
|
+
const min = c.minWidth ?? 40;
|
|
1629
|
+
const max = c.maxWidth ?? Infinity;
|
|
1630
|
+
const w = Math.min(max, Math.max(min, Math.round((this.colStyles.getWidth(c.colId) / totalCurrent) * available)));
|
|
1631
|
+
entries.push([c.colId, w]);
|
|
1632
|
+
allocated += w;
|
|
1633
|
+
}
|
|
1634
|
+
// Absorb rounding/clamping drift into the last column so the total is exact.
|
|
1635
|
+
const diff = available - allocated;
|
|
1636
|
+
if (diff !== 0) {
|
|
1637
|
+
const lastCol = centerCols[centerCols.length - 1];
|
|
1638
|
+
const min = lastCol.minWidth ?? 40;
|
|
1639
|
+
const max = lastCol.maxWidth ?? Infinity;
|
|
1640
|
+
const last = entries[entries.length - 1];
|
|
1641
|
+
entries[entries.length - 1] = [last[0], Math.min(max, Math.max(min, last[1] + diff))];
|
|
1642
|
+
}
|
|
1643
|
+
this.colStyles.setWidths(entries);
|
|
1644
|
+
this.columnModel.setColumnWidths(entries);
|
|
1645
|
+
}
|
|
1646
|
+
/**
|
|
1647
|
+
* Resets a column's width to its original definition. Clears the user-fixed
|
|
1648
|
+
* override in the style manager (so a `flex` column resumes flexing) and
|
|
1649
|
+
* restores the model's width, which re-renders via `COLUMNS_STATE_CHANGED`.
|
|
1650
|
+
*/
|
|
1651
|
+
resetColumnWidth(colId) {
|
|
1652
|
+
this.colStyles.clearUserWidth(colId);
|
|
1653
|
+
this.columnModel.resetColumnWidth(colId);
|
|
1654
|
+
}
|
|
1346
1655
|
onAction(action, colId) {
|
|
1347
1656
|
this.eventBus.emit(GridEventType.COLUMNS_STATE_CHANGED, {
|
|
1348
1657
|
action, colId, states: this.columnModel.getColumnStates(),
|