search results highlight results using reactjs - javascript

I was trying to achieve search results like YouTube. Just trying, but there are some problems I'm encountering. I have made the working application in codesandbox here.
I am having a search box when you type anything it will show search results like a dropdown, for an example when you search in youtube you will be able to see results below, same thing/
The problems I'm facing are:
Assume i type "a", now you will be able to see total 6 results in the searched results div having a scroll.
1) So the 1st result itself will show the highlighted css. When i hover or when i navigate with key only the higlight css should come how can i achieve this.
2) so now if i hover on the second or result highlight css will show in the second or third but the 1st result also will show the css. i just want the higlight css at a time only one. (The same feature is there in youtube search)
3) right now only three elements are visible the remaining three are you need to scroll, when i navigate through arrow keys, the scroll is not happening how can i achieve this with scrollIntoView API
4) In 6 results the search string is "a", how can i make that string to be strong so it remains as highlighted so whatever you search it should be highlighted
Any help is appreciated! Please let me know if you have any queries

With consideration of the code that we updated in this sandbox: https://codesandbox.io/s/react-codesandbox-5fvk9
All you really need is a onMouseEnter() event-handler to get everything working.
Handler
handleMouseEnter = index => {
this.setState({
cursor: index,
})
}
Markup
{searchResults.map((item, index) => {
return (
<li
ref={ref => (this.itemRefs[index] = ref)}
onClick={() => this.goToItem(index)}
className={cursor === index ? 'active' : null}
onMouseEnter={() => this.handleMouseEnter(index)}
>
<label className="first-lb">{item.id}</label>
<label className="second-lb">{item.name}</label>
<label className="third-lb">{item.address}</label>
</li>
)
})}
With this, only one of hover-over or arrow-down/arrow-up will take effect at a time. So only one list-item will ever be highlighted.

Related

changing selected state of fetched item from API

I have an API that returns me arrays. I implemented the code to search 3 of them like this:
{response !== undefined &&
search !== "" &&
response.InsuranceServiceList.filter((insurance) =>
insurance.Name.replace(/ي/g, "ی", /ا/, "آ").includes(search)
)
.slice(0, 3)
.map((filtered, i) => (
<EachInsurance
filtered={filtered}
key={i}
styles={scss}
DatePresence={DatePresence}
IdInsurance={IdInsurance}
TimePresence={TimePresence}
/>
))}
whenever user types something in search box, 3 of these matching arrays will get rendered.
but I have two problems with selecting them.
this is each insurance component:
<Container
className={styles.eachInsurance}
style={{
borderRight: `${selectInsurance ? "20px" : "1px"} solid #fcc4de`,
}}
>
<div
onClick={() => {
setSelectInsurance((prev) => !prev);
setCount(0);
}}
>
<p className={styles.NameService}>{filtered.Name.replace(/ي/g, "ی")}</p>
</div>
</Container>
whenever user clicks on element. it will have pinkish border with the width of 20px.
the problem is, when I type another thing to search another item. it shows that Item selected.
just like the clicked div is still there but the content inside of it has changed.
how can I prevent this problem?
I thought it would render a new div per each array. but it wasn't.
the second problem is search itself. if you delete what you've write completely (emptying search bar). everything you have selected before will get removed and you will need to reselect it again.
I want to prevent this too.
You need to pass the id from your back end to your front end. then add a border based on the ids you pass to the selectInsurance, in that way you will know if the element change the border will be gone.
I think for your second problem you can add a new state that will reserve the whole object of the insurance and you first render from that array so every time you re-render your search array your selected insurance array will stay the same so they will remain in your page

How to use checkbox inside select options? React hook

