Browse Source

Refactor input range

master
morethanwords 4 years ago
parent
commit
22efc8e833
  1. 140
      src/components/rangeSelector.ts
  2. 3
      src/components/sidebarLeft/index.ts
  3. 90
      src/components/sidebarLeft/tabs/generalSettings.ts
  4. 6
      src/components/sidebarLeft/tabs/settings.ts
  5. 132
      src/lib/mediaPlayer.ts
  6. 19
      src/scss/partials/_audio.scss
  7. 7
      src/scss/partials/_chatBubble.scss
  8. 94
      src/scss/partials/_ckin.scss
  9. 40
      src/scss/partials/_leftSidebar.scss

140
src/components/rangeSelector.ts

@ -0,0 +1,140 @@ @@ -0,0 +1,140 @@
import { isTouchSupported } from "../helpers/touchSupport";
import { clamp } from "../helpers/number";
type SUPEREVENT = MouseEvent | TouchEvent;
export default class RangeSelector {
public container: HTMLDivElement;
protected filled: HTMLDivElement;
protected seek: HTMLInputElement;
public mousedown = false;
private events: Partial<{
//onMouseMove: ProgressLine['onMouseMove'],
onMouseDown: RangeSelector['onMouseDown'],
onMouseUp: RangeSelector['onMouseUp'],
onScrub: (scrubTime: number) => void
}> = {};
protected decimals: number;
constructor(protected step: number, protected value: number, protected min: number, protected max: number) {
this.container = document.createElement('div');
this.container.classList.add('progress-line');
this.filled = document.createElement('div');
this.filled.classList.add('progress-line__filled');
const seek = this.seek = document.createElement('input');
seek.classList.add('progress-line__seek');
//seek.setAttribute('max', '0');
seek.type = 'range';
seek.step = '' + step;
seek.min = '' + this.min;
seek.max = '' + this.max;
seek.value = '' + value;
/* this.seek.addEventListener('change', (e) => {
console.log('seek change', e);
}); */
if(value) {
this.setProgress(value);
}
const stepStr = '' + this.step;
const index = stepStr.indexOf('.');
this.decimals = index === -1 ? 0 : stepStr.length - index - 1;
//this.setListeners();
this.container.append(this.filled, seek);
}
public setHandlers(events: RangeSelector['events']) {
this.events = events;
}
onMouseMove = (e: SUPEREVENT) => {
this.mousedown && this.scrub(e);
};
onMouseDown = (e: SUPEREVENT) => {
this.scrub(e);
this.mousedown = true;
this.events?.onMouseDown && this.events.onMouseDown(e);
};
onMouseUp = (e: SUPEREVENT) => {
this.mousedown = false;
this.events?.onMouseUp && this.events.onMouseUp(e);
};
public setListeners() {
this.container.addEventListener('mousemove', this.onMouseMove);
this.container.addEventListener('mousedown', this.onMouseDown);
this.container.addEventListener('mouseup', this.onMouseUp);
if(isTouchSupported) {
this.container.addEventListener('touchmove', this.onMouseMove, {passive: true});
this.container.addEventListener('touchstart', this.onMouseDown, {passive: true});
this.container.addEventListener('touchend', this.onMouseUp, {passive: true});
}
}
public setProgress(value: number) {
this.setFilled(value);
this.seek.value = '' + value;
}
public setFilled(value: number) {
let percents = (value - this.min) / (this.max - this.min);
percents = clamp(percents, 0, 1);
//console.log('setFilled', percents, value);
this.filled.style.width = (percents * 100) + '%';
//this.filled.style.transform = 'scaleX(' + scaleX + ')';
}
protected scrub(e: SUPEREVENT) {
let offsetX: number;
const rect = (e.target as HTMLElement).getBoundingClientRect();
if(e instanceof MouseEvent) {
offsetX = e.pageX - Math.round(rect.left);
} else { // touch
offsetX = e.targetTouches[0].pageX - Math.round(rect.left);
}
let value = this.min + (offsetX / Math.round(rect.width) * (this.max - this.min));
if((value - this.min) < ((this.max - this.min) / 2)) {
value -= this.step / 10;
}
//console.log('scrub value:', value, this.decimals, offsetX, rect.width, e);
value = +value.toFixed(this.decimals);
//const dotIndex = ('' + value).indexOf('.');
//value = +('' + value).slice(0, this.decimals ? dotIndex + this.decimals : dotIndex);
value = clamp(value, this.min, this.max);
this.setFilled(value);
this.events?.onScrub && this.events.onScrub(value);
return value;
}
public removeListeners() {
this.container.removeEventListener('mousemove', this.onMouseMove);
this.container.removeEventListener('mousedown', this.onMouseDown);
this.container.removeEventListener('mouseup', this.onMouseUp);
if(isTouchSupported) {
this.container.removeEventListener('touchmove', this.onMouseMove);
this.container.removeEventListener('touchstart', this.onMouseDown);
this.container.removeEventListener('touchend', this.onMouseUp);
}
this.events = {};
}
}

3
src/components/sidebarLeft/index.ts

@ -28,6 +28,7 @@ import appMessagesManager from "../../lib/appManagers/appMessagesManager"; @@ -28,6 +28,7 @@ import appMessagesManager from "../../lib/appManagers/appMessagesManager";
import apiManagerProxy from "../../lib/mtproto/mtprotoworker";
import AppSearchSuper from "../appSearchSuper.";
import { DateData, fillTipDates } from "../../helpers/date";
import AppGeneralSettingsTab from "./tabs/generalSettings";
const addMembersTab = new AppAddMembersTab();
const contactsTab = new AppContactsTab();
@ -73,6 +74,7 @@ export class AppSidebarLeft extends SidebarSlider { @@ -73,6 +74,7 @@ export class AppSidebarLeft extends SidebarSlider {
public chatFoldersTab: AppChatFoldersTab;
public editFolderTab: AppEditFolderTab;
public includedChatsTab: AppIncludedChatsTab;
public generalSettingsTab: AppGeneralSettingsTab;
//private log = logger('SL');
@ -107,6 +109,7 @@ export class AppSidebarLeft extends SidebarSlider { @@ -107,6 +109,7 @@ export class AppSidebarLeft extends SidebarSlider {
this.editFolderTab = new AppEditFolderTab(this);
this.includedChatsTab = new AppIncludedChatsTab(this);
this.editProfileTab = new AppEditProfileTab(this);
this.generalSettingsTab = new AppGeneralSettingsTab(this);
this.menuEl = this.toolsBtn.querySelector('.btn-menu');
this.newBtnMenu = this.sidebarEl.querySelector('#new-menu');

90
src/components/sidebarLeft/tabs/generalSettings.ts

@ -0,0 +1,90 @@ @@ -0,0 +1,90 @@
import { SliderSuperTab } from "../../slider"
import { AppSidebarLeft } from "..";
import RangeSelector from "../../rangeSelector";
import { clamp } from "../../../helpers/number";
export class RangeSettingSelector {
public container: HTMLDivElement;
private range: RangeSelector;
constructor(name: string, step: number, initialValue: number, minValue: number, maxValue: number) {
const BASE_CLASS = 'range-setting-selector';
this.container = document.createElement('div');
this.container.classList.add(BASE_CLASS);
const details = document.createElement('div');
details.classList.add(BASE_CLASS + '-details');
const nameDiv = document.createElement('div');
nameDiv.classList.add(BASE_CLASS + '-name');
nameDiv.innerHTML = name;
const valueDiv = document.createElement('div');
valueDiv.classList.add(BASE_CLASS + '-value');
valueDiv.innerHTML = '' + initialValue;
details.append(nameDiv, valueDiv);
this.range = new RangeSelector(step, initialValue, minValue, maxValue);
this.range.setListeners();
this.range.setHandlers({
onScrub: value => {
//console.log('font size scrub:', value);
valueDiv.innerText = '' + value;
}
});
this.container.append(details, this.range.container);
}
}
export default class AppGeneralSettingsTab extends SliderSuperTab {
constructor(appSidebarLeft: AppSidebarLeft) {
super(appSidebarLeft);
}
init() {
this.container.classList.add('general-settings-container');
this.title.innerText = 'General';
const generateSection = (name: string) => {
const container = document.createElement('div');
container.classList.add('sidebar-left-section');
const hr = document.createElement('hr');
const h2 = document.createElement('div');
h2.classList.add('sidebar-left-section-name');
h2.innerHTML = name;
const content = document.createElement('div');
content.classList.add('sidebar-left-section-content');
content.append(h2);
container.append(hr, content);
this.scrollable.append(container);
return content;
};
{
const container = generateSection('Settings');
const range = new RangeSettingSelector('Message Text Size', 1, 16, 12, 20);
container.append(range.container);
}
generateSection('Keyboard');
generateSection('Auto-Download Media');
generateSection('Auto-Play Media');
generateSection('Stickers');
}
onOpen() {
if(this.init) {
this.init();
this.init = null;
}
}
}

6
src/components/sidebarLeft/tabs/settings.ts

@ -54,7 +54,7 @@ export default class AppSettingsTab extends SliderSuperTab { @@ -54,7 +54,7 @@ export default class AppSettingsTab extends SliderSuperTab {
const className = 'profile-button btn-primary btn-transparent';
buttonsDiv.append(this.buttons.edit = Button(className, {icon: 'edit', rippleSquare: true, text: 'Edit Profile'}));
buttonsDiv.append(this.buttons.folders = Button(className, {icon: 'folder', rippleSquare: true, text: 'Chat Folders'}));
buttonsDiv.append(this.buttons.general = Button(className + ' btn-disabled', {icon: 'settings', rippleSquare: true, text: 'General Settings'}));
buttonsDiv.append(this.buttons.general = Button(className, {icon: 'settings', rippleSquare: true, text: 'General Settings'}));
buttonsDiv.append(this.buttons.notifications = Button(className + ' btn-disabled', {icon: 'unmute', rippleSquare: true, text: 'Notifications'}));
buttonsDiv.append(this.buttons.privacy = Button(className + ' btn-disabled', {icon: 'lock', rippleSquare: true, text: 'Privacy and Security'}));
buttonsDiv.append(this.buttons.language = Button(className + ' btn-disabled', {icon: 'language', rippleSquare: true, text: 'Language'}));
@ -74,6 +74,10 @@ export default class AppSettingsTab extends SliderSuperTab { @@ -74,6 +74,10 @@ export default class AppSettingsTab extends SliderSuperTab {
this.buttons.folders.addEventListener('click', () => {
appSidebarLeft.chatFoldersTab.open();
});
this.buttons.general.addEventListener('click', () => {
appSidebarLeft.generalSettingsTab.open();
});
}
public fillElements() {

132
src/lib/mediaPlayer.ts

@ -2,131 +2,18 @@ import { cancelEvent } from "../helpers/dom"; @@ -2,131 +2,18 @@ import { cancelEvent } from "../helpers/dom";
import appMediaPlaybackController from "../components/appMediaPlaybackController";
import { isAppleMobile } from "../helpers/userAgent";
import { isTouchSupported } from "../helpers/touchSupport";
import RangeSelector from "../components/rangeSelector";
type SUPEREVENT = MouseEvent | TouchEvent;
export class ProgressLine {
public container: HTMLDivElement;
protected filled: HTMLDivElement;
protected seek: HTMLInputElement;
protected duration = 1;
public mousedown = false;
private events: Partial<{
//onMouseMove: ProgressLine['onMouseMove'],
onMouseDown: ProgressLine['onMouseDown'],
onMouseUp: ProgressLine['onMouseUp'],
onScrub: (scrubTime: number) => void
}> = {};
constructor(initialValue = 0) {
this.container = document.createElement('div');
this.container.classList.add('progress-line');
this.filled = document.createElement('div');
this.filled.classList.add('progress-line__filled');
const seek = this.seek = document.createElement('input');
seek.classList.add('progress-line__seek');
seek.value = '' + initialValue;
seek.setAttribute('min', '0');
//seek.setAttribute('max', '0');
seek.type = 'range';
seek.step = '0.1';
seek.max = '' + (this.duration * 1000);
if(initialValue > 0) {
this.setProgress(initialValue);
}
//this.setListeners();
this.container.append(this.filled, seek);
}
public setHandlers(events: ProgressLine['events']) {
this.events = events;
}
onMouseMove = (e: SUPEREVENT) => {
this.mousedown && this.scrub(e);
};
onMouseDown = (e: SUPEREVENT) => {
this.scrub(e);
this.mousedown = true;
this.events?.onMouseDown && this.events.onMouseDown(e);
};
onMouseUp = (e: SUPEREVENT) => {
this.mousedown = false;
this.events?.onMouseUp && this.events.onMouseUp(e);
};
public setListeners() {
this.container.addEventListener('mousemove', this.onMouseMove);
this.container.addEventListener('mousedown', this.onMouseDown);
this.container.addEventListener('mouseup', this.onMouseUp);
if(isTouchSupported) {
this.container.addEventListener('touchmove', this.onMouseMove, {passive: true});
this.container.addEventListener('touchstart', this.onMouseDown, {passive: true});
this.container.addEventListener('touchend', this.onMouseUp, {passive: true});
}
}
public setProgress(scrubTime: number) {
this.setFilled(scrubTime);
this.seek.value = '' + (scrubTime * 1000);
}
public setFilled(scrubTime: number) {
let scaleX = scrubTime / this.duration;
scaleX = Math.max(0, Math.min(1, scaleX));
this.filled.style.transform = 'scaleX(' + scaleX + ')';
}
protected scrub(e: SUPEREVENT) {
let offsetX: number;
if(e instanceof MouseEvent) {
offsetX = e.offsetX;
} else { // touch
const rect = (e.target as HTMLElement).getBoundingClientRect();
offsetX = e.targetTouches[0].pageX - rect.left;
}
const scrubTime = offsetX / this.container.offsetWidth * this.duration;
this.setFilled(scrubTime);
this.events?.onScrub && this.events.onScrub(scrubTime);
return scrubTime;
}
public removeListeners() {
this.container.removeEventListener('mousemove', this.onMouseMove);
this.container.removeEventListener('mousedown', this.onMouseDown);
this.container.removeEventListener('mouseup', this.onMouseUp);
if(isTouchSupported) {
this.container.removeEventListener('touchmove', this.onMouseMove);
this.container.removeEventListener('touchstart', this.onMouseDown);
this.container.removeEventListener('touchend', this.onMouseUp);
}
this.events = {};
}
}
export class MediaProgressLine extends ProgressLine {
export class MediaProgressLine extends RangeSelector {
private filledLoad: HTMLDivElement;
private stopAndScrubTimeout = 0;
private progressRAF = 0;
constructor(private media: HTMLAudioElement | HTMLVideoElement, private streamable = false) {
super();
super(1000 / 60 / 1000, 0, 0, 1);
if(streamable) {
this.filledLoad = document.createElement('div');
@ -170,8 +57,8 @@ export class MediaProgressLine extends ProgressLine { @@ -170,8 +57,8 @@ export class MediaProgressLine extends ProgressLine {
}
onLoadedData = () => {
this.duration = this.media.duration;
this.seek.setAttribute('max', '' + this.duration * 1000);
this.max = this.media.duration;
this.seek.setAttribute('max', '' + this.max);
};
onEnded = () => {
@ -226,12 +113,13 @@ export class MediaProgressLine extends ProgressLine { @@ -226,12 +113,13 @@ export class MediaProgressLine extends ProgressLine {
//console.log('onProgress correct range:', nearestStart, end, this.media);
const percents = this.media.duration ? end / this.media.duration : 0;
this.filledLoad.style.transform = 'scaleX(' + percents + ')';
this.filledLoad.style.width = (percents * 100) + '%';
//this.filledLoad.style.transform = 'scaleX(' + percents + ')';
}
protected setSeekMax() {
this.duration = this.media.duration;
if(this.duration > 0) {
this.max = this.media.duration;
if(this.max > 0) {
this.onLoadedData();
} else {
this.media.addEventListener('loadeddata', this.onLoadedData);
@ -342,7 +230,7 @@ export default class VideoPlayer { @@ -342,7 +230,7 @@ export default class VideoPlayer {
video.muted = !video.muted;
});
const volumeProgress = new ProgressLine();
const volumeProgress = new RangeSelector(0.01, 1, 0, 1);
volumeProgress.setListeners();
volumeProgress.setHandlers({
onScrub: currentTime => {

19
src/scss/partials/_audio.scss

@ -446,30 +446,21 @@ @@ -446,30 +446,21 @@
// * for audio
.progress-line {
--height: 2px;
--border-radius: 4px;
flex: 1 1 auto;
margin-left: 5px;
margin-top: -1px;
&__filled {
background-color: #0089ff;
transform-origin: left;
height: 2px;
}
&__loaded {
background-color: #cacaca;
}
&__seek {
height: 2px;
--thumb-size: 12px;
--thumb-color: #0089ff;
//background-color: #e6ecf0;
background: rgba(193, 207, 220, 0.39);
&::-webkit-slider-thumb {
height: 12px;
width: 12px;
border: none;
}
background-color: rgba(193, 207, 220, .39);
}
}
}

7
src/scss/partials/_chatBubble.scss

@ -1964,11 +1964,8 @@ $bubble-margin: .25rem; @@ -1964,11 +1964,8 @@ $bubble-margin: .25rem;
}
&__seek {
background: rgba(124, 195, 107, .52);
&::-webkit-slider-thumb {
background: #47aa41;
}
--thumb-color: #47aa41;
background-color: rgba(124, 195, 107, .52);
}
}
}

94
src/scss/partials/_ckin.scss

@ -134,28 +134,18 @@ @@ -134,28 +134,18 @@
transition: all .3s;
text-align: left;
direction: ltr;
border-radius: 0 0 5px 5px;
z-index: 6;
.progress-line {
margin: 0 16px;
height: 5px;
background: rgba(255, 255, 255, 0.38);
border-radius: 4px;
overflow: visible;
border-radius: var(--border-radius);
&__filled {
background: #63a2e3;
transform-origin: left;
border-radius: 4px;
height: 5px;
transform: scaleX(0);
}
&__loaded {
background: rgba(255, 255, 255, 0.38);
left: 11px;
width: calc(100% - 11px);
&__loaded, & {
background: rgba(255, 255, 255, .38);
}
}
}
@ -253,13 +243,14 @@ @@ -253,13 +243,14 @@
background: #fff;
}
input[type=range]::-webkit-slider-thumb {
height: 15px;
width: 15px;
border-radius: 16px;
&__seek {
--thumb-size: 15px;
&::-webkit-slider-thumb {
background: #fff;
}
}
}
}
@ -281,16 +272,23 @@ video::-webkit-media-controls-enclosure { @@ -281,16 +272,23 @@ video::-webkit-media-controls-enclosure {
}
.progress-line {
--height: 5px;
--border-radius: 6px;
height: var(--height);
position: relative;
cursor: pointer;
&__seek {
--thumb-size: 13px;
--thumb-color: #63a2e3;
-webkit-appearance: none;
-moz-appearance: none;
background: transparent;
height: 4.5px;
width: 100%;
height: 100%;
cursor: pointer;
padding: 0;
margin: 0;
outline: none;
&:focus {
@ -310,18 +308,39 @@ video::-webkit-media-controls-enclosure { @@ -310,18 +308,39 @@ video::-webkit-media-controls-enclosure {
cursor: pointer;
border-radius: 1.3px;
-webkit-appearance: none;
transition: all 0.4s ease;
}
&::-webkit-slider-thumb {
height: 15px;
width: 15px;
border-radius: 16px;
background: #63a2e3;
height: var(--thumb-size);
width: var(--thumb-size);
border-radius: 50%;
background-color: var(--thumb-color);
cursor: pointer;
-webkit-appearance: none;
margin-left: -1px;
border: none;
//margin-left: -.5px;
}
&::-moz-range-thumb {
height: var(--thumb-size);
width: var(--thumb-size);
border-radius: 50%;
background-color: var(--thumb-color);
cursor: pointer;
-webkit-appearance: none;
border: none;
//margin-left: -.5px;
}
&::-ms-thumb {
height: var(--thumb-size);
width: var(--thumb-size);
border-radius: 50%;
background-color: var(--thumb-color);
cursor: pointer;
-webkit-appearance: none;
border: none;
//margin-left: -.5px;
}
&::-moz-range-track {
@ -330,33 +349,20 @@ video::-webkit-media-controls-enclosure { @@ -330,33 +349,20 @@ video::-webkit-media-controls-enclosure {
cursor: pointer;
border: 1px solid transparent;
background: transparent;
border-radius: 1.3px;
}
&::-moz-range-thumb {
height: 14px;
width: 14px;
border-radius: 50px;
border: 1px solid #63a2e3;
background: #63a2e3;
cursor: pointer;
margin-top: 5px;
//border-radius: 1.3px;
}
}
&__seek {
position: absolute;
top: 0;
width: 100%;
cursor: pointer;
margin: 0;
&__filled {
padding-right: 1px; // * need because there is border-radius
max-width: 100%;
}
&__loaded {
&__seek, &__filled, &__loaded {
border-radius: var(--border-radius);
position: absolute;
left: 12px;
height: 100%;
top: 0;
width: calc(100% - 12px);
}
}

40
src/scss/partials/_leftSidebar.scss

@ -879,3 +879,43 @@ @@ -879,3 +879,43 @@
padding: 0px 0 7px;
}
}
.sidebar-left-section {
padding: .5rem 0 1rem;
&-content {
padding: 0 1.5rem;
@include respond-to(handhelds) {
padding: 0 1rem;
}
}
&-name {
color: #707579;
font-size: 15px;
font-weight: 500;
}
}
.range-setting-selector {
&-details {
display: flex;
justify-content: space-between;
}
.progress-line {
--height: 2px;
--border-radius: 4px;
background-color: #e6ecf0;
&__filled {
background-color: #3390ec;
}
&__seek {
--thumb-color: #3390ec;
--thumb-size: 12px;
}
}
}
Loading…
Cancel
Save