Add task data
This commit is contained in:
parent
aafdf128a3
commit
7f8d67b551
@ -47,7 +47,8 @@ export default class KanbnBoardPanel {
|
||||
task => KanbnBoardPanel.currentPanel!._kanbn.hydrateTask(index, task)
|
||||
),
|
||||
startedColumns: index.options.startedColumns ?? [],
|
||||
completedColumns: index.options.completedColumns ?? []
|
||||
completedColumns: index.options.completedColumns ?? [],
|
||||
dateFormat: KanbnBoardPanel.currentPanel._kanbn.getDateFormat(index)
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -99,6 +100,11 @@ export default class KanbnBoardPanel {
|
||||
vscode.window.showErrorMessage(message.text);
|
||||
return;
|
||||
|
||||
case 'kanbn.task':
|
||||
// TODO open task panel with task
|
||||
vscode.window.showInformationMessage(`Opening task ${message.taskId}`);
|
||||
return;
|
||||
|
||||
// Move a task
|
||||
case 'kanbn.move':
|
||||
try {
|
||||
@ -110,17 +116,20 @@ export default class KanbnBoardPanel {
|
||||
|
||||
// Create a task
|
||||
case 'kanbn.create':
|
||||
//
|
||||
// TODO open task panel with blank task
|
||||
vscode.window.showInformationMessage(`Creating new task in column ${message.column}`);
|
||||
return;
|
||||
|
||||
// Update a task
|
||||
case 'kanbn.update':
|
||||
//
|
||||
// TODO update task
|
||||
vscode.window.showInformationMessage(`Editing task ${message.taskId}`);
|
||||
return;
|
||||
|
||||
// Delete a task
|
||||
case 'kanbn.delete':
|
||||
//
|
||||
// TODO delete task
|
||||
vscode.window.showInformationMessage(`Deleting task ${message.taskId}`);
|
||||
return;
|
||||
}
|
||||
}, null, this._disposables);
|
||||
|
@ -1,2 +1,134 @@
|
||||
import * as path from 'path';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
export default class KanbnTaskPanel {
|
||||
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[] = [];
|
||||
|
||||
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(KanbnTaskPanel.viewType, 'Kanbn Task', 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, '.kanbn')),
|
||||
vscode.Uri.file(path.join(this._extensionPath, 'node_modules', 'vscode-codicons', 'dist'))
|
||||
]
|
||||
});
|
||||
|
||||
// Set the webview's title to the kanbn task 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;
|
||||
|
||||
// Update a task
|
||||
case 'kanbn.update':
|
||||
// TODO update task
|
||||
vscode.window.showInformationMessage(`Editing task ${message.taskId}`);
|
||||
return;
|
||||
|
||||
// Delete a task
|
||||
case 'kanbn.delete':
|
||||
// TODO delete task
|
||||
vscode.window.showInformationMessage(`Deleting task ${message.taskId}`);
|
||||
return;
|
||||
}
|
||||
}, null, this._disposables);
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
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, '.kanbn', '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 `<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no">
|
||||
<meta name="theme-color" content="#000000">
|
||||
<title>Kanbn Board</title>
|
||||
<link rel="stylesheet" type="text/css" href="${styleUri}">
|
||||
<link rel="stylesheet" type="text/css" href="${customStyleUri}">
|
||||
<link rel="stylesheet" type="text/css" href="${codiconsUri}">
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src vscode-resource: https:; script-src 'nonce-${nonce}'; font-src vscode-resource:; style-src vscode-resource: 'unsafe-inline' http: https: data:;">
|
||||
<base href="${vscode.Uri.file(path.join(this._extensionPath, 'build')).with({ scheme: 'vscode-resource' })}/">
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<script nonce="${nonce}" src="${scriptUri}"></script>
|
||||
</body>
|
||||
</html>`;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
25372
package-lock.json
generated
25372
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -25,6 +25,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@basementuniverse/kanbn": "file:~/Projects/kanbn",
|
||||
"@types/dateformat": "^3.0.1",
|
||||
"dateformat": "^4.5.1",
|
||||
"react": "^16.3.2",
|
||||
"react-beautiful-dnd": "12.2.0",
|
||||
"react-dom": "^16.3.2",
|
||||
|
14
src/App.tsx
14
src/App.tsx
@ -10,6 +10,7 @@ function App() {
|
||||
const [columns, setColumns] = useState({});
|
||||
const [startedColumns, setStartedColumns] = useState([]);
|
||||
const [completedColumns, setCompletedColumns] = useState([]);
|
||||
const [dateFormat, setDateFormat] = useState('');
|
||||
|
||||
window.addEventListener('message', event => {
|
||||
const tasks = Object.fromEntries(event.data.tasks.map(task => [task.id, task]));
|
||||
@ -23,12 +24,21 @@ function App() {
|
||||
));
|
||||
setStartedColumns(event.data.startedColumns);
|
||||
setCompletedColumns(event.data.completedColumns);
|
||||
setDateFormat(event.data.dateFormat);
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Header name={name} description={description} />
|
||||
<Board columns={columns} startedColumns={startedColumns} completedColumns={completedColumns} />
|
||||
<Header
|
||||
name={name}
|
||||
description={description}
|
||||
/>
|
||||
<Board
|
||||
columns={columns}
|
||||
startedColumns={startedColumns}
|
||||
completedColumns={completedColumns}
|
||||
dateFormat={dateFormat}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { DragDropContext, Droppable } from "react-beautiful-dnd";
|
||||
import React, { useState } from "react";
|
||||
import Task from './Task';
|
||||
import VSCodeApi from "./VSCodeApi";
|
||||
|
||||
declare var acquireVsCodeApi: any;
|
||||
const vscode = acquireVsCodeApi();
|
||||
declare var acquireVsCodeApi: Function;
|
||||
const vscode: VSCodeApi = acquireVsCodeApi();
|
||||
|
||||
const onDragEnd = (result, columns, setColumns) => {
|
||||
|
||||
@ -56,10 +57,11 @@ const onDragEnd = (result, columns, setColumns) => {
|
||||
});
|
||||
};
|
||||
|
||||
const Board = ({ columns, startedColumns, completedColumns }: {
|
||||
const Board = ({ columns, startedColumns, completedColumns, dateFormat }: {
|
||||
columns: Record<string, KanbnTask[]>,
|
||||
startedColumns: string[],
|
||||
completedColumns: string[]
|
||||
completedColumns: string[],
|
||||
dateFormat: string
|
||||
}) => {
|
||||
const [, setColumns] = useState(columns);
|
||||
return (
|
||||
@ -82,12 +84,25 @@ const Board = ({ columns, startedColumns, completedColumns }: {
|
||||
key={columnName}
|
||||
>
|
||||
<h2 className="kanbn-column-name">
|
||||
{
|
||||
startedColumns.indexOf(columnName) > -1 &&
|
||||
<i className="codicon codicon-chevron-right"></i>
|
||||
}
|
||||
{
|
||||
completedColumns.indexOf(columnName) > -1 &&
|
||||
<i className="codicon codicon-check"></i>
|
||||
}
|
||||
{columnName}
|
||||
<span className="kanbn-column-count">{column.length || ''}</span>
|
||||
<button
|
||||
type="button"
|
||||
className="kanbn-create-task-button"
|
||||
onClick={e => null}
|
||||
onClick={() => {
|
||||
vscode.postMessage({
|
||||
command: 'kanbn.create',
|
||||
column: columnName
|
||||
})
|
||||
}}
|
||||
title={`Create task in ${columnName}`}
|
||||
>
|
||||
<i className="codicon codicon-add"></i>
|
||||
@ -105,7 +120,12 @@ const Board = ({ columns, startedColumns, completedColumns }: {
|
||||
snapshot.isDraggingOver ? 'drag-over' : null
|
||||
].filter(i => i).join(' ')}
|
||||
>
|
||||
{column.map((task, index) => <Task task={task} index={index} />)}
|
||||
{column.map((task, index) => <Task
|
||||
task={task}
|
||||
index={index}
|
||||
dateFormat={dateFormat}
|
||||
vscode={vscode}
|
||||
/>)}
|
||||
{provided.placeholder}
|
||||
</div>
|
||||
);
|
||||
|
3
src/KanbnTask.d.ts
vendored
3
src/KanbnTask.d.ts
vendored
@ -10,7 +10,8 @@ declare type KanbnTask = {
|
||||
created: Date,
|
||||
updated?: Date,
|
||||
completed?: Date,
|
||||
assigned?: string
|
||||
assigned?: string,
|
||||
tags?: string[]
|
||||
},
|
||||
relations: Array<{
|
||||
type: string,
|
||||
|
83
src/Task.tsx
83
src/Task.tsx
@ -1,7 +1,14 @@
|
||||
import React from "react";
|
||||
import { Draggable } from "react-beautiful-dnd";
|
||||
import formatDate from 'dateformat';
|
||||
import VSCodeApi from "./VSCodeApi";
|
||||
|
||||
const Task = ({ task, index }: { task: KanbnTask, index: number }) => {
|
||||
const Task = ({ task, index, dateFormat, vscode }: {
|
||||
task: KanbnTask,
|
||||
index: number,
|
||||
dateFormat: string,
|
||||
vscode: VSCodeApi
|
||||
}) => {
|
||||
return (
|
||||
<Draggable
|
||||
key={task.id}
|
||||
@ -25,20 +32,74 @@ const Task = ({ task, index }: { task: KanbnTask, index: number }) => {
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
onClick={e => null} // TODO open task editor webview panel when clicked
|
||||
className="kanbn-task-name"
|
||||
onClick={() => {
|
||||
vscode.postMessage({
|
||||
command: 'kanbn.task',
|
||||
taskId: task.id
|
||||
})
|
||||
}}
|
||||
title={task.id}
|
||||
>
|
||||
{task.name}
|
||||
</button>
|
||||
{/*
|
||||
// TODO add task info
|
||||
truncated description (?),
|
||||
progress as %-filled bottom border,
|
||||
created/updated (updated date, fallback to created),
|
||||
tags (provide default colours in css, put tagname in className),
|
||||
count sub-tasks (if >0),
|
||||
count comments (if >0)
|
||||
*/}
|
||||
{
|
||||
'tags' in task.metadata &&
|
||||
<div className="kanbn-task-tags">
|
||||
{task.metadata.tags!.map(tag => {
|
||||
return (
|
||||
<span className={[
|
||||
'kanbn-task-tag',
|
||||
`kanbn-task-tag-${tag}`
|
||||
].join(' ')}>
|
||||
{tag}
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
}
|
||||
{
|
||||
'assigned' in task.metadata &&
|
||||
<div className="kanbn-task-assigned">
|
||||
<i className="codicon codicon-account"></i>{task.metadata.assigned}
|
||||
</div>
|
||||
}
|
||||
<div className="kanbn-task-date">
|
||||
{
|
||||
'updated' in task.metadata
|
||||
? formatDate(task.metadata.updated, dateFormat)
|
||||
: formatDate(task.metadata.created, dateFormat)
|
||||
}
|
||||
</div>
|
||||
<div className="kanbn-task-description">
|
||||
{task.description}
|
||||
</div>
|
||||
{
|
||||
task.comments.length > 0 &&
|
||||
<div className="kanbn-task-comments">
|
||||
<i className="codicon codicon-comment"></i>{task.comments.length}
|
||||
</div>
|
||||
}
|
||||
{
|
||||
task.subTasks.length > 0 &&
|
||||
<div className="kanbn-task-sub-tasks">
|
||||
<i className="codicon codicon-tasklist"></i>
|
||||
{task.subTasks.filter(subTask => subTask.completed).length} / {task.subTasks.length}
|
||||
</div>
|
||||
}
|
||||
{
|
||||
task.workload !== undefined &&
|
||||
<div className="kanbn-task-workload">
|
||||
<i className="codicon codicon-run"></i>{task.workload}
|
||||
</div>
|
||||
}
|
||||
{
|
||||
task.workload !== undefined &&
|
||||
task.progress !== undefined &&
|
||||
<div className="kanbn-task-progress" style={{
|
||||
width: `${task.progress * 100}%`
|
||||
}}></div>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
|
5
src/VSCodeApi.ts
Normal file
5
src/VSCodeApi.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export default interface VSCodeApi {
|
||||
getState: () => any;
|
||||
setState: (newState: any) => any;
|
||||
postMessage: (message: any) => void;
|
||||
}
|
@ -86,7 +86,7 @@ body {
|
||||
border-color: var(--vscode-activityBar-activeBackground);
|
||||
}
|
||||
|
||||
.kanbn-task button {
|
||||
.kanbn-task-name {
|
||||
border: none;
|
||||
outline: none;
|
||||
background-color: transparent;
|
||||
@ -94,7 +94,7 @@ body {
|
||||
color: var(--vscode-editor-foreground);
|
||||
}
|
||||
|
||||
.kanbn-task button:focus, .kanbn-task button:hover {
|
||||
.kanbn-task-name:focus, .kanbn-task-name:hover {
|
||||
border: none;
|
||||
outline: none;
|
||||
text-decoration: underline;
|
||||
|
Loading…
x
Reference in New Issue
Block a user