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.
Files changed (48) hide show
  1. package/browser/fesm2022/taon-ui.mjs +187 -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 +32 -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/build-info._auto-generated_.js.map +1 -1
  9. package/lib/env/env.angular-node-app.d.ts +1 -0
  10. package/lib/env/env.angular-node-app.js +3 -2
  11. package/lib/env/env.angular-node-app.js.map +1 -1
  12. package/lib/env/env.docs-webapp.d.ts +1 -0
  13. package/lib/env/env.docs-webapp.js +3 -2
  14. package/lib/env/env.docs-webapp.js.map +1 -1
  15. package/lib/env/env.electron-app.d.ts +1 -0
  16. package/lib/env/env.electron-app.js +3 -2
  17. package/lib/env/env.electron-app.js.map +1 -1
  18. package/lib/env/env.mobile-app.d.ts +1 -0
  19. package/lib/env/env.mobile-app.js +3 -2
  20. package/lib/env/env.mobile-app.js.map +1 -1
  21. package/lib/env/env.npm-lib-and-cli-tool.d.ts +1 -0
  22. package/lib/env/env.npm-lib-and-cli-tool.js +3 -2
  23. package/lib/env/env.npm-lib-and-cli-tool.js.map +1 -1
  24. package/lib/env/env.vscode-plugin.d.ts +1 -0
  25. package/lib/env/env.vscode-plugin.js +3 -2
  26. package/lib/env/env.vscode-plugin.js.map +1 -1
  27. package/lib/index.js +2 -2
  28. package/lib/layouts/taon-bootstrap-navbar/index.js +2 -2
  29. package/lib/ui/directives/index.js +2 -2
  30. package/lib/ui/index.d.ts +2 -1
  31. package/lib/ui/index.js +2 -2
  32. package/lib/ui/taon-github-fork-me-corner/index.js +2 -2
  33. package/lib/ui/taon-github-fork-me-ribbon/index.js +2 -2
  34. package/lib/ui/taon-iframe-sync/index.d.ts +1 -0
  35. package/lib/ui/taon-iframe-sync/index.js +5 -0
  36. package/lib/ui/taon-iframe-sync/index.js.map +1 -0
  37. package/lib/ui/taon-iframe-sync/taon-iframe-sync.component.d.ts +28 -0
  38. package/lib/ui/taon-notifications/taon-notifications.models.js +2 -2
  39. package/lib/ui/taon-progress-bar/index.js +2 -2
  40. package/lib/ui/taon-session-passcode/index.js +2 -2
  41. package/lib/ui/taon-table/index.js +2 -2
  42. package/package.json +2 -2
  43. package/tmp-environment.json +43 -0
  44. package/websql/fesm2022/taon-ui.mjs +187 -2
  45. package/websql/fesm2022/taon-ui.mjs.map +1 -1
  46. package/websql/lib/ui/index.d.ts +2 -1
  47. package/websql/lib/ui/taon-iframe-sync/taon-iframe-sync.component.d.ts +32 -0
  48. 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,188 @@ 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.loaderBackgroundColor = 'white';
