Eduard Kuzmenko
4 years ago
14 changed files with 673 additions and 69 deletions
@ -0,0 +1,336 @@ |
|||||||
|
import { ColorHsla, hexaToHsla, hslaToRgba, rgbaToHexa as rgbaToHexa, rgbaToHsla } from "../helpers/color"; |
||||||
|
import { clamp } from "../helpers/number"; |
||||||
|
import InputField, { InputState } from "./inputField"; |
||||||
|
|
||||||
|
type EventPosition = {x: number, y: number, isTouch?: boolean}; |
||||||
|
export type ColorPickerColor = { |
||||||
|
hsl: string; |
||||||
|
rgb: string; |
||||||
|
hex: string; |
||||||
|
hsla: string; |
||||||
|
rgba: string; |
||||||
|
hexa: string; |
||||||
|
rgbaArray: import("f:/tweb/src/helpers/color").ColorRgba; |
||||||
|
}; |
||||||
|
|
||||||
|
export default class ColorPicker { |
||||||
|
private static BASE_CLASS = 'color-picker'; |
||||||
|
public container: HTMLElement; |
||||||
|
|
||||||
|
private boxRect: DOMRect; |
||||||
|
//private boxDraggerRect: DOMRect;
|
||||||
|
private hueRect: DOMRect; |
||||||
|
//private hueDraggerRect: DOMRect;
|
||||||
|
|
||||||
|
private hue = 0; |
||||||
|
private saturation = 100; |
||||||
|
private lightness = 50; |
||||||
|
private alpha = 1; |
||||||
|
private elements: { |
||||||
|
box: SVGSVGElement, |
||||||
|
boxDragger: SVGSVGElement, |
||||||
|
sliders: HTMLElement, |
||||||
|
hue: SVGSVGElement, |
||||||
|
hueDragger: SVGSVGElement, |
||||||
|
saturation: SVGLinearGradientElement, |
||||||
|
} = {} as any; |
||||||
|
private hexInputField: InputField; |
||||||
|
private rgbInputField: InputField; |
||||||
|
public onChange: (color: ReturnType<ColorPicker['getCurrentColor']>) => void; |
||||||
|
|
||||||
|
constructor() { |
||||||
|
this.container = document.createElement('div'); |
||||||
|
this.container.classList.add(ColorPicker.BASE_CLASS); |
||||||
|
|
||||||
|
const html = ` |
||||||
|
<svg class="${ColorPicker.BASE_CLASS + '-box'}" viewBox="0 0 380 198"> |
||||||
|
<defs> |
||||||
|
<linearGradient id="color-picker-saturation" x1="0%" y1="0%" x2="100%" y2="0%"> |
||||||
|
<stop offset="0%" stop-color="#fff"></stop> |
||||||
|
<stop offset="100%" stop-color="hsl(0,100%,50%)"></stop> |
||||||
|
</linearGradient> |
||||||
|
<linearGradient id="color-picker-brightness" x1="0%" y1="0%" x2="0%" y2="100%"> |
||||||
|
<stop offset="0%" stop-color="rgba(0,0,0,0)"></stop> |
||||||
|
<stop offset="100%" stop-color="#000"></stop> |
||||||
|
</linearGradient> |
||||||
|
<pattern id="color-picker-pattern" width="100%" height="100%"> |
||||||
|
<rect x="0" y="0" width="100%" height="100%" fill="url(#color-picker-saturation)"></rect> |
||||||
|
<rect x="0" y="0" width="100%" height="100%" fill="url(#color-picker-brightness)"></rect> |
||||||
|
</pattern> |
||||||
|
</defs> |
||||||
|
<rect rx="10" ry="10" x="0" y="0" width="380" height="198" fill="url(#color-picker-pattern)"></rect> |
||||||
|
<svg class="${ColorPicker.BASE_CLASS + '-dragger'} ${ColorPicker.BASE_CLASS + '-box-dragger'}" x="0" y="0"> |
||||||
|
<circle r="11" fill="inherit" stroke="#fff" stroke-width="2"></circle> |
||||||
|
</svg> |
||||||
|
</svg> |
||||||
|
<div class="${ColorPicker.BASE_CLASS + '-sliders'}"> |
||||||
|
<svg class="${ColorPicker.BASE_CLASS + '-color-slider'}" viewBox="0 0 380 24"> |
||||||
|
<defs> |
||||||
|
<linearGradient id="hue" x1="100%" y1="0%" x2="0%" y2="0%"> |
||||||
|
<stop offset="0%" stop-color="#f00"></stop> |
||||||
|
<stop offset="16.666%" stop-color="#f0f"></stop> |
||||||
|
<stop offset="33.333%" stop-color="#00f"></stop> |
||||||
|
<stop offset="50%" stop-color="#0ff"></stop> |
||||||
|
<stop offset="66.666%" stop-color="#0f0"></stop> |
||||||
|
<stop offset="83.333%" stop-color="#ff0"></stop> |
||||||
|
<stop offset="100%" stop-color="#f00"></stop> |
||||||
|
</linearGradient> |
||||||
|
</defs> |
||||||
|
<rect rx="4" ry="4" x="0" y="9" width="380" height="8" fill="url(#hue)"></rect> |
||||||
|
<svg class="${ColorPicker.BASE_CLASS + '-dragger'} ${ColorPicker.BASE_CLASS + '-color-slider-dragger'}" x="0" y="13"> |
||||||
|
<circle r="11" fill="inherit" stroke="#fff" stroke-width="2"></circle> |
||||||
|
</svg> |
||||||
|
</svg> |
||||||
|
</div> |
||||||
|
`;
|
||||||
|
|
||||||
|
this.container.innerHTML = html; |
||||||
|
|
||||||
|
this.elements.box = this.container.firstElementChild as any; |
||||||
|
this.elements.boxDragger = this.elements.box.lastElementChild as any; |
||||||
|
this.elements.saturation = this.elements.box.firstElementChild.firstElementChild as any; |
||||||
|
|
||||||
|
this.elements.sliders = this.elements.box.nextElementSibling as any; |
||||||
|
|
||||||
|
this.elements.hue = this.elements.sliders.firstElementChild as any; |
||||||
|
this.elements.hueDragger = this.elements.hue.lastElementChild as any; |
||||||
|
|
||||||
|
this.hexInputField = new InputField({plainText: true, label: 'Appearance.Color.Hex'}); |
||||||
|
this.rgbInputField = new InputField({plainText: true, label: 'Appearance.Color.RGB'}); |
||||||
|
|
||||||
|
const inputs = document.createElement('div'); |
||||||
|
inputs.className = ColorPicker.BASE_CLASS + '-inputs'; |
||||||
|
inputs.append(this.hexInputField.container, this.rgbInputField.container); |
||||||
|
this.container.append(inputs); |
||||||
|
|
||||||
|
this.hexInputField.input.addEventListener('input', () => { |
||||||
|
let value = this.hexInputField.value.replace(/#/g, '').slice(0, 6); |
||||||
|
|
||||||
|
const match = value.match(/([a-fA-F\d]+)/); |
||||||
|
const valid = match && match[0].length === value.length && [/* 3, 4, */6].includes(value.length); |
||||||
|
this.hexInputField.setState(valid ? InputState.Neutral : InputState.Error); |
||||||
|
|
||||||
|
value = '#' + value; |
||||||
|
this.hexInputField.setValueSilently(value); |
||||||
|
|
||||||
|
if(valid) { |
||||||
|
this.setColor(value, false, true); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
// patched https://stackoverflow.com/a/34029238/6758968
|
||||||
|
const rgbRegExp = /^(?:rgb)?\(?([01]?\d\d?|2[0-4]\d|25[0-5])(?:\W+)([01]?\d\d?|2[0-4]\d|25[0-5])\W+(?:([01]?\d\d?|2[0-4]\d|25[0-5])\)?)$/; |
||||||
|
this.rgbInputField.input.addEventListener('input', () => { |
||||||
|
const match = this.rgbInputField.value.match(rgbRegExp); |
||||||
|
this.rgbInputField.setState(match ? InputState.Neutral : InputState.Error); |
||||||
|
|
||||||
|
if(match) { |
||||||
|
this.setColor(rgbaToHsla(+match[1], +match[2], +match[3]), true, false); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
this.attachBoxListeners(); |
||||||
|
this.attachHueListeners(); |
||||||
|
} |
||||||
|
|
||||||
|
private attachGrabListeners(element: SVGSVGElement, onStart: (position: EventPosition) => void, onMove: (position: EventPosition) => void, onEnd: (position: EventPosition) => void) { |
||||||
|
// * Mouse
|
||||||
|
const onMouseMove = (event: MouseEvent) => { |
||||||
|
onMove({x: event.pageX, y: event.pageY}); |
||||||
|
}; |
||||||
|
|
||||||
|
const onMouseDown = (event: MouseEvent) => { |
||||||
|
if(event.button !== 0) { |
||||||
|
element.addEventListener('mousedown', onMouseDown, {once: true}); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
this.onGrabStart(); |
||||||
|
onStart({x: event.pageX, y: event.pageY}); |
||||||
|
onMouseMove(event); |
||||||
|
|
||||||
|
document.addEventListener('mousemove', onMouseMove); |
||||||
|
document.addEventListener('mouseup', () => { |
||||||
|
document.removeEventListener('mousemove', onMouseMove); |
||||||
|
element.addEventListener('mousedown', onMouseDown, {once: true}); |
||||||
|
this.onGrabEnd(); |
||||||
|
onEnd && onEnd({x: event.pageX, y: event.pageY}); |
||||||
|
}, {once: true}); |
||||||
|
}; |
||||||
|
|
||||||
|
element.addEventListener('mousedown', onMouseDown, {once: true}); |
||||||
|
|
||||||
|
// * Touch
|
||||||
|
const onTouchMove = (event: TouchEvent) => { |
||||||
|
event.preventDefault(); |
||||||
|
onMove({x: event.touches[0].clientX, y: event.touches[0].clientY, isTouch: true}); |
||||||
|
}; |
||||||
|
|
||||||
|
const onTouchStart = (event: TouchEvent) => { |
||||||
|
this.onGrabStart(); |
||||||
|
onStart({x: event.touches[0].clientX, y: event.touches[0].clientY, isTouch: true}); |
||||||
|
onTouchMove(event); |
||||||
|
|
||||||
|
document.addEventListener('touchmove', onTouchMove, {passive: false}); |
||||||
|
document.addEventListener('touchend', (event) => { |
||||||
|
document.removeEventListener('touchmove', onTouchMove); |
||||||
|
element.addEventListener('touchstart', onTouchStart, {passive: true, once: true}); |
||||||
|
this.onGrabEnd(); |
||||||
|
onEnd && onEnd({x: event.touches[0].clientX, y: event.touches[0].clientY, isTouch: true}); |
||||||
|
}, {passive: true, once: true}); |
||||||
|
}; |
||||||
|
|
||||||
|
element.addEventListener('touchstart', onTouchStart, {passive: true, once: true}); |
||||||
|
} |
||||||
|
|
||||||
|
private onGrabStart = () => { |
||||||
|
document.documentElement.style.cursor = this.elements.boxDragger.style.cursor = 'grabbing'; |
||||||
|
}; |
||||||
|
|
||||||
|
private onGrabEnd = () => { |
||||||
|
document.documentElement.style.cursor = this.elements.boxDragger.style.cursor = ''; |
||||||
|
}; |
||||||
|
|
||||||
|
private attachBoxListeners() { |
||||||
|
this.attachGrabListeners(this.elements.box, () => { |
||||||
|
this.boxRect = this.elements.box.getBoundingClientRect(); |
||||||
|
//this.boxDraggerRect = this.elements.boxDragger.getBoundingClientRect();
|
||||||
|
}, (pos) => { |
||||||
|
this.saturationHandler(pos.x, pos.y); |
||||||
|
}, null); |
||||||
|
} |
||||||
|
|
||||||
|
private attachHueListeners() { |
||||||
|
this.attachGrabListeners(this.elements.hue, () => { |
||||||
|
this.hueRect = this.elements.hue.getBoundingClientRect(); |
||||||
|
//this.hueDraggerRect = this.elements.hueDragger.getBoundingClientRect();
|
||||||
|
}, (pos) => { |
||||||
|
this.hueHandler(pos.x); |
||||||
|
}, null); |
||||||
|
} |
||||||
|
|
||||||
|
public setColor(color: ColorHsla | string, updateHexInput = true, updateRgbInput = true) { |
||||||
|
if(color === undefined) { // * set to red
|
||||||
|
color = { |
||||||
|
h: 0, |
||||||
|
s: 100, |
||||||
|
l: 50, |
||||||
|
a: 1 |
||||||
|
}; |
||||||
|
} else if(typeof(color) === 'string') { |
||||||
|
if(color[0] === '#') { |
||||||
|
color = hexaToHsla(color); |
||||||
|
} else { |
||||||
|
const rgb = color.match(/[.?\d]+/g); |
||||||
|
color = rgbaToHsla(+rgb[0], +rgb[1], +rgb[2], rgb[3] === undefined ? 1 : +rgb[3]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Set box
|
||||||
|
this.boxRect = this.elements.box.getBoundingClientRect(); |
||||||
|
|
||||||
|
const boxX = this.boxRect.width / 100 * color.s; |
||||||
|
const percentY = 100 - (color.l / (100 - color.s / 2)) * 100; |
||||||
|
const boxY = this.boxRect.height / 100 * percentY; |
||||||
|
|
||||||
|
this.saturationHandler(this.boxRect.left + boxX, this.boxRect.top + boxY, false); |
||||||
|
|
||||||
|
// Set hue
|
||||||
|
this.hueRect = this.elements.hue.getBoundingClientRect(); |
||||||
|
|
||||||
|
const percentHue = color.h / 360; |
||||||
|
const hueX = this.hueRect.left + this.hueRect.width * percentHue; |
||||||
|
|
||||||
|
this.hueHandler(hueX, false); |
||||||
|
|
||||||
|
// Set values
|
||||||
|
this.hue = color.h; |
||||||
|
this.saturation = color.s; |
||||||
|
this.lightness = color.l; |
||||||
|
this.alpha = color.a; |
||||||
|
|
||||||
|
this.updatePicker(updateHexInput, updateRgbInput); |
||||||
|
}; |
||||||
|
|
||||||
|
public getCurrentColor(): ColorPickerColor { |
||||||
|
const rgbaArray = hslaToRgba(this.hue, this.saturation, this.lightness, this.alpha); |
||||||
|
const hexa = rgbaToHexa(rgbaArray); |
||||||
|
const hex = hexa.slice(0, -2); |
||||||
|
|
||||||
|
return { |
||||||
|
hsl: `hsl(${this.hue}, ${this.saturation}%, ${this.lightness}%)`, |
||||||
|
rgb: `rgb(${rgbaArray[0]}, ${rgbaArray[1]}, ${rgbaArray[2]})`, |
||||||
|
hex: hex, |
||||||
|
hsla: `hsla(${this.hue}, ${this.saturation}%, ${this.lightness}%, ${this.alpha})`, |
||||||
|
rgba: `rgba(${rgbaArray[0]}, ${rgbaArray[1]}, ${rgbaArray[2]}, ${rgbaArray[3]})`, |
||||||
|
hexa: hexa, |
||||||
|
rgbaArray: rgbaArray |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
public updatePicker(updateHexInput = true, updateRgbInput = true) { |
||||||
|
const color = this.getCurrentColor(); |
||||||
|
this.elements.boxDragger.setAttributeNS(null, 'fill', color.hex); |
||||||
|
|
||||||
|
if(updateHexInput) { |
||||||
|
this.hexInputField.setValueSilently(color.hex); |
||||||
|
this.hexInputField.setState(InputState.Neutral); |
||||||
|
} |
||||||
|
|
||||||
|
if(updateRgbInput) { |
||||||
|
this.rgbInputField.setValueSilently(color.rgbaArray.slice(0, -1).join(', ')); |
||||||
|
this.rgbInputField.setState(InputState.Neutral); |
||||||
|
} |
||||||
|
|
||||||
|
if(this.onChange) { |
||||||
|
this.onChange(color); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private hueHandler(pageX: number, update = true) { |
||||||
|
const eventX = clamp(pageX - this.hueRect.left, 0, this.hueRect.width); |
||||||
|
|
||||||
|
const percents = eventX / this.hueRect.width; |
||||||
|
this.hue = Math.round(360 * percents); |
||||||
|
|
||||||
|
const hsla = `hsla(${this.hue}, 100%, 50%, ${this.alpha})`; |
||||||
|
|
||||||
|
this.elements.hueDragger.setAttributeNS(null, 'x', (percents * 100) + '%'); |
||||||
|
this.elements.hueDragger.setAttributeNS(null, 'fill', hsla); |
||||||
|
|
||||||
|
this.elements.saturation.lastElementChild.setAttributeNS(null, 'stop-color', hsla); |
||||||
|
|
||||||
|
if(update) { |
||||||
|
this.updatePicker(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private saturationHandler(pageX: number, pageY: number, update = true) { |
||||||
|
const maxX = this.boxRect.width; |
||||||
|
const maxY = this.boxRect.height; |
||||||
|
|
||||||
|
const eventX = clamp(pageX - this.boxRect.left, 0, maxX); |
||||||
|
const eventY = clamp(pageY - this.boxRect.top, 0, maxY); |
||||||
|
|
||||||
|
const posX = eventX / maxX * 100; |
||||||
|
const posY = eventY / maxY * 100; |
||||||
|
|
||||||
|
const boxDragger = this.elements.boxDragger; |
||||||
|
boxDragger.setAttributeNS(null, 'x', posX + '%'); |
||||||
|
boxDragger.setAttributeNS(null, 'y', posY + '%'); |
||||||
|
|
||||||
|
const saturation = clamp(posX, 0, 100); |
||||||
|
|
||||||
|
const lightnessX = 100 - saturation / 2; |
||||||
|
const lightnessY = 100 - clamp(posY, 0, 100); |
||||||
|
|
||||||
|
const lightness = clamp(lightnessY / 100 * lightnessX, 0, 100); |
||||||
|
|
||||||
|
this.saturation = saturation; |
||||||
|
this.lightness = lightness; |
||||||
|
|
||||||
|
if(update) { |
||||||
|
this.updatePicker(); |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
@ -0,0 +1,142 @@ |
|||||||
|
import { SettingSection } from ".."; |
||||||
|
import { hexaToRgba } from "../../../helpers/color"; |
||||||
|
import { attachClickEvent } from "../../../helpers/dom"; |
||||||
|
import findUpClassName from "../../../helpers/dom/findUpClassName"; |
||||||
|
import highlightningColor from "../../../helpers/highlightningColor"; |
||||||
|
import { throttle } from "../../../helpers/schedulers"; |
||||||
|
import appImManager from "../../../lib/appManagers/appImManager"; |
||||||
|
import appStateManager from "../../../lib/appManagers/appStateManager"; |
||||||
|
import rootScope from "../../../lib/rootScope"; |
||||||
|
import ColorPicker, { ColorPickerColor } from "../../colorPicker"; |
||||||
|
import { SliderSuperTab } from "../../slider"; |
||||||
|
|
||||||
|
export default class AppBackgroundColorTab extends SliderSuperTab { |
||||||
|
private colorPicker: ColorPicker; |
||||||
|
private grid: HTMLElement; |
||||||
|
private applyColor: (hex: string, updateColorPicker?: boolean) => void; |
||||||
|
|
||||||
|
init() { |
||||||
|
this.container.classList.add('background-container', 'background-color-container'); |
||||||
|
this.setTitle('SetColor'); |
||||||
|
|
||||||
|
const section = new SettingSection({}); |
||||||
|
this.colorPicker = new ColorPicker(); |
||||||
|
|
||||||
|
section.content.append(this.colorPicker.container); |
||||||
|
|
||||||
|
this.scrollable.append(section.container); |
||||||
|
|
||||||
|
const grid = this.grid = document.createElement('div'); |
||||||
|
grid.classList.add('grid'); |
||||||
|
|
||||||
|
const colors = [ |
||||||
|
'#E6EBEE', |
||||||
|
'#B2CEE1', |
||||||
|
'#008DD0', |
||||||
|
'#C6E7CB', |
||||||
|
'#C4E1A6', |
||||||
|
'#60B16E', |
||||||
|
'#CCD0AF', |
||||||
|
'#A6A997', |
||||||
|
'#7A7072', |
||||||
|
'#FDD7AF', |
||||||
|
'#FDB76E', |
||||||
|
'#DD8851' |
||||||
|
]; |
||||||
|
|
||||||
|
colors.forEach(color => { |
||||||
|
const item = document.createElement('div'); |
||||||
|
item.classList.add('grid-item'); |
||||||
|
item.dataset.color = color.toLowerCase(); |
||||||
|
|
||||||
|
// * need for transform scale
|
||||||
|
const media = document.createElement('div'); |
||||||
|
media.classList.add('grid-item-media'); |
||||||
|
media.style.backgroundColor = color; |
||||||
|
|
||||||
|
item.append(media); |
||||||
|
grid.append(item); |
||||||
|
}); |
||||||
|
|
||||||
|
attachClickEvent(grid, (e) => { |
||||||
|
const target = findUpClassName(e.target, 'grid-item'); |
||||||
|
if(!target || target.classList.contains('active')) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
const color = target.dataset.color; |
||||||
|
if(!color) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
this.applyColor(color); |
||||||
|
}, {listenerSetter: this.listenerSetter}); |
||||||
|
|
||||||
|
this.scrollable.append(grid); |
||||||
|
|
||||||
|
this.applyColor = throttle(this._applyColor, 16, true); |
||||||
|
} |
||||||
|
|
||||||
|
private setActive() { |
||||||
|
const active = this.grid.querySelector('.active'); |
||||||
|
const background = rootScope.settings.themes.find(t => t.name === rootScope.settings.theme).background; |
||||||
|
const target = background.type === 'color' ? this.grid.querySelector(`.grid-item[data-color="${background.color}"]`) : null; |
||||||
|
if(active === target) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if(active) { |
||||||
|
active.classList.remove('active'); |
||||||
|
} |
||||||
|
|
||||||
|
if(target) { |
||||||
|
target.classList.add('active'); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private _applyColor = (hex: string, updateColorPicker = true) => { |
||||||
|
if(updateColorPicker) { |
||||||
|
this.colorPicker.setColor(hex); |
||||||
|
} else { |
||||||
|
const rgba = hexaToRgba(hex); |
||||||
|
const background = rootScope.settings.themes.find(t => t.name === rootScope.settings.theme).background; |
||||||
|
const hsla = highlightningColor(rgba); |
||||||
|
|
||||||
|
background.color = hex.toLowerCase(); |
||||||
|
background.type = 'color'; |
||||||
|
background.highlightningColor = hsla; |
||||||
|
appStateManager.pushToState('settings', rootScope.settings); |
||||||
|
|
||||||
|
appImManager.applyCurrentTheme(undefined, undefined, true); |
||||||
|
this.setActive(); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
private onColorChange = (color: ColorPickerColor) => { |
||||||
|
this.applyColor(color.hex, false); |
||||||
|
}; |
||||||
|
|
||||||
|
onOpen() { |
||||||
|
setTimeout(() => { |
||||||
|
const background = rootScope.settings.themes.find(t => t.name === rootScope.settings.theme).background; |
||||||
|
|
||||||
|
// * set active if type is color
|
||||||
|
if(background.type === 'color') { |
||||||
|
this.colorPicker.onChange = this.onColorChange; |
||||||
|
} |
||||||
|
|
||||||
|
this.colorPicker.setColor(background.color || '#cccccc'); |
||||||
|
|
||||||
|
if(background.type !== 'color') { |
||||||
|
this.colorPicker.onChange = this.onColorChange; |
||||||
|
} |
||||||
|
}, 0); |
||||||
|
} |
||||||
|
|
||||||
|
onCloseAfterTimeout() { |
||||||
|
this.colorPicker.onChange = undefined; |
||||||
|
this.colorPicker = undefined; |
||||||
|
|
||||||
|
return super.onCloseAfterTimeout(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,13 @@ |
|||||||
|
import { rgbaToHsla } from "./color"; |
||||||
|
|
||||||
|
// * https://github.com/TelegramMessenger/Telegram-iOS/blob/3d062fff78cc6b287c74e6171f855a3500c0156d/submodules/TelegramPresentationData/Sources/PresentationData.swift#L453
|
||||||
|
export default function highlightningColor(rgba: [number, number, number, number?]) { |
||||||
|
let {h, s, l} = rgbaToHsla(rgba[0], rgba[1], rgba[2]); |
||||||
|
if(s > 0) { |
||||||
|
s = Math.min(100, s + 5 + 0.1 * (100 - s)); |
||||||
|
} |
||||||
|
l = Math.max(0, l * .65); |
||||||
|
|
||||||
|
const hsla = `hsla(${h}, ${s}%, ${l}%, .4)`; |
||||||
|
return hsla; |
||||||
|
} |
@ -0,0 +1,35 @@ |
|||||||
|
.color-picker { |
||||||
|
width: 380px; |
||||||
|
max-width: 100%; |
||||||
|
margin: 1.1875rem auto 1rem; |
||||||
|
user-select: none; |
||||||
|
|
||||||
|
&-box { |
||||||
|
width: 100%; |
||||||
|
height: 198px; |
||||||
|
} |
||||||
|
|
||||||
|
&-box, &-color-slider, &-dragger { |
||||||
|
overflow: visible !important; |
||||||
|
} |
||||||
|
|
||||||
|
&-sliders { |
||||||
|
margin: 1rem 0 1.125rem; |
||||||
|
} |
||||||
|
|
||||||
|
&-dragger { |
||||||
|
cursor: grab; |
||||||
|
} |
||||||
|
|
||||||
|
&-inputs { |
||||||
|
display: flex; |
||||||
|
|
||||||
|
.input-field { |
||||||
|
flex: 1 1 auto; |
||||||
|
|
||||||
|
&:not(:first-child) { |
||||||
|
margin-left: 1.25rem; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue