diff --git a/js/help.js b/js/help.js index af83510575d8054fcbfd6c580e49f153eca967d3..0d493863d07df67d3487030f0503b5ca974c841a 100644 --- a/js/help.js +++ b/js/help.js @@ -53,23 +53,6 @@ export const SEARCH_HELP = [ } ] -export const CLASSIFICATIONS_HELP = [ - { - title: 'High confidence', - content: [ - 'High confidence is an immutable attribute set on a class during its creation (e.g. by a Machine Learning tool).', - 'It represents a reliable class and is indicated with an award icon <i class="icon-award has-text-warning"></i> next to the class name.' - ] - }, - { - title: 'Best classes', - content: [ - 'Best classes concept represent overall reliable classes for an element.', - 'They are composed of validated classes, or high confidence classes which have not been rejected.' - ] - } -] - export const KEYBOARD_HELP = [ { content: [ diff --git a/test/Element/Classifications/Classifications.js b/test/Element/Classifications/Classifications.js index 86f478ed83d8e175ca42def2c089fac614158989..fcda24f40cabf67346c4fd685b82b4afd4d6f064 100644 --- a/test/Element/Classifications/Classifications.js +++ b/test/Element/Classifications/Classifications.js @@ -63,8 +63,8 @@ describe('Element', () => { await Vue.nextTick() const [manualGroup, workerGroup] = wrapper.findAll('section > div').wrappers - assert.strictEqual(manualGroup.get('span > strong').text(), 'Manual') - assert.strictEqual(workerGroup.get('span > WorkerVersionDetails-Stub').attributes('workerversionid'), 'versionid') + assert.strictEqual(manualGroup.get('div > strong').text(), 'Manual') + assert.strictEqual(workerGroup.get('div > WorkerVersionDetails-Stub').attributes('workerversionid'), 'versionid') assert.strictEqual(manualGroup.findAll('Classification-Stub').length, 2) assert.strictEqual(workerGroup.findAll('Classification-Stub').length, 2) }) diff --git a/test/Element/Transcription/GroupedTranscriptions.js b/test/Element/Transcription/GroupedTranscriptions.js index f6b1c83c9811ad9a81967b52dddc57c6717bb04e..a2994c8688d46030811e0a907c7883343262b1ab 100644 --- a/test/Element/Transcription/GroupedTranscriptions.js +++ b/test/Element/Transcription/GroupedTranscriptions.js @@ -49,6 +49,6 @@ describe('Element/Transcription/GroupedTranscriptions.vue', () => { } }) - assert.strictEqual(wrapper.find('workertag-stub').exists(), true) + assert.strictEqual(wrapper.find('WorkerVersionDetails-Stub').exists(), true) }) }) diff --git a/test/Element/Transcription/Transcription.js b/test/Element/Transcription/Transcription.js index c4f1ac0872d4c826843608cf152ff922632e2af4..d549fd164b7ff69682dbb8187a678d7d5da27394 100644 --- a/test/Element/Transcription/Transcription.js +++ b/test/Element/Transcription/Transcription.js @@ -112,21 +112,6 @@ describe('Element', () => { assert.ok(!wrapper.find('select').exists()) }) - - it('displays the worker version that created the transcription', () => { - mock.onGet('/transcription/transcriptionid/entities/').reply(200, { count: 0, results: [] }) - const wrapper = shallowMount(Transcription, { - store, - localVue, - propsData: { - transcription: { - id: 'transcriptionid', - worker_version_id: 'versionid' - } - } - }) - assert.strictEqual(wrapper.find('workerversiondetails-stub').attributes('workerversionid'), 'versionid') - }) }) }) }) diff --git a/vue/DropdownContent.vue b/vue/DropdownContent.vue index 1be8157e6b1660e3d0a220b1c4679a9d130b9ba7..415e24a2ba3da6c3c16a7cdab405bd5977ac49cc 100644 --- a/vue/DropdownContent.vue +++ b/vue/DropdownContent.vue @@ -5,16 +5,16 @@ v-on:click="toggle" :class="{ 'disabled': disabled }" > - <h2 :title="disabled ? title + ' not available' : ''"> - {{ title }} - </h2> - <a v-if="!disabled" class="has-text-black"> + <a v-if="!disabled" class="has-text-grey-light mr-2"> <slot name="trigger"> <span> <i class="icon-down-open trigger-icon" :class="{ 'active': toggled }"></i> </span> </slot> </a> + <h2 :title="disabled ? title + ' not available' : ''"> + {{ title }} + </h2> </div> <div class="mt-2" :class="{ 'is-hidden': !toggled || disabled }"> <slot></slot> @@ -63,9 +63,6 @@ export default { .dropdown-toggle:not(.disabled) { cursor: pointer; } -.dropdown-toggle > a { - margin-left: auto; -} h2 { font-size: 1rem; font-style: oblique; diff --git a/vue/Element/AnnotationPanel.vue b/vue/Element/AnnotationPanel.vue index 4f9a3fd7f6e211635d2f2346c05cd5735718be0d..2bd8c7e3d4d79dc1b69a82cc1157ec4ed4607c02 100644 --- a/vue/Element/AnnotationPanel.vue +++ b/vue/Element/AnnotationPanel.vue @@ -1,8 +1,8 @@ <template> <div> <div v-if="!element" class="loading-content loader"></div> - <template v-else> - <DropdownContent title="Element type" :disabled="!canWrite(corpus)"> + <template v-else-if="canWrite(corpus)"> + <DropdownContent title="Element type"> <div class="control"> <div class="select is-fullwidth"> <select v-model="defaultType"> diff --git a/vue/Element/Classifications/Classification.vue b/vue/Element/Classifications/Classification.vue index c20564c848dc2d8a7956c1a976fca184abbc6313..c468d01c10480e8bb6056e37fc76ff2e9c8ca4cc 100644 --- a/vue/Element/Classifications/Classification.vue +++ b/vue/Element/Classifications/Classification.vue @@ -1,5 +1,5 @@ <template> - <div class="columns"> + <div class="columns mr-0"> <div class="column is-6"> {{ name }} <i diff --git a/vue/Element/Classifications/Classifications.vue b/vue/Element/Classifications/Classifications.vue index a2a521cf8cae6b96f73fa98ec938d59ff2f31c01..3c8093041cfc8f2cd11d0b6e4e4dc20b5647c59a 100644 --- a/vue/Element/Classifications/Classifications.vue +++ b/vue/Element/Classifications/Classifications.vue @@ -1,15 +1,14 @@ <template> - <section> + <section class="mb-4"> <div - class="pb-5" + class="mt-2 pb-5" v-for="(results, version) in groupedClassifications" :key="version" > - <span class="ml-4 mb-4"> + <div class="mb-4"> <strong v-if="version === MANUAL_WORKER_VERSION">Manual</strong> - <WorkerVersionDetails v-else :worker-version-id="version" /> - </span> - <hr class="hr mt-0 mb-4" /> + <WorkerVersionDetails v-else :worker-version-id="version" has-outside-title /> + </div> <Classification v-for="classification in results" :key="classification.id" diff --git a/vue/Element/DetailsPanel.vue b/vue/Element/DetailsPanel.vue index 6a83002c283c6a16c82724819bef1915dfefe678..090e90e30a64e74ff6b4be39fa53cec9beb9b26a 100644 --- a/vue/Element/DetailsPanel.vue +++ b/vue/Element/DetailsPanel.vue @@ -7,38 +7,39 @@ <hr /> </template> - <DropdownContent title="Classifications" :disabled="(!canWriteElement(elementId) || !hasMlClasses[corpusId]) && !classifications.length"> - <HelpModal class="is-pulled-right" title="Help for classifications" :data="CLASSIFICATIONS_HELP" /> - <form v-on:submit.prevent="createClassification"> - <div v-if="canWriteElement(elementId) && hasMlClasses" class="field has-addons"> - <p class="control"> - <MLClassSelect - ref="newClassificationSelect" - v-model="selectedNewClassification" - :is-valid.sync="validClassification" - placeholder="Add a classification" - exclude-manual - auto-select - :classifications="element.classifications" - :corpus-id="element.corpus.id" - /> - </p> - <p class="control"> - <button - type="submit" - class="button is-primary" - :class="{ 'is-loading': isSavingNewClassification }" - :disabled="!canCreateClassification" - > - Add - </button> - </p> - </div> - <p class="help is-danger" v-if="manualClassificationExists">A manual classification for this ML class already exists.</p> - </form> - <Classifications v-if="element.classifications" :element="element" /> - </DropdownContent> - <hr /> + <template v-if="(canWriteElement(elementId) && hasMlClasses[corpusId]) || classifications.length"> + <DropdownContent title="Classifications"> + <Classifications v-if="element.classifications" :element="element" /> + <form v-on:submit.prevent="createClassification"> + <div v-if="canWriteElement(elementId) && hasMlClasses" class="field has-addons"> + <p class="control has-margin-left"> + <MLClassSelect + ref="newClassificationSelect" + v-model="selectedNewClassification" + :is-valid.sync="validClassification" + placeholder="Add a classification" + exclude-manual + auto-select + :classifications="element.classifications" + :corpus-id="element.corpus.id" + /> + </p> + <p class="control"> + <button + type="submit" + class="button is-primary" + :class="{ 'is-loading': isSavingNewClassification }" + :disabled="!canCreateClassification" + > + <i class="icon-plus"></i> + </button> + </p> + </div> + <p class="help is-danger" v-if="manualClassificationExists">A manual classification for this ML class already exists.</p> + </form> + </DropdownContent> + <hr /> + </template> <template v-if="elementType.folder === false"> <DropdownContent title="Transcriptions"> @@ -49,14 +50,13 @@ :worker-id="transcriptionGroup[0]" /> <template v-if="canWriteElement(elementId)"> - <a v-on:click="transcriptionModal = true"> - <i class="icon-add-square"></i> - Add - <template v-if="elementTranscriptions.length"> - or edit - </template> - a manual transcription - </a> + <div class="has-text-right"> + <a v-on:click="transcriptionModal = true"> + <button class="button is-primary"> + <i class="icon-plus"></i> + </button> + </a> + </div> <TranscriptionsForm v-if="transcriptionModal" :modal.sync="transcriptionModal" @@ -67,10 +67,12 @@ <hr /> </template> - <DropdownContent title="Metadata" v-model="toggleMetas" :disabled="!metadata.length && !canWriteElement(elementId)"> - <ElementMetadata :corpus-id="element.corpus.id" :element-id="element.id" :opened="toggleMetas" /> - </DropdownContent> - <hr /> + <template v-if="metadata.length || canWriteElement(elementId)"> + <DropdownContent title="Metadata" v-model="toggleMetas"> + <ElementMetadata :corpus-id="element.corpus.id" :element-id="element.id" :opened="toggleMetas" /> + </DropdownContent> + <hr /> + </template> <EntityLinks :element-id="element.id" @@ -83,12 +85,10 @@ <script> import { mapState, mapActions, mapGetters } from 'vuex' import { corporaMixin } from '~/js/mixins' -import { CLASSIFICATIONS_HELP } from '~/js/help' import { MANUAL_WORKER_VERSION } from '~/js/config' import { groupBy, orderBy } from 'lodash' import GroupedTranscriptions from './Transcription' -import HelpModal from '~/vue/HelpModal' import EntityLinks from '~/vue/Entity/Links' import Classifications from './Classifications' import MLClassSelect from '~/vue/MLClassSelect' @@ -108,7 +108,6 @@ export default { MLClassSelect, ElementMetadata, DropdownContent, - HelpModal, TranscriptionsForm, OrientationPanel }, @@ -119,7 +118,6 @@ export default { } }, data: () => ({ - CLASSIFICATIONS_HELP, toggleMetas: true, selectedNewClassification: '', validClassification: null, @@ -241,4 +239,7 @@ export default { height: 1.5rem; margin-right: 1ch; } +.has-margin-left { + margin-left: auto; +} </style> diff --git a/vue/Element/Main.vue b/vue/Element/Main.vue index e02f559e83bd84b0c81b7ed6f18844c0ab838975..a6ddac99302be498bdbbf200e421c44c9b5e1c43 100644 --- a/vue/Element/Main.vue +++ b/vue/Element/Main.vue @@ -53,6 +53,7 @@ <transition name="sidebar"> <div class="column is-one-third" v-if="displayDetails && corpusId"> <PanelHeader :element-id="elementDetailsId" /> + <hr /> <AnnotationPanel :element-id="elementDetailsId" v-if="hasImage && imageEdit" diff --git a/vue/Element/Metadata/Metadata.vue b/vue/Element/Metadata/Metadata.vue index 7a18d421e729ba7cee8c312b11695d95366be556..0e199bad89753aa92b37b6e74aecf2d5527ccac6 100644 --- a/vue/Element/Metadata/Metadata.vue +++ b/vue/Element/Metadata/Metadata.vue @@ -160,13 +160,16 @@ </button> </template> </Modal> - <a - v-if="canWrite(corpus)" - v-on:click="create" - > - <i class="icon-add-square"></i> - Add a metadata - </a> + <div class="has-text-right"> + <a + v-if="canWrite(corpus)" + v-on:click="create" + > + <button class="button is-primary"> + <i class="icon-plus"></i> + </button> + </a> + </div> </div> </template> diff --git a/vue/Element/Metadata/MetadataPanel.vue b/vue/Element/Metadata/MetadataPanel.vue index 8bd5044e0b9feb0df2e86a28eef4068d476f0d31..174844bff4400c1d67950fccfb2ace7a5903f866 100644 --- a/vue/Element/Metadata/MetadataPanel.vue +++ b/vue/Element/Metadata/MetadataPanel.vue @@ -1,47 +1,50 @@ <template> - <nav class="panel"> - <p class="panel-heading"> - {{ meta.length }} Metadata{{ meta.length > 1 ? 's' : '' }} - </p> + <section class="mb-2"> <!-- TODO: Split into one component for each metadata --> - <a v-for="data in sortedMeta" :key="data.id" class="panel-block has-actions"> - <span class="panel-icon" :title="iconTitle(data)"> - <i :class="iconClass(data)"></i> - </span> - <span class="metadata-content"> - {{ data.name }} : - <strong v-if="containsDates(data)"> - <InterpretedDate - :dates="data.dates" - :raw-date="data.value" - /> - </strong> - <strong v-else> - <router-link - v-if="data.entity" - :to="{ name: 'entity-details', params: { id: data.entity.id } }" - > - {{ data.entity.name }} - </router-link> - <a - v-else-if="data.type === 'url'" - :href="data.value" - target="_blank" - > - {{ data.value }} - </a> - <template v-else>{{ data.value }}</template> - </strong> - </span> - <MetadataActions - class="is-actions is-hidden" - :metadata="data" - v-if="data.entity === null" - v-on="$listeners" - /> - <WorkerVersionDetails v-if="data.worker_version" :worker-version-id="data.worker_version" has-dropdown-title /> - </a> - </nav> + <table class="table is-striped is-fullwidth"> + <tbody> + <tr v-for="data in sortedMeta" :key="data.id"> + <td :title="iconTitle(data)" class="key"> + <i :class="iconClass(data)"></i> + {{ data.name }} + </td> + <td class="value"> + <span class="value" v-if="containsDates(data)"> + <InterpretedDate + :dates="data.dates" + :raw-date="data.value" + /> + </span> + <span class="value" v-else> + <router-link + v-if="data.entity" + :to="{ name: 'entity-details', params: { id: data.entity.id } }" + > + {{ data.entity.name }} + </router-link> + <a + v-else-if="data.type === 'url'" + :href="data.value" + target="_blank" + > + {{ data.value }} + </a> + <template v-else>{{ data.value }}</template> + </span> + </td> + <td v-if="data.entity === null"> + <MetadataActions + :metadata="data" + v-on="$listeners" + /> + </td> + <td v-if="data.worker_version"> + <WorkerVersionDetails :worker-version-id="data.worker_version" has-dropdown-title /> + </td> + </tr> + </tbody> + </table> + </section> </template> <script> @@ -86,20 +89,12 @@ export default { </script> <style scoped> -.metadata { - margin-bottom: 1em; +table td.key { + color: #555; + padding-left: 0.1em; } -.content, .content pre { - margin-bottom: 0; -} -.panel-block { - justify-content: space-between; - cursor: default; -} -.metadata-content { - flex: 1; -} -.has-actions:hover > .is-actions { - display: block !important; + +table { + border-bottom: 1px solid #EEE; } </style> diff --git a/vue/Element/PanelHeader.vue b/vue/Element/PanelHeader.vue index 8307693acf5daeec75404517670e380b76b718df..a5a6b506d576c9d30c64a393fa548e0b0088ff4e 100644 --- a/vue/Element/PanelHeader.vue +++ b/vue/Element/PanelHeader.vue @@ -18,13 +18,13 @@ ></a> <template v-if="element.worker_version || element.worker_version_id || element.creator"> <p class="is-pulled-right"> - Created by <!-- Allow both worker_version and worker_version_id because the list and retrieve endpoints are inconsistent --> <WorkerVersionDetails v-if="element.worker_version || element.worker_version_id" :worker-version-id="element.worker_version || element.worker_version_id" + has-outside-title /> - <strong v-else>{{ element.creator }}</strong> + <span v-else>Created by <strong>{{ element.creator }}</strong></span> </p> </template> <Modal v-model="deleteModal" :title="'Delete ' + typeName(element.type) + ' ' + element.name"> diff --git a/vue/Element/Transcription/Box.vue b/vue/Element/Transcription/Box.vue index 53bdec44067fcc4350f32183a65e597d02a9f3f3..db70f3e6bfcf3ad8abb7410160b0c6a3c899af6e 100644 --- a/vue/Element/Transcription/Box.vue +++ b/vue/Element/Transcription/Box.vue @@ -3,7 +3,7 @@ class="content" :style="orientationStyle(transcription.orientation)" > - <blockquote> + <blockquote class="pt-3 pb-3"> <template v-if="entities.length"> <Token v-for="(token, index) in tokens" diff --git a/vue/Element/Transcription/GroupedTranscriptions.vue b/vue/Element/Transcription/GroupedTranscriptions.vue index 463f16126999dc1e1fe6348a2fb5016d59c7f745..7ff7c438d684a93641c0c043a2ed3685f1d1503a 100644 --- a/vue/Element/Transcription/GroupedTranscriptions.vue +++ b/vue/Element/Transcription/GroupedTranscriptions.vue @@ -1,21 +1,28 @@ <template> - <div class="mt-5"> - <WorkerTag :worker-tag="worker" v-if="worker" /> - <div v-else>Manual transcriptions</div> - <Transcription - v-for="transcription in orderedTranscriptions" - :key="transcription.id" - :transcription="transcription" - class="mt-2" - /> - </div> + <section> + <div + class="pb-4" + v-for="(results, version) in groupedTranscriptions" + :key="version" + > + <div class="mb-2"> + <strong v-if="version === MANUAL_WORKER_VERSION">Manual</strong> + <WorkerVersionDetails v-else :worker-version-id="version" has-outside-title /> + </div> + <Transcription + v-for="transcription in results" + :key="transcription.id" + :transcription="transcription" + /> + </div> + </section> </template> <script> -import { mapState, mapActions } from 'vuex' -import { orderBy } from 'lodash' +import { mapState } from 'vuex' +import { orderBy, groupBy } from 'lodash' import { MANUAL_WORKER_VERSION } from '~/js/config' -import WorkerTag from '~/vue/Process/Workers/WorkerTag' +import WorkerVersionDetails from '~/vue/Process/Workers/Versions/Details' import Transcription from './Transcription' export default { props: { @@ -28,25 +35,24 @@ export default { required: true } }, + data: () => ({ + MANUAL_WORKER_VERSION + }), components: { Transcription, - WorkerTag + WorkerVersionDetails }, methods: { - ...mapActions('process', ['getWorker']), getCreatedDate (versionId) { if (!versionId) return return new Date(this.workerVersions[versionId].revision.created) } }, computed: { - ...mapState('process', ['workers', 'workerVersions']), - worker () { - if (this.workerId === MANUAL_WORKER_VERSION) return - return this.workers[this.workerId] - }, - orderedTranscriptions () { - return orderBy(this.transcriptions, t => this.getCreatedDate(t.worker_version_id), 'desc') + ...mapState('process', ['workerVersions']), + groupedTranscriptions () { + const orderedTranscriptions = orderBy(this.transcriptions, t => this.getCreatedDate(t.worker_version_id), 'desc') + return groupBy(orderedTranscriptions, t => t.worker_version_id || MANUAL_WORKER_VERSION) } } } diff --git a/vue/Element/Transcription/Transcription.vue b/vue/Element/Transcription/Transcription.vue index 356767f3aa53329b253cadcf9d454824a058fb4f..175921dbe2cd08afeacb832d4a86911438ec5012 100644 --- a/vue/Element/Transcription/Transcription.vue +++ b/vue/Element/Transcription/Transcription.vue @@ -6,9 +6,6 @@ <option v-for="[id, name] in versionIds" :key="id" :value="id">{{ name }}</option> </select> </div> - <div class="is-pulled-right" v-if="transcription.worker_version_id"> - <WorkerVersionDetails :worker-version-id="transcription.worker_version_id" :has-dropdown-title="true" /> - </div> <Box :transcription="transcription" :worker-version-filter="workerVersionFilter" /> </div> </template> @@ -17,7 +14,6 @@ import { mapState, mapActions } from 'vuex' import { MANUAL_WORKER_VERSION } from '~/js/config' import Box from './Box' -import WorkerVersionDetails from '~/vue/Process/Workers/Versions/Details' export default { props: { transcription: { @@ -26,8 +22,7 @@ export default { } }, components: { - Box, - WorkerVersionDetails + Box }, data: () => ({ workerVersionFilter: '' diff --git a/vue/Entity/Links.vue b/vue/Entity/Links.vue index c1dda2ea059b5dbb8c0a52159424531f3766f110..f30551d7a8d5750cb1b4b71a2cdf62e1fb0fbff9 100644 --- a/vue/Entity/Links.vue +++ b/vue/Entity/Links.vue @@ -1,30 +1,33 @@ <template> - <DropdownContent v-if="allLinks" title="roles" :disabled="!allLinks.length"> - <nav class="panel"> - <p class="panel-heading"> - {{ allLinks.length }} role{{ allLinks.length > 1 ? 's' : '' }} - </p> - <a v-for="link in allLinks" :key="link.id" class="panel-block"> - <span class="panel-icon"> - <i :class="ENTITY_TYPES[link.parent.type].icon"></i> - </span> - <span class="metadata-content"> - <router-link :to="{ name: 'entity-details', params: { id: link.parent.id } }"> - {{ link.parent.name }} - </router-link> - </span> - <span class="metadata-content">{{ link.role.parent_name }} - {{ link.role.child_name }}</span> - <span class="metadata-content"> - <router-link :to="{ name: 'entity-details', params: { id: link.child.id } }"> - {{ link.child.name }} - </router-link> - </span> - <span class="panel-icon"> - <i :class="ENTITY_TYPES[link.child.type].icon"></i> - </span> - </a> - </nav> - </DropdownContent> + <div v-if="allLinks && allLinks.length"> + <DropdownContent title="roles"> + <nav class="panel"> + <p class="panel-heading"> + {{ allLinks.length }} role{{ allLinks.length > 1 ? 's' : '' }} + </p> + <a v-for="link in allLinks" :key="link.id" class="panel-block"> + <span class="panel-icon"> + <i :class="ENTITY_TYPES[link.parent.type].icon"></i> + </span> + <span class="metadata-content"> + <router-link :to="{ name: 'entity-details', params: { id: link.parent.id } }"> + {{ link.parent.name }} + </router-link> + </span> + <span class="metadata-content">{{ link.role.parent_name }} - {{ link.role.child_name }}</span> + <span class="metadata-content"> + <router-link :to="{ name: 'entity-details', params: { id: link.child.id } }"> + {{ link.child.name }} + </router-link> + </span> + <span class="panel-icon"> + <i :class="ENTITY_TYPES[link.child.type].icon"></i> + </span> + </a> + </nav> + </DropdownContent> + <hr /> + </div> </template> <script> diff --git a/vue/Process/Workers/Versions/Details.vue b/vue/Process/Workers/Versions/Details.vue index d7f47adeb32c7ebac738914758d8ea4a1d2f461a..0cf034f524e4d42c9bfe0a3302f8e74475af3964 100644 --- a/vue/Process/Workers/Versions/Details.vue +++ b/vue/Process/Workers/Versions/Details.vue @@ -1,9 +1,10 @@ <template> <span v-if="!version" class="button is-loading empty-button"></span> <span v-else> + <span v-if="hasOutsideTitle">Created by <strong>{{ version.worker.name }}</strong></span> <div class="dropdown is-hoverable" :class="{ 'is-right': dropdownPosition === 'right' }"> <strong class="dropdown-trigger"> - <abbr title v-if="!hasDropdownTitle">{{ version.worker.name }}</abbr> + <abbr title v-if="!hasOutsideTitle && !hasDropdownTitle">{{ version.worker.name }}</abbr> <abbr title v-else><i class="icon-cog-alt"></i></abbr> </strong> <div class="dropdown-menu"> @@ -106,6 +107,10 @@ export default { type: Boolean, default: false }, + hasOutsideTitle: { + type: Boolean, + default: false + }, dropdownPosition: { type: String, default: 'right',