commit
e5e13134c7
57 changed files with 25577 additions and 0 deletions
@ -0,0 +1,70 @@ |
|||||||
|
version: 2.1 |
||||||
|
|
||||||
|
defaults: &defaults |
||||||
|
docker: |
||||||
|
- image: circleci/node:10 |
||||||
|
|
||||||
|
jobs: |
||||||
|
test: |
||||||
|
<<: *defaults |
||||||
|
steps: |
||||||
|
- checkout |
||||||
|
- restore_cache: |
||||||
|
keys: |
||||||
|
- v1-dependencies-{{ checksum "package.json" }} |
||||||
|
- v1-dependencies- |
||||||
|
- run: npm install |
||||||
|
- run: |
||||||
|
name: Run tests |
||||||
|
command: npm test |
||||||
|
- save_cache: |
||||||
|
paths: |
||||||
|
- node_modules |
||||||
|
key: v1-dependencies-{{ checksum "package.json" }} |
||||||
|
- run: npm build |
||||||
|
- persist_to_workspace: |
||||||
|
root: . |
||||||
|
paths: |
||||||
|
- README.md |
||||||
|
- CHANGELOG.md |
||||||
|
- LICENSE |
||||||
|
- package.json |
||||||
|
- package-lock.json |
||||||
|
- .npmignore |
||||||
|
- dist |
||||||
|
deploy: |
||||||
|
<<: *defaults |
||||||
|
steps: |
||||||
|
- attach_workspace: |
||||||
|
at: . |
||||||
|
- run: |
||||||
|
name: List Workspace |
||||||
|
command: ls |
||||||
|
- run: |
||||||
|
name: Authenticate with registry |
||||||
|
command: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > .npmrc |
||||||
|
- run: |
||||||
|
name: Publish package |
||||||
|
command: npm publish |
||||||
|
|
||||||
|
workflows: |
||||||
|
version: 2 |
||||||
|
test-deploy: |
||||||
|
jobs: |
||||||
|
- test: |
||||||
|
filters: |
||||||
|
tags: |
||||||
|
only: /^v.*/ |
||||||
|
- hold: |
||||||
|
type: approval |
||||||
|
requires: |
||||||
|
- test |
||||||
|
filters: |
||||||
|
branches: |
||||||
|
only: master |
||||||
|
- deploy: |
||||||
|
requires: |
||||||
|
- hold |
||||||
|
filters: |
||||||
|
branches: |
||||||
|
only: master |
@ -0,0 +1,2 @@ |
|||||||
|
*/node_modules |
||||||
|
*.log |
@ -0,0 +1,10 @@ |
|||||||
|
# stop .editorconfig files search on current file. |
||||||
|
root = true |
||||||
|
|
||||||
|
# Unix-style newlines with a newline ending every file |
||||||
|
[*] |
||||||
|
charset = utf-8 |
||||||
|
end_of_line = lf |
||||||
|
insert_final_newline = true |
||||||
|
indent_style = tab |
||||||
|
trim_trailing_whitespace = true |
@ -0,0 +1,15 @@ |
|||||||
|
.DS_Store |
||||||
|
|
||||||
|
node_modules |
||||||
|
|
||||||
|
lib/core/metadata.js |
||||||
|
lib/core/MetadataBlog.js |
||||||
|
|
||||||
|
website/translated_docs |
||||||
|
website/build/ |
||||||
|
website/yarn.lock |
||||||
|
website/node_modules |
||||||
|
website/i18n/* |
||||||
|
|
||||||
|
dist/ |
||||||
|
coverage |
@ -0,0 +1,6 @@ |
|||||||
|
.DS_Store |
||||||
|
website |
||||||
|
docs |
||||||
|
.circleci |
||||||
|
test |
||||||
|
coverage |
@ -0,0 +1,6 @@ |
|||||||
|
{ |
||||||
|
"semi": true, |
||||||
|
"trailingComma": "all", |
||||||
|
"singleQuote": true, |
||||||
|
"printWidth": 90 |
||||||
|
} |
@ -0,0 +1,10 @@ |
|||||||
|
FROM node:lts |
||||||
|
|
||||||
|
WORKDIR /app/website |
||||||
|
|
||||||
|
EXPOSE 3000 35729 |
||||||
|
COPY ./docs /app/docs |
||||||
|
COPY ./website /app/website |
||||||
|
RUN yarn install |
||||||
|
|
||||||
|
CMD ["yarn", "start"] |
@ -0,0 +1,21 @@ |
|||||||
|
MIT License |
||||||
|
|
||||||
|
Copyright (c) 2019 Trendyol Open Source |
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||||
|
of this software and associated documentation files (the "Software"), to deal |
||||||
|
in the Software without restriction, including without limitation the rights |
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||||
|
copies of the Software, and to permit persons to whom the Software is |
||||||
|
furnished to do so, subject to the following conditions: |
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all |
||||||
|
copies or substantial portions of the Software. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||||
|
SOFTWARE. |
@ -0,0 +1,3 @@ |
|||||||
|
# Carousel |
||||||
|
|
||||||
|
<img src="docs/carousel.png" height="300px"> |
@ -0,0 +1,7 @@ |
|||||||
|
import React from 'react'; |
||||||
|
|
||||||
|
export const carouselItemNodes = (len: number) => { |
||||||
|
return new Array(len).fill(0).map((_, i) => { |
||||||
|
return <div style={{ height: 600 }}>{i + 1}</div>; |
||||||
|
}); |
||||||
|
}; |
@ -0,0 +1,15 @@ |
|||||||
|
import React from 'react'; |
||||||
|
import { render, cleanup, fireEvent } from '@testing-library/react'; |
||||||
|
import { Arrow } from '../src/components/arrow'; |
||||||
|
|
||||||
|
describe('<Arrow />', () => { |
||||||
|
afterEach(cleanup); |
||||||
|
|
||||||
|
it('should call onClick prop when click event occurs', async () => { |
||||||
|
const onClick = jest.fn(); |
||||||
|
const { getByRole } = render(<Arrow onClick={onClick} />); |
||||||
|
|
||||||
|
fireEvent.click(getByRole('button')); |
||||||
|
expect(onClick).toHaveBeenCalled(); |
||||||
|
}); |
||||||
|
}); |
@ -0,0 +1,256 @@ |
|||||||
|
import React, { MouseEvent } from 'react'; |
||||||
|
import { render, cleanup, fireEvent } from '@testing-library/react'; |
||||||
|
import { Carousel } from '../src/components/carousel'; |
||||||
|
import { defaultProps } from '../src/components/carousel/defaultProps'; |
||||||
|
import { carouselItemNodes } from './__fixtures__/nodes'; |
||||||
|
import * as helpers from '../src/helpers'; |
||||||
|
|
||||||
|
describe('<Carousel />', () => { |
||||||
|
let mockGetPageX: jest.SpyInstance< |
||||||
|
number, |
||||||
|
[React.TouchEvent<Element> | React.MouseEvent<Element, globalThis.MouseEvent>] |
||||||
|
>; |
||||||
|
|
||||||
|
afterEach(() => { |
||||||
|
mockGetPageX.mockRestore(); |
||||||
|
jest.clearAllTimers(); |
||||||
|
jest.resetAllMocks(); |
||||||
|
cleanup(); |
||||||
|
}); |
||||||
|
|
||||||
|
beforeEach(() => { |
||||||
|
Element.prototype.getBoundingClientRect = jest.fn(() => { |
||||||
|
return { |
||||||
|
width: 900, |
||||||
|
height: 600, |
||||||
|
top: 0, |
||||||
|
left: 0, |
||||||
|
bottom: 0, |
||||||
|
right: 0, |
||||||
|
x: 0, |
||||||
|
y: 0, |
||||||
|
toJSON: jest.fn(), |
||||||
|
}; |
||||||
|
}); |
||||||
|
jest.useFakeTimers(); |
||||||
|
mockGetPageX = jest |
||||||
|
.spyOn(helpers, 'getPageX') |
||||||
|
.mockImplementation((_: MouseEvent) => 600); |
||||||
|
}); |
||||||
|
|
||||||
|
it('should render right layout', async () => { |
||||||
|
const { getByTestId } = render( |
||||||
|
<Carousel {...defaultProps} infinite={false} children={carouselItemNodes(6)} />, |
||||||
|
); |
||||||
|
const carousel = getByTestId('carousel'); |
||||||
|
|
||||||
|
expect(carousel.firstChild); |
||||||
|
expect(carousel.firstChild!.firstChild).toBeTruthy(); |
||||||
|
expect(carousel.firstChild!.firstChild!.firstChild).toBeTruthy(); |
||||||
|
expect(carousel.firstChild!.firstChild!.firstChild).toBeTruthy(); |
||||||
|
}); |
||||||
|
|
||||||
|
it('should transform when pressing arrow keys', async () => { |
||||||
|
const { getByTestId } = render( |
||||||
|
<Carousel {...defaultProps} children={carouselItemNodes(6)} useArrowKeys={true} />, |
||||||
|
); |
||||||
|
const carousel = getByTestId('carousel'); |
||||||
|
|
||||||
|
fireEvent.click(carousel); |
||||||
|
fireEvent.keyDown(carousel, { keyCode: 39 }); |
||||||
|
jest.runAllTimers(); |
||||||
|
|
||||||
|
expect(setTimeout).toHaveBeenCalledTimes(1); |
||||||
|
expect(setTimeout).toHaveBeenLastCalledWith( |
||||||
|
expect.any(Function), |
||||||
|
defaultProps.transition * 1000, |
||||||
|
); |
||||||
|
}); |
||||||
|
|
||||||
|
it("shouldn't listen keyboard event if useArrowKeys option false", async () => { |
||||||
|
const { getByTestId } = render( |
||||||
|
<Carousel {...defaultProps} children={carouselItemNodes(6)} useArrowKeys={false} />, |
||||||
|
); |
||||||
|
const carousel = getByTestId('carousel'); |
||||||
|
|
||||||
|
fireEvent.click(carousel); |
||||||
|
fireEvent.keyDown(carousel, { keyCode: 39 }); |
||||||
|
|
||||||
|
expect(setTimeout).toHaveBeenCalledTimes(0); |
||||||
|
}); |
||||||
|
|
||||||
|
it("shouldn't slide to left if carousel not infinite and shows first item", async () => { |
||||||
|
const { getByTestId } = render( |
||||||
|
<Carousel |
||||||
|
{...defaultProps} |
||||||
|
infinite={false} |
||||||
|
useArrowKeys={true} |
||||||
|
children={carouselItemNodes(4)} |
||||||
|
/>, |
||||||
|
); |
||||||
|
const carousel = getByTestId('carousel'); |
||||||
|
|
||||||
|
fireEvent.click(carousel); |
||||||
|
fireEvent.keyDown(carousel, { keyCode: 37 }); |
||||||
|
|
||||||
|
expect(setTimeout).toHaveBeenCalledTimes(0); |
||||||
|
}); |
||||||
|
|
||||||
|
it("shouldn't do anything if press a key that different from arrow keys", async () => { |
||||||
|
const { getByTestId } = render( |
||||||
|
<Carousel |
||||||
|
{...defaultProps} |
||||||
|
infinite={false} |
||||||
|
useArrowKeys={true} |
||||||
|
children={carouselItemNodes(4)} |
||||||
|
/>, |
||||||
|
); |
||||||
|
const carousel = getByTestId('carousel'); |
||||||
|
|
||||||
|
fireEvent.click(carousel); |
||||||
|
fireEvent.keyDown(carousel, { keyCode: 317 }); |
||||||
|
|
||||||
|
expect(setTimeout).toHaveBeenCalledTimes(0); |
||||||
|
}); |
||||||
|
|
||||||
|
it('should slide to left when click left button', async () => { |
||||||
|
const { getByTestId } = render( |
||||||
|
<Carousel {...defaultProps} infinite={true} children={carouselItemNodes(4)} />, |
||||||
|
); |
||||||
|
const carousel = getByTestId('carousel'); |
||||||
|
const button = carousel.querySelector('button'); |
||||||
|
|
||||||
|
expect(button).not.toBeNull(); |
||||||
|
|
||||||
|
fireEvent.click(button!); |
||||||
|
jest.runAllTimers(); |
||||||
|
|
||||||
|
expect(setTimeout).toHaveBeenCalledTimes(1); |
||||||
|
expect(setTimeout).toHaveBeenLastCalledWith( |
||||||
|
expect.any(Function), |
||||||
|
defaultProps.transition * 1000, |
||||||
|
); |
||||||
|
}); |
||||||
|
|
||||||
|
it('should slide to right when click right button', async () => { |
||||||
|
const { getByTestId } = render( |
||||||
|
<Carousel {...defaultProps} infinite={true} children={carouselItemNodes(4)} />, |
||||||
|
); |
||||||
|
const carousel = getByTestId('carousel'); |
||||||
|
const button = carousel.querySelectorAll('button')[1]; |
||||||
|
|
||||||
|
expect(button).not.toBeNull(); |
||||||
|
|
||||||
|
fireEvent.click(button!); |
||||||
|
jest.runAllTimers(); |
||||||
|
|
||||||
|
expect(setTimeout).toHaveBeenCalledTimes(1); |
||||||
|
expect(setTimeout).toHaveBeenLastCalledWith( |
||||||
|
expect.any(Function), |
||||||
|
defaultProps.transition * 1000, |
||||||
|
); |
||||||
|
}); |
||||||
|
|
||||||
|
it('should slide back when swiping is done', async () => { |
||||||
|
const { getByTestId } = render( |
||||||
|
<Carousel |
||||||
|
{...defaultProps} |
||||||
|
infinite={true} |
||||||
|
swiping={true} |
||||||
|
children={carouselItemNodes(12)} |
||||||
|
/>, |
||||||
|
); |
||||||
|
const trackList = getByTestId('trackList'); |
||||||
|
|
||||||
|
mockGetPageX = jest |
||||||
|
.spyOn(helpers, 'getPageX') |
||||||
|
.mockImplementation((_: MouseEvent) => 600); |
||||||
|
|
||||||
|
fireEvent.mouseDown(trackList, { pageX: 600 }); |
||||||
|
|
||||||
|
mockGetPageX = jest |
||||||
|
.spyOn(helpers, 'getPageX') |
||||||
|
.mockImplementation((_: MouseEvent) => 390); |
||||||
|
|
||||||
|
fireEvent.mouseMove(trackList, { pageX: 390 }); |
||||||
|
fireEvent.mouseUp(trackList, { pageX: 390 }); |
||||||
|
jest.runAllTimers(); |
||||||
|
|
||||||
|
expect(setTimeout).toHaveBeenCalledTimes(1); |
||||||
|
expect(setTimeout).toHaveBeenLastCalledWith( |
||||||
|
expect.any(Function), |
||||||
|
defaultProps.transition * 1000, |
||||||
|
); |
||||||
|
}); |
||||||
|
|
||||||
|
it('should slide back when swiping is done and drag size less than item size', async () => { |
||||||
|
const { getByTestId } = render( |
||||||
|
<Carousel |
||||||
|
{...defaultProps} |
||||||
|
infinite={true} |
||||||
|
swiping={true} |
||||||
|
show={5} |
||||||
|
children={carouselItemNodes(10)} |
||||||
|
/>, |
||||||
|
); |
||||||
|
const trackList = getByTestId('trackList'); |
||||||
|
|
||||||
|
mockGetPageX = jest |
||||||
|
.spyOn(helpers, 'getPageX') |
||||||
|
.mockImplementation((_: MouseEvent) => 600); |
||||||
|
|
||||||
|
fireEvent.mouseDown(trackList, { pageX: 600 }); |
||||||
|
|
||||||
|
mockGetPageX = jest |
||||||
|
.spyOn(helpers, 'getPageX') |
||||||
|
.mockImplementation((_: MouseEvent) => 190); |
||||||
|
|
||||||
|
fireEvent.mouseMove(trackList, { pageX: 190 }); |
||||||
|
fireEvent.mouseUp(trackList, { pageX: 190 }); |
||||||
|
jest.runAllTimers(); |
||||||
|
|
||||||
|
expect(setTimeout).toHaveBeenCalledTimes(1); |
||||||
|
expect(setTimeout).toHaveBeenLastCalledWith( |
||||||
|
expect.any(Function), |
||||||
|
defaultProps.transition * 1000, |
||||||
|
); |
||||||
|
}); |
||||||
|
|
||||||
|
it("shouldn't rotate items if carousel is not infinite", async () => { |
||||||
|
const { getByTestId } = render( |
||||||
|
<Carousel {...defaultProps} infinite={false} children={carouselItemNodes(10)} />, |
||||||
|
); |
||||||
|
const carousel = getByTestId('carousel'); |
||||||
|
const button = carousel.querySelector('button'); |
||||||
|
|
||||||
|
expect(button).not.toBeNull(); |
||||||
|
|
||||||
|
fireEvent.click(button!); |
||||||
|
jest.runAllTimers(); |
||||||
|
|
||||||
|
expect(setTimeout).toHaveBeenCalledTimes(1); |
||||||
|
expect(setTimeout).toHaveBeenLastCalledWith( |
||||||
|
expect.any(Function), |
||||||
|
defaultProps.transition * 1000, |
||||||
|
); |
||||||
|
}); |
||||||
|
|
||||||
|
it('should rotate items if carousel is infinite', async () => { |
||||||
|
const { getByTestId } = render( |
||||||
|
<Carousel {...defaultProps} infinite={true} children={carouselItemNodes(10)} />, |
||||||
|
); |
||||||
|
const carousel = getByTestId('carousel'); |
||||||
|
const button = carousel.querySelector('button'); |
||||||
|
|
||||||
|
expect(button).not.toBeNull(); |
||||||
|
|
||||||
|
fireEvent.click(button!); |
||||||
|
jest.runAllTimers(); |
||||||
|
|
||||||
|
expect(setTimeout).toHaveBeenCalledTimes(1); |
||||||
|
expect(setTimeout).toHaveBeenLastCalledWith( |
||||||
|
expect.any(Function), |
||||||
|
defaultProps.transition * 1000, |
||||||
|
); |
||||||
|
}); |
||||||
|
}); |
@ -0,0 +1,70 @@ |
|||||||
|
import React, { MouseEvent, TouchEvent } from 'react'; |
||||||
|
import * as helpers from '../src/helpers'; |
||||||
|
import { carouselItemNodes } from './__fixtures__/nodes'; |
||||||
|
import { SlideDirection } from '../src/types/carousel'; |
||||||
|
|
||||||
|
describe('helpers', () => { |
||||||
|
it('should return to head of circular items list', async () => { |
||||||
|
const items = carouselItemNodes(2); |
||||||
|
const circular = new helpers.Circular(items, 0); |
||||||
|
circular.next(); |
||||||
|
circular.next(); |
||||||
|
|
||||||
|
expect(circular.current()).toEqual(items[0]); |
||||||
|
}); |
||||||
|
|
||||||
|
it('should add items to right and left of the array', async () => { |
||||||
|
const items = carouselItemNodes(2); |
||||||
|
const showingItems = carouselItemNodes(2); |
||||||
|
const result = helpers.rotateItems( |
||||||
|
items, |
||||||
|
showingItems, |
||||||
|
0, |
||||||
|
1, |
||||||
|
1, |
||||||
|
SlideDirection.Right, |
||||||
|
); |
||||||
|
|
||||||
|
expect(result.length).toEqual(4); |
||||||
|
}); |
||||||
|
|
||||||
|
it('should get indicator of the items array', async () => { |
||||||
|
const result = helpers.getCurrent(0, 2, 4, SlideDirection.Right); |
||||||
|
|
||||||
|
expect(result).toEqual(2); |
||||||
|
}); |
||||||
|
|
||||||
|
it('should get indicator of the items array in circular manner', async () => { |
||||||
|
const result = helpers.getCurrent(0, 5, 4, SlideDirection.Right); |
||||||
|
|
||||||
|
expect(result).toEqual(1); |
||||||
|
}); |
||||||
|
|
||||||
|
it('should return 0 if event is not mouseEvent or touchEvent', async () => { |
||||||
|
const mouseEvent = { nativeEvent: { pageX: 10 } }; |
||||||
|
const result = helpers.getPageX(mouseEvent as React.MouseEvent); |
||||||
|
|
||||||
|
expect(result).toEqual(0); |
||||||
|
}); |
||||||
|
|
||||||
|
it('should return pageX if event is mouseEvent', async () => { |
||||||
|
const nativeEvent = new MouseEvent('mousedown'); |
||||||
|
const pageX = 10; |
||||||
|
Object.defineProperty(nativeEvent, 'pageX', { value: pageX }); |
||||||
|
const event = { nativeEvent }; |
||||||
|
const result = helpers.getPageX(event as MouseEvent); |
||||||
|
|
||||||
|
expect(result).toEqual(pageX); |
||||||
|
}); |
||||||
|
|
||||||
|
it('should return pageX if event is touchEvent', async () => { |
||||||
|
const nativeEvent = new TouchEvent('mousedown'); |
||||||
|
const pageX = 10; |
||||||
|
const changedTouches = [{ pageX }]; |
||||||
|
Object.defineProperty(nativeEvent, 'changedTouches', { value: changedTouches }); |
||||||
|
const event = { nativeEvent }; |
||||||
|
const result = helpers.getPageX(event as TouchEvent); |
||||||
|
|
||||||
|
expect(result).toEqual(pageX); |
||||||
|
}); |
||||||
|
}); |
@ -0,0 +1,7 @@ |
|||||||
|
import { Carousel } from '../src'; |
||||||
|
|
||||||
|
describe('index.ts', () => { |
||||||
|
it('should export Carousel component', async () => { |
||||||
|
expect(Carousel).toBeTruthy(); |
||||||
|
}); |
||||||
|
}); |
@ -0,0 +1,165 @@ |
|||||||
|
import React, { MouseEvent } from 'react'; |
||||||
|
import { render, cleanup, fireEvent } from '@testing-library/react'; |
||||||
|
import { ItemProvider, ItemProviderProps } from '../src/components/item'; |
||||||
|
import { defaultProps } from '../src/components/carousel/defaultProps'; |
||||||
|
import * as helpers from '../src/helpers'; |
||||||
|
import { carouselItemNodes } from './__fixtures__/nodes'; |
||||||
|
|
||||||
|
describe('<ItemProvider />', () => { |
||||||
|
const defaultItemProviderProps: ItemProviderProps = { |
||||||
|
...defaultProps, |
||||||
|
items: carouselItemNodes(6), |
||||||
|
widthCallBack: jest.fn(), |
||||||
|
dragCallback: jest.fn(), |
||||||
|
slideCallback: jest.fn(), |
||||||
|
transition: 0, |
||||||
|
transform: 0, |
||||||
|
}; |
||||||
|
let mockGetPageX: jest.SpyInstance< |
||||||
|
number, |
||||||
|
[React.TouchEvent<Element> | React.MouseEvent<Element, globalThis.MouseEvent>] |
||||||
|
>; |
||||||
|
|
||||||
|
afterEach(() => { |
||||||
|
mockGetPageX.mockRestore(); |
||||||
|
jest.resetAllMocks(); |
||||||
|
cleanup(); |
||||||
|
}); |
||||||
|
|
||||||
|
beforeEach(() => { |
||||||
|
Element.prototype.getBoundingClientRect = jest.fn(() => { |
||||||
|
return { |
||||||
|
width: 900, |
||||||
|
height: 600, |
||||||
|
top: 0, |
||||||
|
left: 0, |
||||||
|
bottom: 0, |
||||||
|
right: 0, |
||||||
|
x: 0, |
||||||
|
y: 0, |
||||||
|
toJSON: jest.fn(), |
||||||
|
}; |
||||||
|
}); |
||||||
|
mockGetPageX = jest |
||||||
|
.spyOn(helpers, 'getPageX') |
||||||
|
.mockImplementation((_: MouseEvent) => 600); |
||||||
|
}); |
||||||
|
|
||||||
|
it('should call dragCallback when user swipes less than item width', async () => { |
||||||
|
const { getByTestId } = render( |
||||||
|
<ItemProvider {...defaultItemProviderProps} show={3} swiping={true} />, |
||||||
|
); |
||||||
|
const trackList = getByTestId('trackList'); |
||||||
|
mockGetPageX = jest |
||||||
|
.spyOn(helpers, 'getPageX') |
||||||
|
.mockImplementation((_: MouseEvent) => 600); |
||||||
|
fireEvent.mouseDown(trackList, { pageX: 600 }); |
||||||
|
|
||||||
|
mockGetPageX = jest |
||||||
|
.spyOn(helpers, 'getPageX') |
||||||
|
.mockImplementation((_: MouseEvent) => 390); |
||||||
|
fireEvent.mouseMove(trackList, { pageX: 390 }); |
||||||
|
|
||||||
|
fireEvent.mouseUp(trackList, { pageX: 390 }); |
||||||
|
|
||||||
|
expect(defaultItemProviderProps.dragCallback).toHaveBeenCalledTimes(1); |
||||||
|
}); |
||||||
|
|
||||||
|
it('should call slideCallback when user swipes bigger than item width', async () => { |
||||||
|
const { getByTestId } = render( |
||||||
|
<ItemProvider {...defaultItemProviderProps} show={3} swiping={true} />, |
||||||
|
); |
||||||
|
const trackList = getByTestId('trackList'); |
||||||
|
mockGetPageX = jest |
||||||
|
.spyOn(helpers, 'getPageX') |
||||||
|
.mockImplementation((_: MouseEvent) => 600); |
||||||
|
fireEvent.mouseDown(trackList, { pageX: 600 }); |
||||||
|
|
||||||
|
mockGetPageX = jest |
||||||
|
.spyOn(helpers, 'getPageX') |
||||||
|
.mockImplementation((_: MouseEvent) => 290); |
||||||
|
fireEvent.mouseMove(trackList, { pageX: 290 }); |
||||||
|
|
||||||
|
fireEvent.mouseUp(trackList, { pageX: 290 }); |
||||||
|
|
||||||
|
expect(defaultItemProviderProps.slideCallback).toHaveBeenCalledTimes(1); |
||||||
|
}); |
||||||
|
|
||||||
|
it('should slide to left if drag value bigger than zero', async () => { |
||||||
|
const { getByTestId } = render( |
||||||
|
<ItemProvider {...defaultItemProviderProps} show={3} swiping={true} />, |
||||||
|
); |
||||||
|
const trackList = getByTestId('trackList'); |
||||||
|
mockGetPageX = jest |
||||||
|
.spyOn(helpers, 'getPageX') |
||||||
|
.mockImplementation((_: MouseEvent) => 600); |
||||||
|
fireEvent.mouseDown(trackList, { pageX: 600 }); |
||||||
|
|
||||||
|
mockGetPageX = jest |
||||||
|
.spyOn(helpers, 'getPageX') |
||||||
|
.mockImplementation((_: MouseEvent) => 910); |
||||||
|
fireEvent.mouseMove(trackList, { pageX: 910 }); |
||||||
|
|
||||||
|
fireEvent.mouseUp(trackList, { pageX: 910 }); |
||||||
|
|
||||||
|
expect(defaultItemProviderProps.slideCallback).toHaveBeenCalledTimes(1); |
||||||
|
}); |
||||||
|
|
||||||
|
it("should't call neither drag or slide callbacks when not swiping", async () => { |
||||||
|
const { getByTestId } = render( |
||||||
|
<ItemProvider {...defaultItemProviderProps} show={3} swiping={true} />, |
||||||
|
); |
||||||
|
const trackList = getByTestId('trackList'); |
||||||
|
|
||||||
|
mockGetPageX = jest |
||||||
|
.spyOn(helpers, 'getPageX') |
||||||
|
.mockImplementation((_: MouseEvent) => 290); |
||||||
|
fireEvent.mouseMove(trackList, { pageX: 290 }); |
||||||
|
|
||||||
|
expect(defaultItemProviderProps.slideCallback).toHaveBeenCalledTimes(0); |
||||||
|
expect(defaultItemProviderProps.dragCallback).toHaveBeenCalledTimes(0); |
||||||
|
}); |
||||||
|
|
||||||
|
it("should't call neither drag or slide callbacks when mouse leave and not swiping", async () => { |
||||||
|
const { getByTestId } = render( |
||||||
|
<ItemProvider {...defaultItemProviderProps} show={3} swiping={true} />, |
||||||
|
); |
||||||
|
const trackList = getByTestId('trackList'); |
||||||
|
|
||||||
|
mockGetPageX = jest |
||||||
|
.spyOn(helpers, 'getPageX') |
||||||
|
.mockImplementation((_: MouseEvent) => 290); |
||||||
|
fireEvent.mouseUp(trackList, { pageX: 290 }); |
||||||
|
|
||||||
|
expect(defaultItemProviderProps.slideCallback).toHaveBeenCalledTimes(0); |
||||||
|
expect(defaultItemProviderProps.dragCallback).toHaveBeenCalledTimes(0); |
||||||
|
}); |
||||||
|
|
||||||
|
it("should't listen mouse or touch event when swipiwing option false", async () => { |
||||||
|
const { getByTestId } = render( |
||||||
|
<ItemProvider {...defaultItemProviderProps} swiping={false} />, |
||||||
|
); |
||||||
|
const trackList = getByTestId('trackList'); |
||||||
|
mockGetPageX = jest |
||||||
|
.spyOn(helpers, 'getPageX') |
||||||
|
.mockImplementation((_: MouseEvent) => 600); |
||||||
|
fireEvent.mouseDown(trackList, { pageX: 600 }); |
||||||
|
|
||||||
|
mockGetPageX = jest |
||||||
|
.spyOn(helpers, 'getPageX') |
||||||
|
.mockImplementation((_: MouseEvent) => 390); |
||||||
|
fireEvent.mouseMove(trackList, { pageX: 390 }); |
||||||
|
|
||||||
|
fireEvent.mouseUp(trackList, { pageX: 390 }); |
||||||
|
|
||||||
|
expect(defaultItemProviderProps.dragCallback).toHaveBeenCalledTimes(0); |
||||||
|
expect(defaultItemProviderProps.slideCallback).toHaveBeenCalledTimes(0); |
||||||
|
}); |
||||||
|
|
||||||
|
it("shouldn't call useWindowWidthCahnge hook when responsive option false", async () => { |
||||||
|
render(<ItemProvider {...defaultItemProviderProps} responsive={true} />); |
||||||
|
|
||||||
|
Object.defineProperty(window, 'innerWidth', { value: 5 }); |
||||||
|
fireEvent(window, new Event('resize')); |
||||||
|
}); |
||||||
|
}); |
@ -0,0 +1,18 @@ |
|||||||
|
version: '3' |
||||||
|
|
||||||
|
services: |
||||||
|
docusaurus: |
||||||
|
build: . |
||||||
|
ports: |
||||||
|
- 3000:3000 |
||||||
|
- 35729:35729 |
||||||
|
volumes: |
||||||
|
- ./docs:/app/docs |
||||||
|
- ./website/blog:/app/website/blog |
||||||
|
- ./website/core:/app/website/core |
||||||
|
- ./website/i18n:/app/website/i18n |
||||||
|
- ./website/pages:/app/website/pages |
||||||
|
- ./website/static:/app/website/static |
||||||
|
- ./website/sidebars.json:/app/website/sidebars.json |
||||||
|
- ./website/siteConfig.js:/app/website/siteConfig.js |
||||||
|
working_dir: /app/website |
@ -0,0 +1,21 @@ |
|||||||
|
module.exports = { |
||||||
|
coverageDirectory: 'coverage', |
||||||
|
collectCoverage: true, |
||||||
|
collectCoverageFrom: ['src/**/*.{ts,tsx}'], |
||||||
|
coverageThreshold: { |
||||||
|
global: { |
||||||
|
branches: 100, |
||||||
|
functions: 100, |
||||||
|
lines: 100, |
||||||
|
statements: 100, |
||||||
|
}, |
||||||
|
}, |
||||||
|
testPathIgnorePatterns: ['<rootDir>/__tests__/__fixtures__/'], |
||||||
|
transform: { |
||||||
|
'^.+\\.tsx?$': 'ts-jest', |
||||||
|
'^.+\\.css$': 'jest-transform-css', |
||||||
|
}, |
||||||
|
setupFilesAfterEnv: ['@testing-library/jest-dom/extend-expect'], |
||||||
|
testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.tsx?$', |
||||||
|
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], |
||||||
|
}; |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,84 @@ |
|||||||
|
{ |
||||||
|
"name": "@trendyol/react-carousel", |
||||||
|
"version": "1.0.0", |
||||||
|
"description": "Lightweight carousel component for react", |
||||||
|
"main": "dist/cjs/index.js", |
||||||
|
"module": "dist/es/index.js", |
||||||
|
"jsnext:main": "dist/es/index.js", |
||||||
|
"types": "dist/types/index.d.ts", |
||||||
|
"homepage": "https://trendyol.github.io/react-carousel", |
||||||
|
"repository": "github:Trendyol/carousel", |
||||||
|
"bugs": "https://github.com/Trendyol/react-carousel/issues", |
||||||
|
"scripts": { |
||||||
|
"build": "rollup -c --environment BUILD:production", |
||||||
|
"dev": "rollup -c -w --environment BUILD:development", |
||||||
|
"publish": "npm publish", |
||||||
|
"fmt": "prettier --write 'src/**/*.{ts,tsx,css}' *.{js,json,md} && npm run lint:fix", |
||||||
|
"lint": "tslint -t verbose -c tslint.json 'src/**/*.{ts,tsx}'", |
||||||
|
"lint:fix": "tslint -t verbose -c tslint.json --fix 'src/**/*.{ts,tsx}'", |
||||||
|
"lint:staged": "pretty-quick --staged && lint-staged", |
||||||
|
"test": "jest", |
||||||
|
"security": "npm audit" |
||||||
|
}, |
||||||
|
"lint-staged": { |
||||||
|
"*.{ts,tsx}": "npm run fmt" |
||||||
|
}, |
||||||
|
"husky": { |
||||||
|
"hooks": { |
||||||
|
"pre-commit": "npm lint:staged", |
||||||
|
"pre-push": "npm test" |
||||||
|
} |
||||||
|
}, |
||||||
|
"keywords": [ |
||||||
|
"carousel", |
||||||
|
"react", |
||||||
|
"slider" |
||||||
|
], |
||||||
|
"authors": [ |
||||||
|
"Hasan Genc <hasan.genc@trendyol.com> (https://github.com/hasangenc0)" |
||||||
|
], |
||||||
|
"license": "MIT", |
||||||
|
"peerDependencies": { |
||||||
|
"react": "^16.13.1" |
||||||
|
}, |
||||||
|
"devDependencies": { |
||||||
|
"@rollup/plugin-replace": "^2.3.1", |
||||||
|
"@testing-library/jest-dom": "^5.3.0", |
||||||
|
"@testing-library/react": "^10.0.2", |
||||||
|
"@types/jest": "^25.2.1", |
||||||
|
"@types/react": "^16.9.26", |
||||||
|
"autoprefixer": "^9.7.5", |
||||||
|
"eslint": "^6.8.0", |
||||||
|
"husky": "^4.2.3", |
||||||
|
"jest": "^25.2.7", |
||||||
|
"jest-transform-css": "^2.0.0", |
||||||
|
"lint-staged": "^10.0.9", |
||||||
|
"prettier": "^2.0.2", |
||||||
|
"pretty-quick": "^2.0.1", |
||||||
|
"react": "^16.13.1", |
||||||
|
"react-dom": "^16.13.1", |
||||||
|
"rollup": "^2.3.3", |
||||||
|
"rollup-plugin-peer-deps-external": "^2.2.2", |
||||||
|
"rollup-plugin-postcss-modules": "^2.0.1", |
||||||
|
"rollup-plugin-terser": "^5.3.0", |
||||||
|
"rollup-plugin-typescript2": "^0.27.0", |
||||||
|
"ts-jest": "^25.3.1", |
||||||
|
"tslint": "^6.1.0", |
||||||
|
"tslint-plugin-prettier": "^2.3.0", |
||||||
|
"tslint-react-hooks": "^2.2.2", |
||||||
|
"typescript": "^3.8.3" |
||||||
|
}, |
||||||
|
"sideEffects": false, |
||||||
|
"browserslist": { |
||||||
|
"production": [ |
||||||
|
">0.2%", |
||||||
|
"not dead", |
||||||
|
"not op_mini all" |
||||||
|
], |
||||||
|
"development": [ |
||||||
|
"last 1 chrome version", |
||||||
|
"last 1 firefox version", |
||||||
|
"last 1 safari version" |
||||||
|
] |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,40 @@ |
|||||||
|
import peerDepsExternal from 'rollup-plugin-peer-deps-external'; |
||||||
|
import postcss from 'rollup-plugin-postcss-modules'; |
||||||
|
import pkg from './package.json'; |
||||||
|
import typescript from 'rollup-plugin-typescript2'; |
||||||
|
import autoprefixer from 'autoprefixer'; |
||||||
|
import { terser } from 'rollup-plugin-terser'; |
||||||
|
import replace from '@rollup/plugin-replace'; |
||||||
|
|
||||||
|
const isProduction = process.env.BUILD === 'production'; |
||||||
|
|
||||||
|
export default { |
||||||
|
input: 'src/index.ts', |
||||||
|
output: [ |
||||||
|
{ |
||||||
|
file: pkg.main, |
||||||
|
format: 'cjs', |
||||||
|
sourcemap: !isProduction, |
||||||
|
}, |
||||||
|
{ |
||||||
|
file: pkg.module, |
||||||
|
format: 'es', |
||||||
|
sourcemap: !isProduction, |
||||||
|
}, |
||||||
|
], |
||||||
|
plugins: [ |
||||||
|
peerDepsExternal(), |
||||||
|
postcss({ |
||||||
|
extract: false, |
||||||
|
modules: true, |
||||||
|
plugins: [autoprefixer()], |
||||||
|
writeDefinitions: true, |
||||||
|
}), |
||||||
|
typescript({ useTsconfigDeclarationDir: true }), |
||||||
|
isProduction && replace({ 'data-testid': '' }), |
||||||
|
isProduction && terser(), |
||||||
|
], |
||||||
|
external: { |
||||||
|
react: 'react', |
||||||
|
}, |
||||||
|
}; |
@ -0,0 +1,9 @@ |
|||||||
|
import React, { FunctionComponent } from 'react'; |
||||||
|
import styles from '../../styles/styles.module.css'; |
||||||
|
|
||||||
|
export const Arrow: FunctionComponent<ArrowProps> = (props: ArrowProps) => ( |
||||||
|
<button className={styles.carouselArrow} onClick={props.onClick}></button> |
||||||
|
); |
||||||
|
export interface ArrowProps { |
||||||
|
onClick: (...args: any) => any; |
||||||
|
} |
@ -0,0 +1,15 @@ |
|||||||
|
import { CarouselProps } from '.'; |
||||||
|
|
||||||
|
export const defaultProps: Required<CarouselProps> = { |
||||||
|
children: [], |
||||||
|
show: 1, |
||||||
|
slide: 1, |
||||||
|
transition: 0.5, |
||||||
|
swiping: false, |
||||||
|
swipeOn: 1, |
||||||
|
responsive: false, |
||||||
|
infinite: true, |
||||||
|
className: '', |
||||||
|
useArrowKeys: false, |
||||||
|
a11y: {}, |
||||||
|
}; |
@ -0,0 +1,152 @@ |
|||||||
|
import React, { useState, FunctionComponent, KeyboardEvent } from 'react'; |
||||||
|
import { Arrow } from '../arrow'; |
||||||
|
import { ItemProvider } from '../item'; |
||||||
|
import { |
||||||
|
rotateItems, |
||||||
|
getTransformAmount, |
||||||
|
getCurrent, |
||||||
|
initItems, |
||||||
|
getShowArrow, |
||||||
|
cleanItems, |
||||||
|
} from '../../helpers'; |
||||||
|
import { SlideDirection, Item, ArrowKeys } from '../../types/carousel'; |
||||||
|
import { defaultProps } from './defaultProps'; |
||||||
|
import styles from '../../styles/styles.module.css'; |
||||||
|
|
||||||
|
export const Carousel: FunctionComponent<CarouselProps> = (userProps: CarouselProps) => { |
||||||
|
const props: Required<CarouselProps> = { ...defaultProps, ...userProps }; |
||||||
|
const [items, setItems] = useState( |
||||||
|
initItems(props.children, props.slide, props.infinite), |
||||||
|
); |
||||||
|
const [width, setWidth] = useState(0); |
||||||
|
const [animation, setAnimation] = useState({ |
||||||
|
transform: 0, |
||||||
|
transition: 0, |
||||||
|
isSliding: false, |
||||||
|
}); |
||||||
|
const [current, setCurrent] = useState(0); |
||||||
|
const [showArrow, setShowArrow] = useState( |
||||||
|
getShowArrow(props.children.length, props.show, props.infinite, current), |
||||||
|
); |
||||||
|
|
||||||
|
const slide = (direction: SlideDirection, slide: number): void => { |
||||||
|
if ( |
||||||
|
animation.isSliding || |
||||||
|
(direction === SlideDirection.Right && !showArrow.right) || |
||||||
|
(direction === SlideDirection.Left && !showArrow.left) |
||||||
|
) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
const next = getCurrent(current, slide, props.children.length, direction); |
||||||
|
const rotated = props.infinite |
||||||
|
? rotateItems(props.children, items, next, props.show, slide, direction) |
||||||
|
: items; |
||||||
|
if (props.infinite && direction === SlideDirection.Right) { |
||||||
|
setItems(rotated); |
||||||
|
} |
||||||
|
setAnimation({ |
||||||
|
transform: animation.transform + getTransformAmount(width, slide, direction), |
||||||
|
transition: props.transition, |
||||||
|
isSliding: true, |
||||||
|
}); |
||||||
|
setCurrent(next); |
||||||
|
setShowArrow(getShowArrow(props.children.length, props.show, props.infinite, next)); |
||||||
|
setTimeout(() => { |
||||||
|
if (props.infinite) { |
||||||
|
setItems(cleanItems(rotated, slide, direction)); |
||||||
|
} |
||||||
|
setAnimation({ |
||||||
|
transform: props.infinite |
||||||
|
? getTransformAmount(width, slide, SlideDirection.Right) |
||||||
|
: animation.transform + getTransformAmount(width, slide, direction), |
||||||
|
transition: 0, |
||||||
|
isSliding: false, |
||||||
|
}); |
||||||
|
}, props.transition * 1_0_0_0); |
||||||
|
}; |
||||||
|
|
||||||
|
const widthCallBack = (calculatedWidth: number, slide: number) => { |
||||||
|
setWidth(calculatedWidth); |
||||||
|
setAnimation({ |
||||||
|
transform: props.infinite |
||||||
|
? getTransformAmount(calculatedWidth, slide, SlideDirection.Right) |
||||||
|
: 0, |
||||||
|
transition: 0, |
||||||
|
isSliding: false, |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
const dragCallback = (translateX: number) => { |
||||||
|
setAnimation({ |
||||||
|
transform: translateX, |
||||||
|
transition: props.transition, |
||||||
|
isSliding: false, |
||||||
|
}); |
||||||
|
setTimeout( |
||||||
|
() => setAnimation({ ...animation, transition: 0 }), |
||||||
|
props.transition * 1_0_0_0, |
||||||
|
); |
||||||
|
}; |
||||||
|
|
||||||
|
const slideCallback = (direction: SlideDirection) => { |
||||||
|
slide(direction, props.slide); |
||||||
|
}; |
||||||
|
|
||||||
|
const handleOnKeyDown = (e: KeyboardEvent) => { |
||||||
|
if (e.keyCode === ArrowKeys.Left) { |
||||||
|
slide(SlideDirection.Left, props.slide); |
||||||
|
} else if (e.keyCode === ArrowKeys.Right) { |
||||||
|
slide(SlideDirection.Right, props.slide); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
return ( |
||||||
|
<div |
||||||
|
{...props.a11y} |
||||||
|
data-testid="carousel" |
||||||
|
tabIndex={0} |
||||||
|
{...(props.useArrowKeys ? { onKeyDown: handleOnKeyDown } : {})} |
||||||
|
className={`${styles.carouselBase} ${props.className}`} |
||||||
|
> |
||||||
|
{showArrow.left && ( |
||||||
|
<Arrow onClick={() => slide(SlideDirection.Left, props.slide)} /> |
||||||
|
)} |
||||||
|
<ItemProvider |
||||||
|
{...props} |
||||||
|
transition={animation.transition} |
||||||
|
items={items} |
||||||
|
transform={animation.transform} |
||||||
|
slideCallback={slideCallback} |
||||||
|
dragCallback={dragCallback} |
||||||
|
widthCallBack={widthCallBack} |
||||||
|
/> |
||||||
|
{showArrow.right && ( |
||||||
|
<Arrow onClick={() => slide(SlideDirection.Right, props.slide)} /> |
||||||
|
)} |
||||||
|
</div> |
||||||
|
); |
||||||
|
}; |
||||||
|
|
||||||
|
export interface CarouselProps { |
||||||
|
children: Item[]; |
||||||
|
show: number; |
||||||
|
slide: number; |
||||||
|
transition?: number; |
||||||
|
swiping?: boolean; |
||||||
|
swipeOn?: number; |
||||||
|
responsive?: boolean; |
||||||
|
infinite?: boolean; |
||||||
|
className?: string; |
||||||
|
useArrowKeys?: boolean; |
||||||
|
a11y?: { [key: string]: string }; |
||||||
|
} |
||||||
|
|
||||||
|
export interface CarouselState { |
||||||
|
items: Item[]; |
||||||
|
width: number; |
||||||
|
transform: number; |
||||||
|
transition: number; |
||||||
|
isSliding: boolean; |
||||||
|
current: number; |
||||||
|
} |
@ -0,0 +1,133 @@ |
|||||||
|
import React, { |
||||||
|
FunctionComponent, |
||||||
|
useCallback, |
||||||
|
useState, |
||||||
|
MouseEvent, |
||||||
|
TouchEvent, |
||||||
|
} from 'react'; |
||||||
|
import { Item, SlideDirection } from '../../types/carousel'; |
||||||
|
import { getPageX } from '../../helpers'; |
||||||
|
import { useWindowWidthChange } from '../../hooks'; |
||||||
|
import styles from '../../styles/styles.module.css'; |
||||||
|
|
||||||
|
export const ItemProviderBase: FunctionComponent<ItemProviderProps> = ( |
||||||
|
props: ItemProviderProps, |
||||||
|
) => { |
||||||
|
const [width, setWidth] = useState(200); |
||||||
|
const ref = useCallback( |
||||||
|
(node) => { |
||||||
|
if (node !== null) { |
||||||
|
const calculated = node.getBoundingClientRect().width / props.show; |
||||||
|
setWidth(calculated); |
||||||
|
props.widthCallBack(calculated, props.slide); |
||||||
|
} |
||||||
|
}, |
||||||
|
[width], |
||||||
|
); |
||||||
|
|
||||||
|
// tslint:disable-next-line: no-unused-expression
|
||||||
|
props.responsive && |
||||||
|
useWindowWidthChange((change: number) => { |
||||||
|
setWidth(width - change); |
||||||
|
}); |
||||||
|
const [drag, setDrag] = useState({ |
||||||
|
initial: props.transform, |
||||||
|
start: 0, |
||||||
|
isDown: false, |
||||||
|
drag: 0, |
||||||
|
finished: true, |
||||||
|
pointers: true, |
||||||
|
}); |
||||||
|
const handleDragStart = (e: MouseEvent | TouchEvent) => { |
||||||
|
e.persist(); |
||||||
|
setDrag({ |
||||||
|
...drag, |
||||||
|
isDown: true, |
||||||
|
start: getPageX(e), |
||||||
|
initial: props.transform, |
||||||
|
finished: false, |
||||||
|
}); |
||||||
|
}; |
||||||
|
const handleDragFinish = (e: MouseEvent | TouchEvent) => { |
||||||
|
e.persist(); |
||||||
|
if (drag.finished) { |
||||||
|
return; |
||||||
|
} |
||||||
|
if (Math.abs(drag.drag) < width * props.swipeOn) { |
||||||
|
props.dragCallback(props.transform); |
||||||
|
return setDrag({ |
||||||
|
initial: props.transform, |
||||||
|
start: 0, |
||||||
|
isDown: false, |
||||||
|
drag: 0, |
||||||
|
finished: true, |
||||||
|
pointers: true, |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
props.slideCallback(drag.drag > 0 ? SlideDirection.Right : SlideDirection.Left); |
||||||
|
setDrag({ ...drag, drag: 0, isDown: false, finished: true, pointers: true }); |
||||||
|
return; |
||||||
|
}; |
||||||
|
const handleDragMove = (e: MouseEvent | TouchEvent) => { |
||||||
|
e.persist(); |
||||||
|
if (!drag.isDown) { |
||||||
|
return; |
||||||
|
} |
||||||
|
const pos = getPageX(e); |
||||||
|
setDrag({ ...drag, drag: drag.start - pos, pointers: false }); |
||||||
|
}; |
||||||
|
const swipeProps = props.swiping |
||||||
|
? { |
||||||
|
onTouchCancel: handleDragFinish, |
||||||
|
onTouchEnd: handleDragFinish, |
||||||
|
onTouchMove: handleDragMove, |
||||||
|
onTouchStart: handleDragStart, |
||||||
|
onMouseDown: handleDragStart, |
||||||
|
onMouseLeave: handleDragFinish, |
||||||
|
onMouseUp: handleDragFinish, |
||||||
|
onMouseMove: handleDragMove, |
||||||
|
} |
||||||
|
: {}; |
||||||
|
|
||||||
|
return ( |
||||||
|
<div ref={ref} className={styles.itemProvider}> |
||||||
|
<div |
||||||
|
data-testid="trackList" |
||||||
|
{...swipeProps} |
||||||
|
className={styles.itemTracker} |
||||||
|
style={{ |
||||||
|
transform: `translateX(${props.transform - drag.drag}px)`, |
||||||
|
transition: `transform ${props.transition}s ease 0s`, |
||||||
|
width: width * props.items.length, |
||||||
|
}} |
||||||
|
> |
||||||
|
{props.items.map((item, i) => ( |
||||||
|
<div |
||||||
|
key={i} |
||||||
|
style={{ width, pointerEvents: drag.pointers ? 'all' : 'none' }} |
||||||
|
className={styles.itemContainer} |
||||||
|
> |
||||||
|
{item} |
||||||
|
</div> |
||||||
|
))} |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
); |
||||||
|
}; |
||||||
|
|
||||||
|
export const ItemProvider = React.memo(ItemProviderBase); |
||||||
|
export interface ItemProviderProps { |
||||||
|
items: Item[]; |
||||||
|
show: number; |
||||||
|
slide: number; |
||||||
|
widthCallBack: (width: number, slide: number) => void; |
||||||
|
dragCallback: (transform: number) => void; |
||||||
|
slideCallback: (direction: SlideDirection) => void; |
||||||
|
transition: number; |
||||||
|
transform: number; |
||||||
|
swiping: boolean; |
||||||
|
swipeOn: number; |
||||||
|
responsive: boolean; |
||||||
|
infinite: boolean; |
||||||
|
} |
@ -0,0 +1,135 @@ |
|||||||
|
import { MouseEvent, TouchEvent } from 'react'; |
||||||
|
import { SlideDirection, Item } from '../types/carousel'; |
||||||
|
|
||||||
|
export class Circular<T> { |
||||||
|
constructor(private arr: T[], private currentIndex: number) {} |
||||||
|
|
||||||
|
next(): T { |
||||||
|
const i = this.currentIndex; |
||||||
|
const arr = this.arr; |
||||||
|
this.currentIndex = i < arr.length - 1 ? i + 1 : 0; |
||||||
|
return this.current(); |
||||||
|
} |
||||||
|
|
||||||
|
prev(): T { |
||||||
|
const i = this.currentIndex; |
||||||
|
const arr = this.arr; |
||||||
|
this.currentIndex = i > 0 ? i - 1 : arr.length - 1; |
||||||
|
return this.current(); |
||||||
|
} |
||||||
|
|
||||||
|
current(): T { |
||||||
|
return this.arr[this.currentIndex]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const rotateItems = ( |
||||||
|
items: any[], |
||||||
|
showingItems: any[], |
||||||
|
start: number, |
||||||
|
show: number, |
||||||
|
slide: number, |
||||||
|
direction: SlideDirection, |
||||||
|
): any[] => { |
||||||
|
const circular = new Circular(items, start); |
||||||
|
const newItems: any[] = Array.from(showingItems); |
||||||
|
|
||||||
|
switch (+direction) { |
||||||
|
case SlideDirection.Left: |
||||||
|
for (let i = slide; i >= 0; i--) { |
||||||
|
if (slide - i < 0 || !newItems[i - slide]) { |
||||||
|
newItems.unshift(circular.current()); |
||||||
|
} |
||||||
|
circular.prev(); |
||||||
|
} |
||||||
|
break; |
||||||
|
case SlideDirection.Right: |
||||||
|
for (let i = 0; i < show + slide; i++) { |
||||||
|
if (!newItems[2 * slide + i]) { |
||||||
|
newItems.push(circular.current()); |
||||||
|
} |
||||||
|
circular.next(); |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
return newItems; |
||||||
|
}; |
||||||
|
|
||||||
|
export const getTransformAmount = ( |
||||||
|
width: number, |
||||||
|
slideCount: number, |
||||||
|
direction: SlideDirection, |
||||||
|
): number => { |
||||||
|
return direction * width * slideCount; |
||||||
|
}; |
||||||
|
|
||||||
|
export const getCurrent = ( |
||||||
|
current: number, |
||||||
|
slide: number, |
||||||
|
length: number, |
||||||
|
direction: SlideDirection, |
||||||
|
) => { |
||||||
|
const slideTo = current - direction * slide; |
||||||
|
if (slideTo < 0) { |
||||||
|
return length + slideTo; |
||||||
|
} else if (length <= slideTo) { |
||||||
|
return slideTo - length; |
||||||
|
} |
||||||
|
|
||||||
|
return slideTo; |
||||||
|
}; |
||||||
|
|
||||||
|
export const getShowArrow = ( |
||||||
|
items: number, |
||||||
|
show: number, |
||||||
|
infinite: boolean, |
||||||
|
current: number, |
||||||
|
): { left: boolean; right: boolean } => { |
||||||
|
const isItemsMore = items > show; |
||||||
|
if (infinite) { |
||||||
|
return { |
||||||
|
left: isItemsMore, |
||||||
|
right: isItemsMore, |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
return { |
||||||
|
left: isItemsMore && current !== 0, |
||||||
|
right: isItemsMore && current + show < items, |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
export const cleanItems = ( |
||||||
|
showingItems: any[], |
||||||
|
slide: number, |
||||||
|
direction: SlideDirection, |
||||||
|
): any[] => { |
||||||
|
if (direction === SlideDirection.Left) { |
||||||
|
return showingItems.slice(0, -1 * slide); |
||||||
|
} |
||||||
|
return showingItems.slice(slide); |
||||||
|
}; |
||||||
|
|
||||||
|
export const initItems = (items: Item[], slide: number, infinite: boolean): Item[] => { |
||||||
|
if (!infinite) { |
||||||
|
return items; |
||||||
|
} |
||||||
|
|
||||||
|
const newArray = Array.from(items); |
||||||
|
const circular = new Circular(items, 0); |
||||||
|
for (let i = 0; i < slide; i++) { |
||||||
|
newArray.unshift(circular.prev()); |
||||||
|
} |
||||||
|
|
||||||
|
return newArray; |
||||||
|
}; |
||||||
|
|
||||||
|
export function getPageX(e: TouchEvent | MouseEvent): number { |
||||||
|
if (e.nativeEvent instanceof MouseEvent) { |
||||||
|
return e.nativeEvent.pageX; |
||||||
|
} else if (e.nativeEvent instanceof TouchEvent) { |
||||||
|
return e.nativeEvent.changedTouches[0].pageX; |
||||||
|
} |
||||||
|
return 0; |
||||||
|
} |
@ -0,0 +1,15 @@ |
|||||||
|
import { useLayoutEffect, useState } from 'react'; |
||||||
|
|
||||||
|
export const useWindowWidthChange = (callBack: (changed: number) => any) => { |
||||||
|
const [windowWidth, setWindowWidth] = useState(window.innerWidth); |
||||||
|
useLayoutEffect(() => { |
||||||
|
const update = () => { |
||||||
|
const changed = windowWidth - window.innerWidth; |
||||||
|
setWindowWidth(window.innerWidth); |
||||||
|
callBack(changed); |
||||||
|
}; |
||||||
|
window.addEventListener('resize', update); |
||||||
|
return () => window.removeEventListener('resize', update); |
||||||
|
}, []); |
||||||
|
return; |
||||||
|
}; |
@ -0,0 +1,3 @@ |
|||||||
|
import { Carousel } from './components/carousel'; |
||||||
|
|
||||||
|
export { Carousel }; |
@ -0,0 +1,26 @@ |
|||||||
|
.carousel-base { |
||||||
|
width: 100%; |
||||||
|
box-sizing: border-box; |
||||||
|
display: flex; |
||||||
|
outline: none; |
||||||
|
} |
||||||
|
|
||||||
|
.item-provider { |
||||||
|
overflow: hidden; |
||||||
|
width: 100%; |
||||||
|
cursor: pointer; |
||||||
|
} |
||||||
|
|
||||||
|
.item-container img { |
||||||
|
user-select: none; |
||||||
|
-webkit-user-drag: none; |
||||||
|
} |
||||||
|
|
||||||
|
.item-tracker { |
||||||
|
height: 100%; |
||||||
|
display: flex; |
||||||
|
} |
||||||
|
|
||||||
|
.carousel-arrow { |
||||||
|
z-index: 1; |
||||||
|
} |
@ -0,0 +1,19 @@ |
|||||||
|
export const carouselBase: string; |
||||||
|
export const itemProvider: string; |
||||||
|
export const itemContainer: string; |
||||||
|
export const itemTracker: string; |
||||||
|
export const carouselArrow: string; |
||||||
|
interface Namespace { |
||||||
|
carouselBase: string; |
||||||
|
'carousel-base': string; |
||||||
|
itemProvider: string; |
||||||
|
'item-provider': string; |
||||||
|
itemContainer: string; |
||||||
|
'item-container': string; |
||||||
|
itemTracker: string; |
||||||
|
'item-tracker': string; |
||||||
|
carouselArrow: string; |
||||||
|
'carousel-arrow': string; |
||||||
|
} |
||||||
|
declare const stylesModule: Namespace; |
||||||
|
export default stylesModule; |
@ -0,0 +1,13 @@ |
|||||||
|
import { ReactElement } from 'react'; |
||||||
|
|
||||||
|
export enum SlideDirection { |
||||||
|
Right = -1, |
||||||
|
Left = 1, |
||||||
|
} |
||||||
|
|
||||||
|
export const enum ArrowKeys { |
||||||
|
Right = 39, |
||||||
|
Left = 37, |
||||||
|
} |
||||||
|
|
||||||
|
export type Item = ReactElement; |
@ -0,0 +1,26 @@ |
|||||||
|
{ |
||||||
|
"compilerOptions": { |
||||||
|
"outDir": "./dist", |
||||||
|
"module": "esnext", |
||||||
|
"target": "es6", |
||||||
|
"lib": ["es6", "dom", "es2016", "es2017"], |
||||||
|
"sourceMap": true, |
||||||
|
"allowJs": false, |
||||||
|
"jsx": "react", |
||||||
|
"declaration": true, |
||||||
|
"declarationDir": "./dist/types", |
||||||
|
"moduleResolution": "node", |
||||||
|
"forceConsistentCasingInFileNames": true, |
||||||
|
"noImplicitReturns": true, |
||||||
|
"noImplicitThis": true, |
||||||
|
"noImplicitAny": true, |
||||||
|
"strictNullChecks": true, |
||||||
|
"suppressImplicitAnyIndexErrors": true, |
||||||
|
"allowSyntheticDefaultImports": true, |
||||||
|
"noUnusedLocals": true, |
||||||
|
"noUnusedParameters": true, |
||||||
|
"esModuleInterop": true |
||||||
|
}, |
||||||
|
"include": ["src"], |
||||||
|
"exclude": ["node_modules", "dist"] |
||||||
|
} |
@ -0,0 +1,24 @@ |
|||||||
|
{ |
||||||
|
"defaultSeverity": "error", |
||||||
|
"extends": ["tslint:recommended", "tslint-react-hooks"], |
||||||
|
"jsRules": { |
||||||
|
"trailing-comma": false |
||||||
|
}, |
||||||
|
"rules": { |
||||||
|
"interface-name": [true, "never-prefix"], |
||||||
|
"no-console": false, |
||||||
|
"no-shadowed-variable": false, |
||||||
|
"arrow-parens": false, |
||||||
|
"trailing-comma": false, |
||||||
|
"member-access": [true, "no-public"], |
||||||
|
"callable-types": false, |
||||||
|
"no-empty-interface": false, |
||||||
|
"only-arrow-functions": false, |
||||||
|
"variable-name": [true, "allow-leading-underscore", "allow-pascal-case"], |
||||||
|
"object-literal-sort-keys": false |
||||||
|
}, |
||||||
|
"rulesDirectory": [], |
||||||
|
"linterOptions": { |
||||||
|
"exclude": ["./**/node_modules/**"] |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,20 @@ |
|||||||
|
# Dependencies |
||||||
|
/node_modules |
||||||
|
|
||||||
|
# Production |
||||||
|
/build |
||||||
|
|
||||||
|
# Generated files |
||||||
|
.docusaurus |
||||||
|
.cache-loader |
||||||
|
|
||||||
|
# Misc |
||||||
|
.DS_Store |
||||||
|
.env.local |
||||||
|
.env.development.local |
||||||
|
.env.test.local |
||||||
|
.env.production.local |
||||||
|
|
||||||
|
npm-debug.log* |
||||||
|
yarn-debug.log* |
||||||
|
yarn-error.log* |
@ -0,0 +1,33 @@ |
|||||||
|
# Website |
||||||
|
|
||||||
|
This website is built using [Docusaurus 2](https://v2.docusaurus.io/), a modern static website generator. |
||||||
|
|
||||||
|
### Installation |
||||||
|
|
||||||
|
``` |
||||||
|
$ yarn |
||||||
|
``` |
||||||
|
|
||||||
|
### Local Development |
||||||
|
|
||||||
|
``` |
||||||
|
$ yarn start |
||||||
|
``` |
||||||
|
|
||||||
|
This command starts a local development server and open up a browser window. Most changes are reflected live without having to restart the server. |
||||||
|
|
||||||
|
### Build |
||||||
|
|
||||||
|
``` |
||||||
|
$ yarn build |
||||||
|
``` |
||||||
|
|
||||||
|
This command generates static content into the `build` directory and can be served using any static contents hosting service. |
||||||
|
|
||||||
|
### Deployment |
||||||
|
|
||||||
|
``` |
||||||
|
$ GIT_USER=<Your GitHub username> USE_SSH=true yarn deploy |
||||||
|
``` |
||||||
|
|
||||||
|
If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. |
@ -0,0 +1,11 @@ |
|||||||
|
--- |
||||||
|
id: hola |
||||||
|
title: Hola |
||||||
|
author: Gao Wei |
||||||
|
author_title: Docusaurus Core Team |
||||||
|
author_url: https://github.com/wgao19 |
||||||
|
author_image_url: https://avatars1.githubusercontent.com/u/2055384?v=4 |
||||||
|
tags: [hola, docusaurus] |
||||||
|
--- |
||||||
|
|
||||||
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet |
@ -0,0 +1,17 @@ |
|||||||
|
--- |
||||||
|
id: hello-world |
||||||
|
title: Hello |
||||||
|
author: Endilie Yacop Sucipto |
||||||
|
author_title: Maintainer of Docusaurus |
||||||
|
author_url: https://github.com/endiliey |
||||||
|
author_image_url: https://avatars1.githubusercontent.com/u/17883920?s=460&v=4 |
||||||
|
tags: [hello, docusaurus] |
||||||
|
--- |
||||||
|
|
||||||
|
Welcome to this blog. This blog is created with [**Docusaurus 2 alpha**](https://v2.docusaurus.io/). |
||||||
|
|
||||||
|
<!--truncate--> |
||||||
|
|
||||||
|
This is a test post. |
||||||
|
|
||||||
|
A whole bunch of other information. |
@ -0,0 +1,13 @@ |
|||||||
|
--- |
||||||
|
id: welcome |
||||||
|
title: Welcome |
||||||
|
author: Yangshun Tay |
||||||
|
author_title: Front End Engineer @ Facebook |
||||||
|
author_url: https://github.com/yangshun |
||||||
|
author_image_url: https://avatars0.githubusercontent.com/u/1315101?s=400&v=4 |
||||||
|
tags: [facebook, hello, docusaurus] |
||||||
|
--- |
||||||
|
|
||||||
|
Blog features are powered by the blog plugin. Simply add files to the `blog` directory. It supports tags as well! |
||||||
|
|
||||||
|
Delete the whole directory if you don't want the blog features. As simple as that! |
@ -0,0 +1,26 @@ |
|||||||
|
--- |
||||||
|
id: carousel |
||||||
|
title: Carousel |
||||||
|
--- |
||||||
|
|
||||||
|
Creates carousel component. |
||||||
|
|
||||||
|
```jsx |
||||||
|
<Carousel /> |
||||||
|
``` |
||||||
|
|
||||||
|
### Props |
||||||
|
|
||||||
|
| Name | type | required | default | descripiton | |
||||||
|
| ------------ | :-----: | -------: | ------: | --------------------------------------------------------------------------------: | |
||||||
|
| children | Node[] | true | [] | Child items that will be wrapped by carousel | |
||||||
|
| show | number | false | 1 | number of items to show at per slide | |
||||||
|
| slide | number | false | 1 | number of how many items to slide | |
||||||
|
| infinite | boolean | false | true | scrolling infinity | |
||||||
|
| transition | number | false | 0.5 | same as css transition property's second value | |
||||||
|
| swiping | boolean | false | false | enable swiping/dragging with mouse/touch events | |
||||||
|
| swipeOn | number | false | 1 | percantage of item width that slides when user drag count exceeds | |
||||||
|
| responsive | boolean | false | false | enables the feature that adjusts items width according to screen size dynamically | |
||||||
|
| className | string | false | "" | same as react's className property | |
||||||
|
| useArrowKeys | boolean | false | false | enables sliding when press arrow keys | |
||||||
|
| a11y | Array | false | {} | accessibility attributes | |
@ -0,0 +1,33 @@ |
|||||||
|
--- |
||||||
|
id: infinity |
||||||
|
title: Infinite Carousel |
||||||
|
--- |
||||||
|
|
||||||
|
Carousel is infinite at default |
||||||
|
|
||||||
|
import {Carousel} from '@trendyol/react-carousel'; |
||||||
|
export const Highlight = ({children, color}) => ( <span style={{ |
||||||
|
backgroundColor: color, |
||||||
|
borderRadius: '2px', |
||||||
|
color: '#fff', |
||||||
|
padding: '90px 0', |
||||||
|
display: 'block', |
||||||
|
height: '200px', |
||||||
|
margin: '16px 16px 16px 0', |
||||||
|
}}> {children} </span> ); |
||||||
|
|
||||||
|
<Carousel className={'exampleCarousel1'} show={3.5} slide={2} transition={0.5}> |
||||||
|
<Highlight color="#f27a1a">We love Trendyol orange</Highlight> |
||||||
|
<a target="_blank" href="https://github.com/trendyol/"><Highlight color="#d53f8c">This is our github</Highlight></a> |
||||||
|
<Highlight color="#16be48">We love Trendyol green</Highlight> |
||||||
|
<a target="_blank" href="https://trendyol.com/"><Highlight color="#3f51b5">This is our website</Highlight></a> |
||||||
|
</Carousel> |
||||||
|
|
||||||
|
```jsx |
||||||
|
<Carousel show={3.5} slide={2} transition={0.5}> |
||||||
|
<Highlight color="#f27a1a">We love Trendyol orange</Highlight> |
||||||
|
<Highlight color="#d53f8c">This is our github</Highlight> |
||||||
|
<Highlight color="#16be48">We love Trendyol green</Highlight> |
||||||
|
<Highlight color="#3f51b5">This is our website</Highlight> |
||||||
|
</Carousel> |
||||||
|
``` |
@ -0,0 +1,21 @@ |
|||||||
|
--- |
||||||
|
id: installation |
||||||
|
title: Installation Guide |
||||||
|
sidebar_label: Installation Guide |
||||||
|
--- |
||||||
|
|
||||||
|
Carousel requires React 16.8 or greater. |
||||||
|
|
||||||
|
## Installation |
||||||
|
|
||||||
|
``` |
||||||
|
npm i react react-dom @trendyol/react-carousel --save |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## Importing |
||||||
|
|
||||||
|
```jsx |
||||||
|
import { Carousel } from '@trendyol/react-carousel'; |
||||||
|
``` |
@ -0,0 +1,23 @@ |
|||||||
|
--- |
||||||
|
id: usage |
||||||
|
title: Usage |
||||||
|
--- |
||||||
|
|
||||||
|
Simple carousel that show one item per slide. |
||||||
|
|
||||||
|
```jsx |
||||||
|
import React from 'react'; |
||||||
|
import ReactDOM from 'react-dom'; |
||||||
|
import { Carousel } from '@trendyol/react-carousel'; |
||||||
|
import { Item } from './yourItem'; |
||||||
|
|
||||||
|
ReactDOM.render( |
||||||
|
<Carousel> |
||||||
|
<Item /> |
||||||
|
<Item /> |
||||||
|
<Item /> |
||||||
|
<Item /> |
||||||
|
</Carousel>, |
||||||
|
document.getElementById('root'), |
||||||
|
); |
||||||
|
``` |
@ -0,0 +1,90 @@ |
|||||||
|
module.exports = { |
||||||
|
title: 'Carousel', |
||||||
|
tagline: 'Lightweight carousel component for React', |
||||||
|
url: 'https://trendyol.github.io/react-carousel', |
||||||
|
baseUrl: '/', |
||||||
|
favicon: 'img/icon.png', |
||||||
|
organizationName: 'Trendyol', |
||||||
|
projectName: 'carousel', |
||||||
|
themeConfig: { |
||||||
|
navbar: { |
||||||
|
title: 'React Carousel', |
||||||
|
logo: { |
||||||
|
alt: 'Carousel', |
||||||
|
src: 'img/icon.png', |
||||||
|
}, |
||||||
|
links: [ |
||||||
|
{ |
||||||
|
to: 'docs/installation', |
||||||
|
activeBasePath: 'docs', |
||||||
|
label: 'Docs', |
||||||
|
position: 'left', |
||||||
|
}, |
||||||
|
{ |
||||||
|
href: 'https://github.com/trendyol/react-carousel', |
||||||
|
label: 'GitHub', |
||||||
|
position: 'right', |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
footer: { |
||||||
|
style: 'dark', |
||||||
|
links: [ |
||||||
|
{ |
||||||
|
title: 'Docs', |
||||||
|
items: [ |
||||||
|
{ |
||||||
|
label: 'Installation', |
||||||
|
to: 'docs/installation', |
||||||
|
}, |
||||||
|
{ |
||||||
|
label: 'Usage', |
||||||
|
to: 'docs/usage ', |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
{ |
||||||
|
title: 'Communtiy', |
||||||
|
items: [ |
||||||
|
{ |
||||||
|
label: 'GitHub', |
||||||
|
href: 'https://github.com/trendyol/', |
||||||
|
}, |
||||||
|
{ |
||||||
|
label: 'Meetup', |
||||||
|
href: 'https://www.meetup.com/trendyol/', |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
{ |
||||||
|
title: 'Social', |
||||||
|
items: [ |
||||||
|
{ |
||||||
|
label: 'Medium', |
||||||
|
href: 'https://medium.com/trendyol-tech', |
||||||
|
}, |
||||||
|
{ |
||||||
|
label: 'Youtube', |
||||||
|
href: 'https://www.youtube.com/channel/UCUBiayLMggBAsiYvGLzQJ5w/', |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
], |
||||||
|
copyright: `Copyright © ${new Date().getFullYear()} Trendyol Open Source`, |
||||||
|
}, |
||||||
|
}, |
||||||
|
presets: [ |
||||||
|
[ |
||||||
|
'@docusaurus/preset-classic', |
||||||
|
{ |
||||||
|
docs: { |
||||||
|
sidebarPath: require.resolve('./sidebars.js'), |
||||||
|
editUrl: 'https://github.com/trendyol/react-carousel/edit/master/website/', |
||||||
|
}, |
||||||
|
theme: { |
||||||
|
customCss: require.resolve('./src/css/custom.css'), |
||||||
|
}, |
||||||
|
}, |
||||||
|
], |
||||||
|
], |
||||||
|
}; |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,30 @@ |
|||||||
|
{ |
||||||
|
"name": "website", |
||||||
|
"version": "0.0.0", |
||||||
|
"private": true, |
||||||
|
"scripts": { |
||||||
|
"start": "docusaurus start", |
||||||
|
"build": "docusaurus build", |
||||||
|
"swizzle": "docusaurus swizzle", |
||||||
|
"deploy": "docusaurus deploy" |
||||||
|
}, |
||||||
|
"dependencies": { |
||||||
|
"@docusaurus/core": "^2.0.0-alpha.48", |
||||||
|
"@docusaurus/preset-classic": "^2.0.0-alpha.48", |
||||||
|
"classnames": "^2.2.6", |
||||||
|
"react": "^16.13.1", |
||||||
|
"react-dom": "^16.13.1" |
||||||
|
}, |
||||||
|
"browserslist": { |
||||||
|
"production": [ |
||||||
|
">0.2%", |
||||||
|
"not dead", |
||||||
|
"not op_mini all" |
||||||
|
], |
||||||
|
"development": [ |
||||||
|
"last 1 chrome version", |
||||||
|
"last 1 firefox version", |
||||||
|
"last 1 safari version" |
||||||
|
] |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,7 @@ |
|||||||
|
module.exports = { |
||||||
|
someSidebar: { |
||||||
|
Docusaurus: ['installation', 'usage'], |
||||||
|
API: ['carousel'], |
||||||
|
Examples: ['infinity', 'swipible'], |
||||||
|
}, |
||||||
|
}; |
@ -0,0 +1,50 @@ |
|||||||
|
/* stylelint-disable docusaurus/copyright-header */ |
||||||
|
/** |
||||||
|
* Any CSS included here will be global. The classic template |
||||||
|
* bundles Infima by default. Infima is a CSS framework designed to |
||||||
|
* work well for content-centric websites. |
||||||
|
*/ |
||||||
|
|
||||||
|
/* You can override the default Infima variables here. */ |
||||||
|
:root { |
||||||
|
--ifm-color-primary: #25c2a0; |
||||||
|
--ifm-color-primary-dark: rgb(33, 175, 144); |
||||||
|
--ifm-color-primary-darker: rgb(31, 165, 136); |
||||||
|
--ifm-color-primary-darkest: rgb(26, 136, 112); |
||||||
|
--ifm-color-primary-light: rgb(70, 203, 174); |
||||||
|
--ifm-color-primary-lighter: rgb(102, 212, 189); |
||||||
|
--ifm-color-primary-lightest: rgb(146, 224, 208); |
||||||
|
--ifm-code-font-size: 95%; |
||||||
|
} |
||||||
|
|
||||||
|
.docusaurus-highlight-code-line { |
||||||
|
background-color: rgb(72, 77, 91); |
||||||
|
display: block; |
||||||
|
margin: 0 calc(-1 * var(--ifm-pre-padding)); |
||||||
|
padding: 0 var(--ifm-pre-padding); |
||||||
|
} |
||||||
|
|
||||||
|
.exampleCarousel1 button:first-child { |
||||||
|
background: url('https://cdn.dsmcdn.com/web/production/slick-arrow.svg') no-repeat |
||||||
|
center; |
||||||
|
transform: rotateZ(180deg); |
||||||
|
outline: none; |
||||||
|
border: none; |
||||||
|
cursor: pointer; |
||||||
|
position: relative; |
||||||
|
left: -16px; |
||||||
|
} |
||||||
|
|
||||||
|
.exampleCarousel1 button:last-child { |
||||||
|
background: url('https://cdn.dsmcdn.com/web/production/slick-arrow.svg') no-repeat |
||||||
|
center; |
||||||
|
outline: none; |
||||||
|
border: none; |
||||||
|
cursor: pointer; |
||||||
|
position: relative; |
||||||
|
right: -16px; |
||||||
|
} |
||||||
|
|
||||||
|
.exampleCarousel1 div:first-child { |
||||||
|
text-align: center; |
||||||
|
} |
@ -0,0 +1,126 @@ |
|||||||
|
import React from 'react'; |
||||||
|
import classnames from 'classnames'; |
||||||
|
import Layout from '@theme/Layout'; |
||||||
|
import Link from '@docusaurus/Link'; |
||||||
|
import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; |
||||||
|
import useBaseUrl from '@docusaurus/useBaseUrl'; |
||||||
|
import { Carousel } from '@trendyol/react-carousel'; |
||||||
|
import styles from './styles.module.css'; |
||||||
|
import { Redirect } from '@docusaurus/router'; |
||||||
|
|
||||||
|
const features = [ |
||||||
|
{ |
||||||
|
title: <>Easy to Use</>, |
||||||
|
imageUrl: 'img/undraw_docusaurus_mountain.svg', |
||||||
|
description: ( |
||||||
|
<> |
||||||
|
Docusaurus was designed from the ground up to be easily installed and used to get |
||||||
|
your website up and running quickly. |
||||||
|
</> |
||||||
|
), |
||||||
|
}, |
||||||
|
{ |
||||||
|
title: <>Focus on What Matters</>, |
||||||
|
imageUrl: 'img/undraw_docusaurus_tree.svg', |
||||||
|
description: ( |
||||||
|
<> |
||||||
|
Docusaurus lets you focus on your docs, and we'll do the chores. Go ahead and |
||||||
|
move your docs into the <code>docs</code> directory. |
||||||
|
</> |
||||||
|
), |
||||||
|
}, |
||||||
|
{ |
||||||
|
title: <>Powered by React</>, |
||||||
|
imageUrl: 'img/undraw_docusaurus_react.svg', |
||||||
|
description: ( |
||||||
|
<> |
||||||
|
Extend or customize your website layout by reusing React. Docusaurus can be |
||||||
|
extended while reusing the same header and footer. |
||||||
|
</> |
||||||
|
), |
||||||
|
}, |
||||||
|
]; |
||||||
|
const features2 = (num) => { |
||||||
|
let i = 0; |
||||||
|
return new Array(num).fill(0).map(() => { |
||||||
|
i++; |
||||||
|
return { |
||||||
|
description: <>{i}</>, |
||||||
|
}; |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
function Feature({ imageUrl, title, description }) { |
||||||
|
const imgUrl = useBaseUrl(imageUrl); |
||||||
|
return ( |
||||||
|
<div className={classnames('col col--4', styles.feature)}> |
||||||
|
{imgUrl && ( |
||||||
|
<div className="text--center"> |
||||||
|
<img className={styles.featureImage} src={imgUrl} alt={title} /> |
||||||
|
</div> |
||||||
|
)} |
||||||
|
<h3>{title}</h3> |
||||||
|
<p>{description}</p> |
||||||
|
</div> |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
function Home() { |
||||||
|
return <Redirect to="/docs/installation" />; |
||||||
|
const context = useDocusaurusContext(); |
||||||
|
const { siteConfig = {} } = context; |
||||||
|
return ( |
||||||
|
<Layout |
||||||
|
title={`Hello from ${siteConfig.title}`} |
||||||
|
description="Description will go into a meta tag in <head />" |
||||||
|
> |
||||||
|
<header className={classnames('hero hero--primary', styles.heroBanner)}> |
||||||
|
<div className="container"> |
||||||
|
<h1 className="hero__title">{siteConfig.title}</h1> |
||||||
|
<p className="hero__subtitle">{siteConfig.tagline}</p> |
||||||
|
<div className={styles.buttons}> |
||||||
|
<Link |
||||||
|
className={classnames( |
||||||
|
'button button--outline button--secondary button--lg', |
||||||
|
styles.getStarted, |
||||||
|
)} |
||||||
|
to={useBaseUrl('docs/doc1')} |
||||||
|
> |
||||||
|
Get Started |
||||||
|
</Link> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</header> |
||||||
|
<main> |
||||||
|
<section className={styles.features}> |
||||||
|
<div className="container"> |
||||||
|
<div className="row"> |
||||||
|
<Carousel show={5} slide={5} transition={0.5}> |
||||||
|
{features2(15).map((props, idx) => ( |
||||||
|
<div key={idx} style={{ marginRight: '20px' }}> |
||||||
|
{props.description} |
||||||
|
</div> |
||||||
|
))} |
||||||
|
</Carousel> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</section> |
||||||
|
{features && features.length && ( |
||||||
|
<section className={styles.features}> |
||||||
|
<div className="container"> |
||||||
|
<div className="row"> |
||||||
|
<Carousel show={2} slide={1} transition={0.5}> |
||||||
|
{features.map((props, idx) => ( |
||||||
|
<Feature key={idx} {...props} /> |
||||||
|
))} |
||||||
|
</Carousel> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</section> |
||||||
|
)} |
||||||
|
</main> |
||||||
|
</Layout> |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
export default Home; |
@ -0,0 +1,36 @@ |
|||||||
|
/* stylelint-disable docusaurus/copyright-header */ |
||||||
|
/** |
||||||
|
* CSS files with the .module.css suffix will be treated as CSS modules |
||||||
|
* and scoped locally. |
||||||
|
*/ |
||||||
|
|
||||||
|
.heroBanner { |
||||||
|
padding: 4rem 0; |
||||||
|
text-align: center; |
||||||
|
position: relative; |
||||||
|
overflow: hidden; |
||||||
|
} |
||||||
|
|
||||||
|
@media screen and (max-width: 966px) { |
||||||
|
.heroBanner { |
||||||
|
padding: 2rem; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
.buttons { |
||||||
|
display: flex; |
||||||
|
align-items: center; |
||||||
|
justify-content: center; |
||||||
|
} |
||||||
|
|
||||||
|
.features { |
||||||
|
display: flex; |
||||||
|
align-items: center; |
||||||
|
padding: 2rem 0; |
||||||
|
width: 100%; |
||||||
|
} |
||||||
|
|
||||||
|
.featureImage { |
||||||
|
height: 200px; |
||||||
|
width: 200px; |
||||||
|
} |
After Width: | Height: | Size: 766 B |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 6.3 KiB |
After Width: | Height: | Size: 31 KiB |
After Width: | Height: | Size: 35 KiB |
After Width: | Height: | Size: 12 KiB |
Loading…
Reference in new issue