feat: Improve sidebar UI, add emoji icons instead of ionicons (#1605)

Co-authored-by: Pranav <pranav@chatwoot.com>
This commit is contained in:
Nithin David Thomas 2021-01-12 14:45:10 +05:30 committed by GitHub
parent 346830ab1d
commit 764c90174e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 195 additions and 52 deletions

View file

@ -74,12 +74,10 @@ export default {
}
.conversation--details {
border-top: 1px solid $color-border-light;
padding: var(--space-normal);
padding: 0 var(--space-normal);
}
.contact-conversation--panel {
border-top: 1px solid $color-border-light;
height: 100%;
}

View file

@ -3,11 +3,14 @@
<contact-details-item
:title="$t('CONTACT_PANEL.CONVERSATIONS.TITLE')"
icon="ion-chatboxes"
emoji="💬"
/>
<div v-if="!uiFlags.isFetching">
<p v-if="!previousConversations.length" class="no-results">
{{ $t('CONTACT_PANEL.CONVERSATIONS.NO_RECORDS_FOUND') }}
</p>
<div v-if="!uiFlags.isFetching" class="contact-conversation__wrap">
<div v-if="!previousConversations.length" class="no-label-message">
<span>
{{ $t('CONTACT_PANEL.CONVERSATIONS.NO_RECORDS_FOUND') }}
</span>
</div>
<div v-else class="contact-conversation--list">
<conversation-card
v-for="conversation in previousConversations"
@ -78,11 +81,18 @@ export default {
@import '~dashboard/assets/scss/mixins';
.contact-conversation--panel {
padding: $space-normal;
padding: 0 var(--space-slab) var(--space-two);
}
.no-results {
margin: 0;
color: $color-gray;
.contact-conversation__wrap {
margin-left: var(--space-medium);
}
.no-label-message {
color: var(--b-500);
}
.conv-details--item {
padding-bottom: 0;
}
</style>

View file

@ -3,6 +3,7 @@
<contact-details-item
:title="$t('CONTACT_PANEL.CUSTOM_ATTRIBUTES.TITLE')"
icon="ion-code"
emoji="📕"
/>
<div
v-for="attribute in listOfAttributes"
@ -45,12 +46,15 @@ export default {
<style scoped>
.custom-attributes--panel {
border-top: 1px solid var(--b-100);
padding: var(--space-normal);
padding: 0 var(--space-slab) var(--space-slab);
}
.conv-details--item {
padding-bottom: 0;
}
.custom-attribute--row {
margin-bottom: var(--space-small);
margin-left: var(--space-medium);
}
.custom-attribute--row__attribute {

View file

@ -1,11 +1,15 @@
<template>
<div class="conv-details--item">
<h4 class="conv-details--item__label">
<div>
<i v-if="icon" :class="icon" class="conv-details--item__icon"></i>
<h4 class="conv-details--item__label text-block-title">
<div class="title--icon">
<emoji-or-icon :icon="icon" :emoji="emoji" />
{{ title }}
</div>
<button v-if="showEdit" @click="onEdit">
<button
v-if="showEdit"
class="button clear small edit-button"
@click="onEdit"
>
{{ $t('CONTACT_PANEL.EDIT_LABEL') }}
</button>
</h4>
@ -18,10 +22,16 @@
</template>
<script>
import EmojiOrIcon from 'shared/components/EmojiOrIcon';
export default {
components: {
EmojiOrIcon,
},
props: {
title: { type: String, required: true },
icon: { type: String, default: '' },
emoji: { type: String, default: '' },
value: { type: [String, Number], default: '' },
showEdit: { type: Boolean, default: false },
},
@ -38,29 +48,32 @@ export default {
@import '~dashboard/assets/scss/mixins';
.conv-details--item {
padding-bottom: var(--space-slab);
&:last-child {
padding-bottom: 0;
}
padding-bottom: var(--space-normal);
.conv-details--item__label {
align-items: center;
display: flex;
font-size: $font-size-small;
font-weight: $font-weight-medium;
justify-content: space-between;
margin-bottom: $space-micro;
margin-bottom: var(--space-smaller);
button {
cursor: pointer;
color: $color-body;
.edit-button {
padding: 0;
}
}
.conv-details--item__value {
word-break: break-all;
margin-top: $space-small;
margin-left: var(--space-medium);
}
.title--icon .icon--emoji,
.title--icon .icon--font {
margin-right: var(--space-small);
}
.title--icon {
display: flex;
align-items: center;
}
}
</style>

View file

@ -10,30 +10,35 @@
:title="$t('EDIT_CONTACT.FORM.LOCATION.LABEL')"
:value="location"
icon="ion-map"
emoji="📍"
/>
<contact-details-item
v-if="ipAddress"
:title="$t('CONTACT_PANEL.IP_ADDRESS')"
:value="ipAddress"
icon="ion-android-locate"
emoji="🧭"
/>
<contact-details-item
v-if="browser.browser_name"
:title="$t('CONTACT_PANEL.BROWSER')"
:value="browserName"
icon="ion-ios-world-outline"
emoji="🌐"
/>
<contact-details-item
v-if="browser.platform_name"
:title="$t('CONTACT_PANEL.OS')"
:value="platformName"
icon="ion-laptop"
emoji="💻"
/>
<contact-details-item
v-if="referer"
:title="$t('CONTACT_PANEL.INITIATED_FROM')"
:value="referer"
icon="ion-link"
emoji="🔗"
>
<a :href="referer" rel="noopener noreferrer nofollow" target="_blank">
{{ referer }}
@ -44,6 +49,7 @@
:title="$t('CONTACT_PANEL.INITIATED_AT')"
:value="initiatedAt.timestamp"
icon="ion-clock"
emoji="🕰"
/>
</div>
<contact-custom-attributes
@ -211,8 +217,7 @@ export default {
}
.conversation--details {
border-top: 1px solid $color-border-light;
padding: $space-normal;
padding: 0 var(--space-slab);
}
.conversation--labels {
@ -230,10 +235,6 @@ export default {
}
}
.contact-conversation--panel {
border-top: 1px solid $color-border-light;
}
.contact--mute {
color: $alert-color;
display: block;

View file

@ -22,6 +22,7 @@
:href="contact.email ? `mailto:${contact.email}` : ''"
:value="contact.email"
icon="ion-email"
emoji="✉️"
:title="$t('CONTACT_PANEL.EMAIL_ADDRESS')"
show-copy
/>
@ -30,24 +31,27 @@
:href="contact.phone_number ? `tel:${contact.phone_number}` : ''"
:value="contact.phone_number"
icon="ion-ios-telephone"
emoji="📞"
:title="$t('CONTACT_PANEL.PHONE_NUMBER')"
/>
<contact-info-row
v-if="additionalAttributes.location"
:value="additionalAttributes.location"
icon="ion-map"
emoji="🌍"
:title="$t('CONTACT_PANEL.LOCATION')"
/>
<contact-info-row
:value="additionalAttributes.company_name"
icon="ion-briefcase"
emoji="🏢"
:title="$t('CONTACT_PANEL.COMPANY')"
/>
</div>
</div>
<woot-button
class="expanded"
variant="hollow primary small"
class="clear edit-contact"
variant="primary small"
@click="toggleEditModal"
>
{{ $t('EDIT_CONTACT.BUTTON_LABEL') }}
@ -115,7 +119,7 @@ export default {
@import '~dashboard/assets/scss/mixins';
.contact--profile {
align-items: flex-start;
padding: $space-normal;
padding: var(--space-normal) var(--space-normal) var(--space-large);
.user-thumbnail-box {
margin-right: $space-normal;
@ -151,7 +155,7 @@ export default {
}
.contact--metadata {
margin: $space-small 0 $space-normal;
margin: var(--space-normal) 0 0;
}
.social--icons {
@ -159,4 +163,10 @@ export default {
font-size: $font-weight-normal;
}
}
.edit-contact {
padding: 0 var(--space-slab);
margin-left: var(--space-slab);
margin-top: var(--space-smaller);
}
</style>

View file

@ -1,7 +1,7 @@
<template>
<div class="contact-info--row">
<a v-if="href" :href="href" class="contact-info--details">
<i :class="icon" class="contact-info--icon" />
<emoji-or-icon :icon="icon" :emoji="emoji" />
<span v-if="value" class="text-truncate" :title="value">{{ value }}</span>
<span v-else class="text-muted">{{
$t('CONTACT_PANEL.NOT_AVAILABLE')
@ -18,7 +18,7 @@
</a>
<div v-else class="contact-info--details">
<i :class="icon" class="contact-info--icon" />
<emoji-or-icon :icon="icon" :emoji="emoji" />
<span v-if="value" class="text-truncate">{{ value }}</span>
<span v-else class="text-muted">{{
$t('CONTACT_PANEL.NOT_AVAILABLE')
@ -29,7 +29,12 @@
<script>
import copy from 'copy-text-to-clipboard';
import alertMixin from 'shared/mixins/alertMixin';
import EmojiOrIcon from 'shared/components/EmojiOrIcon';
export default {
components: {
EmojiOrIcon,
},
mixins: [alertMixin],
props: {
href: {
@ -40,6 +45,10 @@ export default {
type: String,
required: true,
},
emoji: {
type: String,
required: true,
},
value: {
type: String,
default: '',
@ -71,7 +80,7 @@ export default {
.contact-info--details {
display: flex;
align-items: center;
margin-bottom: $space-smaller;
margin-bottom: var(--space-one);
color: $color-body;
.copy-icon {
@ -87,4 +96,9 @@ export default {
}
}
}
.contact-info--details .icon--emoji,
.contact-info--details .icon--font {
margin-right: var(--space-small);
}
</style>

View file

@ -7,18 +7,21 @@
<contact-details-item
:title="$t('CONTACT_PANEL.LABELS.TITLE')"
icon="ion-pricetags"
emoji="🏷️"
:show-edit="true"
@edit="onEdit"
/>
<woot-label
v-for="label in activeLabels"
:key="label.id"
:title="label.title"
:description="label.description"
:bg-color="label.color"
/>
<div v-if="!activeLabels.length">
{{ $t('CONTACT_PANEL.LABELS.NO_AVAILABLE_LABELS') }}
<div class="label-wrap">
<woot-label
v-for="label in activeLabels"
:key="label.id"
:title="label.title"
:description="label.description"
:bg-color="label.color"
/>
<div v-if="!activeLabels.length" class="no-label-message">
<span>{{ $t('CONTACT_PANEL.LABELS.NO_AVAILABLE_LABELS') }}</span>
</div>
</div>
<add-label-to-conversation
v-if="isEditing"
@ -119,15 +122,24 @@ export default {
@import '~dashboard/assets/scss/mixins';
.contact-conversation--panel {
padding: $space-normal;
padding: var(--space-medium) var(--space-slab) var(--space-two);
}
.contact-conversation--list .conv-details--item {
padding-bottom: 0;
}
.conversation--label {
color: $color-white;
margin-right: $space-small;
font-size: $font-size-small;
padding: $space-smaller;
}
.label-wrap {
margin-left: var(--space-medium);
}
.no-label-message {
color: var(--b-500);
}
.select-tags {
.multiselect {

View file

@ -0,0 +1,47 @@
<template>
<i v-if="showWrap" :class="className">{{ iconContent }}</i>
</template>
<script>
import { hasEmojiSupport } from 'shared/helpers/emoji';
import { mapGetters } from 'vuex';
export default {
props: {
icon: { type: String, default: '' },
emoji: { type: String, default: '' },
},
computed: {
...mapGetters({ uiSettings: 'getUISettings' }),
isIconTypeEmoji() {
const { icon_type: iconType } = this.uiSettings;
return iconType === 'emoji';
},
showEmoji() {
return this.isIconTypeEmoji && this.emoji && hasEmojiSupport(this.emoji);
},
showIcon() {
return !this.showEmoji && this.icon;
},
showWrap() {
return this.showEmoji || this.showIcon;
},
iconContent() {
return this.showEmoji ? this.emoji : '';
},
className() {
return {
'icon--emoji': this.showEmoji,
'icon--font': this.showIcon,
[this.icon]: this.showIcon,
};
},
},
};
</script>
<style lang="scss" scoped>
.icon--emoji {
font-style: normal;
}
</style>

View file

@ -0,0 +1,34 @@
/**
* Detects support for emoji character sets.
*
* Based on the Modernizr emoji detection.
* https://github.com/Modernizr/Modernizr/blob/347ddb078116cee91b25b6e897e211b023f9dcb4/feature-detects/emoji.js
*
* @return {Boolean} true or false
* @example
*
* hasEmojiSupport()
* // => true|false
*/
export const hasEmojiSupport = () => {
const pixelRatio = window.devicePixelRatio || 1;
const offset = 12 * pixelRatio;
const node = document.createElement('canvas');
// canvastext support
if (
!node.getContext ||
!node.getContext('2d') ||
typeof node.getContext('2d').fillText !== 'function'
) {
return false;
}
const ctx = node.getContext('2d');
ctx.fillStyle = '#f00';
ctx.textBaseline = 'top';
ctx.font = '32px Arial';
ctx.fillText('\ud83d\udc28', 0, 0); // U+1F428 KOALA
return ctx.getImageData(offset, offset, 1, 1).data[0] !== 0;
};