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

CED-1851 Create esSkeleton, SkeletonWrapper, SkeletonImg #1547

Merged
merged 12 commits into from
Oct 8, 2024
Merged
35 changes: 35 additions & 0 deletions es-ds-components/components/es-skeleton-img.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<script setup lang="ts">
const props = defineProps({
aspect: {
type: String,
default: '16:9',
},
noAspect: {
type: Boolean,
default: false,
},
});

const aspects = props.aspect.split(':');
const aspectRatio = computed(() => `${(parseFloat(aspects[1]) / parseFloat(aspects[0])) * 100}%`);
</script>

<template>
<es-skeleton
v-if="noAspect"
v-bind="$attrs" />
<div
v-else
class="d-flex">
<div
class="flex-grow-1"
:style="{ 'padding-bottom': aspectRatio, height: '0px' }" />
<div
class="flex-grow-1 w-100 mw-100"
style="margin-left: -100%">
<es-skeleton
v-bind="$attrs"
class="h-100 w-100" />
</div>
</div>
</template>
17 changes: 17 additions & 0 deletions es-ds-components/components/es-skeleton-wrapper.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<script setup lang="ts">
defineProps({
loading: {
type: Boolean,
default: false,
},
});
</script>

<template>
<div>
<slot
v-if="loading"
name="loading" />
<slot v-else />
</div>
</template>
97 changes: 97 additions & 0 deletions es-ds-components/components/es-skeleton.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<script setup lang="ts">
import Skeleton from 'primevue/skeleton';
defineProps({
animation: {
type: String,
default: 'wave',
validator: (val: string) => ['wave', 'fade', 'none'].includes(val as string),
},
height: {
type: String,
default: '1rem',
},
width: {
type: String,
default: 'auto',
},
size: {
type: String,
default: null,
},
});
</script>

<template>
<skeleton
:height="size ? size : height"
:width="size ? size : width"
class="es-skeleton rounded-xs mb-25 bg-gray-200"
:class="{
'es-skeleton-wave': animation == 'wave',
'es-skeleton-fade': animation == 'fade',
}" />
</template>

