@praxisui/tabs 8.0.0-beta.2 → 8.0.0-beta.20
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/README.md +76 -12
- package/fesm2022/praxisui-tabs.mjs +545 -87
- package/index.d.ts +34 -3
- package/package.json +8 -4
- package/src/lib/praxis-tabs-config-editor.json-api.md +597 -0
- package/src/lib/praxis-tabs.json-api.md +976 -0
- package/src/lib/quick-setup/tabs-quick-setup.json-api.md +491 -0
|
@@ -784,6 +784,7 @@ class PraxisTabsConfigEditor {
|
|
|
784
784
|
this.editedConfig.tabs.push({
|
|
785
785
|
id: `tab${(this.editedConfig.tabs.length + 1)}`,
|
|
786
786
|
textLabel: this.t('defaults.newTabLabel', 'New Tab'),
|
|
787
|
+
visible: true,
|
|
787
788
|
});
|
|
788
789
|
this.onAppearanceChange();
|
|
789
790
|
}
|
|
@@ -859,6 +860,7 @@ class PraxisTabsConfigEditor {
|
|
|
859
860
|
this.nav.links.push({
|
|
860
861
|
id: `link${this.nav.links.length + 1}`,
|
|
861
862
|
label: this.t('defaults.newLinkLabel', 'New Link'),
|
|
863
|
+
visible: true,
|
|
862
864
|
});
|
|
863
865
|
this.onAppearanceChange();
|
|
864
866
|
}
|
|
@@ -1293,6 +1295,9 @@ class PraxisTabsConfigEditor {
|
|
|
1293
1295
|
<mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.label', 'Rotulo') }}</mat-label>
|
|
1294
1296
|
<input matInput [(ngModel)]="tab.textLabel" (ngModelChange)="onAppearanceChange()" />
|
|
1295
1297
|
</mat-form-field>
|
|
1298
|
+
<mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.icon', 'Icone') }}</mat-label>
|
|
1299
|
+
<input matInput [(ngModel)]="tab.icon" (ngModelChange)="onAppearanceChange()" />
|
|
1300
|
+
</mat-form-field>
|
|
1296
1301
|
<mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.labelClass', 'Classe do rotulo') }}</mat-label>
|
|
1297
1302
|
<input matInput [(ngModel)]="tab.labelClass" (ngModelChange)="onAppearanceChange()" />
|
|
1298
1303
|
</mat-form-field>
|
|
@@ -1306,7 +1311,10 @@ class PraxisTabsConfigEditor {
|
|
|
1306
1311
|
<input matInput [(ngModel)]="tab.ariaLabelledby" (ngModelChange)="onAppearanceChange()" />
|
|
1307
1312
|
</mat-form-field>
|
|
1308
1313
|
</div>
|
|
1309
|
-
<
|
|
1314
|
+
<div class="editor-row">
|
|
1315
|
+
<mat-slide-toggle [(ngModel)]="tab.disabled" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.disabled', 'Desativada') }}</mat-slide-toggle>
|
|
1316
|
+
<mat-slide-toggle [ngModel]="tab.visible !== false" (ngModelChange)="tab.visible = $event; onAppearanceChange()">{{ t('editor.toggles.visible', 'Visivel') }}</mat-slide-toggle>
|
|
1317
|
+
</div>
|
|
1310
1318
|
|
|
1311
1319
|
<!-- Widgets (componentes dinâmicos) -->
|
|
1312
1320
|
<div class="editor-divider editor-grid">
|
|
@@ -1384,9 +1392,13 @@ class PraxisTabsConfigEditor {
|
|
|
1384
1392
|
<mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.label', 'Rotulo') }}</mat-label>
|
|
1385
1393
|
<input matInput [(ngModel)]="l.label" (ngModelChange)="onAppearanceChange()" />
|
|
1386
1394
|
</mat-form-field>
|
|
1395
|
+
<mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.icon', 'Icone') }}</mat-label>
|
|
1396
|
+
<input matInput [(ngModel)]="l.icon" (ngModelChange)="onAppearanceChange()" />
|
|
1397
|
+
</mat-form-field>
|
|
1387
1398
|
</div>
|
|
1388
1399
|
<div class="editor-row">
|
|
1389
1400
|
<mat-slide-toggle [(ngModel)]="l.disabled" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.linkDisabled', 'Desativado') }}</mat-slide-toggle>
|
|
1401
|
+
<mat-slide-toggle [ngModel]="l.visible !== false" (ngModelChange)="l.visible = $event; onAppearanceChange()">{{ t('editor.toggles.visible', 'Visivel') }}</mat-slide-toggle>
|
|
1390
1402
|
<mat-slide-toggle [(ngModel)]="l.disableRipple" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.disableRipple', 'Sem ripple') }}</mat-slide-toggle>
|
|
1391
1403
|
<mat-slide-toggle [(ngModel)]="l.fitInkBarToContent" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.fitInkBarToContent', 'Indicador ajustado ao conteudo') }}</mat-slide-toggle>
|
|
1392
1404
|
</div>
|
|
@@ -1797,6 +1809,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
1797
1809
|
<mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.label', 'Rotulo') }}</mat-label>
|
|
1798
1810
|
<input matInput [(ngModel)]="tab.textLabel" (ngModelChange)="onAppearanceChange()" />
|
|
1799
1811
|
</mat-form-field>
|
|
1812
|
+
<mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.icon', 'Icone') }}</mat-label>
|
|
1813
|
+
<input matInput [(ngModel)]="tab.icon" (ngModelChange)="onAppearanceChange()" />
|
|
1814
|
+
</mat-form-field>
|
|
1800
1815
|
<mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.labelClass', 'Classe do rotulo') }}</mat-label>
|
|
1801
1816
|
<input matInput [(ngModel)]="tab.labelClass" (ngModelChange)="onAppearanceChange()" />
|
|
1802
1817
|
</mat-form-field>
|
|
@@ -1810,7 +1825,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
1810
1825
|
<input matInput [(ngModel)]="tab.ariaLabelledby" (ngModelChange)="onAppearanceChange()" />
|
|
1811
1826
|
</mat-form-field>
|
|
1812
1827
|
</div>
|
|
1813
|
-
<
|
|
1828
|
+
<div class="editor-row">
|
|
1829
|
+
<mat-slide-toggle [(ngModel)]="tab.disabled" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.disabled', 'Desativada') }}</mat-slide-toggle>
|
|
1830
|
+
<mat-slide-toggle [ngModel]="tab.visible !== false" (ngModelChange)="tab.visible = $event; onAppearanceChange()">{{ t('editor.toggles.visible', 'Visivel') }}</mat-slide-toggle>
|
|
1831
|
+
</div>
|
|
1814
1832
|
|
|
1815
1833
|
<!-- Widgets (componentes dinâmicos) -->
|
|
1816
1834
|
<div class="editor-divider editor-grid">
|
|
@@ -1888,9 +1906,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
1888
1906
|
<mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.label', 'Rotulo') }}</mat-label>
|
|
1889
1907
|
<input matInput [(ngModel)]="l.label" (ngModelChange)="onAppearanceChange()" />
|
|
1890
1908
|
</mat-form-field>
|
|
1909
|
+
<mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.icon', 'Icone') }}</mat-label>
|
|
1910
|
+
<input matInput [(ngModel)]="l.icon" (ngModelChange)="onAppearanceChange()" />
|
|
1911
|
+
</mat-form-field>
|
|
1891
1912
|
</div>
|
|
1892
1913
|
<div class="editor-row">
|
|
1893
1914
|
<mat-slide-toggle [(ngModel)]="l.disabled" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.linkDisabled', 'Desativado') }}</mat-slide-toggle>
|
|
1915
|
+
<mat-slide-toggle [ngModel]="l.visible !== false" (ngModelChange)="l.visible = $event; onAppearanceChange()">{{ t('editor.toggles.visible', 'Visivel') }}</mat-slide-toggle>
|
|
1894
1916
|
<mat-slide-toggle [(ngModel)]="l.disableRipple" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.disableRipple', 'Sem ripple') }}</mat-slide-toggle>
|
|
1895
1917
|
<mat-slide-toggle [(ngModel)]="l.fitInkBarToContent" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.fitInkBarToContent', 'Indicador ajustado ao conteudo') }}</mat-slide-toggle>
|
|
1896
1918
|
</div>
|
|
@@ -2372,6 +2394,12 @@ class PraxisTabs {
|
|
|
2372
2394
|
config = null;
|
|
2373
2395
|
tabsId;
|
|
2374
2396
|
componentInstanceId;
|
|
2397
|
+
set selectedIndex(index) {
|
|
2398
|
+
if (index == null)
|
|
2399
|
+
return;
|
|
2400
|
+
this.controlledSelectedIndex = index;
|
|
2401
|
+
this.applySelectedIndex(index, false, false);
|
|
2402
|
+
}
|
|
2375
2403
|
enableCustomization = false;
|
|
2376
2404
|
form = null;
|
|
2377
2405
|
context = null;
|
|
@@ -2388,6 +2416,7 @@ class PraxisTabs {
|
|
|
2388
2416
|
selectedIndexSignal = signal(0, ...(ngDevMode ? [{ debugName: "selectedIndexSignal" }] : []));
|
|
2389
2417
|
groupLoaded = new Set();
|
|
2390
2418
|
navLoaded = new Set();
|
|
2419
|
+
controlledSelectedIndex;
|
|
2391
2420
|
destroy$ = new Subject();
|
|
2392
2421
|
widgetDefinitionCache = new WeakMap();
|
|
2393
2422
|
ngOnInit() {
|
|
@@ -2400,6 +2429,7 @@ class PraxisTabs {
|
|
|
2400
2429
|
this.config = stored;
|
|
2401
2430
|
}
|
|
2402
2431
|
this.syncSelectionFromConfig();
|
|
2432
|
+
this.reapplyControlledSelectedIndex();
|
|
2403
2433
|
});
|
|
2404
2434
|
}
|
|
2405
2435
|
}
|
|
@@ -2409,6 +2439,7 @@ class PraxisTabs {
|
|
|
2409
2439
|
this.syncSelectionFromConfig();
|
|
2410
2440
|
// Persist when tabsId provided
|
|
2411
2441
|
this.persistConfig(this.config);
|
|
2442
|
+
this.reapplyControlledSelectedIndex();
|
|
2412
2443
|
}
|
|
2413
2444
|
}
|
|
2414
2445
|
ngOnDestroy() {
|
|
@@ -2427,23 +2458,39 @@ class PraxisTabs {
|
|
|
2427
2458
|
getNavActive(i) {
|
|
2428
2459
|
return this.currentNavIndex() === i;
|
|
2429
2460
|
}
|
|
2461
|
+
visibleNavLinkEntries() {
|
|
2462
|
+
return (this.config?.nav?.links ?? [])
|
|
2463
|
+
.map((link, index) => ({ link, index }))
|
|
2464
|
+
.filter((entry) => entry.link.visible !== false);
|
|
2465
|
+
}
|
|
2466
|
+
visibleTabEntries() {
|
|
2467
|
+
return (this.config?.tabs ?? [])
|
|
2468
|
+
.map((tab, index) => ({ tab, index }))
|
|
2469
|
+
.filter((entry) => entry.tab.visible !== false);
|
|
2470
|
+
}
|
|
2471
|
+
selectedVisibleNavIndex() {
|
|
2472
|
+
const entries = this.visibleNavLinkEntries();
|
|
2473
|
+
const index = entries.findIndex((entry) => entry.index === this.currentNavIndex());
|
|
2474
|
+
return index >= 0 ? index : 0;
|
|
2475
|
+
}
|
|
2476
|
+
selectedVisibleTabIndex() {
|
|
2477
|
+
const entries = this.visibleTabEntries();
|
|
2478
|
+
const index = entries.findIndex((entry) => entry.index === this.selectedIndexSignal());
|
|
2479
|
+
return index >= 0 ? index : 0;
|
|
2480
|
+
}
|
|
2481
|
+
onVisibleTabIndexChange(index) {
|
|
2482
|
+
const entry = this.visibleTabEntries()[index];
|
|
2483
|
+
if (!entry)
|
|
2484
|
+
return;
|
|
2485
|
+
this.onSelectedIndexChange(entry.index);
|
|
2486
|
+
}
|
|
2430
2487
|
onNavClick(i) {
|
|
2431
2488
|
if (!this.config?.nav?.links?.length)
|
|
2432
2489
|
return;
|
|
2433
2490
|
const linksCount = this.config.nav.links.length;
|
|
2434
2491
|
if (i < 0 || i >= linksCount)
|
|
2435
2492
|
return;
|
|
2436
|
-
this.
|
|
2437
|
-
this.config = produce(this.config, (draft) => {
|
|
2438
|
-
if (!draft.nav)
|
|
2439
|
-
return;
|
|
2440
|
-
draft.nav.selectedIndex = i;
|
|
2441
|
-
});
|
|
2442
|
-
this.persistConfig(this.config);
|
|
2443
|
-
// Lazy: mark as loaded
|
|
2444
|
-
this.navLoaded.add(i);
|
|
2445
|
-
// Emit as index change for consumers to track
|
|
2446
|
-
this.selectedIndexChange.emit(i);
|
|
2493
|
+
this.applySelectedIndex(i, true);
|
|
2447
2494
|
}
|
|
2448
2495
|
onNavDrop(event) {
|
|
2449
2496
|
if (!this.config?.nav?.links)
|
|
@@ -2491,10 +2538,39 @@ class PraxisTabs {
|
|
|
2491
2538
|
});
|
|
2492
2539
|
this.persistConfig(this.config);
|
|
2493
2540
|
}
|
|
2541
|
+
onVisibleNavDrop(event) {
|
|
2542
|
+
const entries = this.visibleNavLinkEntries();
|
|
2543
|
+
const previous = entries[event.previousIndex];
|
|
2544
|
+
const current = entries[event.currentIndex];
|
|
2545
|
+
if (!previous || !current)
|
|
2546
|
+
return;
|
|
2547
|
+
this.onNavDrop({
|
|
2548
|
+
...event,
|
|
2549
|
+
previousIndex: previous.index,
|
|
2550
|
+
currentIndex: current.index,
|
|
2551
|
+
});
|
|
2552
|
+
}
|
|
2494
2553
|
onSelectedIndexChange(index) {
|
|
2554
|
+
this.applySelectedIndex(index, true);
|
|
2555
|
+
}
|
|
2556
|
+
applySelectedIndex(index, emit, persist = true) {
|
|
2557
|
+
if (this.isNavMode() && this.config) {
|
|
2558
|
+
const selected = this.clampIndex(index, this.config?.nav?.links?.length ?? 0);
|
|
2559
|
+
this.currentNavIndex.set(selected);
|
|
2560
|
+
this.config = produce(this.config, (draft) => {
|
|
2561
|
+
draft.nav.selectedIndex = selected;
|
|
2562
|
+
});
|
|
2563
|
+
if (persist) {
|
|
2564
|
+
this.persistConfig(this.config);
|
|
2565
|
+
}
|
|
2566
|
+
this.navLoaded.add(selected);
|
|
2567
|
+
if (emit) {
|
|
2568
|
+
this.selectedIndexChange.emit(selected);
|
|
2569
|
+
}
|
|
2570
|
+
return;
|
|
2571
|
+
}
|
|
2495
2572
|
const selected = this.clampIndex(index, this.config?.tabs?.length ?? 0);
|
|
2496
2573
|
this.selectedIndexSignal.set(selected);
|
|
2497
|
-
// Update config immutably
|
|
2498
2574
|
if (this.config) {
|
|
2499
2575
|
this.config = produce(this.config, (draft) => {
|
|
2500
2576
|
if (!draft.group) {
|
|
@@ -2504,11 +2580,20 @@ class PraxisTabs {
|
|
|
2504
2580
|
draft.group.selectedIndex = selected;
|
|
2505
2581
|
}
|
|
2506
2582
|
});
|
|
2507
|
-
|
|
2583
|
+
if (persist) {
|
|
2584
|
+
this.persistConfig(this.config);
|
|
2585
|
+
}
|
|
2508
2586
|
}
|
|
2509
|
-
// Lazy: mark as loaded
|
|
2510
2587
|
this.groupLoaded.add(selected);
|
|
2511
|
-
|
|
2588
|
+
if (emit) {
|
|
2589
|
+
this.selectedIndexChange.emit(selected);
|
|
2590
|
+
}
|
|
2591
|
+
}
|
|
2592
|
+
reapplyControlledSelectedIndex() {
|
|
2593
|
+
if (this.controlledSelectedIndex == null) {
|
|
2594
|
+
return;
|
|
2595
|
+
}
|
|
2596
|
+
this.applySelectedIndex(this.controlledSelectedIndex, false, false);
|
|
2512
2597
|
}
|
|
2513
2598
|
closeTab(index) {
|
|
2514
2599
|
if (!this.config?.tabs)
|
|
@@ -2691,19 +2776,26 @@ class PraxisTabs {
|
|
|
2691
2776
|
return id ? `tabs:${id}` : null;
|
|
2692
2777
|
}
|
|
2693
2778
|
syncSelectionFromConfig() {
|
|
2694
|
-
this.groupLoaded.clear();
|
|
2695
|
-
this.navLoaded.clear();
|
|
2696
2779
|
const tabsLength = this.config?.tabs?.length ?? 0;
|
|
2697
2780
|
const linksLength = this.config?.nav?.links?.length ?? 0;
|
|
2698
2781
|
const groupIndex = this.clampIndex(this.config?.group?.selectedIndex, tabsLength);
|
|
2699
2782
|
const navIndex = this.clampIndex(this.config?.nav?.selectedIndex, linksLength);
|
|
2700
2783
|
this.selectedIndexSignal.set(groupIndex);
|
|
2701
2784
|
this.currentNavIndex.set(navIndex);
|
|
2785
|
+
this.pruneLoadedIndexes(this.groupLoaded, tabsLength);
|
|
2786
|
+
this.pruneLoadedIndexes(this.navLoaded, linksLength);
|
|
2702
2787
|
if (tabsLength > 0)
|
|
2703
2788
|
this.groupLoaded.add(groupIndex);
|
|
2704
2789
|
if (linksLength > 0)
|
|
2705
2790
|
this.navLoaded.add(navIndex);
|
|
2706
2791
|
}
|
|
2792
|
+
pruneLoadedIndexes(indexes, size) {
|
|
2793
|
+
for (const index of Array.from(indexes)) {
|
|
2794
|
+
if (index < 0 || index >= size) {
|
|
2795
|
+
indexes.delete(index);
|
|
2796
|
+
}
|
|
2797
|
+
}
|
|
2798
|
+
}
|
|
2707
2799
|
persistConfig(config) {
|
|
2708
2800
|
const key = this.storageKey();
|
|
2709
2801
|
if (!key || !config)
|
|
@@ -2764,10 +2856,16 @@ class PraxisTabs {
|
|
|
2764
2856
|
return !this.isLazy() || this.navLoaded.has(index) || this.currentNavIndex() === index;
|
|
2765
2857
|
}
|
|
2766
2858
|
isEmptyGlobal() {
|
|
2767
|
-
const hasTabs =
|
|
2768
|
-
const hasLinks =
|
|
2859
|
+
const hasTabs = this.visibleTabEntries().length > 0;
|
|
2860
|
+
const hasLinks = this.visibleNavLinkEntries().length > 0;
|
|
2769
2861
|
return !(hasTabs || hasLinks);
|
|
2770
2862
|
}
|
|
2863
|
+
trackVisibleNavLink(index, entry) {
|
|
2864
|
+
return entry.link.id || `${entry.link.label || 'nav-link'}:${entry.index ?? index}`;
|
|
2865
|
+
}
|
|
2866
|
+
trackVisibleTab(index, entry) {
|
|
2867
|
+
return entry.tab.id || entry.tab.textLabel || `tab:${entry.index ?? index}`;
|
|
2868
|
+
}
|
|
2771
2869
|
trackNavLink(index, link) {
|
|
2772
2870
|
return link.id || `${link.label || 'nav-link'}:${index}`;
|
|
2773
2871
|
}
|
|
@@ -2952,7 +3050,7 @@ class PraxisTabs {
|
|
|
2952
3050
|
return JSON.parse(JSON.stringify(widget));
|
|
2953
3051
|
}
|
|
2954
3052
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisTabs, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2955
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: PraxisTabs, isStandalone: true, selector: "praxis-tabs", inputs: { config: "config", tabsId: "tabsId", componentInstanceId: "componentInstanceId", enableCustomization: "enableCustomization", form: "form", context: "context" }, outputs: { animationDone: "animationDone", focusChange: "focusChange", selectedIndexChange: "selectedIndexChange", selectedTabChange: "selectedTabChange", indexFocused: "indexFocused", selectFocusedIndex: "selectFocusedIndex", widgetEvent: "widgetEvent" }, providers: [providePraxisI18nConfig(PRAXIS_TABS_I18N_CONFIG)], usesOnChanges: true, ngImport: i0, template: `
|
|
3053
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: PraxisTabs, isStandalone: true, selector: "praxis-tabs", inputs: { config: "config", tabsId: "tabsId", componentInstanceId: "componentInstanceId", selectedIndex: "selectedIndex", enableCustomization: "enableCustomization", form: "form", context: "context" }, outputs: { animationDone: "animationDone", focusChange: "focusChange", selectedIndexChange: "selectedIndexChange", selectedTabChange: "selectedTabChange", indexFocused: "indexFocused", selectFocusedIndex: "selectFocusedIndex", widgetEvent: "widgetEvent" }, providers: [providePraxisI18nConfig(PRAXIS_TABS_I18N_CONFIG)], usesOnChanges: true, ngImport: i0, template: `
|
|
2956
3054
|
<div
|
|
2957
3055
|
class="praxis-tabs-root"
|
|
2958
3056
|
[class.density-compact]="config?.appearance?.density === 'compact'"
|
|
@@ -2995,13 +3093,13 @@ class PraxisTabs {
|
|
|
2995
3093
|
cdkDropList
|
|
2996
3094
|
cdkDropListOrientation="horizontal"
|
|
2997
3095
|
[cdkDropListDisabled]="!config?.behavior?.reorderable"
|
|
2998
|
-
(cdkDropListDropped)="
|
|
3096
|
+
(cdkDropListDropped)="onVisibleNavDrop($event)"
|
|
2999
3097
|
[disablePagination]="config?.nav?.disablePagination"
|
|
3000
3098
|
[fitInkBarToContent]="config?.nav?.fitInkBarToContent"
|
|
3001
3099
|
[mat-stretch-tabs]="config?.nav?.stretchTabs"
|
|
3002
3100
|
[color]="config?.nav?.color"
|
|
3003
3101
|
[backgroundColor]="config?.nav?.backgroundColor"
|
|
3004
|
-
[selectedIndex]="
|
|
3102
|
+
[selectedIndex]="selectedVisibleNavIndex()"
|
|
3005
3103
|
[attr.aria-label]="config?.nav?.ariaLabel || config?.group?.ariaLabel || null"
|
|
3006
3104
|
[attr.aria-labelledby]="config?.nav?.ariaLabelledby || config?.group?.ariaLabelledby || null"
|
|
3007
3105
|
[animationDuration]="effectiveAnimationDuration()"
|
|
@@ -3010,21 +3108,22 @@ class PraxisTabs {
|
|
|
3010
3108
|
>
|
|
3011
3109
|
<a
|
|
3012
3110
|
mat-tab-link
|
|
3013
|
-
*ngFor="let
|
|
3111
|
+
*ngFor="let entry of visibleNavLinkEntries(); let i = index; trackBy: trackVisibleNavLink"
|
|
3014
3112
|
cdkDrag
|
|
3015
3113
|
[cdkDragDisabled]="!config?.behavior?.reorderable"
|
|
3016
3114
|
cdkDragLockAxis="x"
|
|
3017
|
-
[active]="getNavActive(
|
|
3018
|
-
[disabled]="link.disabled"
|
|
3019
|
-
[disableRipple]="config?.nav?.disableRipple || link.disableRipple"
|
|
3020
|
-
[fitInkBarToContent]="link.fitInkBarToContent || false"
|
|
3021
|
-
[id]="link.id || ''"
|
|
3022
|
-
(click)="onNavClick(
|
|
3115
|
+
[active]="getNavActive(entry.index)"
|
|
3116
|
+
[disabled]="entry.link.disabled"
|
|
3117
|
+
[disableRipple]="config?.nav?.disableRipple || entry.link.disableRipple"
|
|
3118
|
+
[fitInkBarToContent]="entry.link.fitInkBarToContent || false"
|
|
3119
|
+
[id]="entry.link.id || ''"
|
|
3120
|
+
(click)="onNavClick(entry.index)"
|
|
3023
3121
|
>
|
|
3024
3122
|
<span *ngIf="config?.behavior?.reorderable" class="drag-handle" cdkDragHandle>
|
|
3025
3123
|
<mat-icon fontIcon="drag_indicator"></mat-icon>
|
|
3026
3124
|
</span>
|
|
3027
|
-
|
|
3125
|
+
<mat-icon *ngIf="entry.link.icon" class="tab-label-icon" [praxisIcon]="entry.link.icon"></mat-icon>
|
|
3126
|
+
{{ entry.link.label }}
|
|
3028
3127
|
</a>
|
|
3029
3128
|
</nav>
|
|
3030
3129
|
|
|
@@ -3074,7 +3173,7 @@ class PraxisTabs {
|
|
|
3074
3173
|
[fitInkBarToContent]="config?.group?.fitInkBarToContent"
|
|
3075
3174
|
[headerPosition]="config?.group?.headerPosition ?? 'above'"
|
|
3076
3175
|
[preserveContent]="config?.group?.preserveContent"
|
|
3077
|
-
[selectedIndex]="
|
|
3176
|
+
[selectedIndex]="selectedVisibleTabIndex()"
|
|
3078
3177
|
[mat-stretch-tabs]="config?.group?.stretchTabs"
|
|
3079
3178
|
[mat-align-tabs]="config?.group?.alignTabs || 'start'"
|
|
3080
3179
|
[color]="config?.group?.color"
|
|
@@ -3084,26 +3183,27 @@ class PraxisTabs {
|
|
|
3084
3183
|
[attr.aria-labelledby]="config?.group?.ariaLabelledby || null"
|
|
3085
3184
|
(animationDone)="animationDone.emit()"
|
|
3086
3185
|
(focusChange)="focusChange.emit($event)"
|
|
3087
|
-
(selectedIndexChange)="
|
|
3186
|
+
(selectedIndexChange)="onVisibleTabIndexChange($event)"
|
|
3088
3187
|
(selectedTabChange)="selectedTabChange.emit($event)"
|
|
3089
3188
|
class="praxis-tabs-group"
|
|
3090
3189
|
>
|
|
3091
3190
|
<mat-tab
|
|
3092
|
-
*ngFor="let
|
|
3093
|
-
[disabled]="tab.disabled"
|
|
3094
|
-
[labelClass]="tab.labelClass ?? ''"
|
|
3095
|
-
[bodyClass]="tab.bodyClass ?? ''"
|
|
3096
|
-
[id]="tab.id || ''"
|
|
3097
|
-
[attr.aria-label]="tab.ariaLabel || null"
|
|
3098
|
-
[attr.aria-labelledby]="tab.ariaLabelledby || null"
|
|
3191
|
+
*ngFor="let entry of visibleTabEntries(); let i = index; trackBy: trackVisibleTab"
|
|
3192
|
+
[disabled]="entry.tab.disabled"
|
|
3193
|
+
[labelClass]="entry.tab.labelClass ?? ''"
|
|
3194
|
+
[bodyClass]="entry.tab.bodyClass ?? ''"
|
|
3195
|
+
[id]="entry.tab.id || ''"
|
|
3196
|
+
[attr.aria-label]="entry.tab.ariaLabel || null"
|
|
3197
|
+
[attr.aria-labelledby]="entry.tab.ariaLabelledby || null"
|
|
3099
3198
|
>
|
|
3100
3199
|
<ng-template mat-tab-label>
|
|
3101
|
-
<
|
|
3200
|
+
<mat-icon *ngIf="entry.tab.icon" class="tab-label-icon" [praxisIcon]="entry.tab.icon"></mat-icon>
|
|
3201
|
+
<span>{{ entry.tab.textLabel }}</span>
|
|
3102
3202
|
<button
|
|
3103
3203
|
*ngIf="config?.behavior?.closeable"
|
|
3104
3204
|
mat-icon-button
|
|
3105
3205
|
type="button"
|
|
3106
|
-
(click)="closeTab(
|
|
3206
|
+
(click)="closeTab(entry.index); $event.stopPropagation()"
|
|
3107
3207
|
(keydown.enter)="$event.stopPropagation()"
|
|
3108
3208
|
(keydown.space)="$event.stopPropagation()"
|
|
3109
3209
|
[attr.aria-label]="t('chrome.closeTab', 'Fechar aba')"
|
|
@@ -3114,10 +3214,10 @@ class PraxisTabs {
|
|
|
3114
3214
|
<button
|
|
3115
3215
|
mat-icon-button
|
|
3116
3216
|
type="button"
|
|
3117
|
-
(click)="moveTab(
|
|
3217
|
+
(click)="moveTab(entry.index, -1); $event.stopPropagation()"
|
|
3118
3218
|
(keydown.enter)="$event.stopPropagation()"
|
|
3119
3219
|
(keydown.space)="$event.stopPropagation()"
|
|
3120
|
-
[disabled]="
|
|
3220
|
+
[disabled]="entry.index===0"
|
|
3121
3221
|
[attr.aria-label]="t('chrome.moveTabLeft', 'Mover aba para esquerda')"
|
|
3122
3222
|
>
|
|
3123
3223
|
<mat-icon fontIcon="arrow_back"></mat-icon>
|
|
@@ -3125,10 +3225,10 @@ class PraxisTabs {
|
|
|
3125
3225
|
<button
|
|
3126
3226
|
mat-icon-button
|
|
3127
3227
|
type="button"
|
|
3128
|
-
(click)="moveTab(
|
|
3228
|
+
(click)="moveTab(entry.index, 1); $event.stopPropagation()"
|
|
3129
3229
|
(keydown.enter)="$event.stopPropagation()"
|
|
3130
3230
|
(keydown.space)="$event.stopPropagation()"
|
|
3131
|
-
[disabled]="
|
|
3231
|
+
[disabled]="entry.index===(config?.tabs?.length||1)-1"
|
|
3132
3232
|
[attr.aria-label]="t('chrome.moveTabRight', 'Mover aba para direita')"
|
|
3133
3233
|
>
|
|
3134
3234
|
<mat-icon fontIcon="arrow_forward"></mat-icon>
|
|
@@ -3137,20 +3237,20 @@ class PraxisTabs {
|
|
|
3137
3237
|
</ng-template>
|
|
3138
3238
|
|
|
3139
3239
|
<ng-template matTabContent>
|
|
3140
|
-
<ng-container *ngIf="(tab.content?.length || tab.widgets?.length) && groupContentReady(
|
|
3141
|
-
<ng-container *ngIf="tab.content && form">
|
|
3240
|
+
<ng-container *ngIf="(entry.tab.content?.length || entry.tab.widgets?.length) && groupContentReady(entry.index); else emptyTab">
|
|
3241
|
+
<ng-container *ngIf="entry.tab.content && form">
|
|
3142
3242
|
<ng-container
|
|
3143
3243
|
dynamicFieldLoader
|
|
3144
|
-
[fields]="tab.content || []"
|
|
3244
|
+
[fields]="entry.tab.content || []"
|
|
3145
3245
|
[formGroup]="form!"
|
|
3146
3246
|
></ng-container>
|
|
3147
3247
|
</ng-container>
|
|
3148
|
-
<ng-container *ngIf="tab.widgets?.length">
|
|
3149
|
-
<ng-container *ngFor="let w of tab.widgets; let wi = index; trackBy: trackWidgetDefinition">
|
|
3248
|
+
<ng-container *ngIf="entry.tab.widgets?.length">
|
|
3249
|
+
<ng-container *ngFor="let w of entry.tab.widgets; let wi = index; trackBy: trackWidgetDefinition">
|
|
3150
3250
|
<ng-container
|
|
3151
3251
|
[dynamicWidgetLoader]="resolveWidgetDefinition(w)"
|
|
3152
3252
|
[context]="context || {}"
|
|
3153
|
-
(widgetEvent)="emitWidgetEvent(tabEventPath(tab.id,
|
|
3253
|
+
(widgetEvent)="emitWidgetEvent(tabEventPath(entry.tab.id, entry.index), $event)"
|
|
3154
3254
|
></ng-container>
|
|
3155
3255
|
</ng-container>
|
|
3156
3256
|
</ng-container>
|
|
@@ -3191,7 +3291,7 @@ class PraxisTabs {
|
|
|
3191
3291
|
<mat-icon [praxisIcon]="'restart_alt'"></mat-icon>
|
|
3192
3292
|
</button>
|
|
3193
3293
|
</div>
|
|
3194
|
-
`, isInline: true, styles: [".praxis-tabs-root{position:relative;display:block}.praxis-tabs-group.align-start .mat-mdc-tab-header{justify-content:flex-start}.praxis-tabs-group.align-center .mat-mdc-tab-header{justify-content:center}.praxis-tabs-group.align-end .mat-mdc-tab-header{justify-content:flex-end}.density-compact .mat-mdc-tab-body-content{padding:8px}.density-comfortable .mat-mdc-tab-body-content{padding:16px}.density-spacious .mat-mdc-tab-body-content{padding:24px}.tabs-ai-assistant{position:absolute;right:12px;bottom:72px;z-index:3}.edit-fab{position:absolute;right:12px;bottom:12px;z-index:2}.edit-fab-secondary{right:56px}.tab-empty{padding:16px;color:var(--md-sys-color-on-surface-variant);font-style:italic}.high-contrast{filter:contrast(1.2)}.reduce-motion{--mat-animation-duration: 0ms}.drag-handle{display:inline-flex;align-items:center;vertical-align:middle;margin-right:4px;cursor:grab}:host-context(.pdx-gridster-item) .praxis-tabs-root{display:flex;flex-direction:column;height:100%;min-height:0}:host-context(.pdx-gridster-item) .praxis-tabs-group,:host-context(.pdx-gridster-item) .mat-mdc-tab-group{flex:1 1 auto;min-height:0}:host-context(.pdx-gridster-item) .mat-mdc-tab-body-wrapper,:host-context(.pdx-gridster-item) .mat-mdc-tab-body-content{height:100%;min-height:0}:host-context(.pdx-gridster-item) .mat-mdc-tab-body-content{overflow:auto}:host-context(.pdx-gridster-item) .praxis-tabnav-content{flex:1 1 auto;min-height:0;overflow:auto}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "directive", type: i3$1.MatTabContent, selector: "[matTabContent]" }, { kind: "directive", type: i3$1.MatTabLabel, selector: "[mat-tab-label], [matTabLabel]" }, { kind: "component", type: i3$1.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i3$1.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "mat-align-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "component", type: i3$1.MatTabNav, selector: "[mat-tab-nav-bar]", inputs: ["fitInkBarToContent", "mat-stretch-tabs", "animationDuration", "backgroundColor", "disableRipple", "color", "tabPanel"], exportAs: ["matTabNavBar", "matTabNav"] }, { kind: "component", type: i3$1.MatTabNavPanel, selector: "mat-tab-nav-panel", inputs: ["id"], exportAs: ["matTabNavPanel"] }, { kind: "component", type: i3$1.MatTabLink, selector: "[mat-tab-link], [matTabLink]", inputs: ["active", "disabled", "disableRipple", "tabIndex", "id"], exportAs: ["matTabLink"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i7.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i11.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i6$1.MatMiniFabButton, selector: "button[mat-mini-fab], a[mat-mini-fab], button[matMiniFab], a[matMiniFab]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i6$1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i6$1.MatFabButton, selector: "button[mat-fab], a[mat-fab], button[matFab], a[matFab]", inputs: ["extended"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i10.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i10.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: i10.CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "component", type: EmptyStateCardComponent, selector: "praxis-empty-state-card", inputs: ["icon", "title", "description", "primaryAction", "secondaryActions", "inline", "tone"] }, { kind: "directive", type: DynamicFieldLoaderDirective, selector: "[dynamicFieldLoader]", inputs: ["fields", "formGroup", "enableExternalControlBinding", "itemTemplate", "debugTrace", "debugTraceLabel", "readonlyMode", "disabledMode", "presentationMode", "visible", "canvasMode"], outputs: ["componentsCreated", "fieldCreated", "fieldDestroyed", "renderError", "canvasMouseEnter", "canvasMouseLeave", "canvasClick"] }, { kind: "directive", type: DynamicWidgetLoaderDirective, selector: "[dynamicWidgetLoader]", inputs: ["dynamicWidgetLoader", "ownerWidgetKey", "context", "strictValidation", "autoWireOutputs"], outputs: ["widgetEvent", "widgetDiagnostic"], exportAs: ["dynamicWidgetLoader"] }, { kind: "component", type: PraxisAiAssistantComponent, selector: "praxis-ai-assistant", inputs: ["adapter", "riskPolicy", "allowManualPatchEdit"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
3294
|
+
`, isInline: true, styles: [".praxis-tabs-root{position:relative;display:block}.praxis-tabs-group.align-start .mat-mdc-tab-header{justify-content:flex-start}.praxis-tabs-group.align-center .mat-mdc-tab-header{justify-content:center}.praxis-tabs-group.align-end .mat-mdc-tab-header{justify-content:flex-end}.density-compact .mat-mdc-tab-body-content{padding:8px}.density-comfortable .mat-mdc-tab-body-content{padding:16px}.density-spacious .mat-mdc-tab-body-content{padding:24px}.tabs-ai-assistant{position:absolute;right:12px;bottom:72px;z-index:3}.edit-fab{position:absolute;right:12px;bottom:12px;z-index:2}.edit-fab-secondary{right:56px}.tab-empty{padding:16px;color:var(--md-sys-color-on-surface-variant);font-style:italic}.high-contrast{filter:contrast(1.2)}.reduce-motion{--mat-animation-duration: 0ms}.drag-handle{display:inline-flex;align-items:center;vertical-align:middle;margin-right:4px;cursor:grab}.tab-label-icon{font-size:18px;width:18px;height:18px;margin-right:6px;vertical-align:middle}:host-context(.pdx-gridster-item) .praxis-tabs-root{display:flex;flex-direction:column;height:100%;min-height:0}:host-context(.pdx-gridster-item) .praxis-tabs-group,:host-context(.pdx-gridster-item) .mat-mdc-tab-group{flex:1 1 auto;min-height:0}:host-context(.pdx-gridster-item) .mat-mdc-tab-body-wrapper,:host-context(.pdx-gridster-item) .mat-mdc-tab-body-content{height:100%;min-height:0}:host-context(.pdx-gridster-item) .mat-mdc-tab-body-content{overflow:auto}:host-context(.pdx-gridster-item) .praxis-tabnav-content{flex:1 1 auto;min-height:0;overflow:auto}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "directive", type: i3$1.MatTabContent, selector: "[matTabContent]" }, { kind: "directive", type: i3$1.MatTabLabel, selector: "[mat-tab-label], [matTabLabel]" }, { kind: "component", type: i3$1.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i3$1.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "mat-align-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "component", type: i3$1.MatTabNav, selector: "[mat-tab-nav-bar]", inputs: ["fitInkBarToContent", "mat-stretch-tabs", "animationDuration", "backgroundColor", "disableRipple", "color", "tabPanel"], exportAs: ["matTabNavBar", "matTabNav"] }, { kind: "component", type: i3$1.MatTabNavPanel, selector: "mat-tab-nav-panel", inputs: ["id"], exportAs: ["matTabNavPanel"] }, { kind: "component", type: i3$1.MatTabLink, selector: "[mat-tab-link], [matTabLink]", inputs: ["active", "disabled", "disableRipple", "tabIndex", "id"], exportAs: ["matTabLink"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i7.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i11.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i6$1.MatMiniFabButton, selector: "button[mat-mini-fab], a[mat-mini-fab], button[matMiniFab], a[matMiniFab]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i6$1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i6$1.MatFabButton, selector: "button[mat-fab], a[mat-fab], button[matFab], a[matFab]", inputs: ["extended"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i10.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i10.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: i10.CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "component", type: EmptyStateCardComponent, selector: "praxis-empty-state-card", inputs: ["icon", "title", "description", "primaryAction", "secondaryActions", "inline", "tone"] }, { kind: "directive", type: DynamicFieldLoaderDirective, selector: "[dynamicFieldLoader]", inputs: ["fields", "formGroup", "enableExternalControlBinding", "itemTemplate", "debugTrace", "debugTraceLabel", "readonlyMode", "disabledMode", "presentationMode", "visible", "canvasMode"], outputs: ["componentsCreated", "fieldCreated", "fieldDestroyed", "renderError", "canvasMouseEnter", "canvasMouseLeave", "canvasClick"] }, { kind: "directive", type: DynamicWidgetLoaderDirective, selector: "[dynamicWidgetLoader]", inputs: ["dynamicWidgetLoader", "ownerWidgetKey", "context", "strictValidation", "autoWireOutputs"], outputs: ["widgetEvent", "widgetDiagnostic"], exportAs: ["dynamicWidgetLoader"] }, { kind: "component", type: PraxisAiAssistantComponent, selector: "praxis-ai-assistant", inputs: ["adapter", "riskPolicy", "allowManualPatchEdit"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
3195
3295
|
}
|
|
3196
3296
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisTabs, decorators: [{
|
|
3197
3297
|
type: Component,
|
|
@@ -3251,13 +3351,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
3251
3351
|
cdkDropList
|
|
3252
3352
|
cdkDropListOrientation="horizontal"
|
|
3253
3353
|
[cdkDropListDisabled]="!config?.behavior?.reorderable"
|
|
3254
|
-
(cdkDropListDropped)="
|
|
3354
|
+
(cdkDropListDropped)="onVisibleNavDrop($event)"
|
|
3255
3355
|
[disablePagination]="config?.nav?.disablePagination"
|
|
3256
3356
|
[fitInkBarToContent]="config?.nav?.fitInkBarToContent"
|
|
3257
3357
|
[mat-stretch-tabs]="config?.nav?.stretchTabs"
|
|
3258
3358
|
[color]="config?.nav?.color"
|
|
3259
3359
|
[backgroundColor]="config?.nav?.backgroundColor"
|
|
3260
|
-
[selectedIndex]="
|
|
3360
|
+
[selectedIndex]="selectedVisibleNavIndex()"
|
|
3261
3361
|
[attr.aria-label]="config?.nav?.ariaLabel || config?.group?.ariaLabel || null"
|
|
3262
3362
|
[attr.aria-labelledby]="config?.nav?.ariaLabelledby || config?.group?.ariaLabelledby || null"
|
|
3263
3363
|
[animationDuration]="effectiveAnimationDuration()"
|
|
@@ -3266,21 +3366,22 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
3266
3366
|
>
|
|
3267
3367
|
<a
|
|
3268
3368
|
mat-tab-link
|
|
3269
|
-
*ngFor="let
|
|
3369
|
+
*ngFor="let entry of visibleNavLinkEntries(); let i = index; trackBy: trackVisibleNavLink"
|
|
3270
3370
|
cdkDrag
|
|
3271
3371
|
[cdkDragDisabled]="!config?.behavior?.reorderable"
|
|
3272
3372
|
cdkDragLockAxis="x"
|
|
3273
|
-
[active]="getNavActive(
|
|
3274
|
-
[disabled]="link.disabled"
|
|
3275
|
-
[disableRipple]="config?.nav?.disableRipple || link.disableRipple"
|
|
3276
|
-
[fitInkBarToContent]="link.fitInkBarToContent || false"
|
|
3277
|
-
[id]="link.id || ''"
|
|
3278
|
-
(click)="onNavClick(
|
|
3373
|
+
[active]="getNavActive(entry.index)"
|
|
3374
|
+
[disabled]="entry.link.disabled"
|
|
3375
|
+
[disableRipple]="config?.nav?.disableRipple || entry.link.disableRipple"
|
|
3376
|
+
[fitInkBarToContent]="entry.link.fitInkBarToContent || false"
|
|
3377
|
+
[id]="entry.link.id || ''"
|
|
3378
|
+
(click)="onNavClick(entry.index)"
|
|
3279
3379
|
>
|
|
3280
3380
|
<span *ngIf="config?.behavior?.reorderable" class="drag-handle" cdkDragHandle>
|
|
3281
3381
|
<mat-icon fontIcon="drag_indicator"></mat-icon>
|
|
3282
3382
|
</span>
|
|
3283
|
-
|
|
3383
|
+
<mat-icon *ngIf="entry.link.icon" class="tab-label-icon" [praxisIcon]="entry.link.icon"></mat-icon>
|
|
3384
|
+
{{ entry.link.label }}
|
|
3284
3385
|
</a>
|
|
3285
3386
|
</nav>
|
|
3286
3387
|
|
|
@@ -3330,7 +3431,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
3330
3431
|
[fitInkBarToContent]="config?.group?.fitInkBarToContent"
|
|
3331
3432
|
[headerPosition]="config?.group?.headerPosition ?? 'above'"
|
|
3332
3433
|
[preserveContent]="config?.group?.preserveContent"
|
|
3333
|
-
[selectedIndex]="
|
|
3434
|
+
[selectedIndex]="selectedVisibleTabIndex()"
|
|
3334
3435
|
[mat-stretch-tabs]="config?.group?.stretchTabs"
|
|
3335
3436
|
[mat-align-tabs]="config?.group?.alignTabs || 'start'"
|
|
3336
3437
|
[color]="config?.group?.color"
|
|
@@ -3340,26 +3441,27 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
3340
3441
|
[attr.aria-labelledby]="config?.group?.ariaLabelledby || null"
|
|
3341
3442
|
(animationDone)="animationDone.emit()"
|
|
3342
3443
|
(focusChange)="focusChange.emit($event)"
|
|
3343
|
-
(selectedIndexChange)="
|
|
3444
|
+
(selectedIndexChange)="onVisibleTabIndexChange($event)"
|
|
3344
3445
|
(selectedTabChange)="selectedTabChange.emit($event)"
|
|
3345
3446
|
class="praxis-tabs-group"
|
|
3346
3447
|
>
|
|
3347
3448
|
<mat-tab
|
|
3348
|
-
*ngFor="let
|
|
3349
|
-
[disabled]="tab.disabled"
|
|
3350
|
-
[labelClass]="tab.labelClass ?? ''"
|
|
3351
|
-
[bodyClass]="tab.bodyClass ?? ''"
|
|
3352
|
-
[id]="tab.id || ''"
|
|
3353
|
-
[attr.aria-label]="tab.ariaLabel || null"
|
|
3354
|
-
[attr.aria-labelledby]="tab.ariaLabelledby || null"
|
|
3449
|
+
*ngFor="let entry of visibleTabEntries(); let i = index; trackBy: trackVisibleTab"
|
|
3450
|
+
[disabled]="entry.tab.disabled"
|
|
3451
|
+
[labelClass]="entry.tab.labelClass ?? ''"
|
|
3452
|
+
[bodyClass]="entry.tab.bodyClass ?? ''"
|
|
3453
|
+
[id]="entry.tab.id || ''"
|
|
3454
|
+
[attr.aria-label]="entry.tab.ariaLabel || null"
|
|
3455
|
+
[attr.aria-labelledby]="entry.tab.ariaLabelledby || null"
|
|
3355
3456
|
>
|
|
3356
3457
|
<ng-template mat-tab-label>
|
|
3357
|
-
<
|
|
3458
|
+
<mat-icon *ngIf="entry.tab.icon" class="tab-label-icon" [praxisIcon]="entry.tab.icon"></mat-icon>
|
|
3459
|
+
<span>{{ entry.tab.textLabel }}</span>
|
|
3358
3460
|
<button
|
|
3359
3461
|
*ngIf="config?.behavior?.closeable"
|
|
3360
3462
|
mat-icon-button
|
|
3361
3463
|
type="button"
|
|
3362
|
-
(click)="closeTab(
|
|
3464
|
+
(click)="closeTab(entry.index); $event.stopPropagation()"
|
|
3363
3465
|
(keydown.enter)="$event.stopPropagation()"
|
|
3364
3466
|
(keydown.space)="$event.stopPropagation()"
|
|
3365
3467
|
[attr.aria-label]="t('chrome.closeTab', 'Fechar aba')"
|
|
@@ -3370,10 +3472,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
3370
3472
|
<button
|
|
3371
3473
|
mat-icon-button
|
|
3372
3474
|
type="button"
|
|
3373
|
-
(click)="moveTab(
|
|
3475
|
+
(click)="moveTab(entry.index, -1); $event.stopPropagation()"
|
|
3374
3476
|
(keydown.enter)="$event.stopPropagation()"
|
|
3375
3477
|
(keydown.space)="$event.stopPropagation()"
|
|
3376
|
-
[disabled]="
|
|
3478
|
+
[disabled]="entry.index===0"
|
|
3377
3479
|
[attr.aria-label]="t('chrome.moveTabLeft', 'Mover aba para esquerda')"
|
|
3378
3480
|
>
|
|
3379
3481
|
<mat-icon fontIcon="arrow_back"></mat-icon>
|
|
@@ -3381,10 +3483,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
3381
3483
|
<button
|
|
3382
3484
|
mat-icon-button
|
|
3383
3485
|
type="button"
|
|
3384
|
-
(click)="moveTab(
|
|
3486
|
+
(click)="moveTab(entry.index, 1); $event.stopPropagation()"
|
|
3385
3487
|
(keydown.enter)="$event.stopPropagation()"
|
|
3386
3488
|
(keydown.space)="$event.stopPropagation()"
|
|
3387
|
-
[disabled]="
|
|
3489
|
+
[disabled]="entry.index===(config?.tabs?.length||1)-1"
|
|
3388
3490
|
[attr.aria-label]="t('chrome.moveTabRight', 'Mover aba para direita')"
|
|
3389
3491
|
>
|
|
3390
3492
|
<mat-icon fontIcon="arrow_forward"></mat-icon>
|
|
@@ -3393,20 +3495,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
3393
3495
|
</ng-template>
|
|
3394
3496
|
|
|
3395
3497
|
<ng-template matTabContent>
|
|
3396
|
-
<ng-container *ngIf="(tab.content?.length || tab.widgets?.length) && groupContentReady(
|
|
3397
|
-
<ng-container *ngIf="tab.content && form">
|
|
3498
|
+
<ng-container *ngIf="(entry.tab.content?.length || entry.tab.widgets?.length) && groupContentReady(entry.index); else emptyTab">
|
|
3499
|
+
<ng-container *ngIf="entry.tab.content && form">
|
|
3398
3500
|
<ng-container
|
|
3399
3501
|
dynamicFieldLoader
|
|
3400
|
-
[fields]="tab.content || []"
|
|
3502
|
+
[fields]="entry.tab.content || []"
|
|
3401
3503
|
[formGroup]="form!"
|
|
3402
3504
|
></ng-container>
|
|
3403
3505
|
</ng-container>
|
|
3404
|
-
<ng-container *ngIf="tab.widgets?.length">
|
|
3405
|
-
<ng-container *ngFor="let w of tab.widgets; let wi = index; trackBy: trackWidgetDefinition">
|
|
3506
|
+
<ng-container *ngIf="entry.tab.widgets?.length">
|
|
3507
|
+
<ng-container *ngFor="let w of entry.tab.widgets; let wi = index; trackBy: trackWidgetDefinition">
|
|
3406
3508
|
<ng-container
|
|
3407
3509
|
[dynamicWidgetLoader]="resolveWidgetDefinition(w)"
|
|
3408
3510
|
[context]="context || {}"
|
|
3409
|
-
(widgetEvent)="emitWidgetEvent(tabEventPath(tab.id,
|
|
3511
|
+
(widgetEvent)="emitWidgetEvent(tabEventPath(entry.tab.id, entry.index), $event)"
|
|
3410
3512
|
></ng-container>
|
|
3411
3513
|
</ng-container>
|
|
3412
3514
|
</ng-container>
|
|
@@ -3447,7 +3549,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
3447
3549
|
<mat-icon [praxisIcon]="'restart_alt'"></mat-icon>
|
|
3448
3550
|
</button>
|
|
3449
3551
|
</div>
|
|
3450
|
-
`, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".praxis-tabs-root{position:relative;display:block}.praxis-tabs-group.align-start .mat-mdc-tab-header{justify-content:flex-start}.praxis-tabs-group.align-center .mat-mdc-tab-header{justify-content:center}.praxis-tabs-group.align-end .mat-mdc-tab-header{justify-content:flex-end}.density-compact .mat-mdc-tab-body-content{padding:8px}.density-comfortable .mat-mdc-tab-body-content{padding:16px}.density-spacious .mat-mdc-tab-body-content{padding:24px}.tabs-ai-assistant{position:absolute;right:12px;bottom:72px;z-index:3}.edit-fab{position:absolute;right:12px;bottom:12px;z-index:2}.edit-fab-secondary{right:56px}.tab-empty{padding:16px;color:var(--md-sys-color-on-surface-variant);font-style:italic}.high-contrast{filter:contrast(1.2)}.reduce-motion{--mat-animation-duration: 0ms}.drag-handle{display:inline-flex;align-items:center;vertical-align:middle;margin-right:4px;cursor:grab}:host-context(.pdx-gridster-item) .praxis-tabs-root{display:flex;flex-direction:column;height:100%;min-height:0}:host-context(.pdx-gridster-item) .praxis-tabs-group,:host-context(.pdx-gridster-item) .mat-mdc-tab-group{flex:1 1 auto;min-height:0}:host-context(.pdx-gridster-item) .mat-mdc-tab-body-wrapper,:host-context(.pdx-gridster-item) .mat-mdc-tab-body-content{height:100%;min-height:0}:host-context(.pdx-gridster-item) .mat-mdc-tab-body-content{overflow:auto}:host-context(.pdx-gridster-item) .praxis-tabnav-content{flex:1 1 auto;min-height:0;overflow:auto}\n"] }]
|
|
3552
|
+
`, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".praxis-tabs-root{position:relative;display:block}.praxis-tabs-group.align-start .mat-mdc-tab-header{justify-content:flex-start}.praxis-tabs-group.align-center .mat-mdc-tab-header{justify-content:center}.praxis-tabs-group.align-end .mat-mdc-tab-header{justify-content:flex-end}.density-compact .mat-mdc-tab-body-content{padding:8px}.density-comfortable .mat-mdc-tab-body-content{padding:16px}.density-spacious .mat-mdc-tab-body-content{padding:24px}.tabs-ai-assistant{position:absolute;right:12px;bottom:72px;z-index:3}.edit-fab{position:absolute;right:12px;bottom:12px;z-index:2}.edit-fab-secondary{right:56px}.tab-empty{padding:16px;color:var(--md-sys-color-on-surface-variant);font-style:italic}.high-contrast{filter:contrast(1.2)}.reduce-motion{--mat-animation-duration: 0ms}.drag-handle{display:inline-flex;align-items:center;vertical-align:middle;margin-right:4px;cursor:grab}.tab-label-icon{font-size:18px;width:18px;height:18px;margin-right:6px;vertical-align:middle}:host-context(.pdx-gridster-item) .praxis-tabs-root{display:flex;flex-direction:column;height:100%;min-height:0}:host-context(.pdx-gridster-item) .praxis-tabs-group,:host-context(.pdx-gridster-item) .mat-mdc-tab-group{flex:1 1 auto;min-height:0}:host-context(.pdx-gridster-item) .mat-mdc-tab-body-wrapper,:host-context(.pdx-gridster-item) .mat-mdc-tab-body-content{height:100%;min-height:0}:host-context(.pdx-gridster-item) .mat-mdc-tab-body-content{overflow:auto}:host-context(.pdx-gridster-item) .praxis-tabnav-content{flex:1 1 auto;min-height:0;overflow:auto}\n"] }]
|
|
3451
3553
|
}], propDecorators: { config: [{
|
|
3452
3554
|
type: Input
|
|
3453
3555
|
}], tabsId: [{
|
|
@@ -3455,6 +3557,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
3455
3557
|
args: [{ required: true }]
|
|
3456
3558
|
}], componentInstanceId: [{
|
|
3457
3559
|
type: Input
|
|
3560
|
+
}], selectedIndex: [{
|
|
3561
|
+
type: Input
|
|
3458
3562
|
}], enableCustomization: [{
|
|
3459
3563
|
type: Input
|
|
3460
3564
|
}], form: [{
|
|
@@ -3504,6 +3608,19 @@ const PRAXIS_TABS_PORTS = [
|
|
|
3504
3608
|
description: 'Fragmento canonico de configuracao das tabs/nav e dos widgets internos.',
|
|
3505
3609
|
exposure: { public: true, group: 'config' },
|
|
3506
3610
|
},
|
|
3611
|
+
{
|
|
3612
|
+
id: 'selectedIndex',
|
|
3613
|
+
label: 'Indice selecionado',
|
|
3614
|
+
direction: 'input',
|
|
3615
|
+
semanticKind: 'value',
|
|
3616
|
+
schema: {
|
|
3617
|
+
id: 'number',
|
|
3618
|
+
kind: 'ts-type',
|
|
3619
|
+
ref: 'number',
|
|
3620
|
+
},
|
|
3621
|
+
description: 'Indice ativo de abas/nav para controle externo por composicao.',
|
|
3622
|
+
exposure: { public: true, group: 'state' },
|
|
3623
|
+
},
|
|
3507
3624
|
{
|
|
3508
3625
|
id: 'selectedIndexChange',
|
|
3509
3626
|
label: 'Troca de indice selecionado',
|
|
@@ -3554,6 +3671,12 @@ const PRAXIS_TABS_COMPONENT_METADATA = {
|
|
|
3554
3671
|
label: 'ID da instância',
|
|
3555
3672
|
description: 'Identificador opcional para múltiplas instâncias na mesma rota',
|
|
3556
3673
|
},
|
|
3674
|
+
{
|
|
3675
|
+
name: 'selectedIndex',
|
|
3676
|
+
type: 'number',
|
|
3677
|
+
label: 'Indice selecionado',
|
|
3678
|
+
description: 'Indice ativo de abas/nav controlavel externamente pela composicao',
|
|
3679
|
+
},
|
|
3557
3680
|
{
|
|
3558
3681
|
name: 'enableCustomization',
|
|
3559
3682
|
type: 'boolean',
|
|
@@ -3675,6 +3798,341 @@ function providePraxisTabsMetadata() {
|
|
|
3675
3798
|
};
|
|
3676
3799
|
}
|
|
3677
3800
|
|
|
3801
|
+
const tabItemSchema = {
|
|
3802
|
+
type: 'object',
|
|
3803
|
+
required: ['id', 'textLabel'],
|
|
3804
|
+
properties: {
|
|
3805
|
+
id: { type: 'string' },
|
|
3806
|
+
textLabel: { type: 'string' },
|
|
3807
|
+
icon: { type: 'string' },
|
|
3808
|
+
disabled: { type: 'boolean' },
|
|
3809
|
+
visible: { type: 'boolean', default: true },
|
|
3810
|
+
content: { type: 'array', items: { type: 'object' } },
|
|
3811
|
+
widgets: { type: 'array', items: { type: 'object' } },
|
|
3812
|
+
},
|
|
3813
|
+
};
|
|
3814
|
+
const tabPatchSchema = {
|
|
3815
|
+
type: 'object',
|
|
3816
|
+
minProperties: 1,
|
|
3817
|
+
properties: {
|
|
3818
|
+
id: { type: 'string' },
|
|
3819
|
+
textLabel: { type: 'string' },
|
|
3820
|
+
icon: { type: 'string' },
|
|
3821
|
+
disabled: { type: 'boolean' },
|
|
3822
|
+
visible: { type: 'boolean' },
|
|
3823
|
+
content: { type: 'array', items: { type: 'object' } },
|
|
3824
|
+
widgets: { type: 'array', items: { type: 'object' } },
|
|
3825
|
+
},
|
|
3826
|
+
};
|
|
3827
|
+
const PRAXIS_TABS_AUTHORING_MANIFEST = {
|
|
3828
|
+
schemaVersion: '1.0.0',
|
|
3829
|
+
componentId: 'praxis-tabs',
|
|
3830
|
+
ownerPackage: '@praxisui/tabs',
|
|
3831
|
+
configSchemaId: 'TabsMetadata',
|
|
3832
|
+
manifestVersion: '1.0.0',
|
|
3833
|
+
runtimeInputs: [
|
|
3834
|
+
{ name: 'config', type: 'TabsMetadata', description: 'Canonical tabs/nav configuration.' },
|
|
3835
|
+
{ name: 'tabsId', type: 'string', description: 'Stable id used to derive persistence scope.' },
|
|
3836
|
+
{ name: 'componentInstanceId', type: 'string', description: 'Optional instance discriminator for persistence scope.' },
|
|
3837
|
+
{ name: 'form', type: 'FormGroup', description: 'FormGroup consumed by dynamic field content.' },
|
|
3838
|
+
{ name: 'context', type: 'Record<string, any>', description: 'Context passed to nested widgets.' },
|
|
3839
|
+
{ name: 'enableCustomization', type: 'boolean', description: 'Enables Settings Panel authoring surfaces.' },
|
|
3840
|
+
],
|
|
3841
|
+
editableTargets: [
|
|
3842
|
+
{ kind: 'tab', resolver: 'tab-by-id-or-label', description: 'A group-mode tab in config.tabs[].' },
|
|
3843
|
+
{ kind: 'tabLabel', resolver: 'tab-by-id-or-label', description: 'The text label of a group-mode tab.' },
|
|
3844
|
+
{ kind: 'tabIcon', resolver: 'tab-by-id-or-label', description: 'Icon metadata rendered in a group tab label.' },
|
|
3845
|
+
{ kind: 'tabContent', resolver: 'tab-or-link-by-id', description: 'Dynamic fields or widgets hosted by a tab or nav link.' },
|
|
3846
|
+
{ kind: 'activeTab', resolver: 'tab-index-or-id', description: 'Selected tab or nav link index.' },
|
|
3847
|
+
{ kind: 'visibility', resolver: 'tab-or-link-by-id', description: 'Runtime visibility flag for a group tab or nav link.' },
|
|
3848
|
+
{ kind: 'disabledState', resolver: 'tab-or-link-by-id', description: 'Disabled state of a tab or nav link.' },
|
|
3849
|
+
{ kind: 'layout', resolver: 'tabs-layout-config', description: 'Group/nav mode, header position, density, stretch and behavior settings.' },
|
|
3850
|
+
],
|
|
3851
|
+
operations: [
|
|
3852
|
+
{
|
|
3853
|
+
operationId: 'tab.add',
|
|
3854
|
+
title: 'Add tab',
|
|
3855
|
+
scope: 'global',
|
|
3856
|
+
targetKind: 'tab',
|
|
3857
|
+
target: { kind: 'tab', resolver: 'tabs-array', ambiguityPolicy: 'fail', required: false },
|
|
3858
|
+
inputSchema: tabItemSchema,
|
|
3859
|
+
effects: [{ kind: 'append-unique', path: 'tabs[]', key: 'id' }],
|
|
3860
|
+
validators: ['tab-id-unique', 'tabs-mode-compatible', 'tab-content-valid'],
|
|
3861
|
+
destructive: false,
|
|
3862
|
+
requiresConfirmation: false,
|
|
3863
|
+
affectedPaths: ['tabs[]'],
|
|
3864
|
+
submissionImpact: 'config-only',
|
|
3865
|
+
preconditions: ['config-initialized'],
|
|
3866
|
+
},
|
|
3867
|
+
{
|
|
3868
|
+
operationId: 'tab.remove',
|
|
3869
|
+
title: 'Remove tab',
|
|
3870
|
+
scope: 'layout',
|
|
3871
|
+
targetKind: 'tab',
|
|
3872
|
+
target: { kind: 'tab', resolver: 'tab-by-id-or-label', ambiguityPolicy: 'fail', required: true },
|
|
3873
|
+
inputSchema: {
|
|
3874
|
+
type: 'object',
|
|
3875
|
+
properties: {
|
|
3876
|
+
replacementActiveTabId: { type: 'string' },
|
|
3877
|
+
},
|
|
3878
|
+
},
|
|
3879
|
+
effects: [
|
|
3880
|
+
{
|
|
3881
|
+
kind: 'compile-domain-patch',
|
|
3882
|
+
handler: 'tabs.remove-tab-and-reselect',
|
|
3883
|
+
handlerContract: {
|
|
3884
|
+
reads: ['tabs[]', 'group.selectedIndex'],
|
|
3885
|
+
writes: ['tabs[]', 'group.selectedIndex'],
|
|
3886
|
+
identityKeys: ['tabs[].id'],
|
|
3887
|
+
inputSchema: {
|
|
3888
|
+
type: 'object',
|
|
3889
|
+
properties: { replacementActiveTabId: { type: 'string' } },
|
|
3890
|
+
},
|
|
3891
|
+
failureModes: ['target-tab-missing', 'replacement-tab-missing', 'confirmation-missing'],
|
|
3892
|
+
description: 'Removes the target tab by stable id and reselects a safe replacement when the active/default tab is removed.',
|
|
3893
|
+
},
|
|
3894
|
+
},
|
|
3895
|
+
],
|
|
3896
|
+
destructive: true,
|
|
3897
|
+
requiresConfirmation: true,
|
|
3898
|
+
validators: ['tab-exists', 'active-tab-removal-safe', 'tab-content-removal-confirmed'],
|
|
3899
|
+
affectedPaths: ['tabs[]', 'group.selectedIndex'],
|
|
3900
|
+
submissionImpact: 'config-only',
|
|
3901
|
+
preconditions: ['config-initialized', 'target-tab-exists', 'confirmation-collected'],
|
|
3902
|
+
},
|
|
3903
|
+
{
|
|
3904
|
+
operationId: 'tab.label.set',
|
|
3905
|
+
title: 'Set tab label',
|
|
3906
|
+
scope: 'layout',
|
|
3907
|
+
targetKind: 'tabLabel',
|
|
3908
|
+
target: { kind: 'tabLabel', resolver: 'tab-by-id-or-label', ambiguityPolicy: 'fail', required: true },
|
|
3909
|
+
inputSchema: { type: 'object', required: ['textLabel'], properties: { textLabel: { type: 'string' } } },
|
|
3910
|
+
effects: [{ kind: 'merge-by-key', path: 'tabs[]', key: 'id' }],
|
|
3911
|
+
destructive: false,
|
|
3912
|
+
requiresConfirmation: false,
|
|
3913
|
+
validators: ['tab-exists', 'tab-label-valid'],
|
|
3914
|
+
affectedPaths: ['tabs[].textLabel'],
|
|
3915
|
+
submissionImpact: 'config-only',
|
|
3916
|
+
preconditions: ['config-initialized', 'target-tab-exists'],
|
|
3917
|
+
},
|
|
3918
|
+
{
|
|
3919
|
+
operationId: 'tab.icon.set',
|
|
3920
|
+
title: 'Set tab icon',
|
|
3921
|
+
scope: 'layout',
|
|
3922
|
+
targetKind: 'tabIcon',
|
|
3923
|
+
target: { kind: 'tabIcon', resolver: 'tab-by-id-or-label', ambiguityPolicy: 'fail', required: true },
|
|
3924
|
+
inputSchema: { type: 'object', required: ['icon'], properties: { icon: { type: 'string' } } },
|
|
3925
|
+
effects: [{ kind: 'merge-by-key', path: 'tabs[]', key: 'id' }],
|
|
3926
|
+
destructive: false,
|
|
3927
|
+
requiresConfirmation: false,
|
|
3928
|
+
validators: ['tab-exists', 'tab-icon-valid'],
|
|
3929
|
+
affectedPaths: ['tabs[].icon'],
|
|
3930
|
+
submissionImpact: 'visual-only',
|
|
3931
|
+
preconditions: ['config-initialized', 'target-tab-exists'],
|
|
3932
|
+
},
|
|
3933
|
+
{
|
|
3934
|
+
operationId: 'tab.order.set',
|
|
3935
|
+
title: 'Reorder tabs',
|
|
3936
|
+
scope: 'layout',
|
|
3937
|
+
targetKind: 'tab',
|
|
3938
|
+
target: { kind: 'tab', resolver: 'tab-by-id-or-label', ambiguityPolicy: 'fail', required: true },
|
|
3939
|
+
inputSchema: { type: 'object', required: ['beforeTabId'], properties: { beforeTabId: { type: 'string' } } },
|
|
3940
|
+
effects: [
|
|
3941
|
+
{
|
|
3942
|
+
kind: 'compile-domain-patch',
|
|
3943
|
+
handler: 'tabs.reorder-tab-and-preserve-selection',
|
|
3944
|
+
handlerContract: {
|
|
3945
|
+
reads: ['tabs[]', 'group.selectedIndex'],
|
|
3946
|
+
writes: ['tabs[]', 'group.selectedIndex'],
|
|
3947
|
+
identityKeys: ['tabs[].id'],
|
|
3948
|
+
inputSchema: { type: 'object', required: ['beforeTabId'], properties: { beforeTabId: { type: 'string' } } },
|
|
3949
|
+
failureModes: ['target-tab-missing', 'before-tab-missing', 'unstable-tab-id'],
|
|
3950
|
+
description: 'Reorders tabs by stable id and remaps group.selectedIndex when the selected tab crosses positions.',
|
|
3951
|
+
},
|
|
3952
|
+
},
|
|
3953
|
+
],
|
|
3954
|
+
destructive: false,
|
|
3955
|
+
requiresConfirmation: false,
|
|
3956
|
+
validators: ['tab-exists', 'tab-order-deterministic'],
|
|
3957
|
+
affectedPaths: ['tabs[]', 'group.selectedIndex'],
|
|
3958
|
+
submissionImpact: 'config-only',
|
|
3959
|
+
preconditions: ['config-initialized', 'target-tab-exists'],
|
|
3960
|
+
},
|
|
3961
|
+
{
|
|
3962
|
+
operationId: 'tab.disabled.set',
|
|
3963
|
+
title: 'Set tab disabled state',
|
|
3964
|
+
scope: 'interaction',
|
|
3965
|
+
targetKind: 'disabledState',
|
|
3966
|
+
target: { kind: 'disabledState', resolver: 'tab-or-link-by-id', ambiguityPolicy: 'fail', required: true },
|
|
3967
|
+
inputSchema: { type: 'object', required: ['disabled'], properties: { disabled: { type: 'boolean' } } },
|
|
3968
|
+
effects: [
|
|
3969
|
+
{
|
|
3970
|
+
kind: 'compile-domain-patch',
|
|
3971
|
+
handler: 'tabs.set-tab-or-link-disabled',
|
|
3972
|
+
handlerContract: {
|
|
3973
|
+
reads: ['tabs[]', 'nav.links[]', 'group.selectedIndex', 'nav.selectedIndex'],
|
|
3974
|
+
writes: ['tabs[].disabled', 'nav.links[].disabled'],
|
|
3975
|
+
identityKeys: ['tabs[].id', 'nav.links[].id'],
|
|
3976
|
+
inputSchema: { type: 'object', required: ['disabled'], properties: { disabled: { type: 'boolean' } } },
|
|
3977
|
+
failureModes: ['target-tab-or-link-missing', 'ambiguous-target', 'active-item-disabled-without-reselection'],
|
|
3978
|
+
description: 'Sets disabled on the resolved group tab or nav link without guessing between modes.',
|
|
3979
|
+
},
|
|
3980
|
+
},
|
|
3981
|
+
],
|
|
3982
|
+
destructive: false,
|
|
3983
|
+
requiresConfirmation: false,
|
|
3984
|
+
validators: ['tab-or-link-exists', 'active-tab-disabled-safe'],
|
|
3985
|
+
affectedPaths: ['tabs[].disabled', 'nav.links[].disabled'],
|
|
3986
|
+
submissionImpact: 'config-only',
|
|
3987
|
+
preconditions: ['config-initialized', 'target-tab-or-link-exists'],
|
|
3988
|
+
},
|
|
3989
|
+
{
|
|
3990
|
+
operationId: 'tab.visible.set',
|
|
3991
|
+
title: 'Set tab visibility',
|
|
3992
|
+
scope: 'interaction',
|
|
3993
|
+
targetKind: 'visibility',
|
|
3994
|
+
target: { kind: 'visibility', resolver: 'tab-or-link-by-id', ambiguityPolicy: 'fail', required: true },
|
|
3995
|
+
inputSchema: { type: 'object', required: ['visible'], properties: { visible: { type: 'boolean' } } },
|
|
3996
|
+
effects: [
|
|
3997
|
+
{
|
|
3998
|
+
kind: 'compile-domain-patch',
|
|
3999
|
+
handler: 'tabs.set-tab-or-link-visible',
|
|
4000
|
+
handlerContract: {
|
|
4001
|
+
reads: ['tabs[]', 'nav.links[]', 'group.selectedIndex', 'nav.selectedIndex'],
|
|
4002
|
+
writes: ['tabs[].visible', 'nav.links[].visible'],
|
|
4003
|
+
identityKeys: ['tabs[].id', 'nav.links[].id'],
|
|
4004
|
+
inputSchema: { type: 'object', required: ['visible'], properties: { visible: { type: 'boolean' } } },
|
|
4005
|
+
failureModes: ['target-tab-or-link-missing', 'ambiguous-target', 'active-item-hidden-without-reselection'],
|
|
4006
|
+
description: 'Sets visible on the resolved group tab or nav link and preserves deterministic visible-index mapping.',
|
|
4007
|
+
},
|
|
4008
|
+
},
|
|
4009
|
+
],
|
|
4010
|
+
destructive: false,
|
|
4011
|
+
requiresConfirmation: false,
|
|
4012
|
+
validators: ['tab-or-link-exists', 'active-tab-visibility-safe'],
|
|
4013
|
+
affectedPaths: ['tabs[].visible', 'nav.links[].visible'],
|
|
4014
|
+
submissionImpact: 'config-only',
|
|
4015
|
+
preconditions: ['config-initialized', 'target-tab-or-link-exists'],
|
|
4016
|
+
},
|
|
4017
|
+
{
|
|
4018
|
+
operationId: 'tab.active.set',
|
|
4019
|
+
title: 'Set active tab',
|
|
4020
|
+
scope: 'interaction',
|
|
4021
|
+
targetKind: 'activeTab',
|
|
4022
|
+
target: { kind: 'activeTab', resolver: 'tab-index-or-id', ambiguityPolicy: 'fail', required: true },
|
|
4023
|
+
inputSchema: { type: 'object', required: ['selectedIndex'], properties: { selectedIndex: { type: 'number' }, tabId: { type: 'string' } } },
|
|
4024
|
+
effects: [
|
|
4025
|
+
{
|
|
4026
|
+
kind: 'compile-domain-patch',
|
|
4027
|
+
handler: 'tabs.set-active-item',
|
|
4028
|
+
handlerContract: {
|
|
4029
|
+
reads: ['tabs[]', 'nav.links[]', 'group.selectedIndex', 'nav.selectedIndex'],
|
|
4030
|
+
writes: ['group.selectedIndex', 'nav.selectedIndex'],
|
|
4031
|
+
identityKeys: ['tabs[].id', 'nav.links[].id'],
|
|
4032
|
+
inputSchema: { type: 'object', required: ['selectedIndex'], properties: { selectedIndex: { type: 'number' }, tabId: { type: 'string' } } },
|
|
4033
|
+
failureModes: ['target-tab-or-link-missing', 'selected-index-out-of-range', 'hidden-or-disabled-target'],
|
|
4034
|
+
description: 'Sets the active index for the current primary mode using either selectedIndex or a resolved tab/link id.',
|
|
4035
|
+
},
|
|
4036
|
+
},
|
|
4037
|
+
],
|
|
4038
|
+
destructive: false,
|
|
4039
|
+
requiresConfirmation: false,
|
|
4040
|
+
validators: ['active-tab-exists', 'selected-index-in-range'],
|
|
4041
|
+
affectedPaths: ['group.selectedIndex', 'nav.selectedIndex'],
|
|
4042
|
+
submissionImpact: 'config-only',
|
|
4043
|
+
preconditions: ['config-initialized', 'target-tab-or-link-exists'],
|
|
4044
|
+
},
|
|
4045
|
+
{
|
|
4046
|
+
operationId: 'layout.variant.set',
|
|
4047
|
+
title: 'Set tabs layout variant',
|
|
4048
|
+
scope: 'layout',
|
|
4049
|
+
targetKind: 'layout',
|
|
4050
|
+
target: { kind: 'layout', resolver: 'tabs-layout-config', ambiguityPolicy: 'fail', required: true },
|
|
4051
|
+
inputSchema: {
|
|
4052
|
+
type: 'object',
|
|
4053
|
+
required: ['mode'],
|
|
4054
|
+
properties: {
|
|
4055
|
+
mode: { enum: ['group', 'nav'] },
|
|
4056
|
+
density: { enum: ['compact', 'comfortable', 'spacious'] },
|
|
4057
|
+
headerPosition: { enum: ['above', 'below'] },
|
|
4058
|
+
alignTabs: { enum: ['start', 'center', 'end'] },
|
|
4059
|
+
stretchTabs: { type: 'boolean' },
|
|
4060
|
+
lazyLoad: { type: 'boolean' },
|
|
4061
|
+
},
|
|
4062
|
+
},
|
|
4063
|
+
effects: [{ kind: 'merge-object', path: 'appearance' }, { kind: 'merge-object', path: 'group' }, { kind: 'merge-object', path: 'nav' }, { kind: 'merge-object', path: 'behavior' }],
|
|
4064
|
+
destructive: false,
|
|
4065
|
+
requiresConfirmation: false,
|
|
4066
|
+
validators: ['tabs-mode-compatible', 'layout-values-valid', 'editor-runtime-round-trip'],
|
|
4067
|
+
affectedPaths: ['appearance.density', 'group.headerPosition', 'group.alignTabs', 'group.stretchTabs', 'nav.stretchTabs', 'behavior.lazyLoad'],
|
|
4068
|
+
submissionImpact: 'config-only',
|
|
4069
|
+
preconditions: ['config-initialized'],
|
|
4070
|
+
},
|
|
4071
|
+
{
|
|
4072
|
+
operationId: 'tab.content.set',
|
|
4073
|
+
title: 'Set tab content',
|
|
4074
|
+
scope: 'layout',
|
|
4075
|
+
targetKind: 'tabContent',
|
|
4076
|
+
target: { kind: 'tabContent', resolver: 'tab-or-link-by-id', ambiguityPolicy: 'fail', required: true },
|
|
4077
|
+
inputSchema: tabPatchSchema,
|
|
4078
|
+
effects: [
|
|
4079
|
+
{
|
|
4080
|
+
kind: 'compile-domain-patch',
|
|
4081
|
+
handler: 'tabs.set-tab-or-link-content',
|
|
4082
|
+
handlerContract: {
|
|
4083
|
+
reads: ['tabs[]', 'nav.links[]'],
|
|
4084
|
+
writes: ['tabs[].content', 'tabs[].widgets', 'nav.links[].content', 'nav.links[].widgets'],
|
|
4085
|
+
identityKeys: ['tabs[].id', 'nav.links[].id'],
|
|
4086
|
+
inputSchema: tabPatchSchema,
|
|
4087
|
+
failureModes: ['target-tab-or-link-missing', 'invalid-dynamic-field-content', 'invalid-widget-definition'],
|
|
4088
|
+
description: 'Updates content/widgets only on the resolved group tab or nav link while preserving nested widget identity.',
|
|
4089
|
+
},
|
|
4090
|
+
},
|
|
4091
|
+
],
|
|
4092
|
+
destructive: false,
|
|
4093
|
+
requiresConfirmation: false,
|
|
4094
|
+
validators: ['tab-or-link-exists', 'tab-content-valid', 'widget-event-delegated'],
|
|
4095
|
+
affectedPaths: ['tabs[].content', 'tabs[].widgets', 'nav.links[].content', 'nav.links[].widgets'],
|
|
4096
|
+
submissionImpact: 'config-only',
|
|
4097
|
+
preconditions: ['config-initialized', 'target-tab-or-link-exists'],
|
|
4098
|
+
},
|
|
4099
|
+
],
|
|
4100
|
+
validators: [
|
|
4101
|
+
{ validatorId: 'tab-id-unique', level: 'error', code: 'PTABS001', description: 'Tab ids and nav link ids must be unique within their mode.' },
|
|
4102
|
+
{ validatorId: 'tab-exists', level: 'error', code: 'PTABS002', description: 'Target tab must exist before applying the operation.' },
|
|
4103
|
+
{ validatorId: 'tab-or-link-exists', level: 'error', code: 'PTABS003', description: 'Target must resolve to an existing group tab or nav link.' },
|
|
4104
|
+
{ validatorId: 'active-tab-exists', level: 'error', code: 'PTABS004', description: 'Active tab or nav link selection must reference an existing item.' },
|
|
4105
|
+
{ validatorId: 'selected-index-in-range', level: 'error', code: 'PTABS005', description: 'Selected index must be clamped to the target mode item count.' },
|
|
4106
|
+
{ validatorId: 'active-tab-removal-safe', level: 'error', code: 'PTABS006', description: 'Removing the active/default tab requires confirmation or a replacement active tab.' },
|
|
4107
|
+
{ validatorId: 'tab-content-removal-confirmed', level: 'error', code: 'PTABS007', description: 'Removing a tab or link with content/widgets is destructive and requires confirmation.' },
|
|
4108
|
+
{ validatorId: 'tab-label-valid', level: 'error', code: 'PTABS008', description: 'Tab labels must be non-empty text values after localization/domain projection.' },
|
|
4109
|
+
{ validatorId: 'tab-icon-valid', level: 'warning', code: 'PTABS009', description: 'Tab icon metadata must remain compatible with the icon directive and editor round-trip.' },
|
|
4110
|
+
{ validatorId: 'tab-order-deterministic', level: 'error', code: 'PTABS010', description: 'Tab ordering must use stable ids, not transient array index as identity.' },
|
|
4111
|
+
{ validatorId: 'tabs-mode-compatible', level: 'error', code: 'PTABS011', description: 'Authoring must resolve to one primary mode: group tabs or nav links.' },
|
|
4112
|
+
{ validatorId: 'layout-values-valid', level: 'error', code: 'PTABS012', description: 'Layout values must match TabsMetadata enums and runtime bindings.' },
|
|
4113
|
+
{ validatorId: 'editor-runtime-round-trip', level: 'error', code: 'PTABS013', description: 'Settings Panel, quick setup, JSON editor and runtime must preserve ids, order and selected index.' },
|
|
4114
|
+
{ validatorId: 'active-tab-disabled-safe', level: 'warning', code: 'PTABS014', description: 'Disabling the active item should move selection or request explicit confirmation.' },
|
|
4115
|
+
{ validatorId: 'active-tab-visibility-safe', level: 'warning', code: 'PTABS015', description: 'Hiding the active item should move selection or request explicit confirmation.' },
|
|
4116
|
+
{ validatorId: 'tab-content-valid', level: 'error', code: 'PTABS016', description: 'Tab content must be valid DynamicFieldMetadata[] or WidgetDefinition[] and preserve nested widget identity.' },
|
|
4117
|
+
{ validatorId: 'widget-event-delegated', level: 'info', code: 'PTABS017', description: 'Nested widget event paths remain delegated to the tabs runtime contract and are not redefined by authoring.' },
|
|
4118
|
+
],
|
|
4119
|
+
roundTripRequirements: [
|
|
4120
|
+
'Operations must preserve stable tab/link ids; array index may be used only as a resolver fallback, never as canonical identity.',
|
|
4121
|
+
'Settings Panel, quick setup and JSON editor must round-trip through TabsAuthoringDocument without losing config or bindings.',
|
|
4122
|
+
'Group and nav modes must remain mutually explicit; authoring cannot silently mix config.tabs and nav.links as competing primary modes.',
|
|
4123
|
+
'Nested widget events remain delegated through widgetEvent path enrichment and component-port nestedPath semantics.',
|
|
4124
|
+
],
|
|
4125
|
+
examples: [
|
|
4126
|
+
{ id: 'add-overview-tab', request: 'Add an Overview tab before the details tab.', operationId: 'tab.add', params: { id: 'overview', textLabel: 'Overview' }, isPositive: true },
|
|
4127
|
+
{ id: 'rename-tab', request: 'Rename the details tab to Account Details.', operationId: 'tab.label.set', target: 'details', params: { textLabel: 'Account Details' }, isPositive: true },
|
|
4128
|
+
{ id: 'reorder-tabs', request: 'Move billing before overview.', operationId: 'tab.order.set', target: 'billing', params: { beforeTabId: 'overview' }, isPositive: true },
|
|
4129
|
+
{ id: 'disable-tab', request: 'Disable the audit tab until the user has permission.', operationId: 'tab.disabled.set', target: 'audit', params: { disabled: true }, isPositive: true },
|
|
4130
|
+
{ id: 'activate-tab', request: 'Open the documents tab by default.', operationId: 'tab.active.set', target: 'documents', params: { tabId: 'documents', selectedIndex: 2 }, isPositive: true },
|
|
4131
|
+
{ id: 'reject-duplicate-tab-id', request: 'Add another tab with id overview.', operationId: 'tab.add', params: { id: 'overview', textLabel: 'Duplicate Overview' }, isPositive: false },
|
|
4132
|
+
{ id: 'confirm-remove-content-tab', request: 'Remove the details tab that contains widgets.', operationId: 'tab.remove', target: 'details', params: { replacementActiveTabId: 'overview' }, isPositive: true },
|
|
4133
|
+
],
|
|
4134
|
+
};
|
|
4135
|
+
|
|
3678
4136
|
/*
|
|
3679
4137
|
* Public API Surface of praxis-tabs
|
|
3680
4138
|
*/
|
|
@@ -3683,4 +4141,4 @@ function providePraxisTabsMetadata() {
|
|
|
3683
4141
|
* Generated bundle index. Do not edit.
|
|
3684
4142
|
*/
|
|
3685
4143
|
|
|
3686
|
-
export { PRAXIS_TABS_COMPONENT_METADATA, PRAXIS_TABS_I18N_CONFIG, PRAXIS_TABS_I18N_NAMESPACE, PraxisTabs, PraxisTabsConfigEditor, TABS_AI_CAPABILITIES, buildTabsApplyPlan, createPraxisTabsI18nConfig, createTabsAuthoringDocument, normalizeTabsAuthoringDocument, providePraxisTabsI18n, providePraxisTabsMetadata, serializeTabsAuthoringDocument, toCanonicalTabsConfig, validateTabsAuthoringDocument };
|
|
4144
|
+
export { PRAXIS_TABS_AUTHORING_MANIFEST, PRAXIS_TABS_COMPONENT_METADATA, PRAXIS_TABS_I18N_CONFIG, PRAXIS_TABS_I18N_NAMESPACE, PraxisTabs, PraxisTabsConfigEditor, TABS_AI_CAPABILITIES, buildTabsApplyPlan, createPraxisTabsI18nConfig, createTabsAuthoringDocument, normalizeTabsAuthoringDocument, providePraxisTabsI18n, providePraxisTabsMetadata, serializeTabsAuthoringDocument, toCanonicalTabsConfig, validateTabsAuthoringDocument };
|