Edit relations and sub-tasks

This commit is contained in:
Gordon 2021-04-05 23:09:43 +01:00
parent be9daf1e42
commit 6efe282b81
9 changed files with 379 additions and 120 deletions

View File

@ -42,12 +42,19 @@ export default class KanbnBoardPanel {
vscode.window.showErrorMessage(error instanceof Error ? error.message : error); vscode.window.showErrorMessage(error instanceof Error ? error.message : error);
return; return;
} }
let tasks: any[];
try {
tasks = (await KanbnBoardPanel.currentPanel._kanbn.loadAllTrackedTasks(index)).map(
task => KanbnBoardPanel.currentPanel!._kanbn.hydrateTask(index, task)
);
} catch (error) {
vscode.window.showErrorMessage(error instanceof Error ? error.message : error);
return;
}
KanbnBoardPanel.currentPanel._panel.webview.postMessage({ KanbnBoardPanel.currentPanel._panel.webview.postMessage({
type: 'index', type: 'index',
index, index,
tasks: (await KanbnBoardPanel.currentPanel._kanbn.loadAllTrackedTasks(index)).map( tasks,
task => KanbnBoardPanel.currentPanel!._kanbn.hydrateTask(index, task)
),
startedColumns: index.options.startedColumns ?? [], startedColumns: index.options.startedColumns ?? [],
completedColumns: index.options.completedColumns ?? [], completedColumns: index.options.completedColumns ?? [],
dateFormat: KanbnBoardPanel.currentPanel._kanbn.getDateFormat(index) dateFormat: KanbnBoardPanel.currentPanel._kanbn.getDateFormat(index)

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 { v4 as uuidv4 } from 'uuid';
export default class KanbnTaskPanel { export default class KanbnTaskPanel {
private static readonly viewType = 'react'; private static readonly viewType = 'react';
@ -28,14 +29,21 @@ export default class KanbnTaskPanel {
vscode.window.showErrorMessage(error instanceof Error ? error.message : error); vscode.window.showErrorMessage(error instanceof Error ? error.message : error);
return; return;
} }
let task: any = null; let tasks: any[];
try {
tasks = (await kanbn.loadAllTrackedTasks(index)).map(
task => ({
uuid: uuidv4(),
...kanbn.hydrateTask(index, task)
})
);
} catch (error) {
vscode.window.showErrorMessage(error instanceof Error ? error.message : error);
return;
}
let task = null;
if (taskId) { if (taskId) {
try { task = tasks.find(t => t.id === taskId) ?? null;
task = await kanbn.hydrateTask(index, await kanbn.getTask(taskId));
} catch (error) {
vscode.window.showErrorMessage(error instanceof Error ? error.message : error);
return;
}
} }
// If no columnName is specified, use the first column // If no columnName is specified, use the first column
@ -60,7 +68,7 @@ export default class KanbnTaskPanel {
index, index,
task, task,
columnName: taskPanel._columnName, columnName: taskPanel._columnName,
tasks: await kanbn.loadAllTrackedTasks(index), tasks,
dateFormat: kanbn.getDateFormat(index) dateFormat: kanbn.getDateFormat(index)
}); });
} }

41
package-lock.json generated
View File

