EventRender renders with wrong position - css

I'm currently building a calendar with the timeline view to get a list of events per teacher. And I want to have a week view of the timeline without showing any time per day. Basically all event of each teacher of that specific day listed on top of each other. Which works if I don't have any custom rendering. It would look like this:
Without eventRender
But, I would like to have a Popover on hover of each event to show more information so I use custom event render to inject Ant Design Popover. And since I'm using react I use ReactDOM to render my custom event.
My code somewhat looks like:
const EventDetail = ({ event, el }) => {
const content = <div>{event.title}<div>{event.description}</div></div>;
ReactDOM.render(
<Popover content={content}>
{event.title}
</Popover>,
el);
};
<FullCalendar
{...someOtherProps}
views={{
customWeek: {
type: 'resourceTimeline',
duration: { weeks: 1 },
slotDuration: { days: 1 },
},
}}
eventRender={EventDetail} />
But for some reason, the positioning of the event somehow got messed up due to improper top position. Also, the height of the column itself isn't tall enough for the amount of events rendered. Which looks like this:
With eventRender
My question is: How can I manage to render the custom events properly? Or how can I wrap my around the event element?
Update: Added codesandbox https://codesandbox.io/s/adoring-field-f1vrj
Thanks!

eventRender={info => {
info.el.id = `event-${info.event.id}`;
const content = <div>Description of {info.event.title}</div>;
setTimeout(() => {
ReactDOM.render(
<Popover content={content}>{info.event.title}</Popover>,
document.getElementById(`event-${info.event.id}`)
);
});
return info.el;
}}
Codesandbox: https://codesandbox.io/s/adoring-field-f1vrj

Related

