routing
This commit is contained in:
@@ -1,21 +1,21 @@
|
|||||||
import {SongsComponent} from './components/songs/songs.component';
|
|
||||||
import {NgModule} from '@angular/core';
|
import {NgModule} from '@angular/core';
|
||||||
import {RouterModule, Routes} from '@angular/router';
|
import {RouterModule, Routes} from '@angular/router';
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{
|
|
||||||
path: 'songs',
|
|
||||||
component: SongsComponent
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
redirectTo: 'songs',
|
pathMatch: 'full',
|
||||||
pathMatch: 'full'
|
redirectTo: 'songs'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'songs',
|
||||||
|
loadChildren: () => import('./songs/songs.module').then(_ => _.SongsModule)
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [RouterModule.forRoot(routes)],
|
imports: [RouterModule.forRoot(routes, {onSameUrlNavigation: 'reload'})],
|
||||||
exports: [RouterModule]
|
exports: [RouterModule]
|
||||||
})
|
})
|
||||||
export class AppRoutingModule {
|
export class AppRoutingModule {
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
:root {
|
||||||
|
display: block;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|||||||
@@ -18,28 +18,11 @@ import {MatRadioModule} from '@angular/material/radio';
|
|||||||
import {MatSelectModule} from '@angular/material/select';
|
import {MatSelectModule} from '@angular/material/select';
|
||||||
import {MatTooltipModule} from '@angular/material/tooltip';
|
import {MatTooltipModule} from '@angular/material/tooltip';
|
||||||
import {MatProgressBarModule} from '@angular/material/progress-bar';
|
import {MatProgressBarModule} from '@angular/material/progress-bar';
|
||||||
|
|
||||||
import {TableComponent} from './components/songs/table/table.component';
|
|
||||||
import {SongsComponent} from './components/songs/songs.component';
|
|
||||||
import {SongComponent} from './components/songs/song/song.component';
|
|
||||||
import {SongEditComponent} from './components/songs/song-edit/song-edit.component';
|
|
||||||
import {SongNewComponent} from './components/songs/song-new/song-new.component';
|
|
||||||
import {SongFormComponent} from './components/songs/song-form/song-form.component';
|
|
||||||
import {SongFilesComponent} from './components/songs/song-files/song-files.component';
|
|
||||||
import {FileUploadModule} from 'ng2-file-upload';
|
import {FileUploadModule} from 'ng2-file-upload';
|
||||||
import {SongFileEditComponent} from './components/songs/song-file-edit/song-file-edit.component';
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
AppComponent,
|
AppComponent
|
||||||
SongsComponent,
|
|
||||||
TableComponent,
|
|
||||||
SongComponent,
|
|
||||||
SongEditComponent,
|
|
||||||
SongNewComponent,
|
|
||||||
SongFormComponent,
|
|
||||||
SongFilesComponent,
|
|
||||||
SongFileEditComponent
|
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
|
|||||||
@@ -1,71 +0,0 @@
|
|||||||
import {switchMap} from 'rxjs/operators';
|
|
||||||
import {ChangeDetectorRef, Component} from '@angular/core';
|
|
||||||
import {Song} from 'src/app/models/song.model';
|
|
||||||
import {SongsService} from 'src/app/data/songs.service';
|
|
||||||
import {DownloadService} from 'src/app/data/download.service';
|
|
||||||
import {faDownload, faEdit, faFileUpload, faLongArrowAltLeft, faTrash} from '@fortawesome/free-solid-svg-icons';
|
|
||||||
import {FileuploadFactory} from 'src/app/services/fileupload.factory';
|
|
||||||
import {FileUploader} from 'ng2-file-upload';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-song-files',
|
|
||||||
templateUrl: './song-files.component.html',
|
|
||||||
styleUrls: ['./song-files.component.less']
|
|
||||||
})
|
|
||||||
export class SongFilesComponent {
|
|
||||||
public song: Song;
|
|
||||||
public selectedSongId = 0;
|
|
||||||
public faFileUpload = faFileUpload;
|
|
||||||
public faTrash = faTrash;
|
|
||||||
public faArrow = faLongArrowAltLeft;
|
|
||||||
public faDownload = faDownload;
|
|
||||||
public faEdit = faEdit;
|
|
||||||
public columns = ['name', 'action'];
|
|
||||||
public newFileUploader: FileUploader;
|
|
||||||
public fileEditId: number;
|
|
||||||
|
|
||||||
public fileOverNew = false;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private downloadService: DownloadService,
|
|
||||||
private fileuploadFactory: FileuploadFactory,
|
|
||||||
private songService: SongsService,
|
|
||||||
change: ChangeDetectorRef
|
|
||||||
) {
|
|
||||||
songService.selectedSong.subscribe(_ => {
|
|
||||||
if (_) {
|
|
||||||
this.selectedSongId = _.ID;
|
|
||||||
this.song = _;
|
|
||||||
this.newFileUploader = FileuploadFactory.provideForNewFiles(_.ID);
|
|
||||||
this.newFileUploader.onCompleteItem = () =>
|
|
||||||
songService.selectSong(_.ID).subscribe();
|
|
||||||
this.newFileUploader.onProgressItem = () => change.markForCheck;
|
|
||||||
} else {
|
|
||||||
this.selectedSongId = 0;
|
|
||||||
this.song = null;
|
|
||||||
this.newFileUploader = null;
|
|
||||||
}
|
|
||||||
change.markForCheck();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public onClickDownload(fileId: number, filename): void {
|
|
||||||
this.downloadService.get(this.selectedSongId, fileId, filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
public onFileOverNew(hover: boolean): void {
|
|
||||||
this.fileOverNew = hover;
|
|
||||||
}
|
|
||||||
|
|
||||||
public onClickEdit(fileId: number): void {
|
|
||||||
this.fileEditId = fileId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public onClickDelete(fileId: number): void {
|
|
||||||
const songId = this.song.ID;
|
|
||||||
this.songService
|
|
||||||
.deleteFile$(songId, fileId)
|
|
||||||
.pipe(switchMap(() => this.songService.selectSong(songId)))
|
|
||||||
.subscribe();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
import {SongsService} from 'src/app/data/songs.service';
|
|
||||||
import {ChangeDetectionStrategy, ChangeDetectorRef, Component} from '@angular/core';
|
|
||||||
import {faEdit, faLongArrowAltLeft} from '@fortawesome/free-solid-svg-icons';
|
|
||||||
import {Song} from 'src/app/models/song.model';
|
|
||||||
import {State} from 'src/app/data/state';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-song',
|
|
||||||
templateUrl: './song.component.html',
|
|
||||||
styleUrls: ['./song.component.less'],
|
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush
|
|
||||||
})
|
|
||||||
export class SongComponent {
|
|
||||||
public song: Song;
|
|
||||||
public faArrow = faLongArrowAltLeft;
|
|
||||||
public faEdit = faEdit;
|
|
||||||
public selectedSongId = 0;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private songService: SongsService,
|
|
||||||
change: ChangeDetectorRef
|
|
||||||
) {
|
|
||||||
songService.selectedSong.subscribe(_ => {
|
|
||||||
if (_) {
|
|
||||||
this.selectedSongId = _.ID;
|
|
||||||
this.song = _;
|
|
||||||
} else {
|
|
||||||
this.selectedSongId = 0;
|
|
||||||
this.song = null;
|
|
||||||
}
|
|
||||||
change.markForCheck();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public get text(): string[] {
|
|
||||||
return this.song && this.song.Text ? this.song.Text.split(/\r?\n/) : [];
|
|
||||||
}
|
|
||||||
|
|
||||||
public get comments(): string[] {
|
|
||||||
return this.song && this.song.Comments ? this.song.Comments.split(/\r?\n/) : [];
|
|
||||||
}
|
|
||||||
|
|
||||||
public onBack(): void {
|
|
||||||
this.songService.resetSelectedSong();
|
|
||||||
}
|
|
||||||
|
|
||||||
public onClickEdit(): void {
|
|
||||||
this.songService.state.next(State.edit);
|
|
||||||
}
|
|
||||||
|
|
||||||
public renderSongType(songType: string) {
|
|
||||||
switch (songType) {
|
|
||||||
case 'Praise':
|
|
||||||
return {name: 'Lobpreis', color: '#99FFB8'};
|
|
||||||
case 'Worship':
|
|
||||||
return {name: 'Anbetung', color: '#C999FF'};
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
<div class="songs-container">
|
|
||||||
<app-table></app-table>
|
|
||||||
<div class="song-container">
|
|
||||||
<app-song-edit *ngIf="(songsService.state | async) === State.edit"></app-song-edit>
|
|
||||||
<app-song-new *ngIf="(songsService.state | async) === State.new"></app-song-new>
|
|
||||||
<app-song *ngIf="(songsService.state | async) === State.read"></app-song>
|
|
||||||
<app-song-files *ngIf="(songsService.state | async) === State.read"></app-song-files>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@@ -3,7 +3,7 @@ import {Observable} from 'rxjs';
|
|||||||
import {map, tap} from 'rxjs/operators';
|
import {map, tap} from 'rxjs/operators';
|
||||||
import {base} from './urls';
|
import {base} from './urls';
|
||||||
|
|
||||||
export class OdataService {
|
export class ODataBaseService {
|
||||||
private url: string;
|
private url: string;
|
||||||
|
|
||||||
constructor(private odataService: ODataService, private entity: string) {
|
constructor(private odataService: ODataService, private entity: string) {
|
||||||
@@ -21,14 +21,14 @@ export class OdataService {
|
|||||||
|
|
||||||
public get$<TResponse>(
|
public get$<TResponse>(
|
||||||
id: number,
|
id: number,
|
||||||
properties: string[],
|
select: string[],
|
||||||
expands: string[]
|
expands: string[]
|
||||||
): Observable<TResponse> {
|
): Observable<TResponse> {
|
||||||
const query = new ODataQuery(this.odataService, this.url)
|
const query = new ODataQuery(this.odataService, this.url)
|
||||||
.entitySet(this.entity)
|
.entitySet(this.entity)
|
||||||
.entityKey(id)
|
.entityKey(id)
|
||||||
.expand(expands.map(_ => new Expand(_)))
|
.expand(expands.map(_ => new Expand(_)))
|
||||||
.select(properties);
|
.select(select);
|
||||||
const get = query.get().pipe(map(_ => _.toEntity<TResponse>()));
|
const get = query.get().pipe(map(_ => _.toEntity<TResponse>()));
|
||||||
|
|
||||||
return get;
|
return get;
|
||||||
@@ -2,19 +2,21 @@ import {SongsService} from 'src/app/data/songs.service';
|
|||||||
import {Injectable} from '@angular/core';
|
import {Injectable} from '@angular/core';
|
||||||
import {FormControl, FormGroup, Validators} from '@angular/forms';
|
import {FormControl, FormGroup, Validators} from '@angular/forms';
|
||||||
import {switchMap} from 'rxjs/operators';
|
import {switchMap} from 'rxjs/operators';
|
||||||
import {Song} from '../models/song.model';
|
import {Song} from '../songs/models/song.model';
|
||||||
import {Subscription} from 'rxjs';
|
import {Subscription} from 'rxjs';
|
||||||
|
import {File} from '../songs/models/file.model';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class EditSongService {
|
export class EditSongService {
|
||||||
|
|
||||||
constructor(private songsService: SongsService) {
|
constructor(private songsService: SongsService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public initSongEditForm(attachSync: boolean): FormGroup {
|
public initSongEditForm(attachSync: boolean, _song: Song): FormGroup {
|
||||||
const song = attachSync
|
const song = attachSync
|
||||||
? this.songsService.selectedSong.value
|
? _song
|
||||||
: this.defaultValues();
|
: this.defaultValues();
|
||||||
const form = new FormGroup({
|
const form = new FormGroup({
|
||||||
Number: new FormControl(song.Number, {
|
Number: new FormControl(song.Number, {
|
||||||
@@ -45,10 +47,7 @@ export class EditSongService {
|
|||||||
return form;
|
return form;
|
||||||
}
|
}
|
||||||
|
|
||||||
public initFileEditForm(songId: number, fileId: number): { form: FormGroup, changeSubscription: Subscription } {
|
public initFileEditForm(songId: number, file: File): { form: FormGroup, changeSubscription: Subscription } {
|
||||||
const file = this.songsService.selectedSong.value.Files.filter(
|
|
||||||
_ => _.ID === fileId
|
|
||||||
)[0];
|
|
||||||
const form = new FormGroup({
|
const form = new FormGroup({
|
||||||
Name: new FormControl(file.Name, {
|
Name: new FormControl(file.Name, {
|
||||||
updateOn: 'blur',
|
updateOn: 'blur',
|
||||||
@@ -60,8 +59,7 @@ export class EditSongService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const changeSubscription = form.valueChanges.pipe(
|
const changeSubscription = form.valueChanges.pipe(
|
||||||
switchMap(_ => this.songsService.updateFile$(songId, fileId, _.Name, _.FileType)),
|
switchMap(_ => this.songsService.updateFile$(songId, file.ID, _.Name, _.FileType)),
|
||||||
switchMap(() => this.songsService.selectSong(songId))
|
|
||||||
).subscribe();
|
).subscribe();
|
||||||
|
|
||||||
return {form: form, changeSubscription: changeSubscription};
|
return {form: form, changeSubscription: changeSubscription};
|
||||||
@@ -73,7 +71,6 @@ export class EditSongService {
|
|||||||
form.controls[control].valueChanges
|
form.controls[control].valueChanges
|
||||||
.pipe(
|
.pipe(
|
||||||
switchMap(value => this.songsService.patch$(song.ID, control, value)),
|
switchMap(value => this.songsService.patch$(song.ID, control, value)),
|
||||||
switchMap(() => this.songsService.selectSong(song.ID))
|
|
||||||
)
|
)
|
||||||
.subscribe();
|
.subscribe();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,22 +1,21 @@
|
|||||||
import {HttpClient} from '@angular/common/http';
|
import {HttpClient} from '@angular/common/http';
|
||||||
import {FileType} from '../models/files-types.model.ts';
|
|
||||||
import {Injectable} from '@angular/core';
|
import {Injectable} from '@angular/core';
|
||||||
import {ODataService} from 'odata-v4-ng';
|
import {ODataService} from 'odata-v4-ng';
|
||||||
import {OdataService} from './odata.service';
|
import {ODataBaseService} from './ODataBaseService';
|
||||||
import {Song} from '../models/song.model';
|
import {Song} from '../songs/models/song.model';
|
||||||
import {BehaviorSubject, Observable} from 'rxjs';
|
import {BehaviorSubject, Observable} from 'rxjs';
|
||||||
import {switchMap, tap} from 'rxjs/operators';
|
import {tap} from 'rxjs/operators';
|
||||||
import {State} from './state';
|
import {State} from './state';
|
||||||
import {base} from './urls';
|
import {base} from './urls';
|
||||||
|
import {FileType} from '../songs/models/files-types.model';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class SongsService extends OdataService {
|
export class SongsService extends ODataBaseService {
|
||||||
public state = new BehaviorSubject<State>(State.list);
|
public state = new BehaviorSubject<State>(State.list);
|
||||||
|
|
||||||
public songs: BehaviorSubject<Song[]> = new BehaviorSubject<Song[]>([]);
|
public songs: BehaviorSubject<Song[]> = new BehaviorSubject<Song[]>([]);
|
||||||
public selectedSong: BehaviorSubject<Song> = new BehaviorSubject<Song>(null);
|
|
||||||
|
|
||||||
constructor(odataService: ODataService, private httpClient: HttpClient) {
|
constructor(odataService: ODataService, private httpClient: HttpClient) {
|
||||||
super(odataService, 'songs');
|
super(odataService, 'songs');
|
||||||
@@ -30,40 +29,6 @@ export class SongsService extends OdataService {
|
|||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
public loadSongListAndGoTo$(id: number): Observable<Song> {
|
|
||||||
const properties = ['ID', 'Name', 'Number', 'SongType', 'Key', 'Tempo'];
|
|
||||||
const list = this.list$<Song>(properties).pipe(
|
|
||||||
tap(_ => {
|
|
||||||
this.songs.next(_);
|
|
||||||
}),
|
|
||||||
switchMap(() => this.selectSong(id))
|
|
||||||
);
|
|
||||||
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
public selectSong(id: number): Observable<Song> {
|
|
||||||
this.state.next(State.read);
|
|
||||||
const filter = this.songs.value.filter(_ => _.ID === id);
|
|
||||||
const song = filter.length === 1 ? filter[0] : null;
|
|
||||||
if (!song) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const get = this.get$<Song>(id, ['Text', 'Comments'], ['Files']).pipe(tap(_ => {
|
|
||||||
song.Text = _.Text;
|
|
||||||
song.Comments = _.Comments;
|
|
||||||
song.Files = _.Files;
|
|
||||||
this.selectedSong.next(song);
|
|
||||||
}));
|
|
||||||
|
|
||||||
return get;
|
|
||||||
}
|
|
||||||
|
|
||||||
public resetSelectedSong() {
|
|
||||||
this.state.next(State.list);
|
|
||||||
this.selectedSong.next(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public patch$(id: number, control: string, value: any): Observable<boolean> {
|
public patch$(id: number, control: string, value: any): Observable<boolean> {
|
||||||
const patch = super.patch$(id, control, value).pipe(
|
const patch = super.patch$(id, control, value).pipe(
|
||||||
@@ -72,7 +37,6 @@ export class SongsService extends OdataService {
|
|||||||
const song = songs.filter(_ => _.ID === id)[0];
|
const song = songs.filter(_ => _.ID === id)[0];
|
||||||
song[control] = value;
|
song[control] = value;
|
||||||
this.songs.next(songs);
|
this.songs.next(songs);
|
||||||
this.selectedSong.next(song);
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -80,9 +44,7 @@ export class SongsService extends OdataService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public saveNewSong$(values: any): Observable<Song> {
|
public saveNewSong$(values: any): Observable<Song> {
|
||||||
const newSong = super
|
const newSong = super.post$<Song>(values);
|
||||||
.post$<Song>(values)
|
|
||||||
.pipe(switchMap(_ => this.loadSongListAndGoTo$(_.ID)));
|
|
||||||
|
|
||||||
return newSong;
|
return newSong;
|
||||||
}
|
}
|
||||||
@@ -93,16 +55,7 @@ export class SongsService extends OdataService {
|
|||||||
name: string,
|
name: string,
|
||||||
fileType: FileType
|
fileType: FileType
|
||||||
): Observable<any> {
|
): Observable<any> {
|
||||||
const url =
|
const url = `${base}/api/songs/${songId}/files/${fileId}/edit?Name=${name}&FileType=${fileType}`;
|
||||||
base +
|
|
||||||
'/api/songs/' +
|
|
||||||
songId +
|
|
||||||
'/files/' +
|
|
||||||
fileId +
|
|
||||||
'/edit?Name=' +
|
|
||||||
name +
|
|
||||||
'&FileType=' +
|
|
||||||
fileType;
|
|
||||||
const get = this.httpClient.get(url);
|
const get = this.httpClient.get(url);
|
||||||
return get;
|
return get;
|
||||||
}
|
}
|
||||||
@@ -111,13 +64,7 @@ export class SongsService extends OdataService {
|
|||||||
songId: number,
|
songId: number,
|
||||||
fileId: number
|
fileId: number
|
||||||
): Observable<any> {
|
): Observable<any> {
|
||||||
const url =
|
const url = `${base}/api/songs/${songId}/files/${fileId}/delete`;
|
||||||
base +
|
|
||||||
'/api/songs/' +
|
|
||||||
songId +
|
|
||||||
'/files/' +
|
|
||||||
fileId +
|
|
||||||
'/delete';
|
|
||||||
const get = this.httpClient.get(url);
|
const get = this.httpClient.get(url);
|
||||||
return get;
|
return get;
|
||||||
}
|
}
|
||||||
|
|||||||
6
WEB/src/app/songs/components/songs/songs.component.html
Normal file
6
WEB/src/app/songs/components/songs/songs.component.html
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<div class="songs-container">
|
||||||
|
<app-table></app-table>
|
||||||
|
<div class="song-container">
|
||||||
|
<router-outlet></router-outlet>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -3,6 +3,5 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.song-container {
|
.song-container {
|
||||||
padding: 10px;
|
|
||||||
flex-flow: column;
|
flex-flow: column;
|
||||||
}
|
}
|
||||||
@@ -4,11 +4,11 @@
|
|||||||
>
|
>
|
||||||
<div class="table-container">
|
<div class="table-container">
|
||||||
<button
|
<button
|
||||||
(click)="onClickNew()"
|
|
||||||
class="button-new"
|
class="button-new"
|
||||||
mat-icon-button
|
mat-icon-button
|
||||||
matTooltip="neuen Titel anlegen"
|
matTooltip="neuen Titel anlegen"
|
||||||
matTooltipPosition="left"
|
matTooltipPosition="left"
|
||||||
|
[routerLink]="'/songs/new'"
|
||||||
>
|
>
|
||||||
<fa-icon [icon]="faNew"></fa-icon>
|
<fa-icon [icon]="faNew"></fa-icon>
|
||||||
</button>
|
</button>
|
||||||
@@ -65,10 +65,10 @@
|
|||||||
|
|
||||||
<tr *matHeaderRowDef="columns | async; sticky: true" mat-header-row></tr>
|
<tr *matHeaderRowDef="columns | async; sticky: true" mat-header-row></tr>
|
||||||
<tr
|
<tr
|
||||||
(click)="onClick(row.ID)"
|
|
||||||
*matRowDef="let row; columns: columns | async"
|
*matRowDef="let row; columns: columns | async"
|
||||||
[class.selected]="selectedSongId === row.ID"
|
[routerLink]="'/songs/' + row.ID"
|
||||||
mat-row
|
mat-row
|
||||||
|
routerLinkActive="selected"
|
||||||
></tr>
|
></tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
@@ -1,10 +1,26 @@
|
|||||||
table {
|
table {
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
background: #fffe;
|
background: #fffe;
|
||||||
|
|
||||||
|
tr.selected {
|
||||||
|
background-color: #0002;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: #0001;
|
||||||
|
|
||||||
|
td {
|
||||||
|
color: #ff9900;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
td.mat-cell {
|
||||||
|
padding: 0 5px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-container {
|
.table-container {
|
||||||
//height: 100%;
|
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
@@ -23,3 +39,4 @@ table {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import {SongsService} from '../../../data/songs.service';
|
import {SongsService} from '../../../../data/songs.service';
|
||||||
import {ChangeDetectionStrategy, ChangeDetectorRef, Component} from '@angular/core';
|
import {ChangeDetectionStrategy, ChangeDetectorRef, Component} from '@angular/core';
|
||||||
import {State} from 'src/app/data/state';
|
import {State} from 'src/app/data/state';
|
||||||
import {faFileMedical} from '@fortawesome/free-solid-svg-icons';
|
import {faFileMedical} from '@fortawesome/free-solid-svg-icons';
|
||||||
@@ -12,7 +12,6 @@ import {map} from 'rxjs/operators';
|
|||||||
changeDetection: ChangeDetectionStrategy.OnPush
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
})
|
})
|
||||||
export class TableComponent {
|
export class TableComponent {
|
||||||
public selectedSongId = 0;
|
|
||||||
public State = State;
|
public State = State;
|
||||||
public faNew = faFileMedical;
|
public faNew = faFileMedical;
|
||||||
public columnsFull = ['Number', 'Name', 'Key', 'SongType', 'Tempo'];
|
public columnsFull = ['Number', 'Name', 'Key', 'SongType', 'Tempo'];
|
||||||
@@ -22,11 +21,6 @@ export class TableComponent {
|
|||||||
public songsService: SongsService,
|
public songsService: SongsService,
|
||||||
private change: ChangeDetectorRef
|
private change: ChangeDetectorRef
|
||||||
) {
|
) {
|
||||||
songsService.selectedSong.subscribe(_ => {
|
|
||||||
this.selectedSongId = _ ? _.ID : 0;
|
|
||||||
this.change.markForCheck();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public get columns(): Observable<string[]> {
|
public get columns(): Observable<string[]> {
|
||||||
@@ -43,16 +37,4 @@ export class TableComponent {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public onClick(id: number): void {
|
|
||||||
this.songsService.selectSong(id).subscribe();
|
|
||||||
this.change.detectChanges();
|
|
||||||
}
|
|
||||||
|
|
||||||
public onClickNew(): void {
|
|
||||||
this.songsService.selectSong(null).subscribe();
|
|
||||||
this.songsService.state.next(State.new);
|
|
||||||
this.change.detectChanges();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import {FileType} from './files-types.model.ts';
|
import {FileType} from './files-types.model';
|
||||||
|
|
||||||
export interface File {
|
export interface File {
|
||||||
ID: number;
|
ID: number;
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
<mat-card *ngIf="form" class="mat-elevation-z8">
|
<mat-card *ngIf="form" class="mat-elevation-z8">
|
||||||
<mat-card-header>
|
<mat-card-header>
|
||||||
<div mat-card-avatar>
|
<div mat-card-avatar>
|
||||||
<button (click)="onBack()" color="warn" mat-icon-button>
|
<button [routerLink]="'/songs/'+ songId +'/read'" color="warn" mat-icon-button>
|
||||||
<fa-icon [icon]="faArrow"></fa-icon>
|
<fa-icon [icon]="faArrow"></fa-icon>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -14,11 +14,5 @@
|
|||||||
<mat-card-content>
|
<mat-card-content>
|
||||||
<app-song-form [form]="form"></app-song-form>
|
<app-song-form [form]="form"></app-song-form>
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
<!-- <mat-card-actions>
|
|
||||||
<button mat-button (click)="onClickDownload()">Herunterladen</button>
|
|
||||||
<button mat-button (click)="onClickEdit()">
|
|
||||||
<fa-icon [icon]="faEdit"></fa-icon> Bearbeiten
|
|
||||||
</button>
|
|
||||||
</mat-card-actions> -->
|
|
||||||
</mat-card>
|
</mat-card>
|
||||||
</div>
|
</div>
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
import {SongsService} from 'src/app/data/songs.service';
|
|
||||||
import {FormGroup} from '@angular/forms';
|
import {FormGroup} from '@angular/forms';
|
||||||
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit} from '@angular/core';
|
import {ChangeDetectionStrategy, Component, OnInit} from '@angular/core';
|
||||||
import {EditSongService} from 'src/app/data/edit-song.service';
|
import {EditSongService} from 'src/app/data/edit-song.service';
|
||||||
import {faLongArrowAltLeft} from '@fortawesome/free-solid-svg-icons';
|
import {faLongArrowAltLeft} from '@fortawesome/free-solid-svg-icons';
|
||||||
import {State} from 'src/app/data/state';
|
import {ActivatedRoute} from '@angular/router';
|
||||||
|
import {Song} from '../../../models/song.model';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-song-edit',
|
selector: 'app-song-edit',
|
||||||
@@ -14,21 +14,16 @@ import {State} from 'src/app/data/state';
|
|||||||
export class SongEditComponent implements OnInit {
|
export class SongEditComponent implements OnInit {
|
||||||
public form: FormGroup = null;
|
public form: FormGroup = null;
|
||||||
public faArrow = faLongArrowAltLeft;
|
public faArrow = faLongArrowAltLeft;
|
||||||
|
public songId: number;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private editSongService: EditSongService,
|
private editSongService: EditSongService, private route: ActivatedRoute) {
|
||||||
private songsService: SongsService,
|
|
||||||
private change: ChangeDetectorRef
|
|
||||||
) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.form = this.editSongService.initSongEditForm(true);
|
this.route.data.subscribe((data: { song: Song }) => {
|
||||||
this.change.markForCheck();
|
this.songId = data.song.ID;
|
||||||
}
|
this.form = this.editSongService.initSongEditForm(true, data.song);
|
||||||
|
});
|
||||||
public onBack(): void {
|
|
||||||
this.songsService.state.next(State.read);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
import {EditSongService} from '../../../data/edit-song.service';
|
import {EditSongService} from '../../../../data/edit-song.service';
|
||||||
import {FormGroup} from '@angular/forms';
|
import {FormGroup} from '@angular/forms';
|
||||||
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit} from '@angular/core';
|
import {ChangeDetectionStrategy, Component, OnInit} from '@angular/core';
|
||||||
import {faLongArrowAltLeft, faSave} from '@fortawesome/free-solid-svg-icons';
|
import {faLongArrowAltLeft, faSave} from '@fortawesome/free-solid-svg-icons';
|
||||||
import {State} from 'src/app/data/state';
|
|
||||||
import {SongsService} from 'src/app/data/songs.service';
|
import {SongsService} from 'src/app/data/songs.service';
|
||||||
|
import {Router} from '@angular/router';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-song-new',
|
selector: 'app-song-new',
|
||||||
@@ -19,22 +19,18 @@ export class SongNewComponent implements OnInit {
|
|||||||
constructor(
|
constructor(
|
||||||
private editSongService: EditSongService,
|
private editSongService: EditSongService,
|
||||||
private songsService: SongsService,
|
private songsService: SongsService,
|
||||||
private change: ChangeDetectorRef
|
private router: Router
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.form = this.editSongService.initSongEditForm(false);
|
this.form = this.editSongService.initSongEditForm(false, null);
|
||||||
this.change.markForCheck();
|
|
||||||
}
|
|
||||||
|
|
||||||
public onBack(): void {
|
|
||||||
this.songsService.state.next(State.list);
|
|
||||||
this.songsService.resetSelectedSong();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public onClickAdd(): void {
|
public onClickAdd(): void {
|
||||||
this.songsService.saveNewSong$(this.form.value).subscribe();
|
this.songsService.saveNewSong$(this.form.value).subscribe(song => {
|
||||||
|
this.router.navigateByUrl('/songs/' + song.ID + '/read');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,17 +1,18 @@
|
|||||||
import {SongsService} from '../../../data/songs.service';
|
|
||||||
import {FileType} from '../../../models/files-types.model.ts';
|
|
||||||
import {FormGroup} from '@angular/forms';
|
import {FormGroup} from '@angular/forms';
|
||||||
import {Component, EventEmitter, Input, OnDestroy, OnInit, Output} from '@angular/core';
|
import {Component, EventEmitter, Input, OnDestroy, OnInit, Output} from '@angular/core';
|
||||||
import {EditSongService} from 'src/app/data/edit-song.service';
|
import {EditSongService} from 'src/app/data/edit-song.service';
|
||||||
import {Subscription} from 'rxjs';
|
import {Subscription} from 'rxjs';
|
||||||
|
import {FileType} from '../../../../models/files-types.model';
|
||||||
|
import {File} from '../../../../models/file.model';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-song-file-edit',
|
selector: 'app-song-file-edit',
|
||||||
templateUrl: './song-file-edit.component.html',
|
templateUrl: './song-files-edit.component.html',
|
||||||
styleUrls: ['./song-file-edit.component.less']
|
styleUrls: ['./song-files-edit.component.less']
|
||||||
})
|
})
|
||||||
export class SongFileEditComponent implements OnInit, OnDestroy {
|
export class SongFilesEditComponent implements OnInit, OnDestroy {
|
||||||
@Input() fileId: number;
|
@Input() file: File;
|
||||||
|
@Input() songId: number;
|
||||||
@Output() back = new EventEmitter();
|
@Output() back = new EventEmitter();
|
||||||
public form: FormGroup;
|
public form: FormGroup;
|
||||||
public subscription: Subscription;
|
public subscription: Subscription;
|
||||||
@@ -23,15 +24,13 @@ export class SongFileEditComponent implements OnInit, OnDestroy {
|
|||||||
];
|
];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private editSongService: EditSongService,
|
private editSongService: EditSongService) {
|
||||||
private songService: SongsService
|
|
||||||
) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ngOnInit(): void {
|
public ngOnInit(): void {
|
||||||
const form = this.editSongService.initFileEditForm(
|
const form = this.editSongService.initFileEditForm(
|
||||||
this.songService.selectedSong.value.ID,
|
this.songId,
|
||||||
this.fileId
|
this.file,
|
||||||
);
|
);
|
||||||
this.form = form.form;
|
this.form = form.form;
|
||||||
this.subscription = form.changeSubscription;
|
this.subscription = form.changeSubscription;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<div *ngIf="song" class="song-detail-container files">
|
<div class="song-detail-container files">
|
||||||
<mat-card class="mat-elevation-z8">
|
<mat-card class="mat-elevation-z8">
|
||||||
<mat-card-content>
|
<mat-card-content>
|
||||||
<table [dataSource]="song.Files" class="mat-elevation-z8" mat-table>
|
<table [dataSource]="song.Files" class="mat-elevation-z8" mat-table>
|
||||||
@@ -11,7 +11,8 @@
|
|||||||
<app-song-file-edit
|
<app-song-file-edit
|
||||||
(back)="onClickEdit(null)"
|
(back)="onClickEdit(null)"
|
||||||
*ngIf="fileEditId === element.ID"
|
*ngIf="fileEditId === element.ID"
|
||||||
[fileId]="element.ID"
|
[file]="element"
|
||||||
|
[songId]="song.ID"
|
||||||
></app-song-file-edit>
|
></app-song-file-edit>
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
56
WEB/src/app/songs/modules/read/files/files.component.ts
Normal file
56
WEB/src/app/songs/modules/read/files/files.component.ts
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import {Component, Input, OnInit} from '@angular/core';
|
||||||
|
import {Song} from 'src/app/songs/models/song.model';
|
||||||
|
import {SongsService} from 'src/app/data/songs.service';
|
||||||
|
import {DownloadService} from 'src/app/data/download.service';
|
||||||
|
import {faDownload, faEdit, faFileUpload, faLongArrowAltLeft, faTrash} from '@fortawesome/free-solid-svg-icons';
|
||||||
|
import {FileuploadFactory} from 'src/app/songs/services/fileupload.factory';
|
||||||
|
import {FileUploader} from 'ng2-file-upload';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-song-files',
|
||||||
|
templateUrl: './files.component.html',
|
||||||
|
styleUrls: ['./files.component.less']
|
||||||
|
})
|
||||||
|
export class SongFilesComponent implements OnInit {
|
||||||
|
@Input() public song: Song;
|
||||||
|
public faFileUpload = faFileUpload;
|
||||||
|
public faTrash = faTrash;
|
||||||
|
public faArrow = faLongArrowAltLeft;
|
||||||
|
public faDownload = faDownload;
|
||||||
|
public faEdit = faEdit;
|
||||||
|
public columns = ['name', 'action'];
|
||||||
|
public newFileUploader: FileUploader;
|
||||||
|
public fileEditId: number;
|
||||||
|
|
||||||
|
public fileOverNew = false;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private downloadService: DownloadService,
|
||||||
|
private fileuploadFactory: FileuploadFactory,
|
||||||
|
private songService: SongsService
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public ngOnInit(): void {
|
||||||
|
this.newFileUploader = FileuploadFactory.provideForNewFiles(this.song.ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public onClickDownload(fileId: number, filename): void {
|
||||||
|
this.downloadService.get(this.song.ID, fileId, filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
public onFileOverNew(hover: boolean): void {
|
||||||
|
this.fileOverNew = hover;
|
||||||
|
}
|
||||||
|
|
||||||
|
public onClickEdit(fileId: number): void {
|
||||||
|
this.fileEditId = fileId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public onClickDelete(fileId: number): void {
|
||||||
|
const songId = this.song.ID;
|
||||||
|
this.songService
|
||||||
|
.deleteFile$(songId, fileId)
|
||||||
|
.subscribe();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
<div *ngIf="song" class="song-detail-container">
|
<div class="song-detail-container">
|
||||||
<mat-card class="mat-elevation-z8">
|
<mat-card class="mat-elevation-z8">
|
||||||
<mat-card-header>
|
<mat-card-header>
|
||||||
<div mat-card-avatar>
|
<div mat-card-avatar>
|
||||||
<button (click)="onBack()" color="warn" mat-icon-button>
|
<button color="warn" mat-icon-button routerLink="/songs">
|
||||||
<fa-icon [icon]="faArrow"></fa-icon>
|
<fa-icon [icon]="faArrow"></fa-icon>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
<p *ngFor="let line of comments">{{ line }}</p>
|
<p *ngFor="let line of comments">{{ line }}</p>
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
<mat-card-actions>
|
<mat-card-actions>
|
||||||
<button (click)="onClickEdit()" mat-button>
|
<button [routerLink]="'/songs/' + song.ID + '/edit'" mat-button>
|
||||||
<fa-icon [icon]="faEdit"></fa-icon>
|
<fa-icon [icon]="faEdit"></fa-icon>
|
||||||
bearbeiten
|
bearbeiten
|
||||||
</button>
|
</button>
|
||||||
34
WEB/src/app/songs/modules/read/read/read.component.ts
Normal file
34
WEB/src/app/songs/modules/read/read/read.component.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import {ChangeDetectionStrategy, Component, Input} from '@angular/core';
|
||||||
|
import {faEdit, faLongArrowAltLeft} from '@fortawesome/free-solid-svg-icons';
|
||||||
|
import {Song} from 'src/app/songs/models/song.model';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-song-read',
|
||||||
|
templateUrl: './read.component.html',
|
||||||
|
styleUrls: ['./read.component.less'],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
|
})
|
||||||
|
export class SongReadComponent {
|
||||||
|
@Input() public song: Song;
|
||||||
|
public faArrow = faLongArrowAltLeft;
|
||||||
|
public faEdit = faEdit;
|
||||||
|
|
||||||
|
public get text(): string[] {
|
||||||
|
return this.song && this.song.Text ? this.song.Text.split(/\r?\n/) : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public get comments(): string[] {
|
||||||
|
return this.song && this.song.Comments ? this.song.Comments.split(/\r?\n/) : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public renderSongType(songType: string) {
|
||||||
|
switch (songType) {
|
||||||
|
case 'Praise':
|
||||||
|
return {name: 'Lobpreis', color: '#99FFB8'};
|
||||||
|
case 'Worship':
|
||||||
|
return {name: 'Anbetung', color: '#C999FF'};
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import {base} from '../data/urls';
|
import {base} from '../../data/urls';
|
||||||
import {Injectable} from '@angular/core';
|
import {Injectable} from '@angular/core';
|
||||||
import {FileUploader} from 'ng2-file-upload';
|
import {FileUploader} from 'ng2-file-upload';
|
||||||
|
|
||||||
@@ -8,7 +8,7 @@ import {FileUploader} from 'ng2-file-upload';
|
|||||||
export class FileuploadFactory {
|
export class FileuploadFactory {
|
||||||
public static provideForNewFiles(songId: number): FileUploader {
|
public static provideForNewFiles(songId: number): FileUploader {
|
||||||
const uploader = new FileUploader({
|
const uploader = new FileUploader({
|
||||||
url: base + '/api/songs/' + songId + '/files',
|
url: `${base}/api/songs/${songId}/files`,
|
||||||
autoUpload: true,
|
autoUpload: true,
|
||||||
isHTML5: true
|
isHTML5: true
|
||||||
});
|
});
|
||||||
@@ -1,16 +1,20 @@
|
|||||||
/* You can add global styles to this file, and also import other style files */
|
/* You can add global styles to this file, and also import other style files */
|
||||||
@import "~@angular/material/prebuilt-themes/indigo-pink.css";
|
@import "~@angular/material/prebuilt-themes/indigo-pink.css";
|
||||||
|
|
||||||
body {
|
|
||||||
margin: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
html {
|
html {
|
||||||
background-image: url(https://images.unsplash.com/photo-1476136236990-838240be4859?ixlib=rb-1.2.1&auto=format&fit=crop&w=2167&q=80);
|
background-image: url(https://images.unsplash.com/photo-1476136236990-838240be4859?ixlib=rb-1.2.1&auto=format&fit=crop&w=2167&q=80);
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-container {
|
body {
|
||||||
padding: 20px;
|
margin: 0;
|
||||||
|
|
||||||
|
app-root {
|
||||||
|
padding: 10px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-container {
|
||||||
|
padding: 10px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
@@ -32,24 +36,6 @@ html {
|
|||||||
border-top-left-radius: 8px;
|
border-top-left-radius: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
tbody {
|
|
||||||
tr.selected {
|
|
||||||
background-color: #0002;
|
|
||||||
}
|
|
||||||
|
|
||||||
tr:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
background-color: #0001;
|
|
||||||
|
|
||||||
td {
|
|
||||||
color: #ff9900;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
td.mat-cell {
|
|
||||||
padding: 0 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.pinned {
|
&.pinned {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
@@ -57,40 +43,40 @@ html {
|
|||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
|
|
||||||
th.mat-header-cell:first-of-type {
|
th.mat-header-cell:first-of-type {
|
||||||
border-top-left-radius: 0px;
|
border-top-left-radius: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
th.mat-header-cell:last-of-type {
|
th.mat-header-cell:last-of-type {
|
||||||
border-top-right-radius: 0px;
|
border-top-right-radius: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mat-table thead {
|
.mat-table thead {
|
||||||
border-top-right-radius: 0px;
|
border-top-right-radius: 0;
|
||||||
border-top-left-radius: 0px;
|
border-top-left-radius: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.mat-card {
|
.mat-card {
|
||||||
width: 600px;
|
width: 600px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
background: #fffd;
|
background: #fffd;
|
||||||
margin: 10px;
|
margin: 10px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mat-card-title {
|
.mat-card-title {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
width: 520px;
|
width: 520px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mat-card-content {
|
.mat-card-content {
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.song-detail-container {
|
.song-detail-container {
|
||||||
|
|
||||||
|
|
||||||
.mat-form-field-infix {
|
.mat-form-field-infix {
|
||||||
@@ -117,6 +103,6 @@ html {
|
|||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user