Skip to content

Commit

Permalink
refactor: add keyboard support in year grid
Browse files Browse the repository at this point in the history
  • Loading branch information
motss committed May 23, 2021
1 parent 08fc85c commit c8ec5a9
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 19 deletions.
11 changes: 11 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,14 @@ export const calendarKeyCodeSet = new Set([
keyCodesRecord.ENTER,
keyCodesRecord.SPACE,
]);

export const yearGridKeyCodeSet = new Set([
keyCodesRecord.ARROW_DOWN,
keyCodesRecord.ARROW_LEFT,
keyCodesRecord.ARROW_RIGHT,
keyCodesRecord.ARROW_UP,
keyCodesRecord.END,
keyCodesRecord.ENTER,
keyCodesRecord.HOME,
keyCodesRecord.SPACE,
]);
File renamed without changes.
2 changes: 1 addition & 1 deletion src/month-calendar/month-calendar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { classMap } from 'lit/directives/class-map.js';
import { resetShadowRoot } from '../ stylings.js';
import type { navigationKeyCodeSet } from '../constants.js';
import { calendarKeyCodeSet, keyCodesRecord } from '../constants.js';
import { computeNextSelectedDate } from '../helpers/compute-next-focused-date.js';
import { computeNextSelectedDate } from '../helpers/compute-next-selected-date.js';
import { dispatchCustomEvent } from '../helpers/dispatch-custom-event.js';
import { isInTargetMonth } from '../helpers/is-in-current-month.js';
import { toClosestTarget } from '../helpers/to-closest-target.js';
Expand Down
29 changes: 29 additions & 0 deletions src/year-grid/ to-next-selected-year.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { keyCodesRecord } from '../constants.js';
import type { ToNextSelectableYearInit } from './typings.js';

const {
ARROW_DOWN,
ARROW_LEFT,
ARROW_RIGHT,
ARROW_UP,
END,
HOME,
} = keyCodesRecord;

export function toNextSelectedYear({
keyCode,
max,
min,
year,
}: ToNextSelectableYearInit): number {
switch (keyCode) {
case ARROW_UP: return year - 4;
case ARROW_DOWN: return year + 4;
case ARROW_LEFT: return year - 1;
case ARROW_RIGHT: return year + 1;
case END: return max.getUTCFullYear();
case HOME: return min.getUTCFullYear();
default:
return year;
}
}
3 changes: 2 additions & 1 deletion src/year-grid/stylings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export const yearGridStyling = css`
grid-auto-flow: row;
grid-template-columns: repeat(4, minmax(1px, 56px));
grid-template-rows: repeat(auto-fit, 32px);
place-items: center;
align-items: center;
justify-items: center;
}
`;
11 changes: 10 additions & 1 deletion src/year-grid/typings.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
import type { Formatters } from '../typings.js';
import type { ChangedProperties, Formatters, SupportedKeyCode } from '../typings.js';

export interface ToNextSelectableYearInit {
year: number;
keyCode: SupportedKeyCode;
max: Date;
min: Date;
}

export type YearGridChangedProperties = ChangedProperties<YearGridProperties>;

export interface YearGridData {
date: Date;
Expand Down
76 changes: 60 additions & 16 deletions src/year-grid/year-grid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@ import { html, LitElement } from 'lit';
import { toUTCDate } from 'nodemod/dist/calendar/helpers/to-utc-date.js';

import { resetShadowRoot } from '../ stylings.js';
import { MAX_DATE } from '../constants.js';
import { keyCodesRecord, MAX_DATE, yearGridKeyCodeSet } from '../constants.js';
import { dispatchCustomEvent } from '../helpers/dispatch-custom-event.js';
import { toClosestTarget } from '../helpers/to-closest-target.js';
import { toResolvedDate } from '../helpers/to-resolved-date.js';
import { toYearList } from '../helpers/to-year-list.js';
import { APP_YEAR_GRID_BUTTON_NAME } from '../year-grid-button/constants.js';
import { toNextSelectedYear } from './ to-next-selected-year.js';
import { yearGridStyling } from './stylings.js';
import type { YearGridData, YearGridProperties } from './typings.js';
import type { YearGridChangedProperties, YearGridData, YearGridProperties } from './typings.js';

export class YearGrid extends LitElement implements YearGridProperties {
@property({ attribute: false })
Expand All @@ -29,6 +30,8 @@ export class YearGrid extends LitElement implements YearGridProperties {
yearGridStyling,
];

#selectedYear: number;

constructor() {
super();

Expand All @@ -40,20 +43,29 @@ export class YearGrid extends LitElement implements YearGridProperties {
max: MAX_DATE,
min: todayDate,
};
this.#selectedYear = todayDate.getUTCFullYear();
}

protected shouldUpdate(): boolean {
return this.data.formatters != null;
}

protected async updated(): Promise<void> {
protected async firstUpdated(): Promise<void> {
const selectedYearGridButton = await this.selectedYearGridButton;

if (selectedYearGridButton) {
selectedYearGridButton.scrollIntoView();
}
}

protected update(changedProperties: YearGridChangedProperties): void {
super.update(changedProperties);

if (changedProperties.has('data')) {
this.#selectedYear = this.data.date.getUTCFullYear();
}
}

protected render(): TemplateResult | typeof nothing {
const {
date,
Expand All @@ -72,11 +84,14 @@ export class YearGrid extends LitElement implements YearGridProperties {
yearList.map((year) => {
const yearDate = toUTCDate(year, 1, 1);
const yearLabel = yearFormat(yearDate);
const isYearSelected = yearDate.getUTCFullYear() === date.getUTCFullYear();
const fullYear = yearDate.getUTCFullYear();
// FIXME: To update tabindex
const isYearSelected = fullYear === date.getUTCFullYear();
return html`
<app-year-grid-button
data-year=${year}
tabindex=${isYearSelected ? '0' : '-1'}
data-year=${fullYear}
label=${yearLabel}
?unelevated=${isYearSelected}
></app-year-grid-button>
Expand All @@ -87,20 +102,49 @@ export class YearGrid extends LitElement implements YearGridProperties {
}

#updateYear = (ev: MouseEvent | KeyboardEvent): void => {
/** Do nothing when keyup.key is neither Enter nor ' ' (or Spacebar on older browsers) */
if (
(ev as KeyboardEvent).type === 'keyup' &&
!['Enter', ' ', 'Spacebar'].includes((ev as KeyboardEvent).key)
) return;
const {
date,
max,
min,
} = this.data;

let year = date.getUTCFullYear();

if (ev.type === 'keyup') {
const { keyCode } = ev as KeyboardEvent;
const keyCodeNum = keyCode as typeof keyCode extends Set<infer U> ? U : never;

if (keyCodeNum === keyCodesRecord.TAB) return;

if (yearGridKeyCodeSet.has(keyCodeNum)) {
const selectedYear = toNextSelectedYear({
keyCode: keyCodeNum,
max,
min,
year: this.#selectedYear,
});

const selectedYearGridButton = toClosestTarget(ev, `${APP_YEAR_GRID_BUTTON_NAME}[data-year]`);
const selectedYearGridButton = this.shadowRoot?.querySelector<HTMLButtonElement>(
`${APP_YEAR_GRID_BUTTON_NAME}[data-year="${selectedYear}"]`
);

/** Do nothing when not tapping on the year button */
if (selectedYearGridButton == null) return;
if (selectedYearGridButton) {
selectedYearGridButton.focus();
selectedYearGridButton.scrollIntoView();
}

const year = Number(
selectedYearGridButton.getAttribute('data-year')
);
this.#selectedYear = selectedYear;

return;
}
} else {
const selectedYearGridButton = toClosestTarget(ev, `${APP_YEAR_GRID_BUTTON_NAME}[data-year]`);

/** Do nothing when not tapping on the year button */
if (selectedYearGridButton == null) return;

year = Number(selectedYearGridButton.getAttribute('data-year'));
}

dispatchCustomEvent(this, 'year-updated', { year });
};
Expand Down

0 comments on commit c8ec5a9

Please sign in to comment.