Edit and Add Actions dummy implementation

This commit is contained in:
petkir 2020-06-20 11:50:46 +02:00
parent f5a3e8e9f8
commit 5a84a00f54
9 changed files with 259 additions and 77 deletions

View File

@ -5,4 +5,9 @@ export interface IKanbanBoardRenderers{
task?: (task:IKanbanTask) => JSX.Element ; task?: (task:IKanbanTask) => JSX.Element ;
bucketHeadline?: (bucket:IKanbanBucket) => JSX.Element ; bucketHeadline?: (bucket:IKanbanBucket) => JSX.Element ;
taskDetail?: (task:IKanbanTask) => JSX.Element ; taskDetail?: (task:IKanbanTask) => JSX.Element ;
/*
its an action not a renderer
taskEdit?: (task:IKanbanTask) => JSX.Element ;
taskAdd?: (bucket?:IKanbanBucket) => JSX.Element ;
*/
} }

View File

@ -1,8 +1,18 @@
import { IKanbanBucket } from "./IKanbanBucket"; import { IKanbanBucket } from "./IKanbanBucket";
import { IKanbanTask } from "./IKanbanTask";
export interface IKanbanBoardTaskActions { export interface IKanbanBoardTaskActions {
toggleCompleted?: (taskId: string) => void; toggleCompleted?: (taskId: string) => void;
allowMove?: (taskId: string, prevBucket: IKanbanBucket, targetBucket: IKanbanBucket) => boolean; allowMove?: (taskId: string, prevBucket: IKanbanBucket, targetBucket: IKanbanBucket) => boolean;
moved?: (taskId: string, targetBucket: IKanbanBucket) => void; 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 ;
} }

View File

