Telegram Web K with changes to work inside I2P
https://web.telegram.i2p/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
355 lines
11 KiB
355 lines
11 KiB
/* |
|
* https://github.com/morethanwords/tweb |
|
* Copyright (C) 2019-2021 Eduard Kuzmenko |
|
* https://github.com/morethanwords/tweb/blob/master/LICENSE |
|
*/ |
|
|
|
import CheckboxField, {CheckboxFieldOptions} from './checkboxField'; |
|
import RadioField from './radioField'; |
|
import ripple from './ripple'; |
|
import {SliderSuperTab} from './slider'; |
|
import RadioForm from './radioForm'; |
|
import {i18n, LangPackKey} from '../lib/langPack'; |
|
import replaceContent from '../helpers/dom/replaceContent'; |
|
import setInnerHTML, {setDirection} from '../helpers/dom/setInnerHTML'; |
|
import {attachClickEvent} from '../helpers/dom/clickEvent'; |
|
import ListenerSetter from '../helpers/listenerSetter'; |
|
import Button from './button'; |
|
import createContextMenu from '../helpers/dom/createContextMenu'; |
|
|
|
type K = string | HTMLElement | DocumentFragment | true; |
|
|
|
const setContent = (element: HTMLElement, content: K) => { |
|
if(content === true) { |
|
|
|
} else if(typeof(content) === 'string') { |
|
setInnerHTML(element, content); |
|
} else { |
|
element.append(content); |
|
} |
|
}; |
|
|
|
export type RowMediaSizeType = 'small' | 'medium' | 'big' | 'abitbigger' | 'bigger'; |
|
|
|
export default class Row { |
|
public container: HTMLElement; |
|
public titleRow: HTMLElement; |
|
public titleRight: HTMLElement; |
|
public media: HTMLElement; |
|
|
|
public subtitleRow: HTMLElement; |
|
public subtitleRight: HTMLElement; |
|
|
|
public checkboxField: CheckboxField; |
|
public radioField: RadioField; |
|
|
|
public freezed = false; |
|
|
|
public buttonRight: HTMLElement; |
|
|
|
private _title: HTMLElement; |
|
private _subtitle: HTMLElement; |
|
private _midtitle: HTMLElement; |
|
|
|
constructor(options: Partial<{ |
|
icon: string, |
|
subtitle: K, |
|
subtitleLangKey: LangPackKey, |
|
subtitleLangArgs: any[], |
|
subtitleRight: K, |
|
radioField: Row['radioField'], |
|
checkboxField: Row['checkboxField'], |
|
checkboxFieldOptions: CheckboxFieldOptions, |
|
withCheckboxSubtitle: boolean, |
|
title: K, |
|
titleLangKey: LangPackKey, |
|
titleRight: K, |
|
titleRightSecondary: K, |
|
clickable: boolean | ((e: Event) => void), |
|
navigationTab: SliderSuperTab, |
|
havePadding: boolean, |
|
noRipple: boolean, |
|
noWrap: boolean, |
|
listenerSetter: ListenerSetter, |
|
buttonRight?: HTMLElement | boolean, |
|
buttonRightLangKey: LangPackKey, |
|
asLink: boolean, |
|
contextMenu: Omit<Parameters<typeof createContextMenu>[0], 'findElement' | 'listenTo' | 'listenerSetter'> |
|
}> = {}) { |
|
if(options.checkboxFieldOptions) { |
|
options.checkboxField = new CheckboxField({ |
|
listenerSetter: options.listenerSetter, |
|
...options.checkboxFieldOptions |
|
}); |
|
} |
|
|
|
const tagName = options.asLink ? 'a' : (options.radioField || options.checkboxField ? 'label' : 'div'); |
|
this.container = document.createElement(tagName); |
|
this.container.classList.add('row', 'no-subtitle'); |
|
|
|
if(options.noWrap) { |
|
this.container.classList.add('no-wrap'); |
|
} |
|
|
|
if(options.subtitle) { |
|
const subtitle = this.subtitle; |
|
setContent(subtitle, options.subtitle); |
|
|
|
if(options.noWrap) subtitle.classList.add('no-wrap'); |
|
|
|
if(options.subtitleRight) { |
|
this.container.append(this.subtitleRow = this.createRow()); |
|
this.subtitleRow.classList.add('row-subtitle-row'); |
|
const subtitleRight = this.subtitleRight = document.createElement('div'); |
|
subtitleRight.classList.add('row-subtitle', 'row-subtitle-right'); |
|
|
|
setContent(subtitleRight, options.subtitleRight); |
|
this.subtitleRow.append(subtitle, subtitleRight); |
|
} |
|
} else if(options.subtitleLangKey) { |
|
this.subtitle.append(i18n(options.subtitleLangKey, options.subtitleLangArgs)); |
|
} |
|
|
|
let havePadding = !!options.havePadding; |
|
if(options.radioField || options.checkboxField) { |
|
if(options.radioField) { |
|
this.radioField = options.radioField; |
|
this.container.append(this.radioField.label); |
|
havePadding = true; |
|
} |
|
|
|
if(options.checkboxField) { |
|
this.checkboxField = options.checkboxField; |
|
|
|
const isToggle = options.checkboxField.label.classList.contains('checkbox-field-toggle'); |
|
if(isToggle) { |
|
this.container.classList.add('row-with-toggle'); |
|
options.titleRight = this.checkboxField.label; |
|
} else { |
|
havePadding = true; |
|
if(!this.checkboxField.span) { |
|
this.checkboxField.label.classList.add('checkbox-field-absolute'); |
|
} |
|
this.container.append(this.checkboxField.label); |
|
} |
|
|
|
if(options.withCheckboxSubtitle && !isToggle) { |
|
const onChange = () => { |
|
replaceContent(this.subtitle, i18n(this.checkboxField.input.checked ? 'Checkbox.Enabled' : 'Checkbox.Disabled')); |
|
}; |
|
|
|
if(options.listenerSetter) options.listenerSetter.add(this.checkboxField.input)('change', onChange); |
|
else this.checkboxField.input.addEventListener('change', onChange); |
|
} |
|
} |
|
|
|
const i = options.radioField || options.checkboxField; |
|
i.label.classList.add('disable-hover'); |
|
} |
|
|
|
if(options.title || options.titleLangKey || options.titleRight || options.titleRightSecondary) { |
|
let c: HTMLElement; |
|
const titleRightContent = options.titleRight || options.titleRightSecondary; |
|
if(titleRightContent) { |
|
this.container.append(c = this.titleRow = this.createRow()); |
|
this.titleRow.classList.add('row-title-row'); |
|
} else { |
|
c = this.container; |
|
} |
|
|
|
this._title = this.createTitle(); |
|
if(options.noWrap) this.title.classList.add('no-wrap'); |
|
if(options.title) { |
|
setContent(this.title, options.title); |
|
} else if(options.titleLangKey) { |
|
this.title.append(i18n(options.titleLangKey)); |
|
} |
|
|
|
c.append(this.title); |
|
|
|
if(titleRightContent) { |
|
const titleRight = this.titleRight = document.createElement('div'); |
|
titleRight.classList.add('row-title', 'row-title-right'); |
|
|
|
if(options.titleRightSecondary) { |
|
titleRight.classList.add('row-title-right-secondary'); |
|
} |
|
|
|
setContent(titleRight, titleRightContent); |
|
c.append(titleRight); |
|
} |
|
} |
|
|
|
if(options.icon) { |
|
havePadding = true; |
|
// this.title.classList.add('tgico', 'tgico-' + options.icon); |
|
this.container.classList.add('tgico', 'tgico-' + options.icon); |
|
this.container.classList.add('row-with-icon'); |
|
} |
|
|
|
if(havePadding) { |
|
this.container.classList.add('row-with-padding'); |
|
} |
|
|
|
if(options.navigationTab) { |
|
options.clickable = () => options.navigationTab.open(); |
|
} |
|
|
|
if(options.clickable || options.radioField || options.checkboxField) { |
|
if(typeof(options.clickable) === 'function') { |
|
attachClickEvent(this.container, (e) => { |
|
if(this.freezed) return; |
|
(options.clickable as any)(e); |
|
}, {listenerSetter: options.listenerSetter}); |
|
} |
|
|
|
this.container.classList.add('row-clickable', 'hover-effect'); |
|
|
|
if(!options.noRipple) { |
|
ripple(this.container, undefined, undefined, true); |
|
} |
|
|
|
/* if(options.radioField || options.checkboxField) { |
|
this.container.prepend(this.container.lastElementChild); |
|
} */ |
|
} |
|
|
|
if(options.buttonRight || options.buttonRightLangKey) { |
|
this.buttonRight = options.buttonRight instanceof HTMLElement ? |
|
options.buttonRight : |
|
Button('btn-primary btn-color-primary', {text: options.buttonRightLangKey}); |
|
this.container.append(this.buttonRight); |
|
} |
|
|
|
if(options.contextMenu) { |
|
createContextMenu({ |
|
...options.contextMenu, |
|
listenTo: this.container, |
|
listenerSetter: options.listenerSetter |
|
}); |
|
} |
|
} |
|
|
|
public get title() { |
|
return this._title; |
|
} |
|
|
|
public get subtitle() { |
|
return this._subtitle ??= this.createSubtitle(); |
|
} |
|
|
|
public get midtitle() { |
|
return this._midtitle ??= this.createMidtitle(); |
|
} |
|
|
|
private createRow() { |
|
const c = document.createElement('div'); |
|
c.classList.add('row-row'); |
|
return c; |
|
} |
|
|
|
private createTitle() { |
|
const title = document.createElement('div'); |
|
title.classList.add('row-title'); |
|
setDirection(title); |
|
return title; |
|
} |
|
|
|
private createSubtitle() { |
|
const subtitle = document.createElement('div'); |
|
subtitle.classList.add('row-subtitle'); |
|
setDirection(subtitle); |
|
if(this.title) this.title.after(subtitle); |
|
else this.container.prepend(subtitle); |
|
this.container.classList.remove('no-subtitle'); |
|
return subtitle; |
|
} |
|
|
|
private createMidtitle() { |
|
const midtitle = document.createElement('div'); |
|
midtitle.classList.add('row-midtitle'); |
|
this.subtitle.parentElement.insertBefore(midtitle, this.subtitle); |
|
return midtitle; |
|
} |
|
|
|
public createMedia(size?: RowMediaSizeType) { |
|
const media = document.createElement('div'); |
|
return this.applyMediaElement(media, size); |
|
} |
|
|
|
public applyMediaElement(media: HTMLElement, size?: RowMediaSizeType) { |
|
this.container.classList.add('row-with-padding'); |
|
|
|
this.media = media; |
|
media.classList.add('row-media'); |
|
|
|
if(size) { |
|
media.classList.add('row-media-' + size); |
|
} |
|
|
|
this.container.append(media); |
|
|
|
return media; |
|
} |
|
|
|
public isDisabled() { |
|
return this.container.classList.contains('is-disabled'); |
|
} |
|
|
|
public toggleDisability(disable = !this.container.classList.contains('is-disabled')) { |
|
this.container.classList.toggle('is-disabled', disable); |
|
return () => this.toggleDisability(!disable); |
|
} |
|
|
|
public disableWithPromise(promise: Promise<any>) { |
|
const toggle = this.toggleDisability(true); |
|
promise.finally(() => { |
|
toggle(); |
|
}); |
|
} |
|
|
|
public makeSortable() { |
|
const sortIcon = document.createElement('span'); |
|
this.container.classList.add('row-sortable', 'tgico'); |
|
sortIcon.classList.add('row-sortable-icon', 'tgico-menu'); |
|
this.container.append(sortIcon); |
|
} |
|
|
|
public toggleSorting(enabled?: boolean) { |
|
this.container.classList.toggle('cant-sort', !enabled); |
|
} |
|
} |
|
|
|
export const CreateRowFromCheckboxField = (checkboxField: CheckboxField) => { |
|
return new Row({checkboxField, listenerSetter: checkboxField.listenerSetter}); |
|
}; |
|
|
|
export const RadioFormFromRows = (rows: Row[], onChange: (value: string) => void) => { |
|
return RadioForm(rows.map((r) => ({container: r.container, input: r.radioField.input})), onChange); |
|
}; |
|
|
|
export const RadioFormFromValues = (values: {langPackKey: LangPackKey, value: number | string, checked?: boolean}[], onChange: Parameters<typeof RadioFormFromRows>[1]) => { |
|
const name = 'name-' + (Math.random() * 0x7FFFFF | 0); |
|
let checkedRadioField: RadioField; |
|
const rows = values.map(({langPackKey, value, checked}) => { |
|
const row = new Row({ |
|
radioField: new RadioField({ |
|
langKey: langPackKey, |
|
name, |
|
value: '' + value |
|
}) |
|
}); |
|
|
|
if(checked) { |
|
checkedRadioField = row.radioField; |
|
} |
|
|
|
return row; |
|
}); |
|
|
|
const form = RadioFormFromRows(rows, onChange); |
|
if(checkedRadioField) { |
|
checkedRadioField.checked = true; |
|
} |
|
return form; |
|
};
|
|
|