@radix-ng/primitives 0.51.0 → 1.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (186) hide show
  1. package/fesm2022/radix-ng-primitives-accordion.mjs +105 -38
  2. package/fesm2022/radix-ng-primitives-accordion.mjs.map +1 -1
  3. package/fesm2022/radix-ng-primitives-alert-dialog.mjs +221 -129
  4. package/fesm2022/radix-ng-primitives-alert-dialog.mjs.map +1 -1
  5. package/fesm2022/radix-ng-primitives-arrow.mjs +20 -4
  6. package/fesm2022/radix-ng-primitives-arrow.mjs.map +1 -1
  7. package/fesm2022/radix-ng-primitives-aspect-ratio.mjs.map +1 -1
  8. package/fesm2022/radix-ng-primitives-avatar.mjs +54 -61
  9. package/fesm2022/radix-ng-primitives-avatar.mjs.map +1 -1
  10. package/fesm2022/radix-ng-primitives-button.mjs +123 -0
  11. package/fesm2022/radix-ng-primitives-button.mjs.map +1 -0
  12. package/fesm2022/radix-ng-primitives-calendar.mjs +95 -83
  13. package/fesm2022/radix-ng-primitives-calendar.mjs.map +1 -1
  14. package/fesm2022/radix-ng-primitives-checkbox.mjs +378 -54
  15. package/fesm2022/radix-ng-primitives-checkbox.mjs.map +1 -1
  16. package/fesm2022/radix-ng-primitives-collapsible.mjs +182 -81
  17. package/fesm2022/radix-ng-primitives-collapsible.mjs.map +1 -1
  18. package/fesm2022/radix-ng-primitives-collection.mjs +40 -57
  19. package/fesm2022/radix-ng-primitives-collection.mjs.map +1 -1
  20. package/fesm2022/radix-ng-primitives-config.mjs.map +1 -1
  21. package/fesm2022/radix-ng-primitives-context-menu.mjs +140 -424
  22. package/fesm2022/radix-ng-primitives-context-menu.mjs.map +1 -1
  23. package/fesm2022/radix-ng-primitives-core.mjs +845 -744
  24. package/fesm2022/radix-ng-primitives-core.mjs.map +1 -1
  25. package/fesm2022/radix-ng-primitives-cropper.mjs +288 -308
  26. package/fesm2022/radix-ng-primitives-cropper.mjs.map +1 -1
  27. package/fesm2022/radix-ng-primitives-date-field.mjs +104 -58
  28. package/fesm2022/radix-ng-primitives-date-field.mjs.map +1 -1
  29. package/fesm2022/radix-ng-primitives-dialog.mjs +655 -327
  30. package/fesm2022/radix-ng-primitives-dialog.mjs.map +1 -1
  31. package/fesm2022/radix-ng-primitives-dismissable-layer.mjs +70 -46
  32. package/fesm2022/radix-ng-primitives-dismissable-layer.mjs.map +1 -1
  33. package/fesm2022/radix-ng-primitives-drawer.mjs +960 -0
  34. package/fesm2022/radix-ng-primitives-drawer.mjs.map +1 -0
  35. package/fesm2022/radix-ng-primitives-editable.mjs +304 -23
  36. package/fesm2022/radix-ng-primitives-editable.mjs.map +1 -1
  37. package/fesm2022/radix-ng-primitives-field.mjs +363 -0
  38. package/fesm2022/radix-ng-primitives-field.mjs.map +1 -0
  39. package/fesm2022/radix-ng-primitives-fieldset.mjs +79 -0
  40. package/fesm2022/radix-ng-primitives-fieldset.mjs.map +1 -0
  41. package/fesm2022/radix-ng-primitives-focus-scope.mjs +23 -8
  42. package/fesm2022/radix-ng-primitives-focus-scope.mjs.map +1 -1
  43. package/fesm2022/radix-ng-primitives-input.mjs +172 -0
  44. package/fesm2022/radix-ng-primitives-input.mjs.map +1 -0
  45. package/fesm2022/radix-ng-primitives-label.mjs +6 -6
  46. package/fesm2022/radix-ng-primitives-label.mjs.map +1 -1
  47. package/fesm2022/radix-ng-primitives-menu.mjs +1907 -363
  48. package/fesm2022/radix-ng-primitives-menu.mjs.map +1 -1
  49. package/fesm2022/radix-ng-primitives-menubar.mjs +290 -162
  50. package/fesm2022/radix-ng-primitives-menubar.mjs.map +1 -1
  51. package/fesm2022/radix-ng-primitives-meter.mjs +271 -0
  52. package/fesm2022/radix-ng-primitives-meter.mjs.map +1 -0
  53. package/fesm2022/radix-ng-primitives-navigation-menu.mjs +1052 -1553
  54. package/fesm2022/radix-ng-primitives-navigation-menu.mjs.map +1 -1
  55. package/fesm2022/radix-ng-primitives-number-field.mjs +1102 -367
  56. package/fesm2022/radix-ng-primitives-number-field.mjs.map +1 -1
  57. package/fesm2022/radix-ng-primitives-pagination.mjs.map +1 -1
  58. package/fesm2022/radix-ng-primitives-popover.mjs +978 -989
  59. package/fesm2022/radix-ng-primitives-popover.mjs.map +1 -1
  60. package/fesm2022/radix-ng-primitives-popper.mjs +111 -44
  61. package/fesm2022/radix-ng-primitives-popper.mjs.map +1 -1
  62. package/fesm2022/radix-ng-primitives-portal.mjs +34 -10
  63. package/fesm2022/radix-ng-primitives-portal.mjs.map +1 -1
  64. package/fesm2022/radix-ng-primitives-presence.mjs +134 -246
  65. package/fesm2022/radix-ng-primitives-presence.mjs.map +1 -1
  66. package/fesm2022/radix-ng-primitives-preview-card.mjs +997 -0
  67. package/fesm2022/radix-ng-primitives-preview-card.mjs.map +1 -0
  68. package/fesm2022/radix-ng-primitives-progress.mjs +223 -84
  69. package/fesm2022/radix-ng-primitives-progress.mjs.map +1 -1
  70. package/fesm2022/radix-ng-primitives-radio.mjs +191 -51
  71. package/fesm2022/radix-ng-primitives-radio.mjs.map +1 -1
  72. package/fesm2022/radix-ng-primitives-roving-focus.mjs +96 -50
  73. package/fesm2022/radix-ng-primitives-roving-focus.mjs.map +1 -1
  74. package/fesm2022/radix-ng-primitives-scroll-area.mjs +923 -0
  75. package/fesm2022/radix-ng-primitives-scroll-area.mjs.map +1 -0
  76. package/fesm2022/radix-ng-primitives-select.mjs +791 -509
  77. package/fesm2022/radix-ng-primitives-select.mjs.map +1 -1
  78. package/fesm2022/radix-ng-primitives-separator.mjs +12 -35
  79. package/fesm2022/radix-ng-primitives-separator.mjs.map +1 -1
  80. package/fesm2022/radix-ng-primitives-slider.mjs +969 -717
  81. package/fesm2022/radix-ng-primitives-slider.mjs.map +1 -1
  82. package/fesm2022/radix-ng-primitives-stepper.mjs +15 -19
  83. package/fesm2022/radix-ng-primitives-stepper.mjs.map +1 -1
  84. package/fesm2022/radix-ng-primitives-switch.mjs +125 -113
  85. package/fesm2022/radix-ng-primitives-switch.mjs.map +1 -1
  86. package/fesm2022/radix-ng-primitives-tabs.mjs +390 -108
  87. package/fesm2022/radix-ng-primitives-tabs.mjs.map +1 -1
  88. package/fesm2022/radix-ng-primitives-time-field.mjs +55 -46
  89. package/fesm2022/radix-ng-primitives-time-field.mjs.map +1 -1
  90. package/fesm2022/radix-ng-primitives-toast.mjs +839 -0
  91. package/fesm2022/radix-ng-primitives-toast.mjs.map +1 -0
  92. package/fesm2022/radix-ng-primitives-toggle-group.mjs +121 -247
  93. package/fesm2022/radix-ng-primitives-toggle-group.mjs.map +1 -1
  94. package/fesm2022/radix-ng-primitives-toggle.mjs +98 -61
  95. package/fesm2022/radix-ng-primitives-toggle.mjs.map +1 -1
  96. package/fesm2022/radix-ng-primitives-toolbar.mjs +303 -92
  97. package/fesm2022/radix-ng-primitives-toolbar.mjs.map +1 -1
  98. package/fesm2022/radix-ng-primitives-tooltip.mjs +699 -1072
  99. package/fesm2022/radix-ng-primitives-tooltip.mjs.map +1 -1
  100. package/fesm2022/radix-ng-primitives-visually-hidden.mjs +25 -66
  101. package/fesm2022/radix-ng-primitives-visually-hidden.mjs.map +1 -1
  102. package/meter/README.md +3 -0
  103. package/navigation-menu/README.md +2 -1
  104. package/package.json +39 -18
  105. package/portal/README.md +2 -0
  106. package/preview-card/README.md +3 -0
  107. package/schematics/collection.json +1 -0
  108. package/schematics/ng-add/index.d.ts +3 -2
  109. package/schematics/ng-add/index.js +62 -31
  110. package/schematics/ng-add/index.js.map +1 -1
  111. package/schematics/ng-add/package-config.d.ts +4 -2
  112. package/schematics/ng-add/package-config.js +10 -2
  113. package/schematics/ng-add/package-config.js.map +1 -1
  114. package/schematics/ng-add/schema.d.ts +3 -0
  115. package/schematics/ng-add/schema.js +3 -0
  116. package/schematics/ng-add/schema.js.map +1 -0
  117. package/schematics/ng-add/schema.json +14 -0
  118. package/select/README.md +2 -0
  119. package/types/radix-ng-primitives-accordion.d.ts +51 -16
  120. package/types/radix-ng-primitives-alert-dialog.d.ts +95 -38
  121. package/types/radix-ng-primitives-arrow.d.ts +1 -1
  122. package/types/radix-ng-primitives-aspect-ratio.d.ts +1 -1
  123. package/types/radix-ng-primitives-avatar.d.ts +7 -11
  124. package/types/radix-ng-primitives-button.d.ts +73 -0
  125. package/types/radix-ng-primitives-calendar.d.ts +39 -20
  126. package/types/radix-ng-primitives-checkbox.d.ts +204 -35
  127. package/types/radix-ng-primitives-collapsible.d.ts +114 -40
  128. package/types/radix-ng-primitives-collection.d.ts +38 -34
  129. package/types/radix-ng-primitives-config.d.ts +1 -1
  130. package/types/radix-ng-primitives-context-menu.d.ts +61 -116
  131. package/types/radix-ng-primitives-core.d.ts +345 -235
  132. package/types/radix-ng-primitives-cropper.d.ts +89 -56
  133. package/types/radix-ng-primitives-date-field.d.ts +49 -28
  134. package/types/radix-ng-primitives-dialog.d.ts +283 -165
  135. package/types/radix-ng-primitives-dismissable-layer.d.ts +15 -7
  136. package/types/radix-ng-primitives-drawer.d.ts +426 -0
  137. package/types/radix-ng-primitives-editable.d.ts +91 -14
  138. package/types/radix-ng-primitives-field.d.ts +374 -0
  139. package/types/radix-ng-primitives-fieldset.d.ts +49 -0
  140. package/types/radix-ng-primitives-focus-scope.d.ts +15 -6
  141. package/types/radix-ng-primitives-input.d.ts +87 -0
  142. package/types/radix-ng-primitives-label.d.ts +0 -1
  143. package/types/radix-ng-primitives-menu.d.ts +584 -99
  144. package/types/radix-ng-primitives-menubar.d.ts +61 -50
  145. package/types/radix-ng-primitives-meter.d.ts +194 -0
  146. package/types/radix-ng-primitives-navigation-menu.d.ts +422 -340
  147. package/types/radix-ng-primitives-number-field.d.ts +405 -145
  148. package/types/radix-ng-primitives-pagination.d.ts +2 -2
  149. package/types/radix-ng-primitives-popover.d.ts +366 -351
  150. package/types/radix-ng-primitives-popper.d.ts +68 -11
  151. package/types/radix-ng-primitives-portal.d.ts +14 -6
  152. package/types/radix-ng-primitives-presence.d.ts +28 -76
  153. package/types/radix-ng-primitives-preview-card.d.ts +359 -0
  154. package/types/radix-ng-primitives-progress.d.ts +175 -48
  155. package/types/radix-ng-primitives-radio.d.ts +55 -25
  156. package/types/radix-ng-primitives-roving-focus.d.ts +33 -23
  157. package/types/radix-ng-primitives-scroll-area.d.ts +253 -0
  158. package/types/radix-ng-primitives-select.d.ts +475 -177
  159. package/types/radix-ng-primitives-separator.d.ts +7 -32
  160. package/types/radix-ng-primitives-slider.d.ts +315 -201
  161. package/types/radix-ng-primitives-stepper.d.ts +5 -7
  162. package/types/radix-ng-primitives-switch.d.ts +86 -71
  163. package/types/radix-ng-primitives-tabs.d.ts +213 -79
  164. package/types/radix-ng-primitives-time-field.d.ts +42 -27
  165. package/types/radix-ng-primitives-toast.d.ts +378 -0
  166. package/types/radix-ng-primitives-toggle-group.d.ts +86 -164
  167. package/types/radix-ng-primitives-toggle.d.ts +43 -53
  168. package/types/radix-ng-primitives-toolbar.d.ts +164 -38
  169. package/types/radix-ng-primitives-tooltip.d.ts +348 -384
  170. package/types/radix-ng-primitives-visually-hidden.d.ts +19 -19
  171. package/dropdown-menu/README.md +0 -1
  172. package/fesm2022/radix-ng-primitives-dropdown-menu.mjs +0 -581
  173. package/fesm2022/radix-ng-primitives-dropdown-menu.mjs.map +0 -1
  174. package/fesm2022/radix-ng-primitives-hover-card.mjs +0 -1238
  175. package/fesm2022/radix-ng-primitives-hover-card.mjs.map +0 -1
  176. package/fesm2022/radix-ng-primitives-select2.mjs +0 -897
  177. package/fesm2022/radix-ng-primitives-select2.mjs.map +0 -1
  178. package/fesm2022/radix-ng-primitives-tooltip2.mjs +0 -735
  179. package/fesm2022/radix-ng-primitives-tooltip2.mjs.map +0 -1
  180. package/hover-card/README.md +0 -3
  181. package/select2/README.md +0 -3
  182. package/tooltip2/README.md +0 -3
  183. package/types/radix-ng-primitives-dropdown-menu.d.ts +0 -171
  184. package/types/radix-ng-primitives-hover-card.d.ts +0 -471
  185. package/types/radix-ng-primitives-select2.d.ts +0 -511
  186. package/types/radix-ng-primitives-tooltip2.d.ts +0 -325
@@ -1,180 +1,462 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, model, input, output, Directive, inject, computed, booleanAttribute, effect, NgModule } from '@angular/core';
3
- import { provideToken } from '@radix-ng/primitives/core';
2
+ import { inject, DestroyRef, signal, effect, afterNextRender, untracked, Directive, ElementRef, input, booleanAttribute, computed, model, output, NgModule } from '@angular/core';
3
+ import { createContext, useTransitionStatus, injectId } from '@radix-ng/primitives/core';
4
4
  import * as i1 from '@radix-ng/primitives/roving-focus';
5
5
  import { RdxRovingFocusGroupDirective, RdxRovingFocusItemDirective } from '@radix-ng/primitives/roving-focus';
6
+ import * as i1$1 from '@radix-ng/primitives/presence';
7
+ import { provideRdxPresenceContext, RdxPresenceDirective } from '@radix-ng/primitives/presence';
6
8
 
7
- const RDX_TABS_ROOT_TOKEN = new InjectionToken('RdxTabsRootDirective');
8
- class RdxTabsRootDirective {
9
+ const [injectTabsRootContext, provideTabsRootContext] = createContext('RdxTabsRootContext');
10
+
11
+ function makeTabId(baseId, value) {
12
+ return `${baseId}-tab-${value}`;
13
+ }
14
+ function makePanelId(baseId, value) {
15
+ return `${baseId}-panel-${value}`;
16
+ }
17
+
18
+ /**
19
+ * A visual element that tracks the position and size of the active tab. Exposes the active tab
20
+ * geometry as CSS variables (`--active-tab-{top,right,bottom,left,width,height}`) so it can be
21
+ * animated with CSS.
22
+ *
23
+ * @see https://base-ui.com/react/components/tabs
24
+ */
25
+ class RdxTabsIndicator {
9
26
  constructor() {
10
- /**
11
- * The controlled value of the tab to activate. Should be used in conjunction with `onValueChange`.
12
- */
13
- this.value = model(...(ngDevMode ? [undefined, { debugName: "value" }] : /* istanbul ignore next */ []));
14
- this.defaultValue = input(...(ngDevMode ? [undefined, { debugName: "defaultValue" }] : /* istanbul ignore next */ []));
15
- /**
16
- * When automatic, tabs are activated when receiving focus. When manual, tabs are activated when clicked.
17
- */
18
- this.activationMode = input('automatic', ...(ngDevMode ? [{ debugName: "activationMode" }] : /* istanbul ignore next */ []));
19
- /**
20
- * The orientation of the component.
21
- */
22
- this.orientation = input('horizontal', ...(ngDevMode ? [{ debugName: "orientation" }] : /* istanbul ignore next */ []));
23
- this.dir = input('ltr', ...(ngDevMode ? [{ debugName: "dir" }] : /* istanbul ignore next */ []));
24
- /**
25
- * Event handler called when the value changes.
26
- */
27
- this.onValueChange = output();
27
+ this.rootContext = injectTabsRootContext();
28
+ this.destroyRef = inject(DestroyRef);
29
+ /** @ignore */
30
+ this.geometry = signal(null, ...(ngDevMode ? [{ debugName: "geometry" }] : /* istanbul ignore next */ []));
31
+ // Re-measure whenever the selection, orientation or the list element changes.
32
+ effect(() => {
33
+ this.rootContext.value();
34
+ this.rootContext.orientation();
35
+ this.rootContext.tabListElement();
36
+ this.scheduleMeasure();
37
+ });
38
+ afterNextRender(() => {
39
+ const list = untracked(this.rootContext.tabListElement);
40
+ if (!list || typeof ResizeObserver === 'undefined') {
41
+ this.measure();
42
+ return;
43
+ }
44
+ const observer = new ResizeObserver(() => this.measure());
45
+ observer.observe(list);
46
+ this.destroyRef.onDestroy(() => observer.disconnect());
47
+ this.measure();
48
+ });
28
49
  }
29
- ngOnInit() {
30
- if (this.defaultValue()) {
31
- this.value.set(this.defaultValue());
50
+ scheduleMeasure() {
51
+ if (typeof requestAnimationFrame === 'undefined') {
52
+ this.measure();
53
+ return;
32
54
  }
55
+ requestAnimationFrame(() => this.measure());
33
56
  }
34
- select(value) {
35
- this.value.set(value);
36
- this.onValueChange.emit(value);
37
- }
38
- /** @ignore */
39
- getBaseId() {
40
- return `tabs-${Math.random().toString(36).substr(2, 9)}`;
57
+ measure() {
58
+ const list = untracked(this.rootContext.tabListElement);
59
+ const value = untracked(this.rootContext.value);
60
+ if (!list || value == null || typeof document === 'undefined') {
61
+ this.geometry.set(null);
62
+ return;
63
+ }
64
+ const tab = document.getElementById(makeTabId(this.rootContext.baseId, value));
65
+ if (!tab) {
66
+ this.geometry.set(null);
67
+ return;
68
+ }
69
+ const listRect = list.getBoundingClientRect();
70
+ const tabRect = tab.getBoundingClientRect();
71
+ // Measure `left` / `top` relative to the list's scrollable content origin rather than its
72
+ // visible edge. When the list is a scroll container (e.g. its tabs overflow inside a Scroll
73
+ // Area), the indicator is an absolutely positioned child of that container and scrolls along
74
+ // with the content, so these offsets must be content-relative to stay aligned with the tab.
75
+ // For non-scrolling lists `scrollLeft` / `scrollTop` are `0`, leaving the geometry unchanged.
76
+ // `right` / `bottom` stay visible-edge relative — there is no unambiguous content-relative
77
+ // meaning for them inside a scroll container, and the moving indicator uses `left`/`top`.
78
+ const scrollLeft = list.scrollLeft;
79
+ const scrollTop = list.scrollTop;
80
+ this.geometry.set({
81
+ top: tabRect.top - listRect.top + scrollTop,
82
+ right: listRect.right - tabRect.right,
83
+ bottom: listRect.bottom - tabRect.bottom,
84
+ left: tabRect.left - listRect.left + scrollLeft,
85
+ width: tabRect.width,
86
+ height: tabRect.height
87
+ });
41
88
  }
42
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxTabsRootDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
43
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxTabsRootDirective, 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 }, activationMode: { classPropertyName: "activationMode", publicName: "activationMode", isSignal: true, isRequired: false, transformFunction: null }, orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null }, dir: { classPropertyName: "dir", publicName: "dir", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", onValueChange: "onValueChange" }, host: { properties: { "attr.data-orientation": "orientation()", "attr.dir": "dir()" } }, providers: [provideToken(RDX_TABS_ROOT_TOKEN, RdxTabsRootDirective)], ngImport: i0 }); }
89
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxTabsIndicator, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
90
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxTabsIndicator, isStandalone: true, selector: "[rdxTabsIndicator]", host: { properties: { "attr.data-orientation": "rootContext.orientation()", "attr.data-activation-direction": "rootContext.activationDirection()", "style.--active-tab-top.px": "geometry()?.top", "style.--active-tab-right.px": "geometry()?.right", "style.--active-tab-bottom.px": "geometry()?.bottom", "style.--active-tab-left.px": "geometry()?.left", "style.--active-tab-width.px": "geometry()?.width", "style.--active-tab-height.px": "geometry()?.height" } }, exportAs: ["rdxTabsIndicator"], ngImport: i0 }); }
44
91
  }
