react-flow change node name - javascript

I am very new to react.js (and somewhat new to javascript) so apologies in advance.
I am looking at this example here:
https://reactflow.dev/examples/drag-and-drop/
I believe that matches up to the git repo here:
https://github.com/wbkd/react-flow/tree/main/example/src/DragNDrop
I would like to modify the code so instead of the text being "input", "default", "output", I would like it to be "alpha", "beta" "gamma".
I realize I should be able to set the label as shown in line 19 here:
https://github.com/wbkd/react-flow/blob/main/example/src/DragNDrop/index.tsx
However, in the onDragStart method shown here: https://github.com/wbkd/react-flow/blob/main/example/src/DragNDrop/Sidebar.tsx
They call a event.dataTransfer.setData('application/reactflow', nodeType); . I'm not quite sure how to change hat to set the "label".
Any help would be much appreciated, especially for a beginner in react. Thanks!

You can take a look at this sandbox for live working example.
Names of the nodes can be changed in Sidebar.jsx component like:
import React from "react";
export default () => {
const onDragStart = (event, nodeType, label) => {
event.dataTransfer.setData("application/reactflow", nodeType);
event.dataTransfer.setData("label", label);
event.dataTransfer.effectAllowed = "move";
};
return (
<aside>
<div className="description">
You can drag these nodes to the pane on the right.
</div>
<div
className="dndnode input"
onDragStart={(event) => onDragStart(event, "input", "alpha")}
draggable
>
alpha
</div>
<div
className="dndnode"
onDragStart={(event) => onDragStart(event, "default", "beta")}
draggable
>
beta
</div>
<div
className="dndnode output"
onDragStart={(event) => onDragStart(event, "output", "gamma")}
draggable
>
gamma
</div>
</aside>
);
};
and dragged label info in DndFlow.tsx component can be retrieved like:
import React, { useState, useRef } from "react";
import ReactFlow, {
ReactFlowProvider,
addEdge,
removeElements,
Controls
} from "react-flow-renderer";
import Sidebar from "./Sidebar";
import "./dnd.css";
const initialElements = [
{
id: "1",
type: "input",
data: { label: "alpha" },
position: { x: 250, y: 5 }
}
];
let id = 0;
const getId = () => `dndnode_${id++}`;
const DnDFlow = () => {
const reactFlowWrapper = useRef(null);
const [reactFlowInstance, setReactFlowInstance] = useState(null);
const [elements, setElements] = useState(initialElements);
const onConnect = (params) => setElements((els) => addEdge(params, els));
const onElementsRemove = (elementsToRemove) =>
setElements((els) => removeElements(elementsToRemove, els));
const onLoad = (_reactFlowInstance) =>
setReactFlowInstance(_reactFlowInstance);
const onDragOver = (event) => {
event.preventDefault();
event.dataTransfer.dropEffect = "move";
};
const onDrop = (event) => {
event.preventDefault();
const reactFlowBounds = reactFlowWrapper.current.getBoundingClientRect();
const type = event.dataTransfer.getData("application/reactflow");
const label = event.dataTransfer.getData("label");
const position = reactFlowInstance.project({
x: event.clientX - reactFlowBounds.left,
y: event.clientY - reactFlowBounds.top
});
const newNode = {
id: getId(),
type,
position,
data: { label: label }
};
setElements((es) => es.concat(newNode));
};
return (
<div className="dndflow">
<ReactFlowProvider>
<div className="reactflow-wrapper" ref={reactFlowWrapper}>
<ReactFlow
elements={elements}
onConnect={onConnect}
onElementsRemove={onElementsRemove}
onLoad={onLoad}
onDrop={onDrop}
onDragOver={onDragOver}
>
<Controls />
</ReactFlow>
</div>
<Sidebar />
</ReactFlowProvider>
</div>
);
};
export default DnDFlow;

Related

React - generating a unique random key causes infinite loop

