Limit the number of results for autocomplete component? - reactjs

I'm using React JS and I've created an autocomplete component that returns a list of results.
Here's my code for the autocomplete :
const [pokedexMatch, setPokedexMatch] = useState([]);
const searchPokedex = (text) => {
if (!text) {
setPokedexMatch([]);
} else {
let matches = pokedex.filter((pokedex) => {
const regex = new RegExp(`${text}`, "gi");
return pokedex?.name?.match(regex);
});
setPokedexMatch(matches);
}
};
Here's a working sandbox with some dummy text below. As you can see, when you type a single letter it returns a lot of results and move the text below the results.
What I'd like to do is limit the number of results to 5 and make the list of results go above the text (I guess that I should use absolute positioning but I'd like to know if there is another method without absolute positioning) ?

To limit your results to 5, use simple Javascript!
setPokedexMatch(matches.slice(0, 5));
For the positioning part of your question, you're on the right track - you can use absolute positioning of a div with a white background to make the results appear to be "above" the text.
EDIT: I've updated your sandbox here and made some very minor changes.
When you map over an array, make sure you include a key prop. Read more here.
I added the slice method to limit your results to 5
I added a simple above class to the parent <ul /> element to make the results appear "above" the text. This is just one way to accomplish this, I'll leave it to you to style it better if you'd like.

Related

add line break after certain amount of characters in JSX

I want to render some music data. I am saving the data i get back from an axios request into
an object called dailyPlaylist and destructure the values i need.
Since the artists and the name take a lot of space, i want to do a line break.
The artists come back as an array, so i could do:
artists: track.artists.slice(0, 5).map(artist => <p>{artist.name}</p>)
this works perfectly fine.
But now I am trying to do a line break also on the "name" property since some of the names are really long.
Is there an option to set a line break after a certain amount of characters?? I tried many methods I would use in vanilla JS but nothing worked.
For example i want to change the long name:
Dreamers (Music from the FIFA World Cup)
to:
Dreamers (Music from the <br>
FIFA World Cup)
or something like that.
My code so far:
const dailyPlaylist = {
tracks: response.data.tracks.items.map(({ track }) => ({
key: track.id,
name: track.album.name,
artists: track.artists.slice(0, 5).map(artist => <p>{artist.name}</p>), // line break between artists
image: track.album.images[1].url
}))
}
Thanks in advance
Unless there is a good reason why you want to achieve this using Javascript, I would do it with CSS instead.
Limit the width of the div or span containing the title. You could use max-width for example.
Something like this:
<span style={{maxWidth:100}} >
<p>{artist.name}</p>
</span>

How to dynamically populate tailwindcss grid-cols-x property?