@ -3355,6 +3355,12 @@
"resolved": "https://registry.npmjs.org/@types/dateformat/-/dateformat-3.0.1.tgz", "resolved": "https://registry.npmjs.org/@types/dateformat/-/dateformat-3.0.1.tgz",
"integrity": "sha512-KlPPdikagvL6ELjWsljbyDIPzNCeliYkqRpI+zea99vBBbCIA5JNshZAwQKTON139c87y9qvTFVgkFd14rtS4g==" "integrity": "sha512-KlPPdikagvL6ELjWsljbyDIPzNCeliYkqRpI+zea99vBBbCIA5JNshZAwQKTON139c87y9qvTFVgkFd14rtS4g=="
}, },
"@types/git-user-name": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@types/git-user-name/-/git-user-name-2.0.0.tgz",
"integrity": "sha512-bZhPykkyyPdHW2wMc30aLIWntMIMO49jWKGQipPg3RQNGq2LdAnW7OCinHew+M8EpZ5qHNyFynIiou+UyxOfww==",
"dev": true
},
"@types/jest": { "@types/jest": {
"version": "23.3.14", "version": "23.3.14",
"resolved": "https://registry.npmjs.org/@types/jest/-/jest-23.3.14.tgz", "resolved": "https://registry.npmjs.org/@types/jest/-/jest-23.3.14.tgz",
@ -3417,6 +3423,12 @@
"integrity": "sha512-42zEJkBpNfMEAvWR5WlwtTH22oDzcMjFsL9gDGExwF8X8WvAiw7Vwop7hPw03QT8TKfec83LwbHj6SvpqM4ELQ==", "integrity": "sha512-42zEJkBpNfMEAvWR5WlwtTH22oDzcMjFsL9gDGExwF8X8WvAiw7Vwop7hPw03QT8TKfec83LwbHj6SvpqM4ELQ==",
"dev": true "dev": true
}, },
"@types/uuid": {
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.0.tgz",
"integrity": "sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ==",
"dev": true
},
"@webassemblyjs/ast": { "@webassemblyjs/ast": {
"version": "1.7.11", "version": "1.7.11",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.7.11.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.7.11.tgz",
@ -17478,6 +17490,14 @@
"tough-cookie": "~2.5.0", "tough-cookie": "~2.5.0",
"tunnel-agent": "^0.6.0", "tunnel-agent": "^0.6.0",
"uuid": "^3.3.2" "uuid": "^3.3.2"
},
"dependencies": {
"uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
"dev": true
}
} }
}, },
"request-promise-core": { "request-promise-core": {
@ -18859,6 +18879,12 @@
"requires": { "requires": {
"websocket-driver": ">=0.5.1" "websocket-driver": ">=0.5.1"
} }
},
"uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
"dev": true
} }
} }
}, },
@ -20076,10 +20102,9 @@
"dev": true "dev": true
}, },
"uuid": { "uuid": {
"version": "3.4.0", "version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
"dev": true
}, },
"validate-npm-package-license": { "validate-npm-package-license": {
"version": "3.0.4", "version": "3.0.4",
@ -20913,6 +20938,14 @@
"requires": { "requires": {
"ansi-colors": "^3.0.0", "ansi-colors": "^3.0.0",
"uuid": "^3.3.2" "uuid": "^3.3.2"
},
"dependencies": {
"uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
"dev": true
}
} }
}, },
"webpack-manifest-plugin": { "webpack-manifest-plugin": {

View File

@ -39,6 +39,7 @@
"react-beautiful-dnd": "12.2.0", "react-beautiful-dnd": "12.2.0",
"react-dom": "^16.3.2", "react-dom": "^16.3.2",
"terser": "3.14.1", "terser": "3.14.1",
"uuid": "^8.3.2",
"vscode": "^1.1.37", "vscode": "^1.1.37",
"vscode-codicons": "^0.0.15" "vscode-codicons": "^0.0.15"
}, },
@ -51,11 +52,13 @@
"eject": "react-scripts eject" "eject": "react-scripts eject"
}, },
"devDependencies": { "devDependencies": {
"@types/git-user-name": "^2.0.0",
"@types/jest": "^23.3.14", "@types/jest": "^23.3.14",
"@types/lodash": "^4.14.168", "@types/lodash": "^4.14.168",
"@types/node": "^10.17.56", "@types/node": "^10.17.56",
"@types/react": "^16.14.5", "@types/react": "^16.14.5",
"@types/react-dom": "^16.9.12", "@types/react-dom": "^16.9.12",
"@types/uuid": "^8.3.0",
"react-scripts": "^2.1.8", "react-scripts": "^2.1.8",
"rewire": "^4.0.1", "rewire": "^4.0.1",
"typescript": "^4.0.2" "typescript": "^4.0.2"

View File

@ -18,13 +18,14 @@ function App() {
const [completedColumns, setCompletedColumns] = useState([]); const [completedColumns, setCompletedColumns] = useState([]);
const [dateFormat, setDateFormat] = useState(''); const [dateFormat, setDateFormat] = useState('');
const [task, setTask] = useState({}); const [task, setTask] = useState({});
const [tasks, setTasks] = useState({});
const [columnName, setColumnName] = useState(''); const [columnName, setColumnName] = useState('');
const [columnNames, setColumnNames] = useState([] as string[]); const [columnNames, setColumnNames] = useState([] as string[]);
window.addEventListener('message', event => { window.addEventListener('message', event => {
const tasks = Object.fromEntries(event.data.tasks.map(task => [task.id, task]));
switch (event.data.type) { switch (event.data.type) {
case 'index': case 'index':
const tasks = Object.fromEntries(event.data.tasks.map(task => [task.id, task]));
setName(event.data.index.name); setName(event.data.index.name);
setDescription(event.data.index.description); setDescription(event.data.index.description);
setColumns(Object.fromEntries( setColumns(Object.fromEntries(
@ -39,6 +40,7 @@ function App() {
case 'task': case 'task':
setTask(event.data.task); setTask(event.data.task);
setTasks(tasks);
setColumnName(event.data.columnName); setColumnName(event.data.columnName);
setColumnNames(Object.keys(event.data.index.columns)); setColumnNames(Object.keys(event.data.index.columns));
break; break;
@ -69,6 +71,7 @@ function App() {
type === 'task' && type === 'task' &&
<TaskEditor <TaskEditor
task={task as KanbnTask|null} task={task as KanbnTask|null}
tasks={tasks}
columnName={columnName} columnName={columnName}
columnNames={columnNames} columnNames={columnNames}
dateFormat={dateFormat} dateFormat={dateFormat}

View File

@ -64,12 +64,7 @@ const Board = ({ columns, startedColumns, completedColumns, dateFormat, vscode }
}) => { }) => {
const [, setColumns] = useState(columns); const [, setColumns] = useState(columns);
return ( return (
<div <div className="kanbn-board">
className="kanbn-board"
style={{
display: "flex"
}}
>
<DragDropContext <DragDropContext
onDragEnd={result => onDragEnd(result, columns, setColumns, vscode)} onDragEnd={result => onDragEnd(result, columns, setColumns, vscode)}
> >
@ -80,9 +75,6 @@ const Board = ({ columns, startedColumns, completedColumns, dateFormat, vscode }
'kanbn-column', 'kanbn-column',
`kanbn-column-${paramCase(columnName)}` `kanbn-column-${paramCase(columnName)}`
].join(' ')} ].join(' ')}
style={{
flex: 1
}}
key={columnName} key={columnName}
> >
<h2 className="kanbn-column-name"> <h2 className="kanbn-column-name">

1
src/KanbnTask.d.ts vendored
View File

@ -1,4 +1,5 @@
declare type KanbnTask = { declare type KanbnTask = {
uuid?: string,
id: string, id: string,
name: string, name: string,
description: string, description: string,

View File

@ -1,12 +1,13 @@
import React from 'react'; import React from 'react';
import { Formik } from 'formik'; import { Formik, Form, Field, ErrorMessage, FieldArray } from 'formik';
import formatDate from 'dateformat'; import formatDate from 'dateformat';
import VSCodeApi from './VSCodeApi'; import VSCodeApi from './VSCodeApi';
import { paramCase } from 'param-case'; import { paramCase } from 'param-case';
import * as gitUsername from 'git-user-name'; import gitUsername from 'git-user-name';
const TaskEditor = ({ task, columnName, columnNames, dateFormat, vscode }: { const TaskEditor = ({ task, tasks, columnName, columnNames, dateFormat, vscode }: {
task: KanbnTask|null, task: KanbnTask|null,
tasks: Record<string, KanbnTask>,
columnName: string, columnName: string,
columnNames: string[], columnNames: string[],
dateFormat: string, dateFormat: string,
@ -32,6 +33,7 @@ const TaskEditor = ({ task, columnName, columnNames, dateFormat, vscode }: {
<h1 className="kanbn-task-editor-title">{editing ? 'Update task' : 'Create new task'}</h1> <h1 className="kanbn-task-editor-title">{editing ? 'Update task' : 'Create new task'}</h1>
<Formik <Formik
initialValues={{ initialValues={{
uuid: task ? task.uuid : '',
id: task ? task.id : '', id: task ? task.id : '',
name: task ? task.name : '', name: task ? task.name : '',
description: task ? task.description : '', description: task ? task.description : '',
@ -39,22 +41,23 @@ const TaskEditor = ({ task, columnName, columnNames, dateFormat, vscode }: {
progress: task ? task.progress : 0, progress: task ? task.progress : 0,
metadata: { metadata: {
due: (task && 'due' in task.metadata) ? formatDate(new Date(task.metadata.due!), 'yyyy-mm-dd') : '', due: (task && 'due' in task.metadata) ? formatDate(new Date(task.metadata.due!), 'yyyy-mm-dd') : '',
assigned: (task && 'assigned' in task.metadata) ? task.metadata.assigned : gitUsername(), assigned: (task && 'assigned' in task.metadata) ? task.metadata.assigned : (gitUsername() || ''),
tags: (task && 'tags' in task.metadata) ? task.metadata.tags : [] tags: (task && 'tags' in task.metadata) ? task.metadata.tags : []
}, },
relations: [], relations: task ? task.relations : [],
subTasks: [], subTasks: task ? task.subTasks : [],
comments: [] comments: task ? task.comments : []
}} }}
validate={values => { validate={values => {
const errors: { name?: string } = {}; const errors: { name?: string } = {};
// if (!values.email) { if (!values.name) {
// errors.email = 'Required'; errors.name = 'Task name is required.';
// } else if ( }
// !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(values.email)
// ) { // Check if the id is already in use
// errors.email = 'Invalid email address'; if (values.id in tasks && tasks[values.id].uuid !== values.uuid) {
// } errors.name = 'There is already a task with the same name or id.';
}
return errors; return errors;
}} }}
onSubmit={(values, { setSubmitting }) => { onSubmit={(values, { setSubmitting }) => {
@ -73,125 +76,246 @@ const TaskEditor = ({ task, columnName, columnNames, dateFormat, vscode }: {
> >
{({ {({
values, values,
errors,
touched,
handleChange, handleChange,
handleBlur,
handleSubmit,
isSubmitting isSubmitting
}) => ( }) => (
<form onSubmit={handleSubmit}> <Form>
<div <div style={{ display: "flex" }}>
style={{
display: "flex"
}}
>
<div className="kanbn-task-editor-column-left"> <div className="kanbn-task-editor-column-left">
<div className="kanbn-task-field kanbn-task-field-name"> <div className="kanbn-task-editor-field kanbn-task-editor-field-name">
<label className="kanbn-task-field-label"> <label className="kanbn-task-editor-field-label">
<p>Name</p> <p>Name</p>
<input <Field
className="kanbn-task-field-input" className="kanbn-task-editor-field-input"
placeholder="Name"
name="name" name="name"
value={values.name} placeholder="Name"
onChange={e => { onChange={e => {
handleChange(e); handleChange(e);
handleUpdateName(e, values); handleUpdateName(e, values);
}} }}
onBlur={handleBlur} />
></input>
</label> </label>
{errors.name && touched.name && errors.name} <div className="kanbn-task-editor-id">{values.id}</div>
<span className="kanbn-task-id">{values.id}</span> <ErrorMessage
className="kanbn-task-editor-field-errors"
component="div"
name="name"
/>
</div> </div>
<div className="kanbn-task-field kanbn-task-field-description"> <div className="kanbn-task-editor-field kanbn-task-editor-field-description">
<label className="kanbn-task-field-label"> <label className="kanbn-task-editor-field-label">
<p>Description</p> <p>Description</p>
<textarea <Field
className="kanbn-task-field-textarea" className="kanbn-task-editor-field-textarea"
placeholder="Description" as="textarea"
name="description" name="description"
value={values.description} />
onChange={handleChange}
onBlur={handleBlur}
></textarea>
</label> </label>
<ErrorMessage
className="kanbn-task-editor-field-errors"
component="div"
name="description"
/>
</div>
<div className="kanbn-task-editor-field kanbn-task-editor-field-relations">
<h2 className="kanbn-task-editor-title">Relations</h2>
<FieldArray name="relations">
{({ insert, remove, push }) => (
<div>
{values.relations.length > 0 && values.relations.map((relation, index) => (
<div className="kanbn-task-editor-row kanbn-task-editor-row-relation" key={index}>
<div className="kanbn-task-editor-column kanbn-task-editor-field-relation-type">
<Field
className="kanbn-task-editor-field-input"
name={`relations.${index}.type`}
placeholder="Relation type"
/>
<ErrorMessage
className="kanbn-task-editor-field-errors"
component="div"
name={`relations.${index}.type`}
/>
</div>
<div className="kanbn-task-editor-column kanbn-task-editor-field-relation-task">
<Field
className="kanbn-task-editor-field-select"
as="select"
name={`relations.${index}.task`}
>
{Object.keys(tasks).map(t => <option value={t}>{t}</option>)}
</Field>
<ErrorMessage
className="kanbn-task-editor-field-errors"
component="div"
name={`relations.${index}.task`}
/>
</div>
<div className="kanbn-task-editor-column kanbn-task-editor-column-buttons">
<button
type="button"
className="kanbn-task-editor-button kanbn-task-editor-button-delete"
onClick={() => remove(index)}
>
<i className="codicon codicon-trash"></i>Delete
</button>
</div>
</div>
))}
<div className="kanbn-task-editor-buttons">
<button
type="button"
className="kanbn-task-editor-button kanbn-task-editor-button-add"
onClick={() => push({ type: '', task: '' })}
>
<i className="codicon codicon-plus"></i>Add relation
</button>
</div>
</div>
)}
</FieldArray>
</div>
<div className="kanbn-task-editor-field kanbn-task-editor-field-subtasks">
<h2 className="kanbn-task-editor-title">Sub-tasks</h2>
<FieldArray name="subTasks">
{({ insert, remove, push }) => (
<div>
{values.subTasks.length > 0 && values.subTasks.map((subTask, index) => (
<div className="kanbn-task-editor-row kanbn-task-editor-row-subtask" key={index}>
<div className="kanbn-task-editor-column kanbn-task-editor-field-subtask-completed">
<Field
className="kanbn-task-editor-field-checkbox"
type="checkbox"
name={`subTasks.${index}.completed`}
/>
<ErrorMessage
className="kanbn-task-editor-field-errors"
component="div"
name={`subTasks.${index}.completed`}
/>
</div>
<div className="kanbn-task-editor-column kanbn-task-editor-field-subtask-text">
<Field
className="kanbn-task-editor-field-input"
name={`subTasks.${index}.text`}
placeholder="Sub-task text"
/>
<ErrorMessage
className="kanbn-task-editor-field-errors"
component="div"
name={`subTasks.${index}.text`}
/>
</div>
<div className="kanbn-task-editor-column kanbn-task-editor-column-buttons">
<button
type="button"
className="kanbn-task-editor-button kanbn-task-editor-button-delete"
onClick={() => remove(index)}
>
<i className="codicon codicon-trash"></i>Delete
</button>
</div>
</div>
))}
<div className="kanbn-task-editor-buttons">
<button
type="button"
className="kanbn-task-editor-button kanbn-task-editor-button-add"
onClick={() => push({ completed: false, text: '' })}
>
<i className="codicon codicon-plus"></i>Add sub-task
</button>
</div>
</div>
)}
</FieldArray>
</div> </div>
</div> </div>
<div className="kanbn-task-editor-column-right"> <div className="kanbn-task-editor-column-right">
<div> <div className="kanbn-task-editor-buttons">
{editing && <button
type="button"
className="kanbn-task-editor-button kanbn-task-editor-button-delete"
title="Delete"
>
<i className="codicon codicon-trash"></i>Delete
</button>}
<button <button
type="submit" type="submit"
className="kanbn-task-editor-button kanbn-task-editor-button-submit" className="kanbn-task-editor-button kanbn-task-editor-button-submit"
title="Save" title="Save"
disabled={isSubmitting} disabled={isSubmitting}
>Save</button> >
<button <i className="codicon codicon-save"></i>Save
type="button" </button>
className="kanbn-task-editor-button kanbn-task-editor-button-delete"
title="Delete"
>Delete</button>
</div> </div>
<div className="kanbn-task-field kanbn-task-field-column"> <div className="kanbn-task-editor-field kanbn-task-editor-field-column">
<label className="kanbn-task-field-label"> <label className="kanbn-task-editor-field-label">
<p>Column</p> <p>Column</p>
<select <Field
className="kanbn-task-field-select" className="kanbn-task-editor-field-select"
as="select"
name="column" name="column"
value={values.column}
onChange={handleChange}
onBlur={handleBlur}
> >
{columnNames.map(c => <option value={c}>{c}</option>)} {columnNames.map(c => <option value={c}>{c}</option>)}
</select> </Field>
</label> </label>
<ErrorMessage
className="kanbn-task-editor-field-errors"
component="div"
name="column"
/>
</div> </div>
<div className="kanbn-task-field kanbn-task-field-assigned"> <div className="kanbn-task-editor-field kanbn-task-editor-field-assigned">
<label className="kanbn-task-field-label"> <label className="kanbn-task-editor-field-label">
<p>Assigned to</p> <p>Assigned to</p>
<input <Field
className="kanbn-task-field-input" className="kanbn-task-editor-field-input"
placeholder="Assigned to"
name="metadata.assigned" name="metadata.assigned"
value={values.metadata.assigned} placeholder="Assigned to"
onChange={handleChange} />
onBlur={handleBlur}
></input>
</label> </label>
<ErrorMessage
className="kanbn-task-editor-field-errors"
component="div"
name="metadata.assigned"
/>
</div> </div>
<div className="kanbn-task-field kanbn-task-field-due"> <div className="kanbn-task-editor-field kanbn-task-editor-field-due">
<label className="kanbn-task-field-label"> <label className="kanbn-task-editor-field-label">
<p>Due date</p> <p>Due date</p>
<input <Field
className="kanbn-task-editor-field-input"
type="date" type="date"
className="kanbn-task-field-input"
name="metadata.due" name="metadata.due"
value={values.metadata.due} />
onChange={handleChange}
onBlur={handleBlur}
></input>
</label> </label>
<ErrorMessage
className="kanbn-task-editor-field-errors"
component="div"
name="metadata.due"
/>
</div> </div>
<div className="kanbn-task-field kanbn-task-field-progress"> <div className="kanbn-task-editor-field kanbn-task-editor-field-progress">
<label className="kanbn-task-field-label"> <label className="kanbn-task-editor-field-label">
<p>Progress</p> <p>Progress</p>
<input <Field
className="kanbn-task-editor-field-input"
type="number" type="number"
className="kanbn-task-field-input"
name="progress" name="progress"
value={values.progress}
onChange={handleChange}
onBlur={handleBlur}
min="0" min="0"
max="1" max="1"
step="0.05" step="0.05"
></input> />
</label> </label>
<ErrorMessage
className="kanbn-task-editor-field-errors"
component="div"
name="progress"
/>
</div> </div>
</div> </div>
</div> </div>
</form> </Form>
)} )}
</Formik> </Formik>
</div> </div>

View File

@ -13,6 +13,14 @@ body {
border-bottom: 1px var(--vscode-activityBar-inactiveForeground) solid; border-bottom: 1px var(--vscode-activityBar-inactiveForeground) solid;
} }
.kanbn-board {
display: flex;
}
.kanbn-column {
flex: 1;
}
.kanbn-column-name { .kanbn-column-name {
color: var(--vscode-editor-foreground); color: var(--vscode-editor-foreground);
font-size: 0.8em; font-size: 0.8em;
@ -152,7 +160,7 @@ body {
position: relative; position: relative;
top: 1px; top: 1px;
font-size: 0.9em !important; font-size: 0.9em !important;
margin-right: 4px; margin-right: 0.5em;
} }
.kanbn-task-tag { .kanbn-task-tag {
@ -167,7 +175,7 @@ body {
} }
.kanbn-task-tag-Nothing { .kanbn-task-tag-Nothing {
background-color: #aab; background-color: #6bf;
color: #333; color: #333;
} }
@ -212,6 +220,12 @@ body {
border-bottom: 1px var(--vscode-activityBar-inactiveForeground) solid; border-bottom: 1px var(--vscode-activityBar-inactiveForeground) solid;
} }
.kanbn-task-editor-field .kanbn-task-editor-title {
font-size: 1.1em;
padding: 0.5em 0;
border-bottom: 1px var(--vscode-activityBar-inactiveForeground) solid;
}
.kanbn-task-editor-column-left { .kanbn-task-editor-column-left {
width: 70%; width: 70%;
padding-right: 1em; padding-right: 1em;
@ -221,11 +235,11 @@ body {
width: 30%; width: 30%;
} }
.kanbn-task-field { .kanbn-task-editor-field {
margin-bottom: 1em; margin-bottom: 1em;
} }
.kanbn-task-field-label p { .kanbn-task-editor-field-label p {
color: var(--vscode-editor-foreground); color: var(--vscode-editor-foreground);
font-size: 0.8em; font-size: 0.8em;
letter-spacing: 0.1em; letter-spacing: 0.1em;
@ -234,13 +248,14 @@ body {
padding: 4px 0; padding: 4px 0;
} }
body.vscode-dark .kanbn-task-field-input[type="date"]::-webkit-calendar-picker-indicator { body.vscode-dark .kanbn-task-editor-field-input[type="date"]::-webkit-calendar-picker-indicator {
filter: invert(1); filter: invert(1);
} }
.kanbn-task-field-input, .kanbn-task-editor-field-input,
.kanbn-task-field-select, .kanbn-task-editor-field-select,
.kanbn-task-field-textarea { .kanbn-task-editor-field-checkbox,
.kanbn-task-editor-field-textarea {
box-sizing: border-box; box-sizing: border-box;
display: block; display: block;
width: 100%; width: 100%;
@ -251,16 +266,89 @@ body.vscode-dark .kanbn-task-field-input[type="date"]::-webkit-calendar-picker-i
border: 1px transparent solid; border: 1px transparent solid;
} }
.kanbn-task-field-textarea { .kanbn-task-editor-field-select {
padding-bottom: 7px;
}
.kanbn-task-editor-field-textarea {
min-height: 200px; min-height: 200px;
resize: vertical; resize: vertical;
} }
.kanbn-task-field-input:hover, .kanbn-task-field-input:focus { .kanbn-task-editor-field-input:hover, .kanbn-task-editor-field-input:focus {
border-color: var(--vscode-input-border); border-color: var(--vscode-input-border);
} }
.kanbn-task-id { .kanbn-task-editor-buttons {
text-align: right;
}
.kanbn-task-editor-button {
outline: none;
border: none;
background-color: var(--vscode-button-background);
color: var(--vscode-button-foreground);
padding: 9px 1em;
margin-left: 8px;
}
.kanbn-task-editor-button .codicon {
font-size: 11px !important;
margin-right: 0.5em;
position: relative;
top: 1px;
}
.kanbn-task-editor-button:hover, .kanbn-task-editor-button:focus {
background-color: var(--vscode-button-hoverBackground);
}
.kanbn-task-editor-button-delete:hover, .kanbn-task-editor-button-delete:focus {
background-color: #f42;
}
.kanbn-task-editor-field-errors {
font-weight: bold;
color: #f42;
}
.kanbn-task-editor-id {
margin-bottom: 8px;
font-style: italic; font-style: italic;
opacity: 0.8; opacity: 0.8;
} }
.kanbn-task-editor-row {
display: flex;
}
.kanbn-task-editor-column {
flex: 1;
margin-right: 8px;
}
.kanbn-task-editor-column:last-child {
margin-right: 0;
}
.kanbn-task-editor-field-relation-task {
flex: 2;
}
.kanbn-task-editor-field-subtask-completed {
flex: 0 0 2em;
padding: 10px 0;
}
.kanbn-task-editor-column-buttons {
white-space: nowrap;
flex: 0;
}
.kanbn-task-editor-field .kanbn-task-editor-buttons {
margin-top: 8px;
}
.kanbn-task-editor-column-buttons .kanbn-task-editor-button {
margin: 8px 0;
}