@radix-ng/primitives 1.0.0-beta.5 → 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.
Files changed (62) hide show
  1. package/composite/README.md +3 -0
  2. package/fesm2022/radix-ng-primitives-accordion.mjs +20 -44
  3. package/fesm2022/radix-ng-primitives-accordion.mjs.map +1 -1
  4. package/fesm2022/radix-ng-primitives-checkbox.mjs +134 -58
  5. package/fesm2022/radix-ng-primitives-checkbox.mjs.map +1 -1
  6. package/fesm2022/radix-ng-primitives-composite.mjs +599 -0
  7. package/fesm2022/radix-ng-primitives-composite.mjs.map +1 -0
  8. package/fesm2022/radix-ng-primitives-drawer.mjs +442 -2
  9. package/fesm2022/radix-ng-primitives-drawer.mjs.map +1 -1
  10. package/fesm2022/radix-ng-primitives-menu.mjs +315 -68
  11. package/fesm2022/radix-ng-primitives-menu.mjs.map +1 -1
  12. package/fesm2022/radix-ng-primitives-menubar.mjs +91 -36
  13. package/fesm2022/radix-ng-primitives-menubar.mjs.map +1 -1
  14. package/fesm2022/radix-ng-primitives-navigation-menu.mjs +281 -88
  15. package/fesm2022/radix-ng-primitives-navigation-menu.mjs.map +1 -1
  16. package/fesm2022/radix-ng-primitives-popover.mjs +40 -15
  17. package/fesm2022/radix-ng-primitives-popover.mjs.map +1 -1
  18. package/fesm2022/radix-ng-primitives-popper.mjs +73 -65
  19. package/fesm2022/radix-ng-primitives-popper.mjs.map +1 -1
  20. package/fesm2022/radix-ng-primitives-radio.mjs +63 -27
  21. package/fesm2022/radix-ng-primitives-radio.mjs.map +1 -1
  22. package/fesm2022/radix-ng-primitives-scroll-area.mjs +56 -25
  23. package/fesm2022/radix-ng-primitives-scroll-area.mjs.map +1 -1
  24. package/fesm2022/radix-ng-primitives-select.mjs +59 -29
  25. package/fesm2022/radix-ng-primitives-select.mjs.map +1 -1
  26. package/fesm2022/radix-ng-primitives-slider.mjs +57 -13
  27. package/fesm2022/radix-ng-primitives-slider.mjs.map +1 -1
  28. package/fesm2022/radix-ng-primitives-tabs.mjs +335 -73
  29. package/fesm2022/radix-ng-primitives-tabs.mjs.map +1 -1
  30. package/fesm2022/radix-ng-primitives-toggle-group.mjs +66 -21
  31. package/fesm2022/radix-ng-primitives-toggle-group.mjs.map +1 -1
  32. package/fesm2022/radix-ng-primitives-toggle.mjs +29 -11
  33. package/fesm2022/radix-ng-primitives-toggle.mjs.map +1 -1
  34. package/fesm2022/radix-ng-primitives-toolbar.mjs +68 -36
  35. package/fesm2022/radix-ng-primitives-toolbar.mjs.map +1 -1
  36. package/navigation-menu/README.md +5 -2
  37. package/package.json +6 -10
  38. package/types/radix-ng-primitives-accordion.d.ts +12 -16
  39. package/types/radix-ng-primitives-checkbox.d.ts +98 -70
  40. package/types/radix-ng-primitives-composite.d.ts +195 -0
  41. package/types/radix-ng-primitives-drawer.d.ts +40 -2
  42. package/types/radix-ng-primitives-menu.d.ts +46 -16
  43. package/types/radix-ng-primitives-menubar.d.ts +12 -5
  44. package/types/radix-ng-primitives-navigation-menu.d.ts +65 -33
  45. package/types/radix-ng-primitives-popover.d.ts +9 -5
  46. package/types/radix-ng-primitives-popper.d.ts +1 -0
  47. package/types/radix-ng-primitives-radio.d.ts +11 -9
  48. package/types/radix-ng-primitives-scroll-area.d.ts +4 -1
  49. package/types/radix-ng-primitives-select.d.ts +46 -32
  50. package/types/radix-ng-primitives-slider.d.ts +19 -4
  51. package/types/radix-ng-primitives-tabs.d.ts +69 -14
  52. package/types/radix-ng-primitives-toggle-group.d.ts +27 -16
  53. package/types/radix-ng-primitives-toggle.d.ts +5 -5
  54. package/types/radix-ng-primitives-toolbar.d.ts +84 -69
  55. package/collection/README.md +0 -1
  56. package/fesm2022/radix-ng-primitives-collection.mjs +0 -72
  57. package/fesm2022/radix-ng-primitives-collection.mjs.map +0 -1
  58. package/fesm2022/radix-ng-primitives-roving-focus.mjs +0 -388
  59. package/fesm2022/radix-ng-primitives-roving-focus.mjs.map +0 -1
  60. package/roving-focus/README.md +0 -3
  61. package/types/radix-ng-primitives-collection.d.ts +0 -44
  62. package/types/radix-ng-primitives-roving-focus.d.ts +0 -187
