From 435af5fbc0bd6e13002a0d6a954add24a6532b29 Mon Sep 17 00:00:00 2001 From: Marinich Maxim Date: Wed, 27 Mar 2024 19:06:46 +0300 Subject: [PATCH 1/2] Add DNDLink component --- README.md | 191 ++++++++++-------- .../pages/custom-components/index.tsx | 9 + .../pages/custom-components/links/code.md | 22 ++ .../pages/custom-components/links/index.tsx | 29 +++ src/components/pages/styles.scss | 22 +- src/lib/react-alice-carousel.tsx | 2 + 6 files changed, 190 insertions(+), 85 deletions(-) create mode 100644 src/components/pages/custom-components/links/code.md create mode 100644 src/components/pages/custom-components/links/index.tsx diff --git a/README.md b/README.md index 8c7a4dd2..1151b73a 100644 --- a/README.md +++ b/README.md @@ -33,19 +33,25 @@ React Alice Carousel is a React component for building content galleries, conten - TypeScript ## Installation + ```apacheconfig npm i react-alice-carousel ``` + #### Style import + ``` # CSS @import "react-alice-carousel/lib/alice-carousel.css"; ``` + ``` # SCSS @import "react-alice-carousel/lib/scss/alice-carousel.scss"; ``` + ## Usage + ```javascript import React from 'react'; import AliceCarousel from 'react-alice-carousel'; @@ -54,19 +60,16 @@ import 'react-alice-carousel/lib/alice-carousel.css'; const handleDragStart = (e) => e.preventDefault(); const items = [ - , - , - , + , + , + , ]; -const Gallery = () => { - return ( - - ); -} +const Gallery = () => ; ``` #### Options + - `activeIndex` : Number, default `0` - Set carousel at the specified position. - `animationDuration`: Number, default `400` - Set duration of animation. - `animationEasingFunction`: String or [Function](https://developer.mozilla.org/ru/docs/Web/CSS/animation-timing-function), default `ease` - Property sets how an animation progresses through the duration of each cycle. @@ -78,33 +81,35 @@ const Gallery = () => { - `autoPlayDirection`: String(`ltr`, `rtl`), default `ltr` - Set autoplay direction value. - `autoPlayInterval`: Number, default `400` - Set autoplay interval value. - `autoPlayStrategy`: String(`default`, `action`, `all`, `none`) - Set a strategy for autoplay mode - * `default` - pause automatic playback on the hover - * `action` - stop automatic playback if user action was detected - * `all` - merge `default` && `action` strategies - * `none` - ignore any user actions when the `autoPlay` property was specified + - `default` - pause automatic playback on the hover + - `action` - stop automatic playback if user action was detected + - `all` - merge `default` && `action` strategies + - `none` - ignore any user actions when the `autoPlay` property was specified - `controlsStrategy`: String (`default`, `responsive`, `alternate` or combined string `"default,alternate"`) - Set a strategy for gallery controls. - * `default` - use controls as is - * `alternate` - show each dot for each slide - * `responsive` - navigation will be hidden if the number of gallery elements is equal to the number of items in the slide. + - `default` - use controls as is + - `alternate` - show each dot for each slide + - `responsive` - navigation will be hidden if the number of gallery elements is equal to the number of items in the slide. - `disableButtonsControls`: Boolean, default `false` - Disable buttons controls. - `disableDotsControls`: Boolean, default `false` - Disable dots controls. - `disableSlideInfo`: Boolean, default `true` - Disable information about current slide. - `infinite`: Boolean, default `false` - Set infinite mode. - `innerWidth`: Number, default `0` - Set a static value for a breakpoint(`key`) of the "responsive" property. For example, if you can't use 'window.innerWidth' during SSR. -- `items`: Array, default `undefined` - Set gallery items, preferable to use this property instead of children. -- `keyboardNavigation`: Boolean, default `false` - Enable keyboard navigation - * `ArrowLeft` - go to the prev slide - * `ArrowRight` - go to the next slide - * `Space` - run/stop autoplay mode if `autoPlay` property is equal `true` -- `mouseTracking`: Boolean, default `false` - Enable mouse drag animation. -- `paddingLeft`: Number, default `0` - Set the gallery offset from the left. -- `paddingRight`: Number, default `0` - Set the gallery offset from the right. -- `renderKey`: Number, default `undefined` - Auxiliary property, allows call the render method without changing the state inside the gallery instance. +- `items`: Array, default `undefined` - Set gallery items, preferable to use this property instead of children. +- `keyboardNavigation`: Boolean, default `false` - Enable keyboard navigation + - `ArrowLeft` - go to the prev slide + - `ArrowRight` - go to the next slide + - `Space` - run/stop autoplay mode if `autoPlay` property is equal `true` +- `mouseTracking`: Boolean, default `false` - Enable mouse drag animation. Consider the problem with links, see the example of using the `` component below. +- `paddingLeft`: Number, default `0` - Set the gallery offset from the left. +- `paddingRight`: Number, default `0` - Set the gallery offset from the right. +- `renderKey`: Number, default `undefined` - Auxiliary property, allows call the render method without changing the state inside the gallery instance. - `responsive`: Object, default `undefined` - The key is the breakpoint (default is the result of: () => window.innerWidth or `innerWidth` property if the last presented). - * `items` - set number of items in the slide. Default: `1` - * `itemsFit`: one of (`contain | fill | undefined`) - defines, how item should fill the container according slide's width. Default: `fill`. - - If `contain` is specified, the gallery will use the value from the `items` property to determine the width of the element for each slide and fill in the empty space as needed. + + - `items` - set number of items in the slide. Default: `1` + - `itemsFit`: one of (`contain | fill | undefined`) - defines, how item should fill the container according slide's width. Default: `fill`. + + If `contain` is specified, the gallery will use the value from the `items` property to determine the width of the element for each slide and fill in the empty space as needed. + ```js { 0: { @@ -115,15 +120,16 @@ const Gallery = () => { itemsFit: 'contain', } } - ``` -- `syncStateOnPropsUpdate`: Boolean, default `true` - Sync some props (like `activeIndex`) with carousel state while new props passed. This allows you to avoid resetting the carousel position while updating the props (e.g.: `children` or `items`). -- `swipeDelta`: Number, default `20` - Set minimum distance to the start of the swiping (px). -- `swipeExtraPadding`: Number, default `200` - Set maximum distance from initial place before swipe action will be stopped (px). -- `ssrSilentMode`: Boolean, default `true` - Disable classnames modifiers for server side rendering. -- `touchTracking`: Boolean, default `true` - Enable touch move animation. -- `touchMoveDefaultEvents`: Boolean, default `true` - Enable touch move default events on swiping. If `false` was specified, this prevents vertical scrolling of the parent elements during the swipe. + ``` + +- `syncStateOnPropsUpdate`: Boolean, default `true` - Sync some props (like `activeIndex`) with carousel state while new props passed. This allows you to avoid resetting the carousel position while updating the props (e.g.: `children` or `items`). +- `swipeDelta`: Number, default `20` - Set minimum distance to the start of the swiping (px). +- `swipeExtraPadding`: Number, default `200` - Set maximum distance from initial place before swipe action will be stopped (px). +- `ssrSilentMode`: Boolean, default `true` - Disable classnames modifiers for server side rendering. +- `touchTracking`: Boolean, default `true` - Enable touch move animation. +- `touchMoveDefaultEvents`: Boolean, default `true` - Enable touch move default events on swiping. If `false` was specified, this prevents vertical scrolling of the parent elements during the swipe. - `onInitialized(e: EventObject)`: Function - Fired as callback after the gallery was created. -- `onResizeEvent(e: Event)`: Function, default `shouldProcessResizeEvent` method - Fired during the "resize" event to determine whether to call the event handler. Default method checks is the root element width has changed. +- `onResizeEvent(e: Event)`: Function, default `shouldProcessResizeEvent` method - Fired during the "resize" event to determine whether to call the event handler. Default method checks is the root element width has changed. - `onResized(e: EventObject)`: Function - Fired as callback after the gallery was resized. - `onUpdated(e: EventObject)`: Function - Fired as callback after updating the gallery props. - `onSlideChange(e: EventObject)`: Function - Fired before the event object changes. @@ -135,95 +141,116 @@ const Gallery = () => { - `renderPlayPauseButton({ isPlaying })`: Rendering function - create a custom component. #### Methods (Refs [example](https://maxmarinich.github.io/react-alice-carousel/#custom-components-refs)) + - `slidePrev(e: Event) => void` : Go to the prev slide. - `slideNext(e: Event) => void` : Go to the next slide. - `slideTo(activeIndex?: number) => void` : Go to the specified slide. +#### Components (Links [example](https://maxmarinich.github.io/react-alice-carousel/#custom-components-links)) + +- `` : allows properly handle click and drag events when mouseTracking enabled, extends base HTMLAnchorElement + +```javascript +import React from 'react'; +import AliceCarousel, { Link } from 'react-alice-carousel'; +import 'react-alice-carousel/lib/alice-carousel.css'; + +const Gallery = () => { + return ( + + + + + + ); +}; +``` + #### Types + ```typescript type EventObject = { - item: number; - slide: number; - itemsInSlide: number; - isPrevSlideDisabled: boolean; - isNextSlideDisabled: boolean; - type: EventType; + item: number; + slide: number; + itemsInSlide: number; + isPrevSlideDisabled: boolean; + isNextSlideDisabled: boolean; + type: EventType; }; type SlideInfo = { - item: number; - itemsCount: number; + item: number; + itemsCount: number; }; type DotsItem = { - isActive: boolean; - activeIndex: number; + isActive: boolean; + activeIndex: number; }; enum EventType { - ACTION = 'action', // used if a general user action (button click or swipe) - INIT = 'init', // used if the initial event was triggered - RESIZE = 'resize', // used if the gallery size was changed - UPDATE = 'update', // used if the gallery was updated with props + ACTION = 'action', // used if a general user action (button click or swipe) + INIT = 'init', // used if the initial event was triggered + RESIZE = 'resize', // used if the gallery size was changed + UPDATE = 'update', // used if the gallery was updated with props } ``` #### CSS classes + ```css .alice-carousel - -.alice-carousel__stage -.alice-carousel__stage-item - -.alice-carousel__prev-btn -.alice-carousel__prev-btn-item - -.alice-carousel__next-btn -.alice-carousel__next-btn-item - -.alice-carousel__play-btn -.alice-carousel__play-btn-item - -.alice-carousel__dots -.alice-carousel__dots-item - -.alice-carousel__slide-info -.alice-carousel__slide-info-item + .alice-carousel__stage + .alice-carousel__stage-item + .alice-carousel__prev-btn + .alice-carousel__prev-btn-item + .alice-carousel__next-btn + .alice-carousel__next-btn-item + .alice-carousel__play-btn + .alice-carousel__play-btn-item + .alice-carousel__dots + .alice-carousel__dots-item + .alice-carousel__slide-info + .alice-carousel__slide-info-item; ``` #### CSS modifiers + ```css .alice-carousel.__ssr - -.alice-carousel__stage-item.__active -.alice-carousel__stage-item.__cloned -.alice-carousel__stage-item.__target - -.alice-carousel__next-btn-item.__inactive -.alice-carousel__prev-btn-item.__inactive - -.alice-carousel__play-btn-item.__pause - -.alice-carousel__dots-item.__active -.alice-carousel__dots-item.__custom - -.alice-carousel__slide-info-item.__separator + .alice-carousel__stage-item.__active + .alice-carousel__stage-item.__cloned + .alice-carousel__stage-item.__target + .alice-carousel__next-btn-item.__inactive + .alice-carousel__prev-btn-item.__inactive + .alice-carousel__play-btn-item.__pause + .alice-carousel__dots-item.__active + .alice-carousel__dots-item.__custom + .alice-carousel__slide-info-item.__separator; ``` ## Build the project locally + #### Clone + ```apacheconfig git clone https://github.com/maxmarinich/react-alice-carousel cd react-alice-carousel ``` + #### Run + ```apacheconfig npm ci npm start ``` + #### Test + ```apacheconfig npm test ``` + ## License + MIT diff --git a/src/components/pages/custom-components/index.tsx b/src/components/pages/custom-components/index.tsx index fb4fca05..f00c41fa 100644 --- a/src/components/pages/custom-components/index.tsx +++ b/src/components/pages/custom-components/index.tsx @@ -3,6 +3,7 @@ import RenderExample from './render-components'; import RefsExample from './refs-components'; import PropsExample from './props-components'; import EventsExample from './event-components'; +import Links from './links'; import Anchor, { genAnchorProps } from '../../the-anchor'; const CustomComponentsPage = () => { @@ -37,6 +38,14 @@ const CustomComponentsPage = () => {   Refs +
+
+
+

+ +   Links +

+ ); }; diff --git a/src/components/pages/custom-components/links/code.md b/src/components/pages/custom-components/links/code.md new file mode 100644 index 00000000..b77aa46a --- /dev/null +++ b/src/components/pages/custom-components/links/code.md @@ -0,0 +1,22 @@ +```javascript +import React from 'react'; +import AliceCarousel, { Link } from 'react-alice-carousel'; +import 'react-alice-carousel/lib/alice-carousel.css'; + +const href = '//github.com/maxmarinich/react-alice-carousel'; +const src = '//github.com/maxmarinich/react-alice-carousel/raw/master/src/assets/img/1200x200.jpg'; + +const Carousel = () => ( + + + + + + + + + + + +); +``` diff --git a/src/components/pages/custom-components/links/index.tsx b/src/components/pages/custom-components/links/index.tsx new file mode 100644 index 00000000..3cc87d88 --- /dev/null +++ b/src/components/pages/custom-components/links/index.tsx @@ -0,0 +1,29 @@ +import React from 'react'; + +import markdown from './code.md'; +import TheCode from '../../../the-code'; +import AliceCarousel, { Link } from '../../../../lib/react-alice-carousel'; + +const Links = () => { + const href = '//github.com/maxmarinich/react-alice-carousel'; + const src = '//github.com/maxmarinich/react-alice-carousel/raw/master/src/assets/img/1200x200.jpg'; + + return ( +
+ + + + + + + + + + + + +
+ ); +}; + +export default Links; diff --git a/src/components/pages/styles.scss b/src/components/pages/styles.scss index 92fb41c5..5988dfc9 100644 --- a/src/components/pages/styles.scss +++ b/src/components/pages/styles.scss @@ -17,7 +17,8 @@ li.alice-carousel__dots-item.__custom { padding: 0 100px; position: relative; - .btn-prev, .btn-next { + .btn-prev, + .btn-next { position: absolute; top: 25px; font-size: 50px; @@ -42,7 +43,7 @@ li.alice-carousel__dots-item.__custom { right: 2px; bottom: 2px; border: solid 1px transparent; - transition: border .8s; + transition: border 0.8s; } } @@ -52,7 +53,8 @@ li.alice-carousel__dots-item.__custom { } } -.thumb, .thumb > .item { +.thumb, +.thumb > .item { width: 200px; height: 100px; } @@ -85,3 +87,17 @@ li.alice-carousel__dots-item.__custom { z-index: 1; } } + +.link { + display: block; + height: 200px; + padding: 3px; + position: relative; + z-index: 1; + + img { + display: block; + width: 100%; + height: 100%; + } +} diff --git a/src/lib/react-alice-carousel.tsx b/src/lib/react-alice-carousel.tsx index 896f128b..4e755e7d 100644 --- a/src/lib/react-alice-carousel.tsx +++ b/src/lib/react-alice-carousel.tsx @@ -1,6 +1,7 @@ import React from 'react'; import VS, { EventData } from 'vanilla-swipe'; import { defaultProps } from './defaultProps'; +import Link, { type LinkProps } from './views/DNDLink'; import * as Views from './views'; import * as Utils from './utils'; import { @@ -18,6 +19,7 @@ import { } from './types'; export * from './types'; +export { Link, type LinkProps }; export default class AliceCarousel extends React.PureComponent { static defaultProps = defaultProps; private autoPlayTimeoutId?: Timeout; From 5ba72fb59559cff507a5138d8b5e54115bf5cb2f Mon Sep 17 00:00:00 2001 From: Marinich Maxim Date: Wed, 27 Mar 2024 19:11:02 +0300 Subject: [PATCH 2/2] Add Link component --- .gitignore | 2 ++ src/lib/react-alice-carousel.tsx | 2 +- src/lib/views/Link.tsx | 34 ++++++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 src/lib/views/Link.tsx diff --git a/.gitignore b/.gitignore index 4eb1ae11..d872a6ea 100644 --- a/.gitignore +++ b/.gitignore @@ -69,3 +69,5 @@ npm-debug.log dist lib static + +!src/lib diff --git a/src/lib/react-alice-carousel.tsx b/src/lib/react-alice-carousel.tsx index 4e755e7d..87fca882 100644 --- a/src/lib/react-alice-carousel.tsx +++ b/src/lib/react-alice-carousel.tsx @@ -1,7 +1,7 @@ import React from 'react'; import VS, { EventData } from 'vanilla-swipe'; import { defaultProps } from './defaultProps'; -import Link, { type LinkProps } from './views/DNDLink'; +import Link, { type LinkProps } from './views/Link'; import * as Views from './views'; import * as Utils from './utils'; import { diff --git a/src/lib/views/Link.tsx b/src/lib/views/Link.tsx new file mode 100644 index 00000000..847eb243 --- /dev/null +++ b/src/lib/views/Link.tsx @@ -0,0 +1,34 @@ +import React from 'react'; +import type { AnchorHTMLAttributes, PropsWithChildren, MouseEvent } from 'react'; + +export type LinkProps = PropsWithChildren>; + +export default function Link(props: LinkProps) { + const coords: Record = { + xDown: null, + xUp: null, + }; + + const handleOnMouseDown = (e: MouseEvent) => { + e.preventDefault(); + coords.xUp = null; + coords.xDown = e.clientX; + }; + + const handleMouseUp = (e: MouseEvent) => { + e.preventDefault(); + coords.xUp = e.clientX; + }; + + const handleOnClick = (e: MouseEvent) => { + if (coords.xDown !== coords.xUp) { + e.preventDefault(); + } + }; + + return ( + + {props.children} + + ); +}