Display div on hover for mapped buttons - html

So I have a group of buttons that are displayed using a map. When I hover over one of these buttons I want a black box to appear on the side of it. This black box will be developed into a preview.
However, because of the map function, the rest of the buttons get a preview when I hover over one button. This is how the buttons look before the hover. You have a subject, and the courses are listed under this. Both the courses and subjects are mapped.
When I hover over a single button all the button previews are set to true. When I move my mouse off the button, all the previews are removed. How can I single out a button to have a specific preview?
export default function Yeartwo() {
const [grade, setGrade] = useState(localStorage.getItem('grade'));
const [preview, setPreview] = useState(false);
let grades = Object.keys(courseData)
let nextGrade = grades[Object.keys(courseData).indexOf(grade)+1]
let subjects = courseData[nextGrade].subjects
let courses = courseData[nextGrade].classes
function coursePreview(e) {
e.preventDefault();
console.log('hovered')
setPreview(true)
}
return (
<div className="courseSelect">
{subjects.map((subject, index) => (
<div key={index}>
<h2 className="subject">{subject}</h2>
<div className="courses">
{courses[subject].map((course, index) => (
<div key={index}>
<button value={subject} className="course_btn" onMouseEnter={() => setPreview(true)} onMouseLeave={() => setPreview(false)}>{course}</button>
{preview && <div className="preview"> {course} </div>}
</div>
))}
</div>
</div>
))}
</div>
)
}