@@ -7,8 +7,8 @@ import * as i2 from '@radix-ng/primitives/floating-focus-manager';
7
7
  import { getInteractionTypeFromEvent, provideFloatingFocusManagerConfig, RdxFloatingFocusManager, createRdxTriggerInteraction } from '@radix-ng/primitives/floating-focus-manager';
8
8
  import * as i1 from '@radix-ng/primitives/popper';
9
9
  import { RdxPopper, RdxPopperContent, RdxPopperContentWrapper, legacyPopperVars, provideRdxPopperContentWrapper, provideRdxPopperContentConfig, RdxPopperAnchor } from '@radix-ng/primitives/popper';
10
- import * as i4 from '@radix-ng/primitives/collection';
11
- import { RdxCollectionProvider, RdxCollectionItem } from '@radix-ng/primitives/collection';
10
+ import * as i4 from '@radix-ng/primitives/composite';
11
+ import { RdxCompositeList, RdxCompositeListItem } from '@radix-ng/primitives/composite';
12
12
  import { RdxDismiss } from '@radix-ng/primitives/dismissable-layer';
13
13
  import * as i1$1 from '@radix-ng/primitives/portal';
14
14
  import { RdxPortalPresence } from '@radix-ng/primitives/portal';
@@ -341,9 +341,15 @@ const context$1 = () => {
341
341
  isPositioned: context.isPositioned,
342
342
  selectedItem: context.selectedItem,
343
343
  selectedItemText: context.selectedItemText,
344
+ items: context.items,
344
345
  highlightedItem: context.highlight.highlightedItem,
345
- isHighlighted: (item) => context.highlight.highlightedItem() === item,
346
- highlightItem: (item) => context.highlight.set(item),
346
+ isHighlighted: (element) => context.highlight.highlightedItem()?.element === element,
347
+ highlightItem: (element) => {
348
+ const item = context.items().find((item) => item.element === element);
349
+ if (item) {
350
+ context.highlight.set(item);
351
+ }
352
+ },
347
353
  isKeyboardActive: () => context.isKeyboardActive(),
348
354
  setKeyboardActive: (value) => context.setKeyboardActive(value),
349
355
  onViewportChange: (node) => {
@@ -391,23 +397,23 @@ class RdxSelectPopup {
391
397
  this.floatingContext = inject(RDX_FLOATING_ROOT_CONTEXT);
392
398
  this.registration = inject(RDX_FLOATING_REGISTRATION, { optional: true });
393
399
  this.currentElement = inject(ElementRef);
394
- this.collection = inject(RdxCollectionProvider);
400
+ this.compositeList = inject(RdxCompositeList, { self: true });
395
401
  this.injector = inject(Injector);
396
402
  this.rootContext = injectSelectRootContext();
397
403
  /**
398
404
  * The collected items (DOM order). Exposed so the `item-aligned` positioner — now the popup's
399
- * **ancestor** — can read them without injecting {@link RdxCollectionProvider} (which the popup
400
- * provides as a descendant, so an upward `inject` would not find it).
405
+ * **ancestor** — can read them without injecting the composite list (which the popup provides as
406
+ * a descendant, so an upward `inject` would not find it).
401
407
  */
402
- this.items = this.collection.items;
408
+ this.items = computed(() => this.compositeList.items(), ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
403
409
  /**
404
410
  * Highlight-model navigation over the collected items (DOM order). `loop` is disabled so arrow
405
411
  * navigation stops at the first / last item instead of wrapping around — matching native
406
412
  * `<select>` behavior.
407
413
  */
408
414
  this.highlight = useListHighlight({
409
- items: this.collection.items,
410
- isNavigable: (item) => !item.disabled(),
415
+ items: this.items,
416
+ isNavigable: (item) => !item.metadata()?.disabled,
411
417
  getId: (item) => item.element.id,
412
418
  loop: signal(false),
413
419
  injector: this.injector
@@ -420,6 +426,7 @@ class RdxSelectPopup {
420
426
  // Tracks whether the last interaction was the keyboard, so the highlight doesn't jump to an item
421
427
  // the cursor happens to rest on when arrow-key navigation scrolls the list.
422
428
  this.keyboardActive = false;
429
+ this.hasHighlightedOpen = false;
423
430
  /**
424
431
  * Event handler called when the escape key is down.
425
432
  * Can be prevented.
@@ -438,9 +445,8 @@ class RdxSelectPopup {
438
445
  */
439
446
  this.positioner = inject(RDX_SELECT_POSITIONER_TOKEN, { optional: true });
440
447
  this.positioner?.placed.subscribe(() => {
441
- this.highlightSelectedItem();
442
- this.scrollSelectedIntoView();
443
448
  this.isPositioned.set(true);
449
+ this.highlightSelectedItemAfterPositioned();
444
450
  // In Popper mode the popup lives inside the positioner, which stays `visibility: hidden`
445
451
  // until it is placed — so the mount-time `mountAutoFocus` call no-ops on the hidden
446
452
  // listbox and keyboard navigation never starts. Focus it now that it is visible (skip if
@@ -450,6 +456,16 @@ class RdxSelectPopup {
450
456
  popup.focus({ preventScroll: true });
451
457
  }
452
458
  });
459
+ effect(() => {
460
+ if (!this.rootContext.open()) {
461
+ this.hasHighlightedOpen = false;
462
+ return;
463
+ }
464
+ if (!this.isPositioned() || this.hasHighlightedOpen) {
465
+ return;
466
+ }
467
+ this.highlightSelectedItemAfterPositioned();
468
+ });
453
469
  // Keep the highlighted item in view during keyboard navigation. The highlight model is pure
454
470
  // state (it never moves DOM focus or scrolls), so without this the highlight can move past the
455
471
  // visible viewport — behind the scroll buttons. Only keyboard moves scroll; hover highlights
@@ -550,8 +566,8 @@ class RdxSelectPopup {
550
566
  }
551
567
  /** Highlights the selected item (or the first enabled one) when the popup opens. */
552
568
  highlightSelectedItem() {
553
- const items = this.collection.items();
554
- const selected = items.find((item) => valueComparator(this.rootContext.value(), item.value(), this.rootContext.isItemEqualToValue()));
569
+ const items = this.items();
570
+ const selected = items.find((item) => valueComparator(this.rootContext.value(), item.metadata()?.value, this.rootContext.isItemEqualToValue()));
555
571
  if (selected) {
556
572
  this.highlight.set(selected);
557
573
  }
@@ -559,6 +575,14 @@ class RdxSelectPopup {
559
575
  this.highlight.first();
560
576
  }
561
577
  }
578
+ highlightSelectedItemAfterPositioned() {
579
+ if (this.items().length === 0) {
580
+ return;
581
+ }
582
+ this.highlightSelectedItem();
583
+ this.scrollSelectedIntoView();
584
+ this.hasHighlightedOpen = true;
585
+ }
562
586
  scrollSelectedIntoView() {
563
587
  this.selectedItem()?.scrollIntoView?.({ block: 'nearest' });
564
588
  }
@@ -580,8 +604,9 @@ class RdxSelectPopup {
580
604
  if (SELECTION_KEYS.includes(keyEvent.key)) {
581
605
  event.preventDefault();
582
606
  const item = this.highlight.highlightedItem();
583
- if (item && !item.disabled()) {
584
- this.rootContext.onValueChange(item.value(), 'item-press', event);
607
+ const metadata = item?.metadata();
608
+ if (item && metadata && !metadata.disabled) {
609
+ this.rootContext.onValueChange(metadata.value, 'item-press', event);
585
610
  if (!this.rootContext.multiple()) {
586
611
  this.rootContext.onOpenChange(false, 'item-press', event);
587
612
  }
@@ -624,7 +649,7 @@ class RdxSelectPopup {
624
649
  closeInteractionType: () => rootContext.closeInteractionType()
625
650
  };
626
651
  })
627
- ], hostDirectives: [{ directive: i1.RdxPopperContent }, { directive: i2.RdxFloatingFocusManager, inputs: ["returnFocus", "finalFocus"] }, { directive: i3.RdxFloatingNodeRegistration }, { directive: i4.RdxCollectionProvider }], ngImport: i0 }); }
652
+ ], hostDirectives: [{ directive: i1.RdxPopperContent }, { directive: i2.RdxFloatingFocusManager, inputs: ["returnFocus", "finalFocus"] }, { directive: i3.RdxFloatingNodeRegistration }, { directive: i4.RdxCompositeList }], ngImport: i0 }); }
628
653
  }
629
654
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSelectPopup, decorators: [{
630
655
  type: Directive,
@@ -634,7 +659,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
634
659
  RdxPopperContent,
635
660
  { directive: RdxFloatingFocusManager, inputs: ['returnFocus: finalFocus'] },
636
661
  RdxFloatingNodeRegistration,
637
- RdxCollectionProvider
662
+ RdxCompositeList
638
663
  ],
639
664
  providers: [
640
665
  provideSelectPopupContext(context$1),
@@ -692,7 +717,7 @@ class RdxSelectItem {
692
717
  constructor() {
693
718
  this.rootContext = injectSelectRootContext();
694
719
  this.contentContext = injectSelectPopupContext();
695
- this.collectionItem = inject(RdxCollectionItem);
720
+ this.listItem = inject(RdxCompositeListItem, { self: true });
696
721
  this.currentElement = inject(ElementRef);
697
722
  this.value = input(...(ngDevMode ? [undefined, { debugName: "value" }] : /* istanbul ignore next */ []));
698
723
  this.textValue = input('', ...(ngDevMode ? [{ debugName: "textValue" }] : /* istanbul ignore next */ []));
@@ -700,7 +725,7 @@ class RdxSelectItem {
700
725
  this.textValue$ = linkedSignal(this.textValue, ...(ngDevMode ? [{ debugName: "textValue$" }] : /* istanbul ignore next */ []));
701
726
  this.isSelected = computed(() => valueComparator(this.rootContext.value(), this.value(), this.rootContext.isItemEqualToValue()), ...(ngDevMode ? [{ debugName: "isSelected" }] : /* istanbul ignore next */ []));
702
727
  /** Highlighted via the highlight model (keyboard / hover), not DOM focus. */
703
- this.isHighlighted = computed(() => this.contentContext.isHighlighted(this.collectionItem), ...(ngDevMode ? [{ debugName: "isHighlighted" }] : /* istanbul ignore next */ []));
728
+ this.isHighlighted = computed(() => this.contentContext.isHighlighted(this.currentElement.nativeElement), ...(ngDevMode ? [{ debugName: "isHighlighted" }] : /* istanbul ignore next */ []));
704
729
  /** Item id, referenced by the popup's `aria-activedescendant`. */
705
730
  this.id = injectId('rdx-select-item-');
706
731
  this.textId = injectId('rdx-select-item-text-');
@@ -708,6 +733,13 @@ class RdxSelectItem {
708
733
  this.contentContext.itemRefCallback(this.currentElement.nativeElement, this.value(), this.disabled());
709
734
  });
710
735
  this.SELECT_SELECT = 'select.select';
736
+ effect(() => {
737
+ this.listItem.setMetadata({
738
+ value: this.value(),
739
+ disabled: this.disabled(),
740
+ textValue: this.textValue$()
741
+ });
742
+ });
711
743
  }
712
744
  onPointerUp(event) {
713
745
  if (event.defaultPrevented)
@@ -738,7 +770,7 @@ class RdxSelectItem {
738
770
  return;
739
771
  }
740
772
  if (!this.disabled()) {
741
- this.contentContext.highlightItem(this.collectionItem);
773
+ this.contentContext.highlightItem(this.currentElement.nativeElement);
742
774
  }
743
775
  }
744
776
  handleKeyDown(event) {
@@ -749,7 +781,7 @@ class RdxSelectItem {
749
781
  this.onPointerUp(keyEvent);
750
782
  }
751
783
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSelectItem, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
752
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxSelectItem, isStandalone: true, selector: "[rdxSelectItem]", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, textValue: { classPropertyName: "textValue", publicName: "textValue", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "option" }, listeners: { "pointerup": "onPointerUp($event)", "pointerleave": "onPointerLeave($event)", "pointermove": "onPointerMove($event)" }, properties: { "attr.id": "id", "attr.aria-selected": "isSelected()", "attr.aria-disabled": "disabled() ? \"true\" : undefined", "attr.data-state": "isSelected() ? \"checked\" : \"unchecked\"", "attr.data-selected": "isSelected() ? \"\" : undefined", "attr.data-highlighted": "isHighlighted() ? \"\" : undefined", "attr.data-disabled": "disabled() ? \"\" : undefined" } }, providers: [provideSelectItemContext(context)], exportAs: ["rdxSelectItem"], hostDirectives: [{ directive: i4.RdxCollectionItem, inputs: ["value", "value", "disabled", "disabled"] }], ngImport: i0 }); }
784
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxSelectItem, isStandalone: true, selector: "[rdxSelectItem]", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, textValue: { classPropertyName: "textValue", publicName: "textValue", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "option" }, listeners: { "pointerup": "onPointerUp($event)", "pointerleave": "onPointerLeave($event)", "pointermove": "onPointerMove($event)" }, properties: { "attr.id": "id", "attr.aria-selected": "isSelected()", "attr.aria-disabled": "disabled() ? \"true\" : undefined", "attr.data-state": "isSelected() ? \"checked\" : \"unchecked\"", "attr.data-selected": "isSelected() ? \"\" : undefined", "attr.data-highlighted": "isHighlighted() ? \"\" : undefined", "attr.data-disabled": "disabled() ? \"\" : undefined" } }, providers: [provideSelectItemContext(context)], exportAs: ["rdxSelectItem"], hostDirectives: [{ directive: i4.RdxCompositeListItem }], ngImport: i0 }); }
753
785
  }
