Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"ArrayLike": true,
"InputEvent": true,
"unknown": true,
"requestAnimationFrame": true
"requestAnimationFrame": true,
"navigator": true
}
}
5 changes: 5 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

### 2.29.1

- `Fix` — Toolbox wont be shown when Slash pressed with along with Shift or Alt
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Control or meta keys

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CMD+/ and CTRL+/ open Block Tunes — this behaviour stays the same.
Problem was in Shift+/ and Alt+/ — Toolbox won't be shown now, since "key" will be "?" or "÷" — we don't need to check Shift or Alt separately. Previously we checked keyCode which is the same for those chars.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, okay. Shift is a bit weird for me here, because in some layouts I can't type / without shift.

- `Fix` — Toolbox will be opened when Slash pressed in non-US keyboard layout where there is no physical '/' key.

### 2.29.0

- `New` — Editor Config now has the `style.nonce` attribute that could be used to allowlist editor style tag for Content Security Policy "style-src"
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@editorjs/editorjs",
"version": "2.29.0",
"version": "2.29.1",
"description": "Editor.js — Native JS, based on API and Open Source",
"main": "dist/editorjs.umd.js",
"module": "dist/editorjs.mjs",
Expand Down
25 changes: 18 additions & 7 deletions src/components/modules/blockEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,24 @@ export default class BlockEvents extends Module {
case _.keyCodes.TAB:
this.tabPressed(event);
break;
case _.keyCodes.SLASH:
if (event.ctrlKey || event.metaKey) {
this.commandSlashPressed();
} else {
this.slashPressed();
}
break;
}

/**
* We check for "key" here since on different keyboard layouts "/" can be typed as "Shift + 7" etc
*
* @todo probably using "beforeInput" event would be better here
*/
if (event.key === '/' && !event.ctrlKey && !event.metaKey) {
this.slashPressed();
}

/**
* If user pressed "Ctrl + /" or "Cmd + /" — open Block Settings
* We check for "code" here since on different keyboard layouts there can be different keys in place of Slash.
*/
if (event.code === 'Slash' && (event.ctrlKey || event.metaKey)) {
event.preventDefault();
this.commandSlashPressed();
}
}

Expand Down
8 changes: 5 additions & 3 deletions src/components/modules/toolbar/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import Toolbox, { ToolboxEvent } from '../../ui/toolbox';
import { IconMenu, IconPlus } from '@codexteam/icons';
import { BlockHovered } from '../../events/BlockHovered';
import { beautifyShortcut } from '../../utils';
import { getKeyboardKeyForCode } from '../../utils/keyboard';

/**
* @todo Tab on non-empty block should open Block Settings of the hoveredBlock (not where caret is set)
Expand Down Expand Up @@ -352,7 +353,7 @@ export default class Toolbar extends Module<ToolbarNodes> {
/**
* Draws Toolbar elements
*/
private make(): void {
private async make(): Promise<void> {
this.nodes.wrapper = $.make('div', this.CSS.toolbar);
/**
* @todo detect test environment and add data-cy="toolbar" to use it in tests instead of class name
Expand Down Expand Up @@ -414,10 +415,11 @@ export default class Toolbar extends Module<ToolbarNodes> {

const blockTunesTooltip = $.make('div');
const blockTunesTooltipEl = $.text(I18n.ui(I18nInternalNS.ui.blockTunes.toggler, 'Click to tune'));
const slashRealKey = await getKeyboardKeyForCode('Slash', '/');

blockTunesTooltip.appendChild(blockTunesTooltipEl);
blockTunesTooltip.appendChild($.make('div', this.CSS.plusButtonShortcut, {
textContent: beautifyShortcut('CMD + /'),
textContent: beautifyShortcut(`CMD + ${slashRealKey}`),
}));

tooltip.onHover(this.nodes.settingsToggler, blockTunesTooltip, {
Expand Down Expand Up @@ -585,7 +587,7 @@ export default class Toolbar extends Module<ToolbarNodes> {
/**
* Make Toolbar
*/
this.make();
void this.make();
}

/**
Expand Down
54 changes: 54 additions & 0 deletions src/components/utils/keyboard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
declare global {
/**
* https://developer.mozilla.org/en-US/docs/Web/API/KeyboardLayoutMap
*/
interface KeyboardLayoutMap {
get(key: string): string | undefined;
has(key: string): boolean;
size: number;
entries(): IterableIterator<[string, string]>;
keys(): IterableIterator<string>;
values(): IterableIterator<string>;
forEach(callbackfn: (value: string, key: string, map: KeyboardLayoutMap) => void, thisArg?: unknown): void;
}

/**
* The getLayoutMap() method of the Keyboard interface returns a Promise
* that resolves with an instance of KeyboardLayoutMap which is a map-like object
* with functions for retrieving the strings associated with specific physical keys.
* https://developer.mozilla.org/en-US/docs/Web/API/Keyboard/getLayoutMap
*/
interface Keyboard {
getLayoutMap(): Promise<KeyboardLayoutMap>;
}

interface Navigator {
/**
* Keyboard API. Not supported by Firefox and Safari.
*/
keyboard?: Keyboard;
}
}

/**
* Returns real layout-related keyboard key for a given key code.
* For example, for "Slash" it will return "/" on US keyboard and "-" on Spanish keyboard.
*
* Works with Keyboard API which is not supported by Firefox and Safari. So fallback is used for these browsers.
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/Keyboard
* @param code - {@link https://www.w3.org/TR/uievents-code/#key-alphanumeric-writing-system}
* @param fallback - fallback value to be returned if Keyboard API is not supported (Safari, Firefox)
*/
export async function getKeyboardKeyForCode(code: string, fallback: string): Promise<string> {
const keyboard = navigator.keyboard;

if (!keyboard) {
return fallback;
}

const map = await keyboard.getLayoutMap();
const key = map.get(code);

return key || fallback;
}
37 changes: 31 additions & 6 deletions test/cypress/tests/modules/BlockEvents/Slash.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,37 @@ describe('Slash keydown', function () {
.click()
.type('/');

cy.get('[data-cy="toolbox"]')
.get('.ce-popover')
cy.get('[data-cy="toolbox"] .ce-popover')
.should('be.visible');
});

[
'ctrl',
'cmd',
].forEach((key) => {
it(`should not open Toolbox if Slash pressed with ${key}`, () => {
cy.createEditor({
data: {
blocks: [
{
type: 'paragraph',
data: {
text: '',
},
},
],
},
});

cy.get('[data-cy=editorjs]')
.find('.ce-paragraph')
.click()
.type(`{${key}}/`);

cy.get('[data-cy="toolbox"] .ce-popover')
.should('not.be.visible');
});
});
});

describe('pressed in non-empty block', function () {
Expand All @@ -45,8 +72,7 @@ describe('Slash keydown', function () {
.click()
.type('/');

cy.get('[data-cy="toolbox"]')
.get('.ce-popover')
cy.get('[data-cy="toolbox"] .ce-popover')
.should('not.be.visible');

/**
Expand Down Expand Up @@ -80,8 +106,7 @@ describe('CMD+Slash keydown', function () {
.click()
.type('{cmd}/');

cy.get('[data-cy="block-tunes"]')
.get('.ce-popover')
cy.get('[data-cy="block-tunes"] .ce-popover')
.should('be.visible');
});
});
3 changes: 1 addition & 2 deletions test/cypress/tests/utils/flipper.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ class SomePlugin {

describe('Flipper', () => {
it('should prevent plugins event handlers from being called while keyboard navigation', () => {
const SLASH_KEY_CODE = 191;
const ARROW_DOWN_KEY_CODE = 40;
const ENTER_KEY_CODE = 13;

Expand Down Expand Up @@ -72,7 +71,7 @@ describe('Flipper', () => {
cy.get('[data-cy=editorjs]')
.get('.cdx-some-plugin')
// Open tunes menu
.trigger('keydown', { keyCode: SLASH_KEY_CODE, ctrlKey: true })
.trigger('keydown', { code: 'Slash', ctrlKey: true })
// Navigate to delete button (the second button)
.trigger('keydown', { keyCode: ARROW_DOWN_KEY_CODE })
.trigger('keydown', { keyCode: ARROW_DOWN_KEY_CODE });
Expand Down