@esri/calcite-components
Version:
Web Components for Esri's Calcite Design System.
402 lines (396 loc) • 15.8 kB
JavaScript
/*!
* All material copyright ESRI, All Rights Reserved, unless otherwise specified.
* See https://github.com/Esri/calcite-components/blob/master/LICENSE.md for details.
* v1.5.0-next.4
*/
import { proxyCustomElement, HTMLElement, createEvent, h } from '@stencil/core/internal/client';
import { j as isLonghandHex, i as isValidHex, r as rgbToHex, n as normalizeHex, o as opacityToAlpha, h as hexify, k as hexChar, b as alphaToOpacity } from './utils.js';
import { c as color } from './index2.js';
import { k as focusElement } from './dom.js';
import { a as setUpLoadableComponent, s as setComponentLoaded, c as componentLoaded } from './loadable.js';
import { d as defineCustomElement$4 } from './icon.js';
import { d as defineCustomElement$3 } from './input.js';
import { d as defineCustomElement$2 } from './input-number.js';
import { d as defineCustomElement$1 } from './progress.js';
const CSS$1 = {
channel: "channel",
channels: "channels",
colorField: "color-field",
colorFieldScope: "scope--color-field",
colorMode: "color-mode",
colorModeContainer: "color-mode-container",
container: "container",
control: "control",
controlAndScope: "control-and-scope",
controlSection: "control-section",
deleteColor: "delete-color",
header: "header",
hexAndChannelsGroup: "hex-and-channels-group",
hexOptions: "color-hex-options",
hueScope: "scope--hue",
hueSlider: "hue-slider",
opacityScope: "scope--opacity",
opacitySlider: "opacity-slider",
preview: "preview",
previewAndSliders: "preview-and-sliders",
saveColor: "save-color",
savedColor: "saved-color",
savedColors: "saved-colors",
savedColorsButtons: "saved-colors-buttons",
savedColorsSection: "saved-colors-section",
scope: "scope",
section: "section",
slider: "slider",
sliders: "sliders",
splitSection: "section--split"
};
const DEFAULT_COLOR$1 = color("#007AC2");
const DEFAULT_STORAGE_KEY_PREFIX = "calcite-color-";
const RGB_LIMITS = {
r: 255,
g: 255,
b: 255
};
const HSV_LIMITS = {
h: 360,
s: 100,
v: 100
};
const OPACITY_LIMITS = {
min: 0,
max: 100
};
const DIMENSIONS = {
s: {
slider: {
height: 12,
width: 104
},
colorField: {
height: 80,
width: 160
},
thumb: {
radius: 10
}
},
m: {
slider: {
height: 12,
width: 204
},
colorField: {
height: 150,
width: 272
},
thumb: {
radius: 10
}
},
l: {
slider: {
height: 12,
width: 384
},
colorField: {
height: 200,
width: 464
},
thumb: {
radius: 10
}
}
};
const CSS = {
container: "container",
hexInput: "hex-input",
opacityInput: "opacity-input"
};
const colorPickerHexInputCss = "@keyframes in{0%{opacity:0}100%{opacity:1}}@keyframes in-down{0%{opacity:0;transform:translate3D(0, -5px, 0)}100%{opacity:1;transform:translate3D(0, 0, 0)}}@keyframes in-up{0%{opacity:0;transform:translate3D(0, 5px, 0)}100%{opacity:1;transform:translate3D(0, 0, 0)}}@keyframes in-right{0%{opacity:0;transform:translate3D(-5px, 0, 0)}100%{opacity:1;transform:translate3D(0, 0, 0)}}@keyframes in-left{0%{opacity:0;transform:translate3D(5px, 0, 0)}100%{opacity:1;transform:translate3D(0, 0, 0)}}@keyframes in-scale{0%{opacity:0;transform:scale3D(0.95, 0.95, 1)}100%{opacity:1;transform:scale3D(1, 1, 1)}}:root{--calcite-animation-timing:calc(150ms * var(--calcite-internal-duration-factor));--calcite-internal-duration-factor:var(--calcite-duration-factor, 1);--calcite-internal-animation-timing-fast:calc(100ms * var(--calcite-internal-duration-factor));--calcite-internal-animation-timing-medium:calc(200ms * var(--calcite-internal-duration-factor));--calcite-internal-animation-timing-slow:calc(300ms * var(--calcite-internal-duration-factor))}.calcite-animate{opacity:0;animation-fill-mode:both;animation-duration:var(--calcite-animation-timing)}.calcite-animate__in{animation-name:in}.calcite-animate__in-down{animation-name:in-down}.calcite-animate__in-up{animation-name:in-up}.calcite-animate__in-right{animation-name:in-right}.calcite-animate__in-left{animation-name:in-left}.calcite-animate__in-scale{animation-name:in-scale}@media (prefers-reduced-motion: reduce){:root{--calcite-internal-duration-factor:0.01}}:root{--calcite-floating-ui-transition:var(--calcite-animation-timing);--calcite-floating-ui-z-index:var(--calcite-app-z-index-dropdown)}:host([hidden]){display:none}:host{display:block}.container{display:flex;inline-size:100%;flex-wrap:nowrap;align-items:center}.hex-input{flex-grow:1;text-transform:uppercase}.opacity-input{inline-size:68px;margin-inline-start:-1px}:host([scale=s]) .container{flex-wrap:wrap;row-gap:0.125rem}:host([scale=s]) .opacity-input{inline-size:unset;margin-inline-start:unset}:host([scale=l]) .opacity-input{inline-size:88px}";
const DEFAULT_COLOR = color();
const ColorPickerHexInput = /*@__PURE__*/ proxyCustomElement(class extends HTMLElement {
constructor() {
super();
this.__registerHost();
this.__attachShadow();
this.calciteColorPickerHexInputChange = createEvent(this, "calciteColorPickerHexInputChange", 6);
this.onHexInputBlur = () => {
const node = this.hexInputNode;
const inputValue = node.value;
const hex = `#${inputValue}`;
const { allowEmpty, internalColor } = this;
const willClearValue = allowEmpty && !inputValue;
const isLonghand = isLonghandHex(hex);
// ensure modified pasted hex values are committed since we prevent default to remove the # char.
this.onHexInputChange();
if (willClearValue || (isValidHex(hex) && isLonghand)) {
return;
}
// manipulating DOM directly since rerender doesn't update input value
node.value =
allowEmpty && !internalColor
? ""
: this.formatHexForInternalInput(rgbToHex(
// always display hex input in RRGGBB format
internalColor.object()));
};
this.onOpacityInputBlur = () => {
const node = this.opacityInputNode;
const inputValue = node.value;
const { allowEmpty, internalColor } = this;
const willClearValue = allowEmpty && !inputValue;
if (willClearValue) {
return;
}
// manipulating DOM directly since rerender doesn't update input value
node.value =
allowEmpty && !internalColor ? "" : this.formatOpacityForInternalInput(internalColor);
};
this.onHexInputChange = () => {
const nodeValue = this.hexInputNode.value;
let value = nodeValue;
if (value) {
const normalized = normalizeHex(value, false);
const preserveExistingAlpha = isValidHex(normalized) && this.alphaChannel;
if (preserveExistingAlpha && this.internalColor) {
const alphaHex = normalizeHex(this.internalColor.hexa(), true).slice(-2);
value = `${normalized + alphaHex}`;
}
}
this.internalSetValue(value, this.value);
};
this.onOpacityInputChange = () => {
const node = this.opacityInputNode;
let value;
if (!node.value) {
value = node.value;
}
else {
const alpha = opacityToAlpha(Number(node.value));
value = this.internalColor?.alpha(alpha).hexa();
}
this.internalSetValue(value, this.value);
};
this.onInputKeyDown = (event) => {
const { altKey, ctrlKey, metaKey, shiftKey } = event;
const { alphaChannel, hexInputNode, internalColor, value } = this;
const { key } = event;
const composedPath = event.composedPath();
if (key === "Tab" || key === "Enter") {
if (composedPath.includes(hexInputNode)) {
this.onHexInputChange();
}
else {
this.onOpacityInputChange();
}
if (key === "Enter") {
event.preventDefault();
}
return;
}
const isNudgeKey = key === "ArrowDown" || key === "ArrowUp";
const oldValue = this.value;
if (isNudgeKey) {
if (!value) {
this.internalSetValue(this.previousNonNullValue, oldValue);
event.preventDefault();
return;
}
const direction = key === "ArrowUp" ? 1 : -1;
const bump = shiftKey ? 10 : 1;
this.internalSetValue(hexify(this.nudgeRGBChannels(internalColor, bump * direction, composedPath.includes(hexInputNode) ? "rgb" : "a"), alphaChannel), oldValue);
event.preventDefault();
return;
}
const withModifiers = altKey || ctrlKey || metaKey;
const singleChar = key.length === 1;
const validHexChar = hexChar.test(key);
if (singleChar && !withModifiers && !validHexChar) {
event.preventDefault();
}
};
this.onHexInputPaste = (event) => {
const hex = event.clipboardData.getData("text");
if (isValidHex(hex)) {
event.preventDefault();
this.hexInputNode.value = hex.slice(1);
this.hexInputNode.internalSyncChildElValue();
}
};
this.previousNonNullValue = this.value;
this.storeHexInputRef = (node) => {
this.hexInputNode = node;
};
this.storeOpacityInputRef = (node) => {
this.opacityInputNode = node;
};
this.allowEmpty = false;
this.alphaChannel = false;
this.hexLabel = "Hex";
this.messages = undefined;
this.numberingSystem = undefined;
this.scale = "m";
this.value = normalizeHex(hexify(DEFAULT_COLOR, this.alphaChannel), this.alphaChannel, true);
this.internalColor = DEFAULT_COLOR;
}
//--------------------------------------------------------------------------
//
// Lifecycle
//
//--------------------------------------------------------------------------
connectedCallback() {
const { allowEmpty, alphaChannel, value } = this;
if (value) {
const normalized = normalizeHex(value, alphaChannel);
if (isValidHex(normalized, alphaChannel)) {
this.internalSetValue(normalized, normalized, false);
}
return;
}
if (allowEmpty) {
this.internalSetValue(null, null, false);
}
}
componentWillLoad() {
setUpLoadableComponent(this);
}
componentDidLoad() {
setComponentLoaded(this);
}
handleValueChange(value, oldValue) {
this.internalSetValue(value, oldValue, false);
}
//--------------------------------------------------------------------------
//
// Lifecycle
//
//--------------------------------------------------------------------------
render() {
const { alphaChannel, hexLabel, internalColor, messages, scale, value } = this;
const hexInputValue = this.formatHexForInternalInput(value);
const opacityInputValue = this.formatOpacityForInternalInput(internalColor);
const inputScale = scale === "l" ? "m" : "s";
return (h("div", { class: CSS.container }, h("calcite-input", { class: CSS.hexInput, label: messages?.hex || hexLabel, maxLength: 6, numberingSystem: this.numberingSystem, onCalciteInputChange: this.onHexInputChange, onCalciteInternalInputBlur: this.onHexInputBlur, onKeyDown: this.onInputKeyDown, onPaste: this.onHexInputPaste, prefixText: "#", scale: inputScale, value: hexInputValue,
// eslint-disable-next-line react/jsx-sort-props
ref: this.storeHexInputRef }), alphaChannel ? (h("calcite-input-number", { class: CSS.opacityInput, key: "opacity-input", label: messages?.opacity, max: OPACITY_LIMITS.max, maxLength: 3, min: OPACITY_LIMITS.min, numberButtonType: "none", numberingSystem: this.numberingSystem, onCalciteInputNumberChange: this.onOpacityInputChange, onCalciteInternalInputNumberBlur: this.onOpacityInputBlur, onKeyDown: this.onInputKeyDown, scale: inputScale, suffixText: "%", value: opacityInputValue,
// eslint-disable-next-line react/jsx-sort-props
ref: this.storeOpacityInputRef })) : null));
}
//--------------------------------------------------------------------------
//
// Public Methods
//
//--------------------------------------------------------------------------
/** Sets focus on the component. */
async setFocus() {
await componentLoaded(this);
focusElement(this.hexInputNode);
}
//--------------------------------------------------------------------------
//
// Private Methods
//
//--------------------------------------------------------------------------
internalSetValue(value, oldValue, emit = true) {
if (value) {
const { alphaChannel } = this;
const normalized = normalizeHex(value, alphaChannel, alphaChannel);
if (isValidHex(normalized, alphaChannel)) {
const { internalColor: currentColor } = this;
const nextColor = color(normalized);
const normalizedLonghand = normalizeHex(hexify(nextColor, alphaChannel), alphaChannel);
const changed = !currentColor ||
normalizedLonghand !== normalizeHex(hexify(currentColor, alphaChannel), alphaChannel);
this.internalColor = nextColor;
this.previousNonNullValue = normalizedLonghand;
this.value = normalizedLonghand;
if (changed && emit) {
this.calciteColorPickerHexInputChange.emit();
}
return;
}
}
else if (this.allowEmpty) {
this.internalColor = null;
this.value = null;
if (emit) {
this.calciteColorPickerHexInputChange.emit();
}
return;
}
this.value = oldValue;
}
formatHexForInternalInput(hex) {
return hex ? hex.replace("#", "").slice(0, 6) : "";
}
formatOpacityForInternalInput(color) {
return color ? `${alphaToOpacity(color.alpha())}` : "";
}
nudgeRGBChannels(color$1, amount, context) {
let nudgedChannels;
const channels = color$1.array();
const rgbChannels = channels.slice(0, 3);
if (context === "rgb") {
const nudgedRGBChannels = rgbChannels.map((channel) => channel + amount);
nudgedChannels = [
...nudgedRGBChannels,
this.alphaChannel ? channels[3] : undefined
];
}
else {
const nudgedAlpha = opacityToAlpha(alphaToOpacity(color$1.alpha()) + amount);
nudgedChannels = [...rgbChannels, nudgedAlpha];
}
return color(nudgedChannels);
}
get el() { return this; }
static get watchers() { return {
"value": ["handleValueChange"]
}; }
static get style() { return colorPickerHexInputCss; }
}, [1, "calcite-color-picker-hex-input", {
"allowEmpty": [4, "allow-empty"],
"alphaChannel": [4, "alpha-channel"],
"hexLabel": [1, "hex-label"],
"messages": [16],
"numberingSystem": [1, "numbering-system"],
"scale": [513],
"value": [1537],
"internalColor": [32],
"setFocus": [64]
}]);
function defineCustomElement() {
if (typeof customElements === "undefined") {
return;
}
const components = ["calcite-color-picker-hex-input", "calcite-icon", "calcite-input", "calcite-input-number", "calcite-progress"];
components.forEach(tagName => { switch (tagName) {
case "calcite-color-picker-hex-input":
if (!customElements.get(tagName)) {
customElements.define(tagName, ColorPickerHexInput);
}
break;
case "calcite-icon":
if (!customElements.get(tagName)) {
defineCustomElement$4();
}
break;
case "calcite-input":
if (!customElements.get(tagName)) {
defineCustomElement$3();
}
break;
case "calcite-input-number":
if (!customElements.get(tagName)) {
defineCustomElement$2();
}
break;
case "calcite-progress":
if (!customElements.get(tagName)) {
defineCustomElement$1();
}
break;
} });
}
defineCustomElement();
export { CSS$1 as C, DEFAULT_COLOR$1 as D, HSV_LIMITS as H, OPACITY_LIMITS as O, RGB_LIMITS as R, DIMENSIONS as a, DEFAULT_STORAGE_KEY_PREFIX as b, ColorPickerHexInput as c, defineCustomElement as d };