mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-10-30 03:56:23 -05:00 
			
		
		
		
	Merge pull request #154 from shamoon/fix/issue-152
Searchable dropdowns on document details component
This commit is contained in:
		
							
								
								
									
										8
									
								
								src-ui/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										8
									
								
								src-ui/package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -2056,6 +2056,14 @@ | ||||
|         "tslib": "^2.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "@ng-select/ng-select": { | ||||
|       "version": "5.0.9", | ||||
|       "resolved": "https://registry.npmjs.org/@ng-select/ng-select/-/ng-select-5.0.9.tgz", | ||||
|       "integrity": "sha512-YZeSAiS8/Nx/eHZJPmOOYL8YmcvSq+dr1P8WIrsKmRA7mueorBpPc5xlUj+nLQbpLtsiQvdWDQspf/ykOvD/lA==", | ||||
|       "requires": { | ||||
|         "tslib": "^2.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "@ngtools/webpack": { | ||||
|       "version": "10.2.0", | ||||
|       "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-10.2.0.tgz", | ||||
|   | ||||
| @@ -21,6 +21,7 @@ | ||||
|     "@angular/platform-browser-dynamic": "~10.1.5", | ||||
|     "@angular/router": "~10.1.5", | ||||
|     "@ng-bootstrap/ng-bootstrap": "^8.0.0", | ||||
|     "@ng-select/ng-select": "^5.0.9", | ||||
|     "bootstrap": "^4.5.0", | ||||
|     "ng-bootstrap": "^1.6.3", | ||||
|     "ng2-pdf-viewer": "^6.3.2", | ||||
|   | ||||
| @@ -54,6 +54,7 @@ import { FileSizePipe } from './pipes/file-size.pipe'; | ||||
| import { FilterPipe } from './pipes/filter.pipe'; | ||||
| import { DocumentTitlePipe } from './pipes/document-title.pipe'; | ||||
| import { MetadataCollapseComponent } from './components/document-detail/metadata-collapse/metadata-collapse.component'; | ||||
| import { NgSelectModule } from '@ng-select/ng-select'; | ||||
|  | ||||
| @NgModule({ | ||||
|   declarations: [ | ||||
| @@ -110,7 +111,8 @@ import { MetadataCollapseComponent } from './components/document-detail/metadata | ||||
|     ReactiveFormsModule, | ||||
|     NgxFileDropModule, | ||||
|     InfiniteScrollModule, | ||||
|     PdfViewerModule | ||||
|     PdfViewerModule, | ||||
|     NgSelectModule | ||||
|   ], | ||||
|   providers: [ | ||||
|     DatePipe, | ||||
|   | ||||
| @@ -1,11 +1,15 @@ | ||||
| <div class="form-group"> | ||||
| <div class="form-group paperless-input-select"> | ||||
|   <label [for]="inputId">{{title}}</label> | ||||
|   <div [class.input-group]="showPlusButton()"> | ||||
|     <select class="form-control" [id]="inputId" [(ngModel)]="value" (change)="onChange(value)" (blur)="onTouched()" | ||||
|       [disabled]="disabled" [style.color]="textColor" [style.background]="backgroundColor"> | ||||
|       <option *ngIf="allowNull" [ngValue]="null" class="form-control">---</option> | ||||
|       <option *ngFor="let i of items" [ngValue]="i.id" class="form-control">{{i.name}}</option> | ||||
|     </select> | ||||
|     <ng-select name="correspondent" [(ngModel)]="value" | ||||
|       [disabled]="disabled" | ||||
|       [style.color]="textColor" | ||||
|       [style.background]="backgroundColor" | ||||
|       (change)="onChange(value)" | ||||
|       (blur)="onTouched()"> | ||||
|       <ng-option *ngFor="let i of items" [value]="i.id">{{i.name}}</ng-option> | ||||
|     </ng-select> | ||||
|  | ||||
|     <div *ngIf="showPlusButton()" class="input-group-append"> | ||||
|       <button class="btn btn-outline-secondary" type="button" (click)="createNew.emit()"> | ||||
|         <svg class="buttonicon" fill="currentColor"> | ||||
|   | ||||
| @@ -0,0 +1 @@ | ||||
| // styles for ng-select child are in styles.scss | ||||
|   | ||||
| @@ -1,29 +1,40 @@ | ||||
| <div class="form-group"> | ||||
|   <label for="exampleFormControlTextarea1">Tags</label> | ||||
| <div class="form-group paperless-input-select paperless-input-tags"> | ||||
|   <label for="tags">Tags</label> | ||||
|  | ||||
|   <div class="input-group"> | ||||
|     <div class="form-control tags-form-control" id="tags"> | ||||
|       <app-tag class="mr-2" *ngFor="let id of displayValue" [tag]="getTag(id)" (click)="removeTag(id)"></app-tag> | ||||
|     </div> | ||||
|   <div class="input-group flex-nowrap"> | ||||
|     <ng-select name="tags" [items]="tags" bindLabel="name" bindValue="id" [(ngModel)]="displayValue" | ||||
|       [multiple]="true" | ||||
|       [closeOnSelect]="false" | ||||
|       [disabled]="disabled" | ||||
|       (change)="ngSelectChange()"> | ||||
|  | ||||
|     <div class="input-group-append" ngbDropdown placement="top-right"> | ||||
|       <button class="btn btn-outline-secondary" type="button" ngbDropdownToggle></button> | ||||
|       <div ngbDropdownMenu class="scrollable-menu shadow"> | ||||
|         <button type="button" *ngFor="let tag of tags" ngbDropdownItem (click)="addTag(tag.id)"> | ||||
|           <app-tag [tag]="tag"></app-tag> | ||||
|         </button> | ||||
|       </div> | ||||
|     </div> | ||||
|       <ng-template ng-label-tmp let-item="item"> | ||||
|         <span class="tag-wrap tag-wrap-delete" (click)="removeTag(item.id)"> | ||||
|           <svg width="1.2em" height="1em" viewBox="0 0 16 16" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> | ||||
|             <use xlink:href="assets/bootstrap-icons.svg#x"/> | ||||
|           </svg> | ||||
|           <app-tag style="background-color: none;" [tag]="getTag(item.id)"></app-tag> | ||||
|         </span> | ||||
|       </ng-template> | ||||
|       <ng-template ng-option-tmp let-item="item" let-index="index" let-search="searchTerm"> | ||||
|         <div class="tag-wrap"> | ||||
|           <div class="selected-icon d-inline-block mr-1"> | ||||
|             <svg *ngIf="displayValue.includes(item.id)" width="1em" height="1em" viewBox="0 0 16 16" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> | ||||
|               <use xlink:href="assets/bootstrap-icons.svg#check"/> | ||||
|             </svg> | ||||
|           </div> | ||||
|           <app-tag class="mr-2" [tag]="getTag(item.id)"></app-tag> | ||||
|         </div> | ||||
|       </ng-template> | ||||
|     </ng-select> | ||||
|  | ||||
|     <div class="input-group-append"> | ||||
|  | ||||
|       <button class="btn btn-outline-secondary" type="button" (click)="createTag()"> | ||||
|         <svg class="buttonicon" fill="currentColor"> | ||||
|           <use xlink:href="assets/bootstrap-icons.svg#plus" /> | ||||
|         </svg> | ||||
|       </button> | ||||
|     </div> | ||||
|  | ||||
|   </div> | ||||
|   <small class="form-text text-muted" *ngIf="hint">{{hint}}</small> | ||||
|  | ||||
|   | ||||
| @@ -1,10 +1,12 @@ | ||||
| .tags-form-control { | ||||
|   height: auto; | ||||
| .selected-icon { | ||||
|   min-width: 1em; | ||||
|   min-height: 1em; | ||||
| } | ||||
|  | ||||
|  | ||||
| .scrollable-menu { | ||||
|   height: auto; | ||||
|   max-height: 300px; | ||||
|   overflow-x: hidden; | ||||
| .tag-wrap { | ||||
|   font-size: 1rem; | ||||
| } | ||||
|  | ||||
| .tag-wrap-delete { | ||||
|   cursor: pointer; | ||||
| } | ||||
| @@ -66,29 +66,28 @@ export class TagsComponent implements OnInit, ControlValueAccessor { | ||||
|   removeTag(id) { | ||||
|     let index = this.displayValue.indexOf(id) | ||||
|     if (index > -1) { | ||||
|       this.displayValue.splice(index, 1) | ||||
|       let oldValue = this.displayValue | ||||
|       oldValue.splice(index, 1) | ||||
|       this.displayValue = [...oldValue] | ||||
|       this.onChange(this.displayValue) | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   addTag(id) { | ||||
|     let index = this.displayValue.indexOf(id) | ||||
|     if (index == -1) { | ||||
|       this.displayValue.push(id) | ||||
|       this.onChange(this.displayValue) | ||||
|     } | ||||
|   } | ||||
|  | ||||
|  | ||||
|   createTag() { | ||||
|     var modal = this.modalService.open(TagEditDialogComponent, {backdrop: 'static'}) | ||||
|     modal.componentInstance.dialogMode = 'create' | ||||
|     modal.componentInstance.success.subscribe(newTag => { | ||||
|       this.tagService.listAll().subscribe(tags => { | ||||
|         this.tags = tags.results | ||||
|         this.addTag(newTag.id) | ||||
|         this.displayValue = [...this.displayValue, newTag.id] | ||||
|         this.onChange(this.displayValue) | ||||
|       }) | ||||
|     }) | ||||
|   } | ||||
|  | ||||
|   ngSelectChange() { | ||||
|     this.value = this.displayValue | ||||
|     this.onChange(this.displayValue) | ||||
|   } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -52,9 +52,9 @@ | ||||
|                         </div> | ||||
|                         <app-input-date-time titleDate="Date created" formControlName="created"></app-input-date-time> | ||||
|                         <app-input-select [items]="correspondents" title="Correspondent" formControlName="correspondent" | ||||
|                             allowNull="true" (createNew)="createCorrespondent()"></app-input-select> | ||||
|                             (createNew)="createCorrespondent()"></app-input-select> | ||||
|                         <app-input-select [items]="documentTypes" title="Document type" formControlName="document_type" | ||||
|                             allowNull="true" (createNew)="createDocumentType()"></app-input-select> | ||||
|                             (createNew)="createDocumentType()"></app-input-select> | ||||
|                         <app-input-tags formControlName="tags" title="Tags"></app-input-tags> | ||||
|  | ||||
|                     </ng-template> | ||||
|   | ||||
| @@ -1,7 +1,6 @@ | ||||
| @import "theme"; | ||||
|  | ||||
| @import "node_modules/bootstrap/scss/bootstrap"; | ||||
|  | ||||
| @import "~@ng-select/ng-select/themes/default.theme.css"; | ||||
|  | ||||
| .toolbaricon { | ||||
|   width: 1.2em; | ||||
| @@ -20,7 +19,7 @@ | ||||
| } | ||||
|  | ||||
| body { | ||||
|   font-size: .875rem; | ||||
|   font-size: 0.875rem; | ||||
| } | ||||
|  | ||||
| .form-control-dark { | ||||
| @@ -66,3 +65,38 @@ body { | ||||
|   background-size: 1rem; | ||||
|   float: right; | ||||
| } | ||||
|  | ||||
| .paperless-input-select { | ||||
|   .ng-select { | ||||
|     position: relative; | ||||
|     flex: 1 1 auto; | ||||
|     margin-bottom: 0; | ||||
|     min-height: calc(1.5em + 0.75rem + 5px); | ||||
|     line-height: 1.5; | ||||
|  | ||||
|     .ng-select-container { | ||||
|       height: 100%; | ||||
|       border-top-right-radius: 0; | ||||
|       border-bottom-right-radius: 0; | ||||
|  | ||||
|       .ng-value-container .ng-input { | ||||
|         top: 10px; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     .ng-dropdown-panel .ng-dropdown-panel-items .ng-option.ng-option-selected, | ||||
|     .ng-dropdown-panel .ng-dropdown-panel-items .ng-option.ng-option-selected.ng-option-marked { | ||||
|       background: none; | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| .paperless-input-tags { | ||||
|   .ng-select.ng-select-multiple .ng-select-container .ng-value-container .ng-value { | ||||
|     background-color: transparent; | ||||
|   } | ||||
|  | ||||
|   .ng-select.ng-select-multiple .ng-select-container .ng-value-container { | ||||
|     padding-top: 1px; | ||||
|   } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Jonas Winkler
					Jonas Winkler