@praxisui/tabs 9.0.65 → 9.0.67
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 +13 -0
- package/ai/component-registry.json +2 -2
- package/fesm2022/praxisui-tabs.mjs +78 -3
- package/package.json +5 -5
- package/types/praxisui-tabs.d.ts +9 -0
package/README.md
CHANGED
|
@@ -4,6 +4,19 @@
|
|
|
4
4
|
|
|
5
5
|
Use this package for UI composition. The host still owns route-level state, domain persistence, widget data providers and authorization.
|
|
6
6
|
|
|
7
|
+
### Resizing a tab container
|
|
8
|
+
|
|
9
|
+
In paginated `group` and `nav` headers, changing the container width reveals the
|
|
10
|
+
active label without moving keyboard focus or emitting a selection event. This
|
|
11
|
+
also applies to widget resizing without a window resize, in LTR and RTL. A label
|
|
12
|
+
wider than the available strip is aligned to its leading edge; it cannot be shown
|
|
13
|
+
in full. Explicitly disabled pagination is respected.
|
|
14
|
+
|
|
15
|
+
The runtime observes the header size and preserves Material's scrolling animation.
|
|
16
|
+
The group adapter accesses Material's internal tab header because `MatTabGroup`
|
|
17
|
+
has no focus-neutral reveal operation. Keep the group/nav, LTR/RTL resize regression
|
|
18
|
+
tests when upgrading Material; consumers must not replicate this bridge.
|
|
19
|
+
|
|
7
20
|
## LLM Recommendation Guide
|
|
8
21
|
|
|
9
22
|
- Recommend when: users need metadata-driven tab groups or nav tabs with editable tab content, visibility, order, icons, and runtime authoring.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": "1.0.0",
|
|
3
|
-
"generatedAt": "2026-09-
|
|
3
|
+
"generatedAt": "2026-09-07T00:54:55.301Z",
|
|
4
4
|
"packageName": "@praxisui/tabs",
|
|
5
|
-
"packageVersion": "9.0.
|
|
5
|
+
"packageVersion": "9.0.67",
|
|
6
6
|
"sourceRegistry": "praxis-component-registry-ingestion",
|
|
7
7
|
"sourceRegistryVersion": "1.0.0",
|
|
8
8
|
"componentCount": 1,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { inject, Input, Inject, Component, ChangeDetectorRef, effect, EventEmitter, signal, ViewChild, Output, ChangeDetectionStrategy, ENVIRONMENT_INITIALIZER } from '@angular/core';
|
|
2
|
+
import { inject, Input, Inject, Component, ChangeDetectorRef, effect, EventEmitter, signal, ElementRef, afterEveryRender, ViewChild, Output, ChangeDetectionStrategy, ENVIRONMENT_INITIALIZER } from '@angular/core';
|
|
3
3
|
import { ActivatedRoute } from '@angular/router';
|
|
4
4
|
import * as i1$1 from '@angular/common';
|
|
5
5
|
import { CommonModule } from '@angular/common';
|
|
6
6
|
import * as i3 from '@angular/material/tabs';
|
|
7
|
-
import { MatTabsModule, MatTabGroup } from '@angular/material/tabs';
|
|
7
|
+
import { MatTabsModule, MatTabGroup, MatTabNav } from '@angular/material/tabs';
|
|
8
8
|
import * as i4$1 from '@angular/material/icon';
|
|
9
9
|
import { MatIconModule } from '@angular/material/icon';
|
|
10
10
|
import * as i10 from '@angular/material/tooltip';
|
|
@@ -3184,6 +3184,15 @@ class PraxisTabs {
|
|
|
3184
3184
|
controlledSelectedIndex;
|
|
3185
3185
|
destroyed = false;
|
|
3186
3186
|
groupComponent;
|
|
3187
|
+
navComponent;
|
|
3188
|
+
hostElement = inject(ElementRef);
|
|
3189
|
+
headerResizeObserver;
|
|
3190
|
+
observedHeader;
|
|
3191
|
+
headerResizeFrame;
|
|
3192
|
+
observedHeaderWidth = -1;
|
|
3193
|
+
observeHeaderAfterRender = afterEveryRender({
|
|
3194
|
+
read: () => this.observeHeaderResize(),
|
|
3195
|
+
});
|
|
3187
3196
|
destroy$ = new Subject();
|
|
3188
3197
|
widgetDefinitionCache = new WeakMap();
|
|
3189
3198
|
generatedContentForm = new FormGroup({});
|
|
@@ -3214,6 +3223,10 @@ class PraxisTabs {
|
|
|
3214
3223
|
}
|
|
3215
3224
|
ngOnDestroy() {
|
|
3216
3225
|
this.destroyed = true;
|
|
3226
|
+
this.headerResizeObserver?.disconnect();
|
|
3227
|
+
if (this.headerResizeFrame !== undefined) {
|
|
3228
|
+
this.hostElement.nativeElement.ownerDocument.defaultView?.cancelAnimationFrame(this.headerResizeFrame);
|
|
3229
|
+
}
|
|
3217
3230
|
this.assistantSessions.removeContextSession(this.buildAiAssistantContextSnapshot().identity);
|
|
3218
3231
|
this.aiAssistantStateSubscription?.unsubscribe();
|
|
3219
3232
|
this.destroy$.next();
|
|
@@ -3222,6 +3235,65 @@ class PraxisTabs {
|
|
|
3222
3235
|
isNavMode() {
|
|
3223
3236
|
return !!this.config?.nav && !!this.config?.nav?.links?.length;
|
|
3224
3237
|
}
|
|
3238
|
+
observeHeaderResize() {
|
|
3239
|
+
const header = this.hostElement.nativeElement.querySelector(':scope > .praxis-tabs-root > mat-tab-group > mat-tab-header, :scope > .praxis-tabs-root > nav[mat-tab-nav-bar]') ?? undefined;
|
|
3240
|
+
if (header === this.observedHeader || typeof ResizeObserver === 'undefined')
|
|
3241
|
+
return;
|
|
3242
|
+
this.headerResizeObserver?.disconnect();
|
|
3243
|
+
this.observedHeader = header;
|
|
3244
|
+
this.observedHeaderWidth = -1;
|
|
3245
|
+
if (!header)
|
|
3246
|
+
return;
|
|
3247
|
+
this.headerResizeObserver = new ResizeObserver(() => {
|
|
3248
|
+
const width = header.clientWidth;
|
|
3249
|
+
if (width === this.observedHeaderWidth)
|
|
3250
|
+
return;
|
|
3251
|
+
this.observedHeaderWidth = width;
|
|
3252
|
+
const view = header.ownerDocument.defaultView;
|
|
3253
|
+
if (!view)
|
|
3254
|
+
return;
|
|
3255
|
+
if (this.headerResizeFrame !== undefined)
|
|
3256
|
+
view.cancelAnimationFrame(this.headerResizeFrame);
|
|
3257
|
+
this.headerResizeFrame = view.requestAnimationFrame(() => {
|
|
3258
|
+
this.headerResizeFrame = undefined;
|
|
3259
|
+
if (!this.destroyed && header === this.observedHeader && width > 0) {
|
|
3260
|
+
this.revealActiveHeaderLabel(header);
|
|
3261
|
+
}
|
|
3262
|
+
});
|
|
3263
|
+
});
|
|
3264
|
+
this.headerResizeObserver.observe(header);
|
|
3265
|
+
}
|
|
3266
|
+
revealActiveHeaderLabel(element) {
|
|
3267
|
+
// Material adapter boundary: MatTabGroup exposes no focus-neutral reveal API.
|
|
3268
|
+
// Keep access to its header here; do not change selectedIndex or focusIndex.
|
|
3269
|
+
const header = this.isNavMode() ? this.navComponent : this.groupComponent?._tabHeader;
|
|
3270
|
+
if (!header || header.disablePagination)
|
|
3271
|
+
return;
|
|
3272
|
+
header.updatePagination();
|
|
3273
|
+
this.cdr.detectChanges(); // Material paginator controls must occupy their final width.
|
|
3274
|
+
const viewport = element.querySelector('.mat-mdc-tab-label-container, .mat-mdc-tab-link-container');
|
|
3275
|
+
const active = element.querySelector('[role="tab"][aria-selected="true"]');
|
|
3276
|
+
if (!viewport || !active)
|
|
3277
|
+
return;
|
|
3278
|
+
const rtl = element.ownerDocument.defaultView?.getComputedStyle(element).direction === 'rtl';
|
|
3279
|
+
const labelContainer = active.offsetParent;
|
|
3280
|
+
if (rtl && !labelContainer)
|
|
3281
|
+
return;
|
|
3282
|
+
// Offset geometry excludes the in-flight transform animation. Reading painted
|
|
3283
|
+
// rectangles here would apply an intermediate animation delta a second time.
|
|
3284
|
+
const end = rtl ? labelContainer.offsetWidth - active.offsetLeft
|
|
3285
|
+
: active.offsetLeft + active.offsetWidth;
|
|
3286
|
+
const start = end - active.offsetWidth;
|
|
3287
|
+
const before = header.scrollDistance;
|
|
3288
|
+
const after = before + viewport.clientWidth;
|
|
3289
|
+
const delta = start < before ? start - before
|
|
3290
|
+
: end > after ? Math.min(end - after, start - before) : 0;
|
|
3291
|
+
if (Math.abs(delta) < 1)
|
|
3292
|
+
return;
|
|
3293
|
+
header.scrollDistance += delta;
|
|
3294
|
+
header.updatePagination();
|
|
3295
|
+
this.cdr.markForCheck();
|
|
3296
|
+
}
|
|
3225
3297
|
effectiveAnimationDuration() {
|
|
3226
3298
|
const reduce = this.config?.accessibility?.reduceMotion;
|
|
3227
3299
|
if (reduce)
|
|
@@ -4314,7 +4386,7 @@ class PraxisTabs {
|
|
|
4314
4386
|
return JSON.parse(JSON.stringify(config));
|
|
4315
4387
|
}
|
|
4316
4388
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisTabs, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4317
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PraxisTabs, isStandalone: true, selector: "praxis-tabs", inputs: { config: "config", tabsId: "tabsId", componentInstanceId: "componentInstanceId", configPersistenceStrategy: "configPersistenceStrategy", selectedIndex: "selectedIndex", enableCustomization: "enableCustomization", form: "form", context: "context" }, outputs: { animationDone: "animationDone", focusChange: "focusChange", selectedIndexChange: "selectedIndexChange", selectedTabChange: "selectedTabChange", indexFocused: "indexFocused", selectFocusedIndex: "selectFocusedIndex", configChange: "configChange", widgetEvent: "widgetEvent" }, providers: [providePraxisI18nConfig(PRAXIS_TABS_I18N_CONFIG)], viewQueries: [{ propertyName: "groupComponent", first: true, predicate: MatTabGroup, descendants: true }], usesOnChanges: true, ngImport: i0, template: `
|
|
4389
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PraxisTabs, isStandalone: true, selector: "praxis-tabs", inputs: { config: "config", tabsId: "tabsId", componentInstanceId: "componentInstanceId", configPersistenceStrategy: "configPersistenceStrategy", selectedIndex: "selectedIndex", enableCustomization: "enableCustomization", form: "form", context: "context" }, outputs: { animationDone: "animationDone", focusChange: "focusChange", selectedIndexChange: "selectedIndexChange", selectedTabChange: "selectedTabChange", indexFocused: "indexFocused", selectFocusedIndex: "selectFocusedIndex", configChange: "configChange", widgetEvent: "widgetEvent" }, providers: [providePraxisI18nConfig(PRAXIS_TABS_I18N_CONFIG)], viewQueries: [{ propertyName: "groupComponent", first: true, predicate: MatTabGroup, descendants: true }, { propertyName: "navComponent", first: true, predicate: MatTabNav, descendants: true }], usesOnChanges: true, ngImport: i0, template: `
|
|
4318
4390
|
<div
|
|
4319
4391
|
class="praxis-tabs-root"
|
|
4320
4392
|
[class.density-compact]="config?.appearance?.density === 'compact'"
|
|
@@ -4981,6 +5053,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
|
|
|
4981
5053
|
}], groupComponent: [{
|
|
4982
5054
|
type: ViewChild,
|
|
4983
5055
|
args: [MatTabGroup]
|
|
5056
|
+
}], navComponent: [{
|
|
5057
|
+
type: ViewChild,
|
|
5058
|
+
args: [MatTabNav]
|
|
4984
5059
|
}] } });
|
|
4985
5060
|
|
|
4986
5061
|
class PraxisTabsWidgetConfigEditor {
|
package/package.json
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@praxisui/tabs",
|
|
3
|
-
"version": "9.0.
|
|
3
|
+
"version": "9.0.67",
|
|
4
4
|
"description": "Configurable tabs (group and nav) for Praxis UI with metadata-driven content and runtime editor.",
|
|
5
5
|
"peerDependencies": {
|
|
6
6
|
"@angular/common": "^21.0.0",
|
|
7
7
|
"@angular/core": "^21.0.0",
|
|
8
8
|
"@angular/material": "^21.0.0",
|
|
9
9
|
"@angular/cdk": "^21.0.0",
|
|
10
|
-
"@praxisui/core": "^9.0.
|
|
11
|
-
"@praxisui/dynamic-fields": "^9.0.
|
|
12
|
-
"@praxisui/settings-panel": "^9.0.
|
|
10
|
+
"@praxisui/core": "^9.0.67",
|
|
11
|
+
"@praxisui/dynamic-fields": "^9.0.67",
|
|
12
|
+
"@praxisui/settings-panel": "^9.0.67",
|
|
13
13
|
"@angular/forms": "^21.0.0",
|
|
14
14
|
"@angular/router": "^21.0.0",
|
|
15
|
-
"@praxisui/ai": "^9.0.
|
|
15
|
+
"@praxisui/ai": "^9.0.67",
|
|
16
16
|
"rxjs": "~7.8.0"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
package/types/praxisui-tabs.d.ts
CHANGED
|
@@ -208,6 +208,13 @@ declare class PraxisTabs implements OnInit, OnChanges, OnDestroy {
|
|
|
208
208
|
private controlledSelectedIndex?;
|
|
209
209
|
private destroyed;
|
|
210
210
|
private groupComponent?;
|
|
211
|
+
private navComponent?;
|
|
212
|
+
private readonly hostElement;
|
|
213
|
+
private headerResizeObserver?;
|
|
214
|
+
private observedHeader?;
|
|
215
|
+
private headerResizeFrame?;
|
|
216
|
+
private observedHeaderWidth;
|
|
217
|
+
private readonly observeHeaderAfterRender;
|
|
211
218
|
private readonly destroy$;
|
|
212
219
|
private widgetDefinitionCache;
|
|
213
220
|
private readonly generatedContentForm;
|
|
@@ -215,6 +222,8 @@ declare class PraxisTabs implements OnInit, OnChanges, OnDestroy {
|
|
|
215
222
|
ngOnChanges(changes: SimpleChanges): void;
|
|
216
223
|
ngOnDestroy(): void;
|
|
217
224
|
isNavMode(): boolean;
|
|
225
|
+
private observeHeaderResize;
|
|
226
|
+
private revealActiveHeaderLabel;
|
|
218
227
|
effectiveAnimationDuration(): string;
|
|
219
228
|
getNavActive(i: number): boolean;
|
|
220
229
|
protected visibleNavLinkEntries(): Array<{
|