├── .editorconfig ├── .gitignore ├── .prettierrc ├── README.md ├── package-lock.json ├── package.json ├── rollup.config.js └── src ├── Slider.svelte ├── Thumb.svelte ├── index.js └── slider.js /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | indent_style = space 3 | indent_size = 2 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist/ 4 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "svelteSortOrder": "markup-scripts-styles" 3 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # svelte-slider 2 | 3 | Simple svelte range slider component. [demo](https://svelte.dev/repl/e93d43c42b434e3aa3e2a5815f805c75?version=3.22.3) 4 | 5 | ## Install 6 | 7 | Install with npm or yarn: 8 | 9 | ```bash 10 | npm i -D @bulatdashiev/svelte-slider 11 | ``` 12 | 13 | Then import `Slider` component to your Svelte app. 14 | 15 | ```js 16 | import Slider from '@bulatdashiev/svelte-slider'; 17 | ``` 18 | 19 | ## Usage 20 | 21 | ### Simple usage 22 | ```html 23 | 24 | ``` 25 | 26 | ### Range input 27 | ```html 28 | 29 | ``` 30 | 31 | ### Min, max and step 32 | ```html 33 | 34 | ``` 35 | 36 | You can `bind` to min, max and value, slider will change according to props change 37 | 38 | ### Slots 39 | 40 | Default slot 41 | ```html 42 | 43 | 👏 44 | 45 | ``` 46 | 47 | Left, right slots 48 | ```html 49 | 50 | 👎 51 | 👍 52 | 53 | ``` 54 | 55 | ## Props 56 | 57 | |Name|Type|Default|Description| 58 | |---|---|---|---| 59 | |value|Array [number, number]|`[min, max]`|| 60 | |min|number|`0`|| 61 | |max|number|`100`|| 62 | |step|number|`1`|| 63 | |name|Array [string, string]|empty array|Provide names to inputs if you want use slider in form input| 64 | |range|boolean|`false`|Set to `true` to use range input| 65 | |order|boolean|`false`|Set to `true` if you want value[0] always be greater then value[1]| 66 | 67 | ## Slots 68 | 69 | - `default` - customizes both thumbs if `left` or `right` slots isn't provided 70 | - `left` - provide to customize left thumb 71 | - `right` - provide to customize right thumb 72 | 73 | 74 | ## Events 75 | 76 | - `input` - event fires when the value changes within thumb drag 77 | 78 | ## Style 79 | 80 | ```css 81 | :root { 82 | --track-bg: #ebebeb; 83 | --progress-bg: #8abdff; 84 | --thumb-bg: #5784fd; 85 | } 86 | ``` 87 | 88 | set `--thumb-bg` to `transparent` if you use custom thumb 89 | ```css 90 | :root { 91 | --thumb-bg: transparent; 92 | } 93 | ``` 94 | 95 | ## License 96 | 97 | MIT © BulatDashiev -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "svelte-slider", 3 | "version": "1.0.3", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@rollup/plugin-node-resolve": { 8 | "version": "6.1.0", 9 | "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-6.1.0.tgz", 10 | "integrity": "sha512-Cv7PDIvxdE40SWilY5WgZpqfIUEaDxFxs89zCAHjqyRwlTSuql4M5hjIuc5QYJkOH0/vyiyNXKD72O+LhRipGA==", 11 | "dev": true, 12 | "requires": { 13 | "@rollup/pluginutils": "^3.0.0", 14 | "@types/resolve": "0.0.8", 15 | "builtin-modules": "^3.1.0", 16 | "is-module": "^1.0.0", 17 | "resolve": "^1.11.1" 18 | } 19 | }, 20 | "@rollup/pluginutils": { 21 | "version": "3.0.10", 22 | "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.0.10.tgz", 23 | "integrity": "sha512-d44M7t+PjmMrASHbhgpSbVgtL6EFyX7J4mYxwQ/c5eoaE6N2VgCgEcWVzNnwycIloti+/MpwFr8qfw+nRw00sw==", 24 | "dev": true, 25 | "requires": { 26 | "@types/estree": "0.0.39", 27 | "estree-walker": "^1.0.1", 28 | "picomatch": "^2.2.2" 29 | } 30 | }, 31 | "@types/estree": { 32 | "version": "0.0.39", 33 | "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", 34 | "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", 35 | "dev": true 36 | }, 37 | "@types/node": { 38 | "version": "14.0.5", 39 | "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.5.tgz", 40 | "integrity": "sha512-90hiq6/VqtQgX8Sp0EzeIsv3r+ellbGj4URKj5j30tLlZvRUpnAe9YbYnjl3pJM93GyXU0tghHhvXHq+5rnCKA==", 41 | "dev": true 42 | }, 43 | "@types/resolve": { 44 | "version": "0.0.8", 45 | "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-0.0.8.tgz", 46 | "integrity": "sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ==", 47 | "dev": true, 48 | "requires": { 49 | "@types/node": "*" 50 | } 51 | }, 52 | "acorn": { 53 | "version": "7.2.0", 54 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.2.0.tgz", 55 | "integrity": "sha512-apwXVmYVpQ34m/i71vrApRrRKCWQnZZF1+npOD0WV5xZFfwWOmKGQ2RWlfdy9vWITsenisM8M0Qeq8agcFHNiQ==", 56 | "dev": true 57 | }, 58 | "builtin-modules": { 59 | "version": "3.1.0", 60 | "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.1.0.tgz", 61 | "integrity": "sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw==", 62 | "dev": true 63 | }, 64 | "estree-walker": { 65 | "version": "1.0.1", 66 | "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", 67 | "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", 68 | "dev": true 69 | }, 70 | "is-module": { 71 | "version": "1.0.0", 72 | "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", 73 | "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=", 74 | "dev": true 75 | }, 76 | "path-parse": { 77 | "version": "1.0.7", 78 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", 79 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", 80 | "dev": true 81 | }, 82 | "picomatch": { 83 | "version": "2.2.2", 84 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", 85 | "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", 86 | "dev": true 87 | }, 88 | "require-relative": { 89 | "version": "0.8.7", 90 | "resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz", 91 | "integrity": "sha1-eZlTn8ngR6N5KPoZb44VY9q9Nt4=", 92 | "dev": true 93 | }, 94 | "resolve": { 95 | "version": "1.17.0", 96 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", 97 | "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", 98 | "dev": true, 99 | "requires": { 100 | "path-parse": "^1.0.6" 101 | } 102 | }, 103 | "rollup": { 104 | "version": "1.32.1", 105 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-1.32.1.tgz", 106 | "integrity": "sha512-/2HA0Ec70TvQnXdzynFffkjA6XN+1e2pEv/uKS5Ulca40g2L7KuOE3riasHoNVHOsFD5KKZgDsMk1CP3Tw9s+A==", 107 | "dev": true, 108 | "requires": { 109 | "@types/estree": "*", 110 | "@types/node": "*", 111 | "acorn": "^7.1.0" 112 | } 113 | }, 114 | "rollup-plugin-svelte": { 115 | "version": "5.2.2", 116 | "resolved": "https://registry.npmjs.org/rollup-plugin-svelte/-/rollup-plugin-svelte-5.2.2.tgz", 117 | "integrity": "sha512-I+TJ2T+VLKGbKQcpeMJ4AR2ciROqTZNjxbiMiH4Cn1yByaB9OEuy3CnrgHHuWatQcPuF3yIViyKX7OlETWDKOQ==", 118 | "dev": true, 119 | "requires": { 120 | "require-relative": "^0.8.7", 121 | "rollup-pluginutils": "^2.8.2", 122 | "sourcemap-codec": "^1.4.8" 123 | } 124 | }, 125 | "rollup-pluginutils": { 126 | "version": "2.8.2", 127 | "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz", 128 | "integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==", 129 | "dev": true, 130 | "requires": { 131 | "estree-walker": "^0.6.1" 132 | }, 133 | "dependencies": { 134 | "estree-walker": { 135 | "version": "0.6.1", 136 | "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", 137 | "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==", 138 | "dev": true 139 | } 140 | } 141 | }, 142 | "sourcemap-codec": { 143 | "version": "1.4.8", 144 | "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", 145 | "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", 146 | "dev": true 147 | }, 148 | "svelte": { 149 | "version": "3.22.3", 150 | "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.22.3.tgz", 151 | "integrity": "sha512-DumSy5eWPFPlMUGf3+eHyFSkt5yLqyAmMdCuXOE4qc5GtFyLxwTAGKZmgKmW2jmbpTTeFQ/fSQfDBQbl9Eo7yw==", 152 | "dev": true 153 | } 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bulatdashiev/svelte-slider", 3 | "version": "1.0.3", 4 | "description": "Simple range slider for Svelte 3", 5 | "svelte": "src/index.js", 6 | "module": "dist/index.mjs", 7 | "main": "dist/index.js", 8 | "scripts": { 9 | "build": "rollup -c", 10 | "prepublishOnly": "npm run build" 11 | }, 12 | "devDependencies": { 13 | "@rollup/plugin-node-resolve": "^6.0.0", 14 | "rollup": "^1.20.0", 15 | "rollup-plugin-svelte": "^5.0.0", 16 | "svelte": "^3.0.0" 17 | }, 18 | "keywords": [ 19 | "svelte", 20 | "slider", 21 | "range" 22 | ], 23 | "files": [ 24 | "src", 25 | "dist" 26 | ], 27 | "repository": { 28 | "type": "git", 29 | "url": "git+https://github.com/BulatDashiev/svelte-slider.git" 30 | }, 31 | "bugs": { 32 | "url": "https://github.com/BulatDashiev/svelte-slider/issues" 33 | }, 34 | "homepage": "https://github.com/BulatDashiev/svelte-slider#readme", 35 | "author": "BulatDashiev", 36 | "license": "MIT" 37 | } 38 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import svelte from 'rollup-plugin-svelte'; 2 | import resolve from '@rollup/plugin-node-resolve'; 3 | import pkg from './package.json'; 4 | 5 | const name = pkg.name 6 | .replace(/^(@\S+\/)?(svelte-)?(\S+)/, '$3') 7 | .replace(/^\w/, m => m.toUpperCase()) 8 | .replace(/-\w/g, m => m[1].toUpperCase()); 9 | 10 | export default { 11 | input: 'src/index.js', 12 | output: [ 13 | { file: pkg.module, 'format': 'es' }, 14 | { file: pkg.main, 'format': 'umd', name } 15 | ], 16 | plugins: [ 17 | svelte(), 18 | resolve() 19 | ] 20 | }; 21 | -------------------------------------------------------------------------------- /src/Slider.svelte: -------------------------------------------------------------------------------- 1 | 2 | {#if range} 3 | 4 | {/if} 5 |
6 |
9 | active = v}> 10 | 11 | 12 |
13 | 14 | 15 | 16 | {#if range} 17 | active = v}> 18 | 19 | 20 |
21 | 22 | 23 | 24 | {/if} 25 |
26 | 27 | 78 | 79 | 110 | -------------------------------------------------------------------------------- /src/Thumb.svelte: -------------------------------------------------------------------------------- 1 |
((active = true), dispatch('active', true))} 6 | on:drag={({ detail: v }) => (pos = v)} 7 | on:dragend={() => ((active = false), dispatch('active', false))}> 8 |
9 | 10 |
11 |
12 | 13 | 20 | 21 | 52 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './Slider.svelte'; 2 | -------------------------------------------------------------------------------- /src/slider.js: -------------------------------------------------------------------------------- 1 | export default function handle(node) { 2 | const onDown = getOnDown(node); 3 | 4 | node.addEventListener("touchstart", onDown); 5 | node.addEventListener("mousedown", onDown); 6 | return { 7 | destroy() { 8 | node.removeEventListener("touchstart", onDown); 9 | node.removeEventListener("mousedown", onDown); 10 | } 11 | }; 12 | } 13 | 14 | function getOnDown(node) { 15 | const onMove = getOnMove(node); 16 | 17 | return function (e) { 18 | e.preventDefault(); 19 | node.dispatchEvent(new CustomEvent("dragstart")); 20 | 21 | const moveevent = "touches" in e ? "touchmove" : "mousemove"; 22 | const upevent = "touches" in e ? "touchend" : "mouseup"; 23 | 24 | document.addEventListener(moveevent, onMove); 25 | document.addEventListener(upevent, onUp); 26 | 27 | function onUp(e) { 28 | e.stopPropagation(); 29 | 30 | document.removeEventListener(moveevent, onMove); 31 | document.removeEventListener(upevent, onUp); 32 | 33 | node.dispatchEvent(new CustomEvent("dragend")); 34 | }; 35 | }; 36 | } 37 | 38 | function getOnMove(node) { 39 | const track = node.parentNode; 40 | 41 | return function (e) { 42 | const { left, width } = track.getBoundingClientRect(); 43 | const clickOffset = "touches" in e ? e.touches[0].clientX : e.clientX; 44 | const clickPos = Math.min(Math.max((clickOffset - left) / width, 0), 1) || 0; 45 | node.dispatchEvent(new CustomEvent("drag", { detail: clickPos })); 46 | }; 47 | } --------------------------------------------------------------------------------