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/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