754
786
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSelectItem, decorators: [{
755
787
  type: Directive,
@@ -757,12 +789,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
757
789
  selector: '[rdxSelectItem]',
758
790
  exportAs: 'rdxSelectItem',
759
791
  providers: [provideSelectItemContext(context)],
760
- hostDirectives: [
761
- {
762
- directive: RdxCollectionItem,
763
- inputs: ['value', 'disabled']
764
- }
765
- ],
792
+ hostDirectives: [RdxCompositeListItem],
766
793
  host: {
767
794
  role: 'option',
768
795
  '[attr.id]': 'id',
@@ -777,7 +804,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
777
804
  '(pointermove)': 'onPointerMove($event)'
778
805
  }
779
806
  }]
780
- }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], textValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "textValue", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }] } });
807
+ }], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], textValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "textValue", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }] } });
781
808
 
782
809
  class RdxSelectItemIndicator {
783
810
  constructor() {
@@ -1040,6 +1067,9 @@ class RdxSelectTrigger {
1040
1067
  // We force `focus` in this case. Note: this doesn't create any other side-effect
1041
1068
  // because we are preventing default in `onPointerDown` so effectively
1042
1069
  // this only runs for a label 'click'
1070
+ if (this.rootContext.open()) {
1071
+ return;
1072
+ }
1043
1073
  event?.currentTarget?.focus();
1044
1074
  }
1045
1075
  onPointerDown(event) {