feat(blocks): add keyboard shortcuts for modifiers (#18581)

* feat: added keyboard shortcuts for modifiers

* fix: eventKeys are added per modifier, updated url validation with URL()

* fix: replaced eventKey with isValidEventKey() in modifier store

* refactor: moved insertData to withLinks plugin
This commit is contained in:
Madhuri Sandbhor 2023-10-31 10:21:44 +05:30 committed by GitHub
parent 7150c563ca
commit db0360aa82
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 50 additions and 2 deletions

View File

@ -74,6 +74,21 @@ const BlocksInput = ({ disabled, placeholder }) => {
}
};
/**
* Modifier keyboard shortcuts
*/
const handleKeyboardShortcuts = (event) => {
const isCtrlOrCmd = event.metaKey || event.ctrlKey;
if (isCtrlOrCmd) {
Object.values(modifiers).forEach((value) => {
if (value.isValidEventKey(event)) {
value.handleToggle();
}
});
}
};
const handleKeyDown = (event) => {
if (event.key === 'Enter') {
event.preventDefault();
@ -82,6 +97,7 @@ const BlocksInput = ({ disabled, placeholder }) => {
if (event.key === 'Backspace') {
handleBackspaceEvent(event);
}
handleKeyboardShortcuts(event);
};
/**

View File

@ -44,6 +44,7 @@ const InlineCode = styled.code`
* @returns {{
* [key: string]: {
* icon: IconComponent,
* isValidEventKey: (event: Event) => boolean,
* label: {id: string, defaultMessage: string},
* checkIsActive: () => boolean,
* handleToggle: () => void,
@ -82,6 +83,7 @@ export function useModifiersStore() {
return {
bold: {
icon: Bold,
isValidEventKey: (event) => event.key === 'b',
label: { id: 'components.Blocks.modifiers.bold', defaultMessage: 'Bold' },
checkIsActive: () => baseCheckIsActive('bold'),
handleToggle: () => baseHandleToggle('bold'),
@ -89,6 +91,7 @@ export function useModifiersStore() {
},
italic: {
icon: Italic,
isValidEventKey: (event) => event.key === 'i',
label: { id: 'components.Blocks.modifiers.italic', defaultMessage: 'Italic' },
checkIsActive: () => baseCheckIsActive('italic'),
handleToggle: () => baseHandleToggle('italic'),
@ -96,6 +99,7 @@ export function useModifiersStore() {
},
underline: {
icon: Underline,
isValidEventKey: (event) => event.key === 'u',
label: { id: 'components.Blocks.modifiers.underline', defaultMessage: 'Underline' },
checkIsActive: () => baseCheckIsActive('underline'),
handleToggle: () => baseHandleToggle('underline'),
@ -103,6 +107,7 @@ export function useModifiersStore() {
},
strikethrough: {
icon: StrikeThrough,
isValidEventKey: (event) => event.key === 'S' && event.shiftKey,
label: { id: 'components.Blocks.modifiers.strikethrough', defaultMessage: 'Strikethrough' },
checkIsActive: () => baseCheckIsActive('strikethrough'),
handleToggle: () => baseHandleToggle('strikethrough'),
@ -110,6 +115,7 @@ export function useModifiersStore() {
},
code: {
icon: Code,
isValidEventKey: (event) => event.key === 'e',
label: { id: 'components.Blocks.modifiers.code', defaultMessage: 'Code' },
checkIsActive: () => baseCheckIsActive('code'),
handleToggle: () => baseHandleToggle('code'),

View File

@ -85,6 +85,11 @@ function useResetKey(value) {
return { key, incrementSlateUpdatesCount: () => (slateUpdatesCount.current += 1) };
}
const pipe =
(...fns) =>
(value) =>
fns.reduce((prev, fn) => fn(prev), value);
const BlocksEditor = React.forwardRef(
(
{ intlLabel, labelAction, name, disabled, required, error, value, onChange, placeholder, hint },
@ -92,7 +97,7 @@ const BlocksEditor = React.forwardRef(
) => {
const { formatMessage } = useIntl();
const [editor] = React.useState(() =>
withReact(withStrapiSchema(withLinks(withImages(withHistory(createEditor())))))
pipe(withHistory, withImages, withStrapiSchema, withReact, withLinks)(createEditor())
);
const label = intlLabel.id

View File

@ -1,7 +1,9 @@
import { Path, Transforms, Range, Point, Editor } from 'slate';
import { insertLink } from '../utils/links';
const withLinks = (editor) => {
const { isInline, apply, insertText } = editor;
const { isInline, apply, insertText, insertData } = editor;
// Links are inline elements, so we need to override the isInline method for slate
editor.isInline = (element) => {
@ -55,6 +57,25 @@ const withLinks = (editor) => {
insertText(text);
};
// Add data as a clickable link if its a valid URL
editor.insertData = (data) => {
const pastedText = data.getData('text/plain');
if (pastedText) {
try {
// eslint-disable-next-line no-new
new URL(pastedText);
insertLink(editor, { url: pastedText });
return;
} catch (error) {
// continue normal data insertion
}
}
insertData(data);
};
return editor;
};