From f7f2eebfa41c3bdec4b4e0adf42afcbd569f100f Mon Sep 17 00:00:00 2001 From: Gordon Date: Thu, 15 Apr 2021 01:11:20 +0100 Subject: [PATCH] Add burndown panel --- .gitignore | 1 - ext-src/KanbnBurndownPanel.ts | 168 ++++++++++++++++++++++++++++++++++ resources/burndown_dark.svg | 1 + resources/burndown_light.svg | 1 + resources/kanbn.png | Bin 0 -> 6967 bytes resources/project_dark.svg | 1 + resources/project_light.svg | 1 + resources/task_dark.svg | 13 +++ resources/task_light.svg | 13 +++ src/App.tsx | 6 ++ src/Burndown.tsx | 9 ++ 11 files changed, 213 insertions(+), 1 deletion(-) create mode 100644 ext-src/KanbnBurndownPanel.ts create mode 100644 resources/burndown_dark.svg create mode 100644 resources/burndown_light.svg create mode 100644 resources/kanbn.png create mode 100644 resources/project_dark.svg create mode 100644 resources/project_light.svg create mode 100644 resources/task_dark.svg create mode 100644 resources/task_light.svg create mode 100644 src/Burndown.tsx diff --git a/.gitignore b/.gitignore index 7656ccd..d30f40e 100644 --- a/.gitignore +++ b/.gitignore @@ -10,7 +10,6 @@ /build # misc -/resources .DS_Store .env.local .env.development.local diff --git a/ext-src/KanbnBurndownPanel.ts b/ext-src/KanbnBurndownPanel.ts new file mode 100644 index 0000000..291b68c --- /dev/null +++ b/ext-src/KanbnBurndownPanel.ts @@ -0,0 +1,168 @@ +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 readonly _panel: vscode.WebviewPanel; + private readonly _extensionPath: string; + private readonly _workspacePath: string; + 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') + ) { + const column = vscode.window.activeTextEditor ? vscode.window.activeTextEditor.viewColumn : undefined; + + // If we already have a panel, show it, otherwise create a new panel + if (KanbnBurndownPanel.currentPanel) { + KanbnBurndownPanel.currentPanel._panel.reveal(column); + } else { + KanbnBurndownPanel.currentPanel = new KanbnBurndownPanel( + extensionPath, + workspacePath, + column || vscode.ViewColumn.One, + kanbn + ); + } + } + + public static async update() { + if (KanbnBurndownPanel.currentPanel) { + let index: any; + try { + index = await KanbnBurndownPanel.currentPanel._kanbn.getIndex(); + } catch (error) { + vscode.window.showErrorMessage(error instanceof Error ? error.message : error); + return; + } + KanbnBurndownPanel.currentPanel._panel.webview.postMessage({ + type: 'burndown', + index, + dateFormat: KanbnBurndownPanel.currentPanel._kanbn.getDateFormat(index) + }); + } + } + + private constructor( + extensionPath: string, + workspacePath: string, + column: vscode.ViewColumn, + 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, { + // Enable javascript in the webview + enableScripts: true, + + // Retain state even when hidden + retainContextWhenHidden: true, + + // 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._workspacePath, this._kanbn.getFolderName())), + 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')) + }; + + // Set the webview's title to the kanbn project name + this._kanbn.getIndex().then(index => { + this._panel.title = index.name; + }); + + // Set the webview's initial html content + this._panel.webview.html = this._getHtmlForWebview(); + + // Listen for when the panel is disposed + // This happens when the user closes the panel or when the panel is closed programatically + 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); + } + + public dispose() { + KanbnBurndownPanel.currentPanel = undefined; + + // Clean up our resources + this._panel.dispose(); + while (this._disposables.length) { + const x = this._disposables.pop(); + if (x) { + x.dispose(); + } + } + } + + 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' }); + + // Use a nonce to whitelist which scripts can be run + const nonce = getNonce(); + + return ` + + + + + +Kanbn Board + + + + + + + + +
+ + +`; + } +} + +function getNonce() { + let text = ""; + const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + for (let i = 0; i < 32; i++) { + text += possible.charAt(Math.floor(Math.random() * possible.length)); + } + return text; +} diff --git a/resources/burndown_dark.svg b/resources/burndown_dark.svg new file mode 100644 index 0000000..0fdf507 --- /dev/null +++ b/resources/burndown_dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/burndown_light.svg b/resources/burndown_light.svg new file mode 100644 index 0000000..ae9d112 --- /dev/null +++ b/resources/burndown_light.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/kanbn.png b/resources/kanbn.png new file mode 100644 index 0000000000000000000000000000000000000000..565fce5f79db1748f688753f5269e27e7f002bc2 GIT binary patch literal 6967 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°?omNh4AVWLAE)yv2u+YA`nFZxIhNHnsgMca9!9^!Xx!DIi;Ri|su z!Iv~ODVo!ZsrNdjdb5TngUZI9J&7$3-Mrv6b5t)i&8o|NU#@I0e(=fLBOeFGT;gYP z5J8NY=jj(T#|%!H%iUgmuI!F=;=Q(>O@|5(ebi`tv@ia?dGu*hO0f`cjMM+Q@mzyc z1THqR!9Hc^_uMLpVXaGTcYgPIke4!){-L!>%J(B2I?A-n{c`eLT`zT_=H#u5?ld2r zq+-OUjd!PGOfMe%@;f6M)uOFD??jX#3n5>JUmu{5@8t{PLaaM&eQW!SI&NCHnwV<&7`;AV zs@X%f;lo;-Vq?M>U3l$4>)g(tyUT=JTUqhSNi{qen{@agNjD&{LA5e|ux3loqx#kQ z`3n0V8%Dp(KK1$V!`#yi9S~RF4V|OTBSx0jwUq^kBwXl$xQ!uW%VaZ;8!u~r@C9*3 zATM>ML)%cCfEGzDm#e7P7_mAoI#oGe&Y{oUdc4F \ No newline at end of file diff --git a/resources/project_light.svg b/resources/project_light.svg new file mode 100644 index 0000000..7eff72a --- /dev/null +++ b/resources/project_light.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/task_dark.svg b/resources/task_dark.svg new file mode 100644 index 0000000..a37c407 --- /dev/null +++ b/resources/task_dark.svg @@ -0,0 +1,13 @@ + + + + + + + + + diff --git a/resources/task_light.svg b/resources/task_light.svg new file mode 100644 index 0000000..dc9c489 --- /dev/null +++ b/resources/task_light.svg @@ -0,0 +1,13 @@ + + + + + + + + + diff --git a/src/App.tsx b/src/App.tsx index c919ccb..7709526 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,4 +1,5 @@ import Board from './Board'; +import Burndown from './Burndown'; import TaskEditor from './TaskEditor'; import React, { useState } from "react"; import VSCodeApi from "./VSCodeApi"; @@ -94,6 +95,11 @@ function App() { vscode={vscode} /> } + { + type === 'burndown' && + + } ); } diff --git a/src/Burndown.tsx b/src/Burndown.tsx new file mode 100644 index 0000000..0f98159 --- /dev/null +++ b/src/Burndown.tsx @@ -0,0 +1,9 @@ +import React from 'react'; + +const Burndown = () => { + return ( +
burndown chart
+ ); +}; + +export default Burndown;