diff --git a/samples/react-kanban-board/src/kanban/IKanbanBoardRenderers.tsx b/samples/react-kanban-board/src/kanban/IKanbanBoardRenderers.tsx index d5d765d70..41920604e 100644 --- a/samples/react-kanban-board/src/kanban/IKanbanBoardRenderers.tsx +++ b/samples/react-kanban-board/src/kanban/IKanbanBoardRenderers.tsx @@ -5,4 +5,9 @@ export interface IKanbanBoardRenderers{ task?: (task:IKanbanTask) => JSX.Element ; bucketHeadline?: (bucket:IKanbanBucket) => JSX.Element ; taskDetail?: (task:IKanbanTask) => JSX.Element ; + /* + its an action not a renderer + taskEdit?: (task:IKanbanTask) => JSX.Element ; + taskAdd?: (bucket?:IKanbanBucket) => JSX.Element ; + */ } \ No newline at end of file diff --git a/samples/react-kanban-board/src/kanban/IKanbanBoardTaskActions.ts b/samples/react-kanban-board/src/kanban/IKanbanBoardTaskActions.ts index 0096e937e..83039cf41 100644 --- a/samples/react-kanban-board/src/kanban/IKanbanBoardTaskActions.ts +++ b/samples/react-kanban-board/src/kanban/IKanbanBoardTaskActions.ts @@ -1,8 +1,18 @@ import { IKanbanBucket } from "./IKanbanBucket"; +import { IKanbanTask } from "./IKanbanTask"; export interface IKanbanBoardTaskActions { toggleCompleted?: (taskId: string) => void; allowMove?: (taskId: string, prevBucket: IKanbanBucket, targetBucket: IKanbanBucket) => boolean; moved?: (taskId: string, targetBucket: IKanbanBucket) => void; +/* think about Await??? +*/ + addTaskSaved?: (task: IKanbanTask) => void; + editTaskSaved?: (task: IKanbanTask) => void; + //deleteTask?: (task: IKanbanTask) => void; + + taskEdit?: (task:IKanbanTask) => void ; + taskAdd?: (bucket?:IKanbanBucket) => void ; + } \ No newline at end of file diff --git a/samples/react-kanban-board/src/kanban/IKanbanTask.ts b/samples/react-kanban-board/src/kanban/IKanbanTask.ts index 6d387929c..f9a76dacd 100644 --- a/samples/react-kanban-board/src/kanban/IKanbanTask.ts +++ b/samples/react-kanban-board/src/kanban/IKanbanTask.ts @@ -15,13 +15,16 @@ export interface IKanbanTaskManagedProps { renderer?: (name: string, value: object, type:KanbanTaskMamagedPropertyType) => JSX.Element; } +/* 0 is bad because + const value = EnumType.xyz // = 0 + if(value) {is false} +*/ export enum KanbanTaskMamagedPropertyType { - - string, - number, - percent, - html, - person, - persons, - complex + string=1, + number=2, + percent=3, + html=4, + person=5, + persons=6, + complex=7 } \ No newline at end of file diff --git a/samples/react-kanban-board/src/kanban/KanbanBucket.tsx b/samples/react-kanban-board/src/kanban/KanbanBucket.tsx index 19f4eaf5f..50650135b 100644 --- a/samples/react-kanban-board/src/kanban/KanbanBucket.tsx +++ b/samples/react-kanban-board/src/kanban/KanbanBucket.tsx @@ -13,7 +13,10 @@ import * as strings from 'KanbanBoardStrings'; export interface IKanbanBucketProps extends IKanbanBucket { buckettasks: IKanbanTask[]; tasksettings: IKanbanBoardTaskSettings; - taskactions: IKanbanBoardTaskActions; + + + toggleCompleted?: (taskId: string) => void; + addTask?: (bucket: string) => void; onDragStart: (event, taskId: string, bucket: string) => void; onDragOver: (event, targetbucket: string) => void; @@ -50,7 +53,7 @@ export default class KanbanBucket extends React.Component { const { bucket, bucketheadline, color, buckettasks, - tasksettings, taskactions, percentageComplete, + tasksettings, percentageComplete, allowAddTask, overBucket, leavingTaskId, leavingBucket } = this.props; return ( @@ -71,19 +74,20 @@ export default class KanbanBucket extends React.Component this.props.addTask(bucket)} > {strings.AddTask} )} { buckettasks.map((t) => { - const merge = { ...t, ...tasksettings, ...taskactions }; + const merge = { ...t, ...tasksettings, }; const isMoving = (t.taskId === leavingTaskId && t.bucket === leavingBucket); return (
this.props.onDragStart(event, t.taskId, t.bucket)} diff --git a/samples/react-kanban-board/src/kanban/KanbanComponent.tsx b/samples/react-kanban-board/src/kanban/KanbanComponent.tsx index 1218630f9..760b4d956 100644 --- a/samples/react-kanban-board/src/kanban/KanbanComponent.tsx +++ b/samples/react-kanban-board/src/kanban/KanbanComponent.tsx @@ -1,5 +1,7 @@ import * as React from 'react'; import styles from './KanbanComponent.module.scss'; +import * as strings from 'KanbanBoardStrings'; + import { IKanbanTask } from './IKanbanTask'; import { IKanbanBoardTaskSettings } from './IKanbanBoardTaskSettings'; import { IKanbanBoardTaskActions } from './IKanbanBoardTaskActions'; @@ -11,11 +13,11 @@ import KanbanTaskManagedProp from './KanbanTaskManagedProp'; import { Dialog, DialogType, DialogFooter } from 'office-ui-fabric-react/lib/Dialog'; import { PrimaryButton, DefaultButton } from 'office-ui-fabric-react/lib/Button'; import { IStackStyles, Stack } from 'office-ui-fabric-react/lib/Stack'; - - - +import { clone } from '@microsoft/sp-lodash-subset'; import { CommandBar } from 'office-ui-fabric-react/lib/CommandBar'; +import { stringIsNullOrEmpty } from '@pnp/common'; +import { TooltipHost } from 'office-ui-fabric-react'; export interface IKanbanComponentProps { buckets: IKanbanBucket[]; @@ -24,6 +26,9 @@ export interface IKanbanComponentProps { taskactions: IKanbanBoardTaskActions; showCommandbar?: boolean; renderers?: IKanbanBoardRenderers; + allowEdit?: boolean; + allowAdd?: boolean; + editSchema?: boolean; /* showCommandbarNew: boolean; allowDialog: boolean; TODO im mock @@ -36,6 +41,15 @@ export interface IKanbanComponentState { overBucket?: string; openDialog: boolean; openTaskId?: string; + dialogState?: DialogState; + editTask?: IKanbanTask; + addBucket?: IKanbanBucket; +} + +export enum DialogState { + New = 1, + Edit = 2, + Display = 3 } export default class KanbanComponent extends React.Component { @@ -55,7 +69,11 @@ export default class KanbanComponent extends React.Component { const { buckets, tasks, tasksettings, taskactions, showCommandbar } = this.props; const { openDialog } = this.state; - const { leavingBucket, leavingTaskId, overBucket } = this.state + const { leavingBucket, leavingTaskId, overBucket } = this.state; + const wrappedTaskActions: IKanbanBoardTaskActions = { + + }; + return (
{showCommandbar && x.bucket == b.bucket)} tasksettings={tasksettings} - taskactions={taskactions} - openDetails={this.openDialog.bind(this)} + + toggleCompleted={this.props.taskactions && this.props.taskactions.toggleCompleted ? this.props.taskactions.toggleCompleted : undefined} + + addTask={this.internalAddTask.bind(this)} + openDetails={this.internalOpenDialog.bind(this)} + onDrop={this.onDrop.bind(this)} onDragLeave={this.onDragLeave.bind(this)} onDragOver={this.onDragOver.bind(this)} @@ -87,54 +109,119 @@ export default class KanbanComponent extends React.Component - {openDialog && (this.renderDetails())} + {openDialog && (this.renderDialog())}
); } - - private renderDetails(): JSX.Element { - const renderer = this.props.renderers && this.props.renderers.taskDetail ? this.props.renderers.taskDetail : this.internalTaskDetailRenderer; + private getTaskByID(taskId: string): IKanbanTask { const tasks = this.props.tasks.filter(t => t.taskId == this.state.openTaskId); - if (tasks.length == 1) { - const task = tasks[0]; + return tasks[0]; + } + throw "Error Taks not found by taskId"; + } - return (); + private renderDialog(): JSX.Element { + let renderer: (task?: IKanbanTask, bucket?: IKanbanBucket) => JSX.Element = () => (
Dialog Renderer Not Set
); + let task: IKanbanTask = undefined; + let bucket: IKanbanBucket = undefined; + let dialogheadline:string =''; + switch (this.state.dialogState) { + case DialogState.Edit: + task = this.getTaskByID(this.state.openTaskId); + renderer = this.internalTaskEditRenderer.bind(this); + dialogheadline =strings.EditTaskDlgHeadline; + break; + case DialogState.New: + renderer = this.internalTaskAddRenderer.bind(this); + dialogheadline =strings.AddTaskDlgHeadline; + break; + default: + task = this.getTaskByID(this.state.openTaskId); + dialogheadline =task.title; + renderer = (this.props.renderers && this.props.renderers.taskDetail) ? this.props.renderers.taskDetail : this.internalTaskDetailRenderer.bind(this); + + break; } + return (); + + // Error Not found or more than one - throw "Error Not found or more than one"; + return (
); } + private clickEditTask(): void { + const task = this.getTaskByID(this.state.openTaskId); + if (this.props.taskactions.taskEdit) { + + this.internalCloseDialog(); + this.props.taskactions.taskEdit(clone(task)); + } else { + this.setState({ + dialogState: DialogState.Edit, + editTask: clone(task) + }); + } + } + private saveEditTask() { + + if (this.props.taskactions.editTaskSaved) { + const edittask = clone(this.state.editTask); + //check fist state and than event or in the other way + this.internalCloseDialog(); + this.props.taskactions.editTaskSaved(edittask); + } else { + throw "allowEdit is Set but no handler is set"; + + } + } + private saveAddTask() { + + if (this.props.taskactions.editTaskSaved) { + const edittask = clone(this.state.editTask); + //check fist state and than event or in the other way + this.internalCloseDialog(); + this.props.taskactions.editTaskSaved(edittask); + } else { + throw "allowAdd is Set but no handler is set"; + + } + } + + + private internalTaskDetailRenderer(task: IKanbanTask): JSX.Element { return ( - {/* - - % Complete - - - {task.bucket} - - - - */} + {task.mamagedProperties && ( task.mamagedProperties.map((p, i) => { return ( @@ -146,18 +233,54 @@ export default class KanbanComponent extends React.Component ); } - private closeDialog(ev?: React.MouseEvent) { + + + private internalTaskEditRenderer(task: IKanbanTask): JSX.Element { + const schema = this.props.editSchema; //TODO + return (
Edit
); + } + private internalTaskAddRenderer(task?: IKanbanTask, bucket?: IKanbanBucket): JSX.Element { + const schema = this.props.editSchema; //TODO + return (
New
); + } + + private internalCloseDialog(ev?: React.MouseEvent) { this.setState({ openDialog: false, - openTaskId: undefined + openTaskId: undefined, + dialogState: undefined, + editTask: undefined, + addBucket: undefined }); } - private openDialog(taskid: string) { + private internalOpenDialog(taskid: string) { this.setState({ openDialog: true, - openTaskId: taskid + openTaskId: taskid, + dialogState: DialogState.Display }); + } + private internalAddTask(targetbucket?: string) { + let bucket: IKanbanBucket = undefined; + if (bucket) { + const buckets = this.props.buckets.filter((p) => p.bucket === targetbucket) + if (buckets.length === 1) { + bucket = clone(buckets[0]); + } else { + throw "Bucket not Found in addDialog"; + } + } + if (this.props.taskactions && this.props.taskactions.taskAdd) { + this.props.taskactions.taskAdd(bucket); + } else { + this.setState({ + openDialog: true, + openTaskId: '', + dialogState: DialogState.New, + addBucket: bucket + }); + } } private onDragLeave(event): void { @@ -170,7 +293,7 @@ export default class KanbanComponent extends React.Component s.bucket == this.dragelement.bucket)[0]; const target = this.props.buckets.filter(s => s.bucket == targetbucket)[0]; - + if (this.props.taskactions) { let allowMove = true; if (this.props.taskactions.allowMove) { @@ -249,15 +372,21 @@ export default class KanbanComponent extends React.Component { - return [ - { - key: 'newItem', - name: 'New', - cacheKey: 'myCacheKey', // changing this key will invalidate this items cache - iconProps: { - iconName: 'Add' - } - }] + if (this.props.allowAdd) { + //TODO + return [ + { + key: 'newItem', + name: 'New', + cacheKey: 'myAddBtnKey', + iconProps: { + iconName: 'Add' + }, + onClick: () => this.internalAddTask.bind(this) + }]; + } + return []; + } private getFarItems = () => { diff --git a/samples/react-kanban-board/src/kanban/KanbanTask.tsx b/samples/react-kanban-board/src/kanban/KanbanTask.tsx index b3f8b485b..7f43b56b8 100644 --- a/samples/react-kanban-board/src/kanban/KanbanTask.tsx +++ b/samples/react-kanban-board/src/kanban/KanbanTask.tsx @@ -8,9 +8,9 @@ import { IKanbanBoardTaskActions } from './IKanbanBoardTaskActions'; import classNames from 'classnames'; import { IconNames } from 'office-ui-fabric-react'; -export interface IKanbanTaskProps extends IKanbanTask, IKanbanBoardTaskSettings, IKanbanBoardTaskActions { - +export interface IKanbanTaskProps extends IKanbanTask, IKanbanBoardTaskSettings { + toggleCompleted?: (taskId: string) => void; openDetails: (taskId: string) => void; onDragStart: (event) => void; onDragEnd: (event) => void; diff --git a/samples/react-kanban-board/src/kanban/Readme.md b/samples/react-kanban-board/src/kanban/Readme.md index 0a827589c..47ba41858 100644 --- a/samples/react-kanban-board/src/kanban/Readme.md +++ b/samples/react-kanban-board/src/kanban/Readme.md @@ -2,7 +2,7 @@ Thinking about Kanban component with Fluent Ui Components -# current +## current allowMove from one Bucket to the Other tested move task to other Bucket works @@ -38,7 +38,8 @@ Something like this sould come out, but styling is currently bad Something like this sould come out, but styling is currently bad ![prototype](./sample.gif "prototype on 2nd day") -IMPORTANT +# IMPORTANT + ``` IKanbanTask { taskId: string; @@ -55,3 +56,21 @@ event.dataTransfer.setData('xyz','1') Unexpected call to method or property access. ``` + +# Next Steps Component: +* think about Promise Task Actions, because actions are async +* internalDislplayRenderer: Person / Persons +* EditSchema To support Edit and New PNP Controls :) +* BucketEdit Component (can be used in CustomPropertyPane) + + +# Webpart Steps + +* PNP Placeholder Control for Config +* PNP WebpartTitle Control +* DataConnection +* Usage of BucketEdit in pane +* PNP Order pane Control + + + diff --git a/samples/react-kanban-board/src/kanban/loc/en-us.js b/samples/react-kanban-board/src/kanban/loc/en-us.js index 04114ac42..3e1274178 100644 --- a/samples/react-kanban-board/src/kanban/loc/en-us.js +++ b/samples/react-kanban-board/src/kanban/loc/en-us.js @@ -1,9 +1,15 @@ -define([], function() { +define([], function () { return { "CompleteButton": "Complete", - "IsCompleted": "Is Completed", - "IsNotCompleted": "Is Not Completed", - "AddTask": "Add Task", - "OpenDetails":"Open Details" + "IsCompleted": "Is Completed", + "IsNotCompleted": "Is Not Completed", + "AddTask": "Add Task", + "OpenDetails": "Open Details", + "EditTaskBtn": "Edit", + "SaveTaskBtn": "Save and Close", + "SaveAddTaskBtn": "Add and Close", + "CloseTaskDialog": "Close", + "AddTaskDlgHeadline":"Add Task", + "EditTaskDlgHeadline":"Edit Task", } -}); \ No newline at end of file +}); diff --git a/samples/react-kanban-board/src/kanban/loc/mystrings.d.ts b/samples/react-kanban-board/src/kanban/loc/mystrings.d.ts index 6e0f89f99..0d5893f79 100644 --- a/samples/react-kanban-board/src/kanban/loc/mystrings.d.ts +++ b/samples/react-kanban-board/src/kanban/loc/mystrings.d.ts @@ -4,6 +4,12 @@ declare interface IKanbanBoardStrings { IsNotCompleted: string; AddTask: string; OpenDetails: string; + EditTaskBtn:string; + SaveTaskBtn:string; + SaveAddTaskBtn:string; + CloseTaskDialog:string; + AddTaskDlgHeadline:string; + EditTaskDlgHeadline:string; } declare module 'KanbanBoardStrings' {