Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: BIMDataCarousel component #198

Merged
merged 1 commit into from
Apr 21, 2022
Merged
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 components.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export { default as BIMDataBigSpinner } from "./dist/js/BIMDataComponents/BIMDataBigSpinner.js";
export { default as BIMDataButton } from "./dist/js/BIMDataComponents/BIMDataButton.js";
export { default as BIMDataCard } from "./dist/js/BIMDataComponents/BIMDataCard.js";
export { default as BIMDataCarousel } from "./dist/js/BIMDataComponents/BIMDataCarousel.js";
export { default as BIMDataCheckbox } from "./dist/js/BIMDataComponents/BIMDataCheckbox.js";
export { default as BIMDataColorSelector } from "./dist/js/BIMDataComponents/BIMDataColorSelector.js";
export { default as BIMDataDropdownList } from "./dist/js/BIMDataComponents/BIMDataDropdownList.js";
Expand Down
1 change: 1 addition & 0 deletions rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ function getSingleComponentConfigurations() {
"BIMDataBigSpinner",
"BIMDataButton",
"BIMDataCard",
"BIMDataCarousel",
"BIMDataCheckbox",
"BIMDataColorSelector",
"BIMDataDropdownList",
Expand Down
37 changes: 37 additions & 0 deletions src/BIMDataComponents/BIMDataCarousel/BIMDataCarousel.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
.bimdata-carousel {
--button-size: 44px;

position: relative;

&__container {
overflow-x: hidden;

// Here is a hack to make overflow-y visible
// See: https://stackoverflow.com/q/6421966/8298197
margin: -28px 0;
padding: 28px 0;

&__slider {
position: relative;
transition: transform 0.5s ease-out;
}
}

&__btn-prev,
&__btn-next {
position: absolute;
z-index: 2;
top: calc(50% - var(--button-size) / 2);
width: var(--button-size);
height: var(--button-size);
box-shadow: var(--box-shadow);
}

&__btn-prev {
left: calc(0px - var(--button-size) / 2);
}

&__btn-next {
right: calc(0px - var(--button-size) / 2);
}
}
131 changes: 131 additions & 0 deletions src/BIMDataComponents/BIMDataCarousel/BIMDataCarousel.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
<template>
<div class="bimdata-carousel">
<BIMDataButton
v-show="index > 0"
class="bimdata-carousel__btn-prev"
color="default"
fill
rounded
icon
@click="index--"
>
<BIMDataIcon name="chevron" size="s" :rotate="180" />
</BIMDataButton>
<div class="bimdata-carousel__container">
<div
class="bimdata-carousel__container__slider"
ref="slider"
:style="{
transform: `translateX(-${translations[index]}px)`,
}"
>
<slot></slot>
</div>
</div>
<BIMDataButton
v-show="index < maxIndex"
class="bimdata-carousel__btn-next"
color="default"
fill
rounded
icon
@click="index++"
>
<BIMDataIcon name="chevron" size="s" />
</BIMDataButton>
</div>
</template>

<script>
import BIMDataButton from "../BIMDataButton/BIMDataButton.vue";
import BIMDataIcon from "../BIMDataIcon/BIMDataIcon.vue";

export default {
components: {
BIMDataButton,
BIMDataIcon,
},
props: {
sliderPadding: {
type: Number,
default: 12,
},
minGap: {
// Minimum amount of space between items
type: Number,
default: 24,
},
},
data() {
return {
index: 0,
maxIndex: 0,
translations: [],
};
},
mounted() {
this.distributeItems();
this.resizeObserver = new ResizeObserver(() => this.distributeItems());
this.resizeObserver.observe(this.$refs.slider);
},
unmounted() {
this.resizeObserver.disconnect();
},
destroyed() {
this.resizeObserver.disconnect();
},
methods: {
distributeItems() {
const sliderWidth = this.$refs.slider.getBoundingClientRect().width;
// Compute the actual width that will be available to distribute items
const contentWidth = sliderWidth - 2 * this.sliderPadding;

const children = Array.from(this.$refs.slider.children);

if (children.length > 0) {
// Note: it is assumed that all items have the same dimensions (width/height)
const { width: itemWidth, height: itemHeight } =
children[0].getBoundingClientRect();

// Set slider height according to items height and slider padding
this.$refs.slider.style.height = `${
itemHeight + 2 * this.sliderPadding
}px`;

// Calculate the maximum number of items that can be displayed
// at the same time according to slider width and items width
// taking minimum gap into account
let nbDisplayed = Math.max(
Math.floor(contentWidth / (itemWidth + this.minGap)),
1 // Display at least 1 item even if slider width is not large enough
);

// Calculate the actual gap between items
let gap = this.minGap;
if (nbDisplayed > 1) {
gap = (contentWidth - nbDisplayed * itemWidth) / (nbDisplayed - 1);
}

// Compute offsets (translations) and set items styles
let offset;
const offsetUnit = itemWidth + gap;
const offsets = [];
for (let i = 0; i < children.length; i++) {
offset = i * offsetUnit;
offsets.push(offset);
Object.assign(children[i].style, {
position: "absolute",
top: `${this.sliderPadding}px`,
left: `${this.sliderPadding + offset}px`,
});
}

this.maxIndex = children.length - nbDisplayed;
this.translations = offsets;
}
},
},
};
</script>

