Tooltips overhaul
This commit is contained in:
parent
89861dfbd5
commit
f76d5deb6e
12 changed files with 212 additions and 146 deletions
|
@ -3,7 +3,7 @@
|
||||||
<img v-if="banner" :src="banner" class="banner" :alt="title" />
|
<img v-if="banner" :src="banner" class="banner" :alt="title" />
|
||||||
<div v-else class="title">{{ title }}</div>
|
<div v-else class="title">{{ title }}</div>
|
||||||
<div @click="changelog?.open()" class="version-container">
|
<div @click="changelog?.open()" class="version-container">
|
||||||
<Tooltip display="Changelog" bottom class="version"
|
<Tooltip display="Changelog" :direction="TooltipDirection.DOWN" class="version"
|
||||||
><span>v{{ versionNumber }}</span></Tooltip
|
><span>v{{ versionNumber }}</span></Tooltip
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
@ -26,51 +26,56 @@
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<a href="https://forums.moddingtree.com/" target="_blank">
|
<a href="https://forums.moddingtree.com/" target="_blank">
|
||||||
<Tooltip display="Forums" bottom yoffset="5px">
|
<Tooltip display="Forums" :direction="TooltipDirection.DOWN" yoffset="5px">
|
||||||
<span class="material-icons">forum</span>
|
<span class="material-icons">forum</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div @click="info?.open()">
|
<div @click="info?.open()">
|
||||||
<Tooltip display="Info" bottom class="info">
|
<Tooltip display="Info" :direction="TooltipDirection.DOWN" class="info">
|
||||||
<span class="material-icons">info</span>
|
<span class="material-icons">info</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
<div @click="savesManager?.open()">
|
<div @click="savesManager?.open()">
|
||||||
<Tooltip display="Saves" bottom xoffset="-20px">
|
<Tooltip display="Saves" :direction="TooltipDirection.DOWN" xoffset="-20px">
|
||||||
<span class="material-icons">library_books</span>
|
<span class="material-icons">library_books</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
<div @click="options?.open()">
|
<div @click="options?.open()">
|
||||||
<Tooltip display="Options" bottom xoffset="-66px">
|
<Tooltip display="Options" :direction="TooltipDirection.DOWN" xoffset="-66px">
|
||||||
<span class="material-icons">settings</span>
|
<span class="material-icons">settings</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="overlay-nav" v-bind="$attrs">
|
<div v-else class="overlay-nav" v-bind="$attrs">
|
||||||
<div @click="changelog?.open()" class="version-container">
|
<div @click="changelog?.open()" class="version-container">
|
||||||
<Tooltip display="Changelog" right xoffset="25%" class="version">
|
<Tooltip
|
||||||
|
display="Changelog"
|
||||||
|
:direction="TooltipDirection.RIGHT"
|
||||||
|
xoffset="25%"
|
||||||
|
class="version"
|
||||||
|
>
|
||||||
<span>v{{ versionNumber }}</span>
|
<span>v{{ versionNumber }}</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
<div @click="savesManager?.open()">
|
<div @click="savesManager?.open()">
|
||||||
<Tooltip display="Saves" right>
|
<Tooltip display="Saves" :direction="TooltipDirection.RIGHT">
|
||||||
<span class="material-icons">library_books</span>
|
<span class="material-icons">library_books</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
<div @click="options?.open()">
|
<div @click="options?.open()">
|
||||||
<Tooltip display="Options" right>
|
<Tooltip display="Options" :direction="TooltipDirection.RIGHT">
|
||||||
<span class="material-icons">settings</span>
|
<span class="material-icons">settings</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
<div @click="info?.open()">
|
<div @click="info?.open()">
|
||||||
<Tooltip display="Info" right>
|
<Tooltip display="Info" :direction="TooltipDirection.RIGHT">
|
||||||
<span class="material-icons">info</span>
|
<span class="material-icons">info</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<a href="https://forums.moddingtree.com/" target="_blank">
|
<a href="https://forums.moddingtree.com/" target="_blank">
|
||||||
<Tooltip display="Forums" right xoffset="7px">
|
<Tooltip display="Forums" :direction="TooltipDirection.RIGHT" xoffset="7px">
|
||||||
<span class="material-icons">forum</span>
|
<span class="material-icons">forum</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</a>
|
</a>
|
||||||
|
@ -105,7 +110,8 @@ import { ComponentPublicInstance, ref } from "vue";
|
||||||
import Info from "./Info.vue";
|
import Info from "./Info.vue";
|
||||||
import Options from "./Options.vue";
|
import Options from "./Options.vue";
|
||||||
import SavesManager from "./SavesManager.vue";
|
import SavesManager from "./SavesManager.vue";
|
||||||
import Tooltip from "./Tooltip.vue";
|
import Tooltip from "features/tooltips/Tooltip.vue";
|
||||||
|
import { TooltipDirection } from "features/tooltips/tooltip";
|
||||||
|
|
||||||
const info = ref<ComponentPublicInstance<typeof Info> | null>(null);
|
const info = ref<ComponentPublicInstance<typeof Info> | null>(null);
|
||||||
const savesManager = ref<ComponentPublicInstance<typeof SavesManager> | null>(null);
|
const savesManager = ref<ComponentPublicInstance<typeof SavesManager> | null>(null);
|
||||||
|
|
|
@ -30,7 +30,7 @@ import { coerceComponent, render } from "util/vue";
|
||||||
import { computed, ref, toRefs } from "vue";
|
import { computed, ref, toRefs } from "vue";
|
||||||
import Select from "./fields/Select.vue";
|
import Select from "./fields/Select.vue";
|
||||||
import Toggle from "./fields/Toggle.vue";
|
import Toggle from "./fields/Toggle.vue";
|
||||||
import Tooltip from "./Tooltip.vue";
|
import Tooltip from "features/tooltips/Tooltip.vue";
|
||||||
|
|
||||||
const isOpen = ref(false);
|
const isOpen = ref(false);
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, toRefs, unref } from "vue";
|
import { computed, toRefs, unref } from "vue";
|
||||||
import Tooltip from "../Tooltip.vue";
|
import Tooltip from "features/tooltips/Tooltip.vue";
|
||||||
import "components/common/fields.css";
|
import "components/common/fields.css";
|
||||||
|
|
||||||
const _props = defineProps<{
|
const _props = defineProps<{
|
||||||
|
|
|
@ -26,7 +26,6 @@
|
||||||
import { CoercableComponent, Visibility } from "features/feature";
|
import { CoercableComponent, Visibility } from "features/feature";
|
||||||
import { computeOptionalComponent, processedPropType } from "util/vue";
|
import { computeOptionalComponent, processedPropType } from "util/vue";
|
||||||
import { defineComponent, StyleValue, toRefs, unref } from "vue";
|
import { defineComponent, StyleValue, toRefs, unref } from "vue";
|
||||||
import Tooltip from "components/Tooltip.vue";
|
|
||||||
import Node from "components/Node.vue";
|
import Node from "components/Node.vue";
|
||||||
import MarkNode from "components/MarkNode.vue";
|
import MarkNode from "components/MarkNode.vue";
|
||||||
import "components/common/features.css";
|
import "components/common/features.css";
|
||||||
|
@ -53,8 +52,7 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
Node,
|
Node,
|
||||||
MarkNode,
|
MarkNode
|
||||||
Tooltip
|
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const { display } = toRefs(props);
|
const { display } = toRefs(props);
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
import { CoercableComponent } from "features/feature";
|
|
||||||
import { ProcessedComputable } from "util/computed";
|
|
||||||
|
|
||||||
declare module "@vue/runtime-dom" {
|
|
||||||
interface CSSProperties {
|
|
||||||
"--xoffset"?: string;
|
|
||||||
"--yoffset"?: string;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Tooltip {
|
|
||||||
display: ProcessedComputable<CoercableComponent>;
|
|
||||||
top?: ProcessedComputable<boolean>;
|
|
||||||
left?: ProcessedComputable<boolean>;
|
|
||||||
right?: ProcessedComputable<boolean>;
|
|
||||||
bottom?: ProcessedComputable<boolean>;
|
|
||||||
xoffset?: ProcessedComputable<string>;
|
|
||||||
yoffset?: ProcessedComputable<string>;
|
|
||||||
force?: ProcessedComputable<boolean>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function gatherTooltipProps(tooltip: Tooltip) {
|
|
||||||
const { display, top, left, right, bottom, xoffset, yoffset, force } = tooltip;
|
|
||||||
return { display, top, left, right, bottom, xoffset, yoffset, force };
|
|
||||||
}
|
|
|
@ -1,20 +1,23 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="tooltip-container"
|
class="tooltip-container"
|
||||||
:class="{ shown: isShown }"
|
:class="{ shown: isShown, ...unref(classes) }"
|
||||||
@mouseenter="isHovered = true"
|
@mouseenter="isHovered = true"
|
||||||
@mouseleave="isHovered = false"
|
@mouseleave="isHovered = false"
|
||||||
|
@click="togglePinned"
|
||||||
|
:style="unref(style)"
|
||||||
>
|
>
|
||||||
<slot />
|
<slot />
|
||||||
|
<component v-if="elementComp" :is="elementComp" />
|
||||||
<transition name="fade">
|
<transition name="fade">
|
||||||
<div
|
<div
|
||||||
v-if="isShown"
|
v-if="isShown"
|
||||||
class="tooltip"
|
class="tooltip"
|
||||||
:class="{
|
:class="{
|
||||||
top: unref(top),
|
top: unref(direction) === TooltipDirection.UP,
|
||||||
left: unref(left),
|
left: unref(direction) === TooltipDirection.LEFT,
|
||||||
right: unref(right),
|
right: unref(direction) === TooltipDirection.RIGHT,
|
||||||
bottom: unref(bottom)
|
bottom: unref(direction) === TooltipDirection.DOWN
|
||||||
}"
|
}"
|
||||||
:style="{
|
:style="{
|
||||||
'--xoffset': unref(xoffset) || '0px',
|
'--xoffset': unref(xoffset) || '0px',
|
||||||
|
@ -28,33 +31,76 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { CoercableComponent } from "features/feature";
|
import { CoercableComponent, jsx, StyleValue } from "features/feature";
|
||||||
import { computeOptionalComponent, processedPropType, unwrapRef } from "util/vue";
|
import { Persistent } from "game/persistence";
|
||||||
import { computed, defineComponent, ref, toRefs, unref } from "vue";
|
import {
|
||||||
|
coerceComponent,
|
||||||
|
computeOptionalComponent,
|
||||||
|
processedPropType,
|
||||||
|
render,
|
||||||
|
unwrapRef,
|
||||||
|
VueFeature
|
||||||
|
} from "util/vue";
|
||||||
|
import {
|
||||||
|
Component,
|
||||||
|
computed,
|
||||||
|
defineComponent,
|
||||||
|
PropType,
|
||||||
|
ref,
|
||||||
|
shallowRef,
|
||||||
|
toRefs,
|
||||||
|
unref,
|
||||||
|
watchEffect
|
||||||
|
} from "vue";
|
||||||
|
import { TooltipDirection } from "./tooltip";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
display: processedPropType<CoercableComponent>(Object, String, Function),
|
element: processedPropType<VueFeature>(Object),
|
||||||
top: processedPropType<boolean>(Boolean),
|
display: {
|
||||||
left: processedPropType<boolean>(Boolean),
|
type: processedPropType<CoercableComponent>(Object, String, Function),
|
||||||
right: processedPropType<boolean>(Boolean),
|
required: true
|
||||||
bottom: processedPropType<boolean>(Boolean),
|
},
|
||||||
|
style: processedPropType<StyleValue>(Object, String, Array),
|
||||||
|
classes: processedPropType<Record<string, boolean>>(Object),
|
||||||
|
direction: processedPropType<TooltipDirection>(Number),
|
||||||
xoffset: processedPropType<string>(String),
|
xoffset: processedPropType<string>(String),
|
||||||
yoffset: processedPropType<string>(String),
|
yoffset: processedPropType<string>(String),
|
||||||
force: processedPropType<boolean>(Boolean)
|
pinned: Object as PropType<Persistent<boolean>>
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const { display, force } = toRefs(props);
|
const { element, display, pinned } = toRefs(props);
|
||||||
|
|
||||||
const isHovered = ref(false);
|
const isHovered = ref(false);
|
||||||
const isShown = computed(() => (unwrapRef(force) || isHovered.value) && comp.value);
|
const isShown = computed(() => (unwrapRef(pinned) || isHovered.value) && comp.value);
|
||||||
const comp = computeOptionalComponent(display);
|
const comp = computeOptionalComponent(display);
|
||||||
|
|
||||||
|
const elementComp = shallowRef<Component | "" | null>(null);
|
||||||
|
watchEffect(() => {
|
||||||
|
const currComponent = unwrapRef(element);
|
||||||
|
elementComp.value =
|
||||||
|
currComponent == null
|
||||||
|
? null
|
||||||
|
: coerceComponent(jsx(() => render(currComponent) as JSX.Element));
|
||||||
|
});
|
||||||
|
|
||||||
|
function togglePinned(e: MouseEvent) {
|
||||||
|
const isPinned = pinned as unknown as Persistent<boolean> | undefined; // Vue typing :/
|
||||||
|
if (e.shiftKey && isPinned) {
|
||||||
|
isPinned.value = !isPinned.value;
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
TooltipDirection,
|
||||||
isHovered,
|
isHovered,
|
||||||
isShown,
|
isShown,
|
||||||
comp,
|
comp,
|
||||||
unref
|
elementComp,
|
||||||
|
unref,
|
||||||
|
togglePinned
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
115
src/features/tooltips/tooltip.ts
Normal file
115
src/features/tooltips/tooltip.ts
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
import TooltipComponent from "./Tooltip.vue";
|
||||||
|
import {
|
||||||
|
CoercableComponent,
|
||||||
|
Component,
|
||||||
|
GatherProps,
|
||||||
|
Replace,
|
||||||
|
setDefault,
|
||||||
|
StyleValue
|
||||||
|
} from "features/feature";
|
||||||
|
import {
|
||||||
|
Computable,
|
||||||
|
GetComputableType,
|
||||||
|
GetComputableTypeWithDefault,
|
||||||
|
processComputable,
|
||||||
|
ProcessedComputable
|
||||||
|
} from "util/computed";
|
||||||
|
import { VueFeature } from "util/vue";
|
||||||
|
import { readonly, ref, Ref } from "vue";
|
||||||
|
import { persistent } from "game/persistence";
|
||||||
|
|
||||||
|
declare module "@vue/runtime-dom" {
|
||||||
|
interface CSSProperties {
|
||||||
|
"--xoffset"?: string;
|
||||||
|
"--yoffset"?: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum TooltipDirection {
|
||||||
|
UP,
|
||||||
|
LEFT,
|
||||||
|
RIGHT,
|
||||||
|
DOWN
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TooltipOptions {
|
||||||
|
pinnable?: boolean;
|
||||||
|
display: Computable<CoercableComponent>;
|
||||||
|
classes?: Computable<Record<string, boolean>>;
|
||||||
|
style?: Computable<StyleValue>;
|
||||||
|
direction?: Computable<TooltipDirection>;
|
||||||
|
xoffset?: Computable<string>;
|
||||||
|
yoffset?: Computable<string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BaseTooltip {
|
||||||
|
pinned?: Ref<boolean>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Tooltip<T extends TooltipOptions> = Replace<
|
||||||
|
T & BaseTooltip,
|
||||||
|
{
|
||||||
|
pinnable: T["pinnable"] extends unknown ? true : T["pinnable"];
|
||||||
|
display: GetComputableType<T["display"]>;
|
||||||
|
classes: GetComputableType<T["classes"]>;
|
||||||
|
style: GetComputableType<T["style"]>;
|
||||||
|
direction: GetComputableTypeWithDefault<T["direction"], TooltipDirection.UP>;
|
||||||
|
xoffset: GetComputableType<T["xoffset"]>;
|
||||||
|
yoffset: GetComputableType<T["yoffset"]>;
|
||||||
|
}
|
||||||
|
>;
|
||||||
|
|
||||||
|
export type GenericTooltip = Replace<
|
||||||
|
Tooltip<TooltipOptions>,
|
||||||
|
{
|
||||||
|
pinnable: boolean;
|
||||||
|
direction: ProcessedComputable<TooltipDirection>;
|
||||||
|
}
|
||||||
|
>;
|
||||||
|
|
||||||
|
export function addTooltip<T extends TooltipOptions>(
|
||||||
|
element: VueFeature,
|
||||||
|
options: T & ThisType<Tooltip<T>> & Partial<BaseTooltip>
|
||||||
|
): Tooltip<T> {
|
||||||
|
if (options.pinnable) {
|
||||||
|
if ("pinned" in element) {
|
||||||
|
console.error(
|
||||||
|
"Cannot add pinnable tooltip to element that already has a property called 'pinned'"
|
||||||
|
);
|
||||||
|
options.pinnable = false;
|
||||||
|
} else {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
(element as any).pinned = options.pinned = persistent<boolean>(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
processComputable(options as T, "display");
|
||||||
|
processComputable(options as T, "classes");
|
||||||
|
processComputable(options as T, "style");
|
||||||
|
processComputable(options as T, "direction");
|
||||||
|
setDefault(options, "direction", TooltipDirection.UP);
|
||||||
|
processComputable(options as T, "xoffset");
|
||||||
|
processComputable(options as T, "yoffset");
|
||||||
|
|
||||||
|
const elementComponent = element[Component];
|
||||||
|
element[Component] = TooltipComponent;
|
||||||
|
const elementGratherProps = element[GatherProps].bind(element);
|
||||||
|
element[GatherProps] = function gatherTooltipProps(this: GenericTooltip) {
|
||||||
|
const { display, classes, style, direction, xoffset, yoffset, pinned } = this;
|
||||||
|
return {
|
||||||
|
element: {
|
||||||
|
[Component]: elementComponent,
|
||||||
|
[GatherProps]: elementGratherProps
|
||||||
|
},
|
||||||
|
display,
|
||||||
|
classes,
|
||||||
|
style,
|
||||||
|
direction,
|
||||||
|
xoffset,
|
||||||
|
yoffset,
|
||||||
|
pinned
|
||||||
|
};
|
||||||
|
}.bind(options as GenericTooltip);
|
||||||
|
|
||||||
|
return options as unknown as Tooltip<T>;
|
||||||
|
}
|
|
@ -4,7 +4,6 @@
|
||||||
v-for="(node, nodeIndex) in row"
|
v-for="(node, nodeIndex) in row"
|
||||||
:key="nodeIndex"
|
:key="nodeIndex"
|
||||||
v-bind="gatherNodeProps(node)"
|
v-bind="gatherNodeProps(node)"
|
||||||
:force-tooltip="node.forceTooltip"
|
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<span class="left-side-nodes" v-if="unref(leftSideNodes)">
|
<span class="left-side-nodes" v-if="unref(leftSideNodes)">
|
||||||
|
@ -12,7 +11,6 @@
|
||||||
v-for="(node, nodeIndex) in unref(leftSideNodes)"
|
v-for="(node, nodeIndex) in unref(leftSideNodes)"
|
||||||
:key="nodeIndex"
|
:key="nodeIndex"
|
||||||
v-bind="gatherNodeProps(node)"
|
v-bind="gatherNodeProps(node)"
|
||||||
:force-tooltip="node.forceTooltip"
|
|
||||||
small
|
small
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
|
@ -21,7 +19,6 @@
|
||||||
v-for="(node, nodeIndex) in unref(rightSideNodes)"
|
v-for="(node, nodeIndex) in unref(rightSideNodes)"
|
||||||
:key="nodeIndex"
|
:key="nodeIndex"
|
||||||
v-bind="gatherNodeProps(node)"
|
v-bind="gatherNodeProps(node)"
|
||||||
:force-tooltip="node.forceTooltip"
|
|
||||||
small
|
small
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
|
@ -54,12 +51,10 @@ export default defineComponent({
|
||||||
visibility,
|
visibility,
|
||||||
style,
|
style,
|
||||||
classes,
|
classes,
|
||||||
tooltip,
|
|
||||||
onClick,
|
onClick,
|
||||||
onHold,
|
onHold,
|
||||||
color,
|
color,
|
||||||
glowColor,
|
glowColor,
|
||||||
forceTooltip,
|
|
||||||
canClick,
|
canClick,
|
||||||
mark,
|
mark,
|
||||||
id
|
id
|
||||||
|
@ -69,12 +64,10 @@ export default defineComponent({
|
||||||
visibility,
|
visibility,
|
||||||
style,
|
style,
|
||||||
classes,
|
classes,
|
||||||
tooltip,
|
|
||||||
onClick,
|
onClick,
|
||||||
onHold,
|
onHold,
|
||||||
color,
|
color,
|
||||||
glowColor,
|
glowColor,
|
||||||
forceTooltip,
|
|
||||||
canClick,
|
canClick,
|
||||||
mark,
|
mark,
|
||||||
id
|
id
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<Tooltip
|
<div
|
||||||
v-if="unref(visibility) !== Visibility.None"
|
v-if="unref(visibility) !== Visibility.None"
|
||||||
v-bind="tooltipToBind && gatherTooltipProps(tooltipToBind)"
|
|
||||||
:display="tooltipDisplay"
|
|
||||||
:force="forceTooltip"
|
|
||||||
:style="{ visibility: unref(visibility) === Visibility.Hidden ? 'hidden' : undefined }"
|
:style="{ visibility: unref(visibility) === Visibility.Hidden ? 'hidden' : undefined }"
|
||||||
:class="{
|
:class="{
|
||||||
treeNode: true,
|
treeNode: true,
|
||||||
|
@ -13,7 +10,7 @@
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@click="click"
|
@click="onClick"
|
||||||
@mousedown="start"
|
@mousedown="start"
|
||||||
@mouseleave="stop"
|
@mouseleave="stop"
|
||||||
@mouseup="stop"
|
@mouseup="stop"
|
||||||
|
@ -34,33 +31,20 @@
|
||||||
</div>
|
</div>
|
||||||
<MarkNode :mark="unref(mark)" />
|
<MarkNode :mark="unref(mark)" />
|
||||||
<Node :id="id" />
|
<Node :id="id" />
|
||||||
</Tooltip>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Node from "components/Node.vue";
|
import Node from "components/Node.vue";
|
||||||
import MarkNode from "components/MarkNode.vue";
|
import MarkNode from "components/MarkNode.vue";
|
||||||
import TooltipVue from "components/Tooltip.vue";
|
|
||||||
import { CoercableComponent, StyleValue, Visibility } from "features/feature";
|
import { CoercableComponent, StyleValue, Visibility } from "features/feature";
|
||||||
import { gatherTooltipProps, Tooltip } from "features/tooltip";
|
|
||||||
import { ProcessedComputable } from "util/computed";
|
|
||||||
import {
|
import {
|
||||||
computeOptionalComponent,
|
computeOptionalComponent,
|
||||||
isCoercableComponent,
|
isCoercableComponent,
|
||||||
processedPropType,
|
processedPropType,
|
||||||
setupHoldToClick,
|
setupHoldToClick
|
||||||
unwrapRef
|
|
||||||
} from "util/vue";
|
} from "util/vue";
|
||||||
import {
|
import { defineComponent, PropType, toRefs, unref } from "vue";
|
||||||
computed,
|
|
||||||
defineComponent,
|
|
||||||
PropType,
|
|
||||||
Ref,
|
|
||||||
shallowRef,
|
|
||||||
toRefs,
|
|
||||||
unref,
|
|
||||||
watchEffect
|
|
||||||
} from "vue";
|
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
|
@ -71,15 +55,10 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
style: processedPropType<StyleValue>(String, Object, Array),
|
style: processedPropType<StyleValue>(String, Object, Array),
|
||||||
classes: processedPropType<Record<string, boolean>>(Object),
|
classes: processedPropType<Record<string, boolean>>(Object),
|
||||||
tooltip: processedPropType<CoercableComponent | Tooltip>(Object, String, Function),
|
|
||||||
onClick: Function as PropType<(e?: MouseEvent | TouchEvent) => void>,
|
onClick: Function as PropType<(e?: MouseEvent | TouchEvent) => void>,
|
||||||
onHold: Function as PropType<VoidFunction>,
|
onHold: Function as PropType<VoidFunction>,
|
||||||
color: processedPropType<string>(String),
|
color: processedPropType<string>(String),
|
||||||
glowColor: processedPropType<string>(String),
|
glowColor: processedPropType<string>(String),
|
||||||
forceTooltip: {
|
|
||||||
type: Object as PropType<Ref<boolean>>,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
canClick: {
|
canClick: {
|
||||||
type: processedPropType<boolean>(Boolean),
|
type: processedPropType<boolean>(Boolean),
|
||||||
required: true
|
required: true
|
||||||
|
@ -92,55 +71,22 @@ export default defineComponent({
|
||||||
small: processedPropType<boolean>(Boolean)
|
small: processedPropType<boolean>(Boolean)
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
Tooltip: TooltipVue,
|
|
||||||
MarkNode,
|
MarkNode,
|
||||||
Node
|
Node
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const { tooltip, forceTooltip, onClick, onHold, display } = toRefs(props);
|
const { onClick, onHold, display } = toRefs(props);
|
||||||
|
|
||||||
function click(e: MouseEvent) {
|
|
||||||
if (e.shiftKey && tooltip) {
|
|
||||||
forceTooltip.value = !forceTooltip.value;
|
|
||||||
} else {
|
|
||||||
unref(onClick)?.();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const comp = computeOptionalComponent(display);
|
const comp = computeOptionalComponent(display);
|
||||||
const tooltipDisplay = shallowRef<ProcessedComputable<CoercableComponent> | undefined>(
|
|
||||||
undefined
|
|
||||||
);
|
|
||||||
watchEffect(() => {
|
|
||||||
const currTooltip = unwrapRef(tooltip);
|
|
||||||
|
|
||||||
if (typeof currTooltip === "object" && !isCoercableComponent(currTooltip)) {
|
|
||||||
tooltipDisplay.value = currTooltip.display;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
tooltipDisplay.value = currTooltip;
|
|
||||||
});
|
|
||||||
const tooltipToBind = computed(() => {
|
|
||||||
const currTooltip = unwrapRef(tooltip);
|
|
||||||
|
|
||||||
if (typeof currTooltip === "object" && !isCoercableComponent(currTooltip)) {
|
|
||||||
return currTooltip;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
|
|
||||||
const { start, stop } = setupHoldToClick(onClick, onHold);
|
const { start, stop } = setupHoldToClick(onClick, onHold);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
click,
|
|
||||||
start,
|
start,
|
||||||
stop,
|
stop,
|
||||||
comp,
|
comp,
|
||||||
tooltipDisplay,
|
|
||||||
tooltipToBind,
|
|
||||||
unref,
|
unref,
|
||||||
Visibility,
|
Visibility,
|
||||||
gatherTooltipProps,
|
|
||||||
isCoercableComponent
|
isCoercableComponent
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,9 +12,7 @@ import {
|
||||||
import { Link } from "features/links/links";
|
import { Link } from "features/links/links";
|
||||||
import { GenericReset } from "features/reset";
|
import { GenericReset } from "features/reset";
|
||||||
import { displayResource, Resource } from "features/resources/resource";
|
import { displayResource, Resource } from "features/resources/resource";
|
||||||
import { Tooltip } from "features/tooltip";
|
|
||||||
import TreeComponent from "features/trees/Tree.vue";
|
import TreeComponent from "features/trees/Tree.vue";
|
||||||
import { deletePersistent, persistent } from "game/persistence";
|
|
||||||
import Decimal, { DecimalSource, format, formatWhole } from "util/bignum";
|
import Decimal, { DecimalSource, format, formatWhole } from "util/bignum";
|
||||||
import {
|
import {
|
||||||
Computable,
|
Computable,
|
||||||
|
@ -35,7 +33,6 @@ export interface TreeNodeOptions {
|
||||||
canClick?: Computable<boolean>;
|
canClick?: Computable<boolean>;
|
||||||
color?: Computable<string>;
|
color?: Computable<string>;
|
||||||
display?: Computable<CoercableComponent>;
|
display?: Computable<CoercableComponent>;
|
||||||
tooltip?: Computable<string | Tooltip>;
|
|
||||||
glowColor?: Computable<string>;
|
glowColor?: Computable<string>;
|
||||||
classes?: Computable<Record<string, boolean>>;
|
classes?: Computable<Record<string, boolean>>;
|
||||||
style?: Computable<StyleValue>;
|
style?: Computable<StyleValue>;
|
||||||
|
@ -47,7 +44,6 @@ export interface TreeNodeOptions {
|
||||||
|
|
||||||
export interface BaseTreeNode {
|
export interface BaseTreeNode {
|
||||||
id: string;
|
id: string;
|
||||||
forceTooltip: Ref<boolean>;
|
|
||||||
type: typeof TreeNodeType;
|
type: typeof TreeNodeType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,7 +58,6 @@ export type TreeNode<T extends TreeNodeOptions> = Replace<
|
||||||
classes: GetComputableType<T["classes"]>;
|
classes: GetComputableType<T["classes"]>;
|
||||||
style: GetComputableType<T["style"]>;
|
style: GetComputableType<T["style"]>;
|
||||||
mark: GetComputableType<T["mark"]>;
|
mark: GetComputableType<T["mark"]>;
|
||||||
tooltip: GetComputableType<T["tooltip"]>;
|
|
||||||
}
|
}
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
@ -77,27 +72,17 @@ export type GenericTreeNode = Replace<
|
||||||
export function createTreeNode<T extends TreeNodeOptions>(
|
export function createTreeNode<T extends TreeNodeOptions>(
|
||||||
optionsFunc: OptionsFunc<T, TreeNode<T>, BaseTreeNode>
|
optionsFunc: OptionsFunc<T, TreeNode<T>, BaseTreeNode>
|
||||||
): TreeNode<T> {
|
): TreeNode<T> {
|
||||||
const forceTooltip = persistent(false);
|
|
||||||
return createLazyProxy(() => {
|
return createLazyProxy(() => {
|
||||||
const treeNode = optionsFunc();
|
const treeNode = optionsFunc();
|
||||||
treeNode.id = getUniqueID("treeNode-");
|
treeNode.id = getUniqueID("treeNode-");
|
||||||
treeNode.type = TreeNodeType;
|
treeNode.type = TreeNodeType;
|
||||||
|
|
||||||
if (treeNode.tooltip) {
|
|
||||||
treeNode.forceTooltip = forceTooltip;
|
|
||||||
} else {
|
|
||||||
// If we don't have a tooltip, no point in making this persistent
|
|
||||||
treeNode.forceTooltip = ref(false);
|
|
||||||
deletePersistent(forceTooltip);
|
|
||||||
}
|
|
||||||
|
|
||||||
processComputable(treeNode as T, "visibility");
|
processComputable(treeNode as T, "visibility");
|
||||||
setDefault(treeNode, "visibility", Visibility.Visible);
|
setDefault(treeNode, "visibility", Visibility.Visible);
|
||||||
processComputable(treeNode as T, "canClick");
|
processComputable(treeNode as T, "canClick");
|
||||||
setDefault(treeNode, "canClick", true);
|
setDefault(treeNode, "canClick", true);
|
||||||
processComputable(treeNode as T, "color");
|
processComputable(treeNode as T, "color");
|
||||||
processComputable(treeNode as T, "display");
|
processComputable(treeNode as T, "display");
|
||||||
processComputable(treeNode as T, "tooltip");
|
|
||||||
processComputable(treeNode as T, "glowColor");
|
processComputable(treeNode as T, "glowColor");
|
||||||
processComputable(treeNode as T, "classes");
|
processComputable(treeNode as T, "classes");
|
||||||
processComputable(treeNode as T, "style");
|
processComputable(treeNode as T, "style");
|
||||||
|
|
|
@ -40,8 +40,10 @@ export function createLazyProxy<T extends object, S>(
|
||||||
return (calculateObj() as any)[key];
|
return (calculateObj() as any)[key];
|
||||||
},
|
},
|
||||||
set(target, key, value) {
|
set(target, key, value) {
|
||||||
console.error("Layers and features are shallow readonly", key, value);
|
// TODO give warning about this? It should only be done with caution
|
||||||
return false;
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
(calculateObj() as any)[key] = value;
|
||||||
|
return true;
|
||||||
},
|
},
|
||||||
has(target, key) {
|
has(target, key) {
|
||||||
if (key === ProxyState) {
|
if (key === ProxyState) {
|
||||||
|
|
|
@ -164,8 +164,8 @@ export function getFirstFeature<T extends { visibility: ProcessedComputable<Visi
|
||||||
export function computeComponent(
|
export function computeComponent(
|
||||||
component: Ref<ProcessedComputable<CoercableComponent>>,
|
component: Ref<ProcessedComputable<CoercableComponent>>,
|
||||||
defaultWrapper = "div"
|
defaultWrapper = "div"
|
||||||
): ShallowRef<Component | JSXFunction | ""> {
|
): ShallowRef<Component | ""> {
|
||||||
const comp = shallowRef<Component | JSXFunction | "">();
|
const comp = shallowRef<Component | "">();
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
comp.value = coerceComponent(unwrapRef(component), defaultWrapper);
|
comp.value = coerceComponent(unwrapRef(component), defaultWrapper);
|
||||||
});
|
});
|
||||||
|
@ -174,8 +174,8 @@ export function computeComponent(
|
||||||
export function computeOptionalComponent(
|
export function computeOptionalComponent(
|
||||||
component: Ref<ProcessedComputable<CoercableComponent | undefined> | undefined>,
|
component: Ref<ProcessedComputable<CoercableComponent | undefined> | undefined>,
|
||||||
defaultWrapper = "div"
|
defaultWrapper = "div"
|
||||||
): ShallowRef<Component | JSXFunction | "" | null> {
|
): ShallowRef<Component | "" | null> {
|
||||||
const comp = shallowRef<Component | JSXFunction | "" | null>(null);
|
const comp = shallowRef<Component | "" | null>(null);
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
const currComponent = unwrapRef(component);
|
const currComponent = unwrapRef(component);
|
||||||
comp.value = currComponent == null ? null : coerceComponent(currComponent, defaultWrapper);
|
comp.value = currComponent == null ? null : coerceComponent(currComponent, defaultWrapper);
|
||||||
|
|
Loading…
Reference in a new issue