45
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxTabsRootDirective, decorators: [{
92
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxTabsIndicator, decorators: [{
46
93
  type: Directive,
47
94
  args: [{
48
- selector: '[rdxTabsRoot]',
49
- providers: [provideToken(RDX_TABS_ROOT_TOKEN, RdxTabsRootDirective)],
95
+ selector: '[rdxTabsIndicator]',
96
+ exportAs: 'rdxTabsIndicator',
50
97
  host: {
51
- '[attr.data-orientation]': 'orientation()',
52
- '[attr.dir]': 'dir()'
98
+ '[attr.data-orientation]': 'rootContext.orientation()',
99
+ '[attr.data-activation-direction]': 'rootContext.activationDirection()',
100
+ '[style.--active-tab-top.px]': 'geometry()?.top',
101
+ '[style.--active-tab-right.px]': 'geometry()?.right',
102
+ '[style.--active-tab-bottom.px]': 'geometry()?.bottom',
103
+ '[style.--active-tab-left.px]': 'geometry()?.left',
104
+ '[style.--active-tab-width.px]': 'geometry()?.width',
105
+ '[style.--active-tab-height.px]': 'geometry()?.height'
53
106
  }
54
107
  }]
55
- }], 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 }] }], activationMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "activationMode", required: false }] }], orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }], dir: [{ type: i0.Input, args: [{ isSignal: true, alias: "dir", required: false }] }], onValueChange: [{ type: i0.Output, args: ["onValueChange"] }] } });
108
+ }], ctorParameters: () => [] });
56
109
 
