├── .eslintrc.js ├── .gitignore ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── components ├── SledComponent.js ├── SledImage.js ├── github-logo.js ├── header.js ├── images.js ├── settings.js ├── state.js └── useWindowSize.js ├── next-env.d.ts ├── next.config.js ├── package.json ├── pages ├── css │ ├── header.css │ ├── index.css │ ├── menue.css │ ├── reset.css │ ├── settings.css │ └── toggle.css ├── custom.js ├── index.js ├── test-basic.js └── test.js ├── public ├── favicon.ico ├── plex-mono-bold.woff └── plex-mono.woff ├── react-sled-logo.jpg ├── rollup.config.js ├── sled ├── control │ ├── hooks │ │ ├── useClassName.ts │ │ ├── useClick.ts │ │ ├── useDirectionDisabled.ts │ │ └── useLabel.ts │ ├── index.css │ └── index.tsx ├── hooks │ ├── useAutoPlay.ts │ ├── useConfig.ts │ ├── useContainerStyles.ts │ ├── useCursor.ts │ ├── useDimensions.ts │ ├── useDimensionsDOM.ts │ ├── useDirection.ts │ ├── useDragGesture.ts │ ├── useDragging.ts │ ├── useFocus.ts │ ├── useKeyboard.ts │ ├── useMouseOver.ts │ ├── usePause.ts │ ├── useProportion.ts │ ├── useRewind.ts │ ├── useSelect.ts │ ├── useShowElements.ts │ ├── useSlideBy.ts │ ├── useSlideSteps.ts │ ├── useSliderSize.ts │ ├── useStopOnInteraction.ts │ ├── useViewCount.ts │ └── useX.ts ├── index.css ├── index.ts ├── progress │ ├── controls.tsx │ ├── index.css │ ├── index.tsx │ ├── separators.tsx │ └── track.tsx ├── sled.tsx ├── springs.tsx ├── state │ ├── index.tsx │ ├── reducer.ts │ └── types-defaults.ts ├── utils │ ├── clamp.ts │ └── debounce.ts └── views.tsx ├── tsconfig.json ├── tsconfig.rollup.json └── yarn.lock /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: "react-app", 3 | rules: { 4 | quotes: ["error", "single"], 5 | semi: ["error", "never"], 6 | indent: ["error", 2, { 7 | SwitchCase: 1 8 | }], 9 | "no-multiple-empty-lines": ["error"], 10 | "jsx-a11y/anchor-is-valid": 0 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | _local-assets 2 | /.vscode 3 | sled.code-workspace 4 | /dist 5 | 6 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 7 | 8 | # dependencies 9 | /node_modules 10 | /.pnp 11 | .pnp.js 12 | 13 | # testing 14 | /coverage 15 | 16 | # next.js 17 | /.next/ 18 | /out/ 19 | 20 | # production 21 | /build 22 | 23 | # misc 24 | .DS_Store 25 | .env* 26 | 27 | # debug 28 | npm-debug.log* 29 | yarn-debug.log* 30 | yarn-error.log* 31 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | #### 2.0.4 (2020-02-02) 2 | 3 | ##### Bug Fixes 4 | 5 | * update react-use-gesture (3050ce3f) 6 | 7 | #### 2.0.3 (2020-01-20) 8 | 9 | ##### Documentation Changes 10 | 11 | * update example-link (93e3a934) 12 | 13 | #### 2.0.2 (2020-01-20) 14 | 15 | ##### Documentation Changes 16 | 17 | * correct changelog (b707cf88) 18 | * corrections (35d6ee18) 19 | 20 | #### 2.0.1 (2020-01-20) 21 | 22 | ##### Documentation Changes 23 | 24 | * correct readme (66e0291a) 25 | 26 | ##### Bug Fixes 27 | 28 | * refactor sizing (3c959a70) 29 | 30 | ## 2.0.0 (2020-01-20) 31 | 32 | #### 1.2.6 (2019-12-22) 33 | 34 | ##### Other Changes 35 | 36 | * remove images from repo (5749455d) 37 | 38 | #### 1.2.5 (2019-10-26) 39 | 40 | ##### Bug Fixes 41 | 42 | * fix dragging. fixes #5 (e4121bdf) 43 | 44 | #### 1.2.4 (2019-10-10) 45 | 46 | ##### Bug Fixes 47 | 48 | * update gitignore (783b062a) 49 | 50 | #### 1.2.3 (2019-10-10) 51 | 52 | ##### Bug Fixes 53 | 54 | * fix package.json (c8fb5338) 55 | 56 | #### 1.2.2 (2019-10-10) 57 | 58 | ##### Bug Fixes 59 | 60 | * update package.json and readme (5b54fcf5) 61 | 62 | #### 1.2.1 (2019-10-10) 63 | 64 | ### 1.2.0 (2019-10-10) 65 | 66 | ##### New Features 67 | 68 | * replace react-with-gesture with react-use-gesture. improve performance. (5a0dd72d) 69 | * add lazy-loading to example (4740b3d3) 70 | 71 | ##### Bug Fixes 72 | 73 | * Fix onSledEnd to be called just once. (cfa9de6b) 74 | * example - minor style fixes (88912fd6) 75 | * update readme (e1212f05) 76 | * change linter from standard to eslint (c4284ead) 77 | 78 | ### 1.2.0 (2019-10-10) 79 | 80 | ##### New Features 81 | 82 | * replace react-with-gesture with react-use-gesture. improve performance. (5a0dd72d) 83 | * add lazy-loading to example (4740b3d3) 84 | 85 | ##### Bug Fixes 86 | 87 | * Fix onSledEnd to be called just once. (cfa9de6b) 88 | * example - minor style fixes (88912fd6) 89 | * update readme (e1212f05) 90 | * change linter from standard to eslint (c4284ead) 91 | 92 | #### 1.1.2 (2019-10-09) 93 | 94 | ##### Bug Fixes 95 | 96 | * fix example ssr (d34bc9ff) 97 | 98 | #### 1.1.1 (2019-10-08) 99 | 100 | ### 1.1.0 (2019-10-08) 101 | 102 | ##### Bug Fixes 103 | 104 | * fix content-z-index in example. (96193878) 105 | 106 | ##### Other Changes 107 | 108 | * fix callback bug (3b909b98) 109 | 110 | #### 1.0.8 (2019-09-07) 111 | 112 | ##### Bug Fixes 113 | 114 | * update dependecies and fix typo in readme (639efd77) 115 | 116 | #### 1.0.7 (2019-06-06) 117 | 118 | ##### Bug Fixes 119 | 120 | * Fix gatsby-config. (ef75f8f9) 121 | 122 | #### 1.0.6 (2019-06-06) 123 | 124 | ##### Documentation Changes 125 | 126 | * Fix props-table. (96942b78) 127 | 128 | #### 1.0.5 (2019-06-05) 129 | 130 | #### 1.0.4 (2019-06-05) 131 | 132 | ##### Other Changes 133 | 134 | * fix first animation. (49ea3a1d) 135 | 136 | #### 1.0.3 (2019-06-05) 137 | 138 | ##### Bug Fixes 139 | 140 | * Small change. (0549fbf7) 141 | 142 | #### 1.0.2 (2019-06-05) 143 | 144 | ##### Bug Fixes 145 | 146 | * Small change. (60ce31b2) 147 | 148 | #### 1.0.1 (2019-06-05) 149 | 150 | ##### Documentation Changes 151 | 152 | * Small change. (336465d8) 153 | 154 | ## 1.0.0 (2019-06-05) 155 | 156 | ##### New Features 157 | 158 | * First major release. (03fd1bb8) 159 | 160 | 161 | ### 0.15.0 (2019-06-03) 162 | 163 | ##### New Features 164 | 165 | * Add pause-prop. Expose state-management. (40371acc) 166 | 167 | #### 0.14.1 (2019-06-02) 168 | 169 | ### 0.14.0 (2019-06-02) 170 | 171 | ### 0.14.0 (2019-06-02) 172 | 173 | ### 0.12.0 (2019-06-02) 174 | 175 | ### 0.12.0 (2019-06-02) 176 | 177 | ### 0.13.0 (2019-06-02) 178 | 179 | ### 0.12.0 (2019-06-02) 180 | 181 | ##### New Features 182 | 183 | * Add props onSledEnd and stopOnInteraction. (97d50729) 184 | 185 | ##### Bug Fixes 186 | 187 | * Disable focus-outline. (7cb5bd06) 188 | 189 | #### 0.11.1 (2019-05-11) 190 | 191 | ##### Bug Fixes 192 | 193 | * Progress: Fix bug, when there are only 2 views. (15d8537b) 194 | 195 | ### 0.11.0 (2019-05-03) 196 | 197 | ##### New Features 198 | 199 | * Add classname ot view. (0ff5d0bf) 200 | 201 | #### 0.10.1 (2019-05-03) 202 | 203 | ##### Documentation Changes 204 | 205 | * fix docs. (74d0f7d8) 206 | 207 | ### 0.10.0 (2019-05-02) 208 | 209 | ##### Documentation Changes 210 | 211 | * improve docs and example. (da6cfdb3) 212 | 213 | ##### Bug Fixes 214 | 215 | * change cursor if dragging is deactivated. (4188989e) 216 | 217 | #### 0.9.1 (2019-04-27) 218 | 219 | ##### Bug Fixes 220 | 221 | * Fix preset-names. (43834d9b) 222 | 223 | ### 0.9.0 (2019-04-27) 224 | 225 | ##### New Features 226 | 227 | * complete controls. (6e8182c5) 228 | 229 | #### 0.8.4 (2019-04-27) 230 | 231 | ##### Bug Fixes 232 | 233 | * Go back to old domain. (584761bf) 234 | 235 | #### 0.8.3 (2019-04-27) 236 | 237 | ##### Bug Fixes 238 | 239 | * Change example-domain. (056e1a16) 240 | 241 | #### 0.8.2 (2019-04-27) 242 | 243 | ##### Bug Fixes 244 | 245 | * Make autoplay restartable. (488bc048) 246 | 247 | #### 0.8.1 (2019-04-27) 248 | 249 | ##### Bug Fixes 250 | 251 | * Correction in example. (1452f1f6) 252 | 253 | ### 0.8.0 (2019-04-27) 254 | 255 | ##### New Features 256 | 257 | * Make all props dynamic. (0801e73d) 258 | 259 | #### 0.7.2 (2019-04-27) 260 | 261 | ##### Documentation Changes 262 | 263 | * Change logo. (05fc3609) 264 | 265 | #### 0.7.1 (2019-04-27) 266 | 267 | ##### Documentation Changes 268 | 269 | * Change logo. (7e6321b3) 270 | 271 | ### 0.7.0 (2019-04-27) 272 | 273 | ##### New Features 274 | 275 | * Add rewind-prop. (0a2208f7) 276 | 277 | ##### Bug Fixes 278 | 279 | * Reverse z-indices. (2e90dd31) 280 | 281 | #### 0.6.1 (2019-04-27) 282 | 283 | ##### Documentation Changes 284 | 285 | * Fix docs. (2801f1e4) 286 | 287 | ### 0.6.0 (2019-04-27) 288 | 289 | ##### Refactors 290 | 291 | * Clean up codebase. (21e59655) 292 | 293 | ### 0.5.0 (2019-04-26) 294 | 295 | ##### New Features 296 | 297 | * Add goto-feature. (dc930a9c) 298 | 299 | ##### Refactors 300 | 301 | * Change prop 'keys' to 'keysboard'. (d83d69bb) 302 | 303 | ### 0.4.0 (2019-04-26) 304 | 305 | ##### Refactors 306 | 307 | * Change and improve sizing. (e3086bf1) 308 | 309 | ### 0.3.0 (2019-04-26) 310 | 311 | ##### New Features 312 | 313 | * Keep sled's height intact. (14686209) 314 | 315 | #### 0.2.10 (2019-04-26) 316 | 317 | #### 0.2.9 (2019-04-26) 318 | 319 | #### 0.2.8 (2019-04-26) 320 | 321 | ##### Bug Fixes 322 | 323 | * Fix readme. (9dae398a) 324 | 325 | #### 0.2.7 (2019-04-26) 326 | 327 | ##### Bug Fixes 328 | 329 | * Fix readme. (77f57b97) 330 | 331 | #### 0.2.6 (2019-04-26) 332 | 333 | ##### Bug Fixes 334 | 335 | * Fix readme. (8d0d7236) 336 | 337 | #### 0.2.5 (2019-04-26) 338 | 339 | ##### Bug Fixes 340 | 341 | * Add logo. (4af8b68b) 342 | 343 | #### 0.2.4 (2019-04-26) 344 | 345 | ##### Bug Fixes 346 | 347 | * Progress: Small bug. (0ed91265) 348 | 349 | #### 0.2.3 (2019-04-26) 350 | 351 | ##### Bug Fixes 352 | 353 | * Fix typo in readme. (e10ae2c2) 354 | 355 | #### 0.2.3 (2019-04-26) 356 | 357 | ##### Bug Fixes 358 | 359 | * Fix typo in readme. (e10ae2c2) 360 | 361 | #### 0.2.3 (2019-04-26) 362 | 363 | ##### Bug Fixes 364 | 365 | * Fix typo in readme. (e10ae2c2) 366 | 367 | #### 0.2.2 (2019-04-26) 368 | 369 | ##### Bug Fixes 370 | 371 | * Small fix. (5ed748a8) 372 | 373 | ### 0.2.0 (2019-04-26) 374 | 375 | ##### New Features 376 | 377 | * Add new name. (6b86f188) 378 | 379 | ### 0.1.0 (2019-04-26) 380 | 381 | ##### New Features 382 | 383 | * Getting close to version 1. (c2bf2baf) 384 | 385 | ##### Other Changes 386 | 387 | * Add some features. (7a382afe) 388 | 389 | #### 0.0.1 (2019-04-24) 390 | 391 | #### 1.4.8 (2019-04-15) 392 | 393 | ##### Bug Fixes 394 | 395 | * Fix imports in Control. (f330363d) 396 | 397 | #### 1.4.7 (2019-04-15) 398 | 399 | ##### Bug Fixes 400 | 401 | * Rewire Components for docs. (a6e82d75) 402 | 403 | #### 1.4.6 (2019-04-07) 404 | 405 | ##### Bug Fixes 406 | 407 | * Change align defaults to empty string. (5d9b9519) 408 | 409 | #### 1.4.5 (2019-04-07) 410 | 411 | ##### Bug Fixes 412 | 413 | * Remove overflow:hidden; Change default breakpoints. (b460725b) 414 | 415 | #### 1.4.4 (2019-04-07) 416 | 417 | #### 1.4.3 (2019-04-07) 418 | 419 | #### 1.4.2 (2019-04-07) 420 | 421 | ##### Other Changes 422 | 423 | * Add absolute links for documentation images. (c79a9ed9) 424 | 425 | #### 1.4.1 (2019-04-07) 426 | 427 | ##### Other Changes 428 | 429 | * Add illustration for breakpoints. (7dffd8cb) 430 | 431 | ### 1.4.0 (2019-04-07) 432 | 433 | ##### New Features 434 | 435 | * encapsulate breakpoint mediaqueries with max-width (9d43afc0) 436 | 437 | #### 1.3.9 (2019-04-06) 438 | 439 | ##### Documentation Changes 440 | 441 | * Fix some details. (bd958e22) 442 | 443 | #### 1.3.8 (2019-04-06) 444 | 445 | ##### Other Changes 446 | 447 | * Fixes. (460995eb) 448 | 449 | #### 1.3.7 (2019-04-06) 450 | 451 | ##### Other Changes 452 | 453 | * Add logo. (79596737) 454 | 455 | #### 1.3.6 (2019-04-05) 456 | 457 | ##### Documentation Changes 458 | 459 | * Small fixes. (9bb4c2b4) 460 | 461 | #### 1.3.5 (2019-04-05) 462 | 463 | ##### Documentation Changes 464 | 465 | * Some details. (72c0a401) 466 | 467 | #### 1.3.4 (2019-04-05) 468 | 469 | ##### Documentation Changes 470 | 471 | * Some details. (abb397f7) 472 | 473 | #### 1.3.3 (2019-04-05) 474 | 475 | ##### Documentation Changes 476 | 477 | * Change opening description. (3a66984d) 478 | 479 | #### 1.3.2 (2019-04-05) 480 | 481 | ##### Documentation Changes 482 | 483 | * Fix examples. (6379ef86) 484 | 485 | #### 1.3.1 (2019-04-05) 486 | 487 | ##### Documentation Changes 488 | 489 | * Fix examples. (e7ef84b4) 490 | 491 | ### 1.3.0 (2019-04-05) 492 | 493 | ##### New Features 494 | 495 | * Add custom control-color. (ba912939) 496 | 497 | ### 1.2.0 (2019-03-30) 498 | 499 | ##### Refactors 500 | 501 | * Change prop reset to hasChildBoxes (5072a8c4) 502 | * Improve resetting structure (7b2988e8) 503 | 504 | #### 1.1.1 (2019-03-28) 505 | 506 | ##### Bug Fixes 507 | 508 | * Add box-sizing. Remove width: 100%; (5c27360b) 509 | 510 | ### 1.1.0 (2019-03-27) 511 | 512 | ##### New Features 513 | 514 | * Add manual reset-prop to box. (09923992) 515 | 516 | ## 1.0.0 (2019-03-27) 517 | 518 | ##### Breaking Changes 519 | 520 | * First working version (6834f7f2) 521 | 522 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Jason Quense 4 | 5 | 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: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | 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. 10 | 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![react-sled Logo](https://raw.github.com/andreasfaust/react-sled/master/react-sled-logo.jpg) 2 | 3 | **react-sled** is a carousel made with **react-spring**. 4 | 5 | [![NPM](https://img.shields.io/npm/v/react-sled.svg)](https://www.npmjs.com/package/react-sled) 6 | 7 | - Super-smooth spring animations (thanks to **react-spring**) 8 | - Lightweight and performant architecture 9 | - Touch- and Mousedrag (thanks to **react-with-gesture**) 10 | - Easy to style 11 | - Ready for server-side-rendering 12 | - All props are dynamically changeable 13 | - (Should be) Compatible with older Browsers from Internet Explorer 11 (Needs testing!) 14 | 15 | 🛷 [Have a look at the example!](https://react-sled.andreasfaust.com/) 16 | 17 | ## New Major release 2.0 18 | 19 | **Breaking Changes:** 20 | - Removed styled-components 21 | - Removed custom `ow`-unit 22 | - Use `react-spring` Version 9 and `react-with-gesture` Version 7 23 | 24 | **New Features:** 25 | - Full Type-Script support 26 | - Vertical Sliding 27 | - Set fixed proportion 28 | - Show multiple elements at once (`showElements`) 29 | - Move by multiple elements (`slideBy`) 30 | 31 | 32 | ## Install 33 | 34 | Install all dependencies via Yarn or NPM. 35 | 36 | ```bash 37 | yarn add react-sled react-spring@next react-use-gesture react react-dom 38 | ``` 39 | 40 | ## Usage 41 | 42 | ```jsx 43 | import React from "react"; 44 | import { Sled, Views, Progress, Control } from "react-sled"; 45 | import "react-sled/dist/main.css"; 46 | 47 | const images = ["my-image-1.jpg", "my-image-2.jpg"]; 48 | 49 | const App = () => { 50 | return ( 51 | 52 | 53 | {images.map((image, index) => ( 54 | {`My 55 | ))} 56 | 57 | 58 |
59 | 60 | 61 |
62 |
63 | {images.map((image, index) => ( 64 | 65 | ))} 66 |
67 |
68 | ); 69 | }; 70 | 71 | export default App; 72 | ``` 73 | 74 | ## Sled 75 | 76 | Sled is the wrapper-component. It takes no props. 77 | 78 | 79 | ## Views 80 | 81 | Render all your views into this component. 82 | It takes these optional props: 83 | 84 | | **Name** | **Type** | **Default** | **Description** | 85 | | :------------------- | :--------------- | :----------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------- | 86 | | **width** | String | `'100%'` | Sets the viewpager’s width. Allowed units: all CSS-units | 87 | | **height** | String | `null` | Sets the viewpager’s height. | 88 | | **proportion** | String | `2:1` | Provide either a width or height and set the other dimension proportional to it. If you provide a height and a width `proportion` is disabled. | 89 | | **showElements** | Number | `1` | Determines how many Slides/Views fit in the Sled’s viewport. | 90 | | **slideBy** | Number | `1` | Determines how many Slides/Views the Sled’s slides with one movement . | 91 | | **select** | Number | `undefined` | Select certain view. | 92 | | **style** | Object | `null` | Add inline styles to the view-wrapper.  | 93 | | **keyboard** | Boolean | `true` | Set Keyboard controls.  | 94 | | **dragging** | Boolean | `true` | Set Mouse- and Touch-Dragging.  | 95 | | **dragDistance** | Number or String | `40` | Distance the user has to drag the slider to trigger action. A number is calculated in Pixel. A string is converted to a number unless it has the unit `%`, which means "percent of Sled’s width".  | 96 | | **autoPlay** | Number | `undefined` | Activates automatic Sliding-Interval in Milliseconds.  | 97 | | **config** | Number | `{ mass: 1, tension: 210, friction: 20, clamp: true }` | react-spring animation-settings.  | 98 | | **pause** | Boolean | `false` | `autoPlay` (if activated) gets paused.  | 99 | | **pauseOnMouseOver** | Boolean | `true` | `autoPlay` (if activated) gets paused, as long as the user hovers over the sled.  | 100 | | **stopOnInteraction** | Boolean | `false` | `autoPlay` (if activated) gets stopped, after the user interacted with the sled.  | 101 | | **rewind** | Boolean | `false` | Rewind sled, when you want to go beyond the first or last view.  | 102 | | **onSledEnd** | function | `null` | Callback, that gets triggered after last view. | 103 | | **onAnimationStart** | function | `null` | Callback, that gets triggered when a sliding-animation starts. | 104 | | **onAnimationEnd** | function | `null` | Callback, that gets triggered when a sliding-animation ends. | 105 | 106 | 107 | ## Controls 108 | 109 | There is only one control-component for **Arrows** and **Selecting-Dots**. 110 | The prop `select` decides what the Control-element is: A string called `next` or `prev` will activate Arrow-functionality, a number Select-functionality. 111 | 112 | You can easily style it via CSS. The default-styles are scoped to the class-name `sled-progress-default`. They are contained in the file `dist/index.css`. 113 | If you give it a custom `className`-prop, the default-class will be overridden and the Progress will be completely unstyled. Then you can copy the default-styles from **[here](https://raw.github.com/andreasfaust/react-sled/master/sled/control/index.css)** as a starting-point. 114 | 115 | 116 | **Control Props Overview:** 117 | 118 | | **Name** | **Type** | **Default** | **Description** | 119 | | :--------- | :--------------- | :------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------ | 120 | | **select** | String or Number | `'next'` | Defines, if the `Control` has arrow- or dot-functionality. A number is the index of the target-view. A string can be `'prev'` or `'next'` | 121 | | **className** | String | Default depends on `select` | | 122 | | **style** | String | `''` | If you provide a `style` and no `preset`, the default `preset` gets completely replaced. If you provide a `style` and a `preset`, the `preset` gets extended. | 123 | 124 | **Arrow:** 125 | Default-Design: 126 | ```jsx 127 | 130 | ``` 131 | 132 | Your Custom-Design: 133 | ```jsx 134 | 141 | My custom arrow! 142 | 143 | ``` 144 | 145 | **Selection-Dot:** 146 | ```jsx 147 | 150 | ``` 151 | 152 | ## Progress 153 | 154 | react-sled has an Instagram-like progress-bar. 155 | You can easily style it via CSS. 156 | The default-styles are scoped to the class-name `sled-progress-default`. 157 | If you give it a custom `className`-prop, the Progress will be completely unstyled. You can copy the default-styles from **[here](https://raw.github.com/andreasfaust/react-sled/master/sled/progress/index.css)** as a starting-point. 158 | 159 | 160 | **Progress Props Overview:** 161 | 162 | | **Name** | **Type** | **Default** | **Description** | 163 | | :--------- | :--------------- | :------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------ | 164 | | **className** | String | Default depends on `select` | | 165 | | **style** | String | `''` | If you provide a `style` and no `preset`, the default `preset` gets completely replaced. If you provide a `style` and a `preset`, the `preset` gets extended. | 166 | 167 | ```jsx 168 | 172 | ``` 173 | 174 | ## useSledStore 175 | 176 | A hook, that exposes the plugin’s state-management. 177 | `useSledStore` is only useable inside the `Sled`-Component. 178 | It returns an `Array` with 2 elements: 179 | 180 | 1. **state** of type `object` 181 | 2. **dispatch** of type `function` 182 | 183 | 184 | 185 | ## To-Do 186 | 187 | - [ ] Control animation by frame on drag 188 | - [ ] Infinity-Mode 189 | - [ ] Nice documentation with live examples (using Docz) 190 | - [ ] automated testing 191 | 192 | ## Contributing 193 | 194 | Every contribution is very much appreciated. 195 | 196 | **If you like react-sled, don't hesitate to star it on [GitHub](https://github.com/AndreasFaust/react-sled).** 197 | 198 | ## License 199 | 200 | MIT © [AndreasFaust](https://github.com/AndreasFaust) 201 | 202 | ## Thanks 203 | 204 | This library is derived from the great work and especially this [code-sandbox-example](https://codesandbox.io/embed/n9vo1my91p) provided by [Paul Henschel](https://github.com/drcmda) and the react-spring-team. 205 | -------------------------------------------------------------------------------- /components/SledComponent.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Sled, Views, Progress, Control } from '../sled' 3 | import { useStateValue } from './state' 4 | import images from './images' 5 | 6 | const SledComponent = ({ children }) => { 7 | const [state] = useStateValue() 8 | return ( 9 |
10 | 11 | { 13 | console.log('THIS IS THE END') 14 | }} 15 | width={state.width} 16 | height={state.height} 17 | proportion={state.proportion} 18 | direction={state.direction} 19 | select={state.select} 20 | autoPlayInterval={state.autoPlayInterval} 21 | rewind={state.rewind} 22 | pauseOnMouseOver={state.pauseOnMouseOver} 23 | stopOnInteraction={state.stopOnInteraction} 24 | keyboard={state.keyboard} 25 | dragging={state.dragging} 26 | dragDistance={state.dragDistance} 27 | showElements={state.showElements} 28 | slideBy={state.slideBy} 29 | config={{ 30 | mass: state.mass, 31 | tension: state.tension, 32 | friction: state.friction, 33 | clamp: state.clamp 34 | }} 35 | onAnimationStart={() => { 36 | console.log('START Animation!') 37 | }} 38 | onAnimationEnd={() => { 39 | console.log('END Animation!') 40 | }} 41 | style={{ 42 | background: 'red' 43 | }} 44 | > 45 | {children} 46 | 47 | 48 |
49 | 50 | 51 |
52 |
53 | {images.map((image, index) => ( 54 | 55 | ))} 56 |
57 |
58 |
59 | ) 60 | } 61 | export default SledComponent 62 | -------------------------------------------------------------------------------- /components/SledImage.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react' 2 | import { useSledStore } from '../sled' 3 | 4 | const defaultImage = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7' 5 | 6 | const SledImage = ({ image, index }) => { 7 | const [{ currentIndex }] = useSledStore() 8 | const [src, setSrc] = useState(defaultImage) 9 | const [hasLoaded, setHasLoaded] = useState(false) 10 | 11 | useEffect(() => { 12 | if (image === src) return 13 | switch (index) { 14 | case currentIndex: 15 | case currentIndex - 1: 16 | case currentIndex + 1: 17 | setSrc(image) 18 | break 19 | default: 20 | } 21 | }, [currentIndex, image, index, src]) 22 | 23 | function onLoad() { 24 | if (src !== defaultImage) { 25 | setHasLoaded(true) 26 | } 27 | } 28 | 29 | return ( 30 |
45 |

{index}

46 | {'This 63 |
64 | ) 65 | } 66 | 67 | export default SledImage 68 | -------------------------------------------------------------------------------- /components/github-logo.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default () => ( 4 | 9 | 14 | 15 | 16 | ) 17 | -------------------------------------------------------------------------------- /components/header.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Github from './github-logo' 3 | 4 | export default () => ( 5 |
6 |

7 | 8 | 🛷 9 | 10 | {' '} 11 | react-sled 12 |

13 | 14 | 15 | 16 |
17 | ) -------------------------------------------------------------------------------- /components/images.js: -------------------------------------------------------------------------------- 1 | const images = [ 2 | 'https://source.unsplash.com/ANCoz0JMhiQ/1600x900', 3 | 'https://source.unsplash.com/uR6dIgDnt38/1600x900', 4 | 'https://source.unsplash.com/E2_k8SsuS7s/1600x900', 5 | 'https://source.unsplash.com/mGy1Jjr2e6M/1600x900', 6 | 'https://source.unsplash.com/TMHL7wald8I/1600x900', 7 | 'https://source.unsplash.com/-QKpblZde5I/1600x900', 8 | 'https://source.unsplash.com/o8cMgOUB-Z0/1600x900', 9 | 'https://source.unsplash.com/lzOzsGmAg3s/1600x900', 10 | 'https://source.unsplash.com/_7IUgAL60nc/1600x900', 11 | 'https://source.unsplash.com/7jwHx5q7WeA/1600x900', 12 | ] 13 | 14 | export default images -------------------------------------------------------------------------------- /components/settings.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState, useRef } from 'react' 2 | import Toggle from 'react-toggle' 3 | import debounce from 'lodash/debounce' 4 | import Slider from 'rc-slider' 5 | import 'rc-slider/assets/index.css' 6 | import { slide as Menu } from 'react-burger-menu' 7 | import { useStateValue } from './state' 8 | import useWindowSize from './useWindowSize' 9 | import Select from 'react-select' 10 | 11 | const customStyles = { 12 | 13 | container: () => ({ 14 | width: 150, 15 | position: 'relative', 16 | }), 17 | placeholder: () => ({ 18 | fontSize: '0.9rem' 19 | }), 20 | option: (provided) => ({ 21 | ...provided, 22 | fontSize: '0.9rem', 23 | }), 24 | 25 | } 26 | 27 | const useDebounce = (defaultValue) => { 28 | const [value, setValue] = useState(defaultValue) 29 | const dSetValue = useRef((event) => { 30 | event.persist() 31 | debounce(() => setValue(event.target.value), 150)() 32 | }) 33 | return [value, dSetValue] 34 | } 35 | 36 | const Wrapper = ({ children }) => { 37 | const { width } = useWindowSize() 38 | return width > 1024 39 | ?
{children}
40 | : {children} 41 | } 42 | 43 | const Settings = () => { 44 | const [state, dispatch] = useStateValue() 45 | 46 | const [proportion, setProportion] = React.useState(state.proportion) 47 | const [direction, setDirection] = React.useState(state.direction) 48 | 49 | const [width, setWidth] = useDebounce(state.width) 50 | const [height, setHeight] = useDebounce(state.height) 51 | const [autoPlayInterval, setAutoPlayInterval] = useDebounce(state.autoPlayInterval) 52 | const [select, setSelect] = useDebounce(state.select) 53 | 54 | 55 | useEffect(() => { 56 | dispatch({ type: 'width', value: width }) 57 | dispatch({ type: 'height', value: height }) 58 | dispatch({ type: 'direction', value: direction }) 59 | dispatch({ type: 'autoPlayInterval', value: +autoPlayInterval }) 60 | dispatch({ type: 'proportion', value: proportion }) 61 | }, [width, height, direction, autoPlayInterval, proportion]) 62 | 63 | useEffect(() => { 64 | if (parseInt(select, 10)) { 65 | dispatch({ type: 'select', value: parseInt(select, 10) }) 66 | } 67 | if (select === 'prev' || select === 'next') { 68 | dispatch({ type: 'select', value: select }) 69 | } 70 | }, [select]) 71 | 72 | return ( 73 | 74 |

78 | Settings: 79 |

80 |
81 | 91 | 102 | 133 | 134 | 144 | 154 | 164 | 174 | 184 |
185 | 186 |
187 | 195 | 196 | 204 | 212 | 213 | 221 | 229 | 230 |
231 | 232 |
233 | 236 | 244 | 252 | 260 | 268 |
269 |
270 | ) 271 | } 272 | 273 | export default Settings 274 | -------------------------------------------------------------------------------- /components/state.js: -------------------------------------------------------------------------------- 1 | import React, { createContext, useContext, useReducer } from 'react' 2 | export const StateContext = createContext() 3 | 4 | const initialState = { 5 | select: 3, 6 | rewind: true, 7 | direction: 'horizontal', 8 | showElements: 1, 9 | slideBy: 1, 10 | pauseOnMouseOver: true, 11 | stopOnInteraction: true, 12 | keyboard: true, 13 | dragging: true, 14 | dragDistance: 100, 15 | autoPlayInterval: 3000, 16 | width: '100%', 17 | proportion: '2:1', 18 | clamp: true, 19 | mass: 1, 20 | tension: 170, 21 | friction: 26 22 | } 23 | 24 | const reducer = (state, action) => { 25 | switch (action.type) { 26 | case 'select': return { ...state, select: action.value } 27 | case 'proportion': return { ...state, proportion: action.value } 28 | case 'direction': return { ...state, direction: action.value } 29 | case 'rewind': return { ...state, rewind: action.value } 30 | case 'pauseOnMouseOver': return { ...state, pauseOnMouseOver: action.value } 31 | case 'stopOnInteraction': return { ...state, stopOnInteraction: action.value } 32 | case 'keyboard': return { ...state, keyboard: action.value } 33 | case 'dragging': return { ...state, dragging: action.value } 34 | case 'dragDistance': return { ...state, dragDistance: action.value } 35 | case 'autoPlayInterval': return { ...state, autoPlayInterval: action.value } 36 | case 'width': return { ...state, width: action.value } 37 | case 'height': return { ...state, height: action.value } 38 | case 'clamp': return { ...state, clamp: action.value } 39 | case 'mass': return { ...state, mass: action.value } 40 | case 'tension': return { ...state, tension: action.value } 41 | case 'friction': return { ...state, friction: action.value } 42 | case 'showElements': return { ...state, showElements: action.value } 43 | case 'slideBy': return { ...state, slideBy: action.value } 44 | default: return state 45 | } 46 | } 47 | 48 | export const StateProvider = ({ children }) => ( 49 | 50 | {children} 51 | 52 | ) 53 | 54 | export const useStateValue = () => useContext(StateContext) 55 | -------------------------------------------------------------------------------- /components/useWindowSize.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react' 2 | 3 | export default function useWindowSize() { 4 | function getSize() { 5 | return { 6 | width: window.innerWidth, 7 | height: window.innerHeight 8 | } 9 | } 10 | 11 | const [windowSize, setWindowSize] = useState({ width: undefined, height: undefined }) 12 | 13 | useEffect(() => { 14 | setWindowSize(getSize()) 15 | 16 | function handleResize() { 17 | setWindowSize(getSize()) 18 | } 19 | 20 | window.addEventListener('resize', handleResize) 21 | return () => window.removeEventListener('resize', handleResize) 22 | }, []) 23 | 24 | return windowSize 25 | } 26 | -------------------------------------------------------------------------------- /next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | const withCSS = require('@zeit/next-css') 2 | 3 | module.exports = withCSS({}) 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-sled", 3 | "version": "2.0.4", 4 | "description": "react-sled is a carousel made with react-spring.", 5 | "author": "AndreasFaust", 6 | "license": "MIT", 7 | "repository": "AndreasFaust/react-sled", 8 | "homepage": "https://react-sled.andreasfaust.com/", 9 | "files": [ 10 | "/dist" 11 | ], 12 | "main": "./dist/index.js", 13 | "module": "./dist/index.es.js", 14 | "jsnext:main": "./dist/index.es.js", 15 | "types": "./dist/index.d.ts", 16 | "engines": { 17 | "node": ">=8", 18 | "npm": ">=5" 19 | }, 20 | "dependencies": {}, 21 | "devDependencies": { 22 | "@rollup/plugin-commonjs": "^11.0.1", 23 | "@rollup/plugin-node-resolve": "^6.1.0", 24 | "@rollup/plugin-url": "^4.0.0", 25 | "@types/node": "^13.1.4", 26 | "@types/react": "^16.9.17", 27 | "@typescript-eslint/eslint-plugin": "2.14.0", 28 | "@typescript-eslint/parser": "2.14.0", 29 | "@zeit/next-css": "^1.0.1", 30 | "babel-eslint": "^10.0.3", 31 | "csstype": "^2.6.8", 32 | "eslint": "6.8.0", 33 | "eslint-config-react-app": "^5.1.0", 34 | "eslint-plugin-flowtype": "4.5.3", 35 | "eslint-plugin-import": "2.19.1", 36 | "eslint-plugin-jsx-a11y": "6.x", 37 | "eslint-plugin-react": "7.17.0", 38 | "eslint-plugin-react-hooks": "2.3.0", 39 | "generate-changelog": "^1.8.0", 40 | "gh-pages": "^2.1.1", 41 | "lodash": "^4.17.15", 42 | "next": "9.1.7", 43 | "prop-types": "^15.7.2", 44 | "rc-slider": "^8.7.1", 45 | "react": "16.12.0", 46 | "react-burger-menu": "^2.6.13", 47 | "react-dom": "^16.12.0", 48 | "react-select": "^3.0.8", 49 | "react-spring": "^9.0.0-beta.34", 50 | "react-toggle": "^4.1.1", 51 | "react-use-gesture": "^7.0.1", 52 | "rollup": "^1.28.0", 53 | "rollup-plugin-css-only": "^2.0.0", 54 | "rollup-plugin-peer-deps-external": "^2.2.0", 55 | "rollup-plugin-typescript2": "^0.25.3", 56 | "typescript": "^3.7.4" 57 | }, 58 | "peerDependencies": { 59 | "react": "^16.8.5", 60 | "react-dom": "^16.8.5", 61 | "react-spring": "^9.0.0-beta.34", 62 | "react-use-gesture": "^7.0.0" 63 | }, 64 | "keywords": [ 65 | "React", 66 | "Viewpager", 67 | "Carousel", 68 | "Gallery", 69 | "Slideshow", 70 | "Slider", 71 | "Spring", 72 | "Animation", 73 | "react-spring", 74 | "react-use-gesture" 75 | ], 76 | "scripts": { 77 | "dev": "next dev", 78 | "build": "rollup -c", 79 | "release:major": "yarn build && git add . && changelog -M && git add CHANGELOG.md && git commit -m 'updated CHANGELOG.md' && npm version major && git push origin && git push origin --tags && npm publish", 80 | "release:minor": "yarn build && git add . && changelog -m && git add CHANGELOG.md && git commit -m 'updated CHANGELOG.md' && npm version minor && git push origin && git push origin --tags && npm publish", 81 | "release:patch": "yarn build && git add . && changelog -p && git add CHANGELOG.md && git commit -m 'updated CHANGELOG.md' && npm version patch && git push origin && git push origin --tags && npm publish", 82 | "deploy-files": "rsync -vrtu --delete -e ssh ./out/ box@finlay.uberspace.de:/var/www/virtual/box/html/react-sled.andreasfaust.com", 83 | "deploy": "next build && next export && yarn deploy-files" 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /pages/css/header.css: -------------------------------------------------------------------------------- 1 | .header { 2 | position: fixed; 3 | left: 0; 4 | right: 0; 5 | top: 0; 6 | z-index: 1000; 7 | display: flex; 8 | display: flex; 9 | height: 4rem; 10 | align-items: center; 11 | justify-content: center; 12 | background: #fff; 13 | box-shadow: 0 0 1rem hsla(0, 0%, 0%, 0.1); 14 | } 15 | 16 | h1 { 17 | /* font-family: plex-serif; */ 18 | font-size: 1.25rem; 19 | letter-spacing: -0.01rem; 20 | padding: 0 1rem; 21 | display: flex; 22 | align-items: center; 23 | position: relative; 24 | /* text-align: center; */ 25 | } 26 | 27 | h1 span { 28 | font-size: 2em; 29 | margin: 0 0.5rem 0 0; 30 | } 31 | 32 | .github { 33 | position: absolute; 34 | display: block; 35 | width: 2.25rem; 36 | height: 2.25rem; 37 | right: 0.5rem; 38 | } 39 | 40 | .github svg { 41 | width: 100%; 42 | } 43 | 44 | .github:hover path, 45 | .github:active path { 46 | fill: #cc1d1c; 47 | } 48 | 49 | 50 | @media (min-width: 1025px) { 51 | h1 { 52 | font-size: 2rem; 53 | } 54 | h1 span { 55 | margin: 0 1rem 0 0; 56 | } 57 | .github { 58 | position: absolute; 59 | display: block; 60 | width: 2.75rem; 61 | height: 2.75rem; 62 | right: 0.5rem; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /pages/css/index.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'ia'; 3 | src: url('/plex-mono.woff') format('woff'); 4 | } 5 | @font-face { 6 | font-family: 'ia-bold'; 7 | src: url('/plex-mono-bold.woff') format('woff'); 8 | } 9 | 10 | .bold { 11 | font-family: 'ia-bold'; 12 | } 13 | 14 | body { 15 | font-family: 'ia'; 16 | margin: 0; 17 | padding: 0; 18 | overflow-x: hidden; 19 | } 20 | 21 | .wrapper { 22 | } 23 | 24 | .content { 25 | margin: 8rem 0; 26 | width: 100%; 27 | } 28 | @media (min-width: 1025px) { 29 | .content { 30 | width: calc(100% - 300px); 31 | margin-left: 300px; 32 | } 33 | } 34 | 35 | .sled-wrapper { 36 | flex-grow: 1; 37 | padding: 0 1rem; 38 | } 39 | @media (min-width: 1025px) { 40 | .sled-wrapper { 41 | padding: 0 2rem; 42 | } 43 | } 44 | 45 | .controls { 46 | display: flex; 47 | justify-content: center; 48 | width: 100%; 49 | } 50 | .controls > button { 51 | margin: 10px; 52 | } 53 | 54 | .testContent { 55 | position: absolute; 56 | left: 0; 57 | right: 0; 58 | top: 0; 59 | bottom: 0; 60 | display: flex; 61 | align-items: center; 62 | justify-content: center; 63 | font-size: 4rem; 64 | color: #fff; 65 | } -------------------------------------------------------------------------------- /pages/css/menue.css: -------------------------------------------------------------------------------- 1 | /* Position and sizing of burger button */ 2 | .bm-burger-button { 3 | position: fixed; 4 | width: 2rem; 5 | height: 1rem; 6 | left: 1rem; 7 | top: 1.5rem; 8 | } 9 | 10 | /* Color/shape of burger icon bars */ 11 | .bm-burger-bars { 12 | background: #373a47; 13 | } 14 | 15 | /* Color/shape of burger icon bars on hover*/ 16 | .bm-burger-bars-hover { 17 | background: #a90000; 18 | } 19 | 20 | /* Position and sizing of clickable cross button */ 21 | .bm-cross-button { 22 | height: 24px; 23 | width: 24px; 24 | } 25 | 26 | /* Color/shape of close button cross */ 27 | .bm-cross { 28 | background: #bdc3c7; 29 | } 30 | 31 | .bm-overlay { 32 | top: 0; 33 | } -------------------------------------------------------------------------------- /pages/css/reset.css: -------------------------------------------------------------------------------- 1 | html { 2 | font-family: sans-serif; 3 | -ms-text-size-adjust: 100%; 4 | -webkit-text-size-adjust: 100%; 5 | } 6 | body { 7 | margin: 0; 8 | } 9 | article, 10 | aside, 11 | details, 12 | figcaption, 13 | figure, 14 | footer, 15 | header, 16 | main, 17 | menu, 18 | nav, 19 | section, 20 | summary { 21 | display: block; 22 | } 23 | audio, 24 | canvas, 25 | progress, 26 | video { 27 | display: inline-block; 28 | } 29 | audio:not([controls]) { 30 | display: none; 31 | height: 0; 32 | } 33 | progress { 34 | vertical-align: baseline; 35 | } 36 | [hidden], 37 | template { 38 | display: none; 39 | } 40 | a { 41 | background-color: transparent; 42 | -webkit-text-decoration-skip: objects; 43 | color: $greyText; 44 | text-decoration: none; 45 | } 46 | a:active, 47 | a:hover { 48 | outline-width: 0; 49 | } 50 | abbr[title] { 51 | border-bottom: none; 52 | text-decoration: underline; 53 | text-decoration: underline dotted; 54 | } 55 | b, 56 | strong { 57 | font-weight: normal; 58 | } 59 | dfn { 60 | font-style: normal; 61 | } 62 | mark { 63 | background-color: #ff0; 64 | color: #000; 65 | } 66 | small { 67 | } 68 | sub, 69 | sup { 70 | font-size: 75%; 71 | line-height: 0; 72 | position: relative; 73 | vertical-align: baseline; 74 | } 75 | sub { 76 | bottom: -0.25em; 77 | } 78 | sup { 79 | top: -0.5em; 80 | } 81 | img { 82 | border-style: none; 83 | } 84 | svg:not(:root) { 85 | overflow: hidden; 86 | } 87 | code, 88 | kbd, 89 | pre, 90 | samp { 91 | font-family: monospace, monospace; 92 | font-size: 1em; 93 | } 94 | figure { 95 | /* // margin: 1em 40px; */ 96 | } 97 | hr { 98 | box-sizing: content-box; 99 | height: 0; 100 | overflow: visible; 101 | } 102 | button, 103 | input, 104 | optgroup, 105 | select, 106 | textarea { 107 | font: inherit; 108 | margin: 0; 109 | } 110 | optgroup { 111 | font-weight: 700; 112 | } 113 | button, 114 | input { 115 | overflow: visible; 116 | } 117 | button, 118 | select { 119 | text-transform: none; 120 | } 121 | [type="reset"], 122 | [type="submit"], 123 | button, 124 | html [type="button"] { 125 | -webkit-appearance: button; 126 | } 127 | [type="button"]::-moz-focus-inner, 128 | [type="reset"]::-moz-focus-inner, 129 | [type="submit"]::-moz-focus-inner, 130 | button::-moz-focus-inner { 131 | border-style: none; 132 | padding: 0; 133 | } 134 | [type="button"]:-moz-focusring, 135 | [type="reset"]:-moz-focusring, 136 | [type="submit"]:-moz-focusring, 137 | button:-moz-focusring { 138 | outline: 1px dotted ButtonText; 139 | } 140 | fieldset { 141 | border: 1px solid silver; 142 | margin: 0 2px; 143 | padding: 0.35em 0.625em 0.75em; 144 | } 145 | legend { 146 | box-sizing: border-box; 147 | color: inherit; 148 | display: table; 149 | max-width: 100%; 150 | padding: 0; 151 | white-space: normal; 152 | } 153 | textarea { 154 | overflow: auto; 155 | } 156 | [type="checkbox"], 157 | [type="radio"] { 158 | box-sizing: border-box; 159 | padding: 0; 160 | } 161 | [type="number"]::-webkit-inner-spin-button, 162 | [type="number"]::-webkit-outer-spin-button { 163 | height: auto; 164 | } 165 | [type="search"] { 166 | -webkit-appearance: textfield; 167 | outline-offset: -2px; 168 | } 169 | [type="search"]::-webkit-search-cancel-button, 170 | [type="search"]::-webkit-search-decoration { 171 | -webkit-appearance: none; 172 | } 173 | ::-webkit-input-placeholder { 174 | color: inherit; 175 | opacity: 0.54; 176 | } 177 | ::-webkit-file-upload-button { 178 | -webkit-appearance: button; 179 | font: inherit; 180 | } 181 | html { 182 | box-sizing: border-box; 183 | } 184 | * { 185 | box-sizing: inherit; 186 | } 187 | *:before { 188 | box-sizing: inherit; 189 | } 190 | *:after { 191 | box-sizing: inherit; 192 | } 193 | body { 194 | color: $greyText; 195 | font-weight: normal; 196 | word-wrap: break-word; 197 | font-kerning: normal; 198 | -moz-font-feature-settings: "kern", "liga", "clig", "calt"; 199 | -ms-font-feature-settings: "kern", "liga", "clig", "calt"; 200 | -webkit-font-feature-settings: "kern", "liga", "clig", "calt"; 201 | font-feature-settings: "kern", "liga", "clig", "calt"; 202 | } 203 | img { 204 | max-width: 100%; 205 | margin-left: 0; 206 | margin-right: 0; 207 | margin-top: 0; 208 | margin-bottom: 0; 209 | padding-bottom: 0; 210 | padding-left: 0; 211 | padding-right: 0; 212 | padding-top: 0; 213 | } 214 | h1 { 215 | margin-left: 0; 216 | margin-right: 0; 217 | margin-top: 0; 218 | margin-bottom: 0; 219 | padding-bottom: 0; 220 | padding-left: 0; 221 | padding-right: 0; 222 | padding-top: 0; 223 | color: inherit; 224 | font-weight: normal; 225 | text-rendering: optimizeLegibility; 226 | } 227 | h2 { 228 | margin-left: 0; 229 | margin-right: 0; 230 | margin-top: 0; 231 | margin-bottom: 0; 232 | padding-bottom: 0; 233 | padding-left: 0; 234 | padding-right: 0; 235 | padding-top: 0; 236 | color: inherit; 237 | font-weight: normal; 238 | text-rendering: optimizeLegibility; 239 | } 240 | h3 { 241 | margin-left: 0; 242 | margin-right: 0; 243 | margin-top: 0; 244 | margin-bottom: 0; 245 | padding-bottom: 0; 246 | padding-left: 0; 247 | padding-right: 0; 248 | padding-top: 0; 249 | color: inherit; 250 | font-weight: normal; 251 | text-rendering: optimizeLegibility; 252 | } 253 | h4 { 254 | margin-left: 0; 255 | margin-right: 0; 256 | margin-top: 0; 257 | margin-bottom: 0; 258 | padding-bottom: 0; 259 | padding-left: 0; 260 | padding-right: 0; 261 | padding-top: 0; 262 | color: inherit; 263 | font-weight: normal; 264 | text-rendering: optimizeLegibility; 265 | } 266 | h5 { 267 | margin-left: 0; 268 | margin-right: 0; 269 | margin-top: 0; 270 | margin-bottom: 0; 271 | padding-bottom: 0; 272 | padding-left: 0; 273 | padding-right: 0; 274 | padding-top: 0; 275 | color: inherit; 276 | font-weight: normal; 277 | text-rendering: optimizeLegibility; 278 | } 279 | h6 { 280 | margin-left: 0; 281 | margin-right: 0; 282 | margin-top: 0; 283 | margin-bottom: 0; 284 | padding-bottom: 0; 285 | padding-left: 0; 286 | padding-right: 0; 287 | padding-top: 0; 288 | color: inherit; 289 | font-weight: normal; 290 | text-rendering: optimizeLegibility; 291 | } 292 | hgroup { 293 | margin-left: 0; 294 | margin-right: 0; 295 | margin-top: 0; 296 | padding-bottom: 0; 297 | padding-left: 0; 298 | padding-right: 0; 299 | padding-top: 0; 300 | margin-bottom: 1.45rem; 301 | } 302 | ul { 303 | margin-left: 0; 304 | margin-right: 0; 305 | margin-top: 0; 306 | margin-bottom: 0; 307 | padding-bottom: 0; 308 | padding-left: 0; 309 | padding-right: 0; 310 | padding-top: 0; 311 | list-style: none; 312 | list-style-image: none; 313 | } 314 | ol { 315 | margin-left: 0; 316 | margin-right: 0; 317 | margin-top: 0; 318 | margin-bottom: 0; 319 | padding-bottom: 0; 320 | padding-left: 0; 321 | padding-right: 0; 322 | padding-top: 0; 323 | list-style-position: outside; 324 | list-style-image: none; 325 | } 326 | dl { 327 | margin-left: 0; 328 | margin-right: 0; 329 | margin-top: 0; 330 | padding-bottom: 0; 331 | padding-left: 0; 332 | padding-right: 0; 333 | padding-top: 0; 334 | margin-bottom: 0; 335 | } 336 | dd { 337 | margin-left: 0; 338 | margin-right: 0; 339 | margin-top: 0; 340 | padding-bottom: 0; 341 | padding-left: 0; 342 | padding-right: 0; 343 | padding-top: 0; 344 | margin-bottom: 0; 345 | } 346 | p { 347 | margin-left: 0; 348 | margin-right: 0; 349 | margin-top: 0; 350 | padding-bottom: 0; 351 | padding-left: 0; 352 | padding-right: 0; 353 | padding-top: 0; 354 | margin-bottom: 0; 355 | } 356 | figure { 357 | margin-left: 0; 358 | margin-right: 0; 359 | margin-top: 0; 360 | padding-bottom: 0; 361 | padding-left: 0; 362 | padding-right: 0; 363 | padding-top: 0; 364 | margin-bottom: 0; 365 | } 366 | pre { 367 | margin-left: 0; 368 | margin-right: 0; 369 | margin-top: 0; 370 | padding-bottom: 0; 371 | padding-left: 0; 372 | padding-right: 0; 373 | padding-top: 0; 374 | margin-bottom: 0; 375 | font-size: 0.85rem; 376 | line-height: 1.42; 377 | background: hsla(0, 0%, 0%, 0.04); 378 | border-radius: 3px; 379 | overflow: auto; 380 | word-wrap: normal; 381 | padding: 0; 382 | } 383 | table { 384 | margin-left: 0; 385 | margin-right: 0; 386 | margin-top: 0; 387 | padding-bottom: 0; 388 | padding-left: 0; 389 | padding-right: 0; 390 | padding-top: 0; 391 | margin-bottom: 0; 392 | font-size: 1rem; 393 | line-height: 1; 394 | border-collapse: collapse; 395 | width: 100%; 396 | } 397 | fieldset { 398 | margin-left: 0; 399 | margin-right: 0; 400 | margin-top: 0; 401 | padding-bottom: 0; 402 | padding-left: 0; 403 | padding-right: 0; 404 | padding-top: 0; 405 | margin-bottom: 0; 406 | } 407 | blockquote { 408 | margin-left: 0; 409 | margin-right: 0; 410 | margin-top: 0; 411 | padding-bottom: 0; 412 | padding-left: 0; 413 | padding-right: 0; 414 | padding-top: 0; 415 | margin-bottom: 0; 416 | } 417 | form { 418 | margin-left: 0; 419 | margin-right: 0; 420 | margin-top: 0; 421 | padding-bottom: 0; 422 | padding-left: 0; 423 | padding-right: 0; 424 | padding-top: 0; 425 | margin-bottom: 0; 426 | } 427 | noscript { 428 | margin-left: 0; 429 | margin-right: 0; 430 | margin-top: 0; 431 | padding-bottom: 0; 432 | padding-left: 0; 433 | padding-right: 0; 434 | padding-top: 0; 435 | margin-bottom: 0; 436 | } 437 | iframe { 438 | margin-left: 0; 439 | margin-right: 0; 440 | margin-top: 0; 441 | padding-bottom: 0; 442 | padding-left: 0; 443 | padding-right: 0; 444 | padding-top: 0; 445 | margin-bottom: 0; 446 | } 447 | hr { 448 | margin-left: 0; 449 | margin-right: 0; 450 | margin-top: 0; 451 | padding-bottom: 0; 452 | padding-left: 0; 453 | padding-right: 0; 454 | padding-top: 0; 455 | margin-bottom: 0; 456 | background: hsla(0, 0%, 0%, 0.2); 457 | border: none; 458 | height: 1px; 459 | } 460 | address { 461 | margin-left: 0; 462 | margin-right: 0; 463 | margin-top: 0; 464 | padding-bottom: 0; 465 | padding-left: 0; 466 | padding-right: 0; 467 | padding-top: 0; 468 | margin-bottom: 0; 469 | } 470 | b { 471 | font-weight: normal; 472 | } 473 | strong { 474 | font-weight: normal; 475 | } 476 | dt { 477 | font-weight: normal; 478 | } 479 | th { 480 | font-weight: normal; 481 | } 482 | li { 483 | margin-bottom: 0; 484 | } 485 | ol li { 486 | padding-left: 0; 487 | } 488 | ul li { 489 | padding-left: 0; 490 | } 491 | li > ol { 492 | } 493 | li > ul { 494 | } 495 | blockquote *:last-child { 496 | } 497 | li *:last-child { 498 | } 499 | p *:last-child { 500 | } 501 | li > p { 502 | } 503 | code { 504 | font-size: 0.85rem; 505 | line-height: 1.45rem; 506 | } 507 | kbd { 508 | font-size: 0.85rem; 509 | line-height: 1.45rem; 510 | } 511 | samp { 512 | font-size: 0.85rem; 513 | line-height: 1.45rem; 514 | } 515 | abbr { 516 | border-bottom: 1px dotted hsla(0, 0%, 0%, 0.5); 517 | cursor: help; 518 | } 519 | acronym { 520 | border-bottom: 1px dotted hsla(0, 0%, 0%, 0.5); 521 | cursor: help; 522 | } 523 | abbr[title] { 524 | border-bottom: 1px dotted hsla(0, 0%, 0%, 0.5); 525 | cursor: help; 526 | text-decoration: none; 527 | } 528 | thead { 529 | text-align: left; 530 | } 531 | td, 532 | th { 533 | text-align: left; 534 | border-bottom: 1px solid hsla(0, 0%, 0%, 0.12); 535 | font-feature-settings: "tnum"; 536 | -moz-font-feature-settings: "tnum"; 537 | -ms-font-feature-settings: "tnum"; 538 | -webkit-font-feature-settings: "tnum"; 539 | padding-left: 0.96667rem; 540 | padding-right: 0.96667rem; 541 | padding-top: 0.725rem; 542 | padding-bottom: calc(0.725rem - 1px); 543 | } 544 | th:first-child, 545 | td:first-child { 546 | padding-left: 0; 547 | } 548 | th:last-child, 549 | td:last-child { 550 | padding-right: 0; 551 | } 552 | tt, 553 | code { 554 | background-color: hsla(0, 0%, 0%, 0.04); 555 | border-radius: 3px; 556 | font-family: "SFMono-Regular", Consolas, "Roboto Mono", "Droid Sans Mono", 557 | "Liberation Mono", Menlo, Courier, monospace; 558 | padding: 0; 559 | padding-top: 0.2em; 560 | padding-bottom: 0.2em; 561 | } 562 | pre code { 563 | background: none; 564 | line-height: 1.42; 565 | } 566 | code:before, 567 | code:after, 568 | tt:before, 569 | tt:after { 570 | letter-spacing: -0.2em; 571 | content: " "; 572 | } 573 | pre code:before, 574 | pre code:after, 575 | pre tt:before, 576 | pre tt:after { 577 | content: ""; 578 | } 579 | @media only screen and (max-width: 480px) { 580 | html { 581 | font-size: 100%; 582 | } 583 | } 584 | -------------------------------------------------------------------------------- /pages/css/settings.css: -------------------------------------------------------------------------------- 1 | .settings { 2 | position: fixed; 3 | width: 300px; 4 | z-index: 100; 5 | background: #fff; 6 | box-shadow: 0 0 1rem hsla(0, 0%, 0%, 0.1); 7 | top: 0; 8 | bottom: 0; 9 | padding: 1rem 0; 10 | overflow-y: scroll; 11 | -webkit-overflow-scrolling: touch; 12 | } 13 | 14 | @media (min-width: 1025px) { 15 | .settings { 16 | top: 4rem; 17 | } 18 | } 19 | 20 | .bm-item-list { 21 | padding: 1rem 0 8rem; 22 | } 23 | 24 | .settings__column--3 { 25 | margin-top: 1rem; 26 | } 27 | 28 | 29 | 30 | .background { 31 | } 32 | 33 | 34 | h2 { 35 | 36 | } 37 | 38 | .settings__h2 { 39 | font-size: 0.9rem; 40 | padding: 0.5rem 1rem; 41 | height: 3.5rem; 42 | user-select: none; 43 | display: flex; 44 | align-items: center; 45 | } 46 | .settings__h2:focus { 47 | outline: none; 48 | box-shadow: none; 49 | } 50 | 51 | .settings__h3 { 52 | font-weight: normal; 53 | font-size: 0.9rem; 54 | margin: 0 10px 0 0; 55 | min-width: 150px; 56 | flex-grow: 1; 57 | } 58 | 59 | .settings__label { 60 | display: flex; 61 | align-items: center; 62 | padding: 0.5rem 1rem 0.5rem 0; 63 | margin-left: 2rem; 64 | height: 2.5rem; 65 | border-top: 1px solid #ebebeb; 66 | } 67 | 68 | .settings__label--config .settings__h3 { 69 | min-width: 120px; 70 | } 71 | 72 | .settings__label--disabled > * { 73 | pointer-events: none; 74 | opacity: 0.5; 75 | } 76 | .settings__label--select .settings__h3 { 77 | min-width: auto; 78 | } 79 | 80 | .react-toggle { 81 | margin-left: 1rem; 82 | } 83 | 84 | .settings__input { 85 | font-family: 'ia'; 86 | width: 4rem; 87 | font-size: 0.9rem; 88 | display: flex; 89 | align-items: center; 90 | justify-content: center; 91 | padding: 0.5rem; 92 | margin-left: 0.5rem; 93 | border-radius: 0.25rem; 94 | border: none; 95 | background: #efefef; 96 | box-shadow: none; 97 | } 98 | 99 | .rc-slider-handle { 100 | border-color: red !important; 101 | } 102 | .rc-slider-rail { 103 | background-color: #ccc !important; 104 | } 105 | .rc-slider-track { 106 | background-color: red !important; 107 | } 108 | -------------------------------------------------------------------------------- /pages/css/toggle.css: -------------------------------------------------------------------------------- 1 | .react-toggle { 2 | touch-action: pan-x; 3 | 4 | display: inline-block; 5 | position: relative; 6 | cursor: pointer; 7 | background-color: transparent; 8 | border: 0; 9 | padding: 0; 10 | 11 | -webkit-touch-callout: none; 12 | -webkit-user-select: none; 13 | -khtml-user-select: none; 14 | -moz-user-select: none; 15 | -ms-user-select: none; 16 | user-select: none; 17 | 18 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 19 | -webkit-tap-highlight-color: transparent; 20 | } 21 | 22 | .react-toggle-screenreader-only { 23 | border: 0; 24 | clip: rect(0 0 0 0); 25 | height: 1px; 26 | margin: -1px; 27 | overflow: hidden; 28 | padding: 0; 29 | position: absolute; 30 | width: 1px; 31 | } 32 | 33 | .react-toggle--disabled { 34 | cursor: not-allowed; 35 | opacity: 0.5; 36 | -webkit-transition: opacity 0.25s; 37 | transition: opacity 0.25s; 38 | } 39 | 40 | .react-toggle-track { 41 | width: 50px; 42 | height: 24px; 43 | padding: 0; 44 | border-radius: 30px; 45 | background-color: #4d4d4d; 46 | -webkit-transition: all 0.2s ease; 47 | -moz-transition: all 0.2s ease; 48 | transition: all 0.2s ease; 49 | } 50 | 51 | .react-toggle:hover:not(.react-toggle--disabled) .react-toggle-track { 52 | background-color: #000000; 53 | } 54 | 55 | .react-toggle--checked .react-toggle-track { 56 | background-color: #19ab27; 57 | } 58 | 59 | .react-toggle--checked:hover:not(.react-toggle--disabled) .react-toggle-track { 60 | background-color: #128d15; 61 | } 62 | 63 | .react-toggle-track-check { 64 | position: absolute; 65 | width: 14px; 66 | height: 10px; 67 | top: 0px; 68 | bottom: 0px; 69 | margin-top: auto; 70 | margin-bottom: auto; 71 | line-height: 0; 72 | left: 8px; 73 | opacity: 0; 74 | -webkit-transition: opacity 0.25s ease; 75 | -moz-transition: opacity 0.25s ease; 76 | transition: opacity 0.25s ease; 77 | } 78 | 79 | .react-toggle--checked .react-toggle-track-check { 80 | opacity: 1; 81 | -webkit-transition: opacity 0.25s ease; 82 | -moz-transition: opacity 0.25s ease; 83 | transition: opacity 0.25s ease; 84 | } 85 | 86 | .react-toggle-track-x { 87 | position: absolute; 88 | width: 10px; 89 | height: 10px; 90 | top: 0px; 91 | bottom: 0px; 92 | margin-top: auto; 93 | margin-bottom: auto; 94 | line-height: 0; 95 | right: 10px; 96 | opacity: 1; 97 | -webkit-transition: opacity 0.25s ease; 98 | -moz-transition: opacity 0.25s ease; 99 | transition: opacity 0.25s ease; 100 | } 101 | 102 | .react-toggle--checked .react-toggle-track-x { 103 | opacity: 0; 104 | } 105 | 106 | .react-toggle-thumb { 107 | transition: all 0.5s cubic-bezier(0.23, 1, 0.32, 1) 0ms; 108 | position: absolute; 109 | top: 1px; 110 | left: 1px; 111 | width: 22px; 112 | height: 22px; 113 | border: 1px solid #4d4d4d; 114 | border-radius: 50%; 115 | background-color: #fafafa; 116 | 117 | -webkit-box-sizing: border-box; 118 | -moz-box-sizing: border-box; 119 | box-sizing: border-box; 120 | 121 | -webkit-transition: all 0.25s ease; 122 | -moz-transition: all 0.25s ease; 123 | transition: all 0.25s ease; 124 | } 125 | 126 | .react-toggle--checked .react-toggle-thumb { 127 | left: 27px; 128 | border-color: #19ab27; 129 | } 130 | 131 | .react-toggle--focus .react-toggle-thumb { 132 | -webkit-box-shadow: 0px 0px 3px 2px #0099e0; 133 | -moz-box-shadow: 0px 0px 3px 2px #0099e0; 134 | box-shadow: 0px 0px 2px 3px #0099e0; 135 | } 136 | 137 | .react-toggle:active:not(.react-toggle--disabled) .react-toggle-thumb { 138 | -webkit-box-shadow: 0px 0px 5px 5px #0099e0; 139 | -moz-box-shadow: 0px 0px 5px 5px #0099e0; 140 | box-shadow: 0px 0px 5px 5px #0099e0; 141 | } 142 | -------------------------------------------------------------------------------- /pages/custom.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const NotFoundPage = () => ( 4 |
5 |

I AM CUSTOM 404

6 |

You just hit a route that doesn't exist... the sadness.

7 |
8 | ) 9 | 10 | export default NotFoundPage 11 | -------------------------------------------------------------------------------- /pages/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import './css/reset.css' 3 | import './css/toggle.css' 4 | import './css/index.css' 5 | import './css/header.css' 6 | import './css/settings.css' 7 | import './css/menue.css' 8 | import { StateProvider } from '../components/state' 9 | import SledComponent from '../components/SledComponent' 10 | import Header from '../components/header' 11 | import Settings from '../components/settings' 12 | import SledImage from '../components/SledImage' 13 | 14 | import images from '../components/images' 15 | 16 | const App = () => { 17 | return ( 18 | 19 |
20 | 21 |
22 | 23 | {images.map((image, index) => ( 24 | 29 | ))} 30 | 31 |
32 | 33 | ) 34 | } 35 | 36 | export default App 37 | -------------------------------------------------------------------------------- /pages/test-basic.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import images from '../components/images' 3 | import { Sled, Views, Progress, Control } from '../sled' 4 | 5 | const App = () => { 6 | return ( 7 |
8 | 9 | 13 | {images.map(image => ( 14 | My Image 19 | ))} 20 | 21 | 22 |
23 | 24 | 25 |
26 |
27 | {images.map((image, index) => ( 28 | 29 | ))} 30 |
31 |
32 | 38 |
39 | ) 40 | } 41 | 42 | export default App 43 | -------------------------------------------------------------------------------- /pages/test.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import './css/reset.css' 3 | import './css/toggle.css' 4 | import './css/index.css' 5 | import './css/header.css' 6 | import './css/settings.css' 7 | import './css/menue.css' 8 | import { StateProvider } from '../components/state' 9 | import SledComponent from '../components/SledComponent' 10 | import Header from '../components/header' 11 | import Settings from '../components/settings' 12 | 13 | import images from '../components/images' 14 | 15 | const App = () => { 16 | return ( 17 | 18 |
19 | 20 |
21 | 22 | {images.map((image, index) => ( 23 |
30 | {index + 1} 31 |
32 | ))} 33 |
34 |
35 | 36 | ) 37 | } 38 | 39 | export default App 40 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndreasFaust/react-sled/c9168f72fd1f062d4b8401f0597c1831cdb3027a/public/favicon.ico -------------------------------------------------------------------------------- /public/plex-mono-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndreasFaust/react-sled/c9168f72fd1f062d4b8401f0597c1831cdb3027a/public/plex-mono-bold.woff -------------------------------------------------------------------------------- /public/plex-mono.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndreasFaust/react-sled/c9168f72fd1f062d4b8401f0597c1831cdb3027a/public/plex-mono.woff -------------------------------------------------------------------------------- /react-sled-logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndreasFaust/react-sled/c9168f72fd1f062d4b8401f0597c1831cdb3027a/react-sled-logo.jpg -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import typescript from 'rollup-plugin-typescript2' 2 | import css from 'rollup-plugin-css-only' 3 | import commonjs from '@rollup/plugin-commonjs' 4 | import resolve from '@rollup/plugin-node-resolve' 5 | import external from 'rollup-plugin-peer-deps-external' 6 | 7 | import pkg from './package.json' 8 | 9 | export default { 10 | input: 'sled/index.ts', 11 | output: [ 12 | { 13 | file: pkg.main, 14 | format: 'cjs', 15 | exports: 'named', 16 | sourcemap: true 17 | }, 18 | { 19 | file: pkg.module, 20 | format: 'es', 21 | exports: 'named', 22 | sourcemap: true 23 | } 24 | ], 25 | plugins: [ 26 | external(), 27 | resolve({ 28 | browser: true 29 | }), 30 | typescript({ 31 | tsconfig: 'tsconfig.rollup.json', 32 | rollupCommonJSResolveHack: true, 33 | exclude: '**/__tests__/**', 34 | clean: true 35 | }), 36 | commonjs({ 37 | include: ['node_modules/**'], 38 | exclude: ['**/*.stories.js'], 39 | namedExports: { 40 | 'node_modules/react/react.js': [ 41 | 'Children', 42 | 'Component', 43 | 'PropTypes', 44 | 'createElement' 45 | ], 46 | 'node_modules/react-dom/index.js': ['render'] 47 | } 48 | }), 49 | css({ output: './dist/main.css' }) 50 | ] 51 | } 52 | -------------------------------------------------------------------------------- /sled/control/hooks/useClassName.ts: -------------------------------------------------------------------------------- 1 | import { TSelect } from "../../state/types-defaults" 2 | import { useStateContext } from '../../state' 3 | import { useDirectionDisabled } from './useDirectionDisabled' 4 | 5 | function getClassName(type: 'index' | 'direction', select: TSelect, className: string): string { 6 | const [{ currentIndex }] = useStateContext() 7 | 8 | const directionDisabled = useDirectionDisabled(select) 9 | 10 | const baseClass: string = 'sled-control' 11 | const typeClass: string = `${baseClass}-${type}` 12 | const typeClassSpecific: string = `${typeClass}-${select}` 13 | const distinctClass: string = `${baseClass}-${className || type + '-default'}` 14 | const disabledClasses: string[] = [ 15 | `${baseClass}-disabled`, 16 | `${typeClass}-disabled`, 17 | `${distinctClass}-disabled` 18 | ] 19 | const activeClasses: string[] = [ 20 | `${baseClass}-active`, 21 | `${typeClass}-active`, 22 | `${distinctClass}-active` 23 | ] 24 | 25 | const classes: string[] = [ 26 | baseClass, 27 | typeClass, 28 | typeClassSpecific, 29 | distinctClass, 30 | ] 31 | 32 | if (select === currentIndex) { // isActive 33 | return [...classes, ...activeClasses].join(' ') 34 | } 35 | if (directionDisabled) { 36 | return [...classes, ...disabledClasses].join(' ') 37 | } 38 | return classes.join(' ') 39 | } 40 | 41 | export default (select: TSelect, className: string) => { 42 | switch (typeof select) { 43 | case 'number': 44 | return getClassName('index', select, className) 45 | case 'string': 46 | default: 47 | return getClassName('direction', select, className) 48 | } 49 | } -------------------------------------------------------------------------------- /sled/control/hooks/useClick.ts: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useStateContext } from '../../state' 3 | import { TSelect } from "../../state/types-defaults" 4 | 5 | interface IReturn { 6 | (): void; 7 | } 8 | 9 | const onClickFunction = (select: TSelect): IReturn => { 10 | const [{ stopOnInteraction }, dispatch] = useStateContext() 11 | 12 | function onClick() { 13 | if (stopOnInteraction) { 14 | dispatch({ type: 'SET_AUTOPLAY', autoPlayInterval: undefined }) 15 | } 16 | if (typeof select === 'number') { 17 | dispatch({ type: 'SELECT', index: select }) 18 | } else { 19 | dispatch({ type: select === 'next' ? 'NEXT' : 'PREV' }) 20 | } 21 | } 22 | return onClick 23 | } 24 | 25 | export default onClickFunction -------------------------------------------------------------------------------- /sled/control/hooks/useDirectionDisabled.ts: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react' 2 | import { useStateContext } from '../../state' 3 | import { TSelect } from "../../state/types-defaults" 4 | 5 | export const useDirectionDisabled = (select: TSelect) => { 6 | const [{ rewind, currentIndex, viewCount }] = useStateContext() 7 | const [disabled, setDisabled] = useState(false) 8 | useEffect(() => { 9 | if (typeof select === 'string') { 10 | if (rewind) { 11 | setDisabled(false) 12 | return 13 | } 14 | if (select === 'next' && currentIndex === viewCount - 1) { 15 | setDisabled(true) 16 | } else if (select === 'prev' && currentIndex === 0) { 17 | setDisabled(true) 18 | } else { 19 | setDisabled(false) 20 | } 21 | } 22 | }, [currentIndex, rewind]) 23 | return disabled 24 | } 25 | 26 | -------------------------------------------------------------------------------- /sled/control/hooks/useLabel.ts: -------------------------------------------------------------------------------- 1 | import { TSelect } from "../../state/types-defaults" 2 | 3 | export default (select: TSelect) => { 4 | switch (typeof select) { 5 | case 'number': 6 | return `Slide to view with index ${select}.` 7 | case 'string': 8 | default: 9 | return `Slide to ${select === 'next' ? 'next' : 'previous'} view.` 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /sled/control/index.css: -------------------------------------------------------------------------------- 1 | /* direction */ 2 | 3 | .sled-control-direction-default { 4 | cursor: pointer; 5 | width: 40px; 6 | height: 40px; 7 | display: flex; 8 | justify-content: center; 9 | align-items: center; 10 | background: none; 11 | box-shadow: none; 12 | border: none; 13 | transition: opacity 0.5s, transform 0.5s; 14 | opacity: 0.5; 15 | } 16 | 17 | .sled-control-direction-default.sled-control-direction-disabled { 18 | pointer-events: none; 19 | } 20 | 21 | .sled-control-direction-default.sled-control-direction-prev { 22 | transform-origin: right center; 23 | } 24 | .sled-control-direction-default.sled-control-direction-prev:before { 25 | border-left: 3px solid black; 26 | transform: rotate(-45deg); 27 | } 28 | 29 | .sled-control-direction-default.sled-control-direction-next { 30 | transform-origin: left center; 31 | } 32 | .sled-control-direction-default.sled-control-direction-next:before { 33 | border-right: 3px solid black; 34 | transform: rotate(45deg); 35 | } 36 | 37 | .sled-control-direction-default:focus { 38 | box-shadow: none; 39 | outline: none; 40 | } 41 | 42 | .sled-control-direction-default:before { 43 | content: ''; 44 | display: block; 45 | flex-shrink: 0; 46 | width: 15px; 47 | height: 15px; 48 | border-top: 3px solid black; 49 | } 50 | 51 | .sled-control-direction-default:focus { 52 | opacity: 1; 53 | } 54 | 55 | .sled-control-direction-default:hover, 56 | .sled-control-direction-default:active { 57 | opacity: 1; 58 | transform: scale(1.2); 59 | } 60 | 61 | .sled-control-direction-default-disabled { 62 | opacity: 0.25; 63 | } 64 | 65 | /* index */ 66 | 67 | .sled-control-index-default { 68 | cursor: pointer; 69 | width: 40px; 70 | height: 40px; 71 | display: flex; 72 | justify-content: center; 73 | align-items: center; 74 | background: none; 75 | box-shadow: none; 76 | border: none; 77 | transition: opacity 0.5s, transform 0.5s; 78 | opacity: 0.4; 79 | } 80 | 81 | .sled-control-index-default.sled-control-index-disabled { 82 | pointer-events: none; 83 | } 84 | 85 | .sled-control-index-default:focus { 86 | box-shadow: none; 87 | outline: none; 88 | } 89 | 90 | .sled-control-index-default:before { 91 | content: ''; 92 | display: block; 93 | flex-shrink: 0; 94 | width: 10px; 95 | height: 10px; 96 | background: grey; 97 | border-radius: 50%; 98 | } 99 | 100 | .sled-control-index-default:hover, 101 | .sled-control-index-default:focus { 102 | transform: scale(1.2); 103 | } 104 | 105 | .sled-control-index-default:hover, 106 | .sled-control-index-default:focus, 107 | .sled-control-index-default:active { 108 | opacity: 0.6; 109 | } 110 | 111 | .sled-control-index-default:disabled { 112 | pointer-events: none; 113 | } 114 | 115 | .sled-control-index-default-active { 116 | opacity: 1; 117 | pointer-events: none; 118 | } -------------------------------------------------------------------------------- /sled/control/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { useRef, ReactNode } from 'react' 2 | import CSS from 'csstype'; 3 | import { TSelect } from "../state/types-defaults" 4 | import { useStateContext } from '../state' 5 | import { useDirectionDisabled } from './hooks/useDirectionDisabled' 6 | 7 | import './index.css' 8 | 9 | import useClassName from './hooks/useClassName' 10 | import useLabel from './hooks/useLabel' 11 | import useClick from './hooks/useClick' 12 | 13 | import useFocus from '../hooks/useFocus' 14 | 15 | interface IProps { 16 | select: TSelect 17 | style?: CSS.Properties 18 | className?: string 19 | children?: ReactNode 20 | } 21 | 22 | const SledControl: React.FC = ({ 23 | children, select, style, className 24 | }) => { 25 | const controlRef = useRef() 26 | useFocus(controlRef) 27 | const directionDisabled = useDirectionDisabled(select) 28 | const classNames = useClassName(select, className) 29 | const label = useLabel(select) 30 | const onClick = useClick(select) 31 | const [{ currentIndex }] = useStateContext() 32 | 33 | return ( 34 |
44 | {children} 45 |
46 | ) 47 | } 48 | 49 | export default SledControl 50 | -------------------------------------------------------------------------------- /sled/hooks/useAutoPlay.ts: -------------------------------------------------------------------------------- 1 | import { useRef, useEffect } from 'react' 2 | import { useStateContext } from '../state' 3 | 4 | const useInterval = (callback: any, interval: number): void => { 5 | const savedCallback = useRef(callback) 6 | 7 | useEffect(() => { 8 | savedCallback.current = callback 9 | }) 10 | 11 | useEffect(() => { 12 | function tick() { 13 | savedCallback.current() 14 | } 15 | if (typeof interval === 'number') { 16 | let id = setInterval(tick, interval) 17 | return () => clearInterval(id) 18 | } 19 | }, [interval]) 20 | } 21 | 22 | export default (autoPlayIntervalNew: number): void => { 23 | const [{ pause, autoPlayInterval }, dispatch] = useStateContext() 24 | 25 | useEffect(() => { 26 | if (typeof autoPlayIntervalNew === 'number') { 27 | dispatch({ type: 'SET_AUTOPLAY', autoPlayInterval: autoPlayIntervalNew }) 28 | } else { 29 | console.warn(`Sled-Error: "autoplay" must be of type "number", not "${typeof autoPlayIntervalNew}".`) 30 | } 31 | }, [autoPlayIntervalNew]) 32 | 33 | useInterval(() => { 34 | dispatch({ type: 'NEXT' }) 35 | }, !pause && autoPlayInterval) 36 | } 37 | -------------------------------------------------------------------------------- /sled/hooks/useConfig.ts: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react' 2 | import { useStateContext } from '../state' 3 | import { SpringConfig } from 'react-spring' 4 | 5 | export default (config: SpringConfig) => { 6 | const [, dispatch] = useStateContext() 7 | useEffect(() => { 8 | dispatch({ type: 'SET_CONFIG', config }) 9 | }, [config, dispatch]) 10 | } 11 | -------------------------------------------------------------------------------- /sled/hooks/useContainerStyles.ts: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useStateContext } from '../state' 3 | import CSS from 'csstype' 4 | 5 | export function useSliderStyles() { 6 | const [{ sliderSize, direction, dragging }] = useStateContext() 7 | const [styles, setStyles] = React.useState({}) 8 | 9 | React.useEffect(() => { 10 | const defaultStyles = { 11 | position: 'absolute', 12 | top: 0, 13 | left: 0, 14 | willChange: 'transform', 15 | overflow: 'hidden', 16 | cursor: dragging ? 'grab' : 'auto' 17 | } 18 | if (direction === 'vertical') { 19 | setStyles({ 20 | ...defaultStyles, 21 | width: '100%', 22 | height: sliderSize || 0, 23 | }) 24 | } else { 25 | setStyles({ 26 | ...defaultStyles, 27 | width: sliderSize || 0, 28 | height: '100%', 29 | display: 'flex', 30 | }) 31 | } 32 | }, [direction, dragging, sliderSize]) 33 | 34 | return styles 35 | } 36 | 37 | 38 | export function useViewStyles(): CSS.Properties { 39 | const [{ dimensions: { width, height }, viewCount, direction, showElements }] = useStateContext() 40 | const [styles, setStyles] = React.useState({}) 41 | 42 | React.useEffect(() => { 43 | const defaultStyles = { 44 | position: 'relative', 45 | } 46 | if (direction === 'vertical') { 47 | setStyles({ 48 | ...defaultStyles, 49 | width: '100%', 50 | height: height / showElements 51 | }) 52 | } else { 53 | setStyles({ 54 | ...defaultStyles, 55 | width: width / showElements, 56 | height: '100%' 57 | }) 58 | } 59 | }, [width, height, direction, viewCount, showElements]) 60 | 61 | return styles 62 | } 63 | -------------------------------------------------------------------------------- /sled/hooks/useCursor.ts: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useStateContext } from '../state' 3 | 4 | type TCursor = 'grab' | 'auto' 5 | 6 | export default (): TCursor => { 7 | const [{ dragging }] = useStateContext() 8 | 9 | const [cursor, setCursor] = React.useState('auto') 10 | React.useEffect(() => { 11 | setCursor(dragging ? 'grab' : 'auto') 12 | }, [dragging]) 13 | 14 | return cursor 15 | } -------------------------------------------------------------------------------- /sled/hooks/useDimensions.ts: -------------------------------------------------------------------------------- 1 | 2 | import React from 'react' 3 | import { debounce } from '../utils/debounce' 4 | import { useStateContext } from '../state' 5 | import { TRef, TDimension } from '../state/types-defaults' 6 | 7 | interface IProportion { 8 | width: TDimension 9 | height: TDimension 10 | offsetWidth: number 11 | offsetHeight: number 12 | proportion: string 13 | dispatch 14 | } 15 | 16 | interface IDimensions { 17 | width: TDimension 18 | height: TDimension 19 | } 20 | 21 | function getProportion({ 22 | width, 23 | height, 24 | offsetWidth, 25 | offsetHeight, 26 | proportion, 27 | dispatch 28 | }: IProportion) { 29 | if (!proportion) return 30 | 31 | const [proportionWidth, proportionHeight] = proportion.split(':') 32 | let dimensions: IDimensions = { width: 0, height: 0 } 33 | 34 | if (width) { 35 | const heightValue = (offsetWidth * +proportionHeight) / +proportionWidth 36 | dimensions = { 37 | width: offsetWidth, 38 | height: heightValue 39 | } 40 | } else { 41 | const widthValue = (offsetHeight * +proportionWidth) / +proportionHeight 42 | dimensions = { 43 | width: widthValue, 44 | height: offsetHeight 45 | } 46 | } 47 | dispatch({ 48 | type: 'SET_DIMENSIONS', 49 | dimensions: dimensions 50 | }) 51 | } 52 | 53 | export default (ref: TRef) => { 54 | const [{ dimensionsDOM, proportion }, dispatch] = useStateContext() 55 | 56 | React.useEffect(() => { 57 | function onResize() { 58 | const { offsetWidth, offsetHeight } = ref.current 59 | getProportion({ 60 | ...dimensionsDOM, 61 | offsetWidth, 62 | offsetHeight, 63 | proportion, 64 | dispatch 65 | }) 66 | } 67 | onResize() 68 | const dOnResize = debounce(onResize, 200) 69 | window.addEventListener('resize', dOnResize) 70 | return () => window.removeEventListener('resize', dOnResize) 71 | 72 | }, [dimensionsDOM, proportion]) 73 | 74 | } 75 | -------------------------------------------------------------------------------- /sled/hooks/useDimensionsDOM.ts: -------------------------------------------------------------------------------- 1 | 2 | import React from 'react' 3 | import { useStateContext, TDimension } from '../state' 4 | 5 | export default (width: TDimension, height: TDimension) => { 6 | const [, dispatch] = useStateContext() 7 | 8 | React.useEffect(() => { 9 | if (!width && !height) { 10 | dispatch({ 11 | type: 'SET_DIMENSIONS_DOM', 12 | dimensionsDOM: { width: '100%', height: null } 13 | }) 14 | dispatch({ type: 'SET_PROPORTION', proportion: '2:1' }) 15 | return 16 | } 17 | 18 | dispatch({ 19 | type: 'SET_DIMENSIONS_DOM', 20 | dimensionsDOM: { width, height } 21 | }) 22 | 23 | }, [width, height]) 24 | 25 | } 26 | -------------------------------------------------------------------------------- /sled/hooks/useDirection.ts: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useStateContext } from '../state' 3 | import { TDirection } from '../state/types-defaults' 4 | 5 | export default (direction: TDirection) => { 6 | const [, dispatch] = useStateContext() 7 | 8 | React.useEffect(() => { 9 | if (direction) { 10 | dispatch({ type: 'SET_DIRECTION', direction }) 11 | } 12 | }, [direction]) 13 | } 14 | -------------------------------------------------------------------------------- /sled/hooks/useDragGesture.ts: -------------------------------------------------------------------------------- 1 | import { useDrag } from 'react-use-gesture' 2 | import { useStateContext } from '../state' 3 | import { SpringsUpdateFn } from 'react-spring' 4 | 5 | interface ISet { 6 | x: number 7 | } 8 | 9 | export default (set: SpringsUpdateFn) => { 10 | const [{ 11 | dragging, 12 | dragDistance, 13 | dimensions: { width, height }, 14 | currentIndex, 15 | direction, 16 | stopOnInteraction, 17 | showElements, 18 | slideBy 19 | }, dispatch] = useStateContext() 20 | 21 | const bind = useDrag(({ 22 | down, 23 | movement: [xDelta, yDelta], 24 | direction: [xDir, yDir], 25 | distance, 26 | cancel, 27 | canceled, 28 | }) => { 29 | if (canceled) return 30 | 31 | if (stopOnInteraction) { 32 | dispatch({ type: 'SET_AUTOPLAY', autoPlayInterval: undefined }) 33 | } 34 | if (down && distance > dragDistance) { 35 | const dirValue = direction === 'horizontal' ? xDir : yDir 36 | dispatch({ 37 | type: dirValue > 0 38 | ? 'PREV' 39 | : 'NEXT', 40 | pause: true 41 | }) 42 | cancel() 43 | } 44 | set(() => { 45 | const x = direction === 'horizontal' 46 | ? (-currentIndex * (width / showElements * slideBy)) + (down ? xDelta : 0) 47 | : (-currentIndex * (height / showElements * slideBy)) + (down ? yDelta : 0) 48 | return { 49 | x, 50 | immediate: false, 51 | cursor: down ? 'grabbing' : 'grab', 52 | onStart: undefined, 53 | onRest: undefined 54 | } 55 | }) 56 | dispatch({ type: 'SET_PAUSE', pause: true }) 57 | }) 58 | 59 | return dragging && bind 60 | } 61 | -------------------------------------------------------------------------------- /sled/hooks/useDragging.ts: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react' 2 | import { useStateContext } from '../state' 3 | 4 | function getDistance(distance: string | number, width: number): number { 5 | switch (typeof distance) { 6 | case 'number': return distance 7 | case 'string': 8 | if (distance.indexOf('%') >= 0) { 9 | return (width / 100) * +distance.replace('%', '') 10 | } 11 | const distanceNumber = parseInt(distance, 10) 12 | if (distanceNumber) { 13 | return distanceNumber 14 | } 15 | console.warn('Sled-Error: dragDistance must either be a String with unit % or a number.') 16 | return 40 17 | default: 18 | return 40 19 | } 20 | } 21 | 22 | export default (dragging: boolean, dragDistance: string | number) => { 23 | const [{ dimensions: { width } }, dispatch] = useStateContext() 24 | 25 | useEffect(() => { 26 | const distance = getDistance(dragDistance, width) 27 | dispatch({ type: 'SET_DRAG_DISTANCE', dragDistance: distance }) 28 | }, [dispatch, dragDistance, width]) 29 | 30 | useEffect(() => { 31 | dispatch({ type: 'SET_DRAGGING', dragging }) 32 | }, [dispatch, dragging]) 33 | } 34 | -------------------------------------------------------------------------------- /sled/hooks/useFocus.ts: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react' 2 | import { useStateContext } from '../state' 3 | 4 | const useFocus = (ref: React.MutableRefObject) => { 5 | const [, dispatch] = useStateContext() 6 | 7 | useEffect(() => { 8 | function onFocus() { 9 | dispatch({ type: 'SET_FOCUS', focus: true }) 10 | } 11 | function onBlur() { 12 | dispatch({ type: 'SET_FOCUS', focus: false }) 13 | } 14 | ref.current.addEventListener('focus', onFocus) 15 | ref.current.addEventListener('blur', onBlur) 16 | return () => { 17 | ref.current.removeEventListener('focus', onFocus) 18 | ref.current.removeEventListener('blur', onBlur) 19 | } 20 | }, []) 21 | } 22 | 23 | export default useFocus 24 | -------------------------------------------------------------------------------- /sled/hooks/useKeyboard.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef } from 'react' 2 | import { useStateContext } from '../state' 3 | 4 | const useKeyboard = (keyboard: boolean) => { 5 | const [{ hasFocus, stopOnInteraction }, dispatch] = useStateContext() 6 | const hasFocusRef = useRef(false) 7 | 8 | useEffect(() => { 9 | hasFocusRef.current = hasFocus 10 | }, [hasFocus]) 11 | 12 | useEffect(() => { 13 | function onKeyup(event: KeyboardEvent) { 14 | if (!hasFocusRef.current) return 15 | 16 | if (stopOnInteraction) { 17 | dispatch({ type: 'SET_AUTOPLAY', autoPlayInterval: undefined }) 18 | } 19 | 20 | switch (event.keyCode) { 21 | case 37: 22 | dispatch({ type: 'PREV' }) 23 | break 24 | case 39: 25 | dispatch({ type: 'NEXT' }) 26 | break 27 | } 28 | } 29 | if (keyboard) { 30 | document.addEventListener('keyup', onKeyup) 31 | return () => document.removeEventListener('keyup', onKeyup) 32 | } 33 | }, [keyboard, stopOnInteraction]) 34 | } 35 | 36 | export default useKeyboard 37 | -------------------------------------------------------------------------------- /sled/hooks/useMouseOver.ts: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react' 2 | import { useStateContext } from '../state' 3 | 4 | export default (pauseOnMouseOver: boolean, ref: React.MutableRefObject) => { 5 | const [{ autoPlayInterval }, dispatch] = useStateContext() 6 | useEffect(() => { 7 | dispatch({ type: 'SET_PAUSE', pause: false }) 8 | 9 | function onMouseEnter() { 10 | dispatch({ type: 'SET_MOUSEOVER', pauseOnMouseOver: true }) 11 | dispatch({ type: 'SET_PAUSE', pause: true }) 12 | } 13 | function onMouseLeave() { 14 | dispatch({ type: 'SET_MOUSEOVER', pauseOnMouseOver: false }) 15 | dispatch({ type: 'SET_PAUSE', pause: false }) 16 | } 17 | if (pauseOnMouseOver && autoPlayInterval) { 18 | ref.current.addEventListener('mouseenter', onMouseEnter) 19 | ref.current.addEventListener('mouseover', onMouseEnter) 20 | ref.current.addEventListener('mouseout', onMouseLeave) 21 | return () => { 22 | dispatch({ type: 'SET_MOUSEOVER', pauseOnMouseOver: false }) 23 | ref.current.removeEventListener('mouseenter', onMouseEnter) 24 | ref.current.removeEventListener('mouseover', onMouseEnter) 25 | ref.current.removeEventListener('mouseout', onMouseLeave) 26 | } 27 | } 28 | }, [pauseOnMouseOver, autoPlayInterval, dispatch, ref]) 29 | } 30 | -------------------------------------------------------------------------------- /sled/hooks/usePause.ts: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react' 2 | import { useStateContext } from '../state' 3 | 4 | export default (pause: boolean) => { 5 | const [, dispatch] = useStateContext() 6 | useEffect(() => { 7 | dispatch({ type: 'SET_PAUSE', pause }) 8 | }, [pause]) 9 | } 10 | -------------------------------------------------------------------------------- /sled/hooks/useProportion.ts: -------------------------------------------------------------------------------- 1 | 2 | import React from 'react' 3 | import { useStateContext, TDimension } from '../state' 4 | 5 | export default (width: TDimension, height: TDimension, proportion?: string): void => { 6 | const [, dispatch] = useStateContext() 7 | React.useEffect(() => { 8 | if (!height && !proportion) { 9 | dispatch({ type: 'SET_PROPORTION', proportion: '2:1' }) 10 | } else { 11 | dispatch({ type: 'SET_PROPORTION', proportion }) 12 | } 13 | }, [width, height, proportion]) 14 | } 15 | -------------------------------------------------------------------------------- /sled/hooks/useRewind.ts: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react' 2 | import { useStateContext } from '../state' 3 | 4 | export default (rewind: boolean) => { 5 | const [, dispatch] = useStateContext() 6 | useEffect(() => { 7 | dispatch({ type: 'SET_REWIND', rewind }) 8 | }, [rewind]) 9 | } 10 | -------------------------------------------------------------------------------- /sled/hooks/useSelect.ts: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react' 2 | import { useStateContext } from '../state' 3 | 4 | const useSelect = (select?: number | 'next' | 'prev') => { 5 | const [, dispatch] = useStateContext() 6 | useEffect(() => { 7 | if (typeof select === 'number') { 8 | dispatch({ type: 'SELECT', index: select }) 9 | } else { 10 | dispatch({ type: select === 'next' ? 'NEXT' : 'PREV' }) 11 | } 12 | }, [select]) 13 | } 14 | 15 | export default useSelect 16 | -------------------------------------------------------------------------------- /sled/hooks/useShowElements.ts: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react' 2 | import { useStateContext } from '../state' 3 | 4 | const useShowElements = (showElements: number) => { 5 | const [, dispatch] = useStateContext() 6 | useEffect(() => { 7 | if (showElements) { 8 | dispatch({ type: 'SET_SHOWELEMENTS', showElements }) 9 | } 10 | }, [showElements]) 11 | } 12 | 13 | export default useShowElements 14 | -------------------------------------------------------------------------------- /sled/hooks/useSlideBy.ts: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react' 2 | import { useStateContext } from '../state' 3 | 4 | const useShowElements = (slideBy: number) => { 5 | const [, dispatch] = useStateContext() 6 | useEffect(() => { 7 | if (slideBy) { 8 | dispatch({ type: 'SET_SLIDEBY', slideBy }) 9 | } 10 | }, [slideBy]) 11 | } 12 | 13 | export default useShowElements 14 | -------------------------------------------------------------------------------- /sled/hooks/useSlideSteps.ts: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react' 2 | import { useStateContext } from '../state' 3 | 4 | const useSlideSteps = () => { 5 | const [{ viewCount, slideBy }, dispatch] = useStateContext() 6 | useEffect(() => { 7 | const slideSteps = Math.ceil(viewCount / slideBy) 8 | dispatch({ type: 'SET_SLIDESTEPS', slideSteps }) 9 | }, [viewCount, slideBy]) 10 | } 11 | 12 | export default useSlideSteps 13 | -------------------------------------------------------------------------------- /sled/hooks/useSliderSize.ts: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useStateContext } from '../state' 3 | 4 | export default function useSliderStyles() { 5 | const [{ dimensions: { width, height }, viewCount, direction, showElements }, dispatch] = useStateContext() 6 | 7 | React.useEffect(() => { 8 | if (direction === 'vertical') { 9 | dispatch({ type: 'SET_SLIDERSIZE', sliderSize: (height / showElements) * viewCount }) 10 | } else { 11 | dispatch({ type: 'SET_SLIDERSIZE', sliderSize: (width / showElements) * viewCount }) 12 | } 13 | }, [direction, width, height, showElements, viewCount]) 14 | 15 | } -------------------------------------------------------------------------------- /sled/hooks/useStopOnInteraction.ts: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react' 2 | import { useStateContext } from '../state' 3 | 4 | const useStopOnInteraction = (stopOnInteraction: boolean) => { 5 | const [, dispatch] = useStateContext() 6 | 7 | useEffect(() => { 8 | dispatch({ type: 'SET_STOPONINTERACTION', stopOnInteraction }) 9 | }, [stopOnInteraction]) 10 | } 11 | 12 | export default useStopOnInteraction 13 | -------------------------------------------------------------------------------- /sled/hooks/useViewCount.ts: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react' 2 | import { useStateContext } from '../state' 3 | 4 | export default function (children?: React.ReactNode) { 5 | const [, dispatch] = useStateContext() 6 | 7 | useEffect(() => { 8 | dispatch({ type: 'SET_VIEWCOUNT', count: React.Children.toArray(children).length }) 9 | }, [children]) 10 | } 11 | -------------------------------------------------------------------------------- /sled/hooks/useX.ts: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useStateContext } from '../state' 3 | 4 | export default function () { 5 | const [{ direction, dimensions: { width, height }, showElements, slideBy, currentIndex }] = useStateContext() 6 | const [x, setX] = React.useState(0) 7 | React.useEffect(() => { 8 | if (direction === 'horizontal') { 9 | setX(-currentIndex * (width / showElements * slideBy)) 10 | } else { 11 | setX(-currentIndex * (height / showElements * slideBy)) 12 | } 13 | }, [direction, width, height, showElements, slideBy, currentIndex]) 14 | 15 | return x 16 | } 17 | -------------------------------------------------------------------------------- /sled/index.css: -------------------------------------------------------------------------------- 1 | .sled { 2 | position: relative; 3 | overflow: hidden; 4 | user-select: none; 5 | } 6 | .sled:focus, 7 | .sled-view:focus { 8 | outline: none; 9 | } 10 | 11 | /* .sled-proportion::after { 12 | content: ''; 13 | display: block; 14 | width: 100%; 15 | height: 0; 16 | } */ 17 | -------------------------------------------------------------------------------- /sled/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Sled } from './sled' 2 | export { default as Views } from './views' 3 | export { default as Control } from './control' 4 | export { default as Progress } from './progress' 5 | export { useStateContext as useSledStore } from './state' 6 | -------------------------------------------------------------------------------- /sled/progress/controls.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import CSS from 'csstype' 3 | 4 | import { useStateContext } from '../state' 5 | import Control from '../control' 6 | 7 | interface IProps { 8 | className: string 9 | } 10 | 11 | const SledProgressControls: React.FC = ({ className }) => { 12 | const [{ slideSteps }] = useStateContext() 13 | 14 | return ( 15 |
16 | { 17 | [...Array(slideSteps ? slideSteps || 1 : 1)].map((view, index) => ( 18 | 31 | ))} 32 |
33 | ) 34 | } 35 | 36 | export default SledProgressControls 37 | -------------------------------------------------------------------------------- /sled/progress/index.css: -------------------------------------------------------------------------------- 1 | .sled-progress-default { 2 | position: relative; 3 | width: 100%; 4 | display: flex; 5 | align-items: center; 6 | overflow: hidden; 7 | height: 20px; 8 | } 9 | 10 | .sled-progress-default-rail { 11 | position: absolute; 12 | left: 0; 13 | width: 100%; 14 | pointer-events: none; 15 | background: black; 16 | height: 4px; 17 | } 18 | 19 | .sled-progress-default-track { 20 | background: red; 21 | height: 4px; 22 | } 23 | 24 | .sled-progress-default-separators { 25 | position: absolute; 26 | z-index: 200; 27 | top: 0; 28 | bottom: 0; 29 | left: 0; 30 | right: 0; 31 | display: flex; 32 | justify-content: space-between; 33 | pointer-events: none; 34 | } 35 | 36 | .sled-progress-default-separator { 37 | width: 4px; 38 | background: white; 39 | } 40 | 41 | /* Mimic "justify-content: space-evenly;" */ 42 | .sled-progress-default-separators:before, 43 | .sled-progress-default-separators:after { 44 | content: ''; 45 | display: block; 46 | height: 100%; 47 | } 48 | 49 | .sled-progress-default-controls { 50 | position: absolute; 51 | z-index: 100; 52 | top: 0; 53 | bottom: 0; 54 | left: 0; 55 | right: 0; 56 | display: flex; 57 | } 58 | 59 | .sled-progress-default-control { 60 | position: absolute; 61 | z-index: 100; 62 | top: 0; 63 | bottom: 0; 64 | left: 0; 65 | right: 0; 66 | display: flex; 67 | } -------------------------------------------------------------------------------- /sled/progress/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import SledProgressSeparators from './separators' 3 | import SledProgressTrack from './track' 4 | import SledProgressControls from './controls' 5 | import CSS from 'csstype' 6 | 7 | import './index.css' 8 | 9 | interface IProps { 10 | className?: string 11 | style?: CSS.Properties 12 | } 13 | 14 | const SledProgress: React.FC = ({ className, style }) => { 15 | const [defaultClass, setDefaultClass] = React.useState(className || ' default') 16 | React.useEffect(() => { 17 | setDefaultClass(className || 'default') 18 | }, [className]) 19 | 20 | return ( 21 |
26 |
27 | 28 | 29 | 30 |
31 | ) 32 | } 33 | 34 | export default SledProgress 35 | -------------------------------------------------------------------------------- /sled/progress/separators.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useStateContext } from '../state' 3 | 4 | interface IProps { 5 | className: string 6 | } 7 | 8 | const SledProgressSeparators: React.FC = ({ className }) => { 9 | const [{ slideSteps }] = useStateContext() 10 | return slideSteps && ( 11 |
12 | {[...Array(slideSteps - 1)].map((_, index) => ( 13 |
21 | ))} 22 |
23 | ) 24 | } 25 | 26 | export default SledProgressSeparators 27 | -------------------------------------------------------------------------------- /sled/progress/track.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react' 2 | import { useSpring, animated } from 'react-spring' 3 | 4 | import { useStateContext } from '../state' 5 | 6 | function getX(slideSteps: number, currentIndex: number, goPrevNext: number = 0): number { 7 | return 100 - ((100 / slideSteps) * (currentIndex + goPrevNext)) 8 | } 9 | 10 | interface IProps { 11 | className: string 12 | } 13 | 14 | const SledProgressTrack: React.FC = ({ className }) => { 15 | const [{ 16 | currentIndex, 17 | prevIndex, 18 | slideSteps, 19 | autoPlayInterval, 20 | pause, 21 | pauseOnMouseOver, 22 | config 23 | }] = useStateContext() 24 | 25 | const [props, set] = useSpring(() => ({ 26 | from: { x: 100 } 27 | })) 28 | 29 | useEffect(() => { 30 | if (pauseOnMouseOver) { 31 | set({ 32 | config, 33 | x: getX(slideSteps, currentIndex), 34 | reset: false 35 | }) 36 | } 37 | }, [pauseOnMouseOver]) 38 | 39 | useEffect(() => { 40 | if (!slideSteps) return 41 | const xCalc = getX(slideSteps, currentIndex, !autoPlayInterval && 1) 42 | if (currentIndex === 0) { 43 | set({ 44 | config, 45 | from: { x: 100 }, 46 | x: xCalc, 47 | reset: slideSteps > 2 48 | ? prevIndex !== 1 49 | : true 50 | }) 51 | } else { 52 | set({ 53 | config, 54 | x: xCalc, 55 | reset: false 56 | }) 57 | } 58 | }, [slideSteps, currentIndex, autoPlayInterval]) 59 | 60 | useEffect(() => { 61 | if (!autoPlayInterval) return 62 | set({ 63 | config: autoPlayInterval && !pause 64 | ? { duration: autoPlayInterval } 65 | : config, 66 | from: { x: getX(slideSteps, currentIndex) }, 67 | x: getX(slideSteps, currentIndex, !pause && 1), 68 | reset: true 69 | }) 70 | }, [pause, autoPlayInterval, slideSteps, currentIndex]) 71 | 72 | return ( 73 | `translateX(-${x}%)`) 81 | }} 82 | /> 83 | ) 84 | } 85 | 86 | export default SledProgressTrack 87 | -------------------------------------------------------------------------------- /sled/sled.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import { StateProvider } from './state' 4 | 5 | interface IProps { 6 | children?: React.ReactNode 7 | } 8 | 9 | const Sled: React.FC = (props) => { 10 | const { children } = props 11 | return ( 12 | 13 | {children} 14 | 15 | ) 16 | } 17 | 18 | export default Sled 19 | -------------------------------------------------------------------------------- /sled/springs.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react' 2 | import { useSpring, animated } from 'react-spring' 3 | 4 | import { useStateContext } from './state' 5 | import useDragGesture from './hooks/useDragGesture' 6 | import useFocus from './hooks/useFocus' 7 | import useCursor from './hooks/useCursor' 8 | import useX from './hooks/useX' 9 | import { useSliderStyles, useViewStyles } from './hooks/useContainerStyles' 10 | 11 | interface IProps { 12 | onAnimationStart(): void 13 | onAnimationEnd(): void 14 | onSledEnd(): void 15 | children?: React.ReactNode 16 | } 17 | 18 | const SledSprings: React.FC = ({ 19 | onAnimationStart, 20 | onAnimationEnd, 21 | onSledEnd, 22 | children 23 | }) => { 24 | const [{ 25 | currentIndex, 26 | viewCount, 27 | direction, 28 | dimensions: { width, height }, 29 | config, 30 | }, dispatch] = useStateContext() 31 | 32 | const cursor = useCursor() 33 | const x = useX() 34 | 35 | const [props, set] = useSpring(() => ({ 36 | x: 0, 37 | cursor, 38 | config, 39 | immediate: true 40 | })) 41 | const springRef = React.useRef() 42 | 43 | useFocus(springRef) 44 | 45 | const [dimensionsUpdated, setDimensionsUpdated] = React.useState(false) 46 | 47 | useEffect(() => { 48 | setDimensionsUpdated(true) 49 | }, [width, height]) 50 | 51 | useEffect(() => { 52 | if (!dimensionsUpdated) return 53 | setDimensionsUpdated(false) 54 | set({ 55 | x, 56 | immediate: true, 57 | onStart: null, 58 | onRest: null, 59 | }) 60 | }, [x, dimensionsUpdated]) 61 | 62 | useEffect(() => { 63 | if (dimensionsUpdated) return 64 | set({ 65 | config, 66 | x, 67 | cursor, 68 | immediate: false, 69 | onStart() { 70 | dispatch({ type: 'SET_PAUSE', pause: true }) 71 | if (typeof onAnimationStart === 'function') onAnimationStart() 72 | }, 73 | onRest() { 74 | dispatch({ type: 'SET_PAUSE', pause: false }) 75 | if (typeof onAnimationEnd === 'function') onAnimationEnd() 76 | if (currentIndex === viewCount - 1) { 77 | if (typeof onSledEnd === 'function') onSledEnd() 78 | } 79 | }, 80 | }) 81 | }, [x, currentIndex, dimensionsUpdated, viewCount, cursor]) 82 | 83 | const bind = useDragGesture(set) 84 | 85 | const sliderStyles = useSliderStyles() 86 | const viewStyles = useViewStyles() 87 | 88 | return ( 89 | `translate3d(${x}px,0,0)`) 97 | : props.x.to((x) => `translate3d(0,${x}px,0)`), 98 | cursor: props.cursor, 99 | }} 100 | > 101 | { 102 | React.Children.map(children, child => ( 103 |
107 | {child} 108 |
109 | )) 110 | } 111 |
112 | ) 113 | } 114 | 115 | 116 | export default SledSprings 117 | -------------------------------------------------------------------------------- /sled/state/index.tsx: -------------------------------------------------------------------------------- 1 | // https://medium.com/simply/state-management-with-react-hooks-and-context-api-at-10-lines-of-code-baf6be8302c 2 | 3 | import React, { createContext, useContext, useReducer } from 'react' 4 | import { initialState, IState } from './types-defaults' 5 | import { reducer, ActionType } from './reducer' 6 | 7 | export const StateContext = createContext(initialState) 8 | 9 | interface IProps { 10 | children?: React.ReactNode 11 | } 12 | 13 | const StateProvider: React.FC = ({ children }) => { 14 | const [state, dispatch] = useReducer(reducer, initialState) 15 | return ( 16 | 17 | {children} 18 | 19 | ) 20 | } 21 | 22 | export { StateProvider } 23 | 24 | export const useStateContext = (): [IState, React.Dispatch] => { 25 | const [state, dispatch] = useContext(StateContext) 26 | return [state, dispatch] 27 | } 28 | 29 | export * from './types-defaults' -------------------------------------------------------------------------------- /sled/state/reducer.ts: -------------------------------------------------------------------------------- 1 | import clamp from '../utils/clamp' 2 | import { IState, TDirection, TDimension } from './types-defaults' 3 | import { SpringConfig } from 'react-spring' 4 | 5 | function getNext(currentIndex: number, slideSteps: number, rewind: boolean) { 6 | if (currentIndex === slideSteps - 1 && !rewind) { 7 | return clamp(currentIndex, 0, slideSteps - 1) 8 | } 9 | return (currentIndex + 1) % slideSteps 10 | } 11 | function getPrev(currentIndex: number, slideSteps: number, rewind: boolean) { 12 | if (currentIndex === 0 && !rewind) { 13 | return clamp(currentIndex, 0, slideSteps - 1) 14 | } 15 | return (currentIndex - 1 + slideSteps) % slideSteps 16 | } 17 | 18 | export type ActionType = 19 | | { type: 'NEXT' } 20 | | { type: 'PREV' } 21 | | { type: 'SELECT', index: number } 22 | | { type: 'SET_PAUSE', pause: boolean } 23 | | { type: 'SET_DIRECTION', direction: TDirection } 24 | | { type: 'SET_MOUSEOVER', pauseOnMouseOver: boolean } 25 | | { type: 'SET_VIEWCOUNT', count: number } 26 | | { type: 'SET_FOCUS', focus: boolean } 27 | | { type: 'SET_DIMENSIONS', dimensions: { width: number, height: number } } 28 | | { type: 'SET_DIMENSIONS_DOM', dimensionsDOM: { width: TDimension, height: TDimension } } 29 | | { type: 'SET_PROPORTION', proportion?: string } 30 | | { type: 'SET_DRAGGING', dragging: boolean } 31 | | { type: 'SET_DRAG_DISTANCE', dragDistance: string | number } 32 | | { type: 'SET_AUTOPLAY', autoPlayInterval?: number } 33 | | { type: 'SET_CONFIG', config: SpringConfig } 34 | | { type: 'SET_REWIND', rewind: boolean } 35 | | { type: 'SET_STOPONINTERACTION', stopOnInteraction: boolean } 36 | | { type: 'SET_SHOWELEMENTS', showElements: number } 37 | | { type: 'SET_SLIDEBY', slideBy: number } 38 | | { type: 'SET_SLIDESTEPS', slideSteps: number } 39 | | { type: 'SET_SLIDERSIZE', sliderSize: number } 40 | 41 | 42 | export function reducer(state: IState, action: ActionType): IState { 43 | switch (action.type) { 44 | case 'NEXT': return { 45 | ...state, 46 | currentIndex: getNext(state.currentIndex, state.slideSteps, state.rewind), 47 | prevIndex: state.currentIndex 48 | } 49 | case 'PREV': return { 50 | ...state, 51 | currentIndex: getPrev(state.currentIndex, state.slideSteps, state.rewind), 52 | prevIndex: state.currentIndex 53 | } 54 | case 'SELECT': return { 55 | ...state, 56 | currentIndex: clamp(action.index, 0, state.slideSteps - 1), 57 | prevIndex: state.currentIndex 58 | } 59 | case 'SET_DIRECTION': return { 60 | ...state, 61 | direction: action.direction 62 | } 63 | case 'SET_PAUSE': return { 64 | ...state, 65 | pause: action.pause 66 | } 67 | case 'SET_MOUSEOVER': return { 68 | ...state, 69 | pauseOnMouseOver: action.pauseOnMouseOver 70 | } 71 | case 'SET_VIEWCOUNT': return { 72 | ...state, 73 | viewCount: action.count 74 | } 75 | case 'SET_FOCUS': return { 76 | ...state, 77 | hasFocus: action.focus 78 | } 79 | case 'SET_DIMENSIONS': return { 80 | ...state, 81 | dimensions: action.dimensions 82 | } 83 | case 'SET_DIMENSIONS_DOM': return { 84 | ...state, 85 | dimensionsDOM: action.dimensionsDOM 86 | } 87 | case 'SET_PROPORTION': return { 88 | ...state, 89 | proportion: action.proportion 90 | } 91 | case 'SET_DRAGGING': return { 92 | ...state, 93 | dragging: action.dragging 94 | } 95 | case 'SET_DRAG_DISTANCE': return { 96 | ...state, 97 | dragDistance: action.dragDistance 98 | } 99 | case 'SET_AUTOPLAY': return { 100 | ...state, 101 | autoPlayInterval: action.autoPlayInterval 102 | } 103 | case 'SET_CONFIG': return { 104 | ...state, 105 | config: action.config 106 | } 107 | case 'SET_REWIND': return { 108 | ...state, 109 | rewind: action.rewind 110 | } 111 | case 'SET_STOPONINTERACTION': return { 112 | ...state, 113 | stopOnInteraction: action.stopOnInteraction 114 | } 115 | case 'SET_SLIDEBY': return { 116 | ...state, 117 | slideBy: action.slideBy 118 | } 119 | case 'SET_SLIDESTEPS': return { 120 | ...state, 121 | slideSteps: action.slideSteps 122 | } 123 | case 'SET_SHOWELEMENTS': return { 124 | ...state, 125 | showElements: action.showElements 126 | } 127 | case 'SET_SLIDERSIZE': return { 128 | ...state, 129 | sliderSize: action.sliderSize 130 | } 131 | default: return state 132 | } 133 | } 134 | 135 | -------------------------------------------------------------------------------- /sled/state/types-defaults.ts: -------------------------------------------------------------------------------- 1 | import { SpringConfig } from 'react-spring' 2 | import CSS from 'csstype' 3 | 4 | export type TRef = React.MutableRefObject 5 | 6 | export type TSelect = number | 'next' | 'prev' 7 | export type TDirection = 'horizontal' | 'vertical' | null 8 | export type TProportion = string | null 9 | export type TDimension = string | number | null 10 | 11 | export interface IInternalState { 12 | currentIndex: number 13 | prevIndex?: number 14 | viewCount: number 15 | slideSteps: number 16 | sliderSize: number 17 | hasFocus: boolean 18 | dimensions: { 19 | width: number 20 | height: number 21 | } 22 | dimensionsDOM: { 23 | width: TDimension 24 | height: TDimension 25 | } 26 | } 27 | 28 | interface IStateFromProps { 29 | width?: number | string 30 | height?: number | string 31 | direction?: TDirection 32 | select?: TSelect 33 | proportion?: TProportion 34 | showElements?: number, 35 | slideBy?: number, 36 | keyboard: boolean 37 | dragging: boolean 38 | dragDistance: number | string 39 | autoPlayInterval?: number 40 | pauseOnMouseOver: boolean 41 | rewind: boolean 42 | config: SpringConfig, 43 | stopOnInteraction: boolean 44 | pause: boolean 45 | onSledEnd?(): void 46 | onAnimationStart?(): void 47 | onAnimationEnd?(): void 48 | } 49 | 50 | export interface IState extends IInternalState, IStateFromProps { } 51 | 52 | export interface IViewsProps extends IStateFromProps { 53 | children?: React.ReactNode 54 | style?: CSS.Properties 55 | } 56 | 57 | const stateFromProps = { 58 | width: null, 59 | proportion: null, 60 | select: null, 61 | keyboard: true, 62 | dragging: true, 63 | dragDistance: '25ow', 64 | autoPlayInterval: null, 65 | config: { mass: 1, tension: 210, friction: 20, clamp: true }, 66 | pauseOnMouseOver: true, 67 | stopOnInteraction: false, 68 | rewind: false, 69 | pause: false, 70 | onSledEnd: null, 71 | onAnimationStart: null, 72 | onAnimationEnd: null 73 | } 74 | 75 | export const ViewsProps: IViewsProps = { 76 | ...stateFromProps 77 | } 78 | 79 | export const initialState: IState = { 80 | currentIndex: 0, 81 | prevIndex: undefined, 82 | pause: false, 83 | viewCount: 0, 84 | hasFocus: false, 85 | direction: 'horizontal', 86 | showElements: 1, 87 | slideBy: 1, 88 | slideSteps: 1, 89 | sliderSize: 0, 90 | dimensions: { width: 0, height: 0 }, 91 | dimensionsDOM: { width: 0, height: 0 }, 92 | ...stateFromProps 93 | } 94 | -------------------------------------------------------------------------------- /sled/utils/clamp.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * https://github.com/lodash/lodash/blob/master/clamp.js 3 | * Clamps `number` within the inclusive `lower` and `upper` bounds. 4 | */ 5 | 6 | function clamp(number: number, lower: number, upper: number) { 7 | number = +number 8 | lower = +lower 9 | upper = +upper 10 | lower = lower === lower ? lower : 0 11 | upper = upper === upper ? upper : 0 12 | if (number === number) { 13 | number = number <= upper ? number : upper 14 | number = number >= lower ? number : lower 15 | } 16 | return number 17 | } 18 | 19 | export default clamp 20 | -------------------------------------------------------------------------------- /sled/utils/debounce.ts: -------------------------------------------------------------------------------- 1 | // https://github.com/chodorowicz/ts-debounce/blob/master/src/index.ts 2 | 3 | export type Procedure = (...args: any[]) => void; 4 | 5 | export type Options = { 6 | isImmediate: boolean, 7 | } 8 | 9 | export function debounce( 10 | func: F, 11 | waitMilliseconds = 50, 12 | options: Options = { 13 | isImmediate: false 14 | }, 15 | ): F { 16 | let timeoutId: ReturnType | undefined 17 | 18 | return function (this: any, ...args: any[]) { 19 | const context = this 20 | 21 | const doLater = function () { 22 | timeoutId = undefined 23 | if (!options.isImmediate) { 24 | func.apply(context, args) 25 | } 26 | } 27 | 28 | const shouldCallNow = options.isImmediate && timeoutId === undefined 29 | 30 | if (timeoutId !== undefined) { 31 | clearTimeout(timeoutId) 32 | } 33 | 34 | timeoutId = setTimeout(doLater, waitMilliseconds) 35 | 36 | if (shouldCallNow) { 37 | func.apply(context, args) 38 | } 39 | } as any 40 | } 41 | -------------------------------------------------------------------------------- /sled/views.tsx: -------------------------------------------------------------------------------- 1 | import React, { useRef } from 'react' 2 | 3 | import Springs from './springs' 4 | import { IViewsProps, ViewsProps } from './state/types-defaults' 5 | 6 | import useDirection from './hooks/useDirection' 7 | import useDimensionsDOM from './hooks/useDimensionsDOM' 8 | import useDimensions from './hooks/useDimensions' 9 | import useProportion from './hooks/useProportion' 10 | import useKeyboard from './hooks/useKeyboard' 11 | import useDragging from './hooks/useDragging' 12 | import useMouseOver from './hooks/useMouseOver' 13 | import useSelect from './hooks/useSelect' 14 | import useFocus from './hooks/useFocus' 15 | import useViewCount from './hooks/useViewCount' 16 | import useAutoPlay from './hooks/useAutoPlay' 17 | import useConfig from './hooks/useConfig' 18 | import useRewind from './hooks/useRewind' 19 | import usePause from './hooks/usePause' 20 | import useStopOnInteraction from './hooks/useStopOnInteraction' 21 | import useShowElements from './hooks/useShowElements' 22 | import useSlideBy from './hooks/useSlideBy' 23 | import useSlideSteps from './hooks/useSlideSteps' 24 | import useSliderSize from './hooks/useSliderSize' 25 | import { useStateContext } from './state' 26 | import './index.css' 27 | 28 | const SledViews: React.FC = ({ 29 | children, 30 | style, 31 | width, 32 | height, 33 | proportion, 34 | direction, 35 | select, 36 | slideBy, 37 | showElements, 38 | keyboard, 39 | dragging, 40 | dragDistance, 41 | autoPlayInterval, 42 | config, 43 | pauseOnMouseOver, 44 | stopOnInteraction, 45 | rewind, 46 | pause, 47 | onSledEnd, 48 | onAnimationStart, 49 | onAnimationEnd 50 | }) => { 51 | 52 | const viewsRef = useRef() 53 | const [{ dimensions, dimensionsDOM }] = useStateContext() 54 | 55 | 56 | useProportion(width, height, proportion) 57 | useDimensionsDOM(width, height) 58 | useDimensions(viewsRef) 59 | 60 | useDirection(direction) 61 | 62 | useFocus(viewsRef) 63 | useViewCount(children) 64 | useRewind(rewind) 65 | usePause(pause) 66 | 67 | useShowElements(showElements) 68 | useSlideBy(slideBy) 69 | useSliderSize() 70 | useSlideSteps() 71 | 72 | useStopOnInteraction(stopOnInteraction) 73 | useMouseOver(pauseOnMouseOver, viewsRef) 74 | useSelect(select) 75 | useKeyboard(keyboard) 76 | useDragging(dragging, dragDistance) 77 | useAutoPlay(autoPlayInterval) 78 | 79 | useConfig(config) 80 | 81 | return ( 82 |
91 |
95 | 100 | {children} 101 | 102 |
103 |
104 | ) 105 | } 106 | 107 | SledViews.defaultProps = ViewsProps 108 | 109 | export default SledViews 110 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "strict": false, 12 | "forceConsistentCasingInFileNames": true, 13 | "noEmit": true, 14 | "esModuleInterop": true, 15 | "module": "esnext", 16 | "moduleResolution": "node", 17 | "resolveJsonModule": true, 18 | "isolatedModules": true, 19 | "jsx": "preserve" 20 | }, 21 | "exclude": [ 22 | "node_modules" 23 | ], 24 | "include": [ 25 | "next-env.d.ts", 26 | "**/*.ts", 27 | "**/*.tsx" 28 | ] 29 | } -------------------------------------------------------------------------------- /tsconfig.rollup.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "dist", 4 | "module": "esnext", 5 | "target": "es5", 6 | "lib": [ 7 | "es6", 8 | "dom", 9 | "es2016", 10 | "es2017" 11 | ], 12 | "sourceMap": true, 13 | "allowJs": false, 14 | "jsx": "react", 15 | "declaration": true, 16 | "moduleResolution": "node", 17 | "allowSyntheticDefaultImports": true, 18 | "esModuleInterop": true 19 | }, 20 | "include": [ 21 | "sled/**/*" 22 | ], 23 | "exclude": [ 24 | "node_modules", 25 | "dist" 26 | ] 27 | } --------------------------------------------------------------------------------