Fixed board feature

This commit is contained in:
thepaperpilot 2022-09-07 20:01:05 -05:00
parent 066afc5dfe
commit 6edf16515b
3 changed files with 161 additions and 145 deletions

View file

@ -15,8 +15,12 @@
"@fontsource/material-icons": "^4.5.4", "@fontsource/material-icons": "^4.5.4",
"@fontsource/roboto-mono": "^4.5.8", "@fontsource/roboto-mono": "^4.5.8",
"@pixi/app": "~6.3.2", "@pixi/app": "~6.3.2",
"@pixi/constants": "~6.3.2",
"@pixi/core": "~6.3.2", "@pixi/core": "~6.3.2",
"@pixi/particle-emitter": "^5.0.4", "@pixi/display": "~6.3.2",
"@pixi/math": "~6.3.2",
"@pixi/particle-emitter": "^5.0.7",
"@pixi/sprite": "~6.3.2",
"@pixi/ticker": "~6.3.2", "@pixi/ticker": "~6.3.2",
"@vitejs/plugin-vue": "^2.3.3", "@vitejs/plugin-vue": "^2.3.3",
"@vitejs/plugin-vue-jsx": "^1.3.10", "@vitejs/plugin-vue-jsx": "^1.3.10",
@ -31,7 +35,7 @@
"vite-tsconfig-paths": "^3.5.0", "vite-tsconfig-paths": "^3.5.0",
"vue": "^3.2.26", "vue": "^3.2.26",
"vue-next-select": "^2.10.2", "vue-next-select": "^2.10.2",
"vue-panzoom": "^1.1.6", "vue-panzoom": "https://github.com/thepaperpilot/vue-panzoom.git",
"vue-textarea-autosize": "^1.1.1", "vue-textarea-autosize": "^1.1.1",
"vue-toastification": "^2.0.0-rc.1", "vue-toastification": "^2.0.0-rc.1",
"vue-transition-expand": "^0.1.0", "vue-transition-expand": "^0.1.0",

View file

@ -61,17 +61,15 @@ import type {
import { getNodeProperty } from "features/boards/board"; import { getNodeProperty } from "features/boards/board";
import type { StyleValue } from "features/feature"; import type { StyleValue } from "features/feature";
import { Visibility } from "features/feature"; import { Visibility } from "features/feature";
import { PersistentState } from "game/persistence";
import type { ProcessedComputable } from "util/computed"; import type { ProcessedComputable } from "util/computed";
import { computed, ref, Ref, toRefs, unref } from "vue"; import { computed, ref, Ref, toRefs, unref } from "vue";
import panZoom from "vue-panzoom";
import BoardLinkVue from "./BoardLink.vue"; import BoardLinkVue from "./BoardLink.vue";
import BoardNodeVue from "./BoardNode.vue"; import BoardNodeVue from "./BoardNode.vue";
const _props = defineProps<{ const _props = defineProps<{
nodes: Ref<BoardNode[]>; nodes: Ref<BoardNode[]>;
types: Record<string, GenericNodeType>; types: Record<string, GenericNodeType>;
[PersistentState]: Ref<BoardData>; state: Ref<BoardData>;
visibility: ProcessedComputable<Visibility>; visibility: ProcessedComputable<Visibility>;
width?: ProcessedComputable<string>; width?: ProcessedComputable<string>;
height?: ProcessedComputable<string>; height?: ProcessedComputable<string>;
@ -80,6 +78,7 @@ const _props = defineProps<{
links: Ref<BoardNodeLink[] | null>; links: Ref<BoardNodeLink[] | null>;
selectedAction: Ref<GenericBoardNodeAction | null>; selectedAction: Ref<GenericBoardNodeAction | null>;
selectedNode: Ref<BoardNode | null>; selectedNode: Ref<BoardNode | null>;
mousePosition: Ref<{ x: number; y: number } | null>;
}>(); }>();
const props = toRefs(_props); const props = toRefs(_props);
@ -170,13 +169,13 @@ function mouseDown(e: MouseEvent | TouchEvent, nodeID: number | null = null, dra
} }
} }
if (nodeID != null) { if (nodeID != null) {
props[PersistentState].value.selectedNode = null; props.state.value.selectedNode = null;
props[PersistentState].value.selectedAction = null; props.state.value.selectedAction = null;
} }
} }
function drag(e: MouseEvent | TouchEvent) { function drag(e: MouseEvent | TouchEvent) {
const zoom = stage.value.$panZoomInstance.getTransform().scale; const { x, y, scale } = stage.value.panZoomInstance.getTransform();
let clientX, clientY; let clientX, clientY;
if ("touches" in e) { if ("touches" in e) {
@ -185,6 +184,7 @@ function drag(e: MouseEvent | TouchEvent) {
clientY = e.touches[0].clientY; clientY = e.touches[0].clientY;
} else { } else {
endDragging(dragging.value); endDragging(dragging.value);
props.mousePosition.value = null;
return; return;
} }
} else { } else {
@ -192,9 +192,14 @@ function drag(e: MouseEvent | TouchEvent) {
clientY = e.clientY; clientY = e.clientY;
} }
props.mousePosition.value = {
x: (clientX - x) / scale,
y: (clientY - y) / scale
};
dragged.value = { dragged.value = {
x: dragged.value.x + (clientX - lastMousePosition.value.x) / zoom, x: dragged.value.x + (clientX - lastMousePosition.value.x) / scale,
y: dragged.value.y + (clientY - lastMousePosition.value.y) / zoom y: dragged.value.y + (clientY - lastMousePosition.value.y) / scale
}; };
lastMousePosition.value = { lastMousePosition.value = {
x: clientX, x: clientX,
@ -229,8 +234,8 @@ function endDragging(nodeID: number | null) {
dragging.value = null; dragging.value = null;
} else if (!hasDragged.value) { } else if (!hasDragged.value) {
props[PersistentState].value.selectedNode = null; props.state.value.selectedNode = null;
props[PersistentState].value.selectedAction = null; props.state.value.selectedAction = null;
} }
} }
</script> </script>
@ -239,7 +244,11 @@ function endDragging(nodeID: number | null) {
.vue-pan-zoom-scene { .vue-pan-zoom-scene {
width: 100%; width: 100%;
height: 100%; height: 100%;
cursor: move; cursor: grab;
}
.vue-pan-zoom-scene:active {
cursor: grabbing;
} }
.g1 { .g1 {

View file

@ -21,9 +21,12 @@ import type {
} from "util/computed"; } from "util/computed";
import { processComputable } from "util/computed"; import { processComputable } from "util/computed";
import { createLazyProxy } from "util/proxies"; import { createLazyProxy } from "util/proxies";
import { computed, Ref, unref } from "vue"; import { computed, ref, Ref, unref } from "vue";
import panZoom from "vue-panzoom";
import type { Link } from "../links/links"; import type { Link } from "../links/links";
globalBus.on("setupVue", app => panZoom.install(app));
export const BoardType = Symbol("Board"); export const BoardType = Symbol("Board");
export type NodeComputable<T> = Computable<T> | ((node: BoardNode) => T); export type NodeComputable<T> = Computable<T> | ((node: BoardNode) => T);
@ -166,12 +169,14 @@ export interface BoardOptions {
types: Record<string, NodeTypeOptions>; types: Record<string, NodeTypeOptions>;
} }
export interface BaseBoard extends Persistent<BoardData> { export interface BaseBoard {
id: string; id: string;
state: Persistent<BoardData>;
links: Ref<BoardNodeLink[] | null>; links: Ref<BoardNodeLink[] | null>;
nodes: Ref<BoardNode[]>; nodes: Ref<BoardNode[]>;
selectedNode: Ref<BoardNode | null>; selectedNode: Ref<BoardNode | null>;
selectedAction: Ref<GenericBoardNodeAction | null>; selectedAction: Ref<GenericBoardNodeAction | null>;
mousePosition: Ref<{ x: number; y: number } | null>;
type: typeof BoardType; type: typeof BoardType;
[Component]: typeof BoardComponent; [Component]: typeof BoardComponent;
[GatherProps]: () => Record<string, unknown>; [GatherProps]: () => Record<string, unknown>;
@ -199,18 +204,22 @@ export type GenericBoard = Replace<
export function createBoard<T extends BoardOptions>( export function createBoard<T extends BoardOptions>(
optionsFunc: OptionsFunc<T, BaseBoard, GenericBoard> optionsFunc: OptionsFunc<T, BaseBoard, GenericBoard>
): Board<T> { ): Board<T> {
return createLazyProxy( return createLazyProxy(() => {
persistent => { const board = optionsFunc();
const board = Object.assign(persistent, optionsFunc());
board.id = getUniqueID("board-"); board.id = getUniqueID("board-");
board.type = BoardType; board.type = BoardType;
board[Component] = BoardComponent; board[Component] = BoardComponent;
board.nodes = computed(() => processedBoard[PersistentState].value.nodes); board.state = persistent<BoardData>({
nodes: [],
selectedNode: null,
selectedAction: null
});
board.nodes = computed(() => processedBoard.state.value.nodes);
board.selectedNode = computed( board.selectedNode = computed(
() => () =>
processedBoard.nodes.value.find( processedBoard.nodes.value.find(
node => node.id === board[PersistentState].value.selectedNode node => node.id === processedBoard.state.value.selectedNode
) || null ) || null
); );
board.selectedAction = computed(() => { board.selectedAction = computed(() => {
@ -224,18 +233,16 @@ export function createBoard<T extends BoardOptions>(
} }
return ( return (
type.actions.find( type.actions.find(
action => action.id === processedBoard[PersistentState].value.selectedAction action => action.id === processedBoard.state.value.selectedAction
) || null ) || null
); );
}); });
board.mousePosition = ref(null);
board.links = computed(() => { board.links = computed(() => {
if (processedBoard.selectedAction.value == null) { if (processedBoard.selectedAction.value == null) {
return null; return null;
} }
if ( if (processedBoard.selectedAction.value.links && processedBoard.selectedNode.value) {
processedBoard.selectedAction.value.links &&
processedBoard.selectedNode.value
) {
return getNodeProperty( return getNodeProperty(
processedBoard.selectedAction.value.links, processedBoard.selectedAction.value.links,
processedBoard.selectedNode.value processedBoard.selectedNode.value
@ -276,10 +283,10 @@ export function createBoard<T extends BoardOptions>(
processComputable(nodeType as NodeTypeOptions, "actionDistance"); processComputable(nodeType as NodeTypeOptions, "actionDistance");
setDefault(nodeType, "actionDistance", Math.PI / 6); setDefault(nodeType, "actionDistance", Math.PI / 6);
nodeType.nodes = computed(() => nodeType.nodes = computed(() =>
board[PersistentState].value.nodes.filter(node => node.type === type) processedBoard.state.value.nodes.filter(node => node.type === type)
); );
setDefault(nodeType, "onClick", function (node: BoardNode) { setDefault(nodeType, "onClick", function (node: BoardNode) {
board[PersistentState].value.selectedNode = node.id; processedBoard.state.value.selectedNode = node.id;
}); });
if (nodeType.actions) { if (nodeType.actions) {
@ -298,7 +305,7 @@ export function createBoard<T extends BoardOptions>(
const { const {
nodes, nodes,
types, types,
[PersistentState]: state, state,
visibility, visibility,
width, width,
height, height,
@ -306,12 +313,13 @@ export function createBoard<T extends BoardOptions>(
classes, classes,
links, links,
selectedAction, selectedAction,
selectedNode selectedNode,
mousePosition
} = this; } = this;
return { return {
nodes, nodes,
types, types,
[PersistentState]: state, state,
visibility, visibility,
width, width,
height, height,
@ -319,20 +327,15 @@ export function createBoard<T extends BoardOptions>(
classes, classes,
links, links,
selectedAction, selectedAction,
selectedNode selectedNode,
mousePosition
}; };
}; };
// This is necessary because board.types is different from T and Board // This is necessary because board.types is different from T and Board
const processedBoard = board as unknown as Board<T>; const processedBoard = board as unknown as Board<T>;
return processedBoard; return processedBoard;
}, });
persistent<BoardData>({
nodes: [],
selectedNode: null,
selectedAction: null
})
);
} }
export function getNodeProperty<T>(property: NodeComputable<T>, node: BoardNode): T { export function getNodeProperty<T>(property: NodeComputable<T>, node: BoardNode): T {