tinybase 1.2.2 → 1.3.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/lib/debug/store.d.ts +301 -1
- package/lib/debug/store.js +134 -106
- package/lib/debug/tinybase.js +134 -106
- package/lib/store.d.ts +301 -1
- package/lib/store.js +1 -1
- package/lib/store.js.gz +0 -0
- package/lib/tinybase.js +1 -1
- package/lib/tinybase.js.gz +0 -0
- package/lib/umd/store.js +1 -1
- package/lib/umd/store.js.gz +0 -0
- package/lib/umd/tinybase.js +1 -1
- package/lib/umd/tinybase.js.gz +0 -0
- package/package.json +14 -14
- package/readme.md +2 -2
package/lib/debug/store.d.ts
CHANGED
|
@@ -176,6 +176,32 @@ export type MapCell = (cell: CellOrUndefined) => Cell;
|
|
|
176
176
|
*/
|
|
177
177
|
export type GetCell = (cellId: Id) => CellOrUndefined;
|
|
178
178
|
|
|
179
|
+
/**
|
|
180
|
+
* The TransactionListener type describes a function that is used to listen to
|
|
181
|
+
* the completion of a transaction for the Store.
|
|
182
|
+
*
|
|
183
|
+
* A TransactionListener is provided when using the
|
|
184
|
+
* addWillFinishTransactionListener and addDidFinishTransactionListener methods.
|
|
185
|
+
* See those methods for specific examples.
|
|
186
|
+
*
|
|
187
|
+
* When called, a TransactionListener is simply given a reference to the Store
|
|
188
|
+
* and a boolean to indicate whether Cell values have been touched during the
|
|
189
|
+
* transaction. The latter flag is intended as a hint about whether non-mutating
|
|
190
|
+
* listeners might be being called at the end of the transaction.
|
|
191
|
+
*
|
|
192
|
+
* Here, 'touched' means that Cell values have either been changed, or changed
|
|
193
|
+
* and then changed back to its original value during the transaction. The
|
|
194
|
+
* exception is a transaction that has been rolled back, for which the value of
|
|
195
|
+
* `cellsTouched` in the listener will be `false` because all changes have been
|
|
196
|
+
* reverted.
|
|
197
|
+
*
|
|
198
|
+
* @param store A reference to the Store that is completing a transaction.
|
|
199
|
+
* @param cellsTouched Whether Cell values have been touched during the
|
|
200
|
+
* transaction.
|
|
201
|
+
* @category Listener
|
|
202
|
+
*/
|
|
203
|
+
export type TransactionListener = (store: Store, cellsTouched: boolean) => void;
|
|
204
|
+
|
|
179
205
|
/**
|
|
180
206
|
* The TablesListener type describes a function that is used to listen to
|
|
181
207
|
* changes to the whole Store.
|
|
@@ -566,6 +592,14 @@ export type StoreListenerStats = {
|
|
|
566
592
|
* The number of CellListeners registered with the Store.
|
|
567
593
|
*/
|
|
568
594
|
cell?: number;
|
|
595
|
+
/**
|
|
596
|
+
* The number of InvalidCellListeners registered with the Store.
|
|
597
|
+
*/
|
|
598
|
+
invalidCell?: number;
|
|
599
|
+
/**
|
|
600
|
+
* The number of TransactionListeners registered with the Store.
|
|
601
|
+
*/
|
|
602
|
+
transaction?: number;
|
|
569
603
|
};
|
|
570
604
|
|
|
571
605
|
/**
|
|
@@ -1648,7 +1682,7 @@ export interface Store {
|
|
|
1648
1682
|
|
|
1649
1683
|
/**
|
|
1650
1684
|
* The transaction method takes a function that makes multiple mutations to
|
|
1651
|
-
* the
|
|
1685
|
+
* the Store, buffering all calls to the relevant listeners until it
|
|
1652
1686
|
* completes.
|
|
1653
1687
|
*
|
|
1654
1688
|
* This method is useful for making bulk changes to the data in a Store, and
|
|
@@ -1767,6 +1801,135 @@ export interface Store {
|
|
|
1767
1801
|
) => boolean,
|
|
1768
1802
|
): Return;
|
|
1769
1803
|
|
|
1804
|
+
/**
|
|
1805
|
+
* The startTransaction allows you to explicitly start a transaction that will
|
|
1806
|
+
* make multiple mutations to the Store, buffering all calls to the relevant
|
|
1807
|
+
* listeners until it completes when you call the finishTransaction method.
|
|
1808
|
+
*
|
|
1809
|
+
* Transactions are useful for making bulk changes to the data in a Store, and
|
|
1810
|
+
* when you don't want listeners to be called as you make each change. Changes
|
|
1811
|
+
* are made silently during the transaction, and listeners relevant to the
|
|
1812
|
+
* changes you have made will instead only be called when the whole
|
|
1813
|
+
* transaction is complete.
|
|
1814
|
+
*
|
|
1815
|
+
* Generally it is preferable to use the transaction method to wrap a block of
|
|
1816
|
+
* code as a transaction. It simply calls both the startTransaction and
|
|
1817
|
+
* finishTransaction methods for you. See that method for several transaction
|
|
1818
|
+
* examples.
|
|
1819
|
+
*
|
|
1820
|
+
* Use this startTransaction method when you have a more 'open-ended'
|
|
1821
|
+
* transaction, such as one containing mutations triggered from other events
|
|
1822
|
+
* that are asynchronous or not occurring inline to your code. You must
|
|
1823
|
+
* remember to also call the finishTransaction method explicitly when it is
|
|
1824
|
+
* done, of course.
|
|
1825
|
+
*
|
|
1826
|
+
* @returns A reference to the Store.
|
|
1827
|
+
* @example
|
|
1828
|
+
* This example makes changes to two Cells, first outside, and secondly
|
|
1829
|
+
* within, a transaction that is explicitly started and finished. In the
|
|
1830
|
+
* second case, the Row listener is only called once.
|
|
1831
|
+
*
|
|
1832
|
+
* ```js
|
|
1833
|
+
* const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
|
|
1834
|
+
* store.addRowListener('pets', 'fido', () => console.log('Fido changed'));
|
|
1835
|
+
*
|
|
1836
|
+
* store.setCell('pets', 'fido', 'color', 'brown');
|
|
1837
|
+
* store.setCell('pets', 'fido', 'sold', false);
|
|
1838
|
+
* // -> 'Fido changed'
|
|
1839
|
+
* // -> 'Fido changed'
|
|
1840
|
+
*
|
|
1841
|
+
* store.startTransaction();
|
|
1842
|
+
* store.setCell('pets', 'fido', 'color', 'walnut');
|
|
1843
|
+
* store.setCell('pets', 'fido', 'sold', true);
|
|
1844
|
+
* store.finishTransaction();
|
|
1845
|
+
* // -> 'Fido changed'
|
|
1846
|
+
* ```
|
|
1847
|
+
* @category Transaction
|
|
1848
|
+
*/
|
|
1849
|
+
startTransaction(): Store;
|
|
1850
|
+
|
|
1851
|
+
/**
|
|
1852
|
+
* The finishTransaction allows you to explicitly finish a transaction that
|
|
1853
|
+
* has made multiple mutations to the Store, triggering all calls to the
|
|
1854
|
+
* relevant listeners.
|
|
1855
|
+
*
|
|
1856
|
+
* Transactions are useful for making bulk changes to the data in a Store, and
|
|
1857
|
+
* when you don't want listeners to be called as you make each change. Changes
|
|
1858
|
+
* are made silently during the transaction, and listeners relevant to the
|
|
1859
|
+
* changes you have made will instead only be called when the whole
|
|
1860
|
+
* transaction is complete.
|
|
1861
|
+
*
|
|
1862
|
+
* Generally it is preferable to use the transaction method to wrap a block of
|
|
1863
|
+
* code as a transaction. It simply calls both the startTransaction and
|
|
1864
|
+
* finishTransaction methods for you. See that method for several transaction
|
|
1865
|
+
* examples.
|
|
1866
|
+
*
|
|
1867
|
+
* Use this finishTransaction method when you have a more 'open-ended'
|
|
1868
|
+
* transaction, such as one containing mutations triggered from other events
|
|
1869
|
+
* that are asynchronous or not occurring inline to your code. There must have
|
|
1870
|
+
* been a corresponding startTransaction method that this completes, of
|
|
1871
|
+
* course, otherwise this function has no effect.
|
|
1872
|
+
*
|
|
1873
|
+
* @param doRollback An optional callback that should return `true` if you
|
|
1874
|
+
* want to rollback the transaction at the end.
|
|
1875
|
+
* @returns A reference to the Store.
|
|
1876
|
+
* @example
|
|
1877
|
+
* This example makes changes to two Cells, first outside, and secondly
|
|
1878
|
+
* within, a transaction that is explicitly started and finished. In the
|
|
1879
|
+
* second case, the Row listener is only called once.
|
|
1880
|
+
*
|
|
1881
|
+
* ```js
|
|
1882
|
+
* const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
|
|
1883
|
+
* store.addRowListener('pets', 'fido', () => console.log('Fido changed'));
|
|
1884
|
+
*
|
|
1885
|
+
* store.setCell('pets', 'fido', 'color', 'brown');
|
|
1886
|
+
* store.setCell('pets', 'fido', 'sold', false);
|
|
1887
|
+
* // -> 'Fido changed'
|
|
1888
|
+
* // -> 'Fido changed'
|
|
1889
|
+
*
|
|
1890
|
+
* store.startTransaction();
|
|
1891
|
+
* store.setCell('pets', 'fido', 'color', 'walnut');
|
|
1892
|
+
* store.setCell('pets', 'fido', 'sold', true);
|
|
1893
|
+
* store.finishTransaction();
|
|
1894
|
+
* // -> 'Fido changed'
|
|
1895
|
+
* ```
|
|
1896
|
+
* @example
|
|
1897
|
+
* This example makes multiple changes to the Store, including some attempts
|
|
1898
|
+
* to update a Cell with invalid values. The `doRollback` callback receives
|
|
1899
|
+
* information about the changes and invalid attempts, and then judges that
|
|
1900
|
+
* the transaction should be rolled back to its original state.
|
|
1901
|
+
*
|
|
1902
|
+
* ```js
|
|
1903
|
+
* const store = createStore().setTables({
|
|
1904
|
+
* pets: {fido: {species: 'dog', color: 'brown'}},
|
|
1905
|
+
* });
|
|
1906
|
+
*
|
|
1907
|
+
* store.startTransaction();
|
|
1908
|
+
* store.setCell('pets', 'fido', 'color', 'black');
|
|
1909
|
+
* store.setCell('pets', 'fido', 'eyes', ['left', 'right']);
|
|
1910
|
+
* store.setCell('pets', 'fido', 'info', {sold: null});
|
|
1911
|
+
* store.finishTransaction((changedCells, invalidCells) => {
|
|
1912
|
+
* console.log(store.getTables());
|
|
1913
|
+
* // -> {pets: {fido: {species: 'dog', color: 'black'}}}
|
|
1914
|
+
* console.log(changedCells);
|
|
1915
|
+
* // -> {pets: {fido: {color: ['brown', 'black']}}}
|
|
1916
|
+
* console.log(invalidCells);
|
|
1917
|
+
* // -> {pets: {fido: {eyes: [['left', 'right']], info: [{sold: null}]}}}
|
|
1918
|
+
* return invalidCells['pets'] != null;
|
|
1919
|
+
* });
|
|
1920
|
+
*
|
|
1921
|
+
* console.log(store.getTables());
|
|
1922
|
+
* // -> {pets: {fido: {species: 'dog', color: 'brown'}}}
|
|
1923
|
+
* ```
|
|
1924
|
+
* @category Transaction
|
|
1925
|
+
*/
|
|
1926
|
+
finishTransaction(
|
|
1927
|
+
doRollback?: (
|
|
1928
|
+
changedCells: ChangedCells,
|
|
1929
|
+
invalidCells: InvalidCells,
|
|
1930
|
+
) => boolean,
|
|
1931
|
+
): Store;
|
|
1932
|
+
|
|
1770
1933
|
/**
|
|
1771
1934
|
* The forEachTable method takes a function that it will then call for each
|
|
1772
1935
|
* Table in the Store.
|
|
@@ -2742,6 +2905,143 @@ export interface Store {
|
|
|
2742
2905
|
mutator?: boolean,
|
|
2743
2906
|
): Id;
|
|
2744
2907
|
|
|
2908
|
+
/**
|
|
2909
|
+
* The addWillFinishTransactionListener method registers a listener function
|
|
2910
|
+
* with the Store that will be called just before other non-mutating listeners
|
|
2911
|
+
* are called at the end of the transaction.
|
|
2912
|
+
*
|
|
2913
|
+
* This is useful if you need to know that a set of listeners are about to be
|
|
2914
|
+
* called at the end of a transaction, perhaps to batch _their_ consequences
|
|
2915
|
+
* together.
|
|
2916
|
+
*
|
|
2917
|
+
* The provided TransactionListener will receive a reference to the Store and
|
|
2918
|
+
* a boolean to indicate whether Cell values have been touched during the
|
|
2919
|
+
* transaction. The latter flag is intended as a hint about whether
|
|
2920
|
+
* non-mutating listeners might be being called at the end of the transaction.
|
|
2921
|
+
*
|
|
2922
|
+
* Here, 'touched' means that Cell values have either been changed, or changed
|
|
2923
|
+
* and then changed back to its original value during the transaction. The
|
|
2924
|
+
* exception is a transaction that has been rolled back, for which the value
|
|
2925
|
+
* of `cellsTouched` in the listener will be `false` because all changes have
|
|
2926
|
+
* been reverted.
|
|
2927
|
+
*
|
|
2928
|
+
* @returns A unique Id for the listener that can later be used to remove it.
|
|
2929
|
+
* @example
|
|
2930
|
+
* This example registers a listener that is called at the end of the
|
|
2931
|
+
* transaction, just before its listeners will be called. The transactions
|
|
2932
|
+
* shown here variously change, touch, and rollback cells, demonstrating how
|
|
2933
|
+
* the `cellsTouched` parameter in the listener works.
|
|
2934
|
+
*
|
|
2935
|
+
* ```js
|
|
2936
|
+
* const store = createStore().setTables({
|
|
2937
|
+
* pets: {fido: {species: 'dog', color: 'brown'}},
|
|
2938
|
+
* });
|
|
2939
|
+
* const listenerId = store.addWillFinishTransactionListener(
|
|
2940
|
+
* (store, cellsTouched) => console.log(`Cells touched: ${cellsTouched}`),
|
|
2941
|
+
* );
|
|
2942
|
+
* const listenerId2 = store.addTablesListener(() =>
|
|
2943
|
+
* console.log('Tables changed'),
|
|
2944
|
+
* );
|
|
2945
|
+
*
|
|
2946
|
+
* store.transaction(() => store.setCell('pets', 'fido', 'color', 'brown'));
|
|
2947
|
+
* // -> 'Cells touched: false'
|
|
2948
|
+
*
|
|
2949
|
+
* store.transaction(() => store.setCell('pets', 'fido', 'color', 'walnut'));
|
|
2950
|
+
* // -> 'Cells touched: true'
|
|
2951
|
+
* // -> 'Tables changed'
|
|
2952
|
+
*
|
|
2953
|
+
* store.transaction(() => {
|
|
2954
|
+
* store.setRow('pets', 'felix', {species: 'cat'});
|
|
2955
|
+
* store.delRow('pets', 'felix');
|
|
2956
|
+
* });
|
|
2957
|
+
* // -> 'Cells touched: true'
|
|
2958
|
+
*
|
|
2959
|
+
* store.transaction(
|
|
2960
|
+
* () => store.setRow('pets', 'fido', {species: 'dog'}),
|
|
2961
|
+
* () => true,
|
|
2962
|
+
* );
|
|
2963
|
+
* // -> 'Cells touched: false'
|
|
2964
|
+
* // Transaction was rolled back.
|
|
2965
|
+
*
|
|
2966
|
+
* store.callListener(listenerId);
|
|
2967
|
+
* // -> 'Cells touched: undefined'
|
|
2968
|
+
* // It is meaningless to call this listener directly.
|
|
2969
|
+
*
|
|
2970
|
+
* store.delListener(listenerId).delListener(listenerId2);
|
|
2971
|
+
* ```
|
|
2972
|
+
* @category Listener
|
|
2973
|
+
*/
|
|
2974
|
+
addWillFinishTransactionListener(listener: TransactionListener): Id;
|
|
2975
|
+
|
|
2976
|
+
/**
|
|
2977
|
+
* The addDidFinishTransactionListener method registers a listener function
|
|
2978
|
+
* with the Store that will be called just after other non-mutating listeners
|
|
2979
|
+
* are called at the end of the transaction.
|
|
2980
|
+
*
|
|
2981
|
+
* This is useful if you need to know that a set of listeners have just been
|
|
2982
|
+
* called at the end of a transaction, perhaps to batch _their_ consequences
|
|
2983
|
+
* together.
|
|
2984
|
+
*
|
|
2985
|
+
* The provided TransactionListener will receive a reference to the Store and
|
|
2986
|
+
* a boolean to indicate whether Cell values have been touched during the
|
|
2987
|
+
* transaction. The latter flag is intended as a hint about whether
|
|
2988
|
+
* non-mutating listeners might have been called at the end of the
|
|
2989
|
+
* transaction.
|
|
2990
|
+
*
|
|
2991
|
+
* Here, 'touched' means that Cell values have either been changed, or changed
|
|
2992
|
+
* and then changed back to its original value during the transaction. The
|
|
2993
|
+
* exception is a transaction that has been rolled back, for which the value
|
|
2994
|
+
* of `cellsTouched` in the listener will be `false` because all changes have
|
|
2995
|
+
* been reverted.
|
|
2996
|
+
*
|
|
2997
|
+
* @returns A unique Id for the listener that can later be used to remove it.
|
|
2998
|
+
* @example
|
|
2999
|
+
* This example registers a listener that is called at the end of the
|
|
3000
|
+
* transaction, just after its listeners have been called. The transactions
|
|
3001
|
+
* shown here variously change, touch, and rollback cells, demonstrating how
|
|
3002
|
+
* the `cellsTouched` parameter in the listener works.
|
|
3003
|
+
*
|
|
3004
|
+
* ```js
|
|
3005
|
+
* const store = createStore().setTables({
|
|
3006
|
+
* pets: {fido: {species: 'dog', color: 'brown'}},
|
|
3007
|
+
* });
|
|
3008
|
+
* const listenerId = store.addDidFinishTransactionListener(
|
|
3009
|
+
* (store, cellsTouched) => console.log(`Cells touched: ${cellsTouched}`),
|
|
3010
|
+
* );
|
|
3011
|
+
* const listenerId2 = store.addTablesListener(() =>
|
|
3012
|
+
* console.log('Tables changed'),
|
|
3013
|
+
* );
|
|
3014
|
+
*
|
|
3015
|
+
* store.transaction(() => store.setCell('pets', 'fido', 'color', 'brown'));
|
|
3016
|
+
* // -> 'Cells touched: false'
|
|
3017
|
+
*
|
|
3018
|
+
* store.transaction(() => store.setCell('pets', 'fido', 'color', 'walnut'));
|
|
3019
|
+
* // -> 'Tables changed'
|
|
3020
|
+
* // -> 'Cells touched: true'
|
|
3021
|
+
*
|
|
3022
|
+
* store.transaction(() => {
|
|
3023
|
+
* store.setRow('pets', 'felix', {species: 'cat'});
|
|
3024
|
+
* store.delRow('pets', 'felix');
|
|
3025
|
+
* });
|
|
3026
|
+
* // -> 'Cells touched: true'
|
|
3027
|
+
*
|
|
3028
|
+
* store.transaction(
|
|
3029
|
+
* () => store.setRow('pets', 'fido', {species: 'dog'}),
|
|
3030
|
+
* () => true,
|
|
3031
|
+
* );
|
|
3032
|
+
* // -> 'Cells touched: false'
|
|
3033
|
+
* // Transaction was rolled back.
|
|
3034
|
+
*
|
|
3035
|
+
* store.callListener(listenerId);
|
|
3036
|
+
* // -> 'Cells touched: undefined'
|
|
3037
|
+
* // It is meaningless to call this listener directly.
|
|
3038
|
+
*
|
|
3039
|
+
* store.delListener(listenerId).delListener(listenerId2);
|
|
3040
|
+
* ```
|
|
3041
|
+
* @category Listener
|
|
3042
|
+
*/
|
|
3043
|
+
addDidFinishTransactionListener(listener: TransactionListener): Id;
|
|
3044
|
+
|
|
2745
3045
|
/**
|
|
2746
3046
|
* The callListener method provides a way for you to manually provoke a
|
|
2747
3047
|
* listener to be called, even if the underlying data hasn't changed.
|
package/lib/debug/store.js
CHANGED
|
@@ -209,6 +209,7 @@ const idsChanged = (ids, id, added) =>
|
|
|
209
209
|
mapSet(ids, id, mapGet(ids, id) == -added ? void 0 : added);
|
|
210
210
|
const createStore = () => {
|
|
211
211
|
let hasSchema;
|
|
212
|
+
let cellsTouched;
|
|
212
213
|
let nextRowId = 0;
|
|
213
214
|
let transactions = 0;
|
|
214
215
|
const changedTableIds = mapNew();
|
|
@@ -227,6 +228,7 @@ const createStore = () => {
|
|
|
227
228
|
const cellIdsListeners = mapNewPair();
|
|
228
229
|
const cellListeners = mapNewPair();
|
|
229
230
|
const invalidCellListeners = mapNewPair();
|
|
231
|
+
const finishTransactionListeners = mapNewPair(setNew);
|
|
230
232
|
const [addListener, callListeners, delListenerImpl, callListenerImpl] =
|
|
231
233
|
getListenerFunctions(() => store);
|
|
232
234
|
const validateSchema = (schema) =>
|
|
@@ -459,75 +461,73 @@ const createStore = () => {
|
|
|
459
461
|
)
|
|
460
462
|
: 0;
|
|
461
463
|
const callListenersForChanges = (mutator) => {
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
collForEach(
|
|
483
|
-
collForEach(rowCellIds, (changedIds, rowId) => {
|
|
484
|
-
if (!collIsEmpty(changedIds)) {
|
|
485
|
-
callListeners(cellIdsListeners[mutator], [tableId, rowId]);
|
|
486
|
-
}
|
|
487
|
-
}),
|
|
488
|
-
);
|
|
489
|
-
collForEach(changes[1], (changedIds, tableId) => {
|
|
464
|
+
const emptyIdListeners =
|
|
465
|
+
collIsEmpty(cellIdsListeners[mutator]) &&
|
|
466
|
+
collIsEmpty(rowIdsListeners[mutator]) &&
|
|
467
|
+
collIsEmpty(tableIdsListeners[mutator]);
|
|
468
|
+
const emptyOtherListeners =
|
|
469
|
+
collIsEmpty(cellListeners[mutator]) &&
|
|
470
|
+
collIsEmpty(rowListeners[mutator]) &&
|
|
471
|
+
collIsEmpty(tableListeners[mutator]) &&
|
|
472
|
+
collIsEmpty(tablesListeners[mutator]);
|
|
473
|
+
if (!(emptyIdListeners && emptyOtherListeners)) {
|
|
474
|
+
const changes = mutator
|
|
475
|
+
? [
|
|
476
|
+
mapClone(changedTableIds),
|
|
477
|
+
mapClone2(changedRowIds),
|
|
478
|
+
mapClone(changedCellIds, mapClone2),
|
|
479
|
+
mapClone(changedCells, mapClone2),
|
|
480
|
+
]
|
|
481
|
+
: [changedTableIds, changedRowIds, changedCellIds, changedCells];
|
|
482
|
+
if (!emptyIdListeners) {
|
|
483
|
+
collForEach(changes[2], (rowCellIds, tableId) =>
|
|
484
|
+
collForEach(rowCellIds, (changedIds, rowId) => {
|
|
490
485
|
if (!collIsEmpty(changedIds)) {
|
|
491
|
-
callListeners(
|
|
486
|
+
callListeners(cellIdsListeners[mutator], [tableId, rowId]);
|
|
492
487
|
}
|
|
493
|
-
})
|
|
494
|
-
|
|
495
|
-
|
|
488
|
+
}),
|
|
489
|
+
);
|
|
490
|
+
collForEach(changes[1], (changedIds, tableId) => {
|
|
491
|
+
if (!collIsEmpty(changedIds)) {
|
|
492
|
+
callListeners(rowIdsListeners[mutator], [tableId]);
|
|
496
493
|
}
|
|
494
|
+
});
|
|
495
|
+
if (!collIsEmpty(changes[0])) {
|
|
496
|
+
callListeners(tableIdsListeners[mutator]);
|
|
497
497
|
}
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
cellListeners[mutator],
|
|
508
|
-
[tableId, rowId, cellId],
|
|
509
|
-
newCell,
|
|
510
|
-
oldCell,
|
|
511
|
-
getCellChange,
|
|
512
|
-
);
|
|
513
|
-
tablesChanged = tableChanged = rowChanged = 1;
|
|
514
|
-
}
|
|
515
|
-
});
|
|
516
|
-
if (rowChanged) {
|
|
498
|
+
}
|
|
499
|
+
if (!emptyOtherListeners) {
|
|
500
|
+
let tablesChanged;
|
|
501
|
+
collForEach(changes[3], (rows, tableId) => {
|
|
502
|
+
let tableChanged;
|
|
503
|
+
collForEach(rows, (cells, rowId) => {
|
|
504
|
+
let rowChanged;
|
|
505
|
+
collForEach(cells, ([oldCell, newCell], cellId) => {
|
|
506
|
+
if (newCell !== oldCell) {
|
|
517
507
|
callListeners(
|
|
518
|
-
|
|
519
|
-
[tableId, rowId],
|
|
508
|
+
cellListeners[mutator],
|
|
509
|
+
[tableId, rowId, cellId],
|
|
510
|
+
newCell,
|
|
511
|
+
oldCell,
|
|
520
512
|
getCellChange,
|
|
521
513
|
);
|
|
514
|
+
tablesChanged = tableChanged = rowChanged = 1;
|
|
522
515
|
}
|
|
523
516
|
});
|
|
524
|
-
if (
|
|
525
|
-
callListeners(
|
|
517
|
+
if (rowChanged) {
|
|
518
|
+
callListeners(
|
|
519
|
+
rowListeners[mutator],
|
|
520
|
+
[tableId, rowId],
|
|
521
|
+
getCellChange,
|
|
522
|
+
);
|
|
526
523
|
}
|
|
527
524
|
});
|
|
528
|
-
if (
|
|
529
|
-
callListeners(
|
|
525
|
+
if (tableChanged) {
|
|
526
|
+
callListeners(tableListeners[mutator], [tableId], getCellChange);
|
|
530
527
|
}
|
|
528
|
+
});
|
|
529
|
+
if (tablesChanged) {
|
|
530
|
+
callListeners(tablesListeners[mutator], [], getCellChange);
|
|
531
531
|
}
|
|
532
532
|
}
|
|
533
533
|
}
|
|
@@ -656,61 +656,79 @@ const createStore = () => {
|
|
|
656
656
|
if (transactions == -1) {
|
|
657
657
|
return;
|
|
658
658
|
}
|
|
659
|
-
|
|
659
|
+
startTransaction();
|
|
660
660
|
const result = actions?.();
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
table,
|
|
674
|
-
(row) =>
|
|
675
|
-
mapToObj(
|
|
676
|
-
row,
|
|
677
|
-
(cells) => [...cells],
|
|
678
|
-
([oldCell, newCell]) => oldCell === newCell,
|
|
679
|
-
),
|
|
680
|
-
objIsEmpty,
|
|
681
|
-
),
|
|
682
|
-
objIsEmpty,
|
|
683
|
-
),
|
|
684
|
-
mapToObj(invalidCells, (map) => mapToObj(map, mapToObj)),
|
|
685
|
-
)
|
|
686
|
-
) {
|
|
661
|
+
finishTransaction(doRollback);
|
|
662
|
+
return result;
|
|
663
|
+
};
|
|
664
|
+
const startTransaction = () => {
|
|
665
|
+
transactions++;
|
|
666
|
+
return store;
|
|
667
|
+
};
|
|
668
|
+
const finishTransaction = (doRollback) => {
|
|
669
|
+
if (transactions > 0) {
|
|
670
|
+
transactions--;
|
|
671
|
+
if (transactions == 0) {
|
|
672
|
+
cellsTouched = !collIsEmpty(changedCells);
|
|
687
673
|
transactions = 1;
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
674
|
+
callInvalidCellListeners(1);
|
|
675
|
+
if (cellsTouched) {
|
|
676
|
+
callListenersForChanges(1);
|
|
677
|
+
}
|
|
678
|
+
transactions = -1;
|
|
679
|
+
if (
|
|
680
|
+
doRollback?.(
|
|
681
|
+
mapToObj(
|
|
682
|
+
changedCells,
|
|
683
|
+
(table) =>
|
|
684
|
+
mapToObj(
|
|
685
|
+
table,
|
|
686
|
+
(row) =>
|
|
687
|
+
mapToObj(
|
|
688
|
+
row,
|
|
689
|
+
(cells) => [...cells],
|
|
690
|
+
([oldCell, newCell]) => oldCell === newCell,
|
|
691
|
+
),
|
|
692
|
+
objIsEmpty,
|
|
693
|
+
),
|
|
694
|
+
objIsEmpty,
|
|
694
695
|
),
|
|
695
|
-
|
|
696
|
+
mapToObj(invalidCells, (map) => mapToObj(map, mapToObj)),
|
|
697
|
+
)
|
|
698
|
+
) {
|
|
699
|
+
transactions = 1;
|
|
700
|
+
collForEach(changedCells, (table, tableId) =>
|
|
701
|
+
collForEach(table, (row, rowId) =>
|
|
702
|
+
collForEach(row, ([oldCell], cellId) =>
|
|
703
|
+
isUndefined(oldCell)
|
|
704
|
+
? delCell(tableId, rowId, cellId, true)
|
|
705
|
+
: setCell(tableId, rowId, cellId, oldCell),
|
|
706
|
+
),
|
|
707
|
+
),
|
|
708
|
+
);
|
|
709
|
+
transactions = -1;
|
|
710
|
+
cellsTouched = false;
|
|
711
|
+
}
|
|
712
|
+
callListeners(finishTransactionListeners[0], [], cellsTouched);
|
|
713
|
+
callInvalidCellListeners(0);
|
|
714
|
+
if (cellsTouched) {
|
|
715
|
+
callListenersForChanges(0);
|
|
716
|
+
}
|
|
717
|
+
callListeners(finishTransactionListeners[1], [], cellsTouched);
|
|
718
|
+
transactions = 0;
|
|
719
|
+
arrayForEach(
|
|
720
|
+
[
|
|
721
|
+
changedCells,
|
|
722
|
+
invalidCells,
|
|
723
|
+
changedTableIds,
|
|
724
|
+
changedRowIds,
|
|
725
|
+
changedCellIds,
|
|
726
|
+
],
|
|
727
|
+
collClear,
|
|
696
728
|
);
|
|
697
|
-
transactions = -1;
|
|
698
729
|
}
|
|
699
|
-
callInvalidCellListeners(0);
|
|
700
|
-
callListenersForChanges(0);
|
|
701
|
-
transactions = 0;
|
|
702
|
-
arrayForEach(
|
|
703
|
-
[
|
|
704
|
-
changedCells,
|
|
705
|
-
invalidCells,
|
|
706
|
-
changedTableIds,
|
|
707
|
-
changedRowIds,
|
|
708
|
-
changedCellIds,
|
|
709
|
-
],
|
|
710
|
-
collClear,
|
|
711
|
-
);
|
|
712
730
|
}
|
|
713
|
-
return
|
|
731
|
+
return store;
|
|
714
732
|
};
|
|
715
733
|
const forEachTable = (tableCallback) =>
|
|
716
734
|
collForEach(tablesMap, (tableMap, tableId) =>
|
|
@@ -752,6 +770,10 @@ const createStore = () => {
|
|
|
752
770
|
rowId,
|
|
753
771
|
cellId,
|
|
754
772
|
]);
|
|
773
|
+
const addWillFinishTransactionListener = (listener) =>
|
|
774
|
+
addListener(listener, finishTransactionListeners[0]);
|
|
775
|
+
const addDidFinishTransactionListener = (listener) =>
|
|
776
|
+
addListener(listener, finishTransactionListeners[1]);
|
|
755
777
|
const callListener = (listenerId) => {
|
|
756
778
|
callListenerImpl(listenerId, [getTableIds, getRowIds, getCellIds], (ids) =>
|
|
757
779
|
isUndefined(ids[2]) ? [] : arrayPair(getCell(...ids)),
|
|
@@ -770,6 +792,8 @@ const createStore = () => {
|
|
|
770
792
|
row: collPairSize(rowListeners, collSize3),
|
|
771
793
|
cellIds: collPairSize(cellIdsListeners, collSize3),
|
|
772
794
|
cell: collPairSize(cellListeners, collSize4),
|
|
795
|
+
invalidCell: collPairSize(invalidCellListeners, collSize4),
|
|
796
|
+
transaction: collPairSize(finishTransactionListeners),
|
|
773
797
|
});
|
|
774
798
|
const store = {
|
|
775
799
|
getTables,
|
|
@@ -799,6 +823,8 @@ const createStore = () => {
|
|
|
799
823
|
delCell,
|
|
800
824
|
delSchema,
|
|
801
825
|
transaction,
|
|
826
|
+
startTransaction,
|
|
827
|
+
finishTransaction,
|
|
802
828
|
forEachTable,
|
|
803
829
|
forEachRow,
|
|
804
830
|
forEachCell,
|
|
@@ -810,6 +836,8 @@ const createStore = () => {
|
|
|
810
836
|
addCellIdsListener,
|
|
811
837
|
addCellListener,
|
|
812
838
|
addInvalidCellListener,
|
|
839
|
+
addWillFinishTransactionListener,
|
|
840
|
+
addDidFinishTransactionListener,
|
|
813
841
|
callListener,
|
|
814
842
|
delListener,
|
|
815
843
|
getListenerStats,
|