Major frontend code cleanup (#452)

Many large changes, including:
- TypeScript enums are now string unions throughout
- Strong type-checking throughout the TS and Vue codebase
- Vue component props now all specify `as PropType<...>`
- Usage of annotated return types on all functions
- Sorting of JS import statements
- Explicit usage of Vue bind attribute function call arguments (`@click="foo"` is now `@click=(e) => foo(e)`)
- Much improved code quality related to the color picker
- Consistent camelCase Vue bind and v-model attributes
- Consistent Vue HTML attribute strings with single quotes
- Bug fix and clarity improvement with incorrect hint class parameters
- Empty Vue component objects like `props: {}` and `components: {}` removed
This commit is contained in:
Keavon Chambers
2022-01-02 06:00:02 -08:00
parent 1954aceb0e
commit 7e0cbb60b4
53 changed files with 842 additions and 946 deletions
+43 -52
View File
@@ -4,35 +4,35 @@
<div class="left side">
<DropdownInput :menuEntries="documentModeEntries" v-model:selectedIndex="documentModeSelectionIndex" :drawIcon="true" />
<Separator :type="SeparatorType.Section" />
<Separator :type="'Section'" />
<ToolOptions :activeTool="activeTool" :activeToolOptions="activeToolOptions" />
</div>
<div class="spacer"></div>
<div class="right side">
<OptionalInput v-model:checked="snappingEnabled" @update:checked="setSnap" :icon="'Snapping'" title="Snapping" />
<OptionalInput v-model:checked="snappingEnabled" @update:checked="(newStatus) => setSnap(newStatus)" :icon="'Snapping'" title="Snapping" />
<PopoverButton>
<h3>Snapping</h3>
<p>The contents of this popover menu are coming soon</p>
</PopoverButton>
<Separator :type="SeparatorType.Unrelated" />
<Separator :type="'Unrelated'" />
<OptionalInput v-model:checked="gridEnabled" @update:checked="dialog.comingSoon(318)" :icon="'Grid'" title="Grid" />
<OptionalInput v-model:checked="gridEnabled" @update:checked="() => dialog.comingSoon(318)" :icon="'Grid'" title="Grid" />
<PopoverButton>
<h3>Grid</h3>
<p>The contents of this popover menu are coming soon</p>
</PopoverButton>
<Separator :type="SeparatorType.Unrelated" />
<Separator :type="'Unrelated'" />
<OptionalInput v-model:checked="overlaysEnabled" @update:checked="dialog.comingSoon(99)" :icon="'Overlays'" title="Overlays" />
<OptionalInput v-model:checked="overlaysEnabled" @update:checked="() => dialog.comingSoon(99)" :icon="'Overlays'" title="Overlays" />
<PopoverButton>
<h3>Overlays</h3>
<p>The contents of this popover menu are coming soon</p>
</PopoverButton>
<Separator :type="SeparatorType.Unrelated" />
<Separator :type="'Unrelated'" />
<RadioInput :entries="viewModeEntries" v-model:selectedIndex="viewModeIndex" class="combined-after" />
<PopoverButton>
@@ -40,27 +40,27 @@
<p>The contents of this popover menu are coming soon</p>
</PopoverButton>
<Separator :type="SeparatorType.Section" />
<Separator :type="'Section'" />
<NumberInput @update:value="setRotation" v-model:value="documentRotation" :incrementFactor="15" :unit="`°`" />
<NumberInput @update:value="(newRotation) => setRotation(newRotation)" v-model:value="documentRotation" :incrementFactor="15" :unit="'°'" />
<Separator :type="SeparatorType.Section" />
<Separator :type="'Section'" />
<IconButton :action="() => this.$refs.zoom.onIncrement(IncrementDirection.Increase)" :icon="'ZoomIn'" :size="24" title="Zoom In" />
<IconButton :action="() => this.$refs.zoom.onIncrement(IncrementDirection.Decrease)" :icon="'ZoomOut'" :size="24" title="Zoom Out" />
<IconButton :action="() => this.$refs.zoom.onIncrement('Increase')" :icon="'ZoomIn'" :size="24" title="Zoom In" />
<IconButton :action="() => this.$refs.zoom.onIncrement('Decrease')" :icon="'ZoomOut'" :size="24" title="Zoom Out" />
<IconButton :action="() => this.$refs.zoom.updateValue(100)" :icon="'ZoomReset'" :size="24" title="Zoom to 100%" />
<Separator :type="SeparatorType.Related" />
<Separator :type="'Related'" />
<NumberInput
v-model:value="documentZoom"
@update:value="setCanvasZoom"
@update:value="(newZoom) => setCanvasZoom(newZoom)"
:min="0.000001"
:max="1000000"
:incrementBehavior="IncrementBehavior.Callback"
:incrementBehavior="'Callback'"
:incrementCallbackIncrease="increaseCanvasZoom"
:incrementCallbackDecrease="decreaseCanvasZoom"
:unit="`%`"
:unit="'%'"
:displayDecimalPlaces="4"
ref="zoom"
/>
@@ -74,13 +74,13 @@
<ShelfItemInput icon="LayoutNavigateTool" title="Navigate Tool (Z)" :active="activeTool === 'Navigate'" :action="() => selectTool('Navigate')" />
<ShelfItemInput icon="LayoutEyedropperTool" title="Eyedropper Tool (I)" :active="activeTool === 'Eyedropper'" :action="() => selectTool('Eyedropper')" />
<Separator :type="SeparatorType.Section" :direction="SeparatorDirection.Vertical" />
<Separator :type="'Section'" :direction="'Vertical'" />
<ShelfItemInput icon="ParametricTextTool" title="Text Tool (T)" :active="activeTool === 'Text'" :action="() => dialog.comingSoon(153) && selectTool('Text')" />
<ShelfItemInput icon="ParametricFillTool" title="Fill Tool (F)" :active="activeTool === 'Fill'" :action="() => selectTool('Fill')" />
<ShelfItemInput icon="ParametricGradientTool" title="Gradient Tool (H)" :active="activeTool === 'Gradient'" :action="() => dialog.comingSoon() && selectTool('Gradient')" />
<Separator :type="SeparatorType.Section" :direction="SeparatorDirection.Vertical" />
<Separator :type="'Section'" :direction="'Vertical'" />
<ShelfItemInput icon="RasterBrushTool" title="Brush Tool (B)" :active="activeTool === 'Brush'" :action="() => dialog.comingSoon() && selectTool('Brush')" />
<ShelfItemInput icon="RasterHealTool" title="Heal Tool (J)" :active="activeTool === 'Heal'" :action="() => dialog.comingSoon() && selectTool('Heal')" />
@@ -89,7 +89,7 @@
<ShelfItemInput icon="RasterBlurSharpenTool" title="Detail Tool (D)" :active="activeTool === 'BlurSharpen'" :action="() => dialog.comingSoon() && selectTool('BlurSharpen')" />
<ShelfItemInput icon="RasterRelightTool" title="Relight Tool (O)" :active="activeTool === 'Relight'" :action="() => dialog.comingSoon() && selectTool('Relight')" />
<Separator :type="SeparatorType.Section" :direction="SeparatorDirection.Vertical" />
<Separator :type="'Section'" :direction="'Vertical'" />
<ShelfItemInput icon="VectorPathTool" title="Path Tool (A)" :active="activeTool === 'Path'" :action="() => selectTool('Path')" />
<ShelfItemInput icon="VectorPenTool" title="Pen Tool (P)" :active="activeTool === 'Pen'" :action="() => selectTool('Pen')" />
@@ -111,11 +111,11 @@
</LayoutCol>
<LayoutCol :class="'viewport'">
<LayoutRow :class="'bar-area'">
<CanvasRuler :origin="rulerOrigin.x" :majorMarkSpacing="rulerSpacing" :numberInterval="rulerInterval" :direction="RulerDirection.Horizontal" :class="'top-ruler'" />
<CanvasRuler :origin="rulerOrigin.x" :majorMarkSpacing="rulerSpacing" :numberInterval="rulerInterval" :direction="'Horizontal'" :class="'top-ruler'" />
</LayoutRow>
<LayoutRow :class="'canvas-area'">
<LayoutCol :class="'bar-area'">
<CanvasRuler :origin="rulerOrigin.y" :majorMarkSpacing="rulerSpacing" :numberInterval="rulerInterval" :direction="RulerDirection.Vertical" />
<CanvasRuler :origin="rulerOrigin.y" :majorMarkSpacing="rulerSpacing" :numberInterval="rulerInterval" :direction="'Vertical'" />
</LayoutCol>
<LayoutCol :class="'canvas-area'">
<div class="canvas" ref="canvas">
@@ -125,22 +125,22 @@
</LayoutCol>
<LayoutCol :class="'bar-area'">
<PersistentScrollbar
:direction="ScrollbarDirection.Vertical"
:direction="'Vertical'"
:handlePosition="scrollbarPos.y"
@update:handlePosition="translateCanvasY"
@update:handlePosition="(newValue) => translateCanvasY(newValue)"
v-model:handleLength="scrollbarSize.y"
@pressTrack="pageY"
@pressTrack="(delta) => pageY(delta)"
:class="'right-scrollbar'"
/>
</LayoutCol>
</LayoutRow>
<LayoutRow :class="'bar-area'">
<PersistentScrollbar
:direction="ScrollbarDirection.Horizontal"
:direction="'Horizontal'"
:handlePosition="scrollbarPos.x"
@update:handlePosition="translateCanvasX"
@update:handlePosition="(newValue) => translateCanvasX(newValue)"
v-model:handleLength="scrollbarSize.x"
@pressTrack="pageX"
@pressTrack="(delta) => pageX(delta)"
:class="'bottom-scrollbar'"
/>
</LayoutRow>
@@ -247,24 +247,22 @@
import { defineComponent } from "vue";
import { UpdateArtwork, UpdateOverlays, UpdateScrollbars, UpdateRulers, SetActiveTool, SetCanvasZoom, SetCanvasRotation } from "@/dispatcher/js-messages";
import { SeparatorDirection, SeparatorType } from "@/components/widgets/widgets";
import LayoutRow from "@/components/layout/LayoutRow.vue";
import LayoutCol from "@/components/layout/LayoutCol.vue";
import SwatchPairInput from "@/components/widgets/inputs/SwatchPairInput.vue";
import { MenuDirection } from "@/components/widgets/floating-menus/FloatingMenu.vue";
import ShelfItemInput from "@/components/widgets/inputs/ShelfItemInput.vue";
import Separator from "@/components/widgets/separators/Separator.vue";
import PersistentScrollbar, { ScrollbarDirection } from "@/components/widgets/scrollbars/PersistentScrollbar.vue";
import CanvasRuler, { RulerDirection } from "@/components/widgets/rulers/CanvasRuler.vue";
import LayoutRow from "@/components/layout/LayoutRow.vue";
import IconButton from "@/components/widgets/buttons/IconButton.vue";
import PopoverButton from "@/components/widgets/buttons/PopoverButton.vue";
import RadioInput, { RadioEntries } from "@/components/widgets/inputs/RadioInput.vue";
import NumberInput, { IncrementDirection, IncrementBehavior } from "@/components/widgets/inputs/NumberInput.vue";
import DropdownInput from "@/components/widgets/inputs/DropdownInput.vue";
import OptionalInput from "@/components/widgets/inputs/OptionalInput.vue";
import ToolOptions from "@/components/widgets/options/ToolOptions.vue";
import { SectionsOfMenuListEntries } from "@/components/widgets/floating-menus/MenuList.vue";
import DropdownInput from "@/components/widgets/inputs/DropdownInput.vue";
import NumberInput from "@/components/widgets/inputs/NumberInput.vue";
import OptionalInput from "@/components/widgets/inputs/OptionalInput.vue";
import RadioInput, { RadioEntries } from "@/components/widgets/inputs/RadioInput.vue";
import ShelfItemInput from "@/components/widgets/inputs/ShelfItemInput.vue";
import SwatchPairInput from "@/components/widgets/inputs/SwatchPairInput.vue";
import ToolOptions from "@/components/widgets/options/ToolOptions.vue";
import CanvasRuler from "@/components/widgets/rulers/CanvasRuler.vue";
import PersistentScrollbar from "@/components/widgets/scrollbars/PersistentScrollbar.vue";
import Separator from "@/components/widgets/separators/Separator.vue";
export default defineComponent({
inject: ["editor", "dialog"],
@@ -368,14 +366,14 @@ export default defineComponent({
const documentModeEntries: SectionsOfMenuListEntries = [
[
{ label: "Design Mode", icon: "ViewportDesignMode" },
{ label: "Select Mode", icon: "ViewportSelectMode", action: () => this.dialog.comingSoon(330) },
{ label: "Guide Mode", icon: "ViewportGuideMode", action: () => this.dialog.comingSoon(331) },
{ label: "Select Mode", icon: "ViewportSelectMode", action: (): void => this.dialog.comingSoon(330) },
{ label: "Guide Mode", icon: "ViewportGuideMode", action: (): void => this.dialog.comingSoon(331) },
],
];
const viewModeEntries: RadioEntries = [
{ value: "normal", icon: "ViewModeNormal", tooltip: "View Mode: Normal", action: () => this.setViewMode("Normal") },
{ value: "outline", icon: "ViewModeOutline", tooltip: "View Mode: Outline", action: () => this.setViewMode("Outline") },
{ value: "pixels", icon: "ViewModePixels", tooltip: "View Mode: Pixels", action: () => this.dialog.comingSoon(320) },
{ value: "normal", icon: "ViewModeNormal", tooltip: "View Mode: Normal", action: (): void => this.setViewMode("Normal") },
{ value: "outline", icon: "ViewModeOutline", tooltip: "View Mode: Outline", action: (): void => this.setViewMode("Outline") },
{ value: "pixels", icon: "ViewModePixels", tooltip: "View Mode: Pixels", action: (): void => this.dialog.comingSoon(320) },
];
return {
@@ -400,13 +398,6 @@ export default defineComponent({
rulerOrigin: { x: 0, y: 0 },
rulerSpacing: 100,
rulerInterval: 100,
IncrementBehavior,
IncrementDirection,
MenuDirection,
SeparatorDirection,
ScrollbarDirection,
RulerDirection,
SeparatorType,
};
},
components: {
+39 -30
View File
@@ -1,13 +1,27 @@
<template>
<LayoutCol :class="'layer-tree-panel'">
<LayoutRow :class="'options-bar'">
<DropdownInput v-model:selectedIndex="blendModeSelectedIndex" @update:selectedIndex="setLayerBlendMode" :menuEntries="blendModeEntries" :disabled="blendModeDropdownDisabled" />
<DropdownInput
v-model:selectedIndex="blendModeSelectedIndex"
@update:selectedIndex="(newSelectedIndex) => setLayerBlendMode(newSelectedIndex)"
:menuEntries="blendModeEntries"
:disabled="blendModeDropdownDisabled"
/>
<Separator :type="SeparatorType.Related" />
<Separator :type="'Related'" />
<NumberInput v-model:value="opacity" @update:value="setLayerOpacity" :min="0" :max="100" :unit="`%`" :displayDecimalPlaces="2" :label="'Opacity'" :disabled="opacityNumberInputDisabled" />
<NumberInput
v-model:value="opacity"
@update:value="(newOpacity) => setLayerOpacity(newOpacity)"
:min="0"
:max="100"
:unit="'%'"
:displayDecimalPlaces="2"
:label="'Opacity'"
:disabled="opacityNumberInputDisabled"
/>
<Separator :type="SeparatorType.Related" />
<Separator :type="'Related'" />
<PopoverButton>
<h3>Compositing Options</h3>
@@ -15,7 +29,7 @@
</PopoverButton>
</LayoutRow>
<LayoutRow :class="'layer-tree scrollable-y'">
<LayoutCol :class="'list'" ref="layerTreeList" @click="deselectAllLayers" @dragover="updateLine($event)" @dragend="drop()">
<LayoutCol :class="'list'" ref="layerTreeList" @click="() => deselectAllLayers()" @dragover="updateLine($event)" @dragend="drop()">
<div class="layer-row" v-for="(layer, index) in layers" :key="layer.path">
<div class="layer-visibility">
<IconButton
@@ -26,7 +40,7 @@
/>
</div>
<button
v-if="layer.layer_type === LayerTypeOptions.Folder"
v-if="layer.layer_type === 'Folder'"
class="node-connector"
:class="{ expanded: layer.layer_metadata.expanded }"
@click.stop="handleNodeConnectorClick(layer.path)"
@@ -46,7 +60,7 @@
>
<div class="layer-thumbnail" v-html="layer.thumbnail"></div>
<div class="layer-type-icon">
<IconLabel v-if="layer.layer_type === LayerTypeOptions.Folder" :icon="'NodeTypeFolder'" title="Folder" />
<IconLabel v-if="layer.layer_type === 'Folder'" :icon="'NodeTypeFolder'" title="Folder" />
<IconLabel v-else :icon="'NodeTypePath'" title="Path" />
</div>
<div class="layer-name">
@@ -228,19 +242,17 @@
<script lang="ts">
import { defineComponent } from "vue";
import { BlendMode, DisplayFolderTreeStructure, UpdateLayer, LayerPanelEntry, LayerTypeOptions } from "@/dispatcher/js-messages";
import { SeparatorType } from "@/components/widgets/widgets";
import { BlendMode, DisplayFolderTreeStructure, UpdateLayer, LayerPanelEntry } from "@/dispatcher/js-messages";
import LayoutRow from "@/components/layout/LayoutRow.vue";
import LayoutCol from "@/components/layout/LayoutCol.vue";
import Separator from "@/components/widgets/separators/Separator.vue";
import NumberInput from "@/components/widgets/inputs/NumberInput.vue";
import PopoverButton from "@/components/widgets/buttons/PopoverButton.vue";
import { MenuDirection } from "@/components/widgets/floating-menus/FloatingMenu.vue";
import LayoutRow from "@/components/layout/LayoutRow.vue";
import IconButton from "@/components/widgets/buttons/IconButton.vue";
import IconLabel from "@/components/widgets/labels/IconLabel.vue";
import DropdownInput from "@/components/widgets/inputs/DropdownInput.vue";
import PopoverButton from "@/components/widgets/buttons/PopoverButton.vue";
import { SectionsOfMenuListEntries } from "@/components/widgets/floating-menus/MenuList.vue";
import DropdownInput from "@/components/widgets/inputs/DropdownInput.vue";
import NumberInput from "@/components/widgets/inputs/NumberInput.vue";
import IconLabel from "@/components/widgets/labels/IconLabel.vue";
import Separator from "@/components/widgets/separators/Separator.vue";
const blendModeEntries: SectionsOfMenuListEntries<BlendMode> = [
[{ label: "Normal", value: "Normal" }],
@@ -301,13 +313,10 @@ export default defineComponent({
selectionRangeEndLayer: undefined as undefined | LayerPanelEntry,
opacity: 100,
draggingData: undefined as undefined | { path: BigUint64Array; above: boolean; nearestPath: BigUint64Array; insertLine: HTMLDivElement },
MenuDirection,
SeparatorType,
LayerTypeOptions,
};
},
methods: {
layerIndent(layer: LayerPanelEntry): string {
layerIndent(layer: LayerPanelEntry) {
return `${(layer.path.length - 1) * 16}px`;
},
async toggleLayerVisibility(path: BigUint64Array) {
@@ -316,14 +325,12 @@ export default defineComponent({
async handleNodeConnectorClick(path: BigUint64Array) {
this.editor.instance.toggle_layer_expansion(path);
},
async setLayerBlendMode() {
const blendMode = this.blendModeEntries.flat()[this.blendModeSelectedIndex].value;
if (blendMode) {
this.editor.instance.set_blend_mode_for_selected_layers(blendMode);
}
async setLayerBlendMode(newSelectedIndex: number) {
const blendMode = this.blendModeEntries.flat()[newSelectedIndex].value;
if (blendMode) this.editor.instance.set_blend_mode_for_selected_layers(blendMode);
},
async setLayerOpacity() {
this.editor.instance.set_opacity_for_selected_layers(this.opacity);
async setLayerOpacity(newOpacity: number) {
this.editor.instance.set_opacity_for_selected_layers(newOpacity);
},
async selectLayer(clickedLayer: LayerPanelEntry, ctrl: boolean, shift: boolean) {
this.editor.instance.select_layer(clickedLayer.path, ctrl, shift);
@@ -377,7 +384,7 @@ export default defineComponent({
}
}
// Inserting below current row
else if (distance > -closest && distance > -RANGE_TO_INSERT_WITHIN_BOTTOM_FOLDER_NOT_ROOT && distance < 0 && layer.layer_type !== LayerTypeOptions.Folder) {
else if (distance > -closest && distance > -RANGE_TO_INSERT_WITHIN_BOTTOM_FOLDER_NOT_ROOT && distance < 0 && layer.layer_type !== "Folder") {
closest = -distance;
nearestPath = layer.path;
if (child.parentNode && child.parentNode.nextSibling) {
@@ -491,7 +498,8 @@ export default defineComponent({
this.editor.dispatcher.subscribeJsMessage(DisplayFolderTreeStructure, (displayFolderTreeStructure) => {
const path = [] as bigint[];
this.layers = [] as LayerPanelEntry[];
function recurse(folder: DisplayFolderTreeStructure, layers: LayerPanelEntry[], cache: Map<string, LayerPanelEntry>) {
const recurse = (folder: DisplayFolderTreeStructure, layers: LayerPanelEntry[], cache: Map<string, LayerPanelEntry>): void => {
folder.children.forEach((item) => {
// TODO: fix toString
path.push(BigInt(item.layerId.toString()));
@@ -500,7 +508,8 @@ export default defineComponent({
if (item.children.length >= 1) recurse(item, layers, cache);
path.pop();
});
}
};
recurse(displayFolderTreeStructure, this.layers, this.layerCache);
});
+1 -4
View File
@@ -7,8 +7,5 @@
<script lang="ts">
import { defineComponent } from "vue";
export default defineComponent({
components: {},
props: {},
});
export default defineComponent({});
</script>
@@ -7,8 +7,5 @@
<script lang="ts">
import { defineComponent } from "vue";
export default defineComponent({
components: {},
props: {},
});
export default defineComponent({});
</script>