vuetify
Version:
Vue Material Component Framework
154 lines (146 loc) • 5.24 kB
JavaScript
// Utilities
import { normalizeKey } from "./key-aliases.js";
import { consoleWarn } from "../../util/console.js"; // Types
export const MODIFIERS = ['ctrl', 'shift', 'alt', 'meta', 'cmd'];
/**
* Splits a single combination string into individual key parts.
*
* A combination is a set of keys that must be pressed simultaneously.
* e.g. `ctrl+k`, `shift--`
*/
export function splitKeyCombination(combination) {
let isInternal = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
if (!combination) {
if (!isInternal) consoleWarn('Invalid hotkey combination: empty string provided');
return [];
}
// --- VALIDATION ---
const startsWithPlusOrUnderscore = combination.startsWith('+') || combination.startsWith('_');
const hasInvalidLeadingSeparator =
// Starts with a single '+' or '_' followed by a non-separator character (e.g. '+a', '_a')
startsWithPlusOrUnderscore && !(combination.startsWith('++') || combination.startsWith('__'));
const hasInvalidStructure =
// Invalid leading separator patterns
combination.length > 1 && hasInvalidLeadingSeparator ||
// Disallow literal + or _ keys (they require shift)
combination.includes('++') || combination.includes('__') || combination === '+' || combination === '_' ||
// Ends with a separator that is not part of a doubled literal
combination.length > 1 && (combination.endsWith('+') || combination.endsWith('_')) && combination.at(-2) !== combination.at(-1) ||
// Stand-alone doubled separators (dangling)
combination === '++' || combination === '--' || combination === '__';
if (hasInvalidStructure) {
if (!isInternal) consoleWarn(`Invalid hotkey combination: "${combination}" has invalid structure`);
return [];
}
const keys = [];
let buffer = '';
const flushBuffer = () => {
if (buffer) {
keys.push(normalizeKey(buffer));
buffer = '';
}
};
for (let i = 0; i < combination.length; i++) {
const char = combination[i];
const nextChar = combination[i + 1];
if (char === '+' || char === '_' || char === '-') {
if (char === nextChar) {
flushBuffer();
keys.push(char);
i++;
} else if (char === '+' || char === '_') {
flushBuffer();
} else {
buffer += char;
}
} else {
buffer += char;
}
}
flushBuffer();
// Within a combination, `-` is only valid as a literal key (e.g., `ctrl+-`).
// `-` cannot be part of a longer key name within a combination.
const hasInvalidMinus = keys.some(key => key.length > 1 && key.includes('-') && key !== '--');
if (hasInvalidMinus) {
if (!isInternal) consoleWarn(`Invalid hotkey combination: "${combination}" has invalid structure`);
return [];
}
if (keys.length === 0 && combination) {
return [normalizeKey(combination)];
}
return keys;
}
/**
* Splits a hotkey string into its constituent combination groups.
*
* A sequence is a series of combinations that must be pressed in order.
* e.g. `a-b`, `ctrl+k-p`
*/
export function splitKeySequence(str) {
if (!str) {
consoleWarn('Invalid hotkey sequence: empty string provided');
return [];
}
// A sequence is invalid if it starts or ends with a separator,
// unless it is part of a combination (e.g., `shift+-`).
const hasInvalidStart = str.startsWith('-') && !['---', '--+'].includes(str);
const hasInvalidEnd = str.endsWith('-') && !str.endsWith('+-') && !str.endsWith('_-') && str !== '-' && str !== '---';
if (hasInvalidStart || hasInvalidEnd) {
consoleWarn(`Invalid hotkey sequence: "${str}" contains invalid combinations`);
return [];
}
const result = [];
let buffer = '';
let i = 0;
while (i < str.length) {
const char = str[i];
if (char === '-') {
// Determine if this hyphen is part of the current combination
const prevChar = str[i - 1];
const prevPrevChar = i > 1 ? str[i - 2] : undefined;
const precededBySinglePlusOrUnderscore = (prevChar === '+' || prevChar === '_') && prevPrevChar !== '+';
if (precededBySinglePlusOrUnderscore) {
// Treat as part of the combination (e.g., 'ctrl+-')
buffer += char;
i++;
} else {
// Treat as sequence separator
if (buffer) {
result.push(buffer);
buffer = '';
} else {
// Empty buffer means we have a literal '-' key
result.push('-');
}
i++;
}
} else {
buffer += char;
i++;
}
}
// Add final buffer if it exists
if (buffer) {
result.push(buffer);
}
// Collapse runs of '-' so that every second '-' is removed
const collapsed = [];
let minusCount = 0;
for (const part of result) {
if (part === '-') {
if (minusCount % 2 === 0) collapsed.push('-');
minusCount++;
} else {
minusCount = 0;
collapsed.push(part);
}
}
// Validate that each part of the sequence is a valid combination
const areAllValid = collapsed.every(s => splitKeyCombination(s, true).length > 0);
if (!areAllValid) {
consoleWarn(`Invalid hotkey sequence: "${str}" contains invalid combinations`);
return [];
}
return collapsed;
}
//# sourceMappingURL=hotkey-parsing.js.map