├── .eslintrc.cjs ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── BUG_REPORT.yml │ └── config.yml └── pull_request_template.md ├── .gitignore ├── LICENSE ├── README.md ├── examples ├── .eslintrc.cjs ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── src │ ├── app.html │ ├── global.d.ts │ ├── routes │ │ ├── components │ │ │ ├── AutoplayExample.svelte │ │ │ ├── BasicExample.svelte │ │ │ ├── DynamicSlidesExample.svelte │ │ │ ├── ReactivityExample.svelte │ │ │ ├── ThumbnailsExample.svelte │ │ │ └── VideoExample.svelte │ │ └── index.svelte │ └── utils │ │ ├── generateSlides │ │ └── generateSlides.ts │ │ └── index.ts ├── static │ └── favicon.png ├── svelte.config.js └── tsconfig.json ├── images ├── logo.svg └── svelte-logo.svg ├── package-lock.json ├── package.json ├── scripts └── copy-css.js ├── src ├── app.html ├── global.d.ts ├── lib │ ├── components │ │ ├── Splide │ │ │ ├── Splide.svelte │ │ │ └── bind.ts │ │ ├── SplideSlide │ │ │ └── SplideSlide.svelte │ │ ├── SplideTrack │ │ │ └── SplideTrack.svelte │ │ └── index.ts │ ├── index.ts │ ├── types │ │ ├── events.ts │ │ └── index.ts │ └── utils │ │ ├── classNames │ │ └── classNames.ts │ │ ├── forOwn │ │ ├── forOwn.ts │ │ └── test │ │ │ └── forOwn.test.ts │ │ ├── getSlides │ │ └── getSlides.ts │ │ ├── index.ts │ │ ├── isEqualDeep │ │ ├── isEqualDeep.ts │ │ └── test │ │ │ └── isEqualDeep.test.ts │ │ ├── isEqualShallow │ │ ├── isEqualShallow.ts │ │ └── test │ │ │ └── isEqualShallow.test.ts │ │ ├── isObject │ │ ├── isObject.ts │ │ └── test │ │ │ └── isObject.test.ts │ │ └── merge │ │ ├── merge.test.ts │ │ └── merge.ts └── routes │ ├── components │ └── BasicExample.svelte │ ├── index.svelte │ └── utils │ ├── generateSlides │ └── generateSlides.ts │ └── index.ts ├── static └── favicon.png ├── svelte.config.js └── tsconfig.json /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root : true, 3 | parser : '@typescript-eslint/parser', 4 | extends : [ 'eslint:recommended', 'plugin:@typescript-eslint/recommended' ], 5 | plugins : [ 'svelte3', '@typescript-eslint' ], 6 | ignorePatterns: [ '*.cjs' ], 7 | overrides : [ { files: [ '*.svelte' ], processor: 'svelte3/svelte3' } ], 8 | settings: { 9 | 'svelte3/typescript': () => require( 'typescript' ), 10 | }, 11 | parserOptions: { 12 | sourceType : 'module', 13 | ecmaVersion: 2019, 14 | }, 15 | env: { 16 | browser: true, 17 | es2017 : true, 18 | node : true, 19 | }, 20 | rules: { 21 | '@typescript-eslint/no-explicit-any': 'off', 22 | '@typescript-eslint/ban-types': 'off', 23 | }, 24 | }; 25 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: NaotoshiFujita -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/BUG_REPORT.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Create a bug report. 3 | labels: [ "bug" ] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Thank you for taking your time to post a bug report! 9 | Please fill out the following form. I might need to close the issue without required fields or e.g. clear repro steps. 10 | - type: checkboxes 11 | attributes: 12 | label: Checks 13 | description: Before posting a report, please check following things. 14 | options: 15 | - label: "Not a duplicate." 16 | required: true 17 | - label: "Not a question, feature request, or anything other than a bug report directly related to Svelte Splide. Use Discussions for these topics: https://github.com/Splidejs/splide/discussions" 18 | required: true 19 | - type: input 20 | id: version 21 | attributes: 22 | label: Version 23 | description: The version where the bug happens. 24 | placeholder: x.x.x 25 | validations: 26 | required: true 27 | - type: textarea 28 | attributes: 29 | label: Description 30 | description: Describe the bug. 31 | placeholder: Provide a clear and precise description. Feel free to paste your code, screenshort, etc. 32 | validations: 33 | required: true 34 | - type: input 35 | id: reproduction-link 36 | attributes: 37 | label: Reproduction Link 38 | description: A link to a reproduction (CodeSandbox, etc.). **Do not link to your project**, it has to be a minimal and fresh reproduction. 39 | placeholder: "https://codesandbox.io/" 40 | validations: 41 | required: false 42 | - type: textarea 43 | attributes: 44 | label: Steps to Reproduce 45 | description: Describe steps how to reproduce the bug. 46 | value: | 47 | 1. 48 | 2. 49 | ... 50 | validations: 51 | required: true 52 | - type: textarea 53 | attributes: 54 | label: Expected Behaviour 55 | description: Describe what you expected to happen. 56 | validations: 57 | required: true 58 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | ## Related Issues 6 | 7 | 10 | 11 | ## Description 12 | 13 | 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | /.idea 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2021 Naotoshi Fujita 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |

Svelte Splide

10 | 11 |

12 | Svelte Splide is the Svelte component for the 13 | Splide slider/carousel. 14 |

15 | 16 |

17 | Getting Started 18 |
19 | Demo 20 |
21 | Discussions 22 |

23 |
24 | 25 | ## Quick Start 26 | Get the latest version from NPM: 27 | ``` 28 | $ npm install @splidejs/svelte-splide 29 | ``` 30 | 31 | Import CSS and components: 32 | 33 | ```svelte 34 | 38 | 39 | 40 | 41 | Image 1 42 | 43 | 44 | Image 2 45 | 46 | 47 | ``` 48 | 49 | Visit [this page](https://splidejs.com/integration/svelte-splide) for more details. 50 | 51 | 52 | ## Support Splide 53 | 54 | Please support the project if you like it! 55 | - [GitHub Sponsors](https://github.com/sponsors/NaotoshiFujita) 56 | 57 | 58 | ## License 59 | Svelte Splide and Splide are released under the MIT license. 60 | © 2021 Naotoshi Fujita 61 | -------------------------------------------------------------------------------- /examples/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parser: '@typescript-eslint/parser', 4 | extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'], 5 | plugins: ['svelte3', '@typescript-eslint'], 6 | ignorePatterns: ['*.cjs'], 7 | overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }], 8 | settings: { 9 | 'svelte3/typescript': () => require('typescript') 10 | }, 11 | parserOptions: { 12 | sourceType: 'module', 13 | ecmaVersion: 2019 14 | }, 15 | env: { 16 | browser: true, 17 | es2017: true, 18 | node: true 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /examples/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | Examples for [Svelte Splide](https://github.com/Splidejs/svelte-splide). -------------------------------------------------------------------------------- /examples/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "examples", 3 | "version": "0.0.1", 4 | "scripts": { 5 | "dev": "svelte-kit dev --port 5000", 6 | "build": "svelte-kit build", 7 | "preview": "svelte-kit preview --port 5050", 8 | "check": "svelte-check --tsconfig ./tsconfig.json", 9 | "check:watch": "svelte-check --tsconfig ./tsconfig.json --watch", 10 | "lint": "eslint --ignore-path .gitignore ." 11 | }, 12 | "devDependencies": { 13 | "@sveltejs/adapter-static": "^1.0.0-next.41", 14 | "@sveltejs/kit": "next", 15 | "@typescript-eslint/eslint-plugin": "^5.36.1", 16 | "@typescript-eslint/parser": "^5.36.1", 17 | "eslint": "^8.23.0", 18 | "eslint-plugin-svelte3": "^4.0.0", 19 | "svelte": "^3.49.0", 20 | "svelte-check": "^2.9.0", 21 | "svelte-preprocess": "^4.10.7", 22 | "tslib": "^2.4.0", 23 | "typescript": "^4.8.2" 24 | }, 25 | "type": "module", 26 | "dependencies": { 27 | "@splidejs/splide-extension-video": "^0.7.1", 28 | "@splidejs/svelte-splide": "file:../package" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /examples/src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | %svelte.head% 11 | 12 | 13 |
%svelte.body%
14 | 15 | 16 | -------------------------------------------------------------------------------- /examples/src/global.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /examples/src/routes/components/AutoplayExample.svelte: -------------------------------------------------------------------------------- 1 | 14 | 15 |
16 |

Autoplay

