Skip to content

Commit c0cf17d

Browse files
committed
add fixes of length & loader postion with route events
1 parent 259e76e commit c0cf17d

15 files changed

Lines changed: 135 additions & 22 deletions
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/** @format */
2+
3+
import { Pipe, PipeTransform } from "@angular/core";
4+
import { DomSanitizer, SafeHtml } from "@angular/platform-browser";
5+
6+
@Pipe({ name: "truncateHtml", standalone: true })
7+
export class TruncateHtmlPipe implements PipeTransform {
8+
constructor(private sanitizer: DomSanitizer) {}
9+
10+
transform(value: string, limit: number): SafeHtml {
11+
if (!value) return "";
12+
13+
// Сначала убираем все HTML теги чтобы считать только текст
14+
const plainText = value.replace(/<[^>]*>/g, "");
15+
16+
if (plainText.length <= limit) {
17+
return this.sanitizer.bypassSecurityTrustHtml(value);
18+
}
19+
20+
// Обрезаем по тексту, сохраняя теги
21+
let charCount = 0;
22+
let result = "";
23+
let inTag = false;
24+
let currentTag = "";
25+
const openTags: string[] = [];
26+
const selfClosingTags = ["br", "hr", "img", "input", "meta", "link"];
27+
28+
for (let i = 0; i < value.length; i++) {
29+
const char = value[i];
30+
31+
if (char === "<") {
32+
inTag = true;
33+
currentTag = "";
34+
}
35+
36+
if (inTag) {
37+
result += char;
38+
currentTag += char;
39+
40+
if (char === ">") {
41+
// Закончился тег, анализируем его
42+
const tagContent = currentTag.slice(1, -1).trim(); // убираем < и >
43+
44+
if (tagContent.startsWith("/")) {
45+
// Закрывающий тег
46+
openTags.pop();
47+
} else if (!tagContent.startsWith("!")) {
48+
// Открывающий тег (не комментарий)
49+
const tagNameMatch = tagContent.match(/^(\w+)/i);
50+
if (tagNameMatch) {
51+
const tagName = tagNameMatch[1].toLowerCase();
52+
// Проверяем, что это не самозакрывающийся тег
53+
if (!selfClosingTags.includes(tagName) && !tagContent.endsWith("/")) {
54+
openTags.push(tagName);
55+
}
56+
}
57+
}
58+
59+
inTag = false;
60+
currentTag = "";
61+
}
62+
} else {
63+
if (charCount >= limit) break;
64+
result += char;
65+
charCount++;
66+
}
67+
}
68+
69+
// Закрываем все открытые теги
70+
let closedTags = result;
71+
for (let i = openTags.length - 1; i >= 0; i--) {
72+
closedTags += `</${openTags[i]}>`;
73+
}
74+
75+
return this.sanitizer.bypassSecurityTrustHtml(closedTags + "...");
76+
}
77+
}

projects/skills/src/app/shared/skill-card/skill-card.component.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<!-- @format -->
22

3-
<div class="skill-card--wrapper">
3+
<div class="skill-card--wrapper" (click)="toggleExpand($event)">
44
<div class="skill-card">
55
<div class="skill-card__inner">
66
<app-avatar [size]="50" [url]="skill.fileLink"></app-avatar>
@@ -30,7 +30,7 @@
3030
></app-circle-progress-bar>
3131
</div>
3232

