mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +00:00
(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:
parent
92ef1f400c
commit
5e5bf3af9d
@ -1,6 +1,8 @@
|
|||||||
import { darker, lighter } from "app/client/ui2018/ColorPalette";
|
import { darker, lighter } from "app/client/ui2018/ColorPalette";
|
||||||
import { colors, testId, vars } from 'app/client/ui2018/cssVars';
|
import { colors, testId, vars } from 'app/client/ui2018/cssVars';
|
||||||
|
import { textInput } from "app/client/ui2018/editableLabel";
|
||||||
import { icon } from "app/client/ui2018/icons";
|
import { icon } from "app/client/ui2018/icons";
|
||||||
|
import { isValidHex } from "app/common/gutil";
|
||||||
import { Computed, Disposable, dom, DomArg, Observable, onKeyDown, styled } from "grainjs";
|
import { Computed, Disposable, dom, DomArg, Observable, onKeyDown, styled } from "grainjs";
|
||||||
import { defaultMenuOptions, IOpenController, setPopupToCreateDom } from "popweasel";
|
import { defaultMenuOptions, IOpenController, setPopupToCreateDom } from "popweasel";
|
||||||
|
|
||||||
@ -84,7 +86,11 @@ function buildColorPicker(ctl: IOpenController, textColor: Observable<string>, f
|
|||||||
onKeyDown({
|
onKeyDown({
|
||||||
Escape: () => { revert(); },
|
Escape: () => { revert(); },
|
||||||
Enter: () => { ctl.close(); },
|
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`),
|
testId(`${title}-input`),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
// TODO: make it possible to type in hex value.
|
|
||||||
cssHexBox(
|
cssHexBox(
|
||||||
dom.attr('value', this._color),
|
this._color,
|
||||||
{readonly: true},
|
async (val) => { if (isValidHex(val)) {this._model.setValue(val); }},
|
||||||
dom.on('click', (ev, e) => e.select()),
|
|
||||||
testId(`${title}-hex`),
|
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,
|
title,
|
||||||
@ -275,7 +282,7 @@ const cssContent = styled('div', `
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
`);
|
`);
|
||||||
|
|
||||||
const cssHexBox = styled('input', `
|
const cssHexBox = styled(textInput, `
|
||||||
border: 1px solid #D9D9D9;
|
border: 1px solid #D9D9D9;
|
||||||
border-left: none;
|
border-left: none;
|
||||||
font-size: ${vars.smallFontSize};
|
font-size: ${vars.smallFontSize};
|
||||||
@ -285,6 +292,8 @@ const cssHexBox = styled('input', `
|
|||||||
width: 56px;
|
width: 56px;
|
||||||
outline: none;
|
outline: none;
|
||||||
padding: 0 3px;
|
padding: 0 3px;
|
||||||
|
height: unset;
|
||||||
|
border-radius: unset;
|
||||||
`);
|
`);
|
||||||
|
|
||||||
const cssLightBorder = styled('div', `
|
const cssLightBorder = styled('div', `
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
* TODO: Consider merging this into grainjs's input widget.
|
* TODO: Consider merging this into grainjs's input widget.
|
||||||
*/
|
*/
|
||||||
import { colors } from 'app/client/ui2018/cssVars';
|
import { colors } from 'app/client/ui2018/cssVars';
|
||||||
import { dom, DomElementArg, styled } from 'grainjs';
|
import { dom, DomArg, styled } from 'grainjs';
|
||||||
import { Observable } from 'grainjs';
|
import { Observable } from 'grainjs';
|
||||||
import noop = require('lodash/noop');
|
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
|
* 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 .
|
* 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 input: HTMLInputElement;
|
||||||
let sizer: HTMLSpanElement;
|
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,
|
* 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.
|
* 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);
|
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.
|
* A helper that implements all the saving logic for both editableLabel and textInput.
|
||||||
*/
|
*/
|
||||||
export function rawTextInput(value: Observable<string>, save: SaveFunc, onChange: () => void,
|
export function rawTextInput(value: Observable<string>, save: SaveFunc, onChange: () => void,
|
||||||
...args: DomElementArg[]) {
|
...args: Array<DomArg<HTMLInputElement>>) {
|
||||||
let status: Status = Status.NORMAL;
|
let status: Status = Status.NORMAL;
|
||||||
let inputEl: HTMLInputElement;
|
let inputEl: HTMLInputElement;
|
||||||
|
|
||||||
|
@ -777,6 +777,15 @@ export function isColorDark(hexColor: string, isDarkBelow: number = 220): boolea
|
|||||||
return luma < isDarkBelow;
|
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
|
* Returns a promise that resolves to true if promise takes longer than timeoutMsec to resolve. If not
|
||||||
|
Loading…
Reference in New Issue
Block a user