Custom field editing

This commit is contained in:
Gordon 2021-06-08 01:51:57 +01:00
parent 0aa5776af3
commit 026fbf95cd
6 changed files with 556 additions and 461 deletions

View File

@ -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

@ -3,7 +3,10 @@ import * as vscode 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;
} }
@ -162,7 +176,10 @@ 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();
@ -173,7 +190,11 @@ export default class KanbnTaskPanel {
// 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();
@ -258,6 +279,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,

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

@ -53,11 +53,12 @@ const Markdown = props => (<ReactMarkdown {...{
...props, ...props,
}} />); }} />);
const TaskEditor = ({ task, tasks, columnName, columnNames, dateFormat, panelUuid, vscode }: { const TaskEditor = ({ task, tasks, columnName, columnNames, customFields, dateFormat, panelUuid, vscode }: {
task: KanbnTask | null, task: KanbnTask | null,
tasks: Record<string, KanbnTask>, tasks: Record<string, KanbnTask>,
columnName: string, columnName: string,
columnNames: string[], columnNames: string[],
customFields: { name: string, type: 'boolean' | 'date' | 'number' | 'string' }[],
dateFormat: string, dateFormat: string,
panelUuid: string, panelUuid: string,
vscode: VSCodeApi vscode: VSCodeApi
@ -76,7 +77,17 @@ const TaskEditor = ({ task, tasks, columnName, columnNames, dateFormat, panelUui
due: (task && 'due' in task.metadata) ? formatDate(task.metadata.due!, 'yyyy-mm-dd') : '', due: (task && 'due' in task.metadata) ? formatDate(task.metadata.due!, 'yyyy-mm-dd') : '',
completed: (task && 'completed' in task.metadata) ? formatDate(task.metadata.completed!, 'yyyy-mm-dd') : '', completed: (task && 'completed' in task.metadata) ? formatDate(task.metadata.completed!, '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 || []) : [],
...Object.fromEntries(
customFields.map(customField => [
customField.name,
(task && customField.name in task.metadata)
? (customField.type === 'date'
? formatDate(task.metadata[customField.name], 'yyyy-mm-dd')
: task.metadata[customField.name]
) : null,
]),
)
}, },
relations: task ? task.relations : [], relations: task ? task.relations : [],
subTasks: task ? task.subTasks : [], subTasks: task ? task.subTasks : [],
@ -112,12 +123,14 @@ const TaskEditor = ({ task, tasks, columnName, columnNames, dateFormat, panelUui
command: 'kanbn.update', command: 'kanbn.update',
taskId: task!.id, taskId: task!.id,
taskData: values, taskData: values,
customFields,
panelUuid panelUuid
}); });
} else { } else {
vscode.postMessage({ vscode.postMessage({
command: 'kanbn.create', command: 'kanbn.create',
taskData: values, taskData: values,
customFields,
panelUuid panelUuid
}); });
} }
@ -224,10 +237,41 @@ const TaskEditor = ({ task, tasks, columnName, columnNames, dateFormat, panelUui
handleChange, handleChange,
isSubmitting isSubmitting
}) => ( }) => (
<React.Fragment> <Form>
<h1 className="kanbn-task-editor-title"> <h1 className="kanbn-task-editor-title">
{editing ? 'Update task' : 'Create new task'} {editing ? 'Update task' : 'Create new task'}
{dirty && <span className="kanbn-task-editor-dirty">*</span>} {dirty && <span className="kanbn-task-editor-dirty">*</span>}
</h1>
<div className="kanbn-task-editor-buttons kanbn-task-editor-main-buttons">
{editing && <button
type="button"
className="kanbn-task-editor-button kanbn-task-editor-button-delete"
title="Delete task"
onClick={() => {
handleRemoveTask(values);
}}
>
<i className="codicon codicon-trash"></i>Delete
</button>}
{editing && <button
type="button"
className="kanbn-task-editor-button kanbn-task-editor-button-archive"
title="Archive task"
onClick={() => {
handleArchiveTask(values);
}}
>
<i className="codicon codicon-archive"></i>Archive
</button>}
<button
type="submit"
className="kanbn-task-editor-button kanbn-task-editor-button-submit"
title="Save task"
disabled={isSubmitting}
>
<i className="codicon codicon-save"></i>Save
</button>
</div>
{editing && <span className="kanbn-task-editor-dates"> {editing && <span className="kanbn-task-editor-dates">
{ {
[ [
@ -236,8 +280,6 @@ const TaskEditor = ({ task, tasks, columnName, columnNames, dateFormat, panelUui
].filter(i => i).join(', ') ].filter(i => i).join(', ')
} }
</span>} </span>}
</h1>
<Form>
<div className="kanbn-task-editor-form"> <div className="kanbn-task-editor-form">
<div className="kanbn-task-editor-column-left"> <div className="kanbn-task-editor-column-left">
<div className="kanbn-task-editor-field kanbn-task-editor-field-name"> <div className="kanbn-task-editor-field kanbn-task-editor-field-name">
@ -511,36 +553,6 @@ const TaskEditor = ({ task, tasks, columnName, columnNames, dateFormat, panelUui
</div> </div>
</div> </div>
<div className="kanbn-task-editor-column-right"> <div className="kanbn-task-editor-column-right">
<div className="kanbn-task-editor-buttons">
{editing && <button
type="button"
className="kanbn-task-editor-button kanbn-task-editor-button-delete"
title="Delete task"
onClick={() => {
handleRemoveTask(values);
}}
>
<i className="codicon codicon-trash"></i>Delete
</button>}
{editing && <button
type="button"
className="kanbn-task-editor-button kanbn-task-editor-button-archive"
title="Archive task"
onClick={() => {
handleArchiveTask(values);
}}
>
<i className="codicon codicon-archive"></i>Archive
</button>}
<button
type="submit"
className="kanbn-task-editor-button kanbn-task-editor-button-submit"
title="Save task"
disabled={isSubmitting}
>
<i className="codicon codicon-save"></i>Save
</button>
</div>
<div className="kanbn-task-editor-field kanbn-task-editor-field-column"> <div className="kanbn-task-editor-field kanbn-task-editor-field-column">
<label className="kanbn-task-editor-field-label"> <label className="kanbn-task-editor-field-label">
<p>Column</p> <p>Column</p>
@ -642,6 +654,45 @@ const TaskEditor = ({ task, tasks, columnName, columnNames, dateFormat, panelUui
name="progress" name="progress"
/> />
</div> </div>
{
customFields.map(customField => (
<div className={[
'kanbn-task-editor-field kanbn-task-editor-custom-field',
`kanbn-task-editor-custom-field-${paramCase(customField.name)}`
].join(' ')}>
<label className="kanbn-task-editor-field-label">
{customField.type === 'boolean'
? (
<>
<Field
className="kanbn-task-editor-field-input kanbn-task-editor-custom-checkbox"
type="checkbox"
name={`metadata.${customField.name}`}
/><p>{customField.name}</p>
</>
) : (
<>
<p>{customField.name}</p>
<Field
className="kanbn-task-editor-field-input"
type={{
date: 'date',
number: 'number',
string: 'text',
}[customField.type]}
name={`metadata.${customField.name}`}
/>
</>
)}
</label>
<ErrorMessage
className="kanbn-task-editor-field-errors"
component="div"
name={`metadata.${customField.name}`}
/>
</div>
))
}
<div className="kanbn-task-editor-field kanbn-task-editor-field-tags"> <div className="kanbn-task-editor-field kanbn-task-editor-field-tags">
<label className="kanbn-task-editor-field-label"> <label className="kanbn-task-editor-field-label">
<p>Tags</p> <p>Tags</p>
@ -701,7 +752,6 @@ const TaskEditor = ({ task, tasks, columnName, columnNames, dateFormat, panelUui
</div> </div>
</div> </div>
</Form> </Form>
</React.Fragment>
)} )}
</Formik> </Formik>
</div> </div>

View File

@ -102,7 +102,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>
</> </>
) )

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
-----------------------------------------------------------------------------*/ -----------------------------------------------------------------------------*/