React Autocomplete matching highlighted word - reactjs

I have autocompleted features. when I search for something we will get the data accordingly. But I want to add one more feature like when we search data will come as well as whatever I typed in the search box that character should be highlighted in yellow color.
Here is the piece of code I have written.
import logo from './logo.svg';
import './App.css';
import React, { useState } from "react";
function App() {
const [names, setnames] = useState([{
"name": "Barbara-anne"
}, {
"name": "Debi"
}, {
"name": "Cara"
}, {
"name": "Cristin"
}, {
"name": "Jocelyne"
}, {
"name": "Joellyn"
}, {
"name": "Elmo"
}, {
"name": "Ivette"
}, {
"name": "Lea"
}, {
"name": "Michel"
}, {
"name": "Leigha"
}, {
"name": "Titus"
}, {
"name": "Nollie"
}, {
"name": "Celle"
}, {
"name": "Thea"
}, {
"name": "Brynn"
}, {
"name": "Sloane"
}, {
"name": "Margalo"
}, {
"name": "Genevieve"
}, {
"name": "Niel"
}, {
"name": "Heddi"
}, {
"name": "Gregg"
}, {
"name": "Eduard"
}, {
"name": "Kizzee"
}, {
"name": "Truman"
}, {
"name": "Merill"
}, {
"name": "Lindie"
}, {
"name": "Vasily"
}, {
"name": "Averil"
}, {
"name": "Golda"
}, {
"name": "Zorine"
}, {
"name": "Odele"
}, {
"name": "Amalie"
}, {
"name": "Ilsa"
}, {
"name": "Pepillo"
}, {
"name": "Hewe"
}, {
"name": "Byrann"
}, {
"name": "Alford"
}, {
"name": "Lanny"
}, {
"name": "Kristina"
}, {
"name": "Mar"
}, {
"name": "Vittoria"
}, {
"name": "Winslow"
}, {
"name": "Ashlan"
}, {
"name": "Gayelord"
}])
const [searchTerm, setSearchTerm] = useState('')
const filteredName=names.filter((val)=>{
if(searchTerm ===""){
return val;
}else if(val.name.toLowerCase().includes(searchTerm.toLowerCase())){
return val;
}
});
const renderStatementResult = searchTerm && searchTerm.length > 0;
return (
<>
<div className="srchField">
<label for="statement">Statement Name</label>
<div className="valueField">
<input type="text" name="fileName" id="statement" data-validate="true" placeholder="Type Name" onChange={event => {setSearchTerm(event.target.value)}}/>
{
renderStatementResult ? <ul className="lookup-results">
{filteredName.map((value)=>(<li key={value.name}>{value.name}</li>))}
</ul> : null
}
</div>
</div>
</>
);
}
export default App;
refer this image
Can anyone have an idea of how to match the highlighted text. I want to highlight all s in yellow color

Replace your map function with
filteredName.map((value) => {
let string = value.name.substr(
0,
value.name.toLowerCase().indexOf(searchTerm.toLowerCase())
);
let endString = value.name.substr(
value.name.toLowerCase().indexOf(searchTerm.toLowerCase()) +
searchTerm.length
);
let highlightedText = value.name.substr(
value.name.toLowerCase().indexOf(searchTerm.toLowerCase()),
searchTerm.length
);
return (
<li key={value.name}>
{string}
<span style={{ "background-color": "#FFFF00" }}>
{highlightedText}
</span>
{endString}
</li>
);
})
The first line( let string =...) extracts the part of string which comes before the part that should be highlighted, and the next line extracts the part after the highlight. The search term itself is kept in a span tag which is styled to highlight.
The above snippet only highlights the first occurrence of the search term, so if the search term is 's' and one of the names is 'Samson', only the first 's' would be highlighted. If you want to highlight all occurrences, then you can use regular expressions to find out the indices of all occurrences and loop across the indices, constructing your li tag along the way.
A working sandbox can be found at https://codesandbox.io/embed/elated-chandrasekhar-ggoi1?fontsize=14&hidenavigation=1&theme=dark

