UNPKG

alinea

Version:
226 lines (224 loc) 7.06 kB
import { YMap, YMapEvent } from "../../chunks/chunk-QUIANN6B.js"; import "../../chunks/chunk-AJJSW27C.js"; import "../../chunks/chunk-NZLE2WMY.js"; // src/core/shape/ListShape.ts import { createId } from "../Id.js"; import { generateKeyBetween } from "../util/FractionalIndexing.js"; import { RecordShape } from "./RecordShape.js"; import { ScalarShape } from "./ScalarShape.js"; var ListRow; ((ListRow2) => { ListRow2.id = "_id"; ListRow2.index = "_index"; ListRow2.type = "_type"; })(ListRow || (ListRow = {})); function sort(a, b) { if (a[ListRow.index] < b[ListRow.index]) return -1; if (a[ListRow.index] > b[ListRow.index]) return 1; return 0; } var ListShape = class { constructor(label, shapes, initialValue, postProcess) { this.label = label; this.initialValue = initialValue; this.postProcess = postProcess; this.shapes = Object.fromEntries( Object.entries(shapes).map(([key, type]) => { return [ key, new RecordShape(label, { [ListRow.id]: new ScalarShape("Id"), [ListRow.index]: new ScalarShape("Index"), [ListRow.type]: new ScalarShape("Type"), ...type.shapes }) ]; }) ); } shapes; create() { return this.initialValue ?? []; } toV1(value) { if (!Array.isArray(value)) return []; return value.map(this.normalizeRow).filter(Boolean); } normalizeRow = (row) => { if (ListRow.type in row) return row; const { id, type, index, ...data } = row; if (!id || !type) return void 0; const shape = this.shapes[type]; const updated = shape.toV1(data); return { [ListRow.type]: type, [ListRow.id]: id, [ListRow.index]: index, ...updated }; }; toY(value) { const map = new YMap(); const rows = Array.isArray(value) ? value : []; let currentIndex = null; for (const row of rows) { const type = this.shapes[row[ListRow.type]]; if (!type) continue; currentIndex = generateKeyBetween(currentIndex, null); map.set( row[ListRow.id], type.toY({ ...row, [ListRow.index]: currentIndex }) ); } return map; } fromY(map) { const rows = []; if (!map || typeof map.keys !== "function") return rows; for (const key of map.keys()) { const row = map.get(key); if (!row || typeof row.get !== "function") continue; const type = row.get(ListRow.type); const rowType = this.shapes[type]; if (rowType) rows.push(rowType.fromY(row)); } rows.sort(sort); return rows; } applyY(value, parent, key) { if (!Array.isArray(value)) return; const current = parent.get(key); if (!current) return void parent.set(key, this.toY(value)); const currentKeys = new Set(current.keys()); const valueKeys = new Set(value.map((row) => row[ListRow.id])); const removed = [...currentKeys].filter((key2) => !valueKeys.has(key2)); const added = [...valueKeys].filter((key2) => !currentKeys.has(key2)); const changed = [...valueKeys].filter((key2) => currentKeys.has(key2)); for (const id of removed) current.delete(id); for (const id of added) { const row = value.find((row2) => row2[ListRow.id] === id); if (!row) continue; const type = row[ListRow.type]; const rowType = this.shapes[type]; if (!rowType) continue; current.set(id, rowType.toY(row)); } for (const id of changed) { const row = value.find((row2) => row2[ListRow.id] === id); if (!row) continue; const type = row[ListRow.type]; const currentRow = current.get(id); if (!currentRow) continue; const currentType = currentRow.get(ListRow.type); if (currentType !== type) { current.delete(id); current.set(id, this.shapes[type].toY(row)); continue; } const rowType = this.shapes[type]; if (!rowType) continue; rowType.applyY(row, current, id); } } init(parent, key) { if (!parent.has(key)) parent.set(key, this.toY(this.create())); } watch(parent, key) { const map = parent.get(key); return (fun) => { function w(events, transaction) { for (const event of events) { if (event.target === map) fun(); if (event instanceof YMapEvent && event.keysChanged.has(ListRow.index)) fun(); } } map.observeDeep(w); return () => { map.unobserveDeep(w); }; }; } mutator(parent, key) { const res = { replace: (id, row) => { const record = parent.get(key); const rows = this.fromY(record); const index = rows.findIndex((r) => r[ListRow.id] === id); res.remove(id); res.push(row, index); }, push: (row, insertAt) => { const type = row[ListRow.type]; const shape = this.shapes[type]; const record = parent.get(key); const rows = this.fromY(record); const id = createId(); const before = insertAt === void 0 ? rows.length - 1 : insertAt - 1; const after = before + 1; const keyA = rows[before]?.[ListRow.index] || null; const keyB = rows[after]?.[ListRow.index] || null; const item = shape.toY({ ...shape.create(), ...row, [ListRow.id]: id, [ListRow.index]: generateKeyBetween(keyA, keyB) }); record.set(id, item); }, remove(id) { const record = parent.get(key); record.delete(id); }, move: (oldIndex, newIndex) => { const record = parent.get(key); const rows = this.fromY(record); const from = rows[oldIndex]; const into = rows.filter((row2) => row2[ListRow.id] !== from[ListRow.id]); const prev = into[newIndex - 1]; const next = into[newIndex]; const a = prev?.[ListRow.index] || null; const b = next?.[ListRow.index] || null; const index = generateKeyBetween(a, b); const row = record.get(from[ListRow.id]); row.set(ListRow.index, index); }, read: (id) => { const record = parent.get(key); const rows = this.fromY(record); return rows.find((row) => row._id === id); } }; return res; } async applyLinks(value, loader) { const tasks = []; if (!Array.isArray(value)) return; for (const row of value) { const type = row[ListRow.type]; const shape = this.shapes[type]; if (shape) tasks.push(shape.applyLinks(row, loader)); } await Promise.all(tasks); if (this.postProcess) await this.postProcess(value, loader); } searchableText(value) { let res = ""; const rows = Array.isArray(value) ? value : []; for (const row of rows) { const id = row[ListRow.id]; const type = row[ListRow.type]; const shape = this.shapes[type]; if (!id || !type || !shape) continue; res += shape.searchableText(row); } return res; } }; export { ListRow, ListShape };