Skip to content

Commit

Permalink
feat: multiple UX improvements to labels (#7358)
Browse files Browse the repository at this point in the history
Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
Co-authored-by: Nithin David Thomas <1277421+nithindavid@users.noreply.github.com>
  • Loading branch information
3 people committed Jun 25, 2023
1 parent 4f8ce7b commit 996325f
Show file tree
Hide file tree
Showing 16 changed files with 485 additions and 20 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Expand Up @@ -74,3 +74,5 @@ yalc.lock
/yarn-error.log
yarn-debug.log*
.yarn-integrity

/storybook-static
25 changes: 25 additions & 0 deletions app/javascript/dashboard/components/base/Hotkey.vue
@@ -0,0 +1,25 @@
<template>
<kbd class="hotkey">
<slot />
</kbd>
</template>

<style lang="scss">
kbd.hotkey {
display: inline-flex;
flex-shrink: 0;
align-items: center;
user-select: none;
justify-content: center;
padding: var(--space-micro);
min-width: var(--space-normal);
font-size: var(--font-size-micro);
border-radius: var(--space-smaller);
background-color: var(--color-background);
border: 1px solid var(--color-border);
text-transform: uppercase;
color: var(--color-body);
letter-spacing: var(--space-micro);
line-height: var(--font-size-micro);
}
</style>
22 changes: 21 additions & 1 deletion app/javascript/dashboard/components/widgets/LabelSelector.vue
Expand Up @@ -20,6 +20,7 @@
v-if="showSearchDropdownLabel"
:account-labels="allLabels"
:selected-labels="selectedLabels"
:allow-creation="isAdmin"
@add="addItem"
@remove="removeItem"
/>
Expand All @@ -30,16 +31,23 @@

<script>
import AddLabel from 'shared/components/ui/dropdown/AddLabel';
import eventListenerMixins from 'shared/mixins/eventListenerMixins';
import LabelDropdown from 'shared/components/ui/label/LabelDropdown';
import { mixin as clickaway } from 'vue-clickaway';
import adminMixin from 'dashboard/mixins/isAdmin';
import {
buildHotKeys,
isEscape,
isActiveElementTypeable,
} from 'shared/helpers/KeyboardHelpers';
export default {
components: {
AddLabel,
LabelDropdown,
},
mixins: [clickaway],
mixins: [clickaway, adminMixin, eventListenerMixins],
props: {
allLabels: {
Expand Down Expand Up @@ -80,6 +88,18 @@ export default {
closeDropdownLabel() {
this.showSearchDropdownLabel = false;
},
handleKeyEvents(e) {
const keyPattern = buildHotKeys(e);
if (keyPattern === 'l' && !isActiveElementTypeable(e)) {
this.toggleLabels();
e.preventDefault();
} else if (isEscape(e) && this.showSearchDropdownLabel) {
this.closeDropdownLabel();
e.preventDefault();
}
},
},
};
</script>
Expand Down
3 changes: 2 additions & 1 deletion app/javascript/dashboard/i18n/locale/en/contact.json
Expand Up @@ -33,7 +33,8 @@
"LABEL_SELECT": {
"TITLE": "Add Labels",
"PLACEHOLDER": "Search labels",
"NO_RESULT": "No labels found"
"NO_RESULT": "No labels found",
"CREATE_LABEL": "Create new label"
}
},
"MERGE_CONTACT": "Merge contact",
Expand Down
Expand Up @@ -30,6 +30,7 @@
v-if="showSearchDropdownLabel"
:account-labels="accountLabels"
:selected-labels="savedLabels"
:allow-creation="isAdmin"
@add="addLabelToConversation"
@remove="removeLabelFromConversation"
/>
Expand All @@ -47,7 +48,14 @@ import Spinner from 'shared/components/Spinner';
import LabelDropdown from 'shared/components/ui/label/LabelDropdown';
import AddLabel from 'shared/components/ui/dropdown/AddLabel';
import { mixin as clickaway } from 'vue-clickaway';
import adminMixin from 'dashboard/mixins/isAdmin';
import eventListenerMixins from 'shared/mixins/eventListenerMixins';
import conversationLabelMixin from 'dashboard/mixins/conversation/labelMixin';
import {
buildHotKeys,
isEscape,
isActiveElementTypeable,
} from 'shared/helpers/KeyboardHelpers';
export default {
components: {
Expand All @@ -56,7 +64,7 @@ export default {
AddLabel,
},
mixins: [clickaway, conversationLabelMixin],
mixins: [clickaway, conversationLabelMixin, adminMixin, eventListenerMixins],
props: {
conversationId: {
type: Number,
Expand Down Expand Up @@ -84,6 +92,17 @@ export default {
closeDropdownLabel() {
this.showSearchDropdownLabel = false;
},
handleKeyEvents(e) {
const keyPattern = buildHotKeys(e);
if (keyPattern === 'l' && !isActiveElementTypeable(e)) {
this.toggleLabels();
e.preventDefault();
} else if (isEscape(e) && this.showSearchDropdownLabel) {
this.closeDropdownLabel();
e.preventDefault();
}
},
},
};
</script>
Expand Down
Expand Up @@ -65,6 +65,12 @@ import { getRandomColor } from 'dashboard/helper/labelColor';
export default {
mixins: [alertMixin, validationMixin],
props: {
prefillTitle: {
type: String,
default: '',
},
},
data() {
return {
color: '#000',
Expand All @@ -81,6 +87,7 @@ export default {
},
mounted() {
this.color = getRandomColor();
this.title = this.prefillTitle.toLowerCase();
},
methods: {
onClose() {
Expand Down
5 changes: 4 additions & 1 deletion app/javascript/dashboard/store/modules/labels.js
Expand Up @@ -45,7 +45,10 @@ export const actions = {
commit(types.SET_LABEL_UI_FLAG, { isFetching: true });
try {
const response = await LabelsAPI.get(true);
commit(types.SET_LABELS, response.data.payload);
const sortedLabels = response.data.payload.sort((a, b) =>
a.title.localeCompare(b.title)
);
commit(types.SET_LABELS, sortedLabels);
} catch (error) {
// Ignore error
} finally {
Expand Down
124 changes: 115 additions & 9 deletions app/javascript/shared/components/ui/label/LabelDropdown.vue
@@ -1,8 +1,11 @@
<template>
<div class="dropdown-search-wrap">
<h4 class="text-block-title">
{{ $t('CONTACT_PANEL.LABELS.LABEL_SELECT.TITLE') }}
</h4>
<div class="dropdown-title-container">
<h4 class="text-block-title">
{{ $t('CONTACT_PANEL.LABELS.LABEL_SELECT.TITLE') }}
</h4>
<hotkey>L</hotkey>
</div>
<div class="search-wrap">
<input
ref="searchbar"
Expand All @@ -28,16 +31,48 @@
<div v-if="noResult" class="no-result">
{{ $t('CONTACT_PANEL.LABELS.LABEL_SELECT.NO_RESULT') }}
</div>
<div v-if="allowCreation && shouldShowCreate" class="new-label">
<woot-button
size="small"
variant="clear"
color-scheme="secondary"
icon="add"
is-expanded
class="button-new-label"
:is-disabled="hasExactMatchInResults"
@click="showCreateModal"
>
{{ createLabelPlaceholder }}
{{ parsedSearch }}
</woot-button>

<woot-modal
:show.sync="createModalVisible"
:on-close="hideCreateModal"
>
<add-label-modal
:prefill-title="parsedSearch"
@close="hideCreateModal"
/>
</woot-modal>
</div>
</div>
</div>
</div>
</template>

<script>
import LabelDropdownItem from './LabelDropdownItem';
import Hotkey from 'dashboard/components/base/Hotkey';
import AddLabelModal from 'dashboard/routes/dashboard/settings/labels/AddLabel';
import { picoSearch } from '@scmmishra/pico-search';
import { sanitizeLabel } from 'shared/helpers/sanitizeData';
export default {
components: {
LabelDropdownItem,
AddLabelModal,
Hotkey,
},
props: {
Expand All @@ -49,23 +84,49 @@ export default {
type: Array,
default: () => [],
},
allowCreation: {
type: Boolean,
default: false,
},
},
data() {
return {
search: '',
createModalVisible: false,
};
},
computed: {
createLabelPlaceholder() {
const label = this.$t('CONTACT_PANEL.LABELS.LABEL_SELECT.CREATE_LABEL');
return this.search ? `${label}:` : label;
},
filteredActiveLabels() {
return this.accountLabels.filter(label => {
return label.title.toLowerCase().includes(this.search.toLowerCase());
if (!this.search) return this.accountLabels;
return picoSearch(this.accountLabels, this.search, ['title'], {
threshold: 0.9,
});
},
noResult() {
return this.filteredActiveLabels.length === 0 && this.search !== '';
return this.filteredActiveLabels.length === 0;
},
hasExactMatchInResults() {
return this.filteredActiveLabels.some(
label => label.title === this.search
);
},
shouldShowCreate() {
return this.allowCreation && this.filteredActiveLabels.length < 3;
},
parsedSearch() {
return sanitizeLabel(this.search);
},
},
Expand Down Expand Up @@ -97,11 +158,35 @@ export default {
this.onAdd(label);
}
},
showCreateModal() {
this.createModalVisible = true;
},
hideCreateModal() {
this.createModalVisible = false;
},
},
};
</script>

<style lang="scss" scoped>
.dropdown-title-container {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: var(--space-smaller);
.text-block-title {
flex-grow: 1;
margin: 0;
}
.hotkey {
flex-shrink: 0;
}
}
.dropdown-search-wrap {
display: flex;
flex-direction: column;
Expand All @@ -124,7 +209,7 @@ export default {
}
input:focus {
border: 1px solid var(--w-500);
outline: 1px solid var(--color-border-dark);
}
}
Expand All @@ -143,9 +228,30 @@ export default {
display: flex;
justify-content: center;
color: var(--s-700);
padding: var(--space-smaller) var(--space-one);
padding: var(--space-normal) var(--space-one);
font-weight: var(--font-weight-medium);
font-size: var(--font-size-small);
font-size: var(--font-size-mini);
}
.new-label {
display: flex;
padding-top: var(--space-smaller);
border-top: 1px solid var(--s-100);
.button-new-label {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
align-items: center;
.icon {
min-width: 0;
}
}
.search-term {
color: var(--s-700);
}
}
}
}
Expand Down

0 comments on commit 996325f

Please sign in to comment.