<style scoped lang="scss" src="./BIMDataCarousel.scss"></style>
1 change: 1 addition & 0 deletions src/BIMDataComponents/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import "../assets/css/utilities/_text.css";
export { default as BIMDataBigSpinner } from "./BIMDataBigSpinner/BIMDataBigSpinner.vue";
export { default as BIMDataButton } from "./BIMDataButton/BIMDataButton.vue";
export { default as BIMDataCard } from "./BIMDataCard/BIMDataCard.vue";
export { default as BIMDataCarousel } from "./BIMDataCarousel/BIMDataCarousel.vue";
export { default as BIMDataCheckbox } from "./BIMDataCheckbox/BIMDataCheckbox.vue";
export { default as BIMDataColorSelector } from "./BIMDataColorSelector/BIMDataColorSelector.vue";
export { default as BIMDataDropdownList } from "./BIMDataDropdownList/BIMDataDropdownList.vue";
Expand Down
7 changes: 7 additions & 0 deletions src/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,13 @@ export default new Vuex.Store({
text: "Cards are used to groups a related content.",
btn: "View cards",
},
{
title: "Carousel",
img: require("./web/assets/img/icon-pagination.svg"),
path: "carousel",
text: "A generic carousel component.",
btn: "View carousel",
},
{
title: "Checkbox",
img: require("./web/assets/img/icon-checkbox.svg"),
Expand Down
6 changes: 6 additions & 0 deletions src/web/router/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import Variables from "../views/Guidelines/Variables/Variables.vue";
// import COMPONENTS
import Buttons from "../views/Components/Buttons/Buttons.vue";
import Card from "../views/Components/Card/Card.vue";
import Carousel from "../views/Components/Carousel/Carousel.vue";
import Checkbox from "../views/Components/Checkbox/Checkbox.vue";
import ColorSelector from "../views/Components/ColorSelector/ColorSelector.vue";
import DropdownMenu from "../views/Components/DropdownMenu/DropdownMenu.vue";
Expand Down Expand Up @@ -148,6 +149,11 @@ const routes = [
name: "cards",
component: Card,
},
{
path: "carousel",
name: "carousel",
component: Carousel,
},
{
path: "checkbox",
name: "checkbox",
Expand Down
109 changes: 109 additions & 0 deletions src/web/views/Components/Carousel/Carousel.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<template>
<main class="article article-card">
<div class="article-wrapper">
<BIMDataText component="h1" color="color-primary">
{{ $route.name }}
</BIMDataText>

<ComponentCode :componentTitle="$route.name" language="javascript">
<template #module>
<BIMDataCarousel
:style="{ width: carouselWidth }"
:sliderPadding="+sliderPadding"
:minGap="+minGap"
>
<div
style="height: 100px; background-color: #ddd"
:style="{ width: `${itemWidth}px` }"
v-for="i of [1, 2, 3, 4, 5, 6]"
:key="i"
></div>
</BIMDataCarousel>
</template>

<template #parameters>
<BIMDataInput
margin="24px 0"
type="text"
placeholder="Carousel width"
v-model="carouselWidth"
/>
<!-- <BIMDataInput
margin="24px 0"
type="number"
placeholder="Item width (in px)"
v-model="itemWidth"
/>
<BIMDataInput
margin="24px 0"
type="number"
placeholder="Slider padding (in px)"
v-model="sliderPadding"
/>
<BIMDataInput
margin="24px 0"
type="number"
placeholder="Minimum gap (in px)"
v-model="minGap"
/> -->
</template>

<template #import>
import BIMDataCarousel from
"@bimdata/design-system/dist/js/BIMDataComponents/BIMDataCarousel.js";
</template>

<template #code>
<pre>
&lt;BIMDataCarousel :style="{ width: {{ carouselWidth }} }"&gt;
&lt;div
style="height: 100px; background-color: #ddd"
:style="{ width: {{ `${itemWidth}px` }} }"
v-for="i of [1, 2, 3, 4, 5, 6]"
:key="i"
&gt;&lt;/div&gt;
&lt;/BIMDataCarousel&gt;
</pre>
</template>
</ComponentCode>

<div class="m-t-12">
<BIMDataText component="h5" margin="15px 0 10px" color="color-primary">
Props:
</BIMDataText>
<BIMDataTable :columns="propsData[0]" :rows="propsData.slice(1)" />
</div>
</div>
</main>
</template>

<script>
import propsData from "./props-data.js";

import BIMDataCarousel from "../../../../BIMDataComponents/BIMDataCarousel/BIMDataCarousel.vue";
import BIMDataInput from "../../../../BIMDataComponents/BIMDataInput/BIMDataInput.vue";
import BIMDataTable from "../../../../BIMDataComponents/BIMDataTable/BIMDataTable.vue";
import BIMDataText from "../../../../BIMDataComponents/BIMDataText/BIMDataText.vue";
import ComponentCode from "../../Elements/ComponentCode/ComponentCode.vue";

export default {
components: {
BIMDataCarousel,
BIMDataInput,
BIMDataTable,
BIMDataText,
ComponentCode,
},
data() {
return {
carouselWidth: "60%",
itemWidth: 150,
sliderPadding: 12,
minGap: 24,

// Data
propsData,
};
},
};
</script>
16 changes: 16 additions & 0 deletions src/web/views/Components/Carousel/props-data.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/* eslint-disable */
export default [
[ "Name", "Type", "Default value", "Description" ],
[
"sliderPadding",
"Number",
"12",
"Slider padding (in px)."
],
[
"minGap",
"Number",
"24",
"Minimum amount of space between items (in px)."
],
];