33-
<div class="skill-card__expand" (click)="toggleExpand()">
33+
<div class="skill-card__expand" (click)="toggleExpand($event)">
3434
@if (isExpanded) {
3535
<i appIcon icon="arrow-no-body" class="skill-card__expanded" appSquare="8"></i>
3636
} @else {

projects/skills/src/app/shared/skill-card/skill-card.component.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
&--wrapper {
1818
position: relative;
1919
z-index: 10000;
20+
cursor: pointer;
2021
}
2122

2223
&__info {

projects/skills/src/app/shared/skill-card/skill-card.component.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ export class SkillCardComponent {
4242

4343
isExpanded = false;
4444

45-
toggleExpand(): void {
45+
toggleExpand(event: Event): void {
46+
event.stopPropagation();
4647
this.isExpanded = !this.isExpanded;
4748
}
4849

projects/skills/src/app/task/shared/exclude-task/exclude-task.component.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<div class="exclude" [class.exclude--hasVideo]="!!sanitizedFileUrl">
44
<div class="exclude__body">
55
@if (!sanitizedFileUrl) {
6-
<h2 class="exclude__title text-body-bold-12">{{ data.text }}</h2>
6+
<h2 class="exclude__title text-body-bold-12">{{ data.text | truncate: 110 }}</h2>
77
}
88
<div class="exclude__text text-body-12" [class.exclude__text--hasVideo]="!!sanitizedFileUrl">
99
@if (sanitizedFileUrl) {
@@ -17,14 +17,14 @@ <h2 class="exclude__title text-body-bold-12">{{ data.text }}</h2>
1717
></iframe>
1818
}
1919

20-
<div [innerHTML]="description"></div>
20+
<div [innerHTML]="description | truncate: 700"></div>
2121
</div>
2222
</div>
2323

2424
<div class="exclude__content">
2525
<p class="exclude__answer text-body-12">ответ</p>
2626
<ul class="exclude__list">
27-
<p class="exclude__text text-body-12">{{ data.text }}</p>
27+
<p class="exclude__text text-body-12">{{ data.text | truncate: 110 }}</p>
2828
@for (op of data.answers; track op.id) {
2929
<li
3030
class="exclude__item"

projects/skills/src/app/task/shared/exclude-task/exclude-task.component.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import type { ExcludeQuestion, ExcludeQuestionResponse } from "../../../../model
66
import { DomSanitizer, type SafeResourceUrl } from "@angular/platform-browser";
77
import { ParseBreaksPipe, YtExtractService } from "@corelib";
88
import { CheckboxComponent } from "@ui/components";
9+
import { TruncateHtmlPipe } from "projects/core/src/lib/pipes/truncate-html.pipe";
10+
import { TruncatePipe } from "projects/core/src/lib/pipes/truncate.pipe";
911

1012
/**
1113
* Компо��ент задачи на исключение лишнего
@@ -30,7 +32,7 @@ import { CheckboxComponent } from "@ui/components";
3032
@Component({
3133
selector: "app-exclude-task",
3234
standalone: true,
33-
imports: [CommonModule, ParseBreaksPipe, CheckboxComponent],
35+
imports: [CommonModule, ParseBreaksPipe, TruncateHtmlPipe, TruncatePipe, CheckboxComponent],
3436
templateUrl: "./exclude-task.component.html",
3537
styleUrl: "./exclude-task.component.scss",
3638
})

projects/skills/src/app/task/shared/radio-select-task/radio-select-task.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
</p>
1313
}
1414

15-
<p class="radio__text text-body-12" [innerHTML]="description"></p>
15+
<p class="radio__text text-body-12" [innerHTML]="data.description | truncateHtml: 700"></p>
1616

1717
@if (data.videoUrl) {
1818
<iframe

projects/skills/src/app/task/shared/radio-select-task/radio-select-task.component.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type { SingleQuestion, SingleQuestionError } from "../../../../models/ste
66
import { DomSanitizer, type SafeResourceUrl } from "@angular/platform-browser";
77
import { ParseBreaksPipe, YtExtractService } from "@corelib";
88
import { TruncatePipe } from "projects/core/src/lib/pipes/truncate.pipe";
9+
import { TruncateHtmlPipe } from "projects/core/src/lib/pipes/truncate-html.pipe";
910

1011
/**
1112
* Компонент задачи с выбором одного варианта (радио-кнопки)
@@ -29,7 +30,7 @@ import { TruncatePipe } from "projects/core/src/lib/pipes/truncate.pipe";
2930
@Component({
3031
selector: "app-radio-select-task",
3132
standalone: true,
32-
imports: [CommonModule, ParseBreaksPipe, TruncatePipe],
33+
imports: [CommonModule, ParseBreaksPipe, TruncatePipe, TruncateHtmlPipe],
3334
templateUrl: "./radio-select-task.component.html",
3435
styleUrl: "./radio-select-task.component.scss",
3536
})
@@ -65,7 +66,6 @@ export class RadioSelectTaskComponent {
6566
sanitizer = inject(DomSanitizer); // Сервис для безопасной работы с HTML
6667
ytExtractService = inject(YtExtractService); // Сервис для извлечения YouTube ссылок
6768

68-
description: any; // Обработанное описание
6969
sourceType: "embed" | "img" | null = null;
7070
mediaUrl?: SafeResourceUrl | string;
7171

projects/skills/src/app/task/shared/video-task/info-task.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
<p
1313
class="video__text text-body-12"
1414
[innerHTML]="
15-
data.description | truncate: (data.videoUrl ? 627 : sourceType === 'img' ? 500 : 1000)
15+
data.description | truncateHtml: (data.videoUrl ? 627 : sourceType === 'img' ? 500 : 1000)
1616
"
1717
></p>
1818
</div>

projects/skills/src/app/task/shared/video-task/info-task.component.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { CommonModule } from "@angular/common";
55
import type { InfoSlide } from "../../../../models/step.model";
66
import { DomSanitizer, type SafeResourceUrl } from "@angular/platform-browser";
77
import { YtExtractService } from "@corelib";
8+
import { TruncateHtmlPipe } from "projects/core/src/lib/pipes/truncate-html.pipe";
89
import { TruncatePipe } from "projects/core/src/lib/pipes/truncate.pipe";
910

1011
/**
@@ -24,7 +25,7 @@ import { TruncatePipe } from "projects/core/src/lib/pipes/truncate.pipe";
2425
@Component({
2526
selector: "app-info-task",
2627
standalone: true,
27-
imports: [CommonModule, TruncatePipe],
28+
imports: [CommonModule, TruncateHtmlPipe, TruncatePipe],
2829
templateUrl: "./info-task.component.html",
2930
styleUrl: "./info-task.component.scss",
3031
})

0 commit comments

Comments
 (0)