UNPKG

@vaadin/hilla-react-signals

Version:

Signals for Hilla React

121 lines 4.7 kB
import { CollectionSignal } from './CollectionSignal.js'; import { createInsertLastStateEvent, createRemoveStateEvent, isInsertLastStateEvent, isListSnapshotStateEvent, isRemoveStateEvent, } from './events.js'; import { $createOperation, $processServerResponse, $resolveOperation, $setValueQuietly, $update, } from './FullStackSignal.js'; import { ValueSignal } from './ValueSignal.js'; export class ListSignal extends CollectionSignal { #head; #tail; #values = new Map(); constructor(config) { const initialValue = []; super(initialValue, config); } #computeItems() { let current = this.#head; const result = []; while (current !== undefined) { const entry = this.#values.get(current); result.push(entry.value); current = entry.next; } return result; } [$processServerResponse](event) { if (!event.accepted) { this[$resolveOperation](event.id, `Server rejected the operation with id '${event.id}'. See the server log for more details.`); return; } if (isListSnapshotStateEvent(event)) { this.#handleSnapshotEvent(event); } else if (isInsertLastStateEvent(event)) { this.#handleInsertLastUpdate(event); } else if (isRemoveStateEvent(event)) { this.#handleRemoveUpdate(event); } this[$resolveOperation](event.id); } #handleInsertLastUpdate(event) { if (event.entryId === undefined) { throw new Error('Unexpected state: Entry id should be defined when insert last event is accepted'); } const valueSignal = new ValueSignal(event.value, { ...this.server.config, parentClientSignalId: this.id }, event.entryId); const newEntry = { id: valueSignal.id, value: valueSignal }; if (this.#head === undefined) { this.#head = newEntry.id; this.#tail = this.#head; } else { const tailEntry = this.#values.get(this.#tail); tailEntry.next = newEntry.id; newEntry.prev = this.#tail; this.#tail = newEntry.id; } this.#values.set(valueSignal.id, newEntry); this[$setValueQuietly](this.#computeItems()); } #handleRemoveUpdate(event) { const entryToRemove = this.#values.get(event.entryId); if (entryToRemove === undefined) { return; } this.#values.delete(event.id); if (this.#head === entryToRemove.id) { if (entryToRemove.next === undefined) { this.#head = undefined; this.#tail = undefined; } else { const newHead = this.#values.get(entryToRemove.next); this.#head = newHead.id; newHead.prev = undefined; } } else { const prevEntry = this.#values.get(entryToRemove.prev); const nextEntry = entryToRemove.next !== undefined ? this.#values.get(entryToRemove.next) : undefined; if (nextEntry === undefined) { this.#tail = prevEntry.id; prevEntry.next = undefined; } else { prevEntry.next = nextEntry.id; nextEntry.prev = prevEntry.id; } } this[$setValueQuietly](this.#computeItems()); } #handleSnapshotEvent(event) { event.entries.forEach((entry) => { this.#values.set(entry.id, { id: entry.id, prev: entry.prev, next: entry.next, value: new ValueSignal(entry.value, { ...this.server.config, parentClientSignalId: this.id }, entry.id), }); if (entry.prev === undefined) { this.#head = entry.id; } if (entry.next === undefined) { this.#tail = entry.id; } }); this[$setValueQuietly](this.#computeItems()); } insertLast(value) { const event = createInsertLastStateEvent(value); const promise = this[$update](event); return this[$createOperation]({ id: event.id, promise }); } remove(item) { const entryToRemove = this.#values.get(item.id); if (entryToRemove === undefined) { return { result: Promise.resolve() }; } const removeEvent = createRemoveStateEvent(entryToRemove.value.id); const promise = this[$update](removeEvent); return this[$createOperation]({ id: removeEvent.id, promise }); } } //# sourceMappingURL=ListSignal.js.map