value-cell.ts 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566
  1. /**
  2. * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author David Sehnal <david.sehnal@gmail.com>
  5. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  6. */
  7. import { idFactory } from './id-factory';
  8. /** A mutable value reference. */
  9. interface ValueRef<T> { ref: T }
  10. namespace ValueRef {
  11. export function create<T>(ref: T): ValueRef<T> { return { ref }; }
  12. export function set<T>(ref: ValueRef<T>, value: T) { ref.ref = value; return ref; }
  13. }
  14. const getNextId = idFactory(0, 0x7FFFFFFF);
  15. /**
  16. * An immutable value box that also holds a version of the attribute.
  17. * Optionally includes automatically propadated "metadata".
  18. */
  19. type ValueBox<T, D = never> = {
  20. /** Unique identifier in the range 0 to 0x7FFFFFFF */
  21. readonly id: number,
  22. readonly version: number,
  23. readonly metadata: D,
  24. readonly value: T,
  25. }
  26. namespace ValueBox {
  27. export function create<T, D = never>(value: T, metadata?: D): ValueBox<T, D> {
  28. return { id: getNextId(), version: 0, value, metadata: metadata! };
  29. }
  30. /** The box.metadata is carried over from the old box */
  31. export function withValue<T, D>(box: ValueBox<T, D>, value: T): ValueBox<T, D> {
  32. return { id: box.id, version: box.version + 1, value, metadata: box.metadata };
  33. }
  34. }
  35. /** An immutable box stored inside a mutable cell. */
  36. type ValueCell<T, D = never> = ValueRef<ValueBox<T, D>>
  37. namespace ValueCell {
  38. export function create<T, D = never>(value: T, metadata?: D): ValueCell<T, D> {
  39. return ValueRef.create(ValueBox.create(value, metadata));
  40. }
  41. /** The box.metadata is carried over from the old box */
  42. export function update<T, D>(cell: ValueCell<T, D>, value: T): ValueCell<T, D> {
  43. return ValueRef.set(cell, ValueBox.withValue(cell.ref, value));
  44. }
  45. export function set<T, D>(cell: ValueCell<T, D>, box: ValueBox<T, D>): ValueCell<T, D> {
  46. return ValueRef.set(cell, box);
  47. }
  48. /** Updates the cell if the value is has changed, comparing by reference */
  49. export function updateIfChanged<T, D>(cell: ValueCell<T, D>, value: T): ValueCell<T, D> {
  50. return cell.ref.value !== value ? update(cell, value) : cell;
  51. }
  52. }
  53. export { ValueRef, ValueBox, ValueCell };