From be9daf1e421cd14c3ca2335b1dff77af61be2abe Mon Sep 17 00:00:00 2001 From: Gordon Date: Mon, 5 Apr 2021 13:43:59 +0100 Subject: [PATCH] Refactor task editor to use formik --- docs/styles.md | 2 +- package-lock.json | 42 +++++- package.json | 3 +- src/TaskEditor.tsx | 365 ++++++++++++++++++++++----------------------- 4 files changed, 213 insertions(+), 199 deletions(-) diff --git a/docs/styles.md b/docs/styles.md index 2e81b2b..cf948e4 100644 --- a/docs/styles.md +++ b/docs/styles.md @@ -38,4 +38,4 @@ The kanbn board has a default style which is based on the current vscode theme, ### Task editor -- `// TODO` +- `// TODO add task editor classes` diff --git a/package-lock.json b/package-lock.json index 73b34fb..a682edd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6738,6 +6738,11 @@ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, + "deepmerge": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz", + "integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==" + }, "default-gateway": { "version": "2.7.2", "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-2.7.2.tgz", @@ -8968,6 +8973,20 @@ "mime-types": "^2.1.12" } }, + "formik": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/formik/-/formik-2.2.6.tgz", + "integrity": "sha512-Kxk2zQRafy56zhLmrzcbryUpMBvT0tal5IvcifK5+4YNGelKsnrODFJ0sZQRMQboblWNym4lAW3bt+tf2vApSA==", + "requires": { + "deepmerge": "^2.1.1", + "hoist-non-react-statics": "^3.3.0", + "lodash": "^4.17.14", + "lodash-es": "^4.17.14", + "react-fast-compare": "^2.0.1", + "tiny-warning": "^1.0.2", + "tslib": "^1.10.0" + } + }, "forwarded": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", @@ -10700,11 +10719,6 @@ "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true }, - "immer": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.1.tgz", - "integrity": "sha512-7CCw1DSgr8kKYXTYOI1qMM/f5qxT5vIVMeGLDCDX8CSxsggr1Sjdoha4OhsP0AZ1UvWbyZlILHvLjaynuu02Mg==" - }, "import-cwd": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz", @@ -12244,6 +12258,11 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + }, "lodash._reinterpolate": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", @@ -16792,6 +16811,11 @@ "integrity": "sha512-X1Y+0jR47ImDVr54Ab6V9eGk0Hnu7fVWGeHQSOXHf/C2pF9c6uy3gef8QUeuUiWlNb0i08InPSE5a/KJzNzw1Q==", "dev": true }, + "react-fast-compare": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz", + "integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==" + }, "react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -19588,6 +19612,11 @@ "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz", "integrity": "sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw==" }, + "tiny-warning": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", + "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" + }, "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -19712,8 +19741,7 @@ "tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, "tty-browserify": { "version": "0.0.0", diff --git a/package.json b/package.json index 4e2bf9f..2cce83e 100644 --- a/package.json +++ b/package.json @@ -32,9 +32,8 @@ "@basementuniverse/kanbn": "file:~/Projects/kanbn", "@types/dateformat": "^3.0.1", "dateformat": "^4.5.1", + "formik": "^2.2.6", "git-user-name": "^2.0.0", - "immer": "^9.0.1", - "lodash": "^4.17.21", "param-case": "^3.0.4", "react": "^16.3.2", "react-beautiful-dnd": "12.2.0", diff --git a/src/TaskEditor.tsx b/src/TaskEditor.tsx index a4d0820..492a4a4 100644 --- a/src/TaskEditor.tsx +++ b/src/TaskEditor.tsx @@ -1,29 +1,10 @@ -import React, { useReducer, useCallback } from "react"; +import React from 'react'; +import { Formik } from 'formik'; import formatDate from 'dateformat'; -import VSCodeApi from "./VSCodeApi"; +import VSCodeApi from './VSCodeApi'; import { paramCase } from 'param-case'; -import produce from 'immer'; -import { set, has } from 'lodash'; import * as gitUsername from 'git-user-name'; -// https://levelup.gitconnected.com/handling-complex-form-state-using-react-hooks-76ee7bc937 -function reducer(state, action) { - if (action.constructor === Function) { - return { ...state, ...action(state) }; - } - if (action.constructor === Object) { - if (has(action, "_path") && has(action, "_value")) { - const { _path, _value } = action; - - return produce(state, draft => { - set(draft, _path, _value); - }); - } else { - return { ...state, ...action }; - } - } -} - const TaskEditor = ({ task, columnName, columnNames, dateFormat, vscode }: { task: KanbnTask|null, columnName: string, @@ -32,183 +13,189 @@ const TaskEditor = ({ task, columnName, columnNames, dateFormat, vscode }: { vscode: VSCodeApi }) => { const editing = task !== null; - const [taskData, setTaskData] = useReducer(reducer, { - id: task ? task.id : '', - name: task ? task.name : '', - description: task ? task.description : '', - column: columnName, - progress: task ? task.progress : 0, - metadata: { - 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(), - tags: (task && 'tags' in task.metadata) ? task.metadata.tags : [] - }, - relations: [], - subTasks: [], - comments: [] - }); - const handleChange = useCallback(({ target: { value, name, type } }) => { - const updatePath = name.split("."); + // Called when the name field is changed + const handleUpdateName = ({ target: { value }}, values) => { - // Handle updating checkbox states (depends on previous state) - if (type === 'checkbox') { - setTaskData((previousState) => ({ - [name]: !previousState[name] - })); - return; - } + // Update the id preview + values.id = paramCase(value); - // Handle updating root-level properties - if (updatePath.length === 1) { - const [key] = updatePath; - const newTaskData = { - [key]: value - }; - - // If the name is updated, generate a new id and set the webview panel title - if (key === 'name') { - newTaskData['id'] = paramCase(value); - vscode.postMessage({ - command: 'kanbn.updatePanelTitle', - title: value || 'Untitled task' - }); - } - setTaskData(newTaskData); - } - - // Handle updating nested properties using _path and _value - if (updatePath.length > 1) { - setTaskData({ - _path: updatePath, - _value: value - }); - } - }, []); - - const handleSubmit = e => { - e.preventDefault(); - - // If a task prop was passed in, we're updating a task, otherwise we're creating a new task - if (editing) { - vscode.postMessage({ - command: 'kanbn.update' - }); - } else { - vscode.postMessage({ - command: 'kanbn.create' - }); - } - console.log(e); + // Update the webview panel title + vscode.postMessage({ + command: 'kanbn.updatePanelTitle', + title: value || 'Untitled task' + }); }; return ( -
+

{editing ? 'Update task' : 'Create new task'}

-
{ + const errors: { name?: string } = {}; + // if (!values.email) { + // errors.email = 'Required'; + // } else if ( + // !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(values.email) + // ) { + // errors.email = 'Invalid email address'; + // } + return errors; + }} + onSubmit={(values, { setSubmitting }) => { + if (editing) { + vscode.postMessage({ + command: 'kanbn.update' + }); + } else { + vscode.postMessage({ + command: 'kanbn.create' + }); + } + console.log(values); + setSubmitting(false); }} > -
-
- - {taskData.id} -
-
- -
-
-
-
- - -
-
- -
-
- -
-
- -
-
- -
-
-
- +
+
+ + {errors.name && touched.name && errors.name} + {values.id} +
+
+ +
+
+
+
+ + +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+ + )} + + ); -} +}; export default TaskEditor;