<style lang="scss" scoped>
@use '@energysage/es-ds-styles/scss/variables' as variables;
@keyframes skeleton-animate-wave {
0% {
transform: translateX(-100%);
}
100% {
transform: translateX(100%);
}
}
@keyframes skeleton-animate-fade {
0% {
opacity: 1;
}
100% {
opacity: 0.4;
}
}
.es-skeleton {
overflow: hidden;
&-wave:after {
content: '';
height: 100%;
position: absolute;
left: 0;
right: 0;
top: 0;
z-index: 1;
// wave animation from PrimeVue Skeleton
background-image: linear-gradient(
90deg,
rgba(variables.$white, 0),
rgba(variables.$white, 0.4),
rgba(variables.$white, 0)
);
animation-name: skeleton-animate-wave;
animation-duration: 1.2s;
animation-timing-function: ease;
animation-delay: 0s;
animation-iteration-count: infinite;
animation-direction: normal;
animation-fill-mode: none;
animation-play-state: running;
}
&-fade {
// fade animation from Bootstrap Skeleton
animation-name: skeleton-animate-fade;
animation-duration: 0.875s;
animation-timing-function: ease-in-out;
animation-delay: 0s;
animation-iteration-count: infinite;
animation-direction: alternate;
animation-fill-mode: none;
animation-play-state: running;
}
}
</style>
1 change: 1 addition & 0 deletions es-ds-components/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export default defineNuxtConfig({
'primevue/progressbar',
'primevue/radiobutton',
'primevue/rating',
'primevue/skeleton',
'primevue/slider',
'primevue/tabpanel',
'primevue/tabview',
Expand Down
3 changes: 3 additions & 0 deletions es-ds-docs/components/ds-molecules-list.vue
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@
<li>
<ds-link to="/molecules/rating"> Rating </ds-link>
</li>
<li>
<ds-link to="/molecules/skeleton"> Skeleton </ds-link>
</li>
<li>
<ds-link to="/molecules/slider"> Slider </ds-link>
</li>
Expand Down
167 changes: 167 additions & 0 deletions es-ds-docs/pages/molecules/skeleton.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
<script setup lang="ts">
const loading = ref(true);

const asyncTimeout = async (seconds = 3) => {
const millisecondTimeout = seconds * 1000;
return new Promise((resolve) => {
setTimeout(resolve, millisecondTimeout);
});
};

const startLoading = async () => {
loading.value = true;
await asyncTimeout();
loading.value = false;
};

const esSkeletonProps = [
['animation', 'String', 'wave', `Options are 'wave', 'fade', or 'none'`],
['height', 'String', '1rem', `Manually set height`],
['width', 'String', 'auto', `Manually set width`],
['size', 'String', 'null', `Manually set width and height, overriding 'height' and 'width' props`],
];

const esSkeletonImgProps = [
['aspect', 'String', '16:9', `Adds a container around the skeleton image with given aspect ratio`],
['noAspect', 'Boolean', 'false', `Override the default aspect ratio and height, width, or size`],
...esSkeletonProps,
];
lgeggleston marked this conversation as resolved.
Show resolved Hide resolved

const esSkeletonWrapperProps = [
['loading', 'Boolean', 'false', `Determines whether to show loading template or default content`],
];

const { $prism } = useNuxtApp();
const compCode = ref('');
const docCode = ref('');
onMounted(async () => {
if ($prism) {
const compSource = await import('@energysage/es-ds-components/components/es-skeleton.vue?raw');
// eslint-disable-next-line import/no-self-import
const docSource = await import('./skeleton.vue?raw');
compCode.value = $prism.normalizeCode(compSource.default);
docCode.value = $prism.normalizeCode(docSource.default);
$prism.highlight();
}

startLoading();
});
</script>

<template>
<div>
<h1>Skeleton</h1>
<p>
Extended from
<nuxt-link
href="https://v3.primevue.org/skeleton/"
target="_blank">
PrimeVue Skeleton
</nuxt-link>
and
<nuxt-link
href="https://bootstrap-vue.org/docs/components/skeleton/"
target="_blank">
Bootstrap Skeleton
</nuxt-link>
styles
</p>

<h2>Basic examples</h2>
<p>Wave animation:</p>
<es-row class="mb-200">
<es-col lg="6">
<es-skeleton />
<es-skeleton width="75%" />
<es-skeleton height="3rem" />
</es-col>
<es-col
lg="6"
class="d-flex">
<es-skeleton size="5rem" />
<es-skeleton
size="4rem"
class="rounded-circle ml-100" />
</es-col>
</es-row>

<p>Fade animation:</p>
<es-row class="mb-500">
<es-col lg="6">
<es-skeleton
animation="fade"
height="33px"
width="100%" />
<es-skeleton
animation="fade"
height="25px"
width="100%" />
</es-col>
</es-row>

<h2>Helper components</h2>

<h3>Skeleton image</h3>
<es-row class="mb-500 d-flex">
<es-col lg="6">
<es-skeleton-img />
</es-col>
<es-col lg="6">
<es-skeleton-img aspect="3:1" />
</es-col>
<es-col
cols="12"
class="mt-100">
<es-skeleton-img
no-aspect
height="5rem" />
</es-col>
</es-row>

<h3>Skeleton wrapper</h3>
<es-row class="mb-500 d-flex">
<es-col
cols="12"
lg="3"
class="mb-100 mb-lg-0">
<es-button @click="startLoading()">Reload content</es-button>
</es-col>
<es-col
lg="7"
class="ml-lg-100">
<es-skeleton-wrapper :loading="loading">
<template #loading>
<es-skeleton width="85%"></es-skeleton>
<es-skeleton width="55%"></es-skeleton>
</template>

<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas viverra nunc sapien, non
rhoncus elit tincidunt vitae.
</p>
</es-skeleton-wrapper>
</es-col>
</es-row>

<div class="my-500">
<h2>EsSkeleton props</h2>
<ds-prop-table :rows="esSkeletonProps" />
</div>

<div class="my-500">
<h2>EsSkeletonImg props</h2>
<ds-prop-table :rows="esSkeletonImgProps" />
</div>

<div class="my-500">
<h2>EsSkeletonWrapper props</h2>
<ds-prop-table :rows="esSkeletonWrapperProps" />
</div>

<ds-doc-source
:comp-code="compCode"
comp-source="es-ds-components/components/es-skeleton.vue"
:doc-code="docCode"
doc-source="es-ds-docs/pages/molecules/skeleton.vue" />
</div>
</template>