When I override the div with a column index of 0 that has position absolute and give it a position sticky with left: 0 the first column style just breaks.
Is it possible to have fixed columns or rows with react-virtual?
combineStyles is a function that I wrote to make me able to conditionally add some styles to an element.
const VirtualTable = <T,>({ columns, data }: ITableProps<T>) => {
const parentRef = React.useRef<HTMLDivElement | null>(null);
const rowVirtualizer = useVirtual({
size: data.length,
parentRef,
estimateSize: React.useCallback((i) => 50 as any, []),
overscan: 5,
});
const columnVirtualizer = useVirtual({
horizontal: true,
size: columns.length,
parentRef,
estimateSize: React.useCallback((i) => 100, []),
overscan: 5,
});
return (
<>
<div
ref={parentRef}
className="List"
style={{
height: `calc(100vh) - ${shapes.headerHight}px`,
width: `100%`,
overflow: "auto",
}}
>
<div
style={{
height: `${rowVirtualizer.totalSize}px`,
width: `${columnVirtualizer.totalSize}px`,
position: "relative",
}}
>
{rowVirtualizer.virtualItems.map((virtualRow) => (
<React.Fragment key={virtualRow.index}>
{columnVirtualizer.virtualItems.map(
(virtualColumn, columnIndex) => {
return (
<div
key={virtualColumn.index}
style={combineStyles([
{
position: "absolute",
top: 0,
left: 0,
width: `${virtualColumn.size}px`,
height: `${virtualRow.size}px`,
transform: `translateX(${virtualColumn.start}px) translateY(${virtualRow.start}px)`,
},
columnIndex === 0 &&
{
position: "sticky",
},
])}
>
Cell {virtualRow.index}, {virtualColumn.index}
</div>
);
}
)}
</React.Fragment>
))}
</div>
</div>
</>
);
};
Related
I am using drag and drop in my project for slides. What I done is drag a slide and swap slide position with other.It works perfectly. But the issue is the symbol
comes even if it is allow to drag .I want to remove this icon and show drag icon while draging.Also I need an empty space on dragged slide. My code is
onDragStart = (event, index) => {
this.draggedItem = this.state.slides[index];
event.dataTransfer.setDragImage(event.target.parentNode, 20, 20);
event.dataTransfer.effectAllowed = "move";
event.dataTransfer.setData("text/html", event.target.parentNode);
};
onDragOver = index => {
const { slides } = this.state;
const hoveredItem = slides[index];
if (this.draggedItem === hoveredItem) {
return;
}
const filteredItems = slides.filter(item => item !== this.draggedItem);
filteredItems.splice(index, 0, this.draggedItem);
this.setState({ slides: filteredItems });
};
onDragEnd = () => {
this.draggedIdx = null;
};
<div className="draggableList">
<div key={data} onDragOver={() => onDragOver(index)}>
<div className="handle"
draggable
onDragStart={event => onDragStart(event, index)}
onDragEnd={onDragEnd}>
<div class="d-flex justify-content-center" style={{ fontWeight: "bold", paddingTop: "20px" }}>{"Slide " + (index + 1)}</div>
<div className={"py-3 d-flex justify-content-center course_slide slide_" + index}>
<div style={{ backgroundColor: 'white', height: 150, width: 120, boxShadow: '4px 4px 5px grey', cursor: 'pointer', overflowY: 'hidden', border: selected ? '2px solid limegreen' : '', position: 'relative' }} onClick={onClick}>
<h1 style={{ fontSize: 7 }}>{data.title}</h1>
<div style={{ fontSize: 4 }} dangerouslySetInnerHTML={{ __html: data.description }}>
</div>
{onDelete && <div onClick={onDelete} style={{ padding: 4, position: 'absolute', bottom: 0, right: 0 }}><DeleteOutline /></div>}
</div>
</div>
</div>
</div>
</div>
Can anyone please help me to sort out this issue?
I want to synchronize a divs scroll with a body scroll.
I tried some examples with two divs but I couldn't manage fix it with the body scroll.
Sample code with two divs: https://codesandbox.io/s/react-custom-scroll-sync-of-2-divs-10xpi
My Code
https://codesandbox.io/s/funny-rain-ditbv
import "./styles.css";
import { useRef } from "react";
export default function App() {
const firstDivRef = useRef();
const secondDivRef = useRef();
const handleScrollFirst = (scroll) => {
secondDivRef.current.scrollTop = scroll.target.scrollTop;
};
const handleScrollSecond = (scroll) => {
firstDivRef.current.scrollTop = scroll.target.scrollTop;
};
return (
<div
className="App"
style={{
display: "flex",
}}
>
<div
onScroll={handleScrollFirst}
ref={firstDivRef}
style={{
height: "500px",
overflow: "scroll",
backgroundColor: "#FFDAB9",
position: "sticky",
top: "0px"
}}
>
<div style={{ height: 5000, width: 300 }}>
The first div (or it can be tbody of a table and etc.)
{[...new Array(1000)].map((_, index) => {
const isEven = index % 2 === 0;
return (
<div style={{ backgroundColor: isEven ? "#FFFFE0 " : "#FFDAB9" }}>
{index}
</div>
);
})}
</div>
</div>
<div
onScroll={handleScrollSecond}
ref={secondDivRef}
style={{
height: "100%",
backgroundColor: "#EEE8AA"
}}
>
<div style={{ height: 5000, width: 200 }}>
The second div
{[...new Array(1000)].map((_, index) => {
const isEven = index % 2 === 0;
return (
<div style={{ backgroundColor: isEven ? "#FFFFE0 " : "#FFDAB9" }}>
{index}
</div>
);
})}
</div>
</div>
</div>
);
}
It was easy to use different divs rather than using a div and window.
But finally managed to run it with a div and the body.
The trick is they block each other since they listen each others values.
import "./styles.css";
import { useEffect, useRef, useState } from "react";
export default function App() {
const firstDivRef = useRef();
const [scrollTop, setScrollTop] = useState(0);
const [disableBodyScroll, setDisableBodyScroll] = useState(false);
const handleScrollFirst = (scroll) => {
setScrollTop(scroll.target.scrollTop);
};
useEffect(() => {
if (firstDivRef.current && !disableBodyScroll) {
firstDivRef.current.scrollTop = scrollTop;
}
if (disableBodyScroll) {
window.scrollTo(0, scrollTop);
}
}, [firstDivRef, scrollTop, disableBodyScroll]);
useEffect(() => {
const onScroll = () => {
console.log(disableBodyScroll, window.scrollY);
if (!disableBodyScroll) {
setScrollTop(window.scrollY);
}
};
// clean up code
window.removeEventListener("scroll", onScroll);
window.addEventListener("scroll", onScroll);
return () => window.removeEventListener("scroll", onScroll);
}, [disableBodyScroll]);
return (
<div
className="App"
style={{
display: "flex"
}}
>
<div
onMouseEnter={() => setDisableBodyScroll(true)}
onMouseLeave={() => setDisableBodyScroll(false)}
onScroll={handleScrollFirst}
ref={firstDivRef}
style={{
height: "500px",
overflow: "scroll",
backgroundColor: "#FFDAB9",
position: "sticky",
top: "0px"
}}
>
<div style={{ height: 5000, width: 300 }}>
The first div (or it can be tbody of a table and etc.)
{[...new Array(1000)].map((_, index) => {
const isEven = index % 2 === 0;
return (
<div style={{ backgroundColor: isEven ? "#FFFFE0 " : "#FFDAB9" }}>
{index}
</div>
);
})}
</div>
</div>
<div
style={{
height: "100%",
backgroundColor: "#EEE8AA"
}}
>
<div style={{ height: 5000, width: 200 }}>
The second div
{[...new Array(1000)].map((_, index) => {
const isEven = index % 2 === 0;
return (
<div style={{ backgroundColor: isEven ? "#FFFFE0 " : "#FFDAB9" }}>
{index}
</div>
);
})}
</div>
</div>
</div>
);
}
https://codesandbox.io/s/ancient-dream-tzuel?file=/src/App.js
Try the next example. This is a quick sketch but maybe it will help you.
https://codesandbox.io/s/gallant-goldwasser-19g4d?file=/src/App.js
Currently i'm writing draggable component with react-draggable.
However, when i drag my component into another component (outside of component parent), onDrop event won't fire.
Below here is my component:
const DraggableBaseCard = (props: {
id: String,
positionX: Number,
positionY: Number,
positionZ: Number,
width: Number | String,
height: Number | String,
zoomFactor: Number,
isLocked: Boolean,
}) => {
const boardStore = useBoardStore();
const [position, updatePosition] = useState({
x: props.positionX,
y: props.positionY,
});
const onDragStop = (_, elementData) =>
handleDrop(elementData, updatePosition, boardStore, props.id);
return (
<Draggable
defaultClassName="gulabee-base-card"
disabled={props.isLocked}
handle={props.customHandle ?? ".draggable-component"}
bounds={props.bounds ?? { left: 0, top: 0 }}
defaultPosition={position}
onStop={props.onStop ?? onDragStop}
onDrag={props.onDrag}
scale={props.zoomFactor || 1}
key={props.id}
>
<div
{...props}
className={`draggable-component ${props.className || ""} p-2`}
onDragStart={(e) => {
e.dataTransfer.setData("cardID", props.id);
console.log("Drag Start");
}}
style={{
zIndex: props.positionZ,
cursor: "pointer",
position: "absolute",
width: props.width || "10rem",
height: props.height || "auto",
border: props.noBorder
? undefined
: "solid 1px rgba(0, 0, 0, 0.3)",
}}
>
<Dropdown
overlay={() => CardContextMenu(props.id)}
onContextMenu={(e) => {
e.stopPropagation();
}}
trigger={["contextMenu"]}
>
<div
className="card-children"
style={{ width: "100%", height: "100%" }}
>
{props.children}
</div>
</Dropdown>
</div>
</Draggable>
);
};
const handleDrop = (elementData, updatePosition, boardStore, cardId) => {
updatePosition({
x: roundByGridSize(elementData?.x || 0, GRID_SIZE),
y: roundByGridSize(elementData?.y || 0, GRID_SIZE),
});
boardStore.cards[cardId].positionX = elementData?.x / GRID_SIZE;
boardStore.cards[cardId].positionY = elementData?.y / GRID_SIZE;
};
Here is how i test drop area:
const PocketBag = observer((props) => {
return (
<div style={{ height: "100%" }} onDrop={(e) => alert("Dropped")}>
Dropzone
</div>
);
});
When i drag the DraggableBaseCard into PocketBag, the alert won't show up.
The onDragStart event of the DraggableBaseCard is not working either unless i set draggable props to true, but it somehow conflict with Draggable component
Please help me with my problem i'm crying :(
You need to allow dropping by adding this code to the element to want to drop on. HTML by default doesn't allow drops
onDragOver={event=>event.preventDefault()}
I want to pass the value of width to the component Color from component Scala as prop. But I am getting an error width is not defined, or width not found. Can someone help me. This is a tsx file (typescript)
const Scale = (props: any) => {
const brewer = props.brewer;
let Length = props.Length; //accepting value from colorlegend <- flowlines
if (Length == 0) {
let width = 1;
} else {
let width = Math.floor(120 / Length);
console.log(width);
}
const colors = getColors(chroma.scale(brewer), LENGTH);
return (
<div>
<div
style={{
width: 30,
fontSize: '1.5em',
textAlign: 'right',
display: 'flex',
flexDirection: 'column',
}}
>
{brewer}
</div>{' '}
{colors.map(Color)}
</div>
);
};
const Color = (color: any, props: any) => (
<div
style={{
backgroundColor: color,
width: props.width,
height: 15,
display: 'inline-block',
}}
/>
);
Try to change the return value of the map function:
colors.map(color => <Color key={color} color={color} width={<specify-a-width>});
You should also consider the color as a component prop
const Color = (props: any) => (
<div
style={{
backgroundColor: props.color,
width: props.width,
height: 15,
display: 'inline-block',
}}
/>
);
I have integrated dialogflow in a react page and it is working now the issue is whenever I am writing a phrase in the bot is responding but the chat window is not getting auto scrolled to bottom. I want the bot window to be automatically scrolled to bottom every time.
class App extends Component {
render() {
const { feed, sendMessage } = this.props;// structure of the bot
return (
<div // which is the main div
style={{
backgroundColor: "green",
height: "70%",
width: "23%",
position: "fixed",
bottom: 0,
right: 5
}}
>
<div // inner div
style={{
height: "67%",
width: "22%",
position: "fixed",
bottom: "30px",
maxHeight: "65%",
right: "5px",
overflowY: "scroll",
overflowX: "hidden"
}}
>
<h1>CHATBOT!</h1>
{feed.map(entry => ( // the div where the user is typing the response
<div>{entry.text}</div> // inner- inner div
))}
</div>
<div
style={{
position: "fixed",
right: "23%",
bottom: "28px",
marginLeft: "-1300px"
}}
>
<input
style={{
position: "fixed",
width: "22%",
height: "3%"
}}
type="text" // the value by which the user is connected the bot
onKeyDown={e => // this is the box where the response is coming from the bot
e.keyCode === 13 ? sendMessage(e.target.value) : null
}// 13 is the ascii of ENTER
/>
</div>
</div>
);
}
}
const mapStateToProps = state => ({
feed: state
});
chat.js // intergration with dialogflow
const accessToken = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; //you have to enter your key
const client = new ApiAiClient({ accessToken });
const ON_MESSAGE = "ON_MESSAGE";
export const sendMessage = (text, sender = "user") => ({ // bot text box
type: ON_MESSAGE,
payload: { text, sender }
});
const messageMiddleware = () => next => action => {
next(action);
if (action.type === ON_MESSAGE) {
const { text } = action.payload;
client.textRequest(text).then(onSuccess);
function onSuccess(response) {// response from dialgflow
const {
result: { fulfillment }
} = response;
next(sendMessage(fulfillment.speech, "bot"));
}
}
};
const initState = [{ text: "" }];
const messageReducer = (state = initState, action) => {
switch (action.type) {
case ON_MESSAGE:
return [...state, action.payload];
default:
return state;
}
};
app.js
class App extends Component {
render() {
const { feed, sendMessage } = this.props;// structure of the bot
return (
<div // which is the main div
style={{
backgroundColor: "green",
height: "70%",
width: "23%",
position: "fixed",
bottom: 0,
right: 5
}}
>
<div // inner div
style={{
height: "67%",
width: "22%",
position: "fixed",
bottom: "30px",
maxHeight: "65%",
right: "5px",
overflowY: "scroll",
overflowX: "hidden"
}}
>
<h1>CHATBOT!</h1>
{feed.map(entry => ( // the div where the user is typing the response
<div>{entry.text}</div> // inner- inner div
))}
</div>
<div
style={{
position: "fixed",
right: "23%",
bottom: "28px",
marginLeft: "-1300px"
}}
>
<input
style={{
position: "fixed",
width: "22%",
height: "3%"
}}
type="text" // the value by which the user is connected the bot
onKeyDown={e => // this is the box where the response is coming from the bot
e.keyCode === 13 ? sendMessage(e.target.value) : null
}// 13 is the ascii of ENTER
/>
</div>
</div>
);
}
}
const mapStateToProps = state => ({
feed: state
});
I want the bot window to be automatically scrolled whenever I type anything in the window.
In the App component, you can create a span or div and place it right below the chat-feed.
Then give that element a ref, which we will use to scroll to upon receiving any new message.
You can use React.createRef() to make that ref. Refs essentially give us access to methods you traditionally see in vanilla JavaScript.
It also looks like you're receiving updated messages via props from Redux. So we can use componentDidUpdate() to run some logic that will scroll to that ref element.
class App extends Component {
endOfFeed = React.createRef()
scrollToEnd = () => {
if(this.endOfFeed.current){
this.endOfFeed.current.scrollIntoView()
}
}
componentDidUpdate(prevProps){
if(prevProps.feed.length !== this.props.feed.length){
this.scrollToEnd()
}
}
render() {
const { feed, sendMessage } = this.props;// structure of the bot
return (
<div // which is the main div
style={{
backgroundColor: "green",
height: "70%",
width: "23%",
position: "fixed",
bottom: 0,
right: 5
}}
>
<div // inner div
style={{
height: "67%",
width: "22%",
position: "fixed",
bottom: "30px",
maxHeight: "65%",
right: "5px",
overflowY: "scroll",
overflowX: "hidden"
}}
>
<h1>CHATBOT!</h1>
{feed.map(entry => ( // the div where the user is typing the response
<div>{entry.text}</div> // inner- inner div
))}
<span ref={this.endOfFeed}></span>
</div>
<div
style={{
position: "fixed",
right: "23%",
bottom: "28px",
marginLeft: "-1300px"
}}
>
<input
style={{
position: "fixed",
width: "22%",
height: "3%"
}}
type="text" // the value by which the user is connected the bot
onKeyDown={e => // this is the box where the response is coming from the bot
e.keyCode === 13 ? sendMessage(e.target.value) : null
}// 13 is the ascii of ENTER
/>
</div>
</div>
);
}
}
const mapStateToProps = state => ({
feed: state
});
You can use window.scrollY property and give offset accordingly.
Check this https://developer.mozilla.org/en-US/docs/Web/API/Window/scrollY