diff --git a/sass/components/_timepicker.scss b/sass/components/_timepicker.scss index adb2b4cc0c..44516e409a 100644 --- a/sass/components/_timepicker.scss +++ b/sass/components/_timepicker.scss @@ -1,37 +1,40 @@ /* Timepicker Containers */ -.timepicker-modal { +/* modal removed as of v2.2.1 */ +/* .timepicker-modal { max-width: 325px; max-height: none; -} +}*/ -.timepicker-container.modal-content { +.timepicker-container { display: flex; flex-direction: column; + max-width: 325px; padding: 0; + background-color: var(--md-sys-color-inverse-on-surface); } .text-primary { color: var(--md-sys-color-on-primary); } - /* Clock Digital Display */ .timepicker-digital-display { width: auto; flex: 1 auto; - background-color: var(--md-sys-color-primary);; + // background-color: var(--md-sys-color-surface); padding: 2rem .67rem .67rem .67rem; font-weight: 300; } .timepicker-text-container { + display: flex; font-size: 4rem; text-align: left; color: var(--font-on-primary-color-medium); font-weight: 400; - position: relative; + /*position: relative;*/ user-select: none; - padding: 1rem 1rem 1.5rem 1rem; + padding: 1rem 1rem 1.3rem 1rem; input[type=text]{ height: 4rem; @@ -46,10 +49,15 @@ display: inline-flex; } +.timepicker-display-digital-clock { + flex-grow: 1; + display: inline-flex; +} + .timepicker-input-hours-wrapper, .timepicker-input-minutes-wrapper { width: 6.9rem; - height: 5.2rem; + height: 5.75rem; } .timepicker-input-hours, @@ -61,9 +69,15 @@ input[type=text].timepicker-input-hours, input[type=text].timepicker-input-minutes { height: 100%; - padding: .8rem; + padding: 1.33rem .8rem; border: 0; text-align: center; + color: var(--md-sys-color-on-background); + background-color: var(--md-sys-color-surface-variant); + + &:focus { + background-color: var(--md-sys-color-primary-container); + } } .timepicker-input-divider-wrapper { @@ -71,42 +85,42 @@ input[type=text].timepicker-input-minutes { text-align: center; } -input[type=text].text-primary { +/*input[type=text].text-primary { color: var(--md-sys-color-on-background); -} +}*/ .timepicker-display-am-pm { font-size: 1.3rem; - position: absolute; + /*position: absolute; top: 1rem; - right: 1rem; + right: 1rem;*/ font-weight: 400; } .timepicker-span-am-pm { - height: 5.2rem; + height: 5.75rem; max-width: 3.5rem; } -.timepicker-modal .am-btn, -.timepicker-modal .pm-btn { +.timepicker-container .am-btn, +.timepicker-container .pm-btn { width: 3.6rem; height: 50%; - padding-left: calc(var(--btn-padding) / 1.6); - padding-right: calc(var(--btn-padding) / 1.6); + padding-left: calc(var(--btn-padding) / 1.8); + padding-right: calc(var(--btn-padding) / 1.8); line-height: 2rem; vertical-align: middle; text-align: center; - background-color: transparent; + // background-color: transparent; border: 1px solid var(--md-sys-color-outline); } -.timepicker-modal .am-btn { +.timepicker-container .am-btn { border-bottom-right-radius: 0; border-bottom-left-radius: 0; } -.timepicker-modal .pm-btn { +.timepicker-container .pm-btn { border-top: 0; border-top-left-radius: 0; border-top-right-radius: 0; @@ -116,17 +130,17 @@ input[type=text].text-primary { .timepicker-analog-display { flex: 2.5 auto; padding: .67rem; - background-color: var(--md-sys-color-surface); + // background-color: var(--md-sys-color-surface); } .timepicker-plate { - background-color: rgba(0, 0, 0, 0.09); + background-color: var(--md-sys-color-surface-variant); border-radius: 50%; width: 260px; height: 260px; overflow: visible; position: relative; - margin: 2rem 1.6rem 1.6rem 1.6rem; + margin: 0 1.6rem 1.6rem 1.6rem; user-select: none; } @@ -224,12 +238,12 @@ input[type=text].text-primary { /* Media Queries */ @media #{$large-and-up} { - .timepicker-modal { + .timepicker-container { width: auto; max-width: 620px; } - .timepicker-container.modal-content { + .timepicker-container { flex-direction: row; } @@ -238,42 +252,55 @@ input[type=text].text-primary { } .timepicker-text-container { - top: 31%; + /*top: 31%;*/ + flex-direction: column; + margin-top: 4.8rem; text-align: center; } + .timepicker-display-column { + padding: 0 3%; + } + .timepicker-display-am-pm { - position: relative; + /*position: relative; top: auto; right: auto; - text-align: center; - margin-top: 1.2rem; + text-align: center;*/ + margin-top: 1.1rem; } .timepicker-span-am-pm { + display: flex; + flex-grow: 1; max-width: unset; } - .timepicker-modal .am-btn, - .timepicker-modal .pm-btn { - width: auto; - padding-left: var(--btn-padding); - padding-right: var(--btn-padding); + .timepicker-container .am-btn, + .timepicker-container .pm-btn { + flex-grow: 1; + /*width: auto;*/ + padding-left: calc(var(--btn-padding) / .565); + padding-right: calc(var(--btn-padding) / .565); border-radius: var(--btn-border-radius); border: 1px solid var(--md-sys-color-outline); - line-height: inherit; + /*line-height: inherit; vertical-align: top; - text-align: inherit; + text-align: inherit;*/ } - .timepicker-modal .am-btn { + .timepicker-container .am-btn { border-top-right-radius: 0; border-bottom-right-radius: 0; } - .timepicker-modal .pm-btn { + .timepicker-container .pm-btn { border-left: 0; border-bottom-left-radius: 0; border-top-left-radius: 0; } + + .timepicker-plate { + margin-top: 1.6rem; + } } diff --git a/src/timepicker.ts b/src/timepicker.ts index c55d6066bd..e96dd2c7c8 100644 --- a/src/timepicker.ts +++ b/src/timepicker.ts @@ -40,6 +40,11 @@ export interface TimepickerOptions extends BaseOptions { * @default false */ showClearBtn: boolean; + /** + * Autosubmit timepicker selection to input field + * @default true + */ + autoSubmit: true; /** * Default time to set on the timepicker 'now' or '13:14'. * @default 'now'; @@ -69,6 +74,21 @@ export interface TimepickerOptions extends BaseOptions { * @default null */ onSelect: (hour: number, minute: number) => void; + /** + * Callback function for interaction with input field. + * @default null + */ + onInputInteraction: (() => void) | null; + /** + * Callback function for done. + * @default null + */ + onDone: (() => void) | null; + /** + * Callback function for cancel. + * @default null + */ + onCancel: (() => void) | null; } const _defaults: TimepickerOptions = { @@ -81,6 +101,7 @@ const _defaults: TimepickerOptions = { defaultTime: 'now', // default time, 'now' or '13:14' e.g. fromNow: 0, // Millisecond offset from the defaultTime showClearBtn: false, + autoSubmit: true, // internationalization i18n: { cancel: 'Cancel', @@ -90,7 +111,10 @@ const _defaults: TimepickerOptions = { twelveHour: true, // change to 12 hour AM/PM clock from 24 hour vibrate: true, // vibrate the device when dragging clock hand // Callbacks - onSelect: null + onSelect: null, + onInputInteraction: null, + onDone: null, + onCancel: null, }; type Point = { @@ -236,13 +260,15 @@ export class Timepicker extends Component { } _handleInputClick = () => { - this.open(); + this.inputHours.focus(); + if (typeof this.options.onInputInteraction === 'function') this.options.onInputInteraction.call(this); }; _handleInputKeydown = (e: KeyboardEvent) => { if (Utils.keys.ENTER.includes(e.key)) { e.preventDefault(); - this.open(); + this.inputHours.focus(); + if (typeof this.options.onInputInteraction === 'function') this.options.onInputInteraction.call(this); } }; @@ -296,11 +322,12 @@ export class Timepicker extends Component { } if (this.currentView === 'hours') { + this.inputMinutes.focus(); this.showView('minutes', this.options.duration / 2); } else { - this.minutesView.classList.add('timepicker-dial-out'); + // this.minutesView.classList.add('timepicker-dial-out'); setTimeout(() => { - this.done(); + if (this.options.autoSubmit) this.done(); }, this.options.duration / 2); } @@ -349,26 +376,30 @@ export class Timepicker extends Component { this.amOrPm = 'PM'; } - private _createButton(text: string, visibility: string): HTMLButtonElement { + /*private _createButton(text: string, visibility: string): HTMLButtonElement { const button = document.createElement('button'); - button.classList.add('btn', 'btn-flat', 'waves-effect', 'text'); + button.classList.add('btn', 'waves-effect', 'text'); button.style.visibility = visibility; button.type = 'button'; button.tabIndex = -1; button.innerText = text; return button; - } + }*/ _pickerSetup() { - const clearButton = this._createButton( + // clearButton.classList.add('timepicker-clear'); + // clearButton.addEventListener('click', this.clear); + // this.footer.appendChild(clearButton); + Utils.createButton( + this.footer, this.options.i18n.clear, - this.options.showClearBtn ? '' : 'hidden' + ['timepicker-clear'], + this.options.showClearBtn, + this.clear ); - clearButton.classList.add('timepicker-clear'); - clearButton.addEventListener('click', this.clear); - this.footer.appendChild(clearButton); - const confirmationBtnsContainer = document.createElement('div'); + if (!this.options.autoSubmit) { + /*const confirmationBtnsContainer = document.createElement('div'); confirmationBtnsContainer.classList.add('confirmation-btns'); this.footer.append(confirmationBtnsContainer); @@ -380,7 +411,18 @@ export class Timepicker extends Component { const doneButton = this._createButton(this.options.i18n.done, ''); doneButton.classList.add('timepicker-close'); //doneButton.addEventListener('click', this._finishSelection); - confirmationBtnsContainer.appendChild(doneButton); + confirmationBtnsContainer.appendChild(doneButton);*/ + Utils.createConfirmationContainer( + this.footer, + this.options.i18n.done, + this.options.i18n.cancel, + this.confirm, + this.cancel + ); + } + + this._updateTimeFromInput(); + this.showView('hours'); } _clockSetup() { @@ -389,13 +431,17 @@ export class Timepicker extends Component { this._amBtn = document.createElement('div'); this._amBtn.classList.add('am-btn', 'btn'); this._amBtn.innerText = 'AM'; + this._amBtn.tabIndex = 0; this._amBtn.addEventListener('click', this._handleAmPmClick); + this._amBtn.addEventListener('keypress', this._handleAmPmKeypress); this.spanAmPm.appendChild(this._amBtn); // PM Button this._pmBtn = document.createElement('div'); this._pmBtn.classList.add('pm-btn', 'btn'); this._pmBtn.innerText = 'PM'; + this._pmBtn.tabIndex = 0; this._pmBtn.addEventListener('click', this._handleAmPmClick); + this._pmBtn.addEventListener('keypress', this._handleAmPmKeypress); this.spanAmPm.appendChild(this._pmBtn); } this._buildHoursView(); @@ -491,12 +537,21 @@ export class Timepicker extends Component { } } - _handleAmPmClick = (e) => { - const btnClicked = e.target; - this.amOrPm = btnClicked.classList.contains('am-btn') ? 'AM' : 'PM'; - this._updateAmPmView(); + _handleAmPmClick = (e: MouseEvent) => { + this._handleAmPmInteraction(e.target); }; + _handleAmPmKeypress = (e: KeyboardEvent) => { + if (Utils.keys.ENTER.includes(e.key)) { + this._handleAmPmInteraction(e.target); + } + }; + + _handleAmPmInteraction = (e: HTMLElement) => { + this.amOrPm = e.classList.contains('am-btn') ? 'AM' : 'PM'; + this._updateAmPmView(); + } + _updateAmPmView() { if (this.options.twelveHour) { if (this.amOrPm === 'PM') { @@ -549,13 +604,13 @@ export class Timepicker extends Component { hideView = isHours ? this.minutesView : this.hoursView; this.currentView = view; - if (isHours) { + /*if (isHours) { this.inputHours.classList.add('text-primary'); this.inputMinutes.classList.remove('text-primary'); } else { this.inputHours.classList.remove('text-primary'); this.inputMinutes.classList.add('text-primary'); - } + }*/ // Transition view hideView.classList.add('timepicker-dial-out'); @@ -726,8 +781,7 @@ export class Timepicker extends Component { this.inputHours.value = (this.hours % (this.options.twelveHour ? 12 : 24)).toString(); } - // todo: remove e - done = (e = null, clearValue = null) => { + done = (clearValue = null) => { // Set input value const last = this.el.value; let value = clearValue @@ -744,18 +798,24 @@ export class Timepicker extends Component { new Event('change', { bubbles: true, cancelable: true, composed: true }) ); } - //this.el.focus(); - return e; // just for passing linter, can be removed }; + confirm = () => { + this.done(); + if (typeof this.options.onDone === 'function') this.options.onDone.call(this); + } + + cancel = () => { + this.clear(); + if (typeof this.options.onCancel === 'function') this.options.onCancel.call(this); + } + clear = () => { - this.done(null, true); + this.done(true); }; // deprecated open() { - // this._updateTimeFromInput(); - // this.showView('hours'); console.warn( 'Timepicker.close() is deprecated. Remove this method and wrap in modal yourself.' ); @@ -770,14 +830,12 @@ export class Timepicker extends Component { } static { - Timepicker._template = ` -