From c897c7c426c179282766bba3345f4b44317aee44 Mon Sep 17 00:00:00 2001 From: Ghislain Beaulac Date: Mon, 30 Mar 2020 15:41:36 -0400 Subject: [PATCH] feat(editors): add missing Date Editor --- .../src/editors/__tests__/dateEditor.spec.ts | 371 ++++++++++++++++++ packages/common/src/editors/dateEditor.ts | 277 +++++++++++++ packages/common/src/editors/index.ts | 4 +- .../interfaces/flatpickrOption.interface.ts | 162 ++++++++ packages/common/src/interfaces/index.ts | 13 +- 5 files changed, 819 insertions(+), 8 deletions(-) create mode 100644 packages/common/src/editors/__tests__/dateEditor.spec.ts create mode 100644 packages/common/src/editors/dateEditor.ts create mode 100644 packages/common/src/interfaces/flatpickrOption.interface.ts diff --git a/packages/common/src/editors/__tests__/dateEditor.spec.ts b/packages/common/src/editors/__tests__/dateEditor.spec.ts new file mode 100644 index 000000000..7fb8aa89d --- /dev/null +++ b/packages/common/src/editors/__tests__/dateEditor.spec.ts @@ -0,0 +1,371 @@ +import * as moment from 'moment'; + +import { Editors } from '../index'; +import { DateEditor } from '../dateEditor'; +import { FieldType } from '../../enums/index'; +import { Column, EditorArgs, EditorArguments, GridOption } from '../../interfaces/index'; +import { TranslateServiceStub } from '../../../../../test/translateServiceStub'; + +const containerId = 'demo-container'; + +// define a
container to simulate the grid container +const template = `
`; + +const dataViewStub = { + refresh: jest.fn(), +}; + +const gridOptionMock = { + autoCommitEdit: false, + editable: true, + i18n: null, +} as GridOption; + +const getEditorLockMock = { + commitCurrentEdit: jest.fn(), +}; + +const gridStub = { + getOptions: () => gridOptionMock, + getColumns: jest.fn(), + getEditorLock: () => getEditorLockMock, + getHeaderRowColumn: jest.fn(), + navigateNext: jest.fn(), + navigatePrev: jest.fn(), + render: jest.fn(), +}; + +describe('DateEditor', () => { + let translateService: TranslateServiceStub; + let divContainer: HTMLDivElement; + let editor: DateEditor; + let editorArguments: EditorArguments; + let mockColumn: Column; + let mockItemData: any; + + beforeEach(() => { + translateService = new TranslateServiceStub(); + + divContainer = document.createElement('div'); + divContainer.innerHTML = template; + document.body.appendChild(divContainer); + + mockColumn = { id: 'startDate', field: 'startDate', editable: true, editor: { model: Editors.date }, internalColumnEditor: {} } as Column; + + editorArguments = { + grid: gridStub, + column: mockColumn, + item: mockItemData, + event: null, + cancelChanges: jest.fn(), + commitChanges: jest.fn(), + container: divContainer, + columnMetaData: null, + dataView: dataViewStub, + gridPosition: { top: 0, left: 0, bottom: 10, right: 10, height: 100, width: 100, visible: true }, + position: { top: 0, left: 0, bottom: 10, right: 10, height: 100, width: 100, visible: true }, + }; + }); + + describe('with invalid Editor instance', () => { + it('should throw an error when trying to call init without any arguments', (done) => { + try { + editor = new DateEditor(null); + } catch (e) { + expect(e.toString()).toContain(`[Slickgrid-Universal] Something is wrong with this grid, an Editor must always have valid arguments.`); + done(); + } + }); + }); + + describe('with valid Editor instance', () => { + beforeEach(() => { + mockItemData = { id: 1, startDate: '2001-01-02T11:02:02.000Z', isActive: true }; + mockColumn = { id: 'startDate', field: 'startDate', editable: true, editor: { model: Editors.date }, internalColumnEditor: {} } as Column; + + editorArguments.column = mockColumn; + editorArguments.item = mockItemData; + }); + + afterEach(() => { + editor.destroy(); + }); + + it('should initialize the editor', () => { + gridOptionMock.i18n = translateService; + editor = new DateEditor(editorArguments); + const editorCount = divContainer.querySelectorAll('input.editor-text.editor-startDate').length; + expect(editorCount).toBe(1); + }); + + it('should have a placeholder when defined in its column definition', () => { + const testValue = 'test placeholder'; + mockColumn.internalColumnEditor.placeholder = testValue; + + editor = new DateEditor(editorArguments); + const editorElm = divContainer.querySelector('input.editor-text.editor-startDate'); + + expect(editorElm.placeholder).toBe(testValue); + }); + + it('should have a title (tooltip) when defined in its column definition', () => { + const testValue = 'test title'; + mockColumn.internalColumnEditor.title = testValue; + + editor = new DateEditor(editorArguments); + const editorElm = divContainer.querySelector('input.editor-text.editor-startDate'); + + expect(editorElm.title).toBe(testValue); + }); + + it('should call "columnEditor" GETTER and expect to equal the editor settings we provided', () => { + mockColumn.internalColumnEditor = { + placeholder: 'test placeholder', + title: 'test title', + }; + + editor = new DateEditor(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 DateEditor(editorArguments); + editor.setValue('2001-01-02T11:02:02.000Z'); + + expect(editor.getValue()).toBe('2001-01-02T11:02:02.000Z'); + }); + + 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"', () => { + mockItemData = { id: 1, startDate: '2001-01-02T11:02:02.000Z', isActive: true }; + editor = new DateEditor(editorArguments); + editor.loadValue(mockItemData); + const editorElm = editor.editorDomElement; + + expect(editor.getValue()).toBe('2001-01-02T11:02:02.000Z'); + expect(editorElm[0].defaultValue).toBe('2001-01-02T11:02:02.000Z'); + }); + + it('should hide the DOM element when the "hide" method is called', () => { + editor = new DateEditor(editorArguments); + const spy = jest.spyOn(editor.flatInstance, 'close'); + const calendarElm = document.body.querySelector('.flatpickr-calendar'); + editor.hide(); + + expect(calendarElm).toBeTruthy(); + expect(spy).toHaveBeenCalled(); + }); + + it('should show the DOM element when the "show" method is called', () => { + editor = new DateEditor(editorArguments); + const spy = jest.spyOn(editor.flatInstance, 'open'); + const calendarElm = document.body.querySelector('.flatpickr-calendar'); + editor.show(); + editor.focus(); + + expect(calendarElm).toBeTruthy(); + expect(spy).toHaveBeenCalled(); + }); + + describe('isValueChanged method', () => { + it('should return True when date is changed in the picker', () => { + // change to allow input value only for testing purposes & use the regular flatpickr input to test that one too + mockColumn.internalColumnEditor.editorOptions = { allowInput: true, altInput: false }; + mockItemData = { id: 1, startDate: '2001-01-02T11:02:02.000Z', isActive: true }; + + editor = new DateEditor(editorArguments); + editor.loadValue(mockItemData); + editor.focus(); + const editorInputElm = divContainer.querySelector('input.flatpickr'); + editorInputElm.value = '2024-04-02T16:02:02.239Z'; + editorInputElm.dispatchEvent(new (window.window as any).KeyboardEvent('keydown', { keyCode: 13, bubbles: true, cancelable: true })); + + expect(editor.isValueChanged()).toBe(true); + }); + + it('should return False when date in the picker is the same as the current date', () => { + mockItemData = { id: 1, startDate: '2001-01-02T11:02:02.000Z', isActive: true }; + mockColumn.internalColumnEditor.editorOptions = { allowInput: true }; // change to allow input value only for testing purposes + + editor = new DateEditor(editorArguments); + editor.loadValue(mockItemData); + const editorInputElm = divContainer.querySelector('input.flatpickr-alt-input'); + editorInputElm.value = '2001-01-02T11:02:02.000Z'; + editorInputElm.dispatchEvent(new (window.window as any).KeyboardEvent('keydown', { keyCode: 13, bubbles: true, cancelable: true })); + + expect(editor.isValueChanged()).toBe(false); + }); + }); + + describe('applyValue method', () => { + it('should apply the value to the startDate property when it passes validation', () => { + mockColumn.internalColumnEditor.validator = null; + mockColumn.type = FieldType.dateTimeIsoAmPm; + mockItemData = { id: 1, startDate: '2001-04-05T11:33:42.000Z', isActive: true }; + + const newDate = '2001-01-02T16:02:02.000+05:00'; + editor = new DateEditor(editorArguments); + editor.applyValue(mockItemData, newDate); + + expect(mockItemData).toEqual({ id: 1, startDate: moment(newDate, 'YYYY-MM-DD hh:mm:ss a').toDate(), isActive: true }); + }); + + it('should apply the value to the startDate property with a field having dot notation (complex object) that passes validation', () => { + mockColumn.internalColumnEditor.validator = null; + mockColumn.type = FieldType.dateTimeIsoAmPm; + mockColumn.field = 'employee.startDate'; + mockItemData = { id: 1, employee: { startDate: '2001-04-05T11:33:42.000Z' }, isActive: true }; + + const newDate = '2001-01-02T16:02:02.000+05:00'; + editor = new DateEditor(editorArguments); + editor.applyValue(mockItemData, newDate); + + expect(mockItemData).toEqual({ id: 1, employee: { startDate: moment(newDate, 'YYYY-MM-DD hh:mm:ss a').toDate() }, isActive: true }); + }); + + it('should return item data with an empty string in its value when it fails the custom validation', () => { + mockColumn.internalColumnEditor.validator = (value: any, args: EditorArgs) => { + if (value.length > 10) { + return { valid: false, msg: 'Must be at least 10 chars long.' }; + } + return { valid: true, msg: '' }; + }; + mockItemData = { id: 1, startDate: '2001-04-05T11:33:42.000Z', isActive: true }; + + editor = new DateEditor(editorArguments); + editor.applyValue(mockItemData, '2001-01-02T16:02:02.000+05:00'); + + expect(mockItemData).toEqual({ id: 1, startDate: '', isActive: true }); + }); + }); + + describe('serializeValue method', () => { + it('should return serialized value as a date string', () => { + mockColumn.type = FieldType.dateIso; + mockItemData = { id: 1, startDate: '2001-01-02T16:02:02.000+05:00', isActive: true }; + + editor = new DateEditor(editorArguments); + editor.loadValue(mockItemData); + const output = editor.serializeValue(); + + expect(output).toBe('2001-01-02'); + }); + + it('should return serialized value as an empty string when item value is also an empty string', () => { + mockItemData = { id: 1, startDate: '', isActive: true }; + + editor = new DateEditor(editorArguments); + editor.loadValue(mockItemData); + const output = editor.serializeValue(); + + expect(output).toBe(''); + }); + + it('should return serialized value as an empty string when item value is null', () => { + mockItemData = { id: 1, startDate: null, isActive: true }; + + editor = new DateEditor(editorArguments); + editor.loadValue(mockItemData); + const output = editor.serializeValue(); + + expect(output).toBe(''); + }); + + it('should return serialized value as a date string when using a dot (.) notation for complex object', () => { + mockColumn.type = FieldType.dateIso; + mockColumn.field = 'employee.startDate'; + mockItemData = { id: 1, employee: { startDate: '2001-01-02T16:02:02.000+05:00' }, isActive: true }; + + editor = new DateEditor(editorArguments); + editor.loadValue(mockItemData); + const output = editor.serializeValue(); + + expect(output).toBe('2001-01-02'); + }); + }); + + describe('save method', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should call "getEditorLock" method when "hasAutoCommitEdit" is enabled', () => { + mockItemData = { id: 1, startDate: '2001-01-02T16:02:02.000+05:00', isActive: true }; + gridOptionMock.autoCommitEdit = true; + const spy = jest.spyOn(gridStub.getEditorLock(), 'commitCurrentEdit'); + + editor = new DateEditor(editorArguments); + editor.loadValue(mockItemData); + editor.setValue('2022-03-02T16:02:02.000+05:00'); + editor.save(); + + expect(spy).toHaveBeenCalled(); + }); + + it('should call "commitChanges" method when "hasAutoCommitEdit" is disabled', () => { + mockItemData = { id: 1, startDate: '2001-01-02T16:02:02.000+05:00', isActive: true }; + gridOptionMock.autoCommitEdit = false; + const spy = jest.spyOn(editorArguments, 'commitChanges'); + + editor = new DateEditor(editorArguments); + editor.loadValue(mockItemData); + editor.setValue('2022-03-02T16:02:02.000+05:00'); + editor.save(); + + expect(spy).toHaveBeenCalled(); + }); + + it('should not call anything when the input value is empty but is required', () => { + mockItemData = { id: 1, startDate: '', isActive: true }; + mockColumn.internalColumnEditor.required = true; + gridOptionMock.autoCommitEdit = true; + const spy = jest.spyOn(gridStub.getEditorLock(), 'commitCurrentEdit'); + + editor = new DateEditor(editorArguments); + editor.loadValue(mockItemData); + editor.save(); + + expect(spy).not.toHaveBeenCalled(); + }); + }); + + describe('validate method', () => { + it('should return False when field is required and field is empty', () => { + mockColumn.internalColumnEditor.required = true; + editor = new DateEditor(editorArguments); + const validation = editor.validate(''); + + expect(validation).toEqual({ valid: false, msg: 'Field is required' }); + }); + + it('should return True when field is required and input is a valid input value', () => { + mockColumn.internalColumnEditor.required = true; + editor = new DateEditor(editorArguments); + const validation = editor.validate('text'); + + expect(validation).toEqual({ valid: true, msg: null }); + }); + }); + + describe('with different locale', () => { + it('should display text in new locale', (done) => { + gridOptionMock.i18n = translateService; + + translateService.setLocale('fr-CA'); // will be trimmed to "fr" + editor = new DateEditor(editorArguments); + + const spy = jest.spyOn(editor.flatInstance, 'open'); + const calendarElm = document.body.querySelector('.flatpickr-calendar'); + const selectonOptionElms = calendarElm.querySelectorAll(' .flatpickr-monthDropdown-months option'); + + editor.show(); + + expect(calendarElm).toBeTruthy(); + expect(selectonOptionElms.length).toBe(12); + expect(selectonOptionElms[0].textContent).toBe('janvier'); + expect(spy).toHaveBeenCalled(); + done(); + }); + }); + }); +}); diff --git a/packages/common/src/editors/dateEditor.ts b/packages/common/src/editors/dateEditor.ts new file mode 100644 index 000000000..eca729771 --- /dev/null +++ b/packages/common/src/editors/dateEditor.ts @@ -0,0 +1,277 @@ +import * as flatpickr from 'flatpickr'; +import * as moment_ from 'moment-mini'; +const moment = moment_; // patch to fix rollup "moment has no default export" issue, document here https://github.com/rollup/rollup/issues/670 + +import { Constants } from './../constants'; +import { FieldType } from '../enums/index'; +import { + Column, + ColumnEditor, + Editor, + EditorArguments, + EditorValidator, + EditorValidatorOutput, + FlatpickrOption, + GridOption, +} from './../interfaces/index'; +import { mapFlatpickrDateFormatWithFieldType, mapMomentDateFormatWithFieldType, setDeepValue, getDescendantProperty } from './../services/utilities'; +import { TranslaterService } from '../services/translater.service'; + +// using external non-typed js libraries +declare const $: any; + +declare function require(name: string): any; +require('flatpickr'); + +/* + * An example of a date picker editor using Flatpickr + * https://chmln.github.io/flatpickr + */ +export class DateEditor implements Editor { + private _$inputWithData: any; + private _$input: any; + + flatInstance: any; + defaultDate: string; + originalDate: string; + + /** SlickGrid Grid object */ + grid: any; + + /** Grid options */ + gridOptions: GridOption; + + /** The translate library */ + protected _translaterService: TranslaterService; + + constructor(private 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 = (this.grid.getOptions() || {}) as GridOption; + if (this.gridOptions && this.gridOptions.i18n) { + this._translaterService = this.gridOptions.i18n; + } + this.init(); + } + + /** Get Column Definition object */ + get columnDef(): Column | undefined { + return this.args && this.args.column; + } + + /** Get Column Editor object */ + get columnEditor(): ColumnEditor { + return this.columnDef && this.columnDef.internalColumnEditor || {}; + } + + /** Get the Editor DOM Element */ + get editorDomElement(): any { + return this._$input; + } + + /** Get Flatpickr options passed to the editor by the user */ + get editorOptions(): any { + return this.columnEditor.editorOptions || {}; + } + + get hasAutoCommitEdit() { + return this.grid.getOptions().autoCommitEdit; + } + + /** Get the Validator function, can be passed in Editor property or Column Definition */ + get validator(): EditorValidator | undefined { + return (this.columnEditor && this.columnEditor.validator) || (this.columnDef && this.columnDef.validator); + } + + init(): void { + if (this.args && this.columnDef) { + const columnId = this.columnDef && this.columnDef.id; + const placeholder = this.columnEditor && this.columnEditor.placeholder || ''; + const title = this.columnEditor && this.columnEditor.title || ''; + const gridOptions = (this.args.grid.getOptions() || {}) as GridOption; + this.defaultDate = (this.args.item) ? this.args.item[this.columnDef.field] : null; + const inputFormat = mapFlatpickrDateFormatWithFieldType(this.columnDef.type || FieldType.dateIso); + const outputFormat = mapFlatpickrDateFormatWithFieldType(this.columnDef.outputType || FieldType.dateUtc); + let currentLocale = this._translaterService && this._translaterService.getCurrentLocale && this._translaterService.getCurrentLocale() || gridOptions.locale || 'en'; + if (currentLocale.length > 2) { + currentLocale = currentLocale.substring(0, 2); + } + + const pickerOptions: FlatpickrOption = { + defaultDate: this.defaultDate as string, + altInput: true, + altFormat: inputFormat, + dateFormat: outputFormat, + closeOnSelect: false, + locale: (currentLocale !== 'en') ? this.loadFlatpickrLocale(currentLocale) : 'en', + onChange: (selectedDates: Date[] | Date, dateStr: string, instance: any) => { + this.save(); + }, + }; + + // merge options with optional user's custom options + const pickerMergedOptions: FlatpickrOption = { ...pickerOptions, ...(this.editorOptions as FlatpickrOption) }; + const inputCssClasses = `.editor-text.editor-${columnId}.flatpickr`; + if (pickerMergedOptions.altInput) { + pickerMergedOptions.altInputClass = 'flatpickr-alt-input editor-text'; + } + + this._$input = $(``); + this._$input.appendTo(this.args.container); + this.flatInstance = (flatpickr && this._$input[0] && typeof this._$input[0].flatpickr === 'function') ? this._$input[0].flatpickr(pickerMergedOptions) : null; + + // when we're using an alternate input to display data, we'll consider this input as the one to do the focus later on + // else just use the top one + this._$inputWithData = (pickerMergedOptions && pickerMergedOptions.altInput) ? $(`${inputCssClasses}.flatpickr-alt-input`) : this._$input; + } + } + + destroy() { + this.hide(); + this._$input.remove(); + if (this._$inputWithData && typeof this._$inputWithData.remove === 'function') { + this._$inputWithData.remove(); + } + if (this.flatInstance && typeof this.flatInstance.destroy === 'function') { + this.flatInstance.destroy(); + } + } + + focus() { + this._$input.focus(); + if (this._$inputWithData && typeof this._$inputWithData.focus === 'function') { + this._$inputWithData.focus().select(); + } + } + + hide() { + if (this.flatInstance && typeof this.flatInstance.close === 'function') { + this.flatInstance.close(); + } + } + + show() { + if (this.flatInstance && typeof this.flatInstance.open === 'function') { + this.flatInstance.open(); + } + } + + getValue(): string { + return this._$input.val(); + } + + setValue(val: string) { + this.flatInstance.setDate(val); + } + + applyValue(item: any, state: any) { + const fieldName = this.columnDef && this.columnDef.field; + if (fieldName !== undefined) { + const outputFormat = mapMomentDateFormatWithFieldType(this.columnDef && this.columnDef.type || FieldType.dateIso); + 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(state); + const newValue = (validation && validation.valid) ? moment(state, outputFormat).toDate() : ''; + + // set the new value to the item datacontext + if (isComplexObject) { + setDeepValue(item, fieldName, newValue); + } else { + item[fieldName] = newValue; + } + } + } + + isValueChanged(): boolean { + const elmValue = this._$input.val(); + const outputFormat = mapMomentDateFormatWithFieldType(this.columnDef && this.columnDef.type || FieldType.dateIso); + const elmDateStr = elmValue ? moment(elmValue).format(outputFormat) : ''; + const orgDateStr = this.originalDate ? moment(this.originalDate).format(outputFormat) : ''; + + return (!(elmDateStr === '' && orgDateStr === '')) && (elmDateStr !== orgDateStr); + } + + loadValue(item: any) { + const fieldName = this.columnDef && this.columnDef.field; + + if (fieldName !== undefined) { + // is the field a complex object, "address.streetNumber" + const isComplexObject = fieldName.indexOf('.') > 0; + + if (item && this.columnDef && (item.hasOwnProperty(fieldName) || isComplexObject)) { + const value = (isComplexObject) ? getDescendantProperty(item, fieldName) : item[fieldName]; + this.originalDate = value; + this.flatInstance.setDate(value); + this.show(); + this.focus(); + } + } + } + + save() { + // autocommit will not focus the next editor + const validation = this.validate(); + if (validation && validation.valid && this.isValueChanged()) { + if (this.hasAutoCommitEdit) { + this.grid.getEditorLock().commitCurrentEdit(); + } else { + this.args.commitChanges(); + } + } + } + + serializeValue() { + const domValue: string = this._$input.val(); + + if (!domValue) { + return ''; + } + + const outputFormat = mapMomentDateFormatWithFieldType((this.columnDef && this.columnDef.type) || FieldType.dateIso); + const value = moment(domValue).format(outputFormat); + + return value; + } + + validate(inputValue?: any): EditorValidatorOutput { + const isRequired = this.columnEditor.required; + const elmValue = (inputValue !== undefined) ? inputValue : this._$input && this._$input.val && this._$input.val(); + const errorMsg = this.columnEditor.errorMessage; + + if (this.validator) { + return this.validator(elmValue, this.args); + } + + // by default the editor is almost always valid (except when it's required but not provided) + if (isRequired && elmValue === '') { + return { + valid: false, + msg: errorMsg || Constants.VALIDATION_REQUIRED_FIELD + }; + } + + return { + valid: true, + msg: null + }; + } + + // + // private functions + // ------------------ + + /** Load a different set of locales for Flatpickr to be localized */ + private loadFlatpickrLocale(language: string) { + let locales = 'en'; + + if (language !== 'en') { + // change locale if needed, Flatpickr reference: https://chmln.github.io/flatpickr/localization/ + const localeDefault: any = require(`flatpickr/dist/l10n/${language}.js`).default; + locales = (localeDefault && localeDefault[language]) ? localeDefault[language] : 'en'; + } + return locales; + } +} diff --git a/packages/common/src/editors/index.ts b/packages/common/src/editors/index.ts index 952182127..790f3c3b8 100644 --- a/packages/common/src/editors/index.ts +++ b/packages/common/src/editors/index.ts @@ -1,6 +1,6 @@ import { AutoCompleteEditor } from './autoCompleteEditor'; import { CheckboxEditor } from './checkboxEditor'; -// import { DateEditor } from './dateEditor'; +import { DateEditor } from './dateEditor'; import { FloatEditor } from './floatEditor'; import { IntegerEditor } from './integerEditor'; import { LongTextEditor } from './longTextEditor'; @@ -17,7 +17,7 @@ export const Editors = { checkbox: CheckboxEditor, /** Date Picker Editor (which uses 3rd party lib "flatpickr") */ - // date: DateEditor, + date: DateEditor, /** Float Number Editor */ float: FloatEditor, diff --git a/packages/common/src/interfaces/flatpickrOption.interface.ts b/packages/common/src/interfaces/flatpickrOption.interface.ts new file mode 100644 index 000000000..d1c24ae66 --- /dev/null +++ b/packages/common/src/interfaces/flatpickrOption.interface.ts @@ -0,0 +1,162 @@ +import { Locale } from 'flatpickr/dist/types/locale'; + +export interface FlatpickrOption { + /** defaults to "F j, Y", exactly the same as date format, but for the altInput field */ + altFormat?: string; + + /** default to false, show the user a readable date (as per altFormat), but return something totally different to the server. */ + altInput?: boolean; + + /** defaults to false, allows the user to enter a date directly input the input field. By default, direct entry is disabled. */ + allowInput?: boolean; + + /** This class will be added to the input element created by the altInput option. Note that altInput already inherits classes from the original input. */ + altInputClass?: string; + + /** Instead of body, appends the calendar to the specified node instead*. */ + appendTo?: HTMLElement; + + /** defaults to "F j, Y", defines how the date will be formatted in the aria-label for calendar days, using the same tokens as dateFormat. If you change this, you should choose a value that will make sense if a screen reader reads it out loud. */ + ariaDateFormat?: string; + + /** defaults to true, whether clicking on the input should open the picker. You could disable this if you wish to open the calendar manually with.open() */ + clickOpens?: boolean; + + /** defaults to false, closes the date picker after selecting a date */ + closeOnSelect?: boolean; + + /** defaults to "Y-m-d", a string of characters which are used to define how the date will be displayed in the input box. The supported characters are defined in the table below. */ + dateFormat?: string; + + /** + * Sets the initial selected date(s). + * If you're using mode?: "multiple" or a range calendar supply an Array of Date objects or an Array of date strings which follow your dateFormat. + * Otherwise, you can supply a single Date object or a date string. + */ + defaultDate?: string | string[]; + + /** defaults to 12, initial value of the hour element. */ + defaultHour?: number; + + /** defaults to 0, initial value of the minute element. */ + defaultMinute?: number; + + /** defaults to 0, initial value of the seconds element. */ + defaultSeconds?: number; + + /** See Disabling dates */ + disable?: any[]; + + /** + * defaults to false. + * Set disableMobile to true to always use the non-native picker. + * By default, flatpickr utilizes native datetime widgets unless certain options (e.g. disable) are used. + */ + disableMobile?: boolean; + + /** See Enabling dates */ + enable?: any[]; + + /** defaults to false, enables seconds in the time picker. */ + enableSeconds?: boolean; + + /** defaults to false, enables time picker */ + enableTime?: boolean; + + /** Allows using a custom date formatting function instead of the built-in handling for date formats using dateFormat, altFormat, etc. */ + formatDate?: (dateObj: Date, format: string, locale: Locale) => string; + + /** defaults to 1, adjusts the step for the hour input (incl. scrolling) */ + hourIncrement?: number; + + /** defaults to false, displays the calendar inline */ + inline?: boolean; + + /** provide a custom set of locales */ + locale?: any; + + /** The maximum date that a user can pick to (inclusive). */ + maxDate?: string | Date; + + /** The minimum date that a user can start picking from (inclusive). */ + minDate?: string | Date; + + /** defaults to 5, adjusts the step for the minute input (incl. scrolling) */ + minuteIncrement?: number; + + /** defaults to "dropdown", the selector type to change the month */ + monthSelectorType?: 'dropdown' | 'static'; + + /** defaults to single, what mode to use the picker */ + mode?: 'single' | 'multiple' | 'range'; + + /** defaults to ">", HTML for the arrow icon, used to switch months. */ + nextArrow?: string; + + /** defaults to false, Hides the day selection in calendar. Use it along with enableTime to create a time picker. */ + noCalendar?: boolean; + + /** defaults to false, function that expects a date string and must return a Date object */ + parseDate?: (date: Date, format: string) => void; + + /** Provide external flatpickr plugin(s) */ + plugins?: any | any[]; + + /** Where the calendar is rendered relative to the input. */ + position?: 'auto' | 'above' | 'below'; + + /** defaults to "<"", HTML for the left arrow icon. */ + prevArrow?: string; + + /** defaults to false, show the month using the shorthand version (ie, Sep instead of September). */ + shorthandCurrentMonth?: boolean; + + /** defaults to 1, number of months to show */ + showMonths?: number; + + /** defaults to false, position the calendar inside the wrapper and next to the input element*. */ + static?: boolean; + + /** defaults to false, displays time picker in 24 hour mode without AM/PM selection when enabled. */ + time_24hr?: boolean; + + /** defaults to false, enables display of week numbers in calendar. */ + weekNumbers?: boolean; + + /** defaults to false, custom elements and input groups */ + wrap?: boolean; + + // -- + // Events + // ----------------- + + /** Function(s) to trigger on every date selection. See Events API */ + onChange?: (selectedDates: Date[] | Date, dateStr: string, instance: any) => void; + + /** Function(s) to trigger on every time the calendar is closed. See Events API */ + onClose?: (selectedDates: Date[] | Date, dateStr: string, instance: any) => void; + + /** Function(s) to trigger on every time the calendar gets created. See Events API */ + onDayCreate?: (date: Date | Date[]) => void; + + /** Function(s) to trigger when the date picker gets drestroyed. See Events API */ + onDestroy?: (day: Date) => void; + + /** Function(s) to trigger when the date picker gets drestroyed. See Events API */ + onKeyDown?: (selectedDates: Date[] | Date, dateStr: string, instance: any) => void; + + /** Function(s) to trigger on every time the month changes. See Events API */ + onMonthChange?: (selectedDates: Date[] | Date, dateStr: string, instance: any) => void; + + /** Function(s) to trigger on every time the calendar is opened. See Events API */ + onOpen?: (selectedDates: Date[] | Date, dateStr: string, instance: any) => void; + + /** Function to trigger when the calendar is ready. See Events API */ + onReady?: () => void; + + /** Function(s) to trigger on every time the year changes. See Events API */ + onValueUpdate?: (selectedDates: Date[] | Date, dateStr: string, instance: any) => void; + + /** Function(s) to trigger on every time the year changes. See Events API */ + onYearChange?: (selectedDates: Date[] | Date, dateStr: string, instance: any) => void; +} diff --git a/packages/common/src/interfaces/index.ts b/packages/common/src/interfaces/index.ts index a87e9a96e..f8c14416d 100644 --- a/packages/common/src/interfaces/index.ts +++ b/packages/common/src/interfaces/index.ts @@ -4,18 +4,18 @@ export * from './autoResizeOption.interface'; export * from './backendService.interface'; export * from './backendServiceApi.interface'; export * from './backendServiceOption.interface'; +export * from './cellArgs.interface'; export * from './cellMenu.interface'; export * from './checkboxSelector.interface'; export * from './collectionCustomStructure.interface'; -export * from './cellArgs.interface'; export * from './collectionFilterBy.interface'; +export * from './collectionOption.interface'; export * from './collectionSortBy.interface'; export * from './column.interface'; export * from './columnEditor.interface'; -export * from './collectionOption.interface'; export * from './columnFilter.interface'; -export * from './columnPicker.interface'; export * from './columnFilters.interface'; +export * from './columnPicker.interface'; export * from './columnSort.interface'; export * from './contextMenu.interface'; export * from './currentColumn.interface'; @@ -27,8 +27,8 @@ export * from './draggableGrouping.interface'; export * from './editCommand.interface'; export * from './editor.interface'; export * from './editorArgs.interface'; -export * from './editorValidator.interface'; export * from './editorArguments.interface'; +export * from './editorValidator.interface'; export * from './editorValidatorOutput.interface'; export * from './editUndoRedoBuffer.interface'; export * from './elementPosition.interface'; @@ -36,18 +36,19 @@ export * from './excelCellFormat.interface'; export * from './excelCopyBufferOption.interface'; export * from './excelExportOption.interface'; export * from './excelMetadata.interface'; +export * from './excelStylesheet.interface'; export * from './excelWorkbook.interface'; export * from './excelWorksheet.interface'; export * from './exportOption.interface'; -export * from './excelStylesheet.interface'; export * from './extension.interface'; export * from './extensionModel.interface'; export * from './filter.interface'; export * from './filterArguments.interface'; export * from './filterCallback.interface'; export * from './filterChangedArgs.interface'; -export * from './filterConditionOption.interface'; export * from './filterCondition.interface'; +export * from './filterConditionOption.interface'; +export * from './flatpickrOption.interface'; export * from './formatter.interface'; export * from './formatterOption.interface'; export * from './formatterResultObject.interface';