anki/ts/lib/keys.ts

134 lines
3.9 KiB
TypeScript
Raw Normal View History

2021-06-29 17:15:07 +02:00
// Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
Move away from Bazel (#2202) (for upgrading users, please see the notes at the bottom) Bazel brought a lot of nice things to the table, such as rebuilds based on content changes instead of modification times, caching of build products, detection of incorrect build rules via a sandbox, and so on. Rewriting the build in Bazel was also an opportunity to improve on the Makefile-based build we had prior, which was pretty poor: most dependencies were external or not pinned, and the build graph was poorly defined and mostly serialized. It was not uncommon for fresh checkouts to fail due to floating dependencies, or for things to break when trying to switch to an older commit. For day-to-day development, I think Bazel served us reasonably well - we could generally switch between branches while being confident that builds would be correct and reasonably fast, and not require full rebuilds (except on Windows, where the lack of a sandbox and the TS rules would cause build breakages when TS files were renamed/removed). Bazel achieves that reliability by defining rules for each programming language that define how source files should be turned into outputs. For the rules to work with Bazel's sandboxing approach, they often have to reimplement or partially bypass the standard tools that each programming language provides. The Rust rules call Rust's compiler directly for example, instead of using Cargo, and the Python rules extract each PyPi package into a separate folder that gets added to sys.path. These separate language rules allow proper declaration of inputs and outputs, and offer some advantages such as caching of build products and fine-grained dependency installation. But they also bring some downsides: - The rules don't always support use-cases/platforms that the standard language tools do, meaning they need to be patched to be used. I've had to contribute a number of patches to the Rust, Python and JS rules to unblock various issues. - The dependencies we use with each language sometimes make assumptions that do not hold in Bazel, meaning they either need to be pinned or patched, or the language rules need to be adjusted to accommodate them. I was hopeful that after the initial setup work, things would be relatively smooth-sailing. Unfortunately, that has not proved to be the case. Things frequently broke when dependencies or the language rules were updated, and I began to get frustrated at the amount of Anki development time I was instead spending on build system upkeep. It's now about 2 years since switching to Bazel, and I think it's time to cut losses, and switch to something else that's a better fit. The new build system is based on a small build tool called Ninja, and some custom Rust code in build/. This means that to build Anki, Bazel is no longer required, but Ninja and Rust need to be installed on your system. Python and Node toolchains are automatically downloaded like in Bazel. This new build system should result in faster builds in some cases: - Because we're using cargo to build now, Rust builds are able to take advantage of pipelining and incremental debug builds, which we didn't have with Bazel. It's also easier to override the default linker on Linux/macOS, which can further improve speeds. - External Rust crates are now built with opt=1, which improves performance of debug builds. - Esbuild is now used to transpile TypeScript, instead of invoking the TypeScript compiler. This results in faster builds, by deferring typechecking to test/check time, and by allowing more work to happen in parallel. As an example of the differences, when testing with the mold linker on Linux, adding a new message to tags.proto (which triggers a recompile of the bulk of the Rust and TypeScript code) results in a compile that goes from about 22s on Bazel to about 7s in the new system. With the standard linker, it's about 9s. Some other changes of note: - Our Rust workspace now uses cargo-hakari to ensure all packages agree on available features, preventing unnecessary rebuilds. - pylib/anki is now a PEP420 implicit namespace, avoiding the need to merge source files and generated files into a single folder for running. By telling VSCode about the extra search path, code completion now works with generated files without needing to symlink them into the source folder. - qt/aqt can't use PEP420 as it's difficult to get rid of aqt/__init__.py. Instead, the generated files are now placed in a separate _aqt package that's added to the path. - ts/lib is now exposed as @tslib, so the source code and generated code can be provided under the same namespace without a merging step. - MyPy and PyLint are now invoked once for the entire codebase. - dprint will be used to format TypeScript/json files in the future instead of the slower prettier (currently turned off to avoid causing conflicts). It can automatically defer to prettier when formatting Svelte files. - svelte-check is now used for typechecking our Svelte code, which revealed a few typing issues that went undetected with the old system. - The Jest unit tests now work on Windows as well. If you're upgrading from Bazel, updated usage instructions are in docs/development.md and docs/build.md. A summary of the changes: - please remove node_modules and .bazel - install rustup (https://rustup.rs/) - install rsync if not already installed (on windows, use pacman - see docs/windows.md) - install Ninja (unzip from https://github.com/ninja-build/ninja/releases/tag/v1.11.1 and place on your path, or from your distro/homebrew if it's 1.10+) - update .vscode/settings.json from .vscode.dist
2022-11-27 06:24:20 +01:00
import * as tr from "@tslib/ftl";
2021-06-29 17:15:07 +02:00
import { isApplePlatform } from "./platform";
// those are the modifiers that Anki works with
2021-06-29 17:15:07 +02:00
export type Modifier = "Control" | "Alt" | "Shift" | "Meta";
const allModifiers: Modifier[] = ["Control", "Alt", "Shift", "Meta"];
const platformModifiers: string[] = isApplePlatform()
2021-06-29 17:15:07 +02:00
? ["Meta", "Alt", "Shift", "Control"]
: ["Control", "Alt", "Shift", "OS"];
function translateModifierToPlatform(modifier: Modifier): string {
return platformModifiers[allModifiers.indexOf(modifier)];
}
2021-06-29 17:15:07 +02:00
export function checkIfModifierKey(event: KeyboardEvent): boolean {
// At least the web view on Desktop Anki gives out the wrong values for
// `event.location`, which is why we do it like this.
let isInputKey = false;
Reverse-engineer surrounding with execCommand (#1377) * Add utility functions for saving and restoring the caret location Implement surroundNoSplitting Clarify surroundNoSplitting comments Start implementing surroundSplitting and triggerIfSimpleInput Fix after rebase Implement findBefore / findAfter in lib/surround * to merge adjacent nodes into the surrounding nodes Use new prettier settings with lib/{location,surround} Fix imports that I missed to rename Add some tests for find-adjacent Split find-within from find-adjacent Normalize nodes after insertion in surroundNoSplitting Do not deep clone surroundNode -> no intention of supporting deep nodes, as normalization would be impossible Add some tests concerning nested surrounding nested nodes Select surroundedRange after surrounding Fix ascendWhileSingleInline A flawed first surround/trigger implementation Move trigger out of lib/surround Implement Input Manager as a way to handle bold on empty selection Switch bold button away from execCommand Pass in Matcher instead of selector to find-adjacent and surroundNoSplitting * Also adds a failing test for no-splitting Refactor find-adjacent * add failing test when findBefore's nodes have different amounts of child nodes Change type signature of find-adjacent methods to more single-concern Add test for surrounding where adjacent block becomes three Text elements Make nodes found within surrounded range extend the ranges endOffset Add base parameter to surroundNoSplitting to stop ascending beyond container Stop surrounding from bubbling beyond base in merge-match Make all tests pass Add some failing tests to point to future development Add empty elements as constant Implement a broken version of unsurround Even split text if it creates zero-length texts -> they are still valid, despite what Chromium says Rename {start,end} to {start,end}Container Add more unit tests with surround after a nested element Set endOffset after split-off possibly zero length text nodes Deal with empty elements when surrounding Only include split off end text if zero length Use range anchors instead off calcluating surroundedRange from offsets * this approach allows for removal of base elements when unsurrounding Comment out test which fail because of jsdom bugs We'll be able to enable them again after Jest 28 Make the first unsurround tests pass Add new failing test for unsurround text within tag Fix unsurround Test is deactivated until Jest 28 Rewrite input-manager and trigger callback after insertion Avoid creating zero length text nodes by using insertBefore when appropriate Implement matches vs keepMatches Make shadow root and editable element available on component tree Make WithState work with asynchronous updater functions Add new Bold/Italic/UnderlineButton using our logic Add failing test for unsurrounding * Move surround/ to domlib * Add jest dependency * Make find-within return a sum type array rather than two arrays * Use FoundMatch sum-type for find-above (and find-within) * Fix issue where elements could be cleared twice * if they are IN the range.endContainer * Pass remaining test * Add another failing test * Fix empty text nodes being considered for surrounding * Satisfy svelte check * Make on more type correct * Satisfy remaining tests * Add missing copyright header
2021-11-18 10:18:39 +01:00
for (const modifier of allModifiers) {
isInputKey ||= event.code.startsWith(modifier);
}
return isInputKey;
Reverse-engineer surrounding with execCommand (#1377) * Add utility functions for saving and restoring the caret location Implement surroundNoSplitting Clarify surroundNoSplitting comments Start implementing surroundSplitting and triggerIfSimpleInput Fix after rebase Implement findBefore / findAfter in lib/surround * to merge adjacent nodes into the surrounding nodes Use new prettier settings with lib/{location,surround} Fix imports that I missed to rename Add some tests for find-adjacent Split find-within from find-adjacent Normalize nodes after insertion in surroundNoSplitting Do not deep clone surroundNode -> no intention of supporting deep nodes, as normalization would be impossible Add some tests concerning nested surrounding nested nodes Select surroundedRange after surrounding Fix ascendWhileSingleInline A flawed first surround/trigger implementation Move trigger out of lib/surround Implement Input Manager as a way to handle bold on empty selection Switch bold button away from execCommand Pass in Matcher instead of selector to find-adjacent and surroundNoSplitting * Also adds a failing test for no-splitting Refactor find-adjacent * add failing test when findBefore's nodes have different amounts of child nodes Change type signature of find-adjacent methods to more single-concern Add test for surrounding where adjacent block becomes three Text elements Make nodes found within surrounded range extend the ranges endOffset Add base parameter to surroundNoSplitting to stop ascending beyond container Stop surrounding from bubbling beyond base in merge-match Make all tests pass Add some failing tests to point to future development Add empty elements as constant Implement a broken version of unsurround Even split text if it creates zero-length texts -> they are still valid, despite what Chromium says Rename {start,end} to {start,end}Container Add more unit tests with surround after a nested element Set endOffset after split-off possibly zero length text nodes Deal with empty elements when surrounding Only include split off end text if zero length Use range anchors instead off calcluating surroundedRange from offsets * this approach allows for removal of base elements when unsurrounding Comment out test which fail because of jsdom bugs We'll be able to enable them again after Jest 28 Make the first unsurround tests pass Add new failing test for unsurround text within tag Fix unsurround Test is deactivated until Jest 28 Rewrite input-manager and trigger callback after insertion Avoid creating zero length text nodes by using insertBefore when appropriate Implement matches vs keepMatches Make shadow root and editable element available on component tree Make WithState work with asynchronous updater functions Add new Bold/Italic/UnderlineButton using our logic Add failing test for unsurrounding * Move surround/ to domlib * Add jest dependency * Make find-within return a sum type array rather than two arrays * Use FoundMatch sum-type for find-above (and find-within) * Fix issue where elements could be cleared twice * if they are IN the range.endContainer * Pass remaining test * Add another failing test * Fix empty text nodes being considered for surrounding * Satisfy svelte check * Make on more type correct * Satisfy remaining tests * Add missing copyright header
2021-11-18 10:18:39 +01:00
}
Fix some Mathjax issues (#1547) * Move move-nodes logic into domlib Refactor input-manager Refactor out FocusTrap from EditingArea Remove unnecessary selecting of node from surround Add onInput interface to input-manager Create MathjaxElement.svelte - This should contain all the setup necessary for displaying <anki-mathjax> elements in the rich text input - Does not contain setup necessary for Mathjax Overlay Deal with backwards deletion, when caret inside anki-mathjax Set mathjax elements contenteditable=false Do not undecorate mathjaxx element on disconnect - Fixes issues, where Mathjax might undecorate when it is moved into a different div Add framed element custom element Introduce iterateActions to allow global hooks for RichTextInput Remove some old code Deal with deletion of frame handles Make Anki frame and frame handles restore each other Make FrameElement restore its structure upon modification Frame and strip off framing from MathjaxElement automatically Move FrameHandle to separate FrameStart/FrameEnd Refactor FrameHandle Set data-frames on FrameElement Fix logic error connected to FrameElement Communicate frameHandle move{in,out} to anki-mathjax Clear selection when blurring ContentEditable Make sure frame is destroyed when undecorating Mathjax Use Hairline space instead of zeroWidth - it has better behavior in the contenteditable when placing the caret via clicking Allow detection of block elements with `block` attribute - This way, anki-mathjax block="true" will make field a field be recognized to have block content Make block frame element operater without handles - Clicking on the left/right side of a block mathjax will insert a br element to that side When deleting, remove mathajax-element not just image Update MathjaxButtons to correctly show block state SelectAll when moving into inline anki mathjax Remove CodeMirror autofocus functionality Move it to Mathjaxeditor directly Fix getRangeAt throwing error Update older code to use cross-browser Fix issue with FrameHandles not being properyly removed Satisfy formatting Use === instead of node.isSameNode() Fix issue of focusTrap not initialized * Fix after rebasing * Fix focus not being moved to first field * Add documentation for input-manager and iterate-actions * Export logic of ContentEditable to content-editable * Fix issue with inserting newline right next to inline Mathjax * Fix reframing issue of Mathjax Svelte component * Allow for deletion of Inline Mathjax again * Rename iterate-actions to action-list * Add copyright header * Split off frame-handle from frame-element * Add some comments for framing process * Add mising return types
2022-01-08 02:46:01 +01:00
export function keyboardEventIsPrintableKey(event: KeyboardEvent): boolean {
return event.key.length === 1;
}
Move away from Bazel (#2202) (for upgrading users, please see the notes at the bottom) Bazel brought a lot of nice things to the table, such as rebuilds based on content changes instead of modification times, caching of build products, detection of incorrect build rules via a sandbox, and so on. Rewriting the build in Bazel was also an opportunity to improve on the Makefile-based build we had prior, which was pretty poor: most dependencies were external or not pinned, and the build graph was poorly defined and mostly serialized. It was not uncommon for fresh checkouts to fail due to floating dependencies, or for things to break when trying to switch to an older commit. For day-to-day development, I think Bazel served us reasonably well - we could generally switch between branches while being confident that builds would be correct and reasonably fast, and not require full rebuilds (except on Windows, where the lack of a sandbox and the TS rules would cause build breakages when TS files were renamed/removed). Bazel achieves that reliability by defining rules for each programming language that define how source files should be turned into outputs. For the rules to work with Bazel's sandboxing approach, they often have to reimplement or partially bypass the standard tools that each programming language provides. The Rust rules call Rust's compiler directly for example, instead of using Cargo, and the Python rules extract each PyPi package into a separate folder that gets added to sys.path. These separate language rules allow proper declaration of inputs and outputs, and offer some advantages such as caching of build products and fine-grained dependency installation. But they also bring some downsides: - The rules don't always support use-cases/platforms that the standard language tools do, meaning they need to be patched to be used. I've had to contribute a number of patches to the Rust, Python and JS rules to unblock various issues. - The dependencies we use with each language sometimes make assumptions that do not hold in Bazel, meaning they either need to be pinned or patched, or the language rules need to be adjusted to accommodate them. I was hopeful that after the initial setup work, things would be relatively smooth-sailing. Unfortunately, that has not proved to be the case. Things frequently broke when dependencies or the language rules were updated, and I began to get frustrated at the amount of Anki development time I was instead spending on build system upkeep. It's now about 2 years since switching to Bazel, and I think it's time to cut losses, and switch to something else that's a better fit. The new build system is based on a small build tool called Ninja, and some custom Rust code in build/. This means that to build Anki, Bazel is no longer required, but Ninja and Rust need to be installed on your system. Python and Node toolchains are automatically downloaded like in Bazel. This new build system should result in faster builds in some cases: - Because we're using cargo to build now, Rust builds are able to take advantage of pipelining and incremental debug builds, which we didn't have with Bazel. It's also easier to override the default linker on Linux/macOS, which can further improve speeds. - External Rust crates are now built with opt=1, which improves performance of debug builds. - Esbuild is now used to transpile TypeScript, instead of invoking the TypeScript compiler. This results in faster builds, by deferring typechecking to test/check time, and by allowing more work to happen in parallel. As an example of the differences, when testing with the mold linker on Linux, adding a new message to tags.proto (which triggers a recompile of the bulk of the Rust and TypeScript code) results in a compile that goes from about 22s on Bazel to about 7s in the new system. With the standard linker, it's about 9s. Some other changes of note: - Our Rust workspace now uses cargo-hakari to ensure all packages agree on available features, preventing unnecessary rebuilds. - pylib/anki is now a PEP420 implicit namespace, avoiding the need to merge source files and generated files into a single folder for running. By telling VSCode about the extra search path, code completion now works with generated files without needing to symlink them into the source folder. - qt/aqt can't use PEP420 as it's difficult to get rid of aqt/__init__.py. Instead, the generated files are now placed in a separate _aqt package that's added to the path. - ts/lib is now exposed as @tslib, so the source code and generated code can be provided under the same namespace without a merging step. - MyPy and PyLint are now invoked once for the entire codebase. - dprint will be used to format TypeScript/json files in the future instead of the slower prettier (currently turned off to avoid causing conflicts). It can automatically defer to prettier when formatting Svelte files. - svelte-check is now used for typechecking our Svelte code, which revealed a few typing issues that went undetected with the old system. - The Jest unit tests now work on Windows as well. If you're upgrading from Bazel, updated usage instructions are in docs/development.md and docs/build.md. A summary of the changes: - please remove node_modules and .bazel - install rustup (https://rustup.rs/) - install rsync if not already installed (on windows, use pacman - see docs/windows.md) - install Ninja (unzip from https://github.com/ninja-build/ninja/releases/tag/v1.11.1 and place on your path, or from your distro/homebrew if it's 1.10+) - update .vscode/settings.json from .vscode.dist
2022-11-27 06:24:20 +01:00
export const checkModifiers = (required: Modifier[], optional: Modifier[] = []) => (event: KeyboardEvent): boolean => {
return allModifiers.reduce(
(
matches: boolean,
currentModifier: Modifier,
currentIndex: number,
): boolean =>
matches
&& (optional.includes(currentModifier as Modifier)
|| event.getModifierState(platformModifiers[currentIndex])
=== required.includes(currentModifier)),
true,
);
};
const modifierPressed = (modifier: Modifier) => (event: MouseEvent | KeyboardEvent): boolean => {
const translated = translateModifierToPlatform(modifier);
const state = event.getModifierState(translated);
return event.type === "keyup"
? state && (event as KeyboardEvent).key !== translated
: state;
};
2021-06-29 17:15:07 +02:00
export const controlPressed = modifierPressed("Control");
export const shiftPressed = modifierPressed("Shift");
Move all buttons to our custom inline surrounding (#1682) * Clarify some comments * Don't destructure insertion trigger * Make superscript and subscript use domlib/surround * Create new {Text,Highlight}ColorButton * Use domlib/surround for textcolor - However there's still a crucial bug, when you're breaking existing colored span when unsurrounding, their color is not restored * Add underline format to removeFormats * Simplify type of ElementMatcher and ElementClearer for end users * Add some comments for normalize-insertion-ranges * Split normalize-insertion-ranges into remove-adjacent and remove-within * Factor out find-remove from unsurround.ts * Rename merge-mach, simplify remove-within * Clarify some comments * Refactor first reduce * Refactor reduceRight * Flatten functions in merge-ranges * Move some functionality to merge-ranges and do not export * Refactor merge-ranges * Remove createInitialMergeMatch * Finish refactoring of merge-ranges * Refactor merge-ranges to minimal-ranges and add some unit testing * Move more logic into text-node * Remove most most of the logic from remove-adjacent - remove-adjacent is still part of the "merging" logic, as it increases the scope of the child node ranges * Add some tests for edge cases * Merge remove-adjacent logic into minimal-ranges * Refactor unnecessary list destructuring * Add some TODOs * Put removing nodes and adding new nodes into sequence * Refactor MatchResult to MatchType and return clear from matcher * Inline surround/helpers * Shorten name of param * Add another edge case test * Add an example where commonAncestorContainer != normalization level * Fix bug in find-adjacent when find more than one nested nodes * Allow comments for Along type * Simplify find-adjacent by removing intermediate and/or curried functions * Remove extend-adjacent * Add more tests when find-adjacent finds by descension * Fix find-adjacent descending into block-level elements * Add clarifying comment to refusing to descend into block-level elements * Move shifting logic into find-adjacent * Rename file matcher to match-type * Give a first implemention of TreeVertex * Remove MatchType.ALONG - findAdjacent now directly modifies the range * Rename MatchType.MATCH into MatchType.REMOVE * Implement a version of find-within that utilizies match-tree * Turn child node range into a class * Fix bug in new find-adjacent function * Make all find-adjacent tests test for ranges * Surrounding within farthestMatchingAncestor when available * Fix an issue with negligable elements - also rename "along" elements to "negligable" * Add two TODOs to SurroundFormat interface * Have a messy first implementation of the new tree-node algorithm * Maintain whether formatting nodes are covered or within user selection * Move covered and insideRange into TreeNode superclass * Reimplement findAdjacent logic * Add extension logic * Add an evaluate method to nodes * Introduce BlockNode * Add a first evaluate implementation * Add left shift and inner shift logic * Implement SurroundFormatUser * Allow pass in formatter, ascender and merger from outside * Fix insideRange and covered switch-up * Fix MatchNode.prototype.isAscendable * Fix another switch-up of covered and insideRange... * Remove a lot of old code * Have surround functions only return the range - I still cannot think of a good reason why we should return addedNodes and removedNodes, except for testing. * Create formatting-tree directory * Create build-tree directory + Move find-above up to /domlib * Remove range-anchors * Move unsurround logic into no-splitting * Fix extend-merge * Fix inner shift being eroneusly returned as left shift * Fix oversight in SplitRange * Redefine how ranges are recreated * Rename covered to insideMatch and put as fourth parameter instead of third * Keep track of match holes and match leaves * Rename ChildNodeRange to FlatRange * Change signature of matcher * Fix bug in extend-merge * Improve Match class * Utilize cache in TextColorButton * Implement getBaseSurrounder for TextColorButton * Add matchAncestors field to FormattingNode * Introduce matchAncestors and getCache * Do clearing during parsing already - This way, you know whether elements will be removed before getting to Formatting nodes * Make HighlightColorButton use our surround mechanism * Fix a bug with calling .removeAttribute and .hasAttribute * Add side button to RemoveFormat button * Add disabled to remove format side button * Expose remove formats on RemoveFormat button * Reinvent editor/surround as Surrounder class * Fix split-text when working with insert trigger * Try counteracting the contenteditable's auto surrounding * Remove matching elements before normalizing * Rewrite match-type * Move setting match leaves into build * Change editing strings - So that color strings match bold/italic strings better * Fix border radius of List options menu * Implement extensions functionality * Remove some unnecessary code * Fix split range endOffset * Type MatchType * Reformat MatchType + add docs * Fix domlib/surround/apply * Satisfy last tests * Register Surrounder as package * Clarify some comments * Correctly implement reformat * Reformat with inactive eraser formats * Clear empty spans with RemoveFormatButton * Fix Super/Subscript button * Use ftl string for hardcoded tooltip * Adjust wording
2022-02-22 13:17:22 +01:00
export const altPressed = modifierPressed("Alt");
Insert symbols overlay (#2051) * Add flag for enabling insert symbols feature * Add symbols overlay directory * Detect if :xy is inserted into editable * Allow naive updating of overlay, and special handling of ':' * First step towards better Virtual Element support * Update floating to reference range on insert text * Position SymbolsOverlay always on top or bottom * Add a data-provider to emulate API * Show correct suggestions in symbols overlay * Rename to replacementLength * Allow replacing via clicking in menu * Optionally remove inline padding of Popover * Hide Symbols overlay on blur of content editable * Add specialKey to inputHandler and generalize how arrow movement is detected - This way macOS users can use Ctrl-N to mean down, etc. * Detect special key from within SymbolsOverlay * Implement full backwards search while typing * Allow navigating symbol menu and accepting with enter * Add some entries to data-provider * Satisfy eslint * Generate symbolsTable from sources * Use other github source, allow multiple names In return, symbol must be unique * Automatically scroll in symbols dropdown * Use from npm packages rather than downloading from URL * Remove console.log * Remove print * Add pointerDown event to input-handler - so that SymbolsOverlay can reset on field click * Make tab do the same as enter * Make font a bit smaller but increase relative icon size * Satisfy type requirement of handlerlist * Revert changing default size of DropdownItems * Remove some now unused code for bootstrap dropdowns
2022-09-10 10:46:59 +02:00
export const metaPressed = modifierPressed("Meta");
2021-06-29 17:15:07 +02:00
export function modifiersToPlatformString(modifiers: string[]): string {
const displayModifiers = isApplePlatform()
? ["^", "⌥", "⇧", "⌘"]
: [`${tr.keyboardCtrl()}+`, "Alt+", `${tr.keyboardShift()}+`, "Win+"];
let result = "";
for (const modifier of modifiers) {
result += displayModifiers[platformModifiers.indexOf(modifier)];
}
return result;
}
export function keyToPlatformString(key: string): string {
switch (key) {
case "Backspace":
return "⌫";
case "Delete":
return "⌦";
case "Escape":
return "⎋";
default:
return key;
}
}
Insert symbols overlay (#2051) * Add flag for enabling insert symbols feature * Add symbols overlay directory * Detect if :xy is inserted into editable * Allow naive updating of overlay, and special handling of ':' * First step towards better Virtual Element support * Update floating to reference range on insert text * Position SymbolsOverlay always on top or bottom * Add a data-provider to emulate API * Show correct suggestions in symbols overlay * Rename to replacementLength * Allow replacing via clicking in menu * Optionally remove inline padding of Popover * Hide Symbols overlay on blur of content editable * Add specialKey to inputHandler and generalize how arrow movement is detected - This way macOS users can use Ctrl-N to mean down, etc. * Detect special key from within SymbolsOverlay * Implement full backwards search while typing * Allow navigating symbol menu and accepting with enter * Add some entries to data-provider * Satisfy eslint * Generate symbolsTable from sources * Use other github source, allow multiple names In return, symbol must be unique * Automatically scroll in symbols dropdown * Use from npm packages rather than downloading from URL * Remove console.log * Remove print * Add pointerDown event to input-handler - so that SymbolsOverlay can reset on field click * Make tab do the same as enter * Make font a bit smaller but increase relative icon size * Satisfy type requirement of handlerlist * Revert changing default size of DropdownItems * Remove some now unused code for bootstrap dropdowns
2022-09-10 10:46:59 +02:00
export function isArrowLeft(event: KeyboardEvent): boolean {
if (event.code === "ArrowLeft") {
return true;
}
return isApplePlatform() && metaPressed(event) && event.code === "KeyB";
}
export function isArrowRight(event: KeyboardEvent): boolean {
if (event.code === "ArrowRight") {
return true;
}
return isApplePlatform() && metaPressed(event) && event.code === "KeyF";
}
export function isArrowUp(event: KeyboardEvent): boolean {
if (event.code === "ArrowUp") {
return true;
}
return isApplePlatform() && metaPressed(event) && event.code === "KeyP";
}
export function isArrowDown(event: KeyboardEvent): boolean {
if (event.code === "ArrowDown") {
return true;
}
return isApplePlatform() && metaPressed(event) && event.code === "KeyN";
}
export function onEnterOrSpace(callback: () => void): (event: KeyboardEvent) => void {
return (event: KeyboardEvent) => {
switch (event.code) {
case "Enter":
case "Space":
callback();
break;
}
};
}