Hide dropdowns in loop on click outside - javascript

I have an array of values which would render a list with a dropdown menu.
I would show/hide the dropdown on click of an element.
projectData.map(project => (
<div>
<div>{project.name}</div>
<div
onClick={() => setDropdownId((prevState) => (
prevState === project.id ? null : project.id)
)}
>
Show
</div>
{dropdownId === project.id && (
<ul className="dropdown">
<li>option</li>
</ul>
)}
</div>
))
How can I hide the opened dropdown on outside click?
I tried with ref but is not working as expected as the elements are in a loop.

Use onBlur to trigger changes when loosing focuse
handleBlur = (e) => {
// add you code here
}
projectData.map(project => (
<div>
<div>{project.name}</div>
<div
onBlur={handleBlur}
onClick={() => setDropdownId((prevState) => (
prevState === project.id ? null : project.id)
)}
>
Show
</div>
{dropdownId === project.id && (
<ul className="dropdown">
<li>option</li>
</ul>
)}
</div>
))

Related

How to add border to a active div?

I want to make a border when the user set a div active.
I tried to add hover, selected and active, but it did not work.
This is the part that user will select
{this.state.fields.map((fields, k) =>
<div key={k} onClick={() => this.setState({
activeFields: fields
})}>
<div className="detailsPage-details " >
{fields.map((field) =>
<p key={k}>
{field.value}: {field.key}
</p>
)}
</div>
</div>
)}
How can I do that?
Your fields should have a unique field so that you could check which field was set to active, you could use the index of the array but it is not preferred as could lead to bugs.
{this.state.fields.map((field, k) =>
<div className={this.state.activeField===field.id?"active-div":""} key={k} onClick={() => this.setState({
activeField: field.id
})}>
<div className="detailsPage-details " >
{fields.map((field) =>
<p key={k}>
{field.value}: {field.key}
</p>
)}
</div>
</div>
)}

Search input loses focus when it doesn't find any title

Whenever I try to search, I can type only the letters which are similar to the one of the titles. If any letter is different I lose focus from input. It used to work normally before, but Idk what happened now. Any suggestions?
How it first looks like:
After starting to type on search input:
When I type a letter which is not in the title:
Code:
if (props.items.length === 0) {
return (
<div className="files-list center">
<Card>
<h2>No files found.</h2>
</Card>
</div>
);
} else if (
props.items.filter(
(file) =>
file.title.toLowerCase().includes(searchInput) ||
file.title.toUpperCase().includes(searchInput)
).length === 0
) {
return (
<div className="filesList">
<div className="search">
<input
type="text"
placeholder="Search Files"
onChange={(event) => setSearchInput(event.target.value)}
/>
</div>
<Card>
<h2>No files found.</h2>
</Card>
</div>
);
}
return (
<React.Fragment>
<div className="filesList">
<div className="actionsBtn">
<NavLink to="/add-file" className="addFileBtn" title="Add File">
<FontAwesomeIcon icon={faPlus} />
</NavLink>
<input
type="text"
placeholder="Search Files"
onChange={(event) => setSearchInput(event.target.value)}
/>
</div>
<ul className="files-list">
{props.items
.filter(
(file) =>
file.title.toLowerCase().includes(searchInput) ||
file.title.toUpperCase().includes(searchInput)
)
.map((file) => (
<FilesPostItem
key={file.id}
id={file.id}
title={file.title}
file={file.file}
creator={file.creator.name}
description={file.description}
creatorId={file.creator}
onDelete={props.onDeleteFile}
/>
))}
</ul>
</div>
</React.Fragment>
);
What I think happens is, your “default” return form the beginning gets reset by the return in the “if else”. So the Input is a new DOM Element and loses focus.
You might want to add something like this to your default return and remove your "if else" statement.
<ul className="files-list">
{props.items
.filter(
(file) =>
file.title.toLowerCase().includes(searchInput) ||
file.title.toUpperCase().includes(searchInput)
)
.map((file) => (
<FilesPostItem
key={file.id}
id={file.id}
title={file.title}
file={file.file}
creator={file.creator.name}
description={file.description}
creatorId={file.creator}
onDelete={props.onDeleteFile}
/>
))}
</ul>
{props.items
.filter(
(file) =>
file.title.toLowerCase().includes(searchInput) ||
file.title.toUpperCase().includes(searchInput)
).length > 0 &&
<Card>
<h2>No files found.</h2>
</Card>
}
What does this do?
It adds the "No files found" message to your default return and shows it only, if items is empty.
But i am pretty sure, there is a better way to do this.

React trigger only one element in array

