From ad5106e869c17479ca3f71be380e87faa6f05f05 Mon Sep 17 00:00:00 2001 From: Arathy Kumar Date: Mon, 25 Sep 2023 13:34:19 +0530 Subject: [PATCH 01/15] feat: add pf-date-picker --- elements/package.json | 7 ++++++ elements/pf-date-picker/README.md | 11 ++++++++ elements/pf-date-picker/date-picker-helper.ts | 25 +++++++++++++++++++ elements/pf-date-picker/demo/demo.css | 3 +++ .../pf-date-picker/demo/pf-date-picker.html | 4 +++ .../pf-date-picker/demo/pf-date-picker.js | 1 + .../pf-date-picker/docs/pf-date-picker.md | 17 +++++++++++++ elements/pf-date-picker/pf-calendar.css | 3 +++ elements/pf-date-picker/pf-calendar.ts | 25 +++++++++++++++++++ elements/pf-date-picker/pf-date-picker.css | 3 +++ elements/pf-date-picker/pf-date-picker.ts | 25 +++++++++++++++++++ elements/pf-date-picker/pf-month-select.css | 3 +++ elements/pf-date-picker/pf-month-select.ts | 25 +++++++++++++++++++ elements/pf-date-picker/pf-next-button.css | 3 +++ elements/pf-date-picker/pf-next-button.ts | 25 +++++++++++++++++++ .../pf-date-picker/pf-previous-button.css | 3 +++ elements/pf-date-picker/pf-previous-button.ts | 25 +++++++++++++++++++ elements/pf-date-picker/pf-year-input.css | 3 +++ elements/pf-date-picker/pf-year-input.ts | 25 +++++++++++++++++++ .../pf-date-picker/test/pf-date-picker.e2e.ts | 12 +++++++++ .../test/pf-date-picker.spec.ts | 21 ++++++++++++++++ 21 files changed, 269 insertions(+) create mode 100644 elements/pf-date-picker/README.md create mode 100644 elements/pf-date-picker/date-picker-helper.ts create mode 100644 elements/pf-date-picker/demo/demo.css create mode 100644 elements/pf-date-picker/demo/pf-date-picker.html create mode 100644 elements/pf-date-picker/demo/pf-date-picker.js create mode 100644 elements/pf-date-picker/docs/pf-date-picker.md create mode 100644 elements/pf-date-picker/pf-calendar.css create mode 100644 elements/pf-date-picker/pf-calendar.ts create mode 100644 elements/pf-date-picker/pf-date-picker.css create mode 100644 elements/pf-date-picker/pf-date-picker.ts create mode 100644 elements/pf-date-picker/pf-month-select.css create mode 100644 elements/pf-date-picker/pf-month-select.ts create mode 100644 elements/pf-date-picker/pf-next-button.css create mode 100644 elements/pf-date-picker/pf-next-button.ts create mode 100644 elements/pf-date-picker/pf-previous-button.css create mode 100644 elements/pf-date-picker/pf-previous-button.ts create mode 100644 elements/pf-date-picker/pf-year-input.css create mode 100644 elements/pf-date-picker/pf-year-input.ts create mode 100644 elements/pf-date-picker/test/pf-date-picker.e2e.ts create mode 100644 elements/pf-date-picker/test/pf-date-picker.spec.ts diff --git a/elements/package.json b/elements/package.json index 0cd47fe88f..1c363ebf7e 100644 --- a/elements/package.json +++ b/elements/package.json @@ -29,6 +29,13 @@ "./pf-clipboard-copy/pf-clipboard-copy.js": "./pf-clipboard-copy/pf-clipboard-copy.js", "./pf-code-block/BaseCodeBlock.js": "./pf-code-block/BaseCodeBlock.js", "./pf-code-block/pf-code-block.js": "./pf-code-block/pf-code-block.js", + "./pf-date-picker/pf-calendar.js": "./pf-date-picker/pf-calendar.js", + "./pf-date-picker/pf-date-picker.js": "./pf-date-picker/pf-date-picker.js", + "./pf-date-picker/date-picker-helper.js":"./pf-date-picker/date-picker-helper.js", + "./pf-date-picker/pf-month-select.js": "./pf-date-picker/pf-month-select.js", + "./pf-date-picker/pf-next-button.js": "./pf-date-picker/pf-next-button.js", + "./pf-date-picker/pf-previous-button.js": "./pf-date-picker/pf-previous-button.js", + "./pf-date-picker/pf-year-input.js": "./pf-date-picker/pf-year-input.js", "./pf-icon/BaseIcon.js": "./pf-icon/BaseIcon.js", "./pf-icon/pf-icon.js": "./pf-icon/pf-icon.js", "./pf-icon/icons/*": "./pf-icon/icons/*", diff --git a/elements/pf-date-picker/README.md b/elements/pf-date-picker/README.md new file mode 100644 index 0000000000..950c7e68e0 --- /dev/null +++ b/elements/pf-date-picker/README.md @@ -0,0 +1,11 @@ +# Date Picker +Add a description of the component here. + +## Usage +Describe how best to use this web component along with best practices. + +```html + + + +``` diff --git a/elements/pf-date-picker/date-picker-helper.ts b/elements/pf-date-picker/date-picker-helper.ts new file mode 100644 index 0000000000..a18ac25de8 --- /dev/null +++ b/elements/pf-date-picker/date-picker-helper.ts @@ -0,0 +1,25 @@ +import { LitElement, html } from 'lit'; +import { customElement } from 'lit/decorators/custom-element.js'; + +import styles from './date-picker-helper.css'; + +/** + * Picker Helper + * @slot - Place element content here + */ +@customElement('date-picker-helper') +export class DatePickerHelper extends LitElement { + static readonly styles = [styles]; + + render() { + return html` + + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + 'date-picker-helper': DatePickerHelper; + } +} diff --git a/elements/pf-date-picker/demo/demo.css b/elements/pf-date-picker/demo/demo.css new file mode 100644 index 0000000000..12dfba0ec8 --- /dev/null +++ b/elements/pf-date-picker/demo/demo.css @@ -0,0 +1,3 @@ +pf-date-picker { + /* insert demo styles */ +} diff --git a/elements/pf-date-picker/demo/pf-date-picker.html b/elements/pf-date-picker/demo/pf-date-picker.html new file mode 100644 index 0000000000..219c221a9b --- /dev/null +++ b/elements/pf-date-picker/demo/pf-date-picker.html @@ -0,0 +1,4 @@ + + + + diff --git a/elements/pf-date-picker/demo/pf-date-picker.js b/elements/pf-date-picker/demo/pf-date-picker.js new file mode 100644 index 0000000000..74c7d8c58c --- /dev/null +++ b/elements/pf-date-picker/demo/pf-date-picker.js @@ -0,0 +1 @@ +import '@patternfly/elements/pf-date-picker/pf-date-picker.js'; diff --git a/elements/pf-date-picker/docs/pf-date-picker.md b/elements/pf-date-picker/docs/pf-date-picker.md new file mode 100644 index 0000000000..89025c51c7 --- /dev/null +++ b/elements/pf-date-picker/docs/pf-date-picker.md @@ -0,0 +1,17 @@ +{% renderOverview %} + +{% endrenderOverview %} + +{% band header="Usage" %}{% endband %} + +{% renderSlots %}{% endrenderSlots %} + +{% renderAttributes %}{% endrenderAttributes %} + +{% renderMethods %}{% endrenderMethods %} + +{% renderEvents %}{% endrenderEvents %} + +{% renderCssCustomProperties %}{% endrenderCssCustomProperties %} + +{% renderCssParts %}{% endrenderCssParts %} diff --git a/elements/pf-date-picker/pf-calendar.css b/elements/pf-date-picker/pf-calendar.css new file mode 100644 index 0000000000..5d4e87f30f --- /dev/null +++ b/elements/pf-date-picker/pf-calendar.css @@ -0,0 +1,3 @@ +:host { + display: block; +} diff --git a/elements/pf-date-picker/pf-calendar.ts b/elements/pf-date-picker/pf-calendar.ts new file mode 100644 index 0000000000..2318028329 --- /dev/null +++ b/elements/pf-date-picker/pf-calendar.ts @@ -0,0 +1,25 @@ +import { LitElement, html } from 'lit'; +import { customElement } from 'lit/decorators/custom-element.js'; + +import styles from './pf-calendar.css'; + +/** + * Calendar + * @slot - Place element content here + */ +@customElement('pf-calendar') +export class PfCalendar extends LitElement { + static readonly styles = [styles]; + + render() { + return html` + + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + 'pf-calendar': PfCalendar; + } +} diff --git a/elements/pf-date-picker/pf-date-picker.css b/elements/pf-date-picker/pf-date-picker.css new file mode 100644 index 0000000000..5d4e87f30f --- /dev/null +++ b/elements/pf-date-picker/pf-date-picker.css @@ -0,0 +1,3 @@ +:host { + display: block; +} diff --git a/elements/pf-date-picker/pf-date-picker.ts b/elements/pf-date-picker/pf-date-picker.ts new file mode 100644 index 0000000000..d588d48c9e --- /dev/null +++ b/elements/pf-date-picker/pf-date-picker.ts @@ -0,0 +1,25 @@ +import { LitElement, html } from 'lit'; +import { customElement } from 'lit/decorators/custom-element.js'; + +import styles from './pf-date-picker.css'; + +/** + * Date Picker + * @slot - Place element content here + */ +@customElement('pf-date-picker') +export class PfDatePicker extends LitElement { + static readonly styles = [styles]; + + render() { + return html` + + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + 'pf-date-picker': PfDatePicker; + } +} diff --git a/elements/pf-date-picker/pf-month-select.css b/elements/pf-date-picker/pf-month-select.css new file mode 100644 index 0000000000..5d4e87f30f --- /dev/null +++ b/elements/pf-date-picker/pf-month-select.css @@ -0,0 +1,3 @@ +:host { + display: block; +} diff --git a/elements/pf-date-picker/pf-month-select.ts b/elements/pf-date-picker/pf-month-select.ts new file mode 100644 index 0000000000..0166215bcb --- /dev/null +++ b/elements/pf-date-picker/pf-month-select.ts @@ -0,0 +1,25 @@ +import { LitElement, html } from 'lit'; +import { customElement } from 'lit/decorators/custom-element.js'; + +import styles from './pf-month-select.css'; + +/** + * Month Select + * @slot - Place element content here + */ +@customElement('pf-month-select') +export class PfMonthSelect extends LitElement { + static readonly styles = [styles]; + + render() { + return html` + + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + 'pf-month-select': PfMonthSelect; + } +} diff --git a/elements/pf-date-picker/pf-next-button.css b/elements/pf-date-picker/pf-next-button.css new file mode 100644 index 0000000000..5d4e87f30f --- /dev/null +++ b/elements/pf-date-picker/pf-next-button.css @@ -0,0 +1,3 @@ +:host { + display: block; +} diff --git a/elements/pf-date-picker/pf-next-button.ts b/elements/pf-date-picker/pf-next-button.ts new file mode 100644 index 0000000000..3f95403e5c --- /dev/null +++ b/elements/pf-date-picker/pf-next-button.ts @@ -0,0 +1,25 @@ +import { LitElement, html } from 'lit'; +import { customElement } from 'lit/decorators/custom-element.js'; + +import styles from './pf-next-button.css'; + +/** + * Next Button + * @slot - Place element content here + */ +@customElement('pf-next-button') +export class PfNextButton extends LitElement { + static readonly styles = [styles]; + + render() { + return html` + + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + 'pf-next-button': PfNextButton; + } +} diff --git a/elements/pf-date-picker/pf-previous-button.css b/elements/pf-date-picker/pf-previous-button.css new file mode 100644 index 0000000000..5d4e87f30f --- /dev/null +++ b/elements/pf-date-picker/pf-previous-button.css @@ -0,0 +1,3 @@ +:host { + display: block; +} diff --git a/elements/pf-date-picker/pf-previous-button.ts b/elements/pf-date-picker/pf-previous-button.ts new file mode 100644 index 0000000000..2de42edafe --- /dev/null +++ b/elements/pf-date-picker/pf-previous-button.ts @@ -0,0 +1,25 @@ +import { LitElement, html } from 'lit'; +import { customElement } from 'lit/decorators/custom-element.js'; + +import styles from './pf-previous-button.css'; + +/** + * Previous Button + * @slot - Place element content here + */ +@customElement('pf-previous-button') +export class PfPreviousButton extends LitElement { + static readonly styles = [styles]; + + render() { + return html` + + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + 'pf-previous-button': PfPreviousButton; + } +} diff --git a/elements/pf-date-picker/pf-year-input.css b/elements/pf-date-picker/pf-year-input.css new file mode 100644 index 0000000000..5d4e87f30f --- /dev/null +++ b/elements/pf-date-picker/pf-year-input.css @@ -0,0 +1,3 @@ +:host { + display: block; +} diff --git a/elements/pf-date-picker/pf-year-input.ts b/elements/pf-date-picker/pf-year-input.ts new file mode 100644 index 0000000000..eed6d847c9 --- /dev/null +++ b/elements/pf-date-picker/pf-year-input.ts @@ -0,0 +1,25 @@ +import { LitElement, html } from 'lit'; +import { customElement } from 'lit/decorators/custom-element.js'; + +import styles from './pf-year-input.css'; + +/** + * Year Input + * @slot - Place element content here + */ +@customElement('pf-year-input') +export class PfYearInput extends LitElement { + static readonly styles = [styles]; + + render() { + return html` + + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + 'pf-year-input': PfYearInput; + } +} diff --git a/elements/pf-date-picker/test/pf-date-picker.e2e.ts b/elements/pf-date-picker/test/pf-date-picker.e2e.ts new file mode 100644 index 0000000000..b99ec8b803 --- /dev/null +++ b/elements/pf-date-picker/test/pf-date-picker.e2e.ts @@ -0,0 +1,12 @@ +import { test } from '@playwright/test'; +import { PfeDemoPage } from '@patternfly/pfe-tools/test/playwright/PfeDemoPage.js'; + +const tagName = 'pf-date-picker'; + +test.describe(tagName, () => { + test('snapshot', async ({ page }) => { + const componentPage = new PfeDemoPage(page, tagName); + await componentPage.navigate(); + await componentPage.snapshot(); + }); +}); diff --git a/elements/pf-date-picker/test/pf-date-picker.spec.ts b/elements/pf-date-picker/test/pf-date-picker.spec.ts new file mode 100644 index 0000000000..d6d969dee3 --- /dev/null +++ b/elements/pf-date-picker/test/pf-date-picker.spec.ts @@ -0,0 +1,21 @@ +import { expect, html } from '@open-wc/testing'; +import { createFixture } from '@patternfly/pfe-tools/test/create-fixture.js'; +import { PfDatePicker } from '@patternfly/elements/pf-date-picker/pf-date-picker.js'; + +describe('', function() { + describe('simply instantiating', function() { + let element: PfDatePicker; + it('imperatively instantiates', function() { + expect(document.createElement('pf-date-picker')).to.be.an.instanceof(PfDatePicker); + }); + + it('should upgrade', async function() { + element = await createFixture(html``); + const klass = customElements.get('pf-date-picker'); + expect(element) + .to.be.an.instanceOf(klass) + .and + .to.be.an.instanceOf(PfDatePicker); + }); + }); +}); From 2b0ff7de511cf5f6be42c49e1ac0d4fc793933ea Mon Sep 17 00:00:00 2001 From: Arathy Kumar Date: Wed, 27 Sep 2023 16:05:06 +0530 Subject: [PATCH 02/15] chore: added date picker component --- elements/package.json | 2 +- elements/pf-date-picker/date-picker-helper.ts | 355 +++++++++++- elements/pf-date-picker/demo/demo.css | 24 +- .../pf-date-picker/demo/pf-date-picker.html | 73 ++- elements/pf-date-picker/pf-calendar.css | 65 +++ elements/pf-date-picker/pf-calendar.ts | 252 ++++++++- elements/pf-date-picker/pf-date-picker.css | 147 +++++ elements/pf-date-picker/pf-date-picker.ts | 512 +++++++++++++++++- elements/pf-date-picker/pf-month-select.css | 79 +++ elements/pf-date-picker/pf-month-select.ts | 125 ++++- elements/pf-date-picker/pf-next-button.css | 16 + elements/pf-date-picker/pf-next-button.ts | 30 +- .../pf-date-picker/pf-previous-button.css | 16 + elements/pf-date-picker/pf-previous-button.ts | 30 +- elements/pf-date-picker/pf-year-input.css | 18 + elements/pf-date-picker/pf-year-input.ts | 63 ++- 16 files changed, 1772 insertions(+), 35 deletions(-) diff --git a/elements/package.json b/elements/package.json index 1c363ebf7e..15205dd9e7 100644 --- a/elements/package.json +++ b/elements/package.json @@ -29,9 +29,9 @@ "./pf-clipboard-copy/pf-clipboard-copy.js": "./pf-clipboard-copy/pf-clipboard-copy.js", "./pf-code-block/BaseCodeBlock.js": "./pf-code-block/BaseCodeBlock.js", "./pf-code-block/pf-code-block.js": "./pf-code-block/pf-code-block.js", + "./pf-date-picker/date-picker-helper.js": "./pf-date-picker/date-picker-helper.js", "./pf-date-picker/pf-calendar.js": "./pf-date-picker/pf-calendar.js", "./pf-date-picker/pf-date-picker.js": "./pf-date-picker/pf-date-picker.js", - "./pf-date-picker/date-picker-helper.js":"./pf-date-picker/date-picker-helper.js", "./pf-date-picker/pf-month-select.js": "./pf-date-picker/pf-month-select.js", "./pf-date-picker/pf-next-button.js": "./pf-date-picker/pf-next-button.js", "./pf-date-picker/pf-previous-button.js": "./pf-date-picker/pf-previous-button.js", diff --git a/elements/pf-date-picker/date-picker-helper.ts b/elements/pf-date-picker/date-picker-helper.ts index a18ac25de8..03a0ddbfec 100644 --- a/elements/pf-date-picker/date-picker-helper.ts +++ b/elements/pf-date-picker/date-picker-helper.ts @@ -1,25 +1,342 @@ -import { LitElement, html } from 'lit'; -import { customElement } from 'lit/decorators/custom-element.js'; +export interface DateFormatDetails { + dateParts: string[]; + literal: string | undefined; +} + +export interface InputDate { + day: number; + month: number; + year: number; + literal: string; +} + +export const days: string[] = ['S', 'M', 'T', 'W', 'T', 'F', 'S']; + +// Function to return date object +export const getFormattedDate = (date: Date) => { + const focusDate = { + day: date.getDate(), + month: date.getMonth(), + year: date.getFullYear() + }; + + return focusDate; +}; -import styles from './date-picker-helper.css'; -/** - * Picker Helper - * @slot - Place element content here - */ -@customElement('date-picker-helper') -export class DatePickerHelper extends LitElement { - static readonly styles = [styles]; +// Function to get the date format locale parts +export const getLocaleParts = (language?: string) => { + // Get the browser user locale - Commented for reference + // const userLocale: string = navigator.languages && navigator.languages.length ? + // navigator.languages[0] : navigator.language; - render() { - return html` - - `; + const { timeZone } = Intl.DateTimeFormat().resolvedOptions(); + let formatter: Intl.DateTimeFormatPart[] = []; + + // Set date format options + const options: Intl.DateTimeFormatOptions = { + timeZone: timeZone, + dateStyle: 'short', + localeMatcher: 'lookup' + }; + + // If there is locale passed from parent, pass locale else use "default" + const locale: string = language ? language : 'default'; + + // Try - Catch block is used to catch error and format date using default locale if invalid locale is passed from parent + try { + // Get the date components breakdown array + formatter = new Intl.DateTimeFormat(locale, options).formatToParts(new Date()); + } catch (error) { + if (error) { // Get the date components breakdown array with default locale + formatter = new Intl.DateTimeFormat('default', options).formatToParts(new Date()); + } } -} -declare global { - interface HTMLElementTagNameMap { - 'date-picker-helper': DatePickerHelper; + return formatter; +}; + + +// Function to return the date values from user input +export const getDateValues = (dateString: string, languageCode?: string, dateFormatInput?: string) => { + let parseDay!: number; + let parseMonth!: number; + let parseYear!: number; + let splitWith!: string; + + // If there is a dateFormat input from parent, the input format will be applied + // else date format will be generated and applied from languageCode input or default locale + if (dateFormatInput && isInputDateFormatValid(dateFormatInput)) { + const dateFormatDetails: DateFormatDetails = parseDateFormat(dateFormatInput); + let index = 0; + + dateFormatDetails.dateParts.map((part: string) => { // Generate the date format + switch (part) { + case 'MM': + parseMonth = index; + index++; + break; + case 'DD': + parseDay = index; + index++; + break; + case 'YYYY': + parseYear = index; + index++; + break; + default: + break; + } + }); + splitWith = dateFormatDetails.literal ? dateFormatDetails.literal : '/'; + } else { + const formatter: Intl.DateTimeFormatPart[] = getLocaleParts(languageCode); + let index = 0; + + formatter.map((part: Intl.DateTimeFormatPart) => { // Generate the date format + switch (part.type) { + case 'month': + parseMonth = index; + index++; + break; + case 'day': + parseDay = index; + index++; + break; + case 'year': + parseYear = index; + index++; + break; + default: + splitWith = part.value; + } + }); } -} + + const dateStringArray: string[] = dateString.split(splitWith); + const selectedDayInput: number = parseInt(dateStringArray[parseDay], 10); + const selectedMonthInput: number = parseInt(dateStringArray[parseMonth], 10); + const selectedYearInput: number = parseInt(dateStringArray[parseYear], 10); + const inputDate: InputDate = { + day: selectedDayInput, + month: selectedMonthInput, + year: selectedYearInput, + literal: splitWith + }; + + return inputDate; +}; + +// Function to return the fomatted date string +export const getDateFormat = (day: string, month: string, year: string, languageCode?: string, dateFormatInput?: string) => { + let formattedDate = ``; + const dd: string = day; + const mm: string = month; + const yyyy: string = year; + + // If there is a dateFormat input from parent, the input format will be applied + // else date format will be generated and applied from languageCode input or default locale + if (dateFormatInput && isInputDateFormatValid(dateFormatInput)) { + const dateFormatDetails: DateFormatDetails = parseDateFormat(dateFormatInput); + + dateFormatDetails.dateParts.map((part: string, index: number) => { // Generate the date format + switch (part) { + case 'MM': + formattedDate += `${mm}`; + break; + case 'DD': + formattedDate += `${dd}`; + break; + case 'YYYY': + formattedDate += `${yyyy}`; + break; + default: + break; + } + if (index < 2) { + formattedDate += dateFormatDetails.literal ? dateFormatDetails.literal : '/'; + } + }); + } else { + const formatter: Intl.DateTimeFormatPart[] = getLocaleParts(languageCode); + + formatter.map((part: Intl.DateTimeFormatPart) => { // Generate the date format + switch (part.type) { + case 'month': + formattedDate += `${mm}`; + break; + case 'day': + formattedDate += `${dd}`; + break; + case 'year': + formattedDate += `${yyyy}`; + break; + default: + formattedDate += part.value; + } + }); + } + + return formattedDate; +}; + +// Function to return date format based on date format input or locale +export const getDatePatternFromLocale = (languageCode?: string, dateFormatInput?: string) => { + let localeDateFormat = ''; + + // If there is a dateFormat input from parent, the input format will be applied + // else date format will be generated and applied from languageCode input or default locale + if (dateFormatInput && isInputDateFormatValid(dateFormatInput)) { + localeDateFormat = dateFormatInput; + } else { + const formatter: Intl.DateTimeFormatPart[] = getLocaleParts(languageCode); + + formatter.map((part: Intl.DateTimeFormatPart) => { // Generate the date format + switch (part.type) { + case 'month': + localeDateFormat += 'MM'; + break; + case 'day': + localeDateFormat += 'DD'; + break; + case 'year': + localeDateFormat += 'YYYY'; + break; + default: + localeDateFormat += part.value; + } + }); + } + + return localeDateFormat; +}; + +// Function to generate regex pattern based on date format input or locale +export const getRegexPattern = (languageCode?: string, dateFormatInput?: string) => { + const regDay = '[0-9]{1,2}'; + const regMonth = '[0-9]{1,2}'; + const regYear = '[0-9]{2,4}'; + let regex = '^'; + + // If there is a dateFormat input from parent, the input format will be applied + // else date format will be generated and applied from languageCode input or default locale + if (dateFormatInput && isInputDateFormatValid(dateFormatInput)) { + const dateFormatDetails: DateFormatDetails = parseDateFormat(dateFormatInput); + + dateFormatDetails.dateParts.map((part: string, index: number) => { // Generate the date format + switch (part) { + case 'MM': + regex += regMonth; + break; + case 'DD': + regex += regDay; + break; + case 'YYYY': + regex += regYear; + break; + default: + break; + } + if (index < 2) { + regex += dateFormatDetails.literal ? dateFormatDetails.literal : '/'; + } + }); + } else { + const formatter: Intl.DateTimeFormatPart[] = getLocaleParts(languageCode); + + formatter.map((part: Intl.DateTimeFormatPart) => { // Generate the regex for the date format according to locale + switch (part.type) { + case 'month': + regex += regMonth; + break; + case 'day': + regex += regDay; + break; + case 'year': + regex += regYear; + break; + default: + regex += part.value; // Will append the part literal '/' or '.' or '-' to the regex + break; + } + }); + } + regex += '$'; + + return regex; +}; + +// Function to generate date parts array from date format input +export const parseDateFormat = (dateFormatInput: string) => { + const literals: string[] = ['/', '-', '.']; // Supported date format literals + let datePartsArray: string[] = []; + + const literal = literals.find((literal: string) => { // Find the literal in the format + if (dateFormatInput.includes(literal)) { + return literal; + } + }); + + if (literal) { + datePartsArray = dateFormatInput.split(literal); // Split the format to date parts + } + + const dateFormatParts = { + dateParts: datePartsArray, + literal: literal + }; + + return dateFormatParts; +}; + +// Function to check validity of the date format input +export const isInputDateFormatValid = (dateFormatInput: string) => { + let isDateFormatValid = false; + const supportedDateFormats: string[] = [ // Supported date formats from parent + 'DD/MM/YYYY', 'MM/DD/YYYY', 'YYYY/MM/DD', 'YYYY/DD/MM', 'DD-MM-YYYY', 'MM-DD-YYYY', + 'YYYY-MM-DD', 'YYYY-DD-MM', 'DD.MM.YYYY', 'MM.DD.YYYY', 'YYYY.MM.DD', 'YYYY.DD.MM' + ]; + + if (supportedDateFormats.includes(dateFormatInput)) { + isDateFormatValid = true; + } else { + isDateFormatValid = false; + } + + return isDateFormatValid; +}; + +export const getMonthNamesFromLocale = (languageCode?: string) => { + const monthNames: string[] = [ + 'January', + 'February', + 'March', + 'April', + 'May', + 'June', + 'July', + 'August', + 'September', + 'October', + 'November', + 'December' + ]; + + if (languageCode) { + const date: Date = new Date(); + try { + const translatedMonthNames: string[] = []; + for (let i = 0; i < 12; i++) { + date.setMonth(i); + const monthName: string = new Intl.DateTimeFormat(languageCode, { month: 'long' }).format(new Date(date.getFullYear(), i, date.getDate())); + translatedMonthNames.push(monthName); + } + return translatedMonthNames; + } catch (error) { + if (error) { + return monthNames; + } + } + } + return monthNames; +}; diff --git a/elements/pf-date-picker/demo/demo.css b/elements/pf-date-picker/demo/demo.css index 12dfba0ec8..d25dcffa52 100644 --- a/elements/pf-date-picker/demo/demo.css +++ b/elements/pf-date-picker/demo/demo.css @@ -1,3 +1,23 @@ -pf-date-picker { - /* insert demo styles */ +.container { + min-height: 25vh; + height: auto; + width: 100%; + display: flex; + align-items: flex-start; + justify-content: flex-start; + padding: 40px; + flex-direction: column; } + +.container section { + margin-bottom: var(--rh-space-lg, 16px) +} + +.container section p span { + color: var(--rh-color-text-secondary-on-light, #4d4d4d); + font-family: var(--rh-font-family-code, RedHatMono, "Red Hat Mono", "Courier New", Courier, monospace); +} + +.container section pre { + padding-bottom: var(--rh-space-md, 8px) +} \ No newline at end of file diff --git a/elements/pf-date-picker/demo/pf-date-picker.html b/elements/pf-date-picker/demo/pf-date-picker.html index 219c221a9b..b51fe6b2ec 100644 --- a/elements/pf-date-picker/demo/pf-date-picker.html +++ b/elements/pf-date-picker/demo/pf-date-picker.html @@ -1,4 +1,75 @@ - +
+
+

