Fixed board feature

This commit is contained in:
thepaperpilot 2022-09-07 20:01:05 -05:00 committed by Replit user
parent 7b1dd890fd
commit 919a3cd726
3 changed files with 161 additions and 145 deletions

View file

@ -15,8 +15,12 @@
"@fontsource/material-icons": "^4.5.4",
"@fontsource/roboto-mono": "^4.5.8",
"@pixi/app": "~6.3.2",
"@pixi/constants": "~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",
"@vitejs/plugin-vue": "^2.3.3",
"@vitejs/plugin-vue-jsx": "^1.3.10",
@ -28,7 +32,7 @@
"vite-tsconfig-paths": "^3.5.0",
"vue": "^3.2.26",
"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-toastification": "^2.0.0-rc.1",
"vue-transition-expand": "^0.1.0",

View file

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

View file

@ -21,9 +21,12 @@ import type {
} from "util/computed";
import { processComputable } from "util/computed";
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";
globalBus.on("setupVue", app => panZoom.install(app));
export const BoardType = Symbol("Board");
export type NodeComputable<T> = Computable<T> | ((node: BoardNode) => T);
@ -166,12 +169,14 @@ export interface BoardOptions {
types: Record<string, NodeTypeOptions>;
}
export interface BaseBoard extends Persistent<BoardData> {
export interface BaseBoard {
id: string;
state: Persistent<BoardData>;
links: Ref<BoardNodeLink[] | null>;
nodes: Ref<BoardNode[]>;
selectedNode: Ref<BoardNode | null>;
selectedAction: Ref<GenericBoardNodeAction | null>;
mousePosition: Ref<{ x: number; y: number } | null>;
type: typeof BoardType;
[Component]: typeof BoardComponent;
[GatherProps]: () => Record<string, unknown>;
@ -199,140 +204,138 @@ export type GenericBoard = Replace<
export function createBoard<T extends BoardOptions>(
optionsFunc: OptionsFunc<T, BaseBoard, GenericBoard>
): Board<T> {
return createLazyProxy(
persistent => {
const board = Object.assign(persistent, optionsFunc());
board.id = getUniqueID("board-");
board.type = BoardType;
board[Component] = BoardComponent;
return createLazyProxy(() => {
const board = optionsFunc();
board.id = getUniqueID("board-");
board.type = BoardType;
board[Component] = BoardComponent;
board.nodes = computed(() => processedBoard[PersistentState].value.nodes);
board.selectedNode = computed(
() =>
processedBoard.nodes.value.find(
node => node.id === board[PersistentState].value.selectedNode
) || null
);
board.selectedAction = computed(() => {
const selectedNode = processedBoard.selectedNode.value;
if (selectedNode == null) {
return null;
}
const type = processedBoard.types[selectedNode.type];
if (type.actions == null) {
return null;
}
return (
type.actions.find(
action => action.id === processedBoard[PersistentState].value.selectedAction
) || null
);
});
board.links = computed(() => {
if (processedBoard.selectedAction.value == null) {
return null;
}
if (
processedBoard.selectedAction.value.links &&
processedBoard.selectedNode.value
) {
return getNodeProperty(
processedBoard.selectedAction.value.links,
processedBoard.selectedNode.value
);
}
return null;
});
processComputable(board as T, "visibility");
setDefault(board, "visibility", Visibility.Visible);
processComputable(board as T, "width");
setDefault(board, "width", "100%");
processComputable(board as T, "height");
setDefault(board, "height", "400px");
processComputable(board as T, "classes");
processComputable(board as T, "style");
for (const type in board.types) {
const nodeType: NodeTypeOptions & Partial<BaseNodeType> = board.types[type];
processComputable(nodeType as NodeTypeOptions, "title");
processComputable(nodeType as NodeTypeOptions, "label");
processComputable(nodeType as NodeTypeOptions, "size");
setDefault(nodeType, "size", 50);
processComputable(nodeType as NodeTypeOptions, "draggable");
setDefault(nodeType, "draggable", false);
processComputable(nodeType as NodeTypeOptions, "shape");
setDefault(nodeType, "shape", Shape.Circle);
processComputable(nodeType as NodeTypeOptions, "canAccept");
setDefault(nodeType, "canAccept", false);
processComputable(nodeType as NodeTypeOptions, "progress");
processComputable(nodeType as NodeTypeOptions, "progressDisplay");
setDefault(nodeType, "progressDisplay", ProgressDisplay.Fill);
processComputable(nodeType as NodeTypeOptions, "progressColor");
setDefault(nodeType, "progressColor", "none");
processComputable(nodeType as NodeTypeOptions, "fillColor");
processComputable(nodeType as NodeTypeOptions, "outlineColor");
processComputable(nodeType as NodeTypeOptions, "titleColor");
processComputable(nodeType as NodeTypeOptions, "actionDistance");
setDefault(nodeType, "actionDistance", Math.PI / 6);
nodeType.nodes = computed(() =>
board[PersistentState].value.nodes.filter(node => node.type === type)
);
setDefault(nodeType, "onClick", function (node: BoardNode) {
board[PersistentState].value.selectedNode = node.id;
});
if (nodeType.actions) {
for (const action of nodeType.actions) {
processComputable(action, "visibility");
setDefault(action, "visibility", Visibility.Visible);
processComputable(action, "icon");
processComputable(action, "fillColor");
processComputable(action, "tooltip");
processComputable(action, "links");
}
}
}
board[GatherProps] = function (this: GenericBoard) {
const {
nodes,
types,
[PersistentState]: state,
visibility,
width,
height,
style,
classes,
links,
selectedAction,
selectedNode
} = this;
return {
nodes,
types,
[PersistentState]: state,
visibility,
width,
height,
style: unref(style),
classes,
links,
selectedAction,
selectedNode
};
};
// This is necessary because board.types is different from T and Board
const processedBoard = board as unknown as Board<T>;
return processedBoard;
},
persistent<BoardData>({
board.state = persistent<BoardData>({
nodes: [],
selectedNode: null,
selectedAction: null
})
);
});
board.nodes = computed(() => processedBoard.state.value.nodes);
board.selectedNode = computed(
() =>
processedBoard.nodes.value.find(
node => node.id === processedBoard.state.value.selectedNode
) || null
);
board.selectedAction = computed(() => {
const selectedNode = processedBoard.selectedNode.value;
if (selectedNode == null) {
return null;
}
const type = processedBoard.types[selectedNode.type];
if (type.actions == null) {
return null;
}
return (
type.actions.find(
action => action.id === processedBoard.state.value.selectedAction
) || null
);
});
board.mousePosition = ref(null);
board.links = computed(() => {
if (processedBoard.selectedAction.value == null) {
return null;
}
if (processedBoard.selectedAction.value.links && processedBoard.selectedNode.value) {
return getNodeProperty(
processedBoard.selectedAction.value.links,
processedBoard.selectedNode.value
);
}
return null;
});
processComputable(board as T, "visibility");
setDefault(board, "visibility", Visibility.Visible);
processComputable(board as T, "width");
setDefault(board, "width", "100%");
processComputable(board as T, "height");
setDefault(board, "height", "400px");
processComputable(board as T, "classes");
processComputable(board as T, "style");
for (const type in board.types) {
const nodeType: NodeTypeOptions & Partial<BaseNodeType> = board.types[type];
processComputable(nodeType as NodeTypeOptions, "title");
processComputable(nodeType as NodeTypeOptions, "label");
processComputable(nodeType as NodeTypeOptions, "size");
setDefault(nodeType, "size", 50);
processComputable(nodeType as NodeTypeOptions, "draggable");
setDefault(nodeType, "draggable", false);
processComputable(nodeType as NodeTypeOptions, "shape");
setDefault(nodeType, "shape", Shape.Circle);
processComputable(nodeType as NodeTypeOptions, "canAccept");
setDefault(nodeType, "canAccept", false);
processComputable(nodeType as NodeTypeOptions, "progress");
processComputable(nodeType as NodeTypeOptions, "progressDisplay");
setDefault(nodeType, "progressDisplay", ProgressDisplay.Fill);
processComputable(nodeType as NodeTypeOptions, "progressColor");
setDefault(nodeType, "progressColor", "none");
processComputable(nodeType as NodeTypeOptions, "fillColor");
processComputable(nodeType as NodeTypeOptions, "outlineColor");
processComputable(nodeType as NodeTypeOptions, "titleColor");
processComputable(nodeType as NodeTypeOptions, "actionDistance");
setDefault(nodeType, "actionDistance", Math.PI / 6);
nodeType.nodes = computed(() =>
processedBoard.state.value.nodes.filter(node => node.type === type)
);
setDefault(nodeType, "onClick", function (node: BoardNode) {
processedBoard.state.value.selectedNode = node.id;
});
if (nodeType.actions) {
for (const action of nodeType.actions) {
processComputable(action, "visibility");
setDefault(action, "visibility", Visibility.Visible);
processComputable(action, "icon");
processComputable(action, "fillColor");
processComputable(action, "tooltip");
processComputable(action, "links");
}
}
}
board[GatherProps] = function (this: GenericBoard) {
const {
nodes,
types,
state,
visibility,
width,
height,
style,
classes,
links,
selectedAction,
selectedNode,
mousePosition
} = this;
return {
nodes,
types,
state,
visibility,
width,
height,
style: unref(style),
classes,
links,
selectedAction,
selectedNode,
mousePosition
};
};
// This is necessary because board.types is different from T and Board
const processedBoard = board as unknown as Board<T>;
return processedBoard;
});
}
export function getNodeProperty<T>(property: NodeComputable<T>, node: BoardNode): T {