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
Related
I want to make a progress bar in image with Material UI in React like this:
I tried with a customProgress bar, something like:
export const CustomLinearProgress = styled(LinearProgress)(({ theme }) => ({
height: 35,
borderRadius: 3,
[`&.${ linearProgressClasses.colorPrimary }`]: {
backgroundColor: '#00b4e559',
},
[`&.${ linearProgressClasses.bar }`]: {
borderRadius: 3,
backgroundColor: "#00B4E5"
},
}));
but it doesn't work.
You can use MUI's CircularProgress component and add a label for the progress percent inside it.
import * as React from "react";
import CircularProgress, {
CircularProgressProps,
} from "#mui/material/CircularProgress";
import Typography from "#mui/material/Typography";
import Box from "#mui/material/Box";
function CircularProgressWithLabel(
props: CircularProgressProps & { value: number }
) {
return (
<Box sx={{ position: "relative", display: "inline-flex" }}>
<CircularProgress
variant="determinate"
size={150}
thickness={5}
{...props}
/>
<Box
sx={{
top: 0,
left: 0,
bottom: 0,
right: 0,
position: "absolute",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<Typography
variant="caption"
component="div"
color="text.secondary"
>{`${Math.round(props.value)}%`}</Typography>
</Box>
</Box>
);
}
export default function CircularStatic() {
const [progress, setProgress] = React.useState(10);
React.useEffect(() => {
const timer = setInterval(() => {
setProgress((prevProgress) =>
prevProgress >= 100 ? 0 : prevProgress + 10
);
}, 800);
return () => {
clearInterval(timer);
};
}, []);
return <CircularProgressWithLabel value={progress} />;
}
Change size and thickness props to achieve the style you want.
Learn more ways to control your circular progress in it's API page.
I have a pan responder overlaying the whole screen and a scrollview underneath it.
I would like to call the scrollTo() method and using Pan responders X/Y travel positions to scroll.
I am using this Pan Responder example code to create a Y value that can increment or decrement as you swipe up or down on the screen.
https://reactnative.dev/docs/panresponder
I don't know how to get myScroll view to listen to this Y value change.
Any help is appreciated. thanks
// You should be able to run this by copying & pasting
import { createRef, useRef } from "react";
import { Animated, PanResponder, StyleSheet, Text, View } from "react-native";
// some refs...
const scrollRef = createRef();
const buttonRef = createRef();
// panResponder Hook
const useScreenPanResponder = () => {
const pan = useRef(new Animated.ValueXY()).current;
const panResponder = useRef(
PanResponder.create({
onMoveShouldSetPanResponder: () => true,
onPanResponderGrant: () => {
pan.setOffset({
y: pan.y._value,
});
},
onPanResponderMove: Animated.event([null, { dy: pan.y }]),
onPanResponderRelease: () => {
pan.flattenOffset();
},
})
).current;
return { pan, panResponder };
};
// custom button
const ButtonWrapper = ({ children }) => {
return (
<View
onTouchStart={() => {
buttonRef.current = Date.now();
}}
onTouchEnd={() => {
if (Date.now() - buttonRef.current < 500) {
console.log("Actual Press");
} else {
console.log("Not a Press");
}
}}
>
{children}
</View>
);
};
// long list of text
const Content = () => {
const data = Array.from(Array(100));
return (
<View style={{ backgroundColor: "orange", flexDirection: "column" }}>
{data.map((i) => (
<Text style={{ fontSize: 17, color: "black" }}>Some Content</Text>
))}
</View>
);
};
export default function App() {
const { pan, panResponder } = useScreenPanResponder();
console.log("pan!", pan);
console.log("scrollRef", scrollRef);
const scrollToPosition = () => {
// scrollRef.current.scrollTo({ y: pan.y });
};
return (
<View style={styles.container}>
{/* Container taking up the whole screen, lets us swipe to change pan responder y pos */}
<Animated.View
style={{
position: "absolute",
width: "100%",
height: "100%",
backgroundColor: "rgba(0,255,0,.5)",
}}
{...panResponder.panHandlers}
/>
{/* A animated container that reacts to pan Responder Y pos */}
<Animated.View
style={{
transform: [{ translateY: pan.y }],
}}
>
<ButtonWrapper>
<View style={styles.box} {...panResponder.panHandlers} />
</ButtonWrapper>
</Animated.View>
{/* Here we need the scrollView to be controlled by the Pan Y pos */}
<Animated.ScrollView ref={scrollRef}>
<Content />
</Animated.ScrollView>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: "center",
justifyContent: "center",
},
titleText: {
fontSize: 14,
lineHeight: 24,
fontWeight: "bold",
},
box: {
height: 150,
width: 150,
backgroundColor: "blue",
borderRadius: 5,
},
});
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'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!