Merge branch 'develop' into feature/cw-3191

This commit is contained in:
Muhsin Keloth
2024-04-26 16:03:06 +05:30
committed by GitHub
52 changed files with 664 additions and 924 deletions
@@ -8,16 +8,12 @@
</ul>
</template>
<script>
import eventListenerMixins from 'shared/mixins/eventListenerMixins';
import {
hasPressedArrowUpKey,
hasPressedArrowDownKey,
} from 'shared/helpers/KeyboardHelpers';
import keyboardEventListenerMixins from 'shared/mixins/keyboardEventListenerMixins';
export default {
name: 'WootDropdownMenu',
componentName: 'WootDropdownMenu',
mixins: [eventListenerMixins],
mixins: [keyboardEventListenerMixins],
props: {
placement: {
@@ -31,38 +27,46 @@ export default {
'ul.dropdown li.dropdown-menu__item .button'
);
},
activeElementIndex() {
const menuButtons = this.dropdownMenuButtons();
getActiveButtonIndex(menuButtons) {
const focusedButton = this.$refs.dropdownMenu.querySelector(
'ul.dropdown li.dropdown-menu__item .button:focus'
);
const activeIndex = [...menuButtons].indexOf(focusedButton);
return activeIndex;
return Array.from(menuButtons).indexOf(focusedButton);
},
handleKeyEvents(e) {
getKeyboardEvents() {
const menuButtons = this.dropdownMenuButtons();
const lastElementIndex = menuButtons.length - 1;
return {
ArrowUp: {
action: e => {
e.preventDefault();
this.focusPreviousButton(menuButtons);
},
allowOnFocusedInput: true,
},
ArrowDown: {
action: e => {
e.preventDefault();
this.focusNextButton(menuButtons);
},
allowOnFocusedInput: true,
},
};
},
focusPreviousButton(menuButtons) {
const activeIndex = this.getActiveButtonIndex(menuButtons);
const newIndex =
activeIndex >= 1 ? activeIndex - 1 : menuButtons.length - 1;
this.focusButton(menuButtons, newIndex);
},
focusNextButton(menuButtons) {
const activeIndex = this.getActiveButtonIndex(menuButtons);
const newIndex =
activeIndex === menuButtons.length - 1 ? 0 : activeIndex + 1;
this.focusButton(menuButtons, newIndex);
},
focusButton(menuButtons, newIndex) {
if (menuButtons.length === 0) return;
if (hasPressedArrowUpKey(e)) {
const activeIndex = this.activeElementIndex();
if (activeIndex >= 1) {
menuButtons[activeIndex - 1].focus();
} else {
menuButtons[lastElementIndex].focus();
}
}
if (hasPressedArrowDownKey(e)) {
const activeIndex = this.activeElementIndex();
if (activeIndex === lastElementIndex) {
menuButtons[0].focus();
} else {
menuButtons[activeIndex + 1].focus();
}
}
menuButtons[newIndex].focus();
},
},
};
@@ -1,9 +1,9 @@
export const isEnter = e => {
return e.keyCode === 13;
return e.key === 'Enter';
};
export const isEscape = e => {
return e.keyCode === 27;
return e.key === 'Escape';
};
export const hasPressedShift = e => {
@@ -19,118 +19,7 @@ export const hasPressedEnterAndNotCmdOrShift = e => {
};
export const hasPressedCommandAndEnter = e => {
return e.metaKey && e.keyCode === 13;
};
export const hasPressedCommandAndForwardSlash = e => {
return e.metaKey && e.keyCode === 191;
};
export const hasPressedAltAndCKey = e => {
return e.altKey && e.keyCode === 67;
};
export const hasPressedAltAndVKey = e => {
return e.altKey && e.keyCode === 86;
};
export const hasPressedAltAndRKey = e => {
return e.altKey && e.keyCode === 82;
};
export const hasPressedAltAndSKey = e => {
return e.altKey && e.keyCode === 83;
};
export const hasPressedAltAndBKey = e => {
return e.altKey && e.keyCode === 66;
};
export const hasPressedAltAndNKey = e => {
return e.altKey && e.keyCode === 78;
};
export const hasPressedAltAndAKey = e => {
return e.altKey && e.keyCode === 65;
};
export const hasPressedAltAndPKey = e => {
return e.altKey && e.keyCode === 80;
};
export const hasPressedAltAndLKey = e => {
return e.altKey && e.keyCode === 76;
};
export const hasPressedAltAndEKey = e => {
return e.altKey && e.keyCode === 69;
};
export const hasPressedCommandPlusAltAndEKey = e => {
return e.metaKey && e.altKey && e.keyCode === 69;
};
export const hasPressedAltAndOKey = e => {
return e.altKey && e.keyCode === 79;
};
export const hasPressedAltAndJKey = e => {
return e.altKey && e.keyCode === 74;
};
export const hasPressedAltAndKKey = e => {
return e.altKey && e.keyCode === 75;
};
export const hasPressedAltAndMKey = e => {
return e.altKey && e.keyCode === 77;
};
export const hasPressedArrowUpKey = e => {
return e.keyCode === 38;
};
export const hasPressedArrowDownKey = e => {
return e.keyCode === 40;
};
export const hasPressedArrowLeftKey = e => {
return e.keyCode === 37;
};
export const hasPressedArrowRightKey = e => {
return e.keyCode === 39;
};
export const hasPressedCommandPlusKKey = e => {
return e.metaKey && e.keyCode === 75;
};
/**
* Returns a string representation of the hotkey pattern based on the provided event object.
* @param {KeyboardEvent} e - The keyboard event object.
* @returns {string} - The hotkey pattern string.
*/
export const buildHotKeys = e => {
const key = e.key.toLowerCase();
if (['shift', 'meta', 'alt', 'control'].includes(key)) {
return key;
}
let hotKeyPattern = '';
if (e.altKey) {
hotKeyPattern += 'alt+';
}
if (e.ctrlKey) {
hotKeyPattern += 'ctrl+';
}
if (e.metaKey && !e.ctrlKey) {
hotKeyPattern += 'meta+';
}
if (e.shiftKey) {
hotKeyPattern += 'shift+';
}
hotKeyPattern += key;
return hotKeyPattern;
return hasPressedCommand(e) && isEnter(e);
};
/**
@@ -3,20 +3,19 @@ import {
isEscape,
hasPressedShift,
hasPressedCommand,
buildHotKeys,
isActiveElementTypeable,
} from '../KeyboardHelpers';
describe('#KeyboardHelpers', () => {
describe('#isEnter', () => {
it('return correct values', () => {
expect(isEnter({ keyCode: 13 })).toEqual(true);
expect(isEnter({ key: 'Enter' })).toEqual(true);
});
});
describe('#isEscape', () => {
it('return correct values', () => {
expect(isEscape({ keyCode: 27 })).toEqual(true);
expect(isEscape({ key: 'Escape' })).toEqual(true);
});
});
@@ -31,14 +30,6 @@ describe('#KeyboardHelpers', () => {
expect(hasPressedCommand({ metaKey: true })).toEqual(true);
});
});
describe('#buildHotKeys', () => {
it('returns hot keys', () => {
expect(buildHotKeys({ altKey: true, key: 'alt' })).toEqual('alt');
expect(buildHotKeys({ ctrlKey: true, key: 'a' })).toEqual('ctrl+a');
expect(buildHotKeys({ metaKey: true, key: 'b' })).toEqual('meta+b');
});
});
});
describe('isActiveElementTypeable', () => {
@@ -1,24 +0,0 @@
import { isActiveElementTypeable, isEscape } from '../helpers/KeyboardHelpers';
export default {
mounted() {
document.addEventListener('keydown', this.onKeyDownHandler);
},
beforeDestroy() {
document.removeEventListener('keydown', this.onKeyDownHandler);
},
methods: {
onKeyDownHandler(e) {
const isTypeable = isActiveElementTypeable(e);
if (isTypeable) {
if (isEscape(e)) {
e.target.blur();
}
return;
}
this.handleKeyEvents(e);
},
},
};
@@ -0,0 +1,63 @@
import { isActiveElementTypeable, isEscape } from '../helpers/KeyboardHelpers';
import { createKeybindingsHandler } from 'tinykeys';
// this is a store that stores the handler globally, and only gets reset on reload
const taggedHandlers = [];
export default {
mounted() {
const events = this.getKeyboardEvents();
if (events) {
const wrappedEvents = this.wrapEventsInKeybindingsHandler(events);
const keydownHandler = createKeybindingsHandler(wrappedEvents);
this.appendToHandler(keydownHandler);
document.addEventListener('keydown', keydownHandler);
}
},
beforeDestroy() {
if (this.$el && this.$el.dataset.keydownHandlerIndex) {
const handlerToRemove =
taggedHandlers[this.$el.dataset.keydownHandlerIndex];
document.removeEventListener('keydown', handlerToRemove);
}
},
methods: {
appendToHandler(keydownHandler) {
const indexToAppend = taggedHandlers.push(keydownHandler) - 1;
const root = this.$el;
root.dataset.keydownHandlerIndex = indexToAppend;
},
getKeyboardEvents() {
return null;
},
wrapEventsInKeybindingsHandler(events) {
const wrappedEvents = {};
Object.keys(events).forEach(eventName => {
wrappedEvents[eventName] = this.keydownWrapper(events[eventName]);
});
return wrappedEvents;
},
keydownWrapper(handler) {
return e => {
const actionToPerform =
typeof handler === 'function' ? handler : handler.action;
const allowOnFocusedInput =
typeof handler === 'function' ? false : handler.allowOnFocusedInput;
const isTypeable = isActiveElementTypeable(e);
if (isTypeable) {
if (isEscape(e)) {
e.target.blur();
}
if (!allowOnFocusedInput) return;
}
actionToPerform(e);
};
},
},
};