Hi Team ,
I am using the Bryutum Taskboard in React. In which I have customised the three-dot menu icon. I want to show a task menu item on clicking that icon. I tried achieving that, and I am able to show a menu item list, but the position of the menu item list is not where I wanted it to be.
Below is the code
import React, { Fragment, useRef, useEffect, useState } from "react";
import { BryntumTaskBoard, BryntumTaskBoardProps } from "@bryntum/taskboard-react";
import { TaskBoard, ProjectModel, DomHelper, DomClassList, TaskModel } from "@bryntum/taskboard";
import "./App.scss";
import { axiosWrapper } from "src/utils/axiosWrapper";
import { useDispatch } from "react-redux";
import { drawerActions } from "src/components/drawer/drawerReducers";
import Utils from "src/Utils";
import { Avatar, Dropdown } from "antd";
const menuItems = [
{
key: "1",
label: (
<a target="_blank" rel="noopener noreferrer" href="https://www.antgroup.com">
1st menu item
</a>
),
},
{
key: "2",
label: (
<a target="_blank" rel="noopener noreferrer" href="https://www.aliyun.com">
2nd menu item
</a>
),
},
{
key: "3",
label: (
<a target="_blank" rel="noopener noreferrer" href="https://www.luohanacademy.com">
3rd menu item
</a>
),
},
];
const assigneeItems = [
{
key: "1",
label: (
<a target="_blank" rel="noopener noreferrer" href="https://www.antgroup.com">
1st menu item
</a>
),
},
{
key: "2",
label: (
<a target="_blank" rel="noopener noreferrer" href="https://www.aliyun.com">
2nd menu item
</a>
),
},
];
declare var Celoxis: any;
Celoxis.kanban = {};
const Kanban = (props: any) => {
const { reportData } = props;
const taskBoardRef = useRef<BryntumTaskBoard>(null);
const taskBoardInstance = () => taskBoardRef.current?.instance as TaskBoard;
const [kanbanId] = useState<any>("x" + Utils.randomId());
const [position, setPosition] = useState<any>({});
const dispatch = useDispatch();
if (!Celoxis.kanban[kanbanId]) {
Celoxis.kanban[kanbanId] = {};
Celoxis.kanban[kanbanId].addAssignee = (e) => {
console.log(e);
const rect = e.target.getBoundingClientRect();
setPosition({ x: rect.left, y: rect.top, action: "assignee" });
};
Celoxis.kanban[kanbanId].taskActionMenu = async (e) => {
console.log("abc", e);
const response = await axiosWrapper.get("task.Actions.wm", { id: 17924 }, dispatch);
console.log(response);
setPosition({ x: e.clientX, y: e.clientY, action: "menu" });
};
}
useEffect(() => {
return () => {
delete Celoxis.kanban[kanbanId];
};
}, []);
const dropDownItems = () => {
if (position.action === "menu") {
return menuItems;
}
if (position.action === "assignee") {
return assigneeItems;
}
return [];
};
const kanbanConfig: BryntumTaskBoardProps = {
// Url for resource avatar images
resourceImagePath: "data/TaskBoard/images/users/",
// Experimental, transition moving cards using the editor
useDomTransition: true,
// Columns to display
columns: reportData.serverData.columns,
// Field used to pair a task to a column
columnField: "state",
tasksPerRow: 1,
features: {
columnDrag: true,
taskEdit: {
disabled: true,
},
taskMenu: {
items: {
edit: {
text: "Edit",
icon: "b-fa-fw b-fa-flag",
onItem({ taskRecord }) {
taskRecord.flagged = true;
},
},
},
},
columnToolbars: reportData.serverData.isAppKanban
? { disabled: true }
: {
bottomItems: {
addTask: null,
input: {
type: "textfield",
style: "flex: 1;",
placeholder: "Name | Resources",
onAction: async (event) => {
// spliting the values to get name and resource
console.log("abc", event);
const addTask = await axiosWrapper.formPost(
"task.AddMultiple.do",
{
data: `${event.value}`,
defaults: JSON.stringify({ custom_kanban_state: `${event.source.columnRecord.data.id}` }),
},
{ type: "rp", p_p_id: reportData.serverData.params.p_p_id },
dispatch
);
if (addTask.data.success) {
const values = event.value.split("|");
const {
source: {
columnRecord,
parent: { parent: taskBoard },
},
value,
} = event;
console.log(taskBoard);
const textField = event.source;
if (value.trim() === "") return;
const resource = values[1].trim().toLowerCase();
const res = taskBoard.project.resourceStore.find((item) => item.name.toLowerCase() === resource);
if (Boolean(res)) {
taskBoard.addTask(columnRecord, null, { name: values[0], resourceImages: {} });
textField.value = "";
}
}
},
},
},
},
},
listeners: {
async beforeTaskDrop({ taskRecords, targetColumn }) {
// Show confirmation dialog
if (reportData.serverData.isAppKanban) {
dispatch(
drawerActions.showContent({
contentType: "TimeCodeList",
contentProps: { url: "timeCode.List.wm" },
boType: 51,
})
);
}
if (targetColumn.id === "Done") {
return false;
}
},
taskDrag({ taskRecords, targetColumn }) {
return targetColumn.id !== "Backlog";
},
catchAll: ({ type }) => {
if (!type.includes("mousemove") || type.includes("mouseover")) {
// console.log("type", type);
}
},
taskMenuBeforeShow: async ({ items }) => {
let response = await axiosWrapper.get("task.Actions.wm", { id: 17354 }, dispatch);
let menuItems = response.data.items;
data.forEach((item) => {
return (items[item.key] = item.text);
});
},
taskMenuItem: (e) => {
// console.log("abc", e.item.initialConfig.text);
if (e.item.initialConfig.text === "Edit") {
dispatch(
drawerActions.showContent({
contentType: "TimeCodeList",
contentProps: { url: "timeCode.List.wm" },
boType: 51,
})
);
}
},
},
headerItems: {
text: {
type: "template",
template: ({ taskRecord }) =>
// '<div class="card card-body" id="{tagId}" <tpl if="color">style="border-color:{color};"</tpl>>' +
// '<tpl if="path"><div class="path pb-1 text-light large-only">{path}</span></div></tpl>' +
'<div class="flexbox h-100 align-items-center py-2" style="display:flex ; align-items:center ;justify-content: center;flex-wrap: nowrap;">' +
'<div style="flex: 1;min-width: 0px;overflow: hidden;text-overflow: ellipsis; max-height:99%;">' +
`<a class="h6 mb-0 clickable" href={url} data-url={url} data-open="quickview" data-target="#qv-ajax">${taskRecord.data.name}</a>` +
"</div>" +
`<a style="justify-self:stretch; width: 35px;
height: 35px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color:white;
background: #4db7a9;" class="large-only"
onclick="Celoxis.kanban['${kanbanId}'].addAssignee(event)">${taskRecord.data.resourceImages[0]?.initials}</a>` +
`<div class="large-only action fa-lg far fa-ellipsis-v" onclick="Celoxis.kanban['${kanbanId}'].taskActionMenu(event)" style=" width: 16px; margin: 0; padding: 6px 0px; vertical-align: center; text-align: center; padding-left: 12px"></div>` +
"</div>" +
`<div class="large-only text-light kanban-attrs">${taskRecord.data.customAttrs}</div>` +
"</div>",
style: { width: "100%" },
},
},
footerItems: {
resourceAvatars: null,
},
// Project using inline data
project: {
tasks: reportData.serverData.initialData,
autoLoad: true,
},
taskRenderer({ taskRecord, cardConfig }) {
if (taskRecord.data.prio) {
cardConfig.class[taskRecord.data.prio] = true;
}
},
tbar: [
// Button for picking which columns to show
{ type: "columnpickerbutton" },
{ type: "zoomslider" },
{ type: "taskfilterfield" },
{
type: "textfield",
ref: "highlight",
placeholder: "Highlight tasks",
clearable: true,
keyStrokeChangeDelay: 100,
triggers: {
filter: {
align: "start",
cls: "b-fa b-fa-search",
},
},
onChange(event) {
const {
source: {
parent: { parent: taskBoard },
},
value,
} = event;
taskBoard.project.eventStore.forEach((task) => {
const taskClassList = new DomClassList(task.cls);
if (value !== "" && task.name.toLowerCase().includes(value.toLowerCase())) {
taskClassList.add("b-match");
} else {
taskClassList.remove("b-match");
}
task.cls = taskClassList.value;
});
taskBoard.element.classList[value.length > 0 ? "add" : "remove"]("b-highlighting");
},
},
],
};
return (
<Fragment>
<BryntumTaskBoard ref={taskBoardRef} {...kanbanConfig} />
{position.x ? (
<Dropdown
menu={{ items: dropDownItems() }}
open={true}
onOpenChange={(open) => {
!open && setPosition({});
}}
trigger={["click"]}
>
<div
style={{
position: "fixed",
display: "block",
top: `${position.y}px` || "200px",
left: `${position.x}px` || "400px",
zIndex: 100,
}}
>
</div>
</Dropdown>
) : (
""
)}
{}
</Fragment>
);
};
export default Kanban;
Please find below attach video for your reference