Skip to content
Snippets Groups Projects
Commit 956b1b32 authored by jojohoch's avatar jojohoch
Browse files

Implement colors for anchors and allow anchor children

- Set the duration of highlighting anchors to 15 seconds
- Disable simultaneous highlighting of anchors
parent 23775ba7
No related branches found
No related tags found
No related merge requests found
......@@ -2,8 +2,11 @@ Editor
======
## 1.35.3
### Verbesserungen
- Ändert Symbol und Tooltip-Namen für Knopf als Lückentextelement
- Ändert Symbol und Tooltip-Namen für Knopf als Lückentextelement
- Erweiterung der Möglichkeiten für hervorzuhebende Textbereiche im TextEditor
- Die hervorzuhebenden TextBereichen können mit einer Farbe belegt werden
- Innerhalb eines Textbereichs können weitere Textbereiche mit anderen Farben definiert werden
- Eine noch tiefere Verschachtelung ist nicht möglich
## 1.35.2
### Verbesserungen
......
Player
======
## 1.28.3
### Verbesserungen
- Änderungen an hervorzuhebenden Textbereiche
- Die Dauer der Anzeige beträgt 15 Sekunden
- Es ist immer nur eine Hervorhebung aktiv
### Fehlerbehebungen
- Korrigiert das Scroll-Verhalten von versteckten Abschnitten mit unterschiedlichen Verzögerungen
......
import { Mark } from '@tiptap/core';
import { Mark, mergeAttributes } from '@tiptap/core';
declare module '@tiptap/core' {
interface Commands<ReturnType> {
anchorId: {
toggleAnchorId: (attributes?: { anchorId: string }) => ReturnType,
toggleAnchorId: (attributes?: {
anchorId: string,
parentAnchorId: string,
anchorColor: string,
parentAnchorColor: string
}) => ReturnType,
setAnchorId: (attributes?: {
anchorId: string,
parentAnchorId: string,
anchorColor: string,
parentAnchorColor: string
}) => ReturnType
}
}
}
......@@ -13,7 +24,7 @@ export const AnchorId = Mark.create({
addAttributes() {
return {
anchorId: {
default: null,
default: '',
parseHTML: element => element.getAttribute('data-anchor-id'),
renderHTML: attributes => {
if (!attributes.anchorId) {
......@@ -23,6 +34,43 @@ export const AnchorId = Mark.create({
'data-anchor-id': attributes.anchorId
};
}
},
parentAnchorId: {
default: '',
parseHTML: element => element.getAttribute('data-parent-anchor-id'),
renderHTML: attributes => {
if (!attributes.parentAnchorId) {
return {};
}
return {
'data-parent-anchor-id': attributes.parentAnchorId
};
}
},
anchorColor: {
default: '',
parseHTML: element => element.getAttribute('data-anchor-color') || element.style.backgroundColor,
renderHTML: attributes => {
if (!attributes.anchorColor) {
return {};
}
return {
'data-anchor-color': attributes.anchorColor,
style: `background-color: ${attributes.anchorColor};`
};
}
},
parentAnchorColor: {
default: '',
parseHTML: element => element.getAttribute('data-parent-anchor-color'),
renderHTML: attributes => {
if (!attributes.parentAnchorColor) {
return {};
}
return {
'data-parent-anchor-color': attributes.parentAnchorColor
};
}
}
};
},
......@@ -34,11 +82,12 @@ export const AnchorId = Mark.create({
];
},
renderHTML({ HTMLAttributes }) {
return ['aspect-anchor', HTMLAttributes, 0];
return ['aspect-anchor', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0];
},
addCommands() {
return {
toggleAnchorId: attributes => ({ commands }) => commands.toggleMark(this.name, attributes)
toggleAnchorId: attributes => ({ commands }) => commands.toggleMark(this.name, attributes),
setAnchorId: attributes => ({ commands }) => commands.setMark(this.name, attributes)
};
}
});
......@@ -281,11 +281,19 @@
(click)="toggleBlockquote()">
<mat-icon>format_quote</mat-icon>
</button>
<button mat-icon-button matTooltip="Bereich markieren"
[class.active]="editor.isActive('anchorId')"
(click)="applyAnchorId()">
<mat-icon>read_more</mat-icon>
</button>
<div *ngIf="!clozeMode" class="combo-button" fxLayout="row"
[style.background-color]="selectedAnchorColor">
<button mat-icon-button matTooltip="Bereich markieren"
(click)="applyAnchorId()">
<mat-icon>read_more</mat-icon>
</button>
<mat-select matTooltip="Bereichsfarbe wählen"
(click)="anchorIdColorInput.click()"
(focus)="setSelectedAnchorIdText()">
</mat-select>
<input matInput type="color" #anchorIdColorInput hidden
(input)="selectedAnchorColor = $any($event.target).value; applyAnchorId()">
</div>
</fieldset>
</div>
<div *ngIf="clozeMode" fxLayout="row" fxLayoutAlign="space-around center" >
......
......@@ -47,6 +47,8 @@ export class RichTextEditorComponent implements OnInit, AfterViewInit {
selectedFontColor = 'lightgrey';
selectedHighlightColor = 'lightgrey';
selectedAnchorColor = '#adff2f';
selectedAnchorIdText = '';
selectedFontSize = '20px';
selectedIndentSize = 20;
bulletListStyle: string = 'disc';
......@@ -143,15 +145,55 @@ export class RichTextEditorComponent implements OnInit, AfterViewInit {
}
applyAnchorId(): void {
const text = window?.getSelection()?.toString();
if (text) {
const id = text.replace(/[^0-9a-zA-Z]/g, '_').substring(0, 20);
this.editor.chain().focus().toggleAnchorId({ anchorId: id }).run();
const id = this.getAnchorIdFromSelection();
if (id) {
const activeAnchorId = this.editor.getAttributes('anchorId').anchorId;
const activeAnchorColor = this.editor.getAttributes('anchorId').anchorColor;
const activeParentAnchorId = this.editor.getAttributes('anchorId').parentAnchorId;
const activeParentAnchorColor = this.editor.getAttributes('anchorId').parentAnchorColor;
if (activeParentAnchorId) { // reset nested child
if (this.selectedAnchorColor === activeParentAnchorColor || this.selectedAnchorColor === activeAnchorColor) {
this.editor.chain().focus().setAnchorId({
anchorId: activeParentAnchorId,
parentAnchorId: '',
anchorColor: activeParentAnchorColor,
parentAnchorColor: ''
}).run();
} else { // set new color for nested Child
this.editor.chain().focus().setAnchorId({
anchorId: activeAnchorId,
parentAnchorId: activeParentAnchorId,
anchorColor: this.selectedAnchorColor,
parentAnchorColor: activeParentAnchorColor
}).run();
}
} else { // standard toggle
this.editor.chain().focus().toggleAnchorId({
anchorId: id,
parentAnchorId: (activeAnchorId !== id) ? activeAnchorId : '',
anchorColor: this.selectedAnchorColor,
parentAnchorColor: (activeAnchorId !== id) ? activeAnchorColor : ''
}).run();
}
this.resetSelectedAnchorIdText();
} else {
console.warn('No text selected for anchor!');
}
}
setSelectedAnchorIdText() {
this.selectedAnchorIdText = window.getSelection()?.toString() as string;
}
private getAnchorIdFromSelection(): string {
const selection = window?.getSelection()?.toString() || this.selectedAnchorIdText;
return selection.replace(/[^0-9a-zA-Z]/g, '_').substring(0, 20);
}
private resetSelectedAnchorIdText(): void {
this.selectedAnchorIdText = '';
}
alignText(direction: string): void {
this.editor.chain().focus().setTextAlign(direction).run();
}
......
......@@ -17,6 +17,3 @@ input[type=color] {
cursor: pointer;
}
aspect-anchor {
background-color: #adff2f;
}
......@@ -11,6 +11,7 @@ export class AnchorService {
if (this.activeAnchors[anchorId]) {
this.removeAnchor(anchorId);
} else {
this.reset();
this.addAnchor(anchorId);
}
}
......@@ -18,7 +19,7 @@ export class AnchorService {
private addAnchor(anchorId: string): void {
this.activeAnchors[anchorId] = of(true)
.pipe(
delay(30000))
delay(15000))
.subscribe(() => {
this.removeAnchor(anchorId);
});
......@@ -31,10 +32,32 @@ export class AnchorService {
AnchorService.toggleAnchorRendering(anchorId, false);
}
private static toggleAnchorRendering(anchorId: string, shouldScroll: boolean): void {
const anchors = document.querySelectorAll(`aspect-anchor[data-anchor-id="${anchorId}"]`);
anchors.forEach(anchor => anchor.classList.toggle('active-anchor'));
if (shouldScroll) anchors.item(0).scrollIntoView({ behavior: 'smooth', block: 'start' });
private static toggleAnchorRendering(anchorId: string, showAnchor: boolean): void {
const anchors = Array.from(
document.querySelectorAll(`aspect-anchor[data-anchor-id="${anchorId}"]`)
) as HTMLElement[];
const nestedAnchors = Array.from(
document.querySelectorAll(`aspect-anchor[data-parent-anchor-id="${anchorId}"]`)
) as HTMLElement[];
anchors.forEach(anchor => {
if (!showAnchor && anchor.dataset.parentAnchorColor) {
anchor.style.backgroundColor = anchor.dataset.parentAnchorColor as string;
} else {
anchor.style.backgroundColor = anchor.dataset.anchorColor as string;
}
anchor.classList.toggle('active-anchor');
});
nestedAnchors.forEach(anchor => {
if (showAnchor) {
anchor.style.backgroundColor = (anchor.dataset.parentAnchorColor) as string;
} else {
anchor.style.backgroundColor = anchor.dataset.anchorColor as string;
}
anchor.classList.toggle('active-nested-anchor');
});
if (anchors.length && showAnchor) {
anchors[0].scrollIntoView({ behavior: 'smooth', block: 'start' });
}
}
reset(): void {
......
......@@ -16,10 +16,17 @@ body {
}
.active-anchor {
background-color: #adff2f;
scroll-margin: 100px 0;
}
.active-anchor > mark, .active-anchor > aspect-marked {
background-color: unset !important;
}
.active-nested-anchor > mark, .active-anchor > aspect-marked {
background-color: unset !important;
}
aspect-anchor:not(.active-anchor):not(.active-nested-anchor) {
background-color: unset !important;
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment