master 2da850e964f0 cached
16 files
33.7 KB
10.1k tokens
11 symbols
1 requests
Download .txt
Repository: viktorlarsson/vue-tiny-slider
Branch: master
Commit: 2da850e964f0
Files: 16
Total size: 33.7 KB

Directory structure:
gitextract_1wuhc6zo/

├── .github/
│   └── workflows/
│       └── test.yml
├── .gitignore
├── LICENSE
├── README.md
├── demo/
│   ├── index.html
│   └── main.ts
├── dist/
│   └── index.js
├── package.json
├── src/
│   ├── index.d.ts
│   └── index.js
├── tests/
│   ├── index.test.js
│   ├── ssr.test.js
│   └── types-check.ts
├── tsconfig.json
├── vite.config.js
└── vitest.config.js

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/workflows/test.yml
================================================
name: test

on:
  push:
    branches: [master]
  pull_request:
    branches: [master]

jobs:
  test:
    runs-on: ubuntu-latest
    env:
      # Opt actions/checkout, actions/setup-node, etc. onto Node 24 now.
      # Becomes the default on 2026-06-02; this silences the deprecation
      # warning until then.
      FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
    strategy:
      fail-fast: false
      matrix:
        node-version: [22.x, 24.x]
    steps:
      - uses: actions/checkout@v4

      - name: Use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
          cache: npm

      - run: npm ci

      - run: npm run typecheck

      - run: npm test

      - run: npm run build


