diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-02-18 09:09:24 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-02-18 09:09:24 +0000 |
commit | 4720b569f0fcbb47e9f1a60e95172ae63b6f065a (patch) | |
tree | 5c6bcecbca227e608753a57a9aad19ccfe0567b6 /app/assets/javascripts/registry | |
parent | cefe554b7ce2d0b52f9de855be832a47c2bc24ab (diff) | |
download | gitlab-ce-4720b569f0fcbb47e9f1a60e95172ae63b6f065a.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/registry')
7 files changed, 117 insertions, 26 deletions
diff --git a/app/assets/javascripts/registry/explorer/components/registry_breadcrumb.vue b/app/assets/javascripts/registry/explorer/components/registry_breadcrumb.vue new file mode 100644 index 00000000000..f51948da8cc --- /dev/null +++ b/app/assets/javascripts/registry/explorer/components/registry_breadcrumb.vue @@ -0,0 +1,59 @@ +<script> +import { initial, first, last } from 'lodash'; + +export default { + props: { + crumbs: { + type: Array, + required: true, + }, + }, + computed: { + rootRoute() { + return this.$router.options.routes.find(r => r.meta.root); + }, + isRootRoute() { + return this.$route.name === this.rootRoute.name; + }, + rootCrumbs() { + return initial(this.crumbs); + }, + divider() { + const { classList, tagName, innerHTML } = first(this.crumbs).querySelector('svg'); + return { classList: [...classList], tagName, innerHTML }; + }, + lastCrumb() { + const { children } = last(this.crumbs); + const { tagName, classList } = first(children); + return { + tagName, + classList: [...classList], + text: this.$route.meta.nameGenerator(this.$route), + path: { to: this.$route.name }, + }; + }, + }, +}; +</script> + +<template> + <ul> + <li + v-for="(crumb, index) in rootCrumbs" + :key="index" + :class="crumb.classList" + v-html="crumb.innerHTML" + ></li> + <li v-if="!isRootRoute"> + <router-link ref="rootRouteLink" :to="rootRoute.path"> + {{ rootRoute.meta.nameGenerator(rootRoute) }} + </router-link> + <component :is="divider.tagName" :class="divider.classList" v-html="divider.innerHTML" /> + </li> + <li> + <component :is="lastCrumb.tagName" ref="lastCrumb" :class="lastCrumb.classList"> + <router-link ref="childRouteLink" :to="lastCrumb.path">{{ lastCrumb.text }}</router-link> + </component> + </li> + </ul> +</template> diff --git a/app/assets/javascripts/registry/explorer/index.js b/app/assets/javascripts/registry/explorer/index.js index daa2e4fb109..a36978303c6 100644 --- a/app/assets/javascripts/registry/explorer/index.js +++ b/app/assets/javascripts/registry/explorer/index.js @@ -1,6 +1,7 @@ import Vue from 'vue'; import Translate from '~/vue_shared/translate'; import RegistryExplorer from './pages/index.vue'; +import RegistryBreadcrumb from './components/registry_breadcrumb.vue'; import { createStore } from './stores'; import createRouter from './router'; @@ -19,15 +20,39 @@ export default () => { const router = createRouter(endpoint, store); store.dispatch('setInitialState', el.dataset); - return new Vue({ - el, - store, - router, - components: { - RegistryExplorer, - }, - render(createElement) { - return createElement('registry-explorer'); - }, - }); + const attachMainComponent = () => + new Vue({ + el, + store, + router, + components: { + RegistryExplorer, + }, + render(createElement) { + return createElement('registry-explorer'); + }, + }); + + const attachBreadcrumb = () => { + const breadCrumbEl = document.querySelector('nav .js-breadcrumbs-list'); + const crumbs = [...document.querySelectorAll('.js-breadcrumbs-list li')]; + return new Vue({ + el: breadCrumbEl, + store, + router, + components: { + RegistryBreadcrumb, + }, + render(createElement) { + return createElement('registry-breadcrumb', { + class: breadCrumbEl.className, + props: { + crumbs, + }, + }); + }, + }); + }; + + return { attachBreadcrumb, attachMainComponent }; }; diff --git a/app/assets/javascripts/registry/explorer/pages/details.vue b/app/assets/javascripts/registry/explorer/pages/details.vue index bff67bb8376..bc613db8672 100644 --- a/app/assets/javascripts/registry/explorer/pages/details.vue +++ b/app/assets/javascripts/registry/explorer/pages/details.vue @@ -19,6 +19,7 @@ import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; import { numberToHumanSize } from '~/lib/utils/number_utils'; import timeagoMixin from '~/vue_shared/mixins/timeago'; import Tracking from '~/tracking'; +import { decodeAndParse } from '../utils'; import { LIST_KEY_TAG, LIST_KEY_IMAGE_ID, @@ -62,7 +63,7 @@ export default { computed: { ...mapState(['tags', 'tagsPagination', 'isLoading', 'config']), imageName() { - const { name } = JSON.parse(window.atob(this.$route.params.id)); + const { name } = decodeAndParse(this.$route.params.id); return name; }, fields() { @@ -169,7 +170,7 @@ export default { }, handleSingleDelete(itemToDelete) { this.itemsToBeDeleted = []; - this.requestDeleteTag({ tag: itemToDelete, imageId: this.$route.params.id }); + this.requestDeleteTag({ tag: itemToDelete, params: this.$route.params.id }); }, handleMultipleDelete() { const { itemsToBeDeleted } = this; @@ -178,7 +179,7 @@ export default { this.requestDeleteTags({ ids: itemsToBeDeleted.map(x => this.tags[x].name), - imageId: this.$route.params.id, + params: this.$route.params.id, }); }, onDeletionConfirmed() { diff --git a/app/assets/javascripts/registry/explorer/pages/list.vue b/app/assets/javascripts/registry/explorer/pages/list.vue index dc730ac2828..1dbc7cc2242 100644 --- a/app/assets/javascripts/registry/explorer/pages/list.vue +++ b/app/assets/javascripts/registry/explorer/pages/list.vue @@ -70,7 +70,7 @@ export default { this.itemToDelete = {}; }, encodeListItem(item) { - const params = JSON.stringify({ name: item.path, tags_path: item.tags_path }); + const params = JSON.stringify({ name: item.path, tags_path: item.tags_path, id: item.id }); return window.btoa(params); }, }, diff --git a/app/assets/javascripts/registry/explorer/router.js b/app/assets/javascripts/registry/explorer/router.js index 8cf35b8f245..7e4c3d28623 100644 --- a/app/assets/javascripts/registry/explorer/router.js +++ b/app/assets/javascripts/registry/explorer/router.js @@ -1,8 +1,9 @@ import Vue from 'vue'; import VueRouter from 'vue-router'; -import { __ } from '~/locale'; +import { s__ } from '~/locale'; import List from './pages/list.vue'; import Details from './pages/details.vue'; +import { decodeAndParse } from './utils'; Vue.use(VueRouter); @@ -16,7 +17,8 @@ export default function createRouter(base, store) { path: '/', component: List, meta: { - name: __('Container Registry'), + nameGenerator: () => s__('ContainerRegistry|Container Registry'), + root: true, }, beforeEnter: (to, from, next) => { store.dispatch('requestImagesList'); @@ -28,10 +30,10 @@ export default function createRouter(base, store) { path: '/:id', component: Details, meta: { - name: __('Tags'), + nameGenerator: route => decodeAndParse(route.params.id).name, }, beforeEnter: (to, from, next) => { - store.dispatch('requestTagsList', { id: to.params.id }); + store.dispatch('requestTagsList', { params: to.params.id }); next(); }, }, diff --git a/app/assets/javascripts/registry/explorer/stores/actions.js b/app/assets/javascripts/registry/explorer/stores/actions.js index 25ff105ac53..86d00d4fca9 100644 --- a/app/assets/javascripts/registry/explorer/stores/actions.js +++ b/app/assets/javascripts/registry/explorer/stores/actions.js @@ -13,6 +13,7 @@ import { DELETE_IMAGE_ERROR_MESSAGE, DELETE_IMAGE_SUCCESS_MESSAGE, } from '../constants'; +import { decodeAndParse } from '../utils'; export const setInitialState = ({ commit }, data) => commit(types.SET_INITIAL_STATE, data); @@ -43,9 +44,9 @@ export const requestImagesList = ({ commit, dispatch, state }, pagination = {}) }); }; -export const requestTagsList = ({ commit, dispatch }, { pagination = {}, id }) => { +export const requestTagsList = ({ commit, dispatch }, { pagination = {}, params }) => { commit(types.SET_MAIN_LOADING, true); - const { tags_path } = JSON.parse(window.atob(id)); + const { tags_path } = decodeAndParse(params); const { page = DEFAULT_PAGE, perPage = DEFAULT_PAGE_SIZE } = pagination; return axios @@ -61,13 +62,13 @@ export const requestTagsList = ({ commit, dispatch }, { pagination = {}, id }) = }); }; -export const requestDeleteTag = ({ commit, dispatch, state }, { tag, imageId }) => { +export const requestDeleteTag = ({ commit, dispatch, state }, { tag, params }) => { commit(types.SET_MAIN_LOADING, true); return axios .delete(tag.destroy_path) .then(() => { createFlash(DELETE_TAG_SUCCESS_MESSAGE, 'success'); - dispatch('requestTagsList', { pagination: state.tagsPagination, id: imageId }); + dispatch('requestTagsList', { pagination: state.tagsPagination, params }); }) .catch(() => { createFlash(DELETE_TAG_ERROR_MESSAGE); @@ -77,15 +78,16 @@ export const requestDeleteTag = ({ commit, dispatch, state }, { tag, imageId }) }); }; -export const requestDeleteTags = ({ commit, dispatch, state }, { ids, imageId }) => { +export const requestDeleteTags = ({ commit, dispatch, state }, { ids, params }) => { commit(types.SET_MAIN_LOADING, true); - const url = `/${state.config.projectPath}/registry/repository/${imageId}/tags/bulk_destroy`; + const { id } = decodeAndParse(params); + const url = `/${state.config.projectPath}/registry/repository/${id}/tags/bulk_destroy`; return axios .delete(url, { params: { ids } }) .then(() => { createFlash(DELETE_TAGS_SUCCESS_MESSAGE, 'success'); - dispatch('requestTagsList', { pagination: state.tagsPagination, id: imageId }); + dispatch('requestTagsList', { pagination: state.tagsPagination, params }); }) .catch(() => { createFlash(DELETE_TAGS_ERROR_MESSAGE); diff --git a/app/assets/javascripts/registry/explorer/utils.js b/app/assets/javascripts/registry/explorer/utils.js new file mode 100644 index 00000000000..b1df87c6993 --- /dev/null +++ b/app/assets/javascripts/registry/explorer/utils.js @@ -0,0 +1,2 @@ +// eslint-disable-next-line import/prefer-default-export +export const decodeAndParse = param => JSON.parse(window.atob(param)); |