Skip to content

Commit 196bd69

Browse files
authored
Merge pull request #520 from gselderslaghs/timepicker-accessibility
Timepicker accessibility #476
2 parents b9c12ed + 2abdbe8 commit 196bd69

File tree

2 files changed

+87
-37
lines changed

2 files changed

+87
-37
lines changed

sass/components/_timepicker.scss

+33-8
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
/* Clock Digital Display */
1919
.timepicker-digital-display {
20-
width: 200px;
20+
width: auto;
2121
flex: 1 auto;
2222
background-color: var(--md-sys-color-primary);;
2323
padding: 10px;
@@ -27,15 +27,16 @@
2727
.timepicker-text-container {
2828
font-size: 4rem;
2929
font-weight: bold;
30-
text-align: center;
30+
text-align: left;
3131
color: var(--font-on-primary-color-medium);
3232
font-weight: 400;
3333
position: relative;
3434
user-select: none;
35+
padding: 1rem 1rem 1.5rem 1rem;
3536

3637
input[type=text]{
3738
height: 4rem;
38-
color: rgba(255, 255, 255, 0.6);
39+
color: var(--md-sys-color-secondary);
3940
border-bottom: 0px;
4041
font-size: 4rem;
4142
direction: ltr;
@@ -60,17 +61,26 @@ input[type=text].timepicker-input-minutes {
6061
}
6162

6263
input[type=text].text-primary {
63-
color: rgba(255, 255, 255, 1);
64+
color: var(--md-sys-color-on-background);
6465
}
6566

6667
.timepicker-display-am-pm {
6768
font-size: 1.3rem;
6869
position: absolute;
70+
top: 1.2rem;
6971
right: 1rem;
70-
bottom: 1rem;
72+
bottom: auto;
7173
font-weight: 400;
7274
}
7375

76+
.timepicker-modal .am-btn,
77+
.timepicker-modal .pm-btn {
78+
width: 3.6rem;
79+
height: 2rem;
80+
line-height: 2rem;
81+
vertical-align: middle;
82+
text-align: center;
83+
}
7484

7585
/* Analog Clock Display */
7686
.timepicker-analog-display {
@@ -85,9 +95,8 @@ input[type=text].text-primary {
8595
height: 270px;
8696
overflow: visible;
8797
position: relative;
88-
margin: auto;
89-
margin-top: 25px;
90-
margin-bottom: 5px;
98+
margin: 30px 14px 14px;
99+
padding: 10px;
91100
user-select: none;
92101
}
93102

@@ -195,13 +204,29 @@ input[type=text].text-primary {
195204

196205
.timepicker-text-container {
197206
top: 32%;
207+
padding: 0;
208+
text-align: center;
198209
}
199210

200211
.timepicker-display-am-pm {
201212
position: relative;
213+
top: auto;
202214
right: auto;
203215
bottom: auto;
204216
text-align: center;
205217
margin-top: 1.2rem;
206218
}
219+
220+
input[type=text].timepicker-input-minutes {
221+
min-width: 5.3rem;
222+
}
223+
224+
.timepicker-modal .am-btn,
225+
.timepicker-modal .pm-btn {
226+
width: auto;
227+
height: auto;
228+
line-height: inherit;
229+
vertical-align: top;
230+
text-align: inherit;
231+
}
207232
}

src/timepicker.ts

+54-29
Original file line numberDiff line numberDiff line change
@@ -254,8 +254,10 @@ export class Timepicker extends Component<TimepickerOptions> {
254254
this.plate.addEventListener('mousedown', this._handleClockClickStart);
255255
this.plate.addEventListener('touchstart', this._handleClockClickStart);
256256
this.digitalClock.addEventListener('keyup', this._inputFromTextField);
257-
this.inputHours.addEventListener('click', () => this.showView('hours'));
258-
this.inputMinutes.addEventListener('click', () => this.showView('minutes'));
257+
this.inputHours.addEventListener('focus', () => this.showView('hours'));
258+
this.inputHours.addEventListener('focusout', () => this.formatHours());
259+
this.inputMinutes.addEventListener('focus', () => this.showView('minutes'));
260+
this.inputMinutes.addEventListener('focusout', () => this.formatMinutes());
259261
}
260262

261263
_removeEventHandlers() {
@@ -359,9 +361,23 @@ export class Timepicker extends Component<TimepickerOptions> {
359361

360362
_setupModal() {
361363
this.modal = Modal.init(this.modalEl, {
362-
onOpenStart: this.options.onOpenStart,
364+
onOpenStart: () => {
365+
if (typeof this.options.onOpenStart === 'function') {
366+
this.options.onOpenStart.call(this);
367+
}
368+
this.modalEl.querySelectorAll('.btn').forEach((e: HTMLButtonElement) => {
369+
if (e.style.visibility !== 'hidden') e.tabIndex = 0;
370+
});
371+
},
363372
onOpenEnd: this.options.onOpenEnd,
364-
onCloseStart: this.options.onCloseStart,
373+
onCloseStart: () => {
374+
if (typeof this.options.onCloseStart === 'function') {
375+
this.options.onCloseStart.call(this);
376+
}
377+
this.modalEl.querySelectorAll('.btn').forEach((e: HTMLButtonElement) => {
378+
e.tabIndex = -1;
379+
});
380+
},
365381
onCloseEnd: () => {
366382
if (typeof this.options.onCloseEnd === 'function') {
367383
this.options.onCloseEnd.call(this);
@@ -392,10 +408,10 @@ export class Timepicker extends Component<TimepickerOptions> {
392408

393409
private _createButton(text: string, visibility: string): HTMLButtonElement {
394410
const button = document.createElement('button');
395-
button.classList.add('btn-flat', 'waves-effect');
411+
button.classList.add('btn', 'btn-flat', 'waves-effect', 'text');
396412
button.style.visibility = visibility;
397413
button.type = 'button';
398-
button.tabIndex = this.options.twelveHour ? 3 : 1;
414+
button.tabIndex = -1;
399415
button.innerText = text;
400416
return button;
401417
}
@@ -566,7 +582,7 @@ export class Timepicker extends Component<TimepickerOptions> {
566582
}
567583
this.hours = +value[0] || 0;
568584
this.minutes = +value[1] || 0;
569-
this.inputHours.value = this.hours;
585+
this.inputHours.value = Timepicker._addLeadingZero(this.hours);
570586
this.inputMinutes.value = Timepicker._addLeadingZero(this.minutes);
571587

572588
this._updateAmPmView();
@@ -635,31 +651,26 @@ export class Timepicker extends Component<TimepickerOptions> {
635651

636652
_inputFromTextField = () => {
637653
const isHours = this.currentView === 'hours';
638-
if (isHours) {
654+
if (isHours && this.inputHours.value !== '') {
639655
const value = parseInt(this.inputHours.value);
640-
if (value > 0 && value < 13) {
641-
this.drawClockFromTimeInput(value, isHours);
642-
this.showView('minutes', this.options.duration / 2);
656+
if (value > 0 && value < (this.options.twelveHour ? 13 : 24)) {
643657
this.hours = value;
644-
this.inputMinutes.focus();
645658
}
646659
else {
647-
const hour = new Date().getHours();
648-
this.inputHours.value = (hour % 12).toString();
660+
this.setHoursDefault();
649661
}
662+
this.drawClockFromTimeInput(this.hours, isHours);
650663
}
651-
else {
664+
else if(!isHours && this.inputMinutes.value !== '') {
652665
const value = parseInt(this.inputMinutes.value);
653666
if (value >= 0 && value < 60) {
654-
this.inputMinutes.value = Timepicker._addLeadingZero(value);
655-
this.drawClockFromTimeInput(value, isHours);
656667
this.minutes = value;
657-
(<HTMLElement>this.modalEl.querySelector('.confirmation-btns :nth-child(2)')).focus();
658668
}
659669
else {
660-
const minutes = new Date().getMinutes();
661-
this.inputMinutes.value = Timepicker._addLeadingZero(minutes);
670+
this.minutes = new Date().getMinutes();
671+
this.inputMinutes.value = this.minutes;
662672
}
673+
this.drawClockFromTimeInput(this.minutes, isHours);
663674
}
664675
}
665676

@@ -669,15 +680,10 @@ export class Timepicker extends Component<TimepickerOptions> {
669680
let radius;
670681
if (this.options.twelveHour) {
671682
radius = this.options.outerRadius;
683+
} else {
684+
radius = isHours && value > 0 && value < 13 ? this.options.innerRadius : this.options.outerRadius;
672685
}
673-
let cx1 = Math.sin(radian) * (radius - this.options.tickRadius),
674-
cy1 = -Math.cos(radian) * (radius - this.options.tickRadius),
675-
cx2 = Math.sin(radian) * radius,
676-
cy2 = -Math.cos(radian) * radius;
677-
this.hand.setAttribute('x2', cx1.toString());
678-
this.hand.setAttribute('y2', cy1.toString());
679-
this.bg.setAttribute('cx', cx2.toString());
680-
this.bg.setAttribute('cy', cy2.toString());
686+
this.setClockAttributes(radian, radius);
681687
}
682688

683689
setHand(x, y, roundBy5: boolean = false) {
@@ -742,13 +748,17 @@ export class Timepicker extends Component<TimepickerOptions> {
742748

743749
this[this.currentView] = value;
744750
if (isHours) {
745-
this.inputHours.value = value.toString();
751+
this.inputHours.value = Timepicker._addLeadingZero(value);
746752
}
747753
else {
748754
this.inputMinutes.value = Timepicker._addLeadingZero(value);
749755
}
750756

751757
// Set clock hand and others' position
758+
this.setClockAttributes(radian, radius);
759+
}
760+
761+
setClockAttributes(radian: number, radius: number) {
752762
let cx1 = Math.sin(radian) * (radius - this.options.tickRadius),
753763
cy1 = -Math.cos(radian) * (radius - this.options.tickRadius),
754764
cx2 = Math.sin(radian) * radius,
@@ -759,6 +769,21 @@ export class Timepicker extends Component<TimepickerOptions> {
759769
this.bg.setAttribute('cy', cy2.toString());
760770
}
761771

772+
formatHours() {
773+
if (this.inputHours.value == '') this.setHoursDefault();
774+
this.inputHours.value = Timepicker._addLeadingZero(Number(this.inputHours.value));
775+
}
776+
777+
formatMinutes() {
778+
if (this.inputMinutes.value == '') this.minutes = new Date().getMinutes();
779+
this.inputMinutes.value = Timepicker._addLeadingZero(Number(this.inputMinutes.value));
780+
}
781+
782+
setHoursDefault() {
783+
this.hours = new Date().getHours();
784+
this.inputHours.value = (this.hours % (this.options.twelveHour ? 12 : 24)).toString();
785+
}
786+
762787
/**
763788
* Open timepicker.
764789
*/

0 commit comments

Comments
 (0)