How to expand and collapse each list item using react? - css

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

Related

Unable to scroll through icons carousel

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

Google Places Auto Complete making input field go up

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!

How can i make slider animation?

I need to make the vertical slider animation ( dots and line ) as in this pic
i managed to do the Accordion and the dots but i don't know how i will going to implement it ( i'm using pseudo )
**my accordion component Where i define the logic of my nested accordions as in images based on array of data **
function MultiLevelAccordion({
data,
bodyClass,
headerClass,
wrapperClass,
renderHeader,
renderContent,
}) {
const RootAccordionId = 'parent-0';
const [accordionsStates, setActiveCardsIndex] = useMergeState({});
const onAccordionToggled = (id, activeEventKey) => {
console.log(activeEventKey);
setActiveCardsIndex({
[id]: activeEventKey ? Number(activeEventKey) : activeEventKey
});
};
console.log('data', data);
const accordionGenerator = (data, parentId) => {
return map(data, (item, index) => {
const active = accordionsStates[parentId] === index;
const hasChildren = item.hasOwnProperty('children') && isArray(item.children) && !isEmpty(item.children);
const isRootAccordion = RootAccordionId === parentId;
const isLastNestedAccordion = !isRootAccordion && !hasChildren;
const accordion = (
<Card className={classNames(wrapperClass, {
'nested-root-accordion': !isRootAccordion,
'last-nested-root-accordion': isLastNestedAccordion,
'multi-level-accordion': !isLastNestedAccordion
})}
>
<Accordion.Toggle
{...{ ...item.id && { id: item.id } }}
onClick={() => this}
as={Card.Header}
eventKey={`${index}`}
className={'cursor-pointer d-flex flex-column justify-content-center'}
>
<div className="d-flex justify-content-between align-items-center">
{renderHeader(item, hasChildren)}
<img
style={{
transition: 'all .5s ease-in-out',
transform: `rotate(${active ? 180 : 0}deg)`
}}
src={setIcon('arrow-down')}
className="ml-2"
alt="collapse"
/>
</div>
</Accordion.Toggle>
<Accordion.Collapse eventKey={`${index}`}>
<Card.Body
className={`accordion-content-wrapper ${!hasChildren ? 'accordion-children-body' : ''} ${bodyClass}`}
>
{!hasChildren ? renderContent(item, hasChildren) : (
<Accordion onSelect={activeEventKey => onAccordionToggled(`${parentId}-${index}`, activeEventKey)}>
<Fade cascade top when={active}>
{accordionGenerator(item.children, `${parentId}-${index}`)}
</Fade>
</Accordion>
)}
</Card.Body>
</Accordion.Collapse>
</Card>
);
return isRootAccordion ? accordion : (
<div className={'d-flex align-items-center'}>
{accordion}
<div className="accordion-indicator-wrapper">
<div className="accordion-indicator" id={`indicator-${parentId}-${index}`} />
</div>
</div>
);
});
};
if (!isArray(data)) {
return;
}
return (
<Accordion onSelect={activeEventKey => onAccordionToggled(RootAccordionId, activeEventKey)}>
{accordionGenerator(data, RootAccordionId)}
</Accordion>
);
}
export default MultiLevelAccordion;
the styles used in scss
.faqs-questions-wrapper {
padding: 20px 10px
}
.faqs-q-count {
color: $black-color;
font-size: calc(1rem - 1rem/8)
}
.faqs-q-a-wrapper {
flex-basis: 95%;
}
.faq-child-title {
color: $black-color
}
.nested-root-accordion {
flex-basis: 90%;
}
.accordion-indicator-wrapper {
flex-basis: 10%;
width: 100%;
display: flex;
justify-content: center;
.accordion-indicator {
width: 10px;
height: 10px;
border-radius: 50%;
background-color: $theme-color;
position: relative;
}
}
Any clue?
Thanks in Advance.
React JS is gonna make this easy
The lines expansion will need to be coded based on the height of the box window
For the dropdown container keep the vertical button lines in a separate div than the Accordian
Check out this pen for creating lines between buttons
https://codepen.io/cataldie/pen/ExVGjya
css part:
.status-container{
padding:10px;
margin:10px;
position:relative;
display: inline-block;
}
.bullet{
padding:0px;
margin:0px;
display: inline-block;
z-index: 10;
}
.bullet:before {
content: ' \25CF';
font-size: 5em;
}
.bullet-before{
/*position:relative;
right:-12px;*/
}
.bullet-after{
/*position:relative;
left:-30px;*/
}
.line{
stroke:blue;
stroke-width:0.3em;
padding:0px;
margin:0px;
display: inline-block;
}
.line-on{
stroke:red;
}
.line-off{
stroke:gray;
}
.color-on{
color: red;
}
.color-off{
color: gray;
}
https://codepen.io/emwing/pen/LgzJOx
I think you can use some inspiration here

How to make css have reactive columns that always have same height per row

How do I make reactive bootstrap that has 6 columns, then 3, then 1 but columns always the same height when browser size changes?
The Angular component
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-tiles',
templateUrl: './tiles.component.html',
styleUrls: ['./tiles.component.scss']
})
export class TilesComponent implements OnInit {
superTotalData1: any = [];
superTotalData2: any = [];
tilePlannedBenefit = 'Planned Benefit';
tileMonthUpdated = '# Month Updated';
tileNotOnTrack = '# Not on Track';
tileBlownOut = '# Blown Out';
tileSlipped = '# Slipped';
tileReforecasted = '# Reforecasted';
tilePlannedCost = 'Planned Cost';
tileTotalAfe = 'Total AFE Amount';
tileAfeSpend = 'AFE Spend %';
tileAfeForecast = 'AFE Forecast %';
tileCarryForward = 'Carry Forward';
tileUnspent = 'Unspent';
constructor() { }
ngOnInit() {
this.updateUI();
}
parseAmount(value = 0) {
if (value >= 1000 && value < 1000000) {
return `$${Math.ceil(value / 1000)}k`;
} else if (value >= 1000000) {
return `$${Math.ceil(value / 1000000)}M`;
} else if (value >= 1000000000) {
return `$${Math.ceil(value / 1000000)}B`;
} else if (value >= 1000000000000) {
return `$${Math.ceil(value / 1000000)}T`;
}
return `$${Math.ceil(value)}`;
}
myYAxisTickFormatting(val) {
return '$' + val;
}
updateUI() {
this.superTotalData1 = [
new SuperTotalTile(this.parseAmount(564), this.tilePlannedBenefit),
new SuperTotalTile(String('Mar 2020'), this.tileMonthUpdated),
new SuperTotalTile(String(3), this.tileNotOnTrack),
new SuperTotalTile(String(0), this.tileBlownOut),
new SuperTotalTile(String(34), this.tileSlipped),
new SuperTotalTile(String(4), this.tileReforecasted),
new SuperTotalTile(this.parseAmount(345524), this.tilePlannedCost),
new SuperTotalTile(this.parseAmount(45345), this.tileTotalAfe),
new SuperTotalTile(`35%`, this.tileAfeSpend),
new SuperTotalTile(`6%`, this.tileAfeForecast),
new SuperTotalTile(this.parseAmount(4564), this.tileCarryForward),
new SuperTotalTile(this.parseAmount(4565), this.tileUnspent),
];
}
}
class SuperTotalTile {
constructor(
public value: string,
public label: string
) { }
}
The HTML
<div class="container">
<div class="row">
<div class="col-md-2 col-sm-4 col-xs-12" *ngFor="let superItem of superTotalData1">
<div class="super-tiles">
<div class="super-tile height-max">
<div class="super-tile-value">
{{superItem.value}}
</div>
<br>
<div class="super-tile-label">
{{superItem.label}}
</div>
</div>
</div>
</div>
</div>
</div>
The CSS
.row [class*='col-'] {
text-align: center;
background-color: #cceeee;
background-clip: content-box;
margin-bottom: 5px;
}
.super-tiles{
display: flex;
.super-tile{
margin: 0 5px;
padding: 4px;
flex-grow: 1;
flex-basis: 0;
display: flex;
flex-direction: column;
border-radius: 4px;
box-shadow: 0 1px 5px #d2d2d2;
// align-items: center;
// justify-content: center;
color: #ffffff;
background: #008cba;
}
.super-tile-label{
font-size: 0.85em;
}
.super-tile-value{
font-size: 1.5em;
font-weight: bold;
// padding-left: 8px;
// text-align: right;
}
}
.header-tiles{
display: flex;
.header-tile{
margin: 0 5px;
padding: 2px;
flex-grow: 1;
flex-basis: 0;
display: flex;
flex-direction: column;
// border-radius: 4px;
// box-shadow: 0 1px 5px #d2d2d2;
// align-items: center;
// justify-content: center;
color: #000000;
background: #ffffff;
}
.header-tile-label{
font-size: 0.85em;
}
.header-tile-value{
font-size: 1.5em;
font-weight: bold;
// padding-left: 8px;
// text-align: right;
}
}
I have created an example that shows the issue (see /examples/tiles/*). Here's a Stackblitz:
https://stackblitz.com/edit/bootstrap-rqqm3h?file=app/examples/tiles/tiles.component.ts
If the browser width is wide, then the columns behave as expected. If I decrease the browser width, there is a point where the columns are not filling the full row height as other columns in the row. E.g.: The Darker Blue tile should be the height of the lighter blue of the row.
E.g.: When it's displayed correctly
But if I decrease width a little (before it changes to only 3 columns):
and finally, how it's meant to look when it does reduce to 3 columns:
The example article that explains correct behaviour:
Bootstrap 6 to 3 columns when resize
and
https://medium.com/wdstack/varying-column-heights-in-bootstrap-4e8dd5338643
As seen in the example:
the columns don't have my blue tile the same height for each row unless the browser width is very wide.
there is excess white-space between the columns I can't work out how to minimize.
I'm struggling on what value makes the dark blue the same height as the light blue in all browser width examples. Any help greatly appreciated.

Can't get buttons to wrap to new line instead of overflowing container

I couldn't get a JSFiddle to work properly with React and some other dependencies, so I hope the link to this Github repo is sufficient for demonstrating the issue:
https://github.com/ishraqiyun77/button-issues/
Basically, a group of buttons is rendered and they should be auto-widened to fill white space and take up the whole row. This works in Chrome, Edge, Safari, and Firefox. It looks like this:
This isn't happening in IE. I've been messing with it for hours and haven't made much progress:
Here is the code, although could clone the repo I posted above:
// component.jsx
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import {
Button,
Col,
Modal,
ModalBody,
ModalHeader,
Row
} from 'reactstrap';
import styles from '../assets/scss/app.scss';
class TestPrint extends Component {
constructor(props) {
super(props);
this.state = {
modal: false,
}
this.toggle = this.toggle.bind(this);
}
toggle() {
this.setState({
modal: !this.state.modal
})
}
renderContent() {
let buttons = [];
for (let i = 1; i < 50; i++) {
buttons.push(
<Col key={i}>
<Button
key={i}
className='cuts-btn'
>
{i} - Test
</Button>
</Col>
);
};
return buttons;
}
render() {
return (
<div>
<Button
style={
{
position: 'fixed',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)'
}
}
onClick={this.toggle}
>
Open Modal for Buttons
</Button>
<Modal
size='lg'
isOpen={this.state.modal}
toggle={this.toggle}
className='results-modal'
>
<ModalHeader toggle={this.toggle}>
Button Issues
</ModalHeader>
<ModalBody>
<div className='results-bq-cuts'>
<Row>
{this.renderContent()}
</Row>
</div>
</ModalBody>
</Modal>
</div>
)
}
}
ReactDOM.render(<TestPrint />, document.getElementById('app'));
.results-modal {
max-width: 1200px;
.modal-content {
.modal-body {
margin-left: 13px;
margin-right: 13px;
.results-bq-cuts {
width: 100%;
.col {
padding:2px;
}
.cuts-btn {
font-size: 11px;
padding: 3px;
width: 100%;
box-shadow: none;
}
// .col {
// padding: 2px;
// display: table-cell;
// flex-basis: 100%;
// flex: 1;
// }
// .cuts-btn {
// font-size: 11px;
// padding: 3px;
// width: 100%;
// box-shadow: none;
// }
}
}
}
}
I have all of the <Button> wrapped in <Col> because that should be what is filling the white space by increasing the size of the button.
Thanks for the help!
IE11 doesn't like working out the width of flex items. If you add flex-basis: calc( 100% / 24 ); to .col it works :) Obviously use any width you want, but what I've given replicates the 21 boxes on one line. But essentially flex-basis needs a defined width to work.
​
Or add an extra class to each element (such as col-1 ) This'll also achieve the same thing.

Resources