index.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. import { keyName, base } from 'w3c-keyname';
  2. import { Plugin } from 'prosemirror-state';
  3. const mac = typeof navigator != "undefined" ? /Mac|iP(hone|[oa]d)/.test(navigator.platform) : false;
  4. function normalizeKeyName(name) {
  5. let parts = name.split(/-(?!$)/), result = parts[parts.length - 1];
  6. if (result == "Space")
  7. result = " ";
  8. let alt, ctrl, shift, meta;
  9. for (let i = 0; i < parts.length - 1; i++) {
  10. let mod = parts[i];
  11. if (/^(cmd|meta|m)$/i.test(mod))
  12. meta = true;
  13. else if (/^a(lt)?$/i.test(mod))
  14. alt = true;
  15. else if (/^(c|ctrl|control)$/i.test(mod))
  16. ctrl = true;
  17. else if (/^s(hift)?$/i.test(mod))
  18. shift = true;
  19. else if (/^mod$/i.test(mod)) {
  20. if (mac)
  21. meta = true;
  22. else
  23. ctrl = true;
  24. }
  25. else
  26. throw new Error("Unrecognized modifier name: " + mod);
  27. }
  28. if (alt)
  29. result = "Alt-" + result;
  30. if (ctrl)
  31. result = "Ctrl-" + result;
  32. if (meta)
  33. result = "Meta-" + result;
  34. if (shift)
  35. result = "Shift-" + result;
  36. return result;
  37. }
  38. function normalize(map) {
  39. let copy = Object.create(null);
  40. for (let prop in map)
  41. copy[normalizeKeyName(prop)] = map[prop];
  42. return copy;
  43. }
  44. function modifiers(name, event, shift = true) {
  45. if (event.altKey)
  46. name = "Alt-" + name;
  47. if (event.ctrlKey)
  48. name = "Ctrl-" + name;
  49. if (event.metaKey)
  50. name = "Meta-" + name;
  51. if (shift && event.shiftKey)
  52. name = "Shift-" + name;
  53. return name;
  54. }
  55. /**
  56. Create a keymap plugin for the given set of bindings.
  57. Bindings should map key names to [command](https://prosemirror.net/docs/ref/#commands)-style
  58. functions, which will be called with `(EditorState, dispatch,
  59. EditorView)` arguments, and should return true when they've handled
  60. the key. Note that the view argument isn't part of the command
  61. protocol, but can be used as an escape hatch if a binding needs to
  62. directly interact with the UI.
  63. Key names may be strings like `"Shift-Ctrl-Enter"`—a key
  64. identifier prefixed with zero or more modifiers. Key identifiers
  65. are based on the strings that can appear in
  66. [`KeyEvent.key`](https:developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key).
  67. Use lowercase letters to refer to letter keys (or uppercase letters
  68. if you want shift to be held). You may use `"Space"` as an alias
  69. for the `" "` name.
  70. Modifiers can be given in any order. `Shift-` (or `s-`), `Alt-` (or
  71. `a-`), `Ctrl-` (or `c-` or `Control-`) and `Cmd-` (or `m-` or
  72. `Meta-`) are recognized. For characters that are created by holding
  73. shift, the `Shift-` prefix is implied, and should not be added
  74. explicitly.
  75. You can use `Mod-` as a shorthand for `Cmd-` on Mac and `Ctrl-` on
  76. other platforms.
  77. You can add multiple keymap plugins to an editor. The order in
  78. which they appear determines their precedence (the ones early in
  79. the array get to dispatch first).
  80. */
  81. function keymap(bindings) {
  82. return new Plugin({ props: { handleKeyDown: keydownHandler(bindings) } });
  83. }
  84. /**
  85. Given a set of bindings (using the same format as
  86. [`keymap`](https://prosemirror.net/docs/ref/#keymap.keymap)), return a [keydown
  87. handler](https://prosemirror.net/docs/ref/#view.EditorProps.handleKeyDown) that handles them.
  88. */
  89. function keydownHandler(bindings) {
  90. let map = normalize(bindings);
  91. return function (view, event) {
  92. let name = keyName(event), baseName, direct = map[modifiers(name, event)];
  93. if (direct && direct(view.state, view.dispatch, view))
  94. return true;
  95. // A character key
  96. if (name.length == 1 && name != " ") {
  97. if (event.shiftKey) {
  98. // In case the name was already modified by shift, try looking
  99. // it up without its shift modifier
  100. let noShift = map[modifiers(name, event, false)];
  101. if (noShift && noShift(view.state, view.dispatch, view))
  102. return true;
  103. }
  104. if ((event.shiftKey || event.altKey || event.metaKey || name.charCodeAt(0) > 127) &&
  105. (baseName = base[event.keyCode]) && baseName != name) {
  106. // Try falling back to the keyCode when there's a modifier
  107. // active or the character produced isn't ASCII, and our table
  108. // produces a different name from the the keyCode. See #668,
  109. // #1060
  110. let fromCode = map[modifiers(baseName, event)];
  111. if (fromCode && fromCode(view.state, view.dispatch, view))
  112. return true;
  113. }
  114. }
  115. return false;
  116. };
  117. }
  118. export { keydownHandler, keymap };