Loading src/Playground.vue +27 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import Knob from '@components/Knob/Knob.vue'; import Rating from '@components/Rating/Rating.vue'; import HomeIcon from '@icons/Mono/HomeIcon.vue'; import ProgressBar from '@components/ProgressBar/ProgressBar.vue'; import Carousel from '@components/Carousel/Carousel.vue'; const visibleDrawer = ref(false); const sliderOptions: ISliderOptions[] = [ Loading Loading @@ -201,6 +202,31 @@ const openDrawer = () => (visibleDrawer.value = true); <Checkbox v-model="activeCheckbox" size="large" /> <Checkbox v-model="activeCheckbox" size="huge" /> <ProgressBar v-model="pbValue" /> <Carousel style="margin: 20px" :itemsProps="[ { index: 1, text: 'This is SPARTA!', }, { index: 2, text: 'This is the second item!', }, { index: 3, text: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Animi atque blanditiis debitis distinctio, doloribus,\n' + ' eius est eveniet excepturi facere id iure laboriosam laborum libero, minus nesciunt nostrum nulla repellat\n' + ' veritatis.', }, ]" > <template v-slot="item: unknown"> <h2 style="text-align: center; margin-bottom: 20px">Element {{ item?.index }}</h2> <p>{{ item?.text }}</p></template > </Carousel> {{ tableData[1] }} <Table center Loading @@ -211,6 +237,7 @@ const openDrawer = () => (visibleDrawer.value = true); theme="black" stripedRows paginator editable :no-editing-settings="{ cells: [[0, 0]], }" Loading src/common/interfaces/componentsProps.ts +3 −1 Original line number Diff line number Diff line Loading @@ -81,10 +81,12 @@ export interface IPaginatorProps { export interface ICarouselProps { itemsProps: unknown[]; width?: string; innerWidth?: string; size?: TSize; perView?: number; perScroll?: number; circular?: boolean; buttonsBelow?: boolean; theme?: TThemeColor; darknessTheme?: TDarkness; } Loading src/components/Carousel/Carousel.stories.ts +65 −57 Original line number Diff line number Diff line Loading @@ -14,25 +14,14 @@ const meta: Meta = { }, }, argTypes: { buttons: { control: 'boolean' }, showLabel: { control: 'boolean' }, colorAsTheme: { control: 'boolean' }, textBold: { control: 'boolean' }, min: { control: 'number' }, max: { control: 'number' }, step: { control: 'number' }, fontSize: { control: 'text' }, textBefore: { control: 'text' }, textAfter: { control: 'text' }, colorGaps: { control: 'object' }, itemsProps: { control: 'object' }, innerWidth: { control: 'text' }, perView: { control: 'number' }, perScroll: { control: 'number' }, circular: { control: 'boolean' }, buttonsBelow: { control: 'boolean' }, size: { control: 'select', options: ['small', 'normal', 'large', 'huge'] }, background: { control: 'color' }, darknessTheme: { control: 'select', options: ['100', '200', '300', '400', '500', '600', '700', '800', '900'] }, darknessNegativeTheme: { control: 'select', options: ['100', '200', '300', '400', '500', '600', '700', '800', '900'], }, darknessColor: { control: 'select', options: ['100', '200', '300', '400', '500', '600', '700', '800', '900'] }, theme: { control: 'select', options: [ Loading @@ -53,46 +42,6 @@ const meta: Meta = { 'black', ], }, negativeTheme: { control: 'select', options: [ 'white', 'blue', 'sky', 'cyan', 'teal', 'green', 'yellow', 'orange', 'pink', 'fuchsia', 'purple', 'indigo', 'rose', 'red', 'black', ], }, color: { control: 'select', options: [ 'white', 'blue', 'sky', 'cyan', 'teal', 'green', 'yellow', 'orange', 'pink', 'fuchsia', 'purple', 'indigo', 'rose', 'red', 'black', ], }, }, } satisfies Meta<typeof Carousel>; Loading @@ -103,3 +52,62 @@ type Story = StoryObj<typeof meta>; export const Simple: Story = { args: {}, }; export const Half: Story = { args: { circular: true, perView: 2, perScroll: 1, itemsProps: [ { header: 'First', text: 'Some text', }, { header: 'Second', text: 'Some text', }, { header: 'Third', text: 'Some text', }, { header: 'Forth', text: 'Some text', }, ], }, }; export const Full: Story = { args: { circular: true, perView: 2, perScroll: 2, itemsProps: [ { header: 'First', text: 'Some text', }, { header: 'Second', text: 'Some text', }, { header: 'Third', text: 'Some text', }, { header: 'Forth', text: 'Some text', }, { header: 'Fifth', text: 'Some text', }, ], buttonsBelow: true, }, }; src/components/Carousel/Carousel.vue +76 −8 Original line number Diff line number Diff line Loading @@ -5,19 +5,26 @@ import { computed, ref } from 'vue'; import { convertThemeToColor, convertThemeToTextColor } from '@helpers/common'; import ArrowLeftShortIcon from '@icons/Mono/ArrowLeftShortIcon.vue'; import ArrowRightShortIcon from '@icons/Mono/ArrowRightShortIcon.vue'; import { defaultProps, getNewValue } from './helpers'; const props = withDefaults(defineProps<ICarouselProps>(), { itemsProps: () => [], size: 'normal', innerWidth: '300px', perView: 1, perScroll: 1, theme: 'white', darknessTheme: '500', }); const current = ref(1); const itemsLength = computed(() => Math.ceil(props.itemsProps.length / props.perView)); const itemsLength = computed(() => props.itemsProps?.length ?? 3); const color = computed(() => convertThemeToColor(props.theme, props.darknessTheme)); const textColor = computed(() => convertThemeToTextColor(props.theme, props.darknessTheme)); const isStartDisabled = computed(() => current.value === 1); const isEndDisabled = computed(() => current.value === itemsLength.value); const isStartDisabled = computed(() => (props.circular ? false : current.value === 1 || itemsLength.value <= 1)); const isEndDisabled = computed(() => props.circular ? false : current.value === Math.ceil(itemsLength.value / props.perView) || !itemsLength.value, ); const iconSize = computed(() => { const size = props.size; if (size === 'normal') return '10'; Loading @@ -25,22 +32,83 @@ const iconSize = computed(() => { if (size === 'huge') return '18'; return '7'; }); const itemWidth = computed(() => `calc(${props.innerWidth} / ${props.perView}`); const translate = computed(() => `translateX(calc(-${props.innerWidth} / ${props.perView} * ${current.value - 1}))`); </script> <template> <section :style="`width: ${width}; min-height: 100px`" class="carouselContainer"> <CarouselArrowContainer :textColor="textColor" :color="color" :disable="isStartDisabled"> <section class="carouselContainer"> <CarouselArrowContainer @click="!isStartDisabled ? (current = getNewValue('-', current, itemsLength, perScroll, perView)) : null" :textColor="textColor" :color="color" :disable="isStartDisabled" > <ArrowLeftShortIcon :color="isStartDisabled ? '#aaa' : textColor" :size="iconSize" /> </CarouselArrowContainer> <slot /> <CarouselArrowContainer :textColor="textColor" :color="color" :disable="isEndDisabled"> <div class="content"> <ul class="list"> <li v-for="item of Array(itemsLength).keys()" :key="item" class="item"> <slot v-bind="itemsProps?.[item]" :key="current - 1" /> <div v-if="!$slots.default && !itemsProps"> <h2 style="text-align: center; margin-bottom: 10px">{{ defaultProps[item].header }}</h2> <p> {{ defaultProps[item].text }} </p> </div> <div v-else-if="!$slots.default"> <h2 style="text-align: center; margin-bottom: 10px">{{ itemsProps[item].header }}</h2> <p> {{ itemsProps[item].text }} </p> </div> </li> </ul> </div> <CarouselArrowContainer @click="!isEndDisabled ? (current = getNewValue('+', current, itemsLength, perScroll, perView)) : null" :textColor="textColor" :color="color" :disable="isEndDisabled" > <ArrowRightShortIcon :color="isEndDisabled ? '#aaa' : textColor" :size="iconSize" /> </CarouselArrowContainer> <ul class="buttons"> <li v-for="item of Array(itemsLength).keys()" :key="item" class="button" :style="`width: ${iconSize}px; height: ${iconSize}px`" ></li> </ul> </section> </template> <style scoped> .carouselContainer { display: flex; min-height: 100px; position: relative; } .content { max-width: v-bind(innerWidth); overflow: hidden; } .list { display: flex; transform: v-bind(translate); transition: transform 0.3s ease-out; } .item { min-width: v-bind(itemWidth); } .buttons { position: absolute; bottom: 0; left: 0; transform: translateX(-50%); } .button { background-color: v-bind(color); } </style> src/components/Carousel/CarouselArrowContainer.vue +19 −31 Original line number Diff line number Diff line Loading @@ -7,10 +7,9 @@ defineProps<{ </script> <template> <div class="arrowContainer"> <div :class="[ 'icon', 'arrowContainer', { disable, }, Loading @@ -24,6 +23,7 @@ defineProps<{ }, ]" ></div> <div class="icon"> <slot /> </div> </div> Loading @@ -31,49 +31,37 @@ defineProps<{ <style scoped> .arrowContainer { width: 50px; position: relative; min-width: 50px; min-height: 100%; display: flex; justify-content: center; align-items: center; cursor: pointer; } .icon { position: relative; display: flex; justify-content: center; align-items: center; cursor: pointer; line-height: 1.2; color: v-bind(textColor); } .icon::before { content: ''; position: absolute; width: 100%; height: 100%; border-radius: 50%; z-index: -1; background-color: v-bind(color); } .icon:hover > .bg { .arrowContainer:hover > .bg { background-color: v-bind(textColor); opacity: 0.1; } .icon:active > .bg { .arrowContainer:active > .bg { opacity: 0.2; } .bg { width: 100%; height: 100%; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); padding: 10px; z-index: 5; border-radius: 50%; background-color: transparent; opacity: 0; border-radius: 5px; background-color: transparent; transition: all 0.2s ease; } .disable { Loading Loading
src/Playground.vue +27 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import Knob from '@components/Knob/Knob.vue'; import Rating from '@components/Rating/Rating.vue'; import HomeIcon from '@icons/Mono/HomeIcon.vue'; import ProgressBar from '@components/ProgressBar/ProgressBar.vue'; import Carousel from '@components/Carousel/Carousel.vue'; const visibleDrawer = ref(false); const sliderOptions: ISliderOptions[] = [ Loading Loading @@ -201,6 +202,31 @@ const openDrawer = () => (visibleDrawer.value = true); <Checkbox v-model="activeCheckbox" size="large" /> <Checkbox v-model="activeCheckbox" size="huge" /> <ProgressBar v-model="pbValue" /> <Carousel style="margin: 20px" :itemsProps="[ { index: 1, text: 'This is SPARTA!', }, { index: 2, text: 'This is the second item!', }, { index: 3, text: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Animi atque blanditiis debitis distinctio, doloribus,\n' + ' eius est eveniet excepturi facere id iure laboriosam laborum libero, minus nesciunt nostrum nulla repellat\n' + ' veritatis.', }, ]" > <template v-slot="item: unknown"> <h2 style="text-align: center; margin-bottom: 20px">Element {{ item?.index }}</h2> <p>{{ item?.text }}</p></template > </Carousel> {{ tableData[1] }} <Table center Loading @@ -211,6 +237,7 @@ const openDrawer = () => (visibleDrawer.value = true); theme="black" stripedRows paginator editable :no-editing-settings="{ cells: [[0, 0]], }" Loading
src/common/interfaces/componentsProps.ts +3 −1 Original line number Diff line number Diff line Loading @@ -81,10 +81,12 @@ export interface IPaginatorProps { export interface ICarouselProps { itemsProps: unknown[]; width?: string; innerWidth?: string; size?: TSize; perView?: number; perScroll?: number; circular?: boolean; buttonsBelow?: boolean; theme?: TThemeColor; darknessTheme?: TDarkness; } Loading
src/components/Carousel/Carousel.stories.ts +65 −57 Original line number Diff line number Diff line Loading @@ -14,25 +14,14 @@ const meta: Meta = { }, }, argTypes: { buttons: { control: 'boolean' }, showLabel: { control: 'boolean' }, colorAsTheme: { control: 'boolean' }, textBold: { control: 'boolean' }, min: { control: 'number' }, max: { control: 'number' }, step: { control: 'number' }, fontSize: { control: 'text' }, textBefore: { control: 'text' }, textAfter: { control: 'text' }, colorGaps: { control: 'object' }, itemsProps: { control: 'object' }, innerWidth: { control: 'text' }, perView: { control: 'number' }, perScroll: { control: 'number' }, circular: { control: 'boolean' }, buttonsBelow: { control: 'boolean' }, size: { control: 'select', options: ['small', 'normal', 'large', 'huge'] }, background: { control: 'color' }, darknessTheme: { control: 'select', options: ['100', '200', '300', '400', '500', '600', '700', '800', '900'] }, darknessNegativeTheme: { control: 'select', options: ['100', '200', '300', '400', '500', '600', '700', '800', '900'], }, darknessColor: { control: 'select', options: ['100', '200', '300', '400', '500', '600', '700', '800', '900'] }, theme: { control: 'select', options: [ Loading @@ -53,46 +42,6 @@ const meta: Meta = { 'black', ], }, negativeTheme: { control: 'select', options: [ 'white', 'blue', 'sky', 'cyan', 'teal', 'green', 'yellow', 'orange', 'pink', 'fuchsia', 'purple', 'indigo', 'rose', 'red', 'black', ], }, color: { control: 'select', options: [ 'white', 'blue', 'sky', 'cyan', 'teal', 'green', 'yellow', 'orange', 'pink', 'fuchsia', 'purple', 'indigo', 'rose', 'red', 'black', ], }, }, } satisfies Meta<typeof Carousel>; Loading @@ -103,3 +52,62 @@ type Story = StoryObj<typeof meta>; export const Simple: Story = { args: {}, }; export const Half: Story = { args: { circular: true, perView: 2, perScroll: 1, itemsProps: [ { header: 'First', text: 'Some text', }, { header: 'Second', text: 'Some text', }, { header: 'Third', text: 'Some text', }, { header: 'Forth', text: 'Some text', }, ], }, }; export const Full: Story = { args: { circular: true, perView: 2, perScroll: 2, itemsProps: [ { header: 'First', text: 'Some text', }, { header: 'Second', text: 'Some text', }, { header: 'Third', text: 'Some text', }, { header: 'Forth', text: 'Some text', }, { header: 'Fifth', text: 'Some text', }, ], buttonsBelow: true, }, };
src/components/Carousel/Carousel.vue +76 −8 Original line number Diff line number Diff line Loading @@ -5,19 +5,26 @@ import { computed, ref } from 'vue'; import { convertThemeToColor, convertThemeToTextColor } from '@helpers/common'; import ArrowLeftShortIcon from '@icons/Mono/ArrowLeftShortIcon.vue'; import ArrowRightShortIcon from '@icons/Mono/ArrowRightShortIcon.vue'; import { defaultProps, getNewValue } from './helpers'; const props = withDefaults(defineProps<ICarouselProps>(), { itemsProps: () => [], size: 'normal', innerWidth: '300px', perView: 1, perScroll: 1, theme: 'white', darknessTheme: '500', }); const current = ref(1); const itemsLength = computed(() => Math.ceil(props.itemsProps.length / props.perView)); const itemsLength = computed(() => props.itemsProps?.length ?? 3); const color = computed(() => convertThemeToColor(props.theme, props.darknessTheme)); const textColor = computed(() => convertThemeToTextColor(props.theme, props.darknessTheme)); const isStartDisabled = computed(() => current.value === 1); const isEndDisabled = computed(() => current.value === itemsLength.value); const isStartDisabled = computed(() => (props.circular ? false : current.value === 1 || itemsLength.value <= 1)); const isEndDisabled = computed(() => props.circular ? false : current.value === Math.ceil(itemsLength.value / props.perView) || !itemsLength.value, ); const iconSize = computed(() => { const size = props.size; if (size === 'normal') return '10'; Loading @@ -25,22 +32,83 @@ const iconSize = computed(() => { if (size === 'huge') return '18'; return '7'; }); const itemWidth = computed(() => `calc(${props.innerWidth} / ${props.perView}`); const translate = computed(() => `translateX(calc(-${props.innerWidth} / ${props.perView} * ${current.value - 1}))`); </script> <template> <section :style="`width: ${width}; min-height: 100px`" class="carouselContainer"> <CarouselArrowContainer :textColor="textColor" :color="color" :disable="isStartDisabled"> <section class="carouselContainer"> <CarouselArrowContainer @click="!isStartDisabled ? (current = getNewValue('-', current, itemsLength, perScroll, perView)) : null" :textColor="textColor" :color="color" :disable="isStartDisabled" > <ArrowLeftShortIcon :color="isStartDisabled ? '#aaa' : textColor" :size="iconSize" /> </CarouselArrowContainer> <slot /> <CarouselArrowContainer :textColor="textColor" :color="color" :disable="isEndDisabled"> <div class="content"> <ul class="list"> <li v-for="item of Array(itemsLength).keys()" :key="item" class="item"> <slot v-bind="itemsProps?.[item]" :key="current - 1" /> <div v-if="!$slots.default && !itemsProps"> <h2 style="text-align: center; margin-bottom: 10px">{{ defaultProps[item].header }}</h2> <p> {{ defaultProps[item].text }} </p> </div> <div v-else-if="!$slots.default"> <h2 style="text-align: center; margin-bottom: 10px">{{ itemsProps[item].header }}</h2> <p> {{ itemsProps[item].text }} </p> </div> </li> </ul> </div> <CarouselArrowContainer @click="!isEndDisabled ? (current = getNewValue('+', current, itemsLength, perScroll, perView)) : null" :textColor="textColor" :color="color" :disable="isEndDisabled" > <ArrowRightShortIcon :color="isEndDisabled ? '#aaa' : textColor" :size="iconSize" /> </CarouselArrowContainer> <ul class="buttons"> <li v-for="item of Array(itemsLength).keys()" :key="item" class="button" :style="`width: ${iconSize}px; height: ${iconSize}px`" ></li> </ul> </section> </template> <style scoped> .carouselContainer { display: flex; min-height: 100px; position: relative; } .content { max-width: v-bind(innerWidth); overflow: hidden; } .list { display: flex; transform: v-bind(translate); transition: transform 0.3s ease-out; } .item { min-width: v-bind(itemWidth); } .buttons { position: absolute; bottom: 0; left: 0; transform: translateX(-50%); } .button { background-color: v-bind(color); } </style>
src/components/Carousel/CarouselArrowContainer.vue +19 −31 Original line number Diff line number Diff line Loading @@ -7,10 +7,9 @@ defineProps<{ </script> <template> <div class="arrowContainer"> <div :class="[ 'icon', 'arrowContainer', { disable, }, Loading @@ -24,6 +23,7 @@ defineProps<{ }, ]" ></div> <div class="icon"> <slot /> </div> </div> Loading @@ -31,49 +31,37 @@ defineProps<{ <style scoped> .arrowContainer { width: 50px; position: relative; min-width: 50px; min-height: 100%; display: flex; justify-content: center; align-items: center; cursor: pointer; } .icon { position: relative; display: flex; justify-content: center; align-items: center; cursor: pointer; line-height: 1.2; color: v-bind(textColor); } .icon::before { content: ''; position: absolute; width: 100%; height: 100%; border-radius: 50%; z-index: -1; background-color: v-bind(color); } .icon:hover > .bg { .arrowContainer:hover > .bg { background-color: v-bind(textColor); opacity: 0.1; } .icon:active > .bg { .arrowContainer:active > .bg { opacity: 0.2; } .bg { width: 100%; height: 100%; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); padding: 10px; z-index: 5; border-radius: 50%; background-color: transparent; opacity: 0; border-radius: 5px; background-color: transparent; transition: all 0.2s ease; } .disable { Loading