├── .eslintignore ├── .eslintrc.cjs ├── .github └── workflows │ ├── check.yml │ ├── lint.yml │ └── static.yml ├── .gitignore ├── .npmrc ├── .prettierignore ├── .prettierrc ├── .vscode └── settings.json ├── LICENSE ├── README.md ├── package-lock.json ├── package.json ├── src ├── app.d.ts ├── app.html ├── lib │ ├── Bullet.svelte │ ├── Fullscreen.svelte │ ├── ImageGallery.svelte │ ├── Item.svelte │ ├── LeftNav.svelte │ ├── PlayPause.svelte │ ├── RightNav.svelte │ ├── SVG.svelte │ ├── Slide.svelte │ ├── SlideWrapper.svelte │ ├── SwipeWrapper.svelte │ ├── Thumbnail.svelte │ ├── ThumbnailSwipeWrapper.svelte │ ├── ThumbnailWrapper.svelte │ ├── app.css │ ├── app.css.map │ ├── app.scss │ ├── index.ts │ ├── styling.ts │ └── types.ts └── routes │ ├── +layout.ts │ ├── +page.css │ └── +page.svelte ├── static ├── 1.jpg ├── 10.jpg ├── 10t.jpg ├── 11.jpg ├── 11t.jpg ├── 12.jpg ├── 12t.jpg ├── 1t.jpg ├── 2.jpg ├── 2t.jpg ├── 3.jpg ├── 3t.jpg ├── 4.jpg ├── 4t.jpg ├── 4v.jpg ├── 5.jpg ├── 5t.jpg ├── 6.jpg ├── 6t.jpg ├── 7.jpg ├── 7t.jpg ├── 8.jpg ├── 8t.jpg ├── 9.jpg ├── 9t.jpg └── favicon.png ├── svelte.config.js ├── tsconfig.json └── vite.config.js /.eslintignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | 10 | # Ignore files for PNPM, NPM and YARN 11 | pnpm-lock.yaml 12 | package-lock.json 13 | yarn.lock 14 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parser: '@typescript-eslint/parser', 4 | extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier'], 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: 2020 14 | }, 15 | env: { 16 | browser: true, 17 | es2017: true, 18 | node: true 19 | }, 20 | rules: { 21 | '@typescript-eslint/ban-types': 'off', 22 | '@typescript-eslint/no-inferrable-types': 'off' 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /.github/workflows/check.yml: -------------------------------------------------------------------------------- 1 | name: Check 2 | 3 | on: 4 | push: 5 | branches: 6 | - '**' 7 | pull_request: 8 | branches: 9 | - '**' 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Use Node.js ${{ matrix.node-version }} 17 | uses: actions/setup-node@v1 18 | with: 19 | node-version: 16.x 20 | - uses: actions/cache@v2 21 | with: 22 | path: ~/.npm 23 | key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} 24 | - run: npm ci 25 | - run: npm run check 26 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: 4 | push: 5 | branches: 6 | - '**' 7 | pull_request: 8 | branches: 9 | - '**' 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Use Node.js ${{ matrix.node-version }} 17 | uses: actions/setup-node@v1 18 | with: 19 | node-version: 16.x 20 | - uses: actions/cache@v2 21 | with: 22 | path: ~/.npm 23 | key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} 24 | - run: npm ci 25 | - run: npm run lint 26 | -------------------------------------------------------------------------------- /.github/workflows/static.yml: -------------------------------------------------------------------------------- 1 | # Simple workflow for deploying static content to GitHub Pages 2 | name: Deploy static content to Pages 3 | 4 | on: 5 | # Runs on pushes targeting the default branch 6 | push: 7 | branches: ['main'] 8 | 9 | # Allows you to run this workflow manually from the Actions tab 10 | workflow_dispatch: 11 | 12 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 13 | permissions: 14 | contents: read 15 | pages: write 16 | id-token: write 17 | 18 | # Allow one concurrent deployment 19 | concurrency: 20 | group: 'pages' 21 | cancel-in-progress: true 22 | 23 | jobs: 24 | # Single deploy job since we're just deploying 25 | deploy: 26 | environment: 27 | name: github-pages 28 | url: ${{ steps.deployment.outputs.page_url }} 29 | runs-on: ubuntu-latest 30 | steps: 31 | - name: Checkout 32 | uses: actions/checkout@v3 33 | - name: Setup Pages 34 | uses: actions/configure-pages@v2 35 | - name: Setup Node.js environment 36 | uses: actions/setup-node@v3.6.0 37 | - name: install dependencies 38 | run: npm install 39 | - name: build site 40 | run: npm run buildDemo 41 | - name: Upload artifact 42 | uses: actions/upload-pages-artifact@v1 43 | with: 44 | # Upload entire repository 45 | path: './build' 46 | - name: Deploy to GitHub Pages 47 | id: deployment 48 | uses: actions/deploy-pages@v1 49 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | vite.config.js.timestamp-* 10 | vite.config.ts.timestamp-* 11 | .idea 12 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | 10 | # Ignore files for PNPM, NPM and YARN 11 | pnpm-lock.yaml 12 | package-lock.json 13 | yarn.lock 14 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": false, 3 | "singleQuote": true, 4 | "trailingComma": "none", 5 | "printWidth": 100, 6 | "plugins": ["prettier-plugin-svelte"], 7 | "pluginSearchDirs": ["."], 8 | "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }] 9 | } 10 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "[svelte]": { 3 | "editor.tabSize": 2 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 react2svelte 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, 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, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # @react2svelte/image-gallery 2 | 3 | [![npm version](https://img.shields.io/npm/v/@react2svelte/image-gallery.svg)](https://www.npmjs.com/package/@react2svelte/image-gallery) 4 | 5 | **This is an image gallery for Svelte based on [react-image-gallery](https://github.com/xiaolin/react-image-gallery) v1.2.11. Many thanks to all the contributors of that package for their hard work!** 6 | 7 | ### Live Demo (try it on mobile for swipe support) 8 | 9 | [react2svelte.github.io/image-gallery/](https://react2svelte.github.io/image-gallery/). 10 | 11 | React image gallery is a React component for building image galleries and carousels 12 | 13 | ## Features 14 | 15 | - [x] Mobile swipe gestures (using [@react2svelte/swipable](https://www.npmjs.com/package/@react2svelte/swipable)) 16 | - [x] Thumbnail navigation 17 | - [x] Fullscreen support 18 | - [x] RTL support 19 | - [x] Responsive design 20 | - [x] Tons of customization options (see props below) 21 | - [x] `play`, `pause`, `slide`, `beforeslide`, `screenchange`, `imageload`, `imageerror`, `click` events 22 | 23 | Not yet ported 24 | 25 | - [ ] Custom rendered slides and controls 26 | - [ ] Some events (see below) 27 | 28 | ## Getting started 29 | 30 | Install the library 31 | 32 | ``` 33 | npm i @react2svelte/image-gallery 34 | ``` 35 | 36 | Use it in your component/page 37 | 38 | ```html 39 | 57 | 58 | 59 | ``` 60 | 61 | ### Demo 62 | 63 | You can also have a look at the [demo page](https://react2svelte.github.io/image-gallery/) of the package, it allows setting library settings interactively. To run it locally: 64 | 65 | ``` 66 | git clone https://github.com/react2svelte/image-gallery.git 67 | cd image-gallery 68 | npm install 69 | npm run dev 70 | ``` 71 | 72 | And open http://localhost:5173/ 73 | 74 | ## Props 75 | 76 | - `items`: (required) Array of objects, see example above, 77 | - Available Properties 78 | - `original` - image src url 79 | - `thumbnail` - image thumbnail src url 80 | - `fullscreen` - image for fullscreen (defaults to original) 81 | - `originalHeight` - image height (html5 attribute) 82 | - `originalWidth` - image width (html5 attribute) 83 | - `loading` - image loading. Either "lazy" or "eager" (html5 attribute) 84 | - `thumbnailHeight` - image height (html5 attribute) 85 | - `thumbnailWidth` - image width (html5 attribute) 86 | - `thumbnailLoading` - image loading. Either "lazy" or "eager" (html5 attribute) 87 | - `originalClass` - custom image class 88 | - `thumbnailClass` - custom thumbnail class 89 | - `renderItem` - Function for custom rendering a specific slide (see renderItem below) 90 | - `renderThumbInner` - Function for custom thumbnail renderer (see renderThumbInner below) 91 | - `originalAlt` - image alt 92 | - `thumbnailAlt` - thumbnail image alt 93 | - `originalTitle` - image title 94 | - `thumbnailTitle` - thumbnail image title 95 | - `thumbnailLabel` - label for thumbnail 96 | - `description` - description for image 97 | - `srcSet` - image srcset (html5 attribute) 98 | - `sizes` - image sizes (html5 attribute) 99 | - `bulletClass` - extra class for the bullet of the item 100 | - `infinite`: Boolean, default `true` 101 | - infinite sliding 102 | - `lazyLoad`: Boolean, default `false` 103 | - `showNav`: Boolean, default `true` 104 | - `showThumbnails`: Boolean, default `true` 105 | - `thumbnailPosition`: String, default `bottom` 106 | - available positions: `top, right, bottom, left` 107 | - `showFullscreenButton`: Boolean, default `true` 108 | - `useBrowserFullscreen`: Boolean, default `true` 109 | - if false, fullscreen will be done via css within the browser 110 | - `useTranslate3D`: Boolean, default `true` 111 | - if false, will use `translate` instead of `translate3d` css property to transition slides 112 | - `showPlayButton`: Boolean, default `true` 113 | - `isRTL`: Boolean, default `false` 114 | - if true, gallery's direction will be from right-to-left (to support right-to-left languages) 115 | - `showBullets`: Boolean, default `false` 116 | - `showIndex`: Boolean, default `false` 117 | - `autoPlay`: Boolean, default `false` 118 | - `disableThumbnailScroll`: Boolean, default `false` 119 | - disables the thumbnail container from adjusting 120 | - `disableKeyDown`: Boolean, default `false` 121 | - disables keydown listener for keyboard shortcuts (left arrow, right arrow, esc key) 122 | - `disableSwipe`: Boolean, default `false` 123 | - `disableThumbnailSwipe`: Boolean, default `false` 124 | - `onErrorImageURL`: String, default `undefined` 125 | - an image src pointing to your default image if an image fails to load 126 | - handles both slide image, and thumbnail image 127 | - `indexSeparator`: String, default `' / '`, ignored if `showIndex` is false 128 | - `slideDuration`: Number, default `450` 129 | - transition duration during image slide in milliseconds 130 | - `swipingTransitionDuration`: Number, default `0` 131 | - transition duration while swiping in milliseconds 132 | - `slideInterval`: Number, default `3000` 133 | - `slideOnThumbnailOver`: Boolean, default `false` 134 | - `flickThreshold`: Number (float), default `0.4` 135 | - Determines the max velocity of a swipe before it's considered a flick (lower = more sensitive) 136 | - `swipeThreshold`: Number, default `30` 137 | - A percentage of how far the offset of the current slide is swiped to trigger a slide event. 138 | e.g. If the current slide is swiped less than 30% to the left or right, it will not trigger a slide event. 139 | - `stopPropagation`: Boolean, default `false` 140 | - Automatically calls stopPropagation on all 'swipe' events. 141 | - `startIndex`: Number, default `0` 142 | - `additionalClass`: String, 143 | - Additional class that will be added to the root node of the component. 144 | - `useWindowKeyDown`: Boolean, default `true` 145 | - If `true`, listens to keydown events on window (window.addEventListener) 146 | - If `false`, listens to keydown events on image gallery element (imageGalleryElement.addEventListener) 147 | 148 | ## Functions 149 | 150 | The following functions can be accessed: 151 | 152 | - `play()`: plays the slides 153 | - `pause()`: pauses the slides 154 | - `fullScreen()`: activates full screen 155 | - `exitFullScreen()`: deactivates full screen 156 | - `slideToIndex(index)`: slides to a specific index 157 | - `getCurrentIndex()`: returns the current index 158 | 159 | ## Events 160 | 161 | Already supported 162 | 163 | - [x] `slide`: Function, `details: { currentIndex: number }` 164 | - [x] `beforeslide`: Function, `details: { nextIndex: number }` 165 | - [x] `screenchange`: Function, `details: { fullscreen: boolean }` 166 | - When fullscreen is toggled a boolean is passed to the callback 167 | - [x] `pause`: Function, `details: { currentIndex: number }` 168 | - [x] `play`: Function, `details: { currentIndex: number }` 169 | - [x] `imageload`: Function, `details: { index: number, event }` 170 | - [x] `imageerror`: Function, `details: { index: number, event }` 171 | - [x] `click`: Function, `details: event` 172 | 173 | Not yet supported 174 | 175 | - [ ] `onThumbnailError`: Function, `callback(event)` 176 | - overrides onErrorImage 177 | - [ ] `onThumbnailClick`: Function, `callback(event, index)` 178 | - [ ] `onTouchMove`: Function, `callback(event) on gallery slide` 179 | - [ ] `onTouchEnd`: Function, `callback(event) on gallery slide` 180 | - [ ] `onTouchStart`: Function, `callback(event) on gallery slide` 181 | - [ ] `onMouseOver`: Function, `callback(event) on gallery slide` 182 | - [ ] `onMouseLeave`: Function, `callback(event) on gallery slide` 183 | 184 | ## Custom Controls 185 | 186 | `react-image-gallery` supports rendering custom controls, this feature has not been ported yet. 187 | 188 | ## License 189 | 190 | MIT 191 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@react2svelte/image-gallery", 3 | "version": "1.0.1", 4 | "license": "MIT", 5 | "description": "Svelte image gallery ported from react-image-gallery", 6 | "homepage": "https://react2svelte.github.io/image-gallery/", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/react2svelte/image-gallery" 10 | }, 11 | "scripts": { 12 | "dev": "vite dev", 13 | "build": "svelte-kit sync && svelte-package", 14 | "buildDemo": "vite build", 15 | "prepublishOnly": "echo 'Did you mean to publish `./package/`, instead of `./`?' && exit 1", 16 | "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --use-new-transformation", 17 | "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", 18 | "lint": "prettier --plugin-search-dir . --check . && eslint .", 19 | "format": "prettier --plugin-search-dir . --write ." 20 | }, 21 | "devDependencies": { 22 | "@sveltejs/adapter-static": "^1.0.1", 23 | "@sveltejs/kit": "^1.0.0", 24 | "@sveltejs/package": "^1.0.0", 25 | "@types/throttle-debounce": "^5.0.0", 26 | "@typescript-eslint/eslint-plugin": "^5.45.0", 27 | "@typescript-eslint/parser": "^5.45.0", 28 | "eslint": "^8.28.0", 29 | "eslint-config-prettier": "^8.5.0", 30 | "eslint-plugin-svelte3": "^4.0.0", 31 | "prettier": "^2.8.0", 32 | "prettier-plugin-svelte": "^2.8.1", 33 | "sass": "^1.57.1", 34 | "svelte": "^3.54.0", 35 | "svelte-check": "^2.9.2", 36 | "tslib": "^2.4.1", 37 | "typescript": "^4.9.3", 38 | "vite": "^4.0.0" 39 | }, 40 | "type": "module", 41 | "dependencies": { 42 | "@react2svelte/swipable": "^0.1.4", 43 | "@react2svelte/swipeable": "^0.1.4", 44 | "clsx": "^1.2.1", 45 | "throttle-debounce": "^5.0.0" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/app.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // See https://kit.svelte.dev/docs/types#app 5 | // for information about these interfaces 6 | // and what to do when importing types 7 | declare namespace App { 8 | // interface Error {} 9 | // interface Locals {} 10 | // interface PageData {} 11 | // interface Platform {} 12 | } 13 | -------------------------------------------------------------------------------- /src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | %sveltekit.head% 8 | 9 | 10 |
%sveltekit.body%
11 | 12 | 13 | -------------------------------------------------------------------------------- /src/lib/Bullet.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 18 | -------------------------------------------------------------------------------- /src/lib/ImageGallery.svelte: -------------------------------------------------------------------------------- 1 | 427 | 428 |
435 |
436 | {#if thumbnailPosition === 'bottom' || thumbnailPosition === 'right'} 437 | slideLeft()} 466 | on:slideright={() => slideRight()} 467 | on:slidejump={(event) => { 468 | slideToIndex(event.detail); 469 | }} 470 | on:playtoggle={togglePlay} 471 | on:fullscreentoggle={toggleFullscreen} 472 | on:lazyload={onLazyLoad} 473 | on:imageload={handleImageLoad} 474 | on:imageerror={handleImageError} 475 | on:click 476 | /> 477 | {/if} 478 | {#if showThumbnails} 479 | { 493 | slideToIndex(event.detail); 494 | }} 495 | on:thumbnailmouseover={slideOnThumbnailOver ? onThumbnailMouseOver : undefined} 496 | on:thumbnailmouseleave={slideOnThumbnailOver ? onThumbnailMouseLeave : undefined} 497 | on:imageerror={handleImageError} 498 | /> 499 | {/if} 500 | {#if thumbnailPosition === 'top' || thumbnailPosition === 'left'} 501 | slideLeft()} 530 | on:slideright={() => slideRight()} 531 | on:slidejump={(event) => { 532 | slideToIndex(event.detail); 533 | }} 534 | on:playtoggle={togglePlay} 535 | on:fullscreentoggle={toggleFullscreen} 536 | on:lazyload={onLazyLoad} 537 | on:imageload={handleImageLoad} 538 | on:imageerror={handleImageError} 539 | on:click 540 | /> 541 | {/if} 542 |
543 |
544 | 545 | 549 | -------------------------------------------------------------------------------- /src/lib/Item.svelte: -------------------------------------------------------------------------------- 1 | 20 | 21 | {originalAlt} dispatch('imageload', event)} 31 | on:error={(event) => dispatch('imageerror', event)} 32 | {loading} 33 | /> 34 | {#if description} 35 | 36 | {description} 37 | 38 | {/if} 39 | -------------------------------------------------------------------------------- /src/lib/LeftNav.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 19 | -------------------------------------------------------------------------------- /src/lib/PlayPause.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 18 | -------------------------------------------------------------------------------- /src/lib/RightNav.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 19 | -------------------------------------------------------------------------------- /src/lib/SVG.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 | 17 | {#if icon === 'left'} 18 | 19 | {:else if icon === 'right'} 20 | 21 | {:else if icon === 'maximize'} 22 | 25 | {:else if icon === 'minimize'} 26 | 29 | {:else if icon === 'play'} 30 | 31 | {:else if icon === 'pause'} 32 | 33 | 34 | {/if} 35 | 36 | -------------------------------------------------------------------------------- /src/lib/Slide.svelte: -------------------------------------------------------------------------------- 1 | 25 | 26 |