Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
b5b5b4af
Commit
b5b5b4af
authored
May 12, 2017
by
Phil Hughes
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Added description field to inline edit form
[ci skip]
parent
b1affe07
Changes
11
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
372 additions
and
30 deletions
+372
-30
app/assets/javascripts/dropzone_input.js
app/assets/javascripts/dropzone_input.js
+2
-1
app/assets/javascripts/issue_show/components/app.vue
app/assets/javascripts/issue_show/components/app.vue
+13
-12
app/assets/javascripts/issue_show/components/description.vue
app/assets/javascripts/issue_show/components/description.vue
+39
-16
app/assets/javascripts/issue_show/components/fields/description.vue
.../javascripts/issue_show/components/fields/description.vue
+40
-0
app/assets/javascripts/issue_show/index.js
app/assets/javascripts/issue_show/index.js
+3
-1
app/assets/javascripts/issue_show/stores/index.js
app/assets/javascripts/issue_show/stores/index.js
+1
-0
app/assets/javascripts/vue_shared/components/markdown/field.vue
...sets/javascripts/vue_shared/components/markdown/field.vue
+102
-0
app/assets/javascripts/vue_shared/components/markdown/header.vue
...ets/javascripts/vue_shared/components/markdown/header.vue
+97
-0
app/assets/javascripts/vue_shared/components/markdown/toolbar.vue
...ts/javascripts/vue_shared/components/markdown/toolbar.vue
+26
-0
app/assets/javascripts/vue_shared/components/markdown/toolbar_button.vue
...scripts/vue_shared/components/markdown/toolbar_button.vue
+48
-0
app/views/projects/issues/show.html.haml
app/views/projects/issues/show.html.haml
+1
-0
No files found.
app/assets/javascripts/dropzone_input.js
View file @
b5b5b4af
...
@@ -142,7 +142,8 @@ window.DropzoneInput = (function() {
...
@@ -142,7 +142,8 @@ window.DropzoneInput = (function() {
$
(
child
).
val
(
beforeSelection
+
formattedText
+
afterSelection
);
$
(
child
).
val
(
beforeSelection
+
formattedText
+
afterSelection
);
textarea
.
setSelectionRange
(
caretStart
+
formattedText
.
length
,
caretEnd
+
formattedText
.
length
);
textarea
.
setSelectionRange
(
caretStart
+
formattedText
.
length
,
caretEnd
+
formattedText
.
length
);
textarea
.
style
.
height
=
`
${
textarea
.
scrollHeight
}
px`
;
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
)
{
getFilename
=
function
(
e
)
{
var
value
;
var
value
;
...
...
app/assets/javascripts/issue_show/components/app.vue
View file @
b5b5b4af
...
@@ -41,8 +41,8 @@ export default {
...
@@ -41,8 +41,8 @@ export default {
required
:
false
,
required
:
false
,
default
:
''
,
default
:
''
,
},
},
showForm
:
{
markdownPreviewUrl
:
{
type
:
Boolean
,
type
:
String
,
required
:
true
,
required
:
true
,
},
},
},
},
...
@@ -71,6 +71,13 @@ export default {
...
@@ -71,6 +71,13 @@ export default {
editActions
,
editActions
,
},
},
methods
:
{
methods
:
{
openForm
()
{
this
.
showForm
=
true
;
this
.
store
.
formState
=
{
title
:
this
.
state
.
titleText
,
description
:
this
.
state
.
descriptionText
,
};
},
updateIssuable
()
{
updateIssuable
()
{
this
.
service
.
updateIssuable
(
this
.
formState
)
this
.
service
.
updateIssuable
(
this
.
formState
)
.
then
(()
=>
{
.
then
(()
=>
{
...
@@ -96,14 +103,6 @@ export default {
...
@@ -96,14 +103,6 @@ export default {
});
});
},
},
},
},
methods
:
{
openForm
()
{
this
.
showForm
=
true
;
this
.
store
.
formState
=
{
title
:
this
.
state
.
titleText
,
};
},
},
created
()
{
created
()
{
this
.
service
=
new
Service
(
this
.
endpoint
);
this
.
service
=
new
Service
(
this
.
endpoint
);
this
.
poll
=
new
Poll
({
this
.
poll
=
new
Poll
({
...
@@ -150,12 +149,14 @@ export default {
...
@@ -150,12 +149,14 @@ export default {
:title-html=
"state.titleHtml"
:title-html=
"state.titleHtml"
:title-text=
"state.titleText"
/>
:title-text=
"state.titleText"
/>
<description-component
<description-component
v-if=
"state.descriptionHtml"
:store=
"store"
:show-form=
"showForm"
:can-update=
"canUpdate"
:can-update=
"canUpdate"
:description-html=
"state.descriptionHtml"
:description-html=
"state.descriptionHtml"
:description-text=
"state.descriptionText"
:description-text=
"state.descriptionText"
:updated-at=
"state.updatedAt"
:updated-at=
"state.updatedAt"
:task-status=
"state.taskStatus"
/>
:task-status=
"state.taskStatus"
:markdown-preview-url=
"markdownPreviewUrl"
/>
<edit-actions
<edit-actions
v-if=
"canUpdate && showForm"
v-if=
"canUpdate && showForm"
:can-destroy=
"canDestroy"
/>
:can-destroy=
"canDestroy"
/>
...
...
app/assets/javascripts/issue_show/components/description.vue
View file @
b5b5b4af
<
script
>
<
script
>
import
animateMixin
from
'
../mixins/animate
'
;
import
animateMixin
from
'
../mixins/animate
'
;
import
descriptionField
from
'
./fields/description.vue
'
;
export
default
{
export
default
{
mixins
:
[
animateMixin
],
mixins
:
[
animateMixin
],
...
@@ -24,6 +25,18 @@
...
@@ -24,6 +25,18 @@
type
:
String
,
type
:
String
,
required
:
true
,
required
:
true
,
},
},
store
:
{
type
:
Object
,
required
:
true
,
},
showForm
:
{
type
:
Boolean
,
required
:
true
,
},
markdownPreviewUrl
:
{
type
:
String
,
required
:
true
,
},
},
},
data
()
{
data
()
{
return
{
return
{
...
@@ -75,6 +88,9 @@
...
@@ -75,6 +88,9 @@
}
}
},
},
},
},
components
:
{
descriptionField
,
},
mounted
()
{
mounted
()
{
this
.
renderGFM
();
this
.
renderGFM
();
},
},
...
@@ -82,24 +98,31 @@
...
@@ -82,24 +98,31 @@
</
script
>
</
script
>
<
template
>
<
template
>
<div
<div
:class=
"
{ 'common-note-form': showForm }">
class=
"description"
<description-field
:class=
"
{
v-if=
"showForm"
'js-task-list-container': canUpdate
:store=
"store"
}"
>
:markdown-preview-url=
"markdownPreviewUrl"
/
>
<div
<div
class=
"wiki"
v-else-if=
"descriptionHtml"
class=
"description"
:class=
"
{
:class=
"
{
'issue-realtime-pre-pulse': preAnimation,
'js-task-list-container': canUpdate
'issue-realtime-trigger-pulse': pulseAnimation
}">
}"
<div
v-html="descriptionHtml"
class=
"wiki"
ref="gfm-content">
: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>
</div>
<textarea
class=
"hidden js-task-list-field"
v-if=
"descriptionText"
v-model=
"descriptionText"
>
</textarea>
</div>
</div>
</
template
>
</
template
>
app/assets/javascripts/issue_show/components/fields/description.vue
0 → 100644
View file @
b5b5b4af
<
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
>
app/assets/javascripts/issue_show/index.js
View file @
b5b5b4af
...
@@ -25,6 +25,7 @@ document.addEventListener('DOMContentLoaded', () => {
...
@@ -25,6 +25,7 @@ document.addEventListener('DOMContentLoaded', () => {
canDestroy
,
canDestroy
,
endpoint
,
endpoint
,
issuableRef
,
issuableRef
,
markdownPreviewUrl
,
}
=
issuableElement
.
dataset
;
}
=
issuableElement
.
dataset
;
return
{
return
{
...
@@ -35,6 +36,7 @@ document.addEventListener('DOMContentLoaded', () => {
...
@@ -35,6 +36,7 @@ document.addEventListener('DOMContentLoaded', () => {
initialTitle
:
issuableTitleElement
.
innerHTML
,
initialTitle
:
issuableTitleElement
.
innerHTML
,
initialDescriptionHtml
:
issuableDescriptionElement
?
issuableDescriptionElement
.
innerHTML
:
''
,
initialDescriptionHtml
:
issuableDescriptionElement
?
issuableDescriptionElement
.
innerHTML
:
''
,
initialDescriptionText
:
issuableDescriptionTextarea
?
issuableDescriptionTextarea
.
textContent
:
''
,
initialDescriptionText
:
issuableDescriptionTextarea
?
issuableDescriptionTextarea
.
textContent
:
''
,
markdownPreviewUrl
,
};
};
},
},
render
(
createElement
)
{
render
(
createElement
)
{
...
@@ -47,7 +49,7 @@ document.addEventListener('DOMContentLoaded', () => {
...
@@ -47,7 +49,7 @@ document.addEventListener('DOMContentLoaded', () => {
initialTitle
:
this
.
initialTitle
,
initialTitle
:
this
.
initialTitle
,
initialDescriptionHtml
:
this
.
initialDescriptionHtml
,
initialDescriptionHtml
:
this
.
initialDescriptionHtml
,
initialDescriptionText
:
this
.
initialDescriptionText
,
initialDescriptionText
:
this
.
initialDescriptionText
,
showForm
:
this
.
showForm
,
markdownPreviewUrl
:
this
.
markdownPreviewUrl
,
},
},
});
});
},
},
...
...
app/assets/javascripts/issue_show/stores/index.js
View file @
b5b5b4af
...
@@ -14,6 +14,7 @@ export default class Store {
...
@@ -14,6 +14,7 @@ export default class Store {
};
};
this
.
formState
=
{
this
.
formState
=
{
title
:
''
,
title
:
''
,
description
:
''
,
};
};
}
}
...
...
app/assets/javascripts/vue_shared/components/markdown/field.vue
0 → 100644
View file @
b5b5b4af
<
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
>
app/assets/javascripts/vue_shared/components/markdown/header.vue
0 → 100644
View file @
b5b5b4af
<
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
>
app/assets/javascripts/vue_shared/components/markdown/toolbar.vue
0 → 100644
View file @
b5b5b4af
<
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
>
app/assets/javascripts/vue_shared/components/markdown/toolbar_button.vue
0 → 100644
View file @
b5b5b4af
<
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
>
app/views/projects/issues/show.html.haml
View file @
b5b5b4af
...
@@ -55,6 +55,7 @@
...
@@ -55,6 +55,7 @@
"can-update"
=>
can?
(
current_user
,
:update_issue
,
@issue
).
to_s
,
"can-update"
=>
can?
(
current_user
,
:update_issue
,
@issue
).
to_s
,
"can-destroy"
=>
can?
(
current_user
,
:destroy_issue
,
@issue
).
to_s
,
"can-destroy"
=>
can?
(
current_user
,
:destroy_issue
,
@issue
).
to_s
,
"issuable-ref"
=>
@issue
.
to_reference
,
"issuable-ref"
=>
@issue
.
to_reference
,
"markdown-preview-url"
=>
preview_markdown_path
(
@project
),
}
}
}
}
%h2
.title
=
markdown_field
(
@issue
,
:title
)
%h2
.title
=
markdown_field
(
@issue
,
:title
)
-
if
@issue
.
description
.
present?
-
if
@issue
.
description
.
present?
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment