diff --git a/demo/src/ChatContainer.vue b/demo/src/ChatContainer.vue index 9d6421f6..27655e83 100644 --- a/demo/src/ChatContainer.vue +++ b/demo/src/ChatContainer.vue @@ -51,6 +51,7 @@ :room-actions="roomActions" :menu-actions="menuActions" :room-message="roomMessage" + :templates-text="templatesText" @fetch-more-rooms="fetchMoreRooms" @fetch-messages="fetchMessages" @send-message="sendMessage" @@ -141,7 +142,21 @@ export default { { name: 'removeUser', title: 'Remove User' }, { name: 'deleteRoom', title: 'Delete Room' } ], - styles: { container: { borderRadius: '4px' } } + styles: { container: { borderRadius: '4px' } }, + templatesText: [ + { + tag: 'help', + text: 'This is the help' + }, + { + tag: 'action', + text: 'this is the action' + }, + { + tag: 'action 2', + text: 'this is the second action' + } + ] // ,dbRequestCount: 0 } }, diff --git a/package.json b/package.json index c1aa3e03..64a29fce 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ "typescript": "^4.0.5", "vue": "^2.6.14", "vue-jest": "^3.0.7", - "vue-template-compiler": "^2.6.11" + "vue-template-compiler": "^2.6.14" }, "peerDependencies": { "vue": "^2.6.14" diff --git a/src/lib/ChatWindow.vue b/src/lib/ChatWindow.vue index d29c4f15..a1658d68 100644 --- a/src/lib/ChatWindow.vue +++ b/src/lib/ChatWindow.vue @@ -54,6 +54,7 @@ :room-info-enabled="roomInfoEnabled" :textarea-action-enabled="textareaActionEnabled" :accepted-files="acceptedFiles" + :templates-text="templatesText" @toggle-rooms-list="toggleRoomsList" @room-info="roomInfo" @fetch-messages="fetchMessages" @@ -138,7 +139,8 @@ export default { roomInfoEnabled: { type: Boolean, default: false }, textareaActionEnabled: { type: Boolean, default: false }, roomMessage: { type: String, default: '' }, - acceptedFiles: { type: String, default: '*' } + acceptedFiles: { type: String, default: '*' }, + templatesText: { type: Array, default: null } }, emits: [ diff --git a/src/lib/Room/Room.vue b/src/lib/Room/Room.vue index dd7101a5..5d07fd40 100644 --- a/src/lib/Room/Room.vue +++ b/src/lib/Room/Room.vue @@ -140,6 +140,14 @@ @select-user-tag="selectUserTag($event)" /> + +
@@ -264,7 +278,7 @@ type="file" multiple :accept="acceptedFiles" - style="display:none" + style="display: none" @change="onFileChange($event.target.files)" /> @@ -298,9 +312,10 @@ import RoomFiles from './RoomFiles/RoomFiles' import RoomMessageReply from './RoomMessageReply/RoomMessageReply' import RoomUsersTag from './RoomUsersTag/RoomUsersTag' import RoomEmojis from './RoomEmojis/RoomEmojis' +import RoomTemplatesText from './RoomTemplatesText/RoomTemplatesText' import Message from '../Message/Message' -import filteredUsers from '../../utils/filter-items' +import filteredItems from '../../utils/filter-items' import Recorder from '../../utils/recorder' const { detectMobile, iOSDevice } = require('../../utils/mobile-detection') @@ -327,6 +342,7 @@ export default { RoomMessageReply, RoomUsersTag, RoomEmojis, + RoomTemplatesText, Message }, @@ -360,7 +376,8 @@ export default { linkOptions: { type: Object, required: true }, loadingRooms: { type: Boolean, required: true }, roomInfoEnabled: { type: Boolean, required: true }, - textareaActionEnabled: { type: Boolean, required: true } + textareaActionEnabled: { type: Boolean, required: true }, + templatesText: { type: Array, default: null } }, emits: [ @@ -398,6 +415,9 @@ export default { filteredEmojis: [], filteredUsersTag: [], selectedUsersTag: [], + filteredTemplatesText: [], + activeTemplate: null, + activeUpOrDown: null, textareaCursorPosition: null, cursorRangePosition: null, emojisDB: new Database(), @@ -442,6 +462,7 @@ export default { return ( !!this.filteredEmojis.length || !!this.filteredUsersTag.length || + !!this.filteredTemplatesText.length || !!this.files.length || !!this.messageReply ) @@ -515,7 +536,7 @@ export default { if (isMobile) { this.message = this.message + '\n' setTimeout(() => this.onChangeInput()) - } else { + } else if (this.filteredTemplatesText.length === 0) { this.sendMessage() } } @@ -523,6 +544,7 @@ export default { setTimeout(() => { this.updateFooterList('@') this.updateFooterList(':') + this.updateFooterList('/') }, 60) }), 50 @@ -533,6 +555,7 @@ export default { this.updateFooterList('@') this.updateFooterList(':') + this.updateFooterList('/') }) this.$refs['roomTextarea'].addEventListener('blur', () => { @@ -642,6 +665,10 @@ export default { return } + if (tagChar === '/' && !this.templatesText) { + return + } + if ( this.textareaCursorPosition === this.$refs['roomTextarea'].selectionStart @@ -676,6 +703,8 @@ export default { this.updateEmojis(query) } else if (tagChar === '@') { this.updateShowUsersTag(query) + } else if (tagChar === '/') { + this.updateShowTemplatesText(query) } } else { this.resetFooterList(tagChar) @@ -717,7 +746,7 @@ export default { this.focusTextarea() }, updateShowUsersTag(query) { - this.filteredUsersTag = filteredUsers( + this.filteredUsersTag = filteredItems( this.room.users, 'username', query, @@ -743,14 +772,55 @@ export default { position + user.username.length + space.length + 1 this.focusTextarea() }, + updateShowTemplatesText(query) { + this.filteredTemplatesText = filteredItems( + this.templatesText, + 'tag', + query, + true + ) + }, + selectTemplateText(template) { + this.activeTemplate = false + if (!template) return + const { position, endPosition } = this.getCharPosition('/') + + const space = this.message.substr(endPosition, endPosition).length + ? '' + : ' ' + + this.message = + this.message.substr(0, position - 1) + + template.text + + space + + this.message.substr(endPosition, this.message.length - 1) + + this.cursorRangePosition = + position + template.text.length + space.length + 1 + this.focusTextarea() + }, + beforeEnter() { + if (this.filteredTemplatesText.length > 0) { + this.activeTemplate = true + } + }, + upActiveTemplate() { + this.activeUpOrDown = -1 + }, + downActiveTemplate() { + this.activeUpOrDown = 1 + }, resetFooterList(tagChar = null) { if (tagChar === ':') { this.filteredEmojis = [] } else if (tagChar === '@') { this.filteredUsersTag = [] + } else if (tagChar === '/') { + this.filteredTemplatesText = [] } else { this.filteredEmojis = [] this.filteredUsersTag = [] + this.filteredTemplatesText = [] } this.textareaCursorPosition = null @@ -758,7 +828,9 @@ export default { escapeTextarea() { if (this.filteredEmojis.length) this.filteredEmojis = [] else if (this.filteredUsersTag.length) this.filteredUsersTag = [] - else this.resetMessage() + else if (this.filteredTemplatesText.length) { + this.filteredTemplatesText = [] + } else this.resetMessage() }, resetMessage(disableMobileFocus = false, initRoom = false) { if (!initRoom) { diff --git a/src/lib/Room/RoomTemplatesText/RoomTemplatesText.scss b/src/lib/Room/RoomTemplatesText/RoomTemplatesText.scss new file mode 100644 index 00000000..42fee667 --- /dev/null +++ b/src/lib/Room/RoomTemplatesText/RoomTemplatesText.scss @@ -0,0 +1,45 @@ +.vac-template-container { + position: absolute; + display: flex; + flex-direction: column; + align-items: center; + width: 100%; + + .vac-template-box { + display: flex; + width: 100%; + height: 54px; + overflow: hidden; + cursor: pointer; + background: var(--chat-footer-bg-color-tag); + transition: background-color 0.3s cubic-bezier(0.25, 0.8, 0.5, 1); + } + .vac-template-active { + background: var(--chat-footer-bg-color-tag-active); + transition: background-color 0.3s cubic-bezier(0.25, 0.8, 0.5, 1) !important; + } + .vac-template-info { + display: flex; + overflow: hidden; + padding: 0 20px; + align-items: center; + } + .vac-template-tag { + font-size: 14px; + font-weight: bold; + margin-right: 10px; + } + .vac-template-text { + font-size: 14px; + } + + @media only screen and (max-width: 768px) { + .vac-template-box { + height: 50px; + } + + .vac-template-info { + padding: 0 12px; + } + } +} \ No newline at end of file diff --git a/src/lib/Room/RoomTemplatesText/RoomTemplatesText.vue b/src/lib/Room/RoomTemplatesText/RoomTemplatesText.vue new file mode 100644 index 00000000..fd7cedb4 --- /dev/null +++ b/src/lib/Room/RoomTemplatesText/RoomTemplatesText.vue @@ -0,0 +1,70 @@ + + + diff --git a/src/styles/index.scss b/src/styles/index.scss index 62ef80c9..9c7d721f 100644 --- a/src/styles/index.scss +++ b/src/styles/index.scss @@ -11,6 +11,7 @@ @import '../lib/Room/RoomFiles/RoomFiles'; @import '../lib/Room/RoomFile/RoomFile'; @import '../lib/Room/RoomUsersTag/RoomUsersTag'; +@import '../lib/Room/RoomTemplatesText/RoomTemplatesText'; @import '../lib/RoomsList/RoomsList'; @import '../lib/RoomsList/RoomContent/RoomContent'; diff --git a/src/themes/index.js b/src/themes/index.js index 1517fb40..cf1a4cb1 100644 --- a/src/themes/index.js +++ b/src/themes/index.js @@ -28,7 +28,8 @@ export const defaultThemeStyles = { borderStyleInput: '1px solid #e1e4e8', borderInputSelected: '#1976d2', backgroundReply: '#e5e5e6', - backgroundTagActive: '#e5e5e6' + backgroundTagActive: '#e5e5e6', + backgroundTag: '#fff' }, content: { @@ -167,7 +168,8 @@ export const defaultThemeStyles = { borderStyleInput: 'none', borderInputSelected: '#1976d2', backgroundReply: '#1b1c1c', - backgroundTagActive: '#1b1c1c' + backgroundTagActive: '#1b1c1c', + backgroundTag: '#131415' }, content: { @@ -319,6 +321,7 @@ export const cssThemeVars = ({ '--chat-border-color-input-selected': footer.borderInputSelected, '--chat-footer-bg-color-reply': footer.backgroundReply, '--chat-footer-bg-color-tag-active': footer.backgroundTagActive, + '--chat-footer-bg-color-tag': footer.backgroundTag, // content '--chat-content-bg-color': content.background,