From 2a6e7d117860eed56cadff5ec9af1e8402edf117 Mon Sep 17 00:00:00 2001 From: Eduard Kuzmenko Date: Wed, 17 Mar 2021 19:21:42 +0400 Subject: [PATCH] Layout changes: Input new hover Checkbox ripple Row click --- src/components/checkboxField.ts | 9 +++++- src/components/ripple.ts | 4 +-- src/components/row.ts | 31 ++++++++++++++++--- src/components/sidebarLeft/tabs/background.ts | 3 +- .../sidebarLeft/tabs/editProfile.ts | 19 +++--------- .../sidebarLeft/tabs/generalSettings.ts | 27 ++++++++++------ src/components/sidebarLeft/tabs/settings.ts | 1 - src/lib/mtproto/mtprotoworker.ts | 10 +++--- src/scss/components/_global.scss | 4 +++ src/scss/partials/_checkbox.scss | 5 +++ src/scss/partials/_input.scss | 27 ++++++++++++---- src/scss/partials/_leftSidebar.scss | 27 ++++++++++------ src/scss/style.scss | 13 +++++--- 13 files changed, 123 insertions(+), 57 deletions(-) diff --git a/src/components/checkboxField.ts b/src/components/checkboxField.ts index c9ab7177..ee29439a 100644 --- a/src/components/checkboxField.ts +++ b/src/components/checkboxField.ts @@ -1,5 +1,6 @@ import appStateManager from "../lib/appManagers/appStateManager"; import { getDeepProperty } from "../helpers/object"; +import { ripple } from "./ripple"; export default class CheckboxField { public input: HTMLInputElement; @@ -13,7 +14,8 @@ export default class CheckboxField { stateKey?: string, disabled?: boolean, checked?: boolean, - restriction?: boolean + restriction?: boolean, + withRipple?: boolean } = {}) { const label = this.label = document.createElement('label'); label.classList.add('checkbox-field'); @@ -86,6 +88,11 @@ export default class CheckboxField { if(span) { label.append(span); } + + if(options.withRipple) { + label.classList.add('checkbox-ripple', 'hover-effect'); + ripple(label, undefined, undefined, true); + } } get checked() { diff --git a/src/components/ripple.ts b/src/components/ripple.ts index 55f65926..61653561 100644 --- a/src/components/ripple.ts +++ b/src/components/ripple.ts @@ -3,7 +3,7 @@ import { findUpClassName } from "../helpers/dom"; import rootScope from "../lib/rootScope"; let rippleClickId = 0; -export function ripple(elem: HTMLElement, callback: (id: number) => Promise = () => Promise.resolve(), onEnd: (id: number) => void = null) { +export function ripple(elem: HTMLElement, callback: (id: number) => Promise = () => Promise.resolve(), onEnd: (id: number) => void = null, prepend = false) { //return; if(elem.querySelector('.c-ripple')) return; elem.classList.add('rp'); @@ -16,7 +16,7 @@ export function ripple(elem: HTMLElement, callback: (id: number) => Promise void; //let animationEndPromise: Promise; diff --git a/src/components/row.ts b/src/components/row.ts index 9d199ae9..2e2ef663 100644 --- a/src/components/row.ts +++ b/src/components/row.ts @@ -3,6 +3,7 @@ import RadioField from "./radioField"; import { ripple } from "./ripple"; import { SliderSuperTab } from "./slider"; import RadioForm from "./radioForm"; +import { attachClickEvent, cancelEvent } from "../helpers/dom"; export default class Row { public container: HTMLElement; @@ -19,6 +20,7 @@ export default class Row { subtitle: string, radioField: Row['radioField'], checkboxField: Row['checkboxField'], + noCheckboxSubtitle: boolean, title: string, titleRight: string, clickable: boolean | ((e: Event) => void), @@ -45,8 +47,25 @@ export default class Row { this.checkboxField = options.checkboxField; this.container.append(this.checkboxField.label); - this.checkboxField.input.addEventListener('change', () => { - this.subtitle.innerHTML = this.checkboxField.input.checked ? 'Enabled' : 'Disabled'; + if(!options.noCheckboxSubtitle) { + this.checkboxField.input.addEventListener('change', () => { + this.subtitle.innerHTML = this.checkboxField.input.checked ? 'Enabled' : 'Disabled'; + }); + } + } + + const i = options.radioField || options.checkboxField; + i.label.classList.add('disable-hover'); + + if(options.radioField) { + attachClickEvent(this.container, (e) => { + cancelEvent(e); + i.checked = true; + }); + } else { + attachClickEvent(this.container, (e) => { + cancelEvent(e); + i.checked = !i.checked; }); } } else { @@ -88,7 +107,7 @@ export default class Row { options.clickable = () => options.navigationTab.open(); } - if(options.clickable) { + if(options.clickable || options.radioField || options.checkboxField) { if(typeof(options.clickable) === 'function') { this.container.addEventListener('click', (e) => { if(this.freezed) return; @@ -97,7 +116,11 @@ export default class Row { } this.container.classList.add('row-clickable', 'hover-effect'); - ripple(this.container); + ripple(this.container, undefined, undefined, true); + + /* if(options.radioField || options.checkboxField) { + this.container.prepend(this.container.lastElementChild); + } */ } this.container.append(this.subtitle); diff --git a/src/components/sidebarLeft/tabs/background.ts b/src/components/sidebarLeft/tabs/background.ts index a1caffa5..3e757cf2 100644 --- a/src/components/sidebarLeft/tabs/background.ts +++ b/src/components/sidebarLeft/tabs/background.ts @@ -31,7 +31,8 @@ export default class AppBackgroundTab extends SliderSuperTab { const blurCheckboxField = new CheckboxField({ text: 'Blur Wallpaper Image', name: 'blur', - stateKey: 'settings.background.blur' + stateKey: 'settings.background.blur', + withRipple: true }); blurCheckboxField.input.addEventListener('change', () => { const active = grid.querySelector('.active') as HTMLElement; diff --git a/src/components/sidebarLeft/tabs/editProfile.ts b/src/components/sidebarLeft/tabs/editProfile.ts index ec5325c1..b8201de6 100644 --- a/src/components/sidebarLeft/tabs/editProfile.ts +++ b/src/components/sidebarLeft/tabs/editProfile.ts @@ -19,7 +19,7 @@ export default class AppEditProfileTab extends SliderSuperTab { private editPeer: EditPeer; - protected init() { + protected async init() { this.container.classList.add('edit-profile-container'); this.title.innerText = 'Edit Profile'; @@ -127,27 +127,16 @@ export default class AppEditProfileTab extends SliderSuperTab { this.editPeer.nextBtn.removeAttribute('disabled'); }); }, {listenerSetter: this.listenerSetter}); - } - - public fillElements() { - if(this.init) { - this.init(); - this.init = null; - } const user = appUsersManager.getSelf(); + const userFull = await appProfileManager.getProfile(user.id, true); + this.firstNameInputField.setOriginalValue(user.first_name, true); this.lastNameInputField.setOriginalValue(user.last_name, true); - this.bioInputField.setOriginalValue('', true); + this.bioInputField.setOriginalValue(userFull.about, true); this.usernameInputField.setOriginalValue(user.username, true); - appProfileManager.getProfile(user.id, true).then(userFull => { - if(userFull.about) { - this.bioInputField.setOriginalValue(userFull.about); - } - }); - this.setProfileUrl(); this.editPeer.handleChange(); } diff --git a/src/components/sidebarLeft/tabs/generalSettings.ts b/src/components/sidebarLeft/tabs/generalSettings.ts index c2ffe1ca..af03a68d 100644 --- a/src/components/sidebarLeft/tabs/generalSettings.ts +++ b/src/components/sidebarLeft/tabs/generalSettings.ts @@ -76,7 +76,8 @@ export default class AppGeneralSettingsTab extends SliderSuperTab { const animationsCheckboxField = new CheckboxField({ text: 'Enable Animations', name: 'animations', - stateKey: 'settings.animationsEnabled' + stateKey: 'settings.animationsEnabled', + withRipple: true }); container.append(range.container, chatBackgroundButton, animationsCheckboxField.label); @@ -118,22 +119,26 @@ export default class AppGeneralSettingsTab extends SliderSuperTab { const contactsCheckboxField = new CheckboxField({ text: 'Contacts', name: 'contacts', - stateKey: 'settings.autoDownload.contacts' + stateKey: 'settings.autoDownload.contacts', + withRipple: true }); const privateCheckboxField = new CheckboxField({ text: 'Private Chats', name: 'private', - stateKey: 'settings.autoDownload.private' + stateKey: 'settings.autoDownload.private', + withRipple: true }); const groupsCheckboxField = new CheckboxField({ text: 'Group Chats', name: 'groups', - stateKey: 'settings.autoDownload.groups' + stateKey: 'settings.autoDownload.groups', + withRipple: true }); const channelsCheckboxField = new CheckboxField({ text: 'Channels', name: 'channels', - stateKey: 'settings.autoDownload.channels' + stateKey: 'settings.autoDownload.channels', + withRipple: true }); container.append(contactsCheckboxField.label, privateCheckboxField.label, groupsCheckboxField.label, channelsCheckboxField.label); @@ -146,12 +151,14 @@ export default class AppGeneralSettingsTab extends SliderSuperTab { const gifsCheckboxField = new CheckboxField({ text: 'GIFs', name: 'gifs', - stateKey: 'settings.autoPlay.gifs' + stateKey: 'settings.autoPlay.gifs', + withRipple: true }); const videosCheckboxField = new CheckboxField({ text: 'Videos', name: 'videos', - stateKey: 'settings.autoPlay.videos' + stateKey: 'settings.autoPlay.videos', + withRipple: true }); container.append(gifsCheckboxField.label, videosCheckboxField.label); @@ -163,12 +170,14 @@ export default class AppGeneralSettingsTab extends SliderSuperTab { const suggestCheckboxField = new CheckboxField({ text: 'Suggest Stickers by Emoji', name: 'suggest', - stateKey: 'settings.stickers.suggest' + stateKey: 'settings.stickers.suggest', + withRipple: true }); const loopCheckboxField = new CheckboxField({ text: 'Loop Animated Stickers', name: 'loop', - stateKey: 'settings.stickers.loop' + stateKey: 'settings.stickers.loop', + withRipple: true }); container.append(suggestCheckboxField.label, loopCheckboxField.label); diff --git a/src/components/sidebarLeft/tabs/settings.ts b/src/components/sidebarLeft/tabs/settings.ts index 008a05c0..aaaea64b 100644 --- a/src/components/sidebarLeft/tabs/settings.ts +++ b/src/components/sidebarLeft/tabs/settings.ts @@ -112,7 +112,6 @@ export default class AppSettingsTab extends SliderSuperTab { this.buttons.edit.addEventListener('click', () => { const tab = new AppEditProfileTab(this.slider); - tab.fillElements(); tab.open(); }); diff --git a/src/lib/mtproto/mtprotoworker.ts b/src/lib/mtproto/mtprotoworker.ts index e7cf7045..8ef442a1 100644 --- a/src/lib/mtproto/mtprotoworker.ts +++ b/src/lib/mtproto/mtprotoworker.ts @@ -55,7 +55,7 @@ export class ApiManagerProxy extends CryptoWorkerMethods { private isSWRegistered = true; - private debug = DEBUG && false; + private debug = DEBUG /* && false */; private sockets: Map = new Map(); @@ -319,7 +319,7 @@ export class ApiManagerProxy extends CryptoWorkerMethods { (options as MTMessage).messageId = o.prepareTempMessageId; //console.log('will invokeApi:', method, params, options); - return this.performTaskWorker('invokeApi', method, params, o); + return this.invokeApi(method, params, o); } public invokeApiHashable(method: T, params: Omit = {} as any, options: InvokeApiOptions = {}): Promise { @@ -334,7 +334,7 @@ export class ApiManagerProxy extends CryptoWorkerMethods { } } - return this.performTaskWorker('invokeApi', method, params, options).then((result: any) => { + return this.invokeApi(method, params, options).then((result: any) => { if(result._.includes('NotModified')) { this.debug && this.log.warn('NotModified saved!', method, queryJSON); return cached.result; @@ -355,7 +355,9 @@ export class ApiManagerProxy extends CryptoWorkerMethods { } /* private computeHash(smth: any[]) { - return smth.reduce((hash, v) => (((hash * 0x4F25) & 0x7FFFFFFF) + v.id) & 0x7FFFFFFF, 0); + smth = smth.slice().sort((a, b) => a.id - b.id); + //return smth.reduce((hash, v) => (((hash * 0x4F25) & 0x7FFFFFFF) + v.id) & 0x7FFFFFFF, 0); + return smth.reduce((hash, v) => ((hash * 20261) + 0x80000000 + v.id) % 0x80000000, 0); } */ public setBaseDcId(dcId: number) { diff --git a/src/scss/components/_global.scss b/src/scss/components/_global.scss index 794fa5ee..e2702c77 100644 --- a/src/scss/components/_global.scss +++ b/src/scss/components/_global.scss @@ -86,6 +86,10 @@ Utility Classes display: none !important; } +.hide-overflow { + overflow: hidden; +} + // No Text Select .no-select/* , .no-select * */ { user-select: none; diff --git a/src/scss/partials/_checkbox.scss b/src/scss/partials/_checkbox.scss index 40e3d84d..de96d0ec 100644 --- a/src/scss/partials/_checkbox.scss +++ b/src/scss/partials/_checkbox.scss @@ -92,6 +92,11 @@ } } +.checkbox-ripple { + overflow: hidden; + border-radius: $border-radius-medium; +} + .checkbox-field-round { --size: 1.5rem; diff --git a/src/scss/partials/_input.scss b/src/scss/partials/_input.scss index a77d9a1b..1ccfef6e 100644 --- a/src/scss/partials/_input.scss +++ b/src/scss/partials/_input.scss @@ -70,7 +70,7 @@ box-sizing: border-box; width: 100%; min-height: var(--height); - transition: .2s border-color; + transition: 0s border-color; position: relative; z-index: 1; line-height: 1.3125; @@ -86,10 +86,16 @@ transition: none; } - html.no-touch & { - &:hover:not(:focus):not(.error):not(.valid) { - border-color: var(--color-gray); + @include hover() { + &:not(:focus):not(.error):not(.valid) { + //border-color: var(--color-gray); + border-color: #000; + transition: .2s border-color; } + + /* &:not(:focus):not(.error):not(.valid) ~ label { + transition: .2s transform, .2s padding, .1s opacity, font-weight 0s 1s; + } */ } /* font-weight: 500; */ @@ -140,6 +146,12 @@ color: $button-primary-background; font-weight: 500; } + + // * valid for plain text, empty for contenteditable + &:valid ~ label, + &:not(:empty):focus ~ label { + transition-delay: 0s, 0s, 0s, 0s; + } &:focus ~ label, &:valid ~ label, @@ -251,8 +263,11 @@ input:focus, button:focus { transition: none; } - &:hover { - border-color: var(--color-gray); + @include hover() { + &:not(:focus) { + border-color: var(--color-gray) !important; + //border-color: #000; + } } &:focus { diff --git a/src/scss/partials/_leftSidebar.scss b/src/scss/partials/_leftSidebar.scss index 35466609..4cfc4cd5 100644 --- a/src/scss/partials/_leftSidebar.scss +++ b/src/scss/partials/_leftSidebar.scss @@ -501,10 +501,10 @@ .edit-profile-container { .caption { - margin-top: 1.063rem; - margin-left: 1.438rem; - line-height: 1.2; - padding-bottom: 1.438rem; + margin-top: 1.0625rem; + margin-left: 1.4375rem; + line-height: var(--line-height); + padding-bottom: 1.4375rem; @include respond-to(handhelds) { padding-right: 24px; @@ -512,7 +512,7 @@ } .sidebar-left-h2 { - padding: 0 1.438rem; + padding: 0 1.4375rem; padding-bottom: 1.5rem; } @@ -540,7 +540,7 @@ text-align: center; color: #707579; font-size: 14px; - line-height: 1.3; + line-height: var(--line-height); } } @@ -836,6 +836,12 @@ > .btn-primary { margin: 0; } + + > .checkbox-field { + .checkbox-box { + left: auto; + } + } } &-name { @@ -851,7 +857,7 @@ margin-top: 1rem; font-size: 1rem; color: #707579; - line-height: 1.3125; + line-height: var(--line-height); padding: 0 1rem; @include respond-to(handhelds) { @@ -872,8 +878,7 @@ display: flex; align-items: center; height: 3.5rem; - margin: 0 1.0625rem; - padding: 0; + padding: 0 1.1875rem; } &-disabled { @@ -1032,6 +1037,10 @@ .sidebar-left-section:first-child { padding-bottom: 0; + + .row-title { + font-weight: 500; + } } } diff --git a/src/scss/style.scss b/src/scss/style.scss index 2dfbe0da..5173f197 100644 --- a/src/scss/style.scss +++ b/src/scss/style.scss @@ -62,6 +62,7 @@ $chat-padding-handhelds: .5rem; --messages-container-width: #{$messages-container-width}; --messages-text-size: 16px; --messages-secondary-text-size: calc(var(--messages-text-size) - 1px); + --line-height: 1.3125; --esg-sticker-size: 80px; // https://github.com/overtake/TelegramSwift/blob/5cc7d2475fe4738a6aa0486c23eaf80a89d33b97/submodules/TGUIKit/TGUIKit/PresentationTheme.swift#L2054 @@ -1111,7 +1112,7 @@ middle-ellipsis-element { } &-title { - line-height: 1.3125; + line-height: var(--line-height); &-right { flex: 0 0 auto !important; @@ -1157,11 +1158,13 @@ middle-ellipsis-element { &-subtitle { color: var(--color-text-secondary) !important; font-size: .875rem !important; + line-height: var(--line-height); + margin-top: .125rem; + margin-bottom: .0625rem; - // * lol - line-height: 1rem; - margin-top: .1875rem; - margin-bottom: .125rem; + &:empty { + display: none; + } } }