57
- function makeTriggerId(baseId, value) {
58
- return `${baseId}-trigger-${value}`;
59
- }
60
- function makeContentId(baseId, value) {
61
- return `${baseId}-content-${value}`;
110
+ /**
111
+ * Groups the individual tab buttons and manages keyboard navigation.
112
+ *
113
+ * @see https://base-ui.com/react/components/tabs
114
+ */
115
+ class RdxTabsList {
116
+ constructor() {
117
+ this.rootContext = injectTabsRootContext();
118
+ this.elementRef = inject(ElementRef);
119
+ this.rovingFocusGroup = inject(RdxRovingFocusGroupDirective, { self: true });
120
+ /**
121
+ * Whether a tab is activated when it receives focus (automatic activation).
122
+ * When `false`, tabs are only activated on click or Enter/Space.
123
+ *
124
+ * @default false
125
+ */
126
+ this.activateOnFocus = input(false, { ...(ngDevMode ? { debugName: "activateOnFocus" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
127
+ /**
128
+ * Whether keyboard navigation should loop from the last tab back to the first.
129
+ *
130
+ * @default true
131
+ */
132
+ this.loopFocus = input(true, { ...(ngDevMode ? { debugName: "loopFocus" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
133
+ this.rootContext.setTabListElement(this.elementRef.nativeElement);
134
+ effect(() => {
135
+ this.rovingFocusGroup.setOrientation(this.rootContext.orientation());
136
+ this.rovingFocusGroup.setLoop(this.loopFocus());
137
+ });
138
+ effect(() => this.rootContext.setActivateOnFocus(this.activateOnFocus()));
139
+ }
140
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxTabsList, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
141
+ 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.RdxRovingFocusGroupDirective }], ngImport: i0 }); }
62
142
  }
143
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxTabsList, decorators: [{
144
+ type: Directive,
145
+ args: [{
146
+ selector: '[rdxTabsList]',
147
+ exportAs: 'rdxTabsList',
148
+ hostDirectives: [RdxRovingFocusGroupDirective],
149
+ host: {
150
+ role: 'tablist',
151
+ '[attr.aria-orientation]': 'rootContext.orientation()',
152
+ '[attr.data-orientation]': 'rootContext.orientation()',
153
+ '[attr.data-activation-direction]': 'rootContext.activationDirection()'
154
+ }
155
+ }]
156
+ }], ctorParameters: () => [], propDecorators: { activateOnFocus: [{ type: i0.Input, args: [{ isSignal: true, alias: "activateOnFocus", required: false }] }], loopFocus: [{ type: i0.Input, args: [{ isSignal: true, alias: "loopFocus", required: false }] }] } });
63
157
 
64
- class RdxTabsContentDirective {
158
+ const panelPresenceContext = () => ({ present: inject(RdxTabsPanel).present });
159
+ /**
160
+ * A panel displayed when its corresponding tab is active.
161
+ *
162
+ * By default the panel stays in the DOM and is toggled with the `hidden` attribute. To unmount the
163
+ * contents while inactive (Base UI's default `keepMounted: false`), nest a `*rdxTabsPanelPresence`
164
+ * structural directive inside it; set `keepMounted` to keep the contents mounted regardless.
165
+ *
166
+ * @see https://base-ui.com/react/components/tabs
167
+ */
168
+ class RdxTabsPanel {
65
169
  constructor() {
66
- this.tabsContext = inject(RDX_TABS_ROOT_TOKEN);
170
+ this.elementRef = inject(ElementRef);
171
+ this.rootContext = injectTabsRootContext();
67
172
  /**
68
- * A unique value that associates the content with a trigger.
173
+ * A unique value that associates the panel with a tab.
69
174
  */
70
175
  this.value = input.required(...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
71
- this.contentId = computed(() => makeContentId(this.tabsContext.getBaseId(), this.value()), ...(ngDevMode ? [{ debugName: "contentId" }] : /* istanbul ignore next */ []));
72
- this.triggerId = computed(() => makeTriggerId(this.tabsContext.getBaseId(), this.value()), ...(ngDevMode ? [{ debugName: "triggerId" }] : /* istanbul ignore next */ []));
73
- this.selected = computed(() => this.tabsContext.value() === this.value(), ...(ngDevMode ? [{ debugName: "selected" }] : /* istanbul ignore next */ []));
176
+ /**
177
+ * Keep the panel contents mounted in the DOM while inactive (the contents are still hidden).
178
+ * Only relevant together with `*rdxTabsPanelPresence`, which otherwise unmounts them.
179
+ *
180
+ * @default false
181
+ */
182
+ this.keepMounted = input(false, { ...(ngDevMode ? { debugName: "keepMounted" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
183
+ this.transition = useTransitionStatus(() => { });
184
+ /** Reactive enter/exit transition phase (`'starting'` | `'ending'` | `undefined`). */
185
+ this.transitionStatus = this.transition.status;
186
+ /** @ignore */
187
+ this.panelId = computed(() => makePanelId(this.rootContext.baseId, this.value()), ...(ngDevMode ? [{ debugName: "panelId" }] : /* istanbul ignore next */ []));
188
+ /** @ignore */
189
+ this.tabId = computed(() => makeTabId(this.rootContext.baseId, this.value()), ...(ngDevMode ? [{ debugName: "tabId" }] : /* istanbul ignore next */ []));
190
+ /** Whether this panel's tab is currently selected. */
191
+ this.active = computed(() => this.rootContext.value() === this.value(), ...(ngDevMode ? [{ debugName: "active" }] : /* istanbul ignore next */ []));
192
+ /** `true` once a `*rdxTabsPanelPresence` child takes over mounting. */
193
+ this.hasPresence = signal(false, ...(ngDevMode ? [{ debugName: "hasPresence" }] : /* istanbul ignore next */ []));
194
+ /**
195
+ * Whether the contents should be present for `*rdxTabsPanelPresence`. Flips with `active` so the
196
+ * presence directive owns the exit-animation timing (it keeps the node mounted until its exit
197
+ * `@keyframes` finishes); `keepMounted` keeps them mounted regardless.
198
+ */
199
+ this.present = computed(() => this.keepMounted() || this.active(), ...(ngDevMode ? [{ debugName: "present" }] : /* istanbul ignore next */ []));
200
+ /**
201
+ * The `hidden` attribute value. The panel is shown while active or while its exit transition
202
+ * runs. When a presence child unmounts the contents we no longer force `hidden` (the empty
203
+ * element renders nothing), unless `keepMounted` keeps the inactive contents around.
204
+ */
205
+ this.hidden = computed(() => !this.active() && this.transitionStatus() !== 'ending' && (!this.hasPresence() || this.keepMounted()), ...(ngDevMode ? [{ debugName: "hidden" }] : /* istanbul ignore next */ []));
206
+ /** @ignore Index of the panel, derived from the order of its associated tab. */
207
+ this.index = computed(() => {
208
+ const list = this.rootContext.tabListElement();
209
+ if (!list) {
210
+ return null;
211
+ }
212
+ const tabs = Array.from(list.querySelectorAll('[role="tab"]'));
213
+ const position = tabs.findIndex((tab) => tab.id === makeTabId(this.rootContext.baseId, this.value()));
214
+ return position === -1 ? null : position;
215
+ }, ...(ngDevMode ? [{ debugName: "index" }] : /* istanbul ignore next */ []));
216
+ this.previousActive = false;
217
+ this.isFirstRun = true;
218
+ const unregister = this.transition.registerElement(this.elementRef.nativeElement);
219
+ inject(DestroyRef).onDestroy(unregister);
220
+ effect(() => {
221
+ const active = this.active();
222
+ // Settle the initial state without playing an enter transition.
223
+ if (this.isFirstRun) {
224
+ this.isFirstRun = false;
225
+ this.previousActive = active;
226
+ return;
227
+ }
228
+ if (active !== this.previousActive) {
229
+ this.previousActive = active;
230
+ untracked(() => this.transition.start(active));
231
+ }
232
+ });
74
233
  }
75
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxTabsContentDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
76
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxTabsContentDirective, isStandalone: true, selector: "[rdxTabsContent]", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null } }, host: { attributes: { "role": "tabpanel", "tabindex": "0" }, properties: { "id": "contentId()", "attr.aria-labelledby": "triggerId()", "attr.data-state": "selected() ? \"active\" : \"inactive\"", "attr.data-orientation": "tabsContext.orientation()", "hidden": "!selected()" } }, ngImport: i0 }); }
234
+ /** @ignore Called by `RdxTabsPanelPresence` so the panel stops forcing `hidden`. */
235
+ markHasPresence() {
236
+ this.hasPresence.set(true);
237
+ }
238
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxTabsPanel, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
239
+ 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: { "id": "panelId()", "attr.tabindex": "active() ? 0 : undefined", "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"], ngImport: i0 }); }
77
240
  }