================================================
FILE: .gitignore
================================================
node_modules/
npm-debug.log
# Demo debug screenshots — regenerated as needed.
demo/*.png
# Bun build output for the demo (deployed to Vercel).
dist-demo/
# Keep any npm auth tokens out of the tree.
.npmrc

================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2017 Viktor Sarström

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:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

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.


================================================
FILE: README.md
================================================
# tiny-slider 2.0 for Vue

Wrapper for Tiny slider for all purposes by [ganlanyuan](https://github.com/ganlanyuan/tiny-slider) in Vue. [Live demo →](https://vue-tiny-slider-blush.vercel.app/)

[![version](https://img.shields.io/npm/v/vue-tiny-slider.svg)](https://www.npmjs.com/package/vue-tiny-slider) [![downloads](https://img.shields.io/npm/dt/vue-tiny-slider.svg)](https://www.npmjs.com/package/vue-tiny-slider) [![downloads per week](https://img.shields.io/npm/dw/vue-tiny-slider.svg)](https://www.npmjs.com/package/vue-tiny-slider)

## Table of Contents
  * [Compatibility](#compatibility)
  * [Install](#install)
  * [Use](#use)
  * [Styling](#styling)
  * [Options](#options)
  * [Methods](#methods)
      * [How to use the methods](#how-to-use-the-methods)
  * [Nuxt 3 SSR](#nuxt-3-ssr)
  * [Todos](#todo)
  * [Collaborators](#collaborators)
  * [License](#license)
  * [Cheerios <3](#cheerios-3)

## Compatibility

| `vue-tiny-slider` | Vue      |
| ----------------- | -------- |
| `^1.0.0`          | Vue 3.x  |
| `^0.1.x`          | Vue 2.x  |

If you're on Vue 2, pin to `vue-tiny-slider@^0.1`.

## Install

`npm install vue-tiny-slider`

## Use

**Globally (Vue 3):**

````javascript
import { createApp } from 'vue';
import VueTinySlider from 'vue-tiny-slider';
import App from './App.vue';

createApp(App)
  .component('tiny-slider', VueTinySlider)
  .mount('#app');
````

**Or locally inside a single component:**

````javascript
import VueTinySlider from 'vue-tiny-slider';

export default {
  components: { 'tiny-slider': VueTinySlider }
}
````

````html
<tiny-slider :mouse-drag="true" :loop="false" items="2" gutter="20">
  <div>Slider item #1</div>
  <div>Slider item #2</div>
  <div>Slider item #3</div>
  <div>Slider item #4</div>
  <div>Slider item #5</div>
  <div>Slider item #6</div>
</tiny-slider>
````

## Styling

SCSS
````scss
@import 'tiny-slider/src/tiny-slider';
````

CDN
````html
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/tiny-slider/2.9.1/tiny-slider.css">
````

## Options

````
auto-init
items
mode
gutter
edge-padding
fixed-width
slide-by
controls
controls-text
controls-container
nav
nav-container
arrow-keys
speed
autoplay
autoplay-timeout
autoplay-direction
autoplay-text
autoplay-hover-pause
autoplay-button
autoplay-button-output
autoplay-reset-on-visibility
animate-in
animate-out
animate-normal
animate-delay
loop
rewind
auto-height
responsive
lazyload
touch
mouse-drag
nested
freezable
disable
on-init
center
lazy-load-selector
prevent-action-when-running
prevent-scroll-on-touch
nav-options
auto-width
````

For more detailed information about the options, see the [Tiny-slider documentation (Options)](https://github.com/ganlanyuan/tiny-slider#options).

## Methods

````getInfo````
````goTo````
````destroy````

### How to use the methods

To be able to use the methods, you need to use ref on the component. Ref is used to register a reference to an element or a child component.

```
<vue-tiny-slider ref="tinySlider"></vue-tiny-slider>
```

```
import VueTinySlider from 'vue-tiny-slider';

export default {
  ...,
    methods: {
        getInfo: function(event) {
             this.$refs.tinySlider.slider.getInfo();
        }
     }
}
```

For more detailed information about the methods, see the [Tiny-slider documentation (Methods)](https://github.com/ganlanyuan/tiny-slider#methods).

## Nuxt 3 SSR

Tiny-slider touches the DOM, so the component must only render on the client.

1. `npm install vue-tiny-slider`

2. Create `plugins/vue-tiny-slider.client.js` (the `.client` suffix makes it client-only):

```js
import VueTinySlider from 'vue-tiny-slider';

export default defineNuxtPlugin((nuxtApp) => {
  nuxtApp.vueApp.component('VueTinySlider', VueTinySlider);
});
```

3. Wrap the slider in `<ClientOnly>` where you use it:

```html
<ClientOnly>
  <vue-tiny-slider v-bind="tinySliderOptions">
    <div>#1</div>
    <div>#2</div>
    <div>#3</div>
  </vue-tiny-slider>
</ClientOnly>
```


## Todo
* ~~Add demo link from a fiddle or something similar~~
* Better handling of the responsive-settings
* Add Custom Events
* ~~Add Methods~~

## Collaborators
* [Morgan Eklund](https://github.com/rymdkapten)
* [Viktor Sarström](https://github.com/viktorlarsson)

## License

This project is available under the MIT license.

## Cheerios <3

* Fixed broken demo link, @VitorLuizC
* Moved tiny-slider from devDependencies to dependencies, @TitanFighter
* Added nav position to props, @Irsanarisandy
* Got it to work with NuxtJS SSR, @ilbonte


================================================
FILE: demo/index.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8" />
	<meta name="viewport" content="width=device-width, initial-scale=1" />
	<title>vue-tiny-slider — local demo</title>
	<link rel="stylesheet" href="../node_modules/tiny-slider/dist/tiny-slider.css" />
	<style>
		body { margin: 0; background: #fafafa; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; color: #222; }
		.wrapper { max-width: 720px; margin: 2rem auto; padding: 0 1rem; }
		h1 { font-size: 1.4rem; margin-bottom: .25rem; }
		.lede { color: #666; margin-top: 0; }
		.slide {
			height: 200px;
			display: grid;
			place-items: center;
			background: linear-gradient(135deg, #667eea, #764ba2);
			color: white;
			font-size: 2rem;
			border-radius: 8px;
			user-select: none;
		}
		.slide:nth-child(2n) { background: linear-gradient(135deg, #f093fb, #f5576c); }
		.slide:nth-child(3n) { background: linear-gradient(135deg, #4facfe, #00f2fe); }
		/* Breathing room between slider and controls/nav placed below it. */
		.tns-controls, .tns-nav { margin-top: 1rem; }
		.tns-controls button {
			padding: .35rem .8rem;
			margin-right: .25rem;
			border: 1px solid #ccc;
			background: white;
			border-radius: 4px;
			cursor: pointer;
		}
		.tns-controls button[disabled] { opacity: .4; cursor: default; }
		.tns-nav { text-align: center; }
		.tns-nav button {
			width: 10px; height: 10px;
			border-radius: 50%;
			border: none;
			background: #ccc;
			margin: 0 4px;
			padding: 0;
			cursor: pointer;
		}
		.tns-nav button.tns-nav-active { background: #667eea; }
	</style>
</head>
<body>
	<div id="app"></div>
	<script type="module" src="./main.ts"></script>
</body>
</html>


================================================
FILE: demo/main.ts
================================================
import { createApp, h } from 'vue';
// Pull from local source so the demo always exercises the working tree.
import VueTinySlider from '../src/index.js';

createApp({
	components: { VueTinySlider },
	render() {
		return h('div', { class: 'wrapper' }, [
			h('h1', 'vue-tiny-slider — local demo'),
			h('p', { class: 'lede' }, 'Drag, swipe, or click the controls.'),
			h(
				VueTinySlider,
				{
					mouseDrag: true,
					loop: false,
					items: '2',
					gutter: 20,
					controlsPosition: 'bottom',
					navPosition: 'bottom'
				},
				{
					default: () => [
						h('div', { class: 'slide' }, 'Slide 1'),
						h('div', { class: 'slide' }, 'Slide 2'),
						h('div', { class: 'slide' }, 'Slide 3'),
						h('div', { class: 'slide' }, 'Slide 4'),
						h('div', { class: 'slide' }, 'Slide 5'),
						h('div', { class: 'slide' }, 'Slide 6')
					]
				}
			)
		]);
	}
}).mount('#app');


================================================
FILE: dist/index.js
================================================
(function(e,t){typeof exports==`object`&&typeof module<`u`?module.exports=t(require(`vue`)):typeof define==`function`&&define.amd?define([`vue`],t):(e=typeof globalThis<`u`?globalThis:e||self,e[`vue-tiny-slider`]=t(e.Vue))})(this,function(e){var t=typeof Node<`u`?Node:Object,n={eventsList:[`indexChanged`,`transitionStart`,`transitionEnd`,`newBreakpointStart`,`newBreakpointEnd`,`touchStart`,`touchMove`,`touchEnd`,`dragStart`,`dragMove`,`dragEnd`],props:{mode:{type:[String],default:`carousel`},autoInit:{type:[Boolean],default:!0},axis:{type:[String],validator:e=>e===`horizontal`||e===`vertical`},items:{type:[String,Number],default:1},gutter:{type:[String,Number],default:0},edgePadding:{type:[String,Number],default:0},fixedWidth:{type:[String,Boolean,Number],default:!1},viewportMax:{type:[String,Boolean,Number],default:!1},swipeAngle:{type:[Boolean,Number],default:15},slideBy:{type:[String,Number],default:1},controls:{type:[String,Boolean],default:!0},controlsPosition:{type:[String],validator:e=>e===`top`||e===`bottom`,default:`top`},controlsText:{type:[Array],default:()=>[`prev`,`next`]},controlsContainer:{type:[Boolean,t,String],default:!1},prevButton:{type:[t,String,Boolean],default:!1},nextButton:{type:[t,String,Boolean],default:!1},nav:{type:[Boolean],default:!0},navPosition:{type:[String],default:`top`},navContainer:{type:[Boolean,t,String],default:!1},navAsThumbnails:{type:[Boolean],default:!1},arrowKeys:{type:[Boolean],default:!1},speed:{type:[String,Number],default:300},autoplay:{type:[Boolean],default:!1},autoplayTimeout:{type:[Number],default:5e3},autoplayDirection:{type:[String],default:`forward`,validator:e=>e===`forward`||e===`backward`},autoplayText:{type:[Array],default:()=>[`start`,`stop`]},autoplayHoverPause:{type:[Boolean],default:!1},autoplayButton:{type:[Boolean,t,String],default:!1},autoplayButtonOutput:{type:[Boolean],default:!0},autoplayResetOnVisibility:{type:[Boolean],default:!0},animateIn:{type:[String],default:`tns-fadeIn`},animateOut:{type:[String],default:`tns-fadeOut`},animateNormal:{type:[String],default:`tns-normal`},animateDelay:{type:[String,Number,Boolean],default:!1},loop:{type:[Boolean],default:!0},rewind:{type:[Boolean],default:!1},autoHeight:{type:[Boolean],default:!1},responsive:{type:[Boolean,Object],default:!1},lazyload:{type:[Boolean],default:!1},touch:{type:[Boolean],default:!0},mouseDrag:{type:[Boolean],default:!1},nested:{type:[String,Boolean],default:!1,validator:e=>e===`inner`||e===`outer`||e===!1},freezable:{type:[Boolean],default:!0},disable:{type:[Boolean],default:!1},startIndex:{type:[Number],default:0},onInit:{type:[Function,Boolean],default:!1},center:{type:Boolean,default:!1},lazyLoadSelector:{type:String,default:`.tns-lazy-img`},preventActionWhenRunning:{type:Boolean,default:!1},autoWidth:{type:Boolean,default:!1},preventScrollOnTouch:{type:[String,Boolean],default:!1,validator:e=>e===`auto`||e===`force`||e===!1},useLocalStorage:{type:[Boolean],default:!0}},mounted:function(){this.autoInit&&this.init()},beforeUnmount:function(){this.slider&&this.slider.destroy()},methods:{$_vueTinySlider_subscribeTo(e){this.slider.events.on(e,t=>{this.$emit(e,t)})},$_vueTinySlider_subscribeToAll(){this.$options.eventsList.forEach(this.$_vueTinySlider_subscribeTo)},goTo:function(e){this.slider.goTo(e)},rebuild:function(){this.slider=this.slider.rebuild(),this.$emit(`rebuild`)},getInfo:function(){this.$emit(`getInfo`,this.slider.getInfo(),this.slider)},destroy:function(){this.slider.destroy()},init:async function(){var{tns:e}=await import(`tiny-slider/dist/tiny-slider.js`),t={container:this.$el,axis:this.axis,items:parseInt(this.items),mode:this.mode,gutter:this.gutter,edgePadding:this.edgePadding,fixedWidth:this.fixedWidth?parseInt(this.fixedWidth,10):this.fixedWidth,viewportMax:this.viewportMax,slideBy:this.slideBy,controls:this.controls,controlsPosition:this.controlsPosition,controlsText:this.controlsText,controlsContainer:this.controlsContainer,prevButton:this.prevButton,nextButton:this.nextButton,nav:this.nav,navPosition:this.navPosition,navContainer:this.navContainer,navAsThumbnails:this.navAsThumbnails,arrowKeys:this.arrowKeys,speed:this.speed,autoplay:this.autoplay,autoplayTimeout:this.autoplayTimeout,autoplayDirection:this.autoplayDirection,autoplayText:this.autoplayText,autoplayHoverPause:this.autoplayHoverPause,autoplayButton:this.autoplayButton,autoplayButtonOutput:this.autoplayButtonOutput,autoplayResetOnVisibility:this.autoplayResetOnVisibility,animateIn:this.animateIn,animateOut:this.animateOut,animateNormal:this.animateNormal,animateDelay:this.animateDelay,loop:this.loop,rewind:this.rewind,autoHeight:this.autoHeight,responsive:this.responsive,lazyload:this.lazyload,touch:this.touch,mouseDrag:this.mouseDrag,nested:this.nested,freezable:this.freezable,disable:this.disable,onInit:this.onInit,swipeAngle:this.swipeAngle,startIndex:this.startIndex,center:this.center,lazyLoadSelector:this.lazyLoadSelector,preventActionWhenRunning:this.preventActionWhenRunning,preventScrollOnTouch:this.preventScrollOnTouch,autoWidth:this.autoWidth,useLocalStorage:this.useLocalStorage};r(t),this.slider=e(t),this.$emit(`init`),this.$_vueTinySlider_subscribeToAll()}},render:function(){return(0,e.h)(`div`,this.$slots.default?this.$slots.default():[])}};function r(e){for(var t in e)e.hasOwnProperty(t)&&e[t]===void 0&&delete e[t]}return n});
//# sourceMappingURL=index.js.map

================================================
FILE: package.json
================================================
{
  "name": "vue-tiny-slider",
  "version": "1.1.0",
  "description": "Vanilla javascript slider for all purposes created by ganlanyuan in Vue.",
  "main": "./dist/index.js",
  "types": "./src/index.d.ts",
  "files": [
    "dist",
    "src",
    "README.md",
    "LICENSE"
  ],
  "scripts": {
    "watch": "vite build --watch",
    "build": "vite build",
    "test": "vitest run",
    "typecheck": "tsc",
    "demo": "bun --hot demo/index.html",
    "demo:build": "bun build demo/index.html --outdir=dist-demo --minify"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/viktorlarsson/vue-tiny-slider.git"
  },
  "keywords": [
    "javascript",
    "vue",
    "tiny-slider",
    "slider",
    "carousel"
  ],
  "author": "Viktor Sarström & Morgan Eklund",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/viktorlarsson/vue-tiny-slider/issues"
  },
  "homepage": "https://github.com/viktorlarsson/vue-tiny-slider#readme",
  "dependencies": {
    "tiny-slider": "^2.9.4"
  },
  "devDependencies": {
    "@vitejs/plugin-vue": "^6.0.6",
    "@vue/test-utils": "^2.4.6",
    "happy-dom": "^20.8.9",
    "typescript": "^6.0.2",
    "vite": "^8.0.8",
    "vitest": "^4.1.4",
    "vue": "^3.5.32"
  }
}


================================================
FILE: src/index.d.ts
================================================
import type { DefineComponent } from 'vue';
import type {
	TinySliderSettings,
	TinySliderInstance,
	TinySliderInfo,
	SilderEvent
} from 'tiny-slider';

export type {
	TinySliderSettings,
	TinySliderInstance,
	TinySliderInfo,
	SilderEvent
};

/**
 * Props accepted by the `<VueTinySlider>` component.
 *
 * Inherits every tiny-slider option (except `container`, which the wrapper
 * sets to the component's root element) and adds a handful of wrapper-
 * specific props.
 */
export interface VueTinySliderProps
	extends Partial<Omit<TinySliderSettings, 'container'>> {
	/**
	 * Automatically call `init()` in the mounted hook.
	 * Set to `false` to call `init()` manually via `$refs`.
	 * @default true
	 */
	autoInit?: boolean;
	/**
	 * Position of the prev/next controls relative to the slider.
	 * @default "top"
	 */
	controlsPosition?: 'top' | 'bottom';
	/**
	 * Maximum viewport width (in px) considered for responsive breakpoints.
	 * @default false
	 */
	viewportMax?: number | string | boolean;
}

/**
 * The underlying tiny-slider instance and wrapper methods exposed on the
 * component instance (available through `$refs.yourRef`).
 */
export interface VueTinySliderInstance {
	/** The underlying tiny-slider instance once `init()` has resolved. */
	slider: TinySliderInstance | null;
	/** Initialize (or re-initialize) the slider. Returns a Promise because
	 *  tiny-slider is lazy-loaded to keep the module SSR-safe. */
	init(): Promise<void>;
	/** Go to a specific slide by index or keyword. */
	goTo(target: number | 'next' | 'prev' | 'first' | 'last'): void;
	/** Rebuild the slider. Emits `rebuild`. */
	rebuild(): void;
	/** Emits `getInfo` with the current slider state and instance. */
	getInfo(): void;
	/** Destroy the underlying tiny-slider instance. */
	destroy(): void;
}

declare const VueTinySlider: DefineComponent<VueTinySliderProps>;

export default VueTinySlider;


================================================
FILE: src/index.js
================================================
import { h } from 'vue';

// `Node` is a browser DOM global; fall back to Object on the server so that
// defining the component doesn't throw during SSR module evaluation.
var NodeType = typeof Node !== 'undefined' ? Node : Object;

var VueTinySlider = {
	eventsList: [
		'indexChanged',
		'transitionStart',
		'transitionEnd',
		'newBreakpointStart',
		'newBreakpointEnd',
		'touchStart',
		'touchMove',
		'touchEnd',
		'dragStart',
		'dragMove',
		'dragEnd'
	],
	props: {
		mode: {
			type: [String],
			default: 'carousel'
		},
		autoInit: {
			type: [Boolean],
			default: true
		},
		axis: {
			type: [String],
			validator: value => {
				return value === 'horizontal' || value === 'vertical';
			}
		},
		items: {
			type: [String, Number],
			default: 1
		},
		gutter: {
			type: [String, Number],
			default: 0
		},
		edgePadding: {
			type: [String, Number],
			default: 0
		},
		fixedWidth: {
			type: [String, Boolean, Number],
			default: false
		},
		viewportMax: {
			type: [String, Boolean, Number],
			default: false
		},
		swipeAngle: {
			type: [Boolean, Number],
			default: 15
		},
		slideBy: {
			type: [String, Number],
			default: 1
		},
		controls: {
			type: [String, Boolean],
			default: true
		},
		controlsPosition: {
			type: [String],
			validator: value => {
				return value === 'top' || value === 'bottom';
			},
			default: 'top'
		},
		controlsText: {
			type: [Array],
			default: () => ['prev', 'next']
		},
		controlsContainer: {
			type: [Boolean, NodeType, String],
			default: false
		},
		prevButton: {
			type: [NodeType, String, Boolean],
			default: false
		},
		nextButton: {
			type: [NodeType, String, Boolean],
			default: false
		},
		nav: {
			type: [Boolean],
			default: true
		},
		navPosition: {
			type: [String],
			default: "top"
		},
		navContainer: {
			type: [Boolean, NodeType, String],
			default: false
		},
		navAsThumbnails: {
			type: [Boolean],
			default: false
		},
		arrowKeys: {
			type: [Boolean],
			default: false
		},
		speed: {
			type: [String, Number],
			default: 300
		},
		autoplay: {
			type: [Boolean],
			default: false
		},
		autoplayTimeout: {
			type: [Number],
			default: 5000
		},
		autoplayDirection: {
			type: [String],
			default: 'forward',
			validator: value => {
				return value === 'forward' || value === 'backward';
			}
		},
		autoplayText: {
			type: [Array],
			default: () => ['start', 'stop']
		},
		autoplayHoverPause: {
			type: [Boolean],
			default: false
		},
		autoplayButton: {
			type: [Boolean, NodeType, String],
			default: false,
		},
		autoplayButtonOutput: {
			type: [Boolean],
			default: true
		},
		autoplayResetOnVisibility: {
			type: [Boolean],
			default: true,
		},
		animateIn: {
			type: [String],
			default: 'tns-fadeIn'
		},
		animateOut: {
			type: [String],
			default: 'tns-fadeOut'
		},
		animateNormal: {
			type: [String],
			default: 'tns-normal'
		},
		animateDelay: {
			type: [String, Number, Boolean],
			default: false
		},
		loop: {
			type: [Boolean],
			default: true
		},
		rewind: {
			type: [Boolean],
			default: false
		},
		autoHeight: {
			type: [Boolean],
			default: false
		},
		responsive: {
			type: [Boolean, Object],
			default: false
		},
		lazyload: {
			type: [Boolean],
			default: false
		},
		touch: {
			type: [Boolean],
			default: true
		},
		mouseDrag: {
			type: [Boolean],
			default: false
		},
		nested: {
			type: [String, Boolean],
			default: false,
			validator: value => {
				return value === 'inner' || value === 'outer' || value === false;
			}
		},
		freezable: {
			type: [Boolean],
			default: true
		},
		disable: {
			type: [Boolean],
			default: false
		},
		startIndex: {
			type: [Number],
			default: 0
		},
		onInit: {
			type: [Function, Boolean],
			default: false
		},
		center: {
			type: Boolean,
			default: false
		},
		lazyLoadSelector: {
			type: String,
			default: '.tns-lazy-img'
		},
		preventActionWhenRunning: {
			type: Boolean,
			default: false
		},
		autoWidth: {
			type: Boolean,
			default: false
		},
		preventScrollOnTouch: {
			type: [String, Boolean],
			default: false,
			validator: value => {
				return value === 'auto' || value === 'force' || value === false;
			}
		},
		useLocalStorage: {
			type: [Boolean],
			default: true
		}
	},
	mounted: function () {
		if(this.autoInit) {
			// init() is async (lazy-loads tiny-slider); swallow the returned
			// promise — consumers who need ready-state should use @init event.
			this.init();
		}
	},
	beforeUnmount: function() {
		if(this.slider) {
			this.slider.destroy();
		}
	},
	methods: {
		$_vueTinySlider_subscribeTo (eventName) {
			this.slider.events.on(eventName, (info) => {
				this.$emit(eventName, info);
			});
		},
		$_vueTinySlider_subscribeToAll () {
			this.$options.eventsList.forEach(this.$_vueTinySlider_subscribeTo)
		},
		goTo: function(value) {
			this.slider.goTo(value);
		},
		rebuild: function() {
			this.slider = this.slider.rebuild();
			this.$emit('rebuild');
		},
		getInfo: function() {
			this.$emit('getInfo', this.slider.getInfo(), this.slider);
		},
		destroy: function() {
			this.slider.destroy();
		},
		init: async function() {
			// Lazy-import tiny-slider so the module's top-level `document` /
			// `window` access never runs during SSR (mounted only fires on the
			// client, so this import only happens client-side).
			//
			// We import the pre-built `dist/tiny-slider.js` (single-file CJS
			// bundle) rather than the raw `src/tiny-slider`. The ESM source
			// exports an unbound `requestAnimationFrame` reference; modern
			// bundlers turn the import into namespace-object access
			// (`mod.raf(...)`) which sets `this = mod` and trips an
			// "Illegal invocation" inside the browser API on every pan/drag.
			// The CJS bundle keeps `raf(...)` as a bare call, so it works.
			// The dist path is explicit because some bundlers (Bun) prefer
			// the ESM `src/` even when `main` points at the dist bundle.
			var { tns } = await import('tiny-slider/dist/tiny-slider.js');

			var settings = {
				container: this.$el,
				axis: this.axis,
				items: parseInt(this.items),
				mode: this.mode,
				gutter: this.gutter,
				edgePadding: this.edgePadding,
				fixedWidth: !this.fixedWidth ? this.fixedWidth : parseInt(this.fixedWidth, 10),
				viewportMax: this.viewportMax,
				slideBy: this.slideBy,
				controls: this.controls,
				controlsPosition: this.controlsPosition,
				controlsText: this.controlsText,
				controlsContainer: this.controlsContainer,
				prevButton: this.prevButton,
				nextButton: this.nextButton,
				nav: this.nav,
				navPosition: this.navPosition,
				navContainer: this.navContainer,
				navAsThumbnails: this.navAsThumbnails,
				arrowKeys: this.arrowKeys,
				speed: this.speed,
				autoplay: this.autoplay,
				autoplayTimeout: this.autoplayTimeout,
				autoplayDirection: this.autoplayDirection,
				autoplayText: this.autoplayText,
				autoplayHoverPause: this.autoplayHoverPause,
				autoplayButton: this.autoplayButton,
				autoplayButtonOutput: this.autoplayButtonOutput,
				autoplayResetOnVisibility: this.autoplayResetOnVisibility,
				animateIn: this.animateIn,
				animateOut: this.animateOut,
				animateNormal: this.animateNormal,
				animateDelay: this.animateDelay,
				loop: this.loop,
				rewind: this.rewind,
				autoHeight: this.autoHeight,
				responsive: this.responsive,
				lazyload: this.lazyload,
				touch: this.touch,
				mouseDrag: this.mouseDrag,
				nested: this.nested,
				freezable: this.freezable,
				disable: this.disable,
				onInit: this.onInit,
				swipeAngle: this.swipeAngle,
				startIndex: this.startIndex,
				center: this.center,
				lazyLoadSelector: this.lazyLoadSelector,
				preventActionWhenRunning: this.preventActionWhenRunning,
				preventScrollOnTouch: this.preventScrollOnTouch,
				autoWidth: this.autoWidth,
				useLocalStorage: this.useLocalStorage
			};
			removeUndefinedProps(settings);

			this.slider = tns(settings);

			// Emit init event
			this.$emit('init');
			// Subscribe to all kind of tiny-slider events
			this.$_vueTinySlider_subscribeToAll();
		},
	},
	render: function(){
		return h('div', this.$slots.default ? this.$slots.default() : []);
	}
};

function removeUndefinedProps(obj) {
	for (var prop in obj) {
		if (obj.hasOwnProperty(prop) && obj[prop] === undefined) {
			delete obj[prop];
		}
	}
}

export default VueTinySlider;


================================================
FILE: tests/index.test.js
================================================
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { mount, flushPromises } from '@vue/test-utils';

// Capture the mock so each test can inspect/drive it.
const tnsMock = vi.fn();

vi.mock('tiny-slider/dist/tiny-slider.js', () => ({
	tns: (...args) => tnsMock(...args)
}));

import VueTinySlider from '../src/index.js';

function makeFakeSlider(overrides = {}) {
	return {
		events: { on: vi.fn() },
		goTo: vi.fn(),
		rebuild: vi.fn(function () { return this; }),
		getInfo: vi.fn(() => ({ index: 0 })),
		destroy: vi.fn(),
		...overrides
	};
}

// init() is async (lazy-imports tiny-slider), so after mount we flush
// microtasks before assertions that depend on the slider being ready.
async function mountSlider(props = {}, slots = undefined) {
	const wrapper = mount(VueTinySlider, {
		props,
		slots: slots ?? {
			default: '<div class="s">A</div><div class="s">B</div>'
		}
	});
	await flushPromises();
	return wrapper;
}

