taon-ui 19.0.30 → 21.0.2
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 +187 -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 +32 -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/build-info._auto-generated_.js.map +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 +28 -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 +2 -2
- package/tmp-environment.json +43 -0
- package/websql/fesm2022/taon-ui.mjs +187 -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 +32 -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/websql';
|
|
4
4
|
import { Log, Level } from 'ng2-logger/websql';
|
|
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/websql';
|
|
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/websql';
|
|
|
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';
|
|
@@ -1225,6 +1228,188 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImpo
|
|
|
1225
1228
|
args: ['window:resize', ['$event']]
|
|
1226
1229
|
}] } });
|
|
1227
1230
|
|
|
1231
|
+
// iframe-sync.component.ts
|
|
1232
|
+
// taon-iframe-sync.component.ts
|
|
1233
|
+
class TaonIframeSyncComponent {
|
|
1234
|
+
constructor() {
|
|
1235
|
+
this.loaderBackgroundColor = 'white';
|
|
1236
|
+
this.destroy$ = new Subject();
|
|
1237
|
+
this.iframeWin = null;
|
|
1238
|
+
// This is the key: iframe stays hidden until first correct page is confirmed loaded
|
|
1239
|
+
this.isReady = false;
|
|
1240
|
+
this.hasInitialSync = false;
|
|
1241
|
+
this.queryParamKey = 'internalIframePath';
|
|
1242
|
+
this.initialPath = null;
|
|
1243
|
+
this.router = inject(Router);
|
|
1244
|
+
this.route = inject(ActivatedRoute);
|
|
1245
|
+
this.sanitizer = inject(DomSanitizer);
|
|
1246
|
+
// This is called when iframe confirms: "Yes, I loaded the correct page!"
|
|
1247
|
+
this.handleIframeMessage = (event) => {
|
|
1248
|
+
const expected = this.targetOrigin ?? this.getSafeOrigin(this.iframeSrc);
|
|
1249
|
+
if (event.origin !== expected)
|
|
1250
|
+
return;
|
|
1251
|
+
// NEW: iframe tells us when it's truly ready
|
|
1252
|
+
if (event.data?.type === 'IFRAME_READY') {
|
|
1253
|
+
this.isReady = true; // NOW show the iframe!
|
|
1254
|
+
return;
|
|
1255
|
+
}
|
|
1256
|
+
if (event.data?.type === 'IFRAME_PATH_UPDATE') {
|
|
1257
|
+
const path = event.data.path || '/';
|
|
1258
|
+
this.router.navigate([], {
|
|
1259
|
+
queryParams: { [this.queryParamKey]: path === '/' ? null : path },
|
|
1260
|
+
queryParamsHandling: 'merge',
|
|
1261
|
+
replaceUrl: true,
|
|
1262
|
+
});
|
|
1263
|
+
}
|
|
1264
|
+
};
|
|
1265
|
+
}
|
|
1266
|
+
// ... same inputs as before
|
|
1267
|
+
#src;
|
|
1268
|
+
set iframeSrc(v) {
|
|
1269
|
+
this.#src = v.trim();
|
|
1270
|
+
this.safeSrc = this.sanitizer.bypassSecurityTrustResourceUrl(this.#src);
|
|
1271
|
+
}
|
|
1272
|
+
get iframeSrc() {
|
|
1273
|
+
return this.#src;
|
|
1274
|
+
}
|
|
1275
|
+
ngAfterViewInit() {
|
|
1276
|
+
this.setupSync();
|
|
1277
|
+
window.addEventListener('message', this.handleIframeMessage);
|
|
1278
|
+
}
|
|
1279
|
+
setupSync() {
|
|
1280
|
+
this.route.queryParamMap
|
|
1281
|
+
.pipe(distinctUntilChanged$1((a, b) => a.get(this.queryParamKey) === b.get(this.queryParamKey)), takeUntil$1(this.destroy$))
|
|
1282
|
+
.subscribe(() => this.sendNavigateIfReady());
|
|
1283
|
+
this.router.events
|
|
1284
|
+
.pipe(filter((e) => e instanceof NavigationEnd), takeUntil$1(this.destroy$))
|
|
1285
|
+
.subscribe(() => this.sendNavigateIfReady());
|
|
1286
|
+
}
|
|
1287
|
+
onIframeLoad() {
|
|
1288
|
+
this.iframeWin = this.iframeRef?.nativeElement?.contentWindow;
|
|
1289
|
+
// Send the correct path — iframe will load it
|
|
1290
|
+
this.sendNavigateIfReady();
|
|
1291
|
+
// The real magic: wait for iframe to confirm it loaded the right page
|
|
1292
|
+
// (we'll enhance the iframe script to send a "READY" message)
|
|
1293
|
+
}
|
|
1294
|
+
sendNavigateIfReady() {
|
|
1295
|
+
if (!this.iframeWin ||
|
|
1296
|
+
!this.isValidUrl(this.iframeSrc) ||
|
|
1297
|
+
this.hasInitialSync)
|
|
1298
|
+
return;
|
|
1299
|
+
const path = this.route.snapshot.queryParamMap.get(this.queryParamKey) ??
|
|
1300
|
+
this.initialPath ??
|
|
1301
|
+
'/';
|
|
1302
|
+
const origin = this.targetOrigin ?? this.getSafeOrigin(this.iframeSrc);
|
|
1303
|
+
this.iframeWin.postMessage({ type: 'NAVIGATE', path }, origin);
|
|
1304
|
+
this.hasInitialSync = true;
|
|
1305
|
+
}
|
|
1306
|
+
// ────── Safe URL helpers ──────
|
|
1307
|
+
isValidUrl(string) {
|
|
1308
|
+
if (!string)
|
|
1309
|
+
return false;
|
|
1310
|
+
try {
|
|
1311
|
+
new URL(string, window.location.origin);
|
|
1312
|
+
return true;
|
|
1313
|
+
}
|
|
1314
|
+
catch {
|
|
1315
|
+
return false;
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
getSafeOrigin(src) {
|
|
1319
|
+
if (!src)
|
|
1320
|
+
return window.location.origin;
|
|
1321
|
+
try {
|
|
1322
|
+
// Full URL
|
|
1323
|
+
if (src.startsWith('http')) {
|
|
1324
|
+
return new URL(src).origin;
|
|
1325
|
+
}
|
|
1326
|
+
// Relative path
|
|
1327
|
+
if (src.startsWith('/')) {
|
|
1328
|
+
return window.location.origin;
|
|
1329
|
+
}
|
|
1330
|
+
// Protocol-relative
|
|
1331
|
+
if (src.startsWith('//')) {
|
|
1332
|
+
return new URL(src, window.location.origin).origin;
|
|
1333
|
+
}
|
|
1334
|
+
// Just return current origin for anything else
|
|
1335
|
+
return window.location.origin;
|
|
1336
|
+
}
|
|
1337
|
+
catch {
|
|
1338
|
+
return window.location.origin;
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
// ... rest of helpers (isValidUrl, getSafeOrigin) same as before
|
|
1342
|
+
ngOnDestroy() {
|
|
1343
|
+
this.destroy$.next();
|
|
1344
|
+
this.destroy$.complete();
|
|
1345
|
+
window.removeEventListener('message', this.handleIframeMessage);
|
|
1346
|
+
}
|
|
1347
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: TaonIframeSyncComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1348
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.15", type: TaonIframeSyncComponent, isStandalone: true, selector: "taon-iframe-sync", inputs: { loaderBackgroundColor: "loaderBackgroundColor", iframeSrc: "iframeSrc", queryParamKey: "queryParamKey", initialPath: "initialPath", targetOrigin: "targetOrigin" }, viewQueries: [{ propertyName: "iframeRef", first: true, predicate: ["iframe"], descendants: true }], ngImport: i0, template: `
|
|
1349
|
+
<!-- Hidden by default, shown only when ready -->
|
|
1350
|
+
<iframe
|
|
1351
|
+
#iframe
|
|
1352
|
+
[src]="safeSrc"
|
|
1353
|
+
[style.display]="isReady ? 'block' : 'none'"
|
|
1354
|
+
[style.visibility]="isReady ? 'visible' : 'hidden'"
|
|
1355
|
+
[style.opacity]="isReady ? '1' : '0'"
|
|
1356
|
+
frameborder="0"
|
|
1357
|
+
style="width:100%; height:100%; border:none; transition: opacity 0.2s ease;"
|
|
1358
|
+
(load)="onIframeLoad()"
|
|
1359
|
+
allow="clipboard-write"></iframe>
|
|
1360
|
+
|
|
1361
|
+
<!-- Optional: nice loading placeholder -->
|
|
1362
|
+
<div
|
|
1363
|
+
*ngIf="!isReady"
|
|
1364
|
+
class="iframe-loading-placeholder"
|
|
1365
|
+
[style.background]="loaderBackgroundColor"
|
|
1366
|
+
>
|
|
1367
|
+
<div class="spinner"></div>
|
|
1368
|
+
<p>Loading documentation...</p>
|
|
1369
|
+
</div>
|
|
1370
|
+
`, 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 }); }
|
|
1371
|
+
}
|
|
1372
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: TaonIframeSyncComponent, decorators: [{
|
|
1373
|
+
type: Component,
|
|
1374
|
+
args: [{ selector: 'taon-iframe-sync', template: `
|
|
1375
|
+
<!-- Hidden by default, shown only when ready -->
|
|
1376
|
+
<iframe
|
|
1377
|
+
#iframe
|
|
1378
|
+
[src]="safeSrc"
|
|
1379
|
+
[style.display]="isReady ? 'block' : 'none'"
|
|
1380
|
+
[style.visibility]="isReady ? 'visible' : 'hidden'"
|
|
1381
|
+
[style.opacity]="isReady ? '1' : '0'"
|
|
1382
|
+
frameborder="0"
|
|
1383
|
+
style="width:100%; height:100%; border:none; transition: opacity 0.2s ease;"
|
|
1384
|
+
(load)="onIframeLoad()"
|
|
1385
|
+
allow="clipboard-write"></iframe>
|
|
1386
|
+
|
|
1387
|
+
<!-- Optional: nice loading placeholder -->
|
|
1388
|
+
<div
|
|
1389
|
+
*ngIf="!isReady"
|
|
1390
|
+
class="iframe-loading-placeholder"
|
|
1391
|
+
[style.background]="loaderBackgroundColor"
|
|
1392
|
+
>
|
|
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: { loaderBackgroundColor: [{
|
|
1398
|
+
type: Input
|
|
1399
|
+
}], iframeRef: [{
|
|
1400
|
+
type: ViewChild,
|
|
1401
|
+
args: ['iframe']
|
|
1402
|
+
}], iframeSrc: [{
|
|
1403
|
+
type: Input,
|
|
1404
|
+
args: [{ required: true }]
|
|
1405
|
+
}], queryParamKey: [{
|
|
1406
|
+
type: Input
|
|
1407
|
+
}], initialPath: [{
|
|
1408
|
+
type: Input
|
|
1409
|
+
}], targetOrigin: [{
|
|
1410
|
+
type: Input
|
|
1411
|
+
}] } });
|
|
1412
|
+
|
|
1228
1413
|
//#region @browser
|
|
1229
1414
|
//#endregion
|
|
1230
1415
|
|
|
@@ -1256,5 +1441,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImpo
|
|
|
1256
1441
|
* Generated bundle index. Do not edit.
|
|
1257
1442
|
*/
|
|
1258
1443
|
|
|
1259
|
-
export { SafePipe, TaonAdminModeConfigurationComponent, TaonBootstrapNavbarComponent, TaonFullMaterialModule, TaonGithubForkMeCornerComponent, TaonGithubForkMeCornerModule, TaonGithubForkMeRibbonComponent, TaonGithubForkMeRibbonModule, TaonInjectHTMLDirective, TaonLongPress, TaonNotificationOptions, TaonNotificationsComponent, TaonNotificationsModule, TaonNotificationsService, TaonProgressBarComponent, TaonProgressBarModule, TaonSessionPasscodeComponent, TaonTableComponent, TaonTableModule };
|
|
1444
|
+
export { SafePipe, TaonAdminModeConfigurationComponent, TaonBootstrapNavbarComponent, TaonFullMaterialModule, TaonGithubForkMeCornerComponent, TaonGithubForkMeCornerModule, TaonGithubForkMeRibbonComponent, TaonGithubForkMeRibbonModule, TaonIframeSyncComponent, TaonInjectHTMLDirective, TaonLongPress, TaonNotificationOptions, TaonNotificationsComponent, TaonNotificationsModule, TaonNotificationsService, TaonProgressBarComponent, TaonProgressBarModule, TaonSessionPasscodeComponent, TaonTableComponent, TaonTableModule };
|
|
1260
1445
|
//# sourceMappingURL=taon-ui.mjs.map
|