-
Notifications
You must be signed in to change notification settings - Fork 322
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
React 18 support with vis-timeline #1779
Comments
Hey @m-nathani ,
This function is called via timelineOptions:
|
Hi @annsch, hope you are doing well... Thank you for your response—I really appreciate it you making time. Your solution is quite similar to what we came up... however i like how you use we wanted to first make use of Furthermore, i believe Please find the approach below we for inline const rootElements: Record<string, ReturnType<typeof createRoot>> = {};
.
.
.
{
// other options...
...
template: (item, element, data) => {
if (!item?.id || !data?.id) return null;
// Required because when adding a new item, the id will always be 'new-timeline-item',
// Hence passing a unique id for AddItem popover, which gets destroyed on unmount using `destroyTooltipOnHide`
const elemId = item.id === NEW_TIMELINE_ID ? uuidv4() : item.id;
if (!rootElements[elemId]) {
// If not, create a new root and store it
rootElements[elemId] = createRoot(element);
}
// eslint-disable-next-line react/jsx-props-no-spreading
rootElements[elemId].render(
<Item item={{ ...item, data }} />
);
return rootElements[elemId];
},
} P.S: The only confusion here is we don't know what should we return on template function.. also in the documentation is not clear.. however returning |
@m-nathani yes, this return is very confusing :D this is why we had a look at the template function's implementation: https://github.com/visjs/vis-timeline/blob/master/lib/timeline/component/item/Item.js#L432 |
@annsch , Vis timeline is using the content to check thing's further which is strange... content = templateFunction(itemData, element, this.data); Will test retuning |
@m-nathani Are you able to implement the collapse functionality using this custom group template? |
@Jonas-PRF you need to create a structure of items with group and showNested for example: in this way it will create a group and inside this group you can have multiple items
items inside the group:
|
We're reusing (or atleast trying) the template function for the groups and the items. export const renderItemTemplate = <T extends CommonTimelineProperties>({ item, element, itemType, Component }: RenderItemParams<T>) => {
if (!element || !item) {
return ''
}
const DATA_IS_RENDERED = 'data-is-rendered'
const itemIsRendered = element.hasAttribute(DATA_IS_RENDERED)
if (!itemIsRendered) {
element.setAttribute(DATA_IS_RENDERED, 'true')
const root = createRoot(element)
root.render(
<Component key={`${itemType}-${item.id}`} {...item} />,
)
element.timelineItemRoot = root
} else {
element.timelineItemRoot.render(
<Component key={`${itemType}-${item.id}`} {...item} />,
)
}
return ''
} Using this function the groups are being rendered but once I click the collapse button the component is not rendered const TimelineGroupComponent = ({ content, treeLevel, ...props }: TimelineGroup) => {
return (
<>
{treeLevel === 1 ? (
<div className="ml-2 pb-1 pt-1">
<P type="small">{content}</P>
</div>
) : null}
{treeLevel === 2 ? (
<div className="parent-subgroup-label border-r-grey-300 flex flex-row items-center gap-1 border-r pb-2.5 pt-2.5">
<FontAwesomeIcon fontSize="16px" icon={faBolt} className="mr-1 text-blue-100" />
<div className="flex flex-col items-start">
<P type="small" className="text-grey-300">
{content}
</P>
<P type="strong" className="pr-2">
Montage tafel 1
</P>
</div>
</div>
) : null}
</>
)
} const defaultTimelineOptions: InternalTimelineOptions = {
editable: {
add: false,
remove: false,
updateGroup: true,
updateTime: true,
},
orientation: 'top',
zoomKey: 'ctrlKey',
zoomMin: timeIntervals.ONE_HOUR,
zoomMax: timeIntervals[p.options.range ?? 'ONE_WEEK'],
verticalScroll: true,
horizontalScroll: true,
snap: null,
stack: false,
showCurrentTime: true,
// margin: {
// item: {
// horizontal: -1,
// },
// },
groupHeightMode: 'fitItems',
start: defaultStart,
end: defaultEnd,
initialDate: new Date(),
range: 'ONE_WEEK',
groupTemplate: (item?: TimelineGroup, element?: TimelineElement) => {
return renderItemTemplate({ item, element, itemType: 'group', Component: p.options.groupComponent })
},
// template: (item?: TimelineItem, element?: TimelineElement) => {
// return renderItemTemplate({ item, element, itemType: 'item', Component: p.options.itemComponent })
// },
skipAmount: 'ONE_DAY',
largeSkipAmount: 'ONE_WEEK',
...p.options,
} |
Hi all, it seems you got this working with react 18. Im actually in charge of porting this from a plain js app. I have this simply code here for my component. My issue is and therefor asking for help is if i zoom in/out in the timeline view the item position is not redrawn properly so the start/end time changes when scrolling - i guess a scaling issue. Event the item content appears depending on the zoom level and hides if i drag the current visible time left or right. package.json "dependencies": {
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"@types/jest": "^27.5.2",
"@types/node": "^16.18.96",
"@types/react": "^18.2.75",
"@types/react-dom": "^18.2.24",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"typescript": "^4.9.5",
"vis-data": "^7.1.9",
"vis-timeline": "7.7.3",
"web-vitals": "^2.1.4"
}, the component import { useEffect, useRef } from "react";
import { Timeline as Vis } from 'vis-timeline/standalone';
import { DataSet } from 'vis-data';
import "vis-timeline/styles/vis-timeline-graph2d.css"
// import "./customtimeline.css"
const VisTimeline2 = () => {
const containerRef = useRef<HTMLDivElement | null>(null);
const timelineRef = useRef<Vis | null>(null);
useEffect(() => {
if (!timelineRef.current) initTimeline();
}, [containerRef]);
const initTimeline = () => {
if (!containerRef.current) return;
// var visgroups = new DataSet([
// {id: 1, content: "testgroup"}
// ]);
var items2 = new DataSet([
{id: 1, title: "Title item 1", content: 'Content item 1', start: '2024-04-19 10:00', end: '2024-04-19 14:00'}
// {id: 2, content: 'item 2', start: '2024-04-24'},
// {id: 3, content: 'item 3', start: '2024-04-18'},
// {id: 4, content: 'item 4', start: '2024-04-16 10:00', end: '2024-04-16 16:00', group: 1},
// {id: 5, content: 'item 5', start: '2024-04-25'},
// {id: 6, content: 'item 6', start: '2024-04-27', type: 'point'}
]);
timelineRef.current = new Vis(containerRef.current, items2);
}
return (
<div ref={containerRef} />
)
}
export default VisTimeline2; |
To fix the zoom error add the 'align' option.
|
Hey @Jonas-PRF were you able to figure a workaround regarding the Group disappearing on button collapse? having the same issue and seems not to be able to figure it out yet |
Hi @NwosaEmeka @Jonas-PRF , while having a high level look into the code, it appears when collapse and expand the groupTemplate function is not able to restore the group... A quick fix that made it work was to create and render on every attempt inside groupTemplate: (group, element) => {
if (!group?.id) return null;
// if (!rootElements[group.id]) {
// // If not, create a new root and store it
rootElements[group.id] = createRoot(element);
// }
// eslint-disable-next-line react/jsx-props-no-spreading
rootElements[group.id].render(<Group {...group} />);
return rootElements[group.id];
}, |
This thread helped me come up with the following which allows me to use JSX for my item/group const options = {
// ...options...
template: renderReactTemplate,
groupTemplate: renderReactTemplate
} const elementMap: { [id: string]: HTMLElement } = {};
function renderReactTemplate<T extends TimelineItem | TimelineGroup>(
itemOrGroup: T | null, // This is `null` on .destroy
element: HTMLElement
) {
// Do nothing if it's null
if (!itemOrGroup) return '';
// Do nothing special if content isn't a ReactElement
if (!isValidElement(itemOrGroup.content)) return itemOrGroup.content;
// Return the element reference if we've already rendered this
const mapId = itemOrGroup?.id;
if (elementMap[mapId]) return elementMap[mapId];
// Create a container for the react element (prevents DOM node errors)
const container = document.createElement('div');
element.appendChild(container);
ReactDOM.render(itemOrGroup?.content, container);
// Store the rendered element container to reference later
elementMap[mapId] = container;
// Return the new container
return container;
} |
For someone is using React 18 or higher:
|
I am using latest version of vis-timeline to create a view as shown in the screenshot
Moreover, using
React 16
, i was rendering items and groups using thisReactDOM
approach, as recommended in the documentation:https://visjs.github.io/vis-timeline/docs/timeline/#Templates
Here is the working example for the code.
Now we want to move to
React 18
, andReactDOM.render
is deprecated, has anyone found a way too render using React way ?Additionally i tried rendering using
createPortal
andcreateRoot
but those approach were not successful.The text was updated successfully, but these errors were encountered: