Clean up generic widget handling code in the frontend (#3856)

* Clean up WidgetSpan component code to avoid the giant if-chain for choosing a widget

* Improve typing support
This commit is contained in:
Keavon Chambers
2026-03-04 01:00:09 -08:00
committed by GitHub
parent 8a75c0c1e1
commit 5834ee9ce4
2 changed files with 194 additions and 147 deletions
+12 -20
View File
@@ -1,5 +1,3 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Transform, Type, plainToClass } from "class-transformer";
import { sampleInterpolatedGradient, type EditorHandle } from "@graphite/../wasm/pkg/graphite_wasm";
@@ -971,7 +969,7 @@ export class ColorInput extends WidgetProps {
return new Gradient(
gradient.position,
gradient.midpoint,
gradient.color.map((color: any) => new Color(color.red, color.green, color.blue, color.alpha)),
gradient.color.map((color) => new Color(color.red, color.green, color.blue, color.alpha)),
);
}
@@ -1460,14 +1458,9 @@ const widgetSubTypes = [
] as const;
type WidgetSubTypes = (typeof widgetSubTypes)[number];
type WidgetKindMap = { [T in WidgetSubTypes as T["name"]]: InstanceType<T["value"]> };
export type WidgetPropsNames = keyof WidgetKindMap;
export type WidgetPropsSet = WidgetKindMap[WidgetPropsNames];
export function narrowWidgetProps<K extends WidgetPropsNames>(props: WidgetPropsSet, kind: K) {
if (props.kind === kind) return props as WidgetKindMap[K];
else return undefined;
}
export type WidgetTypes = { [T in WidgetSubTypes as T["name"]]: InstanceType<T["value"]> };
export type WidgetPropsNames = keyof WidgetTypes;
export type WidgetPropsSet = WidgetTypes[WidgetPropsNames];
export class WidgetInstance {
@Type(() => WidgetProps, { discriminator: { property: "kind", subTypes: [...widgetSubTypes] }, keepDiscriminatorProperty: true })
@@ -1476,6 +1469,7 @@ export class WidgetInstance {
widgetId!: bigint;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function hoistWidgetInstance(widgetInstance: any): WidgetInstance {
const kind = Object.keys(widgetInstance.widget)[0];
const props = widgetInstance.widget[kind];
@@ -1490,10 +1484,6 @@ function hoistWidgetInstance(widgetInstance: any): WidgetInstance {
return plainToClass(WidgetInstance, { props, widgetId });
}
function hoistWidgetInstances(widgetInstance: any[]): WidgetInstance[] {
return widgetInstance.map(hoistWidgetInstance);
}
// WIDGET LAYOUT
export type LayoutTarget =
@@ -1516,7 +1506,6 @@ export type LayoutTarget =
| "WorkingColors";
export class WidgetDiffUpdate extends JsMessage {
// TODO: Replace `any` with correct typing
@Transform(({ value }: { value: WidgetDiff[] }) => {
// Unpacking rust types to more usable type in the frontend
return value.map((diff) => {
@@ -1574,7 +1563,7 @@ export function patchLayout(layout: /* &mut */ Layout, updates: WidgetDiffUpdate
diffObject.length = 0;
}
// Remove all of the keys from the old object
Object.keys(diffObject).forEach((key) => delete (diffObject as any)[key]);
Object.keys(diffObject).forEach((key) => delete (diffObject as Record<string, unknown>)[key]);
// Assign keys to the new object
// `Object.assign` works but `diffObject = update.newValue;` doesn't.
@@ -1607,16 +1596,17 @@ export function isWidgetSection(layoutRow: LayoutGroup): layoutRow is WidgetSect
}
// Unpacking a layout group
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function createLayoutGroup(layoutGroup: any): LayoutGroup {
if (layoutGroup.column) {
const columnWidgets = hoistWidgetInstances(layoutGroup.column.columnWidgets);
const columnWidgets = layoutGroup.column.columnWidgets.map(hoistWidgetInstance);
const result: WidgetSpanColumn = { columnWidgets };
return result;
}
if (layoutGroup.row) {
const result: WidgetSpanRow = { rowWidgets: hoistWidgetInstances(layoutGroup.row.rowWidgets) };
const result: WidgetSpanRow = { rowWidgets: layoutGroup.row.rowWidgets.map(hoistWidgetInstance) };
return result;
}
@@ -1634,7 +1624,8 @@ function createLayoutGroup(layoutGroup: any): LayoutGroup {
if (layoutGroup.table) {
const result: WidgetTable = {
tableWidgets: layoutGroup.table.tableWidgets.map(hoistWidgetInstances),
// eslint-disable-next-line @typescript-eslint/no-explicit-any
tableWidgets: layoutGroup.table.tableWidgets.map((row: any) => row.map(hoistWidgetInstance)),
unstyled: layoutGroup.table.unstyled,
};
return result;
@@ -1679,6 +1670,7 @@ export class UpdateToolShelfLayout extends WidgetDiffUpdate {}
export class UpdateWorkingColorsLayout extends WidgetDiffUpdate {}
// `any` is used since the type of the object should be known from the Rust side
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type JSMessageFactory = (data: any, wasm: WebAssembly.Memory, handle: EditorHandle) => JsMessage;
type MessageMaker = typeof JsMessage | JSMessageFactory;