Skip to content

Commit b539090

Browse files
committed
refactor: replace symbols with dumb classes
1 parent d4f85f2 commit b539090

5 files changed

+74
-64
lines changed

.eslintrc.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,8 @@
44
"sourceType": "module",
55
"ecmaVersion": 2018,
66
"project": "./tsconfig.json"
7+
},
8+
"rules": {
9+
"max-classes-per-file": "off"
710
}
811
}

src/keyboard-direction-mixin.ts

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,27 @@ import { SlottedItemsInterface } from './slotted-items-mixin';
44
// eslint-disable-next-line @typescript-eslint/no-explicit-any
55
type Constructor<T = object> = new (...args: any[]) => T;
66

7-
export const $onKeydown = Symbol('onKeydown');
7+
export interface KeyboardDirectionInterface {
8+
items: HTMLElement[];
9+
focused: Element | null;
10+
}
811

9-
export const $isPrevKey = Symbol('isPrevKey');
12+
export abstract class KeyboardDirectionClass extends LitElement {
13+
protected _onKeydown?(event: KeyboardEvent): void;
1014

11-
export const $isNextKey = Symbol('isNextKey');
15+
protected _focus?(item: HTMLElement): void;
1216

13-
export const $focus = Symbol('focus');
17+
protected _isNextKey?(key: string): boolean;
1418

15-
export interface KeyboardDirectionInterface {
16-
items: HTMLElement[];
17-
focused: Element | null;
18-
[$onKeydown](event: KeyboardEvent): void;
19-
[$isPrevKey](key: string): boolean;
20-
[$isNextKey](key: string): boolean;
21-
[$focus](item: HTMLElement): void;
19+
protected _isPrevKey?(key: string): boolean;
2220
}
2321

2422
export type ItemCondition = (item: Element) => boolean;
2523

2624
export const isFocusable: ItemCondition = (item: Element) =>
2725
!item.hasAttribute('disabled') && !item.hasAttribute('hidden');
2826

29-
export type KeyboardDirectionConstructor = Constructor<KeyboardDirectionInterface>;
27+
export type KeyboardDirectionConstructor = Constructor<KeyboardDirectionClass & KeyboardDirectionInterface>;
3028

3129
/**
3230
* Returns index of the next item that satisfies the given condition,
@@ -60,7 +58,7 @@ export const getAvailableIndex = (
6058
return -1;
6159
};
6260

63-
export const KeyboardDirectionMixin = <T extends Constructor<SlottedItemsInterface & LitElement>>(
61+
export const KeyboardDirectionMixin = <T extends Constructor<SlottedItemsInterface & KeyboardDirectionClass>>(
6462
base: T
6563
): KeyboardDirectionConstructor & T => {
6664
class KeyboardDirection extends base {
@@ -81,11 +79,11 @@ export const KeyboardDirectionMixin = <T extends Constructor<SlottedItemsInterfa
8179
super.firstUpdated(props);
8280

8381
this.addEventListener('keydown', (event: KeyboardEvent) => {
84-
this[$onKeydown](event);
82+
this._onKeydown(event);
8583
});
8684
}
8785

88-
[$onKeydown](event: KeyboardEvent) {
86+
protected _onKeydown(event: KeyboardEvent) {
8987
if (event.metaKey || event.ctrlKey) {
9088
return;
9189
}
@@ -97,10 +95,10 @@ export const KeyboardDirectionMixin = <T extends Constructor<SlottedItemsInterfa
9795
let idx;
9896
let increment;
9997

100-
if (this[$isPrevKey](key)) {
98+
if (this._isPrevKey(key)) {
10199
increment = -1;
102100
idx = currentIdx - 1;
103-
} else if (this[$isNextKey](key)) {
101+
} else if (this._isNextKey(key)) {
104102
increment = 1;
105103
idx = currentIdx + 1;
106104
} else if (key === 'Home') {
@@ -116,20 +114,20 @@ export const KeyboardDirectionMixin = <T extends Constructor<SlottedItemsInterfa
116114
event.preventDefault();
117115
const item = items[idx] as HTMLElement;
118116
if (item) {
119-
this[$focus](item);
117+
this._focus(item);
120118
}
121119
}
122120
}
123121

124-
[$isPrevKey](key: string) {
122+
protected _isPrevKey(key: string) {
125123
return key === 'ArrowUp' || key === 'ArrowLeft';
126124
}
127125

128-
[$isNextKey](key: string) {
126+
protected _isNextKey(key: string) {
129127
return key === 'ArrowDown' || key === 'ArrowRight';
130128
}
131129

132-
[$focus](item: HTMLElement) {
130+
protected _focus(item: HTMLElement) {
133131
item.focus();
134132
}
135133
}

src/roving-tabindex-mixin.ts

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,31 @@
11
import { LitElement } from 'lit-element';
2-
import { KeyboardDirectionInterface, getAvailableIndex, isFocusable, $focus } from './keyboard-direction-mixin';
3-
import { $itemsChanged, SlottedItemsInterface } from './slotted-items-mixin';
2+
import {
3+
KeyboardDirectionClass,
4+
KeyboardDirectionInterface,
5+
getAvailableIndex,
6+
isFocusable
7+
} from './keyboard-direction-mixin';
8+
import { SlottedItemsClass, SlottedItemsInterface } from './slotted-items-mixin';
49

510
// eslint-disable-next-line @typescript-eslint/no-explicit-any
611
type Constructor<T = object> = new (...args: any[]) => T;
712

8-
const $setFocusable = Symbol('setFocusable');
13+
export abstract class RovingTabIndexClass extends LitElement {
14+
protected _setFocusable?(idx: number): void;
915

10-
const $setTabIndex = Symbol('setTabIndex');
11-
12-
export interface RovingTabIndexInterface {
13-
[$setFocusable](idx: number): void;
14-
[$setTabIndex](item: HTMLElement): void;
16+
protected _setTabIndex?(item: HTMLElement): void;
1517
}
1618

17-
export type RovingTabIndexConstructor = Constructor<RovingTabIndexInterface>;
19+
export type RovingTabIndexConstructor = Constructor<RovingTabIndexClass>;
1820

1921
export const RovingTabIndexMixin = <
20-
T extends Constructor<KeyboardDirectionInterface & SlottedItemsInterface & LitElement>
22+
T extends Constructor<
23+
KeyboardDirectionClass &
24+
KeyboardDirectionInterface &
25+
SlottedItemsClass &
26+
SlottedItemsInterface &
27+
RovingTabIndexClass
28+
>
2129
>(
2230
base: T
2331
): T & RovingTabIndexConstructor => {
@@ -29,30 +37,30 @@ export const RovingTabIndexMixin = <
2937
}
3038
}
3139

32-
[$itemsChanged](items: HTMLElement[], oldItems: HTMLElement[]) {
33-
super[$itemsChanged](items, oldItems);
40+
protected _itemsChanged(items: HTMLElement[], oldItems: HTMLElement[]) {
41+
super._itemsChanged && super._itemsChanged(items, oldItems); // eslint-disable-line no-unused-expressions
3442

3543
if (items) {
3644
const { focused } = this;
37-
this[$setFocusable](focused && this.contains(focused) ? items.indexOf(focused as HTMLElement) : 0);
45+
this._setFocusable(focused && this.contains(focused) ? items.indexOf(focused as HTMLElement) : 0);
3846
}
3947
}
4048

41-
[$setFocusable](idx: number) {
49+
protected _setFocusable(idx: number) {
4250
const index = getAvailableIndex(this.items, idx, 1, isFocusable);
43-
this[$setTabIndex](this.items[index]);
51+
this._setTabIndex(this.items[index]);
4452
}
4553

46-
[$setTabIndex](item: HTMLElement) {
54+
protected _setTabIndex(item: HTMLElement) {
4755
this.items.forEach((el: HTMLElement) => {
4856
// eslint-disable-next-line no-param-reassign
4957
el.tabIndex = el === item ? 0 : -1;
5058
});
5159
}
5260

53-
[$focus](item: HTMLElement) {
54-
super[$focus](item);
55-
this[$setTabIndex](item);
61+
protected _focus(item: HTMLElement) {
62+
super._focus && super._focus(item); // eslint-disable-line no-unused-expressions
63+
this._setTabIndex(item);
5664
}
5765
}
5866

src/slotted-items-mixin.ts

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,19 @@ import { LitElement, property, PropertyValues } from 'lit-element';
33
// eslint-disable-next-line @typescript-eslint/no-explicit-any
44
type Constructor<T = object> = new (...args: any[]) => T;
55

6-
export const $itemsChanged = Symbol('itemsChanged');
7-
export const $filterItems = Symbol('filterItems');
8-
96
export interface SlottedItemsInterface {
107
items: HTMLElement[];
11-
[$itemsChanged](items: HTMLElement[], oldItems: HTMLElement[]): void;
12-
[$filterItems](): HTMLElement[];
138
}
149

15-
export type SlottedItemsConstructor = Constructor<SlottedItemsInterface>;
10+
export abstract class SlottedItemsClass extends LitElement {
11+
protected _itemsChanged?(items: HTMLElement[], oldItems: HTMLElement[]): void;
12+
13+
protected _filterItems?(): HTMLElement[];
14+
}
15+
16+
export type SlottedItemsConstructor = Constructor<SlottedItemsClass & SlottedItemsInterface>;
1617

17-
export const SlottedItemsMixin = <T extends Constructor<LitElement>>(base: T): SlottedItemsConstructor & T => {
18+
export const SlottedItemsMixin = <T extends Constructor<SlottedItemsClass>>(base: T): SlottedItemsConstructor & T => {
1819
class SlottedItems extends base {
1920
@property({ attribute: false, hasChanged: () => true })
2021
protected _items: HTMLElement[] = [];
@@ -26,12 +27,12 @@ export const SlottedItemsMixin = <T extends Constructor<LitElement>>(base: T): S
2627
connectedCallback() {
2728
super.connectedCallback();
2829

29-
this._items = this[$filterItems]();
30+
this._items = this._filterItems();
3031
}
3132

3233
protected update(props: PropertyValues) {
3334
if (props.has('_items')) {
34-
this[$itemsChanged](this._items, (props.get('_items') || []) as HTMLElement[]);
35+
this._itemsChanged(this._items, (props.get('_items') || []) as HTMLElement[]);
3536
}
3637

3738
super.update(props);
@@ -43,13 +44,13 @@ export const SlottedItemsMixin = <T extends Constructor<LitElement>>(base: T): S
4344
const slot = this.renderRoot.querySelector('slot');
4445
if (slot) {
4546
slot.addEventListener('slotchange', () => {
46-
this._items = this[$filterItems]();
47+
this._items = this._filterItems();
4748
});
4849
}
4950
}
5051

5152
// eslint-disable-next-line @typescript-eslint/no-unused-vars
52-
[$itemsChanged](items: HTMLElement[], _oldItems: HTMLElement[]) {
53+
protected _itemsChanged(items: HTMLElement[], _oldItems: HTMLElement[]) {
5354
this.dispatchEvent(
5455
new CustomEvent('items-changed', {
5556
detail: {
@@ -59,7 +60,7 @@ export const SlottedItemsMixin = <T extends Constructor<LitElement>>(base: T): S
5960
);
6061
}
6162

62-
[$filterItems]() {
63+
protected _filterItems() {
6364
return Array.from(this.children) as HTMLElement[];
6465
}
6566
}

src/vaadin-accordion.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { html, css, customElement, property, PropertyValues } from 'lit-element';
22
import { VaadinElement } from '@vaadin/element-base/vaadin-element.js';
3-
import { KeyboardDirectionMixin, $focus, $isPrevKey, $isNextKey, $onKeydown } from './keyboard-direction-mixin';
4-
import { SlottedItemsMixin, $itemsChanged, $filterItems } from './slotted-items-mixin';
3+
import { KeyboardDirectionMixin } from './keyboard-direction-mixin';
4+
import { SlottedItemsMixin } from './slotted-items-mixin';
55
import { VaadinAccordionPanel } from './vaadin-accordion-panel';
66

77
declare global {
@@ -85,25 +85,25 @@ export class VaadinAccordion extends KeyboardDirectionMixin(SlottedItemsMixin(Va
8585
}
8686
}
8787

88-
[$filterItems]() {
88+
protected _filterItems() {
8989
return Array.from(this.querySelectorAll(VaadinAccordionPanel.is)) as VaadinAccordionPanel[];
9090
}
9191

92-
[$focus](item: VaadinAccordionPanel) {
93-
super[$focus](item);
92+
protected _focus(item: VaadinAccordionPanel) {
93+
super._focus && super._focus(item); // eslint-disable-line no-unused-expressions
9494
item.setAttribute('focus-ring', '');
9595
}
9696

97-
[$isNextKey](key: string) {
97+
protected _isNextKey(key: string) {
9898
return key === 'ArrowDown';
9999
}
100100

101-
[$isPrevKey](key: string) {
101+
protected _isPrevKey(key: string) {
102102
return key === 'ArrowUp';
103103
}
104104

105-
[$itemsChanged](panels: VaadinAccordionPanel[], oldPanels: VaadinAccordionPanel[]) {
106-
super[$itemsChanged](panels, oldPanels);
105+
protected _itemsChanged(panels: VaadinAccordionPanel[], oldPanels: VaadinAccordionPanel[]) {
106+
super._itemsChanged && super._itemsChanged(panels, oldPanels); // eslint-disable-line no-unused-expressions
107107

108108
panels
109109
.filter(panel => !oldPanels.includes(panel))
@@ -118,11 +118,11 @@ export class VaadinAccordion extends KeyboardDirectionMixin(SlottedItemsMixin(Va
118118
});
119119
}
120120

121-
[$onKeydown](event: KeyboardEvent) {
121+
protected _onKeydown(event: KeyboardEvent) {
122122
// only check keyboard events on summary, not on the content
123123
const summary = event.composedPath()[0] as HTMLElement;
124-
if (summary && summary.getAttribute('part') === 'summary') {
125-
super[$onKeydown](event);
124+
if (summary && summary.getAttribute('part') === 'summary' && super._onKeydown) {
125+
super._onKeydown(event);
126126
}
127127
}
128128

0 commit comments

Comments
 (0)