croco-html 4.8.0 → 4.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/croco-html.mjs +156 -72
- package/fesm2022/croco-html.mjs.map +1 -1
- package/lib/components/editor/audio-editor/audio-editor.component.d.ts +7 -6
- package/lib/components/xml-tags/audio-player-tag-view/audio-player-tag-view.component.d.ts +11 -5
- package/lib/extensions/AudioMethods.d.ts +0 -1
- package/lib/index.d.ts +4 -4
- package/lib/models/audio-models.d.ts +0 -2
- package/lib/services/CommonFileInfoQueryService.d.ts +16 -0
- package/package.json +1 -1
package/fesm2022/croco-html.mjs
CHANGED
|
@@ -14,7 +14,7 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
|
|
14
14
|
import * as i29 from '@angular/material/button-toggle';
|
|
15
15
|
import { MatButtonToggleGroup, MatButtonToggle, MatButtonToggleModule } from '@angular/material/button-toggle';
|
|
16
16
|
import { Subject, takeUntil, BehaviorSubject } from 'rxjs';
|
|
17
|
-
import * as
|
|
17
|
+
import * as i6 from '@ng-select/ng-select';
|
|
18
18
|
import { NgSelectModule } from '@ng-select/ng-select';
|
|
19
19
|
import * as i1$2 from '@angular/platform-browser';
|
|
20
20
|
import * as i1$3 from '@angular/material/card';
|
|
@@ -36,7 +36,7 @@ import * as i11 from '@angular/material/select';
|
|
|
36
36
|
import { MatSelect, MatSelectModule } from '@angular/material/select';
|
|
37
37
|
import * as i7 from '@angular/material/progress-spinner';
|
|
38
38
|
import { MatProgressSpinner, MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
|
39
|
-
import * as i5
|
|
39
|
+
import * as i5 from '@angular/material/tabs';
|
|
40
40
|
import { MatTabsModule } from '@angular/material/tabs';
|
|
41
41
|
import * as i2$2 from '@angular/material/dialog';
|
|
42
42
|
import { MatDialogTitle, MatDialogContent, MatDialogActions, MatDialogModule } from '@angular/material/dialog';
|
|
@@ -59,7 +59,7 @@ import * as i9 from '@angular/material/menu';
|
|
|
59
59
|
import { MatMenuModule } from '@angular/material/menu';
|
|
60
60
|
import * as i19 from '@angular/material/paginator';
|
|
61
61
|
import { MatPaginatorModule } from '@angular/material/paginator';
|
|
62
|
-
import * as i6 from '@angular/material/progress-bar';
|
|
62
|
+
import * as i6$1 from '@angular/material/progress-bar';
|
|
63
63
|
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
|
64
64
|
import * as i26 from '@angular/material/sidenav';
|
|
65
65
|
import { MatSidenavModule } from '@angular/material/sidenav';
|
|
@@ -136,7 +136,6 @@ class AngularMaterialTableMethods {
|
|
|
136
136
|
class FileAudioTagDataConsts {
|
|
137
137
|
static { this.TagName = "audio-player"; }
|
|
138
138
|
static { this.FileIdAttrName = "file-id"; }
|
|
139
|
-
static { this.FileNameAttrName = "file-name"; }
|
|
140
139
|
}
|
|
141
140
|
class AudioMethods {
|
|
142
141
|
static buildUrl(fileId, fileName) {
|
|
@@ -746,27 +745,18 @@ class FileAudioTagService {
|
|
|
746
745
|
}
|
|
747
746
|
bodyTagToHtmlStringConverter(bodyTag) {
|
|
748
747
|
let fileIdAttr = "";
|
|
749
|
-
let fileNameAttr = "";
|
|
750
748
|
if (bodyTag.attributes.hasOwnProperty(FileAudioTagDataConsts.FileIdAttrName) &&
|
|
751
749
|
bodyTag.attributes[FileAudioTagDataConsts.FileIdAttrName]) {
|
|
752
750
|
fileIdAttr = `${FileAudioTagDataConsts.FileIdAttrName}="${bodyTag.attributes[FileAudioTagDataConsts.FileIdAttrName]}"`;
|
|
753
751
|
}
|
|
754
|
-
|
|
755
|
-
bodyTag.attributes[FileAudioTagDataConsts.FileNameAttrName]) {
|
|
756
|
-
fileNameAttr = ` ${FileAudioTagDataConsts.FileNameAttrName}="${bodyTag.attributes[FileAudioTagDataConsts.FileNameAttrName]}"`;
|
|
757
|
-
}
|
|
758
|
-
return `<${FileAudioTagDataConsts.TagName} ${fileIdAttr}${fileNameAttr}></${FileAudioTagDataConsts.TagName}>`;
|
|
752
|
+
return `<${FileAudioTagDataConsts.TagName} ${fileIdAttr}></${FileAudioTagDataConsts.TagName}>`;
|
|
759
753
|
}
|
|
760
754
|
extractBlockFromHtmlElement(elem, options) {
|
|
761
755
|
let fileId = elem.getAttribute(FileAudioTagDataConsts.FileIdAttrName);
|
|
762
|
-
let fileName = elem.getAttribute(FileAudioTagDataConsts.FileNameAttrName);
|
|
763
|
-
let src = AudioMethods.buildUrl(fileId, fileName);
|
|
764
756
|
return {
|
|
765
757
|
tagName: FileAudioTagDataConsts.TagName,
|
|
766
758
|
data: {
|
|
767
|
-
|
|
768
|
-
fileId: fileId,
|
|
769
|
-
fileName: fileName
|
|
759
|
+
fileId: fileId
|
|
770
760
|
}
|
|
771
761
|
};
|
|
772
762
|
}
|
|
@@ -776,9 +766,6 @@ class FileAudioTagService {
|
|
|
776
766
|
if (fileData.fileId) {
|
|
777
767
|
attrs[FileAudioTagDataConsts.FileIdAttrName] = fileData.fileId;
|
|
778
768
|
}
|
|
779
|
-
if (fileData.fileName) {
|
|
780
|
-
attrs[FileAudioTagDataConsts.FileNameAttrName] = fileData.fileName;
|
|
781
|
-
}
|
|
782
769
|
return {
|
|
783
770
|
tagDescription: {
|
|
784
771
|
tag: FileAudioTagDataConsts.TagName,
|
|
@@ -797,8 +784,7 @@ class FileAudioTagService {
|
|
|
797
784
|
isCustom: false
|
|
798
785
|
},
|
|
799
786
|
attributes: {
|
|
800
|
-
[FileAudioTagDataConsts.FileIdAttrName]: ''
|
|
801
|
-
[FileAudioTagDataConsts.FileNameAttrName]: ''
|
|
787
|
+
[FileAudioTagDataConsts.FileIdAttrName]: ''
|
|
802
788
|
},
|
|
803
789
|
innerHtml: "",
|
|
804
790
|
};
|
|
@@ -1234,7 +1220,7 @@ class PublicFileUploadService {
|
|
|
1234
1220
|
return this.postFilesInner(formData, applicationId);
|
|
1235
1221
|
}
|
|
1236
1222
|
postFilesInner(formData, applicationId, withProgress = false) {
|
|
1237
|
-
let endpoint = this.baseUrl + `api/files/upload?
|
|
1223
|
+
let endpoint = this.baseUrl + `api/files/upload?executeHandlersNow=true`;
|
|
1238
1224
|
if (applicationId) {
|
|
1239
1225
|
endpoint += `&applicationId=${applicationId}`;
|
|
1240
1226
|
}
|
|
@@ -1265,7 +1251,7 @@ class PrivateFileUploadService {
|
|
|
1265
1251
|
return this.postFilesInner(formData, applicationId);
|
|
1266
1252
|
}
|
|
1267
1253
|
postFilesInner(formData, applicationId, withProgress = false) {
|
|
1268
|
-
let endpoint = this.baseUrl + `api/private-files/upload?
|
|
1254
|
+
let endpoint = this.baseUrl + `api/private-files/upload?executeHandlersNow=true`;
|
|
1269
1255
|
if (applicationId) {
|
|
1270
1256
|
endpoint += `&applicationId=${applicationId}`;
|
|
1271
1257
|
}
|
|
@@ -1479,20 +1465,48 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImpo
|
|
|
1479
1465
|
args: ['BASE_URL']
|
|
1480
1466
|
}] }] });
|
|
1481
1467
|
|
|
1468
|
+
class CommonFileInfoQueryService {
|
|
1469
|
+
constructor(_httpClient, baseUrl) {
|
|
1470
|
+
this._httpClient = _httpClient;
|
|
1471
|
+
this._baseControllerUrl = `${baseUrl}api/common-files`;
|
|
1472
|
+
}
|
|
1473
|
+
/**
|
|
1474
|
+
* Получить информацию о файле на сервере по идентификатору.
|
|
1475
|
+
* @param model
|
|
1476
|
+
* @returns
|
|
1477
|
+
*/
|
|
1478
|
+
getInfo(id) {
|
|
1479
|
+
return this._httpClient.get(`${this._baseControllerUrl}/query/get-info/${id}`);
|
|
1480
|
+
}
|
|
1481
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: CommonFileInfoQueryService, deps: [{ token: i1.HttpClient }, { token: 'BASE_URL' }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1482
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: CommonFileInfoQueryService, providedIn: 'root' }); }
|
|
1483
|
+
}
|
|
1484
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: CommonFileInfoQueryService, decorators: [{
|
|
1485
|
+
type: Injectable,
|
|
1486
|
+
args: [{
|
|
1487
|
+
providedIn: 'root'
|
|
1488
|
+
}]
|
|
1489
|
+
}], ctorParameters: () => [{ type: i1.HttpClient }, { type: undefined, decorators: [{
|
|
1490
|
+
type: Inject,
|
|
1491
|
+
args: ['BASE_URL']
|
|
1492
|
+
}] }] });
|
|
1493
|
+
|
|
1482
1494
|
class AudioEditorComponent {
|
|
1483
|
-
constructor(_publicFileService, _privateFileService, _htmlSettingsService) {
|
|
1495
|
+
constructor(_publicFileService, _privateFileService, _htmlSettingsService, _commonFileInfoService) {
|
|
1484
1496
|
this._publicFileService = _publicFileService;
|
|
1485
1497
|
this._privateFileService = _privateFileService;
|
|
1486
1498
|
this._htmlSettingsService = _htmlSettingsService;
|
|
1499
|
+
this._commonFileInfoService = _commonFileInfoService;
|
|
1487
1500
|
this.hasAudioError = false;
|
|
1501
|
+
this.errorMessage = '';
|
|
1488
1502
|
this.isPlaying = false;
|
|
1489
1503
|
this.isLoading = false;
|
|
1490
1504
|
this.currentTime = 0;
|
|
1491
1505
|
this.duration = 0;
|
|
1492
1506
|
this.volume = 1;
|
|
1493
1507
|
this.isMuted = false;
|
|
1508
|
+
this.audioSrc = '';
|
|
1494
1509
|
this.searchOrEdit = "search";
|
|
1495
|
-
this.fileName = '';
|
|
1496
1510
|
this.files = [];
|
|
1497
1511
|
this.loading = false;
|
|
1498
1512
|
this.q = '';
|
|
@@ -1505,39 +1519,63 @@ class AudioEditorComponent {
|
|
|
1505
1519
|
set fileId(value) {
|
|
1506
1520
|
this.tag.attributes[FileAudioTagDataConsts.FileIdAttrName] = value;
|
|
1507
1521
|
}
|
|
1508
|
-
get fileNameAttr() {
|
|
1509
|
-
return this.tag.attributes[FileAudioTagDataConsts.FileNameAttrName];
|
|
1510
|
-
}
|
|
1511
|
-
set fileNameAttr(value) {
|
|
1512
|
-
this.tag.attributes[FileAudioTagDataConsts.FileNameAttrName] = value;
|
|
1513
|
-
}
|
|
1514
1522
|
hasFileId() {
|
|
1515
1523
|
return this.tag.attributes.hasOwnProperty(FileAudioTagDataConsts.FileIdAttrName) && this.fileId && this.fileId !== '';
|
|
1516
1524
|
}
|
|
1517
|
-
getSrc() {
|
|
1518
|
-
// return AudioMethods.buildUrl(this.fileId, this.fileNameAttr);
|
|
1519
|
-
return this.fileId;
|
|
1520
|
-
}
|
|
1521
1525
|
get audioElement() {
|
|
1522
1526
|
return this.audioPlayer?.nativeElement || null;
|
|
1523
1527
|
}
|
|
1524
1528
|
onErrorHandler() {
|
|
1525
1529
|
this.hasAudioError = true;
|
|
1526
1530
|
this.isLoading = false;
|
|
1531
|
+
if (!this.errorMessage) {
|
|
1532
|
+
this.errorMessage = 'Аудио-файл не найден по указанному идентификатору, возможно файл не существует или отсутствует на сервере.';
|
|
1533
|
+
}
|
|
1527
1534
|
}
|
|
1528
1535
|
removeAudioError() {
|
|
1529
1536
|
this.hasAudioError = false;
|
|
1537
|
+
this.errorMessage = '';
|
|
1538
|
+
}
|
|
1539
|
+
setError(message) {
|
|
1540
|
+
this.hasAudioError = true;
|
|
1541
|
+
this.errorMessage = message;
|
|
1542
|
+
this.isLoading = false;
|
|
1530
1543
|
}
|
|
1531
1544
|
onFileIdChanged(fileId) {
|
|
1532
1545
|
this.fileId = fileId;
|
|
1533
1546
|
this.removeAudioError();
|
|
1534
|
-
this.
|
|
1547
|
+
this.checkAndLoadAudioFile();
|
|
1535
1548
|
}
|
|
1536
|
-
|
|
1537
|
-
this.
|
|
1549
|
+
checkAndLoadAudioFile() {
|
|
1550
|
+
if (!this.hasFileId()) {
|
|
1551
|
+
return;
|
|
1552
|
+
}
|
|
1553
|
+
this.isLoading = true;
|
|
1554
|
+
this.hasAudioError = false;
|
|
1555
|
+
this.errorMessage = '';
|
|
1556
|
+
const fileIdValue = this.fileId;
|
|
1557
|
+
this._commonFileInfoService.getInfo(fileIdValue)
|
|
1558
|
+
.pipe(takeUntil(this.unsubscribe))
|
|
1559
|
+
.subscribe({
|
|
1560
|
+
next: (result) => {
|
|
1561
|
+
if (!result.fileInfo) {
|
|
1562
|
+
this.setError(`Файл с идентификатором ${fileIdValue} не найден на сервере.`);
|
|
1563
|
+
return;
|
|
1564
|
+
}
|
|
1565
|
+
if (result.fileInfo.type !== FileType.Audio) {
|
|
1566
|
+
this.setError(`Файл с идентификатором ${fileIdValue} должен быть c типом Audio, а получено ${result.fileInfo.type}.`);
|
|
1567
|
+
return;
|
|
1568
|
+
}
|
|
1569
|
+
this.audioSrc = result.fileInfo.downloadUrl;
|
|
1570
|
+
this.loadAudioFile();
|
|
1571
|
+
},
|
|
1572
|
+
error: (error) => {
|
|
1573
|
+
this.setError(`Ошибка при получении информации о файле: ${error.message || 'Неизвестная ошибка'}`);
|
|
1574
|
+
}
|
|
1575
|
+
});
|
|
1538
1576
|
}
|
|
1539
1577
|
loadAudioFile() {
|
|
1540
|
-
if (!this.hasFileId()) {
|
|
1578
|
+
if (!this.hasFileId() || !this.audioSrc) {
|
|
1541
1579
|
return;
|
|
1542
1580
|
}
|
|
1543
1581
|
this.isLoading = true;
|
|
@@ -1675,22 +1713,21 @@ class AudioEditorComponent {
|
|
|
1675
1713
|
onFileSelected(fileId) {
|
|
1676
1714
|
const selectedFile = this.files.find(f => f.fileId === fileId);
|
|
1677
1715
|
if (selectedFile) {
|
|
1678
|
-
|
|
1679
|
-
this.
|
|
1680
|
-
this.fileNameAttr = selectedFile.fileName;
|
|
1681
|
-
this.loadAudioFile();
|
|
1716
|
+
this.fileId = fileId;
|
|
1717
|
+
this.checkAndLoadAudioFile();
|
|
1682
1718
|
}
|
|
1683
1719
|
}
|
|
1684
1720
|
onFilesUploaded(fileIds) {
|
|
1685
1721
|
if (fileIds && fileIds.length > 0) {
|
|
1686
|
-
|
|
1687
|
-
this.fileId = '
|
|
1722
|
+
const firstFileId = fileIds[0];
|
|
1723
|
+
this.fileId = typeof firstFileId === 'number' ? firstFileId.toString() : firstFileId;
|
|
1688
1724
|
this.loadFiles();
|
|
1725
|
+
this.checkAndLoadAudioFile();
|
|
1689
1726
|
}
|
|
1690
1727
|
}
|
|
1691
1728
|
removeAudio() {
|
|
1692
1729
|
this.fileId = '';
|
|
1693
|
-
this.
|
|
1730
|
+
this.audioSrc = '';
|
|
1694
1731
|
this.isPlaying = false;
|
|
1695
1732
|
this.currentTime = 0;
|
|
1696
1733
|
this.duration = 0;
|
|
@@ -1700,9 +1737,9 @@ class AudioEditorComponent {
|
|
|
1700
1737
|
if (this.audioPlayer?.nativeElement) {
|
|
1701
1738
|
const audio = this.audioPlayer.nativeElement;
|
|
1702
1739
|
audio.volume = this.volume;
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1740
|
+
}
|
|
1741
|
+
if (this.hasFileId()) {
|
|
1742
|
+
this.checkAndLoadAudioFile();
|
|
1706
1743
|
}
|
|
1707
1744
|
this.loadFiles();
|
|
1708
1745
|
}
|
|
@@ -1710,8 +1747,8 @@ class AudioEditorComponent {
|
|
|
1710
1747
|
this.unsubscribe.next();
|
|
1711
1748
|
this.unsubscribe.complete();
|
|
1712
1749
|
}
|
|
1713
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AudioEditorComponent, deps: [{ token: PublicFilesQueryService }, { token: PrivateFilesQueryService }, { token: CrocoHtmlFileOptionsService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1714
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: AudioEditorComponent, isStandalone: true, selector: "croco-html-audio-editor", inputs: { tag: "tag", presentOrEdit: "presentOrEdit" }, viewQueries: [{ propertyName: "audioPlayer", first: true, predicate: ["audioPlayer"], descendants: true }], ngImport: i0, template: "<div class=\"audio-editor\">\n @if (hasAudioError) {\n <div class=\"error-block\">\n <mat-icon>error</mat-icon>\n <span>\u0410\u0443\u0434\u0438\u043E-\u0444\u0430\u0439\u043B \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D \u043F\u043E \u0443\u043A\u0430\u0437\u0430\u043D\u043D\u043E\u043C\u0443 \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440\u0443, \u0432\u043E\u0437\u043C\u043E\u0436\u043D\u043E \u0444\u0430\u0439\u043B \u043D\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442 \u0438\u043B\u0438 \u043E\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u043D\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435.</span>\n </div>\n }\n\n @if (hasFileId() && !hasAudioError) {\n <div class=\"audio-player-container\">\n <audio\n #audioPlayer\n [src]=\"getSrc()\"\n (error)=\"onErrorHandler()\"\n (loadedmetadata)=\"onLoadedMetadata()\"\n (timeupdate)=\"onTimeUpdate()\"\n (ended)=\"onEnded()\"\n (canplay)=\"onCanPlay()\"\n preload=\"metadata\">\n </audio>\n\n @if (isLoading) {\n <div class=\"loading-indicator\">\n <div class=\"progress-bar\">\n <div class=\"progress-bar-fill\"></div>\n </div>\n <span>\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430 \u0430\u0443\u0434\u0438\u043E...</span>\n </div>\n }\n\n <div class=\"player-controls\">\n <div class=\"control-buttons\">\n <button (click)=\"restart()\" [disabled]=\"!hasFileId()\" mat-icon-button>\n <mat-icon>replay</mat-icon>\n </button>\n\n <button (click)=\"rewind()\" [disabled]=\"!hasFileId()\" mat-icon-button>\n <mat-icon>replay_10</mat-icon>\n </button>\n\n <button\n (click)=\"togglePlayPause()\"\n [disabled]=\"!hasFileId() || isLoading\"\n class=\"play-pause-btn\" mat-icon-button>\n <mat-icon>{{ isPlaying ? 'pause' : 'play_arrow' }}</mat-icon>\n </button>\n\n <button (click)=\"forward()\" [disabled]=\"!hasFileId()\" mat-icon-button>\n <mat-icon>forward_10</mat-icon>\n </button>\n\n @if (!presentOrEdit) {\n <button (click)=\"removeAudio()\" [disabled]=\"!hasFileId()\" color=\"warn\" mat-icon-button>\n <mat-icon>delete</mat-icon>\n </button>\n }\n </div>\n\n <div class=\"progress-section\">\n <span class=\"time-display\">{{ formatTime(currentTime) }}</span>\n\n <input\n type=\"range\"\n min=\"0\"\n max=\"100\"\n [value]=\"getProgress()\"\n (input)=\"onSeekChange($event)\"\n [disabled]=\"!hasFileId() || duration === 0\"\n class=\"progress-slider\">\n\n <span class=\"time-display\">{{ formatTime(duration) }}</span>\n </div>\n\n <div class=\"volume-controls\">\n <button (click)=\"toggleMute()\" [disabled]=\"!hasFileId()\" mat-icon-button>\n <mat-icon>{{ isMuted ? 'volume_off' : 'volume_up' }}</mat-icon>\n </button>\n\n <input\n type=\"range\"\n min=\"0\"\n max=\"100\"\n [value]=\"volume * 100\"\n (input)=\"onVolumeChange($event)\"\n [disabled]=\"!hasFileId()\"\n class=\"volume-slider\">\n </div>\n </div>\n </div>\n }\n\n @if (!hasFileId()) {\n <div class=\"no-file-message\">\n <mat-icon>music_off</mat-icon>\n <h5>\u0410\u0443\u0434\u0438\u043E-\u0444\u0430\u0439\u043B \u043D\u0435 \u0432\u044B\u0431\u0440\u0430\u043D</h5>\n @if (!presentOrEdit) {\n <p>\u041D\u0430\u0436\u043C\u0438\u0442\u0435 \u043A\u043D\u043E\u043F\u043A\u0443 \u0440\u0435\u0434\u0430\u043A\u0442\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u044F \u0434\u043B\u044F \u0432\u044B\u0431\u043E\u0440\u0430 \u0438\u043B\u0438 \u0437\u0430\u0433\u0440\u0443\u0437\u043A\u0438 \u0430\u0443\u0434\u0438\u043E-\u0444\u0430\u0439\u043B\u0430</p>\n }\n </div>\n }\n @if (!presentOrEdit) {\n <div class=\"edit-panel\">\n <div class=\"edit-header\">\n <h6>\u041D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438 \u0430\u0443\u0434\u0438\u043E-\u0444\u0430\u0439\u043B\u0430</h6>\n </div>\n\n <div class=\"mode-toggle\">\n <mat-button-toggle-group [(ngModel)]=\"searchOrEdit\">\n <mat-button-toggle value=\"search\">\n <mat-icon>search</mat-icon>\n \u0418\u0441\u043A\u0430\u0442\u044C \u0444\u0430\u0439\u043B\n </mat-button-toggle>\n <mat-button-toggle value=\"edit\">\n <mat-icon>settings</mat-icon>\n \u0423\u043A\u0430\u0437\u0430\u0442\u044C \u043F\u043E ID\n </mat-button-toggle>\n </mat-button-toggle-group>\n </div>\n\n @if (searchOrEdit === 'search') {\n <div class=\"file-selection\">\n <div class=\"existing-files\">\n <h6>\u0412\u044B\u0431\u0440\u0430\u0442\u044C \u0438\u0437 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044E\u0449\u0438\u0445 \u0444\u0430\u0439\u043B\u043E\u0432:</h6>\n <ng-select\n [(ngModel)]=\"fileId\"\n (ngModelChange)=\"onFileSelected($event)\"\n [items]=\"files\"\n bindLabel=\"fileName\"\n bindValue=\"fileId\"\n (search)=\"onSearchChanged($event)\"\n [loading]=\"loading\"\n [loadingText]=\"'\u0417\u0430\u0433\u0440\u0443\u0436\u0430\u044E \u0444\u0430\u0439\u043B\u044B'\"\n placeholder=\"\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0430\u0443\u0434\u0438\u043E-\u0444\u0430\u0439\u043B\"\n [clearable]=\"true\">\n <ng-template ng-option-tmp let-item=\"item\">\n <div class=\"file-option\">\n <mat-icon>audiotrack</mat-icon>\n <span>{{ item.fileName }}</span>\n <small>ID: {{ item.fileId }}</small>\n </div>\n </ng-template>\n </ng-select>\n </div>\n\n <div class=\"upload-section\">\n <h6>\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C \u043D\u043E\u0432\u044B\u0439 \u0444\u0430\u0439\u043B:</h6>\n <croco-app-upload-files-btn\n extAccepts=\"audio/*\"\n [isMultiple]=\"false\"\n btnText=\"\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C \u0430\u0443\u0434\u0438\u043E-\u0444\u0430\u0439\u043B\"\n (onPublicFilesUploaded)=\"onFilesUploaded($event.fileIds)\"\n (onPrivateFilesUploaded)=\"onFilesUploaded($event.fileIds)\">\n </croco-app-upload-files-btn>\n </div>\n </div>\n } @else if (searchOrEdit === 'edit') {\n <div class=\"manual-input\">\n <mat-form-field class=\"w-100\">\n <mat-label>\u0418\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440 \u0444\u0430\u0439\u043B\u0430</mat-label>\n <input\n matInput\n placeholder=\"\u0412\u0432\u0435\u0434\u0438\u0442\u0435 ID \u0430\u0443\u0434\u0438\u043E-\u0444\u0430\u0439\u043B\u0430\"\n autocomplete=\"off\"\n [(ngModel)]=\"fileId\"\n (ngModelChange)=\"onFileIdChanged($event)\">\n </mat-form-field>\n\n <mat-form-field class=\"w-100\">\n <mat-label>\u0418\u043C\u044F \u0444\u0430\u0439\u043B\u0430</mat-label>\n <input\n matInput\n placeholder=\"\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0438\u043C\u044F \u0444\u0430\u0439\u043B\u0430\"\n autocomplete=\"off\"\n [(ngModel)]=\"fileNameAttr\"\n (ngModelChange)=\"onFileNameChanged($event)\">\n </mat-form-field>\n </div>\n }\n </div>\n }\n</div>\n", styles: [".audio-editor{width:100%;padding:16px;background:#fafafa;border-radius:8px;border:1px solid #e0e0e0}.error-block{display:flex;align-items:center;gap:8px;background-color:#ffebee;color:#c62828;padding:12px;border-radius:8px;border:1px solid #ffcdd2;margin-bottom:16px}.error-block mat-icon{color:#c62828}.audio-player-container{background:#fff;border-radius:12px;padding:20px;box-shadow:0 2px 8px #0000001a;margin-bottom:16px}.loading-indicator{display:flex;flex-direction:column;align-items:center;gap:8px;padding:16px;color:#666}.loading-indicator .progress-bar{width:200px;height:4px;background-color:#e0e0e0;border-radius:2px;overflow:hidden}.loading-indicator .progress-bar .progress-bar-fill{height:100%;background-color:#1976d2;animation:loading 1.5s ease-in-out infinite}@keyframes loading{0%{transform:translate(-100%)}50%{transform:translate(0)}to{transform:translate(100%)}}.file-info{display:flex;align-items:center;gap:8px;margin-bottom:16px;padding:12px;background:#f5f5f5;border-radius:8px}.file-info mat-icon{color:#1976d2}.file-info .file-name{font-weight:500;flex:1}.file-info .edit-btn{margin-left:auto}.player-controls{display:flex;flex-direction:column;gap:16px}.control-buttons{display:flex;justify-content:center;align-items:center;gap:8px}.control-buttons .play-pause-btn{background-color:#1976d2;color:#fff;width:48px;height:48px}.control-buttons .play-pause-btn:hover{background-color:#1565c0}.control-buttons .play-pause-btn mat-icon{font-size:24px}.control-buttons mat-icon-button{width:40px;height:40px}.control-buttons mat-icon-button:disabled{color:#ccc}.progress-section{display:flex;align-items:center;gap:12px}.progress-section .time-display{font-family:Roboto Mono,monospace;font-size:14px;color:#666;min-width:40px;text-align:center}.progress-section .progress-slider{flex:1;height:6px;-webkit-appearance:none;appearance:none;background:#e0e0e0;outline:none;border-radius:3px}.progress-section .progress-slider::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:20px;height:20px;background:#1976d2;border-radius:50%;cursor:pointer;border:2px solid white;box-shadow:0 2px 4px #0003}.progress-section .progress-slider::-moz-range-thumb{width:20px;height:20px;background:#1976d2;border-radius:50%;cursor:pointer;border:2px solid white;box-shadow:0 2px 4px #0003}.progress-section .progress-slider:hover::-webkit-slider-thumb{background:#1565c0}.progress-section .progress-slider:hover::-moz-range-thumb{background:#1565c0}.volume-controls{display:flex;align-items:center;gap:8px;justify-content:center}.volume-controls .volume-slider{width:100px;height:6px;-webkit-appearance:none;appearance:none;background:#e0e0e0;outline:none;border-radius:3px}.volume-controls .volume-slider::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:20px;height:20px;background:#1976d2;border-radius:50%;cursor:pointer;border:2px solid white;box-shadow:0 2px 4px #0003}.volume-controls .volume-slider::-moz-range-thumb{width:20px;height:20px;background:#1976d2;border-radius:50%;cursor:pointer;border:2px solid white;box-shadow:0 2px 4px #0003}.volume-controls .volume-slider:hover::-webkit-slider-thumb{background:#1565c0}.volume-controls .volume-slider:hover::-moz-range-thumb{background:#1565c0}.no-file-message{text-align:center;padding:40px 20px;color:#666}.no-file-message mat-icon{font-size:48px;width:48px;height:48px;margin-bottom:16px;color:#ccc}.no-file-message h5{margin:0 0 8px;color:#999}.no-file-message p{margin:0;font-size:14px;color:#999}.edit-panel{background:#fff;border-radius:8px;padding:20px;border:1px solid #e0e0e0;margin-top:16px}.edit-panel .edit-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:20px}.edit-panel .edit-header h6{margin:0;color:#333}.edit-panel .mode-toggle{margin-bottom:20px}.edit-panel .mode-toggle mat-button-toggle-group{width:100%}.edit-panel .mode-toggle mat-button-toggle{flex:1;display:flex;align-items:center;justify-content:center;gap:8px}.edit-panel .file-selection{display:flex;flex-direction:column;gap:20px}.edit-panel .file-selection h6{margin:0 0 12px;color:#333}.edit-panel .file-selection .file-option{display:flex;align-items:center;gap:8px}.edit-panel .file-selection .file-option mat-icon{color:#1976d2}.edit-panel .file-selection .file-option small{margin-left:auto;color:#666}.edit-panel .file-selection .upload-section{padding-top:16px;border-top:1px solid #e0e0e0}.edit-panel .manual-input{display:flex;flex-direction:column;gap:16px}.edit-button-container{display:flex;justify-content:center;margin-top:16px}.edit-button-container .edit-mode-btn{background-color:#1976d2;color:#fff}.edit-button-container .edit-mode-btn:hover{background-color:#1565c0}@media screen and (max-width: 768px){.audio-editor{padding:12px}.audio-player-container{padding:16px}.control-buttons{flex-wrap:wrap;gap:4px}.progress-section{flex-direction:column;gap:8px}.progress-section .progress-slider{width:100%}.volume-controls{justify-content:flex-start}.volume-controls .volume-slider{width:80px}.edit-panel{padding:16px}.edit-panel .mode-toggle mat-button-toggle-group{flex-direction:column}}::ng-deep .ng-select .ng-select-container{border-radius:4px;border:1px solid #ccc}::ng-deep .ng-select.ng-select-focused .ng-select-container{border-color:#1976d2}input[type=range].progress-slider:disabled,input[type=range].volume-slider:disabled{opacity:.5;cursor:not-allowed}\n"], dependencies: [{ kind: "directive", type: MatButtonToggleGroup, selector: "mat-button-toggle-group", inputs: ["appearance", "name", "vertical", "value", "multiple", "disabled", "disabledInteractive", "hideSingleSelectionIndicator", "hideMultipleSelectionIndicator"], outputs: ["valueChange", "change"], exportAs: ["matButtonToggleGroup"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: MatButtonToggle, selector: "mat-button-toggle", inputs: ["aria-label", "aria-labelledby", "id", "name", "value", "tabIndex", "disableRipple", "appearance", "checked", "disabled", "disabledInteractive"], outputs: ["change"], exportAs: ["matButtonToggle"] }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: MatLabel, selector: "mat-label" }, { kind: "directive", type: MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: UploadFilesBtnComponent, selector: "croco-app-upload-files-btn", inputs: ["extAccepts", "isMultiple", "btnText", "hidden"], outputs: ["postFilesStarted", "onPublicFilesUploaded", "onPrivateFilesUploaded"] }, { kind: "ngmodule", type: NgSelectModule }, { kind: "component", type: i5.NgSelectComponent, selector: "ng-select", inputs: ["ariaLabelDropdown", "bindLabel", "bindValue", "ariaLabel", "markFirst", "placeholder", "fixedPlaceholder", "notFoundText", "typeToSearchText", "preventToggleOnRightClick", "addTagText", "loadingText", "clearAllText", "appearance", "dropdownPosition", "appendTo", "loading", "closeOnSelect", "hideSelected", "selectOnTab", "openOnEnter", "maxSelectedItems", "groupBy", "groupValue", "bufferAmount", "virtualScroll", "selectableGroup", "selectableGroupAsModel", "searchFn", "trackByFn", "clearOnBackspace", "labelForId", "inputAttrs", "tabIndex", "tabFocusOnClearButton", "readonly", "searchWhileComposing", "minTermLength", "editableSearchTerm", "ngClass", "typeahead", "multiple", "addTag", "searchable", "clearable", "isOpen", "items", "compareWith", "clearSearchOnAdd", "deselectOnClick", "keyDownFn"], outputs: ["blur", "focus", "change", "open", "close", "search", "clear", "add", "remove", "scroll", "scrollToEnd"] }, { kind: "directive", type: i5.NgOptionTemplateDirective, selector: "[ng-option-tmp]" }, { kind: "component", type: MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }] }); }
|
|
1750
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AudioEditorComponent, deps: [{ token: PublicFilesQueryService }, { token: PrivateFilesQueryService }, { token: CrocoHtmlFileOptionsService }, { token: CommonFileInfoQueryService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1751
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: AudioEditorComponent, isStandalone: true, selector: "croco-html-audio-editor", inputs: { tag: "tag", presentOrEdit: "presentOrEdit" }, viewQueries: [{ propertyName: "audioPlayer", first: true, predicate: ["audioPlayer"], descendants: true }], ngImport: i0, template: "<div class=\"audio-editor\">\n @if (hasAudioError) {\n <div class=\"error-block\">\n <mat-icon>error</mat-icon>\n <span>{{ errorMessage }}</span>\n </div>\n }\n\n @if (hasFileId() && !hasAudioError) {\n <div class=\"audio-player-container\">\n <audio\n #audioPlayer\n [src]=\"audioSrc\"\n (error)=\"onErrorHandler()\"\n (loadedmetadata)=\"onLoadedMetadata()\"\n (timeupdate)=\"onTimeUpdate()\"\n (ended)=\"onEnded()\"\n (canplay)=\"onCanPlay()\"\n preload=\"metadata\">\n </audio>\n\n @if (isLoading) {\n <div class=\"loading-indicator\">\n <div class=\"progress-bar\">\n <div class=\"progress-bar-fill\"></div>\n </div>\n <span>\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430 \u0430\u0443\u0434\u0438\u043E...</span>\n </div>\n }\n\n <div class=\"player-controls\">\n <div class=\"control-buttons\">\n <button (click)=\"restart()\" [disabled]=\"!hasFileId()\" mat-icon-button>\n <mat-icon>replay</mat-icon>\n </button>\n\n <button (click)=\"rewind()\" [disabled]=\"!hasFileId()\" mat-icon-button>\n <mat-icon>replay_10</mat-icon>\n </button>\n\n <button\n (click)=\"togglePlayPause()\"\n [disabled]=\"!hasFileId() || isLoading\"\n class=\"play-pause-btn\" mat-icon-button>\n <mat-icon>{{ isPlaying ? 'pause' : 'play_arrow' }}</mat-icon>\n </button>\n\n <button (click)=\"forward()\" [disabled]=\"!hasFileId()\" mat-icon-button>\n <mat-icon>forward_10</mat-icon>\n </button>\n\n @if (!presentOrEdit) {\n <button (click)=\"removeAudio()\" [disabled]=\"!hasFileId()\" color=\"warn\" mat-icon-button>\n <mat-icon>delete</mat-icon>\n </button>\n }\n </div>\n\n <div class=\"progress-section\">\n <span class=\"time-display\">{{ formatTime(currentTime) }}</span>\n\n <input\n type=\"range\"\n min=\"0\"\n max=\"100\"\n [value]=\"getProgress()\"\n (input)=\"onSeekChange($event)\"\n [disabled]=\"!hasFileId() || duration === 0\"\n class=\"progress-slider\">\n\n <span class=\"time-display\">{{ formatTime(duration) }}</span>\n </div>\n\n <div class=\"volume-controls\">\n <button (click)=\"toggleMute()\" [disabled]=\"!hasFileId()\" mat-icon-button>\n <mat-icon>{{ isMuted ? 'volume_off' : 'volume_up' }}</mat-icon>\n </button>\n\n <input\n type=\"range\"\n min=\"0\"\n max=\"100\"\n [value]=\"volume * 100\"\n (input)=\"onVolumeChange($event)\"\n [disabled]=\"!hasFileId()\"\n class=\"volume-slider\">\n </div>\n </div>\n </div>\n }\n\n @if (!hasFileId()) {\n <div class=\"no-file-message\">\n <mat-icon>music_off</mat-icon>\n <h5>\u0410\u0443\u0434\u0438\u043E-\u0444\u0430\u0439\u043B \u043D\u0435 \u0432\u044B\u0431\u0440\u0430\u043D</h5>\n @if (!presentOrEdit) {\n <p>\u041D\u0430\u0436\u043C\u0438\u0442\u0435 \u043A\u043D\u043E\u043F\u043A\u0443 \u0440\u0435\u0434\u0430\u043A\u0442\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u044F \u0434\u043B\u044F \u0432\u044B\u0431\u043E\u0440\u0430 \u0438\u043B\u0438 \u0437\u0430\u0433\u0440\u0443\u0437\u043A\u0438 \u0430\u0443\u0434\u0438\u043E-\u0444\u0430\u0439\u043B\u0430</p>\n }\n </div>\n }\n @if (!presentOrEdit) {\n <div class=\"edit-panel\">\n <div class=\"edit-header\">\n <h6>\u041D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438 \u0430\u0443\u0434\u0438\u043E-\u0444\u0430\u0439\u043B\u0430</h6>\n </div>\n\n <div class=\"mode-toggle\">\n <mat-button-toggle-group [(ngModel)]=\"searchOrEdit\">\n <mat-button-toggle value=\"search\">\n <mat-icon>search</mat-icon>\n \u0418\u0441\u043A\u0430\u0442\u044C \u0444\u0430\u0439\u043B\n </mat-button-toggle>\n <mat-button-toggle value=\"edit\">\n <mat-icon>settings</mat-icon>\n \u0423\u043A\u0430\u0437\u0430\u0442\u044C \u043F\u043E ID\n </mat-button-toggle>\n </mat-button-toggle-group>\n </div>\n\n @if (searchOrEdit === 'search') {\n <div class=\"file-selection\">\n <div class=\"existing-files\">\n <h6>\u0412\u044B\u0431\u0440\u0430\u0442\u044C \u0438\u0437 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044E\u0449\u0438\u0445 \u0444\u0430\u0439\u043B\u043E\u0432:</h6>\n <ng-select\n [(ngModel)]=\"fileId\"\n (ngModelChange)=\"onFileSelected($event)\"\n [items]=\"files\"\n bindLabel=\"fileName\"\n bindValue=\"fileId\"\n (search)=\"onSearchChanged($event)\"\n [loading]=\"loading\"\n [loadingText]=\"'\u0417\u0430\u0433\u0440\u0443\u0436\u0430\u044E \u0444\u0430\u0439\u043B\u044B'\"\n placeholder=\"\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0430\u0443\u0434\u0438\u043E-\u0444\u0430\u0439\u043B\"\n [clearable]=\"true\">\n <ng-template ng-option-tmp let-item=\"item\">\n <div class=\"file-option\">\n <mat-icon>audiotrack</mat-icon>\n <span>{{ item.fileName }}</span>\n <small>ID: {{ item.fileId }}</small>\n </div>\n </ng-template>\n </ng-select>\n </div>\n\n <div class=\"upload-section\">\n <h6>\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C \u043D\u043E\u0432\u044B\u0439 \u0444\u0430\u0439\u043B:</h6>\n <croco-app-upload-files-btn\n extAccepts=\"audio/*\"\n [isMultiple]=\"false\"\n btnText=\"\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C \u0430\u0443\u0434\u0438\u043E-\u0444\u0430\u0439\u043B\"\n (onPublicFilesUploaded)=\"onFilesUploaded($event.fileIds)\"\n (onPrivateFilesUploaded)=\"onFilesUploaded($event.fileIds)\">\n </croco-app-upload-files-btn>\n </div>\n </div>\n } @else if (searchOrEdit === 'edit') {\n <div class=\"manual-input\">\n <mat-form-field class=\"w-100\">\n <mat-label>\u0418\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440 \u0444\u0430\u0439\u043B\u0430</mat-label>\n <input\n matInput\n placeholder=\"\u0412\u0432\u0435\u0434\u0438\u0442\u0435 ID \u0430\u0443\u0434\u0438\u043E-\u0444\u0430\u0439\u043B\u0430\"\n autocomplete=\"off\"\n [(ngModel)]=\"fileId\"\n (ngModelChange)=\"onFileIdChanged($event)\">\n </mat-form-field>\n </div>\n }\n </div>\n }\n</div>\n", styles: [".audio-editor{width:100%;padding:16px;background:#fafafa;border-radius:8px;border:1px solid #e0e0e0}.error-block{display:flex;align-items:center;gap:8px;background-color:#ffebee;color:#c62828;padding:12px;border-radius:8px;border:1px solid #ffcdd2;margin-bottom:16px}.error-block mat-icon{color:#c62828}.audio-player-container{background:#fff;border-radius:12px;padding:20px;box-shadow:0 2px 8px #0000001a;margin-bottom:16px}.loading-indicator{display:flex;flex-direction:column;align-items:center;gap:8px;padding:16px;color:#666}.loading-indicator .progress-bar{width:200px;height:4px;background-color:#e0e0e0;border-radius:2px;overflow:hidden}.loading-indicator .progress-bar .progress-bar-fill{height:100%;background-color:#1976d2;animation:loading 1.5s ease-in-out infinite}@keyframes loading{0%{transform:translate(-100%)}50%{transform:translate(0)}to{transform:translate(100%)}}.file-info{display:flex;align-items:center;gap:8px;margin-bottom:16px;padding:12px;background:#f5f5f5;border-radius:8px}.file-info mat-icon{color:#1976d2}.file-info .file-name{font-weight:500;flex:1}.file-info .edit-btn{margin-left:auto}.player-controls{display:flex;flex-direction:column;gap:16px}.control-buttons{display:flex;justify-content:center;align-items:center;gap:8px}.control-buttons .play-pause-btn{background-color:#1976d2;color:#fff;width:48px;height:48px}.control-buttons .play-pause-btn:hover{background-color:#1565c0}.control-buttons .play-pause-btn mat-icon{font-size:24px}.control-buttons mat-icon-button{width:40px;height:40px}.control-buttons mat-icon-button:disabled{color:#ccc}.progress-section{display:flex;align-items:center;gap:12px}.progress-section .time-display{font-family:Roboto Mono,monospace;font-size:14px;color:#666;min-width:40px;text-align:center}.progress-section .progress-slider{flex:1;height:6px;-webkit-appearance:none;appearance:none;background:#e0e0e0;outline:none;border-radius:3px}.progress-section .progress-slider::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:20px;height:20px;background:#1976d2;border-radius:50%;cursor:pointer;border:2px solid white;box-shadow:0 2px 4px #0003}.progress-section .progress-slider::-moz-range-thumb{width:20px;height:20px;background:#1976d2;border-radius:50%;cursor:pointer;border:2px solid white;box-shadow:0 2px 4px #0003}.progress-section .progress-slider:hover::-webkit-slider-thumb{background:#1565c0}.progress-section .progress-slider:hover::-moz-range-thumb{background:#1565c0}.volume-controls{display:flex;align-items:center;gap:8px;justify-content:center}.volume-controls .volume-slider{width:100px;height:6px;-webkit-appearance:none;appearance:none;background:#e0e0e0;outline:none;border-radius:3px}.volume-controls .volume-slider::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:20px;height:20px;background:#1976d2;border-radius:50%;cursor:pointer;border:2px solid white;box-shadow:0 2px 4px #0003}.volume-controls .volume-slider::-moz-range-thumb{width:20px;height:20px;background:#1976d2;border-radius:50%;cursor:pointer;border:2px solid white;box-shadow:0 2px 4px #0003}.volume-controls .volume-slider:hover::-webkit-slider-thumb{background:#1565c0}.volume-controls .volume-slider:hover::-moz-range-thumb{background:#1565c0}.no-file-message{text-align:center;padding:40px 20px;color:#666}.no-file-message mat-icon{font-size:48px;width:48px;height:48px;margin-bottom:16px;color:#ccc}.no-file-message h5{margin:0 0 8px;color:#999}.no-file-message p{margin:0;font-size:14px;color:#999}.edit-panel{background:#fff;border-radius:8px;padding:20px;border:1px solid #e0e0e0;margin-top:16px}.edit-panel .edit-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:20px}.edit-panel .edit-header h6{margin:0;color:#333}.edit-panel .mode-toggle{margin-bottom:20px}.edit-panel .mode-toggle mat-button-toggle-group{width:100%}.edit-panel .mode-toggle mat-button-toggle{flex:1;display:flex;align-items:center;justify-content:center;gap:8px}.edit-panel .file-selection{display:flex;flex-direction:column;gap:20px}.edit-panel .file-selection h6{margin:0 0 12px;color:#333}.edit-panel .file-selection .file-option{display:flex;align-items:center;gap:8px}.edit-panel .file-selection .file-option mat-icon{color:#1976d2}.edit-panel .file-selection .file-option small{margin-left:auto;color:#666}.edit-panel .file-selection .upload-section{padding-top:16px;border-top:1px solid #e0e0e0}.edit-panel .manual-input{display:flex;flex-direction:column;gap:16px}.edit-button-container{display:flex;justify-content:center;margin-top:16px}.edit-button-container .edit-mode-btn{background-color:#1976d2;color:#fff}.edit-button-container .edit-mode-btn:hover{background-color:#1565c0}@media screen and (max-width: 768px){.audio-editor{padding:12px}.audio-player-container{padding:16px}.control-buttons{flex-wrap:wrap;gap:4px}.progress-section{flex-direction:column;gap:8px}.progress-section .progress-slider{width:100%}.volume-controls{justify-content:flex-start}.volume-controls .volume-slider{width:80px}.edit-panel{padding:16px}.edit-panel .mode-toggle mat-button-toggle-group{flex-direction:column}}::ng-deep .ng-select .ng-select-container{border-radius:4px;border:1px solid #ccc}::ng-deep .ng-select.ng-select-focused .ng-select-container{border-color:#1976d2}input[type=range].progress-slider:disabled,input[type=range].volume-slider:disabled{opacity:.5;cursor:not-allowed}\n"], dependencies: [{ kind: "directive", type: MatButtonToggleGroup, selector: "mat-button-toggle-group", inputs: ["appearance", "name", "vertical", "value", "multiple", "disabled", "disabledInteractive", "hideSingleSelectionIndicator", "hideMultipleSelectionIndicator"], outputs: ["valueChange", "change"], exportAs: ["matButtonToggleGroup"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: MatButtonToggle, selector: "mat-button-toggle", inputs: ["aria-label", "aria-labelledby", "id", "name", "value", "tabIndex", "disableRipple", "appearance", "checked", "disabled", "disabledInteractive"], outputs: ["change"], exportAs: ["matButtonToggle"] }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: MatLabel, selector: "mat-label" }, { kind: "directive", type: MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: UploadFilesBtnComponent, selector: "croco-app-upload-files-btn", inputs: ["extAccepts", "isMultiple", "btnText", "hidden"], outputs: ["postFilesStarted", "onPublicFilesUploaded", "onPrivateFilesUploaded"] }, { kind: "ngmodule", type: NgSelectModule }, { kind: "component", type: i6.NgSelectComponent, selector: "ng-select", inputs: ["ariaLabelDropdown", "bindLabel", "bindValue", "ariaLabel", "markFirst", "placeholder", "fixedPlaceholder", "notFoundText", "typeToSearchText", "preventToggleOnRightClick", "addTagText", "loadingText", "clearAllText", "appearance", "dropdownPosition", "appendTo", "loading", "closeOnSelect", "hideSelected", "selectOnTab", "openOnEnter", "maxSelectedItems", "groupBy", "groupValue", "bufferAmount", "virtualScroll", "selectableGroup", "selectableGroupAsModel", "searchFn", "trackByFn", "clearOnBackspace", "labelForId", "inputAttrs", "tabIndex", "tabFocusOnClearButton", "readonly", "searchWhileComposing", "minTermLength", "editableSearchTerm", "ngClass", "typeahead", "multiple", "addTag", "searchable", "clearable", "isOpen", "items", "compareWith", "clearSearchOnAdd", "deselectOnClick", "keyDownFn"], outputs: ["blur", "focus", "change", "open", "close", "search", "clear", "add", "remove", "scroll", "scrollToEnd"] }, { kind: "directive", type: i6.NgOptionTemplateDirective, selector: "[ng-option-tmp]" }, { kind: "component", type: MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }] }); }
|
|
1715
1752
|
}
|
|
1716
1753
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AudioEditorComponent, decorators: [{
|
|
1717
1754
|
type: Component,
|
|
@@ -1726,8 +1763,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImpo
|
|
|
1726
1763
|
UploadFilesBtnComponent,
|
|
1727
1764
|
NgSelectModule,
|
|
1728
1765
|
MatIconButton
|
|
1729
|
-
], template: "<div class=\"audio-editor\">\n @if (hasAudioError) {\n <div class=\"error-block\">\n <mat-icon>error</mat-icon>\n <span>\u0410\u0443\u0434\u0438\u043E-\u0444\u0430\u0439\u043B \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D \u043F\u043E \u0443\u043A\u0430\u0437\u0430\u043D\u043D\u043E\u043C\u0443 \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440\u0443, \u0432\u043E\u0437\u043C\u043E\u0436\u043D\u043E \u0444\u0430\u0439\u043B \u043D\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442 \u0438\u043B\u0438 \u043E\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u043D\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435.</span>\n </div>\n }\n\n @if (hasFileId() && !hasAudioError) {\n <div class=\"audio-player-container\">\n <audio\n #audioPlayer\n [src]=\"getSrc()\"\n (error)=\"onErrorHandler()\"\n (loadedmetadata)=\"onLoadedMetadata()\"\n (timeupdate)=\"onTimeUpdate()\"\n (ended)=\"onEnded()\"\n (canplay)=\"onCanPlay()\"\n preload=\"metadata\">\n </audio>\n\n @if (isLoading) {\n <div class=\"loading-indicator\">\n <div class=\"progress-bar\">\n <div class=\"progress-bar-fill\"></div>\n </div>\n <span>\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430 \u0430\u0443\u0434\u0438\u043E...</span>\n </div>\n }\n\n <div class=\"player-controls\">\n <div class=\"control-buttons\">\n <button (click)=\"restart()\" [disabled]=\"!hasFileId()\" mat-icon-button>\n <mat-icon>replay</mat-icon>\n </button>\n\n <button (click)=\"rewind()\" [disabled]=\"!hasFileId()\" mat-icon-button>\n <mat-icon>replay_10</mat-icon>\n </button>\n\n <button\n (click)=\"togglePlayPause()\"\n [disabled]=\"!hasFileId() || isLoading\"\n class=\"play-pause-btn\" mat-icon-button>\n <mat-icon>{{ isPlaying ? 'pause' : 'play_arrow' }}</mat-icon>\n </button>\n\n <button (click)=\"forward()\" [disabled]=\"!hasFileId()\" mat-icon-button>\n <mat-icon>forward_10</mat-icon>\n </button>\n\n @if (!presentOrEdit) {\n <button (click)=\"removeAudio()\" [disabled]=\"!hasFileId()\" color=\"warn\" mat-icon-button>\n <mat-icon>delete</mat-icon>\n </button>\n }\n </div>\n\n <div class=\"progress-section\">\n <span class=\"time-display\">{{ formatTime(currentTime) }}</span>\n\n <input\n type=\"range\"\n min=\"0\"\n max=\"100\"\n [value]=\"getProgress()\"\n (input)=\"onSeekChange($event)\"\n [disabled]=\"!hasFileId() || duration === 0\"\n class=\"progress-slider\">\n\n <span class=\"time-display\">{{ formatTime(duration) }}</span>\n </div>\n\n <div class=\"volume-controls\">\n <button (click)=\"toggleMute()\" [disabled]=\"!hasFileId()\" mat-icon-button>\n <mat-icon>{{ isMuted ? 'volume_off' : 'volume_up' }}</mat-icon>\n </button>\n\n <input\n type=\"range\"\n min=\"0\"\n max=\"100\"\n [value]=\"volume * 100\"\n (input)=\"onVolumeChange($event)\"\n [disabled]=\"!hasFileId()\"\n class=\"volume-slider\">\n </div>\n </div>\n </div>\n }\n\n @if (!hasFileId()) {\n <div class=\"no-file-message\">\n <mat-icon>music_off</mat-icon>\n <h5>\u0410\u0443\u0434\u0438\u043E-\u0444\u0430\u0439\u043B \u043D\u0435 \u0432\u044B\u0431\u0440\u0430\u043D</h5>\n @if (!presentOrEdit) {\n <p>\u041D\u0430\u0436\u043C\u0438\u0442\u0435 \u043A\u043D\u043E\u043F\u043A\u0443 \u0440\u0435\u0434\u0430\u043A\u0442\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u044F \u0434\u043B\u044F \u0432\u044B\u0431\u043E\u0440\u0430 \u0438\u043B\u0438 \u0437\u0430\u0433\u0440\u0443\u0437\u043A\u0438 \u0430\u0443\u0434\u0438\u043E-\u0444\u0430\u0439\u043B\u0430</p>\n }\n </div>\n }\n @if (!presentOrEdit) {\n <div class=\"edit-panel\">\n <div class=\"edit-header\">\n <h6>\u041D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438 \u0430\u0443\u0434\u0438\u043E-\u0444\u0430\u0439\u043B\u0430</h6>\n </div>\n\n <div class=\"mode-toggle\">\n <mat-button-toggle-group [(ngModel)]=\"searchOrEdit\">\n <mat-button-toggle value=\"search\">\n <mat-icon>search</mat-icon>\n \u0418\u0441\u043A\u0430\u0442\u044C \u0444\u0430\u0439\u043B\n </mat-button-toggle>\n <mat-button-toggle value=\"edit\">\n <mat-icon>settings</mat-icon>\n \u0423\u043A\u0430\u0437\u0430\u0442\u044C \u043F\u043E ID\n </mat-button-toggle>\n </mat-button-toggle-group>\n </div>\n\n @if (searchOrEdit === 'search') {\n <div class=\"file-selection\">\n <div class=\"existing-files\">\n <h6>\u0412\u044B\u0431\u0440\u0430\u0442\u044C \u0438\u0437 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044E\u0449\u0438\u0445 \u0444\u0430\u0439\u043B\u043E\u0432:</h6>\n <ng-select\n [(ngModel)]=\"fileId\"\n (ngModelChange)=\"onFileSelected($event)\"\n [items]=\"files\"\n bindLabel=\"fileName\"\n bindValue=\"fileId\"\n (search)=\"onSearchChanged($event)\"\n [loading]=\"loading\"\n [loadingText]=\"'\u0417\u0430\u0433\u0440\u0443\u0436\u0430\u044E \u0444\u0430\u0439\u043B\u044B'\"\n placeholder=\"\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0430\u0443\u0434\u0438\u043E-\u0444\u0430\u0439\u043B\"\n [clearable]=\"true\">\n <ng-template ng-option-tmp let-item=\"item\">\n <div class=\"file-option\">\n <mat-icon>audiotrack</mat-icon>\n <span>{{ item.fileName }}</span>\n <small>ID: {{ item.fileId }}</small>\n </div>\n </ng-template>\n </ng-select>\n </div>\n\n <div class=\"upload-section\">\n <h6>\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C \u043D\u043E\u0432\u044B\u0439 \u0444\u0430\u0439\u043B:</h6>\n <croco-app-upload-files-btn\n extAccepts=\"audio/*\"\n [isMultiple]=\"false\"\n btnText=\"\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C \u0430\u0443\u0434\u0438\u043E-\u0444\u0430\u0439\u043B\"\n (onPublicFilesUploaded)=\"onFilesUploaded($event.fileIds)\"\n (onPrivateFilesUploaded)=\"onFilesUploaded($event.fileIds)\">\n </croco-app-upload-files-btn>\n </div>\n </div>\n } @else if (searchOrEdit === 'edit') {\n <div class=\"manual-input\">\n <mat-form-field class=\"w-100\">\n <mat-label>\u0418\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440 \u0444\u0430\u0439\u043B\u0430</mat-label>\n <input\n matInput\n placeholder=\"\u0412\u0432\u0435\u0434\u0438\u0442\u0435 ID \u0430\u0443\u0434\u0438\u043E-\u0444\u0430\u0439\u043B\u0430\"\n autocomplete=\"off\"\n [(ngModel)]=\"fileId\"\n (ngModelChange)=\"onFileIdChanged($event)\">\n </mat-form-field>\n\n <mat-form-field class=\"w-100\">\n <mat-label>\u0418\u043C\u044F \u0444\u0430\u0439\u043B\u0430</mat-label>\n <input\n matInput\n placeholder=\"\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0438\u043C\u044F \u0444\u0430\u0439\u043B\u0430\"\n autocomplete=\"off\"\n [(ngModel)]=\"fileNameAttr\"\n (ngModelChange)=\"onFileNameChanged($event)\">\n </mat-form-field>\n </div>\n }\n </div>\n }\n</div>\n", styles: [".audio-editor{width:100%;padding:16px;background:#fafafa;border-radius:8px;border:1px solid #e0e0e0}.error-block{display:flex;align-items:center;gap:8px;background-color:#ffebee;color:#c62828;padding:12px;border-radius:8px;border:1px solid #ffcdd2;margin-bottom:16px}.error-block mat-icon{color:#c62828}.audio-player-container{background:#fff;border-radius:12px;padding:20px;box-shadow:0 2px 8px #0000001a;margin-bottom:16px}.loading-indicator{display:flex;flex-direction:column;align-items:center;gap:8px;padding:16px;color:#666}.loading-indicator .progress-bar{width:200px;height:4px;background-color:#e0e0e0;border-radius:2px;overflow:hidden}.loading-indicator .progress-bar .progress-bar-fill{height:100%;background-color:#1976d2;animation:loading 1.5s ease-in-out infinite}@keyframes loading{0%{transform:translate(-100%)}50%{transform:translate(0)}to{transform:translate(100%)}}.file-info{display:flex;align-items:center;gap:8px;margin-bottom:16px;padding:12px;background:#f5f5f5;border-radius:8px}.file-info mat-icon{color:#1976d2}.file-info .file-name{font-weight:500;flex:1}.file-info .edit-btn{margin-left:auto}.player-controls{display:flex;flex-direction:column;gap:16px}.control-buttons{display:flex;justify-content:center;align-items:center;gap:8px}.control-buttons .play-pause-btn{background-color:#1976d2;color:#fff;width:48px;height:48px}.control-buttons .play-pause-btn:hover{background-color:#1565c0}.control-buttons .play-pause-btn mat-icon{font-size:24px}.control-buttons mat-icon-button{width:40px;height:40px}.control-buttons mat-icon-button:disabled{color:#ccc}.progress-section{display:flex;align-items:center;gap:12px}.progress-section .time-display{font-family:Roboto Mono,monospace;font-size:14px;color:#666;min-width:40px;text-align:center}.progress-section .progress-slider{flex:1;height:6px;-webkit-appearance:none;appearance:none;background:#e0e0e0;outline:none;border-radius:3px}.progress-section .progress-slider::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:20px;height:20px;background:#1976d2;border-radius:50%;cursor:pointer;border:2px solid white;box-shadow:0 2px 4px #0003}.progress-section .progress-slider::-moz-range-thumb{width:20px;height:20px;background:#1976d2;border-radius:50%;cursor:pointer;border:2px solid white;box-shadow:0 2px 4px #0003}.progress-section .progress-slider:hover::-webkit-slider-thumb{background:#1565c0}.progress-section .progress-slider:hover::-moz-range-thumb{background:#1565c0}.volume-controls{display:flex;align-items:center;gap:8px;justify-content:center}.volume-controls .volume-slider{width:100px;height:6px;-webkit-appearance:none;appearance:none;background:#e0e0e0;outline:none;border-radius:3px}.volume-controls .volume-slider::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:20px;height:20px;background:#1976d2;border-radius:50%;cursor:pointer;border:2px solid white;box-shadow:0 2px 4px #0003}.volume-controls .volume-slider::-moz-range-thumb{width:20px;height:20px;background:#1976d2;border-radius:50%;cursor:pointer;border:2px solid white;box-shadow:0 2px 4px #0003}.volume-controls .volume-slider:hover::-webkit-slider-thumb{background:#1565c0}.volume-controls .volume-slider:hover::-moz-range-thumb{background:#1565c0}.no-file-message{text-align:center;padding:40px 20px;color:#666}.no-file-message mat-icon{font-size:48px;width:48px;height:48px;margin-bottom:16px;color:#ccc}.no-file-message h5{margin:0 0 8px;color:#999}.no-file-message p{margin:0;font-size:14px;color:#999}.edit-panel{background:#fff;border-radius:8px;padding:20px;border:1px solid #e0e0e0;margin-top:16px}.edit-panel .edit-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:20px}.edit-panel .edit-header h6{margin:0;color:#333}.edit-panel .mode-toggle{margin-bottom:20px}.edit-panel .mode-toggle mat-button-toggle-group{width:100%}.edit-panel .mode-toggle mat-button-toggle{flex:1;display:flex;align-items:center;justify-content:center;gap:8px}.edit-panel .file-selection{display:flex;flex-direction:column;gap:20px}.edit-panel .file-selection h6{margin:0 0 12px;color:#333}.edit-panel .file-selection .file-option{display:flex;align-items:center;gap:8px}.edit-panel .file-selection .file-option mat-icon{color:#1976d2}.edit-panel .file-selection .file-option small{margin-left:auto;color:#666}.edit-panel .file-selection .upload-section{padding-top:16px;border-top:1px solid #e0e0e0}.edit-panel .manual-input{display:flex;flex-direction:column;gap:16px}.edit-button-container{display:flex;justify-content:center;margin-top:16px}.edit-button-container .edit-mode-btn{background-color:#1976d2;color:#fff}.edit-button-container .edit-mode-btn:hover{background-color:#1565c0}@media screen and (max-width: 768px){.audio-editor{padding:12px}.audio-player-container{padding:16px}.control-buttons{flex-wrap:wrap;gap:4px}.progress-section{flex-direction:column;gap:8px}.progress-section .progress-slider{width:100%}.volume-controls{justify-content:flex-start}.volume-controls .volume-slider{width:80px}.edit-panel{padding:16px}.edit-panel .mode-toggle mat-button-toggle-group{flex-direction:column}}::ng-deep .ng-select .ng-select-container{border-radius:4px;border:1px solid #ccc}::ng-deep .ng-select.ng-select-focused .ng-select-container{border-color:#1976d2}input[type=range].progress-slider:disabled,input[type=range].volume-slider:disabled{opacity:.5;cursor:not-allowed}\n"] }]
|
|
1730
|
-
}], ctorParameters: () => [{ type: PublicFilesQueryService }, { type: PrivateFilesQueryService }, { type: CrocoHtmlFileOptionsService }], propDecorators: { audioPlayer: [{
|
|
1766
|
+
], template: "<div class=\"audio-editor\">\n @if (hasAudioError) {\n <div class=\"error-block\">\n <mat-icon>error</mat-icon>\n <span>{{ errorMessage }}</span>\n </div>\n }\n\n @if (hasFileId() && !hasAudioError) {\n <div class=\"audio-player-container\">\n <audio\n #audioPlayer\n [src]=\"audioSrc\"\n (error)=\"onErrorHandler()\"\n (loadedmetadata)=\"onLoadedMetadata()\"\n (timeupdate)=\"onTimeUpdate()\"\n (ended)=\"onEnded()\"\n (canplay)=\"onCanPlay()\"\n preload=\"metadata\">\n </audio>\n\n @if (isLoading) {\n <div class=\"loading-indicator\">\n <div class=\"progress-bar\">\n <div class=\"progress-bar-fill\"></div>\n </div>\n <span>\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430 \u0430\u0443\u0434\u0438\u043E...</span>\n </div>\n }\n\n <div class=\"player-controls\">\n <div class=\"control-buttons\">\n <button (click)=\"restart()\" [disabled]=\"!hasFileId()\" mat-icon-button>\n <mat-icon>replay</mat-icon>\n </button>\n\n <button (click)=\"rewind()\" [disabled]=\"!hasFileId()\" mat-icon-button>\n <mat-icon>replay_10</mat-icon>\n </button>\n\n <button\n (click)=\"togglePlayPause()\"\n [disabled]=\"!hasFileId() || isLoading\"\n class=\"play-pause-btn\" mat-icon-button>\n <mat-icon>{{ isPlaying ? 'pause' : 'play_arrow' }}</mat-icon>\n </button>\n\n <button (click)=\"forward()\" [disabled]=\"!hasFileId()\" mat-icon-button>\n <mat-icon>forward_10</mat-icon>\n </button>\n\n @if (!presentOrEdit) {\n <button (click)=\"removeAudio()\" [disabled]=\"!hasFileId()\" color=\"warn\" mat-icon-button>\n <mat-icon>delete</mat-icon>\n </button>\n }\n </div>\n\n <div class=\"progress-section\">\n <span class=\"time-display\">{{ formatTime(currentTime) }}</span>\n\n <input\n type=\"range\"\n min=\"0\"\n max=\"100\"\n [value]=\"getProgress()\"\n (input)=\"onSeekChange($event)\"\n [disabled]=\"!hasFileId() || duration === 0\"\n class=\"progress-slider\">\n\n <span class=\"time-display\">{{ formatTime(duration) }}</span>\n </div>\n\n <div class=\"volume-controls\">\n <button (click)=\"toggleMute()\" [disabled]=\"!hasFileId()\" mat-icon-button>\n <mat-icon>{{ isMuted ? 'volume_off' : 'volume_up' }}</mat-icon>\n </button>\n\n <input\n type=\"range\"\n min=\"0\"\n max=\"100\"\n [value]=\"volume * 100\"\n (input)=\"onVolumeChange($event)\"\n [disabled]=\"!hasFileId()\"\n class=\"volume-slider\">\n </div>\n </div>\n </div>\n }\n\n @if (!hasFileId()) {\n <div class=\"no-file-message\">\n <mat-icon>music_off</mat-icon>\n <h5>\u0410\u0443\u0434\u0438\u043E-\u0444\u0430\u0439\u043B \u043D\u0435 \u0432\u044B\u0431\u0440\u0430\u043D</h5>\n @if (!presentOrEdit) {\n <p>\u041D\u0430\u0436\u043C\u0438\u0442\u0435 \u043A\u043D\u043E\u043F\u043A\u0443 \u0440\u0435\u0434\u0430\u043A\u0442\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u044F \u0434\u043B\u044F \u0432\u044B\u0431\u043E\u0440\u0430 \u0438\u043B\u0438 \u0437\u0430\u0433\u0440\u0443\u0437\u043A\u0438 \u0430\u0443\u0434\u0438\u043E-\u0444\u0430\u0439\u043B\u0430</p>\n }\n </div>\n }\n @if (!presentOrEdit) {\n <div class=\"edit-panel\">\n <div class=\"edit-header\">\n <h6>\u041D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438 \u0430\u0443\u0434\u0438\u043E-\u0444\u0430\u0439\u043B\u0430</h6>\n </div>\n\n <div class=\"mode-toggle\">\n <mat-button-toggle-group [(ngModel)]=\"searchOrEdit\">\n <mat-button-toggle value=\"search\">\n <mat-icon>search</mat-icon>\n \u0418\u0441\u043A\u0430\u0442\u044C \u0444\u0430\u0439\u043B\n </mat-button-toggle>\n <mat-button-toggle value=\"edit\">\n <mat-icon>settings</mat-icon>\n \u0423\u043A\u0430\u0437\u0430\u0442\u044C \u043F\u043E ID\n </mat-button-toggle>\n </mat-button-toggle-group>\n </div>\n\n @if (searchOrEdit === 'search') {\n <div class=\"file-selection\">\n <div class=\"existing-files\">\n <h6>\u0412\u044B\u0431\u0440\u0430\u0442\u044C \u0438\u0437 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044E\u0449\u0438\u0445 \u0444\u0430\u0439\u043B\u043E\u0432:</h6>\n <ng-select\n [(ngModel)]=\"fileId\"\n (ngModelChange)=\"onFileSelected($event)\"\n [items]=\"files\"\n bindLabel=\"fileName\"\n bindValue=\"fileId\"\n (search)=\"onSearchChanged($event)\"\n [loading]=\"loading\"\n [loadingText]=\"'\u0417\u0430\u0433\u0440\u0443\u0436\u0430\u044E \u0444\u0430\u0439\u043B\u044B'\"\n placeholder=\"\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0430\u0443\u0434\u0438\u043E-\u0444\u0430\u0439\u043B\"\n [clearable]=\"true\">\n <ng-template ng-option-tmp let-item=\"item\">\n <div class=\"file-option\">\n <mat-icon>audiotrack</mat-icon>\n <span>{{ item.fileName }}</span>\n <small>ID: {{ item.fileId }}</small>\n </div>\n </ng-template>\n </ng-select>\n </div>\n\n <div class=\"upload-section\">\n <h6>\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C \u043D\u043E\u0432\u044B\u0439 \u0444\u0430\u0439\u043B:</h6>\n <croco-app-upload-files-btn\n extAccepts=\"audio/*\"\n [isMultiple]=\"false\"\n btnText=\"\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C \u0430\u0443\u0434\u0438\u043E-\u0444\u0430\u0439\u043B\"\n (onPublicFilesUploaded)=\"onFilesUploaded($event.fileIds)\"\n (onPrivateFilesUploaded)=\"onFilesUploaded($event.fileIds)\">\n </croco-app-upload-files-btn>\n </div>\n </div>\n } @else if (searchOrEdit === 'edit') {\n <div class=\"manual-input\">\n <mat-form-field class=\"w-100\">\n <mat-label>\u0418\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440 \u0444\u0430\u0439\u043B\u0430</mat-label>\n <input\n matInput\n placeholder=\"\u0412\u0432\u0435\u0434\u0438\u0442\u0435 ID \u0430\u0443\u0434\u0438\u043E-\u0444\u0430\u0439\u043B\u0430\"\n autocomplete=\"off\"\n [(ngModel)]=\"fileId\"\n (ngModelChange)=\"onFileIdChanged($event)\">\n </mat-form-field>\n </div>\n }\n </div>\n }\n</div>\n", styles: [".audio-editor{width:100%;padding:16px;background:#fafafa;border-radius:8px;border:1px solid #e0e0e0}.error-block{display:flex;align-items:center;gap:8px;background-color:#ffebee;color:#c62828;padding:12px;border-radius:8px;border:1px solid #ffcdd2;margin-bottom:16px}.error-block mat-icon{color:#c62828}.audio-player-container{background:#fff;border-radius:12px;padding:20px;box-shadow:0 2px 8px #0000001a;margin-bottom:16px}.loading-indicator{display:flex;flex-direction:column;align-items:center;gap:8px;padding:16px;color:#666}.loading-indicator .progress-bar{width:200px;height:4px;background-color:#e0e0e0;border-radius:2px;overflow:hidden}.loading-indicator .progress-bar .progress-bar-fill{height:100%;background-color:#1976d2;animation:loading 1.5s ease-in-out infinite}@keyframes loading{0%{transform:translate(-100%)}50%{transform:translate(0)}to{transform:translate(100%)}}.file-info{display:flex;align-items:center;gap:8px;margin-bottom:16px;padding:12px;background:#f5f5f5;border-radius:8px}.file-info mat-icon{color:#1976d2}.file-info .file-name{font-weight:500;flex:1}.file-info .edit-btn{margin-left:auto}.player-controls{display:flex;flex-direction:column;gap:16px}.control-buttons{display:flex;justify-content:center;align-items:center;gap:8px}.control-buttons .play-pause-btn{background-color:#1976d2;color:#fff;width:48px;height:48px}.control-buttons .play-pause-btn:hover{background-color:#1565c0}.control-buttons .play-pause-btn mat-icon{font-size:24px}.control-buttons mat-icon-button{width:40px;height:40px}.control-buttons mat-icon-button:disabled{color:#ccc}.progress-section{display:flex;align-items:center;gap:12px}.progress-section .time-display{font-family:Roboto Mono,monospace;font-size:14px;color:#666;min-width:40px;text-align:center}.progress-section .progress-slider{flex:1;height:6px;-webkit-appearance:none;appearance:none;background:#e0e0e0;outline:none;border-radius:3px}.progress-section .progress-slider::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:20px;height:20px;background:#1976d2;border-radius:50%;cursor:pointer;border:2px solid white;box-shadow:0 2px 4px #0003}.progress-section .progress-slider::-moz-range-thumb{width:20px;height:20px;background:#1976d2;border-radius:50%;cursor:pointer;border:2px solid white;box-shadow:0 2px 4px #0003}.progress-section .progress-slider:hover::-webkit-slider-thumb{background:#1565c0}.progress-section .progress-slider:hover::-moz-range-thumb{background:#1565c0}.volume-controls{display:flex;align-items:center;gap:8px;justify-content:center}.volume-controls .volume-slider{width:100px;height:6px;-webkit-appearance:none;appearance:none;background:#e0e0e0;outline:none;border-radius:3px}.volume-controls .volume-slider::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:20px;height:20px;background:#1976d2;border-radius:50%;cursor:pointer;border:2px solid white;box-shadow:0 2px 4px #0003}.volume-controls .volume-slider::-moz-range-thumb{width:20px;height:20px;background:#1976d2;border-radius:50%;cursor:pointer;border:2px solid white;box-shadow:0 2px 4px #0003}.volume-controls .volume-slider:hover::-webkit-slider-thumb{background:#1565c0}.volume-controls .volume-slider:hover::-moz-range-thumb{background:#1565c0}.no-file-message{text-align:center;padding:40px 20px;color:#666}.no-file-message mat-icon{font-size:48px;width:48px;height:48px;margin-bottom:16px;color:#ccc}.no-file-message h5{margin:0 0 8px;color:#999}.no-file-message p{margin:0;font-size:14px;color:#999}.edit-panel{background:#fff;border-radius:8px;padding:20px;border:1px solid #e0e0e0;margin-top:16px}.edit-panel .edit-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:20px}.edit-panel .edit-header h6{margin:0;color:#333}.edit-panel .mode-toggle{margin-bottom:20px}.edit-panel .mode-toggle mat-button-toggle-group{width:100%}.edit-panel .mode-toggle mat-button-toggle{flex:1;display:flex;align-items:center;justify-content:center;gap:8px}.edit-panel .file-selection{display:flex;flex-direction:column;gap:20px}.edit-panel .file-selection h6{margin:0 0 12px;color:#333}.edit-panel .file-selection .file-option{display:flex;align-items:center;gap:8px}.edit-panel .file-selection .file-option mat-icon{color:#1976d2}.edit-panel .file-selection .file-option small{margin-left:auto;color:#666}.edit-panel .file-selection .upload-section{padding-top:16px;border-top:1px solid #e0e0e0}.edit-panel .manual-input{display:flex;flex-direction:column;gap:16px}.edit-button-container{display:flex;justify-content:center;margin-top:16px}.edit-button-container .edit-mode-btn{background-color:#1976d2;color:#fff}.edit-button-container .edit-mode-btn:hover{background-color:#1565c0}@media screen and (max-width: 768px){.audio-editor{padding:12px}.audio-player-container{padding:16px}.control-buttons{flex-wrap:wrap;gap:4px}.progress-section{flex-direction:column;gap:8px}.progress-section .progress-slider{width:100%}.volume-controls{justify-content:flex-start}.volume-controls .volume-slider{width:80px}.edit-panel{padding:16px}.edit-panel .mode-toggle mat-button-toggle-group{flex-direction:column}}::ng-deep .ng-select .ng-select-container{border-radius:4px;border:1px solid #ccc}::ng-deep .ng-select.ng-select-focused .ng-select-container{border-color:#1976d2}input[type=range].progress-slider:disabled,input[type=range].volume-slider:disabled{opacity:.5;cursor:not-allowed}\n"] }]
|
|
1767
|
+
}], ctorParameters: () => [{ type: PublicFilesQueryService }, { type: PrivateFilesQueryService }, { type: CrocoHtmlFileOptionsService }, { type: CommonFileInfoQueryService }], propDecorators: { audioPlayer: [{
|
|
1731
1768
|
type: ViewChild,
|
|
1732
1769
|
args: ['audioPlayer', { static: false }]
|
|
1733
1770
|
}], tag: [{
|
|
@@ -1741,29 +1778,23 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImpo
|
|
|
1741
1778
|
const CrocoHtmlOptionsToken = "croco-html-options";
|
|
1742
1779
|
|
|
1743
1780
|
class AudioPlayerTagViewComponent {
|
|
1744
|
-
constructor(_options) {
|
|
1781
|
+
constructor(_options, _commonFileInfoService) {
|
|
1745
1782
|
this._options = _options;
|
|
1783
|
+
this._commonFileInfoService = _commonFileInfoService;
|
|
1746
1784
|
this.hasAudioError = false;
|
|
1785
|
+
this.errorMessage = '';
|
|
1747
1786
|
this.isPlaying = false;
|
|
1748
1787
|
this.isLoading = false;
|
|
1749
1788
|
this.currentTime = 0;
|
|
1750
1789
|
this.duration = 0;
|
|
1751
1790
|
this.volume = 1;
|
|
1752
1791
|
this.isMuted = false;
|
|
1792
|
+
this.audioSrc = '';
|
|
1753
1793
|
this.unsubscribe = new Subject();
|
|
1754
1794
|
}
|
|
1755
1795
|
get fileId() {
|
|
1756
1796
|
return this.data.data.fileId;
|
|
1757
1797
|
}
|
|
1758
|
-
get fileName() {
|
|
1759
|
-
return this.data.data.fileName;
|
|
1760
|
-
}
|
|
1761
|
-
getSrc() {
|
|
1762
|
-
//this._options.fileIdAndNamePathUrlFormat;
|
|
1763
|
-
// TODO если fileId указан то нужно
|
|
1764
|
-
// return AudioMethods.buildUrl(this.fileId, this.fileName);
|
|
1765
|
-
return this.fileId;
|
|
1766
|
-
}
|
|
1767
1798
|
get audioElement() {
|
|
1768
1799
|
return this.audioPlayer?.nativeElement || null;
|
|
1769
1800
|
}
|
|
@@ -1773,6 +1804,14 @@ class AudioPlayerTagViewComponent {
|
|
|
1773
1804
|
onErrorHandler() {
|
|
1774
1805
|
this.hasAudioError = true;
|
|
1775
1806
|
this.isLoading = false;
|
|
1807
|
+
if (!this.errorMessage) {
|
|
1808
|
+
this.errorMessage = 'Аудио-файл не найден по указанному идентификатору.';
|
|
1809
|
+
}
|
|
1810
|
+
}
|
|
1811
|
+
setError(message) {
|
|
1812
|
+
this.hasAudioError = true;
|
|
1813
|
+
this.errorMessage = message;
|
|
1814
|
+
this.isLoading = false;
|
|
1776
1815
|
}
|
|
1777
1816
|
onLoadedMetadata() {
|
|
1778
1817
|
const audio = this.audioElement;
|
|
@@ -1872,30 +1911,75 @@ class AudioPlayerTagViewComponent {
|
|
|
1872
1911
|
const audio = this.audioElement;
|
|
1873
1912
|
if (audio) {
|
|
1874
1913
|
audio.volume = this.volume;
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1914
|
+
}
|
|
1915
|
+
}
|
|
1916
|
+
if (this.hasFileId()) {
|
|
1917
|
+
this.checkAndLoadAudioFile();
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
1920
|
+
checkAndLoadAudioFile() {
|
|
1921
|
+
if (!this.hasFileId()) {
|
|
1922
|
+
return;
|
|
1923
|
+
}
|
|
1924
|
+
this.isLoading = true;
|
|
1925
|
+
this.hasAudioError = false;
|
|
1926
|
+
this.errorMessage = '';
|
|
1927
|
+
const fileIdValue = this.fileId;
|
|
1928
|
+
this._commonFileInfoService.getInfo(fileIdValue)
|
|
1929
|
+
.pipe(takeUntil(this.unsubscribe))
|
|
1930
|
+
.subscribe({
|
|
1931
|
+
next: (result) => {
|
|
1932
|
+
if (!result.fileInfo) {
|
|
1933
|
+
this.setError(`Файл с идентификатором ${fileIdValue} не найден на сервере.`);
|
|
1934
|
+
return;
|
|
1935
|
+
}
|
|
1936
|
+
if (result.fileInfo.type !== FileType.Audio) {
|
|
1937
|
+
this.setError(`Файл с идентификатором ${fileIdValue} должен быть c типом Audio, а получено ${result.fileInfo.type}.`);
|
|
1938
|
+
return;
|
|
1878
1939
|
}
|
|
1940
|
+
this.audioSrc = result.fileInfo.downloadUrl;
|
|
1941
|
+
this.loadAudioFile();
|
|
1942
|
+
},
|
|
1943
|
+
error: (error) => {
|
|
1944
|
+
this.setError(`Ошибка при получении информации о файле: ${error.message || 'Неизвестная ошибка'}`);
|
|
1879
1945
|
}
|
|
1946
|
+
});
|
|
1947
|
+
}
|
|
1948
|
+
loadAudioFile() {
|
|
1949
|
+
if (!this.hasFileId() || !this.audioSrc) {
|
|
1950
|
+
return;
|
|
1951
|
+
}
|
|
1952
|
+
this.isLoading = true;
|
|
1953
|
+
this.hasAudioError = false;
|
|
1954
|
+
const audio = this.audioElement;
|
|
1955
|
+
if (audio) {
|
|
1956
|
+
audio.load();
|
|
1880
1957
|
}
|
|
1881
1958
|
}
|
|
1882
1959
|
ngOnDestroy() {
|
|
1883
1960
|
this.unsubscribe.next();
|
|
1884
1961
|
this.unsubscribe.complete();
|
|
1885
1962
|
}
|
|
1886
|
-
|
|
1887
|
-
|
|
1963
|
+
ngOnChanges(changes) {
|
|
1964
|
+
if (changes['data'] && !changes['data'].firstChange) {
|
|
1965
|
+
if (this.hasFileId()) {
|
|
1966
|
+
this.checkAndLoadAudioFile();
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1970
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AudioPlayerTagViewComponent, deps: [{ token: CrocoHtmlOptionsToken }, { token: CommonFileInfoQueryService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1971
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: AudioPlayerTagViewComponent, isStandalone: true, selector: "croco-html-audio-player-tag-view", inputs: { data: "data" }, viewQueries: [{ propertyName: "audioPlayer", first: true, predicate: ["audioPlayer"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"audio-view\">\n @if (hasAudioError) {\n <div class=\"error-block\">\n <mat-icon>error</mat-icon>\n <span>{{ errorMessage }}</span>\n </div>\n }\n\n @if (hasFileId() && !hasAudioError) {\n <div class=\"audio-player-container\">\n <audio\n #audioPlayer\n [src]=\"audioSrc\"\n (error)=\"onErrorHandler()\"\n (loadedmetadata)=\"onLoadedMetadata()\"\n (timeupdate)=\"onTimeUpdate()\"\n (ended)=\"onEnded()\"\n (canplay)=\"onCanPlay()\"\n preload=\"metadata\">\n </audio>\n\n @if (isLoading) {\n <div class=\"loading-indicator\">\n <div class=\"progress-bar\">\n <div class=\"progress-bar-fill\"></div>\n </div>\n <span>\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430 \u0430\u0443\u0434\u0438\u043E...</span>\n </div>\n }\n\n <div class=\"file-info\">\n <mat-icon>audiotrack</mat-icon>\n <span class=\"file-name\">\u0410\u0443\u0434\u0438\u043E-\u0444\u0430\u0439\u043B</span>\n </div>\n\n <div class=\"player-controls\">\n <div class=\"control-buttons\">\n <button (click)=\"restart()\" [disabled]=\"!hasFileId()\" mat-icon-button>\n <mat-icon>replay</mat-icon>\n </button>\n\n <button (click)=\"rewind()\" [disabled]=\"!hasFileId()\" mat-icon-button>\n <mat-icon>replay_10</mat-icon>\n </button>\n\n <button\n (click)=\"togglePlayPause()\"\n [disabled]=\"!hasFileId() || isLoading\"\n class=\"play-pause-btn\" mat-icon-button>\n <mat-icon>{{ isPlaying ? 'pause' : 'play_arrow' }}</mat-icon>\n </button>\n\n <button (click)=\"forward()\" [disabled]=\"!hasFileId()\" mat-icon-button>\n <mat-icon>forward_10</mat-icon>\n </button>\n </div>\n\n <div class=\"progress-section\">\n <span class=\"time-display\">{{ formatTime(currentTime) }}</span>\n\n <input\n type=\"range\"\n min=\"0\"\n max=\"100\"\n [value]=\"getProgress()\"\n (input)=\"onSeekChange($event)\"\n [disabled]=\"!hasFileId() || duration === 0\"\n class=\"progress-slider\">\n\n <span class=\"time-display\">{{ formatTime(duration) }}</span>\n </div>\n\n <div class=\"volume-controls\">\n <button (click)=\"toggleMute()\" [disabled]=\"!hasFileId()\" mat-icon-button>\n <mat-icon>{{ isMuted ? 'volume_off' : 'volume_up' }}</mat-icon>\n </button>\n\n <input\n type=\"range\"\n min=\"0\"\n max=\"100\"\n [value]=\"volume * 100\"\n (input)=\"onVolumeChange($event)\"\n [disabled]=\"!hasFileId()\"\n class=\"volume-slider\">\n </div>\n </div>\n </div>\n }\n\n <!-- \u0421\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 \u043E\u0431 \u043E\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u0438 \u0444\u0430\u0439\u043B\u0430 -->\n @if (!hasFileId()) {\n <div class=\"no-file-message\">\n <mat-icon>music_off</mat-icon>\n <h5>\u0410\u0443\u0434\u0438\u043E-\u0444\u0430\u0439\u043B \u043D\u0435 \u0432\u044B\u0431\u0440\u0430\u043D</h5>\n </div>\n }\n</div>\n", styles: [".audio-view{width:100%;max-width:500px;margin:0 auto}.error-block{display:flex;align-items:center;gap:8px;background-color:#ffebee;color:#c62828;padding:12px;border-radius:8px;border:1px solid #ffcdd2;margin-bottom:16px}.error-block mat-icon{color:#c62828}.audio-player-container{background:#fff;border-radius:12px;padding:20px;box-shadow:0 2px 8px #0000001a;margin-bottom:16px}.loading-indicator{display:flex;flex-direction:column;align-items:center;gap:8px;padding:16px;color:#666}.loading-indicator .progress-bar{width:200px;height:4px;background-color:#e0e0e0;border-radius:2px;overflow:hidden}.loading-indicator .progress-bar .progress-bar-fill{height:100%;background-color:#1976d2;animation:loading 1.5s ease-in-out infinite}@keyframes loading{0%{transform:translate(-100%)}50%{transform:translate(0)}to{transform:translate(100%)}}.file-info{display:flex;align-items:center;gap:8px;margin-bottom:16px;padding:12px;background:#f5f5f5;border-radius:8px}.file-info mat-icon{color:#1976d2}.file-info .file-name{font-weight:500;flex:1}.player-controls{display:flex;flex-direction:column;gap:16px}.control-buttons{display:flex;justify-content:center;align-items:center;gap:8px}.control-buttons .play-pause-btn{background-color:#1976d2;color:#fff;width:48px;height:48px}.control-buttons .play-pause-btn:hover{background-color:#1565c0}.control-buttons .play-pause-btn mat-icon{font-size:24px}.control-buttons mat-icon-button{width:40px;height:40px}.control-buttons mat-icon-button:disabled{color:#ccc}.progress-section{display:flex;align-items:center;gap:12px}.progress-section .time-display{font-family:Roboto Mono,monospace;font-size:14px;color:#666;min-width:40px;text-align:center}.progress-section .progress-slider{flex:1;height:6px;-webkit-appearance:none;appearance:none;background:#e0e0e0;outline:none;border-radius:3px}.progress-section .progress-slider::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:20px;height:20px;background:#1976d2;border-radius:50%;cursor:pointer;border:2px solid white;box-shadow:0 2px 4px #0003}.progress-section .progress-slider::-moz-range-thumb{width:20px;height:20px;background:#1976d2;border-radius:50%;cursor:pointer;border:2px solid white;box-shadow:0 2px 4px #0003}.progress-section .progress-slider:hover::-webkit-slider-thumb{background:#1565c0}.progress-section .progress-slider:hover::-moz-range-thumb{background:#1565c0}.volume-controls{display:flex;align-items:center;gap:8px;justify-content:center}.volume-controls .volume-slider{width:100px;height:6px;-webkit-appearance:none;appearance:none;background:#e0e0e0;outline:none;border-radius:3px}.volume-controls .volume-slider::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:20px;height:20px;background:#1976d2;border-radius:50%;cursor:pointer;border:2px solid white;box-shadow:0 2px 4px #0003}.volume-controls .volume-slider::-moz-range-thumb{width:20px;height:20px;background:#1976d2;border-radius:50%;cursor:pointer;border:2px solid white;box-shadow:0 2px 4px #0003}.volume-controls .volume-slider:hover::-webkit-slider-thumb{background:#1565c0}.volume-controls .volume-slider:hover::-moz-range-thumb{background:#1565c0}.no-file-message{text-align:center;padding:40px 20px;color:#666}.no-file-message mat-icon{font-size:48px;width:48px;height:48px;margin-bottom:16px;color:#ccc}.no-file-message h5{margin:0;color:#999}@media screen and (max-width: 768px){.audio-view{padding:0 12px}.audio-player-container{padding:16px}.control-buttons{flex-wrap:wrap;gap:4px}.progress-section{flex-direction:column;gap:8px}.progress-section .progress-slider{width:100%}.volume-controls{justify-content:flex-start}.volume-controls .volume-slider{width:80px}}input[type=range].progress-slider:disabled,input[type=range].volume-slider:disabled{opacity:.5;cursor:not-allowed}\n"], dependencies: [{ kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }] }); }
|
|
1888
1972
|
}
|
|
1889
1973
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AudioPlayerTagViewComponent, decorators: [{
|
|
1890
1974
|
type: Component,
|
|
1891
1975
|
args: [{ selector: 'croco-html-audio-player-tag-view', standalone: true, imports: [
|
|
1892
1976
|
MatIcon,
|
|
1893
1977
|
MatIconButton
|
|
1894
|
-
], template: "<div class=\"audio-view\">\n @if (hasAudioError) {\n <div class=\"error-block\">\n <mat-icon>error</mat-icon>\n <span
|
|
1978
|
+
], template: "<div class=\"audio-view\">\n @if (hasAudioError) {\n <div class=\"error-block\">\n <mat-icon>error</mat-icon>\n <span>{{ errorMessage }}</span>\n </div>\n }\n\n @if (hasFileId() && !hasAudioError) {\n <div class=\"audio-player-container\">\n <audio\n #audioPlayer\n [src]=\"audioSrc\"\n (error)=\"onErrorHandler()\"\n (loadedmetadata)=\"onLoadedMetadata()\"\n (timeupdate)=\"onTimeUpdate()\"\n (ended)=\"onEnded()\"\n (canplay)=\"onCanPlay()\"\n preload=\"metadata\">\n </audio>\n\n @if (isLoading) {\n <div class=\"loading-indicator\">\n <div class=\"progress-bar\">\n <div class=\"progress-bar-fill\"></div>\n </div>\n <span>\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430 \u0430\u0443\u0434\u0438\u043E...</span>\n </div>\n }\n\n <div class=\"file-info\">\n <mat-icon>audiotrack</mat-icon>\n <span class=\"file-name\">\u0410\u0443\u0434\u0438\u043E-\u0444\u0430\u0439\u043B</span>\n </div>\n\n <div class=\"player-controls\">\n <div class=\"control-buttons\">\n <button (click)=\"restart()\" [disabled]=\"!hasFileId()\" mat-icon-button>\n <mat-icon>replay</mat-icon>\n </button>\n\n <button (click)=\"rewind()\" [disabled]=\"!hasFileId()\" mat-icon-button>\n <mat-icon>replay_10</mat-icon>\n </button>\n\n <button\n (click)=\"togglePlayPause()\"\n [disabled]=\"!hasFileId() || isLoading\"\n class=\"play-pause-btn\" mat-icon-button>\n <mat-icon>{{ isPlaying ? 'pause' : 'play_arrow' }}</mat-icon>\n </button>\n\n <button (click)=\"forward()\" [disabled]=\"!hasFileId()\" mat-icon-button>\n <mat-icon>forward_10</mat-icon>\n </button>\n </div>\n\n <div class=\"progress-section\">\n <span class=\"time-display\">{{ formatTime(currentTime) }}</span>\n\n <input\n type=\"range\"\n min=\"0\"\n max=\"100\"\n [value]=\"getProgress()\"\n (input)=\"onSeekChange($event)\"\n [disabled]=\"!hasFileId() || duration === 0\"\n class=\"progress-slider\">\n\n <span class=\"time-display\">{{ formatTime(duration) }}</span>\n </div>\n\n <div class=\"volume-controls\">\n <button (click)=\"toggleMute()\" [disabled]=\"!hasFileId()\" mat-icon-button>\n <mat-icon>{{ isMuted ? 'volume_off' : 'volume_up' }}</mat-icon>\n </button>\n\n <input\n type=\"range\"\n min=\"0\"\n max=\"100\"\n [value]=\"volume * 100\"\n (input)=\"onVolumeChange($event)\"\n [disabled]=\"!hasFileId()\"\n class=\"volume-slider\">\n </div>\n </div>\n </div>\n }\n\n <!-- \u0421\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 \u043E\u0431 \u043E\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u0438 \u0444\u0430\u0439\u043B\u0430 -->\n @if (!hasFileId()) {\n <div class=\"no-file-message\">\n <mat-icon>music_off</mat-icon>\n <h5>\u0410\u0443\u0434\u0438\u043E-\u0444\u0430\u0439\u043B \u043D\u0435 \u0432\u044B\u0431\u0440\u0430\u043D</h5>\n </div>\n }\n</div>\n", styles: [".audio-view{width:100%;max-width:500px;margin:0 auto}.error-block{display:flex;align-items:center;gap:8px;background-color:#ffebee;color:#c62828;padding:12px;border-radius:8px;border:1px solid #ffcdd2;margin-bottom:16px}.error-block mat-icon{color:#c62828}.audio-player-container{background:#fff;border-radius:12px;padding:20px;box-shadow:0 2px 8px #0000001a;margin-bottom:16px}.loading-indicator{display:flex;flex-direction:column;align-items:center;gap:8px;padding:16px;color:#666}.loading-indicator .progress-bar{width:200px;height:4px;background-color:#e0e0e0;border-radius:2px;overflow:hidden}.loading-indicator .progress-bar .progress-bar-fill{height:100%;background-color:#1976d2;animation:loading 1.5s ease-in-out infinite}@keyframes loading{0%{transform:translate(-100%)}50%{transform:translate(0)}to{transform:translate(100%)}}.file-info{display:flex;align-items:center;gap:8px;margin-bottom:16px;padding:12px;background:#f5f5f5;border-radius:8px}.file-info mat-icon{color:#1976d2}.file-info .file-name{font-weight:500;flex:1}.player-controls{display:flex;flex-direction:column;gap:16px}.control-buttons{display:flex;justify-content:center;align-items:center;gap:8px}.control-buttons .play-pause-btn{background-color:#1976d2;color:#fff;width:48px;height:48px}.control-buttons .play-pause-btn:hover{background-color:#1565c0}.control-buttons .play-pause-btn mat-icon{font-size:24px}.control-buttons mat-icon-button{width:40px;height:40px}.control-buttons mat-icon-button:disabled{color:#ccc}.progress-section{display:flex;align-items:center;gap:12px}.progress-section .time-display{font-family:Roboto Mono,monospace;font-size:14px;color:#666;min-width:40px;text-align:center}.progress-section .progress-slider{flex:1;height:6px;-webkit-appearance:none;appearance:none;background:#e0e0e0;outline:none;border-radius:3px}.progress-section .progress-slider::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:20px;height:20px;background:#1976d2;border-radius:50%;cursor:pointer;border:2px solid white;box-shadow:0 2px 4px #0003}.progress-section .progress-slider::-moz-range-thumb{width:20px;height:20px;background:#1976d2;border-radius:50%;cursor:pointer;border:2px solid white;box-shadow:0 2px 4px #0003}.progress-section .progress-slider:hover::-webkit-slider-thumb{background:#1565c0}.progress-section .progress-slider:hover::-moz-range-thumb{background:#1565c0}.volume-controls{display:flex;align-items:center;gap:8px;justify-content:center}.volume-controls .volume-slider{width:100px;height:6px;-webkit-appearance:none;appearance:none;background:#e0e0e0;outline:none;border-radius:3px}.volume-controls .volume-slider::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:20px;height:20px;background:#1976d2;border-radius:50%;cursor:pointer;border:2px solid white;box-shadow:0 2px 4px #0003}.volume-controls .volume-slider::-moz-range-thumb{width:20px;height:20px;background:#1976d2;border-radius:50%;cursor:pointer;border:2px solid white;box-shadow:0 2px 4px #0003}.volume-controls .volume-slider:hover::-webkit-slider-thumb{background:#1565c0}.volume-controls .volume-slider:hover::-moz-range-thumb{background:#1565c0}.no-file-message{text-align:center;padding:40px 20px;color:#666}.no-file-message mat-icon{font-size:48px;width:48px;height:48px;margin-bottom:16px;color:#ccc}.no-file-message h5{margin:0;color:#999}@media screen and (max-width: 768px){.audio-view{padding:0 12px}.audio-player-container{padding:16px}.control-buttons{flex-wrap:wrap;gap:4px}.progress-section{flex-direction:column;gap:8px}.progress-section .progress-slider{width:100%}.volume-controls{justify-content:flex-start}.volume-controls .volume-slider{width:80px}}input[type=range].progress-slider:disabled,input[type=range].volume-slider:disabled{opacity:.5;cursor:not-allowed}\n"] }]
|
|
1895
1979
|
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
1896
1980
|
type: Inject,
|
|
1897
1981
|
args: [CrocoHtmlOptionsToken]
|
|
1898
|
-
}] }], propDecorators: { audioPlayer: [{
|
|
1982
|
+
}] }, { type: CommonFileInfoQueryService }], propDecorators: { audioPlayer: [{
|
|
1899
1983
|
type: ViewChild,
|
|
1900
1984
|
args: ['audioPlayer', { static: false }]
|
|
1901
1985
|
}], data: [{
|
|
@@ -2602,7 +2686,7 @@ class FileIdSelectComponent {
|
|
|
2602
2686
|
this.loadFiles();
|
|
2603
2687
|
}
|
|
2604
2688
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FileIdSelectComponent, deps: [{ token: PublicFilesQueryService }, { token: PrivateFilesQueryService }, { token: CrocoHtmlFileOptionsService }, { token: CrocoHtmlOptionsToken }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2605
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: FileIdSelectComponent, isStandalone: true, selector: "croco-html-file-id-select", inputs: { labelText: "labelText", fileId: "fileId" }, outputs: { fileId: "fileId", onFileIdChanged: "onFileIdChanged" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"file-id\">\n <div class=\"form-group\">\n <label for=\"fileName\">{{labelText}}</label>\n <ng-select [(ngModel)]=\"fileId\" (ngModelChange)=\"onModelChanged($event)\" [items]=\"files\" #select\n bindLabel=\"fileName\" (search)=\"onSearchChanged($event)\" [loading]=\"loading\" [loadingText]=\"'\u0417\u0430\u0433\u0440\u0443\u0436\u0430\u044E \u0444\u0430\u0439\u043B\u044B'\"\n dropdownPosition=\"auto\" [bindValue]=\"'fileId'\" labelForId=\"fileName\" placeholder=\"\u0412\u044B\u0431\u0440\u0430\u0442\u044C \u0444\u0430\u0439\u043B\"\n [virtualScroll]=\"true\">\n <ng-template ng-option-tmp let-item=\"item\" let-index=\"index\" let-search=\"searchTerm\">\n <div class=\"file-id__container\">\n <div class=\"file-id__text-container\">\n <div>\n <span class=\"h5\">\u0424\u0430\u0439\u043B: </span>\n <span class=\"h5\"><b>{{item.fileName}}</b></span>\n </div>\n <div>\n <small>\n <span>\n \u0418\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440 \u0444\u0430\u0439\u043B\u0430: <b>{{item.fileId }}</b>\n </span>\n </small>\n </div>\n </div>\n <div class=\"file-id__img\">\n <img class=\"mx-auto d-block img-fluid\" [src]=\"getSrc(item.fileId)\" />\n </div>\n </div>\n\n </ng-template>\n </ng-select>\n </div>\n</div>\n\n<div>\n \u0418\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440 \u0444\u0430\u0439\u043B\u0430: {{fileId}}\n</div>\n", styles: [".file-id__container{display:flex;align-items:center;justify-content:space-between;gap:10px}@media (max-width: 768px){.file-id__container{align-items:unset;flex-direction:column}}.file-id__text-container{display:flex;flex-direction:column}.file-id__text-container b{word-break:break-word;white-space:normal}@media (max-width: 768px){.file-id__text-container b{font-size:14px}}@media (max-width: 768px){.file-id__text-container span{font-size:14px}}.file-id__img img{width:100px;height:100px}\n"], dependencies: [{ kind: "ngmodule", type: NgSelectModule }, { kind: "component", type:
|
|
2689
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: FileIdSelectComponent, isStandalone: true, selector: "croco-html-file-id-select", inputs: { labelText: "labelText", fileId: "fileId" }, outputs: { fileId: "fileId", onFileIdChanged: "onFileIdChanged" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"file-id\">\n <div class=\"form-group\">\n <label for=\"fileName\">{{labelText}}</label>\n <ng-select [(ngModel)]=\"fileId\" (ngModelChange)=\"onModelChanged($event)\" [items]=\"files\" #select\n bindLabel=\"fileName\" (search)=\"onSearchChanged($event)\" [loading]=\"loading\" [loadingText]=\"'\u0417\u0430\u0433\u0440\u0443\u0436\u0430\u044E \u0444\u0430\u0439\u043B\u044B'\"\n dropdownPosition=\"auto\" [bindValue]=\"'fileId'\" labelForId=\"fileName\" placeholder=\"\u0412\u044B\u0431\u0440\u0430\u0442\u044C \u0444\u0430\u0439\u043B\"\n [virtualScroll]=\"true\">\n <ng-template ng-option-tmp let-item=\"item\" let-index=\"index\" let-search=\"searchTerm\">\n <div class=\"file-id__container\">\n <div class=\"file-id__text-container\">\n <div>\n <span class=\"h5\">\u0424\u0430\u0439\u043B: </span>\n <span class=\"h5\"><b>{{item.fileName}}</b></span>\n </div>\n <div>\n <small>\n <span>\n \u0418\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440 \u0444\u0430\u0439\u043B\u0430: <b>{{item.fileId }}</b>\n </span>\n </small>\n </div>\n </div>\n <div class=\"file-id__img\">\n <img class=\"mx-auto d-block img-fluid\" [src]=\"getSrc(item.fileId)\" />\n </div>\n </div>\n\n </ng-template>\n </ng-select>\n </div>\n</div>\n\n<div>\n \u0418\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440 \u0444\u0430\u0439\u043B\u0430: {{fileId}}\n</div>\n", styles: [".file-id__container{display:flex;align-items:center;justify-content:space-between;gap:10px}@media (max-width: 768px){.file-id__container{align-items:unset;flex-direction:column}}.file-id__text-container{display:flex;flex-direction:column}.file-id__text-container b{word-break:break-word;white-space:normal}@media (max-width: 768px){.file-id__text-container b{font-size:14px}}@media (max-width: 768px){.file-id__text-container span{font-size:14px}}.file-id__img img{width:100px;height:100px}\n"], dependencies: [{ kind: "ngmodule", type: NgSelectModule }, { kind: "component", type: i6.NgSelectComponent, selector: "ng-select", inputs: ["ariaLabelDropdown", "bindLabel", "bindValue", "ariaLabel", "markFirst", "placeholder", "fixedPlaceholder", "notFoundText", "typeToSearchText", "preventToggleOnRightClick", "addTagText", "loadingText", "clearAllText", "appearance", "dropdownPosition", "appendTo", "loading", "closeOnSelect", "hideSelected", "selectOnTab", "openOnEnter", "maxSelectedItems", "groupBy", "groupValue", "bufferAmount", "virtualScroll", "selectableGroup", "selectableGroupAsModel", "searchFn", "trackByFn", "clearOnBackspace", "labelForId", "inputAttrs", "tabIndex", "tabFocusOnClearButton", "readonly", "searchWhileComposing", "minTermLength", "editableSearchTerm", "ngClass", "typeahead", "multiple", "addTag", "searchable", "clearable", "isOpen", "items", "compareWith", "clearSearchOnAdd", "deselectOnClick", "keyDownFn"], outputs: ["blur", "focus", "change", "open", "close", "search", "clear", "add", "remove", "scroll", "scrollToEnd"] }, { kind: "directive", type: i6.NgOptionTemplateDirective, selector: "[ng-option-tmp]" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] }); }
|
|
2606
2690
|
}
|
|
2607
2691
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FileIdSelectComponent, decorators: [{
|
|
2608
2692
|
type: Component,
|
|
@@ -3450,7 +3534,7 @@ class MainEditorComponent {
|
|
|
3450
3534
|
});
|
|
3451
3535
|
}
|
|
3452
3536
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MainEditorComponent, deps: [{ token: i1$5.ClipboardService }, { token: i2$3.MatSnackBar }, { token: i0.ChangeDetectorRef }, { token: i2$2.MatDialog }, { token: CrocoHtmlFileOptionsService }, { token: CrocoHtmlOptionsToken }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3453
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: MainEditorComponent, isStandalone: true, selector: "croco-html-main-editor", inputs: { showMarkUp: "showMarkUp", useHtmlRaw: "useHtmlRaw", html: "html" }, outputs: { html: "html", onHtmlChanged: "onHtmlChanged" }, viewQueries: [{ propertyName: "visualEditor", first: true, predicate: ["visualEditor"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: "<mat-tab-group mat-stretch-tabs=\"false\" mat-align-tabs=\"start\" style=\"position: relative\">\n <mat-tab>\n <ng-template mat-tab-label>\n <svg class=\"custom-icon\" width=\"32\" height=\"26\">\n <use [attr.xlink:href]=\"'visual-editor-tab' | spriteIconPath\"></use>\n </svg>\n </ng-template>\n\n <div style=\"height: 20px;\"></div>\n\n <croco-visual-editor #visualEditor [useHtmlRaw]=\"true\" [html]=\"html\" (onHtmlChanged)=\"onHtmlChangedHandler($event)\"\n (rendered)=\"visualEditorRenderedHandler()\">\n </croco-visual-editor>\n\n <div style=\"height: 20px;\"></div>\n </mat-tab>\n\n <mat-tab>\n <ng-template mat-tab-label>\n <svg class=\"custom-icon\" width=\"32\" height=\"26\">\n <use [attr.xlink:href]=\"'source-code-tab' | spriteIconPath\"></use>\n </svg>\n </ng-template>\n\n <div style=\"height: 10px;\"></div>\n @if (visualEditorRendered) {\n <div>\n <mat-form-field class=\"w-100\">\n <mat-label>\u0418\u0441\u0445\u043E\u0434\u043D\u044B\u0439 \u043A\u043E\u0434</mat-label>\n <textarea matInput cdkTextareaAutosize #autosize=\"cdkTextareaAutosize\" cdkAutosizeMinRows=\"1\"\n placeholder=\"\u0418\u0441\u0445\u043E\u0434\u043D\u044B\u0439 \u043A\u043E\u0434\" [(ngModel)]=\"html\" (ngModelChange)=\"textAreaModelChangedHandler()\"></textarea>\n </mat-form-field>\n </div>\n }\n\n <button (click)=\"copyMarkUp()\" mat-raised-button color=\"primary\">\n \u041A\u043E\u043F\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0440\u0430\u0437\u043C\u0435\u0442\u043A\u0443\n </button>\n </mat-tab>\n <mat-tab>\n <ng-template mat-tab-label>\n <svg class=\"custom-icon\" width=\"32\" height=\"30\">\n <use [attr.xlink:href]=\"'viewer-tab' | spriteIconPath\"></use>\n </svg>\n </ng-template>\n\n\n <mat-card style=\"padding: 2px;\" class=\"mat-card\">\n <mat-card-content>\n <croco-html-view [html]=\"html\">\n </croco-html-view>\n </mat-card-content>\n </mat-card>\n </mat-tab>\n</mat-tab-group>\n\n<div [hidden]=\"!showSettingsButton\">\n <mat-icon class=\"settings\" (click)=\"openSettingsModal()\">settings</mat-icon>\n</div>\n", styles: [":host ::ng-deep .mdc-tab.mat-mdc-tab{background-color:#f7f7fa}:host ::ng-deep .mdc-tab.mat-mdc-tab .custom-icon{fill:#323e8b}:host ::ng-deep .mdc-tab.mat-mdc-tab:hover{background-color:#f7f7fa}:host ::ng-deep .mdc-tab.mat-mdc-tab:hover .custom-icon{fill:#747ebf}:host ::ng-deep .mdc-tab.mat-mdc-tab.mdc-tab--active{background-color:#323e8b}:host ::ng-deep .mdc-tab.mat-mdc-tab.mdc-tab--active svg.custom-icon{fill:#fff}:host ::ng-deep div.mat-mdc-tab-labels{justify-content:center}:host{position:relative;width:100%;display:block;overflow-x:hidden}:host ::ng-deep .mat-expansion-panel,:host ::ng-deep .mat-mdc-tab-body-content,:host ::ng-deep .mat-mdc-tab-body,:host ::ng-deep .mat-mdc-tab-body-wrapper{overflow:visible}.example-list{width:500px;max-width:100%;border:solid 1px #ccc;min-height:60px;display:block;background:#fff;border-radius:4px;overflow:hidden}.example-box{padding:20px 10px;border-bottom:solid 1px #ccc;color:#000000de;display:flex;flex-direction:row;align-items:center;justify-content:space-between;box-sizing:border-box;cursor:move;background:#fff;font-size:14px}.cdk-drag-preview{box-sizing:border-box;border-radius:4px;box-shadow:0 5px 5px -3px #0003,0 8px 10px 1px #00000024,0 3px 14px 2px #0000001f}.cdk-drag-placeholder{opacity:0}.cdk-drag-animating{transition:transform .25s cubic-bezier(0,0,.2,1)}.example-box:last-child{border:none}.example-list.cdk-drop-list-dragging .example-box:not(.cdk-drag-placeholder){transition:transform .25s cubic-bezier(0,0,.2,1)}.settings{position:absolute;top:20px;right:20px;cursor:pointer;z-index:1000}\n"], dependencies: [{ kind: "ngmodule", type: MatTabsModule }, { kind: "directive", type: i5
|
|
3537
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: MainEditorComponent, isStandalone: true, selector: "croco-html-main-editor", inputs: { showMarkUp: "showMarkUp", useHtmlRaw: "useHtmlRaw", html: "html" }, outputs: { html: "html", onHtmlChanged: "onHtmlChanged" }, viewQueries: [{ propertyName: "visualEditor", first: true, predicate: ["visualEditor"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: "<mat-tab-group mat-stretch-tabs=\"false\" mat-align-tabs=\"start\" style=\"position: relative\">\n <mat-tab>\n <ng-template mat-tab-label>\n <svg class=\"custom-icon\" width=\"32\" height=\"26\">\n <use [attr.xlink:href]=\"'visual-editor-tab' | spriteIconPath\"></use>\n </svg>\n </ng-template>\n\n <div style=\"height: 20px;\"></div>\n\n <croco-visual-editor #visualEditor [useHtmlRaw]=\"true\" [html]=\"html\" (onHtmlChanged)=\"onHtmlChangedHandler($event)\"\n (rendered)=\"visualEditorRenderedHandler()\">\n </croco-visual-editor>\n\n <div style=\"height: 20px;\"></div>\n </mat-tab>\n\n <mat-tab>\n <ng-template mat-tab-label>\n <svg class=\"custom-icon\" width=\"32\" height=\"26\">\n <use [attr.xlink:href]=\"'source-code-tab' | spriteIconPath\"></use>\n </svg>\n </ng-template>\n\n <div style=\"height: 10px;\"></div>\n @if (visualEditorRendered) {\n <div>\n <mat-form-field class=\"w-100\">\n <mat-label>\u0418\u0441\u0445\u043E\u0434\u043D\u044B\u0439 \u043A\u043E\u0434</mat-label>\n <textarea matInput cdkTextareaAutosize #autosize=\"cdkTextareaAutosize\" cdkAutosizeMinRows=\"1\"\n placeholder=\"\u0418\u0441\u0445\u043E\u0434\u043D\u044B\u0439 \u043A\u043E\u0434\" [(ngModel)]=\"html\" (ngModelChange)=\"textAreaModelChangedHandler()\"></textarea>\n </mat-form-field>\n </div>\n }\n\n <button (click)=\"copyMarkUp()\" mat-raised-button color=\"primary\">\n \u041A\u043E\u043F\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0440\u0430\u0437\u043C\u0435\u0442\u043A\u0443\n </button>\n </mat-tab>\n <mat-tab>\n <ng-template mat-tab-label>\n <svg class=\"custom-icon\" width=\"32\" height=\"30\">\n <use [attr.xlink:href]=\"'viewer-tab' | spriteIconPath\"></use>\n </svg>\n </ng-template>\n\n\n <mat-card style=\"padding: 2px;\" class=\"mat-card\">\n <mat-card-content>\n <croco-html-view [html]=\"html\">\n </croco-html-view>\n </mat-card-content>\n </mat-card>\n </mat-tab>\n</mat-tab-group>\n\n<div [hidden]=\"!showSettingsButton\">\n <mat-icon class=\"settings\" (click)=\"openSettingsModal()\">settings</mat-icon>\n</div>\n", styles: [":host ::ng-deep .mdc-tab.mat-mdc-tab{background-color:#f7f7fa}:host ::ng-deep .mdc-tab.mat-mdc-tab .custom-icon{fill:#323e8b}:host ::ng-deep .mdc-tab.mat-mdc-tab:hover{background-color:#f7f7fa}:host ::ng-deep .mdc-tab.mat-mdc-tab:hover .custom-icon{fill:#747ebf}:host ::ng-deep .mdc-tab.mat-mdc-tab.mdc-tab--active{background-color:#323e8b}:host ::ng-deep .mdc-tab.mat-mdc-tab.mdc-tab--active svg.custom-icon{fill:#fff}:host ::ng-deep div.mat-mdc-tab-labels{justify-content:center}:host{position:relative;width:100%;display:block;overflow-x:hidden}:host ::ng-deep .mat-expansion-panel,:host ::ng-deep .mat-mdc-tab-body-content,:host ::ng-deep .mat-mdc-tab-body,:host ::ng-deep .mat-mdc-tab-body-wrapper{overflow:visible}.example-list{width:500px;max-width:100%;border:solid 1px #ccc;min-height:60px;display:block;background:#fff;border-radius:4px;overflow:hidden}.example-box{padding:20px 10px;border-bottom:solid 1px #ccc;color:#000000de;display:flex;flex-direction:row;align-items:center;justify-content:space-between;box-sizing:border-box;cursor:move;background:#fff;font-size:14px}.cdk-drag-preview{box-sizing:border-box;border-radius:4px;box-shadow:0 5px 5px -3px #0003,0 8px 10px 1px #00000024,0 3px 14px 2px #0000001f}.cdk-drag-placeholder{opacity:0}.cdk-drag-animating{transition:transform .25s cubic-bezier(0,0,.2,1)}.example-box:last-child{border:none}.example-list.cdk-drop-list-dragging .example-box:not(.cdk-drag-placeholder){transition:transform .25s cubic-bezier(0,0,.2,1)}.settings{position:absolute;top:20px;right:20px;cursor:pointer;z-index:1000}\n"], dependencies: [{ kind: "ngmodule", type: MatTabsModule }, { kind: "directive", type: i5.MatTabLabel, selector: "[mat-tab-label], [matTabLabel]" }, { kind: "component", type: i5.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i5.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "mat-align-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "component", type: VisualEditorComponent, selector: "croco-visual-editor", inputs: ["showMarkUp", "useHtmlRaw", "html"], outputs: ["html", "onHtmlChanged", "rendered"] }, { kind: "component", type: MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: MatLabel, selector: "mat-label" }, { kind: "directive", type: MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "directive", type: CdkTextareaAutosize, selector: "textarea[cdkTextareaAutosize]", inputs: ["cdkAutosizeMinRows", "cdkAutosizeMaxRows", "cdkTextareaAutosize", "placeholder"], exportAs: ["cdkTextareaAutosize"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatCardModule }, { kind: "component", type: i1$3.MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "directive", type: i1$3.MatCardContent, selector: "mat-card-content" }, { kind: "component", type: HtmlViewComponent, selector: "croco-html-view", inputs: ["html", "dataController"] }, { kind: "pipe", type: SpriteIconPathPipe, name: "spriteIconPath" }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }] }); }
|
|
3454
3538
|
}
|
|
3455
3539
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: MainEditorComponent, decorators: [{
|
|
3456
3540
|
type: Component,
|
|
@@ -3620,7 +3704,7 @@ class CrocoHtmlModule {
|
|
|
3620
3704
|
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: CrocoHtmlModule, imports: [CommonModule,
|
|
3621
3705
|
FormsModule,
|
|
3622
3706
|
ReactiveFormsModule,
|
|
3623
|
-
NgSelectModule, i1$6.MatBadgeModule, i2$4.MatTableModule, i3$1.MatExpansionModule, i1$4.MatFormFieldModule, i2$3.MatSnackBarModule, i6.MatProgressBarModule, i7.MatProgressSpinnerModule, i5
|
|
3707
|
+
NgSelectModule, i1$6.MatBadgeModule, i2$4.MatTableModule, i3$1.MatExpansionModule, i1$4.MatFormFieldModule, i2$3.MatSnackBarModule, i6$1.MatProgressBarModule, i7.MatProgressSpinnerModule, i5.MatTabsModule, i9.MatMenuModule, i1$3.MatCardModule, i11.MatSelectModule, i12.MatTooltipModule, i13.MatCheckboxModule, i14.MatChipsModule, i15.MatListModule, i2.MatButtonModule, i2$1.MatInputModule, i2$2.MatDialogModule, i19.MatPaginatorModule, i20.MatDatepickerModule, i21.MatGridListModule, i22.MatSlideToggleModule, i23.MatTreeModule, i24.MatNativeDateModule, i25.MatAutocompleteModule, i26.MatSidenavModule, i27.MatIconModule, i28.DragDropModule, i29.MatButtonToggleModule, ErrorBannerComponent,
|
|
3624
3708
|
VkVideoPlayerComponent,
|
|
3625
3709
|
YoutubeVideoPlayerComponent,
|
|
3626
3710
|
DownloadFileButtonEditorComponent,
|