I have the following react component
export interface IDivBodyVisible {
isVisible: string;
}
export default class NavDropDownItem extends React.Component<{ItemContent: string}, IDivBodyVisible> {
constructor(props: any) {
super(props);
this.state = {
isVisible: 'none'
}
}
render() {
return (
<div className="divBox" >
<div className="divHeader" onClick={this.SwitchVisibility}>
<Icon className="icon" iconName="ChevronDown"/>
{this.props.ItemContent}
</div>
{
this.state.isVisible !== 'block' ? null :
<div className="divBody">
<ul className="ItemList">
<li>
<a className="miau" title="item1" onClick={this.ConsoleLog}>Item 1</a>
</li>
<li>
<a title="item2" onClick={this.ConsoleLog}>Item 2</a>
</li>
<li>
<a title="item3" onClick={this.ConsoleLog}>Item 3</a>
</li>
</ul>
</div>
}
</div>
)
}
private ConsoleLog = () : void => {
console.log("Test");
}
private SwitchVisibility = (): void => {
this.setState({
isVisible : this.state.isVisible === 'none' ? 'block' : 'none'
});
}
}
At the moment if I click on the icon, the divbody appears instantly. Instead, I want that the divbody slights very smooth. I already tried to give the .divBox a transition ease-out 0.2, but that didn´t do anything.
Is there any advice what I can try?
Related
I have a button navigation and when you click on a button, the active class is added. My goal is for the active class to be added to the button clicked, but remove that class of active on all other buttons if present. The 'About' button will have a class of active on page load.
Not sure how to translate this to React, in JavaScript on click I would remove the class from all the elements in a loop and add a class to the target clicked if it did not already have the active class.
Code Sandbox - https://codesandbox.io/s/toggle-active-on-class-clicked-remove-from-the-rest-r467l1?file=/src/App.js
export default function Header() {
const [active, setActive] = useState(true);
const toggleColor = function (e) {
// on load, 'About' button has active class
// when clicking another menu item add active class, remove active from the rest of buttons
console.log(e.target);
};
return (
<header className="header-img-container">
<nav>
<ul>
<li>
<button onClick={toggleColor} className={active ? "active" : ""}>
About
</button>
</li>
<li>
<button onClick={toggleColor}>Skills</button>
</li>
<li>
<button onClick={toggleColor}>Projects</button>
</li>
<li>
<button onClick={toggleColor}>Words</button>
</li>
</ul>
</nav>
</header>
);
}
There are so many ways to solve that problem. You can try this if it's meet your requirements.
import "./styles.css";
import { useState } from "react";
const list = ["About", "Skills", "Projects", "Words"];
export default function Header() {
const [activeLink, setActiveLink] = useState("About");
return (
<header className="header-img-container">
<nav>
<ul>
{list.map((item) => (
<li key={item}>
<button
onClick={() => setActiveLink(item)}
className={activeLink === item ? "active" : ""}
>
{item}
</button>
</li>
))}
</ul>
</nav>
</header>
);
}
Create a state like this
const [active, setActive] = useState({About: true, Skills: false, Projects: false, Words: false})
А change local parameter to add a class to element. For example
<li>
<button onClick={() => {
setActive({...active, About: false, Skills: true, Projects: false,
Words: false })
}}>Skills</button>
</li>
There are many possible approaches, here is a basic example that uses an object type active state to store the value for each list item.
const [active, setActive] = useState({ About: true })
The list data is stored in an array so it can be mapped in the JSX part of the component.
const itemList = ["About", "Skills", "Projects", "Words"]
While index is not an ideal key it is used here just for example purpose.
{
itemList.map((item, index) => (
<li key={index}>
<button
onClick={() => toggleColor(item)}
className={active[item] ? "active" : ""}
>
{item}
</button>
</li>
));
}
toggleColor sets value for active, and it specify that active should always be in the format of {About: true}, {Skills: true} and such. The !!! covers the case when certain keys are not existing in the object.
const toggleColor = function (item) {
setActive((prev) => {
return { [item]: !!!prev[item] };
});
};
Below is the full example, it runs in the snippet for convenience.
function Header() {
const [active, setActive] = React.useState({ About: true });
const itemList = ["About", "Skills", "Projects", "Words"];
const toggleColor = function (item) {
// on load, 'About' button has active class
// when clicking another menu item add active class, remove active from the rest of buttons
setActive((prev) => {
return { [item]: !!!prev[item] };
});
};
return (
<header className="header-img-container">
<nav>
<ul>
{itemList.map((item, index) => (
<li key={index}>
<button
onClick={() => toggleColor(item)}
className={active[item] ? "active" : ""}
>
{item}
</button>
</li>
))}
</ul>
</nav>
</header>
);
}
const App = () => {
return (
<div>
<Header />
</div>
);
};
ReactDOM.render(<App />, document.querySelector("#root"));
.App {
font-family: sans-serif;
text-align: center;
}
button {
padding: 6px;
}
.active {
border: 1px solid pink;
color: hotpink;
}
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.production.min.js"></script>
I am a beginner in Web Development.
Why can't I place the pagination bar to be horizontally centered?
I have tried to include width:100% and even display: block.
However, results are the same.
Thank you for your help.
I want to put the pagination in the center of the red border.
App.js
...
<Pagination postsPerPage={imagePerPage} totalPosts={totalImages} paginate= {paginate}/>
...
Pagination.js
import React , { useState, useRef, useEffect, useContext } from 'react';
import 'bootstrap/dist/css/bootstrap.min.css';
const Pagination = ({ postsPerPage, totalPosts, paginate }) => {
const pageNumbers = [];
const [currentPage,setCurrentPage] = useState(1);
for (let i = 1; i <= Math.ceil(totalPosts / postsPerPage); i++) {
pageNumbers.push(i);
}
const totalNumOfPages = pageNumbers.length;
useEffect(() => {
console.log("Cureent Page:")
console.log(currentPage);
},[currentPage]);
return (
<nav>
<ul className='pagination' style={{width:'100%',margin:'auto',border: '1px solid red' }}>
<li class="page-item">
<a class="page-link" aria-label="Previous"
onClick={() =>{
if (currentPage > 1){
paginate(currentPage-1);
setCurrentPage(currentPage-1);
}
}
}
>
<span aria-hidden="true">«</span>
<span class="sr-only">Previous</span>
</a>
</li>
{pageNumbers.map(number => (
<li key={number}
className={ `page-item ${(currentPage === number)? 'active' : '' }`}>
<a onClick={() =>{ paginate(number);setCurrentPage(number)}} className='page-link' >
{number}
</a>
</li>
))}
<li class="page-item">
<a class="page-link" aria-label="Next"
onClick={() =>{
if (currentPage < totalNumOfPages){
paginate(currentPage+1);
setCurrentPage(currentPage+1);
}
}
}
>
<span aria-hidden="true">»</span>
<span class="sr-only">Next</span>
</a>
</li>
</ul>
</nav>
);
};
export default Pagination;
Looks like you are using Bootstrap. .pagination is configured to be a Flex container. This is why your li elements are already displayed in a row. You can still use the concept of auto margins, but instead of doing it on the parent, you can use this property on the children.
li.page-item:first-child {
margin-left: auto;
}
li.page-item:last-child {
margin-right: auto;
}
First, you need to put your elements into div as below, and move the ul style part to a separate div.
<div className="text-center">
<div style={{width:'100%', border: '1px solid red', height : '40px'}}>
<nav>
<ul className='pagination' >
....
Second, override pagination class with
.pagination {
display: inline-flex;
}
Here is the live demo.
I will only show relevant code as there is a lot of it. This is the parent component App:
export default class App extends React.Component {
render() {
return (
<nav className="navbar navbar-expand-xl navbar-side" aria-label="Side Navigation">
<div className={`navbar-toggler ${this.state.notification ? 'has-notification' : ''}`} data-toggle="collapse" data-target="#sidebarCollapse" aria-controls="sidebarCollapse" aria-expanded="false" aria-label="Toggle side navigation">
Menu
</div>
<div className="collapse navbar-collapse" id="sidebarCollapse">
<ul className="navbar-nav mr-auto">
<li className="nav-user">
<div className="profile-pic">
<i className="fa fa-lg fa-user mt-1" />
</div>
<i><span>{this.state.authenticatedUser.first_name} {this.state.authenticatedUser.last_name}</span><br />{this.state.authenticatedUser.job_title}</i>
</li>
<NavBar />
</ul>
</div>
</nav>
)
}
This is the Navbar component:
class NavBar extends Component {
constructor(props) {
super(props);
this.state = {
auth: false,
slide: 0, // How much should the Navbar slide up or down
lastScrollY: 0, // Keep track of current position in state
}
}
componentWillMount() {
let navbar = document.getElementById('navbar-div');
navbar.addEventListener('scroll', this.handleScroll);
}
componentWillUnmount() {
let navbar = document.getElementById('navbar-div');
navbar.removeEventListener('scroll', this.handleScroll);
}
handleScroll = () => {
let navbar = document.getElementById('navbar-div');
const { lastScrollY } = this.state;
const currentScrollY = navbar.scrollY;
if (currentScrollY > lastScrollY) {
this.setState({ slide: '-48px' });
} else {
this.setState({ slide: '0px' });
}
this.setState({ lastScrollY: currentScrollY });
};
render() {
return (
<div className="navbar-div" id="navbar-div">
{this.adminMenu()}
{this.usersMenu()}
</div>
);
}
}
adminMenu and usersMenu are just arrays of objects which output the object names. The error I get says navbar is null hence it cannot add an event listener onto a null object. How do I solve this?
Use componentDidMount rather than componentWillMount. The latter will run before any markup is rendered.
You can read more about lifecycle methods here: https://reactjs.org/docs/state-and-lifecycle.html#adding-lifecycle-methods-to-a-class
However, I'd highly recommend you to apply a handler on the element instead of componentDid/Will/Mount/Unmount, like this:
render() {
return (
<div className="navbar-div" id="navbar-div" onScroll={this.handleScroll}>
{this.adminMenu()}
{this.usersMenu()}
</div>
);
}
This seems like it would be simple but I cannot find anything to address it.
What I'm trying to do is to have a CSS spinner/loader while an asynchronous list loads. I've tried all kinds of things including trying to use ngSwitch, but so far nothing works and the spinner just spins indefinitely. Here is the base code:
<div class="col-md-2 mediaContainer">
Media:
<ul class="media">
<li *ngFor="let medium of media | async"
[class.selected] = "medium === selectedMedium"
(click)="onSelect(medium)">
{{medium.name}}
</li>
</ul>
</div>
While I've tried lots of things the following is an example that will not work:
<div class="col-md-2 mediaContainer">
Media:
<ul class="media" [ngSwitch]="status">
<li *ngSwitchCase="loading"><loaders-css [loader]="'square-spin'" [loaderClass]="'my-loader'"></loaders-css></li>
<li *ngFor="let medium of media | async"
[class.selected] = "medium === selectedMedium"
(click)="onSelect(medium)">
{{medium.name}}
</li>
</ul>
</div>
I've also put the ngSwitch and/or the switch cases inside the list but that doesn't work either. I have a feeling that 'status' only works with the elements themselves not their content, but I'm not sure how to set a switch value either (ie https://angular.io/docs/ts/latest/api/common/index/NgSwitch-directive.html) and this seems like something to do with a promise possibly. Any ideas? Given the nature of what I'm doing it seems like it would be pretty common...
You can use the *ngIf directive to control the UI rendering logic , and use state variables to control the completion of request.
Here is a sample.
on your component
private loadingComplete = false;
private isLoading = true ;
private error;
private media[];
constructor(private mediaService:MediaService) {
this.mediaService.getMedia()
.subscribe(
res =>{
this.media = res;
this.isLoading = false;
this.loadingComplete = true;
},
err => {
this.loadingComplete = true;
this.error = err;
});
}
isEmptyResult(){
//N.B assuming ther server returns empty array if no media found.
return (!this.isLoading && !this.error && this.loadingComplete && this.media && this.media.length===0);
}
mediaAvailable(){
return (!this.isLoading && !this.error && this.loadingComplete && this.media && this.media.length>0);
}
on your template
<div class="col-md-2 mediaContainer">
Media:
<li *ngIf="isLoading">
<loaders-css [loader]="'square-spin'" [loaderClass]="'my-loader'">
</loaders-css>
</li>
<p *ngIf='error'>
Network Error.
</p>
<p *ngIf='isEmptyResult()>
No media found.
</p>
<ul *ngIf="mediaAvailable()" class="media" >
<li *ngFor="let medium of media"
[class.selected] = "medium === selectedMedium"
(click)="onSelect(medium)">
{{medium.name}}
</li>
</ul>
</div>
I have tried showing spinner till data is available.
Please look into the following plunker.
https://plnkr.co/edit/4kSximI2a2l6SWVzqsyl?p=preview
Concerned code:-
HTML:
<ul >
<div class="loader" [hidden]="myVar"></div>
<li *ngFor="let medium of media "
(click)="onSelect(medium)">
{{medium.name}}
</li>
</ul>
CSS:-
/* Styles go here */
.loader {
border: 16px solid #f3f3f3; /* Light grey */
border-top: 16px solid #3498db; /* Blue */
border-radius: 50%;
width: 120px;
height: 120px;
animation: spin 2s linear infinite;
}
#keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
Controller
import {Component, NgModule} from '#angular/core'
import {BrowserModule} from '#angular/platform-browser'
export class Media {
id: number;
name: string;
}
#Component({
selector: 'my-app',
template: `
<div>
<h2>Hello {{name}}</h2>
</div>
<div class="col-md-2 mediaContainer">
Media:
<ul >
<div class="loader" [hidden]="myVar"></div>
<li *ngFor="let medium of media "
(click)="onSelect(medium)">
{{medium.name}}
</li>
</ul>
</div>
`,
})
export class App {
name:string;
myVar: boolean;
media: Media[];
constructor() {
this.name = 'Angular2';
setTimeout(() => {
this.title = "Brave New World";
this.myVar = true;
this.media = [
{ id: 11, name: 'Mr. Nice' },
{ id: 12, name: 'Narco' },
{ id: 13, name: 'Bombasto' },
{ id: 14, name: 'Celeritas' },
{ id: 15, name: 'Magneta' },
{ id: 16, name: 'RubberMan' },
{ id: 17, name: 'Dynama' },
{ id: 18, name: 'Dr IQ' },
{ id: 19, name: 'Magma' },
{ id: 20, name: 'Tornado' }
];
}, 2000);)
}
}
#NgModule({
imports: [ BrowserModule ],
declarations: [ App ],
bootstrap: [ App ]
})
export class AppModule {}
Please let me know in case of any query .
Like slick. Struggling to get it working w/ dynamic dataset from Meteor. Problem that I now have, when opening prior instances of an item I'm left w/ ghost blank slides from priorly selected. If I open an item that has 15 images, close it and select another w/ 1 - I have 14 blank slide. Also, if I had clicked through the 15, say to the 7th slide, when I open the new item w/ 1 I'm still pointed at slide 7, which is blank, and need to click 6 slides to the left to actually see an image.
It would seem to me that I need to somehow reset the slick control? I'm just not sure where of how to do it.
Followed instructions here: Image slider doesn't show my images properly before they're cached to get up and running.
parent template
<div class="col-md-7">
<div class="gallery">
{{#each galleryImages}}
{{> slickItem}}
{{/each}}
</div>
</div>
slick template
<template name="slickItem">
<img class="slick-image" src="{{href}}">
</template>
I've played around w/ a few different options on the slick render.
Template.slickItem.onRendered(function() {
setTimeout(function() {
$('.gallery').slick({
arrows: true,
dots: false,
autoplay: false,
infinite: true,
mobileFirst: true,
adaptiveHeight: true
})
}, 100);
});
Working with friend Patrick Lewis we worked out the following - done now in React versus Blaze.
Carousel = React.createClass({
getInitialState: function() {
return {
carousel : null
}
},
componentDidMount() {
console.log("componentDidMount");
this.setState({
// carousel: $('#maveletCarousel')
carousel: $(this.props.id)
}, function() {
console.log("carousel: componentDidMount", this.state);
this.state.carousel.carousel();
});
},
initCarousel: function() {
// $('#maveletCarousel').carousel();
// Initialize the carousel
if( this.state.carousel ) {
this.state.carousel.carousel({
interval : 2000
});
}
},
render() {
var hrefId = "#" + this.props.id;
// <li data-target={hrefId} key={ index } data-slide-to={ index } className={ indicatorClass }></li>
return (
<div id={this.props.id} className="carousel slide">
<ol className="carousel-indicators">
{
this.props.slides.map((slide, index) => {
return (
<li data-target={hrefId} key={ index } data-slide-to={ index }></li>
);
})
}
</ol>
<div className="carousel-inner" role="listbox">
{
this.props.slides.map((slide, index) => {
return (
slide && <Slide slide={ slide } key={ index } index={ index } initCarousel={ this.initCarousel }/>
);
})
}
</div>
<a className="left carousel-control" href={ hrefId } role="button" data-slide="prev">
<span className="glyphicon glyphicon-chevron-left" aria-hidden="true"></span>
<span className="sr-only">Previous</span>
</a>
<a className="right carousel-control" href={ hrefId } role="button" data-slide="next">
<span className="glyphicon glyphicon-chevron-right" aria-hidden="true"></span>
<span className="sr-only">Next</span>
</a>
</div>
);
}
});
Slide = React.createClass({
componentDidMount() {
this.props.initCarousel();
},
render() {
var isActive = 'item'
if( this.props.index === 0 ) {
isActive = 'item active'
}
return (
<div className={ isActive }>
<img src={this.props.slide.href}></img>
</div>
)
}
})