Skip to content
代码片段 群组 项目
提交 47df1f88 编辑于 作者: Phil Hughes's avatar Phil Hughes
浏览文件

Merge branch '373902-attach-indent-outdent-keys-to-toolbar-buttons' into 'master'

[markdown] attach indent/outdent keys to toolbar buttons for cross platform support

See merge request https://gitlab.com/gitlab-org/gitlab/-/merge_requests/98111



Merged-by: default avatarPhil Hughes <me@iamphill.com>
Approved-by: default avatarRoy Zwambag <rzwambag@gitlab.com>
Approved-by: default avatarEduardo Sanz García <esanz-garcia@gitlab.com>
Approved-by: default avatarPhil Hughes <me@iamphill.com>
Co-authored-by: default avatarBrett Walker <bwalker@gitlab.com>
No related branches found
No related tags found
无相关合并请求
......@@ -145,6 +145,20 @@ export const LINK_TEXT = {
customizable: false,
};
export const INDENT_LINE = {
id: 'editing.indentLine',
description: __('Indent line'),
defaultKeys: ['mod+]'], // eslint-disable-line @gitlab/require-i18n-strings
customizable: false,
};
export const OUTDENT_LINE = {
id: 'editing.outdentLine',
description: __('Outdent line'),
defaultKeys: ['mod+['], // eslint-disable-line @gitlab/require-i18n-strings
customizable: false,
};
export const TOGGLE_MARKDOWN_PREVIEW = {
id: 'editing.toggleMarkdownPreview',
description: __('Toggle Markdown preview'),
......
......@@ -391,13 +391,15 @@ function updateText({ textArea, tag, cursorOffset, blockTag, wrap, select, tagCo
/**
* Indents selected lines to the right by 2 spaces
*
* @param {Object} textArea - the targeted text area
* @param {Object} textArea - jQuery object with the targeted text area
*/
function indentLines(textArea) {
function indentLines($textArea) {
const textArea = $textArea.get(0);
const { lines, selectionStart, selectionEnd, startPos, endPos } = linesFromSelection(textArea);
const shiftedLines = [];
let totalAdded = 0;
textArea.focus();
textArea.setSelectionRange(startPos, endPos);
lines.forEach((line) => {
......@@ -418,13 +420,15 @@ function indentLines(textArea) {
*
* @param {Object} textArea - the targeted text area
*/
function outdentLines(textArea) {
function outdentLines($textArea) {
const textArea = $textArea.get(0);
const { lines, selectionStart, selectionEnd, startPos, endPos } = linesFromSelection(textArea);
const shiftedLines = [];
let totalRemoved = 0;
let removedFromFirstline = -1;
let removedFromLine = 0;
textArea.focus();
textArea.setSelectionRange(startPos, endPos);
lines.forEach((line) => {
......@@ -460,28 +464,10 @@ function outdentLines(textArea) {
);
}
function handleIndentOutdent(e, textArea) {
if (e.altKey || e.ctrlKey || e.shiftKey) return;
if (!e.metaKey) return;
switch (e.key) {
case ']':
e.preventDefault();
indentLines(textArea);
break;
case '[':
e.preventDefault();
outdentLines(textArea);
break;
default:
break;
}
}
/* eslint-disable @gitlab/require-i18n-strings */
function handleSurroundSelectedText(e, textArea) {
if (!gon.markdown_surround_selection) return;
if (e.metaKey) return;
if (e.metaKey || e.ctrlKey) return;
if (textArea.selectionStart === textArea.selectionEnd) return;
const keys = {
......@@ -586,7 +572,6 @@ export function keypressNoteText(e) {
if ($(textArea).atwho?.('isSelecting')) return;
handleIndentOutdent(e, textArea);
handleContinueList(e, textArea);
handleSurroundSelectedText(e, textArea);
}
......@@ -600,15 +585,26 @@ export function compositionEndNoteText() {
}
export function updateTextForToolbarBtn($toolbarBtn) {
return updateText({
textArea: $toolbarBtn.closest('.md-area').find('textarea'),
tag: $toolbarBtn.data('mdTag'),
cursorOffset: $toolbarBtn.data('mdCursorOffset'),
blockTag: $toolbarBtn.data('mdBlock'),
wrap: !$toolbarBtn.data('mdPrepend'),
select: $toolbarBtn.data('mdSelect'),
tagContent: $toolbarBtn.attr('data-md-tag-content'),
});
const $textArea = $toolbarBtn.closest('.md-area').find('textarea');
switch ($toolbarBtn.data('mdCommand')) {
case 'indentLines':
indentLines($textArea);
break;
case 'outdentLines':
outdentLines($textArea);
break;
default:
return updateText({
textArea: $textArea,
tag: $toolbarBtn.data('mdTag'),
cursorOffset: $toolbarBtn.data('mdCursorOffset'),
blockTag: $toolbarBtn.data('mdBlock'),
wrap: !$toolbarBtn.data('mdPrepend'),
select: $toolbarBtn.data('mdSelect'),
tagContent: $toolbarBtn.attr('data-md-tag-content'),
});
}
}
export function addMarkdownListeners(form) {
......
......@@ -7,6 +7,8 @@ import {
ITALIC_TEXT,
STRIKETHROUGH_TEXT,
LINK_TEXT,
INDENT_LINE,
OUTDENT_LINE,
} from '~/behaviors/shortcuts/keybindings';
import { getSelectedFragment } from '~/lib/utils/common_utils';
import { s__, __ } from '~/locale';
......@@ -170,6 +172,8 @@ export default {
italic: keysFor(ITALIC_TEXT),
strikethrough: keysFor(STRIKETHROUGH_TEXT),
link: keysFor(LINK_TEXT),
indent: keysFor(INDENT_LINE),
outdent: keysFor(OUTDENT_LINE),
},
i18n: {
writeTabTitle: __('Write'),
......@@ -317,6 +321,32 @@ export default {
:button-title="__('Add a checklist')"
icon="list-task"
/>
<toolbar-button
v-if="!restrictedToolBarItems.includes('indent')"
class="gl-display-none"
:button-title="
/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */
sprintf(s__('MarkdownEditor|Indent line (%{modifierKey}])'), {
modifierKey /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */,
})
"
:shortcuts="$options.shortcuts.indent"
command="indentLines"
icon="list-indent"
/>
<toolbar-button
v-if="!restrictedToolBarItems.includes('outdent')"
class="gl-display-none"
:button-title="
/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */
sprintf(s__('MarkdownEditor|Outdent line (%{modifierKey}[)'), {
modifierKey /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */,
})
"
:shortcuts="$options.shortcuts.outdent"
command="outdentLines"
icon="list-outdent"
/>
<toolbar-button
v-if="!restrictedToolBarItems.includes('collapsible-section')"
:tag="mdCollapsibleSection"
......
......@@ -47,6 +47,11 @@ export default {
required: false,
default: 0,
},
command: {
type: String,
required: false,
default: '',
},
/**
* A string (or an array of strings) of
......@@ -81,6 +86,7 @@ export default {
:data-md-tag-content="tagContent"
:data-md-prepend="prepend"
:data-md-shortcuts="shortcutsString"
:data-md-command="command"
:title="buttonTitle"
:aria-label="buttonTitle"
:icon="icon"
......
......@@ -24,6 +24,14 @@
= markdown_toolbar_button({ icon: "list-bulleted", data: { "md-tag" => "- ", "md-prepend" => true }, title: _("Add a bullet list") })
= markdown_toolbar_button({ icon: "list-numbered", data: { "md-tag" => "1. ", "md-prepend" => true }, title: _("Add a numbered list") })
= markdown_toolbar_button({ icon: "list-task", data: { "md-tag" => "- [ ] ", "md-prepend" => true }, title: _("Add a checklist") })
= markdown_toolbar_button({ icon: "list-indent",
data: { "md-command" => 'indentLines', "md-shortcuts": '["mod+]"]' },
css_class: 'gl-display-none',
title: sprintf(s_("MarkdownEditor|Indent line (%{modifier_key}])") % { modifier_key: modifier_key }) })
= markdown_toolbar_button({ icon: "list-outdent",
data: { "md-command" => 'outdentLines', "md-shortcuts": '["mod+["]' },
css_class: 'gl-display-none',
title: sprintf(s_("MarkdownEditor|Outdent line (%{modifier_key}[)") % { modifier_key: modifier_key }) })
= markdown_toolbar_button({ icon: "details-block",
data: { "md-tag" => "<details><summary>Click to expand</summary>\n{text}\n</details>", "md-prepend" => true, "md-select" => "Click to expand" },
title: _("Add a collapsible section") })
......
......@@ -21207,6 +21207,9 @@ msgstr ""
msgid "Increase"
msgstr ""
 
msgid "Indent line"
msgstr ""
msgid "Index"
msgstr ""
 
......@@ -24446,6 +24449,18 @@ msgstr ""
msgid "MarkdownEditor|Add strikethrough text (%{modifier_key}⇧X)"
msgstr ""
 
msgid "MarkdownEditor|Indent line (%{modifierKey}])"
msgstr ""
msgid "MarkdownEditor|Indent line (%{modifier_key}])"
msgstr ""
msgid "MarkdownEditor|Outdent line (%{modifierKey}[)"
msgstr ""
msgid "MarkdownEditor|Outdent line (%{modifier_key}[)"
msgstr ""
msgid "MarkdownToolbar|Supports %{markdownDocsLinkStart}Markdown%{markdownDocsLinkEnd}"
msgstr ""
 
......@@ -28165,6 +28180,9 @@ msgstr ""
msgid "OutdatedBrowser|Please install a %{browser_link_start}supported web browser%{browser_link_end} for a better experience."
msgstr ""
 
msgid "Outdent line"
msgstr ""
msgid "Overridden"
msgstr ""
 
......@@ -4,15 +4,30 @@ import {
keypressNoteText,
compositionStartNoteText,
compositionEndNoteText,
updateTextForToolbarBtn,
} from '~/lib/utils/text_markdown';
import '~/lib/utils/jquery_at_who';
import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
describe('init markdown', () => {
let mdArea;
let textArea;
let indentButton;
let outdentButton;
beforeAll(() => {
textArea = document.createElement('textarea');
document.querySelector('body').appendChild(textArea);
setHTMLFixture(
`<div class='md-area'>
<textarea></textarea>
<button data-md-command="indentLines" id="indentButton"></button>
<button data-md-command="outdentLines" id="outdentButton"></button>
</div>`,
);
mdArea = document.querySelector('.md-area');
textArea = mdArea.querySelector('textarea');
indentButton = mdArea.querySelector('#indentButton');
outdentButton = mdArea.querySelector('#outdentButton');
textArea.focus();
// needed for the underlying insertText to work
......@@ -20,7 +35,7 @@ describe('init markdown', () => {
});
afterAll(() => {
textArea.parentNode.removeChild(textArea);
resetHTMLFixture();
});
describe('insertMarkdownText', () => {
......@@ -306,15 +321,6 @@ describe('init markdown', () => {
});
describe('shifting selected lines left or right', () => {
const indentEvent = new KeyboardEvent('keydown', { key: ']', metaKey: true });
const outdentEvent = new KeyboardEvent('keydown', { key: '[', metaKey: true });
beforeEach(() => {
textArea.addEventListener('keydown', keypressNoteText);
textArea.addEventListener('compositionstart', compositionStartNoteText);
textArea.addEventListener('compositionend', compositionEndNoteText);
});
it.each`
selectionStart | selectionEnd | expected | expectedSelectionStart | expectedSelectionEnd
${0} | ${0} | ${' 012\n456\n89'} | ${2} | ${2}
......@@ -338,7 +344,7 @@ describe('init markdown', () => {
textArea.value = text;
textArea.setSelectionRange(selectionStart, selectionEnd);
textArea.dispatchEvent(indentEvent);
updateTextForToolbarBtn($(indentButton));
expect(textArea.value).toEqual(expected);
expect(textArea.selectionStart).toEqual(expectedSelectionStart);
......@@ -350,7 +356,7 @@ describe('init markdown', () => {
textArea.value = '012\n\n89';
textArea.setSelectionRange(4, 4);
textArea.dispatchEvent(indentEvent);
updateTextForToolbarBtn($(indentButton));
expect(textArea.value).toEqual('012\n \n89');
expect(textArea.selectionStart).toEqual(6);
......@@ -381,7 +387,7 @@ describe('init markdown', () => {
textArea.value = text;
textArea.setSelectionRange(selectionStart, selectionEnd);
textArea.dispatchEvent(outdentEvent);
updateTextForToolbarBtn($(outdentButton));
expect(textArea.value).toEqual(expected);
expect(textArea.selectionStart).toEqual(expectedSelectionStart);
......@@ -393,7 +399,7 @@ describe('init markdown', () => {
textArea.value = '012\n\n89';
textArea.setSelectionRange(4, 4);
textArea.dispatchEvent(outdentEvent);
updateTextForToolbarBtn($(outdentButton));
expect(textArea.value).toEqual('012\n\n89');
expect(textArea.selectionStart).toEqual(4);
......
......@@ -54,6 +54,8 @@ describe('Markdown field header component', () => {
'Add a bullet list',
'Add a numbered list',
'Add a checklist',
'Indent line (⌘])',
'Outdent line (⌘[)',
'Add a collapsible section',
'Add a table',
'Go full screen',
......
0% 加载中 .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册