Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions assets/scss/_functions.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@
@import "functions/fluid";
@import "functions/list";
@import "functions/decimal";
@import "functions/flex-grid";
15 changes: 15 additions & 0 deletions assets/scss/functions/_flex-grid.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
@function flex-grid($columns: 1, $total-columns: 1, $length: '%', $fullscreen: false) {
@return calc(#{flex-grid-value($columns, $total-columns, $length, $fullscreen)});
}

@function flex-grid-value($columns: 1, $total-columns: 1, $length: '%', $fullscreen: false) {
$num-gutters: if($fullscreen, $total-columns + 1, $total-columns - 1);
$total-width: '(100#{$length} - #{$num-gutters} * var(--gutter))';
$result: '(#{$total-width} / #{$total-columns})';

@if $columns != 1 {
$result: '#{$result} * #{$columns} + #{$columns - 1} * var(--gutter)';
}

@return $result;
}
44 changes: 44 additions & 0 deletions components/molecules/VSlider/Default.stories.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<script setup lang="ts"></script>

<template>
<NuxtStory>
<NuxtStoryVariant title="Default">
<VSlider v-slot="{ itemClass }" :class="$style.root">
<div v-for="item in 20" :key="item" :class="[$style.item, itemClass]">
{{ item }}
</div>
</VSlider>
</NuxtStoryVariant>
<NuxtStoryVariant title="With scroll bar">
<VSlider v-slot="{ itemClass }" :class="[$style.root, $style['root--with-scroll-bar']]">
<div v-for="item2 in 20" :key="item2 + '-2'" :class="[$style.item, itemClass]">
{{ item2 }}
</div>
</VSlider>
</NuxtStoryVariant>
</NuxtStory>
</template>

<style lang="scss" module>
.root {
--gutter: #{rem(24)};

background-color: #d8d8d8;
gap: rem(24);
padding-block: rem(48);

&--with-scroll-bar {
scrollbar-width: thin;
}
}

.item {
display: flex;
width: flex-grid(1, 4);
height: rem(60);
align-items: center;
justify-content: center;
border: 1px solid black;
background-color: #ffd6d6;
}
</style>
84 changes: 84 additions & 0 deletions components/molecules/VSlider/IrregularWidthSlide.stories.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<script setup lang="ts">
import type { ComponentPublicInstance } from 'vue'
import type { VSlider } from '#components'
import type { Slide } from '~/components/molecules/VSlider/VSlider.vue'

const vSliderInstance = ref<ComponentPublicInstance<typeof VSlider> | null>(null)
const slides = computed(() => vSliderInstance.value?.slides as Slide[])

function setSnapMarkers(slides: Slide[]) {
slides.forEach((slide) => {
const leftMarker = slide.el?.querySelector('.marker-left') as HTMLElement
const rightMarker = slide.el?.querySelector('.marker-right') as HTMLElement

if (leftMarker) leftMarker.style.left = slide.snapLeft + 'px'
if (rightMarker) rightMarker.style.left = slide.snapRight + 'px'
})
}

watch(slides, (value) => {
if (value?.length) setSnapMarkers(value)
})
</script>

<template>
<NuxtStory>
<VSlider v-slot="{ itemClass }" ref="vSliderInstance" :class="$style.root">
<div v-for="item in 10" :key="item" :class="[$style.item, itemClass]">
<div class="marker marker-left"></div>
{{ item }}
<div class="marker marker-right"></div>
</div>
</VSlider>
</NuxtStory>
</template>

<style>
.marker-right,
.marker-left {
position: absolute;
z-index: 2;
left: 0;
width: 2px;
height: 100%;
background-color: red;
}
</style>

<style lang="scss" module>
.root {
--gutter: #{rem(24)};

position: relative;
background-color: #d8d8d8;
gap: rem(24);
padding-block: rem(48);

&--with-scroll-bar {
scrollbar-width: thin;
}
}

.item {
display: flex;
width: flex-grid(2, 10);
height: rem(300);
align-items: center;
justify-content: center;
border: 1px solid black;
background-color: #ffd6d6;

&:nth-child(odd) :global(.marker) {
width: 8px;
background-color: blue;
}

&:nth-child(2n) {
width: flex-grid(6, 10);
}

&:nth-child(3n) {
width: flex-grid(5, 10);
}
}
</style>
84 changes: 84 additions & 0 deletions components/molecules/VSlider/OneSlide.stories.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<script setup lang="ts">
import type { ComponentPublicInstance } from 'vue'
import type { VSlider } from '#components'
import type { Slide } from '~/components/molecules/VSlider/VSlider.vue'

const itemLength = ref(6)
const slideIndex = ref(0)

const vSliderInstance = ref<ComponentPublicInstance<typeof VSlider> | null>(null)
const slides = computed(() => vSliderInstance.value?.slides as Slide[])

function setSnapMarkers(slides: Slide[]) {
slides.forEach((slide) => {
const leftMarker = slide.el?.querySelector('.marker-left') as HTMLElement
const rightMarker = slide.el?.querySelector('.marker-right') as HTMLElement

if (leftMarker) {
leftMarker.style.left = slide.snapLeft + 'px'
}
if (rightMarker) {
rightMarker.style.left = slide.snapRight + 'px'
}
})
}

watch(slides, (value) => {
if (value?.length) setSnapMarkers(value)
})
</script>

<template>
<NuxtStory>
<VSlider v-slot="{ itemClass }" ref="vSliderInstance" v-model="slideIndex" :class="$style.root">
<div v-for="item in itemLength" :key="item" :class="[$style.item, itemClass]">
<div class="marker marker-left"></div>
{{ item }}
<div class="marker marker-right"></div>
</div>
</VSlider>
</NuxtStory>
</template>

<style>
.marker-right,
.marker-left {
position: absolute;
z-index: 2;
left: 0;
width: 2px;
height: 100%;
background-color: red;
}
</style>

<style lang="scss" module>
.root {
--gutter: #{rem(24)};
--v-slider-scroll-snap-align: center;

position: relative;
background-color: #d8d8d8;
padding-block: rem(48);

:global(.nuxt-story__main) {
touch-action: pan-x;
}
}

.item {
display: flex;
width: 60%;
height: rem(260);
align-items: center;
justify-content: center;
border: 1px solid black;
background-color: #ffd6d6;
margin-inline: 20%;

&:nth-child(odd) :global(.marker) {
width: 4px;
background-color: blue;
}
}
</style>
96 changes: 96 additions & 0 deletions components/molecules/VSlider/SnapVisualizer.stories.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<script setup lang="ts">
import type { ComponentPublicInstance } from 'vue'
import type { VSlider } from '#components'
import type { Slide } from '~/components/molecules/VSlider/VSlider.vue'

const itemLength = ref(18)
const value = ref(25)
const slideIndex = ref(0)

const vSliderInstance = ref<ComponentPublicInstance<typeof VSlider> | null>(null)
const slides = computed(() => vSliderInstance.value?.slides as Slide[])

function setSnapMarkers(slides: Slide[]) {
slides.forEach((slide) => {
const leftMarker = slide.el?.querySelector('.marker-left') as HTMLElement
const rightMarker = slide.el?.querySelector('.marker-right') as HTMLElement

if (leftMarker) leftMarker.style.left = slide.snapLeft + 'px'
if (rightMarker) rightMarker.style.left = slide.snapRight + 'px'
})
}

watch(slides, (value) => {
if (value?.length) setSnapMarkers(value)
})
</script>

<template>
<NuxtStory>
<template #aside>
<h4>Slide Index: {{ slideIndex }}</h4>
<h4>Slide Length: {{ itemLength }}</h4>
<button @click="() => (itemLength = itemLength - 1)">Remove slide</button>
<button @click="() => (itemLength = itemLength + 1)">Add slide</button>
<hr />
<h4>Slide width: {{ value }} %</h4>
<label for="item-width">
<input id="item-width" v-model="value" type="range" name="item-width" min="10" step="5" max="100" />
</label>
</template>
<VSlider v-slot="{ itemClass }" ref="vSliderInstance" v-model="slideIndex" :class="$style.root">
<div v-for="item in itemLength" :key="item" :class="[$style.item, itemClass]">
<div class="marker marker-left"></div>
{{ item }}
<div class="marker marker-right"></div>
</div>
</VSlider>

<div>
<button @click="() => (slideIndex = slideIndex - 1)">Prev</button>
<button @click="() => (slideIndex = slideIndex + 1)">Next</button>
</div>
</NuxtStory>
</template>

<style>
.marker-right,
.marker-left {
position: absolute;
z-index: 2;
left: 0;
width: 2px;
height: 100%;
background-color: red;
}
</style>

<style lang="scss" module>
.root {
--gutter: #{rem(24)};

position: relative;
background-color: #d8d8d8;
gap: rem(24);
padding-block: rem(48);

:global(.nuxt-story__main) {
touch-action: pan-x;
}
}

.item {
display: flex;
width: calc(((100% - 99 * var(--gutter)) / 100) * v-bind(value) + ((v-bind(value) - 1) * var(--gutter)));
height: rem(260);
align-items: center;
justify-content: center;
border: 1px solid black;
background-color: #ffd6d6;

&:nth-child(odd) :global(.marker) {
width: 4px;
background-color: blue;
}
}
</style>
41 changes: 41 additions & 0 deletions components/molecules/VSlider/VImgSlider.stories.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<script setup lang="ts">
const imageProps = {
src: '01.jpg',
width: 900,
height: 600,
alt: 'Image alt text',
provider: 'interventionRequest',
}
</script>

<template>
<NuxtStory>
<NuxtStoryVariant title="Default" :class="$style.root">
<VSlider v-slot="{ itemClass }" :class="$style.slider">
<VImg v-for="item in 20" :key="item" v-bind="imageProps" :class="[$style.item, itemClass]" />
</VSlider>
</NuxtStoryVariant>
</NuxtStory>
</template>

<style lang="scss" module>
.root {
:global(.nuxt-story-variant__main) {
display: grid;
}
}

.slider {
--gutter: #{rem(24)};

background-color: #d8d8d8;
gap: rem(24);
padding-block: rem(48);
}

.item {
width: flex-grid(1, 4);
border: 1px solid black;
background-color: #ffd6d6;
}
</style>
Loading