When i apply the below CSS in App.css they render perfectly, however when I apply the same styles directly in my component (below) the CSS does not render.
const getStyles = () => {
const BLACK = "#000000";
const VW = "vw";
const VH = "vh";
const REM = "rem";
return {
editor: {
width: `70${VW}`,
height: `50${VH}`,
margin: `2${REM}`,
padding: `1${REM}`,
fontSize: `1.2${REM}`,
boxShadow: `0 .1${REM} .4rem ${BLACK}`,
border: `1px solid ${BLACK}`,
overflowY: `auto`
}
};
};
const styles = getStyles();
return (
<>
<div className="center">
<div className={styles.editor} contentEditable={true} suppressContentEditableWarning={true}>
<h1>{introText}</h1>
<p>{subText}</p>
</div>
</div>
</>
)
}
To get the effect you desire you should do something like this.
const getStyles = () => {
const BLACK = "#000000";
const VW = "vw";
const VH = "vh";
const REM = "rem";
return {
width: `70${VW}`,
height: `50${VH}`,
margin: `2${REM}`,
padding: `1${REM}`,
fontSize: `1.2${REM}`,
boxShadow: `0 .1${REM} .4rem ${BLACK}`,
border: `1px solid ${BLACK}`,
overflowY: `auto`
};
};
const styles = getStyles();
return (
<>
<div className="center">
<div style={styles.editor} contentEditable={true} suppressContentEditableWarning={true}>
<h1>{introText}</h1>
<p>{subText}</p>
</div>
</div>
</>
)
}
More info at this link:
https://reactjs.org/docs/dom-elements.html#style
Related
I have a simple login screen with a title, input field and button. Once a user clicks the button, the assumption is that the they become authenticated and should move onto the next screen. There is an intermediate transition that I'm trying to implement, which clears the input field and button and translates the title into the center of the div.
Currently, I calculate the container's height and the inner content's height with useRef and then conditionally change the height of Container if the user is authenticated.
Right now, for Title I'm doing,
transform: translateY(${authenticated ? 42 : 0}px);
But 42 could change in the future depending on stuff I might add or different screens that the user is viewing the page on.
Is there a better, more assuring way of doing this?
import * as React from "react";
import styled from "#emotion/styled";
const Container = styled.div`
height: 100vh;
display: grid;
place-items: center;
`;
const Box = styled.div<{
authenticated: boolean;
containerHeight: number;
innerContentHeight: number;
}>`
${({ authenticated, containerHeight, innerContentHeight }) => `
border: 1px solid red;
transition: 0.3s ease-in-out;
width: 15rem;
height: ${
containerHeight
? `${
authenticated
? containerHeight - innerContentHeight
: containerHeight
}px`
: "auto"
}
`}
`;
const Title = styled.h2<{ authenticated: boolean }>`
${({ authenticated }) => `
transform: translateY(${authenticated ? 42 : 0}px);
margin-bottom: 6rem;
`}
`;
const Input = styled.input``;
const Button = styled.button`
width: 100%;
margin-top: 1rem;
`;
export default function App() {
const [containerHeight, setContainerHeight] = React.useState(0);
const [innerContentHeight, setInnerContentHeight] = React.useState(0);
const [authenticated, setAuthenticated] = React.useState(false);
const handleRef = React.useCallback(
(isContainer: boolean) => (ref: HTMLDivElement) => {
if (!ref || (containerHeight && innerContentHeight)) return;
const height = Math.ceil(ref.getBoundingClientRect().height);
console.log("ref", ref);
console.log("height", height);
if (isContainer) {
setContainerHeight(height);
} else {
setInnerContentHeight(height);
}
},
[containerHeight, innerContentHeight]
);
const handleLogin = React.useCallback(() => {
setAuthenticated(true);
}, []);
return (
<Container>
<Box
ref={handleRef(true)}
authenticated={authenticated}
containerHeight={containerHeight}
innerContentHeight={innerContentHeight}
>
<Title authenticated={authenticated}>Welcome</Title>
{!authenticated && (
<div ref={handleRef(false)}>
<Input value={"test#test.com"} />
<Button onClick={handleLogin}>Login</Button>
</div>
)}
</Box>
</Container>
);
}
I am using react-beautiful-dnd. I have it working, except when I drag and drag an item into one of my lists, the item is positioned incorrrectly, has a short delay, then jumps to the correct position.
Here is what it looks like: Link to issue
As you can see, after the item is dropped in a list, the item readjusts itself to fit within the div.
Here is the code for the item:
import React, { useState } from "react";
import styled from "styled-components";
import { Draggable } from "react-beautiful-dnd";
const Container = styled.div`
margin: 0 0 8px 0;
background-color: rgba(140, 240, 255);
`;
const Title = styled.div`
font-size: 1.5rem;
`;
const Gradient = styled.div`
background: black;
height: 2px;
margin: 0.5rem;
`;
const Description = styled.div`
font-size: 1rem;
`;
const Ticket = ({ ticket, setCategories, id, index }) => {
const [isDeleted, setIsDeleted] = useState(false);
const handleDelete = (e) => {
e.preventDefault();
fetch(`/tickets/${ticket.id}`, {
method: "DELETE",
}).then(
fetch("/categories")
.then((r) => r.json())
.then(setCategories)
);
setIsDeleted(true);
};
return (
<Draggable draggableId={id.toString()} index={index}>
{(provided, snapshot) =>
isDeleted ? null : (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
<Container
style={{
backgroundColor: snapshot.isDragging
? "aquamarine"
: "rgba(140, 240, 255)",
}}
>
<Title>{ticket.title}</Title>
<Gradient></Gradient>
<Description>{ticket.description}</Description>
<button onClick={handleDelete}>Delete</button>
</Container>
</div>
)
}
</Draggable>
);
};
export default Ticket;
And here is for the list:
import React, { useState } from "react";
import styled from "styled-components";
import Ticket from "./Ticket";
import { Droppable } from "react-beautiful-dnd";
import { transformData } from "./Categories";
const Container = styled.div`
background-color: rgba(255, 255, 255, 0.8);
border-radius: 0.25em;
box-shadow: 0 0 0.25em rgba(0, 0, 0, 0.25);
text-align: center;
width: 20rem;
font-size: 1.5rem;
padding: 4px;
margin: 1rem;
`;
const Gradient = styled.div`
background: black;
height: 2px;
margin: 1rem;
`;
const FormContainer = styled.div`
margin: 1rem;
border: 1px solid black;
backgroundColor: rgba(140, 240, 255);
`;
const Button = styled.button`
margin-left: 1rem;
`;
const DropDiv = styled.div`
min-height: 50vh;
padding: 4px;
`;
const Category = ({ category, user, setCategories, id }) => {
const [isClicked, setIsClicked] = useState(false);
const [title, setTitle] = useState("");
const [description, setDescription] = useState("");
const handleSubmit = (e) => {
e.preventDefault();
const newTicket = {
title: title,
description: description,
user_id: user.id,
category_id: id,
};
fetch("/tickets", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(newTicket),
}).then(
fetch("/categories")
.then((r) => r.json())
.then(transformData)
.then(setCategories)
);
setIsClicked(false);
};
return (
<Container>
{category.title}
<Button onClick={() => setIsClicked(!isClicked)}>Add</Button>
<Gradient></Gradient>
{isClicked ? (
<FormContainer>
<form onSubmit={handleSubmit}>
<label>Title</label>
<input onChange={(e) => setTitle(e.target.value)}></input>
<label>Description</label>
<input onChange={(e) => setDescription(e.target.value)}></input>
<button type="submit">Submit</button>
</form>
</FormContainer>
) : null}
<Droppable droppableId={id.toString()}>
{(provided, snapshot) => (
<DropDiv
{...provided.droppableProps}
ref={provided.innerRef}
style={{
background: snapshot.isDraggingOver ? "lightblue" : "",
}}
>
{category.tickets.map((ticket, index) => {
return (
<Ticket
ticket={ticket}
key={ticket.id}
setCategories={setCategories}
id={ticket.id}
index={index}
/>
);
})}
{provided.placeholder}
</DropDiv>
)}
</Droppable>
</Container>
);
};
export default Category;
I have tried flexbox styling and messed with margin and padding. If i remove the margin and padding it seems to go away, but in beautiful-dnd examples they all have space between items and theres no delay like this. Does anyone have any ideas?
It looks like the placeholder might not have the 8px bottom margin that the rest of the Draggables have.
You'll notice that when you pick up something (without changing it's position) from somewhere other than the end of the list, the list will shift up a bit right away, and when you drop something at the end of a list, you don't see the issue.
The placeholder gets its margins from the item being dragged. You can see this happening by looking at the inline styles on the placeholder element that appears at the end of the droppable while you are dragging.
So, you might want to try putting the provided innerRef, draggableProps, and dragHandleProps on the Ticket Container itself instead of a parent div, as it's possible that because they are on a different element, react-beautiful-dnd isn't taking the margins in to account.
The delay could be caused because your list is changing size while dragging an element, and this provokes the library to recalculate and animate slower.
The solution is to avoid this size change while dragging.
The causes of the issue could be:
---- Cause A ----
The {provided.placeholder} auto inserted in the DOM while dragging doesn't have the same margins/padding that the other Draggable elements
Solution
Be sure to add the styles that separate the elements (margin/padding) to the element that you are applying the provided innerRef, draggableProps, and dragHandleProps because in this way the {provided.placeholder} will inherit those styles.
---- Cause B ----
You are using flexbox or css-grid with a gap property to separate the elements.
Solution
Stop using gap and just add margin to the element with provided innerRef, draggableProps, and dragHandleProps (do not use inline styles but css classes)
Extra: To confirm that size change is the cause
Drag some elements from somewhere other than the end of the list and notice how another element will move up a bit.
Also, when you drop something at the end of a list, you don't see the problem.
I was having this same issue- but as seen in the docs, inline styling with provided props did the trick for me:
const ListItem = ({ item, index }) => {
return (
<Draggable draggableId={item.id} className="draggableItem" index={index}>
{(provided, snapshot) => {
return (
<div
ref={provided.innerRef}
snapshot={snapshot}
{...provided.draggableProps}
{...provided.dragHandleProps}
style={{
userSelect: "none",
padding: 12,
margin: "0 0 8px 0",
minHeight: "50px",
borderRadius: "4px",
backgroundColor: snapshot.isDragging
? "rgb(247, 247, 247)"
: "#fff",
...provided.draggableProps.style,
}}
>
<div className="cardHeader">Header</div>
<span>Content</span>
<div className="cardFooter">
<span>{item.content}</span>
<div className="author">
{item.id}
<img className="avatar" />
</div>
</div>
</div>
);
}}
</Draggable>
);
};
I'm currently using react-moveable and react-selecto packages to build a moveable component. But it seems to be a problem with these two main packages and Nextjs.
I'm using React v18.1.0 and Next v12.1.6
import React, { useRef, useState } from "react";
import Moveable from "react-moveable";
import Selecto from "react-selecto";
export default function MoveableProvider() {
const [targets, setTargets] = useState([]);
const [frameMap] = useState(() => new Map());
const selectoRef = useRef(null);
const moveableRef = useRef(null);
return (
<div className="container">
<Moveable
ref={moveableRef}
draggable={true}
target={targets}
onClickGroup={(e) => {
selectoRef.current.clickTarget(e.inputEvent, e.inputTarget);
}}
onDrag={(e) => {
const target = e.target;
const frame = frameMap.get(target);
frame.translate = e.beforeTranslate;
target.style.transform = `translate(${frame.translate[0]}px, ${frame.translate[1]}px)`;
}}
onDragStart={(e) => {
const target = e.target;
if (!frameMap.has(target)) {
frameMap.set(target, {
translate: [0, 0],
});
}
const frame = frameMap.get(target);
e.set(frame.translate);
}}
onDragGroupStart={(e) => {
e.events.forEach((ev) => {
const target = ev.target;
if (!frameMap.has(target)) {
frameMap.set(target, {
translate: [0, 0],
});
}
const frame = frameMap.get(target);
ev.set(frame.translate);
});
}}
onDragGroup={(e) => {
e.events.forEach((ev) => {
const target = ev.target;
const frame = frameMap.get(target);
frame.translate = ev.beforeTranslate;
target.style.transform = `translate(${frame.translate[0]}px, ${frame.translate[1]}px)`;
});
}}
/>
<Selecto
ref={selectoRef}
dragContainer={".elements"}
selectableTargets={[".selecto-area .cube"]}
hitRate={0}
selectByClick={true}
selectFromInside={false}
toggleContinueSelect={["shift"]}
ratio={0}
onDragStart={(e) => {
const moveable = moveableRef.current;
const target = e.inputEvent.target;
k;
if (
moveable.isMoveableElement(target) ||
targets.some((t) => t === target || t.contains(target))
) {
e.stop();
}
}}
onSelectEnd={(e) => {
const moveable = moveableRef.current;
setTargets(e.selected);
if (e.isDragStart) {
e.inputEvent.preventDefault();
setTimeout(() => {
moveable.dragStart(e.inputEvent);
});
}
}}
></Selecto>
<div className="elements selecto-area" style={{ display: "flex" }}>
<div
className="cube target"
style={{
width: 250,
height: 200,
background: "green",
}}
></div>
<div
className="cube target"
style={{
width: 250,
height: 200,
background: "yellow",
}}
></div>
<div
className="cube target"
style={{
width: 250,
height: 200,
background: "aqua",
}}
></div>
<div
className="cube target"
style={{
width: 250,
height: 200,
background: "red",
}}
></div>
</div>
<div className="empty elements"></div>
</div>
);
}
In this case onDragGroup doesn't work for no reason in Nextjs but single drag (onDrag) works fine.
Is there any way that SSR would be causing the problem?
I am using React Native Typescript for my. In My app there are three elements one is Map, bottom-sheet (which position is absolute) and One button. I have created one custom bottom sheet. Bottom-sheet is animated Scrollable. Inside the Bottom-sheet I used Custom search bar and under the search bar there is Flat-list where I am rendering the data. In IOS simulator I can see the FlatList card under the Button. which is really bad from UI (IOS-Image) but in Android it looks perfect Android-simulator. I want to hide the flat-List data which is coming under the button. I tried background color white and tried with height, width, bottom.But none of of work. I want to make efficient css position instead of hard code position. But I don't know how to fixed it.
I share my code in Expo-snacks
This is my custom Bottom-sheet
import React from "react";
import {
SafeAreaView,
StyleSheet,
Text,
useWindowDimensions,
TouchableOpacity,
View,
} from "react-native";
import Animated, {
Extrapolate,
interpolate,
useAnimatedGestureHandler,
useAnimatedStyle,
withTiming,
} from "react-native-reanimated";
import { PanGestureHandler } from "react-native-gesture-handler";
import styled from "styled-components/native";
interface Props {
panY: number;
children: React.ReactNode;
}
export default function BottomSheet({ panY, children }: Props) {
const { height } = useWindowDimensions();
const gestureHandler = useAnimatedGestureHandler(
{
onStart(_, context) {
context.startY = panY.value;
},
onActive(event, context) {
panY.value = context.startY + event.translationY;
},
onEnd() {
if (panY.value < -height * 0.4) {
panY.value = withTiming(-(height * 0.6));
} else {
panY.value = withTiming(0);
}
},
},
[height]
);
const animatedStyle = useAnimatedStyle(() => {
return {
transform: [
{
translateY: interpolate(panY.value, [-1, 0], [-1, 0], {
extrapolateLeft: Extrapolate.EXTEND,
extrapolateRight: Extrapolate.CLAMP,
}),
},
],
};
});
return (
<PanGestureHandler onGestureEvent={gestureHandler}>
<Animated.View
style={[styles.container, { top: height * 0.7 }, animatedStyle]}
>
{children}
</Animated.View>
</PanGestureHandler>
);
}
const styles = StyleSheet.create({
container: {
position: "absolute",
top: 0,
left: 0,
right: 0,
backgroundColor: "white",
shadowColor: "black",
shadowOffset: {
height: -6,
width: 0,
},
shadowOpacity: 0.1,
shadowRadius: 5,
borderTopEndRadius: 15,
borderTopLeftRadius: 15,
},
});
This is My App component
import React, { useState } from "react";
import {
StyleSheet,
Text,
View,
Dimensions,
useWindowDimensions,
SafeAreaView,
StatusBar,
RefreshControl,
} from "react-native";
import styled from "styled-components/native";
import MapView from "react-native-maps";
import { useSharedValue } from "react-native-reanimated";
import { FlatList } from "react-native-gesture-handler";
import BottomSheet from "./ActionSheet";
import BottomButton from "./BottomButton";
import SafeAreaWrapper from "./SafeAreaWrapper";
import SearchBar from "./SearchBar";
const { width, height } = Dimensions.get("screen");
const api =
"http://open-api.myhelsinki.fi/v1/events/?distance_filter=60.1699%2C24.9384%2C10&language_filter=en&limit=100";
const initialRegion = {
latitudeDelta: 15,
longitudeDelta: 15,
latitude: 60.1098678,
longitude: 24.7385084,
};
export default function App() {
const { width, height } = useWindowDimensions();
const [loading, setLoading] = useState(false);
const [data, setData] = React.useState([]);
const updateState = async () => {
try {
const response = await fetch(api);
const data = await response.json();
setData(data.data);
} catch (error) {
console.log("failed to catch", error);
}
};
React.useEffect(() => {
updateState();
}, []);
const y = useSharedValue(0);
return (
<SafeAreaWrapper>
<Container>
<MapContent>
<MapView style={styles.mapStyle} initialRegion={initialRegion} />
</MapContent>
<BottomSheet panY={y}>
<Content>
<SearchContainer>
<SearchBar placeholder={"write some thing"} />
</SearchContainer>
{data && data === undefined ? (
<Text>loading</Text>
) : (
<HeroFlatList
data={data}
showsVerticalScrollIndicator={false}
refreshControl={
<RefreshControl
enabled={true}
refreshing={loading}
onRefresh={updateState}
/>
}
keyExtractor={(_, index) => index.toString()}
renderItem={({ item, index }) => {
const image = item?.description.images.map((img) => img.url);
const startDate = item?.event_dates?.starting_day;
return (
<EventContainer key={index}>
<EventImage
source={{
uri:
image[0] ||
"https://res.cloudinary.com/drewzxzgc/image/upload/v1631085536/zma1beozwbdc8zqwfhdu.jpg",
}}
/>
<DescriptionContainer>
<Title ellipsizeMode="tail" numberOfLines={1}>
{item?.name?.en}
</Title>
<DescriptionText>
{item?.description?.intro ||
"No description available"}
</DescriptionText>
<DateText>{startDate}</DateText>
</DescriptionContainer>
</EventContainer>
);
}}
/>
)}
</Content>
</BottomSheet>
<ButtonContainer>
<BottomButton
title={"Save"}
onPress={() => {
console.log("jjj");
}}
/>
</ButtonContainer>
</Container>
</SafeAreaWrapper>
);
}
const styles = StyleSheet.create({
mapStyle: {
width: width,
height: height, // I DON'T KNOW HOW TO CONVERT THEM INTO STYLED COMPONENT
},
});
const HeroFlatList = styled(FlatList).attrs({
contentContainerStyle: {
paddingTop: 20,
paddingBottom: 2000, // BAD PRACTICE
flexGrow: 1, //SEEMS LIKE IT DOES NOT WORK
},
})`
height: 2000px; // BAD PRACTICE
`;
const MapContent = styled.View`
flex: 1;
`;
const Content = styled.View`
flex: 1;
padding: 20px;
`;
const Container = styled.View`
flex: 1;
`;
const ButtonContainer = styled.View`
background-color: #fdfbfb; // My Button container
`;
const TextName = styled.Text`
color: orange;
font-weight: bold;
`;
const SearchContainer = styled.View`
margin-bottom: 5px;
`;
const Title = styled.Text`
font-size: 16px;
font-weight: 700;
margin-bottom: 5px;
`;
const DescriptionText = styled(Title)`
font-size: 14px;
opacity: 0.7;
`;
const DateText = styled(Title)`
font-size: 14px;
opacity: 0.8;
color: #0099cc;
`;
const EventImage = styled.Image`
width: 70px;
height: 70px;
border-radius: 70px;
margin-right: 20px;
`;
const DescriptionContainer = styled.View`
width: 200px;
`;
const EventContainer = styled.View`
flex-direction: row;
padding: 20px;
margin-bottom: 10px;
border-radius: 20px;
background-color: rgba(0, 0, 0, 0.8);
`;
I've been working on a section with expandable/collapsible sections. When I click on a section to expand or collapse it, a blue focus area shows up but it is placed on a weird angle. I don't know what is causing it and would like a solution to either get rid of it or place it back at the normal horizontal angle. Does anybody have any suggestions as to how to fix this?
I am using a Macbook and Chrome browser.
The entire grey block that this component appears in is placed at an angle as you can see from the top of the image attached below but in the reverse direction from the highlighted focus area.
My css:
#import '../../theme/variables.css';
.rotatedSection {
padding-bottom: 2rem;
}
.container {
max-width: 64rem;
margin: 0 auto;
display: flex;
padding: 2rem 0;
#media screen and (max-width: 68rem) {
margin: 0 3rem;
}
}
.accordianContainer {
flex: 1;
margin-right: 2rem;
min-width: 500px;
#media screen and (max-width: $tablet-lg-max-width) {
margin-right: 0;
}
#media screen and (max-width: 900px) {
min-width: 0;
}
}
.imageContainer {
flex: 1;
margin-left: 2rem;
max-height: 300px;
display: flex;
justify-content: center;
img {
flex: 1;
}
#media screen and (max-width: $tablet-lg-max-width) {
margin-left: 0;
}
}
.heading {
composes: h2 from 'theme/text';
margin-left: auto;
margin-right: auto;
}
My react code:
import React, {Component, PropTypes} from 'react';
import RotatedSection from 'components/RotatedSection';
import AccordionItem from './AccordionItem';
import css from './styles.css';
class AccordionSectionWithImage extends Component {
constructor (props) {
super(props);
this.state = {
activeIndex: null,
};
this.onOpen = this.onOpen.bind(this);
this.onClose = this.onClose.bind(this);
this.setActive = this.setActive.bind(this);
this.handleClickOutside = this.handleClickOutside.bind(this);
}
onOpen = (index) => {
this.setActive(index);
};
onClose = (callback = () => null) => {
this.setActive(null);
callback();
};
setActive = (activeIndex) => this.setState({activeIndex});
handleClickOutside = () => this.props.collapseOnBlur && this.onClose();
render () {
const {
entry: {
items,
heading,
image,
},
showIndex,
classNames,
meta = {},
} = this.props;
const {routeParams, toggleHamburger} = meta;
const {activeIndex} = this.state;
return (
<RotatedSection color='whisper' className={css.rotatedSection}>
<div className={css.container}>
<div className={css.accordianContainer}>
<h2 className={css.heading}>{heading}</h2>
{items && items.map((item, index) => (
<AccordionItem
key={index}
showIndex={showIndex}
entry={item}
meta={{
position: index,
isOpen: (index === activeIndex),
onOpen: () => this.onOpen(index),
onClose: () => this.onClose(),
onChildClick: () => this.onClose(toggleHamburger),
routeParams,
}}
classNames={classNames}
/>
))}
</div>
<div className={css.imageContainer}>
<img src={image && image.fields && image.fields.file.url} alt='Educational assessment' />
</div>
</div>
</RotatedSection>
);
}
}
AccordionSectionWithImage.propTypes = {
meta: PropTypes.object,
entry: PropTypes.object,
collapseOnBlur: PropTypes.bool,
showIndex: PropTypes.bool,
classNames: PropTypes.object,
};
export default AccordionSectionWithImage;
React component for individual section:
function AccordionItem (props) {
const {
meta: {
isOpen,
onOpen,
onClose,
},
entry: {
heading,
text,
},
} = props;
const handleClick = () => (isOpen ? onClose() : onOpen());
return (
<div className={css.itemContainer}>
<div className={css.innerContainer}>
<h3 className={css.heading} onClick={handleClick}>
<span className={css.titleText}>{heading}</span>
<i className={`zmdi zmdi-plus ${css.titleToggle}`} />
</h3>
{isOpen && (
<div className={css.contents}>
{text}
</div>
)}
</div>
</div>
);
}
For anybody else experiencing a similar problem:
Problem only appeared on mobile phones and the device mode of chrome inspector. It was due to the tap-highlight property.
Setting -webkit-tap-highlight-color to rgba(0,0,0,0) hid the problem but it's a non standard css property so the solution may not work for all devices/browsers/users.