UNPKG

@empathyco/x-components

Version:
139 lines (136 loc) 5.33 kB
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