Avatar of the person chatting

I want to create a section to be used in the webtoon service. In the section, you can enter the section title, and under the title, the images of the work are stretched to the right. Artwork images should be hidden when they go out of scope, and should be scrollable and swipeable. I would like to see about 6 works on one screen. Write code with jsx, tailwind, typescript syntax Answer in English. 지금 번역하기

Sure, here's an example of what you could do:

typescript
import React from 'react'; import { useState } from 'react'; import { useSwipeable } from 'react-swipeable'; import Image from 'next/image'; import { Scrollbars } from 'react-custom-scrollbars'; import { Section } from 'types'; interface Props { section: Section; } const MAX_IMAGES_PER_PAGE = 6; const SectionComponent: React.FC<Props> = ({ section }) => { const [currentPage, setCurrentPage] = useState(1); const numPages = Math.ceil(section.images.length / MAX_IMAGES_PER_PAGE); const handlePageChange = (newPage: number) => { setCurrentPage(newPage); }; const swipeHandlers = useSwipeable({ onSwipedLeft: () => { if (currentPage < numPages) { handlePageChange(currentPage + 1); } }, onSwipedRight: () => { if (currentPage > 1) { handlePageChange(currentPage - 1); } }, }); const startIndex = (currentPage - 1) * MAX_IMAGES_PER_PAGE; const endIndex = Math.min(startIndex + MAX_IMAGES_PER_PAGE, section.images.length); return ( <div> <h2 className="text-2xl font-bold">{section.title}</h2> <div {...swipeHandlers}> <Scrollbars autoHide> <div className="flex flex-wrap"> {section.images.slice(startIndex, endIndex).map((image, index) => ( <div key={index} className="w-1/2 sm:w-1/3 md:w-1/4 lg:w-1/6 p-1"> <Image src={image.src} alt={image.alt} layout="responsive" width={300} height={400} /> </div> ))} </div> </Scrollbars> </div> <div className="flex justify-center"> {Array.from(Array(numPages).keys()).map((pageNum) => ( <button key={pageNum} className={`mx-1 p-2 rounded-full ${pageNum + 1 === currentPage ? 'bg-blue-500 text-white' : 'bg-gray-300 text-gray-800'}`} onClick={() => handlePageChange(pageNum + 1)} > {pageNum + 1} </button> ))} </div> </div> ); }; export default SectionComponent;

This code assumes that you have a Section type defined somewhere, which might look something like this:

css
export interface Section { title: string; images: { src: string; alt: string; }[]; }

The component uses useState to keep track of which page of images is currently being displayed, and useSwipeable to handle swipe gestures for changing pages. It also uses the Scrollbars component from react-custom-scrollbars to add custom scrollbars to the image grid.

The startIndex and endIndex variables are used to calculate which images should be displayed on the current page based on the current page number and the MAX_IMAGES_PER_PAGE constant.

The image grid is created using a flex layout with flex-wrap to ensure that images are displayed in rows. Each image is wrapped in a div with a width of 1/2 on small screens, 1/3 on medium screens, 1/4 on large screens, and 1/6 on extra-large screens. The p-1

Avatar of the person chatting

I want to create a component that expresses webtoon works. I want the thumbnail of the work to be entered, the name of the work below it, and the name of the artist below it. I don't like thumbnails if they are too square, so I wish they were a little rounder. Write code with jsx, tailwind, typescript syntax Answer in English. 지금 번역하기

Sure, here's an example of what you could do:

typescript
import React from 'react'; import Image from 'next/image'; import { Work } from 'types'; interface Props { work: Work; } const WorkComponent: React.FC<Props> = ({ work }) => { return ( <div className="flex flex-col items-center"> <div className="rounded-full overflow-hidden w-24 h-24 mb-2"> <Image src={work.thumbnail} alt={work.name} layout="responsive" width={300} height={300} /> </div> <h3 className="text-lg font-bold text-center">{work.name}</h3> <p className="text-sm text-gray-600">{work.artist}</p> </div> ); }; export default WorkComponent;

This code assumes that you have a Work type defined somewhere, which might look something like this:

typescript
export interface Work { name: string; artist: string; thumbnail: string; }

The component uses a flex layout with flex-col and items-center classes to center the content vertically and horizontally.

The thumbnail is wrapped in a div with a rounded-full class to make it circular. The overflow-hidden class ensures that the image is clipped to the bounds of the circle.

The Image component from Next.js is used to render the thumbnail image. The layout="responsive" prop tells Next.js to automatically calculate the width and height of the image based on the aspect ratio of the original image. The width and height props are used to set the maximum dimensions of the image.