I have a componenet that wraps its children and slides them in and out based on the stage prop, which represents the active child's index.
As this uses a .map() to wrap each child in a div for styling, I need to give each child a key prop. I want to assign a random key as the children could be anything.
I thought I could just do this
key={`pageSlide-${uuid()}`}
but it causes an infinite loop/React to freeze and I can't figure out why
I have tried
Mapping the children before render and adding a uuid key there, calling it via key={child.uuid}
Creating an array of uuids and assigning them via key={uuids[i]}
Using a custom hook to store the children in a state and assign a uuid prop there
All result in the same issue
Currently I'm just using the child's index as a key key={pageSlide-${i}} which works but is not best practice and I want to learn why this is happening.
I can also assign the key directly to the child in the parent component and then use child.key but this kinda defeats the point of generating the key
(uuid is a function from react-uuid, but the same issue happens with any function including Math.random())
Here is the full component:
import {
Children,
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from "react";
import PropTypes from "prop-types";
import uuid from "react-uuid";
import ProgressBarWithTicks from "./ProgressBarWithTicks";
import { childrenPropType } from "../../../propTypes/childrenPropTypes";
const calculateTranslateX = (i = 0, stage = 0) => {
let translateX = stage === i ? 0 : 100;
if (i < stage) {
translateX = -100;
}
return translateX;
};
const ComponentSlider = ({ stage, children, stageCounter }) => {
const childComponents = Children.toArray(children);
const containerRef = useRef(null);
const [lastResize, setLastResize] = useState(null);
const [currentMaxHeight, setCurrentMaxHeight] = useState(
containerRef.current?.childNodes?.[stage]?.clientHeight
);
const updateMaxHeight = useCallback(
(scrollToTop = true) => {
if (scrollToTop) {
window.scrollTo(0, 0);
}
setCurrentMaxHeight(
Math.max(
containerRef.current?.childNodes?.[stage]?.clientHeight,
window.innerHeight -
(containerRef?.current?.offsetTop || 0) -
48
)
);
},
[stage]
);
useEffect(updateMaxHeight, [stage, updateMaxHeight]);
useEffect(() => updateMaxHeight(false), [lastResize, updateMaxHeight]);
const resizeListener = useMemo(
() => new MutationObserver(() => setLastResize(Date.now())),
[]
);
useEffect(() => {
if (containerRef.current) {
resizeListener.observe(containerRef.current, {
childList: true,
subtree: true,
});
}
}, [resizeListener]);
return (
<div className="w-100">
{stageCounter && (
<ProgressBarWithTicks
currentStage={stage}
stages={childComponents.length}
/>
)}
<div
className="position-relative divSlider align-items-start"
ref={containerRef}
style={{
maxHeight: currentMaxHeight || null,
}}>
{Children.map(childComponents, (child, i) => (
<div
key={`pageSlide-${uuid()}`}
className={`w-100 ${
stage === i ? "opacity-100" : "opacity-0"
} justify-content-center d-flex`}
style={{
zIndex: childComponents.length - i,
transform: `translateX(${calculateTranslateX(
i,
stage
)}%)`,
pointerEvents: stage === i ? null : "none",
cursor: stage === i ? null : "none",
}}>
{child}
</div>
))}
</div>
</div>
);
};
ComponentSlider.propTypes = {
children: childrenPropType.isRequired,
stage: PropTypes.number,
stageCounter: PropTypes.bool,
};
ComponentSlider.defaultProps = {
stage: 0,
stageCounter: false,
};
export default ComponentSlider;
It is only called in this component (twice, happens in both instances)
import { useEffect, useReducer, useState } from "react";
import { useParams } from "react-router-dom";
import {
FaCalendarCheck,
FaCalendarPlus,
FaHandHoldingHeart,
} from "react-icons/fa";
import { IoIosCart } from "react-icons/io";
import { mockMatches } from "../../../templates/mockData";
import { initialSwapFormState } from "../../../templates/initalStates";
import swapReducer from "../../../reducers/swapReducer";
import useFetch from "../../../hooks/useFetch";
import useValidateFields from "../../../hooks/useValidateFields";
import IconWrap from "../../common/IconWrap";
import ComponentSlider from "../../common/transitions/ComponentSlider";
import ConfirmNewSwap from "./ConfirmSwap";
import SwapFormWrapper from "./SwapFormWrapper";
import MatchSwap from "../Matches/MatchSwap";
import SwapOffers from "./SwapOffers";
import CreateNewSwap from "./CreateNewSwap";
import smallNumberToWord from "../../../functions/utils/numberToWord";
import ComponentFader from "../../common/transitions/ComponentFader";
const formStageHeaders = [
"What shift do you want to swap?",
"What shifts can you do instead?",
"Pick a matching shift",
"Good to go!",
];
const NewSwap = () => {
const { swapIdParam } = useParams();
const [formStage, setFormStage] = useState(0);
const [swapId, setSwapId] = useState(swapIdParam || null);
const [newSwap, dispatchNewSwap] = useReducer(swapReducer, {
...initialSwapFormState,
});
const [matches, setMatches] = useState(mockMatches);
const [selectedMatch, setSelectedMatch] = useState(null);
const [validateHook, newSwapValidationErrors] = useValidateFields(newSwap);
const fetchHook = useFetch();
const setStage = (stageIndex) => {
if (!swapId && stageIndex > 1) {
setSwapId(Math.round(Math.random() * 100));
}
if (stageIndex === "reset") {
setSwapId(null);
dispatchNewSwap({ type: "reset" });
}
setFormStage(stageIndex === "reset" ? 0 : stageIndex);
};
const saveMatch = async () => {
const matchResponse = await fetchHook({
type: "addSwap",
options: { body: newSwap },
});
if (matchResponse.success) {
setStage(3);
} else {
setMatches([]);
dispatchNewSwap({ type: "setSwapMatch" });
setStage(1);
}
};
useEffect(() => {
// set matchId of new selected swap
dispatchNewSwap({ type: "setSwapMatch", payload: selectedMatch });
}, [selectedMatch]);
return (
<div>
<div className="my-3">
<div className="d-flex justify-content-center w-100 my-3">
<ComponentSlider stage={formStage}>
<IconWrap colour="primary">
<FaCalendarPlus />
</IconWrap>
<IconWrap colour="danger">
<FaHandHoldingHeart />
</IconWrap>
<IconWrap colour="warning">
<IoIosCart />
</IconWrap>
<IconWrap colour="success">
<FaCalendarCheck />
</IconWrap>
</ComponentSlider>
</div>
<ComponentFader stage={formStage}>
{formStageHeaders.map((x) => (
<h3
key={`stageHeading-${x.id}`}
className="text-center my-3">
{x}
</h3>
))}
</ComponentFader>
</div>
<div className="mx-auto" style={{ maxWidth: "400px" }}>
<ComponentSlider stage={formStage} stageCounter>
<SwapFormWrapper heading="Shift details">
<CreateNewSwap
setSwapId={setSwapId}
newSwap={newSwap}
newSwapValidationErrors={newSwapValidationErrors}
dispatchNewSwap={dispatchNewSwap}
validateFunction={validateHook}
setStage={setStage}
/>
</SwapFormWrapper>
<SwapFormWrapper heading="Swap in return offers">
<p>
You can add up to{" "}
{smallNumberToWord(5).toLowerCase()} offers, and
must have at least one
</p>
<SwapOffers
swapId={swapId}
setStage={setStage}
newSwap={newSwap}
dispatchNewSwap={dispatchNewSwap}
setMatches={setMatches}
/>
</SwapFormWrapper>
<SwapFormWrapper>
<MatchSwap
swapId={swapId}
setStage={setStage}
matches={matches}
selectedMatch={selectedMatch}
setSelectedMatch={setSelectedMatch}
dispatchNewSwap={dispatchNewSwap}
saveMatch={saveMatch}
/>
</SwapFormWrapper>
<SwapFormWrapper>
<ConfirmNewSwap
swapId={swapId}
setStage={setStage}
selectedSwap={selectedMatch}
newSwap={newSwap}
/>
</SwapFormWrapper>
</ComponentSlider>
</div>
</div>
);
};
NewSwap.propTypes = {};
export default NewSwap;
One solution
#Nick Parsons has pointed out I don't even need a key if using React.Children.map(), so this is a non issue
I'd still really like to understand what was causing this problem, aas far as I can tell updateMaxHeight is involved, but I can't quite see the chain that leads to an constant re-rendering
Interstingly if I use useMemo for an array of uuids it works
const uuids = useMemo(
() => Array.from({ length: childComponents.length }).map(() => uuid()),
[childComponents.length]
);
/*...*/
key={uuids[i]}

Why isn't my setInterval function being called in this React app?

I have a function to populate certain sections of my site with new content on a regular interval (5000), however my function isn't executing when I run the app?
If I set the interval to a console.log function, it works, but it doesn't seem to be working on my 'updateTestimonial' function. If I manually change the idx, it works fine...
I am just learning so apologies if this is obvious -_-
import coverphoto from './img/bkg/moss2.jpg';
import quotes from './img/quotes.png';
import quotes2 from './img/quotes2.png';
import React, { useState, useEffect } from 'react';
const Home = props =>{
const testimonials = [
{
name: 'Ben Frank',
position: 'CEO',
photo: require('./img/chrisphoto.png'),
text:
"Test"
},
{
name: 'Jill Cha',
position: 'Software Engineer',
photo: require('./img/chrisphoto.png'),
text:
'Testimonial1'
},
{
name: 'Adam Niskanen',
position: 'Data Entry',
photo: require('./img/chrisphoto.png'),
text:
"Testimonial2"
},
];
let idx = 0;
let name = testimonials[idx].name;
let position= testimonials[idx].position;
let photo= testimonials[idx].photo;
let text = testimonials[idx].text;
function updateTestimonial() {
idx++;
if (idx > testimonials.length - 1) {
idx = 0;
}
name = testimonials[idx].name;
position= testimonials[idx].position;
photo= testimonials[idx].photo;
text = testimonials[idx].text;
}
setInterval(updateTestimonial, 5000);
return (
<div className="home_main" style={{
backgroundImage: `url(${coverphoto})`,
backgroundRepeat: 'no-repeat'}}>
<div className="home-testimonial-container"><img className="quotes" src={quotes}/><img className="quotes2" src={quotes2}/><div className='testimonial-entry'>
<img className='testimonial-photo'
src={photo}
></img>
<div className='testimonial-text'>
<h3 className='titlestyle2'>{name}</h3><h3 className='subtitlestyle' style={{fontSize: "10pt"}}>{position}</h3></div>
<div className='testimonial-body-container'><h3 className='bodystyle' style={{fontStyle:"italic"}}>{text}</h3>
</div>
</div></div>
</div>
);
}
export default Home;
You need to call setInterval inside a useEffect hook and don't forget to clear the interval when the component unmounts
check the following article https://upmostly.com/tutorials/setinterval-in-react-components-using-hooks
You can do like this
const [idx, setIdx] = useState(0);
useEffect(() => {
const interval = setInterval(() => setIdx((previousValue) => previousValue
+1), 5000);
return () => {
clearInterval(interval);
};
}, []);

Styled qr codes in react select options

I need to create react-select where every option is different styled qr code with js library: qr-code-styling.
The problem is that in documentation qr code are appended to dom elements, and options in react-select are created dynamically. I can't find a way to append these qr codes to my options. I think the problem is that, I can't properly create refs, which I can use to apped the codes. Is there any way to do it?
import React, { useEffect, useRef, createRef } from "react";
import "./styles.css";
import QRCodeStyling from "qr-code-styling";
import Select from "react-select";
const qrCode = new QRCodeStyling({
data: "https://qr-code-styling.com",
width: 300,
height: 300,
image:
"https://upload.wikimedia.org/wikipedia/commons/5/51/Facebook_f_logo_%282019%29.svg",
dotsOptions: {
color: "#4267b2",
type: "rounded"
},
imageOptions: {
crossOrigin: "anonymous",
margin: 20
}
});
const qrCode2 = new QRCodeStyling({
data: "https://qr-code-styling.com",
width: 50,
height: 50,
image:
"https://upload.wikimedia.org/wikipedia/commons/5/51/Facebook_f_logo_%282019%29.svg",
dotsOptions: {
color: "red",
type: "rounded"
},
imageOptions: {
crossOrigin: "anonymous",
margin: 20
}
});
const data = [
{
id: "1",
qrcode: qrCode
},
{
id: "2",
qrcode: qrCode2
}
];
export default function App() {
let refs = useRef([createRef(), createRef()]);
const optionLabel = (option, index) => <div ref={refs.current[option.id]} />;
useEffect(() => {
refs.current.forEach((ref) => qrCode.append(ref.current));
}, []);
return (
<div className="App">
<Select
className="select-logo"
getOptionLabel={optionLabel}
options={data}
/>
</div>
);
}
codesandbox
One solution would be to have the Select let you know when to append the QR codes, by using its onMenuOpen prop. First, though, we need to be able to find the element to append, so we need to add a formatOptionLabel (which is what I suspect you meant to use, instead of getOptionLabel in your sandbox):
<Select
formatOptionLabel={({ id }) => <div id={`qr-code-option-${id}`} />}
...etc
/>
Then, we can add a state variable to be triggered when the menu loads:
const [menuIsOpen, setMenuIsOpen] = React.useState(false)
and, when the menuIsOpen, append the options to their qrCodes:
useEffect(() => {
data.forEach(d => {
let o = document.getElementById('qr-code-option-' + d.id)
if (o) {
d.qrcode?.append(o)
}
})
}, [menuIsOpen])
Finally, we use the onMenuOpen and onMenuClose props to trigger the state changes, resulting in the desired result! The full code of the App component is now:
export default function App() {
const [menuIsOpen, setMenuIsOpen] = React.useState(false)
useEffect(() => {
data.forEach(d => {
let o = document.getElementById(`qr-code-option-${d.id}`)
if (o) {
d.qrcode?.append(o)
}
})
}, [menuIsOpen])
return (
<div className="App">
<Select
className="select-logo"
formatOptionLabel={({ id }) => <div id={`qr-code-option-${id}`} />}
onMenuOpen={() => setMenuIsOpen(true)}
onMenuClose={() => setMenuIsOpen(false)}
options={data}
/>
</div>
);
}
which gives:

How to apply the style of the rest of the items when I click item in REACT?

pic1
pic2
pic3
When I select one item, I want to apply the new style of one item and the original style of the rest of the items
Example
when I click SubBookmark33
Current
pic1 -> pic2
But I want to
pic1 -> pic3
BookmarksFolder.js
import BookmarksFolderNode from './BookmarksFolderNode';
import classes from './BookmarksFolder.module.css';
import { folders } from '../../resources/data';
function BookmarksFolder() {
return (
<div className={classes.bookmarksFolder}>
{folders.map((folder) => (
<BookmarksFolderNode
key={folder.id}
folder={folder}
/>
))}
</div>
);
}
export default BookmarksFolder;
BookmarksFolderNode.js
import { useState, useRef } from 'react';
import { AiFillCaretDown, AiFillCaretRight } from 'react-icons/ai';
import Folder from '../../resources/img/folder.svg';
import OpenedFolder from '../../resources/img/opened_folder.svg';
import classes from './BookmarksFolderNode.module.css';
function BookmarksFolderNode(props) {
const [folderIsOpen, setFolderIsOpen] = useState(false);
const tab = useRef();
const img = useRef();
const title = useRef();
const selectFolderHandler = () => {
tab.current.style.backgroundColor = '#1a73eb';
img.current.src = OpenedFolder;
title.current.style.color = '#1a73eb';
};
const openFolderHandler = () => {
setFolderIsOpen((prevState) => !prevState);
};
const paddingLeft = 20 * (props.folder.depth - 1);
return (
<div className={classes.bookmarksFolderNode}>
<div className={classes.bookmarksMainFolderNode}>
<div className={classes.verticalTab} ref={tab}></div>
<div className={classes.innerContainer} style={{paddingLeft}}>
<div className={classes.icon} onClick={openFolderHandler}>
{folderIsOpen ? (
<AiFillCaretDown className={classes.ironIcon} />
) : (
<AiFillCaretRight className={classes.ironIcon} />
)}
</div>
<img src={Folder} className={classes.folderIcon} ref={img} />
<div
className={classes.menuLabel}
onClick={selectFolderHandler}
ref={title}
>
{props.folder.title}
</div>
</div>
</div>
<div className={classes.bookmarksSubFolderNode}>
{props.folder.subFolder &&
props.folder.subFolder.map((subFolder) => (
<BookmarksFolderNode key={subFolder.id} folder={subFolder} />
))}
</div>
</div>
);
}
export default BookmarksFolderNode;
data.js
export const folders = [
{
id: 1,
depth: 1,
title: 'Bookmark 1',
subFolder: [
{
id: 1,
depth: 2,
title: 'SubBookmark 1',
subFolder: [
{
id: 1,
depth: 3,
title: 'SubBookmark 11',
},
{
id: 2,
depth: 3,
title: 'SubBookmark 22',
},
{
id: 3,
depth: 3,
title: 'SubBookmark 33',
subFolder: [
{
id: 1,
depth: 4,
title: 'SubBookmark 111',
},
],
},
],
},
{
id: 2,
depth: 2,
title: 'SubBookmark 2',
},
],
},
];
The idea is that, when you select a folder, you should deselect the others from parent component BookmarksFolder. The solution is a little bit tricky because you select the folder using BookmarksFolderNode local state (I mean folderIsOpen).
Well, lets start to use parent component's state to select/deselect folder:
import BookmarksFolderNode from './BookmarksFolderNode';
import classes from './BookmarksFolder.module.css';
import { folders } from '../../resources/data';
function BookmarksFolder() {
const [foldersSelected, setFoldersSelected] = useState(new Array(folders.length).fill(false));
const selectFolder = (index) => {
let result = new Array(folders.length).fill(false);
result[index] = true;
setFoldersSelected(result);
}
return (
<div className={classes.bookmarksFolder}>
{folders.map((folder, index) => (
<BookmarksFolderNode
key={folder.id}
folder={folder}
isSelected={foldersSelected[index]}
select={(index) => selectFolder(index)}
index={index}
/>
))}
</div>
);
}
export default BookmarksFolder;
So now in BookmarksFolderNode we have to use isSelected and select instead of folderIsOpen and openFolderHandler. Not only but we have to replicate the same logic for subfolders:
import { useState, useRef, useEffect } from 'react';
import { AiFillCaretDown, AiFillCaretRight } from 'react-icons/ai';
import Folder from '../../resources/img/folder.svg';
import OpenedFolder from '../../resources/img/opened_folder.svg';
import classes from './BookmarksFolderNode.module.css';
function BookmarksFolderNode(props) {
const [subfoldersSelected, setSubFoldersSelected] = useState(new Array(props.folder.subFolder.length).fill(false));
const tab = useRef();
const img = useRef();
const title = useRef();
useEffect(() => {
if (props.isSelected) {
tab.current.style.backgroundColor = '#1a73eb';
img.current.src = OpenedFolder;
title.current.style.color = '#1a73eb';
}
else {
tab.current.style.backgroundColor = 'black';
img.current.src = Folder;
title.current.style.color = 'black';
}
}, [props.isSelected])
const openFolderHandler = () => {
props.select(props.index)
};
const paddingLeft = 20 * (props.folder.depth - 1);
const selectSubfolder = (index) => {
let result = new Array(props.folder.subFolder.length).fill(false);
result[index] = true;
setSubFoldersSelected(result);
}
return (
<div className={classes.bookmarksFolderNode}>
<div className={classes.bookmarksMainFolderNode}>
<div className={classes.verticalTab} ref={tab}></div>
<div className={classes.innerContainer} style={{paddingLeft}}>
<div className={classes.icon} onClick={openFolderHandler}>
{props.isSelected ? (
<AiFillCaretDown className={classes.ironIcon} />
) : (
<AiFillCaretRight className={classes.ironIcon} />
)}
</div>
<img src={Folder} className={classes.folderIcon} ref={img} />
<div
className={classes.menuLabel}
onClick={openFolderHandler}
ref={title}
>
{props.folder.title}
</div>
</div>
</div>
<div className={classes.bookmarksSubFolderNode}>
{props.folder.subFolder &&
props.folder.subFolder.map((subFolder, index) => (
<BookmarksFolderNode key={subFolder.id} folder={subFolder} index={index} isSelected={subfoldersSelected[index]} select={(index) => selectSubfolder(index)} />
))}
</div>
</div>
);
}
export default BookmarksFolderNode;
So now, if you select a folder (or a subfolder) the others folder (or subfolders) will be deselected and the useEffect will apply the desidered css.
Your problem seems to be that you are just editing the currently clicked node. What do you need to do is in "selectFolderHandler" to reset the image of all other items.
One approach would be to iterate through each item, and set the image to initial, and then change the currently selected (not wise because of performance).
A suggested approach would be to find an open folder image (if it exists) and reset it. The simplest is to search by class or ref attribute.

React-Big-Calendar Drag and Drop month view always drags event from most left column

I need help with this bug I've encountered while using big-react-calendar. When dragging an event, the event always moves to the most left column despite where the mouse is. Moving the event to a different time slot from any other view works just okay though. We're using next-js for this project alongside tailwind-css.
Here is a video of what the bug looks like.
Thank you for your help.
In Test Calendar
import "react-big-calendar/lib/css/react-big-calendar.css";
import "react-big-calendar/lib/addons/dragAndDrop/styles.css";
import Router from "next/router";
import React, { useState } from "react";
import Select from "react-select";
import { Calendar, dateFnsLocalizer, Views } from "react-big-calendar";
import withDragAndDrop from "react-big-calendar/lib/addons/dragAndDrop";
import format from "date-fns/format";
import parse from "date-fns/parse";
import startOfWeek from "date-fns/startOfWeek";
import getDay from "date-fns/getDay";
const locales = {
"en-US": require("date-fns/locale/en-US"),
};
const localizer = dateFnsLocalizer({
format,
parse,
startOfWeek,
getDay,
locales,
});
const DragAndDropCalendar = withDragAndDrop(Calendar);
export default function MyCalendar() {
const [events, setEvents] = useState(
[
{
id:1,
title:"help",
start:new Date(),
end: new Date(),
}
]
);
const [showCalendarModal, setShowCalendarModal] = useState(false);
const [selectedDate, setSelectedDate] = useState(undefined);
const [showAssignments, setShowAssignments] = useState(true);
const [showCourseTimes, setShowCourseTimes] = useState(true);
const [showOfficeHours, setShowOfficeHours] = useState(true);
const [showStudySessions, setShowStudySessions] = useState(false); // add later
const setView = [
setShowAssignments,
setShowCourseTimes,
setShowOfficeHours,
setShowStudySessions,
];
const handleSelectSlot = ({ start, end, slots }) => {
//pop modal up, from this and be able to pass through, these slots
setSelectedDate(start);
return;
};
const moveEvent = ({ event, start, end }) => {
const thisEvent = event;
const nextEvents = events.map((existingEvent) => {
return existingEvent.id == event.id
? { ...existingEvent, start, end }
: existingEvent;
});
setEvents(nextEvents);
};
const viewOptions = [
{ value: "Assignments", label: "Assignment due dates", index: 0 },
{ value: "Courses", label: "Courses times", index: 1 },
{ value: "Office Hours", label: "Office hours", index: 2 },
{
value: "Study Sessions",
label: "Study sessions (Not implemented)",
index: 3,
},
];
const filterViewChange = (selected) => {
var indexOfSelected = [];
selected.map((selection) =>
indexOfSelected.push(selection.index)
);
viewOptions.map((option) =>
indexOfSelected.includes(option.index)
? setView[option.index](true)
: setView[option.index](false)
);
};
return (
<div className="h-auto">
<div>
<DragAndDropCalendar
selectable
resizable
popup
localizer={localizer}
events={events}
startAccessor="start"
endAccessor="end"
onEventDrop={moveEvent}
onEventResize={moveEvent}
onSelectEvent={(event) => handleSelectEvent(event)}
onSelectSlot={handleSelectSlot}
style={{ height: 500 }}
defaultDate={new Date()}
/>
<Select
defaultValue={[viewOptions[0], viewOptions[1], viewOptions[2]]}
isMulti
options={viewOptions}
name="View"
onChange={filterViewChange}
/>
</div>
</div>
);
}
// notes
// https://jquense.github.io/react-big-calendar/examples/index.html?ref=creativetim#prop-scrollToTime
//examples
// https://github.com/jyhwng/dashboard/blob/master/src/components/Calendar/FullCalendar.js
// https://demos.creative-tim.com/nextjs-material-dashboard-pro/admin/calendar?_ga=2.137767600.1882045019.1616627454-2002691398.1612228651
// https://www.creative-tim.com/learning-lab/nextjs/react-big-calendar/material-dashboard
// error fixes
// https://github.com/jquense/react-big-calendar/issues/234 // style loaderm
// https://nextjs.org/docs/advanced-features/dynamic-import#with-no-ssr // no document found, x on ssr or have loading thing.
// https://stackoverflow.com/questions/24647839/referenceerror-document-is-not-defined-in-plain-javascript/24648001
// https://stackoverflow.com/questions/62348977/is-there-a-way-to-disable-resizing-and-dragging-of-events-for-on-specific-views
Page calling TestCalendar
import dynamic from "next/dynamic";
const Calendar = dynamic(() => import("#/components/calendars/TestCalendar"), {
loading: () => <p>Loading...</p>,
ssr: false,
});
export default function Dashboard() {
return <Calendar />;
}
Found out on their gh issue page that this is a bug with ver. 0.33.2. Luckily it's currently being resolved. tiwatson has fixed it (thank you) and is now waited to be merged into master.
Here is the thread that I found the issue and the pull request.
https://github.com/jquense/react-big-calendar/issues/1886
https://github.com/jquense/react-big-calendar/pull/1892
Edit: They merged it! Yay!

Categories

Resources