diff --git a/packages/devextreme/js/__internal/m_color.ts b/packages/devextreme/js/__internal/m_color.ts index 9b9096fb6126..eed05fea3f1c 100644 --- a/packages/devextreme/js/__internal/m_color.ts +++ b/packages/devextreme/js/__internal/m_color.ts @@ -259,6 +259,27 @@ const standardColorTypes = [ // eslint-disable-next-line @typescript-eslint/naming-convention const _round = Math.round; +export interface ColorInstance { + baseColor: string | undefined; + r: number; + g: number; + b: number; + a: number; + hsv: { h: number; s: number; v: number }; + hsl: { h: number; s: number; l: number }; + colorIsInvalid: boolean; + highlight: (step?: number) => string; + darken: (step?: number) => string; + alter: (step: number) => ColorInstance; + blend: (blendColor: ColorInstance | string, opacity: number) => ColorInstance; + toHex: () => string; + getPureColor: () => ColorInstance; + isValidHex: (hex: string) => boolean; + isValidRGB: (r: number | undefined, g: number | undefined, b: number | undefined) => boolean; + isValidAlpha: (a: number) => boolean; + fromHSL: (hsl: { h: number; s: number; l: number }) => ColorInstance; +} + function Color(value?) { this.baseColor = value; let color; @@ -495,10 +516,10 @@ function isIntegerBetweenMinAndMax(number, min?, max?) { min = min || 0; max = max || 255; - if (number % 1 !== 0 + if (typeof number !== 'number' + || number % 1 !== 0 || number < min || number > max - || typeof number !== 'number' || isNaN(number)) { return false; } @@ -580,4 +601,9 @@ Color.prototype = { }, }; -export default Color; +interface ColorConstructor { + prototype: ColorInstance; + new(value?: string): ColorInstance; +} + +export default Color as unknown as ColorConstructor; diff --git a/packages/devextreme/js/__internal/ui/color_box/m_color_box.ts b/packages/devextreme/js/__internal/ui/color_box/color_box.ts similarity index 93% rename from packages/devextreme/js/__internal/ui/color_box/m_color_box.ts rename to packages/devextreme/js/__internal/ui/color_box/color_box.ts index 08deb3051979..30287931a576 100644 --- a/packages/devextreme/js/__internal/ui/color_box/m_color_box.ts +++ b/packages/devextreme/js/__internal/ui/color_box/color_box.ts @@ -10,8 +10,8 @@ import type { ValueChangedEvent } from '@ts/ui/editor/editor'; import type { PopupProperties } from '../popup/m_popup'; import type Popup from '../popup/m_popup'; -import type { ColorViewProperties } from './m_color_view'; -import ColorView from './m_color_view'; +import type { ColorViewProperties } from './color_view'; +import ColorView from './color_view'; const COLOR_BOX_CLASS = 'dx-colorbox'; const COLOR_BOX_INPUT_CLASS = `${COLOR_BOX_CLASS}-input`; @@ -219,7 +219,7 @@ class ColorBox extends DropDownEditor { onValueChanged: ({ event, value: changedValue, previousValue }): void => { const { applyValueMode: currentValueMode } = this.option(); const isInstantlyMode = currentValueMode === 'instantly'; - const isOldValue = colorUtils.makeRgba(changedValue) === previousValue; + const isOldValue = colorUtils.makeRgba(new Color(changedValue)) === previousValue; const isChangesApplied = isInstantlyMode || this._colorViewEnterKeyPressed; const isValueCleared = this._shouldSaveEmptyValue; @@ -238,7 +238,7 @@ class ColorBox extends DropDownEditor { _enterKeyHandler(e: KeyboardEvent): boolean | undefined { const newValue = this._input().val(); const { value, editAlphaChannel } = this.option(); - const oldValue = value && editAlphaChannel ? colorUtils.makeRgba(value) : value; + const oldValue = value && editAlphaChannel ? colorUtils.makeRgba(new Color(value)) : value; if (!newValue) return false; @@ -251,7 +251,8 @@ class ColorBox extends DropDownEditor { if (newValue !== oldValue) { this._applyColorFromInput(newValue); this._saveValueChangeEvent(e); - this.option('value', editAlphaChannel ? colorUtils.makeRgba(newValue) : newValue); + this.option('value', editAlphaChannel + ? colorUtils.makeRgba(new Color(newValue)) : newValue); } if (this._colorView) { @@ -281,7 +282,8 @@ class ColorBox extends DropDownEditor { super._cancelButtonHandler(); } - // needed to be typed in widget.ts + // need to be typed in widget.ts + // eslint-disable-next-line @typescript-eslint/no-explicit-any _getKeyboardListeners(): any[] { return super._getKeyboardListeners().concat([this._colorView]); } @@ -303,7 +305,7 @@ class ColorBox extends DropDownEditor { } _renderNoColorIcon(): void { - if (!this._$noColorIcon || !this._$noColorIcon.length) { + if (!this._$noColorIcon?.length) { this._$noColorIcon = $('') .addClass(`${DX_ICON_CLASS} ${DX_ICON_COLOR_DISMISS}`) .appendTo(this._$colorResultPreview); @@ -312,14 +314,14 @@ class ColorBox extends DropDownEditor { _updateNoColorIndicator(): void { const { value } = this.option(); - const hasValue = Boolean(value); + const hasValue = value !== null && value !== undefined && value.length > 0; this._$colorBoxInputContainer.toggleClass(COLOR_BOX_COLOR_IS_NOT_DEFINED, !hasValue); if (hasValue) { this._cleanNoColorIcon(); - colorUtils.makeTransparentBackground(this._$colorResultPreview, value); + colorUtils.makeTransparentBackground(this._$colorResultPreview, new Color(value)); } else { this._$colorResultPreview.removeAttr('style'); this._renderNoColorIcon(); @@ -344,7 +346,7 @@ class ColorBox extends DropDownEditor { _renderValue(): DeferredObj { const { value, editAlphaChannel } = this.option(); const shouldConvertToColor = value && editAlphaChannel; - const text = shouldConvertToColor ? colorUtils.makeRgba(value) : value; + const text = shouldConvertToColor ? colorUtils.makeRgba(new Color(value)) : value; this.option('text', text); @@ -388,13 +390,12 @@ class ColorBox extends DropDownEditor { } if (editAlphaChannel) { - return colorUtils.makeRgba(value); + return colorUtils.makeRgba(newColor); } return value; } - // eslint-disable-next-line class-methods-use-this _shouldLogFieldTemplateDeprecationWarning(): boolean { return true; } @@ -435,7 +436,9 @@ class ColorBox extends DropDownEditor { case 'applyButtonText': case 'cancelButtonText': super._optionChanged(args); - this._popup && this._addPopupBottomClasses(); + if (this._popup) { + this._addPopupBottomClasses(); + } break; case 'editAlphaChannel': case 'keyStep': diff --git a/packages/devextreme/js/__internal/ui/color_box/m_color_view.ts b/packages/devextreme/js/__internal/ui/color_box/color_view.ts similarity index 94% rename from packages/devextreme/js/__internal/ui/color_box/m_color_view.ts rename to packages/devextreme/js/__internal/ui/color_box/color_view.ts index d716557dd2cc..1a70c8a699af 100644 --- a/packages/devextreme/js/__internal/ui/color_box/m_color_view.ts +++ b/packages/devextreme/js/__internal/ui/color_box/color_view.ts @@ -10,11 +10,11 @@ import { Guid } from '@ts/core/m_guid'; import { extend } from '@ts/core/utils/m_extend'; import { getHeight, getOuterHeight, getWidth } from '@ts/core/utils/m_size'; import type { OptionChanged } from '@ts/core/widget/types'; -import type { SupportedKeys } from '@ts/core/widget/widget'; +import type { SupportedKeyHandler, SupportedKeys } from '@ts/core/widget/widget'; import eventsEngine from '@ts/events/core/m_events_engine'; import { name as clickEventName } from '@ts/events/m_click'; import { isCommandKeyPressed } from '@ts/events/utils/index'; -import Color from '@ts/m_color'; +import Color, { type ColorInstance } from '@ts/m_color'; import Draggable from '@ts/m_draggable'; import type { EditorProperties, ValueChangedEvent } from '@ts/ui/editor/editor'; import Editor from '@ts/ui/editor/editor'; @@ -85,6 +85,23 @@ export interface ColorViewProperties extends EditorProperties { target?: string | Element | dxElementWrapper | null; } +type EditorWithLabelType = new ( + element: dxElementWrapper, + options?: object, +) => { registerKeyHandler: (key: string, handler: SupportedKeyHandler) => void }; + +interface EditorWithLabelOptions { + editorType: EditorWithLabelType; + value: number | string; + onValueChanged: (args: ValueChangedEvent) => void; + labelText: string; + labelAriaText: string; + labelClass: string; + min?: number; + max?: number; + step?: number; +} + class ColorView extends Editor { _$palette!: dxElementWrapper; @@ -100,8 +117,7 @@ class ColorView extends Editor { _updateByDrag?: boolean; - // need typings and correct class in m_color.ts - _currentColor!: any; + _currentColor!: ColorInstance; _isTopColorHue?: boolean; @@ -401,19 +417,11 @@ class ColorView extends Editor { this._renderAlphaChannelElements(); } - _makeTransparentBackground($el: dxElementWrapper, color: any): void { - if (!(color instanceof Color)) { - color = new Color(color); - } - + _makeTransparentBackground($el: dxElementWrapper, color: ColorInstance): void { $el.css('backgroundColor', this._makeRgba(color)); } - _makeRgba(color: any): string { - if (!(color instanceof Color)) { - color = new Color(color); - } - + _makeRgba(color: ColorInstance): string { return `rgba(${[color.r, color.g, color.b, color.a].join(', ')})`; } @@ -438,15 +446,12 @@ class ColorView extends Editor { if (delta < 0) { delta = Math.abs(delta); const rows: dxElementWrapper[] = []; - let i; - for (i = 0; i < delta; i++) { + for (let i = 0; i < delta; i += 1) { rows.push($('
').addClass(COLOR_VIEW_ROW_CLASS)); } if (renderedRowsCount) { - for (i = 0; i < rows.length; i++) { - $renderedRows.eq(0).after(rows[i]); - } + rows.forEach((row) => { $renderedRows.eq(0).after(row); }); } else { this._$colorPickerContainer.append(rows); } @@ -539,12 +544,12 @@ class ColorView extends Editor { _calculateColorValue(paletteHandlePosition: PaletteHandlePosition): number { const value = Math.floor(paletteHandlePosition.top + this._paletteHandleHeight / 2); - return 100 - Math.round(value * 100 / this._paletteHeight); + return 100 - Math.round((value * 100) / this._paletteHeight); } _calculateColorSaturation(paletteHandlePosition: PaletteHandlePosition): number { const saturation = Math.floor(paletteHandlePosition.left + this._paletteHandleWidth / 2); - return Math.round(saturation * 100 / this._paletteWidth); + return Math.round((saturation * 100) / this._paletteWidth); } _updateColorFromHsv(hue: number, saturation: number, value: number): void { @@ -603,7 +608,7 @@ class ColorView extends Editor { _placeHueScaleHandle(): void { const hueScaleHeight = this._hueScaleWrapperHeight; const handleHeight = this._hueScaleHandleHeight; - let top = (hueScaleHeight - handleHeight) * (360 - this._currentColor.hsv.h) / 360; + let top = ((hueScaleHeight - handleHeight) * (360 - this._currentColor.hsv.h)) / 360; if (hueScaleHeight < top + handleHeight) { top = hueScaleHeight - handleHeight; @@ -662,7 +667,7 @@ class ColorView extends Editor { this._$currentColor = $('
').addClass([COLOR_VIEW_COLOR_PREVIEW, COLOR_VIEW_COLOR_PREVIEW_COLOR_NEW].join(' ')); this._$baseColor = $('
').addClass([COLOR_VIEW_COLOR_PREVIEW, COLOR_VIEW_COLOR_PREVIEW_COLOR_CURRENT].join(' ')); - this._makeTransparentBackground(this._$baseColor, matchValue); + this._makeTransparentBackground(this._$baseColor, new Color(matchValue ?? BLACK_COLOR)); this._makeTransparentBackground(this._$currentColor, this._currentColor); $colorsPreviewContainerInner.append([this._$baseColor, this._$currentColor]); @@ -718,7 +723,7 @@ class ColorView extends Editor { ]; } - _renderEditorWithLabel(options): dxElementWrapper { + _renderEditorWithLabel(options: EditorWithLabelOptions): dxElementWrapper { const $editor = $('
'); const $label = $('