mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-26 10:38:00 +01:00
Make label filter editable for replication rule (#18357)
1. Fixes #15825 2. Now, you can input labels or select them from the candidates Signed-off-by: AllForNothing <sshijun@vmware.com>
This commit is contained in:
parent
e76aff6a0a
commit
339f5b106b
@ -272,69 +272,25 @@
|
|||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="dropdown clr-select-wrapper"
|
class="clr-input-wrapper position-relative width-tag-label">
|
||||||
formArrayName="value">
|
<input
|
||||||
<clr-dropdown class="width-tag-label">
|
class="clr-input width-tag-label label-input"
|
||||||
<div
|
autocomplete="off"
|
||||||
class="width-100 label-text"
|
type="text"
|
||||||
clrDropdownTrigger>
|
id="labelFilter"
|
||||||
<div class="label-container">
|
[(ngModel)]="stringForLabelFilter"
|
||||||
<ng-template
|
[ngModelOptions]="{
|
||||||
ngFor
|
standalone: true
|
||||||
let-label
|
}" />
|
||||||
[ngForOf]="
|
<clr-dropdown>
|
||||||
filter.value.value
|
<clr-icon
|
||||||
"
|
class="down"
|
||||||
let-m="index">
|
clrDropdownTrigger
|
||||||
<hbr-label-piece
|
shape="caret"
|
||||||
class="label-piece"
|
dir="down">
|
||||||
*ngIf="m < 1 && label"
|
</clr-icon>
|
||||||
[hasIcon]="false"
|
|
||||||
[label]="
|
|
||||||
getLabel(label)
|
|
||||||
"
|
|
||||||
[labelWidth]="
|
|
||||||
84
|
|
||||||
"></hbr-label-piece>
|
|
||||||
</ng-template>
|
|
||||||
<span
|
|
||||||
class="ellipsis color-white-dark"
|
|
||||||
*ngIf="
|
|
||||||
filter.value.value
|
|
||||||
.length > 1
|
|
||||||
"
|
|
||||||
>···</span
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
*ngFor="
|
|
||||||
let label1 of filter.value
|
|
||||||
.value;
|
|
||||||
let k = index
|
|
||||||
"
|
|
||||||
hidden="true">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
[formControlName]="k"
|
|
||||||
#labelValue
|
|
||||||
id="{{
|
|
||||||
'label_' +
|
|
||||||
supportedFilters[i]
|
|
||||||
?.type +
|
|
||||||
'_' +
|
|
||||||
label1
|
|
||||||
}}"
|
|
||||||
name="{{
|
|
||||||
'label_' +
|
|
||||||
supportedFilters[i]
|
|
||||||
?.type +
|
|
||||||
'_' +
|
|
||||||
label1
|
|
||||||
}}"
|
|
||||||
placeholder="select labels" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<clr-dropdown-menu
|
<clr-dropdown-menu
|
||||||
[ngStyle]="{ 'max-height.px': 230 }"
|
[ngStyle]="{ 'max-height.px': 230 }"
|
||||||
class="right-align"
|
class="right-align"
|
||||||
@ -346,11 +302,13 @@
|
|||||||
*ngFor="
|
*ngFor="
|
||||||
let value of supportedFilterLabels
|
let value of supportedFilterLabels
|
||||||
"
|
"
|
||||||
(click)="stickLabel(value, i)">
|
(click)="
|
||||||
|
stickLabel(value.name)
|
||||||
|
">
|
||||||
<clr-icon
|
<clr-icon
|
||||||
shape="check"
|
shape="check"
|
||||||
[style.visibility]="
|
[style.visibility]="
|
||||||
value.select
|
isSelect(value.name)
|
||||||
? 'visible'
|
? 'visible'
|
||||||
: 'hidden'
|
: 'hidden'
|
||||||
"></clr-icon>
|
"></clr-icon>
|
||||||
|
@ -272,7 +272,7 @@ clr-modal {
|
|||||||
.right-align {
|
.right-align {
|
||||||
min-width: 204px;
|
min-width: 204px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
transform: translateX(-4.3rem) translateY(1.25rem)!important;
|
transform: translateX(-9rem) translateY(1.25rem)!important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-item {
|
.dropdown-item {
|
||||||
@ -327,3 +327,19 @@ clr-modal {
|
|||||||
.bandwidth {
|
.bandwidth {
|
||||||
margin-top: 0.25rem;
|
margin-top: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.down {
|
||||||
|
height: 25px;
|
||||||
|
width: 25px;
|
||||||
|
right: 0.2rem;
|
||||||
|
color: rgb(164 164 164) !important;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.dropdown {
|
||||||
|
left: -1.2rem;
|
||||||
|
position: relative !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-input {
|
||||||
|
padding-right: 1.2rem;
|
||||||
|
}
|
||||||
|
@ -95,7 +95,6 @@ export class CreateEditRuleComponent implements OnInit, OnDestroy {
|
|||||||
supportedFilterLabels: {
|
supportedFilterLabels: {
|
||||||
name: string;
|
name: string;
|
||||||
color: string;
|
color: string;
|
||||||
select: boolean;
|
|
||||||
scope: string;
|
scope: string;
|
||||||
}[] = [];
|
}[] = [];
|
||||||
|
|
||||||
@ -118,6 +117,8 @@ export class CreateEditRuleComponent implements OnInit, OnDestroy {
|
|||||||
selectedUnit: string = BandwidthUnit.KB;
|
selectedUnit: string = BandwidthUnit.KB;
|
||||||
copySpeedUnit: string = BandwidthUnit.KB;
|
copySpeedUnit: string = BandwidthUnit.KB;
|
||||||
showChunkOption: boolean = false;
|
showChunkOption: boolean = false;
|
||||||
|
stringForLabelFilter: string = '';
|
||||||
|
copyStringForLabelFilter: string = '';
|
||||||
constructor(
|
constructor(
|
||||||
private fb: UntypedFormBuilder,
|
private fb: UntypedFormBuilder,
|
||||||
private repService: ReplicationService,
|
private repService: ReplicationService,
|
||||||
@ -365,9 +366,23 @@ export class CreateEditRuleComponent implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
this.isPushMode = true;
|
this.isPushMode = true;
|
||||||
this.selectedUnit = BandwidthUnit.KB;
|
this.selectedUnit = BandwidthUnit.KB;
|
||||||
|
this.stringForLabelFilter = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
updateRuleFormAndCopyUpdateForm(rule: ReplicationPolicy): void {
|
updateRuleFormAndCopyUpdateForm(rule: ReplicationPolicy): void {
|
||||||
|
if (rule?.filters?.length) {
|
||||||
|
// set stringForLabelFilter
|
||||||
|
this.stringForLabelFilter = '';
|
||||||
|
this.copyStringForLabelFilter = '';
|
||||||
|
rule.filters.forEach(item => {
|
||||||
|
if (item.type === FilterType.LABEL) {
|
||||||
|
this.stringForLabelFilter = (item.value as string[]).join(
|
||||||
|
','
|
||||||
|
);
|
||||||
|
this.copyStringForLabelFilter = this.stringForLabelFilter;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
this.isPushMode = rule.dest_registry.id !== 0;
|
this.isPushMode = rule.dest_registry.id !== 0;
|
||||||
this.checkChunkOption(rule.dest_registry.id || rule.src_registry.id);
|
this.checkChunkOption(rule.dest_registry.id || rule.src_registry.id);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@ -482,6 +497,10 @@ export class CreateEditRuleComponent implements OnInit, OnDestroy {
|
|||||||
// speed unit has been changed
|
// speed unit has been changed
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (this.copyStringForLabelFilter !== this.stringForLabelFilter) {
|
||||||
|
// label filter has been changed
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return !isEmptyObject(this.hasChanges());
|
return !isEmptyObject(this.hasChanges());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -511,6 +530,18 @@ export class CreateEditRuleComponent implements OnInit, OnDestroy {
|
|||||||
copyRuleForm.dest_registry = null;
|
copyRuleForm.dest_registry = null;
|
||||||
}
|
}
|
||||||
let filters: any = copyRuleForm.filters;
|
let filters: any = copyRuleForm.filters;
|
||||||
|
|
||||||
|
// set label filter
|
||||||
|
if (this.stringForLabelFilter) {
|
||||||
|
// set stringForLabelFilter
|
||||||
|
copyRuleForm.filters.forEach(item => {
|
||||||
|
if (item.type === FilterType.LABEL) {
|
||||||
|
item.value = this.stringForLabelFilter
|
||||||
|
.split(',')
|
||||||
|
.filter(item => item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
// remove the filters which user not set.
|
// remove the filters which user not set.
|
||||||
for (let i = filters.length - 1; i >= 0; i--) {
|
for (let i = filters.length - 1; i >= 0; i--) {
|
||||||
if (
|
if (
|
||||||
@ -575,30 +606,6 @@ export class CreateEditRuleComponent implements OnInit, OnDestroy {
|
|||||||
this.noEndpointInfo = 'REPLICATION.NO_ENDPOINT_INFO';
|
this.noEndpointInfo = 'REPLICATION.NO_ENDPOINT_INFO';
|
||||||
}
|
}
|
||||||
if (rule) {
|
if (rule) {
|
||||||
if (
|
|
||||||
this.supportedFilterLabels &&
|
|
||||||
this.supportedFilterLabels.length
|
|
||||||
) {
|
|
||||||
this.supportedFilterLabels.forEach((label, index) => {
|
|
||||||
if (rule.filters && rule.filters.length) {
|
|
||||||
rule.filters.forEach(f => {
|
|
||||||
if (
|
|
||||||
f.type === FilterType.LABEL &&
|
|
||||||
f.value &&
|
|
||||||
(f.value as any).length
|
|
||||||
) {
|
|
||||||
(f.value as any).forEach(name => {
|
|
||||||
if (label.name === name) {
|
|
||||||
this.supportedFilterLabels[
|
|
||||||
index
|
|
||||||
].select = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
this.onGoing = true;
|
this.onGoing = true;
|
||||||
this.policyId = +rule.id;
|
this.policyId = +rule.id;
|
||||||
this.headerTitle = 'REPLICATION.EDIT_POLICY_TITLE';
|
this.headerTitle = 'REPLICATION.EDIT_POLICY_TITLE';
|
||||||
@ -634,14 +641,6 @@ export class CreateEditRuleComponent implements OnInit, OnDestroy {
|
|||||||
adapter => {
|
adapter => {
|
||||||
this.setFilterAndTrigger(adapter);
|
this.setFilterAndTrigger(adapter);
|
||||||
this.copyUpdateForm = clone(this.ruleForm.value);
|
this.copyUpdateForm = clone(this.ruleForm.value);
|
||||||
if (
|
|
||||||
this.supportedFilterLabels &&
|
|
||||||
this.supportedFilterLabels.length
|
|
||||||
) {
|
|
||||||
this.supportedFilterLabels.forEach((label, index) => {
|
|
||||||
label.select = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
(error: any) => {
|
(error: any) => {
|
||||||
this.inlineAlert.showInlineError(error);
|
this.inlineAlert.showInlineError(error);
|
||||||
@ -746,24 +745,20 @@ export class CreateEditRuleComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
stickLabel(value, index) {
|
stickLabel(name: string) {
|
||||||
value.select = !value.select;
|
if (this.isSelect(name)) {
|
||||||
let filters = this.ruleForm.get('filters') as UntypedFormArray;
|
let arr: string[] = this.stringForLabelFilter.split(',');
|
||||||
let fromIndex = filters.controls[index] as UntypedFormGroup;
|
arr = arr.filter(item => {
|
||||||
let labelValue = this.supportedFilterLabels.reduce(
|
return item !== name;
|
||||||
(cumulatedSelectedArrs, currentValue) => {
|
});
|
||||||
if (currentValue.select) {
|
this.stringForLabelFilter = arr.join(',');
|
||||||
if (!cumulatedSelectedArrs.length) {
|
} else {
|
||||||
return [currentValue.name];
|
if (this.stringForLabelFilter) {
|
||||||
}
|
this.stringForLabelFilter += `,${name}`;
|
||||||
return [...cumulatedSelectedArrs, currentValue.name];
|
} else {
|
||||||
}
|
this.stringForLabelFilter += `${name}`;
|
||||||
return cumulatedSelectedArrs;
|
}
|
||||||
},
|
}
|
||||||
[]
|
|
||||||
);
|
|
||||||
|
|
||||||
fromIndex.setControl('value', this.fb.array(labelValue));
|
|
||||||
}
|
}
|
||||||
// set prefix '0 ', so user can not set item of 'seconds'
|
// set prefix '0 ', so user can not set item of 'seconds'
|
||||||
inputInvalid(e: any) {
|
inputInvalid(e: any) {
|
||||||
@ -820,7 +815,6 @@ export class CreateEditRuleComponent implements OnInit, OnDestroy {
|
|||||||
this.supportedFilterLabels.push({
|
this.supportedFilterLabels.push({
|
||||||
name: data.name,
|
name: data.name,
|
||||||
color: data.color ? data.color : '#FFFFFF',
|
color: data.color ? data.color : '#FFFFFF',
|
||||||
select: false,
|
|
||||||
scope: 'g',
|
scope: 'g',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -849,7 +843,6 @@ export class CreateEditRuleComponent implements OnInit, OnDestroy {
|
|||||||
color: data.color
|
color: data.color
|
||||||
? data.color
|
? data.color
|
||||||
: '#FFFFFF',
|
: '#FFFFFF',
|
||||||
select: false,
|
|
||||||
scope: 'g',
|
scope: 'g',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -880,17 +873,6 @@ export class CreateEditRuleComponent implements OnInit, OnDestroy {
|
|||||||
return realSpeed ? realSpeed : -1;
|
return realSpeed ? realSpeed : -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
getLabel(labelName: string): Label {
|
|
||||||
if (this.supportedFilterLabels?.length) {
|
|
||||||
let label: Label;
|
|
||||||
this.supportedFilterLabels.forEach(item => {
|
|
||||||
if (item.name === labelName) {
|
|
||||||
label = item;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return label;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
checkChunkOption(id: number, info?: RegistryInfo) {
|
checkChunkOption(id: number, info?: RegistryInfo) {
|
||||||
this.showChunkOption = false;
|
this.showChunkOption = false;
|
||||||
this.ruleForm.get('copy_by_chunk').reset(false);
|
this.ruleForm.get('copy_by_chunk').reset(false);
|
||||||
@ -906,4 +888,11 @@ export class CreateEditRuleComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isSelect(v: string) {
|
||||||
|
if (v && this.stringForLabelFilter) {
|
||||||
|
return this.stringForLabelFilter.indexOf(v) !== -1;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user