Basic

+
<pf-date-picker></pf-date-picker>
+ +

The basic date picker will use the user's locale to determine the date format. The default minimum valid date + is January 1, 1900, and the default maximum valid date is December 31, 9999.

+
+ +
+

Date Format

+
<pf-date-picker dateFormatInput="YYYY-DD-MM"></pf-date-picker>
+ +

The date picker supports the following 12 date formats:

+

'DD/MM/YYYY', 'MM/DD/YYYY', 'YYYY/MM/DD', 'YYYY/DD/MM', 'DD-MM-YYYY', 'MM-DD-YYYY', 'YYYY-MM-DD', + 'YYYY-DD-MM', 'DD.MM.YYYY', 'MM.DD.YYYY', 'YYYY.MM.DD', 'YYYY.DD.MM'

+

The date format is set globally, independent of the user's locale. + This means that all users will see the same date format, regardless of their language or region.

+
+ +
+

Localization

+
<pf-date-picker localizationLanguageCode="fi"></pf-date-picker>
+ +

The locale string can be passed to the date picker to set the date format.

+

In the above given example, the locale is set to Finnish: fi

+

The date format is set globally, independent of the user's locale. + This means that all users will see the same date format, regardless of their language or region.

+
+ +
+

Translation

+
<pf-date-picker translationLanguageCode="fr"></pf-date-picker>
+ +

The translation language string can be passed to the date picker to localize the month names.

+

In the above given example, the language is set to French: fr

+
+ +
+

Set Date

+
<pf-date-picker inputDate=${new Date(2023, 0, 1)}></pf-date-picker>
+ + +

The default date value can be passed to the date picker to set the initial date that is displayed.

+

In the above given example, the date set to be displayed initially is January 1, 2023.

+
+ +
+

Set Min and Max Date

+
<pf-date-picker minDate=${new Date(2023, 0, 2)} minDate=${new Date(2023, 0, 20)} 
+      inputDate=${new Date(2023, 0, 3)}></pf-date-picker>
+ + + +

The minimum and maximum valid dates can be passed to the date picker to restrict the range of dates that can be selected

+

In the above given example, the minimum valid date + is January 2, 2023, and the maximum valid date is January 20, 2023

+
+ +
+

Disabled

+
<pf-date-picker isDisabled="true"></pf-date-picker>
+ +

The isDisabled attribute can be used to disable the date picker.

