diff --git a/demo-app/src/stories/table/TreeFixedDataTableGantt.stories.tsx b/demo-app/src/stories/table/TreeFixedDataTableGantt.stories.tsx
index 940d844a..d57242ab 100644
--- a/demo-app/src/stories/table/TreeFixedDataTableGantt.stories.tsx
+++ b/demo-app/src/stories/table/TreeFixedDataTableGantt.stories.tsx
@@ -1,17 +1,57 @@
import { TreeFixedDataTableRRC } from "@crispico/foundation-react/components/treeFixedDataTable/TreeFixedDataTable";
-import { Main, Task, root } from "@crispico/foundation-react/components/treeFixedDataTable/TreeFixedDataTable.stories";
+import { Main, Task, root, Segment } from "@crispico/foundation-react/components/treeFixedDataTable/TreeFixedDataTable.stories";
import { ReduxReusableComponents } from "@crispico/foundation-react/reduxReusableComponents/ReduxReusableComponents";
import { Utils } from "@crispico/foundation-react/utils/Utils";
-import { Group, Item, Timeline } from "@famiprog-foundation/react-gantt";
+import { Group, Item, ItemRenderer, Timeline } from "@famiprog-foundation/react-gantt";
import { Cell, Column, Table } from 'fixed-data-table-2';
import { d } from "../sampleData";
import { Message } from "semantic-ui-react";
+import { getKeyThenIncreaseKey } from "antd/lib/message";
export default {
title: 'Features/Table',
};
+class CustomItemRenderer extends ItemRenderer {
+ render() {
+ if (this.props.item.isParentSegment) {
+ return
+ } else {
+ return super.render();
+ }
+ }
+}
+
+class ParentItemRenderer extends ItemRenderer {
+ hasCustomShape() {
+ return true;
+ }
+
+ drawCustomShapeRenderer() {
+ const ARROW_WIDTH = 20;
+ const height = this.getHeight() as number;
+ // We need to make the rectangle a little bit bigger because the row height is 40 px and the actual segment height is 30.
+ // So if we draw the rectangle only till the middle of the segment
+ // the text (that has 18px height) will overflow the rectangle with 3 px
+ const rectangleHeight = (this.getHeight() as number) / 2 + 4;
+ const rightArrowStartX = this.props.width - ARROW_WIDTH;
+ const leftArrowPoints = "0," + rectangleHeight + " " + ARROW_WIDTH + "," + rectangleHeight + " " + ARROW_WIDTH / 2 + "," + height + " 0," + rectangleHeight;
+ const rightArrowPoints = rightArrowStartX + "," + rectangleHeight + " " + (rightArrowStartX + ARROW_WIDTH) + "," + rectangleHeight + " "
+ + (rightArrowStartX + ARROW_WIDTH / 2) + "," + height + " " + rightArrowStartX + "," + rectangleHeight;
+
+ return <>
+
+
+
+ >
+ }
+
+ getTitleStyle() {
+ return {alignSelf: "start"};
+ }
+}
+
export const TreeTable = () => {
// adding an additional task here, so that the shape of the data appears in the Storybook code snippet
// however, subtle detail: don't do this in prod; I mean, don't give a new "root" to the component on each render (which happens here)
@@ -55,7 +95,7 @@ export const TreeTable = () => {
getChildrenFunction={task => Object.keys(task.subtasks).map(key => { return { localId: key, item: task.subtasks[key] } })}
initialExpandedIds={{ 1: true, 2: true, [`2${Utils.defaultIdSeparator}0`]: true }}
renderMainElementFunction={({ mainChildren, linearizedItems }) => {
- const items: Item[] = [];
+ const items: (Item & {isParentSegment: boolean}) [] = [];
const groups: Group[] = [];
// using a for (instead of 2 x map()) in order to avoid 2 iterations
linearizedItems.forEach((li, i) => {
@@ -64,7 +104,8 @@ export const TreeTable = () => {
const task = navigateToTask(li.itemId);
task.segments.forEach((segment, j) => items.push({
key: "" + i + "." + j, row: i, start: segment.start, end: segment.end, title: segment.percentComplete + "%",
- color: segment.percentComplete == 0 ? "#FA0000" : (segment.percentComplete === 100 ? "#9ACD32" : "#FFA500")
+ color: segment.percentComplete == 0 ? "#FA0000" : (segment.percentComplete === 100 ? "#9ACD32" : "#FFA500"),
+ isParentSegment: task.subtasks != undefined && task.subtasks.length > 0
}))
});
@@ -74,7 +115,8 @@ export const TreeTable = () => {
width={420} height={300}>
Name} width={400} cell={props => mainChildren[props.rowIndex]} />
- } />
+ }
+ itemRenderer={CustomItemRenderer}/>
}}
renderItemFunction={({ linearizedItem }) => navigateToTask(linearizedItem.itemId).name}
/>
diff --git a/src/components/ItemRenderer.js b/src/components/ItemRenderer.js
index 46462a53..1df5c6b9 100644
--- a/src/components/ItemRenderer.js
+++ b/src/components/ItemRenderer.js
@@ -4,6 +4,12 @@ import React from 'react';
import {Item} from '../utils/itemUtils';
const ITEM_RENDERER_CLS = 'rct9k-item-renderer';
+
+// This class is used in case of a custom shape renderer drawn via `drawCustomShapeRenderer()`.
+// It disables the visual styles related to the default rectangle shape of the renderer:
+// 1. The background color and the borders
+// 2. The box shadow effect
+const CUSTOM_FIGURE_ITEM_RENDERER_CLS = 'rct9k-custom-shape-item-renderer';
const ITEM_RENDERER_GLOW_CLS = 'rct9k-item-glow';
/**
@@ -34,6 +40,12 @@ export default class ItemRenderer extends React.Component {
*/
tooltip: PropTypes.string,
+ /**
+ * It's passed by the parent. Though not used by this component. It exists because maybe subclasses want to use it.
+ * @type { number }
+ */
+ width: PropTypes.number,
+
/**
* The height of the segment (item).
*
@@ -83,6 +95,11 @@ export default class ItemRenderer extends React.Component {
*/
style: PropTypes.object,
+ /**
+ * @type { object }
+ */
+ titleStyle: PropTypes.object,
+
/**
* Class name used to render the segment (item).
* @type { string }
@@ -100,6 +117,7 @@ export default class ItemRenderer extends React.Component {
tooltip: undefined,
className: undefined,
style: {},
+ titleStyle: {},
item: {}
};
@@ -209,6 +227,10 @@ export default class ItemRenderer extends React.Component {
return style;
}
+ getTitleStyle() {
+ return this.props.titleStyle;
+ }
+
/**
* Returns a css class used to apply glow on item hover.
* @returns { string }
@@ -222,13 +244,48 @@ export default class ItemRenderer extends React.Component {
* @returns { string }
*/
getClassName() {
- return ITEM_RENDERER_CLS + ' ' + this.props.className + ' ' + this.getGlowOnHoverClassName();
+ return (
+ ITEM_RENDERER_CLS +
+ ' ' +
+ this.props.className +
+ ' ' +
+ this.getGlowOnHoverClassName() +
+ (this.hasCustomShape() ? ' ' + CUSTOM_FIGURE_ITEM_RENDERER_CLS : '')
+ );
+ }
+
+ hasCustomShape() {
+ return false;
+ }
+
+ /**
+ * If the user wants an item renderer with a special shape:
+ * 1. It needs to override `hasCustomShape()` to return true
+ * 1. And implement the drawCustomShapeRenderer(). This needs to return the content of a svg
+ *
+ * @type { JSX.element | undefined}
+ */
+ drawCustomShapeRenderer() {
+ throw new Error('In case of a custom shape renderer the `drawCustomShapeRenderer()` should be implemented');
}
render() {
return (
-
- {this.getTitle()}
+
+
+ {this.getTitle()}
+
+ {this.hasCustomShape() && (
+
+ )}
);
}
diff --git a/src/style.css b/src/style.css
index 81be6b45..2a532692 100644
--- a/src/style.css
+++ b/src/style.css
@@ -221,10 +221,10 @@
width: 2px;
}
-.rct9k-item-renderer {
- opacity: 0.8;
- border: darkgrey solid 1px;
- border-radius: 3px;
+.rct9k-custom-shape-item-renderer {
+ box-shadow: none !important;
+ border: none !important;
+ background: none !important;
}
.rct9k-item-renderer-inner {
diff --git a/src/utils/itemUtils.js b/src/utils/itemUtils.js
index 88a4fb47..2f3229af 100644
--- a/src/utils/itemUtils.js
+++ b/src/utils/itemUtils.js
@@ -83,7 +83,14 @@ export function rowItemsRenderer(
data-item-index={i.key}
className={outerClassnames}
style={{left, width, top, backgroundColor: 'transparent'}}>
-
+
);
});