@empathyco/x-components
Version:
Empathy X Components
139 lines (136 loc) • 5.33 kB
JavaScript
import { defineComponent, ref, computed, onMounted } from 'vue';
import '../composables/create-use-device.js';
import 'vuex';
import '@vue/devtools-api';
import '../plugins/devtools/timeline.devtools.js';
import '@empathyco/x-utils';
import 'rxjs/operators';
import 'rxjs';
import '../plugins/devtools/colors.utils.js';
import '../plugins/x-bus.js';
import '../plugins/x-plugin.js';
import { useXBus } from '../composables/use-x-bus.js';
import '@vueuse/core';
import { DirectionalFocusNavigationService } from '../services/directional-focus-navigation.service.js';
/**
* Base component to handle keyboard navigation for elements inside it. It has a required slot to
* include the navigable elements.
*
* @remarks
* The component can be customized through props: an array of navigationHijacker objects, which
* contains: the xEvent to listen to, the moduleName in charge of emitting the event and to which
* direction it should react to; to take control of the navigation and eventsForDirectionLimit, to
* emit an xEvent when reaching the navigation limit in any direction.
*
* @public
*/
var _sfc_main = defineComponent({
name: 'BaseKeyboardNavigation',
props: {
/**
* An array of {@link TakeNavigationControl} objects defining when to
* take control of the keyboard navigation.
*/
navigationHijacker: {
type: Array,
default: () => [
{ xEvent: 'UserPressedArrowKey', moduleName: 'searchBox', direction: 'ArrowDown' },
],
},
/**
* An {@link EventsForDirectionLimit} to emit when the user is already at the furthest element
* in a direction and tries to keep going on the same direction.
*/
eventsForDirectionLimit: {
type: Object,
default: () => ({ ArrowUp: 'UserReachedEmpathizeTop' }),
},
},
setup(props) {
const el = ref();
const xBus = useXBus();
/**
* The {@link SpatialNavigation} service to use.
*/
let navigationService;
/**
* The element to focus.
*/
let elementToFocus;
/**
* Get the navigation hijacker events.
*
* @remarks
* If the same {@link XEvent} is defined multiple times it is only inserted once.
*
* @returns The events to hijack the navigation.
*/
const navigationHijackerEvents = computed(() => {
const eventsSet = props.navigationHijacker.map(({ xEvent }) => xEvent);
return Array.from(new Set(eventsSet));
});
onMounted(() => {
// TODO Replace this with injection
navigationService = new DirectionalFocusNavigationService(el.value);
});
/**
* Checks if the component has to take control of the keyboard navigation.
*
* @param eventPayload - The {@link ArrowKey}.
* @param metadata - The {@link WireMetadata}.
*
* @returns Whether the component needs to take control of the keyboard navigation or not.
* @internal
*/
function hasToTakeNavigationControl(eventPayload, metadata) {
return props.navigationHijacker.some(({ moduleName, direction }) => moduleName === metadata.moduleName && direction === eventPayload);
}
/**
* Focus the next navigable element returned by the navigation service.
*
* @param direction - The navigation direction.
* @internal
*/
function focusNextNavigableElement(direction) {
const dir = typeof direction === 'object' ? direction.key : direction;
const nextElementToFocus = navigationService?.navigateTo(dir);
if (elementToFocus !== nextElementToFocus) {
elementToFocus = nextElementToFocus;
elementToFocus.focus();
}
else {
emitDirectionalLimitReached(dir);
elementToFocus = undefined;
}
}
/**
* Emit the {@link XEvent} associated to the navigation's direction when reaching its limit.
*
* @param direction - The navigation direction.
* @internal
*/
function emitDirectionalLimitReached(direction) {
const xEvent = props.eventsForDirectionLimit?.[direction];
if (xEvent) {
xBus.emit(xEvent, undefined, { target: elementToFocus });
}
}
/**
* Trigger navigation if this component is in control of it.
*
* @param eventPayload - The {@link @empathyco/x-bus#SubjectPayload.eventPayload}.
* @param metadata - The {@link @empathyco/x-bus#SubjectPayload.metadata}.
* @public
*/
navigationHijackerEvents.value.forEach(event => {
xBus.on(event, true).subscribe(({ eventPayload, metadata }) => {
if (hasToTakeNavigationControl(eventPayload, metadata)) {
focusNextNavigableElement(eventPayload);
}
});
});
return { el, focusNextNavigableElement };
},
});
export { _sfc_main as default };
//# sourceMappingURL=base-keyboard-navigation.vue2.js.map