feat: improve performance (#91)

This commit is contained in:
João Peixoto
2021-04-01 17:44:35 +01:00
committed by GitHub
parent 717691a707
commit 77f1f6f550
17 changed files with 9 additions and 909 deletions

View File

@@ -1,162 +0,0 @@
<template>
<RouterLink
v-if="isInternal"
:class="[classObject, type === 'link' && linkColor]"
:to="link"
>
{{ item.text }}
<SVGIcon
v-if="iconName"
class="cta__svg w-5 h-5"
:name="iconName"
:title="iconTitle"
/>
</RouterLink>
<a
v-else
:class="[classObject, type === 'link' && linkColor]"
:href="link"
:target="target"
:rel="rel"
>
{{ item.text }}
<SVGIcon
v-if="iconName"
class="cta__svg w-5 h-5"
:name="iconName"
:title="iconTitle"
/>
</a>
</template>
<script>
import SVGIcon from '@theme/components/base/SVGIcon.vue'
import { isExternal, isMailto, isTel, ensureExt } from '../util'
export default {
name: 'CTA',
components: { SVGIcon },
props: {
type: {
type: String,
default: 'link',
},
icon: {
type: String,
default: '',
},
iconTitle: {
type: String,
default: '',
},
iconPosition: {
type: String,
default: 'post',
},
item: {
type: Object,
required: true,
},
linkColor: {
type: String,
default: '',
},
},
computed: {
classObject() {
return {
btn: this.type !== 'link',
[this.type]: true,
[this.iconPosition]: true,
'type-cta': true,
}
},
link() {
return ensureExt(this.item.link)
},
exact() {
if (this.$site.locales) {
return Object.keys(this.$site.locales).some(
(rootLink) => rootLink === this.link
)
}
return this.link === '/'
},
iconName() {
if (this.type === 'link') {
return this.isInternal ? 'arrow-right-icon' : 'arrow-up-icon'
} else {
return this.icon
}
},
isNonHttpURI() {
return isMailto(this.link) || isTel(this.link)
},
isBlankTarget() {
return this.target === '_blank'
},
isInternal() {
return !isExternal(this.link) && !this.isBlankTarget
},
target() {
if (this.isNonHttpURI) {
return null
}
if (this.item.target) {
return this.item.target
}
return isExternal(this.link) ? '_blank' : ''
},
rel() {
if (this.isNonHttpURI) {
return null
}
if (this.item.rel) {
return this.item.rel
}
return this.isBlankTarget ? 'noopener noreferrer' : ''
},
},
}
</script>
<style scoped lang="postcss">
.btn {
@apply rounded-full inline-flex py-2 px-6 items-center;
@apply transition duration-300 ease-in-out;
}
.btn-outline {
@apply border-solid border-2 border-transparent;
background-image: linear-gradient(104.72deg, #1a74fc -4.4%, #4ef286 112.23%);
background-origin: border-box;
box-shadow: 0px 1000px 1px theme('colors.gray.light') inset;
@apply text-deepBlue;
}
.btn-outline:hover {
@apply shadow-none text-white;
}
.btn-fill {
@apply bg-webBlue text-white fill-current;
}
.btn-fill:hover {
@apply bg-deepBlue;
}
.link {
@apply inline-flex items-center;
@apply transition duration-300 ease-in-out;
}
.post {
@apply flex-row;
}
.post .cta__svg {
@apply ml-2;
}
.pre {
@apply flex-row-reverse;
}
.pre .cta__svg {
@apply mr-2;
}
</style>

View File

@@ -1,54 +0,0 @@
<template>
<div :class="theme === 'dark' ? 'text-deepBlue' : 'text-white'">
<LazyImage v-if="image" v-bind="image" class="mb-6" />
<h3 class="type-h3 mb-3 lg:mb-6">{{ title }}</h3>
<p class="type-p1" :class="[{ 'mb-6 lg:mb-8': cta && cta.item }]">
{{ text }}
</p>
<CTA
v-if="cta && cta.item"
v-bind="cta"
:link-color="
theme === 'dark'
? 'text-inherit hover:text-webBlue'
: 'text-inherit border-b border-transparent hover:border-white'
"
/>
</div>
</template>
<script>
import CTA from '@theme/components/CTA.vue'
import LazyImage from '@theme/components/base/LazyImage.vue'
export default {
name: 'ColumnText',
components: { CTA, LazyImage },
props: {
theme: {
type: String,
default: 'dark',
},
title: {
type: String,
default: '',
},
text: {
type: String,
required: true,
},
cta: {
type: Object,
default: null,
},
image: {
type: Object,
default: null,
},
},
}
</script>
<style scoped lang="postcss">
.image--eyebrow {
max-width: 25%;
}
</style>

View File

@@ -1,46 +0,0 @@
<template>
<div
:class="[
$page.frontmatter.slug,
{ 'relative overflow-auto': content.subnavigation },
]"
>
<Subnavigation
v-if="content.subnavigation"
v-bind="content.subnavigation"
/>
<component
:is="block.component"
v-for="(block, index) in content.body"
:key="index"
v-bind="block"
/>
</div>
</template>
<script>
// Import all components that can be rendered from frontmatter array
import Column from '@theme/components/base/Column.vue'
import Container from '@theme/components/base/Container.vue'
import Section from '@theme/components/Section.vue'
import Typography from '@theme/components/Typography.vue'
// layout compositions
export const components = {
Column,
Container,
Section,
Typography,
}
export default {
name: 'DynamicContent',
components,
props: {
content: {
type: Object,
default: () => ({}),
},
},
}
</script>

View File

@@ -1,214 +0,0 @@
<template>
<div :class="['section', mergedTheme.background]">
<div class="grid grid-margins grid-cols-12" :class="mergedTheme.grid">
<div v-if="background.type === 'video'" class="absolute inset-0 z-0">
<BackgroundVideo
class="w-full h-full"
v-bind="background"
class-list="object-cover w-full h-full"
/>
</div>
<div v-if="background.type === 'image'" class="absolute inset-0 z-0">
<LazyImage
:class="[
'w-full h-full',
{ 'hidden md:block': background.mobileImg },
]"
v-bind="background.img"
:img-class="['w-full h-full', background.size, background.position]"
/>
<LazyImage
v-if="background.mobileImg"
class="w-full h-full md:hidden"
v-bind="background.mobileImg"
:img-class="['w-full h-full', background.size, background.position]"
/>
</div>
<div
:class="[
'z-10',
mergedTheme.content,
extendedPadding
? ['py-20 lg:pt-240px lg:pb-120px']
: ['py-120px lg:py-140px'],
inset ? 'col-span-10 col-start-2' : 'col-span-12',
]"
>
<div v-if="title" v-transition class="grid grid-cols-12 z-10">
<transition name="slide" appear>
<h2
:class="['col-span-12 section-title', mergedTheme.text]"
:itemprop="mergedTheme.textMeta"
>
<span
v-for="(t, i) in splitTitle"
:key="i"
class="anim"
:style="{
'animation-delay': i * 0.06 + 's',
}"
v-html="t"
/>
</h2>
</transition>
</div>
<slot></slot>
<div v-if="children">
<div v-for="(child, index) in children" :key="index" class="z-10">
<hr
:class="[
children.length > 2 && index === 0 ? 'my-10' : 'my-10 lg:mb-16',
mergedTheme.hr,
{ hidden: !title && index === 0 },
{ hidden: child.hideDivider },
]"
/>
<component :is="child.component" v-bind="child"></component>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import BackgroundVideo from '@theme/components/base/BackgroundVideo.vue'
import ColumnText from '@theme/components/ColumnText.vue'
import Section from '@theme/components/Section.vue'
import Column from '@theme/components/base/Column.vue'
import Container from '@theme/components/base/Container.vue'
import LazyImage from '@theme/components/base/LazyImage.vue'
import TextBlock from '@theme/components/TextBlock.vue'
export default {
name: 'Section',
components: {
BackgroundVideo,
ColumnText,
Column,
Container,
LazyImage,
Section,
TextBlock,
},
props: {
background: {
type: Object,
default: function () {
return { type: 'gradient', gradient: 'bg-gradient-1' }
},
},
children: {
type: Array,
default: function () {
return []
},
},
extendedPadding: {
type: Boolean,
default: false,
},
inset: {
type: Boolean,
default: false,
},
title: {
type: String,
default: null,
},
theme: {
type: Object,
default: () => ({}),
},
},
computed: {
splitTitle() {
return this.title.split(' ').map((s) => s + '&nbsp;')
},
mergedTheme() {
switch (this.background.type) {
case 'gradient': {
return {
...{
background: this.background.gradient,
content: '',
text: 'text-white type-h1 lg:col-span-7',
hr: 'hr-gradient',
},
...this.theme,
}
}
case 'transparent': {
return {
...{
background: 'bg-transparent',
content: '',
text: 'text-deepBlue type-h1 lg:col-span-7',
hr: 'hr-gradient',
},
...this.theme,
}
}
case 'image': {
return {
...{
background: 'relative',
content: '',
text: 'text-white type-h1 lg:col-span-7',
hr: 'hr-transparent',
},
...this.theme,
}
}
case 'video': {
return {
...{
background:
'relative bg-gradient-2 video-min-height flex items-center',
content: 'relative pointer-events-none',
text: 'text-white type-h1 lg:col-span-7',
hr: 'hr-transparent',
},
...this.theme,
}
}
default: {
return {
...{
background: '',
content: '',
text: '',
hr: '',
},
...this.theme,
}
}
}
},
},
}
</script>
<style lang="postcss">
@keyframes translate-slide-up {
0% {
transform: translateY(25%);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
.section-title .anim {
display: inline-block;
float: left;
animation: translate-slide-up 0.75s cubic-bezier(0.5, 0, 0, 1) both;
}
@screen lg {
.video-min-height {
min-height: 40vh;
}
}
</style>

View File

@@ -1,53 +0,0 @@
<template>
<div :class="[theme === 'dark' ? 'text-deepBlue' : 'text-white']">
<LazyImage v-if="image" class="image--eyebrow mb-8" v-bind="image" />
<p v-else-if="eyebrow" class="type-p3 mb-4">{{ eyebrow }}</p>
<p class="type-h4 mb-8" v-html="text" />
<CTA
v-if="cta && cta.item"
link-color="text-white border-b border-transparent hover:border-white"
:icon="cta.icon"
:icon-position="cta.iconPosition"
:item="cta.item"
:type="theme === 'dark' ? 'btn-outline' : 'link'"
/>
</div>
</template>
<script>
import CTA from '@theme/components/CTA.vue'
import LazyImage from '@theme/components/base/LazyImage.vue'
export default {
name: 'TextBlock',
components: { CTA, LazyImage },
props: {
eyebrow: {
type: String,
default: null,
},
text: {
type: String,
required: true,
},
theme: {
type: String,
default: 'dark',
},
cta: {
type: Object,
default: null,
},
image: {
type: Object,
default: null,
},
},
}
</script>
<style scoped lang="postcss">
@screen md {
.image--eyebrow {
max-width: 25%;
}
}
</style>

View File

@@ -1,83 +0,0 @@
<template>
<div class="type-p1 grid-margins">
<h2 class="type-h2 mt-12 mb-12">Typography</h2>
<div class="grid md:grid-cols-4 gap-8 mt-12">
<div>
<h1 class="type-h1">Type style 1</h1>
<h2 class="type-h2">Type style 2</h2>
<h3 class="type-h3">Type style 3</h3>
<h4 class="type-h4">Type style 4</h4>
<h5 class="type-h5 mb-12">Type style 5</h5>
</div>
<ul class="list-inside list-disc">
<li>Unordered list items</li>
<li>Unordered list items</li>
<li>Unordered list items</li>
</ul>
<ol class="list-inside list-decimal">
<li>Ordered list items</li>
<li>Ordered list items</li>
<li>Ordered list items</li>
</ol>
</div>
<div class="grid md:grid-cols-4 gap-8">
<div>
<h3 class="type-h5">P1</h3>
<p class="type-p1">
Lorem, ipsum dolor sit amet consectetur adipisicing elit.
<a>Soluta quibusdam aut incidunt tempore</a> assumenda eos commodi
quia ipsam eveniet perferendis delectus, magni praesentium quas.
Debitis mollitia quas perspiciatis vel quidem.
</p>
</div>
<div>
<h3 class="type-h5">P2</h3>
<p class="type-p2">
Lorem, ipsum dolor sit amet consectetur adipisicing elit.
<a>Soluta quibusdam aut incidunt tempore</a> assumenda eos commodi
quia ipsam eveniet perferendis delectus, magni praesentium quas.
Debitis mollitia quas perspiciatis vel quidem.
</p>
</div>
<div>
<h3 class="type-h5">P3</h3>
<p class="type-p3">
Lorem, ipsum dolor sit amet consectetur adipisicing elit.
<a>Soluta quibusdam aut incidunt tempore</a> assumenda eos commodi
quia ipsam eveniet perferendis delectus, magni praesentium quas.
Debitis mollitia quas perspiciatis vel quidem.
</p>
</div>
<div>
<h3 class="type-h5">P4</h3>
<p class="type-p4">
Lorem, ipsum dolor sit amet consectetur adipisicing elit.
<a>Soluta quibusdam aut incidunt tempore</a> assumenda eos commodi
quia ipsam eveniet perferendis delectus, magni praesentium quas.
Debitis mollitia quas perspiciatis vel quidem.
</p>
</div>
</div>
<h2 class="type-h2 mt-12 mb-12">Buttons</h2>
<div class="grid md:grid-cols-4 gap-8 mb-12">
<div v-for="(button, index) in buttons" :key="index">
<CTA v-bind="button" />
</div>
</div>
</div>
</template>
<script>
import CTA from '@theme/components/CTA.vue'
export default {
name: 'Typography',
components: { CTA },
props: {
buttons: {
type: Array,
default: () => [],
},
},
}
</script>

View File

@@ -1,81 +0,0 @@
<template>
<div class="relative">
<video
ref="videoElement"
:class="classList"
muted
preload
playsinline
loop
:poster="poster"
>
<source
v-for="(src, type, index) in srcset"
:key="index"
:src="requireAsset(src)"
:type="type"
/>
</video>
<button
aria-label="Toggle Video Play and Pause"
class="absolute z-10"
style="right: 20px; bottom: 20px"
@click="togglePlayPause"
>
<SVGIcon
:class-list="['h-4', 'w-4']"
:name="videoBeingPlayed ? 'pause' : 'play'"
:title="videoBeingPlayed ? 'Pause Video' : 'Play Video'"
/>
</button>
</div>
</template>
<script>
import SVGIcon from '@theme/components/base/SVGIcon.vue'
import requireAsset from '@theme/components/mixins/requireAsset'
let player = null
export default {
name: 'BackgroundVideo',
components: { SVGIcon },
mixins: [requireAsset],
props: {
classList: {
type: String,
default: '',
},
poster: {
type: String,
default: '',
},
srcset: {
type: Object,
required: true,
},
},
data: function () {
return { videoBeingPlayed: false }
},
mounted: function () {
player = this
if (!window.matchMedia('prefers-reduced-motion: reduce)').matches) {
this.playVideo()
}
},
methods: {
togglePlayPause: function () {
return player.videoBeingPlayed ? this.pauseVideo() : this.playVideo()
},
playVideo: function () {
this.$refs.videoElement.play()
player.videoBeingPlayed = true
},
pauseVideo: function () {
this.$refs.videoElement.pause()
player.videoBeingPlayed = false
},
},
}
</script>

View File

@@ -1,47 +0,0 @@
<template>
<div v-transition class="column">
<div class="flex justify-between flex-wrap">
<component
:is="block.component"
v-for="(block, index) in blocks"
:key="index"
v-bind="block"
:component-index="index"
class="w-full mb-20 lg:mb-0 last:mb-0"
:class="computedClasses"
></component>
</div>
</div>
</template>
<script>
import ColumnText from '@theme/components/ColumnText.vue'
export default {
name: 'Column',
components: { ColumnText },
props: {
cols: { type: Number, default: 1 },
blocks: { type: Array, required: true },
},
computed: {
computedClasses() {
const classes = [`col-${this.cols}`]
return classes
},
},
}
</script>
<style scoped lang="postcss">
@screen lg {
.col-2 {
width: calc(40% - 32px);
}
.col-3 {
width: calc(30% - 32px);
}
}
</style>

View File

@@ -1,24 +0,0 @@
<template>
<div class="grid-margins grid grid-cols-12 gap-2 lg:gap-8">
<component :is="child.component" v-bind="child"></component>
</div>
</template>
<script>
// Add as needed
import Column from '@theme/components/base/Column.vue'
import TextBlock from '@theme/components/TextBlock.vue'
export default {
name: 'Container',
components: {
Column,
TextBlock,
},
props: {
child: {
type: Object,
default: () => ({}),
},
},
}
</script>

View File

@@ -14,7 +14,7 @@
</template>
<script>
import withBase from '@theme/components/mixins/requireAsset'
import withBase from '@theme/components/mixins/withBase'
export default {
name: 'LazyImage',

View File

@@ -0,0 +1,8 @@
export default {
methods: {
withBase: function (path = '') {
const { $withBase } = this.$root
return path.charAt(0) === '/' ? $withBase.call(this, path) : path
},
},
}

View File

@@ -1,39 +0,0 @@
<template>
<Layout>
<Section
title="Pages"
:component-index="0"
:background="{ type: 'gradient', gradient: 'bg-gradient-2' }"
/>
<div class="container grid-margins py-10 type-rich">
<ul>
<li v-for="page in $pagination.pages" :key="page.title">
<router-link class="page-link" :to="page.path">{{
page.title
}}</router-link>
</li>
</ul>
<div>
<router-link v-if="$pagination.hasPrev" :to="$pagination.prevLink"
>Prev</router-link
>
<router-link v-if="$pagination.hasNext" :to="$pagination.nextLink"
>Next</router-link
>
</div>
</div>
</Layout>
</template>
<script>
import Layout from '@theme/layouts/Layout.vue'
import Section from '@theme/components/Section.vue'
export default {
name: 'DirectoryPagination',
components: {
Layout,
Section,
},
}
</script>

View File

@@ -1,22 +1,13 @@
<template>
<div>
<slot name="header"></slot>
<DynamicContent
v-if="$page.frontmatter.body"
:content="$page.frontmatter"
/>
<slot></slot>
<slot name="footer"></slot>
</div>
</template>
<script>
import DynamicContent from '@theme/components/DynamicContent.vue'
export default {
name: 'Layout',
components: {
DynamicContent,
},
}
</script>

View File

@@ -1,27 +0,0 @@
<template>
<div>
<Section
:title="$page.title"
:component-index="0"
:background="{ type: 'gradient', gradient: 'bg-gradient-4' }"
/>
<div class="grid-margins">
<Content class="simple-page type-rich my-10 max-w-3xl" />
</div>
</div>
</template>
<script>
import Section from '@theme/components/Section'
export default {
components: {
Section,
},
}
</script>
<style lang="postcss">
.simple-page.type-rich blockquote {
@apply text-lg leading-normal pl-6;
}
</style>

View File

@@ -1,25 +0,0 @@
<template>
<div>
<div v-for="(block, index) in $page.frontmatter.body" :key="index">
<h1 v-if="block.showTitle" class="type-h2 mt-12 mb-12 grid-margins">
{{ block.component }}
</h1>
<component
:is="block.component"
v-bind="block"
:component-index="index"
></component>
</div>
</div>
</template>
<script>
import { components } from '@theme/components/DynamicContent.vue'
export default {
name: 'StyleGuide',
components: {
...components,
},
}
</script>

View File

@@ -1,22 +0,0 @@
<template>
<Layout>
<ul>
<li v-for="tag in $tag.list" :key="tag.name">
<router-link class="page-link" :to="tag.path">{{
tag.name
}}</router-link>
</li>
</ul>
</Layout>
</template>
<script>
import Layout from '@theme/layouts/Layout.vue'
export default {
name: 'TagIndex',
components: {
Layout,
},
}
</script>

View File

@@ -1,22 +0,0 @@
<template>
<Layout>
<ul>
<li v-for="tag in $tag.list" :key="tag.name">
<router-link class="page-link" :to="tag.path">{{
tag.name
}}</router-link>
</li>
</ul>
</Layout>
</template>
<script>
import Layout from '@theme/layouts/Layout.vue'
export default {
name: 'TagItem',
components: {
Layout,
},
}
</script>