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", "name": "docker-registry-ui",
"version": "2.3.3", "version": "2.3.3",
"type": "module",
"scripts": { "scripts": {
"format": "npm run format-html && npm run format-js && npm run format-riot", "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 {} \\;", "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", "license": "AGPL-3.0",
"description": "A web UI for private docker registry", "description": "A web UI for private docker registry",
"devDependencies": { "devDependencies": {
"@babel/core": "^7.17.9", "@babel/core": "^7.20.5",
"@babel/preset-env": "^7.16.0", "@babel/preset-env": "^7.20.2",
"@riotjs/compiler": "^6.1.3", "@riotjs/compiler": "^6.4.2",
"@riotjs/observable": "^4.1.1", "@riotjs/observable": "^4.1.1",
"@riotjs/route": "^8.0.1", "@riotjs/route": "^8.0.2",
"@rollup/plugin-babel": "^5.2.2", "@rollup/plugin-babel": "^6.0.3",
"@rollup/plugin-commonjs": "^21.1.0", "@rollup/plugin-commonjs": "^23.0.3",
"@rollup/plugin-html": "^0.2.4", "@rollup/plugin-html": "^1.0.1",
"@rollup/plugin-json": "^4.1.0", "@rollup/plugin-json": "^5.0.2",
"@rollup/plugin-node-resolve": "^13.2.1", "@rollup/plugin-node-resolve": "^15.0.1",
"core-js": "^3.22.0", "core-js": "^3.26.1",
"node-sass": "^7.0.1", "node-sass": "^8.0.0",
"prettier": "^2.6.2", "prettier": "^2.8.0",
"riot": "^6.1.2", "riot": "^7.1.0",
"riot-mui": "github:joxit/riot-5-mui#4d68d7f", "riot-mui": "github:joxit/riot-5-mui#a9b0ce4",
"rollup": "^2.70.2", "rollup": "^3.5.1",
"rollup-plugin-app-utils": "^1.0.6", "rollup-plugin-app-utils": "^1.0.6",
"rollup-plugin-commonjs": "^10.1.0", "rollup-plugin-commonjs": "^10.1.0",
"rollup-plugin-copy": "^3.4.0", "rollup-plugin-copy": "^3.4.0",
"rollup-plugin-riot": "^6.0.0", "rollup-plugin-riot": "^6.0.0",
"rollup-plugin-scss": "^3.0.0", "rollup-plugin-scss": "^4.0.0",
"rollup-plugin-serve": "^1.1.0", "rollup-plugin-serve": "^2.0.2",
"rollup-plugin-styles": "^4.0.0", "rollup-plugin-styles": "^4.0.0",
"rollup-plugin-terser": "^7.0.2" "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 scss from 'rollup-plugin-scss';
import serve from 'rollup-plugin-serve'; import serve from 'rollup-plugin-serve';
import html from '@rollup/plugin-html'; 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 json from '@rollup/plugin-json';
import copy from 'rollup-plugin-copy'; import copy from 'rollup-plugin-copy';
import copyTransform from './rollup/copy-transform'; import copyTransform from './rollup/copy-transform.js';
import license from './rollup/license'; import license from './rollup/license.js';
import checkOutput from './rollup/check-output'; import checkOutput from './rollup/check-output.js';
const useServe = process.env.ROLLUP_SERVE === 'true'; const useServe = process.env.ROLLUP_SERVE === 'true';
const output = useServe ? '.serve' : 'dist'; const output = useServe ? '.serve' : 'dist';
@ -22,7 +22,7 @@ const plugins = [
json(), json(),
nodeResolve(), nodeResolve(),
commonjs(), 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' } }]] }), babel({ babelHelpers: 'bundled', presets: [['@babel/env', { useBuiltIns: 'usage', corejs: { version: '2' } }]] }),
copy({ copy({
targets: [ 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)}" if="{!props.filterResults || state.nImages > 0 || matchSearch(props.filterResults, state.image)}"
> >
<material-card class="list highlight" expanded="{state.expanded}" onclick="{ onClick }"> <material-card class="list highlight" expanded="{state.expanded}" onclick="{ onClick }">
<a if="{ state.image }" href="{ router.taglist(state.image) }">
<material-waves center="true" color="#ddd"></material-waves> <material-waves center="true" color="#ddd"></material-waves>
</a>
<material-waves if="{ state.images }" center="true" color="#ddd"></material-waves>
<span> <span>
<i class="material-icons">send</i> <i class="material-icons">send</i>
{ state.image || state.repo } { state.image || state.repo }
@ -76,9 +79,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
}, },
onClick() { onClick() {
const state = this.state; const state = this.state;
if (!state.repo) { if (state.repo) {
router.taglist(state.image);
} else {
this.update({ this.update({
expanded: !this.state.expanded, expanded: !this.state.expanded,
expanding: true, expanding: true,
@ -112,6 +113,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
oReq.send(); oReq.send();
}, },
matchSearch, matchSearch,
router,
}; };
</script> </script>
<!-- End of tag --> <!-- End of tag -->

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,5 +1,5 @@
<search-bar> <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> <script>
import { router } from '@riotjs/route'; import { router } from '@riotjs/route';
@ -39,10 +39,9 @@
</script> </script>
<style> <style>
:host material-input { :host material-input {
position: absolute; line-height: initial;
top: 0em;
right: 64px;
max-width: 20%; max-width: 20%;
min-width: 13em;
} }
@media screen and (max-width: 400px) { @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/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
--> -->
<tag-history> <tag-history>
<material-card ref="tag-history-tag" class="tag-history header"> <material-card>
<div class="material-card-title-action"> <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> <i class="material-icons">arrow_back</i>
</material-button> </material-button>
<h2>History of { props.image }:{ props.tag } <i class="material-icons">history</i></h2> <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 <material-tabs
if="{ state.archs && state.loadend }" if="{ state.archs && state.loadend }"
color="#25313b"
text-color="#fff"
useLine="{ true }" useLine="{ true }"
tabs="{ state.archs }" tabs="{ state.archs }"
onTabChanged="{ onTabChanged }" onTabChanged="{ onTabChanged }"
inverted
></material-tabs> ></material-tabs>
<material-card each="{ element in state.elements }" class="tag-history-element"> <material-card each="{ element in state.elements }">
<tag-history-element <tag-history-element
each="{ entry in element }" each="{ entry in element }"
if="{ entry.value && entry.value.length > 0}" if="{ entry.value && entry.value.length > 0}"
@ -128,7 +139,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
}); });
}, },
toTaglist() { toTaglist() {
router.taglist(this.props.image); return router.taglist(this.props.image);
}, },
}; };
const eltIdx = function (e) { const eltIdx = function (e) {

View file

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

View file

@ -20,6 +20,13 @@
import { dateFormat } from '../../scripts/utils'; import { dateFormat } from '../../scripts/utils';
export default { export default {
onMounted(props) { onMounted(props) {
this.loadCreationDate(props);
},
onUpdated(props) {
this.loadCreationDate(props);
},
loadCreationDate(props) {
if (!props.image.creationDate && !props.image.ociImage) {
props.image.one('creation-date', (date) => { props.image.one('creation-date', (date) => {
this.update({ this.update({
date: date, date: date,
@ -27,6 +34,7 @@
}); });
}); });
props.image.trigger('get-date'); props.image.trigger('get-date');
}
}, },
getDate(image) { getDate(image) {
return !image.ociImage ? `${dateFormat(image.creationDate)} ago` : 'Not Available'; 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/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
--> -->
<pagination> <pagination>
<div class="conatianer"> <div class="container">
<div class="pagination-centered"> <div class="pagination-centered">
<material-button <material-button
aria-label="page-{ p.page }" 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)" waves-color="rgba(158,158,158,.4)"
each="{ (p, idx) in props.pages}" each="{ (p, idx) in props.pages}"
class="{ p.current ? 'current' : ''} { p['space-left'] ? 'space-left' : '' } { p['space-right'] ? 'space-right' : ''}" 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> <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> </material-button>
</div> </div>
</div> </div>
<script></script> <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> </pagination>

View file

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

View file

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

View file

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

View file

@ -14,7 +14,7 @@
* You should have received a copy of the GNU Affero General Public License * 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/>. * 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'; import { encodeURI, decodeURI } from './utils';
function getQueryParams() { function getQueryParams() {
@ -59,16 +59,16 @@ function baseUrl(qs) {
export default { export default {
home() { home() {
router.push(baseUrl({ page: null })); return baseUrl({ page: null });
}, },
taglist(image) { taglist(image) {
router.push(`${baseUrl({ page: null })}#!/taglist/${image}`); return `${baseUrl({ page: null })}#!/taglist/${image}`;
}, },
getTagListImage() { getTagListImage() {
return getCurrentRoute().replace(/^.*(#!)?\/?taglist\//, ''); return getCurrentRoute().replace(/^.*(#!)?\/?taglist\//, '');
}, },
history(image, tag) { history(image, tag) {
router.push(`${baseUrl({ page: null })}#!/taghistory/image/${image}/tag/${tag}`); return `${baseUrl({ page: null })}#!/taghistory/image/${image}/tag/${tag}`;
}, },
getTagHistoryImage() { getTagHistoryImage() {
return getCurrentRoute().replace(/^.*(#!)?\/?taghistory\/image\/(.*)\/tag\/(.*)\/?$/, '$2'); return getCurrentRoute().replace(/^.*(#!)?\/?taghistory\/image\/(.*)\/tag\/(.*)\/?$/, '$2');

View file

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