Compare commits

...

10 Commits

Author SHA1 Message Date
2ffd43908e # 0.12.2 2023-05-30 11:59:24 -07:00
5cb3b39764 # 0.12.1 2023-05-25 21:35:18 -06:00
c0930f4780 # 0.12.0
* First release with the following changes
2023-05-25 13:09:54 -06:00
Gordon
f85619450f Fix typo in docs 2021-06-08 20:44:02 +01:00
Gordon
5fbfb0f740 0.11.0 2021-06-08 18:05:30 +01:00
Gordon
abe249c20a Update docs 2021-06-08 15:23:59 +01:00
Gordon
c1f12c3ec0 Update changelog 2021-06-08 14:02:01 +01:00
Gordon
58cf1eb003 Filter by custom fields 2021-06-08 13:59:54 +01:00
Gordon
0a7918732e Update changelog 2021-06-08 01:53:09 +01:00
Gordon
026fbf95cd Custom field editing 2021-06-08 01:51:57 +01:00
34 changed files with 33173 additions and 696 deletions

28
.kanbn/board.css Normal file
View File

@ -0,0 +1,28 @@
.kanbn-column-wip .kanbn-column-task-list {
border-color: #6929c4;
}
.kanbn-column-next .kanbn-column-task-list {
border-color: #1192e8;
}
.kanbn-column-wait .kanbn-column-task-list {
border-color: #005d5d;
}
.kanbn-column-later .kanbn-column-task-list {
border-color: #9f1853;
}
.kanbn-column-maybe .kanbn-column-task-list {
border-color: #fa4d56;
}
.kanbn-column-friday .kanbn-column-task-list {
border-color: #570408;
}
.kanbn-column-done .kanbn-column-task-list {
border-color: #198038;
}
.kanbn-task-data-workload {
display: none;
}
.kanbn-task-data-relation {
display: block;
color: #198038;
}

30
.kanbn/index.md Normal file
View File

@ -0,0 +1,30 @@
---
startedColumns:
- 'In Progress'
completedColumns:
- Done
---
# VSCode-Kanbn
## Backlog
- [review-save-options](tasks/review-save-options.md)
- [can-this-run-in-the-browser](tasks/can-this-run-in-the-browser.md)
- [how-do-i-use-local-javascript-project-reference](tasks/how-do-i-use-local-javascript-project-reference.md)
## Todo
- [shrink-app-size](tasks/shrink-app-size.md)
## In Progress
## Done
- [add-link-to-raw-in-panel-view](tasks/add-link-to-raw-in-panel-view.md)
- [add-raw-button-in-create-new-task](tasks/add-raw-button-in-create-new-task.md)
- [make-tags-linkable](tasks/make-tags-linkable.md)
- [show-relations-on-the-item-card](tasks/show-relations-on-the-item-card.md)
- [understand-how-relations-work](tasks/understand-how-relations-work.md)
- [review-adding-more-on-item-card](tasks/review-adding-more-on-item-card.md)
- [remove-relations-in-form](tasks/remove-relations-in-form.md)

View File

@ -0,0 +1,14 @@
---
created: 2023-05-25T04:00:46.616Z
updated: 2023-05-25T04:50:52.235Z
assigned: ""
progress: 0
tags: []
started: 2023-05-25T04:41:03.902Z
completed: 2023-05-25T04:50:44.587Z
---
# Add link to raw in panel view
- Double click event
- Extra button

View File

@ -0,0 +1,11 @@
---
created: 2023-05-25T04:00:30.417Z
updated: 2023-05-25T04:50:46.136Z
assigned: ""
progress: 0
tags: []
started: 2023-05-22T00:00:00.000Z
completed: 2023-05-25T04:50:46.136Z
---
# Add raw button in Create new task

View File

@ -0,0 +1,9 @@
---
created: 2023-05-25T04:04:06.760Z
updated: 2023-05-25T04:04:06.753Z
assigned: ""
progress: 0
tags: []
---
# Can this run in the browser?

View File

@ -0,0 +1,19 @@
---
created: 2023-05-25T18:54:00.309Z
updated: 2023-05-25T18:55:32.671Z
assigned: ""
progress: 0
tags: []
started: 2023-05-25T18:55:32.671Z
---
# Fix relations bug
I started to look at th kanbn project all in javascript but I don't know how to reference it
## Comments
- date: 2023-05-25T18:54:44.808Z
```bash
node bin\kanbn add -n Mike
```

View File

@ -0,0 +1,9 @@
---
created: 2023-05-26T03:47:27.942Z
updated: 2023-05-26T03:47:27.933Z
assigned: ""
progress: 0
tags: []
---
# How do I use local javascript project reference

View File

@ -0,0 +1,9 @@
---
created: 2023-05-25T04:05:13.848Z
updated: 2023-05-25T04:05:33.759Z
assigned: ""
progress: 0
column: Backlog
---
# Make tags linkable

View File

@ -0,0 +1,18 @@
---
created: 2023-05-30T18:38:37.231Z
updated: 2023-05-30T18:54:59.604Z
assigned: ""
progress: 1
tags: []
started: 2023-05-28T00:00:00.000Z
completed: 2023-05-30T00:00:00.000Z
---
# Remove relations in form
## Comments
- date: 2023-05-30T18:42:55.772Z
Added count to button title
Used false
see false && values.relations.length > 0 && values.relations.map((relation, index) =>

View File

@ -0,0 +1,14 @@
---
created: 2023-05-25T04:01:15.185Z
updated: 2023-05-26T03:27:06.579Z
assigned: ""
progress: 0
tags:
- Maybe
completed: 2023-05-26T03:26:02.606Z
---
# Review adding more on item card
- Maybe not needed because that is what tags are meant for
- Buy maybe so because I don't like tags in the metadata

View File

@ -0,0 +1,9 @@
---
created: 2023-05-25T04:01:05.154Z
updated: 2023-05-25T04:01:05.146Z
assigned: ""
progress: 0
tags: []
---
# Review save options

View File

@ -0,0 +1,15 @@
---
created: 2023-05-25T04:59:37.306Z
updated: 2023-05-25T18:52:55.747Z
assigned: ""
progress: 0
tags: []
started: 2023-05-24T00:00:00.000Z
completed: 2023-05-25T18:52:55.747Z
---
# Show relations on the item card
## Sub-tasks
- [ ] make clickable

View File

@ -0,0 +1,9 @@
---
created: 2023-05-25T04:03:54.530Z
updated: 2023-05-25T18:55:36.452Z
assigned: ""
progress: 0
tags: []
---
# Shrink app size

View File

@ -0,0 +1,25 @@
---
created: 2023-05-25T18:54:00.309Z
updated: 2023-05-26T03:26:57.786Z
assigned: ""
progress: 0
tags: []
started: 2023-05-25T00:00:00.000Z
completed: 2023-05-26T03:25:48.979Z
---
# Understand how relations work
I started to look at th kanbn project all in javascript but I don't know how to reference it
## Relations
- [a b](b.md)
- [c d](d.md)
## Comments
- date: 2023-05-25T18:54:44.808Z
```bash
node bin\kanbn add -n Mike
```

View File

@ -8,5 +8,6 @@
}, },
"typescript.tsdk": "./node_modules/typescript/lib", // we want to use the TS server from our node_modules folder to control its version "typescript.tsdk": "./node_modules/typescript/lib", // we want to use the TS server from our node_modules folder to control its version
"typescript.tsc.autoDetect": "off", "typescript.tsc.autoDetect": "off",
"todo-tree.tree.scanMode": "workspace only" // Turn off tsc task auto detection since we have the necessary task as npm scripts "todo-tree.tree.scanMode": "workspace only",
"cSpell.enabled": false // Turn off tsc task auto detection since we have the necessary task as npm scripts
} }

View File

@ -1,10 +1,35 @@
# 0.12.3
* See board ...
# 0.12.2
* See below
- [remove-relations-in-form](tasks/remove-relations-in-form.md)
# 0.12.1
* See below
- [understand-how-relations-work](tasks/understand-how-relations-work.md)
- [review-adding-more-on-item-card](tasks/review-adding-more-on-item-card.md)
# 0.12.0
* First release with the following changes
- [add-link-to-raw-in-panel-view](.kanbn/tasks/add-link-to-raw-in-panel-view.md)
- [add-raw-button-in-create-new-task](.kanbn/tasks/add-raw-button-in-create-new-task.md)
- [make-tags-linkable](.kanbn/tasks/make-tags-linkable.md)
- [show-relations-on-the-item-card](.kanbn/tasks/show-relations-on-the-item-card.md)
# 0.11.0 # 0.11.0
* Added KaTeX support to task markdown (description and comments) using `$$...$$` for blocks and `$...$` for inline * KaTeX support in task markdown (description and comments) using `$$...$$` for blocks and `$...$` for inline
* Added syntax highlighting to code blocks in task markdown (description and comments) * Syntax highlighting for code blocks in task markdown (description and comments)
* Added column sorting * Columns can now be sorted, with the ability to optionally save sort settings per column
* Added relations and custom fields to task cards * Added relations and custom fields to task cards
* Added task card customisation using `board.css` * Task cards can be customised using `board.css`
* Custom fields can be modified using task editor
* Board can be filtered by custom field values
# 0.10.0 # 0.10.0

View File

@ -1,7 +1,5 @@
# Kanbn Extension for Visual Studio Code # Kanbn Extension for Visual Studio Code
![Marketplace version](https://vsmarketplacebadge.apphb.com/version-short/gordonlarrigan.vscode-kanbn.svg?color=lightblue) ![Marketplace installs](https://vsmarketplacebadge.apphb.com/installs-short/gordonlarrigan.vscode-kanbn.svg?color=lightblue) ![Marketplace rating](https://vsmarketplacebadge.apphb.com/rating-short/gordonlarrigan.vscode-kanbn.svg?color=lightblue) [![MIT License](https://img.shields.io/github/license/basementuniverse/vscode-kanbn?color=orange)](https://opensource.org/licenses/MIT)
This extension adds a [Kanbn](https://www.npmjs.com/package/@basementuniverse/kanbn)-powered kanban board to Visual Studio Code. This extension adds a [Kanbn](https://www.npmjs.com/package/@basementuniverse/kanbn)-powered kanban board to Visual Studio Code.
![Kanbn](docs/preview.gif "Kanbn") ![Kanbn](docs/preview.gif "Kanbn")
@ -32,20 +30,6 @@ Click on a task's title to open the task editor in a new tab. From here, you can
You can also modify the index or task files directly, or by using Kanbn CLI commands, and the Kanbn board should update automatically to reflect these changes. You can also modify the index or task files directly, or by using Kanbn CLI commands, and the Kanbn board should update automatically to reflect these changes.
## Filtering the Kanbn board
At the top-right of the Kanbn board there is a filter input. To filter visible tasks, enter a filter string and click the filter button (or press Enter).
### Filter string syntax
Text entered into the filter string input will be tested against each task's `id` and `name` fields. To filter on other fields, try the following:
- `overdue` will filter all tasks that have a due date in the past
- `description:search-string` will filter for tasks that contain `search-string` in their description or sub-tasks
- `assigned:search-string` will filter for tasks that contain `search-string` in their assigned user
- `tag:search-string` will filter for tasks that contain `search-string` in one of their tags
- `relation:search-string` will filter for tasks that contain `search-string` in one of their relations (either the relation type or related task id)
## Commands ## Commands
The following commands are available: The following commands are available:
@ -66,6 +50,40 @@ The following configuration settings are available:
- `kanbn.showSprintButton` when set to `true`, a 'Start sprint` button will will appear above the Kanbn board. This button will show the current sprint name if a sprint is currently active, and can be used to start a new sprint. - `kanbn.showSprintButton` when set to `true`, a 'Start sprint` button will will appear above the Kanbn board. This button will show the current sprint name if a sprint is currently active, and can be used to start a new sprint.
- `kanbn.showBurndownButton` when set to `true`, a 'Show burndown chart` button will appear above the Kanbn board. - `kanbn.showBurndownButton` when set to `true`, a 'Show burndown chart` button will appear above the Kanbn board.
## Filtering the Kanbn board
At the top-right of the Kanbn board there is a filter input. To filter visible tasks, enter a filter string and click the filter button (or press Enter).
### Filter string syntax
Text entered into the filter string input will be tested against each task's `id` and `name` fields. To filter on other fields, try the following:
- `overdue` will filter all tasks that have a due date in the past
- `description:search-string` will filter for tasks that contain `search-string` in their description or sub-tasks
- `assigned:search-string` will filter for tasks that contain `search-string` in their assigned user
- `tag:search-string` will filter for tasks that contain `search-string` in one of their tags
- `relation:search-string` will filter for tasks that contain `search-string` in one of their relations (either the relation type or related task id)
- `subtask:search-string` will filter for tasks that contain `search-string` in one of their sub-tasks
- `comment:search-string` will filter for tasks that contain `search-string` in one of their comments (either the comment author or text)
- `{custom field name}:search-string` will filter for tasks that have a custom field in their metadata that contains `search-string` in its value
- `{boolean custom field name}` will filter for tasks that have a boolean custom field in their metadata set to true
#### Examples
For these examples, assume we have a string custom field 'MyCustomField' and a boolean custom field 'MyCustomFlag' defined in the project options, i.e. `index.md` will contain:
```yaml
customFields:
- name: MyCustomField
type: string
- name: MyCustomFlag
type: boolean
```
(See https://github.com/basementuniverse/kanbn/blob/master/docs/index-structure.md for more information on custom fields)
- `assigned:testperson tag:large mycustomflag` will show tasks that are assigned to `testperson` and have a tag `Large` (search terms are case-insensitive) and have `MyCustomFlag` set to true
- `mycustomfield:test123 some title` will show tasks that have both `some` and `title` in their name/id and have a `MyCustomField` field that contains `test123`
## Styling the Kanbn board ## Styling the Kanbn board
This extension has been tested using various themes (light, dark and high-contrast), so it should always look somewhat presentable. However, if you'd like to set your own styles you can do so by creating a CSS file called `board.css` in the Kanbn directory. [Check here](docs/styles.md) for more information. This extension has been tested using various themes (light, dark and high-contrast), so it should always look somewhat presentable. However, if you'd like to set your own styles you can do so by creating a CSS file called `board.css` in the Kanbn directory. [Check here](docs/styles.md) for more information.

View File

@ -102,7 +102,7 @@ Here's an example of a task card style using some of the above features:
- `kanbn-task-tag` - `kanbn-task-tag`
- `kanbn-task-tag-{Tag name in param-case}` - `kanbn-task-tag-{Tag name in param-case}`
- `kanbn-task-data-custom-field` - `kanbn-task-data-custom-field`
- `kanbn-task-data-{Custom field name in param-case} - `kanbn-task-data-{Custom field name in param-case}`
- `kanbn-task-data-assigned` - `kanbn-task-data-assigned`
- `kanbn-task-data-created` - `kanbn-task-data-created`
- `kanbn-task-data-updated` - `kanbn-task-data-updated`
@ -129,6 +129,9 @@ Here's an example of a task card style using some of the above features:
- `kanbn-task-editor-field-label` - `kanbn-task-editor-field-label`
- `kanbn-task-editor-field-label-description` - `kanbn-task-editor-field-label-description`
- `kanbn-task-editor-field-input` - `kanbn-task-editor-field-input`
- `kanbn-task-editor-custom-field`
- `kanbn-task-editor-custom-field-{Custom field name in param-case}`
- `kanbn-task-editor-custom-checkbox`
- `kanbn-task-editor-id` - `kanbn-task-editor-id`
- `kanbn-task-editor-description-preview` - `kanbn-task-editor-description-preview`
- `kanbn-task-editor-button-edit-description` - `kanbn-task-editor-button-edit-description`
@ -145,6 +148,7 @@ Here's an example of a task card style using some of the above features:
- `kanbn-task-editor-button` - `kanbn-task-editor-button`
- `kanbn-task-editor-button-delete` - `kanbn-task-editor-button-delete`
- `kanbn-task-editor-buttons` - `kanbn-task-editor-buttons`
- `kanbn-task-editor-main-buttons`
- `kanbn-task-editor-button-add` - `kanbn-task-editor-button-add`
- `kanbn-task-editor-button-edit` - `kanbn-task-editor-button-edit`
- `kanbn-task-editor-field-relations` - `kanbn-task-editor-field-relations`

View File

@ -1,5 +1,5 @@
import * as path from "path"; import { join } from "path";
import * as vscode from "vscode"; import { WebviewPanel, Disposable, window, ViewColumn, workspace, Uri, commands } from "vscode";
import getNonce from "./getNonce"; import getNonce from "./getNonce";
import KanbnTaskPanel from "./KanbnTaskPanel"; import KanbnTaskPanel from "./KanbnTaskPanel";
import KanbnBurndownPanel from "./KanbnBurndownPanel"; import KanbnBurndownPanel from "./KanbnBurndownPanel";
@ -24,12 +24,12 @@ export default class KanbnBoardPanel {
private static readonly viewType = "react"; private static readonly viewType = "react";
private readonly _panel: vscode.WebviewPanel; private readonly _panel: WebviewPanel;
private readonly _extensionPath: string; private readonly _extensionPath: string;
private readonly _workspacePath: string; private readonly _workspacePath: string;
private readonly _kanbn: typeof import("@basementuniverse/kanbn/src/main"); private readonly _kanbn: typeof import("@basementuniverse/kanbn/src/main");
private readonly _kanbnFolderName: string; private readonly _kanbnFolderName: string;
private _disposables: vscode.Disposable[] = []; private _disposables: Disposable[] = [];
public static createOrShow( public static createOrShow(
extensionPath: string, extensionPath: string,
@ -37,7 +37,7 @@ export default class KanbnBoardPanel {
kanbn: typeof import("@basementuniverse/kanbn/src/main"), kanbn: typeof import("@basementuniverse/kanbn/src/main"),
kanbnFolderName: string kanbnFolderName: string
) { ) {
const column = vscode.window.activeTextEditor ? vscode.window.activeTextEditor.viewColumn : undefined; const column = window.activeTextEditor ? window.activeTextEditor.viewColumn : undefined;
// If we already have a panel, show it, otherwise create a new panel // If we already have a panel, show it, otherwise create a new panel
if (KanbnBoardPanel.currentPanel) { if (KanbnBoardPanel.currentPanel) {
@ -46,7 +46,7 @@ export default class KanbnBoardPanel {
KanbnBoardPanel.currentPanel = new KanbnBoardPanel( KanbnBoardPanel.currentPanel = new KanbnBoardPanel(
extensionPath, extensionPath,
workspacePath, workspacePath,
column || vscode.ViewColumn.One, column || ViewColumn.One,
kanbn, kanbn,
kanbnFolderName kanbnFolderName
); );
@ -59,7 +59,7 @@ export default class KanbnBoardPanel {
try { try {
index = await KanbnBoardPanel.currentPanel._kanbn.getIndex(); index = await KanbnBoardPanel.currentPanel._kanbn.getIndex();
} catch (error) { } catch (error) {
vscode.window.showErrorMessage(error instanceof Error ? error.message : error); window.showErrorMessage(error instanceof Error ? error.message : error);
return; return;
} }
let tasks: any[]; let tasks: any[];
@ -68,7 +68,7 @@ export default class KanbnBoardPanel {
KanbnBoardPanel.currentPanel!._kanbn.hydrateTask(index, task) KanbnBoardPanel.currentPanel!._kanbn.hydrateTask(index, task)
); );
} catch (error) { } catch (error) {
vscode.window.showErrorMessage(error instanceof Error ? error.message : error); window.showErrorMessage(error instanceof Error ? error.message : error);
return; return;
} }
KanbnBoardPanel.currentPanel._panel.webview.postMessage({ KanbnBoardPanel.currentPanel._panel.webview.postMessage({
@ -81,8 +81,8 @@ export default class KanbnBoardPanel {
columnSorting: index.options.columnSorting ?? {}, columnSorting: index.options.columnSorting ?? {},
customFields: index.options.customFields ?? [], customFields: index.options.customFields ?? [],
dateFormat: KanbnBoardPanel.currentPanel._kanbn.getDateFormat(index), dateFormat: KanbnBoardPanel.currentPanel._kanbn.getDateFormat(index),
showBurndownButton: vscode.workspace.getConfiguration("kanbn").get("showBurndownButton"), showBurndownButton: workspace.getConfiguration("kanbn").get("showBurndownButton"),
showSprintButton: vscode.workspace.getConfiguration("kanbn").get("showSprintButton"), showSprintButton: workspace.getConfiguration("kanbn").get("showSprintButton"),
}); });
} }
} }
@ -90,7 +90,7 @@ export default class KanbnBoardPanel {
private constructor( private constructor(
extensionPath: string, extensionPath: string,
workspacePath: string, workspacePath: string,
column: vscode.ViewColumn, column: ViewColumn,
kanbn: typeof import("@basementuniverse/kanbn/src/main"), kanbn: typeof import("@basementuniverse/kanbn/src/main"),
kanbnFolderName: string kanbnFolderName: string
) { ) {
@ -100,7 +100,7 @@ export default class KanbnBoardPanel {
this._kanbnFolderName = kanbnFolderName; this._kanbnFolderName = kanbnFolderName;
// Create and show a new webview panel // Create and show a new webview panel
this._panel = vscode.window.createWebviewPanel(KanbnBoardPanel.viewType, "Kanbn Board", column, { this._panel = window.createWebviewPanel(KanbnBoardPanel.viewType, "Kanbn Board", column, {
// Enable javascript in the webview // Enable javascript in the webview
enableScripts: true, enableScripts: true,
@ -109,14 +109,14 @@ export default class KanbnBoardPanel {
// Restrict the webview to only loading content from allowed paths // Restrict the webview to only loading content from allowed paths
localResourceRoots: [ localResourceRoots: [
vscode.Uri.file(path.join(this._extensionPath, "build")), Uri.file(join(this._extensionPath, "build")),
vscode.Uri.file(path.join(this._workspacePath, this._kanbnFolderName)), Uri.file(join(this._workspacePath, this._kanbnFolderName)),
vscode.Uri.file(path.join(this._extensionPath, "node_modules", "vscode-codicons", "dist")), Uri.file(join(this._extensionPath, "node_modules", "vscode-codicons", "dist")),
], ],
}); });
(this._panel as any).iconPath = { (this._panel as any).iconPath = {
light: vscode.Uri.file(path.join(this._extensionPath, "resources", "project_light.svg")), light: Uri.file(join(this._extensionPath, "resources", "project_light.svg")),
dark: vscode.Uri.file(path.join(this._extensionPath, "resources", "project_dark.svg")), dark: Uri.file(join(this._extensionPath, "resources", "project_dark.svg")),
}; };
// Set the webview's title to the kanbn project name // Set the webview's title to the kanbn project name
@ -138,7 +138,7 @@ export default class KanbnBoardPanel {
// Display error message // Display error message
case "error": case "error":
vscode.window.showErrorMessage(message.text); window.showErrorMessage(message.text);
return; return;
// Open a task in the editor // Open a task in the editor
@ -153,12 +153,18 @@ export default class KanbnBoardPanel {
); );
return; return;
// Go to raw task in the editor
case "kanbn.goToRaw":
let uri = Uri.file(join(this._workspacePath, ".kanbn", "tasks", message.taskId + ".md"));
await commands.executeCommand('vscode.open', uri);
return;
// Move a task // Move a task
case "kanbn.move": case "kanbn.move":
try { try {
await kanbn.moveTask(message.task, message.columnName, message.position); await kanbn.moveTask(message.task, message.columnName, message.position);
} catch (e) { } catch (e) {
vscode.window.showErrorMessage(e.message); window.showErrorMessage(e.message);
} }
return; return;
@ -185,7 +191,7 @@ export default class KanbnBoardPanel {
); );
} }
// Prompt for a task property to sort by // Prompt for a task property to sort by
const sortBy: string = await vscode.window.showQuickPick( const sortBy = await window.showQuickPick(
[ [
'None', 'None',
...Object.keys(sortByFields), ...Object.keys(sortByFields),
@ -204,7 +210,7 @@ export default class KanbnBoardPanel {
} }
// Prompt for sort direction and save settings // Prompt for sort direction and save settings
const sortDirection = await vscode.window.showQuickPick( const sortDirection = await window.showQuickPick(
[ [
'Ascending', 'Ascending',
'Descending', 'Descending',
@ -215,7 +221,7 @@ export default class KanbnBoardPanel {
} }
); );
if (sortDirection !== undefined) { if (sortDirection !== undefined) {
const saveSort = await vscode.window.showQuickPick( const saveSort = await window.showQuickPick(
[ [
"Yes", "Yes",
"No", "No",
@ -256,7 +262,7 @@ export default class KanbnBoardPanel {
// Start a new sprint // Start a new sprint
case "kanbn.sprint": case "kanbn.sprint":
// Prompt for a sprint name // Prompt for a sprint name
const newSprintName = await vscode.window.showInputBox({ const newSprintName = await window.showInputBox({
placeHolder: "The sprint name.", placeHolder: "The sprint name.",
}); });
@ -265,7 +271,7 @@ export default class KanbnBoardPanel {
try { try {
await kanbn.sprint(newSprintName, "", new Date()); await kanbn.sprint(newSprintName, "", new Date());
} catch (e) { } catch (e) {
vscode.window.showErrorMessage(e.message); window.showErrorMessage(e.message);
} }
} }
KanbnBurndownPanel.update(); KanbnBurndownPanel.update();
@ -291,20 +297,20 @@ export default class KanbnBoardPanel {
} }
private _getHtmlForWebview() { private _getHtmlForWebview() {
const manifest = require(path.join(this._extensionPath, "build", "asset-manifest.json")); const manifest = require(join(this._extensionPath, "build", "asset-manifest.json"));
const mainScript = manifest["main.js"]; const mainScript = manifest["main.js"];
const mainStyle = manifest["main.css"]; const mainStyle = manifest["main.css"];
const scriptUri = vscode.Uri.file(path.join(this._extensionPath, "build", mainScript)).with({ const scriptUri = Uri.file(join(this._extensionPath, "build", mainScript)).with({
scheme: "vscode-resource", scheme: "vscode-resource",
}); });
const styleUri = vscode.Uri.file(path.join(this._extensionPath, "build", mainStyle)).with({ const styleUri = Uri.file(join(this._extensionPath, "build", mainStyle)).with({
scheme: "vscode-resource", scheme: "vscode-resource",
}); });
const customStyleUri = vscode.Uri.file( const customStyleUri = Uri.file(
path.join(this._workspacePath, this._kanbnFolderName, "board.css") join(this._workspacePath, this._kanbnFolderName, "board.css")
).with({ scheme: "vscode-resource" }); ).with({ scheme: "vscode-resource" });
const codiconsUri = vscode.Uri.file( const codiconsUri = Uri.file(
path.join(this._extensionPath, "node_modules", "vscode-codicons", "dist", "codicon.css") join(this._extensionPath, "node_modules", "vscode-codicons", "dist", "codicon.css")
).with({ scheme: "vscode-resource" }); ).with({ scheme: "vscode-resource" });
// Use a nonce to whitelist which scripts can be run // Use a nonce to whitelist which scripts can be run
@ -321,7 +327,7 @@ export default class KanbnBoardPanel {
<link rel="stylesheet" type="text/css" href="${customStyleUri}"> <link rel="stylesheet" type="text/css" href="${customStyleUri}">
<link rel="stylesheet" type="text/css" href="${codiconsUri}"> <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:;"> <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" })}/"> <base href="${Uri.file(join(this._extensionPath, "build")).with({ scheme: "vscode-resource" })}/">
</head> </head>
<body> <body>
<noscript>You need to enable JavaScript to run this app.</noscript> <noscript>You need to enable JavaScript to run this app.</noscript>

View File

@ -1,5 +1,5 @@
import * as path from "path"; import { join } from "path";
import * as vscode from "vscode"; import { WebviewPanel, Disposable, window, ViewColumn, Uri } from "vscode";
import getNonce from "./getNonce"; import getNonce from "./getNonce";
export default class KanbnBurndownPanel { export default class KanbnBurndownPanel {
@ -7,7 +7,7 @@ export default class KanbnBurndownPanel {
private static readonly viewType = "react"; private static readonly viewType = "react";
private readonly _panel: vscode.WebviewPanel; private readonly _panel: WebviewPanel;
private readonly _extensionPath: string; private readonly _extensionPath: string;
private readonly _workspacePath: string; private readonly _workspacePath: string;
private readonly _kanbn: typeof import("@basementuniverse/kanbn/src/main"); private readonly _kanbn: typeof import("@basementuniverse/kanbn/src/main");
@ -16,7 +16,7 @@ export default class KanbnBurndownPanel {
private sprint: string = ''; private sprint: string = '';
private startDate: string = ''; private startDate: string = '';
private endDate: string = ''; private endDate: string = '';
private _disposables: vscode.Disposable[] = []; private _disposables: Disposable[] = [];
public static async createOrShow( public static async createOrShow(
extensionPath: string, extensionPath: string,
@ -24,7 +24,7 @@ export default class KanbnBurndownPanel {
kanbn: typeof import("@basementuniverse/kanbn/src/main"), kanbn: typeof import("@basementuniverse/kanbn/src/main"),
kanbnFolderName: string kanbnFolderName: string
) { ) {
const column = vscode.window.activeTextEditor ? vscode.window.activeTextEditor.viewColumn : undefined; const column = window.activeTextEditor ? window.activeTextEditor.viewColumn : undefined;
// If we already have a panel, show it, otherwise create a new panel // If we already have a panel, show it, otherwise create a new panel
if (KanbnBurndownPanel.currentPanel) { if (KanbnBurndownPanel.currentPanel) {
@ -33,7 +33,7 @@ export default class KanbnBurndownPanel {
KanbnBurndownPanel.currentPanel = new KanbnBurndownPanel( KanbnBurndownPanel.currentPanel = new KanbnBurndownPanel(
extensionPath, extensionPath,
workspacePath, workspacePath,
column || vscode.ViewColumn.One, column || ViewColumn.One,
kanbn, kanbn,
kanbnFolderName kanbnFolderName
); );
@ -46,7 +46,7 @@ export default class KanbnBurndownPanel {
try { try {
index = await KanbnBurndownPanel.currentPanel._kanbn.getIndex(); index = await KanbnBurndownPanel.currentPanel._kanbn.getIndex();
} catch (error) { } catch (error) {
vscode.window.showErrorMessage(error instanceof Error ? error.message : error); window.showErrorMessage(error instanceof Error ? error.message : error);
return; return;
} }
KanbnBurndownPanel.currentPanel._panel.webview.postMessage({ KanbnBurndownPanel.currentPanel._panel.webview.postMessage({
@ -78,7 +78,7 @@ export default class KanbnBurndownPanel {
private constructor( private constructor(
extensionPath: string, extensionPath: string,
workspacePath: string, workspacePath: string,
column: vscode.ViewColumn, column: ViewColumn,
kanbn: typeof import("@basementuniverse/kanbn/src/main"), kanbn: typeof import("@basementuniverse/kanbn/src/main"),
kanbnFolderName: string kanbnFolderName: string
) { ) {
@ -88,7 +88,7 @@ export default class KanbnBurndownPanel {
this._kanbnFolderName = kanbnFolderName; this._kanbnFolderName = kanbnFolderName;
// Create and show a new webview panel // Create and show a new webview panel
this._panel = vscode.window.createWebviewPanel(KanbnBurndownPanel.viewType, "Burndown Chart", column, { this._panel = window.createWebviewPanel(KanbnBurndownPanel.viewType, "Burndown Chart", column, {
// Enable javascript in the webview // Enable javascript in the webview
enableScripts: true, enableScripts: true,
@ -97,14 +97,14 @@ export default class KanbnBurndownPanel {
// Restrict the webview to only loading content from allowed paths // Restrict the webview to only loading content from allowed paths
localResourceRoots: [ localResourceRoots: [
vscode.Uri.file(path.join(this._extensionPath, "build")), Uri.file(join(this._extensionPath, "build")),
vscode.Uri.file(path.join(this._workspacePath, this._kanbnFolderName)), Uri.file(join(this._workspacePath, this._kanbnFolderName)),
vscode.Uri.file(path.join(this._extensionPath, "node_modules", "vscode-codicons", "dist")), Uri.file(join(this._extensionPath, "node_modules", "vscode-codicons", "dist")),
], ],
}); });
(this._panel as any).iconPath = { (this._panel as any).iconPath = {
light: vscode.Uri.file(path.join(this._extensionPath, "resources", "burndown_light.svg")), light: Uri.file(join(this._extensionPath, "resources", "burndown_light.svg")),
dark: vscode.Uri.file(path.join(this._extensionPath, "resources", "burndown_dark.svg")), dark: Uri.file(join(this._extensionPath, "resources", "burndown_dark.svg")),
}; };
// Set the webview's title to the kanbn project name // Set the webview's title to the kanbn project name
@ -126,7 +126,7 @@ export default class KanbnBurndownPanel {
// Display error message // Display error message
case "error": case "error":
vscode.window.showErrorMessage(message.text); window.showErrorMessage(message.text);
return; return;
// Refresh the kanbn chart // Refresh the kanbn chart
@ -158,20 +158,20 @@ export default class KanbnBurndownPanel {
} }
private _getHtmlForWebview() { private _getHtmlForWebview() {
const manifest = require(path.join(this._extensionPath, "build", "asset-manifest.json")); const manifest = require(join(this._extensionPath, "build", "asset-manifest.json"));
const mainScript = manifest["main.js"]; const mainScript = manifest["main.js"];
const mainStyle = manifest["main.css"]; const mainStyle = manifest["main.css"];
const scriptUri = vscode.Uri.file(path.join(this._extensionPath, "build", mainScript)).with({ const scriptUri = Uri.file(join(this._extensionPath, "build", mainScript)).with({
scheme: "vscode-resource", scheme: "vscode-resource",
}); });
const styleUri = vscode.Uri.file(path.join(this._extensionPath, "build", mainStyle)).with({ const styleUri = Uri.file(join(this._extensionPath, "build", mainStyle)).with({
scheme: "vscode-resource", scheme: "vscode-resource",
}); });
const customStyleUri = vscode.Uri.file( const customStyleUri = Uri.file(
path.join(this._workspacePath, this._kanbnFolderName, "board.css") join(this._workspacePath, this._kanbnFolderName, "board.css")
).with({ scheme: "vscode-resource" }); ).with({ scheme: "vscode-resource" });
const codiconsUri = vscode.Uri.file( const codiconsUri = Uri.file(
path.join(this._extensionPath, "node_modules", "vscode-codicons", "dist", "codicon.css") join(this._extensionPath, "node_modules", "vscode-codicons", "dist", "codicon.css")
).with({ scheme: "vscode-resource" }); ).with({ scheme: "vscode-resource" });
// Use a nonce to whitelist which scripts can be run // Use a nonce to whitelist which scripts can be run
@ -188,7 +188,7 @@ export default class KanbnBurndownPanel {
<link rel="stylesheet" type="text/css" href="${customStyleUri}"> <link rel="stylesheet" type="text/css" href="${customStyleUri}">
<link rel="stylesheet" type="text/css" href="${codiconsUri}"> <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:;"> <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" })}/"> <base href="${Uri.file(join(this._extensionPath, "build")).with({ scheme: "vscode-resource" })}/">
</head> </head>
<body> <body>
<noscript>You need to enable JavaScript to run this app.</noscript> <noscript>You need to enable JavaScript to run this app.</noscript>

View File

@ -1,15 +1,15 @@
import { status } from '@basementuniverse/kanbn/src/main'; import { status } from '@basementuniverse/kanbn/src/main';
import * as vscode from 'vscode'; import { StatusBarItem, ExtensionContext, window, StatusBarAlignment, workspace } from 'vscode';
export default class KanbnStatusBarItem { export default class KanbnStatusBarItem {
private readonly _statusBarItem: vscode.StatusBarItem; private readonly _statusBarItem: StatusBarItem;
private readonly _kanbn: typeof import('@basementuniverse/kanbn/src/main'); private readonly _kanbn: typeof import('@basementuniverse/kanbn/src/main');
constructor( constructor(
context: vscode.ExtensionContext, context: ExtensionContext,
kanbn: typeof import('@basementuniverse/kanbn/src/main') kanbn: typeof import('@basementuniverse/kanbn/src/main')
) { ) {
this._statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 0); this._statusBarItem = window.createStatusBarItem(StatusBarAlignment.Left, 0);
context.subscriptions.push(this._statusBarItem); context.subscriptions.push(this._statusBarItem);
this._kanbn = kanbn; this._kanbn = kanbn;
} }
@ -28,7 +28,7 @@ export default class KanbnStatusBarItem {
const text = [ const text = [
`$(project) ${status.tasks}` `$(project) ${status.tasks}`
]; ];
let tooltip = []; let tooltip: any = [];
if (status.tasks > 0) { if (status.tasks > 0) {
tooltip = [ tooltip = [
`${status.tasks} task${status.tasks === 1 ? '' : 's'}` `${status.tasks} task${status.tasks === 1 ? '' : 's'}`
@ -52,7 +52,7 @@ export default class KanbnStatusBarItem {
this._statusBarItem.text = '$(project)'; this._statusBarItem.text = '$(project)';
this._statusBarItem.tooltip = 'Initialise Kanbn'; this._statusBarItem.tooltip = 'Initialise Kanbn';
this._statusBarItem.command = 'kanbn.init'; this._statusBarItem.command = 'kanbn.init';
if (vscode.workspace.getConfiguration('kanbn').get('showUninitialisedStatusBarItem')) { if (workspace.getConfiguration('kanbn').get('showUninitialisedStatusBarItem')) {
this._statusBarItem.show(); this._statusBarItem.show();
} else { } else {
this._statusBarItem.hide(); this._statusBarItem.hide();

View File

@ -1,9 +1,12 @@
import * as path from "path"; import { join } from "path";
import * as vscode from "vscode"; import { WebviewPanel, Disposable, window, ViewColumn, Uri, workspace, commands } from "vscode";
import getNonce from "./getNonce"; import getNonce from "./getNonce";
import { v4 as uuidv4 } from "uuid"; import { v4 as uuidv4 } from "uuid";
function transformTaskData(taskData: any) { function transformTaskData(
taskData: any,
customFields: { name: string, type: 'boolean' | 'date' | 'number' | 'string' }[]
) {
const result = { const result = {
id: taskData.id, id: taskData.id,
name: taskData.name, name: taskData.name,
@ -50,6 +53,17 @@ function transformTaskData(taskData: any) {
result.metadata["completed"] = new Date(Date.parse(taskData.metadata.completed)); result.metadata["completed"] = new Date(Date.parse(taskData.metadata.completed));
} }
// Add custom fields
for (let customField of customFields) {
if (customField.name in taskData.metadata && taskData.metadata[customField.name] !== null) {
if (customField.type === 'date') {
result.metadata[customField.name] = new Date(Date.parse(taskData.metadata[customField.name]));
} else {
result.metadata[customField.name] = taskData.metadata[customField.name];
}
}
}
return result; return result;
} }
@ -57,7 +71,7 @@ export default class KanbnTaskPanel {
private static readonly viewType = "react"; private static readonly viewType = "react";
private static panels: Record<string, KanbnTaskPanel> = {}; private static panels: Record<string, KanbnTaskPanel> = {};
private readonly _panel: vscode.WebviewPanel; private readonly _panel: WebviewPanel;
private readonly _extensionPath: string; private readonly _extensionPath: string;
private readonly _workspacePath: string; private readonly _workspacePath: string;
private readonly _kanbn: typeof import("@basementuniverse/kanbn/src/main"); private readonly _kanbn: typeof import("@basementuniverse/kanbn/src/main");
@ -65,7 +79,7 @@ export default class KanbnTaskPanel {
private readonly _panelUuid: string; private readonly _panelUuid: string;
private _taskId: string | null; private _taskId: string | null;
private _columnName: string | null; private _columnName: string | null;
private _disposables: vscode.Disposable[] = []; private _disposables: Disposable[] = [];
public static async show( public static async show(
extensionPath: string, extensionPath: string,
@ -75,14 +89,14 @@ export default class KanbnTaskPanel {
taskId: string | null, taskId: string | null,
columnName: string | null columnName: string | null
) { ) {
const column = vscode.window.activeTextEditor ? vscode.window.activeTextEditor.viewColumn : undefined; const column = window.activeTextEditor ? window.activeTextEditor.viewColumn : undefined;
// Create a new panel // Create a new panel
const panelUuid = uuidv4(); const panelUuid = uuidv4();
const taskPanel = new KanbnTaskPanel( const taskPanel = new KanbnTaskPanel(
extensionPath, extensionPath,
workspacePath, workspacePath,
column || vscode.ViewColumn.One, column || ViewColumn.One,
kanbn, kanbn,
kanbnFolderName, kanbnFolderName,
taskId, taskId,
@ -96,7 +110,7 @@ export default class KanbnTaskPanel {
private constructor( private constructor(
extensionPath: string, extensionPath: string,
workspacePath: string, workspacePath: string,
column: vscode.ViewColumn, column: ViewColumn,
kanbn: typeof import("@basementuniverse/kanbn/src/main"), kanbn: typeof import("@basementuniverse/kanbn/src/main"),
kanbnFolderName: string, kanbnFolderName: string,
taskId: string | null, taskId: string | null,
@ -112,7 +126,7 @@ export default class KanbnTaskPanel {
this._panelUuid = panelUuid; this._panelUuid = panelUuid;
// Create and show a new webview panel // Create and show a new webview panel
this._panel = vscode.window.createWebviewPanel(KanbnTaskPanel.viewType, "New task", column, { this._panel = window.createWebviewPanel(KanbnTaskPanel.viewType, "New task", column, {
// Enable javascript in the webview // Enable javascript in the webview
enableScripts: true, enableScripts: true,
@ -121,14 +135,14 @@ export default class KanbnTaskPanel {
// Restrict the webview to only loading content from allowed paths // Restrict the webview to only loading content from allowed paths
localResourceRoots: [ localResourceRoots: [
vscode.Uri.file(path.join(this._extensionPath, "build")), Uri.file(join(this._extensionPath, "build")),
vscode.Uri.file(path.join(this._workspacePath, this._kanbnFolderName)), Uri.file(join(this._workspacePath, this._kanbnFolderName)),
vscode.Uri.file(path.join(this._extensionPath, "node_modules", "vscode-codicons", "dist")), Uri.file(join(this._extensionPath, "node_modules", "vscode-codicons", "dist")),
], ],
}); });
(this._panel as any).iconPath = { (this._panel as any).iconPath = {
light: vscode.Uri.file(path.join(this._extensionPath, "resources", "task_light.svg")), light: Uri.file(join(this._extensionPath, "resources", "task_light.svg")),
dark: vscode.Uri.file(path.join(this._extensionPath, "resources", "task_dark.svg")), dark: Uri.file(join(this._extensionPath, "resources", "task_dark.svg")),
}; };
// Set the webview's title to the kanbn task name // Set the webview's title to the kanbn task name
@ -152,7 +166,7 @@ export default class KanbnTaskPanel {
// Display error message // Display error message
case "error": case "error":
vscode.window.showErrorMessage(message.text); window.showErrorMessage(message.text);
return; return;
// Update the task webview panel title // Update the task webview panel title
@ -162,49 +176,64 @@ export default class KanbnTaskPanel {
// Create a task // Create a task
case "kanbn.create": case "kanbn.create":
await this._kanbn.createTask(transformTaskData(message.taskData), message.taskData.column); await this._kanbn.createTask(
transformTaskData(message.taskData, message.customFields),
message.taskData.column
);
KanbnTaskPanel.panels[message.panelUuid]._taskId = message.taskData.id; KanbnTaskPanel.panels[message.panelUuid]._taskId = message.taskData.id;
KanbnTaskPanel.panels[message.panelUuid]._columnName = message.taskData.column; KanbnTaskPanel.panels[message.panelUuid]._columnName = message.taskData.column;
KanbnTaskPanel.panels[message.panelUuid].update(); KanbnTaskPanel.panels[message.panelUuid].update();
if (vscode.workspace.getConfiguration("kanbn").get("showTaskNotifications")) { if (workspace.getConfiguration("kanbn").get("showTaskNotifications")) {
vscode.window.showInformationMessage(`Created task '${message.taskData.name}'.`); window.showInformationMessage(`Created task '${message.taskData.name}'.`);
} }
return; return;
// Update a task // Update a task
case "kanbn.update": case "kanbn.update":
await this._kanbn.updateTask(message.taskId, transformTaskData(message.taskData), message.taskData.column); await this._kanbn.updateTask(
message.taskId,
transformTaskData(message.taskData, message.customFields),
message.taskData.column
);
KanbnTaskPanel.panels[message.panelUuid]._taskId = message.taskData.id; KanbnTaskPanel.panels[message.panelUuid]._taskId = message.taskData.id;
KanbnTaskPanel.panels[message.panelUuid]._columnName = message.taskData.column; KanbnTaskPanel.panels[message.panelUuid]._columnName = message.taskData.column;
KanbnTaskPanel.panels[message.panelUuid].update(); KanbnTaskPanel.panels[message.panelUuid].update();
if (vscode.workspace.getConfiguration("kanbn").get("showTaskNotifications")) { if (workspace.getConfiguration("kanbn").get("showTaskNotifications")) {
vscode.window.showInformationMessage(`Updated task '${message.taskData.name}'.`); window.showInformationMessage(`Updated task '${message.taskData.name}'.`);
} }
return; return;
// Delete a task and close the webview panel // Delete a task and close the webview panel
case "kanbn.delete": case "kanbn.delete":
vscode.window window
.showInformationMessage(`Delete task '${message.taskData.name}'?`, "Yes", "No") .showInformationMessage(`Delete task '${message.taskData.name}'?`, "Yes", "No")
.then(async (value) => { .then(async (value) => {
if (value === "Yes") { if (value === "Yes") {
await this._kanbn.deleteTask(message.taskId, true); await this._kanbn.deleteTask(message.taskId, true);
KanbnTaskPanel.panels[message.panelUuid].dispose(); KanbnTaskPanel.panels[message.panelUuid].dispose();
delete KanbnTaskPanel.panels[message.panelUuid]; delete KanbnTaskPanel.panels[message.panelUuid];
if (vscode.workspace.getConfiguration("kanbn").get("showTaskNotifications")) { if (workspace.getConfiguration("kanbn").get("showTaskNotifications")) {
vscode.window.showInformationMessage(`Deleted task '${message.taskData.name}'.`); window.showInformationMessage(`Deleted task '${message.taskData.name}'.`);
} }
} }
}); });
return; return;
// Go to raw task and close the webview panel
case "kanbn.goToRaw":
KanbnTaskPanel.panels[message.panelUuid].dispose();
delete KanbnTaskPanel.panels[message.panelUuid];
let uri = Uri.file(join(this._workspacePath, ".kanbn", "tasks", message.taskId + ".md"));
await commands.executeCommand('vscode.open', uri);
return;
// Archive a task and close the webview panel // Archive a task and close the webview panel
case 'kanbn.archive': case 'kanbn.archive':
await this._kanbn.archiveTask(message.taskId); await this._kanbn.archiveTask(message.taskId);
KanbnTaskPanel.panels[message.panelUuid].dispose(); KanbnTaskPanel.panels[message.panelUuid].dispose();
delete KanbnTaskPanel.panels[message.panelUuid]; delete KanbnTaskPanel.panels[message.panelUuid];
if (vscode.workspace.getConfiguration("kanbn").get("showTaskNotifications")) { if (workspace.getConfiguration("kanbn").get("showTaskNotifications")) {
vscode.window.showInformationMessage(`Archived task '${message.taskData.name}'.`); window.showInformationMessage(`Archived task '${message.taskData.name}'.`);
} }
return; return;
} }
@ -229,7 +258,7 @@ export default class KanbnTaskPanel {
try { try {
index = await this._kanbn.getIndex(); index = await this._kanbn.getIndex();
} catch (error) { } catch (error) {
vscode.window.showErrorMessage(error instanceof Error ? error.message : error); window.showErrorMessage(error instanceof Error ? error.message : error);
return; return;
} }
let tasks: any[]; let tasks: any[];
@ -239,7 +268,7 @@ export default class KanbnTaskPanel {
...this._kanbn.hydrateTask(index, task), ...this._kanbn.hydrateTask(index, task),
})); }));
} catch (error) { } catch (error) {
vscode.window.showErrorMessage(error instanceof Error ? error.message : error); window.showErrorMessage(error instanceof Error ? error.message : error);
return; return;
} }
let task = null; let task = null;
@ -258,6 +287,7 @@ export default class KanbnTaskPanel {
index, index,
task, task,
tasks, tasks,
customFields: index.options.customFields ?? [],
columnName: this._columnName, columnName: this._columnName,
dateFormat: this._kanbn.getDateFormat(index), dateFormat: this._kanbn.getDateFormat(index),
panelUuid: this._panelUuid, panelUuid: this._panelUuid,
@ -265,20 +295,20 @@ export default class KanbnTaskPanel {
} }
private _getHtmlForWebview() { private _getHtmlForWebview() {
const manifest = require(path.join(this._extensionPath, "build", "asset-manifest.json")); const manifest = require(join(this._extensionPath, "build", "asset-manifest.json"));
const mainScript = manifest["main.js"]; const mainScript = manifest["main.js"];
const mainStyle = manifest["main.css"]; const mainStyle = manifest["main.css"];
const scriptUri = vscode.Uri.file(path.join(this._extensionPath, "build", mainScript)).with({ const scriptUri = Uri.file(join(this._extensionPath, "build", mainScript)).with({
scheme: "vscode-resource", scheme: "vscode-resource",
}); });
const styleUri = vscode.Uri.file(path.join(this._extensionPath, "build", mainStyle)).with({ const styleUri = Uri.file(join(this._extensionPath, "build", mainStyle)).with({
scheme: "vscode-resource", scheme: "vscode-resource",
}); });
const customStyleUri = vscode.Uri.file( const customStyleUri = Uri.file(
path.join(this._workspacePath, this._kanbnFolderName, "board.css") join(this._workspacePath, this._kanbnFolderName, "board.css")
).with({ scheme: "vscode-resource" }); ).with({ scheme: "vscode-resource" });
const codiconsUri = vscode.Uri.file( const codiconsUri = Uri.file(
path.join(this._extensionPath, "node_modules", "vscode-codicons", "dist", "codicon.css") join(this._extensionPath, "node_modules", "vscode-codicons", "dist", "codicon.css")
).with({ scheme: "vscode-resource" }); ).with({ scheme: "vscode-resource" });
// Use a nonce to whitelist which scripts can be run // Use a nonce to whitelist which scripts can be run
@ -295,7 +325,7 @@ export default class KanbnTaskPanel {
<link rel="stylesheet" type="text/css" href="${customStyleUri}"> <link rel="stylesheet" type="text/css" href="${customStyleUri}">
<link rel="stylesheet" type="text/css" href="${codiconsUri}"> <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:;"> <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" })}/"> <base href="${Uri.file(join(this._extensionPath, "build")).with({ scheme: "vscode-resource" })}/">
</head> </head>
<body> <body>
<noscript>You need to enable JavaScript to run this app.</noscript> <noscript>You need to enable JavaScript to run this app.</noscript>

View File

@ -1,4 +1,4 @@
import * as vscode from "vscode"; import { ExtensionContext, commands, workspace, window, RelativePattern } from "vscode";
import KanbnStatusBarItem from "./KanbnStatusBarItem"; import KanbnStatusBarItem from "./KanbnStatusBarItem";
import KanbnBoardPanel from "./KanbnBoardPanel"; import KanbnBoardPanel from "./KanbnBoardPanel";
import KanbnBurndownPanel from "./KanbnBurndownPanel"; import KanbnBurndownPanel from "./KanbnBurndownPanel";
@ -6,19 +6,19 @@ import KanbnTaskPanel from "./KanbnTaskPanel";
let kanbnStatusBarItem: KanbnStatusBarItem; let kanbnStatusBarItem: KanbnStatusBarItem;
export async function activate(context: vscode.ExtensionContext) { export async function activate(context: ExtensionContext) {
// Register a command to initialise Kanbn in the current workspace. This command will be invoked when the status // Register a command to initialise Kanbn in the current workspace. This command will be invoked when the status
// bar item is clicked in a workspace where Kanbn isn't already initialised. // bar item is clicked in a workspace where Kanbn isn't already initialised.
context.subscriptions.push( context.subscriptions.push(
vscode.commands.registerCommand("kanbn.init", async () => { commands.registerCommand("kanbn.init", async () => {
// If no workspace folder is opened, we can't initialise kanbn // If no workspace folder is opened, we can't initialise kanbn
if (vscode.workspace.workspaceFolders === undefined) { if (workspace.workspaceFolders === undefined) {
vscode.window.showErrorMessage("You need to open a workspace before initialising Kanbn."); window.showErrorMessage("You need to open a workspace before initialising Kanbn.");
return; return;
} }
// Set the node process directory and import kanbn // Set the node process directory and import kanbn
process.chdir(vscode.workspace.workspaceFolders[0].uri.fsPath); process.chdir(workspace.workspaceFolders[0].uri.fsPath);
const kanbn = await import("@basementuniverse/kanbn/src/main"); const kanbn = await import("@basementuniverse/kanbn/src/main");
// If kanbn is already initialised, get the project name // If kanbn is already initialised, get the project name
@ -28,7 +28,7 @@ export async function activate(context: vscode.ExtensionContext) {
} }
// Prompt for a new project name // Prompt for a new project name
const newProjectName = await vscode.window.showInputBox({ const newProjectName = await window.showInputBox({
value: projectName, value: projectName,
placeHolder: "The project name.", placeHolder: "The project name.",
validateInput: (text) => { validateInput: (text) => {
@ -41,7 +41,7 @@ export async function activate(context: vscode.ExtensionContext) {
await kanbn.initialise({ await kanbn.initialise({
name: newProjectName, name: newProjectName,
}); });
vscode.window.showInformationMessage(`Initialised Kanbn project '${newProjectName}'.`); window.showInformationMessage(`Initialised Kanbn project '${newProjectName}'.`);
KanbnBoardPanel.update(); KanbnBoardPanel.update();
} }
kanbnStatusBarItem.update(); kanbnStatusBarItem.update();
@ -51,28 +51,28 @@ export async function activate(context: vscode.ExtensionContext) {
// Register a command to open the kanbn board. This command will be invoked when the status bar item is clicked // Register a command to open the kanbn board. This command will be invoked when the status bar item is clicked
// in a workspace where kanbn has already been initialised. // in a workspace where kanbn has already been initialised.
context.subscriptions.push( context.subscriptions.push(
vscode.commands.registerCommand("kanbn.board", async () => { commands.registerCommand("kanbn.board", async () => {
// If no workspace folder is opened, we can't open the kanbn board // If no workspace folder is opened, we can't open the kanbn board
if (vscode.workspace.workspaceFolders === undefined) { if (workspace.workspaceFolders === undefined) {
vscode.window.showErrorMessage("You need to open a workspace before viewing the Kanbn board."); window.showErrorMessage("You need to open a workspace before viewing the Kanbn board.");
return; return;
} }
// Set the node process directory and import kanbn // Set the node process directory and import kanbn
process.chdir(vscode.workspace.workspaceFolders[0].uri.fsPath); process.chdir(workspace.workspaceFolders[0].uri.fsPath);
const kanbn = await import("@basementuniverse/kanbn/src/main"); const kanbn = await import("@basementuniverse/kanbn/src/main");
// If kanbn is initialised, view the kanbn board // If kanbn is initialised, view the kanbn board
if (await kanbn.initialised()) { if (await kanbn.initialised()) {
KanbnBoardPanel.createOrShow( KanbnBoardPanel.createOrShow(
context.extensionPath, context.extensionPath,
vscode.workspace.workspaceFolders[0].uri.fsPath, workspace.workspaceFolders[0].uri.fsPath,
kanbn, kanbn,
await kanbn.getFolderName() await kanbn.getFolderName()
); );
KanbnBoardPanel.update(); KanbnBoardPanel.update();
} else { } else {
vscode.window.showErrorMessage("You need to initialise Kanbn before viewing the Kanbn board."); window.showErrorMessage("You need to initialise Kanbn before viewing the Kanbn board.");
} }
kanbnStatusBarItem.update(); kanbnStatusBarItem.update();
}) })
@ -80,57 +80,57 @@ export async function activate(context: vscode.ExtensionContext) {
// Register a command to add a new kanbn task. // Register a command to add a new kanbn task.
context.subscriptions.push( context.subscriptions.push(
vscode.commands.registerCommand("kanbn.addTask", async () => { commands.registerCommand("kanbn.addTask", async () => {
// If no workspace folder is opened, we can't add a new task // If no workspace folder is opened, we can't add a new task
if (vscode.workspace.workspaceFolders === undefined) { if (workspace.workspaceFolders === undefined) {
vscode.window.showErrorMessage("You need to open a workspace before adding a new task."); window.showErrorMessage("You need to open a workspace before adding a new task.");
return; return;
} }
// Set the node process directory and import kanbn // Set the node process directory and import kanbn
process.chdir(vscode.workspace.workspaceFolders[0].uri.fsPath); process.chdir(workspace.workspaceFolders[0].uri.fsPath);
const kanbn = await import("@basementuniverse/kanbn/src/main"); const kanbn = await import("@basementuniverse/kanbn/src/main");
// If kanbn is initialised, open the task webview // If kanbn is initialised, open the task webview
if (await kanbn.initialised()) { if (await kanbn.initialised()) {
KanbnTaskPanel.show( KanbnTaskPanel.show(
context.extensionPath, context.extensionPath,
vscode.workspace.workspaceFolders[0].uri.fsPath, workspace.workspaceFolders[0].uri.fsPath,
kanbn, kanbn,
await kanbn.getFolderName(), await kanbn.getFolderName(),
null, null,
null null
); );
} else { } else {
vscode.window.showErrorMessage("You need to initialise Kanbn before adding a new task."); window.showErrorMessage("You need to initialise Kanbn before adding a new task.");
} }
}) })
); );
// Register a command to open a burndown chart. // Register a command to open a burndown chart.
context.subscriptions.push( context.subscriptions.push(
vscode.commands.registerCommand("kanbn.burndown", async () => { commands.registerCommand("kanbn.burndown", async () => {
// If no workspace folder is opened, we can't open the burndown chart // If no workspace folder is opened, we can't open the burndown chart
if (vscode.workspace.workspaceFolders === undefined) { if (workspace.workspaceFolders === undefined) {
vscode.window.showErrorMessage("You need to open a workspace before viewing the burndown chart."); window.showErrorMessage("You need to open a workspace before viewing the burndown chart.");
return; return;
} }
// Set the node process directory and import kanbn // Set the node process directory and import kanbn
process.chdir(vscode.workspace.workspaceFolders[0].uri.fsPath); process.chdir(workspace.workspaceFolders[0].uri.fsPath);
const kanbn = await import("@basementuniverse/kanbn/src/main"); const kanbn = await import("@basementuniverse/kanbn/src/main");
// If kanbn is initialised, view the burndown chart // If kanbn is initialised, view the burndown chart
if (await kanbn.initialised()) { if (await kanbn.initialised()) {
KanbnBurndownPanel.createOrShow( KanbnBurndownPanel.createOrShow(
context.extensionPath, context.extensionPath,
vscode.workspace.workspaceFolders[0].uri.fsPath, workspace.workspaceFolders[0].uri.fsPath,
kanbn, kanbn,
await kanbn.getFolderName() await kanbn.getFolderName()
); );
KanbnBurndownPanel.update(); KanbnBurndownPanel.update();
} else { } else {
vscode.window.showErrorMessage("You need to initialise Kanbn before viewing the burndown chart."); window.showErrorMessage("You need to initialise Kanbn before viewing the burndown chart.");
} }
kanbnStatusBarItem.update(); kanbnStatusBarItem.update();
}) })
@ -138,15 +138,15 @@ export async function activate(context: vscode.ExtensionContext) {
// Register a command to archive tasks. // Register a command to archive tasks.
context.subscriptions.push( context.subscriptions.push(
vscode.commands.registerCommand("kanbn.archiveTasks", async () => { commands.registerCommand("kanbn.archiveTasks", async () => {
// If no workspace folder is opened, we can't archive tasks // If no workspace folder is opened, we can't archive tasks
if (vscode.workspace.workspaceFolders === undefined) { if (workspace.workspaceFolders === undefined) {
vscode.window.showErrorMessage("You need to open a workspace before sending tasks to the archive."); window.showErrorMessage("You need to open a workspace before sending tasks to the archive.");
return; return;
} }
// Set the node process directory and import kanbn // Set the node process directory and import kanbn
process.chdir(vscode.workspace.workspaceFolders[0].uri.fsPath); process.chdir(workspace.workspaceFolders[0].uri.fsPath);
const kanbn = await import("@basementuniverse/kanbn/src/main"); const kanbn = await import("@basementuniverse/kanbn/src/main");
// Get a list of tracked tasks // Get a list of tracked tasks
@ -155,12 +155,12 @@ export async function activate(context: vscode.ExtensionContext) {
tasks = [...(await kanbn.findTrackedTasks())]; tasks = [...(await kanbn.findTrackedTasks())];
} catch (e) {} } catch (e) {}
if (tasks.length === 0) { if (tasks.length === 0) {
vscode.window.showInformationMessage("There are no tasks to archive."); window.showInformationMessage("There are no tasks to archive.");
return; return;
} }
// Prompt for a selection of tasks to archive // Prompt for a selection of tasks to archive
const archiveTaskIds = await vscode.window.showQuickPick( const archiveTaskIds = await window.showQuickPick(
tasks, tasks,
{ {
placeHolder: 'Select tasks to archive...', placeHolder: 'Select tasks to archive...',
@ -173,8 +173,8 @@ export async function activate(context: vscode.ExtensionContext) {
} }
KanbnBoardPanel.update(); KanbnBoardPanel.update();
kanbnStatusBarItem.update(); kanbnStatusBarItem.update();
if (vscode.workspace.getConfiguration("kanbn").get("showTaskNotifications")) { if (workspace.getConfiguration("kanbn").get("showTaskNotifications")) {
vscode.window.showInformationMessage( window.showInformationMessage(
`Archived ${archiveTaskIds.length} task${archiveTaskIds.length === 1 ? '' : 's'}.` `Archived ${archiveTaskIds.length} task${archiveTaskIds.length === 1 ? '' : 's'}.`
); );
} }
@ -184,15 +184,15 @@ export async function activate(context: vscode.ExtensionContext) {
// Register a command to restore a task from the archive. // Register a command to restore a task from the archive.
context.subscriptions.push( context.subscriptions.push(
vscode.commands.registerCommand("kanbn.restoreTasks", async () => { commands.registerCommand("kanbn.restoreTasks", async () => {
// If no workspace folder is opened, we can't restore tasks from the archive // If no workspace folder is opened, we can't restore tasks from the archive
if (vscode.workspace.workspaceFolders === undefined) { if (workspace.workspaceFolders === undefined) {
vscode.window.showErrorMessage("You need to open a workspace before restoring tasks from the archive."); window.showErrorMessage("You need to open a workspace before restoring tasks from the archive.");
return; return;
} }
// Set the node process directory and import kanbn // Set the node process directory and import kanbn
process.chdir(vscode.workspace.workspaceFolders[0].uri.fsPath); process.chdir(workspace.workspaceFolders[0].uri.fsPath);
const kanbn = await import("@basementuniverse/kanbn/src/main"); const kanbn = await import("@basementuniverse/kanbn/src/main");
// Get a list of archived tasks // Get a list of archived tasks
@ -201,12 +201,12 @@ export async function activate(context: vscode.ExtensionContext) {
archivedTasks = await kanbn.listArchivedTasks(); archivedTasks = await kanbn.listArchivedTasks();
} catch (e) {} } catch (e) {}
if (archivedTasks.length === 0) { if (archivedTasks.length === 0) {
vscode.window.showInformationMessage("There are no archived tasks to restore."); window.showInformationMessage("There are no archived tasks to restore.");
return; return;
} }
// Prompt for a selection of tasks to restore // Prompt for a selection of tasks to restore
const restoreTaskIds = await vscode.window.showQuickPick( const restoreTaskIds = await window.showQuickPick(
archivedTasks, archivedTasks,
{ {
placeHolder: 'Select tasks to restore...', placeHolder: 'Select tasks to restore...',
@ -219,7 +219,7 @@ export async function activate(context: vscode.ExtensionContext) {
const index = await kanbn.getIndex(); const index = await kanbn.getIndex();
// Prompt for a column to restore the tasks into // Prompt for a column to restore the tasks into
const restoreColumn = await vscode.window.showQuickPick( const restoreColumn = await window.showQuickPick(
[ [
'None (use original)', 'None (use original)',
...Object.keys(index.columns) ...Object.keys(index.columns)
@ -234,8 +234,8 @@ export async function activate(context: vscode.ExtensionContext) {
} }
KanbnBoardPanel.update(); KanbnBoardPanel.update();
kanbnStatusBarItem.update(); kanbnStatusBarItem.update();
if (vscode.workspace.getConfiguration("kanbn").get("showTaskNotifications")) { if (workspace.getConfiguration("kanbn").get("showTaskNotifications")) {
vscode.window.showInformationMessage( window.showInformationMessage(
`Restored ${restoreTaskIds.length} task${restoreTaskIds.length === 1 ? '' : 's'}.` `Restored ${restoreTaskIds.length} task${restoreTaskIds.length === 1 ? '' : 's'}.`
); );
} }
@ -245,9 +245,9 @@ export async function activate(context: vscode.ExtensionContext) {
); );
// If a workspace folder is open, add a status bar item and start watching for file changes // If a workspace folder is open, add a status bar item and start watching for file changes
if (vscode.workspace.workspaceFolders !== undefined) { if (workspace.workspaceFolders !== undefined) {
// Set the node process directory and import kanbn // Set the node process directory and import kanbn
process.chdir(vscode.workspace.workspaceFolders[0].uri.fsPath); process.chdir(workspace.workspaceFolders[0].uri.fsPath);
const kanbn = await import("@basementuniverse/kanbn/src/main"); const kanbn = await import("@basementuniverse/kanbn/src/main");
// Create status bar item // Create status bar item
@ -256,10 +256,10 @@ export async function activate(context: vscode.ExtensionContext) {
KanbnBoardPanel.update(); KanbnBoardPanel.update();
// Initialise file watcher // Initialise file watcher
const uri = vscode.workspace.workspaceFolders[0].uri.fsPath; const uri = workspace.workspaceFolders[0].uri.fsPath;
const kanbnFolderName = await kanbn.getFolderName(); const kanbnFolderName = await kanbn.getFolderName();
const fileWatcher = vscode.workspace.createFileSystemWatcher( const fileWatcher = workspace.createFileSystemWatcher(
new vscode.RelativePattern(uri, `${kanbnFolderName}/**.*`) new RelativePattern(uri, `${kanbnFolderName}/**/*.md`)
); );
fileWatcher.onDidChange(() => { fileWatcher.onDidChange(() => {
kanbnStatusBarItem.update(); kanbnStatusBarItem.update();
@ -269,7 +269,7 @@ export async function activate(context: vscode.ExtensionContext) {
} }
// Handle configuration changes // Handle configuration changes
vscode.workspace.onDidChangeConfiguration((e) => { workspace.onDidChangeConfiguration((e) => {
kanbnStatusBarItem.update(); kanbnStatusBarItem.update();
KanbnBoardPanel.update(); KanbnBoardPanel.update();
}); });

32098
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -7,13 +7,13 @@
"color": "#72abdd", "color": "#72abdd",
"theme": "dark" "theme": "dark"
}, },
"version": "0.10.0", "version": "0.12.3",
"engines": { "engines": {
"vscode": "^1.23.0" "vscode": "^1.23.0"
}, },
"author": "Gordon Larrigan", "author": "Gordon Larrigan",
"license": "MIT", "license": "MIT",
"publisher": "gordonlarrigan", "publisher": "phares",
"keywords": [ "keywords": [
"kanbn", "kanbn",
"kanban", "kanban",
@ -118,12 +118,15 @@
"vscode-codicons": "^0.0.15" "vscode-codicons": "^0.0.15"
}, },
"scripts": { "scripts": {
"vscode:prepublish": "./scripts/build-non-split.js && tsc -p tsconfig.extension.json", "vscode:prepublish": "node ./scripts/build-non-split.js && tsc -p tsconfig.extension.json",
"postinstall": "node ./node_modules/vscode/bin/install", "postinstall": "node ./node_modules/vscode/bin/install",
"start": "react-scripts start", "start": "react-scripts start",
"build": "./scripts/build-non-split.js && tsc -p tsconfig.extension.json", "react:build": "node ./scripts/build-non-split.js",
"tsc:build": "tsc -p tsconfig.extension.json",
"build": "node ./scripts/build-non-split.js && tsc -p tsconfig.extension.json",
"test": "react-scripts test --env=jsdom", "test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject" "eject": "react-scripts eject",
"vscode:package": "vsce package"
}, },
"devDependencies": { "devDependencies": {
"@types/dateformat": "^3.0.1", "@types/dateformat": "^3.0.1",

View File

@ -66,6 +66,7 @@ function App() {
setTasks(tasks); setTasks(tasks);
setColumnName(event.data.columnName); setColumnName(event.data.columnName);
setColumnNames(Object.keys(event.data.index.columns)); setColumnNames(Object.keys(event.data.index.columns));
setCustomFields(event.data.customFields);
setPanelUuid(event.data.panelUuid); setPanelUuid(event.data.panelUuid);
break; break;
@ -118,6 +119,7 @@ function App() {
tasks={tasks} tasks={tasks}
columnName={columnName} columnName={columnName}
columnNames={columnNames} columnNames={columnNames}
customFields={customFields}
dateFormat={dateFormat} dateFormat={dateFormat}
panelUuid={panelUuid} panelUuid={panelUuid}
vscode={vscode} vscode={vscode}

View File

@ -70,12 +70,23 @@ const filterProperties = [
'description', 'description',
'assigned', 'assigned',
'tag', 'tag',
'relation' 'relation',
'subtask',
'comment',
]; ];
// Filter tasks according to the filter string // Filter tasks according to the filter string
const filterTask = (task: KanbnTask, taskFilter: string) => { const filterTask = (
task: KanbnTask,
taskFilter: string,
customFields: { name: string, type: 'boolean' | 'date' | 'number' | 'string' }[]
) => {
let result = true; let result = true;
const customFieldMap = Object.fromEntries(customFields.map(customField => [
customField.name.toLowerCase(),
customField,
]));
const customFieldNames = Object.keys(customFieldMap);
taskFilter.split(' ').forEach(f => { taskFilter.split(' ').forEach(f => {
const parts = f.split(':').map(p => p.toLowerCase()); const parts = f.split(':').map(p => p.toLowerCase());
@ -90,6 +101,17 @@ const filterTask = (task: KanbnTask, taskFilter: string) => {
return; return;
} }
// Filter boolean custom fields
if (customFieldNames.includes(parts[0]) && customFieldMap[parts[0]].type === 'boolean') {
if (
!(customFieldMap[parts[0]].name in task.metadata) ||
!task.metadata[customFieldMap[parts[0]].name]
) {
result = false;
}
return;
}
// Filter task id or name // Filter task id or name
if ( if (
!task.id.toLowerCase().includes(parts[0]) && !task.id.toLowerCase().includes(parts[0]) &&
@ -101,9 +123,14 @@ const filterTask = (task: KanbnTask, taskFilter: string) => {
} }
// If this filter section contains a property name and value, check the value against the property // If this filter section contains a property name and value, check the value against the property
if (parts.length === 2 && filterProperties.indexOf(parts[0]) !== -1) { if (
parts.length === 2 && (
filterProperties.includes(parts[0]) ||
customFieldNames.includes(parts[0])
)
) {
// Filter by property value // Fetch the value to filter by
let propertyValue = ''; let propertyValue = '';
switch (parts[0]) { switch (parts[0]) {
case 'description': case 'description':
@ -121,8 +148,24 @@ const filterTask = (task: KanbnTask, taskFilter: string) => {
case 'relation': case 'relation':
propertyValue = task.relations.map(relation => `${relation.type} ${relation.task}`).join(' '); propertyValue = task.relations.map(relation => `${relation.type} ${relation.task}`).join(' ');
break; break;
default: break; case 'subtask':
propertyValue = task.subTasks.map(subTask => `${subTask.text}`).join(' ');
break;
case 'comment':
propertyValue = task.comments.map(comment => `${comment.author} ${comment.text}`).join(' ');
break;
default:
if (
customFieldNames.includes(parts[0]) &&
customFieldMap[parts[0]].type !== 'boolean' &&
customFieldMap[parts[0]].name in task.metadata
) {
propertyValue = `${task.metadata[customFieldMap[parts[0]].name]}`;
}
break;
} }
// Check the search term against the value
if (!propertyValue.toLowerCase().includes(parts[1])) { if (!propertyValue.toLowerCase().includes(parts[1])) {
result = false; result = false;
} }
@ -326,14 +369,16 @@ const Board = ({
snapshot.isDraggingOver ? 'drag-over' : null snapshot.isDraggingOver ? 'drag-over' : null
].filter(i => i).join(' ')} ].filter(i => i).join(' ')}
> >
{column.filter(task => filterTask(task, taskFilter)).map((task, position) => <TaskItem {column
task={task} .filter(task => filterTask(task, taskFilter, customFields))
columnName={columnName} .map((task, position) => <TaskItem
customFields={customFields} task={task}
position={position} columnName={columnName}
dateFormat={dateFormat} customFields={customFields}
vscode={vscode} position={position}
/>)} dateFormat={dateFormat}
vscode={vscode}
/>)}
{provided.placeholder} {provided.placeholder}
</div> </div>
); );

File diff suppressed because it is too large Load Diff

View File

@ -71,10 +71,17 @@ const TaskItem = ({ task, columnName, customFields, position, dateFormat, vscode
<div className="kanbn-task-data kanbn-task-data-tags"> <div className="kanbn-task-data kanbn-task-data-tags">
{task.metadata.tags!.map(tag => { {task.metadata.tags!.map(tag => {
return ( return (
<span className={[ <span onClick={() => {
'kanbn-task-tag', vscode.postMessage({
`kanbn-task-tag-${paramCase(tag)}` command: 'kanbn.goToRaw',
].join(' ')}> taskId: task.id,
columnName: task.column
});
}}
className={[
'kanbn-task-tag',
`kanbn-task-tag-${paramCase(tag)}`
].join(' ')}>
{tag} {tag}
</span> </span>
); );
@ -102,7 +109,9 @@ const TaskItem = ({ task, columnName, customFields, position, dateFormat, vscode
<> <>
<i className="codicon codicon-json"></i> <i className="codicon codicon-json"></i>
<span title={customField.name}> <span title={customField.name}>
{task.metadata[customField.name]} {customField.type === 'date'
? formatDate(task.metadata[customField.name], dateFormat)
: task.metadata[customField.name]}
</span> </span>
</> </>
) )
@ -166,16 +175,30 @@ const TaskItem = ({ task, columnName, customFields, position, dateFormat, vscode
{ {
task.workload !== undefined && task.workload !== undefined &&
<div className="kanbn-task-data kanbn-task-data-workload"> <div className="kanbn-task-data kanbn-task-data-workload">
<i className="codicon codicon-run"></i>{task.workload} <i className="codicon codicon-tools"
onClick={() => {
vscode.postMessage({
command: 'kanbn.goToRaw',
taskId: task.id,
columnName: task.column
});
}}></i>{task.workload}
</div> </div>
} }
{ {
task.relations.length > 0 && task.relations.length > 0 &&
task.relations.map(relation => ( task.relations.map(relation => (
<div className={[ <div onClick={() => {
'kanbn-task-data kanbn-task-data-relation', vscode.postMessage({
relation.type ? `kanbn-task-data-relation-${relation.type}` : null, command: 'kanbn.goToRaw',
].join(' ')}> taskId: task.id,
columnName: task.column
});
}}
className={[
'kanbn-task-data kanbn-task-data-relation',
relation.type ? `kanbn-task-data-relation-${relation.type}` : null,
].join(' ')}>
<i className="codicon codicon-link"></i> <i className="codicon codicon-link"></i>
<span className="kanbn-task-data-label"> <span className="kanbn-task-data-label">
{relation.type} {relation.type}

View File

@ -351,10 +351,9 @@ Task editor styles
-----------------------------------------------------------------------------*/ -----------------------------------------------------------------------------*/
.kanbn-task-editor-title { .kanbn-task-editor-title {
display: inline-block;
font-size: 1.5em; font-size: 1.5em;
margin-top: 0; margin-top: 0;
padding-bottom: 0.5em;
border-bottom: 1px var(--vscode-activityBar-inactiveForeground) solid;
} }
.kanbn-task-editor-dirty { .kanbn-task-editor-dirty {
@ -368,10 +367,12 @@ Task editor styles
font-weight: normal; font-weight: normal;
opacity: 0.8; opacity: 0.8;
float: right; float: right;
margin: 2px;
} }
.kanbn-task-editor-form { .kanbn-task-editor-form {
display: flex; display: flex;
border-top: 1px solid var(--vscode-activityBar-inactiveForeground);
} }
.kanbn-task-editor-field .kanbn-task-editor-title, .kanbn-task-editor-field .kanbn-task-editor-title,
@ -439,6 +440,12 @@ body.vscode-dark .kanbn-task-editor-field-input[type="date"]::-webkit-calendar-p
text-align: right; text-align: right;
} }
.kanbn-task-editor-main-buttons {
float: right;
position: relative;
top: -0.5em;
}
.kanbn-task-editor-button { .kanbn-task-editor-button {
outline: none; outline: none;
border: 1px transparent solid; border: 1px transparent solid;
@ -602,6 +609,14 @@ body.vscode-dark .kanbn-task-editor-field-input[type="date"]::-webkit-calendar-p
float: right; float: right;
} }
.kanbn-task-editor-custom-checkbox {
float: left;
width: auto;
position: relative;
top: -.2em;
margin: .5em 1em .5em 0;
}
/*----------------------------------------------------------------------------- /*-----------------------------------------------------------------------------
Burndown chart styles Burndown chart styles
-----------------------------------------------------------------------------*/ -----------------------------------------------------------------------------*/

View File

@ -1,9 +1,9 @@
import * as React from 'react'; import * as React from 'react';
import * as ReactDOM from 'react-dom'; import { render } from 'react-dom';
import App from './App'; import App from './App';
import './index.css'; import './index.css';
ReactDOM.render( render(
<App />, <App />,
document.getElementById('root') as HTMLElement document.getElementById('root') as HTMLElement
); );

BIN
vscode-kanbn-0.12.0.vsix Normal file

Binary file not shown.

BIN
vscode-kanbn-0.12.1.vsix Normal file

Binary file not shown.

BIN
vscode-kanbn-0.12.2.vsix Normal file

Binary file not shown.