Upgrade primeng to 20.0.1 (#903)

* build(npm): add missed packages

* build(npm): upgrade primeng to 20.0.1

* feat(ui): upgrade ui to support 20 primeng

https://primeng.org/migration/v19#compatible

* fix(ui): p-input value max requires value

* refactor(ui): remove unused component imports

* build(npm): allow common js dependencies

* style(ui): fix some warnings

* fix(ui): replace chips with autoComplete

---------

Co-authored-by: Aditya Chandel <8075870+adityachandelgit@users.noreply.github.com>
This commit is contained in:
Alexander Puzynia
2025-08-14 11:10:45 -07:00
committed by GitHub
parent 77e02ac7f9
commit 60f8442514
20 changed files with 2348 additions and 546 deletions

View File

@@ -47,7 +47,18 @@
"styles": [
"src/styles.scss"
],
"scripts": []
"scripts": [],
"allowedCommonJsDependencies": [
"lodash",
"quill-delta",
"event-emitter",
"showdown",
"@xmldom/xmldom",
"path-webpack",
"jszip/dist/jszip",
"localforage",
"marks-pane"
]
},
"configurations": {
"production": {

File diff suppressed because it is too large Load Diff

View File

@@ -21,10 +21,11 @@
"@angular/platform-browser-dynamic": "^20.1.6",
"@angular/router": "^20.1.6",
"@iharbeck/ngx-virtual-scroller": "^19.0.1",
"@primeng/themes": "^19.1.4",
"@primeng/themes": "^20.0.1",
"@stomp/rx-stomp": "^2.0.1",
"@stomp/stompjs": "^7.1.1",
"@tailwindcss/postcss": "^4.1.8",
"@tweenjs/tween.js": "^25.0.0",
"angular-oauth2-oidc": "^20.0.0",
"epubjs": "^0.3.93",
"jwt-decode": "^4.0.0",
@@ -32,7 +33,7 @@
"ngx-extended-pdf-viewer": "^23.3.1",
"ngx-infinite-scroll": "^20.0.0",
"primeicons": "^7.0.0",
"primeng": "19.1.4",
"primeng": "^20.0.1",
"quill": "^2.0.3",
"rxjs": "^7.8.2",
"showdown": "^2.1.0",

View File

@@ -173,7 +173,7 @@
<i class="pi pi-search"></i>
</button>
<p-overlayPanel #searchDropdown [dismissable]="true" appendTo="body" [style]="{ width: '18rem' }">
<p-popover #searchDropdown [dismissable]="true" appendTo="body" [style]="{ width: '18rem' }">
<div class="relative w-full">
<input
type="text"
@@ -192,7 +192,7 @@
(click)="clearSearch(); searchDropdown.hide()"
></p-button>
</div>
</p-overlayPanel>
</p-popover>
</div>
<div class="hidden md:block relative">

View File

@@ -27,14 +27,12 @@ import {FormsModule} from '@angular/forms';
import {BookFilterComponent} from './book-filter/book-filter.component';
import {Tooltip} from 'primeng/tooltip';
import {EntityViewPreferences, UserService} from '../../../settings/user-management/user.service';
import {OverlayPanelModule} from 'primeng/overlaypanel';
import {SeriesCollapseFilter} from './filters/SeriesCollapseFilter';
import {SideBarFilter} from './filters/SidebarFilter';
import {HeaderFilter} from './filters/HeaderFilter';
import {CoverScalePreferenceService} from './cover-scale-preference.service';
import {BookSorter} from './sorting/BookSorter';
import {BookDialogHelperService} from './BookDialogHelperService';
import {DropdownModule} from 'primeng/dropdown';
import {Checkbox} from 'primeng/checkbox';
import {Popover} from 'primeng/popover';
import {Slider} from 'primeng/slider';
@@ -83,8 +81,8 @@ const SORT_DIRECTION = {
styleUrls: ['./book-browser.component.scss'],
imports: [
Button, VirtualScrollerModule, BookCardComponent, AsyncPipe, ProgressSpinner, Menu, InputText, FormsModule,
BookTableComponent, BookFilterComponent, Tooltip, NgClass, PrimeTemplate, NgStyle, OverlayPanelModule,
DropdownModule, Checkbox, Popover, Slider, Select, Divider, MultiSelect, TieredMenu
BookTableComponent, BookFilterComponent, Tooltip, NgClass, PrimeTemplate, NgStyle, Popover,
Checkbox, Slider, Select, Divider, MultiSelect, TieredMenu
],
providers: [SeriesCollapseFilter],
animations: [

View File

@@ -10,7 +10,6 @@ import {SlicePipe} from '@angular/common';
import {Divider} from 'primeng/divider';
import {UrlHelperService} from '../../../utilities/service/url-helper.service';
import {Router} from '@angular/router';
import {OverlayPanelModule} from 'primeng/overlaypanel';
import {IconField} from 'primeng/iconfield';
import {InputIcon} from 'primeng/inputicon';
@@ -23,7 +22,6 @@ import {InputIcon} from 'primeng/inputicon';
Button,
SlicePipe,
Divider,
OverlayPanelModule,
IconField,
InputIcon
],

View File

@@ -99,7 +99,7 @@
[ngClass]="{
'outlined-input-green': isValueCopied(field.controlName) && !hoveredFields[field.controlName],
}">
<p-chips formControlName="{{field.controlName}}" addOnBlur="true"></p-chips>
<p-autoComplete formControlName="{{field.controlName}}" [multiple]="true" [typeahead]="false" [dropdown]="false" [forceSelection]="false" styleClass="w-full" (onBlur)="onAutoCompleteBlur(field.controlName, $event)"></p-autoComplete>
</div>
<p-button
[icon]="isValueSaved(field.controlName) ? 'pi pi-check' : (hoveredFields[field.controlName] && isValueCopied(field.controlName) ? 'pi pi-times' : 'pi pi-arrow-left')"
@@ -115,11 +115,12 @@
(mouseleave)="onMouseLeave(field.controlName)"/>
<div class="w-full">
<p-chips
<p-autoComplete
[ngModel]="fetchedMetadata[field.fetchedKey] ?? []"
[ngModelOptions]="{ standalone: true }"
[disabled]="true">
</p-chips>
[disabled]="true"
[multiple]="true" [typeahead]="false" [dropdown]="false" [forceSelection]="false" styleClass="w-full">
</p-autoComplete>
</div>
</div>
</div>
@@ -201,7 +202,7 @@
<div class="flex items-center py-1">
<label for="{{field.controlName}}" class="w-[15%]">{{ field.label }}</label>
<div class="flex w-full">
<p-chips class="w-full" formControlName="{{field.controlName}}" addOnBlur="true"></p-chips>
<p-autoComplete formControlName="{{field.controlName}}" [multiple]="true" [typeahead]="false" [dropdown]="false" [forceSelection]="false" styleClass="w-full" (onBlur)="onAutoCompleteBlur(field.controlName, $event)"></p-autoComplete>
</div>
</div>
}

View File

@@ -6,8 +6,8 @@ import {Tooltip} from 'primeng/tooltip';
import {InputText} from 'primeng/inputtext';
import {BookMetadata} from '../../book/model/book.model';
import {UrlHelperService} from '../../utilities/service/url-helper.service';
import {Chips} from 'primeng/chips';
import {Textarea} from 'primeng/textarea';
import {AutoComplete} from 'primeng/autocomplete';
@Component({
selector: 'app-bookdrop-file-metadata-picker-component',
@@ -17,9 +17,9 @@ import {Textarea} from 'primeng/textarea';
Tooltip,
InputText,
NgClass,
Chips,
FormsModule,
Textarea
Textarea,
AutoComplete
],
templateUrl: './bookdrop-file-metadata-picker.component.html',
styleUrl: './bookdrop-file-metadata-picker.component.scss'
@@ -132,6 +132,24 @@ export class BookdropFileMetadataPickerComponent {
this.hoveredFields[field] = false;
}
// Handle blur event for AutoComplete to add custom values
onAutoCompleteBlur(fieldName: string, event: any) {
const inputValue = event.target.value?.trim();
if (inputValue) {
const currentValue = this.metadataForm.get(fieldName)?.value || [];
const values = Array.isArray(currentValue) ? currentValue :
typeof currentValue === 'string' && currentValue ? currentValue.split(',').map((v: string) => v.trim()) : [];
// Add the new value if it's not already in the array
if (!values.includes(inputValue)) {
values.push(inputValue);
this.metadataForm.get(fieldName)?.setValue(values);
}
// Clear the input
event.target.value = '';
}
}
resetAll() {
if (this.originalMetadata) {
this.metadataForm.patchValue({

View File

@@ -7,7 +7,6 @@ import {LibraryService} from '../../book/service/library.service';
import {Library} from '../../book/model/library.model';
import {ProgressSpinner} from 'primeng/progressspinner';
import {DropdownModule} from 'primeng/dropdown';
import {FormControl, FormGroup, FormsModule} from '@angular/forms';
import {Button} from 'primeng/button';
import {Select} from 'primeng/select';
@@ -47,7 +46,6 @@ export interface BookdropFileUI {
styleUrl: './bookdrop-file-review.component.scss',
imports: [
ProgressSpinner,
DropdownModule,
FormsModule,
Button,
Select,

View File

@@ -2,7 +2,6 @@ import {Component, inject, OnInit} from '@angular/core';
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
import {InputText} from 'primeng/inputtext';
import {Button} from 'primeng/button';
import {DropdownModule} from 'primeng/dropdown';
import {Checkbox} from 'primeng/checkbox';
import {ToggleSwitch} from 'primeng/toggleswitch';
@@ -23,7 +22,6 @@ import {LibraryService} from '../../../book/service/library.service';
imports: [
FormsModule,
InputText,
DropdownModule,
Checkbox,
ToggleSwitch,
Divider,

View File

@@ -4,7 +4,6 @@ import {FormsModule} from '@angular/forms';
import {$t} from '@primeng/themes';
import Aura from '@primeng/themes/aura';
import {ButtonModule} from 'primeng/button';
import {InputSwitchModule} from 'primeng/inputswitch';
import {RadioButtonModule} from 'primeng/radiobutton';
import {ToggleSwitchModule} from 'primeng/toggleswitch';
import {AppConfigService} from '../../../core/service/app-config.service';
@@ -27,7 +26,6 @@ interface Palette {
imports: [
CommonModule,
FormsModule,
InputSwitchModule,
ButtonModule,
RadioButtonModule,
ToggleSwitchModule

View File

@@ -69,8 +69,8 @@
<p-inputNumber [formControl]="ruleCtrl.get('valueStart')" [min]="0" class="w-full" placeholder="Start Value" [showButtons]="true"></p-inputNumber>
<p-inputNumber [formControl]="ruleCtrl.get('valueEnd')" [min]="0" class="w-full" placeholder="End Value" [showButtons]="true"></p-inputNumber>
} @else if (numericFieldConfigMap.get(ruleCtrl.get('field')?.value)?.type === 'decimal') {
<p-inputNumber [formControl]="ruleCtrl.get('valueStart')" mode="decimal" [minFractionDigits]="1" [maxFractionDigits]="1" [min]="0" [max]="numericFieldConfigMap.get(ruleCtrl.get('field')?.value)?.max" class="w-full" placeholder="Start Value" [showButtons]="true"></p-inputNumber>
<p-inputNumber [formControl]="ruleCtrl.get('valueEnd')" mode="decimal" [minFractionDigits]="1" [maxFractionDigits]="1" [min]="0" [max]="numericFieldConfigMap.get(ruleCtrl.get('field')?.value)?.max" class="w-full" placeholder="End Value" [showButtons]="true"></p-inputNumber>
<p-inputNumber [formControl]="ruleCtrl.get('valueStart')" mode="decimal" [minFractionDigits]="1" [maxFractionDigits]="1" [min]="0" [max]="numericFieldConfigMap.get(ruleCtrl.get('field')?.value)?.max || 10" class="w-full" placeholder="Start Value" [showButtons]="true"></p-inputNumber>
<p-inputNumber [formControl]="ruleCtrl.get('valueEnd')" mode="decimal" [minFractionDigits]="1" [maxFractionDigits]="1" [min]="0" [max]="numericFieldConfigMap.get(ruleCtrl.get('field')?.value)?.max || 10" class="w-full" placeholder="End Value" [showButtons]="true"></p-inputNumber>
} @else {
<input pInputText [formControl]="ruleCtrl.get('valueStart')" class="w-full" placeholder="Start Value"/>
<input pInputText [formControl]="ruleCtrl.get('valueEnd')" class="w-full" placeholder="End Value"/>
@@ -83,7 +83,7 @@
} @else if (numericFieldConfigMap.get(ruleCtrl.get('field')?.value)?.type === 'number') {
<p-inputNumber formControlName="value" class="w-full" mode="decimal" placeholder="Value" [showButtons]="true"></p-inputNumber>
} @else if (numericFieldConfigMap.get(ruleCtrl.get('field')?.value)?.type === 'decimal') {
<p-inputNumber formControlName="value" mode="decimal" [minFractionDigits]="1" [maxFractionDigits]="1" [min]="0" [max]="numericFieldConfigMap.get(ruleCtrl.get('field')?.value)?.max" class="w-full" placeholder="Value" [showButtons]="true"></p-inputNumber>
<p-inputNumber formControlName="value" mode="decimal" [minFractionDigits]="1" [maxFractionDigits]="1" [min]="0" [max]="numericFieldConfigMap.get(ruleCtrl.get('field')?.value)?.max || 10" class="w-full" placeholder="Value" [showButtons]="true"></p-inputNumber>
} @else if (['includes_any', 'includes_all', 'excludes_all'].includes(ruleCtrl.get('operator')?.value)) {
@if (ruleCtrl.get('field')?.value === 'readStatus') {
<p-multiSelect [options]="readStatusOptions" formControlName="value" display="chip" class="w-full" appendTo="body" placeholder="Select Statuses"></p-multiSelect>
@@ -93,7 +93,7 @@
<p-multiSelect [options]="libraryOptions" formControlName="value" display="chip" class="w-full" appendTo="body" placeholder="Select Libraries"></p-multiSelect>
} @else {
<div class="w-full">
<p-chips [formControl]="ruleCtrl.get('value')" separator="," placeholder="Enter values (press Enter or comma)" class="w-full"></p-chips>
<p-autoComplete [formControl]="ruleCtrl.get('value')" [multiple]="true" [typeahead]="false" [dropdown]="false" [forceSelection]="false" styleClass="w-full" placeholder="Enter values (press Enter or comma)" (onBlur)="onAutoCompleteBlur(ruleCtrl.get('value'), $event)"></p-autoComplete>
</div>
}
} @else if (ruleCtrl.get('field')?.value === 'readStatus') {

View File

@@ -1,6 +1,5 @@
import {Component, inject, OnInit} from '@angular/core';
import {AbstractControl, FormArray, FormControl, FormGroup, ReactiveFormsModule, Validators} from '@angular/forms';
import {DropdownModule} from 'primeng/dropdown';
import {Button} from 'primeng/button';
import {NgTemplateOutlet} from '@angular/common';
import {InputText} from 'primeng/inputtext';
@@ -13,8 +12,8 @@ import {Library} from '../book/model/library.model';
import {MagicShelfService} from '../magic-shelf.service';
import {MessageService} from 'primeng/api';
import {DynamicDialogConfig} from 'primeng/dynamicdialog';
import {Chips} from 'primeng/chips';
import {MultiSelect} from 'primeng/multiselect';
import {AutoComplete} from 'primeng/autocomplete';
import {EMPTY_CHECK_OPERATORS, MULTI_VALUE_OPERATORS, parseValue, removeNulls, serializeDateRules} from '../magic-shelf-utils';
import { IconPickerService } from '../utilities/services/icon-picker.service';
@@ -88,7 +87,7 @@ export interface GroupRule {
name: string;
type: 'group';
join: 'and' | 'or';
rules: Array<Rule | GroupRule>;
rules: (Rule | GroupRule)[];
}
export type RuleFormGroup = FormGroup<{
@@ -138,15 +137,14 @@ const FIELD_CONFIGS: Record<RuleField, FullFieldConfig> = {
standalone: true,
imports: [
ReactiveFormsModule,
DropdownModule,
NgTemplateOutlet,
InputText,
Select,
Button,
DatePicker,
InputNumber,
Chips,
MultiSelect
MultiSelect,
AutoComplete
]
})
export class MagicShelfComponent implements OnInit {
@@ -327,7 +325,7 @@ export class MagicShelfComponent implements OnInit {
return new FormGroup({
type: new FormControl<'group'>('group' as 'group'),
join: new FormControl<'and' | 'or'>('and' as 'and' | 'or'),
rules: new FormArray([] as Array<GroupFormGroup | RuleFormGroup>),
rules: new FormArray([] as (GroupFormGroup | RuleFormGroup)[]),
}) as GroupFormGroup;
}
@@ -413,6 +411,24 @@ export class MagicShelfComponent implements OnInit {
});
}
// Handle blur event for AutoComplete to add custom values
onAutoCompleteBlur(formControl: any, event: any) {
const inputValue = event.target.value?.trim();
if (inputValue) {
const currentValue = formControl.value || [];
const values = Array.isArray(currentValue) ? currentValue :
typeof currentValue === 'string' && currentValue ? currentValue.split(',').map((v: string) => v.trim()) : [];
// Add the new value if it's not already in the array
if (!values.includes(inputValue)) {
values.push(inputValue);
formControl.setValue(values);
}
// Clear the input
event.target.value = '';
}
}
submit() {
if (!this.hasAtLeastOneValidRule(this.group)) {
this.messageService.add({severity: 'warn', summary: 'Validation Error', detail: 'You must add at least one valid rule before saving.'});

View File

@@ -87,7 +87,7 @@
<label for="authors">Authors</label>
<div class="flex justify-between items-center gap-2">
<div class="w-full">
<p-chips formControlName="authors" addOnBlur="true"></p-chips>
<p-autoComplete formControlName="authors" [multiple]="true" [typeahead]="false" [dropdown]="false" [forceSelection]="false" [showClear]="true" (onBlur)="onAutoCompleteBlur('authors', $event)" styleClass="w-full"></p-autoComplete>
</div>
@if (!book.metadata!['authorsLocked']) {
<p-button icon="pi pi-lock-open" [outlined]="true" (onClick)="toggleLock('authors')" severity="success"></p-button>
@@ -128,7 +128,7 @@
<label for="categories">Categories</label>
<div class="flex justify-between items-center gap-2">
<div class="w-full">
<p-chips formControlName="categories" addOnBlur="true"></p-chips>
<p-autoComplete formControlName="categories" [multiple]="true" [typeahead]="false" [dropdown]="false" [forceSelection]="false" [showClear]="true" (onBlur)="onAutoCompleteBlur('categories', $event)" styleClass="w-full"></p-autoComplete>
</div>
@if (!book.metadata!['categoriesLocked']) {
<p-button icon="pi pi-lock-open" [outlined]="true" (onClick)="toggleLock('categories')" severity="success"></p-button>

View File

@@ -21,7 +21,7 @@ import {DialogService} from 'primeng/dynamicdialog';
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
import {MetadataRefreshRequest} from '../../model/request/metadata-refresh-request.model';
import {MetadataRefreshType} from '../../model/request/metadata-refresh-type.enum';
import {Chips} from 'primeng/chips';
import {AutoComplete} from 'primeng/autocomplete';
@Component({
selector: 'app-metadata-editor',
@@ -45,7 +45,7 @@ import {Chips} from 'primeng/chips';
Tab,
TabPanels,
TabPanel,
Chips
AutoComplete
]
})
export class MetadataEditorComponent implements OnInit {
@@ -79,6 +79,24 @@ export class MetadataEditorComponent implements OnInit {
originalMetadata!: BookMetadata;
// Handle blur event for AutoComplete to add custom values
onAutoCompleteBlur(fieldName: string, event: any) {
const inputValue = event.target.value?.trim();
if (inputValue) {
const currentValue = this.metadataForm.get(fieldName)?.value || [];
const values = Array.isArray(currentValue) ? currentValue :
typeof currentValue === 'string' && currentValue ? currentValue.split(',').map((v: string) => v.trim()) : [];
// Add the new value if it's not already in the array
if (!values.includes(inputValue)) {
values.push(inputValue);
this.metadataForm.get(fieldName)?.setValue(values);
}
// Clear the input
event.target.value = '';
}
}
constructor() {
this.metadataForm = new FormGroup({
title: new FormControl(''),

View File

@@ -86,7 +86,7 @@
<p-button icon="pi pi-lock" [outlined]="true" (onClick)="toggleLock(field.controlName)" severity="warn"></p-button>
}
<div class="w-full px-4">
<p-chips formControlName="{{field.controlName}}" addOnBlur="true"></p-chips>
<p-autoComplete formControlName="{{field.controlName}}" [multiple]="true" [typeahead]="false" [dropdown]="false" [forceSelection]="false" (onBlur)="onAutoCompleteBlur(field.controlName, $event)" styleClass="w-full"></p-autoComplete>
</div>
<p-button
[icon]="isValueSaved(field.controlName) ? 'pi pi-check' : (hoveredFields[field.controlName] && isValueCopied(field.controlName) ? 'pi pi-times' : 'pi pi-arrow-left')"
@@ -103,11 +103,12 @@
(mouseleave)="onMouseLeave(field.controlName)"/>
<div class="w-full px-4">
<p-chips
<p-autoComplete
[ngModel]="fetchedMetadata[field.fetchedKey] ?? []"
[ngModelOptions]="{ standalone: true }"
[disabled]="true">
</p-chips>
[disabled]="true"
[multiple]="true" [typeahead]="false" [dropdown]="false" [forceSelection]="false" styleClass="w-full">
</p-autoComplete>
</div>
</div>
</div>

View File

@@ -13,7 +13,7 @@ import {BookService} from '../../../book/service/book.service';
import {Textarea} from 'primeng/textarea';
import {filter, map} from 'rxjs/operators';
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
import {Chips} from 'primeng/chips';
import {AutoComplete} from 'primeng/autocomplete';
@Component({
selector: 'app-metadata-picker',
@@ -31,7 +31,7 @@ import {Chips} from 'primeng/chips';
Tooltip,
AsyncPipe,
Textarea,
Chips
AutoComplete
]
})
export class MetadataPickerComponent implements OnInit {
@@ -77,6 +77,24 @@ export class MetadataPickerComponent implements OnInit {
@Input() book$!: Observable<Book | null>;
@Output() goBack = new EventEmitter<boolean>();
// Handle blur event for AutoComplete to add custom values
onAutoCompleteBlur(fieldName: string, event: any) {
const inputValue = event.target.value?.trim();
if (inputValue) {
const currentValue = this.metadataForm.get(fieldName)?.value || [];
const values = Array.isArray(currentValue) ? currentValue :
typeof currentValue === 'string' && currentValue ? currentValue.split(',').map((v: string) => v.trim()) : [];
// Add the new value if it's not already in the array
if (!values.includes(inputValue)) {
values.push(inputValue);
this.metadataForm.get(fieldName)?.setValue(values);
}
// Clear the input
event.target.value = '';
}
}
metadataForm: FormGroup;
currentBookId!: number;
copiedFields: Record<string, boolean> = {};

View File

@@ -50,7 +50,7 @@
<label for="clearAuthors" class="text-sm text-gray-300 cursor-pointer">Clear</label>
</div>
</label>
<p-chips formControlName="authors" [disabled]="clearFields.authors" addOnBlur="true"></p-chips>
<p-autoComplete formControlName="authors" [disabled]="clearFields.authors" [multiple]="true" [typeahead]="false" [dropdown]="false" [forceSelection]="false" (onBlur)="onAutoCompleteBlur('authors', $event)" styleClass="w-full"></p-autoComplete>
</div>
<!-- Publisher -->
@@ -139,7 +139,7 @@
<label for="clearGenres" class="text-sm text-gray-300 cursor-pointer">Clear</label>
</div>
</label>
<p-chips formControlName="genres" addOnBlur="true" [disabled]="clearFields.genres"></p-chips>
<p-autoComplete formControlName="genres" [disabled]="clearFields.genres" [multiple]="true" [typeahead]="false" [dropdown]="false" [forceSelection]="false" (onBlur)="onAutoCompleteBlur('genres', $event)" styleClass="w-full"></p-autoComplete>
<div class="flex items-center gap-2 mt-2">
<p-checkbox

View File

@@ -4,13 +4,13 @@ import {CommonModule} from '@angular/common';
import {InputText} from 'primeng/inputtext';
import {Button} from 'primeng/button';
import {Tooltip} from 'primeng/tooltip';
import {Chips} from 'primeng/chips';
import {DatePicker} from 'primeng/datepicker';
import {DynamicDialogConfig, DynamicDialogRef} from 'primeng/dynamicdialog';
import {MessageService} from 'primeng/api';
import {BookService} from '../../book/service/book.service';
import {Book, BulkMetadataUpdateRequest} from '../../book/model/book.model';
import {Checkbox} from 'primeng/checkbox';
import {AutoComplete} from 'primeng/autocomplete';
import {ProgressSpinner} from 'primeng/progressspinner';
@Component({
@@ -23,10 +23,10 @@ import {ProgressSpinner} from 'primeng/progressspinner';
InputText,
Button,
Tooltip,
Chips,
DatePicker,
Checkbox,
ProgressSpinner
ProgressSpinner,
AutoComplete
],
providers: [MessageService],
templateUrl: './bulk-metadata-update-component.html',
@@ -83,6 +83,24 @@ export class BulkMetadataUpdateComponent implements OnInit {
}
}
// Handle blur event for AutoComplete to add custom values
onAutoCompleteBlur(fieldName: string, event: any) {
const inputValue = event.target.value?.trim();
if (inputValue) {
const currentValue = this.metadataForm.get(fieldName)?.value || [];
const values = Array.isArray(currentValue) ? currentValue :
typeof currentValue === 'string' && currentValue ? currentValue.split(',').map((v: string) => v.trim()) : [];
// Add the new value if it's not already in the array
if (!values.includes(inputValue)) {
values.push(inputValue);
this.metadataForm.get(fieldName)?.setValue(values);
}
// Clear the input
event.target.value = '';
}
}
onSubmit(): void {
if (!this.metadataForm.valid) return;

View File

@@ -3,7 +3,6 @@ import {FormsModule} from '@angular/forms';
import {Observable} from 'rxjs';
import {Divider} from 'primeng/divider';
import {DropdownModule} from 'primeng/dropdown';
import {Select} from 'primeng/select';
import {Button} from 'primeng/button';
import {Tooltip} from 'primeng/tooltip';
@@ -21,7 +20,6 @@ import {InputText} from 'primeng/inputtext';
standalone: true,
imports: [
Divider,
DropdownModule,
Select,
Button,
Tooltip,