UNPKG

@slidy/element

Version:

Simple, configurable & reusable carousel CustomElement

317 lines (238 loc) 10.4 kB
[![npm version](https://img.shields.io/npm/v/@slidy/element)](https://www.npmjs.com/package/@slidy/element) [![npm bundle size](https://img.shields.io/bundlephobia/minzip/@slidy/element?label=minzip)](https://bundlephobia.com/package/@slidy/element) [![npm downloads](https://img.shields.io/npm/dt/@slidy/element)](https://www.npmjs.com/package/@slidy/element) [![github issues](https://img.shields.io/github/issues/valexr/slidy)](https://github.com/Valexr/slidy/issues) [![npm license](https://img.shields.io/npm/l/@slidy/element)](https://www.npmjs.com/package/@slidy/element) # @slidy/element Simple, configurable & reusable carousel CustomElement based on [@slidy/core](https://github.com/Valexr/slidy/tree/master/packages/core). #### Try the [DEMO] > - [Getting started](#getting-started) > - [Usage](#usage) > - [Options](#options) > - [Events](#events) > - [Methods](#methods) > - [Styling](#styling) ## Getting started The package is available via [NPM]: ```sh npm i -D @slidy/element ``` or from [CDN]: ```html <script src="https://unpkg.com/@slidy/element"></script> ``` Playground is available in svelte [REPL]. ## Usage CustomElement `<slidy-element>` is available via import as `MJS/CJS` module or via global `Window.Slidy` object props as `IIFE`. ### MJS/CJS module import ```html <head> <script type="module"> import 'https://unpkg.com/@slidy/core/dist/index.mjs'; // MJS module // OR import 'https://unpkg.com/@slidy/core/dist/index.cjs'; // CJS module </script> </head> <slidy-element id="slidy" index="5"> <img src="..." width="..." height="..."/> ... </slidy-element> ``` ### IIFE as `Window` Object window.Slidy object contain Slidy Class like `element` props, [@slidy/animation], [@slidy/easing] & [@slidy/media] functions. ```js window.Slidy = { animation, // animation functions easing, // easing functions element, // Slidy Class media // global media store } ``` ```html <head> <script src="https://unpkg.com/@slidy/element/dist/index.js"></script> </head> <slidy-element id="slidy" index="5" snap="center"> <img src="..." width="..." height="..."/> ... </slidy-element> <script> const animation = Slidy.animation.flip const easing = Slidy.easing.cubic const media = Slidy.media const node = document.querySelector('#slidy') node.setAttribute('easing', easing.toString()) // options set after attributes on init <slidy-element> & merged/rewrite node.options = {animation: animation.toString()} </script> ``` ### As third party module in any frameworks ```html <!-- Svelte --> <script> import '@slidy/element'; </script> <slidy-element id="slidy" index="5" snap="center"> <img src="..." width="..." height="..."/> ... </slidy-element> ``` ## Options All `options` are optional & can be passed through element attributes or like options keys. Options set after attributes on init `<slidy-element>` & merged/rewrite. `animation` & `easing` functions also can be passed through attributes with: `key: function.tostring()` transformation. If you need reversed flow use css rules on target node, like: `flex-flow: row-reverse / column-reverse`, `direction: rtl` or html attribute `dir="rtl"`. If you need keyboard navigation just set `tabindex=0` on `<slidy-element>`. ⚠️ Don't positioning childs `absolute`, because @slidy use coordinates from childNodes. For deck flow use `options.snap: 'deck'`. | Key | Default | Type | Description | | :---------- | :---------- | :--------- | :---------- | | `index` | `0` | `number` | Start index | | `clamp` | `0` | `number` | Clamping sliding by index: `clamp - index + clamp` | | `indent` | `1` | `number` | Sliding indent: part of gap padding both start/end edges of slide `gap * indent` | | `sensity` | `5` | `number` | Sliding sensity: how many pixels to drag in the RAF `~16ms` to start move, `0` when sliding | | `gravity` | `1.2` | `number` | Sliding gravity: `0(space) ~ 1(eath) ~ 2(underground)` | | `duration` | `375` | `number` | Sliding duration in ms | | `animation` | `undefuned` | `function` | Animation function: `AnimationFunc = (args: AnimationArgs) => Styles` - predefined in [@slidy/animation]. | | `easing` | `undefined` | `function` | Easing function: `t value from 1 to 0` - predefined in [@slidy/easing]. | | `snap` | `undefined` | `string` | Snapping side: `'start', 'center', 'end', 'deck', undefined`. Default clamp sliding by edges. | | `axis` | `undefined` | `string` | Control coordinate axis: `'x', 'y'`. | | `loop` | `false` | `boolean` | Infinite loop mode | ### Internal calculated - readonly | Key | Type | Description | | :---------- | :------- | :---------- | | `position` | `number` | Current position | | `direction` | `number` | Children move direction | | `vertical` | `number` | Children axis flow: `0` or any `Number` as `true` | | `reverse` | `number` | Children reverse flow: `-1` or `1` | | `scrollable` | `number` | Children full size with gaps > target node size | ### Usage ```html <head> <script src="https://unpkg.com/@slidy/element/dist/index.js"></script> </head> <slidy-element id="slidy" index="5" snap="center"> <img src="..." width="..." height="..."/> ... </slidy-element> <script> let animation = Slidy.animation.flip easing = Slidy.easing.cubic media = Slidy.media node = document.querySelector('#slidy') node.setAttribute('easing', easing.toString()) // options set after attributes on init <slidy-element> & merged/rewrite node.options = {animation: animation.toString()} </script> ``` ## Events Slidy instance reinit on every change childNodes.length in `on:mutate` event. | Name | Detail | Description | | :-------- | :------------------ | :---------- | | `mount` | `{options}` | Fires when `node.children.length` & node.children isConnected | | `resize` | `{ROE,options}` | Fires on resize target node `ROE: ResizeObserverEntry[]`| | `mutate` | `{ML,options}` | Fires on mutation `childNodes` in target node `ML: MutationRecord[]`| | `move` | `{index,position}` | Fires on any sliding | | `index` | `{index}` | Fires on each index change: `index === changed.index` | | `keys` | `{e.key}` | Fires if target node focusing and any key pressed. Predefined keys: `['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown']` is `defaultPrevented` & navigate `to(index + options.clamp)`. Focus not in core & can do programaticaly or with `tabindex` attribute on target node. | | `update` | `{updated.options}` | Fires on each options update | | `destroy` | `{node}` | Fires when async [`destroy()`](#methods) resolved or before target node unmounted from the DOM | ### Usage ```html <head> <script src="https://unpkg.com/@slidy/element/dist/index.js"></script> </head> <slidy-element id="slidy" index="5" snap="center" onmove="onMove"> <img src="..." width="..." height="..."/> ... </slidy-element> <script> const node = document.querySelector('#slidy'); node.addEventListener('mount', (e) => console.log(e)) node.onupdate = (e) => console.log(e.detail) // ??? function onMove(e) { const { index, position } = e.detail console.log(index, position) } </script> ``` ## Methods | Name | Arguments | Description | | :---------- | :----------------- | :---------- | | `to()` | `(index)` | Scroll to `index` | | `init()` | `(node)` | Init slidy() instance | | `update()` | `({option:value})` | Update any property in options | | `destroy()` | `()` | Remove event listners, observers & defaulted props on `slidy()` instance | ### Usage ```html <head> <script src="https://unpkg.com/@slidy/core/dist/index.js"></script> </head> <slidy-element id="slidy" index="5" snap="center" onmove="onMove"> <img src="..." width="..." height="..."/> ... </slidy-element> <nav> <button id="prev">←</button> <button id="next">→</button> </nav> <script>; const slidy = document.querySelector('#slidy'); const prev = document.querySelector('#prev'); const next = document.querySelector('#next'); slidy.update({ snap: 'center' }) prev.onclick = () => slidy.to(index - 1) next.onclick = () => slidy.to(index + 1) </script> ``` ## Styling `<slidy-element>` nested `@slidy/core` styles `outline: 0; overflow: hidden; user-select: none; -webkit-user-select:none;` & have base simple incapsulated styles. Also you can style it & his childs as native HTMLElements or you can pass CSSvariables props via `style` attribute or in css scope. For `<img>` children required pass `width/height` attributes. ### Incapsulated styles ```css :host { display: flex; flex-flow: var(--flow); gap: var(--gap, 1rem); width: 100%; height: var(--height, 20rem); } ::slotted(*) { flex: 0 0 var(--width, auto); width: var(--width, auto); height: 100%; } ``` ### CSS variables | Name | Default | Description | | :--------- | :------ | :----------------------- | | `--flow` | `row` | Flex flow | | `--width` | `auto` | Each children width | | `--height` | `20rem` | `<slidy-element>` height | | `--gap` | `1rem` | Gap between childs | ### Usage ```html <head> <script src="https://unpkg.com/@slidy/core/dist/index.js"></script> <style> #slidy { --flow: row; border: 1px solid red; } </style> </head> <slidy-element id="slidy" index="5" snap="center" style="--width: 100px; --height: 100px; --gap: 1rem;" > <img src="..." width="..." height="..."/> ... </slidy-element> ``` MIT &copy; [Valexr](https://github.com/Valexr) [DEMO]: https://slidy-core.surge.sh [NPM]: https://www.npmjs.com/package/@slidy/element [CDN]: https://unpkg.com/@slidy/element/ [REPL]: https://svelte.dev/repl/4336a0d56f9049c98b85245a8cc33f7f [@slidy/animation]: https://github.com/Valexr/Slidy/tree/master/packages/animation [@slidy/easing]: https://github.com/Valexr/Slidy/tree/master/packages/easing [@slidy/media]: https://github.com/Valexr/Slidy/tree/master/packages/media