Currently I'm trying to create this design.
It doesn't open and close as expected.
I also created a codesandbox
Three things:
1. I have a onClick, but I'm not sure if my logic is correct to open and close the button.Also should there be a useEffect here to listen to changes?
const showPlatformOptions =()=> {
// let checkboxes = el;
// console.log("ref:",myRef.current)
// if (!expanded) {
// //checkboxes.style.display = "block";
// setExpanded(true);
// } else {
// // checkboxes.style.display = "none";
// setExpanded(false);
// }
}
I have a onChange called selectionOptions that should let me know which platforms are selected, but it currently only shows one platform at a time, why?
Is there another way to create this dropdown and checkbox. Maybe a library using hooks?
Any help is appreciated.
import React, { useState,useEffect, useRef} from "react";
const SearchBar =()=>{
const [query, setQuery] = useState("");
const [expanded,setExpanded] = useState(false);
const [selectionOptions, setSelectionOptions] = useState(["Instagram","LinkedIn","Twitter"]);
const myRef = useRef(null);
const showPlatformOptions =()=> {
// let checkboxes = el;
// console.log("ref:",myRef.current)
// if (!expanded) {
// //checkboxes.style.display = "block";
// setExpanded(true);
// } else {
// // checkboxes.style.display = "none";
// setExpanded(false);
// }
}
const handleQueryChange = event => {
console.log("Event:",event.target.value)
setQuery(event.target.value);
};
return (
<form onSubmit={showPlatformOptions}>
<div className="w-64">
<div className="relative" onClick={showPlatformOptions}>
<h6>PLATFORMS </h6>
<select className="w-full font-semibold" onChange={handleQueryChange}>
{selectionOptions.map((platform,x) => (
<option key={x} value={platform}>
{platform}
</option>
))}
</select>
<div className="absolute left-0 right-0 top-0 bottom-0"></div>
</div>
<div
ref={myRef}
className="checkboxes border-gray-200 border border-solid">
<label htmlFor="one" className="block ">
<input type="checkbox" id="one" onChange={handleQueryChange} className="m-3"/>
Instagram</label>
<label htmlFor="two" className="block">
<input type="checkbox" id="two" onChange={handleQueryChange} className="m-3"/>
LinkedIn</label>
<label htmlFor="three" className="block">
<input type="checkbox" id="three" onChange={handleQueryChange} className="m-3"/>
Twitter</label>
</div>
</div>
</form>
);
}
You're really close with your logic and code so far so well done! I'll answer your questions in order:
I have a onClick, but I'm not sure if my logic is correct to open and close the button.Also should there be a useEffect here to listen to changes?
Yep your logic to toggle the hide and show of the dropdown is spot on. The only thing is you don't have to worry about CSS there. You can use this boolean to display or hide the dropdown when the state changes.
I have a onChange called selectionOptions that should let me know which platforms are selected, but it currently only shows one platform at a time, why?
Good question, the reason for this is because its firing on the change of "each" checkbox. As you change checkboxes you'll see the event fire and just show the value for the new checkbox. The checkbox itself is what the event.target refers to. Not the whole form
Is there another way to create this dropdown and checkbox. Maybe a library using hooks?
I don't think you'll need to in this case. I took your code sandbox and enhanced it with just a couple additional bits of code to show how close you were!
Here it is
I'll point out a couple of changes i made:
Instead of dealing with CSS I used your expanded variable to determine if I should render the dropdown. This conditional logic is on line 50.
Because you want to keep track of all the different queries I changed the query variable to keep track of which queries were selected in an array. The logic for this is on line 26, but basically if the user unticks a checkbox I remove it from the array (if its there) and if they check it I add it to the array (if its not there).
I hope this helps you out!
The answer given by Code Novitiate is really good here to solve the bugs in your existing code 👍 This is a slightly different approach that simplifies the code.
Currently you are trying to rendering a select and separately rendering checkboxes. The select and its contents are never actually used, asides of visually to show the dropdown icon.
Personally, I find the native select tag hard to work with, particularly when you want to select multiple options. Most select libraries seem to create their own component out of divs and style them to look and feel like a select, which I think might be the best approach here as well 😀
Approach
Conditionally render the checkboxes based on whether the dropdown has been expanded
Keep track of selections in local state
Update state whenever one of the checkboxes is clicked
Render selections in the dropdown header
Style dropdown header to look like a select
In my example I added a little triangle in css, but you would use something that better matches the designs you have
Example
I put together this CodePen which is hopefully useful for you
Other suggestions
react-select is a really good library for doing what you would like, take a look at the Multi example.
Given that selectionOptions never changes, there's no reason to hold that as state. You can just declare an array instead. In my example, this is the PLATFORMS constant.
remove <div className="absolute left-0 right-0 top-0 bottom-0" />. It looks like the effect is to create a div that covers the whole area? This gave me troubles because it interferes with some click events 😅
All you want to have a dropdown of with checkbox for Each element. Please refer to Sandbox
to understand the making of this whole dropdown.
the approach you are using is not Right and bit complex and not reusable. In react the whole idea is to create common reusable components but your code is very tightly coupled to your use-case. There is no point in creating a "Select" and then separately creating a list of checkboxes.
Checkboxes should appear as a child to you dropdown and not outside it, making it very easy for you to manage dropdown and its state.