beforeEach(() => {
	tnsMock.mockReset();
	tnsMock.mockImplementation(() => makeFakeSlider());
});

describe('VueTinySlider', () => {
	it('renders default slot children into a <div>', async () => {
		const wrapper = await mountSlider();
		expect(wrapper.element.tagName).toBe('DIV');
		expect(wrapper.findAll('.s')).toHaveLength(2);
	});

	it('calls tns() on mount when autoInit is true (default)', async () => {
		await mountSlider();
		expect(tnsMock).toHaveBeenCalledTimes(1);
	});

	it('does NOT call tns() on mount when autoInit is false', async () => {
		await mountSlider({ autoInit: false });
		expect(tnsMock).not.toHaveBeenCalled();
	});

	it('passes the component root element as container', async () => {
		const wrapper = await mountSlider();
		const settings = tnsMock.mock.calls[0][0];
		expect(settings.container).toBe(wrapper.element);
	});

	it('forwards key props to tns(), coercing items to int', async () => {
		await mountSlider({ items: '3', mode: 'gallery', loop: false, mouseDrag: true });
		const settings = tnsMock.mock.calls[0][0];
		expect(settings.items).toBe(3);
		expect(settings.mode).toBe('gallery');
		expect(settings.loop).toBe(false);
		expect(settings.mouseDrag).toBe(true);
	});

	it('fixedWidth passes through as false when falsy, or parsed int when set', async () => {
		await mountSlider();
		expect(tnsMock.mock.calls[0][0].fixedWidth).toBe(false);

		tnsMock.mockClear();
		await mountSlider({ fixedWidth: '250' });
		expect(tnsMock.mock.calls[0][0].fixedWidth).toBe(250);
	});

	it('strips undefined props before calling tns()', async () => {
		// `axis` has no default, so it will be undefined unless set.
		await mountSlider();
		const settings = tnsMock.mock.calls[0][0];
		expect('axis' in settings).toBe(false);
	});

	it('emits init after tns() is created', async () => {
		const wrapper = await mountSlider();
		expect(wrapper.emitted('init')).toBeTruthy();
		expect(wrapper.emitted('init')).toHaveLength(1);
	});

	it('subscribes to every tiny-slider event in eventsList and re-emits them', async () => {
		const fake = makeFakeSlider();
		tnsMock.mockImplementationOnce(() => fake);
		const wrapper = await mountSlider();

		const expected = VueTinySlider.eventsList;
		expect(fake.events.on).toHaveBeenCalledTimes(expected.length);
		const subscribedNames = fake.events.on.mock.calls.map(c => c[0]);
		expect(subscribedNames).toEqual(expected);

		// Simulate tiny-slider firing indexChanged; component should $emit it.
		const indexChangedHandler = fake.events.on.mock.calls.find(
			c => c[0] === 'indexChanged'
		)[1];
		indexChangedHandler({ index: 4 });
		expect(wrapper.emitted('indexChanged')).toBeTruthy();
		expect(wrapper.emitted('indexChanged')[0]).toEqual([{ index: 4 }]);
	});

	it('goTo(value) delegates to the underlying slider', async () => {
		const fake = makeFakeSlider();
		tnsMock.mockImplementationOnce(() => fake);
		const wrapper = await mountSlider();
		wrapper.vm.goTo(2);
		expect(fake.goTo).toHaveBeenCalledWith(2);
	});

	it('rebuild() replaces slider and emits rebuild', async () => {
		const fake = makeFakeSlider();
		tnsMock.mockImplementationOnce(() => fake);
		const wrapper = await mountSlider();
		wrapper.vm.rebuild();
		expect(fake.rebuild).toHaveBeenCalled();
		expect(wrapper.emitted('rebuild')).toBeTruthy();
	});

	it('getInfo() emits getInfo with payload and slider', async () => {
		const info = { index: 7 };
		const fake = makeFakeSlider({ getInfo: vi.fn(() => info) });
		tnsMock.mockImplementationOnce(() => fake);
		const wrapper = await mountSlider();
		wrapper.vm.getInfo();
		const emitted = wrapper.emitted('getInfo');
		expect(emitted).toBeTruthy();
		expect(emitted[0][0]).toBe(info);
		expect(emitted[0][1]).toBe(fake);
	});

	it('destroy() calls slider.destroy', async () => {
		const fake = makeFakeSlider();
		tnsMock.mockImplementationOnce(() => fake);
		const wrapper = await mountSlider();
		wrapper.vm.destroy();
		expect(fake.destroy).toHaveBeenCalled();
	});

	it('destroys the slider when the component is unmounted', async () => {
		const fake = makeFakeSlider();
		tnsMock.mockImplementationOnce(() => fake);
		const wrapper = await mountSlider();
		wrapper.unmount();
		expect(fake.destroy).toHaveBeenCalled();
	});

	it('controlsText default is [prev, next]', async () => {
		await mountSlider();
		expect(tnsMock.mock.calls[0][0].controlsText).toEqual(['prev', 'next']);
	});
});


