281 lines
9.6 KiB
Plaintext
281 lines
9.6 KiB
Plaintext
<!--
|
|
Copyright (C) 2016-2023 Jones Magloire @Joxit
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Affero General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Affero General Public License for more details.
|
|
|
|
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-table>
|
|
<confirm-delete-image
|
|
opened="{ state.confirmDeleteImage }"
|
|
on-click="{ onConfirmDeleteImageClick }"
|
|
registry-url="{ props.registryUrl }"
|
|
on-notify="{ props.onNotify }"
|
|
on-authentication="{ props.onAuthentication }"
|
|
tags="{ props.tags }"
|
|
to-delete="{ state.toDelete }"
|
|
></confirm-delete-image>
|
|
<material-card class="taglist">
|
|
<table style="border: none">
|
|
<thead>
|
|
<tr>
|
|
<th
|
|
class="creation-date { (state.desc && state.orderType === 'date') ? 'material-card-th-sorted-descending' : 'material-card-th-sorted-ascending' }"
|
|
onclick="{() => onPageReorder('date') }"
|
|
>
|
|
Creation date
|
|
</th>
|
|
<th
|
|
class="image-size { (state.desc && state.orderType === 'size') ? 'material-card-th-sorted-descending' : 'material-card-th-sorted-ascending' }"
|
|
onclick="{() => onPageReorder('size') }"
|
|
>
|
|
Size
|
|
</th>
|
|
<th id="image-content-digest-header" if="{ props.showContentDigest }">Content Digest</th>
|
|
|
|
<th
|
|
id="image-tag-header"
|
|
class="{ props.asc ? 'material-card-th-sorted-ascending' : 'material-card-th-sorted-descending' }"
|
|
onclick="{ onReverseOrder }"
|
|
>
|
|
Tag
|
|
</th>
|
|
<th class="show-tag-history">History</th>
|
|
<th
|
|
class="remove-tag { state.toDelete.size > 0 && !state.singleDeleteAction ? 'delete' : '' }"
|
|
if="{ props.isImageRemoveActivated }"
|
|
>
|
|
<material-checkbox
|
|
class="indeterminate"
|
|
checked="{ state.multiDelete }"
|
|
if="{ state.toDelete.size === 0 || state.singleDeleteAction }"
|
|
title="Toggle multi-delete. Alt+Click to select all tags."
|
|
onChange="{ onRemoveImageHeaderChange }"
|
|
>
|
|
</material-checkbox>
|
|
<material-button
|
|
if="{ state.toDelete.size > 0 && !state.singleDeleteAction }"
|
|
waves-center="true"
|
|
color="inherit"
|
|
text-color="var(--neutral-background)"
|
|
waves-color="var(--hover-background)"
|
|
title="This will delete selected images."
|
|
onClick="{ deleteImages }"
|
|
icon
|
|
>
|
|
<i class="material-icons">delete</i>
|
|
</material-button>
|
|
</th>
|
|
</tr>
|
|
</thead>
|
|
<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>
|
|
</td>
|
|
<td class="image-size">
|
|
<image-size image="{ image }"></image-size>
|
|
</td>
|
|
<td if="{ props.showContentDigest }">
|
|
<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>
|
|
<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>
|
|
</td>
|
|
<td if="{ props.isImageRemoveActivated }" class="remove-tag">
|
|
<remove-image
|
|
multi-delete="{ state.multiDelete }"
|
|
image="{ image }"
|
|
registry-url="{ props.registryUrl }"
|
|
handleCheckboxChange="{ onRemoveImageChange }"
|
|
checked="{ state.toDelete.has(image) }"
|
|
on-notify="{ props.onNotify }"
|
|
on-authentication="{ props.onAuthentication }"
|
|
></remove-image>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</material-card>
|
|
<script>
|
|
import { getPage } from '../../scripts/utils';
|
|
import ImageDate from './image-date.riot';
|
|
import ImageSize from './image-size.riot';
|
|
import ImageTag from './image-tag.riot';
|
|
import ImageContentDigest from './image-content-digest.riot';
|
|
import CopyToClipboard from './copy-to-clipboard.riot';
|
|
import TagHistoryButton from './tag-history-button.riot';
|
|
import RemoveImage from './remove-image.riot';
|
|
import { matchSearch } from '../search-bar.riot';
|
|
import ConfirmDeleteImage from '../dialogs/confirm-delete-image.riot';
|
|
const ACTION_CHECK_TO_DELETE = 'CHECK';
|
|
const ACTION_UNCHECK_TO_DELETE = 'UNCHECK';
|
|
const ACTION_DELETE_IMAGE = 'DELETE';
|
|
|
|
export default {
|
|
components: {
|
|
ImageDate,
|
|
ImageSize,
|
|
ImageTag,
|
|
ImageContentDigest,
|
|
CopyToClipboard,
|
|
RemoveImage,
|
|
TagHistoryButton,
|
|
ConfirmDeleteImage,
|
|
},
|
|
onBeforeMount(props) {
|
|
this.state = {
|
|
toDelete: new Set(),
|
|
multiDelete: false,
|
|
page: props.page,
|
|
};
|
|
},
|
|
onBeforeUpdate(props, state) {
|
|
if (state.page !== props.page) {
|
|
state.toDelete.clear();
|
|
}
|
|
state.page = props.page;
|
|
},
|
|
deleteImages() {
|
|
this.update({
|
|
confirmDeleteImage: true,
|
|
});
|
|
},
|
|
onConfirmDeleteImageClick() {
|
|
if (this.state.singleDeleteAction) {
|
|
this.state.toDelete.clear();
|
|
}
|
|
this.update({
|
|
singleDeleteAction: false,
|
|
confirmDeleteImage: false,
|
|
});
|
|
},
|
|
onRemoveImageHeaderChange(event) {
|
|
if (event.altKey === true) {
|
|
const tags = getPage(this.props.tags, this.props.page);
|
|
tags
|
|
.filter((image) => matchSearch(this.props.filterResults, image.tag))
|
|
.forEach((tag) => this.state.toDelete.add(tag));
|
|
this.update({
|
|
multiDelete: true,
|
|
toDelete: this.state.toDelete,
|
|
slectedImage: undefined,
|
|
});
|
|
} else {
|
|
this.update({
|
|
multiDelete: event.target.checked,
|
|
slectedImage: undefined,
|
|
});
|
|
}
|
|
},
|
|
onRemoveImageChange(action, image, shiftKey) {
|
|
let confirmDeleteImage = false;
|
|
let singleDeleteAction = false;
|
|
let slectedImage = undefined;
|
|
switch (action) {
|
|
case ACTION_CHECK_TO_DELETE: {
|
|
this.state.toDelete.add(image);
|
|
if (shiftKey) {
|
|
slectedImage = this.supportShiftKey(image, true);
|
|
}
|
|
break;
|
|
}
|
|
case ACTION_UNCHECK_TO_DELETE: {
|
|
this.state.toDelete.delete(image);
|
|
if (shiftKey) {
|
|
slectedImage = this.supportShiftKey(image, false);
|
|
}
|
|
break;
|
|
}
|
|
case ACTION_DELETE_IMAGE: {
|
|
this.state.toDelete.clear();
|
|
this.state.toDelete.add(image);
|
|
confirmDeleteImage = true;
|
|
singleDeleteAction = true;
|
|
}
|
|
}
|
|
this.update({
|
|
toDelete: this.state.toDelete,
|
|
confirmDeleteImage,
|
|
singleDeleteAction,
|
|
slectedImage,
|
|
});
|
|
},
|
|
supportShiftKey(selectedImage, addOrRemove) {
|
|
if (!this.state.slectedImage) {
|
|
return selectedImage;
|
|
} else {
|
|
let shouldChange = false;
|
|
const tags = getPage(this.props.tags, this.props.page);
|
|
tags
|
|
.filter((image) => {
|
|
if (image == this.state.slectedImage || image == selectedImage) {
|
|
shouldChange = !shouldChange;
|
|
return true;
|
|
}
|
|
return shouldChange;
|
|
})
|
|
.forEach((image) => {
|
|
if (addOrRemove) {
|
|
this.state.toDelete.add(image);
|
|
} else {
|
|
this.state.toDelete.delete(image);
|
|
}
|
|
});
|
|
return undefined;
|
|
}
|
|
},
|
|
onReverseOrder() {
|
|
this.state.orderType = null;
|
|
this.state.desc = false;
|
|
this.props.onReverseOrder();
|
|
},
|
|
onPageReorder(type) {
|
|
this.update({
|
|
orderType: type,
|
|
desc: (this.state.orderType && this.state.orderType !== type) || !this.state.desc,
|
|
});
|
|
},
|
|
getPage(tags, page) {
|
|
const sortedTags = getPage(tags, page);
|
|
if (this.state.orderType === 'date') {
|
|
sortedTags.sort((e1, e2) =>
|
|
!this.state.desc
|
|
? e2.creationDate.getTime() - e1.creationDate.getTime()
|
|
: e1.creationDate.getTime() - e2.creationDate.getTime()
|
|
);
|
|
} else if (this.state.orderType === 'size') {
|
|
sortedTags.sort((e1, e2) => (!this.state.desc ? e2.size - e1.size : e1.size - e2.size));
|
|
}
|
|
return sortedTags;
|
|
},
|
|
matchSearch,
|
|
};
|
|
export { ACTION_CHECK_TO_DELETE, ACTION_UNCHECK_TO_DELETE, ACTION_DELETE_IMAGE };
|
|
</script>
|
|
</tag-table>
|