78
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxTabsContentDirective, decorators: [{
241
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxTabsPanel, decorators: [{
79
242
  type: Directive,
80
243
  args: [{
81
- selector: '[rdxTabsContent]',
244
+ selector: '[rdxTabsPanel]',
245
+ exportAs: 'rdxTabsPanel',
246
+ providers: [provideRdxPresenceContext(panelPresenceContext)],
82
247
  host: {
83
248
  role: 'tabpanel',
84
- tabindex: '0',
85
- '[id]': 'contentId()',
86
- '[attr.aria-labelledby]': 'triggerId()',
87
- '[attr.data-state]': 'selected() ? "active" : "inactive"',
88
- '[attr.data-orientation]': 'tabsContext.orientation()',
89
- '[hidden]': '!selected()'
249
+ '[id]': 'panelId()',
250
+ '[attr.tabindex]': 'active() ? 0 : undefined',
251
+ '[attr.aria-labelledby]': 'tabId()',
252
+ '[attr.data-orientation]': 'rootContext.orientation()',
253
+ '[attr.data-activation-direction]': 'rootContext.activationDirection()',
254
+ '[attr.data-index]': 'index()',
255
+ '[attr.data-hidden]': 'active() ? undefined : ""',
256
+ '[attr.data-starting-style]': 'transitionStatus() === "starting" ? "" : undefined',
257
+ '[attr.data-ending-style]': 'transitionStatus() === "ending" ? "" : undefined',
258
+ '[hidden]': 'hidden()'
90
259
  }
91
260
  }]
92
- }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }] } });
261
+ }], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }], keepMounted: [{ type: i0.Input, args: [{ isSignal: true, alias: "keepMounted", required: false }] }] } });
93
262
 
94
- class RdxTabsListDirective {
263
+ /**
264
+ * Structural directive that mounts the tab panel contents only while the panel is active,
265
+ * unmounting them once the exit animation finishes. Apply it inside an `[rdxTabsPanel]` to get
266
+ * Base UI's default unmounting behavior; combine with `keepMounted` on the panel to keep the
267
+ * contents mounted instead.
268
+ *
269
+ * The presence state is read from the parent panel through {@link RdxPresenceDirective}.
270
+ */
271
+ class RdxTabsPanelPresence {
95
272
  constructor() {
96
- this.tabsContext = inject(RDX_TABS_ROOT_TOKEN);
273
+ inject(RdxTabsPanel).markHasPresence();
97
274
  }
98
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxTabsListDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
99
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxTabsListDirective, isStandalone: true, selector: "[rdxTabsList]", host: { attributes: { "role": "tablist" }, properties: { "attr.aria-orientation": "tabsContext.orientation()", "attr.data-orientation": "tabsContext.orientation()" } }, hostDirectives: [{ directive: i1.RdxRovingFocusGroupDirective, inputs: ["dir", "dir", "orientation", "orientation", "loop", "loop"] }], ngImport: i0 }); }
275
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxTabsPanelPresence, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
276
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxTabsPanelPresence, isStandalone: true, selector: "ng-template[rdxTabsPanelPresence]", hostDirectives: [{ directive: i1$1.RdxPresenceDirective }], ngImport: i0 }); }
100
277
  }
