Fixing more issues
This commit is contained in:
parent
15a460bf42
commit
90e49e196f
60 changed files with 1380 additions and 781 deletions
|
@ -18,7 +18,8 @@ module.exports = {
|
|||
"no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
|
||||
"no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off",
|
||||
"vue/script-setup-uses-vars": "warn",
|
||||
"vue/no-mutating-props": "off"
|
||||
"vue/no-mutating-props": "off",
|
||||
"vue/multi-word-component-names": "off"
|
||||
},
|
||||
globals: {
|
||||
defineProps: "readonly",
|
||||
|
|
38
package-lock.json
generated
38
package-lock.json
generated
|
@ -14,10 +14,10 @@
|
|||
"vue": "^3.2.26",
|
||||
"vue-next-select": "^2.10.2",
|
||||
"vue-panzoom": "^1.1.6",
|
||||
"vue-sortable": "github:Netbel/vue-sortable#master-fix",
|
||||
"vue-textarea-autosize": "^1.1.1",
|
||||
"vue-toastification": "^2.0.0-rc.1",
|
||||
"vue-transition-expand": "^0.1.0"
|
||||
"vue-transition-expand": "^0.1.0",
|
||||
"vuedraggable": "^4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ivanv/vue-collapse-transition": "^1.0.2",
|
||||
|
@ -11483,14 +11483,6 @@
|
|||
"panzoom": "^9.4.1"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-sortable": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "git+ssh://git@github.com/Netbel/vue-sortable.git#f4d4870ace71ea59bd79252eb2ec1cf6bfb02fe7",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"sortablejs": "^1.4.2"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-style-loader": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.3.tgz",
|
||||
|
@ -11549,6 +11541,17 @@
|
|||
"resolved": "https://registry.npmjs.org/vue/-/vue-2.6.14.tgz",
|
||||
"integrity": "sha512-x2284lgYvjOMj3Za7kqzRcUSxBboHqtgRE2zlos1qWaOye5yUmHn42LB1250NJBLRwEcdrB0JRwyPTEPhfQjiQ=="
|
||||
},
|
||||
"node_modules/vuedraggable": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/vuedraggable/-/vuedraggable-4.1.0.tgz",
|
||||
"integrity": "sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==",
|
||||
"dependencies": {
|
||||
"sortablejs": "1.14.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "^3.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/watchpack": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz",
|
||||
|
@ -20849,13 +20852,6 @@
|
|||
"panzoom": "^9.4.1"
|
||||
}
|
||||
},
|
||||
"vue-sortable": {
|
||||
"version": "git+ssh://git@github.com/Netbel/vue-sortable.git#f4d4870ace71ea59bd79252eb2ec1cf6bfb02fe7",
|
||||
"from": "vue-sortable@github:Netbel/vue-sortable#master-fix",
|
||||
"requires": {
|
||||
"sortablejs": "^1.4.2"
|
||||
}
|
||||
},
|
||||
"vue-style-loader": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.3.tgz",
|
||||
|
@ -20916,6 +20912,14 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"vuedraggable": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/vuedraggable/-/vuedraggable-4.1.0.tgz",
|
||||
"integrity": "sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==",
|
||||
"requires": {
|
||||
"sortablejs": "1.14.0"
|
||||
}
|
||||
},
|
||||
"watchpack": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz",
|
||||
|
|
|
@ -14,10 +14,10 @@
|
|||
"vue": "^3.2.26",
|
||||
"vue-next-select": "^2.10.2",
|
||||
"vue-panzoom": "^1.1.6",
|
||||
"vue-sortable": "github:Netbel/vue-sortable#master-fix",
|
||||
"vue-textarea-autosize": "^1.1.1",
|
||||
"vue-toastification": "^2.0.0-rc.1",
|
||||
"vue-transition-expand": "^0.1.0"
|
||||
"vue-transition-expand": "^0.1.0",
|
||||
"vuedraggable": "^4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ivanv/vue-collapse-transition": "^1.0.2",
|
||||
|
|
|
@ -21,20 +21,46 @@
|
|||
</Tooltip>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { GenericAchievement } from "@/features/achievement";
|
||||
import { FeatureComponent } from "@/features/feature";
|
||||
<script lang="ts">
|
||||
import { CoercableComponent, Visibility } from "@/features/feature";
|
||||
import { coerceComponent } from "@/util/vue";
|
||||
import { computed, toRefs } from "vue";
|
||||
import { computed, defineComponent, PropType, StyleValue, toRefs } from "vue";
|
||||
import LinkNode from "../system/LinkNode.vue";
|
||||
import MarkNode from "./MarkNode.vue";
|
||||
import { Visibility } from "@/features/feature";
|
||||
|
||||
const props = toRefs(defineProps<FeatureComponent<GenericAchievement>>());
|
||||
export default defineComponent({
|
||||
props: {
|
||||
visibility: {
|
||||
type: Object as PropType<Visibility>,
|
||||
required: true
|
||||
},
|
||||
display: [Object, String] as PropType<CoercableComponent>,
|
||||
tooltip: [Object, String] as PropType<CoercableComponent>,
|
||||
earned: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
image: String,
|
||||
style: Object as PropType<StyleValue>,
|
||||
classes: Object as PropType<Record<string, boolean>>,
|
||||
mark: [Boolean, String],
|
||||
id: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
const { display } = toRefs(props);
|
||||
|
||||
const component = computed(() => {
|
||||
const display = props.display.value;
|
||||
return display && coerceComponent(display);
|
||||
return {
|
||||
component: computed(() => {
|
||||
return display.value && coerceComponent(display.value);
|
||||
}),
|
||||
LinkNode,
|
||||
MarkNode,
|
||||
Visibility
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -30,38 +30,74 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Direction, GenericBar } from "@/features/bar";
|
||||
import { FeatureComponent, Visibility } from "@/features/feature";
|
||||
import Decimal from "@/util/bignum";
|
||||
<script lang="ts">
|
||||
import { Direction } from "@/features/bar";
|
||||
import { CoercableComponent, Visibility } from "@/features/feature";
|
||||
import Decimal, { DecimalSource } from "@/util/bignum";
|
||||
import { coerceComponent } from "@/util/vue";
|
||||
import { computed, CSSProperties, toRefs, unref } from "vue";
|
||||
import { computed, CSSProperties, defineComponent, PropType, StyleValue, toRefs, unref } from "vue";
|
||||
import LinkNode from "../system/LinkNode.vue";
|
||||
import MarkNode from "./MarkNode.vue";
|
||||
|
||||
const props = toRefs(defineProps<FeatureComponent<GenericBar>>());
|
||||
export default defineComponent({
|
||||
props: {
|
||||
progress: {
|
||||
type: Object as PropType<DecimalSource>,
|
||||
required: true
|
||||
},
|
||||
width: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
height: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
direction: {
|
||||
type: Object as PropType<Direction>,
|
||||
required: true
|
||||
},
|
||||
display: [Object, String] as PropType<CoercableComponent>,
|
||||
visibility: {
|
||||
type: Object as PropType<Visibility>,
|
||||
required: true
|
||||
},
|
||||
style: Object as PropType<StyleValue>,
|
||||
classes: Object as PropType<Record<string, boolean>>,
|
||||
borderStyle: Object as PropType<StyleValue>,
|
||||
textStyle: Object as PropType<StyleValue>,
|
||||
baseStyle: Object as PropType<StyleValue>,
|
||||
fillStyle: Object as PropType<StyleValue>,
|
||||
mark: [Boolean, String],
|
||||
id: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
const { progress, width, height, direction, display } = toRefs(props);
|
||||
|
||||
const normalizedProgress = computed(() => {
|
||||
let progress =
|
||||
props.progress.value instanceof Decimal
|
||||
? props.progress.value.toNumber()
|
||||
: Number(props.progress.value);
|
||||
return (1 - Math.min(Math.max(progress, 0), 1)) * 100;
|
||||
let progressNumber =
|
||||
progress.value instanceof Decimal
|
||||
? progress.value.toNumber()
|
||||
: Number(progress.value);
|
||||
return (1 - Math.min(Math.max(progressNumber, 0), 1)) * 100;
|
||||
});
|
||||
|
||||
const barStyle = computed(() => {
|
||||
const barStyle: Partial<CSSProperties> = {
|
||||
width: unref(props.width) + 0.5 + "px",
|
||||
height: unref(props.height) + 0.5 + "px"
|
||||
width: unref(width) + 0.5 + "px",
|
||||
height: unref(height) + 0.5 + "px"
|
||||
};
|
||||
switch (unref(props.direction)) {
|
||||
switch (unref(direction)) {
|
||||
case Direction.Up:
|
||||
barStyle.clipPath = `inset(${normalizedProgress.value}% 0% 0% 0%)`;
|
||||
barStyle.width = unref(props.width) + 1 + "px";
|
||||
barStyle.width = unref(width) + 1 + "px";
|
||||
break;
|
||||
case Direction.Down:
|
||||
barStyle.clipPath = `inset(0% 0% ${normalizedProgress.value}% 0%)`;
|
||||
barStyle.width = unref(props.width) + 1 + "px";
|
||||
barStyle.width = unref(width) + 1 + "px";
|
||||
break;
|
||||
case Direction.Right:
|
||||
barStyle.clipPath = `inset(0% ${normalizedProgress.value}% 0% 0%)`;
|
||||
|
@ -77,8 +113,19 @@ const barStyle = computed(() => {
|
|||
});
|
||||
|
||||
const component = computed(() => {
|
||||
const display = props.display.value;
|
||||
return display && coerceComponent(display);
|
||||
const currDisplay = unref(display);
|
||||
return currDisplay && coerceComponent(unref(currDisplay));
|
||||
});
|
||||
|
||||
return {
|
||||
normalizedProgress,
|
||||
barStyle,
|
||||
component,
|
||||
MarkNode,
|
||||
LinkNode,
|
||||
Visibility
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -18,62 +18,114 @@
|
|||
{{ buttonText }}
|
||||
</button>
|
||||
<component v-if="component" :is="component" />
|
||||
<default-challenge-display v-else :id="id" />
|
||||
<MarkNode :mark="mark" />
|
||||
<LinkNode :id="id" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="tsx">
|
||||
<script lang="tsx">
|
||||
import { GenericChallenge } from "@/features/challenge";
|
||||
import { FeatureComponent, Visibility } from "@/features/feature";
|
||||
import { StyleValue, Visibility } from "@/features/feature";
|
||||
import { coerceComponent, isCoercableComponent } from "@/util/vue";
|
||||
import { computed, toRefs } from "vue";
|
||||
import { computed, defineComponent, PropType, toRefs, UnwrapRef } from "vue";
|
||||
import LinkNode from "../system/LinkNode.vue";
|
||||
import MarkNode from "./MarkNode.vue";
|
||||
|
||||
const props = toRefs(defineProps<FeatureComponent<GenericChallenge>>());
|
||||
export default defineComponent({
|
||||
props: {
|
||||
active: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
maxed: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
canComplete: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
display: Object as PropType<UnwrapRef<GenericChallenge["display"]>>,
|
||||
visibility: {
|
||||
type: Object as PropType<Visibility>,
|
||||
required: true
|
||||
},
|
||||
style: Object as PropType<StyleValue>,
|
||||
classes: Object as PropType<Record<string, boolean>>,
|
||||
completed: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
canStart: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
mark: [Boolean, String],
|
||||
id: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
toggle: {
|
||||
type: Function as PropType<VoidFunction>,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
const { active, maxed, canComplete, display } = toRefs(props);
|
||||
|
||||
const buttonText = computed(() => {
|
||||
if (props.active.value) {
|
||||
return props.canComplete.value ? "Finish" : "Exit Early";
|
||||
if (active.value) {
|
||||
return canComplete.value ? "Finish" : "Exit Early";
|
||||
}
|
||||
if (props.maxed.value) {
|
||||
if (maxed.value) {
|
||||
return "Completed";
|
||||
}
|
||||
return "Start";
|
||||
});
|
||||
|
||||
const component = computed(() => {
|
||||
const display = props.display.value;
|
||||
if (display == null) {
|
||||
const currDisplay = display.value;
|
||||
if (currDisplay == null) {
|
||||
return null;
|
||||
}
|
||||
if (isCoercableComponent(display)) {
|
||||
return coerceComponent(display);
|
||||
if (isCoercableComponent(currDisplay)) {
|
||||
return coerceComponent(currDisplay);
|
||||
}
|
||||
return (
|
||||
<span>
|
||||
<template v-if={display.title}>
|
||||
<template v-if={currDisplay.title}>
|
||||
{/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */}
|
||||
<component v-is={coerceComponent(display.title!, "h3")} />
|
||||
<component v-is={coerceComponent(currDisplay.title!, "h3")} />
|
||||
</template>
|
||||
<component v-is={coerceComponent(display.description, "div")} />
|
||||
<div v-if={display.goal}>
|
||||
<component v-is={coerceComponent(currDisplay.description, "div")} />
|
||||
<div v-if={currDisplay.goal}>
|
||||
<br />
|
||||
{/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */}
|
||||
Goal: <component v-is={coerceComponent(display.goal!)} />
|
||||
Goal: <component v-is={coerceComponent(currDisplay.goal!)} />
|
||||
</div>
|
||||
<div v-if={display.reward}>
|
||||
<div v-if={currDisplay.reward}>
|
||||
<br />
|
||||
{/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */}
|
||||
Reward: <component v-is={coerceComponent(display.reward!)} />
|
||||
Reward: <component v-is={coerceComponent(currDisplay.reward!)} />
|
||||
</div>
|
||||
<div v-if={display.effectDisplay}>
|
||||
Currently: {/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */}
|
||||
<component v-is={coerceComponent(display.effectDisplay!)} />
|
||||
<div v-if={currDisplay.effectDisplay}>
|
||||
Currently:{" "}
|
||||
{/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */}
|
||||
<component v-is={coerceComponent(currDisplay.effectDisplay!)} />
|
||||
</div>
|
||||
</span>
|
||||
);
|
||||
});
|
||||
|
||||
return {
|
||||
buttonText,
|
||||
component,
|
||||
MarkNode,
|
||||
LinkNode,
|
||||
Visibility
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
:class="{
|
||||
feature: true,
|
||||
clickable: true,
|
||||
can: props.canClick,
|
||||
can: canClick,
|
||||
locked: !canClick,
|
||||
small,
|
||||
...classes
|
||||
|
@ -26,36 +26,73 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="tsx">
|
||||
<script lang="tsx">
|
||||
import { GenericClickable } from "@/features/clickable";
|
||||
import { FeatureComponent, Visibility } from "@/features/feature";
|
||||
import { StyleValue, Visibility } from "@/features/feature";
|
||||
import { coerceComponent, isCoercableComponent, setupHoldToClick } from "@/util/vue";
|
||||
import { computed, toRefs, unref } from "vue";
|
||||
import { computed, defineComponent, PropType, toRefs, unref, UnwrapRef } from "vue";
|
||||
import LinkNode from "../system/LinkNode.vue";
|
||||
import MarkNode from "./MarkNode.vue";
|
||||
|
||||
const props = toRefs(defineProps<FeatureComponent<GenericClickable>>());
|
||||
export default defineComponent({
|
||||
props: {
|
||||
display: {
|
||||
type: Object as PropType<UnwrapRef<GenericClickable["display"]>>,
|
||||
required: true
|
||||
},
|
||||
visibility: {
|
||||
type: Object as PropType<Visibility>,
|
||||
required: true
|
||||
},
|
||||
style: Object as PropType<StyleValue>,
|
||||
classes: Object as PropType<Record<string, boolean>>,
|
||||
onClick: Function as PropType<VoidFunction>,
|
||||
onHold: Function as PropType<VoidFunction>,
|
||||
canClick: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
small: Boolean,
|
||||
mark: [Boolean, String],
|
||||
id: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
const { display, onClick, onHold } = toRefs(props);
|
||||
|
||||
const component = computed(() => {
|
||||
const display = unref(props.display);
|
||||
if (display == null) {
|
||||
const currDisplay = unref(display);
|
||||
if (currDisplay == null) {
|
||||
return null;
|
||||
}
|
||||
if (isCoercableComponent(display)) {
|
||||
return coerceComponent(display);
|
||||
if (isCoercableComponent(currDisplay)) {
|
||||
return coerceComponent(currDisplay);
|
||||
}
|
||||
return (
|
||||
<span>
|
||||
<div v-if={display.title}>
|
||||
<div v-if={currDisplay.title}>
|
||||
{/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */}
|
||||
<component v-is={coerceComponent(display.title!, "h2")} />
|
||||
<component v-is={coerceComponent(currDisplay.title!, "h2")} />
|
||||
</div>
|
||||
<component v-is={coerceComponent(display.description, "div")} />
|
||||
<component v-is={coerceComponent(currDisplay.description, "div")} />
|
||||
</span>
|
||||
);
|
||||
});
|
||||
|
||||
const { start, stop } = setupHoldToClick(props.onClick, props.onHold);
|
||||
const { start, stop } = setupHoldToClick(onClick, onHold);
|
||||
|
||||
return {
|
||||
start,
|
||||
stop,
|
||||
component,
|
||||
LinkNode,
|
||||
MarkNode,
|
||||
Visibility
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
@ -13,13 +13,32 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { FeatureComponent, Visibility } from "@/features/feature";
|
||||
import { GenericGrid } from "@/features/grid";
|
||||
import { defineComponent } from "vue";
|
||||
import GridCell from "./GridCell.vue";
|
||||
import { Visibility } from "@/features/feature";
|
||||
import { GridCell } from "@/features/grid";
|
||||
import { defineComponent, PropType } from "vue";
|
||||
import GridCellVue from "./GridCell.vue";
|
||||
|
||||
// https://github.com/thepaperpilot/The-Modding-Tree-X/issues/1
|
||||
export default defineComponent(function Grid(props: FeatureComponent<GenericGrid>) {
|
||||
return { ...props, GridCell, Visibility };
|
||||
export default defineComponent({
|
||||
props: {
|
||||
visibility: {
|
||||
type: Object as PropType<Visibility>,
|
||||
required: true
|
||||
},
|
||||
rows: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
cols: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
cells: {
|
||||
type: Object as PropType<Record<string, GridCell>>,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
setup() {
|
||||
return { GridCell: GridCellVue, Visibility };
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -19,22 +19,56 @@
|
|||
</button>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Visibility } from "@/features/feature";
|
||||
import { GridCell } from "@/features/grid";
|
||||
<script lang="ts">
|
||||
import { CoercableComponent, StyleValue, Visibility } from "@/features/feature";
|
||||
import { coerceComponent, setupHoldToClick } from "@/util/vue";
|
||||
import { computed, toRefs, unref } from "vue";
|
||||
import { computed, defineComponent, PropType, toRefs, unref } from "vue";
|
||||
import LinkNode from "../system/LinkNode.vue";
|
||||
|
||||
const props = toRefs(defineProps<GridCell>());
|
||||
export default defineComponent({
|
||||
props: {
|
||||
visibility: {
|
||||
type: Object as PropType<Visibility>,
|
||||
required: true
|
||||
},
|
||||
onClick: Function as PropType<VoidFunction>,
|
||||
onHold: Function as PropType<VoidFunction>,
|
||||
display: {
|
||||
type: [Object, String] as PropType<CoercableComponent>,
|
||||
required: true
|
||||
},
|
||||
title: [Object, String] as PropType<CoercableComponent>,
|
||||
style: Object as PropType<StyleValue>,
|
||||
canClick: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
id: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
const { onClick, onHold, title, display } = toRefs(props);
|
||||
|
||||
const { start, stop } = setupHoldToClick(props.onClick, props.onHold);
|
||||
const { start, stop } = setupHoldToClick(onClick, onHold);
|
||||
|
||||
const titleComponent = computed(() => {
|
||||
const title = unref(props.title);
|
||||
return title && coerceComponent(title);
|
||||
const currTitle = unref(title);
|
||||
return currTitle && coerceComponent(currTitle);
|
||||
});
|
||||
const component = computed(() => coerceComponent(unref(display)));
|
||||
|
||||
return {
|
||||
start,
|
||||
stop,
|
||||
titleComponent,
|
||||
component,
|
||||
Visibility,
|
||||
LinkNode
|
||||
};
|
||||
}
|
||||
});
|
||||
const component = computed(() => coerceComponent(unref(props.display)));
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
@ -23,21 +23,57 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
<script lang="ts">
|
||||
import themes from "@/data/themes";
|
||||
import { FeatureComponent, Visibility } from "@/features/feature";
|
||||
import { GenericInfobox } from "@/features/infobox";
|
||||
import { CoercableComponent, Visibility } from "@/features/feature";
|
||||
import settings from "@/game/settings";
|
||||
import { coerceComponent } from "@/util/vue";
|
||||
import { computed, toRefs, unref } from "vue";
|
||||
import LinkNode from "../system/LinkNode.vue";
|
||||
import CollapseTransition from "@ivanv/vue-collapse-transition/src/CollapseTransition.vue";
|
||||
import { computed, defineComponent, PropType, StyleValue, toRefs } from "vue";
|
||||
import LinkNode from "../system/LinkNode.vue";
|
||||
|
||||
const props = toRefs(defineProps<FeatureComponent<GenericInfobox>>());
|
||||
export default defineComponent({
|
||||
props: {
|
||||
visibility: {
|
||||
type: Object as PropType<Visibility>,
|
||||
required: true
|
||||
},
|
||||
display: {
|
||||
type: [Object, String] as PropType<CoercableComponent>,
|
||||
required: true
|
||||
},
|
||||
title: [Object, String] as PropType<CoercableComponent>,
|
||||
color: String,
|
||||
collapsed: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
style: Object as PropType<StyleValue>,
|
||||
titleStyle: Object as PropType<StyleValue>,
|
||||
bodyStyle: Object as PropType<StyleValue>,
|
||||
classes: Object as PropType<Record<string, boolean>>,
|
||||
id: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
const { title, display } = toRefs(props);
|
||||
|
||||
const titleComponent = computed(() => coerceComponent(unref(props.title)));
|
||||
const bodyComponent = computed(() => coerceComponent(unref(props.display)));
|
||||
const titleComponent = computed(() => title.value && coerceComponent(title.value));
|
||||
const bodyComponent = computed(() => coerceComponent(display.value));
|
||||
const stacked = computed(() => themes[settings.theme].stackedInfoboxes);
|
||||
|
||||
return {
|
||||
titleComponent,
|
||||
bodyComponent,
|
||||
stacked,
|
||||
LinkNode,
|
||||
CollapseTransition,
|
||||
Visibility
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
@ -17,15 +17,14 @@ import { coerceComponent } from "@/util/vue";
|
|||
import { computed, StyleValue, toRefs } from "vue";
|
||||
import ResourceVue from "../system/Resource.vue";
|
||||
|
||||
const props = toRefs(
|
||||
defineProps<{
|
||||
const _props = defineProps<{
|
||||
resource: Resource;
|
||||
color?: string;
|
||||
classes?: Record<string, boolean>;
|
||||
style?: StyleValue;
|
||||
effectDisplay?: CoercableComponent;
|
||||
}>()
|
||||
);
|
||||
}>();
|
||||
const props = toRefs(_props);
|
||||
|
||||
const effectComponent = computed(() => {
|
||||
const effectDisplay = props.effectDisplay?.value;
|
||||
|
|
|
@ -10,37 +10,67 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="tsx">
|
||||
import { FeatureComponent, Visibility } from "@/features/feature";
|
||||
<script lang="tsx">
|
||||
import { StyleValue, Visibility } from "@/features/feature";
|
||||
import { GenericMilestone } from "@/features/milestone";
|
||||
import { coerceComponent, isCoercableComponent } from "@/util/vue";
|
||||
import { computed, toRefs } from "vue";
|
||||
import { computed, defineComponent, PropType, toRefs, UnwrapRef } from "vue";
|
||||
import LinkNode from "../system/LinkNode.vue";
|
||||
|
||||
const props = toRefs(defineProps<FeatureComponent<GenericMilestone>>());
|
||||
export default defineComponent({
|
||||
props: {
|
||||
visibility: {
|
||||
type: Object as PropType<Visibility>,
|
||||
required: true
|
||||
},
|
||||
display: {
|
||||
type: Object as PropType<UnwrapRef<GenericMilestone["display"]>>,
|
||||
required: true
|
||||
},
|
||||
style: Object as PropType<StyleValue>,
|
||||
classes: Object as PropType<Record<string, boolean>>,
|
||||
earned: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
id: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
const { display } = toRefs(props);
|
||||
|
||||
const component = computed(() => {
|
||||
const display = props.display.value;
|
||||
if (display == null) {
|
||||
const currDisplay = display.value;
|
||||
if (currDisplay == null) {
|
||||
return null;
|
||||
}
|
||||
if (isCoercableComponent(display)) {
|
||||
return coerceComponent(display);
|
||||
if (isCoercableComponent(currDisplay)) {
|
||||
return coerceComponent(currDisplay);
|
||||
}
|
||||
return (
|
||||
<span>
|
||||
<component v-is={coerceComponent(display.requirement, "h3")} />
|
||||
<div v-if={display.effectDisplay}>
|
||||
<component v-is={coerceComponent(currDisplay.requirement, "h3")} />
|
||||
<div v-if={currDisplay.effectDisplay}>
|
||||
{/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */}
|
||||
<component v-is={coerceComponent(display.effectDisplay!, "b")} />
|
||||
<component v-is={coerceComponent(currDisplay.effectDisplay!, "b")} />
|
||||
</div>
|
||||
<div v-if={display.optionsDisplay}>
|
||||
<div v-if={currDisplay.optionsDisplay}>
|
||||
{/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */}
|
||||
<component v-is={coerceComponent(display.optionsDisplay!, "span")} />
|
||||
<component v-is={coerceComponent(currDisplay.optionsDisplay!, "span")} />
|
||||
</div>
|
||||
</span>
|
||||
);
|
||||
});
|
||||
|
||||
return {
|
||||
component,
|
||||
LinkNode,
|
||||
Visibility
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
@ -7,6 +7,7 @@ import { CoercableComponent } from "@/features/feature";
|
|||
import { coerceComponent } from "@/util/vue";
|
||||
import { computed, toRefs } from "vue";
|
||||
|
||||
const { display } = toRefs(defineProps<{ display: CoercableComponent }>());
|
||||
const _props = defineProps<{ display: CoercableComponent }>();
|
||||
const { display } = toRefs(_props);
|
||||
const component = computed(() => coerceComponent(display));
|
||||
</script>
|
||||
|
|
|
@ -1,30 +1,50 @@
|
|||
<template>
|
||||
<button
|
||||
@click="emits('selectTab')"
|
||||
@click="selectTab"
|
||||
class="tabButton"
|
||||
:style="style"
|
||||
:style="unref(style)"
|
||||
:class="{
|
||||
active,
|
||||
...classes
|
||||
...unref(classes)
|
||||
}"
|
||||
>
|
||||
<component :is="component" />
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { FeatureComponent } from "@/features/feature";
|
||||
import { GenericTabButton } from "@/features/tabFamily";
|
||||
import { coerceComponent } from "@/util/vue";
|
||||
import { computed, toRefs } from "vue";
|
||||
<script lang="ts">
|
||||
import { CoercableComponent, StyleValue } from "@/features/feature";
|
||||
import { ProcessedComputable } from "@/util/computed";
|
||||
import { computeComponent } from "@/util/vue";
|
||||
import { defineComponent, PropType, toRefs, unref } from "vue";
|
||||
|
||||
const props = toRefs(defineProps<FeatureComponent<GenericTabButton> & { active: boolean }>());
|
||||
export default defineComponent({
|
||||
props: {
|
||||
display: {
|
||||
type: [Object, String] as PropType<ProcessedComputable<CoercableComponent>>,
|
||||
required: true
|
||||
},
|
||||
style: Object as PropType<ProcessedComputable<StyleValue>>,
|
||||
classes: Object as PropType<ProcessedComputable<Record<string, boolean>>>,
|
||||
active: [Object, Boolean] as PropType<ProcessedComputable<boolean>>
|
||||
},
|
||||
emits: ["selectTab"],
|
||||
setup(props, { emit }) {
|
||||
const { display } = toRefs(props);
|
||||
|
||||
const emits = defineEmits<{
|
||||
(e: "selectTab"): void;
|
||||
}>();
|
||||
const component = computeComponent(display);
|
||||
|
||||
const component = computed(() => coerceComponent(props.display.value));
|
||||
function selectTab() {
|
||||
emit("selectTab");
|
||||
}
|
||||
|
||||
return {
|
||||
selectTab,
|
||||
component,
|
||||
unref
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<div class="tab-buttons" :class="{ floating }">
|
||||
<TabButton
|
||||
v-for="(button, id) in tabs"
|
||||
@selectTab="selectTab(id)"
|
||||
@selectTab="selected = id"
|
||||
:key="id"
|
||||
:active="button.tab === activeTab"
|
||||
v-bind="button"
|
||||
|
@ -12,48 +12,81 @@
|
|||
</div>
|
||||
</Sticky>
|
||||
<template v-if="activeTab">
|
||||
<component :is="display" />
|
||||
<component :is="display!" />
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
<script lang="ts">
|
||||
import themes from "@/data/themes";
|
||||
import { FeatureComponent, PersistentState } from "@/features/feature";
|
||||
import { GenericTabFamily } from "@/features/tabFamily";
|
||||
import { CoercableComponent } from "@/features/feature";
|
||||
import { GenericTab } from "@/features/tab";
|
||||
import { GenericTabButton } from "@/features/tabFamily";
|
||||
import settings from "@/game/settings";
|
||||
import { coerceComponent, isCoercableComponent } from "@/util/vue";
|
||||
import { computed, toRefs, unref } from "vue";
|
||||
import { computed, defineComponent, PropType, toRefs, unref } from "vue";
|
||||
import Sticky from "../system/Sticky.vue";
|
||||
import TabButton from "./TabButton.vue";
|
||||
|
||||
const props = toRefs(defineProps<FeatureComponent<GenericTabFamily>>());
|
||||
export default defineComponent({
|
||||
props: {
|
||||
activeTab: {
|
||||
type: Object as PropType<GenericTab | CoercableComponent | null>,
|
||||
required: true
|
||||
},
|
||||
selected: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
tabs: {
|
||||
type: Object as PropType<Record<string, GenericTabButton>>,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
const { activeTab } = toRefs(props);
|
||||
|
||||
const floating = computed(() => {
|
||||
return themes[settings.theme].floatingTabs;
|
||||
});
|
||||
|
||||
const display = computed(() => {
|
||||
const activeTab = props.activeTab.value;
|
||||
return activeTab
|
||||
? coerceComponent(isCoercableComponent(activeTab) ? activeTab : activeTab.display)
|
||||
const currActiveTab = activeTab.value;
|
||||
return currActiveTab
|
||||
? coerceComponent(
|
||||
isCoercableComponent(currActiveTab)
|
||||
? currActiveTab
|
||||
: unref(currActiveTab.display)
|
||||
)
|
||||
: null;
|
||||
});
|
||||
|
||||
const classes = computed(() => {
|
||||
const activeTab = props.activeTab.value;
|
||||
const currActiveTab = activeTab.value;
|
||||
const tabClasses =
|
||||
isCoercableComponent(activeTab) || !activeTab ? undefined : unref(activeTab.classes);
|
||||
isCoercableComponent(currActiveTab) || !currActiveTab
|
||||
? undefined
|
||||
: unref(currActiveTab.classes);
|
||||
return tabClasses;
|
||||
});
|
||||
|
||||
const style = computed(() => {
|
||||
const activeTab = props.activeTab.value;
|
||||
return isCoercableComponent(activeTab) || !activeTab ? undefined : unref(activeTab.style);
|
||||
const currActiveTab = activeTab.value;
|
||||
return isCoercableComponent(currActiveTab) || !currActiveTab
|
||||
? undefined
|
||||
: unref(currActiveTab.style);
|
||||
});
|
||||
|
||||
function selectTab(tab: string) {
|
||||
props[PersistentState].value = tab;
|
||||
return {
|
||||
floating,
|
||||
display,
|
||||
classes,
|
||||
style,
|
||||
Sticky,
|
||||
TabButton
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
@ -20,47 +20,97 @@
|
|||
</button>
|
||||
</template>
|
||||
|
||||
<script setup lang="tsx">
|
||||
import { FeatureComponent, Visibility } from "@/features/feature";
|
||||
import { displayResource } from "@/features/resource";
|
||||
<script lang="tsx">
|
||||
import { StyleValue, Visibility } from "@/features/feature";
|
||||
import { displayResource, Resource } from "@/features/resource";
|
||||
import { GenericUpgrade } from "@/features/upgrade";
|
||||
import { DecimalSource } from "@/lib/break_eternity";
|
||||
import { coerceComponent, isCoercableComponent } from "@/util/vue";
|
||||
import { computed, toRefs, unref } from "vue";
|
||||
import { computed, defineComponent, PropType, Ref, toRef, toRefs, unref, UnwrapRef } from "vue";
|
||||
import LinkNode from "../system/LinkNode.vue";
|
||||
import MarkNode from "./MarkNode.vue";
|
||||
|
||||
const props = toRefs(defineProps<FeatureComponent<GenericUpgrade>>());
|
||||
export default defineComponent({
|
||||
props: {
|
||||
display: {
|
||||
type: Object as PropType<UnwrapRef<GenericUpgrade["display"]>>,
|
||||
required: true
|
||||
},
|
||||
visibility: {
|
||||
type: Object as PropType<Visibility>,
|
||||
required: true
|
||||
},
|
||||
style: Object as PropType<StyleValue>,
|
||||
classes: Object as PropType<Record<string, boolean>>,
|
||||
resource: {
|
||||
type: Object as PropType<Resource>,
|
||||
required: true
|
||||
},
|
||||
cost: {
|
||||
type: Object as PropType<DecimalSource>,
|
||||
required: true
|
||||
},
|
||||
canPurchase: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
bought: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
mark: [Boolean, String],
|
||||
id: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
purchase: {
|
||||
type: Function as PropType<VoidFunction>,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
const { display, cost } = toRefs(props);
|
||||
const resource = toRef(props, "resource") as unknown as Ref<Resource>;
|
||||
|
||||
const component = computed(() => {
|
||||
const display = unref(props.display);
|
||||
if (display == null) {
|
||||
const currDisplay = display.value;
|
||||
if (currDisplay == null) {
|
||||
return null;
|
||||
}
|
||||
if (isCoercableComponent(display)) {
|
||||
return coerceComponent(display);
|
||||
if (isCoercableComponent(currDisplay)) {
|
||||
return coerceComponent(currDisplay);
|
||||
}
|
||||
return (
|
||||
<span>
|
||||
<div v-if={display.title}>
|
||||
<div v-if={currDisplay.title}>
|
||||
{/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */}
|
||||
<component v-is={coerceComponent(display.title!, "h2")} />
|
||||
<component v-is={coerceComponent(currDisplay.title!, "h2")} />
|
||||
</div>
|
||||
<component v-is={coerceComponent(display.description, "div")} />
|
||||
<div v-if={display.effectDisplay}>
|
||||
<component v-is={coerceComponent(currDisplay.description, "div")} />
|
||||
<div v-if={currDisplay.effectDisplay}>
|
||||
<br />
|
||||
{/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */}
|
||||
Currently: <component v-is={coerceComponent(display.effectDisplay!)} />
|
||||
Currently: <component v-is={coerceComponent(currDisplay.effectDisplay!)} />
|
||||
</div>
|
||||
<template v-if={unref(props.resource) != null && unref(props.cost) != null}>
|
||||
<template v-if={resource.value != null && cost.value != null}>
|
||||
<br />
|
||||
{/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */}
|
||||
Cost: {displayResource(unref(props.resource)!, unref(props.cost))}{" "}
|
||||
Cost: {displayResource(resource.value, cost.value)}{" "}
|
||||
{/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */}
|
||||
{unref(props.resource)!.displayName}
|
||||
{resource.value.displayName}
|
||||
</template>
|
||||
</span>
|
||||
);
|
||||
});
|
||||
|
||||
return {
|
||||
component,
|
||||
LinkNode,
|
||||
MarkNode,
|
||||
Visibility
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
@ -58,7 +58,8 @@ import panZoom from "vue-panzoom";
|
|||
import BoardLinkVue from "./BoardLink.vue";
|
||||
import BoardNodeVue from "./BoardNode.vue";
|
||||
|
||||
const props = toRefs(defineProps<FeatureComponent<GenericBoard>>());
|
||||
const _props = defineProps<FeatureComponent<GenericBoard>>();
|
||||
const props = toRefs(_props);
|
||||
|
||||
const lastMousePosition = ref({ x: 0, y: 0 });
|
||||
const dragged = ref({ x: 0, y: 0 });
|
||||
|
|
|
@ -14,11 +14,10 @@
|
|||
import { BoardNodeLink } from "@/features/board";
|
||||
import { computed, toRefs, unref } from "vue";
|
||||
|
||||
const props = toRefs(
|
||||
defineProps<{
|
||||
const _props = defineProps<{
|
||||
link: BoardNodeLink;
|
||||
}>()
|
||||
);
|
||||
}>();
|
||||
const props = toRefs(_props);
|
||||
|
||||
const startPosition = computed(() => {
|
||||
const position = props.link.value.startNode.position;
|
||||
|
|
|
@ -182,8 +182,7 @@ import { computed, ref, toRefs, unref, watch } from "vue";
|
|||
|
||||
const sqrtTwo = Math.sqrt(2);
|
||||
|
||||
const props = toRefs(
|
||||
defineProps<{
|
||||
const _props = defineProps<{
|
||||
node: BoardNode;
|
||||
nodeType: GenericNodeType;
|
||||
dragging?: BoardNode;
|
||||
|
@ -195,8 +194,8 @@ const props = toRefs(
|
|||
receivingNode?: boolean;
|
||||
selectedNode?: BoardNode | null;
|
||||
selectedAction?: GenericBoardNodeAction | null;
|
||||
}>()
|
||||
);
|
||||
}>();
|
||||
const props = toRefs(_props);
|
||||
const emit = defineEmits<{
|
||||
(e: "mouseDown", event: MouseEvent | TouchEvent, node: number, isDraggable: boolean): void;
|
||||
(e: "endDragging", node: number): void;
|
||||
|
|
|
@ -1,12 +1,18 @@
|
|||
<template>
|
||||
<span class="row" v-for="(row, index) in nodes" :key="index">
|
||||
<TreeNode v-for="(node, nodeIndex) in row" :key="nodeIndex" v-bind="wrapFeature(node)" />
|
||||
<TreeNode
|
||||
v-for="(node, nodeIndex) in row"
|
||||
:key="nodeIndex"
|
||||
v-bind="node"
|
||||
:force-tooltip="node.forceTooltip"
|
||||
/>
|
||||
</span>
|
||||
<span class="left-side-nodes" v-if="leftSideNodes">
|
||||
<TreeNode
|
||||
v-for="(node, nodeIndex) in leftSideNodes"
|
||||
:key="nodeIndex"
|
||||
v-bind="wrapFeature(node)"
|
||||
v-bind="node"
|
||||
:force-tooltip="node.forceTooltip"
|
||||
small
|
||||
/>
|
||||
</span>
|
||||
|
@ -14,21 +20,30 @@
|
|||
<TreeNode
|
||||
v-for="(node, nodeIndex) in rightSideNodes"
|
||||
:key="nodeIndex"
|
||||
v-bind="wrapFeature(node)"
|
||||
v-bind="node"
|
||||
:force-tooltip="node.forceTooltip"
|
||||
small
|
||||
/>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { FeatureComponent, wrapFeature } from "@/features/feature";
|
||||
import { GenericTree } from "@/features/tree";
|
||||
import { defineComponent } from "vue";
|
||||
import { GenericTreeNode } from "@/features/tree";
|
||||
import { defineComponent, PropType } from "vue";
|
||||
import TreeNode from "./TreeNode.vue";
|
||||
|
||||
// https://github.com/thepaperpilot/The-Modding-Tree-X/issues/1
|
||||
export default defineComponent(function Grid(props: FeatureComponent<GenericTree>) {
|
||||
return { ...props, TreeNode, wrapFeature };
|
||||
export default defineComponent({
|
||||
props: {
|
||||
nodes: {
|
||||
type: Array as PropType<GenericTreeNode[][]>,
|
||||
required: true
|
||||
},
|
||||
leftSideNodes: Array as PropType<GenericTreeNode[]>,
|
||||
rightSideNodes: Array as PropType<GenericTreeNode[]>
|
||||
},
|
||||
setup() {
|
||||
return { TreeNode };
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1,25 +1,15 @@
|
|||
<template>
|
||||
<Tooltip
|
||||
v-if="visibility !== Visibility.None"
|
||||
v-show="visibility === Visibility.Visible"
|
||||
v-bind="
|
||||
typeof tooltip === 'object' && !isCoercableComponent(tooltip)
|
||||
? wrapFeature(tooltip)
|
||||
: null
|
||||
"
|
||||
:display="
|
||||
typeof tooltip === 'object'
|
||||
? isCoercableComponent(tooltip)
|
||||
? unref(tooltip)
|
||||
: tooltip.display
|
||||
: tooltip || ''
|
||||
"
|
||||
v-if="unref(visibility) !== Visibility.None"
|
||||
v-show="unref(visibility) === Visibility.Visible"
|
||||
v-bind="tooltipToBind"
|
||||
:display="tooltipDisplay"
|
||||
:force="forceTooltip"
|
||||
:class="{
|
||||
treeNode: true,
|
||||
can: canClick,
|
||||
small,
|
||||
...classes
|
||||
can: unref(canClick),
|
||||
small: unref(small),
|
||||
...unref(classes)
|
||||
}"
|
||||
>
|
||||
<button
|
||||
|
@ -32,51 +22,113 @@
|
|||
@touchcancel="stop"
|
||||
:style="[
|
||||
{
|
||||
backgroundColor: color,
|
||||
boxShadow: `-4px -4px 4px rgba(0, 0, 0, 0.25) inset, 0 0 20px ${glowColor}`
|
||||
backgroundColor: unref(color),
|
||||
boxShadow: `-4px -4px 4px rgba(0, 0, 0, 0.25) inset, 0 0 20px ${unref(
|
||||
glowColor
|
||||
)}`
|
||||
},
|
||||
style ?? []
|
||||
unref(style) ?? []
|
||||
]"
|
||||
:disabled="!canClick"
|
||||
:disabled="!unref(canClick)"
|
||||
>
|
||||
<component :is="component" />
|
||||
</button>
|
||||
<MarkNode :mark="mark" />
|
||||
<LinkNode :id="id" />
|
||||
<MarkNode :mark="unref(mark)" />
|
||||
<LinkNode :id="unref(id)" />
|
||||
</Tooltip>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { GenericTreeNode } from "@/features/tree";
|
||||
import { coerceComponent, isCoercableComponent, setupHoldToClick } from "@/util/vue";
|
||||
import { computed, toRefs, unref } from "vue";
|
||||
import Tooltip from "@/components/system/Tooltip.vue";
|
||||
import MarkNode from "../MarkNode.vue";
|
||||
import { FeatureComponent, Visibility, wrapFeature } from "@/features/feature";
|
||||
<script lang="ts">
|
||||
import TooltipVue from "@/components/system/Tooltip.vue";
|
||||
import { CoercableComponent, StyleValue, Visibility } from "@/features/feature";
|
||||
import { Tooltip } from "@/features/tooltip";
|
||||
import { ProcessedComputable } from "@/util/computed";
|
||||
import {
|
||||
computeOptionalComponent,
|
||||
isCoercableComponent,
|
||||
setupHoldToClick,
|
||||
unwrapRef
|
||||
} from "@/util/vue";
|
||||
import { computed, defineComponent, PropType, Ref, toRefs, unref } from "vue";
|
||||
import LinkNode from "../../system/LinkNode.vue";
|
||||
import MarkNode from "../MarkNode.vue";
|
||||
|
||||
const props = toRefs(
|
||||
defineProps<
|
||||
FeatureComponent<GenericTreeNode> & {
|
||||
small?: boolean;
|
||||
}
|
||||
>()
|
||||
);
|
||||
export default defineComponent({
|
||||
props: {
|
||||
display: [Object, String] as PropType<ProcessedComputable<CoercableComponent>>,
|
||||
visibility: {
|
||||
type: Object as PropType<ProcessedComputable<Visibility>>,
|
||||
required: true
|
||||
},
|
||||
style: Object as PropType<ProcessedComputable<StyleValue>>,
|
||||
classes: Object as PropType<ProcessedComputable<Record<string, boolean>>>,
|
||||
tooltip: Object as PropType<ProcessedComputable<CoercableComponent | Tooltip>>,
|
||||
onClick: Function as PropType<VoidFunction>,
|
||||
onHold: Function as PropType<VoidFunction>,
|
||||
color: [Object, String] as PropType<ProcessedComputable<string>>,
|
||||
glowColor: [Object, String] as PropType<ProcessedComputable<string>>,
|
||||
forceTooltip: {
|
||||
type: Object as PropType<Ref<boolean>>,
|
||||
required: true
|
||||
},
|
||||
canClick: {
|
||||
type: [Object, Boolean] as PropType<ProcessedComputable<boolean>>,
|
||||
required: true
|
||||
},
|
||||
mark: [Object, Boolean, String] as PropType<ProcessedComputable<boolean | string>>,
|
||||
id: {
|
||||
type: [Object, String] as PropType<ProcessedComputable<string>>,
|
||||
required: true
|
||||
},
|
||||
small: [Object, Boolean] as PropType<ProcessedComputable<boolean>>
|
||||
},
|
||||
setup(props) {
|
||||
const { tooltip, forceTooltip, onClick, onHold, display } = toRefs(props);
|
||||
|
||||
function click(e: MouseEvent) {
|
||||
if (e.shiftKey && props.tooltip) {
|
||||
props.forceTooltip.value = !props.forceTooltip.value;
|
||||
if (e.shiftKey && tooltip) {
|
||||
forceTooltip.value = !forceTooltip.value;
|
||||
} else {
|
||||
unref(props.onClick)?.();
|
||||
unref(onClick)?.();
|
||||
}
|
||||
}
|
||||
|
||||
const component = computed(() => {
|
||||
const display = unref(props.display);
|
||||
return display && coerceComponent(display);
|
||||
const component = computeOptionalComponent(display);
|
||||
const tooltipDisplay = computed(() => {
|
||||
const currTooltip = unwrapRef(tooltip);
|
||||
|
||||
if (typeof currTooltip === "object" && !isCoercableComponent(currTooltip)) {
|
||||
return currTooltip.display;
|
||||
}
|
||||
return currTooltip || "";
|
||||
});
|
||||
const tooltipToBind = computed(() => {
|
||||
const currTooltip = unwrapRef(tooltip);
|
||||
|
||||
if (typeof currTooltip === "object" && !isCoercableComponent(currTooltip)) {
|
||||
return currTooltip;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
const { start, stop } = setupHoldToClick(props.onClick, props.onHold);
|
||||
const { start, stop } = setupHoldToClick(onClick, onHold);
|
||||
|
||||
return {
|
||||
click,
|
||||
start,
|
||||
stop,
|
||||
component,
|
||||
tooltipDisplay,
|
||||
tooltipToBind,
|
||||
Tooltip: TooltipVue,
|
||||
MarkNode,
|
||||
LinkNode,
|
||||
unref,
|
||||
Visibility,
|
||||
isCoercableComponent
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
@ -12,12 +12,11 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, toRefs, unref, watch } from "vue";
|
||||
|
||||
const props = toRefs(
|
||||
defineProps<{
|
||||
const _props = defineProps<{
|
||||
disabled?: boolean;
|
||||
skipConfirm?: boolean;
|
||||
}>()
|
||||
);
|
||||
}>();
|
||||
const props = toRefs(_props);
|
||||
const emit = defineEmits<{
|
||||
(e: "click"): void;
|
||||
(e: "confirmingChanged", value: boolean): void;
|
||||
|
@ -63,7 +62,8 @@ function cancel() {
|
|||
</style>
|
||||
|
||||
<style>
|
||||
.danger {
|
||||
.danger,
|
||||
.button.danger {
|
||||
position: relative;
|
||||
border: solid 2px var(--danger);
|
||||
border-right-width: 16px;
|
||||
|
|
|
@ -5,13 +5,11 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { nextTick, ref, toRefs } from "vue";
|
||||
import { nextTick, ref } from "vue";
|
||||
|
||||
toRefs(
|
||||
defineProps<{
|
||||
left?: boolean;
|
||||
}>()
|
||||
);
|
||||
}>();
|
||||
const emit = defineEmits<{
|
||||
(e: "click"): void;
|
||||
}>();
|
||||
|
|
|
@ -21,15 +21,14 @@ import "vue-next-select/dist/index.css";
|
|||
|
||||
export type SelectOption = { label: string; value: unknown };
|
||||
|
||||
const props = toRefs(
|
||||
defineProps<{
|
||||
const _props = defineProps<{
|
||||
title?: CoercableComponent;
|
||||
modelValue?: unknown;
|
||||
options: SelectOption[];
|
||||
placeholder?: string;
|
||||
closeOnSelect?: boolean;
|
||||
}>()
|
||||
);
|
||||
}>();
|
||||
const props = toRefs(_props);
|
||||
const emit = defineEmits<{
|
||||
(e: "update:modelValue", value: unknown): void;
|
||||
}>();
|
||||
|
|
|
@ -11,14 +11,13 @@
|
|||
import { computed, toRefs, unref } from "vue";
|
||||
import Tooltip from "../system/Tooltip.vue";
|
||||
|
||||
const props = toRefs(
|
||||
defineProps<{
|
||||
const _props = defineProps<{
|
||||
title?: string;
|
||||
modelValue?: number;
|
||||
min?: number;
|
||||
max?: number;
|
||||
}>()
|
||||
);
|
||||
}>();
|
||||
const props = toRefs(_props);
|
||||
const emit = defineEmits<{
|
||||
(e: "update:modelValue", value: number): void;
|
||||
}>();
|
||||
|
|
|
@ -31,15 +31,14 @@ import { coerceComponent } from "@/util/vue";
|
|||
import { computed, onMounted, ref, toRefs, unref } from "vue";
|
||||
import VueTextareaAutosize from "vue-textarea-autosize";
|
||||
|
||||
const props = toRefs(
|
||||
defineProps<{
|
||||
const _props = defineProps<{
|
||||
title?: CoercableComponent;
|
||||
modelValue?: string;
|
||||
textArea?: boolean;
|
||||
placeholder?: string;
|
||||
maxHeight?: number;
|
||||
}>()
|
||||
);
|
||||
}>();
|
||||
const props = toRefs(_props);
|
||||
const emit = defineEmits<{
|
||||
(e: "update:modelValue", value: string): void;
|
||||
(e: "submit"): void;
|
||||
|
|
|
@ -10,12 +10,11 @@ import { CoercableComponent } from "@/features/feature";
|
|||
import { coerceComponent } from "@/util/vue";
|
||||
import { computed, toRefs, unref } from "vue";
|
||||
|
||||
const props = toRefs(
|
||||
defineProps<{
|
||||
const _props = defineProps<{
|
||||
title?: CoercableComponent;
|
||||
modelValue?: boolean;
|
||||
}>()
|
||||
);
|
||||
}>();
|
||||
const props = toRefs(_props);
|
||||
const emit = defineEmits<{
|
||||
(e: "update:modelValue", value: boolean): void;
|
||||
}>();
|
||||
|
|
|
@ -5,9 +5,9 @@
|
|||
<div class="inner-tab">
|
||||
<Layer
|
||||
v-if="layerKeys.includes(tab)"
|
||||
v-bind="wrapFeature(layers[tab])"
|
||||
v-bind="layers[tab]!"
|
||||
:index="index"
|
||||
:tab="() => ($refs[`tab-${index}`] as HTMLElement | undefined)"
|
||||
:tab="() => (($refs[`tab-${index}`] as HTMLElement[] | undefined)?.[0])"
|
||||
/>
|
||||
<component :is="tab" :index="index" v-else />
|
||||
</div>
|
||||
|
@ -18,7 +18,6 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import modInfo from "@/data/modInfo.json";
|
||||
import { wrapFeature } from "@/features/feature";
|
||||
import { layers } from "@/game/layers";
|
||||
import player from "@/game/player";
|
||||
import { computed, toRef } from "vue";
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
<br />
|
||||
<div>
|
||||
<a :href="discordLink">
|
||||
<img src="images/discord.png" class="game-over-modal-discord" />
|
||||
<span class="material-icons game-over-modal-discord">discord</span>
|
||||
{{ discordName }}
|
||||
</a>
|
||||
</div>
|
||||
|
|
|
@ -64,7 +64,8 @@ import { computed, ref, toRefs, unref } from "vue";
|
|||
|
||||
const { title, logo, author, discordName, discordLink, versionNumber, versionTitle } = modInfo;
|
||||
|
||||
const props = toRefs(defineProps<{ changelog: typeof Changelog | null }>());
|
||||
const _props = defineProps<{ changelog: typeof Changelog | null }>();
|
||||
const props = toRefs(_props);
|
||||
|
||||
const isOpen = ref(false);
|
||||
|
||||
|
|
|
@ -1,51 +1,82 @@
|
|||
<template>
|
||||
<div class="layer-container">
|
||||
<button v-if="showGoBack" class="goBack" @click="goBack">←</button>
|
||||
<button class="layer-tab minimized" v-if="minimized" @click="minimized = false">
|
||||
<div>{{ name }}</div>
|
||||
<button class="layer-tab minimized" v-if="minimized.value" @click="minimized.value = false">
|
||||
<div>{{ unref(name) }}</div>
|
||||
</button>
|
||||
<div class="layer-tab" :style="style" :class="classes" v-else>
|
||||
<Links v-if="links" :links="links">
|
||||
<div class="layer-tab" :style="unref(style)" :class="unref(classes)" v-else>
|
||||
<Links v-if="links" :links="unref(links)">
|
||||
<component :is="component" />
|
||||
</Links>
|
||||
<component v-else :is="component" />
|
||||
</div>
|
||||
<button v-if="minimizable" class="minimize" @click="minimized = true">▼</button>
|
||||
<button v-if="unref(minimizable)" class="minimize" @click="minimized.value = true">
|
||||
▼
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
<script lang="ts">
|
||||
import Links from "@/components/system/Links.vue";
|
||||
import { FeatureComponent } from "@/features/feature";
|
||||
import { GenericLayer } from "@/game/layers";
|
||||
import { coerceComponent } from "@/util/vue";
|
||||
import { computed, nextTick, toRefs, unref, watch } from "vue";
|
||||
import modInfo from "@/data/modInfo.json";
|
||||
import { CoercableComponent, PersistentRef, StyleValue } from "@/features/feature";
|
||||
import { Link } from "@/features/links";
|
||||
import player from "@/game/player";
|
||||
import { ProcessedComputable } from "@/util/computed";
|
||||
import { computeComponent, wrapRef } from "@/util/vue";
|
||||
import { computed, defineComponent, nextTick, PropType, toRefs, unref, watch } from "vue";
|
||||
|
||||
const props = toRefs(
|
||||
defineProps<
|
||||
FeatureComponent<GenericLayer> & {
|
||||
index: number;
|
||||
tab: () => HTMLElement | undefined;
|
||||
}
|
||||
>()
|
||||
);
|
||||
export default defineComponent({
|
||||
components: { Links },
|
||||
props: {
|
||||
index: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
tab: {
|
||||
type: Function as PropType<() => HTMLElement | undefined>,
|
||||
required: true
|
||||
},
|
||||
display: {
|
||||
type: [Object, String] as PropType<ProcessedComputable<CoercableComponent>>,
|
||||
required: true
|
||||
},
|
||||
minimized: {
|
||||
type: Object as PropType<PersistentRef<boolean>>,
|
||||
required: true
|
||||
},
|
||||
minWidth: {
|
||||
type: [Object, Number] as PropType<ProcessedComputable<number>>,
|
||||
required: true
|
||||
},
|
||||
name: {
|
||||
type: [Object, String] as PropType<ProcessedComputable<string>>,
|
||||
required: true
|
||||
},
|
||||
style: Object as PropType<ProcessedComputable<StyleValue>>,
|
||||
classes: Object as PropType<ProcessedComputable<Record<string, boolean>>>,
|
||||
links: [Object, Array] as PropType<ProcessedComputable<Link[]>>,
|
||||
minimizable: [Object, Boolean] as PropType<ProcessedComputable<boolean>>
|
||||
},
|
||||
setup(props) {
|
||||
const { display, index, minimized, minWidth, tab } = toRefs(props);
|
||||
|
||||
const component = computed(() => coerceComponent(unref(props.display)));
|
||||
const component = computeComponent(display);
|
||||
const showGoBack = computed(
|
||||
() => modInfo.allowGoBack && unref(props.index) > 0 && !props.minimized.value
|
||||
() => modInfo.allowGoBack && unref(index) > 0 && !minimized.value
|
||||
);
|
||||
|
||||
function goBack() {
|
||||
player.tabs = player.tabs.slice(0, unref(props.index));
|
||||
}
|
||||
|
||||
nextTick(() => updateTab(props.minimized.value, props.minWidth.value));
|
||||
watch([props.minimized, props.minWidth], ([minimized, minWidth]) => updateTab(minimized, minWidth));
|
||||
nextTick(() => updateTab(minimized.value, unref(minWidth.value)));
|
||||
watch([minimized, wrapRef(minWidth)], ([minimized, minWidth]) =>
|
||||
updateTab(minimized, minWidth)
|
||||
);
|
||||
|
||||
function updateTab(minimized: boolean, minWidth: number) {
|
||||
const tabValue = props.tab.value();
|
||||
const tabValue = tab.value();
|
||||
if (tabValue != undefined) {
|
||||
if (minimized) {
|
||||
tabValue.style.flexGrow = "0";
|
||||
|
@ -62,6 +93,15 @@ function updateTab(minimized: boolean, minWidth: number) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
component,
|
||||
showGoBack,
|
||||
unref,
|
||||
goBack
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
@ -12,13 +12,12 @@
|
|||
import { Link, LinkNode } from "@/features/links";
|
||||
import { computed, toRefs, unref } from "vue";
|
||||
|
||||
const props = toRefs(
|
||||
defineProps<{
|
||||
const _props = defineProps<{
|
||||
link: Link;
|
||||
startNode: LinkNode;
|
||||
endNode: LinkNode;
|
||||
}>()
|
||||
);
|
||||
}>();
|
||||
const props = toRefs(_props);
|
||||
|
||||
const startPosition = computed(() => {
|
||||
const position = { x: props.startNode.value.x || 0, y: props.startNode.value.y || 0 };
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
import { RegisterLinkNodeInjectionKey, UnregisterLinkNodeInjectionKey } from "@/features/links";
|
||||
import { computed, inject, ref, toRefs, unref, watch } from "vue";
|
||||
|
||||
const props = toRefs(defineProps<{ id: string }>());
|
||||
const _props = defineProps<{ id: string }>();
|
||||
const props = toRefs(_props);
|
||||
|
||||
const register = inject(RegisterLinkNodeInjectionKey);
|
||||
const unregister = inject(UnregisterLinkNodeInjectionKey);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<slot />
|
||||
<div ref="resizeListener" class="resize-listener" />
|
||||
<svg v-bind="$attrs" v-if="validLinks">
|
||||
<svg v-if="validLinks" v-bind="$attrs">
|
||||
<LinkVue
|
||||
v-for="(link, index) in validLinks"
|
||||
:key="index"
|
||||
|
@ -19,10 +19,11 @@ import {
|
|||
RegisterLinkNodeInjectionKey,
|
||||
UnregisterLinkNodeInjectionKey
|
||||
} from "@/features/links";
|
||||
import { computed, nextTick, onMounted, provide, ref, toRefs, unref } from "vue";
|
||||
import { computed, nextTick, onMounted, provide, ref, toRefs } from "vue";
|
||||
import LinkVue from "./Link.vue";
|
||||
|
||||
const props = toRefs(defineProps<{ links: Link[] }>());
|
||||
const _props = defineProps<{ links: Link[] }>();
|
||||
const { links } = toRefs(_props);
|
||||
|
||||
const observer = new MutationObserver(updateNodes);
|
||||
const resizeObserver = new ResizeObserver(updateBounds);
|
||||
|
@ -42,7 +43,7 @@ onMounted(() => {
|
|||
});
|
||||
|
||||
const validLinks = computed(() =>
|
||||
unref(props.links.value).filter(link => {
|
||||
links.value.filter(link => {
|
||||
const n = nodes.value;
|
||||
return (
|
||||
n[link.startNode.id]?.x != undefined &&
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
<teleport to="#modal-root">
|
||||
<transition
|
||||
name="modal"
|
||||
@before-enter="setAnimating(true)"
|
||||
@after-leave="setAnimating(false)"
|
||||
@before-enter="isAnimating = true"
|
||||
@after-leave="isAnimating = false"
|
||||
>
|
||||
<div
|
||||
class="modal-mask"
|
||||
|
@ -41,13 +41,14 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import { Link } from "@/features/links";
|
||||
import { computed, ref } from "vue";
|
||||
import { computed, ref, toRefs } from "vue";
|
||||
import Links from "./Links.vue";
|
||||
|
||||
const props = defineProps<{
|
||||
const _props = defineProps<{
|
||||
modelValue: boolean;
|
||||
links?: Link[];
|
||||
}>();
|
||||
const props = toRefs(_props);
|
||||
const emit = defineEmits<{
|
||||
(e: "update:modelValue", value: boolean): void;
|
||||
}>();
|
||||
|
@ -58,9 +59,8 @@ function close() {
|
|||
}
|
||||
|
||||
const isAnimating = ref(false);
|
||||
function setAnimating(value: boolean) {
|
||||
isAnimating.value = value;
|
||||
}
|
||||
|
||||
defineExpose({ isOpen });
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<br />
|
||||
<div>
|
||||
<a :href="discordLink" class="nan-modal-discord-link">
|
||||
<img src="images/discord.png" class="nan-modal-discord" />
|
||||
<span class="material-icons nan-modal-discord">discord</span>
|
||||
{{ discordName }}
|
||||
</a>
|
||||
</div>
|
||||
|
@ -48,14 +48,14 @@ import modInfo from "@/data/modInfo.json";
|
|||
import player from "@/game/player";
|
||||
import state from "@/game/state";
|
||||
import Decimal, { DecimalSource, format } from "@/util/bignum";
|
||||
import { computed, ref, toRef } from "vue";
|
||||
import { ComponentPublicInstance, computed, ref, toRef } from "vue";
|
||||
import Toggle from "../fields/Toggle.vue";
|
||||
import SavesManager from "./SavesManager.vue";
|
||||
|
||||
const { discordName, discordLink } = modInfo;
|
||||
const autosave = toRef(player, "autosave");
|
||||
const hasNaN = toRef(state, "hasNaN");
|
||||
const savesManager = ref<typeof SavesManager | null>(null);
|
||||
const savesManager = ref<ComponentPublicInstance<typeof SavesManager> | null>(null);
|
||||
|
||||
const path = computed(() => state.NaNPath?.join("."));
|
||||
const property = computed(() => state.NaNPath?.slice(-1)[0]);
|
||||
|
|
|
@ -113,9 +113,9 @@ import Options from "./Options.vue";
|
|||
import SavesManager from "./SavesManager.vue";
|
||||
import Tooltip from "./Tooltip.vue";
|
||||
|
||||
const info = ref<typeof Info | null>(null);
|
||||
const savesManager = ref<typeof SavesManager | null>(null);
|
||||
const options = ref<typeof Options | null>(null);
|
||||
const info = ref<ComponentPublicInstance<typeof Info> | null>(null);
|
||||
const savesManager = ref<ComponentPublicInstance<typeof SavesManager> | null>(null);
|
||||
const options = ref<ComponentPublicInstance<typeof Options> | null>(null);
|
||||
// For some reason Info won't accept the changelog unless I do this:
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const changelog = ref<ComponentPublicInstance<any> | null>(null);
|
||||
|
@ -262,8 +262,8 @@ function openDiscord() {
|
|||
text-shadow: 5px 0 10px var(--points), -3px 0 12px var(--points);
|
||||
}
|
||||
|
||||
.nav a,
|
||||
.overlay-nav a {
|
||||
.nav > div > a,
|
||||
.overlay-nav > div > a {
|
||||
color: var(--foreground);
|
||||
text-shadow: none;
|
||||
}
|
||||
|
|
|
@ -11,32 +11,24 @@
|
|||
<Toggle title="Show TPS" v-model="showTPS" />
|
||||
<Toggle title="Hide Maxed Challenges" v-model="hideChallenges" />
|
||||
<Toggle title="Unthrottled" v-model="unthrottled" />
|
||||
<Toggle
|
||||
title="Offline Production<tooltip display='Save-specific'>*</tooltip>"
|
||||
v-model="offlineProd"
|
||||
/>
|
||||
<Toggle
|
||||
title="Autosave<tooltip display='Save-specific'>*</tooltip>"
|
||||
v-model="autosave"
|
||||
/>
|
||||
<Toggle
|
||||
title="Pause game<tooltip display='Save-specific'>*</tooltip>"
|
||||
v-model="isPaused"
|
||||
/>
|
||||
<Toggle :title="offlineProdTitle" v-model="offlineProd" />
|
||||
<Toggle :title="autosaveTitle" v-model="autosave" />
|
||||
<Toggle :title="isPausedTitle" v-model="isPaused" />
|
||||
</template>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
<script setup lang="tsx">
|
||||
import Modal from "@/components/system/Modal.vue";
|
||||
import rawThemes from "@/data/themes";
|
||||
import { MilestoneDisplay } from "@/features/milestone";
|
||||
import player from "@/game/player";
|
||||
import settings from "@/game/settings";
|
||||
import { camelToTitle } from "@/util/common";
|
||||
import { computed, ref, toRefs } from "vue";
|
||||
import { computed, ref, toRef, toRefs } from "vue";
|
||||
import Toggle from "../fields/Toggle.vue";
|
||||
import Select from "../fields/Select.vue";
|
||||
import Tooltip from "./Tooltip.vue";
|
||||
|
||||
const isOpen = ref(false);
|
||||
|
||||
|
@ -58,7 +50,8 @@ const msDisplayOptions = Object.values(MilestoneDisplay).map(option => ({
|
|||
}));
|
||||
|
||||
const { showTPS, hideChallenges, theme, msDisplay, unthrottled } = toRefs(settings);
|
||||
const { autosave, offlineProd, devSpeed } = toRefs(player);
|
||||
const { autosave, offlineProd } = toRefs(player);
|
||||
const devSpeed = toRef(player, "devSpeed");
|
||||
const isPaused = computed({
|
||||
get() {
|
||||
return devSpeed.value === 0;
|
||||
|
@ -67,6 +60,22 @@ const isPaused = computed({
|
|||
devSpeed.value = value ? null : 0;
|
||||
}
|
||||
});
|
||||
|
||||
const offlineProdTitle = (
|
||||
<template>
|
||||
Offline Production<Tooltip display="Save-specific">*</Tooltip>
|
||||
</template>
|
||||
);
|
||||
const autosaveTitle = (
|
||||
<template>
|
||||
Autosave<Tooltip display="Save-specific">*</Tooltip>
|
||||
</template>
|
||||
);
|
||||
const isPausedTitle = (
|
||||
<template>
|
||||
Pause game<Tooltip display="Save-specific">*</Tooltip>
|
||||
</template>
|
||||
);
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
@ -8,12 +8,11 @@
|
|||
import { displayResource, Resource } from "@/features/resource";
|
||||
import { computed, toRefs } from "vue";
|
||||
|
||||
const props = toRefs(
|
||||
defineProps<{
|
||||
const _props = defineProps<{
|
||||
resource: Resource;
|
||||
color: string;
|
||||
}>()
|
||||
);
|
||||
}>();
|
||||
const props = toRefs(_props);
|
||||
|
||||
const amount = computed(() => displayResource(props.resource));
|
||||
</script>
|
||||
|
|
|
@ -57,17 +57,16 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import player from "@/game/player";
|
||||
import { computed, ref, toRefs, unref, watch } from "vue";
|
||||
import { computed, ref, toRefs, watch } from "vue";
|
||||
import DangerButton from "../fields/DangerButton.vue";
|
||||
import FeedbackButton from "../fields/FeedbackButton.vue";
|
||||
import Text from "../fields/Text.vue";
|
||||
import { LoadablePlayerData } from "./SavesManager.vue";
|
||||
|
||||
const props = toRefs(
|
||||
defineProps<{
|
||||
const _props = defineProps<{
|
||||
save: LoadablePlayerData;
|
||||
}>()
|
||||
);
|
||||
}>();
|
||||
const { save } = toRefs(_props);
|
||||
const emit = defineEmits<{
|
||||
(e: "export"): void;
|
||||
(e: "open"): void;
|
||||
|
@ -91,8 +90,10 @@ const newName = ref("");
|
|||
|
||||
watch(isEditing, () => (newName.value = ""));
|
||||
|
||||
const isActive = computed(() => unref(props.save).id === player.id);
|
||||
const currentTime = computed(() => (isActive.value ? player.time : unref(props.save).time));
|
||||
const isActive = computed(() => save.value && save.value.id === player.id);
|
||||
const currentTime = computed(() =>
|
||||
isActive.value ? player.time : (save.value && save.value.time) || 0
|
||||
);
|
||||
|
||||
function changeName() {
|
||||
emit("editName", newName.value);
|
||||
|
|
|
@ -1,21 +1,26 @@
|
|||
<template>
|
||||
<Modal v-model="isOpen">
|
||||
<Modal v-model="isOpen" ref="modal">
|
||||
<template v-slot:header>
|
||||
<h2>Saves Manager</h2>
|
||||
</template>
|
||||
<template v-slot:body>
|
||||
<div v-sortable="{ update, handle: '.handle' }">
|
||||
<Draggable
|
||||
:list="settings.saves"
|
||||
handle=".handle"
|
||||
v-if="unref(modal?.isOpen)"
|
||||
:itemKey="(save: string) => save"
|
||||
>
|
||||
<template #item="{ element }">
|
||||
<Save
|
||||
v-for="(save, index) in saves"
|
||||
:key="index"
|
||||
:save="save!"
|
||||
@open="openSave(save!.id)"
|
||||
@export="exportSave(save!.id)"
|
||||
@editName="name => editSave(save!.id, name)"
|
||||
@duplicate="duplicateSave(save!.id)"
|
||||
@delete="deleteSave(save!.id)"
|
||||
:save="saves[element]"
|
||||
@open="openSave(element)"
|
||||
@export="exportSave(element)"
|
||||
@editName="name => editSave(element, name)"
|
||||
@duplicate="duplicateSave(element)"
|
||||
@delete="deleteSave(element)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</Draggable>
|
||||
</template>
|
||||
<template v-slot:footer>
|
||||
<div class="modal-footer">
|
||||
|
@ -55,16 +60,17 @@
|
|||
import Modal from "@/components/system/Modal.vue";
|
||||
import player, { PlayerData } from "@/game/player";
|
||||
import settings from "@/game/settings";
|
||||
import { getUniqueID, loadSave, save, newSave as createNewSave } from "@/util/save";
|
||||
import { nextTick, ref, watch } from "vue";
|
||||
import { getUniqueID, loadSave, save, newSave } from "@/util/save";
|
||||
import { ComponentPublicInstance, computed, nextTick, reactive, ref, unref, watch } from "vue";
|
||||
import Select from "../fields/Select.vue";
|
||||
import Text from "../fields/Text.vue";
|
||||
import Save from "./Save.vue";
|
||||
import vSortable from "vue-sortable";
|
||||
import Draggable from "vuedraggable";
|
||||
|
||||
export type LoadablePlayerData = Omit<Partial<PlayerData>, "id"> & { id: string; error?: unknown };
|
||||
|
||||
const isOpen = ref(false);
|
||||
const modal = ref<ComponentPublicInstance<typeof Modal> | null>(null);
|
||||
|
||||
defineExpose({
|
||||
open() {
|
||||
|
@ -75,12 +81,6 @@ defineExpose({
|
|||
const importingFailed = ref(false);
|
||||
const saveToImport = ref("");
|
||||
|
||||
watch(isOpen, isOpen => {
|
||||
if (isOpen) {
|
||||
loadSaveData();
|
||||
}
|
||||
});
|
||||
|
||||
watch(saveToImport, save => {
|
||||
if (save) {
|
||||
nextTick(() => {
|
||||
|
@ -96,7 +96,6 @@ watch(saveToImport, save => {
|
|||
id,
|
||||
btoa(unescape(encodeURIComponent(JSON.stringify(playerData))))
|
||||
);
|
||||
saves.value[id] = playerData;
|
||||
saveToImport.value = "";
|
||||
importingFailed.value = false;
|
||||
|
||||
|
@ -122,25 +121,36 @@ let bank = ref(
|
|||
}, [])
|
||||
);
|
||||
|
||||
const saves = ref<Record<string, LoadablePlayerData | undefined>>({});
|
||||
|
||||
function loadSaveData() {
|
||||
saves.value = settings.saves.reduce((acc: Record<string, LoadablePlayerData>, curr: string) => {
|
||||
try {
|
||||
const save = localStorage.getItem(curr);
|
||||
const cachedSaves = reactive<Record<string, LoadablePlayerData>>({});
|
||||
function getCachedSave(id: string) {
|
||||
if (!(id in cachedSaves)) {
|
||||
const save = localStorage.getItem(id);
|
||||
if (save == null) {
|
||||
acc[curr] = { error: `Save with id "${curr}" doesn't exist`, id: curr };
|
||||
cachedSaves[id] = { error: `Save with id "${id}" doesn't exist`, id };
|
||||
} else {
|
||||
acc[curr] = JSON.parse(decodeURIComponent(escape(atob(save))));
|
||||
acc[curr].id = curr;
|
||||
}
|
||||
try {
|
||||
cachedSaves[id] = JSON.parse(decodeURIComponent(escape(atob(save))));
|
||||
cachedSaves[id].id = id;
|
||||
} catch (error) {
|
||||
console.warn(`Can't load save with id "${curr}"`, error);
|
||||
acc[curr] = { error, id: curr };
|
||||
cachedSaves[id] = { error, id };
|
||||
}
|
||||
}
|
||||
}
|
||||
return cachedSaves[id];
|
||||
}
|
||||
// Wipe cache whenever the modal is opened
|
||||
watch(isOpen, isOpen => {
|
||||
if (isOpen) {
|
||||
Object.keys(cachedSaves).forEach(key => delete cachedSaves[key]);
|
||||
}
|
||||
});
|
||||
|
||||
const saves = computed(() =>
|
||||
settings.saves.reduce((acc: Record<string, LoadablePlayerData>, curr: string) => {
|
||||
acc[curr] = getCachedSave(curr);
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
}, {})
|
||||
);
|
||||
|
||||
function exportSave(id: string) {
|
||||
let saveToExport;
|
||||
|
@ -172,13 +182,11 @@ function duplicateSave(id: string) {
|
|||
);
|
||||
|
||||
settings.saves.push(playerData.id);
|
||||
saves.value[playerData.id] = playerData;
|
||||
}
|
||||
|
||||
function deleteSave(id: string) {
|
||||
settings.saves = settings.saves.filter((save: string) => save !== id);
|
||||
localStorage.removeItem(id);
|
||||
saves.value[id] = undefined;
|
||||
}
|
||||
|
||||
function openSave(id: string) {
|
||||
|
@ -189,11 +197,6 @@ function openSave(id: string) {
|
|||
loadSave(saves.value[id]!);
|
||||
}
|
||||
|
||||
function newSave() {
|
||||
const playerData = createNewSave();
|
||||
saves.value[playerData.id] = playerData;
|
||||
}
|
||||
|
||||
function newFromPreset(preset: string) {
|
||||
const playerData = JSON.parse(decodeURIComponent(escape(atob(preset))));
|
||||
playerData.id = getUniqueID();
|
||||
|
@ -203,24 +206,19 @@ function newFromPreset(preset: string) {
|
|||
);
|
||||
|
||||
settings.saves.push(playerData.id);
|
||||
saves.value[playerData.id] = playerData;
|
||||
}
|
||||
|
||||
function editSave(id: string, newName: string) {
|
||||
saves.value[id].name = newName;
|
||||
const currSave = saves.value[id];
|
||||
if (currSave) {
|
||||
currSave.name = newName;
|
||||
if (player.id === id) {
|
||||
player.name = newName;
|
||||
save();
|
||||
} else {
|
||||
localStorage.setItem(
|
||||
id,
|
||||
btoa(unescape(encodeURIComponent(JSON.stringify(saves.value[id]))))
|
||||
);
|
||||
localStorage.setItem(id, btoa(unescape(encodeURIComponent(JSON.stringify(currSave)))));
|
||||
}
|
||||
}
|
||||
|
||||
function update(e: { newIndex: number; oldIndex: number }) {
|
||||
settings.saves.splice(e.newIndex, 0, settings.saves.splice(e.oldIndex, 1)[0]);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
<script setup lang="ts">
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
width: string;
|
||||
height: string;
|
||||
width?: string;
|
||||
height?: string;
|
||||
}>(),
|
||||
{
|
||||
width: "8px",
|
||||
|
|
|
@ -2,42 +2,66 @@
|
|||
<div
|
||||
class="tooltip-container"
|
||||
:class="{ shown: isShown }"
|
||||
@mouseenter="setHover(true)"
|
||||
@mouseleave="setHover(false)"
|
||||
@mouseenter="isHovered = true"
|
||||
@mouseleave="isHovered = false"
|
||||
>
|
||||
<slot />
|
||||
<transition name="fade">
|
||||
<div
|
||||
v-if="isShown"
|
||||
class="tooltip"
|
||||
:class="{ top, left, right, bottom }"
|
||||
:class="{
|
||||
top: unref(top),
|
||||
left: unref(left),
|
||||
right: unref(right),
|
||||
bottom: unref(bottom)
|
||||
}"
|
||||
:style="{
|
||||
'--xoffset': xoffset || '0px',
|
||||
'--yoffset': yoffset || '0px'
|
||||
'--xoffset': unref(xoffset) || '0px',
|
||||
'--yoffset': unref(yoffset) || '0px'
|
||||
}"
|
||||
>
|
||||
<component :is="component" />
|
||||
<component v-if="component" :is="component" />
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { FeatureComponent } from "@/features/feature";
|
||||
import { Tooltip } from "@/features/tooltip";
|
||||
import { coerceComponent } from "@/util/vue";
|
||||
import { computed, ref, toRefs, unref } from "vue";
|
||||
<script lang="ts">
|
||||
import { CoercableComponent } from "@/features/feature";
|
||||
import { ProcessedComputable } from "@/util/computed";
|
||||
import { computeOptionalComponent, unwrapRef } from "@/util/vue";
|
||||
import { computed, defineComponent, PropType, ref, toRefs, unref } from "vue";
|
||||
|
||||
const props = toRefs(defineProps<FeatureComponent<Tooltip>>());
|
||||
export default defineComponent({
|
||||
props: {
|
||||
display: {
|
||||
type: [Object, String] as PropType<ProcessedComputable<CoercableComponent>>,
|
||||
required: true
|
||||
},
|
||||
top: Boolean as PropType<ProcessedComputable<boolean>>,
|
||||
left: Boolean as PropType<ProcessedComputable<boolean>>,
|
||||
right: Boolean as PropType<ProcessedComputable<boolean>>,
|
||||
bottom: Boolean as PropType<ProcessedComputable<boolean>>,
|
||||
xoffset: String as PropType<ProcessedComputable<string>>,
|
||||
yoffset: String as PropType<ProcessedComputable<string>>,
|
||||
force: Boolean as PropType<ProcessedComputable<boolean>>
|
||||
},
|
||||
setup(props) {
|
||||
const { display, force } = toRefs(props);
|
||||
|
||||
const isHovered = ref(false);
|
||||
const isShown = computed(() => unwrapRef(force) || isHovered.value);
|
||||
const component = computeOptionalComponent(display);
|
||||
|
||||
function setHover(hover: boolean) {
|
||||
isHovered.value = hover;
|
||||
return {
|
||||
isHovered,
|
||||
isShown,
|
||||
component,
|
||||
unref
|
||||
};
|
||||
}
|
||||
|
||||
const isShown = computed(() => unref(props.force) || isHovered.value);
|
||||
const component = computed(() => props.display.value && coerceComponent(unref(props.display)));
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
@ -360,16 +360,6 @@ export const g = createTreeNode({
|
|||
});
|
||||
export const h = createTreeNode({
|
||||
id: "h",
|
||||
branches: [
|
||||
"g",
|
||||
() => ({
|
||||
target: "flatBoi",
|
||||
featureType: "bar",
|
||||
endOffset: {
|
||||
x: -50 + 100 * flatBoi.progress.value.toNumber()
|
||||
}
|
||||
})
|
||||
],
|
||||
tooltip() {
|
||||
return `Restore your points to ${format(otherThingy.value)}`;
|
||||
},
|
||||
|
@ -388,7 +378,7 @@ const tree = createTree({
|
|||
[g, spook, h]
|
||||
];
|
||||
},
|
||||
branches: [
|
||||
branches: () => [
|
||||
{
|
||||
startNode: fNode,
|
||||
endNode: treeNode,
|
||||
|
@ -415,7 +405,7 @@ const illuminatiTabs = createTabFamily({
|
|||
display: "first"
|
||||
}),
|
||||
second: createTabButton({
|
||||
tab: fTab,
|
||||
tab: () => fTab,
|
||||
display: "second"
|
||||
})
|
||||
},
|
||||
|
@ -569,7 +559,15 @@ const layer = createLayer({
|
|||
id,
|
||||
color,
|
||||
name,
|
||||
links: tree.links,
|
||||
links() {
|
||||
const links = tree.links.value.slice();
|
||||
links.push({
|
||||
startNode: h,
|
||||
endNode: flatBoi,
|
||||
offsetEnd: { x: -50 + 100 * flatBoi.progress.value.toNumber(), y: 0 }
|
||||
});
|
||||
return links;
|
||||
},
|
||||
points,
|
||||
beep,
|
||||
thingy,
|
||||
|
@ -601,7 +599,7 @@ const layer = createLayer({
|
|||
treeNode,
|
||||
resetButton,
|
||||
minWidth: 800,
|
||||
display: tabs
|
||||
display: render(tabs)
|
||||
});
|
||||
|
||||
export default layer;
|
||||
|
|
|
@ -57,28 +57,28 @@ export const main = createLayer({
|
|||
id: "main",
|
||||
name: "Tree",
|
||||
links: tree.links,
|
||||
display() {
|
||||
return (
|
||||
display: (
|
||||
<template>
|
||||
<div v-if={player.devSpeed === 0}>Game Paused</div>
|
||||
<div v-else-if={player.devSpeed && player.devSpeed !== 1}>
|
||||
{/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */}
|
||||
Dev Speed: {format(player.devSpeed!)}x
|
||||
<div v-show={player.devSpeed === 0}>Game Paused</div>
|
||||
<div v-show={player.devSpeed && player.devSpeed !== 1}>
|
||||
Dev Speed: {format(player.devSpeed || 0)}x
|
||||
</div>
|
||||
<div v-if={player.offlineTime != undefined}>
|
||||
{/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */}
|
||||
Offline Time: {formatTime(player.offlineTime!)}
|
||||
<div v-show={player.offlineTime != undefined}>
|
||||
Offline Time: {formatTime(player.offlineTime || 0)}
|
||||
</div>
|
||||
<div>
|
||||
<span v-if={Decimal.lt(points.value, "1e1000")}>You have </span>
|
||||
<span v-show={Decimal.lt(points.value, "1e1000")}>You have </span>
|
||||
<h2>{format(points.value)}</h2>
|
||||
<span v-if={Decimal.lt(points.value, "1e1e6")}> points</span>
|
||||
<span v-show={Decimal.lt(points.value, "1e1e6")}> points</span>
|
||||
</div>
|
||||
<div v-if={Decimal.gt(pointGain.value, 0)}>
|
||||
<div v-show={Decimal.gt(pointGain.value, 0)}>
|
||||
({oomps.value === "" ? formatSmall(pointGain.value) : oomps.value}/sec)
|
||||
</div>
|
||||
<Spacer />
|
||||
<Modal v-model={showModal}>
|
||||
<Modal
|
||||
modelValue={showModal.value}
|
||||
onUpdate:modelValue={value => (showModal.value = value)}
|
||||
>
|
||||
<svg style="height: 80vmin; width: 80vmin;">
|
||||
<path d="M 32 222 Q 128 222, 128 0 Q 128 222, 224 222 L 224 224 L 32 224" />
|
||||
|
||||
|
@ -89,8 +89,7 @@ export const main = createLayer({
|
|||
</Modal>
|
||||
{render(tree)}
|
||||
</template>
|
||||
);
|
||||
},
|
||||
),
|
||||
points,
|
||||
best,
|
||||
total,
|
||||
|
|
|
@ -117,12 +117,16 @@ globalBus.on("addLayer", layer => {
|
|||
achievement[PersistentState].value = true;
|
||||
achievement.onComplete?.();
|
||||
if (achievement.display) {
|
||||
const display = unref(achievement.display);
|
||||
const Display = coerceComponent(unref(achievement.display));
|
||||
toast.info(
|
||||
<template>
|
||||
<h2>Milestone earned!</h2>
|
||||
<div>{coerceComponent(display)}</div>
|
||||
</template>
|
||||
<div>
|
||||
<h3>Achievement earned!</h3>
|
||||
<div>
|
||||
{/* eslint-disable-next-line @typescript-eslint/ban-ts-comment */}
|
||||
{/* @ts-ignore */}
|
||||
<Display />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,10 +3,9 @@ import { GenericLayer } from "@/game/layers";
|
|||
import Decimal, { DecimalSource } from "@/util/bignum";
|
||||
import { ProcessedComputable } from "@/util/computed";
|
||||
import { isArray } from "@vue/shared";
|
||||
import { ComponentOptions, CSSProperties, DefineComponent, isRef, ref, Ref, UnwrapRef } from "vue";
|
||||
import { ComponentOptions, CSSProperties, DefineComponent, isRef, ref, Ref } from "vue";
|
||||
|
||||
export const PersistentState = Symbol("PersistentState");
|
||||
export const SetupPersistence = Symbol("SetupPersistence");
|
||||
export const DefaultValue = Symbol("DefaultValue");
|
||||
export const Component = Symbol("Component");
|
||||
|
||||
|
@ -27,30 +26,19 @@ export type StyleValue = string | CSSProperties | Array<string | CSSProperties>;
|
|||
export type Persistent<T extends State = State> = {
|
||||
[PersistentState]: Ref<T>;
|
||||
[DefaultValue]: T;
|
||||
[SetupPersistence]: () => Ref<T>;
|
||||
};
|
||||
|
||||
export type PersistentRef<T extends State = State> = Ref<T> & {
|
||||
[DefaultValue]: T;
|
||||
[SetupPersistence]: () => Ref<T>;
|
||||
};
|
||||
export type PersistentRef<T extends State = State> = Ref<T> & Persistent<T>;
|
||||
|
||||
// TODO if importing .vue components in .tsx can become type safe,
|
||||
// this type can probably be safely removed
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export type GenericComponent = DefineComponent<any, any, any>;
|
||||
|
||||
// Example usage: `<Upgrade {...wrapComputable<GenericUpgrade>(upgrade)} />`
|
||||
export function wrapFeature<T>(component: T): UnwrapRef<T> {
|
||||
// TODO is this okay, or do we actually need to unref each property?
|
||||
return component as unknown as UnwrapRef<T>;
|
||||
}
|
||||
|
||||
export type FeatureComponent<T> = Omit<
|
||||
{
|
||||
[K in keyof T]: T[K] extends ProcessedComputable<infer S> ? S : T[K];
|
||||
},
|
||||
typeof Component | typeof DefaultValue | typeof SetupPersistence
|
||||
typeof Component | typeof DefaultValue
|
||||
>;
|
||||
|
||||
export type Replace<T, S> = S & Omit<T, keyof S>;
|
||||
|
@ -75,14 +63,13 @@ export function showIf(condition: boolean, otherwise = Visibility.None): Visibil
|
|||
}
|
||||
|
||||
export function persistent<T extends State>(defaultValue: T | Ref<T>): PersistentRef<T> {
|
||||
const persistent = isRef(defaultValue) ? defaultValue : (ref(defaultValue) as Ref<T>);
|
||||
(persistent as unknown as PersistentRef<T>)[DefaultValue] = isRef(defaultValue)
|
||||
? defaultValue.value
|
||||
: defaultValue;
|
||||
(persistent as unknown as PersistentRef<T>)[SetupPersistence] = function () {
|
||||
return persistent;
|
||||
};
|
||||
return persistent as unknown as PersistentRef<T>;
|
||||
const persistent = (
|
||||
isRef(defaultValue) ? defaultValue : (ref<T>(defaultValue) as unknown)
|
||||
) as PersistentRef<T>;
|
||||
|
||||
persistent[PersistentState] = persistent;
|
||||
persistent[DefaultValue] = isRef(defaultValue) ? defaultValue.value : defaultValue;
|
||||
return persistent as PersistentRef<T>;
|
||||
}
|
||||
|
||||
export function makePersistent<T extends State>(
|
||||
|
@ -92,18 +79,8 @@ export function makePersistent<T extends State>(
|
|||
const persistent = obj as Partial<Persistent<T>>;
|
||||
const state = ref(defaultValue) as Ref<T>;
|
||||
|
||||
Object.defineProperty(persistent, PersistentState, {
|
||||
get: () => {
|
||||
return state.value;
|
||||
},
|
||||
set: (val: T) => {
|
||||
state.value = val;
|
||||
}
|
||||
});
|
||||
persistent[PersistentState] = state;
|
||||
persistent[DefaultValue] = isRef(defaultValue) ? (defaultValue.value as T) : defaultValue;
|
||||
persistent[SetupPersistence] = function () {
|
||||
return state;
|
||||
};
|
||||
}
|
||||
|
||||
export function setDefault<T, K extends keyof T>(
|
||||
|
@ -135,32 +112,39 @@ export function findFeatures(obj: Record<string, unknown>, type: symbol): unknow
|
|||
}
|
||||
|
||||
globalBus.on("addLayer", (layer: GenericLayer, saveData: Record<string, unknown>) => {
|
||||
const handleObject = (
|
||||
obj: Record<string, unknown>,
|
||||
persistentState: Record<string, unknown>
|
||||
): boolean => {
|
||||
const handleObject = (obj: Record<string, unknown>, path: string[] = []): boolean => {
|
||||
let foundPersistent = false;
|
||||
Object.keys(obj).forEach(key => {
|
||||
const value = obj[key];
|
||||
if (value && typeof value === "object") {
|
||||
if (SetupPersistence in value) {
|
||||
if (PersistentState in value) {
|
||||
foundPersistent = true;
|
||||
|
||||
// Construct save path if it doesn't exist
|
||||
const persistentState = path.reduce<Record<string, unknown>>((acc, curr) => {
|
||||
if (!(curr in acc)) {
|
||||
acc[curr] = {};
|
||||
}
|
||||
return acc[curr] as Record<string, unknown>;
|
||||
}, saveData);
|
||||
|
||||
// Cache currently saved value
|
||||
const savedValue = persistentState[key];
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
persistentState[key] = (value as PersistentRef | Persistent)[
|
||||
SetupPersistence
|
||||
]();
|
||||
// Add ref to save data
|
||||
persistentState[key] = (value as Persistent)[PersistentState];
|
||||
// Load previously saved value
|
||||
if (savedValue != null) {
|
||||
(persistentState[key] as Ref<unknown>).value = savedValue;
|
||||
}
|
||||
} else if (!(value instanceof Decimal)) {
|
||||
if (typeof persistentState[key] !== "object") {
|
||||
persistentState[key] = {};
|
||||
}
|
||||
const foundPersistentInChild = handleObject(
|
||||
value as Record<string, unknown>,
|
||||
persistentState[key] as Record<string, unknown>
|
||||
);
|
||||
// Continue traversing
|
||||
const foundPersistentInChild = handleObject(value as Record<string, unknown>, [
|
||||
...path,
|
||||
key
|
||||
]);
|
||||
|
||||
// Show warning for persistent values inside arrays
|
||||
// TODO handle arrays better
|
||||
if (foundPersistentInChild) {
|
||||
if (isArray(value)) {
|
||||
console.warn(
|
||||
|
@ -177,5 +161,5 @@ globalBus.on("addLayer", (layer: GenericLayer, saveData: Record<string, unknown>
|
|||
});
|
||||
return foundPersistent;
|
||||
};
|
||||
handleObject(layer, saveData);
|
||||
handleObject(layer);
|
||||
});
|
||||
|
|
|
@ -21,30 +21,44 @@ import {
|
|||
ProcessedComputable
|
||||
} from "@/util/computed";
|
||||
import { createProxy, Proxied } from "@/util/proxies";
|
||||
import { unref } from "vue";
|
||||
import { computed, unref } from "vue";
|
||||
|
||||
export const GridType = Symbol("Grid");
|
||||
|
||||
export type CellComputable<T> = Computable<T> | ((id: string | number, state: State) => T);
|
||||
|
||||
function createGridProxy(object: Record<string, unknown>): Record<string | number, GridCell> {
|
||||
if (object.isProxy) {
|
||||
console.warn(
|
||||
"Creating a proxy out of a proxy! This may cause unintentional function calls and stack overflows."
|
||||
);
|
||||
}
|
||||
return new Proxy(object, gridHandler) as Proxied<Record<string | number, GridCell>>;
|
||||
function createGridProxy(grid: GenericGrid): Record<string | number, GridCell> {
|
||||
return new Proxy({}, getGridHandler(grid)) as Proxied<Record<string | number, GridCell>>;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const gridHandler: ProxyHandler<Record<PropertyKey, any>> = {
|
||||
function getGridHandler(
|
||||
grid: GenericGrid
|
||||
): ProxyHandler<Record<string | number, Proxied<GridCell>>> {
|
||||
const keys = computed(() => {
|
||||
const keys = [];
|
||||
for (let row = 1; row <= grid.rows; row++) {
|
||||
for (let col = 1; col <= grid.cols; col++) {
|
||||
keys.push((row * 100 + col).toString());
|
||||
}
|
||||
}
|
||||
return keys;
|
||||
});
|
||||
return {
|
||||
get(target, key) {
|
||||
if (key === "isProxy") {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (typeof key === "symbol") {
|
||||
return (grid as never)[key];
|
||||
}
|
||||
|
||||
if (target[key] == null) {
|
||||
target[key] = new Proxy(target, getCellHandler(key.toString()));
|
||||
target[key] = new Proxy(
|
||||
grid,
|
||||
getCellHandler(key.toString())
|
||||
) as unknown as Proxied<GridCell>;
|
||||
}
|
||||
|
||||
return target[key];
|
||||
|
@ -52,42 +66,54 @@ const gridHandler: ProxyHandler<Record<PropertyKey, any>> = {
|
|||
set(target, key, value) {
|
||||
console.warn("Cannot set grid cells", target, key, value);
|
||||
return false;
|
||||
},
|
||||
ownKeys() {
|
||||
return keys.value;
|
||||
},
|
||||
has(target, key) {
|
||||
return keys.value.includes(key.toString());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function getCellHandler(id: string) {
|
||||
function getCellHandler(id: string): ProxyHandler<GenericGrid> {
|
||||
return {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
get(target: Record<string, any>, key: string, receiver: typeof Proxy): any {
|
||||
get(target, key, receiver): any {
|
||||
if (key === "isProxy") {
|
||||
return true;
|
||||
}
|
||||
|
||||
let prop = target[key];
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
let prop = (target as any)[key];
|
||||
|
||||
if (isFunction(prop)) {
|
||||
return () => prop.call(receiver, id, target.getState(id));
|
||||
}
|
||||
if (prop != undefined || key.slice == undefined) {
|
||||
if (prop != undefined || typeof key === "symbol") {
|
||||
return prop;
|
||||
}
|
||||
|
||||
key = key.slice(0, 1).toUpperCase() + key.slice(1);
|
||||
prop = target[`get${key}`];
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
prop = (target as any)[`get${key}`];
|
||||
if (isFunction(prop)) {
|
||||
return prop.call(receiver, id, target.getState(id));
|
||||
} else if (prop != undefined) {
|
||||
return unref(prop);
|
||||
}
|
||||
|
||||
prop = target[`on${key}`];
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
prop = (target as any)[`on${key}`];
|
||||
if (isFunction(prop)) {
|
||||
return () => prop.call(receiver, id, target.getState(id));
|
||||
} else if (prop != undefined) {
|
||||
return prop;
|
||||
}
|
||||
|
||||
return target[key];
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
return (target as any)[key];
|
||||
},
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
set(target: Record<string, any>, key: string, value: any, receiver: typeof Proxy): boolean {
|
||||
|
@ -138,7 +164,7 @@ export interface GridOptions {
|
|||
export interface BaseGrid extends Persistent<Record<string | number, State>> {
|
||||
id: string;
|
||||
getID: (id: string | number, state: State) => string;
|
||||
getState: (id: string | number, state: State) => State;
|
||||
getState: (id: string | number) => State;
|
||||
setState: (id: string | number, state: State) => void;
|
||||
cells: Record<string | number, GridCell>;
|
||||
type: typeof GridType;
|
||||
|
@ -176,7 +202,6 @@ export function createGrid<T extends GridOptions>(options: T & ThisType<Grid<T>>
|
|||
grid.id = getUniqueID("grid-");
|
||||
grid[Component] = GridComponent;
|
||||
|
||||
grid.cells = createGridProxy(grid as unknown as Record<string, unknown>);
|
||||
grid.getID = function (this: GenericGrid, cell: string | number) {
|
||||
return grid.id + "-" + cell;
|
||||
};
|
||||
|
@ -205,5 +230,6 @@ export function createGrid<T extends GridOptions>(options: T & ThisType<Grid<T>>
|
|||
processComputable(grid as T, "getDisplay");
|
||||
|
||||
const proxy = createProxy(grid as unknown as Grid<T>);
|
||||
(proxy as GenericGrid).cells = createGridProxy(proxy as GenericGrid);
|
||||
return proxy;
|
||||
}
|
||||
|
|
|
@ -146,13 +146,16 @@ globalBus.on("addLayer", layer => {
|
|||
milestone.onComplete?.();
|
||||
if (milestone.display) {
|
||||
const display = unref(milestone.display);
|
||||
toast.info(
|
||||
<template>
|
||||
<h2>Milestone earned!</h2>
|
||||
<div>
|
||||
{coerceComponent(
|
||||
const Display = coerceComponent(
|
||||
isCoercableComponent(display) ? display : display.requirement
|
||||
)}
|
||||
);
|
||||
toast(
|
||||
<template>
|
||||
<h3>Milestone earned!</h3>
|
||||
<div>
|
||||
{/* eslint-disable-next-line @typescript-eslint/ban-ts-comment */}
|
||||
{/* @ts-ignore */}
|
||||
<Display />
|
||||
</div>
|
||||
</template>
|
||||
);
|
||||
|
|
|
@ -112,3 +112,14 @@ export function displayResource(resource: Resource, overrideAmount?: DecimalSour
|
|||
}
|
||||
return format(amount, resource.precision, resource.small);
|
||||
}
|
||||
|
||||
// unref may unwrap a resource too far, so this function properly unwraps it
|
||||
export function unwrapResource<T extends State>(
|
||||
resource: Resource<T> | Ref<Resource<T>>
|
||||
): Resource<T> {
|
||||
console.log(resource);
|
||||
if ("displayName" in resource) {
|
||||
return resource;
|
||||
}
|
||||
return resource.value;
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import {
|
|||
getUniqueID,
|
||||
makePersistent,
|
||||
Persistent,
|
||||
PersistentRef,
|
||||
PersistentState,
|
||||
Replace,
|
||||
setDefault,
|
||||
|
@ -85,6 +86,7 @@ export interface TabFamilyOptions {
|
|||
interface BaseTabFamily extends Persistent<string> {
|
||||
id: string;
|
||||
activeTab: Ref<GenericTab | CoercableComponent | null>;
|
||||
selected: Ref<string>;
|
||||
type: typeof TabFamilyType;
|
||||
[Component]: typeof TabFamilyComponent;
|
||||
}
|
||||
|
@ -112,6 +114,7 @@ export function createTabFamily<T extends TabFamilyOptions>(
|
|||
tabFamily[Component] = TabFamilyComponent;
|
||||
|
||||
makePersistent<string>(tabFamily, Object.keys(options.tabs)[0]);
|
||||
tabFamily.selected = tabFamily[PersistentState];
|
||||
tabFamily.activeTab = computed(() => {
|
||||
const tabs = unref(proxy.tabs);
|
||||
if (
|
||||
|
|
|
@ -162,7 +162,7 @@ export function createTree<T extends TreeOptions>(options: T & ThisType<Tree<T>>
|
|||
proxy.isResetting.value = false;
|
||||
proxy.resettingNode.value = null;
|
||||
};
|
||||
tree.links = computed(() => proxy.branches as Link[]);
|
||||
tree.links = computed(() => (proxy.branches == null ? [] : unref(proxy.branches)));
|
||||
|
||||
processComputable(tree as T, "visibility");
|
||||
setDefault(tree, "visibility", Visibility.Visible);
|
||||
|
|
|
@ -47,17 +47,17 @@ function update() {
|
|||
|
||||
// Add offline time if any
|
||||
if (player.offlineTime != undefined) {
|
||||
if (player.offlineTime.gt(modInfo.offlineLimit * 3600)) {
|
||||
if (Decimal.gt(player.offlineTime, modInfo.offlineLimit * 3600)) {
|
||||
player.offlineTime = new Decimal(modInfo.offlineLimit * 3600);
|
||||
}
|
||||
if (player.offlineTime.gt(0) && player.devSpeed !== 0) {
|
||||
const offlineDiff = Decimal.max(player.offlineTime.div(10), diff);
|
||||
player.offlineTime = player.offlineTime.sub(offlineDiff);
|
||||
if (Decimal.gt(player.offlineTime, 0) && player.devSpeed !== 0) {
|
||||
const offlineDiff = Decimal.div(player.offlineTime, 10).max(diff);
|
||||
player.offlineTime = Decimal.sub(player.offlineTime, offlineDiff);
|
||||
diff = diff.add(offlineDiff);
|
||||
} else if (player.devSpeed === 0) {
|
||||
player.offlineTime = player.offlineTime.add(diff);
|
||||
player.offlineTime = Decimal.add(player.offlineTime, diff);
|
||||
}
|
||||
if (!player.offlineProd || player.offlineTime.lt(0)) {
|
||||
if (!player.offlineProd || Decimal.lt(player.offlineTime, 0)) {
|
||||
player.offlineTime = null;
|
||||
}
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ function update() {
|
|||
if (diff.eq(0)) {
|
||||
return;
|
||||
}
|
||||
player.timePlayed = player.timePlayed.add(diff);
|
||||
player.timePlayed = Decimal.add(player.timePlayed, diff);
|
||||
globalBus.emit("update", diff, trueDiff);
|
||||
|
||||
if (settings.unthrottled) {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import Decimal, { DecimalSource } from "@/util/bignum";
|
||||
import { isPlainObject } from "@/util/common";
|
||||
import { ProxiedWithState, ProxyPath, ProxyState } from "@/util/proxies";
|
||||
import { reactive, unref } from "vue";
|
||||
import { shallowReactive, unref } from "vue";
|
||||
import transientState from "./state";
|
||||
|
||||
export interface PlayerData {
|
||||
|
@ -12,8 +12,8 @@ export interface PlayerData {
|
|||
time: number;
|
||||
autosave: boolean;
|
||||
offlineProd: boolean;
|
||||
offlineTime: Decimal | null;
|
||||
timePlayed: Decimal;
|
||||
offlineTime: DecimalSource | null;
|
||||
timePlayed: DecimalSource;
|
||||
keepGoing: boolean;
|
||||
minimized: Record<string, boolean>;
|
||||
modID: string;
|
||||
|
@ -23,7 +23,7 @@ export interface PlayerData {
|
|||
|
||||
export type Player = ProxiedWithState<PlayerData>;
|
||||
|
||||
const state = reactive<PlayerData>({
|
||||
const state = shallowReactive<PlayerData>({
|
||||
id: "",
|
||||
devSpeed: null,
|
||||
name: "",
|
||||
|
@ -51,24 +51,17 @@ const playerHandler: ProxyHandler<Record<PropertyKey, any>> = {
|
|||
if (key === ProxyState || key === ProxyPath) {
|
||||
return target[key];
|
||||
}
|
||||
if (target[ProxyState][key] == undefined) {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
isPlainObject(target[ProxyState][key]) &&
|
||||
!(target[ProxyState][key] instanceof Decimal)
|
||||
) {
|
||||
if (target[ProxyState][key] !== target[key]?.[ProxyState]) {
|
||||
|
||||
const value = target[ProxyState][key];
|
||||
if (isPlainObject(value) && !(value instanceof Decimal)) {
|
||||
if (value !== target[key]?.[ProxyState]) {
|
||||
const path = [...target[ProxyPath], key];
|
||||
target[key] = new Proxy(
|
||||
{ [ProxyState]: target[ProxyState][key], [ProxyPath]: path },
|
||||
playerHandler
|
||||
);
|
||||
target[key] = new Proxy({ [ProxyState]: value, [ProxyPath]: path }, playerHandler);
|
||||
}
|
||||
return target[key];
|
||||
}
|
||||
|
||||
return target[ProxyState][key];
|
||||
return value;
|
||||
},
|
||||
set(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
|
|
@ -2,7 +2,7 @@ import modInfo from "@/data/modInfo.json";
|
|||
import { Themes } from "@/data/themes";
|
||||
import { globalBus } from "@/game/events";
|
||||
import { hardReset } from "@/util/save";
|
||||
import { reactive, watch } from "vue";
|
||||
import { shallowReactive, watch } from "vue";
|
||||
|
||||
export interface Settings {
|
||||
active: string;
|
||||
|
@ -12,7 +12,7 @@ export interface Settings {
|
|||
unthrottled: boolean;
|
||||
}
|
||||
|
||||
const state = reactive<Partial<Settings>>({
|
||||
const state = shallowReactive<Partial<Settings>>({
|
||||
active: "",
|
||||
saves: [],
|
||||
showTPS: true,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { reactive } from "vue";
|
||||
import { shallowReactive } from "vue";
|
||||
|
||||
export interface Transient {
|
||||
lastTenTicks: number[];
|
||||
|
@ -7,7 +7,7 @@ export interface Transient {
|
|||
NaNReceiver?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export default window.state = reactive<Transient>({
|
||||
export default window.state = shallowReactive<Transient>({
|
||||
lastTenTicks: [],
|
||||
hasNaN: false,
|
||||
NaNPath: []
|
||||
|
|
|
@ -20,6 +20,8 @@ type ComputableKeysOf<T> = Pick<
|
|||
}[keyof T]
|
||||
>;
|
||||
|
||||
// TODO fix the typing of this function, such that casting isn't necessary and can be used to
|
||||
// detect if a createX function is validly written
|
||||
export function processComputable<T, S extends keyof ComputableKeysOf<T>>(
|
||||
obj: T,
|
||||
key: S
|
||||
|
|
|
@ -28,7 +28,8 @@ export function createProxy<T extends Record<string, unknown>>(object: T): T {
|
|||
"Creating a proxy out of a proxy! This may cause unintentional function calls and stack overflows."
|
||||
);
|
||||
}
|
||||
return new Proxy(object, layerHandler) as T;
|
||||
//return new Proxy(object, layerHandler) as T;
|
||||
return object;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
@ -39,6 +40,7 @@ const layerHandler: ProxyHandler<Record<PropertyKey, any>> = {
|
|||
}
|
||||
|
||||
if (
|
||||
target[key] == null ||
|
||||
isRef(target[key]) ||
|
||||
target[key].isProxy ||
|
||||
target[key] instanceof Decimal ||
|
||||
|
|
|
@ -3,8 +3,8 @@ import player, { Player, PlayerData, stringifySave } from "@/game/player";
|
|||
import settings, { loadSettings } from "@/game/settings";
|
||||
import Decimal from "./bignum";
|
||||
|
||||
export function setupInitialStore(player: Partial<PlayerData> = {}): asserts player is Player {
|
||||
Object.assign(
|
||||
export function setupInitialStore(player: Partial<PlayerData> = {}): Player {
|
||||
return Object.assign(
|
||||
{
|
||||
id: `${modInfo.id}-0`,
|
||||
name: "Default Save",
|
||||
|
@ -20,7 +20,7 @@ export function setupInitialStore(player: Partial<PlayerData> = {}): asserts pla
|
|||
layers: {}
|
||||
},
|
||||
player
|
||||
);
|
||||
) as Player;
|
||||
}
|
||||
|
||||
export function save(): string {
|
||||
|
@ -54,8 +54,8 @@ export async function load(): Promise<void> {
|
|||
|
||||
export function newSave(): PlayerData {
|
||||
const id = getUniqueID();
|
||||
const player = { id };
|
||||
setupInitialStore(player);
|
||||
const player = setupInitialStore({ id });
|
||||
console.log(player);
|
||||
localStorage.setItem(id, btoa(unescape(encodeURIComponent(stringifySave(player)))));
|
||||
|
||||
settings.saves.push(id);
|
||||
|
@ -81,13 +81,15 @@ export async function loadSave(playerObj: Partial<PlayerData>): Promise<void> {
|
|||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
removeLayer(layers[layer]!);
|
||||
}
|
||||
console.log(getInitialLayers(playerObj))
|
||||
getInitialLayers(playerObj).forEach(layer => addLayer(layer, playerObj));
|
||||
|
||||
setupInitialStore(playerObj);
|
||||
playerObj = setupInitialStore(playerObj);
|
||||
if (playerObj.offlineProd && playerObj.time) {
|
||||
if (playerObj.offlineTime == undefined) playerObj.offlineTime = new Decimal(0);
|
||||
playerObj.offlineTime = playerObj.offlineTime.add((Date.now() - playerObj.time) / 1000);
|
||||
playerObj.offlineTime = Decimal.add(
|
||||
playerObj.offlineTime,
|
||||
(Date.now() - playerObj.time) / 1000
|
||||
);
|
||||
}
|
||||
playerObj.time = Date.now();
|
||||
if (playerObj.modVersion !== modInfo.versionNumber) {
|
||||
|
@ -95,7 +97,7 @@ export async function loadSave(playerObj: Partial<PlayerData>): Promise<void> {
|
|||
}
|
||||
|
||||
Object.assign(player, playerObj);
|
||||
settings.active = playerObj.id;
|
||||
settings.active = player.id;
|
||||
}
|
||||
|
||||
setInterval(() => {
|
||||
|
|
|
@ -5,7 +5,21 @@ import {
|
|||
Component as ComponentKey,
|
||||
GenericComponent
|
||||
} from "@/features/feature";
|
||||
import { Component, DefineComponent, defineComponent, h, reactive, Ref } from "vue";
|
||||
import { isArray } from "@vue/shared";
|
||||
import {
|
||||
Component,
|
||||
computed,
|
||||
ComputedRef,
|
||||
DefineComponent,
|
||||
defineComponent,
|
||||
h,
|
||||
PropType,
|
||||
ref,
|
||||
Ref,
|
||||
unref,
|
||||
WritableComputedRef
|
||||
} from "vue";
|
||||
import { ProcessedComputable } from "./computed";
|
||||
|
||||
export function coerceComponent(component: CoercableComponent, defaultWrapper = "span"): Component {
|
||||
if (typeof component === "string") {
|
||||
|
@ -78,24 +92,17 @@ export function setupHoldToClick(
|
|||
stop: VoidFunction;
|
||||
handleHolding: VoidFunction;
|
||||
} {
|
||||
const state = reactive<{
|
||||
interval: null | number;
|
||||
time: number;
|
||||
}>({
|
||||
interval: null,
|
||||
time: 0
|
||||
});
|
||||
const interval = ref<null | number>(null);
|
||||
|
||||
function start() {
|
||||
if (!state.interval) {
|
||||
state.interval = setInterval(handleHolding, 250);
|
||||
if (!interval.value) {
|
||||
interval.value = setInterval(handleHolding, 250);
|
||||
}
|
||||
}
|
||||
function stop() {
|
||||
if (state.interval) {
|
||||
clearInterval(state.interval);
|
||||
state.interval = null;
|
||||
state.time = 0;
|
||||
if (interval.value) {
|
||||
clearInterval(interval.value);
|
||||
interval.value = null;
|
||||
}
|
||||
}
|
||||
function handleHolding() {
|
||||
|
@ -108,3 +115,47 @@ export function setupHoldToClick(
|
|||
|
||||
return { start, stop, handleHolding };
|
||||
}
|
||||
|
||||
export function computeComponent(
|
||||
component: Ref<ProcessedComputable<CoercableComponent>>
|
||||
): ComputedRef<Component> {
|
||||
return computed(() => {
|
||||
return coerceComponent(unref(unref<ProcessedComputable<CoercableComponent>>(component)));
|
||||
});
|
||||
}
|
||||
export function computeOptionalComponent(
|
||||
component: Ref<ProcessedComputable<CoercableComponent | undefined> | undefined>
|
||||
): ComputedRef<Component | undefined> {
|
||||
return computed(() => {
|
||||
let currComponent = unref<ProcessedComputable<CoercableComponent | undefined> | undefined>(
|
||||
component
|
||||
);
|
||||
if (currComponent == null) return;
|
||||
currComponent = unref(currComponent);
|
||||
return currComponent == null ? undefined : coerceComponent(currComponent);
|
||||
});
|
||||
}
|
||||
|
||||
export function wrapRef<T>(ref: Ref<ProcessedComputable<T>>): ComputedRef<T> {
|
||||
return computed(() => unwrapRef(ref));
|
||||
}
|
||||
|
||||
export function unwrapRef<T>(ref: Ref<ProcessedComputable<T>>): T {
|
||||
return unref(unref<ProcessedComputable<T>>(ref));
|
||||
}
|
||||
|
||||
type PropTypes =
|
||||
| typeof Boolean
|
||||
| typeof String
|
||||
| typeof Number
|
||||
| typeof Function
|
||||
| typeof Object
|
||||
| typeof Array;
|
||||
// TODO Unfortunately, the typescript engine gives up on typing completely when you use this method,
|
||||
// Even though it has the same typing as when doing it manually
|
||||
export function processedPropType<T>(...types: PropTypes[]): PropType<ProcessedComputable<T>> {
|
||||
if (!types.includes(Object)) {
|
||||
types.push(Object);
|
||||
}
|
||||
return types as PropType<ProcessedComputable<T>>;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue