diff --git a/ext-src/KanbnBoardPanel.ts b/ext-src/KanbnBoardPanel.ts index c7302f7..9dbe12b 100644 --- a/ext-src/KanbnBoardPanel.ts +++ b/ext-src/KanbnBoardPanel.ts @@ -1,6 +1,7 @@ import * as path from 'path'; import * as vscode from 'vscode'; import KanbnTaskPanel from './KanbnTaskPanel'; +import KanbnBurndownPanel from './KanbnBurndownPanel'; export default class KanbnBoardPanel { public static currentPanel: KanbnBoardPanel | undefined; @@ -148,8 +149,12 @@ export default class KanbnBoardPanel { // Open a burndown chart case 'kanbn.burndown': - // TODO open a burndown chart webview panel - vscode.window.showErrorMessage('Not implemented yet!'); + KanbnBurndownPanel.createOrShow( + this._extensionPath, + this._workspacePath, + this._kanbn + ); + KanbnBurndownPanel.update(); return; // Start a new sprint diff --git a/ext-src/KanbnBurndownPanel.ts b/ext-src/KanbnBurndownPanel.ts index 291b68c..72c883f 100644 --- a/ext-src/KanbnBurndownPanel.ts +++ b/ext-src/KanbnBurndownPanel.ts @@ -1,21 +1,21 @@ -import * as path from 'path'; -import * as vscode from 'vscode'; +import * as path from "path"; +import * as vscode from "vscode"; export default class KanbnBurndownPanel { public static currentPanel: KanbnBurndownPanel | undefined; - private static readonly viewType = 'react'; + private static readonly viewType = "react"; private readonly _panel: vscode.WebviewPanel; private readonly _extensionPath: string; private readonly _workspacePath: string; - private readonly _kanbn: typeof import('@basementuniverse/kanbn/src/main'); + private readonly _kanbn: typeof import("@basementuniverse/kanbn/src/main"); private _disposables: vscode.Disposable[] = []; public static createOrShow( extensionPath: string, workspacePath: string, - kanbn: typeof import('@basementuniverse/kanbn/src/main') + kanbn: typeof import("@basementuniverse/kanbn/src/main") ) { const column = vscode.window.activeTextEditor ? vscode.window.activeTextEditor.viewColumn : undefined; @@ -41,10 +41,20 @@ export default class KanbnBurndownPanel { vscode.window.showErrorMessage(error instanceof Error ? error.message : error); return; } + let tasks: any[]; + try { + tasks = (await KanbnBurndownPanel.currentPanel._kanbn.loadAllTrackedTasks(index)).map( + task => KanbnBurndownPanel.currentPanel!._kanbn.hydrateTask(index, task) + ); + } catch (error) { + vscode.window.showErrorMessage(error instanceof Error ? error.message : error); + return; + } KanbnBurndownPanel.currentPanel._panel.webview.postMessage({ - type: 'burndown', + type: "burndown", index, - dateFormat: KanbnBurndownPanel.currentPanel._kanbn.getDateFormat(index) + tasks, + dateFormat: KanbnBurndownPanel.currentPanel._kanbn.getDateFormat(index), }); } } @@ -53,14 +63,14 @@ export default class KanbnBurndownPanel { extensionPath: string, workspacePath: string, column: vscode.ViewColumn, - kanbn: typeof import('@basementuniverse/kanbn/src/main') + kanbn: typeof import("@basementuniverse/kanbn/src/main") ) { this._extensionPath = extensionPath; this._workspacePath = workspacePath; this._kanbn = kanbn; // Create and show a new webview panel - this._panel = vscode.window.createWebviewPanel(KanbnBurndownPanel.viewType, 'Burndown Chart', column, { + this._panel = vscode.window.createWebviewPanel(KanbnBurndownPanel.viewType, "Burndown Chart", column, { // Enable javascript in the webview enableScripts: true, @@ -69,18 +79,18 @@ export default class KanbnBurndownPanel { // Restrict the webview to only loading content from allowed paths localResourceRoots: [ - vscode.Uri.file(path.join(this._extensionPath, 'build')), + vscode.Uri.file(path.join(this._extensionPath, "build")), vscode.Uri.file(path.join(this._workspacePath, this._kanbn.getFolderName())), - vscode.Uri.file(path.join(this._extensionPath, 'node_modules', 'vscode-codicons', 'dist')) - ] + vscode.Uri.file(path.join(this._extensionPath, "node_modules", "vscode-codicons", "dist")), + ], }); (this._panel as any).iconPath = { - light: vscode.Uri.file(path.join(this._extensionPath, 'resources', 'burndown_light.svg')), - dark: vscode.Uri.file(path.join(this._extensionPath, 'resources', 'burndown_dark.svg')) + light: vscode.Uri.file(path.join(this._extensionPath, "resources", "burndown_light.svg")), + dark: vscode.Uri.file(path.join(this._extensionPath, "resources", "burndown_dark.svg")), }; // Set the webview's title to the kanbn project name - this._kanbn.getIndex().then(index => { + this._kanbn.getIndex().then((index) => { this._panel.title = index.name; }); @@ -92,15 +102,18 @@ export default class KanbnBurndownPanel { this._panel.onDidDispose(() => this.dispose(), null, this._disposables); // Handle messages from the webview - this._panel.webview.onDidReceiveMessage(async message => { - switch (message.command) { - - // Display error message - case 'error': - vscode.window.showErrorMessage(message.text); - return; - } - }, null, this._disposables); + this._panel.webview.onDidReceiveMessage( + async (message) => { + switch (message.command) { + // Display error message + case "error": + vscode.window.showErrorMessage(message.text); + return; + } + }, + null, + this._disposables + ); } public dispose() { @@ -117,21 +130,21 @@ export default class KanbnBurndownPanel { } private _getHtmlForWebview() { - const manifest = require(path.join(this._extensionPath, 'build', 'asset-manifest.json')); - const mainScript = manifest['main.js']; - const mainStyle = manifest['main.css']; - const scriptUri = vscode.Uri - .file(path.join(this._extensionPath, 'build', mainScript)) - .with({ scheme: 'vscode-resource' }); - const styleUri = vscode.Uri - .file(path.join(this._extensionPath, 'build', mainStyle)) - .with({ scheme: 'vscode-resource' }); - const customStyleUri = vscode.Uri - .file(path.join(this._workspacePath, this._kanbn.getFolderName(), 'board.css')) - .with({ scheme: 'vscode-resource' }); - const codiconsUri = vscode.Uri - .file(path.join(this._extensionPath, 'node_modules', 'vscode-codicons', 'dist', 'codicon.css')) - .with({ scheme: 'vscode-resource' }); + const manifest = require(path.join(this._extensionPath, "build", "asset-manifest.json")); + const mainScript = manifest["main.js"]; + const mainStyle = manifest["main.css"]; + const scriptUri = vscode.Uri.file(path.join(this._extensionPath, "build", mainScript)).with({ + scheme: "vscode-resource", + }); + const styleUri = vscode.Uri.file(path.join(this._extensionPath, "build", mainStyle)).with({ + scheme: "vscode-resource", + }); + const customStyleUri = vscode.Uri.file( + path.join(this._workspacePath, this._kanbn.getFolderName(), "board.css") + ).with({ scheme: "vscode-resource" }); + const codiconsUri = vscode.Uri.file( + path.join(this._extensionPath, "node_modules", "vscode-codicons", "dist", "codicon.css") + ).with({ scheme: "vscode-resource" }); // Use a nonce to whitelist which scripts can be run const nonce = getNonce(); @@ -147,7 +160,7 @@ export default class KanbnBurndownPanel { - + diff --git a/ext-src/extension.ts b/ext-src/extension.ts index dc40884..1618854 100644 --- a/ext-src/extension.ts +++ b/ext-src/extension.ts @@ -1,6 +1,7 @@ import * as vscode from 'vscode'; import KanbnStatusBarItem from './KanbnStatusBarItem'; import KanbnBoardPanel from './KanbnBoardPanel'; +import KanbnBurndownPanel from './KanbnBurndownPanel'; import KanbnTaskPanel from './KanbnTaskPanel'; let kanbnStatusBarItem: KanbnStatusBarItem; @@ -105,8 +106,28 @@ export async function activate(context: vscode.ExtensionContext) { // Register a command to open a burndown chart. context.subscriptions.push(vscode.commands.registerCommand('kanbn.burndown', async () => { - // TODO open burndown chart singleton webview panel - vscode.window.showErrorMessage('Not implemented yet!'); + // If no workspace folder is opened, we can't open the burndown chart + if (vscode.workspace.workspaceFolders === undefined) { + vscode.window.showErrorMessage('You need to open a workspace before viewing the burndown chart.'); + return; + } + + // Set the node process directory and import kanbn + process.chdir(vscode.workspace.workspaceFolders[0].uri.fsPath); + const kanbn = await import('@basementuniverse/kanbn/src/main'); + + // If kanbn is initialised, view the burndown chart + if (await kanbn.initialised()) { + KanbnBurndownPanel.createOrShow( + context.extensionPath, + vscode.workspace.workspaceFolders[0].uri.fsPath, + kanbn + ); + KanbnBurndownPanel.update(); + } else { + vscode.window.showErrorMessage('You need to initialise Kanbn before viewing the burndown chart.'); + } + kanbnStatusBarItem.update(); })); // If a workspace folder is open, add a status bar item and start watching for file changes diff --git a/package-lock.json b/package-lock.json index 800071d..f4646bc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1486,6 +1486,32 @@ "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", "dev": true }, + "@types/d3-path": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-1.0.9.tgz", + "integrity": "sha512-NaIeSIBiFgSC6IGUBjZWcscUJEq7vpVu7KthHN8eieTV9d9MqkSOZLH4chq1PmcKy06PNe3axLeKmRIyxJ+PZQ==" + }, + "@types/d3-scale": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-3.2.2.tgz", + "integrity": "sha512-qpQe8G02tzUwt9sdWX1h8A/W0Q1+N48wMnYXVOkrzeLUkCfvzJYV9Ee3aORCS4dN4ONRLFmMvaXdziQ29XGLjQ==", + "requires": { + "@types/d3-time": "*" + } + }, + "@types/d3-shape": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-2.0.0.tgz", + "integrity": "sha512-NLzD02m5PiD1KLEDjLN+MtqEcFYn4ZL9+Rqc9ZwARK1cpKZXd91zBETbe6wpBB6Ia0D0VZbpmbW3+BsGPGnCpA==", + "requires": { + "@types/d3-path": "^1" + } + }, + "@types/d3-time": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-2.0.0.tgz", + "integrity": "sha512-Abz8bTzy8UWDeYs9pCa3D37i29EWDjNTjemdk0ei1ApYVNqulYlGUKip/jLOpogkPSsPz/GvZCYiC7MFlEk0iQ==" + }, "@types/dateformat": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@types/dateformat/-/dateformat-3.0.1.tgz", @@ -1578,6 +1604,11 @@ "redux": "^4.0.0" } }, + "@types/resize-observer-browser": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@types/resize-observer-browser/-/resize-observer-browser-0.1.5.tgz", + "integrity": "sha512-8k/67Z95Goa6Lznuykxkfhq9YU3l1Qe6LNZmwde1u7802a3x8v44oq0j91DICclxatTr0rNnhXx7+VTIetSrSQ==" + }, "@types/scheduler": { "version": "0.16.1", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.1.tgz", @@ -4018,6 +4049,11 @@ } } }, + "classnames": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz", + "integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==" + }, "clean-css": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz", @@ -4884,6 +4920,11 @@ "source-map": "^0.6.1" } }, + "css-unit-converter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.2.tgz", + "integrity": "sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA==" + }, "css-what": { "version": "3.4.2", "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz", @@ -5106,6 +5147,73 @@ "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=", "dev": true }, + "d3-array": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", + "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", + "requires": { + "internmap": "^1.0.0" + } + }, + "d3-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-2.0.0.tgz", + "integrity": "sha512-SPXi0TSKPD4g9tw0NMZFnR95XVgUZiBH+uUTqQuDu1OsE2zomHU7ho0FISciaPvosimixwHFl3WHLGabv6dDgQ==" + }, + "d3-format": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-2.0.0.tgz", + "integrity": "sha512-Ab3S6XuE/Q+flY96HXT0jOXcM4EAClYFnRGY5zsjRGNy6qCYrQsMffs7cV5Q9xejb35zxW5hf/guKw34kvIKsA==" + }, + "d3-interpolate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-2.0.1.tgz", + "integrity": "sha512-c5UhwwTs/yybcmTpAVqwSFl6vrQ8JZJoT5F7xNFK9pymv5C0Ymcc9/LIJHtYIggg/yS9YHw8i8O8tgb9pupjeQ==", + "requires": { + "d3-color": "1 - 2" + } + }, + "d3-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-2.0.0.tgz", + "integrity": "sha512-ZwZQxKhBnv9yHaiWd6ZU4x5BtCQ7pXszEV9CU6kRgwIQVQGLMv1oiL4M+MK/n79sYzsj+gcgpPQSctJUsLN7fA==" + }, + "d3-scale": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-3.2.4.tgz", + "integrity": "sha512-PG6gtpbPCFqKbvdBEswQcJcTzHC8VEd/XzezF5e68KlkT4/ggELw/nR1tv863jY6ufKTvDlzCMZvhe06codbbA==", + "requires": { + "d3-array": "^2.3.0", + "d3-format": "1 - 2", + "d3-interpolate": "1.2.0 - 2", + "d3-time": "1 - 2", + "d3-time-format": "2 - 3" + } + }, + "d3-shape": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-2.1.0.tgz", + "integrity": "sha512-PnjUqfM2PpskbSLTJvAzp2Wv4CZsnAgTfcVRTwW03QR3MkXF8Uo7B1y/lWkAsmbKwuecto++4NlsYcvYpXpTHA==", + "requires": { + "d3-path": "1 - 2" + } + }, + "d3-time": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-2.1.1.tgz", + "integrity": "sha512-/eIQe/eR4kCQwq7yxi7z4c6qEXf2IYGcjoWB5OOQy4Tq9Uv39/947qlDcN2TLkiTzQWzvnsuYPB9TrWaNfipKQ==", + "requires": { + "d3-array": "2" + } + }, + "d3-time-format": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-3.0.0.tgz", + "integrity": "sha512-UXJh6EKsHBTjopVqZBhFysQcoXSv/5yLONZvkQ5Kk3qbwiUYkdX17Xa1PT6U1ZWXGGfB1ey5L8dKMlFq2DO0Ag==", + "requires": { + "d3-time": "1 - 2" + } + }, "damerau-levenshtein": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.6.tgz", @@ -5170,6 +5278,11 @@ "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", "dev": true }, + "decimal.js-light": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==" + }, "decode-uri-component": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", @@ -5565,6 +5678,14 @@ "utila": "~0.4" } }, + "dom-helpers": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", + "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==", + "requires": { + "@babel/runtime": "^7.1.2" + } + }, "dom-serializer": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", @@ -6529,8 +6650,7 @@ "eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "dev": true + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" }, "events": { "version": "3.3.0", @@ -6782,6 +6902,11 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "fast-equals": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-2.0.1.tgz", + "integrity": "sha512-jIHAbyu5Txdi299DitHXr4wuvw7ajz8S4xVgShJmQOUD6TovsKzvMoHoq9G8+dO6xeKWrwH3DURT+ZDKnwjSsA==" + }, "fast-glob": { "version": "2.2.7", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.7.tgz", @@ -10097,6 +10222,11 @@ "ipaddr.js": "^1.5.2" } }, + "internmap": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", + "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==" + }, "invariant": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", @@ -11849,8 +11979,7 @@ "lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", - "dev": true + "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" }, "lodash.memoize": { "version": "4.1.2", @@ -11889,6 +12018,11 @@ "lodash._reinterpolate": "^3.0.0" } }, + "lodash.throttle": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", + "integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ=" + }, "lodash.uniq": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", @@ -13415,8 +13549,7 @@ "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" }, "picomatch": { "version": "2.2.3", @@ -15694,8 +15827,7 @@ "postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" }, "postcss-values-parser": { "version": "2.0.1", @@ -15936,7 +16068,6 @@ "version": "3.4.1", "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", - "dev": true, "requires": { "performance-now": "^2.1.0" } @@ -16381,6 +16512,11 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, "react-redux": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.3.tgz", @@ -16394,6 +16530,17 @@ "react-is": "^16.13.1" } }, + "react-resize-detector": { + "version": "6.6.5", + "resolved": "https://registry.npmjs.org/react-resize-detector/-/react-resize-detector-6.6.5.tgz", + "integrity": "sha512-khKS1IpC2cfx5+6G9HkAU/9CGjDV8woE57pVeH8nP5Ji52yXz6MpQEHEzJZ2obGghWrewN4php8ArxB4yWNqZA==", + "requires": { + "@types/resize-observer-browser": "^0.1.5", + "lodash.debounce": "^4.0.8", + "lodash.throttle": "^4.1.1", + "resize-observer-polyfill": "^1.5.1" + } + }, "react-scripts": { "version": "2.1.8", "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-2.1.8.tgz", @@ -16457,6 +16604,27 @@ } } }, + "react-smooth": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-2.0.0.tgz", + "integrity": "sha512-wK4dBBR6P21otowgMT9toZk+GngMplGS1O5gk+2WSiHEXIrQgDvhR5IIlT74Vtu//qpTcipkgo21dD7a7AUNxw==", + "requires": { + "fast-equals": "^2.0.0", + "raf": "^3.4.0", + "react-transition-group": "2.9.0" + } + }, + "react-transition-group": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz", + "integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==", + "requires": { + "dom-helpers": "^3.4.0", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2", + "react-lifecycles-compat": "^3.0.4" + } + }, "read-pkg": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", @@ -16865,6 +17033,41 @@ "util.promisify": "^1.0.0" } }, + "recharts": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.0.9.tgz", + "integrity": "sha512-JNsXE80PuF3hugUCE7JqDOMSvu5xQLxtjOaqFKKZI2pCJ1PVJzhwDv4TWk0nO4AvADbeWzYEHbg8C5Hcrh42UA==", + "requires": { + "@types/d3-scale": "^3.0.0", + "@types/d3-shape": "^2.0.0", + "classnames": "^2.2.5", + "d3-interpolate": "^2.0.1", + "d3-scale": "^3.2.3", + "d3-shape": "^2.0.0", + "eventemitter3": "^4.0.1", + "lodash": "^4.17.19", + "react-is": "16.10.2", + "react-resize-detector": "^6.6.3", + "react-smooth": "^2.0.0", + "recharts-scale": "^0.4.4", + "reduce-css-calc": "^2.1.8" + }, + "dependencies": { + "react-is": { + "version": "16.10.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.10.2.tgz", + "integrity": "sha512-INBT1QEgtcCCgvccr5/86CfD71fw9EPmDxgiJX4I2Ddr6ZsV6iFXsuby+qWJPtmNuMY0zByTsG4468P7nHuNWA==" + } + } + }, + "recharts-scale": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz", + "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==", + "requires": { + "decimal.js-light": "^2.4.1" + } + }, "recursive-readdir": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz", @@ -16874,6 +17077,15 @@ "minimatch": "3.0.4" } }, + "reduce-css-calc": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-2.1.8.tgz", + "integrity": "sha512-8liAVezDmUcH+tdzoEGrhfbGcP7nOV4NkGE3a74+qqvE7nt9i4sKLGBuZNOnpI4WiGksiNPklZxva80061QiPg==", + "requires": { + "css-unit-converter": "^1.1.1", + "postcss-value-parser": "^3.3.0" + } + }, "redux": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/redux/-/redux-4.0.5.tgz", @@ -17173,6 +17385,11 @@ "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", "dev": true }, + "resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==" + }, "resolve": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz", diff --git a/package.json b/package.json index aac1868..491d2c2 100644 --- a/package.json +++ b/package.json @@ -96,6 +96,7 @@ "react": "^16.3.2", "react-beautiful-dnd": "12.2.0", "react-dom": "^16.3.2", + "recharts": "^2.0.9", "terser": "3.14.1", "uuid": "^8.3.2", "vscode-codicons": "^0.0.15" diff --git a/src/App.tsx b/src/App.tsx index 7709526..7143b9a 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -25,7 +25,9 @@ function App() { const [panelUuid, setPanelUuid] = useState(''); const [showBurndownButton, setShowBurndownButton] = useState(false); const [showSprintButton, setShowSprintButton] = useState(false); + const [sprints, setSprints] = useState([]); const [currentSprint, setCurrentSprint] = useState(null); + const [burndownData, setBurndownData] = useState({ series: [] }); window.addEventListener('message', event => { const tasks = Object.fromEntries(event.data.tasks.map(task => [task.id, task])); @@ -60,6 +62,13 @@ function App() { setColumnNames(Object.keys(event.data.index.columns)); setPanelUuid(event.data.panelUuid); break; + + case 'burndown': + setName(event.data.index.name); + setTasks(tasks); + setSprints(event.data.index.options.sprints || []); + setBurndownData(event.data.burndownData); + break; } setType(event.data.type); setDateFormat(event.data.dateFormat); @@ -98,6 +107,10 @@ function App() { { type === 'burndown' && } diff --git a/src/Board.tsx b/src/Board.tsx index e45e565..b918b29 100644 --- a/src/Board.tsx +++ b/src/Board.tsx @@ -154,11 +154,7 @@ const Board = ({ dateFormat: string, showBurndownButton: boolean, showSprintButton: boolean, - currentSprint: { - start: string, - name: string, - description: string - }|null, + currentSprint: KanbnSprint|null, vscode: VSCodeApi }) => { const [, setColumns] = useState(columns); diff --git a/src/Burndown.tsx b/src/Burndown.tsx index 0f98159..8bdf6c2 100644 --- a/src/Burndown.tsx +++ b/src/Burndown.tsx @@ -1,8 +1,40 @@ import React from 'react'; +import { LineChart, Line, CartesianGrid, XAxis, YAxis, Tooltip } from 'recharts'; + +const Burndown = ({ name, tasks, sprints, burndownData }: { + name: string, + tasks: Record, + sprints: KanbnSprint[], + burndownData: { + series: Array<{ + sprint: KanbnSprint, + from: string, + to: string, + dataPoints: Array<{ + x: string, + y: number + }> + }> + } +}) => { + const data = [ + {name: 'one', uv: 20}, + {name: 'two', uv: 80}, + {name: 'three', uv: 45}, + {name: 'four', uv: 32} + ]; -const Burndown = () => { return ( -
burndown chart
+ +
burndown chart
+ + + + + + + +
); }; diff --git a/src/KanbnSprint.d.ts b/src/KanbnSprint.d.ts new file mode 100644 index 0000000..19f9ccd --- /dev/null +++ b/src/KanbnSprint.d.ts @@ -0,0 +1,6 @@ +// Note that Date properties will be converted to strings (ISO) when a task is serialized and passed as a prop +declare type KanbnSprint = { + start: string; + name: string; + description?: string; +};