Skip to content

Commit f16afa2

Browse files
committed
[store] TableCellIdsListener
1 parent 3ca442c commit f16afa2

File tree

6 files changed

+505
-16
lines changed

6 files changed

+505
-16
lines changed

src/store.ts

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ export const createStore: typeof createStoreDecl = (): Store => {
170170
let valuesTouched: boolean;
171171
let transactions = 0;
172172
const changedTableIds: ChangedIdsMap = mapNew();
173+
const changedTableCellIds: ChangedIdsMap2 = mapNew();
173174
const changedRowIds: ChangedIdsMap2 = mapNew();
174175
const changedCellIds: ChangedIdsMap3 = mapNew();
175176
const changedCells: IdMap3<[CellOrUndefined, CellOrUndefined]> = mapNew();
@@ -189,6 +190,7 @@ export const createStore: typeof createStoreDecl = (): Store => {
189190
const tablesListeners: Pair<IdSet2> = pairNewMap();
190191
const tableIdsListeners: Pair<IdSet2> = pairNewMap();
191192
const tableListeners: Pair<IdSet2> = pairNewMap();
193+
const tableCellIdsListeners: Pair<IdSet2> = pairNewMap();
192194
const rowIdsListeners: Pair<IdSet2> = pairNewMap();
193195
const sortedRowIdsListeners: Pair<IdSet3> = pairNewMap();
194196
const rowListeners: Pair<IdSet3> = pairNewMap();
@@ -576,8 +578,16 @@ export const createStore: typeof createStoreDecl = (): Store => {
576578
added: IdAdded,
577579
): void => {
578580
const cellIds = mapGet(tableCellIds, tableId);
579-
const count = (mapGet(cellIds, cellId) ?? 0) + added;
580-
mapSet(cellIds, cellId, count != 0 ? count : null);
581+
const count = mapGet(cellIds, cellId) ?? 0;
582+
if ((count == 0 && added == 1) || (count == 1 && added == -1)) {
583+
idsChanged(
584+
mapEnsure(changedTableCellIds, tableId, mapNew) as ChangedIdsMap,
585+
cellId,
586+
added,
587+
);
588+
}
589+
mapSet(cellIds, cellId, count != -added ? count + added : null);
590+
581591
idsChanged(
582592
mapEnsure(
583593
mapEnsure(changedCellIds, tableId, mapNew) as ChangedIdsMap2,
@@ -723,6 +733,7 @@ export const createStore: typeof createStoreDecl = (): Store => {
723733
);
724734
const emptyIdListeners =
725735
collIsEmpty(cellIdsListeners[mutator]) &&
736+
collIsEmpty(tableCellIdsListeners[mutator]) &&
726737
collIsEmpty(rowIdsListeners[mutator]) &&
727738
emptySortedRowIdListeners &&
728739
collIsEmpty(tableIdsListeners[mutator]);
@@ -735,19 +746,34 @@ export const createStore: typeof createStoreDecl = (): Store => {
735746
const changes: [
736747
ChangedIdsMap,
737748
ChangedIdsMap2,
749+
ChangedIdsMap2,
738750
ChangedIdsMap3,
739751
IdMap3<[CellOrUndefined, CellOrUndefined]>,
740752
] = mutator
741753
? [
742754
mapClone(changedTableIds),
755+
mapClone2(changedTableCellIds),
743756
mapClone2(changedRowIds),
744757
mapClone(changedCellIds, mapClone2),
745758
mapClone(changedCells, mapClone2),
746759
]
747-
: [changedTableIds, changedRowIds, changedCellIds, changedCells];
760+
: [
761+
changedTableIds,
762+
changedTableCellIds,
763+
changedRowIds,
764+
changedCellIds,
765+
changedCells,
766+
];
748767

749768
if (!emptyIdListeners) {
750-
collForEach(changes[2], (rowCellIds, tableId) =>
769+
collForEach(changes[1], (changedIds, tableId) =>
770+
callIdsListenersIfChanged(
771+
tableCellIdsListeners[mutator],
772+
changedIds,
773+
[tableId],
774+
),
775+
);
776+
collForEach(changes[3], (rowCellIds, tableId) =>
751777
collForEach(rowCellIds, (changedIds, rowId) =>
752778
callIdsListenersIfChanged(cellIdsListeners[mutator], changedIds, [
753779
tableId,
@@ -757,7 +783,7 @@ export const createStore: typeof createStoreDecl = (): Store => {
757783
);
758784

759785
const calledSortableTableIds: IdSet = setNew();
760-
collForEach(changes[1], (changedIds, tableId) => {
786+
collForEach(changes[2], (changedIds, tableId) => {
761787
if (
762788
callIdsListenersIfChanged(rowIdsListeners[mutator], changedIds, [
763789
tableId,
@@ -770,7 +796,7 @@ export const createStore: typeof createStoreDecl = (): Store => {
770796
});
771797

772798
if (!emptySortedRowIdListeners) {
773-
collForEach(changes[3], (rows, tableId) => {
799+
collForEach(changes[4], (rows, tableId) => {
774800
if (!collHas(calledSortableTableIds, tableId)) {
775801
const sortableCellIds: IdSet = setNew();
776802
collForEach(rows, (cells) =>
@@ -795,7 +821,7 @@ export const createStore: typeof createStoreDecl = (): Store => {
795821

796822
if (!emptyOtherListeners) {
797823
let tablesChanged;
798-
collForEach(changes[3], (rows, tableId) => {
824+
collForEach(changes[4], (rows, tableId) => {
799825
let tableChanged;
800826
collForEach(rows, (cells, rowId) => {
801827
let rowChanged;
@@ -1304,6 +1330,7 @@ export const createStore: typeof createStoreDecl = (): Store => {
13041330
arrayForEach(
13051331
[
13061332
changedTableIds,
1333+
changedTableCellIds,
13071334
changedRowIds,
13081335
changedCellIds,
13091336
changedCells,
@@ -1520,6 +1547,7 @@ export const createStore: typeof createStoreDecl = (): Store => {
15201547
[TABLES]: [0, tablesListeners],
15211548
[TABLE_IDS]: [0, tableIdsListeners],
15221549
[TABLE]: [1, tableListeners, [getTableIds]],
1550+
[TABLE + CELL_IDS]: [1, tableCellIdsListeners, [getTableIds]],
15231551
[ROW_IDS]: [1, rowIdsListeners, [getTableIds]],
15241552
[ROW]: [2, rowListeners, [getTableIds, getRowIds]],
15251553
[CELL_IDS]: [2, cellIdsListeners, [getTableIds, getRowIds]],

src/types/docs/store.js

Lines changed: 127 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -461,8 +461,8 @@
461461
* used to query which Ids changed during the transaction.
462462
*
463463
* @param store A reference to the Store that changed.
464-
* @param getIdChanges A function that returns information Id changes, since
465-
* 3.3.
464+
* @param getIdChanges A function that returns information about the Id changes,
465+
* since v3.3.
466466
* @category Listener
467467
*/
468468
/// TableIdsListener
@@ -488,6 +488,24 @@
488488
* @category Listener
489489
*/
490490
/// TableListener
491+
/**
492+
* The TableCellIdsListener type describes a function that is used to listen to
493+
* changes to the Cell Ids that appear anywhere in a Table.
494+
*
495+
* A TableCellIdsListener is provided when using the addTableCellIdsListener
496+
* method. See that method for specific examples.
497+
*
498+
* When called, a TableCellIdsListener is given a reference to the Store, the Id
499+
* of the Table whose Cell Ids changed, and a GetIdChanges function that can be
500+
* used to query which Ids changed during the transaction.
501+
*
502+
* @param store A reference to the Store that changed.
503+
* @param tableId The Id of the Table that changed.
504+
* @param getIdChanges A function that returns information about the Id changes.
505+
* @category Listener
506+
* @since v3.3
507+
*/
508+
/// TableCellIdsListener
491509
/**
492510
* The RowIdsListener type describes a function that is used to listen to
493511
* changes to the Row Ids in a Table.
@@ -503,8 +521,8 @@
503521
*
504522
* @param store A reference to the Store that changed.
505523
* @param tableId The Id of the Table that changed.
506-
* @param getIdChanges A function that returns information Id changes, since
507-
* 3.3.
524+
* @param getIdChanges A function that returns information about the Id changes,
525+
* since v3.3.
508526
* @category Listener
509527
*/
510528
/// RowIdsListener
@@ -573,8 +591,8 @@
573591
* @param store A reference to the Store that changed.
574592
* @param tableId The Id of the Table that changed.
575593
* @param rowId The Id of the Row that changed.
576-
* @param getIdChanges A function that returns information Id changes, since
577-
* 3.3.
594+
* @param getIdChanges A function that returns information about the Id changes,
595+
* since v3.3.
578596
* @category Listener
579597
*/
580598
/// CellIdsListener
@@ -641,8 +659,8 @@
641659
* used to query which Ids changed during the transaction.
642660
*
643661
* @param store A reference to the Store that changed.
644-
* @param getIdChanges A function that returns information Id changes, since
645-
* 3.3.
662+
* @param getIdChanges A function that returns information about the Id changes,
663+
* since v3.3.
646664
* @category Listener
647665
*/
648666
/// ValueIdsListener
@@ -3533,6 +3551,106 @@
35333551
* @category Listener
35343552
*/
35353553
/// Store.addTableListener
3554+
/**
3555+
* The addTableCellIdsListener method registers a listener function with the
3556+
* Store that will be called whenever the Cell Ids that appear anywhere in a
3557+
* Table change.
3558+
*
3559+
* The provided listener is a TableCellIdsListener function, and will be
3560+
* called with a reference to the Store and the Id of the Table that changed.
3561+
*
3562+
* By default, such a listener is only called when a Cell Id is added or
3563+
* removed from the whole of the Table. To listen to all changes in the Table,
3564+
* use the addTableListener method.
3565+
*
3566+
* You can either listen to a single Table (by specifying its Id as the
3567+
* method's first parameter) or changes to any Table (by providing a `null`
3568+
* wildcard).
3569+
*
3570+
* Use the optional mutator parameter to indicate that there is code in the
3571+
* listener that will mutate Store data. If set to `false` (or omitted), such
3572+
* mutations will be silently ignored. All relevant mutator listeners (with
3573+
* this flag set to `true`) are called _before_ any non-mutator listeners
3574+
* (since the latter may become relevant due to changes made in the former).
3575+
* The changes made by mutator listeners do not fire other mutating listeners,
3576+
* though they will fire non-mutator listeners.
3577+
*
3578+
* @param tableId The Id of the Table to listen to, or `null` as a wildcard.
3579+
* @param listener The function that will be called whenever the Cell Ids that
3580+
* appear anywhere in a Table change.
3581+
* @param mutator An optional boolean that indicates that the listener mutates
3582+
* Store data.
3583+
* @returns A unique Id for the listener that can later be used to call it
3584+
* explicitly, or to remove it.
3585+
* @example
3586+
* This example registers a listener that responds to any change to the Cell
3587+
* Ids that appear anywhere in a Table.
3588+
*
3589+
* ```js
3590+
* const store = createStore().setTables({
3591+
* pets: {fido: {species: 'dog', color: 'brown'}},
3592+
* });
3593+
* const listenerId = store.addTableCellIdsListener('pets', (store) => {
3594+
* console.log('Cell Ids in pets table changed');
3595+
* console.log(store.getTableCellIds('pets'));
3596+
* });
3597+
*
3598+
* store.setRow('pets', 'felix', {species: 'cat', legs: 4});
3599+
* // -> 'Cell Ids in pets table changed'
3600+
* // -> ['species', 'color', 'legs']
3601+
*
3602+
* store.delListener(listenerId);
3603+
* ```
3604+
* @example
3605+
* This example registers a listener that responds to any change to the Cell
3606+
* Ids that appear anywhere in any Table.
3607+
*
3608+
* ```js
3609+
* const store = createStore().setTables({
3610+
* pets: {fido: {species: 'dog', color: 'brown'}},
3611+
* species: {dog: {price: 5}, }
3612+
* });
3613+
* const listenerId = store.addTableCellIdsListener(
3614+
* null,
3615+
* (store, tableId) => {
3616+
* console.log(`Cell Ids in ${tableId} table changed`);
3617+
* console.log(store.getTableCellIds(tableId));
3618+
* },
3619+
* );
3620+
*
3621+
* store.setRow('pets', 'felix', {species: 'cat', legs: 4});
3622+
* // -> 'Cell Ids in pets table changed'
3623+
* // -> ['species', 'color', 'legs']
3624+
*
3625+
* store.setRow('species', 'cat', {price: 4, friendly: true});
3626+
* // -> 'Cell Ids in species table changed'
3627+
* // -> ['price', 'friendly']
3628+
*
3629+
* store.delListener(listenerId);
3630+
* ```
3631+
* @example
3632+
* This example registers a listener that responds to the Cell Ids that appear
3633+
* anywhere in a Table, and which also mutates the Store itself.
3634+
*
3635+
* ```js
3636+
* const store = createStore().setTables({
3637+
* pets: {fido: {species: 'dog', color: 'brown'}},
3638+
* });
3639+
* const listenerId = store.addTableCellIdsListener(
3640+
* 'pets',
3641+
* (store, tableId) => store.setCell('meta', 'update', tableId, true),
3642+
* true, // mutator
3643+
* );
3644+
*
3645+
* store.setRow('pets', 'felix', {species: 'cat', legs: 4});
3646+
* console.log(store.getTable('meta'));
3647+
* // -> {update: {pets: true}}
3648+
*
3649+
* store.delListener(listenerId);
3650+
* ```
3651+
* @category Listener
3652+
*/
3653+
/// Store.addTableCellIdsListener
35363654
/**
35373655
* The addRowIdsListener method registers a listener function with the Store
35383656
* that will be called whenever the Row Ids in a Table change.
@@ -3702,7 +3820,7 @@
37023820
* store.delListener(listenerId);
37033821
* ```
37043822
* @example
3705-
* This 111example registers a listener that responds to any change to a
3823+
* This example registers a listener that responds to any change to a
37063824
* paginated section of the sorted Row Ids of a specific Table.
37073825
*
37083826
* ```js

src/types/store.d.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,13 @@ export type TableListener = (
126126
getCellChange: GetCellChange | undefined,
127127
) => void;
128128

129+
/// TableCellIdsListener
130+
export type TableCellIdsListener = (
131+
store: Store,
132+
tableId: Id,
133+
getIdChanges: GetIdChanges | undefined,
134+
) => void;
135+
129136
/// RowIdsListener
130137
export type RowIdsListener = (
131138
store: Store,
@@ -484,6 +491,13 @@ export interface Store {
484491
mutator?: boolean,
485492
): Id;
486493

494+
/// Store.addTableCellIdsListener
495+
addTableCellIdsListener(
496+
tableId: IdOrNull,
497+
listener: TableCellIdsListener,
498+
mutator?: boolean,
499+
): Id;
500+
487501
/// Store.addRowIdsListener
488502
addRowIdsListener(
489503
tableId: IdOrNull,

src/types/with-schemas/store.d.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,31 @@ export type TableListener<
291291
getCellChange: GetCellChange<Schemas[0]> | undefined,
292292
) => void;
293293

294+
/// TableCellIdsListener
295+
export type TableCellIdsListener<
296+
Schemas extends OptionalSchemas,
297+
TableIdOrNull extends TableIdFromSchema<Schemas[0]> | null,
298+
Params extends any[] = (
299+
TableIdOrNull extends null ? TableIdFromSchema<Schemas[0]> : TableIdOrNull
300+
) extends infer TableId
301+
? TableId extends TableIdFromSchema<Schemas[0]>
302+
? [
303+
store: Store<Schemas>,
304+
tableId: TableId,
305+
getIdChanges: GetIdChanges<CellIdFromSchema<Schemas[0], TableId>>,
306+
]
307+
: never
308+
: never,
309+
Params3 extends any[] =
310+
| Params
311+
| [store: never, tableId: never, getIdChanges: never],
312+
Params2 extends any[] = Truncate<Params3>,
313+
// Params1 extends any[] = Truncate<Params2>,
314+
> = Params extends any
315+
? ((...params: Params3) => void) | ((...params: Params2) => void)
316+
: // | ((...params: Params1) => void)
317+
never;
318+
294319
/// RowIdsListener
295320
export type RowIdsListener<
296321
Schemas extends OptionalSchemas,
@@ -882,6 +907,15 @@ export interface Store<in out Schemas extends OptionalSchemas> {
882907
mutator?: boolean,
883908
): Id;
884909

910+
/// Store.addTableCellIdsListener
911+
addTableCellIdsListener<
912+
TableIdOrNull extends TableIdFromSchema<Schemas[0]> | null,
913+
>(
914+
tableId: TableIdOrNull,
915+
listener: TableCellIdsListener<Schemas, TableIdOrNull>,
916+
mutator?: boolean,
917+
): Id;
918+
885919
/// Store.addRowIdsListener
886920
addRowIdsListener<TableIdOrNull extends TableIdFromSchema<Schemas[0]> | null>(
887921
tableId: TableIdOrNull,

0 commit comments

Comments
 (0)