101
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxTabsListDirective, decorators: [{
278
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxTabsPanelPresence, decorators: [{
102
279
  type: Directive,
103
280
  args: [{
104
- selector: '[rdxTabsList]',
105
- hostDirectives: [{ directive: RdxRovingFocusGroupDirective, inputs: ['dir', 'orientation', 'loop'] }],
281
+ selector: 'ng-template[rdxTabsPanelPresence]',
282
+ hostDirectives: [RdxPresenceDirective]
283
+ }]
284
+ }], ctorParameters: () => [] });
285
+
286
+ const rootContext = () => {
287
+ const root = inject(RdxTabsRoot);
288
+ return {
289
+ baseId: root.baseId,
290
+ value: root.value,
291
+ orientation: root.orientation,
292
+ activationDirection: root.activationDirection.asReadonly(),
293
+ activateOnFocus: root.activateOnFocus.asReadonly(),
294
+ tabListElement: root.tabListElement.asReadonly(),
295
+ setValue: (value) => root.setValue(value),
296
+ setActivateOnFocus: (value) => root.activateOnFocus.set(value),
297
+ setTabListElement: (element) => root.tabListElement.set(element)
298
+ };
299
+ };
300
+ /**
301
+ * Groups the tabs and the corresponding panels.
302
+ *
303
+ * @see https://base-ui.com/react/components/tabs
304
+ */
305
+ class RdxTabsRoot {
306
+ constructor() {
307
+ /** @ignore */
308
+ this.baseId = injectId('rdx-tabs-');
309
+ /**
310
+ * The value of the currently selected tab. Use together with `(onValueChange)` for controlled state.
311
+ */
312
+ this.value = model(...(ngDevMode ? [undefined, { debugName: "value" }] : /* istanbul ignore next */ []));
313
+ /**
314
+ * The value of the tab that should be initially selected when uncontrolled.
315
+ */
316
+ this.defaultValue = input(...(ngDevMode ? [undefined, { debugName: "defaultValue" }] : /* istanbul ignore next */ []));
317
+ /**
318
+ * The orientation the tabs are laid out. Controls arrow-key navigation
319
+ * (left/right vs. up/down).
320
+ *
321
+ * @default 'horizontal'
322
+ */
323
+ this.orientation = input('horizontal', ...(ngDevMode ? [{ debugName: "orientation" }] : /* istanbul ignore next */ []));
324
+ /**
325
+ * Event emitted when the selected tab changes.
326
+ */
327
+ this.onValueChange = output();
328
+ /** @ignore Set by `[rdxTabsList]`. */
329
+ this.activateOnFocus = signal(false, ...(ngDevMode ? [{ debugName: "activateOnFocus" }] : /* istanbul ignore next */ []));
330
+ /** @ignore Set by `[rdxTabsList]`. */
331
+ this.tabListElement = signal(null, ...(ngDevMode ? [{ debugName: "tabListElement" }] : /* istanbul ignore next */ []));
332
+ /** @ignore */
333
+ this.activationDirection = signal('none', ...(ngDevMode ? [{ debugName: "activationDirection" }] : /* istanbul ignore next */ []));
334
+ effect(() => {
335
+ const initial = this.defaultValue();
336
+ if (initial !== undefined && untracked(this.value) === undefined) {
337
+ this.value.set(initial);
338
+ }
339
+ });
340
+ }
341
+ /** @ignore */
342
+ setValue(value) {
343
+ const previous = this.value();
344
+ if (previous === value) {
345
+ return;
346
+ }
347
+ this.activationDirection.set(this.computeDirection(previous, value));
348
+ this.value.set(value);
349
+ this.onValueChange.emit(value);
350
+ }
351
+ computeDirection(previous, next) {
352
+ const list = this.tabListElement();
353
+ if (!list || previous === undefined || previous === null) {
354
+ return 'none';
355
+ }
356
+ const tabs = Array.from(list.querySelectorAll('[role="tab"]'));
357
+ const previousIndex = tabs.findIndex((tab) => tab.id === makeTabId(this.baseId, previous));
358
+ const nextIndex = tabs.findIndex((tab) => tab.id === makeTabId(this.baseId, next));
359
+ if (previousIndex === -1 || nextIndex === -1 || previousIndex === nextIndex) {
360
+ return 'none';
361
+ }
362
+ const horizontal = this.orientation() === 'horizontal';
363
+ if (nextIndex > previousIndex) {
364
+ return horizontal ? 'right' : 'down';
365
+ }
366
+ return horizontal ? 'left' : 'up';
367
+ }
368
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxTabsRoot, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
369
+ 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 }); }
370
+ }
371
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxTabsRoot, decorators: [{
372
+ type: Directive,
373
+ args: [{
374
+ selector: '[rdxTabsRoot]',
375
+ exportAs: 'rdxTabsRoot',
376
+ providers: [provideTabsRootContext(rootContext)],
106
377
  host: {
107
- role: 'tablist',
108
- '[attr.aria-orientation]': 'tabsContext.orientation()',
109
- '[attr.data-orientation]': 'tabsContext.orientation()'
378
+ '[attr.data-orientation]': 'orientation()',
379
+ '[attr.data-activation-direction]': 'activationDirection()'
110
380
  }
111
381
  }]
112
- }] });
382
+ }], 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"] }] } });
113
383
 
114
- class RdxTabsTriggerDirective {
384
+ /**
385
+ * An individual interactive tab button that activates its corresponding panel.
386
+ *
387
+ * @see https://base-ui.com/react/components/tabs
388
+ */
389
+ class RdxTabsTab {
115
390
  constructor() {
116
- this.rdxRovingFocusItemDirective = inject(RdxRovingFocusItemDirective);
117
- this.tabsContext = inject(RDX_TABS_ROOT_TOKEN);
391
+ this.rootContext = injectTabsRootContext();
392
+ this.rovingFocusItem = inject(RdxRovingFocusItemDirective);
118
393
  /**
119
- * A unique value that associates the trigger with a content.
394
+ * A unique value that associates the tab with a panel.
120
395
  */
121
396
  this.value = input.required(...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
122
397
  /**
123
- * When true, prevents the user from interacting with the tab.
398
+ * When `true`, prevents the user from interacting with the tab.
124
399
  */
125
400
  this.disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
126
- this.contentId = computed(() => makeContentId(this.tabsContext.getBaseId(), this.value()), ...(ngDevMode ? [{ debugName: "contentId" }] : /* istanbul ignore next */ []));
127
- this.triggerId = computed(() => makeTriggerId(this.tabsContext.getBaseId(), this.value()), ...(ngDevMode ? [{ debugName: "triggerId" }] : /* istanbul ignore next */ []));
128
- this.isSelected = computed(() => this.tabsContext.value() === this.value(), ...(ngDevMode ? [{ debugName: "isSelected" }] : /* istanbul ignore next */ []));
129
- effect(() => this.rdxRovingFocusItemDirective.setActive(this.isSelected()));
401
+ /** @ignore */
402
+ this.tabId = computed(() => makeTabId(this.rootContext.baseId, this.value()), ...(ngDevMode ? [{ debugName: "tabId" }] : /* istanbul ignore next */ []));
403
+ /** @ignore */
404
+ this.panelId = computed(() => makePanelId(this.rootContext.baseId, this.value()), ...(ngDevMode ? [{ debugName: "panelId" }] : /* istanbul ignore next */ []));
405
+ /** @ignore */
406
+ this.active = computed(() => this.rootContext.value() === this.value(), ...(ngDevMode ? [{ debugName: "active" }] : /* istanbul ignore next */ []));
407
+ effect(() => {
408
+ this.rovingFocusItem.setActive(this.active());
409
+ this.rovingFocusItem.setFocusable(!this.disabled());
410
+ });
130
411
  }
412
+ /** @ignore */
131
413
  onMouseDown(event) {
132
- const mouseEvent = event;
133
- // only call handler if it's the left button (mousedown gets triggered by all mouse buttons)
134
- // but not when the control key is pressed (avoiding MacOS right click)
135
- if (!this.disabled() && mouseEvent.button === 0 && !mouseEvent.ctrlKey) {
136
- this.tabsContext?.select(this.value());
414
+ // Only the primary button selects; ignore Ctrl-click (macOS right-click emulation).
415
+ if (!this.disabled() && event.button === 0 && !event.ctrlKey) {
416
+ this.rootContext.setValue(this.value());
137
417
  }
138
418
  else {
139
- // prevent focus to avoid accidental activation
419
+ // Prevent focus to avoid accidental activation.
140
420
  event.preventDefault();
141
421
  }
142
422
  }
423
+ /** @ignore */
143
424
  onKeyDown(event) {
144
- const keyEvent = event;
145
- if ([' ', 'Enter'].includes(keyEvent.key)) {
146
- this.tabsContext?.select(this.value());
425
+ if (!this.disabled() && (event.key === ' ' || event.key === 'Enter')) {
426
+ this.rootContext.setValue(this.value());
147
427
  }
148
428
  }
429
+ /** @ignore */
149
430
  onFocus() {
150
- const isAutomaticActivation = this.tabsContext.activationMode() !== 'manual';
151
- if (!this.isSelected() && !this.disabled() && isAutomaticActivation) {
152
- this.tabsContext?.select(this.value());
431
+ if (!this.active() && !this.disabled() && this.rootContext.activateOnFocus()) {
432
+ this.rootContext.setValue(this.value());
153
433
  }
154
434
  }
155
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxTabsTriggerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
156
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxTabsTriggerDirective, isStandalone: true, selector: "[rdxTabsTrigger]", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "type": "button", "role": "tab" }, listeners: { "mousedown": "onMouseDown($event)", "keydown": "onKeyDown($event)", "focus": "onFocus()" }, properties: { "id": "triggerId()", "attr.aria-selected": "isSelected()", "attr.aria-controls": "contentId()", "attr.data-disabled": "disabled() ? '' : undefined", "attr.disabled": "disabled() ? \"\" : undefined", "attr.data-state": "isSelected() ? 'active' : 'inactive'", "attr.data-orientation": "tabsContext.orientation()" } }, hostDirectives: [{ directive: i1.RdxRovingFocusItemDirective, inputs: ["focusable", "focusable", "active", "active", "allowShiftKey", "allowShiftKey"] }], ngImport: i0 }); }
435
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxTabsTab, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
436
+ 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 } }, host: { attributes: { "type": "button", "role": "tab" }, listeners: { "mousedown": "onMouseDown($event)", "keydown": "onKeyDown($event)", "focus": "onFocus()" }, properties: { "id": "tabId()", "attr.aria-selected": "active()", "attr.aria-controls": "panelId()", "attr.data-orientation": "rootContext.orientation()", "attr.data-activation-direction": "rootContext.activationDirection()", "attr.data-active": "active() ? \"\" : undefined", "attr.data-disabled": "disabled() ? \"\" : undefined", "attr.disabled": "disabled() ? \"\" : undefined" } }, exportAs: ["rdxTabsTab"], hostDirectives: [{ directive: i1.RdxRovingFocusItemDirective, inputs: ["allowShiftKey", "allowShiftKey"] }], ngImport: i0 }); }
157
437
  }
