Added description field to inline edit form

[ci skip]
This commit is contained in:
Phil Hughes 2017-05-12 15:52:45 +01:00
parent b1affe07a1
commit b5b5b4af0c
11 changed files with 372 additions and 30 deletions

View File

@ -142,7 +142,8 @@ window.DropzoneInput = (function() {
$(child).val(beforeSelection + formattedText + afterSelection);
textarea.setSelectionRange(caretStart + formattedText.length, caretEnd + formattedText.length);
textarea.style.height = `${textarea.scrollHeight}px`;
return form_textarea.trigger("input");
form_textarea.trigger("input");
form_textarea.get(0).dispatchEvent(new Event('input'));
};
getFilename = function(e) {
var value;

View File

@ -41,8 +41,8 @@ export default {
required: false,
default: '',
},
showForm: {
type: Boolean,
markdownPreviewUrl: {
type: String,
required: true,
},
},
@ -71,6 +71,13 @@ export default {
editActions,
},
methods: {
openForm() {
this.showForm = true;
this.store.formState = {
title: this.state.titleText,
description: this.state.descriptionText,
};
},
updateIssuable() {
this.service.updateIssuable(this.formState)
.then(() => {
@ -96,14 +103,6 @@ export default {
});
},
},
methods: {
openForm() {
this.showForm = true;
this.store.formState = {
title: this.state.titleText,
};
},
},
created() {
this.service = new Service(this.endpoint);
this.poll = new Poll({
@ -150,12 +149,14 @@ export default {
:title-html="state.titleHtml"
:title-text="state.titleText" />
<description-component
v-if="state.descriptionHtml"
:store="store"
:show-form="showForm"
:can-update="canUpdate"
:description-html="state.descriptionHtml"
:description-text="state.descriptionText"
:updated-at="state.updatedAt"
:task-status="state.taskStatus" />
:task-status="state.taskStatus"
:markdown-preview-url="markdownPreviewUrl" />
<edit-actions
v-if="canUpdate && showForm"
:can-destroy="canDestroy" />

View File

@ -1,5 +1,6 @@
<script>
import animateMixin from '../mixins/animate';
import descriptionField from './fields/description.vue';
export default {
mixins: [animateMixin],
@ -24,6 +25,18 @@
type: String,
required: true,
},
store: {
type: Object,
required: true,
},
showForm: {
type: Boolean,
required: true,
},
markdownPreviewUrl: {
type: String,
required: true,
},
},
data() {
return {
@ -75,6 +88,9 @@
}
},
},
components: {
descriptionField,
},
mounted() {
this.renderGFM();
},
@ -82,24 +98,31 @@
</script>
<template>
<div
class="description"
:class="{
'js-task-list-container': canUpdate
}">
<div :class="{ 'common-note-form': showForm }">
<description-field
v-if="showForm"
:store="store"
:markdown-preview-url="markdownPreviewUrl" />
<div
class="wiki"
v-else-if="descriptionHtml"
class="description"
:class="{
'issue-realtime-pre-pulse': preAnimation,
'issue-realtime-trigger-pulse': pulseAnimation
}"
v-html="descriptionHtml"
ref="gfm-content">
'js-task-list-container': canUpdate
}">
<div
class="wiki"
:class="{
'issue-realtime-pre-pulse': preAnimation,
'issue-realtime-trigger-pulse': pulseAnimation
}"
v-html="descriptionHtml"
ref="gfm-content">
</div>
<textarea
class="hidden js-task-list-field"
v-if="descriptionText"
v-model="descriptionText">
</textarea>
</div>
<textarea
class="hidden js-task-list-field"
v-if="descriptionText"
v-model="descriptionText">
</textarea>
</div>
</template>

View File

@ -0,0 +1,40 @@
<script>
/* global Flash */
import markdownField from '../../../vue_shared/components/markdown/field.vue';
export default {
props: {
store: {
type: Object,
required: true,
},
markdownPreviewUrl: {
type: String,
required: true,
},
},
data() {
return {
state: this.store.formState,
};
},
components: {
markdownField,
},
};
</script>
<template>
<div>
<markdown-field
:markdown-preview-url="markdownPreviewUrl">
<textarea
class="note-textarea js-gfm-input js-autosize markdown-area"
data-supports-slash-commands="false"
v-model="state.description"
ref="textatea"
slot="textarea">
</textarea>
</markdown-field>
</div>
</template>

View File

@ -25,6 +25,7 @@ document.addEventListener('DOMContentLoaded', () => {
canDestroy,
endpoint,
issuableRef,
markdownPreviewUrl,
} = issuableElement.dataset;
return {
@ -35,6 +36,7 @@ document.addEventListener('DOMContentLoaded', () => {
initialTitle: issuableTitleElement.innerHTML,
initialDescriptionHtml: issuableDescriptionElement ? issuableDescriptionElement.innerHTML : '',
initialDescriptionText: issuableDescriptionTextarea ? issuableDescriptionTextarea.textContent : '',
markdownPreviewUrl,
};
},
render(createElement) {
@ -47,7 +49,7 @@ document.addEventListener('DOMContentLoaded', () => {
initialTitle: this.initialTitle,
initialDescriptionHtml: this.initialDescriptionHtml,
initialDescriptionText: this.initialDescriptionText,
showForm: this.showForm,
markdownPreviewUrl: this.markdownPreviewUrl,
},
});
},

View File

@ -14,6 +14,7 @@ export default class Store {
};
this.formState = {
title: '',
description: '',
};
}

View File

@ -0,0 +1,102 @@
<script>
/* global Flash */
import markdownHeader from './header.vue';
import markdownToolbar from './toolbar.vue';
export default {
props: {
markdownPreviewUrl: {
type: String,
required: false,
default: '',
},
},
data() {
return {
markdownPreview: '',
markdownPreviewLoading: false,
previewMarkdown: false,
};
},
components: {
markdownHeader,
markdownToolbar,
},
methods: {
toggleMarkdownPreview() {
this.previewMarkdown = !this.previewMarkdown;
if (!this.previewMarkdown) {
this.markdownPreview = '';
} else {
this.markdownPreviewLoading = true;
this.$http.post(
this.markdownPreviewUrl,
{
/*
Can't use `$refs` as the component is technically in the parent component
so we access the VNode & then get the element
*/
text: this.$slots.textarea[0].elm.value,
},
)
.then((res) => {
const data = res.json();
this.markdownPreviewLoading = false;
this.markdownPreview = data.body;
this.$nextTick(() => {
$(this.$refs['markdown-preview']).renderGFM();
});
})
.catch(() => new Flash('Error loading markdown preview'));
}
},
},
mounted() {
/*
GLForm class handles all the toolbar buttons etc.
*/
return new gl.GLForm($(this.$refs['gl-form']));
},
};
</script>
<template>
<div
class="md-area prepend-top-default append-bottom-default"
ref="gl-form">
<markdown-header
:preview-markdown="previewMarkdown"
@toggle-markdown="toggleMarkdownPreview" />
<div
class="md-write-holder"
v-show="!previewMarkdown">
<div class="zen-backdrop">
<slot name="textarea"></slot>
<a
class="zen-control zen-control-leave js-zen-leave"
href="#"
aria-label="Enter zen mode">
<i
class="fa fa-compress"
aria-hidden="true">
</i>
</a>
<markdown-toolbar />
</div>
</div>
<div
class="md md-preview-holder md-preview"
v-show="previewMarkdown">
<div
ref="markdown-preview"
v-html="markdownPreview">
</div>
<span v-if="markdownPreviewLoading">
Loading...
</span>
</div>
</div>
</template>

View File

@ -0,0 +1,97 @@
<script>
import toolbarButton from './toolbar_button.vue';
export default {
props: {
previewMarkdown: {
type: Boolean,
required: true,
},
},
components: {
toolbarButton,
},
methods: {
toggleMarkdownPreview(e) {
e.target.blur();
this.$emit('toggle-markdown');
},
},
};
</script>
<template>
<div class="md-header">
<ul class="nav-links clearfix">
<li :class="{ active: !previewMarkdown }">
<a
href="#md-write-holder"
tabindex="-1"
@click.prevent="toggleMarkdownPreview($event)">
Write
</a>
</li>
<li :class="{ active: previewMarkdown }">
<a
href="#md-preview-holder"
tabindex="-1"
@click.prevent="toggleMarkdownPreview($event)">
Preview
</a>
</li>
<li class="pull-right">
<div class="toolbar-group">
<toolbar-button
tag="**"
button-title="Add bold text"
icon="bold" />
<toolbar-button
tag="*"
button-title="Add italic text"
icon="italic" />
<toolbar-button
tag="> "
:prepend="true"
button-title="Insert a quote"
icon="quote-right" />
<toolbar-button
tag="`"
tag-block="```"
button-title="Insert code"
icon="code" />
<toolbar-button
tag="* "
:prepend="true"
button-title="Add a bullet list"
icon="list-ul" />
<toolbar-button
tag="1. "
:prepend="true"
button-title="Add a numbered list"
icon="list-ol" />
<toolbar-button
tag="* [ ] "
:prepend="true"
button-title="Add a task list"
icon="check-square-o" />
</div>
<div class="toolbar-group">
<button
aria-label="Go full screen"
class="toolbar-btn js-zen-enter"
data-container="body"
tabindex="-1"
data-toggle="tooltip"
title="Go full screen"
type="button">
<i
aria-hidden="true"
class="fa fa-arrows-alt fa-fw">
</i>
</button>
</div>
</li>
</ul>
</div>
</template>

View File

@ -0,0 +1,26 @@
<script>
</script>
<template>
<div class="comment-toolbar clearfix">
<div class="toolbar-text">
<a
href="/docs"
target="_blank"
tabindex="-1">
Markdown is supported
</a>
</div>
<button
class="toolbar-button markdown-selector"
type="button"
tabindex="-1">
<i
class="fa fa-file-image-o toolbar-button-icon"
aria-hidden="true">
</i>
Attach a file
</button>
</div>
</template>

View File

@ -0,0 +1,48 @@
<script>
export default {
props: {
buttonTitle: {
type: String,
required: true,
},
icon: {
type: String,
required: true,
},
tag: {
type: String,
required: true,
},
tagBlock: {
type: String,
required: false,
default: '',
},
prepend: {
type: Boolean,
required: false,
default: false,
},
},
};
</script>
<template>
<button
type="button"
class="toolbar-btn js-md hidden-xs"
tabindex="-1"
:data-md-tag="tag"
:data-md-block="tagBlock"
:data-md-prepend="prepend"
data-container="body"
data-toggle="tooltip"
:title="buttonTitle"
:aria-label="buttonTitle">
<i
aria-hidden="true"
class="fa fa-fw"
:class="'fa-' + icon">
</i>
</button>
</template>

View File

@ -55,6 +55,7 @@
"can-update" => can?(current_user, :update_issue, @issue).to_s,
"can-destroy" => can?(current_user, :destroy_issue, @issue).to_s,
"issuable-ref" => @issue.to_reference,
"markdown-preview-url" => preview_markdown_path(@project),
} }
%h2.title= markdown_field(@issue, :title)
- if @issue.description.present?