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:
parent
8a75c0c1e1
commit
5834ee9ce4
|
|
@ -2,8 +2,8 @@
|
|||
import { getContext } from "svelte";
|
||||
|
||||
import type { Editor } from "@graphite/editor";
|
||||
import type { LayoutTarget, WidgetInstance, WidgetSpanColumn, WidgetSpanRow } from "@graphite/messages";
|
||||
import { narrowWidgetProps, isWidgetSpanColumn, isWidgetSpanRow } from "@graphite/messages";
|
||||
import type { LayoutTarget, WidgetInstance, WidgetPropsNames, WidgetPropsSet, WidgetTypes, WidgetSpanColumn, WidgetSpanRow } from "@graphite/messages";
|
||||
import { isWidgetSpanColumn, isWidgetSpanRow } from "@graphite/messages";
|
||||
import { debouncer } from "@graphite/utility-functions/debounce";
|
||||
|
||||
import NodeCatalog from "@graphite/components/floating-menus/NodeCatalog.svelte";
|
||||
|
|
@ -71,134 +71,189 @@
|
|||
editor.handle.widgetValueCommitAndUpdate(layoutTarget, widgets[widgetIndex].widgetId, value, resendWidget);
|
||||
}
|
||||
|
||||
// TODO: This seems to work, but verify the correctness and terseness of this, it's adapted from https://stackoverflow.com/a/67434028/775283
|
||||
function exclude<T extends object>(props: T, additional?: (keyof T)[]): Omit<T, typeof additional extends Array<infer K> ? K : never> {
|
||||
const exclusions = ["kind", ...(additional || [])];
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
return Object.fromEntries(Object.entries(props).filter((entry) => !exclusions.includes(entry[0]))) as any;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function exclude(props: WidgetPropsSet, additional?: string[]): Record<string, any> {
|
||||
const exclusions = new Set(["kind", ...(additional || [])]);
|
||||
return Object.fromEntries(Object.entries(props).filter(([key]) => !exclusions.has(key)));
|
||||
}
|
||||
|
||||
type WidgetConfig = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
component: any;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
getProps(props: WidgetPropsSet, widgetIndex: number): Record<string, any> | undefined;
|
||||
getSlotContent?(props: WidgetPropsSet): string;
|
||||
};
|
||||
|
||||
const widgetRegistry: Record<WidgetPropsNames, WidgetConfig> = {
|
||||
CheckboxInput: {
|
||||
component: CheckboxInput,
|
||||
getProps: (props: WidgetTypes["CheckboxInput"], index) => ({
|
||||
...exclude(props),
|
||||
$$events: { checked: (e: CustomEvent) => widgetValueCommitAndUpdate(index, e.detail, true) },
|
||||
}),
|
||||
},
|
||||
ColorInput: {
|
||||
component: ColorInput,
|
||||
getProps: (props: WidgetTypes["ColorInput"], index) => ({
|
||||
...exclude(props),
|
||||
$$events: {
|
||||
value: (e: CustomEvent) => widgetValueUpdate(index, e.detail, false),
|
||||
startHistoryTransaction: () => widgetValueCommit(index, props.value),
|
||||
},
|
||||
}),
|
||||
},
|
||||
CurveInput: {
|
||||
// TODO: CurvesInput is currently unused
|
||||
component: CurveInput,
|
||||
getProps: (props: WidgetTypes["CurveInput"], index) => ({
|
||||
...exclude(props),
|
||||
$$events: {
|
||||
value: (e: CustomEvent) => debouncer((value: unknown) => widgetValueCommitAndUpdate(index, value, false), { debounceTime: 120 }).debounceUpdateValue(e.detail),
|
||||
},
|
||||
}),
|
||||
},
|
||||
DropdownInput: {
|
||||
component: DropdownInput,
|
||||
getProps: (props: WidgetTypes["DropdownInput"], index) => ({
|
||||
...exclude(props),
|
||||
$$events: {
|
||||
hoverInEntry: (e: CustomEvent) => widgetValueUpdate(index, e.detail, false),
|
||||
hoverOutEntry: (e: CustomEvent) => widgetValueUpdate(index, e.detail, false),
|
||||
selectedIndex: (e: CustomEvent) => widgetValueCommitAndUpdate(index, e.detail, true),
|
||||
},
|
||||
}),
|
||||
},
|
||||
ParameterExposeButton: {
|
||||
component: ParameterExposeButton,
|
||||
getProps: (props: WidgetTypes["ParameterExposeButton"], index) => ({
|
||||
...exclude(props),
|
||||
action: () => widgetValueCommitAndUpdate(index, undefined, true),
|
||||
}),
|
||||
},
|
||||
IconButton: {
|
||||
component: IconButton,
|
||||
getProps: (props: WidgetTypes["IconButton"], index) => ({
|
||||
...exclude(props),
|
||||
action: () => widgetValueCommitAndUpdate(index, undefined, true),
|
||||
}),
|
||||
},
|
||||
IconLabel: {
|
||||
component: IconLabel,
|
||||
getProps: (props: WidgetTypes["IconLabel"]) => exclude(props),
|
||||
},
|
||||
ShortcutLabel: {
|
||||
component: ShortcutLabel,
|
||||
getProps: (props: WidgetTypes["ShortcutLabel"]) => {
|
||||
if (!props.shortcut) return undefined;
|
||||
return exclude(props);
|
||||
},
|
||||
},
|
||||
ImageLabel: {
|
||||
component: ImageLabel,
|
||||
getProps: (props: WidgetTypes["ImageLabel"]) => exclude(props),
|
||||
},
|
||||
ImageButton: {
|
||||
component: ImageButton,
|
||||
getProps: (props: WidgetTypes["ImageButton"], index) => ({
|
||||
...exclude(props),
|
||||
action: () => widgetValueCommitAndUpdate(index, undefined, true),
|
||||
}),
|
||||
},
|
||||
NodeCatalog: {
|
||||
component: NodeCatalog,
|
||||
getProps: (props: WidgetTypes["NodeCatalog"], index) => ({
|
||||
...exclude(props),
|
||||
$$events: { selectNodeType: (e: CustomEvent) => widgetValueCommitAndUpdate(index, e.detail, false) },
|
||||
}),
|
||||
},
|
||||
NumberInput: {
|
||||
component: NumberInput,
|
||||
getProps: (props: WidgetTypes["NumberInput"], index) => ({
|
||||
...exclude(props),
|
||||
incrementCallbackIncrease: () => widgetValueCommitAndUpdate(index, "Increment", false),
|
||||
incrementCallbackDecrease: () => widgetValueCommitAndUpdate(index, "Decrement", false),
|
||||
$$events: {
|
||||
value: (e: CustomEvent) => debouncer((value: unknown) => widgetValueUpdate(index, value, true)).debounceUpdateValue(e.detail),
|
||||
startHistoryTransaction: () => widgetValueCommit(index, props.value),
|
||||
},
|
||||
}),
|
||||
},
|
||||
ReferencePointInput: {
|
||||
component: ReferencePointInput,
|
||||
getProps: (props: WidgetTypes["ReferencePointInput"], index) => ({
|
||||
...exclude(props),
|
||||
$$events: { value: (e: CustomEvent) => widgetValueCommitAndUpdate(index, e.detail, true) },
|
||||
}),
|
||||
},
|
||||
PopoverButton: {
|
||||
component: PopoverButton,
|
||||
getProps: (props: WidgetTypes["PopoverButton"]) => ({
|
||||
...exclude(props),
|
||||
layoutTarget,
|
||||
}),
|
||||
},
|
||||
RadioInput: {
|
||||
component: RadioInput,
|
||||
getProps: (props: WidgetTypes["RadioInput"], index) => ({
|
||||
...exclude(props),
|
||||
$$events: { selectedIndex: (e: CustomEvent) => widgetValueCommitAndUpdate(index, e.detail, true) },
|
||||
}),
|
||||
},
|
||||
Separator: {
|
||||
component: Separator,
|
||||
getProps: (props: WidgetTypes["Separator"]) => exclude(props),
|
||||
},
|
||||
WorkingColorsInput: {
|
||||
component: WorkingColorsInput,
|
||||
getProps: (props: WidgetTypes["WorkingColorsInput"]) => exclude(props),
|
||||
},
|
||||
TextAreaInput: {
|
||||
component: TextAreaInput,
|
||||
getProps: (props: WidgetTypes["TextAreaInput"], index) => ({
|
||||
...exclude(props),
|
||||
$$events: { commitText: (e: CustomEvent) => widgetValueCommitAndUpdate(index, e.detail, false) },
|
||||
}),
|
||||
},
|
||||
TextButton: {
|
||||
component: TextButton,
|
||||
getProps: (props: WidgetTypes["TextButton"], index) => ({
|
||||
...exclude(props),
|
||||
action: () => widgetValueCommitAndUpdate(index, [], true),
|
||||
$$events: { selectedEntryValuePath: (e: CustomEvent) => widgetValueCommitAndUpdate(index, e.detail, false) },
|
||||
}),
|
||||
},
|
||||
BreadcrumbTrailButtons: {
|
||||
component: BreadcrumbTrailButtons,
|
||||
getProps: (props: WidgetTypes["BreadcrumbTrailButtons"], index) => ({
|
||||
...exclude(props),
|
||||
action: (breadcrumbIndex: number) => widgetValueCommitAndUpdate(index, breadcrumbIndex, true),
|
||||
}),
|
||||
},
|
||||
TextInput: {
|
||||
component: TextInput,
|
||||
getProps: (props: WidgetTypes["TextInput"], index) => ({
|
||||
...exclude(props),
|
||||
$$events: { commitText: (e: CustomEvent) => widgetValueCommitAndUpdate(index, e.detail, true) },
|
||||
}),
|
||||
},
|
||||
TextLabel: {
|
||||
component: TextLabel,
|
||||
getProps: (props: WidgetTypes["TextLabel"]) => exclude(props, ["value"]),
|
||||
getSlotContent: (props: WidgetTypes["TextLabel"]) => props.value,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<!-- TODO: Refactor this component to use `<svelte:component this={attributesObject} />` to avoid all the separate conditional components -->
|
||||
|
||||
<div class={`widget-span ${className} ${extraClasses}`.trim()} class:narrow class:row={direction === "row"} class:column={direction === "column"}>
|
||||
{#each widgets as component, widgetIndex}
|
||||
{@const checkboxInput = narrowWidgetProps(component.props, "CheckboxInput")}
|
||||
{#if checkboxInput}
|
||||
<CheckboxInput {...exclude(checkboxInput)} on:checked={({ detail }) => widgetValueCommitAndUpdate(widgetIndex, detail, true)} />
|
||||
{/if}
|
||||
{@const colorInput = narrowWidgetProps(component.props, "ColorInput")}
|
||||
{#if colorInput}
|
||||
<ColorInput
|
||||
{...exclude(colorInput)}
|
||||
on:value={({ detail }) => widgetValueUpdate(widgetIndex, detail, false)}
|
||||
on:startHistoryTransaction={() => widgetValueCommit(widgetIndex, colorInput.value)}
|
||||
/>
|
||||
{/if}
|
||||
<!-- TODO: Curves Input is currently unused -->
|
||||
{@const curvesInput = narrowWidgetProps(component.props, "CurveInput")}
|
||||
{#if curvesInput}
|
||||
<CurveInput
|
||||
{...exclude(curvesInput)}
|
||||
on:value={({ detail }) => debouncer((value) => widgetValueCommitAndUpdate(widgetIndex, value, false), { debounceTime: 120 }).debounceUpdateValue(detail)}
|
||||
/>
|
||||
{/if}
|
||||
{@const dropdownInput = narrowWidgetProps(component.props, "DropdownInput")}
|
||||
{#if dropdownInput}
|
||||
<DropdownInput
|
||||
{...exclude(dropdownInput)}
|
||||
on:hoverInEntry={({ detail }) => {
|
||||
return widgetValueUpdate(widgetIndex, detail, false);
|
||||
}}
|
||||
on:hoverOutEntry={({ detail }) => {
|
||||
return widgetValueUpdate(widgetIndex, detail, false);
|
||||
}}
|
||||
on:selectedIndex={({ detail }) => widgetValueCommitAndUpdate(widgetIndex, detail, true)}
|
||||
/>
|
||||
{/if}
|
||||
{@const parameterExposeButton = narrowWidgetProps(component.props, "ParameterExposeButton")}
|
||||
{#if parameterExposeButton}
|
||||
<ParameterExposeButton {...exclude(parameterExposeButton)} action={() => widgetValueCommitAndUpdate(widgetIndex, undefined, true)} />
|
||||
{/if}
|
||||
{@const iconButton = narrowWidgetProps(component.props, "IconButton")}
|
||||
{#if iconButton}
|
||||
<IconButton {...exclude(iconButton)} action={() => widgetValueCommitAndUpdate(widgetIndex, undefined, true)} />
|
||||
{/if}
|
||||
{@const iconLabel = narrowWidgetProps(component.props, "IconLabel")}
|
||||
{#if iconLabel}
|
||||
<IconLabel {...exclude(iconLabel)} />
|
||||
{/if}
|
||||
{@const shortcutLabel = narrowWidgetProps(component.props, "ShortcutLabel")}
|
||||
{@const shortcutLabelShortcut = shortcutLabel?.shortcut ? { ...shortcutLabel, shortcut: shortcutLabel.shortcut } : undefined}
|
||||
{#if shortcutLabel && shortcutLabelShortcut}
|
||||
<ShortcutLabel {...exclude(shortcutLabelShortcut)} />
|
||||
{/if}
|
||||
{@const imageLabel = narrowWidgetProps(component.props, "ImageLabel")}
|
||||
{#if imageLabel}
|
||||
<ImageLabel {...exclude(imageLabel)} />
|
||||
{/if}
|
||||
{@const imageButton = narrowWidgetProps(component.props, "ImageButton")}
|
||||
{#if imageButton}
|
||||
<ImageButton {...exclude(imageButton)} action={() => widgetValueCommitAndUpdate(widgetIndex, undefined, true)} />
|
||||
{/if}
|
||||
{@const nodeCatalog = narrowWidgetProps(component.props, "NodeCatalog")}
|
||||
{#if nodeCatalog}
|
||||
<NodeCatalog {...exclude(nodeCatalog)} on:selectNodeType={(e) => widgetValueCommitAndUpdate(widgetIndex, e.detail, false)} />
|
||||
{/if}
|
||||
{@const numberInput = narrowWidgetProps(component.props, "NumberInput")}
|
||||
{#if numberInput}
|
||||
<NumberInput
|
||||
{...exclude(numberInput)}
|
||||
on:value={({ detail }) => debouncer((value) => widgetValueUpdate(widgetIndex, value, true)).debounceUpdateValue(detail)}
|
||||
on:startHistoryTransaction={() => widgetValueCommit(widgetIndex, numberInput.value)}
|
||||
incrementCallbackIncrease={() => widgetValueCommitAndUpdate(widgetIndex, "Increment", false)}
|
||||
incrementCallbackDecrease={() => widgetValueCommitAndUpdate(widgetIndex, "Decrement", false)}
|
||||
/>
|
||||
{/if}
|
||||
{@const referencePointInput = narrowWidgetProps(component.props, "ReferencePointInput")}
|
||||
{#if referencePointInput}
|
||||
<ReferencePointInput {...exclude(referencePointInput)} on:value={({ detail }) => widgetValueCommitAndUpdate(widgetIndex, detail, true)} />
|
||||
{/if}
|
||||
{@const popoverButton = narrowWidgetProps(component.props, "PopoverButton")}
|
||||
{#if popoverButton}
|
||||
<PopoverButton {...exclude(popoverButton)} {layoutTarget} />
|
||||
{/if}
|
||||
{@const radioInput = narrowWidgetProps(component.props, "RadioInput")}
|
||||
{#if radioInput}
|
||||
<RadioInput {...exclude(radioInput)} on:selectedIndex={({ detail }) => widgetValueCommitAndUpdate(widgetIndex, detail, true)} />
|
||||
{/if}
|
||||
{@const separator = narrowWidgetProps(component.props, "Separator")}
|
||||
{#if separator}
|
||||
<Separator {...exclude(separator)} />
|
||||
{/if}
|
||||
{@const workingColorsInput = narrowWidgetProps(component.props, "WorkingColorsInput")}
|
||||
{#if workingColorsInput}
|
||||
<WorkingColorsInput {...exclude(workingColorsInput)} />
|
||||
{/if}
|
||||
{@const textAreaInput = narrowWidgetProps(component.props, "TextAreaInput")}
|
||||
{#if textAreaInput}
|
||||
<TextAreaInput {...exclude(textAreaInput)} on:commitText={({ detail }) => widgetValueCommitAndUpdate(widgetIndex, detail, false)} />
|
||||
{/if}
|
||||
{@const textButton = narrowWidgetProps(component.props, "TextButton")}
|
||||
{#if textButton}
|
||||
<TextButton
|
||||
{...exclude(textButton)}
|
||||
action={() => widgetValueCommitAndUpdate(widgetIndex, [], true)}
|
||||
on:selectedEntryValuePath={({ detail }) => widgetValueCommitAndUpdate(widgetIndex, detail, false)}
|
||||
/>
|
||||
{/if}
|
||||
{@const breadcrumbTrailButtons = narrowWidgetProps(component.props, "BreadcrumbTrailButtons")}
|
||||
{#if breadcrumbTrailButtons}
|
||||
<BreadcrumbTrailButtons {...exclude(breadcrumbTrailButtons)} action={(breadcrumbIndex) => widgetValueCommitAndUpdate(widgetIndex, breadcrumbIndex, true)} />
|
||||
{/if}
|
||||
{@const textInput = narrowWidgetProps(component.props, "TextInput")}
|
||||
{#if textInput}
|
||||
<TextInput {...exclude(textInput)} on:commitText={({ detail }) => widgetValueCommitAndUpdate(widgetIndex, detail, true)} />
|
||||
{/if}
|
||||
{@const textLabel = narrowWidgetProps(component.props, "TextLabel")}
|
||||
{#if textLabel}
|
||||
<TextLabel {...exclude(textLabel, ["value"])}>{textLabel.value}</TextLabel>
|
||||
{#each widgets as widget, widgetIndex}
|
||||
{@const config = widgetRegistry[widget.props.kind]}
|
||||
{@const props = config?.getProps(widget.props, widgetIndex)}
|
||||
{@const slot = config?.getSlotContent?.(widget.props)}
|
||||
{#if props !== undefined && slot !== undefined}
|
||||
<svelte:component this={config.component} {...props}>{slot}</svelte:component>
|
||||
{:else if props !== undefined}
|
||||
<svelte:component this={config.component} {...props} />
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
|
|
@ -213,8 +268,8 @@
|
|||
.widget-span.row {
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
--row-height: 32px;
|
||||
min-height: var(--row-height);
|
||||
--row-height: 32px;
|
||||
|
||||
&.narrow {
|
||||
--row-height: 24px;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue