Implement annotation on the whole picture.
--- a/src/iconolab/templates/iconolab/detail_image.html Mon May 15 14:44:14 2017 +0200
+++ b/src/iconolab/templates/iconolab/detail_image.html Tue May 16 11:29:33 2017 +0200
@@ -41,7 +41,8 @@
v-bind:is-authenticated="isAuthenticated"
v-bind:annotations="annotations"
v-on:click:annotation="onAnnotationClick($event)"
- v-on:close:annotation="onAnnotationClose"></image-annotator>
+ v-on:close:annotation="onAnnotationClose"
+ v-on:add:annotation="onAnnotationAdd"></image-annotator>
<form id="form-annotation" action="{% url 'annotation_create' collection_name image.image_guid %}" method="POST">
{% csrf_token %}
<input type="hidden" name="{{ form.title.name }}">
@@ -55,15 +56,17 @@
<div class="panel-body">
<annotation-form v-bind:annotation="annotation"
v-bind:is-authenticated="isAuthenticated"
- action="{% url 'annotation_edit' collection_name image_guid ':annotation_guid' %}"
+ new-annotation-url="{% url 'annotation_create' collection_name image.image_guid %}"
+ edit-annotation-url="{% url 'annotation_edit' collection_name image_guid ':annotation_guid' %}"
revisions-url="{% url 'annotation_revisions' collection_name image_guid ':annotation_guid' %}"
- author-url="{% url 'user_home' '--username--' %}">
+ author-url="{% url 'user_home' '--username--' %}"
+ v-on:close="onAnnotationClose">
{% csrf_token %}
</annotation-form>
<div class="annotation-comment-box" id="form-comment">
- <comment-form v-if="annotation && isAuthenticated"></comment-form>
+ <comment-form v-if="showCommentForm"></comment-form>
</div>
- <div v-show="annotation" style="display: none;" class="annotation-comment-list">
+ <div v-show="showCommentList" style="display: none;" class="annotation-comment-list">
<label class="small text-muted">Commentaires</label>
<comment-list v-bind:annotation="annotation"
v-bind:is-authenticated="isAuthenticated"
@@ -159,18 +162,31 @@
isAuthenticated: isAuthenticated,
};
},
+ computed: {
+ showCommentForm: function() {
+ return this.annotation && this.annotation.annotation_guid && this.isAuthenticated
+ },
+ showCommentList: function() {
+ return this.annotation && this.annotation.annotation_guid
+ }
+ },
methods: {
onAnnotationClick: function(annotation) {
var self = this;
- getCommentForm(annotation).then(function(template) {
- updateCommentFormComponent(template);
- self.annotation = annotation;
- history.pushState({ annotation_guid: annotation.annotation_guid }, '', annotationURL.replace(':annotation_guid', annotation.annotation_guid));
- });
+ if (annotation.annotation_guid) {
+ getCommentForm(annotation).then(function(template) {
+ updateCommentFormComponent(template);
+ self.annotation = annotation;
+ history.pushState({ annotation_guid: annotation.annotation_guid }, '', annotationURL.replace(':annotation_guid', annotation.annotation_guid));
+ });
+ }
},
onAnnotationClose: function() {
this.annotation = null;
history.pushState({ annotation_guid: null }, '', imageURL);
+ },
+ onAnnotationAdd: function() {
+ addAnnotationWithoutFragment()
}
}
});
@@ -238,9 +254,11 @@
}
function getCommentForm(annotation) {
- return $.get(getCommentFormURL.replace(':annotation_guid', annotation.annotation_guid), {
- next: annotationURL.replace(':annotation_guid', annotation.annotation_guid)
- })
+ if (annotation.annotation_guid) {
+ return $.get(getCommentFormURL.replace(':annotation_guid', annotation.annotation_guid), {
+ next: annotationURL.replace(':annotation_guid', annotation.annotation_guid)
+ })
+ }
}
function getAnnotationByGuid(guid) {
@@ -249,5 +267,15 @@
});
}
+ function addAnnotationWithoutFragment() {
+ vm.annotation = {
+ annotation_guid: null,
+ title: '',
+ description: '',
+ fragment: '',
+ tags:[]
+ }
+ }
+
</script>
{% endblock %}
--- a/src_js/iconolab-bundle/src/components/editor/AnnotationForm.vue Mon May 15 14:44:14 2017 +0200
+++ b/src_js/iconolab-bundle/src/components/editor/AnnotationForm.vue Tue May 16 11:29:33 2017 +0200
@@ -2,7 +2,8 @@
<div>
<button
data-intro="Proposez une nouvelle version" data-position="left"
- v-if="annotation && isAuthenticated" @click="readonly = !readonly"
+ v-if="annotation && isAuthenticated"
+ @click="onCancelClick"
class="btn btn-xs pull-right"
v-bind:class="{ 'btn-primary': readonly, 'btn-warning': !readonly }">
<i class="fa fa-edit" v-if="readonly"></i>
@@ -20,7 +21,7 @@
<input type="hidden" name="fragment" v-model="fragment">
<div v-if="annotation" class="form-group form-group-sm">
<label v-if="!readonly || title" class="small text-muted">Titre</label>
- <input type="text" class="form-control" name="title" v-model="title" v-if="!readonly">
+ <input type="text" class="form-control" name="title" v-model="title" v-if="!readonly" ref="title">
<p v-if="readonly && title" v-bind:class="{ 'text-muted': !title }">{{ title || 'Pas de titre' }}</p>
</div>
<div v-if="annotation" class="form-group form-group-sm">
@@ -36,13 +37,13 @@
@change="onTagsChange($event.tags)"></tag-list>
<input type="hidden" name="tags" v-model="serializedTags">
</div>
- <p class="small text-center text-muted" v-if="annotation">
+ <p class="small text-center text-muted" v-if="annotation && annotation.annotation_guid">
<a v-bind:href="revisionsUrlComputed">Dernière version</a>
<span>{{ dateComputed }} par</span>
<a v-bind:href="authorUrlComputed">{{ annotation.author }}</a>
</p>
<button type="submit" v-if="annotation && !readonly" v-bind:class="{ disabled: !hasChanged }"
- class="btn btn-block btn-sm btn-primary">Enregistrer une nouvelle version</button>
+ class="btn btn-block btn-sm btn-primary">{{ submitButtonText }}</button>
</form>
</div>
</template>
@@ -66,7 +67,6 @@
export default {
props: {
- action: String,
annotation: {
type: Object,
default: function () {
@@ -78,7 +78,9 @@
default: false
},
revisionsUrl: String,
- authorUrl: String
+ authorUrl: String,
+ newAnnotationUrl: String,
+ editAnnotationUrl: String
},
components: {
'tag-list': TagList
@@ -127,9 +129,19 @@
},
formAction: function() {
if (this.annotation) {
- return this.action.replace(':annotation_guid', this.annotation.annotation_guid);
+ if (this.annotation.annotation_guid) {
+ return this.editAnnotationUrl.replace(':annotation_guid', this.annotation.annotation_guid);
+ }
+
+ return this.newAnnotationUrl
}
},
+ submitButtonText: function() {
+ return this.isNewAnnotation ? 'Enregistrer' : 'Enregister une nouvelle version'
+ },
+ isNewAnnotation: function() {
+ return this.annotation && !this.annotation.annotation_guid
+ },
serializedTags: function() {
var tags = this.tags.map(function(tag) {
return {
@@ -151,6 +163,13 @@
}
},
methods: {
+ onCancelClick: function() {
+ if (this.isNewAnnotation) {
+ this.$emit('close')
+ } else {
+ this.readonly = !this.readonly
+ }
+ },
onTagsChange: function(tags) {
this.tags = tags;
},
@@ -164,8 +183,14 @@
description: annotation.description,
fragment: annotation.fragment,
tags: annotation.tags.slice(),
- readonly: true,
+ readonly: !this.isNewAnnotation,
});
+
+ if (!annotation.annotation_guid) {
+ setTimeout(() => {
+ $(this.$refs.title).focus()
+ }, 500);
+ }
}
}
}
--- a/src_js/iconolab-bundle/src/components/editor/Canvas.vue Mon May 15 14:44:14 2017 +0200
+++ b/src_js/iconolab-bundle/src/components/editor/Canvas.vue Tue May 16 11:29:33 2017 +0200
@@ -59,6 +59,7 @@
</defs>
</svg>
</div>
+ <div class="overlay" v-show="showOverlay"></div>
<div class="controls" v-show="loaded">
<div class="controls-left">
<button
@@ -107,7 +108,7 @@
v-bind:imageWidth="imageWidth"
v-bind:imageHeight="imageHeight"></zoom-thumbnail>
</div>
- <div class="controls-right" data-intro="Zoomez dans l'image" data-position="top">
+ <div class="controls-zoom" data-intro="Zoomez dans l'image" data-position="top">
<button @click="zoomOut" type="button" class="btn"
v-bind:class="{ disabled: scale === 1 }">
<i class="fa fa-minus" aria-hidden="true"></i>
@@ -117,6 +118,12 @@
<i class="fa fa-plus" aria-hidden="true"></i>
</button>
</div>
+ <div class="controls-right">
+ <button @click="addAnnotationWithoutFragment" type="button" class="btn"
+ title="Annoter toute l'image" data-intro="Annoter toute l'image" data-position="top">
+ <i class="fa fa-plus-square" aria-hidden="true"></i>
+ </button>
+ </div>
</div>
<div class="help">
<a href="#" class="btn btn-default" v-on:click.stop.prevent="showOnboarding"><i class="fa fa-question-circle"></i></a>
@@ -208,6 +215,9 @@
}
return normalizedAnnotations;
+ },
+ showOverlay: function() {
+ return this.annotation && !this.annotation.annotation_guid
}
},
watch: {
@@ -633,7 +643,11 @@
Cookies.set('__iconolab_onboarding', 'true', { expires: 365 });
})
intro.start()
- }
+ },
+
+ addAnnotationWithoutFragment: function() {
+ this.$emit('add:annotation')
+ },
}
}
@@ -698,10 +712,14 @@
border-radius: 4px;
padding: 8px;
}
- .controls .controls-right {
+ .controls .controls-zoom {
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
}
+ .controls .controls-right {
+ margin-left: 5px;
+ border-radius: 4px;
+ }
.help {
position: absolute;
@@ -709,4 +727,14 @@
right: 10px;
}
+.overlay {
+ position: absolute;
+ background-color: #c5f2ff;
+ opacity: 0.25;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+}
+
</style>
--- a/src_js/iconolab-bundle/src/components/editor/CommentList.vue Mon May 15 14:44:14 2017 +0200
+++ b/src_js/iconolab-bundle/src/components/editor/CommentList.vue Tue May 16 11:29:33 2017 +0200
@@ -34,7 +34,7 @@
},
watch: {
annotation: function(annotation) {
- if (annotation) {
+ if (annotation && annotation.annotation_guid) {
$.getJSON(this.commentsURL).then((comments) => this.comments = comments);
} else {
this.comments = [];