WIP: Feature Rewrite #87

Draft
thepaperpilot wants to merge 35 commits from feat/ure-rewrite into main
19 changed files with 1058 additions and 1046 deletions
Showing only changes of commit 1e5411d279 - Show all commits

View file

@ -29,14 +29,23 @@ import player from "game/player";
import { computed, toRef, unref } from "vue";
import Layer from "./Layer.vue";
import Nav from "./Nav.vue";
import { deepUnref } from "util/vue";
const tabs = toRef(player, "tabs");
const layerKeys = computed(() => Object.keys(layers));
const useHeader = projInfo.useHeader;
function gatherLayerProps(layer: GenericLayer) {
const { display, minimized, name, color, minimizable, nodes, minimizedDisplay } = layer;
return { display, minimized, name, color, minimizable, nodes, minimizedDisplay };
const { display, name, color, minimizable, minimizedDisplay } = deepUnref(layer);
return {
display,
name,
color,
minimizable,
minimizedDisplay,
minimized: layer.minimized,
nodes: layer.nodes
};
}
</script>

View file

@ -23,80 +23,48 @@
</div>
</template>
<script lang="ts">
<script setup lang="ts">
import projInfo from "data/projInfo.json";
import type { CoercableComponent } from "features/feature";
import type { FeatureNode } from "game/layers";
import player from "game/player";
import { computeComponent, computeOptionalComponent, processedPropType, unwrapRef } from "util/vue";
import { PropType, Ref, computed, defineComponent, onErrorCaptured, ref, toRefs, unref } from "vue";
import { computeComponent, computeOptionalComponent } from "util/vue";
import { Ref, computed, onErrorCaptured, ref, toRef, unref } from "vue";
import Context from "./Context.vue";
import ErrorVue from "./Error.vue";
export default defineComponent({
components: { Context, ErrorVue },
props: {
index: {
type: Number,
required: true
},
display: {
type: processedPropType<CoercableComponent>(Object, String, Function),
required: true
},
minimizedDisplay: processedPropType<CoercableComponent>(Object, String, Function),
minimized: {
type: Object as PropType<Ref<boolean>>,
required: true
},
name: {
type: processedPropType<string>(String),
required: true
},
color: processedPropType<string>(String),
minimizable: processedPropType<boolean>(Boolean),
nodes: {
type: Object as PropType<Ref<Record<string, FeatureNode | undefined>>>,
required: true
}
},
emits: ["setMinimized"],
setup(props) {
const { display, index, minimized, minimizedDisplay } = toRefs(props);
const props = defineProps<{
index: number;
display: CoercableComponent;
minimizedDisplay?: CoercableComponent;
minimized: Ref<boolean>;
name: string;
color?: string;
minimizable?: boolean;
nodes: Ref<Record<string, FeatureNode | undefined>>;
}>();
const component = computeComponent(display);
const minimizedComponent = computeOptionalComponent(minimizedDisplay);
const showGoBack = computed(
() => projInfo.allowGoBack && index.value > 0 && !unwrapRef(minimized)
);
const component = computeComponent(toRef(props, "display"));
const minimizedComponent = computeOptionalComponent(toRef(props, "minimizedDisplay"));
const showGoBack = computed(
() => projInfo.allowGoBack && props.index > 0 && !unref(props.minimized)
);
function goBack() {
player.tabs.splice(unref(props.index), Infinity);
}
function goBack() {
player.tabs.splice(unref(props.index), Infinity);
}
function updateNodes(nodes: Record<string, FeatureNode | undefined>) {
props.nodes.value = nodes;
}
function updateNodes(nodes: Record<string, FeatureNode | undefined>) {
props.nodes.value = nodes;
}
const errors = ref<Error[]>([]);
onErrorCaptured((err, instance, info) => {
console.warn(`Error caught in "${props.name}" layer`, err, instance, info);
errors.value.push(
err instanceof Error ? (err as Error) : new Error(JSON.stringify(err))
);
return false;
});
return {
component,
minimizedComponent,
showGoBack,
updateNodes,
unref,
goBack,
errors
};
}
const errors = ref<Error[]>([]);
onErrorCaptured((err, instance, info) => {
console.warn(`Error caught in "${props.name}" layer`, err, instance, info);
errors.value.push(
err instanceof Error ? (err as Error) : new Error(JSON.stringify(err))
);
return false;
});
</script>

View file

@ -16,8 +16,8 @@
<script setup lang="ts">
import "components/common/fields.css";
import type { CoercableComponent } from "features/feature";
import { computeOptionalComponent, unwrapRef } from "util/vue";
import { ref, toRef, watch } from "vue";
import { computeOptionalComponent } from "util/vue";
import { ref, toRef, unref, watch } from "vue";
import VueNextSelect from "vue-next-select";
import "vue-next-select/dist/index.css";
@ -40,7 +40,7 @@ const value = ref<SelectOption | null>(
props.options.find(option => option.value === props.modelValue) ?? null
);
watch(toRef(props, "modelValue"), modelValue => {
if (unwrapRef(value) !== modelValue) {
if (unref(value) !== modelValue) {
value.value = props.options.find(option => option.value === modelValue) ?? null;
}
});

View file

@ -23,89 +23,62 @@
</div>
</template>
<script lang="tsx">
<script setup lang="tsx">
import "components/common/features.css";
import MarkNode from "components/MarkNode.vue";
import Node from "components/Node.vue";
import { isHidden, isVisible, jsx, Visibility } from "features/feature";
import { displayRequirements, Requirements } from "game/requirements";
import { coerceComponent, isCoercableComponent, processedPropType, unwrapRef } from "util/vue";
import { Component, defineComponent, shallowRef, StyleValue, toRefs, unref, UnwrapRef, watchEffect } from "vue";
import { coerceComponent, isCoercableComponent } from "util/vue";
import { Component, shallowRef, StyleValue, unref, UnwrapRef, watchEffect } from "vue";
import { GenericAchievement } from "./achievement";
export default defineComponent({
props: {
visibility: {
type: processedPropType<Visibility | boolean>(Number, Boolean),
required: true
},
display: processedPropType<UnwrapRef<GenericAchievement["display"]>>(Object, String, Function),
earned: {
type: processedPropType<boolean>(Boolean),
required: true
},
requirements: processedPropType<Requirements>(Object, Array),
image: processedPropType<string>(String),
style: processedPropType<StyleValue>(String, Object, Array),
classes: processedPropType<Record<string, boolean>>(Object),
mark: processedPropType<boolean | string>(Boolean, String),
small: processedPropType<boolean>(Boolean),
id: {
type: String,
required: true
}
},
components: {
Node,
MarkNode
},
setup(props) {
const { display, requirements, earned } = toRefs(props);
const props = defineProps<{
visibility: Visibility | boolean;
display?: UnwrapRef<GenericAchievement["display"]>;
earned: boolean;
requirements?: Requirements;
image?: string;
style?: StyleValue;
classes?: Record<string, boolean>;
mark?: boolean | string;
small?: boolean;
id: string;
}>();
const comp = shallowRef<Component | string>("");
const comp = shallowRef<Component | string>("");
watchEffect(() => {
const currDisplay = unwrapRef(display);
if (currDisplay == null) {
comp.value = "";
return;
}
if (isCoercableComponent(currDisplay)) {
comp.value = coerceComponent(currDisplay);
return;
}
const Requirement = coerceComponent(currDisplay.requirement ? currDisplay.requirement : jsx(() => displayRequirements(unwrapRef(requirements) ?? [])), "h3");
const EffectDisplay = coerceComponent(currDisplay.effectDisplay || "", "b");
const OptionsDisplay = unwrapRef(earned) ?
coerceComponent(currDisplay.optionsDisplay || "", "span") :
"";
comp.value = coerceComponent(
jsx(() => (
<span>
<Requirement />
{currDisplay.effectDisplay != null ? (
<div>
<EffectDisplay />
</div>
) : null}
{currDisplay.optionsDisplay != null ? (
<div class="equal-spaced">
<OptionsDisplay />
</div>
) : null}
</span>
))
);
});
return {
comp,
unref,
Visibility,
isVisible,
isHidden
};
watchEffect(() => {
const currDisplay = props.display;
if (currDisplay == null) {
comp.value = "";
return;
}
if (isCoercableComponent(currDisplay)) {
comp.value = coerceComponent(currDisplay);
return;
}
const Requirement = coerceComponent(currDisplay.requirement ? currDisplay.requirement :
jsx(() => displayRequirements(props.requirements ?? [])), "h3");
const EffectDisplay = coerceComponent(currDisplay.effectDisplay || "", "b");
const OptionsDisplay = props.earned ?
coerceComponent(currDisplay.optionsDisplay || "", "span") :
"";
comp.value = coerceComponent(
jsx(() => (
<span>
<Requirement />
{currDisplay.effectDisplay != null ? (
<div>
<EffectDisplay />
</div>
) : null}
{currDisplay.optionsDisplay != null ? (
<div class="equal-spaced">
<OptionsDisplay />
</div>
) : null}
</span>
))
);
});
</script>

View file

@ -41,107 +41,68 @@
</div>
</template>
<script lang="ts">
import MarkNode from "components/MarkNode.vue";
import Node from "components/Node.vue";
<script setup lang="ts">
import { CoercableComponent, isHidden, isVisible, Visibility } from "features/feature";
import type { DecimalSource } from "util/bignum";
import Decimal from "util/bignum";
import { Direction } from "util/common";
import { computeOptionalComponent, processedPropType, unwrapRef } from "util/vue";
import { computeOptionalComponent } from "util/vue";
import type { CSSProperties, StyleValue } from "vue";
import { computed, defineComponent, toRefs, unref } from "vue";
import { computed, toRef, unref } from "vue";
export default defineComponent({
props: {
progress: {
type: processedPropType<DecimalSource>(String, Object, Number),
required: true
},
width: {
type: processedPropType<number>(Number),
required: true
},
height: {
type: processedPropType<number>(Number),
required: true
},
direction: {
type: processedPropType<Direction>(String),
required: true
},
display: processedPropType<CoercableComponent>(Object, String, Function),
visibility: {
type: processedPropType<Visibility | boolean>(Number, Boolean),
required: true
},
style: processedPropType<StyleValue>(Object, String, Array),
classes: processedPropType<Record<string, boolean>>(Object),
borderStyle: processedPropType<StyleValue>(Object, String, Array),
textStyle: processedPropType<StyleValue>(Object, String, Array),
baseStyle: processedPropType<StyleValue>(Object, String, Array),
fillStyle: processedPropType<StyleValue>(Object, String, Array),
mark: processedPropType<boolean | string>(Boolean, String),
id: {
type: String,
required: true
}
},
components: {
MarkNode,
Node
},
setup(props) {
const { progress, width, height, direction, display } = toRefs(props);
const props = defineProps<{
progress: DecimalSource;
width: number;
height: number;
direction: Direction;
display?: CoercableComponent;
visibility: Visibility | boolean;
style?: StyleValue;
classes?: Record<string, boolean>;
borderStyle?: StyleValue;
textStyle?: StyleValue;
baseStyle?: StyleValue;
fillStyle?: StyleValue;
mark?: boolean | string;
id: string;
}>();
const normalizedProgress = computed(() => {
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: unwrapRef(width) + 0.5 + "px",
height: unwrapRef(height) + 0.5 + "px"
};
switch (unref(direction)) {
case Direction.Up:
barStyle.clipPath = `inset(${normalizedProgress.value}% 0% 0% 0%)`;
barStyle.width = unwrapRef(width) + 1 + "px";
break;
case Direction.Down:
barStyle.clipPath = `inset(0% 0% ${normalizedProgress.value}% 0%)`;
barStyle.width = unwrapRef(width) + 1 + "px";
break;
case Direction.Right:
barStyle.clipPath = `inset(0% ${normalizedProgress.value}% 0% 0%)`;
break;
case Direction.Left:
barStyle.clipPath = `inset(0% 0% 0% ${normalizedProgress.value}%)`;
break;
case Direction.Default:
barStyle.clipPath = "inset(0% 50% 0% 0%)";
break;
}
return barStyle;
});
const component = computeOptionalComponent(display);
return {
normalizedProgress,
barStyle,
component,
unref,
Visibility,
isVisible,
isHidden
};
}
const normalizedProgress = computed(() => {
let progressNumber =
props.progress instanceof Decimal
? props.progress.toNumber()
: Number(props.progress);
return (1 - Math.min(Math.max(progressNumber, 0), 1)) * 100;
});
const barStyle = computed(() => {
const barStyle: Partial<CSSProperties> = {
width: props.width + 0.5 + "px",
height: props.height + 0.5 + "px"
};
switch (props.direction) {
case Direction.Up:
barStyle.clipPath = `inset(${normalizedProgress.value}% 0% 0% 0%)`;
barStyle.width = props.width + 1 + "px";
break;
case Direction.Down:
barStyle.clipPath = `inset(0% 0% ${normalizedProgress.value}% 0%)`;
barStyle.width = props.width + 1 + "px";
break;
case Direction.Right:
barStyle.clipPath = `inset(0% ${normalizedProgress.value}% 0% 0%)`;
break;
case Direction.Left:
barStyle.clipPath = `inset(0% 0% 0% ${normalizedProgress.value}%)`;
break;
case Direction.Default:
barStyle.clipPath = "inset(0% 50% 0% 0%)";
break;
}
return barStyle;
});
const component = computeOptionalComponent(toRef(props, "display"));
</script>
<style scoped>

View file

@ -30,7 +30,7 @@
</div>
</template>
<script lang="tsx">
<script setup lang="tsx">
import "components/common/features.css";
import MarkNode from "components/MarkNode.vue";
import Node from "components/Node.vue";
@ -39,139 +39,92 @@ import type { StyleValue } from "features/feature";
import { isHidden, isVisible, jsx, Visibility } from "features/feature";
import { getHighNotifyStyle, getNotifyStyle } from "game/notifications";
import { displayRequirements, Requirements } from "game/requirements";
import { coerceComponent, isCoercableComponent, processedPropType, unwrapRef } from "util/vue";
import type { Component, PropType, UnwrapRef } from "vue";
import { computed, defineComponent, shallowRef, toRefs, unref, watchEffect } from "vue";
import { coerceComponent, isCoercableComponent } from "util/vue";
import type { Component, UnwrapRef } from "vue";
import { computed, shallowRef, unref, watchEffect } from "vue";
export default defineComponent({
props: {
active: {
type: processedPropType<boolean>(Boolean),
required: true
},
maxed: {
type: processedPropType<boolean>(Boolean),
required: true
},
canComplete: {
type: processedPropType<boolean>(Boolean),
required: true
},
display: processedPropType<UnwrapRef<GenericChallenge["display"]>>(
String,
Object,
Function
),
requirements: processedPropType<Requirements>(Object, Array),
visibility: {
type: processedPropType<Visibility | boolean>(Number, Boolean),
required: true
},
style: processedPropType<StyleValue>(String, Object, Array),
classes: processedPropType<Record<string, boolean>>(Object),
completed: {
type: processedPropType<boolean>(Boolean),
required: true
},
canStart: {
type: processedPropType<boolean>(Boolean),
required: true
},
mark: processedPropType<boolean | string>(Boolean, String),
id: {
type: String,
required: true
},
toggle: {
type: Function as PropType<VoidFunction>,
required: true
}
},
components: {
MarkNode,
Node
},
setup(props) {
const { active, maxed, canComplete, display, requirements } = toRefs(props);
const props = defineProps<{
active: boolean;
maxed: boolean;
canComplete: boolean;
display?: UnwrapRef<GenericChallenge["display"]>;
requirements?: Requirements;
visibility: Visibility | boolean;
style?: StyleValue;
classes?: Record<string, boolean>;
completed: boolean;
canStart: boolean;
mark?: boolean | string;
id: string;
toggle: VoidFunction;
}>();
const buttonText = computed(() => {
if (active.value) {
return canComplete.value ? "Finish" : "Exit Early";
}
if (maxed.value) {
return "Completed";
}
return "Start";
});
const comp = shallowRef<Component | string>("");
const notifyStyle = computed(() => {
const currActive = unwrapRef(active);
const currCanComplete = unwrapRef(canComplete);
if (currActive) {
if (currCanComplete) {
return getHighNotifyStyle();
}
return getNotifyStyle();
}
return {};
});
watchEffect(() => {
const currDisplay = unwrapRef(display);
if (currDisplay == null) {
comp.value = "";
return;
}
if (isCoercableComponent(currDisplay)) {
comp.value = coerceComponent(currDisplay);
return;
}
const Title = coerceComponent(currDisplay.title || "", "h3");
const Description = coerceComponent(currDisplay.description, "div");
const Goal = coerceComponent(currDisplay.goal != null ? currDisplay.goal : jsx(() => displayRequirements(unwrapRef(requirements) ?? [])), "h3");
const Reward = coerceComponent(currDisplay.reward || "");
const EffectDisplay = coerceComponent(currDisplay.effectDisplay || "");
comp.value = coerceComponent(
jsx(() => (
<span>
{currDisplay.title != null ? (
<div>
<Title />
</div>
) : null}
<Description />
<div>
<br />
Goal: <Goal />
</div>
{currDisplay.reward != null ? (
<div>
<br />
Reward: <Reward />
</div>
) : null}
{currDisplay.effectDisplay != null ? (
<div>
Currently: <EffectDisplay />
</div>
) : null}
</span>
))
);
});
return {
buttonText,
notifyStyle,
comp,
Visibility,
isVisible,
isHidden,
unref
};
const buttonText = computed(() => {
if (props.active) {
return props.canComplete ? "Finish" : "Exit Early";
}
if (props.maxed) {
return "Completed";
}
return "Start";
});
const comp = shallowRef<Component | string>("");
const notifyStyle = computed(() => {
const currActive = props.active;
const currCanComplete = props.canComplete;
if (currActive) {
if (currCanComplete) {
return getHighNotifyStyle();
}
return getNotifyStyle();
}
return {};
});
watchEffect(() => {
const currDisplay = props.display;
if (currDisplay == null) {
comp.value = "";
return;
}
if (isCoercableComponent(currDisplay)) {
comp.value = coerceComponent(currDisplay);
return;
}
const Title = coerceComponent(currDisplay.title || "", "h3");
const Description = coerceComponent(currDisplay.description, "div");
const Goal = coerceComponent(currDisplay.goal != null ? currDisplay.goal : jsx(() => displayRequirements(props.requirements ?? [])), "h3");
const Reward = coerceComponent(currDisplay.reward || "");
const EffectDisplay = coerceComponent(currDisplay.effectDisplay || "");
comp.value = coerceComponent(
jsx(() => (
<span>
{currDisplay.title != null ? (
<div>
<Title />
</div>
) : null}
<Description />
<div>
<br />
Goal: <Goal />
</div>
{currDisplay.reward != null ? (
<div>
<br />
Reward: <Reward />
</div>
) : null}
{currDisplay.effectDisplay != null ? (
<div>
Currently: <EffectDisplay />
</div>
) : null}
</span>
))
);
});
</script>

View file

@ -27,7 +27,7 @@
</button>
</template>
<script lang="tsx">
<script setup lang="tsx">
import "components/common/features.css";
import MarkNode from "components/MarkNode.vue";
import Node from "components/Node.vue";
@ -37,90 +37,53 @@ import { isHidden, isVisible, jsx, Visibility } from "features/feature";
import {
coerceComponent,
isCoercableComponent,
processedPropType,
setupHoldToClick,
unwrapRef
setupHoldToClick
} from "util/vue";
import type { Component, PropType, UnwrapRef } from "vue";
import { defineComponent, shallowRef, toRefs, unref, watchEffect } from "vue";
import type { Component, UnwrapRef } from "vue";
import { shallowRef, toRef, unref, watchEffect } from "vue";
export default defineComponent({
props: {
display: {
type: processedPropType<UnwrapRef<GenericClickable["display"]>>(
Object,
String,
Function
),
required: true
},
visibility: {
type: processedPropType<Visibility | boolean>(Number, Boolean),
required: true
},
style: processedPropType<StyleValue>(Object, String, Array),
classes: processedPropType<Record<string, boolean>>(Object),
onClick: Function as PropType<(e?: MouseEvent | TouchEvent) => void>,
onHold: Function as PropType<VoidFunction>,
canClick: {
type: processedPropType<boolean>(Boolean),
required: true
},
small: Boolean,
mark: processedPropType<boolean | string>(Boolean, String),
id: {
type: String,
required: true
}
},
components: {
Node,
MarkNode
},
setup(props) {
const { display, onClick, onHold } = toRefs(props);
const props = defineProps<{
display: UnwrapRef<GenericClickable["display"]>;
visibility: Visibility | boolean;
style?: StyleValue;
classes?: Record<string, boolean>;
onClick?: (e?: MouseEvent | TouchEvent) => void;
onHold?: VoidFunction;
canClick: boolean;
small?: boolean;
mark?: boolean | string;
id: string;
}>();
const comp = shallowRef<Component | string>("");
const comp = shallowRef<Component | string>("");
watchEffect(() => {
const currDisplay = unwrapRef(display);
if (currDisplay == null) {
comp.value = "";
return;
}
if (isCoercableComponent(currDisplay)) {
comp.value = coerceComponent(currDisplay);
return;
}
const Title = coerceComponent(currDisplay.title ?? "", "h3");
const Description = coerceComponent(currDisplay.description, "div");
comp.value = coerceComponent(
jsx(() => (
<span>
{currDisplay.title != null ? (
<div>
<Title />
</div>
) : null}
<Description />
</span>
))
);
});
const { start, stop } = setupHoldToClick(onClick, onHold);
return {
start,
stop,
comp,
Visibility,
isVisible,
isHidden,
unref
};
watchEffect(() => {
const currDisplay = props.display;
if (currDisplay == null) {
comp.value = "";
return;
}
if (isCoercableComponent(currDisplay)) {
comp.value = coerceComponent(currDisplay);
return;
}
const Title = coerceComponent(currDisplay.title ?? "", "h3");
const Description = coerceComponent(currDisplay.description, "div");
comp.value = coerceComponent(
jsx(() => (
<span>
{currDisplay.title != null ? (
<div>
<Title />
</div>
) : null}
<Description />
</span>
))
);
});
const { start, stop } = setupHoldToClick(toRef(props, "onClick"), toRef(props, "onHold"));
</script>
<style scoped>

View file

@ -7,7 +7,7 @@
class="table-grid"
>
<div v-for="row in unref(rows)" class="row-grid" :class="{ mergeAdjacent }" :key="row">
<GridCell
<GridCellVue
v-for="col in unref(cols)"
:key="col"
v-bind="gatherCellProps(unref(cells)[row * 100 + col])"
@ -16,45 +16,26 @@
</div>
</template>
<script lang="ts">
<script setup lang="ts">
import "components/common/table.css";
import themes from "data/themes";
import { isHidden, isVisible, Visibility } from "features/feature";
import type { GridCell } from "features/grids/grid";
import settings from "game/settings";
import { processedPropType } from "util/vue";
import { computed, defineComponent, unref } from "vue";
import { computed, unref } from "vue";
import GridCellVue from "./GridCell.vue";
export default defineComponent({
props: {
visibility: {
type: processedPropType<Visibility | boolean>(Number, Boolean),
required: true
},
rows: {
type: processedPropType<number>(Number),
required: true
},
cols: {
type: processedPropType<number>(Number),
required: true
},
cells: {
type: processedPropType<Record<string, GridCell>>(Object),
required: true
}
},
components: { GridCell: GridCellVue },
setup() {
const mergeAdjacent = computed(() => themes[settings.theme].mergeAdjacent);
defineProps<{
visibility: Visibility | boolean;
rows: number;
cols: number;
cells: Record<string, GridCell>;
}>();
function gatherCellProps(cell: GridCell) {
const { visibility, onClick, onHold, display, title, style, canClick, id } = cell;
return { visibility, onClick, onHold, display, title, style, canClick, id };
}
const mergeAdjacent = computed(() => themes[settings.theme].mergeAdjacent);
return { unref, gatherCellProps, Visibility, mergeAdjacent, isVisible, isHidden };
}
});
function gatherCellProps(cell: GridCell) {
const { visibility, onClick, onHold, display, title, style, canClick, id } = cell;
return { visibility, onClick, onHold, display, title, style, canClick, id };
}
</script>

View file

@ -22,7 +22,7 @@
</button>
</template>
<script lang="ts">
<script setup lang="ts">
import "components/common/features.css";
import Node from "components/Node.vue";
import type { CoercableComponent, StyleValue } from "features/feature";
@ -30,58 +30,26 @@ import { isHidden, isVisible, Visibility } from "features/feature";
import {
computeComponent,
computeOptionalComponent,
processedPropType,
setupHoldToClick
} from "util/vue";
import type { PropType } from "vue";
import { defineComponent, toRefs, unref } from "vue";
import { toRef, unref } from "vue";
export default defineComponent({
props: {
visibility: {
type: processedPropType<Visibility | boolean>(Number, Boolean),
required: true
},
onClick: Function as PropType<(e?: MouseEvent | TouchEvent) => void>,
onHold: Function as PropType<VoidFunction>,
display: {
type: processedPropType<CoercableComponent>(Object, String, Function),
required: true
},
title: processedPropType<CoercableComponent>(Object, String, Function),
style: processedPropType<StyleValue>(String, Object, Array),
canClick: {
type: processedPropType<boolean>(Boolean),
required: true
},
id: {
type: String,
required: true
}
},
components: {
Node
},
setup(props) {
const { onClick, onHold, title, display } = toRefs(props);
const props = defineProps<{
visibility: Visibility | boolean;
onClick?: (e?: MouseEvent | TouchEvent) => void;
onHold?: VoidFunction;
display: CoercableComponent;
title?: CoercableComponent;
style?: StyleValue;
canClick: boolean;
id: string;
}>();
const { start, stop } = setupHoldToClick(onClick, onHold);
const titleComponent = computeOptionalComponent(title);
const component = computeComponent(display);
const { start, stop } = setupHoldToClick(toRef(props, "onClick"), toRef(props, "onHold"));
return {
start,
stop,
titleComponent,
component,
Visibility,
unref,
isVisible,
isHidden
};
}
});
const titleComponent = computeOptionalComponent(toRef(props, "title"));
const component = computeComponent(toRef(props, "display"));
</script>
<style scoped>

View file

@ -28,67 +28,33 @@
</div>
</template>
<script lang="ts">
<script setup lang="ts">
import CollapseTransition from "@ivanv/vue-collapse-transition/src/CollapseTransition.vue";
import Node from "components/Node.vue";
import themes from "data/themes";
import type { CoercableComponent } from "features/feature";
import { isHidden, isVisible, Visibility } from "features/feature";
import settings from "game/settings";
import { computeComponent, processedPropType } from "util/vue";
import type { PropType, Ref, StyleValue } from "vue";
import { computed, defineComponent, toRefs, unref } from "vue";
import { computeComponent } from "util/vue";
import type { Ref, StyleValue } from "vue";
import { computed, toRef, unref } from "vue";
export default defineComponent({
props: {
visibility: {
type: processedPropType<Visibility | boolean>(Number, Boolean),
required: true
},
display: {
type: processedPropType<CoercableComponent>(Object, String, Function),
required: true
},
title: {
type: processedPropType<CoercableComponent>(Object, String, Function),
required: true
},
color: processedPropType<string>(String),
collapsed: {
type: Object as PropType<Ref<boolean>>,
required: true
},
style: processedPropType<StyleValue>(Object, String, Array),
titleStyle: processedPropType<StyleValue>(Object, String, Array),
bodyStyle: processedPropType<StyleValue>(Object, String, Array),
classes: processedPropType<Record<string, boolean>>(Object),
id: {
type: String,
required: true
}
},
components: {
Node,
CollapseTransition
},
setup(props) {
const { title, display } = toRefs(props);
const props = defineProps<{
visibility: Visibility | boolean;
display: CoercableComponent;
title: CoercableComponent;
color?: string;
collapsed: Ref<boolean>;
style?: StyleValue;
titleStyle?: StyleValue;
bodyStyle?: StyleValue;
classes?: Record<string, boolean>;
id: string;
}>();
const titleComponent = computeComponent(title);
const bodyComponent = computeComponent(display);
const stacked = computed(() => themes[settings.theme].mergeAdjacent);
return {
titleComponent,
bodyComponent,
stacked,
unref,
Visibility,
isVisible,
isHidden
};
}
});
const titleComponent = computeComponent(toRef(props, "title"));
const bodyComponent = computeComponent(toRef(props, "display"));
const stacked = computed(() => themes[settings.theme].mergeAdjacent);
</script>
<style scoped>

View file

@ -7,78 +7,61 @@
/>
</template>
<script lang="tsx">
<script setup lang="tsx">
import { Application } from "@pixi/app";
import type { StyleValue } from "features/feature";
import { globalBus } from "game/events";
import "lib/pixi";
import { processedPropType } from "util/vue";
import type { PropType } from "vue";
import { defineComponent, nextTick, onBeforeUnmount, onMounted, shallowRef, unref } from "vue";
import { nextTick, onBeforeUnmount, onMounted, shallowRef, unref } from "vue";
// TODO get typing support on the Particles component
export default defineComponent({
props: {
style: processedPropType<StyleValue>(String, Object, Array),
classes: processedPropType<Record<string, boolean>>(Object),
onInit: {
type: Function as PropType<(app: Application) => void>,
required: true
},
id: {
type: String,
required: true
},
onContainerResized: Function as PropType<(rect: DOMRect) => void>,
onHotReload: Function as PropType<VoidFunction>
},
setup(props) {
const app = shallowRef<null | Application>(null);
const props = defineProps<{
style?: StyleValue;
classes?: Record<string, boolean>;
onInit: (app: Application) => void;
id: string;
onContainerResized?: (rect: DOMRect) => void;
onHotReload?: VoidFunction;
}>();
const resizeObserver = new ResizeObserver(updateBounds);
const resizeListener = shallowRef<HTMLElement | null>(null);
const app = shallowRef<null | Application>(null);
onMounted(() => {
// ResizeListener exists because ResizeObserver's don't work when told to observe an SVG element
const resListener = resizeListener.value;
if (resListener != null) {
resizeObserver.observe(resListener);
app.value = new Application({
resizeTo: resListener,
backgroundAlpha: 0
});
resizeListener.value?.appendChild(app.value.view);
props.onInit?.(app.value as Application);
}
updateBounds();
if (props.onHotReload) {
nextTick(props.onHotReload);
}
const resizeObserver = new ResizeObserver(updateBounds);
const resizeListener = shallowRef<HTMLElement | null>(null);
onMounted(() => {
// ResizeListener exists because ResizeObserver's don't work when told to observe an SVG element
const resListener = resizeListener.value;
if (resListener != null) {
resizeObserver.observe(resListener);
app.value = new Application({
resizeTo: resListener,
backgroundAlpha: 0
});
onBeforeUnmount(() => {
app.value?.destroy();
});
let isDirty = true;
function updateBounds() {
if (isDirty) {
isDirty = false;
nextTick(() => {
if (resizeListener.value != null) {
props.onContainerResized?.(resizeListener.value.getBoundingClientRect());
}
isDirty = true;
});
}
}
globalBus.on("fontsLoaded", updateBounds);
return {
unref,
resizeListener
};
resizeListener.value?.appendChild(app.value.view);
props.onInit?.(app.value as Application);
}
updateBounds();
if (props.onHotReload) {
nextTick(props.onHotReload);
}
});
onBeforeUnmount(() => {
app.value?.destroy();
});
let isDirty = true;
function updateBounds() {
if (isDirty) {
isDirty = false;
nextTick(() => {
if (resizeListener.value != null) {
props.onContainerResized?.(resizeListener.value.getBoundingClientRect());
}
isDirty = true;
});
}
}
globalBus.on("fontsLoaded", updateBounds);
</script>
<style scoped>

View file

@ -19,61 +19,43 @@
</button>
</template>
<script lang="ts">
<script setup lang="ts">
import type { CoercableComponent, StyleValue } from "features/feature";
import { isHidden, isVisible, Visibility } from "features/feature";
import { getNotifyStyle } from "game/notifications";
import { computeComponent, processedPropType, unwrapRef } from "util/vue";
import { computed, defineComponent, toRefs, unref } from "vue";
import { computeComponent } from "util/vue";
import { computed, toRef, unref } from "vue";
export default defineComponent({
props: {
visibility: {
type: processedPropType<Visibility | boolean>(Number, Boolean),
required: true
},
display: {
type: processedPropType<CoercableComponent>(Object, String, Function),
required: true
},
style: processedPropType<StyleValue>(String, Object, Array),
classes: processedPropType<Record<string, boolean>>(Object),
glowColor: processedPropType<string>(String),
active: Boolean,
floating: Boolean
},
emits: ["selectTab"],
setup(props, { emit }) {
const { display, glowColor, floating } = toRefs(props);
const props = defineProps<{
visibility: Visibility | boolean;
display: CoercableComponent;
style?: StyleValue;
classes?: Record<string, boolean>;
glowColor?: string;
active?: boolean;
floating?: boolean;
}>();
const component = computeComponent(display);
const emit = defineEmits<{
selectTab: [];
}>();
const glowColorStyle = computed(() => {
const color = unwrapRef(glowColor);
if (color == null || color === "") {
return {};
}
if (unref(floating)) {
return getNotifyStyle(color);
}
return { boxShadow: `0px 9px 5px -6px ${color}` };
});
const component = computeComponent(toRef(props, "display"));
function selectTab() {
emit("selectTab");
}
return {
selectTab,
component,
glowColorStyle,
unref,
Visibility,
isVisible,
isHidden
};
const glowColorStyle = computed(() => {
const color = props.glowColor;
if (color == null || color === "") {
return {};
}
if (props.floating) {
return getNotifyStyle(color);
}
return { boxShadow: `0px 9px 5px -6px ${color}` };
});
function selectTab() {
emit("selectTab");
}
</script>
<style scoped>

View file

@ -33,7 +33,7 @@
</div>
</template>
<script lang="ts">
<script setup lang="ts">
import Sticky from "components/layout/Sticky.vue";
import themes from "data/themes";
import type { CoercableComponent, StyleValue } from "features/feature";
@ -42,93 +42,60 @@ import type { GenericTab } from "features/tabs/tab";
import TabButton from "features/tabs/TabButton.vue";
import type { GenericTabButton } from "features/tabs/tabFamily";
import settings from "game/settings";
import { coerceComponent, isCoercableComponent, processedPropType, unwrapRef } from "util/vue";
import type { Component, PropType, Ref } from "vue";
import { computed, defineComponent, shallowRef, toRefs, unref, watchEffect } from "vue";
import { coerceComponent, deepUnref, isCoercableComponent } from "util/vue";
import type { Component, Ref } from "vue";
import { computed, shallowRef, unref, watchEffect } from "vue";
export default defineComponent({
props: {
visibility: {
type: processedPropType<Visibility | boolean>(Number, Boolean),
required: true
},
activeTab: {
type: processedPropType<GenericTab | CoercableComponent | null>(Object),
required: true
},
selected: {
type: Object as PropType<Ref<string>>,
required: true
},
tabs: {
type: processedPropType<Record<string, GenericTabButton>>(Object),
required: true
},
style: processedPropType<StyleValue>(String, Object, Array),
classes: processedPropType<Record<string, boolean>>(Object),
buttonContainerStyle: processedPropType<StyleValue>(String, Object, Array),
buttonContainerClasses: processedPropType<Record<string, boolean>>(Object)
},
components: {
Sticky,
TabButton
},
setup(props) {
const { activeTab } = toRefs(props);
const props = defineProps<{
visibility: Visibility | boolean;
activeTab: GenericTab | CoercableComponent | null;
selected: Ref<string>;
tabs: Record<string, GenericTabButton>;
style?: StyleValue;
classes?: Record<string, boolean>;
buttonContainerStyle?: StyleValue;
buttonContainerClasses?: Record<string, boolean>;
}>();
const floating = computed(() => {
return themes[settings.theme].floatingTabs;
});
const component = shallowRef<Component | string>("");
watchEffect(() => {
const currActiveTab = unwrapRef(activeTab);
if (currActiveTab == null) {
component.value = "";
return;
}
if (isCoercableComponent(currActiveTab)) {
component.value = coerceComponent(currActiveTab);
return;
}
component.value = coerceComponent(unref(currActiveTab.display));
});
const tabClasses = computed(() => {
const currActiveTab = unwrapRef(activeTab);
const tabClasses =
isCoercableComponent(currActiveTab) || !currActiveTab
? undefined
: unref(currActiveTab.classes);
return tabClasses;
});
const tabStyle = computed(() => {
const currActiveTab = unwrapRef(activeTab);
return isCoercableComponent(currActiveTab) || !currActiveTab
? undefined
: unref(currActiveTab.style);
});
function gatherButtonProps(button: GenericTabButton) {
const { display, style, classes, glowColor, visibility } = button;
return { display, style: unref(style), classes, glowColor, visibility };
}
return {
floating,
tabClasses,
tabStyle,
Visibility,
component,
gatherButtonProps,
unref,
isVisible,
isHidden
};
}
const floating = computed(() => {
return themes[settings.theme].floatingTabs;
});
const component = shallowRef<Component | string>("");
watchEffect(() => {
const currActiveTab = props.activeTab;
if (currActiveTab == null) {
component.value = "";
return;
}
if (isCoercableComponent(currActiveTab)) {
component.value = coerceComponent(currActiveTab);
return;
}
component.value = coerceComponent(unref(currActiveTab.display));
});
const tabClasses = computed(() => {
const currActiveTab = props.activeTab;
const tabClasses =
isCoercableComponent(currActiveTab) || !currActiveTab
? undefined
: unref(currActiveTab.classes);
return tabClasses;
});
const tabStyle = computed(() => {
const currActiveTab = props.activeTab;
return isCoercableComponent(currActiveTab) || !currActiveTab
? undefined
: unref(currActiveTab.style);
});
function gatherButtonProps(button: GenericTabButton) {
const { display, style, classes, glowColor, visibility } = deepUnref(button);
return { display, style, classes, glowColor, visibility };
}
</script>
<style scoped>

View file

@ -34,7 +34,7 @@
</div>
</template>
<script lang="tsx">
<script setup lang="tsx">
import themes from "data/themes";
import type { CoercableComponent } from "features/feature";
import { jsx, StyleValue } from "features/feature";
@ -45,66 +45,45 @@ import type { VueFeature } from "util/vue";
import {
coerceComponent,
computeOptionalComponent,
processedPropType,
renderJSX,
unwrapRef
renderJSX
} from "util/vue";
import type { Component, PropType } from "vue";
import { computed, defineComponent, ref, shallowRef, toRefs, unref } from "vue";
import type { Component } from "vue";
import { computed, ref, shallowRef, toRef, unref } from "vue";
export default defineComponent({
props: {
element: Object as PropType<VueFeature>,
display: {
type: processedPropType<CoercableComponent>(Object, String, Function),
required: true
},
style: processedPropType<StyleValue>(Object, String, Array),
classes: processedPropType<Record<string, boolean>>(Object),
direction: processedPropType<Direction>(String),
xoffset: processedPropType<string>(String),
yoffset: processedPropType<string>(String),
pinned: Object as PropType<Persistent<boolean>>
},
setup(props) {
const { element, display, pinned } = toRefs(props);
const props = defineProps<{
element?: VueFeature;
display: CoercableComponent;
style?: StyleValue;
classes?: Record<string, boolean>;
direction?: Direction;
xoffset?: string;
yoffset?: string;
pinned?: Persistent<boolean>;
}>();
const isHovered = ref(false);
const isShown = computed(() => (unwrapRef(pinned) || isHovered.value) && comp.value);
const comp = computeOptionalComponent(display);
const isHovered = ref(false);
const isShown = computed(() => (props.pinned?.value === true || isHovered.value) && comp.value);
const comp = computeOptionalComponent(toRef(props, "display"));
const elementComp = shallowRef<Component | "" | null>(
coerceComponent(
jsx(() => {
const currComponent = unwrapRef(element);
return currComponent == null ? "" : renderJSX(currComponent);
})
)
);
const elementComp = shallowRef<Component | "" | null>(
coerceComponent(
jsx(() => {
const currComponent = props.element;
return currComponent == null ? "" : renderJSX(currComponent);
})
)
);
function togglePinned(e: MouseEvent) {
const isPinned = pinned as unknown as Persistent<boolean> | undefined; // Vue typing :/
if (e.shiftKey && isPinned) {
isPinned.value = !isPinned.value;
e.stopPropagation();
e.preventDefault();
}
}
const showPin = computed(() => unwrapRef(pinned) && themes[settings.theme].showPin);
return {
Direction,
isHovered,
isShown,
comp,
elementComp,
unref,
togglePinned,
showPin
};
function togglePinned(e: MouseEvent) {
const isPinned = props.pinned;
if (e.shiftKey && isPinned != null) {
isPinned.value = !isPinned.value;
e.stopPropagation();
e.preventDefault();
}
});
}
const showPin = computed(() => props.pinned?.value === true && themes[settings.theme].showPin);
</script>
<style scoped>

View file

@ -5,74 +5,58 @@
<Links v-if="branches" :links="unref(branches)" />
</template>
<script lang="tsx">
<script setup lang="tsx">
import "components/common/table.css";
import { jsx } from "features/feature";
import Links from "features/links/Links.vue";
import type { GenericTreeNode, TreeBranch } from "features/trees/tree";
import { coerceComponent, processedPropType, renderJSX, unwrapRef } from "util/vue";
import { coerceComponent, renderJSX } from "util/vue";
import type { Component } from "vue";
import { defineComponent, shallowRef, toRefs, unref, watchEffect } from "vue";
import { shallowRef, unref, watchEffect } from "vue";
export default defineComponent({
props: {
nodes: {
type: processedPropType<GenericTreeNode[][]>(Array),
required: true
},
leftSideNodes: processedPropType<GenericTreeNode[]>(Array),
rightSideNodes: processedPropType<GenericTreeNode[]>(Array),
branches: processedPropType<TreeBranch[]>(Array)
},
components: { Links },
setup(props) {
const { nodes, leftSideNodes, rightSideNodes } = toRefs(props);
const props = defineProps<{
nodes: GenericTreeNode[][];
leftSideNodes?: GenericTreeNode[];
rightSideNodes?: GenericTreeNode[];
branches?: TreeBranch[];
}>();
const nodesComp = shallowRef<Component | "">();
watchEffect(() => {
const currNodes = unwrapRef(nodes);
nodesComp.value = coerceComponent(
const nodesComp = shallowRef<Component | "">();
watchEffect(() => {
const currNodes = props.nodes;
nodesComp.value = coerceComponent(
jsx(() => (
<>
{currNodes.map(row => (
<span class="row tree-row" style="margin: 50px auto;">
{row.map(renderJSX)}
</span>
))}
</>
))
);
});
const leftNodesComp = shallowRef<Component | "">();
watchEffect(() => {
const currNodes = props.leftSideNodes;
leftNodesComp.value = currNodes
? coerceComponent(
jsx(() => (
<>
{currNodes.map(row => (
<span class="row tree-row" style="margin: 50px auto;">
{row.map(renderJSX)}
</span>
))}
</>
<span class="left-side-nodes small">{currNodes.map(renderJSX)}</span>
))
);
});
)
: "";
});
const leftNodesComp = shallowRef<Component | "">();
watchEffect(() => {
const currNodes = unwrapRef(leftSideNodes);
leftNodesComp.value = currNodes
? coerceComponent(
jsx(() => (
<span class="left-side-nodes small">{currNodes.map(renderJSX)}</span>
))
)
: "";
});
const rightNodesComp = shallowRef<Component | "">();
watchEffect(() => {
const currNodes = unwrapRef(rightSideNodes);
rightNodesComp.value = currNodes
? coerceComponent(
jsx(() => <span class="side-nodes small">{currNodes.map(renderJSX)}</span>)
)
: "";
});
return {
unref,
nodesComp,
leftNodesComp,
rightNodesComp
};
}
const rightNodesComp = shallowRef<Component | "">();
watchEffect(() => {
const currNodes = props.rightSideNodes;
rightNodesComp.value = currNodes
? coerceComponent(
jsx(() => <span class="side-nodes small">{currNodes.map(renderJSX)}</span>)
)
: "";
});
</script>

View file

@ -33,66 +33,32 @@
</div>
</template>
<script lang="ts">
import MarkNode from "components/MarkNode.vue";
import Node from "components/Node.vue";
import type { CoercableComponent, StyleValue } from "features/feature";
import { isHidden, isVisible, Visibility } from "features/feature";
<script setup lang="ts">
import type { CoercableComponent, StyleValue, Visibility } from "features/feature";
import { isHidden, isVisible } from "features/feature";
import {
computeOptionalComponent,
isCoercableComponent,
processedPropType,
setupHoldToClick
} from "util/vue";
import type { PropType } from "vue";
import { defineComponent, toRefs, unref } from "vue";
import { toRef, unref } from "vue";
export default defineComponent({
props: {
display: processedPropType<CoercableComponent>(Object, String, Function),
visibility: {
type: processedPropType<Visibility | boolean>(Number, Boolean),
required: true
},
style: processedPropType<StyleValue>(String, Object, Array),
classes: processedPropType<Record<string, boolean>>(Object),
onClick: Function as PropType<(e?: MouseEvent | TouchEvent) => void>,
onHold: Function as PropType<VoidFunction>,
color: processedPropType<string>(String),
glowColor: processedPropType<string>(String),
canClick: {
type: processedPropType<boolean>(Boolean),
required: true
},
mark: processedPropType<boolean | string>(Boolean, String),
id: {
type: String,
required: true
}
},
components: {
MarkNode,
Node
},
setup(props) {
const { onClick, onHold, display } = toRefs(props);
const props = defineProps<{
visibility: Visibility | boolean;
canClick: boolean;
id: string;
display?: CoercableComponent;
style?: StyleValue;
classes?: Record<string, boolean>;
onClick?: (e?: MouseEvent | TouchEvent) => void;
onHold?: VoidFunction;
color?: string;
glowColor?: string;
mark?: boolean | string;
}>();
const comp = computeOptionalComponent(display);
const comp = computeOptionalComponent(toRef(props, "display"));
const { start, stop } = setupHoldToClick(onClick, onHold);
return {
start,
stop,
comp,
unref,
Visibility,
isCoercableComponent,
isVisible,
isHidden
};
}
});
const { start, stop } = setupHoldToClick(toRef(props, "onClick"), toRef(props, "onHold"));
</script>
<style scoped>

View file

@ -24,7 +24,7 @@
</button>
</template>
<script lang="tsx">
<script setup lang="tsx">
import "components/common/features.css";
import MarkNode from "components/MarkNode.vue";
import Node from "components/Node.vue";
@ -32,94 +32,56 @@ import type { StyleValue } from "features/feature";
import { isHidden, isVisible, jsx, Visibility } from "features/feature";
import type { GenericUpgrade } from "features/upgrades/upgrade";
import { displayRequirements, Requirements } from "game/requirements";
import { coerceComponent, isCoercableComponent, processedPropType, unwrapRef } from "util/vue";
import type { Component, PropType, UnwrapRef } from "vue";
import { defineComponent, shallowRef, toRefs, unref, watchEffect } from "vue";
import { coerceComponent, isCoercableComponent } from "util/vue";
import type { Component, UnwrapRef } from "vue";
import { shallowRef, unref, watchEffect } from "vue";
export default defineComponent({
props: {
display: {
type: processedPropType<UnwrapRef<GenericUpgrade["display"]>>(String, Object, Function),
required: true
},
visibility: {
type: processedPropType<Visibility | boolean>(Number, Boolean),
required: true
},
style: processedPropType<StyleValue>(String, Object, Array),
classes: processedPropType<Record<string, boolean>>(Object),
requirements: {
type: Object as PropType<Requirements>,
required: true
},
canPurchase: {
type: processedPropType<boolean>(Boolean),
required: true
},
bought: {
type: processedPropType<boolean>(Boolean),
required: true
},
mark: processedPropType<boolean | string>(Boolean, String),
id: {
type: String,
required: true
},
purchase: {
type: Function as PropType<VoidFunction>,
required: true
}
},
components: {
Node,
MarkNode
},
setup(props) {
const { display, requirements, bought } = toRefs(props);
const props = defineProps<{
display: UnwrapRef<GenericUpgrade["display"]>;
visibility: Visibility | boolean;
style?: StyleValue;
classes?: Record<string, boolean>;
requirements: Requirements;
canPurchase: boolean;
bought: boolean;
mark?: boolean | string;
id: string;
purchase?: VoidFunction;
}>();
const component = shallowRef<Component | string>("");
const component = shallowRef<Component | string>("");
watchEffect(() => {
const currDisplay = unwrapRef(display);
if (currDisplay == null) {
component.value = "";
return;
}
if (isCoercableComponent(currDisplay)) {
component.value = coerceComponent(currDisplay);
return;
}
const Title = coerceComponent(currDisplay.title || "", "h3");
const Description = coerceComponent(currDisplay.description, "div");
const EffectDisplay = coerceComponent(currDisplay.effectDisplay || "");
component.value = coerceComponent(
jsx(() => (
<span>
{currDisplay.title != null ? (
<div>
<Title />
</div>
) : null}
<Description />
{currDisplay.effectDisplay != null ? (
<div>
Currently: <EffectDisplay />
</div>
) : null}
{bought.value ? null : <><br />{displayRequirements(requirements.value)}</>}
</span>
))
);
});
return {
component,
unref,
Visibility,
isVisible,
isHidden
};
watchEffect(() => {
const currDisplay = props.display;
if (currDisplay == null) {
component.value = "";
return;
}
if (isCoercableComponent(currDisplay)) {
component.value = coerceComponent(currDisplay);
return;
}
const Title = coerceComponent(currDisplay.title || "", "h3");
const Description = coerceComponent(currDisplay.description, "div");
const EffectDisplay = coerceComponent(currDisplay.effectDisplay || "");
component.value = coerceComponent(
jsx(() => (
<span>
{currDisplay.title != null ? (
<div>
<Title />
</div>
) : null}
<Description />
{currDisplay.effectDisplay != null ? (
<div>
Currently: <EffectDisplay />
</div>
) : null}
{props.bought ? null : <><br />{displayRequirements(props.requirements)}</>}
</span>
))
);
});
</script>

View file

@ -10,7 +10,7 @@ import {
} from "features/feature";
import type { ProcessedComputable } from "util/computed";
import { DoNotCache } from "util/computed";
import type { Component, ComputedRef, DefineComponent, PropType, Ref, ShallowRef } from "vue";
import type { Component, DefineComponent, Ref, ShallowRef, UnwrapRef } from "vue";
import {
computed,
defineComponent,
@ -175,22 +175,22 @@ export function getFirstFeature<
}
export function computeComponent(
component: Ref<ProcessedComputable<CoercableComponent>>,
component: Ref<CoercableComponent>,
defaultWrapper = "div"
): ShallowRef<Component | ""> {
const comp = shallowRef<Component | "">();
watchEffect(() => {
comp.value = coerceComponent(unwrapRef(component), defaultWrapper);
comp.value = coerceComponent(unref(component), defaultWrapper);
});
return comp as ShallowRef<Component | "">;
}
export function computeOptionalComponent(
component: Ref<ProcessedComputable<CoercableComponent | undefined> | undefined>,
component: Ref<CoercableComponent | undefined>,
defaultWrapper = "div"
): ShallowRef<Component | "" | null> {
const comp = shallowRef<Component | "" | null>(null);
watchEffect(() => {
const currComponent = unwrapRef(component);
const currComponent = unref(component);
comp.value =
currComponent === "" || currComponent == null
? null
@ -199,12 +199,14 @@ export function computeOptionalComponent(
return comp;
}
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<T>(unref(ref));
export function deepUnref<T extends object>(refObject: T): { [K in keyof T]: UnwrapRef<T[K]> } {
return (Object.keys(refObject) as (keyof T)[]).reduce(
(acc, curr) => {
acc[curr] = unref(refObject[curr]) as UnwrapRef<T[keyof T]>;
return acc;
},
{} as { [K in keyof T]: UnwrapRef<T[K]> }
);
}
export function setRefValue<T>(ref: Ref<T | Ref<T>>, value: T) {
@ -222,14 +224,6 @@ export type PropTypes =
| 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>>;
}
export function trackHover(element: VueFeature): Ref<boolean> {
const isHovered = ref(false);
@ -245,8 +239,11 @@ export function trackHover(element: VueFeature): Ref<boolean> {
}
export function kebabifyObject(object: Record<string, unknown>) {
return Object.keys(object).reduce((acc, curr) => {
acc[camelToKebab(curr)] = object[curr];
return acc;
}, {} as Record<string, unknown>);
return Object.keys(object).reduce(
(acc, curr) => {
acc[camelToKebab(curr)] = object[curr];
return acc;
},
{} as Record<string, unknown>
);
}

File diff suppressed because it is too large Load diff