Implement chopping
This commit is contained in:
5
package-lock.json
generated
5
package-lock.json
generated
@@ -54,6 +54,7 @@
|
|||||||
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
|
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.27.1",
|
"@babel/code-frame": "^7.27.1",
|
||||||
"@babel/generator": "^7.28.5",
|
"@babel/generator": "^7.28.5",
|
||||||
@@ -515,6 +516,7 @@
|
|||||||
"integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==",
|
"integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/prop-types": "*",
|
"@types/prop-types": "*",
|
||||||
"csstype": "^3.2.2"
|
"csstype": "^3.2.2"
|
||||||
@@ -582,6 +584,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"baseline-browser-mapping": "^2.9.0",
|
"baseline-browser-mapping": "^2.9.0",
|
||||||
"caniuse-lite": "^1.0.30001759",
|
"caniuse-lite": "^1.0.30001759",
|
||||||
@@ -1370,6 +1373,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
|
||||||
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
|
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"loose-envify": "^1.1.0"
|
"loose-envify": "^1.1.0"
|
||||||
},
|
},
|
||||||
@@ -1617,6 +1621,7 @@
|
|||||||
"integrity": "sha512-sDIpIcl3mv1NUaSzZwiXGEy1ZoWwwC2vkxUHY6yiDacR6zf//ZFuBJrozO62gedpE43pmxnLATNR5IYUdAEkMQ==",
|
"integrity": "sha512-sDIpIcl3mv1NUaSzZwiXGEy1ZoWwwC2vkxUHY6yiDacR6zf//ZFuBJrozO62gedpE43pmxnLATNR5IYUdAEkMQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.14.47",
|
"esbuild": "^0.14.47",
|
||||||
"postcss": "^8.4.14",
|
"postcss": "^8.4.14",
|
||||||
|
|||||||
21
src/index.ts
21
src/index.ts
@@ -1,6 +1,7 @@
|
|||||||
import { StickyNote, Image } from "@mirohq/websdk-types";
|
import { StickyNote, Image } from "@mirohq/websdk-types";
|
||||||
|
import { getStickiesToChopInTree } from "./todo-tree-helper";
|
||||||
|
|
||||||
export async function init() {
|
export const init = async () => {
|
||||||
miro.board.ui.on('icon:click', async () => {
|
miro.board.ui.on('icon:click', async () => {
|
||||||
await miro.board.ui.openPanel({ url: 'app.html' });
|
await miro.board.ui.openPanel({ url: 'app.html' });
|
||||||
});
|
});
|
||||||
@@ -42,19 +43,18 @@ const playAxeAnimation = async ({axe, stickyNote, onChopped}: AxeAnimationArgs)
|
|||||||
for (let i = 0; i < 3; i++) {
|
for (let i = 0; i < 3; i++) {
|
||||||
axe.rotation = 90;
|
axe.rotation = 90;
|
||||||
await axe.sync();
|
await axe.sync();
|
||||||
await new Promise(resolve => setTimeout(resolve, 200));
|
await new Promise(resolve => setTimeout(resolve, 300));
|
||||||
|
|
||||||
axe.rotation = 0;
|
axe.rotation = 0;
|
||||||
await axe.sync();
|
await axe.sync();
|
||||||
await new Promise(resolve => setTimeout(resolve, 200));
|
await new Promise(resolve => setTimeout(resolve, 300));
|
||||||
}
|
}
|
||||||
|
|
||||||
onChopped();
|
onChopped();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function postInfoNotification(message: string) {
|
const postInfoNotification = async (message: string) =>
|
||||||
await miro.board.notifications.showInfo(message);
|
await miro.board.notifications.showInfo(message);
|
||||||
}
|
|
||||||
|
|
||||||
const handleChoppingAction = async ({items}: {items:StickyNote[]}) => {
|
const handleChoppingAction = async ({items}: {items:StickyNote[]}) => {
|
||||||
const axe = await findExistingAxe();
|
const axe = await findExistingAxe();
|
||||||
@@ -73,15 +73,22 @@ const handleChoppingAction = async ({items}: {items:StickyNote[]}) => {
|
|||||||
playAxeAnimation({
|
playAxeAnimation({
|
||||||
axe,
|
axe,
|
||||||
stickyNote: target,
|
stickyNote: target,
|
||||||
onChopped: async () => await miro.board.remove(target)
|
onChopped: () => removeTree(target)
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const removeTree = async (root: StickyNote) => {
|
||||||
|
const stickiesToRemove = await getStickiesToChopInTree(root, ["light_green", "red"]);
|
||||||
|
|
||||||
|
for (const item of stickiesToRemove) {
|
||||||
|
await miro.board.remove(item);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const findExistingAxe = async () => {
|
export const findExistingAxe = async () => {
|
||||||
const items = await miro.board.get({ type: 'image' });
|
const items = await miro.board.get({ type: 'image' });
|
||||||
const axe = items.find(item => item.title === 'todo-tree-axe');
|
const axe = items.find(item => item.title === 'todo-tree-axe');
|
||||||
return axe;
|
return axe;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
init();
|
init();
|
||||||
|
|||||||
58
src/todo-tree-helper.ts
Normal file
58
src/todo-tree-helper.ts
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import { Connector, Item, StickyNote } from "@mirohq/websdk-types";
|
||||||
|
|
||||||
|
const getTypedMiroItem = async <T extends Item>(id: string, type: T['type']): Promise<T | undefined> => {
|
||||||
|
const item = await miro.board.getById(id);
|
||||||
|
return item?.type === type ? item as T : undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface ConnectorDirection {
|
||||||
|
parentId?: string;
|
||||||
|
childId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const getConnectorDirection = (connector: Connector): ConnectorDirection | undefined => {
|
||||||
|
const hasEndArrow = connector.style.endStrokeCap !== 'none';
|
||||||
|
const hasStartArrow = connector.style.startStrokeCap !== 'none';
|
||||||
|
|
||||||
|
if (hasEndArrow && !hasStartArrow) {
|
||||||
|
return { parentId: connector.start?.item, childId: connector.end?.item };
|
||||||
|
}
|
||||||
|
if (hasStartArrow && !hasEndArrow) {
|
||||||
|
return { parentId: connector.end?.item, childId: connector.start?.item };
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getStickiesToChopInTree = async (root: StickyNote, removableColors: string[]): Promise<StickyNote[]> => {
|
||||||
|
if (!removableColors.includes(root.style.fillColor)) return [];
|
||||||
|
|
||||||
|
const toRemove: StickyNote[] = [];
|
||||||
|
const visitedIds = new Set<string>();
|
||||||
|
|
||||||
|
const traverseTree = async (node: StickyNote) => {
|
||||||
|
if (visitedIds.has(node.id)){
|
||||||
|
// prevent infinite loops of circular arrows
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
visitedIds.add(node.id);
|
||||||
|
|
||||||
|
for (const connectorId of node.connectorIds || []) {
|
||||||
|
const connector = await getTypedMiroItem<Connector>(connectorId, 'connector');
|
||||||
|
if (!connector) continue;
|
||||||
|
|
||||||
|
const direction = getConnectorDirection(connector);
|
||||||
|
if (!direction || !direction.childId || direction.parentId !== node.id) continue;
|
||||||
|
|
||||||
|
const childSticky = await getTypedMiroItem<StickyNote>(direction.childId, 'sticky_note');
|
||||||
|
if (childSticky) {
|
||||||
|
if (removableColors.includes(childSticky.style.fillColor)) {
|
||||||
|
toRemove.push(childSticky);
|
||||||
|
}
|
||||||
|
await traverseTree(childSticky);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
await traverseTree(root);
|
||||||
|
return toRemove;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user