@radix-ng/primitives 1.0.1 → 1.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.
- package/composite/README.md +1 -1
- package/fesm2022/radix-ng-primitives-accordion.mjs +10 -10
- package/fesm2022/radix-ng-primitives-accordion.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-checkbox.mjs +134 -58
- package/fesm2022/radix-ng-primitives-checkbox.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-composite.mjs +127 -43
- package/fesm2022/radix-ng-primitives-composite.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-menu.mjs +288 -63
- package/fesm2022/radix-ng-primitives-menu.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-menubar.mjs +24 -1
- package/fesm2022/radix-ng-primitives-menubar.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-select.mjs +56 -29
- package/fesm2022/radix-ng-primitives-select.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-slider.mjs +57 -13
- package/fesm2022/radix-ng-primitives-slider.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-tabs.mjs +292 -59
- package/fesm2022/radix-ng-primitives-tabs.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-toolbar.mjs +19 -13
- package/fesm2022/radix-ng-primitives-toolbar.mjs.map +1 -1
- package/package.json +2 -10
- package/types/radix-ng-primitives-accordion.d.ts +4 -4
- package/types/radix-ng-primitives-checkbox.d.ts +98 -70
- package/types/radix-ng-primitives-composite.d.ts +58 -15
- package/types/radix-ng-primitives-menu.d.ts +44 -16
- package/types/radix-ng-primitives-menubar.d.ts +2 -0
- package/types/radix-ng-primitives-select.d.ts +46 -32
- package/types/radix-ng-primitives-slider.d.ts +19 -4
- package/types/radix-ng-primitives-tabs.d.ts +63 -11
- package/types/radix-ng-primitives-toolbar.d.ts +80 -73
- package/collection/README.md +0 -1
- package/fesm2022/radix-ng-primitives-collection.mjs +0 -72
- package/fesm2022/radix-ng-primitives-collection.mjs.map +0 -1
- package/fesm2022/radix-ng-primitives-roving-focus.mjs +0 -420
- package/fesm2022/radix-ng-primitives-roving-focus.mjs.map +0 -1
- package/roving-focus/README.md +0 -3
- package/types/radix-ng-primitives-collection.d.ts +0 -44
- package/types/radix-ng-primitives-roving-focus.d.ts +0 -201
|
@@ -2,7 +2,7 @@ import * as i0 from '@angular/core';
|
|
|
2
2
|
import { inject, DestroyRef, signal, effect, afterNextRender, untracked, Directive, ElementRef, input, booleanAttribute, computed, model, output, NgModule } from '@angular/core';
|
|
3
3
|
import { createContext, useTransitionStatus, injectId, createCancelableChangeEventDetails } from '@radix-ng/primitives/core';
|
|
4
4
|
import * as i1 from '@radix-ng/primitives/composite';
|
|
5
|
-
import { RdxCompositeRoot, RdxCompositeItem } from '@radix-ng/primitives/composite';
|
|
5
|
+
import { RdxCompositeRoot, RdxCompositeListItem, RdxCompositeList, RdxCompositeItem } from '@radix-ng/primitives/composite';
|
|
6
6
|
import * as i1$1 from '@radix-ng/primitives/presence';
|
|
7
7
|
import { provideRdxPresenceContext, RdxPresenceDirective } from '@radix-ng/primitives/presence';
|
|
8
8
|
|
|
@@ -130,7 +130,16 @@ class RdxTabsList {
|
|
|
130
130
|
* @default true
|
|
131
131
|
*/
|
|
132
132
|
this.loopFocus = input(true, { ...(ngDevMode ? { debugName: "loopFocus" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
133
|
-
this.
|
|
133
|
+
this.tabMap = computed(() => {
|
|
134
|
+
const map = new Map();
|
|
135
|
+
this.compositeRoot.itemMap().forEach((metadata, element) => {
|
|
136
|
+
if (isTabsTabMetadata(metadata)) {
|
|
137
|
+
map.set(element, metadata);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
return map;
|
|
141
|
+
}, ...(ngDevMode ? [{ debugName: "tabMap" }] : /* istanbul ignore next */ []));
|
|
142
|
+
this.tabMetadata = computed(() => Array.from(this.tabMap().values()), ...(ngDevMode ? [{ debugName: "tabMetadata" }] : /* istanbul ignore next */ []));
|
|
134
143
|
this.disabledIndices = computed(() => this.tabMetadata()
|
|
135
144
|
.filter((metadata) => metadata.disabled)
|
|
136
145
|
.map((metadata) => metadata.index), ...(ngDevMode ? [{ debugName: "disabledIndices" }] : /* istanbul ignore next */ []));
|
|
@@ -146,11 +155,14 @@ class RdxTabsList {
|
|
|
146
155
|
this.compositeRoot.setEnableHomeAndEndKeys(true);
|
|
147
156
|
});
|
|
148
157
|
effect(() => {
|
|
149
|
-
this.compositeRoot.setDisabledIndices(
|
|
158
|
+
this.compositeRoot.setDisabledIndices([]);
|
|
159
|
+
});
|
|
160
|
+
effect(() => {
|
|
161
|
+
this.rootContext.setTabMap(this.tabMap());
|
|
150
162
|
});
|
|
151
163
|
effect(() => {
|
|
152
164
|
const activeIndex = this.activeIndex();
|
|
153
|
-
if (activeIndex === -1
|
|
165
|
+
if (activeIndex === -1) {
|
|
154
166
|
return;
|
|
155
167
|
}
|
|
156
168
|
const list = this.elementRef.nativeElement;
|
|
@@ -158,12 +170,19 @@ class RdxTabsList {
|
|
|
158
170
|
if (activeElement && list.contains(activeElement)) {
|
|
159
171
|
return;
|
|
160
172
|
}
|
|
173
|
+
if (this.disabledIndices().includes(activeIndex)) {
|
|
174
|
+
const firstEnabledIndex = this.tabMetadata().find((metadata) => !metadata.disabled)?.index;
|
|
175
|
+
if (firstEnabledIndex !== undefined) {
|
|
176
|
+
this.compositeRoot.setHighlightedIndex(firstEnabledIndex);
|
|
177
|
+
}
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
161
180
|
this.compositeRoot.setHighlightedIndex(activeIndex);
|
|
162
181
|
});
|
|
163
182
|
effect(() => this.rootContext.setActivateOnFocus(this.activateOnFocus()));
|
|
164
183
|
}
|
|
165
184
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxTabsList, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
166
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxTabsList, isStandalone: true, selector: "[rdxTabsList]", inputs: { activateOnFocus: { classPropertyName: "activateOnFocus", publicName: "activateOnFocus", isSignal: true, isRequired: false, transformFunction: null }, loopFocus: { classPropertyName: "loopFocus", publicName: "loopFocus", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "tablist" }, properties: { "attr.aria-orientation": "rootContext.orientation()", "attr.data-orientation": "rootContext.orientation()", "attr.data-activation-direction": "rootContext.activationDirection()" } }, exportAs: ["rdxTabsList"], hostDirectives: [{ directive: i1.RdxCompositeRoot }], ngImport: i0 }); }
|
|
185
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxTabsList, isStandalone: true, selector: "[rdxTabsList]", inputs: { activateOnFocus: { classPropertyName: "activateOnFocus", publicName: "activateOnFocus", isSignal: true, isRequired: false, transformFunction: null }, loopFocus: { classPropertyName: "loopFocus", publicName: "loopFocus", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "tablist" }, properties: { "attr.aria-orientation": "rootContext.orientation() === \"vertical\" ? \"vertical\" : undefined", "attr.data-orientation": "rootContext.orientation()", "attr.data-activation-direction": "rootContext.activationDirection()" } }, exportAs: ["rdxTabsList"], hostDirectives: [{ directive: i1.RdxCompositeRoot }], ngImport: i0 }); }
|
|
167
186
|
}
|
|
168
187
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxTabsList, decorators: [{
|
|
169
188
|
type: Directive,
|
|
@@ -173,7 +192,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
173
192
|
hostDirectives: [RdxCompositeRoot],
|
|
174
193
|
host: {
|
|
175
194
|
role: 'tablist',
|
|
176
|
-
'[attr.aria-orientation]': 'rootContext.orientation()',
|
|
195
|
+
'[attr.aria-orientation]': 'rootContext.orientation() === "vertical" ? "vertical" : undefined',
|
|
177
196
|
'[attr.data-orientation]': 'rootContext.orientation()',
|
|
178
197
|
'[attr.data-activation-direction]': 'rootContext.activationDirection()'
|
|
179
198
|
}
|
|
@@ -198,6 +217,7 @@ const panelPresenceContext = () => ({ present: inject(RdxTabsPanel).present });
|
|
|
198
217
|
class RdxTabsPanel {
|
|
199
218
|
constructor() {
|
|
200
219
|
this.elementRef = inject(ElementRef);
|
|
220
|
+
this.listItem = inject(RdxCompositeListItem, { self: true });
|
|
201
221
|
this.rootContext = injectTabsRootContext();
|
|
202
222
|
/**
|
|
203
223
|
* A unique value that associates the panel with a tab.
|
|
@@ -216,7 +236,15 @@ class RdxTabsPanel {
|
|
|
216
236
|
/** @ignore */
|
|
217
237
|
this.panelId = computed(() => makePanelId(this.rootContext.baseId, this.value()), ...(ngDevMode ? [{ debugName: "panelId" }] : /* istanbul ignore next */ []));
|
|
218
238
|
/** @ignore */
|
|
219
|
-
this.tabId = computed(() =>
|
|
239
|
+
this.tabId = computed(() => {
|
|
240
|
+
const value = this.value();
|
|
241
|
+
for (const tabMetadata of this.rootContext.tabMap().values()) {
|
|
242
|
+
if (tabMetadata.value === value) {
|
|
243
|
+
return tabMetadata.id;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return makeTabId(this.rootContext.baseId, value);
|
|
247
|
+
}, ...(ngDevMode ? [{ debugName: "tabId" }] : /* istanbul ignore next */ []));
|
|
220
248
|
/** Whether this panel's tab is currently selected. */
|
|
221
249
|
this.active = computed(() => this.rootContext.value() === this.value(), ...(ngDevMode ? [{ debugName: "active" }] : /* istanbul ignore next */ []));
|
|
222
250
|
/** `true` once a `*rdxTabsPanelPresence` child takes over mounting. */
|
|
@@ -233,20 +261,18 @@ class RdxTabsPanel {
|
|
|
233
261
|
* element renders nothing), unless `keepMounted` keeps the inactive contents around.
|
|
234
262
|
*/
|
|
235
263
|
this.hidden = computed(() => !this.active() && this.transitionStatus() !== 'ending' && (!this.hasPresence() || this.keepMounted()), ...(ngDevMode ? [{ debugName: "hidden" }] : /* istanbul ignore next */ []));
|
|
236
|
-
/** @ignore Index of the panel
|
|
237
|
-
this.index =
|
|
238
|
-
const list = this.rootContext.tabListElement();
|
|
239
|
-
if (!list) {
|
|
240
|
-
return null;
|
|
241
|
-
}
|
|
242
|
-
const tabs = Array.from(list.querySelectorAll('[role="tab"]'));
|
|
243
|
-
const position = tabs.findIndex((tab) => tab.id === makeTabId(this.rootContext.baseId, this.value()));
|
|
244
|
-
return position === -1 ? null : position;
|
|
245
|
-
}, ...(ngDevMode ? [{ debugName: "index" }] : /* istanbul ignore next */ []));
|
|
264
|
+
/** @ignore Index of the panel in DOM order. */
|
|
265
|
+
this.index = this.listItem.index;
|
|
246
266
|
this.previousActive = false;
|
|
247
267
|
this.isFirstRun = true;
|
|
248
268
|
const unregister = this.transition.registerElement(this.elementRef.nativeElement);
|
|
249
269
|
inject(DestroyRef).onDestroy(unregister);
|
|
270
|
+
effect(() => {
|
|
271
|
+
this.listItem.setMetadata({
|
|
272
|
+
id: this.panelId(),
|
|
273
|
+
value: this.value()
|
|
274
|
+
});
|
|
275
|
+
});
|
|
250
276
|
effect(() => {
|
|
251
277
|
const active = this.active();
|
|
252
278
|
// Settle the initial state without playing an enter transition.
|
|
@@ -266,7 +292,7 @@ class RdxTabsPanel {
|
|
|
266
292
|
this.hasPresence.set(true);
|
|
267
293
|
}
|
|
268
294
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxTabsPanel, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
269
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxTabsPanel, isStandalone: true, selector: "[rdxTabsPanel]", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null }, keepMounted: { classPropertyName: "keepMounted", publicName: "keepMounted", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "tabpanel" }, properties: { "attr.id": "panelId()", "attr.tabindex": "active() ? 0 :
|
|
295
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxTabsPanel, isStandalone: true, selector: "[rdxTabsPanel]", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null }, keepMounted: { classPropertyName: "keepMounted", publicName: "keepMounted", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "tabpanel" }, properties: { "attr.id": "panelId()", "attr.tabindex": "active() ? 0 : -1", "attr.aria-labelledby": "tabId()", "attr.data-orientation": "rootContext.orientation()", "attr.data-activation-direction": "rootContext.activationDirection()", "attr.data-index": "index()", "attr.data-hidden": "active() ? undefined : \"\"", "attr.data-starting-style": "transitionStatus() === \"starting\" ? \"\" : undefined", "attr.data-ending-style": "transitionStatus() === \"ending\" ? \"\" : undefined", "hidden": "hidden()" } }, providers: [provideRdxPresenceContext(panelPresenceContext)], exportAs: ["rdxTabsPanel"], hostDirectives: [{ directive: i1.RdxCompositeListItem }], ngImport: i0 }); }
|
|
270
296
|
}
|
|
271
297
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxTabsPanel, decorators: [{
|
|
272
298
|
type: Directive,
|
|
@@ -274,10 +300,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
274
300
|
selector: '[rdxTabsPanel]',
|
|
275
301
|
exportAs: 'rdxTabsPanel',
|
|
276
302
|
providers: [provideRdxPresenceContext(panelPresenceContext)],
|
|
303
|
+
hostDirectives: [RdxCompositeListItem],
|
|
277
304
|
host: {
|
|
278
305
|
role: 'tabpanel',
|
|
279
306
|
'[attr.id]': 'panelId()',
|
|
280
|
-
'[attr.tabindex]': 'active() ? 0 :
|
|
307
|
+
'[attr.tabindex]': 'active() ? 0 : -1',
|
|
281
308
|
'[attr.aria-labelledby]': 'tabId()',
|
|
282
309
|
'[attr.data-orientation]': 'rootContext.orientation()',
|
|
283
310
|
'[attr.data-activation-direction]': 'rootContext.activationDirection()',
|
|
@@ -322,9 +349,11 @@ const rootContext = () => {
|
|
|
322
349
|
activationDirection: root.activationDirection.asReadonly(),
|
|
323
350
|
activateOnFocus: root.activateOnFocus.asReadonly(),
|
|
324
351
|
tabListElement: root.tabListElement.asReadonly(),
|
|
352
|
+
tabMap: root.tabMap.asReadonly(),
|
|
325
353
|
setValue: (value, event, reason) => root.setValue(value, event, reason),
|
|
326
354
|
setActivateOnFocus: (value) => root.activateOnFocus.set(value),
|
|
327
|
-
setTabListElement: (element) => root.tabListElement.set(element)
|
|
355
|
+
setTabListElement: (element) => root.tabListElement.set(element),
|
|
356
|
+
setTabMap: (map) => root.tabMap.set(map)
|
|
328
357
|
};
|
|
329
358
|
};
|
|
330
359
|
/**
|
|
@@ -342,8 +371,11 @@ class RdxTabsRoot {
|
|
|
342
371
|
this.value = model(...(ngDevMode ? [undefined, { debugName: "value" }] : /* istanbul ignore next */ []));
|
|
343
372
|
/**
|
|
344
373
|
* The value of the tab that should be initially selected when uncontrolled.
|
|
374
|
+
* When omitted, Base UI parity uses `0` as the implicit default and falls back to the first enabled tab.
|
|
375
|
+
*
|
|
376
|
+
* @default 0
|
|
345
377
|
*/
|
|
346
|
-
this.defaultValue = input(...(ngDevMode ? [
|
|
378
|
+
this.defaultValue = input(undefined, ...(ngDevMode ? [{ debugName: "defaultValue" }] : /* istanbul ignore next */ []));
|
|
347
379
|
/**
|
|
348
380
|
* The orientation the tabs are laid out. Controls arrow-key navigation
|
|
349
381
|
* (left/right vs. up/down).
|
|
@@ -353,55 +385,146 @@ class RdxTabsRoot {
|
|
|
353
385
|
this.orientation = input('horizontal', ...(ngDevMode ? [{ debugName: "orientation" }] : /* istanbul ignore next */ []));
|
|
354
386
|
/**
|
|
355
387
|
* Event emitted when the selected tab changes.
|
|
388
|
+
*
|
|
389
|
+
* `eventDetails.reason` is `'none'` for user-initiated changes, `'initial'` for the first automatic
|
|
390
|
+
* uncontrolled selection, `'disabled'` when an uncontrolled selection falls back from a disabled tab,
|
|
391
|
+
* and `'missing'` when it falls back from a removed tab. Automatic changes are not cancelable.
|
|
356
392
|
*/
|
|
357
393
|
this.onValueChange = output();
|
|
358
394
|
/** @ignore Set by `[rdxTabsList]`. */
|
|
359
395
|
this.activateOnFocus = signal(false, ...(ngDevMode ? [{ debugName: "activateOnFocus" }] : /* istanbul ignore next */ []));
|
|
360
396
|
/** @ignore Set by `[rdxTabsList]`. */
|
|
361
397
|
this.tabListElement = signal(null, ...(ngDevMode ? [{ debugName: "tabListElement" }] : /* istanbul ignore next */ []));
|
|
398
|
+
/** @ignore Set by `[rdxTabsList]`. */
|
|
399
|
+
this.tabMap = signal(new Map(), ...(ngDevMode ? [{ debugName: "tabMap" }] : /* istanbul ignore next */ []));
|
|
362
400
|
/** @ignore */
|
|
363
401
|
this.activationDirection = signal('none', ...(ngDevMode ? [{ debugName: "activationDirection" }] : /* istanbul ignore next */ []));
|
|
402
|
+
this.externallyControlled = signal(false, ...(ngDevMode ? [{ debugName: "externallyControlled" }] : /* istanbul ignore next */ []));
|
|
403
|
+
this.hasObservedValue = false;
|
|
404
|
+
this.internalValueCommit = false;
|
|
405
|
+
this.hasAppliedDefaultValue = false;
|
|
406
|
+
this.shouldNotifyInitialValueChange = true;
|
|
407
|
+
this.shouldHonorDisabledDefaultValue = false;
|
|
408
|
+
this.didRegisterTabs = false;
|
|
364
409
|
effect(() => {
|
|
365
|
-
const
|
|
366
|
-
if (
|
|
367
|
-
this.
|
|
410
|
+
const currentValue = this.value();
|
|
411
|
+
if (this.internalValueCommit) {
|
|
412
|
+
this.internalValueCommit = false;
|
|
413
|
+
this.hasObservedValue = true;
|
|
414
|
+
this.previousObservedValue = currentValue;
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
if (!this.hasObservedValue) {
|
|
418
|
+
this.hasObservedValue = true;
|
|
419
|
+
this.previousObservedValue = currentValue;
|
|
420
|
+
if (currentValue !== undefined) {
|
|
421
|
+
this.externallyControlled.set(true);
|
|
422
|
+
}
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
if (currentValue !== this.previousObservedValue) {
|
|
426
|
+
this.externallyControlled.set(true);
|
|
427
|
+
this.previousObservedValue = currentValue;
|
|
428
|
+
}
|
|
429
|
+
});
|
|
430
|
+
effect(() => {
|
|
431
|
+
if (this.hasAppliedDefaultValue || untracked(this.value) !== undefined) {
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
const defaultValue = this.defaultValue();
|
|
435
|
+
const hasExplicitDefaultValue = defaultValue !== undefined;
|
|
436
|
+
this.hasAppliedDefaultValue = true;
|
|
437
|
+
this.initialDefaultValue = defaultValue ?? 0;
|
|
438
|
+
this.shouldNotifyInitialValueChange = !hasExplicitDefaultValue;
|
|
439
|
+
this.shouldHonorDisabledDefaultValue = hasExplicitDefaultValue;
|
|
440
|
+
untracked(() => this.commitValue(this.initialDefaultValue));
|
|
441
|
+
});
|
|
442
|
+
effect(() => {
|
|
443
|
+
const tabMap = this.tabMap();
|
|
444
|
+
const value = this.value();
|
|
445
|
+
if (this.externallyControlled()) {
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
if (tabMap.size === 0) {
|
|
449
|
+
if (this.didRegisterTabs && value !== null && !this.lastKnownTabElement?.isConnected) {
|
|
450
|
+
untracked(() => this.commitAutomaticValueChange(null, 'missing'));
|
|
451
|
+
}
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
this.didRegisterTabs = true;
|
|
455
|
+
this.lastKnownTabElement = tabMap.keys().next().value;
|
|
456
|
+
const selectedTabMetadata = getTabMetadataByValue(tabMap, value);
|
|
457
|
+
const firstEnabledTabValue = getFirstEnabledTabValue(tabMap);
|
|
458
|
+
const selectionIsDisabled = selectedTabMetadata?.disabled;
|
|
459
|
+
const selectionIsMissing = selectedTabMetadata == null && value !== null;
|
|
460
|
+
if (!selectionIsDisabled && value === this.initialDefaultValue) {
|
|
461
|
+
this.shouldHonorDisabledDefaultValue = false;
|
|
462
|
+
}
|
|
463
|
+
if (this.shouldHonorDisabledDefaultValue && selectionIsDisabled && value === this.initialDefaultValue) {
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
const shouldNotifyInitialValueChange = this.shouldNotifyInitialValueChange;
|
|
467
|
+
if (selectionIsDisabled || selectionIsMissing) {
|
|
468
|
+
const fallbackValue = firstEnabledTabValue ?? null;
|
|
469
|
+
if (value === fallbackValue) {
|
|
470
|
+
this.shouldNotifyInitialValueChange = false;
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
let fallbackReason = 'missing';
|
|
474
|
+
if (shouldNotifyInitialValueChange) {
|
|
475
|
+
fallbackReason = 'initial';
|
|
476
|
+
}
|
|
477
|
+
else if (selectionIsDisabled) {
|
|
478
|
+
fallbackReason = 'disabled';
|
|
479
|
+
}
|
|
480
|
+
untracked(() => this.commitAutomaticValueChange(fallbackValue, fallbackReason));
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
if (shouldNotifyInitialValueChange && selectedTabMetadata != null) {
|
|
484
|
+
untracked(() => this.notifyAutomaticValueChange(value, 'initial'));
|
|
485
|
+
this.shouldNotifyInitialValueChange = false;
|
|
368
486
|
}
|
|
369
487
|
});
|
|
370
488
|
}
|
|
371
489
|
/** @ignore */
|
|
372
|
-
setValue(value, event, reason =
|
|
490
|
+
setValue(value, event, reason = 'none') {
|
|
373
491
|
const previous = this.value();
|
|
374
492
|
if (previous === value) {
|
|
375
493
|
return;
|
|
376
494
|
}
|
|
377
495
|
const trigger = event?.currentTarget instanceof HTMLElement ? event.currentTarget : undefined;
|
|
378
|
-
const { eventDetails } = createCancelableChangeEventDetails(reason, event ?? new Event('tabs.value-change'), trigger);
|
|
496
|
+
const { eventDetails: baseEventDetails } = createCancelableChangeEventDetails(reason, event ?? new Event('tabs.value-change'), trigger);
|
|
497
|
+
const eventDetails = baseEventDetails;
|
|
498
|
+
const activationDirection = computeActivationDirection(previous, value, this.orientation(), this.tabMap());
|
|
499
|
+
eventDetails.activationDirection = activationDirection;
|
|
379
500
|
this.onValueChange.emit({ value, eventDetails });
|
|
380
501
|
if (eventDetails.isCanceled()) {
|
|
381
502
|
return;
|
|
382
503
|
}
|
|
383
|
-
this.activationDirection.set(
|
|
504
|
+
this.activationDirection.set(activationDirection);
|
|
505
|
+
this.commitValue(value);
|
|
506
|
+
}
|
|
507
|
+
commitValue(value) {
|
|
508
|
+
this.internalValueCommit = true;
|
|
384
509
|
this.value.set(value);
|
|
385
510
|
}
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
return 'none';
|
|
396
|
-
}
|
|
397
|
-
const horizontal = this.orientation() === 'horizontal';
|
|
398
|
-
if (nextIndex > previousIndex) {
|
|
399
|
-
return horizontal ? 'right' : 'down';
|
|
511
|
+
commitAutomaticValueChange(value, reason) {
|
|
512
|
+
this.activationDirection.set('none');
|
|
513
|
+
this.commitValue(value);
|
|
514
|
+
this.notifyAutomaticValueChange(value, reason);
|
|
515
|
+
this.shouldNotifyInitialValueChange = false;
|
|
516
|
+
}
|
|
517
|
+
notifyAutomaticValueChange(value, reason) {
|
|
518
|
+
if (value === undefined) {
|
|
519
|
+
return;
|
|
400
520
|
}
|
|
401
|
-
|
|
521
|
+
const { eventDetails: baseEventDetails } = createCancelableChangeEventDetails(reason, new Event('tabs.value-change'));
|
|
522
|
+
const eventDetails = baseEventDetails;
|
|
523
|
+
eventDetails.activationDirection = 'none';
|
|
524
|
+
this.onValueChange.emit({ value, eventDetails });
|
|
402
525
|
}
|
|
403
526
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxTabsRoot, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
404
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxTabsRoot, isStandalone: true, selector: "[rdxTabsRoot]", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, defaultValue: { classPropertyName: "defaultValue", publicName: "defaultValue", isSignal: true, isRequired: false, transformFunction: null }, orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", onValueChange: "onValueChange" }, host: { properties: { "attr.data-orientation": "orientation()", "attr.data-activation-direction": "activationDirection()" } }, providers: [provideTabsRootContext(rootContext)], exportAs: ["rdxTabsRoot"], ngImport: i0 }); }
|
|
527
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxTabsRoot, isStandalone: true, selector: "[rdxTabsRoot]", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, defaultValue: { classPropertyName: "defaultValue", publicName: "defaultValue", isSignal: true, isRequired: false, transformFunction: null }, orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", onValueChange: "onValueChange" }, host: { properties: { "attr.data-orientation": "orientation()", "attr.data-activation-direction": "activationDirection()" } }, providers: [provideTabsRootContext(rootContext)], exportAs: ["rdxTabsRoot"], hostDirectives: [{ directive: i1.RdxCompositeList }], ngImport: i0 }); }
|
|
405
528
|
}
|
|
406
529
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxTabsRoot, decorators: [{
|
|
407
530
|
type: Directive,
|
|
@@ -409,12 +532,87 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
409
532
|
selector: '[rdxTabsRoot]',
|
|
410
533
|
exportAs: 'rdxTabsRoot',
|
|
411
534
|
providers: [provideTabsRootContext(rootContext)],
|
|
535
|
+
hostDirectives: [RdxCompositeList],
|
|
412
536
|
host: {
|
|
413
537
|
'[attr.data-orientation]': 'orientation()',
|
|
414
538
|
'[attr.data-activation-direction]': 'activationDirection()'
|
|
415
539
|
}
|
|
416
540
|
}]
|
|
417
541
|
}], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], defaultValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultValue", required: false }] }], orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }], onValueChange: [{ type: i0.Output, args: ["onValueChange"] }] } });
|
|
542
|
+
function getTabMetadataByValue(tabMap, value) {
|
|
543
|
+
for (const tabMetadata of tabMap.values()) {
|
|
544
|
+
if (tabMetadata.value === value) {
|
|
545
|
+
return tabMetadata;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
return undefined;
|
|
549
|
+
}
|
|
550
|
+
function getFirstEnabledTabValue(tabMap) {
|
|
551
|
+
for (const tabMetadata of tabMap.values()) {
|
|
552
|
+
if (!tabMetadata.disabled) {
|
|
553
|
+
return tabMetadata.value;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
return undefined;
|
|
557
|
+
}
|
|
558
|
+
function computeActivationDirection(previous, next, orientation, tabMap) {
|
|
559
|
+
if (previous == null || next == null) {
|
|
560
|
+
return 'none';
|
|
561
|
+
}
|
|
562
|
+
let previousTab = null;
|
|
563
|
+
let nextTab = null;
|
|
564
|
+
let previousIndex = -1;
|
|
565
|
+
let nextIndex = -1;
|
|
566
|
+
for (const [tabElement, tabMetadata] of tabMap.entries()) {
|
|
567
|
+
if (tabMetadata.value === previous) {
|
|
568
|
+
previousTab = tabElement;
|
|
569
|
+
previousIndex = tabMetadata.index;
|
|
570
|
+
}
|
|
571
|
+
if (tabMetadata.value === next) {
|
|
572
|
+
nextTab = tabElement;
|
|
573
|
+
nextIndex = tabMetadata.index;
|
|
574
|
+
}
|
|
575
|
+
if (previousTab && nextTab) {
|
|
576
|
+
break;
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
if (!previousTab || !nextTab || previousIndex === nextIndex) {
|
|
580
|
+
return inferActivationDirectionFromValues(previous, next, orientation);
|
|
581
|
+
}
|
|
582
|
+
const previousRect = previousTab.getBoundingClientRect();
|
|
583
|
+
const nextRect = nextTab.getBoundingClientRect();
|
|
584
|
+
if (orientation === 'horizontal') {
|
|
585
|
+
if (nextRect.left < previousRect.left) {
|
|
586
|
+
return 'left';
|
|
587
|
+
}
|
|
588
|
+
if (nextRect.left > previousRect.left) {
|
|
589
|
+
return 'right';
|
|
590
|
+
}
|
|
591
|
+
return nextIndex > previousIndex ? 'right' : 'left';
|
|
592
|
+
}
|
|
593
|
+
if (nextRect.top < previousRect.top) {
|
|
594
|
+
return 'up';
|
|
595
|
+
}
|
|
596
|
+
if (nextRect.top > previousRect.top) {
|
|
597
|
+
return 'down';
|
|
598
|
+
}
|
|
599
|
+
return nextIndex > previousIndex ? 'down' : 'up';
|
|
600
|
+
}
|
|
601
|
+
function inferActivationDirectionFromValues(previous, next, orientation) {
|
|
602
|
+
if (previous !== next && typeof previous === 'number' && typeof next === 'number') {
|
|
603
|
+
if (orientation === 'horizontal') {
|
|
604
|
+
return next > previous ? 'right' : 'left';
|
|
605
|
+
}
|
|
606
|
+
return next > previous ? 'down' : 'up';
|
|
607
|
+
}
|
|
608
|
+
if (previous !== next && typeof previous === 'string' && typeof next === 'string') {
|
|
609
|
+
if (orientation === 'horizontal') {
|
|
610
|
+
return next > previous ? 'right' : 'left';
|
|
611
|
+
}
|
|
612
|
+
return next > previous ? 'down' : 'up';
|
|
613
|
+
}
|
|
614
|
+
return 'none';
|
|
615
|
+
}
|
|
418
616
|
|
|
419
617
|
/**
|
|
420
618
|
* An individual interactive tab button that activates its corresponding panel.
|
|
@@ -431,14 +629,27 @@ class RdxTabsTab {
|
|
|
431
629
|
this.value = input.required(...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
432
630
|
/**
|
|
433
631
|
* When `true`, prevents the user from interacting with the tab.
|
|
632
|
+
* Disabled tabs remain focusable during composite keyboard navigation, matching Base UI.
|
|
434
633
|
*/
|
|
435
634
|
this.disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
635
|
+
/**
|
|
636
|
+
* Whether the host element is a native button. When `true`, `type="button"` is applied.
|
|
637
|
+
*
|
|
638
|
+
* @default true
|
|
639
|
+
*/
|
|
640
|
+
this.nativeButton = input(true, { ...(ngDevMode ? { debugName: "nativeButton" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
641
|
+
/**
|
|
642
|
+
* Optional id for the tab element. When omitted, an id is derived from the root id and tab value.
|
|
643
|
+
*/
|
|
644
|
+
this.id = input(undefined, ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
|
|
436
645
|
/** @ignore */
|
|
437
|
-
this.tabId = computed(() => makeTabId(this.rootContext.baseId, this.value()), ...(ngDevMode ? [{ debugName: "tabId" }] : /* istanbul ignore next */ []));
|
|
646
|
+
this.tabId = computed(() => this.id() ?? makeTabId(this.rootContext.baseId, this.value()), ...(ngDevMode ? [{ debugName: "tabId" }] : /* istanbul ignore next */ []));
|
|
438
647
|
/** @ignore */
|
|
439
648
|
this.panelId = computed(() => makePanelId(this.rootContext.baseId, this.value()), ...(ngDevMode ? [{ debugName: "panelId" }] : /* istanbul ignore next */ []));
|
|
440
649
|
/** @ignore */
|
|
441
650
|
this.active = computed(() => this.rootContext.value() === this.value(), ...(ngDevMode ? [{ debugName: "active" }] : /* istanbul ignore next */ []));
|
|
651
|
+
this.isPressing = false;
|
|
652
|
+
this.isMainButton = false;
|
|
442
653
|
effect(() => {
|
|
443
654
|
this.compositeItem.setMetadata({
|
|
444
655
|
disabled: this.disabled(),
|
|
@@ -448,30 +659,50 @@ class RdxTabsTab {
|
|
|
448
659
|
});
|
|
449
660
|
}
|
|
450
661
|
/** @ignore */
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
this.rootContext.setValue(this.value(), event, 'trigger-press');
|
|
455
|
-
}
|
|
456
|
-
else {
|
|
457
|
-
// Prevent focus to avoid accidental activation.
|
|
458
|
-
event.preventDefault();
|
|
662
|
+
onClick(event) {
|
|
663
|
+
if (this.active() || this.disabled()) {
|
|
664
|
+
return;
|
|
459
665
|
}
|
|
666
|
+
this.rootContext.setValue(this.value(), event, 'none');
|
|
460
667
|
}
|
|
461
668
|
/** @ignore */
|
|
462
669
|
onKeyDown(event) {
|
|
463
670
|
if (!this.disabled() && (event.key === ' ' || event.key === 'Enter')) {
|
|
464
|
-
this.rootContext.setValue(this.value(), event, '
|
|
671
|
+
this.rootContext.setValue(this.value(), event, 'none');
|
|
465
672
|
}
|
|
466
673
|
}
|
|
467
674
|
/** @ignore */
|
|
675
|
+
onPointerDown(event) {
|
|
676
|
+
if (this.active() || this.disabled()) {
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
this.isPressing = true;
|
|
680
|
+
this.isMainButton = event.button === 0;
|
|
681
|
+
const ownerDocument = event.currentTarget instanceof HTMLElement ? event.currentTarget.ownerDocument : document;
|
|
682
|
+
ownerDocument.addEventListener('pointerup', () => {
|
|
683
|
+
this.isPressing = false;
|
|
684
|
+
this.isMainButton = false;
|
|
685
|
+
}, { once: true });
|
|
686
|
+
ownerDocument.addEventListener('pointercancel', () => {
|
|
687
|
+
this.isPressing = false;
|
|
688
|
+
this.isMainButton = false;
|
|
689
|
+
}, { once: true });
|
|
690
|
+
ownerDocument.addEventListener('blur', () => {
|
|
691
|
+
this.isPressing = false;
|
|
692
|
+
this.isMainButton = false;
|
|
693
|
+
}, { once: true });
|
|
694
|
+
}
|
|
695
|
+
/** @ignore */
|
|
468
696
|
onFocus(event) {
|
|
469
|
-
if (
|
|
470
|
-
|
|
697
|
+
if (this.active() || this.disabled()) {
|
|
698
|
+
return;
|
|
699
|
+
}
|
|
700
|
+
if (this.rootContext.activateOnFocus() && (!this.isPressing || this.isMainButton)) {
|
|
701
|
+
this.rootContext.setValue(this.value(), event, 'none');
|
|
471
702
|
}
|
|
472
703
|
}
|
|
473
704
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxTabsTab, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
474
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxTabsTab, isStandalone: true, selector: "[rdxTabsTab]", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } },
|
|
705
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxTabsTab, isStandalone: true, selector: "[rdxTabsTab]", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, nativeButton: { classPropertyName: "nativeButton", publicName: "nativeButton", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "tab" }, listeners: { "click": "onClick($event)", "keydown": "onKeyDown($event)", "pointerdown": "onPointerDown($event)", "focus": "onFocus($event)" }, properties: { "attr.type": "nativeButton() ? \"button\" : undefined", "attr.id": "tabId()", "attr.aria-selected": "active()", "attr.aria-controls": "panelId()", "attr.aria-disabled": "disabled() ? \"true\" : undefined", "attr.disabled": "null", "attr.data-composite-item-active": "active() ? \"\" : undefined", "attr.data-orientation": "rootContext.orientation()", "attr.data-activation-direction": "rootContext.activationDirection()", "attr.data-active": "active() ? \"\" : undefined", "attr.data-disabled": "disabled() ? \"\" : undefined" } }, exportAs: ["rdxTabsTab"], hostDirectives: [{ directive: i1.RdxCompositeItem }], ngImport: i0 }); }
|
|
475
706
|
}
|
|
476
707
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxTabsTab, decorators: [{
|
|
477
708
|
type: Directive,
|
|
@@ -480,23 +711,25 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
480
711
|
exportAs: 'rdxTabsTab',
|
|
481
712
|
hostDirectives: [RdxCompositeItem],
|
|
482
713
|
host: {
|
|
483
|
-
type: 'button',
|
|
714
|
+
'[attr.type]': 'nativeButton() ? "button" : undefined',
|
|
484
715
|
role: 'tab',
|
|
485
716
|
'[attr.id]': 'tabId()',
|
|
486
717
|
'[attr.aria-selected]': 'active()',
|
|
487
718
|
'[attr.aria-controls]': 'panelId()',
|
|
488
719
|
'[attr.aria-disabled]': 'disabled() ? "true" : undefined',
|
|
720
|
+
'[attr.disabled]': 'null',
|
|
489
721
|
'[attr.data-composite-item-active]': 'active() ? "" : undefined',
|
|
490
722
|
'[attr.data-orientation]': 'rootContext.orientation()',
|
|
491
723
|
'[attr.data-activation-direction]': 'rootContext.activationDirection()',
|
|
492
724
|
'[attr.data-active]': 'active() ? "" : undefined',
|
|
493
725
|
'[attr.data-disabled]': 'disabled() ? "" : undefined',
|
|
494
|
-
'(
|
|
726
|
+
'(click)': 'onClick($event)',
|
|
495
727
|
'(keydown)': 'onKeyDown($event)',
|
|
728
|
+
'(pointerdown)': 'onPointerDown($event)',
|
|
496
729
|
'(focus)': 'onFocus($event)'
|
|
497
730
|
}
|
|
498
731
|
}]
|
|
499
|
-
}], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }] } });
|
|
732
|
+
}], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], nativeButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "nativeButton", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }] } });
|
|
500
733
|
|
|
501
734
|
const tabsImports = [RdxTabsRoot, RdxTabsList, RdxTabsTab, RdxTabsPanel, RdxTabsPanelPresence, RdxTabsIndicator];
|
|
502
735
|
class RdxTabsModule {
|