17 | 18 | 19 |
20 | 21 | { #each slides as slide } 22 | 23 | { 24 | 25 | { /each } 26 | 27 |
28 | 29 |
30 |
31 |
32 |
33 | 34 | 38 |
39 |
-------------------------------------------------------------------------------- /examples/src/routes/components/BasicExample.svelte: -------------------------------------------------------------------------------- 1 | 18 | 19 |
20 |

Basic

21 | 22 | console.log( e.detail.splide.length ) } 25 | on:move={ e => console.log( 'move to', e.detail.index ) } 26 | aria-labelledby="basic-example-heading" 27 | class="custom-class" 28 | > 29 | { #each slides as slide } 30 | 31 | { 32 | 33 | { /each } 34 | 35 |
-------------------------------------------------------------------------------- /examples/src/routes/components/DynamicSlidesExample.svelte: -------------------------------------------------------------------------------- 1 | 22 | 23 |
24 |

Dynamic Slides

25 | 26 | 32 |
33 | 34 | { #each slides as slide } 35 | 36 | { 37 | 38 | { /each } 39 | 40 |
41 | 42 |
43 | 44 | 45 |
46 |
47 |
-------------------------------------------------------------------------------- /examples/src/routes/components/ReactivityExample.svelte: -------------------------------------------------------------------------------- 1 | 19 | 20 |
21 |

Reactivity Example

22 | 23 |
24 | 29 | 30 | 34 | 35 | 46 |
47 | 48 | 49 | { #each slides as slide } 50 | 51 | { 52 | 53 | { /each } 54 | 55 |
56 | 57 | -------------------------------------------------------------------------------- /examples/src/routes/components/ThumbnailsExample.svelte: -------------------------------------------------------------------------------- 1 | 39 | 40 |
41 |

Thumbnails

42 | 43 | 44 | { #each slides as slide } 45 | 46 | { 47 | 48 | { /each } 49 | 50 | 51 | 52 | { #each slides as slide } 53 | 54 | { 55 | 56 | { /each } 57 | 58 |
-------------------------------------------------------------------------------- /examples/src/routes/components/VideoExample.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 |
10 |

Video

11 | 12 | 17 | { #each videos as id, index } 18 | 19 | { 23 | 24 | { /each } 25 | 26 |
-------------------------------------------------------------------------------- /examples/src/routes/index.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 |
12 | 13 |
14 | 15 |
16 | 17 |
18 | 19 |
20 | 21 |
22 | 23 |
24 | 25 | -------------------------------------------------------------------------------- /examples/src/utils/generateSlides/generateSlides.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Return an array with objects containing data of sample images. 3 | * 4 | * @param length - Optional. A number of slides. 5 | * @param sig - Optional. The signature for getting a different image. 6 | * 7 | * @return An array with objects for sample images. 8 | */ 9 | export function generateSlides( length = 10, sig = 0 ): Array<{ src: string, alt: string }> { 10 | return Array.from( { length } ).map( ( _, index ) => { 11 | index = sig || index; 12 | 13 | return { 14 | src: `https://source.unsplash.com/random/800x450?sig=${ index }`, 15 | alt: `Image ${ index }`, 16 | }; 17 | } ); 18 | } 19 | -------------------------------------------------------------------------------- /examples/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export { generateSlides } from './generateSlides/generateSlides'; 2 | -------------------------------------------------------------------------------- /examples/static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Splidejs/svelte-splide/72d805bdcb32fc738a225d6373853f9b5c710361/examples/static/favicon.png -------------------------------------------------------------------------------- /examples/svelte.config.js: -------------------------------------------------------------------------------- 1 | import preprocess from 'svelte-preprocess'; 2 | import adapter from '@sveltejs/adapter-static'; 3 | 4 | const dev = process.env.NODE_ENV === 'development'; 5 | 6 | /** @type {import('@sveltejs/kit').Config} */ 7 | const config = { 8 | preprocess: preprocess(), 9 | 10 | kit: { 11 | floc : true, 12 | appDir : 'app', 13 | adapter : adapter( { 14 | fallback: '200.html', // dummy 15 | } ), 16 | paths : { 17 | base: dev ? '' : '/svelte-splide', 18 | }, 19 | prerender: { entries: [] }, 20 | }, 21 | }; 22 | 23 | export default config; 24 | -------------------------------------------------------------------------------- /examples/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.svelte-kit/tsconfig.json", 3 | "compilerOptions": { 4 | "moduleResolution": "node", 5 | "module": "es2020", 6 | "lib": [ 7 | "es2020", 8 | "DOM" 9 | ], 10 | "target": "es2020", 11 | "importsNotUsedAsValues": "error", 12 | "isolatedModules": true, 13 | "resolveJsonModule": true, 14 | "sourceMap": true, 15 | "esModuleInterop": true, 16 | "skipLibCheck": true, 17 | "forceConsistentCasingInFileNames": true, 18 | "baseUrl": ".", 19 | "allowJs": true, 20 | "checkJs": true, 21 | "paths": { 22 | "$lib": [ 23 | "src/lib" 24 | ], 25 | "$lib/*": [ 26 | "src/lib/*" 27 | ] 28 | } 29 | }, 30 | "include": [ 31 | "src/**/*.d.ts", 32 | "src/**/*.js", 33 | "src/**/*.ts", 34 | "src/**/*.svelte" 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /images/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /images/svelte-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@splidejs/svelte-splide", 3 | "version": "0.2.9", 4 | "description": "Svelte component for the Splide slider/carousel.", 5 | "author": "Naotoshi Fujita", 6 | "license": "MIT", 7 | "keywords": [ 8 | "splide", 9 | "slider", 10 | "carousel", 11 | "slideshow", 12 | "lightweight", 13 | "touch", 14 | "responsive", 15 | "svelte" 16 | ], 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/Splidejs/svelte-splide.git" 20 | }, 21 | "bugs": { 22 | "url": "https://github.com/Splidejs/svelte-splide/issues" 23 | }, 24 | "scripts": { 25 | "dev": "svelte-kit dev", 26 | "build": "svelte-kit build", 27 | "package": "node scripts/copy-css.js && svelte-kit package", 28 | "preview": "svelte-kit preview --port 3030", 29 | "check": "svelte-check --tsconfig ./tsconfig.json", 30 | "check:watch": "svelte-check --tsconfig ./tsconfig.json --watch", 31 | "lint": "eslint src", 32 | "deploy": "gh-pages -d examples/build" 33 | }, 34 | "devDependencies": { 35 | "@sveltejs/adapter-static": "^1.0.0-next.42", 36 | "@sveltejs/kit": "next", 37 | "@types/jest": "^29.0.0", 38 | "@typescript-eslint/eslint-plugin": "^5.36.2", 39 | "@typescript-eslint/parser": "^5.36.2", 40 | "eslint": "^8.23.0", 41 | "eslint-plugin-svelte3": "^4.0.0", 42 | "fs-extra": "^10.1.0", 43 | "gh-pages": "^4.0.0", 44 | "jest": "next", 45 | "svelte": "^3.50.0", 46 | "svelte-check": "^2.9.0", 47 | "svelte-preprocess": "^4.10.7", 48 | "svelte2tsx": "^0.5.16", 49 | "tslib": "^2.4.0", 50 | "typescript": "^4.8.2" 51 | }, 52 | "type": "module", 53 | "exports": { 54 | ".": "./index.js", 55 | "./css": "./css/splide.min.css", 56 | "./css/core": "./css/splide-core.min.css", 57 | "./css/*": "./css/themes/splide-*.min.css" 58 | }, 59 | "dependencies": { 60 | "@splidejs/splide": "^4.1.3" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /scripts/copy-css.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs-extra'; 2 | 3 | async function copy() { 4 | await fs.copy( './node_modules/@splidejs/splide/dist/css', './src/lib/css', { overwrite: true } ); 5 | } 6 | 7 | copy().catch( console.error ) -------------------------------------------------------------------------------- /src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | %svelte.head% 8 | 9 | 10 |
%svelte.body%
11 | 12 | 13 | -------------------------------------------------------------------------------- /src/global.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /src/lib/components/Splide/Splide.svelte: -------------------------------------------------------------------------------- 1 | 139 | 140 | 141 | 142 |
147 | { #if hasTrack } 148 | 149 | 150 | 151 | { :else } 152 | 153 | { /if } 154 |
155 | -------------------------------------------------------------------------------- /src/lib/components/Splide/bind.ts: -------------------------------------------------------------------------------- 1 | import type { EventMap, Options, Splide } from '@splidejs/splide'; 2 | import { 3 | EVENT_ACTIVE, 4 | EVENT_ARROWS_MOUNTED, 5 | EVENT_ARROWS_UPDATED, 6 | EVENT_AUTOPLAY_PAUSE, 7 | EVENT_AUTOPLAY_PLAY, 8 | EVENT_AUTOPLAY_PLAYING, 9 | EVENT_CLICK, 10 | EVENT_DESTROY, 11 | EVENT_DRAG, 12 | EVENT_DRAGGED, 13 | EVENT_DRAGGING, 14 | EVENT_HIDDEN, 15 | EVENT_INACTIVE, 16 | EVENT_LAZYLOAD_LOADED, 17 | EVENT_MOUNTED, 18 | EVENT_MOVE, 19 | EVENT_MOVED, 20 | EVENT_NAVIGATION_MOUNTED, 21 | EVENT_PAGINATION_MOUNTED, 22 | EVENT_PAGINATION_UPDATED, 23 | EVENT_REFRESH, 24 | EVENT_RESIZE, 25 | EVENT_RESIZED, 26 | EVENT_SCROLL, 27 | EVENT_SCROLLED, 28 | EVENT_UPDATED, 29 | EVENT_VISIBLE, 30 | } from '@splidejs/splide'; 31 | import type { PaginationData, PaginationItem, SlideComponent } from '@splidejs/splide'; 32 | 33 | 34 | const EVENTS_WITHOUT_ARGS: Array = [ 35 | EVENT_MOUNTED, 36 | EVENT_REFRESH, 37 | EVENT_RESIZE, 38 | EVENT_RESIZED, 39 | EVENT_DRAG, 40 | EVENT_DRAGGING, 41 | EVENT_DRAGGED, 42 | EVENT_SCROLL, 43 | EVENT_SCROLLED, 44 | EVENT_DESTROY, 45 | EVENT_AUTOPLAY_PLAY, 46 | EVENT_AUTOPLAY_PAUSE, 47 | ]; 48 | 49 | /** 50 | * Binds Splide events to the svelte dispatcher. 51 | * 52 | * @since 0.1.0 53 | * 54 | * @param splide - A splide instance. 55 | * @param dispatchFn - A dispatch function created by `createEventDispatcher()`. 56 | */ 57 | export function bind any>( splide: Splide, dispatchFn: T ): void { 58 | const dispatch = ( event: keyof EventMap, detail: Record = {} ) => { 59 | dispatchFn( transform( event ), { splide, ...detail } ); 60 | }; 61 | 62 | splide.on( EVENT_CLICK, ( Slide: SlideComponent, e: MouseEvent ) => { 63 | dispatch( EVENT_CLICK, { Slide, e } ); 64 | } ); 65 | 66 | splide.on( EVENT_MOVE, ( index: number, prev: number, dest: number ) => { 67 | dispatch( EVENT_MOVE, { index, prev, dest } ); 68 | } ); 69 | 70 | splide.on( EVENT_MOVED, ( index: number, prev: number, dest: number ) => { 71 | dispatch( EVENT_MOVED, { index, prev, dest } ); 72 | } ); 73 | 74 | splide.on( EVENT_ACTIVE, ( Slide: SlideComponent ) => { 75 | dispatch( EVENT_ACTIVE, { Slide } ); 76 | } ); 77 | 78 | splide.on( EVENT_INACTIVE, ( Slide: SlideComponent ) => { 79 | dispatch( EVENT_INACTIVE, { Slide } ); 80 | } ); 81 | 82 | splide.on( EVENT_VISIBLE, ( Slide: SlideComponent ) => { 83 | dispatch( EVENT_VISIBLE, { Slide } ); 84 | } ); 85 | 86 | splide.on( EVENT_HIDDEN, ( Slide: SlideComponent ) => { 87 | dispatch( EVENT_HIDDEN, { Slide } ); 88 | } ); 89 | 90 | splide.on( EVENT_UPDATED, ( options: Options ) => { 91 | dispatch( EVENT_UPDATED, options ); 92 | } ); 93 | 94 | splide.on( EVENT_ARROWS_MOUNTED, ( prev: HTMLButtonElement, next: HTMLButtonElement ) => { 95 | dispatch( EVENT_ARROWS_MOUNTED, { prev, next } ); 96 | } ); 97 | 98 | splide.on( EVENT_ARROWS_UPDATED, ( prev: HTMLButtonElement, next: HTMLButtonElement ) => { 99 | dispatch( EVENT_ARROWS_UPDATED, { prev, next } ); 100 | } ); 101 | 102 | splide.on( EVENT_PAGINATION_MOUNTED, ( data: PaginationData, item: PaginationItem ) => { 103 | dispatch( EVENT_PAGINATION_MOUNTED, { data, item } ); 104 | } ); 105 | 106 | splide.on( EVENT_PAGINATION_UPDATED, ( data: PaginationData, prev: PaginationItem, curr: PaginationItem ) => { 107 | dispatch( EVENT_PAGINATION_UPDATED, { data, prev, curr } ); 108 | } ); 109 | 110 | splide.on( EVENT_NAVIGATION_MOUNTED, ( splides: Splide[] ) => { 111 | dispatch( EVENT_NAVIGATION_MOUNTED, { splides } ); 112 | } ); 113 | 114 | splide.on( EVENT_AUTOPLAY_PLAYING, ( rate: number ) => { 115 | dispatch( EVENT_AUTOPLAY_PLAYING, { rate } ); 116 | } ); 117 | 118 | splide.on( EVENT_LAZYLOAD_LOADED, ( img: HTMLImageElement, Slide: SlideComponent ) => { 119 | dispatch( EVENT_LAZYLOAD_LOADED, { img, Slide } ); 120 | } ); 121 | 122 | EVENTS_WITHOUT_ARGS.forEach( event => { 123 | splide.on( event, () => { 124 | dispatch( event ); 125 | } ); 126 | } ); 127 | } 128 | 129 | /** 130 | * Transforms Splide event names to the camel case. 131 | * 132 | * @since 0.1.0 133 | * 134 | * @param event - An event name to transform. 135 | * 136 | * @return A transformed event name. 137 | */ 138 | function transform( event: keyof EventMap ): string { 139 | return event.split( ':' ) 140 | .map( ( fragment, index ) => { 141 | return index > 0 ? fragment.charAt( 0 ).toUpperCase() + fragment.slice( 1 ) : fragment; 142 | } ) 143 | .join( '' ) 144 | .replace( 'Lazyload', 'LazyLoad' ); 145 | } -------------------------------------------------------------------------------- /src/lib/components/SplideSlide/SplideSlide.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 |
  • 10 | 11 |
  • -------------------------------------------------------------------------------- /src/lib/components/SplideTrack/SplideTrack.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 |
    10 |
      11 | 12 |
    13 |
    14 | -------------------------------------------------------------------------------- /src/lib/components/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Splide } from './Splide/Splide.svelte'; 2 | export { default as SplideTrack } from './SplideTrack/SplideTrack.svelte'; 3 | export { default as SplideSlide } from './SplideSlide/SplideSlide.svelte'; 4 | -------------------------------------------------------------------------------- /src/lib/index.ts: -------------------------------------------------------------------------------- 1 | export * from './components'; 2 | export type { Options } from '@splidejs/splide'; -------------------------------------------------------------------------------- /src/lib/types/events.ts: -------------------------------------------------------------------------------- 1 | import type { SlideComponent, Splide } from '@splidejs/splide'; 2 | 3 | 4 | export type EventDetail = Record> = { splide: Splide } & T; 5 | 6 | export type SlideEventDetail = EventDetail<{ Slide: SlideComponent }>; 7 | 8 | export type ArrowsEventDetail = EventDetail<{ prev: HTMLButtonElement, next: HTMLButtonElement }>; 9 | 10 | export type MoveEventDetail = EventDetail<{ index: number, prev: number, dest: number }>; -------------------------------------------------------------------------------- /src/lib/types/index.ts: -------------------------------------------------------------------------------- 1 | export * from './events'; -------------------------------------------------------------------------------- /src/lib/utils/classNames/classNames.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Combines valid class names. 3 | * 4 | * @param classes - An array with classes. 5 | * 6 | * @return A concatenated string with provided class names. 7 | */ 8 | export function classNames( ...classes: Array ): string { 9 | return classes.filter( Boolean ).join( ' ' ); 10 | } 11 | -------------------------------------------------------------------------------- /src/lib/utils/forOwn/forOwn.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Iterates over the provided object by own enumerable keys with calling the iteratee function. 3 | * 4 | * @param object - An object to iterate over. 5 | * @param iteratee - An iteratee function that takes the value and key as arguments. 6 | * 7 | * @return A provided object itself. 8 | */ 9 | export function forOwn( 10 | object: T, 11 | iteratee: ( value: T[ keyof T ], key: keyof T ) => boolean | void 12 | ): T { 13 | if ( object ) { 14 | const keys = Object.keys( object ) as Array; 15 | 16 | for ( let i = 0; i < keys.length; i++ ) { 17 | const key = keys[ i ]; 18 | 19 | if ( key !== '__proto__' ) { 20 | if ( iteratee( object[ key ], key ) === false ) { 21 | break; 22 | } 23 | } 24 | } 25 | } 26 | 27 | return object; 28 | } 29 | -------------------------------------------------------------------------------- /src/lib/utils/forOwn/test/forOwn.test.ts: -------------------------------------------------------------------------------- 1 | import { forOwn } from '../forOwn'; 2 | 3 | 4 | describe( 'forOwn', () => { 5 | test( 'can iterate an object by own enumerable properties.', () => { 6 | const object = { a: 1, b: 2, c: 3 }; 7 | let counter = 0; 8 | 9 | forOwn( object, ( value, key ) => { 10 | counter++; 11 | expect( object[ key ] ).toBe( value ); 12 | } ); 13 | 14 | expect( counter ).toBe( Object.keys( object ).length ); 15 | } ); 16 | 17 | test( 'should not handle inherited properties.', () => { 18 | class Constructor { 19 | a = 1; 20 | b = 2; 21 | } 22 | 23 | ( Constructor as any ).prototype[ 'c' ] = 3; 24 | 25 | const object: any = {}; 26 | 27 | forOwn( new Constructor(), ( value, key ) => { 28 | object[ key ] = value; 29 | } ); 30 | 31 | expect( object ).toStrictEqual( { a: 1, b: 2 } ); 32 | } ); 33 | } ); 34 | -------------------------------------------------------------------------------- /src/lib/utils/getSlides/getSlides.ts: -------------------------------------------------------------------------------- 1 | import type { Splide } from '@splidejs/splide'; 2 | 3 | 4 | export function getSlides( splide: Splide ): HTMLElement[] { 5 | const children = splide.Components.Elements?.list.children; 6 | return children && Array.prototype.slice.call( children ) || []; 7 | } -------------------------------------------------------------------------------- /src/lib/utils/index.ts: -------------------------------------------------------------------------------- 1 | export { classNames } from './classNames/classNames'; 2 | export { forOwn } from './forOwn/forOwn'; 3 | export { getSlides } from './getSlides/getSlides'; 4 | export { isEqualDeep } from './isEqualDeep/isEqualDeep'; 5 | export { isEqualShallow } from './isEqualShallow/isEqualShallow'; 6 | export { isObject } from './isObject/isObject'; 7 | export { merge } from './merge/merge'; 8 | -------------------------------------------------------------------------------- /src/lib/utils/isEqualDeep/isEqualDeep.ts: -------------------------------------------------------------------------------- 1 | import { isObject } from '../isObject/isObject'; 2 | 3 | 4 | /** 5 | * Checks if provided two arrays are shallowly equal or not. 6 | * 7 | * @param subject1 - An array to test. 8 | * @param subject2 - Anther array to test. 9 | * 10 | * @return `true` if they are considered as equal, or otherwise `false`. 11 | */ 12 | export function isEqualDeep( subject1: unknown, subject2: unknown ): boolean { 13 | if ( Array.isArray( subject1 ) && Array.isArray( subject2 ) ) { 14 | return subject1.length === subject2.length 15 | && ! subject1.some( ( elm, index ) => ! isEqualDeep( elm, subject2[ index ] ) ); 16 | } 17 | 18 | if ( isObject( subject1 ) && isObject( subject2 ) ) { 19 | const keys1 = Object.keys( subject1 ) as Array; 20 | const keys2 = Object.keys( subject2 ); 21 | 22 | return keys1.length === keys2.length && ! keys1.some( key => { 23 | return ! Object.prototype.hasOwnProperty.call( subject2, key ) 24 | || ! isEqualDeep( subject1[ key ], subject2[ key ] ); 25 | } ); 26 | } 27 | 28 | return subject1 === subject2; 29 | } 30 | -------------------------------------------------------------------------------- /src/lib/utils/isEqualDeep/test/isEqualDeep.test.ts: -------------------------------------------------------------------------------- 1 | import { isEqualDeep } from '../isEqualDeep'; 2 | 3 | 4 | describe( 'isEqualDeep', () => { 5 | test( 'can check if 2 arrays are deeply equal or not.', () => { 6 | const array1 = [ 1, [ 2, 3 ] ]; 7 | const array2 = [ 1, [ 2, 3 ] ]; 8 | const array3 = [ 1, [ 2, 4 ] ]; 9 | 10 | expect( isEqualDeep( array1, array2 ) ).toBe( true ); 11 | expect( isEqualDeep( array1, array3 ) ).toBe( false ); 12 | } ); 13 | 14 | test( 'can check if 2 objects are deeply equal or not.', () => { 15 | const object1 = { a: 1, b: { c: 2, d: true, e: [ 1, 2 ] } }; 16 | const object2 = { a: 1, b: { c: 2, d: true, e: [ 1, 2 ] } }; 17 | const object3 = { a: 1, b: { c: 2, d: true, e: [ 1, 10 ] } }; 18 | 19 | expect( isEqualDeep( object1, object2 ) ).toBe( true ); 20 | expect( isEqualDeep( object1, object3 ) ).toBe( false ); 21 | } ); 22 | 23 | test( 'can handle complex objects.', () => { 24 | const object1 = { 25 | a: 1, 26 | b: true, 27 | c: 'x', 28 | d: [ true, true ], 29 | e: { e1: 1, e2: [ 1, 2, 3 ] }, 30 | }; 31 | 32 | const object2 = { 33 | a: 1, 34 | b: true, 35 | c: 'x', 36 | d: [ true, true ], 37 | e: { e1: 1, e2: [ 1, 2, 3 ] }, 38 | }; 39 | 40 | const object3 = { 41 | a: 1, 42 | b: true, 43 | c: 'x', 44 | d: [ true, true ], 45 | e: { e1: 1, e2: [ 1, 2, 10 ] }, 46 | }; 47 | 48 | expect( isEqualDeep( object1, object2 ) ).toBe( true ); 49 | expect( isEqualDeep( object1, object3 ) ).toBe( false ); 50 | } ); 51 | } ); 52 | -------------------------------------------------------------------------------- /src/lib/utils/isEqualShallow/isEqualShallow.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Checks if provided two arrays are shallowly equal or not. 3 | * 4 | * @param array1 - An array to test. 5 | * @param array2 - Anther array to test. 6 | * 7 | * @return `true` if they are considered as equal, or otherwise `false`. 8 | */ 9 | export function isEqualShallow( array1: unknown[], array2: unknown[] ): boolean { 10 | return array1.length === array2.length 11 | && ! array1.some( ( elm, index ) => elm !== array2[ index ] ); 12 | } 13 | -------------------------------------------------------------------------------- /src/lib/utils/isEqualShallow/test/isEqualShallow.test.ts: -------------------------------------------------------------------------------- 1 | import { isEqualShallow } from '../isEqualShallow'; 2 | 3 | 4 | describe( 'isEqualShallow', () => { 5 | test( 'can check if 2 arrays with primitives are shallowly equal or not.', () => { 6 | const array1 = [ 1, true, '1' ]; 7 | const array2 = [ 1, true, '1' ]; 8 | const array3 = [ 1, true, '3' ]; 9 | 10 | expect( isEqualShallow( array1, array2 ) ).toBe( true ); 11 | expect( isEqualShallow( array1, array3 ) ).toBe( false ); 12 | } ); 13 | 14 | test( 'can check if 2 arrays with objects are shallowly equal or not.', () => { 15 | const object1 = {}; 16 | const object2 = {}; 17 | 18 | const array1 = [ object1, object2 ]; 19 | const array2 = [ object1, object2 ]; 20 | const array3 = [ object2, object2 ]; 21 | 22 | expect( isEqualShallow( array1, array2 ) ).toBe( true ); 23 | expect( isEqualShallow( array1, array3 ) ).toBe( false ); 24 | } ); 25 | 26 | test( 'should return false if length of testing arrays are different.', () => { 27 | const array1 = [ 1, 1 ]; 28 | const array2 = [ 1, 1, 1 ]; 29 | 30 | expect( isEqualShallow( array1, array2 ) ).toBe( false ); 31 | } ); 32 | } ); 33 | -------------------------------------------------------------------------------- /src/lib/utils/isObject/isObject.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Checks if the given subject is an object or not. 3 | * 4 | * @param subject - A subject to check. 5 | * 6 | * @return `true` if the subject is an object, or otherwise `false`. 7 | */ 8 | export function isObject( subject: unknown ): subject is object { 9 | return subject !== null && typeof subject === 'object'; 10 | } 11 | -------------------------------------------------------------------------------- /src/lib/utils/isObject/test/isObject.test.ts: -------------------------------------------------------------------------------- 1 | import { isObject } from '../isObject'; 2 | 3 | 4 | describe( 'isObject', () => { 5 | test( 'can return `true` if the subject is an object.', () => { 6 | [ {}, { a: 1 }, new Map(), [] ].forEach( subject => { 7 | expect( isObject( subject ) ).toBe( true ); 8 | } ); 9 | } ); 10 | 11 | test( 'can return `false` if the subject is not an object.', () => { 12 | [ 1, true, '1', () => 1, null, undefined, NaN, BigInt( 1 ) ].forEach( subject => { 13 | expect( isObject( subject ) ).toBe( false ); 14 | } ); 15 | } ); 16 | } ); 17 | -------------------------------------------------------------------------------- /src/lib/utils/merge/merge.test.ts: -------------------------------------------------------------------------------- 1 | import { merge } from './merge'; 2 | 3 | 4 | describe( 'merge', () => { 5 | test( 'can merge 2 objects.', () => { 6 | const object = { a: 1, b: '2' }; 7 | const source = { a: 2, c: true }; 8 | 9 | expect( merge( object, source ) ).toStrictEqual( { a: 2, b: '2', c: true } ); 10 | 11 | // Should not change the source 12 | expect( source ).toStrictEqual( { a: 2, c: true } ); 13 | } ); 14 | 15 | test( 'can merge 2 objects recursively.', () => { 16 | const object = { a: 1, b: { c: 2, d: 3 } }; 17 | const source = { b: { d: 4, e: 5 }, f: true }; 18 | 19 | expect( merge( object, source ) ).toStrictEqual( { 20 | a: 1, 21 | b: { c: 2, d: 4, e: 5 }, 22 | f: true, 23 | } ); 24 | } ); 25 | } ); 26 | -------------------------------------------------------------------------------- /src/lib/utils/merge/merge.ts: -------------------------------------------------------------------------------- 1 | import { forOwn } from '../forOwn/forOwn'; 2 | import { isObject } from '../isObject/isObject'; 3 | 4 | 5 | /** 6 | * Merges U to T. 7 | * 8 | * @typeParam T - An object to merge U into. 9 | * @typeParam U - An object to merge properties from. 10 | * 11 | * @return An merged object type. 12 | */ 13 | export type Merge = Omit & { 14 | [ K in ( keyof T & keyof U ) ]: U[ K ] extends object 15 | ? U[ K ] extends any[] 16 | ? U[ K ] 17 | : T[ K ] extends object 18 | ? Merge extends infer A ? Cast : never 19 | : U[ K ] 20 | : U[ K ]; 21 | } & Omit; 22 | 23 | type Cast = T extends U ? T : U; 24 | 25 | /** 26 | * Recursively merges source properties to the object. 27 | * Be aware that this method does not merge arrays. They are just duplicated by `slice()`. 28 | * 29 | * @param object - An object to merge properties to. 30 | * @param source - A source object to merge properties from. 31 | * 32 | * @return A new object with merged properties. 33 | */ 34 | export function merge( object: T, source: U ): Merge { 35 | const merged = object as any; 36 | 37 | forOwn( source, ( value, key ) => { 38 | if ( Array.isArray( value ) ) { 39 | merged[ key ] = value.slice(); 40 | } else if ( isObject( value ) ) { 41 | merged[ key ] = merge( isObject( merged[ key ] ) ? merged[ key ] : {}, value ); 42 | } else { 43 | merged[ key ] = value; 44 | } 45 | } ); 46 | 47 | return merged as Merge; 48 | } 49 | -------------------------------------------------------------------------------- /src/routes/components/BasicExample.svelte: -------------------------------------------------------------------------------- 1 | 20 | 21 |
    22 |

    Basic

    23 | 24 | console.log( e.detail.splide.length ) } 28 | on:move={ e => console.log( 'move to', e.detail.index ) } 29 | > 30 | { #each slides as slide } 31 | 32 | { 33 | 34 | { /each } 35 | 36 |
    -------------------------------------------------------------------------------- /src/routes/index.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 |
    7 | 8 |
    -------------------------------------------------------------------------------- /src/routes/utils/generateSlides/generateSlides.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Return an array with objects containing data of sample images. 3 | * 4 | * @param length - Optional. A number of slides. 5 | * @param sig - Optional. The signature for getting a different image. 6 | * 7 | * @return An array with objects for sample images. 8 | */ 9 | export function generateSlides( length = 10, sig = 0 ): Array<{ src: string, alt: string }> { 10 | return Array.from( { length } ).map( ( _, index ) => { 11 | index = sig || index; 12 | 13 | return { 14 | src: `https://source.unsplash.com/random/800x450?sig=${ index }`, 15 | alt: `Image ${ index }`, 16 | }; 17 | } ); 18 | } 19 | -------------------------------------------------------------------------------- /src/routes/utils/index.ts: -------------------------------------------------------------------------------- 1 | export { generateSlides } from './generateSlides/generateSlides'; 2 | -------------------------------------------------------------------------------- /static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Splidejs/svelte-splide/72d805bdcb32fc738a225d6373853f9b5c710361/static/favicon.png -------------------------------------------------------------------------------- /svelte.config.js: -------------------------------------------------------------------------------- 1 | import preprocess from 'svelte-preprocess'; 2 | import adapter from '@sveltejs/adapter-static'; 3 | 4 | 5 | /** @type {import('@sveltejs/kit').Config} */ 6 | const config = { 7 | preprocess: preprocess(), 8 | 9 | kit: { 10 | adapter: adapter(), 11 | prerender: { 12 | default: true, 13 | }, 14 | }, 15 | }; 16 | 17 | export default config; 18 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.svelte-kit/tsconfig.json", 3 | "compilerOptions": { 4 | "moduleResolution": "node", 5 | "module": "es2020", 6 | "lib": [ 7 | "es2020", 8 | "DOM" 9 | ], 10 | "target": "es2020", 11 | "importsNotUsedAsValues": "error", 12 | "isolatedModules": true, 13 | "resolveJsonModule": true, 14 | "sourceMap": true, 15 | "esModuleInterop": true, 16 | "skipLibCheck": true, 17 | "forceConsistentCasingInFileNames": true, 18 | "baseUrl": ".", 19 | "allowJs": true, 20 | "checkJs": true, 21 | "paths": { 22 | "$lib": [ 23 | "src/lib" 24 | ], 25 | "$lib/*": [ 26 | "src/lib/*" 27 | ] 28 | } 29 | }, 30 | "include": [ 31 | "src/**/*.d.ts", 32 | "src/**/*.js", 33 | "src/**/*.ts", 34 | "src/**/*.svelte" 35 | ] 36 | } 37 | --------------------------------------------------------------------------------