How to hover on list children items in React (and change parent state) without slowing everything down?

I have a list of items and then map pins that correspond to those items. When one hover on the list, the map icon opens. This is the way many popular maps work. The user hovers on the list item and the map changes color or an info window pops open. Take gmaps for example:
But the way I have this setup in React currently just isn't working. So I have a parent list:
<div className={classes.parentList}>
{filtereditems.map((item, index) => (
<ShowCard
key={index}
item={item}
setCardMarkerHover={this.setCardMarkerHover}
resetCardMarkerHover={this.resetCardMarkerHover}
/>
))}
</div>;
Then, within my List items, I call the prop functions like so
onMouseEnter={e => props.setCardMarkerHover(item.node.id)}
onMouseLeave={e => props.resetCardMarkerHover()}
These functions setCardMarkerHover and resetCardMarkerHover call setState, which then rerenders everything(!) so that my map markers change. The infowindow pops open on the right marker.
Here are the functions:
setCardMarkerHover = id => {
this.setState({
hoveredCardId: id,
})
}
resetCardMarkerHover = () => {
this.setState({
hoveredCardId: "",
})
}
hoveredCardId then gets passed to the Map and Markers (pins), and they change according to whether that marker matches the same index/id for the list items being hovered.
The challenge, as you can likely imagine, is that this really bogs my list down. It no longer scrolls smoothly because so many parts of the DOM are having to repaint/rerender as the mouse enters and exits the different list items.
React-virtualized solves this problem through windowing but this seems like extreme overkill.
What would another way to be able to hover on a child list item and have it change something else in another child component without triggering so much repainting?

Drag-drop text into editable div which has multiple lines of text (and essentially inner elements)

I have been trying to get this done for a couple of days - however it seems that I need to reach out for some external help once again.
This is what I have been tasked with. I have to create a html text editor with some basic functionality. This is what it looks like:
This is all done manually - no external resources as far as we can - using React and Typescript/ES6.
Now, the email body is a DIV, which is editable.
<div id="EditableDIV" className="textarea" contentEditable={true} onDragOver={this.onDragOver} onDrop={this.drop}></div>
I want to drop some text into this as a start - but I eventually want to drop a HtmlElement so that I can style the item that was dropped. (you know, to look nice - with a cute little remove button, etc) However, I want to focus on only dropping text for now.
This is the code that fires when I drag an item:
<div className="fields">
{this.state.model.fields.map(function(field, index) {
return (
<div key={index} className="field" draggable={true} onDragStart={(e) => self.dragField(e, field)}>
<span className="dragText">{field.text}</span>
<span className="dragIcon fa fa-arrows"></span>
</div>);
})}
</div>
dragField(e: React.DragEvent<HTMLDivElement>, field: Field) {
e.dataTransfer.setData(
"text/html",
'[%' + field.text + '%]'
);
}
Here is the code that fires when it gets dragged over the Email Body editable DIV.
onDragOver = (e) => {
this._caretPosition = document.caretRangeFromPoint(e.pageX,e.pageY).startOffset;
}
Here, I am trying to keep track of the caret position. So that I can (attempt to...) drop my text at a certain location.
drop = (e) => {
e.preventDefault();
let sel = document.getSelection();
let data = e.dataTransfer.getData("text/html");
let startString = sel.focusNode.textContent.substring(0, this._caretPosition);
let endString = sel.focusNode.textContent.substring(this._caretPosition, sel.focusNode.textContent.length);
this._container.textContent = `${startString}${data}${endString}`;
}
Now, something to keep in mind, is that if I do something like this:
The underlying HTML changes to something like this:
And I think that this is also causing a problem with my drag-drop solution because there are now also elements being created as the user types.
When I drag my field, I can see my cursor move:
However, after letting go of the mouse button to initiate the drop, this is what happens:
Look - the Field dropped at the last caret position (last spot where I typed). It seems like the drop does not respect my location. Does this have something to do with the
document.getSelection()?
If I click between the text where I want to insert the dropped field first, and then do another drop, it seems to work SORT OF:
What just happened was that it placed the text in the correct ROW, BUT also at the caret position the element was dropped at (from the start of the row).
Is there perhaps a way to set the caret position to the one being indicated as it is being dropped? Or a better way to get the caret position?
Thanks in advance!

