(core) Makes the hex value editable in the color select

Summary:
 - reuses the textInput form the editableLabel module
 - adds a isValidHex utility function to gutil

Test Plan: - Adds test to the project test

Reviewers: paulfitz

Reviewed By: paulfitz

Differential Revision: https://phab.getgrist.com/D2744
This commit is contained in:
Cyprien P 2021-03-04 12:04:02 +01:00
parent 92ef1f400c
commit 5e5bf3af9d
3 changed files with 28 additions and 10 deletions

View File

@ -1,6 +1,8 @@
import { darker, lighter } from "app/client/ui2018/ColorPalette";
import { colors, testId, vars } from 'app/client/ui2018/cssVars';
import { textInput } from "app/client/ui2018/editableLabel";
import { icon } from "app/client/ui2018/icons";
import { isValidHex } from "app/common/gutil";
import { Computed, Disposable, dom, DomArg, Observable, onKeyDown, styled } from "grainjs";
import { defaultMenuOptions, IOpenController, setPopupToCreateDom } from "popweasel";
@ -84,7 +86,11 @@ function buildColorPicker(ctl: IOpenController, textColor: Observable<string>, f
onKeyDown({
Escape: () => { revert(); },
Enter: () => { ctl.close(); },
})
}),
// Set focus when `focusout` is bubbling from a children element. This is to allow to receive
// keyboard event again after user interacted with the hex box text input.
dom.on('focusout', (ev, elem) => (ev.target !== elem) && elem.focus()),
);
}
@ -150,12 +156,13 @@ class PickerComponent extends Disposable {
testId(`${title}-input`),
),
),
// TODO: make it possible to type in hex value.
cssHexBox(
dom.attr('value', this._color),
{readonly: true},
dom.on('click', (ev, e) => e.select()),
this._color,
async (val) => { if (isValidHex(val)) {this._model.setValue(val); }},
testId(`${title}-hex`),
// select the hex value on click. Doing it using settimeout allows to avoid some
// sporadically losing the selection just after the click.
dom.on('click', (ev, elem) => setTimeout(() => elem.select(), 0)),
)
),
title,
@ -275,7 +282,7 @@ const cssContent = styled('div', `
align-items: center;
`);
const cssHexBox = styled('input', `
const cssHexBox = styled(textInput, `
border: 1px solid #D9D9D9;
border-left: none;
font-size: ${vars.smallFontSize};
@ -285,6 +292,8 @@ const cssHexBox = styled('input', `
width: 56px;
outline: none;
padding: 0 3px;
height: unset;
border-radius: unset;
`);
const cssLightBorder = styled('div', `

View File

@ -10,7 +10,7 @@
* TODO: Consider merging this into grainjs's input widget.
*/
import { colors } from 'app/client/ui2018/cssVars';
import { dom, DomElementArg, styled } from 'grainjs';
import { dom, DomArg, styled } from 'grainjs';
import { Observable } from 'grainjs';
import noop = require('lodash/noop');
@ -69,7 +69,7 @@ type SaveFunc = (value: string) => Promise<void>;
* cancels editing. Label grows in size with typed input. Validation logic (if any) should happen in
* the save function, to reject a value simply throw an error, this will revert to the saved one .
*/
export function editableLabel(label: Observable<string>, save: SaveFunc, ...args: DomElementArg[]) {
export function editableLabel(label: Observable<string>, save: SaveFunc, ...args: Array<DomArg<HTMLInputElement>>) {
let input: HTMLInputElement;
let sizer: HTMLSpanElement;
@ -92,7 +92,7 @@ export function editableLabel(label: Observable<string>, save: SaveFunc, ...args
* of focus. Escape cancels editing. Validation logic (if any) should happen in the save function,
* to reject a value simply throw an error, this will revert to the the saved one.
*/
export function textInput(label: Observable<string>, save: SaveFunc, ...args: DomElementArg[]) {
export function textInput(label: Observable<string>, save: SaveFunc, ...args: Array<DomArg<HTMLInputElement>>) {
return rawTextInput(label, save, noop, dom.cls(cssTextInput.className), ...args);
}
@ -100,7 +100,7 @@ export function textInput(label: Observable<string>, save: SaveFunc, ...args: Do
* A helper that implements all the saving logic for both editableLabel and textInput.
*/
export function rawTextInput(value: Observable<string>, save: SaveFunc, onChange: () => void,
...args: DomElementArg[]) {
...args: Array<DomArg<HTMLInputElement>>) {
let status: Status = Status.NORMAL;
let inputEl: HTMLInputElement;

View File

@ -777,6 +777,15 @@ export function isColorDark(hexColor: string, isDarkBelow: number = 220): boolea
return luma < isDarkBelow;
}
/**
* Returns true if val is a valid hex color value. For instance: #aabbaa is valid, #aabba is not. Do
* not accept neither short notation nor hex with transparency, ie: #aab, #aabb and #aabbaabb are
* invalid.
*/
export function isValidHex(val: string): boolean {
return /^#([0-9A-F]{6})$/i.test(val);
}
/**
* Returns a promise that resolves to true if promise takes longer than timeoutMsec to resolve. If not