add colorpicker hotkey config icon [default: none]

* uses the last submitted color by default
* doesn't insert the default color until a change or the Enter key
* fix a few bugs in colorpicker introduced in the rewrite
This commit is contained in:
tophf 2017-11-21 18:39:13 +03:00
parent 352846c8b4
commit b99391887d
5 changed files with 102 additions and 27 deletions

View File

@ -193,6 +193,11 @@
<div class="option">
<input id="editor.colorpicker" type="checkbox">
<label for="editor.colorpicker" i18n-text="cm_colorpicker"></label>
<span class="svg-inline-wrapper" i18n-title="shortcutsNote">
<svg id="colorpicker-settings" class="svg-icon settings">
<use xlink:href="#svg-icon-settings"/>
</svg>
</span>
</div>
<div class="option aligned">
<label id="tabSize-label" for="editor.tabSize" i18n-text="cm_tabSize"></label>

View File

@ -1457,6 +1457,7 @@ function initHooks() {
$('#sections-help').addEventListener('click', showSectionHelp, false);
$('#keyMap-help').addEventListener('click', showKeyMapHelp, false);
$('#cancel-button').addEventListener('click', goBackToManage);
$('#colorpicker-settings').addEventListener('click', configureColorpicker);
setupOptionsExpand();
initLint();
@ -1872,8 +1873,8 @@ function showHelp(title, body) {
// avoid chaining on multiple showHelp() calls
$('.dismiss', div).onclick = closeHelp;
}
div.style.display = 'block';
// reset any inline styles
div.style = 'display: block';
return div;
function closeHelp(e) {
@ -2078,6 +2079,7 @@ function onColorpickerReady() {
'/vendor-overwrites/colorpicker/colorpicker.js',
'/vendor-overwrites/colorpicker/colorview.js',
];
prefs.subscribe(['editor.colorpicker.hotkey'], registerHotkey);
prefs.subscribe(['editor.colorpicker'], colorpickerOnDemand);
return prefs.get('editor.colorpicker') && colorpickerOnDemand(null, true);
@ -2087,21 +2089,83 @@ function onColorpickerReady() {
}
function setColorpickerOption(id, enabled) {
CodeMirror.defaults.colorpicker = enabled && {
forceUpdate: editors.length > 0,
tooltip: t('colorpickerTooltip'),
popupOptions: {
tooltipForSwitcher: t('colorpickerSwitchFormatTooltip'),
hexUppercase: prefs.get('editor.colorpicker.hexUppercase'),
hideDelay: 5000,
embedderCallback: state => {
if (state && state.hexUppercase !== prefs.get('editor.colorpicker.hexUppercase')) {
prefs.set('editor.colorpicker.hexUppercase', state.hexUppercase);
}
const defaults = CodeMirror.defaults;
const keyName = prefs.get('editor.colorpicker.hotkey');
delete defaults.extraKeys[keyName];
defaults.colorpicker = enabled;
if (enabled) {
if (keyName) {
CodeMirror.commands.colorpicker = invokeColorpicker;
defaults.extraKeys[keyName] = 'colorpicker';
}
defaults.colorpicker = {
forceUpdate: editors.length > 0,
tooltip: t('colorpickerTooltip'),
popupOptions: {
tooltipForSwitcher: t('colorpickerSwitchFormatTooltip'),
hexUppercase: prefs.get('editor.colorpicker.hexUppercase'),
hideDelay: 5000,
embedderCallback: state => {
['hexUppercase', 'color']
.filter(name => state[name] !== prefs.get('editor.colorpicker.' + name))
.forEach(name => prefs.set('editor.colorpicker.' + name, state[name]));
},
},
},
};
};
}
// on page load runs before CodeMirror.setOption is defined
editors.forEach(cm => cm.setOption('colorpicker', CodeMirror.defaults.colorpicker));
editors.forEach(cm => cm.setOption('colorpicker', defaults.colorpicker));
}
function registerHotkey(id, hotkey) {
const extraKeys = CodeMirror.defaults.extraKeys;
for (const key in extraKeys) {
if (extraKeys[key] === 'colorpicker') {
delete extraKeys[key];
break;
}
}
if (hotkey) {
extraKeys[hotkey] = 'colorpicker';
}
}
function invokeColorpicker(cm) {
cm.state.colorpicker.openPopup(prefs.get('editor.colorpicker.color'));
}
}
function configureColorpicker() {
const input = $element({
tag: 'input',
type: 'search',
spellcheck: false,
value: prefs.get('editor.colorpicker.hotkey'),
onkeydown(event) {
const key = CodeMirror.keyName(event);
// ignore: [Shift?] characters, modifiers-only, [Shift?] Esc, Enter, [Shift?] Tab
if (/^(Enter|(Shift-)?(Esc|Tab|[!-~])|(Shift-?|Ctrl-?|Alt-?|Cmd-?)*)$/.test(key)) {
return;
}
event.preventDefault();
event.stopPropagation();
prefs.set('editor.colorpicker.hotkey', key);
this.value = key;
},
oninput() {
// fired on pressing "x" to clear the field
prefs.set('editor.colorpicker.hotkey', '');
},
onpaste(event) {
event.preventDefault();
}
});
const popup = showHelp(t('helpKeyMapHotkey'), input);
if (this instanceof Element) {
const bounds = this.getBoundingClientRect();
popup.style.left = bounds.right + 10 + 'px';
popup.style.top = bounds.top - popup.clientHeight / 2 + 'px';
popup.style.right = 'auto';
}
input.focus();
}

View File

@ -60,6 +60,10 @@ var prefs = new function Prefs() {
'editor.colorpicker': true,
// #DEAD or #beef
'editor.colorpicker.hexUppercase': false,
// default hotkey
'editor.colorpicker.hotkey': '',
// last color
'editor.colorpicker.color': '',
'iconset': 0, // 0 = dark-themed icon
// 1 = light-themed icon

View File

@ -203,7 +203,7 @@ CodeMirror.defineExtension('colorpicker', function () {
options = PUBLIC_API.options = opt;
prevFocusedElement = document.activeElement;
userActivity = 0;
lastOutputColor = opt.color;
lastOutputColor = opt.color || '';
$formatChangeButton.title = opt.tooltipForSwitcher || '';
opt.hideDelay = Math.max(0, opt.hideDelay) || 2000;
@ -337,7 +337,7 @@ CodeMirror.defineExtension('colorpicker', function () {
}
function validateInput(el) {
const isAlpha = el.type === 'text';
const isAlpha = el === $inputs[currentFormat][3];
let isValid = (isAlpha || el.value.trim()) && el.checkValidity();
if (!isAlpha && !isValid && currentFormat === 'rgb') {
isValid = parseAs(el, parseInt);
@ -352,8 +352,9 @@ CodeMirror.defineExtension('colorpicker', function () {
//endregion
//region State-to-DOM
function setFromColor(color = '#FF0000') {
function setFromColor(color) {
color = typeof color === 'string' ? stringToColor(color) : color;
color = color || stringToColor('#f00');
const newHSV = color.type === 'hsl' ? HSLtoHSV(color) : RGBtoHSV(color);
if (Object.keys(newHSV).every(k => Math.abs(newHSV[k] - HSV[k]) < 1e-3)) {
return;
@ -440,7 +441,7 @@ CodeMirror.defineExtension('colorpicker', function () {
}
}
function onSaturationMouseUp() {
function onSaturationMouseUp(event) {
if (event.button === 0) {
dragging.saturation = false;
releaseMouse();
@ -454,7 +455,7 @@ CodeMirror.defineExtension('colorpicker', function () {
}
}
function onOpacityKnobMouseDown() {
function onOpacityKnobMouseDown(event) {
if (event.button === 0) {
dragging.opacity = true;
captureMouse();
@ -517,6 +518,7 @@ CodeMirror.defineExtension('colorpicker', function () {
if (!e.shiftKey && !e.ctrlKey && !e.altKey && !e.metaKey) {
switch (e.which) {
case 13:
setFromInputs();
colorpickerCallback();
// fallthrough to 27
case 27:
@ -647,7 +649,6 @@ CodeMirror.defineExtension('colorpicker', function () {
str.match(/(..)/g).map(c => parseInt(c, 16));
return {type: 'hex', r, g, b, a: a === 255 ? undefined : a / 255};
}
return;
}
function RGBtoHSV({r, g, b, a}) {

View File

@ -400,10 +400,11 @@
openPopup(color) {
let {line, ch} = this.cm.getCursor();
const lineText = this.cm.getLine(line);
ch -= (lineText.lastIndexOf('!important', ch) >= ch - '!important'.length) ? '!important'.length : 0;
const atImportant = lineText.lastIndexOf('!important', ch);
ch -= (atImportant >= Math.max(0, ch - '!important'.length)) ? '!important'.length : 0;
const lineCache = this.cm.state.colorpicker.cache.get(lineText);
const data = {line, ch, color, isShortCut: true};
for (const [start, {color, colorValue}] of lineCache && lineCache.entries() || []) {
const data = {line, ch, colorValue: color, isShortCut: true};
for (const [start, {color, colorValue = color}] of lineCache && lineCache.entries() || []) {
if (start <= ch && ch <= start + color.length) {
Object.assign(data, {ch: start, color, colorValue});
break;
@ -419,7 +420,7 @@
top,
left,
cm: this.cm,
color: data.colorValue || data.color || '#fff',
color: data.colorValue || data.color,
prevColor: data.color || '',
isShortCut: false,
callback: ColorMarker.popupOnChange,
@ -437,8 +438,8 @@
const {cm, line, ch, embedderCallback} = this;
const to = {line, ch: ch + this.prevColor.length};
if (cm.getRange(this, to) !== newColor) {
this.prevColor = newColor;
cm.replaceRange(newColor, this, to, '*colorpicker');
this.prevColor = newColor;
}
if (typeof embedderCallback === 'function') {
embedderCallback(this);