/**
* This store manages the registration, removal, and formatting of keyboard shortcuts, for use in help UIs
* @file store/shortcuts.ts
* @namespace .shortcuts
* @memberOf store
*/
// setup Pinia store
import { _status } from "@/common";
import { defineStore } from "pinia";
/**
* @memberOf .shortcuts
* @function clean_key
* @description Clean up keystrings for display
* @param {String} key keystring to reformat
* @returns {Array} Formatted keystrings
*/
function clean_key(key) {
if (!key)
return;
key = key.replace(" or ", ", ");
return key.split(", ").map((k) => {
// replace control keys with symbols
k = k.replace("Control", "Ctrl");
// do macos specifics
if ((navigator === null || navigator === void 0 ? void 0 : navigator.userAgent.indexOf("Mac OS X")) != -1) {
k = k.replace("Ctrl", "⌘");
k = k.replace("Meta", "⌘");
k = k.replace("Shift", "⇧");
k = k.replace("Alt", "⌥");
k = k.replace("Escape", "esc");
k = k.replace("Home", "fn + ←");
k = k.replace("End", "fn + →");
}
// replace arrow keys with symbols
k = k.replace("ArrowUp", "↑");
k = k.replace("ArrowDown", "↓");
k = k.replace("ArrowLeft", "←");
k = k.replace("ArrowRight", "→");
// replace other keys with symbols
k = k.replace("Enter", "⏎");
k = k.replace("Escape", "Esc");
k = k.replace("Backspace", "⌫");
k = k.replace("Delete", "⌦");
k = k.replace("Tab", "⇥");
return k;
});
}
export const useShortcuts = defineStore({
id: "shortcuts",
state: () => ({
shortcuts: [],
keys: [],
active: {},
}),
/**
* @namespace .shortcuts.getters
* @memberof .shortcuts
*/
getters: {
/**
* @memberOf .shortcuts.getters
* @function list
* @description Get a list of shortcuts
* @returns {Array} The formatted shortcuts
*/
list() {
return this.shortcuts.map((s) => {
return Object.assign(Object.assign({}, s), { keys: clean_key(s.key) });
});
},
/**
* @memberOf .shortcuts.getters
* @function sections
* @description Get a list of shortcuts by section
* @returns {} Formatted shortcuts sorted by section
*/
sections() {
// take this.list and return an array of sections by tag
let sections = [];
let tags = this.shortcuts.map((s) => s.tag);
tags = [...new Set(tags)];
for (let i = 0; i < tags.length; i++) {
sections.push({
tag: tags[i],
list: this.list.filter((s) => s.tag === tags[i]),
});
}
return sections;
},
},
/**
* @namespace .shortcuts.actions
* @memberof .shortcuts
*/
actions: {
/**
* @memberOf .shortcuts.actions
* @function get
* @description Get a shortcut by object
* @param {Object} shortcut
* @returns {Object} the shortcut object matching the object
*/
get(shortcut) {
return this.shortcuts.find((s) => s === shortcut);
},
/**
* @memberOf .shortcuts.actions
* @function get_key
* @description Get a shortcut by key
* @param {String} key
*/
get_key(key) {
if (!this.keys.includes(key))
return null;
return this.shortcuts.find((s) => s.key === key) || null;
},
/**
* @memberOf .shortcuts.actions
* @function register
* @description Register a new shortcut
* @param {Object} shortcut
* @param {Boolean} skipSort
*/
register(shortcut, skipSort = false) {
if (!(shortcut === null || shortcut === void 0 ? void 0 : shortcut.key) || !(shortcut === null || shortcut === void 0 ? void 0 : shortcut.description)) {
_status.error("⌨️ Shortcut must have key and description text");
return;
}
if (this.keys.includes(shortcut.key))
_status.warn(`⌨️ Shortcut key "${shortcut.key}" already registered`);
this.keys.push(shortcut.key);
this.shortcuts.push(shortcut);
if (!skipSort)
this.sort();
},
/**
* @memberOf .shortcuts.actions
* @function register_all
* @description Register a list of shortcuts with a given tag
* @param {Array} list
* @param {String} tag
*/
register_all(list, tag) {
for (let i = 0; i < list.length; i++) {
this.register(Object.assign(Object.assign({}, list[i]), { tag }), true);
}
this.sort();
},
/**
* @memberOf .shortcuts.actions
* @function remove
* @description Remove a shortcut
* @param {Object} shortcut
*/
remove(shortcut) {
if (!this.shortcuts.includes(shortcut)) {
_status.error("⌨️ Shortcut not registered, cannot remove", shortcut);
return;
}
this.shortcuts.splice(this.shortcuts.indexOf(shortcut), 1);
},
/**
* @memberOf .shortcuts.actions
* @function remove_all
* @description Remove all shortcuts with a given tag
* @param {Array} list
* @param {String} tag
*/
remove_all(list, tag) {
for (let i = 0; i < list.length; i++) {
this.remove(Object.assign(Object.assign({}, list[i]), { tag }));
}
},
/**
* @memberOf .shortcuts.actions
* @function remove_tag
* @description Remove all shortcuts with a given tag
* @param {String} tag
*/
remove_tag(tag) {
this.shortcuts = this.shortcuts.filter((s) => s.tag !== tag);
this.keys = this.shortcuts.map((s) => s.key);
},
/**
* @memberOf .shortcuts.actions
* @function remove_key
* @description Remove a shortcut by key
* @param {String} key
*/
remove_key(key) {
if (!this.keys.includes(key)) {
_status.error(`⌨️ Key "${key}" not registered, cannot remove shortcut`);
return;
}
this.keys.splice(this.keys.indexOf(key), 1);
this.shortcuts.splice(this.shortcuts.indexOf(this.get_key(key)), 1);
},
/**
* @memberOf .shortcuts.actions
* @function remove_keys
* @description Remove shortcuts with any of the given keys
* @param {Array} keys
*/
remove_keys(keys) {
for (let i = 0; i < keys.length; i++) {
this.remove_key(keys[i]);
}
},
/**
* @memberOf .shortcuts.actions
* @function sort
* @description Sort the shortcuts
*/
sort() {
this.shortcuts.sort((a, b) => {
// compare tags, if different, sort by tag, and put General at the top, falsey at the bottom
if (a.tag !== b.tag) {
if (a.tag === "General" || !b.tag)
return -1;
if (b.tag === "General" || !a.tag)
return 1;
return a.tag.localeCompare(b.tag);
}
// check for priority
if (a.top && !b.top)
return -1;
if (!a.top && b.top)
return 1;
// compare key
if (a.key !== b.key) {
return a.key.localeCompare(b.key);
}
// compare description
return a.description.localeCompare(b.description);
});
},
/**
* @memberOf .shortcuts.actions
* @function clear
* @description Clear all shortcuts
*/
clear() {
this.shortcuts = [];
this.keys = [];
_status.log("⌨️ Cleared shortcuts");
},
/**
* @memberOf .shortcuts.actions
* @function set_activity
* @description Set the activity state of a shortcut
* @param {Boolean} state
* @param {String} shortName
*/
set_activity(state, shortName) {
this.active[shortName] = state;
},
/**
* @memberOf .shortcuts.actions
* @function is_active
* @description Check if a shortcut is active
* @param {String} shortName
* @returns {Boolean} state
*/
is_active(shortName) {
return this.active[shortName];
},
},
});