From a59f77aa6db39b9b61baa5a5b9c7396025ba5fa7 Mon Sep 17 00:00:00 2001
From: thepaperpilot <thepaperpilot@gmail.com>
Date: Sat, 22 Apr 2023 19:21:16 -0500
Subject: [PATCH] Support classes and styles to be defined by node types

---
 src/features/boards/BoardNode.vue | 9 ++++++---
 src/features/boards/board.ts      | 8 ++++++++
 2 files changed, 14 insertions(+), 3 deletions(-)

diff --git a/src/features/boards/BoardNode.vue b/src/features/boards/BoardNode.vue
index 47ca3fe..a2300b1 100644
--- a/src/features/boards/BoardNode.vue
+++ b/src/features/boards/BoardNode.vue
@@ -1,8 +1,9 @@
 <template>
+    <!-- Ugly casting to prevent TS compiler error about style because vue doesn't think it supports arrays when it does -->
     <g
         class="boardnode"
-        :class="{ [node.type]: true, isSelected, isDraggable }"
-        :style="{ opacity: dragging?.id === node.id && hasDragged ? 0.5 : 1 }"
+        :class="{ [node.type]: true, isSelected, isDraggable, ...classes }"
+        :style="[{ opacity: dragging?.id === node.id && hasDragged ? 0.5 : 1 }, style ?? []] as unknown as (string | CSSProperties)"
         :transform="`translate(${position.x},${position.y})${isSelected ? ' scale(1.2)' : ''}`"
     >
         <BoardNodeAction
@@ -141,7 +142,7 @@ import type { BoardNode, GenericBoardNodeAction, GenericNodeType } from "feature
 import { ProgressDisplay, Shape, getNodeProperty } from "features/boards/board";
 import { isVisible } from "features/feature";
 import settings from "game/settings";
-import { computed, toRefs, unref, watch } from "vue";
+import { CSSProperties, computed, toRefs, unref, watch } from "vue";
 import BoardNodeAction from "./BoardNodeAction.vue";
 
 const sqrtTwo = Math.sqrt(2);
@@ -244,6 +245,8 @@ const canAccept = computed(
         unref(props.hasDragged) &&
         getNodeProperty(props.nodeType.value.canAccept, unref(props.node))
 );
+const style = computed(() => getNodeProperty(props.nodeType.value.style, unref(props.node)));
+const classes = computed(() => getNodeProperty(props.nodeType.value.classes, unref(props.node)));
 
 function mouseDown(e: MouseEvent | TouchEvent) {
     emit("mouseDown", e, props.node.value.id, isDraggable.value);
diff --git a/src/features/boards/board.ts b/src/features/boards/board.ts
index 961d517..a86b5ae 100644
--- a/src/features/boards/board.ts
+++ b/src/features/boards/board.ts
@@ -92,6 +92,10 @@ export interface NodeTypeOptions {
     label?: NodeComputable<NodeLabel | null>;
     /** The size of the node - diameter for circles, width and height for squares. */
     size: NodeComputable<number>;
+    /** CSS to apply to this node. */
+    style?: NodeComputable<StyleValue>;
+    /** Dictionary of CSS classes to apply to this node. */
+    classes?: NodeComputable<Record<string, boolean>>;
     /** Whether the node is draggable or not. */
     draggable?: NodeComputable<boolean>;
     /** The shape of the node. */
@@ -137,6 +141,8 @@ export type NodeType<T extends NodeTypeOptions> = Replace<
         title: GetComputableType<T["title"]>;
         label: GetComputableType<T["label"]>;
         size: GetComputableTypeWithDefault<T["size"], 50>;
+        style: GetComputableType<T["style"]>;
+        classes: GetComputableType<T["classes"]>;
         draggable: GetComputableTypeWithDefault<T["draggable"], false>;
         shape: GetComputableTypeWithDefault<T["shape"], Shape.Circle>;
         canAccept: GetComputableTypeWithDefault<T["canAccept"], false>;
@@ -378,6 +384,8 @@ export function createBoard<T extends BoardOptions>(
             processComputable(nodeType as NodeTypeOptions, "label");
             processComputable(nodeType as NodeTypeOptions, "size");
             setDefault(nodeType, "size", 50);
+            processComputable(nodeType as NodeTypeOptions, "style");
+            processComputable(nodeType as NodeTypeOptions, "classes");
             processComputable(nodeType as NodeTypeOptions, "draggable");
             setDefault(nodeType, "draggable", false);
             processComputable(nodeType as NodeTypeOptions, "shape");