Skip to content

Commit

Permalink
Merge branch 'main' into 409-add-eslint-stylistic
Browse files Browse the repository at this point in the history
  • Loading branch information
sorenjohanson committed Dec 3, 2024
2 parents 08bdb4c + 939f0f7 commit b1bec83
Show file tree
Hide file tree
Showing 10 changed files with 568 additions and 156 deletions.
243 changes: 152 additions & 91 deletions teammapper-backend/package-lock.json

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions teammapper-backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,13 @@
"@nestjs/schedule": "^4.1.1",
"@nestjs/serve-static": "^4.0.2",
"@nestjs/typeorm": "^10.0.2",
"@nestjs/websockets": "^10.4.1",
"@nestjs/websockets": "^10.4.12",
"@types/uuid": "^10.0.0",
"cache-manager": "^5.7.4",
"class-validator": "^0.14.1",
"dotenv": "16.4.5",
"http-proxy-middleware": "3.0.3",
"npm-check-updates": "^17.1.10",
"npm-check-updates": "^17.1.11",
"pg": "^8.13.1",
"pq": "^0.0.3",
"reflect-metadata": "^0.2.2",
Expand All @@ -70,10 +70,10 @@
"@types/express-serve-static-core": "^4.19.5",
"@types/jest": "29.5.14",
"@types/node": "^22.8.7",
"@types/supertest": "^2.0.16",
"@typescript-eslint/eslint-plugin": "^8.13.0",
"@types/supertest": "^6.0.2",
"@typescript-eslint/eslint-plugin": "^8.16.0",
"@typescript-eslint/parser": "^8.2.0",
"concurrently": "9.0.1",
"concurrently": "9.1.0",
"eslint": "^8.55.0",
"eslint-config-prettier": "^9.1.0",
"eslint-import-resolver-typescript": "3.6.1",
Expand All @@ -91,7 +91,7 @@
"ts-loader": "^9.5.1",
"ts-node": "^10.9.2",
"tsconfig-paths": "^4.2.0",
"typescript": "~5.5.4"
"typescript": "~5.7.2"
},
"overrides": {
"path-to-regexp": "1.9.0"
Expand Down
169 changes: 124 additions & 45 deletions teammapper-frontend/mmp/src/map/handlers/nodes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ import { v4 as uuidv4 } from 'uuid'
import { Event } from './events'
import Log from '../../utils/log'
import Utils from '../../utils/utils'
import { MapSnapshot } from './history'

const NODE_HORIZONTAL_SPACING = 200; // The x-axis spacing between parent and child nodes
const NODE_VERTICAL_SIBLING_OFFSET = 60; // The y-axis spacing between sibling nodes
const NODE_VERTICAL_SPACING = 120; // The initial vertical spacing for the first child node
/**
* Manage the nodes of the map.
*/
Expand Down Expand Up @@ -525,10 +529,22 @@ export default class Nodes {
* Return the orientation of a node in the map (true if left).
* @return {boolean}
*/
public getOrientation(node: Node): boolean {
if (!node.isRoot) {
return node.coordinates.x < this.getRoot().coordinates.x
public getOrientation(
node: Node | ExportNodeProperties,
rootNode?: Node | ExportNodeProperties
): boolean | undefined {
// isRoot exists only on Node type, so type guard is needed
if ('isRoot' in node && node.isRoot) {
return;
}

// For Node type, use this.getRoot() if rootNode not provided
const root = rootNode ?? (('isRoot' in node) ? this.getRoot() : undefined);
if (!root) {
return;
}

return (node.coordinates?.x ?? 0) < (root.coordinates?.x ?? 0);
}

/**
Expand Down Expand Up @@ -661,68 +677,131 @@ export default class Nodes {
}

/**
* Return the appropriate coordinates of the node.
* @param {Node} node
* @returns {Coordinates} coordinates
* Base method for calculating node coordinates.
* The reason this exists is so we can work with a JSON snapshot (as given by an import), but also allow saved, "real" nodes to calculate coordinates
* This prevents duplication, whilst passing methods that differ depending on whether or not a JSON snapshot or "real" node is calculating coordinates.
* @param node Either a node previously saved or one from a JSON snapshot
* @param params
* getParent - Parent node of given node
* getSiblings() - Method to get the siblings of the given node
* isRoot - If parent node is root
* getOrientation() - Method to get the orientation of the node
* @returns
*/
private calculateCoordinates(node: Node): Coordinates {
private calculateNodeCoordinates(
node: Node | ExportNodeProperties,
params: {
nodeParent: (Node | ExportNodeProperties) | null,
getSiblings: () => (Node | ExportNodeProperties)[],
isRoot: boolean,
getOrientation: (n: Node | ExportNodeProperties) => boolean | undefined
}
): Coordinates {
const nodeParent = params.nodeParent;

let coordinates: Coordinates = {
x: node.parent ? node.parent.coordinates.x : node.coordinates.x || 0,
y: node.parent ? node.parent.coordinates.y : node.coordinates.y || 0
},
siblings: Array<Node> = this.getSiblings(node)

if (node.parent && node.parent.isRoot) {
const rightNodes: Array<Node> = [],
leftNodes: Array<Node> = []

for (const sibling of siblings) {
this.getOrientation(sibling) ? leftNodes.push(sibling) : rightNodes.push(sibling)
}

x: nodeParent?.coordinates?.x ?? (node.coordinates?.x ?? 0),
y: nodeParent?.coordinates?.y ?? (node.coordinates?.y ?? 0)
};

let siblings = params.getSiblings();

if (nodeParent && params.isRoot) {
// This will go through sibling nodes and assign them to the left or to the right depending on the orientation of the sibling node
const [leftNodes, rightNodes] = siblings.reduce<[(Node | ExportNodeProperties)[], (Node | ExportNodeProperties)[]]>(
(acc, sibling) => {
params.getOrientation(sibling)
? acc[0].push(sibling)
: acc[1].push(sibling);
return acc;
},
[[], []]
);

if (leftNodes.length <= rightNodes.length) {
coordinates.x -= 200
siblings = leftNodes
coordinates.x -= NODE_HORIZONTAL_SPACING;
siblings = leftNodes;
} else {
coordinates.x += 200
siblings = rightNodes
coordinates.x += NODE_HORIZONTAL_SPACING;
siblings = rightNodes;
}
} else if (!node.detached) {
if (node.parent && this.getOrientation(node.parent)) {
coordinates.x -= 200
if (nodeParent && params.getOrientation(nodeParent)) {
coordinates.x -= NODE_HORIZONTAL_SPACING;
} else {
coordinates.x += 200
coordinates.x += NODE_HORIZONTAL_SPACING;
}
}

if (siblings.length > 0) {
const lowerNode = this.getLowerNode(siblings)
coordinates.y = lowerNode.coordinates.y + 60
coordinates.y = (lowerNode?.coordinates?.y ?? 0) + NODE_VERTICAL_SIBLING_OFFSET;
} else if (!node.detached) {
coordinates.y -= 120
coordinates.y -= NODE_VERTICAL_SPACING;
}

return coordinates;
}

return coordinates
/**
* Existing method to calculate the coordinates of "real", saved nodes in the database.
* This method will pass on existing methods such as this.getSiblings() to calculateNodeCoordinates, so existing implementations don't break
* @param node
* @returns
*/
private calculateCoordinates(node: Node): Coordinates {
return this.calculateNodeCoordinates(
node,
{
nodeParent: node.parent,
getSiblings: () => this.getSiblings(node),
isRoot: node.parent?.isRoot ?? false,
getOrientation: (n: Node) => this.getOrientation(n)
}
);
}

public applyCoordinatesToMapSnapshot = (mapSnapshot: MapSnapshot): MapSnapshot => {
const rootNode = mapSnapshot.find(x => x.isRoot);

return mapSnapshot.map(node => {
if (!node.coordinates) {
/**
* Since we're working with a JSON snapshot here, none of the nodes actually exist.
* This makes existing methods such as this.getSiblings() useless, because they only work with existing nodes.
* So here, we pass on methods that work directly with the JSON.
*/
node.coordinates = this.calculateNodeCoordinates(
node,
{
nodeParent: node.parent ? mapSnapshot.find(x => x.id === node.parent) : null,
getSiblings: () => node.parent ? mapSnapshot.filter(x => x.parent === node.parent && x.id !== node.id) : [],
isRoot: !!node.parent && mapSnapshot.find(x => x.id === node.parent)?.isRoot,
getOrientation: (n: ExportNodeProperties) => this.getOrientation(n, rootNode)
}
);
}

return node
});
}

/**
* Return the lower node of a list of nodes.
* @param {Node[]} nodes
* @returns {Node} lowerNode
*/
private getLowerNode(nodes: Node[] = Array.from(this.nodes.values())): Node {
if (nodes.length > 0) {
let tmp = nodes[0].coordinates.y, lowerNode = nodes[0]

for (const node of nodes) {
if (node.coordinates.y > tmp) {
tmp = node.coordinates.y
lowerNode = node
}
}

return lowerNode
private getLowerNode(nodes: (Node | ExportNodeProperties)[]): Node | ExportNodeProperties | undefined {
if (nodes.length === 0) {
return;
}

return nodes.reduce((lowest, current) => {
const lowestY = lowest.coordinates?.y ?? 0;
const currentY = current.coordinates?.y ?? 0;

return currentY > lowestY ? current : lowest;
}, nodes[0]);
}

/**
Expand Down Expand Up @@ -1081,8 +1160,8 @@ export default class Nodes {
* @param {boolean} direction
*/
private moveSelectionOnBranch(direction: boolean) {
if ((this.getOrientation(this.selectedNode) === false && direction) ||
(this.getOrientation(this.selectedNode) === true && !direction)) {
if ((!this.getOrientation(this.selectedNode) && direction) ||
(this.getOrientation(this.selectedNode) && !direction)) {
this.selectNode(this.selectedNode.parent.id)
} else {
let children = this.getChildren(this.selectedNode)
Expand Down
5 changes: 4 additions & 1 deletion teammapper-frontend/mmp/src/map/map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ import Events from './handlers/events'
import Zoom from './handlers/zoom'
import Draw from './handlers/draw'
import Options, {OptionParameters} from './options'
import History from './handlers/history'
import History, { MapSnapshot } from './handlers/history'
import Drag from './handlers/drag'
import Nodes from './handlers/nodes'
import Export from './handlers/export'
import CopyPaste from './handlers/copy-paste'
import { UserNodeProperties } from './types'

/**
* Initialize all handlers and return a mmp object.
Expand Down Expand Up @@ -91,6 +92,7 @@ export default class MmpMap {
center: this.zoom.center,
copyNode: this.copyPaste.copy,
cutNode: this.copyPaste.cut,
applyCoordinatesToMapSnapshot: this.nodes.applyCoordinatesToMapSnapshot,
deselectNode: this.nodes.deselectNode,
getSelectedNode: this.nodes.getSelectedNode,
editNode: this.nodes.editNode,
Expand Down Expand Up @@ -129,6 +131,7 @@ export interface MmpInstance {
center: Function
copyNode: Function
cutNode: Function
applyCoordinatesToMapSnapshot: Function
deselectNode: Function
getSelectedNode: Function
editNode: Function
Expand Down
21 changes: 12 additions & 9 deletions teammapper-frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions teammapper-frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
"@fortawesome/angular-fontawesome": "^0.15.0",
"@fortawesome/fontawesome-svg-core": "^1.2.27",
"@fortawesome/free-brands-svg-icons": "^6.6.0",
"@fortawesome/free-solid-svg-icons": "^6.5.2",
"@fortawesome/free-solid-svg-icons": "^6.7.1",
"@material-design-icons/font": "^0.13.0",
"@ngx-translate/core": "^16.0.3",
"@ngx-translate/http-loader": "^7.0.0",
Expand Down Expand Up @@ -100,7 +100,7 @@
"eslint-plugin-prettier": "^4.2.1",
"jest": "^29.7.0",
"jest-canvas-mock": "^2.5.2",
"jest-preset-angular": "^14.2.4",
"jest-preset-angular": "^14.4.1",
"minimist": "^1.2.5",
"prettier": "2.8.7",
"ts-node": "~10.9.2",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,7 @@ export class MapSyncService implements OnDestroy {
};
}

return undefined;
return;
};

const { added, updated, deleted } = result.diff;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export const mockMmpService = {
new: jest.fn(),
addNodeImage: jest.fn(),
create: jest.fn(),
map: {
on: jest.fn(),
remove: jest.fn(),
Expand Down
Loading

0 comments on commit b1bec83

Please sign in to comment.