Move important parts of 'FollowList' to sub component 'ProfileList'

This commit is contained in:
Wolfgang Huß 2022-09-12 08:26:10 +02:00
parent a9376cb979
commit b7be7018e8
2 changed files with 169 additions and 136 deletions

View File

@ -1,64 +1,28 @@
<template> <template>
<base-card class="follow-list"> <profile-list
<template v-if="connections && connections.length"> :uniqueName="`${type}Filter`"
<h5 class="title spacer-x-small"> :title="$filters.truncate(userName, 15) + ' ' + $t(`profile.network.${type}`)"
{{ userName | truncate(15) }} {{ $t(`profile.network.${type}`) }} :titleNobody="$filters.truncate(userName, 15) + ' ' + $t(`profile.network.${type}Nobody`)"
</h5> :allConnectionsCount="allConnectionsCount"
<ul :class="connectionsClass"> :connections="connections"
<li :loading="loading"
v-for="connection in filteredConnections" @fetchAllConnections="$emit('fetchAllConnections', type)"
:key="connection.id" />
class="connections__item"
>
<user-teaser :user="connection" />
</li>
</ul>
<base-button
v-if="hasMore"
:loading="loading"
class="spacer-x-small"
size="small"
@click="$emit('fetchAllConnections', type)"
>
{{
$t('profile.network.andMore', {
number: allConnectionsCount - connections.length,
})
}}
</base-button>
<ds-input
v-if="!hasMore"
:name="`${type}Filter`"
:placeholder="filter"
class="spacer-x-small"
icon="filter"
size="small"
@input.native="setFilter"
/>
</template>
<p v-else class="nobody-message">{{ userName }} {{ $t(`profile.network.${type}Nobody`) }}</p>
</base-card>
</template> </template>
<script> <script>
import { escape } from 'xregexp/xregexp-all.js' import ProfileList from '~/components/features/ProfileList/ProfileList'
import UserTeaser from '~/components/UserTeaser/UserTeaser'
export default { export default {
name: 'FollowerList', name: 'FollowerList',
components: { components: {
UserTeaser, ProfileList,
}, },
props: { props: {
user: { type: Object, default: null }, user: { type: Object, default: null },
type: { type: String, default: 'following' }, type: { type: String, default: 'following' },
loading: { type: Boolean, default: false }, loading: { type: Boolean, default: false },
}, },
data() {
return {
filter: null,
}
},
computed: { computed: {
userName() { userName() {
const { name } = this.user || {} const { name } = this.user || {}
@ -70,95 +34,6 @@ export default {
connections() { connections() {
return this.user[this.type] return this.user[this.type]
}, },
hasMore() {
return this.allConnectionsCount > this.connections.length
},
connectionsClass() {
return `connections${this.hasMore ? '' : ' --overflow'}`
},
filteredConnections() {
if (!this.filter) {
return this.connections
}
// @example
// this.filter = 'foo';
// fuzzyExpression = /([^f]*f)([^o]*o)([^o]*o)/i
const fuzzyExpression = new RegExp(
`${this.filter.split('').reduce((expr, c) => `${expr}([^${escape(c)}]*${escape(c)})`, '')}`,
'i',
)
const fuzzyScores = this.connections
.map((user) => {
const match = user.name.match(fuzzyExpression)
if (!match) {
return false
}
let score = 1
for (let i = 1; i <= this.filter.length; i++) {
score *= match[i].length
}
return {
user,
score,
}
})
.filter(Boolean)
.sort((a, b) => a.score - b.score)
return fuzzyScores.map((score) => score.user)
},
},
methods: {
setFilter(evt) {
this.filter = evt.target.value
},
}, },
} }
</script> </script>
<style lang="scss">
.follow-list {
display: flex;
flex-direction: column;
position: relative;
width: auto;
> .title {
color: $text-color-soft;
font-size: $font-size-base;
}
.connections {
height: $size-height-connections;
padding: $space-none;
list-style-type: none;
&.--overflow {
overflow-y: auto;
}
> .connections__item {
padding: $space-xx-small;
&.is-selected,
&:hover {
background-color: $background-color-primary-inverse;
}
}
}
.nobody-message {
text-align: center;
color: $text-color-soft;
}
> :nth-child(n):not(:last-child) {
margin-bottom: $space-small;
}
}
</style>

View File

@ -0,0 +1,158 @@
<template>
<base-card class="follow-list">
<template v-if="connections && connections.length">
<h5 class="title spacer-x-small">
{{ title }}
</h5>
<ul :class="connectionsClass">
<li
v-for="connection in filteredConnections"
:key="connection.id"
class="connections__item"
>
<user-teaser :user="connection" />
</li>
</ul>
<base-button
v-if="hasMore"
:loading="loading"
class="spacer-x-small"
size="small"
@click="$emit('fetchAllConnections')"
>
{{
$t('profile.network.andMore', {
number: allConnectionsCount - connections.length,
})
}}
</base-button>
<ds-input
v-if="!hasMore"
:name="uniqueName"
:placeholder="filter"
class="spacer-x-small"
icon="filter"
size="small"
@input.native="setFilter"
/>
</template>
<p v-else class="nobody-message">{{ titleNobody }}</p>
</base-card>
</template>
<script>
import { escape } from 'xregexp/xregexp-all.js'
import UserTeaser from '~/components/UserTeaser/UserTeaser'
export default {
name: 'ProfileList',
components: {
UserTeaser,
},
props: {
uniqueName: { type: String, required: true },
title: { type: String, required: true },
titleNobody: { type: String, required: true },
allConnectionsCount: { type: Number, required: true },
connections: { type: Array, required: true },
type: { type: String, default: 'following' },
loading: { type: Boolean, default: false },
},
data() {
return {
filter: null,
}
},
computed: {
hasMore() {
return this.allConnectionsCount > this.connections.length
},
connectionsClass() {
return `connections${this.hasMore ? '' : ' --overflow'}`
},
filteredConnections() {
if (!this.filter) {
return this.connections
}
// @example
// this.filter = 'foo';
// fuzzyExpression = /([^f]*f)([^o]*o)([^o]*o)/i
const fuzzyExpression = new RegExp(
`${this.filter.split('').reduce((expr, c) => `${expr}([^${escape(c)}]*${escape(c)})`, '')}`,
'i',
)
const fuzzyScores = this.connections
.map((user) => {
const match = user.name.match(fuzzyExpression)
if (!match) {
return false
}
let score = 1
for (let i = 1; i <= this.filter.length; i++) {
score *= match[i].length
}
return {
user,
score,
}
})
.filter(Boolean)
.sort((a, b) => a.score - b.score)
return fuzzyScores.map((score) => score.user)
},
},
methods: {
setFilter(evt) {
this.filter = evt.target.value
},
},
}
</script>
<style lang="scss">
.follow-list {
display: flex;
flex-direction: column;
position: relative;
width: auto;
> .title {
color: $text-color-soft;
font-size: $font-size-base;
}
.connections {
height: $size-height-connections;
padding: $space-none;
list-style-type: none;
&.--overflow {
overflow-y: auto;
}
> .connections__item {
padding: $space-xx-small;
&.is-selected,
&:hover {
background-color: $background-color-primary-inverse;
}
}
}
.nobody-message {
text-align: center;
color: $text-color-soft;
}
> :nth-child(n):not(:last-child) {
margin-bottom: $space-small;
}
}
</style>