I am currently working on a page using NextJs and TailwindCss. The user has the ability of loading an image locally and setting the number of pieces horiz/vert (rows/cols) that they wish the image to be split in. To properly display this, I need to set the grid to the proper number of columns in the parent element.
I have an API I call locally that uses sharp to perform the split and calculate the width and height and I sort the images in my array so that they are in order since it is async. I then also generate a dynamic class string that just populates the proper number of columns for later assignment to my parent grid elements class.
CLASS STRING POPULATION
const gridClass = `grid grid-cols-${numCols} gap-2 pt-2`;
//*** At this point, the value of gridClass, if columns were set to 3, using template literals is :
'grid grid-cols-3 gap-2 pt-2'
//The proper string is now populated and passed back in the API response via classCss key
res.status(200).json({ msg: 'Success splitting', tileData: tiles, classCss: gridClass})
PAGE SNIPPET:
<div id="final">
<div className={tileCss} > //<--This is where I pull in the generated class string
{
imageData.map((nft, i)=>(
<div key={i} className='border shadow rounded-x1 overflow-hidden'>
<Image src={nft.imgSrc} alt="image" layout="responsive" width={nft.tileDimX} height={nft.tileDimY}/>
</div>
))
}
</div>
</div>
This sometimes works but other times it doesn't. Usually if I set the columns to 3, it seems to work properly, but if I set it to 5 lets say, regardless of the input image size, it just puts them all in a single column with large images. Oddly however, the parent grid class on the page is correct, it just seems that it isn't adhered to. I will provide some snapshots below to show what I'm talking about. I've been trying to figure this out for a couple days, however I haven't had luck and since I'm new to NextJs I thought I would share here and see if I'm just doing something stupid. Thanks!
The below results also don't seem to care if the viewing window is stretched wide or reduced in size. I just took the snapshots below so that you could see what was happening in a smaller viewing window.
This is the expected result where the image columns should match the columns entered by the user:
Notice how the css class shows up under styles as well:
This is the improper result, where the user selected 5 columns, the image was split into the correct number of columns, but the display of this in the front end grid does not follow the css.
As you can see grid-cols-5 is correct from a class standpoint, but the viewed result doesn't adhere to this.
Grid-cols-5 is in html class but missing under styles applied:
So I finally found the source to the issue. It seems that tailwindcss performs an optimization on classes brought in and if you are not using the class anywhere in your html, it doesn't include those classes. So I not only needed to return my dynamic string with the expected class, but I also needed to add all the classes I would possibly be using into my tailwind.config.js file under the safelist key.
So my config now looks like this and it is working as expected:
module.exports = {
content: [
'./pages/**/*.{js,ts,jsx,tsx}',
],
safelist: [
{
pattern: /grid-cols-./,
}
],
theme: {
extend: {},
},
plugins: [],
}
More information can be found here:
https://tailwindcss.com/docs/content-configuration#using-regular-expressions
Thanks again Mohit for your assistance.
One of the problem I noticed is in grid-cols-${numCols} in the line
const gridClass = `grid grid-cols-${numCols} gap-2 pt-2`;
TailwindCSS doesn't allow you to generate classes dynamically. So when you use the following to generate the class… grid-cols-${numCols} as a string.
…TailwindCSS will not pick that up as a valid TailwindCSS class and therefore will not produce the necessary CSS.
You can use the function from where you are getting numCols and instead of returning the value of numCols, simply return grid-cols-${numCols}.
Suppose let say your function be getNumofCols(), then modify it like this
function getNumofCols() {
...
...
...
...
...
return "grid-cols-" + numCols ;
}
So that it returns the complete string .
And use it like again
const gridClass = `grid ${getNumofCols()} gap-2 pt-2`;
If your function uses any parameter then you can create a new function and call this function and just add grid-cols- to the return value.
By doing it this way, the entire string for every class is in your source code, so Tailwind will know to generate the applicable CSS.

how to set a text for a group in vis.js using groupTemplate

I have an application and using vis.js to build timeline events.
I'm using React.
I want to add an icon near each event, so I using groupTemplate and I want to render there a component with group text and icon.
in the options I add:
groupTemplate: (item, element) => {
if (!item) return;
ReactDOM.render(<TimelineGroupComp item={item}/>, element);
}
TimelineGroupComp component render function:
return (
<div className="primary-wrapper" key={this.props.item.content}>
<div style="background-image:url(../images/myIcon.svg);"/>
</div>
);
I get the correct Icon but the text is the item.id instead item.content.
I try to add a span with the correct text in the TimelineGroupComp but it renders also the item.id, not only the correct text.
Also I have another problem: when I collapse/expaned the group, I have an error in the console:
Warning: render(...): It looks like the React-rendered content of this
container was removed without using React. This is not supported and will
cause errors. Instead, call ReactDOM.unmountComponentAtNode to empty a
container.
and the group is render normal, ignored my component.
I know this post is almost a year old, but I thought I'd post the answer to this question anyways for anyone who gets the same issue in the future. I've had the same issue and this is how I fixed it.
Starting with React 16, if you want to use React templates in Vis Timeline you'll have to use React.createPortal in the groupTemplate option, see https://jsfiddle.net/api/post/library/pure/.
For the code above, the correct groupTemplate would be:
groupTemplate: (item, element) => {
if (!item) return;
ReactDOM.createPortal(
ReactDOM.render(<TimelineGroupComp item={item}/>, element),
element
)
}

