Repository: GraphiteEditor/Graphite
File: frontend/src/components/widgets/options/ToolOptions.vue (Vue)

Code before:
<style lang="scss">
.tool-options {
	height: 100%;
	flex: 0 0 auto;
	display: flex;
	align-items: center;
}
</style>

<script lang="ts">
import { defineComponent } from "vue";

import { comingSoon } from "@/utilities/errors";
import { WidgetRow, SeparatorType, IconButtonWidget } from "@/components/widgets/widgets";

import Separator from "@/components/widgets/separators/Separator.vue";
import IconButton from "@/components/widgets/buttons/IconButton.vue";
import PopoverButton from "@/components/widgets/buttons/PopoverButton.vue";
import NumberInput from "@/components/widgets/inputs/NumberInput.vue";

const wasm = import("@/../wasm/pkg");

export default defineComponent({
	props: {
		activeTool: { type: String },
		currentToolOptions: { type: Object },
	},
	computed: {
		activeToolOptions(): Record<string, object> {
			return (this.$props.currentToolOptions || {})[this.$props.activeTool || ""];
		},
	},
	methods: {
		async updateToolOptions(path: string[], newValue: number) {
			this.setToolOption(path, newValue);
			(await wasm).set_tool_options(this.$props.activeTool || "", this.activeToolOptions);
		},
		async sendToolMessage(message: string | object) {
			(await wasm).send_tool_message(this.$props.activeTool || "", message);
		},
		// Traverses the given path into the active tool's option struct, and sets the value at the path tail
		setToolOption(path: string[], newValue: number) {
			let value = this.activeToolOptions as Record<string, object | number>;
			[this.$props.activeTool || "", ...path.slice(0, -1)].forEach((attr) => {
				value = value[attr] as Record<string, object | number>;
			});
			const final = value as Record<string, number>;
			final[path.slice(-1)[0]] = newValue;
		},
		// Traverses the given path into the active tool's option struct, and returns the value at the path tail
		getToolOption(path: string[]): number {

Review comment (suggestion):
As a bonus, could you cast this with `as PropType<Record<string, object | number>>` (perhaps not with the ` | number` part if the following comment is not correct). Search for another usage of `PropType` as an example (you'll have to import it from `"vue"`).

Code after:

<style lang="scss">
.tool-options {
	height: 100%;
	flex: 0 0 auto;
	display: flex;
	align-items: center;
}
</style>

<script lang="ts">
import { defineComponent, PropType } from "vue";

import { comingSoon } from "@/utilities/errors";
import { WidgetRow, SeparatorType, IconButtonWidget } from "@/components/widgets/widgets";

import Separator from "@/components/widgets/separators/Separator.vue";
import IconButton from "@/components/widgets/buttons/IconButton.vue";
import PopoverButton from "@/components/widgets/buttons/PopoverButton.vue";
import NumberInput from "@/components/widgets/inputs/NumberInput.vue";

const wasm = import("@/../wasm/pkg");

export default defineComponent({
	props: {
		activeTool: { type: String },
		activeToolOptions: { type: Object as PropType<Record<string, object>> },
	},
	methods: {
		async updateToolOptions(path: string[], newValue: number) {
			this.setToolOption(path, newValue);
			(await wasm).set_tool_options(this.activeTool || "", this.activeToolOptions);
		},
		async sendToolMessage(message: string | object) {
			(await wasm).send_tool_message(this.activeTool || "", message);
		},
		// Traverses the given path and returns the direct parent of the option
		getRecordContainingOption(optionPath: string[]): Record<string, number> {
			const allButLast = optionPath.slice(0, -1);
			let currentRecord = this.activeToolOptions as Record<string, object | number>;
			[this.activeTool || "", ...allButLast].forEach((attr) => {
				currentRecord = currentRecord[attr] as Record<string, object | number>;
			});
			return currentRecord as Record<string, number>;
		},
		// Traverses the given path into the active tool's option struct, and sets the value at the path tail
		setToolOption(optionPath: string[], newValue: number) {
			const last = optionPath.slice(-1)[0];
			const recordContainingOption = this.getRecordContainingOption(optionPath);
			recordContainingOption[last] = newValue;
		},