================================================
FILE: tests/ssr.test.js
================================================
// @vitest-environment node
// Regression guard: importing the component in a pure Node context
// (no window, no document, no DOM globals) must NOT throw. This catches
// top-level browser references leaking back into the module eval path.
import { describe, it, expect } from 'vitest';

describe('SSR safety', () => {
	it('component module can be imported in Node without DOM globals', async () => {
		const mod = await import('../src/index.js');
		expect(mod.default).toBeTypeOf('object');
		expect(mod.default.props).toBeTypeOf('object');
	});

	it('does not reference window/document at module-eval time', async () => {
		// Assert absence of the browser globals before import — proving
		// the happy-dom env isn't leaking into this test.
		expect(typeof window).toBe('undefined');
		expect(typeof document).toBe('undefined');
		// Re-import is a no-op (cached), but keeps the assertion meaningful.
		await import('../src/index.js');
	});
});


================================================
FILE: tests/types-check.ts
================================================
// Smoke-check the published types from a consumer perspective.
// Run: npx tsc --noEmit tests/types-check.ts (or let CI do it)
import VueTinySlider, {
	type VueTinySliderProps,
	type VueTinySliderInstance,
	type TinySliderInfo,
	type SilderEvent
} from '../src/index.js';

// Default export is a Vue component.
const _component = VueTinySlider;

// Prop interface accepts documented options.
const _props: VueTinySliderProps = {
	items: 3,
	gutter: 20,
	loop: false,
	mouseDrag: true,
	autoInit: false,
	controlsPosition: 'bottom',
	controlsText: ['prev', 'next'],
	mode: 'carousel',
	autoplay: true,
	preventScrollOnTouch: 'auto'
};

// Instance shape available via $refs.
const _fakeRef: VueTinySliderInstance = {
	slider: null,
	init: () => Promise.resolve(),
	goTo: (_t: number | 'next' | 'prev' | 'first' | 'last') => {},
	rebuild: () => {},
	getInfo: () => {},
	destroy: () => {}
};

// Event name type is re-exported from tiny-slider.
const _evt: SilderEvent = 'indexChanged';
const _info: TinySliderInfo = {} as TinySliderInfo;

export { _component, _props, _fakeRef, _evt, _info };


================================================
FILE: tsconfig.json
================================================
{
	"compilerOptions": {
		"target": "esnext",
		"module": "esnext",
		"moduleResolution": "bundler",
		"strict": true,
		"skipLibCheck": true,
		"esModuleInterop": true,
		"noEmit": true
	},
	"include": ["src/**/*.d.ts", "tests/types-check.ts"]
}


================================================
FILE: vite.config.js
================================================
import { defineConfig } from 'vite';
import path from 'path';

export default defineConfig({
	build: {
		lib: {
			entry: path.resolve(__dirname, 'src/index.js'),
			name: 'vue-tiny-slider',
			fileName: () => 'index.js',
			formats: ['umd']
		},
		rollupOptions: {
			// Keep vue and tiny-slider as runtime externals. tiny-slider must
			// not be inlined because it touches `document`/`window` at module
			// eval time — see SSR notes in README.
			external: ['vue', /^tiny-slider(\/|$)/],
			output: {
				globals: {
					vue: 'Vue',
					'tiny-slider': 'tns'
				}
			}
		},
		sourcemap: true
	}
});


================================================
FILE: vitest.config.js
================================================
import { defineConfig } from 'vitest/config';

export default defineConfig({
	test: {
		environment: 'happy-dom',
		globals: false,
		include: ['tests/**/*.test.js']
	}
});
Download .txt
gitextract_1wuhc6zo/

