Skip to content

Commit

Permalink
Fix hook types, fix wrong name of context function (#27)
Browse files Browse the repository at this point in the history
* Fix hook types, fix wrong name of context function

* Fix examples

* Fix example
  • Loading branch information
kadiryazici authored Oct 20, 2022
1 parent 1a06992 commit 398d7d1
Show file tree
Hide file tree
Showing 6 changed files with 40 additions and 34 deletions.
2 changes: 1 addition & 1 deletion playground/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { computed, ref, watchEffect } from 'vue';
import TheSidebar from './components/TheSidebar.vue';
import examples from './examples';
const activeExampleId = ref(
const activeExampleId = ref<string>(
examples.find((example) => example.id === window.location.hash.slice(1))?.id || examples[0].id,
);
Expand Down
2 changes: 1 addition & 1 deletion playground/examples/keyboard/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ function setupHandler(ctx: Context) {
// so it selects twice.
if (document.activeElement?.hasAttribute('data-vue-selectable-items-item')) return;
ctx.selectFocusedElement();
ctx.selectFocusedItem();
},
{ input: true },
);
Expand Down
2 changes: 1 addition & 1 deletion playground/examples/nested/ItemRenderer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ function setupHandler(ctx: Context) {
return;
}
ctx.selectFocusedElement();
ctx.selectFocusedItem();
},
{ input: true },
);
Expand Down
56 changes: 27 additions & 29 deletions src/Component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ import { filterSelectableItems, isComponent, isCustomItem, isItem, isItemGroup }
import type {
AllItems,
Item,
Hook,
FocusHook,
SelectHook,
UnfocusHook,
DOMFocusHook,
HookFnMap,
} from './types';

const enum Direction {
Expand All @@ -38,15 +38,15 @@ export type Context = {
clearFocus(): void;
setFocusByKey(key?: string | null | undefined): void;
setFocusByIndex(index: number): void;
onSelect(fn: Hook): void;
onSelect(fn: SelectHook): void;
onFocus(fn: FocusHook): void;
onUnfocus(fn: Hook): void;
onDOMFocus(fn: Hook): void;
onUnfocus(fn: UnfocusHook): void;
onDOMFocus(fn: DOMFocusHook): void;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
getItemMetaByKey<Meta = any>(key: string): Meta | undefined;
getSelectableItemCount(): number;
getSelectableItems<Meta = unknown>(): Item<Meta>[];
selectFocusedElement(): void;
selectFocusedItem(): void;
getFocusedItem<Meta = unknown>(): Item<Meta> | undefined;
getItemElementByKey(key: string): HTMLElement | undefined;
getItemElementByIndex(index: number): HTMLElement | undefined;
Expand Down Expand Up @@ -107,37 +107,34 @@ export default defineComponent({

const hooks = {
[HookType.Focus]: new Set<FocusHook>(),
[HookType.Select]: new Set<Hook>(),
[HookType.Unfocus]: new Set<Hook>(),
[HookType.DOMFocus]: new Set<Hook>(),
[HookType.Select]: new Set<SelectHook>(),
[HookType.Unfocus]: new Set<UnfocusHook>(),
[HookType.DOMFocus]: new Set<DOMFocusHook>(),
};

const onFocus = (fn: FocusHook) => hooks[HookType.Focus].add(fn);
const onSelect = (fn: Hook) => hooks[HookType.Select].add(fn);
const onUnfocus = (fn: Hook) => hooks[HookType.Unfocus].add(fn);
const onDOMFocus = (fn: Hook) => hooks[HookType.DOMFocus].add(fn);

const runHooks = (type: HookType, item: Item, byPointer = false) => {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const el = selectableItemsElements.get(item.key)!;
if (type === HookType.Focus) {
hooks[type].forEach((hook) => hook(item.meta, item, el, byPointer));
} else {
hooks[type].forEach((hook) => hook(item.meta, item, el));
}
};
const onSelect = (fn: SelectHook) => hooks[HookType.Select].add(fn);
const onUnfocus = (fn: UnfocusHook) => hooks[HookType.Unfocus].add(fn);
const onDOMFocus = (fn: DOMFocusHook) => hooks[HookType.DOMFocus].add(fn);

function runHook<T extends HookType>(hook: T, ...args: Parameters<HookFnMap[T]>) {
// @ts-expect-error spread error
hooks[hook].forEach((_hook) => _hook(...args));
}

function handleFocusedItemChange(to?: Item, from?: Item, byPointer = false) {
if (to?.key === from?.key) return;

if (isItem(from)) {
runHooks(HookType.Unfocus, from);
emit('itemUnfocus', from.meta, from, getItemElementByKey(from.key)!);
const el = getItemElementByKey(from.key)!;
runHook(HookType.Unfocus, from.meta, from, el);
emit('itemUnfocus', from.meta, from, el);
}

if (isItem(to)) {
runHooks(HookType.Focus, to, byPointer);
emit('itemFocus', to.meta, to, getItemElementByKey(to.key)!, byPointer);
const el = getItemElementByKey(to.key)!;
runHook(HookType.Focus, to.meta, to, el, byPointer);
emit('itemFocus', to.meta, to, el, byPointer);
}
}

Expand Down Expand Up @@ -210,8 +207,9 @@ export default defineComponent({
const handleDOMFocus = (event: FocusEvent, item: Item<unknown>) => {
if (item.disabled) return;

runHooks(HookType.DOMFocus, item);
emit('itemDOMFocus', event, item.meta, item, getItemElementByKey(item.key)!);
const el = getItemElementByKey(item.key)!;
runHook(HookType.DOMFocus, event, item.meta, item, el);
emit('itemDOMFocus', event, item.meta, item, el);
};

const focusNextInDirection = (direction: Direction) => {
Expand Down Expand Up @@ -256,7 +254,7 @@ export default defineComponent({
const el = getItemElementByKey(item.key)!;

item.onSelect?.(item.meta, item, el);
runHooks(HookType.Select, item);
runHook(HookType.Select, item.meta, item, el);
emit('select', item.meta, item, el);
};

Expand All @@ -271,7 +269,7 @@ export default defineComponent({
onUnfocus,
onDOMFocus,
getSelectableItemCount: () => selectableItems.value.length,
selectFocusedElement: () => selectItem(focusedItemKey.value),
selectFocusedItem: () => selectItem(focusedItemKey.value),
getItemElementByIndex,
getItemElementByKey,
scrollToFocusedItemElement,
Expand Down
8 changes: 8 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { FunctionalComponent, Ref } from 'vue';
import type { HookType } from './constants';

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type AllItems<T = any> = Item<T> | CustomItem<T> | ItemGroup;
Expand Down Expand Up @@ -71,6 +72,13 @@ export type FocusHook = (meta: any, item: Item<any>, el: HTMLElement, byPointer:
export type DOMFocusHook = (event: FocusEvent, meta: any, item: Item<any>, el: HTMLElement) => void;
/* eslint-enable @typescript-eslint/no-explicit-any */

export interface HookFnMap {
[HookType.DOMFocus]: DOMFocusHook;
[HookType.Select]: SelectHook;
[HookType.Focus]: FocusHook;
[HookType.Unfocus]: UnfocusHook;
}

export type CustomItemOptions<Meta = unknown> = Omit<CustomItem<Meta>, 'type'>;

export type ItemOptions<Meta = unknown, WrapperComponent = unknown, ElementTag = unknown> = Omit<
Expand Down
4 changes: 2 additions & 2 deletions tests/component.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ describe('main tests', () => {
expect(onItemDOMFocusSpy).not.toHaveBeenCalled();
expect(onSelectSpy).not.toHaveBeenCalled();

context.selectFocusedElement();
context.selectFocusedItem();
expect(onSelectSpy).toHaveBeenCalled();
expect(context.getFocusedItemElement()).toBeInstanceOf(HTMLElement);
expect(context.getFocusedItemElement().classList.contains('vue-selectable-items-item')).toBe(
Expand Down Expand Up @@ -186,7 +186,7 @@ describe('main tests', () => {
await wrapper.setProps({ items });
context.clearFocus();
context.focusNext();
context.selectFocusedElement();
context.selectFocusedItem();
expect(itemOnSelectSpy).toHaveBeenCalled();
}
});
Expand Down

0 comments on commit 398d7d1

Please sign in to comment.