Drop Event won't be fired (react-draggable) - reactjs

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()}

Related

Fixed row and column with react-virtual module

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>
</>
);
};

Material UI Custom Hover Color

Haven't made this feature before where you can change the color of button's hover.
I have already made a feature to change the radius with a slider, background color and font color using color-picker. However, I noticed the hover (for background AND font) could be better.
Here is the code:
import React from "react";
import { makeStyles } from "#material-ui/core/styles";
import Grid from "#material-ui/core/Grid";
import Slider from "#material-ui/core/Slider";
import Input from "#material-ui/core/Input";
import Button from "#material-ui/core/Button";
import { ChromePicker } from "react-color";
const useStyles = makeStyles((theme) => ({
root: {
"& > *": {
margin: theme.spacing(1)
}
},
Button: {
width: 150,
height: 50,
borderRadius: "var(--borderRadius)"
},
color: {
width: "36px",
height: "14px",
borderRadius: "2px"
},
swatch: {
padding: "5px",
background: "#fff",
borderRadius: "1px",
display: "inline-block",
cursor: "pointer"
},
popover: {
position: "absolute",
zIndex: "2"
},
cover: {
position: "fixed",
top: "0px",
right: "0px",
bottom: "0px",
left: "0px"
}
}));
export default function InputSlider() {
const classes = useStyles();
const [value, setValue] = React.useState(30);
const [color, setColor] = React.useState({ r: 0, g: 0, b: 0, a: 1 });
const [fontColor, setFontColor] = React.useState({
r: 255,
g: 255,
b: 255,
a: 1
});
const [displayColorPicker, setDisplayColorPicker] = React.useState(true);
const handleSliderChange = (event, newValue) => {
setValue(newValue);
};
const handleInputChange = (event) => {
setValue(event.target.value === "" ? "" : Number(event.target.value));
};
const handleBlur = () => {
if (value < 0) {
setValue(0);
} else if (value > 30) {
setValue(30);
}
};
const handleClick = () => {
setDisplayColorPicker(!displayColorPicker);
};
const handleClose = () => {
setDisplayColorPicker(false);
};
const handleChange = (color) => {
setColor(color.rgb);
};
const handleFontColorChange = (color) => {
setFontColor(color.rgb);
};
return (
<div className={classes.root}>
<style>
{`:root {
--borderRadius = ${value}px;
}`}
</style>
<Button
style={{
borderRadius: value,
background: `rgba(${color.r}, ${color.g}, ${color.b}, ${color.a})`,
color: `rgba(${fontColor.r}, ${fontColor.g}, ${fontColor.b}, ${fontColor.a})`
}}
variant="contained"
color="primary"
value="value"
onChange={handleSliderChange}
className={classes.Button}
>
Fire laser
</Button>
<Grid container spacing={2}>
<Grid item xs>
<Slider
value={typeof value === "number" ? value : 0}
onChange={handleSliderChange}
aria-labelledby="input-slider"
/>
</Grid>
<Grid item>
<Input
value={value}
margin="dense"
onChange={handleInputChange}
onBlur={handleBlur}
inputProps={{
step: 10,
min: 0,
max: 24,
type: "number"
}}
/>
</Grid>
</Grid>
<div>
<div style={useStyles.swatch} onClick={handleClick}>
{displayColorPicker} <p class="h4">Background</p>
<div style={useStyles.color} />
</div>
{displayColorPicker ? (
<div style={useStyles.popover}>
<div style={useStyles.cover} onClick={handleClose}></div>
<ChromePicker color={color} onChange={handleChange} />
</div>
) : null}
</div>
<div>
<div style={useStyles.swatch} onClick={handleClick}>
{displayColorPicker} <p class="h4">Font</p>
<div style={useStyles.color} />
</div>
{displayColorPicker ? (
<div style={useStyles.popover}>
<div style={useStyles.cover} onClick={handleClose}></div>
<ChromePicker color={fontColor} onChange={handleFontColorChange} />
</div>
) : null}
</div>
</div>
);
}
And here is the sandbox - https://codesandbox.io/s/material-demo-forked-t8xut?file=/demo.js
Any advice?
Does anyone have a good Material UI article for editing/cool features and projects to play with?
You need to pass props to makeStyles.
First, pass fontColor variable as below when declaring classes:
const classes = useStyles({ hoverBackgroundColor, hoverFontColor })();
then in the useStyles, you can have access to the fontColor as a prop, as below:
const useStyles = ({ hoverBackgroundColor, hoverFontColor }) =>
makeStyles((theme) => ({
Button: {
width: 150,
height: 50,
borderRadius: "var(--borderRadius)",
"&:hover": {
backgroundColor: `rgba(${hoverBackgroundColor.r}, ${hoverBackgroundColor.g}, ${hoverBackgroundColor.b}, ${hoverBackgroundColor.a}) !important`,
color: `rgba(${hoverFontColor.r}, ${hoverFontColor.g}, ${hoverFontColor.b}, ${hoverFontColor.a}) !important`
}
},
sandbox

React-Rnd one block inside other

I am using the react-rnd library to drag and resize blocks. I created a page. It creates a gray container on it and I click on the "add global container" button and a container appears on the field that I can move and resize within the parent gray container
in the left corner of the created container there is a purple button, clicking on it, another container will be created inside this container and now the first container will be the parent for the new one created.
the problem is that I can resize the inner container, but I can not move it, or rather, it moves with the parent component. the inner component will move inside the outer only when the parent component touches the borders of its parent, the gray container
in code it looks like this
I have a component , which in itself contains a component
the Box component is called inside Rdn - this is the block that you see on the screen, Rdn makes it move
type Props = {
width: string;
height: string;
color: string;
};
class BoxWrapper extends React.Component<Props> {
state = {
width: "100",
height: "40",
x: 0,
y: 0,
};
render() {
const { width, height, color } = this.props;
return (
<Rnd
size={{ width: this.state.width, height: this.state.height }}
position={{ x: this.state.x, y: this.state.y }}
bounds="parent"
onDragStop={(e: any, d: any) => {
e.stopPropagation();
this.setState({ x: d.x, y: d.y });
}}
minHeight={16}
minWidth={16}
onResize={(
e: any,
direction: any,
ref: any,
delta: any,
position: any
) => {
this.setState({
width: ref.style.width,
height: ref.style.height,
...position,
});
}}
>
<Box
x={this.state.x}
y={this.state.y}
width={this.state.width}
height={this.state.height}
externalHeight={height}
externalWidth={width}
color={color}
/>
</Rnd>
);
}
}
export default BoxWrapper;
inside the Box component, the purple button is checked and if it is pressed, you need to create the BoxWrapper component
type BoxProps = {
x: number;
y: number;
width: string;
height: string;
externalWidth: string;
externalHeight: string;
color: string;
};
class Box extends React.Component<BoxProps> {
state = {
isClick: false,
isCreate: false,
};
render() {
const {
x,
y,
width,
height,
externalWidth,
externalHeight,
color,
} = this.props;
const externalH = parseInt(externalHeight);
const externalW = parseInt(externalWidth);
const boxWidth = parseInt(width);
const boxHeight = parseInt(height);
const xUpperLeft = x;
const yUpperLeft = y;
const xUpperRight = x + boxWidth;
const yUpperRight = y;
const xDownLeft = x;
const yDownLeft = y + boxHeight;
const xDownRight = x + boxWidth;
const yDownRight = y + boxHeight;
return (
<>
<div
style={{
margin: 0,
height: "100%",
padding: 0,
backgroundColor: color,
}}
>
<div className="wrapper">
<button
style={{
width: 0,
height: "14px",
borderRadius: "1px",
backgroundColor: "#fff",
display: "flex",
justifyContent: "center",
fontSize: 9,
}}
onClick={() => this.setState({ isClick: !this.state.isClick })}
>
?
</button>
<button
style={{
width: 0,
height: "14px",
borderRadius: "1px",
backgroundColor: "#a079ed",
display: "flex",
justifyContent: "center",
fontSize: 9,
}}
onClick={() => this.setState({ isCreate: !this.state.isCreate })}
/>
</div>
{this.state.isCreate && (
<BoxWrapper
width={width}
height={height}
color="#42d5bc"
/>
)}
</div>
{this.state.isClick && (
<Tooltip
leftDistance={xUpperLeft}
topDistance={yUpperLeft}
rightDistance={externalW - xUpperRight}
bottomDistanse={externalH - yDownLeft}
/>
)}
</>
);
}
}
how can I make it so that I can freely move the inner container without automatically dragging the parent container
I tried in the onDragStop method in Rnd to specify event.stopPropafgation(), but it doesn’t work at all, I don’t know what to do
this is a working example of my problem the inner Rnd container has the bounds of the bounds = "parent" and the outer one is "window"
problem resolved
you need to replace the onDragStop method with onDrag and specify
event.stopImmediatePropagation();
working example with corrections of the previous

Auto Bottom Scroll in reactjs and jsx

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

(google-maps-react) Material-UI popover detail bubble won't follow map marker when map centers to marker (LatLng)

I'm building a map with map markers that show a detail bubble built with the Material-UI Popover component. My code centers the marker when it is clicked, but the detail bubble/popover remains in the spot over where the map marker was before it was centered.
Here is a pic of the detail bubble/Popover when the map marker is centered:
I already tried positioning the detail bubble/popover as such:
.popover {
position: element(#map-marker);
transform: translateY(-100%);
}
But it still behaves the same. I think the popover component
can't calculate the change in the positioning of the map marker because the change is dictated by lat/lng values for the center of the map. I just can't think of any way to circumvent this.
Here is the full code:
Map.js
class ShowsMap extends Component {
constructor(props) {
super(props);
this.state = {
detailBubbleAnchorEl: null // The currently selected marker that the popover anchors to
}
}
handleDetailClose = () => {
this.setState({
detailBubbleAnchorEl: null
})
}
handleMarkerClick = (event, lat, lng) => {
this.setState({
detailBubbleAnchorEl: event.currentTarget
})
// Set center coordinates of map to be those of the map marker (redux action)
this.props.setSearchCenter({ lat, lng })
}
renderMap = () => {
const { detailBubbleAnchorEl } = this.state;
const detailOpen = Boolean(detailBubbleAnchorEl);
const { viewport } = this.props.searchLocation;
const { zoom } = fitBounds(viewport, { width: 400, height: 600})
return (
<GoogleMapReact
yesIWantToUseGoogleMapApiInternals
bootstrapURLKeys={{ key: MYAPIKEY }}
defaultCenter={this.props.center}
defaultZoom={this.props.zoom}
zoom={zoom + 1}
center={this.props.searchLocation.center}
onGoogleApiLoaded={({ map, maps }) => this.handleApiLoaded(map, maps)}
>
{
showLocations.map((location, index) => {
const { lat, lng } = location;
return (
<div lat={lat} lng={lng} key={index}>
<MapMarker onClick={(event) => this.handleMarkerClick(event, lat, lng)} />
<DetailBubble
id="event"
open={detailOpen}
anchorEl={detailBubbleAnchorEl}
onClose={this.handleDetailClose}
/>
</div>
)
})
}
</GoogleMapReact>
)
}
render() {
return (
<div ref={map => this.map = map} style={{ width: '100%', height: '100%',}}>
{this.renderMap()}
</div>
);
}
DetailBubble.js
const DetailBubble = ({ classes, open, anchorEl, onClose, id }) => {
return(
<Popover
id={id}
classes={{ paper: classes.container}}
open={open}
anchorEl={anchorEl}
onClose={onClose}
anchorOrigin={{
vertical: 'top',
horizontal: 'center'
}}
transformOrigin={{
vertical: 'bottom',
horizontal: 'center'
}}
>
</Popover>
)
}
const styles = theme => ({
container: {
position: 'absolute',
left: 0,
top: 0,
right: 0,
bottom: 0,
width: '200px',
height: '150px'
}
});
MapMarker.js
const styles = theme => ({
markerContainer: {
position: 'absolute',
width: 35,
height: 35,
left: -35 / 2,
top: -35 / 2,
},
marker: {
fill: '#3f51b5',
'&:hover': {
fill: 'blue',
cursor: 'pointer'
}
}
})
function MapMarker({ classes, onClick }) {
return (
<div className={classes.markerContainer}>
<Marker onClick={onClick} className={classes.marker} width={30} height={30} />
</div>
)
}
Thanks in advance for your help!

Resources