mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-21 13:58:13 +08:00
Welcome screen, refactor to allow zero documents, and add TS typing to widgets (#702)
* unfinished implementation * Add frontend for the empty panel screen * Add an icon for Folder based on NodeFolder * fixed messages causing peicees of ui not to render on new document * Standardize nextTick syntax * WIP generisization of component subscriptions (not compiling yet) * Fix crash when loading font and there is no active document * Only advertise tool actions with a document * Fix failure to create new document * Initalise the properties panel * Fix highlight tab, canvas jump, warns and layer tree * Fix tests * Possibly fix some things? * Move WorkingColors layout definition to backend * Standardize action macro formatting * Provide typing for widgets in TS/Vue and associated cleanup * Fix viewport positioning initialization * Fix menu bar init at startup not document creation * Fix no viewport bounds bug * Change !=0 to >0 * Simplify the init system Closes #656 Co-authored-by: Keavon Chambers <keavon@keavon.com> Co-authored-by: 0hypercube <0hypercube@gmail.com>
This commit is contained in:
co-authored by
Keavon Chambers
0hypercube
parent
b6793517e2
commit
010ba5a568
@@ -4,35 +4,41 @@
|
||||
<template>
|
||||
<div :class="`widget-${direction}`">
|
||||
<template v-for="(component, index) in widgets" :key="index">
|
||||
<CheckboxInput v-if="component.kind === 'CheckboxInput'" v-bind="component.props" @update:checked="(value: boolean) => updateLayout(component.widget_id, value)" />
|
||||
<ColorInput v-if="component.kind === 'ColorInput'" v-bind="component.props" v-model:open="open" @update:value="(value: string) => updateLayout(component.widget_id, value)" />
|
||||
<DropdownInput v-if="component.kind === 'DropdownInput'" v-bind="component.props" v-model:open="open" @update:selectedIndex="(value: number) => updateLayout(component.widget_id, value)" />
|
||||
<FontInput
|
||||
v-if="component.kind === 'FontInput'"
|
||||
<CheckboxInput v-if="component.props.kind === 'CheckboxInput'" v-bind="component.props" @update:checked="(value: boolean) => updateLayout(component.widgetId, value)" />
|
||||
<ColorInput v-if="component.props.kind === 'ColorInput'" v-bind="component.props" v-model:open="open" @update:value="(value: string) => updateLayout(component.widgetId, value)" />
|
||||
<DropdownInput
|
||||
v-if="component.props.kind === 'DropdownInput'"
|
||||
v-bind="component.props"
|
||||
v-model:open="open"
|
||||
@changeFont="(value: { name: string, style: string, file: string }) => updateLayout(component.widget_id, value)"
|
||||
@update:selectedIndex="(value: number) => updateLayout(component.widgetId, value)"
|
||||
/>
|
||||
<IconButton v-if="component.kind === 'IconButton'" v-bind="component.props" :action="() => updateLayout(component.widget_id, null)" />
|
||||
<IconLabel v-if="component.kind === 'IconLabel'" v-bind="component.props" />
|
||||
<NumberInput
|
||||
v-if="component.kind === 'NumberInput'"
|
||||
<FontInput
|
||||
v-if="component.props.kind === 'FontInput'"
|
||||
v-bind="component.props"
|
||||
@update:value="(value: number) => updateLayout(component.widget_id, value)"
|
||||
:incrementCallbackIncrease="() => updateLayout(component.widget_id, 'Increment')"
|
||||
:incrementCallbackDecrease="() => updateLayout(component.widget_id, 'Decrement')"
|
||||
v-model:open="open"
|
||||
@changeFont="(value: { name: string, style: string, file: string }) => updateLayout(component.widgetId, value)"
|
||||
/>
|
||||
<OptionalInput v-if="component.kind === 'OptionalInput'" v-bind="component.props" @update:checked="(value: boolean) => updateLayout(component.widget_id, value)" />
|
||||
<PopoverButton v-if="component.kind === 'PopoverButton'">
|
||||
<h3>{{ component.props.title }}</h3>
|
||||
<IconButton v-if="component.props.kind === 'IconButton'" v-bind="component.props" :action="() => updateLayout(component.widgetId, null)" />
|
||||
<IconLabel v-if="component.props.kind === 'IconLabel'" v-bind="component.props" />
|
||||
<NumberInput
|
||||
v-if="component.props.kind === 'NumberInput'"
|
||||
v-bind="component.props"
|
||||
@update:value="(value: number) => updateLayout(component.widgetId, value)"
|
||||
:incrementCallbackIncrease="() => updateLayout(component.widgetId, 'Increment')"
|
||||
:incrementCallbackDecrease="() => updateLayout(component.widgetId, 'Decrement')"
|
||||
/>
|
||||
<OptionalInput v-if="component.props.kind === 'OptionalInput'" v-bind="component.props" @update:checked="(value: boolean) => updateLayout(component.widgetId, value)" />
|
||||
<PopoverButton v-if="component.props.kind === 'PopoverButton'" v-bind="component.props">
|
||||
<h3>{{ component.props.header }}</h3>
|
||||
<p>{{ component.props.text }}</p>
|
||||
</PopoverButton>
|
||||
<RadioInput v-if="component.kind === 'RadioInput'" v-bind="component.props" @update:selectedIndex="(value: number) => updateLayout(component.widget_id, value)" />
|
||||
<Separator v-if="component.kind === 'Separator'" v-bind="component.props" />
|
||||
<TextAreaInput v-if="component.kind === 'TextAreaInput'" v-bind="component.props" @commitText="(value: string) => updateLayout(component.widget_id, value)" />
|
||||
<TextButton v-if="component.kind === 'TextButton'" v-bind="component.props" :action="() => updateLayout(component.widget_id, null)" />
|
||||
<TextInput v-if="component.kind === 'TextInput'" v-bind="component.props" @commitText="(value: string) => updateLayout(component.widget_id, value)" />
|
||||
<TextLabel v-if="component.kind === 'TextLabel'" v-bind="withoutValue(component.props)">{{ component.props.value }}</TextLabel>
|
||||
<RadioInput v-if="component.props.kind === 'RadioInput'" v-bind="component.props" @update:selectedIndex="(value: number) => updateLayout(component.widgetId, value)" />
|
||||
<Separator v-if="component.props.kind === 'Separator'" v-bind="component.props" />
|
||||
<SwatchPairInput v-if="component.props.kind === 'SwatchPairInput'" v-bind="component.props" />
|
||||
<TextAreaInput v-if="component.props.kind === 'TextAreaInput'" v-bind="component.props" @commitText="(value: string) => updateLayout(component.widgetId, value)" />
|
||||
<TextButton v-if="component.props.kind === 'TextButton'" v-bind="component.props" :action="() => updateLayout(component.widgetId, null)" />
|
||||
<TextInput v-if="component.props.kind === 'TextInput'" v-bind="component.props" @commitText="(value: string) => updateLayout(component.widgetId, value)" />
|
||||
<TextLabel v-if="component.props.kind === 'TextLabel'" v-bind="withoutValue(component.props)">{{ component.props.value }}</TextLabel>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
@@ -84,6 +90,7 @@ import FontInput from "@/components/widgets/inputs/FontInput.vue";
|
||||
import NumberInput from "@/components/widgets/inputs/NumberInput.vue";
|
||||
import OptionalInput from "@/components/widgets/inputs/OptionalInput.vue";
|
||||
import RadioInput from "@/components/widgets/inputs/RadioInput.vue";
|
||||
import SwatchPairInput from "@/components/widgets/inputs/SwatchPairInput.vue";
|
||||
import TextAreaInput from "@/components/widgets/inputs/TextAreaInput.vue";
|
||||
import TextInput from "@/components/widgets/inputs/TextInput.vue";
|
||||
import IconLabel from "@/components/widgets/labels/IconLabel.vue";
|
||||
@@ -123,21 +130,22 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
components: {
|
||||
Separator,
|
||||
PopoverButton,
|
||||
TextButton,
|
||||
CheckboxInput,
|
||||
NumberInput,
|
||||
TextInput,
|
||||
IconButton,
|
||||
OptionalInput,
|
||||
RadioInput,
|
||||
DropdownInput,
|
||||
TextLabel,
|
||||
IconLabel,
|
||||
ColorInput,
|
||||
DropdownInput,
|
||||
FontInput,
|
||||
IconButton,
|
||||
IconLabel,
|
||||
NumberInput,
|
||||
OptionalInput,
|
||||
PopoverButton,
|
||||
RadioInput,
|
||||
Separator,
|
||||
SwatchPairInput,
|
||||
TextAreaInput,
|
||||
TextButton,
|
||||
TextInput,
|
||||
TextLabel,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<button :class="['icon-button', `size-${size}`, active && 'active']" @click="(e: MouseEvent) => action(e)">
|
||||
<button :class="['icon-button', `size-${size}`, active && 'active']" @click="(e: MouseEvent) => action(e)" :title="tooltip">
|
||||
<IconLabel :icon="icon" />
|
||||
</button>
|
||||
</template>
|
||||
@@ -69,11 +69,13 @@ import IconLabel from "@/components/widgets/labels/IconLabel.vue";
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
action: { type: Function as PropType<(e?: MouseEvent) => void>, required: true },
|
||||
icon: { type: String as PropType<IconName>, required: true },
|
||||
size: { type: Number as PropType<IconSize>, required: true },
|
||||
active: { type: Boolean as PropType<boolean>, default: false },
|
||||
gapAfter: { type: Boolean as PropType<boolean>, default: false },
|
||||
tooltip: { type: String as PropType<string | undefined>, required: false },
|
||||
|
||||
// Callbacks
|
||||
action: { type: Function as PropType<(e?: MouseEvent) => void>, required: true },
|
||||
},
|
||||
components: { IconLabel },
|
||||
});
|
||||
|
||||
@@ -62,8 +62,10 @@ export default defineComponent({
|
||||
LayoutRow,
|
||||
},
|
||||
props: {
|
||||
action: { type: Function as PropType<() => void>, required: false },
|
||||
icon: { type: String as PropType<IconName>, default: "DropdownArrow" },
|
||||
|
||||
// Callbacks
|
||||
action: { type: Function as PropType<() => void>, required: false },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
// TODO: Try and get rid of the need for this file
|
||||
|
||||
export interface TextButtonWidget {
|
||||
kind: "TextButton";
|
||||
tooltip?: string;
|
||||
message?: string | object;
|
||||
callback?: () => void;
|
||||
props: {
|
||||
// `action` is used via `IconButtonWidget.callback`
|
||||
kind: "TextButton";
|
||||
label: string;
|
||||
emphasized?: boolean;
|
||||
disabled?: boolean;
|
||||
minWidth?: number;
|
||||
gapAfter?: boolean;
|
||||
disabled?: boolean;
|
||||
|
||||
// Callbacks
|
||||
// `action` is used via `IconButtonWidget.callback`
|
||||
};
|
||||
}
|
||||
|
||||
@@ -62,12 +62,13 @@ import TextLabel from "@/components/widgets/labels/TextLabel.vue";
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
action: { type: Function as PropType<(e: MouseEvent) => void>, required: true },
|
||||
label: { type: String as PropType<string>, required: true },
|
||||
emphasized: { type: Boolean as PropType<boolean>, default: false },
|
||||
disabled: { type: Boolean as PropType<boolean>, default: false },
|
||||
minWidth: { type: Number as PropType<number>, default: 0 },
|
||||
gapAfter: { type: Boolean as PropType<boolean>, default: false },
|
||||
disabled: { type: Boolean as PropType<boolean>, default: false },
|
||||
|
||||
// Callbacks
|
||||
action: { type: Function as PropType<(e: MouseEvent) => void>, required: true },
|
||||
},
|
||||
components: { TextLabel },
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<LayoutRow class="checkbox-input">
|
||||
<input type="checkbox" :id="`checkbox-input-${id}`" :checked="checked" @change="(e) => $emit('update:checked', (e.target as HTMLInputElement).checked)" />
|
||||
<label :for="`checkbox-input-${id}`" :tabindex="disableTabIndex ? -1 : 0" @keydown.enter="(e) => ((e.target as HTMLElement).previousSibling as HTMLInputElement).click()">
|
||||
<label :for="`checkbox-input-${id}`" tabindex="0" @keydown.enter="(e) => ((e.target as HTMLElement).previousSibling as HTMLInputElement).click()" :title="tooltip">
|
||||
<LayoutRow class="checkbox-box">
|
||||
<IconLabel :icon="icon" />
|
||||
</LayoutRow>
|
||||
@@ -66,6 +66,11 @@ import IconLabel from "@/components/widgets/labels/IconLabel.vue";
|
||||
|
||||
export default defineComponent({
|
||||
emits: ["update:checked"],
|
||||
props: {
|
||||
checked: { type: Boolean as PropType<boolean>, default: false },
|
||||
icon: { type: String as PropType<IconName>, default: "Checkmark" },
|
||||
tooltip: { type: String as PropType<string | undefined>, required: false },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
id: `${Math.random()}`.substring(2),
|
||||
@@ -76,11 +81,6 @@ export default defineComponent({
|
||||
return this.checked;
|
||||
},
|
||||
},
|
||||
props: {
|
||||
checked: { type: Boolean as PropType<boolean>, default: false },
|
||||
icon: { type: String as PropType<IconName>, default: "Checkmark" },
|
||||
disableTabIndex: { type: Boolean as PropType<boolean>, default: false },
|
||||
},
|
||||
components: {
|
||||
IconLabel,
|
||||
LayoutRow,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<LayoutRow class="color-input">
|
||||
<OptionalInput v-if="canSetTransparent" :icon="'CloseX'" :checked="!!value" @update:checked="(val) => updateEnabled(val)"></OptionalInput>
|
||||
<LayoutRow class="color-input" :title="tooltip">
|
||||
<OptionalInput v-if="!noTransparency" :icon="'CloseX'" :checked="Boolean(value)" @update:checked="(val) => updateEnabled(val)"></OptionalInput>
|
||||
<TextInput :value="displayValue" :label="label" :disabled="disabled || !value" @commitText="(value: string) => textInputUpdated(value)" :center="true" />
|
||||
<Separator :type="'Related'" />
|
||||
<LayoutRow class="swatch">
|
||||
@@ -82,11 +82,15 @@ import Separator from "@/components/widgets/labels/Separator.vue";
|
||||
export default defineComponent({
|
||||
emits: ["update:value", "update:open"],
|
||||
props: {
|
||||
value: { type: String as PropType<string | undefined>, required: true },
|
||||
open: { type: Boolean as PropType<boolean>, required: true },
|
||||
value: { type: String as PropType<string | undefined>, required: false },
|
||||
label: { type: String as PropType<string>, required: false },
|
||||
canSetTransparent: { type: Boolean as PropType<boolean>, required: false, default: true },
|
||||
noTransparency: { type: Boolean as PropType<boolean>, default: false },
|
||||
disabled: { type: Boolean as PropType<boolean>, default: false },
|
||||
tooltip: { type: String as PropType<string | undefined>, required: false },
|
||||
|
||||
// Bound through `v-model`
|
||||
// TODO: See if this should be made to follow the pattern of DropdownInput.vue so this could be removed
|
||||
open: { type: Boolean as PropType<boolean>, required: true },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
@@ -99,7 +99,9 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType, toRaw } from "vue";
|
||||
|
||||
import MenuList, { MenuListEntry, SectionsOfMenuListEntries } from "@/components/floating-menus/MenuList.vue";
|
||||
import { MenuListEntry, SectionsOfMenuListEntries } from "@/wasm-communication/messages";
|
||||
|
||||
import MenuList from "@/components/floating-menus/MenuList.vue";
|
||||
import LayoutRow from "@/components/layout/LayoutRow.vue";
|
||||
import IconLabel from "@/components/widgets/labels/IconLabel.vue";
|
||||
|
||||
|
||||
@@ -71,9 +71,10 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent, nextTick, PropType } from "vue";
|
||||
|
||||
import FloatingMenu from "@/components/floating-menus/FloatingMenu.vue";
|
||||
import MenuList, { MenuListEntry } from "@/components/floating-menus/MenuList.vue";
|
||||
import { MenuListEntry } from "@/wasm-communication/messages";
|
||||
|
||||
import FloatingMenu from "@/components/floating-menus/FloatingMenu.vue";
|
||||
import MenuList from "@/components/floating-menus/MenuList.vue";
|
||||
import LayoutCol from "@/components/layout/LayoutCol.vue";
|
||||
import LayoutRow from "@/components/layout/LayoutRow.vue";
|
||||
import IconLabel from "@/components/widgets/labels/IconLabel.vue";
|
||||
@@ -84,8 +85,8 @@ export default defineComponent({
|
||||
props: {
|
||||
fontFamily: { type: String as PropType<string>, required: true },
|
||||
fontStyle: { type: String as PropType<string>, required: true },
|
||||
disabled: { type: Boolean as PropType<boolean>, default: false },
|
||||
isStyle: { type: Boolean as PropType<boolean>, default: false },
|
||||
disabled: { type: Boolean as PropType<boolean>, default: false },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -111,6 +112,7 @@ export default defineComponent({
|
||||
},
|
||||
async setOpen() {
|
||||
this.open = true;
|
||||
|
||||
// Scroll to the active entry (the scroller div does not yet exist so we must wait for vue to render)
|
||||
await nextTick();
|
||||
if (this.activeEntry) {
|
||||
|
||||
@@ -72,9 +72,9 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
import { MenuEntry, UpdateMenuBarLayout } from "@/wasm-communication/messages";
|
||||
import { MenuEntry, UpdateMenuBarLayout, MenuListEntry } from "@/wasm-communication/messages";
|
||||
|
||||
import MenuList, { MenuListEntry } from "@/components/floating-menus/MenuList.vue";
|
||||
import MenuList from "@/components/floating-menus/MenuList.vue";
|
||||
import IconLabel from "@/components/widgets/labels/IconLabel.vue";
|
||||
|
||||
const LOCK_REQUIRING_SHORTCUTS = [
|
||||
@@ -100,7 +100,7 @@ export default defineComponent({
|
||||
group.map((entry) => ({
|
||||
...entry,
|
||||
children: entry.children ? menuEntryToFrontendMenuEntry(entry.children) : undefined,
|
||||
action: (): void => this.editor.instance.update_layout(updateMenuBarLayout.layout_target, entry.action.widget_id, undefined),
|
||||
action: (): void => this.editor.instance.update_layout(updateMenuBarLayout.layout_target, entry.action.widgetId, undefined),
|
||||
shortcutRequiresLock: entry.shortcut ? shortcutRequiresLock(entry.shortcut) : undefined,
|
||||
}))
|
||||
);
|
||||
|
||||
@@ -87,27 +87,30 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType } from "vue";
|
||||
|
||||
import { IncrementBehavior } from "@/wasm-communication/messages";
|
||||
|
||||
import FieldInput from "@/components/widgets/inputs/FieldInput.vue";
|
||||
|
||||
type IncrementBehavior = "Add" | "Multiply" | "Callback" | "None";
|
||||
type IncrementDirection = "Decrease" | "Increase";
|
||||
export type IncrementDirection = "Decrease" | "Increase";
|
||||
|
||||
export default defineComponent({
|
||||
emits: ["update:value"],
|
||||
props: {
|
||||
label: { type: String as PropType<string>, required: false },
|
||||
value: { type: Number as PropType<number>, required: false }, // When not provided, a dash is displayed
|
||||
min: { type: Number as PropType<number>, required: false },
|
||||
max: { type: Number as PropType<number>, required: false },
|
||||
incrementBehavior: { type: String as PropType<IncrementBehavior>, default: "Add" },
|
||||
incrementFactor: { type: Number as PropType<number>, default: 1 },
|
||||
incrementCallbackIncrease: { type: Function as PropType<() => void>, required: false },
|
||||
incrementCallbackDecrease: { type: Function as PropType<() => void>, required: false },
|
||||
isInteger: { type: Boolean as PropType<boolean>, default: false },
|
||||
displayDecimalPlaces: { type: Number as PropType<number>, default: 3 },
|
||||
unit: { type: String as PropType<string>, default: "" },
|
||||
unitIsHiddenWhenEditing: { type: Boolean as PropType<boolean>, default: true },
|
||||
displayDecimalPlaces: { type: Number as PropType<number>, default: 3 },
|
||||
label: { type: String as PropType<string>, required: false },
|
||||
incrementBehavior: { type: String as PropType<IncrementBehavior>, default: "Add" },
|
||||
incrementFactor: { type: Number as PropType<number>, default: 1 },
|
||||
disabled: { type: Boolean as PropType<boolean>, default: false },
|
||||
|
||||
// Callbacks
|
||||
incrementCallbackIncrease: { type: Function as PropType<() => void>, required: false },
|
||||
incrementCallbackDecrease: { type: Function as PropType<() => void>, required: false },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<LayoutRow class="optional-input">
|
||||
<CheckboxInput :checked="checked" @input="(e) => $emit('update:checked', (e.target as HTMLInputElement).checked)" :icon="icon" />
|
||||
<CheckboxInput :checked="checked" @input="(e) => $emit('update:checked', (e.target as HTMLInputElement).checked)" :icon="icon" :tooltip="tooltip" />
|
||||
</LayoutRow>
|
||||
</template>
|
||||
|
||||
@@ -47,6 +47,7 @@ export default defineComponent({
|
||||
props: {
|
||||
checked: { type: Boolean as PropType<boolean>, required: true },
|
||||
icon: { type: String as PropType<IconName>, default: "Checkmark" },
|
||||
tooltip: { type: String as PropType<string | undefined>, required: false },
|
||||
},
|
||||
components: {
|
||||
CheckboxInput,
|
||||
|
||||
@@ -64,22 +64,12 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType } from "vue";
|
||||
|
||||
import { IconName } from "@/utility-functions/icons";
|
||||
import { RadioEntries, RadioEntryData } from "@/wasm-communication/messages";
|
||||
|
||||
import LayoutRow from "@/components/layout/LayoutRow.vue";
|
||||
import IconLabel from "@/components/widgets/labels/IconLabel.vue";
|
||||
import TextLabel from "@/components/widgets/labels/TextLabel.vue";
|
||||
|
||||
export interface RadioEntryData {
|
||||
value?: string;
|
||||
label?: string;
|
||||
icon?: IconName;
|
||||
tooltip?: string;
|
||||
action?: () => void;
|
||||
}
|
||||
|
||||
export type RadioEntries = RadioEntryData[];
|
||||
|
||||
export default defineComponent({
|
||||
emits: ["update:selectedIndex"],
|
||||
props: {
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
<template>
|
||||
<LayoutCol class="swatch-pair">
|
||||
<LayoutRow class="secondary swatch">
|
||||
<button @click="() => clickSecondarySwatch()" ref="secondaryButton" data-hover-menu-spawner></button>
|
||||
<button @click="() => clickSecondarySwatch()" :style="`--swatch-color: ${secondary.toRgbaCSS()}`" data-hover-menu-spawner></button>
|
||||
<FloatingMenu :type="'Popover'" :direction="'Right'" v-model:open="secondaryOpen">
|
||||
<ColorPicker @update:color="(color: RGBA) => secondaryColorChanged(color)" :color="secondaryColor" />
|
||||
<ColorPicker @update:color="(color: RGBA) => secondaryColorChanged(color)" :color="secondary.toRgba()" />
|
||||
</FloatingMenu>
|
||||
</LayoutRow>
|
||||
<LayoutRow class="primary swatch">
|
||||
<button @click="() => clickPrimarySwatch()" ref="primaryButton" data-hover-menu-spawner></button>
|
||||
<button @click="() => clickPrimarySwatch()" :style="`--swatch-color: ${primary.toRgbaCSS()}`" data-hover-menu-spawner></button>
|
||||
<FloatingMenu :type="'Popover'" :direction="'Right'" v-model:open="primaryOpen">
|
||||
<ColorPicker @update:color="(color: RGBA) => primaryColorChanged(color)" :color="primaryColor" />
|
||||
<ColorPicker @update:color="(color: RGBA) => primaryColorChanged(color)" :color="primary.toRgba()" />
|
||||
</FloatingMenu>
|
||||
</LayoutRow>
|
||||
</LayoutCol>
|
||||
@@ -66,10 +66,10 @@
|
||||
</style>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
import { defineComponent, PropType } from "vue";
|
||||
|
||||
import { rgbaToDecimalRgba } from "@/utility-functions/color";
|
||||
import { type RGBA, UpdateWorkingColors } from "@/wasm-communication/messages";
|
||||
import { type RGBA, Color } from "@/wasm-communication/messages";
|
||||
|
||||
import ColorPicker from "@/components/floating-menus/ColorPicker.vue";
|
||||
import FloatingMenu from "@/components/floating-menus/FloatingMenu.vue";
|
||||
@@ -84,12 +84,14 @@ export default defineComponent({
|
||||
LayoutRow,
|
||||
LayoutCol,
|
||||
},
|
||||
props: {
|
||||
primary: { type: Object as PropType<Color>, required: true },
|
||||
secondary: { type: Object as PropType<Color>, required: true },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
primaryOpen: false,
|
||||
secondaryOpen: false,
|
||||
primaryColor: { r: 0, g: 0, b: 0, a: 1 } as RGBA,
|
||||
secondaryColor: { r: 255, g: 255, b: 255, a: 1 } as RGBA,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
@@ -102,44 +104,13 @@ export default defineComponent({
|
||||
this.secondaryOpen = true;
|
||||
},
|
||||
primaryColorChanged(color: RGBA) {
|
||||
this.primaryColor = color;
|
||||
this.updatePrimaryColor();
|
||||
const newColor = rgbaToDecimalRgba(color);
|
||||
this.editor.instance.update_primary_color(newColor.r, newColor.g, newColor.b, newColor.a);
|
||||
},
|
||||
secondaryColorChanged(color: RGBA) {
|
||||
this.secondaryColor = color;
|
||||
this.updateSecondaryColor();
|
||||
const newColor = rgbaToDecimalRgba(color);
|
||||
this.editor.instance.update_secondary_color(newColor.r, newColor.g, newColor.b, newColor.a);
|
||||
},
|
||||
async updatePrimaryColor() {
|
||||
let color = this.primaryColor;
|
||||
const button = this.$refs.primaryButton as HTMLButtonElement;
|
||||
button.style.setProperty("--swatch-color", `rgba(${color.r}, ${color.g}, ${color.b}, ${color.a})`);
|
||||
|
||||
color = rgbaToDecimalRgba(this.primaryColor);
|
||||
this.editor.instance.update_primary_color(color.r, color.g, color.b, color.a);
|
||||
},
|
||||
async updateSecondaryColor() {
|
||||
let color = this.secondaryColor;
|
||||
const button = this.$refs.secondaryButton as HTMLButtonElement;
|
||||
button.style.setProperty("--swatch-color", `rgba(${color.r}, ${color.g}, ${color.b}, ${color.a})`);
|
||||
|
||||
color = rgbaToDecimalRgba(this.secondaryColor);
|
||||
this.editor.instance.update_secondary_color(color.r, color.g, color.b, color.a);
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.editor.subscriptions.subscribeJsMessage(UpdateWorkingColors, (updateWorkingColors) => {
|
||||
this.primaryColor = updateWorkingColors.primary.toRgba();
|
||||
this.secondaryColor = updateWorkingColors.secondary.toRgba();
|
||||
|
||||
const primaryButton = this.$refs.primaryButton as HTMLButtonElement;
|
||||
primaryButton.style.setProperty("--swatch-color", updateWorkingColors.primary.toRgbaCSS());
|
||||
|
||||
const secondaryButton = this.$refs.secondaryButton as HTMLButtonElement;
|
||||
secondaryButton.style.setProperty("--swatch-color", updateWorkingColors.secondary.toRgbaCSS());
|
||||
});
|
||||
|
||||
this.updatePrimaryColor();
|
||||
this.updateSecondaryColor();
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<LayoutRow :class="['icon-label', iconSize, iconStyle]">
|
||||
<LayoutRow :class="['icon-label', iconSizeClass, iconStyleClass]">
|
||||
<component :is="icon" />
|
||||
</LayoutRow>
|
||||
</template>
|
||||
@@ -42,16 +42,15 @@ import LayoutRow from "@/components/layout/LayoutRow.vue";
|
||||
export default defineComponent({
|
||||
props: {
|
||||
icon: { type: String as PropType<IconName>, required: true },
|
||||
gapAfter: { type: Boolean as PropType<boolean>, default: false },
|
||||
style: { type: String as PropType<IconStyle>, default: "" },
|
||||
iconStyle: { type: String as PropType<IconStyle | undefined>, required: false },
|
||||
},
|
||||
computed: {
|
||||
iconSize(): string {
|
||||
iconSizeClass(): string {
|
||||
return `size-${icons[this.icon].size}`;
|
||||
},
|
||||
iconStyle(): string {
|
||||
if (!this.style) return "";
|
||||
return `${this.style}-style`;
|
||||
iconStyleClass(): string {
|
||||
if (!this.iconStyle || this.iconStyle === "Normal") return "";
|
||||
return `${this.iconStyle.toLowerCase()}-style`;
|
||||
},
|
||||
},
|
||||
components: {
|
||||
|
||||
@@ -75,8 +75,7 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType } from "vue";
|
||||
|
||||
export type SeparatorDirection = "Horizontal" | "Vertical";
|
||||
export type SeparatorType = "Related" | "Unrelated" | "Section" | "List";
|
||||
import { SeparatorDirection, SeparatorType } from "@/wasm-communication/messages";
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
|
||||
Reference in New Issue
Block a user