@xh/hoist 75.0.0-SNAPSHOT.1753731626581 → 75.0.0-SNAPSHOT.1754335298677
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 +6 -0
- package/build/types/cmp/dataview/DataViewModel.d.ts +1 -1
- package/build/types/cmp/grid/GridModel.d.ts +1 -1
- package/build/types/cmp/zoneGrid/ZoneGridModel.d.ts +1 -1
- package/build/types/data/Store.d.ts +14 -2
- package/build/types/data/cube/Cube.d.ts +14 -1
- package/data/Store.ts +85 -20
- package/data/cube/Cube.ts +21 -1
- package/package.json +1 -1
- package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -23,6 +23,12 @@
|
|
|
23
23
|
which `dimensions` are provided to the model.
|
|
24
24
|
* Added new `ClipboardButton.errorMessage` prop to customize or suppress a toast alert if the copy
|
|
25
25
|
operation fails. Set to `false` to fail silently (the behavior prior to this change).
|
|
26
|
+
* Added new `Cube.modifyRecordsAsync` for modifying individual field values in a local uncommitted
|
|
27
|
+
state. Additionally enhanced `Store.modifyRecords` to return a `StoreChangeLog` of updates.
|
|
28
|
+
|
|
29
|
+
### 🐞 Bug Fixes
|
|
30
|
+
* Fixed bugs where `Store.modifyRecords`, `Store.revertRecords` and `Store.revert` were not properly
|
|
31
|
+
handling changes to `SummaryRecords`.
|
|
26
32
|
|
|
27
33
|
### 🐞 Bug Fixes
|
|
28
34
|
|
|
@@ -110,7 +110,7 @@ export declare class DataViewModel extends HoistModel {
|
|
|
110
110
|
ensureSelectionVisibleAsync(): Promise<void>;
|
|
111
111
|
doLoadAsync(loadSpec: LoadSpec): Promise<any>;
|
|
112
112
|
loadData(rawData: any[], rawSummaryData?: PlainObject): void;
|
|
113
|
-
updateData(rawData: PlainObject[] | StoreTransaction):
|
|
113
|
+
updateData(rawData: PlainObject[] | StoreTransaction): import("@xh/hoist/data").StoreChangeLog;
|
|
114
114
|
clear(): void;
|
|
115
115
|
setGroupBy(colIds: Some<string>): void;
|
|
116
116
|
setSortBy(sorters: Some<GridSorterLike>): void;
|
|
@@ -450,7 +450,7 @@ export declare class GridModel extends HoistModel {
|
|
|
450
450
|
/** Load the underlying store. */
|
|
451
451
|
loadData(rawData: any[], rawSummaryData?: Some<PlainObject>): void;
|
|
452
452
|
/** Update the underlying store. */
|
|
453
|
-
updateData(rawData: PlainObject[] | StoreTransaction):
|
|
453
|
+
updateData(rawData: PlainObject[] | StoreTransaction): import("@xh/hoist/data").StoreChangeLog;
|
|
454
454
|
/** Clear the underlying store, removing all rows. */
|
|
455
455
|
clear(): void;
|
|
456
456
|
/** @param colConfigs - {@link Column} or {@link ColumnGroup} configs. */
|
|
@@ -243,7 +243,7 @@ export declare class ZoneGridModel extends HoistModel {
|
|
|
243
243
|
ensureSelectionVisibleAsync(): Promise<void>;
|
|
244
244
|
doLoadAsync(loadSpec: LoadSpec): Promise<any>;
|
|
245
245
|
loadData(rawData: any[], rawSummaryData?: Some<PlainObject>): void;
|
|
246
|
-
updateData(rawData: PlainObject[] | StoreTransaction):
|
|
246
|
+
updateData(rawData: PlainObject[] | StoreTransaction): import("@xh/hoist/data").StoreChangeLog;
|
|
247
247
|
clear(): void;
|
|
248
248
|
setGroupBy(colIds: Some<string>): void;
|
|
249
249
|
private createGridModel;
|
|
@@ -104,6 +104,16 @@ export interface StoreTransaction {
|
|
|
104
104
|
*/
|
|
105
105
|
rawSummaryData?: Some<PlainObject>;
|
|
106
106
|
}
|
|
107
|
+
/**
|
|
108
|
+
* Collection of changes made to a Store's RecordSet. Unlike `StoreTransaction` which is used to
|
|
109
|
+
* specify changes, this object is used to report the actual changes made in a single transaction.
|
|
110
|
+
*/
|
|
111
|
+
export interface StoreChangeLog {
|
|
112
|
+
update?: StoreRecord[];
|
|
113
|
+
add?: StoreRecord[];
|
|
114
|
+
remove?: StoreRecordId[];
|
|
115
|
+
summaryRecords?: StoreRecord[];
|
|
116
|
+
}
|
|
107
117
|
export interface ChildRawData {
|
|
108
118
|
/** ID of the pre-existing parent record. */
|
|
109
119
|
parentId: string;
|
|
@@ -197,7 +207,7 @@ export declare class Store extends HoistBase {
|
|
|
197
207
|
* into adds and updates, with updates determined by matching existing records by ID.
|
|
198
208
|
* @returns changes applied, or null if no record changes were made.
|
|
199
209
|
*/
|
|
200
|
-
updateData(rawData: PlainObject[] | StoreTransaction):
|
|
210
|
+
updateData(rawData: PlainObject[] | StoreTransaction): StoreChangeLog;
|
|
201
211
|
/**
|
|
202
212
|
* Re-runs the Filter on the current data. Applications only need to call this method if
|
|
203
213
|
* the state underlying the filter, other than the record data itself, has changed. Store will
|
|
@@ -242,8 +252,9 @@ export declare class Store extends HoistBase {
|
|
|
242
252
|
* Records in this Store. Each object in the list must have an `id` property identifying
|
|
243
253
|
* the StoreRecord to modify, plus any other properties with updated field values to apply,
|
|
244
254
|
* e.g. `{id: 4, quantity: 100}, {id: 5, quantity: 99, customer: 'bob'}`.
|
|
255
|
+
* @returns changes applied, or null if no record changes were made.
|
|
245
256
|
*/
|
|
246
|
-
modifyRecords(modifications: Some<PlainObject>):
|
|
257
|
+
modifyRecords(modifications: Some<PlainObject>): StoreChangeLog;
|
|
247
258
|
/**
|
|
248
259
|
* Revert all changes made to the specified Records since they were last committed.
|
|
249
260
|
*
|
|
@@ -396,4 +407,5 @@ export declare class Store extends HoistBase {
|
|
|
396
407
|
private createFieldMap;
|
|
397
408
|
private parseExperimental;
|
|
398
409
|
private parseIdSpec;
|
|
410
|
+
private revertSummaryRecords;
|
|
399
411
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { HoistBase, PlainObject } from '@xh/hoist/core';
|
|
1
|
+
import { HoistBase, PlainObject, Some } from '@xh/hoist/core';
|
|
2
2
|
import { CubeField, CubeFieldSpec } from './CubeField';
|
|
3
3
|
import { QueryConfig } from './Query';
|
|
4
4
|
import { View } from './View';
|
|
@@ -126,6 +126,19 @@ export declare class Cube extends HoistBase {
|
|
|
126
126
|
* @param infoUpdates - new key-value pairs to be applied to existing info on this cube.
|
|
127
127
|
*/
|
|
128
128
|
updateDataAsync(rawData: PlainObject[] | StoreTransaction, infoUpdates?: PlainObject): Promise<void>;
|
|
129
|
+
/**
|
|
130
|
+
* Similar to `updateDataAsync`, but intended for modifying individual field values in a local
|
|
131
|
+
* uncommitted state - i.e. when updating via an inline grid editor or similar control. Like
|
|
132
|
+
* `updateDataAsync`, this method will update its views asynchronously.
|
|
133
|
+
*
|
|
134
|
+
* This method largely delegates to {@link Store.modifyRecords} - see that method for more info.
|
|
135
|
+
*
|
|
136
|
+
* @param modifications - field-level modifications to apply to existing
|
|
137
|
+
* Records in this Cube. Each object in the list must have an `id` property identifying
|
|
138
|
+
* the StoreRecord to modify, plus any other properties with updated field values to apply,
|
|
139
|
+
* e.g. `{id: 4, quantity: 100}, {id: 5, quantity: 99, customer: 'bob'}`.
|
|
140
|
+
*/
|
|
141
|
+
modifyRecordsAsync(modifications: Some<PlainObject>): Promise<void>;
|
|
129
142
|
/** Clear any/all data and info from this Cube. */
|
|
130
143
|
clearAsync(): Promise<void>;
|
|
131
144
|
/**
|
package/data/Store.ts
CHANGED
|
@@ -24,7 +24,8 @@ import {
|
|
|
24
24
|
remove as lodashRemove,
|
|
25
25
|
uniq,
|
|
26
26
|
first,
|
|
27
|
-
some
|
|
27
|
+
some,
|
|
28
|
+
partition
|
|
28
29
|
} from 'lodash';
|
|
29
30
|
import {Field, FieldSpec} from './Field';
|
|
30
31
|
import {parseFilter} from './filter/Utils';
|
|
@@ -153,6 +154,17 @@ export interface StoreTransaction {
|
|
|
153
154
|
rawSummaryData?: Some<PlainObject>;
|
|
154
155
|
}
|
|
155
156
|
|
|
157
|
+
/**
|
|
158
|
+
* Collection of changes made to a Store's RecordSet. Unlike `StoreTransaction` which is used to
|
|
159
|
+
* specify changes, this object is used to report the actual changes made in a single transaction.
|
|
160
|
+
*/
|
|
161
|
+
export interface StoreChangeLog {
|
|
162
|
+
update?: StoreRecord[];
|
|
163
|
+
add?: StoreRecord[];
|
|
164
|
+
remove?: StoreRecordId[];
|
|
165
|
+
summaryRecords?: StoreRecord[];
|
|
166
|
+
}
|
|
167
|
+
|
|
156
168
|
export interface ChildRawData {
|
|
157
169
|
/** ID of the pre-existing parent record. */
|
|
158
170
|
parentId: string;
|
|
@@ -348,13 +360,13 @@ export class Store extends HoistBase {
|
|
|
348
360
|
*/
|
|
349
361
|
@action
|
|
350
362
|
@logWithDebug
|
|
351
|
-
updateData(rawData: PlainObject[] | StoreTransaction):
|
|
363
|
+
updateData(rawData: PlainObject[] | StoreTransaction): StoreChangeLog {
|
|
352
364
|
if (isEmpty(rawData)) return null;
|
|
353
365
|
|
|
354
|
-
const changeLog:
|
|
366
|
+
const changeLog: StoreChangeLog = {};
|
|
355
367
|
|
|
356
368
|
// Build a transaction object out of a flat list of adds and updates
|
|
357
|
-
let rawTransaction;
|
|
369
|
+
let rawTransaction: StoreTransaction;
|
|
358
370
|
if (isArray(rawData)) {
|
|
359
371
|
const update = [],
|
|
360
372
|
add = [];
|
|
@@ -381,7 +393,7 @@ export class Store extends HoistBase {
|
|
|
381
393
|
throwIf(!isEmpty(other), 'Unknown argument(s) passed to updateData().');
|
|
382
394
|
|
|
383
395
|
// 1) Pre-process updates and adds into Records
|
|
384
|
-
let updateRecs, addRecs
|
|
396
|
+
let updateRecs: StoreRecord[], addRecs: Map<StoreRecordId, StoreRecord>;
|
|
385
397
|
if (update) {
|
|
386
398
|
updateRecs = update.map(it => {
|
|
387
399
|
const recId = this.idSpec(it),
|
|
@@ -426,7 +438,11 @@ export class Store extends HoistBase {
|
|
|
426
438
|
}
|
|
427
439
|
|
|
428
440
|
// 3) Apply changes
|
|
429
|
-
let rsTransaction:
|
|
441
|
+
let rsTransaction: {
|
|
442
|
+
update?: StoreRecord[];
|
|
443
|
+
add?: StoreRecord[];
|
|
444
|
+
remove?: StoreRecordId[];
|
|
445
|
+
} = {};
|
|
430
446
|
if (!isEmpty(updateRecs)) rsTransaction.update = updateRecs;
|
|
431
447
|
if (!isEmpty(addRecs)) rsTransaction.add = Array.from(addRecs.values());
|
|
432
448
|
if (!isEmpty(remove)) rsTransaction.remove = remove;
|
|
@@ -545,13 +561,15 @@ export class Store extends HoistBase {
|
|
|
545
561
|
* Records in this Store. Each object in the list must have an `id` property identifying
|
|
546
562
|
* the StoreRecord to modify, plus any other properties with updated field values to apply,
|
|
547
563
|
* e.g. `{id: 4, quantity: 100}, {id: 5, quantity: 99, customer: 'bob'}`.
|
|
564
|
+
* @returns changes applied, or null if no record changes were made.
|
|
548
565
|
*/
|
|
549
566
|
@action
|
|
550
|
-
modifyRecords(modifications: Some<PlainObject>) {
|
|
567
|
+
modifyRecords(modifications: Some<PlainObject>): StoreChangeLog {
|
|
551
568
|
modifications = castArray(modifications);
|
|
552
569
|
if (isEmpty(modifications)) return;
|
|
553
570
|
|
|
554
|
-
|
|
571
|
+
// 1) Pre-process modifications into Records
|
|
572
|
+
const updateMap = new Map<StoreRecordId, StoreRecord>();
|
|
555
573
|
let hadDupes = false;
|
|
556
574
|
modifications.forEach(mod => {
|
|
557
575
|
let {id} = mod;
|
|
@@ -559,7 +577,7 @@ export class Store extends HoistBase {
|
|
|
559
577
|
// Ignore multiple updates for the same record - we are updating this Store in a
|
|
560
578
|
// transaction after processing all modifications, so this method is not currently setup
|
|
561
579
|
// to process more than one update for a given rec at a time.
|
|
562
|
-
if (
|
|
580
|
+
if (updateMap.has(id)) {
|
|
563
581
|
hadDupes = true;
|
|
564
582
|
return;
|
|
565
583
|
}
|
|
@@ -573,24 +591,45 @@ export class Store extends HoistBase {
|
|
|
573
591
|
data: updatedData,
|
|
574
592
|
parent: currentRec.parent,
|
|
575
593
|
store: currentRec.store,
|
|
576
|
-
committedData: currentRec.committedData
|
|
594
|
+
committedData: currentRec.committedData,
|
|
595
|
+
isSummary: currentRec.isSummary
|
|
577
596
|
});
|
|
578
597
|
|
|
579
598
|
if (!equal(currentRec.data, updatedRec.data)) {
|
|
580
|
-
|
|
599
|
+
updateMap.set(id, updatedRec);
|
|
581
600
|
}
|
|
582
601
|
});
|
|
583
602
|
|
|
584
|
-
if (isEmpty(
|
|
603
|
+
if (isEmpty(updateMap)) return null;
|
|
585
604
|
|
|
586
605
|
warnIf(
|
|
587
606
|
hadDupes,
|
|
588
607
|
'Store.modifyRecords() called with multiple updates for the same Records. Only the first modification for each StoreRecord was processed.'
|
|
589
608
|
);
|
|
590
609
|
|
|
591
|
-
|
|
610
|
+
const updateRecs = Array.from(updateMap.values()),
|
|
611
|
+
changeLog: StoreChangeLog = {};
|
|
592
612
|
|
|
593
|
-
|
|
613
|
+
// 2) Pre-process summary records, peeling them out of updates if needed
|
|
614
|
+
const {summaryRecords} = this;
|
|
615
|
+
let summaryUpdateRecs: StoreRecord[];
|
|
616
|
+
if (!isEmpty(summaryRecords)) {
|
|
617
|
+
summaryUpdateRecs = lodashRemove(updateRecs, ({id}) => some(summaryRecords, {id}));
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
if (!isEmpty(summaryUpdateRecs)) {
|
|
621
|
+
this.summaryRecords = summaryUpdateRecs;
|
|
622
|
+
changeLog.summaryRecords = this.summaryRecords;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
// 3) Apply changes
|
|
626
|
+
if (!isEmpty(updateRecs)) {
|
|
627
|
+
this._current = this._current.withTransaction({update: updateRecs});
|
|
628
|
+
changeLog.update = updateRecs;
|
|
629
|
+
this.rebuildFiltered();
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
return changeLog;
|
|
594
633
|
}
|
|
595
634
|
|
|
596
635
|
/**
|
|
@@ -606,13 +645,20 @@ export class Store extends HoistBase {
|
|
|
606
645
|
records = castArray(records);
|
|
607
646
|
if (isEmpty(records)) return;
|
|
608
647
|
|
|
609
|
-
const recs = records.map(it => (it instanceof StoreRecord ? it : this.getOrThrow(it)))
|
|
648
|
+
const recs = records.map(it => (it instanceof StoreRecord ? it : this.getOrThrow(it))),
|
|
649
|
+
[summaryRecsToRevert, recsToRevert] = partition(recs, 'isSummary');
|
|
610
650
|
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
651
|
+
if (!isEmpty(summaryRecsToRevert)) {
|
|
652
|
+
this.revertSummaryRecords(summaryRecsToRevert);
|
|
653
|
+
}
|
|
614
654
|
|
|
615
|
-
|
|
655
|
+
if (!isEmpty(recsToRevert)) {
|
|
656
|
+
this._current = this._current
|
|
657
|
+
.withTransaction({update: recsToRevert.map(r => this.getCommittedOrThrow(r.id))})
|
|
658
|
+
.normalize(this._committed);
|
|
659
|
+
|
|
660
|
+
this.rebuildFiltered();
|
|
661
|
+
}
|
|
616
662
|
}
|
|
617
663
|
|
|
618
664
|
/**
|
|
@@ -625,6 +671,7 @@ export class Store extends HoistBase {
|
|
|
625
671
|
@action
|
|
626
672
|
revert() {
|
|
627
673
|
this._current = this._committed;
|
|
674
|
+
if (this.summaryRecords) this.revertSummaryRecords(this.summaryRecords);
|
|
628
675
|
this.rebuildFiltered();
|
|
629
676
|
}
|
|
630
677
|
|
|
@@ -705,7 +752,7 @@ export class Store extends HoistBase {
|
|
|
705
752
|
/** True if the store has changes which need to be committed. */
|
|
706
753
|
@computed
|
|
707
754
|
get isDirty(): boolean {
|
|
708
|
-
return this._current !== this._committed;
|
|
755
|
+
return this._current !== this._committed || this.summaryRecords?.some(it => it.isModified);
|
|
709
756
|
}
|
|
710
757
|
|
|
711
758
|
/** Alias for {@link Store.isDirty} */
|
|
@@ -1082,6 +1129,24 @@ export class Store extends HoistBase {
|
|
|
1082
1129
|
'idSpec should be either a name of a field, or a function to generate an id.'
|
|
1083
1130
|
);
|
|
1084
1131
|
}
|
|
1132
|
+
|
|
1133
|
+
@action
|
|
1134
|
+
private revertSummaryRecords(records: StoreRecord[]) {
|
|
1135
|
+
this.summaryRecords = this.summaryRecords.map(summaryRec => {
|
|
1136
|
+
const recToRevert = records.find(it => it.id === summaryRec.id);
|
|
1137
|
+
if (!recToRevert) return summaryRec;
|
|
1138
|
+
|
|
1139
|
+
const ret = new StoreRecord({
|
|
1140
|
+
id: recToRevert.id,
|
|
1141
|
+
raw: recToRevert.raw,
|
|
1142
|
+
data: {...recToRevert.committedData},
|
|
1143
|
+
store: this,
|
|
1144
|
+
isSummary: true
|
|
1145
|
+
});
|
|
1146
|
+
ret.finalize();
|
|
1147
|
+
return ret;
|
|
1148
|
+
});
|
|
1149
|
+
}
|
|
1085
1150
|
}
|
|
1086
1151
|
|
|
1087
1152
|
//---------------------------------------------------------------------
|
package/data/cube/Cube.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Copyright © 2025 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import {HoistBase, managed, PlainObject} from '@xh/hoist/core';
|
|
8
|
+
import {HoistBase, managed, PlainObject, Some} from '@xh/hoist/core';
|
|
9
9
|
import {action, makeObservable, observable} from '@xh/hoist/mobx';
|
|
10
10
|
import {forEachAsync} from '@xh/hoist/utils/async';
|
|
11
11
|
import {CubeField, CubeFieldSpec} from './CubeField';
|
|
@@ -244,6 +244,26 @@ export class Cube extends HoistBase {
|
|
|
244
244
|
}
|
|
245
245
|
}
|
|
246
246
|
|
|
247
|
+
/**
|
|
248
|
+
* Similar to `updateDataAsync`, but intended for modifying individual field values in a local
|
|
249
|
+
* uncommitted state - i.e. when updating via an inline grid editor or similar control. Like
|
|
250
|
+
* `updateDataAsync`, this method will update its views asynchronously.
|
|
251
|
+
*
|
|
252
|
+
* This method largely delegates to {@link Store.modifyRecords} - see that method for more info.
|
|
253
|
+
*
|
|
254
|
+
* @param modifications - field-level modifications to apply to existing
|
|
255
|
+
* Records in this Cube. Each object in the list must have an `id` property identifying
|
|
256
|
+
* the StoreRecord to modify, plus any other properties with updated field values to apply,
|
|
257
|
+
* e.g. `{id: 4, quantity: 100}, {id: 5, quantity: 99, customer: 'bob'}`.
|
|
258
|
+
*/
|
|
259
|
+
async modifyRecordsAsync(modifications: Some<PlainObject>): Promise<void> {
|
|
260
|
+
const changeLog = this.store.modifyRecords(modifications);
|
|
261
|
+
|
|
262
|
+
if (changeLog) {
|
|
263
|
+
await forEachAsync(this._connectedViews, v => v.noteCubeUpdated(changeLog));
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
247
267
|
/** Clear any/all data and info from this Cube. */
|
|
248
268
|
async clearAsync() {
|
|
249
269
|
await this.loadDataAsync([]);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xh/hoist",
|
|
3
|
-
"version": "75.0.0-SNAPSHOT.
|
|
3
|
+
"version": "75.0.0-SNAPSHOT.1754335298677",
|
|
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",
|