Skip to content

Commit 3f35c8d

Browse files
FuriouZzKeavon
authored andcommitted
Refactor frontend input components to use the v-model Vue pattern (#224)
* Use v-model for inputs * Add opacity to LayerTree * Fix FloatingMenu typing
1 parent bb3293a commit 3f35c8d

File tree

7 files changed

+65
-33
lines changed

7 files changed

+65
-33
lines changed

client/web/src/components/panels/Document.vue

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252
</div>
5353
<div class="spacer"></div>
5454
<div class="right side">
55-
<RadioInput :initialIndex="0" @changed="viewModeChanged">
55+
<RadioInput v-model:index="viewModeIndex">
5656
<IconButton :icon="'ViewModeNormal'" :size="24" title="View Mode: Normal" />
5757
<IconButton :icon="'ViewModeOutline'" :size="24" title="View Mode: Outline" />
5858
<IconButton :icon="'ViewModePixels'" :size="24" title="View Mode: Pixels" />
@@ -236,6 +236,7 @@ export default defineComponent({
236236
select_tool(toolName);
237237
},
238238
async viewModeChanged(toolIndex: number) {
239+
console.log(toolIndex);
239240
function todo(_: number) {
240241
return _;
241242
}
@@ -269,6 +270,8 @@ export default defineComponent({
269270
270271
window.addEventListener("keyup", (e: KeyboardEvent) => this.keyUp(e));
271272
window.addEventListener("keydown", (e: KeyboardEvent) => this.keyDown(e));
273+
274+
this.$watch("viewModeIndex", this.viewModeChanged);
272275
},
273276
data() {
274277
return {
@@ -278,6 +281,7 @@ export default defineComponent({
278281
SeparatorDirection,
279282
SeparatorType,
280283
modeMenuEntries,
284+
viewModeIndex: 0,
281285
};
282286
},
283287
components: {

client/web/src/components/panels/LayerTree.vue

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
<Separator :type="SeparatorType.Related" />
77

8-
<NumberInput :value="100" :unit="`%`" />
8+
<NumberInput v-model:value="opacity" :min="0" :max="100" :step="1" :unit="`%`" />
99

1010
<Separator :type="SeparatorType.Related" />
1111

@@ -222,6 +222,7 @@ export default defineComponent({
222222
layers: [] as Array<LayerPanelEntry>,
223223
selectionRangeStartLayer: undefined as LayerPanelEntry | undefined,
224224
selectionRangeEndLayer: undefined as LayerPanelEntry | undefined,
225+
opacity: 100,
225226
};
226227
},
227228
components: {

client/web/src/components/widgets/floating-menus/FloatingMenu.vue

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<div class="floating-menu" :class="[direction.toLowerCase(), type.toLowerCase()]" v-if="open" ref="floatingMenu">
33
<div class="tail" v-if="type === MenuType.Popover"></div>
44
<div class="floating-menu-container" ref="floatingMenuContainer">
5-
<div class="floating-menu-content" ref="floatingMenuContent" :style="{ minWidth: minWidth > 0 ? `${minWidth}px` : undefined }">
5+
<div class="floating-menu-content" ref="floatingMenuContent" :style="floatingMenuContentStyle">
66
<slot></slot>
77
</div>
88
</div>
@@ -350,5 +350,12 @@ export default defineComponent({
350350
}
351351
},
352352
},
353+
computed: {
354+
floatingMenuContentStyle(): Partial<CSSStyleDeclaration> {
355+
return {
356+
minWidth: this.minWidth > 0 ? `${this.minWidth}px` : "",
357+
};
358+
},
359+
},
353360
});
354361
</script>

client/web/src/components/widgets/floating-menus/MenuList.vue

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
v-for="(entry, entryIndex) in section"
77
:key="entryIndex"
88
class="row"
9-
:class="{ open: isMenuEntryOpen(entry), active: entry === activeEntry }"
9+
:class="{ open: isMenuEntryOpen(entry), active: entry === currentEntry }"
1010
@click="handleEntryClick(entry)"
1111
@mouseenter="handleEntryMouseEnter(entry)"
1212
@mouseleave="handleEntryMouseLeave(entry)"
@@ -22,9 +22,8 @@
2222
v-if="entry.children"
2323
:direction="MenuDirection.TopRight"
2424
:menuEntries="entry.children"
25-
:activeEntry="activeEntry"
25+
v-model:active-entry="currentEntry"
2626
:minWidth="minWidth"
27-
:defaultAction="defaultAction"
2827
:drawIcon="drawIcon"
2928
:ref="(ref) => setEntryRefs(entry, ref)"
3029
/>
@@ -146,8 +145,6 @@ const MenuList = defineComponent({
146145
menuEntries: { type: Array as PropType<SectionsOfMenuListEntries>, required: true },
147146
activeEntry: { type: Object as PropType<MenuListEntry>, required: false },
148147
minWidth: { type: Number, default: 0 },
149-
defaultAction: { type: Function, required: false },
150-
widthChanged: { type: Function, required: false },
151148
drawIcon: { type: Boolean, default: false },
152149
},
153150
methods: {
@@ -157,8 +154,11 @@ const MenuList = defineComponent({
157154
handleEntryClick(menuEntry: MenuListEntry) {
158155
(this.$refs.floatingMenu as typeof FloatingMenu).setClosed();
159156
160-
if (menuEntry.action) menuEntry.action();
161-
else if (this.defaultAction) this.defaultAction(menuEntry);
157+
if (menuEntry.action) {
158+
menuEntry.action();
159+
} else {
160+
this.$emit("update:activeEntry", menuEntry);
161+
}
162162
},
163163
handleEntryMouseEnter(menuEntry: MenuListEntry) {
164164
if (!menuEntry.children || !menuEntry.children.length) return;
@@ -193,9 +193,6 @@ const MenuList = defineComponent({
193193
return Boolean(floatingMenu && floatingMenu.isOpen());
194194
},
195195
measureAndReportWidth() {
196-
const { widthChanged } = this;
197-
if (!widthChanged) return;
198-
199196
// API is experimental but supported in all browsers - https://developer.mozilla.org/en-US/docs/Web/API/FontFaceSet
200197
// eslint-disable-next-line @typescript-eslint/no-explicit-any
201198
(document as any).fonts.ready.then(() => {
@@ -212,7 +209,7 @@ const MenuList = defineComponent({
212209
// Restore open/closed state if it was forced open for measurement
213210
if (!initiallyOpen) floatingMenu.setClosed();
214211
215-
widthChanged(width);
212+
this.$emit("width-changed", width);
216213
});
217214
});
218215
});
@@ -246,6 +243,7 @@ const MenuList = defineComponent({
246243
},
247244
data() {
248245
return {
246+
currentEntry: this.activeEntry,
249247
SeparatorDirection,
250248
SeparatorType,
251249
MenuDirection,

client/web/src/components/widgets/inputs/DropdownInput.vue

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,7 @@
55
<span>{{ activeEntry.label }}</span>
66
<Icon :class="'dropdown-arrow'" :icon="'DropdownArrow'" />
77
</div>
8-
<MenuList
9-
:menuEntries="menuEntries"
10-
:activeEntry="activeEntry"
11-
:defaultAction="setActiveEntry"
12-
:direction="MenuDirection.Bottom"
13-
:widthChanged="widthChanged"
14-
:drawIcon="drawIcon"
15-
ref="menuList"
16-
/>
8+
<MenuList :menuEntries="menuEntries" v-model:active-entry="activeEntry" :direction="MenuDirection.Bottom" @width-changed="onWidthChanged" :drawIcon="drawIcon" ref="menuList" />
179
</div>
1810
</template>
1911

@@ -101,7 +93,7 @@ export default defineComponent({
10193
setActiveEntry(newActiveEntry: MenuListEntry) {
10294
this.activeEntry = newActiveEntry;
10395
},
104-
widthChanged(newWidth: number) {
96+
onWidthChanged(newWidth: number) {
10597
this.minWidth = newWidth;
10698
},
10799
},

client/web/src/components/widgets/inputs/NumberInput.vue

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
<template>
22
<div class="number-input">
3-
<button class="arrow left"></button>
4-
<button class="arrow right"></button>
5-
<input type="text" spellcheck="false" :value="`${value}${unit}`" />
3+
<button class="arrow left" @click="onIncrement(-1)"></button>
4+
<button class="arrow right" @click="onIncrement(1)"></button>
5+
<input type="text" spellcheck="false" :value="displayValue" />
66
</div>
77
</template>
88

@@ -99,7 +99,37 @@ export default defineComponent({
9999
components: {},
100100
props: {
101101
value: { type: Number, required: true },
102-
unit: { type: String, default: "" },
102+
unit: { type: String, default: "", required: false },
103+
step: { type: Number, default: 1, required: false },
104+
min: { type: Number, required: false },
105+
max: { type: Number, required: false },
106+
},
107+
computed: {
108+
displayValue(): string {
109+
if (!this.unit) return this.value.toString();
110+
return `${this.value}${this.unit}`;
111+
},
112+
},
113+
methods: {
114+
onIncrement(direction: number) {
115+
const step = this.step * direction;
116+
const newValue = this.value + step;
117+
this.updateValue(newValue);
118+
},
119+
120+
updateValue(newValue: number) {
121+
let value = newValue;
122+
123+
if (Number.isFinite(this.min) && typeof this.min === "number") {
124+
value = Math.max(value, this.min);
125+
}
126+
127+
if (Number.isFinite(this.max) && typeof this.max === "number") {
128+
value = Math.min(value, this.max);
129+
}
130+
131+
this.$emit("update:value", value);
132+
},
103133
},
104134
});
105135
</script>

client/web/src/components/widgets/inputs/RadioInput.vue

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,21 +51,19 @@ import { defineComponent } from "vue";
5151
export default defineComponent({
5252
components: {},
5353
props: {
54-
initialIndex: { type: Number, required: true },
55-
setIndex: { type: Function, required: false },
54+
index: { type: Number, required: true },
5655
},
5756
data() {
5857
return {
59-
activeIndex: this.initialIndex,
58+
activeIndex: this.index,
6059
};
6160
},
6261
mounted() {
6362
this.updateActiveIconButton();
6463
6564
(this.$refs.radioInput as Element).querySelectorAll(".icon-button").forEach((iconButton, index) => {
6665
iconButton.addEventListener("click", () => {
67-
this.activeIndex = index;
68-
this.$emit("changed", index);
66+
this.setActive(index);
6967
});
7068
});
7169
},
@@ -78,6 +76,8 @@ export default defineComponent({
7876
// This method may be called by the user of this component by setting a `ref="radioInput"` attribute and calling `(this.$refs.viewModePicker as typeof RadioInput).setActive(...)`
7977
setActive(index: number) {
8078
this.activeIndex = index;
79+
this.$emit("update:index", index);
80+
this.$emit("changed", index);
8181
},
8282
updateActiveIconButton() {
8383
const iconButtons = (this.$refs.radioInput as Element).querySelectorAll(".icon-button");

0 commit comments

Comments
 (0)