Skip to content

Commit

Permalink
Move AutoCompleteResult to dedicated component
Browse files Browse the repository at this point in the history
Signed-off-by: John Molakvoæ (skjnldsv) <[email protected]>
  • Loading branch information
skjnldsv committed Oct 6, 2020
1 parent 3e35ecc commit 6904aaf
Show file tree
Hide file tree
Showing 5 changed files with 473 additions and 121 deletions.
8 changes: 6 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,12 @@
"core-js": "^3.6.5",
"debounce": "1.2.0",
"emoji-mart-vue-fast": "^7.0.4",
"escape-html": "^1.0.3",
"hammerjs": "^2.0.8",
"linkifyjs": "~2.1.9",
"md5": "^2.2.1",
"regenerator-runtime": "^0.13.5",
"striptags": "^3.1.1",
"tributejs": "^5.1.3",
"v-click-outside": "^3.0.1",
"v-tooltip": "^2.0.3",
Expand Down
180 changes: 180 additions & 0 deletions src/components/RichContenteditable/AutoCompleteResult.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
<!--
- @copyright Copyright (c) 2020 John Molakvoæ <skjnldsv@protonmail.com>
-
- @author John Molakvoæ <[email protected]>
-
- @license GNU AGPL version 3 or any later version
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-->

<template>
<div class="autocomplete-result">
<!-- Avatar or icon -->
<div :class="[icon, `autocomplete-result__icon--${avatarUrl ? 'with-avatar' : ''}`]"
:style="{ backgroundImage: `url(${avatarUrl})` }"
class="autocomplete-result__icon">
<div v-if="haveStatus"
:class="[`autocomplete-result__status--${status && status.icon ? 'icon' : status.status}`]"
class="autocomplete-result__status">
{{ status && status.icon || '' }}
</div>
</div>

<!-- Title and subtitle -->
<span class="autocomplete-result__content">
<span class="autocomplete-result__title">
{{ label }}
</span>
<span v-if="subline" class="autocomplete-result__subline">
{{ subline }}
</span>
</span>
</div>
</template>

<script>
import { generateUrl } from '@nextcloud/router'

export default {
name: 'AutoCompleteResult',

props: {
label: {
type: String,
required: true,
},
subline: {
type: String,
default: null,
},
id: {
type: String,
default: null,
},
icon: {
type: String,
required: true,
},
source: {
type: String,
required: true,
},
status: {
type: [Object, Array],
default: () => ({}),
},
},
computed: {
avatarUrl() {
return this.id && this.source === 'users'
? this.getAvatarUrl(this.id, 44)
: null
},
haveStatus() {
return this.status?.icon || this.status?.status
},
},

methods: {
getAvatarUrl(user, size) {
return generateUrl('/avatar/{user}/{size}', {
user,
size,
})
},
},
}
</script>

<style lang="scss" scoped>
@import '../../fonts/scss/iconfont-vue';

$autocomplete-padding: 10px;

.autocomplete-result {
display: flex;
height: $clickable-area;
padding: $autocomplete-padding;

.highlight & {
color: var(--color-main-text);
background: var(--color-primary-light);
&, * {
cursor: pointer;
}
}

&__icon {
position: relative;
width: $clickable-area;
height: $clickable-area;
border-radius: $clickable-area;
background-color: var(--color-background-darker);
background-repeat: no-repeat;
background-position: center;
background-size: $clickable-area - 2 * $autocomplete-padding;
&--with-avatar {
color: inherit;
background-size: cover;
}
}

&__status {
position: absolute;
right: -4px;
bottom: -4px;
box-sizing: border-box;
width: 18px;
height: 18px;
border: 2px solid var(--color-main-background);
border-radius: 50%;
background-color: var(--color-main-background);
font-size: 14px;
line-height: 14px;
&--online {
color: #49b382;

@include iconfont('user-status-online');
}
&--dnd {
color: #ed484c;
background-color: #fff;

@include iconfont('user-status-dnd');
}
&--away {
color: #f4a331;

@include iconfont('user-status-away');
}
&--icon {
border: none;
background-color: transparent;
}
}

&__content {
display: flex;
flex: 1 1;
flex-direction: column;
justify-content: center;
padding-left: $autocomplete-padding;
}

&__subline {
color: var(--color-text-lighter);
}
}
</style>
150 changes: 150 additions & 0 deletions src/components/RichContenteditable/MentionBubble.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
<!--
- @copyright Copyright (c) 2020 John Molakvoæ <skjnldsv@protonmail.com>
-
- @author John Molakvoæ <[email protected]>
-
- @license GNU AGPL version 3 or any later version
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-->

<template>
<span :class="{'mention-bubble--primary': primary}"
class="mention-bubble"
contenteditable="false">
<div class="mention-bubble__wrapper">
<div class="mention-bubble__content">
<!-- Avatar or icon -->
<div :class="[icon, `mention-bubble__icon--${avatarUrl ? 'with-avatar' : ''}`]"
:style="{ backgroundImage: `url(${avatarUrl})` }"
class="mention-bubble__icon" />

<!-- Title -->
<h6 class="mention-bubble__title" :title="label"></h6>

<span class="mention-bubble__select">{{ mentionText }}</span>
</div>
</div>
</span>
</template>

<script>
import { generateUrl } from '@nextcloud/router'

export default {
name: 'MentionBubble',

props: {
id: {
type: String,
required: true,
},
label: {
type: String,
required: true,
},
icon: {
type: String,
required: true,
},
source: {
type: String,
required: true,
},
primary: {
type: Boolean,
default: false,
},
},
computed: {
avatarUrl() {
return this.id && this.source === 'users'
? this.getAvatarUrl(this.id, 44)
: null
},
mentionText() {
return this.id.indexOf(' ') === -1
? `@${this.id}`
: `@"${this.id}"`
},
},

methods: {
getAvatarUrl(user, size) {
return generateUrl('/avatar/{user}/{size}', {
user,
size,
})
},
},
}
</script>

<style lang="scss" scoped>
$bubble-size: 20px;
$bubble-padding: 2px;
$bubble-avatar-size: $bubble-size - 2 * $bubble-padding;

.mention-bubble {
&__wrapper {
display: inline-block;
vertical-align: middle;
white-space: nowrap;
// Align vertically with nearby text
line-height: 16px;
}

&__content {
display: inline-flex;
align-items: center;
height: $bubble-size;
padding-right: $bubble-padding * 3;
padding-left: $bubble-padding;
border-radius: $bubble-size / 2;
background-color: var(--color-background-dark);
}

&__icon {
position: relative;
width: $bubble-avatar-size;
height: $bubble-avatar-size;
border-radius: $bubble-avatar-size / 2;
background-color: var(--color-background-darker);
background-repeat: no-repeat;
background-position: center;
background-size: $bubble-avatar-size - 2 * $bubble-padding;

&--with-avatar {
color: inherit;
background-size: cover;
}
}

&__title {
margin-left: $bubble-padding;
// Put label in ::before so it is not selectable
&::before {
content: attr(title);
}
}

// Hide the mention id so it is selectable
&__select {
position: absolute;
z-index: -1;
left: -1000px;
}
}

</style>
Loading

0 comments on commit 6904aaf

Please sign in to comment.