Skip to content

Commit

Permalink
feat: rewrite skeleton loading component
Browse files Browse the repository at this point in the history
Signed-off-by: Luka Trovic <[email protected]>
  • Loading branch information
luka-nextcloud committed Jul 3, 2023
1 parent b5adb99 commit 3af5b71
Show file tree
Hide file tree
Showing 5 changed files with 248 additions and 27 deletions.
12 changes: 4 additions & 8 deletions src/components/Collective.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,7 @@
<NcAppContentDetails>
<Version v-if="currentPage && version" />
<Page v-else-if="currentPage" />
<NcEmptyContent v-else-if="loading('collective') || loading('page')">
<template #icon>
<NcLoadingIcon />
</template>
</NcEmptyContent>
<SkeletonLoading v-else-if="loading('collective') || loading('page')" :count="1" type="page-heading" />
<PageNotFound v-else />
</NcAppContentDetails>
</template>
Expand All @@ -15,21 +11,21 @@
import { mapActions, mapGetters, mapMutations } from 'vuex'
import { emit, subscribe, unsubscribe } from '@nextcloud/event-bus'
import { listen } from '@nextcloud/notify_push'
import { NcAppContentDetails, NcEmptyContent, NcLoadingIcon } from '@nextcloud/vue'
import { NcAppContentDetails } from '@nextcloud/vue'
import { GET_PAGES, GET_TRASH_PAGES } from '../store/actions.js'
import { SELECT_VERSION } from '../store/mutations.js'
import displayError from '../util/displayError.js'
import Page from './Page.vue'
import Version from './Page/Version.vue'
import PageNotFound from './Page/PageNotFound.vue'
import SkeletonLoading from './SkeletonLoading.vue'
export default {
name: 'Collective',
components: {
SkeletonLoading,
NcAppContentDetails,
NcEmptyContent,
NcLoadingIcon,
Page,
PageNotFound,
Version,
Expand Down
14 changes: 5 additions & 9 deletions src/components/Navigation.vue
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
<template>
<NcAppNavigation>
<template v-if="loading('collectives')" #default>
<NcEmptyContent>
<template #icon>
<NcLoadingIcon />
</template>
</NcEmptyContent>
<SkeletonLoading type="items" :count="3" />
</template>
<template #list>
<template v-if="!loading('collectives')" #list>
<NcAppNavigationCaption :title="t('collectives', 'Select a collective')" />
<CollectiveListItem v-for="collective in collectives"
v-show="!collective.deleted"
Expand Down Expand Up @@ -36,13 +32,14 @@
import { mapActions, mapGetters } from 'vuex'
import { subscribe, unsubscribe } from '@nextcloud/event-bus'
import { RESTORE_COLLECTIVE, DELETE_COLLECTIVE } from '../store/actions.js'
import { NcAppNavigation, NcAppNavigationCaption, NcButton, NcEmptyContent, NcLoadingIcon } from '@nextcloud/vue'
import { NcAppNavigation, NcAppNavigationCaption, NcButton } from '@nextcloud/vue'
import NewCollectiveModal from './Nav/NewCollectiveModal.vue'
import CollectiveListItem from './Nav/CollectiveListItem.vue'
import CollectivesGlobalSettings from './Nav/CollectivesGlobalSettings.vue'
import CollectivesTrash from './Nav/CollectivesTrash.vue'
import PlusIcon from 'vue-material-design-icons/Plus.vue'
import displayError from '../util/displayError.js'
import SkeletonLoading from './SkeletonLoading.vue'
export default {
name: 'Navigation',
Expand All @@ -55,8 +52,7 @@ export default {
CollectiveListItem,
CollectivesGlobalSettings,
CollectivesTrash,
NcEmptyContent,
NcLoadingIcon,
SkeletonLoading,
PlusIcon,
},
Expand Down
11 changes: 3 additions & 8 deletions src/components/Page/RichText.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,7 @@
:value="shareTokenParam">
<div id="text" class="editor">
<PageInfoBar :current-page="currentPage" />
<NcEmptyContent v-if="loading('pageContent')">
<template #icon>
<NcLoadingIcon />
</template>
</NcEmptyContent>
<SkeletonLoading v-if="loading('pageContent')" type="text" />
<RichTextReader v-else
:content="pageContent"
@click-link="followLink" />
Expand All @@ -23,8 +19,8 @@ import { mapGetters, mapMutations } from 'vuex'
import { RichTextReader, AttachmentResolver, ATTACHMENT_RESOLVER, OUTLINE_STATE, OUTLINE_ACTIONS } from '@nextcloud/text'
import { getCurrentUser } from '@nextcloud/auth'
import { generateUrl } from '@nextcloud/router'
import { NcEmptyContent, NcLoadingIcon } from '@nextcloud/vue'
import PageInfoBar from './PageInfoBar.vue'
import SkeletonLoading from '../SkeletonLoading.vue'
const resolvePath = function(from, rel) {
if (!rel) {
Expand All @@ -49,8 +45,7 @@ export default {
name: 'RichText',
components: {
NcEmptyContent,
NcLoadingIcon,
SkeletonLoading,
PageInfoBar,
RichTextReader,
},
Expand Down
6 changes: 4 additions & 2 deletions src/components/PageList.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<template>
<NcAppContentList :class="{loading: loading('collective') || loading('pagelist')}"
:show-details="showing('details')">
<NcAppContentList :show-details="showing('details')">
<div class="page-list-headerbar">
<NcTextField name="pageFilter"
:value.sync="filterString"
Expand Down Expand Up @@ -54,6 +53,7 @@
</NcActionButton>
</NcActions>
</div>
<SkeletonLoading v-if="loading('collective') || loading('pagelist')" type="items" :count="3" />
<div v-if="currentCollective && collectivePage" class="page-list">
<Item key="Readme"
:to="currentCollectivePath"
Expand Down Expand Up @@ -126,11 +126,13 @@ import PagesTemplateIcon from './Icon/PagesTemplateIcon.vue'
import { SET_COLLECTIVE_USER_SETTING_PAGE_ORDER } from '../store/actions.js'
import { scrollToPage } from '../util/scrollToElement.js'
import { pageOrders } from '../util/sortOrders.js'
import SkeletonLoading from './SkeletonLoading.vue'
export default {
name: 'PageList',
components: {
SkeletonLoading,
NcActions,
NcActionButton,
NcAppContentList,
Expand Down
232 changes: 232 additions & 0 deletions src/components/SkeletonLoading.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
<template>
<div class="placeholder-main" :class="'placeholder-main-' + type">
<!-- Placeholder animation -->
<template v-for="(suffix, gradientIndex) in ['-regular', '-reverse']">
<svg :key="'gradient' + suffix" :class="'placeholder-gradient placeholder-gradient' + suffix">
<defs>
<linearGradient :id="'placeholder-gradient' + suffix">
<stop offset="0%" :stop-color="(gradientIndex === 0) ? colorPlaceholderLight : colorPlaceholderDark" />
<stop offset="100%" :stop-color="(gradientIndex === 0) ? colorPlaceholderDark : colorPlaceholderLight" />
</linearGradient>
</defs>
</svg>

<ul :key="'list' + suffix" :class="'placeholder-list placeholder-list' + suffix">
<li v-for="(width, index) in placeholderData" :key="'placeholder' + suffix + index">
<svg v-if="type === 'items'"
class="items-placeholder"
xmlns="http://www.w3.org/2000/svg"
:fill="'url(#placeholder-gradient' + suffix + ')'">
<circle class="items-placeholder-icon" />
<rect class="items-placeholder-line-one" :style="width" />
</svg>
<svg v-if="type === 'page-heading'"
class="page-heading-placeholder"
xmlns="http://www.w3.org/2000/svg"
:fill="'url(#placeholder-gradient' + suffix + ')'">
<circle class="page-heading-placeholder-icon" />
<rect class="page-heading-placeholder-line-one" :style="width" />
<rect class="page-heading-placeholder-line-two" />
</svg>
<svg v-if="type === 'text'"
class="text-placeholder"
xmlns="http://www.w3.org/2000/svg"
:fill="'url(#placeholder-gradient' + suffix + ')'">
<rect class="text-placeholder-line-one" />
<rect class="text-placeholder-line-two" />
<rect class="text-placeholder-line-three" />
<rect class="text-placeholder-line-four" :style="width" />
</svg>
</li>
</ul>
</template>
</div>
</template>

<script>
const bodyStyles = window.getComputedStyle(document.body)
const colorPlaceholderDark = bodyStyles.getPropertyValue('--color-placeholder-dark')
const colorPlaceholderLight = bodyStyles.getPropertyValue('--color-placeholder-light')
export default {
name: 'SkeletonLoading',
props: {
type: {
type: String,
required: true,
},
count: {
type: Number,
default: 5,
},
},
setup() {
return {
colorPlaceholderDark,
colorPlaceholderLight,
}
},
computed: {
placeholderData() {
const data = []
for (let i = 0; i < this.count; i++) {
// generate random widths
data.push('width: ' + (Math.floor(Math.random() * 40) + 50) + '%')
}
return data
},
},
}
</script>

<style lang="scss" scoped>
$clickable-area: 44px;
$margin: 8px;
$messages-list-max-width: 670px;
.placeholder-main {
max-width: $messages-list-max-width;
position: relative;
margin-bottom: auto;
&-text,
&-page-heading {
margin: auto;
}
}
.placeholder-list {
position: absolute;
transform: translateZ(0);
}
.placeholder-list-regular {
animation: pulse 2s;
animation-iteration-count: infinite;
animation-timing-function: linear;
}
.placeholder-list-reverse {
animation: pulse-reverse 2s;
animation-iteration-count: infinite;
animation-timing-function: linear;
}
.placeholder-gradient {
position: fixed;
height: 0;
width: 0;
z-index: -1;
}
.items-placeholder,
.text-placeholder,
.page-heading-placeholder {
&-icon {
width: $clickable-area;
height: $clickable-area;
cx: calc(#{$clickable-area} / 2);
cy: calc(#{$clickable-area} / 2);
r: calc(#{$clickable-area} / 2);
}
}
.items-placeholder {
width: calc(100% - 2 * #{$margin});
height: $clickable-area;
margin: 0 $margin;
&-line-one {
width: calc(100% - #{$margin + $clickable-area});
position: relative;
height: 1.5em;
x: $margin + $clickable-area;
y: 10px;
}
}
.page-heading-placeholder {
width: min($messages-list-max-width, 100vw);
height: calc(#{$clickable-area} * 2);
margin: $margin auto;
display: block;
&-line-one {
width: min($messages-list-max-width, 100vw);
position: relative;
height: 2em;
x: $margin + $clickable-area;
y: 8px;
}
&-line-two {
width: 30%;
position: relative;
height: 1em;
x: 0;
y: 60px;
}
}
.text-placeholder {
width: min($messages-list-max-width, 100vw);
height: calc(#{$clickable-area} * 2);
margin: $margin auto;
padding: 0 8px;
display: block;
&-line-one,
&-line-two,
&-line-three,
&-line-four {
width: 670px;
height: 1em;
}
&-line-one {
y: 5px;
width: 175px;
}
&-line-two {
y: 25px;
}
&-line-three {
y: 45px;
}
&-line-four {
y: 65px;
}
}
@keyframes pulse {
0% {
opacity: 1;
}
50% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes pulse-reverse {
0% {
opacity: 0;
}
50% {
opacity: 1;
}
100% {
opacity: 0;
}
}
</style>

0 comments on commit 3af5b71

Please sign in to comment.