+
+
+ diff --git a/elements/pf-date-picker/pf-calendar.css b/elements/pf-date-picker/pf-calendar.css index 5d4e87f30f..a2333ad75d 100644 --- a/elements/pf-date-picker/pf-calendar.css +++ b/elements/pf-date-picker/pf-calendar.css @@ -1,3 +1,68 @@ :host { display: block; } + +.date-picker-table-row { + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + margin-bottom: 0.125rem; +} + +.date-picker-table-col { + height: 39px; + width: 39px; + display: flex; + align-items: center; + justify-content: center; +} + +pf-button::part(button):focus, pf-button::part(button):focus-visible { + border: 2px solid var(--rh-color-accent-base-on-light, #0066cc); + outline: none; +} + +pf-button.calendar-date-button::part(button) { + width: 37px; + height: 37px; + white-space: nowrap; + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; +} + +pf-button.calendar-date-button.isToday { + --pf-c-button--m-plain--BackgroundColor: #f0f0f0; + --pf-c-button--m-plain--hover--BackgroundColor: var(--rh-color-blue-50, #e7f1fa); + --pf-c-button--m-plain--focus--BackgroundColor: var(--rh-color-blue-50, #e7f1fa); +} + +pf-button.calendar-date-button { + --pf-c-button--m-plain--Color: var(--rh-color-canvas-black, #151515); + --pf-c-button--m-plain--hover--BackgroundColor: var(--rh-color-blue-50, #e7f1fa); + --pf-c-button--m-plain--focus--BackgroundColor: var(--rh-color-blue-50, #e7f1fa); + --pf-c-button--FontSize: var(--rh-font-size-body-text-sm, 0.875rem); + + font-family: var(--rh-font-family-body-text, RedHatText, "Red Hat Text", "Noto Sans Arabic", "Noto Sans Hebrew", "Noto Sans JP", "Noto Sans KR", "Noto Sans Malayalam", "Noto Sans SC", "Noto Sans TC", "Noto Sans Thai", Helvetica, Arial, sans-serif); +} + +pf-button.calendar-date-button.previous-next-date { + --pf-c-button--m-plain--Color: #6A6E73; +} + +pf-button.calendar-date-button.selected-date::part(button) { + --pf-c-button--m-plain--BackgroundColor: var(--rh-color-accent-base-on-light, #0066cc); + --pf-c-button--m-plain--Color: var(--rh-color-white, #ffffff); + --pf-c-button--m-plain--hover--Color: var(--rh-color-white, #ffffff); + --pf-c-button--m-plain--hover--BackgroundColor: var(--rh-color-interactive-blue-darkest, #004080); + --pf-c-button--m-plain--focus--BackgroundColor: var(--rh-color-interactive-blue-darkest, #004080); +} + +pf-button.calendar-date-button.selected-date::part(button):focus { + box-shadow: 0 0 0.3125rem var(--rh-color-accent-base-on-light, #0066cc); + background: var(--rh-color-interactive-blue-darkest, #004080); + border: none; + outline: none; +} diff --git a/elements/pf-date-picker/pf-calendar.ts b/elements/pf-date-picker/pf-calendar.ts index 2318028329..1f8f9124c7 100644 --- a/elements/pf-date-picker/pf-calendar.ts +++ b/elements/pf-date-picker/pf-calendar.ts @@ -1,7 +1,46 @@ import { LitElement, html } from 'lit'; import { customElement } from 'lit/decorators/custom-element.js'; - +import { property } from 'lit/decorators/property.js'; +import { ref, createRef } from 'lit/directives/ref.js'; import styles from './pf-calendar.css'; +import { getFormattedDate, getMonthNamesFromLocale } from './date-picker-helper.js'; +import { ComposedEvent } from '@patternfly/pfe-core/core.js'; + +export interface FocusedDateValues{ + day: number; + month: number; + year: number; +} + +export interface CalendarDateValues{ + day: number; + month: number; + year: number; + isCurrentMonth: boolean; +} + +/** + * Date Calendar + * @slot - Place element content here + */ + +export class DayChangeEvent extends ComposedEvent { + constructor(public event: Event, public day: number, public month: number, public year: number) { + super('daySelected'); + } +} + +export class FocusChangeEvent extends ComposedEvent { + constructor(public dateToBeFocusedRef: HTMLButtonElement | undefined) { + super('setDateFocus'); + } +} + +export class KeyboardNavigationFocusEvent extends ComposedEvent { + constructor(public event: Event, public day: number, public month: number, public year: number) { + super('onCalendarKeydown'); + } +} /** * Calendar @@ -11,10 +50,215 @@ import styles from './pf-calendar.css'; export class PfCalendar extends LitElement { static readonly styles = [styles]; + private currentDate: Date = new Date(); + private weekdays: number[] = [0, 1, 2, 3, 4, 5, 6]; // S, M, T, W, T, F, S + private weeks: number[] = [0, 1, 2, 3, 4, 5]; // 1 previous month week, 4 current month weeks, 1 next month week + + // Input properties from the parent + @property() currentYear: number = this.currentDate.getFullYear(); + @property() currentMonth: number = this.currentDate.getMonth(); + @property() currentWeek = 0; + @property() selectedDay: number = this.currentDate.getDate(); + @property() focusRef!: HTMLButtonElement; + @property() dayToBeFocused!: number; + @property() focusedDateValues!: FocusedDateValues; + @property() dateSelected!: Date; + @property() firstDayToBeFocused!: number; + @property() minDate!: Date; + @property() maxDate!: Date; + @property() translationLanguageCode!: string; + @property() monthNames: string[] = getMonthNamesFromLocale(this.translationLanguageCode); + focusDateRef = createRef(); // Reference to the button that needs to be focused + + constructor() { + super(); + } + + connectedCallback() { + super.connectedCallback(); + this.#init(); + if (this.translationLanguageCode) { + this.monthNames = getMonthNamesFromLocale(this.translationLanguageCode); + } + } + render() { - return html` - - `; + this.focusedDateValues = getFormattedDate(new Date(this.currentYear, this.currentMonth, this.dayToBeFocused)); + + // Get the total number of days of the current month + const totalDays: number = new Date(this.currentYear, this.currentMonth + 1, 0).getDate(); + + // Find the day of the week for the first day of the month + const firstDay: number = new Date(this.currentYear, this.currentMonth, 1).getDay(); + + // Get the last date of the previous month + const previousMonthLastDate: number = new Date(this.currentYear, this.currentMonth, 0).getDate(); + + // Get the number of weeks in a month + this.weeks = this.#getNoOfWeeks(this.currentYear, this.currentMonth); + + let day = 1; + let nextMonthPointer = 0; + + return html`
+ ${this.weeks.map((week: number) => { + return html` +
+ ${this.weekdays.map((weekDay: number) => { + let calendarDayTemplate: unknown; + if (week === 0 && weekDay < firstDay) { + // Add the last dates of the previous month + const dateValue: CalendarDateValues = { + day: (previousMonthLastDate - (firstDay - 1) + weekDay), + month: this.currentMonth - 1, + year: this.currentYear, + isCurrentMonth: false + }; + calendarDayTemplate = this.#renderCalendarDates(dateValue); + } else if (day > totalDays) { + // Add the first dates of the next month + const dateValue: CalendarDateValues = { + day: nextMonthPointer + 1, + month: this.currentMonth + 1, + year: this.currentYear, + isCurrentMonth: false + }; + calendarDayTemplate = this.#renderCalendarDates(dateValue); + nextMonthPointer++; + } else { + // Add the dates of the current month + const dateValue: CalendarDateValues = { + day: day, + month: this.currentMonth, + year: this.currentYear, + isCurrentMonth: true + }; + calendarDayTemplate = this.#renderCalendarDates(dateValue); + day++; + } + return calendarDayTemplate; + })} +
`; + })} +
`; + } + + // Function to render the days of the calendar + #renderCalendarDates(value: CalendarDateValues) { + const isDayToBeFocused: boolean = ((value.day === this.dayToBeFocused) && + (value.month === this.focusedDateValues?.month) && + (value.year === this.focusedDateValues?.year)); + + const selectedDate: Date = new Date(this.dateSelected); + const isToday: boolean = ((value.day === this.currentDate.getDate()) && + (value.month === this.currentDate.getMonth()) && + (value.year === this.currentDate.getFullYear())); + + const isSelectedDay: boolean = ((value.day === selectedDate?.getDate()) && + (value.month === selectedDate?.getMonth()) && + (value.year === selectedDate?.getFullYear())); + + const dateRendering = new Date(value.year, value.month, value.day); + const isDateInvalid = dateRendering.toString() === 'Invalid Date'; + const isDateDisabled: boolean = isDateInvalid || dateRendering < this.minDate || dateRendering > this.maxDate; + + const dateValue = html`
+ this.#selectDate(e, value.month, value.year)} + class='${this.selectedDay && isSelectedDay ? 'selected-date' : ''} + ${!value.isCurrentMonth && 'previous-next-date'} + ${isToday && 'isToday'} calendar-date-button'> + ${value.day} + +
`; + + return dateValue; + } + + // Function to dispatch the selected date + #selectDate(event: Event, month: number, year: number) { + const { value } = event.target as HTMLButtonElement; + const selectedDay: number = parseInt(value); + let currentMonth: number = month; + let currentYear: number = year; + if (month < 0) { + currentMonth = 11; + currentYear = currentYear - 1; + } + if (month > 11) { + currentMonth = 0; + currentYear = currentYear + 1; + } + + this.dispatchEvent(new DayChangeEvent(event, selectedDay, currentMonth, currentYear)); + } + + async #init() { + await this.updateComplete; + this.#setDateFocus(); + } + + // Function to focus date button; + #setDateFocus() { + const date: HTMLButtonElement | undefined = this.focusDateRef.value!; + this.focusRef = date; + setTimeout(() => { + date?.focus(); + }, 25); + } + + // Function to handle focus on property update + updated() { + this.dispatchEvent(new FocusChangeEvent(this.focusDateRef.value!)); + this.#setDateFocus(); + } + + // Funtion to handle focus based on arrow keys + #onCalendarKeydown(event: KeyboardEvent) { + const date = new Date(this.currentYear, this.currentMonth, this.dayToBeFocused); + + switch (event.key) { + case 'ArrowUp': + date.setDate(date.getDate() - 7); + break; + case 'ArrowDown': + date.setDate(date.getDate() + 7); + break; + case 'ArrowLeft': + date.setDate(date.getDate() - 1); + break; + case 'ArrowRight': + date.setDate(date.getDate() + 1); + break; + } + + if (event.key === 'ArrowUp' || event.key === 'ArrowDown' || event.key === 'ArrowLeft' || event.key === 'ArrowRight') { + this.focusedDateValues = getFormattedDate(date); + this.dispatchEvent(new KeyboardNavigationFocusEvent(event, + this.focusedDateValues.day, this.focusedDateValues.month, this.focusedDateValues.year)); + } + } + + // Find total number of weeks of the month + #getNoOfWeeks(year: number, month: number) { + const firstDayOfMonth: Date = new Date(year, month, 1); + const lastDayOfMonth: Date = new Date(year, month + 1, 0); + + const totalNoOfDays: number = firstDayOfMonth.getDay() + lastDayOfMonth.getDate(); + const totalWeeks: number = Math.ceil( totalNoOfDays / 7); + + const weeks: number[] = []; + + for (let i = 0; i < totalWeeks; i++) { + weeks.push(i); + } + return weeks; } } diff --git a/elements/pf-date-picker/pf-date-picker.css b/elements/pf-date-picker/pf-date-picker.css index 5d4e87f30f..f13250ca69 100644 --- a/elements/pf-date-picker/pf-date-picker.css +++ b/elements/pf-date-picker/pf-date-picker.css @@ -1,3 +1,150 @@ :host { display: block; } + +.datepicker { + position: relative; + display: inline-block; + font-family: var(--rh-font-family-body-text, RedHatText, "Red Hat Text", "Noto Sans Arabic", "Noto Sans Hebrew", "Noto Sans JP", "Noto Sans KR", "Noto Sans Malayalam", "Noto Sans SC", "Noto Sans TC", "Noto Sans Thai", Helvetica, Arial, sans-serif); +} + +.calendar { + width: auto; + min-width: 330px; + max-width: 330px; + background-color: var(--rh-color-white, #ffffff); + padding: var(--rh-space-md, 8px); + padding-bottom: 0; + box-sizing: border-box; +} + +.date-picker-table { + display: block; + width: 100%; +} + +.date-picker-table-row { + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; +} + +.date-picker-table-main-header { + justify-content: space-around; +} + +.date-picker-table-col { + height: 39px; + width: 39px; + display: flex; + align-items: center; + justify-content: center; + font-size: var(--rh-font-size-body-text-sm, 0.875rem); + font-style: normal; + font-weight: var(--rh-font-weight-body-text-regular, 400); + line-height: 21px; +} + +.date-picker-table-month-year { + display: flex; + flex-direction: row; + align-items: center; + width: fit-content; +} + +.date-picker-week-days { + border-bottom: 1px solid #D2D2D2; + margin-bottom: var(--rh-space-md, 8px); + margin-top: var(--rh-space-md, 8px); +} + +#date-input { + padding: 9px 10px; + border-radius: 0; + background-color: transparent; + cursor:text; + position: relative; + border: 1px solid #f0f0f0; + border-bottom: 1px solid #8b8d90; + border-right: none; + box-sizing: border-box; + width: 150px; + z-index: 1; + font-family: var(--rh-font-family-body-text, RedHatText, "Red Hat Text", "Noto Sans Arabic", "Noto Sans Hebrew", "Noto Sans JP", "Noto Sans KR", "Noto Sans Malayalam", "Noto Sans SC", "Noto Sans TC", "Noto Sans Thai", Helvetica, Arial, sans-serif); + font-size: var(--rh-font-size-body-text-md, 1rem); + font-style: normal; + font-weight: var(--rh-font-weight-body-text-regular, 400); + line-height: 24px; + height: 36px; +} + +#date-input:focus { + border-color: var(--rh-color-accent-base-on-light, #0066cc); + outline: var(--rh-color-accent-base-on-light, #0066cc) auto 2px; +} + +.date-input-box { + width: 192px; + background-color: transparent; + display: flex; + align-items: flex-start; + justify-content: flex-start; +} + +.date-input-box-container { + position: relative; +} + +.isDateInvalid { + position: absolute; + top: 50%; + right: var(--rh-space-md, 8px); + transform: translateY(-50%); + color: #c9190b; + display: block; +} + +.invalidDateInput { + border-bottom: 2px solid #c9190b !important; +} + +.showInvalidText { + display: block; + color: #a30000; + font-size: var(--rh-font-size-body-text-sm, 0.875rem); + font-family: var(--rh-font-family-body-text, RedHatText, "Red Hat Text", "Noto Sans Arabic", "Noto Sans Hebrew", "Noto Sans JP", "Noto Sans KR", "Noto Sans Malayalam", "Noto Sans SC", "Noto Sans TC", "Noto Sans Thai", Helvetica, Arial, sans-serif); + position: absolute; + bottom: -24px; + left: 0; + white-space: nowrap; +} + +.hidden { + display: none; +} + +:host .date-picker-toggle-container pf-button::part(button) { + padding: 9px 12px 3px; + height: 36px; +} + +.date-picker-container pf-popover::part(content) { + min-width: 330px; +} + +.disable-popover { + pointer-events: none; +} + +pf-button::part(button):focus, pf-button::part(button):focus-visible { + outline: var(--rh-color-accent-base-on-light, #0066cc) auto 2px; +} + +:host .date-picker-toggle-container pf-button.date-picker-calendar-icon::part(button):focus { + outline: none; +} + +:host .date-picker-toggle-container pf-button.date-picker-calendar-icon::part(button):focus-visible { + outline: var(--rh-color-accent-base-on-light, #0066cc) auto 2px; +} \ No newline at end of file diff --git a/elements/pf-date-picker/pf-date-picker.ts b/elements/pf-date-picker/pf-date-picker.ts index d588d48c9e..3757e7ffae 100644 --- a/elements/pf-date-picker/pf-date-picker.ts +++ b/elements/pf-date-picker/pf-date-picker.ts @@ -1,21 +1,529 @@ import { LitElement, html } from 'lit'; import { customElement } from 'lit/decorators/custom-element.js'; - +import { state } from 'lit/decorators/state.js'; +import { property } from 'lit/decorators/property.js'; +import '@patternfly/elements/pf-icon/pf-icon.js'; +import '@patternfly/elements/pf-button/pf-button.js'; +import '@patternfly/elements/pf-popover/pf-popover.js'; +import { ComposedEvent } from '@patternfly/pfe-core/core.js'; +import { PfPopover } from '@patternfly/elements/pf-popover/pf-popover.js'; +import { bound } from '@patternfly/pfe-core/decorators.js'; +import { query } from 'lit/decorators/query.js'; +import { classMap } from 'lit/directives/class-map.js'; +import { + days, + getDateFormat, + getDateValues, + getDatePatternFromLocale, + getRegexPattern, + getMonthNamesFromLocale +} from './date-picker-helper.js'; import styles from './pf-date-picker.css'; +import './pf-previous-button.js'; +import './pf-month-select.js'; +import './pf-year-input.js'; +import './pf-next-button.js'; +import './pf-calendar.js'; +import { PreviousButtonClickEvent } from './pf-previous-button.js'; +import { NextButtonClickEvent } from './pf-next-button.js'; +import { MonthChangeEvent, MonthPopupStateChangeEvent } from './pf-month-select.js'; +import { DayChangeEvent, FocusChangeEvent, KeyboardNavigationFocusEvent } from './pf-calendar.js'; /** * Date Picker * @slot - Place element content here */ + +interface ErrorMessages{ + inValid: string; + lessThanMinDate: string; + greaterThanMaxDate: string; +} + +export class DateChangeEvent extends ComposedEvent { + constructor(public event: Event, public value: Date | null ) { + super('selectedDate'); + } +} + +/* + * Date Picker + * @slot - Place element content here + */ @customElement('pf-date-picker') export class PfDatePicker extends LitElement { static readonly styles = [styles]; + private _currentDate: Date = new Date(); + private errorMessages: ErrorMessages = { + inValid: 'Invalid date', + lessThanMinDate: 'Date is before the allowable range.', + greaterThanMaxDate: 'Date is after the allowable range.' + }; + + @state() private isDateValid = true; // Checks if the date enetered by user in the input box is valid or not + @state() private isValid = true; // Checks if the year enetered by user in the year input box is valid or not + @state() private monthExpand = false; // Handles the closing and opening of the Month select + + @query('#date-input') _textInput!: HTMLInputElement; // Date picker input box reference + @query('#popover') private _popover!: PfPopover; // Popover reference + + // ----------- Input properties from parent ------------ // + @property({ reflect: true }) minDate: Date = new Date(1900, 0, 1); // Default minimum valid date set as 1st January 1900 + @property({ reflect: true }) maxDate: Date = new Date(9999, 11, 31); // Default maximum valid date set as 31st December 9999 + @property({ reflect: true }) inputDate!: Date; // Handle date value parent value sends to the component + @property({ reflect: true }) isDisabled = false; // Handles if the date picker is disabled or not + @property({ reflect: true }) localizationLanguageCode!: string; // Language code for date format based on localization + @property({ reflect: true }) translationLanguageCode!: string; // Language code for translation of date input and month names + @property({ reflect: true }) dateFormatInput!: // Date format input from parent + 'DD/MM/YYYY' | 'MM/DD/YYYY' | 'YYYY/MM/DD' | 'YYYY/DD/MM' | 'DD-MM-YYYY' | 'MM-DD-YYYY' | 'YYYY-MM-DD' | + 'YYYY-DD-MM' | 'DD.MM.YYYY' | 'MM.DD.YYYY' | 'YYYY.MM.DD' | 'YYYY.DD.MM'; + // ------------- // + + // 'current' refers to the temporary values of Month and Year the user selected before day is selected + // and the input box is updated + @property() monthNames: string[] = getMonthNamesFromLocale(this.translationLanguageCode); + @property() currentMonthSelection: string = this.monthNames[this._currentDate.getMonth()]; + @property() currentMonthIndex: number = this._currentDate.getMonth(); + @property() currentYear: number = this._currentDate.getFullYear(); + + // 'selected' refers to the active selected values of day, Month and Year the user selected + // which is updated in the input box + @property() selectedDay!: number | null; + @property() selectedMonthIndex: number = this._currentDate.getMonth(); + @property() selectedMonthSelection: string = this.monthNames[this._currentDate.getMonth()]; + @property() selectedYear: number = this._currentDate.getFullYear(); + @property() formattedDate = ''; // The value which is updated in the date-picker input box + @property() errorMessage!: string; // Handle the error message on invalid date + @property() dateSelected!: Date; // The date selected by the user + @property() dayToBeFocused!: number; // Handles the day that needs to be focused + @property() dayToBeFocusedRef!: HTMLButtonElement | undefined; // Reference of the day that needs to be focused + @property() firstDayToBeFocused!: number; // Handles the day to be focused on popover open + @property() dateFormat: string = getDatePatternFromLocale(this.localizationLanguageCode, this.dateFormatInput); // Date format + private minYear: number = new Date(this.minDate).getFullYear(); // Minimum Valid Year + private maxYear: number = new Date(this.maxDate).getFullYear(); // Maximum Valid Year + + constructor() { + super(); + } + + connectedCallback() { + super.connectedCallback(); + document.addEventListener('click', this._onOutsideClick); + this.#init(); + if (this.translationLanguageCode) { + this.monthNames = getMonthNamesFromLocale(this.translationLanguageCode); + } + if (this.dateFormatInput || this.localizationLanguageCode) { + this.dateFormat = getDatePatternFromLocale(this.localizationLanguageCode, this.dateFormatInput); + } + } + render() { + this.minDate = new Date(this.minDate); + this.maxDate = new Date(this.maxDate); + const invalidIconClasses = { isDateInvalid: !this.isDateValid, hidden: this.isDateValid }; + const invalidTextClasses = { showInvalidText: !this.isDateValid, hidden: this.isDateValid }; + const invalidInputClasses = { invalidDateInput: !this.isDateValid }; + return html` - +
+
+
+
+ + +
${this.errorMessage}
+
+ +
+ + + +
+
+
+
+
+
+ + +
+
+ + + + +
+
+ + +
+
+
+ ${days.map((day: string) => { + return html` +
+ ${day} +
+ `; + })} +
+ + +
+
+
+
+
+
+
`; } + + async #init() { + await this.updateComplete; + this.#setInputDate(); + } + + disconnectedCallback() { + super.disconnectedCallback(); + document.removeEventListener('click', this._onOutsideClick); + } + + // Function to handle the closing of popover and month select popup on outside click + @bound private _onOutsideClick(event: MouseEvent) { + const path = event.composedPath(); + if (!path.includes(this._popover)) { + if (this.monthExpand) { + this.monthExpand = false; + } else { + this._popover.hide(); + } + } + } + + // Function to set the date input from parent component + #setInputDate() { + if (this.inputDate) { + this.selectedDay = new Date(this.inputDate).getDate(); + this.selectedMonthIndex = new Date(this.inputDate).getMonth(); + this.selectedYear = new Date(this.inputDate).getFullYear(); + this.selectedMonthSelection = this.monthNames[this.selectedMonthIndex]; + this.currentMonthIndex = new Date(this.inputDate).getMonth(); + this.currentMonthSelection = this.monthNames[this.currentMonthIndex]; + this.currentYear = new Date(this.inputDate).getFullYear(); + this.dateSelected = this.inputDate; + this.#setDayToBeFocused(); + this.#getFormattedDate(); + } + } + + // Function to get the reference of the button that needs to be focused + #getDayRefToBeFocused(event: FocusChangeEvent) { + this.dayToBeFocusedRef = event.dateToBeFocusedRef; + } + + // Function to get the month user selected + #getCurrentMonth(event: MonthChangeEvent) { + this.currentMonthSelection = event.name; + this.currentMonthIndex = event.index; + this.#setDayToBeFocused(); + } + + // Function to get the year user entered + #getCurrentYear(event: CustomEvent) { + if (event.detail < 100) { + this.currentYear = this.#validateYearInput(event.detail); + } else { + this.currentYear = event.detail; + } + this.#setDayToBeFocused(); + } + + // Function to get the year user entered on KeyUp/KeyDown + #getCurrentStepValue(event: CustomEvent) { + this.currentYear = this.#validateYearInput(event.detail); + this.#setDayToBeFocused(); + } + + // Function to check if the year entered by the user is valid or not + #validateYearInput(year: number) { + this.minYear = new Date(this.minDate).getFullYear(); + this.maxYear = new Date(this.maxDate).getFullYear(); + const yearInput: number = year; + + this.isValid = yearInput > this.minYear && yearInput < this.maxYear; + if (this.isValid) { + return yearInput; + } else if (yearInput < this.minYear) { + let inputYear: number; + const date: Date = new Date(year, this.currentMonthIndex, this.selectedDay ? + this.selectedDay : this._currentDate.getDate()); + + if (date.getFullYear() >= this.minYear) { + inputYear = date.getFullYear(); + } else { + inputYear = year; + } + return inputYear; + } else if (yearInput > this.maxYear) { + return this.maxYear; + } else { + return this.currentYear; + } + } + + // Function to get the year and month on previous button click + #getPreviousMonthAndYear(event: PreviousButtonClickEvent) { + this.currentMonthSelection = this.monthNames[event.month]; + this.currentMonthIndex = event.month; + this.currentYear = event.year; + this.#setDayToBeFocused(); + } + + // Function to get the year and month on next button click + #getNextMonthAndYear(event: NextButtonClickEvent) { + this.currentMonthSelection = this.monthNames[event.month]; + this.currentMonthIndex = event.month; + this.currentYear = event.year; + this.#setDayToBeFocused(); + } + + // Function to get the day to be focused on KeyDown + #getFocusDate(event: KeyboardNavigationFocusEvent) { + this.currentMonthSelection = this.monthNames[event.month]; + this.currentMonthIndex = event.month; + this.currentYear = event.year; + this.dayToBeFocused = event.day; + } + + // Function to get the selected day and set and update the input box + #getDaySelected(event: DayChangeEvent) { + this.selectedDay = event.day; + this.currentMonthSelection = this.monthNames[event.month]; + this.currentMonthIndex = event.month; + this.selectedMonthIndex = event.month; + this.selectedYear = event.year; + this.currentYear = event.year; + this.selectedMonthSelection = this.monthNames[event.month]; + this.monthExpand = false; + this.#getFormattedDate(); + this.#dispatchSelectedDate(event); + this.isDateValid = true; + this._popover.hide(); + } + + // Function to dispatch selected date values to the parent component + #dispatchSelectedDate(event: Event) { + if (this.selectedDay) { + this.dateSelected = new Date(this.selectedYear, this.selectedMonthIndex, this.selectedDay); + this.dispatchEvent(new DateChangeEvent(event, this.dateSelected)); + } else { + this.dispatchEvent(new DateChangeEvent(event, null)); + } + } + + // Function to get the month select expansion state + #getMonthExpandState(event: MonthPopupStateChangeEvent) { + this.monthExpand = event.isExpanded; + } + + // Function to handle opening of Popover + #openPopover() { + if (this.isDateValid) { + this.currentMonthIndex = this.selectedMonthIndex; + this.currentYear = this.selectedYear; + this.currentMonthSelection = this.selectedMonthSelection; + if (this.selectedDay) { + this.#getFormattedDate(); + this.firstDayToBeFocused = this.selectedDay; + } else { + this.firstDayToBeFocused = this._currentDate.getDate(); + } + } else { + this.currentMonthIndex = this._currentDate.getMonth(); + this.currentYear = this._currentDate.getFullYear(); + this.currentMonthSelection = this.monthNames[this.currentMonthIndex]; + this.firstDayToBeFocused = this._currentDate.getDate(); + this.selectedDay = null; + } + + this.#setDayToBeFocused(); + setTimeout(() => { + this.dayToBeFocusedRef?.focus(); + }, 50); + } + + // Function to set the day to be focused + #setDayToBeFocused() { + const totalDays: number = new Date(this.currentYear, this.currentMonthIndex + 1, 0).getDate(); + if (this.isDateValid && this.selectedDay) { + this.dayToBeFocused = this.selectedDay <= totalDays ? this.selectedDay : totalDays; + } else { + this.dayToBeFocused = this._currentDate.getDate(); + } + } + + // Function to handle click on date input box + #dateInputClick() { + this._popover.hide(); + setTimeout(() => { + this._textInput.focus(); + }, 1); + } + + // Function to handle date input by user + #onInput(event: Event) { + const { value } = event.target as HTMLInputElement; + this.formattedDate = value; + + this.isDateValid = this.#isValidDate(value); + + if (this.isDateValid && value !== '') { + const dateValues = getDateValues(value, this.localizationLanguageCode, this.dateFormatInput); + this.selectedDay = dateValues.day; + this.currentMonthIndex = dateValues.month - 1; + this.currentMonthSelection = this.monthNames[this.currentMonthIndex]; + this.currentYear = dateValues.year; + this.selectedMonthIndex = dateValues.month - 1; + this.selectedYear = dateValues.year; + this.selectedMonthSelection = this.monthNames[this.selectedMonthIndex]; + this.#dispatchSelectedDate(event); + } else if (this.isDateValid && value === '') { + this.selectedDay = null; + this.currentMonthIndex = this._currentDate.getMonth(); + this.currentMonthSelection = this.monthNames[this.currentMonthIndex]; + this.currentYear = this._currentDate.getFullYear(); + this.selectedMonthIndex = this._currentDate.getMonth(); + this.selectedYear = this._currentDate.getFullYear(); + this.selectedMonthSelection = this.monthNames[this.selectedMonthIndex]; + this.#dispatchSelectedDate(event); + } + } + + // Function to check if the date user entered is valid or not + #isValidDate(dateString: string) { + let isValid = true; + if (dateString) { + const monthLength: number[] = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; + // const regex = /^[0-9./-]*$/g; // Commented for reference + + // Parse the date parts to integers + const dateValues = getDateValues(dateString, this.localizationLanguageCode, this.dateFormatInput); + const selectedDayInput: number = dateValues.day; + const selectedMonthInput: number = dateValues.month; + const selectedYearInput: number = dateValues.year; + const date: Date = new Date(selectedYearInput, selectedMonthInput - 1, selectedDayInput); + const regex = new RegExp(getRegexPattern(this.localizationLanguageCode, this.dateFormatInput)); + + if (date.toString() === 'Invalid Date') { + this.errorMessage = this.errorMessages.inValid; + return false; + } + if (date < this.minDate) { + this.errorMessage = this.errorMessages.lessThanMinDate; + return false; + } + if (date > this.maxDate) { + this.errorMessage = this.errorMessages.greaterThanMaxDate; + return false; + } + + // if (!dateString.match(regex)) { // Commented for reference + if (!regex.test(dateString)) { + isValid = false; + this.errorMessage = this.errorMessages.inValid; + return isValid; + } + + // Check the ranges of month and year + if (selectedYearInput < this.minYear || selectedYearInput > this.maxYear || + selectedMonthInput === 0 || selectedMonthInput > 12) { + this.errorMessage = this.errorMessages.inValid; + return false; + } + + // Adjust for leap years + if (selectedYearInput % 400 === 0 || (selectedYearInput % 100 !== 0 && selectedYearInput % 4 === 0)) { + monthLength[1] = 29; + } + + // Check the range of the day + isValid = selectedDayInput > 0 && selectedDayInput <= monthLength[selectedMonthInput - 1]; + if (!isValid) { + this.errorMessage = this.errorMessages.inValid; + } + } else if (dateString === '') { + isValid = true; + } + + return isValid; + } + + // Function to get the formatted date string according to date format selected in parent component + #getFormattedDate() { + const monthSelected: number = this.currentMonthIndex + 1; // Months start at 0! + const daySelected: number = this.selectedDay ? this.selectedDay : this._currentDate.getDate(); + const yearSelected: number = this.selectedYear; + let dd: string = daySelected.toString(); + let mm: string = monthSelected.toString(); + const yyyy: string = yearSelected.toString(); + + if (daySelected < 10) { + dd = `0${dd}`; + } + if (monthSelected < 10) { + mm = `0${mm}`; + } + + this.formattedDate = getDateFormat(dd, mm, yyyy, this.localizationLanguageCode, this.dateFormatInput); + } } declare global { diff --git a/elements/pf-date-picker/pf-month-select.css b/elements/pf-date-picker/pf-month-select.css index 5d4e87f30f..f2a6f17748 100644 --- a/elements/pf-date-picker/pf-month-select.css +++ b/elements/pf-date-picker/pf-month-select.css @@ -1,3 +1,82 @@ :host { display: block; } + +.month-select-container { + font-family: var(--rh-font-family-body-text, RedHatText, "Red Hat Text", "Noto Sans Arabic", "Noto Sans Hebrew", "Noto Sans JP", "Noto Sans KR", "Noto Sans Malayalam", "Noto Sans SC", "Noto Sans TC", "Noto Sans Thai", Helvetica, Arial, sans-serif); + position: relative; + width: 140px; +} + +pf-button#date-picker-month-select::part(button) { + width: 140px; + background: transparent; + font-family: var(--rh-font-family-body-text, RedHatText, "Red Hat Text", "Noto Sans Arabic", "Noto Sans Hebrew", "Noto Sans JP", "Noto Sans KR", "Noto Sans Malayalam", "Noto Sans SC", "Noto Sans TC", "Noto Sans Thai", Helvetica, Arial, sans-serif); + text-align: left; +} + +pf-button#date-picker-month-select:before { + content: ""; + position: absolute; + top: 1px; + right: 0; + width: 36px; + height: 34px; + display: flex; + align-items: center; + justify-content: center; +} + +.date-month-select-icon { + position: absolute; + top: 50%; + transform: translateY(-50%); + right: 16px; + width: 36px; + height: 34px; + display: flex; + align-items: center; + justify-content: center; +} + +div#date-picker-month-select-popup { + width: 140px; + background: var(--rh-color-white, #ffffff); + box-shadow: 0 0.25rem 0.5rem 0 rgba(3, 3, 3, 0.12), 0 0 0.25rem 0 rgba(3, 3, 3, 0.06); + position: absolute; + top: 36px; + left: 0; + z-index: 1; +} + +div#date-picker-month-select-popup ul { + margin-block-start: 0; + margin-block-end: 0; + padding-inline-start: 0; + list-style: none; + padding: 8px 0; +} + +div#date-picker-month-select-popup pf-button { + --pf-c-button--m-plain--Color: var(--rh-color-canvas-black, #151515); + --pf-c-button--m-plain--hover--BackgroundColor: var(--rh-color-blue-50, #e7f1fa); + + width: 100%; + margin-bottom: .125rem; +} + +div#date-picker-month-select-popup pf-button::part(button) { + background: var(--rh-color-white, #ffffff); + text-align: left; + font-family: var(--rh-font-family-body-text, RedHatText, "Red Hat Text", "Noto Sans Arabic", "Noto Sans Hebrew", "Noto Sans JP", "Noto Sans KR", "Noto Sans Malayalam", "Noto Sans SC", "Noto Sans TC", "Noto Sans Thai", Helvetica, Arial, sans-serif); + width: 100%; + border-radius: 0; +} + +div#date-picker-month-select-popup pf-button::part(button):hover { + background:#f0f0f0; +} + +pf-button::part(button):focus, pf-button::part(button):focus-visible { + outline: var(--rh-color-accent-base-on-light, #0066cc) auto 2px; +} \ No newline at end of file diff --git a/elements/pf-date-picker/pf-month-select.ts b/elements/pf-date-picker/pf-month-select.ts index 0166215bcb..da2ed506a5 100644 --- a/elements/pf-date-picker/pf-month-select.ts +++ b/elements/pf-date-picker/pf-month-select.ts @@ -1,7 +1,25 @@ import { LitElement, html } from 'lit'; import { customElement } from 'lit/decorators/custom-element.js'; - +import { property } from 'lit/decorators/property.js'; +import { bound } from '@patternfly/pfe-core/decorators.js'; +import { query } from 'lit/decorators/query.js'; import styles from './pf-month-select.css'; +import { createRef, ref } from 'lit/directives/ref.js'; +import { ifDefined } from 'lit/directives/if-defined.js'; +import { getMonthNamesFromLocale } from './date-picker-helper.js'; +import { ComposedEvent } from '@patternfly/pfe-core/core.js'; + +export class MonthChangeEvent extends ComposedEvent { + constructor(public event: Event, public name: string, public index: number) { + super('currentMonth'); + } +} + +export class MonthPopupStateChangeEvent extends ComposedEvent { + constructor(public event: Event, public isExpanded: boolean) { + super('monthExpandState'); + } +} /** * Month Select @@ -11,11 +29,114 @@ import styles from './pf-month-select.css'; export class PfMonthSelect extends LitElement { static readonly styles = [styles]; + // Input properties from the parent + @property() translationLanguageCode!: string; + @property() monthNames: string[] = getMonthNamesFromLocale(this.translationLanguageCode); + @property() currentDate: Date = new Date(); + @property() currentMonthName: string = this.monthNames[this.currentDate.getMonth()]; + @property() currentMonthIndex: number = this.currentDate.getMonth(); + @property() isMonthExpanded = false; + @property() monthToBeFocused!: number; + + focusMonthRef = createRef(); // Reference to the month that needs to be focused on keyboard navigation + + @query('#date-picker-month-select') private datePickerMonthToggle!: HTMLButtonElement; + + connectedCallback() { + super.connectedCallback(); + document.addEventListener('click', this._onOutsideClick); + if (this.translationLanguageCode) { + this.monthNames = getMonthNamesFromLocale(this.translationLanguageCode); + this.currentMonthName = this.monthNames[this.currentDate.getMonth()]; + } + } + render() { return html` - +
+
+ + ${this.currentMonthName} + +
+
+
    + ${this.monthNames.map((month: string, key: number) => { + return html` +
  • + this.#selectMonth(event, month, key)}>${month} + +
  • `; + })} +
+
+
`; } + + disconnectedCallback() { + super.disconnectedCallback(); + document.removeEventListener('click', this._onOutsideClick); + } + + // Function to focus month on keyboard navigation + updated(changedProperties: Map) { + if (changedProperties.has('monthToBeFocused')) { + const month: HTMLButtonElement | undefined = this.focusMonthRef.value!; + month?.focus(); + } + } + + // Function to handle closing of month select popup on clicking outside + @bound private _onOutsideClick(event: MouseEvent) { + const path = event.composedPath(); + if (!path.includes(this.datePickerMonthToggle) && this.isMonthExpanded) { + this.isMonthExpanded = false; + } + } + + // Function to dispatch selected month + #selectMonth(event: Event, monthName: string, monthIndex: number) { + this.currentMonthIndex = monthIndex; + if (monthName && (monthName.toString() !== this.currentMonthName)) { + this.currentMonthName = monthName.toString(); + this.dispatchEvent(new MonthChangeEvent(event, this.currentMonthName, this.currentMonthIndex)); + } + this.isMonthExpanded = !this.isMonthExpanded; + } + + // Function to handle focus on keyboard navigation + #onMonthSelectKeydown(event: KeyboardEvent) { + switch (event.key) { + case 'ArrowUp': + if (this.monthToBeFocused > 0) { + this.monthToBeFocused = this.monthToBeFocused - 1; + } else { + this.monthToBeFocused = 11; + } + break; + case 'ArrowDown': + if (this.monthToBeFocused < 11) { + this.monthToBeFocused = this.monthToBeFocused + 1; + } else { + this.monthToBeFocused = 0; + } + break; + } + } + + // Function to dispatch value to handle the opening and closing of month select popup + #showMonthSelect(event: Event) { + this.isMonthExpanded = !this.isMonthExpanded; + this.monthToBeFocused = -1; + this.dispatchEvent(new MonthPopupStateChangeEvent(event, this.isMonthExpanded)); + } } declare global { diff --git a/elements/pf-date-picker/pf-next-button.css b/elements/pf-date-picker/pf-next-button.css index 5d4e87f30f..01b1c01b80 100644 --- a/elements/pf-date-picker/pf-next-button.css +++ b/elements/pf-date-picker/pf-next-button.css @@ -1,3 +1,19 @@ :host { display: block; } + +.date-previous-next-button::part(button) { + --pf-c-button--m-plain--Color: #6a6e73; + + width: 36px; + height: 36px; + display: flex; + align-items: center; + justify-content: center; + background: var(--rh-color-white, #ffffff); + border: none; +} + +pf-button::part(button):focus, pf-button::part(button):focus-visible { + outline: var(--rh-color-accent-base-on-light, #0066cc) auto 2px; +} \ No newline at end of file diff --git a/elements/pf-date-picker/pf-next-button.ts b/elements/pf-date-picker/pf-next-button.ts index 3f95403e5c..3b497576db 100644 --- a/elements/pf-date-picker/pf-next-button.ts +++ b/elements/pf-date-picker/pf-next-button.ts @@ -1,8 +1,16 @@ import { LitElement, html } from 'lit'; import { customElement } from 'lit/decorators/custom-element.js'; +import { property } from 'lit/decorators/property.js'; +import { ComposedEvent } from '@patternfly/pfe-core/core.js'; import styles from './pf-next-button.css'; +export class NextButtonClickEvent extends ComposedEvent { + constructor(public event: Event, public month: number, public year: number) { + super('nextMonthAndYear'); + } +} + /** * Next Button * @slot - Place element content here @@ -11,11 +19,31 @@ import styles from './pf-next-button.css'; export class PfNextButton extends LitElement { static readonly styles = [styles]; + // Input properties from the parent + @property() currentYear: number = (new Date).getFullYear(); + @property() currentMonth: number = (new Date).getMonth(); + render() { return html` - + + + `; } + + // Function to dispatch month and year + #nextMonth(event: Event) { + // Increment the current month by one + this.currentMonth++; + + // If the current month is greater than 11 (December), set it to 0 (January) and increment the current year + if (this.currentMonth > 11) { + this.currentMonth = 0; + this.currentYear++; + } + + this.dispatchEvent(new NextButtonClickEvent(event, this.currentMonth, this.currentYear)); + } } declare global { diff --git a/elements/pf-date-picker/pf-previous-button.css b/elements/pf-date-picker/pf-previous-button.css index 5d4e87f30f..a645c25931 100644 --- a/elements/pf-date-picker/pf-previous-button.css +++ b/elements/pf-date-picker/pf-previous-button.css @@ -1,3 +1,19 @@ :host { display: block; } + +.date-previous-next-button::part(button) { + --pf-c-button--m-plain--Color: #6a6e73; + + width: 36px; + height: 36px; + display: flex; + align-items: center; + justify-content: center; + background: var(--rh-color-white, #ffffff); + border: none; +} + +pf-button::part(button):focus, pf-button::part(button):focus-visible { + outline: var(--rh-color-accent-base-on-light, #0066cc) auto 2px; +} \ No newline at end of file diff --git a/elements/pf-date-picker/pf-previous-button.ts b/elements/pf-date-picker/pf-previous-button.ts index 2de42edafe..d56bc5ac5a 100644 --- a/elements/pf-date-picker/pf-previous-button.ts +++ b/elements/pf-date-picker/pf-previous-button.ts @@ -1,8 +1,14 @@ import { LitElement, html } from 'lit'; import { customElement } from 'lit/decorators/custom-element.js'; - +import { property } from 'lit/decorators/property.js'; +import { ComposedEvent } from '@patternfly/pfe-core/core.js'; import styles from './pf-previous-button.css'; +export class PreviousButtonClickEvent extends ComposedEvent { + constructor(public event: Event, public month: number, public year: number) { + super('previousMonthAndYear'); + } +} /** * Previous Button * @slot - Place element content here @@ -11,11 +17,31 @@ import styles from './pf-previous-button.css'; export class PfPreviousButton extends LitElement { static readonly styles = [styles]; + // Input properties from the parent + @property() currentYear: number = (new Date).getFullYear(); + @property() currentMonth: number = (new Date).getMonth(); + render() { return html` - + + + `; } + + // Function to dispatch month and year + #previousMonth(event: Event) { + // Decrement the current month by one + this.currentMonth--; + + // If the current month is less than 0, set it to 11 (December) and decrement the current year + if (this.currentMonth < 0) { + this.currentMonth = 11; + this.currentYear--; + } + + this.dispatchEvent(new PreviousButtonClickEvent(event, this.currentMonth, this.currentYear)); + } } declare global { diff --git a/elements/pf-date-picker/pf-year-input.css b/elements/pf-date-picker/pf-year-input.css index 5d4e87f30f..0bf1506dc7 100644 --- a/elements/pf-date-picker/pf-year-input.css +++ b/elements/pf-date-picker/pf-year-input.css @@ -1,3 +1,21 @@ :host { display: block; } + +.date-year-input-container input { + height: 36px; + box-sizing: border-box; + border-radius: 0; + border: 1px solid #f0f0f0; + border-left: none; + border-bottom: 1px solid #8b8d90; + padding: 6px 8px; + font-size: var(--rh-font-size-body-text-md, 1rem); + font-family: var(--rh-font-family-body-text, RedHatText, "Red Hat Text", "Noto Sans Arabic", "Noto Sans Hebrew", "Noto Sans JP", "Noto Sans KR", "Noto Sans Malayalam", "Noto Sans SC", "Noto Sans TC", "Noto Sans Thai", Helvetica, Arial, sans-serif); + width: 80px; +} + +.date-year-input-container input:focus-visible { + border-left: 2px; + outline: var(--rh-color-accent-base-on-light, #0066cc) auto 2px; +} \ No newline at end of file diff --git a/elements/pf-date-picker/pf-year-input.ts b/elements/pf-date-picker/pf-year-input.ts index eed6d847c9..f6a3553808 100644 --- a/elements/pf-date-picker/pf-year-input.ts +++ b/elements/pf-date-picker/pf-year-input.ts @@ -1,5 +1,7 @@ import { LitElement, html } from 'lit'; import { customElement } from 'lit/decorators/custom-element.js'; +import { property } from 'lit/decorators/property.js'; +import { query } from 'lit/decorators/query.js'; import styles from './pf-year-input.css'; @@ -11,13 +13,72 @@ import styles from './pf-year-input.css'; export class PfYearInput extends LitElement { static readonly styles = [styles]; + // Input properties from the parent + @property() currentYear: number = (new Date).getFullYear(); + @property() isValid = true; + @property() minDate!: Date; + @property() maxDate!: Date; + + private minYear: number = new Date(this.minDate).getFullYear(); + private maxYear: number = new Date(this.maxDate).getFullYear(); + + @query('#year-input') _numberInput!: HTMLInputElement; + render() { return html` - +
+ +
`; } + + // Function to hanlde year input on input + #OnInput(event: Event) { + const { value } = event.target as HTMLInputElement; + this.#dispatchInputYear(parseInt(value), 'setCurrentYear'); + } + + // Function to handle year input on step + #onChange(event: Event) { + const { value } = event.target as HTMLInputElement; + this.#dispatchInputYear(parseInt(value), 'currentYearOnStep'); + } + + // Function to dispatch year input + #dispatchInputYear(value: number, eventName: string) { + if (isNaN(value)) { + this.currentYear = 0; + } else { + this.currentYear = value; + } + + const options = { + detail: this.currentYear, + bubbles: true, + composed: true + }; + this.dispatchEvent(new CustomEvent(eventName, options)); + } + + // Function to handle year input on keyUp + #onKeyUp(event: KeyboardEvent) { + if (event.key === 'ArrowUp' || event.key === 'ArrowDown') { + this.#dispatchInputYear(this.currentYear, 'currentYearOnStep'); + } + } } + declare global { interface HTMLElementTagNameMap { 'pf-year-input': PfYearInput; From 549822bf43d8d915b026cd93447fa126b9a5f125 Mon Sep 17 00:00:00 2001 From: Arathy Kumar Date: Thu, 28 Mar 2024 12:33:21 +0530 Subject: [PATCH 03/15] fix: code updated to work with pf-3 --- elements/package.json | 2 +- elements/pf-date-picker/demo/demo.css | 23 ------------- .../pf-date-picker/demo/pf-date-picker.html | 34 +++++++++++++++++-- .../pf-date-picker/demo/pf-date-picker.js | 1 - elements/pf-date-picker/pf-calendar.ts | 4 +-- elements/pf-date-picker/pf-month-select.ts | 2 +- 6 files changed, 35 insertions(+), 31 deletions(-) delete mode 100644 elements/pf-date-picker/demo/demo.css delete mode 100644 elements/pf-date-picker/demo/pf-date-picker.js diff --git a/elements/package.json b/elements/package.json index b0df4c4114..88ad91e74f 100644 --- a/elements/package.json +++ b/elements/package.json @@ -33,9 +33,9 @@ "./pf-clipboard-copy/pf-clipboard-copy.js": "./pf-clipboard-copy/pf-clipboard-copy.js", "./pf-code-block/BaseCodeBlock.js": "./pf-code-block/BaseCodeBlock.js", "./pf-code-block/pf-code-block.js": "./pf-code-block/pf-code-block.js", + "./pf-date-picker/pf-date-picker.js": "./pf-date-picker/pf-date-picker.js", "./pf-date-picker/date-picker-helper.js": "./pf-date-picker/date-picker-helper.js", "./pf-date-picker/pf-calendar.js": "./pf-date-picker/pf-calendar.js", - "./pf-date-picker/pf-date-picker.js": "./pf-date-picker/pf-date-picker.js", "./pf-date-picker/pf-month-select.js": "./pf-date-picker/pf-month-select.js", "./pf-date-picker/pf-next-button.js": "./pf-date-picker/pf-next-button.js", "./pf-date-picker/pf-previous-button.js": "./pf-date-picker/pf-previous-button.js", diff --git a/elements/pf-date-picker/demo/demo.css b/elements/pf-date-picker/demo/demo.css deleted file mode 100644 index d25dcffa52..0000000000 --- a/elements/pf-date-picker/demo/demo.css +++ /dev/null @@ -1,23 +0,0 @@ -.container { - min-height: 25vh; - height: auto; - width: 100%; - display: flex; - align-items: flex-start; - justify-content: flex-start; - padding: 40px; - flex-direction: column; -} - -.container section { - margin-bottom: var(--rh-space-lg, 16px) -} - -.container section p span { - color: var(--rh-color-text-secondary-on-light, #4d4d4d); - font-family: var(--rh-font-family-code, RedHatMono, "Red Hat Mono", "Courier New", Courier, monospace); -} - -.container section pre { - padding-bottom: var(--rh-space-md, 8px) -} \ No newline at end of file diff --git a/elements/pf-date-picker/demo/pf-date-picker.html b/elements/pf-date-picker/demo/pf-date-picker.html index b51fe6b2ec..41857d2cdb 100644 --- a/elements/pf-date-picker/demo/pf-date-picker.html +++ b/elements/pf-date-picker/demo/pf-date-picker.html @@ -1,6 +1,3 @@ - - -

Basic

@@ -73,3 +70,34 @@

Disabled

+ + + + + diff --git a/elements/pf-date-picker/demo/pf-date-picker.js b/elements/pf-date-picker/demo/pf-date-picker.js deleted file mode 100644 index 74c7d8c58c..0000000000 --- a/elements/pf-date-picker/demo/pf-date-picker.js +++ /dev/null @@ -1 +0,0 @@ -import '@patternfly/elements/pf-date-picker/pf-date-picker.js'; diff --git a/elements/pf-date-picker/pf-calendar.ts b/elements/pf-date-picker/pf-calendar.ts index 1f8f9124c7..476fb0489d 100644 --- a/elements/pf-date-picker/pf-calendar.ts +++ b/elements/pf-date-picker/pf-calendar.ts @@ -68,7 +68,7 @@ export class PfCalendar extends LitElement { @property() maxDate!: Date; @property() translationLanguageCode!: string; @property() monthNames: string[] = getMonthNamesFromLocale(this.translationLanguageCode); - focusDateRef = createRef(); // Reference to the button that needs to be focused + focusDateRef: any = createRef(); // Reference to the button that needs to be focused constructor() { super(); @@ -206,7 +206,7 @@ export class PfCalendar extends LitElement { // Function to focus date button; #setDateFocus() { - const date: HTMLButtonElement | undefined = this.focusDateRef.value!; + const date: HTMLButtonElement = this.focusDateRef.value!; this.focusRef = date; setTimeout(() => { date?.focus(); diff --git a/elements/pf-date-picker/pf-month-select.ts b/elements/pf-date-picker/pf-month-select.ts index da2ed506a5..99cd5fe0f4 100644 --- a/elements/pf-date-picker/pf-month-select.ts +++ b/elements/pf-date-picker/pf-month-select.ts @@ -38,7 +38,7 @@ export class PfMonthSelect extends LitElement { @property() isMonthExpanded = false; @property() monthToBeFocused!: number; - focusMonthRef = createRef(); // Reference to the month that needs to be focused on keyboard navigation + focusMonthRef: any = createRef(); // Reference to the month that needs to be focused on keyboard navigation @query('#date-picker-month-select') private datePickerMonthToggle!: HTMLButtonElement; From 08820f37383c5e9e710a830dedba73fb37dd787f Mon Sep 17 00:00:00 2001 From: Arathy Kumar Date: Thu, 28 Mar 2024 13:28:33 +0530 Subject: [PATCH 04/15] fix: translation and popover fixes updated --- elements/pf-date-picker/pf-date-picker.css | 2 +- elements/pf-date-picker/pf-date-picker.ts | 82 +++++++++++++++++++--- elements/pf-date-picker/pf-month-select.ts | 5 ++ 3 files changed, 80 insertions(+), 9 deletions(-) diff --git a/elements/pf-date-picker/pf-date-picker.css b/elements/pf-date-picker/pf-date-picker.css index f13250ca69..7adc51dd19 100644 --- a/elements/pf-date-picker/pf-date-picker.css +++ b/elements/pf-date-picker/pf-date-picker.css @@ -85,7 +85,7 @@ } .date-input-box { - width: 192px; + width: auto; background-color: transparent; display: flex; align-items: flex-start; diff --git a/elements/pf-date-picker/pf-date-picker.ts b/elements/pf-date-picker/pf-date-picker.ts index 3757e7ffae..a3c9072b2a 100644 --- a/elements/pf-date-picker/pf-date-picker.ts +++ b/elements/pf-date-picker/pf-date-picker.ts @@ -68,17 +68,26 @@ export class PfDatePicker extends LitElement { @query('#date-input') _textInput!: HTMLInputElement; // Date picker input box reference @query('#popover') private _popover!: PfPopover; // Popover reference - // ----------- Input properties from parent ------------ // + // ------------------------Input properties from parent-------------------------------// + // -----------[***Only these properties can be used to pass data from parent***] ------------ // @property({ reflect: true }) minDate: Date = new Date(1900, 0, 1); // Default minimum valid date set as 1st January 1900 @property({ reflect: true }) maxDate: Date = new Date(9999, 11, 31); // Default maximum valid date set as 31st December 9999 - @property({ reflect: true }) inputDate!: Date; // Handle date value parent value sends to the component + + @property({ reflect: true }) inputDateWithUniqueTimeStamp!: string; // Handle date value with a unique time stamp that parent sends to the component. + // The format: inputDateWithUniqueTimeStamp = (new Date(2024, 3, 2)).toDateString() +'#'+ (Date.now() + Math.random()) // + @property({ reflect: true }) isDisabled = false; // Handles if the date picker is disabled or not @property({ reflect: true }) localizationLanguageCode!: string; // Language code for date format based on localization @property({ reflect: true }) translationLanguageCode!: string; // Language code for translation of date input and month names @property({ reflect: true }) dateFormatInput!: // Date format input from parent 'DD/MM/YYYY' | 'MM/DD/YYYY' | 'YYYY/MM/DD' | 'YYYY/DD/MM' | 'DD-MM-YYYY' | 'MM-DD-YYYY' | 'YYYY-MM-DD' | 'YYYY-DD-MM' | 'DD.MM.YYYY' | 'MM.DD.YYYY' | 'YYYY.MM.DD' | 'YYYY.DD.MM'; - // ------------- // + + @property({ reflect: true }) placeholderTextWithUniqueCode!: string; // Placeholder from parent + // The format: placeholderTextWithUniqueCode = 'placeholder-text' + '#' + Math.random(); + + // ----------------------Input properties from parent ends--------------------------------- // + // -------------------------------------------------------// // 'current' refers to the temporary values of Month and Year the user selected before day is selected // and the input box is updated @@ -100,6 +109,7 @@ export class PfDatePicker extends LitElement { @property() dayToBeFocusedRef!: HTMLButtonElement | undefined; // Reference of the day that needs to be focused @property() firstDayToBeFocused!: number; // Handles the day to be focused on popover open @property() dateFormat: string = getDatePatternFromLocale(this.localizationLanguageCode, this.dateFormatInput); // Date format + @property() inputDate!: Date | null; // Handle and format date input value parent sends to the component private minYear: number = new Date(this.minDate).getFullYear(); // Minimum Valid Year private maxYear: number = new Date(this.maxDate).getFullYear(); // Maximum Valid Year @@ -119,9 +129,28 @@ export class PfDatePicker extends LitElement { } } + willUpdate(changedProperties: Map) { + if (changedProperties.has('minDate')) { + this.minDate = new Date(this.minDate); + } + + if (changedProperties.has('maxDate')) { + this.maxDate = new Date(this.maxDate); + } + + if (changedProperties.has('inputDateWithUniqueTimeStamp')) { + // The unique timestamp is used to re-render the date-picker on date set and date reset + this.inputDate = new Date(this.inputDateWithUniqueTimeStamp.split('#')[0]); + this.#setInputDate(); + } + + if (changedProperties.has('placeholderTextWithUniqueCode')) { + const [placeholder, uniqueCode] = this.placeholderTextWithUniqueCode.split('#'); + this.dateFormat = placeholder; + } + } + render() { - this.minDate = new Date(this.minDate); - this.maxDate = new Date(this.maxDate); const invalidIconClasses = { isDateInvalid: !this.isDateValid, hidden: this.isDateValid }; const invalidTextClasses = { showInvalidText: !this.isDateValid, hidden: this.isDateValid }; const invalidInputClasses = { invalidDateInput: !this.isDateValid }; @@ -141,6 +170,7 @@ export class PfDatePicker extends LitElement { .disabled=${this.isDisabled} placeholder=${this.dateFormat} class=${classMap(invalidInputClasses)} + part="input" />
${this.errorMessage}
@@ -155,7 +185,7 @@ export class PfDatePicker extends LitElement { variant="control" part="toggle-button" class="date-picker-calendar-icon"> - +
@@ -171,7 +201,8 @@ export class PfDatePicker extends LitElement {
{ + this.#removePFPopoverDialogFromDOM(); + }, 100); this.#setInputDate(); } @@ -250,7 +286,9 @@ export class PfDatePicker extends LitElement { // Function to set the date input from parent component #setInputDate() { - if (this.inputDate) { + if (this.inputDate?.toString() === 'Invalid Date') { + this.#clearDateSelection(); + } else if (this.inputDate) { this.selectedDay = new Date(this.inputDate).getDate(); this.selectedMonthIndex = new Date(this.inputDate).getMonth(); this.selectedYear = new Date(this.inputDate).getFullYear(); @@ -264,6 +302,18 @@ export class PfDatePicker extends LitElement { } } + // Function to clear date selected + #clearDateSelection() { + this.selectedDay = null; + this.currentMonthIndex = this._currentDate.getMonth(); + this.currentMonthSelection = this.monthNames[this.currentMonthIndex]; + this.currentYear = this._currentDate.getFullYear(); + this.selectedMonthIndex = this._currentDate.getMonth(); + this.selectedYear = this._currentDate.getFullYear(); + this.selectedMonthSelection = this.monthNames[this.selectedMonthIndex]; + this.formattedDate = ''; + } + // Function to get the reference of the button that needs to be focused #getDayRefToBeFocused(event: FocusChangeEvent) { this.dayToBeFocusedRef = event.dateToBeFocusedRef; @@ -376,6 +426,7 @@ export class PfDatePicker extends LitElement { // Function to handle opening of Popover #openPopover() { + this.#addPFPopoverDialogToDOM(); if (this.isDateValid) { this.currentMonthIndex = this.selectedMonthIndex; this.currentYear = this.selectedYear; @@ -524,6 +575,21 @@ export class PfDatePicker extends LitElement { this.formattedDate = getDateFormat(dd, mm, yyyy, this.localizationLanguageCode, this.dateFormatInput); } + + /* The functions #removePFPopoverDialogFromDOM() and #addPFPopoverDialogToDOM() are added in in order to prevent + the responsiveness issue created by pf-popover and can be removed once https://github.com/patternfly/patternfly-elements/issues/2648 is addressed. + + Issue: The pf-popover implementation maintains the popover dialog element in the DOM even when it is not visible or active. + This persistent presence in the DOM causes unintended visual effects, including spacing inconsistencies and + horizontal scroll issues, particularly when the popover is positioned at the far right edge of the page. */ + + #removePFPopoverDialogFromDOM() { + this._popover?.shadowRoot?.querySelector('dialog#popover')?.setAttribute('style', 'display:none'); + } + + #addPFPopoverDialogToDOM() { + this._popover?.shadowRoot?.querySelector('dialog#popover')?.setAttribute('style', 'display:block'); + } } declare global { diff --git a/elements/pf-date-picker/pf-month-select.ts b/elements/pf-date-picker/pf-month-select.ts index 99cd5fe0f4..1708def8e7 100644 --- a/elements/pf-date-picker/pf-month-select.ts +++ b/elements/pf-date-picker/pf-month-select.ts @@ -91,6 +91,11 @@ export class PfMonthSelect extends LitElement { const month: HTMLButtonElement | undefined = this.focusMonthRef.value!; month?.focus(); } + + if (changedProperties.has('translationLanguageCode')) { + this.monthNames = getMonthNamesFromLocale(this.translationLanguageCode); + this.currentMonthName = this.monthNames[this.currentMonthIndex]; + } } // Function to handle closing of month select popup on clicking outside From 19e0e1192dd1141ccf5665329ea197f854155aed Mon Sep 17 00:00:00 2001 From: Arathy Kumar Date: Thu, 28 Mar 2024 16:26:30 +0530 Subject: [PATCH 05/15] chore: split large demo files to individual files --- elements/pf-date-picker/demo/date-format.html | 44 +++++++++++++ elements/pf-date-picker/demo/date-input.html | 43 +++++++++++++ elements/pf-date-picker/demo/disabled.html | 40 ++++++++++++ .../pf-date-picker/demo/localization.html | 43 +++++++++++++ .../pf-date-picker/demo/min-and-max-date.html | 49 +++++++++++++++ .../pf-date-picker/demo/pf-date-picker.html | 62 ------------------- elements/pf-date-picker/demo/translation.html | 41 ++++++++++++ 7 files changed, 260 insertions(+), 62 deletions(-) create mode 100644 elements/pf-date-picker/demo/date-format.html create mode 100644 elements/pf-date-picker/demo/date-input.html create mode 100644 elements/pf-date-picker/demo/disabled.html create mode 100644 elements/pf-date-picker/demo/localization.html create mode 100644 elements/pf-date-picker/demo/min-and-max-date.html create mode 100644 elements/pf-date-picker/demo/translation.html diff --git a/elements/pf-date-picker/demo/date-format.html b/elements/pf-date-picker/demo/date-format.html new file mode 100644 index 0000000000..aca6c137f4 --- /dev/null +++ b/elements/pf-date-picker/demo/date-format.html @@ -0,0 +1,44 @@ +
+
+

Date Format

+
<pf-date-picker dateFormatInput="YYYY-DD-MM"></pf-date-picker>
+ +

The date picker supports the following 12 date formats:

+

'DD/MM/YYYY', 'MM/DD/YYYY', 'YYYY/MM/DD', 'YYYY/DD/MM', 'DD-MM-YYYY', 'MM-DD-YYYY', 'YYYY-MM-DD', + 'YYYY-DD-MM', 'DD.MM.YYYY', 'MM.DD.YYYY', 'YYYY.MM.DD', 'YYYY.DD.MM'

+

The date format is set globally, independent of the user's locale. + This means that all users will see the same date format, regardless of their language or region.

+
+
+ + + + + + diff --git a/elements/pf-date-picker/demo/date-input.html b/elements/pf-date-picker/demo/date-input.html new file mode 100644 index 0000000000..d837d1ef7a --- /dev/null +++ b/elements/pf-date-picker/demo/date-input.html @@ -0,0 +1,43 @@ +
+
+

Set Input Date

+
<pf-date-picker inputDate=${new Date(2023, 0, 1)}></pf-date-picker>
+ + +

The default date value can be passed to the date picker to set the initial date that is displayed.

+

In the above given example, the date set to be displayed initially is January 1, 2023.

+
+
+ + + + + + diff --git a/elements/pf-date-picker/demo/disabled.html b/elements/pf-date-picker/demo/disabled.html new file mode 100644 index 0000000000..52a084385c --- /dev/null +++ b/elements/pf-date-picker/demo/disabled.html @@ -0,0 +1,40 @@ +
+
+

Disabled

+
<pf-date-picker isDisabled="true"></pf-date-picker>
+ +

The isDisabled attribute can be used to disable the date picker.

+
+
+ + + + + + diff --git a/elements/pf-date-picker/demo/localization.html b/elements/pf-date-picker/demo/localization.html new file mode 100644 index 0000000000..3b3a97da24 --- /dev/null +++ b/elements/pf-date-picker/demo/localization.html @@ -0,0 +1,43 @@ +
+
+

Localization

+
<pf-date-picker localizationLanguageCode="fi"></pf-date-picker>
+ +

The locale string can be passed to the date picker to set the date format.

+

In the above given example, the locale is set to Finnish: fi

+

The date format is set globally, independent of the user's locale. + This means that all users will see the same date format, regardless of their language or region.

+
+
+ + + + + + diff --git a/elements/pf-date-picker/demo/min-and-max-date.html b/elements/pf-date-picker/demo/min-and-max-date.html new file mode 100644 index 0000000000..4e441d6030 --- /dev/null +++ b/elements/pf-date-picker/demo/min-and-max-date.html @@ -0,0 +1,49 @@ +
+
+

Set minimum and maximum date range

+
<pf-date-picker minDate=${new Date(2023, 0, 2)} minDate=${new Date(2023, 0, 20)} 
+      inputDate=${new Date(2023, 0, 3)}></pf-date-picker>
+ + + +

The minimum and maximum valid dates can be passed to the date picker to restrict the range of dates that can be selected

+

In the above given example, the minimum valid date + is January 2, 2023, and the maximum valid date is January 20, 2023

+
+
+ + + + + + diff --git a/elements/pf-date-picker/demo/pf-date-picker.html b/elements/pf-date-picker/demo/pf-date-picker.html index 41857d2cdb..f764e6829b 100644 --- a/elements/pf-date-picker/demo/pf-date-picker.html +++ b/elements/pf-date-picker/demo/pf-date-picker.html @@ -6,68 +6,6 @@

Basic

The basic date picker will use the user's locale to determine the date format. The default minimum valid date is January 1, 1900, and the default maximum valid date is December 31, 9999.

- -
-

Date Format

-
<pf-date-picker dateFormatInput="YYYY-DD-MM"></pf-date-picker>
- -

The date picker supports the following 12 date formats:

-

'DD/MM/YYYY', 'MM/DD/YYYY', 'YYYY/MM/DD', 'YYYY/DD/MM', 'DD-MM-YYYY', 'MM-DD-YYYY', 'YYYY-MM-DD', - 'YYYY-DD-MM', 'DD.MM.YYYY', 'MM.DD.YYYY', 'YYYY.MM.DD', 'YYYY.DD.MM'

-

The date format is set globally, independent of the user's locale. - This means that all users will see the same date format, regardless of their language or region.

-
- -
-

Localization

-
<pf-date-picker localizationLanguageCode="fi"></pf-date-picker>
- -

The locale string can be passed to the date picker to set the date format.

-

In the above given example, the locale is set to Finnish: fi

-

The date format is set globally, independent of the user's locale. - This means that all users will see the same date format, regardless of their language or region.

-
- -
-

Translation

-
<pf-date-picker translationLanguageCode="fr"></pf-date-picker>
- -

The translation language string can be passed to the date picker to localize the month names.

-

In the above given example, the language is set to French: fr

-
- -
-

Set Date

-
<pf-date-picker inputDate=${new Date(2023, 0, 1)}></pf-date-picker>
- - -

The default date value can be passed to the date picker to set the initial date that is displayed.

-

In the above given example, the date set to be displayed initially is January 1, 2023.

-
- -
-

Set Min and Max Date

-
<pf-date-picker minDate=${new Date(2023, 0, 2)} minDate=${new Date(2023, 0, 20)} 
-      inputDate=${new Date(2023, 0, 3)}></pf-date-picker>
- - - -

The minimum and maximum valid dates can be passed to the date picker to restrict the range of dates that can be selected

-

In the above given example, the minimum valid date - is January 2, 2023, and the maximum valid date is January 20, 2023

-
- -
-

Disabled

-
<pf-date-picker isDisabled="true"></pf-date-picker>
- -

The isDisabled attribute can be used to disable the date picker.

-
diff --git a/elements/pf-date-picker/demo/translation.html b/elements/pf-date-picker/demo/translation.html new file mode 100644 index 0000000000..8f1b5b3260 --- /dev/null +++ b/elements/pf-date-picker/demo/translation.html @@ -0,0 +1,41 @@ +
+
+

Translation

+
<pf-date-picker translationLanguageCode="fr"></pf-date-picker>
+ +

The translation language string can be passed to the date picker to localize the month names.

+

In the above given example, the language is set to French: fr

+
+
+ + + + + + From 1ae65c5f42fcbf35bdcc18614367d666de3905dd Mon Sep 17 00:00:00 2001 From: Arathy Kumar Date: Thu, 28 Mar 2024 17:19:12 +0530 Subject: [PATCH 06/15] fix: css fixes --- elements/pf-date-picker/pf-calendar.css | 10 +++--- elements/pf-date-picker/pf-date-picker.css | 17 +++++----- elements/pf-date-picker/pf-month-select.css | 32 +++++++------------ elements/pf-date-picker/pf-month-select.ts | 2 +- elements/pf-date-picker/pf-next-button.css | 6 ++-- .../pf-date-picker/pf-previous-button.css | 6 ++-- elements/pf-dropdown/pf-dropdown.css | 4 +-- 7 files changed, 38 insertions(+), 39 deletions(-) diff --git a/elements/pf-date-picker/pf-calendar.css b/elements/pf-date-picker/pf-calendar.css index a2333ad75d..d79a9b09ea 100644 --- a/elements/pf-date-picker/pf-calendar.css +++ b/elements/pf-date-picker/pf-calendar.css @@ -18,12 +18,13 @@ justify-content: center; } -pf-button::part(button):focus, pf-button::part(button):focus-visible { +pf-button:focus, pf-button:focus-visible { border: 2px solid var(--rh-color-accent-base-on-light, #0066cc); outline: none; } -pf-button.calendar-date-button::part(button) { +pf-button.calendar-date-button { + --pf-c-button--BorderRadius:50%; width: 37px; height: 37px; white-space: nowrap; @@ -31,6 +32,7 @@ pf-button.calendar-date-button::part(button) { align-items: center; justify-content: center; border-radius: 50%; + box-sizing: border-box; } pf-button.calendar-date-button.isToday { @@ -52,7 +54,7 @@ pf-button.calendar-date-button.previous-next-date { --pf-c-button--m-plain--Color: #6A6E73; } -pf-button.calendar-date-button.selected-date::part(button) { +pf-button.calendar-date-button.selected-date { --pf-c-button--m-plain--BackgroundColor: var(--rh-color-accent-base-on-light, #0066cc); --pf-c-button--m-plain--Color: var(--rh-color-white, #ffffff); --pf-c-button--m-plain--hover--Color: var(--rh-color-white, #ffffff); @@ -60,7 +62,7 @@ pf-button.calendar-date-button.selected-date::part(button) { --pf-c-button--m-plain--focus--BackgroundColor: var(--rh-color-interactive-blue-darkest, #004080); } -pf-button.calendar-date-button.selected-date::part(button):focus { +pf-button.calendar-date-button.selected-date:focus { box-shadow: 0 0 0.3125rem var(--rh-color-accent-base-on-light, #0066cc); background: var(--rh-color-interactive-blue-darkest, #004080); border: none; diff --git a/elements/pf-date-picker/pf-date-picker.css b/elements/pf-date-picker/pf-date-picker.css index 7adc51dd19..9159896be1 100644 --- a/elements/pf-date-picker/pf-date-picker.css +++ b/elements/pf-date-picker/pf-date-picker.css @@ -124,11 +124,6 @@ display: none; } -:host .date-picker-toggle-container pf-button::part(button) { - padding: 9px 12px 3px; - height: 36px; -} - .date-picker-container pf-popover::part(content) { min-width: 330px; } @@ -137,14 +132,20 @@ pointer-events: none; } -pf-button::part(button):focus, pf-button::part(button):focus-visible { +pf-button:focus, pf-button:focus-visible { outline: var(--rh-color-accent-base-on-light, #0066cc) auto 2px; } -:host .date-picker-toggle-container pf-button.date-picker-calendar-icon::part(button):focus { +:host .date-picker-toggle-container pf-button.date-picker-calendar-icon { + height: 36px; + padding: 8px 12px; + box-sizing: border-box; +} + +:host .date-picker-toggle-container pf-button.date-picker-calendar-icon:focus { outline: none; } -:host .date-picker-toggle-container pf-button.date-picker-calendar-icon::part(button):focus-visible { +:host .date-picker-toggle-container pf-button.date-picker-calendar-icon:focus-visible { outline: var(--rh-color-accent-base-on-light, #0066cc) auto 2px; } \ No newline at end of file diff --git a/elements/pf-date-picker/pf-month-select.css b/elements/pf-date-picker/pf-month-select.css index f2a6f17748..d90b65dc7c 100644 --- a/elements/pf-date-picker/pf-month-select.css +++ b/elements/pf-date-picker/pf-month-select.css @@ -8,35 +8,26 @@ width: 140px; } -pf-button#date-picker-month-select::part(button) { +pf-button#date-picker-month-select { width: 140px; background: transparent; font-family: var(--rh-font-family-body-text, RedHatText, "Red Hat Text", "Noto Sans Arabic", "Noto Sans Hebrew", "Noto Sans JP", "Noto Sans KR", "Noto Sans Malayalam", "Noto Sans SC", "Noto Sans TC", "Noto Sans Thai", Helvetica, Arial, sans-serif); text-align: left; -} - -pf-button#date-picker-month-select:before { - content: ""; - position: absolute; - top: 1px; - right: 0; - width: 36px; - height: 34px; display: flex; align-items: center; - justify-content: center; + justify-content: space-between; + position: relative; + box-sizing: border-box; + height: 36px; } .date-month-select-icon { position: absolute; top: 50%; transform: translateY(-50%); - right: 16px; + left: 92px; width: 36px; height: 34px; - display: flex; - align-items: center; - justify-content: center; } div#date-picker-month-select-popup { @@ -61,22 +52,23 @@ div#date-picker-month-select-popup pf-button { --pf-c-button--m-plain--Color: var(--rh-color-canvas-black, #151515); --pf-c-button--m-plain--hover--BackgroundColor: var(--rh-color-blue-50, #e7f1fa); - width: 100%; + width: 140px; margin-bottom: .125rem; } -div#date-picker-month-select-popup pf-button::part(button) { +div#date-picker-month-select-popup pf-button { background: var(--rh-color-white, #ffffff); text-align: left; font-family: var(--rh-font-family-body-text, RedHatText, "Red Hat Text", "Noto Sans Arabic", "Noto Sans Hebrew", "Noto Sans JP", "Noto Sans KR", "Noto Sans Malayalam", "Noto Sans SC", "Noto Sans TC", "Noto Sans Thai", Helvetica, Arial, sans-serif); - width: 100%; + width: 140px; border-radius: 0; + box-sizing: border-box; } -div#date-picker-month-select-popup pf-button::part(button):hover { +div#date-picker-month-select-popup pf-button:hover { background:#f0f0f0; } -pf-button::part(button):focus, pf-button::part(button):focus-visible { +pf-button:focus, pf-button:focus-visible { outline: var(--rh-color-accent-base-on-light, #0066cc) auto 2px; } \ No newline at end of file diff --git a/elements/pf-date-picker/pf-month-select.ts b/elements/pf-date-picker/pf-month-select.ts index 1708def8e7..a22085b893 100644 --- a/elements/pf-date-picker/pf-month-select.ts +++ b/elements/pf-date-picker/pf-month-select.ts @@ -56,7 +56,7 @@ export class PfMonthSelect extends LitElement {
- ${this.currentMonthName} + ${this.currentMonthName}
diff --git a/elements/pf-date-picker/pf-next-button.css b/elements/pf-date-picker/pf-next-button.css index 01b1c01b80..9fa3db4714 100644 --- a/elements/pf-date-picker/pf-next-button.css +++ b/elements/pf-date-picker/pf-next-button.css @@ -2,7 +2,7 @@ display: block; } -.date-previous-next-button::part(button) { +.date-previous-next-button { --pf-c-button--m-plain--Color: #6a6e73; width: 36px; @@ -12,8 +12,10 @@ justify-content: center; background: var(--rh-color-white, #ffffff); border: none; + line-height: 0.5rem; + box-sizing: border-box; } -pf-button::part(button):focus, pf-button::part(button):focus-visible { +pf-button:focus, pf-button:focus-visible { outline: var(--rh-color-accent-base-on-light, #0066cc) auto 2px; } \ No newline at end of file diff --git a/elements/pf-date-picker/pf-previous-button.css b/elements/pf-date-picker/pf-previous-button.css index a645c25931..f73131f05d 100644 --- a/elements/pf-date-picker/pf-previous-button.css +++ b/elements/pf-date-picker/pf-previous-button.css @@ -2,7 +2,7 @@ display: block; } -.date-previous-next-button::part(button) { +.date-previous-next-button { --pf-c-button--m-plain--Color: #6a6e73; width: 36px; @@ -12,8 +12,10 @@ justify-content: center; background: var(--rh-color-white, #ffffff); border: none; + line-height: 0.5rem; + box-sizing: border-box; } -pf-button::part(button):focus, pf-button::part(button):focus-visible { +pf-button:focus, pf-button:focus-visible { outline: var(--rh-color-accent-base-on-light, #0066cc) auto 2px; } \ No newline at end of file diff --git a/elements/pf-dropdown/pf-dropdown.css b/elements/pf-dropdown/pf-dropdown.css index 25853d649c..de338b8510 100644 --- a/elements/pf-dropdown/pf-dropdown.css +++ b/elements/pf-dropdown/pf-dropdown.css @@ -80,14 +80,14 @@ pf-dropdown-menu, display: block !important; } -pf-button.disabled::part(button), +pf-button.disabled, :host([disabled]) ::slotted([slot="trigger"]) { color: var(---pf-c-dropdown__menu-item--disabled--Color, #6a6e73) !important; background-color: var(--pf-theme--color--surface--lighter, #f0f0f0) !important; border-color: var(--pf-theme--color--surface--lighter, #f0f0f0) !important; } -pf-button::part(button) { +pf-button { display: flex; align-items: center; justify-content: space-between; From 52456d7798aaa64d50cba587b2cc51441499920b Mon Sep 17 00:00:00 2001 From: Arathy Kumar Date: Thu, 28 Mar 2024 17:29:17 +0530 Subject: [PATCH 07/15] fix: button css fix on focus --- elements/pf-date-picker/pf-calendar.css | 4 ++++ elements/pf-date-picker/pf-calendar.ts | 4 +--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/elements/pf-date-picker/pf-calendar.css b/elements/pf-date-picker/pf-calendar.css index d79a9b09ea..678833b86f 100644 --- a/elements/pf-date-picker/pf-calendar.css +++ b/elements/pf-date-picker/pf-calendar.css @@ -41,6 +41,10 @@ pf-button.calendar-date-button.isToday { --pf-c-button--m-plain--focus--BackgroundColor: var(--rh-color-blue-50, #e7f1fa); } +pf-button.calendar-date-button:focus { + --pf-c-button--m-plain--BackgroundColor: var(--rh-color-blue-50, #e7f1fa); +} + pf-button.calendar-date-button { --pf-c-button--m-plain--Color: var(--rh-color-canvas-black, #151515); --pf-c-button--m-plain--hover--BackgroundColor: var(--rh-color-blue-50, #e7f1fa); diff --git a/elements/pf-date-picker/pf-calendar.ts b/elements/pf-date-picker/pf-calendar.ts index 476fb0489d..828c4af934 100644 --- a/elements/pf-date-picker/pf-calendar.ts +++ b/elements/pf-date-picker/pf-calendar.ts @@ -171,9 +171,7 @@ export class PfCalendar extends LitElement { value=${value.day} .disabled=${isDateDisabled} @click=${(e: Event) => this.#selectDate(e, value.month, value.year)} - class='${this.selectedDay && isSelectedDay ? 'selected-date' : ''} - ${!value.isCurrentMonth && 'previous-next-date'} - ${isToday && 'isToday'} calendar-date-button'> + class='${this.selectedDay && isSelectedDay ? 'selected-date' : ''} ${!value.isCurrentMonth ? 'previous-next-date' : ''} ${isToday ? 'isToday' : ''} calendar-date-button'> ${value.day}
`; From 0c94f7595b5e070b5cf1ff01a64265133d61fda0 Mon Sep 17 00:00:00 2001 From: Arathy Kumar Date: Thu, 28 Mar 2024 18:05:20 +0530 Subject: [PATCH 08/15] chore: component image added --- elements/pf-date-picker/docs/screenshot.png | Bin 0 -> 33881 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 elements/pf-date-picker/docs/screenshot.png diff --git a/elements/pf-date-picker/docs/screenshot.png b/elements/pf-date-picker/docs/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..ba57f54dce1f9e79a2c1d4179af1b681bc9ddd91 GIT binary patch literal 33881 zcmeFYRa6~ow=Ie#xP}CGcMI-0c)AS$qHO-k1Au z?!!54(`q(q&Z^I4^wInH!W87h5#g}mz`(!|B_%{aU|`@gU|!zboM?Sg9+q0h~z7)Bl&(bDCYP&7l8U6um1}&BvdIRnm!nANCuJN8)#^VpC-FK zOAp*&>6td^vKCJd7cUm)Jl=0Hz$h_yTy@{FBkmOZ!rTe}ng&MLRI2CVR%{l}kSl{x z@FxG!HvV0Sn`8V|31&cAI4t~`h{hW*h_rrG3J8H&;ipJiYvOX@YoP_u&^s7Ku^z~| z#zsf{5i?!=z=2SO?4PCsJ|zja?B89>$U_n+clNtvuzICqiKKQaANlnLUz!`0hh>l; z9b*%|dK3MMoPDz*D~TLi&*&}q0|DMu=}ak;^b9`7z&hA;4B>)97ksLz3?9OiLvX!6 z`QTH1ze#G&1|C=c<|T@GHB=piWiaRFLT7xUZ5e;qMDl?tMUd#YZ8D)@FM?@d;M=hQ zSE(mM$3OO($4>>Z_dL%qjoSc1ea9(T?BdiXiLct*r2n; z(ncik)mmpcHNItAM(*()MDZOWlnP}t3}!@?CxrK=YhZ@?b^u{3@~l75s5R9%wJB9d=1npFwEuqJ@>c|XF{ZUz^h&N-^us`7qk`J7 z`JH*5&s5F?*O=G1*KE!(5BMSdR2v9(NF4myAL+4L;M78wf-DEJ2Cg>W+8Md>=)SiJ zcj<-R;JlUb#P7!S0rP?Lf#eT-k%%QSL#>463&iw~jTNROJECwzu|~;89SE}*@ydV4 z6goswP5J@VGVJ)PjOK3w8i;kyT!ew^Ar#$eI&ux`>F_6aa>?rDVn53wHoxhRXb-nl}C0} zicbQ_Q^C9EQS{E>O{c$~e+z^>ky@VEMr9u%V&47~kGb}um?qsZu2pDs0nXIIa z>fh9!NuEh2NlVl@Dp=*KW) zL8{0o4JoYw)hPDl^A+cnZB!6VQON}Jo8bDBjwYMf)PTch*jTIHhU8pcU<;^~#x1zSW~lw6eMrER)bExN?I z9_~fFioN5X<({mcklzNsjeDDcpotiasDiK%ybv4}j2uNNW-T@vSsrDLwH28*KsC@2 zS(EXF+?iaGJc)cx#z4tK$#0*2dbMr7E!rW-pmS;m_gG^Iplc#=>{*FzO(Tw%gz~a|*OC58s(O|zk^Ns!!;?2uHDykxjoQZ!{|Q@XC2$ zNoJx+bMn0!>7NolvK!k9C`s7Eo|x7RO{r1qR*PxzcWHGAx-lZa%Mr;D%Td(hUQ_8V zd8+jW_g;FupU3`8-{Hr5aI)Sr&_m;ki-N6ydP@3(Q~`IMk0ASm-{;{m1pX2O2BYz< zAEEP)>s;~P;NDSxAFWU=%+6Z-jcJsM*cqagyv83zc@M&CA#Q>1gLq-s;Zg!!#1u#j zNwA6eMBGI`h+m6qh*X8O<5qp33$>2u9TfR`6oI9+-DNhIpQqk2-#O16frwF~?r9gc z82^)uA_bIUpNt>YKOj1A9P`izyQ%Cju(@!ia#o-%|6A@id^UGYAbX$72F67WSt}_z z1p-AoskJ<=q`fq?tUJ{yx-yI|#RJ)isr7Uw;5=#8-eRs_o;^S+sbqo^V2ogj1UPP$6km7Uwya}t_uH5fH* z4fA^YixJZWzJ;F3zRJz&1MR9V_x=Mv)=E~7IU&ofIp^wWosN&8LGY36U5pyUsGnk}Ol2 z<8q>StltaP1X#(M$a-WXrNbln3C9ah_(VNijc@q&XNjkZr$-+URB?%MC$B*?Rer{@ zlC?OtiLgH8{q57)u+sK4Z?oGoll6m7(G7I=NfEk?q{V1c`MRubI)9pBf;1cJNPF>a z{qxTSEgkp{_whHG9k#1`j|dchTH#)D$ujCS zidie^B6x{?QM@vGqS!6@JrS3a6UFf2{{p(6xwU*~@wGvwm?9?^!DTWne=qO@oPreW zv=|9Y>LXa=Az17O*_)qAZ)IPQQ;AP=T-#5wnLWTP?!jQR-UzI#fQ``nNd7QWG5~=h zBl%eSy&mf&e39%uLFvJ?5qQx6imfXy(JC!!w4aJPi9x z*$fYWVO5RQB~4^y!Ki_I7%=bdv*ya`j_R^9Tt+rl^ah`742|hst!!VD zfbqC;0k>Ahjs}FTR-dgMxLkRO|9OK8xPSebftc{0R~#*QiPdEl2!(CzjS1Q48R;2` z`QQi%33=>4nQ(zb#Qr7+zVQ;9IXc>MF)+BexX`<>(A(IXGB9y+axyS7GcYsL0dLSb zxLG?IxYAiWko?Ky-+V-j9gOVFZ5_>RtO;N9H88Ysa^xi@el6&~{{Ga{*wy^MOR{$O z+bp1g46jcZnCKZ9{wr@FDbMS#Tngr{#-BAr%&ma#0m|TGWo6;{=l%cJlm9O9KT@jy zcgl}!od22fKc4(QQ>r)^+Y8%R0Yy6U{rAZHP5hq^|0d*Ncy0NA^u(WT{^wVqpZVZ; z82)R_fG-*CF zg(RhdMnLF9nlP|cjb_u;UTZg3&l5KH6HdR~P9C~%x=-3}#=36Ub0*v-I30G!crLQs z_R@CJ99V8?DI7@!ApTtY^ zDV|1)Q5lXU|U6erDn)tBZL2z zP^zo}{iiI6FJutP<@A1^KK@&|q7kltt4WWV3vz-bEgMTd=P*RH?EK#IV`l5`d;T{nM`hY1;pc+TZEYDni&GefCx4BQz3jC@d!x zDXfUUK)2@|F)G~_DL7g!ssv?GxMzbwT@oLI4w3J8`u8-o4qY?vspBnz`l%<#W+A4Dcll>YxEc_^(P`>LyO z_y;;_WWHPd_`vlL)0&=V_juDwplHSzU;(%p3x%OY`cCXAajrI)%ExCyUr3{c;sZ-* z07arTh96Tn+n@BKU(fTsv{*DQ)l!QM^1AI_mdo4a!;FOnQ~2zii5xxao+oGX#l|x4 zuPm^!zJkEwL8L|qkLQ3Ai@~SU^S(0{2!6TUit+V$h6X=T$WN zjSgq4&JSyPq!P$wl{-(7$&h5;J>Tu+)a&;`EOdFaNF6pWStpF8B`}B$bsLX<5aeiW zvYZ=vyt^>o8i)er3B57@SsM3gG+CCn;YFkQbK1!C{&dMsc|~=6I;-U(LB*lM))d30 zl3N0Wd@@Z}Tpy$T-f}{?-hD@m3)>gYbp6nJF#xps$DwNCOYqVOdyGg{OZ?nCwHLm#1 zgE&t4s@ZK;k9-hME)Qm;c`J0g+G|v+G!O2FDRM5exSXVd;92Q?3TK0gGQDOSi^46( zQkkyBnSAi%x~9^axRkpa@kPVYCnp0vkF(crig!RO^0a(WAFNRg}cc>v>yy-XPW@w%0Mg9&MhmH0N0sGU#joQf0_He?1 z28W$~8dRD8xe4-n zelhI}gbg2DZgWt8eGpd50WGk$cI;4*67Hj z5@A9iKBVJuIigrLVyxRxf!q$OTDH?nv*Tzr>I3nh5tK8>vD-#w)bx%olZ&KNMjFkh zx6cL%ZusGoDJol%sg%TmG@2}u)@#M8pIt74SS>Irl!#^2UUbvEBL-0hs={3H}#t4PT*2-pAAa=iAF~ zu|&d&fKC=e!XsRgxN5pHf-4PZ8E01m1VGQmy_b(G^JINTd-KN4KPQNK4t|p%VVV|6zL@D z9iIZjZ{hyOOufs^R!b1gYx<*z&3&HN3#Q<`{b9mAp^DVFVr|I0m$75%tkRWEx=-p} z%3$Hmc3T79{10!M`vl^etyeVNFMn|Q-=K}eyE+;?7#!=K92)63Ys z0L#s=s^BeSA6pR~w{xi#pK~&J9QizA^N_xKr(6~n#2aq?FJPizaPOsIZcib(cwCn4 z#Qoz8oFxgbVQlZ&lbHK8KZpe=pp5gDx4s;=toaZ4UNIhN%vi5~@jH;_NTN}j)jhzK z!v$y1WLGIyMzws1uyDQV9(+e3HCPlL`-YLhdZqMaAUqS(w?9s+cST z>#i%+HCgtRLR!D8h6dA#d)k0XFq*BOoBm0*wUAn_e7Eb6IYp07mrOoLExJVg?lIKW zbV2(KTsC}NQNssc?`*&HBG17<4$`s0LB7QL)oyi5QHc>l3U{T$MeSU_hVoOd z?m}+>bVcK-;+x~xXk*txn|ELsEprVp7`ZA7#cQ)!eh! z**xp}v?&<386nSw&|Myg+T^_PwP8B|2BAc~tL$59ge~p>NAW3>84@_+WUU%ineVr!$ z+3e(*^>U}5iIv`2c14ahUZ_%)6CcQ#G^O^lL_Cv|-GjV(=7Z6zRTyT~>gaQpHNEX0 zr-_a1?Iv@8?ZXYC@mV%oR$X5IRH;(E&euhs30}B_BHNW%B`6#t;2SE+#*2;p?idxv z(KvP%bwH6Vj+O`R_@Q>2J!hBe2^u(N-NqnNE@{8r?J11+eN;4*#1|O|+%mCR?b`2e z^uA7#%)^iu@vZynb@!e)PKl-n&_+TS2@d>9##SQn8$wx6P9L&WsoS)aR9JDvM}Qf?P6^<`Hvb~O6kvc`zhY+pMSk%_0P;h?bpQADRw%P49D1f;3UFw@ z95uY8Vv;!y&+qL|2fgw3dpmJ=>8To=MITAm>HGpg)#<7y@;k!@K7?64oGcGmS5tAqlH&UXVwaKYo-q&V zqQR)eEgCoVNWZb9=(j{;%m7g2!jAC1VWh7PRy$dHbi&9CU4CQ;6tTGPY)_ogXzII2 zUsdn85w^`3N9?%xth0E5e3W$wRz<1wyGTqw1#}$?gUpHJ0?-31b6!8!6G^h5m<$9d z3Nx_x#ws!q!jh8ZzSo|jc%kaipJ>dv@bZdjP%`H_2?&}zI|p7|M+7`dn^u+?Op4qj zz1q)E9?9`JPgvDTpzPjk!M7zojBUTff&AP6R2^qJhQ0|@6qc)D*K@a1-1XMnXAf&4 zk8mQJH$r+im8qb8Zetxo#&6S&(I>Ek1RTJ^KsA2VnMm0Qe5qbOTClrcI#FsLV0nn6 zQX(IYCyU7BuoI$E2T+4 z=n`P23FYk$#r5ZWtuq=-Gz{WLFIBD9OtavBfq&`wF_HD$Ndcth0g4@BF@bw67RAXq z)B9dG2USk*a9{fv+6VV^5Q}6In9=*CPo`Dsd6Z4|GfI6hBHs?gzrmw1`U69P`b~gH zhCKIufsR;*UajSENW%s8jY6?zo{+}wc-9iW*O#lw0&xJ#RD!F!3PXqi6OpxEoidRM zs!^zM)aYh6Jesw)iMPD-CM@+eY2#TWicJ6ERy<`PE5^5 z3O)h2J?_ETuo-d!G}3e>Pr7yHE<)^neHKEYz#=mufW}-A2sbQzXY=HC$ zE68*AKpRe)Kr?XfO(~T!nS`8#^8g-1w(fw+0FMt9ha<}+`#WJQjMHh)L}O|%&nGI! zfDh;J2oezHgt_m@b)9s%Pn1BVw^H#LaPQCc^8cOR&gDOZ%bhbMUe zxGn`vqO_;XDE9Vx((j3@?e>>EggF^XrT3eQ>9dkcnUJt1hcQZ(l~_*jrKjV$aobJ` zzn?H1hcQq1uMInNu!np9jF~3}$~)i@tRv?A2vVqY_j-SG|HELbj%eC#Yk>#i`rf3c zSvozlhgYUjdoU2_ptN`8*K<)B7j4~h8<)Hjr0b+8mC z5m-zru4$)>=k?wIDI{LaZ&-3P>RGBhtomH?JRb6dMeaeS_icRKWOCzcSmT!-TS?{C zKQalA5plBjZOxWJ_FURh2Dh|lK!t<4TmI<44i6ZSfZZCC#o4@{Bu z+p{h2Hx)@`s@eCSG1yacp4!Uh29A|xq^6O^_D$7FTZ@7H@0BRX09&;1Sh7l)T^QKX zVB~+`F4n~9!_0{T445w#uy;5$Zi5 zmpf~A0(&AMI1B82Qh68M+>`~sSBwLYm&3c7C{VeafOGCWwyBpw9#`1eKh|!HEcU}U z3u|kCX_H9Mj1)_1>U|m?Nu+9}bC3SXRLnmH@rwsU@8ahb??VShDkb+1w4HO_1C%Nh zFo?|aSl){{1u7Jr|FqWE4a!lgYRP6Ln8JmP0Xk3e6EHCql-_Az1S9Qr&OP(|>5^2@ zFAJ{=F#dyq(KoC-<*eF|0uLH`B(&AXAs1eM`we($)dbpdf~e|x&a}ZECH%e@7J{pEx{jv}~qXP}gcqc;UluZH2ZM-v1rteP9DTvjvE zDhfev)yfHwrZOCmEdUVIAS#}`w17Zoj-@7X30Kd#m0-A_@^h=d(uRK5$=he(JQV?sVX5+;g*5f>1=^7uW`cM)8OAr z`%@zxh;cTpwGz@YmdFdfB9|`r-7=6Y{q(Tk^z(H?w;VnF{hzLMl?StGvaZQ8EpfbW zo96%UJ4(5{_X{Tx_ensPF#jKP(*VPFB zug0pSPHILMOzw8vqoEwB=ns)A5$4-~ICsNwc$m1#ewS7!r6MeiWNZI?&zt>s0Dxt; zl$F`z#wnpe4D1v-o)nJD+)qavZhw%M{#!r&V<(>GHKrov@1@v@{(k|M|2qJWjR$rA zrIJzD{8E+5&p32YoXgJMyjDBZ4&rPvc`y?cb>=Q|_NTDTRNu3;=b59eCbfbKjIawo zNa(*TAdCSvbeAAbaO@AA8kc-~&(f;)auSDVts znfF7C(cx*?$b3eup=bQ~mue)H_LXAQ_~`jBsl5U4W{5Eu!vCVc`d82>+7JD2rC$-t zsVmd}w&aB5J*A1Ct#7|LL4+XT#S{2EDtcL!=ytgaZt*)EB+8_Jl&m-TPTjKNto4J@ z+V%HJhZ>jj5oJy1c{sq73`bF7iDSdyay!fHe$SwQ$DmKd<+LASozo^!sZfI&weFP` zw+oYrZ5NV_6LR^hjnKfQ9v`3f0-Y}RrW%Pj;)C0pHJ^Zio1GNB@;&3xWW@Vrx``+p z_HxH$dYuE+#K^rDD4{AEz^%Ln+~;xACS5-1wEOez;ezUit3!-0=Q%#liW{eo12{Iy z8vyHipw(y=$FB|WSbDYev_a)l!rJ6*3U@`KyAsW)=5Z8NC%IK(8BxtRRve4=jr(Rx zlLG}+;u$f`1Y81#kR8!qn)aiod+@KV$mO2|fsZ2Inta-B9%&vK@8|hiQt&D%dK-Xx zMe}k0O5q0%`L;B!Y=&@TERE$&12n+hbaKsBY7{VAv4(Gb|GsAGi@w@gP_XI#QeiG`lV0R80 z_X}wZ?{%u@X)muc&0|X z<9>d13Y}K*_&ahL6v{91e0L)DJ0nGp^`;Z_Mgx(kGO#|6XM-aQiLJ^4hsecY8EjVN ziS{+RU5ze6S(Ca7b?JKBKphfM-E*9S7EhBi3MZ=&d16T~Nl)4j>8Uj83q5Z)Lckde z62m8jcSe#RAAN2&pv`B>lhl=h4qWf|OJ=Y$kv=*dD7QV|9~v(6P?Oa>k*x9bDJ9)+5g2Nw zl{IT#H!GlgkucxfyNkUPBs{JmfVwPr6b_RX|52ty!D+u8T8@LoLN3=38tgcWR%PO5rd%m3@FLkooRphWYZc)hW^XzF{%M5f4 zSWwt0W|_A8e$iBurCP7@2Ed2Ucr=)N2lZr`RTT}L?~ED)WX@EVhfCe8(OLTCuSsFC zNX)$AcX7#0-qgGv*UCy^jLv=@cRMLXJ}=MbKZKEC(XD|F{;GMB1gxI&y|Yv3J3&)2 z^c#TDCfolRc%F@8pkMelC`4`&SOnGijk8Kc0COc+5F+@lI3S=+-;M~E%UaLtDv4IT z_6xrd(zSQz{XP1dxw-KwJa=9XHo4QIVHmX>Nf@A8eu<-pj_lZQnTg1d2 z3R=PAYJg+B*6O9)U9_xGK_p46auyCoP$bb++GISCP#0RB7wsU%;96$SU6`nI29F-SF6Gw-0`TK*$G z`Cf|D%=Q$M4BO`%0TMpfQ+?Ls@UkU`@NhZ7sNGA)O>qt4LA3A7xe-f5*OOn&YLqL&o{-evD-T2B`)YA#orYj|L!B6?bxX zO4aM~Kd-c_ti8=NXhr}0APW$aw}IpDO-O&(&DRY zXUz$pds?;5lo(#0u!c>WiKdV_gAmm~Iv+AnjDm=4%FW&RQNY z5>}r6L4nN`#k>JkSck#`LAU+5GW;@N{B8saYvV-<%&nHymT>&LLRsZxhP}?dVfa^Z z*v`wvrT4=WYc!ZlmB;!G87etKYOVsXg;qS)hPzzA_qYrgSW2qz>{!ML!5Y=A#6N+{+MHR{9tCW_V9D zmPzf(BXo&Id%IosfSJ+D&(`158oLJ<-1J}a`D(9&AuU}v^efRSz_)KluCWXtB}i`xvDiu{BV z*Sr|t+|vAcA)o7u8T=28K|iiMjR<;%(dd?uTPD*RWd7mxs;k8s~t zfxplvmJ5JBKe3qpe@nhz^RsZA(nR>NNk8tf$yRY}M83B`e0?gN^%MA^RJz|>{q2ft zzmvIl4#NO4xc39Q_eebFKbX}B_H?Od-~-~DKR(r~CuA!-GiINs{HN|r5vAr-URya`(;}LZ8 zmG-7YIxW?4{uj?O#e!f2Ohy4DTn?0=pZ(H?02~B0nkkz9$QAs7u{V}hkj)QhErK~7 z`<(8VW@I?q$j+C?OLNj^@r7O1D_mrI*T+jw6M(*y{|LaD{Tpt#^TP9fmIQ483j5p7 z9{JqwQzU%78qGg!O`i4!wu+9Y4VexOhuug7JTB!Y8`W}UNvUL7syRKMu1ErYfJe&h z8~GedYezM5e|1=7wbaaPyDosm<63*`$#-`#!C&NZvT6_DRwp*5tyi!NsO-{!m<#b$ z7=K;nhzwACm)Ed3+%HSD-FA}B0qsyS2=0C2SeyM0$W-ZB8_;3mxm~PZHEnbTUG1r2%Ym60kqseWOvx^?h0V8d0WH zQZ84m(>#{;-3pCDF0*to7TB01j~APa0j|_PTDrx0MVYt#YF6u5W1~qNYOG2SAKZ>x zBQm?X<2P3upsqeCt~pN2+V>-}4*{7ydKc?7TFgkTy6t6w6}5Zb+1~*QP7zRMaahIn za2!dUELE|(brVjb)3xqrg_^u1o7?#(^M(2%Pe2@gMw(mm zd7(?ubuat$J$;67U-W!$;v_|!Mq!2Xl%Mf7U%%HM)M{%GK!$*R42ZEwK#;*bw^or< zQaqd0qQrEuY!V0&;X8NDt5nvv&D5?AxB#D*n(2V!A)!qWFs}MTh|g}5@WI40)>gC31ecP)ONu*1=i%vy7TYlvT8lgGf_bOtoVg`kS`jMcrI%S^5h+WRR%np znV9|%Waw?E20X`Ilw44ht;Vw5ARegBP_%73UV0k{PTQU5JdqD68^_;dtlrQf=R5n~Tw@dZUauu3W z(bb|1uxDic=UXGENIY(~l`~N;e{p;_5kT3c0wD3bF;asAJQi_v%OaK#Kv4)Bg*bF& zB=y~ZEw-rtG)xR2#YA|9PK6)p=|@~n2Zf*3TI~uVF`0z)zbY2XU?qq~b1wW?=o>Nu zh+Jl?#gCJRhK%U_%8axFld-Bby1cvRChcERVbyE({kZh^^xG>mo5~-bA5NIfr$~Fz zrU1Z~XkVq(QvKyNd>jFAC={tfs8z~g5X|*+8y)vSR};Rh(g}FQZcZ_n1E@e`K_kZ4`QXjo!!_2yhqF~L-&|0te|f*{D82Q(z52Qg=f=!S6pEyPF*jP z##~n93j`hLE${&TksukX^>V8O*|2LrDWHFo*V+ac;d}+WQ%KzaKe*<(F9*|(A^WR9 zaxrYS&y>X)IqBo{%6Iz1u!pvka#m%LR`{vdzvpUlw7sro;)#TV6$*@6Immn!shian z@Q#>&-03u!M6G1Z;RD=x6yqZ><>k|Q`f>56S?3$0P+_qrfz5!SudIaMy%L(;dRaZ& zbu;Wzd;rrQ&9@E>p|3XD!X*`M;3q@&G(aoTLOSkGi8@)=S;5}aI{1n*ny{pQG>@P6 zMHu02`6<%Mn5)cxxBU+5qggC-h`4>~ctE^6Af1zBy5CMh-QpmnFLokWa$3s%<_H@HywMb;P zoi=d0P8N83XavPqBVtQlbzn+&<=x=~iYi5sr!G3JW|(HQP2VGqTJ&OMN9RoFKu2f)%p1iC32IpT!gM%7b^;(r z9orwTY@vWV%shW*T(;|N^#fFE@d2OGfW$vmJ~kIH>hBuY)2#mRL^puA&_0pK1d#uL z*(aq0Ogt2wt4YQmM#>AAPrq4=BmY=YSSi4Y3c_bE{NdSba{wjeK?aNDk22>E43z8- zf79kayv;HN5T*FPtJogUJE2|%je62R1q7kjcGO9)1EO)X&IV$9rW9*npT03XI9k9T z-~v62Q{9tKuY4mPYkerL zU|={=^BJ%|GyuaR&4zjKW3}rca#YR2=>Z_K{W~W`gTDZx8kDhsDZby>ZW#Il+Easl zlPA};!$OBm&#Or(SFR>?S$3|-RBKQCywt68e~lAMn$X11qrAB4B9HcB=C)4Y_?V(M zx_swTHoyot~lSW*lVklnug?((BV&e zSS-dPjRZUMbzAXd(k5+%N`$iy4xulla1K$>^Y;Xs7m8I&Ox3$q$aS zJH3<_DzIDEs|z`EIDQuMWR#k%)Tuc@c~l6oE_Z>X=XIc(v4Xj}i8nqEQ@NS!_3J2I z3_2IcP>l!Zd?%Z!8~{QL15UCrV5eR;=Q53kV#&=nNyg8QcdyidMyqY!_IQ>SHme0G zz;=`>S8H>cxw?zJE{)N#n-hLQ;ozv)C7cyvr!^#eS*;SE#5_t+%_AKzjbzkZ zA+ASeAdCADm&oTxZW74Gv_SVz?2PCVdg=Y9U#K@p*yo>!MHcrWE|N}G{>=Y$1kWmT zymI)4NDni^3yN#^!i=6G)5IGmxmjI zM2I)%Gz`*ZQe8OJ?;=ZYjuvQAyBiN;cw0^9#>+$_OV9WCDO!|DCn!A0nk;nl#kR&x zCp%qc;}S~bh5-y&IPxNlT&asi*=pn4o0&y4{L39@MGM z^kNVGiB_{QG?7+AD7i{~BWcJkiN4tLcpRi~@PS3j(FEnxd3+M#Eou_PbWZ`_v&_L< z$112;Bhd~V9W-aXQsp$vQclGC(m8dm-@yhd)$a`*L6h5_rId6&S)jY36NPKBoTH!l z!v7#7-;^DGH413)BmH=bW#(hHo6(=qaejmX_y0L1&t(S=|lRYL)7`m2u4gQ6x(R z3S4TnOZdFjEquPmJ*V;GsJqu)z2_k0W^T+)ak|KL5t*B#ph9$ zN}(o`eYP1>YLYTGZdI(Zcz+e+>r@&u;Av6SADx)Zz=Ok+Px(Nv*(uWfeC^?OUMCk% zA&*}q5|^@8IcYIRfv;leEgp@ZWT)pzb%KCJZgY89fe@HaYZAZxLj%jKN~O>CppQBU zBHKur_xb`BG^a^GfBQ1&A^?VL)1Atw$Y#A*IiiOX^!>rXuTr-`HA+TWYs5KEg}i6% zR+d_|R6p>s_rUphlwQU1tVVsn05j`;{C<1>%xIR&*4$WpooKUFdNG|m3Gl(Bqp(@G zQ7jUEWFpqu3?cl12tj(H&zw3M=l&_-kx+!v=?nyP zC(f;5lg`a{Ei%VTS`a<#`HR+@p6D72>^4M{O;I;QakTcVg2q`7Ft-D+XEc) z>GC3;pU>XLeu%;ZGF>E{bM$TPQAmOh4_c!U6TEa+Hl1?6t?W`R_pkCZxx6%`J}~@l znzu{nZd#?x;&xHJG5|bM$z-cd@&cxGRKOadgIL~Wc}_MqdDDBP^RA9nXcOOfLC`8ox8!Z^|571 zRCzABH=s$!EPpXNeyLuU?c4X%yN*Z3C)3f*`!nCN7dow$@&!M5dJcYpp8%~qGLlpQ z@tE(G09u)?&n*--y13~K@FIY$}Nqi|&NwOcpKwpULcvRH8POksNUSA6N3 zWj4!|!|#44QVHDH!(_?!eP*!s1!@Q`rdvy&_QrM2pAyYSe4cu6wznquQUFR+3bq3E zYQ(_j*w-(h=u;TlRHw$fp5<5_Zc2t__SSu|I2tDPZkPxziqr~u82azXLoPU>OH^lKl?zbyKnX*b2_}$M1+na70M>z$0mWtc*-KZ z_DojUsPJ;9_KEydH~7vikj6t0%iQ!BnNGddxBX^w;-q1>v}sD;{N`jynu-Eb&ENog zcC@&)BuLseU7xCP|F}KCNDqGvdsl@tN+vc^60~UXU0)xOH5G@&By!n)l&%*aYpVqao@X_V zO6z!at{|nQ@4uY;UHG0ZjtiK6b*p)j`BW(%WL%coR1*~NL+=B(i+ZUdhK z5h+>{RcWYrt)76jR9kNnRpstK{()P&PIa2HM(fp0_#ip(=TuSb>8qR24Tth4bk0`` z__{(o?D_{*qh$krZl4-B1fbahmu&=qcC<*bkpDOWeXlg)EwupHAFsjs^^{Nq>DFIy z*%fdpVD;X>{5kUTg%0>?9ruanuZJNIco-WMVBmj{d@FG7X@)Ky2zPremv95{RmeTx z6aGEA(eiqrNi_cK&%30rFNq{C6B23#0`*PJQrguAOP8x0VgHUX;ftZtZrDjY&lyAtN(c~$}(XE0HTH7 zPxs*tfJ{>e;5KHPRh|S<)%x$5oJzw$#H|LEVtydc-F5=R8);J{V4z@9z5(%HuWZ~a zLHEk>jAe1Fy$am`8y9Xn@>{gW>%sYzYXs2sQkMh?`a5)`G-l&Z{lSRffP2DAoS4r& zna$^=`MY%sX#rqVfGB$8@@knJKAr0BdkYt|Pyt$n=U-nrVv^~!qySluI5yuyan)gh z*M7e!1#k=w(H(~1+hXvzcXf&B2ca}x_0Dh3K>YPNpwEl~jMg^5vAA%OiKXB}q)lHi zugPY80aQDB%$=Q~IN>*9$nUyF2cmFB0Y#k512{z?zG)B&cvupEf>8vZ)APL?9{``2 zNeY+kj;2gZa5uJeB3}WK;v;}O@d;41%ctLjm8;eK?$W1{h-ESo8wSK)0hmZ#rVPLF zf--Bs4J)6XsxuPz5l5W^n5p&yLggyW8HvdfxvX-o$X7-YkT@u@h*-FwF^%V{b(SRB z-pNu%D;Fqi0+L%Ho6Rci?I5d8p3v%EwuhXourok_MA%$nkwm>}lUEXz1_eqe@YcY2 z4vTAu2F%pS`Fdk%nd-9ZlQli3FF)Kq&b+5qmT*%4;WughD(eD#A1hwvlMFYYrD_2u zh<4RVV}Ypk@r1+b4mt0aCzt83vg&RepeVyhWPlQuWvA0_Rao_YIHoMlY%b(`JQW7w z9HVo4drL__;EI(?r_d(>hk-@_dgBe^BoI?hsvpBjsk8!stwJF9!J-R6sW-KI;Qj${ zzIFgtpH1Zmtc{T*0F}L%OxA_K^VE;$@`o@pz$(wQy}5vWM=r5Dw#ycldgTtd6mJ^9 zfyyg{*0CY{22^EsJh#3f3>-S+Omyht5jb?pT>A$59jO8bR*&n?`%5h~kytEZfRAO_ zSn0e`-FYsB#bg*L;9bd`rv$dM*ydpB!B|jj@C|k+<}JQbiF$~5zZ-BCMjAM0l=vtH ziz|YuPTh}rR92E59`+VL>%IDuVN^YElqsH0i>>#w-|&Z~mSy{h^f#@50JRN7w5wWH zyej=atF+jQpTz@|b6U?w^q*K;LH2E@yrX|hYWi* zm+F3WA^IUdBrxhYXZ4QWI@dEpk|b(sy4itDYam)eY8BWgxCpQuV#ZT1a**B%*lN^& zViJ=pA^g=DiOotaP-cishC3T7>-;OQ>vBr^{Bl|zr$(U{l?8l$RTWU0`Bmq6_0#e2 zbY%K7j((udW!a4lJOW3&&S$l3(rE34h6nc7U@+Jti5R0od_H~T!z?oNLZ0?g)3y^0 zRFc=a`~~(A*qDYD+a1k&FAt*FS5uoq3r;^26EYe4Ghw3Bwg#{lkLp8xDi09lz;=|8 z?xXeuf{>?xqkDi5j96M$4;(ey8?nFhgT`l6`3lTOo$$C-Aa39d9>&qd*GBHdgkdL~ zGyxBPHmJ9*Jx|ve>D|9)tF*?x&%QyI$Xu~4#NL4T)`-A&38aBGo(bFjAss9z4io_p zjhmv#G{#5p14AdkjE4g&9$YuA4|u-^OiidIvUZn~Y7wjX+B{Eyl@!AX%Y!@}p{W^) zVCcqVSxP;9fhL1XW9kd;f7z@x=ugZ`J!d>cE-rngtlM5*9zX^|!B2DZ4zF8pb(vTl z?Ii`=#rE*KZ_4jjs3JjiXFD@Xz%fOToo2f%^>^KhaeG7 zY`^vmsvq&x$wpe1I}>-xv4c}jJCY!9LR40yKbLiGGjA==WomQWm*fc0YBV1%=Qn&q zN+~v2O|vZDO@bvO{s$<8DgxG?Nz)enFTKbEeEq-eY1;R9VQ6szu26EBd^8Cu_=#y1 zNW*=y;>O3-@9GbKm~+}KQYfZUqw~3@=r0)i0brp# zl%e$oZAx$w)PX-37zi&_y|mjM!|JuWzchDBq*kFu+hmgKhaG#k3GL)}Xz}E7i2>#2 zg~N+39fbQ&oh=fB9{jcQx(955?Dh$30jN2g$=SfgY&=5B!E7@2i=X+^5aBrFaPP$L z%$sXda&4uubOpFXB3g(;=bYH=HS4CteccxiWE~|D?71O^_Yo#Z%L`RLs)w-d5w|Wp zmUtmCIdL=GawBsQH^ZT8&RTqXxQUhke3s(0B$a+W6Tk@!Z=BI#j7E%1Q%JWIpC*esrXjrq*t^&(WzZOh*KQ}%zixkU){a+TNQ8kHA*R60@5LymPSAtX^`%a zE(H-ur9o1{e5eBbu+ZA} z{V-`kIUp_vz;j#zpj)=d3*|{P6KpM&%MqWP(Di<*6qc}$_nwCm2^{(SH}!^UgD?W{ zx`a?8#k|Q`4aRj^eS)Za*~Ns8qtONlJ%mtY(!Rcc&xMyd?<^&ix!Xn$@`g1$38ueV<=7j=$ZYPj(BBzW$+ zoBS)A#~;uKlmSE{&x^k+>UlEQq=L@HVh@wBzWfp-$i~C?!TTB*!IDXxgSymNQcvCB-JP>9LLq-&JZ>yx$l4apBw7eYlpt zg6*=tt*^DwBgnf^HgknR_?8LB__f59uEF=-E7;1XDFUW(sHX)rk*B`sBt4>6=!pRdoO)&id{A)7t?O_<`l-Y2#z|SH-zLyH&lDA{MpOKvv@&pIrks$`PLXopJivAcy72{+FrW5`JCwbEiY8GlR2gaq1IQ|)78x=WEPY3HN1m%356R`h%iU%6V5UL#E z80qjsoDfO9J`^{?O4|H~LtK(TZRK#xc3y(Q5s)WL|0cy87SGcgfp<#Mw0%(Zr3J0sL`d(p zJzZwo)Sw{B+7GCmhI0DzCk#rLg(y&c8GI}%C?TLj6gf|`yrfWywQ00iGq0Omn7nn7U)Zo(guW^Hu3Hg zO8g}SNW{4jx`fGv-IJ%%#D@8Wbg;zeL40$8-@pJL51_jO=zSM}%Iwjcbyj0~X!Drq z7FxQo+XhjIdO;|_S+77Xc58z1t-tUGdh@q59k|=FB~CQkodasy|kmvrMPuql4uG zEabxD5uwq*WEjDNLfRR)9!$4BocfK@ zJg3Xhv7g>1$OPzadImC>KB<++l;l_Sph(i|lFu&!-7)Lw_S9Do`Wu%R;Xh=dU$^M| z_INVp7a33AHEB_KUQ|_@Pk{T`6-k550f-($zX71+>aaLET&zPT-OB71&^8~>mBwrY z`1%&L`4bT{pOU^(V+=nVO8ma04<#t!uxHJkXjP2tX&u zheR)-8Jvz_mze*Laood-pzU zmJH>4lrl;9muTFf$3H)=MHGI{cz zo)cv-ea_go`0=zS21t~gdx@g(6<^gDr{WlXX?s0+9T}TxiQlfy({&{>Q$fYf@nIV4 zCkD}*9M$&ogXM}dYO8_xwle9ML|&@Z-b6wYmXxEhVAYh|jP6)<)*5=CuvDXzb*54) za>&lh#>Em&M}7Vr`+3HBPIajEb5K!`o=?Q#JMHZf^HW+cHsWhxs_1x*hOq~iH5APc zJ9#4eEi7;O@`XS0H)@v2O-+$q+(_O)3~p7l0OgZ|}yo8{EcjI}tOeWJ>dJtwA&qMJdp@_KjYgGY!?^=$0q0jfk)45k{!)Gy zF*p?CZ0Gm4)h|Ache1dV(mG&=24)uGecVi?Jp%AjA$`zD51;EhMqud0N;K7Y!GFLD zR4jvDhOgu)|JwUqP_Q@^xRHGtSfC)1*d$66&TQNr297{z z*Ql$^LS5mOE%8fi@Q>7oo6N@Hsxdf}P>D4^0RebMv>iQ136Q?|EYL?qEzn*4nHBY@ zOn;@rTyHTc#!T=@^P&>@EN9z}hf|@8jHSp^(zlm}E?NfZ^N}_{vXfF8BArt@-_Enws!rM>3h@7+@H@7#;EZ=??m~OxQay;mCh4%ljlY&?07qaC! z-61i|@yw#T9bUybc;+OJRI>X0 zgqoghgvAF+!7@TNLMCrqBC+KlU~n*h?BU zxKD{D%pi(H2>k*bzF_B7v%1g`rF}si{Ut&=73!ta0@X4DNKf0Kux zzRfe>r{2x%cV^Dl*OBEa_3Q(n2GA9M1ZDK8Ywm0m2L%LJAMSm~$O-HC3Q%o7ee{(` z16;j%X#>++I9`Xobv~GwAInAA!j)NoA$1cHTd~_0CL*AiY+PS7L}acLZqa%H?uBR% z`H8<}l=N^RzD*kw%ca_TOZy)Q`QH|CAm73jn)MN040}uK)%OCTfRKP_%zA`yEg8Z4 z*&>Q{C6g%C$Fp%k9Qz`-|jGjn9E4K+~e9-cu~2nGs$y!jY`SZ8;RZ zEA&yjMEA@hi}4)6Q9M(w*}ONh|1&|=*B}o-gaw>8RjYLshettGb$xg4*T&ZIr3Jv_ zj-bM7ar_;13b(!kUX1li?q7h2 za^U{xf6CeFCF=ALn3*imfO=GaeD+Ttkaewm3x?XI7MnkEyd9v}CV+$DfZsuGs#I&E z^?ZronRtw2Tv^mwaCIUE<(26I=K8uvt%YQJ*p%DSu@(x3wh+^Vag%nehIVp7>zDqL zoZ9s%hJuqd%~3EJN(~HWi5ZhUdrbofL=2=G1owFnY=7hpPl`Kqwjq zNP_|>S8rr`?8g8n*BG_5!sQ`q=+T4iZGaaUz~we-!gbZyn>J~u3dK9SD8;;Axu5Ts z9ci?>yD%Fz0de3seW9R$nhaUOda))fz-=I&8ALmf`s@OCK@v!rL8bVjU8Wu}KFq(o zi~DEe!QE$6Tq;&5c?S;}`$s{N5eo>4iUSrdsJ@;Ly5vZfR-gRGvv_S23E5xGa*TlG zcs-EVC>j`eH2B`m(m=~FwvB&%KI-eQX`vu6~={ah4bcu&CG^KF>P%*{Gpk^ZE%C zI0QFr)`lw4S~CYAH-uytc!(qA3&VX4Po(xifr20LFd1s0vVcRk`mxmHY`2t>7 z&QJlk^jjLgeIh93KW!KFx?+m?@#Y3dLM^r@OFl(nHK?TVDM3iTLdvS?F`~DsSl1K+ ztbn8aM%`u_olB`FwYunv$!qHCY zwR-%D!drK*#$*KNV$A}4Z1clU&@;lKwE@CI+RfDj(N)KK1{9Y{XiJb!FOU~ z__l1GOhosku&#G5osBrRY$QbtK>3%qoQ~4HAI^5?N@4z<7Qf>~SuDT-M@t97L|A*(J2`U69kFM;$BuxRLp?>ZxYmXLm19 zvRzXLRWHJ9)s^5Bx*?V^H(6g`{HwYL9iyqM5xe@u-!UjTjMts0)P4E#ag*~ng~OZ$ zp=!pUqj1+FBBKPxXHL zPNYvm9~NDbp4rW7@}`3+5nGq%48c59K6eF*XMhCyq{SUSFes5prlribiMs;K^4AH; zFdRL5kB&aq);n)jG*7O*P2m~X`YMlI8mSxlnB61s$F`#Wx*B@GA?o8I5bvcUV-_WR zBu8TJPE7a`3G*>)G-!<(b6cR*wD9qYsit4t`yaKehCW65mE#nK&t?@BNhQK^e|K%) z;j|=`PTmt=j>2#o}Z(s=z)4W^*_@Ds=x?d9e*m<(bStoQT;-{(0&%5~P!nyB9FB;Df1F&2l+iACrnt>Wr#m- zMMK`vBRPUH*W_7$Dh=b-jdqMvxmf}Xl<6>2G8DqjuJD8cr>sD7J?X%jW;&S+JW}~rUj3Bu@Biq_3j|DKhObt^eKts z(Y(7^&Z`tl`HIr&#p=7((~PND4p|0EUlbT!LxDk5|E`N{zRFtcr_rJU06)!GG9+Lcz5Wrb@bQcqcb9m#kn%wqpRu`W1=+Y zH)HB*9?W-+h631=-NeT)e)IqfFdJmg-u_8z|NepoRWKU7!oNhd4rY5_4P5-BKKo9@ zq)KCD(c4EBL@`#WkkRnI{TXH5v=-k*wfgtmx%I5Q-fxlTUxEh*Gp6N|*zeGHLDU}U z(-dLsET(Hso?agpbDTw5lWnT*_@h5R|0Jo;o`~9hbTC4n(PgfEHmVDotYOmZLv-7G z1w@1g^g}n&5eRi%HBGcX8klT83qATo5giQBCQTE{g)y<=k|Ns(x^qii zCBDEjX&jh|D%ztES^&?Px~$X>RkF_>G{VQWeHNlx`nbk}DWI_0f0Y5R--=u#=qGJ? z`T7Q@v}w98rLUmBEde%j8YH9_Su-&f=vK^Jyb zGIgsq+HCjQeu$GF?7Ri|RSSS^+ms%K6n{{b-Wlo78&Xoz2Rlbo0s7-x5AbX2#^~f) zdIzfAfRtx~=mUyYIr=25mPdCiyP|4`oBefiwpI33c8O5ZJ^5*vjEk?i>0PP4Po6^lm(e zfvB3QY5vDgz5;%dYA722uU0Qd69nG)|KqQ~>9%{6<4k8lftWSQ2;{wqAPA`V7+9Mg zc+Ugj@(WPbWP(D5pvEoF&88d;J~#w}1T#+*-7h3WrZKQKm%<7*vrX_H2fKpu2@!>A zwKE;wC>ZFqM&}g%NALX*WO6}SEeVu%3k3Me!oLDv3plg6IsE9EZNb&lJ7``3^hqx!w_2deY(L0YbAg<;*$` zJ)R(p3vi%T6lO6HecZA0Mz><(13D~E{yo?o-3T$+>k$H?XYmt}Yt_ZN1s$9>6S_+5`EcB>eh4?#IAJ zqcKF<06MUo-#I-=D8a{Kcarh{3o5PskOqb5sdhYMO*H~Bnw?@!oaey*CO?)dZTYjo zp_58>n1i=*=Km$*QH$f!l>k+q7*Ir$2i@iZ&&wl6Pi|Lz7rkoejObEzqqNW(63bhE*sh;L)R22L-SqRrPT{c+}=Q#3e#bsc0GPC zsLB?A;RX#o1k8=@6vB zvd>lQD1DzPFgQPIEe1a&q{i9F8n7;_xPUJujc#LK!;Pv&ZAiR=ajDPr68fx9eZ90t zhwFi|Xc_Q1kvK8kF0q@x{s&!{94EKHfUa-`azd{_rF~~-zScJO7od$Are!`B%b?O3 zCmdBMYvTx`)|M<8K(x%kjgLzz2e73LlGjW`{u;zQ$37+YZq5fA=ll%r$9p4>VH3_IxbGq zw^sPF>|F&QLRCI%5&WJY-s5QH9#Sy3fi%-WC9bZ}!R>%Abt}Nb{8w*avHrt>k(RY; zy+fHHK)LboU$(=O7vOIHk@SGN3Y6W+Jo7evpX@cknQ?P^U=W5J)_b`daWOqeG!o-#R6v(h`I{s&H+-7 zcf{xmK;(Zdw~OO~nvr_YcCG9tDgT0Wg?gF}_4~?hG{A0i=irv05FLYI(zz7RTdPC< zrbCo8)GK%%Iem`#5j*FT6b)C3s$K>+A=XL*L@wv4w%>B446PUI~4 zE+D5@8?Lb1nr=i(^AD=!CT}9N6VAaX=j`WX@9sD4lpnVOm!&HQteil)#PNzBi3b_H z2N)q!+f{=g@|gOUbwv5(Gm9j4L!_J57J2!1iaH=jQ$tBb|Gt~Kyhxe{x6$bjjvUU8 zr>;*|JahV#UJQ7mOQP!SK?DJ(;oDkH#Dk9x{>C<)2NClbQ(;LUZh4_~l6STp6yV46 zWN|wyS{n9PEgw0?j|iGYq)s-w9j(WFdVr}=YcqvZxC}X9nKg@}Q&VwyW8SZ$^C|-& z{=NJpai87&@C@jJsIYF(wSzWXv3vs+j+mgyhiNALMg0sJmxl}6Q(s;JpUPh6dJy@$ zXdv-_cyY+!j)HXmB`@h+SAGq!iB=U_Sd8b(-wA_Qk6>==5sDN(yVNQ%zm52XZQd~6 z2duod)0#4DuS4E7Z822I%-diOWC=uZRJw-Bd?#~bl80F2^ht$2$5)xEYSV8Ir#j4U zAMJc9d=CExwK_q0Bt?pDk{C|9?7CnlpV_z)Gp+n^nxZVGR=;8dBzrxw{QIlZM&wZj zsWU}~Czy)l1@V~sGql~*LnrQr^y*FT|3Wyd))fA-D~GkRkQ5$g9`%%L28uheZIpoe zk5z@mT8t02^e3hj4uvc`<~frm+$4QrQ)rI$g+kr>Not{}GL-K@tV|+sjSK)VM`yp( zg0CpDr-mB0klL1r50hcK9Ag4nuP{m50q#BY#v2AQ3X<^_z+`MDP1zWW)t-`(kbHvb zGQ8=K(-=gU(7p}RN&0>&%WvL;wYAjROr}N5q}p*1g&B^r?0KwoDr_#}bZ4k`1_`~k zlXZ7Wy;C~Lcu2wHsGWgu!zmDtG|+`Y41c!_l)&R{ULH_5!9Q(SkZxa^C8lZjJ(D0S zNyMtJ8i^JAFZc%XDS^9k`R0)B#G=el-*d~CAs!rz3u`V)M2e<=eO)F&`RfYb92;8( zeSaOAXfy-Rj{S^c8zC3|DHwbJ23I{#X3 z_?P)bV9-hU$*FrZZ(2s?B2f`1u(37uC8+}F*p%&c4*bh(3)fL42?jCrNPYYYaK6l3 zRMwT=nk0h-gJ}6*w)DiPx?po^=e=yA{$fz)IH+HE6%$_=2Re{r1u8nc=BP3%-Q$37 zAW&l^q?d!n2uu3J^3JuCp2o(BJxY`Etf;vjt^z>x8-U~dE%N0h|I&4R0E*@5lhYV) zI}gX{8Tl=$@17t}r~fLdN0&Csz&#A!?unx2NHh)7#WssA*#sm=8$?OaCz>6}TJ!$i zCbR7}5)8lKGOL5XnJVsM#w;vd_Bdz_sffxk%Jr0VKnp%K4A17$iijz8O{N#8tc$AS zvgvY-2A*}EP0a%D8SlCZyZPG*rwpLfKQtR8!g(GaVHs1lo#0Ki$0KtNR;VLJOm>sc zZ{E~!kjNWQI@F4h+s@D50xo0IFS&e7VTojPX0+;%j}aykE=cWlqE^d`P`Fxo(jAlT zf7!P0#X!t3epk{TVRQ&$es&jX-o#bMPEL1J>Yk2+_Pcb%Nu_kph`zzJ%~NGt1>J;B zK%UFsWo67_memIdyClP=0T_et`^qfq4VVocYPX?MeWY2X4KlyQ+_A7toeWl^tQ*=y zUJz#eUSfp+-?GV?dZPOE;j}x57C*xY7FKszxZRrWZPCs?A`3D&5w6KfC zd{K#$?`Do>N0XQQzqOfMRJ>|bUz9z<74&3Q`o3*Fn`DzTYJ5H zdm;~87mA#8jcwn;KuH@Has}dhe?c+b%qpzj@!0q;2fEX(He@=3f%hV|!-6!}e`}`w zZ>!DIGEWgBeK`w%(uJxIBm7dMqPUK8`p=4+h@_o$^NnGG;;uRW1qrplwiF_KrC9}Mr*MFXIt0G z&qAWmp%^Ulh|C0sSTFs>6so$|70#&({3A0V^17Injv783lI1PriG>m51A!#u^m8=x z*tOJB`2u|8>THP}k!&uScfKJbUOtGp1Q$Jx#?#n@XS{9i*TMpXbNYNo5<3>+S@8HQ)EeJ`U>z}#f(1H0AhSJ2?R=djd=8RPA|GgC{J4DI-q7i)F)!P=mr!; z_0rD<7(;HtkdP`^-GWMvCIh9i4V29a4d3S5FH4yD^nl=XA^@T2L?&_1DB|UX>E&K*oo7te z6hDK>*ad>3c%iG+Hj7U}5vD~5EH1J;lze<8%k~#WeAUZILng|DCJh%$kK_>(*;LEn znkxqOh6q7N205fEB?OE|6AeZ_e#~{u>Q^7>#U%?qvg!T)*CK?PP(XwcnU4OSwYvK$ z;L`s;_$w@<sr8KxhGw7!jahcA|_F97W;aPU;gyeYe#7A?sIg`<5IHJranuz@u=s z!v7T&FIy}eNiD_>$=HF;R{@7w6s`_sh@AkfIl!h2KuHZn06;-sFrg3o;n9R!QLe-D zr$>wHCDrc3`lQ^i#jjxD@3CH6^!Qu?-OhcIx*|vii~CXzXT!E{)cd;z^Z-obkiB*1 ze(@J^)mRz-)5cq>AmeOgfnR%_)s{Qjq|dY4$Hs9;`P36DE*L+D-(H`^g4kWUL;imA z!`OnO?*X5yeeMzIwL6h6H>yS`3&1MR@HwTj8nCr~&mD@PYvFG^sO>uvkd+Zo1usYA1B=Q#}0!bv(}h1^2bw*E;Q2X%p#C7`^9s=r^q{E%#!_KP-fn zW?tb%SuuTKk3wjn&CA#B8vccE2x>knMMnqK`P5)D@Y(2VC6v?yGEh2EPvbMW0(R`c_i8d8EMa_jBgIx5PxB z;tDr#pWohW{Tb+#MBNA^zNckOf8Qee!FDU|Ud1QZ6!J(ztr-Tz2l*#zD1%-qxx8wUAe|Mu_rqtCQ zCbM514d=q6g!XUexobLx!~vEpKv)%LUf|> zi!A5CM7G8T?Pqk0Rw(gqEgirp@lwVrsZZFxsW>>zNMUNwqbT!e<;O1f{lK(c^=eM= zSB?jZjyI^XO($Hg3ihK+$3Q{W52*Z_P143GxzWge&VB->^>8$BhYU{JRXD0lndUd# z7&xecuPk5z-)s^O75}egXBj~=^22x7xR>0onfA+!w?5|0uUz}T=gisN9qjP+9dooW zjapuJ^E83}l+(|v(#Wl2qz^ycl3uxN5IOb)99EmI4VK^X{yFLFyr%l73Je`tnDO5D z%~z!=0|k*{j1}!mamK&B-fzH;cC)hLz>Bw@D+u$j`W3W`;rGY-VYmmSx>W5d(++*# z%A6%bt4|Z?lFqfbIe8n`u9|L%!v5Z1a`bO!iLe-7j!T^5X6bf-yJWFhC20x1bKo*% zJI%gj>kN^wmBQ_h;c^nXAyCr1qhm+k?Vr|s)J{P28f*4M;~b2PL7^k{>exafuF?c)Z6|c&ed1z z3tQsZ-QJGX`8tE`?(WV)mhdj2YU>yW1I_ zNn25`T17#p+;Fkm8qXQj?i2OWMBw$>p8n8kO=*9>aD3)9J-c7_=CWA4&>(I zZ)u|oUxgWOh+S3Moa&D))~!{aYqlSrE|hJuOz||Q+3zM^e?7;wH)uJjw0A$)ZLXvN;k@U>9!t81HFllGxo=IJN#~or@N9L4y?WH!M9%ARjS{FDur?K zC`zE5@U#herjAWLUISaSSzNIi ztC~A<=|Fn=7klj~*9V_HO7F}Ag7JJ2P(Z*rq7-k8_zD9G4FV$C1*32ARTMsz1OgJC zuQ)9V-jozlVA{VOe`O)$Zi~r$L_QUR>vdhau)d@&t^`q_22$ z*fsQCNPMYXmlK< zdslWv52~!upx?2&zwxe#79YE^v-MOn#5CMV%J~^;Gq!8? z%O4f@@!}kGPsKa<{XVuv&NwYE@gEp+hgvLKINPMoAk^Bbrs7hwK^K?*qGQ_Dpm})b zy&b>7z?&l3KSW14lEIPhhkpf^JS;9MlEtACPCfu``@X2!>NA4<`I1w+H_ugL*x-!N zg=e#)ye-52V_U~L)w1U*Q@Rlu8F_;uXP1f_H6~g{akkt?Re!8YrK+ZtofGnXgVuzM zf=F5ER99(jc$Wp9T5q`Urj2Ph;>Hg=kO+S2NDQB(OXi(tcm;-R=eno1U3_zEg{X8F zf(>5b?6j<-YHFtW;YmDuF=^7>Rq8T5Eu}fg02fT0W226;zwLD$fNtToLdW#f`#YYC%@{>QXm0}1G3i_AxVTWQA7BVDG^RXsP1$S1jNszn! zyK^6deaqUds;Ubuo6FnvtxGm)afN!WcnoxvxeoL-2YqcgoiY8*R!jK6p&h2`9E|Lm zo)p_V{o;TwVF_(^{>vUKb@{i%nP-IKKi*v~^(fSOcSvg?5~CZup7Cti;tJn$9|nq1HU=jbtn7!Ggxr4jg0lwS+18psjx0jynYg{LFB2x~E38?CqPB z#qHL7WcE@nv7qPRhQb=IM8v( z{tRo!pG{Xx8~8}ui7fp5Yy;Dcpdm&t#mL=vf0aDLHMI0v*mlN%d>eWyYLNY8Y~Ma_ zugv&e_V%(Z-?V)%`U7(*86JViOQO2pE?*IT2R`(HQ}29>oRRGE^k*;r^zZ(Kh&Mbm zFq(L7Uza=CR3pe1+-u-vQ-Z0c6+Il1j#yu(9hqa`Ge2@P-xC>je0UfSol87BUA*B; zUsQ3eCDE?lXsvoKOns-KcqGkEmEoD}o%0yNN4@o+xlr}mz_Z+HW!PKu3GTE6`Tzda z9z`POt7mf0rHmz|06a>hs%dt=YIjvptCOjQyXvdp%Q7#lXXP_@Lxm;ptiqyd9aDgS zALDr#wjxgQc~8{fbKfbQHy%K z-&CXi!uuuZy2>SUQ2{lr5X(LEU!_Oph5meM= zXtMaopvvGaeKz|z$u85<^~-r?(_^hb9lGR)wZ-LqR(l>h9B*Z*{>^@51e;0-VcTJE z`4sX5ZP!FxI7%`8z7DLeUu#80zyEhE#vf@kRCcZBmuWuoA*F%~;hcOEJq*gw$Ri+4 zf&W*W^0%7TJa}^-9vxmh?|jXY?qz4c7X9C}w~Th8MR7gfX1|1`xU()b#rY&r<`W_G zMC+r>t0}S}z4N}Fp|Ihr&88_Iew0TEy?1`}COc;x?rM9R{h_6%pZx>+$VmTPKJrT% zqb$_vwCV87mg1h|RT|oAt|$K{iqa=|741{utIacY1XCpZFCssy2K{@CLFiq1srgkP zv;pS-2!M&|LwJn;4Q6pT*do;z{BO@Dd~wa4#}2<{9 literal 0 HcmV?d00001 From dde0275d2019b2fbe6362e3a9a1cd632e1b0dd19 Mon Sep 17 00:00:00 2001 From: Arathy Kumar Date: Sat, 20 Apr 2024 14:01:35 +0530 Subject: [PATCH 09/15] fix: removed the date-picker child components from package.json --- elements/package.json | 6 ------ 1 file changed, 6 deletions(-) diff --git a/elements/package.json b/elements/package.json index 88ad91e74f..c60fc3a606 100644 --- a/elements/package.json +++ b/elements/package.json @@ -34,12 +34,6 @@ "./pf-code-block/BaseCodeBlock.js": "./pf-code-block/BaseCodeBlock.js", "./pf-code-block/pf-code-block.js": "./pf-code-block/pf-code-block.js", "./pf-date-picker/pf-date-picker.js": "./pf-date-picker/pf-date-picker.js", - "./pf-date-picker/date-picker-helper.js": "./pf-date-picker/date-picker-helper.js", - "./pf-date-picker/pf-calendar.js": "./pf-date-picker/pf-calendar.js", - "./pf-date-picker/pf-month-select.js": "./pf-date-picker/pf-month-select.js", - "./pf-date-picker/pf-next-button.js": "./pf-date-picker/pf-next-button.js", - "./pf-date-picker/pf-previous-button.js": "./pf-date-picker/pf-previous-button.js", - "./pf-date-picker/pf-year-input.js": "./pf-date-picker/pf-year-input.js", "./pf-dropdown/pf-dropdown.js": "./pf-dropdown/pf-dropdown.js", "./pf-dropdown/pf-dropdown-group.js": "./pf-dropdown/pf-dropdown-group.ts", "./pf-dropdown/pf-dropdown-menu.js": "./pf-dropdown/pf-dropdown-menu.ts", From 988447d7dccea276bafd27ddaf7b7bd568e3567f Mon Sep 17 00:00:00 2001 From: Arathy Kumar Date: Sat, 20 Apr 2024 14:24:39 +0530 Subject: [PATCH 10/15] fix: added constants in the helper file --- elements/pf-date-picker/date-picker-helper.ts | 2 ++ elements/pf-date-picker/pf-calendar.ts | 11 ++++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/elements/pf-date-picker/date-picker-helper.ts b/elements/pf-date-picker/date-picker-helper.ts index 03a0ddbfec..d92eab3cdf 100644 --- a/elements/pf-date-picker/date-picker-helper.ts +++ b/elements/pf-date-picker/date-picker-helper.ts @@ -11,6 +11,8 @@ export interface InputDate { } export const days: string[] = ['S', 'M', 'T', 'W', 'T', 'F', 'S']; +export const defaultWeekdays: number[] = [0, 1, 2, 3, 4, 5, 6]; // S, M, T, W, T, F, S +export const defaultWeeks: number[] = [0, 1, 2, 3, 4, 5]; // 1 previous month week, 4 current month weeks, 1 next month week // Function to return date object export const getFormattedDate = (date: Date) => { diff --git a/elements/pf-date-picker/pf-calendar.ts b/elements/pf-date-picker/pf-calendar.ts index 828c4af934..69d3821f26 100644 --- a/elements/pf-date-picker/pf-calendar.ts +++ b/elements/pf-date-picker/pf-calendar.ts @@ -3,7 +3,12 @@ import { customElement } from 'lit/decorators/custom-element.js'; import { property } from 'lit/decorators/property.js'; import { ref, createRef } from 'lit/directives/ref.js'; import styles from './pf-calendar.css'; -import { getFormattedDate, getMonthNamesFromLocale } from './date-picker-helper.js'; +import { + getFormattedDate, + getMonthNamesFromLocale, + defaultWeekdays, + defaultWeeks +} from './date-picker-helper.js'; import { ComposedEvent } from '@patternfly/pfe-core/core.js'; export interface FocusedDateValues{ @@ -51,8 +56,8 @@ export class PfCalendar extends LitElement { static readonly styles = [styles]; private currentDate: Date = new Date(); - private weekdays: number[] = [0, 1, 2, 3, 4, 5, 6]; // S, M, T, W, T, F, S - private weeks: number[] = [0, 1, 2, 3, 4, 5]; // 1 previous month week, 4 current month weeks, 1 next month week + private weekdays: number[] = defaultWeekdays; + private weeks: number[] = defaultWeeks; // Input properties from the parent @property() currentYear: number = this.currentDate.getFullYear(); From 412b41d803b47a78acd483fd14eff0d8617a88a7 Mon Sep 17 00:00:00 2001 From: Arathy Kumar Date: Sat, 20 Apr 2024 14:30:54 +0530 Subject: [PATCH 11/15] fix: removed the constructor --- elements/pf-date-picker/pf-calendar.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/elements/pf-date-picker/pf-calendar.ts b/elements/pf-date-picker/pf-calendar.ts index 69d3821f26..61e5acda5b 100644 --- a/elements/pf-date-picker/pf-calendar.ts +++ b/elements/pf-date-picker/pf-calendar.ts @@ -75,10 +75,6 @@ export class PfCalendar extends LitElement { @property() monthNames: string[] = getMonthNamesFromLocale(this.translationLanguageCode); focusDateRef: any = createRef(); // Reference to the button that needs to be focused - constructor() { - super(); - } - connectedCallback() { super.connectedCallback(); this.#init(); From 543394f5e3b56a01f103a71d6aeaca71588ebd49 Mon Sep 17 00:00:00 2001 From: Arathy Kumar Date: Mon, 22 Apr 2024 22:22:50 +0530 Subject: [PATCH 12/15] fix: changed the format of DOM properties from camelCase to dash-case --- elements/pf-date-picker/demo/date-format.html | 4 ++-- elements/pf-date-picker/demo/date-input.html | 4 ++-- elements/pf-date-picker/demo/disabled.html | 6 +++--- elements/pf-date-picker/demo/localization.html | 4 ++-- .../pf-date-picker/demo/min-and-max-date.html | 6 +++--- elements/pf-date-picker/demo/translation.html | 4 ++-- elements/pf-date-picker/pf-date-picker.ts | 16 ++++++++-------- 7 files changed, 22 insertions(+), 22 deletions(-) diff --git a/elements/pf-date-picker/demo/date-format.html b/elements/pf-date-picker/demo/date-format.html index aca6c137f4..82b45f5ef0 100644 --- a/elements/pf-date-picker/demo/date-format.html +++ b/elements/pf-date-picker/demo/date-format.html @@ -1,8 +1,8 @@

Date Format

-
<pf-date-picker dateFormatInput="YYYY-DD-MM"></pf-date-picker>
- +
<pf-date-picker date-format-input="YYYY-DD-MM"></pf-date-picker>
+

The date picker supports the following 12 date formats:

'DD/MM/YYYY', 'MM/DD/YYYY', 'YYYY/MM/DD', 'YYYY/DD/MM', 'DD-MM-YYYY', 'MM-DD-YYYY', 'YYYY-MM-DD', 'YYYY-DD-MM', 'DD.MM.YYYY', 'MM.DD.YYYY', 'YYYY.MM.DD', 'YYYY.DD.MM'

diff --git a/elements/pf-date-picker/demo/date-input.html b/elements/pf-date-picker/demo/date-input.html index d837d1ef7a..051165741e 100644 --- a/elements/pf-date-picker/demo/date-input.html +++ b/elements/pf-date-picker/demo/date-input.html @@ -1,10 +1,10 @@

Set Input Date

-
<pf-date-picker inputDate=${new Date(2023, 0, 1)}></pf-date-picker>
+
<pf-date-picker input-date=${new Date(2023, 0, 1)}></pf-date-picker>
- +

The default date value can be passed to the date picker to set the initial date that is displayed.

In the above given example, the date set to be displayed initially is January 1, 2023.

diff --git a/elements/pf-date-picker/demo/disabled.html b/elements/pf-date-picker/demo/disabled.html index 52a084385c..cd17d77957 100644 --- a/elements/pf-date-picker/demo/disabled.html +++ b/elements/pf-date-picker/demo/disabled.html @@ -1,9 +1,9 @@

Disabled

-
<pf-date-picker isDisabled="true"></pf-date-picker>
- -

The isDisabled attribute can be used to disable the date picker.

+
<pf-date-picker disabled="true"></pf-date-picker>
+ +

The disabled attribute can be used to disable the date picker.

diff --git a/elements/pf-date-picker/demo/localization.html b/elements/pf-date-picker/demo/localization.html index 3b3a97da24..23d8dad125 100644 --- a/elements/pf-date-picker/demo/localization.html +++ b/elements/pf-date-picker/demo/localization.html @@ -1,8 +1,8 @@

Localization

-
<pf-date-picker localizationLanguageCode="fi"></pf-date-picker>
- +
<pf-date-picker localization-language-code="fi"></pf-date-picker>
+

The locale string can be passed to the date picker to set the date format.

In the above given example, the locale is set to Finnish: fi

The date format is set globally, independent of the user's locale. diff --git a/elements/pf-date-picker/demo/min-and-max-date.html b/elements/pf-date-picker/demo/min-and-max-date.html index 4e441d6030..217b85aed7 100644 --- a/elements/pf-date-picker/demo/min-and-max-date.html +++ b/elements/pf-date-picker/demo/min-and-max-date.html @@ -1,14 +1,14 @@

Set minimum and maximum date range

-
<pf-date-picker minDate=${new Date(2023, 0, 2)} minDate=${new Date(2023, 0, 20)} 
+    
<pf-date-picker min-date=${new Date(2023, 0, 2)} max-date=${new Date(2023, 0, 20)} 
       inputDate=${new Date(2023, 0, 3)}></pf-date-picker>
+ min-date="Mon Jan 02 2023 00:00:00 GMT+0530 (India Standard Time)" + max-date="Fri Jan 20 2023 00:00:00 GMT+0530 (India Standard Time)">

The minimum and maximum valid dates can be passed to the date picker to restrict the range of dates that can be selected

In the above given example, the minimum valid date diff --git a/elements/pf-date-picker/demo/translation.html b/elements/pf-date-picker/demo/translation.html index 8f1b5b3260..841535beec 100644 --- a/elements/pf-date-picker/demo/translation.html +++ b/elements/pf-date-picker/demo/translation.html @@ -1,8 +1,8 @@

Translation

-
<pf-date-picker translationLanguageCode="fr"></pf-date-picker>
- +
<pf-date-picker translation-language-code="fr"></pf-date-picker>
+

The translation language string can be passed to the date picker to localize the month names.

In the above given example, the language is set to French: fr

diff --git a/elements/pf-date-picker/pf-date-picker.ts b/elements/pf-date-picker/pf-date-picker.ts index a3c9072b2a..051ecd8927 100644 --- a/elements/pf-date-picker/pf-date-picker.ts +++ b/elements/pf-date-picker/pf-date-picker.ts @@ -70,20 +70,20 @@ export class PfDatePicker extends LitElement { // ------------------------Input properties from parent-------------------------------// // -----------[***Only these properties can be used to pass data from parent***] ------------ // - @property({ reflect: true }) minDate: Date = new Date(1900, 0, 1); // Default minimum valid date set as 1st January 1900 - @property({ reflect: true }) maxDate: Date = new Date(9999, 11, 31); // Default maximum valid date set as 31st December 9999 + @property({ reflect: true, attribute: 'min-date' }) minDate: Date = new Date(1900, 0, 1); // Default minimum valid date set as 1st January 1900 + @property({ reflect: true, attribute: 'max-date' }) maxDate: Date = new Date(9999, 11, 31); // Default maximum valid date set as 31st December 9999 - @property({ reflect: true }) inputDateWithUniqueTimeStamp!: string; // Handle date value with a unique time stamp that parent sends to the component. + @property({ reflect: true, attribute: 'input-date' }) inputDateWithUniqueTimeStamp!: string; // Handle date value with a unique time stamp that parent sends to the component. // The format: inputDateWithUniqueTimeStamp = (new Date(2024, 3, 2)).toDateString() +'#'+ (Date.now() + Math.random()) // - @property({ reflect: true }) isDisabled = false; // Handles if the date picker is disabled or not - @property({ reflect: true }) localizationLanguageCode!: string; // Language code for date format based on localization - @property({ reflect: true }) translationLanguageCode!: string; // Language code for translation of date input and month names - @property({ reflect: true }) dateFormatInput!: // Date format input from parent + @property({ reflect: true, attribute: 'disabled' }) isDisabled = false; // Handles if the date picker is disabled or not + @property({ reflect: true, attribute: 'localization-language-code' }) localizationLanguageCode!: string; // Language code for date format based on localization + @property({ reflect: true, attribute: 'translation-language-code' }) translationLanguageCode!: string; // Language code for translation of date input and month names + @property({ reflect: true, attribute: 'date-format-input' }) dateFormatInput!: // Date format input from parent 'DD/MM/YYYY' | 'MM/DD/YYYY' | 'YYYY/MM/DD' | 'YYYY/DD/MM' | 'DD-MM-YYYY' | 'MM-DD-YYYY' | 'YYYY-MM-DD' | 'YYYY-DD-MM' | 'DD.MM.YYYY' | 'MM.DD.YYYY' | 'YYYY.MM.DD' | 'YYYY.DD.MM'; - @property({ reflect: true }) placeholderTextWithUniqueCode!: string; // Placeholder from parent + @property({ reflect: true, attribute: 'placeholder' }) placeholderTextWithUniqueCode!: string; // Placeholder from parent // The format: placeholderTextWithUniqueCode = 'placeholder-text' + '#' + Math.random(); // ----------------------Input properties from parent ends--------------------------------- // From c04282eda93129d56a19918229fbefa002220461 Mon Sep 17 00:00:00 2001 From: Arathy Kumar Date: Sat, 29 Jun 2024 19:12:20 +0530 Subject: [PATCH 13/15] chore: readme updated --- elements/pf-date-picker/README.md | 77 +++++++++++++++++++++++++++++-- 1 file changed, 73 insertions(+), 4 deletions(-) diff --git a/elements/pf-date-picker/README.md b/elements/pf-date-picker/README.md index 950c7e68e0..cb75687097 100644 --- a/elements/pf-date-picker/README.md +++ b/elements/pf-date-picker/README.md @@ -1,11 +1,80 @@ # Date Picker -Add a description of the component here. + +The Date Picker component lets users choose dates within a set range defined by both a minimum and maximum date. This feature makes it easy for users to select dates that fall within the specified limits ## Usage -Describe how best to use this web component along with best practices. + +The date-picker component includes an input field for entering dates, a toggle for opening and closing the calendar, and a popup calendar interface for selecting dates. ```html - +
+

Date Format

+
<pf-date-picker date-format-input="YYYY-DD-MM"></pf-date-picker>
+ +

The date picker supports the following 12 date formats:

+

'DD/MM/YYYY', 'MM/DD/YYYY', 'YYYY/MM/DD', 'YYYY/DD/MM', 'DD-MM-YYYY', 'MM-DD-YYYY', 'YYYY-MM-DD', + 'YYYY-DD-MM', 'DD.MM.YYYY', 'MM.DD.YYYY', 'YYYY.MM.DD', 'YYYY.DD.MM'

+

The date format is set globally, independent of the user's locale. + This means that all users will see the same date format, regardless of their language or region.

+
+ +
+

Set Input Date

+
<pf-date-picker input-date=${new Date(2023, 0, 1)}></pf-date-picker>
+ + +

The default date value can be passed to the date picker to set the initial date that is displayed.

+

In the above given example, the date set to be displayed initially is January 1, 2023.

+
+ +
+

Disabled

+
<pf-date-picker disabled="true"></pf-date-picker>
+ +

The disabled attribute can be used to disable the date picker.

+
-
+
+

Localization

+
<pf-date-picker localization-language-code="fi"></pf-date-picker>
+ +

The locale string can be passed to the date picker to set the date format.

+

In the above given example, the locale is set to Finnish: fi

+

The date format is set globally, independent of the user's locale. + This means that all users will see the same date format, regardless of their language or region.

+
+ +
+

Set minimum and maximum date range

+
<pf-date-picker min-date=${new Date(2023, 0, 2)} max-date=${new Date(2023, 0, 20)} 
+      inputDate=${new Date(2023, 0, 3)}></pf-date-picker>
+ + + +

The minimum and maximum valid dates can be passed to the date picker to restrict the range of dates that can be selected

+

In the above given example, the minimum valid date + is January 2, 2023, and the maximum valid date is January 20, 2023

+
+ +
+

Basic

+
<pf-date-picker></pf-date-picker>
+ +

The basic date picker will use the user's locale to determine the date format. The default minimum valid date + is January 1, 1900, and the default maximum valid date is December 31, 9999.

+
+ +
+

Translation

+
<pf-date-picker translation-language-code="fr"></pf-date-picker>
+ +

The translation language string can be passed to the date picker to localize the month names.

+

In the above given example, the language is set to French: fr

+
``` + From 9580ff16fa7a4b2d6fc89465377768975f09d4a0 Mon Sep 17 00:00:00 2001 From: Arathy Kumar Date: Sat, 29 Jun 2024 20:23:57 +0530 Subject: [PATCH 14/15] fix: changed extend composedevent to event --- elements/pf-date-picker/pf-calendar.ts | 70 ++++++----- elements/pf-date-picker/pf-date-picker.ts | 109 ++++++++++++++---- elements/pf-date-picker/pf-month-select.ts | 13 ++- elements/pf-date-picker/pf-next-button.ts | 5 +- elements/pf-date-picker/pf-previous-button.ts | 5 +- 5 files changed, 137 insertions(+), 65 deletions(-) diff --git a/elements/pf-date-picker/pf-calendar.ts b/elements/pf-date-picker/pf-calendar.ts index 61e5acda5b..33f236130c 100644 --- a/elements/pf-date-picker/pf-calendar.ts +++ b/elements/pf-date-picker/pf-calendar.ts @@ -7,17 +7,16 @@ import { getFormattedDate, getMonthNamesFromLocale, defaultWeekdays, - defaultWeeks + defaultWeeks, } from './date-picker-helper.js'; -import { ComposedEvent } from '@patternfly/pfe-core/core.js'; -export interface FocusedDateValues{ +export interface FocusedDateValues { day: number; month: number; year: number; } -export interface CalendarDateValues{ +export interface CalendarDateValues { day: number; month: number; year: number; @@ -29,21 +28,21 @@ export interface CalendarDateValues{ * @slot - Place element content here */ -export class DayChangeEvent extends ComposedEvent { +export class DayChangeEvent extends Event { constructor(public event: Event, public day: number, public month: number, public year: number) { - super('daySelected'); + super('daySelected', { bubbles: true, cancelable: true }); } } -export class FocusChangeEvent extends ComposedEvent { +export class FocusChangeEvent extends Event { constructor(public dateToBeFocusedRef: HTMLButtonElement | undefined) { - super('setDateFocus'); + super('setDateFocus', { bubbles: true, cancelable: true }); } } -export class KeyboardNavigationFocusEvent extends ComposedEvent { +export class KeyboardNavigationFocusEvent extends Event { constructor(public event: Event, public day: number, public month: number, public year: number) { - super('onCalendarKeydown'); + super('onCalendarKeydown', { bubbles: true, cancelable: true }); } } @@ -84,7 +83,9 @@ export class PfCalendar extends LitElement { } render() { - this.focusedDateValues = getFormattedDate(new Date(this.currentYear, this.currentMonth, this.dayToBeFocused)); + this.focusedDateValues = getFormattedDate( + new Date(this.currentYear, this.currentMonth, this.dayToBeFocused) + ); // Get the total number of days of the current month const totalDays: number = new Date(this.currentYear, this.currentMonth + 1, 0).getDate(); @@ -93,7 +94,8 @@ export class PfCalendar extends LitElement { const firstDay: number = new Date(this.currentYear, this.currentMonth, 1).getDay(); // Get the last date of the previous month - const previousMonthLastDate: number = new Date(this.currentYear, this.currentMonth, 0).getDate(); + const previousMonthLastDate: number = + new Date(this.currentYear, this.currentMonth, 0).getDate(); // Get the number of weeks in a month this.weeks = this.#getNoOfWeeks(this.currentYear, this.currentMonth); @@ -113,7 +115,7 @@ export class PfCalendar extends LitElement { day: (previousMonthLastDate - (firstDay - 1) + weekDay), month: this.currentMonth - 1, year: this.currentYear, - isCurrentMonth: false + isCurrentMonth: false, }; calendarDayTemplate = this.#renderCalendarDates(dateValue); } else if (day > totalDays) { @@ -122,7 +124,7 @@ export class PfCalendar extends LitElement { day: nextMonthPointer + 1, month: this.currentMonth + 1, year: this.currentYear, - isCurrentMonth: false + isCurrentMonth: false, }; calendarDayTemplate = this.#renderCalendarDates(dateValue); nextMonthPointer++; @@ -132,7 +134,7 @@ export class PfCalendar extends LitElement { day: day, month: this.currentMonth, year: this.currentYear, - isCurrentMonth: true + isCurrentMonth: true, }; calendarDayTemplate = this.#renderCalendarDates(dateValue); day++; @@ -146,22 +148,24 @@ export class PfCalendar extends LitElement { // Function to render the days of the calendar #renderCalendarDates(value: CalendarDateValues) { - const isDayToBeFocused: boolean = ((value.day === this.dayToBeFocused) && - (value.month === this.focusedDateValues?.month) && - (value.year === this.focusedDateValues?.year)); + const isDayToBeFocused: boolean = ((value.day === this.dayToBeFocused) + && (value.month === this.focusedDateValues?.month) + && (value.year === this.focusedDateValues?.year)); const selectedDate: Date = new Date(this.dateSelected); - const isToday: boolean = ((value.day === this.currentDate.getDate()) && - (value.month === this.currentDate.getMonth()) && - (value.year === this.currentDate.getFullYear())); + const isToday: boolean = ((value.day === this.currentDate.getDate()) + && (value.month === this.currentDate.getMonth()) + && (value.year === this.currentDate.getFullYear())); - const isSelectedDay: boolean = ((value.day === selectedDate?.getDate()) && - (value.month === selectedDate?.getMonth()) && - (value.year === selectedDate?.getFullYear())); + const isSelectedDay: boolean = ((value.day === selectedDate?.getDate()) + && (value.month === selectedDate?.getMonth()) + && (value.year === selectedDate?.getFullYear())); const dateRendering = new Date(value.year, value.month, value.day); const isDateInvalid = dateRendering.toString() === 'Invalid Date'; - const isDateDisabled: boolean = isDateInvalid || dateRendering < this.minDate || dateRendering > this.maxDate; + const isDateDisabled: boolean = isDateInvalid + || dateRendering < this.minDate || dateRendering > this.maxDate; + const dateValue = html`
@@ -506,12 +550,18 @@ export class PfDatePicker extends LitElement { // const regex = /^[0-9./-]*$/g; // Commented for reference // Parse the date parts to integers - const dateValues = getDateValues(dateString, this.localizationLanguageCode, this.dateFormatInput); + const dateValues = getDateValues( + dateString, + this.localizationLanguageCode, + this.dateFormatInput + ); const selectedDayInput: number = dateValues.day; const selectedMonthInput: number = dateValues.month; const selectedYearInput: number = dateValues.year; const date: Date = new Date(selectedYearInput, selectedMonthInput - 1, selectedDayInput); - const regex = new RegExp(getRegexPattern(this.localizationLanguageCode, this.dateFormatInput)); + const regex = new RegExp( + getRegexPattern(this.localizationLanguageCode, this.dateFormatInput) + ); if (date.toString() === 'Invalid Date') { this.errorMessage = this.errorMessages.inValid; @@ -534,14 +584,15 @@ export class PfDatePicker extends LitElement { } // Check the ranges of month and year - if (selectedYearInput < this.minYear || selectedYearInput > this.maxYear || - selectedMonthInput === 0 || selectedMonthInput > 12) { + if (selectedYearInput < this.minYear || selectedYearInput > this.maxYear + || selectedMonthInput === 0 || selectedMonthInput > 12) { this.errorMessage = this.errorMessages.inValid; return false; } // Adjust for leap years - if (selectedYearInput % 400 === 0 || (selectedYearInput % 100 !== 0 && selectedYearInput % 4 === 0)) { + if (selectedYearInput % 400 === 0 || ( + selectedYearInput % 100 !== 0 && selectedYearInput % 4 === 0)) { monthLength[1] = 29; } @@ -573,7 +624,13 @@ export class PfDatePicker extends LitElement { mm = `0${mm}`; } - this.formattedDate = getDateFormat(dd, mm, yyyy, this.localizationLanguageCode, this.dateFormatInput); + this.formattedDate = getDateFormat( + dd, + mm, + yyyy, + this.localizationLanguageCode, + this.dateFormatInput + ); } /* The functions #removePFPopoverDialogFromDOM() and #addPFPopoverDialogToDOM() are added in in order to prevent @@ -584,11 +641,13 @@ export class PfDatePicker extends LitElement { horizontal scroll issues, particularly when the popover is positioned at the far right edge of the page. */ #removePFPopoverDialogFromDOM() { - this._popover?.shadowRoot?.querySelector('dialog#popover')?.setAttribute('style', 'display:none'); + this._popover?.shadowRoot?.querySelector('dialog#popover')?. + setAttribute('style', 'display:none'); } #addPFPopoverDialogToDOM() { - this._popover?.shadowRoot?.querySelector('dialog#popover')?.setAttribute('style', 'display:block'); + this._popover?.shadowRoot?.querySelector('dialog#popover')?. + setAttribute('style', 'display:block'); } } diff --git a/elements/pf-date-picker/pf-month-select.ts b/elements/pf-date-picker/pf-month-select.ts index a22085b893..32a4c7d309 100644 --- a/elements/pf-date-picker/pf-month-select.ts +++ b/elements/pf-date-picker/pf-month-select.ts @@ -7,17 +7,16 @@ import styles from './pf-month-select.css'; import { createRef, ref } from 'lit/directives/ref.js'; import { ifDefined } from 'lit/directives/if-defined.js'; import { getMonthNamesFromLocale } from './date-picker-helper.js'; -import { ComposedEvent } from '@patternfly/pfe-core/core.js'; -export class MonthChangeEvent extends ComposedEvent { +export class MonthChangeEvent extends Event { constructor(public event: Event, public name: string, public index: number) { - super('currentMonth'); + super('currentMonth', { bubbles: true, cancelable: true }); } } -export class MonthPopupStateChangeEvent extends ComposedEvent { +export class MonthPopupStateChangeEvent extends Event { constructor(public event: Event, public isExpanded: boolean) { - super('monthExpandState'); + super('monthExpandState', { bubbles: true, cancelable: true }); } } @@ -111,7 +110,9 @@ export class PfMonthSelect extends LitElement { this.currentMonthIndex = monthIndex; if (monthName && (monthName.toString() !== this.currentMonthName)) { this.currentMonthName = monthName.toString(); - this.dispatchEvent(new MonthChangeEvent(event, this.currentMonthName, this.currentMonthIndex)); + this.dispatchEvent( + new MonthChangeEvent(event, this.currentMonthName, this.currentMonthIndex) + ); } this.isMonthExpanded = !this.isMonthExpanded; } diff --git a/elements/pf-date-picker/pf-next-button.ts b/elements/pf-date-picker/pf-next-button.ts index 3b497576db..a611905613 100644 --- a/elements/pf-date-picker/pf-next-button.ts +++ b/elements/pf-date-picker/pf-next-button.ts @@ -1,13 +1,12 @@ import { LitElement, html } from 'lit'; import { customElement } from 'lit/decorators/custom-element.js'; import { property } from 'lit/decorators/property.js'; -import { ComposedEvent } from '@patternfly/pfe-core/core.js'; import styles from './pf-next-button.css'; -export class NextButtonClickEvent extends ComposedEvent { +export class NextButtonClickEvent extends Event { constructor(public event: Event, public month: number, public year: number) { - super('nextMonthAndYear'); + super('nextMonthAndYear', { bubbles: true, cancelable: true }); } } diff --git a/elements/pf-date-picker/pf-previous-button.ts b/elements/pf-date-picker/pf-previous-button.ts index d56bc5ac5a..8352b1c1f4 100644 --- a/elements/pf-date-picker/pf-previous-button.ts +++ b/elements/pf-date-picker/pf-previous-button.ts @@ -1,12 +1,11 @@ import { LitElement, html } from 'lit'; import { customElement } from 'lit/decorators/custom-element.js'; import { property } from 'lit/decorators/property.js'; -import { ComposedEvent } from '@patternfly/pfe-core/core.js'; import styles from './pf-previous-button.css'; -export class PreviousButtonClickEvent extends ComposedEvent { +export class PreviousButtonClickEvent extends Event { constructor(public event: Event, public month: number, public year: number) { - super('previousMonthAndYear'); + super('previousMonthAndYear', { bubbles: true, cancelable: true }); } } /** From e9e983369ec42d5425a9435c587b3cc76d0d5f52 Mon Sep 17 00:00:00 2001 From: Arathy Kumar Date: Sun, 30 Jun 2024 00:23:41 +0530 Subject: [PATCH 15/15] fix: added ecmascript private variables --- elements/pf-date-picker/pf-calendar.ts | 24 +++---- elements/pf-date-picker/pf-date-picker.ts | 78 +++++++++++------------ elements/pf-date-picker/pf-year-input.ts | 10 +-- 3 files changed, 56 insertions(+), 56 deletions(-) diff --git a/elements/pf-date-picker/pf-calendar.ts b/elements/pf-date-picker/pf-calendar.ts index 33f236130c..170e9ea64d 100644 --- a/elements/pf-date-picker/pf-calendar.ts +++ b/elements/pf-date-picker/pf-calendar.ts @@ -54,15 +54,15 @@ export class KeyboardNavigationFocusEvent extends Event { export class PfCalendar extends LitElement { static readonly styles = [styles]; - private currentDate: Date = new Date(); - private weekdays: number[] = defaultWeekdays; - private weeks: number[] = defaultWeeks; + #currentDate = new Date(); + #weekdays = defaultWeekdays; + #weeks = defaultWeeks; // Input properties from the parent - @property() currentYear: number = this.currentDate.getFullYear(); - @property() currentMonth: number = this.currentDate.getMonth(); + @property() currentYear: number = this.#currentDate.getFullYear(); + @property() currentMonth: number = this.#currentDate.getMonth(); @property() currentWeek = 0; - @property() selectedDay: number = this.currentDate.getDate(); + @property() selectedDay: number = this.#currentDate.getDate(); @property() focusRef!: HTMLButtonElement; @property() dayToBeFocused!: number; @property() focusedDateValues!: FocusedDateValues; @@ -98,16 +98,16 @@ export class PfCalendar extends LitElement { new Date(this.currentYear, this.currentMonth, 0).getDate(); // Get the number of weeks in a month - this.weeks = this.#getNoOfWeeks(this.currentYear, this.currentMonth); + this.#weeks = this.#getNoOfWeeks(this.currentYear, this.currentMonth); let day = 1; let nextMonthPointer = 0; return html`
- ${this.weeks.map((week: number) => { + ${this.#weeks.map((week: number) => { return html`
- ${this.weekdays.map((weekDay: number) => { + ${this.#weekdays.map((weekDay: number) => { let calendarDayTemplate: unknown; if (week === 0 && weekDay < firstDay) { // Add the last dates of the previous month @@ -153,9 +153,9 @@ export class PfCalendar extends LitElement { && (value.year === this.focusedDateValues?.year)); const selectedDate: Date = new Date(this.dateSelected); - const isToday: boolean = ((value.day === this.currentDate.getDate()) - && (value.month === this.currentDate.getMonth()) - && (value.year === this.currentDate.getFullYear())); + const isToday: boolean = ((value.day === this.#currentDate.getDate()) + && (value.month === this.#currentDate.getMonth()) + && (value.year === this.#currentDate.getFullYear())); const isSelectedDay: boolean = ((value.day === selectedDate?.getDate()) && (value.month === selectedDate?.getMonth()) diff --git a/elements/pf-date-picker/pf-date-picker.ts b/elements/pf-date-picker/pf-date-picker.ts index a02d57826d..39a1ab2ced 100644 --- a/elements/pf-date-picker/pf-date-picker.ts +++ b/elements/pf-date-picker/pf-date-picker.ts @@ -53,8 +53,8 @@ export class DateChangeEvent extends Event { export class PfDatePicker extends LitElement { static readonly styles = [styles]; - private _currentDate: Date = new Date(); - private errorMessages: ErrorMessages = { + #currentDate = new Date(); + #errorMessages: ErrorMessages = { inValid: 'Invalid date', lessThanMinDate: 'Date is before the allowable range.', greaterThanMaxDate: 'Date is after the allowable range.', @@ -130,16 +130,16 @@ export class PfDatePicker extends LitElement { // 'current' refers to the temporary values of Month and Year the user selected before day is selected // and the input box is updated @property() monthNames: string[] = getMonthNamesFromLocale(this.translationLanguageCode); - @property() currentMonthSelection: string = this.monthNames[this._currentDate.getMonth()]; - @property() currentMonthIndex: number = this._currentDate.getMonth(); - @property() currentYear: number = this._currentDate.getFullYear(); + @property() currentMonthSelection: string = this.monthNames[this.#currentDate.getMonth()]; + @property() currentMonthIndex: number = this.#currentDate.getMonth(); + @property() currentYear: number = this.#currentDate.getFullYear(); // 'selected' refers to the active selected values of day, Month and Year the user selected // which is updated in the input box @property() selectedDay!: number | null; - @property() selectedMonthIndex: number = this._currentDate.getMonth(); - @property() selectedMonthSelection: string = this.monthNames[this._currentDate.getMonth()]; - @property() selectedYear: number = this._currentDate.getFullYear(); + @property() selectedMonthIndex: number = this.#currentDate.getMonth(); + @property() selectedMonthSelection: string = this.monthNames[this.#currentDate.getMonth()]; + @property() selectedYear: number = this.#currentDate.getFullYear(); @property() formattedDate = ''; // The value which is updated in the date-picker input box @property() errorMessage!: string; // Handle the error message on invalid date @property() dateSelected!: Date; // The date selected by the user @@ -150,8 +150,8 @@ export class PfDatePicker extends LitElement { getDatePatternFromLocale(this.localizationLanguageCode, this.dateFormatInput); // Date format @property() inputDate!: Date | null; // Handle and format date input value parent sends to the component - private minYear: number = new Date(this.minDate).getFullYear(); // Minimum Valid Year - private maxYear: number = new Date(this.maxDate).getFullYear(); // Maximum Valid Year + #minYear = new Date(this.minDate).getFullYear(); // Minimum Valid Year + #maxYear: number = new Date(this.maxDate).getFullYear(); // Maximum Valid Year constructor() { super(); @@ -349,11 +349,11 @@ export class PfDatePicker extends LitElement { // Function to clear date selected #clearDateSelection() { this.selectedDay = null; - this.currentMonthIndex = this._currentDate.getMonth(); + this.currentMonthIndex = this.#currentDate.getMonth(); this.currentMonthSelection = this.monthNames[this.currentMonthIndex]; - this.currentYear = this._currentDate.getFullYear(); - this.selectedMonthIndex = this._currentDate.getMonth(); - this.selectedYear = this._currentDate.getFullYear(); + this.currentYear = this.#currentDate.getFullYear(); + this.selectedMonthIndex = this.#currentDate.getMonth(); + this.selectedYear = this.#currentDate.getFullYear(); this.selectedMonthSelection = this.monthNames[this.selectedMonthIndex]; this.formattedDate = ''; } @@ -388,26 +388,26 @@ export class PfDatePicker extends LitElement { // Function to check if the year entered by the user is valid or not #validateYearInput(year: number) { - this.minYear = new Date(this.minDate).getFullYear(); - this.maxYear = new Date(this.maxDate).getFullYear(); + this.#minYear = new Date(this.minDate).getFullYear(); + this.#maxYear = new Date(this.maxDate).getFullYear(); const yearInput: number = year; - this.isValid = yearInput > this.minYear && yearInput < this.maxYear; + this.isValid = yearInput > this.#minYear && yearInput < this.#maxYear; if (this.isValid) { return yearInput; - } else if (yearInput < this.minYear) { + } else if (yearInput < this.#minYear) { let inputYear: number; const date: Date = new Date(year, this.currentMonthIndex, this.selectedDay ? - this.selectedDay : this._currentDate.getDate()); + this.selectedDay : this.#currentDate.getDate()); - if (date.getFullYear() >= this.minYear) { + if (date.getFullYear() >= this.#minYear) { inputYear = date.getFullYear(); } else { inputYear = year; } return inputYear; - } else if (yearInput > this.maxYear) { - return this.maxYear; + } else if (yearInput > this.#maxYear) { + return this.#maxYear; } else { return this.currentYear; } @@ -479,13 +479,13 @@ export class PfDatePicker extends LitElement { this.#getFormattedDate(); this.firstDayToBeFocused = this.selectedDay; } else { - this.firstDayToBeFocused = this._currentDate.getDate(); + this.firstDayToBeFocused = this.#currentDate.getDate(); } } else { - this.currentMonthIndex = this._currentDate.getMonth(); - this.currentYear = this._currentDate.getFullYear(); + this.currentMonthIndex = this.#currentDate.getMonth(); + this.currentYear = this.#currentDate.getFullYear(); this.currentMonthSelection = this.monthNames[this.currentMonthIndex]; - this.firstDayToBeFocused = this._currentDate.getDate(); + this.firstDayToBeFocused = this.#currentDate.getDate(); this.selectedDay = null; } @@ -501,7 +501,7 @@ export class PfDatePicker extends LitElement { if (this.isDateValid && this.selectedDay) { this.dayToBeFocused = this.selectedDay <= totalDays ? this.selectedDay : totalDays; } else { - this.dayToBeFocused = this._currentDate.getDate(); + this.dayToBeFocused = this.#currentDate.getDate(); } } @@ -532,11 +532,11 @@ export class PfDatePicker extends LitElement { this.#dispatchSelectedDate(event); } else if (this.isDateValid && value === '') { this.selectedDay = null; - this.currentMonthIndex = this._currentDate.getMonth(); + this.currentMonthIndex = this.#currentDate.getMonth(); this.currentMonthSelection = this.monthNames[this.currentMonthIndex]; - this.currentYear = this._currentDate.getFullYear(); - this.selectedMonthIndex = this._currentDate.getMonth(); - this.selectedYear = this._currentDate.getFullYear(); + this.currentYear = this.#currentDate.getFullYear(); + this.selectedMonthIndex = this.#currentDate.getMonth(); + this.selectedYear = this.#currentDate.getFullYear(); this.selectedMonthSelection = this.monthNames[this.selectedMonthIndex]; this.#dispatchSelectedDate(event); } @@ -564,29 +564,29 @@ export class PfDatePicker extends LitElement { ); if (date.toString() === 'Invalid Date') { - this.errorMessage = this.errorMessages.inValid; + this.errorMessage = this.#errorMessages.inValid; return false; } if (date < this.minDate) { - this.errorMessage = this.errorMessages.lessThanMinDate; + this.errorMessage = this.#errorMessages.lessThanMinDate; return false; } if (date > this.maxDate) { - this.errorMessage = this.errorMessages.greaterThanMaxDate; + this.errorMessage = this.#errorMessages.greaterThanMaxDate; return false; } // if (!dateString.match(regex)) { // Commented for reference if (!regex.test(dateString)) { isValid = false; - this.errorMessage = this.errorMessages.inValid; + this.errorMessage = this.#errorMessages.inValid; return isValid; } // Check the ranges of month and year - if (selectedYearInput < this.minYear || selectedYearInput > this.maxYear + if (selectedYearInput < this.#minYear || selectedYearInput > this.#maxYear || selectedMonthInput === 0 || selectedMonthInput > 12) { - this.errorMessage = this.errorMessages.inValid; + this.errorMessage = this.#errorMessages.inValid; return false; } @@ -599,7 +599,7 @@ export class PfDatePicker extends LitElement { // Check the range of the day isValid = selectedDayInput > 0 && selectedDayInput <= monthLength[selectedMonthInput - 1]; if (!isValid) { - this.errorMessage = this.errorMessages.inValid; + this.errorMessage = this.#errorMessages.inValid; } } else if (dateString === '') { isValid = true; @@ -611,7 +611,7 @@ export class PfDatePicker extends LitElement { // Function to get the formatted date string according to date format selected in parent component #getFormattedDate() { const monthSelected: number = this.currentMonthIndex + 1; // Months start at 0! - const daySelected: number = this.selectedDay ? this.selectedDay : this._currentDate.getDate(); + const daySelected: number = this.selectedDay ? this.selectedDay : this.#currentDate.getDate(); const yearSelected: number = this.selectedYear; let dd: string = daySelected.toString(); let mm: string = monthSelected.toString(); diff --git a/elements/pf-date-picker/pf-year-input.ts b/elements/pf-date-picker/pf-year-input.ts index f6a3553808..61c66e9fb8 100644 --- a/elements/pf-date-picker/pf-year-input.ts +++ b/elements/pf-date-picker/pf-year-input.ts @@ -19,8 +19,8 @@ export class PfYearInput extends LitElement { @property() minDate!: Date; @property() maxDate!: Date; - private minYear: number = new Date(this.minDate).getFullYear(); - private maxYear: number = new Date(this.maxDate).getFullYear(); + #minYear = new Date(this.minDate).getFullYear(); + #maxYear = new Date(this.maxDate).getFullYear(); @query('#year-input') _numberInput!: HTMLInputElement; @@ -31,8 +31,8 @@ export class PfYearInput extends LitElement { type="number" id="year-input" aria-label="Select year" - min=${this.minYear} - max=${this.maxYear} + min=${this.#minYear} + max=${this.#maxYear} @input=${this.#OnInput} @click=${this.#onChange} @keyup=${this.#onKeyUp} @@ -65,7 +65,7 @@ export class PfYearInput extends LitElement { const options = { detail: this.currentYear, bubbles: true, - composed: true + composed: true, }; this.dispatchEvent(new CustomEvent(eventName, options)); }