158
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxTabsTriggerDirective, decorators: [{
438
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxTabsTab, decorators: [{
159
439
  type: Directive,
160
440
  args: [{
161
- selector: '[rdxTabsTrigger]',
441
+ selector: '[rdxTabsTab]',
442
+ exportAs: 'rdxTabsTab',
162
443
  hostDirectives: [
163
444
  {
164
445
  directive: RdxRovingFocusItemDirective,
165
- inputs: ['focusable', 'active', 'allowShiftKey']
446
+ inputs: ['allowShiftKey']
166
447
  }
167
448
  ],
168
449
  host: {
169
450
  type: 'button',
170
451
  role: 'tab',
171
- '[id]': 'triggerId()',
172
- '[attr.aria-selected]': 'isSelected()',
173
- '[attr.aria-controls]': 'contentId()',
174
- '[attr.data-disabled]': "disabled() ? '' : undefined",
452
+ '[id]': 'tabId()',
453
+ '[attr.aria-selected]': 'active()',
454
+ '[attr.aria-controls]': 'panelId()',
455
+ '[attr.data-orientation]': 'rootContext.orientation()',
456
+ '[attr.data-activation-direction]': 'rootContext.activationDirection()',
457
+ '[attr.data-active]': 'active() ? "" : undefined',
458
+ '[attr.data-disabled]': 'disabled() ? "" : undefined',
175
459
  '[attr.disabled]': 'disabled() ? "" : undefined',
176
- '[attr.data-state]': "isSelected() ? 'active' : 'inactive'",
177
- '[attr.data-orientation]': 'tabsContext.orientation()',
178
460
  '(mousedown)': 'onMouseDown($event)',
179
461
  '(keydown)': 'onKeyDown($event)',
180
462
  '(focus)': 'onFocus()'
@@ -182,10 +464,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
182
464
  }]
183
465
  }], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }] } });
184
466
 
185
- const tabsImports = [RdxTabsRootDirective, RdxTabsContentDirective, RdxTabsListDirective, RdxTabsTriggerDirective];
467
+ const tabsImports = [RdxTabsRoot, RdxTabsList, RdxTabsTab, RdxTabsPanel, RdxTabsPanelPresence, RdxTabsIndicator];
186
468
  class RdxTabsModule {
187
469
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxTabsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
188
- static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.9", ngImport: i0, type: RdxTabsModule, imports: [RdxTabsRootDirective, RdxTabsContentDirective, RdxTabsListDirective, RdxTabsTriggerDirective], exports: [RdxTabsRootDirective, RdxTabsContentDirective, RdxTabsListDirective, RdxTabsTriggerDirective] }); }
470
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.9", ngImport: i0, type: RdxTabsModule, imports: [RdxTabsRoot, RdxTabsList, RdxTabsTab, RdxTabsPanel, RdxTabsPanelPresence, RdxTabsIndicator], exports: [RdxTabsRoot, RdxTabsList, RdxTabsTab, RdxTabsPanel, RdxTabsPanelPresence, RdxTabsIndicator] }); }
189
471
  static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxTabsModule }); }
190
472
  }
191
473
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxTabsModule, decorators: [{
@@ -200,5 +482,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
200
482
  * Generated bundle index. Do not edit.
201
483
  */
202
484
 
203
- export { RDX_TABS_ROOT_TOKEN, RdxTabsContentDirective, RdxTabsListDirective, RdxTabsModule, RdxTabsRootDirective, RdxTabsTriggerDirective };
485
+ export { RdxTabsIndicator, RdxTabsList, RdxTabsModule, RdxTabsPanel, RdxTabsPanelPresence, RdxTabsRoot, RdxTabsTab, injectTabsRootContext, provideTabsRootContext, tabsImports };
204
486
  //# sourceMappingURL=radix-ng-primitives-tabs.mjs.map