Reorder your preview state to contain an object instead of booleans. The object should be in the following format: { 'course1': boolean, 'course2': boolean, ..., 'courseN': boolean } - course is the key and the boolean value represents whether the preview for this item should be shown or not.
Therefore, your condition should look like this: {preview['course'] && <div className="preview"> {course} </div>}
And update your listeners to be like this: {... onMouseEnter={() => setPreview({...preview, 'course': true}) ...} and the same for the onMouseLeave method.

Related

React: How to reset other Components State and Update a Component State in OnClick function

I wanted to emulate a Google Photos gallery style in my project. Where if I click on an image it will provide a partial view, but when I click on another image the first partial view is replaced by the new image and data.
The structure of my code is that I have a set of components Pictures that contains another component PartialView, I have an onClick function that checks if that picture is clicked, the PartialView component will appear. However, I don't know how to manage the states so that when I click on another different image, the opened PartialView will close and the other component's PartialView will open instead.
My current bug is that I can open both PartialView at the same time, and they end up overlapping. I just want the newly clicked Pictures component to have a PartialView opened.
The structure of my code:
// the function that allows me to click on each Picture independently
// state only updated on Picture id
const [open, setOpen] = useState(false);
function openFunction (id) {
setOpen({
...open,
[id]: !open[id],
});
}
// each Picture component made from different inputs from server
// PartialView will only appear if Picture is clicked
let pictures = null;
if (userData) {
pictures = userData.map(
({ artefactName, artefactImg, description, artefactDate, _id }) => (
<article
className="card-container"
onClick={() => openFunction(_id)}
style={{ padding: open[_id] ? '0 0 480px 0' : '0 0 0 0' }}
>
<div>
<div className="card">
<img src={artefactImg.imgURL} />
<div className="card-title">
<p>{artefactName}</p>
</div>
</div>
<div style={{ display: open[_id] ? 'block' : 'none' }}>
<PartialView title={artefactName} image={artefactImg} desc={description}
date={artefactDate} />
</div>
</div>
</article>
)
);
}
// item Pictures are returned
return (
<main>
<div className="main-container">
<div className="main-cards">
<div className="section-cards">
<div className="feed-cards">
{pictures}
</div>
</div>
</div>
</div>
</main>
);

react onload reference to components rendered HTML

I am making a react component that acts as a tablist, and it contains many tab components inside of it. I want to make an onload function in the tablist so it will reach out and link to its child tabs, but I can't figure out how to send the tablist's location to the function so the function will know which node to start at.
For other events, e.g. onclick, I can send event.target, but for this idk.
Example code, if it helps
const Tabs = ({ list, children, ignoreSession = false }: TabsProps) => {
window.addEventListener('load',initTabs(this.nodePosition)); //<-what I can't figure out
return (
<div
className={`wb-tabs`}
>
<ul role="tablist" className="generated" />
<div className="tabpanels">{children}</div>
</div>
);
};
export default Tabs;```

radio buttons is not functioning properly

a division has a checkboxs and by clicking that checkbox radio buttons options need to be shown for a particular checkbox
checkbox is mapped as according to the array
<form id="lab_test_detail">
{item.subcategory.map((item, index) =>
<>
<input type={"checkbox"} onChange={() => handleChange(item, index)} name={item.id}></input>
<label style={{ position: "inherit", zIndex: "10" }}>{item.categoryname} </label>
<div id={index}></div>
</>
)}
</form>
when the checkbox is changed radio buttons option should have to populate on
handleChange is
const handleChange = (item, index) => {
httpClient.GET(`medical-institute/categoryId/${item.id}`, false, true)
.then(resp => {
// debugger;
document.getElementById(index).innerHTML =
`
${
resp.data.data.map((item1,index1)=>{
return`<form>
<input type="radio" onChange=${handleRadioChange(item,index)}></input>
<label >${item1.medicalinstitutename}</label>
<span> Rs.${item1.price}</span>
<br/>
</form>
`
})
}
`
console.log("response is", resp.data.data)
})
}
const handleRadioChange = (item, index) => {
console.log("inside radiochange")
console.log("dasdas", item, index)
}
so when radio button is clicked i need to do a particular thing but handleRadioChange is called when i click on the checkox but not when i click on radio , why is this happening, a ny solution?
Because you are executing the handleRadioChange once the node is added into the DOM.
Change the onChange handler
handleRadioChange(item,index)
to
() => handleRadioChange(item,index)
You should try to work with the virtual DOM instead of insert html string.
In your case, I think you can use state to store the item data for display and trigger the re-render. BTW you might don't have to fetch the data every time user clicks a check box.

How to hide a component if there is no result found in Angular

In my Home page, I have a search bar and imported three components. my search bar have the ability to search through them but just wondering how can I hide a particular component if a result in not found in that component and only show a component that have a result.
The problem I have right now is, if search result is only found in Application group component then, the attachment and training component is showing me blank (pls check uploaded image below). I just want to hide the components that don't have the result while user is filtering/searching and just show it back the component when a user cancel the search.
I would be really appreciated if I can get help or suggestion on this.
<!-- attachments -->
<div>
<app-attachment [attachments]="entity.attachments"></app-attachment>
</div>
<!-- appgroups -->
<div *ngFor="let entityGroup of entity.entityGroups">
<app-application-group [entityGroup]="entityGroup" [entity]="entity"></app-application-group>
</div>
<!-- Training and Support -->
<div>
<app-training [entity]="entity"></app-training>
</div>
</div>
ngOnInit(): void {
this.searchText$ = this.searchService.searchText
.asObservable()
.pipe(debounceTime(750), distinctUntilChanged())
.subscribe((value) => {
this.filterValue = value;
this.loadApplication(this.entityType, this.entityId);
});
this.collapse = false;
this.expanded = true;
this.route.url.subscribe((_value) => {
this.entityType = BaseEntity.stringToType(_value[0].path);
this.entityId = Number(_value[1].path);
this.loadApplication(this.entityType, this.entityId);
this.populateMeetups(this.entityId);
});
}
loadApplication(entityType: EntityType, entityId: number): void {
this.color = BaseEntity.color(this.entityType);
if (this.entityType && this.entityId) {
// this.filterValue = null;
this.childrenActive = null;
this.pageSize = 999;
this.childrenActive = true; // We want to bring only active children for things that have tables.
}
this.entityService
.getApplicationDetails(
entityId,
entityType,
this.pageSize,
this.childrenActive,
this.filterValue,
)
.subscribe((entity) => {
this.entity = entity;
this.ancestor = this.entity.channels.get(0);
this.entityGroup = this.entity.entityGroups.filter(
(r) => r.entityType === EntityType.Application,
);
this.entity.attachments = this.entity.attachments.filter((app) => {
return app.name.toLowerCase().includes(this.filterValue.toLowerCase());
});
});
}
click here to view my screenshot
Use *ngIf to remove stuff from the DOM you don't want to show. For example:
<ng-container *ngIf="entity.attachments?.length">
<div>
<app-attachment [attachments]="entity.attachments"></app-attachment>
</div>
</ng-container>
Or hide it with css:
<div [ngClass]="entity.attachments?.length ? 'show' : 'hide'">
<app-attachment [attachments]="entity.attachments"></app-attachment>
</div>
and the css:
.hide {
visibility: hidden;
}
You may want to consider placing the *ngIf inside the child component instead of the parent.
Try to use ngIf by checking the length of the search result instead of creating a new variable. Also use else block to display no result found as shown below
Show result here
No result found .

Trying to get an overlay to toggle on a repeating element. Can't use getElementByID

As per the title I want an overlay to trigger when an image is clicked on, but I then want it to disappear if anywhere other than 3 buttons on the overlay are clicked.
Unfortunately using getElementbyID won't work as the items repeat on a masonry layout.
<Masonry
breakpointCols={breakpointColumnsObj}
className="my-masonry-grid"
columnClassName="my-masonry-grid_column">
{this.state.data.map((data) => (
<div>
<div className="tilebutton" key="" style={{width:'100%',position:'relative'}} href={data.URL} >
<div className="tileoverlay" id="overlay" onClick={overlayoff} onclickout key={data.URL} style={{display:'none',width:'100%',zindex:'2',position:'absolute'}}>
<a className="button1" href={data.URL} onClick>{data.Name}</a>
<a className="button2" href={data.CompURL}>{data.Company}</a>
<a className="button3" href={'instagram.com/'+data.insta}>{data.Company}<img src="\img\Icons\instagram.svg" className='instalogo'/></a>
</div>
<img src={data.img} onClick={overlayon} style={{width:'100%'}}/>
</div>
</div>
))}
</Masonry>
)
function overlayon() {
document.getElementById("overlay").style.display = "block";
}
function overlayoff() {
document.getElementById("overlay").style.display = "none";
}
Unfortunately using the id "overlay" means if I click any version of the masonry it will trigger the overlay on the first image. Is there some way to:
a) identify the element clicked so it will be the one with the toggling overlay
b) have an "onclickout" I could apply to the overlay's buttons
this is about 5 days into my first ever web build so frankly I haven't got a clue what I am doing - any help is appreciated.
Thanks in advance.
The idioms used in React discourages you to manipulate the DOM directly, unless you are doing something special, such as animation. And thus I don't recommend "identifying the element clicked".
With that said, you can manipulate the data, and trigger a redraw accordingly, by invoking some setSate function (in the example below, I've defined a setShouldShowOverlay, that, when invoked, will result in a redraw).
What I recommend is for you to pull out the code inside this.state.data.map() into its own component, like so:
import React, { useState } from 'react';
function Data({ data }) {
const [ shouldShowOverlay, setShouldShowOverlay ] = useState(false);
return (
<div>
<div className="tilebutton" key="" style={{width:'100%',position:'relative'}} href={data.URL} >
<div className="tileoverlay" id="overlay" onClick={() => { setShouldShowOverlay(false); }} onclickout key={data.URL} style={{display:'none',width:'100%',zindex:'2',position:'absolute'}}>
<a className="button1" href={data.URL} onClick>{data.Name}</a>
<a className="button2" href={data.CompURL}>{data.Company}</a>
<a className="button3" href={'instagram.com/'+data.insta}>{data.Company}<img src="\img\Icons\instagram.svg" className='instalogo'/></a>
</div>
<img src={data.img} onClick={() => {
setShouldShowOverlay(true);
}} style={{width:'100%'}}/>
</div>
</div>
);
}
Then finally, update your Masonry code like so:
<Masonry
breakpointCols={breakpointColumnsObj}
className="my-masonry-grid"
columnClassName="my-masonry-grid_column">
{this.state.data.map((data) => (
<Data data={data} />
))}
</Masonry>