diff --git a/packages/common/src/editors/__tests__/floatEditor.spec.ts b/packages/common/src/editors/__tests__/floatEditor.spec.ts index 74ed8c7c4..830f82b25 100644 --- a/packages/common/src/editors/__tests__/floatEditor.spec.ts +++ b/packages/common/src/editors/__tests__/floatEditor.spec.ts @@ -93,7 +93,9 @@ describe('FloatEditor', () => { it('should initialize the editor', () => { editor = new FloatEditor(editorArguments); const editorCount = divContainer.querySelectorAll('input.editor-text.editor-price').length; + expect(editorCount).toBe(1); + expect(editor.inputType).toBe('number'); }); it('should initialize the editor and focus on the element after a small delay', () => { @@ -102,6 +104,7 @@ describe('FloatEditor', () => { jest.runAllTimers(); // fast-forward timer expect(editorCount).toBe(1); + expect(editor.inputType).toBe('number'); }); it('should have a placeholder when defined in its column definition', () => { diff --git a/packages/common/src/editors/__tests__/integerEditor.spec.ts b/packages/common/src/editors/__tests__/integerEditor.spec.ts index be7a321f0..8fc0049a7 100644 --- a/packages/common/src/editors/__tests__/integerEditor.spec.ts +++ b/packages/common/src/editors/__tests__/integerEditor.spec.ts @@ -93,7 +93,9 @@ describe('IntegerEditor', () => { it('should initialize the editor', () => { editor = new IntegerEditor(editorArguments); const editorCount = divContainer.querySelectorAll('input.editor-text.editor-price').length; + expect(editorCount).toBe(1); + expect(editor.inputType).toBe('number'); }); it('should initialize the editor and focus on the element after a small delay', () => { @@ -103,6 +105,7 @@ describe('IntegerEditor', () => { jest.runAllTimers(); // fast-forward timer expect(editorCount).toBe(1); + expect(editor.inputType).toBe('number'); }); it('should have a placeholder when defined in its column definition', () => { diff --git a/packages/common/src/editors/__tests__/textEditor.spec.ts b/packages/common/src/editors/__tests__/textEditor.spec.ts index d1a915b68..d83ebe974 100644 --- a/packages/common/src/editors/__tests__/textEditor.spec.ts +++ b/packages/common/src/editors/__tests__/textEditor.spec.ts @@ -1,5 +1,5 @@ import { Editors } from '../index'; -import { TextEditor } from '../textEditor'; +import { InputEditor } from '../TextEditor'; import { KeyCode } from '../../enums/index'; import { AutocompleteOption, Column, ColumnEditor, EditorArguments, GridOption, SlickDataView, SlickGrid, SlickNamespace } from '../../interfaces/index'; @@ -37,9 +37,9 @@ const gridStub = { onCompositeEditorChange: new Slick.Event(), } as unknown as SlickGrid; -describe('TextEditor', () => { +describe('InputEditor', () => { let divContainer: HTMLDivElement; - let editor: TextEditor; + let editor: InputEditor; let editorArguments: EditorArguments; let mockColumn: Column; let mockItemData: any; @@ -69,7 +69,7 @@ describe('TextEditor', () => { describe('with invalid Editor instance', () => { it('should throw an error when trying to call init without any arguments', (done) => { try { - editor = new TextEditor(null as any); + editor = new InputEditor(null as any); } catch (e) { expect(e.toString()).toContain(`[Slickgrid-Universal] Something is wrong with this grid, an Editor must always have valid arguments.`); done(); @@ -91,23 +91,26 @@ describe('TextEditor', () => { }); it('should initialize the editor', () => { - editor = new TextEditor(editorArguments); + editor = new InputEditor(editorArguments); const editorCount = divContainer.querySelectorAll('input.editor-text.editor-title').length; + expect(editorCount).toBe(1); + expect(editor.inputType).toBe('text'); }); it('should initialize the editor and focus on the element after a small delay', () => { - editor = new TextEditor(editorArguments); + editor = new InputEditor(editorArguments); const editorCount = divContainer.querySelectorAll('input.editor-text.editor-title').length; jest.runAllTimers(); // fast-forward timer expect(editorCount).toBe(1); + expect(editor.inputType).toBe('text'); }); it('should initialize the editor even when user define his own editor options', () => { (mockColumn.internalColumnEditor as ColumnEditor).editorOptions = { minLength: 3 } as AutocompleteOption; - editor = new TextEditor(editorArguments); + editor = new InputEditor(editorArguments); const editorCount = divContainer.querySelectorAll('input.editor-text.editor-title').length; expect(editorCount).toBe(1); @@ -117,7 +120,7 @@ describe('TextEditor', () => { const testValue = 'test placeholder'; (mockColumn.internalColumnEditor as ColumnEditor).placeholder = testValue; - editor = new TextEditor(editorArguments); + editor = new InputEditor(editorArguments); const editorElm = divContainer.querySelector('input.editor-text.editor-title') as HTMLInputElement; expect(editorElm.placeholder).toBe(testValue); @@ -127,7 +130,7 @@ describe('TextEditor', () => { const testValue = 'test title'; (mockColumn.internalColumnEditor as ColumnEditor).title = testValue; - editor = new TextEditor(editorArguments); + editor = new InputEditor(editorArguments); const editorElm = divContainer.querySelector('input.editor-text.editor-title') as HTMLInputElement; expect(editorElm.title).toBe(testValue); @@ -140,20 +143,20 @@ describe('TextEditor', () => { alwaysSaveOnEnterKey: false, }; - editor = new TextEditor(editorArguments); + editor = new InputEditor(editorArguments); expect(editor.columnEditor).toEqual(mockColumn.internalColumnEditor); }); it('should call "setValue" and expect the DOM element value to be the same string when calling "getValue"', () => { - editor = new TextEditor(editorArguments); + editor = new InputEditor(editorArguments); editor.setValue('task 1'); expect(editor.getValue()).toBe('task 1'); }); it('should call "setValue" with value & apply value flag and expect the DOM element to have same value and also expect the value to be applied to the item object', () => { - editor = new TextEditor(editorArguments); + editor = new InputEditor(editorArguments); editor.setValue('task 1', true); expect(editor.getValue()).toBe('task 1'); @@ -161,7 +164,7 @@ describe('TextEditor', () => { }); it('should define an item datacontext containing a string as cell value and expect this value to be loaded in the editor when calling "loadValue"', () => { - editor = new TextEditor(editorArguments); + editor = new InputEditor(editorArguments); editor.loadValue(mockItemData); editor.editorDomElement; @@ -172,7 +175,7 @@ describe('TextEditor', () => { const event = new (window.window as any).KeyboardEvent('keydown', { keyCode: KeyCode.LEFT, bubbles: true, cancelable: true }); const spyEvent = jest.spyOn(event, 'stopImmediatePropagation'); - editor = new TextEditor(editorArguments); + editor = new InputEditor(editorArguments); const editorElm = divContainer.querySelector('input.editor-title') as HTMLInputElement; editor.focus(); @@ -186,7 +189,7 @@ describe('TextEditor', () => { const event = new (window.window as any).KeyboardEvent('keydown', { keyCode: KeyCode.RIGHT, bubbles: true, cancelable: true }); const spyEvent = jest.spyOn(event, 'stopImmediatePropagation'); - editor = new TextEditor(editorArguments); + editor = new InputEditor(editorArguments); const editorElm = divContainer.querySelector('input.editor-title') as HTMLInputElement; editor.focus(); @@ -199,7 +202,7 @@ describe('TextEditor', () => { it('should return True when previously dispatched keyboard event is a new char "a"', () => { const event = new (window.window as any).KeyboardEvent('keydown', { keyCode: KEY_CHAR_A, bubbles: true, cancelable: true }); - editor = new TextEditor(editorArguments); + editor = new InputEditor(editorArguments); editor.setValue('z'); const editorElm = divContainer.querySelector('input.editor-title') as HTMLInputElement; @@ -212,7 +215,7 @@ describe('TextEditor', () => { it('should return False when previously dispatched keyboard event is same string number as current value', () => { const event = new (window.window as any).KeyboardEvent('keydown', { keyCode: KEY_CHAR_A, bubbles: true, cancelable: true }); - editor = new TextEditor(editorArguments); + editor = new InputEditor(editorArguments); const editorElm = divContainer.querySelector('input.editor-title') as HTMLInputElement; editor.loadValue({ id: 1, title: 'a', isActive: true }); @@ -226,7 +229,7 @@ describe('TextEditor', () => { const event = new (window.window as any).KeyboardEvent('keydown', { keyCode: KeyCode.ENTER, bubbles: true, cancelable: true }); (mockColumn.internalColumnEditor as ColumnEditor).alwaysSaveOnEnterKey = true; - editor = new TextEditor(editorArguments); + editor = new InputEditor(editorArguments); const editorElm = divContainer.querySelector('input.editor-title') as HTMLInputElement; editor.focus(); @@ -241,7 +244,7 @@ describe('TextEditor', () => { (mockColumn.internalColumnEditor as ColumnEditor).validator = null as any; mockItemData = { id: 1, title: 'task 1', isActive: true }; - editor = new TextEditor(editorArguments); + editor = new InputEditor(editorArguments); editor.applyValue(mockItemData, 'task 2'); expect(mockItemData).toEqual({ id: 1, title: 'task 2', isActive: true }); @@ -252,7 +255,7 @@ describe('TextEditor', () => { mockColumn.field = 'part.title'; mockItemData = { id: 1, part: { title: 'task 1' }, isActive: true }; - editor = new TextEditor(editorArguments); + editor = new InputEditor(editorArguments); editor.applyValue(mockItemData, 'task 2'); expect(mockItemData).toEqual({ id: 1, part: { title: 'task 2' }, isActive: true }); @@ -267,7 +270,7 @@ describe('TextEditor', () => { }; mockItemData = { id: 1, title: 'task 1', isActive: true }; - editor = new TextEditor(editorArguments); + editor = new InputEditor(editorArguments); editor.applyValue(mockItemData, 'task 2'); expect(mockItemData).toEqual({ id: 1, title: '', isActive: true }); @@ -278,7 +281,7 @@ describe('TextEditor', () => { it('should return serialized value as a string', () => { mockItemData = { id: 1, title: 'task 1', isActive: true }; - editor = new TextEditor(editorArguments); + editor = new InputEditor(editorArguments); editor.loadValue(mockItemData); const output = editor.serializeValue(); @@ -288,7 +291,7 @@ describe('TextEditor', () => { it('should return serialized value as an empty string when item value is also an empty string', () => { mockItemData = { id: 1, title: '', isActive: true }; - editor = new TextEditor(editorArguments); + editor = new InputEditor(editorArguments); editor.loadValue(mockItemData); const output = editor.serializeValue(); @@ -298,7 +301,7 @@ describe('TextEditor', () => { it('should return serialized value as an empty string when item value is null', () => { mockItemData = { id: 1, title: null, isActive: true }; - editor = new TextEditor(editorArguments); + editor = new InputEditor(editorArguments); editor.loadValue(mockItemData); const output = editor.serializeValue(); @@ -309,7 +312,7 @@ describe('TextEditor', () => { mockColumn.field = 'task.title'; mockItemData = { id: 1, task: { title: 'task 1' }, isActive: true }; - editor = new TextEditor(editorArguments); + editor = new InputEditor(editorArguments); editor.loadValue(mockItemData); const output = editor.serializeValue(); @@ -327,7 +330,7 @@ describe('TextEditor', () => { gridOptionMock.autoCommitEdit = true; const spy = jest.spyOn(gridStub.getEditorLock(), 'commitCurrentEdit'); - editor = new TextEditor(editorArguments); + editor = new InputEditor(editorArguments); editor.loadValue(mockItemData); editor.setValue('task 21'); editor.save(); @@ -340,7 +343,7 @@ describe('TextEditor', () => { gridOptionMock.autoCommitEdit = false; const spy = jest.spyOn(editorArguments, 'commitChanges'); - editor = new TextEditor(editorArguments); + editor = new InputEditor(editorArguments); editor.loadValue(mockItemData); editor.setValue('task 21'); editor.save(); @@ -354,7 +357,7 @@ describe('TextEditor', () => { gridOptionMock.autoCommitEdit = true; const spy = jest.spyOn(gridStub.getEditorLock(), 'commitCurrentEdit'); - editor = new TextEditor(editorArguments); + editor = new InputEditor(editorArguments); editor.loadValue(mockItemData); editor.setValue(''); editor.save(); @@ -367,7 +370,7 @@ describe('TextEditor', () => { gridOptionMock.autoCommitEdit = true; const spyCommit = jest.spyOn(gridStub.getEditorLock(), 'commitCurrentEdit'); - editor = new TextEditor(editorArguments); + editor = new InputEditor(editorArguments); editor.loadValue(mockItemData); editor.setValue('task 21'); const spySave = jest.spyOn(editor, 'save'); @@ -384,7 +387,7 @@ describe('TextEditor', () => { describe('validate method', () => { it('should return False when field is required and field is empty', () => { (mockColumn.internalColumnEditor as ColumnEditor).required = true; - editor = new TextEditor(editorArguments); + editor = new InputEditor(editorArguments); const validation = editor.validate(null, ''); expect(validation).toEqual({ valid: false, msg: 'Field is required' }); @@ -392,7 +395,7 @@ describe('TextEditor', () => { it('should return True when field is required and input is a valid input value', () => { (mockColumn.internalColumnEditor as ColumnEditor).required = true; - editor = new TextEditor(editorArguments); + editor = new InputEditor(editorArguments); const validation = editor.validate(null, 'text'); expect(validation).toEqual({ valid: true, msg: '' }); @@ -400,7 +403,7 @@ describe('TextEditor', () => { it('should return False when field is lower than a minLength defined', () => { (mockColumn.internalColumnEditor as ColumnEditor).minLength = 5; - editor = new TextEditor(editorArguments); + editor = new InputEditor(editorArguments); const validation = editor.validate(null, 'text'); expect(validation).toEqual({ valid: false, msg: 'Please make sure your text is at least 5 character(s)' }); @@ -409,7 +412,7 @@ describe('TextEditor', () => { it('should return False when field is lower than a minLength defined using exclusive operator', () => { (mockColumn.internalColumnEditor as ColumnEditor).minLength = 5; (mockColumn.internalColumnEditor as ColumnEditor).operatorConditionalType = 'exclusive'; - editor = new TextEditor(editorArguments); + editor = new InputEditor(editorArguments); const validation = editor.validate(null, 'text'); expect(validation).toEqual({ valid: false, msg: 'Please make sure your text is more than 5 character(s)' }); @@ -417,7 +420,7 @@ describe('TextEditor', () => { it('should return True when field is equal to the minLength defined', () => { (mockColumn.internalColumnEditor as ColumnEditor).minLength = 4; - editor = new TextEditor(editorArguments); + editor = new InputEditor(editorArguments); const validation = editor.validate(null, 'text'); expect(validation).toEqual({ valid: true, msg: '' }); @@ -425,7 +428,7 @@ describe('TextEditor', () => { it('should return False when field is greater than a maxLength defined', () => { (mockColumn.internalColumnEditor as ColumnEditor).maxLength = 10; - editor = new TextEditor(editorArguments); + editor = new InputEditor(editorArguments); const validation = editor.validate(null, 'text is 16 chars'); expect(validation).toEqual({ valid: false, msg: 'Please make sure your text is less than or equal to 10 characters' }); @@ -434,7 +437,7 @@ describe('TextEditor', () => { it('should return False when field is greater than a maxLength defined using exclusive operator', () => { (mockColumn.internalColumnEditor as ColumnEditor).maxLength = 10; (mockColumn.internalColumnEditor as ColumnEditor).operatorConditionalType = 'exclusive'; - editor = new TextEditor(editorArguments); + editor = new InputEditor(editorArguments); const validation = editor.validate(null, 'text is 16 chars'); expect(validation).toEqual({ valid: false, msg: 'Please make sure your text is less than 10 characters' }); @@ -442,7 +445,7 @@ describe('TextEditor', () => { it('should return True when field is equal to the maxLength defined', () => { (mockColumn.internalColumnEditor as ColumnEditor).maxLength = 16; - editor = new TextEditor(editorArguments); + editor = new InputEditor(editorArguments); const validation = editor.validate(null, 'text is 16 chars'); expect(validation).toEqual({ valid: true, msg: '' }); @@ -451,7 +454,7 @@ describe('TextEditor', () => { it('should return True when field is equal to the maxLength defined and "operatorType" is set to "inclusive"', () => { (mockColumn.internalColumnEditor as ColumnEditor).maxLength = 16; (mockColumn.internalColumnEditor as ColumnEditor).operatorConditionalType = 'inclusive'; - editor = new TextEditor(editorArguments); + editor = new InputEditor(editorArguments); const validation = editor.validate(null, 'text is 16 chars'); expect(validation).toEqual({ valid: true, msg: '' }); @@ -460,7 +463,7 @@ describe('TextEditor', () => { it('should return False when field is equal to the maxLength defined but "operatorType" is set to "exclusive"', () => { (mockColumn.internalColumnEditor as ColumnEditor).maxLength = 16; (mockColumn.internalColumnEditor as ColumnEditor).operatorConditionalType = 'exclusive'; - editor = new TextEditor(editorArguments); + editor = new InputEditor(editorArguments); const validation = editor.validate(null, 'text is 16 chars'); expect(validation).toEqual({ valid: false, msg: 'Please make sure your text is less than 16 characters' }); @@ -469,7 +472,7 @@ describe('TextEditor', () => { it('should return False when field is not between minLength & maxLength defined', () => { (mockColumn.internalColumnEditor as ColumnEditor).minLength = 0; (mockColumn.internalColumnEditor as ColumnEditor).maxLength = 10; - editor = new TextEditor(editorArguments); + editor = new InputEditor(editorArguments); const validation = editor.validate(null, 'text is 16 chars'); expect(validation).toEqual({ valid: false, msg: 'Please make sure your text length is between 0 and 10 characters' }); @@ -478,7 +481,7 @@ describe('TextEditor', () => { it('should return True when field is is equal to maxLength defined when both min/max values are defined', () => { (mockColumn.internalColumnEditor as ColumnEditor).minLength = 0; (mockColumn.internalColumnEditor as ColumnEditor).maxLength = 16; - editor = new TextEditor(editorArguments); + editor = new InputEditor(editorArguments); const validation = editor.validate(null, 'text is 16 chars'); expect(validation).toEqual({ valid: true, msg: '' }); @@ -488,7 +491,7 @@ describe('TextEditor', () => { (mockColumn.internalColumnEditor as ColumnEditor).minLength = 4; (mockColumn.internalColumnEditor as ColumnEditor).maxLength = 15; (mockColumn.internalColumnEditor as ColumnEditor).operatorConditionalType = 'inclusive'; - editor = new TextEditor(editorArguments); + editor = new InputEditor(editorArguments); const validation = editor.validate(null, 'text'); expect(validation).toEqual({ valid: true, msg: '' }); @@ -498,7 +501,7 @@ describe('TextEditor', () => { (mockColumn.internalColumnEditor as ColumnEditor).minLength = 4; (mockColumn.internalColumnEditor as ColumnEditor).maxLength = 16; (mockColumn.internalColumnEditor as ColumnEditor).operatorConditionalType = 'exclusive'; - editor = new TextEditor(editorArguments); + editor = new InputEditor(editorArguments); const validation1 = editor.validate(null, 'text is 16 chars'); const validation2 = editor.validate(null, 'text'); @@ -508,7 +511,7 @@ describe('TextEditor', () => { it('should return False when field is greater than a maxValue defined', () => { (mockColumn.internalColumnEditor as ColumnEditor).maxLength = 10; - editor = new TextEditor(editorArguments); + editor = new InputEditor(editorArguments); const validation = editor.validate(null, 'Task is longer than 10 chars'); expect(validation).toEqual({ valid: false, msg: 'Please make sure your text is less than or equal to 10 characters' }); @@ -532,7 +535,7 @@ describe('TextEditor', () => { const activeCellMock = { row: 0, cell: 0 }; jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); - editor = new TextEditor(editorArguments); + editor = new InputEditor(editorArguments); editor.setValue('task 1', true); expect(editor.getValue()).toBe('task 1'); @@ -547,7 +550,7 @@ describe('TextEditor', () => { const getCellSpy = jest.spyOn(gridStub, 'getActiveCell').mockReturnValue(activeCellMock); const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue(undefined); - editor = new TextEditor(editorArguments); + editor = new InputEditor(editorArguments); const disableSpy = jest.spyOn(editor, 'disable'); editor.show(); @@ -562,7 +565,7 @@ describe('TextEditor', () => { const onBeforeEditSpy = jest.spyOn(gridStub.onBeforeEditCell, 'notify').mockReturnValue(false); const onCompositeEditorSpy = jest.spyOn(gridStub.onCompositeEditorChange, 'notify').mockReturnValue(false); - editor = new TextEditor(editorArguments); + editor = new InputEditor(editorArguments); editor.loadValue(mockItemData); const disableSpy = jest.spyOn(editor, 'disable'); editor.show(); @@ -587,7 +590,7 @@ describe('TextEditor', () => { excludeDisabledFieldFormValues: true }; - editor = new TextEditor(editorArguments); + editor = new InputEditor(editorArguments); editor.loadValue(mockItemData); const disableSpy = jest.spyOn(editor, 'disable'); editor.show(); @@ -608,7 +611,7 @@ describe('TextEditor', () => { excludeDisabledFieldFormValues: true }; - editor = new TextEditor(editorArguments); + editor = new InputEditor(editorArguments); editor.loadValue({ ...mockItemData, title: 'task 1' }); editor.show(); editor.disable(); @@ -631,7 +634,7 @@ describe('TextEditor', () => { gridOptionMock.autoCommitEdit = true; mockItemData = { id: 1, title: 'task 2', isActive: true }; - editor = new TextEditor(editorArguments); + editor = new InputEditor(editorArguments); editor.loadValue(mockItemData); editor.editorDomElement.value = 'task 2'; editor.editorDomElement.dispatchEvent(new (window.window as any).Event('input')); diff --git a/packages/common/src/editors/editors.index.ts b/packages/common/src/editors/editors.index.ts index d74c0899d..ded2e879f 100644 --- a/packages/common/src/editors/editors.index.ts +++ b/packages/common/src/editors/editors.index.ts @@ -3,12 +3,12 @@ import { CheckboxEditor } from './checkboxEditor'; import { DateEditor } from './dateEditor'; import { DualInputEditor } from './dualInputEditor'; import { FloatEditor } from './floatEditor'; +import { InputEditor } from './textEditor'; import { IntegerEditor } from './integerEditor'; import { LongTextEditor } from './longTextEditor'; import { MultipleSelectEditor } from './multipleSelectEditor'; import { SingleSelectEditor } from './singleSelectEditor'; import { SliderEditor } from './sliderEditor'; -import { TextEditor } from './textEditor'; export const Editors = { /** AutoComplete Editor (using jQuery UI autocomplete feature) */ @@ -42,5 +42,5 @@ export const Editors = { slider: SliderEditor, /** Text Editor */ - text: TextEditor + text: InputEditor }; diff --git a/packages/common/src/editors/floatEditor.ts b/packages/common/src/editors/floatEditor.ts index 9866bf3cd..477fde5b6 100644 --- a/packages/common/src/editors/floatEditor.ts +++ b/packages/common/src/editors/floatEditor.ts @@ -1,74 +1,18 @@ import { KeyCode } from '../enums/index'; -import { Column, ColumnEditor, CompositeEditorOption, Editor, EditorArguments, EditorValidator, EditorValidationResult, GridOption, SlickGrid, SlickNamespace, } from '../interfaces/index'; -import { getDescendantProperty, setDeepValue } from '../services/utilities'; +import { EditorArguments, EditorValidationResult } from '../interfaces/index'; import { floatValidator } from '../editorValidators/floatValidator'; -import { BindingEventService } from '../services/bindingEvent.service'; +import { InputEditor } from './textEditor'; +import { getDescendantProperty } from '../services/utilities'; const DEFAULT_DECIMAL_PLACES = 0; -// using external non-typed js libraries -declare const Slick: SlickNamespace; - -/* - * An example of a 'detached' editor. - * KeyDown events are also handled to provide handling for Tab, Shift-Tab, Esc and Ctrl-Enter. - */ -export class FloatEditor implements Editor { - protected _bindEventService: BindingEventService; - protected _input!: HTMLInputElement; - protected _isValueTouched = false; - protected _lastInputKeyEvent?: KeyboardEvent; - protected _originalValue?: number | string; - protected _timer?: NodeJS.Timeout; - - /** is the Editor disabled? */ - disabled = false; - - /** SlickGrid Grid object */ - grid: SlickGrid; - - /** Grid options */ - gridOptions: GridOption; - +export class FloatEditor extends InputEditor { constructor(protected readonly args: EditorArguments) { - if (!args) { - throw new Error('[Slickgrid-Universal] Something is wrong with this grid, an Editor must always have valid arguments.'); - } - this.grid = args.grid; - this.gridOptions = args.grid && args.grid.getOptions() as GridOption; - this._bindEventService = new BindingEventService(); - this.init(); - } - - /** Get Column Definition object */ - get columnDef(): Column { - return this.args.column; - } - - /** Get Column Editor object */ - get columnEditor(): ColumnEditor { - return this.columnDef && this.columnDef.internalColumnEditor || {}; - } - - /** Getter for the item data context object */ - get dataContext(): any { - return this.args.item; - } - - /** Getter for the Editor DOM Element */ - get editorDomElement(): any { - return this._input; - } - - get hasAutoCommitEdit() { - return this.grid && this.grid.getOptions && this.grid.getOptions().autoCommitEdit; - } - - /** Get the Validator function, can be passed in Editor property or Column Definition */ - get validator(): EditorValidator | undefined { - return this.columnEditor?.validator ?? this.columnDef?.validator; + super(args); + this.inputType = 'number'; } + /** Initialize the Editor */ init() { if (this.columnDef && this.columnEditor && this.args) { const columnId = this.columnDef?.id ?? ''; @@ -108,51 +52,12 @@ export class FloatEditor implements Editor { } if (compositeEditorOptions) { - this._bindEventService.bind(this._input, 'input', this.handleOnInputChange.bind(this) as EventListener); - this._bindEventService.bind(this._input, 'paste', this.handleOnInputChange.bind(this) as EventListener); + this._bindEventService.bind(this._input, ['input', 'paste'], this.handleOnInputChange.bind(this) as EventListener); this._bindEventService.bind(this._input, 'wheel', this.handleOnMouseWheel.bind(this) as EventListener); } } } - destroy() { - this._bindEventService.unbindAll(); - this._input?.remove?.(); - } - - disable(isDisabled = true) { - const prevIsDisabled = this.disabled; - this.disabled = isDisabled; - - if (this._input) { - if (isDisabled) { - this._input.setAttribute('disabled', 'disabled'); - - // clear value when it's newly disabled and not empty - const currentValue = this.getValue(); - if (prevIsDisabled !== isDisabled && this.args?.compositeEditorOptions && currentValue !== '') { - this.reset('', true, true); - } - } else { - this._input.removeAttribute('disabled'); - } - } - } - - focus(): void { - if (this._input) { - this._input.focus(); - } - } - - show() { - const isCompositeEditor = !!this.args?.compositeEditorOptions; - if (isCompositeEditor) { - // when it's a Composite Editor, we'll check if the Editor is editable (by checking onBeforeEditCell) and if not Editable we'll disable the Editor - this.applyInputUsabilityState(); - } - } - getDecimalPlaces(): number { // returns the number of fixed decimal places or null let rtn = this.columnEditor?.decimal ?? this.columnEditor?.params?.decimalPlaces ?? undefined; @@ -176,59 +81,6 @@ export class FloatEditor implements Editor { return '1'; } - getValue(): string { - return this._input?.value || ''; - } - - setValue(value: number | string, isApplyingValue = false, triggerOnCompositeEditorChange = true) { - if (this._input) { - this._input.value = `${value}`; - - if (isApplyingValue) { - this.applyValue(this.args.item, this.serializeValue()); - - // if it's set by a Composite Editor, then also trigger a change for it - const compositeEditorOptions = this.args.compositeEditorOptions; - if (compositeEditorOptions && triggerOnCompositeEditorChange) { - this.handleChangeOnCompositeEditor(null, compositeEditorOptions, 'system'); - } - } - } - } - - applyValue(item: any, state: any) { - const fieldName = this.columnDef && this.columnDef.field; - if (fieldName !== undefined) { - const isComplexObject = fieldName?.indexOf('.') > 0; // is the field a complex object, "address.streetNumber" - - const validation = this.validate(null, state); - const newValue = (validation && validation.valid) ? state : ''; - - // set the new value to the item datacontext - if (isComplexObject) { - // when it's a complex object, user could override the object path (where the editable object is located) - // else we use the path provided in the Field Column Definition - const objectPath = this.columnEditor?.complexObjectPath ?? fieldName ?? ''; - setDeepValue(item, objectPath, newValue); - } else { - item[fieldName] = newValue; - } - } - } - - isValueChanged(): boolean { - const elmValue = this._input?.value; - const lastKeyEvent = this._lastInputKeyEvent && this._lastInputKeyEvent.keyCode; - if (this.columnEditor && this.columnEditor.alwaysSaveOnEnterKey && lastKeyEvent === KeyCode.ENTER) { - return true; - } - return (!(elmValue === '' && (this._originalValue === null || this._originalValue === undefined))) && (elmValue !== this._originalValue); - } - - isValueTouched(): boolean { - return this._isValueTouched; - } - loadValue(item: any) { const fieldName = this.columnDef && this.columnDef.field; @@ -250,42 +102,10 @@ export class FloatEditor implements Editor { } } - /** - * You can reset or clear the input value, - * when no value is provided it will use the original value to reset (could be useful with Composite Editor Modal with edit/clone) - */ - reset(value?: number | string, triggerCompositeEventWhenExist = true, clearByDisableCommand = false) { - const inputValue = value ?? this._originalValue ?? ''; - if (this._input) { - this._originalValue = inputValue; - this._input.value = `${inputValue}`; - } - this._isValueTouched = false; - - const compositeEditorOptions = this.args.compositeEditorOptions; - if (compositeEditorOptions && triggerCompositeEventWhenExist) { - const shouldDeleteFormValue = !clearByDisableCommand; - this.handleChangeOnCompositeEditor(null, compositeEditorOptions, 'user', shouldDeleteFormValue); - } - } - - save() { - const validation = this.validate(); - const isValid = (validation && validation.valid) || false; - - if (this.hasAutoCommitEdit && isValid) { - // do not use args.commitChanges() as this sets the focus to the next row. - // also the select list will stay shown when clicking off the grid - this.grid.getEditorLock().commitCurrentEdit(); - } else { - this.args.commitChanges(); - } - } - serializeValue() { const elmValue = this._input?.value; if (elmValue === undefined || elmValue === '' || isNaN(+elmValue)) { - return elmValue; + return elmValue as string; } let rtn = parseFloat(elmValue); @@ -325,39 +145,6 @@ export class FloatEditor implements Editor { // protected functions // ------------------ - /** when it's a Composite Editor, we'll check if the Editor is editable (by checking onBeforeEditCell) and if not Editable we'll disable the Editor */ - protected applyInputUsabilityState() { - const activeCell = this.grid.getActiveCell(); - const isCellEditable = this.grid.onBeforeEditCell.notify({ - ...activeCell, item: this.dataContext, column: this.args.column, grid: this.grid, target: 'composite', compositeEditorOptions: this.args.compositeEditorOptions - }); - this.disable(isCellEditable === false); - } - - protected handleChangeOnCompositeEditor(event: Event | null, compositeEditorOptions: CompositeEditorOption, triggeredBy: 'user' | 'system' = 'user', isCalledByClearValue = false) { - const activeCell = this.grid.getActiveCell(); - const column = this.args.column; - const columnId = this.columnDef?.id ?? ''; - const item = this.dataContext; - const grid = this.grid; - const newValue = this.serializeValue(); - - // when valid, we'll also apply the new value to the dataContext item object - if (this.validate().valid) { - this.applyValue(this.dataContext, newValue); - } - this.applyValue(compositeEditorOptions.formValues, newValue); - - const isExcludeDisabledFieldFormValues = this.gridOptions?.compositeEditorOptions?.excludeDisabledFieldFormValues ?? false; - if (isCalledByClearValue || (this.disabled && isExcludeDisabledFieldFormValues && compositeEditorOptions.formValues.hasOwnProperty(columnId))) { - delete compositeEditorOptions.formValues[columnId]; // when the input is disabled we won't include it in the form result object - } - grid.onCompositeEditorChange.notify( - { ...activeCell, item, grid, column, formValues: compositeEditorOptions.formValues, editors: compositeEditorOptions.editors, triggeredBy }, - { ...new Slick.EventData(), ...event } - ); - } - /** When the input value changes (this will cover the input spinner arrows on the right) */ protected handleOnMouseWheel(event: KeyboardEvent) { this._isValueTouched = true; @@ -366,14 +153,4 @@ export class FloatEditor implements Editor { this.handleChangeOnCompositeEditor(event, compositeEditorOptions); } } - - protected handleOnInputChange(event: KeyboardEvent) { - this._isValueTouched = true; - const compositeEditorOptions = this.args.compositeEditorOptions; - if (compositeEditorOptions) { - const typingDelay = this.gridOptions?.editorTypingDebounce ?? 500; - clearTimeout(this._timer as NodeJS.Timeout); - this._timer = setTimeout(() => this.handleChangeOnCompositeEditor(event, compositeEditorOptions), typingDelay); - } - } -} +} \ No newline at end of file diff --git a/packages/common/src/editors/index.ts b/packages/common/src/editors/index.ts index 4c8fca10c..f2a94d41e 100644 --- a/packages/common/src/editors/index.ts +++ b/packages/common/src/editors/index.ts @@ -4,10 +4,10 @@ export * from './dateEditor'; export * from './dualInputEditor'; export * from './editors.index'; export * from './floatEditor'; +export * from './textEditor'; export * from './integerEditor'; export * from './longTextEditor'; export * from './multipleSelectEditor'; export * from './selectEditor'; export * from './singleSelectEditor'; export * from './sliderEditor'; -export * from './textEditor'; diff --git a/packages/common/src/editors/integerEditor.ts b/packages/common/src/editors/integerEditor.ts index cdb7c4034..cfa8481bf 100644 --- a/packages/common/src/editors/integerEditor.ts +++ b/packages/common/src/editors/integerEditor.ts @@ -1,74 +1,18 @@ import { KeyCode } from '../enums/index'; -import { Column, ColumnEditor, CompositeEditorOption, Editor, EditorArguments, EditorValidator, EditorValidationResult, GridOption, SlickGrid, SlickNamespace, } from './../interfaces/index'; -import { getDescendantProperty, setDeepValue } from '../services/utilities'; +import { EditorArguments, EditorValidationResult } from '../interfaces/index'; import { integerValidator } from '../editorValidators/integerValidator'; -import { BindingEventService } from '../services/bindingEvent.service'; - -// using external non-typed js libraries -declare const Slick: SlickNamespace; - -/* - * An example of a 'detached' editor. - * KeyDown events are also handled to provide handling for Tab, Shift-Tab, Esc and Ctrl-Enter. - */ -export class IntegerEditor implements Editor { - protected _bindEventService: BindingEventService; - protected _lastInputKeyEvent?: KeyboardEvent; - protected _isValueTouched = false; - protected _input!: HTMLInputElement | undefined; - protected _originalValue?: number | string; - protected _timer?: NodeJS.Timeout; - - /** is the Editor disabled? */ - disabled = false; - - /** SlickGrid Grid object */ - grid: SlickGrid; - - /** Grid options */ - gridOptions: GridOption; +import { InputEditor } from './textEditor'; +import { getDescendantProperty } from '../services/utilities'; +export class IntegerEditor extends InputEditor { constructor(protected readonly args: EditorArguments) { - if (!args) { - throw new Error('[Slickgrid-Universal] Something is wrong with this grid, an Editor must always have valid arguments.'); - } - this.grid = args.grid; - this.gridOptions = args.grid && args.grid.getOptions() as GridOption; - this._bindEventService = new BindingEventService(); - this.init(); - } - - /** Get Column Definition object */ - get columnDef(): Column { - return this.args.column; - } - - /** Get Column Editor object */ - get columnEditor(): ColumnEditor { - return this.columnDef && this.columnDef.internalColumnEditor || {}; - } - - /** Getter for the item data context object */ - get dataContext(): any { - return this.args.item; - } - - /** Getter for the Editor DOM Element */ - get editorDomElement(): any { - return this._input; - } - - get hasAutoCommitEdit() { - return this.grid && this.grid.getOptions && this.grid.getOptions().autoCommitEdit; - } - - /** Get the Validator function, can be passed in Editor property or Column Definition */ - get validator(): EditorValidator | undefined { - return this.columnEditor?.validator ?? this.columnDef?.validator; + super(args); + this.inputType = 'number'; } + /** Initialize the Editor */ init() { - if (this.columnDef && this.columnEditor) { + if (this.columnDef && this.columnEditor && this.args) { const columnId = this.columnDef?.id ?? ''; const placeholder = this.columnEditor?.placeholder ?? ''; const title = this.columnEditor?.title ?? ''; @@ -83,7 +27,7 @@ export class IntegerEditor implements Editor { this._input.placeholder = placeholder; this._input.title = title; this._input.step = `${inputStep}`; - const cellContainer = this.args?.container; + const cellContainer = this.args.container; if (cellContainer && typeof cellContainer.appendChild === 'function') { cellContainer.appendChild(this._input); } @@ -106,155 +50,33 @@ export class IntegerEditor implements Editor { } if (compositeEditorOptions) { - this._bindEventService.bind(this._input, 'input', this.handleOnInputChange.bind(this) as EventListener); - this._bindEventService.bind(this._input, 'paste', this.handleOnInputChange.bind(this) as EventListener); + this._bindEventService.bind(this._input, ['input', 'paste'], this.handleOnInputChange.bind(this) as EventListener); this._bindEventService.bind(this._input, 'wheel', this.handleOnMouseWheel.bind(this) as EventListener); } } } - destroy() { - this._bindEventService.unbindAll(); - this._input?.remove?.(); - } - - disable(isDisabled = true) { - const prevIsDisabled = this.disabled; - this.disabled = isDisabled; - - if (this._input) { - if (isDisabled) { - this._input.setAttribute('disabled', 'disabled'); - - // clear value when it's newly disabled and not empty - const currentValue = this.getValue(); - if (prevIsDisabled !== isDisabled && this.args?.compositeEditorOptions && currentValue !== '') { - this.reset('', true, true); - } - } else { - this._input.removeAttribute('disabled'); - } - } - } - - focus(): void { - if (this._input) { - this._input.focus(); - } - } - - show() { - const isCompositeEditor = !!this.args?.compositeEditorOptions; - if (isCompositeEditor) { - // when it's a Composite Editor, we'll check if the Editor is editable (by checking onBeforeEditCell) and if not Editable we'll disable the Editor - this.applyInputUsabilityState(); - } - } - - getValue(): string { - return this._input?.value || ''; - } - - setValue(value: number | string, isApplyingValue = false, triggerOnCompositeEditorChange = true) { - if (this._input) { - this._input.value = `${value}`; - - if (isApplyingValue && triggerOnCompositeEditorChange) { - this.applyValue(this.args.item, this.serializeValue()); - - // if it's set by a Composite Editor, then also trigger a change for it - const compositeEditorOptions = this.args.compositeEditorOptions; - if (compositeEditorOptions) { - this.handleChangeOnCompositeEditor(null, compositeEditorOptions, 'system'); - } - } - } - } - - applyValue(item: any, state: any) { - const fieldName = this.columnDef && this.columnDef.field; - if (fieldName !== undefined) { - const isComplexObject = fieldName?.indexOf('.') > 0; // is the field a complex object, "address.streetNumber" - - // validate the value before applying it (if not valid we'll set an empty string) - const validation = this.validate(null, state); - const newValue = (validation && validation.valid) ? state : ''; - - // set the new value to the item datacontext - if (isComplexObject) { - // when it's a complex object, user could override the object path (where the editable object is located) - // else we use the path provided in the Field Column Definition - const objectPath = this.columnEditor?.complexObjectPath ?? fieldName ?? ''; - setDeepValue(item, objectPath, newValue); - } else { - item[fieldName] = newValue; - } - } - } - - isValueChanged(): boolean { - const elmValue = this._input?.value; - const lastKeyEvent = this._lastInputKeyEvent && this._lastInputKeyEvent.keyCode; - if (this.columnEditor && this.columnEditor.alwaysSaveOnEnterKey && lastKeyEvent === KeyCode.ENTER) { - return true; - } - return (!(elmValue === '' && (this._originalValue === null || this._originalValue === undefined))) && (elmValue !== this._originalValue); - } - - isValueTouched(): boolean { - return this._isValueTouched; - } - loadValue(item: any) { const fieldName = this.columnDef && this.columnDef.field; - if (item && fieldName !== undefined && this._input) { - // is the field a complex object, "address.streetNumber" - const isComplexObject = fieldName?.indexOf('.') > 0; - - const value = (isComplexObject) ? getDescendantProperty(item, fieldName) : item[fieldName]; - this._originalValue = (isNaN(value) || value === null || value === undefined) ? value : `${value}`; - this._input.value = `${this._originalValue}`; - this._input.select(); - } - } - - /** - * You can reset or clear the input value, - * when no value is provided it will use the original value to reset (could be useful with Composite Editor Modal with edit/clone) - */ - reset(value?: number | string, triggerCompositeEventWhenExist = true, clearByDisableCommand = false) { - const inputValue = value ?? this._originalValue ?? ''; - if (this._input) { - this._originalValue = inputValue; - this._input.value = `${inputValue}`; - } - this._isValueTouched = false; - - const compositeEditorOptions = this.args.compositeEditorOptions; - if (compositeEditorOptions && triggerCompositeEventWhenExist) { - const shouldDeleteFormValue = !clearByDisableCommand; - this.handleChangeOnCompositeEditor(null, compositeEditorOptions, 'user', shouldDeleteFormValue); - } - } + if (fieldName !== undefined) { - save() { - const validation = this.validate(); - const isValid = (validation && validation.valid) || false; + if (item && fieldName !== undefined && this._input) { + // is the field a complex object, "address.streetNumber" + const isComplexObject = fieldName?.indexOf('.') > 0; - if (this.hasAutoCommitEdit && isValid) { - // do not use args.commitChanges() as this sets the focus to the next row. - // also the select list will stay shown when clicking off the grid - this.grid.getEditorLock().commitCurrentEdit(); - } else { - this.args.commitChanges(); + const value = (isComplexObject) ? getDescendantProperty(item, fieldName) : item[fieldName]; + this._originalValue = (isNaN(value) || value === null || value === undefined) ? value : `${value}`; + this._input.value = `${this._originalValue}`; + this._input.select(); + } } } serializeValue() { const elmValue = this._input?.value; if (elmValue === undefined || elmValue === '' || isNaN(+elmValue)) { - return elmValue; + return elmValue as string; } const output = isNaN(+elmValue) ? elmValue : parseInt(elmValue, 10); return isNaN(+output) ? elmValue : output; @@ -287,39 +109,6 @@ export class IntegerEditor implements Editor { // protected functions // ------------------ - /** when it's a Composite Editor, we'll check if the Editor is editable (by checking onBeforeEditCell) and if not Editable we'll disable the Editor */ - protected applyInputUsabilityState() { - const activeCell = this.grid.getActiveCell(); - const isCellEditable = this.grid.onBeforeEditCell.notify({ - ...activeCell, item: this.dataContext, column: this.args.column, grid: this.grid, target: 'composite', compositeEditorOptions: this.args.compositeEditorOptions - }); - this.disable(isCellEditable === false); - } - - protected handleChangeOnCompositeEditor(event: Event | null, compositeEditorOptions: CompositeEditorOption, triggeredBy: 'user' | 'system' = 'user', isCalledByClearValue = false) { - const activeCell = this.grid.getActiveCell(); - const column = this.args.column; - const columnId = this.columnDef?.id ?? ''; - const item = this.dataContext; - const grid = this.grid; - const newValue = this.serializeValue(); - - // when valid, we'll also apply the new value to the dataContext item object - if (this.validate().valid) { - this.applyValue(this.dataContext, newValue); - } - this.applyValue(compositeEditorOptions.formValues, newValue); - - const isExcludeDisabledFieldFormValues = this.gridOptions?.compositeEditorOptions?.excludeDisabledFieldFormValues ?? false; - if (isCalledByClearValue || (this.disabled && isExcludeDisabledFieldFormValues && compositeEditorOptions.formValues.hasOwnProperty(columnId))) { - delete compositeEditorOptions.formValues[columnId]; // when the input is disabled we won't include it in the form result object - } - grid.onCompositeEditorChange.notify( - { ...activeCell, item, grid, column, formValues: compositeEditorOptions.formValues, editors: compositeEditorOptions.editors, triggeredBy }, - { ...new Slick.EventData(), ...event } - ); - } - /** When the input value changes (this will cover the input spinner arrows on the right) */ protected handleOnMouseWheel(event: KeyboardEvent) { this._isValueTouched = true; @@ -328,14 +117,4 @@ export class IntegerEditor implements Editor { this.handleChangeOnCompositeEditor(event, compositeEditorOptions); } } - - protected handleOnInputChange(event: KeyboardEvent) { - this._isValueTouched = true; - const compositeEditorOptions = this.args.compositeEditorOptions; - if (compositeEditorOptions) { - const typingDelay = this.gridOptions?.editorTypingDebounce ?? 500; - clearTimeout(this._timer as NodeJS.Timeout); - this._timer = setTimeout(() => this.handleChangeOnCompositeEditor(event, compositeEditorOptions), typingDelay); - } - } -} +} \ No newline at end of file diff --git a/packages/common/src/editors/textEditor.ts b/packages/common/src/editors/textEditor.ts index c6fb8a01b..edd5da6a4 100644 --- a/packages/common/src/editors/textEditor.ts +++ b/packages/common/src/editors/textEditor.ts @@ -11,12 +11,13 @@ declare const Slick: SlickNamespace; * An example of a 'detached' editor. * KeyDown events are also handled to provide handling for Tab, Shift-Tab, Esc and Ctrl-Enter. */ -export class TextEditor implements Editor { +export class InputEditor implements Editor { protected _bindEventService: BindingEventService; protected _input!: HTMLInputElement | undefined; + protected _inputType = 'text'; protected _isValueTouched = false; protected _lastInputKeyEvent?: KeyboardEvent; - protected _originalValue?: string; + protected _originalValue?: number | string; protected _timer?: NodeJS.Timeout; /** is the Editor disabled? */ @@ -58,8 +59,18 @@ export class TextEditor implements Editor { return this._input; } - get hasAutoCommitEdit() { - return this.grid.getOptions().autoCommitEdit; + get hasAutoCommitEdit(): boolean { + return this.grid.getOptions().autoCommitEdit ?? false; + } + + /** Getter of input type (text, number, password) */ + get inputType() { + return this._inputType; + } + + /** Setter of input type (text, number, password) */ + set inputType(type: string) { + this._inputType = type; } /** Get the Validator function, can be passed in Editor property or Column Definition */ @@ -75,12 +86,12 @@ export class TextEditor implements Editor { this._input = document.createElement('input') as HTMLInputElement; this._input.className = `editor-text editor-${columnId}`; - this._input.type = 'text'; + this._input.type = this._inputType || 'text'; this._input.setAttribute('role', 'presentation'); this._input.autocomplete = 'off'; this._input.placeholder = placeholder; this._input.title = title; - const cellContainer = this.args?.container; + const cellContainer = this.args.container; if (cellContainer && typeof cellContainer.appendChild === 'function') { cellContainer.appendChild(this._input); } @@ -97,11 +108,14 @@ export class TextEditor implements Editor { // the lib does not get the focus out event for some reason // so register it here if (this.hasAutoCommitEdit && !compositeEditorOptions) { - this._bindEventService.bind(this._input, 'focusout', () => this.save()); + this._bindEventService.bind(this._input, 'focusout', () => { + this._isValueTouched = true; + this.save(); + }); } if (compositeEditorOptions) { - this._bindEventService.bind(this._input, 'input', this.handleOnInputChange.bind(this) as EventListener); + this._bindEventService.bind(this._input, ['input', 'paste'], this.handleOnInputChange.bind(this) as EventListener); } } @@ -147,9 +161,9 @@ export class TextEditor implements Editor { return this._input?.value || ''; } - setValue(value: string, isApplyingValue = false, triggerOnCompositeEditorChange = true) { + setValue(value: number | string, isApplyingValue = false, triggerOnCompositeEditorChange = true) { if (this._input) { - this._input.value = value; + this._input.value = `${value}`; if (isApplyingValue) { this.applyValue(this.args.item, this.serializeValue()); @@ -170,7 +184,7 @@ export class TextEditor implements Editor { // validate the value before applying it (if not valid we'll set an empty string) const validation = this.validate(null, state); - const newValue = (validation && validation.valid) ? state : ''; + const newValue = (validation?.valid) ? state : ''; // set the new value to the item datacontext if (isComplexObject) { @@ -215,11 +229,11 @@ export class TextEditor implements Editor { * You can reset or clear the input value, * when no value is provided it will use the original value to reset (could be useful with Composite Editor Modal with edit/clone) */ - reset(value?: string, triggerCompositeEventWhenExist = true, clearByDisableCommand = false) { + reset(value?: number | string, triggerCompositeEventWhenExist = true, clearByDisableCommand = false) { const inputValue = value ?? this._originalValue ?? ''; if (this._input) { this._originalValue = inputValue; - this._input.value = inputValue; + this._input.value = `${inputValue}`; } this._isValueTouched = false; @@ -243,8 +257,8 @@ export class TextEditor implements Editor { } } - serializeValue() { - return this._input?.value; + serializeValue(): number | string { + return this._input?.value ?? ''; } validate(_targetElm?: any, inputValue?: any): EditorValidationResult {