Commit f4902026 authored by Phil Hughes's avatar Phil Hughes

Improve web IDE commit input

Improves the web IDE commit message imput by highlighting any characters
on the first line after 50 & every other line after 72

Closes #44832
parent 25cb70ba
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
commitToCurrentBranchText() { commitToCurrentBranchText() {
return sprintf( return sprintf(
__('Commit to %{branchName} branch'), __('Commit to %{branchName} branch'),
{ branchName: `<strong>${this.currentBranchId}</strong>` }, { branchName: `<strong class="monospace">${this.currentBranchId}</strong>` },
false, false,
); );
}, },
......
<script>
import { __ } from '../../../locale';
import popover from '../../../vue_shared/directives/popover';
export const MAX_TITLE_LENGTH = 50;
export const MAX_BODY_LENGTH = 72;
export default {
directives: {
popover,
},
props: {
text: {
type: String,
required: true,
},
},
data() {
return {
scrollTop: 0,
isFocused: false,
};
},
computed: {
allLines() {
return this.text.replace(/\n$/g, '\n\n').split('\n');
},
},
methods: {
handleScroll() {
this.$nextTick(() => {
this.scrollTop = this.$refs.textarea.scrollTop;
});
},
getLineLength(i) {
return i === 0 ? MAX_TITLE_LENGTH : MAX_BODY_LENGTH;
},
onInput(e) {
this.$emit('input', e.target.value);
},
},
popoverOptions: {
html: true,
trigger: 'hover',
placement: 'top',
content: __(`
The character highligher helps you keep the subject line to 50 characters
and wrap the body at 72 so they are readable in git.
`),
},
};
</script>
<template>
<fieldset class="common-note-form ide-commit-message-field">
<div
class="md-area"
:class="{
'is-focused': isFocused
}"
>
<div
v-once
class="md-header"
>
<ul class="nav-links">
<li>
{{ __('Commit Message') }}
<span
v-popover="$options.popoverOptions"
class="help-block prepend-left-10"
>
<i
aria-hidden="true"
class="fa fa-question-circle"
></i>
</span>
</li>
</ul>
</div>
<div class="ide-commit-message-textarea-container">
<div class="ide-commit-message-highlights-container">
<div
class="note-textarea highlights monospace"
:style="{
transform: `translate3d(0, ${-scrollTop}px, 0)`
}"
>
<div
v-for="(line, index) in allLines"
:key="index"
>
<span
v-text="line.substr(0, getLineLength(index)) || ' '"
>
</span><mark
v-if="line.length > getLineLength(index)"
v-text="line.substr(getLineLength(index))"
>
</mark>
</div>
</div>
</div>
<textarea
class="note-textarea ide-commit-message-textarea"
:placeholder="__('Write a commit message...')"
:value="text"
@scroll="handleScroll"
@input="onInput"
@focus="isFocused = true"
@blur="isFocused = false"
ref="textarea"
>
</textarea>
</div>
</div>
</fieldset>
</template>
...@@ -85,7 +85,7 @@ ...@@ -85,7 +85,7 @@
> >
<input <input
type="text" type="text"
class="form-control" class="form-control monospace"
:placeholder="newBranchName" :placeholder="newBranchName"
@input="updateBranchName($event.target.value)" @input="updateBranchName($event.target.value)"
/> />
......
...@@ -5,6 +5,7 @@ import icon from '~/vue_shared/components/icon.vue'; ...@@ -5,6 +5,7 @@ import icon from '~/vue_shared/components/icon.vue';
import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue'; import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
import LoadingButton from '~/vue_shared/components/loading_button.vue'; import LoadingButton from '~/vue_shared/components/loading_button.vue';
import commitFilesList from './commit_sidebar/list.vue'; import commitFilesList from './commit_sidebar/list.vue';
import CommitMessageField from './commit_sidebar/message_field.vue';
import * as consts from '../stores/modules/commit/constants'; import * as consts from '../stores/modules/commit/constants';
import Actions from './commit_sidebar/actions.vue'; import Actions from './commit_sidebar/actions.vue';
...@@ -15,6 +16,7 @@ export default { ...@@ -15,6 +16,7 @@ export default {
commitFilesList, commitFilesList,
Actions, Actions,
LoadingButton, LoadingButton,
CommitMessageField,
}, },
directives: { directives: {
tooltip, tooltip,
...@@ -38,15 +40,9 @@ export default { ...@@ -38,15 +40,9 @@ export default {
'changedFiles', 'changedFiles',
]), ]),
...mapState('commit', ['commitMessage', 'submitCommitLoading']), ...mapState('commit', ['commitMessage', 'submitCommitLoading']),
...mapGetters('commit', [ ...mapGetters('commit', ['commitButtonDisabled', 'discardDraftButtonDisabled', 'branchName']),
'commitButtonDisabled',
'discardDraftButtonDisabled',
'branchName',
]),
statusSvg() { statusSvg() {
return this.lastCommitMsg return this.lastCommitMsg ? this.committedStateSvgPath : this.noChangesStateSvgPath;
? this.committedStateSvgPath
: this.noChangesStateSvgPath;
}, },
}, },
methods: { methods: {
...@@ -64,9 +60,7 @@ export default { ...@@ -64,9 +60,7 @@ export default {
}); });
}, },
forceCreateNewBranch() { forceCreateNewBranch() {
return this.updateCommitAction(consts.COMMIT_TO_NEW_BRANCH).then(() => return this.updateCommitAction(consts.COMMIT_TO_NEW_BRANCH).then(() => this.commitChanges());
this.commitChanges(),
);
}, },
}, },
}; };
...@@ -105,16 +99,10 @@ export default { ...@@ -105,16 +99,10 @@ export default {
@submit.prevent.stop="commitChanges" @submit.prevent.stop="commitChanges"
v-if="!rightPanelCollapsed" v-if="!rightPanelCollapsed"
> >
<div class="multi-file-commit-fieldset"> <commit-message-field
<textarea :text="commitMessage"
class="form-control multi-file-commit-message" @input="updateCommitMessage"
name="commit-message" />
:value="commitMessage"
:placeholder="__('Write a commit message...')"
@input="updateCommitMessage($event.target.value)"
>
</textarea>
</div>
<div class="clearfix prepend-top-15"> <div class="clearfix prepend-top-15">
<actions /> <actions />
<loading-button <loading-button
......
...@@ -5,45 +5,71 @@ import * as types from '../mutation_types'; ...@@ -5,45 +5,71 @@ import * as types from '../mutation_types';
export const getProjectData = ( export const getProjectData = (
{ commit, state, dispatch }, { commit, state, dispatch },
{ namespace, projectId, force = false } = {}, { namespace, projectId, force = false } = {},
) => new Promise((resolve, reject) => { ) =>
if (!state.projects[`${namespace}/${projectId}`] || force) { new Promise((resolve, reject) => {
commit(types.TOGGLE_LOADING, { entry: state }); if (!state.projects[`${namespace}/${projectId}`] || force) {
service.getProjectData(namespace, projectId)
.then(res => res.data)
.then((data) => {
commit(types.TOGGLE_LOADING, { entry: state }); commit(types.TOGGLE_LOADING, { entry: state });
commit(types.SET_PROJECT, { projectPath: `${namespace}/${projectId}`, project: data }); service
if (!state.currentProjectId) commit(types.SET_CURRENT_PROJECT, `${namespace}/${projectId}`); .getProjectData(namespace, projectId)
resolve(data); .then(res => res.data)
}) .then(data => {
.catch(() => { commit(types.TOGGLE_LOADING, { entry: state });
flash('Error loading project data. Please try again.', 'alert', document, null, false, true); commit(types.SET_PROJECT, { projectPath: `${namespace}/${projectId}`, project: data });
reject(new Error(`Project not loaded ${namespace}/${projectId}`)); if (!state.currentProjectId)
}); commit(types.SET_CURRENT_PROJECT, `${namespace}/${projectId}`);
} else { resolve(data);
resolve(state.projects[`${namespace}/${projectId}`]); })
} .catch(() => {
}); flash(
'Error loading project data. Please try again.',
'alert',
document,
null,
false,
true,
);
reject(new Error(`Project not loaded ${namespace}/${projectId}`));
});
} else {
resolve(state.projects[`${namespace}/${projectId}`]);
}
});
export const getBranchData = ( export const getBranchData = (
{ commit, state, dispatch }, { commit, state, dispatch },
{ projectId, branchId, force = false } = {}, { projectId, branchId, force = false } = {},
) => new Promise((resolve, reject) => { ) =>
if ((typeof state.projects[`${projectId}`] === 'undefined' || new Promise((resolve, reject) => {
!state.projects[`${projectId}`].branches[branchId]) if (
|| force) { typeof state.projects[`${projectId}`] === 'undefined' ||
service.getBranchData(`${projectId}`, branchId) !state.projects[`${projectId}`].branches[branchId] ||
.then(({ data }) => { force
const { id } = data.commit; ) {
commit(types.SET_BRANCH, { projectPath: `${projectId}`, branchName: branchId, branch: data }); service
commit(types.SET_BRANCH_WORKING_REFERENCE, { projectId, branchId, reference: id }); .getBranchData(`${projectId}`, branchId)
resolve(data); .then(({ data }) => {
}) const { id } = data.commit;
.catch(() => { commit(types.SET_BRANCH, {
flash('Error loading branch data. Please try again.', 'alert', document, null, false, true); projectPath: `${projectId}`,
reject(new Error(`Branch not loaded - ${projectId}/${branchId}`)); branchName: branchId,
}); branch: data,
} else { });
resolve(state.projects[`${projectId}`].branches[branchId]); commit(types.SET_BRANCH_WORKING_REFERENCE, { projectId, branchId, reference: id });
} commit(types.SET_CURRENT_BRANCH, branchId);
}); resolve(data);
})
.catch(() => {
flash(
'Error loading branch data. Please try again.',
'alert',
document,
null,
false,
true,
);
reject(new Error(`Branch not loaded - ${projectId}/${branchId}`));
});
} else {
resolve(state.projects[`${projectId}`].branches[branchId]);
}
});
...@@ -662,11 +662,6 @@ ...@@ -662,11 +662,6 @@
} }
} }
.multi-file-commit-message.form-control {
height: 160px;
resize: none;
}
.dirty-diff { .dirty-diff {
// !important need to override monaco inline style // !important need to override monaco inline style
width: 4px !important; width: 4px !important;
...@@ -839,3 +834,74 @@ ...@@ -839,3 +834,74 @@
align-items: center; align-items: center;
font-weight: $gl-font-weight-bold; font-weight: $gl-font-weight-bold;
} }
.ide-commit-message-field {
height: 200px;
background-color: $white-light;
.md-area {
display: flex;
flex-direction: column;
height: 100%;
}
.nav-links {
height: 30px;
}
.help-block {
margin-top: 0;
color: $blue-500;
cursor: pointer;
}
}
.ide-commit-message-textarea-container {
position: relative;
width: 100%;
height: 100%;
overflow: hidden;
.note-textarea {
font-family: $monospace_font;
}
}
.ide-commit-message-highlights-container {
position: absolute;
left: 0;
top: 0;
right: -100px;
bottom: 0;
padding-right: 100px;
pointer-events: none;
z-index: 1;
.highlights {
white-space: pre-wrap;
word-wrap: break-word;
color: transparent;
}
mark {
margin-left: -1px;
padding: 0 2px;
border-radius: $border-radius-small;
background-color: $orange-200;
color: transparent;
opacity: 0.6;
}
}
.ide-commit-message-textarea {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
z-index: 2;
background: transparent;
resize: none;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment