From d6b987eba4965d4103571fb9a1f55ef4841da9ba Mon Sep 17 00:00:00 2001 From: Eduard Kuzmenko Date: Fri, 4 Mar 2022 14:55:08 +0200 Subject: [PATCH] New bot commands menu --- src/components/chat/autocompleteHelper.ts | 30 +++-- src/components/chat/autocompletePeerHelper.ts | 15 ++- src/components/chat/botCommands.ts | 54 +++++++++ src/components/chat/commandsHelper.ts | 59 ++++++---- src/components/chat/input.ts | 109 +++++++++++++++++- src/scss/components/_global.scss | 6 +- src/scss/partials/_animatedIcon.scss | 51 ++++++-- src/scss/partials/_chat.scss | 94 ++++++++++++--- src/scss/partials/_chatBotCommands.scss | 85 ++++++++++++++ src/scss/style.scss | 1 + 10 files changed, 439 insertions(+), 65 deletions(-) create mode 100644 src/components/chat/botCommands.ts create mode 100644 src/scss/partials/_chatBotCommands.scss diff --git a/src/components/chat/autocompleteHelper.ts b/src/components/chat/autocompleteHelper.ts index adf83479..fbd91f3c 100644 --- a/src/components/chat/autocompleteHelper.ts +++ b/src/components/chat/autocompleteHelper.ts @@ -16,6 +16,7 @@ import AutocompleteHelperController from "./autocompleteHelperController"; export default class AutocompleteHelper extends EventListenerBase<{ hidden: () => void, visible: () => void, + hiding: () => void }> { protected hidden = true; protected container: HTMLElement; @@ -34,7 +35,7 @@ export default class AutocompleteHelper extends EventListenerBase<{ constructor(options: { appendTo: HTMLElement, - controller: AutocompleteHelper['controller'], + controller?: AutocompleteHelper['controller'], listType: AutocompleteHelper['listType'], onSelect: AutocompleteHelper['onSelect'], waitForKey?: AutocompleteHelper['waitForKey'] @@ -50,7 +51,7 @@ export default class AutocompleteHelper extends EventListenerBase<{ this.attachNavigation(); - this.controller.addHelper(this); + this.controller && this.controller.addHelper(this); } public toggleListNavigation(enabled: boolean) { @@ -110,7 +111,7 @@ export default class AutocompleteHelper extends EventListenerBase<{ this.addEventListener('visible', this.onVisible); } - public toggle(hide?: boolean, fromController = false) { + public toggle(hide?: boolean, fromController = false, skipAnimation?: boolean) { if(this.init) { return; } @@ -130,7 +131,7 @@ export default class AutocompleteHelper extends EventListenerBase<{ this.hidden = hide; if(!hide) { - this.controller.hideOtherHelpers(this); + this.controller && this.controller.hideOtherHelpers(this); this.dispatchEvent('visible'); // fire it before so target will be set } else { if(this.navigationItem) { @@ -138,7 +139,7 @@ export default class AutocompleteHelper extends EventListenerBase<{ this.navigationItem = undefined; } - if(!fromController) { + if(!fromController && this.controller) { this.controller.hideOtherHelpers(); } @@ -147,8 +148,21 @@ export default class AutocompleteHelper extends EventListenerBase<{ } } - SetTransition(this.container, 'is-visible', !hide, rootScope.settings.animationsEnabled ? 200 : 0, () => { - this.hidden && this.dispatchEvent('hidden'); - }); + const useRafs = this.controller || hide ? 0 : 2; + + if(hide) { + this.dispatchEvent('hiding'); + } + + SetTransition( + this.container, + 'is-visible', + !hide, + rootScope.settings.animationsEnabled && !skipAnimation ? 300 : 0, + () => { + this.hidden && this.dispatchEvent('hidden'); + }, + useRafs + ); } } diff --git a/src/components/chat/autocompletePeerHelper.ts b/src/components/chat/autocompletePeerHelper.ts index 909cd618..55e79d7d 100644 --- a/src/components/chat/autocompletePeerHelper.ts +++ b/src/components/chat/autocompletePeerHelper.ts @@ -16,7 +16,12 @@ export default class AutocompletePeerHelper extends AutocompleteHelper { protected static BASE_CLASS_LIST_ELEMENT = AutocompletePeerHelper.BASE_CLASS + '-list-element'; private scrollable: Scrollable; - constructor(appendTo: HTMLElement, controller: AutocompleteHelperController, protected className: string, onSelect: (target: Element) => boolean | void) { + constructor( + appendTo: HTMLElement, + controller: AutocompleteHelperController, + protected className: string, + onSelect: (target: Element) => boolean | void + ) { super({ appendTo, controller, @@ -29,7 +34,7 @@ export default class AutocompletePeerHelper extends AutocompleteHelper { protected init() { this.list = document.createElement('div'); - this.list.classList.add(AutocompletePeerHelper.BASE_CLASS + '-list'); + this.list.classList.add(AutocompletePeerHelper.BASE_CLASS + '-list', this.className + '-list'); this.container.append(this.list); @@ -42,7 +47,7 @@ export default class AutocompletePeerHelper extends AutocompleteHelper { }); } - public render(data: {peerId: PeerId, name?: string, description?: string}[]) { + public render(data: {peerId: PeerId, name?: string, description?: string}[], doNotShow?: boolean) { if(this.init) { if(!data.length) { return; @@ -66,7 +71,9 @@ export default class AutocompletePeerHelper extends AutocompleteHelper { }); } - this.toggle(!data.length); + if(!doNotShow) { + this.toggle(!data.length); + } } public static listElement(options: { diff --git a/src/components/chat/botCommands.ts b/src/components/chat/botCommands.ts new file mode 100644 index 00000000..cfdd835d --- /dev/null +++ b/src/components/chat/botCommands.ts @@ -0,0 +1,54 @@ +/* + * https://github.com/morethanwords/tweb + * Copyright (C) 2019-2021 Eduard Kuzmenko + * https://github.com/morethanwords/tweb/blob/master/LICENSE + */ + +import type { AppProfileManager } from "../../lib/appManagers/appProfileManager"; +import type ChatInput from "./input"; +import callbackify from "../../helpers/callbackify"; +import AutocompletePeerHelper from "./autocompletePeerHelper"; +import { processPeerFullForCommands } from "./commandsHelper"; + +const CLASS_NAME = 'bot-commands'; +export default class ChatBotCommands extends AutocompletePeerHelper { + private userId: UserId; + + constructor( + appendTo: HTMLElement, + private chatInput: ChatInput, + private appProfileManager: AppProfileManager + ) { + super(appendTo, undefined, CLASS_NAME, (target) => { + const innerHTML = target.querySelector(`.${AutocompletePeerHelper.BASE_CLASS_LIST_ELEMENT}-name`).innerHTML; + return chatInput.getReadyToSend(() => { + chatInput.messageInput.innerHTML = innerHTML; + chatInput.sendMessage(true); + this.toggle(true); + }); + }); + } + + public setUserId(userId: UserId, middleware: () => boolean) { + if(this.userId === userId && this.list?.childElementCount) { + this.toggle(false); + return; + } + + this.userId = userId; + return callbackify(this.appProfileManager.getProfile(userId), (full) => { + if(!middleware()) return; + const filtered = processPeerFullForCommands(full); + + const PADDING_TOP = 8; + // const PADDING_BOTTOM = 8; + const PADDING_BOTTOM = 24; + const height = filtered.length * 50 + PADDING_TOP + PADDING_BOTTOM; + this.container.style.setProperty('--height', height + 'px'); + + this.render(filtered); + + // this.container.style.top = + }); + } +} diff --git a/src/components/chat/commandsHelper.ts b/src/components/chat/commandsHelper.ts index 541c8c2c..a8a27eeb 100644 --- a/src/components/chat/commandsHelper.ts +++ b/src/components/chat/commandsHelper.ts @@ -7,11 +7,46 @@ import type ChatInput from "./input"; import type { AppProfileManager } from "../../lib/appManagers/appProfileManager"; import type { AppUsersManager } from "../../lib/appManagers/appUsersManager"; -import type { BotInfo } from "../../layer"; +import type { BotInfo, ChatFull, UserFull } from "../../layer"; import AutocompleteHelperController from "./autocompleteHelperController"; import AutocompletePeerHelper from "./autocompletePeerHelper"; import SearchIndex from "../../lib/searchIndex"; +export function processPeerFullForCommands(full: ChatFull.chatFull | ChatFull.channelFull | UserFull.userFull, query?: string) { + const botInfos: BotInfo.botInfo[] = [].concat(full.bot_info); + let index: SearchIndex; + + if(query !== undefined) { + index = new SearchIndex({ + ignoreCase: true + }); + } + + const commands: Map = new Map(); + botInfos.forEach(botInfo => { + botInfo.commands.forEach(botCommand => { + const c = '/' + botCommand.command; + commands.set(botCommand.command, { + peerId: botInfo.user_id.toPeerId(false), + name: c, + description: botCommand.description + }); + + if(index) { + index.indexObject(botCommand.command, c); + } + }); + }); + + if(!index) { + return [...commands.values()]; + } + + const found = index.search(query); + const filtered = Array.from(found).map(command => commands.get(command)); + return filtered; +} + export default class CommandsHelper extends AutocompletePeerHelper { constructor(appendTo: HTMLElement, controller: AutocompleteHelperController, @@ -42,27 +77,7 @@ export default class CommandsHelper extends AutocompletePeerHelper { return; } - const botInfos: BotInfo.botInfo[] = [].concat(full.bot_info); - const index = new SearchIndex({ - ignoreCase: true - }); - - const commands: Map = new Map(); - botInfos.forEach(botInfo => { - botInfo.commands.forEach(botCommand => { - const c = '/' + botCommand.command; - commands.set(botCommand.command, { - peerId: botInfo.user_id.toPeerId(false), - name: c, - description: botCommand.description - }); - - index.indexObject(botCommand.command, c); - }); - }); - - const found = index.search(query); - const filtered = Array.from(found).map(command => commands.get(command)); + const filtered = processPeerFullForCommands(full, query); this.render(filtered); // console.log('found commands', found, filtered); }); diff --git a/src/components/chat/input.ts b/src/components/chat/input.ts index 4dd9fa88..dab0bf1e 100644 --- a/src/components/chat/input.ts +++ b/src/components/chat/input.ts @@ -32,7 +32,7 @@ import PopupNewMedia from '../popups/newMedia'; import { toast } from "../toast"; import { wrapReply } from "../wrappers"; import InputField from '../inputField'; -import { MessageEntity, DraftMessage, WebPage, Message, ChatFull } from '../../layer'; +import { MessageEntity, DraftMessage, WebPage, Message, ChatFull, UserFull } from '../../layer'; import StickersHelper from './stickersHelper'; import ButtonIcon from '../buttonIcon'; import ButtonMenuToggle from '../buttonMenuToggle'; @@ -91,6 +91,7 @@ import AvatarElement from '../avatar'; import type { AppProfileManager } from '../../lib/appManagers/appProfileManager'; import { indexOfAndSplice } from '../../helpers/array'; import callbackify from '../../helpers/callbackify'; +import ChatBotCommands from './botCommands'; const RECORD_MIN_TIME = 500; const POSTING_MEDIA_NOT_ALLOWED = 'Posting media content isn\'t allowed in this group.'; @@ -222,6 +223,11 @@ export default class ChatInput { public sendAsPeerId: PeerId; private updatingSendAsPromise: Promise; + private botCommandsToggle: HTMLElement; + private botCommands: ChatBotCommands; + private botCommandsIcon: HTMLDivElement; + hasBotCommands: number; + // private activeContainer: HTMLElement; constructor( @@ -573,6 +579,38 @@ export default class ChatInput { }); this.listenerSetter.add(this.replyKeyboard)('open', () => this.btnToggleReplyMarkup.classList.add('active')); this.listenerSetter.add(this.replyKeyboard)('close', () => this.btnToggleReplyMarkup.classList.remove('active')); + + this.botCommands = new ChatBotCommands(this.rowsWrapper, this, this.appProfileManager); + this.botCommandsToggle = document.createElement('div'); + this.botCommandsToggle.classList.add('new-message-bot-commands'); + + const scaler = document.createElement('div'); + scaler.classList.add('new-message-bot-commands-icon-scale'); + + const icon = this.botCommandsIcon = document.createElement('div'); + icon.classList.add('animated-menu-icon', 'animated-menu-close-icon'); + scaler.append(icon); + this.botCommandsToggle.append(scaler); + + attachClickEvent(this.botCommandsToggle, (e) => { + cancelEvent(e); + const isShown = icon.classList.contains('state-back'); + if(isShown) { + this.botCommands.toggle(true); + icon.classList.remove('state-back'); + } else { + this.botCommands.setUserId(this.chat.peerId.toUserId(), this.chat.bubbles.getMiddleware()); + icon.classList.add('state-back'); + } + }, {listenerSetter: this.listenerSetter}); + + this.botCommands.addEventListener('visible', () => { + icon.classList.add('state-back'); + }); + + this.botCommands.addEventListener('hiding', () => { + icon.classList.remove('state-back'); + }); } this.attachMenuButtons = [{ @@ -619,7 +657,7 @@ export default class ChatInput { this.fileInput.multiple = true; this.fileInput.style.display = 'none'; - this.newMessageWrapper.append(...[this.sendAsContainer, this.btnToggleEmoticons, this.inputMessageContainer, this.btnScheduled, this.btnToggleReplyMarkup, this.attachMenu, this.recordTimeEl, this.fileInput].filter(Boolean)); + this.newMessageWrapper.append(...[this.sendAsContainer, this.botCommandsToggle, this.btnToggleEmoticons, this.inputMessageContainer, this.btnScheduled, this.btnToggleReplyMarkup, this.attachMenu, this.recordTimeEl, this.fileInput].filter(Boolean)); this.rowsWrapper.append(this.replyElements.container); this.autocompleteHelperController = new AutocompleteHelperController(); @@ -1194,7 +1232,7 @@ export default class ChatInput { public finishPeerChange(startParam?: string) { const peerId = this.chat.peerId; - const {forwardElements, btnScheduled, replyKeyboard, sendMenu, goDownBtn, chatInput, sendAsContainer} = this; + const {forwardElements, btnScheduled, replyKeyboard, sendMenu, goDownBtn, chatInput, sendAsContainer, botCommandsToggle} = this; chatInput.style.display = ''; const isBroadcast = this.appPeersManager.isBroadcast(peerId); @@ -1225,6 +1263,24 @@ export default class ChatInput { }); } + this.updateOffset(null, false, true); + + if(botCommandsToggle) { + this.hasBotCommands = undefined; + this.botCommands.toggle(true, undefined, true); + this.updateBotCommandsToggle(true); + botCommandsToggle.remove(); + if(this.appPeersManager.isBot(peerId)) { + const userId = peerId.toUserId(); + const middleware = this.chat.bubbles.getMiddleware(); + const getUserFullResult = this.appProfileManager.getProfile(userId); + callbackify(getUserFullResult, (userFull) => { + if(!middleware()) return; + this.updateBotCommands(userFull, !(getUserFullResult instanceof Promise)); + }); + } + } + if(sendAsContainer) { if(this.sendAsAvatar) { this.sendAsAvatar.remove(); @@ -1232,7 +1288,6 @@ export default class ChatInput { } sendAsContainer.remove(); - SetTransition(this.newMessageWrapper, 'has-send-as', false, 0); this.sendAsPeerId = undefined; this.updatingSendAsPromise = undefined; @@ -1261,6 +1316,39 @@ export default class ChatInput { this.center(false); } + private updateOffset(type: 'commands' | 'as', forwards: boolean, skipAnimation?: boolean, useRafs?: number) { + if(type) { + this.newMessageWrapper.dataset.offset = type; + } else { + delete this.newMessageWrapper.dataset.offset; + } + + SetTransition(this.newMessageWrapper, 'has-offset', forwards, skipAnimation ? 0 : 300, undefined, useRafs); + } + + private updateBotCommands(userFull: UserFull.userFull, skipAnimation?: boolean) { + this.hasBotCommands = userFull.bot_info && userFull.bot_info.commands.length; + this.updateBotCommandsToggle(skipAnimation); + } + + private updateBotCommandsToggle(skipAnimation?: boolean) { + const {botCommandsToggle, hasBotCommands} = this; + + const show = hasBotCommands && this.isInputEmpty(); + if(!hasBotCommands) { + botCommandsToggle.remove(); + } + + const forwards = show; + const useRafs = botCommandsToggle.parentElement ? 0 : 2; + + if(!botCommandsToggle.parentElement) { + this.newMessageWrapper.prepend(botCommandsToggle); + } + + this.updateOffset('commands', forwards, skipAnimation, useRafs); + } + private updateSendAsButtons(peerIds: PeerId[]) { const buttons: ButtonMenuItemOptions[] = peerIds.map((sendAsPeerId, idx) => { const textElement = document.createElement('div'); @@ -1415,7 +1503,7 @@ export default class ChatInput { useRafs = 2; } - SetTransition(this.newMessageWrapper, 'has-send-as', true, skipAnimation ? 0 : SEND_AS_ANIMATION_DURATION, undefined, useRafs); + this.updateOffset('as', true, skipAnimation, useRafs); this.updatingSendAsPromise = undefined; }); @@ -1850,7 +1938,8 @@ export default class ChatInput { } } - if(!richValue.trim()) { + const isEmpty = !richValue.trim(); + if(isEmpty) { if(this.lastTimeType) { this.appMessagesManager.setTyping(this.chat.peerId, {_: 'sendMessageCancelAction'}); } @@ -1864,6 +1953,14 @@ export default class ChatInput { this.lastTimeType = time; this.appMessagesManager.setTyping(this.chat.peerId, {_: 'sendMessageTypingAction'}); } + + if(this.botCommands) { + this.botCommands.toggle(true); + } + } + + if(this.botCommands) { + this.updateBotCommandsToggle(); } if(!this.editMsgId) { diff --git a/src/scss/components/_global.scss b/src/scss/components/_global.scss index b5f65245..45f0074e 100644 --- a/src/scss/components/_global.scss +++ b/src/scss/components/_global.scss @@ -96,7 +96,11 @@ Utility Classes } .no-transition { - transition: none !important; + &, + &:before, + &:after { + transition: none !important; + } /* &-all, &-all * { transition: none !important; diff --git a/src/scss/partials/_animatedIcon.scss b/src/scss/partials/_animatedIcon.scss index 0cc6b0c6..90985183 100644 --- a/src/scss/partials/_animatedIcon.scss +++ b/src/scss/partials/_animatedIcon.scss @@ -31,12 +31,6 @@ &, &:before, &:after { transition: transform var(--slide-header-transition); } - - &.no-transition { - &, &:before, &:after { - transition: none; - } - } } &.state-back { @@ -53,13 +47,14 @@ } .animated-menu-icon { + --color: var(--secondary-text-color); position: absolute; &, &:before, &:after { width: 1.125rem; height: .125rem; border-radius: .125rem; - background-color: var(--secondary-text-color); + background-color: var(--color); transform: rotate(0); } @@ -96,6 +91,48 @@ } } +.animated-menu-close-icon { + margin-top: -.625rem; + + &:before { + top: .3125rem; + opacity: 1; + + @include animation-level(2) { + transition: transform .25s, opacity .125s 0s; + } + } + + &:after { + top: .625rem; + } + + &.state-back { + transform: translate(0, .3125rem) rotate(135deg); + + &:before { + transform: rotate(45deg); + opacity: 0; + } + + &:after { + transform: translate(-.0rem, -.625rem) rotate(90deg); + } + } + /* &.state-back { + transform: rotate(135deg) translate(.25rem, -.1875rem); + + &:before { + transform: rotate(45deg); + opacity: 0; + } + + &:after { + transform: rotate(90deg) translate(-.625rem, 0rem); + } + } */ +} + .animated-button-icon { > .tgico { position: absolute; diff --git a/src/scss/partials/_chat.scss b/src/scss/partials/_chat.scss index b0c9f060..671a08ca 100644 --- a/src/scss/partials/_chat.scss +++ b/src/scss/partials/_chat.scss @@ -5,7 +5,8 @@ */ $btn-send-margin: .5rem; -$chat-helper-size: 36px; +$chat-helper-size: 45px; +$chat-input-box-shadow: 0px 1px 8px 1px rgb(0 0 0 / 18%); $input-transition-time: .2s; $input-half-transition-time: #{$input-transition-time / 2}; @@ -765,7 +766,7 @@ $background-transition-total-time: #{$input-transition-time - $background-transi bottom: 0; left: 0; border-radius: inherit; - box-shadow: 0px 1px 8px 1px rgb(0 0 0 / 18%); + box-shadow: $chat-input-box-shadow; background-color: #fff; background-color: var(--surface-color); opacity: 1; @@ -1047,9 +1048,9 @@ $background-transition-total-time: #{$input-transition-time - $background-transi height: 100%; z-index: 1; */ height: 0; - width: calc(100% - var(--padding-horizontal) * 2); - padding: 0; - margin-top: .5625rem;//var(--padding-vertical); + width: 100%; + padding: .5625rem var(--padding-horizontal) 0; + // margin-top: .5625rem;//var(--padding-vertical); margin-bottom: -.5625rem; //height: calc(#{$chat-helper-size} + .3125rem); align-items: center; @@ -1064,7 +1065,7 @@ $background-transition-total-time: #{$input-transition-time - $background-transi } @include respond-to(esg-bottom-new) { - margin-top: .3125rem; + padding-top: .3125rem; margin-bottom: -.3125rem; } @@ -1163,29 +1164,78 @@ $background-transition-total-time: #{$input-transition-time - $background-transi } */ } + /* &:after { + content: " "; + position: absolute; + top: 0; + width: 100%; + height: .0625rem; + background-color: var(--border-color); + opacity: 0; + z-index: 2; + } + + &.have-commands { + &:after { + opacity: 1; + } + } */ + .new-message-wrapper { --send-as-size: 1.875rem; --send-as-margin-left: .25rem; --send-as-margin-right: .375rem; --send-as-total-size: calc(var(--send-as-size) + var(--send-as-margin-left) + var(--send-as-margin-right)); + --commands-size: 2.375rem; + --commands-margin-left: .25rem; + --commands-margin-right: .375rem; + --commands-total-size: calc(var(--commands-size) + var(--commands-margin-left) + var(--commands-margin-right)); + --offset-translateX: 0px; //padding: 4.5px 0; //padding-bottom: 4.5px; align-items: flex-end; min-height: var(--chat-input-size); + .new-message-bot-commands, + .new-message-send-as-container { + position: absolute; + flex: 0 0 auto; + bottom: calc(var(--padding-vertical) + .4375rem); + cursor: pointer; + transform: scale(0); + z-index: 2; + } + + .new-message-bot-commands { + width: var(--commands-size); + height: 1.875rem; + border-radius: 1.875rem; + background-color: var(--primary-color); + display: flex; + align-items: center; + justify-content: center; + margin-left: var(--commands-margin-left); + cursor: pointer; + + &-icon-scale { + transform: scale(.875); + display: flex; + align-items: center; + justify-content: center; + } + + .animated-menu-close-icon { + --color: #fff; + } + } + .new-message-send-as { &-container { width: var(--send-as-size); height: var(--send-as-size); - position: absolute; - flex: 0 0 auto; // margin: 0 0.375rem .4375rem var(--send-as-margin-left); margin-left: var(--send-as-margin-left); - bottom: calc(var(--padding-vertical) + .4375rem); - cursor: pointer; - transform: scale(0); background: none !important; - z-index: 2; .btn-menu { max-height: 20rem; @@ -1253,7 +1303,15 @@ $background-transition-total-time: #{$input-transition-time - $background-transi } } - &.has-send-as { + &.has-offset { + &[data-offset="as"] { + --offset-translateX: var(--send-as-total-size); + } + + &[data-offset="commands"] { + --offset-translateX: 48px; + } + .toggle-emoticons, .input-message-container { transform: translateX(0); @@ -1261,16 +1319,17 @@ $background-transition-total-time: #{$input-transition-time - $background-transi &:not(.backwards) { .toggle-emoticons { - transform: translateX(var(--send-as-total-size)); + transform: translateX(var(--offset-translateX)); } .input-message-container { - --translateX: calc(var(--send-as-total-size)); + --translateX: calc(var(--offset-translateX)); padding-right: var(--translateX); transform: translate(var(--translateX)); } - .new-message-send-as-container { + .new-message-send-as-container, + .new-message-bot-commands { transform: scale(1); } } @@ -1278,7 +1337,8 @@ $background-transition-total-time: #{$input-transition-time - $background-transi &.animating { .toggle-emoticons, .input-message-container, - .new-message-send-as-container { + .new-message-send-as-container, + .new-message-bot-commands { transition: transform var(--transition-standard-in); } } diff --git a/src/scss/partials/_chatBotCommands.scss b/src/scss/partials/_chatBotCommands.scss new file mode 100644 index 00000000..e93a8f2c --- /dev/null +++ b/src/scss/partials/_chatBotCommands.scss @@ -0,0 +1,85 @@ +/* + * https://github.com/morethanwords/tweb + * Copyright (C) 2019-2021 Eduard Kuzmenko + * https://github.com/morethanwords/tweb/blob/master/LICENSE + */ + +.bot-commands { + --border-radius-padding: #{$border-radius-big * 2}; + --offset: .5rem; + position: absolute !important; + // bottom: 100%; + bottom: calc(100% - var(--border-radius-padding)); + right: calc(var(--offset) * -1); + left: calc(var(--offset) * -1); + width: auto !important; + max-height: 20rem; + max-width: none; + border-radius: $border-radius-big $border-radius-big 0 0 !important; + background-color: transparent !important; + pointer-events: none; + overflow: hidden; + padding: var(--offset) var(--offset) 0 !important; + box-shadow: none; + animation: none !important; + visibility: visible !important; + transition: none !important; + display: flex !important; + + .scrollable { + background-color: var(--surface-color); + box-shadow: $chat-input-box-shadow; + border-radius: inherit; + height: auto; + pointer-events: all; + // max-height: 20rem; + + @include animation-level(2) { + opacity: 0; + transform: translateY(var(--height)); + } + } + + &.is-visible { + &.animating { + .scrollable { + transition: transform var(--transition-standard-in), opacity var(--transition-standard-in); + } + } + + &:not(.backwards) { + .scrollable { + transform: translateY(0); + opacity: 1; + } + } + } + + &-list { + border-radius: inherit; + width: 100%; + height: var(--height); + // padding-bottom: var(--border-radius-padding); + padding-bottom: 0; + + &-element { + border-radius: 0 !important; + flex-direction: column; + align-items: flex-start; + justify-content: center; + padding-left: 3.375rem; + + &-avatar { + position: absolute; + left: .75rem; + } + + &-name, + &-description { + margin-left: 0; + font-size: .875rem; + line-height: var(--line-height-14); + } + } + } +} diff --git a/src/scss/style.scss b/src/scss/style.scss index 49b0e734..28ea3f10 100644 --- a/src/scss/style.scss +++ b/src/scss/style.scss @@ -307,6 +307,7 @@ $chat-input-inner-padding-handhelds: .25rem; @import "partials/chatInlineHelper"; @import "partials/chatSearch"; @import "partials/chatDrop"; +@import "partials/chatBotCommands"; @import "partials/crop"; @import "partials/sidebar"; @import "partials/profile";