taon-ui 19.0.29 → 19.0.32
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/browser/fesm2022/taon-ui.mjs +180 -2
- package/browser/fesm2022/taon-ui.mjs.map +1 -1
- package/browser/lib/ui/index.d.ts +2 -1
- package/browser/lib/ui/taon-iframe-sync/taon-iframe-sync.component.d.ts +31 -0
- package/browser/package.json +1 -1
- package/lib/build-info._auto-generated_.d.ts +1 -1
- package/lib/build-info._auto-generated_.js +1 -1
- package/lib/env/env.angular-node-app.d.ts +1 -0
- package/lib/env/env.angular-node-app.js +3 -2
- package/lib/env/env.angular-node-app.js.map +1 -1
- package/lib/env/env.docs-webapp.d.ts +1 -0
- package/lib/env/env.docs-webapp.js +3 -2
- package/lib/env/env.docs-webapp.js.map +1 -1
- package/lib/env/env.electron-app.d.ts +1 -0
- package/lib/env/env.electron-app.js +3 -2
- package/lib/env/env.electron-app.js.map +1 -1
- package/lib/env/env.mobile-app.d.ts +1 -0
- package/lib/env/env.mobile-app.js +3 -2
- package/lib/env/env.mobile-app.js.map +1 -1
- package/lib/env/env.npm-lib-and-cli-tool.d.ts +1 -0
- package/lib/env/env.npm-lib-and-cli-tool.js +3 -2
- package/lib/env/env.npm-lib-and-cli-tool.js.map +1 -1
- package/lib/env/env.vscode-plugin.d.ts +1 -0
- package/lib/env/env.vscode-plugin.js +3 -2
- package/lib/env/env.vscode-plugin.js.map +1 -1
- package/lib/index.js +2 -2
- package/lib/layouts/taon-bootstrap-navbar/index.js +2 -2
- package/lib/ui/directives/index.js +2 -2
- package/lib/ui/index.d.ts +2 -1
- package/lib/ui/index.js +2 -2
- package/lib/ui/taon-github-fork-me-corner/index.js +2 -2
- package/lib/ui/taon-github-fork-me-ribbon/index.js +2 -2
- package/lib/ui/taon-iframe-sync/index.d.ts +1 -0
- package/lib/ui/taon-iframe-sync/index.js +5 -0
- package/lib/ui/taon-iframe-sync/index.js.map +1 -0
- package/lib/ui/taon-iframe-sync/taon-iframe-sync.component.d.ts +27 -0
- package/lib/ui/taon-iframe-sync/taon-iframe-sync.d.ts +24 -0
- package/lib/ui/taon-iframe-sync/taon-iframe-sync.js +118 -0
- package/lib/ui/taon-iframe-sync/taon-iframe-sync.js.map +1 -0
- package/lib/ui/taon-notifications/taon-notifications.models.js +2 -2
- package/lib/ui/taon-progress-bar/index.js +2 -2
- package/lib/ui/taon-session-passcode/index.js +2 -2
- package/lib/ui/taon-table/index.js +2 -2
- package/package.json +1 -1
- package/websql/fesm2022/taon-ui.mjs +180 -2
- package/websql/fesm2022/taon-ui.mjs.map +1 -1
- package/websql/lib/ui/index.d.ts +2 -1
- package/websql/lib/ui/taon-iframe-sync/taon-iframe-sync.component.d.ts +31 -0
- package/websql/package.json +1 -1
|
@@ -3,6 +3,7 @@ import { EventEmitter, HostListener, HostBinding, Output, Input, Directive, Pipe
|
|
|
3
3
|
import { _, json5, Helpers } from 'tnp-core/browser';
|
|
4
4
|
import { Log, Level } from 'ng2-logger/browser';
|
|
5
5
|
import * as i1 from '@angular/platform-browser';
|
|
6
|
+
import { DomSanitizer } from '@angular/platform-browser';
|
|
6
7
|
import { A11yModule } from '@angular/cdk/a11y';
|
|
7
8
|
import * as i5$1 from '@angular/cdk/drag-drop';
|
|
8
9
|
import { DragDropModule } from '@angular/cdk/drag-drop';
|
|
@@ -52,7 +53,7 @@ import { MatToolbarModule } from '@angular/material/toolbar';
|
|
|
52
53
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
53
54
|
import { MatTreeModule } from '@angular/material/tree';
|
|
54
55
|
import * as i2$1 from '@angular/common';
|
|
55
|
-
import { CommonModule } from '@angular/common';
|
|
56
|
+
import { CommonModule, NgIf } from '@angular/common';
|
|
56
57
|
import { Resource } from 'ng2-rest/browser';
|
|
57
58
|
import * as i1$1 from '@ngneat/hot-toast';
|
|
58
59
|
import { __decorate, __metadata } from 'tslib';
|
|
@@ -72,6 +73,8 @@ import { StaticColumnsModule } from 'static-columns/browser';
|
|
|
72
73
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
|
73
74
|
import * as i4 from 'ngx-scrollbar';
|
|
74
75
|
import { NgScrollbarModule } from 'ngx-scrollbar';
|
|
76
|
+
import { Router, ActivatedRoute, NavigationEnd } from '@angular/router';
|
|
77
|
+
import { distinctUntilChanged as distinctUntilChanged$1, takeUntil as takeUntil$1, filter } from 'rxjs/operators';
|
|
75
78
|
import * as i2$3 from '@ng-bootstrap/ng-bootstrap';
|
|
76
79
|
import { NgbCollapseModule } from '@ng-bootstrap/ng-bootstrap';
|
|
77
80
|
import { ModalModule } from 'ngx-bootstrap/modal';
|
|
@@ -1230,6 +1233,181 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImpo
|
|
|
1230
1233
|
args: ['window:resize', ['$event']]
|
|
1231
1234
|
}] } });
|
|
1232
1235
|
|
|
1236
|
+
// iframe-sync.component.ts
|
|
1237
|
+
// taon-iframe-sync.component.ts
|
|
1238
|
+
class TaonIframeSyncComponent {
|
|
1239
|
+
constructor() {
|
|
1240
|
+
this.destroy$ = new Subject();
|
|
1241
|
+
this.iframeWin = null;
|
|
1242
|
+
// This is the key: iframe stays hidden until first correct page is confirmed loaded
|
|
1243
|
+
this.isReady = false;
|
|
1244
|
+
this.hasInitialSync = false;
|
|
1245
|
+
this.queryParamKey = 'internalIframePath';
|
|
1246
|
+
this.initialPath = null;
|
|
1247
|
+
this.router = inject(Router);
|
|
1248
|
+
this.route = inject(ActivatedRoute);
|
|
1249
|
+
this.sanitizer = inject(DomSanitizer);
|
|
1250
|
+
// This is called when iframe confirms: "Yes, I loaded the correct page!"
|
|
1251
|
+
this.handleIframeMessage = (event) => {
|
|
1252
|
+
const expected = this.targetOrigin ?? this.getSafeOrigin(this.iframeSrc);
|
|
1253
|
+
if (event.origin !== expected)
|
|
1254
|
+
return;
|
|
1255
|
+
// NEW: iframe tells us when it's truly ready
|
|
1256
|
+
if (event.data?.type === 'IFRAME_READY') {
|
|
1257
|
+
this.isReady = true; // NOW show the iframe!
|
|
1258
|
+
return;
|
|
1259
|
+
}
|
|
1260
|
+
if (event.data?.type === 'IFRAME_PATH_UPDATE') {
|
|
1261
|
+
const path = event.data.path || '/';
|
|
1262
|
+
this.router.navigate([], {
|
|
1263
|
+
queryParams: { [this.queryParamKey]: path === '/' ? null : path },
|
|
1264
|
+
queryParamsHandling: 'merge',
|
|
1265
|
+
replaceUrl: true,
|
|
1266
|
+
});
|
|
1267
|
+
}
|
|
1268
|
+
};
|
|
1269
|
+
}
|
|
1270
|
+
// ... same inputs as before
|
|
1271
|
+
#src;
|
|
1272
|
+
set iframeSrc(v) {
|
|
1273
|
+
this.#src = v.trim();
|
|
1274
|
+
this.safeSrc = this.sanitizer.bypassSecurityTrustResourceUrl(this.#src);
|
|
1275
|
+
}
|
|
1276
|
+
get iframeSrc() {
|
|
1277
|
+
return this.#src;
|
|
1278
|
+
}
|
|
1279
|
+
ngAfterViewInit() {
|
|
1280
|
+
this.setupSync();
|
|
1281
|
+
window.addEventListener('message', this.handleIframeMessage);
|
|
1282
|
+
}
|
|
1283
|
+
setupSync() {
|
|
1284
|
+
this.route.queryParamMap
|
|
1285
|
+
.pipe(distinctUntilChanged$1((a, b) => a.get(this.queryParamKey) === b.get(this.queryParamKey)), takeUntil$1(this.destroy$))
|
|
1286
|
+
.subscribe(() => this.sendNavigateIfReady());
|
|
1287
|
+
this.router.events
|
|
1288
|
+
.pipe(filter((e) => e instanceof NavigationEnd), takeUntil$1(this.destroy$))
|
|
1289
|
+
.subscribe(() => this.sendNavigateIfReady());
|
|
1290
|
+
}
|
|
1291
|
+
onIframeLoad() {
|
|
1292
|
+
this.iframeWin = this.iframeRef?.nativeElement?.contentWindow;
|
|
1293
|
+
// Send the correct path — iframe will load it
|
|
1294
|
+
this.sendNavigateIfReady();
|
|
1295
|
+
// The real magic: wait for iframe to confirm it loaded the right page
|
|
1296
|
+
// (we'll enhance the iframe script to send a "READY" message)
|
|
1297
|
+
}
|
|
1298
|
+
sendNavigateIfReady() {
|
|
1299
|
+
if (!this.iframeWin ||
|
|
1300
|
+
!this.isValidUrl(this.iframeSrc) ||
|
|
1301
|
+
this.hasInitialSync)
|
|
1302
|
+
return;
|
|
1303
|
+
const path = this.route.snapshot.queryParamMap.get(this.queryParamKey) ??
|
|
1304
|
+
this.initialPath ??
|
|
1305
|
+
'/';
|
|
1306
|
+
const origin = this.targetOrigin ?? this.getSafeOrigin(this.iframeSrc);
|
|
1307
|
+
this.iframeWin.postMessage({ type: 'NAVIGATE', path }, origin);
|
|
1308
|
+
this.hasInitialSync = true;
|
|
1309
|
+
}
|
|
1310
|
+
// ────── Safe URL helpers ──────
|
|
1311
|
+
isValidUrl(string) {
|
|
1312
|
+
if (!string)
|
|
1313
|
+
return false;
|
|
1314
|
+
try {
|
|
1315
|
+
new URL(string, window.location.origin);
|
|
1316
|
+
return true;
|
|
1317
|
+
}
|
|
1318
|
+
catch {
|
|
1319
|
+
return false;
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
getSafeOrigin(src) {
|
|
1323
|
+
if (!src)
|
|
1324
|
+
return window.location.origin;
|
|
1325
|
+
try {
|
|
1326
|
+
// Full URL
|
|
1327
|
+
if (src.startsWith('http')) {
|
|
1328
|
+
return new URL(src).origin;
|
|
1329
|
+
}
|
|
1330
|
+
// Relative path
|
|
1331
|
+
if (src.startsWith('/')) {
|
|
1332
|
+
return window.location.origin;
|
|
1333
|
+
}
|
|
1334
|
+
// Protocol-relative
|
|
1335
|
+
if (src.startsWith('//')) {
|
|
1336
|
+
return new URL(src, window.location.origin).origin;
|
|
1337
|
+
}
|
|
1338
|
+
// Just return current origin for anything else
|
|
1339
|
+
return window.location.origin;
|
|
1340
|
+
}
|
|
1341
|
+
catch {
|
|
1342
|
+
return window.location.origin;
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
// ... rest of helpers (isValidUrl, getSafeOrigin) same as before
|
|
1346
|
+
ngOnDestroy() {
|
|
1347
|
+
this.destroy$.next();
|
|
1348
|
+
this.destroy$.complete();
|
|
1349
|
+
window.removeEventListener('message', this.handleIframeMessage);
|
|
1350
|
+
}
|
|
1351
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: TaonIframeSyncComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1352
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.15", type: TaonIframeSyncComponent, isStandalone: true, selector: "taon-iframe-sync", inputs: { iframeSrc: "iframeSrc", queryParamKey: "queryParamKey", initialPath: "initialPath", targetOrigin: "targetOrigin" }, viewQueries: [{ propertyName: "iframeRef", first: true, predicate: ["iframe"], descendants: true }], ngImport: i0, template: `
|
|
1353
|
+
<!-- Hidden by default, shown only when ready -->
|
|
1354
|
+
<iframe
|
|
1355
|
+
#iframe
|
|
1356
|
+
[src]="safeSrc"
|
|
1357
|
+
[style.display]="isReady ? 'block' : 'none'"
|
|
1358
|
+
[style.visibility]="isReady ? 'visible' : 'hidden'"
|
|
1359
|
+
[style.opacity]="isReady ? '1' : '0'"
|
|
1360
|
+
frameborder="0"
|
|
1361
|
+
style="width:100%; height:100%; border:none; transition: opacity 0.2s ease;"
|
|
1362
|
+
(load)="onIframeLoad()"
|
|
1363
|
+
allow="clipboard-write"></iframe>
|
|
1364
|
+
|
|
1365
|
+
<!-- Optional: nice loading placeholder -->
|
|
1366
|
+
<div
|
|
1367
|
+
*ngIf="!isReady"
|
|
1368
|
+
class="iframe-loading-placeholder">
|
|
1369
|
+
<div class="spinner"></div>
|
|
1370
|
+
<p>Loading documentation...</p>
|
|
1371
|
+
</div>
|
|
1372
|
+
`, isInline: true, styles: [":host{display:block;width:100%;height:100%;position:relative}.iframe-loading-placeholder{position:absolute;inset:0;background:var(--md-primary-fg-color--light);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;color:var(--text-color, #666);font-family:system-ui,sans-serif}.spinner{width:40px;height:40px;border:4px solid #f3f3f3;border-top:4px solid #3498db;border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1373
|
+
}
|
|
1374
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: TaonIframeSyncComponent, decorators: [{
|
|
1375
|
+
type: Component,
|
|
1376
|
+
args: [{ selector: 'taon-iframe-sync', template: `
|
|
1377
|
+
<!-- Hidden by default, shown only when ready -->
|
|
1378
|
+
<iframe
|
|
1379
|
+
#iframe
|
|
1380
|
+
[src]="safeSrc"
|
|
1381
|
+
[style.display]="isReady ? 'block' : 'none'"
|
|
1382
|
+
[style.visibility]="isReady ? 'visible' : 'hidden'"
|
|
1383
|
+
[style.opacity]="isReady ? '1' : '0'"
|
|
1384
|
+
frameborder="0"
|
|
1385
|
+
style="width:100%; height:100%; border:none; transition: opacity 0.2s ease;"
|
|
1386
|
+
(load)="onIframeLoad()"
|
|
1387
|
+
allow="clipboard-write"></iframe>
|
|
1388
|
+
|
|
1389
|
+
<!-- Optional: nice loading placeholder -->
|
|
1390
|
+
<div
|
|
1391
|
+
*ngIf="!isReady"
|
|
1392
|
+
class="iframe-loading-placeholder">
|
|
1393
|
+
<div class="spinner"></div>
|
|
1394
|
+
<p>Loading documentation...</p>
|
|
1395
|
+
</div>
|
|
1396
|
+
`, standalone: true, imports: [NgIf], changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block;width:100%;height:100%;position:relative}.iframe-loading-placeholder{position:absolute;inset:0;background:var(--md-primary-fg-color--light);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;color:var(--text-color, #666);font-family:system-ui,sans-serif}.spinner{width:40px;height:40px;border:4px solid #f3f3f3;border-top:4px solid #3498db;border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}\n"] }]
|
|
1397
|
+
}], propDecorators: { iframeRef: [{
|
|
1398
|
+
type: ViewChild,
|
|
1399
|
+
args: ['iframe']
|
|
1400
|
+
}], iframeSrc: [{
|
|
1401
|
+
type: Input,
|
|
1402
|
+
args: [{ required: true }]
|
|
1403
|
+
}], queryParamKey: [{
|
|
1404
|
+
type: Input
|
|
1405
|
+
}], initialPath: [{
|
|
1406
|
+
type: Input
|
|
1407
|
+
}], targetOrigin: [{
|
|
1408
|
+
type: Input
|
|
1409
|
+
}] } });
|
|
1410
|
+
|
|
1233
1411
|
//#region @browser
|
|
1234
1412
|
//#endregion
|
|
1235
1413
|
|
|
@@ -1261,5 +1439,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImpo
|
|
|
1261
1439
|
* Generated bundle index. Do not edit.
|
|
1262
1440
|
*/
|
|
1263
1441
|
|
|
1264
|
-
export { SafePipe, TaonAdminModeConfigurationComponent, TaonBootstrapNavbarComponent, TaonFullMaterialModule, TaonGithubForkMeCornerComponent, TaonGithubForkMeCornerModule, TaonGithubForkMeRibbonComponent, TaonGithubForkMeRibbonModule, TaonInjectHTMLDirective, TaonLongPress, TaonNotificationOptions, TaonNotificationsComponent, TaonNotificationsModule, TaonNotificationsService, TaonProgressBarComponent, TaonProgressBarModule, TaonSessionPasscodeComponent, TaonTableComponent, TaonTableModule };
|
|
1442
|
+
export { SafePipe, TaonAdminModeConfigurationComponent, TaonBootstrapNavbarComponent, TaonFullMaterialModule, TaonGithubForkMeCornerComponent, TaonGithubForkMeCornerModule, TaonGithubForkMeRibbonComponent, TaonGithubForkMeRibbonModule, TaonIframeSyncComponent, TaonInjectHTMLDirective, TaonLongPress, TaonNotificationOptions, TaonNotificationsComponent, TaonNotificationsModule, TaonNotificationsService, TaonProgressBarComponent, TaonProgressBarModule, TaonSessionPasscodeComponent, TaonTableComponent, TaonTableModule };
|
|
1265
1443
|
//# sourceMappingURL=taon-ui.mjs.map
|