Merge pull request #6 from vadimkorr/feature/#3_Add-dots

Feature/#3 Add dots
This commit is contained in:
Vadim
2021-01-22 16:40:52 +03:00
committed by GitHub
11 changed files with 400 additions and 49 deletions

13
src/Dot/Dot.stories.js Normal file
View File

@@ -0,0 +1,13 @@
import DotView from './DotView.svelte';
export default {
title: 'Dot',
component: DotView
};
const Template = ({ ...args }) => ({
Component: DotView,
props: args
});
export const Primary = Template.bind({});

54
src/Dot/Dot.svelte Normal file
View File

@@ -0,0 +1,54 @@
<script>
import { tweened } from 'svelte/motion';
import { cubicInOut } from 'svelte/easing';
const size = tweened(10, {
duration: 100,
easing: cubicInOut
});
/**
* Indicates if dot is active
*/
export let active = false
$: {
size.set(active ? 13 : 10)
}
</script>
<div class="main-container">
<div
class="dot"
class:current="{active}"
style="
height: {$size}px;
width: {$size}px;
"
on:click
></div>
</div>
<style>
.main-container {
display: flex;
align-items: center;
justify-content: center;
height: 16px;
width: 16px;
}
.dot {
background-color: #5d5d5d;
border-radius: 50%;
display: inline-block;
opacity: 0.5;
cursor: pointer;
transition: opacity 100ms ease;
}
.dot:hover {
opacity: 0.9;
}
.current {
opacity: 0.7;
}
</style>

17
src/Dot/DotView.svelte Normal file
View File

@@ -0,0 +1,17 @@
<script>
import Dot from './Dot.svelte'
/**
* Indicates if dot is active
*/
export let active = false
function handleDotClick() {
active = !active
}
</script>
<Dot
{active}
on:click={handleDotClick}
/>

13
src/Dots/Dots.stories.js Normal file
View File

@@ -0,0 +1,13 @@
import DotsView from './DotsView.svelte';
export default {
title: 'Dots',
component: DotsView
};
const Template = ({ ...args }) => ({
Component: DotsView,
props: args
});
export const Primary = Template.bind({});

48
src/Dots/Dots.svelte Normal file
View File

@@ -0,0 +1,48 @@
<script>
import { createEventDispatcher } from 'svelte'
import Dot from '../Dot/Dot.svelte'
const dispatch = createEventDispatcher()
/**
* Amount of pages (amount of dots)
*/
export let pagesCount = 1
/**
* Index of the current page
*/
export let currentPageIndex = 0
function handleDotClick(pageIndex) {
dispatch('pageChange', pageIndex)
}
</script>
<div class="main-container">
{#each Array(pagesCount) as _, pageIndex (pageIndex)}
<div class="dot-container">
<Dot
active={currentPageIndex === pageIndex}
on:click={() => handleDotClick(pageIndex)}
></Dot>
</div>
{/each}
</div>
<style>
:root {
--dot-size: 10px;
}
.main-container {
display: flex;
align-items: center;
}
.dot-container {
height: calc(var(--dot-size) + 10px);
width: calc(var(--dot-size) + 10x);
display: flex;
align-items: center;
justify-content: center;
}
</style>

24
src/Dots/DotsView.svelte Normal file
View File

@@ -0,0 +1,24 @@
<script>
import Dots from './Dots.svelte'
/**
* Amount of pages (amount of dots)
*/
export let pagesCount = 5
/**
* Index of the current page
*/
export let currentPageIndex = 3
function handlePageChange(event) {
currentPageIndex = event.detail
}
</script>
<Dots
{pagesCount}
{currentPageIndex}
on:pageChange={handlePageChange}
>
</Dots>

View File

@@ -0,0 +1,22 @@
import ImageCarouselView from './ImageCarouselView.svelte';
import ImageCarouselViewCustomDots from './ImageCarouselViewCustomDots.svelte';
export default {
title: 'ImageCarousel',
component: ImageCarouselView
};
const Template = ({ ...args }) => ({
Component: ImageCarouselView,
props: args
});
const TemplateCustomDots = ({ ...args }) => ({
Component: ImageCarouselViewCustomDots,
props: args
});
export const Primary = Template.bind({});
export const WithCustomDots = TemplateCustomDots.bind({});

View File

@@ -1,15 +1,18 @@
<script>
// TODO: rename image carousel to just carousel
// TODO: seems CarouselChild component can be removed
// TODO: subscribe on mount and unsubscribe on destroy to
// $store.currentItemIndex to avoid multiple subscriptions
import { onMount } from 'svelte'
import { store } from './store'
import { store } from '../store'
import {
getPageIndex,
getPagesCount,
getSlidesToShowTail,
getSlideSize,
getIsNotCompletePage
} from './utils/size'
} from '../utils/size'
import Dots from '../Dots/Dots.svelte'
/**
* Enable Next/Prev arrows
@@ -51,6 +54,11 @@
*/
export let autoplayDirection = 'next'
/**
* Current page indicator dots
*/
export let dots = true
let pagesCount = 0
let contentContainerWidth = 0
let offset
@@ -106,6 +114,10 @@
}
})
function handlePageChange(event) {
showPage(event.detail)
}
function applyOffset() {
offset = -$store.currentItemIndex * contentContainerWidth
}
@@ -125,6 +137,7 @@
</script>
<div class="main-container">
<div class="carousel-container">
{#if arrows}
<div class="side-container">
<span
@@ -156,11 +169,32 @@
</div>
{/if}
</div>
{#if dots}
<slot
name="dots"
currentPage={$store.currentItemIndex}
{pagesCount}
{showPage}
>
<Dots
{pagesCount}
currentPageIndex={$store.currentItemIndex}
on:pageChange={handlePageChange}
></Dots>
</slot>
{/if}
</div>
<style>
.main-container {
display: flex;
width: 100%;
flex-direction: column;
align-items: center;
}
.carousel-container {
display: flex;
width: 100%;
}
.content-container {
flex: 1;

View File

@@ -1,5 +1,5 @@
<script>
import ImageCarousel from '../ImageCarousel.svelte'
import ImageCarousel from './ImageCarousel.svelte'
/**
* Enable Next/Previos arrows
@@ -41,6 +41,11 @@
*/
export let autoplayDirection = 'next'
/**
* Current page indicator dots
*/
export let dots = true
const colors = [
'#e5f9f0',
'#ccf3e2',
@@ -65,6 +70,7 @@
{autoplay}
{autoplaySpeed}
{autoplayDirection}
{dots}
>
{#each colors as color (color)}
<div

View File

@@ -0,0 +1,139 @@
<script>
import ImageCarousel from './ImageCarousel.svelte'
/**
* Enable Next/Previos arrows
*/
export let arrows = true;
/**
* Infinite looping
*/
export let infinite = true;
/**
* Number of slides to show at a time
*/
export let slidesToShow = 1;
/**
* Page to start on
*/
export let initialPage = 1
/**
* Transition speed (ms)
*/
export let speed = 500
/**
* Enables auto play of slides
*/
export let autoplay = false
/**
* Auto play change interval
*/
export let autoplaySpeed = 3000
/**
* Auto play change direction ('next', 'prev')
*/
export let autoplayDirection = 'next'
/**
* Current page indicator dots
*/
export let dots = true
function onPageChange(event, showPage) {
showPage(event.target.value)
}
const colors = [
'#e5f9f0',
'#ccf3e2',
'#b2edd3',
'#99e7c5',
'#7fe1b7',
'#66dba8',
'#4cd59a',
'#32cf8b',
'#19c97d',
'#00c36f'
]
</script>
<div class="main-container">
<ImageCarousel
{arrows}
{infinite}
{slidesToShow}
{initialPage}
{speed}
{autoplay}
{autoplaySpeed}
{autoplayDirection}
{dots}
let:currentPage
let:pagesCount
let:showPage
>
{#each colors as color (color)}
<div
class="color-container"
style="background-color: {color};"
>
<p>{color}</p>
</div>
{/each}
<div slot="dots">
<div class="select-container">
<select
value={currentPage}
on:change="{(event) => onPageChange(event, showPage)}"
on:blur="{(event) => onPageChange(event, showPage)}"
>
{#each Array(pagesCount) as _, pageIndex (pageIndex)}
<option value={pageIndex} class:active={currentPage === pageIndex}>
{pageIndex}
</option>
{/each}
</select>
</div>
</div>
</ImageCarousel>
</div>
<style>
.main-container {
display: flex;
width: 100%;
}
.color-container {
height: 100px;
display: flex;
align-items: center;
justify-content: center;
}
.color-container > p {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
font-style: italic;
font-size: 18px;
}
.active {
background-color: grey;
color: white;
}
.select-container {
padding: 5px 0;
}
.select-container > select {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
font-style: italic;
height: 25px;
width: 100px;
border-radius: 5px;
}
</style>

View File

@@ -1,19 +0,0 @@
import ImageCarouselView from './ImageCarouselView.svelte';
export default {
title: 'ImageCarousel',
component: ImageCarouselView,
argTypes: {
arrows: { control: 'boolean' },
},
};
const Template = ({ ...args }) => ({
Component: ImageCarouselView,
props: args,
});
export const Primary = Template.bind({});
Primary.args = {
arrows: true
};