changing css code and setting focus at the same time (why doesn't it work?)

I tried to make...
There is no input on the screen at the first rendering. When I click the button, input appears. And I want to set focuse on the input at the same time.
Let me explain what i made.
At first, the input is not visible on the screen.
Because the display property of the Box(the div tag), which is the parent component of the input, is none.
But when i click the button, the display property of the Box changes to block.
And here is what i want to do.
i'm going to set focus on the input on the screen.
In the function called when the button is clicked, I wrote a code that changes the css code and sets the focus on the input.
But it didn't work.
Please take a look at the following code.
const [inputDisplay, setInputDisplay] = useState("none");
const refInput = useRef(null);
const HandleShowInput = () => {
setInputDisplay("block");
refInput.current.focus();
};
return (
<>
<Box theme={inputDisplay}>
<Input ref={refInput}/>
<Box/>
<Button onClick={HandleShowInput}/>
</>
)
Below is the code that is dynamically changing the css of the Box component.
import styled, { css } from "styled-components";
const Box = ({ children, ...props }) => {
return <StBox {...props}>{children}</StBox>;
};
const StBox = styled.div`
${({ theme }) => {
switch (theme) {
case "block":
return css`
display: ${theme} !important;
`;
default:
break;
}
}}
`;
export default Box;
But this below code is worked. I separated the code by putting it in useEffect.
const [inputDisplay, setInputDisplay] = useState("none");
const refInput = useRef(null);
const HandleShowInput = () => {
setInputDisplay("block");
};
useEffect(() => {
refInput.current.focus();
}, [inputDisplay]);
return (
<>
<Box theme={inputDisplay}>
<Input ref={refInput}/>
<Box/>
<Button onClick={HandleShowInput}/>
</>
)
I want to know why the upper case not works and the lower case works. I don't know if I have lack react knowledge or css knowledge. I would be very grateful if you could help a beginner in react. Also, please understand if there are any unnatural sentences because i'm not good at English. thank you.
When you are trying to focus on the input element by HandleShowInput this function.Here two things are happening your changing the state and focus of input.It will focus the input but time will be so less that we can't see on the ui.And also due to the state change render will happen and again ref will get the input element. Thus you are not able to see this focussed input
But in case of useEffect this will happen after the render. After this no rendering. So we can see the focussed input
The way of thinking about React is a little different from Javascript.
You may expect the below two run in the same way.
setInputDisplay("block");
refInput.current.focus();
and
document.querySelector('#canFocus').style.display='block'
document.querySelector('#canFocus').focus();
NO~ It's not.
JS block the Dom and then focus it, it works well.
But React works like the code below.
setTimeout(()=>{
// next react render cycle callback
document.querySelector('#canNotFocus').style.display='block'
}, 1000)
document.querySelector('#canNotFocus').focus();
While focus method is called, the dom is display as none;
You set state in react, ReactDom will make it as a display block in the next life cycle of function component.
demo here : https://codesandbox.io/s/confident-wilson-q01ktj?file=/index.html
useEffect(() => {
refInput.current.focus();
}, [inputDisplay]);
is a watching function. While inputDisplay changed, the function inside will be called.
you set state to block
react re-render the component as a newer state
render function called, and dom is block
Effect watching function is called and the focus() called.

In VueJS, is there a way to make your binded styling react to the size change of the screen?

I have a div that is conditionally binded to a class in vueJS. The formula for my computed variable uses Screen.width. It seems to work correctly when first loading, but if I change the size of the screen it doesn't rebind with the new screen size, unless I refresh the page. Is there a way I can get my conditionally binding to honor the change in screen?
<div class="div_1" v-bind:class="{ horizontalScroll : showScroll }"/>
showScroll(){
return this.events.length*225>(screen.width*.84);
}
If you wanna do it this way, you will probably have to register a 'resize' listener. Your code should look something like this:
data: () => ({
windowWidth: document.documentElement.clientWidth,
windowHeight: document.documentElement.clientHeight
}),
mounted() {
window.addEventListener('resize', this.setDimensions);
},
methods: {
setDimensions() {
this.windowWidth = document.documentElement.clientWidth;
this.windowHeight = document.documentElement.clientHeight;
},
}
and don't forget to remove it:
beforeDestroy() {
window.removeEventListener('resize', this.setDimensions);
},

Vue/Vuex watcher dynamic/async component loading

I have a base component within which I have a dynamic component with a v-for that displays based on a computed property.
All I've really tried doing thus far, which was an incorrect methodology, was to wrap the method that loads data in a settimeout. This question is as much a methodology question as it is a coding question.
My base component looks like this:
<template>
<div>
<v-progress-linear
v-model="progressValue"
v-if="loading"
></v-progress-linear>
<component
v-for="table in tables"
:key="table.id"
:is="table.structure"
:table="table"
></component>
</div>
</template>
<script>
import Annual from './DataTables/Annual';
import { mapState, mapGetters } from 'vuex';
export default {
name: "Page",
props: [],
components: {
Annual,
},
data: () => ({
progressValue: 0,
loading: false,
tables: [],
}),
computed: {
...mapGetters({
currentTables: 'getCurrentPageTables',
tableTitles: 'getCurrentPageTableTitles',
}),
...mapState({
pageName: state => state.pageName,
snakeName: state => state.snakeName,
}),
methods: {
updateTables(payload) {
this.loading = true;
payload.forEach(title => {
this.tables.push(this.currentTables.filter(e => title === e.name)[0]);
this.progressValue = this.tables.length / payload.length;
})
},
},
watch: {
snakeName: {
handler() {
this.progressValue = 0;
this.updateTables(this.tableTitles);
this.$nextTick(() => {this.loading = false;})
},
immediate: true,
},
}
}
</script>
Annual.vue is simply a component that displays a Vuetify v-data-table element and its structure is fairly inconsequential to this.
For all intents and purposes we can consider currentTables and tableTitles to both be arrays, the first of objects whose data populate the v-data-tables in Annual.vue, and the second of strings which are just the names of the tables.
When the user navigates to another page the getters return different data, based on the page the user navigates to, but some of the pages have over 20 tables, which makes page loading slow upon navigation to these pages. I am trying to do one of two things:
1. Asynchronously load the components one at a time while still making the page functional for the user to navigate through.
2. Display a loader that disappears after all of the content is rendered. I'm having trouble figuring out how to do the latter because I can't put this functionality into the mounted() hook since all of this happens upon the watched parameter changing (hence the component is not re-mounted each time the route changes).
Any advice on how to tackle this would be appreciated.

React day picker overlay always on

I am using DayPickerInput and I have it set up like this (Range with 2 day picker inputs). I want to always display overlay and I don't want to hide it. I am aware of showOverlay prop but it only displays overlay during the initial rendering and the overlay can be closed. Once closed it won't be opened by default again. Is it possible to have overlay always displayed using DayPickerInput or I should use DayPicker with my own input fields?
This question is over 3 years old but in case someone stills looking for a way to do it now you can combine the props showOverlay and hideOnDayClick to have the overlay always on.
By doing something like this:
<DayPickerInput showOverlay hideOnDayClick={false} ... />
source - VERSION 7.4.8
If you're for some reason still looking for an answer, I found another tricky way of how to always show overlay.
What I've done is:
Created measuredRef that replaces default hideDayPicker method with noop:
const measuredRef = useCallback(node => {
if (node !== null) node.hideDayPicker = noop
}, [])
Passed this ref to DayPickerInput together with showOverlay prop:
<DayPickerInput ref={measuredRef} showOverlay ... />
After this manipulations you'll have overlay that is always shown.
Furthermore, if you need to control DayPickerInput state, you can:
Create useState:
const [isDatePickerOpen, setIsDatePickerOpen] = useState(false);
Use setIsDatePickerOpen the way you need (for example you want to hide overlay on day change):
<DayPickerInput
ref={measuredRef}
showOverlay
onDayChange={() => setIsDatePickerOpen(false)}
...
/>
Create custom OverlayComponent, pass it to DayPickerInput and control the way you show overlay:
const OverlayComponent = ({ children, classNames, selectedDay, ...props }) =>
(
<div
className={cn({
"is-open": isDatePickerOpen,
})}
{...props}
>
{children}
</div>
)
------------------------------
<DayPickerInput
ref={measuredRef}
showOverlay
onDayChange={() => setIsDatePickerOpen(false)}
overlayComponent={OverlayComponent}
...
/>

react css animation when component mounts

Here's a fiddle of what I want to do: https://jsfiddle.net/s7s07chm/7/
But I want to do this with react instead of jquery. Basically, I put the className of the element in the state, and on componentDidMount I update the className to initiate the transition.
But this isn't working. The component is just rendering with the transitioned state. In other words, instead of sliding down, it appears at the bottom from the beginning
Am I doing this wrong?
If so, is there another way to accomplish this?
here's the actual code
getInitialState: function() {
return {
childClass: 'child'
};
},
componentDidMount: function() {
this.setState({
childClass: 'child low'
});
},
the reason that won't work is because your DOM won't be updated until the component is mounted. So the class you're assigning with getInitialState will never appear in the DOM, but the one you set with componentDidMount will. As Ray mentioned, you should take a look at ReactCSSTransitionGroup.
componentDidMount is called after React hands them off to the browser, but the browser might not have finished drawing.
You can however use requestAnimationFrame to ensure setState is called after the browser has finished drawing the component.
componentDidMount() {
requestAnimationFrame(() => this.setState({
childClass: 'child low'
}))
}

Resources