Skip to content

Commit 799c746

Browse files
committed
feature(Timepicker) implemented modal display plugin materializecss#557
1 parent 7a17d57 commit 799c746

File tree

2 files changed

+105
-11
lines changed

2 files changed

+105
-11
lines changed

src/plugin/modalDisplayPlugin.ts

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
export interface ModalDisplayPluginOptions {
2+
classList: string[],
3+
title: HTMLElement|null
4+
}
5+
6+
const _defaults: ModalDisplayPluginOptions = {
7+
classList: ['modal'],
8+
title: null
9+
}
10+
11+
export class ModalDisplayPlugin {
12+
private readonly el: HTMLElement;
13+
private readonly container: HTMLDialogElement;
14+
private options: Partial<ModalDisplayPluginOptions>;
15+
private visible: boolean;
16+
footer: HTMLElement;
17+
18+
constructor(el: HTMLElement, container: HTMLElement, options: Partial<ModalDisplayPluginOptions>) {
19+
this.el = el;
20+
this.options = {
21+
..._defaults,
22+
...options,
23+
};
24+
25+
this.container = document.createElement('dialog');
26+
this.container.classList.add('modal', 'display-modal', this.options.classList.join(' '));
27+
28+
if(options.title) {
29+
const modalHeader = document.createElement('div');
30+
modalHeader.classList.add('modal-header');
31+
modalHeader.append(options.title);
32+
this.container.append(modalHeader);
33+
}
34+
35+
const modalContent = document.createElement('div');
36+
modalContent.classList.add('modal-content');
37+
modalContent.append(container);
38+
this.container.append(modalContent);
39+
40+
this.footer = document.createElement('div');
41+
this.footer.classList.add('modal-footer');
42+
this.container.append(this.footer);
43+
44+
document.body.append(this.container);
45+
46+
document.addEventListener('click', (e) => {
47+
if (this.visible && !(this.el === <HTMLElement>e.target) && !((<HTMLElement>e.target).closest('.display-modal'))) {
48+
this.hide();
49+
}
50+
});
51+
}
52+
53+
/**
54+
* Initializes instance of ModalDisplayPlugin
55+
* @param el HTMLElement to position to
56+
* @param container HTMLElement to be positioned
57+
* @param options Plugin options
58+
*/
59+
static init(el: HTMLElement, container: HTMLElement, options?: Partial<ModalDisplayPluginOptions>): ModalDisplayPlugin {
60+
return new ModalDisplayPlugin(el, container, options);
61+
}
62+
63+
show = () => {
64+
if (this.visible) return;
65+
this.visible = true;
66+
this.container.setAttribute('open', 'true');
67+
};
68+
69+
hide = () => {
70+
if (!this.visible) return;
71+
this.visible = false;
72+
this.container.removeAttribute('open');
73+
};
74+
}

src/timepicker.ts

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Utils } from './utils';
22
import { Component, BaseOptions, InitElements, MElement, I18nOptions } from './component';
33
import { DockedDisplayPlugin } from './plugin/dockedDisplayPlugin';
4+
import { ModalDisplayPlugin } from './plugin/modalDisplayPlugin';
45

56
export type Views = 'hours' | 'minutes';
67

@@ -45,6 +46,7 @@ export interface TimepickerOptions extends BaseOptions {
4546
* Autosubmit timepicker selection to input field
4647
* @default true
4748
*/
49+
// @todo this is only working on analog clock, should apply to hour/minute input fields and am/pm selector as well
4850
autoSubmit: true;
4951
/**
5052
* Default time to set on the timepicker 'now' or '13:14'.
@@ -176,7 +178,7 @@ export class Timepicker extends Component<TimepickerOptions> {
176178
g: Element;
177179
toggleViewTimer: string | number | NodeJS.Timeout;
178180
vibrateTimer: NodeJS.Timeout | number;
179-
private displayPlugin: DockedDisplayPlugin;
181+
private displayPlugin: DockedDisplayPlugin | ModalDisplayPlugin;
180182

181183
constructor(el: HTMLInputElement, options: Partial<TimepickerOptions>) {
182184
super(el, options, Timepicker);
@@ -190,11 +192,10 @@ export class Timepicker extends Component<TimepickerOptions> {
190192
this._setupVariables();
191193
this._setupEventHandlers();
192194
this._clockSetup();
193-
this._pickerSetup();
194-
195195
if (this.options.displayPlugin) {
196-
if (this.options.displayPlugin === 'docked') this.displayPlugin = DockedDisplayPlugin.init(this.el, this.containerEl, this.options.displayPluginOptions);
196+
this._setupDisplayPlugin();
197197
}
198+
this._pickerSetup();
198199
}
199200

200201
static get defaults(): TimepickerOptions {
@@ -260,10 +261,12 @@ export class Timepicker extends Component<TimepickerOptions> {
260261

261262
_setupEventHandlers() {
262263
this.el.addEventListener('click', this._handleInputClick);
264+
// @todo allow input field to fill values from input field when container/modal opens
263265
this.el.addEventListener('keydown', this._handleInputKeydown);
264266
this.plate.addEventListener('mousedown', this._handleClockClickStart);
265267
this.plate.addEventListener('touchstart', this._handleClockClickStart);
266268
this.digitalClock.addEventListener('keyup', this._inputFromTextField);
269+
267270
this.inputHours.addEventListener('focus', () => this.showView('hours'));
268271
this.inputHours.addEventListener('focusout', () => this.formatHours());
269272
this.inputMinutes.addEventListener('focus', () => this.showView('minutes'));
@@ -408,13 +411,15 @@ export class Timepicker extends Component<TimepickerOptions> {
408411
// clearButton.classList.add('timepicker-clear');
409412
// clearButton.addEventListener('click', this.clear);
410413
// this.footer.appendChild(clearButton);
411-
Utils.createButton(
412-
this.footer,
413-
this.options.i18n.clear,
414-
['timepicker-clear'],
415-
this.options.showClearBtn,
416-
this.clear
417-
);
414+
if (this.options.showClearBtn) {
415+
Utils.createButton(
416+
this.footer,
417+
this.options.i18n.clear,
418+
['timepicker-clear'],
419+
true,
420+
this.clear
421+
);
422+
}
418423

419424
if (!this.options.autoSubmit) {
420425
/*const confirmationBtnsContainer = document.createElement('div');
@@ -443,6 +448,18 @@ export class Timepicker extends Component<TimepickerOptions> {
443448
this.showView('hours');
444449
}
445450

451+
private _setupDisplayPlugin() {
452+
if (this.options.displayPlugin === 'docked') this.displayPlugin = DockedDisplayPlugin.init(this.el, this.containerEl, this.options.displayPluginOptions);
453+
if (this.options.displayPlugin === 'modal') {
454+
this.displayPlugin = ModalDisplayPlugin.init(this.el, this.containerEl, {
455+
...this.options.displayPluginOptions,
456+
...{ classList: ['timepicker-modal'] }
457+
});
458+
this.footer.remove();
459+
this.footer = this.displayPlugin.footer;
460+
}
461+
}
462+
446463
_clockSetup() {
447464
if (this.options.twelveHour) {
448465
// AM Button
@@ -820,16 +837,19 @@ export class Timepicker extends Component<TimepickerOptions> {
820837

821838
confirm = () => {
822839
this.done();
840+
if (this.displayPlugin) this.displayPlugin.hide();
823841
if (typeof this.options.onDone === 'function') this.options.onDone.call(this);
824842
}
825843

826844
cancel = () => {
827845
// not logical clearing the input field on cancel, since the end user might want to make use of the previously submitted value
828846
// this.clear();
847+
if (this.displayPlugin) this.displayPlugin.hide();
829848
if (typeof this.options.onCancel === 'function') this.options.onCancel.call(this);
830849
}
831850

832851
clear = () => {
852+
// @todo should clear timepicker hour/minute input elems and reset analog clock, currently clears input el
833853
this.done(true);
834854
};
835855

0 commit comments

Comments
 (0)