Merge pull request #2 from Trendyol/develop

Add scrolling carousel
master
hasan genc 5 years ago committed by GitHub
commit a9ddce292e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      README.md
  2. 10
      __tests__/helpers.spec.tsx
  3. 1
      jest.config.js
  4. 4
      src/components/carousel/index.tsx
  5. 155
      src/components/scrolling-carousel/index.tsx
  6. 10
      src/helpers/index.ts
  7. 3
      src/index.ts
  8. 20
      src/styles/slider/styles.module.css
  9. 8
      src/styles/slider/styles.module.css.d.ts
  10. 49
      website/docs/scrollingCarousel.md
  11. 17
      website/docs/scrollingCarouselApi.md
  12. 4
      website/sidebars.js
  13. 61
      website/src/css/custom.css
  14. 140
      website/src/pages/index.js

@ -33,5 +33,5 @@ ReactDOM.render(
``` ```
# License # License
Distributed under the MIT License. See `LICENSE` for more information.
Distributed under the MIT License. See `LICENSE` for more information.

@ -2,6 +2,7 @@ import React, { MouseEvent, TouchEvent } from 'react';
import * as helpers from '../src/helpers'; import * as helpers from '../src/helpers';
import { carouselItemNodes } from './__fixtures__/nodes'; import { carouselItemNodes } from './__fixtures__/nodes';
import { SlideDirection } from '../src/types/carousel'; import { SlideDirection } from '../src/types/carousel';
import { getOuterWidth } from '../src/helpers';
describe('helpers', () => { describe('helpers', () => {
it('should return to head of circular items list', async () => { it('should return to head of circular items list', async () => {
@ -67,4 +68,13 @@ describe('helpers', () => {
expect(result).toEqual(pageX); expect(result).toEqual(pageX);
}); });
it('should return width of element', async () => {
const width = 30;
const element = document.createElement('div');
Object.defineProperty(element, 'offsetWidth', { value: width });
const result = getOuterWidth(element);
expect(result).toEqual(width);
});
}); });

@ -10,6 +10,7 @@ module.exports = {
statements: 100, statements: 100,
}, },
}, },
coveragePathIgnorePatterns: ['<rootDir>/src/components/scrolling-carousel'],
testPathIgnorePatterns: ['<rootDir>/__tests__/__fixtures__/'], testPathIgnorePatterns: ['<rootDir>/__tests__/__fixtures__/'],
transform: { transform: {
'^.+\\.tsx?$': 'ts-jest', '^.+\\.tsx?$': 'ts-jest',

@ -51,7 +51,9 @@ export const Carousel: FunctionComponent<CarouselProps> = (userProps: CarouselPr
isSliding: true, isSliding: true,
}); });
setCurrent(next); setCurrent(next);
setShowArrow(getShowArrow(props.children.length, props.show, props.infinite, next)); setShowArrow(
getShowArrow(props.children.length, props.show, props.infinite, next),
);
setTimeout(() => { setTimeout(() => {
if (props.infinite) { if (props.infinite) {
setItems(cleanItems(rotated, slide, direction)); setItems(cleanItems(rotated, slide, direction));

@ -0,0 +1,155 @@
import React, {
FunctionComponent,
MouseEvent,
useState,
useRef,
useCallback,
} from 'react';
import { Item, SlideDirection } from '../../types/carousel';
import styles from '../../styles/slider/styles.module.css';
import { getOuterWidth } from '../../helpers';
export const ScrollingCarousel: FunctionComponent<SliderProps> = ({
children,
className,
}: SliderProps) => {
const slider = useRef<HTMLDivElement>(null);
const [isDown, setIsDown] = useState(false);
const [position, setPosition] = useState({
startX: 0,
scrollLeft: 0,
});
const showArrows = (): Arrows => {
const sliderElement = slider.current;
return {
left: !!sliderElement && sliderElement.scrollLeft > 0,
right:
!!sliderElement &&
sliderElement.scrollWidth >
sliderElement.scrollLeft + sliderElement.offsetWidth,
};
};
const [showArrow, setShowArrow] = useState<Arrows>(showArrows());
const onScroll = (_: Event) => {
setShowArrow(showArrows());
};
const ref = useCallback(
(node) => {
if (node !== null) {
Object.defineProperty(slider, 'current', { value: node });
setShowArrow(showArrows());
node.addEventListener('scroll', onScroll);
}
},
[slider],
);
const mouseDown = (e: MouseEvent) => {
setIsDown(true);
setPosition({
startX: e.pageX - slider.current!.offsetLeft,
scrollLeft: slider.current!.scrollLeft,
});
};
const mouseUp = (_: MouseEvent) => {
setIsDown(false);
setShowArrow(showArrows());
};
const mouseMove = (e: MouseEvent) => {
if (!isDown) return;
e.preventDefault();
const eventPosition = e.pageX - slider.current!.offsetLeft;
const slide = eventPosition - position.startX;
slider.current!.scrollLeft = position.scrollLeft - slide;
};
const calculateSlideAmount = (direction: SlideDirection): number => {
const _slider = slider.current!;
const currentView =
direction === SlideDirection.Left
? _slider.scrollLeft + _slider.offsetWidth
: _slider.scrollLeft;
const childNodes = Array.from(_slider.children) as HTMLElement[];
let nodeWidthSum = 0;
for (const node of childNodes) {
const nodeWidth = getOuterWidth(node);
nodeWidthSum += nodeWidth;
if (nodeWidthSum >= currentView) {
const showingPart =
direction === SlideDirection.Left
? nodeWidthSum - currentView
: nodeWidth;
return (_slider.offsetWidth - showingPart) * direction;
}
}
return _slider.offsetWidth;
};
const slide = (direction: SlideDirection) => {
const slideAmount = calculateSlideAmount(direction);
const start = slider.current!.scrollLeft;
smoothHorizontalScroll(500, slideAmount, start);
};
const smoothHorizontalScroll = (time: number, amount: number, start: number) => {
let curTime = 0;
for (let scrollCounter = 0; curTime <= time; scrollCounter++) {
window.setTimeout(
smoothHorizontalScrollBehavior,
curTime,
(scrollCounter * amount) / 100 + start,
);
curTime += time / 100;
}
};
const smoothHorizontalScrollBehavior = (amount: number) => {
slider.current!.scrollLeft = amount;
};
return (
<div className={`${styles.sliderBase} ${className}`}>
{showArrow.left && (
<div data-arrow="left">
<button onClick={() => slide(SlideDirection.Right)} />
</div>
)}
{showArrow.right && (
<div data-arrow="right">
<button onClick={() => slide(SlideDirection.Left)} />
</div>
)}
<div
ref={ref}
onMouseDown={mouseDown}
onMouseLeave={mouseUp}
onMouseUp={mouseUp}
onMouseMove={mouseMove}
className={styles.slider}
>
{children}
</div>
</div>
);
};
export interface SliderProps {
children: Item[];
className?: string;
}
export type Arrows = {
left: boolean;
right: boolean;
};

@ -133,3 +133,13 @@ export function getPageX(e: TouchEvent | MouseEvent): number {
} }
return 0; return 0;
} }
export function getOuterWidth(el: HTMLElement) {
const style = getComputedStyle(el);
return (
el.offsetWidth +
(parseInt(style.marginLeft, 10) || 0) +
(parseInt(style.marginRight, 10) || 0)
);
}

@ -1,3 +1,4 @@
import { Carousel } from './components/carousel'; import { Carousel } from './components/carousel';
import { ScrollingCarousel } from './components/scrolling-carousel';
export { Carousel }; export { Carousel, ScrollingCarousel };

@ -0,0 +1,20 @@
.sliderBase {
width: 100%;
position: relative;
}
.slider {
display: flex;
overflow-x: auto;
scrollbar-width: none; /* Firefox 64 */
-ms-overflow-style: none; /* Internet Explorer 11 */
}
.slider::-webkit-scrollbar {
/** WebKit */
display: none;
}
.slider > * {
flex: 0 0 auto;
}

@ -0,0 +1,8 @@
export const sliderBase: string;
export const slider: string;
interface Namespace {
sliderBase: string;
slider: string;
}
declare const stylesModule: Namespace;
export default stylesModule;

File diff suppressed because one or more lines are too long

@ -0,0 +1,17 @@
---
id: scrolling-carousel-api
title: Scrolling Carousel
---
Scrollable carousel like google's web component `g-scrolling-carousel`
```jsx
<ScrollingCarousel />
```
### Props
| Name | type | required | default | descripiton |
| --------- | :----: | -------: | ------: | -------------------------------------------: |
| children | Node[] | true | [] | Child items that will be wrapped by carousel |
| className | string | false | "" | same as react's className property |

@ -1,7 +1,7 @@
module.exports = { module.exports = {
someSidebar: { someSidebar: {
Docusaurus: ['installation', 'usage'], Docusaurus: ['installation', 'usage'],
API: ['carousel'], API: ['carousel', 'scrolling-carousel-api'],
Examples: ['infinity', 'swipible'], Examples: ['infinity', 'swipible', 'scrolling-carousel'],
}, },
}; };

@ -48,3 +48,64 @@
.exampleCarousel1 div:first-child { .exampleCarousel1 div:first-child {
text-align: center; text-align: center;
} }
.scrolling-carousel-example1-container {
width: 660px;
}
.scrolling-carousel-example1 > [data-arrow='left'] {
background: linear-gradient(90deg, #fff 10%, rgba(255, 255, 255, 0));
top: 0px;
left: 0;
opacity: 1;
height: 42px;
width: 88px;
position: absolute;
cursor: pointer;
bottom: 0;
margin-bottom: auto;
margin-top: auto;
}
.scrolling-carousel-example1 > [data-arrow='left'] > button {
background: url('https://cdn.dsmcdn.com/web/production/slick-arrow.svg') no-repeat
center;
transform: rotateZ(180deg) scale(0.5);
height: 40px;
left: -13px;
border: none;
outline: none;
cursor: pointer;
}
.scrolling-carousel-example1 > [data-arrow='right'] {
background: linear-gradient(270deg, #fff 10%, rgba(255, 255, 255, 0));
right: 0;
opacity: 1;
height: 42px;
width: 88px;
position: absolute;
cursor: pointer;
top: 0;
bottom: 0;
margin-bottom: auto;
margin-top: auto;
}
.scrolling-carousel-example1 > [data-arrow='right'] > button {
background: url('https://cdn.dsmcdn.com/web/production/slick-arrow.svg') no-repeat;
height: 40px;
transform: scale(0.5);
border: none;
outline: none;
position: absolute;
right: -16px;
cursor: pointer;
}
[data-theme='dark'] .scrolling-carousel-example1 > [data-arrow='right'] {
background: linear-gradient(270deg, #121212 10%, rgba(255, 255, 255, 0));
}
[data-theme='dark'] .scrolling-carousel-example1 > [data-arrow='left'] {
background: linear-gradient(90deg, #121212 10%, rgba(255, 255, 255, 0));
top: 0px;
}

@ -4,10 +4,112 @@ import Layout from '@theme/Layout';
import Link from '@docusaurus/Link'; import Link from '@docusaurus/Link';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import useBaseUrl from '@docusaurus/useBaseUrl'; import useBaseUrl from '@docusaurus/useBaseUrl';
import { Carousel } from '@trendyol-js/react-carousel'; import { Carousel, ScrollingCarousel } from '@trendyol-js/react-carousel';
import styles from './styles.module.css'; import styles from './styles.module.css';
import { Redirect } from '@docusaurus/router'; import { Redirect } from '@docusaurus/router';
const slideData = [
{
text: 'skyline',
img:
'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAkGBwgHBgkIBwgKCgkLDRYPDQwMDRsUFRAWIB0iIiAdHx8kKDQsJCYxJx8fLT0tMTU3Ojo6Iys/RD84QzQ5OjcBCgoKDQwNGg8PGjclHyU3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3N//AABEIACAAIAMBIgACEQEDEQH/xAAZAAADAQEBAAAAAAAAAAAAAAAEBQcDAgH/xAAvEAACAQIEAwUIAwAAAAAAAAABAgMEEQAFEiETMVFBYXGBkRQjMoKxssLwBiJC/8QAFgEBAQEAAAAAAAAAAAAAAAAAAwQC/8QAIREAAQQBAwUAAAAAAAAAAAAAAQACAxEEEjHwBSFBYYH/2gAMAwEAAhEDEQA/AFYz3KA0UbVjI8jBNSxEqvifHp64fSURSxNSQOw8K3nzxNeLC2oVWX00hIKkoNPnZSADftAxSsnzWlzaiHBbdAFbWu6+O+KHYWrYArAmr0s56CopYjOt5Y99TD4k77dMc+0RG2mzX5Ww5pEWJSEqKhiu22kW9RgWfK6KpqONA1TTSsukknWhNulhbpsbd2JZuly1qaEzMsbFSmanqYT76ETqP9xHS4HhyPpgzJp3o5hPHUMkc4ZCrf1J+K3zCw27LnffDhoL722xjPlUVQtm1qNWqysQL9bcjhhkPBF+CiMYoo6k/mBWniiMBZ4xpNpgS3PckW/RgOXO5plVlnqujcSoCHbbkFP1GMhlNTGvuK1UW/L2WI/jjw5ZVstjmcy90KLF9gGKDlM71fPqMRlf/9k=',
},
{
text: 'r35',
img:
'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAkGBwgHBgkIBwgKCgkLDRYPDQwMDRsUFRAWIB0iIiAdHx8kKDQsJCYxJx8fLT0tMTU3Ojo6Iys/RD84QzQ5OjcBCgoKDQwNGg8PGjclHyU3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3N//AABEIACAAIAMBIgACEQEDEQH/xAAYAAADAQEAAAAAAAAAAAAAAAAEBQYHAv/EACwQAAEDAwEFCAMBAAAAAAAAAAECAwQABRESBiExQXETIiNRYZGhsRRSwQf/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAf/EABYRAQEBAAAAAAAAAAAAAAAAAAABEf/aAAwDAQACEQMRAD8A2VaCsUG/AyCpakpSOJJwKVzNvbLGcCEOaxzUTgDpxzUhdtqjc5ZEO4PvKUT2TaYoV2Y8gAoZ64JqyJar5DlrYJbdu0BC/wBVyUA/dBybaiU2XIrzT6cZy0sK+qxiVZp866POsrdkvBRW4oN6dIHHmcdKfWa2SC2gvSWoTqDlK0vHI6ADI96uGpWfcIxltIQmdhWB4jaUg53b+4Dkdafx7ibcw0xFeVokK0vAtJBCfQjfR7+wDBV4E+U0nOcFWr7rhH+fpAwq5PHcRkoSdx9Du+KCZRdl/kx57bim21btGvuhJz3ccgBjkcnJzniSm5F86ITMh5fmngf581UW3YW1wUpS6pcjScjtDu9qesx4sROlhpCQPIUH/9k=',
},
{
text: 'custom',
img:
'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAkGBwgHBgkIBwgKCgkLDRYPDQwMDRsUFRAWIB0iIiAdHx8kKDQsJCYxJx8fLT0tMTU3Ojo6Iys/RD84QzQ5OjcBCgoKDQwNGg8PGjclHyU3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3N//AABEIACAAIAMBIgACEQEDEQH/xAAYAAEAAwEAAAAAAAAAAAAAAAAGAwQHBf/EADAQAAIBAwIEBQALAQAAAAAAAAECAwQFEQAhBhITMRQiQVFhFSMyM0JEcZGhwdEH/8QAFgEBAQEAAAAAAAAAAAAAAAAAAQIA/8QAGxEAAgMAAwAAAAAAAAAAAAAAAAECEUEDEhP/2gAMAwEAAhEDEQA/ANQt6RNU1xVdhMoGO33aH+9RXF+ndqWUo5iWlnyApKklosZAB27742Gh9tqfpS0LPLItPJ0o35ESIiM8qqcc+cAAex+CdR1dTXMI6mC6VPXkikmTp0S5yWww5S/qFGBn0/DqHyPC1BaduE2yO2LT0ppopPpJX6YAjblNT5crsfsn+NXaykJ4ioZgBhaKpXOd93g/zQl2miqqrxslS0KQRMB4bBk88bEK2dmG+2TsD231yOIuJpKbwdRSTVFPP4aKR2EhPkLHC7nYZjGRv7HT6VguKwpcNSUFZbZ6y/VCUlppWSEsFDyluXChVAyNiNwD6+2ldpb/AJzdZ6e1Ut0uck0mRCplniztnl5gqj07E7n50YquGLZV03hqaaWjgZldo4wCGdQwDEnfOGPrjtqnQ8FrbbhTVtDdwJaeVZU6lPzeZSCOzDTYdGMrlYOFrPAtc5utOVqIUhU3Au0khcAEoSRyjuc52B21mt1eOS+wz0a1G8jo/M6yGRRy/WHChRk74xjykke6Op4cFZM8lwuhfM8k6JBF0wrvyE4yzbAopAGMfptqektlrtamSlVpKo/mJn5mA+PY/P7b76E6VXZlBn//2Q==',
},
{
text: 'liberty walk',
img:
'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAkGBwgHBgkIBwgKCgkLDRYPDQwMDRsUFRAWIB0iIiAdHx8kKDQsJCYxJx8fLT0tMTU3Ojo6Iys/RD84QzQ5OjcBCgoKDQwNGg8PGjclHyU3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3N//AABEIACAAIAMBIgACEQEDEQH/xAAYAAADAQEAAAAAAAAAAAAAAAADBAYFAv/EAC0QAAEDAgQEAwkAAAAAAAAAAAECAwQFEQASIWEGMUFREyJxFDJSYpGhwdHh/8QAGAEAAwEBAAAAAAAAAAAAAAAAAQMEAgD/xAAeEQACAQMFAAAAAAAAAAAAAAABAgAREyEDEjJBQv/aAAwDAQACEQMRAD8AsFsbYCuPtjYWztgK2tsa3QFJjqjbYmalw6uRJelPqCE2JUpOtx6bdsWrxabF3XEIHzKAwoZUFRIEyMT28VJ/OOajDMC1U4hKpxNDp8hEd91tLy+aQq+X1On72wm/WWZ1Of8AZaszDlJzJSHmiqx6EEWHbodxfEsvhSqJflyC5HdcdW6prxHFEgLI5nKToAPXlvgNN4PqzGjwp0keGEgLdeTbe4Av/TicWzyNZS13yI/Hg0hyOl6o1JIqK02eU2C4OeoClam/fp0tjmVTqDQKcHXZzknx0qUGn0hXlJ943vl25ddOeAq4RqJOZMano6+SU9f7g4TXwxXpMtblREZacqbFLxUUlObLpkHxK+uGnVToxC6D1ys//9k=',
},
{
text: 'wallpaper',
img:
'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAkGBwgHBgkIBwgKCgkLDRYPDQwMDRsUFRAWIB0iIiAdHx8kKDQsJCYxJx8fLT0tMTU3Ojo6Iys/RD84QzQ5OjcBCgoKDQwNGg8PGjclHyU3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3N//AABEIACAAIAMBIgACEQEDEQH/xAAaAAACAwEBAAAAAAAAAAAAAAAFBgMEBwIB/8QALBAAAgEDAwEGBgMAAAAAAAAAAQIDBBESAAUhMQYTIkFRcRRhkaHR8VJigf/EABYBAQEBAAAAAAAAAAAAAAAAAAIDBP/EABsRAQACAwEBAAAAAAAAAAAAAAEAAhIhQREi/9oADAMBAAIRAxEAPwDKYaeecXiiJW9siQAPPqffU7U0cSK0hkYsoZVxC34v1P0007rR10dUKXbqZJKwO6yzPGlxi1reIWt53At6aml7O7lFSyy7v8EC1sDmnN79ceOfl89aC1nsgtTkXkXb4ZFRjSqzAEu+cmIsb8A29uv+a8jr441kQU8NXGwZbgCHEnE5BV6EY8X/AJEe9+PYkWb4VKYlpLE965Kpb0A8R5/eu5uzxgpUSCCesldj4STghBtwo638uToPhbFdyoWtTMPmWV36uemDR1itFyoU1AUgeliR9tQbr2nqqs09NInfUcao5ja+OQHJNvMm/PtqzP2So5PFSVGH9XGQ/OgdbtG5wHuhTysitcGK7qT68A206p7uSCrGKg3umi7U0j1FOYacSCN42cNZHFgb+nIPtovX9swY0gSekVg8vfCFXOCC+FiSRcG/TWYypOkmMzEOoC4EG9h0FjohSbXXbg3jCwxHkkrj9vPoPpoO2LECf//Z',
},
{
text: 'nismo',
img:
'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAkGBwgHBgkIBwgKCgkLDRYPDQwMDRsUFRAWIB0iIiAdHx8kKDQsJCYxJx8fLT0tMTU3Ojo6Iys/RD84QzQ5OjcBCgoKDQwNGg8PGjclHyU3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3N//AABEIACAAIAMBIgACEQEDEQH/xAAZAAADAQEBAAAAAAAAAAAAAAADBQYHBAL/xAAqEAABBAAFAwIHAQAAAAAAAAABAgMEEQAFEiExBhNRQZEUIjJhgaGxB//EABUBAQEAAAAAAAAAAAAAAAAAAAAB/8QAFhEBAQEAAAAAAAAAAAAAAAAAAGER/9oADAMBAAIRAxEAPwAqYivGCJhqPpiiZyuQ6abjOna70ED3wF6FmQKmmcuX3K47qCoDzQVgEvwR8Y8mGfGFEuJ1u28SqBOCLNJbU3f6Jwy6dV1RNbpeWh1tCtJckI7WqjRpVjf70cA06R/0lnPM0cgTm2WCGi42806pSTRFg6kitjf4OCSeoe3KfVIzRiWlCCtKIqB20AXeo9w2fGw9efTDYqp0Na1sxUhS0FCqO1EUecHjzc0Ql5CkyHA+jQsuPWQnfYE3XOC5V70z1rPX1KWHgEtTUn5QskoOkqBvnbjcnnauMWzudqiUubNhM2B9bzd/0nGHIZmLcadaKmpAG60m62rbHS3lMl1ZcfecKibO+m/bER//2Q==',
},
{
text: 'godzilla',
img:
'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAkGBwgHBgkIBwgKCgkLDRYPDQwMDRsUFRAWIB0iIiAdHx8kKDQsJCYxJx8fLT0tMTU3Ojo6Iys/RD84QzQ5OjcBCgoKDQwNGg8PGjclHyU3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3N//AABEIACAAIAMBIgACEQEDEQH/xAAZAAACAwEAAAAAAAAAAAAAAAADBgQFBwL/xAAsEAACAQIFAQcEAwAAAAAAAAABAgMEEQAFEiExYQYTIlGBkaEUMkJxI0FD/8QAFgEBAQEAAAAAAAAAAAAAAAAAAwIB/8QAGxEAAgIDAQAAAAAAAAAAAAAAAAECERIyQQP/2gAMAwEAAhEDEQA/AIWVUWa0njRUZOfG+xxapOgD99EI9H5wynSPQ47y7tLTTQd3UUClebhr+uM+zzNJMzrtKPppi40IrbLfi/XDN0DGLkObdpcrWoiphKZDIyLrKC3i69Bb3HWx1qqWvoqerpphoqNZjjOzFVYi9vb9XtjNAIFO+oSBiFHkdsHy+YUFbC5P8ZCozHmMFvEVA5Nr+pxmRT80MOQuI2jYWJB4OL5I6dqerf6ZHNQSZABu5t/fXCLl1RJ9IwgnhWU/aTKLj0OA1NTmUjBnmMZQaQBokv15J3xll4EpKCKhhTvLGokH+ymMRm3ALWBPHGIQhqhWRxV8EwQfcAASAeLHAJ6upmCPMkSvGPxhCaz5kAAHn4xYp2rrYY0iENNaInT3cdgDvuPj2xLvgsIwe7o//9k=',
},
{
text: 'blue',
img:
'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcR7vhDBHGcYfgGumwP_JdDsWdbmRBJrO822ayjYBWnUeOpg4mvv5HBrUCHh&s',
},
{
text: 'r32',
img:
'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTcedysoUHEN5Pu8C5gy1_H_-fwbYUbZTqHhLblNaFvYGMlDVdQ6r0d3T5n&s',
},
{
text: 'gold',
img:
'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRVUpJPnUV2JjON_U8j_GsNfpzB1TE_1YSPTFB4ElJX3-e7BPB_Dz0WJLvZ&s',
},
{
text: 'white',
img:
'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQawL7bAfO-DV0rY3XlWnkm2cJcAAB9JQsZw4P9k8R0RElWDOhPyJ06WEMO&s',
},
{
text: 'nissan',
img:
'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcR-3a4aW0nL4gsFvceXOcqfFT5qlrOwHU0DySevbEo8-45XzIJJxu4x5ZeB&s',
},
{
text: 'modified',
img:
'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSXsPphsOHUfc10c5bCr6VkCDK2d0YYYc7PkjiA527xIMsJZw7x5qqt3GZZ&s',
},
{
text: 'red',
img:
'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSoJnGchkU3zwrTdq4WbFSjd5E-ehy9gtEaaxiuOE9-LHZnh8PYhANsp3D_&s',
},
{
text: 'rocket bunny',
img:
'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRYefgcSyvUOC_qcNY9yZV7AX8yPaDlnZ3BU5eH794jT3AQH6AiTVXPDl5S&s',
},
{
text: 'r33',
img:
'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRA2ZYkl0giWGtaNXKwr2z8HbJszowxKEQTDATCJMLvJ7kpGsRl9dr8CBnD&s',
},
{
text: 'chrome',
img:
'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRFtKFD8Oalc6HqSrkWVMtPTitLO0mXpV9ofKYxgusg8zjfZdlaA4wX3KVv&s',
},
{
text: 'car',
img:
'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQdTsdEjLHkQIYipiaZcLUP9LG2GsplKl7MQSuawbDbOgtQ_5ygK46i_3xc&s',
},
{
text: 'cool',
img:
'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQfhHP8XoQ0X3eL4J_Xy0BJ0pnvrkINkKR78N6yhfXa7eWXn3UBh1ZzMep8&s',
},
{
text: 'widebody',
img:
'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRlGGm-Hh5so6PtScyr7lXrB9V13dFr7mtMJC5YlO9aaDPHfPQNRvzsFPK_&s',
},
];
const features = [ const features = [
{ {
title: <>Easy to Use</>, title: <>Easy to Use</>,
@ -92,6 +194,42 @@ function Home() {
</div> </div>
</header> </header>
<main> <main>
<section className={styles.features}>
<div className="container">
<div className="row" style={{ width: '660px', margin: 'auto' }}>
<ScrollingCarousel>
{slideData.map((d) => (
<div
style={{
cursor: 'pointer',
display: 'flex',
border: '1px solid #DFE1E5',
borderRadius: '20px',
height: '40px',
lineHeight: '38px',
paddingRight: '12px',
margin: '14px 8px 14px 0',
}}
>
<img
style={{
borderRadius: '16px',
height: '32px',
marginBottom: '3px',
marginLeft: '3px',
marginRight: '8px',
marginTop: '3px',
width: '32px',
}}
src={d.img}
/>
<span style={{ fontSize: '12px' }}>{d.text}</span>
</div>
))}
</ScrollingCarousel>
</div>
</div>
</section>
<section className={styles.features}> <section className={styles.features}>
<div className="container"> <div className="container">
<div className="row"> <div className="row">

Loading…
Cancel
Save