feat: Replace the use of keyboardEventListener mixin to a composable (Part -3) (#9897)
This commit is contained in:
@@ -1,43 +1,34 @@
|
|||||||
<script>
|
<script setup>
|
||||||
|
import { ref, computed } from 'vue';
|
||||||
|
import { useKeyboardEvents } from 'dashboard/composables/useKeyboardEvents';
|
||||||
import WootMessageEditor from 'dashboard/components/widgets/WootWriter/Editor.vue';
|
import WootMessageEditor from 'dashboard/components/widgets/WootWriter/Editor.vue';
|
||||||
import keyboardEventListenerMixins from 'shared/mixins/keyboardEventListenerMixins';
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
WootMessageEditor,
|
|
||||||
},
|
|
||||||
mixins: [keyboardEventListenerMixins],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
noteContent: '',
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
const emit = defineEmits(['add']);
|
||||||
buttonDisabled() {
|
|
||||||
return this.noteContent === '';
|
const addNoteRef = ref(null);
|
||||||
},
|
const noteContent = ref('');
|
||||||
},
|
|
||||||
methods: {
|
const buttonDisabled = computed(() => noteContent.value === '');
|
||||||
getKeyboardEvents() {
|
|
||||||
return {
|
const onAdd = () => {
|
||||||
'$mod+Enter': {
|
if (noteContent.value !== '') {
|
||||||
action: () => this.onAdd(),
|
emit('add', noteContent.value);
|
||||||
allowOnFocusedInput: true,
|
}
|
||||||
},
|
noteContent.value = '';
|
||||||
};
|
};
|
||||||
},
|
|
||||||
onAdd() {
|
const keyboardEvents = {
|
||||||
if (this.noteContent !== '') {
|
'$mod+Enter': {
|
||||||
this.$emit('add', this.noteContent);
|
action: () => onAdd(),
|
||||||
}
|
allowOnFocusedInput: true,
|
||||||
this.noteContent = '';
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
useKeyboardEvents(keyboardEvents, addNoteRef);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
|
ref="addNoteRef"
|
||||||
class="flex flex-col flex-grow p-4 mb-2 overflow-hidden bg-white border border-solid rounded-md shadow-sm border-slate-75 dark:border-slate-700 dark:bg-slate-900 text-slate-700 dark:text-slate-100"
|
class="flex flex-col flex-grow p-4 mb-2 overflow-hidden bg-white border border-solid rounded-md shadow-sm border-slate-75 dark:border-slate-700 dark:bg-slate-900 text-slate-700 dark:text-slate-100"
|
||||||
>
|
>
|
||||||
<WootMessageEditor
|
<WootMessageEditor
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
<script>
|
<script>
|
||||||
|
import { ref } from 'vue';
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import { useAdmin } from 'dashboard/composables/useAdmin';
|
import { useAdmin } from 'dashboard/composables/useAdmin';
|
||||||
|
import { useKeyboardEvents } from 'dashboard/composables/useKeyboardEvents';
|
||||||
import Spinner from 'shared/components/Spinner.vue';
|
import Spinner from 'shared/components/Spinner.vue';
|
||||||
import LabelDropdown from 'shared/components/ui/label/LabelDropdown.vue';
|
import LabelDropdown from 'shared/components/ui/label/LabelDropdown.vue';
|
||||||
import AddLabel from 'shared/components/ui/dropdown/AddLabel.vue';
|
import AddLabel from 'shared/components/ui/dropdown/AddLabel.vue';
|
||||||
import keyboardEventListenerMixins from 'shared/mixins/keyboardEventListenerMixins';
|
|
||||||
import conversationLabelMixin from 'dashboard/mixins/conversation/labelMixin';
|
import conversationLabelMixin from 'dashboard/mixins/conversation/labelMixin';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -14,7 +15,7 @@ export default {
|
|||||||
AddLabel,
|
AddLabel,
|
||||||
},
|
},
|
||||||
|
|
||||||
mixins: [conversationLabelMixin, keyboardEventListenerMixins],
|
mixins: [conversationLabelMixin],
|
||||||
props: {
|
props: {
|
||||||
// conversationId prop is used in /conversation/labelMixin,
|
// conversationId prop is used in /conversation/labelMixin,
|
||||||
// remove this props when refactoring to composable if not needed
|
// remove this props when refactoring to composable if not needed
|
||||||
@@ -26,14 +27,46 @@ export default {
|
|||||||
},
|
},
|
||||||
setup() {
|
setup() {
|
||||||
const { isAdmin } = useAdmin();
|
const { isAdmin } = useAdmin();
|
||||||
|
|
||||||
|
const conversationLabelBoxRef = ref(null);
|
||||||
|
const showSearchDropdownLabel = ref(false);
|
||||||
|
|
||||||
|
const toggleLabels = () => {
|
||||||
|
showSearchDropdownLabel.value = !showSearchDropdownLabel.value;
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeDropdownLabel = () => {
|
||||||
|
showSearchDropdownLabel.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const keyboardEvents = {
|
||||||
|
KeyL: {
|
||||||
|
action: e => {
|
||||||
|
e.preventDefault();
|
||||||
|
toggleLabels();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Escape: {
|
||||||
|
action: () => {
|
||||||
|
if (showSearchDropdownLabel.value) {
|
||||||
|
toggleLabels();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
allowOnFocusedInput: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
useKeyboardEvents(keyboardEvents, conversationLabelBoxRef);
|
||||||
return {
|
return {
|
||||||
isAdmin,
|
isAdmin,
|
||||||
|
conversationLabelBoxRef,
|
||||||
|
showSearchDropdownLabel,
|
||||||
|
closeDropdownLabel,
|
||||||
|
toggleLabels,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
selectedLabels: [],
|
selectedLabels: [],
|
||||||
showSearchDropdownLabel: false,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -42,37 +75,11 @@ export default {
|
|||||||
conversationUiFlags: 'conversationLabels/getUIFlags',
|
conversationUiFlags: 'conversationLabels/getUIFlags',
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
methods: {
|
|
||||||
toggleLabels() {
|
|
||||||
this.showSearchDropdownLabel = !this.showSearchDropdownLabel;
|
|
||||||
},
|
|
||||||
closeDropdownLabel() {
|
|
||||||
this.showSearchDropdownLabel = false;
|
|
||||||
},
|
|
||||||
getKeyboardEvents() {
|
|
||||||
return {
|
|
||||||
KeyL: {
|
|
||||||
action: e => {
|
|
||||||
e.preventDefault();
|
|
||||||
this.toggleLabels();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Escape: {
|
|
||||||
action: () => {
|
|
||||||
if (this.showSearchDropdownLabel) {
|
|
||||||
this.toggleLabels();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
allowOnFocusedInput: true,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="sidebar-labels-wrap">
|
<div ref="conversationLabelBoxRef" class="sidebar-labels-wrap">
|
||||||
<div
|
<div
|
||||||
v-if="!conversationUiFlags.isFetching"
|
v-if="!conversationUiFlags.isFetching"
|
||||||
class="contact-conversation--list"
|
class="contact-conversation--list"
|
||||||
|
|||||||
@@ -1,53 +1,51 @@
|
|||||||
<script>
|
<script setup>
|
||||||
import keyboardEventListenerMixins from 'shared/mixins/keyboardEventListenerMixins';
|
import { ref, onMounted } from 'vue';
|
||||||
|
import { useKeyboardEvents } from 'dashboard/composables/useKeyboardEvents';
|
||||||
|
|
||||||
export default {
|
defineProps({
|
||||||
name: 'ChatwootSearch',
|
title: {
|
||||||
mixins: [keyboardEventListenerMixins],
|
type: String,
|
||||||
props: {
|
default: 'Chatwoot',
|
||||||
title: {
|
},
|
||||||
type: String,
|
});
|
||||||
default: 'Chatwoot',
|
|
||||||
|
const emit = defineEmits(['search', 'close']);
|
||||||
|
|
||||||
|
const articleSearchHeaderRef = ref(null);
|
||||||
|
const searchInputRef = ref(null);
|
||||||
|
const searchQuery = ref('');
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
searchInputRef.value.focus();
|
||||||
|
});
|
||||||
|
|
||||||
|
const onInput = e => {
|
||||||
|
emit('search', e.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onClose = () => {
|
||||||
|
emit('close');
|
||||||
|
};
|
||||||
|
|
||||||
|
const keyboardEvents = {
|
||||||
|
Slash: {
|
||||||
|
action: e => {
|
||||||
|
e.preventDefault();
|
||||||
|
searchInputRef.value.focus();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
data() {
|
Escape: {
|
||||||
return {
|
action: () => {
|
||||||
searchQuery: '',
|
onClose();
|
||||||
isInputFocused: false,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.$refs.searchInput.focus();
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
onInput(e) {
|
|
||||||
this.$emit('search', e.target.value);
|
|
||||||
},
|
|
||||||
onClose() {
|
|
||||||
this.$emit('close');
|
|
||||||
},
|
|
||||||
onFocus() {
|
|
||||||
this.isInputFocused = true;
|
|
||||||
},
|
|
||||||
onBlur() {
|
|
||||||
this.isInputFocused = false;
|
|
||||||
},
|
|
||||||
getKeyboardEvents() {
|
|
||||||
return {
|
|
||||||
Slash: {
|
|
||||||
action: e => {
|
|
||||||
e.preventDefault();
|
|
||||||
this.$refs.searchInput.focus();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
|
allowOnFocusedInput: true,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
useKeyboardEvents(keyboardEvents, articleSearchHeaderRef);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col py-1">
|
<div ref="articleSearchHeaderRef" class="flex flex-col py-1">
|
||||||
<div class="flex items-center justify-between py-2 mb-1">
|
<div class="flex items-center justify-between py-2 mb-1">
|
||||||
<h3 class="text-base text-slate-900 dark:text-slate-25">
|
<h3 class="text-base text-slate-900 dark:text-slate-25">
|
||||||
{{ title }}
|
{{ title }}
|
||||||
@@ -68,13 +66,11 @@ export default {
|
|||||||
<fluent-icon icon="search" class="" size="18" />
|
<fluent-icon icon="search" class="" size="18" />
|
||||||
</div>
|
</div>
|
||||||
<input
|
<input
|
||||||
ref="searchInput"
|
ref="searchInputRef"
|
||||||
type="text"
|
type="text"
|
||||||
:placeholder="$t('HELP_CENTER.ARTICLE_SEARCH.PLACEHOLDER')"
|
:placeholder="$t('HELP_CENTER.ARTICLE_SEARCH.PLACEHOLDER')"
|
||||||
class="block w-full !h-9 ltr:!pl-8 rtl:!pr-8 dark:!bg-slate-700 !bg-slate-25 text-sm rounded-md leading-8 text-slate-700 shadow-sm ring-2 ring-transparent ring-slate-300 border border-solid border-slate-300 placeholder:text-slate-400 focus:border-woot-600 focus:ring-woot-200 !mb-0 focus:bg-slate-25 dark:focus:bg-slate-700 dark:focus:ring-woot-700"
|
class="block w-full !h-9 ltr:!pl-8 rtl:!pr-8 dark:!bg-slate-700 !bg-slate-25 text-sm rounded-md leading-8 text-slate-700 shadow-sm ring-2 ring-transparent ring-slate-300 border border-solid border-slate-300 placeholder:text-slate-400 focus:border-woot-600 focus:ring-woot-200 !mb-0 focus:bg-slate-25 dark:focus:bg-slate-700 dark:focus:ring-woot-700"
|
||||||
:value="searchQuery"
|
:value="searchQuery"
|
||||||
@focus="onFocus"
|
|
||||||
@blur="onBlur"
|
|
||||||
@input="onInput"
|
@input="onInput"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
<script>
|
<script>
|
||||||
import { debounce } from '@chatwoot/utils';
|
import { debounce } from '@chatwoot/utils';
|
||||||
import { useAlert } from 'dashboard/composables';
|
import { useAlert } from 'dashboard/composables';
|
||||||
import keyboardEventListenerMixins from 'shared/mixins/keyboardEventListenerMixins';
|
|
||||||
|
|
||||||
import SearchHeader from './Header.vue';
|
import SearchHeader from './Header.vue';
|
||||||
import SearchResults from './SearchResults.vue';
|
import SearchResults from './SearchResults.vue';
|
||||||
@@ -17,7 +16,7 @@ export default {
|
|||||||
SearchResults,
|
SearchResults,
|
||||||
ArticleView,
|
ArticleView,
|
||||||
},
|
},
|
||||||
mixins: [portalMixin, keyboardEventListenerMixins],
|
mixins: [portalMixin],
|
||||||
props: {
|
props: {
|
||||||
selectedPortalSlug: {
|
selectedPortalSlug: {
|
||||||
type: String,
|
type: String,
|
||||||
@@ -114,16 +113,6 @@ export default {
|
|||||||
useAlert(this.$t('HELP_CENTER.ARTICLE_SEARCH.SUCCESS_ARTICLE_INSERTED'));
|
useAlert(this.$t('HELP_CENTER.ARTICLE_SEARCH.SUCCESS_ARTICLE_INSERTED'));
|
||||||
this.onClose();
|
this.onClose();
|
||||||
},
|
},
|
||||||
getKeyboardEvents() {
|
|
||||||
return {
|
|
||||||
Escape: {
|
|
||||||
action: () => {
|
|
||||||
this.onClose();
|
|
||||||
},
|
|
||||||
allowOnFocusedInput: true,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,59 +1,63 @@
|
|||||||
<script>
|
<script setup>
|
||||||
import keyboardEventListenerMixins from 'shared/mixins/keyboardEventListenerMixins';
|
import { ref } from 'vue';
|
||||||
export default {
|
import { useKeyboardEvents } from 'dashboard/composables/useKeyboardEvents';
|
||||||
name: 'WootDropdownMenu',
|
|
||||||
componentName: 'WootDropdownMenu',
|
|
||||||
|
|
||||||
mixins: [keyboardEventListenerMixins],
|
defineProps({
|
||||||
|
placement: {
|
||||||
props: {
|
type: String,
|
||||||
placement: {
|
default: 'top',
|
||||||
type: String,
|
|
||||||
default: 'top',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
methods: {
|
});
|
||||||
dropdownMenuButtons() {
|
|
||||||
return this.$refs.dropdownMenu.querySelectorAll(
|
const dropdownMenuRef = ref(null);
|
||||||
'ul.dropdown li.dropdown-menu__item .button'
|
|
||||||
);
|
const dropdownMenuButtons = () => {
|
||||||
},
|
return dropdownMenuRef.value.querySelectorAll(
|
||||||
getActiveButtonIndex(menuButtons) {
|
'ul.dropdown li.dropdown-menu__item .button'
|
||||||
const focusedButton = this.$refs.dropdownMenu.querySelector(
|
);
|
||||||
'ul.dropdown li.dropdown-menu__item .button:focus'
|
};
|
||||||
);
|
|
||||||
return Array.from(menuButtons).indexOf(focusedButton);
|
const getActiveButtonIndex = menuButtons => {
|
||||||
},
|
const focusedButton = dropdownMenuRef.value.querySelector(
|
||||||
getKeyboardEvents() {
|
'ul.dropdown li.dropdown-menu__item .button:focus'
|
||||||
const menuButtons = this.dropdownMenuButtons();
|
);
|
||||||
return {
|
return Array.from(menuButtons).indexOf(focusedButton);
|
||||||
ArrowUp: () => this.focusPreviousButton(menuButtons),
|
};
|
||||||
ArrowDown: () => this.focusNextButton(menuButtons),
|
|
||||||
};
|
const focusButton = (menuButtons, newIndex) => {
|
||||||
},
|
if (menuButtons.length === 0) return;
|
||||||
focusPreviousButton(menuButtons) {
|
menuButtons[newIndex].focus();
|
||||||
const activeIndex = this.getActiveButtonIndex(menuButtons);
|
};
|
||||||
const newIndex =
|
|
||||||
activeIndex >= 1 ? activeIndex - 1 : menuButtons.length - 1;
|
const focusPreviousButton = menuButtons => {
|
||||||
this.focusButton(menuButtons, newIndex);
|
const activeIndex = getActiveButtonIndex(menuButtons);
|
||||||
},
|
const newIndex = activeIndex >= 1 ? activeIndex - 1 : menuButtons.length - 1;
|
||||||
focusNextButton(menuButtons) {
|
focusButton(menuButtons, newIndex);
|
||||||
const activeIndex = this.getActiveButtonIndex(menuButtons);
|
};
|
||||||
const newIndex =
|
|
||||||
activeIndex === menuButtons.length - 1 ? 0 : activeIndex + 1;
|
const focusNextButton = menuButtons => {
|
||||||
this.focusButton(menuButtons, newIndex);
|
const activeIndex = getActiveButtonIndex(menuButtons);
|
||||||
},
|
const newIndex = activeIndex === menuButtons.length - 1 ? 0 : activeIndex + 1;
|
||||||
focusButton(menuButtons, newIndex) {
|
focusButton(menuButtons, newIndex);
|
||||||
if (menuButtons.length === 0) return;
|
};
|
||||||
menuButtons[newIndex].focus();
|
|
||||||
},
|
const keyboardEvents = {
|
||||||
|
ArrowUp: {
|
||||||
|
action: () => focusPreviousButton(dropdownMenuButtons()),
|
||||||
|
allowOnFocusedInput: true,
|
||||||
|
},
|
||||||
|
ArrowDown: {
|
||||||
|
action: () => focusNextButton(dropdownMenuButtons()),
|
||||||
|
allowOnFocusedInput: true,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useKeyboardEvents(keyboardEvents, dropdownMenuRef);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<ul
|
<ul
|
||||||
ref="dropdownMenu"
|
ref="dropdownMenuRef"
|
||||||
class="dropdown menu vertical"
|
class="dropdown menu vertical"
|
||||||
:class="[placement && `dropdown--${placement}`]"
|
:class="[placement && `dropdown--${placement}`]"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<script>
|
<script>
|
||||||
import messageFormatterMixin from 'shared/mixins/messageFormatterMixin';
|
import messageFormatterMixin from 'shared/mixins/messageFormatterMixin';
|
||||||
import keyboardEventListenerMixins from 'shared/mixins/keyboardEventListenerMixins';
|
|
||||||
import PlaygroundHeader from '../../components/playground/Header.vue';
|
import PlaygroundHeader from '../../components/playground/Header.vue';
|
||||||
import UserMessage from '../../components/playground/UserMessage.vue';
|
import UserMessage from '../../components/playground/UserMessage.vue';
|
||||||
import BotMessage from '../../components/playground/BotMessage.vue';
|
import BotMessage from '../../components/playground/BotMessage.vue';
|
||||||
@@ -13,7 +12,7 @@ export default {
|
|||||||
BotMessage,
|
BotMessage,
|
||||||
TypingIndicator,
|
TypingIndicator,
|
||||||
},
|
},
|
||||||
mixins: [messageFormatterMixin, keyboardEventListenerMixins],
|
mixins: [messageFormatterMixin],
|
||||||
props: {
|
props: {
|
||||||
componentData: {
|
componentData: {
|
||||||
type: Object,
|
type: Object,
|
||||||
@@ -35,16 +34,6 @@ export default {
|
|||||||
this.focusInput();
|
this.focusInput();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getKeyboardEvents() {
|
|
||||||
return {
|
|
||||||
'$mod+Enter': {
|
|
||||||
action: () => {
|
|
||||||
this.onMessageSend();
|
|
||||||
},
|
|
||||||
allowOnFocusedInput: true,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
focusInput() {
|
focusInput() {
|
||||||
this.$refs.messageInput.focus();
|
this.$refs.messageInput.focus();
|
||||||
},
|
},
|
||||||
@@ -133,6 +122,8 @@ export default {
|
|||||||
placeholder="Type a message... [CMD/CTRL + Enter to send]"
|
placeholder="Type a message... [CMD/CTRL + Enter to send]"
|
||||||
autofocus
|
autofocus
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
|
@keydown.meta.enter="onMessageSend"
|
||||||
|
@keydown.ctrl.enter="onMessageSend"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
Reference in New Issue
Block a user