feat(riot-mui): upgrade riot mui and all dependencies (#279)

I [forked riot-mui](https://github.com/Joxit/riot-5-mui) in 2021 to be compatible with [riot.js 5+](https://riot.js.org/) because they change a lot of stuff. 

This was bundled in 2.0.0 of docker-registry-ui (see https://github.com/Joxit/docker-registry-ui/pull/176)

Now im improving riot-mui's DX to be more component oriented and add new features in the library.

Major changes:
* CTRL + click on buttons (catalog <=> taglist; taglist <=> tag-history)
* Fix history multi-arch tabs
* Fix tag list pagination (creation date missing) 

This is still a work in progress but I'm integrating this in Docker-Registry-UI. It will help to have new features like dark mode or custom UI.

Stay tuned!
This commit is contained in:
Jones Magloire 2022-12-31 10:15:14 +01:00 committed by GitHub
commit a0dcc84ca6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 277 additions and 251 deletions

View File

@ -1,6 +1,7 @@
{
"name": "docker-registry-ui",
"version": "2.3.3",
"type": "module",
"scripts": {
"format": "npm run format-html && npm run format-js && npm run format-riot",
"format-html": "find src rollup rollup.config.js -name '*.html' -exec prettier --config .prettierrc -w --parser html {} \\;",
@ -18,28 +19,28 @@
"license": "AGPL-3.0",
"description": "A web UI for private docker registry",
"devDependencies": {
"@babel/core": "^7.17.9",
"@babel/preset-env": "^7.16.0",
"@riotjs/compiler": "^6.1.3",
"@babel/core": "^7.20.5",
"@babel/preset-env": "^7.20.2",
"@riotjs/compiler": "^6.4.2",
"@riotjs/observable": "^4.1.1",
"@riotjs/route": "^8.0.1",
"@rollup/plugin-babel": "^5.2.2",
"@rollup/plugin-commonjs": "^21.1.0",
"@rollup/plugin-html": "^0.2.4",
"@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-node-resolve": "^13.2.1",
"core-js": "^3.22.0",
"node-sass": "^7.0.1",
"prettier": "^2.6.2",
"riot": "^6.1.2",
"riot-mui": "github:joxit/riot-5-mui#4d68d7f",
"rollup": "^2.70.2",
"@riotjs/route": "^8.0.2",
"@rollup/plugin-babel": "^6.0.3",
"@rollup/plugin-commonjs": "^23.0.3",
"@rollup/plugin-html": "^1.0.1",
"@rollup/plugin-json": "^5.0.2",
"@rollup/plugin-node-resolve": "^15.0.1",
"core-js": "^3.26.1",
"node-sass": "^8.0.0",
"prettier": "^2.8.0",
"riot": "^7.1.0",
"riot-mui": "github:joxit/riot-5-mui#a9b0ce4",
"rollup": "^3.5.1",
"rollup-plugin-app-utils": "^1.0.6",
"rollup-plugin-commonjs": "^10.1.0",
"rollup-plugin-copy": "^3.4.0",
"rollup-plugin-riot": "^6.0.0",
"rollup-plugin-scss": "^3.0.0",
"rollup-plugin-serve": "^1.1.0",
"rollup-plugin-scss": "^4.0.0",
"rollup-plugin-serve": "^2.0.2",
"rollup-plugin-styles": "^4.0.0",
"rollup-plugin-terser": "^7.0.2"
}

View File

@ -7,12 +7,12 @@ import { babel } from '@rollup/plugin-babel';
import scss from 'rollup-plugin-scss';
import serve from 'rollup-plugin-serve';
import html from '@rollup/plugin-html';
import htmlUseref from './rollup/html-useref';
import htmlUseref from './rollup/html-useref.js';
import json from '@rollup/plugin-json';
import copy from 'rollup-plugin-copy';
import copyTransform from './rollup/copy-transform';
import license from './rollup/license';
import checkOutput from './rollup/check-output';
import copyTransform from './rollup/copy-transform.js';
import license from './rollup/license.js';
import checkOutput from './rollup/check-output.js';
const useServe = process.env.ROLLUP_SERVE === 'true';
const output = useServe ? '.serve' : 'dist';
@ -22,7 +22,7 @@ const plugins = [
json(),
nodeResolve(),
commonjs(),
scss({ output: `./${output}/docker-registry-ui.css`, outputStyle: 'compressed' }),
scss({ fileName: `docker-registry-ui.css`, outputStyle: 'compressed' }),
babel({ babelHelpers: 'bundled', presets: [['@babel/env', { useBuiltIns: 'usage', corejs: { version: '2' } }]] }),
copy({
targets: [

View File

@ -21,7 +21,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
if="{!props.filterResults || state.nImages > 0 || matchSearch(props.filterResults, state.image)}"
>
<material-card class="list highlight" expanded="{state.expanded}" onclick="{ onClick }">
<material-waves center="true" color="#ddd"></material-waves>
<a if="{ state.image }" href="{ router.taglist(state.image) }">
<material-waves center="true" color="#ddd"></material-waves>
</a>
<material-waves if="{ state.images }" center="true" color="#ddd"></material-waves>
<span>
<i class="material-icons">send</i>
{ state.image || state.repo }
@ -76,9 +79,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
},
onClick() {
const state = this.state;
if (!state.repo) {
router.taglist(state.image);
} else {
if (state.repo) {
this.update({
expanded: !this.state.expanded,
expanding: true,
@ -112,6 +113,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
oReq.send();
},
matchSearch,
router,
};
</script>
<!-- End of tag -->

View File

@ -16,16 +16,16 @@
-->
<add-registry-url>
<material-popup opened="{ props.opened }" onClick="{ props.onClose }">
<div slot="title">Add your Server ?</div>
<div slot="content">
<material-input onkeyup="{ onKeyUp }" placeholder="Server URL"></material-input>
<div class="material-popup-title">Add your Server ?</div>
<div class="material-popup-content">
<material-input onkeyup="{ onKeyUp }" label="Server URL" label-color="#666" valid="{ registryUrlValidator }"></material-input>
<span>Write your URL without /v2</span>
</div>
<div slot="action">
<material-button class="dialog-button" waves-color="rgba(158,158,158,.4)" onClick="{ add }">
<div class="material-popup-action">
<material-button class="dialog-button" waves-color="rgba(158,158,158,.4)" onClick="{ add }" color="#000" inverted>
Add
</material-button>
<material-button class="dialog-button" waves-color="rgba(158,158,158,.4)" onClick="{ props.onClose }">
<material-button class="dialog-button" waves-color="rgba(158,158,158,.4)" onClick="{ props.onClose }" color="#000" inverted>
Cancel
</material-button>
</div>
@ -55,6 +55,9 @@
this.props.onClose();
setTimeout(() => router.updateUrlQueryParam(url), 100);
},
registryUrlValidator(input) {
return /^https?:\/\//.test(input) && !/\/v2\/?$/.test(input)
}
};
</script>
</add-registry-url>

View File

@ -15,18 +15,18 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<change-registry-url>
<material-popup opened="{ props.opened }" onClick="{ props.onClick }">
<div slot="title">Change your Server ?</div>
<div slot="content">
<material-popup opened="{ props.opened }" onClick="{ props.onClose }">
<div class="material-popup-title">Change your Server ?</div>
<div class="material-popup-content">
<select>
<option each="{ url in getRegistryServers() }" value="{ url }">{ url }</option>
</select>
</div>
<div slot="action">
<material-button class="dialog-button" waves-color="rgba(158,158,158,.4)" onClick="{ change }">
<div class="material-popup-action">
<material-button class="dialog-button" waves-color="rgba(158,158,158,.4)" onClick="{ change }" color="#000" inverted>
Change
</material-button>
<material-button class="dialog-button" waves-color="rgba(158,158,158,.4)" onClick="{ props.onClose }">
<material-button class="dialog-button" waves-color="rgba(158,158,158,.4)" onClick="{ props.onClose }" color="#000" inverted>
Cancel
</material-button>
</div>
@ -62,6 +62,7 @@
background: 0 0;
border: none;
font-weight: 400;
font-size: 1em;
line-height: 24px;
height: 24px;
border-bottom: 1px solid #2f6975;

View File

@ -16,17 +16,17 @@
-->
<confirm-delete-image>
<material-popup opened="{ props.opened }" onClick="{ props.onClick }">
<div slot="title">These images will be deleted</div>
<div slot="content">
<div class="material-popup-title">These images will be deleted</div>
<div class="material-popup-content">
<ul>
<li each="{ image in displayImagesToDelete(props.toDelete, props.tags) }">{ image.name }:{ image.tag }</li>
</ul>
</div>
<div slot="action">
<material-button class="dialog-button" waves-color="rgba(158,158,158,.4)" onClick="{ deleteImages }">
<div class="material-popup-action">
<material-button class="dialog-button" waves-color="rgba(158,158,158,.4)" onClick="{ deleteImages }" color="#000" inverted>
Delete
</material-button>
<material-button class="dialog-button" waves-color="rgba(158,158,158,.4)" onClick="{ props.onClick }">
<material-button class="dialog-button" waves-color="rgba(158,158,158,.4)" onClick="{ props.onClick }" color="#000" inverted>
Cancel
</material-button>
</div>

View File

@ -35,15 +35,23 @@
on-notify="{ props.onNotify }"
on-server-change="{ props.onServerChange }"
></remove-registry-url>
<div class="container">
<material-button onClick="{ onClick }" waves-center="true" rounded="true" waves-opacity="0.6" waves-duration="600">
<div class="material-dropdown-wrapper">
<material-button
onClick="{ onClick }"
waves-center="true"
waves-opacity="0.6"
waves-duration="600"
color="rgba(0,0,0,0)"
text-color="#fff"
icon
>
<i class="material-icons">more_vert</i>
</material-button>
<material-dropdown-list
<material-dropdown
items="{ dropdownItems.filter(item => item.ro || !props.readOnlyRegistries) }"
onSelect="{ onDropdownSelect }"
on-click="{ onDropdownSelect }"
opened="{ state.isDropdownOpened }"
/>
></material-dropdown>
</div>
<div class="overlay" onclick="{ onClick }" if="{ state.isDropdownOpened }"></div>
<script>
@ -74,9 +82,9 @@
ro: false,
},
],
onDropdownSelect(key, item) {
onDropdownSelect(event) {
this.update({
[item.name]: true,
[event.target.item]: true,
isDropdownOpened: false,
});
},
@ -96,15 +104,17 @@
};
</script>
<style>
:host > .container {
position: absolute;
top: 0px;
right: 16px;
:host > .material-dropdown-wrapper {
color: #000;
list-style-type: disc;
margin-block-start: 0.7em;
}
:host .material-dropdown-wrapper material-dropdown .material-dropdown-container {
right: 0;
top: 2em;
}
:host .overlay {
position: fixed;
height: 100%;
@ -114,33 +124,15 @@
z-index: 10;
}
:host material-button {
background: rgba(255, 255, 255, 0);
:host material-button button {
float: right;
z-index: 2;
}
:host material-button .content i.material-icons {
color: #fff;
font-size: 24px;
}
:host material-dropdown-list {
display: inline-block;
position: relative;
}
:host material-dropdown-list ul.dropdown-content {
min-width: 156px;
padding: 8px 0;
margin: 0;
}
:host material-dropdown-list ul.dropdown-content li span {
font-size: 1rem;
line-height: 1.2em;
}
:host material-popup * {
line-height: 1em;
}

View File

@ -16,17 +16,18 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<remove-registry-url>
<material-popup opened="{ props.opened }" onClick="{ props.onClose }">
<div slot="title">Remove your Registry Server ?</div>
<div slot="content">
<div class="material-popup-title">Remove your Registry Server ?</div>
<div class="material-popup-content">
<ul class="list">
<li each="{ url in getRegistryServers() }">
<span>
<material-button
onClick="{ remove }"
onClick="{ remove(url) }"
url="{ url }"
rounded="true"
waves-color="rgba(158,158,158,.4)"
waves-center="true"
inverted
icon
>
<i class="material-icons">delete</i>
</material-button>
@ -35,8 +36,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</li>
</ul>
</div>
<div slot="action">
<material-button class="dialog-button" waves-color="rgba(158,158,158,.4)" onClick="{ props.onClose }">
<div class="material-popup-action">
<material-button class="dialog-button" waves-color="rgba(158,158,158,.4)" onClick="{ props.onClose }" color="#000" inverted>
Close
</material-button>
</div>
@ -44,10 +45,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<script>
import { getRegistryServers, removeRegistryServers } from '../../scripts/utils';
export default {
remove(event) {
const url = event.currentTarget.attributes.url && event.currentTarget.attributes.url.value;
removeRegistryServers(url);
setTimeout(() => this.update(), 100);
remove(url) {
return (event) => {
console.log(url, event)
removeRegistryServers(url);
setTimeout(() => this.update(), 100);
}
},
getRegistryServers,
};

View File

@ -17,15 +17,17 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<docker-registry-ui>
<header>
<material-navbar>
<div class="logo">Docker Registry UI</div>
<search-bar on-search="{ onSearch }"></search-bar>
<dialogs-menu
if="{props.singleRegistry !== 'true'}"
on-notify="{ notifySnackbar }"
on-server-change="{ onServerChange }"
default-registries="{ props.defaultRegistries }"
read-only-registries="{ truthy(props.readOnlyRegistries) }"
></dialogs-menu>
<span class="logo">Docker Registry UI</span>
<div class="menu">
<search-bar on-search="{ onSearch }"></search-bar>
<dialogs-menu
if="{props.singleRegistry !== 'true'}"
on-notify="{ notifySnackbar }"
on-server-change="{ onServerChange }"
default-registries="{ props.defaultRegistries }"
read-only-registries="{ truthy(props.readOnlyRegistries) }"
></dialogs-menu>
</div>
</material-navbar>
</header>
<main>
@ -81,9 +83,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<material-snackbar message="{ state.snackbarMessage }" is-error="{ state.snackbarIsError }"></material-snackbar>
</main>
<footer>
<material-footer>
<a slot="logo" href="https://joxit.github.io/docker-registry-ui/">Docker Registry UI { version }</a>
<ul slot="link-list">
<material-footer mini="true">
<a class="material-footer-logo" href="https://joxit.github.io/docker-registry-ui/">Docker Registry UI { version }</a>
<ul>
<li>
<a href="https://github.com/Joxit/docker-registry-ui">Contribute on GitHub</a>
</li>
@ -197,4 +199,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
stringToArray,
};
</script>
<style>
material-navbar {
height: 64px;
}
material-navbar .menu {
display: flex;
}
</style>
</docker-registry-ui>

View File

@ -1,5 +1,5 @@
<search-bar>
<material-input placeholder="Search in page"></material-input>
<material-input label="Search in page" text-color="#fff" label-color="#aaa"></material-input>
<script>
import { router } from '@riotjs/route';
@ -39,10 +39,9 @@
</script>
<style>
:host material-input {
position: absolute;
top: 0em;
right: 64px;
line-height: initial;
max-width: 20%;
min-width: 13em;
}
@media screen and (max-width: 400px) {

View File

@ -15,9 +15,17 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<tag-history>
<material-card ref="tag-history-tag" class="tag-history header">
<material-card>
<div class="material-card-title-action">
<material-button waves-center="true" rounded="true" waves-color="#ddd" onClick="{ toTaglist }">
<material-button
color="#777"
waves-center="true"
rounded="true"
waves-color="#ddd"
href="{ toTaglist() }"
inverted
icon
>
<i class="material-icons">arrow_back</i>
</material-button>
<h2>History of { props.image }:{ props.tag } <i class="material-icons">history</i></h2>
@ -29,12 +37,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<material-tabs
if="{ state.archs && state.loadend }"
color="#25313b"
text-color="#fff"
useLine="{ true }"
tabs="{ state.archs }"
onTabChanged="{ onTabChanged }"
inverted
></material-tabs>
<material-card each="{ element in state.elements }" class="tag-history-element">
<material-card each="{ element in state.elements }">
<tag-history-element
each="{ entry in element }"
if="{ entry.value && entry.value.length > 0}"
@ -128,7 +139,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
});
},
toTaglist() {
router.taglist(this.props.image);
return router.taglist(this.props.image);
},
};
const eltIdx = function (e) {

View File

@ -18,11 +18,13 @@
<div class="copy-to-clipboard">
<input style="display: none; width: 1px; height: 1px" value="{ getDockerCmd(props) }" />
<material-button
waves-center="true"
rounded="true"
color="rgba(0,0,0,0)"
text-color="#777"
waves-color="#ddd"
waves-center="true"
onClick="{ copy }"
title="Copy pull command."
icon
>
<i class="material-icons">content_copy</i>
</material-button>

View File

@ -20,13 +20,21 @@
import { dateFormat } from '../../scripts/utils';
export default {
onMounted(props) {
props.image.one('creation-date', (date) => {
this.update({
date: date,
localDate: date && date.toLocaleString(),
this.loadCreationDate(props);
},
onUpdated(props) {
this.loadCreationDate(props);
},
loadCreationDate(props) {
if (!props.image.creationDate && !props.image.ociImage) {
props.image.one('creation-date', (date) => {
this.update({
date: date,
localDate: date && date.toLocaleString(),
});
});
});
props.image.trigger('get-date');
props.image.trigger('get-date');
}
},
getDate(image) {
return !image.ociImage ? `${dateFormat(image.creationDate)} ago` : 'Not Available';

View File

@ -15,19 +15,56 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<pagination>
<div class="conatianer">
<div class="container">
<div class="pagination-centered">
<material-button
aria-label="page-{ p.page }"
color="rgba(0, 0, 0, { p.current ? 0.12 : 0 } )"
text-color="#000"
waves-color="rgba(158,158,158,.4)"
each="{ (p, idx) in props.pages}"
class="{ p.current ? 'current' : ''} { p['space-left'] ? 'space-left' : '' } { p['space-right'] ? 'space-right' : ''}"
onClick="{() => props.onPageUpdate(idx)}"
onClick="{(e) => props.onPageUpdate(idx)}"
outlined
>
<i if="{ p.icon }" class="material-icons">{ p.icon }</i>
<div if="{ !p.icon }">{ p.page }</div>
<template if="{ !p.icon }">{ p.page }</template>
</material-button>
</div>
</div>
<script></script>
<style>
:host .container {
display: flex;
}
:host .container .pagination-centered {
margin: auto;
}
:host material-button > :first-child {
padding: 0;
min-width: 40px;
}
:host material-button > :first-child .content {
display: flex;
align-content: center;
flex-direction: column;
font-size: 16px;
line-height: 42px;
}
:host material-button.current > :first-child.space-left {
margin-left: 85px;
}
:host material-button.current > :first-child.space-right {
margin-right: 85px;
}
:host material-button .content i.material-icons {
height: unset;
}
</style>
</pagination>

View File

@ -16,13 +16,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<remove-image>
<material-button
waves-center="true"
rounded="true"
color="rgba(0,0,0,0)"
text-color="#777"
waves-color="#ddd"
waves-center="true"
title="This will delete the image."
if="{ !props.multiDelete }"
disabled="{ !state.contentDigest }"
onClick="{ deleteImage }"
icon
>
<i class="material-icons">delete</i>
</material-button>
@ -56,8 +58,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
deleteImage() {
this.props.handleCheckboxChange(ACTION_DELETE_IMAGE, this.props.image);
},
handleCheckboxChange(checked) {
const action = checked ? ACTION_CHECK_TO_DELETE : ACTION_UNCHECK_TO_DELETE;
handleCheckboxChange(event) {
const action = event.target.checked ? ACTION_CHECK_TO_DELETE : ACTION_UNCHECK_TO_DELETE;
this.props.handleCheckboxChange(action, this.props.image);
},
};

View File

@ -17,11 +17,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<tag-history-button>
<material-button
title="{ buttonTittle() }"
waves-center="true"
rounded="true"
color="rgba(0,0,0,0)"
text-color="#777"
waves-color="#ddd"
onClick="{ routeToHistory }"
waves-center="true"
href="{ routeToHistory() }"
disabled="{ props.image.ociImage }"
icon
>
<i class="material-icons">history</i>
</material-button>
@ -40,7 +42,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
},
routeToHistory() {
if (!this.props.image.ociImage) {
router.history(this.props.image.name, this.props.image.tag);
return router.history(this.props.image.name, this.props.image.tag);
}
},
};

View File

@ -17,7 +17,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<tag-list>
<material-card class="header">
<div class="material-card-title-action">
<material-button waves-center="true" rounded="true" waves-color="#ddd" onClick="{ router.home }">
<material-button
color="#777"
waves-color="#ddd"
waves-center="true"
href="{ router.home() }"
icon
inverted
>
<i class="material-icons">arrow_back</i>
</material-button>
<h2>

View File

@ -63,12 +63,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
>
</material-checkbox>
<material-button
if="{ state.toDelete.size > 0 && !state.singleDeleteAction }"
waves-center="true"
rounded="true"
color="#fff"
text-color="#777"
waves-color="#ddd"
title="This will delete selected images."
onClick="{ deleteImages }"
if="{ state.toDelete.size > 0 && !state.singleDeleteAction }"
icon
>
<i class="material-icons">delete</i>
</material-button>
@ -78,31 +80,31 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<tbody>
<tr each="{ image in getPage(props.tags, props.page) }" if="{ matchSearch(props.filterResults, image.tag) }">
<td class="creation-date">
<image-date image="{ image }" />
<image-date image="{ image }"></image-date>
</td>
<td class="image-size">
<image-size image="{ image }" />
<image-size image="{ image }"></image-size>
</td>
<td if="{ props.showContentDigest }">
<image-content-digest image="{ image }" />
<image-content-digest image="{ image }"></image-content-digest>
<copy-to-clipboard
target="digest"
image="{ image }"
pull-url="{ props.pullUrl }"
on-notify="{ props.onNotify }"
/>
></copy-to-clipboard>
</td>
<td>
<image-tag image="{ image }" />
<image-tag image="{ image }"></image-tag>
<copy-to-clipboard
target="tag"
image="{ image }"
pull-url="{ props.pullUrl }"
on-notify="{ props.onNotify }"
/>
></copy-to-clipboard>
</td>
<td class="show-tag-history">
<tag-history-button image="{ image }" />
<tag-history-button image="{ image }"></tag-history-button>
</td>
<td if="{ props.isImageRemoveActivated }" class="remove-tag">
<remove-image
@ -113,7 +115,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
checked="{ state.toDelete.has(image) }"
on-notify="{ props.onNotify }"
on-authentication="{ props.onAuthentication }"
/>
></remove-image>
</td>
</tr>
</tbody>
@ -172,7 +174,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
confirmDeleteImage: false,
});
},
onRemoveImageHeaderChange(checked, event) {
onRemoveImageHeaderChange(event) {
if (event.altKey === true) {
const tags = getPage(this.props.tags, this.props.page);
tags
@ -184,7 +186,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
});
} else {
this.update({
multiDelete: checked,
multiDelete: event.target.checked,
});
}
},

View File

@ -10,7 +10,7 @@ import {
MaterialCheckbox,
MaterialTabs,
MaterialSnackbar,
MaterialDropdownList,
MaterialDropdown,
MaterialPopup,
MaterialInput,
} from 'riot-mui';
@ -28,7 +28,7 @@ register('material-waves', MaterialWaves);
register('material-checkbox', MaterialCheckbox);
register('material-snackbar', MaterialSnackbar);
register('material-tabs', MaterialTabs);
register('material-dropdown-list', MaterialDropdownList);
register('material-dropdown', MaterialDropdown);
register('material-popup', MaterialPopup);
register('material-input', MaterialInput);

View File

@ -14,7 +14,7 @@
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { router, getCurrentRoute } from '@riotjs/route';
import { getCurrentRoute } from '@riotjs/route';
import { encodeURI, decodeURI } from './utils';
function getQueryParams() {
@ -59,16 +59,16 @@ function baseUrl(qs) {
export default {
home() {
router.push(baseUrl({ page: null }));
return baseUrl({ page: null });
},
taglist(image) {
router.push(`${baseUrl({ page: null })}#!/taglist/${image}`);
return `${baseUrl({ page: null })}#!/taglist/${image}`;
},
getTagListImage() {
return getCurrentRoute().replace(/^.*(#!)?\/?taglist\//, '');
},
history(image, tag) {
router.push(`${baseUrl({ page: null })}#!/taghistory/image/${image}/tag/${tag}`);
return `${baseUrl({ page: null })}#!/taghistory/image/${image}/tag/${tag}`;
},
getTagHistoryImage() {
return getCurrentRoute().replace(/^.*(#!)?\/?taghistory\/image\/(.*)\/tag\/(.*)\/?$/, '$2');

View File

@ -23,18 +23,20 @@
@import 'riot-mui/src/material-elements/material-checkbox/material-checkbox.scss';
@import 'riot-mui/src/material-elements/material-tabs/material-tabs.scss';
@import 'riot-mui/src/material-elements/material-snackbar/material-snackbar.scss';
@import 'riot-mui/src/material-elements/material-dropdown-list/material-dropdown-list.scss';
@import 'riot-mui/src/material-elements/material-dropdown/material-dropdown.scss';
@import 'riot-mui/src/material-elements/material-popup/material-popup.scss';
@import 'riot-mui/src/material-elements/material-input/material-input.scss';
@import './roboto.scss';
@import './material-icons.scss';
html > body {
font-family: 'Roboto', 'Helvetica', 'Arial', sans-serif !important;
font-size: 16px;
}
html, body {
html,
body {
margin: 0;
height: 100%;
}
@ -59,44 +61,35 @@ html, body {
font-weight: inherit;
}
material-card, material-tabs, pagination .conatianer {
material-card,
material-tabs,
pagination .container {
max-width: 95%;
margin: auto;
margin-top: 20px;
margin-bottom: 20px;
}
pagination .conatianer {
display: flex;
display: -moz-flex;
display: -webkit-flex;
display: -ms-flexbox;
}
pagination .conatianer .pagination-centered {
margin: auto;
}
/* 1515px * 0.95 = 1440px */
@media screen and (min-width: 1515px){
material-card, material-tabs, pagination .conatianer {
@media screen and (min-width: 1515px) {
material-card,
material-tabs,
pagination .container {
max-width: 1440px;
}
}
material-tabs {
display: block;
-webkit-box-shadow: 0 2px 5px 0 rgba(0,0,0,.16), 0 2px 10px 0 rgba(0,0,0,.12);
-ms-box-shadow: 0 2px 5px 0 rgba(0,0,0,.16),0 2px 10px 0 rgba(0,0,0,.12);
-moz-box-shadow: 0 2px 5px 0 rgba(0,0,0,.16),0 2px 10px 0 rgba(0,0,0,.12);
-o-box-shadow: 0 2px 5px 0 rgba(0,0,0,.16),0 2px 10px 0 rgba(0,0,0,.12);
box-shadow: 0 2px 5px 0 rgba(0,0,0,.16), 0 2px 10px 0 rgba(0,0,0,.12);
-webkit-box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.16), 0 2px 10px 0 rgba(0, 0, 0, 0.12);
-ms-box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.16), 0 2px 10px 0 rgba(0, 0, 0, 0.12);
-moz-box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.16), 0 2px 10px 0 rgba(0, 0, 0, 0.12);
-o-box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.16), 0 2px 10px 0 rgba(0, 0, 0, 0.12);
box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.16), 0 2px 10px 0 rgba(0, 0, 0, 0.12);
}
material-tabs material-button,
material-tabs material-button .content .text {
background-color: #fff;
color: #aaa;
text-transform: none;
}
@ -117,20 +110,12 @@ material-spinner {
flex-direction: column;
}
material-navbar {
height: 64px;
}
material-navbar nav-wrapper {
display: flex;
}
.logo {
padding: 0 16px 0 72px;
text-decoration: none;
font-size: 20px;
line-height: 1;
letter-spacing: .02em;
letter-spacing: 0.02em;
font-weight: 400;
}
@ -169,18 +154,22 @@ h2 {
overflow: hidden;
}
docker-registry-ui material-button > :first-child .content i.material-icons,
docker-registry-ui material-button > :first-child .content i.material-icons.material-icons {
font-size: 24px;
}
.list > span i.material-icons,
.list > li i.material-icons {
margin-right: 32px;
height: 24px;
width: 24px;
font-size: 24px;
box-sizing: border-box;
color: #757575;
}
.list > span .right i.material-icons.animated {
transition: all 350ms cubic-bezier(.4,0,.2,1);
transition: all 350ms cubic-bezier(0.4, 0, 0.2, 1);
margin-right: 10px;
}
@ -237,7 +226,7 @@ material-card table {
width: 100%;
border: none;
position: relative;
border: 1px solid rgba(0, 0, 0, .12);
border: 1px solid rgba(0, 0, 0, 0.12);
border-collapse: collapse;
white-space: nowrap;
font-size: 13px;
@ -250,7 +239,7 @@ material-card table th {
vertical-align: bottom;
line-height: 24px;
height: 48px;
color: rgba(0, 0, 0, .54);
color: rgba(0, 0, 0, 0.54);
box-sizing: border-box;
padding: 0 18px 12px 18px;
text-align: right;
@ -260,17 +249,16 @@ material-card table th {
text-align: left;
}
material-card material-button:hover,
material-card table tbody tr:hover,
pagination material-button:hover {
background-color: #eee;
material-button:hover > :first-child[inverted='true'],
material-card .material-card-title-action material-button:hover button,
material-card table tbody tr:hover {
background-color: #eee !important;
}
material-card material-button,
material-card table tbody tr,
pagination material-button {
transition-duration: .28s;
transition-timing-function: cubic-bezier(.4, 0, .2, 1);
material-button > :first-child[inverted='true'],
material-card table tbody tr {
transition-duration: 0.28s;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-property: background-color;
}
@ -283,8 +271,8 @@ material-card table td {
font-size: 16px;
position: relative;
height: 48px;
border-top: 1px solid rgba(0, 0, 0, .12);
border-bottom: 1px solid rgba(0, 0, 0, .12);
border-top: 1px solid rgba(0, 0, 0, 0.12);
border-bottom: 1px solid rgba(0, 0, 0, 0.12);
padding: 12px 18px;
box-sizing: border-box;
vertical-align: middle;
@ -292,27 +280,30 @@ material-card table td {
}
tag-history-button button:hover,
material-card table th.material-card-th-sorted-ascending:hover, material-card table th.material-card-th-sorted-descending:hover {
material-card table th.material-card-th-sorted-ascending:hover,
material-card table th.material-card-th-sorted-descending:hover {
cursor: pointer;
}
material-card table th.material-card-th-sorted-ascending:hover:before, material-card table th.material-card-th-sorted-descending:hover:before {
color: rgba(0, 0, 0, .26);
material-card table th.material-card-th-sorted-ascending:hover:before,
material-card table th.material-card-th-sorted-descending:hover:before {
color: rgba(0, 0, 0, 0.26);
}
material-card table th.material-card-th-sorted-ascending:before, material-card table th.material-card-th-sorted-descending:before {
material-card table th.material-card-th-sorted-ascending:before,
material-card table th.material-card-th-sorted-descending:before {
font-family: 'Material Icons';
font-weight: 400;
font-style: normal;
line-height: 1;
font-size: 16px;
content: "\e5d8";
content: '\e5d8';
margin-right: 5px;
vertical-align: sub;
}
material-card table th.material-card-th-sorted-descending:before {
content: "\e5db";
content: '\e5db';
}
material-button .content i.material-icons,
@ -329,19 +320,13 @@ material-snackbar .toast {
height: auto;
}
material-popup material-button,
pagination material-button {
background-color: #fff;
color: #000;
}
material-popup material-button:hover material-waves {
background-color: hsla(0, 0%, 75%, .2);
background-color: hsla(0, 0%, 75%, 0.2);
}
material-popup .popup {
material-popup .popup > .content {
padding: 1em;
max-width: 450px;
top: 2em;
}
footer {
@ -366,7 +351,8 @@ material-footer {
/* 5 + 2 + 3 + 24 + 3 + 2 + 18 */
padding-right: 57px;
}
image-tag, .copy-to-clipboard {
image-tag,
.copy-to-clipboard {
display: inline-block;
}
image-content-digest {
@ -405,28 +391,11 @@ taglist .image-size {
width: 7em;
}
catalog material-card,
tag-history material-card {
min-height: auto;
}
tag-history-button button {
background: none;
border: none;
}
material-card material-button,
pagination material-button {
max-height: 30px;
max-width: 30px;
}
material-button:hover material-waves {
background: none;
}
material-card material-button,
pagination material-button {
material-card material-button {
background-color: inherit;
}
@ -436,7 +405,7 @@ catalog-element material-card {
}
catalog-element catalog-element material-card {
transition: all 350ms cubic-bezier(.4,0,.2,1);
transition: all 350ms cubic-bezier(0.4, 0, 0.2, 1);
z-index: 1;
position: relative;
}
@ -451,7 +420,7 @@ catalog-element catalog-element > .content {
margin-left: 3em;
}
@media screen and (min-width: 1515px){
@media screen and (min-width: 1515px) {
catalog-element catalog-element > .content material-card {
max-width: calc(1440px - 3em);
}
@ -489,29 +458,3 @@ material-checkbox .checkbox {
material-checkbox .checkbox.checked {
background-color: #777;
}
pagination material-button {
padding: 0.2em 0.75em;
}
pagination material-button .content {
display: flex;
align-content: center;
line-height: 1.9em;
}
pagination material-button.current {
border: 1px solid rgba(0, 0, 0, .12);
}
pagination material-button.current.space-left {
margin-left: 85px;
}
pagination material-button.current.space-right {
margin-right: 85px;
}
pagination material-button .content i.material-icons {
color: #000;
}