Files
Graphite/frontend/src/utility-functions/widgets.ts

58 lines
2.9 KiB
TypeScript

import type { Layout, LayoutGroup, WidgetDiff, WidgetInstance } from "/wrapper/pkg/graphite_wasm_wrapper";
type UIItem = Layout | LayoutGroup | WidgetInstance[][] | WidgetInstance[] | WidgetInstance;
// Updates a widget layout based on a list of updates, giving the new layout by mutating the `layout` argument
export function patchLayout(layout: /* &mut */ Layout, diffs: WidgetDiff[]) {
diffs.forEach((update) => {
// Extract the actual content from the DiffUpdate tagged enum
const { newValue } = update;
let newContent: Layout | LayoutGroup | WidgetInstance;
if ("layout" in newValue) newContent = newValue.layout;
else if ("layoutGroup" in newValue) newContent = newValue.layoutGroup;
else if ("widget" in newValue) newContent = newValue.widget;
else throw new Error("DiffUpdate invalid");
// Find the object where the diff applies to
const diffObject = update.widgetPath.reduce((targetLayout, index: bigint): UIItem | undefined => {
const i = Number(index);
if (targetLayout && "Column" in targetLayout) return targetLayout.Column.columnWidgets[i];
if (targetLayout && "Row" in targetLayout) return targetLayout.Row.rowWidgets[i];
if (targetLayout && "Table" in targetLayout) return targetLayout.Table.tableWidgets[i];
if (targetLayout && "Section" in targetLayout) return targetLayout.Section.layout[i];
if (targetLayout && "widget" in targetLayout && "widgetId" in targetLayout) {
if ("PopoverButton" in targetLayout.widget && targetLayout.widget.PopoverButton.popoverLayout) {
return targetLayout.widget.PopoverButton.popoverLayout[i];
}
// eslint-disable-next-line no-console
console.error("Tried to index widget");
return targetLayout;
}
return targetLayout?.[i];
}, layout);
// Exit if we failed to produce a valid patch for the existing layout.
// This means that the backend assumed an existing layout that doesn't exist in the frontend. This can happen, for
// example, if a panel is destroyed in the frontend but was never cleared in the backend, so the next time the backend
// tries to update the layout, it attempts to insert only the changes against the old layout that no longer exists.
if (diffObject === undefined) {
// eslint-disable-next-line no-console
console.error("In `patchLayout`, the `diffObject` is undefined. The layout has not been updated. See the source code comment above this error for hints.");
return;
}
// If this is a list with a length, then set the length to 0 to clear the list
if ("length" in diffObject) {
diffObject.length = 0;
}
// Remove all of the keys from the old object
Object.keys(diffObject).forEach((key) => Reflect.deleteProperty(diffObject, key));
// Assign keys to the new object
// `Object.assign` works but `diffObject = update.newValue;` doesn't.
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
Object.assign(diffObject, newContent);
});
}