Implementaiton of h1, h2, h3 and link button on publication editor.
This commit is contained in:
@@ -29,19 +29,19 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<button type="button" disabled matTooltip="Click to insert a title 1 section">
|
<button type="button" matTooltip="Click to insert a title 1 section" (click)="insertTitle(1)">
|
||||||
H1
|
H1
|
||||||
</button>
|
</button>
|
||||||
<button type="button" disabled matTooltip="Click to insert a title 2 section">
|
<button type="button" matTooltip="Click to insert a title 2 section" (click)="insertTitle(2)">
|
||||||
H2
|
H2
|
||||||
</button>
|
</button>
|
||||||
<button type="button" disabled matTooltip="Click to insert a title 1 section">
|
<button type="button" matTooltip="Click to insert a title 1 section" (click)="insertTitle(3)">
|
||||||
H3
|
H3
|
||||||
</button>
|
</button>
|
||||||
<button type="button" disabled matTooltip="Click to insert a picture">
|
<button type="button" disabled matTooltip="Click to insert a picture">
|
||||||
<mat-icon>image</mat-icon>
|
<mat-icon>image</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
<button type="button" disabled matTooltip="Click to insert a link">
|
<button type="button" matTooltip="Click to insert a link" (click)="insertLink()">
|
||||||
<mat-icon>link</mat-icon>
|
<mat-icon>link</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
<button type="button" disabled matTooltip="Click to insert a code block">
|
<button type="button" disabled matTooltip="Click to insert a code block">
|
||||||
@@ -53,7 +53,14 @@
|
|||||||
</div>
|
</div>
|
||||||
<mat-form-field class="example-form-field">
|
<mat-form-field class="example-form-field">
|
||||||
<mat-label>Content</mat-label>
|
<mat-label>Content</mat-label>
|
||||||
<textarea matInput formControlName="text" class="text-input"></textarea>
|
<textarea
|
||||||
|
#textArea
|
||||||
|
matInput
|
||||||
|
formControlName="text"
|
||||||
|
class="text-input"
|
||||||
|
(keyup)="updateCursorPosition($event)"
|
||||||
|
(click)="updateCursorPosition($event)">
|
||||||
|
</textarea>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
</mat-tab>
|
</mat-tab>
|
||||||
|
|||||||
@@ -59,7 +59,6 @@ export class PublicationEditionComponent implements OnInit, OnDestroy {
|
|||||||
['title', 'description', 'text'].forEach(fieldName => {
|
['title', 'description', 'text'].forEach(fieldName => {
|
||||||
const fieldSubscription = this.publicationEditionForm.controls[fieldName].valueChanges
|
const fieldSubscription = this.publicationEditionForm.controls[fieldName].valueChanges
|
||||||
.pipe(
|
.pipe(
|
||||||
debounceTime(300),
|
|
||||||
map(value => value?.length ? value as string : '')
|
map(value => value?.length ? value as string : '')
|
||||||
)
|
)
|
||||||
.subscribe(fieldValue => {
|
.subscribe(fieldValue => {
|
||||||
@@ -76,18 +75,17 @@ export class PublicationEditionComponent implements OnInit, OnDestroy {
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
this.publicationEditionService
|
|
||||||
});
|
});
|
||||||
this.subscriptions.push(fieldSubscription);
|
this.subscriptions.push(fieldSubscription);
|
||||||
});
|
});
|
||||||
|
|
||||||
const publicationSubscription = this.publicationEditionService.publication$.subscribe(publication => {
|
const publicationSubscription = this.publicationEditionService.state$.subscribe(state => {
|
||||||
this.publication = publication;
|
this.publication = state.publication;
|
||||||
this.publicationEditionForm.controls['title'].setValue(publication.title, { emitEvent: false });
|
this.publicationEditionForm.controls['title'].setValue(this.publication.title, { emitEvent: false });
|
||||||
this.publicationEditionForm.controls['description'].setValue(publication.description, { emitEvent: false });
|
this.publicationEditionForm.controls['description'].setValue(this.publication.description, { emitEvent: false });
|
||||||
this.publicationEditionForm.controls['text'].setValue(publication.text, { emitEvent: false });
|
this.publicationEditionForm.controls['text'].setValue(this.publication.text, { emitEvent: false });
|
||||||
this.publicationEditionForm.controls['illustrationId'].setValue(publication.illustrationId, { emitEvent: false });
|
this.publicationEditionForm.controls['illustrationId'].setValue(this.publication.illustrationId, { emitEvent: false });
|
||||||
this.publicationEditionForm.controls['categoryId'].setValue(publication.categoryId, { emitEvent: false });
|
this.publicationEditionForm.controls['categoryId'].setValue(this.publication.categoryId, { emitEvent: false });
|
||||||
});
|
});
|
||||||
this.subscriptions.push(publicationSubscription);
|
this.subscriptions.push(publicationSubscription);
|
||||||
|
|
||||||
@@ -102,6 +100,14 @@ export class PublicationEditionComponent implements OnInit, OnDestroy {
|
|||||||
this.location.back();
|
this.location.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
insertTitle(titleNumber: number): void {
|
||||||
|
this.publicationEditionService.insertTitle(titleNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
insertLink(): void {
|
||||||
|
this.publicationEditionService.insertLink();
|
||||||
|
}
|
||||||
|
|
||||||
save(): void {
|
save(): void {
|
||||||
this.publicationEditionService.save();
|
this.publicationEditionService.save();
|
||||||
}
|
}
|
||||||
@@ -109,4 +115,18 @@ export class PublicationEditionComponent implements OnInit, OnDestroy {
|
|||||||
displayPictureSectionDialog(): void {
|
displayPictureSectionDialog(): void {
|
||||||
this.publicationEditionService.displayPictureSectionDialog();
|
this.publicationEditionService.displayPictureSectionDialog();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateCursorPosition(event: KeyboardEvent | MouseEvent): void {
|
||||||
|
if (event.target) {
|
||||||
|
const textarea = event.target as HTMLTextAreaElement;
|
||||||
|
|
||||||
|
const positionStart = textarea.selectionStart;
|
||||||
|
const positionEnd = textarea.selectionEnd;
|
||||||
|
|
||||||
|
const selectedCharacterCount = positionEnd - positionStart;
|
||||||
|
console.log(`cursor position updated: [${positionStart}, ${positionEnd}] (${selectedCharacterCount})`);
|
||||||
|
this.publicationEditionService.editCursorPosition(positionStart, positionEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,22 @@ import { copy } from '../../core/utils/ObjectUtils';
|
|||||||
import { MatDialog } from "@angular/material/dialog";
|
import { MatDialog } from "@angular/material/dialog";
|
||||||
import { PictureSelectionDialog } from "./picture-selection-dialog/picture-selection-dialog.component";
|
import { PictureSelectionDialog } from "./picture-selection-dialog/picture-selection-dialog.component";
|
||||||
|
|
||||||
|
export class CursorPosition {
|
||||||
|
start: number;
|
||||||
|
end: number;
|
||||||
|
selectedCharacters: number;
|
||||||
|
|
||||||
|
constructor(start: number, end: number) {
|
||||||
|
this.start = start;
|
||||||
|
this.end = end;
|
||||||
|
this.selectedCharacters = end - start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PublicationEditionState {
|
||||||
|
publication: Publication;
|
||||||
|
cursorPosition: CursorPosition;
|
||||||
|
}
|
||||||
|
|
||||||
const DEFAULT_PUBLICATION: Publication = {
|
const DEFAULT_PUBLICATION: Publication = {
|
||||||
id: '',
|
id: '',
|
||||||
@@ -27,6 +43,13 @@ const DEFAULT_PUBLICATION: Publication = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const DEFAULT_CURSOR_POSITION = new CursorPosition(0, 0);
|
||||||
|
|
||||||
|
const DEFAULT_STATE: PublicationEditionState = {
|
||||||
|
publication: DEFAULT_PUBLICATION,
|
||||||
|
cursorPosition: DEFAULT_CURSOR_POSITION
|
||||||
|
};
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PublicationEditionService implements OnDestroy {
|
export class PublicationEditionService implements OnDestroy {
|
||||||
private readonly activatedRoute = inject(ActivatedRoute);
|
private readonly activatedRoute = inject(ActivatedRoute);
|
||||||
@@ -37,7 +60,7 @@ export class PublicationEditionService implements OnDestroy {
|
|||||||
private readonly dialog = inject(MatDialog);
|
private readonly dialog = inject(MatDialog);
|
||||||
|
|
||||||
private isLoadingSubject = new BehaviorSubject<boolean>(false);
|
private isLoadingSubject = new BehaviorSubject<boolean>(false);
|
||||||
private publicationSubject = new BehaviorSubject<Publication>(copy(DEFAULT_PUBLICATION));
|
private stateSubject = new BehaviorSubject<PublicationEditionState>(copy(DEFAULT_STATE));
|
||||||
private subscriptions: Subscription[] = [];
|
private subscriptions: Subscription[] = [];
|
||||||
private isSavingSubject = new BehaviorSubject<boolean>(false);
|
private isSavingSubject = new BehaviorSubject<boolean>(false);
|
||||||
|
|
||||||
@@ -45,12 +68,12 @@ export class PublicationEditionService implements OnDestroy {
|
|||||||
this.subscriptions.forEach(subscription => subscription.unsubscribe());
|
this.subscriptions.forEach(subscription => subscription.unsubscribe());
|
||||||
}
|
}
|
||||||
|
|
||||||
private get _publication(): Publication {
|
private get _state(): PublicationEditionState {
|
||||||
return this.publicationSubject.value;
|
return this.stateSubject.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _save(publication: Publication): void {
|
private _save(state: PublicationEditionState): void {
|
||||||
this.publicationSubject.next(publication);
|
this.stateSubject.next(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
get isLoading$(): Observable<boolean> {
|
get isLoading$(): Observable<boolean> {
|
||||||
@@ -61,8 +84,8 @@ export class PublicationEditionService implements OnDestroy {
|
|||||||
return this.isSavingSubject.asObservable();
|
return this.isSavingSubject.asObservable();
|
||||||
}
|
}
|
||||||
|
|
||||||
get publication$(): Observable<Publication> {
|
get state$(): Observable<PublicationEditionState> {
|
||||||
return this.publicationSubject.asObservable();
|
return this.stateSubject.asObservable();
|
||||||
}
|
}
|
||||||
|
|
||||||
loadPublication(): void {
|
loadPublication(): void {
|
||||||
@@ -76,7 +99,9 @@ export class PublicationEditionService implements OnDestroy {
|
|||||||
} else {
|
} else {
|
||||||
this.publicationRestService.getById(publicationId)
|
this.publicationRestService.getById(publicationId)
|
||||||
.then(publication => {
|
.then(publication => {
|
||||||
this.publicationSubject.next(publication);
|
const state = this._state;
|
||||||
|
state.publication = publication;
|
||||||
|
this.stateSubject.next(state);
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
const errorMessage = 'A technical error occurred while loading publication data.';
|
const errorMessage = 'A technical error occurred while loading publication data.';
|
||||||
@@ -89,27 +114,27 @@ export class PublicationEditionService implements OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
editTitle(title: string): void {
|
editTitle(title: string): void {
|
||||||
const publication = this._publication;
|
const state = this._state;
|
||||||
publication.title = title;
|
state.publication.title = title;
|
||||||
this._save(publication);
|
this._save(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
editDescription(description: string): void {
|
editDescription(description: string): void {
|
||||||
const publication = this._publication;
|
const state = this._state;
|
||||||
publication.description = description;
|
state.publication.description = description;
|
||||||
this._save(publication);
|
this._save(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
editText(text: string): void {
|
editText(text: string): void {
|
||||||
const publication = this._publication;
|
const state = this._state;
|
||||||
publication.text = text;
|
state.publication.text = text;
|
||||||
this._save(publication);
|
this._save(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
editIllustrationId(pictureId: string): void {
|
editIllustrationId(pictureId: string): void {
|
||||||
const publication = this._publication;
|
const state = this._state;
|
||||||
publication.illustrationId = pictureId
|
state.publication.illustrationId = pictureId
|
||||||
this._save(publication);
|
this._save(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
displayPictureSectionDialog(): void {
|
displayPictureSectionDialog(): void {
|
||||||
@@ -124,11 +149,55 @@ export class PublicationEditionService implements OnDestroy {
|
|||||||
this.subscriptions.push(afterDialogCloseSubscription);
|
this.subscriptions.push(afterDialogCloseSubscription);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
editCursorPosition(positionStart: number, positionEnd: number): void {
|
||||||
|
const state = this._state;
|
||||||
|
|
||||||
|
state.cursorPosition.start = positionStart;
|
||||||
|
state.cursorPosition.end = positionEnd;
|
||||||
|
|
||||||
|
this._save(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
insertTitle(titleNumber: number): void {
|
||||||
|
if (titleNumber >= 1 && titleNumber <= 3) {
|
||||||
|
const state = this._state;
|
||||||
|
|
||||||
|
const publication = state.publication;
|
||||||
|
|
||||||
|
const publicationTextLeftPart = publication.text.substring(0, state.cursorPosition.start);
|
||||||
|
const publicationTextMiddlePart = publication.text.substring(state.cursorPosition.start, state.cursorPosition.end);
|
||||||
|
const publicationTextRightPart = publication.text.substring(state.cursorPosition.end);
|
||||||
|
const textWithTags = `${publicationTextLeftPart}[h${titleNumber}]${publicationTextMiddlePart}[/h${titleNumber}]${publicationTextRightPart}`;
|
||||||
|
|
||||||
|
publication.text = textWithTags;
|
||||||
|
|
||||||
|
this._save(state);
|
||||||
|
} else {
|
||||||
|
console.error(`Bad value for parameter of function 'insertTitle': '${titleNumber}'.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
insertLink(): void {
|
||||||
|
const state = this._state;
|
||||||
|
|
||||||
|
const publication = state.publication;
|
||||||
|
|
||||||
|
const publicationTextLeftPart = publication.text.substring(0, state.cursorPosition.start);
|
||||||
|
const publicationTextMiddlePart = publication.text.substring(state.cursorPosition.start, state.cursorPosition.end);
|
||||||
|
const publicationTextRightPart = publication.text.substring(state.cursorPosition.end);
|
||||||
|
const textWithTags = `${publicationTextLeftPart}[link href="" txt="${publicationTextMiddlePart}" /]${publicationTextRightPart}`;
|
||||||
|
|
||||||
|
publication.text = textWithTags;
|
||||||
|
|
||||||
|
this._save(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
save(): void {
|
save(): void {
|
||||||
const publication = this._publication;
|
const state = this._state;
|
||||||
|
|
||||||
this.isSavingSubject.next(true);
|
this.isSavingSubject.next(true);
|
||||||
this.publicationRestService.update(publication)
|
this.publicationRestService.update(state.publication)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.snackBar.open('Publication updated succesfully!', 'Close', { duration: 5000 });
|
this.snackBar.open('Publication updated succesfully!', 'Close', { duration: 5000 });
|
||||||
this.router.navigate(['/home']);
|
this.router.navigate(['/home']);
|
||||||
|
|||||||
Reference in New Issue
Block a user