Skip to content

Commit

Permalink
feat(taglist-order): add new option TAGLIST_ORDER (Joxit#307)
Browse files Browse the repository at this point in the history
Create new option named `TAGLIST_ORDER` that will order tags using user defined configuration.

Available values are:
| value | description |
| --- | --- |
| `num-asc;alpha-asc` | Numbers come first in **ascending** order then alphabet in **ascending** order |
| `num-desc;alpha-asc` | Numbers come first in **descending** order then alphabet in **ascending** order |
| `num-asc;alpha-desc` | Numbers come first in **ascending** order then alphabet in **descending** order |
| `num-desc;alpha-desc` | Numbers come first in **descending** order then alphabet in **descending** order |
| `alpha-asc;num-asc` | Alphabet come first in **ascending** order then numbers in **ascending** order |
| `alpha-asc;num-desc`  (default) | Alphabet come first in **ascending** order then numbers in **descending** order |
| `alpha-desc;num-asc` | Alphabet come first in **descending** order then numbers in **ascending** order |
| `alpha-desc;num-desc` | Alphabet come first in **descending** order then numbers in **descending** order |

Some examples in [test/taglist-order.test.js](https://github.com/Joxit/docker-registry-ui/blob/8bbfc5c390c334e66741d5a639d4b17c8fe6fcea/test/taglist-order.test.js)


closes Joxit#294
  • Loading branch information
Joxit authored May 19, 2023
2 parents bc80050 + c6dee14 commit d2222be
Show file tree
Hide file tree
Showing 10 changed files with 277 additions and 31 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ Some env options are available for use this interface for **only one server** (w
- `USE_CONTROL_CACHE_HEADER`: Use `Control-Cache` header and set to `no-store, no-cache`. This will avoid some issues on multi-arch images (see [#260](https://github.com/Joxit/docker-registry-ui/issues/260) and [#265](https://github.com/Joxit/docker-registry-ui/pull/265)). This option requires registry configuration: `Access-Control-Allow-Headers` with `Cache-Control`. (default: `false`). Since 2.3.0
- `THEME`: Chose your default theme, could be `dark`, `light` or `auto` (see [#283](https://github.com/Joxit/docker-registry-ui/pull/283)). When auto is selected, you will have a switch to manually change from light to dark and vice-versa (see [#291](https://github.com/Joxit/docker-registry-ui/pull/291)). (default: `auto`). Since 2.4.0
- `THEME_*`: See table in [Theme options](#theme-options) section (see [#283](https://github.com/Joxit/docker-registry-ui/pull/283)). Since 2.4.0
- `TAGLIST_ORDER`: Set the default order for the taglist page, could be `num-asc;alpha-asc`, `num-desc;alpha-asc`, `num-asc;alpha-desc`, `num-desc;alpha-desc`, `alpha-asc;num-asc`, `alpha-asc;num-desc`, `alpha-desc;num-asc` or `alpha-desc;num-desc` (see [#307](https://github.com/Joxit/docker-registry-ui/pull/307)). (default: `alpha-asc;num-desc`) Since 2.5.0

There are some examples with [docker-compose](https://docs.docker.com/compose/) and docker-registry-ui as proxy [here](https://github.com/Joxit/docker-registry-ui/tree/main/examples/ui-as-proxy/) or docker-registry-ui as standalone [here](https://github.com/Joxit/docker-registry-ui/tree/main/examples/ui-as-standalone/).

Expand Down
1 change: 1 addition & 0 deletions bin/90-docker-registry-ui.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ sed -i "s~\${READ_ONLY_REGISTRIES}~${READ_ONLY_REGISTRIES}~" index.html
sed -i "s~\${SHOW_CATALOG_NB_TAGS}~${SHOW_CATALOG_NB_TAGS}~" index.html
sed -i "s~\${HISTORY_CUSTOM_LABELS}~${HISTORY_CUSTOM_LABELS}~" index.html
sed -i "s~\${USE_CONTROL_CACHE_HEADER}~${USE_CONTROL_CACHE_HEADER}~" index.html
sed -i "s~\${TAGLIST_ORDER}~${TAGLIST_ORDER}~" index.html

grep -o 'THEME[A-Z_]*' index.html | while read e; do
sed -i "s~\${$e}~$(printenv $e)~" index.html
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
"format-riot": "find src rollup rollup.config.js -name '*.riot' -exec prettier --config .prettierrc -w --parser html {} \\;",
"start": "rollup -c -w --environment ROLLUP_SERVE:true",
"build": "rollup -c",
"build:electron": "npm run build && cd examples/electron && npm install && npm run dist"
"build:electron": "npm run build && cd examples/electron && npm install && npm run dist",
"test": "mocha"
},
"repository": {
"type": "git",
Expand All @@ -31,6 +32,7 @@
"@rollup/plugin-node-resolve": "^15.0.1",
"@rollup/plugin-terser": "^0.2.1",
"core-js": "^3.27.1",
"mocha": "^10.2.0",
"node-sass": "^8.0.0",
"prettier": "^2.8.1",
"riot": "^7.1.0",
Expand Down
1 change: 1 addition & 0 deletions src/components/docker-registry-ui.riot
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
filter-results="{ state.filter }"
on-authentication="{ onAuthentication }"
use-control-cache-header="{ truthy(props.useControlCacheHeader) }"
taglist-order="{ props.taglistOrder }"
></tag-list>
</route>
<route path="{baseRoute}taghistory/(.*)">
Expand Down
14 changes: 11 additions & 3 deletions src/components/tag-list/tag-list.riot
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.

<script>
import { Http } from '../../scripts/http';
import { DockerImage, compare } from '../../scripts/docker-image';
import { DockerImage } from '../../scripts/docker-image';
import { getNumPages, getPageLabels } from '../../scripts/utils';
import Pagination from './pagination.riot';
import TagTable from './tag-table.riot';
import router from '../../scripts/router';
import { getTagComparator, taglistOrderParser } from '../../scripts/taglist-order';
export default {
components: {
Pagination,
Expand All @@ -79,13 +81,19 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
asc: true,
page: router.getPageQueryParam() || 1,
};
try {
this.state.taglistOrder = taglistOrderParser(props.taglistOrder)
} catch(e) {
props.onNotify(e);
}
},
onMounted(props, state) {
this.display(props, state);
window.addEventListener('resize', this.onResize);
// this may be run before the final document size is available, so schedule
// a correction once everything is set up.
window.requestAnimationFrame(this.onResize);
this.tagComparator = getTagComparator(props.taglistOrder);
},
display(props, state) {
state.tags = [];
Expand All @@ -106,7 +114,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
useControlCacheHeader: props.useControlCacheHeader,
})
)
.sort(compare);
.sort(self.tagComparator);
window.requestAnimationFrame(self.onResize);
self.update({
page: Math.min(state.page, getNumPages(tags)),
Expand Down Expand Up @@ -168,7 +176,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
this.state.tags.reverse();
this.state.asc = false;
} else {
this.state.tags.sort(compare);
this.state.tags.sort(this.tagComparator);
this.state.asc = true;
}
this.update();
Expand Down
2 changes: 2 additions & 0 deletions src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
show-catalog-nb-tags="${SHOW_CATALOG_NB_TAGS}"
history-custom-labels="${HISTORY_CUSTOM_LABELS}"
use-control-cache-header="${USE_CONTROL_CACHE_HEADER}"
taglist-order="${TAGLIST_ORDER}"
theme="${THEME}"
theme-primary-text="${THEME_PRIMARY_TEXT}"
theme-neutral-text="${THEME_NEUTRAL_TEXT}"
Expand All @@ -73,6 +74,7 @@
show-catalog-nb-tags="true"
history-custom-labels="first_custom_labels,second_custom_labels"
use-control-cache-header="false"
taglist-order=""
theme="auto"
theme-primary-text=""
theme-neutral-text=""
Expand Down
27 changes: 0 additions & 27 deletions src/scripts/docker-image.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,33 +18,6 @@ import { Http } from './http';
import { isDigit, eventTransfer, ERROR_CAN_NOT_READ_CONTENT_DIGEST } from './utils';
import observable from '@riotjs/observable';

const tagReduce = (acc, e) => {
if (acc.length > 0 && isDigit(acc[acc.length - 1].charAt(0)) == isDigit(e)) {
acc[acc.length - 1] += e;
} else {
acc.push(e);
}
return acc;
};

export function compare(e1, e2) {
const tag1 = e1.tag.match(/./g).reduce(tagReduce, []);
const tag2 = e2.tag.match(/./g).reduce(tagReduce, []);

for (var i = 0; i < tag1.length && i < tag2.length; i++) {
const compare = tag1[i].localeCompare(tag2[i]);
if (isDigit(tag1[i].charAt(0)) && isDigit(tag2[i].charAt(0))) {
const diff = tag1[i] - tag2[i];
if (diff != 0) {
return diff;
}
} else if (compare != 0) {
return compare;
}
}
return e1.tag.length - e2.tag.length;
}

export class DockerImage {
constructor(name, tag, { list, registryUrl, onNotify, onAuthentication, useControlCacheHeader }) {
this.name = name;
Expand Down
6 changes: 6 additions & 0 deletions src/scripts/error.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export class DockerRegistryUIError extends Error {
constructor(msg) {
super(msg);
this.isError = true;
}
}
89 changes: 89 additions & 0 deletions src/scripts/taglist-order.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { DockerRegistryUIError } from './error.js';
import { isDigit } from './utils.js';

const TAGLIST_ORDER_REGEX = /(alpha-(asc|desc);num-(asc|desc))|(num-(asc|desc);alpha-(asc|desc))/;

export const taglistOrderVariants = (taglistOrder) => {
switch (taglistOrder) {
case 'desc':
return 'alpha-desc;num-desc';
case 'asc':
return 'num-asc;alpha-asc';
case 'alpha-desc':
case 'alpha-asc':
case 'num-desc':
case 'num-asc':
return `${taglistOrder};${taglistOrder.startsWith('num') ? 'alpha' : 'num'}-asc`;
default:
if (!taglistOrder) {
return 'alpha-asc;num-desc';
} else if (TAGLIST_ORDER_REGEX.test(taglistOrder)) {
return taglistOrder;
}
throw new DockerRegistryUIError(`The taglist order \`${taglistOrder}\` is not recognized.`);
}
};

export const taglistOrderParser = (taglistOrder) => {
const orders = taglistOrderVariants(taglistOrder)
.split(';')
.filter((e) => e)
.map((e) => e.split('-').filter((e) => e))
.reduce((acc, e, idx) => {
if (e.length > 1) {
acc[e[0] + 'Asc'] = e[1] === 'asc';
}
if (idx === 0) {
acc.numFirst = e[0] === 'num';
}
return acc;
}, {});

return orders;
};

export const tagReduce = (acc, e) => {
if (acc.length > 0 && isDigit(acc[acc.length - 1].charAt(0)) == isDigit(e)) {
acc[acc.length - 1] += e;
} else {
acc.push(e);
}
return acc;
};

export const splitTagToArray = (tag) =>
tag
.split('')
.reduce(tagReduce, [])
.map((e) => (isDigit(e.charAt(0)) ? parseInt(e) : e));

const applyOrder = (order, e1, e2) => {
if (e1 === e2) {
return 0;
}
const numFirst = order.numFirst ? 1 : -1;
if (typeof e1 === 'number') {
const factor = order.numAsc ? 1 : -1;
return typeof e2 === 'number' ? (e1 - e2) * factor : -1 * numFirst;
} else if (typeof e2 === 'number') {
return 1 * numFirst;
} else {
const factor = order.alphaAsc ? 1 : -1;
return e1.localeCompare(e2) * factor;
}
};

export const getTagComparator = (order) => {
return (e1, e2) => {
const tag1 = splitTagToArray(e1.tag || e1);
const tag2 = splitTagToArray(e2.tag || e2);

for (var i = 0; i < tag1.length && i < tag2.length; i++) {
const compare = applyOrder(order, tag1[i], tag2[i]);
if (compare != 0) {
return compare;
}
}
return (e1.tag || e1).length - (e2.tag || e2).length;
};
};
Loading

0 comments on commit d2222be

Please sign in to comment.