@progress/kendo-angular-inputs
Version:
Kendo UI for Angular Inputs Package - Everything you need to build professional form functionality (Checkbox, ColorGradient, ColorPalette, ColorPicker, FlatColorPicker, FormField, MaskedTextBox, NumericTextBox, RadioButton, RangeSlider, Slider, Switch, Te
499 lines (498 loc) • 20 kB
JavaScript
/**-----------------------------------------------------------------------------------------
* Copyright © 2025 Progress Software Corporation. All rights reserved.
* Licensed under commercial license. See LICENSE.md in the project root for more information
*-------------------------------------------------------------------------------------------*/
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Component, Input, Output, EventEmitter, ElementRef, HostBinding, ViewChild, Renderer2, ChangeDetectorRef } from '@angular/core';
import { getRGBA, parseColor, getColorFromRGBA } from './utils';
import { isPresent } from '../common/utils';
import { guid, isDocumentAvailable } from '@progress/kendo-angular-common';
import { Subscription } from 'rxjs';
import { LocalizationService } from '@progress/kendo-angular-l10n';
import { NumericTextBoxComponent } from './../numerictextbox/numerictextbox.component';
import { caretAltExpandIcon } from '@progress/kendo-svg-icons';
import { NumericLabelDirective } from './color-gradient-numeric-label.directive';
import { ButtonComponent } from '@progress/kendo-angular-buttons';
import { TextBoxComponent } from '../textbox/textbox.component';
import { TextLabelDirective } from './color-gradient-text-label.directive';
import * as i0 from "@angular/core";
import * as i1 from "@progress/kendo-angular-l10n";
const DEFAULT_SIZE = 'medium';
/**
* @hidden
*/
export class ColorInputComponent {
host;
renderer;
cdr;
localizationService;
/**
* Sets the `id` of the hex input.
*/
focusableId = `k-${guid()}`;
/**
* Sets the color format view.
*/
formatView;
/**
* Sets the size of the ColorInput.
*
* @default 'medium'
*/
size = DEFAULT_SIZE;
/**
* Sets the `tabindex` of the inputs.
* @default -1
*/
tabindex = -1;
/**
* Sets the color value to parse and populate the hex and RGBA inputs.
*/
value;
/**
* Shows or hides the alpha slider.
*
* @default true
*/
opacity = true;
/**
* Disables the ColorInput.
*
* @default false
*/
disabled = false;
/**
* Sets the read-only state of the ColorInput.
*
* @default false
*/
readonly = false;
/**
* Emits a parsed RGBA string color.
*/
valueChange = new EventEmitter();
/**
* Fires when the user tabs out of the last focusable input.
*/
tabOut = new EventEmitter();
colorInputClass = true;
opacityInput;
hexInput;
blueInput;
toggleFormatButton;
/**
* Holds the RGBA input values.
*/
rgba = {};
/*
* Holds the hex input value.
*/
hex;
/**
* Returns `true` if any of the inputs are focused.
*/
get isFocused() {
if (!(isDocumentAvailable() && isPresent(this.host))) {
return false;
}
const activeElement = document.activeElement;
return this.host.nativeElement.contains(activeElement);
}
/**
* Returns `true` if all RGBA inputs have values.
*/
get rgbaInputValid() {
return Object.keys(this.rgba).every(key => isPresent(this.rgba[key]));
}
/**
* @hidden
*/
caretAltExpandIcon = caretAltExpandIcon;
subscriptions = new Subscription();
constructor(host, renderer, cdr, localizationService) {
this.host = host;
this.renderer = renderer;
this.cdr = cdr;
this.localizationService = localizationService;
}
ngAfterViewInit() {
this.initDomEvents();
}
ngOnDestroy() {
if (this.subscriptions) {
this.subscriptions.unsubscribe();
}
}
ngOnChanges(changes) {
if (isPresent(changes['value']) && !this.isFocused) {
this.hex = parseColor(this.value, 'hex', this.opacity);
this.rgba = getRGBA(this.value);
this.rgba.a = parseColor(this.value, 'rgba', this.opacity) ? this.rgba.a : 1;
}
}
get formatButtonTitle() {
return this.localizationService.get('formatButton');
}
handleRgbaValueChange() {
const color = getColorFromRGBA(this.rgba);
if (!this.rgbaInputValid || color === this.value) {
return;
}
this.value = color;
this.rgba = getRGBA(this.value);
this.hex = parseColor(color, 'hex', this.opacity);
this.valueChange.emit(color);
}
focusDragHandle(event) {
event.preventDefault();
event.stopImmediatePropagation();
this.tabOut.emit();
}
handleHexValueChange(hex) {
this.hex = hex;
const color = parseColor(hex, 'rgba', this.opacity);
if (!isPresent(color) || color === this.value) {
return;
}
this.value = color;
this.rgba = getRGBA(color);
this.valueChange.emit(color);
}
handleRgbaInputBlur() {
if (!this.rgbaInputValid) {
this.rgba = getRGBA(this.value);
}
}
handleHexInputBlur() {
this.hex = parseColor(this.value, 'hex', this.opacity);
}
focusLast() {
this.lastInput().focus();
}
onTab() {
if (this.opacity) {
return;
}
}
toggleFormatView() {
this.formatView = this.formatView === 'hex' ? 'rgba' : 'hex';
// needed to update the view when ChangeDetectionStrategy.OnPush
this.cdr.markForCheck();
}
initDomEvents() {
if (!this.host) {
return;
}
this.subscriptions.add(this.renderer.listen(this.toggleFormatButton.nativeElement, 'click', () => this.toggleFormatView()));
}
lastInput() {
return this.hexInput?.nativeElement || this.opacityInput || this.blueInput;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ColorInputComponent, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }, { token: i0.ChangeDetectorRef }, { token: i1.LocalizationService }], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ColorInputComponent, isStandalone: true, selector: "kendo-colorinput", inputs: { focusableId: "focusableId", formatView: "formatView", size: "size", tabindex: "tabindex", value: "value", opacity: "opacity", disabled: "disabled", readonly: "readonly" }, outputs: { valueChange: "valueChange", tabOut: "tabOut" }, host: { properties: { "class.k-colorgradient-inputs": "this.colorInputClass", "class.k-hstack": "this.colorInputClass" } }, viewQueries: [{ propertyName: "opacityInput", first: true, predicate: ["opacityInput"], descendants: true }, { propertyName: "hexInput", first: true, predicate: ["hexInput"], descendants: true }, { propertyName: "blueInput", first: true, predicate: ["blue"], descendants: true }, { propertyName: "toggleFormatButton", first: true, predicate: ["toggleFormatButton"], descendants: true, read: ElementRef }], usesOnChanges: true, ngImport: i0, template: `
<div class="k-vstack">
<button
kendoButton
type="button"
fillMode="flat"
icon="caret-alt-expand"
[]="caretAltExpandIcon"
[]="size"
class="k-colorgradient-toggle-mode"
[]="formatButtonTitle"
[]="formatButtonTitle"
[]="disabled"
[]="tabindex.toString()"
>
</button>
</div>
@if (formatView === 'hex') {
<div class="k-vstack k-flex-1">
<kendo-textbox
kendoTextLabel
[]="focusableId"
class="k-hex-value"
[]="size"
[]="readonly"
[]="disabled"
[]="readonly"
[]="hex || ''"
(blur)="handleHexInputBlur()"
(input)="handleHexValueChange(hexInput.value)"
[]="tabindex"
(keydown.tab)="focusDragHandle($event)">
</kendo-textbox>
<label [for]="focusableId" class="k-colorgradient-input-label">HEX</label>
</div>
}
@if (formatView === 'rgba') {
<div class="k-vstack">
<kendo-numerictextbox
kendoAdditionalNumericLabel="red"
[]="localizationService"
[]="disabled"
[]="size"
[]="readonly"
[]="tabindex"
[]="0"
[]="255"
[(value)]="rgba.r"
[]="true"
[]="false"
[]="'n'"
[]="0"
(blur)="handleRgbaInputBlur()"
(valueChange)="handleRgbaValueChange()">
</kendo-numerictextbox>
<label [for]="red.focusableId" class="k-colorgradient-input-label">R</label>
</div>
<div class="k-vstack">
<kendo-numerictextbox
kendoAdditionalNumericLabel="green"
[]="localizationService"
[]="disabled"
[]="readonly"
[]="tabindex"
[]="size"
[]="0"
[]="255"
[(value)]="rgba.g"
[]="true"
[]="false"
[]="'n'"
[]="0"
(blur)="handleRgbaInputBlur()"
(valueChange)="handleRgbaValueChange()">
</kendo-numerictextbox>
<label [for]="green.focusableId" class="k-colorgradient-input-label">G</label>
</div>
<div class="k-vstack">
<kendo-numerictextbox
kendoAdditionalNumericLabel="blue"
[]="localizationService"
[]="disabled"
[]="readonly"
[]="tabindex"
[]="size"
[]="0"
[]="255"
[(value)]="rgba.b"
[]="true"
[]="false"
[]="'n'"
[]="0"
(blur)="handleRgbaInputBlur()"
(valueChange)="handleRgbaValueChange()"
(keydown.tab)="onTab()">
</kendo-numerictextbox>
<label [for]="blue.focusableId" class="k-colorgradient-input-label">B</label>
</div>
@if (opacity) {
<div class="k-vstack">
<kendo-numerictextbox
kendoAdditionalNumericLabel="alpha"
[]="localizationService"
[]="disabled"
[]="readonly"
[]="tabindex"
[]="size"
[]="0"
[]="1"
[(value)]="rgba.a"
[]="true"
[]="false"
[]="0.01"
[]="'n2'"
[]="2"
(blur)="handleRgbaInputBlur()"
(valueChange)="handleRgbaValueChange()"
(keydown.tab)="focusDragHandle($event)">
</kendo-numerictextbox>
<label [for]="alpha.focusableId" class="k-colorgradient-input-label">A</label>
</div>
}
}
`, isInline: true, dependencies: [{ kind: "component", type: ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon", "primary", "look"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "component", type: NumericTextBoxComponent, selector: "kendo-numerictextbox", inputs: ["focusableId", "disabled", "readonly", "title", "autoCorrect", "format", "max", "min", "decimals", "placeholder", "step", "spinners", "rangeValidation", "tabindex", "tabIndex", "changeValueOnScroll", "selectOnFocus", "value", "maxlength", "size", "rounded", "fillMode", "inputAttributes"], outputs: ["valueChange", "focus", "blur", "inputFocus", "inputBlur"], exportAs: ["kendoNumericTextBox"] }, { kind: "directive", type: NumericLabelDirective, selector: "[kendoAdditionalNumericLabel]", inputs: ["kendoAdditionalNumericLabel", "localizationService"] }, { kind: "component", type: TextBoxComponent, selector: "kendo-textbox", inputs: ["focusableId", "title", "type", "disabled", "readonly", "tabindex", "value", "selectOnFocus", "showSuccessIcon", "showErrorIcon", "clearButton", "successIcon", "successSvgIcon", "errorIcon", "errorSvgIcon", "clearButtonIcon", "clearButtonSvgIcon", "size", "rounded", "fillMode", "tabIndex", "placeholder", "maxlength", "inputAttributes"], outputs: ["valueChange", "inputFocus", "inputBlur", "focus", "blur"], exportAs: ["kendoTextBox"] }, { kind: "directive", type: TextLabelDirective, selector: "[kendoTextLabel]", inputs: ["focusableId"] }] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ColorInputComponent, decorators: [{
type: Component,
args: [{
selector: 'kendo-colorinput',
template: `
<div class="k-vstack">
<button
kendoButton
type="button"
fillMode="flat"
icon="caret-alt-expand"
[]="caretAltExpandIcon"
[]="size"
class="k-colorgradient-toggle-mode"
[]="formatButtonTitle"
[]="formatButtonTitle"
[]="disabled"
[]="tabindex.toString()"
>
</button>
</div>
@if (formatView === 'hex') {
<div class="k-vstack k-flex-1">
<kendo-textbox
kendoTextLabel
[]="focusableId"
class="k-hex-value"
[]="size"
[]="readonly"
[]="disabled"
[]="readonly"
[]="hex || ''"
(blur)="handleHexInputBlur()"
(input)="handleHexValueChange(hexInput.value)"
[]="tabindex"
(keydown.tab)="focusDragHandle($event)">
</kendo-textbox>
<label [for]="focusableId" class="k-colorgradient-input-label">HEX</label>
</div>
}
@if (formatView === 'rgba') {
<div class="k-vstack">
<kendo-numerictextbox
kendoAdditionalNumericLabel="red"
[]="localizationService"
[]="disabled"
[]="size"
[]="readonly"
[]="tabindex"
[]="0"
[]="255"
[(value)]="rgba.r"
[]="true"
[]="false"
[]="'n'"
[]="0"
(blur)="handleRgbaInputBlur()"
(valueChange)="handleRgbaValueChange()">
</kendo-numerictextbox>
<label [for]="red.focusableId" class="k-colorgradient-input-label">R</label>
</div>
<div class="k-vstack">
<kendo-numerictextbox
kendoAdditionalNumericLabel="green"
[]="localizationService"
[]="disabled"
[]="readonly"
[]="tabindex"
[]="size"
[]="0"
[]="255"
[(value)]="rgba.g"
[]="true"
[]="false"
[]="'n'"
[]="0"
(blur)="handleRgbaInputBlur()"
(valueChange)="handleRgbaValueChange()">
</kendo-numerictextbox>
<label [for]="green.focusableId" class="k-colorgradient-input-label">G</label>
</div>
<div class="k-vstack">
<kendo-numerictextbox
kendoAdditionalNumericLabel="blue"
[]="localizationService"
[]="disabled"
[]="readonly"
[]="tabindex"
[]="size"
[]="0"
[]="255"
[(value)]="rgba.b"
[]="true"
[]="false"
[]="'n'"
[]="0"
(blur)="handleRgbaInputBlur()"
(valueChange)="handleRgbaValueChange()"
(keydown.tab)="onTab()">
</kendo-numerictextbox>
<label [for]="blue.focusableId" class="k-colorgradient-input-label">B</label>
</div>
@if (opacity) {
<div class="k-vstack">
<kendo-numerictextbox
kendoAdditionalNumericLabel="alpha"
[]="localizationService"
[]="disabled"
[]="readonly"
[]="tabindex"
[]="size"
[]="0"
[]="1"
[(value)]="rgba.a"
[]="true"
[]="false"
[]="0.01"
[]="'n2'"
[]="2"
(blur)="handleRgbaInputBlur()"
(valueChange)="handleRgbaValueChange()"
(keydown.tab)="focusDragHandle($event)">
</kendo-numerictextbox>
<label [for]="alpha.focusableId" class="k-colorgradient-input-label">A</label>
</div>
}
}
`,
standalone: true,
imports: [ButtonComponent, NumericTextBoxComponent, NumericLabelDirective, TextBoxComponent, TextLabelDirective]
}]
}], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.Renderer2 }, { type: i0.ChangeDetectorRef }, { type: i1.LocalizationService }], propDecorators: { focusableId: [{
type: Input
}], formatView: [{
type: Input
}], size: [{
type: Input
}], tabindex: [{
type: Input
}], value: [{
type: Input
}], opacity: [{
type: Input
}], disabled: [{
type: Input
}], readonly: [{
type: Input
}], valueChange: [{
type: Output
}], tabOut: [{
type: Output
}], colorInputClass: [{
type: HostBinding,
args: ['class.k-colorgradient-inputs']
}, {
type: HostBinding,
args: ['class.k-hstack']
}], opacityInput: [{
type: ViewChild,
args: ['opacityInput']
}], hexInput: [{
type: ViewChild,
args: ['hexInput']
}], blueInput: [{
type: ViewChild,
args: ['blue']
}], toggleFormatButton: [{
type: ViewChild,
args: ['toggleFormatButton', { static: false, read: ElementRef }]
}] } });