Skip to content

Instantly share code, notes, and snippets.

@AvocadoVenom
Created June 19, 2022 20:58
Show Gist options
  • Select an option

  • Save AvocadoVenom/63e27a566e1a7f6842ac66d29f33b3f2 to your computer and use it in GitHub Desktop.

Select an option

Save AvocadoVenom/63e27a566e1a7f6842ac66d29f33b3f2 to your computer and use it in GitHub Desktop.
Expandable block content with smooth read less/read more feature
<template>
<div class="block-content">
<transition
@before-enter="onBeforeEnter"
@enter="onEnter"
@after-enter="onAfterEnter"
@before-leave="onBeforeLeave"
@leave="onLeave"
mode="out-in"
>
<p v-if="!isExpanded" class="block-content__preview" v-html="content" />
<p
v-else
:class="{
'block-content__paragraph': true,
'block-content__paragraph--is-expanded': isExpanded,
}"
v-html="content"
/>
</transition>
<button
type="button"
class="block-content__button"
aria-label="Toggle button"
@click="isExpanded = !isExpanded"
>
{{ toggleCtaLabel }}
</button>
</div>
</template>
<script lang="ts">
import { computed, defineComponent, ref, toRefs } from "vue";
export default defineComponent({
name: "ExpandableBlockContent",
props: {
content: { type: String },
visibleLines: { type: Number, default: 4 },
},
setup(props) {
const { visibleLines } = toRefs(props);
// Collapsed state
// Assuming that default line-height is 24px
const LINE_HEIGHT = 24;
const maxHeightCollapsed = computed(() => LINE_HEIGHT * visibleLines.value);
// Animation hooks
const onBeforeEnter = (el: Element) => {
const htmlEl = el as HTMLElement;
htmlEl.style.height = maxHeightCollapsed.value + "px";
};
const onEnter = (el: Element) => {
const htmlEl = el as HTMLElement;
htmlEl.style.height = el.scrollHeight + "px";
};
const onAfterEnter = (el: Element) => {
const htmlEl = el as HTMLElement;
htmlEl.style.overflow = "hidden";
};
const onBeforeLeave = (el: Element) => {
const htmlEl = el as HTMLElement;
htmlEl.style.height = `${el.scrollHeight}px`;
htmlEl.style.overflow = "hidden";
};
const onLeave = (el: Element) => {
const htmlEl = el as HTMLElement;
htmlEl.style.height = maxHeightCollapsed.value + "px";
htmlEl.style.overflow = "hidden";
};
// Expanded state
const isExpanded = ref(false);
const toggleCtaLabel = computed(() =>
isExpanded.value ? "Read less" : "Read more"
);
return {
isExpanded,
toggleCtaLabel,
onBeforeEnter,
onEnter,
onAfterEnter,
onBeforeLeave,
onLeave,
};
},
});
</script>
<style lang="scss" scoped>
.block-content {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 8px;
padding: 24px 20px;
background-color: skyblue;
border-radius: 8px;
}
.block-content__preview {
/* default line-height is 24px */
max-height: calc(24px * v-bind(visibleLines));
overflow: hidden;
color: white;
}
.block-content__paragraph {
color: white;
overflow: hidden;
transition: all 250ms ease-out;
}
.block-content__paragraph.block-content__paragraph--is-expanded {
overflow: initial;
}
.block-content__button {
color: white;
text-decoration: underline;
align-self: flex-end;
}
</style>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment