taon-ui 19.0.30 → 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.
Files changed (49) hide show
  1. package/browser/fesm2022/taon-ui.mjs +180 -2
  2. package/browser/fesm2022/taon-ui.mjs.map +1 -1
  3. package/browser/lib/ui/index.d.ts +2 -1
  4. package/browser/lib/ui/taon-iframe-sync/taon-iframe-sync.component.d.ts +31 -0
  5. package/browser/package.json +1 -1
  6. package/lib/build-info._auto-generated_.d.ts +1 -1
  7. package/lib/build-info._auto-generated_.js +1 -1
  8. package/lib/env/env.angular-node-app.d.ts +1 -0
  9. package/lib/env/env.angular-node-app.js +3 -2
  10. package/lib/env/env.angular-node-app.js.map +1 -1
  11. package/lib/env/env.docs-webapp.d.ts +1 -0
  12. package/lib/env/env.docs-webapp.js +3 -2
  13. package/lib/env/env.docs-webapp.js.map +1 -1
  14. package/lib/env/env.electron-app.d.ts +1 -0
  15. package/lib/env/env.electron-app.js +3 -2
  16. package/lib/env/env.electron-app.js.map +1 -1
  17. package/lib/env/env.mobile-app.d.ts +1 -0
  18. package/lib/env/env.mobile-app.js +3 -2
  19. package/lib/env/env.mobile-app.js.map +1 -1
  20. package/lib/env/env.npm-lib-and-cli-tool.d.ts +1 -0
  21. package/lib/env/env.npm-lib-and-cli-tool.js +3 -2
  22. package/lib/env/env.npm-lib-and-cli-tool.js.map +1 -1
  23. package/lib/env/env.vscode-plugin.d.ts +1 -0
  24. package/lib/env/env.vscode-plugin.js +3 -2
  25. package/lib/env/env.vscode-plugin.js.map +1 -1
  26. package/lib/index.js +2 -2
  27. package/lib/layouts/taon-bootstrap-navbar/index.js +2 -2
  28. package/lib/ui/directives/index.js +2 -2
  29. package/lib/ui/index.d.ts +2 -1
  30. package/lib/ui/index.js +2 -2
  31. package/lib/ui/taon-github-fork-me-corner/index.js +2 -2
  32. package/lib/ui/taon-github-fork-me-ribbon/index.js +2 -2
  33. package/lib/ui/taon-iframe-sync/index.d.ts +1 -0
  34. package/lib/ui/taon-iframe-sync/index.js +5 -0
  35. package/lib/ui/taon-iframe-sync/index.js.map +1 -0
  36. package/lib/ui/taon-iframe-sync/taon-iframe-sync.component.d.ts +27 -0
  37. package/lib/ui/taon-iframe-sync/taon-iframe-sync.d.ts +24 -0
  38. package/lib/ui/taon-iframe-sync/taon-iframe-sync.js +118 -0
  39. package/lib/ui/taon-iframe-sync/taon-iframe-sync.js.map +1 -0
  40. package/lib/ui/taon-notifications/taon-notifications.models.js +2 -2
  41. package/lib/ui/taon-progress-bar/index.js +2 -2
  42. package/lib/ui/taon-session-passcode/index.js +2 -2
  43. package/lib/ui/taon-table/index.js +2 -2
  44. package/package.json +1 -1
  45. package/websql/fesm2022/taon-ui.mjs +180 -2
  46. package/websql/fesm2022/taon-ui.mjs.map +1 -1
  47. package/websql/lib/ui/index.d.ts +2 -1
  48. package/websql/lib/ui/taon-iframe-sync/taon-iframe-sync.component.d.ts +31 -0
  49. 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