From 37a1eaa37dcccb62c3687eb18e56bac177d9a3f7 Mon Sep 17 00:00:00 2001 From: morethanwords Date: Sat, 10 Oct 2020 17:11:42 +0300 Subject: [PATCH] Send stickers, gifs, polls, media, document rights --- src/components/chat/input.ts | 97 +++++++++++++------ src/components/emoticonsDropdown/index.ts | 69 +++++++++---- .../emoticonsDropdown/tabs/emoji.ts | 12 +-- .../emoticonsDropdown/tabs/stickers.ts | 40 ++++---- src/components/horizontalMenu.ts | 2 +- src/components/popupCreatePoll.ts | 4 +- src/index.hbs | 52 ++++------ src/lib/appManagers/appChatsManager.ts | 14 ++- src/lib/rootScope.ts | 4 +- src/scss/partials/_chat.scss | 2 +- src/scss/partials/_emojiDropdown.scss | 28 ++---- src/scss/partials/_slider.scss | 75 ++++++++++++++ src/scss/style.scss | 7 +- 13 files changed, 274 insertions(+), 132 deletions(-) diff --git a/src/components/chat/input.ts b/src/components/chat/input.ts index 7e4ec0da..951ce387 100644 --- a/src/components/chat/input.ts +++ b/src/components/chat/input.ts @@ -1,5 +1,6 @@ import Recorder from '../../../public/recorder.min'; import { isTouchSupported } from "../../helpers/touchSupport"; +import appChatsManager from '../../lib/appManagers/appChatsManager'; import appDocsManager from "../../lib/appManagers/appDocsManager"; import appImManager from "../../lib/appManagers/appImManager"; import appMessagesManager from "../../lib/appManagers/appMessagesManager"; @@ -8,15 +9,19 @@ import apiManager from "../../lib/mtproto/mtprotoworker"; //import Recorder from '../opus-recorder/dist/recorder.min'; import opusDecodeController from "../../lib/opusDecodeController"; import { RichTextProcessor } from "../../lib/richtextprocessor"; +import $rootScope from '../../lib/rootScope'; import { calcImageInBox, cancelEvent, getRichValue } from "../../lib/utils"; +import ButtonMenu, { ButtonMenuItemOptions } from '../buttonMenu'; import emoticonsDropdown from "../emoticonsDropdown"; import { Layouter, RectPart } from "../groupedLayout"; import PopupCreatePoll from "../popupCreatePoll"; +import { ripple } from '../ripple'; import Scrollable from "../scrollable"; import { toast } from "../toast"; import { wrapDocument, wrapReply } from "../wrappers"; const RECORD_MIN_TIME = 500; +const POSTING_MEDIA_NOT_ALLOWED = 'Posting media content isn\'t allowed in this group.'; export class ChatInput { public pageEl = document.getElementById('page-chats') as HTMLDivElement; @@ -32,12 +37,8 @@ export class ChatInput { private inputContainer = this.btnSend.parentElement.parentElement as HTMLDivElement; private chatInput = this.inputContainer.parentElement as HTMLDivElement; - public attachMenu: { - container?: HTMLButtonElement, - media?: HTMLDivElement, - document?: HTMLDivElement, - poll?: HTMLDivElement - } = {}; + public attachMenu: HTMLButtonElement; + private attachMenuButtons: (ButtonMenuItemOptions & {verify: (peerID: number) => boolean})[]; public attachMediaPopUp: { container?: HTMLDivElement, @@ -71,10 +72,49 @@ export class ChatInput { private scrollDiff = 0; constructor() { - this.attachMenu.container = document.getElementById('attach-file') as HTMLButtonElement; - this.attachMenu.media = this.attachMenu.container.querySelector('.menu-media') as HTMLDivElement; - this.attachMenu.document = this.attachMenu.container.querySelector('.menu-document') as HTMLDivElement; - this.attachMenu.poll = this.attachMenu.container.querySelector('.menu-poll') as HTMLDivElement; + this.attachMenu = document.getElementById('attach-file') as HTMLButtonElement; + + this.attachMenuButtons = [{ + icon: 'photo', + text: 'Photo or Video', + onClick: () => { + this.fileInput.setAttribute('accept', 'image/*, video/*'); + willAttach.type = 'media'; + this.fileInput.click(); + }, + verify: (peerID: number) => peerID > 0 || appChatsManager.hasRights(peerID, 'send', 'send_media') + }, { + icon: 'document', + text: 'Document', + onClick: () => { + this.fileInput.removeAttribute('accept'); + willAttach.type = 'document'; + this.fileInput.click(); + }, + verify: (peerID: number) => peerID > 0 || appChatsManager.hasRights(peerID, 'send', 'send_media') + }, { + icon: 'poll', + text: 'Poll', + onClick: () => { + new PopupCreatePoll().show(); + }, + verify: (peerID: number) => peerID < 0 && appChatsManager.hasRights(peerID, 'send', 'send_polls') + }]; + + /* this.attachMenu.addEventListener('mousedown', (e) => { + const hidden = this.attachMenu.querySelectorAll('.hide'); + if(hidden.length == this.attachMenuButtons.length) { + toast(POSTING_MEDIA_NOT_ALLOWED); + cancelEvent(e); + return false; + } + }, {passive: false, capture: true}); */ + + const attachBtnMenu = ButtonMenu(this.attachMenuButtons); + attachBtnMenu.classList.add('top-left'); + this.attachMenu.append(attachBtnMenu); + + ripple(this.attachMenu); this.attachMediaPopUp.container = this.pageEl.querySelector('.popup-send-photo') as HTMLDivElement; this.attachMediaPopUp.titleEl = this.attachMediaPopUp.container.querySelector('.popup-title') as HTMLDivElement; @@ -103,6 +143,19 @@ export class ChatInput { this.updateSendBtn(); + $rootScope.$on('peer_changed', (e) => { + const peerID = e.detail; + + const visible = this.attachMenuButtons.filter(button => { + const good = button.verify(peerID); + button.element.classList.toggle('hide', !good); + return good; + }); + + this.attachMenu.toggleAttribute('disabled', !visible.length); + this.updateSendBtn(); + }); + this.messageInput.addEventListener('keydown', (e: KeyboardEvent) => { if(e.key == 'Enter' && !isTouchSupported) { /* if(e.ctrlKey || e.metaKey) { @@ -429,24 +482,9 @@ export class ChatInput { attachFiles(Array.from(files)); }, false); - this.attachMenu.media.addEventListener('click', () => { - this.fileInput.setAttribute('accept', 'image/*, video/*'); - willAttach.type = 'media'; - this.fileInput.click(); - }); - - this.attachMenu.document.addEventListener('click', () => { - this.fileInput.removeAttribute('accept'); - willAttach.type = 'document'; - this.fileInput.click(); - }); - - this.attachMenu.poll.addEventListener('click', () => { - new PopupCreatePoll().show(); - }); - document.addEventListener('paste', (event) => { - if(!appImManager.peerID || this.attachMediaPopUp.container.classList.contains('active')) { + const peerID = $rootScope.selectedPeerID; + if(!peerID || this.attachMediaPopUp.container.classList.contains('active') || (peerID < 0 && !appChatsManager.hasRights(peerID, 'send', 'send_media'))) { return; } @@ -529,6 +567,11 @@ export class ChatInput { this.sendMessage(); } } else { + if($rootScope.selectedPeerID < 0 && !appChatsManager.hasRights($rootScope.selectedPeerID, 'send', 'send_media')) { + toast(POSTING_MEDIA_NOT_ALLOWED); + return; + } + this.chatInput.classList.add('is-locked'); this.recorder.start().then(() => { this.recordCanceled = false; diff --git a/src/components/emoticonsDropdown/index.ts b/src/components/emoticonsDropdown/index.ts index f9f14b60..5f370a35 100644 --- a/src/components/emoticonsDropdown/index.ts +++ b/src/components/emoticonsDropdown/index.ts @@ -1,16 +1,18 @@ -import LazyLoadQueue, { LazyLoadQueueIntersector } from "../lazyLoadQueue"; -import GifsTab from "./tabs/gifs"; +import { isTouchSupported } from "../../helpers/touchSupport"; +import appChatsManager from "../../lib/appManagers/appChatsManager"; +import appImManager from "../../lib/appManagers/appImManager"; +import { MOUNT_CLASS_TO } from "../../lib/mtproto/mtproto_config"; +import $rootScope from "../../lib/rootScope"; import { findUpClassName, findUpTag, whichChild } from "../../lib/utils"; -import { horizontalMenu } from "../horizontalMenu"; import animationIntersector from "../animationIntersector"; -import appSidebarRight from "../sidebarRight"; -import appImManager from "../../lib/appManagers/appImManager"; +import { horizontalMenu } from "../horizontalMenu"; +import LazyLoadQueue, { LazyLoadQueueIntersector } from "../lazyLoadQueue"; import Scrollable, { ScrollableX } from "../scrollable"; +import appSidebarRight from "../sidebarRight"; +import StickyIntersector from "../stickyIntersector"; import EmojiTab from "./tabs/emoji"; +import GifsTab from "./tabs/gifs"; import StickersTab from "./tabs/stickers"; -import StickyIntersector from "../stickyIntersector"; -import { MOUNT_CLASS_TO } from "../../lib/mtproto/mtproto_config"; -import { isTouchSupported } from "../../helpers/touchSupport"; export const EMOTICONSSTICKERGROUP = 'emoticons-dropdown'; @@ -53,6 +55,8 @@ export class EmoticonsDropdown { onOpenAfter: [] }; + private selectTab: ReturnType; + constructor() { this.element = document.getElementById('emoji-dropdown') as HTMLDivElement; @@ -113,13 +117,7 @@ export class EmoticonsDropdown { this.container = this.element.querySelector('.emoji-container .tabs-container') as HTMLDivElement; this.tabsEl = this.element.querySelector('.emoji-tabs') as HTMLUListElement; - horizontalMenu(this.tabsEl, this.container, (id) => { - animationIntersector.checkAnimations(true, EMOTICONSSTICKERGROUP); - - this.tabID = id; - this.searchButton.classList.toggle('hide', this.tabID == 0); - this.deleteBtn.classList.toggle('hide', this.tabID != 0); - }, () => { + this.selectTab = horizontalMenu(this.tabsEl, this.container, this.onSelectTabClick, () => { const tab = this.tabs[this.tabID]; if(tab.init) { tab.init(); @@ -156,10 +154,45 @@ export class EmoticonsDropdown { //appSidebarRight.stickersTab.init(); }); - (this.tabsEl.firstElementChild.children[1] as HTMLLIElement).click(); // set emoji tab + (this.tabsEl.children[1] as HTMLLIElement).click(); // set emoji tab this.tabs[0].init(); // onTransitionEnd не вызовется, т.к. это первая открытая вкладка + + $rootScope.$on('peer_changed', this.checkRights); + this.checkRights(); } + private onSelectTabClick = (id: number) => { + if(this.tabID == id) { + return; + } + + animationIntersector.checkAnimations(true, EMOTICONSSTICKERGROUP); + + this.tabID = id; + this.searchButton.classList.toggle('hide', this.tabID == 0); + this.deleteBtn.classList.toggle('hide', this.tabID != 0); + }; + + public checkRights = () => { + const peerID = $rootScope.selectedPeerID; + const children = this.tabsEl.children; + const tabsElements = Array.from(children) as HTMLElement[]; + + const canSendStickers = peerID > 0 || appChatsManager.hasRights(peerID, 'send', 'send_stickers'); + tabsElements[2].toggleAttribute('disabled', !canSendStickers); + + const canSendGifs = peerID > 0 || appChatsManager.hasRights(peerID, 'send', 'send_gifs'); + tabsElements[3].toggleAttribute('disabled', !canSendGifs); + + const active = this.tabsEl.querySelector('.active'); + if(active && whichChild(active) != 1 && (!canSendStickers || !canSendGifs)) { + this.selectTab(0); + this.onSelectTabClick(0); + active.classList.remove('active'); + children[1].classList.add('active'); + } + }; + public toggle = async(enable?: boolean) => { //if(!this.element) return; const willBeActive = (!!this.element.style.display && enable === undefined) || enable; @@ -241,7 +274,7 @@ export class EmoticonsDropdown { //animationIntersector.checkAnimations(false, EMOTICONSSTICKERGROUP); }; - public static menuOnClick = (menu: HTMLUListElement, scroll: Scrollable, menuScroll?: ScrollableX) => { + public static menuOnClick = (menu: HTMLElement, scroll: Scrollable, menuScroll?: ScrollableX) => { let prevId = 0; let jumpedTo = -1; @@ -284,7 +317,7 @@ export class EmoticonsDropdown { menu.addEventListener('click', (e) => { let target = e.target as HTMLElement; - target = findUpTag(target, 'LI'); + target = findUpClassName(target, 'menu-horizontal-div-item'); if(!target) { return; diff --git a/src/components/emoticonsDropdown/tabs/emoji.ts b/src/components/emoticonsDropdown/tabs/emoji.ts index aecd5d5e..06574271 100644 --- a/src/components/emoticonsDropdown/tabs/emoji.ts +++ b/src/components/emoticonsDropdown/tabs/emoji.ts @@ -1,10 +1,10 @@ -import { EmoticonsTab, EmoticonsDropdown } from ".."; -import Scrollable from "../../scrollable"; -import Config from "../../../lib/config"; -import { putPreloader } from "../../misc"; +import { EmoticonsDropdown, EmoticonsTab } from ".."; +import appImManager from "../../../lib/appManagers/appImManager"; import appStateManager from "../../../lib/appManagers/appStateManager"; +import Config from "../../../lib/config"; import { RichTextProcessor } from "../../../lib/richtextprocessor"; -import appImManager from "../../../lib/appManagers/appImManager"; +import { putPreloader } from "../../misc"; +import Scrollable from "../../scrollable"; import StickyIntersector from "../../stickyIntersector"; export default class EmojiTab implements EmoticonsTab { @@ -80,7 +80,7 @@ export default class EmojiTab implements EmoticonsTab { } //console.timeEnd('emojiParse'); - const menu = this.content.previousElementSibling.firstElementChild as HTMLUListElement; + const menu = this.content.previousElementSibling as HTMLElement; const emojiScroll = this.scroll = new Scrollable(this.content, 'EMOJI', null); //emojiScroll.setVirtualContainer(emojiScroll.container); diff --git a/src/components/emoticonsDropdown/tabs/stickers.ts b/src/components/emoticonsDropdown/tabs/stickers.ts index 1d580cc0..6237e564 100644 --- a/src/components/emoticonsDropdown/tabs/stickers.ts +++ b/src/components/emoticonsDropdown/tabs/stickers.ts @@ -1,20 +1,20 @@ -import emoticonsDropdown, { EmoticonsTab, EMOTICONSSTICKERGROUP, EmoticonsDropdown } from ".."; +import emoticonsDropdown, { EmoticonsDropdown, EMOTICONSSTICKERGROUP, EmoticonsTab } from ".."; +import { readBlobAsText } from "../../../helpers/blob"; +import mediaSizes from "../../../helpers/mediaSizes"; import { StickerSet } from "../../../layer"; -import Scrollable, { ScrollableX } from "../../scrollable"; -import { wrapSticker } from "../../wrappers"; -import appStickersManager from "../../../lib/appManagers/appStickersManager"; +import appDocsManager, { MyDocument } from "../../../lib/appManagers/appDocsManager"; import appDownloadManager from "../../../lib/appManagers/appDownloadManager"; -import { readBlobAsText } from "../../../helpers/blob"; +import appStickersManager from "../../../lib/appManagers/appStickersManager"; import lottieLoader from "../../../lib/lottieLoader"; -import { renderImageFromUrl, putPreloader } from "../../misc"; +import apiManager from "../../../lib/mtproto/mtprotoworker"; import { RichTextProcessor } from "../../../lib/richtextprocessor"; import $rootScope from "../../../lib/rootScope"; -import apiManager from "../../../lib/mtproto/mtprotoworker"; -import StickyIntersector from "../../stickyIntersector"; -import appDocsManager, {MyDocument} from "../../../lib/appManagers/appDocsManager"; import animationIntersector from "../../animationIntersector"; import { LazyLoadQueueRepeat } from "../../lazyLoadQueue"; -import mediaSizes from "../../../helpers/mediaSizes"; +import { putPreloader, renderImageFromUrl } from "../../misc"; +import Scrollable, { ScrollableX } from "../../scrollable"; +import StickyIntersector from "../../stickyIntersector"; +import { wrapSticker } from "../../wrappers"; export default class StickersTab implements EmoticonsTab { public content: HTMLElement; @@ -29,7 +29,7 @@ export default class StickersTab implements EmoticonsTab { private scroll: Scrollable; - private menu: HTMLUListElement; + private menu: HTMLElement; private mounted = false; @@ -110,18 +110,18 @@ export default class StickersTab implements EmoticonsTab { const categoryDiv = document.createElement('div'); categoryDiv.classList.add('sticker-category'); - const li = document.createElement('li'); - li.classList.add('btn-icon'); + const button = document.createElement('button'); + button.classList.add('btn-icon', 'menu-horizontal-div-item'); this.stickerSets[set.id] = { stickers: categoryDiv, - tab: li + tab: button }; if(prepend) { - this.menu.insertBefore(li, this.menu.firstElementChild.nextSibling); + this.menu.insertBefore(button, this.menu.firstElementChild.nextSibling); } else { - this.menu.append(li); + this.menu.append(button); } //stickersScroll.append(categoryDiv); @@ -142,7 +142,7 @@ export default class StickersTab implements EmoticonsTab { //.then(JSON.parse) .then(json => { lottieLoader.loadAnimationWorker({ - container: li, + container: button, loop: true, autoplay: false, animationData: json, @@ -155,14 +155,14 @@ export default class StickersTab implements EmoticonsTab { const image = new Image(); promise.then(blob => { renderImageFromUrl(image, URL.createObjectURL(blob), () => { - li.append(image); + button.append(image); }); }); } } else if(stickerSet.documents[0]._ != 'documentEmpty') { // as thumb will be used first sticker wrapSticker({ doc: stickerSet.documents[0], - div: li as any, + div: button as any, group: EMOTICONSSTICKERGROUP }); // kostil } @@ -230,7 +230,7 @@ export default class StickersTab implements EmoticonsTab { this.recentDiv.classList.add('sticker-category'); let menuWrapper = this.content.previousElementSibling as HTMLDivElement; - this.menu = menuWrapper.firstElementChild.firstElementChild as HTMLUListElement; + this.menu = menuWrapper.firstElementChild as HTMLUListElement; let menuScroll = new ScrollableX(menuWrapper); diff --git a/src/components/horizontalMenu.ts b/src/components/horizontalMenu.ts index 527c46b9..84905ef8 100644 --- a/src/components/horizontalMenu.ts +++ b/src/components/horizontalMenu.ts @@ -116,7 +116,7 @@ export function horizontalMenu(tabs: HTMLElement, content: HTMLElement, onClick? if(tabs) { const useStripe = !tabs.classList.contains('no-stripe'); - const tagName = 'LI';//tabs.firstElementChild.tagName; + const tagName = tabs.classList.contains('menu-horizontal-div') ? 'BUTTON' : 'LI';//tabs.firstElementChild.tagName; tabs.addEventListener('click', function(e) { let target = e.target as HTMLElement; diff --git a/src/components/popupCreatePoll.ts b/src/components/popupCreatePoll.ts index 8064d18c..5c5ba53a 100644 --- a/src/components/popupCreatePoll.ts +++ b/src/components/popupCreatePoll.ts @@ -179,7 +179,9 @@ export default class PopupCreatePoll extends PopupElement { }; //poll.id = randomIDS; - const inputMediaPoll = appPollsManager.getInputMediaPoll(poll, this.correctAnswers, this.quizSolutionInput ? this.quizSolutionInput.value : undefined); + const inputMediaPoll = appPollsManager.getInputMediaPoll(poll, this.correctAnswers, this.quizSolutionInput.value || undefined); + + //console.log('Will try to create poll:', inputMediaPoll); appMessagesManager.sendOther($rootScope.selectedPeerID, inputMediaPoll); }; diff --git a/src/index.hbs b/src/index.hbs index 2dba5788..f80d8e7a 100644 --- a/src/index.hbs +++ b/src/index.hbs @@ -513,13 +513,7 @@
- +
@@ -537,27 +531,23 @@
-
@@ -569,15 +559,13 @@
- + diff --git a/src/lib/appManagers/appChatsManager.ts b/src/lib/appManagers/appChatsManager.ts index 5005cab2..19aeae5b 100644 --- a/src/lib/appManagers/appChatsManager.ts +++ b/src/lib/appManagers/appChatsManager.ts @@ -1,4 +1,4 @@ -import { InputChannel, InputChatPhoto, InputPeer, Updates } from "../../layer"; +import { ChatBannedRights, InputChannel, InputChatPhoto, InputPeer, Updates } from "../../layer"; import apiManager from '../mtproto/mtprotoworker'; import { RichTextProcessor } from "../richtextprocessor"; import $rootScope from "../rootScope"; @@ -151,7 +151,7 @@ export class AppChatsManager { return this.chats[id] || {_: 'chatEmpty', id: id, deleted: true, access_hash: this.channelAccess[id]}; } - public hasRights(id: number, action: ChatRights) { + public hasRights(id: number, action: ChatRights, flag?: keyof ChatBannedRights['pFlags']) { const chat = this.getChat(id); if(chat._ == 'chatEmpty') return false; @@ -171,12 +171,16 @@ export class AppChatsManager { switch(action) { // good case 'send': { - if(chat._ == 'channel' && - !chat.pFlags.megagroup && - !myFlags.post_messages) { + if(flag && myFlags[flag]) { return false; } + if(chat._ == 'channel') { + if((!chat.pFlags.megagroup && !myFlags.post_messages)) { + return false; + } + } + break; } diff --git a/src/lib/rootScope.ts b/src/lib/rootScope.ts index 8587aeb7..1b1b9524 100644 --- a/src/lib/rootScope.ts +++ b/src/lib/rootScope.ts @@ -1,13 +1,14 @@ import type { StickerSet } from "../layer"; import type { MyDocument } from "./appManagers/appDocsManager"; -import type { Poll, PollResults } from "./appManagers/appPollsManager"; import type { AppMessagesManager, Dialog, MyDialogFilter } from "./appManagers/appMessagesManager"; +import type { Poll, PollResults } from "./appManagers/appPollsManager"; import { MOUNT_CLASS_TO } from "./mtproto/mtproto_config"; type BroadcastEvents = { 'user_update': number, 'user_auth': {dcID?: number, id: number}, 'peer_changed': number, + 'peer_pinned_message': number, 'filter_delete': MyDialogFilter, 'filter_update': MyDialogFilter, @@ -48,7 +49,6 @@ type BroadcastEvents = { //'contacts_update': any, 'avatar_update': number, 'chat_full_update': number, - 'peer_pinned_message': number, 'poll_update': {poll: Poll, results: PollResults}, 'chat_update': number, 'stateSynchronized': void, diff --git a/src/scss/partials/_chat.scss b/src/scss/partials/_chat.scss index 476fb16b..a9db1931 100644 --- a/src/scss/partials/_chat.scss +++ b/src/scss/partials/_chat.scss @@ -342,7 +342,7 @@ } .btn-icon { - transition: .2s color, background-color .2s; + transition: .2s color, background-color .2s, .2s opacity; } #btn-record-cancel, #btn-send { diff --git a/src/scss/partials/_emojiDropdown.scss b/src/scss/partials/_emojiDropdown.scss index b4f683be..3fdac182 100644 --- a/src/scss/partials/_emojiDropdown.scss +++ b/src/scss/partials/_emojiDropdown.scss @@ -34,7 +34,7 @@ margin-left: -.5rem; } */ - > .menu-horizontal { + > .menu-horizontal-div { //font-weight: 500; margin-top: 2px; border: none; @@ -52,9 +52,7 @@ } .emoji-tabs { - ul { - justify-content: center; - } + justify-content: center; &-search { position: absolute; @@ -113,7 +111,7 @@ .emoji-padding.active { @include respond-to(handhelds) { - .menu-horizontal li { + .menu-horizontal-div .menu-horizontal-div-item { flex: unset; padding: 0; } @@ -143,7 +141,7 @@ } .emoji-padding, .stickers-padding { - .menu-horizontal { + .menu-horizontal-div { height: 48px; border-bottom: none; padding: 2px; @@ -151,7 +149,7 @@ box-shadow: 0px 1px 5px -1px rgba(0, 0, 0, 0.21); z-index: 4; - li { + .menu-horizontal-div-item { margin: 0; } } @@ -248,12 +246,6 @@ } } } - - .menu-horizontal { - li { - border-radius: 50%; - } - } #content-stickers { .scrollable { @@ -261,12 +253,12 @@ } } - .menu-horizontal { + .menu-horizontal-div { width: 100%; height: 48px; box-shadow: 0px -2px 5px -1px rgba(0, 0, 0, .21); - li { + .menu-horizontal-div-item { font-size: 1.5rem; margin: 0 12px; width: 48px; @@ -285,10 +277,10 @@ box-shadow: 0px 1px 5px -1px rgba(0, 0, 0, .21); } - .menu-horizontal { + .menu-horizontal-div { box-shadow: none; - & li { + .menu-horizontal-div-item { height: 48px; width: 48px; padding: 0; @@ -305,7 +297,7 @@ //border-top: 1px solid $lightgrey; } - li { + .menu-horizontal-div-item { /* width: calc(100% / 7); */ flex: 0 0 auto; diff --git a/src/scss/partials/_slider.scss b/src/scss/partials/_slider.scss index 382dd442..baf9da89 100644 --- a/src/scss/partials/_slider.scss +++ b/src/scss/partials/_slider.scss @@ -1,5 +1,80 @@ $slider-time: .25s; +.menu-horizontal-div { + width: 100%; + display: flex; + justify-content: space-around; + align-items: center; + position: relative; + z-index: 2; + flex-direction: row; + + color: $color-gray; + border-bottom: 1px solid $lightgrey; + position: relative; + + &-item { + display: inline-block; + padding: .75rem 1rem; + cursor: pointer; + text-align: center; + flex: 1 1 auto; + //flex: 0 0 auto; + //overflow: hidden; + user-select: none; + // font-size: 1rem; + font-size: 14px; + font-weight: 500; + position: relative; + border-top-left-radius: 6px; + border-top-right-radius: 6px; + + html.no-touch & { + background-color: transparent; + transition: background-color .15s ease-in-out; + + &:hover { + background-color: var(--color-gray-hover); + } + } + + > span { + position: relative; + display: inline-flex; + align-items: center; + } + + &.active { + color: $color-blue; + + i { + opacity: 1; + } + } + } + + i { + position: absolute; + bottom: calc(-.625rem - 2px); + left: 0; + opacity: 0; + background-color: $color-blue; + height: .1875rem; + width: 100%; + border-radius: .1875rem .1875rem 0 0; + pointer-events: none; + padding-right: .5rem; + margin-left: -.25rem; + box-sizing: content-box; + transform-origin: left; + z-index: 1; + + &.animate { + transition: transform $slider-time; + } + } +} + .menu-horizontal { color: $color-gray; border-bottom: 1px solid $lightgrey; diff --git a/src/scss/style.scss b/src/scss/style.scss index 339edbc0..cbba79dc 100644 --- a/src/scss/style.scss +++ b/src/scss/style.scss @@ -302,7 +302,7 @@ input, textarea { font-size: 1.5rem; line-height: 1.5rem; border-radius: 50%; - transition: background-color .15s ease-in-out; + transition: background-color .15s ease-in-out, opacity .15s ease-in-out; color: $color-gray; cursor: pointer; background-color: transparent; @@ -323,6 +323,11 @@ input, textarea { html.no-touch &:hover { background-color: var(--color-gray-hover); } + + &:disabled { + pointer-events: none !important; + opacity: .25; + } } .btn-icon:disabled {