@amcharts/amcharts5
Version:
amCharts 5
526 lines • 20.9 kB
JavaScript
import { ColorPickerDefaultTheme } from "./ColorPickerDefaultTheme";
import { color, Color } from "../../core/util/Color";
import { ListTemplate } from "../../core/util/List";
import { p100 } from "../../core/util/Percent";
import { Template } from "../../core/util/Template";
import { Button } from "../../core/render/Button";
import { Circle } from "../../core/render/Circle";
import { Container } from "../../core/render/Container";
import { EditableLabel } from "../../core/render/EditableLabel";
import { Graphics } from "../../core/render/Graphics";
import { Label } from "../../core/render/Label";
import { Rectangle } from "../../core/render/Rectangle";
import { RoundedRectangle } from "../../core/render/RoundedRectangle";
import { Slider } from "../../core/render/Slider";
import { LinearGradient } from "../../core/render/gradients/LinearGradient";
import * as $utils from "../../core/util/Utils";
import * as $type from "../../core/util/Type";
export class ColorPicker extends Container {
constructor() {
super(...arguments);
this._clickOverlay = this.children.push(Rectangle.new(this._root, {
position: "absolute",
fillOpacity: 0,
interactive: true,
isMeasured: false,
fill: color(0x000000),
forceHidden: true
}));
this.rectangles = new ListTemplate(Template.new({}), () => RoundedRectangle._new(this._root, {}, [this.rectangles.template]));
/**
* Container for color gradients.
*/
this.gradientsContainer = this.children.push(Container.new(this._root, {
themeTagsSelf: ["gradientscontainer"],
width: p100,
layout: this._root.horizontalLayout
}));
/**
* Container for color gradients.
* @ignore
*/
this.colorGradientsContainer = this.gradientsContainer.children.push(Container.new(this._root, {
width: p100,
layer: 0
}));
/**
* Rectangle that displays the color gradient.
* @ignore
*/
this.colorRectangle = this.colorGradientsContainer.children.push(Rectangle.new(this._root, {
crisp: true,
themeTags: ["colorrectangle"],
width: p100,
height: p100,
strokeWidth: 1
}));
/**
* Rectangle that displays the white overlay.
* @ignore*
*/
this.colorRectangleWhiteOverlay = this.colorGradientsContainer.children.push(Rectangle.new(this._root, {
crisp: true,
forceInactive: true,
width: p100,
height: p100
}));
/**
* Rectangle that displays the black overlay.
* @ignore
*/
this.colorRectangleBlackOverlay = this.colorGradientsContainer.children.push(Rectangle.new(this._root, {
crisp: true,
forceInactive: true,
width: p100,
height: p100
}));
/**
* Slider that displays the hue of the color.
*/
this.slider = this.gradientsContainer.children.push(Slider.new(this._root, {
orientation: "vertical",
themeTags: ["gradient"]
}));
/**
* @ignore
*/
this.inputContainer = this.children.push(Container.new(this._root, {
marginTop: 10,
layout: this._root.horizontalLayout,
width: p100
}));
/**
* Button that shows color picker button.
*/
this.pickerButton = this.inputContainer.children.push(Button.new(this._root, {
themeTags: ["picker", "pickertool"],
toggleKey: "active",
icon: Graphics.new(this._root, {
themeTags: ["icon"]
})
}));
/**
* Button that allows to remove color.
*/
this.noColorButton = this.inputContainer.children.push(Button.new(this._root, {
themeTags: ["nocolor", "pickertool"],
icon: Graphics.new(this._root, {
themeTags: ["icon"]
})
}));
/**
* Editable label that allows to input color in hex format.
*/
this.colorInput = this.inputContainer.children.push(EditableLabel.new(this._root, {
themeTags: ["input", "color"]
}));
/**
* @ignore
*/
this.opacityContainer = this.children.push(Container.new(this._root, {
width: p100,
marginTop: 10,
layout: this._root.horizontalLayout
}));
/**
* Slider that allows to change opacity of the color.
*/
this.opacitySlider = this.opacityContainer.children.push(Slider.new(this._root, {
themeTags: ["opacity"],
orientation: "horizontal"
}));
/**
* Circle that indicates the target color.
*/
this.targetCircle = this.colorGradientsContainer.children.push(Circle.new(this._root, {
themeTags: ["circle", "target"],
isMeasured: false,
forceInactive: true,
position: "absolute",
layer: 30
}));
/**
* @ignore
*/
this.buttonsContainer = this.children.push(Container.new(this._root, {
marginTop: 10,
layout: this._root.horizontalLayout,
width: p100
}));
/**
* Button that allows to cancel color selection.
*/
this.cancelButton = this.buttonsContainer.children.push(Button.new(this._root, {
themeTags: ["cancel"],
label: Label.new(this._root, {})
}));
/**
* Button that allows to confirm color selection.
*/
this.okButton = this.buttonsContainer.children.push(Button.new(this._root, {
themeTags: ["ok"],
label: Label.new(this._root, {})
}));
this._isColorDown = false;
this._isPicking = false;
this._isEditing = false;
}
/**
* @ignore
*/
_afterNew() {
this._defaultThemes.push(ColorPickerDefaultTheme.new(this._root));
this.addTag("colorpicker");
super._afterNew();
this.set("tooltip", this._root.systemTooltip);
this.cancelButton.events.on("click", () => {
this.cancel();
});
this.okButton.events.on("click", () => {
this.set("colorButton", undefined);
// dispatch event
this.events.dispatch("colorchanged", {
type: "colorchanged",
target: this,
color: this.get("color"),
colorOpacity: this.get("colorOpacity")
});
});
const container = this.root.container;
container.events.on("boundschanged", () => {
const w = container.width();
const h = container.height();
this._clickOverlay.setAll({
width: w,
height: h,
x: 0,
y: 0
});
});
this._clickOverlay.events.on("click", () => {
if (!this._isPicking && !this._isEditing) {
this.cancel();
}
else {
this.pickerButton.set("active", false);
}
});
this.set("layout", this._root.verticalLayout);
// remove text color input when nocolor button is pressed
this.noColorButton.events.on("click", () => {
this.set("color", undefined);
const colorButton = this.get("colorButton");
if (colorButton) {
colorButton.setAll({
color: undefined,
colorOpacity: 1
});
}
// dispatch event
this.events.dispatch("colorchanged", {
type: "colorchanged",
target: this,
color: undefined,
colorOpacity: 1
});
this.set("colorButton", undefined);
});
this.colorInput.on("text", (text) => {
if (text) {
text = text.replace(/[^abcdef0-9#]/ig, "");
if (text.substring(0, 1) !== "#") {
text = "#" + text;
}
try {
this.set("color", color(text));
}
catch (e) {
return;
}
this.colorInput.set("text", text);
}
});
this.pickerButton.on("active", (active) => {
this._isPicking = Boolean(active);
this.set("forceInactive", active);
});
if ($utils.supports("keyboardevents")) {
this._disposers.push($utils.addEventListener(document, "keyup", (e) => {
if (e.key === "Escape") {
this.set("colorButton", undefined);
}
}));
}
this.gradientsContainer.events.on("boundschanged", () => {
const w = Math.floor(this.colorGradientsContainer.width());
this.colorGradientsContainer.setAll({ width: w, height: w });
this.slider.set("height", w);
});
this.targetCircle.setAll({
x: this.colorRectangle.width(),
y: 0
});
// limit target circle to the bounds of the color gradient
this.targetCircle.adapters.add("x", (x) => {
if ($type.isNumber(x)) {
const w = this.colorRectangle.width();
return Math.min(w, Math.max(0, x));
}
});
this.targetCircle.adapters.add("y", (y) => {
if ($type.isNumber(y)) {
const h = this.colorRectangle.height();
return Math.min(h, Math.max(0, y));
}
});
this.slider.on("start", () => {
this.set("hue", this.slider.get("start", 0));
});
const sliderBg = this.slider.get("background");
if (sliderBg) {
sliderBg.set("layer", 0);
}
// handle opacity slider changes
this.opacitySlider.on("start", () => {
this.set("colorOpacity", this.opacitySlider.get("start", 1));
});
const opacitySliderBg = this.opacitySlider.get("background");
if (opacitySliderBg) {
opacitySliderBg.set("layer", 0);
}
// big rectangle events
this.colorRectangle.events.on("pointerdown", (e) => {
this._isColorDown = true;
this._handleTargetMove(e);
this.set("draggable", false);
this.dragStop(e);
});
this.colorRectangle.events.on("globalpointermove", (e) => {
if (this._isColorDown) {
this._handleTargetMove(e);
}
if (this._isPicking) {
const display = this._root.container._display;
const context = display.getLayer().context;
const resolution = display._renderer.resolution;
const imageData = context.getImageData(Math.floor(e.point.x * resolution), Math.floor(e.point.y * resolution), 1, 1).data;
const fill = Color.fromRGB(imageData[0], imageData[1], imageData[2]);
this.set("color", fill);
}
});
this.colorRectangle.events.on("globalpointerup", (e) => {
this.set("draggable", true);
this.dragStop(e);
this._isColorDown = false;
});
this.colorInput.on("active", (active) => {
this._isEditing = Boolean(active);
});
}
/**
* @ignore
*/
_updateChildren() {
var _a, _b, _c, _d;
super._updateChildren();
if (this.isDirty("colorButton")) {
this.pickerButton.set("active", false);
const colorButton = this.get("colorButton");
let hidden = true;
if (colorButton) {
this.opacityContainer.set("forceHidden", colorButton.get("disableOpacity"));
this._previousColor = colorButton.get("color", colorButton.getPrivate("color"));
this._previousOpacity = colorButton.get("colorOpacity");
const parent = this.parent;
if (parent) {
parent.children.moveValue(this._clickOverlay);
parent.children.moveValue(this);
}
hidden = false;
this.show();
this.set("color", colorButton.get("color", colorButton.getPrivate("color")));
this.set("colorOpacity", colorButton.get("colorOpacity") || 1);
}
else {
this.hide();
}
this._clickOverlay.set("forceHidden", hidden);
}
if (this.isDirty("hue")) {
this._handleDirtyHue();
this._updateColor();
}
if (this.isDirty("colorOpacity")) {
const opacitySlider = this.opacitySlider;
const opacity = this.get("colorOpacity", 1);
opacitySlider.startGrip.set("tooltipText", "opacity:" + Math.round(opacity * 100) + "%");
if (!opacitySlider.startGrip.isDragging()) {
opacitySlider.set("start", opacity);
opacitySlider.markDirtyKey("start");
}
else {
opacitySlider.startGrip.hover();
}
const colorButton = this.get("colorButton");
if (colorButton) {
colorButton.set("colorOpacity", opacity);
}
this._updateOpacitySliderGrip();
}
if (this.isDirty("color")) {
const fill = this.get("color");
const prevColor = this._prevColor;
let same = false;
if (fill && prevColor && fill.toCSSHex() === prevColor.toCSSHex()) {
same = true;
// void
}
if (!same) {
if (fill) {
this.targetCircle.show();
if (!this._isColorDown) {
const hsv = $utils.hslToHsv(fill.toHSL());
this.setRaw("hue", hsv.h);
if (!this.slider.startGrip.isDragging()) {
this.slider.set("start", hsv.h);
}
this.targetCircle.setAll({
x: this.colorRectangle.width() * hsv.s,
y: this.colorRectangle.height() * (1 - hsv.v)
});
}
this.targetCircle.set("fill", fill);
this._handleDirtyHue();
this.colorInput.set("text", fill.toCSSHex().substring(1, 7));
}
else {
this.targetCircle.hide();
this.colorInput.set("text", "");
}
this._prevColor = fill;
const opacitySliderBg = this.opacitySlider.get("background");
if (opacitySliderBg) {
opacitySliderBg.set("fillGradient", LinearGradient.new(this._root, {
rotation: 0,
stops: [
{ color: fill, opacity: 0 },
{ color: fill, opacity: 1 }
]
}));
}
const colorButton = this.get("colorButton");
if (colorButton) {
colorButton.set("color", fill);
}
this._updateOpacitySliderGrip();
}
}
if (this.isDirty("backgroundColor")) {
const bgColor = this.get("backgroundColor", color(0xffffff));
this.colorRectangle.setAll({
stroke: bgColor
});
this.colorRectangleBlackOverlay.setAll({
stroke: bgColor
});
this.colorRectangleWhiteOverlay.setAll({
stroke: bgColor
});
(_a = this.pickerButton.get("background")) === null || _a === void 0 ? void 0 : _a.states.applyAnimate("default", 0);
(_b = this.noColorButton.get("background")) === null || _b === void 0 ? void 0 : _b.states.applyAnimate("default", 0);
(_c = this.cancelButton.get("background")) === null || _c === void 0 ? void 0 : _c.states.applyAnimate("default", 0);
(_d = this.okButton.get("background")) === null || _d === void 0 ? void 0 : _d.states.applyAnimate("default", 0);
}
}
/**
* @ignore
*/
_handleDirtyHue() {
var _a, _b, _c;
const start = this.slider.get("start", 0);
const fill = Color.fromHSL(start, 1, .5);
const grip = this.slider.startGrip;
this.colorRectangle.setAll({
fill: fill,
stroke: this.get("backgroundColor", color(0xffffff))
});
const gripBg = grip.get("background");
if (gripBg) {
gripBg.set("fill", fill);
(_a = gripBg.states.lookup("default")) === null || _a === void 0 ? void 0 : _a.set("fill", fill);
(_b = gripBg.states.lookup("hover")) === null || _b === void 0 ? void 0 : _b.set("fill", fill);
(_c = gripBg.states.lookup("down")) === null || _c === void 0 ? void 0 : _c.set("fill", fill);
}
this.colorRectangleWhiteOverlay.setAll({
fillGradient: LinearGradient.new(this._root, {
rotation: 0,
stops: [
{ color: color(0xffffff), opacity: 1, offset: 0 },
{ color: color(0xffffff), opacity: 0, offset: 1 }
]
})
});
this.colorRectangleBlackOverlay.setAll({
fillGradient: LinearGradient.new(this._root, {
stops: [
{ color: color(0x000000), opacity: 0, offset: 0 },
{ color: color(0x000000), opacity: 1, offset: 1 }
]
})
});
}
/**
* @ignore
*/
_handleTargetMove(e) {
const point = this.colorGradientsContainer.toLocal({ x: e.point.x, y: e.point.y });
this.targetCircle.setAll({
x: point.x,
y: point.y
});
this._updateColor();
}
/**
* @ignore
*/
_updateColor() {
const x = this.targetCircle.x();
const y = this.targetCircle.y();
const h = this.colorRectangle.height();
const w = this.colorRectangle.width();
const start = this.slider.get("start", 0);
let hsl = $utils.hsvToHsl({ h: start, s: x / w, v: 1 - y / h });
this.set("color", Color.fromHSL(hsl.h, hsl.s, hsl.l));
}
/**
* @ignore
*/
_updateOpacitySliderGrip() {
var _a, _b, _c;
const opacityBg = this.opacitySlider.startGrip.get("background");
if (opacityBg) {
const fill = Color.interpolate(this.get("colorOpacity", 1), this.get("backgroundColor", color(0xffffff)), this.get("color", color(0xffffff)));
opacityBg.set("fill", fill);
(_a = opacityBg.states.lookup("default")) === null || _a === void 0 ? void 0 : _a.set("fill", fill);
(_b = opacityBg.states.lookup("hover")) === null || _b === void 0 ? void 0 : _b.set("fill", fill);
(_c = opacityBg.states.lookup("down")) === null || _c === void 0 ? void 0 : _c.set("fill", fill);
}
}
/**
* Cancels color selection and restores previous color.
*/
cancel() {
var _a;
(_a = this.get("colorButton")) === null || _a === void 0 ? void 0 : _a.setAll({
color: this._previousColor,
colorOpacity: this._previousOpacity
});
this._isEditing = false;
this._isPicking = false;
this.set("color", this._previousColor);
this.set("colorButton", undefined);
}
}
ColorPicker.className = "ColorPicker";
ColorPicker.classNames = Container.classNames.concat([ColorPicker.className]);
//# sourceMappingURL=ColorPicker.js.map