React-slick Slider stays on same page if slides items are changed

I am stuck with an issue with react-slick. The subject line may not make sense, but I will try to explain the scenario in detail. See this example fiddle to see the issue in action.
var DemoSlider = React.createClass({
getSlides(count) {
var slides = [];
for(var i = 0; i < count; i++) {
slides.push((<img key={i} src='http://placekitten.com/g/400/200' />));
}
return slides;
},
render: function() {
var settings = {
dots: false,
infinite: false,
slidesToShow: 3,
slidesToScroll: 3
}
var slides = this.getSlides(this.props.count);
return (
<div className='container'>
<Slider {...settings}>
{ slides }
</Slider>
</div>
);
}
});
In this demo, initially the slider shows 20 slides (3 per page). The idea is that if you click the button, it will generate a random number, which would be the new number of slides. The code is fairly simple and self-explanatory.
To reproduce the problem,
1. keep on clicking Next arrow until you reach the last slide.
2. click on the button that says 'Click' to generate a new random number of slides.
My expectation was that the slide will go back to the first slide but not to stay on the page where it previously was. Even worse, if the new number of slides is lower than the previous number, the user will see a blank page with no slides. On clicking Previous error, he/she can go to the previous slides, and everything works normally but the initial display ruins the user experience.
Is there something I am missing to add in the code, or is this a bug?
Thanks,
Abhi.
I would say it is rather a buggy behavior, but still you can achieve what you want by forcing a redraw after new collection has been passed as props, by resetting react's key:
<DemoSlider key={Date.now()} count={this.state.count}/>
UPDATED FIDDLE
Quick workaround: When you change to a new set of slides, remove the component, and reconstruct it again. It would then start with the first slide. You can do this by incrementing the key attribute of DemoSlider.
For a proper fix, you'd need to tell the component to change the 'current slide', so that it's not looking at a slide index beyond the end, but a casual look at the source at https://cdnjs.cloudflare.com/ajax/libs/react-slick/0.11.0/react-slick.js suggests it currently does not allow this. A change would need to be made to InnerSlider.componentWillReceiveProps() to check state.currentSlide against props.slidesToShow and state.slideCount.
It would also benefit from allowing currentSlide to be passed as props.

How to use .NET TextBoxRenderer with TextBoxState.Hot to draw a hot text box?

i am trying to use TextBoxRenderer to render a "hot" text box:
TextBoxRenderer.DrawTextBox(e.Graphics, rectangle, TextBoxState.Hot);
except that it doesn't work, it doesn't render the text box as hot.
TextBoxState.Selected doesn't render as selected
TextBoxState.Hot doesn't render as hot
How do i make TextBoxRenderer.DrawTextBox(..., Hot) render as Hot?
Related but different question:
How do i make TextBoxRenderer.DrawTextBox(..., Selected) render as Selected?
It seems that TextBoxRenderer uses EP_BACKGROUNDWITHBORDER, whereas EP_EDITBORDER_NOSCROLL is typically used by TextBox controls[1].
if (VisualStyleRenderer.IsSupported)
{
// Use the text control's focus rectangle.
// EP_EDITBORDER_NOSCROLL, EPSN_FOCUSED
VisualStyleElement element = VisualStyleElement.CreateElement("EDIT", 6, 3);
if (VisualStyleRenderer.IsElementDefined(element))
{
VisualStyleRenderer renderer = new VisualStyleRenderer(element);
renderer.DrawBackground(e.Graphics, ClientRectangle);
}
}
(It's tempting to try to get the element from VisualStyleElement but there's no nested class for EP_EDITBORDER_NOSCROLL. So numeric constants 6 and 3 it is.)

Resources