@inf-monkeys-tech/monkeys-design 2.0.0 → 2.0.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.
@@ -3121,6 +3121,30 @@ function MoreIcon2() {
3121
3121
  }
3122
3122
  );
3123
3123
  }
3124
+ function DisclosureIcon({ expanded }) {
3125
+ return /* @__PURE__ */ jsx(
3126
+ "svg",
3127
+ {
3128
+ "aria-hidden": "true",
3129
+ viewBox: "0 0 20 20",
3130
+ className: cn(
3131
+ "h-4 w-4 transition-transform motion-reduce:transition-none",
3132
+ expanded && "rotate-180"
3133
+ ),
3134
+ fill: "none",
3135
+ children: /* @__PURE__ */ jsx(
3136
+ "path",
3137
+ {
3138
+ d: "m6 8 4 4 4-4",
3139
+ stroke: "currentColor",
3140
+ strokeWidth: "1.6",
3141
+ strokeLinecap: "round",
3142
+ strokeLinejoin: "round"
3143
+ }
3144
+ )
3145
+ }
3146
+ );
3147
+ }
3124
3148
  function assignRef(ref, node) {
3125
3149
  if (typeof ref === "function") {
3126
3150
  ref(node);
@@ -3276,6 +3300,94 @@ function resolveNavigationTreeClassNames(classNames) {
3276
3300
  )
3277
3301
  };
3278
3302
  }