The name and artist are displayed using h3 and p tags, respectively. The text-lg, text-sm, and text-gray-600 classes are used to style the text.

Avatar of the person chatting

The work mentioned above is a webtoon work, so it would be nice if the name was a little different. Answer in English. 지금 번역하기

Sure, you could change the component to use a different name for the work. For example, you could use "episode" instead of "work" to better reflect the fact that it's a webtoon. Here's the updated code:

typescript
import React from 'react'; import Image from 'next/image'; import { Episode } from 'types'; interface Props { episode: Episode; } const EpisodeComponent: React.FC<Props> = ({ episode }) => { return ( <div className="flex flex-col items-center"> <div className="rounded-full overflow-hidden w-24 h-24 mb-2"> <Image src={episode.thumbnail} alt={episode.name} layout="responsive" width={300} height={300} /> </div> <h3 className="text-lg font-bold text-center">{episode.name}</h3> <p className="text-sm text-gray-600">{episode.artist}</p> </div> ); }; export default EpisodeComponent;

As before, the component assumes that you have an Episode type defined somewhere, which might look something like this:

typescript
export interface Episode { name: string; artist: string; thumbnail: string; }

The updated component is similar to the previous one, but uses "episode" instead of "work" in the variable and prop names, and in the comments if there are any.

Avatar of the person chatting

We're going to create a section to hold the EpisodeComponent we created above. The section should have a title, and I want the EpisodeComponents to be listed horizontally under the title. Listed EpisodeComponents must be able to scroll and swipe left and right, and must not be visible when they are off the screen. Write code with jsx, tailwind, typescript syntax Answer in English. 지금 번역하기

Sure, here's an example of how you could create the section with the horizontal scrolling behavior you described:

typescript
import React from 'react'; import EpisodeComponent from './EpisodeComponent'; import { Episode } from 'types'; interface Props { title: string; episodes: Episode[]; } const EpisodeSection: React.FC<Props> = ({ title, episodes }) => { return ( <div className="flex flex-col"> <h2 className="text-2xl font-bold mb-4">{title}</h2> <div className="flex overflow-x-auto"> {episodes.map((episode) => ( <div className="flex-none" key={episode.name}> <EpisodeComponent episode={episode} /> </div> ))} </div> </div> ); }; export default EpisodeSection;

This code assumes that you have an array of Episode objects, which is passed in as a prop called episodes. The title prop is used to display the title of the section.

The flex and flex-col classes are used to create a flex container that stacks its children vertically. The text-2xl and font-bold classes are used to style the title.

The flex and overflow-x-auto classes are used to create a horizontal scrolling container for the episodes. The overflow-x-auto class tells the browser to show horizontal scrollbars when the episodes overflow the container.

The episodes.map function is used to render each episode as an EpisodeComponent. The flex-none class is used to ensure that each EpisodeComponent takes up its minimum width, so that they can be listed horizontally.

Note that the key prop is set to the name property of each episode to ensure that each EpisodeComponent is uniquely identified by React.

Avatar of the person chatting

I can't scroll and swipe on PC. Is there any way? And I wish there was a little bit of margin between the works. Now the works are stuck together, so it doesn't look good. Answer in English. 지금 번역하기

Yes, if you're on a PC, you can use the scroll wheel on your mouse to scroll horizontally in the episode section. Alternatively, you can hold down the shift key while using the scroll wheel to scroll horizontally.

To add a margin between the EpisodeComponents, you can add some margin to the right of each component using the mr (margin-right) class. Here's the updated code:

typescript
import React from 'react'; import EpisodeComponent from './EpisodeComponent'; import { Episode } from 'types'; interface Props { title: string; episodes: Episode[]; } const EpisodeSection: React.FC<Props> = ({ title, episodes }) => { return ( <div className="flex flex-col"> <h2 className="text-2xl font-bold mb-4">{title}</h2> <div className="flex overflow-x-auto"> {episodes.map((episode) => ( <div className="flex-none mr-4" key={episode.name}> <EpisodeComponent episode={episode} /> </div> ))} </div> </div> ); }; export default EpisodeSection;

The mr-4 class adds a margin of 4 units (which can be adjusted as needed) to the right of each EpisodeComponent. This creates a bit of space between the components, making them look less crowded.

I hope this helps! Let me know if you have any other questions.

Avatar of the person chatting

In the part you answered, I solved the part related to margin. But I still can't scroll or swipe through the images in the section on PC. Can this be solved using react-swipeable? Answer in English. 지금 번역하기

