@xh/hoist 63.0.2 → 63.1.0

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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 63.1.0 - 2024-04-23
4
+
5
+ ### 🎁 New Features
6
+
7
+ * `Store` now supports multiple `summaryRecords`.
8
+
9
+ ## 63.0.3 - 2024-04-16
10
+
11
+ ### 🐞 Bug Fixes
12
+
13
+ * Import BPs default datetime package css in addition to datetime2 css to support BP datetime components.
14
+
3
15
  ## 63.0.2 - 2024-04-16
4
16
 
5
17
  ### 🐞 Bug Fixes
package/cmp/grid/Grid.ts CHANGED
@@ -326,7 +326,7 @@ export class GridLocalModel extends HoistModel {
326
326
  const {model} = this,
327
327
  {store} = model;
328
328
  return {
329
- track: () => [model.isReady, store._filtered, model.showSummary, store.summaryRecord],
329
+ track: () => [model.isReady, store._filtered, model.showSummary, store.summaryRecords],
330
330
  run: () => {
331
331
  if (model.isReady) this.syncData();
332
332
  }
@@ -583,11 +583,11 @@ export class GridLocalModel extends HoistModel {
583
583
  pinnedTopRowData = agGridModel.getPinnedTopRowData().filter(filterSummaryFn),
584
584
  pinnedBottomRowData = agGridModel.getPinnedBottomRowData().filter(filterSummaryFn);
585
585
 
586
- if (showSummary && store.summaryRecord) {
586
+ if (showSummary && !isEmpty(store.summaryRecords)) {
587
587
  if (showSummary === 'bottom') {
588
- pinnedBottomRowData.push(store.summaryRecord);
588
+ pinnedBottomRowData.push(...store.summaryRecords);
589
589
  } else {
590
- pinnedTopRowData.unshift(store.summaryRecord);
590
+ pinnedTopRowData.unshift(...store.summaryRecords);
591
591
  }
592
592
  }
593
593
 
@@ -123,7 +123,7 @@ export interface GridConfig {
123
123
  /** True if grid is a tree grid (default false). */
124
124
  treeMode?: boolean;
125
125
 
126
- /** Location for a docked summary row. Requires `store.SummaryRecord` to be populated. */
126
+ /** Location for docked summary row(s). Requires `store.summaryRecords` to be populated. */
127
127
  showSummary?: boolean | VSide;
128
128
 
129
129
  /** Specification of selection behavior. Defaults to 'single' (desktop) and 'disabled' (mobile) */
@@ -1038,7 +1038,7 @@ export class GridModel extends HoistModel {
1038
1038
  }
1039
1039
 
1040
1040
  /** Load the underlying store. */
1041
- loadData(rawData: any[], rawSummaryData?: PlainObject) {
1041
+ loadData(rawData: any[], rawSummaryData?: Some<PlainObject>) {
1042
1042
  return this.store.loadData(rawData, rawSummaryData);
1043
1043
  }
1044
1044
 
@@ -102,7 +102,7 @@ export interface ZoneGridConfig {
102
102
  /** True if grid is a tree grid (default false). */
103
103
  treeMode?: boolean;
104
104
 
105
- /** Location for a docked summary row. Requires `store.SummaryRecord` to be populated. */
105
+ /** Location for docked summary row(s). Requires `store.summaryRecords` to be populated. */
106
106
  showSummary?: boolean | VSide;
107
107
 
108
108
  /** Specification of selection behavior. Defaults to 'single' (desktop) and 'disabled' (mobile) */
@@ -520,7 +520,7 @@ export class ZoneGridModel extends HoistModel {
520
520
  return this.gridModel.doLoadAsync(loadSpec);
521
521
  }
522
522
 
523
- loadData(rawData: any[], rawSummaryData?: PlainObject) {
523
+ loadData(rawData: any[], rawSummaryData?: Some<PlainObject>) {
524
524
  return this.gridModel.loadData(rawData, rawSummaryData);
525
525
  }
526
526
 
package/data/Store.ts CHANGED
@@ -21,7 +21,9 @@ import {
21
21
  isString,
22
22
  values,
23
23
  remove as lodashRemove,
24
- uniq
24
+ uniq,
25
+ first,
26
+ some
25
27
  } from 'lodash';
26
28
  import {Field, FieldSpec} from './Field';
27
29
  import {parseFilter} from './filter/Utils';
@@ -143,11 +145,11 @@ export interface StoreTransaction {
143
145
  remove?: StoreRecordId[];
144
146
 
145
147
  /**
146
- * Update to the dedicated summary row for this store. If the store has its
148
+ * Update to the dedicated summary record(s) for this store. If the store has its
147
149
  * `loadRootAsSummary` flag set to true, the summary record should instead be provided via the
148
150
  * `update` property.
149
151
  */
150
- rawSummaryData?: PlainObject;
152
+ rawSummaryData?: Some<PlainObject>;
151
153
  }
152
154
 
153
155
  export interface ChildRawData {
@@ -197,9 +199,9 @@ export class Store extends HoistBase {
197
199
  @observable
198
200
  lastLoaded: number = null;
199
201
 
200
- /** Record containing summary data. */
202
+ /** Records containing summary data. */
201
203
  @observable.ref
202
- summaryRecord: StoreRecord = null;
204
+ summaryRecords: StoreRecord[] = null;
203
205
 
204
206
  /** @internal - used internally by any StoreFilterField bound to this store. */
205
207
  @observable
@@ -290,23 +292,25 @@ export class Store extends HoistBase {
290
292
  * created with its `loadRootAsSummary` flag set to true.
291
293
  *
292
294
  * @param rawData - source data to load
293
- * @param rawSummaryData - source data for an optional summary record, representing
294
- * a custom aggregation to show as a "grand total" for the dataset, if desired.
295
+ * @param rawSummaryData - source data for optional summary record(s), representing
296
+ * custom aggregations for the dataset, if desired.
295
297
  */
296
298
  @action
297
299
  @logWithDebug
298
- loadData(rawData: PlainObject[], rawSummaryData?: PlainObject) {
300
+ loadData(rawData: PlainObject[], rawSummaryData?: Some<PlainObject>) {
299
301
  // Extract rootSummary if loading non-empty data[] (i.e. not clearing) and loadRootAsSummary
300
302
  if (rawData.length !== 0 && this.loadRootAsSummary) {
301
303
  throwIf(
302
- rawData.length !== 1 || rawSummaryData,
304
+ rawData.length !== 1 || !isEmpty(rawSummaryData),
303
305
  'Incorrect call to loadData with loadRootAsSummary=true. Summary data should be in a single root node with top-level row data as its children.'
304
306
  );
305
307
  rawSummaryData = rawData[0];
306
308
  rawData = rawData[0].children ?? [];
307
309
  }
308
310
 
309
- this.summaryRecord = rawSummaryData ? this.createRecord(rawSummaryData, null, true) : null;
311
+ this.summaryRecords = rawSummaryData
312
+ ? castArray(rawSummaryData).map(it => this.createRecord(it, null, true))
313
+ : null;
310
314
 
311
315
  const records = this.createRecords(rawData, null);
312
316
  this._committed = this._current = this._committed.withNewRecords(records);
@@ -381,7 +385,7 @@ export class Store extends HoistBase {
381
385
  'In order to update grid data, records must have stable ids. Note: XH.genId() will not provide such ids.'
382
386
  ),
383
387
  parent = rec.parent,
384
- isSummary = recId === this.summaryRecord?.id;
388
+ isSummary = some(this.summaryRecords, {id: recId});
385
389
  return this.createRecord(it, parent, isSummary);
386
390
  });
387
391
  }
@@ -398,20 +402,22 @@ export class Store extends HoistBase {
398
402
  });
399
403
  }
400
404
 
401
- // 2) Pre-process summary record, peeling it out of updates if needed
402
- const {summaryRecord} = this;
403
- let summaryUpdateRec;
404
- if (summaryRecord) {
405
- [summaryUpdateRec] = lodashRemove(updateRecs, {id: summaryRecord.id});
405
+ // 2) Pre-process summary records, peeling them out of updates if needed
406
+ const {summaryRecords} = this;
407
+ let summaryUpdateRecs: StoreRecord[];
408
+ if (!isEmpty(summaryRecords)) {
409
+ summaryUpdateRecs = lodashRemove(updateRecs, ({id}) => some(summaryRecords, {id}));
406
410
  }
407
411
 
408
- if (!summaryUpdateRec && rawSummaryData) {
409
- summaryUpdateRec = this.createRecord(rawSummaryData, null, true);
412
+ if (isEmpty(summaryUpdateRecs) && rawSummaryData) {
413
+ summaryUpdateRecs = castArray(rawSummaryData).map(it =>
414
+ this.createRecord(it, null, true)
415
+ );
410
416
  }
411
417
 
412
- if (summaryUpdateRec) {
413
- this.summaryRecord = summaryUpdateRec;
414
- changeLog.summaryRecord = this.summaryRecord;
418
+ if (!isEmpty(summaryUpdateRecs)) {
419
+ this.summaryRecords = summaryUpdateRecs;
420
+ changeLog.summaryRecords = this.summaryRecords;
415
421
  }
416
422
 
417
423
  // 3) Apply changes
@@ -672,6 +678,15 @@ export class Store extends HoistBase {
672
678
  return this._current.rootList;
673
679
  }
674
680
 
681
+ /** Record containing summary data. */
682
+ get summaryRecord(): StoreRecord {
683
+ throwIf(
684
+ this.summaryRecords.length > 1,
685
+ 'Store has multiple summary records. Use Store.summaryRecords.'
686
+ );
687
+ return first(this.summaryRecords);
688
+ }
689
+
675
690
  /** True if the store has changes which need to be committed. */
676
691
  @computed
677
692
  get isModified(): boolean {
@@ -787,7 +802,8 @@ export class Store extends HoistBase {
787
802
  */
788
803
  getById(id: StoreRecordId, respectFilter: boolean = false): StoreRecord {
789
804
  if (isNil(id)) return null;
790
- if (id === this.summaryRecord?.id) return this.summaryRecord;
805
+ const summaryRecord = this.summaryRecords?.find(it => it.id === id);
806
+ if (summaryRecord) return summaryRecord;
791
807
 
792
808
  const rs = respectFilter ? this._filtered : this._current;
793
809
  return rs.getById(id);
@@ -892,7 +908,7 @@ export class Store extends HoistBase {
892
908
  @action
893
909
  private resetRecords() {
894
910
  this._committed = this._current = this._filtered = new RecordSet(this);
895
- this.summaryRecord = null;
911
+ this.summaryRecords = null;
896
912
  }
897
913
 
898
914
  private parseFields(fields: any[], defaults: any): Field[] {
@@ -965,7 +981,7 @@ export class Store extends HoistBase {
965
981
  {id} = rec;
966
982
 
967
983
  throwIf(
968
- recordMap.has(id) || this.summaryRecord?.id === id,
984
+ recordMap.has(id) || this.summaryRecords?.some(it => it.id === id),
969
985
  `ID ${id} is not unique. Use the 'Store.idSpec' config to resolve a unique ID for each record.`
970
986
  );
971
987
 
@@ -19,6 +19,7 @@ import {
19
19
  useHotkeys
20
20
  } from '@blueprintjs/core';
21
21
  import '@blueprintjs/core/lib/css/blueprint.css';
22
+ import '@blueprintjs/datetime/lib/css/blueprint-datetime.css';
22
23
  import '@blueprintjs/datetime2/lib/css/blueprint-datetime2.css';
23
24
  import {elementFactory} from '@xh/hoist/core';
24
25
  import './styles.scss';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xh/hoist",
3
- "version": "63.0.2",
3
+ "version": "63.1.0",
4
4
  "description": "Hoist add-on for building and deploying React Applications.",
5
5
  "repository": "github:xh/hoist-react",
6
6
  "homepage": "https://xh.io",
@@ -139,8 +139,8 @@ export class GridAutosizeService extends HoistService {
139
139
  }
140
140
 
141
141
  // Ensure the summary record is always included, since it is likely to contain the largest values.
142
- if (gridModel.showSummary && store.summaryRecord) {
143
- ret.push(store.summaryRecord);
142
+ if (gridModel.showSummary && !isEmpty(store.summaryRecords)) {
143
+ ret.push(...store.summaryRecords);
144
144
  }
145
145
  return ret;
146
146
  }
@@ -75,7 +75,7 @@ export class GridExportService extends HoistService {
75
75
 
76
76
  const config = XH.configService.get('xhExportConfig', {}),
77
77
  exportColumns = this.getExportableColumns(gridModel, columns),
78
- summaryRecord = gridModel.store.summaryRecord,
78
+ summaryRecords = gridModel.store.summaryRecords,
79
79
  records = gridModel.store.rootRecords,
80
80
  meta = this.getColumnMetadata(exportColumns);
81
81
 
@@ -84,18 +84,17 @@ export class GridExportService extends HoistService {
84
84
  return;
85
85
  }
86
86
 
87
- // If the grid includes a summary row, add it to the export payload as a root-level node.
88
- const rows =
89
- gridModel.showSummary && summaryRecord
90
- ? [
91
- this.getHeaderRow(exportColumns, type, gridModel),
92
- this.getRecordRow(gridModel, summaryRecord, exportColumns, type, 0),
93
- ...this.getRecordRowsRecursive(gridModel, records, exportColumns, type, 1)
94
- ]
95
- : [
96
- this.getHeaderRow(exportColumns, type, gridModel),
97
- ...this.getRecordRowsRecursive(gridModel, records, exportColumns, type, 0)
98
- ];
87
+ // If the grid includes summary rows, add them to the export payload as root-level nodes.
88
+ const rows = [
89
+ this.getHeaderRow(exportColumns, type, gridModel),
90
+ ...(gridModel.showSummary && !isEmpty(summaryRecords)
91
+ ? summaryRecords.map(summaryRecord =>
92
+ this.getRecordRow(gridModel, summaryRecord, exportColumns, type, 0)
93
+ )
94
+ : []),
95
+
96
+ ...this.getRecordRowsRecursive(gridModel, records, exportColumns, type, 1)
97
+ ];
99
98
 
100
99
  // Show separate 'started' toasts for larger (i.e. slower) exports.
101
100
  let startToast = null,