mirror of
https://github.com/strapi/strapi.git
synced 2025-11-02 10:55:37 +00:00
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:
parent
7150c563ca
commit
db0360aa82
@ -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);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -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'),
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user