@ -15,13 +15,16 @@ export interface IKanbanTaskManagedProps {
renderer?: (name: string, value: object, type:KanbanTaskMamagedPropertyType) => JSX.Element; 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 { export enum KanbanTaskMamagedPropertyType {
string=1,
string, number=2,
number, percent=3,
percent, html=4,
html, person=5,
person, persons=6,
persons, complex=7
complex
} }

View File

@ -13,7 +13,10 @@ import * as strings from 'KanbanBoardStrings';
export interface IKanbanBucketProps extends IKanbanBucket { export interface IKanbanBucketProps extends IKanbanBucket {
buckettasks: IKanbanTask[]; buckettasks: IKanbanTask[];
tasksettings: IKanbanBoardTaskSettings; tasksettings: IKanbanBoardTaskSettings;
taskactions: IKanbanBoardTaskActions;
toggleCompleted?: (taskId: string) => void;
addTask?: (bucket: string) => void;
onDragStart: (event, taskId: string, bucket: string) => void; onDragStart: (event, taskId: string, bucket: string) => void;
onDragOver: (event, targetbucket: string) => void; onDragOver: (event, targetbucket: string) => void;
@ -50,7 +53,7 @@ export default class KanbanBucket extends React.Component<IKanbanBucketProps, IK
*/ */
public render(): React.ReactElement<IKanbanBucketProps> { public render(): React.ReactElement<IKanbanBucketProps> {
const { bucket, bucketheadline, color, buckettasks, const { bucket, bucketheadline, color, buckettasks,
tasksettings, taskactions, percentageComplete, tasksettings, percentageComplete,
allowAddTask, overBucket, leavingTaskId, leavingBucket } = this.props; allowAddTask, overBucket, leavingTaskId, leavingBucket } = this.props;
return ( return (
@ -71,19 +74,20 @@ export default class KanbanBucket extends React.Component<IKanbanBucketProps, IK
{allowAddTask && (<ActionButton {allowAddTask && (<ActionButton
iconProps={{ iconName: 'Add' }} iconProps={{ iconName: 'Add' }}
allowDisabledFocus={true} allowDisabledFocus={true}
onClick={() => this.props.addTask(bucket)}
> >
{strings.AddTask} {strings.AddTask}
</ActionButton>)} </ActionButton>)}
{ {
buckettasks.map((t) => { buckettasks.map((t) => {
const merge = { ...t, ...tasksettings, ...taskactions }; const merge = { ...t, ...tasksettings, };
const isMoving = (t.taskId === leavingTaskId && t.bucket === leavingBucket); const isMoving = (t.taskId === leavingTaskId && t.bucket === leavingBucket);
return (<div className={isMoving ? styles.placeholder : undefined} key={'' + t.taskId} > return (<div className={isMoving ? styles.placeholder : undefined} key={'' + t.taskId} >
<KanbanTask <KanbanTask
key={'task' + t.taskId} key={'task' + t.taskId}
{...merge} {...merge}
toggleCompleted={this.props.toggleCompleted}
isMoving={isMoving} isMoving={isMoving}
openDetails={this.props.openDetails} openDetails={this.props.openDetails}
onDragStart={(event) => this.props.onDragStart(event, t.taskId, t.bucket)} onDragStart={(event) => this.props.onDragStart(event, t.taskId, t.bucket)}

View File

@ -1,5 +1,7 @@
import * as React from 'react'; import * as React from 'react';
import styles from './KanbanComponent.module.scss'; import styles from './KanbanComponent.module.scss';
import * as strings from 'KanbanBoardStrings';
import { IKanbanTask } from './IKanbanTask'; import { IKanbanTask } from './IKanbanTask';
import { IKanbanBoardTaskSettings } from './IKanbanBoardTaskSettings'; import { IKanbanBoardTaskSettings } from './IKanbanBoardTaskSettings';
import { IKanbanBoardTaskActions } from './IKanbanBoardTaskActions'; import { IKanbanBoardTaskActions } from './IKanbanBoardTaskActions';
@ -11,11 +13,11 @@ import KanbanTaskManagedProp from './KanbanTaskManagedProp';
import { Dialog, DialogType, DialogFooter } from 'office-ui-fabric-react/lib/Dialog'; import { Dialog, DialogType, DialogFooter } from 'office-ui-fabric-react/lib/Dialog';
import { PrimaryButton, DefaultButton } from 'office-ui-fabric-react/lib/Button'; import { PrimaryButton, DefaultButton } from 'office-ui-fabric-react/lib/Button';
import { IStackStyles, Stack } from 'office-ui-fabric-react/lib/Stack'; 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 { CommandBar } from 'office-ui-fabric-react/lib/CommandBar';
import { stringIsNullOrEmpty } from '@pnp/common';
import { TooltipHost } from 'office-ui-fabric-react';
export interface IKanbanComponentProps { export interface IKanbanComponentProps {
buckets: IKanbanBucket[]; buckets: IKanbanBucket[];
@ -24,6 +26,9 @@ export interface IKanbanComponentProps {
taskactions: IKanbanBoardTaskActions; taskactions: IKanbanBoardTaskActions;
showCommandbar?: boolean; showCommandbar?: boolean;
renderers?: IKanbanBoardRenderers; renderers?: IKanbanBoardRenderers;
allowEdit?: boolean;
allowAdd?: boolean;
editSchema?: boolean;
/* /*
showCommandbarNew: boolean; showCommandbarNew: boolean;
allowDialog: boolean; TODO im mock allowDialog: boolean; TODO im mock
@ -36,6 +41,15 @@ export interface IKanbanComponentState {
overBucket?: string; overBucket?: string;
openDialog: boolean; openDialog: boolean;
openTaskId?: string; openTaskId?: string;
dialogState?: DialogState;
editTask?: IKanbanTask;
addBucket?: IKanbanBucket;
}
export enum DialogState {
New = 1,
Edit = 2,
Display = 3
} }
export default class KanbanComponent extends React.Component<IKanbanComponentProps, IKanbanComponentState> { export default class KanbanComponent extends React.Component<IKanbanComponentProps, IKanbanComponentState> {
@ -55,7 +69,11 @@ export default class KanbanComponent extends React.Component<IKanbanComponentPro
public render(): React.ReactElement<IKanbanComponentProps> { public render(): React.ReactElement<IKanbanComponentProps> {
const { buckets, tasks, tasksettings, taskactions, showCommandbar } = this.props; const { buckets, tasks, tasksettings, taskactions, showCommandbar } = this.props;
const { openDialog } = this.state; const { openDialog } = this.state;
const { leavingBucket, leavingTaskId, overBucket } = this.state const { leavingBucket, leavingTaskId, overBucket } = this.state;
const wrappedTaskActions: IKanbanBoardTaskActions = {
};
return ( return (
<div> <div>
{showCommandbar && <CommandBar {showCommandbar && <CommandBar
@ -74,8 +92,12 @@ export default class KanbanComponent extends React.Component<IKanbanComponentPro
{...merge} {...merge}
buckettasks={tasks.filter((x) => x.bucket == b.bucket)} buckettasks={tasks.filter((x) => x.bucket == b.bucket)}
tasksettings={tasksettings} 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)} onDrop={this.onDrop.bind(this)}
onDragLeave={this.onDragLeave.bind(this)} onDragLeave={this.onDragLeave.bind(this)}
onDragOver={this.onDragOver.bind(this)} onDragOver={this.onDragOver.bind(this)}
@ -87,25 +109,48 @@ export default class KanbanComponent extends React.Component<IKanbanComponentPro
)} )}
</div> </div>
{openDialog && (this.renderDetails())} {openDialog && (this.renderDialog())}
</div> </div>
); );
} }
private getTaskByID(taskId: string): IKanbanTask {
private renderDetails(): JSX.Element {
const renderer = this.props.renderers && this.props.renderers.taskDetail ? this.props.renderers.taskDetail : this.internalTaskDetailRenderer;
const tasks = this.props.tasks.filter(t => t.taskId == this.state.openTaskId); const tasks = this.props.tasks.filter(t => t.taskId == this.state.openTaskId);
if (tasks.length == 1) { if (tasks.length == 1) {
const task = tasks[0]; return tasks[0];
}
throw "Error Taks not found by taskId";
}
private renderDialog(): JSX.Element {
let renderer: (task?: IKanbanTask, bucket?: IKanbanBucket) => JSX.Element = () => (<div>Dialog Renderer Not Set</div>);
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 (<Dialog return (<Dialog
minWidth={600} minWidth={600}
hidden={!this.state.openDialog} hidden={!this.state.openDialog}
onDismiss={this.closeDialog.bind(this)} onDismiss={this.internalCloseDialog.bind(this)}
dialogContentProps={{ dialogContentProps={{
type: DialogType.largeHeader, type: DialogType.largeHeader,
title: task.title, title: dialogheadline,
subText: '' subText: ''
}} }}
modalProps={{ modalProps={{
@ -113,28 +158,70 @@ export default class KanbanComponent extends React.Component<IKanbanComponentPro
styles: { main: { minWidth: 600 } } styles: { main: { minWidth: 600 } }
}} }}
> >
{renderer(task)} {renderer(task, bucket)}
<DialogFooter>
{(this.props.allowEdit && this.state.dialogState === DialogState.Display) &&
(<PrimaryButton onClick={this.clickEditTask.bind(this)} text={strings.EditTaskBtn} />)}
{(this.props.allowEdit && this.state.dialogState === DialogState.Edit) &&
(<PrimaryButton onClick={this.saveEditTask.bind(this)} text={strings.SaveTaskBtn} />)}
{(this.props.allowAdd && this.state.dialogState === DialogState.New) &&
(<PrimaryButton onClick={this.saveAddTask.bind(this)} text={strings.SaveAddTaskBtn} />)}
<DefaultButton onClick={this.internalCloseDialog.bind(this)} text={strings.CloseTaskDialog} />
</DialogFooter>
</Dialog>); </Dialog>);
}
// Error Not found or more than one // Error Not found or more than one
throw "Error Not found or more than one";
return (<div></div>); return (<div></div>);
} }
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 { private internalTaskDetailRenderer(task: IKanbanTask): JSX.Element {
return (<Stack> return (<Stack>
{/* <Stack horizontal horizontalAlign="stretch">
<Stack.Item align="auto" styles={rowStyle1}>
<span>% Complete</span>
</Stack.Item>
<Stack.Item align="stretch" styles={rowStyle2}>
<span>{task.bucket}</span>
</Stack.Item>
</Stack>
<KanbanTaskManagedProp name:'person' ... key={p.name+i} />
*/}
{task.mamagedProperties && ( {task.mamagedProperties && (
task.mamagedProperties.map((p, i) => { task.mamagedProperties.map((p, i) => {
return ( return (
@ -146,19 +233,55 @@ export default class KanbanComponent extends React.Component<IKanbanComponentPro
</Stack> </Stack>
); );
} }
private closeDialog(ev?: React.MouseEvent<HTMLButtonElement>) {
private internalTaskEditRenderer(task: IKanbanTask): JSX.Element {
const schema = this.props.editSchema; //TODO
return (<div>Edit</div>);
}
private internalTaskAddRenderer(task?: IKanbanTask, bucket?: IKanbanBucket): JSX.Element {
const schema = this.props.editSchema; //TODO
return (<div>New</div>);
}
private internalCloseDialog(ev?: React.MouseEvent<HTMLButtonElement>) {
this.setState({ this.setState({
openDialog: false, openDialog: false,
openTaskId: undefined openTaskId: undefined,
dialogState: undefined,
editTask: undefined,
addBucket: undefined
}); });
} }
private openDialog(taskid: string) { private internalOpenDialog(taskid: string) {
this.setState({ this.setState({
openDialog: true, 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 { private onDragLeave(event): void {
console.log('onDragLeave'); console.log('onDragLeave');
@ -170,7 +293,7 @@ export default class KanbanComponent extends React.Component<IKanbanComponentPro
private onDragEnd(event): void { private onDragEnd(event): void {
console.log('onDragEnd'); console.log('onDragEnd');
this.dragelement=undefined; this.dragelement = undefined;
} }
private onDragStart(event, taskId: string, bucket: string): void { private onDragStart(event, taskId: string, bucket: string): void {
@ -249,15 +372,21 @@ export default class KanbanComponent extends React.Component<IKanbanComponentPro
} }
private getItems = () => { private getItems = () => {
if (this.props.allowAdd) {
//TODO
return [ return [
{ {
key: 'newItem', key: 'newItem',
name: 'New', name: 'New',
cacheKey: 'myCacheKey', // changing this key will invalidate this items cache cacheKey: 'myAddBtnKey',
iconProps: { iconProps: {
iconName: 'Add' iconName: 'Add'
},
onClick: () => this.internalAddTask.bind(this)
}];
} }
}] return [];
} }
private getFarItems = () => { private getFarItems = () => {

View File

@ -8,9 +8,9 @@ import { IKanbanBoardTaskActions } from './IKanbanBoardTaskActions';
import classNames from 'classnames'; import classNames from 'classnames';
import { IconNames } from 'office-ui-fabric-react'; 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; openDetails: (taskId: string) => void;
onDragStart: (event) => void; onDragStart: (event) => void;
onDragEnd: (event) => void; onDragEnd: (event) => void;

View File

@ -2,7 +2,7 @@
Thinking about Kanban component with Fluent Ui Components Thinking about Kanban component with Fluent Ui Components
# current ## current
allowMove from one Bucket to the Other tested allowMove from one Bucket to the Other tested
move task to other Bucket works 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 Something like this sould come out, but styling is currently bad
![prototype](./sample.gif "prototype on 2nd day") ![prototype](./sample.gif "prototype on 2nd day")
IMPORTANT # IMPORTANT
``` ```
IKanbanTask { IKanbanTask {
taskId: string; taskId: string;
@ -55,3 +56,21 @@ event.dataTransfer.setData('xyz','1')
Unexpected call to method or property access. 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

View File

@ -1,9 +1,15 @@
define([], function() { define([], function () {
return { return {
"CompleteButton": "Complete", "CompleteButton": "Complete",
"IsCompleted": "Is Completed", "IsCompleted": "Is Completed",
"IsNotCompleted": "Is Not Completed", "IsNotCompleted": "Is Not Completed",
"AddTask": "Add Task", "AddTask": "Add Task",
"OpenDetails":"Open Details" "OpenDetails": "Open Details",
"EditTaskBtn": "Edit",
"SaveTaskBtn": "Save and Close",
"SaveAddTaskBtn": "Add and Close",
"CloseTaskDialog": "Close",
"AddTaskDlgHeadline":"Add Task",
"EditTaskDlgHeadline":"Edit Task",
} }
}); });

View File

@ -4,6 +4,12 @@ declare interface IKanbanBoardStrings {
IsNotCompleted: string; IsNotCompleted: string;
AddTask: string; AddTask: string;
OpenDetails: string; OpenDetails: string;
EditTaskBtn:string;
SaveTaskBtn:string;
SaveAddTaskBtn:string;
CloseTaskDialog:string;
AddTaskDlgHeadline:string;
EditTaskDlgHeadline:string;
} }
declare module 'KanbanBoardStrings' { declare module 'KanbanBoardStrings' {