3303
+ function entryClassName({
3304
+ item,
3305
+ active,
3306
+ collapsed,
3307
+ mobile,
3308
+ embedded = false,
3309
+ appearance,
3310
+ classNames,
3311
+ domClassName
3312
+ }) {
3313
+ return cn(
3314
+ "group/navigation-item relative flex w-full min-w-0 items-center gap-3 rounded-[var(--radius)] border border-transparent text-left text-sm outline-none transition-[background-color,border-color,color,box-shadow] focus-visible:ring-2 focus-visible:ring-ring motion-reduce:transition-none",
3315
+ appearance.itemHeight,
3316
+ appearance.itemPadding,
3317
+ embedded ? "min-h-0 self-stretch border-0 bg-transparent text-inherit hover:bg-transparent hover:text-inherit" : active ? activeVariantClassName(appearance.activeVariant) : "text-muted-foreground hover:border-border/70 hover:bg-muted/60 hover:text-foreground",
3318
+ collapsed && !mobile && "justify-center px-0",
3319
+ item.disabled && "pointer-events-none opacity-50",
3320
+ classNames?.item,
3321
+ active && classNames?.itemActive,
3322
+ mobile && classNames?.mobileItem,
3323
+ domClassName,
3324
+ item.className
3325
+ );
3326
+ }
3327
+ function EntryContent({
3328
+ item,
3329
+ collapsed,
3330
+ mobile,
3331
+ classNames,
3332
+ endIndicator
3333
+ }) {
3334
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
3335
+ hasRenderableNode(item.icon) ? /* @__PURE__ */ jsx(
3336
+ "span",
3337
+ {
3338
+ className: cn(
3339
+ "flex size-5 shrink-0 items-center justify-center [&_svg]:size-full",
3340
+ classNames?.itemIcon
3341
+ ),
3342
+ "aria-hidden": "true",
3343
+ children: item.icon
3344
+ }
3345
+ ) : null,
3346
+ mobile || !collapsed ? /* @__PURE__ */ jsxs(
3347
+ "span",
3348
+ {
3349
+ className: cn(
3350
+ "flex min-w-0 flex-1 flex-col",
3351
+ classNames?.itemContent
3352
+ ),
3353
+ children: [
3354
+ /* @__PURE__ */ jsx(
3355
+ "span",
3356
+ {
3357
+ className: cn(
3358
+ "min-w-0 truncate font-semibold",
3359
+ classNames?.itemLabel
3360
+ ),
3361
+ children: item.label
3362
+ }
3363
+ ),
3364
+ hasRenderableNode(item.description) ? /* @__PURE__ */ jsx(
3365
+ "span",
3366
+ {
3367
+ className: cn(
3368
+ "mt-0.5 truncate text-xs font-normal",
3369
+ classNames?.itemDescription
3370
+ ),
3371
+ children: item.description
3372
+ }
3373
+ ) : null
3374
+ ]
3375
+ }
3376
+ ) : null,
3377
+ (mobile || !collapsed) && hasRenderableNode(item.meta) ? /* @__PURE__ */ jsx(
3378
+ "span",
3379
+ {
3380
+ className: cn(
3381
+ "shrink-0 text-xs text-muted-foreground",
3382
+ classNames?.itemMeta
3383
+ ),
3384
+ children: item.meta
3385
+ }
3386
+ ) : null,
3387
+ (mobile || !collapsed) && hasRenderableNode(item.endContent) ? /* @__PURE__ */ jsx("span", { className: cn("shrink-0", classNames?.itemEndContent), children: item.endContent }) : null,
3388
+ (mobile || !collapsed) && hasRenderableNode(endIndicator) ? /* @__PURE__ */ jsx("span", { className: "flex shrink-0 items-center justify-center", children: endIndicator }) : null
3389
+ ] });
3390
+ }
3279
3391
  function EntryButton({
3280
3392
  item,
3281
3393
  active,
@@ -3284,7 +3396,11 @@ function EntryButton({
3284
3396
  appearance,
3285
3397
  classNames,
3286
3398
  mobile = false,
3287
- onSelect
3399
+ embedded = false,
3400
+ onSelect,
3401
+ ariaControls,
3402
+ ariaExpanded,
3403
+ endIndicator
3288
3404
  }) {
3289
3405
  const ItemComponent = itemAs ?? (item.href ? "a" : "button");
3290
3406
  const title = getNodeTitle(item.label, item.title);
@@ -3301,90 +3417,90 @@ function EntryButton({
3301
3417
  ...domProps,
3302
3418
  ...item.to ? { to: item.to } : {},
3303
3419
  ...item.href ? { href: item.href } : {},
3304
- ...ItemComponent === "button" ? { type: "button" } : {},
3420
+ ...ItemComponent === "button" ? { type: "button", disabled: item.disabled } : {},
3305
3421
  title,
3306
3422
  "aria-current": active ? "page" : void 0,
3307
3423
  "aria-disabled": item.disabled || void 0,
3424
+ "aria-controls": ariaControls,
3425
+ "aria-expanded": ariaExpanded,
3308
3426
  tabIndex: item.disabled ? -1 : domProps.tabIndex,
3309
3427
  onClick: handleClick
3310
3428
  };
3311
- return /* @__PURE__ */ jsxs(
3429
+ return /* @__PURE__ */ jsx(
3312
3430
  ItemComponent,
3313
3431
  {
3314
3432
  ...componentProps,
3315
3433
  "data-navigation-sidebar-item-id": item.id,
3316
3434
  "data-active": active ? "true" : "false",
3317
- className: cn(
3318
- "group/navigation-item relative flex w-full min-w-0 items-center gap-3 rounded-[var(--radius)] border border-transparent text-left text-sm outline-none transition-[background-color,border-color,color,box-shadow] focus-visible:ring-2 focus-visible:ring-ring motion-reduce:transition-none",
3319
- appearance.itemHeight,
3320
- appearance.itemPadding,
3321
- active ? activeVariantClassName(appearance.activeVariant) : "text-muted-foreground hover:border-border/70 hover:bg-muted/60 hover:text-foreground",
3322
- collapsed && !mobile && "justify-center px-0",
3323
- item.disabled && "pointer-events-none opacity-50",
3324
- classNames?.item,
3325
- active && classNames?.itemActive,
3326
- mobile && classNames?.mobileItem,
3327
- domProps.className,
3328
- item.className
3329
- ),
3330
- children: [
3331
- hasRenderableNode(item.icon) ? /* @__PURE__ */ jsx(
3332
- "span",
3333
- {
3334
- className: cn(
3335
- "flex size-5 shrink-0 items-center justify-center [&_svg]:size-full",
3336
- classNames?.itemIcon
3337
- ),
3338
- "aria-hidden": "true",
3339
- children: item.icon
3340
- }
3341
- ) : null,
3342
- mobile || !collapsed ? /* @__PURE__ */ jsxs(
3343
- "span",
3344
- {
3345
- className: cn(
3346
- "flex min-w-0 flex-1 flex-col",
3347
- classNames?.itemContent
3348
- ),
3349
- children: [
3350
- /* @__PURE__ */ jsx(
3351
- "span",
3352
- {
3353
- className: cn(
3354
- "min-w-0 truncate font-semibold",
3355
- classNames?.itemLabel
3356
- ),
3357
- children: item.label
3358
- }
3359
- ),
3360
- hasRenderableNode(item.description) ? /* @__PURE__ */ jsx(
3361
- "span",
3362
- {
3363
- className: cn(
3364
- "mt-0.5 truncate text-xs font-normal",
3365
- classNames?.itemDescription
3366
- ),
3367
- children: item.description
3368
- }
3369
- ) : null
3370
- ]
3371
- }
3372
- ) : null,
3373
- (mobile || !collapsed) && hasRenderableNode(item.meta) ? /* @__PURE__ */ jsx(
3374
- "span",
3375
- {
3376
- className: cn(
3377
- "shrink-0 text-xs text-muted-foreground",
3378
- classNames?.itemMeta
3379
- ),
3380
- children: item.meta
3381
- }
3382
- ) : null,
3383
- (mobile || !collapsed) && hasRenderableNode(item.endContent) ? /* @__PURE__ */ jsx("span", { className: cn("shrink-0", classNames?.itemEndContent), children: item.endContent }) : null
3384
- ]
3435
+ "data-expanded": ariaExpanded === void 0 ? void 0 : String(ariaExpanded),
3436
+ className: entryClassName({
3437
+ item,
3438
+ active,
3439
+ collapsed,
3440
+ mobile,
3441
+ embedded,
3442
+ appearance,
3443
+ classNames,
3444
+ domClassName: domProps.className
3445
+ }),
3446
+ children: /* @__PURE__ */ jsx(
3447
+ EntryContent,
3448
+ {
3449
+ item,
3450
+ collapsed,
3451
+ mobile,
3452
+ classNames,
3453
+ endIndicator
3454
+ }
3455
+ )
3456
+ }
3457
+ );
3458
+ }
3459
+ function EntryPresentation({
3460
+ item,
3461
+ appearance,
3462
+ classNames
3463
+ }) {
3464
+ const domProps = item.domProps ?? {};
3465
+ return /* @__PURE__ */ jsx(
3466
+ "div",
3467
+ {
3468
+ ...domProps,
3469
+ "data-navigation-sidebar-item-id": item.id,
3470
+ "aria-disabled": item.disabled || void 0,
3471
+ title: getNodeTitle(item.label, item.title),
3472
+ className: entryClassName({
3473
+ item,
3474
+ active: false,
3475
+ collapsed: false,
3476
+ mobile: true,
3477
+ embedded: true,
3478
+ appearance,
3479
+ classNames,
3480
+ domClassName: domProps.className
3481
+ }),
3482
+ children: /* @__PURE__ */ jsx(
3483
+ EntryContent,
3484
+ {
3485
+ item,
3486
+ collapsed: false,
3487
+ mobile: true,
3488
+ classNames
3489
+ }
3490
+ )
3385
3491
  }
3386
3492
  );
3387
3493
  }
3494
+ function routeRowClassName({
3495
+ active,
3496
+ appearance
3497
+ }) {
3498
+ return cn(
3499
+ "flex w-full min-w-0 items-center gap-0.5 rounded-[var(--radius)] border border-transparent pr-1 text-sm transition-[background-color,border-color,color,box-shadow] motion-reduce:transition-none",
3500
+ appearance.itemHeight,
3501
+ active ? activeVariantClassName(appearance.activeVariant) : "text-muted-foreground hover:border-border/70 hover:bg-muted/60 hover:text-foreground"
3502
+ );
3503
+ }
3388
3504
  function filterCatalogItems(items, activeFilterId) {
3389
3505
  if (!activeFilterId) return items;
3390
3506
  return items.filter(
@@ -3516,7 +3632,9 @@ function ChildEntry({
3516
3632
  active,
3517
3633
  interactive,
3518
3634
  onSelect,
3519
- classNames
3635
+ appearance,
3636
+ classNames,
3637
+ embedded = false
3520
3638
  }) {
3521
3639
  const Component = interactive ? "button" : "div";
3522
3640
  const domProps = child.domProps ?? {};
@@ -3531,8 +3649,11 @@ function ChildEntry({
3531
3649
  title: getNodeTitle(child.label, child.title),
3532
3650
  disabled: interactive ? child.disabled : void 0,
3533
3651
  className: cn(
3534
- "flex min-h-9 w-full min-w-0 items-center gap-2 rounded-md border border-transparent px-2 text-left text-xs font-medium outline-none transition-[background-color,border-color,color] focus-visible:ring-2 focus-visible:ring-ring motion-reduce:transition-none",
3535
- active ? "border-primary/35 bg-primary/10 text-primary" : "text-muted-foreground hover:border-border hover:bg-muted/60 hover:text-foreground",
3652
+ "flex w-full min-w-0 flex-1 items-center gap-3 rounded-[var(--radius)] border border-transparent text-left text-sm font-semibold outline-none transition-[background-color,border-color,color,box-shadow] focus-visible:ring-2 focus-visible:ring-ring motion-reduce:transition-none",
3653
+ appearance.itemHeight,
3654
+ appearance.itemPadding,
3655
+ "pl-11",
3656
+ embedded ? "min-h-0 self-stretch border-0 bg-transparent text-inherit hover:bg-transparent hover:text-inherit" : active ? activeVariantClassName(appearance.activeVariant) : "text-muted-foreground hover:border-border/70 hover:bg-muted/60 hover:text-foreground",
3536
3657
  child.disabled && "pointer-events-none opacity-50",
3537
3658
  classNames?.childItem,
3538
3659
  active && classNames?.childItemActive,
@@ -3544,7 +3665,7 @@ function ChildEntry({
3544
3665
  hasRenderableNode(child.icon) ? /* @__PURE__ */ jsx(
3545
3666
  "span",
3546
3667
  {
3547
- className: "flex size-4 shrink-0 items-center justify-center",
3668
+ className: "flex size-5 shrink-0 items-center justify-center [&_svg]:size-full",
3548
3669
  "aria-hidden": "true",
3549
3670
  children: child.icon
3550
3671
  }
@@ -3554,29 +3675,167 @@ function ChildEntry({
3554
3675
  }
3555
3676
  );
3556
3677
  }
3678
+ function RouteActions({
3679
+ itemId,
3680
+ actions,
3681
+ kind,
3682
+ className,
3683
+ mobile = false
3684
+ }) {
3685
+ if (!hasRenderableNode(actions)) return null;
3686
+ return /* @__PURE__ */ jsx(
3687
+ "div",
3688
+ {
3689
+ "data-navigation-sidebar-item-actions": kind === "item" ? itemId : void 0,
3690
+ "data-navigation-sidebar-child-actions": kind === "child" ? itemId : void 0,
3691
+ "data-navigation-sidebar-actions-mobile": mobile ? "true" : void 0,
3692
+ className: cn(
3693
+ "ml-auto flex shrink-0 items-center gap-1 [&>button]:border-0 [&>button]:bg-transparent [&>button]:shadow-none [&>button:hover]:bg-muted",
3694
+ className
3695
+ ),
3696
+ children: actions
3697
+ }
3698
+ );
3699
+ }
3700
+ function isDisclosureRouteItem(item) {
3701
+ return item.interaction === "disclosure";
3702
+ }
3703
+ function validateRouteItems(routeItems) {
3704
+ const itemIds = /* @__PURE__ */ new Set();
3705
+ const childIds = /* @__PURE__ */ new Set();
3706
+ const collapsibleItemIds = /* @__PURE__ */ new Set();
3707
+ for (const item of routeItems) {
3708
+ const itemId = item.id;
3709
+ if (itemIds.has(itemId)) {
3710
+ throw new RangeError(
3711
+ `NavigationSidebar route item ids must be unique; received "${itemId}" more than once.`
3712
+ );
3713
+ }
3714
+ itemIds.add(itemId);
3715
+ const disclosure = isDisclosureRouteItem(item);
3716
+ if (disclosure) {
3717
+ if (!item.children.length) {
3718
+ throw new RangeError(
3719
+ `NavigationSidebar disclosure item "${item.id}" must include at least one child.`
3720
+ );
3721
+ }
3722
+ if (item.href !== void 0 || item.to !== void 0 || item.onClick !== void 0) {
3723
+ throw new TypeError(
3724
+ `NavigationSidebar disclosure item "${itemId}" cannot define href, to, or onClick.`
3725
+ );
3726
+ }
3727
+ collapsibleItemIds.add(item.id);
3728
+ } else if (item.childrenPresentation === "collapsible") {
3729
+ if (!item.children?.length) {
3730
+ throw new RangeError(
3731
+ `NavigationSidebar collapsible route item "${item.id}" must include at least one child.`
3732
+ );
3733
+ }
3734
+ collapsibleItemIds.add(item.id);
3735
+ }
3736
+ for (const child of item.children ?? []) {
3737
+ if (childIds.has(child.id)) {
3738
+ throw new RangeError(
3739
+ `NavigationSidebar route child ids must be unique; received "${child.id}" more than once.`
3740
+ );
3741
+ }
3742
+ childIds.add(child.id);
3743
+ }
3744
+ }
3745
+ return collapsibleItemIds;
3746
+ }
3747
+ function RouteChildrenToggle({
3748
+ item,
3749
+ expanded,
3750
+ controls,
3751
+ label,
3752
+ onToggle,
3753
+ classNames
3754
+ }) {
3755
+ const itemLabel = typeof item.label === "string" ? item.label : item.id;
3756
+ return /* @__PURE__ */ jsx(
3757
+ "button",
3758
+ {
3759
+ type: "button",
3760
+ disabled: item.disabled,
3761
+ "aria-expanded": expanded,
3762
+ "aria-controls": controls,
3763
+ "aria-label": `${label}: ${itemLabel}`,
3764
+ title: label,
3765
+ "data-navigation-sidebar-disclosure-trigger": item.id,
3766
+ className: cn(
3767
+ "inline-flex h-8 w-7 shrink-0 items-center justify-center rounded-md border-0 bg-transparent text-muted-foreground shadow-none outline-none hover:bg-muted hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
3768
+ classNames?.childMenuTrigger
3769
+ ),
3770
+ onClick: onToggle,
3771
+ children: /* @__PURE__ */ jsx(DisclosureIcon, { expanded })
3772
+ }
3773
+ );
3774
+ }
3557
3775
  function CollapsedChildMenu({
3558
3776
  item,
3559
3777
  activeChildId,
3560
3778
  label,
3561
3779
  onChildSelect,
3562
- classNames
3780
+ classNames,
3781
+ appearance,
3782
+ triggerVariant = "submenu",
3783
+ mobile = false
3563
3784
  }) {
3564
3785
  const enabledChildren = (item.children ?? []).filter(
3565
3786
  (child) => !child.disabled
3566
3787
  );
3567
- if (enabledChildren.length === 0) return null;
3788
+ if (!item.children?.length) return null;
3789
+ if (triggerVariant === "submenu" && (!onChildSelect || enabledChildren.length === 0))
3790
+ return null;
3791
+ const itemLabel = typeof item.label === "string" ? item.label : item.id;
3792
+ const itemTriggerDisabled = Boolean(item.disabled) || !onChildSelect || enabledChildren.length === 0;
3793
+ const domProps = item.domProps ?? {};
3568
3794
  return /* @__PURE__ */ jsxs(DropdownMenu.Root, { children: [
3569
- /* @__PURE__ */ jsx(DropdownMenu.Trigger, { asChild: true, children: /* @__PURE__ */ jsx(
3795
+ /* @__PURE__ */ jsx(DropdownMenu.Trigger, { asChild: true, children: triggerVariant === "item" && appearance ? /* @__PURE__ */ jsx(
3796
+ "button",
3797
+ {
3798
+ ...domProps,
3799
+ type: "button",
3800
+ disabled: itemTriggerDisabled,
3801
+ "aria-label": `${label}: ${itemLabel}`,
3802
+ title: label,
3803
+ "data-navigation-sidebar-item-id": item.id,
3804
+ "data-navigation-sidebar-disclosure-menu": "true",
3805
+ className: entryClassName({
3806
+ item,
3807
+ active: false,
3808
+ collapsed: !mobile,
3809
+ mobile,
3810
+ appearance,
3811
+ classNames,
3812
+ domClassName: domProps.className
3813
+ }),
3814
+ children: /* @__PURE__ */ jsx(
3815
+ EntryContent,
3816
+ {
3817
+ item,
3818
+ collapsed: !mobile,
3819
+ mobile,
3820
+ classNames,
3821
+ endIndicator: mobile ? /* @__PURE__ */ jsx(DisclosureIcon, { expanded: false }) : void 0
3822
+ }
3823
+ )
3824
+ }
3825
+ ) : /* @__PURE__ */ jsx(
3570
3826
  "button",
3571
3827
  {
3572
3828
  type: "button",
3829
+ disabled: triggerVariant === "disclosure" ? itemTriggerDisabled : void 0,
3830
+ "data-navigation-sidebar-disclosure-trigger": triggerVariant === "disclosure" ? item.id : void 0,
3573
3831
  className: cn(
3574
- "inline-flex h-8 w-6 shrink-0 items-center justify-center rounded-md text-muted-foreground outline-none hover:bg-muted hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring",
3832
+ "inline-flex h-8 shrink-0 items-center justify-center rounded-md border-0 bg-transparent text-muted-foreground shadow-none outline-none hover:bg-muted hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
3833
+ triggerVariant === "disclosure" ? "w-7" : "w-6",
3575
3834
  classNames?.childMenuTrigger
3576
3835
  ),
3577
- "aria-label": `${label}: ${typeof item.label === "string" ? item.label : item.id}`,
3836
+ "aria-label": `${label}: ${itemLabel}`,
3578
3837
  title: label,
3579
- children: /* @__PURE__ */ jsx(MoreIcon2, {})
3838
+ children: triggerVariant === "disclosure" ? /* @__PURE__ */ jsx(DisclosureIcon, { expanded: false }) : /* @__PURE__ */ jsx(MoreIcon2, {})
3580
3839
  }
3581
3840
  ) }),
3582
3841
  /* @__PURE__ */ jsx(DropdownMenu.Portal, { children: /* @__PURE__ */ jsx(
@@ -3599,7 +3858,7 @@ function CollapsedChildMenu({
3599
3858
  "flex cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
3600
3859
  child.id === activeChildId && "bg-accent text-accent-foreground"
3601
3860
  ),
3602
- onSelect: () => onChildSelect(child, item),
3861
+ onSelect: () => onChildSelect?.(child, item),
3603
3862
  children: [
3604
3863
  hasRenderableNode(child.icon) ? /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: child.icon }) : null,
3605
3864
  /* @__PURE__ */ jsx("span", { className: "truncate", children: child.label })
@@ -3620,22 +3879,81 @@ function RouteContent({
3620
3879
  routeItems,
3621
3880
  activeItemId,
3622
3881
  activeChildId,
3882
+ expandedItemIds,
3883
+ defaultExpandedItemIds = [],
3623
3884
  itemAs,
3624
3885
  onItemSelect,
3625
3886
  onChildSelect,
3887
+ onExpandedItemIdsChange,
3626
3888
  collapsedChildMenuLabel = "Open submenu",
3889
+ disclosureLabel = "Toggle submenu",
3627
3890
  ariaLabel,
3628
3891
  classNames
3629
3892
  } = props;
3630
- return /* @__PURE__ */ jsx("nav", { "aria-label": ariaLabel, children: /* @__PURE__ */ jsx("ul", { className: cn("grid list-none gap-2 p-0", classNames?.list), children: routeItems.map((item) => {
3631
- const active = item.id === activeItemId;
3893
+ const disclosureId = useId();
3894
+ const collapsibleItemIds = useMemo(
3895
+ () => validateRouteItems(routeItems),
3896
+ [routeItems]
3897
+ );
3898
+ const [internalExpandedItemIds, setInternalExpandedItemIds] = useState(
3899
+ () => defaultExpandedItemIds
3900
+ );
3901
+ const isExpandedControlled = expandedItemIds !== void 0;
3902
+ const resolvedExpandedItemIds = expandedItemIds ?? internalExpandedItemIds;
3903
+ const normalizedExpandedItemIds = useMemo(
3904
+ () => resolvedExpandedItemIds.filter(
3905
+ (itemId) => collapsibleItemIds.has(itemId)
3906
+ ),
3907
+ [collapsibleItemIds, resolvedExpandedItemIds]
3908
+ );
3909
+ const expandedItemIdSet = useMemo(
3910
+ () => new Set(normalizedExpandedItemIds),
3911
+ [normalizedExpandedItemIds]
3912
+ );
3913
+ const toggleItemExpanded = useCallback(
3914
+ (item) => {
3915
+ if (item.disabled || !collapsibleItemIds.has(item.id)) return;
3916
+ const nextExpandedItemIds = expandedItemIdSet.has(item.id) ? normalizedExpandedItemIds.filter((itemId) => itemId !== item.id) : [...normalizedExpandedItemIds, item.id];
3917
+ if (!isExpandedControlled) {
3918
+ setInternalExpandedItemIds(nextExpandedItemIds);
3919
+ }
3920
+ onExpandedItemIdsChange?.(nextExpandedItemIds);
3921
+ },
3922
+ [
3923
+ collapsibleItemIds,
3924
+ expandedItemIdSet,
3925
+ isExpandedControlled,
3926
+ normalizedExpandedItemIds,
3927
+ onExpandedItemIdsChange
3928
+ ]
3929
+ );
3930
+ return /* @__PURE__ */ jsx("nav", { "aria-label": ariaLabel, children: /* @__PURE__ */ jsx("ul", { className: cn("grid list-none gap-2 p-0", classNames?.list), children: routeItems.map((item, itemIndex) => {
3931
+ const disclosure = isDisclosureRouteItem(item);
3932
+ const active = !disclosure && item.id === activeItemId;
3933
+ const hasChildren = Boolean(item.children?.length);
3934
+ const childrenCollapsible = collapsibleItemIds.has(item.id);
3935
+ const childrenExpanded = !childrenCollapsible || expandedItemIdSet.has(item.id);
3936
+ const showItemActions = !collapsed && hasRenderableNode(item.actions);
3937
+ const compoundItemRow = collapsed && hasChildren && Boolean(onChildSelect) || !collapsed && childrenCollapsible && !disclosure || showItemActions;
3938
+ const childListId = `${disclosureId}-route-children-${itemIndex}`;
3632
3939
  return /* @__PURE__ */ jsxs("li", { className: "grid min-w-0 gap-1", children: [
3633
- /* @__PURE__ */ jsxs(
3940
+ collapsed && disclosure ? /* @__PURE__ */ jsx(
3941
+ CollapsedChildMenu,
3942
+ {
3943
+ item,
3944
+ activeChildId,
3945
+ label: collapsedChildMenuLabel,
3946
+ onChildSelect,
3947
+ classNames,
3948
+ appearance,
3949
+ triggerVariant: "item"
3950
+ }
3951
+ ) : /* @__PURE__ */ jsxs(
3634
3952
  "div",
3635
3953
  {
3636
- className: cn(
3637
- collapsed && Boolean(item.children?.length) && Boolean(onChildSelect) && "flex min-w-0 items-center gap-0.5"
3638
- ),
3954
+ "data-navigation-sidebar-item-row": compoundItemRow ? item.id : void 0,
3955
+ "data-active": compoundItemRow ? String(active) : void 0,
3956
+ className: compoundItemRow ? routeRowClassName({ active, appearance }) : void 0,
3639
3957
  children: [
3640
3958
  /* @__PURE__ */ jsx(
3641
3959
  EntryButton,
@@ -3643,12 +3961,25 @@ function RouteContent({
3643
3961
  item,
3644
3962
  active,
3645
3963
  collapsed,
3646
- itemAs,
3964
+ embedded: compoundItemRow,
3965
+ itemAs: disclosure ? "button" : itemAs,
3647
3966
  appearance,
3648
3967
  classNames,
3649
- onSelect: onItemSelect ? () => onItemSelect(item) : void 0
3968
+ onSelect: disclosure ? () => toggleItemExpanded(item) : onItemSelect ? () => onItemSelect(item) : void 0,
3969
+ ariaControls: !collapsed && childrenCollapsible && disclosure ? childListId : void 0,
3970
+ ariaExpanded: !collapsed && childrenCollapsible && disclosure ? childrenExpanded : void 0,
3971
+ endIndicator: !collapsed && disclosure && !showItemActions ? /* @__PURE__ */ jsx(DisclosureIcon, { expanded: childrenExpanded }) : void 0
3650
3972
  }
3651
3973
  ),
3974
+ showItemActions ? /* @__PURE__ */ jsx(
3975
+ RouteActions,
3976
+ {
3977
+ itemId: item.id,
3978
+ actions: item.actions,
3979
+ kind: "item",
3980
+ className: classNames?.itemActions
3981
+ }
3982
+ ) : null,
3652
3983
  collapsed && onChildSelect ? /* @__PURE__ */ jsx(
3653
3984
  CollapsedChildMenu,
3654
3985
  {
@@ -3658,28 +3989,71 @@ function RouteContent({
3658
3989
  onChildSelect,
3659
3990
  classNames
3660
3991
  }
3992
+ ) : null,
3993
+ !collapsed && childrenCollapsible && (!disclosure || showItemActions) ? /* @__PURE__ */ jsx(
3994
+ RouteChildrenToggle,
3995
+ {
3996
+ item,
3997
+ expanded: childrenExpanded,
3998
+ controls: childListId,
3999
+ label: disclosureLabel,
4000
+ onToggle: () => toggleItemExpanded(item),
4001
+ classNames
4002
+ }
3661
4003
  ) : null
3662
4004
  ]
3663
4005
  }
3664
4006
  ),
3665
- !collapsed && item.children?.length ? /* @__PURE__ */ jsx(
4007
+ !collapsed && hasChildren ? /* @__PURE__ */ jsx(
3666
4008
  "ul",
3667
4009
  {
4010
+ id: childListId,
4011
+ hidden: !childrenExpanded,
4012
+ style: childrenExpanded ? void 0 : { display: "none" },
3668
4013
  className: cn(
3669
4014
  "ml-5 grid list-none gap-0.5 border-l border-border/70 py-1 pl-3",
3670
4015
  classNames?.childList
3671
4016
  ),
3672
- children: item.children.map((child) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
3673
- ChildEntry,
3674
- {
3675
- child,
3676
- parent: item,
3677
- active: child.id === activeChildId,
3678
- interactive: Boolean(onChildSelect),
3679
- onSelect: onChildSelect,
3680
- classNames
3681
- }
3682
- ) }, child.id))
4017
+ children: item.children?.map((child) => {
4018
+ const hasChildActions = hasRenderableNode(child.actions);
4019
+ const childActive = child.id === activeChildId;
4020
+ const childEntry = /* @__PURE__ */ jsx(
4021
+ ChildEntry,
4022
+ {
4023
+ child,
4024
+ parent: item,
4025
+ active: childActive,
4026
+ interactive: Boolean(onChildSelect),
4027
+ onSelect: onChildSelect,
4028
+ appearance,
4029
+ classNames,
4030
+ embedded: hasChildActions
4031
+ }
4032
+ );
4033
+ return /* @__PURE__ */ jsx("li", { children: hasChildActions ? /* @__PURE__ */ jsxs(
4034
+ "div",
4035
+ {
4036
+ "data-navigation-sidebar-child-row": child.id,
4037
+ "data-active": String(childActive),
4038
+ className: routeRowClassName({
4039
+ active: childActive,
4040
+ appearance
4041
+ }),
4042
+ children: [
4043
+ childEntry,
4044
+ /* @__PURE__ */ jsx(
4045
+ RouteActions,
4046
+ {
4047
+ itemId: child.id,
4048
+ actions: child.actions,
4049
+ kind: "child",
4050
+ className: classNames?.childActions
4051
+ }
4052
+ )
4053
+ ]
4054
+ }
4055
+ ) : childEntry }, child.id);
4056
+ })
3683
4057
  }
3684
4058
  ) : null
3685
4059
  ] }, item.id);
@@ -3713,23 +4087,116 @@ function MobileNavigation({
3713
4087
  classNames?.mobileNav,
3714
4088
  props.mobile.className
3715
4089
  ),
3716
- children: items.map((item) => /* @__PURE__ */ jsx(
3717
- EntryButton,
3718
- {
3719
- item,
3720
- active: props.kind === "catalog" ? item.active ?? item.id === props.activeItemId : item.id === props.activeItemId,
3721
- collapsed: false,
3722
- itemAs: props.itemAs,
3723
- appearance,
3724
- classNames: {
3725
- ...classNames,
3726
- mobileItem: cn(classNames?.mobileItem, props.mobile?.itemClassName)
4090
+ children: items.map((item) => {
4091
+ const mobileClassNames = {
4092
+ ...classNames,
4093
+ mobileItem: cn(
4094
+ classNames?.mobileItem,
4095
+ props.mobile?.itemClassName
4096
+ )
4097
+ };
4098
+ const routeItem = props.kind === "route" ? item : void 0;
4099
+ const showRouteActions = routeItem !== void 0 && hasRenderableNode(routeItem.actions);
4100
+ if (props.kind === "route" && isDisclosureRouteItem(item)) {
4101
+ return showRouteActions ? /* @__PURE__ */ jsxs(
4102
+ "div",
4103
+ {
4104
+ "data-navigation-sidebar-item-row": item.id,
4105
+ "data-active": "false",
4106
+ className: cn(
4107
+ routeRowClassName({ active: false, appearance }),
4108
+ "shrink-0"
4109
+ ),
4110
+ children: [
4111
+ /* @__PURE__ */ jsx(
4112
+ EntryPresentation,
4113
+ {
4114
+ item,
4115
+ appearance,
4116
+ classNames: mobileClassNames
4117
+ }
4118
+ ),
4119
+ /* @__PURE__ */ jsx(
4120
+ RouteActions,
4121
+ {
4122
+ itemId: item.id,
4123
+ actions: item.actions,
4124
+ kind: "item",
4125
+ className: classNames?.itemActions,
4126
+ mobile: true
4127
+ }
4128
+ ),
4129
+ /* @__PURE__ */ jsx(
4130
+ CollapsedChildMenu,
4131
+ {
4132
+ item,
4133
+ activeChildId: props.activeChildId,
4134
+ label: props.collapsedChildMenuLabel ?? "Open submenu",
4135
+ onChildSelect: props.onChildSelect,
4136
+ classNames: mobileClassNames,
4137
+ appearance,
4138
+ triggerVariant: "disclosure",
4139
+ mobile: true
4140
+ }
4141
+ )
4142
+ ]
4143
+ },
4144
+ item.id
4145
+ ) : /* @__PURE__ */ jsx(Fragment$1, { children: /* @__PURE__ */ jsx(
4146
+ CollapsedChildMenu,
4147
+ {
4148
+ item,
4149
+ activeChildId: props.activeChildId,
4150
+ label: props.collapsedChildMenuLabel ?? "Open submenu",
4151
+ onChildSelect: props.onChildSelect,
4152
+ classNames: mobileClassNames,
4153
+ appearance,
4154
+ triggerVariant: "item",
4155
+ mobile: true
4156
+ }
4157
+ ) }, item.id);
4158
+ }
4159
+ const active = props.kind === "catalog" ? item.active ?? item.id === props.activeItemId : item.id === props.activeItemId;
4160
+ const entryButton = /* @__PURE__ */ jsx(
4161
+ EntryButton,
4162
+ {
4163
+ item,
4164
+ active,
4165
+ collapsed: false,
4166
+ embedded: showRouteActions,
4167
+ itemAs: props.itemAs,
4168
+ appearance,
4169
+ classNames: mobileClassNames,
4170
+ mobile: true,
4171
+ onSelect: props.kind === "route" && props.onItemSelect ? () => props.onItemSelect?.(item) : void 0
4172
+ }
4173
+ );
4174
+ return showRouteActions && routeItem ? /* @__PURE__ */ jsxs(
4175
+ "div",
4176
+ {
4177
+ "data-navigation-sidebar-item-row": item.id,
4178
+ "data-active": String(active),
4179
+ className: cn(
4180
+ routeRowClassName({ active, appearance }),
4181
+ "shrink-0"
4182
+ ),
4183
+ children: [
4184
+ entryButton,
4185
+ /* @__PURE__ */ jsx(
4186
+ RouteActions,
4187
+ {
4188
+ itemId: routeItem.id,
4189
+ actions: routeItem.actions,
4190
+ kind: "item",
4191
+ className: classNames?.itemActions,
4192
+ mobile: true
4193
+ }
4194
+ )
4195
+ ]
3727
4196
  },
3728
- mobile: true,
3729
- onSelect: props.kind === "route" && props.onItemSelect ? () => props.onItemSelect?.(item) : void 0
3730
- },
3731
- item.id
3732
- ))
4197
+ item.id
4198
+ ) : /* @__PURE__ */ jsx(Fragment$1, { children: entryButton }, item.id);
4199
+ })
3733
4200
  }
3734
4201
  );
3735
4202
  }