├── .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 | 
2 |
3 | **react-sled** is a carousel made with **react-spring**.
4 |
5 | [](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 |
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 |

63 |
64 | )
65 | }
66 |
67 | export default SledImage
68 |
--------------------------------------------------------------------------------
/components/github-logo.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | export default () => (
4 |
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 | :
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 |
119 |
120 |
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 |
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 |
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 | }
--------------------------------------------------------------------------------