mirror of
https://github.com/Joxit/docker-registry-ui.git
synced 2025-04-26 15:09:53 +03:00
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:
commit
a0dcc84ca6
21 changed files with 277 additions and 251 deletions
35
package.json
35
package.json
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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: [
|
||||
|
|
|
@ -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 -->
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
},
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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');
|
||||
|
|
163
src/style.scss
163
src/style.scss
|
@ -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;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue