UNPKG

@esri/calcite-components

Version:

Web Components for Esri's Calcite Design System.

350 lines (344 loc) • 13.2 kB
/*! * All material copyright ESRI, All Rights Reserved, unless otherwise specified. * See https://github.com/Esri/calcite-components/blob/master/LICENSE.md for details. */ import { proxyCustomElement, HTMLElement, createEvent, h } from '@stencil/core/internal/client'; import { n as normalizeHex, i as isValidHex, a as isLonghandHex, r as rgbToHex, h as hexToRGB, b as hexChar } from './utils.js'; import { c as color, d as defineCustomElement$4 } from './color-picker-swatch.js'; import { f as focusElement } from './dom.js'; import { d as defineCustomElement$3 } from './icon.js'; import { d as defineCustomElement$2 } from './input.js'; import { d as defineCustomElement$1 } from './progress.js'; const CSS$1 = { container: "container", controlSection: "control-section", hexOptions: "color-hex-options", section: "section", header: "header", control: "control", splitSection: "section--split", colorModeContainer: "color-mode-container", colorMode: "color-mode", channels: "channels", channel: "channel", savedColors: "saved-colors", savedColorsSection: "saved-colors-section", saveColor: "save-color", deleteColor: "delete-color", savedColorsButtons: "saved-colors-buttons", headerHex: "header--hex", colorFieldAndSlider: "color-field-and-slider", colorFieldAndSliderInteractive: "color-field-and-slider--interactive", colorFieldAndSliderWrap: "color-field-and-slider-wrap", scope: "scope", hueScope: "scope--hue", colorFieldScope: "scope--color-field", savedColor: "saved-color" }; 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 TEXT = { b: "B", blue: "Blue", deleteColor: "Delete color", g: "G", green: "Green", h: "H", hsv: "HSV", hex: "Hex", hue: "Hue", noColor: "No color", r: "R", red: "Red", rgb: "RGB", s: "S", saturation: "Saturation", saveColor: "Save color", saved: "Saved", v: "V", value: "Value" }; const DIMENSIONS = { s: { slider: { height: 10, width: 160 }, colorField: { height: 80, width: 160 }, thumb: { radius: 8 } }, m: { slider: { height: 14, width: 272 }, colorField: { height: 150, width: 272 }, thumb: { radius: 10 } }, l: { slider: { height: 16, width: 464 }, colorField: { height: 200, width: 464 }, thumb: { radius: 12 } } }; const CSS = { container: "container", preview: "preview", input: "input" }; const colorPickerHexInputCss = "@-webkit-keyframes in{0%{opacity:0}100%{opacity:1}}@keyframes in{0%{opacity:0}100%{opacity:1}}@-webkit-keyframes in-down{0%{opacity:0;-webkit-transform:translate3D(0, -5px, 0);transform:translate3D(0, -5px, 0)}100%{opacity:1;-webkit-transform:translate3D(0, 0, 0);transform:translate3D(0, 0, 0)}}@keyframes in-down{0%{opacity:0;-webkit-transform:translate3D(0, -5px, 0);transform:translate3D(0, -5px, 0)}100%{opacity:1;-webkit-transform:translate3D(0, 0, 0);transform:translate3D(0, 0, 0)}}@-webkit-keyframes in-up{0%{opacity:0;-webkit-transform:translate3D(0, 5px, 0);transform:translate3D(0, 5px, 0)}100%{opacity:1;-webkit-transform:translate3D(0, 0, 0);transform:translate3D(0, 0, 0)}}@keyframes in-up{0%{opacity:0;-webkit-transform:translate3D(0, 5px, 0);transform:translate3D(0, 5px, 0)}100%{opacity:1;-webkit-transform:translate3D(0, 0, 0);transform:translate3D(0, 0, 0)}}@-webkit-keyframes in-scale{0%{opacity:0;-webkit-transform:scale3D(0.95, 0.95, 1);transform:scale3D(0.95, 0.95, 1)}100%{opacity:1;-webkit-transform:scale3D(1, 1, 1);transform:scale3D(1, 1, 1)}}@keyframes in-scale{0%{opacity:0;-webkit-transform:scale3D(0.95, 0.95, 1);transform:scale3D(0.95, 0.95, 1)}100%{opacity:1;-webkit-transform:scale3D(1, 1, 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;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-duration:var(--calcite-animation-timing);animation-duration:var(--calcite-animation-timing)}.calcite-animate__in{-webkit-animation-name:in;animation-name:in}.calcite-animate__in-down{-webkit-animation-name:in-down;animation-name:in-down}.calcite-animate__in-up{-webkit-animation-name:in-up;animation-name:in-up}.calcite-animate__in-scale{-webkit-animation-name:in-scale;animation-name:in-scale}:root{--calcite-popper-transition:var(--calcite-animation-timing)}:host([hidden]){display:none}:host{display:block}.container{display:inline-grid;width:100%;-ms-flex-align:center;align-items:center;grid-template-columns:1fr auto}.preview{grid-column:2/3;pointer-events:none;margin-top:0px;margin-bottom:0px;margin-left:0.25rem;margin-right:0.25rem;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;z-index:1}.preview,.input{grid-row:1}.input{grid-column:1/3;width:100%;text-transform:uppercase}"; const DEFAULT_COLOR = color(); const ColorPickerHexInput = /*@__PURE__*/ proxyCustomElement(class extends HTMLElement { constructor() { super(); this.__registerHost(); this.__attachShadow(); this.calciteColorPickerHexInputChange = createEvent(this, "calciteColorPickerHexInputChange", 7); //-------------------------------------------------------------------------- // // Public Properties // //-------------------------------------------------------------------------- /** * When false, empty color (null) will be allowed as a value. Otherwise, a color value is always enforced by the component. * * When true, clearing the input and blurring will restore the last valid color set. When false, it will set it to empty. */ this.allowEmpty = false; /** * Label used for the hex input. * @default "Hex" */ this.intlHex = TEXT.hex; /** * Label used for the hex input when there is no color selected. * @default "No color" */ this.intlNoColor = TEXT.noColor; /** * The component's scale. */ this.scale = "m"; /** * The hex value. */ this.value = normalizeHex(DEFAULT_COLOR.hex()); this.onCalciteInputBlur = () => { const node = this.inputNode; const inputValue = node.value; const hex = `#${inputValue}`; const willClearValue = this.allowEmpty && !inputValue; if (willClearValue || (isValidHex(hex) && isLonghandHex(hex))) { return; } // manipulating DOM directly since rerender doesn't update input value node.value = this.allowEmpty && !this.internalColor ? "" : this.formatForInternalInput(rgbToHex(this.internalColor.object())); }; this.onInputChange = () => { const inputValue = this.inputNode.value; let value; if (inputValue) { const hex = inputValue; const color = hexToRGB(`#${hex}`); if (!color) { return; } value = normalizeHex(hex); } else if (this.allowEmpty) { value = null; } this.value = value; this.calciteColorPickerHexInputChange.emit(); }; /** * The last valid/selected color. Used as a fallback if an invalid hex code is entered. */ this.internalColor = DEFAULT_COLOR; this.previousNonNullValue = this.value; //-------------------------------------------------------------------------- // // Private Methods // //-------------------------------------------------------------------------- this.storeInputRef = (node) => { this.inputNode = node; }; } //-------------------------------------------------------------------------- // // Lifecycle // //-------------------------------------------------------------------------- connectedCallback() { const { allowEmpty, value } = this; if (value) { const normalized = normalizeHex(value); if (isValidHex(normalized)) { this.internalColor = color(normalized); this.value = normalized; } return; } if (allowEmpty) { this.internalColor = null; this.value = null; } } handleValueChange(value, oldValue) { if (value) { const normalized = normalizeHex(value); if (isValidHex(normalized)) { const { internalColor } = this; const changed = !internalColor || normalized !== normalizeHex(internalColor.hex()); this.internalColor = color(normalized); this.previousNonNullValue = normalized; this.value = normalized; if (changed) { this.calciteColorPickerHexInputChange.emit(); } return; } } else if (this.allowEmpty) { this.internalColor = null; this.value = null; this.calciteColorPickerHexInputChange.emit(); return; } this.value = oldValue; } // using @Listen as a workaround for VDOM listener not firing onInputKeyDown(event) { const { altKey, ctrlKey, metaKey, shiftKey } = event; const { internalColor, value } = this; const key = event.key; if (key === "Tab" || key === "Enter") { this.onInputChange(); return; } const isNudgeKey = key === "ArrowDown" || key === "ArrowUp"; if (isNudgeKey) { if (!value) { this.value = this.previousNonNullValue; event.preventDefault(); return; } const direction = key === "ArrowUp" ? 1 : -1; const bump = shiftKey ? 10 : 1; this.value = normalizeHex(this.nudgeRGBChannels(internalColor, bump * direction).hex()); event.preventDefault(); return; } const withModifiers = altKey || ctrlKey || metaKey; const singleChar = key.length === 1; const validHexChar = hexChar.test(key); if (singleChar && !withModifiers && !validHexChar) { event.preventDefault(); } } //-------------------------------------------------------------------------- // // Lifecycle // //-------------------------------------------------------------------------- render() { const { intlHex, value } = this; const hexInputValue = this.formatForInternalInput(value); return (h("div", { class: CSS.container }, h("calcite-input", { class: CSS.input, label: intlHex, maxLength: 6, onCalciteInputBlur: this.onCalciteInputBlur, onCalciteInputChange: this.onInputChange, prefixText: "#", ref: this.storeInputRef, scale: this.scale, value: hexInputValue }), hexInputValue ? (h("calcite-color-picker-swatch", { active: true, class: CSS.preview, color: `#${hexInputValue}`, scale: this.scale })) : null)); } //-------------------------------------------------------------------------- // // Public Methods // //-------------------------------------------------------------------------- /** Sets focus on the component. */ async setFocus() { focusElement(this.inputNode); } formatForInternalInput(hex) { return hex ? hex.replace("#", "") : ""; } nudgeRGBChannels(color$1, amount) { return color.rgb(color$1.array().map((channel) => channel + amount)); } 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"], "intlHex": [1, "intl-hex"], "intlNoColor": [1, "intl-no-color"], "scale": [513], "value": [1537], "internalColor": [32], "setFocus": [64] }, [[2, "keydown", "onInputKeyDown"]]]); function defineCustomElement() { if (typeof customElements === "undefined") { return; } const components = ["calcite-color-picker-hex-input", "calcite-color-picker-swatch", "calcite-icon", "calcite-input", "calcite-progress"]; components.forEach(tagName => { switch (tagName) { case "calcite-color-picker-hex-input": if (!customElements.get(tagName)) { customElements.define(tagName, ColorPickerHexInput); } break; case "calcite-color-picker-swatch": if (!customElements.get(tagName)) { defineCustomElement$4(); } break; case "calcite-icon": if (!customElements.get(tagName)) { defineCustomElement$3(); } break; case "calcite-input": 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, RGB_LIMITS as R, TEXT as T, DIMENSIONS as a, DEFAULT_STORAGE_KEY_PREFIX as b, ColorPickerHexInput as c, defineCustomElement as d };