mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-02 03:08:12 +08:00
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:
@@ -2,8 +2,8 @@
|
|||||||
import { getContext } from "svelte";
|
import { getContext } from "svelte";
|
||||||
|
|
||||||
import type { Editor } from "@graphite/editor";
|
import type { Editor } from "@graphite/editor";
|
||||||
import type { LayoutTarget, WidgetInstance, WidgetSpanColumn, WidgetSpanRow } from "@graphite/messages";
|
import type { LayoutTarget, WidgetInstance, WidgetPropsNames, WidgetPropsSet, WidgetTypes, WidgetSpanColumn, WidgetSpanRow } from "@graphite/messages";
|
||||||
import { narrowWidgetProps, isWidgetSpanColumn, isWidgetSpanRow } from "@graphite/messages";
|
import { isWidgetSpanColumn, isWidgetSpanRow } from "@graphite/messages";
|
||||||
import { debouncer } from "@graphite/utility-functions/debounce";
|
import { debouncer } from "@graphite/utility-functions/debounce";
|
||||||
|
|
||||||
import NodeCatalog from "@graphite/components/floating-menus/NodeCatalog.svelte";
|
import NodeCatalog from "@graphite/components/floating-menus/NodeCatalog.svelte";
|
||||||
@@ -71,134 +71,189 @@
|
|||||||
editor.handle.widgetValueCommitAndUpdate(layoutTarget, widgets[widgetIndex].widgetId, value, resendWidget);
|
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
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
function exclude<T extends object>(props: T, additional?: (keyof T)[]): Omit<T, typeof additional extends Array<infer K> ? K : never> {
|
function exclude(props: WidgetPropsSet, additional?: string[]): Record<string, any> {
|
||||||
const exclusions = ["kind", ...(additional || [])];
|
const exclusions = new Set(["kind", ...(additional || [])]);
|
||||||
|
return Object.fromEntries(Object.entries(props).filter(([key]) => !exclusions.has(key)));
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
return Object.fromEntries(Object.entries(props).filter((entry) => !exclusions.includes(entry[0]))) as any;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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>
|
</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"}>
|
<div class={`widget-span ${className} ${extraClasses}`.trim()} class:narrow class:row={direction === "row"} class:column={direction === "column"}>
|
||||||
{#each widgets as component, widgetIndex}
|
{#each widgets as widget, widgetIndex}
|
||||||
{@const checkboxInput = narrowWidgetProps(component.props, "CheckboxInput")}
|
{@const config = widgetRegistry[widget.props.kind]}
|
||||||
{#if checkboxInput}
|
{@const props = config?.getProps(widget.props, widgetIndex)}
|
||||||
<CheckboxInput {...exclude(checkboxInput)} on:checked={({ detail }) => widgetValueCommitAndUpdate(widgetIndex, detail, true)} />
|
{@const slot = config?.getSlotContent?.(widget.props)}
|
||||||
{/if}
|
{#if props !== undefined && slot !== undefined}
|
||||||
{@const colorInput = narrowWidgetProps(component.props, "ColorInput")}
|
<svelte:component this={config.component} {...props}>{slot}</svelte:component>
|
||||||
{#if colorInput}
|
{:else if props !== undefined}
|
||||||
<ColorInput
|
<svelte:component this={config.component} {...props} />
|
||||||
{...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>
|
|
||||||
{/if}
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
@@ -213,8 +268,8 @@
|
|||||||
.widget-span.row {
|
.widget-span.row {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
--row-height: 32px;
|
|
||||||
min-height: var(--row-height);
|
min-height: var(--row-height);
|
||||||
|
--row-height: 32px;
|
||||||
|
|
||||||
&.narrow {
|
&.narrow {
|
||||||
--row-height: 24px;
|
--row-height: 24px;
|
||||||
|
|||||||
+12
-20
@@ -1,5 +1,3 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
||||||
|
|
||||||
import { Transform, Type, plainToClass } from "class-transformer";
|
import { Transform, Type, plainToClass } from "class-transformer";
|
||||||
|
|
||||||
import { sampleInterpolatedGradient, type EditorHandle } from "@graphite/../wasm/pkg/graphite_wasm";
|
import { sampleInterpolatedGradient, type EditorHandle } from "@graphite/../wasm/pkg/graphite_wasm";
|
||||||
@@ -971,7 +969,7 @@ export class ColorInput extends WidgetProps {
|
|||||||
return new Gradient(
|
return new Gradient(
|
||||||
gradient.position,
|
gradient.position,
|
||||||
gradient.midpoint,
|
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;
|
] as const;
|
||||||
|
|
||||||
type WidgetSubTypes = (typeof widgetSubTypes)[number];
|
type WidgetSubTypes = (typeof widgetSubTypes)[number];
|
||||||
type WidgetKindMap = { [T in WidgetSubTypes as T["name"]]: InstanceType<T["value"]> };
|
export type WidgetTypes = { [T in WidgetSubTypes as T["name"]]: InstanceType<T["value"]> };
|
||||||
export type WidgetPropsNames = keyof WidgetKindMap;
|
export type WidgetPropsNames = keyof WidgetTypes;
|
||||||
export type WidgetPropsSet = WidgetKindMap[WidgetPropsNames];
|
export type WidgetPropsSet = WidgetTypes[WidgetPropsNames];
|
||||||
|
|
||||||
export function narrowWidgetProps<K extends WidgetPropsNames>(props: WidgetPropsSet, kind: K) {
|
|
||||||
if (props.kind === kind) return props as WidgetKindMap[K];
|
|
||||||
else return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class WidgetInstance {
|
export class WidgetInstance {
|
||||||
@Type(() => WidgetProps, { discriminator: { property: "kind", subTypes: [...widgetSubTypes] }, keepDiscriminatorProperty: true })
|
@Type(() => WidgetProps, { discriminator: { property: "kind", subTypes: [...widgetSubTypes] }, keepDiscriminatorProperty: true })
|
||||||
@@ -1476,6 +1469,7 @@ export class WidgetInstance {
|
|||||||
widgetId!: bigint;
|
widgetId!: bigint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
function hoistWidgetInstance(widgetInstance: any): WidgetInstance {
|
function hoistWidgetInstance(widgetInstance: any): WidgetInstance {
|
||||||
const kind = Object.keys(widgetInstance.widget)[0];
|
const kind = Object.keys(widgetInstance.widget)[0];
|
||||||
const props = widgetInstance.widget[kind];
|
const props = widgetInstance.widget[kind];
|
||||||
@@ -1490,10 +1484,6 @@ function hoistWidgetInstance(widgetInstance: any): WidgetInstance {
|
|||||||
return plainToClass(WidgetInstance, { props, widgetId });
|
return plainToClass(WidgetInstance, { props, widgetId });
|
||||||
}
|
}
|
||||||
|
|
||||||
function hoistWidgetInstances(widgetInstance: any[]): WidgetInstance[] {
|
|
||||||
return widgetInstance.map(hoistWidgetInstance);
|
|
||||||
}
|
|
||||||
|
|
||||||
// WIDGET LAYOUT
|
// WIDGET LAYOUT
|
||||||
|
|
||||||
export type LayoutTarget =
|
export type LayoutTarget =
|
||||||
@@ -1516,7 +1506,6 @@ export type LayoutTarget =
|
|||||||
| "WorkingColors";
|
| "WorkingColors";
|
||||||
|
|
||||||
export class WidgetDiffUpdate extends JsMessage {
|
export class WidgetDiffUpdate extends JsMessage {
|
||||||
// TODO: Replace `any` with correct typing
|
|
||||||
@Transform(({ value }: { value: WidgetDiff[] }) => {
|
@Transform(({ value }: { value: WidgetDiff[] }) => {
|
||||||
// Unpacking rust types to more usable type in the frontend
|
// Unpacking rust types to more usable type in the frontend
|
||||||
return value.map((diff) => {
|
return value.map((diff) => {
|
||||||
@@ -1574,7 +1563,7 @@ export function patchLayout(layout: /* &mut */ Layout, updates: WidgetDiffUpdate
|
|||||||
diffObject.length = 0;
|
diffObject.length = 0;
|
||||||
}
|
}
|
||||||
// Remove all of the keys from the old object
|
// 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
|
// Assign keys to the new object
|
||||||
// `Object.assign` works but `diffObject = update.newValue;` doesn't.
|
// `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
|
// Unpacking a layout group
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
function createLayoutGroup(layoutGroup: any): LayoutGroup {
|
function createLayoutGroup(layoutGroup: any): LayoutGroup {
|
||||||
if (layoutGroup.column) {
|
if (layoutGroup.column) {
|
||||||
const columnWidgets = hoistWidgetInstances(layoutGroup.column.columnWidgets);
|
const columnWidgets = layoutGroup.column.columnWidgets.map(hoistWidgetInstance);
|
||||||
|
|
||||||
const result: WidgetSpanColumn = { columnWidgets };
|
const result: WidgetSpanColumn = { columnWidgets };
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (layoutGroup.row) {
|
if (layoutGroup.row) {
|
||||||
const result: WidgetSpanRow = { rowWidgets: hoistWidgetInstances(layoutGroup.row.rowWidgets) };
|
const result: WidgetSpanRow = { rowWidgets: layoutGroup.row.rowWidgets.map(hoistWidgetInstance) };
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1634,7 +1624,8 @@ function createLayoutGroup(layoutGroup: any): LayoutGroup {
|
|||||||
|
|
||||||
if (layoutGroup.table) {
|
if (layoutGroup.table) {
|
||||||
const result: WidgetTable = {
|
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,
|
unstyled: layoutGroup.table.unstyled,
|
||||||
};
|
};
|
||||||
return result;
|
return result;
|
||||||
@@ -1679,6 +1670,7 @@ export class UpdateToolShelfLayout extends WidgetDiffUpdate {}
|
|||||||
export class UpdateWorkingColorsLayout extends WidgetDiffUpdate {}
|
export class UpdateWorkingColorsLayout extends WidgetDiffUpdate {}
|
||||||
|
|
||||||
// `any` is used since the type of the object should be known from the Rust side
|
// `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 JSMessageFactory = (data: any, wasm: WebAssembly.Memory, handle: EditorHandle) => JsMessage;
|
||||||
type MessageMaker = typeof JsMessage | JSMessageFactory;
|
type MessageMaker = typeof JsMessage | JSMessageFactory;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user