1241
+ this.destroy$ = new Subject();
1242
+ this.iframeWin = null;
1243
+ // This is the key: iframe stays hidden until first correct page is confirmed loaded
1244
+ this.isReady = false;
1245
+ this.hasInitialSync = false;
1246
+ this.queryParamKey = 'internalIframePath';
1247
+ this.initialPath = null;
1248
+ this.router = inject(Router);
1249
+ this.route = inject(ActivatedRoute);
1250
+ this.sanitizer = inject(DomSanitizer);
1251
+ // This is called when iframe confirms: "Yes, I loaded the correct page!"
1252
+ this.handleIframeMessage = (event) => {
1253
+ const expected = this.targetOrigin ?? this.getSafeOrigin(this.iframeSrc);
1254
+ if (event.origin !== expected)
1255
+ return;
1256
+ // NEW: iframe tells us when it's truly ready
1257
+ if (event.data?.type === 'IFRAME_READY') {
1258
+ this.isReady = true; // NOW show the iframe!
1259
+ return;
1260
+ }
1261
+ if (event.data?.type === 'IFRAME_PATH_UPDATE') {
1262
+ const path = event.data.path || '/';
1263
+ this.router.navigate([], {
1264
+ queryParams: { [this.queryParamKey]: path === '/' ? null : path },
1265
+ queryParamsHandling: 'merge',
1266
+ replaceUrl: true,
1267
+ });
1268
+ }
1269
+ };
1270
+ }
1271
+ // ... same inputs as before
1272
+ #src;
1273
+ set iframeSrc(v) {
1274
+ this.#src = v.trim();
1275
+ this.safeSrc = this.sanitizer.bypassSecurityTrustResourceUrl(this.#src);
1276
+ }
1277
+ get iframeSrc() {
1278
+ return this.#src;
1279
+ }
1280
+ ngAfterViewInit() {
1281
+ this.setupSync();
1282
+ window.addEventListener('message', this.handleIframeMessage);
1283
+ }
1284
+ setupSync() {
1285
+ this.route.queryParamMap
1286
+ .pipe(distinctUntilChanged$1((a, b) => a.get(this.queryParamKey) === b.get(this.queryParamKey)), takeUntil$1(this.destroy$))
1287
+ .subscribe(() => this.sendNavigateIfReady());
1288
+ this.router.events
1289
+ .pipe(filter((e) => e instanceof NavigationEnd), takeUntil$1(this.destroy$))
1290
+ .subscribe(() => this.sendNavigateIfReady());
1291
+ }
1292
+ onIframeLoad() {
1293
+ this.iframeWin = this.iframeRef?.nativeElement?.contentWindow;
1294
+ // Send the correct path — iframe will load it
1295
+ this.sendNavigateIfReady();
1296
+ // The real magic: wait for iframe to confirm it loaded the right page
1297
+ // (we'll enhance the iframe script to send a "READY" message)
1298
+ }
1299
+ sendNavigateIfReady() {
1300
+ if (!this.iframeWin ||
1301
+ !this.isValidUrl(this.iframeSrc) ||
1302
+ this.hasInitialSync)
1303
+ return;
1304
+ const path = this.route.snapshot.queryParamMap.get(this.queryParamKey) ??
1305
+ this.initialPath ??
1306
+ '/';
1307
+ const origin = this.targetOrigin ?? this.getSafeOrigin(this.iframeSrc);
1308
+ this.iframeWin.postMessage({ type: 'NAVIGATE', path }, origin);
1309
+ this.hasInitialSync = true;
1310
+ }
1311
+ // ────── Safe URL helpers ──────
1312
+ isValidUrl(string) {
1313
+ if (!string)
1314
+ return false;
1315
+ try {
1316
+ new URL(string, window.location.origin);
1317
+ return true;
1318
+ }
1319
+ catch {
1320
+ return false;
1321
+ }
1322
+ }
1323
+ getSafeOrigin(src) {
1324
+ if (!src)
1325
+ return window.location.origin;
1326
+ try {
1327
+ // Full URL
1328
+ if (src.startsWith('http')) {
1329
+ return new URL(src).origin;
1330
+ }
1331
+ // Relative path
1332
+ if (src.startsWith('/')) {
1333
+ return window.location.origin;
1334
+ }
1335
+ // Protocol-relative
1336
+ if (src.startsWith('//')) {
1337
+ return new URL(src, window.location.origin).origin;
1338
+ }
1339
+ // Just return current origin for anything else
1340
+ return window.location.origin;
1341
+ }
1342
+ catch {
1343
+ return window.location.origin;
1344
+ }
1345
+ }
1346
+ // ... rest of helpers (isValidUrl, getSafeOrigin) same as before
1347
+ ngOnDestroy() {
1348
+ this.destroy$.next();
1349
+ this.destroy$.complete();
1350
+ window.removeEventListener('message', this.handleIframeMessage);
1351
+ }
1352
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: TaonIframeSyncComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1353
+ 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: `
1354
+ <!-- Hidden by default, shown only when ready -->
1355
+ <iframe
1356
+ #iframe
1357
+ [src]="safeSrc"
1358
+ [style.display]="isReady ? 'block' : 'none'"
1359
+ [style.visibility]="isReady ? 'visible' : 'hidden'"
1360
+ [style.opacity]="isReady ? '1' : '0'"
1361
+ frameborder="0"
1362
+ style="width:100%; height:100%; border:none; transition: opacity 0.2s ease;"
1363
+ (load)="onIframeLoad()"
1364
+ allow="clipboard-write"></iframe>
1365
+
1366
+ <!-- Optional: nice loading placeholder -->
1367
+ <div
1368
+ *ngIf="!isReady"
1369
+ class="iframe-loading-placeholder"
1370
+ [style.background]="loaderBackgroundColor"
1371
+ >
1372
+ <div class="spinner"></div>
1373
+ <p>Loading documentation...</p>
1374
+ </div>
1375
+ `, 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 }); }
1376
+ }
1377
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: TaonIframeSyncComponent, decorators: [{
1378
+ type: Component,
1379
+ args: [{ selector: 'taon-iframe-sync', template: `
1380
+ <!-- Hidden by default, shown only when ready -->
1381
+ <iframe
1382
+ #iframe
1383
+ [src]="safeSrc"
1384
+ [style.display]="isReady ? 'block' : 'none'"
1385
+ [style.visibility]="isReady ? 'visible' : 'hidden'"
1386
+ [style.opacity]="isReady ? '1' : '0'"
1387
+ frameborder="0"
1388
+ style="width:100%; height:100%; border:none; transition: opacity 0.2s ease;"
1389
+ (load)="onIframeLoad()"
1390
+ allow="clipboard-write"></iframe>
1391
+
1392
+ <!-- Optional: nice loading placeholder -->
1393
+ <div
1394
+ *ngIf="!isReady"
1395
+ class="iframe-loading-placeholder"
1396
+ [style.background]="loaderBackgroundColor"
1397
+ >
1398
+ <div class="spinner"></div>
1399
+ <p>Loading documentation...</p>
1400
+ </div>
1401
+ `, 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"] }]
1402
+ }], propDecorators: { loaderBackgroundColor: [{
1403
+ type: Input
1404
+ }], iframeRef: [{
1405
+ type: ViewChild,
1406
+ args: ['iframe']
1407
+ }], iframeSrc: [{
1408
+ type: Input,
1409
+ args: [{ required: true }]
1410
+ }], queryParamKey: [{
1411
+ type: Input
1412
+ }], initialPath: [{
1413
+ type: Input
1414
+ }], targetOrigin: [{
1415
+ type: Input
1416
+ }] } });
1417
+
1233
1418
  //#region @browser
