Task markup and styling

This commit is contained in:
Gordon 2021-04-02 02:30:41 +01:00
parent 7f8d67b551
commit b2fc0be4f2
7 changed files with 278 additions and 103 deletions

View File

@ -1,5 +1,6 @@
import * as path from 'path'; import * as path from 'path';
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import KanbnTaskPanel from './KanbnTaskPanel';
export default class KanbnBoardPanel { export default class KanbnBoardPanel {
public static currentPanel: KanbnBoardPanel | undefined; public static currentPanel: KanbnBoardPanel | undefined;
@ -101,7 +102,12 @@ export default class KanbnBoardPanel {
return; return;
case 'kanbn.task': case 'kanbn.task':
// TODO open task panel with task // KanbnTaskPanel.create(
// this._extensionPath,
// this._workspacePath,
// this._kanbn,
// message.taskId
// );
vscode.window.showInformationMessage(`Opening task ${message.taskId}`); vscode.window.showInformationMessage(`Opening task ${message.taskId}`);
return; return;

61
package-lock.json generated
View File

@ -7101,6 +7101,39 @@
"domelementtype": "1" "domelementtype": "1"
} }
}, },
"dot-case": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz",
"integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==",
"requires": {
"no-case": "^3.0.4",
"tslib": "^2.0.3"
},
"dependencies": {
"lower-case": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
"integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==",
"requires": {
"tslib": "^2.0.3"
}
},
"no-case": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
"integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==",
"requires": {
"lower-case": "^2.0.2",
"tslib": "^2.0.3"
}
},
"tslib": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz",
"integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A=="
}
}
},
"dot-prop": { "dot-prop": {
"version": "5.3.0", "version": "5.3.0",
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz",
@ -10111,6 +10144,17 @@
"param-case": "2.1.x", "param-case": "2.1.x",
"relateurl": "0.2.x", "relateurl": "0.2.x",
"uglify-js": "3.4.x" "uglify-js": "3.4.x"
},
"dependencies": {
"param-case": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz",
"integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=",
"dev": true,
"requires": {
"no-case": "^2.2.0"
}
}
} }
}, },
"html-webpack-plugin": { "html-webpack-plugin": {
@ -13314,12 +13358,19 @@
} }
}, },
"param-case": { "param-case": {
"version": "2.1.1", "version": "3.0.4",
"resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz",
"integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=", "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==",
"dev": true,
"requires": { "requires": {
"no-case": "^2.2.0" "dot-case": "^3.0.4",
"tslib": "^2.0.3"
},
"dependencies": {
"tslib": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz",
"integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A=="
}
} }
}, },
"parent-module": { "parent-module": {

View File

@ -27,6 +27,7 @@
"@basementuniverse/kanbn": "file:~/Projects/kanbn", "@basementuniverse/kanbn": "file:~/Projects/kanbn",
"@types/dateformat": "^3.0.1", "@types/dateformat": "^3.0.1",
"dateformat": "^4.5.1", "dateformat": "^4.5.1",
"param-case": "^3.0.4",
"react": "^16.3.2", "react": "^16.3.2",
"react-beautiful-dnd": "12.2.0", "react-beautiful-dnd": "12.2.0",
"react-dom": "^16.3.2", "react-dom": "^16.3.2",

View File

@ -2,6 +2,7 @@ import { DragDropContext, Droppable } from "react-beautiful-dnd";
import React, { useState } from "react"; import React, { useState } from "react";
import Task from './Task'; import Task from './Task';
import VSCodeApi from "./VSCodeApi"; import VSCodeApi from "./VSCodeApi";
import { paramCase } from 'param-case';
declare var acquireVsCodeApi: Function; declare var acquireVsCodeApi: Function;
const vscode: VSCodeApi = acquireVsCodeApi(); const vscode: VSCodeApi = acquireVsCodeApi();
@ -77,7 +78,10 @@ const Board = ({ columns, startedColumns, completedColumns, dateFormat }: {
{Object.entries(columns).map(([columnName, column]) => { {Object.entries(columns).map(([columnName, column]) => {
return ( return (
<div <div
className="kanbn-column" className={[
'kanbn-column',
`kanbn-column-${paramCase(columnName)}`
].join(' ')}
style={{ style={{
flex: 1 flex: 1
}} }}
@ -86,11 +90,11 @@ const Board = ({ columns, startedColumns, completedColumns, dateFormat }: {
<h2 className="kanbn-column-name"> <h2 className="kanbn-column-name">
{ {
startedColumns.indexOf(columnName) > -1 && startedColumns.indexOf(columnName) > -1 &&
<i className="codicon codicon-chevron-right"></i> <i className="kanbn-column-icon codicon codicon-chevron-right"></i>
} }
{ {
completedColumns.indexOf(columnName) > -1 && completedColumns.indexOf(columnName) > -1 &&
<i className="codicon codicon-check"></i> <i className="kanbn-column-icon codicon codicon-check"></i>
} }
{columnName} {columnName}
<span className="kanbn-column-count">{column.length || ''}</span> <span className="kanbn-column-count">{column.length || ''}</span>

1
src/KanbnTask.d.ts vendored
View File

@ -9,6 +9,7 @@ declare type KanbnTask = {
metadata: { metadata: {
created: Date, created: Date,
updated?: Date, updated?: Date,
started?: Date,
completed?: Date, completed?: Date,
assigned?: string, assigned?: string,
tags?: string[] tags?: string[]

View File

@ -9,6 +9,10 @@ const Task = ({ task, index, dateFormat, vscode }: {
dateFormat: string, dateFormat: string,
vscode: VSCodeApi vscode: VSCodeApi
}) => { }) => {
const createdDate = 'created' in task.metadata ? formatDate(task.metadata.created, dateFormat) : '';
const updatedDate = 'updated' in task.metadata ? formatDate(task.metadata.updated, dateFormat) : '';
const startedDate = 'started' in task.metadata ? formatDate(task.metadata.started, dateFormat) : '';
const completedDate = 'completed' in task.metadata ? formatDate(task.metadata.completed, dateFormat) : '';
return ( return (
<Draggable <Draggable
key={task.id} key={task.id}
@ -30,76 +34,83 @@ const Task = ({ task, index, dateFormat, vscode }: {
...provided.draggableProps.style ...provided.draggableProps.style
}} }}
> >
<button <div className="kanbn-task-row">
type="button" <button
className="kanbn-task-name" type="button"
onClick={() => { className="kanbn-task-name"
vscode.postMessage({ onClick={() => {
command: 'kanbn.task', vscode.postMessage({
taskId: task.id command: 'kanbn.task',
}) taskId: task.id
}} })
title={task.id} }}
> title={task.id}
{task.name} >
</button> {task.name}
{ </button>
'tags' in task.metadata && </div>
<div className="kanbn-task-tags"> <div className="kanbn-task-row">
{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 'tags' in task.metadata &&
? formatDate(task.metadata.updated, dateFormat) <div className="kanbn-task-data kanbn-task-tags">
: formatDate(task.metadata.created, dateFormat) {task.metadata.tags!.map(tag => {
return (
<span className={[
'kanbn-task-tag',
`kanbn-task-tag-${tag}`
].join(' ')}>
{tag}
</span>
);
})}
</div>
} }
</div> </div>
<div className="kanbn-task-description"> <div className="kanbn-task-row">
{task.description} {
'assigned' in task.metadata &&
<div className="kanbn-task-data kanbn-task-assigned">
<i className="codicon codicon-account"></i>{task.metadata.assigned}
</div>
}
{
(createdDate || updatedDate) &&
<div className="kanbn-task-data kanbn-task-date" title={[
createdDate ? `Created ${createdDate}` : null,
updatedDate ? `Updated ${updatedDate}` : null,
startedDate ? `Started ${startedDate}` : null,
completedDate ? `Completed ${completedDate}` : null
].filter(i => i).join('\n')}>
<i className="codicon codicon-clock"></i>{updatedDate || createdDate}
</div>
}
{
task.comments.length > 0 &&
<div className="kanbn-task-data kanbn-task-comments">
<i className="codicon codicon-comment"></i>{task.comments.length}
</div>
}
{
task.subTasks.length > 0 &&
<div className="kanbn-task-data 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-data 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> </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> </div>
); );
}} }}

View File

@ -1,17 +1,3 @@
/*
--vscode-editor-background
--vscode-editor-foreground
--vscode-foreground
--vscode-activityBar-background
--vscode-activityBar-foreground
--vscode-activityBar-inactiveForeground
--vscode-button-background
--vscode-button-foreground
--vscode-button-hoverBackground
*/
body { body {
margin: 0; margin: 0;
padding: 1em; padding: 1em;
@ -37,16 +23,35 @@ body {
padding-left: 8px; padding-left: 8px;
} }
.kanbn-column-icon {
font-size: 0.8em !important;
margin-right: 0.5em;
}
.kanbn-create-task-button { .kanbn-create-task-button {
position: relative;
top: -2px;
left: -4px;
float: right; float: right;
border: none;
outline: none;
color: var(--vscode-foreground);
background-color: transparent;
padding: 2px;
height: 16px;
width: 16px;
margin-right: 8px;
}
.kanbn-create-task-button:focus, .kanbn-create-task-button:hover {
border: none;
outline: none;
color: var(--vscode-editor-background);
background-color: var(--vscode-foreground);
} }
.kanbn-create-task-button .codicon { .kanbn-create-task-button .codicon {
font-size: 14px; font-size: 11px !important;
}
.kanbn-column:first-child .kanbn-column-name {
padding-left: 0;
} }
.kanbn-column-count { .kanbn-column-count {
@ -61,32 +66,62 @@ body {
border-left: 3px var(--vscode-activityBar-inactiveForeground) solid; border-left: 3px var(--vscode-activityBar-inactiveForeground) solid;
} }
.kanbn-column:first-child .kanbn-column-task-list {
margin-left: 0;
}
.kanbn-column:last-child .kanbn-column-task-list {
margin-right: 0;
}
.kanbn-column-task-list.drag-over { .kanbn-column-task-list.drag-over {
border-color: var(--vscode-activityBar-foreground); border-color: var(--vscode-activityBar-foreground);
} }
.kanbn-column-backlog .kanbn-column-task-list {
border-color: #36d;
}
.kanbn-column-backlog .kanbn-column-task-list.drag-over {
border-color: #69f;
}
.kanbn-column-in-progress .kanbn-column-task-list {
border-color: #194;
}
.kanbn-column-in-progress .kanbn-column-task-list.drag-over {
border-color: #3c7;
}
.kanbn-column-todo .kanbn-column-task-list {
border-color: #eb1;
}
.kanbn-column-todo .kanbn-column-task-list.drag-over {
border-color: #fe5;
}
.kanbn-column-done .kanbn-column-task-list {
border-color: #e83;
}
.kanbn-column-done .kanbn-column-task-list.drag-over {
border-color: #fa4;
}
.kanbn-task { .kanbn-task {
position: relative;
overflow: hidden;
padding: 4px; padding: 4px;
margin: 8px; margin: 8px;
min-height: 50px; min-height: 30px;
border: 1px var(--vscode-activityBar-inactiveForeground) solid; border: 1px var(--vscode-activityBar-inactiveForeground) solid;
border-radius: 2px; border-radius: 2px;
color: var(--vscode-editor-foreground); color: var(--vscode-editor-foreground);
transition: background-color .5s ease-in-out;
} }
.kanbn-task.drag { .kanbn-task.drag {
background-color: var(--vscode-activityBar-inactiveForeground);
border-color: var(--vscode-activityBar-activeBackground); border-color: var(--vscode-activityBar-activeBackground);
} }
.kanbn-task-name { .kanbn-task-name {
margin: 0;
padding: 0;
border: none; border: none;
outline: none; outline: none;
background-color: transparent; background-color: transparent;
@ -99,3 +134,69 @@ body {
outline: none; outline: none;
text-decoration: underline; text-decoration: underline;
} }
.kanbn-task-data {
display: inline-block;
margin: 4px 8px 4px 0;
min-width: 30%;
opacity: 0.7;
font-size: 0.9em;
font-weight: normal;
font-style: italic;
}
.kanbn-task div .codicon {
position: relative;
top: 1px;
font-size: 0.9em !important;
margin-right: 4px;
}
.kanbn-task-tag {
display: inline-block;
background-color: var(--vscode-activityBar-inactiveForeground);
color: var(--vscode-editor-background);
font-weight: bold;
padding: 2px 4px;
margin: 4px 4px 4px 0;
border-radius: 2px;
}
.kanbn-task-tag-Nothing {
background-color: #aab;
color: #333;
}
.kanbn-task-tag-Tiny {
background-color: #36d;
color: #333;
}
.kanbn-task-tag-Small {
background-color: #194;
color: #333;
}
.kanbn-task-tag-Medium {
background-color: #eb1;
color: #333;
}
.kanbn-task-tag-Large {
background-color: #e83;
color: #333;
}
.kanbn-task-tag-Huge {
background-color: #f42;
color: #333;
}
.kanbn-task-progress {
position: absolute;
bottom: -2px;
left: 0;
height: 4px;
background-color: #3c7;
opacity: 0.7;
}