I am working with the react-places-autocomplete package.
Whenever I type text into the input field and get suggestions, it makes the whole input field jumps up, ruining the look.
How can I get the input field to stay in place and just have the suggestions drop down normally? I've tried adding a position: relative and top: Npx style to the suggestions, but that doesn't stop the input field from jumping up.
Searchbar.js
return (
<div className="search">
<PlacesAutocomplete
value={locationChars}
onSelect={handleSelect}
searchOptions={{ types: ["(cities)"] }}
onChange={handleChange}
>
{({ getInputProps, suggestions, getSuggestionItemProps, loading }) => (
<div>
<input
ref={inputRef}
{...getInputProps({ placeholder: "Search Location" })}
className="search-input"
/>
<div className="suggestions-container">
{loading ? <div>Loading...</div> : null}
{suggestions.map((suggestion) => {
const style = {
backgroundColor: suggestion.active ? "#41b6e6" : "white",
};
return (
<div {...getSuggestionItemProps(suggestion, { style })}>
{suggestion.description}
</div>
);
})}
</div>
</div>
)}
</PlacesAutocomplete>
<SearchIcon className="search-icon" />
</div>
Searchbar.css
.search {
display: flex;
flex: 1;
align-items: center;
border-radius: 24px;
background-color: purple;
margin-right: 1rem;
color: black;
opacity: 1 !important;
}
.search-input {
height: 12px;
padding: 10px;
border: none;
width: 100%;
}
.search-icon {
padding: 5px;
height: 22px !important;
background-color: blue;
}
.suggestions-container
}
Just a guess but you might want to do something with the suggestions-container class. I recently found react-google-autocomplete to be a very smooth experience btw. Happy Hacking!
Related
I am making an Airbnb clone. I am unable to make this locations filter carousel work. The carousel either takes up more space in the x-axis or if I use overflow: hidden, I am unable to make the hidden icons display on scrolling. Please find the code below.
function LocationFilter() {
const slide = (e) => {
console.log(e);
let handle = e.target.className.baseVal;
let leftHandle = document.querySelector(".left-handle");
console.log(handle);
let locations = document.querySelector(".locations");
let cs = getComputedStyle(locations);
let locationsIndex = parseInt(cs.getPropertyValue("--locations-index"));
if(handle == "right-handle") {
locations.style.setProperty("--locations-index", locationsIndex + 5);
} else {
if(locationsIndex - 5 > 0) {
locations.style.setProperty("--locations-index", locationsIndex - 5);
}
}
// console.log("locationsIndex: ", locationsIndex);
if(locationsIndex > 0) {
leftHandle.style.setProperty("display", "flex");
}
}
return (
<div className='location-filter-container'>
<RiArrowLeftSLine className="left-handle" onClick={(e) => slide(e)} />
<div className="locations snaps-inline">
{
locationData.map((data, index) => (
<button id={index} className="btn">
<img src={data.link} />
<span>{data.desc}</span>
</button>
))
}
</div>
<RiArrowRightSLine className="right-handle" onClick={(e) => slide(e)} />
<div className="location-filters-flex">
<div className="location-filters">
<RiEqualizerLine />
<span>Filters</span>
</div>
</div>
</div>
)
}
export default LocationFilter;
//CSS
.location-filter-container {
display: flex;
align-items: center;
padding: 10px 20px;
margin: 20px auto;
height: 80px;
column-gap: 10px;
background: whitesmoke;
}
.location-filter-container > .locations {
--locations-index: 0;
width: 100%;
max-width: 100%;
display: flex;
flex: 1;
column-gap: 50px;
align-items: center;
justify-content: space-between;
overflow-x: hidden;
overscroll-behavior-inline: contain;
transform: translateX(calc(var(--locations-index) * -10%));
transition: transform .5s ease-in-out;
}
Please find the screenshot below.
enter image description here
I'm hitting an API to get these data. I mapped the data to my select menu. I'm not able to change the height of the dropdown box . any idea on how it can be achieved?
react.js
inside render
<div className={styles.nationality}>
<label htmlFor="nationality">Nationality<span className={styles.star}>*</span></label><br/>
{/* <img className={styles.nationalitydrop} src={drop} alt='drop' /> */}
{['nationality'].map(key => (
<select
key={key}
className={styles.nationalitybox}
type="text"
placeholder="select"
menuPlacement="bottom"
onChange={(event) => this.handleUserInput(event)}
value={this.state.nationality}
name='nationality'
> {this.props.nationalityData.map(({[key]:id}) => <option className={styles.data}key={id}>{id}</option>)}
</select>
))}
</div>
stylesheet
.nationalitybox
{
width: 408px;
height: 56px;
background: #30333F 0% 0% no-repeat padding-box;
border-radius: 3px;
opacity: 1;
color: #B4B6C4;
font-size: 20px;
border-style:none none solid;
outline: none;
}
.nationalitybox:placeholder-shown{
font-size: 14px;
color: #B4B6C4;
}
.data{
background-color: #B4B6C4;
color: black;
width: 220px;
}
[![dropdown][1]][1]
I think you are using wrong to style your select menu, you cannot use 'style.nationalitybox' because that not a class.
I suggest you use these options.
1st Option:
Change className={styles.nationalitybox} To className = "nationalitybox".
2nd Option:
If you are using Material UI then you have to use useStyles above the component.
For example:
const useStyles = makeStyles((theme) => ({
nationalitybox: {
height: "56px"
}
}));
const ComponentName = () => {
const styles= useStyles();
}
here is a code example where i use Ref's to change style
import React, {useRef, useState, useEffect} from 'react'
import S from "./collapsible.module.css"
import PropTypes from 'prop-types'
import { faMinus, faPlus } from '#fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome'
function Collapsible(props) {
let myRef = useRef(null);
let buttonRef = useRef(null);
let [ button, setButton] = useState(true)
let Show = () => {
if(button) {
setButton(false)
buttonRef.current.style.backgroundColor = "#555"
myRef.current.style.maxHeight = myRef.current.scrollHeight + "px";
} else {
setButton(true)
buttonRef.current.style.backgroundColor = "hsl(181, 100%, 11%)"
myRef.current.style.maxHeight = "0px";
}
}
return (
<div
className={S.body}
>
<button
className={S.collapsible}
onClick={Show}
ref={buttonRef}
> {props.label}
<div className={S.icon}>
{button? <FontAwesomeIcon icon={faPlus} />:
<FontAwesomeIcon icon={faMinus} />}
</div>
</button>
<div
className={S.content}
ref={myRef}
>
<h3>{props.body}</h3>
</div>
</div>
)
}
Collapsible.propTypes = {
label: PropTypes.string,
body: PropTypes.string,
}
Collapsible.defaultProps = {
label: '',
body: "",
}
export default Collapsible
css:
.collapsible {
display: flex;
background-color: hsl(181, 100%, 11%);
color: white;
cursor: pointer;
padding: 18px;
width: 100%;
border: none;
outline: none;
font-size: 15px;
border-radius: 3px;
/* margin-bottom: 3px; */
box-shadow: 0px 1px 5px 1px black;
margin-top:13px;
}
.icon{
color:white;
position:absolute;
right:50px;
text-align:right;
justify-content: flex-end;
}
.active, .collapsible:hover {
background-color: #555;
}
.content {
padding: 0 18px;
max-height: 0px;
overflow: hidden;
transition: max-height 0.2s ease-out;
background-color: #f1f1f1;
}
This is just replicating this in React:
https://www.w3schools.com/howto/tryit.asp?filename=tryhow_js_collapsible_animate
I have read that using Refs is bad, especially when using it to change the DOM, but if I didn't change the style with the exact amount shown in "scrollHeight" then the transition would be a messed up speed.
If there is another method Is this still bad practice?
It's more common practice to use a state value to determine the style like this:
<button
className={S.collapsible}
onClick={Show}
style={{backgroundColor: button ? "#555" : "hsl(181, 100%, 11%)"}
>
{props.label}
<div className={S.icon}>
{button ? (
<FontAwesomeIcon icon={faPlus} />
) : (
<FontAwesomeIcon icon={faMinus} />
)}
</div>
</button>
Or have a conditional className for your styles:
<button
className={`${S.collapsible} ${
button ? S.buttonColor : S.anotherButtonColor
}`}
onClick={Show}
>
{props.label}
<div className={S.icon}>
{button ? (
<FontAwesomeIcon icon={faPlus} />
) : (
<FontAwesomeIcon icon={faMinus} />
)}
</div>
</button>
and add .buttonColor and .anotherButtonColor to your CSS Module file (collapsible.module.css).
.buttonColor {
background-color: #555;
}
.anotherButtonColor {
background-color: hsl(181, 100%, 11%);
}
For the maxHeight on myRef, I'd do something like:
<div className={S.content} ref={myRef}>
<div style={{ maxHeight: myRef.current.scrollHeight }}>
<h3>{props.body}</h3>
</div>
</div>
i want to display a list of items in the side panel. If the height of content in list item exceeds 36px i want to hide the contents fitting more than 36px and show expand button for that list item (whose content exceeded 36px) and clicking expand button should show the whole content of list item.
What i have tried so far?
I have added a ref to the span element containing the text that overflows. and expand button appears if height of span element exceeds 36px and clicking expand button shows the content for the list item.
The problem now?
It works fine. except that expand buttons are added at the end of all list items....i want them to be shown at the bottom of that particular list item whose content exceeds 36px.
It works like in image below.
Below is the code,
switch (notification.type) {
case 'new_model_uploaded':
return (
<Notification
icon={<SvgProject width="26" height="26"/>}
text={<Text
base_height={36}
name={name}
text=' created'
item_name={itemname}/>}
timestamp={notification.timestamp}>
<div className="additional_details">
<Image
width={70}
height={70}
item_id={filtered_item.id}
/>
</div>
</Notification>
);
case 'deleted':
return (
<List
icon={<Svg width="20" height="22"/>}
text={<Text
base_height={36}
name={list.name}
text=' deleted item '
item_name={itemname}/>}
timestamp={item.timestamp}/>
);
default:
return null;
}
function List(props) {
return (
<li className="list">
<div className="details_container">
<div className="details">
{props.icon}
{props.text}
<Time>{props.timestamp}</Time>
</div>
{props.children}
</div>
</li>
);
}
class Text extends React.PureComponent {
constructor(props) {
super(props);
this.span_ref = React.createRef();
this.state = {
expanded: false,
overflow: false,
};
}
componentDidMount () {
if (this.span_ref.current.offsetHeight <
this.span_ref.current.scrollHeight) {
this.setState({overflow: true});
}
}
set_expanded = () => {
this.setState({expanded: !this.state.expanded});
};
render () {
return (
<span ref={this.span_ref} className={this.props.classname}
style={{overflow: 'hidden', height: (this.state.expanded ?
null : this.props.base_height)}}>
<span className="red">{name}</span> {this.props.text}
<span className="red">{this.props.name}
{this.props.item_name}</span>
{this.props.additional_text}
{this.state.overflow && <button onClick={this.set_expanded}
style={{position: 'absolute', bottom:
0}}>expand</button>}
</span>
);
}
}
.list {
display: flex;
flex-direction: row;
font-size: 12px;
padding: 8px;
min-height: 49px;
li {
list-style: none;
}
.details_container {
display: flex;
flex-direction: column;
flex-grow: 1;
margin-right: 8px;
.details {
display: flex;
color: #333;
align-items: center;
justify-content: center;
flex-grow: 1;
svg {
margin-right: 8px;
margin-left: 7px;
flex: 0 0 auto;
align-self: center;
flex-shrink: 0;
}
span {
flex-grow: 1;
}
time {
flex: 0 0 auto;
margin-left: 8px;
padding-top: 2px;
color: #CCCCCC;
}
}
span {
word-break: break-all;
}
}
}
Good day. I have been attempting to create the animation demonstrated here in ReactJS. I have using that codepen as a loose reference, yet the animation doesn't work when I press the login button on my own project implementing this effect on the login page.
What have I done wrong?
Thanks in advance,
Duke J. Morgan.
The specific aspects of my project that, as far am I aware, are primary components in the login page that, in this problem, might be the cause of the problem I mentioned above:
import React, { Component } from 'react';
class LoginComponent extends Component {
constructor(props) {
super(props)
this.state = {
incorrect: false
}
}
onLoginButtonClick() {
let passwordInput = document.getElementById("passwordInput");
if (passwordInput.innerHTML !== "test") {
// passwordInput.classList.add("incorrect-login");
let copy = this.state;
copy.incorrect = true;
console.log(`Incorrect boolean: ${copy.incorrect}`);
this.setState(copy);
return;
}
this.props.setPage();
}
render() {
return (<div className="box" id="loginBox">
<h2 className="title">Username</h2>
<input type="text" placeholder="Username" className="input"/>
<h2 className="title">Password</h2>
<input id="passwordInput" placeholder="Password" className={`input ${this.state.incorrect ? 'incorrect-login' : ''}`} type="password"/>
{/* Forgot Password? */}
<button className="button" id="login" onClick={() => {this.onLoginButtonClick()}}>Login</button>
</div>);
}
}
export default LoginComponent;
//The CSS of the login box. incorrect-login is the class added to the password input element when the login button is clicked but the incorrect password, or no password, has been entered.
#loginBox {
.title:nth-child(1) {
margin-bottom: 10px;
}
.title:nth-child(3) {
margin-bottom: 10px;
margin-top: 10px;
}
.incorrect-login {
margin: 4px auto;
width: 70%;
height: 15%;
display: block;
padding: 5px;
padding-left: 10px;
border: 2px solid red;
animation: move 10s;
}
}
#keyframes move {
0%, 100% { left: 0px;}
20% , 60%{left: 15px;}
40% , 80%{left: -15px;}
}
The problem is in the css. I think you should make separated classes for the login and incorrect-login.
.login {
margin: 4px auto;
width: 70%;
height: 15%;
display: block;
padding: 5px;
padding-left: 10px;
position: absolute;
}
.incorrect-login {
border: 2px solid red;
animation: move 10s;
}
If you want to use property "left" in animation you have also add property position: absolute; to the component's class.
And code for the password input class name should be:
className={`input ${this.state.incorrect ? 'login incorrect-login' : 'login'}`}
Also you should not use document.getElementById in React applications directly. If you really need to get reference of the dom element created by React, you should use ref prop instead.