Prepare for chat multiselect:
Chat selection by context menu Clear selection Fix highlighting bubbles Fix highlight first unread bubble
This commit is contained in:
parent
2289fbff07
commit
1909fe5c60
@ -11,8 +11,8 @@ import { PopupButton } from "../popup";
|
||||
import PopupPeer from "../popupPeer";
|
||||
import appSidebarRight from "../sidebarRight";
|
||||
|
||||
export class ChatContextMenu {
|
||||
private buttons: (ButtonMenuItemOptions & {verify: (peerID: number, msgID: number, target: HTMLElement) => boolean})[];
|
||||
export default class ChatContextMenu {
|
||||
private buttons: (ButtonMenuItemOptions & {verify: () => boolean, notDirect?: () => boolean})[];
|
||||
private element: HTMLElement;
|
||||
|
||||
private target: HTMLElement;
|
||||
@ -26,22 +26,19 @@ export class ChatContextMenu {
|
||||
this.init = null;
|
||||
}
|
||||
|
||||
let bubble: HTMLElement = null;
|
||||
let bubble: HTMLElement, bubbleContainer: HTMLElement;
|
||||
|
||||
try {
|
||||
bubble = findUpClassName(e.target, 'bubble__container');
|
||||
bubbleContainer = findUpClassName(e.target, 'bubble__container');
|
||||
bubble = bubbleContainer ? bubbleContainer.parentElement : findUpClassName(e.target, 'bubble');
|
||||
} catch(e) {}
|
||||
|
||||
if(!bubble) return;
|
||||
|
||||
if(e instanceof MouseEvent) e.preventDefault();
|
||||
if(this.element.classList.contains('active')) {
|
||||
return false;
|
||||
}
|
||||
if(e instanceof MouseEvent) e.cancelBubble = true;
|
||||
|
||||
bubble = bubble.parentElement as HTMLDivElement; // bc container
|
||||
|
||||
const msgID = +bubble.dataset.mid;
|
||||
if(!msgID) return;
|
||||
|
||||
@ -57,7 +54,9 @@ export class ChatContextMenu {
|
||||
}
|
||||
|
||||
this.buttons.forEach(button => {
|
||||
const good = button.verify(this.peerID, this.msgID, this.target);
|
||||
const good = bubbleContainer ?
|
||||
button.verify() :
|
||||
button.notDirect && button.notDirect() && button.verify();
|
||||
button.element.classList.toggle('hide', !good);
|
||||
});
|
||||
|
||||
@ -77,58 +76,75 @@ export class ChatContextMenu {
|
||||
icon: 'reply',
|
||||
text: 'Reply',
|
||||
onClick: this.onReplyClick,
|
||||
verify: (peerID: number, msgID: number) => (peerID > 0 || appChatsManager.hasRights(-peerID, 'send')) && msgID > 0
|
||||
verify: () => (this.peerID > 0 || appChatsManager.hasRights(-this.peerID, 'send')) && this.msgID > 0
|
||||
}, {
|
||||
icon: 'edit',
|
||||
text: 'Edit',
|
||||
onClick: this.onEditClick,
|
||||
verify: (peerID: number, msgID: number) => appMessagesManager.canEditMessage(msgID, 'text')
|
||||
verify: () => appMessagesManager.canEditMessage(this.msgID, 'text')
|
||||
}, {
|
||||
icon: 'copy',
|
||||
text: 'Copy',
|
||||
onClick: this.onCopyClick,
|
||||
verify: (peerID: number, msgID: number) => !!appMessagesManager.getMessage(msgID).message
|
||||
verify: () => !!appMessagesManager.getMessage(this.msgID).message
|
||||
}, {
|
||||
icon: 'pin',
|
||||
text: 'Pin',
|
||||
onClick: this.onPinClick,
|
||||
verify: (peerID: number, msgID: number) => {
|
||||
const message = appMessagesManager.getMessage(msgID);
|
||||
return msgID > 0 && message._ != 'messageService' && appImManager.pinnedMsgID != msgID && (peerID == $rootScope.myID || (peerID < 0 && appChatsManager.hasRights(-peerID, 'pin')));
|
||||
verify: () => {
|
||||
const message = appMessagesManager.getMessage(this.msgID);
|
||||
return this.msgID > 0 && message._ != 'messageService' && appImManager.pinnedMsgID != this.msgID && (this.peerID == $rootScope.myID || (this.peerID < 0 && appChatsManager.hasRights(-this.peerID, 'pin')));
|
||||
}
|
||||
}, {
|
||||
icon: 'unpin',
|
||||
text: 'Unpin',
|
||||
onClick: this.onUnpinClick,
|
||||
verify: (peerID: number, msgID: number) => appImManager.pinnedMsgID == msgID && (peerID == $rootScope.myID || (peerID < 0 && appChatsManager.hasRights(-peerID, 'pin')))
|
||||
verify: () => appImManager.pinnedMsgID == this.msgID && (this.peerID == $rootScope.myID || (this.peerID < 0 && appChatsManager.hasRights(-this.peerID, 'pin')))
|
||||
}, {
|
||||
icon: 'revote',
|
||||
text: 'Revote',
|
||||
onClick: this.onRetractVote,
|
||||
verify: (peerID: number, msgID) => {
|
||||
const message = appMessagesManager.getMessage(msgID);
|
||||
verify: () => {
|
||||
const message = appMessagesManager.getMessage(this.msgID);
|
||||
const poll = message.media?.poll as Poll;
|
||||
return poll && poll.chosenIndexes.length && !poll.pFlags.closed && !poll.pFlags.quiz;
|
||||
}
|
||||
}, {
|
||||
icon: 'lock',
|
||||
icon: 'stop',
|
||||
text: 'Stop poll',
|
||||
onClick: this.onStopPoll,
|
||||
verify: (peerID: number, msgID) => {
|
||||
const message = appMessagesManager.getMessage(msgID);
|
||||
verify: () => {
|
||||
const message = appMessagesManager.getMessage(this.msgID);
|
||||
const poll = message.media?.poll;
|
||||
return appMessagesManager.canEditMessage(msgID, 'poll') && poll && !poll.pFlags.closed && msgID > 0;
|
||||
return appMessagesManager.canEditMessage(this.msgID, 'poll') && poll && !poll.pFlags.closed && this.msgID > 0;
|
||||
}
|
||||
}, {
|
||||
icon: 'forward',
|
||||
text: 'Forward',
|
||||
onClick: this.onForwardClick,
|
||||
verify: (peerID: number, msgID: number) => msgID > 0
|
||||
verify: () => this.msgID > 0
|
||||
}, {
|
||||
icon: 'revote',
|
||||
text: 'Select',
|
||||
onClick: this.onSelectClick,
|
||||
verify: () => {
|
||||
const message = appMessagesManager.getMessage(this.msgID);
|
||||
return !message.action && !appImManager.chatSelection.selectedMids.has(this.msgID);
|
||||
},
|
||||
notDirect: () => true
|
||||
}, {
|
||||
icon: 'revote',
|
||||
text: 'Clear selection',
|
||||
onClick: this.onClearSelectionClick,
|
||||
verify: () => {
|
||||
return appImManager.chatSelection.selectedMids.has(this.msgID);
|
||||
},
|
||||
notDirect: () => appImManager.chatSelection.selectedMids.has(this.msgID)
|
||||
}, {
|
||||
icon: 'delete danger',
|
||||
text: 'Delete',
|
||||
onClick: this.onDeleteClick,
|
||||
verify: (peerID: number, msgID: number) => peerID > 0 || appMessagesManager.getMessage(msgID).fromID == $rootScope.myID || appChatsManager.hasRights(-peerID, 'deleteRevoke')
|
||||
verify: () => this.peerID > 0 || appMessagesManager.getMessage(this.msgID).fromID == $rootScope.myID || appChatsManager.hasRights(-this.peerID, 'deleteRevoke')
|
||||
}];
|
||||
|
||||
this.element = ButtonMenu(this.buttons);
|
||||
@ -193,6 +209,14 @@ export class ChatContextMenu {
|
||||
appSidebarRight.forwardTab.open([this.msgID]);
|
||||
};
|
||||
|
||||
private onSelectClick = () => {
|
||||
appImManager.chatSelection.toggleByBubble(findUpClassName(this.target, 'bubble'));
|
||||
};
|
||||
|
||||
private onClearSelectionClick = () => {
|
||||
appImManager.chatSelection.cancelSelection();
|
||||
};
|
||||
|
||||
private onDeleteClick = () => {
|
||||
const peerID = $rootScope.selectedPeerID;
|
||||
const firstName = appPeersManager.getPeerTitle(peerID, false, true);
|
||||
|
106
src/components/chat/selection.ts
Normal file
106
src/components/chat/selection.ts
Normal file
@ -0,0 +1,106 @@
|
||||
import type { AppImManager } from "../../lib/appManagers/appImManager";
|
||||
import type { AppMessagesManager } from "../../lib/appManagers/appMessagesManager";
|
||||
import CheckboxField from "../checkbox";
|
||||
|
||||
export default class ChatSelection {
|
||||
public selectedMids: Set<number> = new Set();
|
||||
public isSelecting = false;
|
||||
|
||||
constructor(private appImManager: AppImManager, private appMessagesManager: AppMessagesManager) {
|
||||
|
||||
}
|
||||
|
||||
public toggleBubbleCheckbox(bubble: HTMLElement, show: boolean) {
|
||||
const hasCheckbox = !!this.getCheckboxInputFromBubble(bubble);
|
||||
if(show) {
|
||||
if(hasCheckbox) return;
|
||||
|
||||
const checkboxField = CheckboxField('', bubble.dataset.mid, true);
|
||||
checkboxField.label.classList.add('bubble-select-checkbox');
|
||||
|
||||
// * if it is a render of new message
|
||||
const mid = +bubble.dataset.mid;
|
||||
if(this.selectedMids.has(mid)) {
|
||||
checkboxField.input.checked = true;
|
||||
bubble.classList.add('is-selected');
|
||||
}
|
||||
|
||||
bubble.append(checkboxField.label);
|
||||
} else if(hasCheckbox) {
|
||||
bubble.lastElementChild.remove();
|
||||
}
|
||||
}
|
||||
|
||||
public getCheckboxInputFromBubble(bubble: HTMLElement) {
|
||||
return bubble.lastElementChild.tagName == 'LABEL' && bubble.lastElementChild.firstElementChild as HTMLInputElement;
|
||||
}
|
||||
|
||||
public toggleSelection() {
|
||||
const wasSelecting = this.isSelecting;
|
||||
this.isSelecting = this.selectedMids.size > 0;
|
||||
|
||||
if(wasSelecting == this.isSelecting) return;
|
||||
|
||||
this.appImManager.bubblesContainer.classList.toggle('is-selecting', !!this.selectedMids.size);
|
||||
|
||||
for(const mid in this.appImManager.bubbles) {
|
||||
const bubble = this.appImManager.bubbles[mid];
|
||||
this.toggleBubbleCheckbox(bubble, this.isSelecting);
|
||||
}
|
||||
}
|
||||
|
||||
public cancelSelection() {
|
||||
for(const mid of this.selectedMids) {
|
||||
const bubble = this.appImManager.bubbles[mid];
|
||||
if(bubble) {
|
||||
this.toggleByBubble(bubble);
|
||||
}
|
||||
}
|
||||
|
||||
this.selectedMids.clear();
|
||||
this.toggleSelection();
|
||||
}
|
||||
|
||||
public cleanup() {
|
||||
this.isSelecting = false;
|
||||
this.selectedMids.clear();
|
||||
this.appImManager.bubblesContainer.classList.remove('is-selecting');
|
||||
}
|
||||
|
||||
public toggleByBubble(bubble: HTMLElement) {
|
||||
const mid = +bubble.dataset.mid;
|
||||
const mids = this.appMessagesManager.getMidsByMid(mid);
|
||||
|
||||
const found = mids.find(mid => this.selectedMids.has(mid));
|
||||
if(found) {
|
||||
mids.forEach(mid => this.selectedMids.delete(mid));
|
||||
} else {
|
||||
mids.forEach(mid => this.selectedMids.add(mid));
|
||||
}
|
||||
|
||||
this.toggleBubbleCheckbox(bubble, true);
|
||||
const input = this.getCheckboxInputFromBubble(bubble);
|
||||
input.checked = !found;
|
||||
|
||||
this.toggleSelection();
|
||||
if(found) {
|
||||
bubble.classList.add('backwards');
|
||||
bubble.dataset.timeout = '' + setTimeout(() => {
|
||||
delete bubble.dataset.timeout;
|
||||
bubble.classList.remove('backwards', 'is-selected');
|
||||
}, 200);
|
||||
} else {
|
||||
bubble.classList.remove('backwards');
|
||||
const timeout = bubble.dataset.timeout;
|
||||
if(timeout !== undefined) {
|
||||
clearTimeout(+timeout);
|
||||
}
|
||||
|
||||
bubble.classList.add('is-selected');
|
||||
}
|
||||
}
|
||||
|
||||
public selectMessage(mid: number) {
|
||||
|
||||
}
|
||||
}
|
@ -1,13 +1,16 @@
|
||||
const CheckboxField = (text: string, name: string) => {
|
||||
const CheckboxField = (text: string, name: string, round = false) => {
|
||||
const label = document.createElement('label');
|
||||
label.classList.add('checkbox-field');
|
||||
label.classList.add(round ? 'checkbox-field-round' : 'checkbox-field');
|
||||
|
||||
const input = document.createElement('input');
|
||||
input.type = 'checkbox';
|
||||
input.id = 'input-' + name;
|
||||
|
||||
const span = document.createElement('span');
|
||||
span.innerText = text;
|
||||
span.classList.add('checkbox-caption');
|
||||
if(text) {
|
||||
span.innerText = text;
|
||||
}
|
||||
|
||||
label.append(input, span);
|
||||
|
||||
|
@ -716,7 +716,7 @@ export function wrapAlbum({groupID, attachmentDiv, middleware, uploading, lazyLo
|
||||
const items: {size: PhotoSize.photoSize, media: any, message: any}[] = [];
|
||||
|
||||
// !higher msgID will be the FIRST in album
|
||||
const storage = Object.keys(appMessagesManager.groupedMessagesStorage[groupID]).map(id => +id).sort((a, b) => a - b);
|
||||
const storage = appMessagesManager.getMidsByAlbum(groupID);
|
||||
for(const mid of storage) {
|
||||
const m = appMessagesManager.getMessage(mid);
|
||||
const media = m.media.photo || m.media.document;
|
||||
|
@ -238,6 +238,8 @@ class AppDocsManager {
|
||||
|
||||
if(!thumb.url) {
|
||||
if('bytes' in thumb) {
|
||||
// * exclude from state
|
||||
defineNotNumerableProperties(thumb, ['url']);
|
||||
thumb.url = appPhotosManager.getPreviewURLFromBytes(thumb.bytes, !!doc.sticker);
|
||||
} else {
|
||||
//return this.getFileURL(doc, false, thumb);
|
||||
|
@ -5,12 +5,14 @@ import AudioElement from '../../components/audio';
|
||||
import AvatarElement from '../../components/avatar';
|
||||
import BubbleGroups from '../../components/bubbleGroups';
|
||||
import { ChatAudio } from '../../components/chat/audio';
|
||||
import { ChatContextMenu } from '../../components/chat/contextMenu';
|
||||
import ChatContextMenu from '../../components/chat/contextMenu';
|
||||
import { ChatInput } from '../../components/chat/input';
|
||||
import { MessageRender } from '../../components/chat/messageRender';
|
||||
import PinnedContainer from '../../components/chat/pinnedContainer';
|
||||
import ReplyContainer from '../../components/chat/replyContainer';
|
||||
import { ChatSearch } from '../../components/chat/search';
|
||||
import ChatSelection from '../../components/chat/selection';
|
||||
import CheckboxField from '../../components/checkbox';
|
||||
import { horizontalMenu } from '../../components/horizontalMenu';
|
||||
import LazyLoadQueue from '../../components/lazyLoadQueue';
|
||||
import { formatPhoneNumber, parseMenuButtonsTo } from '../../components/misc';
|
||||
@ -74,6 +76,7 @@ export class AppImManager {
|
||||
|
||||
public chatInputC: ChatInput;
|
||||
public chatAudio: ChatAudio;
|
||||
public chatSelection: ChatSelection;
|
||||
|
||||
private menuButtons: {
|
||||
search: HTMLButtonElement
|
||||
@ -168,6 +171,8 @@ export class AppImManager {
|
||||
|
||||
parseMenuButtonsTo(this.menuButtons, this.columnEl.querySelector('.chat-more-button').firstElementChild.children);
|
||||
|
||||
this.chatSelection = new ChatSelection(this, appMessagesManager/* this.bubblesContainer, this.bubbles */);
|
||||
|
||||
this.chatAudio = new ChatAudio();
|
||||
this.chatInfo.nextElementSibling.prepend(this.chatAudio.divAndCaption.container);
|
||||
|
||||
@ -430,6 +435,14 @@ export class AppImManager {
|
||||
return;
|
||||
}
|
||||
|
||||
// ! Trusted - due to audio autoclick
|
||||
if(this.chatSelection.isSelecting && !bubble.classList.contains('service') && e.isTrusted) {
|
||||
cancelEvent(e);
|
||||
//console.log('bubble click', e);
|
||||
this.chatSelection.toggleByBubble(bubble);
|
||||
return;
|
||||
}
|
||||
|
||||
let contactDiv: HTMLElement = findUpClassName(target, 'contact');
|
||||
if(contactDiv) {
|
||||
this.setPeer(+contactDiv.dataset.peerID);
|
||||
@ -1261,6 +1274,8 @@ export class AppImManager {
|
||||
let peerID = this.peerID;
|
||||
this.peerChanged = true;
|
||||
|
||||
this.chatSelection.cleanup();
|
||||
|
||||
this.avatarEl.setAttribute('peer', '' + this.peerID);
|
||||
this.avatarEl.update();
|
||||
|
||||
@ -1381,13 +1396,13 @@ export class AppImManager {
|
||||
public highlightBubble(element: HTMLElement) {
|
||||
if(element.dataset.timeout) {
|
||||
clearTimeout(+element.dataset.timeout);
|
||||
element.classList.remove('is-selected');
|
||||
element.classList.remove('is-highlighted');
|
||||
void element.offsetWidth; // reflow
|
||||
}
|
||||
|
||||
element.classList.add('is-selected');
|
||||
element.classList.add('is-highlighted');
|
||||
element.dataset.timeout = '' + setTimeout(() => {
|
||||
element.classList.remove('is-selected');
|
||||
element.classList.remove('is-highlighted');
|
||||
delete element.dataset.timeout;
|
||||
}, 2000);
|
||||
}
|
||||
@ -1575,7 +1590,7 @@ export class AppImManager {
|
||||
bubble.appendChild(bubbleContainer);
|
||||
this.bubbles[+message.mid] = bubble;
|
||||
} else {
|
||||
const save = ['is-selected'];
|
||||
const save = ['is-highlighted'];
|
||||
const wasClassNames = bubble.className.split(' ');
|
||||
const classNames = ['bubble'].concat(save.filter(c => wasClassNames.includes(c)));
|
||||
bubble.className = classNames.join(' ');
|
||||
@ -1592,6 +1607,10 @@ export class AppImManager {
|
||||
|
||||
bubble.dataset.mid = message.mid;
|
||||
|
||||
if(this.chatSelection.isSelecting) {
|
||||
this.chatSelection.toggleBubbleCheckbox(bubble, true);
|
||||
}
|
||||
|
||||
if(message._ == 'messageService') {
|
||||
let action = message.action;
|
||||
let _ = action._;
|
||||
|
@ -2195,6 +2195,16 @@ export class AppMessagesManager {
|
||||
});
|
||||
}
|
||||
|
||||
public getMidsByAlbum(grouped_id: string) {
|
||||
return Object.keys(this.groupedMessagesStorage[grouped_id]).map(id => +id).sort((a, b) => a - b);
|
||||
}
|
||||
|
||||
public getMidsByMid(mid: number) {
|
||||
const message = this.messagesStorage[mid];
|
||||
if(message?.grouped_id) return this.getMidsByAlbum(message.grouped_id);
|
||||
else return [mid];
|
||||
}
|
||||
|
||||
public saveMessages(messages: any[], options: {
|
||||
isEdited?: boolean
|
||||
} = {}) {
|
||||
|
@ -154,7 +154,7 @@ export class AppPhotosManager {
|
||||
}
|
||||
|
||||
public getPreviewURLFromThumb(thumb: PhotoSize.photoCachedSize | PhotoSize.photoStrippedSize, isSticker = false) {
|
||||
return thumb.url ?? (thumb.url = this.getPreviewURLFromBytes(thumb.bytes, isSticker));
|
||||
return thumb.url ?? (defineNotNumerableProperties(thumb, ['url']), thumb.url = this.getPreviewURLFromBytes(thumb.bytes, isSticker));
|
||||
}
|
||||
|
||||
public setAttachmentPreview(bytes: Uint8Array | number[], element: HTMLElement | SVGForeignObjectElement, isSticker = false, background = false) {
|
||||
|
@ -1,8 +1,12 @@
|
||||
$btn-send-margin: .5625rem;
|
||||
$chat-input-size: 3rem;
|
||||
$chat-input-handhelds-size: 2.875rem;
|
||||
|
||||
#bubble-contextmenu > div {
|
||||
padding: 0 84px 0 16px;
|
||||
padding: 0 5.25 0 1rem;
|
||||
|
||||
@include respond-to(handhelds) {
|
||||
padding: 0 60px 0 16px;
|
||||
padding: 0 3.75rem 0 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,10 +14,10 @@
|
||||
width: 100%;
|
||||
background-color: #fff;
|
||||
user-select: none;
|
||||
box-shadow: 0px 1px 5px -1px rgba(0,0,0,0.21);
|
||||
box-shadow: 0px 1px 5px -1px rgba(0, 0, 0, .21);
|
||||
z-index: 1;
|
||||
min-height: 56px;
|
||||
max-height: 56px;
|
||||
min-height: 3.5rem;
|
||||
max-height: 3.5rem;
|
||||
// border-bottom: 1px solid #DADCE0;
|
||||
|
||||
@include respond-to(handhelds) {
|
||||
@ -248,6 +252,7 @@
|
||||
width: 100%;
|
||||
padding: 0 .5rem;
|
||||
flex: 0 0 auto;
|
||||
position: relative;
|
||||
|
||||
@include respond-to(not-handhelds) {
|
||||
padding-left: 1rem;
|
||||
@ -311,9 +316,19 @@
|
||||
#btn-record-cancel {
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
transition: width .1s .1s, margin-right .1s .1s, visibility 0s .1s, opacity .1s 0s;
|
||||
transition: visibility 0s .1s, opacity .1s 0s;
|
||||
padding: 0;
|
||||
z-index: 3;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
|
||||
// here percents can be used since there are no other transforms
|
||||
transform: translateX(calc(-100% + #{-1rem + -$btn-send-margin}));
|
||||
|
||||
@include respond-to(handhelds) {
|
||||
transform: translateX(calc(-100% + #{-.5rem + -$btn-send-margin}));
|
||||
}
|
||||
}
|
||||
|
||||
.btn-send-container {
|
||||
@ -404,8 +419,7 @@
|
||||
#btn-record-cancel {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
margin-right: 9px;
|
||||
transition: width .1s, margin-right .1s, visibility 0s .1s, opacity .1s .1s;
|
||||
transition: visibility 0s .1s, opacity .1s .1s;
|
||||
}
|
||||
|
||||
// unlock
|
||||
@ -413,6 +427,14 @@
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
.input-message {
|
||||
width: calc(100% - #{$chat-input-size * 2 + $btn-send-margin * 3 + $btn-send-margin / 2});
|
||||
|
||||
@include respond-to(handhelds) {
|
||||
width: calc(100% - #{$chat-input-handhelds-size * 2 + $btn-send-margin * 2});
|
||||
}
|
||||
}
|
||||
|
||||
#attach-file {
|
||||
display: none;
|
||||
}
|
||||
@ -544,29 +566,30 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
width: calc(100% - 3.75rem);
|
||||
width: calc(100% - #{$chat-input-size + $btn-send-margin + $btn-send-margin / 2});
|
||||
justify-content: center;
|
||||
background-color: #fff;
|
||||
border-radius: 12px;
|
||||
border-bottom-right-radius: 0;
|
||||
box-shadow: 0 1px 2px 0 rgba(16, 35, 47, .07);
|
||||
margin-right: 9px;
|
||||
padding: 4.5px .5rem;
|
||||
/* padding: 3px .5rem 6px .5rem; */
|
||||
min-height: 54px;
|
||||
min-height: $chat-input-size;
|
||||
max-height: 30rem;
|
||||
caret-color: $button-primary-background;
|
||||
flex: 1;
|
||||
flex: 0 0 auto;
|
||||
position: relative;
|
||||
z-index: 3;
|
||||
transition: width .1s;
|
||||
|
||||
@include respond-to(handhelds) {
|
||||
min-height: 46px;
|
||||
width: calc(100% - #{$chat-input-handhelds-size + $btn-send-margin});
|
||||
min-height: $chat-input-handhelds-size;
|
||||
padding: .5px .5rem;
|
||||
}
|
||||
|
||||
@include respond-to(esg-bottom) {
|
||||
min-height: 46px;
|
||||
min-height: $chat-input-handhelds-size;
|
||||
padding: .5px .5rem;
|
||||
}
|
||||
|
||||
@ -984,6 +1007,19 @@
|
||||
}
|
||||
}
|
||||
|
||||
&.is-selecting {
|
||||
cursor: default !important;
|
||||
user-select: none;
|
||||
|
||||
.is-in .bubble__container {
|
||||
transform: translateX(2.5rem);
|
||||
}
|
||||
}
|
||||
|
||||
&.is-selecting > .scrollable::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
> .scrollable {
|
||||
height: auto;
|
||||
/* position: absolute;
|
||||
|
@ -45,21 +45,42 @@ $bubble-margin: .25rem;
|
||||
z-index: 1;
|
||||
margin: 0 auto;
|
||||
|
||||
&.is-selected {
|
||||
&.is-highlighted, &.is-selected, /* #bubbles.is-selecting */ & {
|
||||
&:after {
|
||||
position: absolute;
|
||||
left: -50%;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
/* top: 0;
|
||||
bottom: 0; */
|
||||
top: #{$bubble-margin / 2};
|
||||
bottom: -#{$bubble-margin / 2};
|
||||
content: " ";
|
||||
background-color: rgba(0, 132, 255, .3);
|
||||
animation: bubbleSelected 2s linear;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* &.is-highlighted, &.is-selected {
|
||||
&:not(.is-group-last):after {
|
||||
height: calc(100% + $bubble-margin);
|
||||
height: calc(100% + #{$bubble-margin / 2}) !important;
|
||||
}
|
||||
|
||||
& + &:not(.is-group-last) {
|
||||
&:after {
|
||||
top: .125rem !important;
|
||||
height: calc(100% - #{$bubble-margin / 2}) !important;
|
||||
}
|
||||
}
|
||||
} */
|
||||
|
||||
// ! if turn this on, there will be an empty space
|
||||
/* &.is-highlighted, &.is-selected {
|
||||
&.is-group-last:after {
|
||||
bottom: #{$bubble-margin / 2} !important;
|
||||
}
|
||||
} */
|
||||
|
||||
&.is-highlighted:after {
|
||||
background-color: rgba(0, 132, 255, .3);
|
||||
animation: bubbleSelected 2s linear;
|
||||
}
|
||||
|
||||
&.is-first-unread {
|
||||
@ -78,10 +99,30 @@ $bubble-margin: .25rem;
|
||||
font-weight: 500;
|
||||
font-size: 15px;
|
||||
background-color: rgba(255, 255, 255, 0.95);
|
||||
z-index: 2;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&.is-highlighted, &.is-selected {
|
||||
&:after {
|
||||
top: #{2rem + $bubble-margin} !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.is-selected:after, &.is-first-unread:before {
|
||||
&.is-selected {
|
||||
&:after {
|
||||
background-color: rgba(77, 142, 80, .4);
|
||||
animation: fade-in-opacity .2s linear forwards;
|
||||
}
|
||||
|
||||
&.backwards:after {
|
||||
animation: fade-in-backwards-opacity .2s linear forwards;
|
||||
}
|
||||
}
|
||||
|
||||
//&.is-highlighted:after, &.is-first-unread:before, &.is-selected:after {
|
||||
&:after, &:before {
|
||||
width: 200%;
|
||||
display: block;
|
||||
}
|
||||
@ -115,6 +156,45 @@ $bubble-margin: .25rem;
|
||||
}
|
||||
}
|
||||
|
||||
&-select-checkbox {
|
||||
z-index: 2;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
//bottom: .25rem; // * by avatar
|
||||
left: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
display: flex;
|
||||
|
||||
[type="checkbox"] {
|
||||
&:not(:checked) + .checkbox-caption:after {
|
||||
|
||||
}
|
||||
|
||||
&:checked + .checkbox-caption {
|
||||
&:after {
|
||||
background-color: #61c642;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.checkbox-caption {
|
||||
&:before {
|
||||
top: 7px !important;
|
||||
left: 3px !important;
|
||||
width: 6px !important;
|
||||
height: 11px !important;
|
||||
}
|
||||
|
||||
&:after {
|
||||
box-shadow: 0px 0px 3px 0px rgba(0, 0, 0, .4);
|
||||
border: 2px solid #fff !important;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__container {
|
||||
//min-width: 60px;
|
||||
min-width: 56px;
|
||||
@ -128,6 +208,7 @@ $bubble-margin: .25rem;
|
||||
width: max-content;
|
||||
height: fit-content;
|
||||
z-index: 2;
|
||||
transition: .2s transform;
|
||||
|
||||
@include respond-to(not-handhelds) {
|
||||
max-width: 85%;
|
||||
@ -408,6 +489,7 @@ $bubble-margin: .25rem;
|
||||
max-width: 100%;
|
||||
border-radius: inherit;
|
||||
overflow: hidden;
|
||||
user-select: none;
|
||||
|
||||
display: flex; // lol
|
||||
justify-content: center;
|
||||
|
195
src/scss/partials/_checkbox.scss
Normal file
195
src/scss/partials/_checkbox.scss
Normal file
@ -0,0 +1,195 @@
|
||||
.checkbox-field {
|
||||
margin: 1.25rem 0;
|
||||
display: block;
|
||||
text-align: left;
|
||||
padding: 0 1.125rem;
|
||||
/* font-weight: 500; */
|
||||
position: relative;
|
||||
|
||||
@include respond-to(handhelds) {
|
||||
margin-bottom: 27px;
|
||||
}
|
||||
}
|
||||
|
||||
.checkbox-field-round {
|
||||
display: block;
|
||||
text-align: left;
|
||||
|
||||
[type="checkbox"] {
|
||||
&:checked + .checkbox-caption {
|
||||
&:before {
|
||||
top: 5px;
|
||||
left: 0px;
|
||||
}
|
||||
|
||||
&:after {
|
||||
background-color: #4EA4F6;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.checkbox-caption:after {
|
||||
border-radius: 50%;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
border-color: #dadbdc;
|
||||
}
|
||||
}
|
||||
|
||||
.radio-field {
|
||||
display: block;
|
||||
position: relative;
|
||||
padding-left: 3.5rem;
|
||||
text-align: left;
|
||||
margin: 1.25rem 0;
|
||||
line-height: 1.5rem;
|
||||
cursor: pointer;
|
||||
|
||||
&.hidden-widget {
|
||||
cursor: default;
|
||||
|
||||
.radio-field-main {
|
||||
&::before, &::after {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> input {
|
||||
&:checked {
|
||||
& ~ .radio-field-main {
|
||||
&::before {
|
||||
border-color: $button-primary-background;
|
||||
}
|
||||
|
||||
&::after {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.radio-field-main {
|
||||
&::before, &::after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: .25rem;
|
||||
top: 50%;
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
&::before {
|
||||
border: 2px solid #8d969c;
|
||||
border-radius: 50%;
|
||||
background-color: white;
|
||||
opacity: 1;
|
||||
transition: border-color .1s ease, opacity .1s ease;
|
||||
}
|
||||
|
||||
&::after {
|
||||
left: .5625rem;
|
||||
width: .625rem;
|
||||
height: .625rem;
|
||||
border-radius: 50%;
|
||||
background: $button-primary-background;
|
||||
opacity: 0;
|
||||
transition: opacity .1s ease;
|
||||
}
|
||||
|
||||
/* .label {
|
||||
display: block;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.subLabel {
|
||||
display: block;
|
||||
font-size: 0.875rem;
|
||||
line-height: 1rem;
|
||||
color: var(--color-text-secondary);
|
||||
} */
|
||||
}
|
||||
}
|
||||
|
||||
[type="checkbox"], [type="radio"] {
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
opacity: 0;
|
||||
z-index: var(--z-below);
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
[type="checkbox"] {
|
||||
& + span {
|
||||
position: relative;
|
||||
padding-left: 3.5rem;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
height: 25px;
|
||||
line-height: 25px;
|
||||
user-select: none;
|
||||
transition: .2s opacity;
|
||||
|
||||
&:before, &:after {
|
||||
content: '';
|
||||
left: 0;
|
||||
position: absolute;
|
||||
transition: border .25s, background-color .25s, width .20s .1s, height .20s .1s, top .20s .1s, left .20s .1s;
|
||||
}
|
||||
|
||||
&:before {
|
||||
border-radius: 2px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&:after {
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
z-index: 0;
|
||||
border: 2px solid $button-primary-background;
|
||||
border-radius: 3px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
}
|
||||
|
||||
&:not(:checked) + span:before {
|
||||
width: 0;
|
||||
height: 0;
|
||||
border: 2px solid transparent;
|
||||
left: 6px;
|
||||
top: 10px;
|
||||
transform: rotateZ(45deg);
|
||||
transform-origin: 100% 100%;
|
||||
}
|
||||
|
||||
&:checked + span:before {
|
||||
top: 4px;
|
||||
left: -1px;
|
||||
width: 8px;
|
||||
height: 14px;
|
||||
border-top: 2px solid transparent;
|
||||
border-left: 2px solid transparent;
|
||||
border-right: 2px solid #fff;
|
||||
border-bottom: 2px solid #fff;
|
||||
transform: rotateZ(45deg);
|
||||
transform-origin: 100% 100%;
|
||||
}
|
||||
|
||||
&:not(:checked) + span:after {
|
||||
background-color: transparent;
|
||||
border-color: #8d969c;
|
||||
}
|
||||
|
||||
&:checked + span:after {
|
||||
background-color: $button-primary-background;
|
||||
}
|
||||
|
||||
&:disabled + span {
|
||||
cursor: default;
|
||||
opacity: .25;
|
||||
}
|
||||
}
|
@ -105,6 +105,7 @@
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
user-select: none;
|
||||
|
||||
&-ico, &-download {
|
||||
position: absolute;
|
||||
|
@ -582,11 +582,6 @@
|
||||
padding-left: 38px;
|
||||
}
|
||||
}
|
||||
|
||||
[type="checkbox"]:checked+span:before {
|
||||
top: 5px;
|
||||
left: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -670,22 +665,33 @@
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
[type="checkbox"]+span {
|
||||
[type="checkbox"] + span {
|
||||
padding-left: 26px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.checkbox [type="checkbox"]+span:after {
|
||||
border-radius: 50%;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
border-color: #dadbdc;
|
||||
}
|
||||
.checkbox [type="checkbox"] {
|
||||
& + span:after {
|
||||
border-radius: 50%;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
border-color: #dadbdc;
|
||||
}
|
||||
|
||||
.checkbox [type="checkbox"]:checked+span:after {
|
||||
background-color: #4EA4F6;
|
||||
border: none;
|
||||
&:checked {
|
||||
& + span {
|
||||
&:before {
|
||||
top: 5px;
|
||||
left: 0px;
|
||||
}
|
||||
|
||||
&:after {
|
||||
background-color: #4EA4F6;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.folder-category-button {
|
||||
|
@ -100,6 +100,7 @@ $floating-left-sidebar: 925px;
|
||||
}
|
||||
|
||||
@import "partials/ico";
|
||||
@import "partials/checkbox";
|
||||
@import "partials/chatlist";
|
||||
@import "partials/chat";
|
||||
@import "partials/chatBubble";
|
||||
@ -707,176 +708,6 @@ hr {
|
||||
}
|
||||
}
|
||||
|
||||
.checkbox-field {
|
||||
margin: 1.25rem 0;
|
||||
display: block;
|
||||
text-align: left;
|
||||
padding: 0 1.125rem;
|
||||
/* font-weight: 500; */
|
||||
position: relative;
|
||||
|
||||
@include respond-to(handhelds) {
|
||||
margin-bottom: 27px;
|
||||
}
|
||||
}
|
||||
|
||||
.radio-field {
|
||||
display: block;
|
||||
position: relative;
|
||||
padding-left: 3.5rem;
|
||||
text-align: left;
|
||||
margin: 1.25rem 0;
|
||||
line-height: 1.5rem;
|
||||
cursor: pointer;
|
||||
|
||||
&.hidden-widget {
|
||||
cursor: default;
|
||||
|
||||
.radio-field-main {
|
||||
&::before, &::after {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> input {
|
||||
&:checked {
|
||||
& ~ .radio-field-main {
|
||||
&::before {
|
||||
border-color: $button-primary-background;
|
||||
}
|
||||
|
||||
&::after {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.radio-field-main {
|
||||
&::before, &::after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: .25rem;
|
||||
top: 50%;
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
&::before {
|
||||
border: 2px solid #8d969c;
|
||||
border-radius: 50%;
|
||||
background-color: white;
|
||||
opacity: 1;
|
||||
transition: border-color .1s ease, opacity .1s ease;
|
||||
}
|
||||
|
||||
&::after {
|
||||
left: .5625rem;
|
||||
width: .625rem;
|
||||
height: .625rem;
|
||||
border-radius: 50%;
|
||||
background: $button-primary-background;
|
||||
opacity: 0;
|
||||
transition: opacity .1s ease;
|
||||
}
|
||||
|
||||
/* .label {
|
||||
display: block;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.subLabel {
|
||||
display: block;
|
||||
font-size: 0.875rem;
|
||||
line-height: 1rem;
|
||||
color: var(--color-text-secondary);
|
||||
} */
|
||||
}
|
||||
}
|
||||
|
||||
[type="checkbox"], [type="radio"] {
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
opacity: 0;
|
||||
z-index: var(--z-below);
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
[type="checkbox"] {
|
||||
& + span {
|
||||
position: relative;
|
||||
padding-left: 3.5rem;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
height: 25px;
|
||||
line-height: 25px;
|
||||
user-select: none;
|
||||
transition: .2s opacity;
|
||||
|
||||
&:before, &:after {
|
||||
content: '';
|
||||
left: 0;
|
||||
position: absolute;
|
||||
transition: border .25s, background-color .25s, width .20s .1s, height .20s .1s, top .20s .1s, left .20s .1s;
|
||||
}
|
||||
|
||||
&:before {
|
||||
border-radius: 2px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&:after {
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
z-index: 0;
|
||||
border: 2px solid $button-primary-background;
|
||||
border-radius: 3px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
}
|
||||
|
||||
&:not(:checked) + span:before {
|
||||
width: 0;
|
||||
height: 0;
|
||||
border: 2px solid transparent;
|
||||
left: 6px;
|
||||
top: 10px;
|
||||
transform: rotateZ(45deg);
|
||||
transform-origin: 100% 100%;
|
||||
}
|
||||
|
||||
&:checked + span:before {
|
||||
top: 4px;
|
||||
left: -1px;
|
||||
width: 8px;
|
||||
height: 14px;
|
||||
border-top: 2px solid transparent;
|
||||
border-left: 2px solid transparent;
|
||||
border-right: 2px solid #fff;
|
||||
border-bottom: 2px solid #fff;
|
||||
transform: rotateZ(45deg);
|
||||
transform-origin: 100% 100%;
|
||||
}
|
||||
|
||||
&:not(:checked) + span:after {
|
||||
background-color: transparent;
|
||||
border-color: #8d969c;
|
||||
}
|
||||
|
||||
&:checked + span:after {
|
||||
background-color: $button-primary-background;
|
||||
}
|
||||
|
||||
&:disabled + span {
|
||||
cursor: default;
|
||||
opacity: .25;
|
||||
}
|
||||
}
|
||||
|
||||
.input-wrapper > * + * {
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user