1234
1419
  //#endregion
1235
1420
 
@@ -1261,5 +1446,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImpo
1261
1446
  * Generated bundle index. Do not edit.
1262
1447
  */
1263
1448
 
1264
- export { SafePipe, TaonAdminModeConfigurationComponent, TaonBootstrapNavbarComponent, TaonFullMaterialModule, TaonGithubForkMeCornerComponent, TaonGithubForkMeCornerModule, TaonGithubForkMeRibbonComponent, TaonGithubForkMeRibbonModule, TaonInjectHTMLDirective, TaonLongPress, TaonNotificationOptions, TaonNotificationsComponent, TaonNotificationsModule, TaonNotificationsService, TaonProgressBarComponent, TaonProgressBarModule, TaonSessionPasscodeComponent, TaonTableComponent, TaonTableModule };
1449
+ export { SafePipe, TaonAdminModeConfigurationComponent, TaonBootstrapNavbarComponent, TaonFullMaterialModule, TaonGithubForkMeCornerComponent, TaonGithubForkMeCornerModule, TaonGithubForkMeRibbonComponent, TaonGithubForkMeRibbonModule, TaonIframeSyncComponent, TaonInjectHTMLDirective, TaonLongPress, TaonNotificationOptions, TaonNotificationsComponent, TaonNotificationsModule, TaonNotificationsService, TaonProgressBarComponent, TaonProgressBarModule, TaonSessionPasscodeComponent, TaonTableComponent, TaonTableModule };
1265
1450
  //# sourceMappingURL=taon-ui.mjs.map