js-draw
Version:
Draw pictures using a pen, touchscreen, or mouse! JS-draw is a drawing library for JavaScript and TypeScript.
152 lines (151 loc) • 6.17 kB
JavaScript
import { Color4 } from '@js-draw/math';
import PipetteTool from '../../../tools/PipetteTool.mjs';
import { EditorEventType } from '../../../types.mjs';
// Returns [ color input, input container, callback to change the color value ].
export const makeColorInput = (editor, onColorChange) => {
const container = document.createElement('span');
const inputWrapper = document.createElement('span');
const colorInput = document.createElement('input');
colorInput.type = 'button';
colorInput.classList.add('coloris_input');
container.classList.add('color-input-container');
inputWrapper.classList.add('color-input-wrapper');
inputWrapper.appendChild(colorInput);
container.appendChild(inputWrapper);
const pipetteController = addPipetteTool(editor, container, (color) => {
colorInput.value = color.toHexString();
onInputEnd();
// Update the color preview, if it exists (may be managed by Coloris).
const parentElem = colorInput.parentElement;
if (parentElem && parentElem.classList.contains('clr-field')) {
parentElem.style.color = colorInput.value;
}
});
let currentColor;
const handleColorInput = () => {
currentColor = Color4.fromHex(colorInput.value);
};
// Only change the pen color when we finish sending input (this limits the number of
// editor events triggered and accessibility announcements).
const onInputEnd = () => {
handleColorInput();
if (currentColor) {
editor.announceForAccessibility(editor.localization.colorChangedAnnouncement(currentColor.toHexString()));
onColorChange(currentColor);
editor.notifier.dispatch(EditorEventType.ColorPickerColorSelected, {
kind: EditorEventType.ColorPickerColorSelected,
color: currentColor,
});
}
};
colorInput.oninput = handleColorInput;
let isOpen = false;
colorInput.addEventListener('open', () => {
isOpen = true;
editor.notifier.dispatch(EditorEventType.ColorPickerToggled, {
kind: EditorEventType.ColorPickerToggled,
open: true,
});
pipetteController.cancel();
container.classList.add('picker-open');
// Focus the Coloris color picker, if it exists.
// Don't focus the text input within the color picker, however,
// as this displays a keyboard on mobile devices.
const colorPickerElem = document.querySelector('#clr-picker #clr-hue-slider');
colorPickerElem?.focus();
});
const onClose = () => {
isOpen = false;
editor.notifier.dispatch(EditorEventType.ColorPickerToggled, {
kind: EditorEventType.ColorPickerToggled,
open: false,
});
onInputEnd();
// Restore focus to the input that opened the color picker
colorInput.focus();
container.classList.remove('picker-open');
};
colorInput.addEventListener('close', () => {
onClose();
});
const setColorInputValue = (color) => {
if (typeof color === 'object') {
color = color.toHexString();
}
colorInput.value = color;
// Fire all color event listeners. See
// https://github.com/mdbassit/Coloris#manually-updating-the-thumbnail
colorInput.dispatchEvent(new Event('input', { bubbles: true }));
};
return {
input: colorInput,
container,
setValue: setColorInputValue,
closePicker: () => {
if (isOpen) {
onInputEnd();
}
},
registerWithHelpTextDisplay: (helpDisplay) => {
helpDisplay.registerTextHelpForElement(inputWrapper, editor.localization.colorPickerToggleHelpText);
pipetteController.registerWithHelpTextDisplay(helpDisplay);
},
};
};
const addPipetteTool = (editor, container, onColorChange) => {
const pipetteButton = document.createElement('button');
pipetteButton.classList.add('pipetteButton');
pipetteButton.title = editor.localization.pickColorFromScreen;
pipetteButton.setAttribute('alt', pipetteButton.title);
const pickColorLabel = document.createElement('span');
pickColorLabel.classList.add('pickColorInstructions');
pickColorLabel.innerText = editor.localization.clickToPickColorAnnouncement;
const updatePipetteButtonContent = (color) => {
pipetteButton.replaceChildren(editor.icons.makePipetteIcon(color), pickColorLabel);
};
updatePipetteButtonContent();
const pipetteTool = editor.toolController.getMatchingTools(PipetteTool)[0];
const endColorSelectMode = () => {
pipetteTool?.clearColorListener();
updatePipetteButtonContent();
pipetteButton.classList.remove('active');
};
const pipetteColorSelect = (color) => {
endColorSelectMode();
if (color) {
onColorChange(color);
}
};
const pipetteColorPreview = (color) => {
if (color) {
updatePipetteButtonContent(color);
}
else {
updatePipetteButtonContent();
}
};
pipetteButton.onclick = () => {
// If already picking, cancel it.
if (pipetteButton.classList.contains('active')) {
endColorSelectMode();
editor.announceForAccessibility(editor.localization.colorSelectionCanceledAnnouncement);
return;
}
pipetteTool?.setColorListener(pipetteColorPreview, pipetteColorSelect);
if (pipetteTool) {
pipetteButton.classList.add('active');
editor.announceForAccessibility(editor.localization.clickToPickColorAnnouncement);
}
};
container.appendChild(pipetteButton);
return {
// Cancel a pipette color selection if one is in progress.
cancel: () => {
endColorSelectMode();
},
registerWithHelpTextDisplay: (helpDisplay) => {
helpDisplay.registerTextHelpForElement(pipetteButton, editor.localization.colorPickerPipetteHelpText);
},
};
};
export default makeColorInput;