Try this
{filteredName.map((value)=>(<li key={value.name}>{
value.name.split('').map((char) => {
if (searchTerm.toLowerCase().split('').includes(char.toLowerCase())) {
return <span style={{ color: 'yellow' }}>{char}</span>;
} else {
return <span>{char}</span>;
}
})
}</li>))}

Related

React rest call map result to selectbox with avatar and label per option

Hi here is rest response:
[
{
"self": "https://your-domain.atlassian.net/rest/api/3/project/EX",
"id": "10000",
"key": "EX",
"name": "Example",
"avatarUrls": {
"48x48": "https://your-domain.atlassian.net/secure/projectavatar?size=large&pid=10000",
"24x24": "https://your-domain.atlassian.net/secure/projectavatar?size=small&pid=10000",
"16x16": "https://your-domain.atlassian.net/secure/projectavatar?size=xsmall&pid=10000",
"32x32": "https://your-domain.atlassian.net/secure/projectavatar?size=medium&pid=10000"
},
"projectCategory": {
"self": "https://your-domain.atlassian.net/rest/api/3/projectCategory/10000",
"id": "10000",
"name": "FIRST",
"description": "First Project Category"
},
"simplified": false,
"style": "classic",
"insight": {
"totalIssueCount": 100,
"lastIssueUpdateTime": "2022-12-08T07:09:19.702+0000"
}
},
{
"self": "https://your-domain.atlassian.net/rest/api/3/project/ABC",
"id": "10001",
"key": "ABC",
"name": "Alphabetical",
"avatarUrls": {
"48x48": "https://your-domain.atlassian.net/secure/projectavatar?size=large&pid=10001",
"24x24": "https://your-domain.atlassian.net/secure/projectavatar?size=small&pid=10001",
"16x16": "https://your-domain.atlassian.net/secure/projectavatar?size=xsmall&pid=10001",
"32x32": "https://your-domain.atlassian.net/secure/projectavatar?size=medium&pid=10001"
},
"projectCategory": {
"self": "https://your-domain.atlassian.net/rest/api/3/projectCategory/10000",
"id": "10000",
"name": "FIRST",
"description": "First Project Category"
},
"simplified": false,
"style": "classic",
"insight": {
"totalIssueCount": 100,
"lastIssueUpdateTime": "2022-12-08T07:09:19.702+0000"
}
}
]
I want to make select bobx having
<option value={data.id}><Img {data.16x16}/>data.label</option>
But result would be all projects if company has multiple projects so select box values have to map or loop into react
<Select options="result">
Im stuck as my code displays only label not any image there.
Another problem is that using data.avatarUrls.16x16 does not compile. VSCode says expecting "," and puts red underline to 16x16
Here is my code a lot is broken here because I have tested a lot ways but no luck
import React, { useState } from 'react';
import Select from 'react-select'
import { components } from 'react-select';
//Kun selectbox
const handleChange = event => {
//console.log(event.target.value);
setSelected(event.target.value);
};
//Palauttaa projectit json taulukon
const getProjects = async () => {
//Matti tähän sitten atlasion cmpany projection haku
const response = await api.asUser().requestJira(route`/rest/api/3/project`, {
headers: {
'Accept': 'application/json'
}
});
const data = await response.json();
//Mapataa hausta tarvittavat tiedot
const result = data.map(function (item, i) {
console.log('test');
return [
{
label: item.name,
value: item.id,
avatar: item.avatarUrls.16x16
}
]
})
return result
}
function Projects() {
//haetaan atlasiansita projectit array
const p = getProjects
//asetetaan state selectbox muutokselle
const [selected, setSelected] = useState(p.id);
return (
<div className='projects'>
<Select
className='select-projects'
options={p}
onChange={handleChange}
/>
</div>
);
}
export default Projects

Creating a map function inside a map in React

