diff options
Diffstat (limited to 'app/assets/javascripts/ide')
9 files changed, 262 insertions, 151 deletions
diff --git a/app/assets/javascripts/ide/components/changed_file_icon.vue b/app/assets/javascripts/ide/components/changed_file_icon.vue index fbcd0834809..9ce653f6e81 100644 --- a/app/assets/javascripts/ide/components/changed_file_icon.vue +++ b/app/assets/javascripts/ide/components/changed_file_icon.vue @@ -1,12 +1,12 @@ <script> import tooltip from '~/vue_shared/directives/tooltip'; -import icon from '~/vue_shared/components/icon.vue'; +import Icon from '~/vue_shared/components/icon.vue'; import { pluralize } from '~/lib/utils/text_utility'; import { __, sprintf } from '~/locale'; export default { components: { - icon, + Icon, }, directives: { tooltip, diff --git a/app/assets/javascripts/ide/components/commit_sidebar/actions.vue b/app/assets/javascripts/ide/components/commit_sidebar/actions.vue index 2cbd982af19..45321df191c 100644 --- a/app/assets/javascripts/ide/components/commit_sidebar/actions.vue +++ b/app/assets/javascripts/ide/components/commit_sidebar/actions.vue @@ -1,41 +1,27 @@ <script> - import { mapState } from 'vuex'; - import { sprintf, __ } from '~/locale'; - import * as consts from '../../stores/modules/commit/constants'; - import RadioGroup from './radio_group.vue'; +import { mapState } from 'vuex'; +import { sprintf, __ } from '~/locale'; +import * as consts from '../../stores/modules/commit/constants'; +import RadioGroup from './radio_group.vue'; - export default { - components: { - RadioGroup, +export default { + components: { + RadioGroup, + }, + computed: { + ...mapState(['currentBranchId']), + commitToCurrentBranchText() { + return sprintf( + __('Commit to %{branchName} branch'), + { branchName: `<strong class="monospace">${this.currentBranchId}</strong>` }, + false, + ); }, - computed: { - ...mapState([ - 'currentBranchId', - ]), - newMergeRequestHelpText() { - return sprintf( - __('Creates a new branch from %{branchName} and re-directs to create a new merge request'), - { branchName: this.currentBranchId }, - ); - }, - commitToCurrentBranchText() { - return sprintf( - __('Commit to %{branchName} branch'), - { branchName: `<strong>${this.currentBranchId}</strong>` }, - false, - ); - }, - commitToNewBranchText() { - return sprintf( - __('Creates a new branch from %{branchName}'), - { branchName: this.currentBranchId }, - ); - }, - }, - commitToCurrentBranch: consts.COMMIT_TO_CURRENT_BRANCH, - commitToNewBranch: consts.COMMIT_TO_NEW_BRANCH, - commitToNewBranchMR: consts.COMMIT_TO_NEW_BRANCH_MR, - }; + }, + commitToCurrentBranch: consts.COMMIT_TO_CURRENT_BRANCH, + commitToNewBranch: consts.COMMIT_TO_NEW_BRANCH, + commitToNewBranchMR: consts.COMMIT_TO_NEW_BRANCH_MR, +}; </script> <template> @@ -53,13 +39,11 @@ :value="$options.commitToNewBranch" :label="__('Create a new branch')" :show-input="true" - :help-text="commitToNewBranchText" /> <radio-group :value="$options.commitToNewBranchMR" :label="__('Create a new branch and merge request')" :show-input="true" - :help-text="newMergeRequestHelpText" /> </div> </template> diff --git a/app/assets/javascripts/ide/components/commit_sidebar/message_field.vue b/app/assets/javascripts/ide/components/commit_sidebar/message_field.vue new file mode 100644 index 00000000000..dcd934f76b7 --- /dev/null +++ b/app/assets/javascripts/ide/components/commit_sidebar/message_field.vue @@ -0,0 +1,130 @@ +<script> +import { __, sprintf } from '../../../locale'; +import Icon from '../../../vue_shared/components/icon.vue'; +import popover from '../../../vue_shared/directives/popover'; +import { MAX_TITLE_LENGTH, MAX_BODY_LENGTH } from '../../constants'; + +export default { + directives: { + popover, + }, + components: { + Icon, + }, + props: { + text: { + type: String, + required: true, + }, + }, + data() { + return { + scrollTop: 0, + isFocused: false, + }; + }, + computed: { + allLines() { + return this.text.split('\n').map((line, i) => ({ + text: line.substr(0, this.getLineLength(i)) || ' ', + highlightedText: line.substr(this.getLineLength(i)), + })); + }, + }, + methods: { + handleScroll() { + if (this.$refs.textarea) { + 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); + }, + updateIsFocused(isFocused) { + this.isFocused = isFocused; + }, + }, + popoverOptions: { + trigger: 'hover', + placement: 'top', + content: sprintf( + __(` + The character highligher helps you keep the subject line to %{titleLength} characters + and wrap the body at %{bodyLength} so they are readable in git. + `), + { titleLength: MAX_TITLE_LENGTH, bodyLength: MAX_BODY_LENGTH }, + ), + }, +}; +</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" + > + <icon + name="question" + /> + </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.text" + > + </span><mark + v-show="line.highlightedText" + v-text="line.highlightedText" + > + </mark> + </div> + </div> + </div> + <textarea + class="note-textarea ide-commit-message-textarea" + name="commit-message" + :placeholder="__('Write a commit message...')" + :value="text" + @scroll="handleScroll" + @input="onInput" + @focus="updateIsFocused(true)" + @blur="updateIsFocused(false)" + ref="textarea" + > + </textarea> + </div> + </div> + </fieldset> +</template> diff --git a/app/assets/javascripts/ide/components/commit_sidebar/radio_group.vue b/app/assets/javascripts/ide/components/commit_sidebar/radio_group.vue index 4310d762c78..b660a2961cb 100644 --- a/app/assets/javascripts/ide/components/commit_sidebar/radio_group.vue +++ b/app/assets/javascripts/ide/components/commit_sidebar/radio_group.vue @@ -1,52 +1,40 @@ <script> - import { mapActions, mapState, mapGetters } from 'vuex'; - import tooltip from '~/vue_shared/directives/tooltip'; +import { mapActions, mapState, mapGetters } from 'vuex'; +import tooltip from '~/vue_shared/directives/tooltip'; - export default { - directives: { - tooltip, +export default { + directives: { + tooltip, + }, + props: { + value: { + type: String, + required: true, }, - props: { - value: { - type: String, - required: true, - }, - label: { - type: String, - required: false, - default: null, - }, - checked: { - type: Boolean, - required: false, - default: false, - }, - showInput: { - type: Boolean, - required: false, - default: false, - }, - helpText: { - type: String, - required: false, - default: null, - }, + label: { + type: String, + required: false, + default: null, }, - computed: { - ...mapState('commit', [ - 'commitAction', - ]), - ...mapGetters('commit', [ - 'newBranchName', - ]), + checked: { + type: Boolean, + required: false, + default: false, }, - methods: { - ...mapActions('commit', [ - 'updateCommitAction', - 'updateBranchName', - ]), + showInput: { + type: Boolean, + required: false, + default: false, }, - }; + }, + computed: { + ...mapState('commit', ['commitAction']), + ...mapGetters('commit', ['newBranchName']), + }, + methods: { + ...mapActions('commit', ['updateCommitAction', 'updateBranchName']), + }, +}; </script> <template> @@ -65,18 +53,6 @@ {{ label }} </template> <slot v-else></slot> - <span - v-if="helpText" - v-tooltip - class="help-block inline" - :title="helpText" - > - <i - class="fa fa-question-circle" - aria-hidden="true" - > - </i> - </span> </span> </label> <div @@ -85,7 +61,7 @@ > <input type="text" - class="form-control" + class="form-control monospace" :placeholder="newBranchName" @input="updateBranchName($event.target.value)" /> diff --git a/app/assets/javascripts/ide/components/repo_commit_section.vue b/app/assets/javascripts/ide/components/repo_commit_section.vue index 5b82ce605db..32b6af892d8 100644 --- a/app/assets/javascripts/ide/components/repo_commit_section.vue +++ b/app/assets/javascripts/ide/components/repo_commit_section.vue @@ -6,8 +6,9 @@ import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue'; import LoadingButton from '~/vue_shared/components/loading_button.vue'; import commitFilesList from './commit_sidebar/list.vue'; import EmptyState from './commit_sidebar/empty_state.vue'; -import Actions from './commit_sidebar/actions.vue'; +import CommitMessageField from './commit_sidebar/message_field.vue'; import * as consts from '../stores/modules/commit/constants'; +import Actions from './commit_sidebar/actions.vue'; export default { components: { @@ -17,6 +18,7 @@ export default { EmptyState, Actions, LoadingButton, + CommitMessageField, }, directives: { tooltip, @@ -92,16 +94,10 @@ export default { @submit.prevent.stop="commitChanges" v-if="!rightPanelCollapsed" > - <div class="multi-file-commit-fieldset"> - <textarea - class="form-control multi-file-commit-message" - name="commit-message" - :value="commitMessage" - :placeholder="__('Write a commit message...')" - @input="updateCommitMessage($event.target.value)" - > - </textarea> - </div> + <commit-message-field + :text="commitMessage" + @input="updateCommitMessage" + /> <div class="clearfix prepend-top-15"> <actions /> <loading-button diff --git a/app/assets/javascripts/ide/constants.js b/app/assets/javascripts/ide/constants.js new file mode 100644 index 00000000000..b60d042e0be --- /dev/null +++ b/app/assets/javascripts/ide/constants.js @@ -0,0 +1,3 @@ +// Fuzzy file finder +export const MAX_TITLE_LENGTH = 50; +export const MAX_BODY_LENGTH = 72; diff --git a/app/assets/javascripts/ide/ide_router.js b/app/assets/javascripts/ide/ide_router.js index 20983666b4a..4a0a303d5a6 100644 --- a/app/assets/javascripts/ide/ide_router.js +++ b/app/assets/javascripts/ide/ide_router.js @@ -36,11 +36,11 @@ const router = new VueRouter({ base: `${gon.relative_url_root}/-/ide/`, routes: [ { - path: '/project/:namespace/:project', + path: '/project/:namespace/:project+', component: EmptyRouterComponent, children: [ { - path: ':targetmode/:branch/*', + path: ':targetmode(edit|tree|blob)/:branch/*', component: EmptyRouterComponent, }, { diff --git a/app/assets/javascripts/ide/stores/actions/project.js b/app/assets/javascripts/ide/stores/actions/project.js index b3882cb8d21..4eb23b2ee0f 100644 --- a/app/assets/javascripts/ide/stores/actions/project.js +++ b/app/assets/javascripts/ide/stores/actions/project.js @@ -5,45 +5,71 @@ import * as types from '../mutation_types'; export const getProjectData = ( { commit, state, dispatch }, { namespace, projectId, force = false } = {}, -) => new Promise((resolve, reject) => { - if (!state.projects[`${namespace}/${projectId}`] || force) { - commit(types.TOGGLE_LOADING, { entry: state }); - service.getProjectData(namespace, projectId) - .then(res => res.data) - .then((data) => { +) => + new Promise((resolve, reject) => { + if (!state.projects[`${namespace}/${projectId}`] || force) { commit(types.TOGGLE_LOADING, { entry: state }); - commit(types.SET_PROJECT, { projectPath: `${namespace}/${projectId}`, project: data }); - if (!state.currentProjectId) commit(types.SET_CURRENT_PROJECT, `${namespace}/${projectId}`); - resolve(data); - }) - .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}`]); - } -}); + service + .getProjectData(namespace, projectId) + .then(res => res.data) + .then(data => { + commit(types.TOGGLE_LOADING, { entry: state }); + commit(types.SET_PROJECT, { projectPath: `${namespace}/${projectId}`, project: data }); + if (!state.currentProjectId) + commit(types.SET_CURRENT_PROJECT, `${namespace}/${projectId}`); + resolve(data); + }) + .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 = ( { commit, state, dispatch }, { projectId, branchId, force = false } = {}, -) => new Promise((resolve, reject) => { - if ((typeof state.projects[`${projectId}`] === 'undefined' || - !state.projects[`${projectId}`].branches[branchId]) - || force) { - service.getBranchData(`${projectId}`, branchId) - .then(({ data }) => { - const { id } = data.commit; - commit(types.SET_BRANCH, { projectPath: `${projectId}`, branchName: branchId, branch: data }); - commit(types.SET_BRANCH_WORKING_REFERENCE, { projectId, branchId, reference: id }); - 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]); - } -}); +) => + new Promise((resolve, reject) => { + if ( + typeof state.projects[`${projectId}`] === 'undefined' || + !state.projects[`${projectId}`].branches[branchId] || + force + ) { + service + .getBranchData(`${projectId}`, branchId) + .then(({ data }) => { + const { id } = data.commit; + commit(types.SET_BRANCH, { + projectPath: `${projectId}`, + branchName: branchId, + branch: data, + }); + 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]); + } + }); diff --git a/app/assets/javascripts/ide/stores/mutations/tree.js b/app/assets/javascripts/ide/stores/mutations/tree.js index 7f7e470c9bb..1176c040fb9 100644 --- a/app/assets/javascripts/ide/stores/mutations/tree.js +++ b/app/assets/javascripts/ide/stores/mutations/tree.js @@ -17,12 +17,8 @@ export default { }); }, [types.SET_DIRECTORY_DATA](state, { data, treePath }) { - Object.assign(state, { - trees: Object.assign(state.trees, { - [treePath]: { - tree: data, - }, - }), + Object.assign(state.trees[treePath], { + tree: data, }); }, [types.SET_LAST_COMMIT_URL](state, { tree = state, url }) { |