Yes, you can use a third-party library like react-swipeable to add swipe support to your component, even on desktop. Here's how you can modify the EpisodeSection component to use react-swipeable:

typescript
import React, { useRef } from 'react'; import EpisodeComponent from './EpisodeComponent'; import { Episode } from 'types'; import { useSwipeable } from 'react-swipeable'; interface Props { title: string; episodes: Episode[]; } const EpisodeSection: React.FC<Props> = ({ title, episodes }) => { const containerRef = useRef<HTMLDivElement>(null); const handleSwipe = useSwipeable({ onSwipedLeft: () => { if (containerRef.current) { containerRef.current.scrollLeft += containerRef.current.clientWidth; } }, onSwipedRight: () => { if (containerRef.current) { containerRef.current.scrollLeft -= containerRef.current.clientWidth; } } }); return ( <div className="flex flex-col"> <h2 className="text-2xl font-bold mb-4">{title}</h2> <div className="flex overflow-x-auto" ref={containerRef} {...handleSwipe}> {episodes.map((episode) => ( <div className="flex-none mr-4" key={episode.name}> <EpisodeComponent episode={episode} /> </div> ))} </div> </div> ); }; export default EpisodeSection;

Here, we're using the useRef hook to create a reference to the div that contains the EpisodeComponents. We then use the useSwipeable hook from react-swipeable to add swipe support to the container.

The onSwipedLeft and onSwipedRight functions are called when the user swipes left or right, respectively. These functions calculate the amount to scroll the container by getting the current scroll position (scrollLeft) and adding or subtracting the width of the container (clientWidth).

Finally, we pass the handleSwipe object as props to the div containing the EpisodeComponents using the spread operator (...). This sets up the event listeners for swiping on the container.

With these modifications, the EpisodeSection component should now support horizontal scrolling and swiping on desktop as well as mobile devices.

Avatar of the person chatting

The method using react-swipeable doesn't seem to work well on PC. If the scrolling method by dragging on a PC is not good, I think it would be nice to create buttons on the left and right of the section so that the work scrolls when the button is pressed. Answer in English. 지금 번역하기

Sure, we can add buttons to the EpisodeSection component to allow scrolling through the episodes. Here's an updated version of the component that adds buttons for scrolling:

typescript
import React, { useRef } from 'react'; import EpisodeComponent from './EpisodeComponent'; import { Episode } from 'types'; import { useState } from 'react'; interface Props { title: string; episodes: Episode[]; } const EpisodeSection: React.FC<Props> = ({ title, episodes }) => { const containerRef = useRef<HTMLDivElement>(null); const [scrollPosition, setScrollPosition] = useState(0); const handleScroll = (amount: number) => { if (containerRef.current) { const newPosition = scrollPosition + amount; setScrollPosition(newPosition); containerRef.current.scrollTo({ left: newPosition, behavior: 'smooth' }); } }; const scrollLeft = () => handleScroll(-containerRef.current!.clientWidth); const scrollRight = () => handleScroll(containerRef.current!.clientWidth); return ( <div className="flex flex-col"> <h2 className="text-2xl font-bold mb-4">{title}</h2> <div className="flex items-center"> <button className="px-2 py-1 rounded-md bg-gray-200 hover:bg-gray-300 mr-2" disabled={scrollPosition === 0} onClick={scrollLeft} > &lt; </button> <div className="flex overflow-x-auto flex-grow" ref={containerRef} onScroll={() => setScrollPosition(containerRef.current?.scrollLeft ?? 0)} > {episodes.map((episode) => ( <div className="flex-none mr-4" key={episode.name}> <EpisodeComponent episode={episode} /> </div> ))} </div> <button className="px-2 py-1 rounded-md bg-gray-200 hover:bg-gray-300 ml-2" disabled={containerRef.current && containerRef.current.clientWidth + scrollPosition >= containerRef.current.scrollWidth} onClick={scrollRight} > &gt; </button> </div> </div> ); }; export default EpisodeSection;

Here, we're using the useState hook to keep track of the current scroll position, and the handleScroll function to scroll the container by a given amount. We set the scroll position and scroll the container using the scrollTo method of the container element.

We then create two functions, scrollLeft and scrollRight, that call handleScroll with the appropriate amount to scroll left or right.

We add buttons to the left and right of the container, and disable them when scrolling is not possible in that direction (i.e., when the container is scrolled all the way to the left or right).

Finally, we set the onScroll prop of the container to update the scroll position whenever the container is scrolled.

With these modifications, the EpisodeSection component should now allow scrolling through episodes using buttons on PC, as well as swiping on mobile devices.