I have a JSON array like below. I want to generate dynamic components using the array. I have an array inside the array and need to generate a checkbox dynamically inside a card.
export const DemoProductSpec =
{
"productSpecCharacteristic": [
{
"configurable": false,
"name": "testing",
"productSpecCharacteristicValue": [
{
"value": 2000
},
{
"value": 2002
},
{
"value": 2003
}
],
},
{
"configurable": false,
"name": "testing2",
"productSpecCharacteristicValue": [
{
"value": 20003
},
{
"value": 200233
},
{
"value": 200333
},
{
"value": 20034
}
],
},
]
}
My react code is as below. but this code is not working. first map function is working. but the second map is not working.
{DemoProductSpec.productSpecCharacteristic.map((Characteristic) => (
<Card
title={Characteristic.name}
bordered={true}
size="small"
extra={
<Radio.Group onChange={onChange} value={value}>
<Radio value={1}>Deafult</Radio>
<Radio value={2}>Overriden</Radio>
</Radio.Group>
}
>
{" "}
<span>Values: </span>
{Characteristic.productSpecCharacteristicValue.map((Values) => (<Checkbox>{Values.value}</Checkbox>))}
</Card>
))
}

How do I sort this array by date?

I'm trying to sort the dates from this external API in my latestResults array by latest on top to oldest on bottom but can't seem to figure out how.
Right now they're displayed with the oldest date first and it's working fine, but it's in the wrong order for me.
I tried using result in latestResults.reverse() but that just reverses the 7 items currently in the array.
HTML:
<div v-for="result in latestResults" v-bind:key="result.latestResults">
<small">{{ result.utcDate }}</small>
</div>
Script:
<script>
import api from '../api'
export default {
data () {
return {
latestResults: [],
limit: 7,
busy: false,
loader: false,
}
},
methods: {
loadMore() {
this.loader = true;
this.busy = true;
api.get('competitions/PL/matches?status=FINISHED')
.then(response => { const append = response.data.matches.slice(
this.latestResults.length,
this.latestResults.length + this.limit,
this.latestResults.sort((b, a) => {
return new Date(b.utcDate) - new Date(a.utcDate);
})
);
setTimeout(() => {
this.latestResults = this.latestResults.concat(append);
this.busy = false;
this.loader = false;
}, 500);
});
}
},
created() {
this.loadMore();
}
}
</script>
The JSON where I'm getting matches like this that has utcDate:
{
"count": 205,
"filters": {
"status": [
"FINISHED"
]
},
"competition": {
"id": 2021,
"area": {
"id": 2072,
"name": "England"
},
"name": "Premier League",
"code": "PL",
"plan": "TIER_ONE",
"lastUpdated": "2021-02-01T16:20:10Z"
},
"matches": [
{
"id": 303759,
"season": {
"id": 619,
"startDate": "2020-09-12",
"endDate": "2021-05-23",
"currentMatchday": 22
},
"utcDate": "2020-09-12T11:30:00Z",
"status": "FINISHED",
"matchday": 1,
"stage": "REGULAR_SEASON",
"group": "Regular Season",
"lastUpdated": "2020-09-13T00:08:13Z",
"odds": {
"msg": "Activate Odds-Package in User-Panel to retrieve odds."
},
},

How to search and filter in array of objects on setState

I'm trying to create a search based on an array of objects with react which data is in this format:
const data = [
{"category 1" : [
{
"name": "Orange",
"desc": "juice, orange, Water"
},
{
"name": "Ananas",
"desc": "juice, ananas, water"
}
]
},
{"category 2" : [
{
"name": "Banana Split",
"desc": "Banana, ice cream, chocolat, topping",
"allergens": "nuts"
},
{
"name": "Mango Sticky Rice",
"desc": "Mango, rice, milk",
"allergens": ""
}
]
}
]
I stored this data inside useState declaration to be able to render accordingly on data chnage:
const [filteredBySearch, setFilteredBySearch] = useState(data)
I have an input where we can type anything and set inside useState declaration.
Goal:
If I type in my input:
"Jui"
Output should be:
console.log(filteredBySearch)
/* output:
[
{"category 1" : [
{
"name": "Orange",
"desc": "juice, orange, Water"
},
{
"name": "Ananas",
"desc": "juice, ananas, water"
}
]
},
{"category 2" : []
}
]*/
Exemple 2:
If I type in my input:
"Orange banana"
Output should be:
console.log(filteredBySearch)
/* output: [
{"category 1" : [
{
"name": "Orange",
"desc": "juice, orange, Water"
}
]
},
{"category 2" : [
{
"name": "Banana Split",
"desc": "Banana, ice cream, chocolat, topping",
"allergens": "nuts"
}
]
}
]*/
I've try creating a new object with map and filter and set it with setFilteredBySearch, but I can't get anything, even creating this new object.
This the full component:
import Card from '../components/Card'
import React, { useState } from 'react';
export default function IndexPage({ data, search }) {
//search is the result of input value set on a useState
//Filter categoriesFoods by search
const [FilteredBySearch, setFilteredBySearch] = useState(data)
return (
<div className="main-content">
<div className="card-container">
{
FilteredBySearch.map(function(el, i) {
return (
<div key={i}>
<h2 className="category" id={Object.keys(el)}>{Object.keys(el)}</h2>
{
el[Object.keys(el)].map (function(itm,index){
return <Card key={index} infoItem={itm}/>
})
}
</div>
)
})
}
</div>
<style jsx>{`...`}</style>
</div>
)}
Any idea for me ?
Thanks a lot for your guidance!
I think this is what you are looking for. I have created below utilities for filtering as per your requirement.
const dataObj = [
{
'category 1': [
{
name: 'Orange',
desc: 'juice, orange, Water',
},
{
name: 'Ananas',
desc: 'juice, ananas, water',
},
],
},
{
'category 2': [
{
name: 'Banana Split',
desc: 'Banana, ice cream, chocolat, topping',
allergens: 'nuts',
},
{
name: 'Mango Sticky Rice',
desc: 'Mango, rice, milk',
allergens: '',
},
],
},
]
const checkIfInputMatches = (input, desc) => input.toLowerCase().split(" ").some(o => desc.toLowerCase().includes(o))
const filterByInput = (data, input) => {
let finalResult = [];
data.forEach(d => {
let keys = Object.keys(d);
let values = Object.values(d);
finalResult = [...finalResult, ...values.map((obj, index) => {
let result = obj.filter(o => checkIfInputMatches(input, o.desc))
return {[keys[index]]: result}
})]
})
return finalResult
}
console.log(filterByInput(dataObj, 'JUI'))
console.log(filterByInput(dataObj, "orange"))
console.log(filterByInput(dataObj, "rice"))
console.log(filterByInput(dataObj, "Orange banana"))
Hope this helps.

Number input within a React Mapped Array

I'm currently working on my first react App and I'm having a little trouble with a product list that I'm trying to create.
Essentially I'm trying to create a list (parsed into react from a JSON file) which will enable the user to select the number of products that they want.
However the quantity selector that I have created is updating all of the iterations of the mapped array rather than just the one that the selector is inside of. How can I adjust the code so that only one counter updates at a time?
My Code is as follows
import React, { Component } from "react";
import "./App.css";
import productdata from "./catalog.json";
class App extends Component {
constructor(props) {
super(props);
this.state = {
BundleVal: "",
ProductVal:"",
CounterVal: 0
}
}
updateBundle = (val) => {
this.setState({
BundleVal: val
})
};
updateProduct = (val) => {
this.setState({
ProductVal: val
})
};
counterincrease = (val) => {
this.setState({
CounterVal: this.state.CounterVal + 1
})
};
counterdecrease = (val) => {
this.setState({
CounterVal: this.state.CounterVal - 1
})
};
render() {
const BundleProducts = [].concat(productdata.data.products).map((item, i) =>
<div key={item.id}>
{item.id} <br />
{item.name} <br />
{item.description} <br />
Installation: {item.price.installation} <br />
Monthly: {item.price.recurring} <br />
{this.state.CounterVal}
<button onClick={this.counterincrease}>+</button>
<button onClick={this.counterdecrease}>-</button>
</div>
);
let bname = null;
if (this.state.BundleVal === "1") {
bname = "Bundle 1";
}
else if (this.state.BundleVal === "2") {
bname = "Bundle 2";
}
else if (this.state.BundleVal === "3") {
bname = "Bundle 3";
}
else if (this.state.BundleVal === "4") {
bname = "Bundle 4";
}
else {bname = null;}
return (
<div>
<h2>Order</h2>
Bundle Id: {this.state.BundleVal}
<br/>
Chosen Bundle: {bname}
<br/>
Number of Products: {this.state.ProductVal}
<br/>
<Bundle updateBundle={this.updateBundle} />
{BundleProducts}
</div>
)
}
}
class Bundle extends React.Component {
constructor(props) {
super(props);
this.state = {
BundleVal: ""
}
}
updatebundle = (e) => {
this.props.updateBundle(e.target.value);
this.setState({BundleVal: e.target.value});
};
render() {
return (
<div>
<h4>Bundle</h4>
<input
type="radio"
value="1"
onChange={this.updatebundle}
checked={this.state.BundleVal==='1'}
/> Bundle 1
<input
type="radio"
value="2"
onChange={this.updatebundle}
checked={this.state.BundleVal==='2'}
/> Bundle 2
</div>
)
}
}
export default App;
Some code from my JSON file is included below
{
"timestamp": 1502121471,
"data": {
"adverts": [],
"bundles": [{
"id": "1",
"name": "Bundle 1",
"description": "Bundle 1 Description",
"maximumPeripherals": 32,
"available": true,
"count": 0,
"price": {
"installation": "99.99",
"recurring": "23.99"
},
"image": {
"file": "bundle-one.png",
},
"products": ["1", "2", "3"]
}, {
"id": "2",
"name": "Bundle 2",
"description": "Bundle 2 Description",
"maximumPeripherals": 32,
"available": true,
"count": 0,
"price": {
"installation": "99.99",
"recurring": "23.99"
},
"image": {
"file": "bundle-two.png",
},
"products": ["1", "2", "2", "2", "2"]
}],
"products": [{
"id": "1",
"name": "Product 1",
"description": "Product 1 Description",
"maximumQuantity": 1,
"isPeripheral": false,
"isAvailable": true,
"price": {
"upfront": null,
"installation": "0.00",
"recurring": "0.00"
},
"image": {
"file": "product-one.png",
}
}, {
"id": "2",
"name": "Product 2",
"description": "Product 2 Description",
"maximumQuantity": null,
"isPeripheral": true,
"isAvailable": true,
"count": 0,
"price": {
"upfront": "60.00",
"installation": "9.60",
"recurring": "1.25"
},
"image": {
"file": "product-two.png",
}
}, {
"id": "3",
"name": "Product Three",
"description": "Product Three Description",
"maximumQuantity": null,
"isPeripheral": true,
"isAvailable": true,
"count": 0,
"price": {
"upfront": "132.00",
"installation": "9.60",
"recurring": "2.75"
},
"image": {
"file": "product-three.png",
}
}]
}
}
A friend of mine suggested the following to answer this question. Essentially the array will now loop through the values and add a different id will be applied to each input.
updateQuantity = (e) => {
var pCounter = this.state.ProductCounter;
var el = parseInt(e.target.id.split("_")[1], 10);
pCounter[el] = parseInt(e.target.value, 10);
this.setState({ProductCounter: pCounter});
console.log(this.state.ProductCounter);
};
render() {
const BundleProducts = [].concat(productdata.data.products).map((item, i) =>
<div key={item.id}>
{item.id} <br />
{item.name} <br />
{item.description} <br />
Installation: {item.price.installation} <br />
Monthly: {item.price.recurring} <br />
<input
type="number"
onChange={this.updateQuantity}
value={this.state.ProductCounter[item.id] || 0}
id={"product_"+item.id}
/><br />
{this.state.ProductCounter[item.id] || 0}
<hr />
</div>
);

Resources