├── .github/
│   └── workflows/
│       └── test.yml
├── .gitignore
├── LICENSE
├── README.md
├── demo/
│   ├── index.html
│   └── main.ts
├── dist/
│   └── index.js
├── package.json
├── src/
│   ├── index.d.ts
│   └── index.js
├── tests/
│   ├── index.test.js
│   ├── ssr.test.js
│   └── types-check.ts
├── tsconfig.json
├── vite.config.js
└── vitest.config.js
Download .txt
SYMBOL INDEX (11 symbols across 5 files)

FILE: demo/main.ts
  method render (line 7) | render() {

FILE: dist/index.js
  method $_vueTinySlider_subscribeTo (line 1) | $_vueTinySlider_subscribeTo(e){this.slider.events.on(e,t=>{this.$emit(e,...
  method $_vueTinySlider_subscribeToAll (line 1) | $_vueTinySlider_subscribeToAll(){this.$options.eventsList.forEach(this.$...
  function r (line 1) | function r(e){for(var t in e)e.hasOwnProperty(t)&&e[t]===void 0&&delete ...

FILE: src/index.d.ts
  type VueTinySliderProps (line 23) | interface VueTinySliderProps
  type VueTinySliderInstance (line 47) | interface VueTinySliderInstance {

FILE: src/index.js
  method $_vueTinySlider_subscribeTo (line 258) | $_vueTinySlider_subscribeTo (eventName) {
  method $_vueTinySlider_subscribeToAll (line 263) | $_vueTinySlider_subscribeToAll () {
  function removeUndefinedProps (line 364) | function removeUndefinedProps(obj) {

FILE: tests/index.test.js
  function makeFakeSlider (line 13) | function makeFakeSlider(overrides = {}) {
  function mountSlider (line 26) | async function mountSlider(props = {}, slots = undefined) {
Condensed preview — 16 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (38K chars).
[
  {
    "path": ".github/workflows/test.yml",
    "chars": 769,
    "preview": "name: test\n\non:\n  push:\n    branches: [master]\n  pull_request:\n    branches: [master]\n\njobs:\n  test:\n    runs-on: ubuntu"
  },
  {
    "path": ".gitignore",
    "chars": 204,
    "preview": "node_modules/\nnpm-debug.log\n# Demo debug screenshots — regenerated as needed.\ndemo/*.png\n# Bun build output for the demo"
  },
  {
    "path": "LICENSE",
    "chars": 1072,
    "preview": "MIT License\n\nCopyright (c) 2017 Viktor Sarström\n\nPermission is hereby granted, free of charge, to any person obtaining a"
  },
  {
    "path": "README.md",
    "chars": 4525,
    "preview": "# tiny-slider 2.0 for Vue\n\nWrapper for Tiny slider for all purposes by [ganlanyuan](https://github.com/ganlanyuan/tiny-s"
  },
  {
    "path": "demo/index.html",
    "chars": 1674,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<meta charset=\"UTF-8\" />\n\t<meta name=\"viewport\" content=\"width=device-width, in"
  },
  {
    "path": "demo/main.ts",
    "chars": 892,
    "preview": "import { createApp, h } from 'vue';\n// Pull from local source so the demo always exercises the working tree.\nimport VueT"
  },
  {
    "path": "dist/index.js",
    "chars": 5395,
    "preview": "(function(e,t){typeof exports==`object`&&typeof module<`u`?module.exports=t(require(`vue`)):typeof define==`function`&&d"
  },
  {
    "path": "package.json",
    "chars": 1235,
    "preview": "{\n  \"name\": \"vue-tiny-slider\",\n  \"version\": \"1.1.0\",\n  \"description\": \"Vanilla javascript slider for all purposes create"
  },
  {
    "path": "src/index.d.ts",
    "chars": 1896,
    "preview": "import type { DefineComponent } from 'vue';\nimport type {\n\tTinySliderSettings,\n\tTinySliderInstance,\n\tTinySliderInfo,\n\tSi"
  },
  {
    "path": "src/index.js",
    "chars": 8377,
    "preview": "import { h } from 'vue';\n\n// `Node` is a browser DOM global; fall back to Object on the server so that\n// defining the c"
  },
  {
    "path": "tests/index.test.js",
    "chars": 5356,
    "preview": "import { describe, it, expect, vi, beforeEach } from 'vitest';\nimport { mount, flushPromises } from '@vue/test-utils';\n\n"
  },
  {
    "path": "tests/ssr.test.js",
    "chars": 949,
    "preview": "// @vitest-environment node\n// Regression guard: importing the component in a pure Node context\n// (no window, no docume"
  },
  {
    "path": "tests/types-check.ts",
    "chars": 1092,
    "preview": "// Smoke-check the published types from a consumer perspective.\n// Run: npx tsc --noEmit tests/types-check.ts (or let CI"
  },
  {
    "path": "tsconfig.json",
    "chars": 247,
    "preview": "{\n\t\"compilerOptions\": {\n\t\t\"target\": \"esnext\",\n\t\t\"module\": \"esnext\",\n\t\t\"moduleResolution\": \"bundler\",\n\t\t\"strict\": true,\n\t"
  },
  {
    "path": "vite.config.js",
    "chars": 605,
    "preview": "import { defineConfig } from 'vite';\nimport path from 'path';\n\nexport default defineConfig({\n\tbuild: {\n\t\tlib: {\n\t\t\tentry"
  },
  {
    "path": "vitest.config.js",
    "chars": 173,
    "preview": "import { defineConfig } from 'vitest/config';\n\nexport default defineConfig({\n\ttest: {\n\t\tenvironment: 'happy-dom',\n\t\tglob"
  }
]

About this extraction

This page contains the full source code of the viktorlarsson/vue-tiny-slider GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 16 files (33.7 KB), approximately 10.1k tokens, and a symbol index with 11 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!