jQuery For Loop Image Check and Display

Good afternoon Stack Overflow,
I'm inexperienced when it comes to coding in general and I've been having a problem that's doing my head in!
If you'll allow me to set the scene...
The section of the project I am currently working on involves a user picking items from a warehouse in order to fulfil a shipment and in some cases they have to pick the same item from various locations, when that needs to be done, the small "!" type icon appears next to the item.
The user then can click on the icon and choose which locations they will be retrieving the stock from, they then press confirm on the modal and when it closes it sets the text back to blue and hides the icon.
The part I am having trouble with is that once all the locations have been established, the order needs to be processed and this requires a button to be clicked on, which I only want to appear once all the "!" icons are hidden.
I know there are alot of questions based on for loops and images checks and believe me when I say I've tried hard to figure this out myself and I've tried different approaches:
ShowProcess = false
for (i = 0; i<Picker; i++) {
if ($('#MultiLocIcon'+i).is(':visible')){
ShowProcess = true
}
if (ShowProcess == true) {
$('#ProcessConfirm').show()
};
};
This obviously wouldn't work because its setting the first variable in the list to "true" and will always read it as true, therefore always showing the image, even if the "!" icon still exists in other rows.
I also tried using .each() to test each rows text color of a specific but also had no luck:
var table = $('#RequestedItemsTable');
table.find('tbody > tr').each(function(){
if $('#Desc').css('color') == '#0000FF'){
//do something
I feel like my experience is letting me down as I still have a lot to learn and have a suspicious feeling that the solution is going to be really easy, but then again, its only easy if you know how.
If anyone could take the time to help me with this problem or offer me any advice, I'd be really grateful.
Here is a section of my code which might be useful:
Modal "Confirm" button:
//CONFIRM Button which will submit final picks.
'Confirm': function() {
//Reset the length loop
length = undefined;
//Remove "Multiple Location" icon from the row.
$('#icon'+id).hide();
//Change text colour back to blue to have visual confirmation that item is ready for picking
$('#Desc'+id).css('color', '#0000FF');
$('#QtyReq'+id).css('color', '#0000FF');
$('#QtyinStock'+id).css('color', '#0000FF');
$(this).dialog('close');
The "!" Icon:
<td id= "MultiLocIcon<?=$i;?>"><?if($row->Count_Location > 1)
{?><img src="<?=base_url();?>public/css/images/error.png" alt="LocPick" title="Multiple Locations" style="cursor: pointer;" id= "icon<?=$i;?>" onClick="$.LocPick(<?=$i;?>);"/><?}?></td>
Basically just need to know how my image can show once the loop checks and knows that the "!" icon is hidden from every possible row.
Thank you for your patience.
You'll need to add a second check in your modal logic, perhaps after your .hide():
//Remove "Multiple Location" icon from the row.
$('#icon'+id).hide();
$('img[id^=icon]:visible').length || $('#ProcessConfirm').show();
What this does is combines the :visible pseudo-selector and a regex selector for all img tags with id starting with "icon". This assumes you won't have any other unrelated image tags with an id like "icon*". If the length is 0, it will go ahead and show the #ProcessConfirm element.
simplest solution I would give is to add a class warning to all the table column which has warning icon & then check for visibility of the same.
if($('.warning:visible').length === 0){
//all warning icons are hidden
}
What I would do is based off your HTML, select all the alert icons, and do a :visible psuedo selector on it. This will return all the visible alert icons, if there are none in the array, you know none of them are visible. You will need to identify them with a class, such as .alert:
if( $(".alert:visible").length === 0 ){
// Do your code in here for no visible alert icons!
}
When user clicks confirm on modal you should run a check on how many icons are still visible, and if the amount is 0 then show the button, like this:
// This searchs for every <td> with an id that contains '#MultiLocIcon'
// Then checks if the amount of those which are visible is 0 and do something
if ( $('td[id*=MultiLocIcon]').not(':visible').length === 0 ) {
$('#ProcessConfirm').show()
}

Categories

Resources