From 4cd9222a8a90d84895b1fc3cf973e163fa1ca977 Mon Sep 17 00:00:00 2001 From: smuddyx Date: Sat, 21 Mar 2020 23:37:37 +0100 Subject: [PATCH] text rendering component --- .../services/text-rendering.service.spec.ts | 6 +- .../songs/services/text-rendering.service.ts | 34 ++++++---- .../modules/songs/song/song.component.html | 5 +- src/app/modules/songs/song/song.module.ts | 2 + .../song-text/song-text.component.html | 12 ++++ .../song-text/song-text.component.less | 48 +++++++++++++ .../song-text/song-text.component.spec.ts | 25 +++++++ .../song-text/song-text.component.ts | 67 +++++++++++++++++++ .../components/song-text/song-text.module.ts | 16 +++++ 9 files changed, 201 insertions(+), 14 deletions(-) create mode 100644 src/app/widget-modules/components/song-text/song-text.component.html create mode 100644 src/app/widget-modules/components/song-text/song-text.component.less create mode 100644 src/app/widget-modules/components/song-text/song-text.component.spec.ts create mode 100644 src/app/widget-modules/components/song-text/song-text.component.ts create mode 100644 src/app/widget-modules/components/song-text/song-text.module.ts diff --git a/src/app/modules/songs/services/text-rendering.service.spec.ts b/src/app/modules/songs/services/text-rendering.service.spec.ts index de4216a..338f326 100644 --- a/src/app/modules/songs/services/text-rendering.service.spec.ts +++ b/src/app/modules/songs/services/text-rendering.service.spec.ts @@ -35,9 +35,13 @@ Cool bridge without any chords const service: TextRenderingService = TestBed.get(TextRenderingService); const sections = service.parse(testText); expect(sections[0].type).toBe(SectionType.Verse); + expect(sections[0].number).toBe(0); expect(sections[1].type).toBe(SectionType.Verse); + expect(sections[1].number).toBe(1); expect(sections[2].type).toBe(SectionType.Chorus); + expect(sections[2].number).toBe(0); expect(sections[3].type).toBe(SectionType.Bridge); + expect(sections[3].number).toBe(0); }); it('should parse text lines', () => { @@ -60,6 +64,7 @@ Cool bridge without any chords it('should parse chord lines', () => { const service: TextRenderingService = TestBed.get(TextRenderingService); const sections = service.parse(testText); + console.log(sections); expect(sections[0].lines[0].type).toBe(LineType.chord); expect(sections[0].lines[0].text).toBe('C D E F G A H'); expect(sections[0].lines[2].type).toBe(LineType.chord); @@ -72,7 +77,6 @@ Cool bridge without any chords expect(sections[2].lines[0].text).toBe('c c# db c7 cmaj7 c/e'); // c c# db c7 cmaj7 c/e - console.log(sections[2].lines[0].chords); expect(sections[2].lines[0].chords).toEqual([ {chord: 'c', length: 2, position: 0}, {chord: 'c#', length: 3, position: 2}, diff --git a/src/app/modules/songs/services/text-rendering.service.ts b/src/app/modules/songs/services/text-rendering.service.ts index 18e170e..b356a75 100644 --- a/src/app/modules/songs/services/text-rendering.service.ts +++ b/src/app/modules/songs/services/text-rendering.service.ts @@ -35,8 +35,6 @@ export interface Section { providedIn: 'root' }) export class TextRenderingService { - - private regexSection = /(Strophe|Refrain|Bridge)/; constructor() { @@ -44,29 +42,40 @@ export class TextRenderingService { public parse(text: string): Section[] { const arrayOfLines = text.split(/\r?\n/).filter(_ => _); - return arrayOfLines.reduce((array, line) => { + const indices = { + [SectionType.Bridge]: 0, + [SectionType.Chorus]: 0, + [SectionType.Verse]: 0, + }; + const sections = arrayOfLines.reduce((array, line) => { + const type = this.getSectionTypeOfLine(line); if (line.match(this.regexSection)) return [...array, { - type: this.getSectionTypeOfLine(line), - number: -1, + type: type, + number: indices[type]++, lines: [] }]; array[array.length - 1].lines.push(this.getLineOfLineText(line)); return array; }, [] as Section[]); + console.log(indices); + + return sections; } private getLineOfLineText(text: string): Line { - const regex = /\b(C#|C|Db|D#|D|Eb|E|F#|F|Gb|G#|G|Ab|A#|A|B|H|c#|c|db|d#|d|eb|e|f#|f|gb|g#|g|ab|a#|a|b|h)(\/(C#|C|Db|D#|D|Eb|E|F#|F|Gb|G#|G|Ab|A#|A|B|H|c#|c|db|d#|d|eb|e|f#|f|gb|g#|g|ab|a#|a|b|h))?(\d+|maj7)?.?\b/mg; - - const match = text.match(regex); - const hasMatches = !!match; + if (!text) return null; + const cords = this.readChords(text); + const hasMatches = cords.length > 0; const type = hasMatches ? LineType.chord : LineType.text; - return {type, text, chords: hasMatches ? this.readChords(text) : undefined} + return {type, text, chords: hasMatches ? cords : undefined} } private getSectionTypeOfLine(line: string): SectionType { - const typeString = line.match(this.regexSection)[1]; + if (!line) return null; + const match = line.match(this.regexSection); + if (!match || match.length < 2) return null; + const typeString = match[1]; switch (typeString) { case "Strophe": return SectionType.Verse; @@ -81,7 +90,8 @@ export class TextRenderingService { let match; const chords: Chord[] = []; - const regex = /\b(C#|C|Db|D#|D|Eb|E|F#|F|Gb|G#|G|Ab|A#|A|B|H|c#|c|db|d#|d|eb|e|f#|f|gb|g#|g|ab|a#|a|b|h)(\/(C#|C|Db|D#|D|Eb|E|F#|F|Gb|G#|G|Ab|A#|A|B|H|c#|c|db|d#|d|eb|e|f#|f|gb|g#|g|ab|a#|a|b|h))?(\d+|maj7)?.?\b/mg; + // https://regex101.com/r/68jMB8/3 + const regex = /\b(C#|C|Db|D#|D|Eb|E|F#|F|Gb|G#|G|Ab|A#|A|B|H|c#|c|db|d#|d|eb|e|f#|f|gb|g#|g|ab|a#|a|b|h)(\/(C#|C|Db|D#|D|Eb|E|F#|F|Gb|G#|G|Ab|A#|A|B|H|c#|c|db|d#|d|eb|e|f#|f|gb|g#|g|ab|a#|a|b|h))?(\d+|maj7)?\s?\b/mg; while ((match = regex.exec(chordLine)) !== null) { const chord: Chord = { diff --git a/src/app/modules/songs/song/song.component.html b/src/app/modules/songs/song/song.component.html index 0299272..8a29577 100644 --- a/src/app/modules/songs/song/song.component.html +++ b/src/app/modules/songs/song/song.component.html @@ -15,7 +15,10 @@ -
{{song.text}}
+ + + +
{{song.comment}}

Anhänge

diff --git a/src/app/modules/songs/song/song.module.ts b/src/app/modules/songs/song/song.module.ts index f5b880f..918484c 100644 --- a/src/app/modules/songs/song/song.module.ts +++ b/src/app/modules/songs/song/song.module.ts @@ -7,6 +7,7 @@ import {MatButtonModule} from '@angular/material/button'; import {ButtonRowModule} from '../../../widget-modules/components/button-row/button-row.module'; import {RouterModule} from '@angular/router'; import {LegalOwnerTranslatorModule} from '../../../widget-modules/pipes/legal-owner-translator/legal-owner-translator.module'; +import {SongTextModule} from '../../../widget-modules/components/song-text/song-text.module'; @NgModule({ @@ -21,6 +22,7 @@ import {LegalOwnerTranslatorModule} from '../../../widget-modules/pipes/legal-ow MatButtonModule, ButtonRowModule, LegalOwnerTranslatorModule, + SongTextModule, ] }) export class SongModule { diff --git a/src/app/widget-modules/components/song-text/song-text.component.html b/src/app/widget-modules/components/song-text/song-text.component.html new file mode 100644 index 0000000..7616c68 --- /dev/null +++ b/src/app/widget-modules/components/song-text/song-text.component.html @@ -0,0 +1,12 @@ +
+ + + +
+
+ {{line.text}} +
+
+
diff --git a/src/app/widget-modules/components/song-text/song-text.component.less b/src/app/widget-modules/components/song-text/song-text.component.less new file mode 100644 index 0000000..dcd970e --- /dev/null +++ b/src/app/widget-modules/components/song-text/song-text.component.less @@ -0,0 +1,48 @@ +.song-text { + white-space: pre-wrap; + position: relative; + + &.chords { + font-family: 'Ubuntu Mono', monospace; + } + + &:hover .menu { + opacity: 1; + } +} + +.menu { + position: absolute; + right: 0; + background: #eee; + border-radius: 8px; + padding: 5px; + width: 20px; + height: 20px; + display: flex; + justify-content: center; + align-items: center; + transition: 300ms all ease-in-out; + cursor: pointer; + opacity: 0; + + &:hover { + background: #ddd; + } + + +} + +.section { + margin-bottom: 20px; + border-left: 3px solid #ddd; +} + +.chorus { + margin-left: 20px; + border-left: none; +} + +.chord { + font-weight: bold; +} diff --git a/src/app/widget-modules/components/song-text/song-text.component.spec.ts b/src/app/widget-modules/components/song-text/song-text.component.spec.ts new file mode 100644 index 0000000..37f30e6 --- /dev/null +++ b/src/app/widget-modules/components/song-text/song-text.component.spec.ts @@ -0,0 +1,25 @@ +import {async, ComponentFixture, TestBed} from '@angular/core/testing'; + +import {SongTextComponent} from './song-text.component'; + +describe('SongTextComponent', () => { + let component: SongTextComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [SongTextComponent] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(SongTextComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/widget-modules/components/song-text/song-text.component.ts b/src/app/widget-modules/components/song-text/song-text.component.ts new file mode 100644 index 0000000..bfbd85e --- /dev/null +++ b/src/app/widget-modules/components/song-text/song-text.component.ts @@ -0,0 +1,67 @@ +import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core'; +import { + Line, + LineType, + Section, + SectionType, + TextRenderingService +} from '../../../modules/songs/services/text-rendering.service'; +import {faGripLines} from '@fortawesome/free-solid-svg-icons/faGripLines'; + +export type ChordMode = 'show' | 'hide' | 'onlyFirst' + +@Component({ + selector: 'app-song-text', + templateUrl: './song-text.component.html', + styleUrls: ['./song-text.component.less'] +}) +export class SongTextComponent implements OnInit { + public sections: Section[]; + public chordMode: ChordMode = 'hide'; + @Output() public chordModeChanged = new EventEmitter(); + public faLines = faGripLines; + + constructor(private textRenderingService: TextRenderingService) { + } + + @Input() + public set text(value: string) { + this.sections = this.textRenderingService.parse(value).sort((a, b) => a.type - b.type); + console.log(this.sections) + } + + ngOnInit(): void { + } + + public getLines(section: Section): Line[] { + return section.lines.filter(_ => { + if (_.type !== LineType.chord) return true; + + switch (this.chordMode) { + case 'show': + return true; + case 'hide': + return false; + case 'onlyFirst': + return section.number === 0 || section.type !== SectionType.Verse; + } + }); + } + + public onChordClick(): void { + const next = this.getNextChordMode(); + this.chordMode = next; + this.chordModeChanged.next(next); + } + + private getNextChordMode(): ChordMode { + switch (this.chordMode) { + case 'show': + return 'hide'; + case 'hide': + return 'onlyFirst'; + case 'onlyFirst': + return 'show'; + } + } +} diff --git a/src/app/widget-modules/components/song-text/song-text.module.ts b/src/app/widget-modules/components/song-text/song-text.module.ts new file mode 100644 index 0000000..f57ba16 --- /dev/null +++ b/src/app/widget-modules/components/song-text/song-text.module.ts @@ -0,0 +1,16 @@ +import {NgModule} from '@angular/core'; +import {CommonModule} from '@angular/common'; +import {SongTextComponent} from './song-text.component'; +import {FontAwesomeModule} from '@fortawesome/angular-fontawesome'; + + +@NgModule({ + declarations: [SongTextComponent], + exports: [SongTextComponent], + imports: [ + CommonModule, + FontAwesomeModule + ] +}) +export class SongTextModule { +}