Merge remote-tracking branch 'template/main'

This commit is contained in:
thepaperpilot 2022-03-29 19:40:52 -05:00
commit f0ba5a69f5
15 changed files with 128 additions and 50 deletions

View file

@ -6,6 +6,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] ## [Unreleased]
## [0.2.0] - 2022-03-27
### Added
- Particles feature
- Collapsible layout component
- Utility function for splitting off the first from the list of features that meets a given filter
### Changed
- **BREAKING** Reworked most of the code from Links into a generic Context component that manages the positions of features in the DOM
- Updated vue-cli and TS dependencies
- Challenges cannot be started when maxed, and `canStart` now defaults to `true`
- onClick listeners on various features now get passed a MouseEvent or TouchEvent when possible
- Minor style changes to Milestones, most notably removing min-height
### Fixed
- Buyables didn't support CoercableComponents for displays
- TreeNodes would have a double glow effect on hover
### Removed
- Unused mousemove listener attached to App.vue
## [0.1.4] - 2022-03-13 ## [0.1.4] - 2022-03-13
### Added ### Added
- You can now access this.on() from within a createLayer function (and other BaseLayer properties) - You can now access this.on() from within a createLayer function (and other BaseLayer properties)

4
package-lock.json generated
View file

@ -1,12 +1,12 @@
{ {
"name": "profectus", "name": "profectus",
"version": "0.1.4", "version": "0.2.0",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "profectus", "name": "profectus",
"version": "0.1.4", "version": "0.2.0",
"dependencies": { "dependencies": {
"core-js": "^3.6.5", "core-js": "^3.6.5",
"lodash.clonedeep": "^4.5.0", "lodash.clonedeep": "^4.5.0",

View file

@ -1,6 +1,6 @@
{ {
"name": "profectus", "name": "profectus",
"version": "0.1.4", "version": "0.2.0",
"private": true, "private": true,
"scripts": { "scripts": {
"start": "vue-cli-service serve", "start": "vue-cli-service serve",

View file

@ -1,6 +1,6 @@
<template> <template>
<div id="modal-root" :style="theme" /> <div id="modal-root" :style="theme" />
<div class="app" @mousemove="updateMouse" :style="theme" :class="{ useHeader }"> <div class="app" :style="theme" :class="{ useHeader }">
<Nav v-if="useHeader" /> <Nav v-if="useHeader" />
<Game /> <Game />
<TPS v-if="unref(showTPS)" /> <TPS v-if="unref(showTPS)" />
@ -24,10 +24,6 @@ import themes from "./data/themes";
import settings, { gameComponents } from "./game/settings"; import settings, { gameComponents } from "./game/settings";
import "./main.css"; import "./main.css";
function updateMouse(/* event */) {
// TODO use event to update mouse position for particles
}
const useHeader = projInfo.useHeader; const useHeader = projInfo.useHeader;
const theme = computed(() => themes[settings.theme].variables); const theme = computed(() => themes[settings.theme].variables);
const showTPS = toRef(settings, "showTPS"); const showTPS = toRef(settings, "showTPS");

View file

@ -24,11 +24,11 @@ const observerOptions = {
provide(RegisterNodeInjectionKey, (id, element) => { provide(RegisterNodeInjectionKey, (id, element) => {
const observer = new MutationObserver(() => updateNode(id)); const observer = new MutationObserver(() => updateNode(id));
observer.observe(element, observerOptions); observer.observe(element, observerOptions);
nodes.value[id] = { element, observer }; nodes.value[id] = { element, observer, rect: element.getBoundingClientRect() };
nextTick(() => updateNode(id)); nextTick(() => updateNode(id));
}); });
provide(UnregisterNodeInjectionKey, id => { provide(UnregisterNodeInjectionKey, id => {
nodes.value[id]?.observer?.disconnect(); nodes.value[id]?.observer.disconnect();
nodes.value[id] = undefined; nodes.value[id] = undefined;
}); });
provide(NodesInjectionKey, nodes); provide(NodesInjectionKey, nodes);

View file

@ -1,5 +1,5 @@
<template> <template>
<div class="tabs-container"> <div class="tabs-container" :class="{ useHeader }">
<div v-for="(tab, index) in tabs" :key="index" class="tab" :ref="`tab-${index}`"> <div v-for="(tab, index) in tabs" :key="index" class="tab" :ref="`tab-${index}`">
<Nav v-if="index === 0 && !useHeader" /> <Nav v-if="index === 0 && !useHeader" />
<div class="inner-tab"> <div class="inner-tab">
@ -29,9 +29,8 @@ const layerKeys = computed(() => Object.keys(layers));
const useHeader = projInfo.useHeader; const useHeader = projInfo.useHeader;
function gatherLayerProps(layer: GenericLayer) { function gatherLayerProps(layer: GenericLayer) {
const { display, minimized, minWidth, name, color, style, classes, links, minimizable, nodes } = const { display, minimized, minWidth, name, color, style, classes, minimizable, nodes } = layer;
layer; return { display, minimized, minWidth, name, color, style, classes, minimizable, nodes };
return { display, minimized, minWidth, name, color, style, classes, links, minimizable, nodes };
} }
</script> </script>
@ -44,6 +43,11 @@ function gatherLayerProps(layer: GenericLayer) {
display: flex; display: flex;
} }
.tabs-container:not(.useHeader) {
width: calc(100vw - 50px);
margin-left: 50px;
}
.tab { .tab {
position: relative; position: relative;
height: 100%; height: 100%;

View file

@ -147,12 +147,12 @@ function openDiscord() {
} }
.overlay-nav { .overlay-nav {
position: absolute; position: fixed;
top: 10px; top: 10px;
left: 10px; left: 10px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
z-index: 1; z-index: 2;
} }
.overlay-nav > * { .overlay-nav > * {

View file

@ -0,0 +1,51 @@
<template>
<Col style="width: 100%">
<button @click="collapsed.value = !collapsed.value" class="feature collapsible-toggle">
<component :is="displayComponent" />
</button>
<component v-if="!collapsed.value" :is="contentComponent" />
</Col>
</template>
<script setup lang="ts">
import { CoercableComponent } from "features/feature";
import { coerceComponent } from "util/vue";
import { computed, Ref } from "vue";
import Col from "./Column.vue";
const props = defineProps<{
collapsed: Ref<boolean>;
display: CoercableComponent;
content: CoercableComponent;
}>();
const displayComponent = computed(() => coerceComponent(props.display));
const contentComponent = computed(() => coerceComponent(props.content));
</script>
<style scoped>
.collapsible-toggle {
width: calc(100% - 10px);
background: var(--raised-background);
padding: var(--feature-margin);
color: var(--foreground);
cursor: pointer;
}
:deep(.col) {
margin-top: 0;
margin-bottom: 0;
width: 100%;
}
.mergeAdjacent .collapsible-toggle {
border: 0;
border-top-left-radius: 0 !important;
border-top-right-radius: 0 !important;
}
:deep(.mergeAdjacent .feature:not(.dontMerge):first-child) {
border-top-left-radius: 0 !important;
border-top-right-radius: 0 !important;
}
</style>

View file

@ -26,8 +26,7 @@ const props = toRefs(_props);
const startPosition = computed(() => { const startPosition = computed(() => {
const rect = props.startNode.value.rect; const rect = props.startNode.value.rect;
const boundingRect = props.boundingRect.value; const boundingRect = props.boundingRect.value;
const position = const position = boundingRect
rect && boundingRect
? { ? {
x: rect.x + rect.width / 2 - boundingRect.x, x: rect.x + rect.width / 2 - boundingRect.x,
y: rect.y + rect.height / 2 - boundingRect.y y: rect.y + rect.height / 2 - boundingRect.y
@ -43,8 +42,7 @@ const startPosition = computed(() => {
const endPosition = computed(() => { const endPosition = computed(() => {
const rect = props.endNode.value.rect; const rect = props.endNode.value.rect;
const boundingRect = props.boundingRect.value; const boundingRect = props.boundingRect.value;
const position = const position = boundingRect
rect && boundingRect
? { ? {
x: rect.x + rect.width / 2 - boundingRect.x, x: rect.x + rect.width / 2 - boundingRect.x,
y: rect.y + rect.height / 2 - boundingRect.y y: rect.y + rect.height / 2 - boundingRect.y

View file

@ -14,7 +14,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { Link } from "features/links/links"; import { Link } from "features/links/links";
import { NodesInjectionKey } from "game/layers"; import { FeatureNode, NodesInjectionKey } from "game/layers";
import { computed, inject, nextTick, onMounted, ref, toRef } from "vue"; import { computed, inject, nextTick, onMounted, ref, toRef } from "vue";
import LinkVue from "./Link.vue"; import LinkVue from "./Link.vue";
@ -43,9 +43,8 @@ function updateNodes() {
isDirty = false; isDirty = false;
nextTick(() => { nextTick(() => {
boundingRect.value = resizeListener.value?.getBoundingClientRect(); boundingRect.value = resizeListener.value?.getBoundingClientRect();
Object.values(nodes.value).forEach( (Object.values(nodes.value) as FeatureNode[]).forEach(
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion node => (node.rect = node.element.getBoundingClientRect())
node => (node!.rect = node?.element.getBoundingClientRect())
); );
isDirty = true; isDirty = true;
}); });

View file

@ -102,13 +102,10 @@ export default defineComponent({
min-width: 120px; min-width: 120px;
padding-left: 5px; padding-left: 5px;
padding-right: 5px; padding-right: 5px;
min-height: 75px;
background-color: var(--locked); background-color: var(--locked);
border-width: 4px; border-width: 4px;
border-radius: 5px; border-radius: 5px;
color: rgba(0, 0, 0, 0.5); color: rgba(0, 0, 0, 0.5);
margin-top: 0;
margin-bottom: 0;
} }
.milestone.done { .milestone.done {
@ -116,12 +113,12 @@ export default defineComponent({
cursor: default; cursor: default;
} }
.milestone >>> .equal-spaced { .milestone :deep(.equal-spaced) {
display: flex; display: flex;
justify-content: center; justify-content: center;
} }
.milestone >>> .equal-spaced > * { .milestone :deep(.equal-spaced > *) {
margin: auto; margin: auto;
} }
</style> </style>

View file

@ -35,7 +35,7 @@ import { Emitters } from "tsparticles-plugin-emitters/Emitters";
import { EmitterContainer } from "tsparticles-plugin-emitters/EmitterContainer"; import { EmitterContainer } from "tsparticles-plugin-emitters/EmitterContainer";
import { defineComponent, inject, nextTick, onMounted, PropType, ref } from "vue"; import { defineComponent, inject, nextTick, onMounted, PropType, ref } from "vue";
import { ParticlesComponent } from "particles.vue3"; import { ParticlesComponent } from "particles.vue3";
import { NodesInjectionKey } from "game/layers"; import { FeatureNode, NodesInjectionKey } from "game/layers";
// TODO get typing support on the Particles component // TODO get typing support on the Particles component
export default defineComponent({ export default defineComponent({
@ -93,9 +93,8 @@ export default defineComponent({
nextTick(() => { nextTick(() => {
if (resizeListener.value != null && props.onContainerResized) { if (resizeListener.value != null && props.onContainerResized) {
// TODO don't overlap with Links.vue // TODO don't overlap with Links.vue
Object.values(nodes.value).forEach( (Object.values(nodes.value) as FeatureNode[]).forEach(
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion node => (node.rect = node.element.getBoundingClientRect())
node => (node!.rect = node?.element.getBoundingClientRect())
); );
props.onContainerResized(resizeListener.value.getBoundingClientRect()); props.onContainerResized(resizeListener.value.getBoundingClientRect());
} }

View file

@ -12,7 +12,7 @@
...unref(classes) ...unref(classes)
}" }"
> >
<button <div
@click="click" @click="click"
@mousedown="start" @mousedown="start"
@mouseleave="stop" @mouseleave="stop"
@ -31,7 +31,7 @@
]" ]"
> >
<component :is="unref(comp)" /> <component :is="unref(comp)" />
</button> </div>
<MarkNode :mark="unref(mark)" /> <MarkNode :mark="unref(mark)" />
<Node :id="id" /> <Node :id="id" />
</Tooltip> </Tooltip>
@ -156,7 +156,7 @@ export default defineComponent({
margin: 0 10px 0 10px; margin: 0 10px 0 10px;
} }
.treeNode button { .treeNode > *:first-child {
width: 100%; width: 100%;
height: 100%; height: 100%;
border: 2px solid rgba(0, 0, 0, 0.125); border: 2px solid rgba(0, 0, 0, 0.125);
@ -166,6 +166,7 @@ export default defineComponent({
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.25); text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.25);
box-shadow: -4px -4px 4px rgba(0, 0, 0, 0.25) inset, 0px 0px 20px var(--background); box-shadow: -4px -4px 4px rgba(0, 0, 0, 0.25) inset, 0px 0px 20px var(--background);
text-transform: capitalize; text-transform: capitalize;
display: flex;
} }
.treeNode.small { .treeNode.small {
@ -173,7 +174,7 @@ export default defineComponent({
width: 60px; width: 60px;
} }
.treeNode.small button { .treeNode.small > *:first-child {
font-size: 30px; font-size: 30px;
} }

View file

@ -22,8 +22,8 @@ import { persistent, PersistentRef } from "./persistence";
import player from "./player"; import player from "./player";
export interface FeatureNode { export interface FeatureNode {
rect?: DOMRect; rect: DOMRect;
observer?: MutationObserver; observer: MutationObserver;
element: HTMLElement; element: HTMLElement;
} }

View file

@ -5,7 +5,8 @@ import {
Component as ComponentKey, Component as ComponentKey,
GatherProps, GatherProps,
GenericComponent, GenericComponent,
JSXFunction JSXFunction,
Visibility
} from "features/feature"; } from "features/feature";
import { import {
Component, Component,
@ -121,6 +122,21 @@ export function setupHoldToClick(
return { start, stop, handleHolding }; return { start, stop, handleHolding };
} }
export function getFirstFeature<T extends { visibility: ProcessedComputable<Visibility> }>(
features: T[],
filter: (feature: T) => boolean
): { firstFeature: Ref<T | undefined>; hiddenFeatures: Ref<T[]> } {
const filteredFeatures = computed(() =>
features.filter(
feature => unref(feature.visibility) === Visibility.Visible && filter(feature)
)
);
return {
firstFeature: computed(() => filteredFeatures.value[0]),
hiddenFeatures: computed(() => filteredFeatures.value.slice(1))
};
}
export function computeComponent( export function computeComponent(
component: Ref<ProcessedComputable<CoercableComponent>>, component: Ref<ProcessedComputable<CoercableComponent>>,
defaultWrapper = "div" defaultWrapper = "div"