@deephaven/js-plugin-pivot 0.0.3-dev.984 → 0.2.1-dev.1002

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.
Files changed (2) hide show
  1. package/dist/index.js +454 -471
  2. package/package.json +16 -11
package/dist/index.js CHANGED
@@ -10,8 +10,8 @@ const plugin = require("@deephaven/plugin");
10
10
  const icons = require("@deephaven/icons");
11
11
  const React = require("react");
12
12
  const irisGrid = require("@deephaven/iris-grid");
13
- const jsapiBootstrap = require("@deephaven/jsapi-bootstrap");
14
13
  const components = require("@deephaven/components");
14
+ const jsapiBootstrap = require("@deephaven/jsapi-bootstrap");
15
15
  const Log = require("@deephaven/log");
16
16
  const jsapiUtils = require("@deephaven/jsapi-utils");
17
17
  const dashboardCorePlugins = require("@deephaven/dashboard-core-plugins");
@@ -499,9 +499,6 @@ function isEditableGridModel(model) {
499
499
  function isExpandableGridModel(model) {
500
500
  return (model === null || model === void 0 ? void 0 : model.hasExpandableRows) !== void 0;
501
501
  }
502
- function isExpandableColumnGridModel(model) {
503
- return (model === null || model === void 0 ? void 0 : model.hasExpandableColumns) !== void 0;
504
- }
505
502
  var NAN = 0 / 0;
506
503
  var symbolTag = "[object Symbol]";
507
504
  var reTrim = /^\s+|\s+$/g;
@@ -7311,20 +7308,20 @@ class GridColumnSeparatorMouseHandler extends GridSeparatorMouseHandler$1 {
7311
7308
  var {
7312
7309
  x,
7313
7310
  y,
7314
- columnHeaderDepth
7311
+ columnHeaderDepth: depth
7315
7312
  } = gridPoint;
7316
7313
  var {
7317
7314
  modelColumns
7318
7315
  } = metrics;
7319
7316
  var separatorIndex = GridUtils.getColumnSeparatorIndex(x, y, metrics, theme);
7320
- if (separatorIndex == null || columnHeaderDepth == null || columnHeaderDepth > 0) {
7317
+ if (separatorIndex == null || depth == null) {
7321
7318
  return null;
7322
7319
  }
7323
7320
  var columnIndex = modelColumns.get(separatorIndex);
7324
7321
  if (columnIndex != null) {
7325
7322
  return {
7326
7323
  index: separatorIndex,
7327
- depth: 0
7324
+ depth
7328
7325
  };
7329
7326
  }
7330
7327
  return null;
@@ -9920,10 +9917,10 @@ _defineProperty(GridRenderer, "getCachedColorWithAlpha", memoizeClear$1(GridColo
9920
9917
  _defineProperty(GridRenderer, "getCachedColorIsDark", memoizeClear$1(ColorUtils$1.isDark, {
9921
9918
  max: 1e3
9922
9919
  }));
9923
- function isExpandableColumnHeaderGroup(group) {
9924
- return group instanceof ExpandableColumnHeaderGroup;
9920
+ function isPivotColumnHeaderGroup(group) {
9921
+ return group instanceof PivotColumnHeaderGroup;
9925
9922
  }
9926
- class ExpandableColumnHeaderGroup extends irisGrid.ColumnHeaderGroup {
9923
+ class PivotColumnHeaderGroup extends irisGrid.ColumnHeaderGroup {
9927
9924
  constructor({
9928
9925
  name,
9929
9926
  displayName,
@@ -9978,7 +9975,14 @@ function makeColumn({
9978
9975
  isSortable = false,
9979
9976
  depth = ROOT_DEPTH,
9980
9977
  hasChildren = false,
9981
- isExpanded = false
9978
+ isExpanded = false,
9979
+ isProxy = false,
9980
+ filter = () => {
9981
+ throw new Error("Filter not implemented for virtual column");
9982
+ },
9983
+ sort = () => {
9984
+ throw new Error("Sort not implemented for virtual column");
9985
+ }
9982
9986
  }) {
9983
9987
  return {
9984
9988
  name,
@@ -9986,18 +9990,14 @@ function makeColumn({
9986
9990
  type,
9987
9991
  isPartitionColumn: false,
9988
9992
  isSortable,
9989
- isProxy: false,
9993
+ isProxy,
9990
9994
  description,
9991
9995
  index,
9992
9996
  depth,
9993
9997
  hasChildren,
9994
9998
  isExpanded,
9995
- filter: () => {
9996
- throw new Error("Filter not implemented for virtual column");
9997
- },
9998
- sort: () => {
9999
- throw new Error("Sort not implemented for virtual column");
10000
- },
9999
+ filter,
10000
+ sort,
10001
10001
  formatColor: () => {
10002
10002
  throw new Error("Color not implemented for virtual column");
10003
10003
  },
@@ -10022,15 +10022,15 @@ function makeGrandTotalColumnName(valueSource) {
10022
10022
  return `__GRAND_TOTAL/${valueSource.name}`;
10023
10023
  }
10024
10024
  function makeColumnName(keys2, depth) {
10025
- return keys2.slice(0, depth + 1).filter((k) => k != null).join("/");
10025
+ return keys2.slice(0, depth + 1).filter((k) => k != null).map((k) => encodeURIComponent(String(k))).join("/");
10026
10026
  }
10027
10027
  function makeColumnGroupName(keys2, columnSources, depth) {
10028
- return keys2.slice(0, depth + 1).map((k, i) => k == null ? columnSources[i].name : k).join("/");
10028
+ return keys2.slice(0, depth + 1).map((k, i) => k == null ? columnSources[i].name : k).map((k) => encodeURIComponent(String(k))).join("/");
10029
10029
  }
10030
10030
  function makeValueSourceColumnName(columnName, valueSource) {
10031
10031
  return `${columnName}/${valueSource.name}`;
10032
10032
  }
10033
- function makeExpandableDisplayColumn(snapshotDim, valueSource, originalIndex, offset) {
10033
+ function makeColumnFromSnapshot(snapshotDim, valueSource, originalIndex, offset) {
10034
10034
  const keys2 = snapshotDim.getKeys(originalIndex);
10035
10035
  const depth = snapshotDim.getDepth(originalIndex);
10036
10036
  const hasChildren = snapshotDim.hasChildren(originalIndex);
@@ -10052,7 +10052,7 @@ function makeExpandableDisplayColumn(snapshotDim, valueSource, originalIndex, of
10052
10052
  hasChildren
10053
10053
  });
10054
10054
  }
10055
- function makePlaceholderDisplayColumn(valueSource, originalIndex, offset) {
10055
+ function makePlaceholderColumn(valueSource, originalIndex, offset) {
10056
10056
  return makeColumn({
10057
10057
  name: makePlaceholderColumnName(originalIndex, valueSource),
10058
10058
  displayName: "",
@@ -10063,31 +10063,40 @@ function makePlaceholderDisplayColumn(valueSource, originalIndex, offset) {
10063
10063
  hasChildren: false
10064
10064
  });
10065
10065
  }
10066
- function makeRowSourceColumn(source, index) {
10066
+ function makeColumnFromSource(source, index) {
10067
10067
  const { name, type, isSortable, description } = source;
10068
- return makeColumn({ name, type, index, isSortable, description });
10068
+ return makeColumn({
10069
+ name,
10070
+ type,
10071
+ index,
10072
+ isSortable,
10073
+ description,
10074
+ filter: source.filter.bind(source),
10075
+ sort: source.sort.bind(source)
10076
+ });
10069
10077
  }
10070
10078
  function checkColumnsChanged(prevColumns, newColumns) {
10071
10079
  return prevColumns.length !== newColumns.length || prevColumns.some((col, i) => col.name !== newColumns[i].name);
10072
10080
  }
10073
- function getKeyColumnGroups(columnSources, rowSources) {
10081
+ function makeKeyColumnGroups(columnSources, rowSources, includeGroupColumn) {
10082
+ const groupName = includeGroupColumn ? ["__GROUP__"] : [];
10074
10083
  const groups = columnSources.length === 0 ? [
10075
- new ExpandableColumnHeaderGroup({
10076
- // TODO:
10077
- name: "__All",
10084
+ new PivotColumnHeaderGroup({
10085
+ name: "/",
10078
10086
  displayName: "",
10079
- // TODO: what if rowSources is empty?
10080
- children: rowSources.map((c) => c.name),
10087
+ // For empty row sources we will render a "dead column"
10088
+ // or a Groups column, depending on the table settings
10089
+ children: [...groupName, ...rowSources.map((c) => c.name)],
10081
10090
  childIndexes: [],
10082
10091
  isKeyColumnGroup: true,
10083
10092
  depth: 1,
10084
10093
  isExpandable: false
10085
10094
  })
10086
10095
  ] : columnSources.map(
10087
- (source, i) => new ExpandableColumnHeaderGroup({
10096
+ (source, i) => new PivotColumnHeaderGroup({
10088
10097
  name: source.name,
10089
10098
  displayName: source.name,
10090
- children: i === columnSources.length - 1 ? rowSources.map((c) => c.name) : [columnSources[i + 1].name],
10099
+ children: i === columnSources.length - 1 ? [...groupName, ...rowSources.map((c) => c.name)] : [columnSources[i + 1].name],
10091
10100
  childIndexes: [],
10092
10101
  isKeyColumnGroup: true,
10093
10102
  depth: columnSources.length - i,
@@ -10099,23 +10108,18 @@ function getKeyColumnGroups(columnSources, rowSources) {
10099
10108
  []
10100
10109
  ) : groups;
10101
10110
  }
10102
- function getTotalsColumnGroups(columnSources, valueSources, isRootColumnExpanded) {
10111
+ function makeTotalsColumnGroups(columnSources, valueSources, isRootColumnExpanded) {
10103
10112
  const groupName = pluralize(valueSources.length, GRAND_TOTALS_GROUP_NAME);
10104
10113
  return columnSources.length === 0 ? [
10105
- new ExpandableColumnHeaderGroup({
10106
- // TODO:
10107
- name: "TMP__GrandTotals",
10114
+ new PivotColumnHeaderGroup({
10115
+ name: "/GrandTotals",
10108
10116
  displayName: groupName,
10109
10117
  children: valueSources.map((v) => makeGrandTotalColumnName(v)),
10110
10118
  childIndexes: [],
10111
10119
  depth: 1
10112
- // Only the top level is expandable
10113
- // TODO:
10114
- // isExpandable: i === 0,
10115
- // isExpanded: isRootColumnExpanded,
10116
10120
  })
10117
10121
  ] : columnSources.map(
10118
- (source, i) => new ExpandableColumnHeaderGroup({
10122
+ (source, i) => new PivotColumnHeaderGroup({
10119
10123
  name: makeGrandTotalColumnName(source),
10120
10124
  displayName: i === 0 ? groupName : "",
10121
10125
  children: i === columnSources.length - 1 ? valueSources.map((v) => makeGrandTotalColumnName(v)) : [makeGrandTotalColumnName(columnSources[i + 1])],
@@ -10128,7 +10132,7 @@ function getTotalsColumnGroups(columnSources, valueSources, isRootColumnExpanded
10128
10132
  })
10129
10133
  );
10130
10134
  }
10131
- function getSnapshotColumnGroups(snapshotColumns, columnSources, valueSources, formatValue) {
10135
+ function makeSnapshotColumnGroups(snapshotColumns, columnSources, valueSources, formatValue) {
10132
10136
  const maxDepth = Math.max(columnSources.length, 1);
10133
10137
  const groupMap = /* @__PURE__ */ new Map();
10134
10138
  const groupName = pluralize(valueSources.length, TOTALS_GROUP_NAME);
@@ -10141,7 +10145,7 @@ function getSnapshotColumnGroups(snapshotColumns, columnSources, valueSources, f
10141
10145
  const isTotalGroup = keys2[i] == null;
10142
10146
  const parentKey = i > 0 ? keys2[i - 1] : null;
10143
10147
  const totalsGroupDisplayName = parentKey == null ? "" : groupName;
10144
- const group = groupMap.get(name) ?? new ExpandableColumnHeaderGroup({
10148
+ const group = groupMap.get(name) ?? new PivotColumnHeaderGroup({
10145
10149
  name,
10146
10150
  displayName: isTotalGroup ? totalsGroupDisplayName : keys2[i],
10147
10151
  isTotalGroup,
@@ -10168,16 +10172,20 @@ function getSnapshotColumnGroups(snapshotColumns, columnSources, valueSources, f
10168
10172
  }
10169
10173
  return [...groupMap.values()];
10170
10174
  }
10171
- function getColumnGroups(pivotTable, snapshotColumns, isRootColumnExpanded = true, formatValue = (v, t) => String(v)) {
10175
+ function makeColumnGroups(pivotTable, snapshotColumns, isRootColumnExpanded = true, includeGroupColumn = false, formatValue = (v, t) => String(v)) {
10172
10176
  const virtualColumnGroups = [
10173
- ...getKeyColumnGroups(pivotTable.columnSources, pivotTable.rowSources),
10174
- ...getTotalsColumnGroups(
10177
+ ...makeKeyColumnGroups(
10178
+ pivotTable.columnSources,
10179
+ pivotTable.rowSources,
10180
+ includeGroupColumn
10181
+ ),
10182
+ ...makeTotalsColumnGroups(
10175
10183
  pivotTable.columnSources,
10176
10184
  pivotTable.valueSources,
10177
10185
  isRootColumnExpanded
10178
10186
  )
10179
10187
  ];
10180
- const snapshotColumnGroups = snapshotColumns == null ? [] : getSnapshotColumnGroups(
10188
+ const snapshotColumnGroups = snapshotColumns == null ? [] : makeSnapshotColumnGroups(
10181
10189
  snapshotColumns,
10182
10190
  pivotTable.columnSources,
10183
10191
  pivotTable.valueSources
@@ -10189,6 +10197,19 @@ const SET_VIEWPORT_THROTTLE = 150;
10189
10197
  const APPLY_VIEWPORT_THROTTLE = 0;
10190
10198
  const ROW_BUFFER_PAGES = 1;
10191
10199
  const COLUMN_BUFFER_PAGES = 1;
10200
+ const VirtualGroupColumn = Object.freeze(
10201
+ makeColumn({
10202
+ name: "__GROUP__",
10203
+ displayName: "Group",
10204
+ type: "java.lang.String",
10205
+ index: 0,
10206
+ depth: 2,
10207
+ isProxy: true
10208
+ })
10209
+ );
10210
+ function isIrisGridPivotModel(model) {
10211
+ return typeof model === "object" && model !== null && "pivotTable" in model && "keyColumns" in model && "expandAll" in model && "collapseAll" in model && "hasExpandableRows" in model && "hasExpandableColumns" in model;
10212
+ }
10192
10213
  class IrisGridPivotModel extends irisGrid.IrisGridModel {
10193
10214
  constructor(dh, pivotTable, formatter = new jsapiUtils.Formatter(dh), config = {}) {
10194
10215
  if (!isCorePlusDh(dh)) {
@@ -10196,13 +10217,9 @@ class IrisGridPivotModel extends irisGrid.IrisGridModel {
10196
10217
  }
10197
10218
  super(dh);
10198
10219
  __publicField(this, "pivotTable");
10199
- __publicField(this, "keyColumns");
10220
+ __publicField(this, "showExtraGroupCol", true);
10200
10221
  __publicField(this, "_layoutHints");
10201
- __publicField(this, "_columnHeaderGroupMap", /* @__PURE__ */ new Map());
10202
- __publicField(this, "columnHeaderParentMap", /* @__PURE__ */ new Map());
10203
- __publicField(this, "_columnHeaderMaxDepth", null);
10204
- __publicField(this, "_columnHeaderGroups", []);
10205
- __publicField(this, "_isColumnHeaderGroupsInitialized", false);
10222
+ __publicField(this, "_sorts", EMPTY_ARRAY);
10206
10223
  __publicField(this, "viewportData", null);
10207
10224
  __publicField(this, "formattedStringData", []);
10208
10225
  __publicField(this, "snapshotColumns", null);
@@ -10219,73 +10236,80 @@ class IrisGridPivotModel extends irisGrid.IrisGridModel {
10219
10236
  __publicField(this, "dh");
10220
10237
  __publicField(this, "getCachedColumns", memoizeOne(
10221
10238
  (snapshotColumns, virtualColumns, valueSources) => {
10239
+ const columns = [];
10240
+ this.pivotTable.columnSources.forEach((source, col) => {
10241
+ const index = -this.pivotTable.columnSources.length + col;
10242
+ columns[index] = makeColumnFromSource(source, index);
10243
+ });
10244
+ columns.push(...virtualColumns);
10222
10245
  if (snapshotColumns == null) {
10223
- log$3.debug2("getCachedColumns", {
10224
- snapshotColumns,
10225
- valueSources
10226
- });
10227
- return virtualColumns;
10246
+ return columns;
10228
10247
  }
10229
- const columns = [...virtualColumns];
10230
10248
  for (let i = 0; i < snapshotColumns.totalCount; i += 1) {
10231
10249
  const isColumnInViewport = i >= snapshotColumns.offset && i < snapshotColumns.offset + snapshotColumns.count;
10232
10250
  for (let v = 0; v < valueSources.length; v += 1) {
10233
10251
  columns.push(
10234
- isColumnInViewport ? makeExpandableDisplayColumn(
10252
+ isColumnInViewport ? makeColumnFromSnapshot(
10235
10253
  snapshotColumns,
10236
10254
  valueSources[v],
10237
10255
  i,
10238
10256
  virtualColumns.length
10239
- ) : makePlaceholderDisplayColumn(
10240
- valueSources[v],
10241
- i,
10242
- virtualColumns.length
10243
- )
10257
+ ) : makePlaceholderColumn(valueSources[v], i, virtualColumns.length)
10244
10258
  );
10245
10259
  }
10246
10260
  }
10247
- log$3.debug2("getCachedColumns", {
10248
- snapshotColumns,
10249
- valueSources,
10250
- columns: columns.map(({ name }) => name)
10251
- });
10252
10261
  return columns;
10253
10262
  }
10254
10263
  ));
10255
10264
  __publicField(this, "getCachedTotalsColumns", memoizeOne(
10256
- (pivotTable, valueSources) => valueSources.map(
10265
+ (pivotTable, valueSources, groupColumn) => valueSources.map(
10257
10266
  (source, col) => makeColumn({
10258
10267
  name: makeGrandTotalColumnName(source),
10259
10268
  displayName: source.name,
10260
10269
  description: source.description,
10261
10270
  type: source.type,
10262
- index: pivotTable.rowSources.length + col,
10271
+ index: pivotTable.rowSources.length + col + (groupColumn == null ? 0 : 1),
10263
10272
  depth: 2,
10264
10273
  isExpanded: true,
10265
10274
  hasChildren: true
10266
10275
  })
10267
10276
  )
10268
10277
  ));
10278
+ __publicField(this, "getCachedKeyColumns", memoizeOne(
10279
+ (pivotTable, groupColumn) => pivotTable.rowSources.map(
10280
+ (source, index) => makeColumnFromSource(source, index + (groupColumn == null ? 0 : 1))
10281
+ )
10282
+ ));
10269
10283
  __publicField(this, "getCachedVirtualColumns", memoizeOne(
10270
- (keyColumns, totalsColumns) => [...keyColumns, ...totalsColumns]
10284
+ (groupColumn, keyColumns, totalsColumns) => groupColumn ? [groupColumn, ...keyColumns, ...totalsColumns] : [...keyColumns, ...totalsColumns]
10271
10285
  ));
10272
10286
  /**
10273
- * Get the cached column header groups.
10287
+ * Get the cached header groups data, including groups array, max depth, parent map, and group map.
10274
10288
  * Returns groups for the key columns, totals, and the snapshot column in the current viewport.
10275
10289
  * Placeholder columns are not included in the groups.
10276
10290
  */
10277
- __publicField(this, "getCachedColumnHeaderGroups", memoizeOne(
10278
- (snapshotColumns, isRootColumnExpanded, formatValue) => getColumnGroups(
10279
- this.pivotTable,
10280
- snapshotColumns,
10281
- isRootColumnExpanded,
10282
- formatValue
10283
- )
10291
+ __publicField(this, "getCachedParsedColumnHeaderData", memoizeOne(
10292
+ (snapshotColumns, groupColumn, formatter, isRootColumnExpanded) => {
10293
+ const columnGroups = makeColumnGroups(
10294
+ this.pivotTable,
10295
+ snapshotColumns,
10296
+ isRootColumnExpanded,
10297
+ groupColumn != null,
10298
+ (value2, type) => this.getCachedFormattedString(formatter, value2, type, "")
10299
+ );
10300
+ return irisGrid.IrisGridUtils.parseColumnHeaderGroups(
10301
+ this,
10302
+ columnGroups,
10303
+ (args) => new PivotColumnHeaderGroup(args)
10304
+ );
10305
+ }
10284
10306
  ));
10285
10307
  __publicField(this, "getColumnIndicesByNameMap", memoizeOne(
10286
10308
  (columns) => {
10287
10309
  const indices = /* @__PURE__ */ new Map();
10288
- columns.forEach(({ name }, i) => indices.set(name, i));
10310
+ Object.entries(columns).forEach(
10311
+ ([i, { name }]) => indices.set(name, Number(i))
10312
+ );
10289
10313
  return indices;
10290
10314
  }
10291
10315
  ));
@@ -10417,9 +10441,6 @@ class IrisGridPivotModel extends irisGrid.IrisGridModel {
10417
10441
  this.snapshotValueSources = pivotTable.valueSources;
10418
10442
  this.rowBufferPages = config.rowBufferPages ?? ROW_BUFFER_PAGES;
10419
10443
  this.columnBufferPages = config.columnBufferPages ?? COLUMN_BUFFER_PAGES;
10420
- this.keyColumns = pivotTable.rowSources.map(
10421
- (source, col) => makeRowSourceColumn(source, col)
10422
- );
10423
10444
  this._layoutHints = {
10424
10445
  backColumns: [],
10425
10446
  hiddenColumns: [],
@@ -10436,10 +10457,38 @@ class IrisGridPivotModel extends irisGrid.IrisGridModel {
10436
10457
  }
10437
10458
  set filter(_) {
10438
10459
  }
10439
- get sort() {
10440
- return EMPTY_ARRAY;
10460
+ hydratePivotSort(sort) {
10461
+ const sourceIndex = this.getColumnIndexByName(sort.column.name);
10462
+ const source = this.columns[sourceIndex ?? -1];
10463
+ return (source == null ? void 0 : source.sort()[sort.direction === "ASC" ? "asc" : "desc"]()) ?? null;
10441
10464
  }
10442
- set sort(_) {
10465
+ get sort() {
10466
+ return this._sorts ?? EMPTY_ARRAY;
10467
+ }
10468
+ set sort(sorts) {
10469
+ log$3.debug("Setting sorts on pivot table", sorts);
10470
+ this._sorts = sorts;
10471
+ const columnBySorts = [];
10472
+ const rowBySorts = [];
10473
+ sorts.forEach((s) => {
10474
+ const sort = this.hydratePivotSort(s);
10475
+ if (sort == null) {
10476
+ log$3.warn(`Cannot hydrate sort for source: ${s.column.name}`, s);
10477
+ return;
10478
+ }
10479
+ const index = this.getColumnIndexByName(sort.name);
10480
+ if (index == null) {
10481
+ log$3.warn(`Cannot find index for source: ${s.column.name}`, s);
10482
+ return;
10483
+ }
10484
+ if (index < 0) {
10485
+ columnBySorts.push(sort);
10486
+ } else {
10487
+ rowBySorts.push(sort);
10488
+ }
10489
+ });
10490
+ this.pivotTable.applyRowSort(rowBySorts);
10491
+ this.pivotTable.applyColumnSort(columnBySorts);
10443
10492
  }
10444
10493
  get customColumns() {
10445
10494
  return EMPTY_ARRAY;
@@ -10484,99 +10533,79 @@ class IrisGridPivotModel extends irisGrid.IrisGridModel {
10484
10533
  async export() {
10485
10534
  throw new Error("Method not implemented.");
10486
10535
  }
10487
- async showFilter() {
10488
- throw new Error("Method not implemented.");
10489
- }
10490
- async quickFilter() {
10491
- throw new Error("Method not implemented.");
10536
+ get showExtraGroupColumn() {
10537
+ return this.showExtraGroupCol;
10492
10538
  }
10493
- async autoResizeColumns() {
10494
- throw new Error("Method not implemented.");
10495
- }
10496
- async applySort() {
10497
- throw new Error("Method not implemented.");
10498
- }
10499
- async clearFilter() {
10500
- throw new Error("Method not implemented.");
10539
+ set showExtraGroupColumn(showExtraGroupCol) {
10540
+ if (showExtraGroupCol === this.showExtraGroupCol) {
10541
+ return;
10542
+ }
10543
+ this.showExtraGroupCol = showExtraGroupCol;
10544
+ this.dispatchEvent(
10545
+ new EventShimCustomEvent(irisGrid.IrisGridModel.EVENT.COLUMNS_CHANGED, {
10546
+ detail: this.columns
10547
+ })
10548
+ );
10501
10549
  }
10502
- async applyFilter() {
10503
- throw new Error("Method not implemented.");
10550
+ get groupColumn() {
10551
+ return this.pivotTable.rowSources.length !== 1 && this.showExtraGroupCol ? VirtualGroupColumn : null;
10504
10552
  }
10505
- async copy() {
10506
- throw new Error("Method not implemented.");
10553
+ get keyColumns() {
10554
+ return this.getCachedKeyColumns(this.pivotTable, this.groupColumn);
10507
10555
  }
10508
10556
  get totalsColumns() {
10509
10557
  return this.getCachedTotalsColumns(
10510
10558
  this.pivotTable,
10511
- this.snapshotValueSources
10559
+ this.snapshotValueSources,
10560
+ this.groupColumn
10512
10561
  );
10513
10562
  }
10514
10563
  get virtualColumns() {
10515
- return this.getCachedVirtualColumns(this.keyColumns, this.totalsColumns);
10564
+ return this.getCachedVirtualColumns(
10565
+ this.groupColumn,
10566
+ this.keyColumns,
10567
+ this.totalsColumns
10568
+ );
10516
10569
  }
10517
- get initialColumnHeaderGroups() {
10518
- const groups = this.getCachedColumnHeaderGroups(
10570
+ getParsedColumnHeaderData() {
10571
+ return this.getCachedParsedColumnHeaderData(
10519
10572
  this.snapshotColumns,
10520
- this.isRootColumnExpanded,
10521
- (value2, type) => (
10522
- // Ignore name based formatting, pass empty column name
10523
- this.getCachedFormattedString(this.formatter, value2, type, "")
10524
- )
10573
+ this.groupColumn,
10574
+ this.formatter,
10575
+ this.isRootColumnExpanded
10525
10576
  );
10526
- log$3.debug2("initialColumnHeaderGroups", groups);
10527
- return groups;
10577
+ }
10578
+ get initialColumnHeaderGroups() {
10579
+ return this.columnHeaderGroups;
10528
10580
  }
10529
10581
  get columnHeaderMaxDepth() {
10530
- return this._columnHeaderMaxDepth ?? 1;
10582
+ const { maxDepth } = this.getParsedColumnHeaderData();
10583
+ return maxDepth;
10531
10584
  }
10532
- set columnHeaderMaxDepth(depth) {
10533
- this._columnHeaderMaxDepth = depth;
10585
+ get columnHeaderParentMap() {
10586
+ const { parentMap } = this.getParsedColumnHeaderData();
10587
+ return parentMap;
10534
10588
  }
10535
10589
  get columnHeaderGroupMap() {
10536
- this.initializeColumnHeaderGroups();
10537
- return this._columnHeaderGroupMap;
10590
+ const { groupMap } = this.getParsedColumnHeaderData();
10591
+ return groupMap;
10538
10592
  }
10539
10593
  get columnHeaderGroups() {
10540
- this.initializeColumnHeaderGroups();
10541
- return this._columnHeaderGroups;
10594
+ const { groups } = this.getParsedColumnHeaderData();
10595
+ return groups;
10542
10596
  }
10543
10597
  set columnHeaderGroups(_groups) {
10544
10598
  }
10545
- setInternalColumnHeaderGroups(groups) {
10546
- if (groups === this._columnHeaderGroups) {
10547
- return;
10548
- }
10549
- const {
10550
- groups: newGroups,
10551
- maxDepth,
10552
- parentMap,
10553
- groupMap
10554
- } = irisGrid.IrisGridUtils.parseColumnHeaderGroups(
10555
- this,
10556
- groups,
10557
- (args) => new ExpandableColumnHeaderGroup(args)
10558
- );
10559
- this._columnHeaderGroups = newGroups;
10560
- this.columnHeaderMaxDepth = maxDepth;
10561
- this.columnHeaderParentMap = parentMap;
10562
- this._columnHeaderGroupMap = groupMap;
10563
- this._isColumnHeaderGroupsInitialized = true;
10564
- }
10565
- initializeColumnHeaderGroups() {
10566
- if (!this._isColumnHeaderGroupsInitialized) {
10567
- this.setInternalColumnHeaderGroups(this.initialColumnHeaderGroups);
10568
- }
10569
- }
10570
10599
  textForColumnHeader(x, depth = 0) {
10571
10600
  const header = this.columnAtDepth(x, depth);
10572
- if (isExpandableColumnHeaderGroup(header)) {
10601
+ if (isPivotColumnHeaderGroup(header)) {
10573
10602
  return header.isNew ? "" : header.displayName ?? header.name;
10574
10603
  }
10575
10604
  return (header == null ? void 0 : header.displayName) ?? (header == null ? void 0 : header.name);
10576
10605
  }
10577
10606
  colorForColumnHeader(x, depth = 0, theme = {}) {
10578
10607
  const column = this.columnAtDepth(x, depth);
10579
- if (isExpandableColumnHeaderGroup(column)) {
10608
+ if (isPivotColumnHeaderGroup(column)) {
10580
10609
  if (column.isTotalGroup != null && column.isTotalGroup) {
10581
10610
  return theme.totalsHeaderBackground ?? null;
10582
10611
  }
@@ -10588,7 +10617,7 @@ class IrisGridPivotModel extends irisGrid.IrisGridModel {
10588
10617
  }
10589
10618
  getColumnHeaderGroup(modelIndex, depth) {
10590
10619
  const group = this.columnAtDepth(modelIndex, depth);
10591
- if (isExpandableColumnHeaderGroup(group)) {
10620
+ if (isPivotColumnHeaderGroup(group)) {
10592
10621
  return group;
10593
10622
  }
10594
10623
  return void 0;
@@ -10625,6 +10654,10 @@ class IrisGridPivotModel extends irisGrid.IrisGridModel {
10625
10654
  get initialMovedColumns() {
10626
10655
  return EMPTY_ARRAY;
10627
10656
  }
10657
+ /**
10658
+ * Get the columns in the pivot model.
10659
+ * Returned array includes column sources with negative indexes.
10660
+ */
10628
10661
  get columns() {
10629
10662
  return this.getCachedColumns(
10630
10663
  this.snapshotColumns,
@@ -10654,7 +10687,8 @@ class IrisGridPivotModel extends irisGrid.IrisGridModel {
10654
10687
  return false;
10655
10688
  }
10656
10689
  isColumnSortable(columnIndex) {
10657
- return false;
10690
+ var _a;
10691
+ return ((_a = this.columns[columnIndex]) == null ? void 0 : _a.isSortable) ?? false;
10658
10692
  }
10659
10693
  get isTotalsAvailable() {
10660
10694
  return false;
@@ -10720,10 +10754,10 @@ class IrisGridPivotModel extends irisGrid.IrisGridModel {
10720
10754
  this.formattedStringData = [];
10721
10755
  this.viewportData = this.extractSnapshotData(snapshot);
10722
10756
  this.updatePendingExpandCollapseState();
10723
- this.setInternalColumnHeaderGroups(this.initialColumnHeaderGroups);
10724
10757
  log$3.debug2("Pivot updated", {
10725
10758
  columns: this.columns,
10726
- snapshot: this.snapshotColumns,
10759
+ snapshot,
10760
+ snapshotColumns: this.snapshotColumns,
10727
10761
  viewport: (_a = this.viewportData) == null ? void 0 : _a.rowTotalCount,
10728
10762
  columnCount: this.columnCount,
10729
10763
  rowCount: this.rowCount
@@ -10774,10 +10808,7 @@ class IrisGridPivotModel extends irisGrid.IrisGridModel {
10774
10808
  const keyData = /* @__PURE__ */ new Map();
10775
10809
  const totalsData = /* @__PURE__ */ new Map();
10776
10810
  for (let c = 0; c < keys2.length; c += 1) {
10777
- keyData.set(c, {
10778
- // Only render the value for the deepest level
10779
- value: c === depth - 1 ? keys2[c] : void 0
10780
- });
10811
+ keyData.set(c, { value: keys2[c] });
10781
10812
  }
10782
10813
  for (let v = 0; v < snapshot.valueSources.length; v += 1) {
10783
10814
  totalsData.set(v, {
@@ -10988,41 +11019,33 @@ class IrisGridPivotModel extends irisGrid.IrisGridModel {
10988
11019
  this.setColumnExpanded(this.keyColumns.length, false, true);
10989
11020
  }
10990
11021
  isColumnExpandable(x, depth) {
10991
- var _a, _b;
10992
- log$3.debug2("isColumnExpandable", {
10993
- x,
10994
- depth,
10995
- name: (_a = this.columns[x]) == null ? void 0 : _a.name,
10996
- v: this.virtualColumns,
10997
- cC: this.columnCount,
10998
- c: this.columns
10999
- });
11000
- if (x >= this.keyColumns.length && x < this.virtualColumns.length) {
11022
+ var _a;
11023
+ if (this.isGrandTotalsColumn(x)) {
11001
11024
  return !this.isRootColumnExpanded || this.columns.length > this.virtualColumns.length;
11002
11025
  }
11003
11026
  if (x < this.keyColumns.length) {
11004
11027
  return false;
11005
11028
  }
11006
- return ((_b = this.columns[x]) == null ? void 0 : _b.hasChildren) ?? false;
11029
+ return ((_a = this.columns[x]) == null ? void 0 : _a.hasChildren) ?? false;
11030
+ }
11031
+ isGrandTotalsColumn(x) {
11032
+ const totalsStartIndex = this.keyColumns.length + (this.groupColumn == null ? 0 : 1);
11033
+ return x >= totalsStartIndex && x < this.virtualColumns.length;
11007
11034
  }
11008
11035
  isColumnExpanded(x) {
11009
11036
  var _a;
11010
- if (x >= this.keyColumns.length && x < this.virtualColumns.length) {
11037
+ if (this.isGrandTotalsColumn(x)) {
11011
11038
  return this.isRootColumnExpanded;
11012
11039
  }
11013
11040
  return ((_a = this.columns[x]) == null ? void 0 : _a.isExpanded) ?? false;
11014
11041
  }
11015
11042
  setColumnExpanded(x, isExpanded, expandDescendants = false) {
11016
- var _a;
11017
- log$3.debug2("[0] setColumnExpanded", {
11043
+ log$3.debug2("setColumnExpanded", {
11018
11044
  x,
11019
11045
  isExpanded,
11020
- name: (_a = this.columns[x]) == null ? void 0 : _a.name,
11021
- v: this.virtualColumns,
11022
- cC: this.columnCount,
11023
- c: this.columns
11046
+ expandDescendants
11024
11047
  });
11025
- if (x >= this.keyColumns.length && x < this.virtualColumns.length) {
11048
+ if (this.isGrandTotalsColumn(x)) {
11026
11049
  this.pivotTable.setRootColumnExpanded(isExpanded, expandDescendants);
11027
11050
  this.isRootColumnExpanded = isExpanded;
11028
11051
  return;
@@ -11088,15 +11111,27 @@ class IrisGridPivotModel extends irisGrid.IrisGridModel {
11088
11111
  this.formattedStringData[x][y] = text;
11089
11112
  }
11090
11113
  dataForCell(x, y) {
11091
- var _a, _b, _c;
11114
+ var _a, _b, _c, _d, _e, _f;
11092
11115
  const keyCount = this.keyColumns.length;
11093
- if (x < keyCount) {
11094
- return (_a = this.row(y)) == null ? void 0 : _a.keyData.get(x);
11116
+ const groupOffset = this.groupColumn == null ? 0 : 1;
11117
+ if (groupOffset === 1 && x === 0) {
11118
+ const rowDepth = ((_a = this.row(y)) == null ? void 0 : _a.depth) ?? 2;
11119
+ return y === 0 ? (
11120
+ // Empty value for the group cell in the totals row
11121
+ { value: "" }
11122
+ ) : {
11123
+ // Render all key values except the last one in the group column
11124
+ // to match the Rollup UI
11125
+ value: rowDepth < keyCount ? (_c = (_b = this.row(y)) == null ? void 0 : _b.keyData.get(rowDepth - 1)) == null ? void 0 : _c.value : ""
11126
+ };
11127
+ }
11128
+ if (x < keyCount + groupOffset) {
11129
+ return (_d = this.row(y)) == null ? void 0 : _d.keyData.get(x - groupOffset);
11095
11130
  }
11096
11131
  if (x < this.virtualColumns.length) {
11097
- return (_b = this.row(y)) == null ? void 0 : _b.totalsData.get(x - keyCount);
11132
+ return (_e = this.row(y)) == null ? void 0 : _e.totalsData.get(x - keyCount - groupOffset);
11098
11133
  }
11099
- return (_c = this.row(y)) == null ? void 0 : _c.data.get(x - this.virtualColumns.length);
11134
+ return (_f = this.row(y)) == null ? void 0 : _f.data.get(x - this.virtualColumns.length);
11100
11135
  }
11101
11136
  formatForCell(x, y) {
11102
11137
  var _a;
@@ -11169,8 +11204,8 @@ function useIrisGridPivotModel(fetch) {
11169
11204
  [model]
11170
11205
  );
11171
11206
  const makeModel = React.useCallback(async () => {
11172
- const pivotWidget = await fetch();
11173
- return new IrisGridPivotModel(dh, pivotWidget);
11207
+ const pivotTable = await fetch();
11208
+ return new IrisGridPivotModel(dh, pivotTable);
11174
11209
  }, [dh, fetch]);
11175
11210
  const reload = React.useCallback(async () => {
11176
11211
  setIsLoading(true);
@@ -11218,6 +11253,22 @@ function useIrisGridPivotModel(fetch) {
11218
11253
  }
11219
11254
  throw new Error("Invalid state");
11220
11255
  }
11256
+ const log$2 = Log.module("@deephaven/js-plugin-pivot/usePivotTableFetch");
11257
+ function usePivotTableFetch(fetch) {
11258
+ const api = jsapiBootstrap.useApi();
11259
+ return React.useCallback(
11260
+ () => fetch().then((widget) => {
11261
+ log$2.debug("Pivot fetch result:", widget);
11262
+ if (!isCorePlusDh(api)) {
11263
+ throw new Error("CorePlus is not available");
11264
+ }
11265
+ const pivotTable = new api.coreplus.pivot.PivotTable(widget);
11266
+ log$2.debug("Created pivot table:", pivotTable);
11267
+ return pivotTable;
11268
+ }),
11269
+ [api, fetch]
11270
+ );
11271
+ }
11221
11272
  class PivotColumnGroupMouseHandler extends GridMouseHandler {
11222
11273
  constructor(irisGrid2) {
11223
11274
  super();
@@ -11233,6 +11284,26 @@ class PivotColumnGroupMouseHandler extends GridMouseHandler {
11233
11284
  }
11234
11285
  return null;
11235
11286
  }
11287
+ setCursor(gridPoint, grid) {
11288
+ const { column, columnHeaderDepth } = gridPoint;
11289
+ if (this.isExpandableColumnGroup(column, columnHeaderDepth)) {
11290
+ this.cursor = "pointer";
11291
+ return { stopPropagation: false, preventDefault: false };
11292
+ }
11293
+ this.cursor = null;
11294
+ return false;
11295
+ }
11296
+ isExpandableColumnGroup(column, columnHeaderDepth = 0) {
11297
+ const { model } = this.irisGrid.props;
11298
+ if (column == null || model == null) {
11299
+ return false;
11300
+ }
11301
+ const group = model.getColumnHeaderGroup(column, columnHeaderDepth);
11302
+ return group != null && isPivotColumnHeaderGroup(group) && group.isExpandable;
11303
+ }
11304
+ onMove(gridPoint, grid) {
11305
+ return this.setCursor(gridPoint, grid);
11306
+ }
11236
11307
  // We need to remember where the down started, because the canvas element will trigger a click wherever mouseUp is
11237
11308
  onDown(gridPoint) {
11238
11309
  this.column = this.getColumnGroupFromGridPoint(gridPoint);
@@ -11240,165 +11311,77 @@ class PivotColumnGroupMouseHandler extends GridMouseHandler {
11240
11311
  }
11241
11312
  onClick(gridPoint, grid, event) {
11242
11313
  const column = this.getColumnGroupFromGridPoint(gridPoint);
11243
- if (column != null && column === this.column) {
11244
- this.irisGrid.toggleExpandColumn(column);
11314
+ if (column != null && column === this.column && this.isExpandableColumnGroup(column, gridPoint.columnHeaderDepth)) {
11315
+ this.irisGrid.toggleExpandColumn(
11316
+ column,
11317
+ GridUtils.isModifierKeyDown(event)
11318
+ );
11319
+ return true;
11320
+ }
11321
+ return false;
11322
+ }
11323
+ }
11324
+ class PivotSortMouseHandler extends GridMouseHandler {
11325
+ constructor(irisGrid2) {
11326
+ super();
11327
+ __publicField(this, "columnSource");
11328
+ __publicField(this, "irisGrid");
11329
+ this.columnSource = null;
11330
+ this.irisGrid = irisGrid2;
11331
+ }
11332
+ /**
11333
+ * Get the column source from a grid point
11334
+ * @param gridPoint The grid point to check
11335
+ * @returns The column source index if the grid point is in a column source header, else null
11336
+ */
11337
+ getColumnSourceHeaderFromGridPoint(gridPoint) {
11338
+ const { column, row, columnHeaderDepth } = gridPoint;
11339
+ const { model } = this.irisGrid.props;
11340
+ assertNotNull(model);
11341
+ const sourceIndex = columnHeaderDepth != null ? -columnHeaderDepth : null;
11342
+ if (column == null || row !== null || columnHeaderDepth == null) {
11343
+ return null;
11344
+ }
11345
+ const group = model.getColumnHeaderGroup(column, columnHeaderDepth);
11346
+ if (sourceIndex != null && sourceIndex < 0 && isIrisGridPivotModel(model) && model.isColumnSortable(sourceIndex) && isPivotColumnHeaderGroup(group) && group.isKeyColumnGroup) {
11347
+ return sourceIndex;
11348
+ }
11349
+ return null;
11350
+ }
11351
+ // We need to remember where the down started, because the canvas element will trigger a click where mouseUp is
11352
+ onDown(gridPoint, grid, event) {
11353
+ this.columnSource = this.getColumnSourceHeaderFromGridPoint(gridPoint);
11354
+ return false;
11355
+ }
11356
+ onClick(gridPoint, grid, event) {
11357
+ const columnSource = this.getColumnSourceHeaderFromGridPoint(gridPoint);
11358
+ if (columnSource != null && columnSource === this.columnSource) {
11359
+ const addToExisting = components.ContextActionUtils.isModifierKeyDown(event);
11360
+ this.irisGrid.toggleSort(columnSource, addToExisting);
11245
11361
  return true;
11246
11362
  }
11247
11363
  return false;
11248
11364
  }
11249
11365
  }
11366
+ function usePivotMouseHandlers() {
11367
+ return React.useMemo(
11368
+ () => [
11369
+ (irisGrid2) => new PivotColumnGroupMouseHandler(irisGrid2),
11370
+ (irisGrid2) => new PivotSortMouseHandler(irisGrid2)
11371
+ ],
11372
+ []
11373
+ );
11374
+ }
11250
11375
  function getColumnGroupName(model, modelColumn, depth) {
11251
11376
  var _a;
11252
11377
  return (_a = model.getColumnHeaderGroup(modelColumn, depth ?? 0)) == null ? void 0 : _a.name;
11253
11378
  }
11254
11379
  class IrisGridPivotRenderer extends irisGrid.IrisGridRenderer {
11255
- drawColumnHeaders(context, state) {
11256
- const {
11257
- mouseX,
11258
- mouseY,
11259
- theme,
11260
- metrics,
11261
- draggingColumnSeparator,
11262
- isDragging,
11263
- model
11264
- } = state;
11265
- const {
11266
- columnHeaderHeight,
11267
- floatingColumns,
11268
- gridX,
11269
- width,
11270
- visibleColumns,
11271
- allColumnWidths,
11272
- allColumnXs,
11273
- floatingLeftColumnCount,
11274
- floatingLeftWidth,
11275
- floatingRightWidth,
11276
- modelColumns,
11277
- columnHeaderMaxDepth
11278
- } = metrics;
11279
- if (columnHeaderHeight <= 0) {
11280
- return;
11281
- }
11282
- const {
11283
- headerHiddenSeparatorSize,
11284
- headerHiddenSeparatorHoverColor,
11285
- headerSeparatorColor,
11286
- headerSeparatorHoverColor
11287
- } = theme;
11288
- const hiddenSeparatorHeight = columnHeaderHeight * 0.5;
11289
- const hiddenY = columnHeaderHeight * (columnHeaderMaxDepth - 1) + columnHeaderHeight * 0.5 - hiddenSeparatorHeight * 0.5;
11290
- const containsFrozenColumns = floatingLeftColumnCount > 0;
11291
- if (!isExpandableColumnGridModel(model)) {
11292
- throw new Error("Unsupported model type");
11293
- }
11294
- context.save();
11295
- this.drawColumnHeadersForRange(
11296
- context,
11297
- state,
11298
- [visibleColumns[0], visibleColumns[visibleColumns.length - 1]],
11299
- {
11300
- minX: gridX + floatingLeftWidth,
11301
- maxX: width - floatingRightWidth
11302
- }
11303
- );
11304
- if (containsFrozenColumns) {
11305
- this.drawColumnHeadersForRange(
11306
- context,
11307
- state,
11308
- [floatingColumns[0], floatingColumns[floatingColumns.length - 1]],
11309
- {
11310
- minX: gridX,
11311
- maxX: gridX + floatingLeftWidth
11312
- }
11313
- );
11314
- }
11315
- if (headerSeparatorColor) {
11316
- context.strokeStyle = headerSeparatorColor;
11317
- const hiddenColumns = [...allColumnWidths.entries()].filter(([_, w]) => w === 0).map(([index]) => index);
11318
- context.beginPath();
11319
- context.fillStyle = headerSeparatorColor;
11320
- for (let i = 0; i < hiddenColumns.length; i += 1) {
11321
- const column = hiddenColumns[i];
11322
- const columnX = getOrThrow(allColumnXs, column);
11323
- const columnWidth = getOrThrow(allColumnWidths, column);
11324
- const minX = gridX + columnX + columnWidth + 0.5 - headerHiddenSeparatorSize * 0.5;
11325
- context.rect(
11326
- minX,
11327
- hiddenY,
11328
- headerHiddenSeparatorSize,
11329
- hiddenSeparatorHeight
11330
- );
11331
- }
11332
- context.fill();
11333
- }
11334
- if (headerSeparatorHoverColor) {
11335
- let { index: highlightedSeparator, depth } = draggingColumnSeparator ?? {};
11336
- if (highlightedSeparator == null && mouseX != null && mouseY != null) {
11337
- const separator = GridColumnSeparatorMouseHandler$1.getColumnSeparator(
11338
- GridUtils.getGridPointFromXY(mouseX, mouseY, metrics),
11339
- metrics,
11340
- model,
11341
- theme
11342
- );
11343
- highlightedSeparator = separator == null ? void 0 : separator.index;
11344
- depth = separator == null ? void 0 : separator.depth;
11345
- }
11346
- let shouldDrawSeparator;
11347
- if (highlightedSeparator == null) {
11348
- shouldDrawSeparator = false;
11349
- } else {
11350
- const columnIndex = modelColumns.get(highlightedSeparator);
11351
- const nextColumnIndex = modelColumns.get(highlightedSeparator + 1);
11352
- if (columnIndex == null || nextColumnIndex == null) {
11353
- shouldDrawSeparator = false;
11354
- } else {
11355
- shouldDrawSeparator = getColumnGroupName(model, columnIndex, depth) !== getColumnGroupName(model, nextColumnIndex, depth);
11356
- }
11357
- }
11358
- if (shouldDrawSeparator && highlightedSeparator != null && depth != null && (!isDragging || draggingColumnSeparator != null)) {
11359
- context.strokeStyle = headerSeparatorHoverColor;
11360
- const columnX = getOrThrow(allColumnXs, highlightedSeparator);
11361
- const columnWidth = getOrThrow(allColumnWidths, highlightedSeparator);
11362
- const x = gridX + columnX + columnWidth + 0.5;
11363
- const visibleColumnIndex = visibleColumns.indexOf(highlightedSeparator);
11364
- const nextColumn = visibleColumnIndex < visibleColumns.length - 1 ? visibleColumns[visibleColumnIndex + 1] : null;
11365
- const nextColumnWidth = nextColumn != null ? allColumnWidths.get(nextColumn) : null;
11366
- const isColumnHidden = columnWidth === 0;
11367
- const isNextColumnHidden = nextColumnWidth != null && nextColumnWidth === 0;
11368
- if (isColumnHidden) {
11369
- context.strokeStyle = headerHiddenSeparatorHoverColor;
11370
- context.fillStyle = headerHiddenSeparatorHoverColor;
11371
- context.fillRect(
11372
- x,
11373
- hiddenY,
11374
- headerHiddenSeparatorSize * 0.5,
11375
- hiddenSeparatorHeight
11376
- );
11377
- } else if (isNextColumnHidden) {
11378
- context.fillStyle = headerSeparatorHoverColor;
11379
- context.fillRect(
11380
- x - headerHiddenSeparatorSize * 0.5,
11381
- hiddenY,
11382
- headerHiddenSeparatorSize * 0.5,
11383
- hiddenSeparatorHeight
11384
- );
11385
- }
11386
- context.beginPath();
11387
- context.moveTo(
11388
- x,
11389
- (columnHeaderMaxDepth - depth - 1) * columnHeaderHeight
11390
- );
11391
- context.lineTo(
11392
- x,
11393
- (columnHeaderMaxDepth - depth) * columnHeaderHeight - 1
11394
- );
11395
- context.stroke();
11396
- }
11397
- }
11398
- context.restore();
11399
- }
11400
11380
  drawColumnHeadersAtDepth(context, state, range, bounds, depth) {
11401
11381
  const { metrics, model, theme } = state;
11382
+ if (!isIrisGridPivotModel(model)) {
11383
+ throw new Error("Unsupported model type");
11384
+ }
11402
11385
  const {
11403
11386
  modelColumns,
11404
11387
  allColumnXs,
@@ -11432,10 +11415,14 @@ class IrisGridPivotRenderer extends irisGrid.IrisGridRenderer {
11432
11415
  while (columnIndex <= endIndex) {
11433
11416
  const { columnCount } = metrics;
11434
11417
  const modelColumn = getOrThrow(modelColumns, columnIndex);
11435
- const columnGroupColor = isExpandableColumnGridModel(model) ? model.colorForColumnHeader(modelColumn, depth, theme) : model.colorForColumnHeader(modelColumn, depth);
11418
+ const columnGroupColor = model.colorForColumnHeader(
11419
+ modelColumn,
11420
+ depth,
11421
+ theme
11422
+ );
11436
11423
  const headerGroup = model.getColumnHeaderGroup(modelColumn, depth ?? 0);
11437
- const isExpandable = isExpandableColumnHeaderGroup(headerGroup) && headerGroup.isExpandable;
11438
- const isExpanded = isExpandableColumnHeaderGroup(headerGroup) && headerGroup.isExpanded;
11424
+ const isExpandable = isPivotColumnHeaderGroup(headerGroup) && headerGroup.isExpandable;
11425
+ const isExpanded = isPivotColumnHeaderGroup(headerGroup) && headerGroup.isExpanded;
11439
11426
  const columnGroupName = getColumnGroupName(model, modelColumn, depth);
11440
11427
  let columnGroupLeft = getOrThrow(allColumnXs, columnIndex) + gridX;
11441
11428
  let columnGroupRight = columnGroupLeft + getOrThrow(allColumnWidths, columnIndex);
@@ -11481,7 +11468,8 @@ class IrisGridPivotRenderer extends irisGrid.IrisGridRenderer {
11481
11468
  },
11482
11469
  bounds,
11483
11470
  isExpandable,
11484
- isExpanded
11471
+ isExpanded,
11472
+ jsapiUtils.TableUtils.getSortForColumn(model.sort, columnGroupName)
11485
11473
  );
11486
11474
  }
11487
11475
  columnIndex += 1;
@@ -11489,7 +11477,7 @@ class IrisGridPivotRenderer extends irisGrid.IrisGridRenderer {
11489
11477
  }
11490
11478
  context.restore();
11491
11479
  }
11492
- drawColumnHeader(context, state, columnText, columnX, columnWidth, style, bounds, isExpandable = false, isExpanded = false) {
11480
+ drawColumnHeader(context, state, columnText, columnX, columnWidth, style, bounds, isExpandable = false, isExpanded = false, sort = null) {
11493
11481
  if (columnWidth <= 0) {
11494
11482
  return;
11495
11483
  }
@@ -11597,6 +11585,15 @@ class IrisGridPivotRenderer extends irisGrid.IrisGridRenderer {
11597
11585
  isExpanded
11598
11586
  );
11599
11587
  }
11588
+ this.drawColumnSourceSortIndicator(
11589
+ context,
11590
+ state,
11591
+ sort,
11592
+ columnText,
11593
+ columnX,
11594
+ columnWidth,
11595
+ { minX, maxX }
11596
+ );
11600
11597
  context.restore();
11601
11598
  }
11602
11599
  drawColumnHeaderTreeMarker(context, state, columnX, columnWidth, headerY, headerHeight, treeBox, isExpanded) {
@@ -11624,39 +11621,60 @@ class IrisGridPivotRenderer extends irisGrid.IrisGridRenderer {
11624
11621
  isExpanded
11625
11622
  );
11626
11623
  }
11624
+ drawColumnSourceSortIndicator(context, state, sort, columnText, columnX, columnWidth, bounds) {
11625
+ const { metrics, theme } = state;
11626
+ const { gridX, columnHeaderHeight } = metrics;
11627
+ const { headerHorizontalPadding, iconSize: themeIconSize } = theme;
11628
+ const iconSize = Math.round(themeIconSize * 0.75);
11629
+ if (sort == null) {
11630
+ return;
11631
+ }
11632
+ const icon = this.getSortIcon(sort, iconSize);
11633
+ if (!icon) {
11634
+ return;
11635
+ }
11636
+ const textWidth = this.getCachedHeaderWidth(context, columnText);
11637
+ const textRight = gridX + columnX + textWidth + headerHorizontalPadding;
11638
+ let { maxX } = bounds;
11639
+ maxX -= headerHorizontalPadding;
11640
+ const defaultX = gridX + columnX + columnWidth - iconSize;
11641
+ const x = textRight > maxX ? textRight + 1 : Math.min(maxX, defaultX);
11642
+ const y = (columnHeaderHeight - iconSize) * 0.5;
11643
+ context.save();
11644
+ context.fillStyle = theme.headerSortBarColor;
11645
+ context.translate(x, y);
11646
+ context.fill(icon);
11647
+ context.restore();
11648
+ }
11627
11649
  }
11628
- const IrisGridPivotTheme = Object.freeze({
11650
+ function usePivotRenderer() {
11651
+ return React.useMemo(() => new IrisGridPivotRenderer(), []);
11652
+ }
11653
+ const IrisGridPivotThemeColors = Object.freeze({
11629
11654
  columnSourceHeaderBackground: "var(--dh-color-grid-bg)",
11630
11655
  totalsHeaderBackground: "var(--dh-color-grid-bg)"
11631
11656
  });
11632
- const log$2 = Log.module("@deephaven/js-plugin-pivot/PivotWidget");
11657
+ function getIrisGridPivotTheme() {
11658
+ return Object.freeze({
11659
+ ...components.resolveCssVariablesInRecord(IrisGridPivotThemeColors)
11660
+ });
11661
+ }
11662
+ const log$1 = Log.module("@deephaven/js-plugin-pivot/usePivotTheme");
11663
+ function usePivotTheme() {
11664
+ const theme = components.useTheme();
11665
+ return React.useMemo(() => {
11666
+ log$1.debug("Theme changed, updating pivot theme", theme);
11667
+ return getIrisGridPivotTheme();
11668
+ }, [theme]);
11669
+ }
11633
11670
  function PivotWidget({
11634
11671
  fetch
11635
11672
  }) {
11636
- const dh = jsapiBootstrap.useApi();
11637
- const mouseHandlers = React.useMemo(
11638
- () => [(irisGrid2) => new PivotColumnGroupMouseHandler(irisGrid2)],
11639
- []
11640
- );
11641
- const renderer = React.useMemo(() => new IrisGridPivotRenderer(), []);
11642
- const theme = components.useTheme();
11643
- const pivotTheme = React.useMemo(() => {
11644
- log$2.debug("Theme changed, updating pivot theme", theme);
11645
- return components.resolveCssVariablesInRecord(IrisGridPivotTheme);
11646
- }, [theme]);
11647
- const pivotTableFetch = React.useCallback(
11648
- () => fetch().then((result) => {
11649
- log$2.debug("pivotWidget fetch result:", result);
11650
- if (!isCorePlusDh(dh)) {
11651
- throw new Error("CorePlus is not available");
11652
- }
11653
- const pivot = new dh.coreplus.pivot.PivotTable(result);
11654
- log$2.debug("Created pivot table:", pivot);
11655
- return pivot;
11656
- }),
11657
- [dh, fetch]
11658
- );
11659
- const fetchResult = useIrisGridPivotModel(pivotTableFetch);
11673
+ const pivotFetch = usePivotTableFetch(fetch);
11674
+ const mouseHandlers = usePivotMouseHandlers();
11675
+ const renderer = usePivotRenderer();
11676
+ const pivotTheme = usePivotTheme();
11677
+ const fetchResult = useIrisGridPivotModel(pivotFetch);
11660
11678
  if (fetchResult.status === "loading") {
11661
11679
  return /* @__PURE__ */ jsxRuntimeExports.jsx(components.LoadingOverlay, { isLoading: true });
11662
11680
  }
@@ -11680,70 +11698,82 @@ function PivotWidget({
11680
11698
  }
11681
11699
  );
11682
11700
  }
11683
- const log$1 = Log.module("@deephaven/js-plugin-pivot/useHydratePivotGrid");
11684
- function useHydratePivotGrid(fetch, id, metadata) {
11701
+ const log = Log.module("@deephaven/js-plugin-pivot/useHydratePivotGrid");
11702
+ function useHydratePivotGrid(id, metadata) {
11703
+ assertNotNull(metadata, "Missing Pivot metadata");
11704
+ const objectFetch = jsapiBootstrap.useObjectFetch(metadata);
11685
11705
  const api = jsapiBootstrap.useApi();
11686
11706
  const loadPlugin = dashboardCorePlugins.useLoadTablePlugin();
11687
- const fetchTable = React.useCallback(
11688
- () => fetch().then((result) => {
11689
- log$1.debug("pivotWidget fetch result:", result);
11690
- if (!isCorePlusDh(api)) {
11691
- throw new Error("CorePlus is not available");
11692
- }
11693
- const pivot = new api.coreplus.pivot.PivotTable(result);
11694
- log$1.debug("Created pivot table:", pivot);
11695
- return pivot;
11696
- }),
11697
- [api, fetch]
11698
- );
11699
- const mouseHandlers = React.useMemo(
11700
- () => [(irisGrid2) => new PivotColumnGroupMouseHandler(irisGrid2)],
11701
- []
11702
- );
11703
- const renderer = React.useMemo(() => new IrisGridPivotRenderer(), []);
11704
- const theme = components.useTheme();
11705
- const pivotTheme = React.useMemo(() => {
11706
- log$1.debug("Theme changed, updating pivot theme", theme);
11707
- return components.resolveCssVariablesInRecord(IrisGridPivotTheme);
11708
- }, [theme]);
11709
- const hydratedProps = React.useMemo(
11710
- () => ({
11707
+ const mouseHandlers = usePivotMouseHandlers();
11708
+ const renderer = usePivotRenderer();
11709
+ const theme = usePivotTheme();
11710
+ const { status } = objectFetch;
11711
+ if (status === "loading") {
11712
+ log.debug("Widget is loading");
11713
+ return { status: "loading" };
11714
+ }
11715
+ if (status === "error") {
11716
+ log.debug("Error fetching widget:", objectFetch.error);
11717
+ return {
11718
+ status: "error",
11719
+ error: objectFetch.error
11720
+ };
11721
+ }
11722
+ const { fetch } = objectFetch;
11723
+ return {
11724
+ status: "success",
11725
+ props: {
11711
11726
  loadPlugin,
11712
11727
  localDashboardId: id,
11713
- makeModel: async () => {
11714
- const pivotWidget = await fetchTable();
11715
- return new IrisGridPivotModel(api, pivotWidget);
11728
+ makeModel: async function makeModel() {
11729
+ log.debug("Fetching pivot widget");
11730
+ const widget = await fetch();
11731
+ log.debug("Pivot fetch result:", widget);
11732
+ if (!isCorePlusDh(api)) {
11733
+ throw new Error("CorePlus is not available");
11734
+ }
11735
+ const pivotTable = new api.coreplus.pivot.PivotTable(widget);
11736
+ log.debug("Created pivot table:", pivotTable);
11737
+ return new IrisGridPivotModel(api, pivotTable);
11716
11738
  },
11717
11739
  metadata,
11718
11740
  mouseHandlers,
11719
11741
  renderer,
11720
- theme: pivotTheme
11721
- }),
11722
- [
11723
- api,
11724
- fetchTable,
11725
- id,
11726
- loadPlugin,
11727
- metadata,
11728
- mouseHandlers,
11729
- renderer,
11730
- pivotTheme
11731
- ]
11732
- );
11733
- return hydratedProps;
11734
- }
11735
- function PivotPanel(props) {
11736
- const { localDashboardId, fetch, metadata } = props;
11737
- const hydratedProps = useHydratePivotGrid(fetch, localDashboardId, metadata);
11738
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
11739
- dashboardCorePlugins.IrisGridPanel,
11740
- {
11741
- ...props,
11742
- ...hydratedProps
11742
+ theme
11743
11743
  }
11744
- );
11744
+ };
11745
11745
  }
11746
- PivotPanel.COMPONENT = "PivotPanel";
11746
+ const PivotPanel = React.forwardRef(
11747
+ // Unconnected IrisGridPanel type is not exported from dashboard-core-plugins
11748
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
11749
+ (panelProps, ref) => {
11750
+ const { localDashboardId, metadata, panelState, ...props } = panelProps;
11751
+ const hydrateResult = useHydratePivotGrid(localDashboardId, metadata);
11752
+ if (hydrateResult.status === "loading") {
11753
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(components.LoadingOverlay, { isLoading: true });
11754
+ }
11755
+ if (hydrateResult.status === "error") {
11756
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
11757
+ components.LoadingOverlay,
11758
+ {
11759
+ errorMessage: getErrorMessage(hydrateResult.error),
11760
+ isLoading: false
11761
+ }
11762
+ );
11763
+ }
11764
+ const { props: hydratedProps } = hydrateResult;
11765
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
11766
+ dashboardCorePlugins.IrisGridPanel,
11767
+ {
11768
+ ref,
11769
+ ...props,
11770
+ ...hydratedProps,
11771
+ panelState
11772
+ }
11773
+ );
11774
+ }
11775
+ );
11776
+ PivotPanel.displayName = "PivotPanel";
11747
11777
  const PivotPlugin = {
11748
11778
  name: "@deephaven/js-plugin-pivot",
11749
11779
  type: plugin.PluginType.WIDGET_PLUGIN,
@@ -11753,62 +11783,15 @@ const PivotPlugin = {
11753
11783
  icon: icons.dhTable,
11754
11784
  title: "Pivot Table"
11755
11785
  };
11756
- const urlAlphabet = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
11757
- let nanoid = (size = 21) => {
11758
- let id = "";
11759
- let bytes = crypto.getRandomValues(new Uint8Array(size |= 0));
11760
- while (size--) {
11761
- id += urlAlphabet[bytes[size] & 63];
11762
- }
11763
- return id;
11764
- };
11765
11786
  const VARIABLE_TYPE = "PivotTable";
11766
- const log = Log.module("@deephaven/js-plugin-pivot/DashboardPlugin");
11767
- function DashboardPlugin({
11768
- id,
11769
- layout,
11770
- registerComponent
11771
- }) {
11772
- const handlePanelOpen = React.useCallback(
11773
- ({
11774
- dragEvent,
11775
- fetch,
11776
- metadata = {},
11777
- panelId = nanoid(),
11778
- widget
11779
- }) => {
11780
- const { name, type } = widget;
11781
- if (type !== VARIABLE_TYPE) {
11782
- return;
11783
- }
11784
- log.info("Panel opened of type", type);
11785
- const config = {
11786
- type: "react-component",
11787
- component: PivotPanel.COMPONENT,
11788
- props: {
11789
- localDashboardId: id,
11790
- id: panelId,
11791
- metadata: {
11792
- ...metadata,
11793
- ...widget
11794
- },
11795
- fetch
11796
- },
11797
- title: name ?? void 0,
11798
- id: panelId
11799
- };
11800
- const { root: root2 } = layout;
11801
- dashboard.LayoutUtils.openComponent({ root: root2, config, dragEvent });
11802
- },
11803
- [id, layout]
11804
- );
11805
- React.useEffect(() => {
11806
- const cleanups = [registerComponent(PivotPanel.COMPONENT, PivotPanel)];
11807
- return () => {
11808
- cleanups.forEach((cleanup) => cleanup());
11809
- };
11810
- }, [registerComponent]);
11811
- dashboard.useListener(layout.eventHub, "PanelEvent.OPEN", handlePanelOpen);
11787
+ function DashboardPlugin(dashboardProps) {
11788
+ assertNotNull(PivotPanel.displayName);
11789
+ dashboard.useDashboardPanel({
11790
+ dashboardProps,
11791
+ componentName: PivotPanel.displayName,
11792
+ supportedTypes: VARIABLE_TYPE,
11793
+ component: PivotPanel
11794
+ });
11812
11795
  return null;
11813
11796
  }
11814
11797
  exports.DashboardPlugin = DashboardPlugin;