I am in the process of making a comment system like the one on youtube. In my implementation, when I click on modify, all comments are now inputs but only the value of the selected input will be modified. how to trigger only the element i clicked.
as you can see it triggers all the array elements
function App() {
const [open, setOpen] = useState(false);
return (
<div className="container mt-5">
<MDBRow>
{data &&
data.map((item) => (
<MDBCol md="7" lg="7" key={item.id} className="mb-4">
{!open && (
<>
<div className="font-weight-bolder float-left pr-2">
{item.name}
</div>
<div className="float-right pr-2">
<button
onClick={() => {
setOpen(true);
}}
>
Modifier
</button>
</div>
</>
)}
{open && (
<UpdateData
id={item.id}
name={item.name}
onAbort={() => setOpen(false)}
submit={() => setOpen(false)}
/>
)}
</MDBCol>
))}
</MDBRow>
</div>
);
}
export const UpdateData = ({ name, id, onAbort, submit }) => {
const formik = useFormik({
initialValues: {
id: id,
name: name,
},
onSubmit: async (values) => {
console.log(values);
submit();
},
});
return (
<form onSubmit={formik.handleSubmit}>
<MDBInput
value={formik.values.name}
name="name"
onChange={formik.handleChange}
/>
<div className="float-right">
<span onClick={onAbort} className="text-capitalize grey-text">
Cancel
</span>
<button type="submit">confirm</button>
</div>
</form>
);
};
And this is the sandbox
that i have created
To trigger only one element to be clicked you have to pass the index
function App() {
const [open, setOpen] = useState(false);
const [selectedRow, setSelectedRow] = useState(undefined);
const onSelectedRow = (index) => {
setSelectedRow(index);
setOpen(true);
}
return (
<div className="container mt-5">
<MDBRow>
{data &&
// here you will get the index
data.map((item,index) => (
<MDBCol md="7" lg="7" key={item.id} className="mb-4">
{!open && (
<>
<div className="font-weight-bolder float-left pr-2">
{item.name}
</div>
<div className="float-right pr-2">
// Now onClick pass the index of selected row to onSelectedRow
<button
onClick={() =>onSelectedRow(index)}
>
Modifier
</button>
</div>
</>
)}
// here add condition to open selected row
{ (open === true && selectedRow === index) ? (
<UpdateData
id={item.id}
name={item.name}
onAbort={() => setOpen(false)}
submit={() => setOpen(false)}
/>
) : null
}
</MDBCol>
))}
</MDBRow>
</div>
);
}
Sandbox code https://codesandbox.io/s/youthful-wave-k4eih?file=/src/App.js
If you have any queries comment below!
instead of having a false default value in your hook you should have a unique key for each element. By default, it applies to all elements.

How to open active menu when user visit manually url

I think it might be silly question to ask but trust me I am new to React . Actually I am working on app where I have different collapsible menu. For example when I click on menu then dropdown show what I want to achieve when user copy manually link and put in the browser then I want to show specific menu . could someone please help me how to achieve this goal .
code
menus.map((item, index) => (
<UnlockAccess
currentUser={userType || null}
roles={item.roles}
key={index}
>
<div key={index}>
<p
onClick={() => {
this.RouteTo(`/${item.url}`);
this.toggle(item.id);
}}
className={`element ${this.state[item.activeTab]}`}
>
<span
className={
collapse === item.id ? "fa fa-minus" : "fa fa-plus"
}
key={index}
></span>
<p className={this.state[item.activeTab]}>
{item.name}
</p>
</p>
{collapse === item.id ? (
<div className="prop-child">
<ul>
{item.children.map((item, index) => (
<li
key={index}
className={this.state[item.activeTab]}
onClick={() =>
this.setState({ collapse: item.itemId }, () =>
this.toggle(item.itemId)
)
}
>
<Link
onClick={() => this.RouteTo(`/${item.url}`)}
to={item.url}
className={this.state[item.activeTab]}
>
{item.name}
</Link>
</li>
))}
</ul>
</div>
) : null}
toggle = (id) => {
if (!this.state.collapse || id !== this.state.collapse) {
this.setState({
collapse: id,
});
}
};
Just check the url params in the componentDidMount of your class. Then open the menu you want to be opened.

onChange and onClick inside a button doesn't work on I.E

React: 16.
onClick in input field and onChange doesn't work on IE. It works smoothly on chrome.
Is adding two onclick causing this issue ? One on button and another on input field ?
<button onClick={this.addEventOnTimePicker()}>
<div style={style.buttonContent}>
<div style={style.buttonIcon}>
<Clock style={style.iconStyle} />
</div>
<input
style={style.timeInput}
onClick={() => {
this.timePickerToggle(!open);
}}
disabled={disabled}
ref={instance => (this.dropDown = instance)}
value={typeof time === 'string' ? time : moment(time).format('LT')}
onChange={this.onTimeChange.bind(this)}
></input>
</div>
</button>
timePickerToggle = open => {
this.addEventOnTimePicker();
this.setState({ open }, () => {
....
}
});
};
Final Working code on IE:
<div
style={style.buttonContent}
onClick={() => {
this.timePickerToggle(!open);
}}
>
<div style={style.timeIcon}>
<Clock style={style.iconStyle} />
</div>
<input
style={style.timeInput}
disabled={disabled}
ref={instance => (this.dropDown = instance)}
value={typeof time === 'string' ? time : moment(time).format('LT')}
onChange={this.onTimeChange.bind(this)}
></input>
</div>

Categories

Resources